关于ELF可重定位目标文件的实例说明(CSAPP)

mac2026-06-10  17

运行环境: Linux系统 gcc编译器 参考书籍: 《Computer Systems:A Programmer’s Perspective》(《深入理解计算机系统》) 主要内容: ELF可重定位目标文件(.o文件),主要讲解ELF头部信息,节头部表,及符号表 运行实例: swap.c、main.c(main.c引用swap.c) ELF头部信息,节头部表,及符号表在代码区后面有相应注释,注意查看 几个表是相互关联的,最好结合起来查看! swap.c

extern int buf[]; int *bufp0=&buf[0]; static int *bufp1; void swap() { int temp; bufp1=&buf[1]; temp=*bufp0; *bufp0=*bufp1; *bufp1=temp; }

main.c

int buf[2]={1,2}; void swap(); int main() { swap(); return 0; }

在Unix系统上从源文件到目标文件的转化是由编译器驱动程序完成的 在这里我们主要讲解 .o文件 相关信息 典型的ELF可重定位目标文件的格式:

一、查看ELF头(ELF header):

-$ readelf -h main.o

ELF文件头结构定义在“/usr/include/elf.h”头文件下,ELF文件有32位版本和64位版本,故其头文件结构也有32位结构和64位结构,分别定义为Elf32_Ehdr和Elf64_Ehdr。两种版本文件内容一样,只是有些成员的大小不一样。以下是32位版本的文件头结构Elf32_Ehdr。

ELF header/格式代码如下: #define EI_NIDENT 16   typedef struct{   unsigned char e_ident[EI_NIDENT];   Elf32_Half e_type; //它标识的是该文件的类型。   Elf32_Half e_machine; //表明运行该程序需要的体系结构。   Elf32_Word e_version; // 表示文件的版本。   Elf32_Addr e_entry; //程序的入口地址。   Elf32_Off e_phoff; //表示Program header table 在文件中的偏移量   Elf32_Off e_shoff; //表示Section header table 在文件中的偏移量   Elf32_Word e_flags; // 对IA32而言,此项为0。   Elf32_Half e_ehsize; //表示ELF header大小   Elf32_Half e_phentsize; //表示Program header table中每一个条目的大小。   Elf32_Half e_phnum; //表示Program header table中有多少个条目。   Elf32_Half e_shentsize; // 表示Section header table中的每一个条目的大小   Elf32_Half e_shnum; //表示Section header table中有多少个条目。   Elf32_Half e_shstrndx; //包含节名称的字符串是第几个节(从零开始计数   }Elf32_Ehdr; ELF 头: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 // Magic 魔数,用来指名该文件是一个 ELF 目标文件。第一个字节 7F 是个固定的数;后面的 3 个字节正是 E, L, F 三个字母的 ASCII 形式 类别: ELF64 //64位的ELF文件格式 数据: 2 补码,小端序 (little endian) //数据按二进制补码形式给出并由小端模式存放 版本: 1 (current) // ELF文件头版本号为1 OS/ABI: UNIX - System V //指出操作系统类型,此条详情见https://www.cnblogs.com/DragonStart/p/7524995.html ABI 版本: 0 // ABI版本号 类型: REL (可重定位文件)//文件类型 1.可重定位目标文件 2.可执行目标文件 3.共享目标文件 系统架构: Advanced Micro Devices X86-64 //机器平台类型 版本: 0x1 //当前目标文件的版本号 入口点地址: 0x0//入口点地址指当程序真正执行起来的时候,其第1条要运行的指令地址.o文件的ELF文件是链接视图不是执行视图,不会进行执行,所以地址为0 程序头起点: 0 (bytes into file)//.o文件的ELF格式不存在程序头部,所以起点是0(a.out文件中存在) Start of section headers: 680 (bytes into file)//给出节头表(Section header table)的起始位置,从文件地址为0 的位置数680个字节的位置(0x2a8) 标志: 0x0//是一个与处理器相关联的标志,x86 平台上该处为 0 本头的大小: 64 (字节)//ELF header大小为64(0x40) 程序头大小: 0 (字节)//.o文件误无程序头表,因此大小为0 Number of program headers: 0//.o文件误无程序头表,因此表项个数也为0 节头大小: 64 (字节)// 这里每个 节头大小为64个字节(节头表大小一共12*64=768字节 0x300) 节头数量: 12//一共有12个节头,由起始位置和大小可完全确定节头表在文件中的位置 字符串表索引节头: 11//节头表会给出.text .data.bss 等的位置,这里指.strtab节的位置索引为11

