1use std::{
2 borrow::Cow,
3 collections::{BTreeMap, BTreeSet, VecDeque},
4 ops::Range,
5};
6
7use anyhow::{Result, ensure};
8pub use diff::{DiffEntry, DiffResult, Differ, StaticModuleInfo};
9use smallvec::SmallVec;
10
11use crate::{
12 InputModule, analysis,
13 helpers::{RangeComp, RangeExt},
14 index::{DataSegmentId, Id, IdMap, IdVec, InputFuncId, InputGlobalId, SymbolId, TableId},
15};
16mod diff;
17
18#[derive(PartialEq, Eq, Debug, Clone, Copy)]
19pub enum SymbolKind {
20 Func {
21 input_id: InputFuncId,
22 },
23 DataDefined {
24 segment_id: DataSegmentId,
25 offset: usize,
26 length: usize,
27 },
28 Global(InputGlobalId),
29 Table(TableId),
30 Duplicate(SymbolId),
31}
32
33#[derive(PartialEq, Eq, Debug, Clone)]
34pub struct SymbolRecord<'a> {
35 pub name: Cow<'a, str>,
38 pub linking_name: Option<Cow<'a, str>>,
39 pub flags: wasmparser::SymbolFlags,
40 pub relocs: Vec<wasmparser::RelocationEntry>,
44 pub kind: SymbolKind,
45}
46
47impl SymbolRecord<'_> {
48 fn apply_empty_relocs(body: &mut [u8], relocations: &[wasmparser::RelocationEntry]) {
49 for rel in relocations {
50 let reloc_range = rel.relocation_range();
51 body[reloc_range].fill(0);
52 }
53 }
54
55 pub fn stable_name(&self) -> Option<&str> {
56 match self.kind {
57 SymbolKind::Func { .. } => Some(&self.name),
58 _ => None,
59 }
60 }
61
62 pub fn stable_content(&self, input: &analysis::ModuleInfo) -> Option<Vec<u8>> {
64 match self.kind {
65 SymbolKind::Func { input_id } => {
66 let Some(defined_id) = input.as_defined_function_id(input_id) else {
67 return None;
69 };
70
71 let func = &input.wasm.code.defined_funcs[defined_id];
72 let mut body = func.body.as_bytes().to_vec();
73 Self::apply_empty_relocs(&mut body, &self.relocs);
74 Some(body)
75 }
76 SymbolKind::DataDefined {
77 segment_id,
78 offset,
79 length,
80 } => {
81 let segment = &input.wasm.data.data_segments[segment_id];
82 let start = offset;
83 let end = start + length;
84 let mut data = segment.data[start..end].to_vec();
85 Self::apply_empty_relocs(&mut data, &self.relocs);
86 Some(data)
87 }
88 SymbolKind::Global(_) | SymbolKind::Table(_) | SymbolKind::Duplicate(_) => {
89 None
91 }
92 }
93 }
94
95 pub fn childs(&self) -> impl Iterator<Item = SymbolId> + '_ {
96 let filter_non_types = |reloc: &&wasmparser::RelocationEntry| {
97 !matches!(reloc.ty, wasmparser::RelocationType::TypeIndexLeb)
98 };
99
100 let duplicate_iter = match self.kind {
101 SymbolKind::Duplicate(original_id) => Some(original_id),
102 _ => None,
103 };
104 self.relocs
105 .iter()
106 .filter(filter_non_types)
107 .map(|reloc| Id::from_index(reloc.index))
108 .chain(duplicate_iter)
109 }
110}
111
112#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
113struct DataSymbolKey {
114 data_segment: DataSegmentId,
115 offset: usize,
116 symbol_id: SymbolId,
117}
118
119#[derive(Clone, Default, Debug)]
120pub struct SymbolMap<'src> {
121 symbols: IdVec<SymbolRecord<'src>>,
122 funcs_ids: IdMap<InputFuncId, SymbolId>,
123 datas_ids: BTreeSet<DataSymbolKey>,
124}
125
126impl<'src> SymbolMap<'src> {
127 pub fn empty() -> Self {
128 Self {
129 symbols: IdVec::new(),
130 funcs_ids: IdMap::new(),
131 datas_ids: BTreeSet::new(),
132 }
133 }
134 pub fn new(wasm: &'_ crate::read::InputModule<'src>, num_imports_fn: usize) -> Result<Self> {
135 use wasmparser::SymbolInfo;
136
137 #[derive(Debug)]
138 struct SymbolRange {
139 id: SymbolId,
140 range: Range<usize>,
141 }
142 struct SymIm<'src> {
143 flags: wasmparser::SymbolFlags,
144 name: Cow<'src, str>,
145 linking_name: Option<&'src str>,
146 symbol_kind: SymbolKind,
147 }
148
149 let (code_relocs, data_relocs) = Self::collect_ordered_relocs(wasm)?;
150
151 type DupIds = SmallVec<[SymbolId; 4]>;
152 let mut func_ids = IdMap::<InputFuncId, (SymbolRange, DupIds)>::new();
153 let mut data_ids = BTreeMap::<DataSymbolKey, SymbolRange>::new();
155
156 let mut symbols = IdVec::new();
157
158 let data_section_start = wasm.data.starting_offset;
159
160 for (symbol_id, symbol) in wasm.linking.linking_symbols.symbols.iter().enumerate() {
161 let symbol_id = Id::from_index(symbol_id);
162
163 let sym = match *symbol {
164 SymbolInfo::Func { index, flags, name } => {
165 let input_function_id = Id::from_index(index);
166 let fn_name = Self::get_or_create_name(
167 name,
168 wasm.names.functions.get(input_function_id).map(|n| *n),
169 || panic!("function {index} does not have a name"),
170 );
171
172 let fn_range = if input_function_id.as_raw_index() >= num_imports_fn {
173 let defined_index = input_function_id.as_raw_index() - num_imports_fn;
174 let func = wasm
175 .code
176 .defined_funcs
177 .get(Id::from_index(defined_index))
178 .expect("defined function id should be valid");
179 func.body.range().shift_left(wasm.code.starting_offset)
180 } else {
181 0..0
182 };
183
184 func_ids
186 .entry(input_function_id)
187 .or_insert_with(|| {
188 (
189 SymbolRange {
190 id: symbol_id,
191 range: fn_range,
192 },
193 DupIds::new(),
194 )
195 })
196 .1
197 .push(symbol_id);
198
199 SymIm {
200 flags,
201 name: fn_name,
202 linking_name: name,
203 symbol_kind: SymbolKind::Func {
204 input_id: input_function_id,
205 },
206 }
207 }
208 SymbolInfo::Data {
209 flags,
210 name,
211 symbol,
212 } => {
213 let Some(defined) = symbol.as_ref() else {
214 log::warn!(
215 "Skipping undefined data symbol '{}' in linking section",
216 name
217 );
218 continue;
219 };
220
221 let segment_id = Id::from_index(defined.index);
222 let segment = &wasm.data.data_segments[segment_id];
223
224 let segment_data_start = segment.range.end - segment.data.len();
226 let segment_offset = segment_data_start - data_section_start;
228 let segment_end = segment_offset + segment.data.len();
229
230 let start = segment_offset + defined.offset as usize;
231 let end = start + defined.size as usize;
232
233 ensure!(
234 end <= segment_end,
235 "Data symbol '{}' extends beyond segment",
236 name
237 );
238
239 let data_range = start..end;
240 data_ids.insert(
241 DataSymbolKey {
242 data_segment: segment_id,
243 offset: start,
244 symbol_id,
245 },
246 SymbolRange {
247 id: symbol_id,
248 range: data_range,
249 },
250 );
251
252 SymIm {
253 flags,
254 name: name.into(),
255 linking_name: name.into(),
256 symbol_kind: SymbolKind::DataDefined {
257 segment_id,
258 offset: defined.offset as usize,
259 length: defined.size as usize,
260 },
261 }
262 }
263 SymbolInfo::Global { flags, name, index } => {
264 let global_id = Id::from_index(index);
265 let global_name = Self::get_or_create_name(
266 name,
267 wasm.names.globals.get(global_id).map(|n| *n),
268 || format!("global_{index}"),
269 );
270 SymIm {
271 flags,
272 name: global_name,
273 linking_name: name,
274 symbol_kind: SymbolKind::Global(global_id),
275 }
276 }
277 SymbolInfo::Table { index, name, flags } => {
278 let table_id = Id::from_index(index);
279 let table_name = Self::get_or_create_name(
280 name,
281 wasm.names.tables.get(table_id).map(|n| *n),
282 || format!("table_{}", table_id),
283 );
284 SymIm {
285 flags,
286 name: table_name,
287 linking_name: name,
288 symbol_kind: SymbolKind::Table(table_id),
289 }
290 }
291 SymbolInfo::Event { .. } | SymbolInfo::Section { .. } => {
293 continue;
294 }
295 };
296
297 let id = symbols.push(SymbolRecord {
298 name: sym.name,
299 linking_name: sym.linking_name.map(From::from),
300 flags: sym.flags,
301 kind: sym.symbol_kind,
302 relocs: Vec::new(),
303 });
304 debug_assert_eq!(id, symbol_id)
305 }
306
307 fn move_relocs<'a, U: 'a>(
308 symbols: &mut IdVec<SymbolRecord>,
309 iterator: impl IntoIterator<Item = (U, &'a SymbolRange)>,
310 mut relocations: VecDeque<wasmparser::RelocationEntry>,
311 ) {
312 'next_sym: for (_, symbol) in iterator {
313 while let Some(reloc) = relocations.front() {
314 match symbol.range.cmp_range(reloc.relocation_range()) {
315 RangeComp::Equal | RangeComp::Overlap => {}
316 RangeComp::Left => continue 'next_sym,
317 RangeComp::Right | RangeComp::NonComparable | RangeComp::Within => {
318 panic!(
319 "BUG: Relocation entry is not related to symbols: {symbol:?} and {reloc:?}",
320 );
321 }
322 }
323 let mut reloc = relocations.pop_front().unwrap();
325 reloc.offset -= symbol.range.start as u32;
326 symbols[symbol.id].relocs.push(reloc);
327 }
328 }
329 }
330 fn move_dup_symbols(
332 symbols: &mut IdVec<SymbolRecord>,
333 func_ids: &IdMap<InputFuncId, (SymbolRange, DupIds)>,
334 ) {
335 for (_input_id, (sym_range, dup_ids)) in func_ids.iter() {
336 if dup_ids.len() <= 1 {
337 continue;
338 }
339
340 for &dup_id in dup_ids.iter() {
341 if dup_id == sym_range.id {
342 continue;
343 }
344 let dup_sym = &mut symbols[dup_id];
346 let relocs = std::mem::take(&mut dup_sym.relocs);
347 dup_sym.kind = SymbolKind::Duplicate(sym_range.id);
348 symbols[sym_range.id].relocs.extend(relocs.into_iter());
350 }
351 }
352 }
353
354 move_relocs(
358 &mut symbols,
359 func_ids.iter().map(|(k, (v, _))| (k, v)),
360 code_relocs,
361 );
362 move_relocs(&mut symbols, data_ids.iter(), data_relocs);
363 move_dup_symbols(&mut symbols, &func_ids);
364
365 Ok(Self {
366 symbols,
367 funcs_ids: func_ids.into_iter().map(|(k, (v, _))| (k, v.id)).collect(),
368 datas_ids: data_ids.into_iter().map(|(k, _)| k).collect(),
369 })
370 }
371
372 pub fn clone_owned(&self) -> SymbolMap<'static> {
373 let symbols = self
374 .symbols
375 .iter()
376 .map(|(_id, sym)| SymbolRecord {
377 name: sym.name.clone().into_owned().into(),
378 linking_name: sym.linking_name.clone().map(|n| Cow::Owned(n.into_owned())),
379 flags: sym.flags,
380 relocs: sym.relocs.clone(),
381 kind: sym.kind,
382 })
383 .collect();
384 SymbolMap {
385 symbols,
386 funcs_ids: self.funcs_ids.clone(),
387 datas_ids: self.datas_ids.clone(),
388 }
389 }
390
391 pub fn get(&self, id: SymbolId) -> Option<&SymbolRecord<'src>> {
393 self.symbols.get(id).map(|sym| match &sym.kind {
394 SymbolKind::Duplicate(original_id) => &self.symbols[*original_id],
395 _ => sym,
396 })
397 }
398 pub fn get_function_symbol(&self, func_id: InputFuncId) -> Option<SymbolId> {
399 self.funcs_ids.get(func_id).copied()
400 }
401
402 pub fn as_input_function(&self, id: SymbolId) -> Option<InputFuncId> {
412 match &self.symbols.get(id)?.kind {
413 SymbolKind::Func { input_id } => Some(*input_id),
414 _ => None,
415 }
416 }
417 pub fn as_duplicate_mapped(&self, id: SymbolId) -> Option<SymbolId> {
418 match &self.symbols.get(id)?.kind {
419 SymbolKind::Duplicate(original_id) => Some(*original_id),
420 _ => None,
421 }
422 }
423
424 pub fn is_function(&self, id: SymbolId) -> bool {
425 self.as_input_function(id).is_some()
426 }
427 pub fn is_data(&self, id: SymbolId) -> bool {
428 matches!(self.symbols[id].kind, SymbolKind::DataDefined { .. })
429 }
430
431 fn get_or_create_name<'a>(
432 linking_name: Option<&'a str>,
433 name_section_name: Option<&'a str>,
434 create_name: impl Fn() -> String,
435 ) -> Cow<'a, str> {
436 match (linking_name, name_section_name) {
437 (Some(linking), Some(name_section)) => {
438 if linking != name_section {
439 log::trace!(
441 "Conflicting names for symbol: linking section name '{linking}', name section name '{name_section}'"
442 );
443 }
444 name_section.into()
445 }
446 (Some(linking), None) => linking.into(),
447 (None, Some(name_section)) => name_section.into(),
448 (None, None) => create_name().into(),
449 }
450 }
451
452 fn collect_ordered_relocs(
454 input: &'_ InputModule<'src>,
455 ) -> Result<(
456 VecDeque<wasmparser::RelocationEntry>,
457 VecDeque<wasmparser::RelocationEntry>,
458 )> {
459 let mut code = input.relocs.relocs[input.code.section_index]
461 .entries
462 .clone();
463 let mut data = input.relocs.relocs[input.data.section_index]
464 .entries
465 .clone();
466
467 code.sort_by_key(|r| r.offset);
468 data.sort_by_key(|r| r.offset);
469 #[cfg(debug_assertions)]
471 {
472 for pair in code.windows(2) {
473 let first = &pair[0];
474 let second = &pair[1];
475 let first_end = first.relocation_range().end as u32;
476 if first_end > second.offset {
477 panic!(
478 "Overlapping relocations found: first={first:?} (end={first_end}), second={second:?}"
479 );
480 }
481 }
482 for pair in data.windows(2) {
483 let first = &pair[0];
484 let second = &pair[1];
485 let first_end = first.relocation_range().end as u32;
486 if first_end > second.offset {
487 panic!(
488 "Overlapping relocations found: first={first:?} (end={first_end}), second={second:?}"
489 );
490 }
491 }
492 }
493
494 Ok((code.into(), data.into()))
495 }
496
497 pub fn print_debug(&self) {
498 for (id, symbol) in self.symbols.iter() {
499 println!("---{id} <{name}>", name = &symbol.name);
500 println!(" record: {:?}", symbol);
501 for reloc in &symbol.relocs {
502 let id = Id::from_index(reloc.index);
503 println!(
504 "-->{id} <{name}> reloc{:?}",
505 reloc,
506 name = self.symbols[id].name,
507 );
508 }
509 }
510 }
511
512 pub fn iter_data_symbols(
513 &self,
514 ) -> impl Iterator<Item = (DataSegmentId, SymbolId, &SymbolRecord<'src>)> {
515 self.datas_ids.iter().map(|key| {
516 (
517 key.data_segment,
518 key.symbol_id,
519 &self.symbols[key.symbol_id],
520 )
521 })
522 }
523 pub fn iter(&self) -> impl Iterator<Item = (SymbolId, &SymbolRecord<'src>)> {
524 self.symbols.iter()
525 }
526}