Linux动态连接原理 GOT PLT表详解

Linux Debugging(七): 使用反汇编理解动态库函数调用方式GOT/PLT 本文主要讲解动态库函数的地址是如何在运行时被定位的。首先介绍一下PIC和Relocatable的动态库的区别。然后讲解一下GOT和PLT的理论知识。GOT是Global Offset Table,是保存库函数地址的区域。程序运行时,库函数的地址会设置到GOT中。由于动态库的函数是在使用时才被加载,因此刚开始GOT表是空的。地址的设置就涉及到了PLT,Procedure Linkage Table,它包含了一些代码以调用库函数,它可以被理解成一系列的小函数,这些小函数的数量其实就是库函数的被使用到的函数的数量 阅读详情
分类:  LINUX系统开发2010-11-23 20:21  2621人阅读  评论(2)  收藏  举报

注意:

以下所用的连接器是指,ld,

而加载器是指ld-linux.so;

<!--[if !supportLists]-->1,  <!--[endif]-->GOT表;

GOT(Global Offset Table)表中每一项都是本运行模块要引用的一个全局变量或函数的地址。可以用GOT表来间接引用全局变量、函数,也可以把GOT表的首地址作为一个基 准,用相对于该基准的偏移量来引用静态变量、静态函数。由于加载器不会把运行模块加载到固定地址,在不同进程的地址空间中,各运行模块的绝对地址、相对位 置都不同。这种不同反映到GOT表上,就是每个进程的每个运行模块都有独立的GOT表,所以进程间不能共享GOT表。
在x86体系结构 上,本运行模块的GOT表首地址始终保存在%ebx寄存器中。编译器在每个函数入口处都生成一小段代码,用来初始化%ebx寄存器。这一步是必要的,否 则,如果对该函数的调用来自另一运行模块,%ebx中就是调用者模块的GOT表地址;不重新初始化%ebx就用来引用全局变量和函数,当然出错。

 

这两段话的意思是说,GOT是一个映射表,这里的内容是此段代码里面引用到的外部符号的地址映射,比如你用用到了一个printf函数,在这里就会有一项假设是1000,则就像这样的:

.Got

符号                             地址

Printf                      1000

………

这样的话程序在运行到printf的时候就寻找到这个地址1000从而走到其实际的代码中的地方去。

但是这里存在一个问题,因为printf是在共享库里面的,而共享库在加载的时候是没有固定地址的,所以你不知道它的地址是1000还是2000?怎么办呢?

于是引入了下面的表plt,这个表的内容是什么呢?请看下面:

<!--[if !supportLists]-->2,  <!--[endif]-->PLT表;

PLT(Procedure Linkage Table)表每一项都是一小段代码,对应于本运行模块要引用的一个全局函数。以对函数fun的调用为例,PLT中代码片断如下:

.PLTfun:  jmp *fun@GOT(%ebx)
          pushl $offset
          jmp .PLT0@PC

其中引用的GOT表项被加载器初始化为下一条指令(pushl)的地址,那么该jmp指令相当于nop空指令。

用户程序中对fun的直接调用经编译连接后生成一条call [email]fun@PLT 指令,这是一条相对跳转指令(满足浮动代码的要求!),跳到.PLTfun 。如果这是本运行模块中第一次调用该函数,此处的jmp等于一个空指令,继续往下执行,接着就跳到PLT[email]0。该PLT项保留给编译器生成的 额外代码,会把程序流程引入到加载器中去。加载器计算fun的实际入口地址,填入fun@GOT表项。图示如下:

                     user program
                    --------------
                     call fun@PLT
                           |
                           v
      DLL             PLT table                loader
--------------   --------------   -----------------------
fun:           <-- jmp*fun@GOT  --> change GOT entry from
                           |             $loader to $fun,
                           v             then jump to there
                       GOT table
                    --------------
                    fun@GOT <!--[if !vml]--><!--[endif]-->loader

第 一次调用以后,GOT表项已指向函数的正确入口。以后再有对该函数的调用,跳到PLT表后,不再进入加载器,直接跳进函数正确入口了。从性能上分析,只有第一次调用才要加载器作一些额外处理,这是完全可以容忍的。还可以看出,加载时不用对相对跳转的代码进行修补,所以整个代码段都能在进程间共享。

 

上面的话是什么意思呢?

拿我们上面举的例子,printf在got表里面对应的地址是1000,而这个1000到底以为着什么呢?

PLTfun:  jmp *fun@GOT(%ebx)
 1000
: pushl $offset
          jmp .PLT0@PC

