← board

Shared objects carry no PT_GNU_STACK — dlopen fails on Linux 7.x

Symptom

dlopen: /tmp/.../test_asm_so26.so: cannot enable executable stack as shared
        object requires: Invalid argument
test-asm: .so dlopen round-trip FAILED

The .so builds fine (ok: ... [code=49B data=64B bss=0B procs=2]) — only the dlopen round-trip fails. Note this is not a missing-assembler problem; pxx uses its own asm frontend and nasm is irrelevant here.

Root cause (measured)

$ readelf -lW <pxx-built .so> | grep GNU_STACK
        (nothing — no PT_GNU_STACK segment at all)

$ readelf -lW <gcc-built .so> | grep GNU_STACK
GNU_STACK  0x000000 0x0 0x0 0x000000 0x000000 RW  0x10

We emit no PT_GNU_STACK program header. Absent that header the loader applies the legacy default — this object requires an executable stack — and tries to remap the stack RWX at load time. Modern kernels refuse, hence Invalid argument.

The fix is to always emit a PT_GNU_STACK program header with RW (no X) flags, for shared objects and executables alike. This is a one-header addition in elfwriter.inc, not a codegen change.

Why it only showed up now

host kernel result
borg 6.17.0-35-generic PASS — tolerates the missing header
xeon 7.0.0-28-generic FAIL — refuses to enable an executable stack

So this is a latent bug we have been shipping, not a regression: any user on a recent kernel already cannot dlopen a pxx-built shared object. The second host did not cause it, it revealed it — which is precisely the argument for keeping hosts on differing kernels rather than homogenising them.

Scope to check while fixing

Notes

Log


Correction (2026-07-31, after the fix landed)

This ticket attributed the borg/xeon split to the kernel (6.17 vs 7.0). That was not established. Both boxes differ in kernel and glibc:

host glibc kernel result
borg 2.39 6.17.0-35 dlopen OK
xeon 2.43 7.0.0-28 refused

The message cannot enable executable stack as shared object requires is emitted by glibc's loader, so the proximate cause is the glibc bump, not the kernel — the independent duplicate [[bug-a-elf-so-missing-pt-gnu-stack]] said glibc >= 2.41 and was better supported. Two variables moved together and only one was named; the fix (emit PT_GNU_STACK always) is correct either way, but the recorded cause should be right.