ld链接器脚本(一)

为了能够看懂uboot,顺便把ld的脚本看了一下。笔记记录下来
翻译自《The GNU linker-ld(Sourcery G++ Lite 2010q1-188)Version 2.19.51》

ld链接器脚本

每种链接器都由链接器脚本控制,这个脚本使用连接器命令语言编写

这个脚本的主要目的是:将输入文件中的段,组织成输出文件,并且控制输出段的内存布局。

次要目的:链接脚本可以使用下面描述的命令,使链接器执行其他操作。

链接器始终使用链接脚本。 如果用户不提供,则链接器将使用内置的脚本,这个内置脚本被编译到链接器可执行文件中。

用户可以使用’–verbose’命令行选项显示默认链接脚本。 某些命令行选项(例如’-r’或’-N’)将影响默认的链接脚本

用户可以使用’-T’命令行选项提供自己的链接脚本。 执行此操作时,链接脚本将替换默认链接脚本。

链接脚本基本概念

为了描述链接脚本,需要定义一些基本的概念和词汇。

链接器将多个输入文件,组织成单个输出文件。输出文件和每个输入文件都有特殊的数据格式,这个格式称为:obj文件格式。每个文件都称为:obj文件。输出文件也被称为可执行文件,但是,此处我们也称之为obj文件。

每个obj文件,都有一组section。将输入文件的secton称之为输入section。输出文件的section,称之为输出section。

每个section都有一个名字和大小。大多数的section还有一个数据块,称之为section内容。

section还可以被标记为可加载。这意味着,当输出文件运行时,这个section应该被加载到内存中。

没有内容的section可以是可分配的,可分配意味着,内存应该留一段区域,而这个区域什么也不需要加载。(某些情况下,这段内存必须清零)

如果一个section既不是可加载,也不是可分配,则这个section通常包含有一系列的调试信息。

每个可加载和可分配的section有两个地址。第一个地址称之为VMA,或称之为虚拟内存地址。这是运行输出文件时,该section的地址。第二个地址称为LMA,或称为加载地址。这个地址是section被加载时的地址。在常见情况下,这两个地址相同。

这两个地址不同的常见例子是:在程序启动时,一个数据section被加载进ROM,然后被复制进RAM。(这种技术通常用于初始化基于ROM的系统中的全局变量)。在此种情况下,ROM地址为LMA,RAM地址为VMA

通过objdump命令的-h选项,可以查看obj文件中的section信息。

每个obj文件有一组符号,称之为符号表。符号可以是已经被定义了的,也可以是未被定义的。每个符号都有一个名字,每个被定义的符号都有一个地址。

如果编译c或者c++成obj文件,那么对于每个已经定义的函数,全局和静态变量,都会有一个定义的符号生成。而对于每一个在输入文件中引用的,但是未定义的函数,或者全局变量或者静态变量,都会有一个未定义的符号生成。

可以通过nm程序,或者objdump,配合-t选项,查看obj文件的符号表。

链接器脚本格式

链接器脚本为文本文件。

链接脚本为一系列命令。每个命令都是关键字,可能后跟参数,或者是对符号的赋值。可以使用分号分隔命令。 空格通常被忽略。

通常可以直接输入诸如文件名或格式名等字符串。字符串中的逗号作为文件名的分隔符,如果文件名需要逗号,则使用双引号括起来。文件名中不支持双引号。

脚本中可以包含注释,使用‘/*'和‘*/’.和c语言一样,在这注释中间的注释被当做空白处理。

一个简单的连接器脚本例子

许多链接器脚本非常类似。

最简单的连接器脚本就是只包含一个命令:“SECTIONS”。使用SECTIONS命令来描述输出文件的内存布局。

SECTIONS命令是非常有用的命令,此处我们举一个简单的例子。

假定你的程序,只包含:代码,已被初始化的数据和未被初始化的数据。他们将分别位于:.text,.data,.bss section中。让我们进一步假定,你的程序只包包含上述的section。

对于上面的例子,我们认为代码段应该加载到0x10000地址处,代码段应该从0x8000000处开始。则相应的连接器脚本如下:

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

用关键字SECTIONS来表示SECTOINS命令,然后是用花括号括起来的,一系列的赋值语句,和输出段描述。

上面例子SECTIONS命令的第一行是,为特殊符号"."设置一个值。这个特殊符号称为位置计数器。

如果你没有通过某种方式(后续描述具体的方式)指定输出section的地址。那么地址则由位置计数器来指定。然后位置计数器变为:当前值加上这个section的大小。

SECTONS命令的开始,位置计数器的值为0.

第二行定义了一个输出section,名为“.text”.后续的冒号是必要语法,但是现在可以忽略。在输出section名字后面的大括号内,列出了输入section的名字。表示将这些输入section组织在这个输出section中。“*”是通配符,他匹配任何文件名。表达式“*(.text)”表示所有输入文件的.text section被放置在这个输出section中。

当输出section----.text被定义时,位置计数器值为0x1000000.链接器将设置这个输出section的地址为0x1000000.

剩下的行定义了.data和.bss section。链接器将.data section放置在0x8000000地址处。然后位置计数器的值将是0x8000000加上.data section的大小。它将影响.bss section在内存中的位置。

链接器将通过在必要时增加位置计数器来确保每个输出节具有所需的对齐。

在此示例中,’.text’和’.data’部分的指定地址可能会满足任何对齐约束,但链接器可能必须在’.data’和’.bss’部分之间创建一个小间隙,来满足对齐。

上面就是,一个简单而完整的链接脚本。

下一节,脚本里面使用的各种命令。
另外,这里需要说明一下,对于section,segment。我暂时不知如何去用汉语表达,所以有时候就直接用的英文。有时候又在用‘节’,‘段’这样的汉字。又因为是自己的笔记,又懒得去细细分别了。如果真的有人看到了,可直接留言疑问。大家多多交流。

对于这两者的区别,可以这样理解:segment由来自各个源文件的section组成。segment是具有相同属性的seciton的集合。

这点在前面的armlink中已经有所体现。望有看到的人,注意一下。

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值