Cross toolchain under linux

2014年9月17日 15:21

Cross compile toolchain是linux常用的一种跨架构,跨设备的开发模式,多用于嵌入式开发过程(由于目标架构的编译能力较弱,比如树莓派等),比如在x86 linux平台作arm开发,在linux下作ios开发等等。即使不作嵌入式开发,在你需要调整系统架构,调整系统基础库,裁剪系统或者在x86_64下cross x86或者反过来,或者,从无到有为一个架构移植新系统时,也总有用到cross toolchain的时候。更有甚者,如MAC OS X,其MAC native应用的开发在xcode中也是cross的方式,这里的cross不再是cross架构,而是cross sysroot,从而实现了SDK和系统Runtime物理的分离。

这篇小文主要是从原理和形式的角度来简单介绍一些cross toolchain到底是个什么构成,写这个东西不是为了解决具体问题,主要是为希望了解cross toolchain的同志们提供一个大概的view。

1,基本构成:

一条Cross toolchain开发环境,主要由以下两个部分组成:

Host cross toolchain:能够运行在host OS,可以生成目标OS代码的汇编器、链接器、编译器,常见的是gcc+binutils。我在Linux ios toolchain中使用的是clang/llvm + 从苹果开源代码移植的cctools。

target sysroot:目标系统的整体镜像,包括了开发库,头文件等等。往往表现为一个目录。host cross toolchain在编译,链接时,使用目标系统的头文件、动态库、静态库等,而不会再使用host系统的头文件等。

生成目标代码后需要在真实物理设备或者在以qemu为代表的虚拟运行环境中运行,所以,android需要一个qemu仿真环境,Xcode ios开发环境也提供了一个IOS模拟器。

注:基于clang/llvm的cross toolchain是非常简单的,如果你系统的clang/llvm编译构建时没有关闭clang/llvm的目标架构支持,那么编译器部分已经可以支持cross,只要系统提供了<tripplet>-ld, <tripplet>-ar等,那么配合target sysroot和--target=<tripplet>直接就可以cross compile。具体可以参考 https://code.google.com/p/ios-toolchain-based-on-clang-for-linux,我在wiki部分比较详细的介绍了linux ios toolchain的过程,这个toolchain也是目前最接近xcode toolchain的实现。

下面主要讲一下binutils, gcc等cross的构成。

 

2,第一步,汇编器和链接器,cross的binutils

binutils提供了汇编器和链接器(as和ld)以及ranlib等其他常用的二进制处理相关的工具,cross的binutils是运行在host os但能够汇编目标架构的汇编代码、并通过sysroot链接目标机器的开发库,生成目标架构的二进制码。

所以binutils是制作cross toolchain的第一步。

这里主要注意两个configure参数:

--target=<target tripplet>,比如arm可能是arm-myos-linux-gnueabihf

--sysroot=<sysroot absolute path>,指定一下sysroot将要放置的目录,可以是/opt/sysroot等。这个参数,决定了链接时ld到哪里去找动态、静态库。

--prefix可以设置成一个非标准路径,以避免跟标准路径下的系统工具冲突,比如--prefix=/opt/cross-toolchain。

这样做出来的binutils,就是可以运行在host系统并能够支持目标架构汇编代码的编译器和链接器了。其命令的名称可能是<target tripplet>-as等形式。

 

3,第二步,最小的cross gcc,使用cross的binutils.

gcc为了更好的支持cross toolchain的构建,已经提供了一种最小化编译安装的方式(不依赖系统headers,自包含一个简单的libc,使用的是红帽的newlib)。

这一步是为后面制作完整的cross gcc作准备,也就是我们只需要能够生成一个将C代码转换为目标架构汇编代码的C编译器即可(C++编译器都不需要)。

这里我们需要的只有gcc和libgcc。

注:cross toolchain的gcc是运行在host os的,也就是cross toolchain的gcc命令是host os的二进制代码,但是,cross toolchain中的libgcc*.a,crtbegin/end.o,却是目标架构代码的(因为它是用来编译、链接,生成目标架构二进制码的),所以这里必须使用前面构建的cross binutils.

export PATH=<cross binutils bin path>:$PATH

让编译过程能够找到前面制作的cross binutils.

 

几个主要的congfigure参数如下:

--without-headers --with-newlib:不使用任何系统header,使用gcc自带的newlib libc库。

--target=<target tripplet>,比如,前面我们使用的是arm-myos-linux-gnueabihf,可以确保编译过程能够找到并使用<target tripplet>-as/ld等命令。

--prefix=<cross toolchain path>,这里跟前面binutils的一样。

其他诸如--enable-languages=c,c++以及一些目标架构相关的配置参数等可以根据实际情况添加,比如树莓派可能需要添加--with-abi=aapcs-linux --with-arch=armv6zk --with-mode=arm --with-float=hard --with-fpu=vfp。

编译时使用:

make all-gcc

make install-gcc

make all-target-libgcc

make install-target-libgcc

这里生成的gcc命令,其名称是<target tripplet>-gcc。

到目前为止,实际上我们已经得到了基本的cross 编译器(gcc/g++,但是没有libstdc++-v3)、链接器和汇编器。

 

4,第三步,target sysroot中最基本的架构相关的头文件,kernel headers.

kernel headers并不是内核模块开发的kernel development头文件,而是kernel提供的跟架构相关的用户态头文件。

比如asm/unistd_*.h,提供的是系统调用号等,这些头文件,往往会被libc二次封装。

编译安装过程比较简单,解开内核源代码,以arm为例:

make ARCH=arm headers_check

make ARCH=arm INSTALL_HDR_PATH=/opt/sysroot/usr headers_install

将必要的头文件安装到target sysroot中/usr/include相关目录。

 

5,第四步,target sysroot最基本的库,C库。

C库的选择是比较多的,常见的由glibc/eglibc, uclibc, musl-libc等等。

具体的编译参数取决于你使用哪个C库,但常见的配置原则如下:

export PATH=<cross toolchain bin path>:$PATH

export CC=<target tripplet>-gcc

export LD=<target tripplet>-ld

export AS=<target tripplet>-as

编译时注意:

--host=<target tripplet>

--target=<target tripplet>

如果是glibc,还需要--with-headers=</opt/sysroot/usr/include>.

对于musl-libc之类的轻量级C库,可能相对比较简单,只需要使用target gcc/as/ld编译即可。

configure的过程中如果需要生成临时代码探测系统特征,肯定是会失败的(因为生成的是目标架构二进制,却要在host os运行),所以,往往要通过一些预制的参数或者config.sub的使用来避免这种动态探测。

编译后,需要将其安装到target sysroot(一般使用make install DESTDIR=/opt/sysroot)。这里就是/opt/sysroot,往往会在/opt/sysroot/lib目录安装loader(常见的ld-linux.so.x等),会在/opt/sysroot/usr/include安装C库头文件。

特别注意,这里生成的C库的二进制是目标架构的二进制,而不是host OS的二进制。

 