你可以看到所谓1000就是它下面的这个地址,也就是说在外部函数还没有实现连接的时候,got表里面的内容其实是指向下一条指令的,于是开始执行了plt表里面的内容,于是这个段里面的内容肯定包括计算当前这个函数的实际地址的内容,于是求得实际地址添入got表,假设地址为0x800989898

于是got表里面的内容就应该这样的:

Printf                        0x800989898

………………..

这样当下一次调用这个printf的时候就不需要再去plt表里面走一遭了。
这里需要提一下的是,查找printf的地址实际上就是递归查找当前执行的程序所依赖的库,在她们export的符号表里面寻找,如果找到就返回,否则,报错,就是我们经常看到的undefined referenc to XXXXX.

<!--[if !supportLists]-->3,  <!--[endif]-->代码段重定位前提。

代码段本身是存在于只读区域的,所以理论上它是不可能在运行的时候重新修改的,但是这就涉及一个问题,如何保证Got表的正确使用,因为每一个进程都有自己的got表,而共享库完全同时被许多个进程使用的,于是在每个函数的入口都有这样的语句:

call L1
L1:  popl %ebx
     addl $GOT+[.-.L1], %ebx
.o:  R_386_GOTPC
.so: NULL

上述过程是编译、连接相合作的结果。编译器生成目标文件时,因为此时还不存在GOT表(每个运行模块有一个GOT表,一个PLT表,由连接器生成),所以暂时不能计算GOT表与当前IP间的差值,仅在第三句处设上一个R_386_GOTPC重定位标记而已。然后进行连接。连接器注意到GOTPC重定位项,于是计算GOT与此处IP的差值,作为addl指令的立即寻址方式操作数。以后再也不需要重定位了。

这样做的好处是目的是什么呢?

就是在函数内部引用外部符号的时候能够正确的转到适当的地方去。

<!--[if !supportLists]-->4,  <!--[endif]-->变量、函数引用

当引用的是静态变量、静态函数或字符串常量时,使用R_386_GOTOFF重定位方式。它与GOTPC重定位方式很相似,同样首先由编译器在目标文件中设上重定位标记,然后连接器计算GOT表与被引用元素首地址的差值,作为leal指令的变址寻址方式操作数。代码片断如下:

leal .LC1@GOTOFF(%ebx), %eax
.o:  R_386_GOTOFF
.so: NULL
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

当引用的是全局变量、全局函数时,编译器会在目标文件中设上一个R_386_GOT32重定位标记。连接器会在GOT表中保留一项,注上 R_386_GLOB_DAT重定位标记,用于加载器填写被引用元素的实际地址。连接器还要计算该保留项在GOT表中的偏移,作为movl指令的变址寻址 方式操作数。代码片断如下:

movl x@GOT(%ebx), %eax
.o:  R_386_GOT32
.so: R_386_GLOB_DAT

需要指出,引用全局函数时,由GOT表读出不是全局函数的实际入口地址,而是该函数在PLT表中的入口.PLTfun。这样,无论直接调用,还是先取得函数地址再间接调用,程序流程都会转入PLT表,进而把控制权转移给加载器。加载器就是利用这个机会进行动态连接的。

 

   注意:这里讨论的是变量函数的引用,不是函数的直接调用,而是函数,变量的地址的取得,如果是函数的话,取得的实际上是plt里面的地址,于是最终还是没能逃过加载器的协助。

<!--[if !supportLists]-->5,  <!--[endif]-->直接调用函数
如前所述,浮动代码中的函数调用语句会编译成相对跳转指令。首先编译器会在目标文件中设上一个R_386_PLT32重定位标记,然后视静态函数、全局函数不同而连接过程也有所不同。

如果是静态函数,调用一定来自同一运行模块,调用点相对于函数入口点的偏移量在连接时就可计算出来,作为call指令的相对当前IP偏移跳转操作数,由此直接进入函数入口,不用加载器操心。相关代码片断如下:

call f@PLT
.o:  R_386_PLT32
.so: NULL

如果是全局函数,连接器将生成到.PLTfun的相对跳转指令,之后就如前面所述,对全局函数的第一次调用会把程序流程转到加载器中去,然后计算函数的入口地址,填充fun@GOT表项。这称为R_386_JMP_SLOT重定位方式。相关代码片断如下:

call f@PLT
.o:  R_386_PLT32
.so: R_386_JMP_SLOT

