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
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);
/// let core = CoreDumpSection::from_reader(&mut 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> FromReader<'a> for CoreDumpSection<'a> {
    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 name");
        }
        let name = reader.read_string()?;
        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 mut reader = BinaryReader::new(data);
/// let modules_section = CoreDumpModulesSection::from_reader(&mut 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> FromReader<'a> for CoreDumpModulesSection<'a> {
    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
        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()?);
        }
        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<'a> FromReader<'a> for CoreDumpInstancesSection {
    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
        let mut instances = vec![];
        for _ in 0..reader.read_var_u32()? {
            instances.push(CoreDumpInstance::from_reader(reader)?);
        }
        Ok(CoreDumpInstancesSection { instances })
    }
}

/// A single instance from a coredump instances section
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
///
/// ```
/// let data: &[u8] = &[0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x01, 0x00, 0x04,
///     0x2a, 0x33, 0x01, 0x7f, 0x01, 0x01, 0x7f, 0x02];
/// use wasmparser::{ BinaryReader, CoreDumpStackSection, FromReader };
/// let mut reader = BinaryReader::new(data);
/// let corestack = CoreDumpStackSection::from_reader(&mut 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> FromReader<'a> for CoreDumpStackSection<'a> {
    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 name");
        }
        let name = reader.read_string()?;
        let mut frames = vec![];
        for _ in 0..reader.read_var_u32()? {
            frames.push(CoreDumpStackFrame::from_reader(reader)?);
        }
        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"),
        }
    }
}