Compact unwind describes how to unwind each function with one 32-bit value, instead
of DWARF call frame information. The compiler writes a table of them into object
files (__LD,__compact_unwind) and the linker merges those into a compact
two-level form in the program (__TEXT,__unwind_info).
The 32-bit encoding
| Bits | Meaning |
|---|---|
| 31 | Set when this entry does not start a function. |
| 30 | Set when the function has an LSDA. |
| 28–29 | The personality function’s index, counted from 1; 0 for none. |
| 24–27 | The mode, by processor, below. |
| 0–23 | Mode-dependent. |
| Mode | x86-64 and x86 | ARM64 |
|---|---|---|
| 1 | Frame: rbp/ebp based. Bits 16–23 give the offset of the saved registers from the frame pointer, in pointer sizes. The low 15 bits hold five 3-bit register numbers. | |
| 2 | Frameless, small stack: bits 16–23 give the stack size in pointer sizes. | Frameless: bits 12–23 give the stack size divided by 16. |
| 3 | Frameless, large stack: the size is read from the code. | DWARF: the low 24 bits are the offset of the FDE in __eh_frame. |
| 4 | DWARF: the low 24 bits are the offset of the FDE. | Frame: x29 and x30 are saved. Bits 0–4 mark pairs x19–x28 saved; bits 8–11 pairs d8–d15. |
| 5 | Frame on arm64e, with the LR signed with pacibsppc: bits 12–23 are an offset from the start of the range, in units of 4 bytes. |
The register bits in x86-64’s frameless modes and the shifting of ARM64’s pairs are
set out in <mach-o/compact_unwind_encoding.h>.
__LD,__compact_unwind
One record per function or range, in object files. Fields are pointer-sized (8 or 4 bytes) unless noted:
| Field | Size | Meaning |
|---|---|---|
| function | pointer | The start of the function or range. |
| length | 4 | Its length. |
| encoding | 4 | The 32-bit encoding. |
| personality | pointer | The personality function, or 0. |
| lsda | pointer | The LSDA, or 0. |
__TEXT,__unwind_info
0header: version 1, then an offset and count for each table28 bytes…common encodings: 32-bit encodings used by many pages4 bytes each…personalities: 32-bit offsets to the personality pointers4 bytes each…first-level index: the first function of each page and where the page is12 bytes each…LSDA index: function and LSDA offsets8 bytes each…second-level pages: functions and their encodingsvaries
__unwind_info.| Header field | Size | Meaning |
|---|---|---|
| version | 4 | 1. |
| commonEncodingsArraySectionOffset, Count | 4 each | The common encodings. |
| personalityArraySectionOffset, Count | 4 each | The personalities. |
| indexSectionOffset, Count | 4 each | The first-level index. |
A first-level entry is functionOffset,
secondLevelPagesSectionOffset and
lsdaIndexArraySectionOffset, 4 bytes each. Function offsets are from the
Mach header. The last entry only marks the end, with no page of its own.
Regular pages
kind 2 (4 bytes), then entryPageOffset and
entryCount (2 bytes each), then 8-byte entries of
functionOffset and encoding, up to 511 of them.
Compressed pages
kind 3, entryPageOffset, entryCount,
encodingsPageOffset and encodingsCount, then 4-byte
entries: the low 24 bits are a function offset from the first-level entry’s
function, and the high 8 bits an index of an encoding. Indexes below the common
encodings’ count are common encodings; the rest are in this page’s own encodings
array. A compressed page can hold up to 1,021 entries.