1、命令介绍
命令 作用 KEEP 保证该段一定在输出文件里,不会被丢弃 MEMORY 描述目标设备的内存情况,内存分几个区域,每个内存区域的属性 PROVIDE 从链接脚本导出符号给C语言或者汇编语言使用 ENTRY 程序入口 AT 指定段的加载地址 ALIGN 指定地址的对齐 LOADADDR 获取段的加载地址
想了解更多MEMORY命令,可参考博客:《GNU链接脚本的MEMORY命令解析》;
2、freeRTOS的链接脚本分析
2.1、freeRTOS的完整链接脚本
OUTPUT_ARCH( "riscv" ) /*输出程序是riscv架构*/
ENTRY( _start ) /*程序的入口是_start标号处*/
MEMORY
{
/* Fake ROM area */
rom ( rxa) : ORIGIN = 0x80000000, LENGTH = 512K
ram ( wxa) : ORIGIN = 0x80080000, LENGTH = 512K
}
SECTIONS
{
.init :
{
_text = . ;
KEEP ( *( SORT_NONE( .init)) ) //保证.init段不会被丢弃
} > rom AT> rom
.text :
{
*( .text.unlikely .text.unlikely.*)
*( .text.startup .text.startup.*)
*( .text .text.*)
*( .gnu.linkonce.t.*)
} > rom AT> rom
.fini :
{
KEEP ( *( SORT_NONE( .fini)) )
_etext = . ;
} > rom AT> rom
.rodata.align :
{
. = ALIGN( 4 ) ;
_rodata = . ;
} > rom AT> rom
.rodata.start :
{
_rodata_lma = LOADADDR( .rodata.start) ; //获取.rodata.start段的加载地址
} > rom AT> rom
.rodata :
{
*( .rdata)
*( .rodata .rodata.*)
*( .gnu.linkonce.r.*)
. = ALIGN( 4 ) ;
_erodata = . ;
} > rom AT> rom
.data.align :
{
. = ALIGN( 4 ) ;
_data = . ;
} > ram AT> rom
.data.start :
{
_data_lma = LOADADDR( .data.start) ;
} > ram AT> rom
.data :
{
*( .data .data.*)
*( .gnu.linkonce.d.*)
. = ALIGN( 8 ) ;
PROVIDE( __global_pointer$ = . + 0x800 ) ; //导出符号“__global_pointer$”
*( .sdata .sdata.*)
*( .sdata2 .sdata2.*)
*( .gnu.linkonce.s.*)
. = ALIGN( 8 ) ;
*( .srodata.cst16)
*( .srodata.cst8)
*( .srodata.cst4)
*( .srodata.cst2)
*( .srodata .srodata.*)
. = ALIGN( 4 ) ; //下一个地址要4字节对齐
_edata = . ;
} > ram AT> rom //.data段的链接地址是在ram地址区域,但是加载地址在rom地址区域
.bss.align :
{
. = ALIGN( 4 ) ;
_bss = . ;
} > ram AT> rom
.bss.start :
{
_bss_lma = LOADADDR( .bss.start) ;
} > ram AT> rom
.bss :
{
*( .sbss*)
*( .gnu.linkonce.sb.*)
*( .bss .bss.*)
*( .gnu.linkonce.b.*)
*( COMMON)
. = ALIGN( 4 ) ;
_ebss = . ;
} > ram AT> rom
. = ALIGN( 8 ) ;
_end = . ;
.stack :
{
. = ALIGN( 16 ) ;
. += __stack_size;
_stack_top = . ;
} > ram AT> ram
}
2.2、分析.data段的重定位代码
2.3、.data段重定位的实际效果