GNU 链接文件介绍

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
}

可以简单分析出:

  1. 程序启动地址是Reset_Handler。
  2. 内存分一个空间,起始位置: 0x20000000 长度: 64K
  3. FLASH分一个空间,起始地址: 0x08000000 长度: 512K

3. 命令介绍

  • ENTRY(SYMBOL); SYMBOL可以是一个符号,或者是一个地址。
    选择可执行文件的入口地址,该地址可以由以下方式指定:
  1. 使用gnu-ld命令行的-e选项,后跟入口地址
  2. 脚本的ENTRY命令
  3. 如果定义了start符号,则可以使用start符号值。这个可能和实际链接器有关系。
  4. 使用.text section的开始地址
  5. 使用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做分析

  1. 符号赋值
    可以在段内定义一个符号,并且对符号进行算数运算。一般配合 . 来使用。
  2. 输出段描述
    输出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的大小。

你可能感兴趣的:(stm32,gnu)