网站建设与管理维护 大学论文,网站开发后端,98同城招聘网信息,厦门公司注册网站8种机械键盘轴体对比本人程序员#xff0c;要买一个写代码的键盘#xff0c;请问红轴和茶轴怎么选#xff1f;本文主要阐述 Linux 的目标文件(有可重定位目标文件、可执行目标文件和共享目标文件三种形式)#xff0c;并把重点放在其格式和案例分析上。注#xff1a;一般情…8种机械键盘轴体对比本人程序员要买一个写代码的键盘请问红轴和茶轴怎么选本文主要阐述 Linux 的目标文件(有可重定位目标文件、可执行目标文件和共享目标文件三种形式)并把重点放在其格式和案例分析上。注一般情况下我们说的目标文件专指可重定位目标文件而可执行文件专指可执行目标文件但在本文中为了使概念更加清晰我们会使用这两种文件的全称。按照《程序员的自我修养——链接、装载与库》一书第 3.1 节的说法从广义上看目标文件与可执行文件的格式几乎是一模一样的所以我们可以广义地将目标文件与可执行文件看成是一种类型的文件。即把它们统称为 ELF(Executable Linkable Format)文件。实际上可重定位目标文件跟可执行目标文件还是有区别的。目标文件有三种形式可重定位目标文件(.o文件)包含二进制代码和数据其形式可以在编译时与其他可重定位目标文件合并起来创建一个可执行目标文件可执行目标文件包含二进制代码和数据其形式可以被直接拷贝到存储器并执行。[仅为完整性本文不阐述]共享目标文件是一种特殊类型的可重定位目标文件可以在加载或者运行时被动态地加载到存储器并链接。我们先来看一下可重定位目标文件和可执行目标文件生成的过程。可重定位目标文件和可执行目标文件生成的过程源文件经过以下几步生成可重定位目标文件和可执行目标文件预处理(preprocessor)对 #include、#define、#ifdef/#endif、#ifndef/#endif 等宏进行处理编译(compiler)将源码编译为汇编代码汇编(assembler)将汇编代码汇编为可重定位目标代码(文件)链接(linker)将可重定位目标代码链接为可执行目标文件整个过程可用图表示如下图片来源接下来我们分开对可重定位目标文件与可执行目标文件进行阐述。《深入理解计算机系统》一书第 7 章“链接”和《程序员的自我修养——链接、装载与库》一书第 3 章“目标文件里有什么”将作为主要参考资料。可重定位目标文件可重定位目标文件格式可重定位目标文件的典型格式如下图所示图片来源《深入理解计算机系统》其中ELF Header: 包含了描述整个文件的基本属性如 ELF 文件版本、目标机器型号、程序入口地址等。.text已编译程序机器(二进制)代码。.rodata只读数据如 printf 语句中的格式串和开关语句的跳转表。.data已初始化的全局(静态)变量或静态变量。但如果我们对全局变量或静态变量赋值为 0那它会被放到 .bss 段。 (注意全局变量和全局静态变量的区别作用域。).bss未初始化的全局变量或静态变量。该段不占用可重定位目标文件的实际空间但当它被加载到内存中时该段内的变量是要占用内存的。.symtab一个符号表它存放在程序中定义和使用的函数和全局变量的信息但不包含局部变量的条目。.rel.text一个 .text 节中位置的列表当链接器把这个可重定位文件和其他文件结合时需要修改这些位置。一般而言任何调用外部函数或者引用全局变量的指令都需要修改。另一方面调用本地函数的指令不用修改。注意可执行目标文件中并不需要重定位信息因此通常省略除非用户显式地指示链接器包含这些信息。.ret.data被模块引用或定义的任何全局变量的重定位信息。一般而言任何已初始化的全局变量如果它的初始值是一个全局变量地址或者外部定义函数的地址都需要被修改。.debug一个调试符号表其条目是程序中定义的局部变量和类型定义、程序中定义和引用的全局变量以及原始的 C/C 源文件。只有以 -g 选项调用编译驱动程序时才会得到这个表。.line原始 C/C 源程序中的行号和 .text 节中机器指令之间的映射。只有以 -g 选项调用编译驱动程序时才会得到这个表。.strtab一个字符串表其内容包括 .symtab 和 .debug 节中的符号表以及节头部中的节名字。字符串表就是以 null 结尾的字符串序列。Section Header Table描述了 ELF 文件包含的所有段的信息如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其他属性。案例分析接下来我们利用《程序员的自我修养——链接、装载与库》一书第 3 章提供的一个样例来分析下这些段。编译样例样例如下123456789101112131415161718192021222324252627282930 File Name: SimpleSection.c Compile on Linux:gcc -c SimpleSection.c Compile on Windows:cl SimpleSection.c /c /Za*************************************/int (cont char* format, ...);int global_init_var 84;// .dataint global_uninit_var;// .bssvoid func(int i){printf(%dn, i);}int main(){static int static_init_var 85; // .datastatic int static_uninit_var; // .bssint a 1;// Stackint b;// Stackfunc(static_init_var static_uninit_var a b);return 0;}编译该程序可得可重定位目标文件 SimpleSection.o1$ gcc -c SimpleSection.c查看 ELF Header我们可以查看该目标文件的 ELF Header:1$ readelf -h SimpleSection.o并得到如下结果我们可以看到ELF Header 定义了诸多该目标文件的属性。关于各个属性详细信息请参考《程序员的自我修养——链接、装载与库》一书第 3.4.1 节“文件头”。查看 Section Header Table我们还可以继续查看一下该目标文件的 Section Header Table1$ readelf -S SimpleSection.o并得到如下结果结合该图和上边关于 ELF Header 的图我们可以将 SimpleSection.o 的 Section Header Table 及所有段的位置和长度画出来正如我们在前边提到的Section Header Table 描述了 ELF 文件包含的所有段的信息如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其他属性。具体地它是利用了Elf32_Shdr (也称为段描述符)这样一个结构体来描述每一段的属性该结构体长度为 40 个字节。在 SimpleSection.o 文件中总共有 13 个段也就需要 13 个Elf32_Shdr来描述它们总共长度为 13 * 40 0x208 个字节。另外Section Header Table 的第一个元素是无效的Elf32_Shdr它的类型为 NULL。整个 SimpleSection.o 文件的长度为 1336 个字节这跟我们用du命令查看的结果是一致的关于 Section Header Table 详细信息请参考《程序员的自我修养——链接、装载与库》一书第 3.4.2 节“段表”。可执行目标文件可执行目标文件格式可执行目标文件的典型格式如下图所示图片来源《深入理解计算机系统》案例分析我们还接着可重定位文件的例子继续分析。静态链接我们先将 SimpleSection.o 文件静态链接成可执行文件 SimpleSection.static1$ gcc -static SimpleSection.o -o SimpleSection.static再查看下 SimpleSection.static 文件的 Section Header Table1$ readelf -S SimpleSection.static并得到如下结果具体分析过程跟“可重定位目标文件”部分相同。动态链接我们先将 SimpleSection.o 文件动态链接成可执行文件 SimpleSection.dynamic1$ gcc SimpleSection.o -o SimpleSection.dynamic再查看下 SimpleSection.dynamic 文件的 Section Header Table1$ readelf -S SimpleSection.dynamic并得到如下结果重定位段信息值得注意的是上图(动态链接)中的两个属性为重定位表的段.rel.dyn和.rel.plt。它们跟.rel.text或.rel.data并不一样。我们先用readelf -r命令来查看一下可重定位目标文件和可执行目标文件中的可重定位段的区别可重定位目标文件1$ readelf -r SimpleSection.o并得到结果如下可执行目标文件1$ readelf -r SimpleSection.dynamic并得到结果如下比较上边两图我们(从Offset属性)可以知道.rel.text属于普通重定位段由编译器编译产生存在于可重定位目标文件内用于最终可执行目标文件或者动态库的重定位。.rel.dyn和.rel.plt属于动态重定位段由链接器产生存在于可执行目标文件或者动态库内借助这两个段可动态修改对应的.got和.got.plt段从而实现运行时重定位。.rel.dyn对应.got段.rel.plt对应.got.plt。后记本来的打算是先阐述目标文件再说明如何将可执行文件加载到进程虚拟地址空间从而达到叙述虚拟地址空间的目的。很明显我失误了。光是目标文件这么少的知识点我都要写这么长的博文再加上加载和虚拟地址空间可以预计博文将会很长。那时不但读者受不了自己也受不了…所以还是将大的主题继续拆分。关于加载和虚拟地址空间请见下一博文。