6,第五步,完整的cross gcc,支持sysroot的使用。

这一步的构建过程,跟第二步最小的cross gcc类似,主要需要注意以下几点:

6.1,添加--with-sysroot=/opt/sysroot,使用sysroot中的c库和头文件。

6.2,去掉--without-headers --with-newlib,不再使用gcc自带的newlib。

6.3,添加目标代码需要的各种补丁.

6.4,--prefix仍然指定为/opt/cross-toolchain

6.5, --host=<host tripplet> --target=<target tripplet>,也就是仍然用本地gcc构建cross toolchain的<target tripplet>-gcc。

编译,安装。(必要时,可能需要在编译过程中作make install-gcc; make install-target-libgcc; make install-target-libstdc++-v3)

至此,我们得到了:

1,完整的cross toolchian,包括支持sysroot的binutils,gcc等

2,基本的target sysroot,包括kernel headers和C库。

 

7,第六步,使用最终的cross toolchain,构建内核镜像,基础utils等,直到完成一个可以self boot的sysroot。

目前的target sysroot只包含了kernel headers和基础C库,距离一个可以self boot的基础可运行环境还相去甚远。

至少需要添加内核镜像,busybox或者其他基础工具和shell,并构造initscripts等,直到可以自举为止。

添加必要组件后,即可将target sysroot导入到目标机器存储,从目标机器直接启动系统。

 

8,如果是从无到有的制作新架构的booting系统,且目标架构具备足够的运算能力,如龙芯,ppc或者较高主频的arm。

可以通过cross toolchain编译出目标架构的gcc和binutils,后续的软件编译可以在目标系统完成。

 

总结:

1,一个cross 开发环境,包含了运行在host os的cross toolchain和target sysroot支撑环境两个部分。

2,cross toolchain制作的核心思想是在host os上搭建出能够生成目标代码的编译器,汇编器,链接器。

3,target sysroot提供了目标系统软件开发所需要头文件和支撑库,相当于将完整的目标系统安装到了一个系统目录。

4,toolchain制作的步骤,大概是1)先有链接器、汇编器,2)再作最小的编译器,3)准备sysroot的基础头文件和C库,4)重作编译器以便于编译时可以使用target sysroot的头文件和C库。

5,弱运算能力的目标架构,一般会采用cross模式进行开发;如果目标架构的运算能力足够的强,应该将主要的工作转移到完成self boot的目标系统并为目标系统配备binutils,gcc等,直接在目标系统作编译,以避免cross 过程中出现的特征探测等编译配置问题。

6,理论上所有的开发工作都可以cross,但因为上述原因,cross用起来未必舒服,这里也是见开发商功力的地方。比如,xcode作IOS开发的集成度就比Android的SDK用起来要舒服很多,集成度,细节隐藏也要好很多,而工程统一的代码管理方式和配置方式,也不会出现为android迁移开源工程时的配置探测问题等。

顺便说一下,xcode作mac native开发也是cross 思想,目前,mac的编译器主要用的是clang,xcode将mac sdk安装到了单独的目录,编译时会通过sysroot来指定真实的系统路径。

 

注意:

1,autotools系列工具,也就是configure脚本类型的工程管理,--host, --target, --build参数经常在cross compile中使用,这是需要了解的内容。

2,configure过程,往往会通过临时生成一个二进制运行探测系统特征,在cross compile中,这是很大的问题,因为目标代码的二进制无法在host os运行,可以通过1)config.sub预设或者2)qemu usermode模拟的方式来运行。

3,没有目标架构的硬件,也可以通过qemu等尝试cross toolchain的练习,最后将target sysroot作成qemu镜像来运行。

4,toolchain制作过程中,注意target tripplet, toolchain路径等的统一,尽量不要出错返工。

 

 

 

评论(0) 阅读(15571)

怎样构建一个不依赖gcc/binutils的llvm/clang Linux工具链

2014年9月10日 16:32

本文不讲原理,只讲步骤。需要了解什么是unwind,crtbengin/end,ehtable以及libgcc_s/libgcc_eh或者c++abi/supc++的,请查阅相关文档。

1,Build llvm/clang/lldb/lld 3.5.0等组件

1.0  准备:

至少需要从llvm.org下载llvm, cfe, lldb, compiler-rt,lld等3.5.0版本的代码。

$tar xf llvm-3.5.0.src.tar.gz

$cd llvm-3.5.0.src

$mkdir -p tools/clang
$mkdir -p tools/clang/tools/extra
$mkdir -p tools/lld
$mkdir -p projects/compiler-rt

$tar xf cfe-3.5.0.src.tar.xz -C tools/clang --strip-components=1
$tar xf compiler-rt-3.5.0.src.tar.xz -C projects/compiler-rt --strip-components=1
$tar xf lldb-3.5.0.src.tar.xz -C tools/clang/tools/extra --strip-components=1
$tar xf lld-3.5.0.src.tar.xz -C tools/lld --strip-components=1

 

1.1 【可选】使用clang --stdlib=libc++时,自动添加-lc++abi。

libc++组件可以使用gcc libstdc++的supc++ ABI,也可以使用c++abi,cxxrt等,实际上自动添加-lc++abi是不必要的,这里这么处理,主要是为了方便起见。实际上完全可以在“clang++ -stdlib=libc++”时再手工添加-lc++abi给链接器。

这里涉及到链接时DSO隐式还是显式的问题,早些时候ld在链接库时会自动引入由库引入的依赖动态库,后来因为这个行为的不可控性,所以ld链接器的行为做了修改,需要显式的写明所有需要链接的动态库,才会有手工添加-lc++abi这种情况出现。

--- llvm-3.0.src/tools/clang/lib/Driver/ToolChain.cpp   2012-03-26 18:49:06.663029075 +0800
+++ llvm-3.0.srcn/tools/clang/lib/Driver/ToolChain.cpp  2012-03-26 19:36:04.260071355 +0800
@@ -251,6 +251,7 @@
   switch (Type) {
   case ToolChain::CST_Libcxx:
     CmdArgs.push_back("-lc++");
+    CmdArgs.push_back("-lc++abi");
     break;

   case ToolChain::CST_Libstdcxx:

 

1.2 【必要】给clang++添加-fnolibgcc开关。

这个开关主要用来控制是否连接到libgcc或者libunwind。

注:libgcc不等于libunwind。libgcc_eh以及supc++的一部分跟libunwind功能相当。

注:libgcc_s和compiler_rt的一部分相当。

 

这个补丁是必要的,不会对clang的正常使用造成任何影响,只有在使用“-fnolibgcc"参数时才会起作用。

之所以进行了很多unwind的引入,主要是为了避免不必要的符号缺失麻烦,这里的处理相对来说是干净的,通过as-needed规避了不必要的引入。

--- llvm-static-3.5.0.bak/tools/clang/lib/Driver/Tools.cpp	2014-09-10 13:46:02.581543888 +0800
+++ llvm-static-3.5.0/tools/clang/lib/Driver/Tools.cpp	2014-09-10 16:03:37.559019321 +0800
@@ -2060,9 +2060,15 @@
                                           ".a");
 
   CmdArgs.push_back(Args.MakeArgString(LibClangRT));
