Search code examples
assemblyx86segmentation-faultnasm

I am getting segmentation fault - assembly


I'm trying assembly x86 but I got the zsh: segmentation fault ./test error. I am trying to make some basic library myself to use later on. It's divided into three files - string.asm for string manipulation, stdio.asm for standard io operations, and libtest.asm for library testing. I was planning to add more functions, but I wanted to test these first.

;;;;;;;;;;;;;;;;;
;;; stdio.asm ;;;
;;;;;;;;;;;;;;;;;
global print ; void print(string* text)
extern strlen ; int32 strlen(string* string)

section .data
stdin dd 0
stdout dd 1
stderr dd 2
newline db 0x0a, 0x00

section .text

print:
    push ebp
    mov ebp, esp
    push eax
    push ebx
    push ecx
    push edx
    
    ; get parameters
    mov ecx, dword [ebp+8]
    ; call strlen
    push ecx
    call strlen
    add esp, 4
    ; moving length
    mov ecx, eax
    ; moving syscall num and out desc
    mov eax, 4
    mov ebx, [stdout]
    ; syscall
    int 0x80
    
    pop edx
    pop ecx
    pop ebx
    pop eax
    mov esp, ebp
    pop ebp
    ret

;;;;;;;;;;;;;;;;;
;; string.asm ;;;
;;;;;;;;;;;;;;;;;
global strlen ; int32 strlen(string* str)

section .text
strlen:
    push ebp
    mov ebp, esp
    push ebx
    
    mov ebx, dword [ebp+8]
    
    mov eax, 0
    loop1:
        cmp [ebx+eax], byte 0x00
        inc eax
        jne loop1
    dec eax
    
    pop ebx
    mov esp, ebp
    pop ebp
    ret

;;;;;;;;;;;;;;;;;
;; libtest.asm ;;
;;;;;;;;;;;;;;;;;
global _start

extern print

section .data
msg db 'Hello, world!', 0x0a, 0x00

section .text
_start:
    push msg
    call print
    add esp, 4
    
    mov eax, 1
    mov ebx, 0
    int 0x80

Execution:

$ nasm -f elf32 stdio.asm

$ nasm -f elf32 string.asm

$ nasm -f elf32 libtest.asm

$ ld -m elf_i386 -o test stdio.o string.o libtest.o

$ ./test

I don't know where the error could be, so I would appreciate any help from you guys.

Thanks!


Solution

  • Two bugs:

        ; moving length
        mov ecx, eax
        ; moving syscall num and out desc
        mov eax, 4
        mov ebx, [stdout]
        ; syscall
        int 0x80
    

    Referring to Linux system call conventions, the write system call needs the buffer pointer in ecx and the length in edx. You have the length in ecx and the buffer pointer is nowhere at all. Make it:

        mov edx, eax
        mov ecx, dword [ebp+8]
        mov eax, 4
        mov ebx, [stdout]
        int 0x80
    

    Next, look at:

            cmp [ebx+eax], byte 0x00
            inc eax
            jne loop1
    

    The inc instruction sets the zero flag according to its output. So your jne doesn't branch on the result of the cmp, but rather on whether eax was incremented to zero (i.e. wrapped around). So your loop will iterate far too many times.

    The jne needs to be immediately after the cmp, with no other flag-modifying instructions in between. There are several ways you could rewrite. One would be:

        mov eax, -1
    loop1:
            inc eax
            cmp byte [ebx+eax], 0x00
            jne loop1
    

    Note this eliminates the need for the extra dec eax at the end.

    After fixing these, the program works for me.