版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
楼主 |
发表于 2016-12-6 16:27
|
显示全部楼层
本帖最后由 kenson 于 2016-12-6 16:29 编辑
6 R& ~( T: M4 E+ Z5 `9 s" m0 P* [6 \/ k
链接定位是系统级软件开发过程中必不可少的一部分,嵌入式软件开发均属于系统级开发,绝大部分嵌入式软件都涉及到链接定位脚本文件;链接定位脚本使得我们的目标代码组织更加灵活.
8 i: Q) a" w3 t/ P& ?7 C' [( r6 r) ~8 L' W4 k
1)链接定位脚本文件说明
" X) d! M& P( O" }链接定位过程一般由链接器根据链接定位脚本完成,比较简单的系统可以通过设置链接器开关选项取代链接定位脚本;链接定位的关键是链接定位脚本的编写.我们从典型的目标文件结构开始,来介绍链接定位脚本文件的编写.下面是该系统一个目标文件的典型组织:
8 G8 m, `- @1 r& w I' D/ m
5 n4 Y* l8 g2 }- b4 k
4 W1 H( H( x( U' I# A
! l& q7 S, a* x, ~+ O1 w8 a) e4 T其中第二栏开始分别展示了该文件各个段(Sections)的属性:名称(Name),类型(Type),地址(Addr),偏移(Offs),大小 (Size),固定单元大小(Es),标志(Flg),连接依赖(Lk),附加属性(Inf),字节对其宽度(Al). 地址部分(Addr)描述了这一段在目标系统中的地址,而偏移(Offs)则记载了该段在目标文件中的偏移,大小(size)表示该段的实际长度;比如上 图中.Text段的地址为0x0c700000,偏移为0x008000,大小为0x00d950,说明该段位于文件的偏移0x008000处,它将被下 载到目标板0x0c700000处. 从段的分类来看,第7段以后的内容仅仅与调试有关,涉及到定位的也就是前面几段:.text,.data,.rodata,.bss,下面是一个具体的链 接定位脚本文件:
: t5 p" m! e9 p# m: J* B6 OSECTIONS L7 i! Z- S6 y6 F6 D
{ , j2 q% n1 |9 T. b
. = 0x0c200000; /*赋当前地址,后续的代码将从该地址开始存放 */
- T6 K( w8 _$ F4 V8 h. o) f.text : { (.text) } /*.text段表示代码段,从0x0c200000开始放置代码*/ % |5 O( E7 `* `2 ]6 w5 M; G" b' D
. K* q3 ]7 F: e3 O7 ]
Image_RW_Base = .; /* RW(可写数据)基址,实际上是在这里声明了一个全局符号,我们可
/ H/ }7 u& K i$ h以在程序中使用该符号,它等同于在代码中声明一个全局变量,但它的值由链接器指定,在这里"=."表
N! f- J7 y9 v+ A2 i% W6 x4 t3 K示该符号的值等于当前地址;下面的定义类似*/
0 g6 g! z" Z- L2 l. v" a, c; N( |5 u7 [. |! B/ s; ~
.data : { (.data) } /*数据段, 保存已经初始化的全局数据 */
: J9 w Z: {* i3 [6 t3 [6 I.rodata : { *(.rodata) } /*只读数据段, 保存已经初始化的全局只读数据*/
/ a: V6 K7 Q% k8 o) D
/ o4 L# E1 n* Y) H! n$ Q+ EImage_ZI_Base = .; /*ZI基地址, 需要清零的区域 zero init*/
# w4 D* L1 e' g3 p3 Q: m.bss : { *(.bss) } /*堆栈段,未初始化的全局变量也保存在此*/
3 |4 h, @# J% L1 f! C
6 u( ~% e( M7 ]4 y' q! O0 I5 f$ s__bss_start__ = .; /* bss的基地址*/ N) l1 l$ _/ B: C3 c
__bss_end__ = .; /* bss的结束地址*/ + w8 }( a$ j$ h0 Y
% h" Z6 p A3 f0 G
__EH_FRAME_BEGIN__ = .; /* FRAME开始地址(基地址)*/ - w1 ~ E7 h+ i# p* s
__EH_FRAME_END__ = .; /* FRAME结束地址,gcc编译器使用 */ 6 k+ z' `1 u( i/ |, w
1 _/ V7 p, n+ C; g" \; ?
FAQ 4 [2 C. [- j N' l' i5 D" o
PROVIDE (__stack = .); /* 当前地址赋给栈,栈地址一般是可读写区最高处*/
$ K5 e/ h$ T \, I5 _: {3 q0 \, G0 Z% d; ?' N, R0 z
end = .; /* 结束地址*/ 6 S/ f# c$ `0 @" i. {
_end = .; /* 结束地址*/
: {+ _* J0 m2 y5 _6 W* n5 N* s9 i7 u1 k Y% ? V6 y6 X" D$ b
.debug_info 0 : { *(.debug_info) } /*调试信息*/
% \. D5 e$ Y7 E( F' }6 n.debug_line 0 : { *(.debug_line) } /*调试信息*/
; B ^, b6 D, o# v* M1 n1 n.debug_abbrev 0 : { *(.debug_abbrev)} /*调试信息*/
8 E, Z4 |) K7 y0 z5 i' j.debug_frame 0 : { *(.debug_frame) } /*调试信息*/
6 f9 ?3 {6 U" H. N} % ]+ H6 n. E* }* C; B9 C
text段是程序代码段,紧随其后的是几个符号定义,它们是由编译器在编译连接时自动计算的,当我们在链接定位文件中申明这些符号后,编译连接时,该符号 的值会自动代入到源程序的引用中,如果你想进一步了解连接定位的一些含义,可以参考编程手册中的ld一章. data段的起始位置也是由连接定位文件所确定,大小在编译连接时自动分配,它和我们的程序大小没有关系,但和程序使用到的全局变量,常量数量相关. bss的初始值也是由我们自己定义的连接定位文件所确定,我们应该将它定义在可读写的RAM区内,stack的顶部在可读写的RAM区的最后,我们可以非 常灵活的定义其起点和大小,但对大部分情况来说,程序区在ROM或FLASH中,可读写区域在SRAM或DRAM中,我们可以考虑一下自己程序规模,函数 调用规模,存储器组织,然后参照一个连接定位文件稍加修改就可以了.6 @- x% H4 m# N1 B0 h
_& J4 M' [. n* k7 [
2)链接定位脚本修改实例
$ B, _) |% A% a3 ?' E" x8 {SECTIONS
+ @, j; t8 F# i1 ^{ 5 L7 A, u/ X0 X5 F
. = 0x00000000; /*将代码段起始地址修改到0*/ 2 x) u9 X- O& ^1 O7 j" f5 Y; x
.text : { *(.text) }
; z6 Y7 T% ]* w6 ]. A& OImage_RW_Base = .; # A Q2 M4 d9 \* v, s) Y/ y) U
.=0xc0000000 /*设置数据段从0xc0000000开始存放*/
+ h! \, N% G( C& G1 s' M6 i3 j2 C.data : { *(.data) }
2 e) x: a& g$ _* x+ c7 X" i& F
7 S% C7 p1 }5 H) R.=0xd0000000 /*设置只读数据段从0xd0000000开始存放*/ 2 H3 `- b9 W" E0 Q
.rodata : { *(.rodata) }
: J) ^( @$ G3 r# L: |$ q! W8 g" N/ F+ A
Image_ZI_Base = .; ' o0 {9 `3 \% q4 K# @' u, t
.bss : { *(.bss) } , R) h: b% S2 }8 p* i6 _ Z7 ~( W( g
Image_ZI_Limit = .;
; d* ~2 Y9 q- T0 ^" d% {* k, S
7 \) s& F. f, O7 C; A/ c- X/*申明一个符号download_size */ : R. b' x8 e6 `, A
download_size = SIZEOF(.text)+SIZEOF(.data)+SIZEOF(.rodata)+SIZEOF(.bss); 9 p$ `8 D9 `# g3 m5 f+ Y/ U
( `! U. P% U$ @; |
__bss_start__ = .; ) \$ \6 N1 O9 v0 ~/ d9 q
__bss_end__ = .; |
|