Repository: bytecodealliance/wasmtime (rust)

Issue:
Fold Wasm<--->host trampoline functionality into component trampolines
This is a follow up to https://github.com/bytecodealliance/wasmtime/pull/4431

In that PR we don't save entry SP and exit FP/return pointer for calls into/out of components because they use a different set of trampolines. However, simply saving the entry SP and exit FP/return pointer isn't something we can simply add to the existing component trampolines because they are defined in CLIF and CLIF doesn't have a way to talk about these particular architecture-specific details. Mach insts do via operand constraints given to regalloc, but CLIF itself doesn't. So we would need to either have two layered trampolines that bounce from the first to the second when calling into / out of components (very not ideal) or we need to add an instruction to CLIF or something to grab the current SP/FP/return pointer (probably we should do this, but it requires some thought/design).

Pull request:
Save exit Wasm FP and PC in component-to-host trampolines 
Fixes #4535 

Patch:
diff --git a/cranelift/codegen/src/ir/immediates.rs b/cranelift/codegen/src/ir/immediates.rs
index ae7c5f20f..3dba40645 100644
--- a/cranelift/codegen/src/ir/immediates.rs
+++ b/cranelift/codegen/src/ir/immediates.rs
@@ -426,6 +426,12 @@ impl From<i32> for Offset32 {
     }
 }
 