-  CmdArgs.push_back("-lgcc_s");
-  if (TC.getDriver().CCCIsCXX())
-    CmdArgs.push_back("-lgcc_eh");
+  if (Args.hasArg(options::OPT_fnolibgcc)) {
+    CmdArgs.push_back("--as-needed");
+    CmdArgs.push_back("-lunwind");
+    CmdArgs.push_back("--no-as-needed");
+  } else {
+    CmdArgs.push_back("-lgcc_s");
+    if (TC.getDriver().CCCIsCXX())
+      CmdArgs.push_back("-lgcc_eh");
+  }
 }
 
 static void addProfileRT(
@@ -7150,24 +7156,50 @@
   bool isAndroid = Triple.getEnvironment() == llvm::Triple::Android;
   bool StaticLibgcc = Args.hasArg(options::OPT_static_libgcc) ||
                       Args.hasArg(options::OPT_static);
+
+
+
   if (!D.CCCIsCXX())
-    CmdArgs.push_back("-lgcc");
+    if (Args.hasArg(options::OPT_fnolibgcc)) {
+        CmdArgs.push_back("--as-needed");
+        CmdArgs.push_back("-lunwind");
+        CmdArgs.push_back("--no-as-needed");
+    } else
+        CmdArgs.push_back("-lgcc");
 
   if (StaticLibgcc || isAndroid) {
     if (D.CCCIsCXX())
-      CmdArgs.push_back("-lgcc");
+    if (Args.hasArg(options::OPT_fnolibgcc)) {
+        CmdArgs.push_back("--as-needed");
+        CmdArgs.push_back("-lunwind");
+        CmdArgs.push_back("--no-as-needed");
+    } else
+        CmdArgs.push_back("-lgcc");
   } else {
     if (!D.CCCIsCXX())
       CmdArgs.push_back("--as-needed");
-    CmdArgs.push_back("-lgcc_s");
+    if (Args.hasArg(options::OPT_fnolibgcc))
+        CmdArgs.push_back("-lunwind");
+    else
+        CmdArgs.push_back("-lgcc_s");
     if (!D.CCCIsCXX())
       CmdArgs.push_back("--no-as-needed");
   }
 
   if (StaticLibgcc && !isAndroid)
-    CmdArgs.push_back("-lgcc_eh");
+    if (Args.hasArg(options::OPT_fnolibgcc)) {
+        CmdArgs.push_back("--as-needed");
+        CmdArgs.push_back("-lunwind");
+        CmdArgs.push_back("--no-as-needed");
+    } else
+        CmdArgs.push_back("-lgcc_eh");
   else if (!Args.hasArg(options::OPT_shared) && D.CCCIsCXX())
-    CmdArgs.push_back("-lgcc");
+    if (Args.hasArg(options::OPT_fnolibgcc)) {
+        CmdArgs.push_back("--as-needed");
+        CmdArgs.push_back("-lunwind");
+        CmdArgs.push_back("--no-as-needed");
+    } else
+        CmdArgs.push_back("-lgcc");
 
   // According to Android ABI, we have to link with libdl if we are
   // linking with non-static libgcc.
--- llvm-static-3.5.0.bak/tools/clang/include/clang/Driver/Options.td	2014-08-07 12:51:51.000000000 +0800
+++ llvm-static-3.5.0/tools/clang/include/clang/Driver/Options.td	2014-09-10 13:36:34.598511176 +0800
@@ -788,6 +788,7 @@
 def fomit_frame_pointer : Flag<["-"], "fomit-frame-pointer">, Group<f_Group>;
 def fopenmp : Flag<["-"], "fopenmp">, Group<f_Group>, Flags<[CC1Option, NoArgumentUnused]>;
 def fopenmp_EQ : Joined<["-"], "fopenmp=">, Group<f_Group>, Flags<[CC1Option]>;
+def fnolibgcc : Flag<["-"], "fnolibgcc">, Group<f_Group>, Flags<[CC1Option, NoArgumentUnused]>;
 def fno_optimize_sibling_calls : Flag<["-"], "fno-optimize-sibling-calls">, Group<f_Group>;
 def foptimize_sibling_calls : Flag<["-"], "foptimize-sibling-calls">, Group<f_Group>;
 def force__cpusubtype__ALL : Flag<["-"], "force_cpusubtype_ALL">;

1.3 llvm的其他补丁。

llvm/clang将gcc toolchain的路径hard code在代码中,请查阅tools/clang/lib/Driver/ToolChains.cpp。

找到x86_64-redhat-linux之类的字符串。

如果没有你系统特有的gcc tripple string,请自行添加。

这个tripple string主要是给llvm/clang搜索gcc头文件等使用的,不影响本文要构建的toolchain

 

1.4 构建clang/llvm/lldb

本文使用ninja。顺便说一下,llvm支持configure和cmake两种构建方式。可能是因为工程太大,这两种构建方式的工程文件都有各种缺陷(主要表现在开关选项上,比如configure有,但是cmake却没有等)。llvm-3.4.1就是因为cmake工程文件的错误而导致了3.4.2版本的发布。

综合而言,cmake+ninja的方式是目前最快的构建方式之一,可以将构建时间缩短一半以上。

mkdir build
cd build

cmake \
    -G Ninja \
    -DCMAKE_INSTALL_PREFIX=/usr \
    -DCMAKE_BUILD_TYPE="Release" \
    -DCMAKE_CXX_FLAGS="-std=c++11" \
    -DBUILD_SHARED_LIBS=OFF \
    -DLLVM_ENABLE_PIC=ON \
    -DLLVM_TARGETS_TO_BUILD="all" \
    -DCLANG_VENDOR="MyOS" ..

ninja

ninja install

    

如果系统原来就有clang/clang++的可用版本,可以添加:

    -DCMAKE_C_COMPILER=clang \
    -DCMAKE_CXX_COMPILER=clang++ \

这样就会使用系统的clang++来构建llvm/clang

 

2,测试clang/clang++。

自己找几个简单的c/cpp/objc等编译测试一下即可。完整测试可以在构建时作ninja check-all

 

3,libunwind/libc++/libc++abi,一套不依赖libgcc, libstdc++的c++运行库。

3.1 从https://github.com/pathscale/libunwind 获取代码。

libunwind有很多个实现,比如gnu的libunwind, path64的libunwind,还有libcxxabi自带的Unwinder.

这里作下说明:

1),gnu的libunwind会有符号缺失和冲突。

2),libcxxabi自带的Unwinder是给mac和ios用的,也就是只能在darwin体系构建。目前Linux的实现仍然不全,等linux实现完整了或许就不再需要path64的unwind实现了。

暂时建议使用pathscale的unwind实现。

mkdir -p build
cd build
cmake -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_C_FLAGS="-m64" ..
ninja

