wasmparser/readers/core/
coredumps.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
use crate::prelude::*;
use crate::{BinaryReader, FromReader, Result};

/// The data portion of a custom section representing a core dump. Per the
/// tool-conventions repo, this section just specifies the executable name that
/// the core dump came from while the rest of the core dump information is
/// contained in a corestack custom section
///
/// # Examples
///
/// ```
/// use wasmparser::{BinaryReader, CoreDumpSection, FromReader, Result};
/// let data: &[u8] = &[0x00, 0x09, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x77, 0x61,
///      0x73, 0x6d];
/// let mut reader = BinaryReader::new(data, 0);
/// let core = CoreDumpSection::new(reader).unwrap();
/// assert!(core.name == "test.wasm")
/// ```
pub struct CoreDumpSection<'a> {
    /// The name of the process that created the core dump
    pub name: &'a str,
}

impl<'a> CoreDumpSection<'a> {
    /// Parses this section from the provided `reader`, derived from a custom
    /// section.
    pub fn new(mut reader: BinaryReader<'a>) -> Result<CoreDumpSection<'a>> {
        let pos = reader.original_position();
        if reader.read_u8()? != 0 {
            bail!(pos, "invalid start byte for core dump name");
        }
        let name = reader.read_string()?;
        if !reader.eof() {
            bail!(
                reader.original_position(),
                "trailing bytes at end of custom section"
            );
        }
        Ok(CoreDumpSection { name })
    }
}

/// The data portion of a "coremodules" custom section. This contains a vec of
/// module names that will be referenced by index by other coredump sections.
///
/// # Example
///
/// ```
/// use wasmparser::{BinaryReader, CoreDumpModulesSection, FromReader, Result};
/// let data: &[u8] = &[0x01, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74];
/// let reader = BinaryReader::new(data, 0);
/// let modules_section = CoreDumpModulesSection::new(reader).unwrap();
/// assert!(modules_section.modules[0] == "test")
/// ```
#[derive(Debug)]
pub struct CoreDumpModulesSection<'a> {
    /// A list of module names, which may be URLs, file paths, or other
    /// identifiers for the module.
    pub modules: Vec<&'a str>,
}

impl<'a> CoreDumpModulesSection<'a> {
    /// Parses this section from the provided `reader`, derived from a custom
    /// section.
    pub fn new(mut reader: BinaryReader<'a>) -> Result<CoreDumpModulesSection<'a>> {
        let pos = reader.original_position();
        let mut modules = vec![];
        for _ in 0..reader.read_var_u32()? {
            if reader.read_u8()? != 0 {
                bail!(pos, "invalid start byte for coremodule");
            }
            modules.push(reader.read_string()?);
        }
        if !reader.eof() {
            bail!(
                reader.original_position(),
                "trailing bytes at end of custom section"
            );
        }
        Ok(CoreDumpModulesSection { modules })
    }
}
/// A custom section representing the instances involved in a given coredump
pub struct CoreDumpInstancesSection {
    /// The instances for the coredump
    pub instances: Vec<CoreDumpInstance>,
}

impl CoreDumpInstancesSection {
    /// Parses this section from the provided `reader`, derived from a custom
    /// section.
    pub fn new(mut reader: BinaryReader<'_>) -> Result<CoreDumpInstancesSection> {
        let mut instances = vec![];
        for _ in 0..reader.read_var_u32()? {
            instances.push(CoreDumpInstance::from_reader(&mut reader)?);
        }
        if !reader.eof() {
            bail!(
                reader.original_position(),
                "trailing bytes at end of custom section"
            );
        }
        Ok(CoreDumpInstancesSection { instances })
    }
}

/// A single instance from a coredump instances section
#[derive(Debug)]
pub struct CoreDumpInstance {
    /// The module that this is an instance of, as an index into a "coremodules"
    /// section.
    pub module_index: u32,

    /// Which of the coredump's memories are this instance's memories, via
    /// indexing into the memory index space.
    pub memories: Vec<u32>,

    /// Which of the coredump's globals are this instance's globals, via
    /// indexing into the global index space.
    pub globals: Vec<u32>,
}

impl<'a> FromReader<'a> for CoreDumpInstance {
    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
        let pos = reader.original_position();
        if reader.read_u8()? != 0 {
            bail!(pos, "invalid start byte for core dump instance");
        }
        let module_index = reader.read_var_u32()?;
        let mut memories = vec![];
        for _ in 0..reader.read_var_u32()? {
            memories.push(reader.read_var_u32()?);
        }
        let mut globals = vec![];

        for _ in 0..reader.read_var_u32()? {
            globals.push(reader.read_var_u32()?);
        }