如此一来,一个全局函数可能有多至两个重定位项。一个是必需JMP_SLOT重定位项,加载器把它指向真正的函数入口;另一个是GLOB_DAT重定位 项,加载器把它指向PLT表中的代码片断。取函数地址时,取得的总是GLOB_DAT重定位项的值,也就是指向.PLTfun,而不是真正的函数入口。

进一步考虑这样一个问题:两个动态连接库,取同一个全局函数的地址,两个结果进行比较。由前面的讨论可知,两个结果都没有指向函数的真正入口,而是分别指向两个不同的PLT表。简单进行比较,会得出"不相等"的结论,显然不正确,所以要特殊处理。

注意:

一个是必需JMP_SLOT重定位项,这里指的就是直接调用函数的情况;

另一个是GLOB_DAT重定位 项,这里指函数地址引用的情况;

<!--[if !supportLists]-->6,  <!--[endif]-->数据段的重定位

在数据段中的重定位是指对指针类型的静态变量、全局变量进行初始化。它与代码段中的重定位比较起来至少有以下明显不 同:一、在用户程序获得控制权(main函数开始执行)之前就要全部完成;二、不经过GOT表间接寻址,这是因为此时%ebx中还没有正确的GOT表首地 址;三、直接修改数据段,而代码段重定位时不能修改代码段。

如果引用的是静态变量、函数、串常量,编译器会在目标文件中设上 R_386_32重定位标记,并计算被引用变量、函数相对于所在段首地址的偏移量。连接器把它改成R_386_RELATIVE重定位标记,计算它相对于动态连接库首地址(通常为零)的偏移量。加载器会把运行模块真正的首地址(不为零)与该偏移量相加,结果用来初始化指针变量。代码片断如下:

.section .rodata
.LC0: .string "Ok/n"
.data
p:     .long .LC0
.o:  R_386_32 w/ section
.so: R_386_RELATIVE

如果引用的是全局变量、函数,编译器同样设上R_386_32重定位标记,并且记录引用的符号名字。连接器不必动作。最后加载器查找被引用符号,结果用来初始化指针变量。对于全局函数,查找的结果仍然是函数在PLT表中的代码片断,而不是实际入口。这与前面引用全局函数的讨论相同。代码片断如下:

.data
p:       .long printf
.o:  R_386_32 w/ symbol
.so: R_386_32 w/ symbol

 

<!--[if !supportLists]-->7,  <!--[endif]-->总结:

下表给出了前面讨论得到的全部结果:
                                .o                          .so
--------------------------------------------------------------------------
      |装载GOT表首地址        R_386_GOTPC     NULL
代码段|-----------------------------------------------------
重定位|引用变量函数地址 静态  R_386_GOTOFF    NULL
      |                 全局  R_386_GOT32     R_386_GLOB_DAT
      |-----------------------------------------------------
      |直接调用函数     静态  R_386_PLT32     NULL
      |                    全局  R_386_PLT32     R_386_JMP_SLOT
------|-----------------------------------------------------
数据段|引用变量函数地址 静态  R_386_32 w/sec  R_386_RELATIVE
重定位|                 全局  R_386_32 w/sym  R_386_32 w/sym

Android和Linux动态加载机制及PLT/GOT作用介绍 首先回顾一下Linux平台上,一个模块甲需要调用另外一个模块乙中的函数时的动态链接机制:     1、模块甲在编译期间,将要引用的模块乙的名字与函数名写入自身的符号表。     2、运行期模块甲调用时,调用流程是从调用代码到PLT表到GOT表再跳入模块乙。     而如何保证模块甲的代码能从其PLT/GOT跳到正确的模块乙入口,这就是链接器做的事情。     标准Linux链接器是 阅读详情

相关推荐

linux动态链接中的PLT和GOT,Linux中的GOT和PLT到底是个啥?

*本文原创作者:PhyzX,本文属FreeBuf原创奖励计划,未经许可禁止转载0x00 Intro本文以Ian Wienand的博客 为蓝本,我在必要的地方予以增补、解释以及再实验,希望读者对PLT和GOT有一个初步的、相对完整的认识。0x01The Simplest Example原文的标题是“PLT and GOT - the key to code sharing and dynamic l...

weixin_35126480的博客 1143

linux动态链接中的PLT和GOT,(转)Linux GOT与PLT

读《深入理解计算机系统》第七章《链接》笔记。在ELF文件的动态连接机制中,每一个外部定义的符号在全局偏移表 (Global OffsetTable,GOT)中有相应的条目,如果符号是函数则在过程连接表(Procedure LinkageTable,PLT)中也有相应的条目,且一个PLT条目对应一个GOT条目。以简单的例子观察GOT与PLT的变化机制。下面是一个简单的HelloWorld程序:123...

