Solved x86_64 assembly "Hello, world!" segfaults even though exit syscall has been used

I recently started trying to program in FreeBSD x86_64 assembly, following some tutorials and I ended up with such hello world program.
Code:
section .data
    hello     db "Hello, world!", 10

section .text
global _start
_start:
    mov     rdi, 1
    mov    rsi, hello
    mov    rdx, 14
    mov     rax, 4
    syscall

    mov    rax, 1
    mov    rdi, 0
    syscall
Everything seems to be right, the write syscall, which is 4 is called with the correct arguments, the exit syscall which is 1 is also called with the correct argument in the rdi register. I compile the file using yasm: yasm -f elf64 hello.asm -o hello.o and it compiles without errors. I link the file using ld: ld -o hello hello.o and there are no errors here either.
Running the file yields this output:
Code:
Hello, world!
fish: Job 1, './hello' terminated by signal SIGSEGV (Address boundary error)
I do not know what I can do about this. I tried using objdump to ensure that the code is right.
Code:
> objdump -d hello.o

hello.o:    file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <_start>:
       0: 48 c7 c7 01 00 00 00             movq    $0x1, %rdi
       7: 48 c7 c6 00 00 00 00             movq    $0x0, %rsi
       e: 48 c7 c2 0e 00 00 00             movq    $0xe, %rdx
      15: 48 c7 c0 04 00 00 00             movq    $0x4, %rax
      1c: 0f 05                            syscall
      1e: 48 c7 c0 01 00 00 00             movq    $0x1, %rax
      25: 48 c7 c7 00 00 00 00             movq    $0x0, %rdi
      2c: 0f 05                            syscall
Everything looks fine.
 
it works for me.
just had to brandelf the executable freebsd
truss ./a
Hello, world!
write(1,"Hello, world!\n",14) = 14 (0xe)
exit(0x0)
process exit, rval = 0
 
BSD uses the C calling convention (pushing arguments on the stack), Linux uses registers.

Pushing arguments onto the stack in right-to-left order and thus following the convention like this
Code:
section .data
    hello     db "Hello, world!", 10, 0

section .text
global _start
_start:
    push    14
    push    hello
    push     1
    mov     rax, 4
    syscall

    push    0
    mov    rax, 1
    syscall
results in not even a "Hello, world!".
It compiles successfully using the same yasm command and it links without errors. Upon runtime it just segfaults.
 
[…] Everything seems to be right, […] the exit syscall which is 1 is also called with the correct argument in the rdi register. […]
How did you confirm this? echo $? or truss(1)? If there is a segment violation, it strongly suggests that sys_exit was not successful.​
[…] it compiles without errors. […]
Assembly programs are not “compiled”. You say compilation if you are translating from a more abstract programming language to a more concrete “programming” language. For instance Java → Java Bytecode.​
BSD uses the C calling convention (pushing arguments on the stack), Linux uses registers.

That is for the int 0x80 gate. Krallbert uses syscall and FreeBSD complies with the x86 processor supplement ABI here.​
[…] just had to brandelf the executable freebsd […]
As far as I understand yasm(1) is a refactored nasm(1). So far I’ve used only nasm(1) and never had to brandelf(1) it (unless using Linuxlator). Maybe yasm(1) isn’t “as smart” as nasm(1) yet and you’re supposed to insert .osabi stanzas yourself?​
 
Ah, I see the error.
For some reason the program tries to call Linux syscalls, not FreeBSD. It thinks that the second one is not an exit, but a write. It thinks that the first one is whatever syscall 4 is on linux.
 
Ah, I see the error.
For some reason the program tries to call Linux syscalls, not FreeBSD. It thinks that the second one is not an exit, but a write. It thinks that the first one is whatever syscall 4 is on linux.
Yes, that makes sense. The values in rsi and rdx don't get edited by whatever linux syscall 4 is and they go on to the next syscall which writes them... to stdin?
 
BSD uses the C calling convention (pushing arguments on the stack), Linux uses registers.


OP is asking about amd64, not i386.

On amd64 the syscall interface is similar to the amd64 ABI for function calls. The syscall number goes in RDI, the first 5 syscall arguments go in RSI RDX R10 R8 R9 and any further arguments go on the stack.
 
Back
Top