Compare commits

..

No commits in common. "main" and "v3.0.0-beta.7" have entirely different histories.

24 changed files with 233 additions and 539 deletions

View File

@ -1,4 +1,5 @@
# statically link the C runtime so the executable does not depend on [target.x86_64-pc-windows-msvc]
# that shared/dynamic library. linker = "rust-lld"
[target.'cfg(all(target_env = "msvc", target_os = "windows"))']
rustflags = ["-C", "target-feature=+crt-static"] [target.aarch64-pc-windows-msvc]
linker = "rust-lld"

8
Cargo.lock generated
View File

@ -3373,7 +3373,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-cli" name = "objdiff-cli"
version = "3.0.0-beta.9" version = "3.0.0-beta.7"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"argp", "argp",
@ -3396,7 +3396,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-core" name = "objdiff-core"
version = "3.0.0-beta.9" version = "3.0.0-beta.7"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arm-attr", "arm-attr",
@ -3450,7 +3450,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-gui" name = "objdiff-gui"
version = "3.0.0-beta.9" version = "3.0.0-beta.7"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cfg-if", "cfg-if",
@ -3486,7 +3486,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-wasm" name = "objdiff-wasm"
version = "3.0.0-beta.9" version = "3.0.0-beta.7"
dependencies = [ dependencies = [
"log", "log",
"objdiff-core", "objdiff-core",

View File

@ -14,7 +14,7 @@ strip = "debuginfo"
codegen-units = 1 codegen-units = 1
[workspace.package] [workspace.package]
version = "3.0.0-beta.9" version = "3.0.0-beta.7"
authors = ["Luke Street <luke@street.dev>"] authors = ["Luke Street <luke@street.dev>"]
edition = "2024" edition = "2024"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"

View File

@ -175,10 +175,6 @@
"type": "boolean", "type": "boolean",
"description": "If true, objdiff will run the build command with the context file as an argument to generate it.", "description": "If true, objdiff will run the build command with the context file as an argument to generate it.",
"default": false "default": false
},
"preset_id": {
"type": "number",
"description": "The decomp.me preset ID to use for the scratch.\nCompiler and flags in the config will take precedence over the preset, but the preset is useful for organizational purposes."
} }
}, },
"required": [ "required": [

View File

@ -245,14 +245,12 @@ fn report_object(
for (symbol, symbol_diff) in obj.symbols.iter().zip(&obj_diff.symbols) { for (symbol, symbol_diff) in obj.symbols.iter().zip(&obj_diff.symbols) {
if symbol.section != Some(section_idx) if symbol.section != Some(section_idx)
|| symbol.size == 0 || symbol.size == 0
|| symbol.flags.contains(SymbolFlag::Hidden) || symbol.flags.contains(SymbolFlag::Hidden | SymbolFlag::Ignored)
|| symbol.flags.contains(SymbolFlag::Ignored)
{ {
continue; continue;
} }
if let Some(existing_functions) = &mut existing_functions { if let Some(existing_functions) = &mut existing_functions {
if (symbol.flags.contains(SymbolFlag::Global) if symbol.flags.contains(SymbolFlag::Global | SymbolFlag::Weak)
|| symbol.flags.contains(SymbolFlag::Weak))
&& !existing_functions.insert(symbol.name.clone()) && !existing_functions.insert(symbol.name.clone())
{ {
continue; continue;

View File

@ -450,11 +450,11 @@ impl UiView for FunctionDiffUi {
fn reload(&mut self, state: &AppState) -> Result<()> { fn reload(&mut self, state: &AppState) -> Result<()> {
let left_sym = let left_sym =
state.left_obj.as_ref().and_then(|(o, _)| o.symbol_by_name(&self.symbol_name)); state.left_obj.as_ref().and_then(|(o, _)| find_function(o, &self.symbol_name));
let right_sym = let right_sym =
state.right_obj.as_ref().and_then(|(o, _)| o.symbol_by_name(&self.symbol_name)); state.right_obj.as_ref().and_then(|(o, _)| find_function(o, &self.symbol_name));
let prev_sym = let prev_sym =
state.prev_obj.as_ref().and_then(|(o, _)| o.symbol_by_name(&self.symbol_name)); state.prev_obj.as_ref().and_then(|(o, _)| find_function(o, &self.symbol_name));
self.num_rows = match ( self.num_rows = match (
get_symbol(state.left_obj.as_ref(), left_sym), get_symbol(state.left_obj.as_ref(), left_sym),
get_symbol(state.right_obj.as_ref(), right_sym), get_symbol(state.right_obj.as_ref(), right_sym),
@ -650,3 +650,12 @@ fn get_symbol(
let sym = sym?; let sym = sym?;
Some((obj, sym, &diff.symbols[sym])) Some((obj, sym, &diff.symbols[sym]))
} }
fn find_function(obj: &Object, name: &str) -> Option<usize> {
for (symbol_idx, symbol) in obj.symbols.iter().enumerate() {
if symbol.name == name {
return Some(symbol_idx);
}
}
None
}

View File

@ -41,7 +41,6 @@ any-arch = [
"dep:regex", "dep:regex",
"dep:similar", "dep:similar",
"dep:syn", "dep:syn",
"dep:encoding_rs"
] ]
bindings = [ bindings = [
"dep:prost", "dep:prost",
@ -172,7 +171,7 @@ notify-debouncer-full = { version = "0.5.0", optional = true }
shell-escape = { version = "0.1", optional = true } shell-escape = { version = "0.1", optional = true }
tempfile = { version = "3.19", optional = true } tempfile = { version = "3.19", optional = true }
time = { version = "0.3", optional = true } time = { version = "0.3", optional = true }
encoding_rs = { version = "0.8.35", optional = true } encoding_rs = "0.8.35"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", optional = true } winapi = { version = "0.3", optional = true }

View File

@ -225,7 +225,7 @@ impl Arch for ArchArm {
let mut address = start_addr; let mut address = start_addr;
while address < end_addr { while address < end_addr {
while let Some(next) = next_mapping.filter(|x| address >= x.address) { while let Some(next) = next_mapping.take_if(|x| address >= x.address) {
// Change mapping // Change mapping
mode = next.mapping; mode = next.mapping;
next_mapping = mappings_iter.next(); next_mapping = mappings_iter.next();

View File

@ -6,7 +6,7 @@ use iced_x86::{
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter, Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register,
}; };
use object::{Endian as _, Object as _, ObjectSection as _, elf, pe}; use object::{Endian as _, Object as _, ObjectSection as _, pe};
use crate::{ use crate::{
arch::Arch, arch::Arch,
@ -67,24 +67,16 @@ impl ArchX86 {
pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4), pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4),
_ => None, _ => None,
}, },
RelocationFlags::Elf(typ) => match typ {
elf::R_386_32 | elf::R_386_PC32 => Some(4),
elf::R_386_16 => Some(2),
_ => None, _ => None,
}, },
},
Architecture::X86_64 => match flags { Architecture::X86_64 => match flags {
RelocationFlags::Coff(typ) => match typ { RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32 => Some(4), pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32 => Some(4),
pe::IMAGE_REL_AMD64_ADDR64 => Some(8), pe::IMAGE_REL_AMD64_ADDR64 => Some(8),
_ => None, _ => None,
}, },
RelocationFlags::Elf(typ) => match typ {
elf::R_X86_64_PC32 => Some(4),
elf::R_X86_64_64 => Some(8),
_ => None, _ => None,
}, },
},
} }
} }
} }
@ -235,8 +227,7 @@ impl Arch for ArchX86 {
) -> Result<i64> { ) -> Result<i64> {
match self.arch { match self.arch {
Architecture::X86 => match flags { Architecture::X86 => match flags {
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => {
| RelocationFlags::Elf(elf::R_386_32 | elf::R_386_PC32) => {
let data = let data =
section.data()?[address as usize..address as usize + 4].try_into()?; section.data()?[address as usize..address as usize + 4].try_into()?;
Ok(self.endianness.read_i32_bytes(data) as i64) Ok(self.endianness.read_i32_bytes(data) as i64)
@ -244,14 +235,12 @@ impl Arch for ArchX86 {
flags => bail!("Unsupported x86 implicit relocation {flags:?}"), flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
}, },
Architecture::X86_64 => match flags { Architecture::X86_64 => match flags {
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32) RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32) => {
| RelocationFlags::Elf(elf::R_X86_64_32 | elf::R_X86_64_PC32) => {
let data = let data =
section.data()?[address as usize..address as usize + 4].try_into()?; section.data()?[address as usize..address as usize + 4].try_into()?;
Ok(self.endianness.read_i32_bytes(data) as i64) Ok(self.endianness.read_i32_bytes(data) as i64)
} }
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR64) RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR64) => {
| RelocationFlags::Elf(elf::R_X86_64_64) => {
let data = let data =
section.data()?[address as usize..address as usize + 8].try_into()?; section.data()?[address as usize..address as usize + 8].try_into()?;
Ok(self.endianness.read_i64_bytes(data)) Ok(self.endianness.read_i64_bytes(data))

View File

@ -325,11 +325,12 @@ fn reloc_eq(
|| display_ins_data_literals(left_obj, left_ins) || display_ins_data_literals(left_obj, left_ins)
== display_ins_data_literals(right_obj, right_ins)) == display_ins_data_literals(right_obj, right_ins))
} }
(Some(_), None) => false,
(None, Some(_)) => { (None, Some(_)) => {
// Match if possibly stripped weak symbol // Match if possibly stripped weak symbol
symbol_name_addend_matches && right_reloc.symbol.flags.contains(SymbolFlag::Weak) symbol_name_addend_matches && right_reloc.symbol.flags.contains(SymbolFlag::Weak)
} }
(Some(_), None) | (None, None) => symbol_name_addend_matches, (None, None) => symbol_name_addend_matches,
} }
} }

View File

@ -53,11 +53,12 @@ fn reloc_eq(
section_name_eq(left_obj, right_obj, sl, sr) section_name_eq(left_obj, right_obj, sl, sr)
&& (symbol_name_addend_matches || address_eq(left, right)) && (symbol_name_addend_matches || address_eq(left, right))
} }
(Some(_), None) => false,
(None, Some(_)) => { (None, Some(_)) => {
// Match if possibly stripped weak symbol // Match if possibly stripped weak symbol
symbol_name_addend_matches && right.symbol.flags.contains(SymbolFlag::Weak) symbol_name_addend_matches && right.symbol.flags.contains(SymbolFlag::Weak)
} }
(Some(_), None) | (None, None) => symbol_name_addend_matches, (None, None) => symbol_name_addend_matches,
} }
} }

View File

@ -77,7 +77,7 @@ impl<'a> DiffTextSegment<'a> {
const EOL_SEGMENT: DiffTextSegment<'static> = const EOL_SEGMENT: DiffTextSegment<'static> =
DiffTextSegment { text: DiffText::Eol, color: DiffTextColor::Normal, pad_to: 0 }; DiffTextSegment { text: DiffText::Eol, color: DiffTextColor::Normal, pad_to: 0 };
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone, PartialEq, Eq)]
pub enum HighlightKind { pub enum HighlightKind {
#[default] #[default]
None, None,
@ -288,18 +288,6 @@ pub fn display_row(
Ok(()) Ok(())
} }
impl PartialEq<HighlightKind> for HighlightKind {
fn eq(&self, other: &HighlightKind) -> bool {
match (self, other) {
(HighlightKind::Opcode(a), HighlightKind::Opcode(b)) => a == b,
(HighlightKind::Argument(a), HighlightKind::Argument(b)) => a.loose_eq(b),
(HighlightKind::Symbol(a), HighlightKind::Symbol(b)) => a == b,
(HighlightKind::Address(a), HighlightKind::Address(b)) => a == b,
_ => false,
}
}
}
impl PartialEq<DiffText<'_>> for HighlightKind { impl PartialEq<DiffText<'_>> for HighlightKind {
fn eq(&self, other: &DiffText) -> bool { fn eq(&self, other: &DiffText) -> bool {
match (self, other) { match (self, other) {
@ -616,9 +604,7 @@ fn symbol_matches_filter(
return false; return false;
} }
if !show_hidden_symbols if !show_hidden_symbols
&& (symbol.size == 0 && (symbol.size == 0 || symbol.flags.contains(SymbolFlag::Hidden | SymbolFlag::Ignored))
|| symbol.flags.contains(SymbolFlag::Hidden)
|| symbol.flags.contains(SymbolFlag::Ignored))
{ {
return false; return false;
} }

View File

@ -341,25 +341,11 @@ pub fn diff_objs(
if let (Some((right_obj, right_out)), Some((left_obj, left_out))) = if let (Some((right_obj, right_out)), Some((left_obj, left_out))) =
(right.as_mut(), left.as_mut()) (right.as_mut(), left.as_mut())
{ {
if let Some(right_name) = mapping_config.selecting_left.as_deref() { if let Some(right_name) = &mapping_config.selecting_left {
generate_mapping_symbols( generate_mapping_symbols(right_obj, right_name, left_obj, left_out, diff_config)?;
left_obj,
left_out,
right_obj,
right_out,
MappingSymbol::Right(right_name),
diff_config,
)?;
} }
if let Some(left_name) = mapping_config.selecting_right.as_deref() { if let Some(left_name) = &mapping_config.selecting_right {
generate_mapping_symbols( generate_mapping_symbols(left_obj, left_name, right_obj, right_out, diff_config)?;
left_obj,
left_out,
right_obj,
right_out,
MappingSymbol::Left(left_name),
diff_config,
)?;
} }
} }
@ -370,28 +356,17 @@ pub fn diff_objs(
}) })
} }
#[derive(Clone, Copy)]
enum MappingSymbol<'a> {
Left(&'a str),
Right(&'a str),
}
/// When we're selecting a symbol to use as a comparison, we'll create comparisons for all /// When we're selecting a symbol to use as a comparison, we'll create comparisons for all
/// symbols in the other object that match the selected symbol's section and kind. This allows /// symbols in the other object that match the selected symbol's section and kind. This allows
/// us to display match percentages for all symbols in the other object that could be selected. /// us to display match percentages for all symbols in the other object that could be selected.
fn generate_mapping_symbols( fn generate_mapping_symbols(
left_obj: &Object, base_obj: &Object,
left_out: &mut ObjectDiff, base_name: &str,
right_obj: &Object, target_obj: &Object,
right_out: &mut ObjectDiff, target_out: &mut ObjectDiff,
mapping_symbol: MappingSymbol,
config: &DiffObjConfig, config: &DiffObjConfig,
) -> Result<()> { ) -> Result<()> {
let (base_obj, base_name, target_obj) = match mapping_symbol { let Some(base_symbol_ref) = symbol_ref_by_name(base_obj, base_name) else {
MappingSymbol::Left(name) => (left_obj, name, right_obj),
MappingSymbol::Right(name) => (right_obj, name, left_obj),
};
let Some(base_symbol_ref) = base_obj.symbol_by_name(base_name) else {
return Ok(()); return Ok(());
}; };
let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]); let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]);
@ -402,30 +377,32 @@ fn generate_mapping_symbols(
{ {
continue; continue;
} }
let (left_symbol_idx, right_symbol_idx) = match mapping_symbol { match base_section_kind {
MappingSymbol::Left(_) => (base_symbol_ref, target_symbol_index),
MappingSymbol::Right(_) => (target_symbol_index, base_symbol_ref),
};
let (left_diff, right_diff) = match base_section_kind {
SectionKind::Code => { SectionKind::Code => {
diff_code(left_obj, right_obj, left_symbol_idx, right_symbol_idx, config) let (left_diff, _right_diff) =
diff_code(target_obj, base_obj, target_symbol_index, base_symbol_ref, config)?;
target_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: target_symbol_index,
symbol_diff: left_diff,
});
} }
SectionKind::Data => { SectionKind::Data => {
diff_data_symbol(left_obj, right_obj, left_symbol_idx, right_symbol_idx) let (left_diff, _right_diff) =
diff_data_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?;
target_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: target_symbol_index,
symbol_diff: left_diff,
});
} }
SectionKind::Bss | SectionKind::Common => { SectionKind::Bss | SectionKind::Common => {
diff_bss_symbol(left_obj, right_obj, left_symbol_idx, right_symbol_idx) let (left_diff, _right_diff) =
diff_bss_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?;
target_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: target_symbol_index,
symbol_diff: left_diff,
});
} }
SectionKind::Unknown => continue, SectionKind::Unknown => {}
}?;
match mapping_symbol {
MappingSymbol::Left(_) => right_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: right_symbol_idx,
symbol_diff: right_diff,
}),
MappingSymbol::Right(_) => left_out
.mapping_symbols
.push(MappingSymbolDiff { symbol_index: left_symbol_idx, symbol_diff: left_diff }),
} }
} }
Ok(()) Ok(())
@ -457,6 +434,10 @@ pub struct MappingConfig {
pub selecting_right: Option<String>, pub selecting_right: Option<String>,
} }
fn symbol_ref_by_name(obj: &Object, name: &str) -> Option<usize> {
obj.symbols.iter().position(|s| s.name == name)
}
fn apply_symbol_mappings( fn apply_symbol_mappings(
left: &Object, left: &Object,
right: &Object, right: &Object,
@ -468,25 +449,25 @@ fn apply_symbol_mappings(
// If we're selecting a symbol to use as a comparison, mark it as used // If we're selecting a symbol to use as a comparison, mark it as used
// This ensures that we don't match it to another symbol at any point // This ensures that we don't match it to another symbol at any point
if let Some(left_name) = &mapping_config.selecting_left { if let Some(left_name) = &mapping_config.selecting_left {
if let Some(left_symbol) = left.symbol_by_name(left_name) { if let Some(left_symbol) = symbol_ref_by_name(left, left_name) {
left_used.insert(left_symbol); left_used.insert(left_symbol);
} }
} }
if let Some(right_name) = &mapping_config.selecting_right { if let Some(right_name) = &mapping_config.selecting_right {
if let Some(right_symbol) = right.symbol_by_name(right_name) { if let Some(right_symbol) = symbol_ref_by_name(right, right_name) {
right_used.insert(right_symbol); right_used.insert(right_symbol);
} }
} }
// Apply manual symbol mappings // Apply manual symbol mappings
for (left_name, right_name) in &mapping_config.mappings { for (left_name, right_name) in &mapping_config.mappings {
let Some(left_symbol_index) = left.symbol_by_name(left_name) else { let Some(left_symbol_index) = symbol_ref_by_name(left, left_name) else {
continue; continue;
}; };
if left_used.contains(&left_symbol_index) { if left_used.contains(&left_symbol_index) {
continue; continue;
} }
let Some(right_symbol_index) = right.symbol_by_name(right_name) else { let Some(right_symbol_index) = symbol_ref_by_name(right, right_name) else {
continue; continue;
}; };
if right_used.contains(&right_symbol_index) { if right_used.contains(&right_symbol_index) {

View File

@ -118,7 +118,7 @@ impl Section {
Err(i) => self Err(i) => self
.relocations .relocations
.get(i) .get(i)
.filter(|r| r.address < ins_ref.address + ins_ref.size as u64), .take_if(|r| r.address < ins_ref.address + ins_ref.size as u64),
} }
.and_then(|relocation| { .and_then(|relocation| {
let symbol = obj.symbols.get(relocation.target_symbol)?; let symbol = obj.symbols.get(relocation.target_symbol)?;
@ -308,10 +308,6 @@ impl Object {
let offset = symbol.address.checked_sub(section.address)?; let offset = symbol.address.checked_sub(section.address)?;
section.data.get(offset as usize..offset as usize + symbol.size as usize) section.data.get(offset as usize..offset as usize + symbol.size as usize)
} }
pub fn symbol_by_name(&self, name: &str) -> Option<usize> {
self.symbols.iter().position(|symbol| symbol.section.is_some() && symbol.name == name)
}
} }
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]

View File

@ -121,15 +121,6 @@ fn map_symbols(
Ok((symbols, symbol_indices)) Ok((symbols, symbol_indices))
} }
/// When inferring a symbol's size, we ignore symbols that start with specific prefixes. They are
/// usually emitted as branch targets and do not represent the start of a function or object.
fn is_local_label(symbol: &Symbol) -> bool {
const LABEL_PREFIXES: &[&str] = &[".L", "LAB_"];
symbol.size == 0
&& symbol.flags.contains(SymbolFlag::Local)
&& LABEL_PREFIXES.iter().any(|p| symbol.name.starts_with(p))
}
fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) { fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
// Create a sorted list of symbol indices by section // Create a sorted list of symbol indices by section
let mut symbols_with_section = Vec::<usize>::with_capacity(symbols.len()); let mut symbols_with_section = Vec::<usize>::with_capacity(symbols.len());
@ -176,7 +167,10 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
if last_end.0 == section_idx && last_end.1 > symbol.address { if last_end.0 == section_idx && last_end.1 > symbol.address {
continue; continue;
} }
let next_symbol = loop { let next_symbol = match symbol.kind {
// For function/object symbols, find the next function/object symbol (in other words:
// skip over labels)
SymbolKind::Function | SymbolKind::Object => loop {
if iter_idx >= symbols_with_section.len() { if iter_idx >= symbols_with_section.len() {
break None; break None;
} }
@ -184,20 +178,16 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
if next_symbol.section != Some(section_idx) { if next_symbol.section != Some(section_idx) {
break None; break None;
} }
if match symbol.kind { if let SymbolKind::Function | SymbolKind::Object = next_symbol.kind {
SymbolKind::Function | SymbolKind::Object => {
// For function/object symbols, find the next function/object
matches!(next_symbol.kind, SymbolKind::Function | SymbolKind::Object)
}
SymbolKind::Unknown | SymbolKind::Section => {
// For labels (or anything else), stop at any symbol
true
}
} && !is_local_label(next_symbol)
{
break Some(next_symbol); break Some(next_symbol);
} }
iter_idx += 1; iter_idx += 1;
},
// For labels (or anything else), simply use the next symbol's address
SymbolKind::Unknown | SymbolKind::Section => symbols_with_section
.get(iter_idx)
.map(|&i| &symbols[i])
.take_if(|s| s.section == Some(section_idx)),
}; };
let next_address = next_symbol.map(|s| s.address).unwrap_or_else(|| { let next_address = next_symbol.map(|s| s.address).unwrap_or_else(|| {
let section = &sections[section_idx]; let section = &sections[section_idx];
@ -351,7 +341,7 @@ fn map_section_relocations(
let idx = if let Some(section_symbol) = obj_file let idx = if let Some(section_symbol) = obj_file
.symbol_by_index(idx) .symbol_by_index(idx)
.ok() .ok()
.filter(|s| s.kind() == object::SymbolKind::Section) .take_if(|s| s.kind() == object::SymbolKind::Section)
{ {
let section_index = let section_index =
section_symbol.section_index().context("Section symbol without section")?; section_symbol.section_index().context("Section symbol without section")?;

View File

@ -68,12 +68,3 @@ fn read_x86_jumptable() {
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config); let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
insta::assert_snapshot!(output); insta::assert_snapshot!(output);
} }
// Inferred size of functions should ignore symbols with specific prefixes
#[test]
#[cfg(feature = "x86")]
fn read_x86_local_labels() {
let diff_config = diff::DiffObjConfig::default();
let obj = obj::read::parse(include_object!("data/x86/local_labels.obj"), &diff_config).unwrap();
insta::assert_debug_snapshot!(obj);
}

View File

@ -1,163 +0,0 @@
---
source: objdiff-core/tests/arch_x86.rs
expression: obj
---
Object {
arch: ArchX86 {
arch: X86,
endianness: Little,
},
endianness: Little,
symbols: [
Symbol {
name: "42b830_convertToUppercaseShiftJIS.obj",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.text]",
demangled_name: None,
address: 0,
size: 0,
kind: Section,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b850",
demangled_name: None,
address: 32,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b883",
demangled_name: None,
address: 83,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b87c",
demangled_name: None,
address: 76,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b884",
demangled_name: None,
address: 84,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b889",
demangled_name: None,
address: 89,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b845",
demangled_name: None,
address: 21,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b869",
demangled_name: None,
address: 57,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "ConvertToUppercaseShiftJIS",
demangled_name: None,
address: 0,
size: 92,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | SizeInferred),
align: None,
virtual_address: None,
},
],
sections: [
Section {
id: ".text-0",
name: ".text",
address: 0,
size: 92,
kind: Code,
data: SectionData(
92,
),
flags: FlagSet(),
align: Some(
16,
),
relocations: [],
line_info: {},
virtual_address: None,
},
],
split_meta: None,
path: None,
timestamp: None,
}

View File

@ -147,20 +147,14 @@ pub(crate) fn data_row_ui(
cur_addr += diff.len; cur_addr += diff.len;
} else { } else {
for byte in &diff.data { for byte in &diff.data {
let mut byte_text = format!("{byte:02x} ");
let mut byte_color = base_color; let mut byte_color = base_color;
if let Some(reloc_diff) = reloc_diffs if let Some(reloc_diff) = reloc_diffs.iter().find(|reloc_diff| {
.iter() reloc_diff.kind != DataDiffKind::None
.find(|reloc_diff| reloc_diff.range.contains(&cur_addr_actual)) && reloc_diff.range.contains(&cur_addr_actual)
{ }) {
if *byte == 0 {
// Display 00 data bytes with a relocation as ?? instead.
byte_text = "?? ".to_string();
}
if reloc_diff.kind != DataDiffKind::None {
byte_color = get_color_for_diff_kind(reloc_diff.kind, appearance); byte_color = get_color_for_diff_kind(reloc_diff.kind, appearance);
} }
} let byte_text = format!("{byte:02x} ");
write_text(byte_text.as_str(), byte_color, &mut job, appearance.code_font.clone()); write_text(byte_text.as_str(), byte_color, &mut job, appearance.code_font.clone());
cur_addr += 1; cur_addr += 1;
cur_addr_actual += 1; cur_addr_actual += 1;

View File

@ -49,9 +49,7 @@ impl<'a> DiffColumnContext<'a> {
let selected_symbol = match view { let selected_symbol = match view {
View::SymbolDiff => None, View::SymbolDiff => None,
View::FunctionDiff | View::ExtabDiff => match (obj, selected_symbol) { View::FunctionDiff | View::ExtabDiff => match (obj, selected_symbol) {
(Some(obj), Some(s)) => { (Some(obj), Some(s)) => find_symbol(&obj.0, s).map(SelectedSymbol::Symbol),
obj.0.symbol_by_name(&s.symbol_name).map(SelectedSymbol::Symbol)
}
_ => None, _ => None,
}, },
View::DataDiff => match (obj, selected_symbol) { View::DataDiff => match (obj, selected_symbol) {
@ -499,7 +497,6 @@ pub fn diff_view_ui(
(state.current_view, left_ctx.obj, right_ctx.obj, left_ctx.section, right_ctx.section) (state.current_view, left_ctx.obj, right_ctx.obj, left_ctx.section, right_ctx.section)
{ {
// Joint diff view // Joint diff view
hotkeys::check_scroll_hotkeys(ui, true);
let left_total_bytes = let left_total_bytes =
left_section_diff.data_diff.iter().fold(0usize, |accum, item| accum + item.len); left_section_diff.data_diff.iter().fold(0usize, |accum, item| accum + item.len);
let right_total_bytes = let right_total_bytes =
@ -782,6 +779,10 @@ fn missing_obj_ui(ui: &mut Ui, appearance: &Appearance) {
}); });
} }
fn find_symbol(obj: &Object, selected_symbol: &SymbolRefByName) -> Option<usize> {
obj.symbols.iter().position(|symbol| symbol.name == selected_symbol.symbol_name)
}
fn find_section(obj: &Object, section_name: &str) -> Option<usize> { fn find_section(obj: &Object, section_name: &str) -> Option<usize> {
obj.sections.iter().position(|section| section.name == section_name) obj.sections.iter().position(|section| section.name == section_name)
} }

View File

@ -1,12 +1,12 @@
{ {
"name": "objdiff-wasm", "name": "objdiff-wasm",
"version": "3.0.0-beta.9", "version": "3.0.0-beta.7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "objdiff-wasm", "name": "objdiff-wasm",
"version": "3.0.0-beta.9", "version": "3.0.0-beta.7",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.9.3", "@biomejs/biome": "^1.9.3",

View File

@ -1,6 +1,6 @@
{ {
"name": "objdiff-wasm", "name": "objdiff-wasm",
"version": "3.0.0-beta.9", "version": "3.0.0-beta.7",
"description": "A local diffing tool for decompilation projects.", "description": "A local diffing tool for decompilation projects.",
"author": { "author": {
"name": "Luke Street", "name": "Luke Street",

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, MappingConfig, Object, ObjectBorrow, ObjectDiff, ObjectDiffBorrow, GuestObjectDiff, 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,
SymbolDisplay, SymbolFilter, SymbolNavigationKind, SectionDisplaySymbol, SymbolDisplay, SymbolFilter, SymbolFlags, SymbolKind,
SymbolNavigationKind, SymbolRef,
}, },
}; };
@ -59,17 +59,15 @@ 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,
&mapping_config, &diff::MappingConfig::default(),
) )
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
Ok(DiffResult { Ok(DiffResult {
@ -136,47 +134,48 @@ 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.into_iter().map(to_symbol_ref).collect(), symbols: d
.symbols
.into_iter()
.map(|s| SectionDisplaySymbol {
symbol: s.symbol as SymbolRef,
is_mapping_symbol: s.is_mapping_symbol,
})
.collect(),
}) })
.collect() .collect()
} }
fn display_symbol(diff: ObjectDiffBorrow, symbol_ref: SymbolRef) -> SymbolDisplay { fn display_symbol(
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_display = from_symbol_ref(symbol_ref); let symbol_idx = symbol_display.symbol as usize;
let Some(symbol) = obj.symbols.get(symbol_display.symbol) else { let Some(symbol) = obj.symbols.get(symbol_idx) else {
return SymbolDisplay { return SymbolDisplay { name: "<unknown>".to_string(), ..Default::default() };
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_display.symbol) .find(|s| s.symbol_index == symbol_idx)
.map(|s| &s.symbol_diff) .map(|s| &s.symbol_diff)
} else { } else {
obj_diff.symbols.get(symbol_display.symbol) obj_diff.symbols.get(symbol_idx)
}; };
SymbolDisplay { SymbolDisplay {
info: SymbolInfo {
id: to_symbol_ref(symbol_display),
name: symbol.name.clone(), name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(), demangled_name: symbol.demangled_name.clone(),
address: symbol.address, address: symbol.address,
size: symbol.size, size: symbol.size,
kind: SymbolKind::from(symbol.kind), kind: SymbolKind::from(symbol.kind),
section: symbol.section.map(|s| s as u32), 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), flags: SymbolFlags::from(symbol.flags),
align: symbol.align.map(|a| a.get()), align: symbol.align.map(|a| a.get()),
virtual_address: symbol.virtual_address, 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),
@ -186,22 +185,22 @@ impl GuestDisplay for Component {
fn display_instruction_row( fn display_instruction_row(
diff: ObjectDiffBorrow, diff: ObjectDiffBorrow,
symbol_ref: SymbolRef, symbol_display: SectionDisplaySymbol,
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_display = from_symbol_ref(symbol_ref); let symbol_idx = symbol_display.symbol as usize;
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_display.symbol) .find(|s| s.symbol_index == symbol_idx)
.map(|s| &s.symbol_diff) .map(|s| &s.symbol_diff)
} else { } else {
obj_diff.symbols.get(symbol_display.symbol) obj_diff.symbols.get(symbol_idx)
}; };
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 {
@ -209,7 +208,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_display.symbol, row, &diff_config, |segment| { diff::display::display_row(obj, symbol_idx, row, &diff_config, |segment| {
segments.push(DiffTextSegment::from(segment)); segments.push(DiffTextSegment::from(segment));
Ok(()) Ok(())
}) })
@ -217,22 +216,26 @@ impl GuestDisplay for Component {
InstructionDiffRow { segments, diff_kind: InstructionDiffKind::from(row.kind) } InstructionDiffRow { segments, diff_kind: InstructionDiffKind::from(row.kind) }
} }
fn symbol_context(diff: ObjectDiffBorrow, symbol_ref: SymbolRef) -> Vec<ContextItem> { fn symbol_context(
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(diff: ObjectDiffBorrow, symbol_ref: SymbolRef) -> Vec<HoverItem> { fn symbol_hover(
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))
@ -241,22 +244,22 @@ impl GuestDisplay for Component {
fn instruction_context( fn instruction_context(
diff: ObjectDiffBorrow, diff: ObjectDiffBorrow,
symbol_ref: SymbolRef, symbol_display: SectionDisplaySymbol,
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_display = from_symbol_ref(symbol_ref); let symbol_idx = symbol_display.symbol as usize;
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_display.symbol) .find(|s| s.symbol_index == symbol_idx)
.map(|s| &s.symbol_diff) .map(|s| &s.symbol_diff)
} else { } else {
obj_diff.symbols.get(symbol_display.symbol) obj_diff.symbols.get(symbol_idx)
}; };
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))
@ -265,7 +268,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_display.symbol, ins_ref) else { let Some(resolved) = obj.resolve_instruction_ref(symbol_idx, 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()),
@ -288,22 +291,22 @@ impl GuestDisplay for Component {
fn instruction_hover( fn instruction_hover(
diff: ObjectDiffBorrow, diff: ObjectDiffBorrow,
symbol_ref: SymbolRef, symbol_display: SectionDisplaySymbol,
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_display = from_symbol_ref(symbol_ref); let symbol_idx = symbol_display.symbol as usize;
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_display.symbol) .find(|s| s.symbol_index == symbol_idx)
.map(|s| &s.symbol_diff) .map(|s| &s.symbol_diff)
} else { } else {
obj_diff.symbols.get(symbol_display.symbol) obj_diff.symbols.get(symbol_idx)
}; };
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))
@ -312,7 +315,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_display.symbol, ins_ref) else { let Some(resolved) = obj.resolve_instruction_ref(symbol_idx, 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(),
@ -494,9 +497,11 @@ impl GuestObject for ResourceObject {
} }
impl GuestObjectDiff for ResourceObjectDiff { impl GuestObjectDiff for ResourceObjectDiff {
fn find_symbol(&self, name: String, section_name: Option<String>) -> Option<SymbolInfo> { fn find_symbol(&self, name: String, section_name: Option<String>) -> Option<SymbolRef> {
let obj = self.0.as_ref(); let obj = self.0.as_ref();
let symbol_idx = obj.symbols.iter().position(|s| { obj.symbols
.iter()
.position(|s| {
s.name == name s.name == name
&& match section_name.as_deref() { && match section_name.as_deref() {
Some(section_name) => { Some(section_name) => {
@ -504,46 +509,8 @@ impl GuestObjectDiff for ResourceObjectDiff {
} }
None => true, None => true,
} }
})?;
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,
}) })
.map(|i| i as SymbolRef)
} }
} }
@ -613,28 +580,18 @@ impl Default for SymbolFlags {
fn default() -> Self { Self::empty() } fn default() -> Self { Self::empty() }
} }
impl Default for SymbolInfo { impl Default for SymbolDisplay {
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(),
@ -643,30 +600,4 @@ 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,8 +24,58 @@ 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,
@ -44,74 +94,17 @@ interface diff {
ignored, ignored,
} }
record symbol-info { record symbol-display {
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>>,
@ -239,36 +232,36 @@ interface display {
display-symbol: func( display-symbol: func(
diff: borrow<object-diff>, diff: borrow<object-diff>,
symbol: symbol-ref, symbol: section-display-symbol,
) -> symbol-display; ) -> symbol-display;
display-instruction-row: func( display-instruction-row: func(
diff: borrow<object-diff>, diff: borrow<object-diff>,
symbol: symbol-ref, symbol: section-display-symbol,
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: symbol-ref, symbol: section-display-symbol,
) -> list<context-item>; ) -> list<context-item>;
symbol-hover: func( symbol-hover: func(
diff: borrow<object-diff>, diff: borrow<object-diff>,
symbol: symbol-ref, symbol: section-display-symbol,
) -> list<hover-item>; ) -> list<hover-item>;
instruction-context: func( instruction-context: func(
diff: borrow<object-diff>, diff: borrow<object-diff>,
symbol: symbol-ref, symbol: section-display-symbol,
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: symbol-ref, symbol: section-display-symbol,
row-index: u32, row-index: u32,
config: borrow<diff-config>, config: borrow<diff-config>,
) -> list<hover-item>; ) -> list<hover-item>;