pub enum CfiOp {
DefCfa {
reg: u16,
offset: i32,
},
DefCfaOffset(i32),
DefCfaRegister(u16),
Offset {
reg: u16,
offset: i32,
},
Restore(u16),
RememberState,
RestoreState,
}Expand description
One row of the table that says what the frame looks like at a given instruction.
Design: spec/11-asm-objects-debug.md section 11.6.
An unwinder is handed a return address and has to answer two questions about the function it landed in: where the caller’s stack pointer was, and where the caller’s copy of each register this function overwrote went. The first answer is a register and an offset and is called the canonical frame address. The second is one entry per register that was saved. Both change as the prologue runs, which is why this is a table over the function and not a fact about it.
The register numbers here are DWARF’s and not the machine’s, because the two disagree on
x86-64 and there is no reason to write the mapping down twice: rucc-target holds it, the
prologue asks for it once, and the number that comes out is the number the listing prints and
the number the FDE encodes. That is why this enum can live in a crate that knows nothing about
any particular machine.
Every row takes effect after the instruction it is attached to, which is the only arrangement that works: a rule describes the state a machine is in, and the machine is not in that state until the instruction that puts it there has run.
Variants§
DefCfa
The canonical frame address is that register plus that offset from here on.
Fields
DefCfaOffset(i32)
The same register as before and a new offset, which is what a push or a subtraction from the stack pointer produces while the stack pointer is still what the address is counted from.
DefCfaRegister(u16)
The same offset as before and a new register, which is what pointing the frame pointer at the frame produces, and is the whole reason a frame pointer is worth having to an unwinder: after it the address stops depending on what the body does to the stack.
Offset
The caller’s copy of that register is in memory, that far from the canonical frame address. The offset is almost always negative, since the frame is below the address.
Fields
Restore(u16)
That register holds what the caller left in it again, so the rule that said where the saved copy went stops applying.
Worth writing down rather than leaving the old rule standing, because a table that is asked about every instruction is asked about the ones between a pop and the return, and by then the memory the old rule points at is above the stack pointer and is where a signal handler’s own frame goes.
RememberState
Put the whole rule set on a stack, so that an epilogue can undo its own changes without the next one starting from what it left behind.
A function with several returns has several epilogues, and they are laid out one after another rather than nested, so without this the second one would begin from the rules the first one ended with rather than from the rules the body had.
RestoreState
Take the rule set back off that stack.