gpt4 book ai didi

c - 目标文件重定位表中条目的含义

转载 作者:行者123 更新时间:2023-12-03 14:39:07 25 4
gpt4 key购买 nike

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

//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.cld 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的拆解是
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

我的问题是: shared在 14 和 shared在 2e 处是完全相同的对象。为什么它们有不同的符号名称?

最佳答案

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

有什么不同?

0x140x3b : 全局变量地址shared必须移到注册 %rsi为了调用函数swap .

但是,因为程序是用 -mcmodel=small 编译的。 (gcc 的默认值,另见 this question ),编译器可以假设该地址适合 32 位并使用 movl而不是 movq (实际上编译器会使用其他指令,但是比较 movl 与“天真”movq 很好地解释了差异),这需要更多的字节进行编码。

因此,得到的重定位是R_X86_64_32 (即 64 位地址被截断为没有符号扩展的 32 位)而不是 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 位 rip 相对偏移量就足够了(因此使用重定位 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 可以看到),因为重定位的偏移量 0x2e 之间存在 8 个字节的差异和实际的 %rip - 0x36 .
  • P是重定位的偏移量,即0x26 . P-A%rip 中的地址.

  • 如您所见,结果不是 SR_X86_64_32 的情况上面,但是 S - (P-A) .在生成的二进制文件中也可以看到 - 不同的值将在这两种不同重定位类型的占位符处修补。

    There是 Eli Bendersky 关于这个主题的一篇很棒的文章。

    关于c - 目标文件重定位表中条目的含义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52215495/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com