c链接,加载,装载(二)

 

编译驱动程序(compiler driver)


 这代表用户在需要时调用语言预处理器、编译器、汇编器和链接器。

预处理器  cpp

c编译器 cc1

汇编器 as

链接器 ld

 

在需要时体现在,比如

unix> gcc -c xxx.c

经过:

gcc --help

得知:

-c Compile and assemble, but do not link

即生成一个xxx.o的可重定位的目标模块文件。

值得留意得是驱动程序经过相同的程序生成xxx.o,最后,它运行链接器程序ld,将main.o文件和xxx.o以及一些必要的系统目标文件组合起来,创建一个可执行目标文件。

像unix ld这样的静态链接器(static linker)以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件作为输出。输入的可重定位目标文件由各种不同的代码和数据节(section)组成。指令在一个节中,初始化的全局变量在另一个节中,而未初始化的变量在另外一个节中。

 

 

 

unix> ./p

 

再次铺垫下,外壳调用操作系统中一个叫做加载器的函数,它拷贝可执行文件p中的代码和数据到存储器,然后将控制转移到这个程序的开头。 

目标文件纯属字节块的组合。在这些块中,有些包含程序代码,有些则包含程序数据,而其他的则包含指导链接器和加载器的数据结构。链接器将这些块连接起来,确定被连接块的运行时位置, 并且修改代码和数据块中的各种位置。

 

 

目标文件


 

  1. 可重定位目标文件
  2. 可执行目标文件
  3. 共享目标文件

 

编译器和汇编器生成可重定位的目标文件(包括共享目标文件)。

链接器生成可执行目标文件。

 

目标文件的格式:

System V Unix  (COFF)Common Object File Format
Windows NT (PE)Portable Executable
Unix(如Linux, System V Unix后来的版本,各种 BSD Unix等) (ELF)Executable and Linkable Format

 

 

以下是一个ELF格式的可重定位目标文件:

 

 

以下两个主题暂时跳过。

  • 符号和符号表
  • 符号解析

 

与静态库链接


 

在Unix系统中,静态库以一种称为存档(archive)的特殊文件格式存放在磁盘中。

存档文件是由一组连接起来的可重定位目标文件组合,有一个头部用来描述每个成员目标文件的大小和位置。

存档文件名由后缀.a标识。

 

静态库概念:相关函数可以被编译为独立的目标模块,然后封装成一个单独的静态库文件。然后应用程序可以通过命令行上指定单独的文件名字来使用这些在库中定义的函数。

unix> gcc main.c /usr/lib/libm.a

在链接时,链接器将只拷贝被程序引用的目标模块

以上概念展示如下,提供数组"加法"与"乘法"的一个静态库:

--------code/addvec.c

void addvec(int *x, int *y, int *z, int n)
{
    int i;

    for(i = 0;i < n; i++)
        z[i] = x[i] + y[i];
}

--------code/addvec.c

void multvec(int *x, int *y, int *z, int n)
{
    int i;

    for (i = 0; i < n; ++i)
    {
        z[i] = x[i] + y[i];
    }
}

创建这个库:

unix> gcc addvec.c multvec.c
unix> ar rcs libvector.a addvec.o multvec.o

其中经过静态编译分别得到可重定位的addvec.o 和 multvec.o目标文件。

ar工具用以创建静态库文件,并命名为libvector.a。

为了使用这个静态库,以下是main的代码(顺带可以问下自己一个老问题,为什么每个C程序需要main函数?):

#include <stdio.h>
#include "vector.h"

int x[2] = {1, 2};
int y[2] = {3 ,4};
int z[2];

