链接脚本(LinkerScript)用法解析(一)关键字SECTIONS与MEMORY
链接脚本(LinkerScript)⽤法解析(⼀)关键字SECTIONS与MEMORY
1.MEMORY关键字⽤于描述⼀个MCU ROM和RAM的内存地址分布(Memory Map),MEMORY中所做的内存描述主要⽤于SECTIONS中LMA和VMA的定义。
2.SECTIONS关键字⽤于定义output section(输出段)的相应input section(输⼊段)、LMA和VMA,是整个连接脚本中最为重要的部分。注:output section 是实际存储在内存中的“段”,⽽input section是其构成成员,如.data为数据段,由所有全局变量构成(默认情况下);.text为代码段,由所有函数构成(默认情况下)...
3.下⾯我们⾸先来介绍MEMORY的语法,MEMORY的语法格式如下:
MEMORY
{
  <name> [(<attr>)] : ORIGIN = <origin>, LENGTH = <len>
  ...
}
其中<name>是所要定义的内存区域的名字,<origin>是其起始地址,<len>为内存区域的⼤⼩。另外,<attr>是可选的,并不重要,具体⽤法可参考GNU Linker 的语法说明。
MEMORY的⼀个具体使⽤实例如下:
MEMORY
{
  rom (rx) : ORIGIN = 0, LENGTH = 256K  // MEMORY语法中可以使⽤如K、M和G这样的内存单位
  ram (!rx) : org = 0x40000000, l = 4M  // ORIGIN可以写为org,⽽LENGTH可以写为l
}
4.在介绍SECTIONS的⽤法之前,我们先对之前提到的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处寻它。
6.现在我们可以开始介绍SECTIONS了,SECTIONS的语法如下:
SECTIONS
{
  <sections−command>
  <sections−command>
  ...
}
其中主要的部分是<sections−command>,⽽SECTIONS{ }属于框架。<sections−command>的语法如下:
<section> [<address>] [(<type>)] : [AT(<lma>)]
{
  <output−section−command>
  <var{output−section−command>
  ...
} [><region>] [AT><lma region>] [:<phdr> :<phdr> ...] [=<fillexp>]
我们从使⽤的⾓度来讲解其语法:(假设有⼀个全局变量myData,我们⽤#pragma section命令将其定义为.myData段(input section))
(1)我们⾸先可以定义output section的名字,随便什么都可以,⽐如.my_data;
(2)然后我们可以定义其构成成员,*(.myData);
(3)接下来我们就要指定.my_data的LMA和VMA了,有4种⽅法:
  a) [<address>] + [AT(<lma>)];
  b) [<address>] + [AT><lma region>];
  c) [><region>] + [AT><lma region>];
  d) [><region>] + [AT(<lma>)].
  但是要注意这些⽤法的不同:[<address>] 和 [AT(<lma>)]必须指定具体的地址,⽽ [><region>] 和 [AT><lma region>]只需指定内存空间,具体地址紧接着上⼀个output section的末尾地址。
  经过以上步骤,我们得出如下section定义:(这⾥只列出2种)
SECTIONS
{
  .my_data ( 0xD0004000 ) : AT ( 0x80000020 )
  {
    *(.myData)
  }
  ...
}
SECTIONS
{
  .my_data :
  {
    *(.myData)
  } > ram AT> rom
  ...
}
  以上为了说明SECTION的语法,使⽤了全局变量这种LMA和VMA不同的例⼦。⽽对于代码段.text这种LMA与VMA相同的情况,由于默认情况下
