GNU链接脚本使用

随声附和 提交于 2019-12-05 20:02:45

GNU链接脚本使用


链接脚本主要作用是描述输入文件的section是如何映射到输出文件的内存布局中。通俗的讲法就是编译完成后的各个obj文件,按照哪种顺序放与bin的哪个地址中;同时将运行时变量符号与VMA关联。
主要参考了下边几个博客:
1. 链接脚本的 __attribute__ 属性
2. GNU-ld链接脚本浅析

使用案例

1. 将变量或函数存放在用户定义的区域

主要目的是可以将变量或函数,运行时链接到用户定义的区域中,比如SRAM,某些CPU比直接放在FLASH中运行快(不一定,比如STM32,片内FLASH在ICODE总线上,指令预取功能使能将实现类似多级流水线的操作,SRAM则不再ICODE上,需要 "取指->译码->执行"无法加速。
主要使用链接脚本语法有:
MEMORY命令定义存储区域,通过输出section描述的 >REGION 显式的将该段限定到具体的存储区域。 如下:最后的区域为stm32f746ng最后64k,用来存放测试的变量。

MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 320K
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 960K
USER_AREA(rx)   : ORIGIN = 0x80F0000, LENGTH = 64K
}

在链接脚本的SECTIONS 段内添加下列描述:

  _siusrdata = LOADADDR(.usrdata); /* LOADADDR描述了.usrdata段的LMA,可在程序中声明:extern char _siusrdata; 使用时只能只用_siusrdata的地址:char *src = &_siusrdata;  src实际等于0x80F0000*/

  .usrdata :
  {
    . = ALIGN(4);
    _susrdata = .;        /* create a global symbol at usrdata start;VMA地址*/
    *(.usrdata)           /* .usrdata sections */
    *(.usrdata*)          /* .usrdata* sections */

    . = ALIGN(4);
    _eusrdata = .;        /* define a global symbol at usrdata end */
  } >RAM AT> USER_AREA

主程序中在声明处使用 attribute属性声明section, 使用extern 声明链接脚本中的变量:

int data_usrarea __attribute__((section(".usrdata"))) = 6666 ;
extern char _siusrdata,_susrdata,_eusrdata;

使用时:

printf("usrarea LMA = %X, VMA = %X\r\n", &_siusrdata, &_susrdata);
printf(usrdata addr = %X, value=%d\r\n", &data_usrarea,data_usrarea);

打印结果如下:

usrarea LMA = 80F0000, VMA = 20000070
usrdata addr = 20000070, value=-536140031

地址是对的,但是value怎么是错的,实际是因为,我们自定义的区域并沒有自己将数据加载到正确的VMA地址。而系统默认的.data区域是由系统启动时加载的,不同的工程实现方法不同,如stm32 cubumx 生成的makefile工程中是这样做的

Reset_Handler:  
  ldr   sp, =_estack      /* set stack pointer */

/* Copy the data segment initializers from flash to SRAM */  
  movs  r1, #0
  b  LoopCopyDataInit

CopyDataInit:
  ldr  r3, =_sidata
  ldr  r3, [r3, r1]
  str  r3, [r0, r1]
  adds  r1, r1, #4
    
LoopCopyDataInit:
  ldr  r0, =_sdata
  ldr  r3, =_edata
  adds  r2, r0, r1
  cmp  r2, r3
  bcc  CopyDataInit
  ldr  r2, =_sbss
  b  LoopFillZerobss

这段汇编的目的就是加载.data区至SRAM中,即已初始化的全局变量区。我们也可以在汇编中添加自己的代码,但是最好还是在main函数中去实现不破坏原有文件的完整性。

  char *srcdata_ptr = &_siusrdata;
  char *dstdata_ptr = &_susrdata;
  while(dstdata_ptr < &_eusrdata)
  {
    *dstdata_ptr++ = *srcdata_ptr++;
  }

添加上述代码完成.usrdata区域从 LMA处加载到VMA处,以后就可以快乐的运行了,打印信息如下。

usrarea LMA = 80F0000, VMA = 20000070
usrdata addr = 20000070, value=6666

思考,有了这个功能,实际上我们可以将程序分段更新,如定义一些配置参数存放在特定区域,然后在更新时可以直接这个区域更新。更加灵活的完成在线升级。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!