链接脚本文件入门介绍

本文将对链接脚本文件从Why/What/How角度进行入门介绍,辅助对链接脚本的初步上手,如您对汽车电子BSW部分Autosar全模块实战感兴趣,可参读热销专栏:AutoSar实战进阶系列导读,本文大纲如下:
在这里插入图片描述

1. Why<为什么需要链接文件>

在嵌入式软件编译过程中,需要经历预处理→编译→汇编→链接的过程,那在链接过程中,各个段在可执行文件如elf中的先后组装顺序也是一个需要考虑的问题,一个可执行程序肯定会有入口地址的,一般先执行的代码要放到前面。那么如何指定程序的链接地址和各个段的组装顺序呢?这就是链接脚本的作用了。

链接脚本对于嵌入式系统的程序开发尤其重要,为了系统的正常加载运行,开发者需要精确控制代码和数据在内存中的位置及顺序。

2.What<是什么及组成>

链接脚本由链接器(链接程序,如 GNU 的 ld)用于控制链接过程,最终生成可执行文件。链接脚本本质上是一个脚本文件,在这个脚本文件里,不仅规定了各个段的组装顺序、起始地址、位置对齐等信息,同时对输出的可执行文件格式、运行平台、入口地址等信息做了详细的描述。

链接文件主要包括两部分:MEMORY及SECTIONS

2.1 MEMORY介绍

MEMORY定义在链接脚本中用来描述目标系统的物理内存布局,它定义了一组内存区域,每个区域都有一个名称、起始地址、长度和属性。链接器会根据这些信息,将各个段放置在合适的内存区域中。

ORIGIN是一个关于内存区域地始地址的表达式。在内存分配执行之前,这个表达式必须被求值产生一个常数,这意味着你不可以使用任何节相关的符号。关键字’ORIGIN’可以被缩写为’org’或’o’(但是,不可以写为,比如‘ORG’)
LEN是一个关于内存区域长充(以字节为单位)的表达式。就像ORIGIN表达式,这个表达式在分配执行前也必须被求得为一个常数值。关键字’LENGTH’可以被简写为‘len’或’l’。

MEMORY
{
        Flash0          : org = 0x8000,          l = 2M
}

2.2 SECTIONS介绍

SECTIONS关键字用于的作用是定义输出文件中的段,并控制这些段的内容、布局和属性,包括output section(输出段)的相应input section(输入段)、LMA和VMA,是整个连接脚本中最为重要的部分。

LMA和VMA说明如下:每个output section都有一个LMA和一个VMA,LMA是其存储地址,而VMA是其运行时地址,例如将全局变量g_data所在数据段.data的LMA设为0x80000020(属于ROM地址),VMA设为0xD0004000(属于RAM地址),那么g_data的值将存储在ROM中的0x80000020处,而程序运行时,用到g_Data的程序会到RAM中的0xD0004000处寻找它。

如下介绍三种不同的case:

SECTIONS
{
    // case1:指定固定地址
  .my_data1 ( 0xD0004000 ) : AT ( 0x80000020 )
  {
    *(.myData1)
  }  ...

    // case2:仅指定内存空间,具体地址紧接着上一个output section的末尾地址。
  .my_data2 :
  {
    *(.myData2)
  } > ram AT> rom  ...

    //case3:对于代码段.text这种LMA与VMA相同的情况,可只定义VMA而不必指明LMA 
    .my_data_start align(4) :>Flash0//这是一个自定义的section名称
    .my_data3:
    {
        *(.mydata3)// 这是一个通配符表达式,用于匹配所有以.sdata.ptavect开头的输入文件中的符号,并将它们添加到输出文件的.rosdata section中。
    } > Flash0 //表示将.rosdata section的内容放置在名为ASW0_Flash的存储器区域中。
    .my_data_end :>Flash0 链接段解析
}

3.How<链接文件应用>

在嵌入式软件实际开发中,会遇到一些场景需要将数据放入特定的字段,如对于XCP开发时,需要将标定量放到DS段中等,在后续介绍中会对一些常见场景及使用方法进行介绍。

3.1 定义特定字段

SECTIONS
{
    .rosdata:
    {
        *(.sdata.cal)
    } > Flash0 
}

3.2 将变量定义在指定段

可使用#pragma或attribute section两种方式:

/* case1 use #pragma */
#pragma section ".sdata.cal" awB
int testVar;
#pragma section

/* case2 use attribute */
int testVar __attribute__((section(".sdata.cal" ,"f=awB")) );

//a表示allocatable,w表示writable,B表示uninitialized(bss)

3.3 将变量定义在不同的段

当变量在section1存不下时,保存到section2,此时需要在两个段中都定义。

MEMORY
{
  ...
  MYRAM1 : org = D0000000, len = 8K
  MYRAM2 : org = D0004000, len = 8K
  ...
}

SECTIONS
{
    .mydata1 :
    {(.mydata)
     } > MYRAM1

    .mydata2 :
    {(.mydata)
    } > MYRAM2
}

所有的数组都被放在.mydata段中,但是.mydata1和.mydata2分别被映射到MYRAM1和MYRAM2中,如果MYRAM1满了后,剩余的值将会置于MYRAM2中。

/* use #pragma */
#pragma section ".mydata" awB
int testVar;
#pragma section

3.4 将变量定义在指定地址

定义时,可将段的地址明确,在将地址放入段时地址就明确了。

SECTIONS
{
   .user_defined_bss 0x90001000 : ALIGN(4) FLAGS(aw)
   {
        *(.sdata.cal)    
   } > CPU0_DLMU
}

3.5 将函数定义在指定段

__attribute__((section(".mydata"))) void myFunction() 
{
    // 函数实现
}

3.6 将函数定义在指定地址

只需要在section定义时加上地址即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Autosar汽车电子进阶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值