链接

2022/06/13

CSAPP中关于链接(linker)部分的笔记

1. Object file

Object file是一些程序指令、数据结构和数据所组成的集合,由三种形式,分别为Relocatable object file, Executable object fileShared object file

在Linux与Unix中把这种数据格式称为Executable and Linkable Format, 简称为ELF, 格式为

image-20220613144452004

2. 静态链接库

编译流程图

根据上面的那个编译流程图可知,在生成可执行文件的时候需要把多个Relocatble object file链接在一起,我们在写程序的时候经常会用到一些通用程序库,如何把他们链接在一起呢?一个方法是把所有的库文件都生成一个.o文件,然后编译的时候传给链接器,但是这个会导致程序变得非常大,并且当程序运行的时候需要载入内存,也会造成内存空间的浪费。那如果我们把库中的每个代码文件分开生成.o文件,然后当写的代码需要那一部分代码就引入那个.o文件,这样如果代码复杂最后需要传入的文件太多了,比较麻烦。所以此时就提出了静态链接库的概念,可以首先将每个代码文件生成一个.o文件,再将这些相似的文件合并成一个.a文件,这个.a文件就是静态链接库。下面创建一个vector库,实现两个数组对应元素的相加与相乘功能,首先编写数组对应元素相加与相乘的函数,分别命名为addvec.cmultvec.c,函数内容如下

  1. addvec.c

    void addvec(int *x, int *y, int *z, int n){
        int i;
        addcnt++;
        for(i = 0; i < n; i++){
            z[i] = x[i] + y[i];
        }
    }
  2. multvec.c

    ar libvector.a addvec.o multvec.ovoid multvec(int *x, int *y, int *z, int n){
        int i;
        multcnt++;
        for(i = 0; i < n; i++){
            z[i] = x[i] * y[i];
        }ar libvector.a addvec.o multvec.o
    }

然后再写一个头文件包含这两个库的函数原型, 命名为vector.h

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

现在开始将这些文件编译成一个静态库,首先将addvec.cmultvec.c编译但不链接,也就是生成.o文件

gcc -c addvec.c multvec.c

这里-c参数就表示只编译,但是不打包,这样就会生成addvec.omultvec.o这两个relocation object file,然后可以将这两个文件合并成一个静态库

ar rcs libvector.a addvec.o multvec.o

生成的libvector.a就是我们需要的静态库,下面可以写一个main.c来调用刚才生成的静态库

#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]\n", z[0], z[1]);
    return 0;
}

首先需要将main.c编译成main.orelocation object file, 然后再使用静态链接,将静态库和main.o链接再一起,命令如下

gcc -c main.c
gcc -static -o a.out main.o -L. -lvector

3. 动态链接库

静态库虽然有很多优先但是也有它的缺陷,比如当使用静态库的时候,如果库中的代码发生更改,那么所有需要该库的项目都需要重新编译,又比如需要项目都用到了一些基础库,比如输入,当这些项目运行起来之后,这些库会被重复载入内存,浪费内存空间。为了解决这些问题,就提出了动态链接库,动态链接库的定义为: Ashared library is an object module that, at either run time or load time, can be loaded at an arbitrary memory address and linked with a program in memory. 如何来创建并引入动态库可以参考这篇文章 shared-libraries-linux-gcc