为什么打印浮点数时需要按相同的值 +4?

Why do I need to push the same value +4 when printing a float?

提问人:Radioactive Epic Face 提问时间:1/31/2023 最后编辑:zx485Radioactive Epic Face 更新时间:1/31/2023 访问量:73

问:

很长一段时间以来,我一直在尝试在 FASM 中打印出浮点值,直到我终于找到解决方案,它有效,但是,为什么会这样?在 C 程序上,我使 x64dbg 生成的汇编代码如下所示:

mov dword ptr ss:[esp+1C],eax
fld st(0), dword ptr ss:[esp+1C] ;<--- focusing on these 2
fstp qword ptr ss:[esp+4],st(0)  ;<---
mov dword ptr ss:[esp],c.404000
call <JMP.&printf>

我认为将浮点值加载到 st(0) 中并在地址上加载 st(0) 值,基本上很明显,所以我曾经尝试过以下操作:fldfstp

format PE
entry start

include '%include%\win32a.inc'

section '.text' code readable executable
start:
fld dword ptr dVal
fstp qword ptr dVal2
push msg
call [printf]
call [getchar]
call [exit]

section '.idata' import data readable
library msvcrt, 'msvcrt.dll'

import msvcrt, printf, 'printf'\
exit, 'exit', getchar, 'getchar'

section '.bss' data readable writeable
dVal2 dq ?

section '.data' data readable
msg db '%f',10,13,0
dVal dq 3.14

经过多次尝试,导致我崩溃或结果为 0 或随机数,与我想要的无关。然而,经过这么多的研究,我终于发现我可以简单地做这样的事情

...
section '.text' code readable executable
start:
fld dword ptr dVal
fstp qword ptr dVal2
push dword ptr dVal2+4
push dword ptr dVal2
push msg
call [printf]
call [getchar]
call [exit]
...

或者只是

...
section '.text' code readable executable
start:
push dword ptr dVal+4
push dword ptr dVal
push msg
call [printf]
call [getchar]
call [exit]
...

为什么在打印浮点值之前需要推送相同的值 +4?MASM的REAL4价值本质上是什么?

程序集 x86 浮点 FASM

评论


答:

4赞 fuz 1/31/2023 #1

该函数只能打印 64 位浮点数(双精度)。这样的双精度包含 8 个字节。通过将此值从 FPU 存储到堆栈来将此值推送到堆栈上:printf

sub     esp, 8           ; make space for the double
fstp    qword ptr [esp]  ; store double into that space
push    OFFSET msg
call    [printf]

或者分两步推送到堆栈,每步 4 个字节:

fstp    [dVal2]          ; store double to global/static variable
push    dword ptr [dVal2+4]    ; push high half of double
push    dword ptr [dVal2]      ; push low half of double
push    OFFSET msg
call    [printf]

请注意,随着堆栈的增长,我们必须将高 4 个字节推到低 4 个字节之前。需要两次单独的推送,因为一次只能推送 4 个字节,但双精度是 8 个字节。


假设您像问题中一样声明为 qword,这将意味着 的大小为 .FSTP 还可以转换为 DWORD(浮点数)或存储为 x87 寄存器中使用的原始 10 字节内部格式。这就是我们需要的原因,因为这并不意味着操作数大小。dVal2fstp [dVal2]fstp qword ptr [esp][esp]

与往常一样,要访问大小与该标签后第一项不同的数据标签附近的存储,MASM 需要覆盖该大小,否则它会抱怨不匹配,因此 ,因为仅在 64 位模式下可用。(它将在 XMM1 中作为第二个参数传递给函数。push dword ptr [dVal2+4]push qword ptr

评论

0赞 Radioactive Epic Face 1/31/2023
谢谢你的解释,这真的很容易理解!很抱歉让你在 FASM 和 MASM 上混淆了,只是我想知道 REAL4、REAL8 和 REAL10 等数据类型是什么意思,因为每当我在 ASM 中搜索浮点数时,我总是会找到它们,经过进一步研究,结合你向我展示的内容,我发现 REAL4 是一个 32 位浮点数, REAL8 是 64 位浮点数,REAL10 是 80 位浮点数(如果我错了,请纠正我)。你帮助我更好地了解了浮点数和双打之间的区别。