ELF 文件 动态链接 - 地址无关代码(GOT)

Linux 系统中,ELF动态链接文件被称为 动态共享对象(DSODynamic Shared Object),简称共享对象

文件拓展名为“.so”

 

动态链接下 一个程序可以被分成若干个文件:程序的主要部分 - 可执行文件 和 程序所依赖的共享对象(一个或多个.so文件),它们都可称作为程序的模块。

动态链接文件(共享对象)的装载地址为0x00000000;这并非工作时的实际地址,实际地址由装载器根据当前进程地址空间的空闲情况来动态分配一块足够大的虚拟地址空间给共享对象。

 

装载时重定位

基本思路:在链接时对所有的绝对地址的引用不作重定位,而把这一步放在装载时完成。一旦模块装载完成,既目标地址确定,那么系统将对程序中的所有绝对地址的引用进行重定位。

 

静态链接时的重定位称为 链接时重定位(Link Time Relocation)

动态链接时的重定位称为 装载时重定位(Load Time Relocation) gcc -shared

 

然而光有装载时重定位也不能解决所有问题,因为对于动态链接文件来讲,它时可以分为可修改数据部分和不可修改数据部分,如果只有装载重定位,那每个程序必须都有一个共享对象的副本,这样会很浪费内存

这样就引入来下一个话题

 

地址无关代码 (gcc -shared -fPIC)

基本思想: 把指令中需要被修改的部分分离出来,跟数据部分放在一起,这样指令部分就保持不变了,而数据部分为每个进程都有一个副本,

这就是地址无关代码(PIC, Position-independent Code)技术

 

共享对象模块内的地址引用分为四种情况:

1. 模块内部调用或跳转:

    它们之间位置固定,使用相对地址调用。

2. 模块内部数据访问

    同样使用相对寻址(使用当前(指令地址)PC + 偏移量)

3. 模块与模块之间的数据访问(重点)

    模块之间的数据访问,其目标地址需要等到装载后才能确定。

    这里的基本思想时将这部分与地址相关的指令的当前地址放入到数据段里面。

    ELF 的做法是在数据段中建立了一个 指向这些变量的指针数组 ,也被称为 全局偏移表(GOT, Global Offset Table),当代码需要引用该全局变量时,通过GOT间接引用,

    即GOT中记录着该外部函数真正的地址,装载器在做动态链接时,会查找每个外部符号的地址,然后填充到GOT的对应的项中。

 

    GOT 如何做到与指令无关的呢?(在.so 文件中对应 .got 段)

    1. 模块在编译期可以确定模块内部变量相对与当前指令的偏移,同样在编译期也可以确定GOT相对于当前指令的偏移。确定GOT的位置和确定内部变量的位置方法上时一样的,通过

     得到PC值然后加上一个偏移量即可得到GOT的位置。当然GOT中的每个地址对应于哪个符号(变量,函数都是符号)由编译期决定。

 

4. 模块间跳用和跳转

    此时与3.中方法类似,只是对应的GOT的位置保存的时目标函数的地址,通过GOT实现间接跳转

 

地址无关小结:

 

                    各种引用方式
 指令跳转和调用 数据访问
模块内部 相对跳转调用 相对访问
模块外部GOT间接跳转访问GOT间接跳转访问

 

转载于:https://www.cnblogs.com/gradyblog/p/8963785.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了解析ELF文件并获取链接脚本,您可以使用类似libelf的库。Libelf是一种开放源代码库,可用于访问ELF文件的内容。以下是一个简短的C程序示例,该程序使用libelf库解析ELF文件并获取链接脚本: ``` #include <fcntl.h> #include <libelf.h> #include <stdio.h> int main() { //打开ELF文件 int fd = open("path/to/elf", O_RDONLY, 0); if (fd < 0) { perror("open failed"); return 1; } //加载ELF文件 Elf *e = elf_begin(fd, ELF_C_READ, NULL); if (e == NULL) { perror("elf_begin failed"); return 1; } //获取链接脚本的字符串节 size_t shstrndx; if (elf_getshdrstrndx(e, &shstrndx) != 0) { perror("elf_getshdrstrndx failed"); return 1; } Elf_Scn *scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { //获取节头 GElf_Shdr shdr; if (gelf_getshdr(scn, &shdr) != &shdr) { perror("gelf_getshdr failed"); return 1; } //获取节名 char *sec_name = elf_strptr(e, shstrndx, shdr.sh_name); if (sec_name == NULL) { perror("elf_strptr failed"); return 1; } //如果是链接脚本节 if (shdr.sh_type == SHT_PROGBITS && strcmp(sec_name, ".linker_script") == 0) { //获取数据 char *data = (char*)malloc(shdr.sh_size); if (data == NULL) { perror("malloc failed"); return 1; } if (gelf_getdata(scn, data, shdr.sh_size, shdr.sh_offset) == NULL) { perror("gelf_getdata failed"); return 1; } //打印链接脚本数据 printf("%s\n", data); free(data); } } //释放ELF 文件 elf_end(e); close(fd); return 0; } ``` 请注意,这只是一个简单的示例程序。实际上,解析ELF文件可能会更复杂,取决于所需的信息类型。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值