[UP] add some viki files, knowledge.
[arrow.git] / viki / binutils / ld-cn.viki
blob96949c7411eca57b074efe937c225d551415855c
1 en:
2 http://sourceware.org/binutils/docs-2.16/ld/Simple-Example.html#Simple-Example
4 cn:
5 http://blog.csdn.net/rstevens/archive/2008/01/28/2070568.aspx
6 http://www.diybl.com/course/6_system/linux/Linuxjs/2008622/127448.html
7 http://blog.chinaunix.net/u/15960/showart_151189.html
8 all2: http://blog.21ic.org/user1/2543/archives/2007/41309.html
11 ld 中文使用手册完全版(译)
12 使用ld
13 ********
14 本文档介绍GNU连接器ld的2.14版本.
16 本文档在GNU自由文档许可证下发行.在"GNU自由文档许可证"一章中有关于本许可证的一份拷贝.
18 概述
19 ********
21 'ld'把一定量的目标文件跟档案文件连接起来,并重定位它们的数据,连接符号引用.一般,在编译一个程序
22 时,最后一步就是运行'ld'.
24 'ld'能接受连接命令语言文件,这是一种用AT&T的连接编辑命令语言的超集写成的文件,用来在连接的整个
25 过程中提供显式的,全局的控制.
27 本版本的'ld'使用通用BFD库来操作目标文件.这就允许'ld'读取,合并,写入目标文件时,可以使用各种不同
28 的格式,比如,COFF或'a.out'. 不同的格式可以被连接到一起产生一个有效的目标文件.
30 除了它的灵活性,GNU连接器比其它连接器更有用的地方在于它提供了诊断信息. 许多连接器在碰到一个错误
31 的时候立即放弃执行;但'ld'却能够继续执行,以让你发现其他的错误(或者,在某些情况下,得到一个带有错误
32 的输出文件)
34 引用
35 **********
37 GNU连接器'ld'能够处理大量的不同情况,并且跟其他的连接器保持尽可能的兼容.这样,你就拥有更多的选择来
38 控制它的行为.
40 命令行选项
41 ====================
43 连接器提供大量的命令行选项,但是,在实际使用中,只有少数被经常使用.比如,'ld'的一个经常的使用场合是在
44 一个标准的Unix系统上连接标准的Unix目标文件.在这样的一个系统上,连接文件'hello.o'如下:
46 ld -o OUTPUT /lib/crt0.o hello.o -lc
48 这告诉'ld'产生一个叫OUTPUT的文件,作为连接文件'/lib/crt0.o'和'hello.o'和库'libc.a'的结果.'libc.a'
49 来自标准的搜索路径.(参阅下文的关于'-l'选项的讨论).
51 有些命令行选项可以在命令行的任何位置出现.但是,那些带有文件名的选项,比如'-l'或者'-T',会让文件在选
52 项出现的位置上被读取. 对于非文件选项,以带不同的参数重复它,不会有进一步的效果,或者覆盖掉前面的相同
53 项.那些多次出现时具有特殊含义的选项会在下文的描述中指出.
55 无参数选项是那些被连接的目标文件和档案文件.它们可能紧随命令行选项,或在它们前面,或者跟它们夹杂在一
56 起,但是一个目标文件参数是不会出现在一个选项跟它的参数之间的.
58 通常,连接器至少引用一个目标文件,但是你可指定其它形式的二进制输入文件,这可以通过'-l','-R'或者脚本
59 命令语言来实现.如果没有任何二进制文件被指定,连接器不会产生任何输出,并给出信息:"缺少输入文件."
61 如果连接器不能识别目标文件的格式,它会假设这些只是连接脚本.以这种方式指定的脚本增加了连接用的主连
62 接脚本的内容(主连接脚本即缺省连接脚本或使用'-T'指定的脚本). 这个特性可以允许连接器连接一些文件,
63 它们看上去既像目标文件,又像档案文件,但实际上只是定义了一些符号值,或者使用'INPUT'或'GROUP'来载入其
64 它的目标文件.需要注意的是,用这种方式指定一个脚本只是增加了主连接脚本的内容;要完全替换掉主连接脚本
65 ,需要使用'-T'.
67 对于名称是单个字符的选项,选项参数必须紧跟在选项字母后面,中间不留空,或者也可留有一个空格.
69 对于名称是多个字符的选项,选项前可以有一个或两个破折号;比如,'-trace-symbol'和`--trace-symbol'是等价
70 的. 注意,对于这条规则有一个例外.那些以小写字母'o'开头的多字符选项前面只能是两个破折号,这是为了避免
71 跟选项'-o'混淆. 比如'-omagic'把输出文件的名字定为'magic',而'--omagic'在输出文件中设置NMAGIC标志.
73 多字符选项的参数必须跟选项名间以一个等于号分开,或者以一个空格分开.比如:`--trace-symbol foo'和
74 `--trace-symbol=foo'是等价的. 多字符选项的名字唯一缩写符也是可以被接受的.
76 注意,如果连接器通过被编译器驱动来间接引用(比如gcc), 那所有的连接器命令行选项前必须加上前缀'-Wl'
77 (或者能被特定编译器驱动接受的其他前缀),就像下面这样:
79 gcc -Wl,--startgroup foo.o bar.o -Wl,--endgroup
81 这很重要,因为否则的话,编译器驱动程序会默认丢掉这些连接选项,产生一个错误的连接.
83 下面是关于被GNU连接器接受的常用命令行开关的一个列表:
85 `-aKEYWORD'
86 这个选项在HP/UX兼容系统上被支持. 参数KEYWORD必须是下面字符串中的一个:`archive',
87 `shared', or `default'. `-aarchive'在功能上跟`-Bstatic'相同,而另外两个关键字功能上跟
88 `-Bdynamic'相同. 这个选项可被多次使用.
90 `-AARCHITECTURE'
91 `--architecture=ARCHITECTURE'
92 在最近发行版本的'ld'中,这个选项只在Intel 960系列架构上有用. 在那种'ld'配置中,参数
93 ARCHITECTURE确定960系列的某一特定架构,启用某些安全措施,并修改档案库的搜索路径.
95 将来的'ld'发行版可能为其它架构系列支持相似的功能.
97 `-b INPUT-formAT'
98 `--format=INPUT-formAT'
99 'ld'可以被配置为支持多于一种的目标文件.如果你的'ld'以这种方式被配置,你可以使用'-b'选
100 项为输入目标文件指定二进制格式. 就算'ld'被配置为支持可选目标格式,你不必经常指定这一项,
101 因为'ld'被配置为在每一台机子上把最常用的格式作为默认输入格式. INPUT-formAT是一个字符串,
103 你可能在连接一个不常用的二进制格式文件时需要这个参数.你也可使用'-b'来显式切换格式(在连接
104 不同格式的目标文件时),方法是在每一组特定格式的目标前使用'-b INPUT-formAT'.
106 缺省的格式是从环境变量'GNUTARGET'中得到的.你也可以从一个脚本中定义输入格式,使用的命令是
107 'TARGET'.
109 `-c MRI-COMMANDFILE'
110 `--mri-script=MRI-COMMANDFILE'
111 为了跟MRI生产的连接器兼容,'ld'接受另一种用受限命令语言写成的脚本文件,通过选项'-c'引入MRI
112 脚本文件;使用'-T'选项是运行用普通'ld'脚本语言写的连接脚本.如果MRI-CMDFILE不存在,'ld'在'-L'
113 指定的目录中寻找.
115 `-d'
116 `-dc'
117 `-dp'
118 这三个选项是等价的; 多字符形式是为了跟其他连接器兼容才被支持的.它们给普通符号分配空间,即
119 使一个重定位输出文件已经被指定(通过'-r'). 脚本命令`FORCE_COMMON_ALLOCATION'具有同样的效果.
121 `-e ENTRY'
122 `--entry=ENTRY'
123 使用符号ENTRY作为你的程序的开始执行点,而不是使用缺省的进入点.如果没有叫做ENTRY的符号,连接器
124 会企图把ENTRY作为一个数字进行分析,并使用它作为入口地址(数字会被解释为10进制的;你可以使用前
125 导的'0x'强制为16进制,或'0'作为8进制.)
127 `-E'
128 `--export-dynamic'
129 当创建一个动态连接的可执行程序时, 把所有的符号加到动态符号表中.动态符号表是一个符号集,这
130 些符号对于运行时的动态对象是可见的.
132 如果你不使用这个选项,动态符号表中就会只含有那些连接进来的动态对象中用到的符号
134 如果你使用'dlopen'来载入动态对象,它需要引用程序中的符号,那你可能需要在连接程序时用到这个
135 选项.
137 你也可以使用版本脚本来控制哪些符号应当被加到动态符号表中.
139 `-EB'
140 连接big-endian对象. 这会影响缺省输出格式.
142 `-EL'
143 连接little-endian对象. 这会影响缺省输出格式.
145 `-g'
146 忽略. 为了跟其它工具兼容而提供.
148 `-i'
149 执行一个增量连接(跟'-r'等同)
151 `-init NAME'
152 当创建一个ELF可执行文件或共享对象时,当可执行文件或共享对象被加载时,调用NAME, 这是通过把
153 DT_INIT设置成函数的地址实现的. 缺省情况下,连接器使用'_init'作为调用的函数.
155 `-lARCHIVE'
156 `--library=ARCHIVE'
157 增加一个档案文件ARCHIVE到连接的文件列表中.这个选项可以被多次使用. 'ld'会为每一个指定的
158 ARCHIVE搜索它的路径列表,寻找`libARCHIVE.a'
160 对于支持共享库的系统, 'ld'可能还会搜索扩展名不是'.a'库.特别的,在ELF和SunOS系统上,'ld'会
161 在搜索带有'.a'扩展名的库前搜索带'.so'扩展名的库.
163 `-M'
164 `--print-map'
165 打印一个连接位图到标准输出.一个连接位图提供的关于连接的信息有如下一些:
167 * 目标文件和符号被映射到内存的哪些地方.
169 * 普通符号如何被分配空间.
171 * 所有被连接进来的档案文件,还有导致档案文件被包含进来的那个符号.
173 `-n'
174 `--nmagic'
175 关闭所有节的页对齐,如果可能,把输出格式标识为'NMAGIC'.
177 `-N'
178 `--omagic'
179 把text和data节设置为可读写.同时,取消数据节的页对齐,同时,取消对共享库的连接.如果输出格式
180 支持Unix风格的magic number, 把输出标志为'OMAGIC'.
182 `--no-omagic'
183 这个选项执行的操作大部分正好跟'-N'相反.它设置text节只读,强制data节页对齐. 但是,这个选项
184 并不开启连接共享库的功能. 使用'-Bdynamic'开启这个功能.
186 `-o OUTPUT'
187 `--output=OUTPUT'
188 使用OUTPUT作为'ld'产生的程序的名字;如果这个选项没有指定,缺省的输出文件名是'a.out'.脚本命
189 令'OUTPUT'也可以被用来指定输出文件的文件名.
191 `-O LEVEL'
192 如果LEVEL是一个比0大的数值, 'ld'优化输出.这可能会明显多占用时间,所以只有在生成最后的文件
193 时使用.
195 `-q'
196 `--emit-relocs'
197 把重定位节和内容留在完全连接后的可执行文件中. 连接分析和优化工具可能需要这些信息用来进行
198 正确的修改与执行. 这在大的可执行文件中有用.
200 这个选项目前只支持ELF平台.
201 `-r'
202 `--relocateable'
203 产生可重定位的输出, 比如,产生一个输出文件它可再次作为'ld'的输入.这经常被叫做"部分连接".
204 作为一个副作用,在支持标准Unix魔数的环境中,这个选项会把输出文件的魔数设置为'OMAGIC'. 如
205 果这个选项没有被指定,一个绝对文件就会被产生.当连接C++程序时,这个选项就不会解析构造函数的
206 引用;要解析,必须使用'-Ur'
208 如果输入文件跟输出文件的格式不同,只有在输入文件不含有重定位信息的时候部分连接才被支持.输
209 出格式不同的时候会有更多的限制.比如,有些'a.out'的格式在输入文件是其他格式的时候完全不支
210 持部分连接.
212 这个选项跟'-i'等效.
214 `-R FILENAME'
215 `--just-symbols=FILENAME'
216 从FILENAME中读取符号名跟它们的值,但不重位这个文件,或者根本不把它包含在输出文件中.这就允
217 许你的输出文件引用其它程序中定义的绝对内存地址.你可以多次使用这个选项.
219 为了跟其他ELF连接器兼容,如果'-R'选项后面跟有一个目录名,而不是一个文件名,它会被处理成
220 '-rpath'选项.
222 `-s'
223 `--strip-all'
224 忽略输出文件中所有的符号信息.
226 `-S'
227 `--strip-debug'
228 忽略输出文件中所有的调试符号信息(但不是所有符号).
230 `-t'
231 `--trace'
232 打印'ld'处理的所有输入文件的名字.
234 `-T SCRIPTFILE'
235 `--script=SCRIPTFILE'
236 把SCRIPTFILE作为连接脚本使用. 这个脚本会替代'ld'的缺省连接脚本(而不是增加它的内容),所以
237 命令文件必须指定所有需要的东西以精确描述输出文件. 如果SCRIPTFILE在当前目录下不存在,'ld'
238 会在'-L'选项指定的所有目录下去寻找.多个'-T'选项会使内容累积.
240 `-u SYMBOL'
241 `--undefined=SYMBOL'
242 强制SYMBOL在输出文件中作为一个无定义的符号被输入.这样做会有一些效果,比如,会引发从标准库
243 中连接更多的模块. '-u'可以以不同的参数反复使用,以输入多个无定义的符号.这个选项跟连接脚
244 本命令中的'EXTERN'是等效的.
246 `-Ur'
247 对于不是C++的程序,这个选项跟'-r'是等效的: 它产生可重定位的输出,比如,一个输出文件它可以再
248 次作为'ld'的输入. 当连接C++程序时,'-Ur'解析构造函数的引用,跟'-r'不同. 但如果在一些用'-Ur'
249 连接过的文件上再次使用'-Ur',它不会工作,因为一旦构造函数表被建立,它不能被添加内容.请只在
250 最后一遍连接的时候使用'-Ur', 对其它的,只使用'-r'.
252 `--unique[=SECTION]'
253 对于所有匹配SECTION的输入节,在输出文件中都各自创建单独的节,或者,如果可选的通配符SECTION
254 参数丢失了,为每一个孤儿输入节创建一个输出节. 一个孤儿节是一个连接脚本中没有指定的节.你
255 可以在命令行上多次使用这个选项; 它阻止对同名输入节的合并,在连接脚本中重载输出节分配.
257 `-v'
258 `--version'
259 `-V'
260 显示'ld'的版本. '-V'选项同时会列出支持的模拟器.
262 `-x'
263 `--discard-all'
264 删除所有的本地符号.
266 `-X'
267 `--discard-locals'
268 删除所有的临时本地符号.对于大多数目标平台,就是所有的名字以'L'开头的本地符号.
270 `-y SYMBOL'
271 `--trace-symbol=SYMBOL'
272 打印出所有SYMBOL出现的被连接文件的名字. 这个选项可以被多次使用. 在很多系统中,这在预先确定底
273 线时很有必要.
275 当你拥有一个未定义的符号,但不知道这个引用出自哪里的时候,这个选项很有用.
277 `-Y PATH'
278 为缺省的库搜索路径增加一条路径.这个选项是为了跟Solaris兼容.
280 `-z KEYWORD'
281 能被识别的关键字包括'initfirst', 'interpose', 'loadfltr',`nodefaultlib', `nodelete',
282 `nodlopen', `nodump', `now', `origin',`combreloc', `nocombreloc' and `nocopyreloc'. 为了跟
283 Solaris兼容,所有其它的关键字都被忽略. 'initfirst'标志一个对象,使它在运行时,在所有其他对象之
284 前被初始化. 'interpose'标志一个对象,使它的符号表放在所有其他符号之前,作为主要的执行者.
285 'loadfltr'标志一个对象, 使它的过滤器在运行时立即被处理.'nodefaultlib'标志一个对象,使在搜索
286 本对象所依赖的库时,忽略所有缺省库搜索路径. 'nodelete'标志一个对象,使它在运行时不会被从内存
287 中删除.'nodlopen'标志一个对象,使这个对象不可以通过'dlopen'载入.'nodump'标志一个对象,使它不能
288 被'dldump'转储. 'now'标志一个对象,使它成为非懒惰运行时绑定对象. 'origin'标志一些可能含有
289 $ORIGIN的对象,'defs'不允许无定义符号. 'muldefs'允许重定义. 'comberloc'组合多个重定位节,重新
290 排布它们,让动态符号可见. 'nocomberloc'使多个重定位节组合无效. 'nocopyreloc'使重定位拷贝后的
291 结果无效.
293 `-( ARCHIVES -)'
294 `--start-group ARCHIVES --end-group'
295 ARCHIVES应当是一个关于档案文件的列表. 它们可以是显式的文件名,或者'-l'选项.
297 这些指定的档案文件会被多遍搜索,直到没有新的无定义引用被创建. 通常,一个档案文件只会被搜索一
298 次. 但如果这个档案文件中的一个符号需要被用来解析一个档案中的目标引用到的无定义的符号,而这个
299 符号在命令行上的后面某个档案文件中出现, 连接器不能解析这个引用. 把这些档案文件分组后,它们都
300 可被反复搜索直到所有可能的引用都被解析了为止.
302 使用这个选项有一个很大的运行开销. 只有在无法避免在多个档案文件中使用循环引用时才用它.
304 `--accept-unknown-input-arch'
305 `--no-accept-unknown-input-arch'
306 告诉连接器接受那些架构不能被识别的输入文件. 但前提假设是用户知道他们在做什么,并且是故意要连
307 接这些未知的输入文件. 在版本2.14之前,这个是连接器的缺省行为. 从版本2.14以后的,缺省行为是拒
308 绝这类输入文件, 所以`--accept-unknown-input-arch'选项被用来恢复旧的行为.
310 `-assert KEYWORD'
311 这个选项被忽略,只是用来跟SunOS保持兼容.
313 `-Bdynamic'
314 `-dy'
315 `-call_shared'
316 连接动态链接库. 这个仅仅在支持共享库的平台上有用.在这些平台上,这个选项通常是默认行为. 这个选
317 项的不同形式是为了跟不同的系统保持兼容. 你可以在命令行上多次使用这个选项:它影响紧随其后的'-l'
318 选项的库搜索.
320 `-Bgroup'
321 在动态节的'DT_FLAGS_1'入口上设置'DF_1_GROUP'标志.这会让运行时连接器在处理在这个对象和它的相
322 关部分搜索时只在组中. '--no-undefined'是隐式的. 这个选项只在支持共享库的ELF平台上有用.
324 `-Bstatic'
325 `-dn'
326 `-non_shared'
327 `-static'
328 不连接共享库. 这个仅仅在支持共享库的平台上有用. 这个选项的不同形式是为了跟不同的系统保持兼
329 容. 你可以在命令行上多次使用这个选项:它影响紧随其后的'-l'选项的库搜索.
331 `-Bsymbolic'
332 当创建一个共享库时, 把对全局符号的引用绑定到共享库中的定义(如果有), 通常, 一个连接共享库的程
333 序重载共享库中的定义是可能的. 这个选项只在支持共享库的ELF平台上有用.
335 `--check-sections'
336 `--no-check-sections'
337 让连接器在节地址被分配后不要去检查节地址是否重叠.通常,连接器会执行这种检查,如果它发现了任何
338 重叠,它会产生相应的错误信息. 连接器知道也允许节的重叠. 缺省的行为可以使用命令行开关
339 `--check-sections'来恢复.
341 `--cref'
342 输出一个交叉引用表. 如果一个连接器位图文件被产生, 交叉引用表被打印到位图文件. 否则, 它被打印
343 到标准输出.
345 表的格式相当的简单, 所以,如果需要,可以通过一个脚本很轻易地处理它. 符号是以名字被打印输出,存
346 储. 对于每一个符号,给出一个文件名列表. 如果符号被定义了, 列出的第一个文件是符号定义的所在.
347 接下来的文件包含符号的引用.
349 `--no-define-common'
350 这个选项限制对普通符号的地址分配. 脚本命令`INHIBIT_COMMON_ALLOCATION'具有同等的效果.
352 `--no-define-common'选项允许从输出文件的类型选择中确定对普通符号的地址分配; 否则, 一个非重定
353 位输出类型强制为普通符号分配地址. 使用'--no-define-common'允许那些从共享库中引用的普通符号只
354 在主程序中被分配地址. 这会消除在共享库中的无用的副本的空间, 同时,也防止了在有多个指定了搜索
355 路径的动态模块在进行运行时符号解析时引起的混乱.
357 `--defsym SYMBOL=EXPRESSION'
358 在输出文件中建立一个全局符号,这个符号拥有一个EXPRESSION指定的绝对地址. 你可以多次使用这个选
359 项定义多个符号. EXPRESSION支持一个受限形式的算术运算:你可以给出一个十六进制常数或者一个已存
360 在符号的名字,或者使用'+'和'-'来加或减十六进制常数或符号. 如果你需要更多的表达式,可以考虑在脚
361 本中使用连接器命令语言, 注意在SYMBOL,=和EXPRESSION之间不允许有空格.
363 `--demangle[=style]'
364 `--no-demangle'
365 这些选项控制是否在错误信息和其它的输出中重组符号名. 当连接器被告知要重组, 它会试图把符号名以
366 一种可读的形式的展现: 如果符号被以目标文件格式使用,它剥去前导的下划线,并且把C++形式的符号名
367 转换成用户可读的名字. 不同的编译器有不同的重组形式. 可选的重组形式参数可以被用来为你的编译器
368 选择一个相应的重组形式. 连接器会以缺省形式重组直至环境变量`COLLECT_NO_DEMANGLE'被设置. 这些
369 选项可以被用来重载缺省的设置.
371 `--dynamic-linker FILE'
372 设置动态连接器的名字. 这个只在产生动态连接的ELF可执行文件时有效. 缺省的动态连接器通常是正确
373 的; 除非你知道你在干什么,不要使用这个选项.
375 `--embedded-relocs'
376 这个选项只在连接MIPS嵌入式PIC代码时有效, 这些代码必须是由GNU的编译器跟汇编器通过-membedded-pic
377 选项生成的. 它导致连接器产生一个表,这个表被用来在运行时重定位所有的被静态初始化为指针值的数
378 据.
380 `--fatal-warnings'
381 把所有的警告视为错误.
383 `--force-exe-suffix'
384 确保输出文件有一个.exe后缀.
386 如果一个被成功完整连接的输出文件不带有一个'.exe'或'.dll'后缀, 这个选项确保连接器把输出文件
387 拷贝成带有'.exe'后缀的同名文件. 这个选项在使用微软系统来编译未经修改的Unix的makefile时很有
388 用, 因为有些版本的windows不会运行一个不带有'.exe'后缀的映像.
390 `--no-gc-sections'
391 `--gc-sections'
392 允许对未使用的输入节的碎片收集. 在不支持这个选项的平台上,被忽略. 这个选项不能跟 '-r'选项共存
393 也不能被用来进行动态连接. 缺省行为可以用`--no-gc-sections'进行恢复.
395 `--help'
396 在标准输出上打印一个命令行选项概要,然后退出.
398 `--target-help'
399 打印一个所有目标平台相关的选项的概要,然后退出.
401 `-Map MAPFILE'
402 打印一个连接位图到文件MAPFILE中. 参阅上面关于'-M'选项的描述.
404 `--no-keep-memory'
405 'ld'通常会以速度优先于内存使用的方式优化程序,这是通过把输入文件的符号表放在内存缓冲中实现的,
406 这个选项告诉'ld'以内存使用优先来优化, 尽可能的减小符号表的重读. 这在'ld'在连接一个大文件时
407 超出内存限制时有用.
409 `--no-undefined'
410 `-z defs'
411 通常,当创建一个非符号共享库时, 无定义的符号允许出现,并留待运行时连接器去解决. 这个选项关闭这
412 样的无定义符号的使用. 开关`--no-allow-shlib-undefined'控制共享对象被连接进共享库时的行为.
414 `--allow-multiple-definition'
415 `-z muldefs'
416 通常,当一个符号被定义多次时, 连接器会报告一个致命错误. 这些选项允许重定义并且第一个定义被使
420 `--allow-shlib-undefined'
421 `--no-allow-shlib-undefined'
422 允许(缺省)或不允许无定义符号存在于共享对象中. 这个开关的设置会重载'--no-undefined',这里只关
423 注共享对象. 这样,如果'--no-undefined'被设置,但'--no-allow-shlib-undefined'未被设置, 连锁反应
424 是存在于规则对象文件中的无定义的符号会引起一个错误,但是在共享对象中的未定义的符号会被忽略.
426 把`--allow-shlib-undefined'设置为缺省的原因是在连接时指定的共享对象并不一定是载入时可载入的
427 那个,所以,符号可能要到载入时间才被解析.
429 `--no-undefined-version'
430 通常当一个符号有一个未定义的版本时,连接器会忽略它. 这个选项不允许符号有未定义的版本,并且碰
431 到这种情况,会报告一个严重错误.
433 `--no-warn-mismatch'
434 通常, 如果你因为一些原因,企图把一些不匹配的输入文件连接起来的时候,'ld'会给出一个错误,可能这
435 些文件是因为由不同的处理器编译. 这个选项告诉'ld'应当对这样的错误默认允许. 这个选项必须小心
436 使用.
438 `--no-whole-archive'
439 为后面的档案文件关闭'--whole-archive'选项的影响.
441 `--noinhibit-exec'
442 当一个可执行文件还可以使用时,就保留它. 通常,连接器如果在连接过程中遇到了错误,就不会产生输出
443 文件;当它遇上错误时,它会退出而不写输出文件.
445 `-nostdlib'
446 仅搜索那些在命令行上显式指定的库路径. 在连接脚本中(包含在命令行上指定的连接脚本)指定的库路
447 径都被忽略.
449 `--oformat OUTPUT-formAT'
450 'ld'可以被配置为支持多于一种的目标文件. 如果你的'ld'以这种方式被配置,你可以使用'--oformat'
451 选项来指定输出目标文件的二进制格式.就算'ld'被配置为支持多种目标格式,你也不必指定这个项,因
452 为'ld'应当被配置为把最常用的输出格式作为默认格式. OUTPUT-formAT是一个文本串,是被BFD库支持
453 的一个特定格式的名字.脚本命令'OUTPUT_formAT'也可以指定输出格式,但这个选项可以覆盖它.
455 `-qmagic'
456 这个选项被忽略,只是为了跟Linux保持兼容.
458 `-Qy'
459 这个选项被忽略,只是为了跟SVR4保持兼容.
461 `--relax'
462 一个机器相关的选项. 只有在少数平台上,这个选项被支持.
464 在某些平台上,'--relax'选项在连接器解析程序中的地址时执行可能的全局优化, 比如松散地址模式和在输出文件
465 中合成新的指令.
467 在某些平台上,连接时全局优化会进行符号调试导致程序不能运行.
469 在不支持这个选项的平台上,'--relax'被接受,但被忽略.
471 `--retain-symbols-file FILENAME'
472 只保留在FILENAME中列出的那些符号,丢弃所有其他的. FILENAME是一个简单地平坦模式文件, 一个符号占一行.
473 这个选项在那些会逐步积累起一个大的全局符号表的系统中(比如 VxWorks)会很有用,它能有效地节约内存空间.
475 '--retain-symbols-file'不丢弃未定义的符号,和需要重定位的符号.
477 你可能在命令行上只指定'--retain-symbol-file'一次, 它覆盖'-s'和'-S'的功能.
479 `-rpath DIR'
480 为运行时库的搜索路径增加一个目录. 这个在连接带有共享库的ELF可执行文件时有用. '-rpath'的所有参数会被
481 连接起来传递给运行时连接器, 运行时连接器在运行时用它们定位共享对象. '-rpath'选项在定位那些在连接参数
482 指定的共享对象需要的共享对象时也很有用; 参阅关于'-rpath-link'选项的描述, 如果在连接一个ELF可执行文件
483 时不使用'-rpath'选项,那些环境变量'LD_RUN_PATH'选项就会被使用.
485 '-rptah'选项也可以使用在SunOS上. 缺省地,在SunOS上,连接器会从所有的'-L'选项中形成一个运行时搜索路径.
486 如果使用了'-rpath'选项, 那运行时搜索路径就只从'-rpath'选项中得到, 忽略'-L'选项. 这在使用GCC时非常有
487 用, 它会用上很多的'-L'选项,而这些路径很可能就是NFS挂上去的文件系统中.
489 为了同ELF的连接器兼容, 如果'-R'选面后面跟有一个目录名, 而不是一个文件名,那它也会被处理成'-rpath'选
490 项.
492 `-rpath-link DIR'
493 当在SunOS上使用ELF时,一个共享库可能会用到另一个共享库. 当'ld -share'把一个共享库作为一个输入文件连接
494 时就有可能发生这种情况.
496 当一个连接器在作非共享,不可重定位连接时,如果遇上这种依赖情况,它会自动定位需要的共享库,然后把它包含在
497 连接中, 如果在这种情况中,它没有被显式包含, 那'-rpath-link'选项指定优先搜索的一组路径名.
499 这个选项必须小心使用,因为它会覆盖那些可能已经被编译进共享库中的搜索路径. 在这种情况下,它就有可能使用
500 一个非内部的不同的搜索路径.
502 连接器使用下面的搜索路径来定位需要的共享库:
504 1. 所有由'-rpath-link'选项指定的搜索路径.
506 2. 所有由'-rpath'指定的搜索路径. '-rpath'跟'-rpath_link'的不同之处在于,由'-rpath'指定的路径被包含在可
507 执行文件中,并在运行时使用, 而'-rpath-link'选项仅仅在连接时起作用. 它只用于本地连接器.
509 3. 在一个ELF系统中, 如果'-rpath'和'rpath-link'选项没有被使用, 会搜索环境变量'LD_RUN_PATH'的内容.它也只
510 对本地连接器起作用.
512 4. 在SunOS上, '-rpath'选项不使用, 只搜索所有由'-L'指定的目录.
514 5. 对于一个本地连接器,环境变量'LD_LIBRARY_PATH'的内容被搜索.
516 6. 对于一个本地ELF连接器,共享库中的`DT_RUNPATH'和`DT_RPATH'操作符会被需要它的共享库搜索. 如果'DT_RUNPATH'
517 存在了, 那'DT_RPATH'就会被忽略.
519 7. 缺省目录, 常规的,如'/lib'和'/usr/lib'.
521 8. 对于ELF系统上的本地连接器, 如果文件'/etc/ld.so.conf'存在, 这个文件中有的目录会被搜索.
523 如果需要的共享库没有被找到, 那连接器会发出一条警告信息,并继续执行连接.
525 `-shared'
526 `-Bshareable'
527 创建一个共享库. 这个选项只在ELF, XCOFF和SunOS平台上有用。 在SunOS上,如果'-e'选项没有被使用,并在连接
528 中有未定义的符号,连接器会自动创建一个共享库,
530 `--sort-common'
531 这个选项告诉'ld'当它把普通符号放到相应的输出节中时按大小进行排序。排在最前面的是所有的一字节符号,然
532 后是所有的二字节,然后是所有的四字节, 然后是其它的。 这是为了避免因为对齐约束而在符号间产生的断裂
534 `--split-by-file [SIZE]'
535 跟'--split-by-reloc'相似,但在SIZE达到时,为每一个输入文件创建一个新的输出节。如果没有给出,SIZE缺省
536 地设置为1
538 `--split-by-reloc [COUNT]'
539 试图在输出文件中创建节外的节,这样就没有单个的输出节含有多于COUNT个重定位符。这在产生巨大的用于COFF格
540 式的实时内核的可重定位文件时非常有用;因为COFF不能在一个节中表示多于65535个重定位。 注意,这在不支持
541 专有节的目标文件格式中会失败,连接器不会把单个输入节分割进行重分配, 所以,如果单个输入节含有多于COUNT
542 个重定位符, 那一个输出节会含有同样多的可重定位符。COUNT缺省被设为32768.
544 `--stats'
545 计算并显示关于连接器操作的统计信息, 比如执行时间,内存占用等.
547 `--traditional-format'
548 对于某些目标平台, 'ld'的输出会跟某些面有的连接器的输出有所不同. 这个开关要求'ld'使用传
549 统的格式.
551 比如, 在SunOS上, 'ld'会把符号串表中的两上完全相同的入口合并起来. 这可以把一个带有调试信息
552 的输出文件的大小减小百发之三十. 不幸地是, SunOS的'dbx'程序不能读取这个输出的程序(gdb就没
553 有问题).'--trafitinal-format'开关告诉'ld'不要把相同的入口合并起来.
555 `--section-start SECTIONNAME=ORG'
556 通过指定ORG, 指定节在输出文件中的绝对地址. 你可以多次使用这个选项来定位多个节. ORG必须是
557 一个十六进制整数; 为了跟基他的连接器兼容,你可以忽略前导'0x'. 注意,在SECTIONNAME,等号,ORG
558 之间不允许有空格出现.
560 `-Tbss ORG'
561 `-Tdata ORG'
562 `-Ttext ORG'
563 跟-section-start同义, 不过把SECTIONNAME替换为'.bss', '.data'或'.text'.
565 `--dll-verbose'
566 `--verbose'
567 显示'ld'的版本号,并列出支持的连接器模拟. 显示哪些输入文件能被打开,而哪些不能. 显示连接器
568 使用的连接脚本.
570 `--version-script=VERSION-SCRIPTFILE'
571 指定连接器的脚本的版本名. 这个常在创建一个需要指定附加的关于版本层次的信息的共享库时使用,
572 这个选项只有支持共享库的ELF平台上有效.
574 `--warn-common'
575 当一个普通符号跟另一个普通符号或会号定义合并起来时,警告. 类Unix连接器允许这个选项,有时比
576 较实用, 但是在其他的操作系统上的连接器不允许这个. 这个选项可以让你在合并全局符号时发现某
577 些潜在的问题. 不幸的是,有些C库使用这项特性,所以你可能会像在你的程序中一样,在库中得到一些
578 警告信息.
580 这里给出三种类型的全局符号的解释(用C语言):
582 `int i = 1;'
583 一个定义, 它会存在于输出文件中的已初始化数据节.
585 `extern int i;'
586 一个未定义符号,它不占用空间. 必须在另外某一处对它有一个定义,或一个普通符号
588 `int i;'
589 一个普通符号.如果对于一个变量只有(一个或多个)普通符号, 它进入输出文件的未初始化数据域. 连
590 接器会把同一变量的多个普通符号合并成一个单一的符号. 如果他们有不同的大小, 它采用最大的一
591 个. 如果是对同一变量的定义,连接器把一个普通符号转化为一个声明.
593 '--warn-common'选项可以产生五种类型的警告. 每种警告由两行组成: 第一行描述遇到的符号, 第二
594 行描述遇到的前一个具有相同名字的符号. 一个或两个都可能成为普通符号.
596 1. 把一个普通符号转化为一个引用, 因为这个符号已经有一个定义了.
597 FILE(SECTION): warning: common of `SYMBOL'
598 overridden by definition
599 FILE(SECTION): warning: defined here
601 2. 把一个普通符号转化为一个引用,因为遇到了第二个关于符号的定义. 这跟前一种情况相同,除了符
602 号遇到的顺序相反.
603 FILE(SECTION): warning: definition of `SYMBOL'
604 overriding common
605 FILE(SECTION): warning: common is here
607 3. 把一个普通符号跟前一个相同大小的普通符号合并.
608 FILE(SECTION): warning: multiple common
609 of `SYMBOL'
610 FILE(SECTION): warning: previous common is here
612 4. 把一个普通符号跟前一个更大的普通符号合并.
613 FILE(SECTION): warning: common of `SYMBOL'
614 overridden by larger common
615 FILE(SECTION): warning: larger common is here
617 5. 把一个普通符号跟前一个更小的普通符号合并. 这跟前一种情况相同, 除了遇到的符号的顺序不同.
618 FILE(SECTION): warning: common of `SYMBOL'
619 overriding smaller common
620 FILE(SECTION): warning: smaller common is here
622 `--warn-constructors'
623 如果有全局结构体被使用到了,警告. 这只对很少的一些目标文件格式有用. 对于COFF或ELF格式, 连
624 接器不同探测到全局结构体的使用.
626 `--warn-multiple-gp'
627 如果在输出文件中,需要多个全局指针值,警告. 这只对特定的处理器有意义, 比如Alpha. 特别的,有
628 些处理器在特定的节中放入很大的常数值. 一个特殊的寄存器(全局指针)指向这个节的中间部分, 所
629 以通过一个基地址寄存器相关的地址模式,这个常数可以很容易地被载入. 因为这个基寄存器相关模式
630 的偏移值是固定的而且很小(比如,16位), 这会限制常量池的最大尺寸. 所以,一个很大的问题是,为了
631 能够定位所有可能的常数,经常需要使用多个全局指针值. 这个选项在这种情况发生时产生一条警告.
633 `--warn-once'
634 对于每一个未定义符号只警告一次, 而不是在每一个用到它的模块中警告一次.
636 `--warn-section-align'
637 如果输出节的地址因为对齐被改变了,警告. 通常, 对齐会被输入节设置. 如果'SECTION'命令没有指
638 定节的起始地址, 地址就会被隐式改变.
640 `--whole-archive'
641 对于每一个在命令行中'--whole-archive'选项后面出现的档案文件, 在连接中包含档案文件中的所有
642 目标文件, 而不是为需要的目标文件搜索档案文件. 这在把一个档案文件转化为一个共享库时使用, 把
643 所有的目标放到最终的共享库中. 这个选项可以被多次使用.
645 在GCC中使用这个选项需要注意两点: 首先,GCC不知道这个选项, 所以,你必须使用'-Wl, -whole-archive'.
646 第二, 不要忘了在你的档案文件列表的后面使用'-Wl, -no-whole-archive',因为GCC会把它自己的档
647 案列表加到你的连接后面, 而这可能并不是你所预期的.
649 `--wrap SYMBOL'
650 对SYMBOL符号使用包装函数. 任何未定义的对SYMBOL符号的引用会被解析成'_wrap_SYMBOL'. 而任何
651 未定义的对'_real_SYMBOL'的引用会被解析成SYMBOL.
653 这可以用来为系统函灵敏提供一个包装. 包装函灵敏应当被叫做'__wrap_SYMBOL'. 如果需要调用这个
654 函数, 那就应该调用'__real_SYMBOL'
656 这里是一个没什么实用价值的例子:
658 void *
659 __wrap_malloc (int c)
661 printf ("malloc called with %ld\n", c);
662 return __real_malloc ©;
665 如果你使用'--wrap malloc'把这节代码跟其他的代码连接, 那所有的对'malloc'的调用都会调用
666 '__wrap_malloc'函数. 而在'__wrap_malloc'中的'__real_malloc'会调用真正的'malloc'函数.
668 你有可能也希望提供一个'__real_malloc'函数, 这样,不带有'--wrap'的连接器也会成功连接.如果
669 你这样做了, 你不能把'__real_malloc'的定义跟'__wrap_malloc'放到同一个文件中;如果放在一起
670 汇编器会在连接器之前把调用解析成真正的'malloc'.
672 `--enable-new-dtags'
673 `--disable-new-dtags'
674 连接器可以在ELF中创建一个新的动态标签. 但是旧的ELF系统可能不理解这个. 如果你指定了
675 '--enable-new-dtags',动态标签会按需要被创建. 如果你指定了'--disable-new-dtags',那不会有
676 新的动态标签被创建. 缺省地,新的动态标签不会被创建. 注意这些选项只在ELF系统中有效.
678 i386 PE平台的特定选项.
679 -----------------------------------
681 i386 PE连接器支持'-shared'选项, 它使输出文件为一个动态链接库(DLL),而不是一个普通的可执行文件. 在
682 使用这个选项的时候,你应当为输出文件取名'*.dll',另外, 连接器完全支持标准的'*.def'文件, 这类文件可
683 以在连接器命令行上象一个目标文件一样被指定(实际上, 它应当被放在它从中导出符号的那个档案文件前面,
684 以保证它们象一个普通的目标文件一样被连接进去.)
686 除了对所有平台通用的那些选项外,i386 PE连接器支持一些只对i386平台专有的命令行选面. 带有值的选项应
687 当用空格或等号把它跟值分隔开.
689 `--add-stdcall-alias'
690 如果给出这个选项, 带有标准调用后缀(@NN)的符号会被剥掉后缀后导出.
692 `--base-file FILE'
693 使用FILE作为文件名,该文件是存放用'dlltool'产生 DLL文件时所需的所有重定位符的基地址的.(这
694 个选面是i386 PE平台所专有的]
696 `--dll'
697 创建一个DLL文件而不是一个常规可执行文件. 你可能在一个给出的'.def'文件中使用'-shared'或指
698 定'LIBRARY'.
700 `--enable-stdcall-fixup'
701 `--disable-stdcall-fixup'
702 如果连接器发现有符号不能解析, 它会试图进行'失真连接',即寻找另一个定义的符号,它们只是在
703 符号名的格式上不同(cdecl vs stdcall),并把符号解析为找到的这个符号. 比如, 一个未定义的符
704 号'_foo'可能被连接到函数'_foo@12', 或者一个未定义的符号'_bar@16'可能被连接到函数'_bar'.
705 如果连接器这么做了, 它会打印出一条警告信息, 因为在正常情况下,这会连接失败, 但有时,由第三
706 方库产生的导入库可能需要这个特性. 如果你指定了'--enable-stdcall-fixup', 这个特性会被完全
707 开启,警告信息也不会打印出来. 如果你指定了'--disable-stdcall-fixup',这个特性被关闭,而且这
708 样的错误匹配会被认为是个错误.
710 `--export-all-symbols'
711 如果给出这个选项,目标中所有由DLL建立的全局符号会被DLL导出. 注意这是缺省情况,否则没有任何
712 符号被导出. 如果符号由DEF文件显式地导出,或由函数本身的属性隐式地导出, 缺省情况是除非选项
713 给出,否则不导出任何其他的符号. 注意符号`DllMain@12',`DllEntryPoint@0',
714 `DllMainCRTStartup@12'和`impure_ptr'不会自动被导出.而且,由其他的DLL导入的符号也不会被再
715 次导出, 还有指定DLL内部布局的符号,比如那些以'_head_'开头,或者以'_iname'结尾的符号也不会
716 被导出.还有,'libgcc','libstd++','libmingw32'或'crtX.o'中的符号也不会被导出. ......
718 环境变量
719 =====================
721 你可以通过环境变量`GNUTARGET', `LDEMULATION'和`COLLECT_NO_DEMANGLE'改变'ld'的行为.
723 `GNUTARGET'在你没有使用'-b'(或者它的同义词'--format')的时候,决定输入文件的格式. 它的值应当是BFD
724 中关于输入格式的一个名字. 如果环境中没有'GNUTARGET'变量, 'ld'使用目标平台的缺省格式. 如果
725 'GNUTARGET'被设为'default', 那BFD就会通过检查二进制的输入文件来找到输入格式; 这个方法通常会成功,
726 但会有潜在的不明确性, 因为没有办法保证指定一个目标文件格式的魔数总是唯一的. 但是, 在每一个系统上
727 的BFD配置程序会把这个系统的常规格式放在搜索列表的首位, 所以不明确性可以通过这种惯列来解决.
729 `LDEMULATION'在你没有使用'-m'选项的时候决定缺省的模拟器. 模拟器可以影响到连接器行为的很多方面,
730 特别是连接器的缺省连接脚本. 你可以通过'--verbose'或'-V'选项列出所有可用的模拟器. 如果'-m'选项没
731 有使用, 而且`LDEMULATION'环境变量没有定义, 缺省的模拟器跟连接器如何被配置有关.
733 一般地,连接器缺省状况下会重构符号.但是,如果在环境中设置了`COLLECT_NO_DEMANGLE', 那缺省状态下就不
734 会重构符号.这个环境变量在GCC的连接包装程序中会以相似的方式被使用. 这个缺省的行为可以被'--demangle'
735 或'--no-demangle'选项覆盖.
737 连接脚本
738 **************
740 每个连接都被一个'连接脚本'所控制. 这个脚本是用连接命令语言书写的.
742 连接脚本的一个主要目的是描述输入文件中的节如何被映射到输出文件中,并控制输出文件的内存排布. 几乎
743 所有的连接脚本只做这两件事情. 但是,在需要的时候,连接器脚本还可以指示连接器执行很多其他的操作.这
744 通过下面描述的命令实现.
746 连接器总是使用连接器脚本的.如果你自己不提供, 连接器会使用一个缺省的脚本,这个脚本是被编译进连接器
747 可执行文件的. 你可以使用'--verbose'命令行选项来显示缺省的连接器脚本的内容. 某些命令行选项,比如
748 '-r'或'-N', 会影响缺省的连接脚本.
750 你可以过使用'-T'命令行选项来提供你自己的连接脚本. 当你这么做的时候, 你的连接脚本会替换缺省的连
751 接脚本.
753 你也可以通过把连接脚本作为一个连接器的输入文件来隐式地使用它,就象它们是一个被连接的文件一样.
755 基本的连接脚本的概念
756 ============================
758 我们需要定义一些基本的概念与词汇以描述连接脚本语言.
760 连接器把多个输入文件合并成单个输出文件. 输出文件和输入文件都以一种叫做'目标文件格式'的数据格式形
761 式存在. 每一个文件被叫做'目标文件'. 输出文件经常被叫做'可执行文件',但是由于需要,我们也把它叫做目
762 标文件. 每一个目标文件中,在其它东西之间,有一个节列表.我们有时把输入文件的节叫做输入节; 相似的,输
763 出文件中的一个节经常被叫做输出节.
765 一个目标文件中的每一个节都有一个名字和一个大小尺寸. 大多数节还有一个相关的数据块, 称为节内容. 某
766 一个节可能被标式讵'loadable',含义是在输出文件被执行时,这个节应当被载入到内存中去. 一个没有内容的
767 节可能是'allocatable', 含义是内存中必须为这个节开辟一块空间,但是没有实际的内容载入到这里(在某些
768 情况下,这块内存必须被标式讵零). 一个既不是loadable也不是allocatable的节一般含有一些调试信息.
770 每一个loadable或allocatable的输出节有两个地址. 第一个是'VMA'或称为虚拟内存地址. 这是当输出文件运
771 行时节所拥有的地址. 第二个是"LMA', 或称为载入内存地址. 这个节即将要载入的内存地址. 这大多数情况下
772 这两个地址是相同的. 它们两个有可能不同的一个例子是当一个数据节在ROM中时, 当程序启动时,被拷贝到RAM
773 中(这个技术经常被用在基于ROM的系统中进行全局变量的初始化). 在这种情况下, ROM地址就是LMA, 而RAM地
774 址就是VMA.
776 你可以通过使用带有'-h'选项的'objdump'来察看目标文件中的节.
778 每一个目标文件还有一个关于符号的列表, 被称为'符号表'. 一个符号可能是定义过了的,也可能是未定义的.
779 每一个符号有一个名字, 而且每一个定义的符号有一个地址. 如果你把一个C/C++程序编译为一个目标文件,对
780 于每一个定义的函数和全局或静态变量,你为得到一个定义的符号. 每一个在输入文件中只是一个引用而未定义
781 的函数或全局变量会变成一个未定义的符号.
783 你可以使用'nm'程序来看一个目标文件中的符号, 或者使用'objdump'程序带有'-t'选项.
785 连接脚本的格式
786 ====================
788 连接脚本是文本文件.
790 你写了一系列的命令作为一个连接脚本. 每一个命令是一个带有参数的关键字,或者是一个对符号的赋值. 你可
791 以用分号分隔命令. 空格一般被忽略.
793 文件名或格式名之类的字符串一般可以被直接键入. 如果文件名含有特殊字符,比如一般作为分隔文件名用的逗
794 号, 你可以把文件名放到双引号中. 文件名中间无法使用双引号.
796 你可以象在C语言中一样,在连接脚本中使用注释, 用'/*'和'*/'隔开. 就像在C中,注释在语法上等同于空格.
798 简单的连接脚本示例
799 ============================
801 许多脚本是相当的简单的.
803 可能的最简单的脚本只含有一个命令: 'SECTIONS'. 你可以使用'SECTIONS'来描述输出文件的内存布局.
805 'SECTIONS'是一个功能很强大的命令. 这里这们会描述一个很简单的使用. 让我们假设你的程序只有代码节,
806 初始化过的数据节, 和未初始化过的数据节. 这些会存在于'.text','.data'和'.bss'节, 另外, 让我们进一
807 步假设在你的输入文件中只有这些节.
809 对于这个例子, 我们说代码应当被载入到地址'0x10000'处, 而数据应当从0x8000000处开始. 下面是一个实现
810 这个功能的脚本:
812 SECTIONS
814 . = 0x10000;
815 .text : { *(.text) }
816 . = 0x8000000;
817 .data : { *(.data) }
818 .bss : { *(.bss) }
821 你使用关键字'SECTIONS'写了这个SECTIONS命令, 后面跟有一串放在花括号中的符号赋值和输出节描述的内容.
823 上例中, 在'SECTIONS'命令中的第一行是对一个特殊的符号'.'赋值, 这是一个定位计数器. 如果你没有以其
824 它的方式指定输出节的地址(其他方式在后面会描述), 那地址值就会被设为定位计数器的现有值. 定位计数器
825 然后被加上输出节的尺寸. 在'SECTIONS'命令的开始处, 定位计数器拥有值'0'.
827 第二行定义一个输出节,'.text'. 冒号是语法需要,现在可以被忽略. 节名后面的花括号中,你列出所有应当被
828 放入到这个输出节中的输入节的名字. '*'是一个通配符,匹配任何文件名. 表达式'*(.text)'意思是所有的输
829 入文件中的'.text'输入节.
831 因为当输出节'.text'定义的时候, 定位计数器的值是'0x10000',连接器会把输出文件中的'.text'节的地址设
832 为'0x10000'.
834 余下的内容定义了输出文件中的'.data'节和'.bss'节. 连接器会把'.data'输出节放到地址'0x8000000'处. 连接
835 器放好'.data'输出节之后, 定位计数器的值是'0x8000000'加上'.data'输出节的长度. 得到的结果是连接器会
836 把'.bss'输出节放到紧接'.data'节后面的位置.
838 连接器会通过在必要时增加定位计数器的值来保证每一个输出节具有它所需的对齐. 在这个例子中, 为'.text'
839 和'.data'节指定的地址会满足对齐约束, 但是连接器可能会需要在'.data'和'.bss'节之间创建一个小的缺口.
841 就这样,这是一个简单但完整的连接脚本.
843 简单的连接脚本命令.
844 =============================
846 在本章中,我们会描述一些简单的脚本命令.
848 设置入口点.
849 -----------------------
851 在运行一个程序时第一个被执行到的指令称为"入口点". 你可以使用'ENTRY'连接脚本命令来设置入口点.参数
852 是一个符号名:
853 ENTRY(SYMBOL)
855 有多种不同的方法来设置入口点.连接器会通过按顺序尝试以下的方法来设置入口点, 如果成功了,就会停止.
857 * `-e'入口命令行选项;
859 * 连接脚本中的`ENTRY(SYMBOL)'命令;
861 * 如果定义了start, 就使用start的值;
863 * 如果存在,就使用'.text'节的首地址;
865 * 地址`0'.
867 处理文件的命令.
868 ---------------------------
870 有几个处理文件的连接脚本命令.
872 `INCLUDE FILENAME'
873 在当前点包含连接脚本文件FILENAME. 在当前路径下或用'-L'选项指定的所有路径下搜索这个文件,
874 你可以嵌套使用'INCLUDE'达10层.
876 `INPUT(FILE, FILE, ...)'
877 `INPUT(FILE FILE ...)'
878 'INPUT'命令指示连接器在连接时包含文件, 就像它们是在命令行上指定的一样.
880 比如,如果你在连接的时候总是要包含文件'subr.o',但是你对每次连接时要在命令行上输入感到厌烦
881 , 你就可以在你的连接脚本中输入'INPUT (subr.o).
883 事实上,如果你喜欢,你可以把你所有的输入文件列在连接脚本中, 然后在连接的时候什么也不需要,
884 只要一个'-T'选项就够了.
886 在一个'系统根前缀'被配置的情况下, 一个文件名如果以'/'字符打头, 并且脚本也存放在系统根
887 前缀的某个子目录下, 文件名就会被在系统根前缀下搜索. 否则连接器就会企图打开当前目录下的文
888 件. 如果没有发现, 连接器会通过档案库搜索路径进行搜索.
890 如果你使用了'INPUT (-lFILE)', 'ld'会把文件名转换为'libFILE.a', 就象命令行参数'-l'一样.
892 当你在一个隐式连接脚本中使用'INPUT'命令的时候, 文件就会在连接时连接脚本文件被包含的点上
893 被包含进来. 这会影响到档案搜索.
895 `GROUP(FILE, FILE, ...)'
896 `GROUP(FILE FILE ...)'
897 除了文件必须全是档案文件之外, 'GROUP'命令跟'INPUT'相似, 它们会被反复搜索,直至没有未定义
898 的引用被创建.
900 `OUTPUT(FILENAME)'
901 'OUTPUT'命令命名输出文件. 在连接脚本中使用'OUTPUT(FILENAME)'命令跟在命令行中使用'-o
902 FILENAME'命令是完全等效的. 如果两个都使用了, 那命令行选项优先.
904 你可以使用'OUTPUT'命令为输出文件创建一个缺省的文件名,而不是常用的'a.out'.
906 `SEARCH_DIR(PATH)'
907 `SEARCH_DIR'命令给'ld'用于搜索档案文件的路径中再增加新的路径. 使用`SEARCH_DIR(PATH)'跟在
908 命令行上使用'-L PATH'选项是完全等效的. 如果两个都使用了, 那连接器会两个路径都搜索. 用命
909 令行选项指定的路径首先被搜索.
911 `STARTUP(FILENAME)'
912 除了FILENAME会成为第一个被连接的输入文件, 'STARTUP'命令跟'INPUT'命令完全相似, 就象这个文
913 件是在命令行上第一个被指定的文件一样. 如果在一个系统中, 入口点总是存在于第一个文件中,那
914 这个就很有用.
916 处理目标文件格式的命令.
917 -----------------------------------------
919 有两个处理目标文件格式的连接脚本命令.
921 `OUTPUT_formAT(BFDNAME)'
922 `OUTPUT_formAT(DEFAULT, BIG, LITTLE)'
923 `OUTPUT_formAT'命令为输出文件使用的BFD格式命名. 使用`OUTPUT_formAT(BFDNAME)'跟在命令行上
924 使用'-oformat BFDNAME'是完全等效的. 如果两个都使用了, 命令行选项优先.
926 你可在使用`OUTPUT_formAT'时带有三个参数以使用不同的基于'-EB'和'-EL'的命令行选项的格式.
928 如果'-EB'和'-EL'都没有使用, 那输出格式会是第一个参数DEFAULT, 如果使用了'-EB',输出格式会是
929 第二个参数BIG, 如果使用了'-EL', 输出格式会是第三个参数, LITTLE.
931 比如, 缺省的基于MIPS ELF平台连接脚本使用如下命令:
933 OUTPUT_formAT(elf32-bigmips, elf32-bigmips, elf32-littlemips)
934 这表示缺省的输出文件格式是'elf32-bigmips', 但是当用户使用'-EL'命令行选项的时候, 输出文件就会
935 被以`elf32-littlemips'格式创建.
937 `TARGET(BFDNAME)'
938 'TARGET'命令在读取输入文件时命名BFD格式. 它会影响到后来的'INPUT'和'GROUP'命令. 这个命令跟
939 在命令行上使用`-b BFDNAME'相似. 如果使用了'TARGET'命令但`OUTPUT_formAT'没有指定, 最后的
940 'TARGET'命令也被用来设置输出文件的格式.
942 其它的连接脚本命令.
943 ----------------------------
945 还有一些其它的连接脚本命令.
947 `ASSERT(EXP, MESSAGE)'
948 确保EXP不等于零,如果等于零, 连接器就会返回一个错误码退出,并打印出MESSAGE.
950 `EXTERN(SYMBOL SYMBOL ...)'
951 强制SYMBOL作为一个无定义的符号输入到输出文件中去. 这样做了,可能会引发从标准库中连接一些
952 节外的库. 你可以为每一个EXTERN'列出几个符号, 而且你可以多次使用'EXTERN'. 这个命令跟'-u'
953 命令行选项具有相同的效果.
955 `FORCE_COMMON_ALLOCATION'
956 这个命令跟命令行选项'-d'具有相同的效果: 就算指定了一个可重定位的输出文件('-r'),也让'ld'
957 为普通符号分配空间.
959 `INHIBIT_COMMON_ALLOCATION'
960 这个命令跟命令行选项`--no-define-common'具有相同的效果: 就算是一个不可重位输出文件, 也让
961 'ld'忽略为普通符号分配的空间.
963 `NOCROSSREFS(SECTION SECTION ...)'
964 这个命令在遇到在某些特定的节之间引用的时候会产生一条错误信息.
966 在某些特定的程序中, 特别是在使用覆盖技术的嵌入式系统中, 当一个节被载入内存时,另外一个节
967 就不会在内存中. 任何在两个节之间的直接引用都会是一个错误. 比如, 如果节1中的代码调用了另
968 一个节中的一个函数,这就会产生一个错误.
970 `NOCROSSREFS'命令带有一个输出节名字的列表. 如果'ld'遇到任何在这些节之间的交叉引用, 它就
971 会报告一个错误,并返回一个非零退出码. 注意, `NOCROSSREFS'命令使用输出节名,而不是输入节名.
973 `OUTPUT_ARCH(BFDARCH)'
974 指定一个特定的输出机器架构. 这个参数是BFD库中使用的一个名字. 你可以通过使用带有'-f'选项
975 的'objdump'程序来查看一个目标文件的架构.
977 为符号赋值.
978 ===========================
980 你可以在一个连接脚本中为一个符号赋一个值. 这会把一个符号定义为一个全局符号.
982 简单的赋值.
983 ------------------
985 你可以使用所有的C赋值符号为一个符号赋值.
987 `SYMBOL = EXPRESSION ;'
988 `SYMBOL += EXPRESSION ;'
989 `SYMBOL -= EXPRESSION ;'
990 `SYMBOL *= EXPRESSION ;'
991 `SYMBOL /= EXPRESSION ;'
992 `SYMBOL <<= EXPRESSION ;'
993 `SYMBOL >>= EXPRESSION ;'
994 `SYMBOL &= EXPRESSION ;'
995 `SYMBOL |= EXPRESSION ;'
997 第一个情况会把SYMBOL定义为值EXPRESSION. 其它情况下, SYMBOL必须是已经定义了的, 而值会作出相应的调
998 整.
1000 特殊符号名'.'表示定位计数器. 你只可以在'SECTIONS'命令中使用它.
1002 EXPRESSION后面的分号是必须的.
1004 表达式下面会定义.
1006 你在写表达式赋值的时候,可以把它们作为单独的部分,也可以作为'SECTIONS'命令中的一个语句,或者作为
1007 'SECTIONS'命令中输出节描述的一个部分.
1009 符号所在的节会被设置成表达式所在的节.
1011 下面是一个关于在三处地方使用符号赋值的例子:
1013 floating_point = 0;
1014 SECTIONS
1016 .text :
1018 *(.text)
1019 _etext = .;
1021 _bdata = (. + 3) & ~ 3;
1022 .data : { *(.data) }
1025 在这个例子中, 符号`floating_point'被定义为零. 符号'-etext'会被定义为前面一个'.text'节尾部的地址.
1026 而符号'_bdata'会被定义为'.text'输出节后面的一个向上对齐到4字节边界的一个地址值.
1028 PROVIDE
1029 -------
1031 在某些情况下, 一个符号被引用到的时候只在连接脚本中定义,而不在任何一个被连接进来的目标文件中定
1032 义. 这种做法是比较明智的. 比如, 传统的连接器定义了一个符号'etext'. 但是, ANSI C需要用户能够把
1033 'etext'作为一个函数使用而不会产生错误. 'PROVIDE'关键字可以被用来定义一个符号, 比如'etext', 这个
1034 定义只在它被引用到的时候有效,而在它被定义的时候无效.语法是 `PROVIDE(SYMBOL = EXPRESSION)'.
1036 下面是一个关于使用'PROVIDE'定义'etext'的例子:
1038 SECTIONS
1040 .text :
1042 *(.text)
1043 _etext = .;
1044 PROVIDE(etext = .);
1048 在这个例子中, 如果程序定义了一个'_etext'(带有一个前导下划线), 连接器会给出一个重定义错误. 如果,
1049 程序定义了一个'etext'(不带前导下划线), 连接器会默认使用程序中的定义. 如果程序引用了'etext'但不
1050 定义它, 连接器会使用连接脚本中的定义.
1052 SECTIONS命令
1053 ================
1055 'SECTIONS'命令告诉连接器如何把输入节映射到输出节, 并如何把输出节放入到内存中.
1057 'SECTIONS'命令的格式如下:
1059 SECTIONS
1061 SECTIONS-COMMAND
1062 SECTIONS-COMMAND
1066 每一个SECTIONS-COMMAND可能是如下的一种:
1068 * 一个'ENTRY'命令.
1070 * 一个符号赋值.
1072 * 一个输出节描述.
1074 * 一个重叠描述.
1076 'ENTRY'命令和符号赋值在'SECTIONS'命令中是允许的, 这是为了方便在这些命令中使用定位计数器. 这也可
1077 以让连接脚本更容易理解, 因为你可以在更有意义的地方使用这些命令来控制输出文件的布局.
1079 输出节描述和重叠描述在下面描述.
1081 如果你在连接脚本中不使用'SECTIONS'命令, 连接器会按在输入文件中遇到的节的顺序把每一个输入节放到同
1082 名的输出节中. 如果所有的输入节都在第一个文件中存在,那输出文件中的节的顺序会匹配第一个输入文件中
1083 的节的顺序. 第一个节会在地址零处.
1085 输出节描述
1086 --------------------------
1088 一个完整的输出节的描述应该是这个样子的:
1090 SECTION [ADDRESS] [(TYPE)] : [AT(LMA)]
1092 OUTPUT-SECTION-COMMAND
1093 OUTPUT-SECTION-COMMAND
1095 } [>REGION] [AT>LMA_REGION] [:PHDR :PHDR ...] [=FILLEXP]
1097 大多数输出节不使用这里的可选节属性.
1099 SECTION边上的空格是必须的, 所以节名是明确的. 冒号跟花括号也是必须的. 断行和其他的空格是可选的.
1101 每一个OUTPUT-SECTION-COMMAND可能是如下的情况:
1103 * 一个符号赋值.
1105 * 一个输入节描述.
1107 * 直接包含的数据值.
1109 * 一个特定的输出节关键字.
1111 输出节名.
1112 -------------------
1114 输出节的名字是SECTION. SECTION必须满足你的输出格式的约束. 在一个只支持限制数量的节的格式中,比如
1115 'a.out',这个名字必须是格式支持的节名中的一个(比如, 'a.out'只允许'.text', '.data'或'.bss').如果
1116 输出格式支持任意数量的节, 但是只支持数字,而没有名字(就像Oasys中的情况), 名字应当以一个双引号中的
1117 数值串的形式提供.一个节名可以由任意数量的字符组成,但是一个含有任意非常用字符(比如逗号)的字句必须
1118 用双引号引起来.
1120 输出节描述
1121 --------------------------
1123 ADDRESS是关于输出节中VMS的一个表达式. 如果你不提供ADDRESS, 连接器会基于REGION(如果存在)设置它,或
1124 者基于定位计数器的当前值.
1126 如果你提供了ADDRESS, 那输出节的地址会被精确地设为这个值. 如果你既不提供ADDRESS也不提供REGION, 那
1127 输出节的地址会被设为当前的定位计数器向上对齐到输出节需要的对齐边界的值. 输出节的对齐要求是所有输
1128 入节中含有的对齐要求中最严格的一个.
1130 比如:
1131 .text . : { *(.text) }
1134 .text : { *(.text) }
1136 有细微的不同. 第一个会把'.text'输出节的地址设为当前定位计数器的值. 第二个会把它设为定位计数器的
1137 当前值向上对齐到'.text'输入节中对齐要求最严格的一个边界.
1139 ADDRESS可以是任意表达式; 比如,如果你需要把节对齐对0x10字节边界,这样就可以让低四字节的节地址值为
1140 零, 你可以这样做:
1142 .text ALIGN(0x10) : { *(.text) }
1144 这个语句可以正常工作,因为'ALIGN'返回当前的定位计数器,并向上对齐到指定的值.
1146 指定一个节的地址会改变定位计数器的值.
1148 输入节描述
1149 -------------------------
1151 最常用的输出节命令是输入节描述.
1153 输入节描述是最基本的连接脚本操作. 你使用输出节来告诉连接器在内存中如何布局你的程序. 你使用输入节
1154 来告诉连接器如何把输入文件映射到你的内存中.
1156 输入节基础
1157 ---------------------------
1159 一个输入节描述由一个文件名后跟有可选的括号中的节名列表组成.
1161 文件名和节名可以通配符形式出现, 这个我们以后再介绍.
1163 最常用的输入节描述是包含在输出节中的所有具有特定名字的输入节. 比如, 包含所有输入'.text'节,你可以
1164 这样写:
1166 *(.text)
1168 这里,'*'是一个通配符,匹配所有的文件名. 为把一部分文件排除在匹配的名字通配符之外, EXCLUDE_FILE可
1169 以用来匹配所有的除了在EXCLUDE_FILE列表中指定的文件.比如:
1171 (*(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors))
1173 会让除了`crtend.o'文件和`otherfile.o'文件之外的所有的文件中的所有的.ctors节被包含进来.
1175 有两种方法包含多于一个的节:
1177 *(.text .rdata)
1178 *(.text) *(.rdata)
1180 上面两句的区别在于'.text'和'.rdata'输入节的输出节中出现的顺序不同. 在第一个例子中, 两种节会交替
1181 出现,并以连接器的输入顺序排布. 在第二个例子中,所有的'.text'输入节会先出现,然后是所有的'.rdata'节.
1183 你可以指定文件名,以从一个特定的文件中包含节. 如果一个或多个你的文件含有特殊的数据在内存中需要特
1184 殊的定位,你可以这样做. 比如:
1186 data.o(.data)
1188 如果你使用一个不带有节列表的文件名, 那输入文件中的所有的节会被包含到输出节中. 通常不会这样做, 但
1189 是在某些场合下这个可能非常有用. 比如:
1191 data.o
1193 当你使用一个不含有任何通配符的文件名时, 连接器首先会查看你是否在连接命令行上指定了文件名或者在
1194 'INPUT'命令中. 如果你没有, 连接器会试图把这个文件作为一个输入文件打开, 就像它在命令行上出现一样.
1195 注意这跟'INPUT'命令不一样, 因为连接器会在档案搜索路径中搜索文件.
1197 输入节通配符
1198 ---------------------------------
1200 在一个输入节描述中, 文件名或者节名,或者两者同时都可以是通配符形式.
1202 文件名通配符'*'在很多例子中都可以看到,这是一个简单的文件名通配符形式.
1204 通配符形式跟Unix Shell中使用的一样.
1207 匹配任意数量的字符.
1210 匹配单个字符.
1212 `[CHARS]'
1213 匹配CHARS中的任意单个字符; 字符'-'可以被用来指定字符的方讧, 比如[a-z]匹配任意小字字符.
1216 转义其后的字符.
1218 当一个文件名跟一个通配符匹配时, 通配符字符不会匹配一个'/'字符(在UNIX系统中用来分隔目录名), 一个
1219 含有单个'*'字符的形式是个例外; 它总是匹配任意文件名, 不管它是否含有'/'. 在一个节名中, 通配符字
1220 符会匹配'/'字符.
1222 文件名通配符只匹配那些在命令行或在'INPUT'命令上显式指定的文件. 连接器不会通过搜索目录来展开通配
1223 符.
1225 如果一个文件名匹配多于一个通配符, 或者如果一个文件名显式出现同时又匹配了一个通配符, 连接器会使用
1226 第一次匹配到的连接脚本. 比如, 下面的输入节描述序列很可能就是错误的,因为'data.o'规则没有被使用:
1228 .data : { *(.data) }
1229 .data1 : { data.o(.data) }
1231 通常, 连接器会把匹配通配符的文件和节按在连接中被看到的顺序放置. 你可以通过'SORT'关键字改变它, 它
1232 出现在括号中的通配符之前(比如, 'SORT(.text*)'). 当'SORT'关键字被使用时, 连接器会在把文件和节放到
1233 输出文件中之前按名字顺序重新排列它们.
1235 如果你对于输入节被放置到哪里去了感到很困惑, 那可以使用'-M'连接选项来产生一个位图文件. 位图文件会
1236 精确显示输入节是如何被映射到输出节中的.
1238 这个例子显示了通配符是如何被用来区分文件的. 这个连接脚本指示连接器把所有的'.text'节放到'.text'中, 把所有的'.bss'节放到'.bss'. 连接器会把所有的来自文件名以一个大写字母开始的文件中的'.data'节放进'.DATA'节中; 对于所有其他文件, 连接器会把'.data'节放进'.data'节中.
1240 SECTIONS {
1241 .text : { *(.text) }
1242 .DATA : { [A-Z]*(.data) }
1243 .data : { *(.data) }
1244 .bss : { *(.bss) }
1247 输入节中的普通符号.
1248 -----------------------------------
1250 对于普通符号,需要一个特殊的标识, 因为在很多目标格式中, 普通符号没有一个特定的输入节. 连接器会把
1251 普通符号处理成好像它们在一个叫做'COMMON'的节中.
1253 你可能像使用带有其他输入节的文件名一样使用带有'COMMON'节的文件名。你可以通过这个把来自一个特定输
1254 入文件的普通符号放入一个节中,同时把来自其它输入文件的普通符号放入另一个节中。
1256 在大多数情况下,输入文件中的普通符号会被放到输出文件的'.bss'节中。比如:
1258 .bss { *(.bss) *(COMMON) }
1260 有些目标文件格式具有多于一个的普通符号。比如,MIPS ELF目标文件格式区分标准普通符号和小普通符号。
1261 在这种情况下,连接器会为其他类型的普通符号使用一个不同的特殊节名。 在MIPS ELF的情况中, 连接器
1262 为标准普通符号使用'COMMON',并且为小普通符号使用'.common'。这就允许你把不同类型的普通符号映射到
1263 内存的不同位置。
1265 在一些老的连接脚本上,你有时会看到'[COMMON]'。这个符号现在已经过时了, 它等效于'*(COMMON)'。
1267 输入节和垃圾收集
1268 ---------------------------------------
1270 当连接时垃圾收集正在使用中时('--gc-sections'),这在标识那些不应该被排除在外的节时非常有用。这
1271 是通过在输入节的通配符入口外面加上'KEEP()'实现的,比如'KEEP(*(.init))'或者'KEEP(SORT(*)(.sorts))
1272 '。
1274 输入节示例
1275 ---------------------
1277 接下来的例子是一个完整的连接脚本。它告诉连接器去读取文件'all.o'中的所有节,并把它们放到输出节
1278 'outputa'的开始位置处, 该输出节是从位置'0x10000'处开始的。 从文件'foo.o'中来的所有节'.input1'
1279 在同一个输出节中紧密排列。 从文件'foo.o'中来的所有节'.input2'全部放入到输出节'outputb'中,后面
1280 跟上从'foo1.o'中来的节'.input1'。来自所有文件的所有余下的'.input1'和'.input2'节被写入到输出节
1281 'outputc'中。
1283 SECTIONS {
1284 outputa 0x10000 :
1286 all.o
1287 foo.o (.input1)
1289 outputb :
1291 foo.o (.input2)
1292 foo1.o (.input1)
1294 outputc :
1296 *(.input1)
1297 *(.input2)
1301 输出节数据
1302 -------------------
1304 你可以通过使用输出节命令'BYTE','SHORT','LONG','QUAD',或者'SQUAD'在输出节中显式包含几个字节的数据
1305 每一个关键字后面都跟上一个圆括号中的要存入的值。表达式的值被存在当前的定位计数器的值处。
1307 ‘BYTE’,‘SHORT’,‘LONG’‘QUAD’命令分别存储一个,两个,四个,八个字节。存入字节后,定位计
1308 数器的值加上被存入的字节数。
1310 比如,下面的命令会存入一字节的内容1,后面跟上四字节,其内容是符号'addr'的值。
1312 BYTE(1)
1313 LONG(addr)
1315 当使用64位系统时,‘QUAD’和‘SQUAD’是相同的;它们都会存储8字节,或者说是64位的值。而如果软硬件
1316 系统都是32位的,一个表达式就会被作为32位计算。在这种情况下,‘QUAD’存储一个32位值,并把它零扩展
1317 到64位, 而‘SQUAD’会把32位值符号扩展到64位。
1319 如果输出文件的目标文件格式有一个显式的endianness,它在正常的情况下,值就会被以这种endianness存储
1320 当一个目标文件格式没有一个显式的endianness时, 值就会被以第一个输入目标文件的endianness存储。
1322 注意, 这些命令只在一个节描述内部才有效,而不是在它们之间, 所以,下面的代码会使连接器产生一个错
1323 误信息:
1325 SECTIONS { .text : { *(.text) } LONG(1) .data : { *(.data) } }
1327 而这个才是有效的:
1329 SECTIONS { .text : { *(.text) ; LONG(1) } .data : { *(.data) } }
1331 你可能使用‘FILL’命令来为当前节设置填充样式。它后面跟有一个括号中的表达式。任何未指定的节内内存
1332 区域(比如,因为输入节的对齐要求而造成的裂缝)会以这个表达式的值进行填充。一个'FILL'语句会覆盖到
1333 它本身在节定义中出现的位置后面的所有内存区域;通过引入多个‘FILL’语句,你可以在输出节的不同位置
1334 拥有不同的填充样式。
1336 这个例子显示如何在未被指定的内存区域填充'0x90':
1338 FILL(0x90909090)
1340 ‘FILL’命令跟输出节的‘=FILLEXP’属性相似,但它只影响到节内跟在‘FILL’命令后面的部分,而不是
1341 整个节。如果两个都用到了,那‘FILL’命令优先。
1343 输出节关键字
1344 -----------------------
1346 有两个关键字作为输出节命令的形式出现。
1348 `CREATE_OBJECT_SYMBOLS'
1349 这个命令告诉连接器为每一个输入文件创建一个符号。而符号的名字正好就是相关输入文件的名字。
1350 而每一个符号的节就是`CREATE_OBJECT_SYMBOLS'命令出现的那个节。
1352 这个命令一直是a.out目标文件格式特有的。 它一般不为其它的目标文件格式所使用。
1354 `CONSTRUCTORS'
1355 当使用a.out目标文件格式进行连接的时候, 连接器使用一组不常用的结构以支持C++的全局构造函
1356 数和析构函数。当连接不支持专有节的目标文件格式时, 比如ECOFF和XCOFF,连接器会自动辩识C++
1357 全局构造函数和析构函数的名字。对于这些目标文件格式,‘CONSTRUCTORS’命令告诉连接器把构造
1358 函数信息放到‘CONSTRUCTORS’命令出现的那个输出节中。对于其它目标文件格式,‘CONSTRUCTORS’
1359 命令被忽略。
1361 符号`__CTOR_LIST__'标识全局构造函数的开始,而符号`__DTOR_LIST'标识结束。这个列表的第一个
1362 WORD是入口的数量,紧跟在后面的是每一个构造函数和析构函数的地址,再然后是一个零WORD。编译
1363 器必须安排如何实际运行代码。对于这些目标文件格式,GNU C++通常从一个`__main'子程序中调用
1364 构造函数,而对`__main'的调用自动被插入到`main'的启动代码中。GNU C++通常使用'atexit'运行
1365 析构函数,或者直接从函数'exit'中运行。
1367 对于像‘COFF’或‘ELF’这样支持专有节名的目标文件格式,GNU C++通常会把全局构造函数与析构
1368 函数的地址值放到'.ctors'和'.dtors'节中。把下面的代码序列放到你的连接脚本中去,这样会构建
1369 出GNU C++运行时代码希望见到的表类型。
1371 __CTOR_LIST__ = .;
1372 LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
1373 *(.ctors)
1374 LONG(0)
1375 __CTOR_END__ = .;
1376 __DTOR_LIST__ = .;
1377 LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
1378 *(.dtors)
1379 LONG(0)
1380 __DTOR_END__ = .;
1382 如果你正使用GNU C++支持来进行优先初始化,那它提供一些可以控制全局构造函数运行顺序的功能,
1383 你必须在连接时给构造函数排好序以保证它们以正确的顺序被执行。当使用'CONSTRUCTORS'命令时,
1384 替代为`SORT(CONSTRUCTORS)'。当使用'.ctors'和'dtors'节时,使用`*(SORT(.ctors))'和
1385 `*(SORT(.dtors))' 而不是`*(.ctors)'和`*(.dtors)'。
1387 通常,编译器和连接器会自动处理这些事情,并且你不必亲自关心这些事情。但是,当你正在使用
1388 C++,并自己编写连接脚本时,你可能就要考虑这些事情了。
1390 输出节的丢弃。
1391 -------------------------
1393 连接器不会创建那些不含有任何内容的输出节。这是为了引用那些可能出现或不出现在任何输入文件中的输入
1394 节时方便。比如:
1396 .foo { *(.foo) }
1398 如果至少在一个输入文件中有'.foo'节,它才会在输出文件中创建一个'.foo'节
1400 如果你使用了其它的而不是一个输入节描述作为一个输出节命令,比如一个符号赋值,那这个输出节总是被
1401 创建,即使没有匹配的输入节也会被创建。
1403 一个特殊的输出节名`/DISCARD/'可以被用来丢弃输入节。任何被分配到名为`/DISCARD/'的输出节中的输入
1404 节不包含在输出文件中。
1406 输出节属性
1407 -------------------------
1409 上面,我们已经展示了一个完整的输出节描述,看下去就象这样:
1411 SECTION [ADDRESS] [(TYPE)] : [AT(LMA)]
1413 OUTPUT-SECTION-COMMAND
1414 OUTPUT-SECTION-COMMAND
1416 } [>REGION] [AT>LMA_REGION] [:PHDR :PHDR ...] [=FILLEXP]
1418 我们已经介绍了SECTION, ADDRESS, 和OUTPUT-SECTION-COMMAND. 在这一节中,我们将介绍余下的节属性。
1420 输出节类型
1421 ...................
1423 每一个输出节可以有一个类型。类型是一个放在括号中的关键字,已定义的类型如下所示:
1425 `NOLOAD'
1426 这个节应当被标式讵不可载入,所以当程序运行时,它不会被载入到内存中。
1428 `DSECT'
1429 `COPY'
1430 `INFO'
1431 `OVERLAY'
1432 支持这些类型名只是为了向下兼容,它们很少使用。它们都具有相同的效果:这个节应当被标式讵不
1433 可分配,所以当程序运行时,没有内存为这个节分配。
1435 连接器通常基于映射到输出节的输入节来设置输出节的属性。你可以通过使用节类型来重设这个属性,
1436 比如,在下面的脚本例子中,‘ROM’节被定址在内存地址零处,并且在程序运行时不需要被载入。
1437 ‘ROM’节的内容会正常出现在连接输出文件中。
1439 SECTIONS {
1440 ROM 0 (NOLOAD) : { ... }
1444 输出节LMA
1445 ..................
1447 每一个节有一个虚地址(VMA)和一个载入地址(LMA);出现在输出节描述中的地址表达式设置VMS
1449 连接器通常把LMA跟VMA设成相等。你可以通过使用‘AT’关键字改变这个。跟在关键字‘AT’后面的表达式
1450 LMA指定节的载入地址。或者,通过`AT>LMA_REGION'表达式, 你可以为节的载入地址指定一个内存区域。
1452 这个特性是为了便于建立ROM映像而设计的。比如,下面的连接脚本创建了三个输出节:一个叫做‘.text’
1453 从地址‘0x1000’处开始,一个叫‘.mdata’,尽管它的VMA是'0x2000',它会被载入到'.text'节的后面,最
1454 后一个叫做‘.bss’是用来放置未初始化的数据的,其地址从'0x3000'处开始。符号'_data'被定义为值
1455 '0x2000', 它表示定位计数器的值是VMA的值,而不是LMA。
1457 SECTIONS
1459 .text 0x1000 : { *(.text) _etext = . ; }
1460 .mdata 0x2000 :
1461 AT ( ADDR (.text) + SIZEOF (.text) )
1462 { _data = . ; *(.data); _edata = . ; }
1463 .bss 0x3000 :
1464 { _bstart = . ; *(.bss) *(COMMON) ; _bend = . ;}
1467 这个连接脚本产生的程序使用的运行时初始化代码会包含象下面所示的一些东西,以把初始化后的数据从ROM
1468 映像中拷贝到它的运行时地址中去。注意这节代码是如何利用好连接脚本定义的符号的。
1470 extern char _etext, _data, _edata, _bstart, _bend;
1471 char *src = &_etext;
1472 char *dst = &_data;
1474 /* ROM has data at end of text; copy it. */
1475 while (dst < &_edata) {
1476 *dst++ = *src++;
1479 /* Zero bss */
1480 for (dst = &_bstart; dst< &_bend; dst++)
1481 *dst = 0;
1483 输出节区域
1484 .....................
1486 你可以通过使用`>REGION'把一个节赋给前面已经定义的一个内存区域。
1488 这里有一个简单的例子:
1490 MEMORY { rom : ORIGIN = 0x1000, LENGTH = 0x1000 }
1491 SECTIONS { ROM : { *(.text) } >rom }
1493 输出节Phdr 
1495 ----8<----
1496  某些特定类型的程序头描述系统载入程序要从文件中载入到内存的节。在连接脚本中,
1497  你通过把可载入的输出节放到段中来指定这些段的内容。你可以使用 ‘:PHDR’输出节属性把一个节放到一个特定的段中。把某些节放到多个段中也是正常的。这仅仅暗示了一个内存段中含有另一个段。你可以重复使用 ‘:PHDR’,在每一个应当含有这个节的段中使用它一次。如果你使用‘:PHDR’把一个节放到多个段中,那连接器把随后的所有没有指定‘:PHDR’ 的可分配节都放到同一个段中。这是为了方便,因为通常一串连续的节会被放到一个单独的段中。你可以使用‘:NONE’来覆盖缺省的段,告诉连接器不要把节放到任何一个段中。你可能在程序头类型后面使用‘FILEHDR’和‘PHDRS’关键字来进一步描述段的内容。‘FILEHDR’关键字表示段应当包含 ELF文件头。‘PHDRS’关键字表示段应当包含ELF程序头本身。 TYPE可以是如下的一个。数字表示关键字的值。 `PT_NULL' (0) 表示一个不用的程序头。 `PT_LOAD' (1) 表示这个程序头描述了一个被从文件中载入的段。 `PT_DYNAMIC' (2) 表示一个可以从中找到动态链接信息的段。 `PT_INTERP' (3) 表示一个可以从中找到关于程序名解释的段。 `PT_NOTE' (4) 表示一个存有备注信息的段。 `PT_SHLIB' (5) 一个保留的程序头类型,被定义了,但没有被ELF ABI指定。 `PT_PHDR' (6) 表示一个可以从中找到程序头的段。 EXPRESSION 一个给出程序头的数值类型的表达式。这可以在使用上面未定义的类型时使用。你可以通过使用‘AT’表达式指定一个段应当被载入到内存中的一个特定的地址。这跟在输出节属性中使用‘AT’命令是完全一样的。程序头中的‘AT’命令会覆盖输出节属性中的。连接器通常会基于组成段的节来设置段属性。你可以通过使用‘FLAGS’关键字来显式指定段标志。FLAGS的值必须是一个整型值。它被用来设置程序头的‘p_flags'域。这里是一个关于‘PHDRS’的例子。它展示一个在纯ELF系统上的一个标准的程序头设置。 PHDRS { headers PT_PHDR PHDRS ; interp PT_INTERP ; text PT_LOAD FILEHDR PHDRS ; data PT_LOAD ; dynamic PT_DYNAMIC ; } SECTIONS { . = SIZEOF_HEADERS; .interp : { *(.interp) } :text :interp .text : { *(.text) } :text .rodata : { *(.rodata) } /* defaults to :text */ ... . = . + 0x1000; /* move to a new page in memory */ .data : { *(.data) } :data .dynamic : { *(.dynamic) } :data :dynamic ... } VERSION命令 =============== 在使用ELF时,连接器支持符号版本。符号版本只在使用共享库时有用。动态连接器在运行一个可能跟一个更早版本的共享库链接程序时,可以使用符号版本来选择一个函数的特定版本。你可以直接在主连接脚本中包含一个版本脚本,或者你可以以一个隐式连接脚本的形式提供这个版本脚本。你也可以使用‘-- version-script'连接器选项。 ‘VERSION’命令的语法很简单: VERSION { version-script-commands } 版本脚本命令的格式跟Sun在Solaris 2.5中的连接器的格式是完全一样的。版本脚本定义一个版本节点树。你可以在版本脚本中指定节点名和依赖关系。你可以指定哪些符号被绑定到哪些版本节点上,你还可以把一组指定的符号限定到本地范围,这样在共享库的外面它们就不是全局可见的了。最简单的演示版本脚本语言的方法是出示几个小例子: VERS_1.1 { global: foo1; local: old*; original*; new*; }; VERS_1.2 { foo2; } VERS_1.1; VERS_2.0 { bar1; bar2; } VERS_1.2; 这个示例版本脚本定义了三个版本节点。第一个版本节点定义为‘VERS_1.1’它没有其它的依赖。脚本把符号‘foo1’绑定给‘VERS_1.1’。它把一些数量的符号限定到本地范围,这样它们在共享库的外面就不可见了;这是通过通配符来完成的,所以任何名字以‘old’,‘original’或 ‘new’开头的符号都会被匹配。可用的通配符跟在shell中匹配文件名时一样。下面,版本脚本定义一个节点‘VER_1.2’。这个节点依赖 ‘VER_1.1’。脚本把符号‘foo2’绑定给节点‘VERS_1.2’。最后,版本脚本定义节点‘VERS_2.0’。这个节点依赖 ‘VERS_1.2’。脚本把符号‘bar1’和 ‘bar2 ’绑定给版本节点‘VERS_2.0’。当连接器发现一个定义在库中的符号没有被指定绑定到一个版本节点,它会把它绑定到一个未指定基础版本的库。你可以通过使用‘global: *;’把所有未指定的符号绑定到一个给定的版本节点上。版本节点的名字没有任何特殊的含义只是为了方便人们阅读。版本‘2.0’可以出现在‘1.1’和 ‘1.2’之间。但是,在书写版本脚本时,这会是一个引起混乱的办法。如果在版本脚本中,这是一个唯一的版本节点,节点名可以被省略。这样的版本脚本不给符号赋任何版本,只是选择哪些符号会被全局可见而哪些不会。 { global: foo; bar; local: *; }; 当你把一个程序跟一个带有版本符号的共享库连接时,程序自身知道每个符号的哪个版本是它需要的,而且它还知道它连接的每一个节享库中哪些版本的节点是它需要的。这样,在运行时,动态载入程序可以做一个快速的确认,以保证你连接的库确实提供了所有的程序需要用来解析所有动态符号的版本节点。用这种方法,就有可能让每一个动态连接器知道所有的外部符号不需要通过搜索每一个符号引用就能解析。符号版本在SunOS上做次版本确认是一种很成熟的方法。一个被提出来的基本的问题是对于外部函数的标准引用会在需要时被绑定到正确的版本,但不是在程序启动的时候全部被绑定。如果一个共享库过期了,一个需要的界面可能就不存在了;当程序需要使用这个界面的时候,它可能会突然地意外失败。有了符号版本后,当用户启动他们的程序时,如果要使用的共享库太老了的话,用户会得到一条警告信息。 GNU对Sun的版本确认办法有一些扩展。首先就是能在符号定义的源文件中把一个符号绑定到一个版本节点而不是在一个版本脚本中。这主要是为了减轻库维护的工作量。你可以通过类似下面的代码实现这一点: __asm__(".symver original_foo,foo@VERS_1.1"); 在C源文件中。这句会给函数'original_foo'取一个别名'foo',并绑定到版本节点`VERS_1.1'。操作符'local:'可以被用来阻止符号'original_foo'被导出。操作符'.symver'使这句优先于版本脚本。第二个GNU的扩展是在一个给定的共享库中允许同一个函数的多个版本。通过这种办法,你可以不增加共享库的主版本号而对界面做完全不相容的修改。要实现这个,你必须在一个源文件中多次使用'.symver'操作符。这里是一个例子: __asm__(".symver original_foo,foo@"); __asm__(".symver old_foo,foo@VERS_1.1"); __asm__(".symver old_foo1,foo@VERS_1.2"); __asm__(".symver new_foo,foo@@VERS_2.0"); 在这个例子中,'foo@'表示把符号'foo'绑定到一个没有指基版本的符号上。含有这个例子的源文件必须定义4个C函数:`original_foo', `old_foo', `old_foo1', 和`new_foo'. 当你有一个给定符号的多个定义后,有必要有一个方法可以指定一个缺省的版本,对于这个符号的外部引用就可以找到这个版本。用这种方法,你可以只声明一个符号的一个版本作为缺省版本,否则,你会拥有同一个符号的多个定义。如果你想要绑定一个引用到共享库中的符号的一个指定的版本,你可以很方便地使用别名(比如, old_foo),或者你可以使用'.symver'操作符来指定绑定到一个外部函数的特定版本。你也可以在版本脚本中指定语言。 VERSION extern "lang" { version-script-commands } 被支持的'lang'有‘C’,‘C++’和‘Java’。连接脚本中的表达式 ============================= 连接脚本语言中的表达式的语法跟C的表达式是完全是致的。所有的表达式都以整型值被求值。所有的表达式也被以相同的宽度求值。在32位系统是它是32位,否则是64位。你可以在表达式中使用和设置符号值。连接器为了使用表达式,定义了几个具有特殊途的内建函数。常数 --------- 所有的常数都是整型值。就像在C中,连接器把以'0'开头的整型数视为八进制数,把以'0x'或'0X'开头的视为十六进制。连接器把其它的整型数视为十进制。另外,你可以使用'K'和'M'后缀作为常数的度量单位,分别为'1024'和'1024*1024'。比如,下面的三个常数表示同一个值。 _fourk_1 = 4K; _fourk_2 = 4096; _fourk_3 = 0x1000; 符号名 ------------ 除了引用,符号名都是以一个字母,下划线或者句号开始,可以包含字母,数字,下划线,句点和连接号。不是被引用的符号名必须不和任何关键字冲突。你可以指定一个含有不固定它符数或具有跟关键字相同名字但符号名必须在双引号内: "SECTION" = 9; "with a space" = "also with a space" + 10; 因为符号可以含有很多非文字字符,所以以空格分隔符号是很安全的。比如,'A-B'是一个符号,而'A - B' 是一个执行减法运算的表达式。定位计数器 -------------------- 一个特殊的连接器变量"dot"'.'总是含有当前的输出定位计数器。因为'.'总引用输出段中的一个位置,它只可以出现在'SECTIONS'命令中的表达式中。'.'符号可以出现在表达式中一个普能符号允许出现的任何位置。把一个值赋给'.'会让定位计数器产生移动。这会在输出段中产生空洞。定位计数器从不向前移动。 SECTIONS { output : { file1(.text) . = . + 1000; file2(.text) . += 1000; file3(.text) } = 0x12345678; } 在前面的例子中,来自'file1'的'.text'节被定位在输出节'output'的起始位置。它后面跟有1000byte的空隙。然后是来自 'file2'的'.text'节,同样是后面跟有1000byte的空隙,最后是来自'file3'的'.text' 节。符号'=0x12345678'指定在空隙中填入什么样的数据。注意:'.'实际上引用的是当前包含目标的从开始处的字节偏移。通常,它就是 'SECTIONS'语句,其起始地址是0,因为'.'可以被用作绝对地址。但是如果'.'被用在一个节描述中,它引用的是从这个节起始处开始的偏移,而不是一个绝对地址。这样,在下面这样一个脚本中: SECTIONS { . = 0x100 .text: { *(.text) . = 0x200 } . = 0x500 .data: { *(.data) . += 0x600 } } '.text'节被赋于起始地址0x100,尽管在'.text'输入节中没有足够的数据来填充这个区域,但其长度还是0x200bytes。(如果数据太多,那会产生一条错误信息,因为这会试图把'.'向前移)。'.data' 节会从0x500处开始,并且它在结尾处还会有0x600的额外空间。运算符 --------- 连接器可以识别标准的C的算术运算符集, 以及它们的优先集. 优先集 结合性 运算符 备注 (highest) 1 left ! - ~ (1) 2 left * / % 3 left + - 4 left >> << 5 left == != > < <= >= 6 left & 7 left | 8 left && 9 left || 10 right ? : 11 right &= += -= *= /= (2) (lowest) 注: (1) 前缀运算符 (2) *Note Assignments::. 求值 ---------- 连接器是懒惰求表达式的值。它只在确实需要的时候去求一个表达式的值。连接器需要一些信息,比如第一个节的起始地址的值,还有内存区域的起点与长度,在做任何连接的时候这都需要。在连接器读取连接脚本的时候,这些值在可能的时候被计算出来。但是,其它的值(比如符号的值)直到内存被分配之后才会知道或需要。这样的值直到其它信息(比如输出节的长度)可以被用来进行符号赋值的时候才被计算出来。直到内存分配之后,节的长度才会被知道,所以依赖于节长度的赋值只能到内存分配之后才会被执行。有些表达式,比如那些依赖于定位计数器'.'的表达式,必须在节分配的过程中被计算出来。如果一个表达式的结果现在被需要,但是目前得不到这个值,这样会导致一个错误。比如,象下面这样一个脚本: SECTIONS { .text 9+this_isnt_constant : { *(.text) } } 会产生一个错误信息'non constant expression for initial address'. 表达式的节 ---------------------------- 当一个连接器计算一个表达式时,得到的结果可能是一个绝对值,也可能跟某个节相关。一个节相关的表达式是从一个节的基地址开始的固定的偏称值。表达式在连接脚本中的位置决定了它是绝对的或节相关的。一个出现在输出节定义中的表达式是跟输出节的基地址相关的。一个出现在其它地方的表达式则是绝对的。如果你通过'-r'选项指定需要可重位输出,那一个被赋为节相关的表达式的符号就会是可重定位的。意思是下一步的连接操作会改变这个符号的值。符号的节就是节相关的表达式所在的节。一个被赋为绝对表达式的符号在后面进一步的连接操作中会始终保持它的值不变。符号会是绝对的,并不会有任何的特定的相关节。如果一个表达式有可能会是节相关的,你可以使用内建函数'ABSOLUTE'强制一个表达式为绝对的。比如,要创建一个被赋为输出节'.data'的末尾地址的绝对符号: SECTIONS { .data : { *(.data) _edata = ABSOLUTE(.); } } 如果没有使用'ABSOLUTE','_edata'会跟节'.data'相关。内建函数 ----------------- 为了使用连接脚本表达式,连接脚本语言含有一些内建函数。 `ABSOLUTE(EXP)' 返回表达式EXP的绝对值(不可重定位,而不是非负)。主要在把一个绝对值赋给一个节定义内的符号时有用。 `ADDR(SECTION)' 返回节SECTION的绝对地址(VMA)。你的脚本之前必须已经定义了这个节的地址。在接下来的例子中,'symbol_1'和'symbol_2'被赋以相同的值。 SECTIONS { ... .output1 : { start_of_output_1 = ABSOLUTE(.); ... } .output : { symbol_1 = ADDR(.output1); symbol_2 = start_of_output_1; } ... } `ALIGN(EXP)' 返回定位计数器'.'对齐到下一个EXP指定的边界后的值。‘ALIGN’不改变定位计数器的值,它只是在定位计数器上面作了一个算术运算。这里有一个例子,它在前面的节之后,把输出节'.data'对齐到下一个'0x2000'字节的边界,并在输入节之后把节内的一个变量对齐到下一个'0x8000'字节的边界。 SECTIONS { ... .data ALIGN(0x2000): { *(.data) variable = ALIGN(0x8000); } ... } 这个例子中前一个'ALIGN'指定一个节的位置,因为它是作为节定义的可选项ADDRESS属性出现的。第二个‘ALIGN’被用来定义一个符号的值。内建函数'NEXT'跟‘ALIGN’非常相似。 `BLOCK(EXP)' 这是'ALIGN'的同义词,是为了与其它的连接器保持兼容。这在设置输出节的地址时非常有用。 `DATA_SEGMENT_ALIGN(MAXPAGESIZE, COMMONPAGESIZE)' 这跟下面的两个表达同义: (ALIGN(MAXPAGESIZE) + (. & (MAXPAGESIZE - 1))) 或者: (ALIGN(MAXPAGESIZE) + (. & (MAXPAGESIZE - COMMONPAGESIZE))) 隐式连接脚本 ======================= 如果你指定了一个连接器输出文件,而连接器不能识别它是一个目标文件还是档案文件,它会试图把它读作一个连接脚本。如果这个文件不能作为一个连接脚本被分析,连接器就会报告一个错误。一个隐式的连接器脚本不会替代缺省的连接器脚本。一般,一个隐式的连接器脚本只包含符号赋值,或者 'INPUT','GROUP'或'VERSION'命令。 BFD *** 连接器通过BFD库来对目标文件和档案文件进行操作。这些库允许连接器忽略目标文件的格式而使用相关的例程来操作目标文件。只要简单地创建一个新的BFD 后台并把它加到库中,一个不同的目标文件格式就会被支持。但是为了节约运行时内存,连接器和相关的工具一般被配置为只支持可用的目标文件格式的一个子集,你可以使用'objdump -i'来列出你配置的所有支持的格式。就像大多数的案例,BFD是一个在多种相互有冲突的需求之间的一个折中,影响BFD设计的一个最主要的因素是效率。因为BFD简化了程序和后台,更多的时间和精力被放在了优化算法以追求更快的速度。 BFD解决方案的一个副产品是你必须记住有信息丢失的潜在可能。在使用BFD机制时,有两处地方有用信息可能丢失:在转化时和在输出时。它如何工作: BFD概要。 =============================== 当一个目标文件被打开时,BFD子程序自动确定输入目标文件的格式。然后它们在内存中用指向子程序的指针构建一个描述符,这个描述符被用作存取目标文件的数据结构元素。因为需要来自目标文件的不同信息,BFD从文件的不同节中读取它们,并处理。比如,连接器的一个非常普遍的操作是处理符号表。每一个BFD 后台提供一个在目标文件的符号表达形式跟内部规范格式之间的转化的函数,当一个连接器需要一个目标文件的符号表时,它通过一个内存指针调用一个来自相应的 BFD后台的子程序,这个子程序读取表并把它转化为规范表。然后,连接器写输出文件的符号表,另一个BFD后台子程序被调用,以创建新的符号表并把它转化为选定的输出格式。信息丢失。 ---------------- 在输出的过程中,信息可能会被丢失。BFD支持的输出格式并不提供一致的特性,并且在某一种格式中可以被描述的信息可能在另一种格式中没有地方可放。一个例子是在'b.out'中的对齐信息,在一个'a.out'格式的文件中,没有地方可以存储对齐信息,所以当一个文件是从'b.out'连接而成的,并产生的是一个'a.out'的文件,对齐信息就不会被传入到输出文件中(连接器还是在内部使用对齐信息,所以连接器的执行还是正确的)另一个例子是COFF 节名字。COFF文件中可以含有不限数量的节,每一个都有一个文字的节名。如果连接的目标是一种不支持过多节的格式(比如,'a.out')或者是一种不含有节名的格式(比如,Oasys格式),连接器不能像通常那样简单地处理它。你可以通过把所需的输入输出节通过连接脚本语言进行详细映射来解决这下问题。在规范化的过程中信息也会丢失。BFD内部的对应于外部格式的规范形式并不是完全详尽的;有些在输入格式中的结构在内部并没有对应的表示方法。这意味着BFD后台在从外部到内部或从内部到外部的转化过程中不能维护所有可能的数据。这个限制只在一个程序读取一种格式并写成另一种格式的时候会是一个问题。每一个BFD后台有责任维护尽可能多的数据,内部的BFD规范格式具有对BFD内核不透明的结构体,只导出给后台。当一个文件以一种格式读取后,规范格式就会为之产生。同时,后台把所有可能丢失的信息进行存储。如果这些数据随后会写以相同的格式写回,后台程序就可以使用BFD内核提供的跟选前准备的相同的规范格式。因为在后台之间有大量相同的东西,在把big endianCOFF拷贝成littile endian COFF时,或者'a.out'到'b.out'时,不会有信息丢失。当一些混合格式被连接到一起时,只有那些格式跟目标格式不同的文件会丢失信息。