小猫爪:嵌入式小知识04-IAR icf链接文件解析

1 前言

前一章我们提到了源文件最后变成了代码的过程,这一节我们详细得介绍一下在IAR中链接过程以及如何在IAR中自定义链接地址。

2 Section、Block、Region

介绍正题之前,我们得先了解一下Section、Block、Region的概念。

2.1 Section

《小猫爪:嵌入式小知识03-程序的组成、存储与运行(转自野火教程)》中我们说到了程序中的内容按照性质可以分成一下几个域:
在这里插入图片描述
所以代码在编译后就会按照上面的域分成一个个的块,也就是section,链接器链接过程中操作的基本单位就是section。IAR为了让操作更加简单和人性化,就按照以下性质:
在这里插入图片描述
以及程序功能作用规定了以下section:

Section Description
.bss Holds zero-initialized static and global variables.
CSTACK Holds the stack used by C or C++ programs.
.data Holds static and global initialized variables.
.data_init Holds initial values for .data sections when the linker directive initialize is used.
.exc.text Holds exception-related code.
HEAP Holds the heap used for dynamically allocated data.
_ _iar_tls.$$ DATA Holds initial values for TLS variables.
.iar.dynexit Holds the atexit table.
.init_array Holds a table of dynamic initialization functions.
.intvec Holds the reset vector table
IRQ_STACK Holds the stack for interrupt requests, IRQ, and exceptions.
.noinit Holds _ _no_init static and global variables.
.preinit_array Holds a table of dynamic initialization functions.
.prepreinit_array Holds a table of dynamic initialization functions.
.rodata Holds constant data.
.text Holds the program code.
.textrw Holds _ _ramfunc declared program code.
.textrw_init Holds initializers for the .textrw declared section.
Veneer$$CMSE Holds secure gateway veneers.

当然我们也可以自定义section,我们在IAR的c代码中可以使用以下语法定义自己的section:

#pragma location = "name" content

name为section名, content则为具体的变量或者函数。
具体操作后面会举例说明。

2.2 Block

Block是IAR为了更加方便的管理Section而定义出的一个概念,用户可以在链接文件中定义一个Block空间,指定对齐方式和大小。下面举个常见的操作例子:

/*这一部分则是定义一个__size_cstack__大小的block,8字节对齐, 其实这就是栈的大小*/
define block CSTACK    with alignment = 8, size = __size_cstack__   {
    
     };
/*这一部分则是定义了一个RW Block,并将 readwrite 和  m_usb_dma_init_data 这两个section放入 */
define block RW        {
    
     first readwrite, section m_usb_dma_init_data };

后面还会对其详细介绍。

2.3 Region

IAR使用Region来表示实际的存储区,通常需要通过实际的芯片映射存储地址来定义,我们则可以通过以下语法来定义Region。

define region 名字 = mem:[from 起始地址 to 终止地址];

3 IAR 链接文件

介绍完上面三个概念,接下里来介绍链接文件。

3.1 IAR链接过程

首先我们介绍一下IAR链接文件的流程:

①决定哪些模块需要包含到应用程序中。

②选择链接目标文件用到的标准库文件。

③决定目标文件哪些section需要包含进来。

④完成RAM中的代码和变量的初始化。初始化指令可以让链接器产生额外的代码能够copy ROM中的内容到RAM中。每个通过copy完成初始化的段被分成了两个段,一个在ROM,一个在RAM。如果没有人工初始化的话,链接器回自动产生启动代码来完成初始化。

⑤通过链接文件的放置指令来决定每个section存放的位置。

⑥生成最终的包含可执行映像和调试信息的最终文件。

⑦最后生成map文件,包括了各个section的的在存储器中的最终地址,global symbol的地址和用到的存储器和库汇总。

其实除了第五步,我们经常要人为干预外(可以灵活的配置相关数据的运行区域,比如有段程序需要其非常快的执行或者有段数据需要经常得访问,那么我们则可以将其放置在RAM中,提供执行速度),其他的部分一般都是IAR自动完成的。

3.2 链接文件的组成

