一、链接脚本实例:
我们来句句分析这些链接脚本的实例:
. 代表当前地址
SECTIONS{.= 0xC0200000; //*对于STM32MP157设置链接地址为0xC0200000,对于IMX6ULL设为0x80200000*/.= ALIGN(4);//将当前位置向四取整.text //开始摆放代码段数据,当前地址往上增长
{(.text)//代码段取出来
}.= ALIGN(4): //当前位置向四取整
.rodata : { *(.rodata) }//只读数据段取出来,当前地址往上继续增长·= ALIGN(4);
.data : { *(.data) } //数据段取出来.= ALIGN(4):
__bss_start =.; //用它存当前地址作为bss起始地址
.bss : { *(.bss) *(.COMMON) }//bss段取出来
__bss_end =.; //用它存当前地址作为bss结束地址
}
注:到时候吧bss起始地址和结束地址取出来,直接清零就可以清零bss段了
二、完整的语法
SECTIONS {
...
secname start BLocK(align)(NOLOAD) : AT( ldadr ) //secname是段名{ contents }>region :phdr =fill
...
}//secname是段名,我们随便命名、start是起始地址(不写默认当前地址)、 BLocK(align)指定怎么对齐、
//AT( ldadr ) 是加载地址,不填就是等于他的链接地址、 { contents }段的内容,一般写成*(.test)
三、重定位数据段:
1、框架:
2、概述
由图可知:将A处代码重定位带B处
具体思路:A处的只读数据的结束地址减去起始地址通过memcy函数重定位到B
代码如下:
.text
.globle _start
_start/* 设置sp */
ldr sp = (0xc0000000 + 0x100000) //栈的起始地址/* rodata/data的重定位 */ldr r0, = __rodata_start //读出只读数据段的起始地址
//只读数据段的结束地址 = 只读数据段的起始地址 - (B - A)
ldr r2, =_start /* link addr*/
adr r3, _start /* load addr */
sub r2, r2, r3 //B - A
sub r1, r0 ,r2 /* 只读数据段的结束地址 */ldr r3 = __bss_start
sub r2, r3, r0bl memcpy /* r0: 目的, r1:源, r2:长度 *//* 调用main函数 */
bl mainvoid memcpy(void *dest, void *Src, unsigned int len)
{unsigned char* pcDest = dest;unsigned char* pcSrc = Src;while(led--){ *pcDest = *pcSrc;//目的 = 源pcSrc++;pcDest++;}
}
四、清除bss段
1、概述:
由于没有初始值或者初始值为零的全局变量,太浪费内存,我们把它放在bss段,只记录他的起始地址和结束地址,使用之前把那些段清零就可以
2、代码实现:
.text
.globle _start
_start/* 设置sp */
ldr sp = (0xc0000000 + 0x100000) //栈的起始地址/* rodata/data的重定位 */ldr r0, = __rodata_start //读出只读数据段的起始地址
//只读数据段的结束地址 = 只读数据段的起始地址 - (B - A)
ldr r2, =_start /* link addr*/
adr r3, _start /* load addr */
sub r2, r2, r3 //B - A
sub r1, r0 ,r2 /* 只读数据段的结束地址 */ldr r3 = __bss_start
sub r2, r3, r0bl memcpy /* r0: 目的, r1:源, r2:长度 *//* clear_bss */
ldr r0, = __bss_start
mov r1, #0
ldr r2, = __bss_end
sub r2, r2, r0
bl memset /* r0: dest, r1: val(0), r2: len *//* 调用main函数 */
bl mainvoid memcpy(void *dest, void *Src, unsigned int len)
{unsigned char* pcDest = dest;unsigned char* pcSrc = Src;while(led--){ *pcDest = *pcSrc;//目的 = 源pcSrc++;pcDest++;}
}
void memset(void *dest, unsigned char val, unsigned int len)
{unsigned char* pcDest = dest;while(led--){ *pcDest = val;//目的 = 源pcDest++;}
}
五、重定位代码段
为什么我们重定向了数据段、清除了bss,执行main程序可以打印出来呢?
因为现在还在A加载地址附近运行程序,没去B链接地址运行
代码实现:
.text
.globle _start
_start/* 设置sp */
ldr sp = (0xc0000000 + 0x100000) //栈的起始地址/* text/rodata/data的重定位 */
ldr r0, = _start
adr r1, _startldr r3, = __bss_startsub r2, r3, r0bl memcpy /* r0: 目的, r1:源, r2:长度 *//* clear_bss */
ldr r0, = __bss_start
mov r1, #0
ldr r2, = __bss_end
sub r2, r2, r0
bl memset /* r0: dest, r1: val(0), r2: len *//* 调用main函数 */
bl mainvoid memcpy(void *dest, void *Src, unsigned int len)
{unsigned char* pcDest = dest;unsigned char* pcSrc = Src;while(led--){ *pcDest = *pcSrc;//目的 = 源pcSrc++;pcDest++;}
}
void memset(void *dest, unsigned char val, unsigned int len)
{unsigned char* pcDest = dest;while(led--){ *pcDest = val;//目的 = 源pcDest++;}
}