Search code examples
assemblysegmentation-faultx86-64yasm

x86 Assembly: Segmentation Fault (Core dumped) while trying to reverse print array


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

Solution

  • 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