[Bug 286875] emulators/qemu-user-static-devel: Switch to blitz, and sync to 11.0.50; restore build on 15+

From: <bugzilla-noreply_at_freebsd.org>
Date: Mon, 14 Sep 2026 07:55:13 UTC
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=286875

--- Comment #38 from Rick Richard <rick@sloservers.com> ---
Testing blitz HEAD (e725658f, VERSION 11.0.50) on FreeBSD 15.1-RELEASE-p2 amd64
with an armv7 guest jail, per comment #10.

blitz HEAD builds, but does not execute guest binaries on 15.1. Six defects,
all
in bsd-user, none of them port-side. The pull request below carries two further
fixes found afterwards: a lost thread wakeup, which armv7 does not reach, and
accept4(2) dropping its flags, which it does:

    https://github.com/qemu-bsd-user/qemu-bsd-user/pull/68

1. target_mmap() checks MAP_EXCL against the guest address space with
   page_check_range_empty() and then forwards the flag to the host mmap. When
   reserved_va is set, qemu has already mapped the guest range as a PROT_NONE
   reservation, so MAP_FIXED|MAP_EXCL returns ENOMEM for every PT_LOAD segment
   the ELF loader places inside it. No guest binary reaches its entry point.

2. cap_rights_limit(2), cap_ioctls_limit(2), cap_fcntls_limit(2) and
   cap_enter(2) return ENOSYS, and the cpu loop raises SIGSYS for any syscall
   that returns ENOSYS. /bin/echo calls cap_rights_limit(2) during startup and
   exits on SIGSYS.

3. TARGET_BIG_ENDIAN is written into the per-target config header as a value:

       arm-bsd-user-config-target.h:  #define TARGET_BIG_ENDIAN 0
       ppc-bsd-user-config-target.h:  #define TARGET_BIG_ENDIAN 1

   Four sites in bsd-user still test it with #ifdef, which matches on every
   target, so little-endian targets compile the big-endian branch. In
   do_bsd_lseek() the two halves of the 64-bit off_t return are swapped:
   lseek(fd, 0, SEEK_END) on a 154556-byte file returns 154556 << 32.
   pkg-static reports "Seek error" and cannot extract packages. This is
   issue #28, which also notes the ppc site.

4. loader_exec() resolves the program with realpath() into a stack array and
   stores that pointer in bprm->fullpath, which outlives the call. The guest
   reads it back through KERN_PROC_PATHNAME and gets an empty string. perl
   derives $^X from that sysctl, so lang/perl5 fails in mkppport, which invokes
   $^X and reaches the emulator binary instead of miniperl.
devel/p5-Locale-gettext
   and devel/p5-Locale-libintl, the ports in issues #24 and #29, build with
this
   applied.

5. posix_fadvise(2) is unimplemented, reaches the default case, and returns
   ENOSYS, which becomes SIGSYS. xz(1) calls it, and devel/gettext-tools dies
   in its autotools directory.

6. qemu_thread_create() leaves SIGSEGV, SIGFPE and SIGILL unblocked in helper
   threads, since blocking a synchronous fault is undefined. A process-directed
   signal can therefore arrive on a thread that runs no guest code, where
   thread_cpu is NULL, and host_signal_handler() dereferenced it. A guest that
   blocks SIGFPE and calls kill(getpid(), SIGFPE) crashes qemu. This is
   issue #34.

Results for the libthr regressions tracked in issues #30 to #37. Three columns,
so that upstream's work and these patches are not conflated:

  - 6.2.50: the version the port currently builds, with the #37 batching fix
    from ab12ac38ef backported so the comparison is not against a known-broken
    umtx path
  - blitz: blitz HEAD with only the two patches needed to execute a guest at
    all (items 1 and 2 above)
  - blitz+8: blitz HEAD with the full series

                                          6.2.50   blitz    blitz+8
    #37  umtx_op_test:batching            pass     pass     pass
    #36  timedmutex_test:mutex3           fail     pass     pass
    #35  timedmutex_test:mutex2           pass     pass     pass
    #34  sigmask_test:upcalls_not_started fail     fail     pass
    #33  sem_test:before_start_one_thread timeout  pass     pass
    #32  sem_test:before_start_no_threads timeout  pass     pass
    #31  mutex_test:mutex2                timeout  pass     pass
    #30  cond_test:cond_timedwait_race    fail     pass     pass

Six of these are already fixed on blitz and owe nothing to the patches above;
the middle column is there to make that clear. Only #34 needed a new fix, item
6. The first column is the argument for moving the port off 6.2.50.

Regarding comment #28 and the reliable hang creating the pkg package: item 3
looks like the cause. pkg-static uses lseek heavily and was receiving offsets
with the halves transposed. That is a hypothesis rather than a confirmed
diagnosis, since I did not reproduce the hang before fixing the endianness
test,
but pkg-static has been exercised heavily since and has not hung.

With the first six applied, poudriere cross-built 80 armv7 packages: 80 built,
0
failed, 0 skipped, in 1h40m. A 14-port request expanded to that closure,
covering perl5, python312, ruby34, git, openssl, sqlite3, curl, cmake, nginx,
bash, libarchive, texinfo and the autotools. Every package went through
pkg-static create, and the run also exercises pkg-static add.

Host: FreeBSD 15.1-RELEASE-p2 amd64, 8 cores. Guest jail: 15.1-STABLE 1501502,
arm.armv7, built with poudriere jail -m git+https. Ports tree 8fd09ff988d2.

I am not proposing a port patch; the work in this bug covers what I ran into.
One port-side item in case it is not already handled: qemu 11 emits the target
binaries directly into ${BUILD_WRKSRC} with no per-target *-bsd-user
subdirectories, so an ELF_FEATURES setting of the form
"+wxneeded:*-bsd-user/qemu-*" matches nothing and elfctl fails the build.
Deriving the list from QEMU_TARGETS works:

    ELF_FEATURES=       ${QEMU_TARGETS:@t@+wxneeded:qemu-${t:S/-bsd-user//}@}

-- 
You are receiving this mail because:
You are the assignee for the bug.