Path: blob/main/crates/environ/src/address_map.rs
3081 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/// Get the null file position.33pub fn none() -> FilePos {34FilePos(u32::MAX)35}3637/// Is this the null file position?38#[inline]39pub fn is_none(&self) -> bool {40*self == FilePos::none()41}4243/// Returns the offset that this offset was created with.44///45/// Note that positions created with `FilePos::none` and the `Default`46/// implementation will return `None` here, whereas positions created with47/// `FilePos::new` will return `Some`.48pub fn file_offset(self) -> Option<u32> {49if self.0 == u32::MAX {50None51} else {52Some(self.0)53}54}55}5657impl Default for FilePos {58fn default() -> FilePos {59FilePos::none()60}61}6263/// Parse an `ELF_WASMTIME_ADDRMAP` section, returning the slice of code offsets64/// and the slice of associated file positions for each offset.65fn parse_address_map(66section: &[u8],67) -> Option<(&[U32Bytes<LittleEndian>], &[U32Bytes<LittleEndian>])> {68let mut section = Bytes(section);69// NB: this matches the encoding written by `append_to` in the70// `compile::address_map` module.71let count = section.read::<U32Bytes<LittleEndian>>().ok()?;72let count = usize::try_from(count.get(LittleEndian)).ok()?;73let (offsets, section) =74object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;75let (positions, section) =76object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?;77debug_assert!(section.is_empty());78Some((offsets, positions))79}8081/// Lookup an `offset` within an encoded address map section, returning the82/// original `FilePos` that corresponds to the offset, if found.83///84/// This function takes a `section` as its first argument which must have been85/// created with `AddressMapSection` above. This is intended to be the raw86/// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact.87///88/// The `offset` provided is a relative offset from the start of the text89/// section of the pc that is being looked up. If `offset` is out of range or90/// doesn't correspond to anything in this file then `None` is returned.91pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {92let (offsets, positions) = parse_address_map(section)?;9394// First perform a binary search on the `offsets` array. This is a sorted95// array of offsets within the text section, which is conveniently what our96// `offset` also is. Note that we are somewhat unlikely to find a precise97// match on the element in the array, so we're largely interested in which98// "bucket" the `offset` falls into.99let offset = u32::try_from(offset).ok()?;100let index = match offsets.binary_search_by_key(&offset, |v| v.get(LittleEndian)) {101// Exact hit!102Ok(i) => i,103104// This *would* be at the first slot in the array, so no105// instructions cover `pc`.106Err(0) => return None,107108// This would be at the `nth` slot, so we're at the `n-1`th slot.109Err(n) => n - 1,110};111112// Using the `index` we found of which bucket `offset` corresponds to we can113// lookup the actual `FilePos` value in the `positions` array.114let pos = positions.get(index)?;115Some(FilePos(pos.get(LittleEndian)))116}117118/// Iterate over the address map contained in the given address map section.119///120/// This function takes a `section` as its first argument which must have been121/// created with `AddressMapSection` above. This is intended to be the raw122/// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact.123///124/// The yielded offsets are relative to the start of the text section for this125/// map's code object.126pub fn iterate_address_map<'a>(127section: &'a [u8],128) -> Option<impl Iterator<Item = (u32, FilePos)> + 'a> {129let (offsets, positions) = parse_address_map(section)?;130131Some(132offsets133.iter()134.map(|o| o.get(LittleEndian))135.zip(positions.iter().map(|pos| FilePos(pos.get(LittleEndian)))),136)137}138139140