LMA=VMA,因此可以只定义VMA⽽不必指明LMA,例如:
.text :
{
*(.text)
*(.text.*)
. = ALIGN(4);
} > pfls0
7.最后,我们有必要提及“.”这个符号(不是.text、.data中的".",⽽是如上例中.=ALIGN(4);中的"."),以下介绍来⾃于HighTec编译器⼿册
8.最后的最后,附上英飞凌TC2xx 的Linker file ,其中的语法和⽤法值得深⼊的去学习,可以极⼤地加深对链接脚本的理解。由于本篇博⽂着重介绍链接脚本中最重要的SECTIONS 关键字的使⽤,⼤家可能在下⾯的链接脚本⽰例中碰到很多本⽂未提及的语法,不过这些都可以在第19章中到。
/* Default linker script, for normal executables */
OUTPUT_FORMAT("elf32-tricore")
OUTPUT_ARCH(tricore)
ENTRY(_START)
__TRICORE_DERIVATE_MEMORY_MAP__ = 0x270;
LCF_CSA0_SIZE = 8k;
LCF_USTACK0_SIZE = 2k;
LCF_ISTACK0_SIZE = 1k;
LCF_CSA1_SIZE = 8k;
LCF_USTACK1_SIZE = 2k;
LCF_ISTACK1_SIZE = 1k;
LCF_CSA2_SIZE = 8k;
LCF_USTACK2_SIZE = 2k;
LCF_ISTACK2_SIZE = 1k;
LCF_HEAP_SIZE = 4k;
LCF_DSPR2_START = 0x50000000;
LCF_DSPR2_SIZE = 120k;
LCF_DSPR1_START = 0x60000000;
LCF_DSPR1_SIZE = 120k;
LCF_DSPR0_START = 0x70000000;
LCF_DSPR0_SIZE = 112k;
LCF_CSA2_OFFSET    =    (LCF_DSPR2_SIZE - 1k - LCF_CSA2_SIZE);
LCF_ISTACK2_OFFSET =    (LCF_CSA2_OFFSET - 256 - LCF_ISTACK2_SIZE);
LCF_USTACK2_OFFSET =    (LCF_ISTACK2_OFFSET - 256 - LCF_USTACK2_SIZE);
LCF_CSA1_OFFSET    =    (LCF_DSPR1_SIZE - 1k - LCF_CSA1_SIZE);
LCF_ISTACK1_OFFSET =    (LCF_CSA1_OFFSET - 256 - LCF_ISTACK1_SIZE);
LCF_USTACK1_OFFSET =    (LCF_ISTACK1_OFFSET - 256 - LCF_USTACK1_SIZE);
LCF_CSA0_OFFSET    =    (LCF_DSPR0_SIZE - 1k - LCF_CSA0_SIZE);
LCF_ISTACK0_OFFSET =    (LCF_CSA0_OFFSET - 256 - LCF_ISTACK0_SIZE);
LCF_USTACK0_OFFSET =    (LCF_ISTACK0_OFFSET - 256 - LCF_USTACK0_SIZE);
LCF_HEAP0_OFFSET =    (LCF_USTACK0_OFFSET - LCF_HEAP_SIZE);
LCF_HEAP1_OFFSET =    (LCF_USTACK1_OFFSET - LCF_HEAP_SIZE);
LCF_HEAP2_OFFSET =    (LCF_USTACK2_OFFSET - LCF_HEAP_SIZE);
LCF_INTVEC0_START = 0x801F4000;
LCF_TRAPVEC0_START = 0x80000100;
LCF_TRAPVEC1_START = 0x801F6800;
LCF_TRAPVEC2_START = 0x801F6000;
RESET = 0x80000020;
MEMORY
{
dsram2_local (w!xp): org = 0xd0000000, len = 120K
dsram2 (w!xp): org = 0x50000000, len = 120K
psram2 (w!xp): org = 0x50100000, len = 24K
dsram1_local (w!xp): org = 0xd0000000, len = 120K
dsram1 (w!xp): org = 0x60000000, len = 120K
psram1 (w!xp): org = 0x60100000, len = 24K
dsram0_local (w!xp): org = 0xd0000000, len = 112K
dsram0 (w!xp): org = 0x70000000, len = 112K
psram0 (w!xp): org = 0x70100000, len = 24K
psram_local (w!xp): org = 0xc0000000, len = 24K
pfls0 (rx!p): org = 0x80000000, len = 2M
pfls0_nc (rx!p): org = 0xa0000000, len = 2M
pfls1 (rx!p): org = 0x80200000, len = 2M        /*Not used to allocate and sections*/
pfls1_nc (rx!p): org = 0xa0200000, len = 2M    /*Not used to allocate and sections*/
dfls0 (rx!p): org = 0xaf000000, len = 384K
lmuram (w!xp): org = 0x90000000, len = 32K
lmuram_nc (w!xp): org = 0xb0000000, len = 32K
edmem (w!xp): org = 0x9f000000, len = 1M
edmem_nc (w!xp): org = 0xbf000000, len = 1M
}
/* map local memory address to a global address */
REGION_MAP( CPU0 , ORIGIN(dsram0_local), LENGTH(dsram0_local), ORIGIN(dsram0)) REGION_MAP( CPU1 , ORIGIN(dsram1_local), LENGTH(dsram1_local), ORIGIN(dsram1)) REGION_MAP( CPU2 , ORIGIN(dsram2_local), LENGTH(dsram2_local), ORIGIN(dsram2))
/*Un comment one of the below statements to enable CpuX DMI RAM to hold global variables*/ /*REGION_ALIAS( default_ram , dsram0)*/
REGION_ALIAS( default_ram , dsram1)
/*REGION_ALIAS( default_ram , dsram2)*/
CORE_ID = GLOBAL ;
SECTIONS
{
/*This section is always required as Boot mode header 0 address absolutely restricted at address 0x80000000*/
.bmhd_0 (0x80000000) : FLAGS(arl)
{
BootModeHeader0 = .;
KEEP (*(.bmhd_0))
} > pfls0
/*This section is always required as Boot mode header 1 address absolutely restricted at address 0x80020000*/
.bmhd_1 (0x80020000) : FLAGS(arl)
{
BootModeIndex = .;
KEEP (*(.bmhd_1));
} > pfls0
/*This section is always required as user start address absolutely restricted at address 0x80000020*/
.startup (0x80000020) : FLAGS(rxl)
{
BootModeIndex = .;
. = ALIGN(4);
KEEP (*(.start));
. = ALIGN(4);
} > pfls0 =0x800
/
*This section contains the data indirection pointers to interface external devices*/
.interface_const (0x80000040) :
{
__IF_CONST = .;
KEEP (*(.interface_const));
. = ALIGN(4);
} > pfls0
.traptab_tc0 (LCF_TRAPVEC0_START) :
{
PROVIDE(__TRAPTAB_CPU0 = .);
KEEP (*(.traptab_cpu0));
} > pfls0
.zrodata : FLAGS(arl)
{
*(.zrodata)
*(.zrodata.*)
} > pfls0
.sdata2 : FLAGS(arsl)
{
*(.srodata)
*(.srodata.*)
} > pfls0
_SMALL_DATA2_ = SIZEOF(CORE_SEC(.sdata2)) ? ADDR(CORE_SEC(.sdata2)) + 32k : (ADDR(CORE_SEC(.sdata2)) & 0xF0000000) + 32k ;    __A1_MEM = _SMALL_DATA2_;
.rodata : FLAGS(arl)
{
*(.rodata)
*(.rodata.*)
*(.*)
/*
* Create the clear and copy tables that tell the startup code
* which memory areas to clear and to copy, respectively.
*/
. = ALIGN(4) ;
PROVIDE(__clear_table = .) ;
LONG(0 + ADDR(.CPU2.zbss));    LONG(SIZEOF(.CPU2.zbss));
LONG(0 + ADDR(.CPU2.bss));    LONG(SIZEOF(.CPU2.bss));
LONG(0 + ADDR(.CPU1.zbss));    LONG(SIZEOF(.CPU1.zbss));
LONG(0 + ADDR(.CPU1.bss));    LONG(SIZEOF(.CPU1.bss));
LONG(0 + ADDR(.CPU0.zbss));    LONG(SIZEOF(.CPU0.zbss));
LONG(0 + ADDR(.CPU0.bss));    LONG(SIZEOF(.CPU0.bss));
LONG(0 + ADDR(.zbss));    LONG(SIZEOF(.zbss));
LONG(0 + ADDR(.sbss));    LONG(SIZEOF(.sbss));
LONG(0 + ADDR(.bss));    LONG(SIZEOF(.bss));
LONG(0 + ADDR(.sbss4));    LONG(SIZEOF(.sbss4));
LONG(0 + ADDR(.bss_emem));    LONG(SIZEOF(.bss_emem));
LONG(-1);                LONG(-1);
PROVIDE(__copy_table = .) ;
LONG(LOADADDR(.CPU2.zdata));    LONG(0 + ADDR(.CPU2.zdata));    LONG(SIZEOF(.CPU2.zdata));
LONG(LOADADDR(.CPU2.data));    LONG(0 + ADDR(.CPU2.data));    LONG(SIZEOF(.CPU2.data));
LONG(LOADADDR(.CPU1.zdata));    LONG(0 + ADDR(.CPU1.zdata));    LONG(SIZEOF(.CPU1.zdata));
LONG(LOADADDR(.CPU1.data));    LONG(0 + ADDR(.CPU1.data));    LONG(SIZEOF(.CPU1.data));
LONG(LOADADDR(.CPU0.zdata));    LONG(0 + ADDR(.CPU0.zdata));    LONG(SIZEOF(.CPU0.zdata));
LONG(LOADADDR(.CPU0.data));    LONG(0 + ADDR(.CPU0.data));    LONG(SIZEOF(.CPU0.data));
LONG(LOADADDR(.zdata));    LONG(0 + ADDR(.zdata));    LONG(SIZEOF(.zdata));
LONG(LOADADDR(.sdata));    LONG(0 + ADDR(.sdata));    LONG(SIZEOF(.sdata));
LONG(LOADADDR(.data));    LONG(0 + ADDR(.data));    LONG(SIZEOF(.data));
LONG(LOADADDR(.data_emem));    LONG(0 + ADDR(.data_emem));    LONG(SIZEOF(.data_emem));
LONG(LOADADDR(.data_lmu));    LONG(0 + ADDR(.data_lmu));    LONG(SIZEOF(.data_lmu));
LONG(LOADADDR(.sdata4));    LONG(0 + ADDR(.sdata4));    LONG(SIZEOF(.sdata4));
LONG(LOADADDR(.CPU0.psram_text));    LONG(0 + ADDR(.CPU0.psram_text));    LONG(SIZEOF(.CPU0.psram_text));        LONG(LOADADDR(.CPU1.psram_text));    LONG(0 + ADDR(.CPU1.psram_text));    LONG(SIZEOF(.CPU1.psram_text));        LONG(LOADADDR(.CPU2.psram_text));    LONG(0 + ADDR(.CPU2.psram_text));    LONG(SIZEOF(.CPU2.psram_text));        LONG(-1);                LONG(-1);                LONG(-1);
. = ALIGN(8);
} > pfls0
.text  : FLAGS(axl)
{rom是什么
*(.text)
*(.text.*)
*(.*)
*(.gnu.warning)        /* .gnu.warning sections are handled specially */
. = ALIGN(4);
} > pfls0
/*
* C++ exception handling tables.  NOTE: gcc emits .eh_frame
* sections when compiling C sources with debugging enabled (-g).
* If you can be sure that your final application consists
* exclusively of C objects (i.e., no C++ objects), you may use
* the -R option of the "strip" and "objcopy" utilities to remove
* the .eh_frame section from the executable.
*/
.eh_frame  :
{
*(.gcc_except_table)
__EH_FRAME_BEGIN__ = . ;
KEEP (*(.eh_frame))
__EH_FRAME_END__ = . ;
. = ALIGN(8);
} > pfls0
/*
* Constructors and destructors.
*/
.ctors : FLAGS(ar)
{
__CTOR_LIST__ = . ;
LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2);
*(.ctors)
LONG(0) ;
__CTOR_END__ = . ;
. = ALIGN(8);
} > pfls0
.dtors : FLAGS(ar)
{
__DTOR_LIST__ = . ;
LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2);
*(.dtors)
LONG(0) ;
__DTOR_END__ = . ;
. = ALIGN(8);
} > pfls0
.traptab_tc2 (LCF_TRAPVEC2_START) :
{
PROVIDE(__TRAPTAB_CPU2 = .);
KEEP (*(.traptab_cpu2));
} > pfls0
.traptab_tc1 (LCF_TRAPVEC1_START) :
{
PROVIDE(__TRAPTAB_CPU1 = .);
KEEP (*(.traptab_cpu1));

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。