如何使用clang+llvm+binutils+newlib+gdb搭建交叉编译环境

如题所述

  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++?)
温馨提示:答案为网友推荐,仅供参考