Reliable cross-platform way to fetch context/individual registers?
⚓ Rust 📅 2026-04-27 👤 surdeus 👁️ 1Thought I'd give a shot at trying to implement my own garbage collector. One of the requirements for a conservative implementation seems to involve scanning everything that looks like a root. Including the stack, as well as the CPU's GPRs the compiler might have stored a pointer in.
After a bit of research, there appears to exist a whole bunch of ways to skin this cat:
getcontextfor Linux, orGetThreadContextfor Win32- Linux-only
setjmp, which is not exactly reliable - manual architecture-specific
asm!write-up - a cross-platform (?) version of 1.
- interrupting the thread with a signal, then 1. again
The 4. appears to be the right tool for the job. Yet the specifics of the how I'm meant to be calling into it elude me somewhat. The function itself expects a *mut ucontext_t. Which I can only provide it, safely, through a MaybeUninit, be it zeroed or otherwise. Which must first be called itself, thus scrambling the register values I was looking for, to begin with. By the time getcontext returns, what are the chances I'm reading the exact GPR values as they were before the call?
Assuming there is a way around this particular part, what do I do about the stack frame of the function I'm calling it to begin the scanning of roots, itself? For instance, at any part in my main program, I may want to run my GC. Thus, I call into GC::collect. Which is where I'm fetching GPRs via getcontext or similar. Yet by the time the function call is underway and I'm in the frame of the GC::collect, the registers themselves are no longer the same as they were before the call.
What I'm looking for are the GPRs at the exact moment I choose to run the GC, not what they are a few frames deeper where I might be making my call into the getcontext. Am I supposed to have the GC::collect to be not a function, but an inlined collect! macro instead? To record at which point in the stack I'm about to call into collect, then inside the function itself scan the last few dozens of bytes leading up to that point; just to ensure any registers spilled onto the stack in the given prologue/epilogue are still properly scanned and accounted for?
"Talking" to an LLM about this clearly was a mistake. Whatever "intuition" and clarity it might have imparted to me at the beginning quickly turned into a bunch of self-contradicting nonsense: from "you must getcontext use to scan for any pointers in the registers", to "you're absolutely right, machine-specific asm! is the way to go", to "I apologize, you're absolutely right, there's no way to capture the registers at any one moment in any given thread from within itself, as it's only possible by suspending it from outside by a signal, before fetching the context inside the signal's handler".
I simply can't imagine how someone could let leave entire codebase at the mercy of a GPT assistant, at this point. But that has quite little to do with the topic at hand. So, any pointers?
2 posts - 2 participants
🏷️ Rust_feed