【编译、链接、装载八】链接

【编译和链接八】链接

  • 一、链接的起源——链接器年龄比编译器长
    • 1、 机器指令时代
    • 2、汇编指令时代
    • 3、链接
    • 4、静态链接
    • 5、结合CPU指令分析链接
  • 二、链接的接口——符号
    • 1、ELF符号表结构
      • 1.1、符号类型和绑定信息(st_info)
      • 1.2、符号所在段(st_shndx)
      • 1.3、符号值( st_value )
    • 2、实战
      • 2.1、分析一:符号表的列
      • 2.2、分析二:Ndx = COM
      • 2.3、分析三:Ndx = UND
      • 2.4、分析四:LOCAL、GLOBAL
      • 2.5、分析五:每一个变量符号所在的段
  • 三、符号修饰与函数签名
    • 1.C++符号修饰
    • 2.函数签名(Function Signature)
    • 3.extern “C”

一、链接的起源——链接器年龄比编译器长

1、 机器指令时代

很久很久以前……人们编写程序时,将所有源代码都写在同一个文件中,发展到后来一个程序源代码的文件长达数百万行,以至于这个地方的人类已经没有能力维护这个程序了。人们开始寻找新的办法

在最开始的时候,程序员(当时程序员的概念应该跟现在相差很大了)先把一个程序在纸上写好,当然当时没有很高级的语言,用的都是机器语言,甚至连汇编语言都没有。当程序须要被运行时,程序员人工将他写的程序写入到存储设备上,

程序的第一条指令就是一条跳转指令,它的目的地址是第5条指令

程序并不是一写好就永远不变化的,它可能会经常被修改。比如我们在第1条指令之后、第5条指令之前插入了一条或多条指令,那么第5条指令及后面的指令的位置将会相应地往后移动,原先第一条指令的低4位的数字将需要相应地调整。在这个过程中,程序员需要人工重新计算每个子程序或跳转的目标地址。当程序修改的时候,这些位置都要重新计算,十分繁琐又耗时,并且很容易出错。这种重新计算各个目标的地址过程被叫做重定位(Relocation)。

2、汇编指令时代

先驱者发明了汇编语言,这相比机器语言来说是个很大的进步。汇编语言使用接近人类的
各种符号和标记来帮助记忆,比如指令采用两个或三个字母的缩写,记住“jmp”比记住0001XXXX是跳转(jump)指令容易得多了;汇编语言还可以使用符号来标记位置。

我们把刚开始第5条指令开始的子程序命名为“foo”,那么第一条指令的汇编就是:
jmp foo

当然人们可以使用这种符号命名子程序或跳转目标以后,不管这个“foo”之前插入或减少了多少条指令导致“foo”目标地址发生了什么变化,汇编器在每次汇编程序的时候会重新计算“foo”这个符号的地址,然后把所有引用到“foo”的指令修正到这个正确的地址。

整个过程不需要人工参与,对于一个有成百上千个类似的符号的程序,程序员终于摆脱了这种低级的繁琐的调整地址的工作,

符号(Symbol)这个概念随着汇编语言的普及迅速被使用,它用来表示一个地址,这个地址可能是一段子程序(后来发展成函数)的起始地址,也可以是一个变量的起始地址。

3、链接

有了汇编语言以后,生产力大大提高了。人们开始将代码按照功能或性质划分,分别形成不同的功能模块,不同的模块之间按照层次结构或其他结构来组织。

在一个程序被分割成多个模块以后,这些模块之间最后如何组合形成一个单一的程序是须解决的问题。模块之间如何组合的问题可以归结为模块之间如何通信的问题,最常见的属于静态语言的C/C++模块之间通信有两种方式,一种是模块间的函数调用,另外一种是模块间的变量访问。函数访问须知道目标函数的地址,变量访问也须知道目标变量的地址,所以这两种方式都可以归结为一种方式,那就是模块间符号的引用。模块间依靠符号来通信类似于拼图版,定义符号的模块多出一块区域,引用该符号的模块刚好少了那一块区域,两者一拼接刚好完美组合(见图)。这个模块的拼接过程就是本书的一个主题:链接(Linking)。

【编译、链接、装载八】链接_第1张图片

这种基于符号的模块化的一个直接结果是链接过程在整个程序开发中变得十分重要和突出。

4、静态链接

编译器、链接器更为复杂,功能更为强大,但从原理上来讲,它的工作无非就是把一些指令对其他符号地址的引用加以修正。链接过程主要包括了地址和空间分配(Address and Storage Allocation)、符号决议(Symbol Resolution)和重定位(Relocation)等这些步骤。
符号决议有时候也被叫做符号绑定(Symbol Binding)、名称绑定(Name Binding)、名称决议(Name Resolution),甚至还有叫做地址绑定(Address Binding)、指令绑定(Instruction Binding)的,大体上它们的意思都一样,但从细节角度来区分,它们之间还是存在一定区别的,比如“决议”更倾向于静态链接,而“绑定”更倾向于动态链接,

  • 比如我们在程序模块main.c中使用另外一个模块func.c中的函数foo()。我们在main.c模块中每一处调用foo的时候都必须确切知道foo这个函数的地址,但是由于每个模块都是单独编译的,在编译器编译main.c的时候它并不知道foo函数的地址,所以它暂时把这些调用foo的指令的目标地址搁置,等待最后链接的时候由链接器去将这些指令的目标地址修正。

  • 如果没有链接器,须要我们手工把每个调用foo的指令进行修正,则填入正确的foo函数地址。当func.c模块被重新编译,foo函数的地址有可能改变时,那么我们在main.c中所有使用到foo的地址的指令将要全部重新调整。这些繁琐的工作将成为程序员的噩梦。使用链接器,你可以直接引用其他模块的函数和全局变量而无须知道它们的地址,

  • 因为链接器在链接的时候,会根据你所引用的符号 foo,自动去相应的func.c模块查找foo的地址,然后将main.c模块中所有引用到foo的指令重新修正,让它们的目标地址为真正的foo函数的地址。这就是静态链接的最基本的过程和作用。

5、结合CPU指令分析链接

结合CPU指令来了解这个过程。假设我们有个全局变量叫做var,它在目标文件A里面。我们在目标文件B里面要访问这个全局变量,比如我们在目标文件B里面有这么一条指令:
movl $0x2a, var
这条指令就是给这个var变量赋值0x2a,相当于C语言里面的语句var =42。然后我们编译目标文件B,得到这条指令机器码,如图所示。
【编译、链接、装载八】链接_第2张图片

由于在编译目标文件B的时候,编译器并不知道变量var的目标地址,所以编译器在没法确定地址的情况下,将这条mov指令的目标地址置为0,等待链接器在将目标文件A和B链接起来的时候再将其修正。我们假设A和B链接后,变量var的地址确定下来为0x1000,那么链接器将会把这个指令的目标地址部分修改成0x10000。这个地址修正的过程也被叫做重定位(Relocation),每个要被修正的地方叫一个重定位入口(Relocation Entry)。重定位所做的就是给程序中每个这样的绝对地址引用的位置“打补丁”,使它们指向正确的地址。

  • demo
#include

int main()
{
	static int var = 42;
     printf("%d",var);
}
  • 编译(查看 汇编指令、符号表)

编译

gcc -c hello.c -o hello.o

查看 汇编指令

objdump -s -d hello.o
0000000000000000 <main>:
   0:	55                   	push   %rbp
   1:	48 89 e5             	mov    %rsp,%rbp
   4:	8b 05 00 00 00 00    	mov    0x0(%rip),%eax        # a <main+0xa>
   a:	89 c6                	mov    %eax,%esi
   c:	bf 00 00 00 00       	mov    $0x0,%edi
  11:	b8 00 00 00 00       	mov    $0x0,%eax
  16:	e8 00 00 00 00       	callq  1b <main+0x1b>
  1b:	5d                   	pop    %rbp
  1c:	c3                   	retq 

查看 符号表

readelf -a hello.o
Symbol table '.symtab' contains 12 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.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     4 OBJECT  LOCAL  DEFAULT    3 var.2178
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
    10: 0000000000000000    29 FUNC    GLOBAL DEFAULT    1 main
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

  • 链接(查看 汇编指令、符号表)

链接

gcc hello.o -o hello.out

查看 汇编指令

objdump -s -d hello.out
000000000040052d <main>:
  40052d:	55                   	push   %rbp
  40052e:	48 89 e5             	mov    %rsp,%rbp
  400531:	8b 05 fd 0a 20 00    	mov    0x200afd(%rip),%eax        # 601034 <var.2178>
  400537:	89 c6                	mov    %eax,%esi
  400539:	bf e0 05 40 00       	mov    $0x4005e0,%edi
  40053e:	b8 00 00 00 00       	mov    $0x0,%eax
  400543:	e8 c8 fe ff ff       	callq  400410 <printf@plt>
  400548:	5d                   	pop    %rbp
  400549:	c3                   	retq   
  40054a:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)

查看 符号表

readelf -a hello.out
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000400238     0 SECTION LOCAL  DEFAULT    1 
     2: 0000000000400254     0 SECTION LOCAL  DEFAULT    2 
     3: 0000000000400274     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000400298     0 SECTION LOCAL  DEFAULT    4 
     5: 00000000004002b8     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000400318     0 SECTION LOCAL  DEFAULT    6 
     7: 0000000000400358     0 SECTION LOCAL  DEFAULT    7 
     8: 0000000000400360     0 SECTION LOCAL  DEFAULT    8 
     9: 0000000000400380     0 SECTION LOCAL  DEFAULT    9 
    10: 0000000000400398     0 SECTION LOCAL  DEFAULT   10 
    11: 00000000004003e0     0 SECTION LOCAL  DEFAULT   11 
    12: 0000000000400400     0 SECTION LOCAL  DEFAULT   12 
    13: 0000000000400440     0 SECTION LOCAL  DEFAULT   13 
    14: 00000000004005c4     0 SECTION LOCAL  DEFAULT   14 
    15: 00000000004005d0     0 SECTION LOCAL  DEFAULT   15 
    16: 00000000004005e4     0 SECTION LOCAL  DEFAULT   16 
    17: 0000000000400618     0 SECTION LOCAL  DEFAULT   17 
    18: 0000000000600e10     0 SECTION LOCAL  DEFAULT   18 
    19: 0000000000600e18     0 SECTION LOCAL  DEFAULT   19 
    20: 0000000000600e20     0 SECTION LOCAL  DEFAULT   20 
    21: 0000000000600e28     0 SECTION LOCAL  DEFAULT   21 
    22: 0000000000600ff8     0 SECTION LOCAL  DEFAULT   22 
    23: 0000000000601000     0 SECTION LOCAL  DEFAULT   23 
    24: 0000000000601030     0 SECTION LOCAL  DEFAULT   24 
    25: 0000000000601038     0 SECTION LOCAL  DEFAULT   25 
    26: 0000000000000000     0 SECTION LOCAL  DEFAULT   26 
    27: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    28: 0000000000600e20     0 OBJECT  LOCAL  DEFAULT   20 __JCR_LIST__
    29: 0000000000400470     0 FUNC    LOCAL  DEFAULT   13 deregister_tm_clones
    30: 00000000004004a0     0 FUNC    LOCAL  DEFAULT   13 register_tm_clones
    31: 00000000004004e0     0 FUNC    LOCAL  DEFAULT   13 __do_global_dtors_aux
    32: 0000000000601038     1 OBJECT  LOCAL  DEFAULT   25 completed.6355
    33: 0000000000600e18     0 OBJECT  LOCAL  DEFAULT   19 __do_global_dtors_aux_fin
    34: 0000000000400500     0 FUNC    LOCAL  DEFAULT   13 frame_dummy
    35: 0000000000600e10     0 OBJECT  LOCAL  DEFAULT   18 __frame_dummy_init_array_
    36: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
    37: 0000000000601034     4 OBJECT  LOCAL  DEFAULT   24 var.2178
    38: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    39: 0000000000400708     0 OBJECT  LOCAL  DEFAULT   17 __FRAME_END__
    40: 0000000000600e20     0 OBJECT  LOCAL  DEFAULT   20 __JCR_END__
    41: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS 
    42: 0000000000600e18     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
    43: 0000000000600e28     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC
    44: 0000000000600e10     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_start
    45: 00000000004005e4     0 NOTYPE  LOCAL  DEFAULT   16 __GNU_EH_FRAME_HDR
    46: 0000000000601000     0 OBJECT  LOCAL  DEFAULT   23 _GLOBAL_OFFSET_TABLE_
    47: 00000000004005c0     2 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    48: 0000000000601030     0 NOTYPE  WEAK   DEFAULT   24 data_start
    49: 0000000000601038     0 NOTYPE  GLOBAL DEFAULT   24 _edata
    50: 00000000004005c4     0 FUNC    GLOBAL DEFAULT   14 _fini
    51: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.2.5
    52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    53: 0000000000601030     0 NOTYPE  GLOBAL DEFAULT   24 __data_start
    54: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    55: 00000000004005d8     0 OBJECT  GLOBAL HIDDEN    15 __dso_handle
    56: 00000000004005d0     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
    57: 0000000000400550   101 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
    58: 0000000000601040     0 NOTYPE  GLOBAL DEFAULT   25 _end
    59: 0000000000400440     0 FUNC    GLOBAL DEFAULT   13 _start
    60: 0000000000601038     0 NOTYPE  GLOBAL DEFAULT   25 __bss_start
    61: 000000000040052d    29 FUNC    GLOBAL DEFAULT   13 main
    62: 0000000000601038     0 OBJECT  GLOBAL HIDDEN    24 __TMC_END__
    63: 00000000004003e0     0 FUNC    GLOBAL DEFAULT   11 _init
  • 我们可以看到:

    • 符号表中的static int var 确实是在,链接后确定的地址。
    • 如果是调用函数的话,一样的道理
  • 链接前

   4:	8b 05 00 00 00 00    	mov    0x0(%rip),%eax        # a <main+0xa>
  • 链接后
  400531:	8b 05 fd 0a 20 00    	mov    0x200afd(%rip),%eax        # 601034 <var.2178>

8b 05 = mov
fd 0a 20 = 0x 200afd = 601034

二、链接的接口——符号

链接过程的本质就是要把多个不同的目标文件之间相互“粘”到一起,在链接中,目标文件之间相互拼合实
际上是目标文件之间对地址的引用,即对函数和变量的地址的引用。


如目标文件B要用到了目标文件A中的函数“foo”,那么我们就称目标文件A定义(Define)了函数“foo”,称目标文件B引用(Reference)了目标文件A中的函数“foo”。这两个概念也同样适用于变量。每个函数或变量都有自己独特的名字,才能避免链接过程中不同变量和函数之间的混淆。在链接中,我们将函数和变量统称为符号(Symbol),函数名或变量名就是符号名(Symbol Name)。

