在 x86 nasm 32 位中使用 printf 打印浮点数 [英] Printing floats with printf in x86 nasm 32-bit
问题描述
我正在尝试使用 NASM 风格的 x86 程序集打印出一些 32 位浮点数.这是我正在尝试做的最小工作示例:
全局主外部 printf, scanf.data 节scan_format: db "%f",0打印格式:db "%f",0xA,0.bss 节result_num: resb 4节.text主要的:推送 result_num推送扫描格式调用 scanf添加 esp, 8推双字 [result_num]推送打印格式调用 printf添加 esp, 8退
当我运行这个时,我得到了一些奇怪的输出:
$ nasm -felf32 -g printf_test.asm$ gcc printf_test.o -o printf_test.out$ ./printf_test.out <<<1234-0.000000
如果我在程序运行时尝试检查该值,它似乎是正确的:
$ gdb ./printf_test.out(gdb) 反汇编 *main转储函数 main 的汇编代码:0x08048420 <+0>:推送 0x804a0280x08048425 <+5>:推送 0x804a0180x0804842a <+10>:呼叫 0x8048330 <scanf@plt>0x0804842f <+15>: 添加 esp,0x80x08048432 <+18>:推送 DWORD PTR ds:0x804a0280x08048438 <+24>:推送 0x804a01b0x0804843d <+29>:呼叫 0x8048320 <printf@plt>0x08048442 <+34>: 添加 esp,0x80x08048445 <+37>:返回0x08048446 <+38>:没有0x08048447 <+39>:没有0x08048448 <+40>:没有0x08048449 <+41>:没有0x0804844a <+42>:nop0x0804844b <+43>: nop0x0804844c <+44>: nop0x0804844d <+45>:没有0x0804844e <+46>: nop0x0804844f <+47>: nop汇编程序转储结束.(gdb) 中断 *main+34断点 1 位于 0x8048442(gdb) r启动程序:/vagrant/project_03/printf_test.out1234-0.000000断点 1, 0x08048442 in main()(gdb) p/f result_num1 美元 = 1234
我在这里做错了什么?
编辑
如果我尝试使用双打,它甚至不会组装,这个程序:
全局主外部 printf, scanf.data 节scan_format: db "%f",0打印格式:db "%f",0xA,0.bss 节result_num: resb 4result_num_dub: resb 8节.text主要的:推送 result_num推送扫描格式调用 scanf添加 esp, 8fld 双字 [result_num]fstp qword [result_num_dub]push qword [result_num_dub] ;汇编器错误在这里推送打印格式调用 printf添加 esp, 8退
生成此输出:
$ nasm -felf32 -g printf_test.asmprintf_test.asm:22: 错误:32 位模式不支持指令
如果我尝试直接从浮点堆栈转到内存堆栈,则会出现段错误,即使正确的东西似乎存储在内存中.
fld dword [result_num]fstp qword [esp]推送格式调用 printf
它在 gdb 中看起来不错:
(gdb) p *((double*)($esp))9 美元 = 2.5
但是它在 printf 调用的中间产生了一个段错误.
我必须遗漏一些东西.
正如 Michael 所指出的,printf
中的 %f
需要一个 double
,所以你的数字必须在将它推入printf
的堆栈之前转换成一个double
:
全局主外部 printf, scanf.data 节scan_format: db "%f",0打印格式:db结果:%f",0xA,0.bss 节result_num: resb 4节.text主要的:推送 result_num推送扫描格式调用 scanf添加 esp, 8sub esp,8 ;为双栈保留栈mov ebx,result_numfld dword [ebx] ;加载浮点数fstp qword [esp] ;store double (8087 在内部进行转换)推送打印格式调用 printf添加 esp, 12退
<块引用>
push qword [result_num_dub] ;汇编器错误在这里
您不能在 32 位模式下进行 64 位操作,例如一次推送 64 位.这就是我使用 sub esp
方法来保留堆栈空间的原因.你的第二个程序只需要这个:
section .text主要的:推送 result_num推送扫描格式调用 scanf添加 esp, 8fld 双字 [result_num]fstp qword [result_num_dub]push dword [result_num_dub+4] ;pushes 32 bits (MSB)push dword [result_num_dub] ;pushes 32 bits (LSB)推送打印格式调用 printf添加 esp, 12 ;<-- 12 个字节,而不是 8 个.退
I'm trying to print out some 32-bit floats using NASM flavored x86 assembly. This is a minimum working example of what I'm trying to do:
global main
extern printf, scanf
section .data
scan_format: db "%f",0
print_format: db "%f",0xA,0
section .bss
result_num: resb 4
section .text
main:
push result_num
push scan_format
call scanf
add esp, 8
push dword [result_num]
push print_format
call printf
add esp, 8
ret
When I run this, I get some strange output:
$ nasm -felf32 -g printf_test.asm
$ gcc printf_test.o -o printf_test.out
$ ./printf_test.out <<< 1234
-0.000000
If I try to examine the value while the program is running, it seems to be correct:
$ gdb ./printf_test.out
(gdb) disassemble *main
Dump of assembler code for function main:
0x08048420 <+0>: push 0x804a028
0x08048425 <+5>: push 0x804a018
0x0804842a <+10>: call 0x8048330 <scanf@plt>
0x0804842f <+15>: add esp,0x8
0x08048432 <+18>: push DWORD PTR ds:0x804a028
0x08048438 <+24>: push 0x804a01b
0x0804843d <+29>: call 0x8048320 <printf@plt>
0x08048442 <+34>: add esp,0x8
0x08048445 <+37>: ret
0x08048446 <+38>: nop
0x08048447 <+39>: nop
0x08048448 <+40>: nop
0x08048449 <+41>: nop
0x0804844a <+42>: nop
0x0804844b <+43>: nop
0x0804844c <+44>: nop
0x0804844d <+45>: nop
0x0804844e <+46>: nop
0x0804844f <+47>: nop
End of assembler dump.
(gdb) break *main+34
Breakpoint 1 at 0x8048442
(gdb) r
Starting program: /vagrant/project_03/printf_test.out
1234
-0.000000
Breakpoint 1, 0x08048442 in main ()
(gdb) p /f result_num
$1 = 1234
What am I doing wrong here?
EDIT
If I try to use doubles, it won't even assemble, this program:
global main
extern printf, scanf
section .data
scan_format: db "%f",0
print_format: db "%f",0xA,0
section .bss
result_num: resb 4
result_num_dub: resb 8
section .text
main:
push result_num
push scan_format
call scanf
add esp, 8
fld dword [result_num]
fstp qword [result_num_dub]
push qword [result_num_dub] ;ASSEMBLER ERROR HERE
push print_format
call printf
add esp, 8
ret
Generates this output:
$ nasm -felf32 -g printf_test.asm
printf_test.asm:22: error: instruction not supported in 32-bit mode
And if I try to go directly from the float stack to the memory stack, I get a segfault, even though the right thing appears to be stored in memory.
fld dword [result_num]
fstp qword [esp]
push format
call printf
It looks right in gdb:
(gdb) p *((double*)($esp))
$9 = 2.5
But it generates a segfault in the middle of the printf call.
I have to be missing something.
As Michael pointed, %f
in printf
expects a double
, so your number must be converted into a double
just before pushing it on the stack for printf
:
global main
extern printf, scanf
section .data
scan_format: db "%f",0
print_format: db "Result: %f",0xA,0
section .bss
result_num: resb 4
section .text
main:
push result_num
push scan_format
call scanf
add esp, 8
sub esp,8 ;reserve stack for a double in stack
mov ebx,result_num
fld dword [ebx] ;load float
fstp qword [esp] ;store double (8087 does the conversion internally)
push print_format
call printf
add esp, 12
ret
push qword [result_num_dub] ;ASSEMBLER ERROR HERE
You cannot do 64 bit operations, like pushing 64 bits at a time, in 32 bit mode. This is why I used the sub esp
method to reserve stack space. Your second program just needs this:
section .text
main:
push result_num
push scan_format
call scanf
add esp, 8
fld dword [result_num]
fstp qword [result_num_dub]
push dword [result_num_dub+4] ;pushes 32 bits (MSB)
push dword [result_num_dub] ;pushes 32 bits (LSB)
push print_format
call printf
add esp, 12 ;<-- 12 bytes, not 8.
ret
这篇关于在 x86 nasm 32 位中使用 printf 打印浮点数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!