FreeBSD code reaches its thread pointer through %fs and macOS cannot set
the fs base. The macOS relinker turns each %fs access of a guest image
into a stub that reads the thread pointer from a pthread key and calls
Aps5GuestTlsAllocate on a thread's first access, registers the TLS
descriptor of an image with Aps5GuestTlsRegister, and routes
__tls_get_addr to Aps5GuestTlsGetAddr. No library provided them, so a
relinked executable could not start.
- Aps5GuestTlsRegister validates the descriptor the relinker wrote and
creates the key; its destructor frees the block of a thread.
- Aps5GuestTlsAllocateThreadPointer allocates the block (the TLS template
followed by zeros, then a 0x100-byte thread control block whose first
word points at itself, as on FreeBSD) and stores the thread pointer
under the key.
- Aps5GuestTlsAllocate is the entry the stubs call: rax holds the
descriptor and returns the thread pointer, every other register is
preserved, the upper halves of the ymm registers only where AVX exists.
- Aps5GuestTlsGetAddr returns the block of the thread plus the offset.
Co-authored-by: mugurc <[email protected]>
FreeBSD 11.4's sys___sysctl and userland_sysctl copy the name and the
old length in, and copy the value and the length out, so a bad pointer
gives EFAULT in that order. sysctl dereferenced them directly, and
sysctlbyname read its name with strcmp.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
- mlock on macOS wires only the pages libkernel has not wired yet. XNU
counts each wire of a page, and the locked ranges merge overlapping
locks, so a page locked twice stayed wired after munlock and
munlockall. The mlock test checks the wire count on macOS.
- mprotect changes a watched page through GuestWriteWatchProtect, which
applies the host protection and records it under the write watch's
lock, so a collection on another thread cannot run between the two.
The write watch test covers it.
- sceKernelVirtualQuery takes a watched page's write permission from the
write watch for memory outside the guest allocation registry.
- GuestAllocationsGuestPages counts an image segment up to the end of
its last host page, as the image page collection does.
- The macOS includes stay in main's include block, so the host code
other pull requests add there still sees them; the unused Mach-O
headers and the end trim, which never changed the protected spans,
are gone, and the guest page size is passed in.
- The libc and libkernel debt entries describe what is wired now.
- mprotect: the guest rounds a range to its 16 KiB pages. A Mach-O image
is loaded at a multiple of the host page size only (4 KiB under
Rosetta), so the pages are counted from where the ELF address 0 of the
image holding the range was loaded, and a rounded edge smaller than a
guest page that lies in the gap between two segments, where no host page
exists, is left out. The write watch is told the new protection.
- sceKernelVirtualQuery reads the region with mach_vm_region.
- sceKernelMunlockall unlocks the ranges libkernel locked, one by one:
macOS has no munlockall, which fails there with ENOSYS and would
overwrite errno.
- The stack protect test reads page protections with mach_vm_region.
This is mugurc's f66a4df741 from his macOS series, without the mlock
check that #10889 adds.
Co-authored-by: mugurc <[email protected]>