GCC Multilib
Table of Contents
1. GCC Multilib
1.1. gcc
gcc 需要解决两个和 multilib 相关的问题:
- 使用 gcc 时需要根据 march/mabi 找到对应的 multilib dir, 以找到正确的 startfile/libgcc
- 编译 gcc 时需要编译出前面用到的 multilib dir
1.1.1. 运行时查找 multilib dir
解决问题 1 依赖于 makefile 中定义的几个变量:
- MULTILIB_OPTIONS
- MULTILIB_DIRNAMES
- MULTILIB_REQUIRED
- MULTILIB_REUSE
这几个变量主要决定了:
- 针对哪些 march/mabi 的组合需要编译出 startfile/libgcc
- 某个 march/mabi 的组合需要使用哪个 multilib dir
对 riscv 来说, 定义这些变量比较繁锁, gcc 提供了一个 multilib-generator 脚本来生成这些变量. config.gcc 中使用 multilib-generator 针对不同的 target 预先生成了几套例如 t-linux-multilib
, t-elf-multilib
等, 用户可以通过
--with-multilib-generator
向 multilib-generator 传递特定的参数, 生成自己需要的配置
例如:
# This file was generated by multilib-generator with the command: # ./multilib-generator rv32imac-ilp32-rv32ima,rv32imaf,rv32imafd,rv32imafc,rv32imafdc- # rv32imafdc-ilp32d-rv32imafd- rv64imac-lp64-rv64ima,rv64imaf,rv64imafd,rv64imafc,rv64imafdc- # rv64imafdc-lp64d-rv64imafd- MULTILIB_OPTIONS =\ march=rv32imac/march=rv32ima/march=rv32imaf/march=rv32imafd/march=rv32imafc/\ march=rv32imafdc/march=rv32g/march=rv32gc/march=rv64imac/march=rv64ima/march=rv64imaf/\ march=rv64imafd/march=rv64imafc/march=rv64imafdc/march=rv64g/march=rv64gc \ mabi=ilp32/mabi=ilp32d/mabi=lp64/mabi=lp64d # MULTILIB_DIRNAMES 与 MULTILIB_OPTIONS 是一一对应的 MULTILIB_DIRNAMES = rv32imac \ rv32ima \ rv32imaf \ rv32imafd \ ... \ rv64gc \ ilp32 \ ilp32d \ lp64 \ lp64d # MULTILIB_REQUIRED 表示仅针对以下组合生成 multlib, 而不是针对 MULTILIB_OPTIONS # 中的每个组合(cartesian product) 都生成 multilib MULTILIB_REQUIRED = march=rv32imac/mabi=ilp32 \ march=rv32imafdc/mabi=ilp32d \ march=rv64imac/mabi=lp64 \ march=rv64imafdc/mabi=lp64d # rv32ima/ilp32 会 fallback 到 rv32imac/ilp32 MULTILIB_REUSE = march.rv32imac/mabi.ilp32=march.rv32ima/mabi.ilp32 \ march.rv32imac/mabi.ilp32=march.rv32imaf/mabi.ilp32 \ march.rv32imac/mabi.ilp32=march.rv32imafd/mabi.ilp32 \ ... \ march.rv64imafdc/mabi.lp64d=march.rv64imafd/mabi.lp64d \ march.rv64imafdc/mabi.lp64d=march.rv64gc/mabi.lp64d \ march.rv64imafdc/mabi.lp64d=march.rv64g/mabi.lp64d
通过 gcc -print-multi-os-directory
可以确定运行时 march/mabi 会找到哪个
multilib dir.
以上 makefile 中定义的的 MULTILIB_XXX 做为运行时参数会通过 spec 保存在 gcc 中,
gcc 在运行时查找 startfile(通过 find_file
)或构造 library path (-L xxx
) 时, 会处理这些 spec:
- 先通过
add_prefix
确定startfile_prefixes
, 包括:- gcc -B 指定的 path
- SYSROOT
- GCC_EXEC_PREFIX
- …
- 通过
set_multilib_dir
解析 spec 确定 multilib dir for_each_path
会在startfile_prefixes
的路径后加上 multilib dir 得到最终的路径, 例如<SYSROOT>/lib32/ilp32
1.1.1.1. startfile_prefix_spec
startfile_prefixes 包含了硬编码的 startfile_prefix_spec
#define STARTFILE_PREFIX_SPEC \ "/lib" XLEN_SPEC "/" ABI_SPEC "/ " \ "/usr/lib" XLEN_SPEC "/" ABI_SPEC "/ " \ "/lib/ " \ "/usr/lib/ " #define XLEN_SPEC \ "%{march=rv32*:32}" \ "%{march=rv64*:64}" \ #define ABI_SPEC \ "%{mabi=ilp32:ilp32}" \ "%{mabi=ilp32e:ilp32e}" \ "%{mabi=ilp32f:ilp32f}" \ "%{mabi=ilp32d:ilp32d}" \ "%{mabi=lp64:lp64}" \ "%{mabi=lp64f:lp64f}" \ "%{mabi=lp64d:lp64d}" \
gcc 会把 sysroot/lib32/ilp32e 加入到 startfile_prefixes
if (*startfile_prefix_spec != 0 && do_spec_2(startfile_prefix_spec, NULL) == 0 && do_spec_1(" ", 0, NULL) == 0) { for (const char *arg : argbuf) add_sysrooted_prefix( &startfile_prefixes, arg, "BINUTILS", PREFIX_PRIORITY_LAST, 0, 1); }
由于 multlib dir 会被加到 startfile_prefixes 之后, 导致 print-search-dir 时有时会看到这种奇怪的 path: sysroot/lib32/ilp32e/lib32/ilp32
. 通过测试,
startfile_prefix_spec
看起来并没有什么用处, gcc 通过 multilib spec 在 sysroot 或
gcc_exec_prefix 中能正常找到 startfile
1.1.1.2. linker_spec
dynamic linker 属于 libc, 但它有不一样的 multilib 配置
- 它的位置在 sysroot/lib/ld-linux-{march}-{mabi}.so.1
gcc 在 linker_spec 硬编码了 dynmaic linker 的位置
#define GLIBC_DYNAMIC_LINKER "/lib/ld-linux-riscv" XLEN_SPEC "-" ABI_SPEC ".so.1"
1.1.2. 编译出 multilib dir
编译完 gcc 后, 通过 gcc -print-multi-lib
可以输出 march/mabi 对应的 multilib
dir, 例如:
.; lib32/ilp32;@march=rv32imac@mabi=ilp32 lib32/ilp32d;@march=rv32imafdc@mabi=ilp32d lib64/lp64;@march=rv64imac@mabi=lp64 lib64/lp64d;@march=rv64imafdc@mabi=lp64d
实际就是 MULTILIB_REQUIRED
. 然后编译系统会解析这个输出, 为 libgcc 等构造多个
build 目录, 并在对应的 Makefile 中生成 march=rv32imac
这些 flag, 例如:
riscv64-unknown-linux-gnu/lib32/ilp32/libgcc/Makefile riscv64-unknown-linux-gnu/lib32/ilp32d/libgcc/Makefile riscv64-unknown-linux-gnu/lib64/lp64/libgcc/Makefile riscv64-unknown-linux-gnu/lib64/lp64d/libgcc/Makefile riscv64-unknown-linux-gnu/libgcc/Makefile
相关代码主要在 config-ml.in
中
1.1.3. 总结
- makefile 指定的 MULTILIB_XXX 保存在 gcc spec 中
- gcc 运行时主要通过
multilib_reuse
spec 确定 march/mabi 对应的 multilib dir, 查找 startfile 或生成 library path 时会考虑这个 multilib dir - 编译 startfile/libgcc 时通过
gcc --print-multi-lib
确定需要输出的 multilib dir 和 flag
1.2. libc
1.2.1. glibc
glibc 并没有像 gcc 一样内置 multilib 支持, riscv-gnu-toolchain 是通过主动编译出多个 glibc 并安装到对应的目录实现 multilib
$> cat configure ... if test "x$enable_multilib" != xno; then : glibc_multilib_names="rv32imac-ilp32 rv32imafdc-ilp32d rv64imac-lp64 rv64imafdc-lp64d" else glibc_multilib_names="$with_arch-$with_abi" fi ... $> cat Makefile.in GLIBC_MULTILIB_NAMES := @glibc_multilib_names@ build-libc: $(addprefix stamps/build-glibc-linux-,$(GLIBC_MULTILIB_NAMES)) stamps/build-glibc-linux-%: ifeq ($(MULTILIB_FLAGS),--enable-multilib) $(eval $@_ARCH := $(word 4,$(subst -, ,$@))) $(eval $@_ABI := $(word 5,$(subst -, ,$@))) else $(eval $@_ARCH := ) $(eval $@_ABI := ) endif $(eval $@_LIBDIRSUFFIX := $(if $($@_ABI),$(shell echo $($@_ARCH) | sed 's/.*rv\([0-9]*\).*/\1/')/$($@_ABI),)) $(eval $@_CFLAGS := $(if $($@_ABI),-march=$($@_ARCH) -mabi=$($@_ABI),)) $(eval $@_LIBDIROPTS := $(if $@_LIBDIRSUFFIX,--libdir=/usr/lib$($@_LIBDIRSUFFIX) libc_cv_slibdir=/lib$($@_LIBDIRSUFFIX) libc_cv_rtlddir=/lib,)) cd $(notdir $@) && \ .. \ CFLAGS="$(CFLAGS_FOR_TARGET) -O2 $($@_CFLAGS)" \ $</configure \ .. \ $($@_LIBDIROPTS)
可见 riscv-gnu-toolchain 会固定的编译几个 glibc 的 multilib, 和编译 gcc 时使用的 multilib 配置无关
1.2.2. newlib
newlib 的 build 系统使用了和 gcc 相同的 config-ml.in, 因此它依赖于 gcc
--print-multi-lib
从而能保证与 gcc 的 multilib 配置一致
1.3. debug
gcc --print-search-dirs gcc --print-file-name crt1.o gcc --print-multi-lib gcc --print-multi-os-directory gcc -v