对象文件重定位表中条目的含义

时间:2018-09-07 04:51:23

标签: c linker x86-64 relocation

在理解从C源文件编译的重定位表的条目时遇到了一些问题。 我的程序如下:

tableView

我编译并使用以下命令//a.c extern int shared; int main(){ int a = 100; swap(&a, &shared); a = 200; shared = 1; swap(&a, &shared); } //b.c int shared = 1; void swap(int* a, int* b) { if (a != b) *b ^= *a ^= *b, *a ^= *b; } gcc -c -fno-stack-protector a.c b.c链接它们。 然后,我ld a.o b.o -e main -o ab检查其重定位表。

objdump -r a.o

RELOCATION RECORDS FOR [.text]: OFFSET TYPE VALUE 0000000000000014 R_X86_64_32 shared 0000000000000021 R_X86_64_PC32 swap-0x0000000000000004 000000000000002e R_X86_64_PC32 shared-0x0000000000000008 000000000000003b R_X86_64_32 shared 0000000000000048 R_X86_64_PC32 swap-0x0000000000000004 的反汇编为

a.o

我的问题是: 14处的Disassembly of section .text: 0000000000000000 <main>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 48 83 ec 10 sub $0x10,%rsp 8: c7 45 fc 64 00 00 00 movl $0x64,-0x4(%rbp) f: 48 8d 45 fc lea -0x4(%rbp),%rax 13: be 00 00 00 00 mov $0x0,%esi 18: 48 89 c7 mov %rax,%rdi 1b: b8 00 00 00 00 mov $0x0,%eax 20: e8 00 00 00 00 callq 25 <main+0x25> 25: c7 45 fc c8 00 00 00 movl $0xc8,-0x4(%rbp) 2c: c7 05 00 00 00 00 01 movl $0x1,0x0(%rip) # 36 <main+0x36> 33: 00 00 00 36: 48 8d 45 fc lea -0x4(%rbp),%rax 3a: be 00 00 00 00 mov $0x0,%esi 3f: 48 89 c7 mov %rax,%rdi 42: b8 00 00 00 00 mov $0x0,%eax 47: e8 00 00 00 00 callq 4c <main+0x4c> 4c: b8 00 00 00 00 mov $0x0,%eax 51: c9 leaveq 52: c3 retq 和2e处的shared是完全相同的对象。为什么它们具有不同的符号名称?

1 个答案:

答案 0 :(得分:2)

这是相同的地址,但重定位类型不同。重定位类型在x86-64-abi中定义。

有什么区别?

0x140x3b处:全局变量shared的地址必须移至寄存器%rsi才能调用函数swap。 / p>

但是,由于该程序是使用-mcmodel=small编译的(gcc的默认设置,另请参见this question),因此编译器可以假定地址适合32位并使用movl而不是movq(实际上,编译器会另外使用其他指令,但是将movl与“天真” movq进行比较可以很好地说明区别),这将需要更多的字节进行编码。

因此,最终的重定位为R_X86_64_32(即64位地址被截断为32bit而没有符号扩展名)而不是R_X86_64_64,即链接器将写入地址的低4个字节而不是占位符,也是4字节宽。

0x2e,您想将值1写入内存地址shared。但是,目标地址是相对于%rip给出的,即相对于0x36给出的:

movl   $0x1,0x0(%rip)  # 36 <main+0x36>

很显然,仅通过shared放置R_X86_64_32的绝对地址是没有用的-需要更复杂的计算,而这就是R_X86_64_PC32的目的。

再一次,由于代码模型较小,编译器可以假定32位相对相对偏移就足够了(因此使用了重定位R_X86_64_PC32而不是R_X86_64_PC64)和占位符只有4个字节宽。

根据x86-64-abi的数据,重新分配的公式为(第4.4节):

result = S+A-P (32bit-word, i.e. the lower 4 bytes of the result) 
S = the value of the symbol whose index resides in the relocation entry 
A = the addend used to compute the value of the relocatable field 
P = the place (section offset or address) of the storage unit being relocated (computed using r_offset)

这意味着:

  • Sshared变量的地址。
  • A-8(例如,可以通过调用readelf -r a.oobjdump -r a.o来看到),因为重定位{{ 1}}和实际的0x2e-%rip
  • 0x36是重定位的偏移量,即P0x26P-A中的地址。

如您所见,结果不是%rip,而是上面的S。在生成的二进制文件中也可以看到它-这两种不同的重定位类型将在占位符处修补不同的值。


There是Eli Bendersky撰写的有关此主题的精彩文章。