Cross toolchain under linux

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

cjacker posted @ 2014年9月10日 16:32 in 代码 , 5451 阅读

本文不讲原理,只讲步骤。需要了解什么是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运行时库等。

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

Avatar_small
说:
2014年9月11日 14:35

好像 if (D.CCCIsCXX()) 这一行应该删除?
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");


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter