【发布时间】:2012-03-13 15:11:15
【问题描述】:
我想在我的程序中获取 _GLOBAL_OFFSET_TABLE_ 的地址。一种方法是在 Linux 中使用 nm 命令,可能会将输出重定向到文件并解析该文件以获取 _GLOBAL_OFFSET_TABLE_ 的地址。但是,这种方法似乎效率很低。有哪些更有效的方法?
【问题讨论】:
我想在我的程序中获取 _GLOBAL_OFFSET_TABLE_ 的地址。一种方法是在 Linux 中使用 nm 命令,可能会将输出重定向到文件并解析该文件以获取 _GLOBAL_OFFSET_TABLE_ 的地址。但是,这种方法似乎效率很低。有哪些更有效的方法?
【问题讨论】:
这似乎有效:
// test.c
#include <stdio.h>
extern void *_GLOBAL_OFFSET_TABLE_;
int main()
{
printf("_GLOBAL_OFFSET_TABLE = %p\n", &_GLOBAL_OFFSET_TABLE_);
return 0;
}
为了获得_GLOBAL_OFFSET_TABLE_ 的一致地址,匹配nm 的结果,您需要使用-fPIE 编译您的代码以进行代码生成,就像链接到与位置无关的可执行文件一样。 (否则你会得到一个像0x2ed6 和-fno-pie -no-pie 这样的小整数)。大多数现代 Linux 发行版的 GCC 默认值为 -fPIE -pie,这将使 nm 地址只是相对于映像库的偏移量,并且运行时地址是 ASLRed。 (这通常有利于安全,但您可能不想要它。)
$: gcc -fPIE -no-pie test.c -o test
它给出:
$ ./test
_GLOBAL_OFFSET_TABLE = 0x6006d0
但是,nm 的想法不同:
$ nm test | fgrep GLOBAL
0000000000600868 d _GLOBAL_OFFSET_TABLE_
或者如果 GCC 太老了,根本不知道 PIE,更不用说将 -fPIE -pie 作为默认值,-fpic 可以工作。
【讨论】:
nm给出不同的值?
DYNAMIC 的指针,而不是 GOT 的地址。要打印 GOT 的地址,您应该使用 &_GLOBAL_OFFSET_TABLE_。
-fPIE -pie 已经是默认设置,所以我想我应该调整一些。我可以用-fno-pie -no-pie 确认奇怪之处,比如打印出0x2ed6 用于64 位模式,或者0x2e76 用于-m32。我不完全理解,所以你能看看我的编辑,确保我没有破坏你提出的任何重要观点吗?
如果你使用汇编语言,你可以得到_GLOBAL_OFFSET_TABLE_地址而不用get_pc_thunk。
这是一种棘手的方式。 :)
这是示例代码:
$ cat test.s
.global main
main:
lea HEREIS, %eax # Now %eax holds address of _GLOBAL_OFFSET_TABLE_
.section .got
HEREIS:
$ gcc -o test test.s
这是可用的,因为.got 部分与<.got.plt> 相邻
因此符号HEREIS 和_GLOBAL_OFFSET_TABLE_ 位于同一地址。
PS。您可以使用 objdump 检查它是否有效。
Disassembly of section .got:
080495e8 <HEREIS-0x4>:
80495e8: 00 00 add %al,(%eax)
...
Disassembly of section .got.plt:
080495ec <_GLOBAL_OFFSET_TABLE_>:
80495ec: 00 95 04 08 00 00 add %dl,0x804(%ebp)
80495f2: 00 00 add %al,(%eax)
80495f4: 00 00 add %al,(%eax)
【讨论】:
lea HEREIS(%rip), %rsi / ... / 调用 printf@plt。或者只是_GLOBAL_OFFSET_TABLE_(%rip)。但是,如果您打算使用 32 位绝对地址,则始终首选 mov $HEREIS, %eax。 LEA 对静态地址没有意义,除了 RIP-relative。 (更大的代码大小可以用 [disp32] 寻址模式做同样的事情)