link脚本组成

1. 链接脚本文件主要有什么内容呢? 

为了规范,我们分为三个部分:

(1)链接配置(可有可无)

(2)内存布局定义

(3)段链接定义

1.1 链接配置(可有可无)

如一些符号变量的定义、入口地址、输出格式等
 

STACK_SIZE = 0X200; 
OUTPUT_FORMAT(elf32-littlearm)
OUTPUT_ARCH(arm)
ENTRY(_start) 

1.2 内存布局定义

脚本中以MEMORY命令定义了存储空间,其中以ORIGIN定义地址空间的起始地址,
LENGTH定义地址空间的长度。

link脚本组成_第1张图片

1.3 段链接定义

最简单的链接脚本只有一个命令:’SECTIONS ’ 。 # 您可以使用 ’SECTIONS ’ 命令来描述输出文件的内存布局。
’SECTIONS ’ 命令功能非常强大。 在这里,我们将描述它的一个简单用法。 假设您的程序仅包含代码,初始化数据和未初始化数据。
它们分别位于“ .text ”,“.data ”和“ .bss ”段中。 我们进一步假设这些是唯一将会出现在输入文件中的段。

脚本中以SECTIONS命令定义一些段(text、data、bss等段)链接分布。

SECTIONS
{
    .text :
    {
      *(.text*)
    } > FLASH
}
.text段即代码段,* (.text*)指示将工程中所有目标文件的.text段链接到FLASH中


在此示例中,假设代码应在地址 0x10000 处加载,数据应从地址 0x8000000 开始。下面的链接脚本将会执行如下操作:

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

2. 常用关键字和命令

2.1 MEMORY命令

使用MEMORY来定义内存如下:
MEMORY {
NAME1 [(ATTR)] : ORIGIN = ORIGIN1, LENGTH = LEN2
NAME2 [(ATTR)] : ORIGIN = ORIGIN2, LENGTH = LEN2
…
}
NAME :存储区域的名字。(自己可以随意命名)

ATTR :定义该存储区域的属性。ATTR属性内可以出现以下7 个字符:

R 只读section
W 读/写section
X 可执行section
A 可分配的section
I 初始化了的section
L 同I
! 不满足该字符之后的任何一个属性的section

ORIGIN :关键字,区域的开始地址,可简写成org 或o
LENGTH :关键字,区域的大小,可简写成len 或l

MEMORY
{
rom (rx) : ORIGIN = 0, LENGTH = 256K
ram (!rx) : org = 0×40000000, l = 4M
}

2.2 定位符号‘.’的使用

‘.’表示当前地址,= 等号代表赋值,它可以被赋值也可以赋值给某个变量。
如下为将当前地址赋值给某个变量(链接器链接是按照SECTIONS里的段顺序排列的,前面的排列完之后就能计算出当前地址)

RAM_START = .;

如下为将段存放在特定的地址中:
SECTIONS
{
    . = 0×10000;
    .text : 
    { 
        *(.text)
    }
    
    . = 0×8000000;
    .data : 
    { 
        *(.data) 
    }
}

“. = 0×10000;”该语句表示将当前地址设置为0x10000。如上代码中,意思是将所有
目标文件的text段从0x10000地址开始存放。

2.3 SECTIONS 命令

SECTIONS基本的命令语法如下:

SECTIONS
{
       ...
      secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
      { 
        contents 
      } >region :phdr =fill
      ...
}

这么多参数中,只有secname 和 contents 是必须的,即可简写成:

SECTIONS
{
       ...
      secname :
      { 
        contents 
      } 
      ...
}

链接脚本本质就是描述输入和输出。secname表示输出文件的段,即输出文件中有哪些段。
而contents就是描述输出文件的这个段从哪些文件里抽取而来,即输入文件,一般就是目标文件之类的。
如下,将目标文件的数据段存放在输出文件的数据段(段名自己定义,段名前后必须要有空格)

SECTIONS
{
       ...
      .data :
      { 
        main.o(.data)
        *(.data)
      } 
      ...
}

其中 *(.data) 表示将所有的目标的.data段链接到输出文件.data段中, 特别注意的是,
之前链接的就不会再链接,这样做的目的是可以将某些特殊的目标文件链接到地址前面。

我们继续来讲一下其他参数。
start :表示将某个段强制链接到的地址。
AT(addr):实现存放地址和加载地址不一致的功能,AT表示在文件中存放的位置,而在内存里呢,按照普通方式存储。
region:这个region就是前面说的MEMORY命令定义的位置信息。

2.4 PROVIDE关键字

该关键字定义一个(目标文件内被引用但没定义)符号。相当于定义一个全局变量的符号表
其他C文件可以通过该符号来操作对应的存储内存

SECTIONS
{
    .text :
    {
        *(.text)
        _etext = .;
        PROVIDE(etext = .);
    }
}

如上,在链接脚本中声明了_etext 符号。特别注意的是_etext 只是一个符号,没有存储内存,
并不是一个变量,该符对应(映射)的是一个地址,,其地址为.text section之后的第一个字
节的地址。C文件中引用用法如下。

int main()
{
    //引用该变量
    extern char  _etext;
    char *p = &_etext;
    //...
}

若在链接脚本中 " _etext = 0x100; ",即表示符号_etext对应的地址为0X100, 此时 
& _etext的值为 0x100, char a= *p;表示为 从0X100地址取值存储的值赋值给变量a。

2.5 KEEP关键字

在连接命令行内使用了选项–gc-sections后,连接器可能将某些它认为没用的section过滤掉,此时就有必要强制连接器保留一些特定的 section,可用KEEP()关键字达此目的。如KEEP(* (.text))或KEEP(SORT(*)(.text))。说的通俗易懂就是:防止被优化

2.6 ALIGN 关键字

表示字节对齐, 如 “ . = ALIGN(4);”表示从该地址开始后面的存储进行4字节对齐。

2.7 SORT 关键字

可以用SORT()关键字对满足字符串模式的所有名字进行递增排序,如SORT(.text*)。

你可能感兴趣的:(编译链接,c语言)