pub enum UnwindInst {
PushFrameRegs {
offset_upward_to_caller_sp: u32,
},
DefineNewFrame {
offset_upward_to_caller_sp: u32,
offset_downward_to_clobbers: u32,
},
StackAlloc {
size: u32,
},
SaveReg {
clobber_offset: u32,
reg: RealReg,
},
RegStackOffset {
clobber_offset: u32,
reg: RealReg,
},
Aarch64SetPointerAuth {
return_addresses: bool,
},
}
Expand description
Unwind pseudoinstruction used in VCode backends: represents that at the present location, an action has just been taken.
VCode backends always emit unwind info that is relative to a frame pointer, because we are planning to allow for dynamic frame allocation, and because it makes the design quite a lot simpler in general: we don’t have to be precise about SP adjustments throughout the body of the function.
We include only unwind info for prologues at this time. Note that unwind
info for epilogues is only necessary if one expects to unwind while within
the last few instructions of the function (after FP has been restored) or
if one wishes to instruction-step through the epilogue and see a backtrace
at every point. This is not necessary for correct operation otherwise and so
we simplify the world a bit by omitting epilogue information. (Note that
some platforms also don’t require or have a way to describe unwind
information for epilogues at all: for example, on Windows, the UNWIND_INFO
format only stores information for the function prologue.)
Because we are defining an abstraction over multiple unwind formats (at least Windows/fastcall and System V) and multiple architectures (at least x86-64 and aarch64), we have to be a little bit flexible in how we describe the frame. However, it turns out that a least-common-denominator prologue works for all of the cases we have to worry about today!
We assume the stack looks something like this:
+----------------------------------------------+
| stack arg area, etc (according to ABI) |
| ... |
SP at call --> +----------------------------------------------+
| return address (pushed by HW or SW) |
+----------------------------------------------+
| old frame pointer (FP) |
FP in this --> +----------------------------------------------+
function | clobbered callee-save registers |
| ... |
start of --> +----------------------------------------------+
clobbers | (rest of function's frame, irrelevant here) |
| ... |
SP in this --> +----------------------------------------------+
function
We assume that the prologue consists of:
PushFrameRegs
: A push operation that adds the old FP to the stack (and maybe the link register, on architectures that do not push return addresses in hardware)DefineFrame
: An update that sets FP to SP to establish a new frameSaveReg
: A number of stores or pushes to the stack to save clobbered registers
Each of these steps has a corresponding pseudo-instruction. At each step,
we need some information to determine where the current stack frame is
relative to SP or FP. When the PushFrameRegs
occurs, we need to know how
much SP was decremented by, so we can allow the unwinder to continue to find
the caller’s frame. When we define the new frame, we need to know where FP
is in relation to “SP at call” and also “start of clobbers”, because
different unwind formats define one or the other of those as the anchor by
which we define the frame. Finally, when registers are saved, we need to
know which ones, and where.
Different unwind formats work differently; here is a whirlwind tour of how they define frames to help understanding:
-
Windows unwind information defines a frame that must start below the clobber area, because all clobber-save offsets are non-negative. We set it at the “start of clobbers” in the figure above. The
UNWIND_INFO
contains a “frame pointer offset” field; when we define the new frame, the frame is understood to be the value of FP (RBP
) minus this offset. In other words, the FP is at the frame pointer offset relative to the start-of-clobber-frame. We use the “FP offset down to clobber area” offset to generate this info. -
System V unwind information defines a frame in terms of the CFA (call-frame address), which is equal to the “SP at call” above. SysV allows negative offsets, so there is no issue defining clobber-save locations in terms of CFA. The format allows us to define CFA flexibly in terms of any register plus an offset; we define it in terms of FP plus the clobber-to-caller-SP offset once FP is established.
Note that certain architectures impose limits on offsets: for example, on Windows, the base of the clobber area must not be more than 240 bytes below FP.
Unwind pseudoinstructions are emitted inline by ABI code as it generates a prologue. Thus, for the usual case, a prologue might look like (using x64 as an example):
push rbp
unwind UnwindInst::PushFrameRegs { offset_upward_to_caller_sp: 16 }
mov rbp, rsp
unwind UnwindInst::DefineNewFrame { offset_upward_to_caller_sp: 16,
offset_downward_to_clobbers: 16 }
sub rsp, 32
mov [rsp+16], r12
unwind UnwindInst::SaveReg { reg: R12, clobber_offset: 0 }
mov [rsp+24], r13
unwind UnwindInst::SaveReg { reg: R13, clobber_offset: 8 }
...
Variants§
PushFrameRegs
The frame-pointer register for this architecture has just been pushed to
the stack (and on architectures where return-addresses are not pushed by
hardware, the link register as well). The FP has not been set to this
frame yet. The current location of SP is such that
offset_upward_to_caller_sp
is the distance to SP-at-callsite (our
caller’s frame).
Fields
DefineNewFrame
The frame-pointer register for this architecture has just been
set to the current stack location. We wish to define a new
frame that is anchored on this new FP value. Offsets are provided
upward to the caller’s stack frame and downward toward the clobber
area. We expect this pseudo-op to come after PushFrameRegs
.
Fields
StackAlloc
The stack pointer was adjusted to allocate the stack.
SaveReg
The stack slot at the given offset from the clobber-area base has been used to save the given register.
Given that CreateFrame
has occurred first with some
offset_downward_to_clobbers
, SaveReg
with clobber_offset
indicates
that the value of reg
is saved on the stack at address FP - offset_downward_to_clobbers + clobber_offset
.
Fields
RegStackOffset
Computes the value of the given register in the caller as stack offset.
Typically used to unwind the stack pointer if the default rule does not apply.
The clobber_offset
is computed the same way as for the SaveReg
rule.
Fields
Aarch64SetPointerAuth
Defines if the aarch64-specific pointer authentication available for ARM v8.3+ devices is enabled for certain pointers or not.
Trait Implementations§
Source§impl Clone for UnwindInst
impl Clone for UnwindInst
Source§fn clone(&self) -> UnwindInst
fn clone(&self) -> UnwindInst
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moreSource§impl Debug for UnwindInst
impl Debug for UnwindInst
Source§impl PartialEq for UnwindInst
impl PartialEq for UnwindInst
impl Eq for UnwindInst
impl StructuralPartialEq for UnwindInst
Auto Trait Implementations§
impl Freeze for UnwindInst
impl RefUnwindSafe for UnwindInst
impl Send for UnwindInst
impl Sync for UnwindInst
impl Unpin for UnwindInst
impl UnwindSafe for UnwindInst
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key
and return true
if they are equal.