WASM API updates, support symbol mapping

This commit is contained in:
Luke Street 2025-05-27 21:31:29 -06:00
parent d0afd3b83e
commit a57e5db983
2 changed files with 199 additions and 123 deletions

View File

@ -24,14 +24,14 @@ wit_bindgen::generate!({
use exports::objdiff::core::{ use exports::objdiff::core::{
diff::{ diff::{
DiffConfigBorrow, DiffResult, Guest as GuestDiff, GuestDiffConfig, GuestObject, DiffConfigBorrow, DiffResult, Guest as GuestDiff, GuestDiffConfig, GuestObject,
GuestObjectDiff, Object, ObjectBorrow, ObjectDiff, ObjectDiffBorrow, GuestObjectDiff, MappingConfig, Object, ObjectBorrow, ObjectDiff, ObjectDiffBorrow,
SymbolFlags, SymbolInfo, SymbolKind, SymbolRef,
}, },
display::{ display::{
ContextItem, ContextItemCopy, ContextItemNavigate, DiffText, DiffTextColor, DiffTextOpcode, ContextItem, ContextItemCopy, ContextItemNavigate, DiffText, DiffTextColor, DiffTextOpcode,
DiffTextSegment, DiffTextSymbol, DisplayConfig, Guest as GuestDisplay, HoverItem, DiffTextSegment, DiffTextSymbol, DisplayConfig, Guest as GuestDisplay, HoverItem,
HoverItemColor, HoverItemText, InstructionDiffKind, InstructionDiffRow, SectionDisplay, HoverItemColor, HoverItemText, InstructionDiffKind, InstructionDiffRow, SectionDisplay,
SectionDisplaySymbol, SymbolDisplay, SymbolFilter, SymbolFlags, SymbolKind, SymbolDisplay, SymbolFilter, SymbolNavigationKind,
SymbolNavigationKind, SymbolRef,
}, },
}; };
@ -59,15 +59,17 @@ impl GuestDiff for Component {
left: Option<ObjectBorrow>, left: Option<ObjectBorrow>,
right: Option<ObjectBorrow>, right: Option<ObjectBorrow>,
diff_config: DiffConfigBorrow, diff_config: DiffConfigBorrow,
mapping_config: MappingConfig,
) -> Result<DiffResult, String> { ) -> Result<DiffResult, String> {
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow(); let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
let mapping_config = diff::MappingConfig::from(mapping_config);
log::debug!("Running diff with config: {:?}", diff_config); log::debug!("Running diff with config: {:?}", diff_config);
let result = diff::diff_objs( let result = diff::diff_objs(
left.as_ref().map(|o| o.get::<ResourceObject>().0.as_ref()), left.as_ref().map(|o| o.get::<ResourceObject>().0.as_ref()),
right.as_ref().map(|o| o.get::<ResourceObject>().0.as_ref()), right.as_ref().map(|o| o.get::<ResourceObject>().0.as_ref()),
None, None,
&diff_config, &diff_config,
&diff::MappingConfig::default(), &mapping_config,
) )
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
Ok(DiffResult { Ok(DiffResult {
@ -134,48 +136,47 @@ impl GuestDisplay for Component {
name: d.name, name: d.name,
size: d.size, size: d.size,
match_percent: d.match_percent, match_percent: d.match_percent,
symbols: d symbols: d.symbols.into_iter().map(to_symbol_ref).collect(),
.symbols
.into_iter()
.map(|s| SectionDisplaySymbol {
symbol: s.symbol as SymbolRef,
is_mapping_symbol: s.is_mapping_symbol,
})
.collect(),
}) })
.collect() .collect()
} }
fn display_symbol( fn display_symbol(diff: ObjectDiffBorrow, symbol_ref: SymbolRef) -> SymbolDisplay {
diff: ObjectDiffBorrow,
symbol_display: SectionDisplaySymbol,
) -> SymbolDisplay {
let obj_diff = diff.get::<ResourceObjectDiff>(); let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref(); let obj = obj_diff.0.as_ref();
let obj_diff = &obj_diff.1; let obj_diff = &obj_diff.1;
let symbol_idx = symbol_display.symbol as usize; let symbol_display = from_symbol_ref(symbol_ref);
let Some(symbol) = obj.symbols.get(symbol_idx) else { let Some(symbol) = obj.symbols.get(symbol_display.symbol) else {
return SymbolDisplay { name: "<unknown>".to_string(), ..Default::default() }; return SymbolDisplay {
info: SymbolInfo { name: "<unknown>".to_string(), ..Default::default() },
..Default::default()
};
}; };
let symbol_diff = if symbol_display.is_mapping_symbol { let symbol_diff = if symbol_display.is_mapping_symbol {
obj_diff obj_diff
.mapping_symbols .mapping_symbols
.iter() .iter()
.find(|s| s.symbol_index == symbol_idx) .find(|s| s.symbol_index == symbol_display.symbol)
.map(|s| &s.symbol_diff) .map(|s| &s.symbol_diff)
} else { } else {
obj_diff.symbols.get(symbol_idx) obj_diff.symbols.get(symbol_display.symbol)
}; };
SymbolDisplay { SymbolDisplay {
name: symbol.name.clone(), info: SymbolInfo {
demangled_name: symbol.demangled_name.clone(), id: to_symbol_ref(symbol_display),
address: symbol.address, name: symbol.name.clone(),
size: symbol.size, demangled_name: symbol.demangled_name.clone(),
kind: SymbolKind::from(symbol.kind), address: symbol.address,
section: symbol.section.map(|s| s as u32), size: symbol.size,
flags: SymbolFlags::from(symbol.flags), kind: SymbolKind::from(symbol.kind),
align: symbol.align.map(|a| a.get()), section: symbol.section.map(|s| s as u32),
virtual_address: symbol.virtual_address, section_name: symbol
.section
.and_then(|s| obj.sections.get(s).map(|sec| sec.name.clone())),
flags: SymbolFlags::from(symbol.flags),
align: symbol.align.map(|a| a.get()),
virtual_address: symbol.virtual_address,
},
target_symbol: symbol_diff.and_then(|sd| sd.target_symbol.map(|s| s as u32)), target_symbol: symbol_diff.and_then(|sd| sd.target_symbol.map(|s| s as u32)),
match_percent: symbol_diff.and_then(|sd| sd.match_percent), match_percent: symbol_diff.and_then(|sd| sd.match_percent),
diff_score: symbol_diff.and_then(|sd| sd.diff_score), diff_score: symbol_diff.and_then(|sd| sd.diff_score),
@ -185,22 +186,22 @@ impl GuestDisplay for Component {
fn display_instruction_row( fn display_instruction_row(
diff: ObjectDiffBorrow, diff: ObjectDiffBorrow,
symbol_display: SectionDisplaySymbol, symbol_ref: SymbolRef,
row_index: u32, row_index: u32,
diff_config: DiffConfigBorrow, diff_config: DiffConfigBorrow,
) -> InstructionDiffRow { ) -> InstructionDiffRow {
let obj_diff = diff.get::<ResourceObjectDiff>(); let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref(); let obj = obj_diff.0.as_ref();
let obj_diff = &obj_diff.1; let obj_diff = &obj_diff.1;
let symbol_idx = symbol_display.symbol as usize; let symbol_display = from_symbol_ref(symbol_ref);
let symbol_diff = if symbol_display.is_mapping_symbol { let symbol_diff = if symbol_display.is_mapping_symbol {
obj_diff obj_diff
.mapping_symbols .mapping_symbols
.iter() .iter()
.find(|s| s.symbol_index == symbol_idx) .find(|s| s.symbol_index == symbol_display.symbol)
.map(|s| &s.symbol_diff) .map(|s| &s.symbol_diff)
} else { } else {
obj_diff.symbols.get(symbol_idx) obj_diff.symbols.get(symbol_display.symbol)
}; };
let Some(row) = symbol_diff.and_then(|sd| sd.instruction_rows.get(row_index as usize)) let Some(row) = symbol_diff.and_then(|sd| sd.instruction_rows.get(row_index as usize))
else { else {
@ -208,7 +209,7 @@ impl GuestDisplay for Component {
}; };
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow(); let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
let mut segments = Vec::with_capacity(16); let mut segments = Vec::with_capacity(16);
diff::display::display_row(obj, symbol_idx, row, &diff_config, |segment| { diff::display::display_row(obj, symbol_display.symbol, row, &diff_config, |segment| {
segments.push(DiffTextSegment::from(segment)); segments.push(DiffTextSegment::from(segment));
Ok(()) Ok(())
}) })
@ -216,26 +217,22 @@ impl GuestDisplay for Component {
InstructionDiffRow { segments, diff_kind: InstructionDiffKind::from(row.kind) } InstructionDiffRow { segments, diff_kind: InstructionDiffKind::from(row.kind) }
} }
fn symbol_context( fn symbol_context(diff: ObjectDiffBorrow, symbol_ref: SymbolRef) -> Vec<ContextItem> {
diff: ObjectDiffBorrow,
symbol_display: SectionDisplaySymbol,
) -> Vec<ContextItem> {
let obj_diff = diff.get::<ResourceObjectDiff>(); let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref(); let obj = obj_diff.0.as_ref();
let symbol_display = from_symbol_ref(symbol_ref);
diff::display::symbol_context(obj, symbol_display.symbol as usize) diff::display::symbol_context(obj, symbol_display.symbol as usize)
.into_iter() .into_iter()
.map(|item| ContextItem::from(item)) .map(|item| ContextItem::from(item))
.collect() .collect()
} }
fn symbol_hover( fn symbol_hover(diff: ObjectDiffBorrow, symbol_ref: SymbolRef) -> Vec<HoverItem> {
diff: ObjectDiffBorrow,
symbol_display: SectionDisplaySymbol,
) -> Vec<HoverItem> {
let obj_diff = diff.get::<ResourceObjectDiff>(); let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref(); let obj = obj_diff.0.as_ref();
let addend = 0; // TODO let addend = 0; // TODO
let override_color = None; // TODO: colorize replaced/deleted/inserted relocations let override_color = None; // TODO: colorize replaced/deleted/inserted relocations
let symbol_display = from_symbol_ref(symbol_ref);
diff::display::symbol_hover(obj, symbol_display.symbol as usize, addend, override_color) diff::display::symbol_hover(obj, symbol_display.symbol as usize, addend, override_color)
.into_iter() .into_iter()
.map(|item| HoverItem::from(item)) .map(|item| HoverItem::from(item))
@ -244,22 +241,22 @@ impl GuestDisplay for Component {
fn instruction_context( fn instruction_context(
diff: ObjectDiffBorrow, diff: ObjectDiffBorrow,
symbol_display: SectionDisplaySymbol, symbol_ref: SymbolRef,
row_index: u32, row_index: u32,
diff_config: DiffConfigBorrow, diff_config: DiffConfigBorrow,
) -> Vec<ContextItem> { ) -> Vec<ContextItem> {
let obj_diff = diff.get::<ResourceObjectDiff>(); let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref(); let obj = obj_diff.0.as_ref();
let obj_diff = &obj_diff.1; let obj_diff = &obj_diff.1;
let symbol_idx = symbol_display.symbol as usize; let symbol_display = from_symbol_ref(symbol_ref);
let symbol_diff = if symbol_display.is_mapping_symbol { let symbol_diff = if symbol_display.is_mapping_symbol {
obj_diff obj_diff
.mapping_symbols .mapping_symbols
.iter() .iter()
.find(|s| s.symbol_index == symbol_idx) .find(|s| s.symbol_index == symbol_display.symbol)
.map(|s| &s.symbol_diff) .map(|s| &s.symbol_diff)
} else { } else {
obj_diff.symbols.get(symbol_idx) obj_diff.symbols.get(symbol_display.symbol)
}; };
let Some(ins_ref) = symbol_diff let Some(ins_ref) = symbol_diff
.and_then(|sd| sd.instruction_rows.get(row_index as usize)) .and_then(|sd| sd.instruction_rows.get(row_index as usize))
@ -268,7 +265,7 @@ impl GuestDisplay for Component {
return Vec::new(); return Vec::new();
}; };
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow(); let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
let Some(resolved) = obj.resolve_instruction_ref(symbol_idx, ins_ref) else { let Some(resolved) = obj.resolve_instruction_ref(symbol_display.symbol, ins_ref) else {
return vec![ContextItem::Copy(ContextItemCopy { return vec![ContextItem::Copy(ContextItemCopy {
value: "Failed to resolve instruction".to_string(), value: "Failed to resolve instruction".to_string(),
label: Some("error".to_string()), label: Some("error".to_string()),
@ -291,22 +288,22 @@ impl GuestDisplay for Component {
fn instruction_hover( fn instruction_hover(
diff: ObjectDiffBorrow, diff: ObjectDiffBorrow,
symbol_display: SectionDisplaySymbol, symbol_ref: SymbolRef,
row_index: u32, row_index: u32,
diff_config: DiffConfigBorrow, diff_config: DiffConfigBorrow,
) -> Vec<HoverItem> { ) -> Vec<HoverItem> {
let obj_diff = diff.get::<ResourceObjectDiff>(); let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref(); let obj = obj_diff.0.as_ref();
let obj_diff = &obj_diff.1; let obj_diff = &obj_diff.1;
let symbol_idx = symbol_display.symbol as usize; let symbol_display = from_symbol_ref(symbol_ref);
let symbol_diff = if symbol_display.is_mapping_symbol { let symbol_diff = if symbol_display.is_mapping_symbol {
obj_diff obj_diff
.mapping_symbols .mapping_symbols
.iter() .iter()
.find(|s| s.symbol_index == symbol_idx) .find(|s| s.symbol_index == symbol_display.symbol)
.map(|s| &s.symbol_diff) .map(|s| &s.symbol_diff)
} else { } else {
obj_diff.symbols.get(symbol_idx) obj_diff.symbols.get(symbol_display.symbol)
}; };
let Some(ins_ref) = symbol_diff let Some(ins_ref) = symbol_diff
.and_then(|sd| sd.instruction_rows.get(row_index as usize)) .and_then(|sd| sd.instruction_rows.get(row_index as usize))
@ -315,7 +312,7 @@ impl GuestDisplay for Component {
return Vec::new(); return Vec::new();
}; };
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow(); let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
let Some(resolved) = obj.resolve_instruction_ref(symbol_idx, ins_ref) else { let Some(resolved) = obj.resolve_instruction_ref(symbol_display.symbol, ins_ref) else {
return vec![HoverItem::Text(HoverItemText { return vec![HoverItem::Text(HoverItemText {
label: "Error".to_string(), label: "Error".to_string(),
value: "Failed to resolve instruction".to_string(), value: "Failed to resolve instruction".to_string(),
@ -497,20 +494,56 @@ impl GuestObject for ResourceObject {
} }
impl GuestObjectDiff for ResourceObjectDiff { impl GuestObjectDiff for ResourceObjectDiff {
fn find_symbol(&self, name: String, section_name: Option<String>) -> Option<SymbolRef> { fn find_symbol(&self, name: String, section_name: Option<String>) -> Option<SymbolInfo> {
let obj = self.0.as_ref(); let obj = self.0.as_ref();
obj.symbols let symbol_idx = obj.symbols.iter().position(|s| {
.iter() s.name == name
.position(|s| { && match section_name.as_deref() {
s.name == name Some(section_name) => {
&& match section_name.as_deref() { s.section.is_some_and(|n| obj.sections[n].name == section_name)
Some(section_name) => {
s.section.is_some_and(|n| obj.sections[n].name == section_name)
}
None => true,
} }
}) None => true,
.map(|i| i as SymbolRef) }
})?;
let symbol = obj.symbols.get(symbol_idx)?;
Some(SymbolInfo {
id: symbol_idx as SymbolRef,
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
address: symbol.address,
size: symbol.size,
kind: SymbolKind::from(symbol.kind),
section: symbol.section.map(|s| s as u32),
section_name: symbol
.section
.and_then(|s| obj.sections.get(s).map(|sec| sec.name.clone())),
flags: SymbolFlags::from(symbol.flags),
align: symbol.align.map(|a| a.get()),
virtual_address: symbol.virtual_address,
})
}
fn get_symbol(&self, symbol_ref: SymbolRef) -> Option<SymbolInfo> {
let obj = self.0.as_ref();
let symbol_display = from_symbol_ref(symbol_ref);
let Some(symbol) = obj.symbols.get(symbol_display.symbol) else {
return None;
};
Some(SymbolInfo {
id: to_symbol_ref(symbol_display),
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
address: symbol.address,
size: symbol.size,
kind: SymbolKind::from(symbol.kind),
section: symbol.section.map(|s| s as u32),
section_name: symbol
.section
.and_then(|s| obj.sections.get(s).map(|sec| sec.name.clone())),
flags: SymbolFlags::from(symbol.flags),
align: symbol.align.map(|a| a.get()),
virtual_address: symbol.virtual_address,
})
} }
} }
@ -580,18 +613,28 @@ impl Default for SymbolFlags {
fn default() -> Self { Self::empty() } fn default() -> Self { Self::empty() }
} }
impl Default for SymbolDisplay { impl Default for SymbolInfo {
fn default() -> Self { fn default() -> Self {
Self { Self {
id: u32::MAX,
name: Default::default(), name: Default::default(),
demangled_name: Default::default(), demangled_name: Default::default(),
address: Default::default(), address: Default::default(),
size: Default::default(), size: Default::default(),
kind: Default::default(), kind: Default::default(),
section: Default::default(), section: Default::default(),
section_name: Default::default(),
flags: Default::default(), flags: Default::default(),
align: Default::default(), align: Default::default(),
virtual_address: Default::default(), virtual_address: Default::default(),
}
}
}
impl Default for SymbolDisplay {
fn default() -> Self {
Self {
info: Default::default(),
target_symbol: Default::default(), target_symbol: Default::default(),
match_percent: Default::default(), match_percent: Default::default(),
diff_score: Default::default(), diff_score: Default::default(),
@ -600,4 +643,30 @@ impl Default for SymbolDisplay {
} }
} }
impl From<MappingConfig> for diff::MappingConfig {
fn from(config: MappingConfig) -> Self {
Self {
mappings: config.mappings.into_iter().collect(),
selecting_left: config.selecting_left,
selecting_right: config.selecting_right,
}
}
}
fn from_symbol_ref(symbol_ref: SymbolRef) -> diff::display::SectionDisplaySymbol {
diff::display::SectionDisplaySymbol {
symbol: (symbol_ref & !(1 << 31)) as usize,
is_mapping_symbol: (symbol_ref & (1 << 31)) != 0,
}
}
fn to_symbol_ref(display_symbol: diff::display::SectionDisplaySymbol) -> SymbolRef {
if display_symbol.is_mapping_symbol {
// Use the highest bit to indicate a mapping symbol
display_symbol.symbol as u32 | (1 << 31)
} else {
display_symbol.symbol as u32
}
}
export!(Component); export!(Component);

View File

@ -24,58 +24,8 @@ interface diff {
hash: func() -> u64; hash: func() -> u64;
} }
resource object-diff {
find-symbol: func(
name: string,
section-name: option<string>
) -> option<u32>;
}
record diff-result {
left: option<object-diff>,
right: option<object-diff>,
}
run-diff: func(
left: option<borrow<object>>,
right: option<borrow<object>>,
config: borrow<diff-config>,
) -> result<diff-result, string>;
}
interface display {
use diff.{
object,
object-diff,
diff-config
};
type symbol-ref = u32; type symbol-ref = u32;
record display-config {
show-hidden-symbols: bool,
show-mapped-symbols: bool,
reverse-fn-order: bool,
}
record symbol-filter {
regex: option<string>,
mapping: option<symbol-ref>,
}
record section-display-symbol {
symbol: symbol-ref,
is-mapping-symbol: bool,
}
record section-display {
id: string,
name: string,
size: u64,
match-percent: option<f32>,
symbols: list<section-display-symbol>,
}
enum symbol-kind { enum symbol-kind {
unknown, unknown,
function, function,
@ -94,17 +44,74 @@ interface display {
ignored, ignored,
} }
record symbol-display { record symbol-info {
id: symbol-ref,
name: string, name: string,
demangled-name: option<string>, demangled-name: option<string>,
address: u64, address: u64,
size: u64, size: u64,
kind: symbol-kind, kind: symbol-kind,
section: option<u32>, section: option<u32>,
section-name: option<string>,
%flags: symbol-flags, %flags: symbol-flags,
align: option<u32>, align: option<u32>,
virtual-address: option<u64>, virtual-address: option<u64>,
}
resource object-diff {
find-symbol: func(
name: string,
section-name: option<string>
) -> option<symbol-info>;
get-symbol: func(
id: u32
) -> option<symbol-info>;
}
record diff-result {
left: option<object-diff>,
right: option<object-diff>,
}
run-diff: func(
left: option<borrow<object>>,
right: option<borrow<object>>,
config: borrow<diff-config>,
mapping: mapping-config,
) -> result<diff-result, string>;
}
interface display {
use diff.{
object,
object-diff,
diff-config,
symbol-info,
symbol-ref
};
record display-config {
show-hidden-symbols: bool,
show-mapped-symbols: bool,
reverse-fn-order: bool,
}
record symbol-filter {
regex: option<string>,
mapping: option<symbol-ref>,
}
record section-display {
id: string,
name: string,
size: u64,
match-percent: option<f32>,
symbols: list<symbol-ref>,
}
record symbol-display {
info: symbol-info,
target-symbol: option<symbol-ref>, target-symbol: option<symbol-ref>,
match-percent: option<f32>, match-percent: option<f32>,
diff-score: option<tuple<u64, u64>>, diff-score: option<tuple<u64, u64>>,
@ -232,36 +239,36 @@ interface display {
display-symbol: func( display-symbol: func(
diff: borrow<object-diff>, diff: borrow<object-diff>,
symbol: section-display-symbol, symbol: symbol-ref,
) -> symbol-display; ) -> symbol-display;
display-instruction-row: func( display-instruction-row: func(
diff: borrow<object-diff>, diff: borrow<object-diff>,
symbol: section-display-symbol, symbol: symbol-ref,
row-index: u32, row-index: u32,
config: borrow<diff-config>, config: borrow<diff-config>,
) -> instruction-diff-row; ) -> instruction-diff-row;
symbol-context: func( symbol-context: func(
diff: borrow<object-diff>, diff: borrow<object-diff>,
symbol: section-display-symbol, symbol: symbol-ref,
) -> list<context-item>; ) -> list<context-item>;
symbol-hover: func( symbol-hover: func(
diff: borrow<object-diff>, diff: borrow<object-diff>,
symbol: section-display-symbol, symbol: symbol-ref,
) -> list<hover-item>; ) -> list<hover-item>;
instruction-context: func( instruction-context: func(
diff: borrow<object-diff>, diff: borrow<object-diff>,
symbol: section-display-symbol, symbol: symbol-ref,
row-index: u32, row-index: u32,
config: borrow<diff-config>, config: borrow<diff-config>,
) -> list<context-item>; ) -> list<context-item>;
instruction-hover: func( instruction-hover: func(
diff: borrow<object-diff>, diff: borrow<object-diff>,
symbol: section-display-symbol, symbol: symbol-ref,
row-index: u32, row-index: u32,
config: borrow<diff-config>, config: borrow<diff-config>,
) -> list<hover-item>; ) -> list<hover-item>;