【问题标题】:how to specify alternate linker command when linking with 'cc'与“cc”链接时如何指定备用链接器命令
【发布时间】:2016-11-07 17:22:29
【问题描述】:

当您使用cc(1) 链接程序时,它将调用默认链接器命令。例如,您的编译器版本可能已构建为在类 unix 平台上默认使用 /usr/bin/ld。

有没有办法指定cc(1)(或c++(1))应该使用的不同链接器命令(例如,/usr/local/bin/ld 而不是/usr/bin/ld)?我主要对 gcc 和 clang 感兴趣。

我不是在寻找涉及单独运行各种编译步骤(例如,预处理、编译、汇编、链接)的方法。

例如,我希望这样的事情可以完成这项工作:

env LD=/usr/local/bin/ld cc foo.c -o foo

但这不适用于 gcc 或 clang。当然,如果你有一个先构建目标文件的 makefile,然后调用 ${LD} 进行链接(例如,env LD=/usr/local/bin/ld make

更新(可能的动机之一):使用与默认链接器不同的链接器轻松测试。例如,如果能够做到这一点,那就太好了:

cc --linker=/usr/local/bin/ld foo.c -o foo

相反,您必须生成目标文件,运行 cc -v 以找出 ld 的参数,手动运行您想要的带有这些参数的 ld

cc -c foo.c
cc -v foo.c -o /dev/null

现在查看链接器调用并手动复制/粘贴替换链接器和临时目标文件。像这样(示例取自 Fedora 23 上的测试),您将 /usr/libexec/gcc/x86_64-redhat-linux/5.3.1/collect2 替换为 /usr/local/bin/ld(尽管它与 collect2完全相同):

/usr/local/bin/ld -plugin /usr/libexec/gcc/x86_64-redhat-linux/5.3.1/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-redhat-linux/5.3.1/lto-wrapper -plugin-opt=-fresolution=/tmp/jhein/ccM2XKIg.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o c /usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/5.3.1/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/5.3.1 -L/usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../.. c.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/5.3.1/crtend.o /usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64/crtn.o

如您所见,这并不容易。请注意,gcc 信息页面中有关于 collect2 如何查找链接器程序的文档。但是根据那些文档,它首先看起来不是环境变量或您可以在命令行上指定的东西(例如,--linker)。文档说它看起来 first 是“硬编码的链接器文件名”。如果该文档是正确的,要强制它不使用该链接器(即欺骗它),您必须重命名默认链接器(例如,sudo mv /usr/bin/ld /usr/bin/ld.tmp-disable)。

更新 2:使用 -B 似乎可以很好地满足我的需求。请参阅下面我发布答案的地方。我不能接受自己的答案,但如果可以的话,我会接受——这似乎很好地解决了问题。

【问题讨论】:

  • cc 只是 gccclang 或系统使用的任何默认 C 编译器的别名。
  • @BasileStarynkevitch:我问是因为我想测试与安装在 /usr/bin 中的链接器不同的链接器。这里没有XY问题。只是针对不同版本的链接器进行测试。
  • @Olaf:是的,我知道 cc 是什么。我特别提到我已经知道拆分编译器/链接器步骤的方法。这个问题专门关于如何告诉 cc(它可以通过一次调用 cc 来执行所有构建阶段)如何使用与构建 cc 时默认指定的链接器不同的链接器。也许您不知道 cc 可以调用链接器(尝试使用 cc -v helloworld.c 构建一个简单的 C 程序)。
  • 看起来像 XY 问题。 为什么您要使用与默认链接器不同的链接器?对于正常编译,这应该是您想要的,对于交叉编译,您应该使用适当的工具链,其中还包括正确的链接器。
  • 评论不是问题的一部分。您应该编辑问题,查看How to Ask 并阅读常见问题解答。但是,是的,我监督巴兹尔已经怀疑过同样的事情。不过,我不明白你的问题是什么。 cc 用于默认编译/构建。对于任何较大的程序,无论如何您都应该使用构建工具,它们总是允许单独指定编译器和链接器。

标签: c++ c linker


【解决方案1】:

-B 选项允许您为编译器将使用的可执行文件、库、包含文件和数据文件指定备用搜索路径。这适用于某些版本的 gcc [1] 和 clang(目前未记录 - 在至少 clang 3.7 和 3.8 的手册页中):

cc -B/usr/local/bin foo.c -o foo

请注意,这将导致 cc 在-B 指定的路径中搜索其他工具(例如,汇编程序)。因此,假设您在 /usr/local/bin 中安装了不同版本的 binutils,如果您想使用该链接器(而不是 /usr/local/bin/as 等),您可以做一些事情像这样:

mkdir /tmp/usemyld
ln -s /usr/local/bin/ld /tmp/usemyld
cc -B/tmp/usemyld foo.c -o foo

-B 有自己的一套规则,允许您覆盖 gcc 编译器尝试使用的不同文件(程序、库、包含文件、数据文件)。这至少可以追溯到 gcc 2.95 - 阅读 gcc 手册/信息页面。我不知道-B 的行为与clang 的兼容性如何。如前所述,它目前没有记录在 clang 手册页中。但它工作得很好,让我可以选择一个备用链接器,如上所示。

gcc 还支持调用-wrapper 指定的脚本/程序。 clang 没有(目前)。您也可以使用它并指向一个包装脚本,该脚本会改变编译器正在调用的程序。我不知道collect2 是否注意到-wrapper 选项(对于gcccollect2 至少在编译c/c++ 程序时调用链接器)。

[1] gcc 信息页面中记录的链接器搜索顺序 collect2 表示,如果 GCC 配置了 '--with-ld' 选项,它将首先搜索“硬编码的链接器文件名”)。所以如果你的gcc没有配置'--with-ld',那么它最终会在-B指定的路径中搜索(如果它没有首先找到real-ld)。如果您的 gcc 配置了 --with-ld,那么 -B 选项将无法帮助您指定您选择的备用链接器。