weixin_34368102的博客 572

jemalloc的malloc案例来分析GOT表和PLT表有关流程

jemalloc的malloc案例来分析GOT表和PLT表,main之前的malloc分析,rip寄存器,glibc,_dl_runtime_resolve,_dl_fixup

zhaoxin1989的博客 4522

linux 动态连接原理,Linux动态连接原理 GOT PLT表详解

注意:以下所用的连接器是指,ld,而加载器是指ld-linux.so;1,GOT表;GOT(Global Offset Table)表中每一项都是本运行模块要引用的一个全局变量或函数的地址。可以用GOT表来间接引用全局变量、函数,也可以把GOT表的首地址作为一个基 准,用相对于该基准的偏移量来引用静态变量、静态函数。由于加载器不会把运行模块加载到固定地址,在不同进程的地址空间中,各运行模块的绝对地...

weixin_29859471的博客 494

腾讯滑块验证码collect参数逆向与JSVMP对抗实战

本文深入解析腾讯滑块验证码collect参数的逆向过程与JSVMP虚拟机对抗技术。通过详细的环境搭建、抓包分析和JSVMP逆向技巧,揭示collect参数的生成逻辑与加密流程,并提供完整的模拟实现方案和常见问题排查方法,助力开发者突破腾讯天御验证码的防护机制。

weixin_29009501的博客 1578

风控IV、AUC、KS python计算代码

df_temp:数据集,Y:是否逾期标签,feas_list:需要计算IV的 变量。2、 按月和不同Y 下的 IV计算。

暮雨听轩的专栏 1618

重定位类型分析(2)

本节分析R_386_GOTPC,R_386_GOT32,R_386_GLOB_DAT和R_386_PLT32 PIC位置无关代码,使用-fPIC参数就可以编译得到 >>>>>>>>>>>>>>>>>R_386_GOTPC 看看第一个重定位符号的重定位信息 汇编代码:        call    .L3 //.L3地址压栈.L3:      

ruixj的专栏 3619

fMRI脑影像特征提取——静息态与任务态,ALFF/fALFF和ReHo(Dpabi,Rest1.8)

任务态和静息态是大脑的两种状态,前者主要是指大脑在执行记忆、识别以及运动等具体任务时的状态,静息态则是指大脑不执行具体认知任务、保持安静、放松、清醒时的状态,是大脑所处的各种复杂状态中最基础和最本质的状态。任务态fMRI分析有非常悠远的历史,而静息态fMRI自1995年biswal教授及其同事率先报告静息态fMRI具有生理意义后,越来越多的人进入了静息态的研究领域。任务态fMRI主流的分析方法是模型驱动(GLM+HRF),也可以引用静息态fMRI

意疏的学习笔记 6万+

GOT表,PLT表,代码段重定位,数据段重定位--Linux动态连接原理

转载地址:http://blog.youkuaiyun.com/lmh12506/article/details/6801630  Linux动态连接原理 注意: 以下所用的连接器是指,ld, 而加载器是指ld-linux.so; 1,  GOT表; GOT(Global Offset Table)表中每一项都是本运行模块要引用的一个全局变量或函数的地址。可以用GOT表来间接引用全局变量、函数

yaoyaowugui的专栏 4126

卸载重装Ubuntu22.04双系统

先说一下写此篇文章的背景吧,我在Windows下配置了三个cornerNet深度学习环境分别是cuda10.3(显卡版本太高不支持),cuda11.3(配置后偶然看到一篇说这个版本win下只能用CPU跑),最后是cuda11.1终究还是没能跑通代码,因为它的代码是在Liunx系统写的,就想着装个Ubuntu试试看,在网上看说一般软件不要装太高版本,我就下载了470版的显卡驱动,跟着步骤升级了系统内核,结果第二天开机Ubuntu就崩了,开机黑屏左上角只有一道横杠显示,只能按ctrl+shift+f5进入命令窗

编程小白的博客 8109

ENSP从下载到运行:用虚拟机搭建华为认证实验环境全流程(含USG6000V镜像配置)

本文提供了一份详细的华为ENSP仿真平台从下载、安装到配置USG6000V防火墙镜像的完整指南。内容涵盖前置软件安装、ENSP主程序配置、核心设备镜像导入及虚拟机资源分配策略,旨在帮助网络工程师和学习者快速搭建一个稳定、功能齐全的华为认证实验环境,为备考和模拟企业网络部署打下坚实基础。

