如何在Bazel+gcc/configure环境中控制RPATH的值?
在Bazel环境中使用foreign_cc封装项目依赖库的configure/make构建流程时,需要将RPATH设置为固定值。通过copts参数注入-Wl,--rpath,SOME_VALUE后,该参数虽被写入构建脚本的ASFLAGS、CFLAGS和CXXFLAGS,但生成的二进制文件RPATH包含额外前缀路径:
$ objdump -x bazel-bin/external/path/to/binary | grep PATH RPATH /opt/gcc-12.2.0/lib/../lib64:/long/path/inside/bazel/cache:SOME_VALUE
而在Bazel环境外,使用完全相同的环境变量执行configure和make install时,生成的二进制文件RUNPATH是预期的固定值:
$ objdump -x dest/usr/local/lib/binary |grep 'R.*PATH' RUNPATH SOME_VALUE
疑问:
- 导致RPATH值被拼接的原因是什么?
- Bazel构建脚本与外部构建的差异在哪里?
- 是否存在隐藏环境变量会在RPATH前添加工具链相关路径?
以下是构建脚本build_script.sh的相关部分:
ARFLAGS="rcsD" \ AR_FLAGS="rcsD" \ ASFLAGS="-U_FORTIFY_SOURCE -fstack-protector -Wall -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer -fno-canonical-system-headers -Wno-builtin-macro-redefined -D__DATE__=\"redacted\" -D__TIMESTAMP__=\"redacted\" -D__TIME__=\"redacted\" -Wl,--rpath,SOME_VALUE" \ CFLAGS="-U_FORTIFY_SOURCE -fstack-protector -Wall -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer -fno-canonical-system-headers -Wno-builtin-macro-redefined -D__DATE__=\"redacted\" -D__TIMESTAMP__=\"redacted\" -D__TIME__=\"redacted\" -Wl,--rpath,SOME_VALUE" \ CXXFLAGS="-U_FORTIFY_SOURCE -fstack-protector -Wall -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer -std=c++0x -fno-canonical-system-headers -Wno-builtin-macro-redefined -D__DATE__=\"redacted\" -D__TIMESTAMP__=\"redacted\" -D__TIME__=\"redacted\" -Wl,--rpath,SOME_VALUE" \ LDFLAGS="-fuse-ld=gold -Wl,-no-as-needed -Wl,-z,relro,-z,now -B/usr/bin -pass-exit-codes -lstdc++ -lm -L$EXT_BUILD_DEPS/openssl/lib" \ AR="/usr/bin/ar" \ CC="/usr/bin/gcc" \ CXX="/usr/bin/gcc" \ RANLIB=":" \ CPPFLAGS="-I$EXT_BUILD_DEPS/openssl/include" \ ./configure $EXT_BUILD_ROOT/bazel-out/k8-opt-exec-2B5CBBC6/bin/external/rules_foreign_cc/toolchains/make/bin/make install
核心原因
Bazel工具链自动注入链接路径
Bazel为保障构建可复现性,会自动向编译器/链接器注入工具链相关的RPATH(如GCC的依赖库路径)以及Bazel缓存中的依赖路径。这些路径通过隐藏的环境变量或工具链配置追加到链接参数中,你手动添加的--rpath是附加在已有路径之后,而非替换。RPATH与RUNPATH的行为差异
外部构建生成的是RUNPATH,而Bazel构建生成的是RPATH。两者的查找优先级和处理逻辑不同:Bazel默认可能强制使用RPATH,或者configure在Bazel隔离环境下检测到链接器行为变化,选择生成RPATH而非RUNPATH。Bazel对构建环境的隔离与变量覆盖
尽管你看到的build_script.sh变量与外部一致,但Bazel在执行脚本前已设置前置环境变量,比如LDFLAGS会被预先添加工具链路径,你的配置属于追加而非替换。此外,Bazel使用的是自身封装的make工具(路径为external/rules_foreign_cc/toolchains/make/bin/make),与系统make存在行为差异,可能影响链接参数的处理。
解决方法
- 直接在LDFLAGS中设置RPATH并强制替换:将
--rpath写入LDFLAGS,使用=明确指定值,比如-Wl,--rpath=SOME_VALUE;若需要强制使用RUNPATH,可追加-Wl,--disable-new-dtags。 - 使用linkopts而非copts传递链接参数:
copts是编译阶段参数,链接阶段参数应通过linkopts传递,避免编译参数被误用于链接环节。 - 强制configure生成RUNPATH:在configure命令中添加
--enable-runpath或--disable-rpath,明确指定使用RUNPATH,再手动设置你的目标路径。 - 检查Bazel工具链配置:查看
crosstool_top相关配置,是否存在强制添加RPATH的规则,按需调整工具链参数。
内容的提问来源于stack exchange,提问作者frans

