Path: blob/main/crates/environ/src/address_map.rs
1691 views
//! Data structures to provide transformation of the source12use object::{Bytes, LittleEndian, U32Bytes};3use serde_derive::{Deserialize, Serialize};45/// Single source location to generated address mapping.6#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]7pub struct InstructionAddressMap {8/// Where in the source wasm binary this instruction comes from, specified9/// in an offset of bytes from the front of the file.10pub srcloc: FilePos,1112/// Offset from the start of the function's compiled code to where this13/// instruction is located, or the region where it starts.14pub code_offset: u32,15}1617/// A position within an original source file,18///19/// This structure is used as a newtype wrapper around a 32-bit integer which20/// represents an offset within a file where a wasm instruction or function is21/// to be originally found.22#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]23pub struct FilePos(u32);2425impl FilePos {26/// Create a new file position with the given offset.27pub fn new(pos: u32) -> FilePos {28assert!(pos != u32::MAX);29FilePos(pos)30}3132/// Returns the offset that this offset was created with.33///34/// Note that the `Default` implementation will return `None` here, whereas35/// positions created with `FilePos::new` will return `Some`.36pub fn file_offset(self) -> Option<u32> {37if self.0 == u32::MAX {38None39} else {40Some(self.0)41}42}43}4445impl Default for FilePos {46fn default() -> FilePos {47FilePos(u32::MAX)48}49}5051/// Parse an `ELF_WASMTIME_ADDRMAP` section, returning the slice of code offsets52/// and the slice of associated file positions for each offset.53fn parse_address_map(54section: &[u8],55) -> Option<(&[U32Bytes<LittleEndian>], &[U32Bytes<LittleEndian>])> {56let mut section = Bytes(section);57// NB: this matches the encoding written by `append_to` in the58// `compile::address_map` module.59let count = section.read::<U32Bytes<LittleEndian>>().ok()?;60let count = usize::try_from(count.get(LittleEndian)).ok()?;61let (offsets, section) =62object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;63let (positions, section) =64object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?;65debug_assert!(section.is_empty());66Some((offsets, positions))67}6869/// Lookup an `offset` within an encoded address map section, returning the70/// original `FilePos` that corresponds to the offset, if found.71///72/// This function takes a `section` as its first argument which must have been73/// created with `AddressMapSection` above. This is intended to be the raw74/// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact.75///76/// The `offset` provided is a relative offset from the start of the text77/// section of the pc that is being looked up. If `offset` is out of range or78/// doesn't correspond to anything in this file then `None` is returned.79pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {80let (offsets, positions) = parse_address_map(section)?;8182// First perform a binary search on the `offsets` array. This is a sorted83// array of offsets within the text section, which is conveniently what our84// `offset` also is. Note that we are somewhat unlikely to find a precise85// match on the element in the array, so we're largely interested in which86// "bucket" the `offset` falls into.87let offset = u32::try_from(offset).ok()?;88let index = match offsets.binary_search_by_key(&offset, |v| v.get(LittleEndian)) {89// Exact hit!90Ok(i) => i,9192// This *would* be at the first slot in the array, so no93// instructions cover `pc`.94Err(0) => return None,9596// This would be at the `nth` slot, so we're at the `n-1`th slot.97Err(n) => n - 1,98};99100// Using the `index` we found of which bucket `offset` corresponds to we can101// lookup the actual `FilePos` value in the `positions` array.102let pos = positions.get(index)?;103Some(FilePos(pos.get(LittleEndian)))104}105106/// Iterate over the address map contained in the given address map section.107///108/// This function takes a `section` as its first argument which must have been109/// created with `AddressMapSection` above. This is intended to be the raw110/// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact.111///112/// The yielded offsets are relative to the start of the text section for this113/// map's code object.114pub fn iterate_address_map<'a>(115section: &'a [u8],116) -> Option<impl Iterator<Item = (u32, FilePos)> + 'a> {117let (offsets, positions) = parse_address_map(section)?;118119Some(120offsets121.iter()122.map(|o| o.get(LittleEndian))123.zip(positions.iter().map(|pos| FilePos(pos.get(LittleEndian)))),124)125}126127128