weixin_29230865的博客 536

linux 链接 got plt,Linux动态连接原理 GOT PLT表详解

转载逆风飞扬的文章,有时间有需要认真研究。Linux动态连接原理 GOT PLT表详解 注意: 以下所用的连接器是指,ld, 而加载器是指ld-linux.so; 1, GOT表; GOT(Global Offset Table)表中每一项都是本运行模块要引用的一个全局变量或函数的地址。可以用GOT表来间接引用全局变量、函数,也可以把GOT表的首地址作为一个基 准,用相对于该基准...

weixin_39991055的博客 277

【硬件】LT3763中文手册

1)精确控制输入和输出电流;2) 3000:1真彩色PWM™调光;3) ±1.5%电压调节精度;4) ±6%电流调节精度;5)6V至60 V输入电压范围;6)宽输出范围最高可达55V;7)<2µA关断电流;8)用于负载电流热控制的控制引脚;9)输入和输出电流监控和限制;10)开路、短路和C/10故障检测;11)适用于LED应用的PWM驱动器输出;12)热增强型28引脚FE封装。

qq_39200110的博客 489

GOT和PLT原理简析

GOT(Global Offset Table)和PLT(Procedure Linkage Table)是Linux系统下面ELF格式的可执行文件中,用于定位全局变量和过程的数据信息。以C程序为例,一个程序可能会包含多个文件,可执行文件的生成过程通常由以下几步组成。 1. 编译器把每个.c文件编译成汇编(.s)文件。 2. 汇编器把每个(.s)文件转换为(.o)文件。 3. 链接器把多个.

softee的专栏 1万+

基于高频变压器的隔离式推挽栅极驱动电路

摘要:本文介绍了一种基于高频变压器的隔离式推挽栅极驱动电路,通过变压器实现电气隔离,为功率MOS管提供可靠驱动信号。电路包含输入侧PWM信号处理、变压器磁耦合隔离传输以及副边驱动逻辑等关键部分,利用电阻电容网络控制开关速度并防止误触发。该隔离驱动方案广泛应用于高压开关电源、新能源汽车电机控制器、光伏逆变器和工业变频器等需要高电压隔离及抗干扰能力的电力电子设备中,有效保障系统安全性和可靠性。

qq_37999850的博客 485

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

【Linux库制作与原理进阶】从 ELF 装载到延迟绑定:搞懂动态库、GOT 与 PLT

本文围绕 ELF 装载与动态链接展开,讲清 Program Header Table 如何指导 Segment 映射,动态库如何进入进程地址空间,以及 PIC、GOT、PLT 分别解决什么问题。通过首次调用与后续调用的对比,进一步理解符号解析、地址重定位和延迟绑定的完整过程。

2402_87731470的博客 4104

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

深入解析PLT与GOT表:动态库调用的奥秘与安全防护

PLT与GOT表是Linux系统中动态库调用的核心机制,它们通过巧妙的设计实现了高效的符号解析和函数调用。然而,这种机制也带来了安全风险。通过使用像Virbox Protector这样的加壳工具,开发者可以有效保护程序的安全性,防止恶意篡改和逆向工程。希望本文能够帮助你更好地理解PLT与GOT表的工作原理,以及如何保护你的程序免受安全威胁。

VBP_2025的博客 1104

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

ELF动态链接时GOT、PLT原理

背景 我们知道linux为了降低可执行程序体积,提高空间利用效率,经常采用动态链接方式生成动态库或者可执行程序。在动态链接时,为了避免在加载时对代码段进行重定位导致动态库代码段无法实现共享,我们采用了位置无关代码PIC技术(Position-Independent Code)。针对模块外代码,为了实现PIC技术,我们需要借助全局偏移表(GOT,Gobal Offset Table)。 全局偏移表GOT 首先写一个小的测试程序进行说明。在SendMessage.c文件中实现了一个函数SendMessag

sy4331的博客 2442

一篇文章,搞懂 GOT表 & PLT表

一篇文章,搞懂 GOT表 & PLT表 一个栗子 以一段很简单的代码为例#include<stdio.h> int main(void){ printf("1st"); printf("2nd"); return 0; } 在 Linux 下,使用 gcc 编译,编译时关闭 ASLR(Address Space Layout Randomization,Linux 中叫做 pie)gcc ./test.c -o test -no-pie #-no-pie

qq_43547885的博客 2623
上一篇: 中断调用过程
下一篇: ELF的GOT和PLT以及PIC
linuxheik
博客等级 码龄15年 865粉丝 135原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值