为什么 nasm 带有对 _GLOBAL_OFFSET_TABLE_ 的非限定引用的程序集显然可以作为 PIC 进行组装和链接?
Why can nasm assembly with unqualified references to _GLOBAL_OFFSET_TABLE_ apparently be assembled and linked as PIC?
为什么我可以 assemble 和 link get_got.asm
作为位置无关的代码,当它包含对其 GOT 的绝对地址的引用时?
get_got.asm
extern _GLOBAL_OFFSET_TABLE_
section .text
global get_got
get_got:
mov rax, _GLOBAL_OFFSET_TABLE_
ret
main.c
#include <stdio.h>
void* get_got(void);
int main(int argc, char* argv[]) {
printf("%p\n", get_got());
}
组装、编译、linking、运行:
nasm -felf64 -o get_got.o get_got.asm
gcc -fPIC -shared -o get_got.so get_got.o
gcc -Wl,-rpath=$ORIGIN -o main main.c get_got.so
./main
0x148ba1cba000
这是怎么回事?在我看来,get_got.so 以某种方式具有 GOT 的内置绝对地址,直到运行时才会有已知地址。反汇编 get_got.so 表明 mov 实际上包含立即数 (0x201000)。显然我对某些事情有很大的误解。我预计这会导致 nasm 生成 link 人会窒息的重定位。
我使用 lea rax, [rel _GLOBAL_OFFSET_TABLE_]
.
构建了您的代码和修改版本
我比较了 readelf -a
输出。但是,来自不同地址的噪音很多。
readelf -a get_got.so | diff -u - <(readelf -a get_got_rel.so) | less
最有趣的区别是:
--- readelf -a get_got.so
+++ readelf -a get_got_rel.so
....
-Dynamic section at offset 0xe40 contains 22 entries:
+Dynamic section at offset 0xe50 contains 21 entries:
...
- 0x0000000000000016 (TEXTREL) 0x0
0x000000006ffffffe (VERNEED) 0x3b0
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x398
- 0x000000006ffffff9 (RELACOUNT) 4
+ 0x000000006ffffff9 (RELACOUNT) 3
所以绝对版有文字重定位。我不知道 Linux / ELF 动态 linking 可以在映射共享库后应用修正。但显然可以。 (最好不要,因为它会弄脏内存页,因此它不再仅由磁盘上的文件支持。)
但我检查了 GDB,这就是发生的事情:在 get_got
和 运行 中设置一个断点:
(gdb) disas
Dump of assembler code for function get_got:
=> 0x00007f9e77b235b0 <+0>: movabs rax,0x7f9e77d24000
0x00007f9e77b235ba <+10>: ret
objdump -dRC -Mintel get_got.so
:(注意没有 -w
的换行):
00000000000005b0 <get_got>:
5b0: 48 b8 00 10 20 00 00 movabs rax,0x201000
5b7: 00 00 00
5b2: R_X86_64_RELATIVE *ABS*+0x201000
5ba: c3 ret
感谢@Jester 的 -R
提示;我通常使用 objdump -dr ...
,而不是 -R
,小写的 r 不会为 .so
.
打印任何重定位
在 get_got.o
上,-r
显示 movabs rax,0x0 2: R_X86_64_64 _GLOBAL_OFFSET_TABLE_
。
gcc -nostdlib -pie
也将 link 64 位绝对重定位到 PIE 可执行文件中。 (PIE 可执行文件 是 ELF 共享对象)。
PIC / PIE 中不允许的是 32 位绝对重定位:32-bit absolute addresses no longer allowed in x86-64 Linux?。您收到 linker 错误。 array[rcx*4]
等寻址模式在 PIC/PIE 代码中不可用,您需要单独的指令将地址放入寄存器。
lea rdi, [rel array]
比 64 位立即绝对值 much 更好的选择,因为它更小并且对 uop 缓存更友好,并且不需要加载时修正。
为什么我可以 assemble 和 link get_got.asm
作为位置无关的代码,当它包含对其 GOT 的绝对地址的引用时?
get_got.asm
extern _GLOBAL_OFFSET_TABLE_
section .text
global get_got
get_got:
mov rax, _GLOBAL_OFFSET_TABLE_
ret
main.c
#include <stdio.h>
void* get_got(void);
int main(int argc, char* argv[]) {
printf("%p\n", get_got());
}
组装、编译、linking、运行:
nasm -felf64 -o get_got.o get_got.asm
gcc -fPIC -shared -o get_got.so get_got.o
gcc -Wl,-rpath=$ORIGIN -o main main.c get_got.so
./main
0x148ba1cba000
这是怎么回事?在我看来,get_got.so 以某种方式具有 GOT 的内置绝对地址,直到运行时才会有已知地址。反汇编 get_got.so 表明 mov 实际上包含立即数 (0x201000)。显然我对某些事情有很大的误解。我预计这会导致 nasm 生成 link 人会窒息的重定位。
我使用 lea rax, [rel _GLOBAL_OFFSET_TABLE_]
.
我比较了 readelf -a
输出。但是,来自不同地址的噪音很多。
readelf -a get_got.so | diff -u - <(readelf -a get_got_rel.so) | less
最有趣的区别是:
--- readelf -a get_got.so
+++ readelf -a get_got_rel.so
....
-Dynamic section at offset 0xe40 contains 22 entries:
+Dynamic section at offset 0xe50 contains 21 entries:
...
- 0x0000000000000016 (TEXTREL) 0x0
0x000000006ffffffe (VERNEED) 0x3b0
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x398
- 0x000000006ffffff9 (RELACOUNT) 4
+ 0x000000006ffffff9 (RELACOUNT) 3
所以绝对版有文字重定位。我不知道 Linux / ELF 动态 linking 可以在映射共享库后应用修正。但显然可以。 (最好不要,因为它会弄脏内存页,因此它不再仅由磁盘上的文件支持。)
但我检查了 GDB,这就是发生的事情:在 get_got
和 运行 中设置一个断点:
(gdb) disas
Dump of assembler code for function get_got:
=> 0x00007f9e77b235b0 <+0>: movabs rax,0x7f9e77d24000
0x00007f9e77b235ba <+10>: ret
objdump -dRC -Mintel get_got.so
:(注意没有 -w
的换行):
00000000000005b0 <get_got>:
5b0: 48 b8 00 10 20 00 00 movabs rax,0x201000
5b7: 00 00 00
5b2: R_X86_64_RELATIVE *ABS*+0x201000
5ba: c3 ret
感谢@Jester 的 -R
提示;我通常使用 objdump -dr ...
,而不是 -R
,小写的 r 不会为 .so
.
打印任何重定位
在 get_got.o
上,-r
显示 movabs rax,0x0 2: R_X86_64_64 _GLOBAL_OFFSET_TABLE_
。
gcc -nostdlib -pie
也将 link 64 位绝对重定位到 PIE 可执行文件中。 (PIE 可执行文件 是 ELF 共享对象)。
PIC / PIE 中不允许的是 32 位绝对重定位:32-bit absolute addresses no longer allowed in x86-64 Linux?。您收到 linker 错误。 array[rcx*4]
等寻址模式在 PIC/PIE 代码中不可用,您需要单独的指令将地址放入寄存器。
lea rdi, [rel array]
比 64 位立即绝对值 much 更好的选择,因为它更小并且对 uop 缓存更友好,并且不需要加载时修正。