关于 x86_32 的两种重定位修正方式
宏定义 | 值 | 重定位修正方式 |
---|---|---|
R_386_32 | 1 | 绝对寻址修正 S + A |
R_386_PC32 | 2 | 相对寻址修正 S + A - P |
关于 S
A
P
说明:
S
:符号的实际地址A
:保存在修正位置上的值(一般为0,或 -4)P
:修正位置的地址
更多重定位类型 点这里
绝对寻址修正
符号地址直接计算,直接存放在修正位置。比如,mov 指令获取全局变量的值。
0300:
gval dw 0x12345678
1011: c7 45 f8 64 00 00 00 movl $0x64, 0xfffffff8(%esp)
1018: c7 44 24 04 00 00 00 00 movl $0, 0x4(%esp) # movl gval, 0x(%esp)
1020: 8d 45 f8 lea 0xfffffff8(%ebp), %eax
这段代码中,1018 有一个 R_386_32
类型的重定位,它的信息如下:
信息 | 说明 |
---|---|
修正位置 | 101D = 1018 + 5 |
修正位置的长度 | 4 个字节 |
S | 300 |
A | 0,一般就是 0 |
P | 101D |
最终修正为 | 300 = 300 + 0 |
1018: c7 44 24 04 00 03 00 00 movl $300, 0x4(%esp) # movl gval, 0x(%esp)
相对寻址修正
修正位置上的符号地址,是符号相对于 PC 的偏移。比如:call 指令调用子程序。
1023: 8d 45 f8 lea 0xfffffff8(%ebp), %eax
1026: e8 fc ff ff ff call -4 # call foo
102b: 89 04 24 mov %eax, (%esp)
1234:
foo:
...
...
...
ret
这段代码中,1026 有一个 R_386_PC32
类型的重定位,它的信息如下:
信息 | 说明 |
---|---|
修正位置 | 1027 = 1026 + 1 |
修正位置的长度 | 4 个字节 |
S | 1234 |
A | -4,一般就是 0xfffffffc(即 -4) |
P | 1027 |
最终修正为 | 209 = 1234 + (-4) - 1027 |
1026: e8 09 02 00 00 call -4 # call foo
这里 call 指令是一个偏移值,符号相对于修正位置的偏移,那为什么需要减 4 呢。因为,当 CPU 执行 call 指令时,PC 值已经是下一条指令地址了,即:当 CPU 执行 1026 时,PC 寄存器是 102b,需要 PC - 4 调整为修正位置的地址,再加上 call 指令后的偏移 209,就将 PC 调整到 foo 函数处了。