每一个目标文件都会有一个相应的符号表(Symbol Table),

1、ELF符号表结构

ELF文件中的符号表往往是文件中的一个段,段名一般叫“.symtab”,结构定义如下:

typedef struct {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;

这几个成员的定义如下表所示。
【编译、链接、装载八】链接_第3张图片

1.1、符号类型和绑定信息(st_info)

符号类型和绑定信息( st_info ) 该成员低4位表示符号的类型( Symbol Type ),高28位表示符号绑定信息( Symbol Binding ),如表下面两个表所示。

  • 绑定信息( Symbol Binding )
    【编译、链接、装载八】链接_第4张图片
  • 类型( Symbol Type )
    【编译、链接、装载八】链接_第5张图片

1.2、符号所在段(st_shndx)

符号所在段( st_shndx )如果符号定义在本目标文件中,那么这个成员表示符号所在的段在段表中的下标;但是如果符号不是定义在本目标文件中,或者对于有些特殊符号, sh_shndx 的值有些特殊,如下表所示
【编译、链接、装载八】链接_第6张图片

1.3、符号值( st_value )

符号值( st_value ) 我们前面已经介绍过,每个符号都有一个对应的值,如果这个符号是一个函数或变量的定义,那么符号的值就是这个函数或变量的地址,更准确地讲应该按下面这几种情况区别对待。

在目标文件中,如果是符号的定义并且该符号不是“COMMON块”类型的(即st_shndx不为SHN_COMMON,具体请参照“深入静态链接”一章中的“COMMON块”),则st_value表示该符号在段中的偏移。即符号所对应的函数或变量位于由st_shndx指定的段,偏移st_value的位置。这也是目标文件中定义全局变量的符号的最常见情况,比如SimpleSection.o中的“ func1 ”、“ main ”和“ global_init_var ”。

在目标文件中,如果符号是“COMMON块”类型的(即 st_shndx 为SHN_COMMON),则st_value表示该符号的对齐属性。比如SimpleSection.o中的“ global_uninit_var ”。在可执行文件中,st_value表示符号的虚拟地址。这个虚拟地址对于动态链接器来说十分有用。我们将在后面讲述动态链接器。

2、实战

  • 代码
#include

int global_init_var = 11;
int global_uninit_var;
const int const_g =22;

void show( int i )
{
	printf( "%d\n", i );
}

int main()
{
	static int static_var = 33;
	static int static_var2;

	const int const_l =44;

	int a_local = 1;
	int b_local;

	show(a_local);

	return -1;
}
  • 链接前:编译、查看目标文件
[dev1@localhost test01]$ gcc -c hello.c -o hello.o
[dev1@localhost test01]$ readelf -a hello.o
ELF 头:
  Magic:  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF64
  数据:                              2 补码,小端序 (little endian)
  版本:                              1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              REL (可重定位文件)
  系统架构:                          Advanced Micro Devices X86-64
  版本:                              0x1
  入口点地址:              0x0
  程序头起点:              0 (bytes into file)
  Start of section headers:          1024 (bytes into file)
  标志:             0x0
  本头的大小:       64 (字节)
  程序头大小:       0 (字节)
  Number of program headers:         0
  节头大小:         64 (字节)
  节头数量:         13
  字符串表索引节头: 12

节头:
  [] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       0000000000000048  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  00000320
       0000000000000048  0000000000000018   I      10     1     8
  [ 3] .data             PROGBITS         0000000000000000  00000088
       0000000000000008  0000000000000000  WA       0     0     4
  [ 4] .bss              NOBITS           0000000000000000  00000090
       0000000000000004  0000000000000000  WA       0     0     4
  [ 5] .rodata           PROGBITS         0000000000000000  00000090
       0000000000000008  0000000000000000   A       0     0     4
  [ 6] .comment          PROGBITS         0000000000000000  00000098
       000000000000002e  0000000000000001  MS       0     0     1
  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  000000c6
       0000000000000000  0000000000000000           0     0     1
  [ 8] .eh_frame         PROGBITS         0000000000000000  000000c8
       0000000000000058  0000000000000000   A       0     0     8
  [ 9] .rela.eh_frame    RELA             0000000000000000  00000368
       0000000000000030  0000000000000018   I      10     8     8
  [10] .symtab           SYMTAB           0000000000000000  00000120
       0000000000000198  0000000000000018          11    11     8
  [11] .strtab           STRTAB           0000000000000000  000002b8
       0000000000000065  0000000000000000           0     0     1
  [12] .shstrtab         STRTAB           0000000000000000  00000398
       0000000000000061  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

There are no section groups in this file.

本文件中没有程序头。

重定位节 '.rela.text' 位于偏移量 0x320 含有 3 个条目:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000011  00050000000a R_X86_64_32       0000000000000000 .rodata + 4
00000000001b  000f00000002 R_X86_64_PC32     0000000000000000 printf - 4
00000000003d  000e00000002 R_X86_64_PC32     0000000000000000 show - 4

重定位节 '.rela.eh_frame' 位于偏移量 0x368 含有 2 个条目:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0
000000000040  000200000002 R_X86_64_PC32     0000000000000000 .text + 21

The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.

Symbol table '.symtab' contains 17 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.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     4 OBJECT  LOCAL  DEFAULT    4 static_var2.2185
     7: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    3 static_var.2184
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
    11: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var
    12: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM global_uninit_var
    13: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    5 const_g
    14: 0000000000000000    33 FUNC    GLOBAL DEFAULT    1 show
    15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    16: 0000000000000021    39 FUNC    GLOBAL DEFAULT    1 main

No version information found in this file.

  • 链接后:查看可执行文件
[dev1@localhost test01]$ gcc hello.o -o hello.out
[dev1@localhost test01]$ readelf -a hello.out
ELF 头:
  Magic:  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF64
  数据:                              2 补码,小端序 (little endian)
  版本:                              1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              EXEC (可执行文件)
  系统架构:                          Advanced Micro Devices X86-64
  版本:                              0x1
  入口点地址:              0x400440
  程序头起点:              64 (bytes into file)
  Start of section headers:          6672 (bytes into file)
  标志:             0x0
  本头的大小:       64 (字节)
  程序头大小:       56 (字节)
  Number of program headers:         9
  节头大小:         64 (字节)
  节头数量:         30
  字符串表索引节头: 29

节头:
  [] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       0000000000000060  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400318  00000318
       000000000000003f  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400358  00000358
       0000000000000008  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400360  00000360
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000400380  00000380
       0000000000000018  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000400398  00000398
       0000000000000048  0000000000000018  AI       5    23     8
  [11] .init             PROGBITS         00000000004003e0  000003e0
       000000000000001a  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000400400  00000400
       0000000000000040  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         0000000000400440  00000440
       00000000000001b2  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         00000000004005f4  000005f4
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         0000000000400600  00000600
       0000000000000018  0000000000000000   A       0     0     8
  [16] .eh_frame_hdr     PROGBITS         0000000000400618  00000618
       000000000000003c  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         0000000000400658  00000658
       0000000000000114  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       0000000000600e10  00000e10
       0000000000000008  0000000000000008  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000600e18  00000e18
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .jcr              PROGBITS         0000000000600e20  00000e20
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .dynamic          DYNAMIC          0000000000600e28  00000e28
       00000000000001d0  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         0000000000600ff8  00000ff8
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .got.plt          PROGBITS         0000000000601000  00001000
       0000000000000030  0000000000000008  WA       0     0     8
  [24] .data             PROGBITS         0000000000601030  00001030
       000000000000000c  0000000000000000  WA       0     0     4
  [25] .bss              NOBITS           000000000060103c  0000103c
       000000000000000c  0000000000000000  WA       0     0     4
  [26] .comment          PROGBITS         0000000000000000  0000103c
       000000000000002d  0000000000000001  MS       0     0     1
  [27] .symtab           SYMTAB           0000000000000000  00001070
       0000000000000678  0000000000000018          28    48     8
  [28] .strtab           STRTAB           0000000000000000  000016e8
       000000000000021c  0000000000000000           0     0     1
  [29] .shstrtab         STRTAB           0000000000000000  00001904
       0000000000000108  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

There are no section groups in this file.

程序头:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001f8 0x00000000000001f8  R E    8
  INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x000000000000076c 0x000000000000076c  R E    200000
  LOAD           0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
                 0x000000000000022c 0x0000000000000238  RW     200000
  DYNAMIC        0x0000000000000e28 0x0000000000600e28 0x0000000000600e28
                 0x00000000000001d0 0x00000000000001d0  RW     8
  NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_EH_FRAME   0x0000000000000618 0x0000000000400618 0x0000000000400618
                 0x000000000000003c 0x000000000000003c  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10
  GNU_RELRO      0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
                 0x00000000000001f0 0x00000000000001f0  R      1

 Section to Segment mapping:
  段节...
   00     
   01     .interp 
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     
   08     .init_array .fini_array .jcr .dynamic .got 

Dynamic section at offset 0xe28 contains 24 entries:
  标记        类型                         名称/0x0000000000000001 (NEEDED)             共享库:[libc.so.6]
 0x000000000000000c (INIT)               0x4003e0
 0x000000000000000d (FINI)               0x4005f4
 0x0000000000000019 (INIT_ARRAY)         0x600e10
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x600e18
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x400298
 0x0000000000000005 (STRTAB)             0x400318
 0x0000000000000006 (SYMTAB)             0x4002b8
 0x000000000000000a (STRSZ)              63 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x601000
 0x0000000000000002 (PLTRELSZ)           72 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x400398
 0x0000000000000007 (RELA)               0x400380
 0x0000000000000008 (RELASZ)             24 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x400360
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x400358
 0x0000000000000000 (NULL)               0x0

重定位节 '.rela.dyn' 位于偏移量 0x380 含有 1 个条目:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000600ff8  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0

重定位节 '.rela.plt' 位于偏移量 0x398 含有 3 个条目:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000601018  000100000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
000000601020  000200000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000601028  000300000007 R_X86_64_JUMP_SLO 0000000000000000 __gmon_start__ + 0

The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.

Symbol table '.dynsym' contains 4 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

Symbol table '.symtab' contains 69 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000400238     0 SECTION LOCAL  DEFAULT    1 
     2: 0000000000400254     0 SECTION LOCAL  DEFAULT    2 
     3: 0000000000400274     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000400298     0 SECTION LOCAL  DEFAULT    4 
     5: 00000000004002b8     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000400318     0 SECTION LOCAL  DEFAULT    6 
     7: 0000000000400358     0 SECTION LOCAL  DEFAULT    7 
     8: 0000000000400360     0 SECTION LOCAL  DEFAULT    8 
     9: 0000000000400380     0 SECTION LOCAL  DEFAULT    9 
    10: 0000000000400398     0 SECTION LOCAL  DEFAULT   10 
    11: 00000000004003e0     0 SECTION LOCAL  DEFAULT   11 
    12: 0000000000400400     0 SECTION LOCAL  DEFAULT   12 
    13: 0000000000400440     0 SECTION LOCAL  DEFAULT   13 
    14: 00000000004005f4     0 SECTION LOCAL  DEFAULT   14 
    15: 0000000000400600     0 SECTION LOCAL  DEFAULT   15 
    16: 0000000000400618     0 SECTION LOCAL  DEFAULT   16 
    17: 0000000000400658     0 SECTION LOCAL  DEFAULT   17 
    18: 0000000000600e10     0 SECTION LOCAL  DEFAULT   18 
    19: 0000000000600e18     0 SECTION LOCAL  DEFAULT   19 
    20: 0000000000600e20     0 SECTION LOCAL  DEFAULT   20 
    21: 0000000000600e28     0 SECTION LOCAL  DEFAULT   21 
    22: 0000000000600ff8     0 SECTION LOCAL  DEFAULT   22 
    23: 0000000000601000     0 SECTION LOCAL  DEFAULT   23 
    24: 0000000000601030     0 SECTION LOCAL  DEFAULT   24 
    25: 000000000060103c     0 SECTION LOCAL  DEFAULT   25 
    26: 0000000000000000     0 SECTION LOCAL  DEFAULT   26 
    27: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    28: 0000000000600e20     0 OBJECT  LOCAL  DEFAULT   20 __JCR_LIST__
    29: 0000000000400470     0 FUNC    LOCAL  DEFAULT   13 deregister_tm_clones
    30: 00000000004004a0     0 FUNC    LOCAL  DEFAULT   13 register_tm_clones
    31: 00000000004004e0     0 FUNC    LOCAL  DEFAULT   13 __do_global_dtors_aux
    32: 000000000060103c     1 OBJECT  LOCAL  DEFAULT   25 completed.6355
    33: 0000000000600e18     0 OBJECT  LOCAL  DEFAULT   19 __do_global_dtors_aux_fin
    34: 0000000000400500     0 FUNC    LOCAL  DEFAULT   13 frame_dummy
    35: 0000000000600e10     0 OBJECT  LOCAL  DEFAULT   18 __frame_dummy_init_array_
    36: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
    37: 0000000000601040     4 OBJECT  LOCAL  DEFAULT   25 static_var2.2185
    38: 0000000000601038     4 OBJECT  LOCAL  DEFAULT   24 static_var.2184
    39: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    40: 0000000000400768     0 OBJECT  LOCAL  DEFAULT   17 __FRAME_END__
    41: 0000000000600e20     0 OBJECT  LOCAL  DEFAULT   20 __JCR_END__
    42: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS 
    43: 0000000000600e18     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
    44: 0000000000600e28     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC
    45: 0000000000600e10     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_start
    46: 0000000000400618     0 NOTYPE  LOCAL  DEFAULT   16 __GNU_EH_FRAME_HDR
    47: 0000000000601000     0 OBJECT  LOCAL  DEFAULT   23 _GLOBAL_OFFSET_TABLE_
    48: 00000000004005f0     2 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    49: 0000000000601030     0 NOTYPE  WEAK   DEFAULT   24 data_start
    50: 000000000060103c     0 NOTYPE  GLOBAL DEFAULT   24 _edata
    51: 00000000004005f4     0 FUNC    GLOBAL DEFAULT   14 _fini
    52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.2.5
    53: 000000000040052d    33 FUNC    GLOBAL DEFAULT   13 show
    54: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    55: 0000000000601030     0 NOTYPE  GLOBAL DEFAULT   24 __data_start
    56: 0000000000400610     4 OBJECT  GLOBAL DEFAULT   15 const_g
    57: 0000000000601044     4 OBJECT  GLOBAL DEFAULT   25 global_uninit_var
    58: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    59: 0000000000400608     0 OBJECT  GLOBAL HIDDEN    15 __dso_handle
    60: 0000000000400600     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
    61: 0000000000400580   101 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
    62: 0000000000601048     0 NOTYPE  GLOBAL DEFAULT   25 _end
    63: 0000000000400440     0 FUNC    GLOBAL DEFAULT   13 _start
    64: 0000000000601034     4 OBJECT  GLOBAL DEFAULT   24 global_init_var
    65: 000000000060103c     0 NOTYPE  GLOBAL DEFAULT   25 __bss_start
    66: 000000000040054e    39 FUNC    GLOBAL DEFAULT   13 main
    67: 0000000000601040     0 OBJECT  GLOBAL HIDDEN    24 __TMC_END__
    68: 00000000004003e0     0 FUNC    GLOBAL DEFAULT   11 _init

Version symbols section '.gnu.version' contains 4 entries:
 地址:0000000000400358  Offset: 0x000358  Link: 5 (.dynsym)
  000:   0 (*本地*)       2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)   0 (*本地*)    