【讨论】:

  • 使用了gcc -v -B/usr/bin …,但仍然接到了/opt/local/bin/ld -syslibroot …的电话——所以这也不起作用。
  • @Martin - 哪个 gcc 版本? /usr/bin 中是否有有效的“ld”?考虑使用跟踪工具(ktrace、strace)来查看 gcc 是否首先尝试 /usr/bin。你的 gcc 是用 --with-ld 构建的吗(见脚注)?
【解决方案2】:

某些链接器易于使用 - 只需 gcc -fuse-ld=lld main.c。这似乎是在 gcc 版本 4 中添加的。-fuse-ld 也适用于 clang 10.0.1

支持的链接器列在https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

-fuse-ld=bfd

使用 bfd 链接器而不是默认链接器。

-fuse-ld=黄金

使用黄金链接器而不是默认链接器。

-fuse-ld=lld

使用 LLVM lld 链接器而不是默认链接器。

希望这对来自搜索的人有所帮助,因为 OP 很久以前就回答了他们的问题。

【讨论】:

  • 这是一个很好的发现。 gcc 不允许您指定链接器的路径,但是:gcc9 -fuse-ld=/opt/ld.bfd x.c -o /dev/null gcc9: error: unrecognized command line option '-fuse-ld=/opt/ld.bfd' 。抱歉,应该分成两行 - 我不知道如何在评论中做到这一点。但是,clang 确实 允许-fuse-ld 的完整路径规范。那很好。请注意,-fuse-ld 甚至不会与 clang --help 一起出现。我还没有找到 llvm/clang 的文档记录在哪里。 (截至 llvm90)。
  • 这不起作用:gcc: error: unrecognized command line option '-fuse-ld=/usr/bin/ld' — 继续寻找。
  • 正如我在回答中提到的,这只适用于少数特殊情况。如果要使用bfd,则必须-fuse-ld=bfd,并且不能使用bfd链接器的整个路径。
  • @Martin - 正如 asky 所指出的,-fuse-ld 的一些当前实现的参数是特殊的。正如我之前在上面的评论中提到的,对于 gcc 的实现(例如 gcc 9),它不采用任意路径,而是采用 ld 之后的扩展名(例如,我上面评论中的示例中的 -fuse-ld=bfd )。如前所述,一条适用于 clang 的路径。可以调整答案以使其更加清晰。
【解决方案3】:

GCC 在内部使用spec files(决定gcc 程序的行为方式,特别是它如何链接以及使用什么链接器)。您可以配置或更改 with -specs= 以便拥有自己的规范文件并使用它。或使用-T optiongcc显式将其传递给ld)提供链接器脚本。

默认spec是用gcc -dumpspecs获取的

另外,通过strace(1)-ing 一些gcc 命令,你会发现它试图访问例如/usr/lib/gcc/x86_64-linux-gnu/specs;所以把你自己的规范文件放在那里。

那些规范文件是文本的,所以你应该可以自己写。

但我不确定这是个好主意。

顺便说一句,/usr/bin/cc 在 Linux 发行版上是一个符号链接(在 Debian 上:/usr/bin/cc -> /etc/alternatives/cc -> /usr/bin/gcc),可以指向某些 gcc 或某些 clang。 AFAIK,cc(和 c99)在 POSIX 中指定(但当然,它与ld 的关系没有被告知)

【讨论】:

  • -T 选项适用于 ld(1),而不适用于 cc(1)。我会看看-specs。看起来它是特定于 gcc 的。而且我同意它感觉不是正确的解决方案。
  • -T 选项适用于 gcc
  • -T 用于ld(1)。类似于将其他选项传递给链接器(例如--startgroup),要传递-T' on to the linker using gcc`(或clang),您可以使用-Wl,-T -Wl,your_link_script_file.ld。事实证明,作为一种快捷方式(类似于-L),您可以将-T 传递给gccclang,但在我看到的任何地方(手册页、信息页、--help)都没有记录。所以,是的,你是对的。抱歉 - 我犯了在文档中查找它的错误。但是,链接描述文件由 ld 读取,而不是 cc - 即使有办法指定它,也无法指向不同的命令。
  • 不,-T记录gcc 选项(点击链接)。当然是传给ld,但是gcc知道
  • 是的,我现在在在线文档中看到了。它仍然只是直接传递给/usr/bin/ld(而不是由cc 解析)或在构建cc 时指定的任何默认链接器。所以它无助于解决原来的问题。
【解决方案4】:

将包含替代链接器ld 的目录放在PATH 环境变量的前面。

例如,如果替代 ld~/local/bin 中。 export PATH=~/local/bin:$PATH 将导致 cc 使用 ~/local/bin/ld

【讨论】:

  • 更改 PATH 并不总是很方便。我来这里是为了寻找不同的解决方案。
猜你喜欢
  • 2023-01-07
  • 2018-06-05
  • 1970-01-01
  • 2013-08-20
  • 2010-11-13
  • 1970-01-01
  • 2022-07-08
  • 2012-09-21
相关资源
最近更新 更多