【编译和链接九】静态链接
- 一、demo
- 二、空间与地址分配
- 1、相似段合并
- 三、即虚拟地址VMA(Virtual Memory Address)
- 四、重定位
- 1、add调用
- 2、printf调用——同add
- 2、shared
- 五、重定位表
- 六、符号解析
- 七、c++相关问题
- 1、重复代码消除
- 2、全局构造与析构
- 3、C++与ABI
- 八、静态库链接
一、demo
假设我们的程序只有这两个模块“main.c”和“add.c”。首先我们使用gcc将“main.c”和“add.c”分别编译成目标文件“main.o”和“add.o”:经过编译以后我们就得到了“main.o”和“add.o”这两个目标文件。
从代码中可以看到,“add.c”总共定义了两个全局符号,一个是变量“shared”,另外一个是函数“add”;“main.c”里面定义了一个全局符号就是“main”。模块“main.c”里面引用到了“add.c”里面的“add”和“shared”。我们接下来要做的就是把“main.o”和“add.o”这两个目标文件链接在一起并最终形成一个可执行文件“mainadd”。
- main.c
#include<stdio.h>extern int shared;int main()
{int a=2;int b = 3;int sum=add(a,b);int c=shared;printf("sum = %d,\nshared = %d\n",sum,c);return -1;
}
- add.c
//#include<stdio.h>
int shared =100;int add( int i,int j )
{return i+j;
}
- 编译、链接、执行
[dev1@localhost test02]$ gcc -c add.c -o add.o
[dev1@localhost test02]$ gcc -c main.c -o main.o
[dev1@localhost test02]$ gcc main.o add.o -o mainadd
[dev1@localhost test02]$ ./mainadd
sum = 5
shared = 100
[dev1@localhost test02]$
- 链接前add.o
[dev1@localhost test02]$ objdump -s -d -x -t add.oadd.o: 文件格式 elf64-x86-64
add.o
体系结构:i386:x86-64,标志 0x00000011:
HAS_RELOC, HAS_SYMS
起始地址 0x0000000000000000节:
Idx Name Size VMA LMA File off Algn0 .text 00000014 0000000000000000 0000000000000000 00000040 2**0CONTENTS, ALLOC, LOAD, READONLY, CODE1 .data 00000004 0000000000000000 0000000000000000 00000054 2**2CONTENTS, ALLOC, LOAD, DATA2 .bss 00000000 0000000000000000 0000000000000000 00000058 2**0ALLOC3 .comment 0000002e 0000000000000000 0000000000000000 00000058 2**0CONTENTS, READONLY4 .note.GNU-stack 00000000 0000000000000000 0000000000000000 00000086 2**0CONTENTS, READONLY5 .eh_frame 00000038 0000000000000000 0000000000000000 00000088 2**3CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 add.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 0000000000000004 shared
0000000000000000 g F .text 0000000000000014 addContents of section .text:0000 554889e5 897dfc89 75f88b45 f88b55fc UH...}..u..E..U.0010 01d05dc3 ..].
Contents of section .data:0000 64000000 d...
Contents of section .comment:0000 00474343 3a202847 4e552920 342e382e .GCC: (GNU) 4.8.0010 35203230 31353036 32332028 52656420 5 20150623 (Red 0020 48617420 342e382e 352d3434 2900 Hat 4.8.5-44).
Contents of section .eh_frame:0000 14000000 00000000 017a5200 01781001 .........zR..x..0010 1b0c0708 90010000 1c000000 1c000000 ................0020 00000000 14000000 00410e10 8602430d .........A....C.0030 064f0c07 08000000 .O...... Disassembly of section .text:0000000000000000 <add>:0: 55 push %rbp1: 48 89 e5 mov %rsp,%rbp4: 89 7d fc mov %edi,-0x4(%rbp)7: 89 75 f8 mov %esi,-0x8(%rbp)a: 8b 45 f8 mov -0x8(%rbp),%eaxd: 8b 55 fc mov -0x4(%rbp),%edx10: 01 d0 add %edx,%eax12: 5d pop %rbp13: c3 retq
[dev1@localhost test02]$
- 链接前main.o
[dev1@localhost test02]$ objdump -s -d -x -t main.omain.o: 文件格式 elf64-x86-64
main.o
体系结构:i386:x86-64,标志 0x00000011:
HAS_RELOC, HAS_SYMS
起始地址 0x0000000000000000节:
Idx Name Size VMA LMA File off Algn0 .text 00000054 0000000000000000 0000000000000000 00000040 2**0CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data 00000000 0000000000000000 0000000000000000 00000094 2**0CONTENTS, ALLOC, LOAD, DATA2 .bss 00000000 0000000000000000 0000000000000000 00000094 2**0ALLOC3 .rodata 00000017 0000000000000000 0000000000000000 00000094 2**0CONTENTS, ALLOC, LOAD, READONLY, DATA4 .comment 0000002e 0000000000000000 0000000000000000 000000ab 2**0CONTENTS, READONLY5 .note.GNU-stack 00000000 0000000000000000 0000000000000000 000000d9 2**0CONTENTS, READONLY6 .eh_frame 00000038 0000000000000000 0000000000000000 000000e0 2**3CONTENTS, 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 .rodata 0000000000000000 .rodata
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 0000000000000054 main
0000000000000000 *UND* 0000000000000000 add
0000000000000000 *UND* 0000000000000000 shared
0000000000000000 *UND* 0000000000000000 printfContents of section .text:0000 554889e5 4883ec10 c745fc02 000000c7 UH..H....E......0010 45f80300 00008b55 f88b45fc 89d689c7 E......U..E.....0020 b8000000 00e80000 00008945 f48b0500 ...........E....0030 00000089 45f08b55 f08b45f4 89c6bf00 ....E..U..E.....0040 000000b8 00000000 e8000000 00b8ffff ................0050 ffffc9c3 ....
Contents of section .rodata:0000 73756d20 3d202564 2c0a7368 61726564 sum = %d,.shared0010 203d2025 640a00 = %d..
Contents of section .comment:0000 00474343 3a202847 4e552920 342e382e .GCC: (GNU) 4.8.0010 35203230 31353036 32332028 52656420 5 20150623 (Red 0020 48617420 342e382e 352d3434 2900 Hat 4.8.5-44).
Contents of section .eh_frame:0000 14000000 00000000 017a5200 01781001 .........zR..x..0010 1b0c0708 90010000 1c000000 1c000000 ................0020 00000000 54000000 00410e10 8602430d ....T....A....C.0030 06024f0c 07080000 ..O..... Disassembly of section .text:0000000000000000 <main>:0: 55 push %rbp1: 48 89 e5 mov %rsp,%rbp4: 48 83 ec 10 sub $0x10,%rsp8: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)f: c7 45 f8 03 00 00 00 movl $0x3,-0x8(%rbp)16: 8b 55 f8 mov -0x8(%rbp),%edx19: 8b 45 fc mov -0x4(%rbp),%eax1c: 89 d6 mov %edx,%esi1e: 89 c7 mov %eax,%edi20: b8 00 00 00 00 mov $0x0,%eax25: e8 00 00 00 00 callq 2a <main+0x2a>26: R_X86_64_PC32 add-0x42a: 89 45 f4 mov %eax,-0xc(%rbp)2d: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 33 <main+0x33>2f: R_X86_64_PC32 shared-0x433: 89 45 f0 mov %eax,-0x10(%rbp)36: 8b 55 f0 mov -0x10(%rbp),%edx39: 8b 45 f4 mov -0xc(%rbp),%eax3c: 89 c6 mov %eax,%esi3e: bf 00 00 00 00 mov $0x0,%edi3f: R_X86_64_32 .rodata43: b8 00 00 00 00 mov $0x0,%eax48: e8 00 00 00 00 callq 4d <main+0x4d>49: R_X86_64_PC32 printf-0x44d: b8 ff ff ff ff mov $0xffffffff,%eax52: c9 leaveq 53: c3 retq
[dev1@localhost test02]$
- 链接后mainadd
[dev1@localhost test02]$ objdump -s -d -x -t mainaddmainadd: 文件格式 elf64-x86-64
mainadd
体系结构:i386:x86-64,标志 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
起始地址 0x0000000000400440程序头:PHDR off 0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**3filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r-xINTERP off 0x0000000000000238 vaddr 0x0000000000400238 paddr 0x0000000000400238 align 2**0filesz 0x000000000000001c memsz 0x000000000000001c flags r--LOAD off 0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21filesz 0x000000000000079c memsz 0x000000000000079c flags r-xLOAD off 0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**21filesz 0x0000000000000228 memsz 0x0000000000000230 flags rw-DYNAMIC off 0x0000000000000e28 vaddr 0x0000000000600e28 paddr 0x0000000000600e28 align 2**3filesz 0x00000000000001d0 memsz 0x00000000000001d0 flags rw-NOTE off 0x0000000000000254 vaddr 0x0000000000400254 paddr 0x0000000000400254 align 2**2filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
EH_FRAME off 0x0000000000000648 vaddr 0x0000000000400648 paddr 0x0000000000400648 align 2**2filesz 0x000000000000003c memsz 0x000000000000003c flags r--STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-RELRO off 0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**0filesz 0x00000000000001f0 memsz 0x00000000000001f0 flags r--动态节:NEEDED libc.so.6INIT 0x00000000004003e0FINI 0x0000000000400614INIT_ARRAY 0x0000000000600e10INIT_ARRAYSZ 0x0000000000000008FINI_ARRAY 0x0000000000600e18FINI_ARRAYSZ 0x0000000000000008GNU_HASH 0x0000000000400298STRTAB 0x0000000000400318SYMTAB 0x00000000004002b8STRSZ 0x000000000000003fSYMENT 0x0000000000000018DEBUG 0x0000000000000000PLTGOT 0x0000000000601000PLTRELSZ 0x0000000000000048PLTREL 0x0000000000000007JMPREL 0x0000000000400398RELA 0x0000000000400380RELASZ 0x0000000000000018RELAENT 0x0000000000000018VERNEED 0x0000000000400360VERNEEDNUM 0x0000000000000001VERSYM 0x0000000000400358版本引用:required from libc.so.6:0x09691a75 0x00 02 GLIBC_2.2.5节:
Idx Name Size VMA LMA File off Algn0 .interp 0000001c 0000000000400238 0000000000400238 00000238 2**0CONTENTS, ALLOC, LOAD, READONLY, DATA1 .note.ABI-tag 00000020 0000000000400254 0000000000400254 00000254 2**2CONTENTS, ALLOC, LOAD, READONLY, DATA2 .note.gnu.build-id 00000024 0000000000400274 0000000000400274 00000274 2**2CONTENTS, ALLOC, LOAD, READONLY, DATA3 .gnu.hash 0000001c 0000000000400298 0000000000400298 00000298 2**3CONTENTS, ALLOC, LOAD, READONLY, DATA4 .dynsym 00000060 00000000004002b8 00000000004002b8 000002b8 2**3CONTENTS, ALLOC, LOAD, READONLY, DATA5 .dynstr 0000003f 0000000000400318 0000000000400318 00000318 2**0CONTENTS, ALLOC, LOAD, READONLY, DATA6 .gnu.version 00000008 0000000000400358 0000000000400358 00000358 2**1CONTENTS, ALLOC, LOAD, READONLY, DATA7 .gnu.version_r 00000020 0000000000400360 0000000000400360 00000360 2**3CONTENTS, ALLOC, LOAD, READONLY, DATA8 .rela.dyn 00000018 0000000000400380 0000000000400380 00000380 2**3CONTENTS, ALLOC, LOAD, READONLY, DATA9 .rela.plt 00000048 0000000000400398 0000000000400398 00000398 2**3CONTENTS, ALLOC, LOAD, READONLY, DATA10 .init 0000001a 00000000004003e0 00000000004003e0 000003e0 2**2CONTENTS, ALLOC, LOAD, READONLY, CODE11 .plt 00000040 0000000000400400 0000000000400400 00000400 2**4CONTENTS, ALLOC, LOAD, READONLY, CODE12 .text 000001d2 0000000000400440 0000000000400440 00000440 2**4CONTENTS, ALLOC, LOAD, READONLY, CODE13 .fini 00000009 0000000000400614 0000000000400614 00000614 2**2CONTENTS, ALLOC, LOAD, READONLY, CODE14 .rodata 00000027 0000000000400620 0000000000400620 00000620 2**3CONTENTS, ALLOC, LOAD, READONLY, DATA15 .eh_frame_hdr 0000003c 0000000000400648 0000000000400648 00000648 2**2CONTENTS, ALLOC, LOAD, READONLY, DATA16 .eh_frame 00000114 0000000000400688 0000000000400688 00000688 2**3CONTENTS, ALLOC, LOAD, READONLY, DATA17 .init_array 00000008 0000000000600e10 0000000000600e10 00000e10 2**3CONTENTS, ALLOC, LOAD, DATA18 .fini_array 00000008 0000000000600e18 0000000000600e18 00000e18 2**3CONTENTS, ALLOC, LOAD, DATA19 .jcr 00000008 0000000000600e20 0000000000600e20 00000e20 2**3CONTENTS, ALLOC, LOAD, DATA20 .dynamic 000001d0 0000000000600e28 0000000000600e28 00000e28 2**3CONTENTS, ALLOC, LOAD, DATA21 .got 00000008 0000000000600ff8 0000000000600ff8 00000ff8 2**3CONTENTS, ALLOC, LOAD, DATA22 .got.plt 00000030 0000000000601000 0000000000601000 00001000 2**3CONTENTS, ALLOC, LOAD, DATA23 .data 00000008 0000000000601030 0000000000601030 00001030 2**2CONTENTS, ALLOC, LOAD, DATA24 .bss 00000008 0000000000601038 0000000000601038 00001038 2**0ALLOC25 .comment 0000002d 0000000000000000 0000000000000000 00001038 2**0CONTENTS, READONLY
SYMBOL TABLE:
0000000000400238 l d .interp 0000000000000000 .interp
0000000000400254 l d .note.ABI-tag 0000000000000000 .note.ABI-tag
0000000000400274 l d .note.gnu.build-id 0000000000000000 .note.gnu.build-id
0000000000400298 l d .gnu.hash 0000000000000000 .gnu.hash
00000000004002b8 l d .dynsym 0000000000000000 .dynsym
0000000000400318 l d .dynstr 0000000000000000 .dynstr
0000000000400358 l d .gnu.version 0000000000000000 .gnu.version
0000000000400360 l d .gnu.version_r 0000000000000000 .gnu.version_r
0000000000400380 l d .rela.dyn 0000000000000000 .rela.dyn
0000000000400398 l d .rela.plt 0000000000000000 .rela.plt
00000000004003e0 l d .init 0000000000000000 .init
0000000000400400 l d .plt 0000000000000000 .plt
0000000000400440 l d .text 0000000000000000 .text
0000000000400614 l d .fini 0000000000000000 .fini
0000000000400620 l d .rodata 0000000000000000 .rodata
0000000000400648 l d .eh_frame_hdr 0000000000000000 .eh_frame_hdr
0000000000400688 l d .eh_frame 0000000000000000 .eh_frame
0000000000600e10 l d .init_array 0000000000000000 .init_array
0000000000600e18 l d .fini_array 0000000000000000 .fini_array
0000000000600e20 l d .jcr 0000000000000000 .jcr
0000000000600e28 l d .dynamic 0000000000000000 .dynamic
0000000000600ff8 l d .got 0000000000000000 .got
0000000000601000 l d .got.plt 0000000000000000 .got.plt
0000000000601030 l d .data 0000000000000000 .data
0000000000601038 l d .bss 0000000000000000 .bss
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000600e20 l O .jcr 0000000000000000 __JCR_LIST__
0000000000400470 l F .text 0000000000000000 deregister_tm_clones
00000000004004a0 l F .text 0000000000000000 register_tm_clones
00000000004004e0 l F .text 0000000000000000 __do_global_dtors_aux
0000000000601038 l O .bss 0000000000000001 completed.6355
0000000000600e18 l O .fini_array 0000000000000000 __do_global_dtors_aux_fini_array_entry
0000000000400500 l F .text 0000000000000000 frame_dummy
0000000000600e10 l O .init_array 0000000000000000 __frame_dummy_init_array_entry
0000000000000000 l df *ABS* 0000000000000000 main.c
0000000000000000 l df *ABS* 0000000000000000 add.c
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000400798 l O .eh_frame 0000000000000000 __FRAME_END__
0000000000600e20 l O .jcr 0000000000000000 __JCR_END__
0000000000000000 l df *ABS* 0000000000000000
0000000000600e18 l .init_array 0000000000000000 __init_array_end
0000000000600e28 l O .dynamic 0000000000000000 _DYNAMIC
0000000000600e10 l .init_array 0000000000000000 __init_array_start
0000000000400648 l .eh_frame_hdr 0000000000000000 __GNU_EH_FRAME_HDR
0000000000601000 l O .got.plt 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000400610 g F .text 0000000000000002 __libc_csu_fini
0000000000601030 w .data 0000000000000000 data_start
0000000000400581 g F .text 0000000000000014 add
0000000000601038 g .data 0000000000000000 _edata
0000000000400614 g F .fini 0000000000000000 _fini
0000000000000000 F *UND* 0000000000000000 printf@@GLIBC_2.2.5
0000000000000000 F *UND* 0000000000000000 __libc_start_main@@GLIBC_2.2.5
0000000000601030 g .data 0000000000000000 __data_start
0000000000000000 w *UND* 0000000000000000 __gmon_start__
0000000000400628 g O .rodata 0000000000000000 .hidden __dso_handle
0000000000400620 g O .rodata 0000000000000004 _IO_stdin_used
00000000004005a0 g F .text 0000000000000065 __libc_csu_init
0000000000601040 g .bss 0000000000000000 _end
0000000000400440 g F .text 0000000000000000 _start
0000000000601038 g .bss 0000000000000000 __bss_start
000000000040052d g F .text 0000000000000054 main
0000000000601038 g O .data 0000000000000000 .hidden __TMC_END__
00000000004003e0 g F .init 0000000000000000 _init
0000000000601034 g O .data 0000000000000004 sharedContents of section .interp:400238 2f6c6962 36342f6c 642d6c69 6e75782d /lib64/ld-linux-400248 7838362d 36342e73 6f2e3200 x86-64.so.2.
Contents of section .note.ABI-tag:400254 04000000 10000000 01000000 474e5500 ............GNU.400264 00000000 02000000 06000000 20000000 ............ ...
Contents of section .note.gnu.build-id:400274 04000000 14000000 03000000 474e5500 ............GNU.400284 7b916d52 74b6e506 a365e83e c10b3e35 {.mRt....e.>..>5400294 48d94e85 H.N.
Contents of section .gnu.hash:400298 01000000 01000000 01000000 00000000 ................4002a8 00000000 00000000 00000000 ............
Contents of section .dynsym:4002b8 00000000 00000000 00000000 00000000 ................4002c8 00000000 00000000 0b000000 12000000 ................4002d8 00000000 00000000 00000000 00000000 ................4002e8 12000000 12000000 00000000 00000000 ................4002f8 00000000 00000000 24000000 20000000 ........$... ...400308 00000000 00000000 00000000 00000000 ................
Contents of section .dynstr:400318 006c6962 632e736f 2e360070 72696e74 .libc.so.6.print400328 66005f5f 6c696263 5f737461 72745f6d f.__libc_start_m400338 61696e00 5f5f676d 6f6e5f73 74617274 ain.__gmon_start400348 5f5f0047 4c494243 5f322e32 2e3500 __.GLIBC_2.2.5.
Contents of section .gnu.version:400358 00000200 02000000 ........
Contents of section .gnu.version_r:400360 01000100 01000000 10000000 00000000 ................400370 751a6909 00000200 33000000 00000000 u.i.....3.......
Contents of section .rela.dyn:400380 f80f6000 00000000 06000000 03000000 ..`.............400390 00000000 00000000 ........
Contents of section .rela.plt:400398 18106000 00000000 07000000 01000000 ..`.............4003a8 00000000 00000000 20106000 00000000 ........ .`.....4003b8 07000000 02000000 00000000 00000000 ................4003c8 28106000 00000000 07000000 03000000 (.`.............4003d8 00000000 00000000 ........
Contents of section .init:4003e0 4883ec08 488b050d 0c200048 85c07405 H...H.... .H..t.4003f0 e83b0000 004883c4 08c3 .;...H....
Contents of section .plt:400400 ff35020c 2000ff25 040c2000 0f1f4000 .5.. ..%.. ...@.400410 ff25020c 20006800 000000e9 e0ffffff .%.. .h.........400420 ff25fa0b 20006801 000000e9 d0ffffff .%.. .h.........400430 ff25f20b 20006802 000000e9 c0ffffff .%.. .h.........
Contents of section .text:400440 31ed4989 d15e4889 e24883e4 f0505449 1.I..^H..H...PTI400450 c7c01006 400048c7 c1a00540 0048c7c7 ....@.H....@.H..400460 2d054000 e8b7ffff fff4660f 1f440000 -.@.......f..D..400470 b83f1060 0055482d 38106000 4883f80e .?.`.UH-8.`.H...400480 4889e577 025dc3b8 00000000 4885c074 H..w.]......H..t400490 f45dbf38 106000ff e00f1f80 00000000 .].8.`..........4004a0 b8381060 0055482d 38106000 48c1f803 .8.`.UH-8.`.H...4004b0 4889e548 89c248c1 ea3f4801 d048d1f8 H..H..H..?H..H..4004c0 75025dc3 ba000000 004885d2 74f45d48 u.]......H..t.]H4004d0 89c6bf38 106000ff e20f1f80 00000000 ...8.`..........4004e0 803d510b 20000075 11554889 e5e87eff .=Q. ..u.UH...~.4004f0 ffff5dc6 053e0b20 0001f3c3 0f1f4000 ..]..>. ......@.400500 48833d18 09200000 741eb800 00000048 H.=.. ..t......H400510 85c07414 55bf200e 60004889 e5ffd05d ..t.U. .`.H....]400520 e97bffff ff0f1f00 e973ffff ff554889 .{.......s...UH.400530 e54883ec 10c745fc 02000000 c745f803 .H....E......E..400540 0000008b 55f88b45 fc89d689 c7b80000 ....U..E........400550 0000e82a 00000089 45f48b05 d40a2000 ...*....E..... .400560 8945f08b 55f08b45 f489c6bf 30064000 .E..U..E....0.@.400570 b8000000 00e896fe ffffb8ff ffffffc9 ................400580 c3554889 e5897dfc 8975f88b 45f88b55 .UH...}..u..E..U400590 fc01d05d c3662e0f 1f840000 00000090 ...].f..........4005a0 41574189 ff415649 89f64155 4989d541 AWA..AVI..AUI..A4005b0 544c8d25 58082000 55488d2d 58082000 TL.%X. .UH.-X. .4005c0 534c29e5 31db48c1 fd034883 ec08e80d SL).1.H...H.....4005d0 feffff48 85ed741e 0f1f8400 00000000 ...H..t.........4005e0 4c89ea4c 89f64489 ff41ff14 dc4883c3 L..L..D..A...H..4005f0 014839eb 75ea4883 c4085b5d 415c415d .H9.u.H...[]A\A]400600 415e415f c390662e 0f1f8400 00000000 A^A_..f.........400610 f3c3 ..
Contents of section .fini:400614 4883ec08 4883c408 c3 H...H....
Contents of section .rodata:400620 01000200 00000000 00000000 00000000 ................400630 73756d20 3d202564 2c0a7368 61726564 sum = %d,.shared400640 203d2025 640a00 = %d..
Contents of section .eh_frame_hdr:400648 011b033b 3c000000 06000000 b8fdffff ...;<...........400658 88000000 f8fdffff 58000000 e5feffff ........X.......400668 b0000000 39ffffff d0000000 58ffffff ....9.......X...400678 f0000000 c8ffffff 38010000 ........8...
Contents of section .eh_frame:400688 14000000 00000000 017a5200 01781001 .........zR..x..400698 1b0c0708 90010710 14000000 1c000000 ................4006a8 98fdffff 2a000000 00000000 00000000 ....*...........4006b8 14000000 00000000 017a5200 01781001 .........zR..x..4006c8 1b0c0708 90010000 24000000 1c000000 ........$.......4006d8 28fdffff 40000000 000e1046 0e184a0f (...@......F..J.4006e8 0b770880 003f1a3b 2a332422 00000000 .w...?.;*3$"....4006f8 1c000000 44000000 2dfeffff 54000000 ....D...-...T...400708 00410e10 8602430d 06024f0c 07080000 .A....C...O.....400718 1c000000 64000000 61feffff 14000000 ....d...a.......400728 00410e10 8602430d 064f0c07 08000000 .A....C..O......400738 44000000 84000000 60feffff 65000000 D.......`...e...400748 00420e10 8f02450e 188e0345 0e208d04 .B....E....E. ..400758 450e288c 05480e30 8606480e 3883074d E.(..H.0..H.8..M400768 0e406c0e 38410e30 410e2842 0e20420e .@l.8A.0A.(B. B.400778 18420e10 420e0800 14000000 cc000000 .B..B...........400788 88feffff 02000000 00000000 00000000 ................400798 00000000 ....
Contents of section .init_array:600e10 00054000 00000000 ..@.....
Contents of section .fini_array:600e18 e0044000 00000000 ..@.....
Contents of section .jcr:600e20 00000000 00000000 ........
Contents of section .dynamic:600e28 01000000 00000000 01000000 00000000 ................600e38 0c000000 00000000 e0034000 00000000 ..........@.....600e48 0d000000 00000000 14064000 00000000 ..........@.....600e58 19000000 00000000 100e6000 00000000 ..........`.....600e68 1b000000 00000000 08000000 00000000 ................600e78 1a000000 00000000 180e6000 00000000 ..........`.....600e88 1c000000 00000000 08000000 00000000 ................600e98 f5feff6f 00000000 98024000 00000000 ...o......@.....600ea8 05000000 00000000 18034000 00000000 ..........@.....600eb8 06000000 00000000 b8024000 00000000 ..........@.....600ec8 0a000000 00000000 3f000000 00000000 ........?.......600ed8 0b000000 00000000 18000000 00000000 ................600ee8 15000000 00000000 00000000 00000000 ................600ef8 03000000 00000000 00106000 00000000 ..........`.....600f08 02000000 00000000 48000000 00000000 ........H.......600f18 14000000 00000000 07000000 00000000 ................600f28 17000000 00000000 98034000 00000000 ..........@.....600f38 07000000 00000000 80034000 00000000 ..........@.....600f48 08000000 00000000 18000000 00000000 ................600f58 09000000 00000000 18000000 00000000 ................600f68 feffff6f 00000000 60034000 00000000 ...o....`.@.....600f78 ffffff6f 00000000 01000000 00000000 ...o............600f88 f0ffff6f 00000000 58034000 00000000 ...o....X.@.....600f98 00000000 00000000 00000000 00000000 ................600fa8 00000000 00000000 00000000 00000000 ................600fb8 00000000 00000000 00000000 00000000 ................600fc8 00000000 00000000 00000000 00000000 ................600fd8 00000000 00000000 00000000 00000000 ................600fe8 00000000 00000000 00000000 00000000 ................
Contents of section .got:600ff8 00000000 00000000 ........
Contents of section .got.plt:601000 280e6000 00000000 00000000 00000000 (.`.............601010 00000000 00000000 16044000 00000000 ..........@.....601020 26044000 00000000 36044000 00000000 &.@.....6.@.....
Contents of section .data:601030 00000000 64000000 ....d...
Contents of section .comment:0000 4743433a 2028474e 55292034 2e382e35 GCC: (GNU) 4.8.50010 20323031 35303632 33202852 65642048 20150623 (Red H0020 61742034 2e382e35 2d343429 00 at 4.8.5-44). Disassembly of section .init:00000000004003e0 <_init>:4003e0: 48 83 ec 08 sub $0x8,%rsp4003e4: 48 8b 05 0d 0c 20 00 mov 0x200c0d(%rip),%rax # 600ff8 <__gmon_start__>4003eb: 48 85 c0 test %rax,%rax4003ee: 74 05 je 4003f5 <_init+0x15>4003f0: e8 3b 00 00 00 callq 400430 <__gmon_start__@plt>4003f5: 48 83 c4 08 add $0x8,%rsp4003f9: c3 retq Disassembly of section .plt:0000000000400400 <.plt>:400400: ff 35 02 0c 20 00 pushq 0x200c02(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>400406: ff 25 04 0c 20 00 jmpq *0x200c04(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>40040c: 0f 1f 40 00 nopl 0x0(%rax)0000000000400410 <printf@plt>:400410: ff 25 02 0c 20 00 jmpq *0x200c02(%rip) # 601018 <printf@GLIBC_2.2.5>400416: 68 00 00 00 00 pushq $0x040041b: e9 e0 ff ff ff jmpq 400400 <.plt>0000000000400420 <__libc_start_main@plt>:400420: ff 25 fa 0b 20 00 jmpq *0x200bfa(%rip) # 601020 <__libc_start_main@GLIBC_2.2.5>400426: 68 01 00 00 00 pushq $0x140042b: e9 d0 ff ff ff jmpq 400400 <.plt>0000000000400430 <__gmon_start__@plt>:400430: ff 25 f2 0b 20 00 jmpq *0x200bf2(%rip) # 601028 <__gmon_start__>400436: 68 02 00 00 00 pushq $0x240043b: e9 c0 ff ff ff jmpq 400400 <.plt>Disassembly of section .text:0000000000400440 <_start>:400440: 31 ed xor %ebp,%ebp400442: 49 89 d1 mov %rdx,%r9400445: 5e pop %rsi400446: 48 89 e2 mov %rsp,%rdx400449: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp40044d: 50 push %rax40044e: 54 push %rsp40044f: 49 c7 c0 10 06 40 00 mov $0x400610,%r8400456: 48 c7 c1 a0 05 40 00 mov $0x4005a0,%rcx40045d: 48 c7 c7 2d 05 40 00 mov $0x40052d,%rdi400464: e8 b7 ff ff ff callq 400420 <__libc_start_main@plt>400469: f4 hlt 40046a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)0000000000400470 <deregister_tm_clones>:400470: b8 3f 10 60 00 mov $0x60103f,%eax400475: 55 push %rbp400476: 48 2d 38 10 60 00 sub $0x601038,%rax40047c: 48 83 f8 0e cmp $0xe,%rax400480: 48 89 e5 mov %rsp,%rbp400483: 77 02 ja 400487 <deregister_tm_clones+0x17>400485: 5d pop %rbp400486: c3 retq 400487: b8 00 00 00 00 mov $0x0,%eax40048c: 48 85 c0 test %rax,%rax40048f: 74 f4 je 400485 <deregister_tm_clones+0x15>400491: 5d pop %rbp400492: bf 38 10 60 00 mov $0x601038,%edi400497: ff e0 jmpq *%rax400499: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)00000000004004a0 <register_tm_clones>:4004a0: b8 38 10 60 00 mov $0x601038,%eax4004a5: 55 push %rbp4004a6: 48 2d 38 10 60 00 sub $0x601038,%rax4004ac: 48 c1 f8 03 sar $0x3,%rax4004b0: 48 89 e5 mov %rsp,%rbp4004b3: 48 89 c2 mov %rax,%rdx4004b6: 48 c1 ea 3f shr $0x3f,%rdx4004ba: 48 01 d0 add %rdx,%rax4004bd: 48 d1 f8 sar %rax4004c0: 75 02 jne 4004c4 <register_tm_clones+0x24>4004c2: 5d pop %rbp4004c3: c3 retq 4004c4: ba 00 00 00 00 mov $0x0,%edx4004c9: 48 85 d2 test %rdx,%rdx4004cc: 74 f4 je 4004c2 <register_tm_clones+0x22>4004ce: 5d pop %rbp4004cf: 48 89 c6 mov %rax,%rsi4004d2: bf 38 10 60 00 mov $0x601038,%edi4004d7: ff e2 jmpq *%rdx4004d9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)00000000004004e0 <__do_global_dtors_aux>:4004e0: 80 3d 51 0b 20 00 00 cmpb $0x0,0x200b51(%rip) # 601038 <__TMC_END__>4004e7: 75 11 jne 4004fa <__do_global_dtors_aux+0x1a>4004e9: 55 push %rbp4004ea: 48 89 e5 mov %rsp,%rbp4004ed: e8 7e ff ff ff callq 400470 <deregister_tm_clones>4004f2: 5d pop %rbp4004f3: c6 05 3e 0b 20 00 01 movb $0x1,0x200b3e(%rip) # 601038 <__TMC_END__>4004fa: f3 c3 repz retq 4004fc: 0f 1f 40 00 nopl 0x0(%rax)0000000000400500 <frame_dummy>:400500: 48 83 3d 18 09 20 00 cmpq $0x0,0x200918(%rip) # 600e20 <__JCR_END__>400507: 00 400508: 74 1e je 400528 <frame_dummy+0x28>40050a: b8 00 00 00 00 mov $0x0,%eax40050f: 48 85 c0 test %rax,%rax400512: 74 14 je 400528 <frame_dummy+0x28>400514: 55 push %rbp400515: bf 20 0e 60 00 mov $0x600e20,%edi40051a: 48 89 e5 mov %rsp,%rbp40051d: ff d0 callq *%rax40051f: 5d pop %rbp400520: e9 7b ff ff ff jmpq 4004a0 <register_tm_clones>400525: 0f 1f 00 nopl (%rax)400528: e9 73 ff ff ff jmpq 4004a0 <register_tm_clones>000000000040052d <main>:40052d: 55 push %rbp40052e: 48 89 e5 mov %rsp,%rbp400531: 48 83 ec 10 sub $0x10,%rsp400535: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)40053c: c7 45 f8 03 00 00 00 movl $0x3,-0x8(%rbp)400543: 8b 55 f8 mov -0x8(%rbp),%edx400546: 8b 45 fc mov -0x4(%rbp),%eax400549: 89 d6 mov %edx,%esi40054b: 89 c7 mov %eax,%edi40054d: b8 00 00 00 00 mov $0x0,%eax400552: e8 2a 00 00 00 callq 400581 <add>400557: 89 45 f4 mov %eax,-0xc(%rbp)40055a: 8b 05 d4 0a 20 00 mov 0x200ad4(%rip),%eax # 601034 <shared>400560: 89 45 f0 mov %eax,-0x10(%rbp)400563: 8b 55 f0 mov -0x10(%rbp),%edx400566: 8b 45 f4 mov -0xc(%rbp),%eax400569: 89 c6 mov %eax,%esi40056b: bf 30 06 40 00 mov $0x400630,%edi400570: b8 00 00 00 00 mov $0x0,%eax400575: e8 96 fe ff ff callq 400410 <printf@plt>40057a: b8 ff ff ff ff mov $0xffffffff,%eax40057f: c9 leaveq 400580: c3 retq 0000000000400581 <add>:400581: 55 push %rbp400582: 48 89 e5 mov %rsp,%rbp400585: 89 7d fc mov %edi,-0x4(%rbp)400588: 89 75 f8 mov %esi,-0x8(%rbp)40058b: 8b 45 f8 mov -0x8(%rbp),%eax40058e: 8b 55 fc mov -0x4(%rbp),%edx400591: 01 d0 add %edx,%eax400593: 5d pop %rbp400594: c3 retq 400595: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)40059c: 00 00 00 40059f: 90 nop00000000004005a0 <__libc_csu_init>:4005a0: 41 57 push %r154005a2: 41 89 ff mov %edi,%r15d4005a5: 41 56 push %r144005a7: 49 89 f6 mov %rsi,%r144005aa: 41 55 push %r134005ac: 49 89 d5 mov %rdx,%r134005af: 41 54 push %r124005b1: 4c 8d 25 58 08 20 00 lea 0x200858(%rip),%r12 # 600e10 <__frame_dummy_init_array_entry>4005b8: 55 push %rbp4005b9: 48 8d 2d 58 08 20 00 lea 0x200858(%rip),%rbp # 600e18 <__init_array_end>4005c0: 53 push %rbx4005c1: 4c 29 e5 sub %r12,%rbp4005c4: 31 db xor %ebx,%ebx4005c6: 48 c1 fd 03 sar $0x3,%rbp4005ca: 48 83 ec 08 sub $0x8,%rsp4005ce: e8 0d fe ff ff callq 4003e0 <_init>4005d3: 48 85 ed test %rbp,%rbp4005d6: 74 1e je 4005f6 <__libc_csu_init+0x56>4005d8: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)4005df: 00 4005e0: 4c 89 ea mov %r13,%rdx4005e3: 4c 89 f6 mov %r14,%rsi4005e6: 44 89 ff mov %r15d,%edi4005e9: 41 ff 14 dc callq *(%r12,%rbx,8)4005ed: 48 83 c3 01 add $0x1,%rbx4005f1: 48 39 eb cmp %rbp,%rbx4005f4: 75 ea jne 4005e0 <__libc_csu_init+0x40>4005f6: 48 83 c4 08 add $0x8,%rsp4005fa: 5b pop %rbx4005fb: 5d pop %rbp4005fc: 41 5c pop %r124005fe: 41 5d pop %r13400600: 41 5e pop %r14400602: 41 5f pop %r15400604: c3 retq 400605: 90 nop400606: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)40060d: 00 00 00 0000000000400610 <__libc_csu_fini>:400610: f3 c3 repz retq Disassembly of section .fini:0000000000400614 <_fini>:400614: 48 83 ec 08 sub $0x8,%rsp400618: 48 83 c4 08 add $0x8,%rsp40061c: c3 retq
[dev1@localhost test02]$
二、空间与地址分配
对于多个输入目标文件,链接器如何将它们的各个段合并到输出文件?或者说,输出文件中的空间如何分配给输入文件?
1、相似段合并
将所有输入文件的“.text”合并到输出文件的“.text”段,接着是“.data”段、“.bss”段等,如图4-2所示
“链接器为目标文件分配地址和空间”这句话中的“地址和空间”其实有两个含义:第一个是在输出的可执行文件中的空间;第二个是在装载后的虚拟地址中的虚拟地址空间。对于有实际数据的段,
比如“.text”和“.data”来说,它们在文件中和虚拟地址中都要分配空间,因为它们在这两者中都存在;
事实上,我们在这里谈到的空间分配只关注于虚拟地址空间的分配,因为这个关系到链接器后面的关于地址计算的步骤,而可执行文件本身的空间分配与链接过程关系并不是很大。
关于可执行文件和虚拟地址空间之间的关系请参考第10章“可执行文件的装载与进程”。
现在的链接器空间分配的策略基本上都采用上述方法,使用这种方法的链接器一般都采用一种叫两步链接(Two-pass Linking)的方法。也就是说整个链接过程分两步。
- 第一步 空间与地址分配 扫描所有的输入目标文件,并且获得它们的各个段的长度、属性和位置,并且将输入目标文件中的符号表中所有的符号定义和符号引用收集起来,统一放到一个全局符号表。这一步中,链接器将能够获得所有输入目标文件的段长度,并且将它们合并,计算出输出文件中各个段合并后的长度与位置,并建立映射关系。
- 第二步 符号解析与重定位 使用上面第一步中收集到的所有信息,读取输入文件中段的数据、重定位信息,并且进行符号解析与重定位、调整代码中的地址等。事实上第二步是链接过程的核心,特别是重定位过程。
三、即虚拟地址VMA(Virtual Memory Address)
VMA表示Virtual Memory Address,即虚拟地址,LMA表示Load Memory Address,即加载地址,正常情况下这两个值应该是一样的,但是在有些嵌入式系统中,特别是在那些程序放在ROM的系统中时,LMA和VMA是不相同的。这里我们只要关注VMA即可。
链接前后的程序中所使用的地址已经是程序在进程中的虚拟地址,即我们关心上面各个段中的VMA(Virtual Memory Address)和Size,而忽略文件偏移(File off)。
如下面所示。我们可以看到,在链接之前,目标文件中的所有段的VMA都是0,因为虚拟空间还没有被分配,所以它们默认都为0。等到链接之后,可执行文件“ab”中的各个段都被分配到了相应的虚拟地址。这里的输出程序“ab”中,“.text”段被分配到了地址0x0000000000400440,大小为0x1c2字节(Size字段就是大小);“.data”段从地址0x0000000000601030开始,大小为0x8字节(Size字段)。
- 链接前main.o add.o
add.o
节:
Idx Name Size VMA LMA File off Algn0 .text 00000014 0000000000000000 0000000000000000 00000040 2**0CONTENTS, ALLOC, LOAD, READONLY, CODE1 .data 00000004 0000000000000000 0000000000000000 00000054 2**2CONTENTS, ALLOC, LOAD, DATA
main.o
节:
Idx Name Size VMA LMA File off Algn0 .text 0000004e 0000000000000000 0000000000000000 00000040 2**0CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data 00000000 0000000000000000 0000000000000000 0000008e 2**0CONTENTS, ALLOC, LOAD, DATA
- 链接后mainadd
节:
Idx Name Size VMA LMA File off Algn12 .text 000001c2 0000000000400440 0000000000400440 00000440 2**4CONTENTS, ALLOC, LOAD, READONLY, CODE23 .data 00000008 0000000000601030 0000000000601030 00001030 2**2CONTENTS, ALLOC, LOAD, DATA
为什么链接器要将可执行文件“ab”的“.text”分配到0000000000400440、将“.data”分配0x0000000000601030?而不是从虚拟空间的0地址开始分配呢?这涉及操作系统的进程虚拟地址空间的分配规则,在Linux下,ELF可执行文件默认从地址0000000000400440开始分配。关于进程的虚拟地址分配等相关内容我们将在“可执行文件的装载与进程”这一章进行详细的分析。
四、重定位
在完成空间和地址的分配步骤以后,链接器就进入了符号解析与重定位的步骤,这也是静态链接的核心内容。在分析符号解析和重定位之前,首先让我们来看看“main.o”里面是怎么使用这两个外部符号的,也就是说我们在“main.c”的源程序里面使用了“shared”变量和“add”函数,那么编译器在将“main.c”编译成指令时,它如何访问“shared”变量?如何调用“add”函数?
我们知道在程序的代码里面使用的都是虚拟地址,在这里也可以看到“main”的起始地址为0x00000000,这是因为在未进行前面提到过的空间分配之前,目标文件代码段中的起始地址以0x00000000开始,等到空间分配完成以后,各个函数才会确定自己在虚拟地址空间中的位置。
- 我们可以很清楚地看到“main.o”的反汇编结果中,“main.o”共定义了一个函数main。这个函数占用0x53个字节,共24条指令;最左边那列是每条指令的偏移量.
当前面一步完成之后,链接器开始计算各个符号的虚拟地址。因为各个符号在段内的相对位置是固定的,所以这时候其实“main”、“shared”和“add”的地址也已经是确定的了,只不过链接器须要给每个符号加上一个偏移量,使它们能够调整到正确的虚拟地址。比如我们假设“main.o”中的“main”函数相对于“main.o”的“.text”段的偏移是X,但是经过链接合并以后,“main.o”的“.text”段位于虚拟地址0x0000000000400440,那么“main”的地址应该是0x08048094 + X。从前面“objdump”的输出看到,“main”不位于“main.o”的“.text”段的最开始,也就是偏移为X,所以“main”这个符号在最终的输出文件中的地址应该是0x0000000000400440+ X,即0x000000000040052d.
- 链接前
查看链接前main.c中反汇编的结果
[dev1@localhost test02]$ objdump -d main.omain.o: 文件格式 elf64-x86-64Disassembly of section .text:0000000000000000 <main>:0: 55 push %rbp1: 48 89 e5 mov %rsp,%rbp4: 48 83 ec 10 sub $0x10,%rsp8: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)f: c7 45 f8 03 00 00 00 movl $0x3,-0x8(%rbp)16: 8b 55 f8 mov -0x8(%rbp),%edx19: 8b 45 fc mov -0x4(%rbp),%eax1c: 89 d6 mov %edx,%esi1e: 89 c7 mov %eax,%edi20: b8 00 00 00 00 mov $0x0,%eax25: e8 00 00 00 00 callq 2a <main+0x2a>2a: 89 45 f4 mov %eax,-0xc(%rbp)2d: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 33 <main+0x33>33: 89 45 f0 mov %eax,-0x10(%rbp)36: 8b 55 f0 mov -0x10(%rbp),%edx39: 8b 45 f4 mov -0xc(%rbp),%eax3c: 89 c6 mov %eax,%esi3e: bf 00 00 00 00 mov $0x0,%edi43: b8 00 00 00 00 mov $0x0,%eax48: e8 00 00 00 00 callq 4d <main+0x4d>4d: b8 ff ff ff ff mov $0xffffffff,%eax52: c9 leaveq 53: c3 retq
[dev1@localhost test02]$ ^C
- 链接后
查看链接前mainadd中反汇编的结果
[dev1@localhost test02]$ objdump -d mainaddmainadd: 文件格式 elf64-x86-64Disassembly of section .init:00000000004003e0 <_init>:4003e0: 48 83 ec 08 sub $0x8,%rsp4003e4: 48 8b 05 0d 0c 20 00 mov 0x200c0d(%rip),%rax # 600ff8 <__gmon_start__>4003eb: 48 85 c0 test %rax,%rax4003ee: 74 05 je 4003f5 <_init+0x15>4003f0: e8 3b 00 00 00 callq 400430 <__gmon_start__@plt>4003f5: 48 83 c4 08 add $0x8,%rsp4003f9: c3 retq Disassembly of section .plt:0000000000400400 <.plt>:400400: ff 35 02 0c 20 00 pushq 0x200c02(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>400406: ff 25 04 0c 20 00 jmpq *0x200c04(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>40040c: 0f 1f 40 00 nopl 0x0(%rax)0000000000400410 <printf@plt>:400410: ff 25 02 0c 20 00 jmpq *0x200c02(%rip) # 601018 <printf@GLIBC_2.2.5>400416: 68 00 00 00 00 pushq $0x040041b: e9 e0 ff ff ff jmpq 400400 <.plt>0000000000400420 <__libc_start_main@plt>:400420: ff 25 fa 0b 20 00 jmpq *0x200bfa(%rip) # 601020 <__libc_start_main@GLIBC_2.2.5>400426: 68 01 00 00 00 pushq $0x140042b: e9 d0 ff ff ff jmpq 400400 <.plt>0000000000400430 <__gmon_start__@plt>:400430: ff 25 f2 0b 20 00 jmpq *0x200bf2(%rip) # 601028 <__gmon_start__>400436: 68 02 00 00 00 pushq $0x240043b: e9 c0 ff ff ff jmpq 400400 <.plt>Disassembly of section .text:0000000000400440 <_start>:400440: 31 ed xor %ebp,%ebp400442: 49 89 d1 mov %rdx,%r9400445: 5e pop %rsi400446: 48 89 e2 mov %rsp,%rdx400449: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp40044d: 50 push %rax40044e: 54 push %rsp40044f: 49 c7 c0 10 06 40 00 mov $0x400610,%r8400456: 48 c7 c1 a0 05 40 00 mov $0x4005a0,%rcx40045d: 48 c7 c7 2d 05 40 00 mov $0x40052d,%rdi400464: e8 b7 ff ff ff callq 400420 <__libc_start_main@plt>400469: f4 hlt 40046a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)0000000000400470 <deregister_tm_clones>:400470: b8 3f 10 60 00 mov $0x60103f,%eax400475: 55 push %rbp400476: 48 2d 38 10 60 00 sub $0x601038,%rax40047c: 48 83 f8 0e cmp $0xe,%rax400480: 48 89 e5 mov %rsp,%rbp400483: 77 02 ja 400487 <deregister_tm_clones+0x17>400485: 5d pop %rbp400486: c3 retq 400487: b8 00 00 00 00 mov $0x0,%eax40048c: 48 85 c0 test %rax,%rax40048f: 74 f4 je 400485 <deregister_tm_clones+0x15>400491: 5d pop %rbp400492: bf 38 10 60 00 mov $0x601038,%edi400497: ff e0 jmpq *%rax400499: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)00000000004004a0 <register_tm_clones>:4004a0: b8 38 10 60 00 mov $0x601038,%eax4004a5: 55 push %rbp4004a6: 48 2d 38 10 60 00 sub $0x601038,%rax4004ac: 48 c1 f8 03 sar $0x3,%rax4004b0: 48 89 e5 mov %rsp,%rbp4004b3: 48 89 c2 mov %rax,%rdx4004b6: 48 c1 ea 3f shr $0x3f,%rdx4004ba: 48 01 d0 add %rdx,%rax4004bd: 48 d1 f8 sar %rax4004c0: 75 02 jne 4004c4 <register_tm_clones+0x24>4004c2: 5d pop %rbp4004c3: c3 retq 4004c4: ba 00 00 00 00 mov $0x0,%edx4004c9: 48 85 d2 test %rdx,%rdx4004cc: 74 f4 je 4004c2 <register_tm_clones+0x22>4004ce: 5d pop %rbp4004cf: 48 89 c6 mov %rax,%rsi4004d2: bf 38 10 60 00 mov $0x601038,%edi4004d7: ff e2 jmpq *%rdx4004d9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)00000000004004e0 <__do_global_dtors_aux>:4004e0: 80 3d 51 0b 20 00 00 cmpb $0x0,0x200b51(%rip) # 601038 <__TMC_END__>4004e7: 75 11 jne 4004fa <__do_global_dtors_aux+0x1a>4004e9: 55 push %rbp4004ea: 48 89 e5 mov %rsp,%rbp4004ed: e8 7e ff ff ff callq 400470 <deregister_tm_clones>4004f2: 5d pop %rbp4004f3: c6 05 3e 0b 20 00 01 movb $0x1,0x200b3e(%rip) # 601038 <__TMC_END__>4004fa: f3 c3 repz retq 4004fc: 0f 1f 40 00 nopl 0x0(%rax)0000000000400500 <frame_dummy>:400500: 48 83 3d 18 09 20 00 cmpq $0x0,0x200918(%rip) # 600e20 <__JCR_END__>400507: 00 400508: 74 1e je 400528 <frame_dummy+0x28>40050a: b8 00 00 00 00 mov $0x0,%eax40050f: 48 85 c0 test %rax,%rax400512: 74 14 je 400528 <frame_dummy+0x28>400514: 55 push %rbp400515: bf 20 0e 60 00 mov $0x600e20,%edi40051a: 48 89 e5 mov %rsp,%rbp40051d: ff d0 callq *%rax40051f: 5d pop %rbp400520: e9 7b ff ff ff jmpq 4004a0 <register_tm_clones>400525: 0f 1f 00 nopl (%rax)400528: e9 73 ff ff ff jmpq 4004a0 <register_tm_clones>000000000040052d <main>:40052d: 55 push %rbp40052e: 48 89 e5 mov %rsp,%rbp400531: 48 83 ec 10 sub $0x10,%rsp400535: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)40053c: c7 45 f8 03 00 00 00 movl $0x3,-0x8(%rbp)400543: 8b 55 f8 mov -0x8(%rbp),%edx400546: 8b 45 fc mov -0x4(%rbp),%eax400549: 89 d6 mov %edx,%esi40054b: 89 c7 mov %eax,%edi40054d: b8 00 00 00 00 mov $0x0,%eax400552: e8 2a 00 00 00 callq 400581 <add>400557: 89 45 f4 mov %eax,-0xc(%rbp)40055a: 8b 05 d4 0a 20 00 mov 0x200ad4(%rip),%eax # 601034 <shared>400560: 89 45 f0 mov %eax,-0x10(%rbp)400563: 8b 55 f0 mov -0x10(%rbp),%edx400566: 8b 45 f4 mov -0xc(%rbp),%eax400569: 89 c6 mov %eax,%esi40056b: bf 30 06 40 00 mov $0x400630,%edi400570: b8 00 00 00 00 mov $0x0,%eax400575: e8 96 fe ff ff callq 400410 <printf@plt>40057a: b8 ff ff ff ff mov $0xffffffff,%eax40057f: c9 leaveq 400580: c3 retq 0000000000400581 <add>:400581: 55 push %rbp400582: 48 89 e5 mov %rsp,%rbp400585: 89 7d fc mov %edi,-0x4(%rbp)400588: 89 75 f8 mov %esi,-0x8(%rbp)40058b: 8b 45 f8 mov -0x8(%rbp),%eax40058e: 8b 55 fc mov -0x4(%rbp),%edx400591: 01 d0 add %edx,%eax400593: 5d pop %rbp400594: c3 retq 400595: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)40059c: 00 00 00 40059f: 90 nop00000000004005a0 <__libc_csu_init>:4005a0: 41 57 push %r154005a2: 41 89 ff mov %edi,%r15d4005a5: 41 56 push %r144005a7: 49 89 f6 mov %rsi,%r144005aa: 41 55 push %r134005ac: 49 89 d5 mov %rdx,%r134005af: 41 54 push %r124005b1: 4c 8d 25 58 08 20 00 lea 0x200858(%rip),%r12 # 600e10 <__frame_dummy_init_array_entry>4005b8: 55 push %rbp4005b9: 48 8d 2d 58 08 20 00 lea 0x200858(%rip),%rbp # 600e18 <__init_array_end>4005c0: 53 push %rbx4005c1: 4c 29 e5 sub %r12,%rbp4005c4: 31 db xor %ebx,%ebx4005c6: 48 c1 fd 03 sar $0x3,%rbp4005ca: 48 83 ec 08 sub $0x8,%rsp4005ce: e8 0d fe ff ff callq 4003e0 <_init>4005d3: 48 85 ed test %rbp,%rbp4005d6: 74 1e je 4005f6 <__libc_csu_init+0x56>4005d8: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)4005df: 00 4005e0: 4c 89 ea mov %r13,%rdx4005e3: 4c 89 f6 mov %r14,%rsi4005e6: 44 89 ff mov %r15d,%edi4005e9: 41 ff 14 dc callq *(%r12,%rbx,8)4005ed: 48 83 c3 01 add $0x1,%rbx4005f1: 48 39 eb cmp %rbp,%rbx4005f4: 75 ea jne 4005e0 <__libc_csu_init+0x40>4005f6: 48 83 c4 08 add $0x8,%rsp4005fa: 5b pop %rbx4005fb: 5d pop %rbp4005fc: 41 5c pop %r124005fe: 41 5d pop %r13400600: 41 5e pop %r14400602: 41 5f pop %r15400604: c3 retq 400605: 90 nop400606: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)40060d: 00 00 00 0000000000400610 <__libc_csu_fini>:400610: f3 c3 repz retq Disassembly of section .fini:0000000000400614 <_fini>:400614: 48 83 ec 08 sub $0x8,%rsp400618: 48 83 c4 08 add $0x8,%rsp40061c: c3 retq
[dev1@localhost test02]$
- 链接前的汇编main
0000000000000000 <main>:0: 55 push %rbp1: 48 89 e5 mov %rsp,%rbp4: 48 83 ec 10 sub $0x10,%rsp8: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)f: c7 45 f8 03 00 00 00 movl $0x3,-0x8(%rbp)16: 8b 55 f8 mov -0x8(%rbp),%edx19: 8b 45 fc mov -0x4(%rbp),%eax1c: 89 d6 mov %edx,%esi1e: 89 c7 mov %eax,%edi20: b8 00 00 00 00 mov $0x0,%eax25: e8 00 00 00 00 callq 2a <main+0x2a>26: R_X86_64_PC32 add-0x42a: 89 45 f4 mov %eax,-0xc(%rbp)2d: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 33 <main+0x33>2f: R_X86_64_PC32 shared-0x433: 89 45 f0 mov %eax,-0x10(%rbp)36: 8b 55 f0 mov -0x10(%rbp),%edx39: 8b 45 f4 mov -0xc(%rbp),%eax3c: 89 c6 mov %eax,%esi3e: bf 00 00 00 00 mov $0x0,%edi3f: R_X86_64_32 .rodata43: b8 00 00 00 00 mov $0x0,%eax48: e8 00 00 00 00 callq 4d <main+0x4d>49: R_X86_64_PC32 printf-0x44d: b8 ff ff ff ff mov $0xffffffff,%eax52: c9 leaveq 53: c3 retq
- 链接后的汇编main
000000000040052d <main>:40052d: 55 push %rbp40052e: 48 89 e5 mov %rsp,%rbp400531: 48 83 ec 10 sub $0x10,%rsp400535: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)40053c: c7 45 f8 03 00 00 00 movl $0x3,-0x8(%rbp)400543: 8b 55 f8 mov -0x8(%rbp),%edx400546: 8b 45 fc mov -0x4(%rbp),%eax400549: 89 d6 mov %edx,%esi40054b: 89 c7 mov %eax,%edi40054d: b8 00 00 00 00 mov $0x0,%eax400552: e8 2a 00 00 00 callq 400581 <add>400557: 89 45 f4 mov %eax,-0xc(%rbp)40055a: 8b 05 d4 0a 20 00 mov 0x200ad4(%rip),%eax # 601034 <shared>400560: 89 45 f0 mov %eax,-0x10(%rbp)400563: 8b 55 f0 mov -0x10(%rbp),%edx400566: 8b 45 f4 mov -0xc(%rbp),%eax400569: 89 c6 mov %eax,%esi40056b: bf 30 06 40 00 mov $0x400630,%edi400570: b8 00 00 00 00 mov $0x0,%eax400575: e8 96 fe ff ff callq 400410 <printf@plt>40057a: b8 ff ff ff ff mov $0xffffffff,%eax40057f: c9 leaveq 400580: c3 retq
我们也可以通过完全一样的计算方法得知所有符号的地址,在这个例子里面,有三个全局符号,所以链接器在更新全局符号表的符号地址以后,各个符号的最终地址如表所示。
符号 | 类型 | 虚拟地址 |
---|---|---|
main | 函数 | 000000000040052d <main>: |
add | 函数 | 0000000000400581 <add>: |
printf | 函数 | 0000000000400410 <printf@plt> |
shared | 变量 | 0000000000601034 |
这里,我们重点分析下add调用、shared调用、printf调用
1、add调用
偏移为0x19的指令的一条调用指令,它其实就表示对add函数的调用,如图所示。
紧跟在这条call指令后面的那条指令为add指令,add指令的地址为0x2f,而相对于add指令偏移为“-4”的地址即 0x2f + 4 = 0x33。所以这条call指令的实际调用地址为0x33。我们可以看到0x33存放着并不是add函数的地址,跟前面“shared”一样,“0x00 00 00 00”只是一个临时的假地址,因为在编译的时候,编译器并不知道“add”的真正地址。编译器把这两条指令的地址部分暂时用地址“0x00 00 00 00”代替着,把真正的地址计算工作留给了链接器。我们通过前面的空间与地址分配可以得知,链接器在完成地址和空间分配之后就已经可以确定所有符号的虚拟地址了,那么链接器就可以根据符号的地址对每个需要重定位的指令进行地位修正。
(如上)我们用objdump来反汇编输出程序“main”的代码段,可以看到main函数的两个重定位入口都已经被修正到正确的位置:
这个“call”指令是一条近址相对位移调用指令,它后面跟的是调用指令的下一条指令的偏移量,“call”指令的下一条指令是“add”,它的地址是0x080480bf,所以“相对于add指令偏移量为0x2a”的地址为0x400552 + 0x2a + 4 = 0x400581,
0x400552 + 0x2a + 4是0x400580,少了1,还不知道为啥。后面再说吧。
25: e8 00 00 00 00 callq 2a <main+0x2a>26: R_X86_64_PC32 add-0x42a: 89 45 f4 mov %eax,-0xc(%rbp)
400552: e8 2a 00 00 00 callq 400581 <add>
2、printf调用——同add
2、shared
偏移为0x2d的mov指令,它的二进制显示占据了两行)。对于“shared”的引用是一
条“mov”指令,这条指令总共8个字节,它的作用是将“shared”的地址赋值到eax寄存器+33的偏移地址,前面4个字节是指令码,后面4个字节是“shared”的地址。
当源代码“a.c”在被编译成目标文件时,编译器并不知道“shared”和“add”的地址,因为它们定义在其他目标文件中。所以编译器就暂时把地址0看作是“shared”的地址,我们可以看到这条“mov”指令中,关于“shared”的地址部分为“0x00000000”。
五、重定位表
那么链接器是怎么知道哪些指令是要被调整的呢?这些指令的哪些部分要被调整?怎么调整?比如上面例子中“mov”指令和“call”指令的调整方式就有所不同。事实上在ELF文件中,有一个叫重定位表(Relocation Table)的结构专门用来保存这些与重定位相关的信息。
对于可重定位的ELF文件来说,它必须包含有重定位表,用来描述如何修改相应的段里的内容。对于每个要被重定位的ELF段都有一个对应的重定位表,而一个重定位表往往就是ELF文件中的一个段,所以其实重定位表也可以叫重定位段,我们在这里统一称作重定位表。比如代码段“.text”如有要被重定位的地方,那么会有一个相对应叫“.rel.text”的段保存了代码段的重定位表;如果代码段“.data”有要被重定位的地方,就会有一个相对应叫“.rel.data”的段保存了数据段的重定位表。我们可以使用objdump来查看目标文件的重定位表:
26+4=2a
2f+4=33
49+4=4d
链接完成后,重定位表就没了,只有可重定位的才有重定位表。
[dev1@localhost test02]$ objdump -r main.omain.o: 文件格式 elf64-x86-64RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
0000000000000026 R_X86_64_PC32 add-0x0000000000000004
000000000000002f R_X86_64_PC32 shared-0x0000000000000004
000000000000003f R_X86_64_32 .rodata
0000000000000049 R_X86_64_PC32 printf-0x0000000000000004RELOCATION RECORDS FOR [.eh_frame]:
OFFSET TYPE VALUE
0000000000000020 R_X86_64_PC32 .text[dev1@localhost test02]$
对于32位的Intel x86系列处理器来说,重定位表的结构也很简单,它是一个Elf32_Rel结构的数组,每个数组元素对应一个重定位入口。
Elf32_Rel的定义如下:
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;
六、符号解析
在我们通常的观念里,之所以要链接是因为我们目标文件中用到的符号被定义在其他目标文件,所以要将它们链接起来。比如我们直接使用ld来链接“a.o”,而不将“b.o”作为输入。链接器就会发现shared和add两个符号没有被定义,没有办法完成链接工作:
[dev1@localhost test02]$ gcc main.o -o mainadd.out
main.o:在函数‘main’中:
main.c:(.text+0x26):对‘add’未定义的引用
main.c:(.text+0x2f):对‘shared’未定义的引用
collect2: 错误:ld 返回 1
[dev1@localhost test02]$
其实重定位过程也伴随着符号的解析过程,每个目标文件都可能定义一些符号,也可能引用到定义在其他目标文件
的符号。重定位的过程中,每个重定位的入口都是对一个符号的引用,那么当链接器须要对某个符号的引用进行重定位时,它就要确定这个符号的目标地址。这时候链接器就会去查找由所有输入目标文件的符号表组成的全局符号表,找到相应的符号后进行重定位。
比如我们查看“a.o”的符号表:
[dev1@localhost test02]$ readelf -s main.oSymbol table '.symtab' contains 13 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c2: 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 0 SECTION LOCAL DEFAULT 7 7: 0000000000000000 0 SECTION LOCAL DEFAULT 8 8: 0000000000000000 0 SECTION LOCAL DEFAULT 6 9: 0000000000000000 84 FUNC GLOBAL DEFAULT 1 main10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND add11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND shared12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
[dev1@localhost test02]$
“GLOBAL”类型的符号,除了“main”函数是定义在代码段之外,其他两个“shared”和“add”都是“UND”,即“undefined”未定义类型,这种未定义的符号都是因为该目标文件中有关于它们的重定位项。所以在链接器
扫描完所有的输入目标文件之后,所有这些未定义的符号都应该能够在全局符号表中找到,否则链接器就报符号未定义错误。
七、c++相关问题
1、重复代码消除
C++编译器在很多时候会产生重复的代码,比如模板(Templates)、外部内联函数(Extern Inline Function)和虚函数表(Virtual FunctionTable)都有可能在不同的编译单元里生成相同的代码。最简单的情况就拿模板来说,模板从本质上来讲很像宏,当模板在一个编译单元里被实例化时,它并不知道自己是否在别的编译单元也被实例化了。所以当一个模板在多个编译单元同时实例化成相同的类型的时候,必然会生成重复的代码。
当然,最简单的方案就是不管这些,将这些重复的代码都保留下来。不过这样做的主要问题有以下几方面。
1.空间浪费。可以想象一个有几百个编译单元的工程同时实例化了许多个模板,最后链接的时候必须将这些重复的代码消除掉,否则最终程序的大小肯定会膨胀得很厉害。
2.地址较易出错。有可能两个指向同一个函数的指针会不相等。
3.指令运行效率较低。因为现代的CPU都会对指令和数据进行缓存,如果同样一份指令有多份副本,那么指令Cache的命中率就会降低。
一个比较有效的做法就是将每个模板的实例代码都单独地存放在一个段里,每个段只包含一个模板实例。比如有个模板函数是 add(), 某个编译单元以int类型和float类型实例化了该模板函数,那么该编译单元的目标文件中就包含了两个该模板实例的段。为了简单起见,我们假设这两个段的名字分别叫 .temp.add 和 .temp.add。 这样,当别的编译单元也以int或float类型实例化该模板函数后,也会生成同样的名字,这样链接器在最终链接的时候可以区分这些相同的模板实例段,然后将它们合并入最后的代码段。
2、全局构造与析构
我们知道一般的一个C/C++程序是从main开始执行的,随着main函数的结束而结束。然而,其实在main函数被调用之前,为了程序能够顺利执行,要先初始化进程执行环境,比如堆分配初始化(malloc、free)、线程子系统等,关于main之前所执行的部分。C++的全局对象构造函数也是在这一时期被执行的,我们知道C++的全局对象的构造函数在main之前被执行,C++全局对象的析构函数在main之后被执行。
Linux系统下一般程序的入口是“_start”,这个函数是Linux系统库(Glibc)的一部分。当我们的程序与Glibc库链接在一起形成最终可执行文件以后,这个函数就是程序的初始化部分的入口,程序初始化部分完成一系列初始化过程之后,会调用main函数来执行程序的主体。在main函数执行完成以后,返回到初始化部分,它进行一些清理工作,然后结束进程。对于有些场合,程序的一些特定的操作必须在main函数之前被执行,还有一些操作必须在main函数之后被执行,其中很具有代表性的就是C++的全局对象的构造和析构函数。因此ELF文件还定义了两
种特殊的段。
- .init 该段里面保存的是可执行指令,它构成了进程的初始化代码。因此,当一个程序开始运行时,在main函数被调用之前,Glibc的初始化部分安排执行这个段的中的代码。
- .fini 该段保存着进程终止代码指令。因此,当一个程序的main函数正常退出时,Glibc会安排执行这个段中的代码。
这两个段.init和.fini的存在有着特别的目的,如果一个函数放到.init段,在main函数执行前系统就会执行它。同理,假如一个函数放到.fint段,在main函数返回后该函数就会被执行。利用这两个特性,C++的全局构造和析构函数就由此实现。
3、C++与ABI
既然每个编译器都能将源代码编译成目标文件,那么有没有不同编译器编译出来的目标文件是不能够相互链接的呢?有没有可能将MSVC编译出来的目标文件和GCC编译出来的目标文件链接到一起,形成一个可执行文件呢?
对于上面这些问题,首先我们可以想到的是,如果要将两个不同编译器的编译结果链接到一起,那么,首先链接器必须支持这两个编译器产生的目标文件的格式。比如MSVC编译的目标文件是PE/COFF格式的,而GCC编译的结果是ELF格式的,链接器必须同时认识这两种格式才行,否则肯定没戏。那是不是链接器只要同时认识目标文件的格式就可以了呢?
事实并不像我们想象的那么简单,如果要使两个编译器编译出来的目标文件能够相互链接,那么这两个目标文件必须满足下面这些条件:采用同样的目标文件格式、拥有同样的符号修饰标准、变量的内存分布方式相同、函数的调用方式相同,等等。其中我们把符号修饰标准、变量内存布局、函数调用方式等这些跟可执行代码二进制兼容性相关的内容称为ABI(Application Binary Interface)。
ABI与API的区别
-
ABI是定义二进制级别的,两个模块的接口
比如一个二进制模块想要调用另外一个二进制模块提供的功能,它需要知道怎样通过汇编语言(即机器指令)去调用,以及怎样传递相应的参数和返回值(通过寄存器还是栈内存,以及参数压栈的顺序等细节)。 -
API是源代码级别的两个模块的接口
是提供到语言层次的函数调用,已经是和具体语言相关的。
八、静态库链接
程序之所以有用,因为它会有输入输出,这些输入输出的对象可以是数据,可以是人,也可以是另外一个程序,还可以是另外一台计算机,一个没有输入输出的程序没有任何意义。但是一个程序如何做到输入输出呢?最简单的办法是使用操作系统提供的应用程序编程接口(API,Application Programming Interface)。
让我们还是先回到一个比较初步的问题,就是程序如何使用操作系统提供的API。在一般的情况下,一种语言的开发环境往往会附带有语言库(Language Library)。这些库就是对操作系统的API的包装,比如我们经典的C语言版“Hello World”程序,它使用C语言标准库的“printf”函数来输出一个字符串,“printf”函数对字符串进行一些必要的处理以后,最后会调用操作系统提供的API。各个操作系统下,往终端输出字符串的API都不一样,在Linux下,它是一个“write”的系统调用,而在Windows下它是“WriteConsole”系统API。
其实一个静态库可以简单地看成一组目标文件的集合,即很多目标文件经过压缩打包后形成的一个文件。比如我们在Linux中最常用的C语言静态库libc位于/usr/lib/libc.a,它属于glibc项目的一部分;
像Windows这样的平台上,最常使用的C语言库是由集成开发环境所附带的运行库,这些库一般由编译器厂商提供,比如Visual C++附带了多个版本的C/C++运行库。表4-3列出了VC2008(内部版本号VC9)所附带的一部分C运行库(库文件存放在VC安装目录下的lib\目录)。
在一个C语言的运行库中,包含了很多跟系统功能相关的代码,比如输入输出、文件操作、时间日期、内存管理等。glibc本身是用C语言开发的,它由成百上千个C语言源代码文件组成,也就是说,编译完成以后有相同数量的目标文件,比如输入输出有printf.o,scanf.o;文件操作有fread.o,fwrite.o;时间日期有date.o,time.o;内存管理有malloc.o等。把这些零散的目标文件直接提供给库的使用者,很大程度上会造成文件传输、管理和组织方面的不便,于是通常人们使用“ar”压缩程序将这些目标文件压缩到一起,并且对其进行编号和索引,以便于查找和检索,就形成了libc.a这个静态库文件。在我的机器上,该文件有2.8 MB大小,我们也可以使用“ar”工具来查看这个文件包含了哪些目标文件:
[dev1@localhost test01]$ ar -t /usr/lib64/libc.a
init-first.o
libc-start.o
sysdep.o
version.o
check_fds.o
libc-tls.o
elf-init.o
dso_handle.o
errno.o
errno-loc.o
iconv_open.o
iconv.o
iconv_close.o
gconv_open.o
gconv.o
gconv_close.o
gconv_db.o
gconv_conf.o
gconv_builtin.o
gconv_simple.o
gconv_trans.o
gconv_cache.o
gconv_dl.o
setlocale.o
findlocale.o
loadlocale.o
loadarchive.o
localeconv.o
nl_langinfo.o
nl_langinfo_l.o
mb_cur_max.o
newlocale.o
duplocale.o
freelocale.o
uselocale.o
。。。(略)
- 静态链接过程可以简单理解为如下图过程
- 查看静态链接的过程
当我们编译和链接一个普通C程序的时候,不仅要用到C语言库libc.a,而且还有其他一些辅助性质的目标文件和库。我们可以使用下面的GCC命令编译“hello.c”,“-verbose”表示将整个编译链接过程的中间步骤打印出来:
这里要使用“- fno-builtin ”参数。是因为默认没有参数的情况下,GCC会将printf(“Hello World”)替换成“puts”函数,以提高运行速度,我们可以使用“ fno-builtin ”关闭这个内置函数优化选项。
[dev1@localhost test01]$ gcc -static --verbose -fno-builtin hello.c
使用内建 specs。
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
目标:x86_64-redhat-linux
配置为:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
线程模型:posix
gcc 版本 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
COLLECT_GCC_OPTIONS='-static' '-v' '-fno-builtin' '-mtune=generic' '-march=x86-64'/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1 -quiet -v hello.c -quiet -dumpbase hello.c -mtune=generic -march=x86-64 -auxbase hello -version -fno-builtin -o /tmp/ccws6Wbm.s
GNU C (GCC) 版本 4.8.5 20150623 (Red Hat 4.8.5-44) (x86_64-redhat-linux)由 GNU C 版本 4.8.5 20150623 (Red Hat 4.8.5-44) 编译,GMP 版本 6.0.0,MPFR 版本 3.1.1,MPC 版本 1.0.1
GGC 准则:--param ggc-min-expand=100 --param ggc-min-heapsize=131072
忽略不存在的目录“/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include-fixed”
忽略不存在的目录“/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/include”
#include "..." 搜索从这里开始:
#include <...> 搜索从这里开始:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/usr/local/include/usr/include
搜索列表结束。
GNU C (GCC) 版本 4.8.5 20150623 (Red Hat 4.8.5-44) (x86_64-redhat-linux)由 GNU C 版本 4.8.5 20150623 (Red Hat 4.8.5-44) 编译,GMP 版本 6.0.0,MPFR 版本 3.1.1,MPC 版本 1.0.1
GGC 准则:--param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 231b3394950636dbfe0428e88716bc73
COLLECT_GCC_OPTIONS='-static' '-v' '-fno-builtin' '-mtune=generic' '-march=x86-64'as -v --64 -o /tmp/ccjGXSsy.o /tmp/ccws6Wbm.s
GNU assembler version 2.27 (x86_64-redhat-linux) using BFD version version 2.27-44.base.el7
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-static' '-v' '-fno-builtin' '-mtune=generic' '-march=x86-64'/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --hash-style=gnu -m elf_x86_64 -static /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbeginT.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. /tmp/ccjGXSsy.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o
[dev1@localhost test01]$
关键的三个步骤上面已经表示出来了,
- 第一步是调用cc1程序,这个程序实际上就是GCC的C语言编译器,它将“hello.c”编译成一个临时的汇编文件“/tmp/ccws6Wbm.s”;
/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1 -quiet -v hello.c -quiet -dumpbase hello.c -mtune=generic -march=x86-64 -auxbase hello -version -fno-builtin -o /tmp/ccws6Wbm.s
- 然后调用as程序,as程序是GNU的汇编器,它将“/tmp/ccUhtGSB.s”汇编成临时目标文件“/tmp/ccjGXSsy.o”,这个“/tmp/ccQZRPL5.o”实际上就是前面的“hello.o”;
as -v --64 -o /tmp/ccjGXSsy.o /tmp/ccws6Wbm.s
- 接着最关键的步骤是最后一步,GCC调用collect2程序来完成最后的链接。
/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --hash-style=gnu -m elf_x86_64 -static /usr/lib/gcc/x86_64-redhat-linux/4.8.5/…/…/…/…/lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/…/…/…/…/lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbeginT.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/…/…/…/…/lib64 -L/lib/…/lib64 -L/usr/lib/…/lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/…/…/… /tmp/ccjGXSsy.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/…/…/…/…/lib64/crtn.o
但是按照我们之前的理解,链接过程应该由ld链接器来完成,这里怎么忽然杀出个collect2?这是个什么程序?实际上collect2可以看作是ld链接器的一个包装,它会调用ld链接器来完成对目标文件的链接,然后再对链接结果进行一些处理,主要是收集所有与程序初始化相关的信息并且构造初始化的结构。
- 出现的问题和解决方案
问题
/usr/bin/ld: 找不到 -lc
collect2: 错误:ld 返回 1
原因
linux 系统 下安装 glibc-devel、glibc和gcc-c++ 时,都不会安装libc.a. ** 只安装libc.so.** 所以当使用**-static** 时,libc.a C库不能使用。只能报错:找不到libc了 。
解决方案
安装glibc-static ,安装需要一定的权限 sudo
sudo yum install glibc-static
-
静态链接的可执行文件大小 —— 861.2KB
-
动态链接的可执行文件大小 —— 8.3KB
参考
1、《程序员的自我修养链接装载与库》