本文是翻译自man ld
名称
ld - The GNU linker
语法
ld [options] bobjfile …
描述
ld
连结许多目标文件和静态库文件(.a),重定位他们的数据,绑定符号引用。通常,编译一个程序的最后一步是运行ld
ld
接受链接器命令行语言文件,提供对链接程序的显式和整体控制。
该版本ld使用通用目的的BFD库去操作目标文件。该库允许ld程序以多种不同的格式(例如,COFF或a.out)去读,连结,和写目标文件 .不同的格式可能会被链接到一起去产生任何可用类型的目标文件。
得益于它的灵活性,GNU linker在提供诊断信息方面比其他linkers更有用。许多链接器在产生错误后会立即放弃执行。但是对于ld来说,只要可能,就会继续执行,允许你去确定其他错误。
The GNU linker ld 目标是覆盖大部分的场景,尽可能的兼容其他的链接器。因此,你可以有多个机会去控制它的行为
ld 要求被依赖的库在命令行的后面,例如a.so依赖b.so ,需要ld -la -lb 而不能ld -lb -la
选项
链接器支持特别多的命令行选项,但是在实际使用中,只有一小部分选项是经常会使用到的。例如,一个ld的高频使用场景是在标准unix系统上链接标准的unix 对象文件。在类似的一个系统上,链接一个hell.o文件:
ld -o <output> /lib/crt0.o hello -lc
上面的命令告诉ld产生一个叫output的文件作为链接/lib/crt0.o,hello.o,和库 libc.a(来自与标准搜索目录)的结果。
ld的一些命令行参数可以在任何位置被指定。然而, 指向文件的选项,像-l和-T,会导致文件在参数指定处(参数出现的位置)被读取.用不同的参数重复指定非文件类的选项可能会引起两种结果:1.不会对将来的结果造成影响. 2. 覆写之前已经产生的事件。在下文的描述中,可以指定多次的选项需要注意。
非选项的参数是目标文件或者静态库文件,这些文件将被链接到一起。他们可能会出现在参数之前,之后,或者混合出现,但是一个目标文件不能放置在一个选项和它的参数之间。
通常地,链接器被调用以处理至少一个目标文件,但是用-l,-R
和脚本命令语言,你能指定其他格式的二进制输入文件。如果没有二进制输入文件被指定,链接器不会产生任何输出,会产生一条No input files
消息。
如果链接器不能识别对象文件的格式,它将假定该文件是一个连接器脚本。以这种方式指定的脚本可能会增大用于链接的主连接器脚本(默认连接器脚本或者用-T指定的脚本)。该功能允许连接器链接一个看起来是目标文件或者静态库文件,但是实际上定义了一些符号值或者使用INPUT
或GROUP
来加载其他对象。以这种方式指定一个脚本只是增大主连接器脚本,用额外的命令放置在主脚本后;用-T
选项去替换默认的连接器脚本,但是要注意INSERT
命令的影响。
对于那些名字是单字符的选项,选项参数有2种形式给出:
- 紧跟着选项,中间不能有空白字符
- 作为分离参数(空格分割)立即跟在要求的选项后给出。
对于那些名字是多字符的选项来说,在选项名字前可以有一个或者两个-
;例如,-trace-symbol
和--trace-symbol
是相同的。注意,有一个列外。以小写字母o
开始的选项只能使用两个-
。这是为了减少与 -o 选项的混淆。例如,-omagic 设置输出文件名字为 magic,然而 --omagic 是为该选项设置 MAGIC标志。
多字符选项的参数有两种形式给出:
- 参数和选项以
=
分割 - 作为分离参数(空格分割)立即跟在要求的选项后给出。
例如,--trace-symbol foo
和--trace-symbol=foo
是等同的。对于多字符命名的选项的唯一缩写,以上规则也适用。
注意,如果链接器是通过编译器驱动调用(例如gcc),而不是直接被调用,此时所有的链接器命令行参数都需要带有前缀-Wl,
(每个ld option 前都需要有 -Wl,
).就像以下实例一样:
gcc -Wl, --start-group foo.o bar.o -Wl,--end-group
这是非常重要的,因为编译器驱动程序可能会静默的扔掉连接器选项,会导致错误的链接。当传输需要带有值的选项到一个驱动器中,混乱可能会产生,因为在选项和参数之间的空格作为一个分割器,会导致驱动器只传选项到连接器,参数传给编译器。这种情况下,使用紧凑格式是最简单有效的,例如:
gcc foo.o bar.o -Wl,-eENTRY -Wl,-Map=a.map
下面是GNU linker常用的命令行选项表:
@file
从 file 读命令行参数。
-a keyword
该选项被设计是用来兼容HP/UX的。keyword参数必须是以下字符串中的之一: archive,share, default. -aarchiev 在功能性上等同于 -Bstatic, 其他的两个参数等同于-Bdynamic. 该选项可能会被使用多次。
–audit AUDITLIB
添加AUDITLIB到dynamic section的 DT_AUDIT条目中。AUDITLIB不会被检查是否存在,也不会使用库中的DT_SONAME。如果指定多次,DT_AUDIT将包含一个由冒号分割的audit列表给使用者。如果链接器在搜索动态库后用一个audit条目找到了一个对象,它将在输出文件中添加一个对应的DT_DEPAUDIT条目。该选项只有在支持 rtld-audit接口的 ELF平台上才有意义
-A architecture
–architecture=architechure
在当前的ld发行版中,该选项只对intel 960 系列处理器有用。
-b input-format
–format=input-format
ld 可能会被配置以支持多种类型的对象文件。如果你的ld以这种方式配置,你可以使用-b 选项为输入的对象文件指定二进制格式,输入的对象文件会跟在该选项后面。即使当ld被配置以支持可选的对象格式时,你也不经常去指定它,因为ld 应该被配置为最常用的格式。input-format是一个文本字符串,被BFD库支持的特定格式的名字(可以通过objdump -i
查看)
-c MRI-commandfile
–mri-script=MRI-commandfile
为了兼容MRI连接器
-d
-dc
-dp
这三个选项是等价的,多种格式被支持为了兼容其他连接器。他们分配空间给常见的符号,即使一个重定位输出文件被指定(用 -r).脚本命令 FORCE_COMMON_ALLOCATION 有相同的作用
–depaudit AUDITLIB
-P AUDITLIB
添加AUDITLIB到dynamic section的 DT_DEPAUDIT条目中。AUDITLIB不会被检查是否存在,也不会使用库中的DT_SONAME。如果指定多次,DT_DEPAUDIT将包含一个由冒号分割的audit列表给使用者。如果连接器在搜索动态库后用一个audit条目找到了一个对象,它将在输出文件中添加一个对应的DT_DEPAUDIT条目。该选项只有在支持 rtld-audit接口的 ELF平台上才有意义.
-e entry
–entry=entry
当开始执行程序时,使用entry作为显式的入口符号,而不是采用默认的程序入口点。如果这里没有一个名字为entry的符号,连接器将尝试解析entry作为一个数字,使用它作为入口地址(数字将以10进制进行解释;你可以使用前导0x表示16进制,前导0表示8进制)
note: 用户可以指定某个函数作为程序的入口点,而不是main函数
–exclude-libs lib,lib,…
指定一个归档库列表,这些库中的符号不应该自动导出。库名可能以逗号或者冒号分割,指定--exclude-libs ALL
禁止所有库中的符号自动导出。该选项只对 i386 PE 和 ELF格式有效。对i386 PE,显式列在 .def 文件中的符号仍然会被导出,不论该选项是否被设置。对于ELF ,被该选项影响的符号将被隐藏起来
–exclude-modules-for-implib module,module,…
指定一系列对象或者归档成员,存在与列表中的符号不会被自动导出.仅仅对 i386 PE有效。
-E
–export-dynamic
–no-export-dynamic
当创建一个动态地链接过的可执行程序时,使用 -E 选项或者 --export-dynamic 选项导致链接器添加所有符号到动态符号表。动态符号表是符号的集合,这些符号是在运行期间从动态对象中可以查看的。
如果你没有使用这些选项(或者使用–no-export-dynamic选项重新存储默认行为),动态符号表将只包含那些在链接期间提到过的动态对象所引用的符号。
你可以使用动态列表控制哪些符号应该被添加到动态符号表,如果输出格式支持。可以参考 --dynamic-list.
注意:该选项只对ELF格式有用。PE 目标格式支持一个相似的功能从DLL or EXE 中导出所有的符号。参考 --export-all-symbols
-EB 链接大端对象,该选项影响默认输出格式
-EL 链接小端对象,该选项影响默认输出格式
-f name
–auxiliary=name
当创建一个ELF格式的动态库时,设置内部的DT_AUXILIARY 字段为指定的name.这个选项告诉动态连接器,创建的动态库的符号表应该被用作一个作用在动态库name的符号表上的过滤器。
如果你之后把这个过滤器动态库链接到一个程序,当你运行该程序时,动态链接器将会看到 DT_FILTER字段。动态链接器将像平常一样根据filter object 的符号表来处理符号,但是它实际将链接在动态库name中的定义。因此,过滤器动态库能被用于选择由name动态库提供的符号子集。
-fini=name
当创建一个ELF可执行程序或者动态库时,调用 NAME 当可执行或者动态库不能加载时,通过设置DT_FINI 为函数地址。默认地,连接器使用 _fini 作为 函数去调用
note:
-g 兼容其他工具
-G value
–gpsize=value
设置目标文件的最大尺寸,使用GP 寄存器把目标文件大小优化到size.只对MIPS ELF格式的目标文件有用,其他格式忽略。
-h name
–soname=name
当创建一个ELF动态库时,设置内部的DT_SONAME 字段为name。链接有DT_SONAME字段的动态库到一个可执行文件,当可执行程序运行时,动态链接器将尝试加载由DT_SONAME字段指定的动态库,而不是使用原来的动态库名称。
note: 通过该机制可以解决共享库版本兼容问题,可以查看Library Interface Versioning in Solaris and Linux
和语义化版2.0.0 文章
-i 与 -r 选项一样
-init=name
当创建一个ELF可执行程序或者动态库时,设置DT_INIT 为name函数地址。当可执行或者动态库被加载时,调用 name 函数。默认地,连接器使用 _init 作为 被调用的入口函数
-l namespace
–library=namespace
增加由namespace 指定的静态库/动态库到链接文件列表中。该选项可以随时使用。如果namespace 是 :filename
格式,ld 将会在库路径查找filename的文件,否则它将在库路径下搜索 libnamespace.a 文件。
在支持动态库的系统上,ld 可能会优先搜索动态文件libnamespace.so,而不是 libnamespace.a. 特别的,在 ELF和SUNOS系统上,ld 将搜索目录寻找libnamespace.so的文件,如果找不到再找libnamespace.a. 注意到,该选项的行为不应用于:filename
,该格式总是指定一个叫做filename的文件。
连接器只搜索静态库一次,在该选项出现在位置处。如果静态库A定义了一个符号,该符号在其他目标文件B(在命令行中B出现的比A早)中是未定义的,链接器将从静态库A中包含合适的文件到目标文件B。然而,如果对象B在命令行中出现的晚,对象B的未定义符号将不会引起链接器的再次查询静态库A。
查看 -( 选项,提供了一种强制链接器查找静态库多次的方法。
你可以在命令行中列出同一个静态库多次。
-L searchdir
–library-path=searchdir
添加路径searchdir到路径列表,ld使用这些路径搜索静态库/动态库和ld 控制脚本。可以使用该选项任意次数。以在命令行中指定的顺序进行搜索。在命令行中指定的目录会优先被搜索,之后才是默认路径。所有的 -L 选项应用于所有的 -l 选项,不论选项出现的顺序。
-L 选项不影响ld搜索一个链接器脚本的方式,除非-T选项被指定。
如果 searchdir 目录以 =
或者 $SYSROOT开始,则前缀会被sysroot prefix (–sysroot选项控制)替代。
搜索路径也可以由 SEARCH_DIR
命令指定的连接器脚本指定。
-m emulation
模拟指定的 emulation 链接器。可以通过命令ld -V
查看可用的模拟器。
如果-m 选项没有被使用,模拟器值将从环境变量 LDEMULATION 获取,如果被定义。否则,默认的模拟器依赖于链接器的配置
-M
–print-map
打印 链接map 到标准输出。link map提供链接信息,包括以下内容:
- 对象文件在何处被映射进内存
- 一般的符号如何被分配空间
- 所有的包括在链接内的归档成员, 一提及导致归档成员被带入的符号
- 赋值给符号的值
符号的值可以被表达式计算。
-n
–nmagic
关闭section的页对齐,失能链接到共享库的能力。
-N
–omagic
设置text和data section 可读可写。同时,不做
-o output
–output = output
使用output作为ld程序产出结果的名字。如果该选项没有指定,则使用默认的a.out作为输出结果的名字。
-O level
如果level是一个比0大的数字,ld 会优化输出结果。
-s
–strip-all
从输出文件中去除所有的符号信息
-S
–strip-debug
从输出文件中去除所有的debug 符号信息
–strip-discarded: 默认使能
–no-strip-discarded
-t
–trace
当ld处理输入文件时,打印文件名称
-rpath=dir
添加运行时动态库搜索目录。当链接动态库到可执行程序时,会使用这个选项。所有的-rpath指定的目录会连接到一起(concatenate),传给运行时连接器,用于在运行时定位动态库位置。该选项也被用于链接时,被动态库依赖的动态库路径搜索,具体可以看-rpath-link。如果链接时没有指定-rpath,如果LD_RUN_PATH被设置,就使用LD_RUN_PATH。
-rpath-link=dir
当使用ELF时,一个动态库可能会依赖另一个。当ld -shared
命令中 一个动态库作为输入时,将会产生。
当ld处理一个非动态,非重定向的连接时,会产生一个依赖需求,ld会自动定位被依赖的库,如果它没有显式包含在链接中,则把它包含在本次链接中。在这种情况下,-rpath-link
选项指定了搜索目录集合。它可以指定一系列由冒号(:)分割的目录名称,或者该选项可以出现多次。
$LIB 和$ORIGIN 可以出现在这些搜索目录中。他们会被全路径所覆盖。
ld 使用以下的路径用于搜索被依赖的库:
1. 由 -rpath-link指定的目录
2. 由-rpath指定的目录。
3. 由环境变量 LD_RUN_PATH指定的目录
4. 对于本机链接器,使用环境变量LD_LIBRARY_PATH 指定的目录
5. 对于本机链接器,使用DT_RUNPATH DT_RPATH
6. 默认目录:/lib and /usr/lib
7. 对于本机链接器,如果/etc/ld.so.conf存在,搜索该文件中列举的目录
如果以上目录还没有搜索到被依赖的库,ld会发出警告,继续链接。
-shared
-Bshareable
创建一个动态库。
-Bsymbolic
在创建动态库时,尽可能的把全局符号的引用绑定到本动态库的符号定义上。正常情况下,该选项允许链接到动态库的程序覆写在动态库中定义的符号。可以查看-Bsymbolic会导致严重副作用。
正常情况下,在linux平台上(不使用-Bsymbolic),加载的目标文件中第一次出现的符号将在程序中一直被使用,不论是定义在静态可执行部分,还是在动态目标文件中。这是通过符号抢占(symbol preemption)来实现的。动态加载器构建符号表,所有的动态符号根据该符号表被决议。所以正常情况下,如果一个符号实例出现在动态库(DSO)中,但是已经在静态可执行文件或者之前加载的动态库中被定义,那么以前的定义也将被用于当前的动态库中。
-Bsymbolic 通过关闭DOS中的符号抢占来改变这种行为。
-whole-archive
在命令行–whole-archive选项后面的每个静态库,把静态库中的每个目标文件都包含在链接中,而不是只包含需要的目标文件。该选项经常被用于把一个静态库整体放入一个动态库中,强迫动态库包含所有的目标文件。可以使用多次该选项。
有2点需要注意:
- 使用gcc时,需要 -Wl,-whole-archive使用
- 不要忘了-Wl,-no-whole-archive,这是成对出现的。
-(archive -)
–start-group archives --end-group
archives应该是一个静态库列表,可以是完整的文件名称(libxxx.a),也可以是-lxx的形式。
指定的静态库会被重复搜索,直到没有新的未定义引用产生。正常情况下,静态库只会被搜索一次,以在命令行中出现的顺序。考虑如下情况:
liba.a定义了一个符号symbol,libb.a 引用了该符号,在命令行中 -la -lb 顺序出现时,链接器就无法处理b中的符号引用了,会抛出未定义符号错误。正常情况是,-lb -la ,链接器就会正确处理。当使用–start-group选项后,会把这些静态库放到一组里,会被重复搜索,直到所有可能的引用都被决议。
使用该选项会带来严重的性能损耗。最好只在处理不可避免的循环引用时,才使用。
未完