【编译、链接、装载九】静态链接

【编译和链接九】静态链接

  • 一、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.o

add.o:     文件格式 elf64-x86-64
add.o
体系结构:i386:x86-64,标志 0x00000011:
HAS_RELOC, HAS_SYMS
起始地址 0x0000000000000000

节:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00000014  0000000000000000  0000000000000000  00000040  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00000004  0000000000000000  0000000000000000  00000054  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  0000000000000000  0000000000000000  00000058  2**0
                  ALLOC
  3 .comment      0000002e  0000000000000000  0000000000000000  00000058  2**0
                  CONTENTS, READONLY
  4 .note.GNU-stack 00000000  0000000000000000  0000000000000000  00000086  2**0
                  CONTENTS, READONLY
  5 .eh_frame     00000038  0000000000000000  0000000000000000  00000088  2**3
                  CONTENTS, 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 add


Contents 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   %rbp
   1:	48 89 e5             	mov    %rsp,%rbp
   4:	89 7d fc             	mov    %edi,-0x4(%rbp)
   7:	89 75 f8             	mov    %esi,-0x8(%rbp)
   a:	8b 45 f8             	mov    -0x8(%rbp),%eax
   d:	8b 55 fc             	mov    -0x4(%rbp),%edx
  10:	01 d0                	add    %edx,%eax
  12:	5d                   	pop    %rbp
  13:	c3                   	retq   
[dev1@localhost test02]$ 
  • 链接前main.o
[dev1@localhost test02]$ objdump -s -d -x -t main.o

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         00000054  0000000000000000  0000000000000000  00000040  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000000  0000000000000000  0000000000000000  00000094  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  0000000000000000  0000000000000000  00000094  2**0
                  ALLOC
  3 .rodata       00000017  0000000000000000  0000000000000000  00000094  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      0000002e  0000000000000000  0000000000000000  000000ab  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  0000000000000000  0000000000000000  000000d9  2**0
                  CONTENTS, READONLY
  6 .eh_frame     00000038  0000000000000000  0000000000000000  000000e0  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  .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 printf


Contents 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,.shared
 0010 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   %rbp
   1:	48 89 e5             	mov    %rsp,%rbp
   4:	48 83 ec 10          	sub    $0x10,%rsp
   8:	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),%edx
  19:	8b 45 fc             	mov    -0x4(%rbp),%eax
  1c:	89 d6                	mov    %edx,%esi
  1e:	89 c7                	mov    %eax,%edi
  20:	b8 00 00 00 00       	mov    $0x0,%eax
  25:	e8 00 00 00 00       	callq  2a <main+0x2a>
			26: R_X86_64_PC32	add-0x4
  2a:	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-0x4
  33:	89 45 f0             	mov    %eax,-0x10(%rbp)
  36:	8b 55 f0             	mov    -0x10(%rbp),%edx
  39:	8b 45 f4             	mov    -0xc(%rbp),%eax
  3c:	89 c6                	mov    %eax,%esi
  3e:	bf 00 00 00 00       	mov    $0x0,%edi
			3f: R_X86_64_32	.rodata
  43:	b8 00 00 00 00       	mov    $0x0,%eax
  48:	e8 00 00 00 00       	callq  4d <main+0x4d>
			49: R_X86_64_PC32	printf-0x4
  4d:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
  52:	c9                   	leaveq 
  53:	c3                   	retq   
[dev1@localhost test02]$ 


  • 链接后mainadd
[dev1@localhost test02]$ objdump -s -d -x -t mainadd

mainadd:     文件格式 elf64-x86-64
mainadd
体系结构:i386:x86-64,标志 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
起始地址 0x0000000000400440

程序头:
    PHDR off    0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**3
         filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r-x
  INTERP off    0x0000000000000238 vaddr 0x0000000000400238 paddr 0x0000000000400238 align 2**0
         filesz 0x000000000000001c memsz 0x000000000000001c flags r--
    LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21
         filesz 0x000000000000079c memsz 0x000000000000079c flags r-x
    LOAD off    0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**21
         filesz 0x0000000000000228 memsz 0x0000000000000230 flags rw-
 DYNAMIC off    0x0000000000000e28 vaddr 0x0000000000600e28 paddr 0x0000000000600e28 align 2**3
         filesz 0x00000000000001d0 memsz 0x00000000000001d0 flags rw-
    NOTE off    0x0000000000000254 vaddr 0x0000000000400254 paddr 0x0000000000400254 align 2**2
         filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
