mirror of
https://github.com/encounter/objdiff.git
synced 2026-07-10 12:18:36 -07:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f7efe5fdff | |||
| 0692deac59 | |||
| c3e3d175c5 | |||
| c45f4bbc99 | |||
| b0c5431ac5 | |||
| 9ab246367b | |||
| dcafe51eda |
Generated
+308
-314
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -13,7 +13,7 @@ strip = "debuginfo"
|
||||
codegen-units = 1
|
||||
|
||||
[workspace.package]
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
+75
-30
@@ -12,7 +12,7 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use argp::FromArgs;
|
||||
use crossterm::{
|
||||
event,
|
||||
@@ -27,9 +27,11 @@ use objdiff_core::{
|
||||
watcher::{create_watcher, Watcher},
|
||||
BuildConfig,
|
||||
},
|
||||
config::{build_globset, default_watch_patterns, ProjectConfig, ProjectObject},
|
||||
config::{build_globset, ProjectConfig, ProjectObject},
|
||||
diff,
|
||||
diff::ObjDiff,
|
||||
diff::{
|
||||
ConfigEnum, ConfigPropertyId, ConfigPropertyKind, DiffObjConfig, MappingConfig, ObjDiff,
|
||||
},
|
||||
jobs::{
|
||||
objdiff::{start_build, ObjDiffConfig},
|
||||
Job, JobQueue, JobResult,
|
||||
@@ -63,9 +65,6 @@ pub struct Args {
|
||||
#[argp(option, short = 'u')]
|
||||
/// Unit name within project
|
||||
unit: Option<String>,
|
||||
#[argp(switch, short = 'x')]
|
||||
/// Relax relocation diffs
|
||||
relax_reloc_diffs: bool,
|
||||
#[argp(option, short = 'o')]
|
||||
/// Output file (one-shot mode) ("-" for stdout)
|
||||
output: Option<PathBuf>,
|
||||
@@ -75,6 +74,18 @@ pub struct Args {
|
||||
#[argp(positional)]
|
||||
/// Function symbol to diff
|
||||
symbol: Option<String>,
|
||||
#[argp(option, short = 'c')]
|
||||
/// Configuration property (key=value)
|
||||
config: Vec<String>,
|
||||
#[argp(option, short = 'm')]
|
||||
/// Symbol mapping (target=base)
|
||||
mapping: Vec<String>,
|
||||
#[argp(option)]
|
||||
/// Left symbol name for selection
|
||||
selecting_left: Option<String>,
|
||||
#[argp(option)]
|
||||
/// Right symbol name for selection
|
||||
selecting_right: Option<String>,
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
@@ -84,7 +95,9 @@ pub fn run(args: Args) -> Result<()> {
|
||||
&args.project,
|
||||
&args.unit,
|
||||
) {
|
||||
(Some(t), Some(b), None, None) => (Some(t.clone()), Some(b.clone()), None),
|
||||
(Some(_), Some(_), None, None)
|
||||
| (Some(_), None, None, None)
|
||||
| (None, Some(_), None, None) => (args.target.clone(), args.base.clone(), None),
|
||||
(None, None, p, u) => {
|
||||
let project = match p {
|
||||
Some(project) => project.clone(),
|
||||
@@ -193,6 +206,43 @@ pub fn run(args: Args) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_config_from_args(args: &Args) -> Result<(DiffObjConfig, MappingConfig)> {
|
||||
let mut diff_config = DiffObjConfig::default();
|
||||
for config in &args.config {
|
||||
let (key, value) = config.split_once('=').context("--config expects \"key=value\"")?;
|
||||
let property_id = ConfigPropertyId::from_str(key)
|
||||
.map_err(|()| anyhow!("Invalid configuration property: {}", key))?;
|
||||
diff_config.set_property_value_str(property_id, value).map_err(|()| {
|
||||
let mut options = String::new();
|
||||
match property_id.kind() {
|
||||
ConfigPropertyKind::Boolean => {
|
||||
options = "true, false".to_string();
|
||||
}
|
||||
ConfigPropertyKind::Choice(variants) => {
|
||||
for (i, variant) in variants.iter().enumerate() {
|
||||
if i > 0 {
|
||||
options.push_str(", ");
|
||||
}
|
||||
options.push_str(variant.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
anyhow!("Invalid value for {}. Expected one of: {}", property_id.name(), options)
|
||||
})?;
|
||||
}
|
||||
let mut mapping_config = MappingConfig {
|
||||
mappings: Default::default(),
|
||||
selecting_left: args.selecting_left.clone(),
|
||||
selecting_right: args.selecting_right.clone(),
|
||||
};
|
||||
for mapping in &args.mapping {
|
||||
let (target, base) =
|
||||
mapping.split_once('=').context("--mapping expects \"target=base\"")?;
|
||||
mapping_config.mappings.insert(target.to_string(), base.to_string());
|
||||
}
|
||||
Ok((diff_config, mapping_config))
|
||||
}
|
||||
|
||||
fn run_oneshot(
|
||||
args: &Args,
|
||||
output: &Path,
|
||||
@@ -200,17 +250,19 @@ fn run_oneshot(
|
||||
base_path: Option<&Path>,
|
||||
) -> Result<()> {
|
||||
let output_format = OutputFormat::from_option(args.format.as_deref())?;
|
||||
let config = diff::DiffObjConfig {
|
||||
relax_reloc_diffs: args.relax_reloc_diffs,
|
||||
..Default::default() // TODO
|
||||
};
|
||||
let (diff_config, mapping_config) = build_config_from_args(args)?;
|
||||
let target = target_path
|
||||
.map(|p| obj::read::read(p, &config).with_context(|| format!("Loading {}", p.display())))
|
||||
.map(|p| {
|
||||
obj::read::read(p, &diff_config).with_context(|| format!("Loading {}", p.display()))
|
||||
})
|
||||
.transpose()?;
|
||||
let base = base_path
|
||||
.map(|p| obj::read::read(p, &config).with_context(|| format!("Loading {}", p.display())))
|
||||
.map(|p| {
|
||||
obj::read::read(p, &diff_config).with_context(|| format!("Loading {}", p.display()))
|
||||
})
|
||||
.transpose()?;
|
||||
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)?;
|
||||
let result =
|
||||
diff::diff_objs(&diff_config, &mapping_config, target.as_ref(), base.as_ref(), None)?;
|
||||
let left = target.as_ref().and_then(|o| result.left.as_ref().map(|d| (o, d)));
|
||||
let right = base.as_ref().and_then(|o| result.right.as_ref().map(|d| (o, d)));
|
||||
write_output(&DiffResult::new(left, right), Some(output), output_format)?;
|
||||
@@ -229,9 +281,10 @@ pub struct AppState {
|
||||
pub prev_obj: Option<(ObjInfo, ObjDiff)>,
|
||||
pub reload_time: Option<time::OffsetDateTime>,
|
||||
pub time_format: Vec<time::format_description::FormatItem<'static>>,
|
||||
pub relax_reloc_diffs: bool,
|
||||
pub watcher: Option<Watcher>,
|
||||
pub modified: Arc<AtomicBool>,
|
||||
pub diff_obj_config: DiffObjConfig,
|
||||
pub mapping_config: MappingConfig,
|
||||
}
|
||||
|
||||
fn create_objdiff_config(state: &AppState) -> ObjDiffConfig {
|
||||
@@ -257,13 +310,8 @@ fn create_objdiff_config(state: &AppState) -> ObjDiffConfig {
|
||||
.is_some_and(|p| p.build_target.unwrap_or(false)),
|
||||
target_path: state.target_path.clone(),
|
||||
base_path: state.base_path.clone(),
|
||||
diff_obj_config: diff::DiffObjConfig {
|
||||
relax_reloc_diffs: state.relax_reloc_diffs,
|
||||
..Default::default() // TODO
|
||||
},
|
||||
symbol_mappings: Default::default(),
|
||||
selecting_left: None,
|
||||
selecting_right: None,
|
||||
diff_obj_config: state.diff_obj_config.clone(),
|
||||
mapping_config: state.mapping_config.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,6 +362,7 @@ fn run_interactive(
|
||||
let Some(symbol_name) = &args.symbol else { bail!("Interactive mode requires a symbol name") };
|
||||
let time_format = time::format_description::parse_borrowed::<2>("[hour]:[minute]:[second]")
|
||||
.context("Failed to parse time format")?;
|
||||
let (diff_obj_config, mapping_config) = build_config_from_args(&args)?;
|
||||
let mut state = AppState {
|
||||
jobs: Default::default(),
|
||||
waker: Default::default(),
|
||||
@@ -326,17 +375,13 @@ fn run_interactive(
|
||||
prev_obj: None,
|
||||
reload_time: None,
|
||||
time_format,
|
||||
relax_reloc_diffs: args.relax_reloc_diffs,
|
||||
watcher: None,
|
||||
modified: Default::default(),
|
||||
diff_obj_config,
|
||||
mapping_config,
|
||||
};
|
||||
if let Some(project_dir) = &state.project_dir {
|
||||
let watch_patterns = state
|
||||
.project_config
|
||||
.as_ref()
|
||||
.and_then(|c| c.watch_patterns.as_ref())
|
||||
.cloned()
|
||||
.unwrap_or_else(default_watch_patterns);
|
||||
if let (Some(project_dir), Some(project_config)) = (&state.project_dir, &state.project_config) {
|
||||
let watch_patterns = project_config.build_watch_patterns()?;
|
||||
state.watcher = Some(create_watcher(
|
||||
state.modified.clone(),
|
||||
project_dir,
|
||||
|
||||
@@ -169,22 +169,26 @@ fn report_object(
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() };
|
||||
let diff_config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() };
|
||||
let mapping_config = diff::MappingConfig::default();
|
||||
let target = object
|
||||
.target_path
|
||||
.as_ref()
|
||||
.map(|p| {
|
||||
obj::read::read(p, &config).with_context(|| format!("Failed to open {}", p.display()))
|
||||
obj::read::read(p, &diff_config)
|
||||
.with_context(|| format!("Failed to open {}", p.display()))
|
||||
})
|
||||
.transpose()?;
|
||||
let base = object
|
||||
.base_path
|
||||
.as_ref()
|
||||
.map(|p| {
|
||||
obj::read::read(p, &config).with_context(|| format!("Failed to open {}", p.display()))
|
||||
obj::read::read(p, &diff_config)
|
||||
.with_context(|| format!("Failed to open {}", p.display()))
|
||||
})
|
||||
.transpose()?;
|
||||
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)?;
|
||||
let result =
|
||||
diff::diff_objs(&diff_config, &mapping_config, target.as_ref(), base.as_ref(), None)?;
|
||||
|
||||
let metadata = ReportUnitMetadata {
|
||||
complete: object.complete(),
|
||||
|
||||
@@ -370,7 +370,8 @@ impl UiView for FunctionDiffUi {
|
||||
}
|
||||
// Toggle relax relocation diffs
|
||||
KeyCode::Char('x') => {
|
||||
state.relax_reloc_diffs = !state.relax_reloc_diffs;
|
||||
state.diff_obj_config.relax_reloc_diffs =
|
||||
!state.diff_obj_config.relax_reloc_diffs;
|
||||
result.redraw = true;
|
||||
return EventControlFlow::Reload;
|
||||
}
|
||||
|
||||
+109
-16
@@ -16,18 +16,104 @@ documentation = "https://docs.rs/objdiff-core"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
all = ["config", "dwarf", "mips", "ppc", "x86", "arm", "arm64", "bindings", "build"]
|
||||
any-arch = ["config", "dep:bimap", "dep:strum", "dep:similar", "dep:flagset", "dep:log", "dep:memmap2", "dep:byteorder", "dep:num-traits"] # Implicit, used to check if any arch is enabled
|
||||
bindings = ["dep:serde_json", "dep:prost", "dep:pbjson", "dep:serde", "dep:prost-build", "dep:pbjson-build"]
|
||||
build = ["dep:shell-escape", "dep:path-slash", "dep:winapi", "dep:notify", "dep:notify-debouncer-full", "dep:reqwest", "dep:self_update", "dep:tempfile", "dep:time"]
|
||||
config = ["dep:bimap", "dep:globset", "dep:semver", "dep:serde_json", "dep:serde_yaml", "dep:serde", "dep:filetime"]
|
||||
all = [
|
||||
# Features
|
||||
"bindings",
|
||||
"build",
|
||||
"config",
|
||||
"dwarf",
|
||||
# Architectures
|
||||
"mips",
|
||||
"ppc",
|
||||
"x86",
|
||||
"arm",
|
||||
"arm64",
|
||||
]
|
||||
# Implicit, used to check if any arch is enabled
|
||||
any-arch = [
|
||||
"config",
|
||||
"dep:bimap",
|
||||
"dep:byteorder",
|
||||
"dep:flagset",
|
||||
"dep:heck",
|
||||
"dep:log",
|
||||
"dep:memmap2",
|
||||
"dep:num-traits",
|
||||
"dep:prettyplease",
|
||||
"dep:proc-macro2",
|
||||
"dep:quote",
|
||||
"dep:serde",
|
||||
"dep:serde_json",
|
||||
"dep:similar",
|
||||
"dep:strum",
|
||||
"dep:syn",
|
||||
]
|
||||
bindings = [
|
||||
"dep:pbjson",
|
||||
"dep:pbjson-build",
|
||||
"dep:prost",
|
||||
"dep:prost-build",
|
||||
"dep:serde",
|
||||
"dep:serde_json",
|
||||
]
|
||||
build = [
|
||||
"dep:notify",
|
||||
"dep:notify-debouncer-full",
|
||||
"dep:path-slash",
|
||||
"dep:reqwest",
|
||||
"dep:self_update",
|
||||
"dep:shell-escape",
|
||||
"dep:tempfile",
|
||||
"dep:time",
|
||||
"dep:winapi",
|
||||
]
|
||||
config = [
|
||||
"dep:bimap",
|
||||
"dep:filetime",
|
||||
"dep:globset",
|
||||
"dep:semver",
|
||||
"dep:serde",
|
||||
"dep:serde_json",
|
||||
"dep:serde_yaml",
|
||||
]
|
||||
dwarf = ["dep:gimli"]
|
||||
mips = ["any-arch", "dep:rabbitizer"]
|
||||
ppc = ["any-arch", "dep:cwdemangle", "dep:cwextab", "dep:ppc750cl"]
|
||||
x86 = ["any-arch", "dep:cpp_demangle", "dep:iced-x86", "dep:msvc-demangler"]
|
||||
arm = ["any-arch", "dep:cpp_demangle", "dep:unarm", "dep:arm-attr"]
|
||||
arm64 = ["any-arch", "dep:cpp_demangle", "dep:yaxpeax-arch", "dep:yaxpeax-arm"]
|
||||
wasm = ["bindings", "any-arch", "dep:console_error_panic_hook", "dep:console_log", "dep:wasm-bindgen", "dep:tsify-next", "dep:log"]
|
||||
mips = [
|
||||
"any-arch",
|
||||
"dep:rabbitizer",
|
||||
]
|
||||
ppc = [
|
||||
"any-arch",
|
||||
"dep:cwdemangle",
|
||||
"dep:cwextab",
|
||||
"dep:ppc750cl",
|
||||
]
|
||||
x86 = [
|
||||
"any-arch",
|
||||
"dep:cpp_demangle",
|
||||
"dep:iced-x86",
|
||||
"dep:msvc-demangler",
|
||||
]
|
||||
arm = [
|
||||
"any-arch",
|
||||
"dep:arm-attr",
|
||||
"dep:cpp_demangle",
|
||||
"dep:unarm",
|
||||
]
|
||||
arm64 = [
|
||||
"any-arch",
|
||||
"dep:cpp_demangle",
|
||||
"dep:yaxpeax-arch",
|
||||
"dep:yaxpeax-arm",
|
||||
]
|
||||
wasm = [
|
||||
"any-arch",
|
||||
"bindings",
|
||||
"dep:console_error_panic_hook",
|
||||
"dep:console_log",
|
||||
"dep:log",
|
||||
"dep:tsify-next",
|
||||
"dep:wasm-bindgen",
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["all"]
|
||||
@@ -83,8 +169,8 @@ yaxpeax-arch = { version = "0.3", default-features = false, features = ["std"],
|
||||
yaxpeax-arm = { version = "0.3", default-features = false, features = ["std"], optional = true }
|
||||
|
||||
# build
|
||||
notify = { git = "https://github.com/notify-rs/notify", rev = "128bf6230c03d39dbb7f301ff7b20e594e34c3a2", optional = true }
|
||||
notify-debouncer-full = { git = "https://github.com/notify-rs/notify", rev = "128bf6230c03d39dbb7f301ff7b20e594e34c3a2", optional = true }
|
||||
notify = { git = "https://github.com/notify-rs/notify", rev = "128bf6230c03d39dbb7f301ff7b20e594e34c3a2", version = "6.1.1", optional = true }
|
||||
notify-debouncer-full = { git = "https://github.com/notify-rs/notify", rev = "128bf6230c03d39dbb7f301ff7b20e594e34c3a2", version = "0.4.0", optional = true }
|
||||
shell-escape = { version = "0.1", optional = true }
|
||||
tempfile = { version = "3.14", optional = true }
|
||||
time = { version = "0.3", optional = true }
|
||||
@@ -96,13 +182,20 @@ winapi = { version = "0.3", optional = true }
|
||||
# For Linux static binaries, use rustls
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "multipart", "rustls-tls"], optional = true }
|
||||
self_update = { version = "0.41", default-features = false, features = ["rustls"], optional = true }
|
||||
self_update = { version = "0.42", default-features = false, features = ["rustls"], optional = true }
|
||||
|
||||
# For all other platforms, use native TLS
|
||||
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
||||
reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "multipart", "default-tls"], optional = true }
|
||||
self_update = { version = "0.41", optional = true }
|
||||
self_update = { version = "0.42", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
prost-build = { version = "0.13", optional = true }
|
||||
heck = { version = "0.5", optional = true }
|
||||
pbjson-build = { version = "0.7", optional = true }
|
||||
prettyplease = { version = "0.2", optional = true }
|
||||
proc-macro2 = { version = "1.0", optional = true }
|
||||
prost-build = { version = "0.13", optional = true }
|
||||
quote = { version = "1.0", optional = true }
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
syn = { version = "2.0", optional = true }
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
#[cfg(feature = "any-arch")]
|
||||
mod config_gen;
|
||||
|
||||
fn main() {
|
||||
#[cfg(feature = "bindings")]
|
||||
compile_protos();
|
||||
#[cfg(feature = "any-arch")]
|
||||
config_gen::generate_diff_config();
|
||||
}
|
||||
|
||||
#[cfg(feature = "bindings")]
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
{
|
||||
"properties": [
|
||||
{
|
||||
"id": "relaxRelocDiffs",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"name": "Relax relocation diffs",
|
||||
"description": "Ignores differences in relocation targets. (Address, name, etc)"
|
||||
},
|
||||
{
|
||||
"id": "spaceBetweenArgs",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"name": "Space between args",
|
||||
"description": "Adds a space between arguments in the diff output."
|
||||
},
|
||||
{
|
||||
"id": "combineDataSections",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"name": "Combine data sections",
|
||||
"description": "Combines data sections with equal names."
|
||||
},
|
||||
{
|
||||
"id": "arm.archVersion",
|
||||
"type": "choice",
|
||||
"default": "auto",
|
||||
"name": "Architecture version",
|
||||
"description": "ARM architecture version to use for disassembly.",
|
||||
"items": [
|
||||
{
|
||||
"value": "auto",
|
||||
"name": "Auto"
|
||||
},
|
||||
{
|
||||
"value": "v4t",
|
||||
"name": "ARMv4T (GBA)"
|
||||
},
|
||||
{
|
||||
"value": "v5te",
|
||||
"name": "ARMv5TE (DS)"
|
||||
},
|
||||
{
|
||||
"value": "v6k",
|
||||
"name": "ARMv6K (3DS)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "arm.unifiedSyntax",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"name": "Unified syntax",
|
||||
"description": "Disassemble as unified assembly language (UAL)."
|
||||
},
|
||||
{
|
||||
"id": "arm.avRegisters",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"name": "Use A/V registers",
|
||||
"description": "Display R0-R3 as A1-A4 and R4-R11 as V1-V8."
|
||||
},
|
||||
{
|
||||
"id": "arm.r9Usage",
|
||||
"type": "choice",
|
||||
"default": "generalPurpose",
|
||||
"name": "Display R9 as",
|
||||
"items": [
|
||||
{
|
||||
"value": "generalPurpose",
|
||||
"name": "R9 or V6",
|
||||
"description": "Use R9 as a general-purpose register."
|
||||
},
|
||||
{
|
||||
"value": "sb",
|
||||
"name": "SB (static base)",
|
||||
"description": "Used for position-independent data (PID)."
|
||||
},
|
||||
{
|
||||
"value": "tr",
|
||||
"name": "TR (TLS register)",
|
||||
"description": "Used for thread-local storage."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "arm.slUsage",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"name": "Display R10 as SL",
|
||||
"description": "Used for explicit stack limits."
|
||||
},
|
||||
{
|
||||
"id": "arm.fpUsage",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"name": "Display R11 as FP",
|
||||
"description": "Used for frame pointers."
|
||||
},
|
||||
{
|
||||
"id": "arm.ipUsage",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"name": "Display R12 as IP",
|
||||
"description": "Used for interworking and long branches."
|
||||
},
|
||||
{
|
||||
"id": "mips.abi",
|
||||
"type": "choice",
|
||||
"default": "auto",
|
||||
"name": "ABI",
|
||||
"description": "MIPS ABI to use for disassembly.",
|
||||
"items": [
|
||||
{
|
||||
"value": "auto",
|
||||
"name": "Auto"
|
||||
},
|
||||
{
|
||||
"value": "o32",
|
||||
"name": "O32"
|
||||
},
|
||||
{
|
||||
"value": "n32",
|
||||
"name": "N32"
|
||||
},
|
||||
{
|
||||
"value": "n64",
|
||||
"name": "N64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mips.instrCategory",
|
||||
"type": "choice",
|
||||
"default": "auto",
|
||||
"name": "Instruction category",
|
||||
"description": "MIPS instruction category to use for disassembly.",
|
||||
"items": [
|
||||
{
|
||||
"value": "auto",
|
||||
"name": "Auto"
|
||||
},
|
||||
{
|
||||
"value": "cpu",
|
||||
"name": "CPU"
|
||||
},
|
||||
{
|
||||
"value": "rsp",
|
||||
"name": "RSP (N64)"
|
||||
},
|
||||
{
|
||||
"value": "r3000gte",
|
||||
"name": "R3000 GTE (PS1)"
|
||||
},
|
||||
{
|
||||
"value": "r4000allegrex",
|
||||
"name": "R4000 ALLEGREX (PSP)"
|
||||
},
|
||||
{
|
||||
"value": "r5900",
|
||||
"name": "R5900 EE (PS2)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "x86.formatter",
|
||||
"type": "choice",
|
||||
"default": "intel",
|
||||
"name": "Format",
|
||||
"description": "x86 disassembly syntax.",
|
||||
"items": [
|
||||
{
|
||||
"value": "intel",
|
||||
"name": "Intel"
|
||||
},
|
||||
{
|
||||
"value": "gas",
|
||||
"name": "AT&T"
|
||||
},
|
||||
{
|
||||
"value": "nasm",
|
||||
"name": "NASM"
|
||||
},
|
||||
{
|
||||
"value": "masm",
|
||||
"name": "MASM"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": "general",
|
||||
"name": "General",
|
||||
"properties": [
|
||||
"relaxRelocDiffs",
|
||||
"spaceBetweenArgs",
|
||||
"combineDataSections"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "arm",
|
||||
"name": "ARM",
|
||||
"properties": [
|
||||
"arm.archVersion",
|
||||
"arm.unifiedSyntax",
|
||||
"arm.avRegisters",
|
||||
"arm.r9Usage",
|
||||
"arm.slUsage",
|
||||
"arm.fpUsage",
|
||||
"arm.ipUsage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mips",
|
||||
"name": "MIPS",
|
||||
"properties": [
|
||||
"mips.abi",
|
||||
"mips.instrCategory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "x86",
|
||||
"name": "x86",
|
||||
"properties": [
|
||||
"x86.formatter"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,491 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct ConfigSchema {
|
||||
pub properties: Vec<ConfigProperty>,
|
||||
pub groups: Vec<ConfigGroup>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum ConfigProperty {
|
||||
#[serde(rename = "boolean")]
|
||||
Boolean(ConfigPropertyBoolean),
|
||||
#[serde(rename = "choice")]
|
||||
Choice(ConfigPropertyChoice),
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct ConfigPropertyBase {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct ConfigPropertyBoolean {
|
||||
#[serde(flatten)]
|
||||
pub base: ConfigPropertyBase,
|
||||
pub default: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct ConfigPropertyChoice {
|
||||
#[serde(flatten)]
|
||||
pub base: ConfigPropertyBase,
|
||||
pub default: String,
|
||||
pub items: Vec<ConfigPropertyChoiceItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct ConfigPropertyChoiceItem {
|
||||
pub value: String,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct ConfigGroup {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub properties: Vec<String>,
|
||||
}
|
||||
|
||||
fn build_doc(name: &str, description: Option<&str>) -> TokenStream {
|
||||
let mut doc = format!(" {}", name);
|
||||
let mut out = quote! { #[doc = #doc] };
|
||||
if let Some(description) = description {
|
||||
doc = format!(" {}", description);
|
||||
out.extend(quote! { #[doc = ""] });
|
||||
out.extend(quote! { #[doc = #doc] });
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub fn generate_diff_config() {
|
||||
let schema_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("config-schema.json");
|
||||
println!("cargo:rerun-if-changed={}", schema_path.display());
|
||||
let schema_file = File::open(schema_path).expect("Failed to open config schema file");
|
||||
let schema: ConfigSchema =
|
||||
serde_json::from_reader(schema_file).expect("Failed to parse config schema");
|
||||
|
||||
let mut enums = TokenStream::new();
|
||||
for property in &schema.properties {
|
||||
let ConfigProperty::Choice(choice) = property else {
|
||||
continue;
|
||||
};
|
||||
let enum_ident = format_ident!("{}", choice.base.id.to_upper_camel_case());
|
||||
let mut variants = TokenStream::new();
|
||||
let mut full_variants = TokenStream::new();
|
||||
let mut variant_info = TokenStream::new();
|
||||
let mut variant_to_str = TokenStream::new();
|
||||
let mut variant_to_name = TokenStream::new();
|
||||
let mut variant_to_description = TokenStream::new();
|
||||
let mut variant_from_str = TokenStream::new();
|
||||
for item in &choice.items {
|
||||
let variant_name = item.value.to_upper_camel_case();
|
||||
let variant_ident = format_ident!("{}", variant_name);
|
||||
let is_default = item.value == choice.default;
|
||||
variants.extend(build_doc(&item.name, item.description.as_deref()));
|
||||
if is_default {
|
||||
variants.extend(quote! { #[default] });
|
||||
}
|
||||
let value = &item.value;
|
||||
variants.extend(quote! {
|
||||
#[serde(rename = #value, alias = #variant_name)]
|
||||
#variant_ident,
|
||||
});
|
||||
full_variants.extend(quote! { #enum_ident::#variant_ident, });
|
||||
variant_to_str.extend(quote! { #enum_ident::#variant_ident => #value, });
|
||||
let name = &item.name;
|
||||
variant_to_name.extend(quote! { #enum_ident::#variant_ident => #name, });
|
||||
if let Some(description) = &item.description {
|
||||
variant_to_description.extend(quote! {
|
||||
#enum_ident::#variant_ident => Some(#description),
|
||||
});
|
||||
} else {
|
||||
variant_to_description.extend(quote! {
|
||||
#enum_ident::#variant_ident => None,
|
||||
});
|
||||
}
|
||||
let description = if let Some(description) = &item.description {
|
||||
quote! { Some(#description) }
|
||||
} else {
|
||||
quote! { None }
|
||||
};
|
||||
variant_info.extend(quote! {
|
||||
ConfigEnumVariantInfo {
|
||||
value: #value,
|
||||
name: #name,
|
||||
description: #description,
|
||||
is_default: #is_default,
|
||||
},
|
||||
});
|
||||
variant_from_str.extend(quote! {
|
||||
if s.eq_ignore_ascii_case(#value) { return Ok(#enum_ident::#variant_ident); }
|
||||
});
|
||||
}
|
||||
enums.extend(quote! {
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify), tsify(from_wasm_abi))]
|
||||
pub enum #enum_ident {
|
||||
#variants
|
||||
}
|
||||
impl ConfigEnum for #enum_ident {
|
||||
#[inline]
|
||||
fn variants() -> &'static [Self] {
|
||||
static VARIANTS: &[#enum_ident] = &[#full_variants];
|
||||
VARIANTS
|
||||
}
|
||||
#[inline]
|
||||
fn variant_info() -> &'static [ConfigEnumVariantInfo] {
|
||||
static VARIANT_INFO: &[ConfigEnumVariantInfo] = &[
|
||||
#variant_info
|
||||
];
|
||||
VARIANT_INFO
|
||||
}
|
||||
fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
#variant_to_str
|
||||
}
|
||||
}
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
#variant_to_name
|
||||
}
|
||||
}
|
||||
fn description(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
#variant_to_description
|
||||
}
|
||||
}
|
||||
}
|
||||
impl std::str::FromStr for #enum_ident {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#variant_from_str
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut groups = TokenStream::new();
|
||||
let mut group_idents = Vec::new();
|
||||
for group in &schema.groups {
|
||||
let ident = format_ident!("CONFIG_GROUP_{}", group.id.to_shouty_snake_case());
|
||||
let id = &group.id;
|
||||
let name = &group.name;
|
||||
let description = if let Some(description) = &group.description {
|
||||
quote! { Some(#description) }
|
||||
} else {
|
||||
quote! { None }
|
||||
};
|
||||
let properties =
|
||||
group.properties.iter().map(|p| format_ident!("{}", p.to_upper_camel_case()));
|
||||
groups.extend(quote! {
|
||||
ConfigPropertyGroup {
|
||||
id: #id,
|
||||
name: #name,
|
||||
description: #description,
|
||||
properties: &[#(ConfigPropertyId::#properties,)*],
|
||||
},
|
||||
});
|
||||
group_idents.push(ident);
|
||||
}
|
||||
|
||||
let mut property_idents = Vec::new();
|
||||
let mut property_variants = TokenStream::new();
|
||||
let mut variant_info = TokenStream::new();
|
||||
let mut config_property_id_to_str = TokenStream::new();
|
||||
let mut config_property_id_to_name = TokenStream::new();
|
||||
let mut config_property_id_to_description = TokenStream::new();
|
||||
let mut config_property_id_to_kind = TokenStream::new();
|
||||
let mut property_fields = TokenStream::new();
|
||||
let mut default_fields = TokenStream::new();
|
||||
let mut get_property_value_variants = TokenStream::new();
|
||||
let mut set_property_value_variants = TokenStream::new();
|
||||
let mut set_property_value_str_variants = TokenStream::new();
|
||||
let mut config_property_id_from_str = TokenStream::new();
|
||||
for property in &schema.properties {
|
||||
let base = match property {
|
||||
ConfigProperty::Boolean(b) => &b.base,
|
||||
ConfigProperty::Choice(c) => &c.base,
|
||||
};
|
||||
let id = &base.id;
|
||||
let enum_ident = format_ident!("{}", id.to_upper_camel_case());
|
||||
property_idents.push(enum_ident.clone());
|
||||
config_property_id_to_str.extend(quote! { Self::#enum_ident => #id, });
|
||||
let name = &base.name;
|
||||
config_property_id_to_name.extend(quote! { Self::#enum_ident => #name, });
|
||||
if let Some(description) = &base.description {
|
||||
config_property_id_to_description.extend(quote! {
|
||||
Self::#enum_ident => Some(#description),
|
||||
});
|
||||
} else {
|
||||
config_property_id_to_description.extend(quote! {
|
||||
Self::#enum_ident => None,
|
||||
});
|
||||
}
|
||||
let doc = build_doc(name, base.description.as_deref());
|
||||
property_variants.extend(quote! { #doc #enum_ident, });
|
||||
property_fields.extend(doc);
|
||||
let field_ident = format_ident!("{}", id.to_snake_case());
|
||||
match property {
|
||||
ConfigProperty::Boolean(b) => {
|
||||
let default = b.default;
|
||||
if default {
|
||||
property_fields.extend(quote! {
|
||||
#[serde(default = "default_true")]
|
||||
});
|
||||
}
|
||||
property_fields.extend(quote! {
|
||||
pub #field_ident: bool,
|
||||
});
|
||||
default_fields.extend(quote! {
|
||||
#field_ident: #default,
|
||||
});
|
||||
}
|
||||
ConfigProperty::Choice(_) => {
|
||||
property_fields.extend(quote! {
|
||||
pub #field_ident: #enum_ident,
|
||||
});
|
||||
default_fields.extend(quote! {
|
||||
#field_ident: #enum_ident::default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
let property_value = match property {
|
||||
ConfigProperty::Boolean(_) => {
|
||||
quote! { ConfigPropertyValue::Boolean(self.#field_ident) }
|
||||
}
|
||||
ConfigProperty::Choice(_) => {
|
||||
quote! { ConfigPropertyValue::Choice(self.#field_ident.as_str()) }
|
||||
}
|
||||
};
|
||||
get_property_value_variants.extend(quote! {
|
||||
ConfigPropertyId::#enum_ident => #property_value,
|
||||
});
|
||||
match property {
|
||||
ConfigProperty::Boolean(_) => {
|
||||
set_property_value_variants.extend(quote! {
|
||||
ConfigPropertyId::#enum_ident => {
|
||||
if let ConfigPropertyValue::Boolean(value) = value {
|
||||
self.#field_ident = value;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
},
|
||||
});
|
||||
set_property_value_str_variants.extend(quote! {
|
||||
ConfigPropertyId::#enum_ident => {
|
||||
if let Ok(value) = value.parse() {
|
||||
self.#field_ident = value;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
ConfigProperty::Choice(_) => {
|
||||
set_property_value_variants.extend(quote! {
|
||||
ConfigPropertyId::#enum_ident => {
|
||||
if let ConfigPropertyValue::Choice(value) = value {
|
||||
if let Ok(value) = value.parse() {
|
||||
self.#field_ident = value;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
},
|
||||
});
|
||||
set_property_value_str_variants.extend(quote! {
|
||||
ConfigPropertyId::#enum_ident => {
|
||||
if let Ok(value) = value.parse() {
|
||||
self.#field_ident = value;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
let description = if let Some(description) = &base.description {
|
||||
quote! { Some(#description) }
|
||||
} else {
|
||||
quote! { None }
|
||||
};
|
||||
variant_info.extend(quote! {
|
||||
ConfigEnumVariantInfo {
|
||||
value: #id,
|
||||
name: #name,
|
||||
description: #description,
|
||||
is_default: false,
|
||||
},
|
||||
});
|
||||
match property {
|
||||
ConfigProperty::Boolean(_) => {
|
||||
config_property_id_to_kind.extend(quote! {
|
||||
Self::#enum_ident => ConfigPropertyKind::Boolean,
|
||||
});
|
||||
}
|
||||
ConfigProperty::Choice(_) => {
|
||||
config_property_id_to_kind.extend(quote! {
|
||||
Self::#enum_ident => ConfigPropertyKind::Choice(#enum_ident::variant_info()),
|
||||
});
|
||||
}
|
||||
}
|
||||
let snake_id = id.to_snake_case();
|
||||
config_property_id_from_str.extend(quote! {
|
||||
if s.eq_ignore_ascii_case(#id) || s.eq_ignore_ascii_case(#snake_id) {
|
||||
return Ok(Self::#enum_ident);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let tokens = quote! {
|
||||
pub trait ConfigEnum: Sized {
|
||||
fn variants() -> &'static [Self];
|
||||
fn variant_info() -> &'static [ConfigEnumVariantInfo];
|
||||
fn as_str(&self) -> &'static str;
|
||||
fn name(&self) -> &'static str;
|
||||
fn description(&self) -> Option<&'static str>;
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ConfigEnumVariantInfo {
|
||||
pub value: &'static str,
|
||||
pub name: &'static str,
|
||||
pub description: Option<&'static str>,
|
||||
pub is_default: bool,
|
||||
}
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum ConfigPropertyId {
|
||||
#property_variants
|
||||
}
|
||||
impl ConfigEnum for ConfigPropertyId {
|
||||
#[inline]
|
||||
fn variants() -> &'static [Self] {
|
||||
static VARIANTS: &[ConfigPropertyId] = &[#(ConfigPropertyId::#property_idents,)*];
|
||||
VARIANTS
|
||||
}
|
||||
#[inline]
|
||||
fn variant_info() -> &'static [ConfigEnumVariantInfo] {
|
||||
static VARIANT_INFO: &[ConfigEnumVariantInfo] = &[
|
||||
#variant_info
|
||||
];
|
||||
VARIANT_INFO
|
||||
}
|
||||
fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
#config_property_id_to_str
|
||||
}
|
||||
}
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
#config_property_id_to_name
|
||||
}
|
||||
}
|
||||
fn description(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
#config_property_id_to_description
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ConfigPropertyId {
|
||||
pub fn kind(&self) -> ConfigPropertyKind {
|
||||
match self {
|
||||
#config_property_id_to_kind
|
||||
}
|
||||
}
|
||||
}
|
||||
impl std::str::FromStr for ConfigPropertyId {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#config_property_id_from_str
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ConfigPropertyGroup {
|
||||
pub id: &'static str,
|
||||
pub name: &'static str,
|
||||
pub description: Option<&'static str>,
|
||||
pub properties: &'static [ConfigPropertyId],
|
||||
}
|
||||
pub static CONFIG_GROUPS: &[ConfigPropertyGroup] = &[#groups];
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum ConfigPropertyValue {
|
||||
Boolean(bool),
|
||||
Choice(&'static str),
|
||||
}
|
||||
impl ConfigPropertyValue {
|
||||
pub fn to_json(&self) -> serde_json::Value {
|
||||
match self {
|
||||
ConfigPropertyValue::Boolean(value) => serde_json::Value::Bool(*value),
|
||||
ConfigPropertyValue::Choice(value) => serde_json::Value::String(value.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ConfigPropertyKind {
|
||||
Boolean,
|
||||
Choice(&'static [ConfigEnumVariantInfo]),
|
||||
}
|
||||
#enums
|
||||
#[inline(always)]
|
||||
fn default_true() -> bool { true }
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify), tsify(from_wasm_abi))]
|
||||
#[serde(default)]
|
||||
pub struct DiffObjConfig {
|
||||
#property_fields
|
||||
}
|
||||
impl Default for DiffObjConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
#default_fields
|
||||
}
|
||||
}
|
||||
}
|
||||
impl DiffObjConfig {
|
||||
pub fn get_property_value(&self, id: ConfigPropertyId) -> ConfigPropertyValue {
|
||||
match id {
|
||||
#get_property_value_variants
|
||||
}
|
||||
}
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn set_property_value(&mut self, id: ConfigPropertyId, value: ConfigPropertyValue) -> Result<(), ()> {
|
||||
match id {
|
||||
#set_property_value_variants
|
||||
}
|
||||
}
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn set_property_value_str(&mut self, id: ConfigPropertyId, value: &str) -> Result<(), ()> {
|
||||
match id {
|
||||
#set_property_value_str_variants
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let file = syn::parse2(tokens).unwrap();
|
||||
let formatted = prettyplease::unparse(&file);
|
||||
std::fs::write(
|
||||
PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("config.gen.rs"),
|
||||
formatted,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
@@ -21,9 +21,9 @@ enum SymbolFlag {
|
||||
SYMBOL_NONE = 0;
|
||||
SYMBOL_GLOBAL = 1;
|
||||
SYMBOL_LOCAL = 2;
|
||||
SYMBOL_WEAK = 3;
|
||||
SYMBOL_COMMON = 4;
|
||||
SYMBOL_HIDDEN = 5;
|
||||
SYMBOL_WEAK = 4;
|
||||
SYMBOL_COMMON = 8;
|
||||
SYMBOL_HIDDEN = 16;
|
||||
}
|
||||
|
||||
// A single parsed instruction
|
||||
@@ -122,10 +122,17 @@ message InstructionBranchTo {
|
||||
uint32 branch_index = 2;
|
||||
}
|
||||
|
||||
message FunctionDiff {
|
||||
message SymbolRef {
|
||||
optional uint32 section_index = 1;
|
||||
uint32 symbol_index = 2;
|
||||
}
|
||||
|
||||
message SymbolDiff {
|
||||
Symbol symbol = 1;
|
||||
repeated InstructionDiff instructions = 2;
|
||||
optional float match_percent = 3;
|
||||
// The symbol ref in the _other_ object that this symbol was diffed against
|
||||
optional SymbolRef target = 5;
|
||||
}
|
||||
|
||||
message DataDiff {
|
||||
@@ -140,7 +147,7 @@ message SectionDiff {
|
||||
SectionKind kind = 2;
|
||||
uint64 size = 3;
|
||||
uint64 address = 4;
|
||||
repeated FunctionDiff functions = 5;
|
||||
repeated SymbolDiff symbols = 5;
|
||||
repeated DataDiff data = 6;
|
||||
optional float match_percent = 7;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -139,9 +139,9 @@ impl ObjArch for ObjArchArm {
|
||||
|
||||
let version = match config.arm_arch_version {
|
||||
ArmArchVersion::Auto => self.detected_version.unwrap_or(ArmVersion::V5Te),
|
||||
ArmArchVersion::V4T => ArmVersion::V4T,
|
||||
ArmArchVersion::V5TE => ArmVersion::V5Te,
|
||||
ArmArchVersion::V6K => ArmVersion::V6K,
|
||||
ArmArchVersion::V4t => ArmVersion::V4T,
|
||||
ArmArchVersion::V5te => ArmVersion::V5Te,
|
||||
ArmArchVersion::V6k => ArmVersion::V6K,
|
||||
};
|
||||
let endian = match self.endianness {
|
||||
object::Endianness::Little => unarm::Endian::Little,
|
||||
|
||||
@@ -99,8 +99,8 @@ impl ObjArch for ObjArchMips {
|
||||
MipsInstrCategory::Auto => self.instr_category,
|
||||
MipsInstrCategory::Cpu => InstrCategory::CPU,
|
||||
MipsInstrCategory::Rsp => InstrCategory::RSP,
|
||||
MipsInstrCategory::R3000Gte => InstrCategory::R3000GTE,
|
||||
MipsInstrCategory::R4000Allegrex => InstrCategory::R4000ALLEGREX,
|
||||
MipsInstrCategory::R3000gte => InstrCategory::R3000GTE,
|
||||
MipsInstrCategory::R4000allegrex => InstrCategory::R4000ALLEGREX,
|
||||
MipsInstrCategory::R5900 => InstrCategory::R5900,
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::{
|
||||
ObjDataDiff, ObjDataDiffKind, ObjDiff, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo,
|
||||
ObjInsDiff, ObjInsDiffKind, ObjSectionDiff, ObjSymbolDiff,
|
||||
},
|
||||
obj,
|
||||
obj::{
|
||||
ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSectionKind, ObjSymbol,
|
||||
ObjSymbolFlagSet, ObjSymbolFlags,
|
||||
@@ -39,14 +40,14 @@ impl ObjectDiff {
|
||||
impl SectionDiff {
|
||||
pub fn new(obj: &ObjInfo, section_index: usize, section_diff: &ObjSectionDiff) -> Self {
|
||||
let section = &obj.sections[section_index];
|
||||
let functions = section_diff.symbols.iter().map(|d| FunctionDiff::new(obj, d)).collect();
|
||||
let symbols = section_diff.symbols.iter().map(|d| SymbolDiff::new(obj, d)).collect();
|
||||
let data = section_diff.data_diff.iter().map(|d| DataDiff::new(obj, d)).collect();
|
||||
Self {
|
||||
name: section.name.to_string(),
|
||||
kind: SectionKind::from(section.kind) as i32,
|
||||
size: section.size,
|
||||
address: section.address,
|
||||
functions,
|
||||
symbols,
|
||||
data,
|
||||
match_percent: section_diff.match_percent,
|
||||
}
|
||||
@@ -64,13 +65,22 @@ impl From<ObjSectionKind> for SectionKind {
|
||||
}
|
||||
}
|
||||
|
||||
impl FunctionDiff {
|
||||
impl From<obj::SymbolRef> for SymbolRef {
|
||||
fn from(value: obj::SymbolRef) -> Self {
|
||||
Self {
|
||||
section_index: if value.section_idx == obj::SECTION_COMMON {
|
||||
None
|
||||
} else {
|
||||
Some(value.section_idx as u32)
|
||||
},
|
||||
symbol_index: value.symbol_idx as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolDiff {
|
||||
pub fn new(object: &ObjInfo, symbol_diff: &ObjSymbolDiff) -> Self {
|
||||
let (_section, symbol) = object.section_symbol(symbol_diff.symbol_ref);
|
||||
// let diff_symbol = symbol_diff.diff_symbol.map(|symbol_ref| {
|
||||
// let (_section, symbol) = object.section_symbol(symbol_ref);
|
||||
// Symbol::from(symbol)
|
||||
// });
|
||||
let instructions = symbol_diff
|
||||
.instructions
|
||||
.iter()
|
||||
@@ -78,9 +88,9 @@ impl FunctionDiff {
|
||||
.collect();
|
||||
Self {
|
||||
symbol: Some(Symbol::new(symbol)),
|
||||
// diff_symbol,
|
||||
instructions,
|
||||
match_percent: symbol_diff.match_percent,
|
||||
target: symbol_diff.target_symbol.map(SymbolRef::from),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,7 +120,7 @@ impl Symbol {
|
||||
fn symbol_flags(value: ObjSymbolFlagSet) -> u32 {
|
||||
let mut flags = 0u32;
|
||||
if value.0.contains(ObjSymbolFlags::Global) {
|
||||
flags |= SymbolFlag::SymbolNone as u32;
|
||||
flags |= SymbolFlag::SymbolGlobal as u32;
|
||||
}
|
||||
if value.0.contains(ObjSymbolFlags::Local) {
|
||||
flags |= SymbolFlag::SymbolLocal as u32;
|
||||
|
||||
@@ -13,20 +13,22 @@ fn parse_object(
|
||||
fn parse_and_run_diff(
|
||||
left: Option<Box<[u8]>>,
|
||||
right: Option<Box<[u8]>>,
|
||||
config: diff::DiffObjConfig,
|
||||
diff_config: diff::DiffObjConfig,
|
||||
mapping_config: diff::MappingConfig,
|
||||
) -> Result<DiffResult, JsError> {
|
||||
let target = parse_object(left, &config)?;
|
||||
let base = parse_object(right, &config)?;
|
||||
run_diff(target.as_ref(), base.as_ref(), config)
|
||||
let target = parse_object(left, &diff_config)?;
|
||||
let base = parse_object(right, &diff_config)?;
|
||||
run_diff(target.as_ref(), base.as_ref(), diff_config, mapping_config)
|
||||
}
|
||||
|
||||
fn run_diff(
|
||||
left: Option<&obj::ObjInfo>,
|
||||
right: Option<&obj::ObjInfo>,
|
||||
config: diff::DiffObjConfig,
|
||||
diff_config: diff::DiffObjConfig,
|
||||
mapping_config: diff::MappingConfig,
|
||||
) -> Result<DiffResult, JsError> {
|
||||
log::debug!("Running diff with config: {:?}", config);
|
||||
let result = diff::diff_objs(&config, left, right, None).to_js()?;
|
||||
log::debug!("Running diff with config: {:?}", diff_config);
|
||||
let result = diff::diff_objs(&diff_config, &mapping_config, left, right, None).to_js()?;
|
||||
let left = left.and_then(|o| result.left.as_ref().map(|d| (o, d)));
|
||||
let right = right.and_then(|o| result.right.as_ref().map(|d| (o, d)));
|
||||
Ok(DiffResult::new(left, right))
|
||||
@@ -46,9 +48,10 @@ fn run_diff(
|
||||
pub fn run_diff_proto(
|
||||
left: Option<Box<[u8]>>,
|
||||
right: Option<Box<[u8]>>,
|
||||
config: diff::DiffObjConfig,
|
||||
diff_config: diff::DiffObjConfig,
|
||||
mapping_config: diff::MappingConfig,
|
||||
) -> Result<Box<[u8]>, JsError> {
|
||||
let out = parse_and_run_diff(left, right, config)?;
|
||||
let out = parse_and_run_diff(left, right, diff_config, mapping_config)?;
|
||||
Ok(out.encode_to_vec().into_boxed_slice())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fs,
|
||||
fs::File,
|
||||
io::{BufReader, BufWriter, Read},
|
||||
@@ -6,11 +7,11 @@ use std::{
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use bimap::BiBTreeMap;
|
||||
use filetime::FileTime;
|
||||
use globset::{Glob, GlobSet, GlobSetBuilder};
|
||||
|
||||
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify), tsify(from_wasm_abi))]
|
||||
pub struct ProjectConfig {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub min_version: Option<String>,
|
||||
@@ -27,7 +28,7 @@ pub struct ProjectConfig {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub build_target: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub watch_patterns: Option<Vec<Glob>>,
|
||||
pub watch_patterns: Option<Vec<String>>,
|
||||
#[serde(default, alias = "objects", skip_serializing_if = "Option::is_none")]
|
||||
pub units: Option<Vec<ProjectObject>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
@@ -52,9 +53,21 @@ impl ProjectConfig {
|
||||
pub fn progress_categories_mut(&mut self) -> &mut Vec<ProjectProgressCategory> {
|
||||
self.progress_categories.get_or_insert_with(Vec::new)
|
||||
}
|
||||
|
||||
pub fn build_watch_patterns(&self) -> Result<Vec<Glob>, globset::Error> {
|
||||
Ok(if let Some(watch_patterns) = &self.watch_patterns {
|
||||
watch_patterns
|
||||
.iter()
|
||||
.map(|s| Glob::new(s))
|
||||
.collect::<Result<Vec<Glob>, globset::Error>>()?
|
||||
} else {
|
||||
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify), tsify(from_wasm_abi))]
|
||||
pub struct ProjectObject {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
@@ -78,9 +91,11 @@ pub struct ProjectObject {
|
||||
pub symbol_mappings: Option<SymbolMappings>,
|
||||
}
|
||||
|
||||
pub type SymbolMappings = BiBTreeMap<String, String>;
|
||||
#[cfg_attr(feature = "wasm", tsify_next::declare)]
|
||||
pub type SymbolMappings = BTreeMap<String, String>;
|
||||
|
||||
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify), tsify(from_wasm_abi))]
|
||||
pub struct ProjectObjectMetadata {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub complete: Option<bool>,
|
||||
@@ -95,6 +110,7 @@ pub struct ProjectObjectMetadata {
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify), tsify(from_wasm_abi))]
|
||||
pub struct ProjectProgressCategory {
|
||||
#[serde(default)]
|
||||
pub id: String,
|
||||
@@ -154,6 +170,7 @@ impl ProjectObject {
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify), tsify(from_wasm_abi))]
|
||||
pub struct ScratchConfig {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub platform: Option<String>,
|
||||
|
||||
@@ -259,7 +259,7 @@ fn arg_eq(
|
||||
_ => false,
|
||||
},
|
||||
ObjInsArg::Arg(l) => match right {
|
||||
ObjInsArg::Arg(r) => l == r,
|
||||
ObjInsArg::Arg(r) => l.loose_eq(r),
|
||||
// If relocations are relaxed, match if left is a constant and right is a reloc
|
||||
// Useful for instances where the target object is created without relocations
|
||||
ObjInsArg::Reloc => config.relax_reloc_diffs,
|
||||
|
||||
+21
-196
@@ -18,188 +18,7 @@ pub mod code;
|
||||
pub mod data;
|
||||
pub mod display;
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Default,
|
||||
Eq,
|
||||
PartialEq,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
strum::VariantArray,
|
||||
strum::EnumMessage,
|
||||
)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
|
||||
pub enum X86Formatter {
|
||||
#[default]
|
||||
#[strum(message = "Intel (default)")]
|
||||
Intel,
|
||||
#[strum(message = "AT&T")]
|
||||
Gas,
|
||||
#[strum(message = "NASM")]
|
||||
Nasm,
|
||||
#[strum(message = "MASM")]
|
||||
Masm,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Default,
|
||||
Eq,
|
||||
PartialEq,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
strum::VariantArray,
|
||||
strum::EnumMessage,
|
||||
)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
|
||||
pub enum MipsAbi {
|
||||
#[default]
|
||||
#[strum(message = "Auto (default)")]
|
||||
Auto,
|
||||
#[strum(message = "O32")]
|
||||
O32,
|
||||
#[strum(message = "N32")]
|
||||
N32,
|
||||
#[strum(message = "N64")]
|
||||
N64,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Default,
|
||||
Eq,
|
||||
PartialEq,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
strum::VariantArray,
|
||||
strum::EnumMessage,
|
||||
)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
|
||||
pub enum MipsInstrCategory {
|
||||
#[default]
|
||||
#[strum(message = "Auto (default)")]
|
||||
Auto,
|
||||
#[strum(message = "CPU")]
|
||||
Cpu,
|
||||
#[strum(message = "RSP (N64)")]
|
||||
Rsp,
|
||||
#[strum(message = "R3000 GTE (PS1)")]
|
||||
R3000Gte,
|
||||
#[strum(message = "R4000 ALLEGREX (PSP)")]
|
||||
R4000Allegrex,
|
||||
#[strum(message = "R5900 EE (PS2)")]
|
||||
R5900,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Default,
|
||||
Eq,
|
||||
PartialEq,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
strum::VariantArray,
|
||||
strum::EnumMessage,
|
||||
)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
|
||||
pub enum ArmArchVersion {
|
||||
#[default]
|
||||
#[strum(message = "Auto (default)")]
|
||||
Auto,
|
||||
#[strum(message = "ARMv4T (GBA)")]
|
||||
V4T,
|
||||
#[strum(message = "ARMv5TE (DS)")]
|
||||
V5TE,
|
||||
#[strum(message = "ARMv6K (3DS)")]
|
||||
V6K,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Default,
|
||||
Eq,
|
||||
PartialEq,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
strum::VariantArray,
|
||||
strum::EnumMessage,
|
||||
)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
|
||||
pub enum ArmR9Usage {
|
||||
#[default]
|
||||
#[strum(
|
||||
message = "R9 or V6 (default)",
|
||||
detailed_message = "Use R9 as a general-purpose register."
|
||||
)]
|
||||
GeneralPurpose,
|
||||
#[strum(
|
||||
message = "SB (static base)",
|
||||
detailed_message = "Used for position-independent data (PID)."
|
||||
)]
|
||||
Sb,
|
||||
#[strum(message = "TR (TLS register)", detailed_message = "Used for thread-local storage.")]
|
||||
Tr,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn default_true() -> bool { true }
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
|
||||
#[cfg_attr(feature = "wasm", tsify(from_wasm_abi))]
|
||||
#[serde(default)]
|
||||
pub struct DiffObjConfig {
|
||||
pub relax_reloc_diffs: bool,
|
||||
#[serde(default = "default_true")]
|
||||
pub space_between_args: bool,
|
||||
pub combine_data_sections: bool,
|
||||
#[serde(default)]
|
||||
pub symbol_mappings: MappingConfig,
|
||||
// x86
|
||||
pub x86_formatter: X86Formatter,
|
||||
// MIPS
|
||||
pub mips_abi: MipsAbi,
|
||||
pub mips_instr_category: MipsInstrCategory,
|
||||
// ARM
|
||||
pub arm_arch_version: ArmArchVersion,
|
||||
pub arm_unified_syntax: bool,
|
||||
pub arm_av_registers: bool,
|
||||
pub arm_r9_usage: ArmR9Usage,
|
||||
pub arm_sl_usage: bool,
|
||||
pub arm_fp_usage: bool,
|
||||
pub arm_ip_usage: bool,
|
||||
}
|
||||
|
||||
impl Default for DiffObjConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
relax_reloc_diffs: false,
|
||||
space_between_args: true,
|
||||
combine_data_sections: false,
|
||||
symbol_mappings: Default::default(),
|
||||
x86_formatter: Default::default(),
|
||||
mips_abi: Default::default(),
|
||||
mips_instr_category: Default::default(),
|
||||
arm_arch_version: Default::default(),
|
||||
arm_unified_syntax: true,
|
||||
arm_av_registers: false,
|
||||
arm_r9_usage: Default::default(),
|
||||
arm_sl_usage: false,
|
||||
arm_fp_usage: false,
|
||||
arm_ip_usage: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
include!(concat!(env!("OUT_DIR"), "/config.gen.rs"));
|
||||
|
||||
impl DiffObjConfig {
|
||||
pub fn separator(&self) -> &'static str {
|
||||
@@ -386,12 +205,13 @@ pub struct DiffObjsResult {
|
||||
}
|
||||
|
||||
pub fn diff_objs(
|
||||
config: &DiffObjConfig,
|
||||
diff_config: &DiffObjConfig,
|
||||
mapping_config: &MappingConfig,
|
||||
left: Option<&ObjInfo>,
|
||||
right: Option<&ObjInfo>,
|
||||
prev: Option<&ObjInfo>,
|
||||
) -> Result<DiffObjsResult> {
|
||||
let symbol_matches = matching_symbols(left, right, prev, &config.symbol_mappings)?;
|
||||
let symbol_matches = matching_symbols(left, right, prev, mapping_config)?;
|
||||
let section_matches = matching_sections(left, right)?;
|
||||
let mut left = left.map(|p| (p, ObjDiff::new_from_obj(p)));
|
||||
let mut right = right.map(|p| (p, ObjDiff::new_from_obj(p)));
|
||||
@@ -409,8 +229,10 @@ pub fn diff_objs(
|
||||
let (right_obj, right_out) = right.as_mut().unwrap();
|
||||
match section_kind {
|
||||
ObjSectionKind::Code => {
|
||||
let left_code = process_code_symbol(left_obj, left_symbol_ref, config)?;
|
||||
let right_code = process_code_symbol(right_obj, right_symbol_ref, config)?;
|
||||
let left_code =
|
||||
process_code_symbol(left_obj, left_symbol_ref, diff_config)?;
|
||||
let right_code =
|
||||
process_code_symbol(right_obj, right_symbol_ref, diff_config)?;
|
||||
let (left_diff, right_diff) = diff_code(
|
||||
left_obj,
|
||||
right_obj,
|
||||
@@ -418,14 +240,15 @@ pub fn diff_objs(
|
||||
&right_code,
|
||||
left_symbol_ref,
|
||||
right_symbol_ref,
|
||||
config,
|
||||
diff_config,
|
||||
)?;
|
||||
*left_out.symbol_diff_mut(left_symbol_ref) = left_diff;
|
||||
*right_out.symbol_diff_mut(right_symbol_ref) = right_diff;
|
||||
|
||||
if let Some(prev_symbol_ref) = prev_symbol_ref {
|
||||
let (prev_obj, prev_out) = prev.as_mut().unwrap();
|
||||
let prev_code = process_code_symbol(prev_obj, prev_symbol_ref, config)?;
|
||||
let prev_code =
|
||||
process_code_symbol(prev_obj, prev_symbol_ref, diff_config)?;
|
||||
let (_, prev_diff) = diff_code(
|
||||
left_obj,
|
||||
right_obj,
|
||||
@@ -433,7 +256,7 @@ pub fn diff_objs(
|
||||
&prev_code,
|
||||
right_symbol_ref,
|
||||
prev_symbol_ref,
|
||||
config,
|
||||
diff_config,
|
||||
)?;
|
||||
*prev_out.symbol_diff_mut(prev_symbol_ref) = prev_diff;
|
||||
}
|
||||
@@ -464,7 +287,7 @@ pub fn diff_objs(
|
||||
let (left_obj, left_out) = left.as_mut().unwrap();
|
||||
match section_kind {
|
||||
ObjSectionKind::Code => {
|
||||
let code = process_code_symbol(left_obj, left_symbol_ref, config)?;
|
||||
let code = process_code_symbol(left_obj, left_symbol_ref, diff_config)?;
|
||||
*left_out.symbol_diff_mut(left_symbol_ref) =
|
||||
no_diff_code(&code, left_symbol_ref)?;
|
||||
}
|
||||
@@ -478,7 +301,7 @@ pub fn diff_objs(
|
||||
let (right_obj, right_out) = right.as_mut().unwrap();
|
||||
match section_kind {
|
||||
ObjSectionKind::Code => {
|
||||
let code = process_code_symbol(right_obj, right_symbol_ref, config)?;
|
||||
let code = process_code_symbol(right_obj, right_symbol_ref, diff_config)?;
|
||||
*right_out.symbol_diff_mut(right_symbol_ref) =
|
||||
no_diff_code(&code, right_symbol_ref)?;
|
||||
}
|
||||
@@ -549,11 +372,11 @@ pub fn diff_objs(
|
||||
if let (Some((right_obj, right_out)), Some((left_obj, left_out))) =
|
||||
(right.as_mut(), left.as_mut())
|
||||
{
|
||||
if let Some(right_name) = &config.symbol_mappings.selecting_left {
|
||||
generate_mapping_symbols(right_obj, right_name, left_obj, left_out, config)?;
|
||||
if let Some(right_name) = &mapping_config.selecting_left {
|
||||
generate_mapping_symbols(right_obj, right_name, left_obj, left_out, diff_config)?;
|
||||
}
|
||||
if let Some(left_name) = &config.symbol_mappings.selecting_right {
|
||||
generate_mapping_symbols(left_obj, left_name, right_obj, right_out, config)?;
|
||||
if let Some(left_name) = &mapping_config.selecting_right {
|
||||
generate_mapping_symbols(left_obj, left_name, right_obj, right_out, diff_config)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,7 +459,9 @@ struct SectionMatch {
|
||||
section_kind: ObjSectionKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify), tsify(from_wasm_abi))]
|
||||
#[serde(default)]
|
||||
pub struct MappingConfig {
|
||||
/// Manual symbol mappings
|
||||
pub mappings: SymbolMappings,
|
||||
|
||||
@@ -5,7 +5,6 @@ use time::OffsetDateTime;
|
||||
|
||||
use crate::{
|
||||
build::{run_make, BuildConfig, BuildStatus},
|
||||
config::SymbolMappings,
|
||||
diff::{diff_objs, DiffObjConfig, MappingConfig, ObjDiff},
|
||||
jobs::{start_job, update_status, Job, JobContext, JobResult, JobState},
|
||||
obj::{read, ObjInfo},
|
||||
@@ -18,9 +17,7 @@ pub struct ObjDiffConfig {
|
||||
pub target_path: Option<PathBuf>,
|
||||
pub base_path: Option<PathBuf>,
|
||||
pub diff_obj_config: DiffObjConfig,
|
||||
pub symbol_mappings: SymbolMappings,
|
||||
pub selecting_left: Option<String>,
|
||||
pub selecting_right: Option<String>,
|
||||
pub mapping_config: MappingConfig,
|
||||
}
|
||||
|
||||
pub struct ObjDiffResult {
|
||||
@@ -34,15 +31,8 @@ pub struct ObjDiffResult {
|
||||
fn run_build(
|
||||
context: &JobContext,
|
||||
cancel: Receiver<()>,
|
||||
mut config: ObjDiffConfig,
|
||||
config: ObjDiffConfig,
|
||||
) -> Result<Box<ObjDiffResult>> {
|
||||
// Use the per-object symbol mappings, we don't set mappings globally
|
||||
config.diff_obj_config.symbol_mappings = MappingConfig {
|
||||
mappings: config.symbol_mappings,
|
||||
selecting_left: config.selecting_left,
|
||||
selecting_right: config.selecting_right,
|
||||
};
|
||||
|
||||
let mut target_path_rel = None;
|
||||
let mut base_path_rel = None;
|
||||
if config.build_target || config.build_base {
|
||||
@@ -180,7 +170,13 @@ fn run_build(
|
||||
|
||||
update_status(context, "Performing diff".to_string(), step_idx, total, &cancel)?;
|
||||
step_idx += 1;
|
||||
let result = diff_objs(&config.diff_obj_config, first_obj.as_ref(), second_obj.as_ref(), None)?;
|
||||
let result = diff_objs(
|
||||
&config.diff_obj_config,
|
||||
&config.mapping_config,
|
||||
first_obj.as_ref(),
|
||||
second_obj.as_ref(),
|
||||
None,
|
||||
)?;
|
||||
|
||||
update_status(context, "Complete".to_string(), step_idx, total, &cancel)?;
|
||||
Ok(Box::new(ObjDiffResult {
|
||||
|
||||
@@ -31,8 +31,8 @@ const_format = "0.2"
|
||||
cwdemangle = "1.0"
|
||||
cwextab = "1.0"
|
||||
dirs = "5.0"
|
||||
egui = "0.29"
|
||||
egui_extras = "0.29"
|
||||
egui = "0.30"
|
||||
egui_extras = "0.30"
|
||||
filetime = "0.2"
|
||||
float-ord = "0.3"
|
||||
font-kit = "0.14"
|
||||
@@ -54,7 +54,7 @@ time = { version = "0.3", features = ["formatting", "local-offset"] }
|
||||
|
||||
# Keep version in sync with egui
|
||||
[dependencies.eframe]
|
||||
version = "0.29"
|
||||
version = "0.30"
|
||||
features = [
|
||||
"default_fonts",
|
||||
"persistence",
|
||||
@@ -65,7 +65,7 @@ default-features = false
|
||||
|
||||
# Keep version in sync with eframe
|
||||
[dependencies.wgpu]
|
||||
version = "22.1"
|
||||
version = "23.0"
|
||||
features = [
|
||||
"dx12",
|
||||
"metal",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user