为什么编译器会生成这段代码?
我使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
另外,也许它是在发布模式下编译的,但该变量已被标记为
易失性
,这告诉编译器该变量可能会在不知情的情况下发生变化,因此它被迫不断地写入/恢复它/从堆栈中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这是在调试模式下构建的吗?如果是这样,编译器会将每个局部变量存储在堆栈上,以便调试器可以以一致的方式找到它们。
消除这种不必要的存储和重新加载是构成“发布”模式的优化之一。
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.
易失性
与否,这是EAX
在调用函数之前必须直接初始化的唯一技术原因如果该函数
被声明为__syscall
,即使用Windows CS_SYSCALL调用约定,则Windows是这样。从概念上讲,这有点类似于 UN*X x86_64 约定,其中%al
包含在%xmm
寄存器中传递的浮点类型参数的数量。Windows 上的系统调用调用约定与 __cdecl 相同,即函数参数以相反的顺序存放在堆栈上,但另外,
AL
包含参数数量的计数;这样做是为了让通常位于最后的内核代码知道要从用户堆栈读取多少数据到内核堆栈以检索参数。EAX
是 32 位 Windows 上所有调用约定的临时寄存器,它的值永远不会在函数调用中保留,在调用之前直接初始化它是多余的。即使它保存的变量是易失性的 - 因为简单的重新加载不是内存屏障,并且不会“提交”先前的存储。此外,位置[EBP - 4]
位于堆栈内,因此该变量是本地(并且是易失性 code> 限定符没有什么意义)。
如果这不是错过的优化,那么它可能是使用不同数量的参数调用 __syscall function(...) ,就像假设的那样,
这可能会创建像您这样的汇编输出。
volatile
or not, the only technical reason whyEAX
would have to be initialized directly before making a function call on Windows were if thatfunction
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 thatAL
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 werevolatile
- 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 avolatile
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,This could conceivably create assembly output like yours.