怎样构建一个不依赖gcc/binutils的llvm/clang Linux工具链
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了怎样构建一个不依赖gcc/binutils的llvm/clang Linux工具链相关的知识,希望对你有一定的参考价值。
参考技术A 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
gcc 9.2 交叉工具链构建过程
1. 环境准备
1.1 host环境
ubuntu 16.04 64bit + gcc 5.4.0
1.2 target环境
aarch64 + linux + glibc
1.3 源码
binutils源码
url=git://sourceware.org/git/binutils-gdb.git
version=da3b036b57c0d409fc1fc3e25597fa13dc71baf5
gcc源码(9.2)
直接从arm官网下载,目前的源码版本是9.2
https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-a/downloads
linux kernel源码
url=git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
version=v4.20.13
glibc源码
url=git://sourceware.org/git/glibc.git
version=50f20fe506abb8853641006a7b90a81af21d7b91
一些依赖包根据需要安装,缺少的具体包在configure时会提示。
gcc源码依赖下面几个包,从arm官网下载的源码包里包含了这些依赖包的源码,在gcc源码目录里创建符号链接即可
cd gcc
ln -s ../mpfr-x.x.x mpfr
ln -s ../gmp-x.x.x gmp
ln -s ../mpc-x.x.x mpc
ln -s ../isl-x.x.x isl
x.x.x是版本号,若自己单独下载这些依赖包,不同的版本号可能会带来一些兼容性问题,所以直接下载arm的gcc源码好处是这些依赖包已经打包好,且经过编译验证没有问题的。
2. 构建步骤
设置环境变量
export TARGET=aarch64-xxx-linux-gnu
export PREFIX=/home/summit/projects/cross-toolchain/install
export SYSROOT=$PREFIX/$TARGET/libc
export PATH=$PREFIX/bin:$PATH
其中,xxx表示vendor,SYSROOT就是用于存放内核头文件和glibc头文件等。
2.1 构建binutils
Binutils源码主要用于构建交叉汇编器、链接器和其他工具,具体包括如下:
构建
mkdir build-binutils
cd build-binutils
../binutils-gdb/configure --prefix=$PREFIX --with-sysroot=$SYSROOT --with-build-sysroot=$SYSROOT --target=$TARGET
make -j4
make install
2.2 安装内核头文件
在内核源码目录执行
make ARCH=arm64 INSTALL_HDR_PATH=$SYSROOT/usr headers_install
安装后的文件内容如下图所示
2.3 第一次构建gcc
mkdir build-gcc-s1
cd build-gcc-s1
../gcc-arm-src-snapshot-9.2-2019.12/configure --prefix=$PREFIX --with-sysroot=$SYSROOT --with-build-sysroot=$SYSROOT --target=$TARGET --disable-libssp --disable-libquadmath --disable-threads --without-headers --with-newlib --disable-libmudflap --disable-bootstrap --disable-decimal-float --disable-libgomp --disable-libatomic --disable-libsanitizer --disable-plugins --disable-libitm --disable-libstdcxx --disable-libvtv --enable-languages=c,c++ --disable-shared
make -j4 all-gcc
make -j4 all-target-libgcc
make install-gcc
make install-target-libgcc
以前构建gcc第一阶段的时候,只需构建c语言即可,配置选项设置:
--enable-languages=c
从最近版本的glibc开始,一些组件需要c++构建,
因此在第一阶段需要使用下面的配置选项:
--disable-libstdcxx --disable-libvtv --enable-languages=c,c++
2.4 构建glibc
mkdir -p build-glibc
cd build-glibc
../glibc/configure --prefix=/usr --build=$MACHTYPE --host=$TARGET --target=$TARGET --with-headers=$SYSROOT/usr/include --without-gd --enable-obsolete-rpc --disable-profile libc_cv_forced_unwind=yes libc_cv_c_cleanup=yes --without-selinux --disable-werror
make -j4
make install_root=$SYSROOT install
注意,prefix参数的含义与一般的prefix的参数的含义不太一样。若按照构建gcc或binutils那样去设置该参数,会出现下面链接里的错误。
[1] https://gcc-help.gcc.gnu.narkive.com/zbYyAAYO/gcc-binutils-libc-cross-compiling-path-to-libc-so-6-malformed-in-ld
实际上,glibc考虑到了native和cross的构建场景,统一按照native的使用场景设置prefix即可。这样生成的glibc.so等库文件将$SYSROOT和prefix指定的路径拼接时不会出错。
2.5 第二次构建gcc
mkdir build-gcc-s2
cd build-gcc-s2
../gcc-arm-src-snapshot-9.2-2019.12/configure --prefix=$PREFIX --with-sysroot=$SYSROOT --with-build-sysroot=$SYSROOT --target=$TARGET --enable-languages=c,c++ --with-bugurl=https://bugs.linaro.org/ --enable-gnu-indirect-function --enable-shared --disable-libssp --disable-libmudflap --enable-checking=release --enable-fix-cortex-a53-843419 --with-pkgversion=‘GNU Toolchain for the A-profile Architecture 9.2-2019.12 (arm-9.10)‘
make j4
make install
至此,交叉编译工具链构建完毕。
以上是关于怎样构建一个不依赖gcc/binutils的llvm/clang Linux工具链的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Mac OS X 主机上为 MIPS 目标构建 GCC 4.8.x
无法安装 cargo-binutils 库:找不到工具。是不是安装了`gcc.exe`?