Compare commits

...

8 Commits

Author SHA1 Message Date
Luke Street 0f599bea2b Version 3.7.0 2026-03-08 13:30:17 -06:00
Luke Street 9927fc35b6 Update time and bytes crates (resolves advisories) 2026-03-08 13:07:20 -06:00
Sebastián Meljem 16fb6a491f Demangle function name on decomp.me scratch creation (#329) 2026-03-08 11:14:24 -06:00
Luke Street 2541aef71a Add symbol kind and structured flags to diff proto output (#337)
* Add symbol kind to diff proto output

Expose SymbolKind (Function, Object, Section, Unknown) in the
DiffSymbol proto message, allowing CLI oneshot diff consumers to
distinguish symbol types without inferring from instruction/data
presence.

* Stop filtering symbols from diff output, replace flags bitmask with message

Remove the symbol filter (size == 0 / Ignored) from DiffObject so all
symbols are emitted. This fixes target_symbol indices being invalid
after filtering.

Replace the DiffSymbolFlag bitmask enum + uint32 field with a
DiffSymbolFlags message of booleans, adding ignored and size_inferred
flags so consumers can filter on their end.
2026-03-08 11:13:40 -06:00
First Last 7ad1f95bf7 Fix symbol index-to-addend bug (#336) 2026-02-21 21:15:53 -07:00
LagoLunatic 66c879a95d Version 3.6.1 (#328) 2026-01-30 05:40:02 +00:00
rjkiv d53c358d2f automatically match MSVC symbols in anonymous namespaces (#325)
* automatically match MSVC symbols in anonymous namespaces

* tweak logic for detecting anonymous MSVC symbols

* fix package format

* address feedback

* lazylock the regex

* formatting

* use once_cell instead

* formatting

* remove regex
2026-01-30 00:13:10 -05:00
Avery Townsend f24c017545 merge gnu function and data sections (#327) 2026-01-29 16:58:52 -07:00
11 changed files with 98 additions and 53 deletions
Generated
+15 -15
View File
@@ -598,9 +598,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
version = "1.11.0"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "calloop"
@@ -3157,9 +3157,9 @@ dependencies = [
[[package]]
name = "num-conv"
version = "0.1.0"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
[[package]]
name = "num-traits"
@@ -3491,7 +3491,7 @@ dependencies = [
[[package]]
name = "objdiff-cli"
version = "3.6.0"
version = "3.7.0"
dependencies = [
"anyhow",
"argp",
@@ -3514,7 +3514,7 @@ dependencies = [
[[package]]
name = "objdiff-core"
version = "3.6.0"
version = "3.7.0"
dependencies = [
"anyhow",
"arm-attr",
@@ -3570,7 +3570,7 @@ dependencies = [
[[package]]
name = "objdiff-gui"
version = "3.6.0"
version = "3.7.0"
dependencies = [
"anyhow",
"argp",
@@ -3608,7 +3608,7 @@ dependencies = [
[[package]]
name = "objdiff-wasm"
version = "3.6.0"
version = "3.7.0"
dependencies = [
"log",
"objdiff-core",
@@ -5327,9 +5327,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.44"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
@@ -5337,22 +5337,22 @@ dependencies = [
"num-conv",
"num_threads",
"powerfmt",
"serde",
"serde_core",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.6"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.2.24"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [
"num-conv",
"time-core",
+1 -1
View File
@@ -14,7 +14,7 @@ default-members = [
resolver = "3"
[workspace.package]
version = "3.6.0"
version = "3.7.0"
authors = ["Luke Street <luke@street.dev>"]
edition = "2024"
license = "MIT OR Apache-2.0"
+18 -9
View File
@@ -39,7 +39,8 @@ message DiffSymbol {
optional string demangled_name = 2;
uint64 address = 3;
uint64 size = 4;
uint32 flags = 5;
DiffSymbolFlags flags = 5;
DiffSymbolKind kind = 10;
// The symbol index in the _other_ object that this symbol was diffed against
optional uint32 target_symbol = 6;
optional float match_percent = 7;
@@ -49,14 +50,22 @@ message DiffSymbol {
repeated DiffDataSegment data_diff = 9;
}
// Symbol visibility flags (bitmask)
enum DiffSymbolFlag {
SYMBOL_NONE = 0;
SYMBOL_GLOBAL = 1;
SYMBOL_LOCAL = 2;
SYMBOL_WEAK = 4;
SYMBOL_COMMON = 8;
SYMBOL_HIDDEN = 16;
enum DiffSymbolKind {
SYMBOL_UNKNOWN = 0;
SYMBOL_FUNCTION = 1;
SYMBOL_OBJECT = 2;
SYMBOL_SECTION = 3;
}
// Symbol flags
message DiffSymbolFlags {
bool global = 1;
bool local = 2;
bool weak = 3;
bool common = 4;
bool hidden = 5;
bool ignored = 6;
bool size_inferred = 7;
}
// A single instruction diff row
Binary file not shown.
+2 -2
View File
@@ -238,9 +238,9 @@ impl Arch for ArchPpc {
addend: 0,
})),
|(_, reloc)| match reloc.target() {
object::RelocationTarget::Symbol(index) => Ok(Some(RelocationOverride {
object::RelocationTarget::Symbol(_) => Ok(Some(RelocationOverride {
target: RelocationOverrideTarget::Keep,
addend: index.0 as u16 as i16 as i64,
addend: 0,
})),
target => Err(anyhow!("Unsupported IMAGE_REL_PPC_PAIR target {target:?}")),
},
+21 -20
View File
@@ -43,9 +43,6 @@ impl DiffObject {
let mut symbols = Vec::with_capacity(obj.symbols.len());
for (symbol_idx, symbol) in obj.symbols.iter().enumerate() {
let symbol_diff = &diff.symbols[symbol_idx];
if symbol.size == 0 || symbol.flags.contains(SymbolFlag::Ignored) {
continue;
}
symbols.push(DiffSymbol::new(obj, symbol_idx, symbol, symbol_diff, diff_config)?);
}
@@ -112,6 +109,7 @@ impl DiffSymbol {
address: symbol.address,
size: symbol.size,
flags: symbol_flags(&symbol.flags),
kind: DiffSymbolKind::from(symbol.kind) as i32,
// Diff information
target_symbol: symbol_diff.target_symbol.map(|i| i as u32),
match_percent: symbol_diff.match_percent,
@@ -121,24 +119,27 @@ impl DiffSymbol {
}
}
fn symbol_flags(flags: &obj::SymbolFlagSet) -> u32 {
let mut result = 0u32;
if flags.contains(SymbolFlag::Global) {
result |= DiffSymbolFlag::SymbolGlobal as u32;
impl From<obj::SymbolKind> for DiffSymbolKind {
fn from(value: obj::SymbolKind) -> Self {
match value {
obj::SymbolKind::Unknown => DiffSymbolKind::SymbolUnknown,
obj::SymbolKind::Function => DiffSymbolKind::SymbolFunction,
obj::SymbolKind::Object => DiffSymbolKind::SymbolObject,
obj::SymbolKind::Section => DiffSymbolKind::SymbolSection,
}
}
if flags.contains(SymbolFlag::Local) {
result |= DiffSymbolFlag::SymbolLocal as u32;
}
if flags.contains(SymbolFlag::Weak) {
result |= DiffSymbolFlag::SymbolWeak as u32;
}
if flags.contains(SymbolFlag::Common) {
result |= DiffSymbolFlag::SymbolCommon as u32;
}
if flags.contains(SymbolFlag::Hidden) {
result |= DiffSymbolFlag::SymbolHidden as u32;
}
result
}
fn symbol_flags(flags: &obj::SymbolFlagSet) -> Option<DiffSymbolFlags> {
Some(DiffSymbolFlags {
global: flags.contains(SymbolFlag::Global),
local: flags.contains(SymbolFlag::Local),
weak: flags.contains(SymbolFlag::Weak),
common: flags.contains(SymbolFlag::Common),
hidden: flags.contains(SymbolFlag::Hidden),
ignored: flags.contains(SymbolFlag::Ignored),
size_inferred: flags.contains(SymbolFlag::SizeInferred),
})
}
impl DiffInstructionRow {
+10 -1
View File
@@ -5,6 +5,7 @@ use typed_path::{Utf8PlatformPathBuf, Utf8UnixPathBuf};
use crate::{
build::{BuildConfig, BuildStatus, run_make},
diff::Demangler,
jobs::{Job, JobContext, JobResult, JobState, start_job, update_status},
};
@@ -21,6 +22,7 @@ pub struct CreateScratchConfig {
pub function_name: String,
pub target_obj: Utf8PlatformPathBuf,
pub preset_id: Option<u32>,
pub demangler: Demangler,
}
#[derive(Default, Debug, Clone)]
@@ -74,7 +76,14 @@ fn run_create_scratch(
.text("diff_label", config.function_name.clone())
.text("diff_flags", diff_flags)
.text("context", context.unwrap_or_default())
.text("source_code", "// Move related code from Context tab to here");
.text("source_code", "// Move related code from Context tab to here")
.text(
"name",
config
.demangler
.demangle(&config.function_name)
.unwrap_or_else(|| config.function_name.clone()),
);
if let Some(preset) = config.preset_id {
form = form.text("preset", preset.to_string());
}
+27 -2
View File
@@ -40,6 +40,7 @@ fn map_section_kind(section: &object::Section) -> SectionKind {
/// e.g. symbol$1234 and symbol$2345 will both be replaced with symbol$0000 internally.
fn get_normalized_symbol_name(name: &str) -> Option<String> {
const DUMMY_UNIQUE_ID: &str = "0000";
const DUMMY_UNIQUE_MSVC_ID: &str = "00000000";
if let Some((prefix, suffix)) = name.split_once("@class$")
&& let Some(idx) = suffix.chars().position(|c| !c.is_numeric())
&& idx > 0
@@ -59,6 +60,26 @@ fn get_normalized_symbol_name(name: &str) -> Option<String> {
{
// Match GCC symbol.1234 against symbol.2345
Some(format!("{prefix}.{DUMMY_UNIQUE_ID}"))
} else if name.starts_with('?') {
// Match MSVC anonymous class symbol names, ignoring the unique ID.
// e.g. ?CheckContextOr@?A0x24773155@@YA_NPBVDataArray@@@Z
// and: ?CheckContextOr@?A0xddf6240c@@YA_NPBVDataArray@@@Z
let mut name_str = String::from(name);
let anon_indices: Vec<usize> = name_str.match_indices("?A0x").map(|(idx, _)| idx).collect();
if !anon_indices.is_empty() {
for idx in anon_indices {
// the str sequence we're looking for is: ?A0xXXXXXXXX@@
if u32::from_str_radix(&name_str[idx + 4..idx + 12], 16).is_ok()
&& &name_str[idx + 12..idx + 14] == "@@"
{
// if the two above checks passed, we're good to replace the hash
name_str.replace_range(idx + 4..idx + 12, DUMMY_UNIQUE_MSVC_ID);
}
}
Some(name_str)
} else {
None
}
} else {
None
}
@@ -874,8 +895,12 @@ fn combine_sections(
let mut data_sections = BTreeMap::<String, Vec<usize>>::new();
let mut text_sections = BTreeMap::<String, Vec<usize>>::new();
for (i, section) in sections.iter().enumerate() {
let base_name =
if let Some(i) = section.name.rfind('$') { &section.name[..i] } else { &section.name };
let base_name = section
.name
.get(1..)
.and_then(|s| s.rfind(['$', '.']))
.and_then(|i| section.name.get(..i + 1))
.unwrap_or(&section.name);
match section.kind {
SectionKind::Data | SectionKind::Bss => {
data_sections.entry(base_name.to_string()).or_default().push(i);
+1
View File
@@ -75,6 +75,7 @@ fn create_scratch_config(
function_name,
target_obj: target_path.clone(),
preset_id: scratch_config.preset_id,
demangler: state.effective_diff_config().demangler,
})
}
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "objdiff-wasm",
"version": "3.6.0",
"version": "3.7.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "objdiff-wasm",
"version": "3.6.0",
"version": "3.7.0",
"license": "MIT OR Apache-2.0",
"devDependencies": {
"@biomejs/biome": "^1.9.3",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "objdiff-wasm",
"version": "3.6.0",
"version": "3.7.0",
"description": "A local diffing tool for decompilation projects.",
"author": {
"name": "Luke Street",