Version needs section '.gnu.version_r' contains 1 entries:
 地址:0x0000000000400360  Offset: 0x000360  Link: 6 (.dynstr)
  000000: 版本: 1  文件:libc.so.6  计数:1
  0x0010:名称:GLIBC_2.2.5  标志:无  版本:2

Displaying notes found at file offset 0x00000254 with length 0x00000020:
  所有者             Data size	Description
  GNU                  0x00000010	NT_GNU_ABI_TAG (ABI version tag)
    OS: Linux, ABI: 2.6.32

Displaying notes found at file offset 0x00000274 with length 0x00000024:
  所有者             Data size	Description
  GNU                  0x00000014	NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: d80924a11e18a61d0bfa7beeae080dea26eb0b72
[dev1@localhost test01]$ 

2.1、分析一:符号表的列

  • 第一列——Num表示符号表数组的下标,从0开始,共17个符号;
  • 第二列——Value就是符号值,即 st_value ;
  • 第三列——Size为符号大小,即 st_size ;
  • 第四列和第五列——分别为符号类型和绑定信息,即对应 st_info 的低4位和高28位;
  • 第六列——Vis目前在C/C++语言中未使用,我们可以暂时忽略它;
  • 第七列——Ndx即 st_shndx ,表示该符号所属的段;当然最后一列也最明显,即符号名称。
