程序的链接——动态链接

前言:程序的链接最后一部分「动态链接」

这篇博客主要讲在第一次加载并运行时进行动态链接

大致过程如下:

如何使用动态链接

// main.c
void func1();
int main() {
    func1();
    return 0;
}
// func.c

#include <stdio.h>


void func() {
    printf("func \n");
}

void func1() {
    printf("func 1\n");
}
  • 生成位置无关的共享库

    gcc -c func.c

    gcc -shared -fPIC -o func.so func.o

  • 生成可重定位目标文件

    gcc -c main.c

  • 与动态链接库一起生成可执行目标文件

    gcc -o main main.o ./func.o

  • 运行

    结果:func 1

如果把 func.so 从文件夹中删除,那么就运行不了了。

要注意!系统自带的共享库函数是不用自己写进命令里面的,因为命令本身会自动补全

原理

原理正如图所示:

程序在第一次加载并运行时,才重定位链接共享库函数和数据

同样是和静态链接的可执行文件一样的方法运行,为什么动态链接的可执行文件在执行的时候会有动态链接器呢?

程序序头表中有一个特殊的段:INTERP,其中记录了动态链接器目录及文件

也就是说:

加载器发现在其程序头表中有 .interp 段,其中包含了动态链接器路径名 ld-linux.so,因而加载器根据指定路径加载并启动动态链接器运行。动态链接器完成相应的重定位工作后,再把控制权交给 myproc,启动其第一条指令执行

位置无关

之前提到了位置无关,只有共享库被链接成位置无关的代码和数据链接器才无需修改代码即可将共享库加载到任意地址运行

一共有四种情况:(位置无关主要解决后两者的问题)

模块内的过程调用、跳转

  • 调用或跳转源与目的地都在同一个模块,相对位置固定,只要用相对偏移寻址即可

  • 无需动态链接器进行重定位

模块内数据访问,如模块内的全局变量和静态变量

  • .data 节与 .text 节之间的相对位置确定,任何引用局部符号的指令与该符号之间的距离是一个常数

模块外的数据访问,如外部变量的访问

  • 引用其他模块的全局变量,无法确定相对距离

  • 在 .data 节开始处设置一个指针数组(全局偏移表,GOT),指针可指向一个全局变量

  • GOT 与引用数据的指令之间相对距离固定

  • 编译器为 GOT 每一项生成一个重定位项(如 .rel 节...)

  • 加载时,动态链接器对GOT中各项进行重定位,填入所引用的地址

简而言之:在 .data 节里面有一个 GOT 表,它的相对位置固定,在动态连接时把外部数据的地址填入里面,内部用时,用的是 GOT 表中的地址

模块外的过程调用、跳转

  • 也可以用上述的办法,但是调用模块外的函数都要增加三条指令
  • 可以使用延迟绑定的方法(留个坑,以后好好介绍)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。