I want to write a native coroutine in my Apple silicon Macbook. Since the native coroutine need the jmp_buf
in setjmp.h
, I tried to figure out which registers are stored in the jmp_buf
.
When I jump into the setjmp.h
file in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk/usr/include/setjmp.h
,I just found out the code but it seems a little bit weird.
The code is:
/*
* _JBLEN is the number of ints required to save the following:
* r21-r29, sp, fp, lr == 12 registers, 8 bytes each. d8-d15
* are another 8 registers, each 8 bytes long. (aapcs64 specifies
* that only 64-bit versions of FP registers need to be saved).
* Finally, two 8-byte fields for signal handling purposes.
*/
#define _JBLEN ((14 + 8 + 2) * 2)
typedef int jmp_buf[_JBLEN];
typedef int sigjmp_buf[_JBLEN + 1];
#else
# error Undefined platform for setjmp
#endif
The _JBLEN
seems defined 14 + 8 registers but the comment said 12 + 8. As other post saying, in arm64 arch the x19-x28 are callee saved registers which should be saved and x29(fp), x30(lr), x31(sp), pc also should be saved. In that case, 14 registers seems make sense. Is the comment wrong or my calculation is wrong?
How can I figure out which registers are exactly saved in the jmp_buf, and if the MacOSX sdk's comment is wrong, how can I correct it?
The comment is obviously wrong, since it misses x19
and x20
, and it mentions x29
and fp
, which are one and the same.
Here's what the ABI declares as callee-saved:
x19
through x30
(includes x29
= fp
and x0
= lr
)1q8
through q15
, i.e. d8
-d15
sp
There's no point in saving pc
, since you know where you're currently executing from. So really you need 13 + 8
.
Now, Apple's source dumps do include an implementation of setjmp
, but that source dump is a lie because the actual implementation used in production uses pointer authentication and is so far closed source. I could dump you the disassembly of that, but I think that'd be a bad idea. The whole reason that jmp_buf
is declared as an opaque blob is because its layout is private and the implementations of setjmp
and longjmp
are allowed to change.
If you need the ability to modify a jmp_buf
, then you're better off just writing your own assembly routines that spill/reload the callee-saved registers.
1x30
is callee-saved in the sense that the function is expected to return there. But in the context of setjmp
, it needs to be saved.