int main()
{
    addvec(x, y, z, 2);
    printf("z = [%d %d]
", z[0], z[1]);
    return 0;
}

很明显,这个程序调用了libvector.a中一个成员addvec.o的函数addvec。

为了创建可执行文件:

unix> gcc -c main2.c
unic> gcc -static -o p2 main2.o ./libvector.a

这个过程可表示如图:

 

其中左上角代表了第一条输入终端的命令,然后-static指示链接器完全链接可执行目标文件,这个可执行文件可以加载到存储器运行,在加载时无需进一步的链接。

值得注意的是图中仅仅指示了libvector.a中的addvec.o被拷贝到可执行文件。

额外地,链接器还会拷贝libc.a中的printf.o模块,以及许多C运行时系统中的其他模块。 

以下主题省略没有详细看:

  • 链接器如何使用静态库来解析引用
  • 重定位PC相对引用/绝对引用

段头部表(segment header table)


回到可执行目标文件。然而链接器也可以将多个目标模块(如addvec.o)合并成一个可执行目标文件的。

C程序开始时时一组ASCCII文本文件,最后被转化为一个二进制文件。

需要留意的是这个二进制文件包含加载程序到存储器并运行它的所有信息。比如包括程序的入口点(entry point),也就是程序运行时执行的第一条指令的地址。

ELF可执行文件被设计的很容易加载到存储器(我受够这个翻译的名字了,不就是内存吗?难道还有其他地方?),可执行文件的连续的片(chunk)被映射到连续的存储器段。

 图中对段头部表:Maps contiguous(连续的;相邻的) file sections to runtime memory segments

 代码段:code segment,只读 

 数据段:data segment,可读写

相关更详细的数据要研究工具objdump(注意是小写):

指令:

linux> objdump -h a.out
a.out:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001c  0000000000400200  0000000000400200  00000200  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  000000000040021c  000000000040021c  0000021c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  000000000040023c  000000000040023c  0000023c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .gnu.hash     0000001c  0000000000400260  0000000000400260  00000260  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .dynsym       00000048  0000000000400280  0000000000400280  00000280  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynstr       00000038  00000000004002c8  00000000004002c8  000002c8  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .gnu.version  00000006  0000000000400300  0000000000400300  00000300  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .gnu.version_r 00000020  0000000000400308  0000000000400308  00000308  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .rela.dyn     00000018  0000000000400328  0000000000400328  00000328  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .rela.plt     00000018  0000000000400340  0000000000400340  00000340  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .init         00000018  0000000000400358  0000000000400358  00000358  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .plt          00000020  0000000000400370  0000000000400370  00000370  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .text         000001d8  0000000000400390  0000000000400390  00000390  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .fini         0000000e  0000000000400568  0000000000400568  00000568  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 14 .rodata       00000010  0000000000400578  0000000000400578  00000578  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 15 .eh_frame_hdr 00000024  0000000000400588  0000000000400588  00000588  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 16 .eh_frame     0000007c  00000000004005b0  00000000004005b0  000005b0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 17 .ctors        00000010  0000000000600630  0000000000600630  00000630  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 18 .dtors        00000010  0000000000600640  0000000000600640  00000640  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 19 .jcr          00000008  0000000000600650  0000000000600650  00000650  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 20 .dynamic      00000190  0000000000600658  0000000000600658  00000658  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 21 .got          00000008  00000000006007e8  00000000006007e8  000007e8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 22 .got.plt      00000020  00000000006007f0  00000000006007f0  000007f0  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 23 .data         00000004  0000000000600810  0000000000600810  00000810  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 24 .bss          00000010  0000000000600818  0000000000600818  00000814  2**3
                  ALLOC
 25 .comment      0000002d  0000000000000000  0000000000000000  00000814  2**0
                  CONTENTS, READONLY

 

 未完的更精彩的主题:

加载器实际上是如何工作的?(这里牵扯到进程,虚拟存储器等概念,都是帮助了解程序的)

gcc -shared -fPIC -o libvector.so addvec.c multvec.c(动态链接器,加载时)

gcc -rdynamic -o p3 xxx.c -ldl(动态链接器,运行时)

以及PIC!

to be continued ...

 

原文地址:https://www.cnblogs.com/dotdog/p/4570659.html