Coverage Report

Created: 2025-10-12 07:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasmtime/crates/environ/src/address_map.rs
Line
Count
Source
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
36.1M
    pub fn new(pos: u32) -> FilePos {
29
36.1M
        assert!(pos != u32::MAX);
30
36.1M
        FilePos(pos)
31
36.1M
    }
32
33
    /// Get the null file position.
34
45.8M
    pub fn none() -> FilePos {
35
45.8M
        FilePos(u32::MAX)
36
45.8M
    }
37
38
    /// Is this the null file position?
39
    #[inline]
40
16.5M
    pub fn is_none(&self) -> bool {
41
16.5M
        *self == FilePos::none()
42
16.5M
    }
43
44
    /// Returns the offset that this offset was created with.
45
    ///
46
    /// Note that positions created with `FilePos::none` and the `Default`
47
    /// implementation will return `None` here, whereas positions created with
48
    /// `FilePos::new` will return `Some`.
49
45.1M
    pub fn file_offset(self) -> Option<u32> {
50
45.1M
        if self.0 == u32::MAX {
51
12.4M
            None
52
        } else {
53
32.6M
            Some(self.0)
54
        }
55
45.1M
    }
56
}
57
58
impl Default for FilePos {
59
24.2M
    fn default() -> FilePos {
60
24.2M
        FilePos::none()
61
24.2M
    }
62
}
63
64
/// Parse an `ELF_WASMTIME_ADDRMAP` section, returning the slice of code offsets
65
/// and the slice of associated file positions for each offset.
66
16.5M
fn parse_address_map(
67
16.5M
    section: &[u8],
68
16.5M
) -> Option<(&[U32Bytes<LittleEndian>], &[U32Bytes<LittleEndian>])> {
69
16.5M
    let mut section = Bytes(section);
70
    // NB: this matches the encoding written by `append_to` in the
71
    // `compile::address_map` module.
72
16.5M
    let count = section.read::<U32Bytes<LittleEndian>>().ok()?;
73
8.42M
    let count = usize::try_from(count.get(LittleEndian)).ok()?;
74
8.42M
    let (offsets, section) =
75
8.42M
        object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;
76
8.42M
    let (positions, section) =
77
8.42M
        object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?;
78
8.42M
    debug_assert!(section.is_empty());
79
8.42M
    Some((offsets, positions))
80
16.5M
}
81
82
/// Lookup an `offset` within an encoded address map section, returning the
83
/// original `FilePos` that corresponds to the offset, if found.
84
///
85
/// This function takes a `section` as its first argument which must have been
86
/// created with `AddressMapSection` above. This is intended to be the raw
87
/// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact.
88
///
89
/// The `offset` provided is a relative offset from the start of the text
90
/// section of the pc that is being looked up. If `offset` is out of range or
91
/// doesn't correspond to anything in this file then `None` is returned.
92
16.5M
pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {
93
16.5M
    let (offsets, positions) = parse_address_map(section)?;
94
95
    // First perform a binary search on the `offsets` array. This is a sorted
96
    // array of offsets within the text section, which is conveniently what our
97
    // `offset` also is. Note that we are somewhat unlikely to find a precise
98
    // match on the element in the array, so we're largely interested in which
99
    // "bucket" the `offset` falls into.
100
8.42M
    let offset = u32::try_from(offset).ok()?;
101
70.9M
    let index = match offsets.binary_search_by_key(&offset, |v| v.get(LittleEndian)) {
102
        // Exact hit!
103
83.9k
        Ok(i) => i,
104
105
        // This *would* be at the first slot in the array, so no
106
        // instructions cover `pc`.
107
0
        Err(0) => return None,
108
109
        // This would be at the `nth` slot, so we're at the `n-1`th slot.
110
8.34M
        Err(n) => n - 1,
111
    };
112
113
    // Using the `index` we found of which bucket `offset` corresponds to we can
114
    // lookup the actual `FilePos` value in the `positions` array.
115
8.42M
    let pos = positions.get(index)?;
116
8.42M
    Some(FilePos(pos.get(LittleEndian)))
117
16.5M
}
118
119
/// Iterate over the address map contained in the given address map section.
120
///
121
/// This function takes a `section` as its first argument which must have been
122
/// created with `AddressMapSection` above. This is intended to be the raw
123
/// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact.
124
///
125
/// The yielded offsets are relative to the start of the text section for this
126
/// map's code object.
127
0
pub fn iterate_address_map<'a>(
128
0
    section: &'a [u8],
129
0
) -> Option<impl Iterator<Item = (u32, FilePos)> + 'a> {
130
0
    let (offsets, positions) = parse_address_map(section)?;
131
132
    Some(
133
0
        offsets
134
0
            .iter()
135
0
            .map(|o| o.get(LittleEndian))
136
0
            .zip(positions.iter().map(|pos| FilePos(pos.get(LittleEndian)))),
137
    )
138
0
}