jonrob.net



OpenBSD x64 Assembly

This past week I've been learning x86 assembly language on an old laptop, purely because I couldn't get anything running on my x64 OpenBSD machine. My 64 bit helloworld.s was as follows:
section .data

 msg db "Hello world!", 10
 len equ $-msg

;syscalls
 %define SYS_exit 1
 %define SYS_write 4

section .text

global _start
_start:

 mov rax, SYS_write
 mov rdi, 1 ;stdout
 mov rsi, msg
 mov rdx, len

 syscall

 mov rax, SYS_exit
 xor rdi, rdi

 syscall
Giving the errors:
jon@OpenBsD:~/dev/asm;$ nasm -f elf64 -o helloworld.o helloworld.s
jon@OpenBsD:~/dev/asm;$ ld -o helloworld helloworld.o
ld: warning: creating a DT_TEXTREL in a shared object.
jon@OpenBsD:~/dev/asm;$ yasm -f elf64 -o helloworld.o helloworld.s
jon@OpenBsD:~/dev/asm;$ ld -o helloworld helloworld.o 
ld: helloworld.o: relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC
helloworld.o: could not read symbols: Bad value
I did search /usr/src for any assembly files I could straight up copy and test, but I didn't find anything suitable, however the code looks fine and as the assembler didn't throw out any errors I'm thinking this to be an issue with the linker. Running ld with the verbose flag we are shown the options for available architectures:
jon@OpenBsD:~>$ ld -V
GNU ld version 2.17
  Supported emulations:
   elf_x86_64_obsd
   elf_i386_obsd
   elf_i386
But even specifying the architecture gives the same error, so I then searched for -fpic as the yasm compiled program error suggested and found in man 1 gcc the option -fpic/-fpie. I don't know much about position independent code but I found that the ld flag -nopie disables it.
jon@OpenBsD:~/dev/asm>$ nasm -f elf64 -o helloworld.o helloworld.s
jon@OpenBsD:~/dev/asm>$ ld -m elf_x86_64_obsd -o helloworld -nopie helloworld.o
jon@OpenBsD:~/dev/asm>$ ./helloworld
./helloworld[1]: ELF: not found
./helloworld[3]: no closing quote
Progress! After digging into this new error I found in man 5 elf "that OpenBSD native executables contain a .note.openbsd.ident section to identify themselves, for the kernel to bypass any compatibility ELF binary emulation tests when loading the file." Looking online for this note section I found multiple examples which where all the same so I copied that into my program:
section .note.openbsd.ident
 align 2
 dd 8, 4, 1
 db 'OpenBSD',0
 dd 0
 align 2

section .data

   msg db "Hello world!", 10
   len equ $-msg

;syscalls
   %define SYS_exit        1
   %define SYS_write       4

section .text

global  _start
_start:

   mov  rax, SYS_write
   mov rdi, 1; stdout
   mov rsi, msg
   mov rdx, len

   syscall

   mov rax, SYS_exit
   xor rdi, rdi

   syscall
and compiled with:
jon@OpenBsD:~/dev/asm>$ nasm -f elf64 -o helloworld.o helloworld.s             
jon@OpenBsD:~/dev/asm>$ ld -m elf_x86_64_obsd -o helloworld -nopie helloworld.o
jon@OpenBsD:~/dev/asm>$ ./helloworld
Hello world!
Success!
2020-07-03 Update
The default ld in OpenBSD has recently changed from GNU LD(ld(1)) to LLVM LD(ld.lld(1)), the above still works if you replace the ld command with ld.bfd.
Alternatively to make the change to ld.lld(1) you will need to upgrade to nasm-2.15 which is currently in ports and then run:
sed -i 's/.note.openbsd.ident/.note.openbsd.ident note/' helloworld.s
nasm -f elf64 -o helloworld.o helloworld.s
ld -m elf_x86_64 -o helloworld -no-pie helloworld.o
Also the notes alignment is automatically set to 4 bytes instead of 2 so the alignment no longer needs to be specified in nasm 2.15.src.