Symbol table '.symtab' contains 17 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.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     4 OBJECT  LOCAL  DEFAULT    4 static_var2.2185
     7: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    3 static_var.2184
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
    11: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var
    12: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM global_uninit_var
    13: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    5 const_g
    14: 0000000000000000    33 FUNC    GLOBAL DEFAULT    1 show
    15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    16: 0000000000000021    39 FUNC    GLOBAL DEFAULT    1 main

2.2、分析二:Ndx = COM

  • COM 未初始化的全局符号
    12: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM global_uninit_var

在目标文件中,如果符号是“COMMON块”类型的(即 st_shndx 为SHN_COMMON),则st_value表示该符号的对齐属性。比如SimpleSection.o中的“ global_uninit_var ”。在可执行文件中,st_value表示符号的虚拟地址。这个虚拟地址对于动态链接器来说十分有用。我们将在后面讲述动态链接器。

2.3、分析三:Ndx = UND

  • UND 该符号未定义。这个符号表示该符号在本目标文件被引用到,到那时定义在其他目标文件中。
    15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

2.4、分析四:LOCAL、GLOBAL

  • LOCAL 局部符号,对于目标文件的外部不可见
  • GLOBAL 全局符号,外部可见
     6: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.2185
     7: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    3 static_var.2184
    11: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var

