【转】链接脚本(1)

1、什么是ld?它有什么作用?

ld是GNU binutils工具集中的一个,是众多Linkers(链接器)的一种。完成的功能自然也就是链接器的基本功能:把各种目标文件和库文件链接起来,并重定向它们的数据,完成符号解析。Linking其实主要就是完成四个方面的工作:storage allocation、symbol management、libraries、relocation。

ld可以识别一种Linker command Language表示的linker scriopt文件来显式的控制链接的过程。通过BFD(Binary Format Description)库,ld可以读取和操作COFF(common object file format)、ELF(executable and linking format)、a.out等各种格式的目标文件。

2、常用的选项

-b TARGET  设置目标文件的文件格式

-e ADDRESS 设置目标文件的开始地址

-EB  链接big-endian的目标文件

-EL  链接small-endian的目标文件

-l LIBNAME    创建执行程序时要链接的库文件(比如某个库为test,则可以为-ltest)

-L DIRECTORY  寻找要链接的库文件时搜索的文件路径

-o FILE  设置输出文件的名字

-s  去除输出文件中的所有符号信息

-S  去除输出文件中的调试符号信息

-T FILE  读取链接描述脚本,以确定符号等的定位地址

-v  输出ld的版本信息

-x  去除所有的局部符号信息

-X  去除临时的局部符号信息,默认情况下会设置这个选项

-Bstatic   创建的输出文件链接静态链接库

-Bdynamic  创建的输出文件链接动态链接库

-Tbss ADDRESS  设置section bss的起始地址

-Tdata ADDRESS 设置section data的起始地址

-Ttext ADDRESS 设置section text的起始地址

3、链接描述脚本

链接描述脚本描述了各个输入文件的各个section如何映射到输出文件的各section中,并控制输出文件中section和符号的内存布局。

目标文件中每个section都有名字和大小,而且可以标识为loadable(表示该section可以加载到内存中)、allocatable(表示必须为这个section开辟一块空间,但是没有实际内容下载到这里)。如果不是loadable或者allocatable,则一般含有调试信息。

每个有loadable或allocatable标识的输出section有两种地址,一种是VMA(Virtual Memory Address),这种地址是输出文件运行时section的运行地址;一种是LMA(Load Memory Address),这种地址是加载输出文件时section的加载地址。一般,这两种地址相同。但在嵌入式系统中,经常存在执行地址和加载地址不一致的情况。如把输出文件加载到开发板的flash存储器中(地址由LMA指定),但运行时,要把flash存储器中的输出文件复制到SDRAM中运行(地址有 VMA指定)。

在链接脚本中使用注释,可以用“/*...*/”。

每个目标文件有许多符号,每个符号有一个名字和一个地址,一个符号可以是定义的,也可以是未定义的。对于普通符号,需要一个特殊的标识,因为在目标文件中,普通符号没有一个特定的输入section。链接器会把普通符号处理成好像它们都在一个叫做COMMON的section中。

下面给出vivi的ld script的内容及分析。

(1)[Makefile]


LINKFLAGS
= -Tarch/vivi.lds
-Bstatic

可见,链接的脚本是arch/vivi.lds,而且链接静态库。但是在arch下没有vivi.lds,而是有vivi.lds.in。看了一下vivi.lds.in的内容,


SECTIONS
{

. = TEXTADDR;

.text :
{ *(.text)
}

.data ALIGN(4)
: {
*(.data)
}

.bss ALIGN(4)
: {
*(.bss)
*(COMMON)
}

}

很明显,这个就是原始的vivi的链接脚本。但是存在一个变量TEXTADDR没有赋值,也就是说,这个量根据配置的不同是不同的,所以肯定就在Makefile中执行了生成方法。下一步就要看[arch/Makefile]

(2)[arch/Makefile]


LDSCRIPT
= arch/vivi.lds.in


ifeq
($(CONFIG_ARCH_S3C2410),y)

MACHINE = s3c2410

ifeq ($(CONFIG_S3C2410_NAND_BOOT),y)

TEXTADDR = 0x33f00000

else

TEXTADDR = 0x00000000

endif

endif


vivi: $(HEAD) arch/vivi.lds

arch/vivi.lds: $(LDSCRIPT)

@sed s/TEXTADDR/$(TEXTADDR)/ $(LDSCRIPT)
>$@

很明显,这步主要完成的工作就是要把vivi.lds.in文件中的TEXTADDR用配置后的实际值来代替。根据我的配置,这里我的TEXTADDR就是0x33f00000.


SECTIONS
{

. = 0x33f00000;

.text :
{ *(.text)
}

.data ALIGN(4)
: {
*(.data)
}

.bss ALIGN(4)
: {
*(.bss)
*(COMMON)
}

}

SECTIONS表示段。第一行表示当前地址为0x33f00000,就是VMA,同时也是text段的起始地址。第二行用了通配符*表示所有字符,这里的意思就是说指定的每个目标文件的text
section的内容都放到同一个.text中。第三行表示指定的每个目标文件的data
section的内容都放到同一个.data中,而且要四字节边界对齐。第四行表示指定的每个目标文件的bss
section的内容都放到同一个.bss中,所有的普通符号都放到COMMON中,而且要四字节边界对齐。

这算是最为简单的ld scripts,不过也够用了。如果不考虑对齐等因素,则可以直接在命令行中指定-Ttext 0x33f00000,就可以完成了。当然,对Linux kernel等,ld scripts要处理复杂的内存分配等操作,相应的要复杂一些,读那些的方法就是查阅using ld手册,同时还要研究MCU的内存分配,这样才能作出合理的安排

时间: 2024-11-10 13:53:26

【转】链接脚本(1)的相关文章

关于链接脚本中程序入口的一些问题

http://www.jb51.net/article/62360.htm 在编写普通的c语言程序时,我们都会先写一个main函数,并认为main函数是所有程序的入口函数,其实main函数只是我们所编写的程序的入口函数,真正可执行文件的入口点并不是main函数,在执行main函数之前还有许多的初始化工作需要做,这些在main函数之前的工作是由标准 C 库完成的,然后再由标准  C 库调用main函数. 真正可执行文件的入口点可以通过查看链接脚本(在使用ld命令时加上-verbose参数)可以看出

Linux链接脚本学习--lds(转)

Linux链接脚本学习--lds 一.概论 ld: GNU的链接器. 用来把一定量的目标文件跟档案文件链接在一起,并重新定位它们的数据,链接符号引用. 一般编译一个程序时,最后一步就是运行ld进行链接 每一个链接都被一个链接脚本所控制,这个脚本是用链接命令语言书写的. 二.链接脚本 链接脚本的一个主要目的是描述输入文件中的各个段(数据段,代码段,堆,栈,bss)如何被映射到输出文件中,并控制输出文件的内存排布. 链接器总是使用链接脚本的,如果你不提供,则链接器会使用一个缺省的脚本,这个脚本是被编

链接脚本之LMA VMA解释

链接脚本中的LMA和VMA是什么意思.这个问题纠结了一段时间,今天在看<ARM体系结构与编程>时,豁然开朗,写下自己的认识.分享例如以下: LMA:载入地址 位于存储器中的地址  LOAD MEMORY ADDRESS VMA:执行地址(虚拟地址) 执行时的地址 VIRTUAL MEMORY ADDRESS  为什么用VMA表示呐?由于cpu执行的地址都是虚拟地址,经过MMU转为物理地址.在没有开MMU的裸板下,延续了这一称呼.理解为执行地址. 为什么要分 两种地址? 执行映像文件时,有些域能

重定位和链接脚本

大部分指令是"位置有关编码" 位置无关编码:汇编源文件编码成二进制可执行程序时,编码方式与位置无关. 在我们写程序时,必须给编译器链接器指定地址.将来的程序被执行时必须放在当时编译链接时给定的地址才能运行. 位置有关编码:汇编源码编码成二进制可执行程序后和内存地址是有关的. 但是也有一种特别的指令他可以跟指定的链接地址没有关系,这些代码不管放在哪里都可以正常运行. 分析: 之前的裸机程序中,makefile中用-Ttext 0x0来指定链接地址是0x0;这意味着我们认为将来这个程序会被

powerpc uboot链接脚本大改造

在做完了linux由arm处理器核移植到ppc处理器核的工作后,还需要进行uboot的移植,之前对uboot的分析文章都是基于arm平台,感兴趣的朋友可以看看,链接如下: http://blog.csdn.net/column/details/uboot-note.html 借这次ppc移植工作,也学习了ppc uboot的相关知识,顺道写几篇文章记录下. 工作背景是公司处理器由arm处理器核cortex A8换为ppc处理器核ppc460s,现在处于FPGA仿真验证阶段,SOC的外设控制器都没

内核移植(四)——Makefile和链接脚本分析

1:Makefile分析 (1) kernel的Makefile写法和规则等和uboot的Makefile是一样的,甚至Makefile中的很多内容都是一样的. (2) kernel的Makefile比uboot的Makefile要复杂,这里我们并不会一行一行的详细分析. (3) Makefile中只有一些值得关注的我会强调一下,其他不强调的地方暂时可以不管. (4) Makefile中刚开始定义了kernel的内核版本号.这个版本号挺重要(在模块化驱动安装时会需要用到),要注意会查,会改. (

linux 内核移植(四)——Makefile和链接脚本分析

1:Makefile分析 (1) kernel的Makefile写法和规则等和uboot的Makefile是一样的,甚至Makefile中的很多内容都是一样的. (2) kernel的Makefile比uboot的Makefile要复杂,这里我们并不会一行一行的详细分析. (3) Makefile中只有一些值得关注的我会强调一下,其他不强调的地方暂时可以不管. (4) Makefile中刚开始定义了kernel的内核版本号.这个版本号挺重要(在模块化驱动安装时会需要用到),要注意会查,会改. (

第二部分:S5PV210_重定位和链接脚本_2

重定位和链接脚本 (1)重定位:分为静态重定位和动态重定位 静态重定位:静态重定位是在程序执行之前进行重定位,它根据装配模块将要装入的内存起始位置,直接修改装配模块中的有关使用地址的指令 我们下面要分析就是静态重定位的情况. 动态重定位:动态重定位是指,不是在程序执行之前而是在程序执行过程中进行地址重定位.更确切地说,是在每次访问内存单元前才进行地址变换.动态重定位可使装配模块不加任何修改而装入内存,但是它需要硬件——定位寄存器的支持. 图片的参考来源:http://c.biancheng.ne

重定位与链接脚本

1.为什么需要重定位 位置无关编码(PIC,position independent code):汇编源文件被编码成二进制可执行程序时编码方式与位置(内存地址)无关. 位置有关编码:汇编源码编码成二进制可执行程序后和内存地址是有关的. 我们在设计一个程序时,会给这个程序指定一个运行地址(链接地址).就是说我们在编译程序时其实心里是知道我们程序将来被运行时的地址(运行地址)的,而且必须给编译器链接器指定这个地址(链接地址)才行.最后得到的二进制程序理论上是和你指定的运行地址有关的,将来这个程序被执

u-boot链接脚本分析

eclipse 64位下载地址:http://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/kepler/SR1/eclipse-standard-kepler-SR1-win32-x86_64.zip 如果从nand flash 启动,0地址就对应的是nand flash ,nand flash 不能像内存那样去写,通过这个可以判断是否是从nand flash 启动 U-boot最根本