Enum wasmtime_environ::isa::unwind::UnwindInst[][src]

pub enum UnwindInst {
    PushFrameRegs {
        offset_upward_to_caller_sp: u32,
    },
    DefineNewFrame {
        offset_upward_to_caller_sp: u32,
        offset_downward_to_clobbers: u32,
    },
    SaveReg {
        clobber_offset: u32,
        reg: RealReg,
    },
    Aarch64SetPointerAuth {
        return_addresses: bool,
    },
}

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 frame
  • SaveReg: 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 of PushFrameRegs

offset_upward_to_caller_sp: u32

The offset from the current SP (after push) to the SP at caller’s callsite.

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 of DefineNewFrame

offset_upward_to_caller_sp: u32

The offset from the current SP and FP value upward to the value of SP at the callsite that invoked us.

offset_downward_to_clobbers: u32

The offset from the current SP and FP value downward to the start of the clobber area.

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 of SaveReg

clobber_offset: u32

The offset from the start of the clobber area to this register’s stack location.

reg: RealReg

The saved register.

Aarch64SetPointerAuth

Defines if the aarch64-specific pointer authentication available for ARM v8.3+ devices is enabled for certain pointers or not.

Fields of Aarch64SetPointerAuth

return_addresses: bool

Whether return addresses (hold in LR) contain a pointer-authentication code.

Trait Implementations

impl Clone for UnwindInst[src]

impl Debug for UnwindInst[src]

impl<'de> Deserialize<'de> for UnwindInst[src]

impl Eq for UnwindInst[src]

impl PartialEq<UnwindInst> for UnwindInst[src]

impl Serialize for UnwindInst[src]

impl StructuralEq for UnwindInst[src]

impl StructuralPartialEq for UnwindInst[src]

Auto Trait Implementations

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> DeserializeOwned for T where
    T: for<'de> Deserialize<'de>, 
[src]

impl<Q, K> Equivalent<K> for Q where
    K: Borrow<Q> + ?Sized,
    Q: Eq + ?Sized
[src]

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<T> ToOwned for T where
    T: Clone
[src]

type Owned = T

The resulting type after obtaining ownership.

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.