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