Rohan Mahtani
Rohan Mahtani

Reputation: 31

C calling conventions in assembly (64 bit) - scanf

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

Answers (2)

Armali
Armali

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

Sergey L.
Sergey L.

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

Related Questions