Path: blob/main/crates/environ/src/stack_map.rs
1692 views
use cranelift_bitset::ScalarBitSet;1use object::{Bytes, LittleEndian, U32Bytes};23struct StackMapSection<'a> {4pcs: &'a [U32Bytes<LittleEndian>],5pointers_to_stack_map: &'a [U32Bytes<LittleEndian>],6stack_map_data: &'a [U32Bytes<LittleEndian>],7}89impl<'a> StackMapSection<'a> {10fn parse(section: &'a [u8]) -> Option<StackMapSection<'a>> {11let mut section = Bytes(section);12// NB: this matches the encoding written by `append_to` in the13// `compile::stack_map` module.14let pc_count = section.read::<U32Bytes<LittleEndian>>().ok()?;15let pc_count = usize::try_from(pc_count.get(LittleEndian)).ok()?;16let (pcs, section) =17object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, pc_count).ok()?;18let (pointers_to_stack_map, section) =19object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, pc_count).ok()?;20let stack_map_data =21object::slice_from_all_bytes::<U32Bytes<LittleEndian>>(section).ok()?;22Some(StackMapSection {23pcs,24pointers_to_stack_map,25stack_map_data,26})27}2829fn lookup(&self, pc: u32) -> Option<StackMap<'a>> {30let pc_index = self31.pcs32.binary_search_by_key(&pc, |v| v.get(LittleEndian))33.ok()?;34self.get(pc_index)35}3637fn into_iter(self) -> impl Iterator<Item = (u32, StackMap<'a>)> + 'a {38self.pcs39.iter()40.enumerate()41.map(move |(i, pc)| (pc.get(LittleEndian), self.get(i).unwrap()))42}4344/// Returns the stack map corresponding to the `i`th pc.45fn get(&self, i: usize) -> Option<StackMap<'a>> {46let pointer_to_stack_map = self.pointers_to_stack_map[i].get(LittleEndian) as usize;47let data = self.stack_map_data.get(pointer_to_stack_map..)?;4849let (frame_size, data) = data.split_first()?;50let (count, data) = data.split_first()?;51let data = data.get(..count.get(LittleEndian) as usize)?;5253Some(StackMap {54frame_size: frame_size.get(LittleEndian),55data,56})57}58}5960/// A map for determining where live GC references live in a stack frame.61///62/// Note that this is currently primarily documented as cranelift's63/// `binemit::StackMap`, so for detailed documentation about this please read64/// the docs over there.65pub struct StackMap<'a> {66frame_size: u32,67data: &'a [U32Bytes<LittleEndian>],68}6970impl<'a> StackMap<'a> {71/// Looks up a stack map for `pc` within the `section` provided.72///73/// The `section` should be produced by `StackMapSection` in the74/// `compile::stack_map` module. The `pc` should be relative to the start75/// of the `.text` section in the final executable.76pub fn lookup(pc: u32, section: &'a [u8]) -> Option<StackMap<'a>> {77StackMapSection::parse(section)?.lookup(pc)78}7980/// Iterate over the stack maps contained in the given stack map section.81///82/// This function takes a `section` as its first argument which must have83/// been created with `StackMapSection` builder. This is intended to be the84/// raw `ELF_WASMTIME_STACK_MAP` section from the compilation artifact.85///86/// The yielded offsets are relative to the start of the text section for87/// this map's code object.88pub fn iter(section: &'a [u8]) -> Option<impl Iterator<Item = (u32, StackMap<'a>)> + 'a> {89Some(StackMapSection::parse(section)?.into_iter())90}9192/// Returns the byte size of this stack map's frame.93pub fn frame_size(&self) -> u32 {94self.frame_size95}9697/// Given a frame pointer, get the stack pointer.98///99/// # Safety100///101/// The `fp` must be the frame pointer at the code offset that this stack102/// map is associated with.103pub unsafe fn sp(&self, fp: *mut usize) -> *mut usize {104let frame_size = usize::try_from(self.frame_size).unwrap();105unsafe { fp.byte_sub(frame_size) }106}107108/// Given the stack pointer, get a reference to each live GC reference in109/// the stack frame.110///111/// # Safety112///113/// The `sp` must be the stack pointer at the code offset that this stack114/// map is associated with.115pub unsafe fn live_gc_refs(&self, sp: *mut usize) -> impl Iterator<Item = *mut u32> + '_ {116self.offsets().map(move |i| {117log::trace!("Live GC ref in frame at frame offset {i:#x}");118let i = usize::try_from(i).unwrap();119let ptr_to_gc_ref = unsafe { sp.byte_add(i) };120121// Assert that the pointer is inside this stack map's frame.122assert!({123let delta = ptr_to_gc_ref as usize - sp as usize;124let frame_size = usize::try_from(self.frame_size).unwrap();125delta < frame_size126});127128ptr_to_gc_ref.cast::<u32>()129})130}131132/// Returns the offsets that this stack map registers GC references at.133pub fn offsets(&self) -> impl Iterator<Item = u32> + '_ {134// Here `self.data` is a bit set of offsets divided by 4, so iterate135// over all the bits in `self.data` and multiply their position by 4.136let bit_positions = self.data.iter().enumerate().flat_map(|(i, word)| {137ScalarBitSet(word.get(LittleEndian))138.iter()139.map(move |bit| (i as u32) * 32 + u32::from(bit))140});141142bit_positions.map(|pos| pos * 4)143}144}145146147