为什么编译器会生成这段代码?

发布于 2024-12-28 22:23:59 字数 497 浏览 1 评论 0原文

我使用 DumpBin 反汇编了一个目标文件(很可能是使用 Visual C++ 编译器生成的),并看到了以下代码:

...         ...
mov         dword ptr [ebp-4],eax       // Why save EAX?
push        dword ptr [ebp+14h]
push        dword ptr [ebp+10h]
push        dword ptr [ebp+0Ch]
push        dword ptr [ebp+8]
mov         eax,dword ptr [ebp-4]       // Why restore EAX? Did it change at all?
call        <function>
...         ...

有人可以解释一下为什么 EAX 寄存器在这 4 个 中保存和恢复吗? >推送指令?

I disassembled an object file (most likely generated using the Visual C++ compiler) using DumpBin and saw the following piece of code:

...         ...
mov         dword ptr [ebp-4],eax       // Why save EAX?
push        dword ptr [ebp+14h]
push        dword ptr [ebp+10h]
push        dword ptr [ebp+0Ch]
push        dword ptr [ebp+8]
mov         eax,dword ptr [ebp-4]       // Why restore EAX? Did it change at all?
call        <function>
...         ...

Could someone please explain why the EAX register is being saved and restored across these 4 push instructions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

挽袖吟 2025-01-04 22:23:59

另外,也许它是在发布模式下编译的,但该变量已被标记为易失性,这告诉编译器该变量可能会在不知情的情况下发生变化,因此它被迫不断地写入/恢复它/从堆栈中

Also, maybe it's compiled in release mode, but that variable has been marked as volatile, which tells the compiler that such variable may change without it knowing, so it is forced to continuously write/restore it on/from the stack

东走西顾 2025-01-04 22:23:59

这是在调试模式下构建的吗?如果是这样,编译器会将每个局部变量存储在堆栈上,以便调试器可以以一致的方式找到它们。

消除这种不必要的存储和重新加载是构成“发布”模式的优化之一。

Was this built in debug mode? If so, the compiler stores every local variable on the stack so that the debugger can find them in a consistent way.

The elision of such unnecessary stores and reloads is one of the optimizations that constitutes "release" mode.

謸气贵蔟 2025-01-04 22:23:59

易失性与否,这是EAX在调用函数之前必须直接初始化的唯一技术原因如果该函数被声明为__syscall,即使用Windows CS_SYSCALL调用约定,则Windows是这样。从概念上讲,这有点类似于 UN*X x86_64 约定,其中 %al 包含在 %xmm 寄存器中传递的浮点类型参数的数量。

Windows 上的系统调用调用约定与 __cdecl 相同,即函数参数以相反的顺序存放在堆栈上,但另外,AL 包含参数数量的计数;这样做是为了让通常位于最后的内核代码知道要从用户堆栈读取多少数据到内核堆栈以检索参数。

EAX 是 32 位 Windows 上所有调用约定的临时寄存器,它的值永远不会在函数调用中保留,在调用之前直接初始化它是多余的。即使它保存的变量是易失性的 - 因为简单的重新加载不是内存屏障,并且不会“提交”先前的存储。此外,位置[EBP - 4]位于堆栈内,因此该变量是本地(并且是易失性 code> 限定符没有什么意义)。

如果这不是错过的优化,那么它可能是使用不同数量的参数调用 __syscall function(...) ,就像假设的那样,

__syscall printf_syscall_conv(char *fmt, ...);

void possibly_print_three_vals(char *fmt, int val1, int val2, int val3)
{
    if (*strchr('%', fmt) == '\0')    // if no "%" in fmt, pass no args
        printf_syscall_conv(fmt);
    else
        printf_syscall_conv(fmt, val1, val2, val3);
}

这可能会创建像您这样的汇编输出。

volatile or not, the only technical reason why EAX would have to be initialized directly before making a function call on Windows were if that function is declared __syscall, i.e. using the Windows CS_SYSCALL calling convention. Conceptually, this is a bit similar to the UN*X x86_64 convention where %al contains the number of floating point type args passed in %xmm registers.

The syscall calling convention on Windows is identical to __cdecl, i.e. function args on stack in reverse order, but with the addition that AL contains a count of the number of arguments; this is done so that the kernel code which is usually at the final end of this knows how much data to read from the user stack onto the kernel stack to retrieve the args.

EAX is a scratch register for all calling conventions on 32bit Windows, its value is never preserved over function calls, initializing it directly before making a call is redundant. Even if the variable it holds were volatile - because a simple re-load isn't a memory barrier and doesn't "commit" a previous store. In addition, the location [EBP - 4] is within the stack, so the variable is local (and a volatile qualifier makes little sense).

If it's not a missed optimization then it could be an invocation of a __syscall function(...) with different numbers of arguments, like, hypothetically,

__syscall printf_syscall_conv(char *fmt, ...);

void possibly_print_three_vals(char *fmt, int val1, int val2, int val3)
{
    if (*strchr('%', fmt) == '\0')    // if no "%" in fmt, pass no args
        printf_syscall_conv(fmt);
    else
        printf_syscall_conv(fmt, val1, val2, val3);
}

This could conceivably create assembly output like yours.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文