Reputation: 31
I have some assembly code that uses scanf and printf and I'm running into some problems. When both of these functions are used in the same code, the values in the registers seem to be lost. The program basically loads a number and prints it out. We run it using
nasm -f elf64 file.asm && gcc -o file file.o && ./file
on linux
Here's our code:
extern printf
extern scanf
section .data
a db "set: ", 0
b db "not set: ", 0
reading db "Please enter a number: ", 0
message db "\n", 0
printsent db "%s", 10, 0
printint db "%d", 10, 0
printchar db "%c", 10, 0
readInt db "%d", 0
input db "%d", 0
section .text
global main
main:
hatta:
push rbp,
mov rbp, rsp,
push rbx,
xor rax, rax,
mov rdi, printsent,
mov rsi, reading
call printf,
pop rbx,
xor rax, rax,
mov rdi, readInt,
call scanf,
mov rbx, rdi
push rbx,
xor rax, rax,
mov rdi, printint,
mov rsi, rbx,
call printf,
pop rbx,
pop rbp,
ret
The odd thing is that if the line mov rdi, printint,
is removed, we obtain the correct values. However, if we do the same thing with printsentence, we get a segmentation fault. Could anyone tell us the reason for this?
Thanks!
Upvotes: 3
Views: 6250
Reputation: 19375
There are two errors in your scanf
usage, possibly based on one false assumption: You seem to mean that scanf
returns the loaded number in rdi
and that no further argument is needed with format "%d"
. In truth the number (if scanned successfully) is returned in the memory pointed to by the second argument. Thus, the following changes make the code work.
pop rbx, | ;delete
=
xor rax, rax, = xor rax, rax,
mov rdi, readInt, = mov rdi, readInt,
> mov rsi, rsp
call scanf, = call scanf,
mov rbx, rdi | pop rbx,
Concerning if the line mov rdi, printint, is removed, we obtain the correct values - I doubt that.
Upvotes: 2
Reputation: 22542
I don't understand why you have the C flag here, there is no C code involved, but to your question:
As far as I remember the calling convention in Linux glibc x64 for printf(format, argument)
is format in rdi
, argument in rsi
.
If you remove mov rdi, printsent,
then you are calling printf(undefined,"Please enter a number: ")
. You did not provide a format argument in rdi
, but printf
doesn't know that and uses whatever is in rdi
at that moment. Most likely an invalid memory address which thus invokes a SIGSEGV
.
By default function calls in x86 are supposed to be non-destructive on the arguments, though this is not a requirement. Standard library functions generally are. They achieve this by pushing the arguments on to the stack and reload them when done.
As such when you call scanf(readInt, ...)
it will restore the pointer to readInt
which has the same contents as printint
in rdi
when it returns. As such removing the line mov rdi, printint,
has no effect because rdi
contains a valid pointer to the format you need.
Upvotes: 0