Linux 理解ASM。为什么这在Windows中工作?

Linux 理解ASM。为什么这在Windows中工作?,linux,windows,assembly,Linux,Windows,Assembly,我和几个朋友正在处理一个非常奇怪的问题。我们的应用程序在一个小的汇编程序部分(用于加快进程)中遇到了崩溃。该错误是由于摆弄堆栈指针而未在最后重置堆栈指针而导致的,看起来如下所示: push ebp mov ebp, esp ; do stuff here including sub and add on esp pop ebp 如果正确,则应写为: push ebp mov ebp, esp ; do stuff

我和几个朋友正在处理一个非常奇怪的问题。我们的应用程序在一个小的汇编程序部分(用于加快进程)中遇到了崩溃。该错误是由于摆弄堆栈指针而未在最后重置堆栈指针而导致的,看起来如下所示:

push        ebp
mov         ebp, esp

; do stuff here including sub and add on esp

pop         ebp
如果正确,则应写为:

push        ebp
mov         ebp, esp

; do stuff here including sub and add on esp
mov         esp,ebp
pop         ebp

现在我们的突破点是:为什么这在Windows中工作?我们在将应用程序移植到Linux时发现了错误,我们在Linux中遇到了崩溃。无论是在Windows还是Android(使用NDK),我们都没有遇到任何问题,也永远不会发现这个错误。是否有任何堆栈指针恢复?是否有防止误用堆栈指针的保护措施?

ebp esp用法称为堆栈帧,其目的是在堆栈上分配变量,然后在执行
ret
指令之前快速恢复堆栈。所有新版本的x86CPU都可以使用enter/leave指令将这些指令压缩在一起

esp是CPU在执行push/pop/call/ret时使用的实际堆栈指针。 ebp是一个用户操纵的基指针,几乎所有编译器都将其用作本地存储的堆栈指针


如果缺少
mov esp,ebp
指令,则如果esp!=当CPU到达弹出ebp时返回ebp,但只有在该时间。

编译器才能在windows中处理堆栈:
我能想象的唯一方式是:
Microsoft Visual C特别关注B{uu stdcall}函数。由于参数的数量在编译时是已知的,编译器将参数字节计数编码到符号名本身中。
_ustdcall约定主要由Windows API使用,它比u cdecl更紧凑。主要区别在于,任何给定函数都有一组硬编码的参数,并且这些参数不能像在C中那样在不同的调用之间变化(没有“可变函数”)。
请参阅:

和:

-fomit帧指针
已成为32位x86的gcc和clang中的默认值至少一年了。它在x86-64中的默认值已经存在更长时间了
enter
/
leave
在80186中是新的,所以所有支持32位的x86 CPU都支持它们,而不仅仅是新的<不过,code>enter使用起来太慢了<如果需要
mov-esp,ebp
部分操作,则code>leave值得使用,否则只要
pop-ebp
功能体已经使用
add
恢复
esp
,这样它就可以
pop
ebp
之前的其他注册表。对
esp
的修改是否应该平衡?如果
add
s和
pop
s平衡了
sub
s和
push
es,那么
esp==ebp
已经存在,那么您应该省去
mov-esp,ebp
(因为它不需要)。如果它崩溃了,那么很明显他们没有平衡。在Windows和Linux上使用相同的汇编程序吗?不同的汇编程序将把同一源代码汇编成不同的代码。(例如,在NASM中,
mov-reg,symbol
mov-reg,imm32
,但在MASM中它是一个负载。)ABI甚至对于32位也略有不同。这个汇编程序是在单独的文件中还是内联的?@PeterCordes:不,它们不平衡。在Linux下,我的Stackpointer完全是一个bug。在Windows下,当您单步执行时会发生什么?
esp
在什么时候恢复同步?你确定你的源代码在为Windows构建它时是相同的吗?如果它不是内联的,那么我很好奇你是如何声明这个函数的。是用
funcname PROC
语句完成的吗?汇编文件顶部的
model
语句是什么(如果它不是内联汇编)。真正有帮助的是更多的信息,比如一个最小的完整的可验证示例,其中包含在Windows下工作的确切代码,以及您在移植到Linux失败时编写的等效代码。事实上,我认为没有足够的信息来充分回答这个问题。任何答案都是猜测。如果他的函数没有用
ret 8
或其他什么弹出参数,那么
\u stdcall
就不可能工作。调用方和被调用方之间的不匹配将导致中断,而不是阻止它。我不认为这是合理的解释,即使是在一个特定的情况下,你得到幸运和坏代码碰巧工作的情况下。是的,我同意,在程序集中,调用方和被调用方之间的不匹配会导致破坏。