Linux动态链接懒加载

Linux动态链接懒加载

懒加载

最近了解到一种技术——懒加载,这是一种与动态链接相关的技术,我对它有点感兴趣,于是决定深入了解一番。

懒加载是一种延迟加载资源的策略,它将资源的加载推迟到在首次访问或需要时才执行。这意味着在应用程序启动时,并不会立即加载和链接所有可能需要的库或模块,而是等到应用程序执行到实际需要使用它们的代码路径时才加载和链接。这可以减少启动时间和内存占用,因为应用程序只加载了实际需要的部分。

比如,以下面的程序为例。当程序刚运行到main函数时,got中还没有printf函数的地址。当程序要调用printf函数的时候,才会将printf的地址写入got中

#include <stdio.h>

int main() {
    printf("Hello World!\n");
    return 0;
}

我听说Linux中默认采用懒加载,就将上面的程序编译后进行调试。

gcc main.c -o main -g
gdb main

main函数反汇编

printf函数参数只有字符串,此时编译器会将printf函数替换为puts函数。因为采用的是动态链接,要使用puts函数,会先跳转到plt,再从plt跳转到got中的地址。

先不急着运行,我们来看看got中的数据。

got数据

好奇怪,got中已经有了puts的地址。说好的懒加载呢?

那么got中什么时候被写入了puts的地址呢?

got数据被修改

我重新调试一次。starti命令会开始执行程序,并在第一条指令处停下来。0x555555557fd0是got中puts的地址,一开始的值是0x1030。watch指令在检测到地址中的值改变后会停止运行。

这里可以看到,程序在…/sysdeps/x86_64/dl-machine.h中停了下来。程序现在还处于/lib64/ld-linux-x86-64.so.2中,这是linux的动态链接器,负责动态库加载,符号解析和重定位等。

从上面的调试过程可以看出,linux的动态链接并不是默认采用懒加载

那怎么才能让linux动态链接的时候采用懒加载呢?需要在编译的时候加上特别的参数。

gcc main.c -o main -g -Wl,-z,lazy

这样就可以让linux以懒加载的方式解析符号地址。-z,lazy是把lazy参数传递给链接器。从这里我们可以看出,是否采用懒加载是由链接器决定的

重新调试,得到下面的结果。

懒加载got

可以看到got中puts的地址还未被修改成正确的地址。那么这个0x0000555555555030又是什么呢。

plt

objdump plt

上面的图是通过objdump得到的数据,0x0000555555555030对应的是0x1030,0x555555555020对应的是0x1020。

我们的跳转顺序为 main -> puts@plt -> .plt。0x555555555026会跳转到动态链接器,由动态链接器完成动态库加载和符号解析,并把解析后的地址填入got中。

这就是整个懒加载的过程了。

RELRO

RELRO(Read-Only Relocations)是一种用于加强可执行文件的安全性的机制,特别是针对共享库(动态链接库)的。它的主要目的是防止一些常见的攻击技巧,例如覆盖全局偏移表(GOT)和过程链接表(PLT)的攻击。

RELRO 的作用和原理:

  1. Partial RELRO(PRelRO):这是 RELRO 的一种模式,它将 GOT 表中的一部分条目标记为只读。在程序启动后,这些条目将被标记为只读,从而保护它们免受任何修改。这提供了一定程度的保护,但仍然允许一些符号的重定位,这些符号可能需要在运行时解析。
  2. Full RELRO(FRelRO):这是 RELRO 的更强版本,它将 GOT 表中的所有条目都标记为只读。这意味着在程序启动后,所有 GOT 表中的符号都无法被修改。这提供了更高级别的保护,但可能导致一些特定情况下的性能损失,因为某些符号可能需要在运行时解析,但它们现在无法被修改。

我们怎么知道一个可行性文件是Partial RELRO还是Full RELRO?可以通过checksec来查看。

sudo apt install checksec

checksec

可以看到懒加载是Partial RELRO,非懒加载是Full RELRO。

如果不采用懒加载,在程序正式运行前,会由动态链接器ld.so进行动态库加载和符号解析,并把dynamic和got等段设置为只读。

如果采用懒加载,只会将dynamic和部分got设置为只读,另一部分got设置为可写。

那么这种机制是如何实现的呢?我们需要通过objdump查看可执行文件的段偏移来理解。

非懒加载got

懒加载got

可以看到,在懒加载中,有两个got段:.got和.got.plt。.got的内容会在程序运行前被修改,然后会把从rodata到got的读写权限设置为只读。另外,.got.plt的起始地址是4K对齐的,.got.plt和.data在一个页面中,它们的读写权限也一样,都是可读可写。在运行过程中,动态链接器会动态解析符号地址并填入.got.plt中。

而非懒加载中,.got的内容会在程序运行前被修改,然后会把从rodata到got的权限设置为只读。

本篇博客主要讲了如何让可执行文件以懒加载的方式执行,懒加载的大致流程,RELRO的原理。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值