0 参考资料
GNU-LD-v2.30-中文手册.pdf
GNU linker.pdf
1 前言
一个完整的编译工具链应该包含以下4个部分:
(1)编译器
(2)汇编器
(3)链接器
(4)lib库
在GNU工具链中,对应的是:
(1)编译器:GCC(GNU Compiler Collection,GNU编译器套件)
(2)汇编器:GAS(GNU Assembler,GNU汇编器)
(3)链接器:LD(GNU Linker,GNU链接器)
(4)lib库:glibc(GNU C Library,GNU C 库)
本文介绍GNU链接器脚本中设置入口点(ENTRY命令)的用法及实例解析。
2 GNU linker(链接器,LD):设置入口点(ENTRY命令)的用法及实例解析
2.1 设置入口点官方释义及ENTRY命令的用法
CPU需要执行的第一条指令被称为入口点(Entry Point)。可以使用链接器脚本命令ENTRY设置入口点,不过ENTRY命令设置的入口点是elf loader加载程序的入口点,而不是CPU需要执行的第一条指令。使用ENTRY命令会影响elf文件的entry_address 字段。ENTRY命令格式如下:
ENTRY(symbol)
symbol:符号名称,可以是函数名、地址
LD支持几种方法设置入口点,链接器将按照以下顺序设置入口点,直到找到成功的入口点为止:
(1)添加entry命令行选项:‘-e’
(2)在链接器脚本使用ENTRY(symbol)命令
(3)目标指定符号的值(如果已经定义):大部分情况下如果定义了start符号则使用start符号,但基于PE和BeOS的系统会检查其它可行的入口符号,直到找到第一个符合的
(4)代码段(.text段)首地址
(5)地址0
2.2 ENTRY命令实例解析
很多链接脚本将复位中断服务函数作为入口点,配置SECTION内.text段的输出分区存储区域就可以将复位中断服务函数放置到代码段首地址。如下是stm32MP135的链接器脚本ENTRY的定义:
/* Entry Point */
ENTRY(Reset_Handler)
text段定义如下:
.RESET . : {
__TEXT_START__ = .;
*(.text.Reset_Handler)
* (RESET)
*(.text*)
/* .init is used by libc_nano */
KEEP (*(.init))
KEEP (*(.fini))
__TEXT_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as
* read-only, executable. No non-executable data from the next section must
* creep in. Ensure the rest of the current memory page is unused.
*/
__TEXT_END__ = .;
. = NEXT(4096);
__RO_START__ = .;
*(.rodata*)
__RO_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as
* read-only, non-executable. No RW data from the next section must
* creep in. Ensure the rest of the current memory page is unused.
*/
. = NEXT(4096);
__RO_END__ = .;
} >RAM :text
这样操作之后代码段首地址就是Reset_Handler函数,查看编译生成的.map文件如下:
可以看到Reset_Handler函数被定义在代码段首地址。
stm32MP135的SYSRAM起始地址定义如下:
2 总结
(1)ENTRY命令不能指定CPU执行的第一条指令,只会影响到elf文件的entry_address 字段
(2)ENTRY命令通常使用复位中断服务函数作为符号,通过修改.text段的输出分区,将复位中断服务函数分配在镜像的起始地址