1 | //! Support for reading Wasm files. |
2 | //! |
3 | //! [`WasmFile`] implements the [`Object`] trait for Wasm files. |
4 | use alloc::boxed::Box; |
5 | use alloc::vec::Vec; |
6 | use core::marker::PhantomData; |
7 | use core::ops::Range; |
8 | use core::{slice, str}; |
9 | use wasmparser as wp; |
10 | |
11 | use crate::read::{ |
12 | self, Architecture, ComdatKind, CompressedData, CompressedFileRange, Error, Export, FileFlags, |
13 | Import, NoDynamicRelocationIterator, Object, ObjectComdat, ObjectKind, ObjectSection, |
14 | ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Relocation, RelocationMap, |
15 | Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, SymbolFlags, SymbolIndex, |
16 | SymbolKind, SymbolScope, SymbolSection, |
17 | }; |
18 | |
19 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
20 | #[repr (usize)] |
21 | enum SectionId { |
22 | Custom = 0, |
23 | Type = 1, |
24 | Import = 2, |
25 | Function = 3, |
26 | Table = 4, |
27 | Memory = 5, |
28 | Global = 6, |
29 | Export = 7, |
30 | Start = 8, |
31 | Element = 9, |
32 | Code = 10, |
33 | Data = 11, |
34 | DataCount = 12, |
35 | Tag = 13, |
36 | } |
37 | // Update this constant when adding new section id: |
38 | const MAX_SECTION_ID: usize = SectionId::Tag as usize; |
39 | |
40 | /// A WebAssembly object file. |
41 | #[derive (Debug)] |
42 | pub struct WasmFile<'data, R = &'data [u8]> { |
43 | data: &'data [u8], |
44 | has_memory64: bool, |
45 | // All sections, including custom sections. |
46 | sections: Vec<SectionHeader<'data>>, |
47 | // Indices into `sections` of sections with a non-zero id. |
48 | id_sections: Box<[Option<usize>; MAX_SECTION_ID + 1]>, |
49 | // Whether the file has DWARF information. |
50 | has_debug_symbols: bool, |
51 | // Symbols collected from imports, exports, code and name sections. |
52 | symbols: Vec<WasmSymbolInternal<'data>>, |
53 | // Address of the function body for the entry point. |
54 | entry: u64, |
55 | marker: PhantomData<R>, |
56 | } |
57 | |
58 | #[derive (Debug)] |
59 | struct SectionHeader<'data> { |
60 | id: SectionId, |
61 | range: Range<usize>, |
62 | name: &'data str, |
63 | } |
64 | |
65 | #[derive (Clone)] |
66 | enum LocalFunctionKind { |
67 | Unknown, |
68 | Exported { symbol_ids: Vec<u32> }, |
69 | Local { symbol_id: u32 }, |
70 | } |
71 | |
72 | impl<T> ReadError<T> for wasmparser::Result<T> { |
73 | fn read_error(self, error: &'static str) -> Result<T> { |
74 | self.map_err(|_| Error(error)) |
75 | } |
76 | } |
77 | |
78 | impl<'data, R: ReadRef<'data>> WasmFile<'data, R> { |
79 | /// Parse the raw wasm data. |
80 | pub fn parse(data: R) -> Result<Self> { |
81 | let len = data.len().read_error("Unknown Wasm file size" )?; |
82 | let data = data.read_bytes_at(0, len).read_error("Wasm read failed" )?; |
83 | let parser = wp::Parser::new(0).parse_all(data); |
84 | |
85 | let mut file = WasmFile { |
86 | data, |
87 | has_memory64: false, |
88 | sections: Vec::new(), |
89 | id_sections: Default::default(), |
90 | has_debug_symbols: false, |
91 | symbols: Vec::new(), |
92 | entry: 0, |
93 | marker: PhantomData, |
94 | }; |
95 | |
96 | let mut main_file_symbol = Some(WasmSymbolInternal { |
97 | name: "" , |
98 | address: 0, |
99 | size: 0, |
100 | kind: SymbolKind::File, |
101 | section: SymbolSection::None, |
102 | scope: SymbolScope::Compilation, |
103 | }); |
104 | |
105 | let mut imported_funcs_count = 0; |
106 | let mut local_func_kinds = Vec::new(); |
107 | let mut entry_func_id = None; |
108 | let mut code_range_start = 0; |
109 | let mut code_func_index = 0; |
110 | // One-to-one mapping of globals to their value (if the global is a constant integer). |
111 | let mut global_values = Vec::new(); |
112 | |
113 | for payload in parser { |
114 | let payload = payload.read_error("Invalid Wasm section header" )?; |
115 | |
116 | match payload { |
117 | wp::Payload::Version { encoding, .. } => { |
118 | if encoding != wp::Encoding::Module { |
119 | return Err(Error("Unsupported Wasm encoding" )); |
120 | } |
121 | } |
122 | wp::Payload::TypeSection(section) => { |
123 | file.add_section(SectionId::Type, section.range(), "" ); |
124 | } |
125 | wp::Payload::ImportSection(section) => { |
126 | file.add_section(SectionId::Import, section.range(), "" ); |
127 | let mut last_module_name = None; |
128 | |
129 | for import in section { |
130 | let import = import.read_error("Couldn't read an import item" )?; |
131 | let module_name = import.module; |
132 | |
133 | if last_module_name != Some(module_name) { |
134 | file.symbols.push(WasmSymbolInternal { |
135 | name: module_name, |
136 | address: 0, |
137 | size: 0, |
138 | kind: SymbolKind::File, |
139 | section: SymbolSection::None, |
140 | scope: SymbolScope::Dynamic, |
141 | }); |
142 | last_module_name = Some(module_name); |
143 | } |
144 | |
145 | let kind = match import.ty { |
146 | wp::TypeRef::Func(_) => { |
147 | imported_funcs_count += 1; |
148 | SymbolKind::Text |
149 | } |
150 | wp::TypeRef::Memory(memory) => { |
151 | file.has_memory64 |= memory.memory64; |
152 | SymbolKind::Data |
153 | } |
154 | wp::TypeRef::Table(_) | wp::TypeRef::Global(_) => SymbolKind::Data, |
155 | wp::TypeRef::Tag(_) => SymbolKind::Unknown, |
156 | }; |
157 | |
158 | file.symbols.push(WasmSymbolInternal { |
159 | name: import.name, |
160 | address: 0, |
161 | size: 0, |
162 | kind, |
163 | section: SymbolSection::Undefined, |
164 | scope: SymbolScope::Dynamic, |
165 | }); |
166 | } |
167 | } |
168 | wp::Payload::FunctionSection(section) => { |
169 | file.add_section(SectionId::Function, section.range(), "" ); |
170 | local_func_kinds = |
171 | vec![LocalFunctionKind::Unknown; section.into_iter().count()]; |
172 | } |
173 | wp::Payload::TableSection(section) => { |
174 | file.add_section(SectionId::Table, section.range(), "" ); |
175 | } |
176 | wp::Payload::MemorySection(section) => { |
177 | file.add_section(SectionId::Memory, section.range(), "" ); |
178 | for memory in section { |
179 | let memory = memory.read_error("Couldn't read a memory item" )?; |
180 | file.has_memory64 |= memory.memory64; |
181 | } |
182 | } |
183 | wp::Payload::GlobalSection(section) => { |
184 | file.add_section(SectionId::Global, section.range(), "" ); |
185 | for global in section { |
186 | let global = global.read_error("Couldn't read a global item" )?; |
187 | let mut address = None; |
188 | if !global.ty.mutable { |
189 | // There should be exactly one instruction. |
190 | let init = global.init_expr.get_operators_reader().read(); |
191 | address = match init.read_error("Couldn't read a global init expr" )? { |
192 | wp::Operator::I32Const { value } => Some(value as u64), |
193 | wp::Operator::I64Const { value } => Some(value as u64), |
194 | _ => None, |
195 | }; |
196 | } |
197 | global_values.push(address); |
198 | } |
199 | } |
200 | wp::Payload::ExportSection(section) => { |
201 | file.add_section(SectionId::Export, section.range(), "" ); |
202 | if let Some(main_file_symbol) = main_file_symbol.take() { |
203 | file.symbols.push(main_file_symbol); |
204 | } |
205 | |
206 | for export in section { |
207 | let export = export.read_error("Couldn't read an export item" )?; |
208 | |
209 | let (kind, section_idx) = match export.kind { |
210 | wp::ExternalKind::Func => { |
211 | if let Some(local_func_id) = |
212 | export.index.checked_sub(imported_funcs_count) |
213 | { |
214 | let local_func_kind = local_func_kinds |
215 | .get_mut(local_func_id as usize) |
216 | .read_error("Invalid Wasm export index" )?; |
217 | if let LocalFunctionKind::Unknown = local_func_kind { |
218 | *local_func_kind = LocalFunctionKind::Exported { |
219 | symbol_ids: Vec::new(), |
220 | }; |
221 | } |
222 | let symbol_ids = match local_func_kind { |
223 | LocalFunctionKind::Exported { symbol_ids } => symbol_ids, |
224 | _ => unreachable!(), |
225 | }; |
226 | symbol_ids.push(file.symbols.len() as u32); |
227 | } |
228 | (SymbolKind::Text, SectionId::Code) |
229 | } |
230 | wp::ExternalKind::Table |
231 | | wp::ExternalKind::Memory |
232 | | wp::ExternalKind::Global => (SymbolKind::Data, SectionId::Data), |
233 | // TODO |
234 | wp::ExternalKind::Tag => continue, |
235 | }; |
236 | |
237 | // Try to guess the symbol address. Rust and C export a global containing |
238 | // the address in linear memory of the symbol. |
239 | let mut address = 0; |
240 | if export.kind == wp::ExternalKind::Global { |
241 | if let Some(&Some(x)) = global_values.get(export.index as usize) { |
242 | address = x; |
243 | } |
244 | } |
245 | |
246 | file.symbols.push(WasmSymbolInternal { |
247 | name: export.name, |
248 | address, |
249 | size: 0, |
250 | kind, |
251 | section: SymbolSection::Section(SectionIndex(section_idx as usize)), |
252 | scope: SymbolScope::Dynamic, |
253 | }); |
254 | } |
255 | } |
256 | wp::Payload::StartSection { func, range, .. } => { |
257 | file.add_section(SectionId::Start, range, "" ); |
258 | entry_func_id = Some(func); |
259 | } |
260 | wp::Payload::ElementSection(section) => { |
261 | file.add_section(SectionId::Element, section.range(), "" ); |
262 | } |
263 | wp::Payload::CodeSectionStart { range, .. } => { |
264 | code_range_start = range.start; |
265 | file.add_section(SectionId::Code, range, "" ); |
266 | if let Some(main_file_symbol) = main_file_symbol.take() { |
267 | file.symbols.push(main_file_symbol); |
268 | } |
269 | } |
270 | wp::Payload::CodeSectionEntry(body) => { |
271 | let i = code_func_index; |
272 | code_func_index += 1; |
273 | |
274 | let range = body.range(); |
275 | |
276 | let address = range.start as u64 - code_range_start as u64; |
277 | let size = (range.end - range.start) as u64; |
278 | |
279 | if entry_func_id == Some(i as u32) { |
280 | file.entry = address; |
281 | } |
282 | |
283 | let local_func_kind = local_func_kinds |
284 | .get_mut(i) |
285 | .read_error("Invalid Wasm code section index" )?; |
286 | match local_func_kind { |
287 | LocalFunctionKind::Unknown => { |
288 | *local_func_kind = LocalFunctionKind::Local { |
289 | symbol_id: file.symbols.len() as u32, |
290 | }; |
291 | file.symbols.push(WasmSymbolInternal { |
292 | name: "" , |
293 | address, |
294 | size, |
295 | kind: SymbolKind::Text, |
296 | section: SymbolSection::Section(SectionIndex( |
297 | SectionId::Code as usize, |
298 | )), |
299 | scope: SymbolScope::Compilation, |
300 | }); |
301 | } |
302 | LocalFunctionKind::Exported { symbol_ids } => { |
303 | for symbol_id in core::mem::take(symbol_ids) { |
304 | let export_symbol = &mut file.symbols[symbol_id as usize]; |
305 | export_symbol.address = address; |
306 | export_symbol.size = size; |
307 | } |
308 | } |
309 | _ => unreachable!(), |
310 | } |
311 | } |
312 | wp::Payload::DataSection(section) => { |
313 | file.add_section(SectionId::Data, section.range(), "" ); |
314 | } |
315 | wp::Payload::DataCountSection { range, .. } => { |
316 | file.add_section(SectionId::DataCount, range, "" ); |
317 | } |
318 | wp::Payload::TagSection(section) => { |
319 | file.add_section(SectionId::Tag, section.range(), "" ); |
320 | } |
321 | wp::Payload::CustomSection(section) => { |
322 | let name = section.name(); |
323 | let size = section.data().len(); |
324 | let mut range = section.range(); |
325 | range.start = range.end - size; |
326 | file.add_section(SectionId::Custom, range, name); |
327 | if name == "name" { |
328 | let reader = wp::BinaryReader::new(section.data(), section.data_offset()); |
329 | for name in wp::NameSectionReader::new(reader) { |
330 | // TODO: Right now, ill-formed name subsections |
331 | // are silently ignored in order to maintain |
332 | // compatibility with extended name sections, which |
333 | // are not yet supported by the version of |
334 | // `wasmparser` currently used. |
335 | // A better fix would be to update `wasmparser` to |
336 | // the newest version, but this requires |
337 | // a major rewrite of this file. |
338 | if let Ok(wp::Name::Function(name_map)) = name { |
339 | for naming in name_map { |
340 | let naming = |
341 | naming.read_error("Couldn't read a function name" )?; |
342 | if let Some(local_index) = |
343 | naming.index.checked_sub(imported_funcs_count) |
344 | { |
345 | if let LocalFunctionKind::Local { symbol_id } = |
346 | local_func_kinds[local_index as usize] |
347 | { |
348 | file.symbols[symbol_id as usize].name = naming.name; |
349 | } |
350 | } |
351 | } |
352 | } |
353 | } |
354 | } else if name.starts_with(".debug_" ) { |
355 | file.has_debug_symbols = true; |
356 | } |
357 | } |
358 | _ => {} |
359 | } |
360 | } |
361 | |
362 | Ok(file) |
363 | } |
364 | |
365 | fn add_section(&mut self, id: SectionId, range: Range<usize>, name: &'data str) { |
366 | let section = SectionHeader { id, range, name }; |
367 | self.id_sections[id as usize] = Some(self.sections.len()); |
368 | self.sections.push(section); |
369 | } |
370 | } |
371 | |
372 | impl<'data, R> read::private::Sealed for WasmFile<'data, R> {} |
373 | |
374 | impl<'data, R: ReadRef<'data>> Object<'data> for WasmFile<'data, R> { |
375 | type Segment<'file> |
376 | = WasmSegment<'data, 'file, R> |
377 | where |
378 | Self: 'file, |
379 | 'data: 'file; |
380 | type SegmentIterator<'file> |
381 | = WasmSegmentIterator<'data, 'file, R> |
382 | where |
383 | Self: 'file, |
384 | 'data: 'file; |
385 | type Section<'file> |
386 | = WasmSection<'data, 'file, R> |
387 | where |
388 | Self: 'file, |
389 | 'data: 'file; |
390 | type SectionIterator<'file> |
391 | = WasmSectionIterator<'data, 'file, R> |
392 | where |
393 | Self: 'file, |
394 | 'data: 'file; |
395 | type Comdat<'file> |
396 | = WasmComdat<'data, 'file, R> |
397 | where |
398 | Self: 'file, |
399 | 'data: 'file; |
400 | type ComdatIterator<'file> |
401 | = WasmComdatIterator<'data, 'file, R> |
402 | where |
403 | Self: 'file, |
404 | 'data: 'file; |
405 | type Symbol<'file> |
406 | = WasmSymbol<'data, 'file> |
407 | where |
408 | Self: 'file, |
409 | 'data: 'file; |
410 | type SymbolIterator<'file> |
411 | = WasmSymbolIterator<'data, 'file> |
412 | where |
413 | Self: 'file, |
414 | 'data: 'file; |
415 | type SymbolTable<'file> |
416 | = WasmSymbolTable<'data, 'file> |
417 | where |
418 | Self: 'file, |
419 | 'data: 'file; |
420 | type DynamicRelocationIterator<'file> |
421 | = NoDynamicRelocationIterator |
422 | where |
423 | Self: 'file, |
424 | 'data: 'file; |
425 | |
426 | #[inline ] |
427 | fn architecture(&self) -> Architecture { |
428 | if self.has_memory64 { |
429 | Architecture::Wasm64 |
430 | } else { |
431 | Architecture::Wasm32 |
432 | } |
433 | } |
434 | |
435 | #[inline ] |
436 | fn is_little_endian(&self) -> bool { |
437 | true |
438 | } |
439 | |
440 | #[inline ] |
441 | fn is_64(&self) -> bool { |
442 | self.has_memory64 |
443 | } |
444 | |
445 | fn kind(&self) -> ObjectKind { |
446 | // TODO: check for `linking` custom section |
447 | ObjectKind::Unknown |
448 | } |
449 | |
450 | fn segments(&self) -> Self::SegmentIterator<'_> { |
451 | WasmSegmentIterator { file: self } |
452 | } |
453 | |
454 | fn section_by_name_bytes<'file>( |
455 | &'file self, |
456 | section_name: &[u8], |
457 | ) -> Option<WasmSection<'data, 'file, R>> { |
458 | self.sections() |
459 | .find(|section| section.name_bytes() == Ok(section_name)) |
460 | } |
461 | |
462 | fn section_by_index(&self, index: SectionIndex) -> Result<WasmSection<'data, '_, R>> { |
463 | // TODO: Missing sections should return an empty section. |
464 | let id_section = self |
465 | .id_sections |
466 | .get(index.0) |
467 | .and_then(|x| *x) |
468 | .read_error("Invalid Wasm section index" )?; |
469 | let section = self.sections.get(id_section).unwrap(); |
470 | Ok(WasmSection { |
471 | file: self, |
472 | section, |
473 | }) |
474 | } |
475 | |
476 | fn sections(&self) -> Self::SectionIterator<'_> { |
477 | WasmSectionIterator { |
478 | file: self, |
479 | sections: self.sections.iter(), |
480 | } |
481 | } |
482 | |
483 | fn comdats(&self) -> Self::ComdatIterator<'_> { |
484 | WasmComdatIterator { file: self } |
485 | } |
486 | |
487 | #[inline ] |
488 | fn symbol_by_index(&self, index: SymbolIndex) -> Result<WasmSymbol<'data, '_>> { |
489 | let symbol = self |
490 | .symbols |
491 | .get(index.0) |
492 | .read_error("Invalid Wasm symbol index" )?; |
493 | Ok(WasmSymbol { index, symbol }) |
494 | } |
495 | |
496 | fn symbols(&self) -> Self::SymbolIterator<'_> { |
497 | WasmSymbolIterator { |
498 | symbols: self.symbols.iter().enumerate(), |
499 | } |
500 | } |
501 | |
502 | fn symbol_table(&self) -> Option<WasmSymbolTable<'data, '_>> { |
503 | Some(WasmSymbolTable { |
504 | symbols: &self.symbols, |
505 | }) |
506 | } |
507 | |
508 | fn dynamic_symbols(&self) -> Self::SymbolIterator<'_> { |
509 | WasmSymbolIterator { |
510 | symbols: [].iter().enumerate(), |
511 | } |
512 | } |
513 | |
514 | #[inline ] |
515 | fn dynamic_symbol_table(&self) -> Option<WasmSymbolTable<'data, '_>> { |
516 | None |
517 | } |
518 | |
519 | #[inline ] |
520 | fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> { |
521 | None |
522 | } |
523 | |
524 | fn imports(&self) -> Result<Vec<Import<'data>>> { |
525 | // TODO: return entries in the import section |
526 | Ok(Vec::new()) |
527 | } |
528 | |
529 | fn exports(&self) -> Result<Vec<Export<'data>>> { |
530 | // TODO: return entries in the export section |
531 | Ok(Vec::new()) |
532 | } |
533 | |
534 | fn has_debug_symbols(&self) -> bool { |
535 | self.has_debug_symbols |
536 | } |
537 | |
538 | fn relative_address_base(&self) -> u64 { |
539 | 0 |
540 | } |
541 | |
542 | #[inline ] |
543 | fn entry(&self) -> u64 { |
544 | self.entry |
545 | } |
546 | |
547 | #[inline ] |
548 | fn flags(&self) -> FileFlags { |
549 | FileFlags::None |
550 | } |
551 | } |
552 | |
553 | /// An iterator for the segments in a [`WasmFile`]. |
554 | /// |
555 | /// This is a stub that doesn't implement any functionality. |
556 | #[derive (Debug)] |
557 | pub struct WasmSegmentIterator<'data, 'file, R = &'data [u8]> { |
558 | #[allow (unused)] |
559 | file: &'file WasmFile<'data, R>, |
560 | } |
561 | |
562 | impl<'data, 'file, R> Iterator for WasmSegmentIterator<'data, 'file, R> { |
563 | type Item = WasmSegment<'data, 'file, R>; |
564 | |
565 | #[inline ] |
566 | fn next(&mut self) -> Option<Self::Item> { |
567 | None |
568 | } |
569 | } |
570 | |
571 | /// A segment in a [`WasmFile`]. |
572 | /// |
573 | /// This is a stub that doesn't implement any functionality. |
574 | #[derive (Debug)] |
575 | pub struct WasmSegment<'data, 'file, R = &'data [u8]> { |
576 | #[allow (unused)] |
577 | file: &'file WasmFile<'data, R>, |
578 | } |
579 | |
580 | impl<'data, 'file, R> read::private::Sealed for WasmSegment<'data, 'file, R> {} |
581 | |
582 | impl<'data, 'file, R> ObjectSegment<'data> for WasmSegment<'data, 'file, R> { |
583 | #[inline ] |
584 | fn address(&self) -> u64 { |
585 | unreachable!() |
586 | } |
587 | |
588 | #[inline ] |
589 | fn size(&self) -> u64 { |
590 | unreachable!() |
591 | } |
592 | |
593 | #[inline ] |
594 | fn align(&self) -> u64 { |
595 | unreachable!() |
596 | } |
597 | |
598 | #[inline ] |
599 | fn file_range(&self) -> (u64, u64) { |
600 | unreachable!() |
601 | } |
602 | |
603 | fn data(&self) -> Result<&'data [u8]> { |
604 | unreachable!() |
605 | } |
606 | |
607 | fn data_range(&self, _address: u64, _size: u64) -> Result<Option<&'data [u8]>> { |
608 | unreachable!() |
609 | } |
610 | |
611 | #[inline ] |
612 | fn name_bytes(&self) -> Result<Option<&[u8]>> { |
613 | unreachable!() |
614 | } |
615 | |
616 | #[inline ] |
617 | fn name(&self) -> Result<Option<&str>> { |
618 | unreachable!() |
619 | } |
620 | |
621 | #[inline ] |
622 | fn flags(&self) -> SegmentFlags { |
623 | unreachable!() |
624 | } |
625 | } |
626 | |
627 | /// An iterator for the sections in a [`WasmFile`]. |
628 | #[derive (Debug)] |
629 | pub struct WasmSectionIterator<'data, 'file, R = &'data [u8]> { |
630 | file: &'file WasmFile<'data, R>, |
631 | sections: slice::Iter<'file, SectionHeader<'data>>, |
632 | } |
633 | |
634 | impl<'data, 'file, R> Iterator for WasmSectionIterator<'data, 'file, R> { |
635 | type Item = WasmSection<'data, 'file, R>; |
636 | |
637 | fn next(&mut self) -> Option<Self::Item> { |
638 | let section: &'file SectionHeader<'_> = self.sections.next()?; |
639 | Some(WasmSection { |
640 | file: self.file, |
641 | section, |
642 | }) |
643 | } |
644 | } |
645 | |
646 | /// A section in a [`WasmFile`]. |
647 | /// |
648 | /// Most functionality is provided by the [`ObjectSection`] trait implementation. |
649 | #[derive (Debug)] |
650 | pub struct WasmSection<'data, 'file, R = &'data [u8]> { |
651 | file: &'file WasmFile<'data, R>, |
652 | section: &'file SectionHeader<'data>, |
653 | } |
654 | |
655 | impl<'data, 'file, R> read::private::Sealed for WasmSection<'data, 'file, R> {} |
656 | |
657 | impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for WasmSection<'data, 'file, R> { |
658 | type RelocationIterator = WasmRelocationIterator<'data, 'file, R>; |
659 | |
660 | #[inline ] |
661 | fn index(&self) -> SectionIndex { |
662 | // Note that we treat all custom sections as index 0. |
663 | // This is ok because they are never looked up by index. |
664 | SectionIndex(self.section.id as usize) |
665 | } |
666 | |
667 | #[inline ] |
668 | fn address(&self) -> u64 { |
669 | 0 |
670 | } |
671 | |
672 | #[inline ] |
673 | fn size(&self) -> u64 { |
674 | let range = &self.section.range; |
675 | (range.end - range.start) as u64 |
676 | } |
677 | |
678 | #[inline ] |
679 | fn align(&self) -> u64 { |
680 | 1 |
681 | } |
682 | |
683 | #[inline ] |
684 | fn file_range(&self) -> Option<(u64, u64)> { |
685 | let range = &self.section.range; |
686 | Some((range.start as _, range.end as _)) |
687 | } |
688 | |
689 | #[inline ] |
690 | fn data(&self) -> Result<&'data [u8]> { |
691 | let range = &self.section.range; |
692 | self.file |
693 | .data |
694 | .read_bytes_at(range.start as u64, range.end as u64 - range.start as u64) |
695 | .read_error("Invalid Wasm section size or offset" ) |
696 | } |
697 | |
698 | fn data_range(&self, _address: u64, _size: u64) -> Result<Option<&'data [u8]>> { |
699 | unimplemented!() |
700 | } |
701 | |
702 | #[inline ] |
703 | fn compressed_file_range(&self) -> Result<CompressedFileRange> { |
704 | Ok(CompressedFileRange::none(self.file_range())) |
705 | } |
706 | |
707 | #[inline ] |
708 | fn compressed_data(&self) -> Result<CompressedData<'data>> { |
709 | self.data().map(CompressedData::none) |
710 | } |
711 | |
712 | #[inline ] |
713 | fn name_bytes(&self) -> Result<&'data [u8]> { |
714 | self.name().map(str::as_bytes) |
715 | } |
716 | |
717 | #[inline ] |
718 | fn name(&self) -> Result<&'data str> { |
719 | Ok(match self.section.id { |
720 | SectionId::Custom => self.section.name, |
721 | SectionId::Type => "<type>" , |
722 | SectionId::Import => "<import>" , |
723 | SectionId::Function => "<function>" , |
724 | SectionId::Table => "<table>" , |
725 | SectionId::Memory => "<memory>" , |
726 | SectionId::Global => "<global>" , |
727 | SectionId::Export => "<export>" , |
728 | SectionId::Start => "<start>" , |
729 | SectionId::Element => "<element>" , |
730 | SectionId::Code => "<code>" , |
731 | SectionId::Data => "<data>" , |
732 | SectionId::DataCount => "<data_count>" , |
733 | SectionId::Tag => "<tag>" , |
734 | }) |
735 | } |
736 | |
737 | #[inline ] |
738 | fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { |
739 | Ok(None) |
740 | } |
741 | |
742 | #[inline ] |
743 | fn segment_name(&self) -> Result<Option<&str>> { |
744 | Ok(None) |
745 | } |
746 | |
747 | #[inline ] |
748 | fn kind(&self) -> SectionKind { |
749 | match self.section.id { |
750 | SectionId::Custom => match self.section.name { |
751 | "reloc." | "linking" => SectionKind::Linker, |
752 | _ => SectionKind::Other, |
753 | }, |
754 | SectionId::Type => SectionKind::Metadata, |
755 | SectionId::Import => SectionKind::Linker, |
756 | SectionId::Function => SectionKind::Metadata, |
757 | SectionId::Table => SectionKind::UninitializedData, |
758 | SectionId::Memory => SectionKind::UninitializedData, |
759 | SectionId::Global => SectionKind::Data, |
760 | SectionId::Export => SectionKind::Linker, |
761 | SectionId::Start => SectionKind::Linker, |
762 | SectionId::Element => SectionKind::Data, |
763 | SectionId::Code => SectionKind::Text, |
764 | SectionId::Data => SectionKind::Data, |
765 | SectionId::DataCount => SectionKind::UninitializedData, |
766 | SectionId::Tag => SectionKind::Data, |
767 | } |
768 | } |
769 | |
770 | #[inline ] |
771 | fn relocations(&self) -> WasmRelocationIterator<'data, 'file, R> { |
772 | WasmRelocationIterator(PhantomData) |
773 | } |
774 | |
775 | fn relocation_map(&self) -> read::Result<RelocationMap> { |
776 | RelocationMap::new(self.file, self) |
777 | } |
778 | |
779 | #[inline ] |
780 | fn flags(&self) -> SectionFlags { |
781 | SectionFlags::None |
782 | } |
783 | } |
784 | |
785 | /// An iterator for the COMDAT section groups in a [`WasmFile`]. |
786 | /// |
787 | /// This is a stub that doesn't implement any functionality. |
788 | #[derive (Debug)] |
789 | pub struct WasmComdatIterator<'data, 'file, R = &'data [u8]> { |
790 | #[allow (unused)] |
791 | file: &'file WasmFile<'data, R>, |
792 | } |
793 | |
794 | impl<'data, 'file, R> Iterator for WasmComdatIterator<'data, 'file, R> { |
795 | type Item = WasmComdat<'data, 'file, R>; |
796 | |
797 | #[inline ] |
798 | fn next(&mut self) -> Option<Self::Item> { |
799 | None |
800 | } |
801 | } |
802 | |
803 | /// A COMDAT section group in a [`WasmFile`]. |
804 | /// |
805 | /// This is a stub that doesn't implement any functionality. |
806 | #[derive (Debug)] |
807 | pub struct WasmComdat<'data, 'file, R = &'data [u8]> { |
808 | #[allow (unused)] |
809 | file: &'file WasmFile<'data, R>, |
810 | } |
811 | |
812 | impl<'data, 'file, R> read::private::Sealed for WasmComdat<'data, 'file, R> {} |
813 | |
814 | impl<'data, 'file, R> ObjectComdat<'data> for WasmComdat<'data, 'file, R> { |
815 | type SectionIterator = WasmComdatSectionIterator<'data, 'file, R>; |
816 | |
817 | #[inline ] |
818 | fn kind(&self) -> ComdatKind { |
819 | unreachable!(); |
820 | } |
821 | |
822 | #[inline ] |
823 | fn symbol(&self) -> SymbolIndex { |
824 | unreachable!(); |
825 | } |
826 | |
827 | #[inline ] |
828 | fn name_bytes(&self) -> Result<&'data [u8]> { |
829 | unreachable!(); |
830 | } |
831 | |
832 | #[inline ] |
833 | fn name(&self) -> Result<&'data str> { |
834 | unreachable!(); |
835 | } |
836 | |
837 | #[inline ] |
838 | fn sections(&self) -> Self::SectionIterator { |
839 | unreachable!(); |
840 | } |
841 | } |
842 | |
843 | /// An iterator for the sections in a COMDAT section group in a [`WasmFile`]. |
844 | /// |
845 | /// This is a stub that doesn't implement any functionality. |
846 | #[derive (Debug)] |
847 | pub struct WasmComdatSectionIterator<'data, 'file, R = &'data [u8]> { |
848 | #[allow (unused)] |
849 | file: &'file WasmFile<'data, R>, |
850 | } |
851 | |
852 | impl<'data, 'file, R> Iterator for WasmComdatSectionIterator<'data, 'file, R> { |
853 | type Item = SectionIndex; |
854 | |
855 | fn next(&mut self) -> Option<Self::Item> { |
856 | None |
857 | } |
858 | } |
859 | |
860 | /// A symbol table in a [`WasmFile`]. |
861 | #[derive (Debug)] |
862 | pub struct WasmSymbolTable<'data, 'file> { |
863 | symbols: &'file [WasmSymbolInternal<'data>], |
864 | } |
865 | |
866 | impl<'data, 'file> read::private::Sealed for WasmSymbolTable<'data, 'file> {} |
867 | |
868 | impl<'data, 'file> ObjectSymbolTable<'data> for WasmSymbolTable<'data, 'file> { |
869 | type Symbol = WasmSymbol<'data, 'file>; |
870 | type SymbolIterator = WasmSymbolIterator<'data, 'file>; |
871 | |
872 | fn symbols(&self) -> Self::SymbolIterator { |
873 | WasmSymbolIterator { |
874 | symbols: self.symbols.iter().enumerate(), |
875 | } |
876 | } |
877 | |
878 | fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> { |
879 | let symbol: &WasmSymbolInternal<'_> = self |
880 | .symbols |
881 | .get(index.0) |
882 | .read_error("Invalid Wasm symbol index" )?; |
883 | Ok(WasmSymbol { index, symbol }) |
884 | } |
885 | } |
886 | |
887 | /// An iterator for the symbols in a [`WasmFile`]. |
888 | #[derive (Debug)] |
889 | pub struct WasmSymbolIterator<'data, 'file> { |
890 | symbols: core::iter::Enumerate<slice::Iter<'file, WasmSymbolInternal<'data>>>, |
891 | } |
892 | |
893 | impl<'data, 'file> Iterator for WasmSymbolIterator<'data, 'file> { |
894 | type Item = WasmSymbol<'data, 'file>; |
895 | |
896 | fn next(&mut self) -> Option<Self::Item> { |
897 | let (index: usize, symbol: &'file WasmSymbolInternal<'_>) = self.symbols.next()?; |
898 | Some(WasmSymbol { |
899 | index: SymbolIndex(index), |
900 | symbol, |
901 | }) |
902 | } |
903 | } |
904 | |
905 | /// A symbol in a [`WasmFile`]. |
906 | /// |
907 | /// Most functionality is provided by the [`ObjectSymbol`] trait implementation. |
908 | #[derive (Clone, Copy, Debug)] |
909 | pub struct WasmSymbol<'data, 'file> { |
910 | index: SymbolIndex, |
911 | symbol: &'file WasmSymbolInternal<'data>, |
912 | } |
913 | |
914 | #[derive (Clone, Debug)] |
915 | struct WasmSymbolInternal<'data> { |
916 | name: &'data str, |
917 | address: u64, |
918 | size: u64, |
919 | kind: SymbolKind, |
920 | section: SymbolSection, |
921 | scope: SymbolScope, |
922 | } |
923 | |
924 | impl<'data, 'file> read::private::Sealed for WasmSymbol<'data, 'file> {} |
925 | |
926 | impl<'data, 'file> ObjectSymbol<'data> for WasmSymbol<'data, 'file> { |
927 | #[inline ] |
928 | fn index(&self) -> SymbolIndex { |
929 | self.index |
930 | } |
931 | |
932 | #[inline ] |
933 | fn name_bytes(&self) -> read::Result<&'data [u8]> { |
934 | Ok(self.symbol.name.as_bytes()) |
935 | } |
936 | |
937 | #[inline ] |
938 | fn name(&self) -> read::Result<&'data str> { |
939 | Ok(self.symbol.name) |
940 | } |
941 | |
942 | #[inline ] |
943 | fn address(&self) -> u64 { |
944 | self.symbol.address |
945 | } |
946 | |
947 | #[inline ] |
948 | fn size(&self) -> u64 { |
949 | self.symbol.size |
950 | } |
951 | |
952 | #[inline ] |
953 | fn kind(&self) -> SymbolKind { |
954 | self.symbol.kind |
955 | } |
956 | |
957 | #[inline ] |
958 | fn section(&self) -> SymbolSection { |
959 | self.symbol.section |
960 | } |
961 | |
962 | #[inline ] |
963 | fn is_undefined(&self) -> bool { |
964 | self.symbol.section == SymbolSection::Undefined |
965 | } |
966 | |
967 | #[inline ] |
968 | fn is_definition(&self) -> bool { |
969 | (self.symbol.kind == SymbolKind::Text || self.symbol.kind == SymbolKind::Data) |
970 | && self.symbol.section != SymbolSection::Undefined |
971 | } |
972 | |
973 | #[inline ] |
974 | fn is_common(&self) -> bool { |
975 | self.symbol.section == SymbolSection::Common |
976 | } |
977 | |
978 | #[inline ] |
979 | fn is_weak(&self) -> bool { |
980 | false |
981 | } |
982 | |
983 | #[inline ] |
984 | fn scope(&self) -> SymbolScope { |
985 | self.symbol.scope |
986 | } |
987 | |
988 | #[inline ] |
989 | fn is_global(&self) -> bool { |
990 | self.symbol.scope != SymbolScope::Compilation |
991 | } |
992 | |
993 | #[inline ] |
994 | fn is_local(&self) -> bool { |
995 | self.symbol.scope == SymbolScope::Compilation |
996 | } |
997 | |
998 | #[inline ] |
999 | fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> { |
1000 | SymbolFlags::None |
1001 | } |
1002 | } |
1003 | |
1004 | /// An iterator for the relocations for a [`WasmSection`]. |
1005 | /// |
1006 | /// This is a stub that doesn't implement any functionality. |
1007 | #[derive (Debug)] |
1008 | pub struct WasmRelocationIterator<'data, 'file, R = &'data [u8]>( |
1009 | PhantomData<(&'data (), &'file (), R)>, |
1010 | ); |
1011 | |
1012 | impl<'data, 'file, R> Iterator for WasmRelocationIterator<'data, 'file, R> { |
1013 | type Item = (u64, Relocation); |
1014 | |
1015 | #[inline ] |
1016 | fn next(&mut self) -> Option<Self::Item> { |
1017 | None |
1018 | } |
1019 | } |
1020 | |