1452086fbSAlex Crichton use cranelift_bitset::ScalarBitSet; 2*a2cc11f3SPhilip Craig use object::{Bytes, LittleEndian, U32}; 3452086fbSAlex Crichton 4452086fbSAlex Crichton struct StackMapSection<'a> { 5*a2cc11f3SPhilip Craig pcs: &'a [U32<LittleEndian>], 6*a2cc11f3SPhilip Craig pointers_to_stack_map: &'a [U32<LittleEndian>], 7*a2cc11f3SPhilip Craig stack_map_data: &'a [U32<LittleEndian>], 8452086fbSAlex Crichton } 9452086fbSAlex Crichton 10452086fbSAlex Crichton impl<'a> StackMapSection<'a> { parse(section: &'a [u8]) -> Option<StackMapSection<'a>>11452086fbSAlex Crichton fn parse(section: &'a [u8]) -> Option<StackMapSection<'a>> { 12452086fbSAlex Crichton let mut section = Bytes(section); 13452086fbSAlex Crichton // NB: this matches the encoding written by `append_to` in the 14452086fbSAlex Crichton // `compile::stack_map` module. 15*a2cc11f3SPhilip Craig let pc_count = section.read::<U32<LittleEndian>>().ok()?; 16452086fbSAlex Crichton let pc_count = usize::try_from(pc_count.get(LittleEndian)).ok()?; 17452086fbSAlex Crichton let (pcs, section) = 18*a2cc11f3SPhilip Craig object::slice_from_bytes::<U32<LittleEndian>>(section.0, pc_count).ok()?; 19452086fbSAlex Crichton let (pointers_to_stack_map, section) = 20*a2cc11f3SPhilip Craig object::slice_from_bytes::<U32<LittleEndian>>(section, pc_count).ok()?; 21*a2cc11f3SPhilip Craig let stack_map_data = object::slice_from_all_bytes::<U32<LittleEndian>>(section).ok()?; 22452086fbSAlex Crichton Some(StackMapSection { 23452086fbSAlex Crichton pcs, 24452086fbSAlex Crichton pointers_to_stack_map, 25452086fbSAlex Crichton stack_map_data, 26452086fbSAlex Crichton }) 27452086fbSAlex Crichton } 28452086fbSAlex Crichton lookup(&self, pc: u32) -> Option<StackMap<'a>>29452086fbSAlex Crichton fn lookup(&self, pc: u32) -> Option<StackMap<'a>> { 30452086fbSAlex Crichton let pc_index = self 31452086fbSAlex Crichton .pcs 32452086fbSAlex Crichton .binary_search_by_key(&pc, |v| v.get(LittleEndian)) 33452086fbSAlex Crichton .ok()?; 34452086fbSAlex Crichton self.get(pc_index) 35452086fbSAlex Crichton } 36452086fbSAlex Crichton into_iter(self) -> impl Iterator<Item = (u32, StackMap<'a>)> + 'a37452086fbSAlex Crichton fn into_iter(self) -> impl Iterator<Item = (u32, StackMap<'a>)> + 'a { 38452086fbSAlex Crichton self.pcs 39452086fbSAlex Crichton .iter() 40452086fbSAlex Crichton .enumerate() 41452086fbSAlex Crichton .map(move |(i, pc)| (pc.get(LittleEndian), self.get(i).unwrap())) 42452086fbSAlex Crichton } 43452086fbSAlex Crichton 44452086fbSAlex Crichton /// Returns the stack map corresponding to the `i`th pc. get(&self, i: usize) -> Option<StackMap<'a>>45452086fbSAlex Crichton fn get(&self, i: usize) -> Option<StackMap<'a>> { 46452086fbSAlex Crichton let pointer_to_stack_map = self.pointers_to_stack_map[i].get(LittleEndian) as usize; 47452086fbSAlex Crichton let data = self.stack_map_data.get(pointer_to_stack_map..)?; 48452086fbSAlex Crichton 49452086fbSAlex Crichton let (frame_size, data) = data.split_first()?; 50452086fbSAlex Crichton let (count, data) = data.split_first()?; 51452086fbSAlex Crichton let data = data.get(..count.get(LittleEndian) as usize)?; 52452086fbSAlex Crichton 53452086fbSAlex Crichton Some(StackMap { 54452086fbSAlex Crichton frame_size: frame_size.get(LittleEndian), 55452086fbSAlex Crichton data, 56452086fbSAlex Crichton }) 57452086fbSAlex Crichton } 58452086fbSAlex Crichton } 5987c33c29SAlex Crichton 6087c33c29SAlex Crichton /// A map for determining where live GC references live in a stack frame. 6187c33c29SAlex Crichton /// 6287c33c29SAlex Crichton /// Note that this is currently primarily documented as cranelift's 6387c33c29SAlex Crichton /// `binemit::StackMap`, so for detailed documentation about this please read 6487c33c29SAlex Crichton /// the docs over there. 65452086fbSAlex Crichton pub struct StackMap<'a> { 66b2025eadSNick Fitzgerald frame_size: u32, 67*a2cc11f3SPhilip Craig data: &'a [U32<LittleEndian>], 6887c33c29SAlex Crichton } 6987c33c29SAlex Crichton 70452086fbSAlex Crichton impl<'a> StackMap<'a> { 71452086fbSAlex Crichton /// Looks up a stack map for `pc` within the `section` provided. 72452086fbSAlex Crichton /// 73452086fbSAlex Crichton /// The `section` should be produced by `StackMapSection` in the 74452086fbSAlex Crichton /// `compile::stack_map` module. The `pc` should be relative to the start 75452086fbSAlex Crichton /// of the `.text` section in the final executable. lookup(pc: u32, section: &'a [u8]) -> Option<StackMap<'a>>76452086fbSAlex Crichton pub fn lookup(pc: u32, section: &'a [u8]) -> Option<StackMap<'a>> { 77452086fbSAlex Crichton StackMapSection::parse(section)?.lookup(pc) 78452086fbSAlex Crichton } 79452086fbSAlex Crichton 80452086fbSAlex Crichton /// Iterate over the stack maps contained in the given stack map section. 81452086fbSAlex Crichton /// 82452086fbSAlex Crichton /// This function takes a `section` as its first argument which must have 83452086fbSAlex Crichton /// been created with `StackMapSection` builder. This is intended to be the 84452086fbSAlex Crichton /// raw `ELF_WASMTIME_STACK_MAP` section from the compilation artifact. 85452086fbSAlex Crichton /// 86452086fbSAlex Crichton /// The yielded offsets are relative to the start of the text section for 87452086fbSAlex Crichton /// this map's code object. iter(section: &'a [u8]) -> Option<impl Iterator<Item = (u32, StackMap<'a>)> + 'a>88452086fbSAlex Crichton pub fn iter(section: &'a [u8]) -> Option<impl Iterator<Item = (u32, StackMap<'a>)> + 'a> { 89452086fbSAlex Crichton Some(StackMapSection::parse(section)?.into_iter()) 9087c33c29SAlex Crichton } 9187c33c29SAlex Crichton 92b2025eadSNick Fitzgerald /// Returns the byte size of this stack map's frame. frame_size(&self) -> u3293b2025eadSNick Fitzgerald pub fn frame_size(&self) -> u32 { 94b2025eadSNick Fitzgerald self.frame_size 9587c33c29SAlex Crichton } 9687c33c29SAlex Crichton 97b2025eadSNick Fitzgerald /// Given a frame pointer, get the stack pointer. 98b2025eadSNick Fitzgerald /// 99b2025eadSNick Fitzgerald /// # Safety 100b2025eadSNick Fitzgerald /// 101b2025eadSNick Fitzgerald /// The `fp` must be the frame pointer at the code offset that this stack 102b2025eadSNick Fitzgerald /// map is associated with. sp(&self, fp: *mut usize) -> *mut usize103b2025eadSNick Fitzgerald pub unsafe fn sp(&self, fp: *mut usize) -> *mut usize { 104b2025eadSNick Fitzgerald let frame_size = usize::try_from(self.frame_size).unwrap(); 105073aedabSAlex Crichton unsafe { fp.byte_sub(frame_size) } 106b2025eadSNick Fitzgerald } 107b2025eadSNick Fitzgerald 108b2025eadSNick Fitzgerald /// Given the stack pointer, get a reference to each live GC reference in 109b2025eadSNick Fitzgerald /// the stack frame. 110b2025eadSNick Fitzgerald /// 111b2025eadSNick Fitzgerald /// # Safety 112b2025eadSNick Fitzgerald /// 113b2025eadSNick Fitzgerald /// The `sp` must be the stack pointer at the code offset that this stack 114b2025eadSNick Fitzgerald /// map is associated with. live_gc_refs(&self, sp: *mut usize) -> impl Iterator<Item = *mut u32> + '_115b2025eadSNick Fitzgerald pub unsafe fn live_gc_refs(&self, sp: *mut usize) -> impl Iterator<Item = *mut u32> + '_ { 116452086fbSAlex Crichton self.offsets().map(move |i| { 1172bac6574SAlex Crichton log::trace!("Live GC ref in frame at frame offset {i:#x}"); 118452086fbSAlex Crichton let i = usize::try_from(i).unwrap(); 119073aedabSAlex Crichton let ptr_to_gc_ref = unsafe { sp.byte_add(i) }; 120b2025eadSNick Fitzgerald 121b2025eadSNick Fitzgerald // Assert that the pointer is inside this stack map's frame. 122b2025eadSNick Fitzgerald assert!({ 123b2025eadSNick Fitzgerald let delta = ptr_to_gc_ref as usize - sp as usize; 124b2025eadSNick Fitzgerald let frame_size = usize::try_from(self.frame_size).unwrap(); 125b2025eadSNick Fitzgerald delta < frame_size 126b2025eadSNick Fitzgerald }); 127b2025eadSNick Fitzgerald 128b2025eadSNick Fitzgerald ptr_to_gc_ref.cast::<u32>() 129b2025eadSNick Fitzgerald }) 13087c33c29SAlex Crichton } 131452086fbSAlex Crichton 132452086fbSAlex Crichton /// Returns the offsets that this stack map registers GC references at. offsets(&self) -> impl Iterator<Item = u32> + '_133452086fbSAlex Crichton pub fn offsets(&self) -> impl Iterator<Item = u32> + '_ { 134452086fbSAlex Crichton // Here `self.data` is a bit set of offsets divided by 4, so iterate 135452086fbSAlex Crichton // over all the bits in `self.data` and multiply their position by 4. 136452086fbSAlex Crichton let bit_positions = self.data.iter().enumerate().flat_map(|(i, word)| { 137452086fbSAlex Crichton ScalarBitSet(word.get(LittleEndian)) 138452086fbSAlex Crichton .iter() 139452086fbSAlex Crichton .map(move |bit| (i as u32) * 32 + u32::from(bit)) 140452086fbSAlex Crichton }); 141452086fbSAlex Crichton 142452086fbSAlex Crichton bit_positions.map(|pos| pos * 4) 143452086fbSAlex Crichton } 14487c33c29SAlex Crichton } 145