This is an article I wish I could have read many months ago.

> Hence, the most basic safety issue with setjmp is that if we call it and then return from the function that had called it, the context saved by setjmp is not valid to longjmp to.

> longjmp is only safe if it's called at a time when the stack frame used by setjmp could not have possibly been overwritten, since that is the only way to guarantee that the register state restored by longjmp matches the stack frame that the stack pointer points to.

That limitation could be lifted by simply copying the stack frames somewhere else prior to long jumping, and then spilling that entire thing on top of the current stack instead of just restoring the registers from the jump buffer. This is how delimited continuations work! What ruins this for C is the existence of pointers. Stacks aren't freely relocatable since pointers into the stack could exist. Other languages don't have this problem.

So much fun stuff in this article! The "fibers with ucontext", essentially swapping stack pointers back and forth, are how I implemented generators! I too reached for musl source code in order to understand setjmp, but for a different reason: its ability to spill the registers onto the stack was instrumental for my garbage collector.

Blogged about all of these things too, in case anyone is curious:

https://www.matheusmoreira.com/articles/delimited-continuati...

https://www.matheusmoreira.com/articles/generators-in-lone-l...

https://www.matheusmoreira.com/articles/babys-second-garbage...

I’ve used the copy-stack trick before! It’s really great!

You can work around the pointer relocation issue by always coping the stack back onto the main stack. So you’re always running on the same range of stack in memory and saved stacks are always elsewhere

Is this the technique you are describing?

https://langdev.stackexchange.com/a/4242

https://www.microsoft.com/en-us/research/wp-content/uploads/...

> How does he do it? By simply always restoring the continuation to exactly the place it was captured from, of course!

Pretty awesome. Gets around the problem by not relocating at all. I haven't read the full paper, to be honest. I just assumed it'd require defensive copying in case the stacks overlapped in memory.

Alexis King does outline the safety constraints that Fil-C would care about:

> references to stack-allocated data must not be shared across continuation chunk boundaries, as both capture and restore may relocate portions of the stack, making all references to stack-allocated memory in the relocated portions temporarily dangling

> What ruins this for C is the existence of pointers. Stacks aren't freely relocatable since pointers into the stack could exist.

I sometimes wonder what computing would be like if the 80286 hadn't sucked, if segmentation had won over flat address spaces, and if we'd been able to do relocation pain-free by changing a segment base register in one spot instead of rewriting linear pointers everywhere. We could have done paging within segments.

Oh well.

That'd only help for one object per address space. Main thing needing relocation - shared libraries - needs arbitrarily-many segment bases.

And when you're not a library, relocation is just a mild probabalistic security improvement (...that'd be massively-more bypassable than it already is if the program was littered full of gadgets of "read register as unrelocated offset and use it with its correct base" instructions).

mmap with MAP_FIXED has allowed you to do this for some time.

Not if you have an address space conflict, e.g. if you want to move a stack within a single process. Linear addresses are relative to a whole address space root.

> What ruins this for C is the existence of pointers. Stacks aren't freely relocatable since pointers into the stack could exist. Other languages don't have this problem

What about languages with pass by reference?

I suppose it would depend on the implementation. References could be implemented with absolute pointers, or with base pointers plus offsets.

Base pointers are trivially relocatable. Just set the base pointer and it's done. This is how my language supports stack expansion: just reallocate the memory, overwrite base pointers, elements always dereference base plus offset, done. The heap is also implemented this way. All objects are indexes into a massive object array, and the base pointer is implicit.

https://www.matheusmoreira.com/articles/lone-lisp-heap

Absolute pointer references are not easily relocatable. You'd need to rewrite every single pointer. I've never seen anyone actually do this in C since it'd hit the same problem conservative garbage collectors do: you might rewrite an integer variable that just happens to look like a pointer.

If you're audacious enough you can try to somehow remap the stacks in the exact same spot they used to be at in order to not invalidate the pointers to begin with:

https://langdev.stackexchange.com/a/4242

https://www.microsoft.com/en-us/research/wp-content/uploads/...

Some languages like Go manage to do it because they just know where all the pointers are in most circumstances. They tend to choke only on foreign code they can't introspect into.