1 | use crate::prelude::*; |
2 | use crate::{BinaryReader, FromReader, Result}; |
3 | |
4 | /// The data portion of a custom section representing a core dump. Per the |
5 | /// tool-conventions repo, this section just specifies the executable name that |
6 | /// the core dump came from while the rest of the core dump information is |
7 | /// contained in a corestack custom section |
8 | /// |
9 | /// # Examples |
10 | /// |
11 | /// ``` |
12 | /// use wasmparser::{BinaryReader, CoreDumpSection, FromReader, Result}; |
13 | /// let data: &[u8] = &[0x00, 0x09, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x77, 0x61, |
14 | /// 0x73, 0x6d]; |
15 | /// let mut reader = BinaryReader::new(data, 0); |
16 | /// let core = CoreDumpSection::new(reader).unwrap(); |
17 | /// assert!(core.name == "test.wasm" ) |
18 | /// ``` |
19 | pub struct CoreDumpSection<'a> { |
20 | /// The name of the process that created the core dump |
21 | pub name: &'a str, |
22 | } |
23 | |
24 | impl<'a> CoreDumpSection<'a> { |
25 | /// Parses this section from the provided `reader`, derived from a custom |
26 | /// section. |
27 | pub fn new(mut reader: BinaryReader<'a>) -> Result<CoreDumpSection<'a>> { |
28 | let pos: usize = reader.original_position(); |
29 | if reader.read_u8()? != 0 { |
30 | bail!(pos, "invalid start byte for core dump name" ); |
31 | } |
32 | let name: &'a str = reader.read_string()?; |
33 | if !reader.eof() { |
34 | bail!( |
35 | reader.original_position(), |
36 | "trailing bytes at end of custom section" |
37 | ); |
38 | } |
39 | Ok(CoreDumpSection { name }) |
40 | } |
41 | } |
42 | |
43 | /// The data portion of a "coremodules" custom section. This contains a vec of |
44 | /// module names that will be referenced by index by other coredump sections. |
45 | /// |
46 | /// # Example |
47 | /// |
48 | /// ``` |
49 | /// use wasmparser::{BinaryReader, CoreDumpModulesSection, FromReader, Result}; |
50 | /// let data: &[u8] = &[0x01, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74]; |
51 | /// let reader = BinaryReader::new(data, 0); |
52 | /// let modules_section = CoreDumpModulesSection::new(reader).unwrap(); |
53 | /// assert!(modules_section.modules[0] == "test" ) |
54 | /// ``` |
55 | #[derive (Debug)] |
56 | pub struct CoreDumpModulesSection<'a> { |
57 | /// A list of module names, which may be URLs, file paths, or other |
58 | /// identifiers for the module. |
59 | pub modules: Vec<&'a str>, |
60 | } |
61 | |
62 | impl<'a> CoreDumpModulesSection<'a> { |
63 | /// Parses this section from the provided `reader`, derived from a custom |
64 | /// section. |
65 | pub fn new(mut reader: BinaryReader<'a>) -> Result<CoreDumpModulesSection<'a>> { |
66 | let pos: usize = reader.original_position(); |
67 | let mut modules: Vec<&'a str> = vec![]; |
68 | for _ in 0..reader.read_var_u32()? { |
69 | if reader.read_u8()? != 0 { |
70 | bail!(pos, "invalid start byte for coremodule" ); |
71 | } |
72 | modules.push(reader.read_string()?); |
73 | } |
74 | if !reader.eof() { |
75 | bail!( |
76 | reader.original_position(), |
77 | "trailing bytes at end of custom section" |
78 | ); |
79 | } |
80 | Ok(CoreDumpModulesSection { modules }) |
81 | } |
82 | } |
83 | /// A custom section representing the instances involved in a given coredump |
84 | pub struct CoreDumpInstancesSection { |
85 | /// The instances for the coredump |
86 | pub instances: Vec<CoreDumpInstance>, |
87 | } |
88 | |
89 | impl CoreDumpInstancesSection { |
90 | /// Parses this section from the provided `reader`, derived from a custom |
91 | /// section. |
92 | pub fn new(mut reader: BinaryReader<'_>) -> Result<CoreDumpInstancesSection> { |
93 | let mut instances: Vec = vec![]; |
94 | for _ in 0..reader.read_var_u32()? { |
95 | instances.push(CoreDumpInstance::from_reader(&mut reader)?); |
96 | } |
97 | if !reader.eof() { |
98 | bail!( |
99 | reader.original_position(), |
100 | "trailing bytes at end of custom section" |
101 | ); |
102 | } |
103 | Ok(CoreDumpInstancesSection { instances }) |
104 | } |
105 | } |
106 | |
107 | /// A single instance from a coredump instances section |
108 | #[derive (Debug)] |
109 | pub struct CoreDumpInstance { |
110 | /// The module that this is an instance of, as an index into a "coremodules" |
111 | /// section. |
112 | pub module_index: u32, |
113 | |
114 | /// Which of the coredump's memories are this instance's memories, via |
115 | /// indexing into the memory index space. |
116 | pub memories: Vec<u32>, |
117 | |
118 | /// Which of the coredump's globals are this instance's globals, via |
119 | /// indexing into the global index space. |
120 | pub globals: Vec<u32>, |
121 | } |
122 | |
123 | impl<'a> FromReader<'a> for CoreDumpInstance { |
124 | fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
125 | let pos: usize = reader.original_position(); |
126 | if reader.read_u8()? != 0 { |
127 | bail!(pos, "invalid start byte for core dump instance" ); |
128 | } |
129 | let module_index: u32 = reader.read_var_u32()?; |
130 | let mut memories: Vec = vec![]; |
131 | for _ in 0..reader.read_var_u32()? { |
132 | memories.push(reader.read_var_u32()?); |
133 | } |
134 | let mut globals: Vec = vec![]; |
135 | |
136 | for _ in 0..reader.read_var_u32()? { |
137 | globals.push(reader.read_var_u32()?); |
138 | } |
139 | |
140 | Ok(CoreDumpInstance { |
141 | module_index, |
142 | memories, |
143 | globals, |
144 | }) |
145 | } |
146 | } |
147 | |
148 | /// The data portion of a custom section representing a core dump stack. The |
149 | /// structure of this follows the coredump spec in the tool-conventions repo |
150 | /// |
151 | /// # Examples |
152 | /// |
153 | /// ``` |
154 | /// use wasmparser::{BinaryReader, CoreDumpStackSection, FromReader}; |
155 | /// |
156 | /// let data: &[u8] = &[0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x01, 0x00, 0x04, |
157 | /// 0x2a, 0x33, 0x01, 0x7f, 0x01, 0x01, 0x7f, 0x02]; |
158 | /// let reader = BinaryReader::new(data, 0); |
159 | /// let corestack = CoreDumpStackSection::new(reader).unwrap(); |
160 | /// assert!(corestack.name == "main" ); |
161 | /// assert!(corestack.frames.len() == 1); |
162 | /// let frame = &corestack.frames[0]; |
163 | /// assert!(frame.instanceidx == 4); |
164 | /// assert!(frame.funcidx == 42); |
165 | /// assert!(frame.codeoffset == 51); |
166 | /// assert!(frame.locals.len() == 1); |
167 | /// assert!(frame.stack.len() == 1); |
168 | /// ``` |
169 | pub struct CoreDumpStackSection<'a> { |
170 | /// The thread name |
171 | pub name: &'a str, |
172 | /// The stack frames for the core dump |
173 | pub frames: Vec<CoreDumpStackFrame>, |
174 | } |
175 | |
176 | impl<'a> CoreDumpStackSection<'a> { |
177 | /// Parses this section from the provided `reader`, derived from a custom |
178 | /// section. |
179 | pub fn new(mut reader: BinaryReader<'a>) -> Result<CoreDumpStackSection<'a>> { |
180 | let pos = reader.original_position(); |
181 | if reader.read_u8()? != 0 { |
182 | bail!(pos, "invalid start byte for core dump stack name" ); |
183 | } |
184 | let name = reader.read_string()?; |
185 | let mut frames = vec![]; |
186 | for _ in 0..reader.read_var_u32()? { |
187 | frames.push(CoreDumpStackFrame::from_reader(&mut reader)?); |
188 | } |
189 | if !reader.eof() { |
190 | bail!( |
191 | reader.original_position(), |
192 | "trailing bytes at end of custom section" |
193 | ); |
194 | } |
195 | Ok(CoreDumpStackSection { |
196 | name: name, |
197 | frames: frames, |
198 | }) |
199 | } |
200 | } |
201 | |
202 | /// A single stack frame from a core dump |
203 | #[derive (Debug)] |
204 | pub struct CoreDumpStackFrame { |
205 | /// The instance that this stack frame belongs to. |
206 | pub instanceidx: u32, |
207 | /// The function index in the module |
208 | pub funcidx: u32, |
209 | /// The instruction's offset relative to the function's start |
210 | pub codeoffset: u32, |
211 | /// The locals for this stack frame (including function parameters) |
212 | pub locals: Vec<CoreDumpValue>, |
213 | /// The values on the stack |
214 | pub stack: Vec<CoreDumpValue>, |
215 | } |
216 | |
217 | impl<'a> FromReader<'a> for CoreDumpStackFrame { |
218 | fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
219 | let pos = reader.original_position(); |
220 | if reader.read_u8()? != 0 { |
221 | bail!(pos, "invalid start byte for core dump stack frame" ); |
222 | } |
223 | let instanceidx = reader.read_var_u32()?; |
224 | let funcidx = reader.read_var_u32()?; |
225 | let codeoffset = reader.read_var_u32()?; |
226 | let mut locals = vec![]; |
227 | for _ in 0..reader.read_var_u32()? { |
228 | locals.push(CoreDumpValue::from_reader(reader)?); |
229 | } |
230 | let mut stack = vec![]; |
231 | for _ in 0..reader.read_var_u32()? { |
232 | stack.push(CoreDumpValue::from_reader(reader)?); |
233 | } |
234 | |
235 | Ok(CoreDumpStackFrame { |
236 | instanceidx, |
237 | funcidx, |
238 | codeoffset, |
239 | locals, |
240 | stack, |
241 | }) |
242 | } |
243 | } |
244 | |
245 | /// Local and stack values are encoded using one byte for the type (similar to |
246 | /// Wasm's Number Types) followed by bytes representing the actual value |
247 | /// See the tool-conventions repo for more details. |
248 | #[derive (Clone, Debug)] |
249 | pub enum CoreDumpValue { |
250 | /// A missing value (usually missing because it was optimized out) |
251 | Missing, |
252 | /// An i32 value |
253 | I32(i32), |
254 | /// An i64 value |
255 | I64(i64), |
256 | /// An f32 value |
257 | F32(f32), |
258 | /// An f64 value |
259 | F64(f64), |
260 | } |
261 | |
262 | impl<'a> FromReader<'a> for CoreDumpValue { |
263 | fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
264 | let pos: usize = reader.original_position(); |
265 | match reader.read_u8()? { |
266 | 0x01 => Ok(CoreDumpValue::Missing), |
267 | 0x7F => Ok(CoreDumpValue::I32(reader.read_var_i32()?)), |
268 | 0x7E => Ok(CoreDumpValue::I64(reader.read_var_i64()?)), |
269 | 0x7D => Ok(CoreDumpValue::F32(f32::from_bits( |
270 | reader.read_f32()?.bits(), |
271 | ))), |
272 | 0x7C => Ok(CoreDumpValue::F64(f64::from_bits( |
273 | reader.read_f64()?.bits(), |
274 | ))), |
275 | _ => bail!(pos, "invalid CoreDumpValue type" ), |
276 | } |
277 | } |
278 | } |
279 | |