C++程序静态链接

C++程序静态链接

先看代码:

//simpleApp.cpp
int foo(int a, int b);

int main(){
    int a = 0;
    int b = 1;
    int c;
    c = foo(a, b);
    return c;
}
[root@xxxx] g++ -c simpleApp.cpp                #生成.o文件
[root@xxxx] g++ -o simpleApp simpleApp.cpp      #生成可执行文件
/tmp/cc8NKLpJ.o: In function `main':
simpleApp.cpp:(.text+0x21): undefined reference to `foo(int, int)'
collect2: error: ld returned 1 exit status

可以看到,生成.o文件成功。生成可执行文件失败,错误提示为foo(int, int)未定义的引用。

使用objdump工具查看一下生成的.o文件的汇编代码:

[root@xxxx]# objdump -d simpleApp.o

simpleApp.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <main>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 10             sub    $0x10,%rsp
   8:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
   f:   c7 45 f8 01 00 00 00    movl   $0x1,-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:   e8 00 00 00 00          callq  25 <main+0x25>           #这是调用foo函数的地方,随便填了一个数
  25:   89 45 f4                mov    %eax,-0xc(%rbp)
  28:   8b 45 f4                mov    -0xc(%rbp),%eax
  2b:   c9                      leaveq 
  2c:   c3                      retq 

从汇编代码中可以看到,在main函数中调用foo函数的地方,编译器随意填了一个数。

再查看一下simpleApp.o的符号表:

[root@xxxx]# readelf -s simpleApp.o

Symbol table '.symtab' contains 10 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND                      #未使用
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS simpleApp.cpp        #源文件名
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1                      #某个段
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3                      #某个段
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4                      #某个段
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    6                      #某个段
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7                      #某个段
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    5                      #某个段
     8: 0000000000000000    45 FUNC    GLOBAL DEFAULT    1 main                 #main函数
     9: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z3fooii             #未定义的foo函数

从上述符号表中我们可以看到foo函数的符号被标记为UND,即未定义的符号。

//foo.c

int foo(int a, int b){
    return a + b;
}

上述代码是foo函数的定义,常规操作,将其编译为.o文件,然后反汇编看一下:

[root@xxxx]# g++ -c foo.c
[root@xxxx]# objdump -d foo.o

foo.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <_Z3fooii>:                                #c++ name-mangling机制
   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 55 fc                mov    -0x4(%rbp),%edx
   d:   8b 45 f8                mov    -0x8(%rbp),%eax
  10:   01 d0                   add    %edx,%eax
  12:   5d                      pop    %rbp
  13:   c3                      retq  

查看foo.o的符号表:

[root@xxxx]# readelf -s foo.o

Symbol table '.symtab' contains 9 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS foo.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     8: 0000000000000000    20 FUNC    GLOBAL DEFAULT    1 _Z3fooii

下面对两个.o文件进行链接,生成可执行文件,并查看其反汇编代码:

[root@xxxx]# g++ -o simpleApp simpleApp.o foo.o
[root@xxxx]# objdump -d simpleApp

simpleApp:     file format elf64-x86-64

Disassembly of section .text:

0000000000400556 <main>:
  400556:       55                      push   %rbp
  400557:       48 89 e5                mov    %rsp,%rbp
  40055a:       48 83 ec 10             sub    $0x10,%rsp
  40055e:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
  400565:       c7 45 f8 01 00 00 00    movl   $0x1,-0x8(%rbp)
  40056c:       8b 55 f8                mov    -0x8(%rbp),%edx
  40056f:       8b 45 fc                mov    -0x4(%rbp),%eax
  400572:       89 d6                   mov    %edx,%esi
  400574:       89 c7                   mov    %eax,%edi
  400576:       e8 08 00 00 00          callq  400583 <_Z3fooii>            #这里foo函数的具体位置确定了
  40057b:       89 45 f4                mov    %eax,-0xc(%rbp)
  40057e:       8b 45 f4                mov    -0xc(%rbp),%eax
  400581:       c9                      leaveq 
  400582:       c3                      retq   

0000000000400583 <_Z3fooii>:
  400583:       55                      push   %rbp
  400584:       48 89 e5                mov    %rsp,%rbp
  400587:       89 7d fc                mov    %edi,-0x4(%rbp)
  40058a:       89 75 f8                mov    %esi,-0x8(%rbp)
  40058d:       8b 55 fc                mov    -0x4(%rbp),%edx
  400590:       8b 45 f8                mov    -0x8(%rbp),%eax
  400593:       01 d0                   add    %edx,%eax
  400595:       5d                      pop    %rbp
  400596:       c3                      retq   
  400597:       66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
  40059e:       00 00 
......#此处省略其它部分的代码

从上述代码中可以看出main函数中的foo函数地址被确定了。

总结一下,整个静态链接的过程就是将几个输入目标文件加工后合并成一个输出文件,可以分为两步:

  • 空间与地址分配。在这个例子中链接器首先扫描simpleApp.o和foo.o文件,获得它们的各个段的长度、属性和位置,并且将输入目标文件中的符号表中所有的符号定义和符号引用收集起来,统一放到全局符号表。链接器将各个段合并,计算输出文件中各个段合并后的长度和位置,并建立映射关系。
  • 符号解析与重定位。使用第一步中收集到的所有信息,读取输入文件中段的数据、重定位信息,并且进行符号解析与重定位、调整代码中的地址等。上述例子中合并完simpleApp.o和foo.o的.text段之后,实际上foo函数的位置已经确定,链接器需要修改main函数中关于foo函数引用的地址,这就是重定位。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值