NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: HD1920.1 on April 20, 2014, 10:15:39 AM
-
When I try to get a floating point value with scanf and display it with printf, scanf seems not to work. It doesn't throw an error, but the value seems not to be stored. What's wrong with my code?
extern scanf
extern printf
extern exit
global Start
section .text
Start:
sub rsp, 0x28
mov rcx, info
call printf
mov rcx, formatin
mov rdx, float1
call scanf
mov rcx, formatout
mov rdx, qword [float1]
call printf
xor rcx, rcx
call exit
add rsp, 0x28
ret
section .data
info db "Please enter a floating point value: ", 0
formatin db " %f", 0
formatout db "You entered %f.", 10, 0
float1 dq 0.0 ; 0.000000 is always printed out. When I replace this with 4.0, 4.000001 is printed out.
-
Hi,
i confirm, tested and it's problem there.
Q: What kinda things we do usually when somewhere it works and somewhere it doesn't?
A: We use debugger on C code and see whats behind it.
main.c
#include <stdio.h>
int main ()
{
double f;
scanf ("%lf",&f);
printf("\r\n %lf",f);
return 0;
}
Compile: "gcc main.c -o main.exe"
Debug: "objdump -D main.exe >> out.txt"
Debug of main:
00000000004014e0 <main>:
4014e0: 55 push %rbp
4014e1: 48 89 e5 mov %rsp,%rbp
4014e4: 48 83 ec 30 sub $0x30,%rsp
4014e8: e8 23 0e 00 00 callq 402310 <__main>
4014ed: 48 8d 45 fc lea -0x4(%rbp),%rax
4014f1: 48 89 c2 mov %rax,%rdx
4014f4: 48 8d 0d 05 2b 00 00 lea 0x2b05(%rip),%rcx # 404000 <.rdata>
4014fb: e8 e0 15 00 00 callq 402ae0 <scanf>
401500: 8b 45 fc mov -0x4(%rbp),%eax
401503: 66 0f 6e d0 movd %eax,%xmm2
401507: 0f 14 d2 unpcklps %xmm2,%xmm2
40150a: 0f 5a c2 cvtps2pd %xmm2,%xmm0
40150d: 66 48 0f 7e c0 movq %xmm0,%rax
401512: 48 89 c2 mov %rax,%rdx
401515: 66 48 0f 6e ca movq %rdx,%xmm1
40151a: 48 89 c2 mov %rax,%rdx
40151d: 48 8d 0d df 2a 00 00 lea 0x2adf(%rip),%rcx # 404003 <.rdata+0x3>
401524: e8 bf 15 00 00 callq 402ae8 <printf>
401529: b8 00 00 00 00 mov $0x0,%eax
40152e: 48 83 c4 30 add $0x30,%rsp
401532: 5d pop %rbp
401533: c3 retq
....
SO, "scanf ("%lf",&f);" is the same as:
4014ed: 48 8d 45 fc lea -0x4(%rbp),%rax
4014f1: 48 89 c2 mov %rax,%rdx
4014f4: 48 8d 0d 05 2b 00 00 lea 0x2b05(%rip),%rcx # 404000 <.rdata>
4014fb: e8 e0 15 00 00 callq 402ae0 <scanf>
SO, "printf("\r\n %lf",f);" is the same as:
401500: 8b 45 fc mov -0x4(%rbp),%eax
401503: 66 0f 6e d0 movd %eax,%xmm2
401507: 0f 14 d2 unpcklps %xmm2,%xmm2
40150a: 0f 5a c2 cvtps2pd %xmm2,%xmm0
40150d: 66 48 0f 7e c0 movq %xmm0,%rax
401512: 48 89 c2 mov %rax,%rdx
401515: 66 48 0f 6e ca movq %rdx,%xmm1
40151a: 48 89 c2 mov %rax,%rdx
40151d: 48 8d 0d df 2a 00 00 lea 0x2adf(%rip),%rcx # 404003 <.rdata+0x3>
401524: e8 bf 15 00 00 callq 402ae8 <printf>
My ambition was to find working version of scanf input and i achieved my goal!
Problem was not that scanf call, but some extra code after it.
Here it is, yours version modified into my version, you can have it!
test.asm
extern scanf
extern printf
extern exit
global main
bits 64
section .text use64
align 16
main:
sub rsp, 0x28
mov rcx, info
call printf
mov rcx, formatin
mov rdx, float1
call scanf
;
; NEW
;
mov eax,[float1]
movd xmm2,eax
unpcklps xmm2,xmm2
cvtps2pd xmm0,xmm2
movq rax,xmm0
mov rdx,rax
mov rcx, formatout
;mov rdx, qword [float1]
call printf
;
;
;
xor rcx, rcx
call exit
add rsp, 0x28
ret
section .data use64
align 16
info db "Please enter a floating point value: ", 0
align 16
formatin db "%f", 0
align 16
formatout db "You entered %f.", 10, 0
align 16
float1 dq 5.0 ; 0.000000 is always printed out. When I replace this with 4.0, 4.000001 is printed out.
Compile: "nasm.exe -f win64 test.asm -o test.obj"
Build: "gcc test.obj -o test.exe"
Thanks for the challenge, it was fun to do!
I think that's all i wanted to say!
Bye, Encryptor256!!!
-
OK, thank you! So printf doesn't like the scanf output format?! But I don't have gcc. Here is a GoLink capable version:
extern scanf
extern printf
extern exit
global main
section .text
Start:
sub rsp, 0x28
mov rcx, info
call printf
mov rcx, formatin
mov rdx, float1
call scanf
mov rcx, formatout
mov eax, [float1]
movd xmm2, eax
unpcklps xmm2, xmm2
cvtps2pd xmm0, xmm2
movq rax, xmm0
mov rdx, rax
call printf
xor rcx, rcx
call exit
add rsp, 0x28
ret
section .data
info db "Please enter a floating point value: ", 0
formatin db "%f", 0
formatout db "You entered %f.", 10, 0
float1 dq 0.0
-
WAIT!!!
I think i found THE REAL PROBLEM!
Problem was that format!!!
Back to square one!
Your code with updated format, test.asm:
extern scanf
extern printf
extern exit
global Start
section .text
Start:
sub rsp, 0x28
mov rcx, info
call printf
mov rcx, formatin
mov rdx, float1
call scanf
mov rcx, formatout
mov rdx, qword [float1]
call printf
xor rcx, rcx
call exit
add rsp, 0x28
ret
section .data
info db "Please enter a floating point value: ", 0
formatin db " %lf", 0
formatout db "You entered %lf.", 10, 0
float1 dq 0.0 ; 0.000000 is always printed out. When I replace this with 4.0, 4.000001 is printed out.
Compile with: "nasm.exe -f win64 test.asm -o test.obj"
Build GoLink: "golink.exe /console /entry Start test.obj kernel32.dll MSVCRT.dll"
Worked fine!
My command line output:
J:\>nasm.exe -f win64 test.asm -o test.obj
J:\>golink.exe /console /entry Start test.obj kernel32.dll MSVCRT.dll
GoLink.Exe Version 0.28.0.0 - Copyright Jeremy Gordon 2002/12 - JG@JGnet.co.uk
Output file: test.exe
Format: X64 size: 2,048 bytes
J:\>test.exe
Please enter a floating point value: 4564.7897
You entered 4564.789700.
J:\>
Format was the problem!!!
Changed:
...
formatin db "%f", 0
formatout db "You entered %f.", 10, 0
...
Into:
...
formatin db "%lf", 0
formatout db "You entered %lf.", 10, 0
...
Bye, Encryptor256!
-
Near off topic, but on topic:
C is fake, can't be trusted and why should,
C can mess your head, if you program assembly and think C way.
Let's investigate this code, pay attention to formats:
#include <stdio.h>
int main ()
{
float f;
scanf ("%f",&f);
printf("\r\n %f",f);
return 0;
}
Compiled into obj file: "gcc -g -O -c main.c" (Creates main.o)
Debug with: "objdump -D main.o >> out.txt"
Let's find entry point - main:
Disassembly of section .text:
...
...
00000000004014e0 <main>:
4014e0: 55 push %rbp
4014e1: 48 89 e5 mov %rsp,%rbp
4014e4: 48 83 ec 30 sub $0x30,%rsp
4014e8: e8 13 0e 00 00 callq 402300 <__main>
4014ed: 48 8d 45 f8 lea -0x8(%rbp),%rax
4014f1: 48 89 c2 mov %rax,%rdx
4014f4: 48 8d 0d 05 2b 00 00 lea 0x2b05(%rip),%rcx # 404000 <.rdata>
4014fb: e8 d0 15 00 00 callq 402ad0 <scanf>
401500: 48 8b 45 f8 mov -0x8(%rbp),%rax
401504: 48 89 c2 mov %rax,%rdx
401507: 66 48 0f 6e ca movq %rdx,%xmm1
40150c: 48 89 c2 mov %rax,%rdx
40150f: 48 8d 0d ee 2a 00 00 lea 0x2aee(%rip),%rcx # 404004 <.rdata+0x4>
401516: e8 bd 15 00 00 callq 402ad8 <printf>
40151b: b8 00 00 00 00 mov $0x0,%eax
401520: 48 83 c4 30 add $0x30,%rsp
401524: 5d pop %rbp
401525: c3 retq
...
There you can see is a call of scanf and printf, they load format from rdata section, so let's go, find it, those formats:
Disassembly of section .rdata:
...
...
0000000000404000 <.rdata>:
404000: 25 6c 66 00 0d and $0xd00666c,%eax
404005: 0a 20 or (%rax),%ah
404007: 25 6c 66 00 00 and $0x666c,%eax
40400c: 00 00 add %al,(%rax)
...
Here we are, line address 404000 is an address that is passed to a scanf.
So, if we passed "%f", then what is here:
404000: 25 6c 66
0x25 = ASCII '%'
0x6C = ASCII 'l'
0x66 = ASCII 'f'
results in "%lf" - long float or float64 or double.
So, where did that "L".toLowerCase() arised, if we wanted only "%f"...
1. GCC inserted "L".toLowerCase() into format.
2. GCC altered our piece of art (code) without asking us a permission.
So, well then, if something works in C and doesn't in Assembly, no wonder why. :)
No wonder my head almost hurt's from this investigation, because it was a small problem and got messy. :D
Well, at least, we found some answers on "why and what" was wrongy (- new word).
So, if you print code with gcc:
printf("Hello world");
Then, don't be surprised, if you debug and find into rdata section:
"<GCCWASHERE>Hello world</GCCWASHERE>"
:D
Bye.
-
Thank you very much, encryptor256! It works!!!!!!!!!!!!!!!!!!!!!!!!