EH_FRAME off    0x0000000000000648 vaddr 0x0000000000400648 paddr 0x0000000000400648 align 2**2
         filesz 0x000000000000003c memsz 0x000000000000003c flags r--
   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
   RELRO off    0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**0
         filesz 0x00000000000001f0 memsz 0x00000000000001f0 flags r--

动态节:
  NEEDED               libc.so.6
  INIT                 0x00000000004003e0
  FINI                 0x0000000000400614
  INIT_ARRAY           0x0000000000600e10
  INIT_ARRAYSZ         0x0000000000000008
  FINI_ARRAY           0x0000000000600e18
  FINI_ARRAYSZ         0x0000000000000008
  GNU_HASH             0x0000000000400298
  STRTAB               0x0000000000400318
  SYMTAB               0x00000000004002b8
  STRSZ                0x000000000000003f
  SYMENT               0x0000000000000018
  DEBUG                0x0000000000000000
  PLTGOT               0x0000000000601000
  PLTRELSZ             0x0000000000000048
  PLTREL               0x0000000000000007
  JMPREL               0x0000000000400398
  RELA                 0x0000000000400380
  RELASZ               0x0000000000000018
  RELAENT              0x0000000000000018
  VERNEED              0x0000000000400360
  VERNEEDNUM           0x0000000000000001
  VERSYM               0x0000000000400358

版本引用:
  required from libc.so.6:
    0x09691a75 0x00 02 GLIBC_2.2.5

节:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001c  0000000000400238  0000000000400238  00000238  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  0000000000400254  0000000000400254  00000254  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  0000000000400274  0000000000400274  00000274  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .gnu.hash     0000001c  0000000000400298  0000000000400298  00000298  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .dynsym       00000060  00000000004002b8  00000000004002b8  000002b8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynstr       0000003f  0000000000400318  0000000000400318  00000318  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .gnu.version  00000008  0000000000400358  0000000000400358  00000358  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .gnu.version_r 00000020  0000000000400360  0000000000400360  00000360  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .rela.dyn     00000018  0000000000400380  0000000000400380  00000380  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .rela.plt     00000048  0000000000400398  0000000000400398  00000398  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .init         0000001a  00000000004003e0  00000000004003e0  000003e0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .plt          00000040  0000000000400400  0000000000400400  00000400  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .text         000001d2  0000000000400440  0000000000400440  00000440  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .fini         00000009  0000000000400614  0000000000400614  00000614  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 14 .rodata       00000027  0000000000400620  0000000000400620  00000620  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 15 .eh_frame_hdr 0000003c  0000000000400648  0000000000400648  00000648  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 16 .eh_frame     00000114  0000000000400688  0000000000400688  00000688  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 17 .init_array   00000008  0000000000600e10  0000000000600e10  00000e10  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 18 .fini_array   00000008  0000000000600e18  0000000000600e18  00000e18  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 19 .jcr          00000008  0000000000600e20  0000000000600e20  00000e20  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 20 .dynamic      000001d0  0000000000600e28  0000000000600e28  00000e28  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 21 .got          00000008  0000000000600ff8  0000000000600ff8  00000ff8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 22 .got.plt      00000030  0000000000601000  0000000000601000  00001000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 23 .data         00000008  0000000000601030  0000000000601030  00001030  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 24 .bss          00000008  0000000000601038  0000000000601038  00001038  2**0
                  ALLOC
 25 .comment      0000002d  0000000000000000  0000000000000000  00001038  2**0
                  CONTENTS, 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              shared


Contents 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.>..>5
 400294 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.print
 400328 66005f5f 6c696263 5f737461 72745f6d  f.__libc_start_m
 400338 61696e00 5f5f676d 6f6e5f73 74617274  ain.__gmon_start
 400348 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...PTI
 400450 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..t
 400490 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.]H
 4004d0 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......H
 400510 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..U
 400590 fc01d05d c3662e0f 1f840000 00000090  ...].f..........
 4005a0 41574189 ff415649 89f64155 4989d541  AWA..AVI..AUI..A
 4005b0 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,.shared
 400640 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..M
 400768 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.5
 0010 20323031 35303632 33202852 65642048   20150623 (Red H
 0020 61742034 2e382e35 2d343429 00        at 4.8.5-44).   

