【发布时间】:2012-05-25 22:13:15
【问题描述】:
System V AMD64 ABI call convention 是这样规定的:
注册
%rbp、%rbx和%r12到%r15“属于”呼叫 函数和被调用函数需要保存它们的值。 换句话说,被调用的函数必须保留这些寄存器' 调用者的值。剩余的寄存器“属于”被调用 功能。如果调用函数想要保留这样的寄存器 跨函数调用的值,它必须将值保存在其本地 堆栈帧。
例如,给定以下代码:
void f1(const int i, const double j, const char * k)
{
printf("i = %d\n", i);
printf("j = %g\n", j);
printf("k = %s\n", k);
}
程序集表示为:
f1():
4004f4: 55 push %rbp
4004f5: 48 89 e5 mov %rsp,%rbp
4004f8: 48 83 ec 20 sub $0x20,%rsp
4004fc: 89 7d fc mov %edi,-0x4(%rbp)
4004ff: f2 0f 11 45 f0 movsd %xmm0,-0x10(%rbp)
400504: 48 89 75 e8 mov %rsi,-0x18(%rbp)
400508: b8 70 06 40 00 mov $0x400670,%eax
40050d: 8b 55 fc mov -0x4(%rbp),%edx
400510: 89 d6 mov %edx,%esi
400512: 48 89 c7 mov %rax,%rdi
400515: b8 00 00 00 00 mov $0x0,%eax
40051a: e8 d1 fe ff ff callq 4003f0 <printf@plt>
40051f: b8 78 06 40 00 mov $0x400678,%eax
400524: f2 0f 10 45 f0 movsd -0x10(%rbp),%xmm0
400529: 48 89 c7 mov %rax,%rdi
40052c: b8 01 00 00 00 mov $0x1,%eax
400531: e8 ba fe ff ff callq 4003f0 <printf@plt>
400536: b8 80 06 40 00 mov $0x400680,%eax
40053b: 48 8b 55 e8 mov -0x18(%rbp),%rdx
40053f: 48 89 d6 mov %rdx,%rsi
400542: 48 89 c7 mov %rax,%rdi
400545: b8 00 00 00 00 mov $0x0,%eax
40054a: e8 a1 fe ff ff callq 4003f0 <printf@plt>
40054f: c9 leaveq
400550: c3 retq
在本例中,参数已传入%edi、%xmm0 和%rsi。调用约定规定这些寄存器“属于”被调用函数,这意味着f1 没有义务保留它们的值。事实上,%edi、%xmm0 和 %rsi 都被丢弃在以下几行:
400510: 89 d6 mov %edx,%esi
400512: 48 89 c7 mov %rax,%rdi
..
400524: f2 0f 10 45 f0 movsd -0x10(%rbp),%xmm0
我想保留所有参数寄存器。文档说明该值可以保存在我尝试过的本地堆栈中,如下所示:
void f1(const int i, const double j, const char * k)
{
uint32_t edi;
__asm ("movl %%edi, %0;" : "=r" ( edi ));
printf("i = %d\n", i);
printf("j = %g\n", j);
printf("k = %s\n", k);
__asm ("movl %0, %%edi;" : "=d"( edi ));
}
这会生成以下内容:
f1():
4004f4: 55 push %rbp
4004f5: 48 89 e5 mov %rsp,%rbp
4004f8: 53 push %rbx
4004f9: 48 83 ec 38 sub $0x38,%rsp
4004fd: 89 7d dc mov %edi,-0x24(%rbp)
400500: f2 0f 11 45 d0 movsd %xmm0,-0x30(%rbp)
400505: 48 89 75 c8 mov %rsi,-0x38(%rbp)
400509: 89 fb mov %edi,%ebx
40050b: 89 5d ec mov %ebx,-0x14(%rbp)
40050e: b8 80 06 40 00 mov $0x400680,%eax
400513: 8b 55 dc mov -0x24(%rbp),%edx
400516: 89 d6 mov %edx,%esi
400518: 48 89 c7 mov %rax,%rdi
40051b: b8 00 00 00 00 mov $0x0,%eax
400520: e8 cb fe ff ff callq 4003f0 <printf@plt>
400525: b8 88 06 40 00 mov $0x400688,%eax
40052a: f2 0f 10 45 d0 movsd -0x30(%rbp),%xmm0
40052f: 48 89 c7 mov %rax,%rdi
400532: b8 01 00 00 00 mov $0x1,%eax
400537: e8 b4 fe ff ff callq 4003f0 <printf@plt>
40053c: b8 90 06 40 00 mov $0x400690,%eax
400541: 48 8b 55 c8 mov -0x38(%rbp),%rdx
400545: 48 89 d6 mov %rdx,%rsi
400548: 48 89 c7 mov %rax,%rdi
40054b: b8 00 00 00 00 mov $0x0,%eax
400550: e8 9b fe ff ff callq 4003f0 <printf@plt>
400555: 89 d7 mov %edx,%edi
400557: 89 d3 mov %edx,%ebx
400559: 89 5d ec mov %ebx,-0x14(%rbp)
40055c: 48 83 c4 38 add $0x38,%rsp
400560: 5b pop %rbx
400561: 5d pop %rbp
400562: c3 retq
这似乎不会恢复%edi 的值。保存所有参数/参数寄存器的正确方法是什么?
【问题讨论】:
-
您是否检查过生成的程序集并在调试器中验证是否恢复了正确的值?内联汇编可能是一个讨厌的野兽。
-
@NiklasB.:我确实看过反汇编,但发布起来非常冗长。这里是:pastebin.com/HUexwViQ 看了之后我不确定是哪里出了问题。执行第一个
printf后发生段错误。 -
抱歉,没有意识到这一点。我不认为你可以像你想做的那样玩筹码。您可能想要做的是声明局部变量或局部缓冲区并使用它们来保存寄存器值。
-
@NiklasB.:我刚刚添加了我曾经尝试这样做的扩展 asm。
-
我没有看到它恢复了价值。也许漏洞优化器正在剥离您的还原,因为它知道 RDI 可以被破坏?