MACH-FILE(3)

πŸ“¦ version 0_git20260208, in alpine 3.24, arch rolling, nixos 26.05, nixpkgs unstable

πŸ’… MACH-FILE(3) β€” machine-independent access to executable files and running processes

okay so bestie, welcome to MACH-FILE(3) from the Library Functions Manual β€” this is the page where we learn how to peek inside executables and vibe-check running processes without caring what machine you're on. it's giving very "one API to rule them all" and honestly? obsessed. πŸŽ€

There's also a whole crew of functions here: crackhdr, uncrackhdr, mapfile, unmapfile, mapproc, unmapproc, detachproc, ctlproc, and procnotes. yes that's a lot of names, no you don't need to memorize them all at once, breathe. πŸ’–


✨ SYNOPSIS ✨

#include <u.h>
#include <libc.h>
#include <mach.h>

int   crackhdr(int fd, Fhdr *hdr)
void  uncrackhdr(Fhdr *hdr)

int   mapfile(Fhdr *hdr, ulong base, Map *map, Regs **regs)
void  unmapfile(Fhdr *hdr, Map *map)
int   mapproc(int pid, Map *map, Regs **regs)
void  unmapproc(Map *map)
int   detachproc(int pid)
int   ctlproc(int pid, char *msg)
int   procnotes(int pid, char ***notes)

πŸ¦‹ DESCRIPTION πŸ¦‹

So like… these functions parse executable files and also give you access to those files and to running processes. double duty, main character energy fr fr. πŸ‘‘

πŸ“ cracking the header

crackhdr opens and parses the named executable file. the data structure it hands back, hdr, is initialized with a machine-independent description of the header info β€” like, the header but make it portable. ✨

here are the fields you'll actually use, the ones everyone screams about:

  • mach β€” a pointer to the Mach structure for the target architecture.
  • mname β€” the name of the target architecture.
  • fname β€” a description of the kind of file (e.g. executable, core dump). the vibe check of the file, basically.
  • aname β€” a description of the application binary interface this file uses; typically it's the name of an operating system.
ngl there's a little footnote situation here: if the global variable mach is nil, crackhdr points it to the same Mach structure. sneaky but helpful. πŸ™ˆ

Also, uncrackhdr is the one that undoes the crack β€” it's the clean-up bestie. 🧹

🌸 mapping files

mapfile adds the segments found in hdr to map. if hdr is an executable file, there are typically three segments: text, data, and a zero-backed bss. classic trio, iconic lineup. πŸ’…

if hdr is a dynamic shared library, its segments get relocated by base before being mapped.

if hdr is a core file, there's one segment named core for each contiguous section of memory recorded in the core file. and there are often quite a few of these, because most operating systems omit clean memory pages when writing core files β€” Mac OS X is the only exception among the supported systems, so shoutout to them I guess. 🍎

because core files have such holes, it's typically necessary to construct the core map by calling mapfile on the executable and then calling it again on the core file. newly-added segments are mapped on top of existing segments, so this arrangement uses the core file for the segments it contains but falls back to the executable for the rest. layers, on layers, on layers β€” it works. 🫢

unmapfile removes the mappings in map corresponding to hdr. bye felicia. πŸ‘‹

🧚 mapping processes

mapproc attaches to a running program and adds its segments to the given map. it adds one segment for each contiguous section of mapped memory. on systems where this info can't be determined, it adds a single segment covering the entire address space.

⚠️ okay important: accessing areas of that segment that are actually not mapped in the process address space will cause the get/put routines to return errors. so like, tread carefully, bestie. πŸ’€

unmapproc removes the mappings in map corresponding to pid.

detachproc detaches from all previously attached processes. it's the "we're done here, no hard feelings" of the group chat. ✌️

πŸ’œ controlling processes

ctlproc manipulates the process with id pid according to the message msg. the valid messages are:

  • kill β€” terminate the process.
  • startstop β€” start the process and wait for it to stop.
  • sysstop β€” arrange for the process to stop at its next system call, start the process, and then wait for it to stop.
  • waitstop β€” wait for the process to stop.
  • start β€” start the process.

πŸŽ€ notes on notes

procnotes fills *notes with a pointer to an array of strings representing pending notes waiting for the process. (on Unix, these notes are textual descriptions of any pending signals.) it returns the number of pending notes. πŸ”₯

the memory at *notes should be freed via free (see malloc(3)) when no longer needed β€” yes, actually free it, don't just ghost it, that's rude to your heap. 😭


🌈 SOURCE 🌈

/src/libmach β€” that's where she lives. 🏠


⭐ SEE ALSO ⭐

go read those too, they're literally the extended universe. iykyk. πŸ‘‘


and that's the whole page, bestie! this was MACH-FILE(3) from the Library Functions Manual, living in /src/libmach, brought to you with love and a little free(3) reminder at the end. mwah. πŸ’–πŸŽ€πŸ¦‹πŸŒΈπŸ’