Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/environ/src/address_map.rs
1691 views
1
//! Data structures to provide transformation of the source
2
3
use object::{Bytes, LittleEndian, U32Bytes};
4
use serde_derive::{Deserialize, Serialize};
5
6
/// Single source location to generated address mapping.
7
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
8
pub struct InstructionAddressMap {
9
/// Where in the source wasm binary this instruction comes from, specified
10
/// in an offset of bytes from the front of the file.
11
pub srcloc: FilePos,
12
13
/// Offset from the start of the function's compiled code to where this
14
/// instruction is located, or the region where it starts.
15
pub code_offset: u32,
16
}
17
18
/// A position within an original source file,
19
///
20
/// This structure is used as a newtype wrapper around a 32-bit integer which
21
/// represents an offset within a file where a wasm instruction or function is
22
/// to be originally found.
23
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
24
pub struct FilePos(u32);
25
26
impl FilePos {
27
/// Create a new file position with the given offset.
28
pub fn new(pos: u32) -> FilePos {
29
assert!(pos != u32::MAX);
30
FilePos(pos)
31
}
32
33
/// Returns the offset that this offset was created with.
34
///
35
/// Note that the `Default` implementation will return `None` here, whereas
36
/// positions created with `FilePos::new` will return `Some`.
37
pub fn file_offset(self) -> Option<u32> {
38
if self.0 == u32::MAX {
39
None
40
} else {
41
Some(self.0)
42
}
43
}
44
}
45
46
impl Default for FilePos {
47
fn default() -> FilePos {
48
FilePos(u32::MAX)
49
}
50
}
51
52
/// Parse an `ELF_WASMTIME_ADDRMAP` section, returning the slice of code offsets
53
/// and the slice of associated file positions for each offset.
54
fn parse_address_map(
55
section: &[u8],
56
) -> Option<(&[U32Bytes<LittleEndian>], &[U32Bytes<LittleEndian>])> {
57
let mut section = Bytes(section);
58
// NB: this matches the encoding written by `append_to` in the
59
// `compile::address_map` module.
60
let count = section.read::<U32Bytes<LittleEndian>>().ok()?;
61
let count = usize::try_from(count.get(LittleEndian)).ok()?;
62
let (offsets, section) =
63
object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;
64
let (positions, section) =
65
object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?;
66
debug_assert!(section.is_empty());
67
Some((offsets, positions))
68
}
69
70
/// Lookup an `offset` within an encoded address map section, returning the
71
/// original `FilePos` that corresponds to the offset, if found.
72
///
73
/// This function takes a `section` as its first argument which must have been
74
/// created with `AddressMapSection` above. This is intended to be the raw
75
/// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact.
76
///
77
/// The `offset` provided is a relative offset from the start of the text
78
/// section of the pc that is being looked up. If `offset` is out of range or
79
/// doesn't correspond to anything in this file then `None` is returned.
80
pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {
81
let (offsets, positions) = parse_address_map(section)?;
82
83
// First perform a binary search on the `offsets` array. This is a sorted
84
// array of offsets within the text section, which is conveniently what our
85
// `offset` also is. Note that we are somewhat unlikely to find a precise
86
// match on the element in the array, so we're largely interested in which
87
// "bucket" the `offset` falls into.
88
let offset = u32::try_from(offset).ok()?;
89
let index = match offsets.binary_search_by_key(&offset, |v| v.get(LittleEndian)) {
90
// Exact hit!
91
Ok(i) => i,
92
93
// This *would* be at the first slot in the array, so no
94
// instructions cover `pc`.
95
Err(0) => return None,
96
97
// This would be at the `nth` slot, so we're at the `n-1`th slot.
98
Err(n) => n - 1,
99
};
100
101
// Using the `index` we found of which bucket `offset` corresponds to we can
102
// lookup the actual `FilePos` value in the `positions` array.
103
let pos = positions.get(index)?;
104
Some(FilePos(pos.get(LittleEndian)))
105
}
106
107
/// Iterate over the address map contained in the given address map section.
108
///
109
/// This function takes a `section` as its first argument which must have been
110
/// created with `AddressMapSection` above. This is intended to be the raw
111
/// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact.
112
///
113
/// The yielded offsets are relative to the start of the text section for this
114
/// map's code object.
115
pub fn iterate_address_map<'a>(
116
section: &'a [u8],
117
) -> Option<impl Iterator<Item = (u32, FilePos)> + 'a> {
118
let (offsets, positions) = parse_address_map(section)?;
119
120
Some(
121
offsets
122
.iter()
123
.map(|o| o.get(LittleEndian))
124
.zip(positions.iter().map(|pos| FilePos(pos.get(LittleEndian)))),
125
)
126
}
127
128