示例程序 main.c:

#include <stdio.h>void print_banner()
{printf("Welcome to World of PLT and GOT\n");
}int main(void)
{print_banner();return 0;
}

一个源文件想变成能够在内存中运行的程序,需经历编译、链接、载入3个步骤。

编译:gcc -Wall -g -o main.o -c main.c -m32
链接:gcc -o main main.o -m32

注:
可执行文件跟.o的区别在于:可执行文件有一个可由操作系统调用的入口函数main。

1、编译

编译生成的每一个.o文件包含3部分内容:
1)代码段:存放当前源文件定义的函数;
2)数据段:存放当前源文件定义的全局变量;
3)符号表:存放当前源文件未定义却使用了的函数或全局变量(它们可能由其他.o或.so定义)。
在符号表中,未定义的函数/全局变量被标记为UND,即undefined。(可使用命令:objdump -x XXX.o 查看XXX.o的符号表和重定位表)

在编译阶段,可确定前两部分相对当前.o起始位置的偏移地址,但对于第三部分,由于编译器不清楚printf的地址,因此此处预写一个 0XFF FF FF FC(即-4),用于告诉链接器:需根据printf地址来修正该地址。

2、链接(ld)

链接包括静态链接、载入时动态链接、运行时动态链接。

2.1、静态链接(.o之间的链接或.o与静态库之间的链接)

链接器根据链接顺序,将所有的.o(对于静态库,只会合并所用到的目标文件)合并为一个可执行文件:
1)所有.o中的代码段/数据段 合并到 可执行文件的代码段/数据段。合并后,可执行文件中的函数/全局变量相对文件起始位置的偏移地址,即为最终内存地址。

2)对于符号表中的函数/全局变量,若它们是由其他.o或.a定义,则通过查找前一步的合并结果,即可确定其最终内存地址,并将0XFF FF FF FC修正为最终内存地址即可。

2.2、载入时动态链接

在2.1中,对于.so定义的函数/全局变量的最终内存地址,链接器是无法确定的,而只能在载入动态库后确定。那么,在载入动态库glibc后,应如何让call指令找到printf的地址?
最为简单的方法是,直接将0XFF FF FF FC修改为printf的真正地址即可。问题在于:
1)call指令位于代码段中,而代码段在运行时是不允许被修改;
2)即使代码段允许修改,但若print_banner定义在.so中,则修改后就无法在多个进程之间共用这个.so。因为它们共享.so的代码段,但每个进程都有一份独立的数据段。

那么,能否在.so加载后,将函数/数据的地址存入数据段,而代码直接读取数据段内的地址?
Yes!
这个存储函数/数据地址的数据段,即为GOT(Global Offset Table,全局偏移表)。表中的每一项为函数/数据的地址。

因此链接器的做法是,生成一段额外代码print_stub,并将call printf指令重定位到print_stub,而printf_stub又重定位到GOT中的函数/数据地址。
这个存储额外代码的表,称为PLT(Procedure Link Table,程序链接表)。

载入时动态链接是指,在将可执行文件加载到内存后,且在程序开始运行前,查找链接时所指定的动态库,并将GOT中的函数/数据地址修正为其真正地址。

那么程序在运行阶段,printf的调用过程:

2.3、PLT/GOT

PLT表包含若干项,第一项为公共项,指向GOT中的第二项,即动态链接器内的_dl_runtime_resolve函数(该项在链接阶段为0X000000,载入阶段由动态链接器填充),其余每一项对应一个动态库函数。(每一项包含若干指令)

第一次调用printf:
1)调用PLT中printf对应的代码片段;
2)调用GOT中存储的printf。此处存储的地址是1)中PLT的下一条指令的地址,因此程序调回到PLT中;
3)调用PLT第一项_dl_runtime_resolve对应的代码段;
4)调用GOT中存储的_dl_runtime_resolve函数的真实地址,而_dl_runtime_resolve会去.so中查找printf的地址,并将2)中GOT存储的printf地址修改为printf的真实地址(那么下次调用无需_dl_runtime_resolve),并调用printf函数。

非第一次调用printf:

2.4 PIC(position-independent code,地址无关代码)

对于动态库来说,是指将动态库中的代码段映射到不同进程的地址空间,其实现是借助GOT/PLT表。
原理是:当某指令想访问某个函数/数据时,并不是通过绝对地址进行访问,而是访问存放函数/数据的绝对地址的GOT表。(指令与GOT的相对位置链接器已知)

2.5、运行时动态链接(延迟重定位)

运行时动态链接是指,在编译链接时不指定所依赖的动态库,而在程序中使用dlopen/dlsym时,才对GOT表项做重定位。因此相比载入时动态链接,可加快程序的启动速度。

3、静态链接 VS 动态链接

对于同时包含静态/动态链接库的若干个可执行文件:
1)磁盘空间:每个可执行文件中都有一份静态链接库的代码/数据,但仅包含动态链接库的必要信息。
2)独立运行:动态链接生成的执行文件不可独立运行;
3)性能:加载时动态链接会拖慢程序的启动速度,对于运行时动态链接,当首次调用动态库的函数时,程序会被暂停直至链接结束。
4)内存:若干个同时运行的程序,都使用了某个静态链接库/动态链接库,则内存中会有该静态链接库的多个副本,而大家共用一份动态链接库。
5)重新编译:若修改了动态库,只要未改变接口,则无需重新编译可执行文件。