二、查看节头表(Section header table):

-$ readelf -S main.o 以下是32位系统对应的数据结构:

地址:全为0,.o可重定位文件是链接视图不是执行视图,不会加载执行,所以每个节对应起始虚拟地址都是0旗标:表示访问属性(可执行,可读等) There are 12 section headers, starting at offset 0x2a8: //共12个节头,起始位置为0x2a8,与ELF头信息对应! 节头: [] 名称 类型 地址 偏移量 大小 全体大小 旗标 链接 信息 对齐 [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS 0000000000000000 00000040 0000000000000015 0000000000000000 AX 0 0 1 [ 2] .rela.text RELA 0000000000000000 00000218 0000000000000018 0000000000000018 I 9 1 8 [ 3] .data PROGBITS 0000000000000000 00000058 0000000000000008 0000000000000000 WA 0 0 8 [ 4] .bss NOBITS 0000000000000000 00000060 0000000000000000 0000000000000000 WA 0 0 1 [ 5] .comment PROGBITS 0000000000000000 00000060 000000000000002b 0000000000000001 MS 0 0 1 [ 6] .note.GNU-stack PROGBITS 0000000000000000 0000008b 0000000000000000 0000000000000000 0 0 1 [ 7] .eh_frame PROGBITS 0000000000000000 00000090 0000000000000038 0000000000000000 A 0 0 8 [ 8] .rela.eh_frame RELA 0000000000000000 00000230 0000000000000018 0000000000000018 I 9 7 8 [ 9] .symtab SYMTAB 0000000000000000 000000c8 0000000000000120 0000000000000018 10 8 8 [10] .strtab STRTAB 0000000000000000 000001e8 000000000000002c 0000000000000000 0 0 1 [11] .shstrtab STRTAB 0000000000000000 00000248 0000000000000059 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), l (large), p (processor specific)

下表对应于main.o的ELF存储结构:

三、查看符号表(.symtab):

readelf -s main.o

补充知识 • Global symbols(模块内部定义的全局符号) – 由模块m定义并能被其他模块引用的符号。例如,非static C函数和非static的C全局变量(指不带static的全局变量) 如,main.c 中的全局变量名buf • External symbols(外部定义的全局符号) – 由其他模块定义并被模块m引用的全局符号 如,main.c 中的函数名swap • Local symbols(本模块的局部符号) – 仅由模块m定义和引用的本地符号。例如,在模块m中定义的带static的C函数和全局变量 如,swap.c 中的static变量名bufp1

.symtab结构数组: 其他 Ndx: ABS表示不该被重定位、UND表示未定义、COM表示未初始化数据(.bss) Bind: 绑定属性:全局符号、局部符号 Type: 符号类型:函数、数据、源文件、节、未知 Value: .o.文件中是偏移量、a.out文件中是虚拟地址 main.o

Symbol table '.symtab' contains 12 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 4: 0000000000000000 0 SECTION LOCAL DEFAULT 4 5: 0000000000000000 0 SECTION LOCAL DEFAULT 6 6: 0000000000000000 0 SECTION LOCAL DEFAULT 7 7: 0000000000000000 0 SECTION LOCAL DEFAULT 5 8: 0000000000000000 8 OBJECT GLOBAL DEFAULT 3 buf 9: 0000000000000000 21 FUNC GLOBAL DEFAULT 1 main 10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_ 11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND swap int buf[2]={1,2};//buf 已定义的全局符号 强符号 void swap();//swap函数 全局符号(外部符号 )弱符号 int main()//main函数 全局符号 强符号 { swap(); return 0; } Num: Value Size Type Bind Vis Ndx Name 8: 0000000000000000 8 OBJECT GLOBAL DEFAULT 3 buf

buf是main.o中第3节((Section header table).data)偏移为0的符号,是全局变量,占8B

