wasmtime: Privately expose a module's address map and its function's bytes (#5973)
This will allow us to build developer tools for Wasmtime and Cranelift like WAT and asm side-by-side viewers (a la Godbolt). These are not proper public APIs, so they are marked `doc(hidden)` and have comments saying they are only for use within this repo's workspace.
This commit is contained in:
@@ -116,6 +116,23 @@ impl AddressMapSection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse an `ELF_WASMTIME_ADDRMAP` section, returning the slice of code offsets
|
||||||
|
/// and the slice of associated file positions for each offset.
|
||||||
|
fn parse_address_map(
|
||||||
|
section: &[u8],
|
||||||
|
) -> Option<(&[U32Bytes<LittleEndian>], &[U32Bytes<LittleEndian>])> {
|
||||||
|
let mut section = Bytes(section);
|
||||||
|
// NB: this matches the encoding written by `append_to` above.
|
||||||
|
let count = section.read::<U32Bytes<LittleEndian>>().ok()?;
|
||||||
|
let count = usize::try_from(count.get(LittleEndian)).ok()?;
|
||||||
|
let (offsets, section) =
|
||||||
|
object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;
|
||||||
|
let (positions, section) =
|
||||||
|
object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?;
|
||||||
|
debug_assert!(section.is_empty());
|
||||||
|
Some((offsets, positions))
|
||||||
|
}
|
||||||
|
|
||||||
/// Lookup an `offset` within an encoded address map section, returning the
|
/// Lookup an `offset` within an encoded address map section, returning the
|
||||||
/// original `FilePos` that corresponds to the offset, if found.
|
/// original `FilePos` that corresponds to the offset, if found.
|
||||||
///
|
///
|
||||||
@@ -127,15 +144,7 @@ impl AddressMapSection {
|
|||||||
/// section of the pc that is being looked up. If `offset` is out of range or
|
/// section of the pc that is being looked up. If `offset` is out of range or
|
||||||
/// doesn't correspond to anything in this file then `None` is returned.
|
/// doesn't correspond to anything in this file then `None` is returned.
|
||||||
pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {
|
pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {
|
||||||
let mut section = Bytes(section);
|
let (offsets, positions) = parse_address_map(section)?;
|
||||||
// NB: this matches the encoding written by `append_to` above.
|
|
||||||
let count = section.read::<U32Bytes<LittleEndian>>().ok()?;
|
|
||||||
let count = usize::try_from(count.get(LittleEndian)).ok()?;
|
|
||||||
let (offsets, section) =
|
|
||||||
object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;
|
|
||||||
let (positions, section) =
|
|
||||||
object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?;
|
|
||||||
debug_assert!(section.is_empty());
|
|
||||||
|
|
||||||
// First perform a binary search on the `offsets` array. This is a sorted
|
// First perform a binary search on the `offsets` array. This is a sorted
|
||||||
// array of offsets within the text section, which is conveniently what our
|
// array of offsets within the text section, which is conveniently what our
|
||||||
@@ -160,3 +169,24 @@ pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {
|
|||||||
let pos = positions.get(index)?;
|
let pos = positions.get(index)?;
|
||||||
Some(FilePos(pos.get(LittleEndian)))
|
Some(FilePos(pos.get(LittleEndian)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterate over the address map contained in the given address map section.
|
||||||
|
///
|
||||||
|
/// This function takes a `section` as its first argument which must have been
|
||||||
|
/// created with `AddressMapSection` above. This is intended to be the raw
|
||||||
|
/// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact.
|
||||||
|
///
|
||||||
|
/// The yielded offsets are relative to the start of the text section for this
|
||||||
|
/// map's code object.
|
||||||
|
pub fn iterate_address_map<'a>(
|
||||||
|
section: &'a [u8],
|
||||||
|
) -> Option<impl Iterator<Item = (u32, FilePos)> + 'a> {
|
||||||
|
let (offsets, positions) = parse_address_map(section)?;
|
||||||
|
|
||||||
|
Some(
|
||||||
|
offsets
|
||||||
|
.iter()
|
||||||
|
.map(|o| o.get(LittleEndian))
|
||||||
|
.zip(positions.iter().map(|pos| FilePos(pos.get(LittleEndian)))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1042,6 +1042,73 @@ impl Module {
|
|||||||
self.inner.memory_images()?;
|
self.inner.memory_images()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the map from `.text` section offsets to Wasm binary offsets for this
|
||||||
|
/// module.
|
||||||
|
///
|
||||||
|
/// Each entry is a (`.text` section offset, Wasm binary offset) pair.
|
||||||
|
///
|
||||||
|
/// Entries are yielded in order of `.text` section offset.
|
||||||
|
///
|
||||||
|
/// Some entries are missing a Wasm binary offset. This is for code that is
|
||||||
|
/// not associated with any single location in the Wasm binary, or for when
|
||||||
|
/// source information was optimized away.
|
||||||
|
///
|
||||||
|
/// Not every module has an address map, since address map generation can be
|
||||||
|
/// turned off on `Config`.
|
||||||
|
///
|
||||||
|
/// There is not an entry for every `.text` section offset. Every offset
|
||||||
|
/// after an entry's offset, but before the next entry's offset, is
|
||||||
|
/// considered to map to the same Wasm binary offset as the original
|
||||||
|
/// entry. For example, the address map will not contain the following
|
||||||
|
/// sequnce of entries:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// [
|
||||||
|
/// // ...
|
||||||
|
/// (10, Some(42)),
|
||||||
|
/// (11, Some(42)),
|
||||||
|
/// (12, Some(42)),
|
||||||
|
/// (13, Some(43)),
|
||||||
|
/// // ...
|
||||||
|
/// ]
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Instead, it will drop the entries for offsets `11` and `12` since they
|
||||||
|
/// are the same as the entry for offset `10`:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// [
|
||||||
|
/// // ...
|
||||||
|
/// (10, Some(42)),
|
||||||
|
/// (13, Some(43)),
|
||||||
|
/// // ...
|
||||||
|
/// ]
|
||||||
|
/// ```
|
||||||
|
pub fn address_map<'a>(&'a self) -> Option<impl Iterator<Item = (usize, Option<u32>)> + 'a> {
|
||||||
|
Some(
|
||||||
|
wasmtime_environ::iterate_address_map(
|
||||||
|
self.code_object().code_memory().address_map_data(),
|
||||||
|
)?
|
||||||
|
.map(|(offset, file_pos)| (offset as usize, file_pos.file_offset())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get this module's code object's `.text` section, containing its compiled
|
||||||
|
/// executable code.
|
||||||
|
pub fn text(&self) -> &[u8] {
|
||||||
|
self.code_object().code_memory().text()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the locations of functions in this module's `.text` section.
|
||||||
|
///
|
||||||
|
/// Each function's locartion is a (`.text` section offset, length) pair.
|
||||||
|
pub fn function_locations<'a>(&'a self) -> impl ExactSizeIterator<Item = (usize, usize)> + 'a {
|
||||||
|
self.compiled_module().finished_functions().map(|(f, _)| {
|
||||||
|
let loc = self.compiled_module().func_loc(f);
|
||||||
|
(loc.start as usize, loc.length as usize)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleInner {
|
impl ModuleInner {
|
||||||
|
|||||||
Reference in New Issue
Block a user