原创 关于GNU LD和LD脚本

2009-2-23 13:37 2159 4 4 分类: MCU/ 嵌入式

 


ld,即GNU的连接工具,用于将各目标文件合并在一起,并重新安排他们的数据以及符号的引用,常常是程序编译的最后一步。


ld scripts 即ld脚本。ld 脚本的主要目的是要描述怎样将输入文件的各段印象到输出文件中去。它控制输出文件在内存的布局情况。


--------------------------------------------------------------------------------


关于VMA ,LMA


每一个可装载的输出段都有两个地址:VMA(Virtual memory address) 和 LMA(Load mem
ory address)VMA 是输出段运行时的地址,LMA 则是输出段被装载的地址。而这2个地址常常是相同的。在某些情况下二者是有区别的。比如,一个data段被装载到ROM中,然后在程序启动的时候被拷贝到了RAM中去。(这种技术常常用在以ROM 为基础的系统中,用来初始化全局变量,而我们的系统的处理方法可能与此类似?)


 


--------------------------------------------------------------------------------


 


关于SECTIONS命令的使用
SECTIONS告诉LD怎样将输入的段印象到输出的段,以及怎样将输出的段装载到内存中去。这是我们在内存布局中常常要用到的命令。段的基本结构如下:
SECTIONS
{
sections- command
sections- command
...
}
其中的sections-command 可作如下选择:
* 程序入口点设置命令ENTRY。
* 符号赋值
* 输出段描述(下面会提到)
* 覆盖描述。(overlay description)


1.输出段描述(Output Section Description):
输出段描述的完整格式如下:


SECTION [ address] [( type)] : [AT( LMA)]
{
output-sections-command
output-sections-command
...
} [> region] [: phdr : phdr ...] [=fillexp]


一般对于以上的描述不会全部用到,对于这些描述的用法,后面将会讲述到。
(1).关于段名
对于不同的输出格式,段名应该满足相应的约定。比如a.out格式的文件就应该只能
使用这些段名:.text .data .bss
(3).输出段地址


输出段地址 address 是输出段的VMA地址。例:


.text [address] :{ *(.text)}
如果没有指定 address 则将按照 region 或者是当前地址记数值"."来分配地址。注意,这里分配的是VMA地址(见VMA的说明)。
另外,在分配地址的时候还有地址对齐操作,这里就不赘述了。



2.输入段描述:
输出段描述告诉连接器怎样在内存中安排你的程序布局,而输入段描述则告诉连接器怎样将各输入文件映射到你的内存布局中去。
(1).输入段的基本语法如下:


输入文件名(段名)
例: file1(.text)
表示将文件 file1的.text 段放于此处。
也支持通配符,如:
*(.text)
将所有的文件的.text段放置到此处。
又例:
a.out
直接将a.out的所有段放置此处。
(2).输入段的通配符使用,就是一般的通配符语法,这里就不赘述了。
3.COMMON 段的设置
在很多的目标文件格式中,对于common symbols(什么是common symbols?)都没有专门的段来存放。
所以在连接时,连接器专门指定了一个COMMON段来包含这些common symbols.
而COMMON段一般放于输出文件的.bss段中。
例:
.bss { *(.bss) *(COMMON) }
在.bss段中放置所有输入文件的.bss段 和 所有输入文件的 common symbols
4.输入段描述的示例:


SECTIONS {
outputa 0x10000 :
{
all.o all.o的所有段
foo.o (.input1) foo.o的所有.input1 段
}
outputb :
{
foo.o (.input2) foo.o的所有.input2 段
foo1.o (.input1) foo1.o的所有.input1段
}
outputc :
{
*(.input1) 所有文件的余下的.input1段
*(.input2) 所有文件的余下的.input2段
}
}



5.在输出段中装填数据。
(1). BYTE, SHORT, LONG, and QUAD
如:BYTE(1)
在当前位置装填1字节的1
(2). FILL 的使用,可以从当前位置开始装填本SECTION。
6.两个输出段关键字。
CREATE_OBJECT_SYMBOLS 和 CONSTRUCTORS


