首先我们先看两段代码:
a.c
extern int shared; int main(){ int a=100; swap(&a,&shared); }
b.c
int shared=1; void swap(int* a,int* b){ *a^=*b^=*a^=*b; }
gcc -c a.c b.c 得到a.o 与b.o
1、查看a.o:
[root@tlinux misc]# objdump -h a.o a.o: file format elf64-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000027 0000000000000000 0000000000000000 00000040 2**0 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE 1 .data 00000000 0000000000000000 0000000000000000 00000067 2**0 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000000 0000000000000000 0000000000000000 00000067 2**0 ALLOC 3 .comment 0000002e 0000000000000000 0000000000000000 00000067 2**0 CONTENTS, READONLY 4 .note.GNU-stack 00000000 0000000000000000 0000000000000000 00000095 2**0 CONTENTS, READONLY 5 .eh_frame 00000038 0000000000000000 0000000000000000 00000098 2**3 CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
2、查看b.o:
[root@tlinux misc]# objdump -h b.o b.o: file format elf64-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .text 0000004a 0000000000000000 0000000000000000 00000040 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .data 00000004 0000000000000000 0000000000000000 0000008c 2**2 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000000 0000000000000000 0000000000000000 00000090 2**0 ALLOC 3 .comment 0000002e 0000000000000000 0000000000000000 00000090 2**0 CONTENTS, READONLY 4 .note.GNU-stack 00000000 0000000000000000 0000000000000000 000000be 2**0 CONTENTS, READONLY 5 .eh_frame 00000038 0000000000000000 0000000000000000 000000c0 2**3 CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
3、链接之前,VMA与LMA都是0,即目标文件的虚拟空间地址与装载地址都无效。经过链接ld过程,才会给链接文件分配虚拟地址空间。
ld a.o b.o -e main -o ab
链接过程,合并了a.o与b.o的代码段、数据段,具体的位置与大小如下所示:
具体信息如下所示
[root@tlinux misc]# objdump -h ab ab: file format elf64-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000071 00000000004000e8 00000000004000e8 000000e8 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .eh_frame 00000058 0000000000400160 0000000000400160 00000160 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .data 00000004 0000000000601000 0000000000601000 00001000 2**2 CONTENTS, ALLOC, LOAD, DATA 3 .comment 0000002d 0000000000000000 0000000000000000 00001004 2**0 CONTENTS, READONLY
同时,我们可以看一下,链接后,各个源文件的符号表也合成一张全局符号表,且符号表中表明各个符号的虚拟空间位置:
readelf -s ab
Symbol table '.symtab' contains 13 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000004000e8 0 SECTION LOCAL DEFAULT 1 2: 0000000000400160 0 SECTION LOCAL DEFAULT 2 3: 0000000000601000 0 SECTION LOCAL DEFAULT 3 4: 0000000000000000 0 SECTION LOCAL DEFAULT 4 5: 0000000000000000 0 FILE LOCAL DEFAULT ABS a.c 6: 0000000000000000 0 FILE LOCAL DEFAULT ABS b.c 7: 000000000040010f 74 FUNC GLOBAL DEFAULT 1 swap 8: 0000000000601000 4 OBJECT GLOBAL DEFAULT 3 shared 9: 0000000000601004 0 NOTYPE GLOBAL DEFAULT 3 __bss_start 10: 00000000004000e8 39 FUNC GLOBAL DEFAULT 1 main 11: 0000000000601004 0 NOTYPE GLOBAL DEFAULT 3 _edata 12: 0000000000601008 0 NOTYPE GLOBAL DEFAULT 3 _end
4、接下来介绍一下符号的解析与重定位:
首先查看一下未重定位之前,a.o中是怎么处理shared变量与swap函数的:
利用 objdump -d a.o查看一下,a.o的反汇编代码
[root@tlinux misc]# objdump -d a.o a.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <main>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 48 83 ec 10 sub $0x10,%rsp 8: c7 45 fc 64 00 00 00 movl $0x64,-0x4(%rbp) f: 48 8d 45 fc lea -0x4(%rbp),%rax 13: be 00 00 00 00 mov $0x0,%esi //00 00 00 00 shared 未给地址 18: 48 89 c7 mov %rax,%rdi 1b: b8 00 00 00 00 mov $0x0,%eax 20: e8 00 00 00 00 callq 25 <main+0x25> //swap函数也未给地址 25: c9 leaveq 26: c3 retq
5、经过ld链接之后,在最后文件ab中,shared 与 swap都知道了地址。因为链接过程会分配虚拟地址,那么根据前面第三点链接过后的信息,可以知道各个段的虚拟地址,那么
其中各个符号的地址也会知道。那么,经过连接之后,ab中的代码反汇编结果如何,如下所示:
[root@tlinux misc]# objdump -d ab ab: file format elf64-x86-64 Disassembly of section .text: 00000000004000e8 <main>: 4000e8: 55 push %rbp 4000e9: 48 89 e5 mov %rsp,%rbp 4000ec: 48 83 ec 10 sub $0x10,%rsp 4000f0: c7 45 fc 64 00 00 00 movl $0x64,-0x4(%rbp) 4000f7: 48 8d 45 fc lea -0x4(%rbp),%rax 4000fb: be 00 10 60 00 mov $0x601000,%esi //00 60 10 00 详见ab文件的数据段 400100: 48 89 c7 mov %rax,%rdi 400103: b8 00 00 00 00 mov $0x0,%eax 400108: e8 02 00 00 00(相对下一行命令偏移 02) callq 40010f <swap> // 由下面swap在 40010f处可知,a.o b.o链接到一起后,swap的函数虚拟地址可知 call 命令: 40010d+00000002 40010d: c9 leaveq 40010e: c3 retq 000000000040010f <swap>: 40010f: 55 push %rbp 400110: 48 89 e5 mov %rsp,%rbp 400113: 48 89 7d f8 mov %rdi,-0x8(%rbp) 400117: 48 89 75 f0 mov %rsi,-0x10(%rbp) 40011b: 48 8b 45 f8 mov -0x8(%rbp),%rax 40011f: 8b 10 mov (%rax),%edx 400121: 48 8b 45 f0 mov -0x10(%rbp),%rax 400125: 8b 08 mov (%rax),%ecx 400127: 48 8b 45 f8 mov -0x8(%rbp),%rax 40012b: 8b 30 mov (%rax),%esi 40012d: 48 8b 45 f0 mov -0x10(%rbp),%rax 400131: 8b 00 mov (%rax),%eax 400133: 31 c6 xor %eax,%esi 400135: 48 8b 45 f8 mov -0x8(%rbp),%rax 400139: 89 30 mov %esi,(%rax) 40013b: 48 8b 45 f8 mov -0x8(%rbp),%rax 40013f: 8b 00 mov (%rax),%eax 400141: 31 c1 xor %eax,%ecx 400143: 48 8b 45 f0 mov -0x10(%rbp),%rax 400147: 89 08 mov %ecx,(%rax) 400149: 48 8b 45 f0 mov -0x10(%rbp),%rax 40014d: 8b 00 mov (%rax),%eax 40014f: 31 c2 xor %eax,%edx 400151: 48 8b 45 f8 mov -0x8(%rbp),%rax 400155: 89 10 mov %edx,(%rax) 400157: 5d pop %rbp 400158: c3 retq
6、重定位表信息:
对于可重定位文件,必须包含重定位表,用来描述如何修改相应的段。可以利用objdump -r a.o查看重定位表
[root@tlinux misc]# objdump -r a.o a.o: file format elf64-x86-64 RELOCATION RECORDS FOR [.text]: OFFSET TYPE VALUE 0000000000000014 R_X86_64_32 shared 0000000000000021 R_X86_64_PC32 swap-0x0000000000000004
OFFSET指的是需要被重定位的内容在可重定位文件中的位置,看第4点的反汇编内容可知,0x14位置和0x21位置分别为shared 与 swap.需要被重定位