From 5b53725e359e9f5e8d269950e42f8d792b2c58cd Mon Sep 17 00:00:00 2001 From: lapla Date: Mon, 11 May 2026 12:14:57 +0900 Subject: [PATCH] read/wasm: Implement `reloc.*` section reading (#895) --- crates/examples/testfiles/wasm/base.o.objdump | 8 ++ src/common.rs | 6 ++ src/read/wasm.rs | 88 +++++++++++++++++-- 3 files changed, 93 insertions(+), 9 deletions(-) diff --git a/crates/examples/testfiles/wasm/base.o.objdump b/crates/examples/testfiles/wasm/base.o.objdump index a300b0b..999c0c5 100644 --- a/crates/examples/testfiles/wasm/base.o.objdump +++ b/crates/examples/testfiles/wasm/base.o.objdump @@ -22,6 +22,14 @@ Symbols 4: Symbol { name: "main", address: 5e, size: f, kind: Text, section: Section(SectionIndex(a)), scope: Linkage, weak: false, flags: None } 5: Symbol { name: "__main_void", address: 2, size: 5b, kind: Text, section: Section(SectionIndex(a)), scope: Dynamic, weak: false, flags: None } + relocations +(6, Relocation { kind: Unknown, encoding: Generic, size: 28, target: Symbol(SymbolIndex(1)), addend: 0, implicit_addend: false, flags: Wasm { r_type: 7 } }) +(1b, Relocation { kind: Unknown, encoding: Generic, size: 28, target: Symbol(SymbolIndex(1)), addend: 0, implicit_addend: false, flags: Wasm { r_type: 7 } }) +(2c, Relocation { kind: Unknown, encoding: Generic, size: 28, target: Symbol(SymbolIndex(2)), addend: 0, implicit_addend: false, flags: Wasm { r_type: 4 } }) +(3c, Relocation { kind: Unknown, encoding: Generic, size: 28, target: Symbol(SymbolIndex(3)), addend: 0, implicit_addend: false, flags: Wasm { r_type: 0 } }) +(54, Relocation { kind: Unknown, encoding: Generic, size: 28, target: Symbol(SymbolIndex(1)), addend: 0, implicit_addend: false, flags: Wasm { r_type: 7 } }) +(62, Relocation { kind: Unknown, encoding: Generic, size: 28, target: Symbol(SymbolIndex(0)), addend: 0, implicit_addend: false, flags: Wasm { r_type: 0 } }) + Dynamic symbols Symbol map diff --git a/src/common.rs b/src/common.rs index d6270ca..ef9a050 100644 --- a/src/common.rs +++ b/src/common.rs @@ -764,6 +764,12 @@ pub enum RelocationFlags { /// `r_rsize` field in the XCOFF relocation. r_rsize: u8, }, + /// Wasm relocation fields. + #[cfg(feature = "wasm")] + Wasm { + /// Relocation type (the `R_WASM_*` constant). + r_type: u8, + }, } /// Wrapper to print as `[..]` without a manual `Debug` implementation, rather than dumping an diff --git a/src/read/wasm.rs b/src/read/wasm.rs index 31fd18e..f56f1e4 100644 --- a/src/read/wasm.rs +++ b/src/read/wasm.rs @@ -16,6 +16,7 @@ use crate::read::{ RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, SymbolFlags, SymbolIndex, SymbolKind, SymbolScope, SymbolSection, }; +use crate::{RelocationEncoding, RelocationFlags, RelocationKind, RelocationTarget}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(usize)] @@ -47,6 +48,8 @@ pub struct WasmFile<'data, R = &'data [u8]> { sections: Vec>, // Indices into `sections` of sections with a non-zero id. id_sections: Box<[Option; MAX_SECTION_ID + 1]>, + // Parsed `reloc.*` custom sections, keyed by the binary index of the target section. + relocations: Vec, // Whether the file has DWARF information. has_debug_symbols: bool, // Symbols collected from imports, exports, code and name sections. @@ -56,11 +59,18 @@ pub struct WasmFile<'data, R = &'data [u8]> { marker: PhantomData, } +#[derive(Debug)] +struct RelocSection { + target: u32, + entries: Vec, +} + #[derive(Debug)] struct SectionHeader<'data> { id: SectionId, range: Range, name: &'data str, + binary_index: u32, } #[derive(Clone)] @@ -87,6 +97,7 @@ impl<'data, R: ReadRef<'data>> WasmFile<'data, R> { has_memory64: false, sections: Vec::new(), id_sections: Default::default(), + relocations: Vec::new(), has_debug_symbols: false, symbols: Vec::new(), entry: 0, @@ -213,6 +224,18 @@ impl<'data, R: ReadRef<'data>> WasmFile<'data, R> { symbols = Some(s); } } + } else if name.strip_prefix("reloc.").is_some() { + // https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md#relocation-sections + let reader = wp::BinaryReader::new(section.data(), section.data_offset()); + let reloc = wp::RelocSectionReader::new(reader) + .read_error("Invalid Wasm reloc section")?; + let target = reloc.section_index(); + let mut entries = Vec::new(); + for entry in reloc.entries() { + let entry = entry.read_error("Invalid Wasm reloc entry")?; + entries.push(entry); + } + file.relocations.push(RelocSection { target, entries }); } else if name.starts_with(".debug_") { file.has_debug_symbols = true; } @@ -509,7 +532,13 @@ impl<'data, R: ReadRef<'data>> WasmFile<'data, R> { } fn add_section(&mut self, id: SectionId, range: Range, name: &'data str) { - let section = SectionHeader { id, range, name }; + let binary_index = self.sections.len() as u32; + let section = SectionHeader { + id, + range, + name, + binary_index, + }; self.id_sections[id as usize] = Some(self.sections.len()); self.sections.push(section); } @@ -920,7 +949,12 @@ impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for WasmSection<'data #[inline] fn relocations(&self) -> WasmRelocationIterator<'data, 'file, R> { - WasmRelocationIterator(PhantomData) + WasmRelocationIterator { + target: self.section.binary_index, + sections: self.file.relocations.iter(), + entries: [].iter(), + marker: PhantomData, + } } fn relocation_map(&self) -> read::Result { @@ -1154,18 +1188,54 @@ impl<'data, 'file> ObjectSymbol<'data> for WasmSymbol<'data, 'file> { } /// An iterator for the relocations for a [`WasmSection`]. -/// -/// This is a stub that doesn't implement any functionality. #[derive(Debug)] -pub struct WasmRelocationIterator<'data, 'file, R = &'data [u8]>( - PhantomData<(&'data (), &'file (), R)>, -); +pub struct WasmRelocationIterator<'data, 'file, R = &'data [u8]> { + /// Binary index of the wasm section we are iterating relocations for. + target: u32, + /// Remaining `reloc.*` sections that may target this section. + sections: slice::Iter<'file, RelocSection>, + /// Remaining entries from the current matching `reloc.*` section. + entries: slice::Iter<'file, wp::RelocationEntry>, + marker: PhantomData<(&'data (), R)>, +} impl<'data, 'file, R> Iterator for WasmRelocationIterator<'data, 'file, R> { type Item = (u64, Relocation); - #[inline] fn next(&mut self) -> Option { - None + let entry = loop { + if let Some(entry) = self.entries.next() { + break *entry; + } + let next = self.sections.find(|r| r.target == self.target)?; + self.entries = next.entries.iter(); + }; + let r_type = entry.ty as u8; + // Number of bits the relocation patches in the target section. + let size = (entry.ty.extent() * 8) as u8; + // For `R_WASM_TYPE_INDEX_LEB`, the `index` field refers to the type section, not the symbol table. + let (target, addend) = if entry.ty == wp::RelocationType::TypeIndexLeb { + ( + RelocationTarget::Section(SectionIndex(SectionId::Type as usize)), + entry.index as i64, + ) + } else { + ( + RelocationTarget::Symbol(SymbolIndex(entry.index as usize)), + entry.addend, + ) + }; + let relocation = Relocation { + kind: RelocationKind::Unknown, + encoding: RelocationEncoding::Generic, + size, + target, + subtractor: None, + // Wasm relocation entries always carry an explicit addend. + implicit_addend: false, + addend, + flags: RelocationFlags::Wasm { r_type }, + }; + Some((entry.offset as u64, relocation)) } }