+impl From<u8> for Offset32 {
+    fn from(val: u8) -> Offset32 {
+        Self(val.into())
+    }
+}
+
 impl Display for Offset32 {
     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
         // 0 displays as an empty offset.
diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs
index 99d125b61..addb9177b 100644
--- a/crates/cranelift/src/compiler.rs
+++ b/crates/cranelift/src/compiler.rs
@@ -30,8 +30,8 @@ use std::mem;
 use std::sync::Mutex;
 use wasmtime_environ::{
     AddressMapSection, CompileError, FilePos, FlagValue, FunctionBodyData, FunctionInfo,
-    InstructionAddressMap, Module, ModuleTranslation, ModuleTypes, StackMapInformation, Trampoline,
-    TrapCode, TrapEncodingBuilder, TrapInformation, Tunables, VMOffsets,
+    InstructionAddressMap, Module, ModuleTranslation, ModuleTypes, PtrSize, StackMapInformation,
+    Trampoline, TrapCode, TrapEncodingBuilder, TrapInformation, Tunables, VMOffsets,
 };
 
 #[cfg(feature = "component-model")]
@@ -196,7 +196,7 @@ impl wasmtime_environ::Compiler for Compiler {
         });
         let stack_limit = context.func.create_global_value(ir::GlobalValueData::Load {
             base: interrupts_ptr,
-            offset: i32::try_from(func_env.offsets.vmruntime_limits_stack_limit())
+            offset: i32::try_from(func_env.offsets.ptr.vmruntime_limits_stack_limit())
                 .unwrap()
                 .into(),
             global_type: isa.pointer_type(),
diff --git a/crates/cranelift/src/compiler/component.rs b/crates/cranelift/src/compiler/component.rs
index 803a5629a..318764d11 100644
--- a/crates/cranelift/src/compiler/component.rs
+++ b/crates/cranelift/src/compiler/component.rs
@@ -13,7 +13,7 @@ use wasmtime_environ::component::{
     AlwaysTrapInfo, CanonicalOptions, Component, ComponentCompiler, ComponentTypes, FunctionInfo,
     LowerImport, LoweredIndex, RuntimeAlwaysTrapIndex, VMComponentOffsets,
 };
-use wasmtime_environ::{PrimaryMap, SignatureIndex, Trampoline, TrapCode, WasmFuncType};
+use wasmtime_environ::{PrimaryMap, PtrSize, SignatureIndex, Trampoline, TrapCode, WasmFuncType};
 
 impl ComponentCompiler for Compiler {
     fn compile_lowered_trampoline(
@@ -46,6 +46,44 @@ impl ComponentCompiler for Compiler {
             self.wasm_to_host_spill_args(ty, &mut builder, block0);
         let vmctx = builder.func.dfg.block_params(block0)[0];
 
+        // Save the exit FP and return address for stack walking purposes.
+        //
+        // First we need to get the `VMRuntimeLimits`.
+        let limits = builder.ins().load(
+            pointer_type,
+            MemFlags::trusted(),
+            vmctx,
+            i32::try_from(offsets.limits()).unwrap(),
+        );
+        // Then save the exit Wasm FP to the limits. We dereference the current
+        // FP to get the previous FP because the current FP is the trampoline's
+        // FP, and we want the Wasm function's FP, which is the caller of this
+        // trampoline.
+        let trampoline_fp = builder.ins().get_frame_pointer(pointer_type);
+        let wasm_fp = builder.ins().load(
+            pointer_type,
+            MemFlags::trusted(),
+            trampoline_fp,
+            // The FP always points to the next older FP for all supported
+            // targets. See assertion in
+            // `crates/runtime/src/traphandlers/backtrace.rs`.
+            0,
+        );
+        builder.ins().store(
+            MemFlags::trusted(),
+            wasm_fp,
+            limits,
+            offsets.ptr.vmruntime_limits_last_wasm_exit_fp(),
+        );
+        // Finally save the Wasm return address to the limits.
+        let wasm_pc = builder.ins().get_return_address(pointer_type);
+        builder.ins().store(
+            MemFlags::trusted(),
+            wasm_pc,
+            limits,
+            offsets.ptr.vmruntime_limits_last_wasm_exit_pc(),
+        );
+
         // Below this will incrementally build both the signature of the host
         // function we're calling as well as the list of arguments since the
         // list is somewhat long.
diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs
index 0c2239153..7d5e053b0 100644
--- a/crates/cranelift/src/func_environ.rs
+++ b/crates/cranelift/src/func_environ.rs
@@ -528,7 +528,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
     ) -> (ir::Value, ir::immediates::Offset32) {
         (
             builder.use_var(self.vmruntime_limits_ptr),
-            i32::from(self.offsets.vmruntime_limits_fuel_consumed()).into(),
+            i32::from(self.offsets.ptr.vmruntime_limits_fuel_consumed()).into(),
         )
     }
 
@@ -628,12 +628,15 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
 
     fn epoch_load_deadline_into_var(&mut self, builder: &mut FunctionBuilder<'_>) {
         let interrupts = builder.use_var(self.vmruntime_limits_ptr);
-        let deadline = builder.ins().load(
-            ir::types::I64,
-            ir::MemFlags::trusted(),
-            interrupts,
-            ir::immediates::Offset32::new(self.offsets.vmruntime_limits_epoch_deadline() as i32),
-        );
+        let deadline =
+            builder.ins().load(
+                ir::types::I64,
+                ir::MemFlags::trusted(),
+                interrupts,
+                ir::immediates::Offset32::new(
+                    self.offsets.ptr.vmruntime_limits_epoch_deadline() as i32
+                ),
+            );
         builder.def_var(self.epoch_deadline_var, deadline);
     }
 
diff --git a/crates/environ/src/component/vmcomponent_offsets.rs b/crates/environ/src/component/vmcomponent_offsets.rs
index 9a26c1475..649aae50f 100644
--- a/crates/environ/src/component/vmcomponent_offsets.rs
+++ b/crates/environ/src/component/vmcomponent_offsets.rs
@@ -3,6 +3,7 @@
 // struct VMComponentContext {
 //      magic: u32,
 //      store: *mut dyn Store,
+//      limits: *const VMRuntimeLimits,
 //      flags: [VMGlobalDefinition; component.num_runtime_component_instances],
 //      lowering_anyfuncs: [VMCallerCheckedAnyfunc; component.num_lowerings],
 //      always_trap_anyfuncs: [VMCallerCheckedAnyfunc; component.num_always_trap],
@@ -60,6 +61,7 @@ pub struct VMComponentOffsets<P> {
     // precalculated offsets of various member fields
     magic: u32,
     store: u32,
+    limits: u32,
     flags: u32,
     lowering_anyfuncs: u32,
     always_trap_anyfuncs: u32,
@@ -93,6 +95,7 @@ impl<P: PtrSize> VMComponentOffsets<P> {
             num_always_trap: component.num_always_trap,
             magic: 0,
             store: 0,
+            limits: 0,
             flags: 0,
             lowering_anyfuncs: 0,
             always_trap_anyfuncs: 0,
@@ -131,6 +134,7 @@ impl<P: PtrSize> VMComponentOffsets<P> {
             size(magic) = 4u32,
             align(u32::from(ret.ptr.size())),
             size(store) = cmul(2, ret.ptr.size()),
+            size(limits) = ret.ptr.size(),
             align(16),
             size(flags) = cmul(ret.num_runtime_component_instances, ret.ptr.size_of_vmglobal_definition()),
             align(u32::from(ret.ptr.size())),
@@ -177,6 +181,12 @@ impl<P: PtrSize> VMComponentOffsets<P> {
         self.store
     }
 
+    /// The offset of the `limits` field.
+    #[inline]
+    pub fn limits(&self) -> u32 {
+        self.limits
+    }
+
     /// The offset of the `lowering_anyfuncs` field.
     #[inline]
     pub fn lowering_anyfuncs(&self) -> u32 {
diff --git a/crates/environ/src/vmoffsets.rs b/crates/environ/src/vmoffsets.rs
index 09d3783e9..d301765b4 100644
--- a/crates/environ/src/vmoffsets.rs
+++ b/crates/environ/src/vmoffsets.rs
@@ -135,6 +135,39 @@ pub trait PtrSize {
     fn size_of_vmglobal_definition(&self) -> u8 {
         16
     }
+
+    /// Return the offset of the `stack_limit` field of `VMRuntimeLimits`
+    #[inline]
+    fn vmruntime_limits_stack_limit(&self) -> u8 {
+        0
+    }
+
+    /// Return the offset of the `fuel_consumed` field of `VMRuntimeLimits`
+    #[inline]
+    fn vmruntime_limits_fuel_consumed(&self) -> u8 {
+        self.size()
+    }
+
+    /// Return the offset of the `epoch_deadline` field of `VMRuntimeLimits`
+    #[inline]
+    fn vmruntime_limits_epoch_deadline(&self) -> u8 {
+        self.vmruntime_limits_fuel_consumed() + 8 // `stack_limit` is a pointer; `fuel_consumed` is an `i64`
+    }
+
+    /// Return the offset of the `last_wasm_exit_fp` field of `VMRuntimeLimits`.
+    fn vmruntime_limits_last_wasm_exit_fp(&self) -> u8 {
+        self.vmruntime_limits_epoch_deadline() + 8
+    }
+
+    /// Return the offset of the `last_wasm_exit_pc` field of `VMRuntimeLimits`.
+    fn vmruntime_limits_last_wasm_exit_pc(&self) -> u8 {
+        self.vmruntime_limits_last_wasm_exit_fp() + self.size()
+    }
+
+    /// Return the offset of the `last_enty_sp` field of `VMRuntimeLimits`.
+    fn vmruntime_limits_last_wasm_entry_sp(&self) -> u8 {
+        self.vmruntime_limits_last_wasm_exit_pc() + self.size()
+    }
 }
 
 /// Type representing the size of a pointer for the current compilation host
@@ -545,42 +578,6 @@ impl<P: PtrSize> VMOffsets<P> {
     }
 }
 
-/// Offsets for `VMRuntimeLimits`.
-impl<P: PtrSize> VMOffsets<P> {
-    /// Return the offset of the `stack_limit` field of `VMRuntimeLimits`
-    #[inline]
-    pub fn vmruntime_limits_stack_limit(&self) -> u8 {
-        0
-    }
-
-    /// Return the offset of the `fuel_consumed` field of `VMRuntimeLimits`
-    #[inline]
-    pub fn vmruntime_limits_fuel_consumed(&self) -> u8 {
-        self.pointer_size()
-    }
-
-    /// Return the offset of the `epoch_deadline` field of `VMRuntimeLimits`
-    #[inline]
-    pub fn vmruntime_limits_epoch_deadline(&self) -> u8 {
-        self.vmruntime_limits_fuel_consumed() + 8 // `stack_limit` is a pointer; `fuel_consumed` is an `i64`
-    }
-
-    /// Return the offset of the `last_wasm_exit_fp` field of `VMRuntimeLimits`.
-    pub fn vmruntime_limits_last_wasm_exit_fp(&self) -> u8 {
-        self.vmruntime_limits_epoch_deadline() + 8
-    }
-
-    /// Return the offset of the `last_wasm_exit_pc` field of `VMRuntimeLimits`.
-    pub fn vmruntime_limits_last_wasm_exit_pc(&self) -> u8 {
-        self.vmruntime_limits_last_wasm_exit_fp() + self.pointer_size()
-    }
-
-    /// Return the offset of the `last_enty_sp` field of `VMRuntimeLimits`.
-    pub fn vmruntime_limits_last_wasm_entry_sp(&self) -> u8 {
-        self.vmruntime_limits_last_wasm_exit_pc() + self.pointer_size()
-    }
-}
-
 /// Offsets for `VMContext`.
 impl<P: PtrSize> VMOffsets<P> {
     /// Return the offset to the `magic` value in this `VMContext`.
diff --git a/crates/runtime/src/component.rs b/crates/runtime/src/component.rs
index f324ff9dc..96db80b19 100644
--- a/crates/runtime/src/component.rs
+++ b/crates/runtime/src/component.rs
@@ -367,6 +367,8 @@ impl ComponentInstance {
     unsafe fn initialize_vmctx(&mut self, store: *mut dyn Store) {
         *self.vmctx_plus_offset(self.offsets.magic()) = VMCOMPONENT_MAGIC;
         *self.vmctx_plus_offset(self.offsets.store()) = store;
+        *self.vmctx_plus_offset(self.offsets.limits()) = (*store).vmruntime_limits();
+
         for i in 0..self.offsets.num_runtime_component_instances {
             let i = RuntimeComponentInstanceIndex::from_u32(i);
             let mut def = VMGlobalDefinition::new();
diff --git a/crates/runtime/src/trampolines/aarch64.rs b/crates/runtime/src/trampolines/aarch64.rs
index 757811d5d..7312bd827 100644
--- a/crates/runtime/src/trampolines/aarch64.rs
+++ b/crates/runtime/src/trampolines/aarch64.rs
@@ -35,7 +35,7 @@ asm_func!(
 
 #[cfg(test)]
 mod host_to_wasm_trampoline_offsets_tests {
-    use wasmtime_environ::{Module, VMOffsets};
+    use wasmtime_environ::{Module, PtrSize, VMOffsets};
 
     #[test]
     fn test() {
@@ -43,7 +43,7 @@ mod host_to_wasm_trampoline_offsets_tests {
         let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);
 
         assert_eq!(8, offsets.vmctx_runtime_limits());
-        assert_eq!(40, offsets.vmruntime_limits_last_wasm_entry_sp());
+        assert_eq!(40, offsets.ptr.vmruntime_limits_last_wasm_entry_sp());
         assert_eq!(16, offsets.vmctx_callee());
         assert_eq!(0x65726f63, u32::from_le_bytes(*b"core"));
     }
@@ -79,7 +79,7 @@ asm_func!(
 mod wasm_to_host_trampoline_offsets_tests {
     use crate::VMHostFuncContext;
     use memoffset::offset_of;
-    use wasmtime_environ::{Module, VMOffsets};
+    use wasmtime_environ::{Module, PtrSize, VMOffsets};
 
     #[test]
     fn test() {
@@ -87,8 +87,8 @@ mod wasm_to_host_trampoline_offsets_tests {
         let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);
 
         assert_eq!(8, offsets.vmctx_runtime_limits());
-        assert_eq!(24, offsets.vmruntime_limits_last_wasm_exit_fp());
-        assert_eq!(32, offsets.vmruntime_limits_last_wasm_exit_pc());
+        assert_eq!(24, offsets.ptr.vmruntime_limits_last_wasm_exit_fp());
+        assert_eq!(32, offsets.ptr.vmruntime_limits_last_wasm_exit_pc());
         assert_eq!(8, offset_of!(VMHostFuncContext, host_func));
     }
 }
diff --git a/crates/runtime/src/trampolines/s390x.rs b/crates/runtime/src/trampolines/s390x.rs
index 95ecb72bc..c6cd6335c 100644
--- a/crates/runtime/src/trampolines/s390x.rs
+++ b/crates/runtime/src/trampolines/s390x.rs
@@ -3,7 +3,7 @@
 
 #[cfg(test)]
 mod host_to_wasm_trampoline_offsets_tests {
-    use wasmtime_environ::{Module, VMOffsets};
+    use wasmtime_environ::{Module, PtrSize, VMOffsets};
 
     #[test]
     fn test() {
@@ -11,7 +11,7 @@ mod host_to_wasm_trampoline_offsets_tests {
         let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);
 
         assert_eq!(8, offsets.vmctx_runtime_limits());
-        assert_eq!(40, offsets.vmruntime_limits_last_wasm_entry_sp());
+        assert_eq!(40, offsets.ptr.vmruntime_limits_last_wasm_entry_sp());
         assert_eq!(16, offsets.vmctx_callee());
         assert_eq!(0x65726f63, u32::from_le_bytes(*b"core"));
     }
@@ -24,7 +24,7 @@ mod host_to_wasm_trampoline_offsets_tests {
 mod wasm_to_host_trampoline_offsets_tests {
     use crate::VMHostFuncContext;
     use memoffset::offset_of;
-    use wasmtime_environ::{Module, VMOffsets};
+    use wasmtime_environ::{Module, PtrSize, VMOffsets};
 
     #[test]
     fn test() {
@@ -32,8 +32,8 @@ mod wasm_to_host_trampoline_offsets_tests {
         let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);
 
         assert_eq!(8, offsets.vmctx_runtime_limits());
-        assert_eq!(24, offsets.vmruntime_limits_last_wasm_exit_fp());
-        assert_eq!(32, offsets.vmruntime_limits_last_wasm_exit_pc());
+        assert_eq!(24, offsets.ptr.vmruntime_limits_last_wasm_exit_fp());
+        assert_eq!(32, offsets.ptr.vmruntime_limits_last_wasm_exit_pc());
         assert_eq!(8, offset_of!(VMHostFuncContext, host_func));
     }
 }
diff --git a/crates/runtime/src/trampolines/x86_64.rs b/crates/runtime/src/trampolines/x86_64.rs
index 03580fda6..0eae6af46 100644
--- a/crates/runtime/src/trampolines/x86_64.rs
+++ b/crates/runtime/src/trampolines/x86_64.rs
@@ -47,7 +47,7 @@ asm_func!(
 
 #[cfg(test)]
 mod host_to_wasm_trampoline_offsets_tests {
-    use wasmtime_environ::{Module, VMOffsets};
+    use wasmtime_environ::{Module, PtrSize, VMOffsets};
 
     #[test]
     fn test() {
@@ -55,7 +55,7 @@ mod host_to_wasm_trampoline_offsets_tests {
         let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);
 
         assert_eq!(8, offsets.vmctx_runtime_limits());
-        assert_eq!(40, offsets.vmruntime_limits_last_wasm_entry_sp());
+        assert_eq!(40, offsets.ptr.vmruntime_limits_last_wasm_entry_sp());
         assert_eq!(16, offsets.vmctx_callee());
         assert_eq!(0x65726f63, u32::from_le_bytes(*b"core"));
     }
@@ -92,7 +92,7 @@ asm_func!(
 mod wasm_to_host_trampoline_offsets_tests {
     use crate::VMHostFuncContext;
     use memoffset::offset_of;
-    use wasmtime_environ::{Module, VMOffsets};
+    use wasmtime_environ::{Module, PtrSize, VMOffsets};
 
     #[test]
     fn test() {
@@ -100,8 +100,8 @@ mod wasm_to_host_trampoline_offsets_tests {
         let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);
 
         assert_eq!(8, offsets.vmctx_runtime_limits());
-        assert_eq!(24, offsets.vmruntime_limits_last_wasm_exit_fp());
-        assert_eq!(32, offsets.vmruntime_limits_last_wasm_exit_pc());
+        assert_eq!(24, offsets.ptr.vmruntime_limits_last_wasm_exit_fp());
+        assert_eq!(32, offsets.ptr.vmruntime_limits_last_wasm_exit_pc());
         assert_eq!(8, offset_of!(VMHostFuncContext, host_func));
     }
 }
diff --git a/crates/runtime/src/traphandlers/backtrace.rs b/crates/runtime/src/traphandlers/backtrace.rs
index a84ee6c0d..53cfcd93d 100644
--- a/crates/runtime/src/traphandlers/backtrace.rs
+++ b/crates/runtime/src/traphandlers/backtrace.rs
@@ -114,6 +114,7 @@ impl Backtrace {
         trap_pc_and_fp: Option<(usize, usize)>,
         mut f: impl FnMut(Frame) -> ControlFlow<()>,
     ) {
+        log::trace!("====== Capturing Backtrace ======");
         let (last_wasm_exit_pc, last_wasm_exit_fp) = match trap_pc_and_fp {
             // If we exited Wasm by catching a trap, then the Wasm-to-host
             // trampoline did not get a chance to save the last Wasm PC and FP,
@@ -137,6 +138,7 @@ impl Backtrace {
             *(*state.limits).last_wasm_entry_sp.get(),
             &mut f,
         ) {
+            log::trace!("====== Done Capturing Backtrace ======");
             return;
         }
 
@@ -151,6 +153,7 @@ impl Backtrace {
                 debug_assert_eq!(state.old_last_wasm_exit_pc, 0);
                 debug_assert_eq!(state.old_last_wasm_exit_fp, 0);
                 debug_assert_eq!(state.old_last_wasm_entry_sp, 0);
+                log::trace!("====== Done Capturing Backtrace ======");
                 return;
             }
 
@@ -160,9 +163,12 @@ impl Backtrace {
                 state.old_last_wasm_entry_sp,
                 &mut f,
             ) {
+                log::trace!("====== Done Capturing Backtrace ======");
                 return;
             }
         }
+
+        unreachable!()
     }
 
     /// Walk through a contiguous sequence of Wasm frames starting with the
@@ -245,7 +251,13 @@ impl Backtrace {
 
             pc = arch::get_next_older_pc_from_fp(fp);
 
-            let next_older_fp = arch::get_next_older_fp_from_fp(fp);
+            // We rely on this offset being zero for all supported architectures
+            // in `crates/cranelift/src/component/compiler.rs` when we set the
+            // Wasm exit FP. If this ever changes, we will need to update that
+            // code as well!
+            assert_eq!(arch::NEXT_OLDER_FP_FROM_FP_OFFSET, 0);
+
+            let next_older_fp = *(fp as *mut usize).add(arch::NEXT_OLDER_FP_FROM_FP_OFFSET);
             // Because the stack always grows down, the older FP must be greater
             // than the current FP.
             assert!(next_older_fp > fp, "{next_older_fp:#x} > {fp:#x}");
diff --git a/crates/runtime/src/traphandlers/backtrace/aarch64.rs b/crates/runtime/src/traphandlers/backtrace/aarch64.rs
index 87980d3f1..063bdf8db 100644
--- a/crates/runtime/src/traphandlers/backtrace/aarch64.rs
+++ b/crates/runtime/src/traphandlers/backtrace/aarch64.rs
@@ -30,9 +30,9 @@ pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
 
     pc
 }
-pub unsafe fn get_next_older_fp_from_fp(fp: usize) -> usize {
-    *(fp as *mut usize)
-}
+
+// And the current frame pointer points to the next older frame pointer.
+pub const NEXT_OLDER_FP_FROM_FP_OFFSET: usize = 0;
 
 pub fn reached_entry_sp(fp: usize, first_wasm_sp: usize) -> bool {
     // Calls in aarch64 push two i64s (old FP and return PC) so our entry SP is
diff --git a/crates/runtime/src/traphandlers/backtrace/s390x.rs b/crates/runtime/src/traphandlers/backtrace/s390x.rs
index 6f041c346..4cc46a9d9 100644
--- a/crates/runtime/src/traphandlers/backtrace/s390x.rs
+++ b/crates/runtime/src/traphandlers/backtrace/s390x.rs
@@ -5,11 +5,9 @@ pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
     *(fp as *mut usize).offset(14)
 }
 
-pub unsafe fn get_next_older_fp_from_fp(fp: usize) -> usize {
-    // The next older "FP" (backchain pointer) was saved in the slot pointed to
-    // by the current "FP".
-    *(fp as *mut usize)
-}
+// The next older "FP" (backchain pointer) was saved in the slot pointed to
+// by the current "FP".
+pub const NEXT_OLDER_FP_FROM_FP_OFFSET: usize = 0;
 
 pub fn reached_entry_sp(fp: usize, first_wasm_sp: usize) -> bool {
     // The "FP" (backchain pointer) holds the value of the stack pointer at
diff --git a/crates/runtime/src/traphandlers/backtrace/x86_64.rs b/crates/runtime/src/traphandlers/backtrace/x86_64.rs
index 51a4948c7..7c250c5e7 100644
--- a/crates/runtime/src/traphandlers/backtrace/x86_64.rs
+++ b/crates/runtime/src/traphandlers/backtrace/x86_64.rs
@@ -4,10 +4,8 @@ pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
     *(fp as *mut usize).offset(1)
 }
 
-pub unsafe fn get_next_older_fp_from_fp(fp: usize) -> usize {
-    // And the current frame pointer points to the next older frame pointer.
-    *(fp as *mut usize)
-}
+// And the current frame pointer points to the next older frame pointer.
+pub const NEXT_OLDER_FP_FROM_FP_OFFSET: usize = 0;
 
 pub fn reached_entry_sp(fp: usize, first_wasm_sp: usize) -> bool {
     // When the FP is just below the SP (because we are in a function prologue
diff --git a/crates/runtime/src/vmcontext.rs b/crates/runtime/src/vmcontext.rs
index 5d1125b1d..73eb7bf85 100644
--- a/crates/runtime/src/vmcontext.rs
+++ b/crates/runtime/src/vmcontext.rs
@@ -791,7 +791,7 @@ mod test_vmruntime_limits {
     use super::VMRuntimeLimits;
     use memoffset::offset_of;
     use std::mem::size_of;
-    use wasmtime_environ::{Module, VMOffsets};
+    use wasmtime_environ::{Module, PtrSize, VMOffsets};
 
     #[test]
     fn field_offsets() {
@@ -799,27 +799,27 @@ mod test_vmruntime_limits {
         let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
         assert_eq!(
             offset_of!(VMRuntimeLimits, stack_limit),
-            usize::from(offsets.vmruntime_limits_stack_limit())
+            usize::from(offsets.ptr.vmruntime_limits_stack_limit())
         );
         assert_eq!(
             offset_of!(VMRuntimeLimits, fuel_consumed),
-            usize::from(offsets.vmruntime_limits_fuel_consumed())
+            usize::from(offsets.ptr.vmruntime_limits_fuel_consumed())
         );
         assert_eq!(
             offset_of!(VMRuntimeLimits, epoch_deadline),
-            usize::from(offsets.vmruntime_limits_epoch_deadline())
+            usize::from(offsets.ptr.vmruntime_limits_epoch_deadline())
         );
         assert_eq!(
             offset_of!(VMRuntimeLimits, last_wasm_exit_fp),
-            usize::from(offsets.vmruntime_limits_last_wasm_exit_fp())
+            usize::from(offsets.ptr.vmruntime_limits_last_wasm_exit_fp())
         );
         assert_eq!(
             offset_of!(VMRuntimeLimits, last_wasm_exit_pc),
-            usize::from(offsets.vmruntime_limits_last_wasm_exit_pc())
+            usize::from(offsets.ptr.vmruntime_limits_last_wasm_exit_pc())
         );
         assert_eq!(
             offset_of!(VMRuntimeLimits, last_wasm_entry_sp),
-            usize::from(offsets.vmruntime_limits_last_wasm_entry_sp())
+            usize::from(offsets.ptr.vmruntime_limits_last_wasm_entry_sp())
         );
     }
 }


Test patch:
diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs
index 09cbabc89..5e67d92df 100644
--- a/tests/all/component_model/import.rs
+++ b/tests/all/component_model/import.rs
@@ -222,61 +222,57 @@ fn attempt_to_leave_during_malloc() -> Result<()> {
     let component = Component::new(&engine, component)?;
     let mut store = Store::new(&engine, ());
 
-    // TODO(#4535): we need to fold the Wasm<--->host trampoline functionality into
-    // component trampolines. Until then, we panic when getting a backtrace here.
-    if false {
-        // Assert that during a host import if we return values to wasm that a trap
-        // happens if we try to leave the instance.
-        let trap = linker
-            .instantiate(&mut store, &component)?
-            .get_typed_func::<(), (), _>(&mut store, "run")?
-            .call(&mut store, ())
-            .unwrap_err()
-            .downcast::<Trap>()?;
-        assert!(
-            trap.to_string().contains("cannot leave component instance"),
-            "bad trap: {}",
-            trap,
-        );
-
-        let trace = trap.trace().unwrap();
-        assert_eq!(trace.len(), 4);
-
-        // This was our entry point...
-        assert_eq!(trace[3].module_name(), Some("m"));
-        assert_eq!(trace[3].func_name(), Some("run"));
-
-        // ... which called an imported function which ends up being originally
-        // defined by the shim instance. The shim instance then does an indirect
-        // call through a table which goes to the `canon.lower`'d host function
-        assert_eq!(trace[2].module_name(), Some("host_shim"));
-        assert_eq!(trace[2].func_name(), Some("shim_ret_string"));
-
-        // ... and the lowered host function will call realloc to allocate space for
-        // the result
-        assert_eq!(trace[1].module_name(), Some("m"));
-        assert_eq!(trace[1].func_name(), Some("realloc"));
-
-        // ... but realloc calls the shim instance and tries to exit the
-        // component, triggering a dynamic trap
-        assert_eq!(trace[0].module_name(), Some("host_shim"));
-        assert_eq!(trace[0].func_name(), Some("shim_thunk"));
-
-        // In addition to the above trap also ensure that when we enter a wasm
-        // component if we try to leave while lowering then that's also a dynamic
-        // trap.
-        let trap = linker
-            .instantiate(&mut store, &component)?
-            .get_typed_func::<(&str,), (), _>(&mut store, "take-string")?
-            .call(&mut store, ("x",))
-            .unwrap_err()
-            .downcast::<Trap>()?;
-        assert!(
-            trap.to_string().contains("cannot leave component instance"),
-            "bad trap: {}",
-            trap,
-        );
-    }
+    // Assert that during a host import if we return values to wasm that a trap
+    // happens if we try to leave the instance.
+    let trap = linker
+        .instantiate(&mut store, &component)?
+        .get_typed_func::<(), (), _>(&mut store, "run")?
+        .call(&mut store, ())
+        .unwrap_err()
+        .downcast::<Trap>()?;
+    assert!(
+        trap.to_string().contains("cannot leave component instance"),
+        "bad trap: {}",
+        trap,
+    );
+
+    let trace = trap.trace().unwrap();
+    assert_eq!(trace.len(), 4);
+
+    // This was our entry point...
+    assert_eq!(trace[3].module_name(), Some("m"));
+    assert_eq!(trace[3].func_name(), Some("run"));
+
+    // ... which called an imported function which ends up being originally
+    // defined by the shim instance. The shim instance then does an indirect
+    // call through a table which goes to the `canon.lower`'d host function
+    assert_eq!(trace[2].module_name(), Some("host_shim"));
+    assert_eq!(trace[2].func_name(), Some("shim_ret_string"));
+
+    // ... and the lowered host function will call realloc to allocate space for
+    // the result
+    assert_eq!(trace[1].module_name(), Some("m"));
+    assert_eq!(trace[1].func_name(), Some("realloc"));
+
+    // ... but realloc calls the shim instance and tries to exit the
+    // component, triggering a dynamic trap
+    assert_eq!(trace[0].module_name(), Some("host_shim"));
+    assert_eq!(trace[0].func_name(), Some("shim_thunk"));
+
+    // In addition to the above trap also ensure that when we enter a wasm
+    // component if we try to leave while lowering then that's also a dynamic
+    // trap.
+    let trap = linker
+        .instantiate(&mut store, &component)?
+        .get_typed_func::<(&str,), (), _>(&mut store, "take-string")?
+        .call(&mut store, ("x",))
+        .unwrap_err()
+        .downcast::<Trap>()?;
+    assert!(
+        trap.to_string().contains("cannot leave component instance"),
+        "bad trap: {}",
+        trap,
+    );
     Ok(())
 }
 
@@ -605,24 +601,21 @@ fn bad_import_alignment() -> Result<()> {
     let component = Component::new(&engine, component)?;
     let mut store = Store::new(&engine, ());
 
-    // TODO(#4535): we need to fold the Wasm<--->host trampoline functionality into
-    // component trampolines. Until then, we panic when getting a backtrace here.
-    if false {
-        let trap = linker
-            .instantiate(&mut store, &component)?
-            .get_typed_func::<(), (), _>(&mut store, "unaligned-retptr")?
-            .call(&mut store, ())
-            .unwrap_err()
-            .downcast::<Trap>()?;
-        assert!(trap.to_string().contains("pointer not aligned"), "{}", trap);
-        let trap = linker
-            .instantiate(&mut store, &component)?
-            .get_typed_func::<(), (), _>(&mut store, "unaligned-argptr")?
-            .call(&mut store, ())
-            .unwrap_err()
-            .downcast::<Trap>()?;
-        assert!(trap.to_string().contains("pointer not aligned"), "{}", trap);
-    }
+    let trap = linker
+        .instantiate(&mut store, &component)?
+        .get_typed_func::<(), (), _>(&mut store, "unaligned-retptr")?
+        .call(&mut store, ())
+        .unwrap_err()
+        .downcast::<Trap>()?;
+    assert!(trap.to_string().contains("pointer not aligned"), "{}", trap);
+    let trap = linker
+        .instantiate(&mut store, &component)?
+        .get_typed_func::<(), (), _>(&mut store, "unaligned-argptr")?
+        .call(&mut store, ())
+        .unwrap_err()
+        .downcast::<Trap>()?;
+    assert!(trap.to_string().contains("pointer not aligned"), "{}", trap);
+
     Ok(())
 }