目标文件(包括可执行文件)具有固定的格式, 在UNIX或GNU/Linux平台下, 一般为ELF格式. |
在目标文件中, loadable或allocatable的输出section有两种地址: VMA(virtual MemoryAddress)和LMA(Load Memory Address). VMA是执行输出文件时section所在的地址,而LMA是加载输出文件时section所在的地址. 一般而言, 某section的VMA == LMA. 但在嵌入式系统中,经常存在加载地址和执行地址不同的情况: 比如将输出文件加载到开发板的flash中(由LMA指定),而在运行时将位于flash中的输出文件复制到SDRAM中(由VMA指定). |
ENTRY(SYMBOL) : 将符号SYMBOL的值设置成入口地址。 |
INCLUDE filename: 包含其他名为filename的链接脚本 |
INPUT(files):将括号内的文件做为链接过程的输入文件 |
GROUP(files) :指定需要重复搜索符号定义的多个输入文件 |
OUTPUT(FILENAME): 定义输出文件的名字 |
SEARCH_DIR(PATH):定义搜索路径, |
STARTUP(filename): 指定filename为第一个输入文件 |
OUTPUT_FORMAT(BFDNAME) :设置输出文件使用的BFD格式 |
OUTPUT_FORMAT(DEFAULT,BIG,LITTLE): 定义三种输出文件的格式(大小端) |
TARGET(BFDNAME):设置输入文件的BFD格式 |
可通过 man -S 1 ld查看ld的联机帮助, 里面也包括了对这些命令的介绍.
6. 链接脚本相关:
(1).编译工具介绍
GNU提供的编译工具包括汇编器as、C编译器gcc、C++编译器g++、连接器ld和二进制转换工具objcopy。基于ARM平台的工具分别为arm-linux-as、arm-linux-gcc、arm-linux-g++、arm-linux-ld和arm-linux- objcopy。GNU的编译器功能非常强大,共有上百个操作选项,这也是这类工具让初学者头痛的原因。不过,实际开发中只需要用到有限的几个,大部分可以采用缺省选项。GNU工具的开发流程如下:编写C、C++语言或汇编源程序,用gcc或g++生成目标文件,编写连接脚本文件,用连接器生成最终目标文件(elf格式),用二进制转换工具生成可下载的二进制代码。
(2)编写C、C++语言或汇编源程序
通常汇编源程序用于系统最基本的初始化,如初始化堆栈指针、设置页表、操作ARM的协处理器等。初始化完成后就可以跳转到C代码执行。需要注意的是,GNU的汇编器遵循AT&T的汇编语法,读者可以从GNU的站点(www.gnu.org)上下载有关规范。汇编程序的缺省入口是 start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点(见下文关于连接脚本的说明)。
(3)用gcc或g++生成目标文件
如果应用程序包括多个文件,就需要进行分别编译,最后用连接器连接起来。如笔者的引导程序包括3个文件:init.s(汇编代码、初始化硬件)xmrecever.c(通信模块,采用Xmode协议)和flash.c(Flash擦写模块)。
分别用如下命令生成目标文件: arm-linux-gcc-c-O2-oinit.oinit.s arm-linux-gcc-c-O2-oxmrecever.oxmrecever.c arm-linux-gcc-c-O2-oflash.oflash.c 其中-c命令表示只生成目标代码,不进行连接;-o命令指明目标文件的名称;-O2表示采用二级优化,采用优化后可使生成的代码更短,运行速度更快。如果项目包含很多文件,则需要编写makefile文件。关于makefile的内容,请感兴趣的读者参考相关资料。
(4) 编写链接脚本
gcc等编译器内置有缺省的连接脚本。如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行。为了能在嵌入式系统上直接运行,需要编写自己的连接脚本文件。编写连接脚本,首先要对目标文件的格式有一定了解。GNU编译器生成的目标文件缺省为elf格式。elf文件由若干段(section)组成,如不特殊指明,由C源程序生成的目标代码中包含如下段:.text(正文段)包含程序的指令代码;.data(数据段)包含固定的数据,如常量、字符串;.bss(未初始化数据段)包含未初始化的变量、数组等。C++源程序生成的目标代码中还包括.fini(析构函数代码)和. init(构造函数代码)等。连接器的任务就是将多个目标文件的.text、.data和.bss等段连接在一起,而连接脚本文件是告诉连接器从什么地址开始放置这些段。
例如,连接文件link.lds为:
ENTRY(begin)
SECTION
{
.=0x30000000;
.text:{*(.text)}
.data:{*(.data)}
.bss:{*(.bss)}
}
其中,ENTRY(begin)指明程序的入口点为begin标号;.= 0x30000000指明目标代码的起始地址为0x30000000;.text:{*(.text)}表示从0x30000000开始放置所有目标文件的代码段,随后的.data:{* (.data)}表示数据段从代码段的末尾开始,再后是.bss段。
(5)用连接器生成最终目标文件
有了连接脚本文件,如下命令可生成最终的目标文件:
arm-linux-ld –no stadlib –o bootstrap.elf -Tlink.lds init.o xmrecever.o flash.o
其中,ostadlib表示不连接系统的运行库,而是直接从begin入口;-o指明目标文件的名称;-T指明采用的连接脚本文件(也可以使用-Ttext address,address表示执行区地址);最后是需要连接的目标文件列表。
(6)生成二进制代码
连接生成的elf文件还不能直接下载执行,通过objcopy工具可生成最终的二进制文件:
arm-linux-objcopy –O binary bootstrap.elf bootstrap.bin
其中-O binary指定生成为二进制格式文件。Objcopy还可以生成S格式的文件,只需将参数换成-O srec。还可以使用-S选项,移除所有的符号信息及重定位信息。如果想将生成的目标代码反汇编,还可以用objdump工具:
arm-linux-objdump -D bootstrap.elf
至此,所生成的目标文件就可以直接写入Flash中运行了。
【例2】makefile举例:
example: head.s main.c
arm-linux-gcc -c -o head.o head.s
arm-linux-gcc -c -o main.o main.c
arm-linux-ld -Tlink.lds head.o ain.o -o example.elf
arm-linux-objcopy -O binary -S example_tmp.o example
arm-linux-objdump -D -b binary -m arm example >ttt.s
7.段与重定位
1. 概述
段是具有相同属性的一段内容。
链接器ld用于把多个objectfile(partial program)合并为一个可执行文件。as生成的目标文件(partialprogram)都假定从地址0开始,ld为其指定最终的地址。
链接器ld把objectfile中的每个section都作为一个整体,为其分配运行的地址(memorylayout),这个过程就是重定位(relocation)。
as所产生的一个目标文件至少有text、data和bss这3个段,每个段都可以为空。如果为COFF或ELF格式的目标文件,as还可以根据源文件中使用'.section'伪指令所指定的任意名字的段。源文件中使用'.text'或'.data'所指定的内容会分别分配到text和data段中。源文件中的这些段都属于input section。
在目标文件中,textsection从地址0开始,随后是data section,最后是bss section(这与ARMADS中描述的映像文件的RO、RW和ZI段的排列是一致的)。
2. Linker Sections
ld处理下面4类section:
(1)named sections, text section 和data section
这些段存放着你的程序。程序运行时,textsection是unalterable的(RO段),包含了指令、常量等;datasection则是alterable的(RW段),例如C变量就放在这里。
(2)bss section
该段实际为ZI段,全为0,用于存放未初始化的变量。
(3)absolute section
该段中的地址0总是被“重定位”到运行时的地址0。当需要指定一个地址不希望在ld重定位时被改变时,这很有用。这时我们也说absoluteaddress是unrelocateable的。
(4)undefined section
其他目标文件的地址信息。
3. Sub-section
汇编程序最后变为text和data两个段(当然还有自命名的段)。同一属性和名字的段可以分布在程序中的多个地方,这可以使用数字来对subsection编号来实现(0到8192)。例如,你想把text段中的code放在一起,把常量放在一起,最后再组成textsection;这时,你可以把code使用'.text 0'标记,把常量使用'.text 1'标记即可。