symbolic_debuginfo/
wasm.rs1use std::borrow::Cow;
3use std::fmt;
4
5use thiserror::Error;
6
7use symbolic_common::{Arch, AsSelf, CodeId, DebugId, Uuid};
8
9use crate::base::*;
10use crate::dwarf::{Dwarf, DwarfDebugSession, DwarfError, DwarfSection, Endian};
11
12mod parser;
13
14#[derive(Debug, Error)]
16#[non_exhaustive]
17pub enum WasmError {
18 #[error("invalid wasm file")]
20 Read(#[from] wasmparser::BinaryReaderError),
21 #[error("function references unknown type")]
23 UnknownFunctionType,
24}
25
26pub struct WasmObject<'data> {
31 dwarf_sections: Vec<(&'data str, &'data [u8])>,
32 funcs: Vec<Symbol<'data>>,
33 build_id: Option<&'data [u8]>,
34 data: &'data [u8],
35 code_offset: u64,
36 kind: ObjectKind,
37}
38
39impl<'data> WasmObject<'data> {
40 pub fn test(data: &[u8]) -> bool {
42 data.starts_with(b"\x00asm")
43 }
44
45 pub fn file_format(&self) -> FileFormat {
47 FileFormat::Wasm
48 }
49
50 #[inline]
54 pub fn code_id(&self) -> Option<CodeId> {
55 self.build_id.map(CodeId::from_binary)
56 }
57
58 #[inline]
62 pub fn debug_id(&self) -> DebugId {
63 self.build_id
64 .and_then(|data| {
65 data.get(..16)
66 .and_then(|first_16| Uuid::from_slice(first_16).ok())
67 })
68 .map(DebugId::from_uuid)
69 .unwrap_or_else(DebugId::nil)
70 }
71
72 pub fn arch(&self) -> Arch {
74 Arch::Wasm32
76 }
77
78 #[inline]
80 pub fn kind(&self) -> ObjectKind {
81 self.kind
82 }
83
84 pub fn load_address(&self) -> u64 {
88 0
89 }
90
91 pub fn has_symbols(&self) -> bool {
93 true
94 }
95
96 pub fn symbols(&self) -> WasmSymbolIterator<'data, '_> {
98 WasmSymbolIterator {
99 funcs: self.funcs.clone().into_iter(),
100 _marker: std::marker::PhantomData,
101 }
102 }
103
104 pub fn symbol_map(&self) -> SymbolMap<'data> {
106 self.symbols().collect()
107 }
108
109 #[inline]
111 pub fn has_debug_info(&self) -> bool {
112 self.dwarf_sections
113 .iter()
114 .any(|(name, _)| *name == ".debug_info")
115 }
116
117 pub fn debug_session(&self) -> Result<DwarfDebugSession<'data>, DwarfError> {
119 let symbols = self.symbol_map();
120 DwarfDebugSession::parse(self, symbols, -(self.code_offset() as i64), self.kind())
122 }
123
124 #[inline]
126 pub fn has_unwind_info(&self) -> bool {
127 self.dwarf_sections
128 .iter()
129 .any(|(name, _)| *name == ".debug_frame")
130 }
131
132 pub fn has_sources(&self) -> bool {
134 false
135 }
136
137 pub fn is_malformed(&self) -> bool {
139 false
140 }
141
142 pub fn data(&self) -> &'data [u8] {
144 self.data
145 }
146
147 pub fn code_offset(&self) -> u64 {
149 self.code_offset
150 }
151}
152
153impl fmt::Debug for WasmObject<'_> {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 f.debug_struct("WasmObject")
156 .field("code_id", &self.code_id())
157 .field("debug_id", &self.debug_id())
158 .field("arch", &self.arch())
159 .field("kind", &self.kind())
160 .field("load_address", &format_args!("{:#x}", self.load_address()))
161 .field("has_symbols", &self.has_symbols())
162 .field("has_debug_info", &self.has_debug_info())
163 .field("has_unwind_info", &self.has_unwind_info())
164 .field("is_malformed", &self.is_malformed())
165 .finish()
166 }
167}
168
169impl<'slf, 'd: 'slf> AsSelf<'slf> for WasmObject<'d> {
170 type Ref = WasmObject<'slf>;
171
172 fn as_self(&'slf self) -> &'slf Self::Ref {
173 self
174 }
175}
176
177impl<'d> Parse<'d> for WasmObject<'d> {
178 type Error = WasmError;
179
180 fn test(data: &[u8]) -> bool {
181 Self::test(data)
182 }
183
184 fn parse(data: &'d [u8]) -> Result<Self, WasmError> {
185 Self::parse(data)
186 }
187}
188
189impl<'data: 'object, 'object> ObjectLike<'data, 'object> for WasmObject<'data> {
190 type Error = DwarfError;
191 type Session = DwarfDebugSession<'data>;
192 type SymbolIterator = WasmSymbolIterator<'data, 'object>;
193
194 fn file_format(&self) -> FileFormat {
195 self.file_format()
196 }
197
198 fn code_id(&self) -> Option<CodeId> {
199 self.code_id()
200 }
201
202 fn debug_id(&self) -> DebugId {
203 self.debug_id()
204 }
205
206 fn arch(&self) -> Arch {
207 self.arch()
208 }
209
210 fn kind(&self) -> ObjectKind {
211 self.kind()
212 }
213
214 fn load_address(&self) -> u64 {
215 self.load_address()
216 }
217
218 fn has_symbols(&self) -> bool {
219 self.has_symbols()
220 }
221
222 fn symbols(&'object self) -> Self::SymbolIterator {
223 self.symbols()
224 }
225
226 fn symbol_map(&self) -> SymbolMap<'data> {
227 self.symbol_map()
228 }
229
230 fn has_debug_info(&self) -> bool {
231 self.has_debug_info()
232 }
233
234 fn debug_session(&self) -> Result<Self::Session, Self::Error> {
235 self.debug_session()
236 }
237
238 fn has_unwind_info(&self) -> bool {
239 self.has_unwind_info()
240 }
241
242 fn has_sources(&self) -> bool {
243 self.has_sources()
244 }
245
246 fn is_malformed(&self) -> bool {
247 self.is_malformed()
248 }
249}
250
251impl<'data> Dwarf<'data> for WasmObject<'data> {
252 fn endianity(&self) -> Endian {
253 Endian::Little
254 }
255
256 fn raw_section(&self, section_name: &str) -> Option<DwarfSection<'data>> {
257 self.dwarf_sections.iter().find_map(|(name, data)| {
258 if name.strip_prefix('.') == Some(section_name) {
259 Some(DwarfSection {
260 data: Cow::Borrowed(data),
261 address: 0,
263 offset: 0,
264 align: 4,
265 })
266 } else {
267 None
268 }
269 })
270 }
271}
272
273pub struct WasmSymbolIterator<'data, 'object> {
277 funcs: std::vec::IntoIter<Symbol<'data>>,
278 _marker: std::marker::PhantomData<&'object u8>,
279}
280
281impl<'data> Iterator for WasmSymbolIterator<'data, '_> {
282 type Item = Symbol<'data>;
283
284 fn next(&mut self) -> Option<Self::Item> {
285 self.funcs.next()
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use super::*;
292
293 #[test]
294 fn test_invalid_header() {
295 let data = b"\x00asm ";
296
297 assert!(WasmObject::parse(data).is_err());
298 }
299}