Num: Value Size Type Bind Vis Ndx Name 9: 0000000000000000 21 FUNC GLOBAL DEFAULT 1 main

main是第1节((Section header table).text)偏移为0的符号,是全局函数,占21B

Num: Value Size Type Bind Vis Ndx Name 11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND swap

swap是未定义的符号,不知道类型和大小,是在其他模块(swap.c)定义的全局符号 swap.o

swap.o节头表 There are 14 section headers, starting at offset 0x370: 节头: [] 名称 类型 地址 偏移量 大小 全体大小 旗标 链接 信息 对齐 [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS 0000000000000000 00000040 000000000000003f 0000000000000000 AX 0 0 1 [ 2] .rela.text RELA 0000000000000000 00000248 0000000000000090 0000000000000018 I 11 1 8 [ 3] .data PROGBITS 0000000000000000 0000007f 0000000000000000 0000000000000000 WA 0 0 1 [ 4] .bss NOBITS 0000000000000000 00000080 0000000000000008 0000000000000000 WA 0 0 8 [ 5] .data.rel PROGBITS 0000000000000000 00000080 0000000000000008 0000000000000000 WA 0 0 8 [ 6] .rela.data.rel RELA 0000000000000000 000002d8 0000000000000018 0000000000000018 I 11 5 8 [ 7] .comment PROGBITS 0000000000000000 00000088 000000000000002b 0000000000000001 MS 0 0 1 [ 8] .note.GNU-stack PROGBITS 0000000000000000 000000b3 0000000000000000 0000000000000000 0 0 1 [ 9] .eh_frame PROGBITS 0000000000000000 000000b8 0000000000000038 0000000000000000 A 0 0 8 [10] .rela.eh_frame RELA 0000000000000000 000002f0 0000000000000018 0000000000000018 I 11 9 8 [11] .symtab SYMTAB 0000000000000000 000000f0 0000000000000138 0000000000000018 12 10 8 [12] .strtab STRTAB 0000000000000000 00000228 000000000000001d 0000000000000000 0 0 1 [13] .shstrtab STRTAB 0000000000000000 00000308 0000000000000068 0000000000000000 0 0 1 Symbol table '.symtab' contains 13 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS swap.c 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 4: 0000000000000000 0 SECTION LOCAL DEFAULT 4 5: 0000000000000000 0 SECTION LOCAL DEFAULT 5 6: 0000000000000000 8 OBJECT LOCAL DEFAULT 4 bufp1 7: 0000000000000000 0 SECTION LOCAL DEFAULT 8 8: 0000000000000000 0 SECTION LOCAL DEFAULT 9 9: 0000000000000000 0 SECTION LOCAL DEFAULT 7 10: 0000000000000000 8 OBJECT GLOBAL DEFAULT 5 bufp0 11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND buf 12: 0000000000000000 63 FUNC GLOBAL DEFAULT 1 swap extern int buf[];//buf全局符号(外部符号) 弱符号 int *bufp0=&buf[0];//bufp0 已定义的全局符号 强符号 static int *bufp1;//bufp1 本地局部符号 void swap()//swap函数 全局符号 强符号 { int temp; bufp1=&buf[1]; temp=*bufp0; *bufp0=*bufp1; *bufp1=temp; } Num: Value Size Type Bind Vis Ndx Name 6: 0000000000000000 8 OBJECT LOCAL DEFAULT 4 bufp1

bufp1是第4节((Section header table).bss)偏移为0的符号,是静态局部变量,指针类型占8B

Num: Value Size Type Bind Vis Ndx Name 10: 0000000000000000 8 OBJECT GLOBAL DEFAULT 5 bufp0

bufp0是第5节((Section header table).data.rel)偏移为0的符号,是全局变量,指针类型占8B

Num: Value Size Type Bind Vis Ndx Name 11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND buf

buf是未定义的符号,不知道类型和大小,是在其他模块(main.c)定义的全局符号

Num: Value Size Type Bind Vis Ndx Name 12: 0000000000000000 63 FUNC GLOBAL DEFAULT 1 swap

swap是第1节((Section header table).text)偏移为0的符号,是全局函数,占63B

最新回复(0)