mkdir -p /usr/lib
cp src/libunwind.so /usr/lib
cp src/libunwind.a /usr/lib

 

3.2 第一次构建libcxx.

必须先构建一次libcxx,以便后面构建libcxxabi。这里构建的libcxx实际上是使用gcc的libgcc/stdc++/supc++的。

打上这个补丁来禁止libgcc的引入:

diff -Nur libcxx/cmake/config-ix.cmake libcxxn/cmake/config-ix.cmake
--- libcxx/cmake/config-ix.cmake    2014-06-25 06:57:50.000000000 +0800
+++ libcxxn/cmake/config-ix.cmake   2014-06-25 09:05:24.980350544 +0800
@@ -28,5 +28,4 @@
 check_library_exists(c printf "" LIBCXX_HAS_C_LIB)
 check_library_exists(m ccos "" LIBCXX_HAS_M_LIB)
 check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
-check_library_exists(gcc_s __gcc_personality_v0 "" LIBCXX_HAS_GCC_S_LIB)

 

编译安装:

mkdir build
cd build
cmake \
    -G Ninja \
    -DCMAKE_INSTALL_PREFIX=/usr \
    -DCMAKE_C_COMPILER=clang \
    -DCMAKE_CXX_COMPILER=clang++  \
    ..
ninja
ninja install


3.3,测试第一次构建的libcxx。

使用"clang++ -stdlib=libc++  -o test test.cpp -lstdc++"编译简单c++代码,检查是否出错。(如果前面构建clang是已经apply了c++abi的链接补丁,这里会出现找不到c++abi的情况,跳过即可)

使用"ldd test"查看test二进制动态库使用情况。可以发现,test依赖于libgcc_s/libc++/libstdc++。(多少有些不爽了吧?使用了libc++居然还要依赖libstdc++?)

 

3.4,构建libcxxabi。

打上以下补丁,让libcxxabi链接到libc++,并生成静态库。

diff -Nur libcxxabi/lib/buildit libcxxabin/lib/buildit
--- libcxxabi/lib/buildit   2013-12-05 20:36:16.000000000 +0800
+++ libcxxabin/lib/buildit  2014-06-25 08:14:25.922459892 +0800
@@ -70,7 +70,7 @@
     SOEXT=so
     LDSHARED_FLAGS="-o libc++abi.so.1.0 \
         -shared -nodefaultlibs -Wl,-soname,libc++abi.so.1 \
-        -lpthread -lrt -lc -lstdc++"
+        -lpthread -lrt -lc -lc++"
     ;;
 esac

@@ -92,6 +92,8 @@
   ;;
 esac
 $CC *.o $RC_CFLAGS $LDSHARED_FLAGS $EXTRA_FLAGS
+ar rc libc++abi.a *.o
+ranlib libc++abi.a

 if [ -z $RC_XBS ]
 then     

 

编译安装:

cd lib
export CC="clang -fPIC"
export CXX="clang++ -fPIC"
./buildit
install -m 0644 lib/libc++abi.so.1.0 /usr/lib
install -m 0644 lib/libc++abi.a /usr/lib
cp -r include/* /usr/include
cd /usr/lib
ln -s libc++abi.so.1.0 libc++abi.so.1
ln -s libc++abi.so.1.0 libc++abi.so

 

3.5,第二次构建libcxx,使其使用c++abi,并同时构建出静态库。

仍然要打上禁用_gcc_personality_v0的补丁。

mkdir build-shared
cd build-shared
cmake \
    -G Ninja \
    -DCMAKE_INSTALL_PREFIX=/usr \
    -DCMAKE_C_COMPILER=clang \
    -DCMAKE_CXX_COMPILER=clang++  \
    -DLIBCXX_CXX_ABI=libcxxabi \
    -DLIBCXX_LIBCXXABI_INCLUDE_PATHS="/usr/include" \
    ..
ninja
ninja install
cd ..

mkdir build-static
cd build-static
cmake \
    -G Ninja \
    -DCMAKE_INSTALL_PREFIX=/usr \
    -DCMAKE_C_COMPILER=clang \
    -DCMAKE_CXX_COMPILER=clang++  \
    -DLIBCXX_CXX_ABI=libcxxabi \
    -DLIBCXX_LIBCXXABI_INCLUDE_PATHS="/usr/include" \
    -DLIBCXX_ENABLE_SHARED=OFF \
    ..
ninja
ninja install
cd ..

 

至此,unwind/libcxx/libcxxabi构建完成,并摆脱了gcc运行时库libgcc/libstdc++的依赖。

3.6,测试验证一下

"ld -lc++ -lc++abi -lgcc_s"

"ld -lc++ -lc++abi -lunwind"

这两种情况都应该可以满足符号依赖,不存在符号找不到的情况。(主要是_Unwind_符号)

至此:

使用:

clang++ -o test test.cpp
ldd test

test将链接到stdc++/gcc_s. 这是默认使用gcc runtime的情况,其二进制也会跟gcc生成的二进制完全兼容。

clang++ -fnolibgcc -o test test.cpp
ldd test

test将链接到stdc++/unwind.(注:gcc_s跟unwind的功能不完全一样,但互有交集实现)

clang++ -stdlib=libc++ -o test test.cpp
ldd test

test将链接到c++/c++abi/gcc_s(已经剥离了stdc++依赖,但仍然使用gcc_s,也是因为_Unwind_符号的原因)

clang++ -stdlib=libc++ -fnolibgcc -o test test.cpp
ldd test

test将链接到c++/c++abi/unwind.(这大概是很多同志想要的。)

 

4. crtbegin.o/crtend.o

 

这两货是什么作用可以查一下文档,gcc默认参数构建的每个二进制中,这两个.o都被链接进去了,具体构建链接参数,可以在clang编译代码时加 -v查看。默认情况下,clang会使用gcc的crtbegin/end。

这里我们用Netbsd的实现替代了gcc的实现。

x86_64的汇编代码从这里下载: http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/csu/arch/x86_64/?only_with_tag=MAIN。

注意安装路径,这是clang最优先的搜索路径。

as crtbegin.S -o /usr/lib/clang/3.5.0/crtbegin.o
as crtbegin.S -o /usr/lib/clang/3.5.0/crtbeginS.o
as crtbegin.S -o /usr/lib/clang/3.5.0/crtbeginT.o

as crtend.S -o /usr/lib/clang/3.5.0/crtend.o
as crtend.S -o /usr/lib/clang/3.5.0/crtendS.o
as crtend.S -o /usr/lib/clang/3.5.0/crtendT.o

 

5,至此,我们基本获得了一条不依赖gcc任何组件的clang/llvm工具链。

编译时通过-fnolibgcc参数以及-stdlib=libc++参数的配合,可以完成c/c++代码的编译并脱离对gcc运行时库的依赖。

 

6,使用新的clang toolchain完成上述所有组件的bootstrap。(也就是用新toolchain重新编译llvm/clang)

具体构建过程参考步骤1.

在apply上述补丁的同时,需要apply以下补丁。

主要原因是ehtable支持需要调用register_frame实现,这是libgcc特有的实现。

--- llvm-3.4.2.src.bak/lib/ExecutionEngine/RTDyldMemoryManager.cpp  2013-12-24 14:50:45.000000000 +0800
+++ llvm-3.4.2.src/lib/ExecutionEngine/RTDyldMemoryManager.cpp  2014-06-25 14:11:53.721693229 +0800
@@ -33,12 +33,7 @@
 RTDyldMemoryManager::~RTDyldMemoryManager() {}

 // Determine whether we can register EH tables.
-#if (defined(__GNUC__) && !defined(__ARM_EABI__) && !defined(__ia64__) && \
-     !defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__))
-#define HAVE_EHTABLE_SUPPORT 1
-#else
 #define HAVE_EHTABLE_SUPPORT 0
-#endif

 #if HAVE_EHTABLE_SUPPORT
 extern "C" void __register_frame(void*);


在编译时,注意增加clang/clang++参数如下:

-DCMAKE_CXX_FLAGS="-std=c++11 --stdlib=libc++ -fnolibgcc -fPIC"
-DCMAKE_C_FLAGS="-fPIC" 

 

7,使用lld。

lld的使用方法非常简单,只需要将/usr/bin/lld链接成/usr/bin/ld即可。

更通常的情况,是使用alternatives来管理和切换多个ld(至少,目前我们有binutils的ld.bfd, google的gold以及llvm的lld可用)

根据经验,lld应该可以胜任大部分的链接工作,但毕竟是新生项目,且仍然有大量的gnu ld的参数没有支持,所以,这里不建议未经测试和验证的就在生产环境使用。

此外,完全脱离binutils目前也不太可能,毕竟出了gnu as/ld,binutils也提供了ar,ranlib等utility。虽然llvm提供了llvm-ar等,但这些llvm utility是处理llvm bc IR字节码的,并不是用来处理系统目标文件的工具。

8,__gcc_personality_v0符号问题。

在部分静态库或者动态库链接时,如果强制使用-fnolibgcc参数禁止掉libgcc的使用,可能会出现__gcc_personality_v0符号缺失问题,这个问题的解决方法有以下两个:

7.1 添加--rtlib=compiler-rt, llvm的compiler-rt有该符号的完整实现。

7.2 或者为libunwind打上这个补丁,将实现放到libunwind中。

diff -Nur libunwind/src/CMakeLists.txt libunwindn/src/CMakeLists.txt
--- libunwind/src/CMakeLists.txt	2014-06-25 07:14:23.000000000 +0800
+++ libunwindn/src/CMakeLists.txt	2014-06-25 11:48:55.139999875 +0800
@@ -50,6 +50,8 @@
     dwarf/Lpe.c
     dwarf/Lstep.c
     dwarf/Lfind_proc_info-lsb.c
+    gcc_personality_v0/gcc_personality_v0.c 
+    gcc_personality_v0/int_util.c 
    )
 
 
diff -Nur libunwind/src/gcc_personality_v0/gcc_personality_v0.c libunwindn/src/gcc_personality_v0/gcc_personality_v0.c
--- libunwind/src/gcc_personality_v0/gcc_personality_v0.c	1970-01-01 08:00:00.000000000 +0800
+++ libunwindn/src/gcc_personality_v0/gcc_personality_v0.c	2014-06-25 11:47:54.010002060 +0800
@@ -0,0 +1,247 @@
+/* ===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------===
+ *
+ *      	       The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ */
+
+#include "int_lib.h"
+
+/*
+ * _Unwind_* stuff based on C++ ABI public documentation
+ * http://refspecs.freestandards.org/abi-eh-1.21.html
+ */
+
+typedef enum {
+    _URC_NO_REASON = 0,
+    _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
+    _URC_FATAL_PHASE2_ERROR = 2,
+    _URC_FATAL_PHASE1_ERROR = 3,
+    _URC_NORMAL_STOP = 4,
+    _URC_END_OF_STACK = 5,
+    _URC_HANDLER_FOUND = 6,
+    _URC_INSTALL_CONTEXT = 7,
+    _URC_CONTINUE_UNWIND = 8
+} _Unwind_Reason_Code;
+
+typedef enum {
+    _UA_SEARCH_PHASE = 1,
+    _UA_CLEANUP_PHASE = 2,
+    _UA_HANDLER_FRAME = 4,
+    _UA_FORCE_UNWIND = 8,
+    _UA_END_OF_STACK = 16
+} _Unwind_Action;
+
+typedef struct _Unwind_Context* _Unwind_Context_t;
+
+struct _Unwind_Exception {
+    uint64_t                exception_class;
+    void                    (*exception_cleanup)(_Unwind_Reason_Code reason, 
+                                                 struct _Unwind_Exception* exc);
+    uintptr_t                private_1;    
+    uintptr_t                private_2;    
+};
+
+extern const uint8_t*    _Unwind_GetLanguageSpecificData(_Unwind_Context_t c);
+extern void              _Unwind_SetGR(_Unwind_Context_t c, int i, uintptr_t n);
+extern void              _Unwind_SetIP(_Unwind_Context_t, uintptr_t new_value);
+extern uintptr_t         _Unwind_GetIP(_Unwind_Context_t context);
+extern uintptr_t         _Unwind_GetRegionStart(_Unwind_Context_t context);
+
+
+/*
+ * Pointer encodings documented at:
+ *   http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html
+ */
+
+#define DW_EH_PE_omit      0xff  /* no data follows */
+
+#define DW_EH_PE_absptr    0x00
+#define DW_EH_PE_uleb128   0x01
+#define DW_EH_PE_udata2    0x02
+#define DW_EH_PE_udata4    0x03
+#define DW_EH_PE_udata8    0x04
+#define DW_EH_PE_sleb128   0x09
+#define DW_EH_PE_sdata2    0x0A
+#define DW_EH_PE_sdata4    0x0B
+#define DW_EH_PE_sdata8    0x0C
+
+#define DW_EH_PE_pcrel     0x10
+#define DW_EH_PE_textrel   0x20
+#define DW_EH_PE_datarel   0x30
+#define DW_EH_PE_funcrel   0x40
+#define DW_EH_PE_aligned   0x50  
+#define DW_EH_PE_indirect  0x80 /* gcc extension */
+
+
+
+/* read a uleb128 encoded value and advance pointer */
+static uintptr_t readULEB128(const uint8_t** data)
+{
+    uintptr_t result = 0;
+    uintptr_t shift = 0;
+    unsigned char byte;
+    const uint8_t* p = *data;
+    do {
+        byte = *p++;
+        result |= (byte & 0x7f) << shift;
+        shift += 7;
+    } while (byte & 0x80);
+    *data = p;
+    return result;
+}
+
+/* read a pointer encoded value and advance pointer */
+static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding)
+{
+    const uint8_t* p = *data;
+    uintptr_t result = 0;
+
+    if ( encoding == DW_EH_PE_omit ) 
+        return 0;
+
+    /* first get value */
+    switch (encoding & 0x0F) {
+        case DW_EH_PE_absptr:
+            result = *((uintptr_t*)p);
+            p += sizeof(uintptr_t);
+            break;
+        case DW_EH_PE_uleb128:
+            result = readULEB128(&p);
+            break;
+        case DW_EH_PE_udata2:
+            result = *((uint16_t*)p);
+            p += sizeof(uint16_t);
+            break;
+        case DW_EH_PE_udata4:
+            result = *((uint32_t*)p);
+            p += sizeof(uint32_t);
+            break;
+        case DW_EH_PE_udata8:
+            result = *((uint64_t*)p);
+            p += sizeof(uint64_t);
+            break;
+        case DW_EH_PE_sdata2:
+            result = *((int16_t*)p);
+            p += sizeof(int16_t);
+            break;
+        case DW_EH_PE_sdata4:
+            result = *((int32_t*)p);
+            p += sizeof(int32_t);
+            break;
+        case DW_EH_PE_sdata8:
+            result = *((int64_t*)p);
+            p += sizeof(int64_t);
+            break;
+        case DW_EH_PE_sleb128:
+        default:
+            /* not supported */
+            compilerrt_abort();
+            break;
+    }
+
+    /* then add relative offset */
+    switch ( encoding & 0x70 ) {
+        case DW_EH_PE_absptr:
+            /* do nothing */
+            break;
+        case DW_EH_PE_pcrel:
+            result += (uintptr_t)(*data);
+            break;
+        case DW_EH_PE_textrel:
+        case DW_EH_PE_datarel:
+        case DW_EH_PE_funcrel:
+        case DW_EH_PE_aligned:
+        default:
+            /* not supported */
+            compilerrt_abort();
+            break;
+    }
+
+    /* then apply indirection */
+    if (encoding & DW_EH_PE_indirect) {
+        result = *((uintptr_t*)result);
+    }
+
+    *data = p;
+    return result;
+}
+
+
+/*
+ * The C compiler makes references to __gcc_personality_v0 in
+ * the dwarf unwind information for translation units that use
+ * __attribute__((cleanup(xx))) on local variables.
+ * This personality routine is called by the system unwinder
+ * on each frame as the stack is unwound during a C++ exception
+ * throw through a C function compiled with -fexceptions.
+ */
+#if __arm__
+// the setjump-longjump based exceptions personality routine has a different name
+_Unwind_Reason_Code __gcc_personality_sj0(int version, _Unwind_Action actions,
+         uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject,
+         _Unwind_Context_t context)
+#else
+_Unwind_Reason_Code __gcc_personality_v0(int version, _Unwind_Action actions,
+         uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject,
+         _Unwind_Context_t context)
+#endif
+{
+    /* Since C does not have catch clauses, there is nothing to do during */
+    /* phase 1 (the search phase). */
+    if ( actions & _UA_SEARCH_PHASE ) 
+        return _URC_CONTINUE_UNWIND;
+        
+    /* There is nothing to do if there is no LSDA for this frame. */
+    const uint8_t* lsda = _Unwind_GetLanguageSpecificData(context);
+    if ( lsda == (uint8_t*) 0 )
+        return _URC_CONTINUE_UNWIND;
+
+    uintptr_t pc = _Unwind_GetIP(context)-1;
+    uintptr_t funcStart = _Unwind_GetRegionStart(context);
+    uintptr_t pcOffset = pc - funcStart;
+
+    /* Parse LSDA header. */
+    uint8_t lpStartEncoding = *lsda++;
+    if (lpStartEncoding != DW_EH_PE_omit) {
+        readEncodedPointer(&lsda, lpStartEncoding); 
+    }
+    uint8_t ttypeEncoding = *lsda++;
+    if (ttypeEncoding != DW_EH_PE_omit) {
+        readULEB128(&lsda);  
+    }
+    /* Walk call-site table looking for range that includes current PC. */
+    uint8_t         callSiteEncoding = *lsda++;
+    uint32_t        callSiteTableLength = readULEB128(&lsda);
+    const uint8_t*  callSiteTableStart = lsda;
+    const uint8_t*  callSiteTableEnd = callSiteTableStart + callSiteTableLength;
+    const uint8_t* p=callSiteTableStart;
+    while (p < callSiteTableEnd) {
+        uintptr_t start = readEncodedPointer(&p, callSiteEncoding);
+        uintptr_t length = readEncodedPointer(&p, callSiteEncoding);
+        uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding);
+        readULEB128(&p); /* action value not used for C code */
+        if ( landingPad == 0 )
+            continue; /* no landing pad for this entry */
+        if ( (start <= pcOffset) && (pcOffset < (start+length)) ) {
+            /* Found landing pad for the PC.
+             * Set Instruction Pointer to so we re-enter function 
+             * at landing pad. The landing pad is created by the compiler
+             * to take two parameters in registers.
+	     */
+            _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), 
+                                                (uintptr_t)exceptionObject);
+            _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
+            _Unwind_SetIP(context, funcStart+landingPad);
+            return _URC_INSTALL_CONTEXT;
+        }
+    }
+    
+    /* No landing pad found, continue unwinding. */
+    return _URC_CONTINUE_UNWIND;
+}
+
diff -Nur libunwind/src/gcc_personality_v0/int_endianness.h libunwindn/src/gcc_personality_v0/int_endianness.h
--- libunwind/src/gcc_personality_v0/int_endianness.h	1970-01-01 08:00:00.000000000 +0800
+++ libunwindn/src/gcc_personality_v0/int_endianness.h	2014-06-25 11:47:54.010002060 +0800
@@ -0,0 +1,111 @@
+/* ===-- int_endianness.h - configuration header for compiler-rt ------------===
+ *
+ *		       The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file is a configuration header for compiler-rt.
+ * This file is not part of the interface of this library.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#ifndef INT_ENDIANNESS_H
+#define INT_ENDIANNESS_H
+
+#if defined(__SVR4) && defined(__sun)
+#include <sys/byteorder.h>
+
+#if defined(_BIG_ENDIAN)
+#define _YUGA_LITTLE_ENDIAN 0
+#define _YUGA_BIG_ENDIAN    1
+#elif defined(_LITTLE_ENDIAN)
+#define _YUGA_LITTLE_ENDIAN 1
+#define _YUGA_BIG_ENDIAN    0
+#else /* !_LITTLE_ENDIAN */
+#error "unknown endianness"
+#endif /* !_LITTLE_ENDIAN */
+
+#endif /* Solaris and AuroraUX. */
+
+/* .. */
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__minix)
+#include <sys/endian.h>
+
+#if _BYTE_ORDER == _BIG_ENDIAN
+#define _YUGA_LITTLE_ENDIAN 0
+#define _YUGA_BIG_ENDIAN    1
+#elif _BYTE_ORDER == _LITTLE_ENDIAN
+#define _YUGA_LITTLE_ENDIAN 1
+#define _YUGA_BIG_ENDIAN    0
+#endif /* _BYTE_ORDER */
+
+#endif /* *BSD */
+
+#if defined(__OpenBSD__) || defined(__Bitrig__)
+#include <machine/endian.h>
+
+#if _BYTE_ORDER == _BIG_ENDIAN
+#define _YUGA_LITTLE_ENDIAN 0
+#define _YUGA_BIG_ENDIAN    1
+#elif _BYTE_ORDER == _LITTLE_ENDIAN
+#define _YUGA_LITTLE_ENDIAN 1
+#define _YUGA_BIG_ENDIAN    0
+#endif /* _BYTE_ORDER */
+
+#endif /* OpenBSD and Bitrig. */
+
+/* .. */
+
+/* Mac OSX has __BIG_ENDIAN__ or __LITTLE_ENDIAN__ automatically set by the compiler (at least with GCC) */
+#if defined(__APPLE__) && defined(__MACH__) || defined(__ellcc__ )
+
+#ifdef __BIG_ENDIAN__
+#if __BIG_ENDIAN__
+#define _YUGA_LITTLE_ENDIAN 0
+#define _YUGA_BIG_ENDIAN    1
+#endif
+#endif /* __BIG_ENDIAN__ */
+
+#ifdef __LITTLE_ENDIAN__
+#if __LITTLE_ENDIAN__
+#define _YUGA_LITTLE_ENDIAN 1
+#define _YUGA_BIG_ENDIAN    0
+#endif
+#endif /* __LITTLE_ENDIAN__ */
+
+#endif /* Mac OSX */
+
+/* .. */
+
+#if defined(__linux__)
+#include <endian.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define _YUGA_LITTLE_ENDIAN 0
+#define _YUGA_BIG_ENDIAN    1
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#define _YUGA_LITTLE_ENDIAN 1
+#define _YUGA_BIG_ENDIAN    0
+#endif /* __BYTE_ORDER */
+
+#endif /* GNU/Linux */
+
+#if defined(_WIN32)
+
+#define _YUGA_LITTLE_ENDIAN 1
+#define _YUGA_BIG_ENDIAN    0
+
+#endif /* Windows */
+
+/* . */
+
+#if !defined(_YUGA_LITTLE_ENDIAN) || !defined(_YUGA_BIG_ENDIAN)
+#error Unable to determine endian
+#endif /* Check we found an endianness correctly. */
+
+#endif /* INT_ENDIANNESS_H */
diff -Nur libunwind/src/gcc_personality_v0/int_lib.h libunwindn/src/gcc_personality_v0/int_lib.h
--- libunwind/src/gcc_personality_v0/int_lib.h	1970-01-01 08:00:00.000000000 +0800
+++ libunwindn/src/gcc_personality_v0/int_lib.h	2014-06-25 11:47:54.009002060 +0800
@@ -0,0 +1,46 @@
+/* ===-- int_lib.h - configuration header for compiler-rt  -----------------===
+ *
+ *                     The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file is a configuration header for compiler-rt.
+ * This file is not part of the interface of this library.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#ifndef INT_LIB_H
+#define INT_LIB_H
+
+/* Assumption: Signed integral is 2's complement. */
+/* Assumption: Right shift of signed negative is arithmetic shift. */
+/* Assumption: Endianness is little or big (not mixed). */
+
+/* ABI macro definitions */
+
+#if __ARM_EABI__
+# define ARM_EABI_FNALIAS(aeabi_name, name)         \
+  void __aeabi_##aeabi_name() __attribute__((alias("__" #name)));
+# define COMPILER_RT_ABI __attribute__((pcs("aapcs")))
+#else
+# define ARM_EABI_FNALIAS(aeabi_name, name)
+# define COMPILER_RT_ABI
+#endif
+
+/* Include the standard compiler builtin headers we use functionality from. */
+#include <limits.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <float.h>
+
+/* Include the commonly used internal type definitions. */
+#include "int_types.h"
+
+/* Include internal utility function declarations. */
+#include "int_util.h"
+
+#endif /* INT_LIB_H */
diff -Nur libunwind/src/gcc_personality_v0/int_math.h libunwindn/src/gcc_personality_v0/int_math.h
--- libunwind/src/gcc_personality_v0/int_math.h	1970-01-01 08:00:00.000000000 +0800
+++ libunwindn/src/gcc_personality_v0/int_math.h	2014-06-25 11:47:54.009002060 +0800
@@ -0,0 +1,67 @@
+/* ===-- int_math.h - internal math inlines ---------------------------------===
+ *
+ *                     The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===-----------------------------------------------------------------------===
+ *
+ * This file is not part of the interface of this library.
+ *
+ * This file defines substitutes for the libm functions used in some of the
+ * compiler-rt implementations, defined in such a way that there is not a direct
+ * dependency on libm or math.h. Instead, we use the compiler builtin versions
+ * where available. This reduces our dependencies on the system SDK by foisting
+ * the responsibility onto the compiler.
+ *
+ * ===-----------------------------------------------------------------------===
+ */
+
+#ifndef INT_MATH_H
+#define INT_MATH_H
+
+#ifndef __has_builtin
+#  define  __has_builtin(x) 0
+#endif
+
+#define CRT_INFINITY __builtin_huge_valf()
+
+#define crt_isinf(x) __builtin_isinf((x))
+#define crt_isnan(x) __builtin_isnan((x))
+
+/* Define crt_isfinite in terms of the builtin if available, otherwise provide
+ * an alternate version in terms of our other functions. This supports some
+ * versions of GCC which didn't have __builtin_isfinite.
+ */
+#if __has_builtin(__builtin_isfinite)
+#  define crt_isfinite(x) __builtin_isfinite((x))
+#else
+#  define crt_isfinite(x) \
+  __extension__(({ \
+      __typeof((x)) x_ = (x); \
+      !crt_isinf(x_) && !crt_isnan(x_); \
+    }))
+#endif
+
+#define crt_copysign(x, y) __builtin_copysign((x), (y))
+#define crt_copysignf(x, y) __builtin_copysignf((x), (y))
+#define crt_copysignl(x, y) __builtin_copysignl((x), (y))
+
+#define crt_fabs(x) __builtin_fabs((x))
+#define crt_fabsf(x) __builtin_fabsf((x))
+#define crt_fabsl(x) __builtin_fabsl((x))
+
+#define crt_fmax(x, y) __builtin_fmax((x), (y))
+#define crt_fmaxf(x, y) __builtin_fmaxf((x), (y))
+#define crt_fmaxl(x, y) __builtin_fmaxl((x), (y))
+
+#define crt_logb(x) __builtin_logb((x))
+#define crt_logbf(x) __builtin_logbf((x))
+#define crt_logbl(x) __builtin_logbl((x))
+
+#define crt_scalbn(x, y) __builtin_scalbn((x), (y))
+#define crt_scalbnf(x, y) __builtin_scalbnf((x), (y))
+#define crt_scalbnl(x, y) __builtin_scalbnl((x), (y))
+
+#endif /* INT_MATH_H */
diff -Nur libunwind/src/gcc_personality_v0/int_types.h libunwindn/src/gcc_personality_v0/int_types.h
--- libunwind/src/gcc_personality_v0/int_types.h	1970-01-01 08:00:00.000000000 +0800
+++ libunwindn/src/gcc_personality_v0/int_types.h	2014-06-25 11:47:54.009002060 +0800
@@ -0,0 +1,140 @@
+/* ===-- int_lib.h - configuration header for compiler-rt  -----------------===
+ *
+ *                     The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file is not part of the interface of this library.
+ *
+ * This file defines various standard types, most importantly a number of unions
+ * used to access parts of larger types.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#ifndef INT_TYPES_H
+#define INT_TYPES_H
+
+#include "int_endianness.h"
+
+typedef      int si_int;
+typedef unsigned su_int;
+
+typedef          long long di_int;
+typedef unsigned long long du_int;
+
+typedef union
+{
+    di_int all;
+    struct
+    {
+#if _YUGA_LITTLE_ENDIAN
+        su_int low;
+        si_int high;
+#else
+        si_int high;
+        su_int low;
+#endif /* _YUGA_LITTLE_ENDIAN */
+    }s;
+} dwords;
+
+typedef union
+{
+    du_int all;
+    struct
+    {
+#if _YUGA_LITTLE_ENDIAN
+        su_int low;
+        su_int high;
+#else
+        su_int high;
+        su_int low;
+#endif /* _YUGA_LITTLE_ENDIAN */
+    }s;
+} udwords;
+
+#if __x86_64
+
+typedef int      ti_int __attribute__ ((mode (TI)));
+typedef unsigned tu_int __attribute__ ((mode (TI)));
+
+typedef union
+{
+    ti_int all;
+    struct
+    {
+#if _YUGA_LITTLE_ENDIAN
+        du_int low;
+        di_int high;
+#else
+        di_int high;
+        du_int low;
+#endif /* _YUGA_LITTLE_ENDIAN */
+    }s;
+} twords;
+
+typedef union
+{
+    tu_int all;
+    struct
+    {
+#if _YUGA_LITTLE_ENDIAN
+        du_int low;
+        du_int high;
+#else
+        du_int high;
+        du_int low;
+#endif /* _YUGA_LITTLE_ENDIAN */
+    }s;
+} utwords;
+
+static inline ti_int make_ti(di_int h, di_int l) {
+    twords r;
+    r.s.high = h;
+    r.s.low = l;
+    return r.all;
+}
+
+static inline tu_int make_tu(du_int h, du_int l) {
+    utwords r;
+    r.s.high = h;
+    r.s.low = l;
+    return r.all;
+}
+
+#endif /* __x86_64 */
+
+typedef union
+{
+    su_int u;
+    float f;
+} float_bits;
+
+typedef union
+{
+    udwords u;
+    double  f;
+} double_bits;
+
+typedef struct
+{
+#if _YUGA_LITTLE_ENDIAN
+    udwords low;
+    udwords high;
+#else
+    udwords high;
+    udwords low;
+#endif /* _YUGA_LITTLE_ENDIAN */
+} uqwords;
+
+typedef union
+{
+    uqwords     u;
+    long double f;
+} long_double_bits;
+
+#endif /* INT_TYPES_H */
+
diff -Nur libunwind/src/gcc_personality_v0/int_util.c libunwindn/src/gcc_personality_v0/int_util.c
--- libunwind/src/gcc_personality_v0/int_util.c	1970-01-01 08:00:00.000000000 +0800
+++ libunwindn/src/gcc_personality_v0/int_util.c	2014-06-25 11:59:12.934977792 +0800
@@ -0,0 +1,61 @@
+/* ===-- int_util.c - Implement internal utilities --------------------------===
+ *
+ *                     The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#include "int_util.h"
+#include "int_lib.h"
+
+/* NOTE: The definitions in this file are declared weak because we clients to be
+ * able to arbitrarily package individual functions into separate .a files. If
+ * we did not declare these weak, some link situations might end up seeing
+ * duplicate strong definitions of the same symbol.
+ *
+ * We can't use this solution for kernel use (which may not support weak), but
+ * currently expect that when built for kernel use all the functionality is
+ * packaged into a single library.
+ */
+
+#ifdef KERNEL_USE
+
+extern void panic(const char *, ...) __attribute__((noreturn));
+#ifndef _WIN32
+__attribute__((visibility("hidden")))
+#endif
+void compilerrt_abort_impl(const char *file, int line, const char *function) {
+  panic("%s:%d: abort in %s", file, line, function);
+}
+
+#elif __APPLE__
+
+/* from libSystem.dylib */
+extern void __assert_rtn(const char *func, const char *file, 
+                     int line, const char * message) __attribute__((noreturn));
+
+#ifndef _WIN32
+__attribute__((weak))
+__attribute__((visibility("hidden")))
+#endif
+void compilerrt_abort_impl(const char *file, int line, const char *function) {
+  __assert_rtn(function, file, line, "libcompiler_rt abort");
+}
+
+#else
+
+/* Get the system definition of abort() */
+#include <stdlib.h>
+
+#ifndef _WIN32
+__attribute__((weak))
+__attribute__((visibility("hidden")))
+#endif
+void compilerrt_abort_impl(const char *file, int line, const char *function) {
+  abort();
+}
+
+#endif
diff -Nur libunwind/src/gcc_personality_v0/int_util.h libunwindn/src/gcc_personality_v0/int_util.h
--- libunwind/src/gcc_personality_v0/int_util.h	1970-01-01 08:00:00.000000000 +0800
+++ libunwindn/src/gcc_personality_v0/int_util.h	2014-06-25 11:47:54.010002060 +0800
@@ -0,0 +1,29 @@
+/* ===-- int_util.h - internal utility functions ----------------------------===
+ *
+ *                     The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===-----------------------------------------------------------------------===
+ *
+ * This file is not part of the interface of this library.
+ *
+ * This file defines non-inline utilities which are available for use in the
+ * library. The function definitions themselves are all contained in int_util.c
+ * which will always be compiled into any compiler-rt library.
+ *
+ * ===-----------------------------------------------------------------------===
+ */
+
+#ifndef INT_UTIL_H
+#define INT_UTIL_H
+
+/** \brief Trigger a program abort (or panic for kernel code). */
+#define compilerrt_abort() compilerrt_abort_impl(__FILE__, __LINE__, \
+                                                 __FUNCTION__)
+
+void compilerrt_abort_impl(const char *file, int line,
+                           const char *function) __attribute__((noreturn));
+
+#endif /* INT_UTIL_H */

 

9,其他:

Apple还有自己的Blocks语法实现,以及基于Blocks的GCD(dispatch)等等。以及支持ObjectiveC-2.0语法的gnustep libobjc2运行时库等。

这些内容都可以自行查阅相关文档来尝试,有时间的话再补充吧。

评论(68) 阅读(9752)