之前初步介绍了链接,在这里将会用实例作进一步解释。
main.c代码内容:
/* main.c */
/* $begin main */
int sum(int *a, int n);
int array[2] = {1, 2};
int main()
{
int val = sum(array, 2);
return val;
}
/* $end main */
sum.c代码内容:
/* sum.c */
/* $begin sum */
int sum(int *a, int n)
{
int i, s = 0;
for (i = 0; i < n; i++) {
s += a[i];
}
return s;
}
/* $end sum */
注:这是一个小的运行示例,可以帮助说明链接是如何工作的一些重要知识点
通过读代码,可以知道sum.c文件定义了一个函数sum,功能是实现数组值的相加,而main.c文件定义了一个sum函数,一个全局变量array数组和一个主函数main。在主函数main里引用了sum函数,但是,该.c文件并未对sum函数的内容进行定义。这导致如果单独编译驱动main.c文件时前三个阶段可以顺利进行而因缺少链接无法生成可执行目标文件。因此我们先通过在命令行输入gcc -c main.c sum.c得到这两个模块的可重定位目标文件(main.o和sum.o)然后逐步分析其过程。
gcc -Wall -Og -o prog main.o sum.o
在生成可重定位目标文件后,进行此操作即可得可执行目标文件,这里命名为prog。
其中,
- -Wall表示允许发出gcc提供的所有有用的报警信息
- -Og表示启用全局优化
- -o表示设定输出文件名,不加此选项会默认可执行文件名为a.out
注意此处要加上两个可重定位目标文件才能链接成功生成可执行目标文件。
gcc -Wall -Og -S main.c
- -S表示只对文件进行预处理和编译两个阶段,在这里是让main.c文件形成对应main.s文件,里面是相应的汇编代码,可以用文本编辑器查看
知识补充:目标文件格式
- 目标代码:指编译器和汇编器处理源代码后所生成的机器语言目标代码,即.o文件里的代码
- 目标文件:指包含目标代码的文件,即.o文件
标准的目标文件格式有:
- 在Windows上:PE格式,称为可移植可执行(Portable Executable,简称PE)
- 在Linux等类UNIX上:ELF格式,称为可执行可链接(Executable and Linkable Format,简称ELF)
下面主要介绍ELF格式。
来源:网课-计算机系统基础(一):程序的表示、转换与链接(第十周)
- ELF头:包括16字节标识信息、文件类型(.o,exec,.so)、机器类型(如IA-32)、节头表的偏移、节头表的表项大小以及表项个数并且有指向节头表的指针。
- .text:已编译程序的机器代码,即编译后的代码部分。
- .rodata:只读数据,如printf语句中的格式串和switch跳转表等。
- .data:已初始化的全局变量和静态C变量。注意局部C变量在运行时被保存在栈中,既不出现在.data节中,也不出现在.bss节中。
- .bss:未初始化的全局变量和静态C变量,以及所有被初始化为0的全局或静态变量。在目标文件中仅是占位符,不占据任何实际磁盘空间。区分初始化和非初始化是为了空间效率。并且它仅在节头表里表示长度。
- .symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息,它不包括局部变量。
- .rel.text:一个.text节中位置的列表,有.text节的重定位信息。当链接器把这个目标文件和其他文件组合时,需重新修改代码段的指令中的地址信息。
- .rel.data:被模块引用或定义的所有全局变量的重定位信息,即.data节的重定位信息。
- .debug:一个调试用符号表(gcc -g)。
- .line:原始C源程序中的行号和.text节中机器指令之间的映射。当用-g选项调用编译器驱动程序时,才会得到这张表。
- .strtab:一个字符串表,包含symtab和debug节中符号(是变量名或函数名的字符串)及节名。
- 节头表:每个节的节名、偏移和大小。
objdump -dx main.o
objdump是gcc的工具,可以查看编译后文件的组成。
- -d表示将代码段反汇编
- -x显示所有可用的头信息,包括符号表、重定位入口,使用此选项可以将文件内容做标注,方便我们查看理解
以下是在机器终端上运行的结果:
main.o: 文件格式 elf64-x86-64
main.o
体系结构:i386:x86-64, 标志 0x00000011:
HAS_RELOC, HAS_SYMS
起始地址 0x0000000000000000
节:
Idx Name Size VMA LMA File off Algn
0 .text 00000021 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000008 0000000000000000 0000000000000000 00000068 2**3
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 0000000000000000 0000000000000000 00000070 2**0
ALLOC
3 .comment 0000002b 0000000000000000 0000000000000000 00000070 2**0
CONTENTS, READONLY
4 .note.GNU-stack 00000000 0000000000000000 0000000000000000 0000009b 2**0
CONTENTS, READONLY
5 .eh_frame 00000038 0000000000000000 0000000000000000 000000a0 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 main.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g O .data 0000000000000008 array
0000000000000000 g F .text 0000000000000021 main
0000000000000000 *UND* 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000000 *UND* 0000000000000000 sum
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: be 02 00 00 00 mov $0x2,%esi
d: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 14 <main+0x14>
10: R_X86_64_PC32 array-0x4
14: e8 00 00 00 00 callq 19 <main+0x19>
15: R_X86_64_PLT32 sum-0x4
19: 89 45 fc mov %eax,-0x4(%rbp)
1c: 8b 45 fc mov -0x4(%rbp),%eax
1f: c9 leaveq
20: c3 retq
由反汇编信息可知,开头标注了该结果是在x86-64机器上跑出来的结果,并且由于这是一个可重定位目标文件而非可执行目标文件,因此起始地址并不是明确值而是0。再看节部分,大部分节名都有对应,其中File off指明了每个节在ELF结构中的偏移地址(不是实际地址),Size指明了每个节所占大小,例.data节地址00000068加上长度00000008(均为十六进制数)正好为00000070,即.bss节的地址(注:.text节和.data节之间有空余所以.data节地址不为.text节地址加.text节长)
再下面SYMBOL TABLE显示了符号表,可以看到它存放了赋初值的全局变量array和函数main的信息
最后显示了main.o的汇编代码,注意
14: e8 00 00 00 00 callq 19 <main+0x19>
15: R_X86_64_PLT32 sum-0x4
是对函数sum的调用,由于函数sum未定义,因此调用指令call(即汇编的e8)后的数为0,并记录了PC(该条指令下一条指令地址)为0x19
objdump -dx -j .data main.o
- -j name显示指定名称为name的section的信息。
这里即在main.o的反汇编里仅显示.data节信息
main.o: 文件格式 elf64-x86-64
main.o
体系结构:i386:x86-64, 标志 0x00000011:
HAS_RELOC, HAS_SYMS
起始地址 0x0000000000000000
节:
Idx Name Size VMA LMA File off Algn
1 .data 00000008 0000000000000000 0000000000000000 00000068 2**3
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 g O .data 0000000000000008 array
Disassembly of section .data:
0000000000000000 <array>:
0: 01 00 00 00 02 00 00 00 ........
运用该指令我们可以只看到.data节的相关信息,注意最后反汇编.data节里存放的全局变量array,由于它已被初始化,且机器为小端模式,因此0000000000000000<array>: 0: 01 00 00 00 02 00 00 00 ........
01 00 00 00 02 00 00 00即为初始化的两个数1,2。
readelf -s main.o
readelf程序是一个查看目标文件内容的很方便的工具,其中
- -s显示符号表段中的项。
因此我们可以看到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 array
9: 0000000000000000 33 FUNC GLOBAL DEFAULT 1 main
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND sum
- 其中Value表符号的地址,由于是可重定位目标文件,因此符号表显示它们的地址为0。
- Size为目标的大小,可以看到已被定义的符号array和main有大小,未链接所以不清楚内容的sum符号大小为0。
- Type表示类型,例FUNC是函数,OBJECT是数组数据,NOTYPE为无类型。
- Bind表示符号是本地的还是全局的。array、main和sum都是全局符号。GLOBAL 即该符号对外部文件的可见,LOCAL 即该符号只在这个文件中可见
- Vis表示符号可以是默认的、受保护的、隐藏的或内部的。
- Ndx为索引,对应objdump -dx main.o所展示的Idx来说明是哪个节,UND表示未知。
- Name为变量名称。
可以看到在8,9,11三个条目中
8:全局符号array是位于.data节中偏移量为0的8字节目标,因为数组中有两个int型元素所以占8B。
9:全局符号main是位于.text节偏移量为0的33字节函数。
11:全局符号sum是未定义的符号,它在其它模块定义,且未链接,所以不知道它的信息。
objdump -dx sum.o
与上面的objdump -dx main.o同理,这里是看sum.o的头信息
sum.o: 文件格式 elf64-x86-64
sum.o
体系结构:i386:x86-64, 标志 0x00000011:
HAS_RELOC, HAS_SYMS
起始地址 0x0000000000000000
节:
Idx Name Size VMA LMA File off Algn
0 .text 00000045 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000000 0000000000000000 0000000000000000 00000085 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 0000000000000000 0000000000000000 00000085 2**0
ALLOC
3 .comment 0000002b 0000000000000000 0000000000000000 00000085 2**0
CONTENTS, READONLY
4 .note.GNU-stack 00000000 0000000000000000 0000000000000000 000000b0 2**0
CONTENTS, READONLY
5 .eh_frame 00000038 0000000000000000 0000000000000000 000000b0 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 sum.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g F .text 0000000000000045 sum
Disassembly of section .text:
0000000000000000 <sum>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 89 7d e8 mov %rdi,-0x18(%rbp)
8: 89 75 e4 mov %esi,-0x1c(%rbp)
b: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
12: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)
19: eb 1d jmp 38 <sum+0x38>
1b: 8b 45 f8 mov -0x8(%rbp),%eax
1e: 48 98 cltq
20: 48 8d 14 85 00 00 00 lea 0x0(,%rax,4),%rdx
27: 00
28: 48 8b 45 e8 mov -0x18(%rbp),%rax
2c: 48 01 d0 add %rdx,%rax
2f: 8b 00 mov (%rax),%eax
31: 01 45 fc add %eax,-0x4(%rbp)
34: 83 45 f8 01 addl $0x1,-0x8(%rbp)
38: 8b 45 f8 mov -0x8(%rbp),%eax
3b: 3b 45 e4 cmp -0x1c(%rbp),%eax
3e: 7c db jl 1b <sum+0x1b>
40: 8b 45 fc mov -0x4(%rbp),%eax
43: 5d pop %rbp
44: c3 retq
objdump -dx -j .data sum.o
看sum.o的.data节信息
sum.o: 文件格式 elf64-x86-64
sum.o
体系结构:i386:x86-64, 标志 0x00000011:
HAS_RELOC, HAS_SYMS
起始地址 0x0000000000000000
节:
Idx Name Size VMA LMA File off Algn
1 .data 00000000 0000000000000000 0000000000000000 00000085 2**0
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
0000000000000000 l d .data 0000000000000000 .data
objdump -dx prog
这里将两个模块链接后生成的可执行文件prog进行反汇编,可以看到它的变化:
prog: 文件格式 elf64-x86-64
prog
体系结构:i386:x86-64, 标志 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
起始地址 0x00000000000004f0
程序头:
PHDR off 0x0000000000000040 vaddr 0x0000000000000040 paddr 0x0000000000000040 align 2**3
filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r--
INTERP off 0x0000000000000238 vaddr 0x0000000000000238 paddr 0x0000000000000238 align 2**0
filesz 0x000000000000001c memsz 0x000000000000001c flags r--
LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**21
filesz 0x0000000000000850 memsz 0x0000000000000850 flags r-x
LOAD off 0x0000000000000df0 vaddr 0x0000000000200df0 paddr 0x0000000000200df0 align 2**21
filesz 0x0000000000000228 memsz 0x0000000000000230 flags rw-
DYNAMIC off 0x0000000000000e00 vaddr 0x0000000000200e00 paddr 0x0000000000200e00 align 2**3
filesz 0x00000000000001c0 memsz 0x00000000000001c0 flags rw-
NOTE off 0x0000000000000254 vaddr 0x0000000000000254 paddr 0x0000000000000254 align 2**2
filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
EH_FRAME off 0x00000000000006e4 vaddr 0x00000000000006e4 paddr 0x00000000000006e4 align 2**2
filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
RELRO off 0x0000000000000df0 vaddr 0x0000000000200df0 paddr 0x0000000000200df0 align 2**0
filesz 0x0000000000000210 memsz 0x0000000000000210 flags r--
动态节:
NEEDED libc.so.6
INIT 0x00000000000004b8
FINI 0x00000000000006d4
INIT_ARRAY 0x0000000000200df0
INIT_ARRAYSZ 0x0000000000000008
FINI_ARRAY 0x0000000000200df8
FINI_ARRAYSZ 0x0000000000000008
GNU_HASH 0x0000000000000298
STRTAB 0x0000000000000348
SYMTAB 0x00000000000002b8
STRSZ 0x000000000000007d
SYMENT 0x0000000000000018
DEBUG 0x0000000000000000
PLTGOT 0x0000000000200fc0
RELA 0x00000000000003f8
RELASZ 0x00000000000000c0
RELAENT 0x0000000000000018
FLAGS 0x0000000000000008
FLAGS_1 0x0000000008000001
VERNEED 0x00000000000003d8
VERNEEDNUM 0x0000000000000001
VERSYM 0x00000000000003c6
RELACOUNT 0x0000000000000003
版本引用:
required from libc.so.6:
0x09691a75 0x00 02 GLIBC_2.2.5
节:
Idx Name Size VMA LMA File off Algn
0 .interp 0000001c 0000000000000238 0000000000000238 00000238 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 0000000000000254 0000000000000254 00000254 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 0000000000000274 0000000000000274 00000274 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 0000001c 0000000000000298 0000000000000298 00000298 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 00000090 00000000000002b8 00000000000002b8 000002b8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 0000007d 0000000000000348 0000000000000348 00000348 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 0000000c 00000000000003c6 00000000000003c6 000003c6 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000020 00000000000003d8 00000000000003d8 000003d8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rela.dyn 000000c0 00000000000003f8 00000000000003f8 000003f8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .init 00000017 00000000000004b8 00000000000004b8 000004b8 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
10 .plt 00000010 00000000000004d0 00000000000004d0 000004d0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt.got 00000008 00000000000004e0 00000000000004e0 000004e0 2**3
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .text 000001e2 00000000000004f0 00000000000004f0 000004f0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .fini 00000009 00000000000006d4 00000000000006d4 000006d4 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .rodata 00000004 00000000000006e0 00000000000006e0 000006e0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
15 .eh_frame_hdr 00000044 00000000000006e4 00000000000006e4 000006e4 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame 00000128 0000000000000728 0000000000000728 00000728 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .init_array 00000008 0000000000200df0 0000000000200df0 00000df0 2**3
CONTENTS, ALLOC, LOAD, DATA
18 .fini_array 00000008 0000000000200df8 0000000000200df8 00000df8 2**3
CONTENTS, ALLOC, LOAD, DATA
19 .dynamic 000001c0 0000000000200e00 0000000000200e00 00000e00 2**3
CONTENTS, ALLOC, LOAD, DATA
20 .got 00000040 0000000000200fc0 0000000000200fc0 00000fc0 2**3
CONTENTS, ALLOC, LOAD, DATA
21 .data 00000018 0000000000201000 0000000000201000 00001000 2**3
CONTENTS, ALLOC, LOAD, DATA
22 .bss 00000008 0000000000201018 0000000000201018 00001018 2**0
ALLOC
23 .comment 00000055 0000000000000000 0000000000000000 00001018 2**0
CONTENTS, READONLY
SYMBOL TABLE:
0000000000000238 l d .interp 0000000000000000 .interp
0000000000000254 l d .note.ABI-tag 0000000000000000 .note.ABI-tag
0000000000000274 l d .note.gnu.build-id 0000000000000000 .note.gnu.build-id
0000000000000298 l d .gnu.hash 0000000000000000 .gnu.hash
00000000000002b8 l d .dynsym 0000000000000000 .dynsym
0000000000000348 l d .dynstr 0000000000000000 .dynstr
00000000000003c6 l d .gnu.version 0000000000000000 .gnu.version
00000000000003d8 l d .gnu.version_r 0000000000000000 .gnu.version_r
00000000000003f8 l d .rela.dyn 0000000000000000 .rela.dyn
00000000000004b8 l d .init 0000000000000000 .init
00000000000004d0 l d .plt 0000000000000000 .plt
00000000000004e0 l d .plt.got 0000000000000000 .plt.got
00000000000004f0 l d .text 0000000000000000 .text
00000000000006d4 l d .fini 0000000000000000 .fini
00000000000006e0 l d .rodata 0000000000000000 .rodata
00000000000006e4 l d .eh_frame_hdr 0000000000000000 .eh_frame_hdr
0000000000000728 l d .eh_frame 0000000000000000 .eh_frame
0000000000200df0 l d .init_array 0000000000000000 .init_array
0000000000200df8 l d .fini_array 0000000000000000 .fini_array
0000000000200e00 l d .dynamic 0000000000000000 .dynamic
0000000000200fc0 l d .got 0000000000000000 .got
0000000000201000 l d .data 0000000000000000 .data
0000000000201018 l d .bss 0000000000000000 .bss
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000000520 l F .text 0000000000000000 deregister_tm_clones
0000000000000560 l F .text 0000000000000000 register_tm_clones
00000000000005b0 l F .text 0000000000000000 __do_global_dtors_aux
0000000000201018 l O .bss 0000000000000001 completed.7697
0000000000200df8 l O .fini_array 0000000000000000 __do_global_dtors_aux_fini_array_entry
00000000000005f0 l F .text 0000000000000000 frame_dummy
0000000000200df0 l O .init_array 0000000000000000 __frame_dummy_init_array_entry
0000000000000000 l df *ABS* 0000000000000000 main.c
0000000000000000 l df *ABS* 0000000000000000 sum.c
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
000000000000084c l O .eh_frame 0000000000000000 __FRAME_END__
0000000000000000 l df *ABS* 0000000000000000
0000000000200df8 l .init_array 0000000000000000 __init_array_end
0000000000200e00 l O .dynamic 0000000000000000 _DYNAMIC
0000000000200df0 l .init_array 0000000000000000 __init_array_start
00000000000006e4 l .eh_frame_hdr 0000000000000000 __GNU_EH_FRAME_HDR
0000000000200fc0 l O .got 0000000000000000 _GLOBAL_OFFSET_TABLE_
00000000000006d0 g F .text 0000000000000002 __libc_csu_fini
0000000000000000 w *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000201000 w .data 0000000000000000 data_start
0000000000201010 g O .data 0000000000000008 array
0000000000201018 g .data 0000000000000000 _edata
00000000000006d4 g F .fini 0000000000000000 _fini
0000000000000000 F *UND* 0000000000000000 __libc_start_main@@GLIBC_2.2.5
0000000000201000 g .data 0000000000000000 __data_start
0000000000000000 w *UND* 0000000000000000 __gmon_start__
0000000000201008 g O .data 0000000000000000 .hidden __dso_handle
000000000000061b g F .text 0000000000000045 sum
00000000000006e0 g O .rodata 0000000000000004 _IO_stdin_used
0000000000000660 g F .text 0000000000000065 __libc_csu_init
0000000000201020 g .bss 0000000000000000 _end
00000000000004f0 g F .text 000000000000002b _start
0000000000201018 g .bss 0000000000000000 __bss_start
00000000000005fa g F .text 0000000000000021 main
0000000000201018 g O .data 0000000000000000 .hidden __TMC_END__
0000000000000000 w *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 w F *UND* 0000000000000000 __cxa_finalize@@GLIBC_2.2.5
00000000000004b8 g F .init 0000000000000000 _init
Disassembly of section .init:
00000000000004b8 <_init>:
4b8: 48 83 ec 08 sub $0x8,%rsp
4bc: 48 8b 05 25 0b 20 00 mov 0x200b25(%rip),%rax # 200fe8 <__gmon_start__>
4c3: 48 85 c0 test %rax,%rax
4c6: 74 02 je 4ca <_init+0x12>
4c8: ff d0 callq *%rax
4ca: 48 83 c4 08 add $0x8,%rsp
4ce: c3 retq
Disassembly of section .plt:
00000000000004d0 <.plt>:
4d0: ff 35 f2 0a 20 00 pushq 0x200af2(%rip) # 200fc8 <_GLOBAL_OFFSET_TABLE_+0x8>
4d6: ff 25 f4 0a 20 00 jmpq *0x200af4(%rip) # 200fd0 <_GLOBAL_OFFSET_TABLE_+0x10>
4dc: 0f 1f 40 00 nopl 0x0(%rax)
Disassembly of section .plt.got:
00000000000004e0 <__cxa_finalize@plt>:
4e0: ff 25 12 0b 20 00 jmpq *0x200b12(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
4e6: 66 90 xchg %ax,%ax
Disassembly of section .text:
00000000000004f0 <_start>:
4f0: 31 ed xor %ebp,%ebp
4f2: 49 89 d1 mov %rdx,%r9
4f5: 5e pop %rsi
4f6: 48 89 e2 mov %rsp,%rdx
4f9: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
4fd: 50 push %rax
4fe: 54 push %rsp
4ff: 4c 8d 05 ca 01 00 00 lea 0x1ca(%rip),%r8 # 6d0 <__libc_csu_fini>
506: 48 8d 0d 53 01 00 00 lea 0x153(%rip),%rcx # 660 <__libc_csu_init>
50d: 48 8d 3d e6 00 00 00 lea 0xe6(%rip),%rdi # 5fa <main>
514: ff 15 c6 0a 20 00 callq *0x200ac6(%rip) # 200fe0 <__libc_start_main@GLIBC_2.2.5>
51a: f4 hlt
51b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
0000000000000520 <deregister_tm_clones>:
520: 48 8d 3d f1 0a 20 00 lea 0x200af1(%rip),%rdi # 201018 <__TMC_END__>
527: 55 push %rbp
528: 48 8d 05 e9 0a 20 00 lea 0x200ae9(%rip),%rax # 201018 <__TMC_END__>
52f: 48 39 f8 cmp %rdi,%rax
532: 48 89 e5 mov %rsp,%rbp
535: 74 19 je 550 <deregister_tm_clones+0x30>
537: 48 8b 05 9a 0a 20 00 mov 0x200a9a(%rip),%rax # 200fd8 <_ITM_deregisterTMCloneTable>
53e: 48 85 c0 test %rax,%rax
541: 74 0d je 550 <deregister_tm_clones+0x30>
543: 5d pop %rbp
544: ff e0 jmpq *%rax
546: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
54d: 00 00 00
550: 5d pop %rbp
551: c3 retq
552: 0f 1f 40 00 nopl 0x0(%rax)
556: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
55d: 00 00 00
0000000000000560 <register_tm_clones>:
560: 48 8d 3d b1 0a 20 00 lea 0x200ab1(%rip),%rdi # 201018 <__TMC_END__>
567: 48 8d 35 aa 0a 20 00 lea 0x200aaa(%rip),%rsi # 201018 <__TMC_END__>
56e: 55 push %rbp
56f: 48 29 fe sub %rdi,%rsi
572: 48 89 e5 mov %rsp,%rbp
575: 48 c1 fe 03 sar $0x3,%rsi
579: 48 89 f0 mov %rsi,%rax
57c: 48 c1 e8 3f shr $0x3f,%rax
580: 48 01 c6 add %rax,%rsi
583: 48 d1 fe sar %rsi
586: 74 18 je 5a0 <register_tm_clones+0x40>
588: 48 8b 05 61 0a 20 00 mov 0x200a61(%rip),%rax # 200ff0 <_ITM_registerTMCloneTable>
58f: 48 85 c0 test %rax,%rax
592: 74 0c je 5a0 <register_tm_clones+0x40>
594: 5d pop %rbp
595: ff e0 jmpq *%rax
597: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
59e: 00 00
5a0: 5d pop %rbp
5a1: c3 retq
5a2: 0f 1f 40 00 nopl 0x0(%rax)
5a6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
5ad: 00 00 00
00000000000005b0 <__do_global_dtors_aux>:
5b0: 80 3d 61 0a 20 00 00 cmpb $0x0,0x200a61(%rip) # 201018 <__TMC_END__>
5b7: 75 2f jne 5e8 <__do_global_dtors_aux+0x38>
5b9: 48 83 3d 37 0a 20 00 cmpq $0x0,0x200a37(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
5c0: 00
5c1: 55 push %rbp
5c2: 48 89 e5 mov %rsp,%rbp
5c5: 74 0c je 5d3 <__do_global_dtors_aux+0x23>
5c7: 48 8b 3d 3a 0a 20 00 mov 0x200a3a(%rip),%rdi # 201008 <__dso_handle>
5ce: e8 0d ff ff ff callq 4e0 <__cxa_finalize@plt>
5d3: e8 48 ff ff ff callq 520 <deregister_tm_clones>
5d8: c6 05 39 0a 20 00 01 movb $0x1,0x200a39(%rip) # 201018 <__TMC_END__>
5df: 5d pop %rbp
5e0: c3 retq
5e1: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
5e8: f3 c3 repz retq
5ea: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
00000000000005f0 <frame_dummy>:
5f0: 55 push %rbp
5f1: 48 89 e5 mov %rsp,%rbp
5f4: 5d pop %rbp
5f5: e9 66 ff ff ff jmpq 560 <register_tm_clones>
00000000000005fa <main>:
5fa: 55 push %rbp
5fb: 48 89 e5 mov %rsp,%rbp
5fe: 48 83 ec 10 sub $0x10,%rsp
602: be 02 00 00 00 mov $0x2,%esi
607: 48 8d 3d 02 0a 20 00 lea 0x200a02(%rip),%rdi # 201010 <array>
60e: e8 08 00 00 00 callq 61b <sum>
613: 89 45 fc mov %eax,-0x4(%rbp)
616: 8b 45 fc mov -0x4(%rbp),%eax
619: c9 leaveq
61a: c3 retq
000000000000061b <sum>:
61b: 55 push %rbp
61c: 48 89 e5 mov %rsp,%rbp
61f: 48 89 7d e8 mov %rdi,-0x18(%rbp)
623: 89 75 e4 mov %esi,-0x1c(%rbp)
626: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
62d: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)
634: eb 1d jmp 653 <sum+0x38>
636: 8b 45 f8 mov -0x8(%rbp),%eax
639: 48 98 cltq
63b: 48 8d 14 85 00 00 00 lea 0x0(,%rax,4),%rdx
642: 00
643: 48 8b 45 e8 mov -0x18(%rbp),%rax
647: 48 01 d0 add %rdx,%rax
64a: 8b 00 mov (%rax),%eax
64c: 01 45 fc add %eax,-0x4(%rbp)
64f: 83 45 f8 01 addl $0x1,-0x8(%rbp)
653: 8b 45 f8 mov -0x8(%rbp),%eax
656: 3b 45 e4 cmp -0x1c(%rbp),%eax
659: 7c db jl 636 <sum+0x1b>
65b: 8b 45 fc mov -0x4(%rbp),%eax
65e: 5d pop %rbp
65f: c3 retq
0000000000000660 <__libc_csu_init>:
660: 41 57 push %r15
662: 41 56 push %r14
664: 49 89 d7 mov %rdx,%r15
667: 41 55 push %r13
669: 41 54 push %r12
66b: 4c 8d 25 7e 07 20 00 lea 0x20077e(%rip),%r12 # 200df0 <__frame_dummy_init_array_entry>
672: 55 push %rbp
673: 48 8d 2d 7e 07 20 00 lea 0x20077e(%rip),%rbp # 200df8 <__init_array_end>
67a: 53 push %rbx
67b: 41 89 fd mov %edi,%r13d
67e: 49 89 f6 mov %rsi,%r14
681: 4c 29 e5 sub %r12,%rbp
684: 48 83 ec 08 sub $0x8,%rsp
688: 48 c1 fd 03 sar $0x3,%rbp
68c: e8 27 fe ff ff callq 4b8 <_init>
691: 48 85 ed test %rbp,%rbp
694: 74 20 je 6b6 <__libc_csu_init+0x56>
696: 31 db xor %ebx,%ebx
698: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
69f: 00
6a0: 4c 89 fa mov %r15,%rdx
6a3: 4c 89 f6 mov %r14,%rsi
6a6: 44 89 ef mov %r13d,%edi
6a9: 41 ff 14 dc callq *(%r12,%rbx,8)
6ad: 48 83 c3 01 add $0x1,%rbx
6b1: 48 39 dd cmp %rbx,%rbp
6b4: 75 ea jne 6a0 <__libc_csu_init+0x40>
6b6: 48 83 c4 08 add $0x8,%rsp
6ba: 5b pop %rbx
6bb: 5d pop %rbp
6bc: 41 5c pop %r12
6be: 41 5d pop %r13
6c0: 41 5e pop %r14
6c2: 41 5f pop %r15
6c4: c3 retq
6c5: 90 nop
6c6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
6cd: 00 00 00
00000000000006d0 <__libc_csu_fini>:
6d0: f3 c3 repz retq
Disassembly of section .fini:
00000000000006d4 <_fini>:
6d4: 48 83 ec 08 sub $0x8,%rsp
6d8: 48 83 c4 08 add $0x8,%rsp
6dc: c3 retq
可执行目标文件与可重定位目标文件稍有不同。由反汇编信息可知,开头标注了该结果是在x86-64机器上跑出来的结果,并且由于这是一个可执行目标文件,因此起始地址有明确值0x00000000000004f0。可执行目标文件多了程序头表,.init节而少了两个.rel节(因为无需重定位)。init节用于定义_init函数来进行对可执行目标文件执行时初始化工作。
来源:网课-计算机系统基础(一):程序的表示、转换与链接(第十周)
最后显示了prog的汇编代码,注意在00000000000005fa <main>
里:
60e: e8 08 00 00 00 callq 61b <sum>
是对函数sum的调用,由于函数sum已链接且机器为小端模式,因此调用指令call(即汇编的e8)后的数为08 00 00 00,并记录了跳转地址为0x61b,正好是sum函数的起始地址。其中08 00 00 00为重定位值*refptr,针对重定位时PC相对寻址和绝对寻址两种方法,有如下算式:
if(r.type == R_X86_64_PC32){//PC相对寻址
refaddr = ADDR(s) + r.offset;
*refptr = (unsigned)(ADDR(r.symbol) + r.addend - refaddr);
}
if(r.type == R_X86_64_32){//绝对寻址
*refptr = (unsigned)(ADDR(r.symbol) + r.addend);
其中refaddr为重定位地址;ADDR(s) 和 ADDR(r.symbol) 为节的运行时地址(这里为.text节并且同时为main首地址,即引用函数的地址)和符号运行时地址(这里为sum函数首地址,及被引用函数的地址)ADDR(s)和ADDR(r.symbol)由可执行目标文件可知;r.offset为偏移量;*refptr为重定位值,也即偏移地址;r.addend为修正值。
在本例中,比较main的可重定位文件
14: e8 00 00 00 00 callq 19 <main+0x19>//19为PC值
15: R_X86_64_PLT32 sum-0x4//15为偏移量r.offset,sum-0x4表示修正值r.addend为-4
和可执行目标文件
00000000000005fa <main>: //00000000000005fa为ADDR(s)
5fa: 55 push %rbp
5fb: 48 89 e5 mov %rsp,%rbp
5fe: 48 83 ec 10 sub $0x10,%rsp
602: be 02 00 00 00 mov $0x2,%esi
607: 48 8d 3d 02 0a 20 00 lea 0x200a02(%rip),%rdi # 201010 <array>
60e: e8 08 00 00 00 callq 61b <sum>//*refptr为8
613: 89 45 fc mov %eax,-0x4(%rbp)
616: 8b 45 fc mov -0x4(%rbp),%eax
619: c9 leaveq
61a: c3 retq
000000000000061b <sum>: //000000000000061b为ADDR(r.symbol)
61b: 55 push %rbp
61c: 48 89 e5 mov %rsp,%rbp
61f: 48 89 7d e8 mov %rdi,-0x18(%rbp)
623: 89 75 e4 mov %esi,-0x1c(%rbp)
626: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
62d: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)
634: eb 1d jmp 653 <sum+0x38>
636: 8b 45 f8 mov -0x8(%rbp),%eax
639: 48 98 cltq
63b: 48 8d 14 85 00 00 00 lea 0x0(,%rax,4),%rdx
642: 00
643: 48 8b 45 e8 mov -0x18(%rbp),%rax
647: 48 01 d0 add %rdx,%rax
64a: 8b 00 mov (%rax),%eax
64c: 01 45 fc add %eax,-0x4(%rbp)
64f: 83 45 f8 01 addl $0x1,-0x8(%rbp)
653: 8b 45 f8 mov -0x8(%rbp),%eax
656: 3b 45 e4 cmp -0x1c(%rbp),%eax
659: 7c db jl 636 <sum+0x1b>
65b: 8b 45 fc mov -0x4(%rbp),%eax
65e: 5d pop %rbp
65f: c3 retq
由以上信息,即可进行相关计算refaddr = ADDR(s) + r.offset=0x5fa+0x15=0x60f
*refptr = (unsigned)(ADDR(r.symbol) + r.addend - refaddr)=0x61b+(-4)-0x60f=8
objdump -dx -j .data prog
只看可执行目标文件的.data节内容如下
prog: 文件格式 elf64-x86-64
prog
体系结构:i386:x86-64, 标志 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
起始地址 0x00000000000004f0
程序头:
PHDR off 0x0000000000000040 vaddr 0x0000000000000040 paddr 0x0000000000000040 align 2**3
filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r--
INTERP off 0x0000000000000238 vaddr 0x0000000000000238 paddr 0x0000000000000238 align 2**0
filesz 0x000000000000001c memsz 0x000000000000001c flags r--
LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**21
filesz 0x0000000000000850 memsz 0x0000000000000850 flags r-x
LOAD off 0x0000000000000df0 vaddr 0x0000000000200df0 paddr 0x0000000000200df0 align 2**21
filesz 0x0000000000000228 memsz 0x0000000000000230 flags rw-
DYNAMIC off 0x0000000000000e00 vaddr 0x0000000000200e00 paddr 0x0000000000200e00 align 2**3
filesz 0x00000000000001c0 memsz 0x00000000000001c0 flags rw-
NOTE off 0x0000000000000254 vaddr 0x0000000000000254 paddr 0x0000000000000254 align 2**2
filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
EH_FRAME off 0x00000000000006e4 vaddr 0x00000000000006e4 paddr 0x00000000000006e4 align 2**2
filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
RELRO off 0x0000000000000df0 vaddr 0x0000000000200df0 paddr 0x0000000000200df0 align 2**0
filesz 0x0000000000000210 memsz 0x0000000000000210 flags r--
动态节:
NEEDED libc.so.6
INIT 0x00000000000004b8
FINI 0x00000000000006d4
INIT_ARRAY 0x0000000000200df0
INIT_ARRAYSZ 0x0000000000000008
FINI_ARRAY 0x0000000000200df8
FINI_ARRAYSZ 0x0000000000000008
GNU_HASH 0x0000000000000298
STRTAB 0x0000000000000348
SYMTAB 0x00000000000002b8
STRSZ 0x000000000000007d
SYMENT 0x0000000000000018
DEBUG 0x0000000000000000
PLTGOT 0x0000000000200fc0
RELA 0x00000000000003f8
RELASZ 0x00000000000000c0
RELAENT 0x0000000000000018
FLAGS 0x0000000000000008
FLAGS_1 0x0000000008000001
VERNEED 0x00000000000003d8
VERNEEDNUM 0x0000000000000001
VERSYM 0x00000000000003c6
RELACOUNT 0x0000000000000003
版本引用:
required from libc.so.6:
0x09691a75 0x00 02 GLIBC_2.2.5
节:
Idx Name Size VMA LMA File off Algn
21 .data 00000018 0000000000201000 0000000000201000 00001000 2**3
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
0000000000201000 l d .data 0000000000000000 .data
0000000000201000 w .data 0000000000000000 data_start
0000000000201010 g O .data 0000000000000008 array
0000000000201018 g .data 0000000000000000 _edata
0000000000201000 g .data 0000000000000000 __data_start
0000000000201008 g O .data 0000000000000000 .hidden __dso_handle
0000000000201018 g O .data 0000000000000000 .hidden __TMC_END__
Disassembly of section .data:
0000000000201000 <__data_start>:
...
0000000000201008 <__dso_handle>:
201008: 08 10 20 00 00 00 00 00 .. .....
0000000000201010 <array>:
201010: 01 00 00 00 02 00 00 00 ........
经过上述操作,可以看到链接操作对多个可重定位目标文件进行了符号解析和重定位这两步操作,连接器解析符号引用的方法是将每个符号引用与它输入的可重定位目标文件的符号表中的一个确定的符号定义关联起来;而重定位将代码合并输入模块,并为每个符号分配运行时地址。经由这两步操作,一个可执行目标文件就形成了。这便是链接的全过程。
更多详细信息参考:网课-计算机系统基础(一):程序的表示、转换与链接(第十周)
书籍:深入理解计算机系统(CSAPP)第七章链接