深入理解计算机系统——程序模块的链接(一)

mac2022-06-30  27

链接概述

链接(linking)是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或被拷贝)到存储并执行。

链接时机 1.编译时,源代码被翻译成机器代码时。 2.加载时,程序被加载器加载到存储器并执行时。 3.甚至执行于运行时,由应用程序执行。

早期的计算机系统中,链接是手动执行的;现代的系统中,链接是由叫做链接器(linker)的程序自动执行的。

链接器的关键角色:使分离编译(separate compilation)成为可能。即:将一个很大的应用程序分解为很多小的模块,这些模块可以独立修改和编译。即各司其职,当有地方需要修改时就不必重新编译所有文件,只需编译一个小模块,并重新链接应用。

初识ELF文件

目标文件是按照特定的目标文件格式来组织的,每个系统的目标文件格式都不相同。现代x86-64Linux和Unix系统使用可执行可链接格式(Executable and Linkable Format,ELF),这也是学习我当前学习深入理解计算机系统时最需要了解的。 维基百科中描述:

在计算机科学中,ELF文件是一种用于可执行文件、目标文件、共享库和核心转储(core dump)的标准文件格式。其中核心转储是指: 操作系统在进程收到某些信号而终止时,将此时进程地址空间的内容以及有关进程状态的其他信息写出的一个磁盘文件。这种信息往往用于调试。

一、ELF文件类型

通俗点说由汇编器和链接器生成的文件都属于ELF文件。通常我们接触的ELF文件主要有以下三类: 可重定位目标文件(relocatable file) 包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件。 可执行目标文件(excutable file)包含二进制代码和数据,其形式可以被直接拷贝到存储器并执行。 共享目标文件(shared object file)一种特殊类型的可重定位目标文件,可以在加载或者运行地被动态地加载到存储器并链接。 总之,ELF文件是一种文件格式,下面我们来介绍ELF文件的格式规则。

二、可重定位目标文件格式

图片来源:《深入理解计算机系统》

图7-3展示了一个典型的ELF可重定位目标文件的格式。

ELF头(ELF header)以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。 夹在ELF头和节头部表之间都是节。一个典型的ELF可重定位目标文件包含以下几个节:

.text:已编译程序的机器代码。.rodata:只读数据,比如printf语句中的格式串和开关语句的跳转表。.data:已初始化的全局C变量。.bss:未初始化的全局和静态C变量,以及所有初始化为0的全局或静态变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。.symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。.rel.text:一个.text节中位置的列表,当链接器把这个目标文件和其他文件结合时,需要修改这些位置。.rel.data:被模块引用或定义的任何全局变量的重定位信息。.debug:一个调试符号表,其条目是程序中定义的局部变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。只有以-g选项调用编译驱动程序时才会得到这张表。.line:原始C源程序中的行号和.text节中机器指令之间的映射。.strtab:一个字符串表,其内容包括:.symtab和.debug节中的符号表,以及节头部中的节名字。字符串表就是以null结尾的字符串序列。

三、实践

为了进一步加深对ELF文件整体结构的理解,去一个64bit的Linux上可执行文件China来实践(这里借助这个程序庆祝祖国七十周年生日快乐!!!嘻嘻)

//China.c #include<stdio.h> int main() { int i,count=0; for(i=1949;i<2019;i++) { count++; } printf("China %d years Happy Birthday!\n",count;) }

写好程序后,在终端用gcc指令生成可重定位目标文件China.o,并运行试试看结果。 PS:-o生成可执行文件并确定输出文件名,-c生成二进制目标文件且不链接成可执行文件。 然后,使用readelf工具查看其ELF Header信息:

$ readelf -h China ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: REL(可重定位文件) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x0 Start of program headers: 0(bytes into file) Start of section headers: 784(bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 0(bytes) Number of program headers: 0 Size of section headers: 64 (bytes) Number of section headers: 13 Section header string table index: 12

从ELF Header中我们得到如下信息:

ELF Header大小(Size of this header)为64 byteProgram Header Table的起始地址(Start of program headers)是0byte,其内部共有0个(Number of program headers)Program Header,每个大小为64 byte(Size of program headers)Section Header Table的起始地址(Start of section headers)是784byte,其内部共有13个(Number of section headers)Section Header,每个大小为64 byte(Size of this header)

其次,使用redelf命令读取section header table信息:

$ readelf -S China //由于太多,直接给截图吧 *==

显然,各个section正好和下一个section首尾相接。然后,通过ls -l China.o命令查看该二进制文件的大小,得到该文件大小为1616byte,而Section Header Table的end_addr 0x650换算成十进制大小正好是1616byte。从而证明我们上述的分析是没问题的。

四、总结

ELF文件包括可重定位文件、可执行文件、共享库文件。其基本结构包括ELF Header,Program Header Table,Sections和Section Header Table。其中不同的sections组成了不同的segment。 ELF Header描述了该ELF文件中Program Header Table的位置、大小,Section Header Table的位置、大小和程序执行的入口地址等信息。 Program Header Table描述了各个segment的类型、虚拟地址、相对文件的偏移地址等信息。 Section Header Table描述了各个section的名字、类型、相对文件的偏移地址等信息。

最新回复(0)