本文分析ELF文件在运行时动态重定向的实现。
以下面的两个程序为例:
$ cat mlib.c
int boo()
{
return 0;
}
int foo()
{
void *fn = boo;
boo();
return 0;
}
$ cat ./main.c
int boo();
int main()
{
void *fn = boo;$ cat ./main.c
int boo();
int main()
{
void *fn = boo;
boo();
}
boo();
}
$ gcc -o libmlib.so -shared -fPIC ./mlib.c
$ gcc -o main -lmlib -L. ./main.c
main函数中引用的函数boo定义在shared library中。
loader如何实现对shared library中的函数调用进行重定向
初始时,main函数中对boo的调用实际call的是boo@plt,地址为0x400560,位置处于section .plt中(参见表一)。(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400660 <+0>: push %rbp
0x0000000000400661 <+1>: mov %rsp,%rbp
=> 0x0000000000400664 <+4>: sub $0x10,%rsp
0x0000000000400668 <+8>: movq $0x400560,-0x8(%rbp)
0x0000000000400670 <+16>: mov $0x0,%eax
0x0000000000400675 <+21>: callq 0x400560 <boo@plt> <---- call boo()
0x000000000040067a <+26>: leaveq
0x000000000040067b <+27>: retq
End of assembler dump.
对boo@plt进行反汇编,可以看出第一条jmp语句跳到了下一行0x400566。0x2被push到了堆栈。0x2应该代表.rela.plt中的第三项(0为第一项),该信息将被传递给loader。
(gdb) disassemble 0x400560
Dump of assembler code for function boo@plt:
0x0000000000400560 <+0>: jmpq *0x2004f2(%rip) # 0x600a58 <boo@got.plt>
0x0000000000400566 <+6>: pushq $0x2
0x000000000040056b <+11>: jmpq 0x400530
(gdb) x /xg 0x600a58
0x600a58 <boo@got.plt>: 0x0000000000400566
push操作后,jmp到了0x400530,接着又把0x600a38中的数据push到了堆栈。注意0x600a38是.got.plt 中的第二个entry(每个entry的size是8)。最后又jmp到了0x00007ffff7def2d0(地址0x600a40中的数据)。
(gdb) x /5i 0x400530
0x400530: pushq 0x200502(%rip) # 0x600a38
0x400536: jmpq *0x200504(%rip) # 0x600a40
0x40053c: nopl 0x0(%rax)
0x400540 <__libc_start_main@plt>: jmpq *0x200502(%rip) # 0x600a48 <__libc_start_main@got.plt>
0x400546 <__libc_start_main@plt+6>: pushq $0x0
(gdb) x /xg 0x600a38
0x600a38: 0x00007ffff7ffe190
(gdb) x /xg 0x600a40
0x600a40: 0x00007ffff7def2d0
从shared library map中可以看出,0x00007ffff7def2d0对应到/lib64/ld-linux-x86-64.so.2中。loader将利用之前push的两个参数0x2和0x00007ffff7ffe190对boo@got.plt(即0x600a58)中的数