与 等同

[(type)]  关键字 的定义和使用 : 

FILL 关键字 用于内存对齐产生的空位填充,图例用0x90字节填充空位

 /DISCARD/ 关键字 用以丢弃部分不要的输入段

 

[address] 是VMA的地址值, AT(中的值)是LMA的地址值;AT(lma) 等同于AT>lma_region; 若既没有指定VMA也没指定LMA,则认为VMA=LMA,且值为LMA的值

.mdata段的vma是0x2000,lma是AT表达式的值,即0x1000+SIZEOF(.text)

ADDR 关键字 指示一个段的绝对VMA值; LOADADDR (.text0) 关键字 指示一个段的绝对LMA值

   空位用0x90909090填充

KEEP 关键字 的作用是标记那些(垃圾)段不应该被删除

LONG  关键字 用于在rom/ram中分配4字节的空间,并在这4字节的地址内存入(中)表达式的值,同时使本地计数器+4;  可以将各段的起始地址和长度信息等存入ROM中

 

 PROVIDE 关键字 对外提供一个符号symbol,其值等于 expression 的计算结果;这个symbol是在ld文件中定义的,而非其他文件中

 EXCLUDE_FILE 关键字 用于汇总 除(内)文件外 的其余所有 .ctors 输入段

符号的定义和赋值可以在 SECTIONS 的 {内/外} 进行,但本地计数器 . 的赋值操作只能在SECTIONS 的 {内} 进行

extern 了3个链接器符号变量,其数值会在ld文件中赋值, 一切皆文件,符号即地址; 在makefile,ld脚本,汇编代码中出现的符号都是地址数值的代名词

 查看各输入段信息

任何后缀的文件都可被 INCLUDE 进来

.data段存放在REGION_DATA的存储区域中
.data段在REGION_DATA的存储区域中的存放起始地址是rodata_end标号的值,AT 指示段的LMA地址

 .bss段相对于.data段的优势在于.bss段不占用ROM空间;  .rodata段相对于.data段的优势在于.rodata段不占用RAM空间,rodata段即用const声明的.data段,而非.text段

SECTIONS是关键词,定义了4个输出段,输出段的名字分别为 : 前的字符串,这些输出段中分别放置 ( 内 ) 的输入段,而这些输入段又是各c文件编译为.o文件后生成的各段。

输出段 来告诉链接器如何在内存中布局您的程序; 输入段 描述来告诉链接器如何将输入文件映射到内存(rom and ram)布局中。

SECTIONS语句 { 内的 . } 表示绝对地址,输出段.data语句 { 内的 . } 表示从该段开始的字节偏移量,而不是绝对地址;包括表达式也是如此,即:

出现在输出段定义中的表达式值是相对于输出段基地址的偏移值,而在在其他地方出现的表达式值将是绝对值。可通过ABSOLUTE关键字将输出段中的相对偏移值转换为绝对值

第一个 { } 解读为 最终目标文件中一个名为 .text 的输出段中,囊括了所有源文件中的.text段,而这些 .text段 作为目标文件的输入段。 而这个 .text 的输出段 被放置( > )在 REGION_TEXT 区域中。

第二个 { } 中出现了对链接器变量 rodata_end 的赋值

  attr 的定义

MEMORY 关键字定义芯片内各存储区域的起始地址和长度, REGION_ALIAS 关键字 为这些存储区起了别名

 参考:https://www.404bugs.com/index.php/details/1084978780534788096

    https://www.cnblogs.com/uestcliming666/p/11456217.html

 

1:每个output section都有一个LMA和一个VMA,LMA加载地址,往哪里存放;VMA虚拟地址,在哪里使用;LMA是其存储地址(即.data数据存储在fls中的地址),而VMA是其运行时地址(加载到ram中的地址),例如将全局变量g_Data所在数据段.data的LMA设为0x80000020(属于ROM地址),VMA设为0xD0004000(属于RAM地址),那么g_Data的值将存储在ROM中的0x80000020处,而程序运行时,用到g_Data的程序会到RAM中的0xD0004000处寻找它。

 

2:ENTRY(Reset_Handler) //设置入口地址,ENTRY是 LD文件的关键字

  HEAP_SIZE = DEFINED(__heap_size__) ? __heap_size__ : 0x0400; //设置堆大小 ;DEFINED是 LD文件的关键字,类似ifdef

 

3:SECTION { 中 } AT后面的都是LMA地址/区域,不带AT都是VMA地址/区域; 

  VMA是必需的,如果指定了就用指定的值,如果没指定vma但指定了region,则基于region会计算一个vma值;如果region也没指定,则基于本地计数器计算一个vma值;如果输出段没有分配VMA值,则链接器也将使用lma_region作为vma_region, 并在vma_region中计算一个合适的值作为VMA的值

 

