在Igor Zhirkov 的《低级编程》一书中,有一个自定义共享库函数(“libfun”)的 got/plt 示例。
请参阅下面的原始 c 代码。
在书中,他表明在反汇编中,对 libfun 的调用转到 got/plt :
0000000000400580 <libfun@plt>:
jmp QWORD PTR [rip+0x200a92] # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
push 0x0
jmp 400570 <_init+0x20>
但在我的情况下,甚至没有使用 got/plt。我直接跳到 libfun 实现,objdump -D -Mintel-mnemonic main
:
0000000000001050 <libfun@plt>:
endbr64
bnd jmp QWORD PTR [rip+0x2f75] # 3fd0 <libfun>
nop DWORD PTR [rax+rax*1+0x0]
为什么我没有任何 got/plt 用于 libfun ?
我的动态库创建有问题吗?国旗不见了?
当然,对于 printf 调用,got/plt 可以正常工作。第一次解析 printf 地址(推 0,jmp 到动态链接器...),第二次直接跳转到 printf 地址。
编码 :
主库.c:
extern void libfun( int value );
int global1 = 100;
int main( void ) {
libfun( 42 );
libfun( 24 );
return 0;
}
dynlib.c:
#include <stdio.h>
extern int global1;
void libfun(int value) {
printf( "param: %d\n", value );
printf( "global: %d\n", global1 );
}
编译和链接:
gcc -c -o mainlib.o mainlib.c -g
gcc -c -fPIC -o dynlib.o dynlib.c
gcc -o dynlib.so -shared dynlib.o
gcc -o main mainlib.o dynlib.so
编辑 :
使用类似的程序,这次在汇编中,got/plt 按预期工作:
0000000000401010 <sofun@plt>:
401010: ff 25 02 20 00 00 jmp QWORD PTR [rip+0x2002] # 403018 <sofun>
401016: 68 00 00 00 00 push 0x0
40101b: e9 e0 ff ff ff jmp 401000 <.plt>
编码 :
主.asm:
extern _GLOBAL_OFFSET_TABLE_
global _start
extern sofun
section .text
_start:
call sofun wrt ..plt
call sofun wrt ..plt ; 2nd time jumps to sofun implementation
; `exit` system call
mov rdi, 0
mov rax, 60
syscall
lib.asm:
extern _GLOBAL_OFFSET_TABLE_
global sofun:function
section .rodata
msg: db "SO function called", 10
.end:
section .text
sofun:
mov rax, 1
mov rdi, 1
lea rsi, [rel msg]
mov rdx, msg.end - msg
syscall
ret
编译:
nasm -felf64 main.asm -o main.o -g -F dwarf
nasm -felf64 lib.asm -o lib.o
ld -shared lib.o -o lib.so
ld --dynamic-linker=/lib64/ld-linux-x86-64.so.2 main.o lib.so -o main
编辑 2:
回到c程序。这次用“no-pie”编译:gcc -o main -no-pie mainlib.o dynlib.so
。
现在它起作用了。
gdb main
:
0x401143 <main+13> call 0x401040 <libfun@plt>
0x401040 <libfun@plt> endbr64
0x401044 <libfun@plt+4> bnd jmp QWORD PTR [rip+0x2fcd] # 0x404018 <libfun@got.plt>
0x401030 endbr64
0x401034 push 0x0
0x401039 bnd jmp 0x401020
0x401020 push QWORD PTR [rip+0x2fe2] # 0x404008
0x401026 bnd jmp QWORD PTR [rip+0x2fe3] # 0x404010
0x7ffff7fe7bb0 endbr64
0x7ffff7fe7bb4 push rbx
... (job of dynamic linker)
第二个 libfun 调用(直接跳转到 libfun 实现):
0x40114d <main+23> call 0x401040 <libfun@plt>
0x401040 <libfun@plt> endbr64
0x401044 <libfun@plt+4> bnd jmp QWORD PTR [rip+0x2fcd] # 0x404018 <libfun@got.plt>
0x7ffff7fc5119 <libfun> endbr64
0x7ffff7fc511d <libfun+4> push rbp
所以,主要问题是:
使用 PIE(即没有 -no-pie),编译器如何知道在第一次调用库函数时在哪里解析它?
gcc -o main mainlib.o dynlib.so
:
0x555555555156 <main+13> call 0x555555555050 <libfun@plt>
0x555555555050 <libfun@plt> endbr64
0x555555555054 <libfun@plt+4> bnd jmp QWORD PTR [rip+0x2f75] # 0x555555557fd0 <libfun@got.plt>
0x7ffff7fc5119 <libfun> endbr64
0x7ffff7fc511d <libfun+4> push rbp