Search code examples
cmacosapple-m1arm64setjmp

Which registers are stored in jmp_buf in apple silicon arm64 environment?


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?


Solution

  • 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)1
    • The lower 64 bits of q8 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.