构建使用musl libc的LLVM系列函数库

由于科研需求, 我需要构建一份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。

参考资料

  1. https://s3.amazonaws.com/connect.linaro.org/bkk19/presentations/bkk19-210.pdf
  2. https://12101111.github.io/llvm-cross-compile/
  3. https://llvm.org/docs/HowToCrossCompileBuiltinsOnArm.html
  4. https://libcxx.llvm.org/docs/UsingLibcxx.html

发表评论

注意 - 你可以用以下 HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

:wink: :twisted: :roll: :oops: :mrgreen: :lol: :idea: :evil: :cry: :arrow: :?: :-| :-x :-o :-P :-D :-? :) :( :!: 8-O 8)

本文链接:https://twd2.me/archives/14624QrCode