1

I've written the following program in NASM x86, but all I seem to get when I try to run it are segfaults (when trying to print) and infinite loops when I stop the printing. The code manages to call printNumber successfully ONCE, and then segfaults; because of this I am led to believe it is something happening in the second iteration of the loop. What am I missing here? I've added comments to my source to explain what's happening, but I cannot understand why this is throwing segfaults for what should be a simple program (don't have access to valgrind or gdb at the moment).

This is the output:

0Segmentation fault: 11

.. And here is the small program I wrote. What am I not seeing here?

SECTION .data
    arraylen dd 10
    num db 0
    farewell: db "Blah"
SECTION .bss
    array resq 8

SECTION .text
    global start

start:
    call fillArr
    mov rax, 0x02000001
    mov rdi, 0
    syscall

printNum:
    mov rsi, num
    mov rdx, 2
    call print
    ret

fillArr:
    lea rsi, [rel array] ; needed for 64 bit on mac
    xor rcx, rcx ; make this 0
    arrayloop:
        mov [rsi+rcx*8], rcx ; array element rcx updated with value
        mov rbx, [rsi + rcx*8] ; grab value at index we just filled

        add rbx, 48 ; now offset number by 48 to make it ascii'able
        mov [rsi+rcx*8], rbx ; overwritten the value we saw as it's ascii
        mov [rel num], rbx ; update storage var
        call printNum ; print
        inc rcx ; we don't need this anymore
        cmp rcx, 4
        jne arrayloop

    mov rsi, farewell
    mov rdx, 4
    call print
    ret

print:
    mov rax, 0x02000004
    mov rdi, 1
    syscall
    ret
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Jenna
  • 97
  • 7
  • What OS is involved? What operation is `rax=0x2000004` supposed to do? – wallyk Oct 17 '20 at 15:07
  • macOS, 64bit. I use the following commands to compile it: nasm -f macho64 arr.asm ld -macosx_version_min 10.7.0 -lSystem -o arrayTest arr.o That operation is equivalent to the sys call 4 on Linux (so print) – Jenna Oct 17 '20 at 15:10
  • 2
    I'd run it in a debugger (like `lldb`) step by step but just looking at the code one concern I have is that `syscall` (the instruction itself) clobbers RCX and R11. You don't use R11 but it does seem your code relies on RCX not being clobbered in function `fillArr`. You could try preserving RCX in function `print` by saving (ie: PUSH RCX) and restoring it before returning (ie: POP RCX) – Michael Petch Oct 17 '20 at 15:46
  • @wallyk: It was tagged MacOS from the start. Call numbers like that are what MacOS uses for "normal" POSIX system calls like `read` and `write`. Of course, good source code would comment each `syscall` with exactly what system call it was actually making. – Peter Cordes Oct 17 '20 at 17:45
  • @PeterCordes: I misread `macos` and thought it was `macros`! – wallyk Oct 17 '20 at 21:17

1 Answers1

4

The problem is here when called from printNum:

print:
    mov rax, 0x02000004
    mov rdi, 1
    syscall
    ret

As @MichaelPrefetch and @PeterCordes mentioned in the comments syscall clobbers rcx and r11. See also (it's a linux tagged answer but in this regard the kernel behavior is identical to MacOS in x86-64). If you're curious how it works under the hood when not single stepping with a debugger rcx after the syscall will contain its return address which will be your program next instruction (ret in this case).

Since your arrayloop logic relies on rcx afterwards this will fail with EXC_BAD_ACCESS in this line:

arrayloop:
    mov [rsi+rcx*8], rcx ; array element rcx updated with value

A possible fix might look like this:

print:
    mov rax, 0x02000004
    mov rdi, 1
    push rcx
    syscall
    pop rcx
    ret

which preserves rcx value on the stack and immediately restores its value right after the syscall.

As an alternative @PeterCordes suggests to refactor the loop not to rely on rcx at all. There are plenty of remaining registers to pick from(except aforementioned r11 and rax,rdx used for syscall return value).

Kamil.S
  • 5,205
  • 2
  • 22
  • 51
  • The normal way would be to pick a call-preserved register for a loop that contains function calls, rather than inventing a calling convention where functions save RCX. Also, `syscall` always destroys RCX, regardless of what value ends up there when it eventually returns. No real need to go into detail about what the kernel does after `syscall` itself destroys the old value before the kernel gets control; that's enough to explain why it's not preserved. – Peter Cordes Oct 17 '20 at 22:00
  • This was the issue! Thank you all for the help, can't believe I forgot about the syscall's side effects. – Jenna Oct 17 '20 at 22:24
  • @Kamil: Neither of us mentioned that syscall also clobbers R11 (although Michael Petch did in comments) >.<. The OP stepped on that next: [CMP Instruction and JE not firing](https://stackoverflow.com/q/64409124) Next time, remember to mention both destroyed registers. Or just link [Why does a syscall clobber rcx and r11?](https://stackoverflow.com/q/50571275) – Peter Cordes Oct 18 '20 at 05:02
  • @PeterCordes I've edited the answer in spirit of your remarks. Thank you. – Kamil.S Oct 18 '20 at 05:39