Disassembly of section .init:

00000000004003e0 <_init>:
  4003e0:	48 83 ec 08          	sub    $0x8,%rsp
  4003e4:	48 8b 05 0d 0c 20 00 	mov    0x200c0d(%rip),%rax        # 600ff8 <__gmon_start__>
  4003eb:	48 85 c0             	test   %rax,%rax
  4003ee:	74 05                	je     4003f5 <_init+0x15>
  4003f0:	e8 3b 00 00 00       	callq  400430 <__gmon_start__@plt>
  4003f5:	48 83 c4 08          	add    $0x8,%rsp
  4003f9:	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  $0x0
  40041b:	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  $0x1
  40042b:	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  $0x2
  40043b:	e9 c0 ff ff ff       	jmpq   400400 <.plt>

Disassembly of section .text:

0000000000400440 <_start>:
  400440:	31 ed                	xor    %ebp,%ebp
  400442:	49 89 d1             	mov    %rdx,%r9
  400445:	5e                   	pop    %rsi
  400446:	48 89 e2             	mov    %rsp,%rdx
  400449:	48 83 e4 f0          	and    $0xfffffffffffffff0,%rsp
  40044d:	50                   	push   %rax
  40044e:	54                   	push   %rsp
  40044f:	49 c7 c0 10 06 40 00 	mov    $0x400610,%r8
  400456:	48 c7 c1 a0 05 40 00 	mov    $0x4005a0,%rcx
  40045d:	48 c7 c7 2d 05 40 00 	mov    $0x40052d,%rdi
  400464:	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,%eax
  400475:	55                   	push   %rbp
  400476:	48 2d 38 10 60 00    	sub    $0x601038,%rax
  40047c:	48 83 f8 0e          	cmp    $0xe,%rax
  400480:	48 89 e5             	mov    %rsp,%rbp
  400483:	77 02                	ja     400487 <deregister_tm_clones+0x17>
  400485:	5d                   	pop    %rbp
  400486:	c3                   	retq   
  400487:	b8 00 00 00 00       	mov    $0x0,%eax
  40048c:	48 85 c0             	test   %rax,%rax
  40048f:	74 f4                	je     400485 <deregister_tm_clones+0x15>
  400491:	5d                   	pop    %rbp
  400492:	bf 38 10 60 00       	mov    $0x601038,%edi
  400497:	ff e0                	jmpq   *%rax
  400499:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)

00000000004004a0 <register_tm_clones>:
  4004a0:	b8 38 10 60 00       	mov    $0x601038,%eax
  4004a5:	55                   	push   %rbp
  4004a6:	48 2d 38 10 60 00    	sub    $0x601038,%rax
  4004ac:	48 c1 f8 03          	sar    $0x3,%rax
  4004b0:	48 89 e5             	mov    %rsp,%rbp
  4004b3:	48 89 c2             	mov    %rax,%rdx
  4004b6:	48 c1 ea 3f          	shr    $0x3f,%rdx
  4004ba:	48 01 d0             	add    %rdx,%rax
  4004bd:	48 d1 f8             	sar    %rax
  4004c0:	75 02                	jne    4004c4 <register_tm_clones+0x24>
  4004c2:	5d                   	pop    %rbp
  4004c3:	c3                   	retq   
  4004c4:	ba 00 00 00 00       	mov    $0x0,%edx
  4004c9:	48 85 d2             	test   %rdx,%rdx
  4004cc:	74 f4                	je     4004c2 <register_tm_clones+0x22>
  4004ce:	5d                   	pop    %rbp
  4004cf:	48 89 c6             	mov    %rax,%rsi
  4004d2:	bf 38 10 60 00       	mov    $0x601038,%edi
  4004d7:	ff e2                	jmpq   *%rdx
  4004d9:	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   %rbp
  4004ea:	48 89 e5             	mov    %rsp,%rbp
  4004ed:	e8 7e ff ff ff       	callq  400470 <deregister_tm_clones>
  4004f2:	5d                   	pop    %rbp
  4004f3:	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,%eax
  40050f:	48 85 c0             	test   %rax,%rax
  400512:	74 14                	je     400528 <frame_dummy+0x28>
  400514:	55                   	push   %rbp
  400515:	bf 20 0e 60 00       	mov    $0x600e20,%edi
  40051a:	48 89 e5             	mov    %rsp,%rbp
  40051d:	ff d0                	callq  *%rax
  40051f:	5d                   	pop    %rbp
  400520:	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   %rbp
  40052e:	48 89 e5             	mov    %rsp,%rbp
  400531:	48 83 ec 10          	sub    $0x10,%rsp
  400535:	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),%edx
  400546:	8b 45 fc             	mov    -0x4(%rbp),%eax
  400549:	89 d6                	mov    %edx,%esi
  40054b:	89 c7                	mov    %eax,%edi
  40054d:	b8 00 00 00 00       	mov    $0x0,%eax
  400552:	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),%edx
  400566:	8b 45 f4             	mov    -0xc(%rbp),%eax
  400569:	89 c6                	mov    %eax,%esi
  40056b:	bf 30 06 40 00       	mov    $0x400630,%edi
  400570:	b8 00 00 00 00       	mov    $0x0,%eax
  400575:	e8 96 fe ff ff       	callq  400410 <printf@plt>
  40057a:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
  40057f:	c9                   	leaveq 
  400580:	c3                   	retq   

