Home > other >  Why do I need to push the same value 4 when printing a float?
Why do I need to push the same value 4 when printing a float?

Time:02-01

I have been trying to print out a float value in FASM for a pretty long time until I finally found a solution, it worked but, why is it like that? On a C program I made the assembly code generated by x64dbg was something like this:

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>

I thought the fact fld loaded the float value into st(0) and fstp loaded the st(0) value on an address, basically, quite obvious so I used to try things like:

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

Which after many tries, rendered me crashes or results like 0 or random numbers that have nothing to do with what I wanted. However, after so much research I finally found out that I could simply do something like

...
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]
...

or just

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

Why do I need to push the same value 4 before printing a float value? And what essentially is the REAL4 value from MASM?

CodePudding user response:

The printf function can only print 64 bit floats (double). Such a double comprises 8 bytes. Either push this value onto the stack by storing to the stack from the FPU:

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

or push to the stack in two steps of 4 bytes each:

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]

Note that as the stack grows down, we have to push the high 4 bytes before the low 4 bytes. Two separate pushes are required as you can only push 4 bytes at a time, but a double is 8 bytes.


Assuming you declared dVal2 as a qword like in the question, that will imply a size for fstp [dVal2]. fstp can also convert to dword (float) or store as the raw 10-byte internal format used in x87 registers. That's why we needed fstp qword ptr [esp] since [esp] doesn't imply an operand size.

As always, to access storage near a data label with a size different from the first item after that label, MASM needs you to override the size or it will complain about a mismatch, thus push dword ptr [dVal2 4], because push qword ptr is only available in 64-bit mode. (Where it would be passed in XMM1 as the 2nd arg to a function.)

  • Related