Author Topic: Can't make scanf work properly  (Read 10089 times)

Offline HD1920.1

  • Jr. Member
  • *
  • Posts: 40
Can't make scanf work properly
« 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?
Code: [Select]
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.


Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Re: Can't make scanf work properly
« Reply #1 on: April 20, 2014, 11:18:07 AM »
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
Code: [Select]
#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:
Code: [Select]
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:
Code: [Select]
  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:
Quote
  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
Code: [Select]
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!!!
Encryptor256's Investigation \ Research Department.

Offline HD1920.1

  • Jr. Member
  • *
  • Posts: 40
Re: Can't make scanf work properly
« Reply #2 on: April 20, 2014, 02:00:49 PM »
OK, thank you! So printf doesn't like the scanf output format?! But I don't have gcc. Here is a GoLink capable version:
Code: [Select]
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


Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Re: Can't make scanf work properly
« Reply #3 on: April 20, 2014, 02:58:55 PM »
WAIT!!!

I think i found THE REAL PROBLEM!

Problem was that format!!!

Back to square one!

Your code with updated format, test.asm:
Code: [Select]
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:
Code: [Select]
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:
Code: [Select]
...
formatin db "%f", 0
formatout db "You entered %f.", 10, 0
...
Into:
Code: [Select]
...
formatin db "%lf", 0
formatout db "You entered %lf.", 10, 0
...

Bye, Encryptor256!



Encryptor256's Investigation \ Research Department.

Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Re: Can't make scanf work properly
« Reply #4 on: April 20, 2014, 04:35:19 PM »
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:
Code: [Select]
#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:
Code: [Select]
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:
Code: [Select]
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:
Code: [Select]
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:
Code: [Select]
printf("Hello world");
Then, don't be surprised, if you debug and find into rdata section:
Code: [Select]
"<GCCWASHERE>Hello world</GCCWASHERE>"

:D

Bye.


Encryptor256's Investigation \ Research Department.

Offline HD1920.1

  • Jr. Member
  • *
  • Posts: 40
Re: Can't make scanf work properly
« Reply #5 on: April 21, 2014, 06:05:03 AM »
Thank you very much, encryptor256! It works!!!!!!!!!!!!!!!!!!!!!!!!