Question Details

No question body available.

Tags

x86 linker nasm elf openbsd

Answers (1)

Accepted Answer Available
Accepted Answer
November 19, 2025 Score: 3 Rep: 88,845 Quality: Expert Completeness: 80%

This answer is based on the comments on the question. There is no official, explicit documentation about this behavior.

OpenBSD 7.3 has introduced execute-only pages. More specifically, it interprets pflags (in Elf32Phdr) for ptype == PTLOAD like this:

  • The value 4 means read-only.
  • The value 5 means execute-only. In OpenBSD =6.0, even with mmap(2). (This is the famous W^X protection check.)

On OpenBSD 7.3--7.8 amd64, the execute-only permission is fully enforced: attempting to read bytes from such a page directly causes a segmentation fault (SIGSEGV), and attempting read bytes from such a page using a syscall (such as write(2)) makes the syscall fail with EFAULT.

On OpenBSD 7.3--7.8 i386, the execute-only permission is partially enforced because of limited CPU support: attempting to read bytes from such a page directly still works (thus not enforced), but attempting read bytes from such a page using a syscall (such as write(2)) makes the syscall fail with EFAULT (thus enforced).

Possible workarounds for OpenBSD >=7.3:

  • Emit ELF section .rodata (read-only) as a separate Elf32Phdr segment with pflags == 4. (ELF section .text is still emitted with pflags == 5, now meaning execute-only.) This is what ld does (including on other OSes like GNU/Linux.)
  • Alternatively, on i386 only, for each syscall which has to read bytes from a page with pflags == 5, copy the bytes to a page with p_flags == 6 first. Code example: see rep movsd in w2.nasm in the question. Please note that this may break in future versions of OpenBSD (>7.8).