e² studio中链接脚本的修改指导

描述

程序编译的几个阶段

一般而言,程序编译经历下图四个阶段,链接是编译的最后一步,无论是在PC上编译代码,还是在PC上使用嵌入式gcc工具交叉编译嵌入式代码,编译过程都是如下几步。深入理解链接过程是嵌入式工程师必要掌握的能力!

脚本

ld链接脚本的基础概念

链接过程是将各式各样的.o文件链接为一个文件的过程。链接脚本描述连接器如何将这些输入文件(.o)文件映射为一个输出文件的,并且定义了输出文件的memory layout。几乎所有的链接脚本都是在做这些事情。

下面给出一个简单的链接脚本实例,每行脚本都有相应的注解:

左右滑动查看完整内容

 

 

SECTIONS
{
    . = 0x10000; 
    .text : { *(.text) }
    . = 0x8000000;
    .bss : { *(.bss) }
}

 

 

上面提到的定位计数器就是点 ‘.

这个链接脚本文件(Linker Scripty),用于告诉链接器如何将不同的代码和数据段(sections)组合在一起形成可执行文件。下面我会解释其中的每一部分:

1 . = 0x10000;

这行代码重新设置了定位计数器(location counter)的值为0x10000,即地址0x10000。

它告诉链接器在此处开始分配.text段的地址空间。

2 .text : { *(.text) }

这行代码定义了一个.text段,并告诉链接器将所有名为.text的数据节(section)放入这个段中。

*(.text)表示将所有输入文件中的.text段合并到输出文件的.text段中。

3  . = 0x8000000;

这行代码重新设置了定位计数器的值为 0x8000000,即地址 0x8000000。

它告诉链接器在此处开始分配.data和.bss段的地址空间。

4  .data : { *(.data) }

这行代码定义了一个.data段,并告诉链接器将所有名为.data的数据节放入这个段中。

*(.data)表示将所有输入文件中的.data段合并到输出文件的.data段中。

5 .bss : { *(.bss) }

这行代码定义了一个.bss段,并告诉链接器将所有名为.bss的数据节放入这个段中。

*(.bss)表示将所有输入文件中的.bss段合并到输出文件的.bss段中。

总体来说,这段链接脚本告诉链接器在特定的地址处分配.text、.data和.bss段,并将对应的数据节合并到这些段中。

链接脚本相关的概念

内存(Memory)

左右滑动查看完整内容

 

 

MEMORY
{
    name [(attr)] : ORIGIN = origin, LENGTH = len
…
}

 

 

注解:这里的“attr”只能由以下特性组成:

‘R’ Read-only section

‘W’ -- Read/write section

‘X’ -- Executable section

‘A’ -- Allocatable section

‘I’ -- Initialized section

‘L’ -- Same as ‘I’

‘!’ -- Invert the sense of any of the attributes that follow

左右滑动查看完整内容

 

 

/* Memories definition */
MEMORY
{
    RAM (xrw) : ORIGIN = 0x20000300, LENGTH = 36K
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
}

 

 

注解:

“xrw”表示“RAM”区是可读、可写和可执行的,且RAM 的起始地址为“0x20000300”,长度为36K。

“rx”表示“FLASH”区是可读和可执行的,FLASH的起始地址为“0x08000000”,长度为128K。

段(Section)

Section有loadable(可加载)和allocatable(可分配)两种类型。不可加载也不可分配的内存段,通常包含某些调试信息。

loadable(可加载)是指:程序运行时,该段内容应该被加载到内存中。

allocatable(可分配)是指:该段的内容应该被预留出,但不应该加载任何别的内容(某些情况下,这些内存必须归零)。

“可加载”和“可分配”的section都有两个地址:“VMA”和“LMA”。

VMA(the virtual memory address):这是运行输出文件时,该section的地址。VMA是可选项,可以不设置。

LMA(load memory address):这是加载section时的地址。

在大多数情况下,这两个地址是相同的。当然也可以不相等,比如下面的例子就是LMA和VMA不同的案例:

数据段被加载到ROM中,然后在程序启动时复制到RAM中(通常用于初始化全局变量)。此时ROM地址就是LMA,RAM地址就是VMA。

语法:

左右滑动查看完整内容

 

 

