程序运行之静态链接一

mac2022-06-30  70

前面介绍了单个.o文件的格式以及里面的内容。那么如果我们有多个目标文件,如何将它们链接成一个可执行的文件呢。多个目标文件就涉及到了链接。我们首先介绍静态链接。

有如下2个文件,test.c/test1.c

test.c

#include<stdlib.h>

#include<stdio.h>

extern int shared;

int main(){

int a=100;

swap(&a,&shared);

}

test1.c

#include <stdio.h>  

int shared=1;

void swap(int *a,int *b){

int temp;

temp=*a;

*a=*b;

*b=temp;

}

首先我们通过gcc分别得到2个文件的.o文件。 gcc -c test.c test1.c

从代码中可以看到,test1.c总共定义了两个全局符号,一个是变量shared,一个是函数swaptest.c里面引用到了test1.c中的sharedswap。接下里的工作就是要把test.otest1.o这两个目标文件链接在一起形成一个可执行的文件。

前面介绍过每个目标文件都有代码段,数据段等等。那么多个目标文件链接在一起的话,这些段该如何组合呢。

一个最简单的方法就是叠加,如下图所示,在最终的目标文件中,把所有的目标文件都依次叠加。这样做的确很简单明了。但是会造成一个问题,在有很多输入文件的情况下,输出文件将会有很多零散的段。比如一个规模稍大的工程就有好几百个文件。而且每个文件都有.data,.text.bss段。那么最后输出的文件将会有成百上千个零散的段。这样非常浪费空间,而且每个段都必须要用一定的地址和空间对齐要求。比如X86的硬件,段的装载地址和空间的对齐单位是页,也就是4096字节。如果一个段的长度是1个字节。在内存中就要占用4096字节。多个这样的段将会极大的浪费内存。

那么第二种方法就是相似段合并了。我们把目标文件的.data,.text,.bss段分别整合在一起。如下图所示。

这种链接方法内部分为两个步骤:

第一步: 空间与地址分配

扫描所有的输入目标文件,获得它们的各个段的长度,属性和位置,并且将输入目标文件中的符号表中所有的符号定义和符号引用收集起来。统一放到一个全局符号表。这一步中,链接器能够所有输入目标文件的段长度,并且将它们合并,计算出输出文件中各个段合并后的长度与位置,并建立映射关系。

第二步:符号解析与重定位 

使用上面收集到的所有信息,读取输入文件中段的数据,重定位消息,并且进行符合解析与重定位,调整代码中的地址等

比如我们将test.otest1.o链接起来生成一个可执行的文件

gcc test.o test1.o -o ab

 

首先来看test.o的文件

root@zhf-maple:/home/zhf/c_prj# objdump -h test.o

 

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

 

节:

Idx Name          Size      VMA               LMA               File off  Algn

  0 .text         00000051  0000000000000000  0000000000000000  00000040  2**0

                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE

  1 .data         00000000  0000000000000000  0000000000000000  00000091  2**0

                  CONTENTS, ALLOC, LOAD, DATA

  2 .bss          00000000  0000000000000000  0000000000000000  00000091  2**0

                  ALLOC

 

 

objdump -h test1.o

 

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

 

节:

Idx Name          Size      VMA               LMA               File off  Algn

  0 .text         0000002d  0000000000000000  0000000000000000  00000040  2**0

                  CONTENTS, ALLOC, LOAD, READONLY, CODE

  1 .data         00000004  0000000000000000  0000000000000000  00000070  2**2

                  CONTENTS, ALLOC, LOAD, DATA

  2 .bss          00000000  0000000000000000  0000000000000000  00000074  2**0

                  ALLOC

 

 

objdump -h ab

 

ab:     文件格式 elf64-x86-64

 

节:

Idx Name          Size      VMA               LMA               File off  Algn

                  CONTENTS, ALLOC, LOAD, READONLY, CODE

 13 .text         00000202  0000000000000560  0000000000000560  00000560  2**4

 22 .data         00000014  0000000000201000  0000000000201000  00001000  2**3

                  CONTENTS, ALLOC, LOAD, DATA

 23 .bss          00000004  0000000000201014  0000000000201014  00001014  2**0

                  ALLOC

 24 .comment      00000023  0000000000000000  0000000000000000  00001014  2**0

                  CONTENTS, READONLY

 

从上面的结果可以看到,在链接之前,VMA的地址都是0,这是因为虚拟空间还没有分配。等到链接之后,可执行文件ab中的各个段都被分配到了相应的虚拟地址。在ab.text段被分配到了0000000000000560,大小为0x202字节。.data段从地址0000000000201000开始。大小为14字节。

 

完成空间和地址的分配步骤以后,链接器就进入了符号解析与重定位的步骤。这也是静态链接的核心。在分析符号解析和重定位之前。我们首先看下test.o里面是怎么使用这两个外部符号的。

 

objdump -d test.o

 

test.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: 64 48 8b 04 25 28 00  mov    %fs:0x28,%rax

   f: 00 00 

  11: 48 89 45 f8           mov    %rax,-0x8(%rbp)

  15: 31 c0                 xor    

转载请注明原文地址: https://mac.8miu.com/read-21887.html
最新回复(0)