0000000000400581 <add>:
  400581:	55                   	push   %rbp
  400582:	48 89 e5             	mov    %rsp,%rbp
  400585:	89 7d fc             	mov    %edi,-0x4(%rbp)
  400588:	89 75 f8             	mov    %esi,-0x8(%rbp)
  40058b:	8b 45 f8             	mov    -0x8(%rbp),%eax
  40058e:	8b 55 fc             	mov    -0x4(%rbp),%edx
  400591:	01 d0                	add    %edx,%eax
  400593:	5d                   	pop    %rbp
  400594:	c3                   	retq   
  400595:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
  40059c:	00 00 00 
  40059f:	90                   	nop

00000000004005a0 <__libc_csu_init>:
  4005a0:	41 57                	push   %r15
  4005a2:	41 89 ff             	mov    %edi,%r15d
  4005a5:	41 56                	push   %r14
  4005a7:	49 89 f6             	mov    %rsi,%r14
  4005aa:	41 55                	push   %r13
  4005ac:	49 89 d5             	mov    %rdx,%r13
  4005af:	41 54                	push   %r12
  4005b1:	4c 8d 25 58 08 20 00 	lea    0x200858(%rip),%r12        # 600e10 <__frame_dummy_init_array_entry>
  4005b8:	55                   	push   %rbp
  4005b9:	48 8d 2d 58 08 20 00 	lea    0x200858(%rip),%rbp        # 600e18 <__init_array_end>
  4005c0:	53                   	push   %rbx
  4005c1:	4c 29 e5             	sub    %r12,%rbp
  4005c4:	31 db                	xor    %ebx,%ebx
  4005c6:	48 c1 fd 03          	sar    $0x3,%rbp
  4005ca:	48 83 ec 08          	sub    $0x8,%rsp
  4005ce:	e8 0d fe ff ff       	callq  4003e0 <_init>
  4005d3:	48 85 ed             	test   %rbp,%rbp
  4005d6:	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,%rdx
  4005e3:	4c 89 f6             	mov    %r14,%rsi
  4005e6:	44 89 ff             	mov    %r15d,%edi
  4005e9:	41 ff 14 dc          	callq  *(%r12,%rbx,8)
  4005ed:	48 83 c3 01          	add    $0x1,%rbx
  4005f1:	48 39 eb             	cmp    %rbp,%rbx
  4005f4:	75 ea                	jne    4005e0 <__libc_csu_init+0x40>
  4005f6:	48 83 c4 08          	add    $0x8,%rsp
  4005fa:	5b                   	pop    %rbx
  4005fb:	5d                   	pop    %rbp
  4005fc:	41 5c                	pop    %r12
  4005fe:	41 5d                	pop    %r13
  400600:	41 5e                	pop    %r14
  400602:	41 5f                	pop    %r15
  400604:	c3                   	retq   
  400605:	90                   	nop
  400606:	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,%rsp
  400618:	48 83 c4 08          	add    $0x8,%rsp
  40061c:	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  Algn
  0 .text         00000014  0000000000000000  0000000000000000  00000040  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00000004  0000000000000000  0000000000000000  00000054  2**2
                  CONTENTS, ALLOC, LOAD, DATA

