Assembly 程序集x86 FPU-堆栈混乱
我正在努力理解FPU,我很困惑。问题是,据我所知,FPU有自己的堆栈。但例如,在本代码(NASM)中: 我必须让Assembly 程序集x86 FPU-堆栈混乱,assembly,x86,nasm,cpu-registers,x87,Assembly,X86,Nasm,Cpu Registers,X87,我正在努力理解FPU,我很困惑。问题是,据我所知,FPU有自己的堆栈。但例如,在本代码(NASM)中: 我必须让子esp,8行为双精度腾出空间,否则程序崩溃。但通过这样做,我改变了“常规堆栈”的指针,这与我的两个独立堆栈的想法不符 我肯定我不明白什么,但我不知道这是什么 FPU有一个“寄存器堆栈”(而不是RAM中的堆栈) 本质上;有8个寄存器(我们称它们为FPU\u R0,FPU\u R1,…,FPU\u R7)和8个名称(假设它们是st0,st1,…,st7),还有一个“FPU堆栈顶部”值确定
子esp,8
行为双精度
腾出空间,否则程序崩溃。但通过这样做,我改变了“常规堆栈”的指针,这与我的两个独立堆栈的想法不符
我肯定我不明白什么,但我不知道这是什么 FPU有一个“寄存器堆栈”(而不是RAM中的堆栈)
本质上;有8个寄存器(我们称它们为FPU\u R0
,FPU\u R1
,…,FPU\u R7
)和8个名称(假设它们是st0
,st1
,…,st7
),还有一个“FPU堆栈顶部”值确定哪个名称用于哪个寄存器
您可以将新值推送到FPU寄存器堆栈上。例如:
fld qword [A] ;st0 = FPU_R7 = A
fld qword [B] ;st0 = FPU_R6 = B, st1 = FPU_R7 = A
fld qword [C] ;st0 = FPU_R5 = C, st1 = FPU_R6 = B, st2 = FPU_R7 = A
;st0 = FPU_R5 = C, st1 = FPU_R6 = B, st2 = FPU_R7 = A
fstp qword [C] ;st0 = FPU_R6 = B, st1 = FPU_R7 = A
fstp qword [B] ;st0 = FPU_R7 = A
fstp qword [A]
可以从FPU寄存器堆栈中弹出值。例如:
fld qword [A] ;st0 = FPU_R7 = A
fld qword [B] ;st0 = FPU_R6 = B, st1 = FPU_R7 = A
fld qword [C] ;st0 = FPU_R5 = C, st1 = FPU_R6 = B, st2 = FPU_R7 = A
;st0 = FPU_R5 = C, st1 = FPU_R6 = B, st2 = FPU_R7 = A
fstp qword [C] ;st0 = FPU_R6 = B, st1 = FPU_R7 = A
fstp qword [B] ;st0 = FPU_R7 = A
fstp qword [A]
x87加载/存储使用与其他所有操作相同的内存地址。 x87堆栈是寄存器st0..st7,而不是内存 有关x87寄存器堆栈的详细信息,请参阅
fstp qword[esp]
将8个字节存储到常规调用堆栈中,如mov[esp],eax
/mov[esp+4],edx
与x87加载/存储指令一起使用时,寻址模式不会改变其含义即,您的进程只有一个地址空间
因此,如果您删除
子esp,8
,您的fstp
将覆盖您的回信地址
然后在函数结束时,add esp,12
将使esp
指向8个字节以上,因此ret
将向EIP中弹出一些垃圾,然后您在尝试从该错误地址提取代码时将其分段,或者将其中的字节解码为分段错误的指令
在main
的返回地址上方,您将找到argc
,然后找到char**argv
。它是指向指针数组的指针,因此使用它作为返回地址意味着您将指针值作为代码执行。(如果我没弄错的话。)
使用调试器查看单步执行时寄存器和内存发生的情况。
请注意,
添加esp,4
/子esp,8
有点傻AddESP,+4-8
(即,AddESP,-4
)将是一种使用一条指令实现这一点的自记录方式。,因为您使用了从FPU堆栈传输到CPU堆栈的fstp qword[esp]
。所以你们需要CPU堆栈上的空间。我认为这个答案不值得否决。第一句是对OP的困惑的回答,其余的是演示x87堆栈是如何工作的(它是一个寄存器堆栈,与内存中的调用堆栈无关。)这不是一个很好的答案,这就是为什么我写了自己的;它几乎没有回答这个问题。但它所说的一切实际上都是正确的。(呃,除了st0在几个注释中被重复。我认为您有一个复制/粘贴+编辑错误,并且忘记了增加您的变量名)。展示fmulst3
或其他东西也不错。这个答案是完整答案的重要部分,谁否决了这个,为什么?它没有从OPs问题中解释关于常规堆栈的任何内容,但正确地解释了FPU“堆栈”部分,其本身在技术上应该足以消除OPs的混淆并解决其问题。(关于这个答案的质量,甚至显示fst
和fstp
之间的差异也有一定的意义)(编辑2:我也没有得到OP的否决票,这有点低,因为在第一次阅读FPU后可能会感到困惑,但问题是相当清楚的,以显示困惑是什么,以及尝试了什么)这个答案当然不值得投反对票。虽然它没有彼得·科德斯的完整,但我会投票表决。尽管如此,我还是非常感谢@Brendan在这方面花费的时间和精力:)问题是为什么需要子esp
,而不是FPU堆栈如何工作。这个答案并没有提供任何洞察。但这本身并没有错。