4:ld文件中不同section{ }段中的 . 不是递增的;重定位到相同MEMORY分区中各section{ }段中的 . 是递增的,且初始 . 值为MEMORY分区各段的ORG值,而非0; SECTION { 中 } 各输出段外层的 . 值是绝对地址值;输出段中的 . 值是一个相对MEMORY起始地址的偏移值;. 在不同的MEMORY中不会递增,在相同的MEMORY中会记录上次的结束值并递增,且在相同的MEMORY中 . 的值不能回退(减小);因此一个MEMORY中的输出段最好按照地址递增的方式先后出现在SECTION中,如果先写了MEMORY中靠近结尾处的输出段,则会使后面的 . 值变得回退,且这里不能指定输出region,见下图

 

5:不能访问链接器脚本定义的符号的值,它没有值;只能访问链接器脚本定义的符号的地址

 

6:map文件中输出段内的 . = 0x2F4000 ;  是将当前 . 值加了0x2F4000,不是变为0x2F4000

7:.shstrtab段保存着各Section的名字,.strtab段保存着程序中用到的符号的名字。每个名字都是以'\0'结尾的字符串。

8:在加载时.bss段和.data段一样都是可读可写的数据,但是在ELF文件中.data段需要占用一部分空间保存初始值,而.bss段则不需要。.bss段在文件中只占一个Section Header而没有对应的Section,程序加载时.bss段占多大内存空间会在Section Header中描述。

 

SECTIONS //定义输出段

{
  __NCACHE_REGION_START = ORIGIN(m_data2); //将m_data2的起始地址赋值给__NCACHE_REGION_START, ORIGIN是LD文件的关键字,取原始值之意; = 就是赋值语句
  __NCACHE_REGION_SIZE = 0;//__NCACHE_REGION_SIZE赋值0

  .interrupts : //输出段描述,表示这一段是中断向量表,就是一个名字,大家可以自己取,是其下{}内所有内容的别名
  {
    __VECTOR_TABLE = .; //把当前位置计数器的值赋值给__VECTOR_TABLE ,位置计数器的值一开始默认为0
    __Vectors = .;//把当前位置计数器的值赋值给__VECTOR_TABLE ,位置计数器的值一开始默认为0
    . = ALIGN(4); //这句话不是再给位置计数器赋值,而是给它增加限制条件,表示其增加一次增加4,在内存中的表现即为4字节对齐
    KEEP(*(.isr_vector))//放置所有文件中的.isr_vector section,*是通配符,表示所有,就跟我们搜索文件使用*时一样的。使用KEEP的意思就是告诉编译器这段数据非常重要,不要把它当成垃圾优化掉了
    . = ALIGN(4);
  } > m_interrupts //将这一输出段链接至m_interrupts区域处,所以中断向量表的链接地址就是m_interrupts的起始地址,m_interrupts是上面MEMORY定义的空间名称之一

  // > m_interrupts 只有这个表示 LMA 和VMA是一个地址,不需要从fls搬到ram,如果是 > m_interrupts  AT > m_data 的形式就表示LMA 和 VMA不是一个地址,VMA位于m_interrupts中,LMA位于m_data中

  .text : //定义输出段,就是一个名字,大家可以自己取
  {
    . = ALIGN(4); //4字节对齐
    *(.text) /*放置所有文件的.text段(code) */
    *(.rodata) /*放置所有文件的.rodata段(constants, strings, etc.) */
    KEEP (*(.init))
    KEEP (*(.fini))
  } > m_text //将这一输出段链接至m_text区域处

  .ctors : //定义输出段,就是一个名字,大家可以自己取

  {
    __CTOR_LIST__ = .;
    KEEP (*crtbegin.o(.ctors)) //放置*crtbegin.o中的.ctors段,并保证不被优化
    KEEP (*crtbegin?.o(.ctors)) //同上

    /*下面的语句中出现了EXCLUDE_FILE函数,这个函数的意思就是把括号里面的除外,
    意思就是说放置所有文件除了*crtend?.o *crtend.o文件的 .ctors段,
    因为在上面已经放置过了*/
    KEEP (*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors))
    /*下面的语句中出现了SPORT函数,SOPT是SORT_BY_NAME的别名,
    意思是放置.ctors.*段的时候,按照名字的排列顺序来放置*/
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
    __CTOR_END__ = .;
  } > m_text //将这一输出段链接至m_text区域处

  .preinit_array : //定义输出段,就是一个名字,大家可以自己取

  {
    /*下面的出现了PROVIDE_HIDDEN, 意思就是后面的这个符号__preinit_array_start 只能在
    链接器中被使用,外部文件是不能调用的,与它相反的还有PROVIDE, PROVIDE表示这个符号可以
    被外部调用,而且如果外部文件也定义了同样的符号也不会发生冲突,优先使用外部定义值,
    后面会出现很多PROVIDE*/
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } > m_text

  __etext = .;

  __DATA_ROM = .;

  __VECTOR_RAM = ORIGIN(m_interrupts);
  __RAM_VECTOR_TABLE_SIZE_BYTES = 0x0;

  ASSERT(__StackLimit >= __HeapLimit, "region m_data overflowed with stack and heap") /*ASSERT表示断言,跟C中的assert功能是一摸一样的*/

}

posted on 2023-04-20 17:38  lance9527  阅读(1393)  评论(0编辑  收藏  举报