输出节描述和重叠描述在下面描述.
如果你在连接脚本中不使用'SECTIONS'命令, 连接器会按在输入文件中遇到的节的顺序把每一个输入节放到同名的输出节中. 如果所有的输入节都在第一个文件中存在,那输出文件中的节的顺序会匹配第一个输入文件中的节的顺序. 第一个节会在地址零处. 输出节描述
--------------------------
一个完整的输出节的描述应该是这个样子的: SECTION [ADDRESS] [(TYPE)] : [AT(LMA)] {
OUTPUT-SECTION-COMMAND OUTPUT-SECTION-COMMAND ...
} [>REGION] [AT>LMA_REGION] [:PHDR :PHDR ...] [=FILLEXP]
大多数输出节不使用这里的可选节属性.
SECTION边上的空格是必须的, 所以节名是明确的. 冒号跟花括号也是必须的. 断行和其他的空格是可选的.
每一个OUTPUT-SECTION-COMMAND可能是如下的情况: * 一个符号赋值. * 一个输入节描述. * 直接包含的数据值.
* 一个特定的输出节关键字.
输出节名.-------------------
输出节的名字是SECTION. SECTION必须满足你的输出格式的约束. 在一个只支持限制数量的节的格式中,比如'a.out',这个名字必须是格式支持的节名中的一个(比如, 'a.out'只允许'.text', '.data'或'.bss').如果输出格式支持任意数量的节, 但是只支持数字,而没有名字(就像Oasys中的情况), 名字应当以一个双引号中的数值串的形式提供.一个节名可以由任意数量的字符组成,但是一个含有任意非常用字符(比如逗号)的字句必须用双引号引起来.
输出节描述
--------------------------
ADDRESS是关于输出节中VMS的一个表达式. 如果你不提供ADDRESS, 连接器会基于REGION(如果存在)设置它,或者基于定位计数器的当前值.如果你提供了ADDRESS, 那输出节的地址会被精确地设为这个值. 如果你既不提供ADDRESS也不提供REGION, 那输出节的地址会被设为当前的定位计数器向上对齐到输出节 需要的对齐边界的值. 输出节的对齐要求是所有输入节中含有的对齐要求中最严格的一个. 比如:
.text . : { *(.text) } 和
.text : { *(.text) }
有细微的不同. 第一个会把'.text'输出节的地址设为当前定位计数器的值. 第二个会把它设为定位计数器的当前值向上对齐到'.text'输入节中对齐要求最严格的一个边界.
ADDRESS可以是任意表达式; 比如,如果你需要把节对齐对0x10字节边界,这样就可以让低四字节的节地址值为零, 你可以这样做: .text ALIGN(0x10) : { *(.text) }
这个语句可以正常工作,因为'ALIGN'返回当前的定位计数器,并向上对齐到指定的值.
指定一个节的地址会改变定位计数器的值.
输入节描述
-------------------------
最常用的输出节命令是输入节描述.
输入节描述是最基本的连接脚本操作. 你使用输出节来告诉连接器在内存中如何布局你的程序. 你使用输入节来告诉连接器如何把输入文件映射到你的内存中.
输入节基础
---------------------------
一个输入节描述由一个文件名后跟有可选的括号中的节名列表组成. 文件名和节名可以通配符形式出现, 这个我们以后再介绍.
最常用的输入节描述是包含在输出节中的所有具有特定名字的输入节. 比如, 包含所有输入'.text'节,你可以这样写: *(.text)
这里,'*'是一个通配符,匹配所有的文件名. 为把一部分文件排除在匹配的名字通配符之外, EXCLUDE_FILE可以用来匹配所有的除了在EXCLUDE_FILE列表中指定的文件.比如:
(*(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors))
会让除了`crtend.o'文件和`otherfile.o'文件之外的所有的文件中的所有的.ctors节被包含进来.
有两种方法包含多于一个的节: *(.text .rdata) *(.text) *(.rdata)
上面两句的区别在于'.text'和'.rdata'输入节的输出节中出现的顺序不同. 在第一个例子中, 两种节会交替出现,并以连接器的输入顺序排布. 在第二个例子中,所有的'.text'输入节会先出现,然后是所有的'.rdata'节.
你可以指定文件名,以从一个特定的文件中包含节. 如果一个或多个你的文件含有特殊的数据在内存中需要特殊的定位,你可以这样做. 比如: data.o(.data)
如果你使用一个不带有节列表的文件名, 那输入文件中的所有的节会被包含到输出节中.通常不会这样做, 但是在某些场合下这个可能非常有用. 比如: data.o
当你使用一个不含有任何通配符的文件名时, 连接器首先会查看你是否在连接命令行上指定了文件名或者在'INPUT'命令中. 如果你没有, 连接器会试图把这个文件作为一个输入文件打开, 就像它在命令行上出现一样.注意这跟'INPUT'命令不一样, 因为连接器会在档案搜索路径中搜索文件.
输入节通配符
---------------------------------
在一个输入节描述中, 文件名或者节名,或者两者同时都可以是通配符形式.文件名通配符'*'在很多例子中都可以看到,这是一个简单的文件名通配符形式.通配符形式跟Unix Shell中使用的一样. `*'
匹配任意数量的字符. `?'
匹配单个字符. `[CHARS]'
匹配CHARS中的任意单个字符; 字符'-'可以被用来指定字符的方讧, 比如[a-z]匹配任意小字字符. `\\'
转义其后的字符.
当一个文件名跟一个通配符匹配时, 通配符字符不会匹配一个'/'字符(在UNIX系统中用来分隔目录名), 一个含有单个'*'字符的形式是个例外; 它总是匹配任意文件名, 不管它是否含有'/'. 在一个节名中, 通配符字符会匹配'/'字符. 文件名通配符只匹配那些在命令行或在'INPUT'命令上显式指定的文件. 连接器不会通过搜索目录来展开通配符.
如果一个文件名匹配多于一个通配符, 或者如果一个文件名显式出现同时又匹配了一个通配符, 连接器会使用第一次匹配到的连接脚本. 比如, 下面的输入节描述序列很可能就是错误的,因为'data.o'规则没有被使用: .data : { *(.data) }
.data1 : { data.o(.data) }
通常, 连接器会把匹配通配符的文件和节按在连接中被看到的顺序放置. 你可以通过'SORT'关键字改变它, 它出现在括号中的通配符之前(比如,
SORT(.text*)'). 当'SORT'关键字被使用时, 连接器会在把文件和节放到输出文件中之前按名字顺序重新排列它们.
如果你对于输入节被放置到哪里去了感到很困惑, 那可以使用'-M'连接选项来产生一个位图文件. 位图文件会精确显示输入节是如何被映射到输出节中的. 这个例子显示了通配符是如何被用来区分文件的. 这个连接脚本指示连接器把所有的'.text'节放到'.text'中, 把所有的'.bss'节放到'.bss'. 连接器会把所有的来自文件名以一个大写字母开始的文件中的'.data'节放进'.DATA'节中; 对于所有其他文件, 连接器会把'.data'节放进'.data'节中. SECTIONS {
.text : { *(.text) }
.DATA : { [A-Z]*(.data) } .data : { *(.data) }
.bss : { *(.bss) } }
输入节中的普通符号.
-----------------------------------
对于普通符号,需要一个特殊的标识, 因为在很多目标格式中, 普通符号没有一个特定的输入节. 连接器会把普通符号处理成好像它们在一个叫做'COMMON'的节中.
你可能像使用带有其他输入节的文件名一样使用带有'COMMON'节的文件名。你可以通过这个把来自一个特定输入文件的普通符号放入一个节中,同时把来自其它输入文件的普通符号放入另一个节中。 在大多数情况下,输入文件中的普通符号会被放到输出文件的'.bss'节中。比如: .bss { *(.bss) *(COMMON) }
有些目标文件格式具有多于一个的普通符号。比如,MIPS ELF目标文件格式区分标准普通符号和小普通符号。在这种情况下,连接器会为其他类型的普通符号使用一个不同的特殊节名。 在MIPS ELF的情况中, 连接器为标准普通符号使用'COMMON',并且为小普通符号使用'.common'。这就允许你把不同类型的普通符号映射到内存的不同位置。
在一些老的连接脚本上,你有时会看到'[COMMON]'。这个符号现在已经过时了, 它等效于'*(COMMON)'。
标 题: GLD中文手册--(七)
输入节和垃圾收集
--------------------------------------- 当连接时垃圾收集正在使用中时('--gc-sections'),这在标识那些不应该被排除在外的节时非常有用。这是通过在输入节的通配符入口外面加上'KEEP()'实现的,比如'KEEP(*(.init))'或者'KEEP(SORT(*)(.sorts)) '。
输入节示例
---------------------
接下来的例子是一个完整的连接脚本。它告诉连接器去读取文件'all.o'中的所有节,并把它们放到输出节'outputa'的开始位置处, 该输出节是从位置'0x10000'处开始的。
从文件'foo.o'中来的所有节'.input1'在同一个输出节中紧密排列。 从文件'foo.o'中来的所有节'.input2'全部放入到输出节'outputb'中,后面跟上从'foo1.o'中来的节'.input1'。来自所有文件的所有余下的'.input1'和'.input2'节被写入到输出节'outputc'中。 SECTIONS {
outputa 0x10000 : {
all.o
foo.o (.input1) }
outputb : {
foo.o (.input2) foo1.o (.input1) }
outputc : {
*(.input1) *(.input2) } }
输出节数据
-------------------
你可以通过使用输出节命令'BYTE','SHORT','LONG','QUAD',或者'SQUAD'在输出节中显式包含几个字节的数据每一个关键字后面都跟上一个圆括号中的要存入的值。表达式的值被存在当前的定位计数器的值处。
‘BYTE’,‘SHORT’,‘LONG’‘QUAD’命令分别存储一个,两个,四个,八个字节。存入字节后,定位计数器的值加上被存入的字节数。 比如,下面的命令会存入一字节的内容1,后面跟上四字节,其内容是符号'addr'的值。
BYTE(1) LONG(addr)
当使用64位系统时,‘QUAD’和‘SQUAD’是相同的;它们都会存储8字节,或者说是64位的值。而如果软硬件系统都是32位的,一个表达式就会被作为32位计算。在这种情况下,‘QUAD’存储一个32位值,并把它零扩展到64位, 而‘SQUAD’会把32位值符号扩展到64位。
如果输出文件的目标文件格式有一个显式的endianness,它在正常的情况下,值就会被以这种endianness存储当一个目标文件格式没有一个显式的endianness时, 值就会被以第一个输入目标文件的endianness存储。
注意,这些命令只在一个节描述内部才有效,而不是在它们之间, 所以,下面的代码会使连接器产生一个错误信息:
SECTIONS { .text : { *(.text) } LONG(1) .data : { *(.data) } } 而这个才是有效的:
SECTIONS { .text : { *(.text) ; LONG(1) } .data : { *(.data) } } 你可能使用‘FILL’命令来为当前节设置填充样式。它后面跟有一个括号中的表达式。任何未指定的节内内存区域(比如,因为输入节的对齐要求而造成的裂缝)会以这个表达式的值进行填充。一个'FILL'语句会覆盖到它本身在节定义中出现的位置后面的所有内存区域;通过引入多个‘FILL’语句,你可以在输出节的不同位置拥有不同的填充样式。
这个例子显示如何在未被指定的内存区域填充'0x90': FILL(0x90909090)
‘FILL’命令跟输出节的‘=FILLEXP’属性相似,但它只影响到节内跟在
‘FILL’命令后面的部分,而不是整个节。如果两个都用到了,那‘FILL’命令优先。