2.5、分析五:每一个变量符号所在的段

Ndx name section
ABS hello.c 文件类型
4 static_var2.2185 .bss
3 static_var.2184 .data
3 global_init_var .data
COM global_uninit_var 未初始化全局变量
5 const_g .rodata
1 show .text
UND printf 在外部
1 main .text
节头:
  [] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       0000000000000048  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  00000320
       0000000000000048  0000000000000018   I      10     1     8
  [ 3] .data             PROGBITS         0000000000000000  00000088
       0000000000000008  0000000000000000  WA       0     0     4
  [ 4] .bss              NOBITS           0000000000000000  00000090
       0000000000000004  0000000000000000  WA       0     0     4
  [ 5] .rodata           PROGBITS         0000000000000000  00000090
       0000000000000008  0000000000000000   A       0     0     4
  [ 6] .comment          PROGBITS         0000000000000000  00000098
       000000000000002e  0000000000000001  MS       0     0     1
  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  000000c6
       0000000000000000  0000000000000000           0     0     1
  [ 8] .eh_frame         PROGBITS         0000000000000000  000000c8
       0000000000000058  0000000000000000   A       0     0     8
  [ 9] .rela.eh_frame    RELA             0000000000000000  00000368
       0000000000000030  0000000000000018   I      10     8     8
  [10] .symtab           SYMTAB           0000000000000000  00000120
       0000000000000198  0000000000000018          11    11     8
  [11] .strtab           STRTAB           0000000000000000  000002b8
       0000000000000065  0000000000000000           0     0     1
  [12] .shstrtab         STRTAB           0000000000000000  00000398
       0000000000000061  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

