GNU链接语法
1. 脚本格式
链接脚本是由若干条命令组成,每个命令是由关键字组成
/ * */ 里面放的是注释信息。
2. 对STM32链接文件进行简单分析
/* Entry Point */
/* 入口地址 */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
_Min_Heap_Size = 0x200 ; /* required amount of heap */
_Min_Stack_Size = 0x400 ; /* required amount of stack */
/* Memories definition */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K
}
/* Sections */
SECTIONS
{
/* The startup code into "FLASH" Rom type memory */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
}
可以简单分析出:
- 程序启动地址是Reset_Handler。
- 内存分一个空间,起始位置: 0x20000000 长度: 64K
- FLASH分一个空间,起始地址: 0x08000000 长度: 512K
3. 命令介绍
- ENTRY(SYMBOL); SYMBOL可以是一个符号,或者是一个地址。
选择可执行文件的入口地址,该地址可以由以下方式指定:
- 使用gnu-ld命令行的-e选项,后跟入口地址
- 脚本的ENTRY命令
- 如果定义了start符号,则可以使用start符号值。这个可能和实际链接器有关系。
- 使用.text section的开始地址
- 使用0
优先级顺序,1最高,5最低
- INCLUDE(file) 包含名为file的链接文件,搜索路径按照默认配置路径
和C代码的#include类似,可以在需要的位置,包含另一个外部链接脚本。搜索路径需要由-L指定。脚本的嵌套使用最高支持10层,超过之后可能会有问题。 - INPUT(files) 将files作为输入文件。参加链接过程
首先会在当前路径下,搜索该文件,如果没有找到,则会去-L的指定路径下,进行查找。如果出现在多个脚本,则回按照执行顺序进行链接。 - GROUP(file) 指定要重复搜索符号定义的多个输入文件。
file 必须是库文件。 - OUTPUT(FILE) 定义输出文件的名字
相当于命令行的-o选项,但是-o的优先级较高。 - SEARCH_DIR(PATH) 搜索路径
相当于命令行的-L选项,但是-L的优先级较高 - STARTUP(file) 指定file为第一个输入文件
链接文件是需要有顺序的。STARTUP可以讲文件指定为输入的第一个文件。 - OUTPUT_FORMAT(BFD) 设置输出文件的BFD格式
- ASSERT(EXT, ,MSG) 断言,如果EXP不为真,则终止连接过程
- EXTERN(SYMBOL SYMBOL …):在输出文件中增加未定义的符号,如同连接器选项-u
- NOCROSSREFS(SECTION SECTION …):检查列出的输出section,如果发现他们之间有相互引用,则报错。对于某些系统,特别是内存较紧张的嵌入式系统,某些section是不能同时存在内存中的,所以他们之间不能相互引用。
- OUTPUT_ARCH(BFDARCH):设置输出文件的machine architecture(体系结构),BFDARCH为被BFD库使用的名字之一。可以用命令objdump -f查看。
4. 实际使用
- 赋值
SYMBOL = VALUE;
可以对一个符号进行赋值,最后需要使用;
作为语句结束。每个符号的值都可以作为符号的地址,显示在map文件中。
其中K,M的值为1024,1024*1024,方便使用。 - 运算
SYMBOL = EXPRESSION ;
SYMBOL += EXPRESSION ;
SYMBOL -= EXPRESSION ;
SYMBOL *= EXPRESSION ;
SYMBOL /= EXPRESSION ;
SYMBOL <<= EXPRESSION ;
SYMBOL >>= EXPRESSION ;
SYMBOL &= EXPRESSION ;
SYMBOL |= EXPRESSION ;
除了上述几种常见算数运算以外,还支持 .
运算符,表示当前地址,执行程序的一个位置,可以是RAM空间,也可以是FLASH空间。举例说明: test_point = . ;
注意:赋值语句包含4个语法元素:符号名、操作符、表达式、分号;一个也不能少。
符号可以在 1. 全局位置位置,2. SECTION的全局位置,3. 具体的section描述内部.
PROVIDE命令 该关键字用于定义这类符号:在目标文件内被引用,但没有在任何目标文件内被定义的符号。
- MEMORY 内存区域命令
MEMORY命令的文法如下,
MEMORY {
NAME1 [(ATTR)] : ORIGIN = ORIGIN1, LENGTH = LEN2
NAME2 [(ATTR)] : ORIGIN = ORIGIN2, LENGTH = LEN2
...
}
① NAME: 存储区名字
② ORIGIN: 区域的开始地址,必须是数字,可以有运算,但是不可以有符号。支持K, M符号,例如2K, 1M
③ LENGTH : 区域的长度,必须位数字,可以有运算,但是不可以有符号。支持K, M符号,例如2K, 1M
④ ATTR 区域属性,ATTR可以出现以下几个属性:
R 只读section
W 读/写section
X 可执行section
A ‘可分配的’section
I 初始化了的section
L 同I
! 不满足该字符之后的任何一个属性的section
例如:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K
}
定义RAM区域属性是 读+写+可执行,区域从0x2000 0000开始,长度为64K,正好符合STM32F103ZET6芯片使用的最大RAM空间。同时属性也是和RAM特性保持一致。
定义FLASH区域属性是 读+可执行,区域从0x0800 0000开始,长度为512K,正好符合STM32F103ZET6芯片使用的最大FLASH空间。同时属性和FLASH保持一致。
可以将RAM空间或者FLASH空间分割成几块MEMORY区域,但是不允许重叠。
- SECTION
SECTION命令是告诉链接器如何将输入段映射到某个输出段,如何将输出段映射到LMA和VMA上。命令用法:
SECTIONS
{
SECTIONS-COMMAND
SECTIONS-COMMAND
...
}
SECTIONS-COMMAND 常用的有 1, 符号赋值 2. 输出段描述 3. 输入段描述 下面简单对几个COMMOND做分析
- 符号赋值
可以在段内定义一个符号,并且对符号进行算数运算。一般配合.
来使用。 - 输出段描述
输出section描述具有如下格式:
SECTION [ADDRESS] [(TYPE)] : [AT(LMA)]
{
OUTPUT-SECTION-COMMAND
OUTPUT-SECTION-COMMAND
...
} [>REGION] [AT>LMA_REGION] [:PHDR :PHDR ...] [=FILLEXP]
[] 内参数是可选内容,可以不写
最简单格式
SECTION :
{
OUTPUT-SECTION-COMMAND
OUTPUT-SECTION-COMMAND
...
}
SECTION 段名字,用来区分各个section的
ADDRESS : 来指定当前SECTION所使用的VMA ,可以是一个表达式。如果没有该选项且有REGION选项,那么连接器将根据REGION设置VMA;如果也没有 REGION选项,那么连接器将根据定位符号‘.’的值设置该section的VMA,将定位符号的值调整到满足输出section对齐要求后的值,输出 section的对齐要求为:该输出section描述内用到的所有输入section的对齐要求中最严格的。
例子:
·.text . : { *(.text) }
和
.text : { *(.text) }
这两个描述是截然不同的,第一个将.text section的VMA设置为定位符号的值,而第二个则是设置成定位符号的修调值,满足对齐要求后的。
ADDRESS可以是一个任意表达式,比如ALIGN(0x10)这将把该section的VMA设置成定位符号的修调值,满足16字节对齐后的。
注意:设置ADDRESS值,将更改定位符号的值。
TYPE :每个输出section都有一个类型,如果没有指定TYPE类型,那么连接器根据输出section引用的输入section的类型设置该输出section的类型。它可以为以下五种值,
NOLOAD
:该section在程序运行时,不被载入内存。
DSECT
,COPY
,INFO
,OVERLAY
:这些类型很少被使用,为了向后兼容才被保留下来。这种类型的section必须被标记为“不可加载的”,以便在程序运行不为它们分配内存。
AT(LMA) 一般来说LMA和VMA是同一个地址,但是也可以通过AT命令来手动指定加载地址(LMA) LMA是一个地址或者符号
OUTPUT-SECTION-COMMAND 命令,可以是
REGION 指定输出的内存段,必须是MEMORY中已经定义的
AT>LMA_REGION 可以使用AT指令,重新指定LMA到一个MEMORY段
FILLEXP 将section中所有未使用的位置,填充上FILLEXP值,有效数据为两个字节,例如 FILLEXP=0x9090
3. 输入段描述
基本语法:FILENAME([EXCLUDE_FILE (FILENAME1 FILENAME2 …) SECTION1 SECTION2 …)
FILENAM 可以是一个带有相对路径的目标文件名,同时支持通配符格式。如果是库内目标文件名字的话,不需要带有路径,只保留文件名就行。
EXCLUDE_FILE(file1, file2) 除file1, file2之外的所有目标文件
SECTION 可以是一个setcion名字或者是一个字符串
下面举例说明:
① * (.text)
第一个*代表所有文件 总体就是,所有输入目标文件的 .text 段
② data.o (.data)
data.o这个目标文件的所有.,data段
③ ifx*.o (.text, .data)
所有以 ifx
为开头的目标文件的所有.text和.data段。其中Ifx*.o是不带路径的,也就是选取的库内目标文件或者同目录下的目标文件。
④ *(EXCLUDE_FILE(file1.o, *file2.o) (.text))
除了file.o文件和所有以file2为结尾的目标文件以外的所有文件的.text段
通配符可以使用
字符串模式内可存在以下通配符:
* :表示任意多个字符
? :表示任意一个字符
[CHARS] :表示任意一个CHARS内的字符,可用-号表示范围,如:a-z
可以将多个 input section 组成 output section 例如:
SECTIONS {
.text : { *(.text) }
.DATA : { [A-Z]*(.data) }
.data : { *(.data) }
.bss : { *(.bss) }
}
① 所有文件的.text
输入段,组成.text
输出段
② [A-Z]开头的文件的.data
输入端,组成.DATA
输出段
③ 剩余文件的 .data
输入段,组成.data
输出段
④ 所有文件的 .bss
输入段,组成 .bss
输出段
- 内部函数
ABSOLUTE(EXP) :转换成绝对值
ADDR(SECTION) :返回某section的VMA值。
ALIGN(EXP) :返回定位符’.'的修调值,对齐后的值,(. + EXP - 1) & ~(EXP - 1)
BLOCK(EXP) :如同ALIGN(EXP),为了向前兼容。
DEFINED(SYMBOL) :如果符号SYMBOL在全局符号表内,且被定义了,那么返回1,否则返回0。
LOADADDR(SECTION) :返回三SECTION的LMA
MAX(EXP1,EXP2) :返回大者
MIN(EXP1,EXP2) :返回小者
NEXT(EXP) :返回下一个能被使用的地址,该地址是EXP的倍数,类似于ALIGN(EXP)。
SIZEOF(SECTION) :返回SECTION的大小。