| # llvm-libgcc |
| |
| :::{contents} |
| :local: true |
| ::: |
| |
| **Note that these instructions assume a Linux and bash-friendly environment. |
| YMMV if you’re on a non Linux-based platform.** |
| |
| (introduction)= |
| |
| ## Motivation |
| |
| Enabling libunwind as a replacement for libgcc on Linux has proven to be |
| challenging since libgcc_s.so is a required dependency in the [Linux standard |
| base][5]. Some software is transitively dependent on libgcc because glibc makes |
| hardcoded calls to functions in libgcc_s. For example, the function |
| `__GI___backtrace` eventually makes its way to a [hardcoded dlopen to libgcc_s' |
| _Unwind_Backtrace][1]. Since `libgcc_{eh.a,s.so}` and libunwind have the same ABI, |
| but different implementations, the two libraries end up [cross-talking, which |
| ultimately results in a segfault][2]. |
| |
| To solve this problem, libunwind needs libgcc "front" that is, link the |
| necessary functions from compiler-rt and libunwind into an archive and shared |
| object that advertise themselves as `libgcc.a`, `libgcc_eh.a`, and |
| `libgcc_s.so`, so that glibc’s baked calls are diverted to the correct objects |
| in memory. Fortunately for us, compiler-rt and libunwind use the same ABI as the |
| libgcc family, so the problem is solvable at the llvm-project configuration |
| level: no program source needs to be edited. Thus, the end result is for a |
| distro manager to configure their LLVM build with a flag that indicates they |
| want to archive compiler-rt/unwind as libgcc. We achieve this by compiling |
| libunwind with all the symbols necessary for compiler-rt to emulate the libgcc |
| family, and then generate symlinks named for our "libgcc" that point to their |
| corresponding libunwind counterparts. |
| |
| ## Alternatives |
| |
| We alternatively considered patching glibc so that the source doesn't directly |
| refer to libgcc, but rather *defaults* to libgcc, so that a system preferring |
| compiler-rt/libunwind can point to these libraries at the config stage instead. |
| Even if we modified the Linux standard base, this alternative won't work because |
| binaries that are built using libgcc will still end up having cross-talk between |
| the differing implementations. |
| |
| (target-audience)= |
| |
| ## Target audience |
| |
| llvm-libgcc is not for the casual LLVM user. It is intended to be used by distro |
| managers who want to replace libgcc with compiler-rt and libunwind, but cannot |
| fully abandon the libgcc family (e.g. because they are dependent on glibc). Such |
| managers must have worked out their compatibility requirements ahead of using |
| llvm-libgcc. |
| |
| (cmake-options)= |
| |
| ## CMake options |
| |
| :::{option} `LLVM_LIBGCC_EXPLICIT_OPT_IN` |
| |
| **Required** |
| |
| Since llvm-libgcc is such a fundamental, low-level component, we have made it |
| difficult to accidentally build, by requiring you to set an opt-in flag. |
| ::: |
| |
| ### Building llvm-libgcc |
| |
| The first build tree is a mostly conventional build tree and gets you a Clang |
| build with these compiler-rt symbols exposed. |
| |
| ```bash |
| # Assumes $(PWD) is /path/to/llvm-project |
| $ cmake -GNinja -S llvm -B build-primary \ |
| -DCMAKE_BUILD_TYPE=Release \ |
| -DCMAKE_CROSSCOMPILING=On \ |
| -DCMAKE_INSTALL_PREFIX=/tmp/aarch64-unknown-linux-gnu \ |
| -DLLVM_ENABLE_PROJECTS='clang' \ |
| -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;llvm-libgcc" \ |
| -DLLVM_TARGETS_TO_BUILD=AArch64 \ |
| -DLLVM_DEFAULT_TARGET_TRIPLE=aarch64-unknown-linux-gnu \ |
| -DLLVM_LIBGCC_EXPLICIT_OPT_IN=Yes |
| $ ninja -C build-primary install |
| ``` |
| |
| It's very important to notice that neither `compiler-rt`, nor `libunwind`, |
| are listed in `LLVM_ENABLE_RUNTIMES`. llvm-libgcc makes these subprojects, and |
| adding them to this list will cause you problems due to there being duplicate |
| targets. As such, configuring the runtimes build will reject explicitly mentioning |
| either project with `llvm-libgcc`. |
| |
| To avoid issues when building with `-DLLVM_ENABLE_RUNTIMES=all`, `llvm-libgcc` |
| is not included, and all runtimes targets must be manually listed. |
| |
| \## Verifying your results |
| |
| This gets you a copy of libunwind with the libgcc symbols. You can verify this |
| using `readelf`. |
| |
| ```bash |
| $ llvm-readelf -W --dyn-syms "${LLVM_LIBGCC_SYSROOT}/lib/libunwind.so" | grep FUNC | grep GCC_3.0 |
| ``` |
| |
| Roughly sixty symbols should appear, all suffixed with `@@GCC_3.0`. You can |
| replace `GCC_3.0` with any of the supported version names in the version |
| script you’re exporting to verify that the symbols are exported. |
| |
| (supported-platforms)= |
| |
| ## Supported platforms |
| |
| llvm-libgcc currently supports the following target triples: |
| |
| - `aarch64-*-*-*` |
| - `armv7a-*-*-gnueabihf` |
| - `i386-*-*-*` |
| - `x86_64-*-*-*` |
| |
| If you would like to support another triple (e.g. `powerpc64-*-*-*`), you'll |
| need to generate a new version script, and then edit `lib/gcc_s.ver`. |
| |
| ### Generating a new version script |
| |
| To generate a new version script, we need to generate the list of symbols that |
| exist in the set (`clang-builtins.a` ∪ `libunwind.a`) ∩ `libgcc_s.so.1`. |
| The prerequisites for generating a version script are a binaries for the three |
| aforementioned libraries targeting your architecture (without having built |
| llvm-libgcc). |
| |
| Once these libraries are in place, to generate a new version script, run the |
| following command. |
| |
| ```bash |
| /path/to/llvm-project |
| $ export ARCH=powerpc64 |
| $ llvm/tools/llvm-libgcc/generate_version_script.py \ |
| --compiler_rt=/path/to/libclang_rt.builtins-${ARCH}.a \ |
| --libunwind=/path/to/libunwind.a \ |
| --libgcc_s=/path/to/libgcc_s.so.1 \ |
| --output=${ARCH} |
| ``` |
| |
| This will generate a new version script a la |
| `/path/to/llvm-project/llvm/tools/llvm-libgcc/gcc_s-${ARCH}.ver`, which we use |
| in the next section. |
| |
| ### Editing `lib/gcc_s.ver` |
| |
| Our freshly generated version script is unique to the specific architecture that |
| it was generated for, but a lot of the symbols are shared among many platforms. |
| As such, we don't check in unique version scripts, but rather have a single |
| version script that's run through the C preprocessor to prune symbols we won't |
| be using in `lib/gcc_s.ver`. |
| |
| Working out which symbols are common is largely a manual process at the moment, |
| because some symbols may be shared across different architectures, but not in |
| the same versions of libgcc. As such, a symbol appearing in `lib/gcc_s.ver` |
| doesn't guarantee that the symbol is available for our new architecture: we need |
| to verify that the versions are the same, and if they're not, add the symbol to |
| the new version section, with the appropriate include guards. |
| |
| There are a few macros that aim to improve readability. |
| |
| - `ARM_GNUEABIHF`, which targets exactly `arm-*-*-gnueabihf`. |
| - `GLOBAL_X86`, which should be used to target both x86 and x86_64, regardless |
| of the triple. |
| - `GLOBAL_32BIT`, which is be used to target 32-bit platforms. |
| - `GLOBAL_64BIT`, which is be used to target 64-bit platforms. |