每一个链接都是链接脚本(linker script)控制的,脚本是按照链接器命令语言(linker command language)书写的。
链接脚本的主要目的是描述输入文件的section如何映射到输出文件,以及如何控制输出文件的内存布局。除此之外,大多数链接脚本不做什么其它的事。然而如果需要,链接脚本也可以指导链接器执行其它许多操作。
链接器总是使用链接脚本,如果没有提供,它使用缺省脚本,使用--verbose命令行选项,可以显示缺省的链接脚本。一些命令如-r或者-N,会 影响缺省链接脚本。
可以使用-T选项指定自己的链接脚本,这么做时,你的脚本完全替换缺省链接脚本。
基本的链接脚本概念:
section:可参考本博客ELF分类中的文章。
input section:输入文件中的section。
output section:输出文件中的section。
loadable:当输出文件运行时,该section的内容加载内存中。
allocatable:该section没有内容,但是会分配内存,通常是.bss section。
每个可加载和可分配section有两个地址,第一个是VMA(virtual memory address),这是输出文件运行时,section的地址。第二个是LMA(load memory address),这是section要加载到的地址。大多数情况下,这两个地址相同。但是在一些嵌入式系统中,整个目标文件烧写到nor flash(指令可直接执行),只读段仍然在ROM中,但是变量加载到RAM中,此时VMA和LMA不同。
链接脚本格式:
链接脚本是文本文件,链接脚本由一系列命令组成。每一个命令或者是一个关键字,关键字可以带参数;或者是给符号赋值。可以用分号分开各个命令。注释的格式为 /* */。文件名和格式名可直接输入,如果有特殊字符,使用双引号。
简单的链接脚本例子:
最简单的链接脚本只有一个命令:"SECTIONS",这个命令用来描述输出文件的内存布局。
1 2 3 4 5 6 7 8 | SECTIONS { . = 0x10000; .text : { *(.text) } . = 0x8000000; .data : { *(.data) } .bss : { *(.bss) } } |
.符号是location counter,用于指定section的地址,SECTIONS命令开始时,其值为0,可以显式设置,如果没有设置,则按照section大小自动增长。.text定义一个ouput section。后面跟一个冒号,现在可以省略。大括号用来指定input sections,*是通配符,匹配任何文件名。表达式'*(.text)'表示输入文件的所有.text section。链接器保证output section满足对齐要求。
程序执行的第一条指令叫做entry point。ENTRY命令用于设置入口点。链接器按照下面的顺序寻找入口点。
-e 命令行选项。
ENTRY(symbol)链接脚本命令。
目标指定的符号,通常是start。
.text的第一个字节。
0
INCLUDE,用来包含其它链接脚本。INCLUDE filename
INPUT,用来包含要链接的目标文件,INPUT(file, file, ...) 或者 INPUT(file file ...)。file可以为-lfile格式。
OUTPUT,用来指定输出文件,OUTPUT(file)
SEARCH_DIR,用来指定库搜索路径,同命令行-L。SEARCH_DIR(path)
STARTUP,STARTUP(file),指定第一个链接的文件。
REGION_ALIAS命令,用来给内存区指定一个别名,如 REGION_ALIAS("REGION_BSS", RAM)。通常用来将section放入一个指定的位置。
可以使用任何c语言赋值:
1 2 3 4 5 6 7 8 9 | symbol = expression ; symbol += expression ; symbol -= expression ; symbol *= expression ; symbol /= expression ; symbol <<= expression ; symbol >>= expression ; symbol &= expression ; symbol |= expression ; |
第一种定义符号的值。其它的情况,符号必须已有定义。特殊的符号.表示location counter,只能使用在SECTIONS命令中。表达式后面的分号是要求的。表达式的的定义参考后续小节。
HIDDEN命令
可以隐藏符号,使其在模块之外不可见,使用HIDDEN(symbol=expression)。
PROVIDE/PROVIDE_HIDDEN命令
一些情况下,链接器可以定义一个目标文件没定义的符号,目标文件引用该符号。可以用PROVIDE(symbol=expression)。如果目标文件有定义,则会报错,此方法可以将一些变量定义放在链接阶段,在某些情况下非常有用。PROVIDE会把变量放在PROVIDE所在的段的位置。PROVIDE_HIDDEN和PROVIDE相似,但是隐藏起来,不导出。
高级语言引用
访问链接脚本定义的变量可能和直觉不同,首先上层语言的变量名和链接阶段的变量名可能不同,为了简单,这里考虑相同的情况。当上层语言定义一个变量时,两件事情发生:在程序内存中保留一些空间存储符号的值;在符号表创建一个条目存储符号的地址。当程序引用一个符号时,编译器产生访问符号表的代码,找到符号的内存地址,然后从内存地址读取数据。而
int *a = & foo;
在符号表寻找符号foo,得到他的地址,拷贝到变量a的内存块。然而链接脚本符号声明,只是在符号表创建一个条目,不分配任何的内存。因此他们是只是地址。例如:
foo = 1000;
在符号表创建一个叫foo的条目,地址为1000。但实际上地址1000没有任何东西,这意味着不能访问链接器脚本定义的符号的值。因此,当在源代码中使用链接器定义的符号时,应当总是使用符号的地址,而不要去尝试用它的值。例如,下面的例子将ROM section的内容拷贝到FLASH section。链接脚本包含:
1 2 3 | start_of_ROM = .ROM; end_of_ROM = .ROM + sizeof (.ROM) - 1; start_of_FLASH = .FLASH; |
c源代码执行拷贝操作:
1 2 | extern char start_of_ROM, end_of_ROM, start_of_FLASH; memcpy(& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM); |
SECTIONS命令告诉链接器,如何映射输入section到输出section,如何将输出section放到内存。SECTIONS命令的格式:
1 2 3 4 5 6 | SECTIONS { sections-command sections-command ... } |
每个sections-command可能是下列之一:
一个ENTRY命令
一个符号赋值
一个输出section描述
一个overlay描述
ENTRY命令和符号赋值允许出现在SECTIONS命令中,是为了在这些命令中方便的使用location counter。这也使链接脚本更容易理解,因为你将这些命令放在输出文件布局中有意义的位置。
如果在链接脚本中,没有使用SECTIONS命令。链接器将每一个输入section放入一个唯一命名的输出section。按照其在第一个输入文件的顺序。当然也会合并输入section。第一个section的地址将为0。
输出section描述
完整的格式:
1 2 3 4 5 6 7 8 9 10 | section [address] [(type)] : [AT(lma)] [ALIGN(section_align) | ALIGN_WITH_INPUT] [SUBALIGN(subsection_align)] [constraint] { output-section-command output-section-command ... } [>region] [AT>lma_region] [:phdr :phdr ...] [=fillexp] |
大多数输出section不使用大多数section属性。section周围的空白是必须的。冒号和大括号也是必须的。换行和其它空白是可选的。每个output-section-command是下列之一:
符号赋值
input section description
data values to include directly
a special output section keyword
Output section name
上述Ouput section description格式中的section,就是section的名字。
Output section addresses
上述Ouput section description格式中的addresses。是输出section的VMA的表达式。这个地址是可选的,如果提供,那么将严格设置为指定的地址。如果没有指定地址,地址被调整为满足输出section的对齐要求,按照下面的方法:
- 如果为section设置了输出内存区,那么它的地址是region的下一个空闲地址。
- 如果用MEMORY命令创建一系列内存区,那么第一个属性匹配的内存区被选择存放section。
- 如果没有内存区,则选择location counter。
注意,下述是不同的。
1 2 | .text . : { *(.text) } .text : { *(.text) } |
前者完全是.的值,后者以.为参考,但是满足对齐要求。地址可以为任何表达式。
Input section description
大多数output-section-command是一个input section description。input section description是最基本的链接脚本操作。你使用output sections告诉链接器如何在内存中布局你的程序。使用input section descriptions告诉链接器如何映射输入文件到内存布局。输入section描述符有一个文件名,跟着一个section名的列表,列表用括号括起来,如
1 | a.o(.text .rodata) |
最常用的输入section描述符是使用一个特殊的名字来包含所有的输入section。例如,包含所有的输入的.text section。
1 | *(.text) |
通配符*表示所有的输入文件。如果要排除一些文件可以使用EXCLUDE_FILE,如下:
1 | *(EXCLUDE_FILE (*ctrend.o *otherfile.o) .ctors) |
有两种方法,包含多于一种section。
1 2 | *(.text .rdata) *(.text) *(.rdata) |
不同之处是顺序,前者按文件,后者按section类型。为了根据section的flag包含,可以使用INPUT_SECTION_FLAGS。如:
1 2 3 4 | SECTIONS { .text : { INPUT_SECTION_FLAGS (SHF_MERGE & SHF_STRINGS) *(.text) } .text2 : { INPUT_SECTION_FLAGS (!SHF_WRITE) *(.text) } } |
也可以指定静态库中的文件。如archive:file,匹配库中的文件。archive是静态库的名字,file是库中文件的名字。archive,匹配整个库。:file,匹配所有非静态库中的文件。
通配符
* 匹配任何数量的字符。
? 匹配任何单个字符。
[chars] 匹配指定的字符。如[a-z]
\ 转义接下来的字符。
匹配文件名时,通配符不匹配/。但是单一的*是个例外。
当出现多次匹配时,选择第一次匹配。
排序
相关关键字有SORT_BY_NAME,SORT_BY_ALIGMENT,SORT_BY_INIT_PRIORITY。排序可以嵌套1层。
SORT_NONE可以取消命令行的--sort-sections选项。
Input Section for Common Symbols
通用符号通常没有一个特别的输入section。链接器对待common symbols好像他们都在一个名叫COMMON的section中。
大多数情况下,通用符号放在.bss section后,如下:
1 | .bss { *(.bss) *(COMMON) } |
input section and garbage collection
garbage collection就是删除不用的section,不输出到输出文件中,使用--gc-sections选项设置。但是可以使用KEEP来保留。如下:
1 | KEEP(*(.init)) |
Output section data
可以使用BYTE, SHORT, LONG,QUAD或者SQUAD来在输出section中包含数据。每个关键字跟着一个括号括起来的表达来表示存储的值。存储的位置是当前location counter。如:
BYTE(1)
在64位机器上,QUAD和SQUAD是相同的,都是存储64位数据。在32位机器上,表达式的值是计算为32位,QUAD按0扩展64位。SQUAD按位扩展64位。如果输出文件有大小端,值将按其大小端存储。
注意:只能放在section描述中,而不是能section之间,如下将产生一个链接错误。
1 | SECTIONS {.text:{*(.text)} LONG(1) .data : { *(.data)}} |
下面这个是正面的:
1 | SECTIONS {.text:{*(.text) LONG(1)} .data : { *(.data)}} |
同时可以使用FILL命令来填充当前section。同样跟着一个括号表达式表示填充的值。section中所有未定义的区域被填充。如:
FILL(0x90909090)
FILL命令等同于输出section的=fillexp属性。但是其只影响FILL之后的区域。
Output section keywords
CREATE_OBJECT_SYMBOLS
CONSTRUCTORS
在ELF格式中,没用到。
Output section discarding
链接器不会创建没有内容的输出section。引用不在任何输入文件中存在的sections时,这很方便。如:
1 | .foo : { *(.foo) } |
如果所有的输入文件中,都没有.foo这个section,那么输出文件也将没有。在丢弃的output section,链接器忽略地址赋值。特殊的输出section名'/DISCARD/'用来丢弃输入section。所有在这个输出section中的输入section都不会包含在输出文件中。
Output section type
每个输出section可以有一个类型,类型是用圆括号括起来的关键字。定义了下列类型:
NOLOAD,section被标记为不可加载类型,程序运行时不会载入到内存。
DSECT/COPY/INFO/OVERLAY,几乎不使用,向后兼容。被标记为不分配。程序运行时不分配内存。
Output section LMA
每个section有一个虚拟地址VMA和一个加载地址LMA。加载地址是通过AT或者AT>关键字指定的。AT关键字跟一个表达式做参数表示家在地址。AT>关键字跟一个memory region名做参数,section的加载地址设置为区的下一个空闲地址,并满足对齐要求。
如果没有指定AT或者AT>,按下面的方式决定加载地址。
如果section有一个指定的VMA地址,使用这个地址作为LMA地址。
如果section不是allocatable,设置为VMA。
如果兼容本section的memory region找到,其region包含至少一个section。LMA被设置,VMA的差值等于LMA的差值。
如果没有memory region,这使用default region。该region覆盖整个地址空间,按照3的方式设置。
最后,使用VMA。
LMA通常用来创建ROM镜像。
Forced output alignment
ALIGN/ALIGN_WITH_INPUT
Forced Input alignment
SUBALIGN
Output section constraint
输出section只有在输入section全部为只读或者读写时才创建。ONLY_IF_RO/ONLY_IF_RW.
Output section region
使用>region,把一个section赋给之前定义的内存区。如:
1 2 | MEMORY { rom : ORIGIN = 0x1000, LENGTH = 0x1000 } SECTIONS { ROM : { *(.text) } >rom } |
Output section phdr
可以把一个输出section赋给之前定义的程序段,使用:phdr。如果一个section被赋给一个或多个段,那么后续的section都将赋给那个段,除非显示的设置。
1 2 | PHDRS { text PT_LOAD ; } SECTIONS { .text : { *(.text) } :text } |
Output section fill
使用=fileexp的形式,指定填充内容。内存区中未定义的内容(如对齐导致的空白)都将被填充该内容,
Overlay description
overlay描述符用来实现不同的output section运行在相同的地址。OVERLAY命令用在SECTIONS命令中。语法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | OVERLAY [start] : [NOCROSSREFS] [AT ( ldaddr )] { secname1 { output-section-command output-section-command ... } [:phdr...] [=fill] secname2 { output-section-command output-section-command ... } [:phdr...] [=fill] ... } [>region] [:phdr...] [=fill] |
除了OVERLAY,其它都是可选的。section定义和普通的SECTIONS命令里的一样。除了没有地址和没有内存区外。所有的sections有相同的起始地址,但是加载地址是连贯的。如果NOCROSSREFS关键字使用,则检查sections之间是否有相互的符号引用。如果有,链接器将报错。链接器为每个section自动定义两个符号。__load_start_secname和__load_stop_secname。表示加载地址的开始和结束。下面是一个例子:
1 2 3 4 5 | OVERLAY 0x1000 : AT (0x4000) { .text0 { o1/*.o(.text) } .text1 { o2/*.o(.text) } } |
.text0和.text1的起始地址为0x100,.text0加载到0x4000。.text0紧跟着.text0。下述符号将被定义:__load_start_text0 __load_stop_text0 __load_start_text1 __load_stop_text1。可以使用c代码,拷贝.text1到重叠区:
1 2 3 | extern char __load_start_text1, __load_stop_text1; memcpy (( char *) 0x1000, &__load_start_text1, &__load_stop_text1 - &__load_start_text1); |
OVERLAY命令只是简化链接脚本,也可以用基本命令实现。如下:
1 2 3 4 5 6 7 | .text0 0x1000 : AT (0x4000) { o1/*.o(.text) } PROVIDE (__load_start_text0 = LOADADDR (.text0)); PROVIDE (__load_stop_text0 = LOADADDR (.text0) + SIZEOF (.text0)); .text1 0x1000 : AT (0x4000 + SIZEOF (.text0)) { o2/*.o(.text) } PROVIDE (__load_start_text1 = LOADADDR (.text1)); PROVIDE (__load_stop_text1 = LOADADDR (.text1) + SIZEOF (.text1)); . = 0x1000 + MAX (SIZEOF (.text0), SIZEOF (.text1)); |
链接器的缺省配置允许分配所有可用的内存,可以使用MEMORY命令改变。MEMORY命令描述了目标中内存块的位置和大小。可以用来告诉链接器哪个内存区可以用,哪个不可以。然后把section放入指定的内存区。链接器将根据内存区设置section的地址。并且当内存区满时,给出警告。一个链接脚本最多包含一个MEMORY命令,但是在命令里面可以包含许多内存块。
1 2 3 4 5 | MEMORY { name [(attr)] : ORIGIN = origin, LENGTH = len ... } |
名字用来指向region。命令在链接脚本外无意义,名字也和符号冲突。名字也可以有别名(见REGION_ALIAS)。attr字符串是可选的。用来实现满足特定属性的输入section放入该内存区。该字符串只能包含下列字符:
'R',只读section。
'W',读写section。
'X',可执行section。
A,Allocatable section
I, Initialized section
L, 和I相同。
!,非。
如果有任何未映射(unmapped)的section匹配上述属性,则放入该内存区。ORIGIN是数字表达式,表示内存区的开始地址。表达式必须是常量,不能有任何符号。可简写为org或o。LENGTH是内存区长度的表达式。也必须是常量。可简写为len或l。
下面为例子:
1 2 3 4 5 | MEMORY { rom (rx) : ORIGIN = 0, LENGTH = 256K ram (!rx) : org = 0x40000000, l = 4M } |
一旦定义了内存区,可以指导链接器将输出section放入内存区,通过使用>region属性。如果没有指定地址,则放入内存区下一个可用的地址。可以访问内存区的地址和长度,通过表打死ORIGIN(memory)和LENGTH(memory),如:
1 | _fstack = ORIGIN(ram) + LENGTH(ram) - 4; |
链接器缺省或创建合适的程序头,然而,你可能需要创建特定的程序头。这时可以使用PHDRS来实现。命令格式如下:
1 2 3 4 5 | PHDRS { name type [ FILEHDR ] [ PHDRS ] [ AT ( address ) ] [ FLAGS ( flags ) ] ; } |
PHDRS,FILEHDR, AT, FLAGS是关键字。
name,段的名字,只在SECTIONS命令中引用,不会放到输出文件中,也不会和符号、文件名、section名冲突。
type,段的类型,为下面关键字的值。
PT_NULL (0),未使用的程序头。
PT_LOAD (1),可加载段。
PT_DYNAMIC (2),动态链接信息段。
PT_INTERP (3),该段包含程序解释器名字。
PT_NOTE,包含note信息。
PT_SHLIB,ELF标准未定义。
PT_PHDR,包含程序头本身。
使用 :phdr 输出section属性,将一个输出section放入段中。将一个输出section放如多个段是常见的,通过重复使用 :phdr实现。当使用 :phdr将一个section放入段中后,后续的section都将放入相同的段,除非指定特定的段,这只是为了方便。可以使用:NONE属性告诉临界期不要将section放入任何段中。FILEHDR关键字表示段应该包含ELF头。PHDRS关键字表示应该包含ELF程序头本身。第一个PT_LOAD段,必须有一个上述关键字。使用AT命令,指定段的地址。链接器通常跟着成员section设置段flag。当然,也可以使用flags来显示的设置。
下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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 ... } |
ELF支持符号版本(symbol versions),符号版本只在使用共享库时有用。动态链接器使用符号版本来选择特定版本的函数。你可以在主链接脚本中包含版本脚本,也可以也可应用版本脚本为一个隐含的链接脚本,也可以使用--version-script链接选项。VERSION命令的语法如下:
1 | VERSION { version-script-commands } |
和c表达式一致,所有的表达式评估为整数,且有相同的size。在32位target上为32位,64位target则为64位。可以在表达式中使用符号的值。
常量:
0开头的是8进制。0x或0X开头的是16进制。
h或H结尾的是16进制。o或O结尾的是8进制。b或B结尾的是二进制。d或D结尾的是10进制。
可以使用K或M结尾,表示1024和1024*1024.
符号常量
格式CONSTANT(NAME)。NAME只有两种:
MAXPAGESIZE/COMMONPAGESIZE。表示最大和缺省页大小。
如 .text ALIGN (CONSTANT(MAXPAGESIZE)) : { *(.text) }
符号名:
除非使用引号。符号名以字母、下划线和点开始,跟着字母、数字、下划线、点和连字符。
无引号的符号名不能和任何关键字冲突。
"SECTION" = 9
Orphan sections
孤儿section是指那些输入文件中的,在链接脚本中没有显示的放入输出文件中的section。链接器仍然会将这些section放入输出文件。链接器会猜想如何放置,链接器使用简单的启发式的方法。链接器将其放在相同属性的非孤儿section方面。如果没有足够的空间,将放在最后面。
ELF目标属性包括section类型和section flag。
如果孤儿section的名字符合C标识符的规范,则链接器提供两个符号:__start_SECNAME和__stop_SECNAME。表示孤儿section的开始地址和结束地址。以.开头的section名不符合c标识符规范。
The location counter
输出链接器变量. 总是包含当前输出位置。既然 . 总是指向一个输出section的位置。因此他可能只出现在SECTIONS命令内的表达式中。在表达式中,. 符号可以出现在普通符号允许的地方。
给 . 赋值将改变location counter。这可能导致output section的空洞。
. 为相对当前containing object的起始位置的字节偏移。在SECTIONS命令内,其起始地址为0,因此也未绝对地址。然而如果 . 用在section description内,. 为相对于section起始地址的偏移,不是一个绝对地址。如
1 2 3 4 5 6 7 8 9 10 11 12 13 | SECTIONS { . = 0x100 .text: { *(.text) . = 0x200 } . = 0x500 .data: { *(.data) . += 0x600 } } |
如上.text将从0x100开始。而且大小为 0x200。如果0x200小于 *(.text)的大小,将报错(因为这视图向后移动.)。.data从0x500开始。包含*(.data),后面在跟0x600的填充。
在output section语句之外将.赋值给符号可能有期望之外的结果。因为链接器可能将孤儿orphan放在某个位置,这可能无形影响.的值。如:
1 2 3 4 5 6 7 8 9 10 | SECTIONS { start_of_text = . ; .text: { *(.text) } end_of_text = . ; start_of_data = . ; .data: { *(.data) } end_of_data = . ; } |
链接器可能将.rodata放入下述位置。
1 2 3 4 5 6 7 8 9 10 11 | SECTIONS { start_of_text = . ; .text: { *(.text) } end_of_text = . ; start_of_data = . ; .rodata: { *(.rodata) } .data: { *(.data) } end_of_data = . ; } |
链接器将赋值和其它语句想象为属于之前的输出section。但是赋给.的语句想象为下一个输出section。所以为了避免上述问题。可以在分割处添加 .=.
1 2 3 4 5 6 7 8 9 10 11 12 | SECTIONS { start_of_text = . ; .text: { *(.text) } end_of_text = . ; .=. start_of_data = . ; .rodata: { *(.rodata) } .data: { *(.data) } end_of_data = . ; } |
操作符
链接器识别c算术运算符集合,绑定和优先级相同。
计算
链接器计算表达式非常懒,除非有绝对必要时,才会计算。
链接器需要一些信息,如第一个section的起始地址,内存区的地址和长度等,这些值会尽快计算。
然而其他值会在存储分配后计算。如果表达式的值需要,但是当前无法计算,将导致一个错误。
表达式的section
一个地址或者符号可以是section相对的或绝对的,一个section相对的符号是可重定向的。如果使用-r生成可重定向输出。后续的链接可能改变section相对符号的值。儿绝对符号在链接器阶段不变。
表达式中的某些术语是地址。如section相对符号和返回地址的内建函数。例如:ADDR,LOADADDR,ORIGIN和SEGMENT_START。其它术语主要数字后者返回非地址的内建函数,如LENGTH。
除非设置LD_FEATURE("SANE_EXPR"),数字和绝对符号被不同的对待,这依赖于他们的位置。为了和老版本的ld兼容。在输出section定义之外的表达式,所有数字当做绝对地址。如果给出了LD_FEATURE("SANE_EXPR"),绝对符号和数字被简单的对待为数字。例子:
1 2 3 4 5 6 7 8 9 10 11 12 | SECTIONS { . = 0x100; __executable_start = 0x100; .data : { . = 0x10; __data_start = 0x10; *(.data) } ... } |
第一个.和__executable_start是绝对地址。而第二.和__data_start是相对于.data的地址。
对于涉及数字、相对地址和绝对地址的表达式,ld按下面的规则评估。
绝对地址和数字的单操作符,两个绝对地址或两个绝对数的双操作符,或者一个绝对地址、一个数字。应用操作符为value(es).
相对地址的单操作符,两个相同section内的两个相对地址,或者一个相对地址、一个数字的双操作。应用操作符为地址的偏移部分。
其它双操作符,即两个不在同一个section的相对地址;相对地址和绝对地址的。在引用到操作符之前,先将非绝对量转化为绝对量
子表达式的结果如下:
操作仅涉及数字结果是一个数字。
比较运算、&&和||的结果为数字。
同section的两个相对地址、两个绝对地址的双操作数算术和逻辑运算也是一个数字。
相对地址、一个相对地址一个数字的其它运算,是同一个section里的相对地址。
不满足上述条件的,绝对地址的其它运算,是一个绝对地址。
可以使用内建的ABSOLUTE函数,将一个表达式强制为了绝对。例如,创建一个绝对符号,地址为输出section .data的最后。
1 2 3 4 | SECTIONS { .data : { *(data) _edata = ABSOLUTE(.); } } |
使用LOADADDR也将一个表达式强制为绝对。
ABSOLUTE(exp)
返回表达式的绝对值(non-relocatable)。主要在section定义中,将一个绝对地址赋给符号。
ADDR(section)
返回名叫section的section的VMA地址。
ALIGN(align) ALIGN(exp, align)
前者返回.按align对齐的地址。后者返回表达式按align对齐的地址。
ALIGNOF(section)
返回setion的对齐要求。
DATA_SEGMENT_ALIGN(maxpagesize, commonpagesize)
DATA_SEGMENT_END(exp)
DATA_SEGMENT_RELRO_END(offset, exp)
DEFINED(symbol)
符号是否在全局符号表中定义,请定义在该语句之前。
LENGTH(memory)
返回内存区的长度
LOADADDR(section)
返回section的LMA地址
LOG2CEIL(exp)
MAX(exp1, exp2)
MIN(exp1,exp2)
NEXT(exp)
和ALIGN基本相同,除非使用MEMORY命令定义不连续的内存。
ORIGIN(memory)
返回memory的origin。
SEGMENT_START(segment, default)
返回段的基地址,如果没有-T选项设置,则返回default。
SIZEOF(section)
返回section的大小。
SIZEOF_HEADERS
返回文件头的大小。