首页 > 解决方案 > linux进程中的多个全局偏移表

问题描述

我正在检查正在运行的进程的内存布局并进行了有趣的观察。似乎有多个 GOT(全局偏移表)。这是我在研究 malloc 函数时在调试器中看到的内容:

(gdb) p (void *) 0x7ff5806ae020
$5 = (void *) 0x7ff5806ae020 <malloc@got.plt>
(gdb) p (void *) 0x7ff5806471d0
$6 = (void *) 0x7ff5806471d0 <malloc@got.plt>
(gdb) p (void *) 0x5634ef446030
$7 = (void *) 0x5634ef446030 <malloc@got.plt>

我检查了malloc蹦床的 3 个不同地址。当我查看进程的内存映射时,这些地址对应于以下条目:

7ff580647000-7ff580648000 rw-p 0001c000 fd:01 547076                     /lib/x86_64-linux-gnu/libpthread-2.31.so
5634ef446000-5634ef447000 rw-p 00003000 fd:02 12248955                   /home/user/binary
7ff5806ae000-7ff5806af000 rw-p 0002a000 fd:01 523810                     /lib/x86_64-linux-gnu/ld-2.31.so

我看到不同的条目对应于不同的“可链接对象”:二进制和两个动态库。

此外,三分之二的蹦床指向实际功能。并且两个指针都是一样的。第三个蹦床指向存根。

(gdb) p *(void **) 0x5634ef446030
$8 = (void *) 0x7ff5804ef1b0 <__GI___libc_malloc>
(gdb) p *(void **) 0x7ff5806471d0
$9 = (void *) 0x7ff580631396 <malloc@plt+6>
(gdb) p *(void **) 0x7ff5806ae020
$10 = (void *) 0x7ff5804ef1b0 <__GI___libc_malloc>

真的需要三个蹦床吗?如果是,那为什么?

标签: linuxlinker

解决方案


我意识到这样的系统是实现蹦床的唯一明智方法。

在汇编中,对动态链接函数的每条调用指令基本上是指该函数在 GOT 中的索引。索引直接编码在指令中。因此,静态链接时必须知道索引是最新的。否则,程序代码必须在程序每次启动时由动态链接器更新。显然,非常繁琐的任务。

此外,每个库都是单独编译的,因此不能依赖其他库,包括它们的确切 GOT 布局。如果只有一个 GOT,那么一起加载的所有库必须以某种方式就 GOT 中每个条目的含义达成一致。拥有一个由所有库一起填充的共享数据结构(GOT)几乎肯定会产生这种依赖关系。

例如,readelf 说 .so-files 也有表:

$ readelf -S /lib/ld-linux.so.2 
   [18] .got              PROGBITS        00029ff4 028ff4 000008 04  WA  0   0  4
   [19] .got.plt          PROGBITS        0002a000 029000 000028 04  WA  0   0  4
$ readelf -S /usr/lib/libpurple.so.0.13.0
   [21] .got              PROGBITS         0000000000137318  00136318
   0000000000003cd8  0000000000000008  WA       0     0     8

虽然, libpurple 没有.got.plt,我不完全理解。

我的困惑来自一个事实,即该表被称为“全球”。与编译模块(.o 文件)相比,“全局”一词实际上意味着该表在可链接对象级别是全局的。

其次,我有一种错觉,认为 GOT 指的是可执行应用程序,而不是任何可动态链接的对象。


推荐阅读