elf .got .plt 表以及共享库加载原理,在没有mmu的单片机上实现共享库

本文探讨了在无MMU的单片机上实现动态加载的方法,重点介绍了使用gcc编译器与工具TrueStudio的优势。文章详细分析了ELF文件格式,特别是动态库的结构及其重定位过程,包括.got表、.got.plt表和.rel.dyn表的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    之前研究了在没有mmu的单片机上实现动态加载,因为之前使用的是keil编译工具,它使用的是arm公司自己开发的编译器,很多地方不好研究。其实使用gcc也是可以开发Stm32的,最近发现一个开发工具叫tureStudio,是一个利用Eclipse与gcc组合起来的开发工具,特点是使用了gcc编译器,资料多呀,特别是对于elf文件格式的与linux下基本一模一样。下面就总结一下最近研究到的东西。

    elf文件格式大致可以分两种,一种是执行程序,一种是动态库。可执行程序的elf文件内的地址是固定地址,代码在加载的时候都必须被加载到固定位置的地址上,如果单片机有mmu,就必须加载到固定位置的虚拟地址上。动态库的elf文件内的符号地址都是可以重定向的,下面我们就具体研究一下这个动态库,这对于单片机上进行动态加载是非常重要的。

    介绍两个工具:readelf.exe 以及 objdump.exe    前者可以对elf文件的各个段内信息等进行分析,后者对于段内代码可以进行反汇编等。对于研究elf文件非常有用

    解释:符号:函数,变量都叫符号

    一般来说一个elf文件分为,elf头,程序表,section等 部分。一切elf内的数据都可以通过这个elf头得到。在elf文件中程序头表如下图:


上面的load段代表了需要被加载进内存的代码位置等信息,dynamic代码动态段的位置,下面会单独说动态段。


elf文件中section段一般如下:

    

一般来说关注.got .got.plt .rel.dyn表,这几个表对于重定向来说比较重要,对于在不同文件内进行重定向.dynamic段比较重要。

    在这里.got表存放了被重定向的符号的地址,一般在.rel.dyn表内会给出需要修改的.got表的位置。

    在.got.plt表中存放了间接访问的代码,用于延迟加载。什么意思呢?意思是在程序中去访问某一个函数时并不会立刻去访问到那个函数的地址上去,而是通过了先去访问这个.got.plt表中的一小段代码,然后这一小段代码再去从.got表中得到函数被重定向后的地址去访问。这样的好处是不用去修改代码,只需要修改.got表中的内容就行了。.got.plt表中的内容像下面一样:

    

上面红色框内的就是一个.got.plt中的一项,当一个函数被执行时,先执行.got.plt表中的代码吗,这段代码会去.got表中找到真实的函数地址,然后进行访问。可以看到,其实这样会降低一些运行效率。

    下面说说重定向是怎么实现的,其实就是对.rel.dyn表进行遍历,先看下面这张表:


这里我们只关系R_ARM_GLOB_DATA 与R_ARM_JUMP_SLOT这两个类型的数据,可以看到一个offset项,这个就是其在内存中的偏移,执行观察会发现指向的其实是.got表中的内容,通过这个偏移去改变其地址,就实现动态加载,注意这里只是简单实现,真正要做的有很多。

    说完了重定向,说下动态库之间是如何重定向的,其实就是我们之前说的dynamic中的数据,这张表中包含了一些段内信息,同时也包含了需要使用到的共享库,我们看看下面这张图:

    

从上面我们可以看到,一个类型为NEEDED的项,这一项包含了我们需要使用到的共享库,当我们在编译器中包含了共享库的时候,编译器就会在这里增加一项,当我们动态链接这一个程序的时候,程序就会在指定目录下去找这一个共享库,并与当前共享库进行重定向。

### `.got.plt` 的作用与原理ELF(可执行与可链接格式)文件中,`.got.plt` 是全局偏移(Global Offset Table, GOT)的一部分,主要用于支持延迟绑定(Lazy Binding),这是动态链接器优化函数调用的一种机制。 #### 1. `.got.plt` 的结构和用途 `.got.plt` 区段通常位于程序的 `.got` 段内,并保留特定条目用于 PLT(过程链接)机制。典型的 `.got.plt` 前三个条目具有特殊含义: - **GOT[0]**: 存储的是动态段(`.dynamic`)的地址,供动态链接器使用。 - **GOT[1]**: 存储的是重定位信息的索引,用于解析外部符号。 - **GOT[2]**: 存储的是 `_dl_runtime_resolve` 函数的地址,它是动态链接器中的核心函数,负责在运行时解析未绑定的符号。 后续的 `.got.plt` 条目则对应各个导入函数的地址,在第一次调用某个外部函数时,控制流会跳转到 PLT ,然后通过 `.got.plt` 中的占位符引导至 `_dl_runtime_resolve` 来完成实际地址的解析[^4]。 #### 2. `.got.plt` 和 PLT 的交互机制 当程序首次调用一个外部函数时,例如 `puts()`,控制流首先进入 PLT 项(如 `plt[1]`)。该 PLT 项的第一步操作通常是跳转到对应的 `.got.plt` 条目。如果该函数尚未被解析,`.got.plt` 中存储的是 PLT 中下一条指令的地址,而不是实际的函数地址。这将触发 `_dl_runtime_resolve` 函数来解析函数地址,并更新 `.got.plt` 条目以供后续直接调用。 #### 示例代码:PLTGOT 的典型汇编结构 ```assembly ; PLT[0] push DWORD PTR [rip+0x2009e2] ; GOT[1],模块标识符 jmp QWORD PTR [rip+0x2009e4] ; 跳转到 _dl_runtime_resolve ``` 在上述代码中,`GOT[1]` 提供了指向重定位信息的指针,而 `GOT[2]` 则指向 `_dl_runtime_resolve` 函数,这两个条目是动态链接的关键组成部分[^4]。 #### 3. 动态链接的延迟绑定 延迟绑定的核心思想是按需解析符号地址,避免在程序启动时解析所有外部函数,从而提升性能。`.got.plt` 在此过程中充当了缓存角色:一旦某个函数地址被解析,其值会被写入 `.got.plt` 对应的位置,之后的调用将直接跳转至实际地址,无需再次解析。 #### 4. 安全性和利用场景 在安全领域,尤其是 Pwn 类题目中,`.got.plt` 经常成为攻击目标。由于它存储了函数的实际地址,攻击者可以通过覆盖 `.got.plt` 中的条目劫持控制流。例如,通过格式化字符串漏洞或栈溢出修改 `.got.plt` 中的函数指针,使其指向恶意代码片段(如 shellcode)。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值