静态链接与动态链接原理相关推荐

  1. 深入浅出静态链接和动态链接

    作为一名C/C++程序员,对于编译链接的过程要了然于胸.首先大概介绍一下,编译分为3步,首先对源文件进行预处理,这个过程主要是处理一些#号定义的命令或语句(如宏.#include.预编译指令#ifde ...

  2. 嵌入式学习笔记之二(静态链接与动态链接)

    要了解静态链接与动态链接,首先要了解什么是静态链接和动态链接,这一部分这里不做讲解,可以去度娘查找. 1.静态链接 静态链接通过静态库进行链接,生成的目标程序中包含运行需要的所有库,可以直接运行,不过 ...

  3. linux gcc 静态编译,GCC 程序编译的静态链接和动态链接

    (给Linux爱好者加星标,提升Linux技能)转自:Mr_Bluyee 在链接阶段中,所有对应于源文件的 .o 文件.'-l' 选项指定的库文件.无法识别的文件名(包括指定的.o目标文件和.a库文件 ...

  4. 静态库调用_静态链接和动态链接对比简析

    0. 简介 在Linux环境下进行开发工作,代码要经过编译链接生成二进制可执行文件,才能被CPU识别并执行:程序的编译过程可以参考另外一篇文章<linux程序编译过程简析>:链接过程分为两 ...

  5. c++定义一个动态全局变量_静态链接与动态链接的宏观概述及微观详解

    静态链接与动态链接的宏观概述及微观详解 第一部分 宏观概述 1. 静态链接 静态链接就是在程序运行前,链接器通过对象文件中包含的重定位表,完成所有重定位操作,并最终形成一个在运行时不需要再次进行依赖库 ...

  6. 静态链接与动态链接的区别

    动态链接库.静态库.import库区别 动态链接库(Dynamic Linked Library): Windows为应用程序提供了丰富的函数调用,这些函数调用都包含在动态链接库中.其中有3个最重要的 ...

  7. 装入归档文件时出现了一个错误linux_静态链接与动态链接(Linux)

    前言 上一篇分享了静态链接与动态链接的实验(Windows下).这一篇分享Linux下的笔记,同时对上一篇笔记做一个补充. 首先,我们把静态链接与动态链接做一个这样子的比喻: 把链接过程看做我们平时学 ...

  8. libcurl linux 静态链接库_GCC 程序编译的静态链接和动态链接

    转自:Mr_Bluyee 在链接阶段中,所有对应于源文件的 .o 文件."-l" 选项指定的库文件.无法识别的文件名(包括指定的.o目标文件和.a库文件)按命令行中的顺序传递给链接 ...

  9. C语言 | Linux下的静态链接与动态链接

    1024G 嵌入式资源大放送!包括但不限于C/C++.单片机.Linux等.关注微信公众号[嵌入式大杂烩],回复1024,即可免费获取! 前言 上一篇分享了静态链接与动态链接的实验(Windows): ...

最新文章

  1. Flash与组件:国外收费组件网站
  2. 计算机重启如何进入设置界面,win7电脑无法正常启动,一开机就进入到bios设置界面怎么办?...
  3. 【C++ Primer | 16】容器适配器全特化、偏特化
  4. leetcode1292. 元素和小于等于阈值的正方形的最大边长(二分法+前缀和)
  5. java 判断是不是微信打开_Java判断浏览器是微信还是支付宝
  6. 基于lis3dh的简易倾角仪c源码_开源网关apisix源码阅读和最佳实践
  7. 端口号及对应的服务汇总 (适用于Linux/Windows系统)
  8. 求过度矩阵+(二次型标准化)正交变换的过度矩阵
  9. soul源码阅读(七)Spring Cloud应用示例运行续2
  10. 云杰恒指:9.4恒指期货实盘指导交易复盘
  11. Autovue 版本有关的几个问题
  12. nginx reopen nginx 日志
  13. c语言算术运算的作用,C语言算术运算符和算术表达式
  14. 微服务并不能修复你破碎的组织文化
  15. 南方稻米借聊城水运 国稻种芯·中国水稻节:山东江北稻崛起
  16. 图片转world文档 Excel excel 新
  17. 普通家用电风扇电机绕组
  18. windows 安装 IDES
  19. 基础加强 -- 反射
  20. 题目65:一个句子中也许有多个连续空格,过滤掉多余的空格,只留下一个空格。

热门文章

  1. 微信小程序开发 | 02 - 轮播图实现(swiper组件)
  2. 史上最全Nginx配置优化
  3. liunx安装node
  4. 液晶面板里面有些什么配件_液晶电视的主要部件包括哪些?
  5. 【solidity】函数修饰器(Function Modifiers)
  6. cerr与cout心得
  7. 【工具】JS脚本|网页任意视频倍速播放(包括MOOC、本地视频、其他的视频)
  8. vps怎么做文件服务器,vps搭建文件服务器
  9. ubuntu 8.10安装配置经验(转载)
  10. PSO粒子群算法调节PID控制器参数