SECTIONS
{
    section [address] [(type)] :
    {
        [AT(lma)]
        [ALIGN(section_align)   | ALIGN_WITH_INPUT]
        [SUBALIGN(subsection_align)]
        [constraint]
        {
            output-section-command  
            output-section-command  
            …
        } [>region] [AT>lma_region] [:phdr   :phdr ...] [=fillexp] [,]
    ...
}

 

 

大多数的段仅使用了上述的一部分属性。

示例:

左右滑动查看完整内容

 

 

/* Sections */ 
SECTIONS
{
    /* The startup code into "FLASH" Rom type memory */
    .isr_vector : 
    {
        . = ALIGN(4); 
        KEEP(*(.isr_vector)) /* Startup code */ 
        . = ALIGN(4); 
    } >FLASH


    /* Initialized data sections into "RAM" Ram type memory */
    .data:
    {
        . = ALIGN(4);
        _sdata = .; /* create a global symbol at data start */
        *(.data) /* .data sections */
        *(.data*) /* .data* sections */
        . = ALIGN(4);
        _edata = .; /* define a global symbol at data end */
    } >RAM AT> FLASH
 
}

 

 

上述示例中“.isr_vector”的LMA与VMA是相等的。“.data”因为有“>RAM AT> FLASH”的修饰,表示.data段的VMA为RAM,LMA为FLASH。即.data段的内容会放在FLASH中,但是运行时,会加载到RAM中。

常用命令

ASSERT

语法:ASSERT(exp, message)

确保exp是非零值,如果为零,将以错误码的形式退出链接文件,并输出message。在必要的位置添加断言,可以清晰的定位问题。

左右滑动查看完整内容

 

 

/* The usage of ASSERT */ 
.test : 
{ 
    ASSERT ((_estack > (_Min_Stack_Size + _Min_Heap_Size)),"Error: There is an ERR occurred"); 
}

 

 

当示例中的“_estack”大于“_Min_Stack_Size + _Min_Heap_Size”时,就会打印“There is an ERR occurred”。

KEEP

用途:当链接器使用('--gc-sections')进行垃圾回收时,KEEP()可以使得被标记段的内容不被清除。

左右滑动查看完整内容

 

 

/* The startup code into "FLASH" Rom type memory */
.isr_vector : 
{
    . = ALIGN(4); 
    KEEP(*(.isr_vector)) /* Startup code */ 
    . = ALIGN(4); 
} >FLASH

 

 

指定“变量”的输出地址:

可以定义如下的memory,然后将“变量”存放于该memory,就能控制“变量”的输出地址。

左右滑动查看完整内容

 

 

/* Memories definition */
 
MEMORY 
{ 
    FW_RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x300 /* 0x20000000 ~ 0x200002FF */ 
    RAM (xrw) : ORIGIN = 0x20000300, LENGTH = 35K 
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K 
}

 

 

同时在c文件中,在定义“变量”时,添加如下对应的属性:

__attribute__((section(".FW_RAM"))) uint8_t key[8] = {0,1,2,3,4,5,6,7 };

变量将位于“0x20000000 ~ 0x200002FF”区域(如果仅仅只有key数组位于该区域,将从0x20000000开始存放,如果有多个变量存储于该区域,将按照编译的顺序,从0x20000000依次存放)。

指定“函数”的输出地址:

可以定义如下的memory和section,然后将“函数”存放于该section,就能控制“函数”的输出地址。

左右滑动查看完整内容

 

 

/* Memories definition */ 
MEMORY 
{ 
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 0x300 /* 0x08000000 ~ 0x080002FF */ 
    CG_FLASH (rx) : ORIGIN = 0x08000300, LENGTH = 0x134 /* 0x08000300 ~ 0x08000433 */ 
    RAM (xrw) : ORIGIN = 0x20000300, LENGTH = 0x900 /* 0x20000300 ~ 0x20001FFF */ 
}
 
SECTIONS
{
    …
    .SE_Call_Fun:
    {
         . = ALIGN(4); 
         . = . + 0x4; 
        *(.SE_Call_Fun)  
         . = ALIGN(4);
    } > CG_FLASH
    …
}

 

 

同时在c文件中, 在“函数”的实现部分,添加如下对应的属性:

__attribute__((section(".SE_Call_Fun"))) uint32_t call_fun(Callgate_Func_Type_t ftype, void *param)

函数“call_fun”将存放于0x08000304处(留意此处的位置计数器将产生0x04的内存间隙)。

指定“文件”输出地址:

可以定义如下的memory和section,然后将指定的文件存放于该section,就能控制“文件”的输出地址。

左右滑动查看完整内容

 

 

/* Memories definition */
 MEMORY 
{
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 0x300 /* 0x08000000 ~ 0x080002FF */ 
    FW_FLASH (rx) : ORIGIN = 0x08000434, LENGTH = 0x2BCC/* 0x08000434 ~ 0x08003000 */ 
    RAM (xrw) : ORIGIN = 0x20000300, LENGTH = 0x900 /* 0x20000300 ~ 0x20001FFF */ 
}
/* Sections */ 
SECTIONS 
{
    … 
    .main_section : 
    {
        . = ALIGN(4); 
        Core/Src/main.o(.text*) 
        . = ALIGN(4); 
    } >FLASH 
    … 
}

 

 

示例中将main.o指定到FLASH区域中;更改FLASH的地址或者main_section的LMA,就可以实现将特定文件指定到特定内存区域。

案例:RZ/N2L把 .text, .data, .bss段从ATCM改到SYSTEM_RAM

这里描述的RZ/N2L的内存分配:

左右滑动查看完整内容

脚本

长按可保存查看大图

把.text段从ATCM改到SYSTEM_RAM:

左右滑动查看完整内容

脚本

长按可保存查看大图

把.data段从ATCM改到SYSTEM_RAM:

左右滑动查看完整内容

脚本

长按可保存查看大图

.bss段的改动也是类似的:

左右滑动查看完整内容

脚本

长按可保存查看大图

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分