CREATE_OBJECT_SYMBOLS:
每一个输入的文件将对应一个同名的symbol.而这些symbol将被放置在CREATE_OBJECT_SYMBOLS
命令出现的段中。
CONSTRUCTORS:
在此命令出现的地方放置C++全局构造和析构函数信息。对于a.out 格式,它使用其专有的方法来支持C++构造与析构函数。对于不能使用任意段名的文件格式如:XCOFF 和ECOFF 则需要一个CONSTRUCTORS 命令来在输出文件中记录C++全局构造函数和析构函数的信息。对于能使用任意段名的文件格式如COFF 和 ELF 。则连接器将自动生成名为.ctors 和 .dtors 的段来记录相应信息。我们可能暂时用不到这个东西。


7.丢弃段。
命名为 /DISCARD/ 的段的内容将不被包含到输出文件中。


8.输出段描述的其它特性,输出段描述的基本结构正如前边所提到的如下所示:


SECTION [ address] [( type)] : [AT( LMA)]
{
output-sections-command
output-sections-command
...
} [> region] [: phdr : phdr ...] [=fillexp]


下面对其各个特性做一个比较详细的说明:
(1). Output Section Type (即输出段的type属性)


此属性可以赋值为:
NOLOAD 此段的内容将不被装载到内存中去,比如直接在ROM中运行程序。


DSECT
COPY
INFO
OVERLAY
以上4个代表运行时不另外为改段分配内存空间,这样可以一定的节省程序
空间。


(2). 输出段LMA
使用AT 命令来设置段的LMA
这样的话可以使用这个特性很方便的建立ROM image.
例如:
SECTIONS
{
.text 0x1000 : { *(.text) _etext = . ; } //.text 的VMA为0x1000
.mdata 0x2000 : //.mdata 的VMA为0x2000
AT ( ADDR (.text) + SIZEOF (.text) ) //.mdata 的LMA为紧随.text
//段之后。
{ _data = . ; *(.data); _edata = . ; }
.bss 0x3000 : //.bss的VMA为0x3000
{ _bstart = . ; *(.bss) *(COMMON) ; _bend = . ;}
}
与之配合的初始化代码示例如下(可能是由ld自动生成):
extern char _etext, _data, _edata, _bstart, _bend;
char *src = &_etext; //从这里可以看出,"_etext=."的意思实际上是令_etext的地址
//等于当前的VMA
char *dst = &_data;
/* ROM has data at end of text; copy it. */
while (dst < &_edata) {
*dst++ = *src++;
}
//将.text段结尾的.data段拷贝到其运行时地址去。


 


/* Zero bss */
for (dst = &_bstart; dst< &_bend; dst++)
*dst = 0;
//将未初始化数据全部清零。
(3). Output Section Region
使用 >region 属性可以将某个段定位到预定义好的内存位置去。
例:
MEMORY { rom : ORIGIN = 0x1000, LENGTH = 0x1000 }
SECTIONS { ROM : { *(.text) } >rom }
(4). Output Section Segmentation
程序段的定义。
例:
PHDRS { text PT_LOAD ; }
SECTIONS { .text : { *(.text) } :text }
后面将提到PHDRS
(5).输出段填充
例:采用如下的方法来填充段
SECTIONS { .text : { *(.text) } =0x9090 }
9.覆盖描述:(Overlay Description)
定义如下:
OVERLAY [ start] : [NOCROSSREFS] [AT ( ldaddr )]
{
secname1
{
output-section-command
output-section-command
...
} [:PHDR...] [=FILL]
secname2
{
output-section-command
output-section-command
...
} [: phdr...] [= fill]
...
} [> region] [: phdr...] [= fill]
OVERLAY 命令应该定义于SECTIONS 命令之内。
--------------------------------------------------------------------------------
------------
10.MEMORY 命令


分配内存区域
MEMORY
{
name [( attr)] : ORIGIN = origin, LENGTH = len
...
}


atrr 为段的匹配特性,如果一个段没有明确指定将其放置到某一个region中去,那么如果
其属性匹配该region的atrr属性,将被加入到该region中去。


有以下属性:
R 只读段
W 只写段
X 执行段
A 可分配段
I 已初始化段
L 与I相同。
! 非特性


例子:
MEMORY
{
rom (rx) : ORIGIN = 0, LENGTH = 256K
ram (!rx) : org = 0x40000000, l = 4M
}
一旦完成了region的定义,就可以使用 >region 的方法将某段指定添加到某个region中去

文章出处:http://www.diybl.com/course/6_system/linux/Linuxjs/2008831/139137.html

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
4
关闭 站长推荐上一条 /3 下一条