main.o

节:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         0000004e  0000000000000000  0000000000000000  00000040  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000000  0000000000000000  0000000000000000  0000008e  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  • 链接后mainadd
节:
Idx Name          Size      VMA               LMA               File off  Algn
 12 .text         000001c2  0000000000400440  0000000000400440  00000440  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 23 .data         00000008  0000000000601030  0000000000601030  00001030  2**2
                  CONTENTS, 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.o

main.o:     文件格式 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 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),%edx
  19:	8b 45 fc             	mov    -0x4(%rbp),%eax
  1c:	89 d6                	mov    %edx,%esi
  1e:	89 c7                	mov    %eax,%edi
  20:	b8 00 00 00 00       	mov    $0x0,%eax
  25:	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),%edx
  39:	8b 45 f4             	mov    -0xc(%rbp),%eax
  3c:	89 c6                	mov    %eax,%esi
  3e:	bf 00 00 00 00       	mov    $0x0,%edi
  43:	b8 00 00 00 00       	mov    $0x0,%eax
  48:	e8 00 00 00 00       	callq  4d <main+0x4d>
  4d:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
  52:	c9                   	leaveq 
  53:	c3                   	retq   
[dev1@localhost test02]$ ^C
  • 链接后
    查看链接前mainadd中反汇编的结果
[dev1@localhost test02]$ objdump -d  mainadd

mainadd:     文件格式 elf64-x86-64


Disassembly of section .init:

00000000004003e0 <_init>:
  4003e0:	48 83 ec 08          	sub    $0x8,%rsp
  4003e4:	48 8b 05 0d 0c 20 00 	mov    0x200c0d(%rip),%rax        # 600ff8 <__gmon_start__>
  4003eb:	48 85 c0             	test   %rax,%rax
  4003ee:	74 05                	je     4003f5 <_init+0x15>
  4003f0:	e8 3b 00 00 00       	callq  400430 <__gmon_start__@plt>
  4003f5:	48 83 c4 08          	add    $0x8,%rsp
  4003f9:	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  $0x0
  40041b:	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  $0x1
  40042b:	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  $0x2
  40043b:	e9 c0 ff ff ff       	jmpq   400400 <.plt>

Disassembly of section .text:

0000000000400440 <_start>:
  400440:	31 ed                	xor    %ebp,%ebp
  400442:	49 89 d1             	mov    %rdx,%r9
  400445:	5e                   	pop    %rsi
  400446:	48 89 e2             	mov    %rsp,%rdx
  400449:	48 83 e4 f0          	and    $0xfffffffffffffff0,%rsp
  40044d:	50                   	push   %rax
  40044e:	54                   	push   %rsp
  40044f:	49 c7 c0 10 06 40 00 	mov    $0x400610,%r8
  400456:	48 c7 c1 a0 05 40 00 	mov    $0x4005a0,%rcx
  40045d:	48 c7 c7 2d 05 40 00 	mov    $0x40052d,%rdi
  400464:	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,%eax
  400475:	55                   	push   %rbp
  400476:	48 2d 38 10 60 00    	sub    $0x601038,%rax
  40047c:	48 83 f8 0e          	cmp    $0xe,%rax
  400480:	48 89 e5             	mov    %rsp,%rbp
  400483:	77 02                	ja     400487 <deregister_tm_clones+0x17>
  400485:	5d                   	pop    %rbp
  400486:	c3                   	retq   
  400487:	b8 00 00 00 00       	mov    $0x0,%eax
  40048c:	48 85 c0             	test   %rax,%rax
  40048f:	74 f4                	je     400485 <deregister_tm_clones+0x15>
  400491:	5d                   	pop    %rbp
  400492:	bf 38 10 60 00       	mov    $0x601038,%edi
  400497:	ff e0                	jmpq   *%rax
  400499:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)

