RISC-V MCU ld链接脚本说明
作者:互联网
1、什么是ld链接脚本?
通常,程序编译的最后一步就是链接,此过程根据“*.ld”链接文件将多个目标文件(.o)和库文件(.a)输入文件链接成一个可执行输出文件(.elf)。涉及到对空间和地址的分配以及符号解析与重定位。
而ld链接脚本控制这整个链接过程,主要用于规定各输入文件中的程序、数据等内容段在输出文件中的空间和地址如何分配。通俗的讲,链接脚本用于描述输入文件中的段,将其映射到输出文件中,并指定输出文件中的内存分配。
2、ld链接脚本的主要内容
2.1 链接配置(可选)
常见的配置有入口地址、输出格式、符号变量的定义等。如:
1 ENTRY( _start ) /* 入口地址 */ 2 3 __stack_size = 2048; /* 定义栈大小 */ 4 PROVIDE( _stack_size = __stack_size );/* 定义_stack_size符号,类似于全局变量 */
2.2 内存布局定义
对MCU的Flash及RAM空间进行分配,其中以ORIGIN定义地址空间的起始地址,LENGTH定义地址空间的长度。
语法如下:
1 MEMORY 2 { 3 name[(attr)] : ORIGIN = origin, LENGTH = length 4 ... 5 }
这里的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
2.3 段链接定义
用于定义目标文件(.o)的text、data、bss等段的链接分布。语法如下:
1 SECTIONS 2 { 3 section [address] [(type)] : 4 [AT(lma)] 5 [ALIGN(section_align) | ALIGN_WITH_INPUT] 6 [SUBALIGN(subsection_align)] 7 [constraint] 8 { 9 output-section-command 10 output-section-command 11 ... 12 } [>region] [AT>lma_region] [:phdr :phdr ...] [=fillexp] [,] 13 14 ... 15 } 16 17 /* 大多数的段仅使用了上述部分属性,可以简写成如下形式 */ 18 SECTIONS 19 { 20 ... 21 secname : 22 { 23 output-section-command 24 } 25 ... 26 }
链接脚本本质就是描述输入和输出。secname表示输出文件的段,而output-section-command用来描述输出文件的这个段从哪些文件里抽取而来,即输入目标文件(.o)和库文件(.a)。
Section 分为loadable(可加载)和allocatable(可分配)两种类型。不可加载也不可分配的内存段,通常包含一些调试等信息。
loadable:程序运行时,该段应该被加载到内存中。
allocatable:该段内容被预留出,同时不应该加载任何其他内容(某些情况下,这些内存必须归零)。
loadable和allocatable的section都有两个地址:"VMA"和"LMA"。
VMA (the vortual memory address):运行输出文件时,该section的地址。可选项,可不配置。
LAM (load memory address):加载section时的地址。
在大多数情况下,这两个地址时相同的。但有些情况下,需将代码从Flash中加载至RAM运行,此时Flash地址为LAM,RAM地址为VMA。如:
1 .data : 2 { 3 *(.data .data.*) 4 . = ALIGN(8); 5 PROVIDE( __global_pointer$ = . + 0x800 ); 6 *(.sdata .sdata.*) 7 *(.sdata2.*) 8 . = ALIGN(4); 9 PROVIDE( _edata = .); 10 } >RAM AT>FLASH
上述示例中,.data段的内容会放在Flash中,但是运行时,会加载至RAM中(通常为初始化全局变量),即.data段的VMA为RAM,LMA为Flash。
3、常用关键字及命令
3.1 ENTRY
语法:ENTRY(symbol),程序中要执行的第一个指令,也称入口点。示例:
1 /* Entry Point */ 2 ENTRY( _start ) /* CH32V103为启动文件 j handle_reset 指令*/
3.2 PROVIDE
语法:PROVIDE (symbol = expression),用于定义一个可被引用的符号,类似于全局变量。示例:
1 PROVIDE( end = . );
3.3 HIDDEN
语法:HIDDEN (symbol = expression),对于ELF目标端口,符号将被隐藏且不被导出。示例:
1 HIDDEN (mySymbol = .);
3.4 PROVIDE_HIDDEN
语法:PROVIDE_HIDDEN (symbol = expression),是PROVIDE 和HIDDEN的结合体,类似于局部变量。示例:
1 PROVIDE_HIDDEN (__preinit_array_start = .);
3.5 点位符号 '.'
‘.’表示当前地址,它是一个变量,总是代表输出文件中的一个地址(根据输入文件section的大小不断增加,不能倒退,且只用于SECTIONS指令中)。它可以被赋值也可以赋值给某个变量;也可进行算术运算用于产生指定长度的内存空间。示例:
1 PROVIDE( end = . ); /* 当前地址赋值给 end符号 */ 2 3 .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size : 4 { 5 . = ALIGN(4); 6 PROVIDE(_susrstack = . ); 7 . = . + __stack_size; /* 当前地址加上__stack_size长度,产生__stack_size长度的空间*/ 8 PROVIDE( _eusrstack = .); 9 } >RAM
3.6 KEEP
当链接器使用('--gc-sections')进行垃圾回收时,KEEP()可以使得被标记段的内容不被清楚。示例
1 .init : 2 { 3 _sinit = .; 4 . = ALIGN(4); 5 KEEP(*(SORT_NONE(.init))) 6 . = ALIGN(4); 7 _einit = .; 8 } >FLASH AT>FLASH
3.7 ASSERT
语法:ASSERT(exp, message),确保exp是非零值,如果为零,将以错误码的形式退出链接文件,并输出message。主要用于添加断言,定位问题。
示例:
1 /* The usage of ASSERT */ 2 PROVIDE (__stack_size = 0x100); 3 4 .stack 5 { 6 PROVIDE (__stack = .); 7 ASSERT ((__stack > (_end + __stack_size)), "Error: No room left for the stack"); 8 } 9 /* 当"__stack" 大于 "_end + __stack_size"时,在链接时,会出现错误,并提示"Error: No room left for the stack" */ 10
4、完整ld链接脚本示例
以RISC-V MCU CH32V103为例。
1 ENTRY( _start ) 2 3 __stack_size = 2048; 4 5 PROVIDE( _stack_size = __stack_size ); 6 7 8 MEMORY 9 { 10 FLASH (rx) : ORIGIN = 0x00000000 , LENGTH = 0x10000 11 RAM (xrw) : ORIGIN = 0x20000000 , LENGTH = 0x5000 12 } 13 14 15 SECTIONS 16 { 17 18 .init : 19 { 20 _sinit = .; 21 . = ALIGN(4); 22 KEEP(*(SORT_NONE(.init))) 23 . = ALIGN(4); 24 _einit = .; 25 } >FLASH AT>FLASH 26 27 .vector : 28 { 29 *(.vector); 30 . = ALIGN(64); 31 } >FLASH AT>FLASH 32 33 .flag : 34 { 35 . = ORIGIN(FLASH)+0x8000; 36 KEEP(*(SORT_NONE(.myBufSection))) 37 }>FLASH AT>FLASH 38 39 .text : 40 { 41 . = ALIGN(4); 42 *(.text) 43 *(.text.*) 44 *(.rodata) 45 *(.rodata*) 46 *(.glue_7) 47 *(.glue_7t) 48 *(.gnu.linkonce.t.*) 49 . = ALIGN(4); 50 } >FLASH AT>FLASH 51 52 .fini : 53 { 54 KEEP(*(SORT_NONE(.fini))) 55 . = ALIGN(4); 56 } >FLASH AT>FLASH 57 58 PROVIDE( _etext = . ); 59 PROVIDE( _eitcm = . ); 60 61 .preinit_array : 62 { 63 PROVIDE_HIDDEN (__preinit_array_start = .); 64 KEEP (*(.preinit_array)) 65 PROVIDE_HIDDEN (__preinit_array_end = .); 66 } >FLASH AT>FLASH 67 68 .init_array : 69 { 70 PROVIDE_HIDDEN (__init_array_start = .); 71 KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) 72 KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) 73 PROVIDE_HIDDEN (__init_array_end = .); 74 } >FLASH AT>FLASH 75 76 .fini_array : 77 { 78 PROVIDE_HIDDEN (__fini_array_start = .); 79 KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) 80 KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) 81 PROVIDE_HIDDEN (__fini_array_end = .); 82 } >FLASH AT>FLASH 83 84 .ctors : 85 { 86 /* gcc uses crtbegin.o to find the start of 87 the constructors, so we make sure it is 88 first. Because this is a wildcard, it 89 doesn't matter if the user does not 90 actually link against crtbegin.o; the 91 linker won't look for a file to match a 92 wildcard. The wildcard also means that it 93 doesn't matter which directory crtbegin.o 94 is in. */ 95 KEEP (*crtbegin.o(.ctors)) 96 KEEP (*crtbegin?.o(.ctors)) 97 /* We don't want to include the .ctor section from 98 the crtend.o file until after the sorted ctors. 99 The .ctor section from the crtend file contains the 100 end of ctors marker and it must be last */ 101 KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) 102 KEEP (*(SORT(.ctors.*))) 103 KEEP (*(.ctors)) 104 } >FLASH AT>FLASH 105 106 .dtors : 107 { 108 KEEP (*crtbegin.o(.dtors)) 109 KEEP (*crtbegin?.o(.dtors)) 110 KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) 111 KEEP (*(SORT(.dtors.*))) 112 KEEP (*(.dtors)) 113 } >FLASH AT>FLASH 114 115 .dalign : 116 { 117 . = ALIGN(4); 118 PROVIDE(_data_vma = .); 119 } >RAM AT>FLASH 120 121 .dlalign : 122 { 123 . = ALIGN(4); 124 PROVIDE(_data_lma = .); 125 } >FLASH AT>FLASH 126 127 .data : 128 { 129 *(.gnu.linkonce.r.*) 130 *(.data .data.*) 131 *(.gnu.linkonce.d.*) 132 . = ALIGN(8); 133 PROVIDE( __global_pointer$ = . + 0x800 ); 134 *(.sdata .sdata.*) 135 *(.sdata2.*) 136 *(.gnu.linkonce.s.*) 137 . = ALIGN(8); 138 *(.srodata.cst16) 139 *(.srodata.cst8) 140 *(.srodata.cst4) 141 *(.srodata.cst2) 142 *(.srodata .srodata.*) 143 . = ALIGN(4); 144 PROVIDE( _edata = .); 145 } >RAM AT>FLASH 146 147 .bss : 148 { 149 . = ALIGN(4); 150 PROVIDE( _sbss = .); 151 *(.sbss*) 152 *(.gnu.linkonce.sb.*) 153 *(.bss*) 154 *(.gnu.linkonce.b.*) 155 *(COMMON*) 156 . = ALIGN(4); 157 PROVIDE( _ebss = .); 158 } >RAM AT>FLASH 159 160 PROVIDE( _end = _ebss); 161 PROVIDE( end = . ); 162 163 .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size : 164 { 165 . = ALIGN(4); 166 PROVIDE(_susrstack = . ); 167 . = . + __stack_size; 168 PROVIDE( _eusrstack = .); 169 } >RAM 170 171 }
标签:__,ld,PROVIDE,ALIGN,FLASH,RISC,KEEP,MCU,stack 来源: https://www.cnblogs.com/Zhuzzz/p/16293250.html