由于科研需求, 我需要构建一份musl libc以及基于musl libc的RISC-V平台的LLVM系列的函数库。其中, 这些函数库包括编译器运行时库compiler-rt (对应GCC系列的libgcc)、unwind库libunwind (对应GCC系列的libgcc_s)、C++库libc++ (对应GCC系列的libstdc++)以及libc++abi (对应GCC系列的libsupc++)。
构建之前需要首先准备好编译好的LLVM编译器, 可以参考这篇文章。
整个构建过程分为如下几步, 其中所需的命令请见下文提供的脚本, 此处记录每一步的作用和意义:
一、安装Linux的头文件
之后在编译libc++时, 需要用到Linux的头文件, 因此需要安装。此处的”安装”是指将相应文件复制到sysroot目录中, 下同。
二、安装musl libc的头文件
后续编译compiler-rt时, 需要用到。compiler-rt和libc似乎是相互耦合的。
三、编译并安装compiler-rt
这一步需要说明的是, 由于此时编译器无法编译链接生成完整的可执行程序, cmake无法正确检测编译器是否可用以及检测指针的大小, 因此需要特别加入下列参数:
-DCMAKE_C_COMPILER_WORKS=1 \ -DCMAKE_CXX_COMPILER_WORKS=1 \ -DCMAKE_SIZEOF_VOID_P=8 \
前两个参数在后续也会出现。
另外, 由于一个bug, 在rv64imac
等没有硬件浮点支持而需要使用软件浮点的架构上编译, 还需要定义-D__SOFT_FP__
。
这一步能够将compiler-rt编译并安装在sysroot中。然而, 实践表明, 之后编译器在工作时, 无论sysroot路径怎么设置, 其都会使用编译器安装目录下的compiler-rt。编译器安装目录下确实有文件名包含”riscv64″的compiler-rt库, 但是我使用objdump分析这些已经存在的库文件, 发现其实际上为x86-64指令集的, 十分迷惑。因此, 脚本此处还会将sysroot中的compiler-rt复制(覆盖)到编译器安装目录中。将这一步的cmake命令的sysroot (安装目标)参数直接设置为编译器安装目录似乎也可以。
四、编译并安装musl libc
直接编译并安装即可。此处还可以指定编译器使用上一步编译生成的compiler-rt。不过, 当上一步正确完成后, 此时编译器应当能够自行找到并使用正确的compiler-rt。
至此, C语言相关的函数库都编译好了, 包括compiler-rt以及musl libc。此时, 编译器可以正常编译C程序了, 后续的cmake也不再需要手动指定CMAKE_C_COMPILER_WORKS
参数了。另外, 由于其已被安装到sysroot, 后续编译过程也指定了sysroot参数, 因此在后面的编译步骤中, 使用的就是本步编译的musl libc, 而不是默认的了。
五、编译并安装libunwind
直接编译并安装即可。这一步骤需要额外指定如下参数来跳过一些会失败的检查:
-DLLVM_COMPILER_CHECKED=ON \
六、编译并安装libc++abi
直接编译并安装即可。
七、编译并安装libc++
这一步需要通过cmake的LIBCXX_CXX_ABI_INCLUDE_PATHS
参数指定使用上一步被编译的C++ ABI对应的头文件, 通过LIBCXX_CXX_ABI_LIBRARY_PATH
指定上一步编译好的C++ ABI库, 之后正常编译并安装即可。此外, 本步骤还设置了LIBCXX_ENABLE_STATIC_ABI_LIBRARY
参数, 使得libc++abi被静态链接到libc++中, 方便后续使用。这一参数是可选的, 若未设置该参数, 后续使用时要相应修改编译参数。
上述这些步骤编译的过程中都使用的是已经编译好的clang以及clang++, 因此还可以载入自己编写的LLVM Pass对这些函数库, 特别是libc、libc++以及libc++abi等, 进行自己需要的代码插桩、变换等高级操作。
至此, C++语言相关的函数库也都编译好了。此后, clang和clang++可以按如下命令使用:
clang -static -rtlib=compiler-rt -march=rv64imac --sysroot=/xxx/sysroot xxx.c clang++ -static -rtlib=compiler-rt -stdlib=libc++ -unwindlib=libunwind -march=rv64imac --sysroot=/xxx/sysroot -stdlib++-isystem /xxx/sysroot/include/c++/v1 xxx.cpp
上文提到的脚本:
#!/bin/bash set -e MAKE_ARGS=-j16 LLVM=$(dirname $(readlink -f $0))/llvm-project SYSROOT= # TODO ARCH=riscv TARGET_TRIPLE="riscv64-unknown-linux-gnu" CROSS_COMPILE="${TARGET_TRIPLE}-" CFLAGS="-O2 --gcc-toolchain=/opt/riscv -march=rv64imac" LDFLAGS="-z separate-code" echo "ARCH = ${ARCH}" echo "TARGET_TRIPLE = ${TARGET_TRIPLE}" echo "CROSS_COMPILE = ${CROSS_COMPILE}" echo "CFLAGS = ${CFLAGS}" echo "LDFLAGS = ${LDFLAGS}" echo "SYSROOT = ${SYSROOT}" # Clean. echo "Cleaning..." pushd musl-* || (echo "musl-libc (musl-*) not found" && exit 1) make clean popd pushd llvm-project || (echo "LLVM source code (llvm-project) not found" && exit 1) rm -r build-* || true popd # Install Linux headers. echo "Installing Linux headers..." pushd linux-* || (echo "Linux source code (linux-*) not found" && exit 1) make ARCH=riscv INSTALL_HDR_PATH=${SYSROOT} headers_install popd # Install musl libc headers. echo "Installing musl libc headers..." pushd musl-* ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} CC="clang" CFLAGS="${CFLAGS} ${LDFLAGS}" ./configure --prefix=${SYSROOT} make install-headers popd # Build and install compiler runtime. echo "Building and installing compiler runtime..." pushd llvm-project mkdir build-compiler-rt pushd build-compiler-rt cmake -G "Unix Makefiles" \ -DCOMPILER_RT_BUILD_BUILTINS=ON \ -DCOMPILER_RT_INCLUDE_TESTS=OFF \ -DCOMPILER_RT_BUILD_SANITIZERS=OFF \ -DCOMPILER_RT_BUILD_XRAY=OFF \ -DCOMPILER_RT_BUILD_LIBFUZZER=OFF \ -DCOMPILER_RT_BUILD_PROFILE=OFF \ -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \ -DCMAKE_C_COMPILER=clang \ -DCMAKE_CXX_COMPILER=clang++ \ -DCMAKE_ASM_COMPILER_TARGET="${TARGET_TRIPLE}" \ -DCMAKE_C_COMPILER_TARGET="${TARGET_TRIPLE}" \ -DCMAKE_CXX_COMPILER_TARGET="${TARGET_TRIPLE}" \ -DCMAKE_ASM_FLAGS="${CFLAGS}" \ -DCMAKE_C_FLAGS="${CFLAGS} -D__SOFT_FP__" \ -DCMAKE_CXX_FLAGS="${CFLAGS}" \ -DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}" \ -DCMAKE_SYSROOT=${SYSROOT} \ -DCMAKE_INSTALL_PREFIX=${SYSROOT} \ -DCMAKE_C_COMPILER_WORKS=1 \ -DCMAKE_CXX_COMPILER_WORKS=1 \ -DCMAKE_SIZEOF_VOID_P=8 \ ../compiler-rt make ${MAKE_ARGS} make install popd popd # Copy compiler-rt to ${LLVM}/build/lib/clang/*/lib/linux/, # because clang will use compiler-rt in that path. cp ${SYSROOT}/lib/linux/* ${LLVM}/build/lib/clang/*/lib/linux/ # Build and install musl libc. echo "Building and installing musl libc..." pushd musl-* ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} CC="clang" CFLAGS="${CFLAGS} ${LDFLAGS}" LIBCC="${SYSROOT}/lib/linux/libclang_rt.builtins-riscv64.a" ./configure --prefix=${SYSROOT} make ${MAKE_ARGS} make install popd pushd llvm-project # Build and install libunwind. echo "Building and installing libunwind..." mkdir build-libunwind pushd build-libunwind cmake -G "Unix Makefiles" \ -DLIBUNWIND_USE_COMPILER_RT=YES \ -DCMAKE_C_COMPILER=clang \ -DCMAKE_CXX_COMPILER=clang++ \ -DCMAKE_ASM_COMPILER_TARGET="${TARGET_TRIPLE}" \ -DCMAKE_C_COMPILER_TARGET="${TARGET_TRIPLE}" \ -DCMAKE_CXX_COMPILER_TARGET="${TARGET_TRIPLE}" \ -DCMAKE_ASM_FLAGS="${CFLAGS}" \ -DCMAKE_C_FLAGS="${CFLAGS}" \ -DCMAKE_CXX_FLAGS="${CFLAGS}" \ -DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}" \ -DCMAKE_SYSROOT=${SYSROOT} \ -DCMAKE_INSTALL_PREFIX=${SYSROOT} \ -DCMAKE_CXX_COMPILER_WORKS=1 \ -DLLVM_COMPILER_CHECKED=ON \ ../libunwind make ${MAKE_ARGS} make install popd # Build and install libc++abi. echo "Building and installing libc++abi..." mkdir build-libcxxabi pushd build-libcxxabi cmake -G "Unix Makefiles" \ -DLIBCXXABI_USE_COMPILER_RT=YES \ -DLIBCXXABI_USE_LLVM_UNWINDER=YES \ -DLIBCXXABI_LIBCXX_PATH="${LLVM}/libcxx" \ -DCMAKE_C_COMPILER=clang \ -DCMAKE_CXX_COMPILER=clang++ \ -DCMAKE_ASM_COMPILER_TARGET="${TARGET_TRIPLE}" \ -DCMAKE_C_COMPILER_TARGET="${TARGET_TRIPLE}" \ -DCMAKE_CXX_COMPILER_TARGET="${TARGET_TRIPLE}" \ -DCMAKE_ASM_FLAGS="${CFLAGS}" \ -DCMAKE_C_FLAGS="${CFLAGS}" \ -DCMAKE_CXX_FLAGS="${CFLAGS}" \ -DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}" \ -DCMAKE_SYSROOT=${SYSROOT} \ -DCMAKE_INSTALL_PREFIX=${SYSROOT} \ -DCMAKE_CXX_COMPILER_WORKS=1 \ ../libcxxabi make ${MAKE_ARGS} make install popd # Build and install libc++. echo "Building and installing libc++..." mkdir build-libcxx pushd build-libcxx cmake -G "Unix Makefiles" \ -DLIBCXX_CXX_ABI=libcxxabi \ -DLIBCXX_USE_COMPILER_RT=YES \ -DLIBCXX_HAS_MUSL_LIBC=ON \ -DLIBCXX_CXX_ABI_INCLUDE_PATHS="${LLVM}/libcxxabi/include" \ -DLIBCXX_CXX_ABI_LIBRARY_PATH="${LLVM}/build-libcxxabi/lib" \ -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON \ -DCMAKE_C_COMPILER=clang \ -DCMAKE_CXX_COMPILER=clang++ \ -DCMAKE_ASM_COMPILER_TARGET="${TARGET_TRIPLE}" \ -DCMAKE_C_COMPILER_TARGET="${TARGET_TRIPLE}" \ -DCMAKE_CXX_COMPILER_TARGET="${TARGET_TRIPLE}" \ -DCMAKE_ASM_FLAGS="${CFLAGS}" \ -DCMAKE_C_FLAGS="${CFLAGS}" \ -DCMAKE_CXX_FLAGS="${CFLAGS}" \ -DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}" \ -DCMAKE_SYSROOT=${SYSROOT} \ -DCMAKE_INSTALL_PREFIX=${SYSROOT} \ -DCMAKE_CXX_COMPILER_WORKS=1 \ ../libcxx make ${MAKE_ARGS} make install popd
在使用该脚本前, 同目录应该准备好LLVM源代码(存于llvm-project)、Linux源代码(存于linux-x.y.z)、以及musl libc源代码(存于musl-x.y.z)。同时, GCC工具链所在目录(如/opt/riscv/bin)以及编译好的LLVM编译器所在目录(如/xxx/llvm-project/build/bin)都应当已被加入PATH。
参考资料
发表评论