00000000004004a0 <register_tm_clones>:
  4004a0:	b8 38 10 60 00       	mov    $0x601038,%eax
  4004a5:	55                   	push   %rbp
  4004a6:	48 2d 38 10 60 00    	sub    $0x601038,%rax
  4004ac:	48 c1 f8 03          	sar    $0x3,%rax
  4004b0:	48 89 e5             	mov    %rsp,%rbp
  4004b3:	48 89 c2             	mov    %rax,%rdx
  4004b6:	48 c1 ea 3f          	shr    $0x3f,%rdx
  4004ba:	48 01 d0             	add    %rdx,%rax
  4004bd:	48 d1 f8             	sar    %rax
  4004c0:	75 02                	jne    4004c4 <register_tm_clones+0x24>
  4004c2:	5d                   	pop    %rbp
  4004c3:	c3                   	retq   
  4004c4:	ba 00 00 00 00       	mov    $0x0,%edx
  4004c9:	48 85 d2             	test   %rdx,%rdx
  4004cc:	74 f4                	je     4004c2 <register_tm_clones+0x22>
  4004ce:	5d                   	pop    %rbp
  4004cf:	48 89 c6             	mov    %rax,%rsi
  4004d2:	bf 38 10 60 00       	mov    $0x601038,%edi
  4004d7:	ff e2                	jmpq   *%rdx
  4004d9:	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   %rbp
  4004ea:	48 89 e5             	mov    %rsp,%rbp
  4004ed:	e8 7e ff ff ff       	callq  400470 <deregister_tm_clones>
  4004f2:	5d                   	pop    %rbp
  4004f3:	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,%eax
  40050f:	48 85 c0             	test   %rax,%rax
  400512:	74 14                	je     400528 <frame_dummy+0x28>
  400514:	55                   	push   %rbp
  400515:	bf 20 0e 60 00       	mov    $0x600e20,%edi
  40051a:	48 89 e5             	mov    %rsp,%rbp
  40051d:	ff d0                	callq  *%rax
  40051f:	5d                   	pop    %rbp
  400520:	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   %rbp
  40052e:	48 89 e5             	mov    %rsp,%rbp
  400531:	48 83 ec 10          	sub    $0x10,%rsp
  400535:	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),%edx
  400546:	8b 45 fc             	mov    -0x4(%rbp),%eax
  400549:	89 d6                	mov    %edx,%esi
  40054b:	89 c7                	mov    %eax,%edi
  40054d:	b8 00 00 00 00       	mov    $0x0,%eax
  400552:	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),%edx
  400566:	8b 45 f4             	mov    -0xc(%rbp),%eax
  400569:	89 c6                	mov    %eax,%esi
  40056b:	bf 30 06 40 00       	mov    $0x400630,%edi
  400570:	b8 00 00 00 00       	mov    $0x0,%eax
  400575:	e8 96 fe ff ff       	callq  400410 <printf@plt>
  40057a:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
  40057f:	c9                   	leaveq 
  400580:	c3                   	retq   

0000000000400581 <add>:
  400581:	55                   	push   %rbp
  400582:	48 89 e5             	mov    %rsp,%rbp
  400585:	89 7d fc             	mov    %edi,-0x4(%rbp)
  400588:	89 75 f8             	mov    %esi,-0x8(%rbp)
  40058b:	8b 45 f8             	mov    -0x8(%rbp),%eax
  40058e:	8b 55 fc             	mov    -0x4(%rbp),%edx
  400591:	01 d0                	add    %edx,%eax
  400593:	5d                   	pop    %rbp
  400594:	c3                   	retq   
  400595:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
  40059c:	00 00 00 
  40059f:	90                   	nop

