a.out目标文件格式

上传人:小** 文档编号:90594467 上传时间:2019-06-13 格式:PDF 页数:6 大小:295.26KB
返回 下载 相关 举报
a.out目标文件格式_第1页
第1页 / 共6页
a.out目标文件格式_第2页
第2页 / 共6页
a.out目标文件格式_第3页
第3页 / 共6页
a.out目标文件格式_第4页
第4页 / 共6页
a.out目标文件格式_第5页
第5页 / 共6页
点击查看更多>>
资源描述

《a.out目标文件格式》由会员分享,可在线阅读,更多相关《a.out目标文件格式(6页珍藏版)》请在金锄头文库上搜索。

1、 1 2.1 Linux 0.11 支持的目标文件 为了生成内核代码文件,Linux 0.11 使用了两种编译器。第一种是汇编编译器 as86 和相应的链接程 序 ld86。 它们专门用于编译和链接运行在实地址模式下的 16 位内核引导扇区程序 bootsect.s 和设置程序 setup.s。第二种是 GNU 的汇编器 gas 和 C 语言编译器 gcc 以及相应的链接程序 gld。编译器用于为源程 序文件产生含有生成的二进制代码和数据的目标文件。链接程序用于对相关的所有目标文件进行组合处 理,形成一个可被内核加载执行的目标文件,即可执行文件。 下面我们首先简单说明编译器产生的目标文件结构,

2、然后描述链接器如何把需要链接在一起的目标文 件模块组合在一起,以生成二进制可执行映像文件或一个大的模块文件。最后说明 Linux 0.11 内核二进 制代码文件 Image 的生成原理和过程。有关目标文件和链接程序的基本工作原理可参见 John R. Levine 著的Linkers / 段内需要重定位的地址。 unsigned int r_symbolnum:24; / 含义与 r_extern 有关。指定符号表中一个符号或者一个段。 unsigned int r_pcrel:1; / 1 比特。PC 相关标志。 unsigned int r_length:2; / 2 比特。指定要被重定位

3、字段长度(2 的次方)。 unsigned int r_extern:1; / 外部标志位。1 - 以符号的值重定位。0 - 以段的地址重定位。 unsigned int r_pad:4; / 没有使用的 4 个比特位,但最好将它们复位掉。 ; 重定位项的功能有两个。一是当代码段被重定位到一个不同的基地址处时,重定位项则用于指出需要 修改的地方。二是在模块文件中存在对未定义符号的引用时,当此未定义符号最终被定义时链接程序就可 以使用相应重定位项对符号的值进行修正。由上面重定位记录项的定义可以看出,每个记录项含有模块文 件代码部分(代码段)和数据部分(数据段)中需要重定位处长度为 4 字节的地址

4、以及规定如何具体进行 重定位操作的信息。地址字段 r_address 是指可重定位项从代码段或数据段开始算起的偏移值。2 比特的 长度字段 r_length 指出被重定位项的长度,0 到 3 分别表示被重定位项的宽度是 1 字节、2 字节、4 字节 或 8 字节。标志位 r_pcrel 指出被重定位项是一个“PC 相关的”的项,即它作为一个相对地址被用于指令 当中。外部标志位 r_extern 控制着 r_symbolnum 的含义,指明重定位项参考的是段还是一个符号。如果 该标志值是 0,那么该重定位项是一个普通的重定位项,此时 r_symbolnum 字段指定是在哪个段中寻址定 位。如果该

5、标志是 1,那么该重定位项是对一个外部符号的引用,此时 r_symbolnum 指定目标文件中符号 表中的一个符号,需要使用符号的值进行重定位。 目标文件的最后一部分是符号表和相关的字符串表。符号表记录项的结构如下所示。 struct nlist union char *n_name; / 字符串指针, struct nlist *n_next; / 或者是指向另一个符号项结构的指针, long n_strx; / 或者是符号名称在字符串表中的字节偏移值。 n_un; unsigned char n_type; / 该字节分成 3 个字段,参见 a.out.h 文件 146-154 行。 ch

6、ar n_other; / 通常不用。 short n_desc; / unsigned long n_value; / 符号的值。 ; 由于 GNU gcc 编译器允许任意长度的标识符,因此标识符字符串都位于符号表后的字符串表中。每个 符号表记录项长度为 12 字节,其中第一个字段给出了符号名字符串(以 null 结尾)在字符串表中的偏移 位置。类型字段 n_type 指明了符号的类型。该字段的最后一个比特位用于指明符号是否是外部的(全局 的) 。如果该位为 1 的话,那么说明该符号是一个全局符号。链接程序并不需要非全局符号信息,但可供 调试程序使用。n_type 字段的其余比特位用来指明符

7、号类型。a.out.h 头文件中定义了这些类型值常量符 号。符号的主要的类型包括: 4 ? text、data 或 bbs 指明是本模块文件中定义的符号。此时符号的值是模块中该符号的可重定位 地址。 ? abs 指明符号是一个绝对的(固定的)不可重定位的符号。符号的值就是该固定值。 ? undef 指明是一个本模块文件中未定义的符号。此时符号的值通常是 0。 但作为一种特殊情况,编译器能够使用一个未定义的符号来要求链接程序为指定的符号名保留一块存 储空间。如果一个未定义的外部(全局)符号具有非零值,那么对链接程序而言该值就是程序希望指定符 号寻址的存储空间的大小值。在链接操作期间,如果该符号确

