将符号 symbol 的值设置为入口地址
链接器 (ld) 有多种方法设置程序入口地址,按照一下顺序:(编号越前,优先级越高)
如果输入文件没有定义,start,可以简单定义一个并给一个合适的值
start = 0x2020;
上面的示例中使用的是绝对地址,当然,也可以指定为任意合法的表达式。
start = other_symbol;
实际使用中,可以指定为复位向量地址。
/* Entry Point */
ENTRY(Reset_Handler)
MEMORY 声明一个或多个内存区域,其属性指定该区域是否可以写入、读取或执行。这主要用于不同地址空间区域可能包含不同访问权限的嵌入式系统。
MEMORY
{
name (attr) : ORIGIN = origin, LENGTH = len
...
}
MEMORY
{
rom (rx) : ORIGIN = 0, LENGTH = 256K
ram (!rx) : org = 0x40000000, l = 4M
}
SECTIONS 命令告诉 ld
如何把输入文件的 sections 映射到输出文件的各个 section: 如何将输入 section 合为输出 section
如何把输出 section 放入程序地址空间 (VMA) 和进程地址空间 (LMA).
SECTIONS
{
SECTIONS-COMMAND
SECTIONS-COMMAND
…
}
SECTION-COMMAND 有四种:
如果整个连接脚本内没有 SECTIONS 命令, 那么 ld 将所有同名输入 section 合成为一个输出 section 内, 各输入 section 的顺序为它们被连接器发现的顺序. 如果某输入 section 没有在 SECTIONS 命令中提到, 那么该 section 将被直接拷贝成输出 section。
链接器脚本中的 KEEP 语句将指示链接器保留指定的节,即使其中没有引用任何符号。此语句在链接器脚本的节中使用。当在链接时执行垃圾收集时,这一点就变得很重要,在链接命令行内使用了选项 -gc-sections
后,链接器可能将某些它认为没用的 section 过滤掉,此时就有必要强制让链接器保留一些特定的 section,可用 KEEP() 关键字达此目的。说的通俗易懂就是:
防止被优化。
该语句常见于针对 ARM 体系结构的链接器脚本中,用于将中断向量表放置在偏移量 0x00000000 处。如果没有这个指令,代码中可能不会显式引用的表将被删除。
例如在 NXP imx6ul 相关链接脚本中
.interrupts :
{
__VECTOR_TABLE = .;
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} > m_interrupts
为在任何链接目标中没有定义但是被引用的一个符号, 而在链接脚本定义一个符号。 PROVIDE(symbol = expression)
。下面是一个提供 __bss_start
和 __bss_end
的例子
.bss :
{
PROVIDE(__bss_start = .);
*(.bss)
*(.bss.*)
*(.dynbss)
PROVIDE(__bss_end = .);
}
如果程序定义了 __bss_start
(带有前导下划线),链接器将给出重复定义错误。
如果程序定义了 bss_start
(没有前导下划线),链接器会默认使用程序中的定义。
如果程序引用了 __bss_start
但没有定义它,链接器将使用链接器脚本中的定义。
在 C 语言中可以这样使用
extern char __bss_start;
extern char __bss_end;
#define BSS_START_ADDR (void *)&__bss_start
#define BSS_END_ADDR (void *)&__bss_end
char *src = &__bss_start;
char *dst = &__bss_end;
section 包含两个地址:VMA(virtual memory address 虚拟内存地址或程序地址空间地址) 和 LMA(load memory address 加载内存地址或进程地址空间地址)。默认情况下 LMA 等于 VMA,但可以通过关键字 AT() 指定 LMA。
用关键字 AT() 指定,括号内包含表达式,表达式的值用于设置 LMA。如果不用 AT() 关键字,那么可用 AT>LMA_REGION
表达式设置指定该 section 加载地址的范围。这个属性主要用于构件 ROM 境象。
一般而言, 某 section 的 VMA == LMA. 但在嵌入式系统中, 经常存在加载地址和执行地址不同的情况: 比如将输出文件加载到开发板的 flash 中 (由 LMA 指定), 而在运行时将位于 flash 中的输出文件复制到 SDRAM 中 (由 VMA 指定)。
AT(LMA_ADDR)
使用示例
.data : AT(0x00900000)
{
__data_start__ = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
__data_end__ = .; /* define a global symbol at data end */
}
AT>LMA_REGION
使用示例
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
表示字节对齐, 如 . = ALIGN(4);
表示从该地址开始后面的存储进行 4 字节对齐。