我现在通过下面的过程链接表了解如何引用动态函数:
Dump of assembler code for function foo@plt:
0x0000000000400528 <foo@plt+0>: jmpq *0x2004d2(%rip) # 0x600a00 <_GLOBAL_OFFSET_TABLE_+40>
0x000000000040052e <foo@plt+6>: pushq $0x2
0x0000000000400533 <foo@plt+11>: jmpq 0x4004f8
(gdb) disas 0x4004f8
No function contains specified address.
但是我不知道动态变量是如何被引用的,虽然我发现这些值一旦启动就在GOT中填充,但是没有像上面那样的存根,它是如何工作的?
答案 0 :(得分:1)
动态加载程序在将控制转移到用户程序之前重定位所有对变量的引用。
它们没有“存根”,因为一旦用户程序开始执行,加载程序就无法重新获得控制权并更新变量地址。如果您不清楚这一点,那么您还没有真正了解PLT延迟解析存根的工作原理。
答案 1 :(得分:0)
通过全局偏移表间接访问全局变量。
要查看此操作,请考虑以下代码片段。
int v1;
int f(void) { return !v1; }
函数f
引用全局v1
。机器码生成
该函数如下所示(在i386上):
% gcc -c -fpic a.c
% objdump --disassemble --reloc a.o
[snip]
Disassembly of section .text:
00000000 <f>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: e8 fc ff ff ff call 4 <f+0x4>
4: R_386_PC32 __i686.get_pc_thunk.cx
8: 81 c1 02 00 00 00 add $0x2,%ecx
a: R_386_GOTPC _GLOBAL_OFFSET_TABLE_
e: 8b 81 00 00 00 00 mov 0x0(%ecx),%eax
10: R_386_GOT32 v1
14: 8b 00 mov (%eax),%eax
16: 85 c0 test %eax,%eax
18: 0f 94 c0 sete %al
1b: 0f b6 c0 movzbl %al,%eax
1e: 5d pop %ebp
1f: c3 ret
Disassembly of section .text.__i686.get_pc_thunk.cx:
00000000 <__i686.get_pc_thunk.cx>:
0: 8b 0c 24 mov (%esp),%ecx
3: c3 ret
机器代码演练:
__i686.get_pc_thunk.cx
的调用准备
通过加载指令的地址来进行PC相对寻址
在致电注册%ecx
。%ecx
中的值被调整为指向开始
全局偏移表的。这种调整由信号发出信号
类型R_386_GOTPC
的重定位条目。v1
的地址。该
R_386_GOT32
重定位提供v1
条目的偏移量
全局偏移表的基础。v1
中的值被检索到寄存器%eax
。f
的其余计算。在最终的共享对象中,链接器将函数的代码修补为 以下内容:
% gcc -shared -o a.so a.o
% objdump --disassemble a.so
...snip...
0000044c <f>:
44c: 55 push %ebp
44d: 89 e5 mov %esp,%ebp
44f: e8 18 00 00 00 call 46c <__i686.get_pc_thunk.cx>
454: 81 c1 a0 1b 00 00 add $0x1ba0,%ecx
45a: 8b 81 f8 ff ff ff mov -0x8(%ecx),%eax
460: 8b 00 mov (%eax),%eax
462: 85 c0 test %eax,%eax
...snip...
%ecx
。%ecx
中减去8
获取全局偏移表中v1
的槽位地址,
即,v1
的时隙距离开始时的偏移量为0x1FEC
共享对象。查看共享对象的动态重定位记录,我们
查看指示运行时加载程序存储的重定位记录
偏移量为0x1FEC的v1
的实际地址。
% objdump -R a.so
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
...snip...
00001fec R_386_GLOB_DAT v1
...snip...
进一步阅读: