【发布时间】:2019-07-10 17:54:33
【问题描述】:
C 源代码:
int sum(int a, int b) {
return a + b;
}
int main() {
int (*ptr_sum_1)(int,int) = sum; // assign the address of the "sum"
int (*ptr_sum_2)(int,int) = sum; // to the function pointer
int (*ptr_sum_3)(int,int) = sum;
int a = (*ptr_sum_1)(2,4); // call the "sum" through the pointer
int b = sum(2,4); // call the "sum" by usual way
return 0;
}
汇编代码的关键部分:
lea rax, sum[rip]
mov QWORD PTR -24[rbp], rax
lea rax, sum[rip]
mov QWORD PTR -16[rbp], rax
lea rax, sum[rip]
mov QWORD PTR -8[rbp], rax
来自 GDB 的执行程序指令:
0x5fa <sum>: push rbp
0x5fb <sum+1>: mov rbp,rsp
0x5fe <sum+4>: mov DWORD PTR [rbp-0x4],edi
0x601 <sum+7>: mov DWORD PTR [rbp-0x8],esi
0x604 <sum+10>: mov edx,DWORD PTR [rbp-0x4]
0x607 <sum+13>: mov eax,DWORD PTR [rbp-0x8]
0x60a <sum+16>: add eax,edx
0x60c <sum+18>: pop rbp
0x60d <sum+19>: ret
0x60e <main>: push rbp
0x60f <main+1>: mov rbp,rsp
0x612 <main+4>: sub rsp,0x20
0x616 <main+8>: lea rax,[rip+0xffffffffffffffdd] # 0x5fa <sum>
0x61d <main+15>: mov QWORD PTR [rbp-0x18],rax
0x621 <main+19>: lea rax,[rip+0xffffffffffffffd2] # 0x5fa <sum>
0x628 <main+26>: mov QWORD PTR [rbp-0x10],rax
0x62c <main+30>: lea rax,[rip+0xffffffffffffffc7] # 0x5fa <sum>
0x633 <main+37>: mov QWORD PTR [rbp-0x8],rax
0x637 <main+41>: mov rax,QWORD PTR [rbp-0x18]
0x63b <main+45>: mov esi,0x4
0x640 <main+50>: mov edi,0x2
0x645 <main+55>: call rax
0x647 <main+57>: mov DWORD PTR [rbp-0x20],eax
0x64a <main+60>: mov esi,0x4
0x64f <main+65>: mov edi,0x2
0x654 <main+70>: call 0x5fa <sum>
0x659 <main+75>: mov DWORD PTR [rbp-0x1c],eax
0x65c <main+78>: mov eax,0x0
0x661 <main+83>: leave
0x662 <main+84>: ret
我认为sum标签只是sum过程的起始地址-0x5fa,所以我不明白为什么gcc不能直接使用,而是使用计算@987654329 @为此。
问题:
- 为什么在汇编的
lea rax, sum[rip]指令中使用sum[rip],而不是简单的sum标签,例如lea rax, sum? -
mov rax, 0x5fa指令是否也会这样做?因为我们知道链接后的sum地址:call 0x5fa <sum>指令直接使用就行了。
【问题讨论】:
-
GDB 正在执行的程序指令: 不,它还没有执行。您从 GDB 内部反汇编了可执行文件,但地址是映像库的偏移量(直到进程开始才确定)。 在
start命令之后,您会看到0x5555555546aa之类的地址,用于main。此地址不是链接时间常数,因此它不能用作mov的 32 位立即数。 (此外,它不适合 32 位,而是位置相关的可执行文件 do 中的静态地址,在 Linux 上。) -
@PeterCordes 你说得对,我刚刚做了
x /30i sum,没有start。并且想知道为什么所有地址都这么短,因为通常它们看起来像0x5555555545fa,正如你所说:) -
@PeterCordes “此地址不是链接时间常数,因此不能用作 mov 的 32 位立即数。”但是为什么这条指令使用它作为常量:
0x654 <main+70>: call 0x5fa <sum>? -
near call/jmp 使用
rel32编码(felixcloutier.com/x86/CALL.html),两个静态地址之间的距离是链接时间常数。 (或汇编时,用于来自同一源文件的位置)。 GDB 的反汇编程序会填写绝对地址,因为这样更有用,但是如果您查看 hexdump,您会看到相对编码。在 GDB 中使用disas /r,或者使用objdump -d。