8、实没有定义,那么链接程序就会在 bss 段中 为该符号名建立一块存储空间,空间的大小是所有被链接模块中该符号值最大的一个。这个就是 bss 段中 所谓的公共块(Common block)定义,主要用于支持未初始化的外部(全局)数据。例如程序中定义的未 初始化的数组。如果该符号在任意一个模块中已经被定义了,那么链接程序就会使用该定义而忽略该值。 在 Linux 0.11 系统中,我们可以使用 objdump 命令来查看模块文件或执行文件中文件头结构的具体 值。例如,下面列出了 hello.o 目标文件及其执行文件中文件头的具体值。 /usr/root# gcc -c -o hello.o hel

9、lo.cgcc -c -o hello.o hello.c /usr/root# gcc -o hello hello.ogcc -o hello hello.o /usr/root# /usr/root# objdumpobjdump Usage: objdump -hnrt +header +nstuff +relocation +symbols objfile. /usr/root# /usr/root# objdump -h hello.oobjdump -h hello.o hello.o: magic: 0x107 (407)machine type: 0 flags: 0x0 t

10、ext 0x28 data 0x0 bss 0x0 nsyms 3 entry 0x0 trsize 0x10 drsize 0x0 /usr/root# /usr/root# objdump -h helloobjdump -h hello hello: magic: 0x10b (413)machine type: 0 flags: 0x0 text 0x3000 data 0x1000 bss 0x0 nsyms 141 entry 0x0 trsize 0x0 drsize 0x0 /usr/root# 可以看出,hello.o 模块文件的魔数是 0407(OMAGIC) ,除了文件头

11、结构以外,还包括一个长度为 0x28 字 节的代码段和一个具有 3 个符号项的符号表以及长度为 0x10 字节的代码段重定位信息。其余各段的长度 均为 0。对应的执行文件 hello 的魔数是 0413(ZMAGIC) ,代码段和数据段的长度分别为 0x3000 和 0x1000 字节,并带有包含 141 个项的符号表。我们可以使用命令 strip 删除执行文件中的符号表信息。例如下面 我们删除了 hello 执行文件中的符号信息。可以看出 hello 执行文件的符号表长度变成了 0,并且 hello 文件的长度也从原来的 20591 字节减小到 17412 字节。 /usr/root# ll

12、 helloll hello -rwx-x-x 1 root 4096 20591 Nov 14 18:30 hello /usr/root# objdump -h helloobjdump -h hello hello: magic: 0x10b (413)machine type: 0flags: 0x0text 0x3000 data 0x1000 bss 0x0 nsyms 141 entry 0x0 trsize 0x0 drsize 0x0 /usr/root# strip hellostrip hello /usr/root# ll helloll hello -rwx-x-x

13、1 root 4096 17412 Nov 14 18:33 hello /usr/root# objdump -h helloobjdump -h hello hello: magic: 0x10b (413)machine type: 0flags: 0x0text 0x3000 data 0x1000 bss 0x0 5 nsyms 0 entry 0x0 trsize 0x0 drsize 0x0 /usr/root# 磁盘上 a.out 执行文件的各部分在进程逻辑地址空间中的对应关系见图 2 所示。Linux 0.11 系统中进 程的逻辑空间大小是 64MB。对于 ZMAGIC 类型

14、的 a.out 执行文件,它的代码部分的长度是内存页面的整数 倍。由于 Linux 0.11 内核使用需求页(Demand-paging)技术,即在一页代码实际要使用的时候才被加载 到物理内存页面中,而在进行加载操作的 fs/execve()函数中仅仅为其设置了分页机制的页目录项和页表 项,因此需求页技术可以加快程序的加载的速度。 图 2 a.out 执行文件映射到进程逻辑地址空间 图中 bss 是进程的未初始化数据部分,用于存放静态的未初始化数据。在开始执行程序时 bss 的第 1 页内存会被设置为全 0。图中 heap 是堆空间部分,用于分配进程在执行过程中动态申请的内存空间。 2.1.2

15、 链接程序输出 链接程序对输入的一个或多个模块文件以及相关的库函数模块进行处理,最终生成相应的二进制执行 文件或者是一个所有模块组合成的大模块文件。在这个过程中,链接程序的首要任务是给执行文件(或者 输出的模块文件)进行存储空间分配操作。一旦存储位置确定,链接程序就可以继续执行符号邦定操作和 代码修正操作。因为模块文件中定义的大多数符号与文件中的存储位置有关,所以在符号对应的位置没有 确定下来之前符号是没有办法解析的。 每个模块文件中包括几种类型的段,链接程序的第二个任务就是把所有模块中相同类型的段组合连接 在一起,在输出文件中为指定段类型形成单一一个段。例如,链接程序需要把所有输入模块文件中

16、的代码 段合并成一个段放在输出的执行文件中。 对于 a.out 格式的模块文件来说,由于段类型是预先知道的,因此链接程序对 a.out 格式的模块文件 进行存储分配比较容易。例如,对于具有两个输入模块文件和需要连接一个库函数模块的情况,其存储分 配情况见图 3 所示。每个模块文件都有一个代码段(text) 、数据段(data)和一个 bss 段,也许还会有 一些看似外部(全局)符号的公共块。链接程序会收集每个模块文件包括任何库函数模块中的代码段、数 据段和 bss 段的大小。在读入并处理了所有模块之后,任何具有非零值的未解析的外部符号都将作为公共 块来看待,并且把它们分配存储在 bss 段的末尾处。 指定长度 a.out 执行文件 header 代码 text 数据

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 商业/管理/HR > 管理学资料

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号