2

In my code i'm trying to print an array in reverse. My two main ideas were to either use the stack and the LIFO property to do it or use a loop as index from 10 to 0 zero to access the elements in reverse. I went with the second one due to alignments problems with the stack method.

I'm quite new to assembly and would appreciate some help on that one, to know where my error is. Thanks in advance !

DEFAULT REL
; external functions for inputs/outputs printf and scanf/printf
extern printf
extern scanf

section .data
prompt      db "Entrez un entier : ",0
longIntFormat  db "%ld",0


section .bss
entier resb 10       ; array of 10 integers


section.text
push rbp

    mov rcx, 0  
    mov rdx, 0     ; initialise counter
    lea rcx, [entier]    ; load array into register rcx

; fills the array with user input
_getLoop:
; call printf
    lea rdi,[prompt]
        mov rax,0
    call printf wrt ..plt

; call scanf
        lea rdi,[longIntFormat]
        lea rsi, [rcx + rdx]     ; array + Index
        mov rax,0
    call scanf wrt ..plt
    inc rdx                      ; inc. Index/counter
    cmp rdx, 10
    jl _getLoop                  ; While counter is less than 10 (size of array)

    mov rcx, 0          ; set rcx to 0
    mov rdx, 10         ; counter set to 10
    lea rcx, [entier]   ; load array into rcx

; print the array in reverse using the counter as Index
_printLoop:

; call printf
    lea rdi, [rcx + rdx]     ; rdi = [array + Index]
        mov rax,0
    call printf wrt ..plt
    dec rdx
    cmp rdx, 0               ; compare counter with 0
    jge _printLoop           ; Once 0 is reached the loop has gone through all the array

;restores registers
pop rbp

; returns 0 to C program
        mov     rax, 0            
        ret
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Ivan
  • 37
  • 8
  • 1
    Why do you have a `ret` after your `jl _getLoop`? This seems to prematurely return from the function. – zx485 Jan 02 '20 at 21:37
  • the ret after jl was an error, should the original value be preserved or each calls have an effect on rcx and rdx ? I don't really know from which lib printf and scanf are but if that can help they are defined as ```DEFAULT REL extern printf extern scanf``` – Ivan Jan 02 '20 at 21:52
  • What OS are you using? Calling conventions are different on different systems. – Nate Eldredge Jan 02 '20 at 23:22
  • Did your code get truncated? The last instruction is `jge` but what happens if the jump is not taken? It would also be good to see the code that calls this; i.e. the complete program, with everything a person would need to build and run it (including the commands used to build it!). In other words a [mcve]. – Nate Eldredge Jan 02 '20 at 23:25
  • Added the end of the code, i don't really know on what it can be runned, it's for an assignment and i'm running the code on a web based sort of command line. It is supposed to be using YASM, as my course is based on it – Ivan Jan 02 '20 at 23:42

1 Answers1

5

There are a lot of mistakes, e.g.:

  • A function can change several registers according to the calling convention
  • scanf and printf have a slightly different syntax for format strings. If you plan to modify the printf output (e.g. with a \n) you have to create another format string.
  • In your loop you forgot to pass any format string to printf, just the address of an integer. (printf needs a format string and takes integers by value.)
  • "%ld" means "long integer". On my sytem this is a Quadword (8 bytes). You are strictly dealing just with one byte.
  • Before calling a function the stack has to be aligned to a multiple of 16. The kernel follows the x86-64 System V ABI and makes sure this is the case on process entry (usually the entry point is called _start). If you push/pop, make sure you don't leave the stack misaligned before a call.
  • _start (the process entry point) is not a function; you can't ret from it. Call glibc's exit function to make sure stdio buffers are flushed, or make a raw _exit system call.
  • section.text is missing a space. It gets parsed as a label name like foo.bar: instead of a directive switching to the .text section. So your code ended up in .data (or perhaps .bss somehow), and segfaulted because those sections are linked into non-executable memory pages.

Take a look at my corrected - now working - program:

DEFAULT REL
; external functions for inputs/outputs printf and scanf/printf
extern printf, fflush
extern scanf

section .data
    prompt      db "Entrez un entier : ",0
    longIntFormat  db " %ld",0


section .bss
    entier resq 10              ; array of 10 integers


global _start
section .text

_start:
    ;and rsp, -16                ; Align stack to 16 (the ABI already guarantees this for _start)

    mov rbx, 0                  ; initialise counter

; fills the array with user input
_getLoop:
; call printf
    lea rdi,[prompt]
    mov rax,0
    call printf wrt ..plt

; call scanf
    lea rdi,[longIntFormat]
    lea rsi, [entier + rbx * 8]    ; array + Index
    mov rax,0
    call scanf wrt ..plt
    inc rbx                     ; inc. Index/counter
    cmp rbx, 10
    jl _getLoop                 ; While counter is less than 10 (size of array)

    mov rbx, 9                  ; counter set to 10

; print the array in reverse using the counter as Index
_printLoop:

; call printf
    lea rdi,[longIntFormat]
    mov rsi, [entier + rbx*8]   ; rdi = [array + Index]
    mov rax,0
    call printf wrt ..plt
    dec rbx
    cmp rbx, 0                  ; compare counter with 0
    jge _printLoop

    xor edi, edi                ; RDI=0: all streams
    call fflush  wrt ..plt

    mov rax,60                  ; SYS_EXIT
    mov rdi,0                   ; Exitcode: RDI=0
    syscall                     ; Call Linux64
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
rkhb
  • 14,159
  • 7
  • 32
  • 60
  • The program indeed works, i would like to know, whats the use of the fflush function ? Other than that the rest totally makes sense now, Thanks ! – Ivan Jan 02 '20 at 23:59
  • 1
    @HowLong: `printf` writes to a buffer that will not be output when the program ends with `SYSCALL`: https://stackoverflow.com/a/55799627/3512216. You cannot end the program with a `ret` due to the aligned (=changed) stack. – rkhb Jan 03 '20 at 00:06
  • @HowLong: More fundamentally, you can't ever `ret` from `_start`. It's not a function; on entry to `_start` RSP points at `argc` (and is guaranteed by the x86-64 System V ABI to be 16-byte aligned). `and rsp, -16` is a no-op at that point. You can assume your kernel and dynamic linker obey the ABI. (Your `push rbp` misaligned it, leading to scanf segfaulting: [glibc scanf Segmentation faults when called from a function that doesn't align RSP](//stackoverflow.com/q/51070716)) – Peter Cordes Jan 03 '20 at 07:38
  • 1
    @HowLong: I'd recommend using `call exit wrt ..plt` in a program that uses libc functions from `_start`. That will fflush and do whatever else might be necessary. Although it's still kind of a hack to use libc functions from `_start` instead of `main`; not recommended unless you know what you're doing, and only works because libc uses dynamic linker hooks to get itself initialized before your `_start` executes. Static linking would make your program break. – Peter Cordes Jan 03 '20 at 07:42