        Ok(CoreDumpInstance {
            module_index,
            memories,
            globals,
        })
    }
}

/// The data portion of a custom section representing a core dump stack. The
/// structure of this follows the coredump spec in the tool-conventions repo
///
/// # Examples
///
/// ```
/// use wasmparser::{BinaryReader, CoreDumpStackSection, FromReader};
///
/// let data: &[u8] = &[0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x01, 0x00, 0x04,
///     0x2a, 0x33, 0x01, 0x7f, 0x01, 0x01, 0x7f, 0x02];
/// let reader = BinaryReader::new(data, 0);
/// let corestack = CoreDumpStackSection::new(reader).unwrap();
/// assert!(corestack.name == "main");
/// assert!(corestack.frames.len() == 1);
/// let frame = &corestack.frames[0];
/// assert!(frame.instanceidx == 4);
/// assert!(frame.funcidx == 42);
/// assert!(frame.codeoffset == 51);
/// assert!(frame.locals.len() == 1);
/// assert!(frame.stack.len() == 1);
/// ```
pub struct CoreDumpStackSection<'a> {
    /// The thread name
    pub name: &'a str,
    /// The stack frames for the core dump
    pub frames: Vec<CoreDumpStackFrame>,
}

impl<'a> CoreDumpStackSection<'a> {
    /// Parses this section from the provided `reader`, derived from a custom
    /// section.
    pub fn new(mut reader: BinaryReader<'a>) -> Result<CoreDumpStackSection<'a>> {
        let pos = reader.original_position();
        if reader.read_u8()? != 0 {
            bail!(pos, "invalid start byte for core dump stack name");
        }
        let name = reader.read_string()?;
        let mut frames = vec![];
        for _ in 0..reader.read_var_u32()? {
            frames.push(CoreDumpStackFrame::from_reader(&mut reader)?);
        }
        if !reader.eof() {
            bail!(
                reader.original_position(),
                "trailing bytes at end of custom section"
            );
        }
        Ok(CoreDumpStackSection {
            name: name,
            frames: frames,
        })
    }
}

/// A single stack frame from a core dump
#[derive(Debug)]
pub struct CoreDumpStackFrame {
    /// The instance that this stack frame belongs to.
    pub instanceidx: u32,
    /// The function index in the module
    pub funcidx: u32,
    /// The instruction's offset relative to the function's start
    pub codeoffset: u32,
    /// The locals for this stack frame (including function parameters)
    pub locals: Vec<CoreDumpValue>,
    /// The values on the stack
    pub stack: Vec<CoreDumpValue>,
}

impl<'a> FromReader<'a> for CoreDumpStackFrame {
    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
        let pos = reader.original_position();
        if reader.read_u8()? != 0 {
            bail!(pos, "invalid start byte for core dump stack frame");
        }
        let instanceidx = reader.read_var_u32()?;
        let funcidx = reader.read_var_u32()?;
        let codeoffset = reader.read_var_u32()?;
        let mut locals = vec![];
        for _ in 0..reader.read_var_u32()? {
            locals.push(CoreDumpValue::from_reader(reader)?);
        }
        let mut stack = vec![];
        for _ in 0..reader.read_var_u32()? {
            stack.push(CoreDumpValue::from_reader(reader)?);
        }

        Ok(CoreDumpStackFrame {
            instanceidx,
            funcidx,
            codeoffset,
            locals,
            stack,
        })
    }
}

/// Local and stack values are encoded using one byte for the type (similar to
/// Wasm's Number Types) followed by bytes representing the actual value
/// See the tool-conventions repo for more details.
#[derive(Clone, Debug)]
pub enum CoreDumpValue {
    /// A missing value (usually missing because it was optimized out)
    Missing,
    /// An i32 value
    I32(i32),
    /// An i64 value
    I64(i64),
    /// An f32 value
    F32(f32),
    /// An f64 value
    F64(f64),
}

impl<'a> FromReader<'a> for CoreDumpValue {
    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
        let pos = reader.original_position();
        match reader.read_u8()? {
            0x01 => Ok(CoreDumpValue::Missing),
            0x7F => Ok(CoreDumpValue::I32(reader.read_var_i32()?)),
            0x7E => Ok(CoreDumpValue::I64(reader.read_var_i64()?)),
            0x7D => Ok(CoreDumpValue::F32(f32::from_bits(
                reader.read_f32()?.bits(),
            ))),
            0x7C => Ok(CoreDumpValue::F64(f64::from_bits(
                reader.read_f64()?.bits(),
            ))),
            _ => bail!(pos, "invalid CoreDumpValue type"),
        }
    }
}