Symbol table '.symtab' contains 17 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
     6: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.2185
     7: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    3 static_var.2184
    11: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var
    12: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM global_uninit_var
    13: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    5 const_g
    14: 0000000000000000    33 FUNC    GLOBAL DEFAULT    1 show
    15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    16: 0000000000000021    39 FUNC    GLOBAL DEFAULT    1 main

三、符号修饰与函数签名

约在20世纪70年代以前,编译器编译源代码产生目标文件时,符号名与相应的变量和函数的名字是一样的。比如一个汇编源代码里面包含了一个函数foo,那么汇编器将它编译成目标文件以后,foo在目标文件中的相对应的符号名也是foo。当后来UNIX平台和C语言发明时,已经存在了相当多的使用汇编编写的库和目标文件。这样就产生了一个问题,那就是如果一个C程序要使用这些库的话,C语言中不可以使用这些库中定义的函数和变量的名字作为符号名,否则将会跟现有的目标文件冲突。比如有个用汇编编写的库中定义了一个函数叫做main,那么我们在C语言里面就不可以再定义一个main函数或变量了。

为了防止类似的符号名冲突,UNIX下的C语言就规定,C语言源代码文件中的所有全局的变量和函数经过编译以后,相对应的符号名前加上下划线“_”。而比如一个C语言函数“ foo ”,那么它编译后的符号名就是 “_foo ”;

这种简单而原始的方法的确能够暂时减少多种语言目标文件之间的符号 冲突的概率,但还是没有从根本上解决符号冲突的问题。比如同一种语 言编写的目标文件还有可能会产生符号冲突,当程序很大时,不同的模 块由多个部门(个人)开发,它们之间的命名规范如果不严格,则有可 能导致冲突。于是像C++这样的后来设计的语言开始考虑到了这个问 题,增加了名称空间(Namespace)的方法来解决多模块的符号冲突问 题。

但是随着时间的推移,很多操作系统和编译器被完全重写了好几遍,比如UNIX也分化成了很多种,整个环境发生了很大的变化,上面所提到的跟Fortran和古老的汇编库的符号冲突问题已经不是那么明显了。在现在的Linux下的GCC编译器中,默认情况下已经去掉了在C语言符号前加“”的这种方式;但是Windows平台下的编译器还保持的这样的传统,比如Visual C++编译器就会在C语言符号前加“”,GCC在Windows平台下的版本(cygwin、mingw)也会加“_”。GCC编译器也可以通过参数选项“ -fleading-underscore ”或 “-fno-leading-underscore ”来打开和关闭是否在C语言符号前加上下划线。

1.C++符号修饰

众所周知,强大而又复杂的C++拥有类、继承、虚机制、重载、名称空间等这些特性,它们使得符号管理更为复杂。最简单的例子,两个相同名字的函数func(int)和func(double),尽管函数名相同,但是参数列表不同,这是C++里面函数重载的最简单的一种情况,那么编译器和链接器在链接过程中如何区分这两个函数呢?为了支持C++这些复杂的特性,人们发明了符号修饰(Name Decoration)或符号改编(Name Mangling)的机制,

首先出现的一个问题是C++允许多个不同参数类型的函数拥有一样的名字,就是所谓的函数重载;

2.函数签名(Function Signature)

我们引入一个术语叫做函数签名(Function Signature),函数签名包含了一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息。函数签名用于识别不同的函数,就像签名用于识别不同的人一样,函数的名字只是函数签名的一部分。由于上面6个同名函数的参数类型及所处的类和名称空间不同,我们可以认为它们的函数签名不同。在编译器及链接器处理符号时,它们使用某种名称修饰的方法,使得每个函数签名对应一个修饰后名称(Decorated Name)。

函数签名在GCC编译器下,相对应的修饰后名称如下表所示

【编译、链接、装载八】链接_第7张图片
GCC的基本C++名称修饰方法如下:所有的符号都以“_Z”开头,对于嵌套的名字(在名称空间或在类里面的),后面紧跟“N”,然后是各个名称空间和类的名字,每个名字前是名字字符串长度,再以“E”结尾。比如 N::C::func 经过名称修饰以后就是 _ZN1N1C4funcE 。对于一个函数来说,它的参数列表紧跟在“E”后面,对于int类型来说,就是字母“i”。

c++ 和c不同,感兴趣的可以写个c++的代码查看下,利用我们第二部分讲解的指令方法,对比下.cpp和.c文件编译出的不同

3.extern “C”

C++为了与C兼容,在符号的管理上,C++有一个用来声明或定义一个C的符号的“extern “C””关键字用法:

extern ”C” {
int func(int);
int var;
}

C++编译器会将在extern “C” 的大括号内部的代码当作C语言代码处理。所以很明显,上面的代码中,C++的名称修饰机制将不会起作用。

参考
1、《程序员的自我修养链接装载与库》

你可能感兴趣的:(编译和链接,c++,开发语言,链接)