00000000004005a0 <__libc_csu_init>:
  4005a0:	41 57                	push   %r15
  4005a2:	41 89 ff             	mov    %edi,%r15d
  4005a5:	41 56                	push   %r14
  4005a7:	49 89 f6             	mov    %rsi,%r14
  4005aa:	41 55                	push   %r13
  4005ac:	49 89 d5             	mov    %rdx,%r13
  4005af:	41 54                	push   %r12
  4005b1:	4c 8d 25 58 08 20 00 	lea    0x200858(%rip),%r12        # 600e10 <__frame_dummy_init_array_entry>
  4005b8:	55                   	push   %rbp
  4005b9:	48 8d 2d 58 08 20 00 	lea    0x200858(%rip),%rbp        # 600e18 <__init_array_end>
  4005c0:	53                   	push   %rbx
  4005c1:	4c 29 e5             	sub    %r12,%rbp
  4005c4:	31 db                	xor    %ebx,%ebx
  4005c6:	48 c1 fd 03          	sar    $0x3,%rbp
  4005ca:	48 83 ec 08          	sub    $0x8,%rsp
  4005ce:	e8 0d fe ff ff       	callq  4003e0 <_init>
  4005d3:	48 85 ed             	test   %rbp,%rbp
  4005d6:	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,%rdx
  4005e3:	4c 89 f6             	mov    %r14,%rsi
  4005e6:	44 89 ff             	mov    %r15d,%edi
  4005e9:	41 ff 14 dc          	callq  *(%r12,%rbx,8)
  4005ed:	48 83 c3 01          	add    $0x1,%rbx
  4005f1:	48 39 eb             	cmp    %rbp,%rbx
  4005f4:	75 ea                	jne    4005e0 <__libc_csu_init+0x40>
  4005f6:	48 83 c4 08          	add    $0x8,%rsp
  4005fa:	5b                   	pop    %rbx
  4005fb:	5d                   	pop    %rbp
  4005fc:	41 5c                	pop    %r12
  4005fe:	41 5d                	pop    %r13
  400600:	41 5e                	pop    %r14
  400602:	41 5f                	pop    %r15
  400604:	c3                   	retq   
  400605:	90                   	nop
  400606:	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,%rsp
  400618:	48 83 c4 08          	add    $0x8,%rsp
  40061c:	c3                   	retq   
[dev1@localhost test02]$ 

  • 链接前的汇编main
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 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),%edx
  19:	8b 45 fc             	mov    -0x4(%rbp),%eax
  1c:	89 d6                	mov    %edx,%esi
  1e:	89 c7                	mov    %eax,%edi
  20:	b8 00 00 00 00       	mov    $0x0,%eax
  25:	e8 00 00 00 00       	callq  2a <main+0x2a>
			26: R_X86_64_PC32	add-0x4
  2a:	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-0x4
  33:	89 45 f0             	mov    %eax,-0x10(%rbp)
  36:	8b 55 f0             	mov    -0x10(%rbp),%edx
  39:	8b 45 f4             	mov    -0xc(%rbp),%eax
  3c:	89 c6                	mov    %eax,%esi
  3e:	bf 00 00 00 00       	mov    $0x0,%edi
			3f: R_X86_64_32	.rodata
  43:	b8 00 00 00 00       	mov    $0x0,%eax
  48:	e8 00 00 00 00       	callq  4d <main+0x4d>
			49: R_X86_64_PC32	printf-0x4
  4d:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
  52:	c9                   	leaveq 
  53:	c3                   	retq   
  • 链接后的汇编main
000000000040052d <main>:
  40052d:	55                   	push   %rbp
  40052e:	48 89 e5             	mov    %rsp,%rbp
  400531:	48 83 ec 10          	sub    $0x10,%rsp
  400535:	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),%edx
  400546:	8b 45 fc             	mov    -0x4(%rbp),%eax
  400549:	89 d6                	mov    %edx,%esi
  40054b:	89 c7                	mov    %eax,%edi
  40054d:	b8 00 00 00 00       	mov    $0x0,%eax
  400552:	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),%edx
  400566:	8b 45 f4             	mov    -0xc(%rbp),%eax
  400569:	89 c6                	mov    %eax,%esi
  40056b:	bf 30 06 40 00       	mov    $0x400630,%edi
  400570:	b8 00 00 00 00       	mov    $0x0,%eax
  400575:	e8 96 fe ff ff       	callq  400410 <printf@plt>
  40057a:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
  40057f:	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-0x4
  2a:	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.o

main.o:     文件格式 elf64-x86-64

RELOCATION 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-0x0000000000000004


RELOCATION 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.o

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 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    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 main
    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND add
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND shared
    12: 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、《程序员的自我修养链接装载与库》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郑同学的笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值