在第五步中,链接器会根据链接文件中的内容来决定每一个section的位置,那么一个链接文件中具体会包含下面这些内容:

  • 定义可用的可编址空间(memory)
  • 定义ROM或RAM的可用内存区域(region)
  • 定义Block 定义应用程序初始化
  • 放置section
  • 使用symbols, expressions, and numbers

3.3 链接文件的常见指令

(这是我直接抄过来的:《解析IAR的ILINK链接器icf配置文件》

下面让我们了解一下基本指令:

(1)define [ exported ] symbol name = expr;
作用:指定某个符号的值。

参数 描述
exported 导出该symbol,使其对可执行镜像可用
name 符号名
expr 符号值

举例:

define symbol RAM_START_ADDRESS = 0x40000000;  /*  定义 RAM 起始地址  */ 
define symbol RAM_END_ADDRESS  = 0x4000FFFF;  /*  定义 RAM 结束地址  */

(2)define memory name with size = expr [, unit-size];
作用:定义一个可编址的存储地址空间(memory)。

参数 描述
name memory的名称
expr 地址空间的大小
unit-size expr的单位,可以是位(unitbitsize),缺省是字节(unitbytesize)

举例:

define memory MEM with size = 4G;

(3)define region name = mem:[from address_start to address_end];
作用:定义一个存储地址区域(region)。一个区域可由一个或多个范围组成,每个范围内地址必须连续,但几个范围之间不必是连续的。
参数:

参数 描述
name region的名称
mem 存储器名字(一般由define memory定义)
address_start 起始地址
address_end 终止地址

举例:

define region ROM = MEM:[from 0x0 to 0xFFFF]; 
    /* 定义 ROM region,位于地址空间MEM 中,起始地址为0x0,大小为0x10000 字节  */ 
define region ROM = MEM:[from 0x0 to 0xFFFF]; 
    /* 定义 ROM region,位于地址空间MEM 中,起始地址为0x0,结束地址为0xFFFF */

(4)define block name [ with param, param… ] { extended-selectors };
作用:定义一个地址块(block);它可以是个只保留指定大小的地址空间的空块,比如栈、堆;也可以包含一系列的sections,由extended-selectors 选择。

参数 描述
name block 的名称
param size = expr (块的大小)
maximum size = expr (块大小的上限)
alignment = expr (最小对齐字节数)
fixed order (按照固定顺序放置sections)
extended-selector [ first / last ] { section-selector / block name / overlay name }

其中:
first:最先存放;
Last:最后存放
section-selector: [ section-attribute ][ section sectionname ][object filename ]
section-attribute: [ readonly [ code | data ] | readwrite [ code | data ] | zeroinit ]

其中:
sectionname : section的名称
filename : 目标文件的名称
name : block或overlay的名称

注:这里可以按照section的属性,名称及其所在目标文件这三个过滤条件中,任意选取一个条件或多个条件进行组合,来圈定所要求的sections。

define block HEAP with size = 0x1000, alignment = 4 {
    
     }; 
    /* 定义 HEAP block,大小为0x1000,4 字节对齐,没有内容  */ 
define block MYBLOCK1 = {
    
     section mysection1, section mysection2, readwrite }; 
     /* 定义 MYBLOCK1 block,含有mysection1,mysection2,以及所有readwrite 属性的sections */ 
define block MYBLOCK2 = {
    
     readwrite object myfile2.o }; 
    /* 定义 MYBLOCK2 block,含有目标文件myfile2.o 中所有readwrite 属性的sections */ 
define block MYBLOCK3 = {
    
     readonly code object myfile3.o }; 
    /* 定义 MYBLOCK3 block,含有目标文件myfile3.o 中所有readonly 属性的code sections */ 

(5)initialize { by copy | manually } [ with param, param… ] { section-selectors };
作用:初始化sections

参数 描述
by copy 在程序启动时自动执行初始化
manually 在程序启动时不自动执行初始化
param packing = { none/compress1 / compress2 /auto } copy routine = functionname
packing表示是否压缩数据,缺省是auto functionname表示是否使用自己的拷贝函数来取代缺省的拷贝函数
section-selectors 同上

举例:

initialize by copy {
    
     readwrite };  /*  在启动时初始化所有属性为 readwrite 的sections */ 

(6)do not initialize { section-selectors };
作用:规定在程序启动时不需要初始化的sections;一般用于__no_init 声明的变量段(.noinit)
section-selectors :

参数 描述
section-selectors 同上

举例:

do not initialize {
    
     .noinit };  /*  在启动时不要初始化.noinit section */ 

(7)place at { address memory [:expr] | start of region_expr | end of region_expr } { extended-selectors };
作用: 把section 或 block 放置在某个具体的起始地址处,或者一个 region 的开始或结束处

参数 描述
memory memory 的名称
expr 地址值,该地址必须在 memory 所定义的范围内
region_expr region 的名称
extended-selector 同上

举例:

place at end of ROM {
    
     section .checksum };                /*  把.checksum 放在 ROM region 的最后  */ 
place at address MEM:0x0 {
    
     section .intvec };             /*  把.intvec 放在地址 0x0 */ 
place at address MEM:0x1000 {
    
     section .text object myfile.o };  /* the .text section of myfile.o */ 
place at address MEM:0x1000 {
    
     readonly object myfile.o };      /* all read-only sections of myfile.o */ 
place at address MEM:0x1000 {
    
     readonly data object myfile.o }; /* all read-only data sections of myfile.o */ 

(8)
place in region-expr { extended-selectors };
作用:把section 或 block (按任意顺序)放置在某个region 中

参数 描述
region-expr region 的名称
extended-selector 同上

举例:

place in ROM {
    
     readonly };               /* all readonly sections */ 
place in RAM {
    
     readwrite };              /* all readwrite sections */ 
place in RAM {
    
     block HEAP, block CSTACK, block IRQ_STACK };  /* heap and stacks */ 
place in ROM {
    
     section .text object myfile.o }; /* the .text section of myfile.o */ 
place in ROM {
    
     readonly object myfile.o }; /* all read-only sections of myfile.o */ 
place in ROM {
    
     readonly data object myfile.o }; /* all read-only data sections myfile.o */ 

3.4 实战演练

下面我们以RT工程的一个链接文件做一个详细的解释:

/*第一部分*/
define symbol __ram_vector_table_size__        =  isdefinedsymbol(__ram_vector_table__) ? 0x00000400 : 0;
define symbol __ram_vector_table_offset__      =  isdefinedsymbol(__ram_vector_table__) ? 0x000003FF : 0;

define symbol m_interrupts_start       = 0x60002000;
define symbol m_interrupts_end         = 0x600023FF;

define symbol m_text_start             = 0x60002400;
define symbol m_text_end               = 0x63FFFFFF;

define symbol m_interrupts_ram_start   = 0x20000000;
define symbol m_interrupts_ram_end     = 0x20000000 + __ram_vector_table_offset__;

define symbol m_data_start             = m_interrupts_ram_start + __ram_vector_table_size__;
define symbol m_data_end               = 0x2001FFFF;

define symbol m_data2_start            = 0x20200000;
define symbol m_data2_end              = 0x2023FFFF;

define symbol m_data3_start            = 0x80000000;
define symbol m_data3_end              = 0x81DFFFFF;

define symbol m_ncache_start           = 0x81E00000;
define symbol m_ncache_end             = 0x81FFFFFF;

define exported symbol m_boot_hdr_conf_start = 0x60000000;
define symbol m_boot_hdr_ivt_start           = 0x60001000;
define symbol m_boot_hdr_boot_data_start     = 0x60001020;
define symbol m_boot_hdr_dcd_data_start      = 0x60001030;

if (isdefinedsymbol(__stack_size__)) {
    
    
  define symbol __size_cstack__        = __stack_size__;
} else {
    
    
  define symbol __size_cstack__        = 0x0400;
}

if (isdefinedsymbol(__heap_size__)) {
    
    
  define symbol __size_heap__          = __heap_size__;
} else {
    
    
  define symbol __size_heap__          = 0x0400;
}

define exported symbol __NCACHE_REGION_START   = m_ncache_start;
define exported symbol __NCACHE_REGION_SIZE    = m_ncache_end - m_ncache_start + 1;

define exported symbol __VECTOR_TABLE          = m_interrupts_start;
define exported symbol __VECTOR_RAM            = isdefinedsymbol(__ram_vector_table__) ? m_interrupts_ram_start : m_interrupts_start;
define exported symbol __RAM_VECTOR_TABLE_SIZE = __ram_vector_table_size__;

/*第二部分*/
define memory mem with size = 4G;
define region TEXT_region = mem:[from m_interrupts_start to m_interrupts_end]
                          | mem:[from m_text_start to m_text_end];
define region DATA_region = mem:[from m_data_start to m_data_end];
define region DATA2_region = mem:[from m_data2_start to m_data2_end];
define region DATA3_region  = mem:[from m_data3_start to m_data3_end-__size_cstack__];
define region CSTACK_region = mem:[from m_data3_end-__size_cstack__+1 to m_data3_end];
define region NCACHE_region = mem:[from m_ncache_start to m_ncache_end];

/*第三部分 */
define block CSTACK    with alignment = 8, size = __size_cstack__   {
    
     };
define block HEAP      with alignment = 8, size = __size_heap__     {
    
     };
define block RW        {
    
     first readwrite, section m_usb_dma_init_data };
define block ZI         with alignment = 32  {
    
     first zi, section m_usb_dma_noninit_data };
define block NCACHE_VAR {
    
     section NonCacheable , section NonCacheable.init };

/*第四部分 */
initialize by copy {
    
     readwrite, section .textrw };
do not initialize  {
    
     section .noinit };

/*第五部分 */
place at address mem: m_interrupts_start    {
    
     readonly section .intvec };
place at address mem:m_boot_hdr_conf_start {
    
     section .boot_hdr.conf };
place at address mem:m_boot_hdr_ivt_start {
    
     section .boot_hdr.ivt };
place at address mem:m_boot_hdr_boot_data_start {
    
     readonly section .boot_hdr.boot_data };
place at address mem:m_boot_hdr_dcd_data_start {
    
     readonly section .boot_hdr.dcd_data };

/*第六部分 */
keep{
    
     section .boot_hdr.conf, section .boot_hdr.ivt, section .boot_hdr.boot_data, section .boot_hdr.dcd_data };

/*第七部分 */
place in TEXT_region                        {
    
     readonly };
place in DATA3_region                       {
    
     block RW };
place in DATA3_region                       {
    
     block ZI };
if (isdefinedsymbol(__heap_noncacheable__)) {
    
    
  place in NCACHE_region                    {
    
     last block HEAP };
} else {
    
    
  place in DATA3_region                     {
    
     last block HEAP };
}
place in CSTACK_region                      {
    
     block CSTACK };
place in NCACHE_region                      {
    
     block NCACHE_VAR };

(1)第一部分主要是在定义各种符号的值,然后给下面的调用。
(2)第二部分则是定义了很多region,将RT整个存储空间分成了好几个区域。
(3)第三部分则是定义很多block。
(4)第四部分则是定义哪些section需要初始化,哪些不需要初始化。
(5)第五部分则是将部分section放入对应的地址中。
(6)第六部分则是保持住头信息数据,避免这部分数据被IAR链接器优化。
(7)第七部分则是将第三部分定义block放入相应的region中。

大家结合前面的命令语法,好好对照,非常简单。。。

(附加:有的小伙伴可能又要问了,上面你指定了自定义的sectio的位置,那那些没有指定名称的section是怎么放的?

IAR在编译过程中,没有自定义名称的section则是会按照其属性划分为三种:readonly,readwrite,zi,(也就是上面介绍语法提到的section-attribute种类)所以在icf文件中,这三种类别则是代表了那些没有名字的section,仔细研究可以看到readonly放在了ROM区,readwrite和zi放在了RAM区.)

对于icf文件,还有更加细致的应用介绍,大家请参考我的另外一篇文章:《小猫爪:嵌入式小知识05-IAR icf链接文件操作-链接代码至RAM》

END

猜你喜欢

转载自blog.csdn.net/Oushuwen/article/details/109243294