mirror of
https://github.com/encounter/objdiff.git
synced 2026-07-10 12:18:36 -07:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a4fdb61f04 | |||
| 2876be37a3 | |||
| 11171763eb | |||
| 6037a79ba2 | |||
| f7efe5fdff | |||
| 0692deac59 | |||
| c3e3d175c5 | |||
| c45f4bbc99 | |||
| b0c5431ac5 | |||
| 9ab246367b | |||
| dcafe51eda | |||
| c65e87c382 | |||
| 1756b9f6c5 | |||
| 303f2938a2 | |||
| 526e031251 | |||
| 10b2a9c129 | |||
| abe68ef2f2 | |||
| 304df96411 | |||
| 7aa878b48e | |||
| a119d9a6dd | |||
| ebf653816a | |||
| 424434edd6 | |||
| 7f14b684bf | |||
| c5da7f7dd5 | |||
| 2fd655850a | |||
| 79bd7317c1 | |||
| 21f8f2407c | |||
| d2b7a9ef25 |
@@ -65,7 +65,7 @@ jobs:
|
|||||||
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||||
with:
|
with:
|
||||||
command: check ${{ matrix.checks }}
|
command: check ${{ matrix.checks }}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -18,4 +18,4 @@ android.keystore
|
|||||||
*.frag
|
*.frag
|
||||||
*.vert
|
*.vert
|
||||||
*.metal
|
*.metal
|
||||||
.vscode/launch.json
|
.vscode/
|
||||||
|
|||||||
Generated
+1071
-733
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -13,9 +13,9 @@ strip = "debuginfo"
|
|||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "2.3.3"
|
version = "2.7.0"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/encounter/objdiff"
|
repository = "https://github.com/encounter/objdiff"
|
||||||
rust-version = "1.74"
|
rust-version = "1.81"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ Supports:
|
|||||||
- MIPS (N64, PS1, PS2, PSP)
|
- MIPS (N64, PS1, PS2, PSP)
|
||||||
- x86 (COFF only at the moment)
|
- x86 (COFF only at the moment)
|
||||||
- ARM (GBA, DS, 3DS)
|
- ARM (GBA, DS, 3DS)
|
||||||
|
- ARM64 (Switch, experimental)
|
||||||
|
|
||||||
See [Usage](#usage) for more information.
|
See [Usage](#usage) for more information.
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ allow = [
|
|||||||
"BSL-1.0",
|
"BSL-1.0",
|
||||||
"CC0-1.0",
|
"CC0-1.0",
|
||||||
"MPL-2.0",
|
"MPL-2.0",
|
||||||
"Unicode-DFS-2016",
|
"Unicode-3.0",
|
||||||
"Zlib",
|
"Zlib",
|
||||||
"0BSD",
|
"0BSD",
|
||||||
"OFL-1.1",
|
"OFL-1.1",
|
||||||
@@ -239,7 +239,7 @@ allow-git = []
|
|||||||
|
|
||||||
[sources.allow-org]
|
[sources.allow-org]
|
||||||
# github.com organizations to allow git sources for
|
# github.com organizations to allow git sources for
|
||||||
github = ["notify-rs"]
|
github = []
|
||||||
# gitlab.com organizations to allow git sources for
|
# gitlab.com organizations to allow git sources for
|
||||||
gitlab = []
|
gitlab = []
|
||||||
# bitbucket.org organizations to allow git sources for
|
# bitbucket.org organizations to allow git sources for
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ publish = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
argp = "0.3"
|
argp = "0.4"
|
||||||
crossterm = "0.28"
|
crossterm = "0.28"
|
||||||
enable-ansi-support = "0.2"
|
enable-ansi-support = "0.2"
|
||||||
memmap2 = "0.9"
|
memmap2 = "0.9"
|
||||||
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
||||||
prost = "0.13"
|
prost = "0.13"
|
||||||
ratatui = "0.28"
|
ratatui = "0.29"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|||||||
+226
-740
File diff suppressed because it is too large
Load Diff
@@ -169,22 +169,29 @@ fn report_object(
|
|||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() };
|
let diff_config = diff::DiffObjConfig {
|
||||||
|
function_reloc_diffs: diff::FunctionRelocDiffs::None,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mapping_config = diff::MappingConfig::default();
|
||||||
let target = object
|
let target = object
|
||||||
.target_path
|
.target_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|p| {
|
.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()?;
|
.transpose()?;
|
||||||
let base = object
|
let base = object
|
||||||
.base_path
|
.base_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|p| {
|
.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()?;
|
.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 {
|
let metadata = ReportUnitMetadata {
|
||||||
complete: object.complete(),
|
complete: object.complete(),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
mod argp_version;
|
mod argp_version;
|
||||||
mod cmd;
|
mod cmd;
|
||||||
mod util;
|
mod util;
|
||||||
|
mod views;
|
||||||
|
|
||||||
// musl's allocator is very slow, so use mimalloc when targeting musl.
|
// musl's allocator is very slow, so use mimalloc when targeting musl.
|
||||||
// Otherwise, use the system allocator to avoid extra code size.
|
// Otherwise, use the system allocator to avoid extra code size.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use crossterm::event::Event;
|
||||||
|
use ratatui::Frame;
|
||||||
|
|
||||||
|
use crate::cmd::diff::AppState;
|
||||||
|
|
||||||
|
pub mod function_diff;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct EventResult {
|
||||||
|
pub redraw: bool,
|
||||||
|
pub click_xy: Option<(u16, u16)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum EventControlFlow {
|
||||||
|
Break,
|
||||||
|
Continue(EventResult),
|
||||||
|
Reload,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UiView {
|
||||||
|
fn draw(&mut self, state: &AppState, f: &mut Frame, result: &mut EventResult);
|
||||||
|
fn handle_event(&mut self, state: &mut AppState, event: Event) -> EventControlFlow;
|
||||||
|
fn reload(&mut self, state: &AppState) -> Result<()>;
|
||||||
|
}
|
||||||
+131
-11
@@ -16,16 +16,104 @@ documentation = "https://docs.rs/objdiff-core"
|
|||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
all = ["config", "dwarf", "mips", "ppc", "x86", "arm", "bindings"]
|
all = [
|
||||||
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
|
# Features
|
||||||
config = ["dep:bimap", "dep:globset", "dep:semver", "dep:serde_json", "dep:serde_yaml", "dep:serde", "dep:filetime"]
|
"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"]
|
dwarf = ["dep:gimli"]
|
||||||
mips = ["any-arch", "dep:rabbitizer"]
|
mips = [
|
||||||
ppc = ["any-arch", "dep:cwdemangle", "dep:cwextab", "dep:ppc750cl"]
|
"any-arch",
|
||||||
x86 = ["any-arch", "dep:cpp_demangle", "dep:iced-x86", "dep:msvc-demangler"]
|
"dep:rabbitizer",
|
||||||
arm = ["any-arch", "dep:cpp_demangle", "dep:unarm", "dep:arm-attr"]
|
]
|
||||||
bindings = ["dep:serde_json", "dep:prost", "dep:pbjson", "dep:serde", "dep:prost-build", "dep:pbjson-build"]
|
ppc = [
|
||||||
wasm = ["bindings", "any-arch", "dep:console_error_panic_hook", "dep:console_log", "dep:wasm-bindgen", "dep:tsify-next", "dep:log"]
|
"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]
|
[package.metadata.docs.rs]
|
||||||
features = ["all"]
|
features = ["all"]
|
||||||
@@ -61,7 +149,7 @@ gimli = { version = "0.31", default-features = false, features = ["read-all"], o
|
|||||||
|
|
||||||
# ppc
|
# ppc
|
||||||
cwdemangle = { version = "1.0", optional = true }
|
cwdemangle = { version = "1.0", optional = true }
|
||||||
cwextab = { version = "1.0.2", optional = true }
|
cwextab = { version = "1.0", optional = true }
|
||||||
ppc750cl = { version = "0.3", optional = true }
|
ppc750cl = { version = "0.3", optional = true }
|
||||||
|
|
||||||
# mips
|
# mips
|
||||||
@@ -76,6 +164,38 @@ msvc-demangler = { version = "0.10", optional = true }
|
|||||||
unarm = { version = "1.6", optional = true }
|
unarm = { version = "1.6", optional = true }
|
||||||
arm-attr = { version = "0.1", optional = true }
|
arm-attr = { version = "0.1", optional = true }
|
||||||
|
|
||||||
|
# arm64
|
||||||
|
yaxpeax-arch = { version = "0.3", default-features = false, features = ["std"], optional = true }
|
||||||
|
yaxpeax-arm = { version = "0.3", default-features = false, features = ["std"], optional = true }
|
||||||
|
|
||||||
|
# build
|
||||||
|
notify = { version = "8.0.0", optional = true }
|
||||||
|
notify-debouncer-full = { version = "0.5.0", optional = true }
|
||||||
|
shell-escape = { version = "0.1", optional = true }
|
||||||
|
tempfile = { version = "3.15", optional = true }
|
||||||
|
time = { version = "0.3", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
path-slash = { version = "0.2", optional = true }
|
||||||
|
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.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.42", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
prost-build = { version = "0.13", optional = true }
|
heck = { version = "0.5", optional = true }
|
||||||
pbjson-build = { version = "0.7", 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 }
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ objdiff-core contains the core functionality of [objdiff](https://github.com/enc
|
|||||||
- **`ppc`**: Enables the PowerPC backend powered by [ppc750cl](https://github.com/encounter/ppc750cl).
|
- **`ppc`**: Enables the PowerPC backend powered by [ppc750cl](https://github.com/encounter/ppc750cl).
|
||||||
- **`x86`**: Enables the x86 backend powered by [iced-x86](https://crates.io/crates/iced-x86).
|
- **`x86`**: Enables the x86 backend powered by [iced-x86](https://crates.io/crates/iced-x86).
|
||||||
- **`arm`**: Enables the ARM backend powered by [unarm](https://github.com/AetiasHax/unarm).
|
- **`arm`**: Enables the ARM backend powered by [unarm](https://github.com/AetiasHax/unarm).
|
||||||
|
- **`arm64`**: Enables the ARM64 backend powered by [yaxpeax-arm](https://github.com/iximeow/yaxpeax-arm).
|
||||||
- **`bindings`**: Enables serialization and deserialization of objdiff data structures.
|
- **`bindings`**: Enables serialization and deserialization of objdiff data structures.
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
|
#[cfg(feature = "any-arch")]
|
||||||
|
mod config_gen;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
#[cfg(feature = "bindings")]
|
#[cfg(feature = "bindings")]
|
||||||
compile_protos();
|
compile_protos();
|
||||||
|
#[cfg(feature = "any-arch")]
|
||||||
|
config_gen::generate_diff_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "bindings")]
|
#[cfg(feature = "bindings")]
|
||||||
|
|||||||
@@ -0,0 +1,248 @@
|
|||||||
|
{
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "functionRelocDiffs",
|
||||||
|
"type": "choice",
|
||||||
|
"default": "name_address",
|
||||||
|
"name": "Function relocation diffs",
|
||||||
|
"description": "How relocation targets will be diffed in the function view.",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"value": "none",
|
||||||
|
"name": "None"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "name_address",
|
||||||
|
"name": "Name or address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "data_value",
|
||||||
|
"name": "Data value"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "all",
|
||||||
|
"name": "Name or address, data value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": [
|
||||||
|
"functionRelocDiffs",
|
||||||
|
"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_NONE = 0;
|
||||||
SYMBOL_GLOBAL = 1;
|
SYMBOL_GLOBAL = 1;
|
||||||
SYMBOL_LOCAL = 2;
|
SYMBOL_LOCAL = 2;
|
||||||
SYMBOL_WEAK = 3;
|
SYMBOL_WEAK = 4;
|
||||||
SYMBOL_COMMON = 4;
|
SYMBOL_COMMON = 8;
|
||||||
SYMBOL_HIDDEN = 5;
|
SYMBOL_HIDDEN = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A single parsed instruction
|
// A single parsed instruction
|
||||||
@@ -122,10 +122,17 @@ message InstructionBranchTo {
|
|||||||
uint32 branch_index = 2;
|
uint32 branch_index = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message FunctionDiff {
|
message SymbolRef {
|
||||||
|
optional uint32 section_index = 1;
|
||||||
|
uint32 symbol_index = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SymbolDiff {
|
||||||
Symbol symbol = 1;
|
Symbol symbol = 1;
|
||||||
repeated InstructionDiff instructions = 2;
|
repeated InstructionDiff instructions = 2;
|
||||||
optional float match_percent = 3;
|
optional float match_percent = 3;
|
||||||
|
// The symbol ref in the _other_ object that this symbol was diffed against
|
||||||
|
optional SymbolRef target = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DataDiff {
|
message DataDiff {
|
||||||
@@ -140,7 +147,7 @@ message SectionDiff {
|
|||||||
SectionKind kind = 2;
|
SectionKind kind = 2;
|
||||||
uint64 size = 3;
|
uint64 size = 3;
|
||||||
uint64 address = 4;
|
uint64 address = 4;
|
||||||
repeated FunctionDiff functions = 5;
|
repeated SymbolDiff symbols = 5;
|
||||||
repeated DataDiff data = 6;
|
repeated DataDiff data = 6;
|
||||||
optional float match_percent = 7;
|
optional float match_percent = 7;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -124,11 +124,9 @@ impl ObjArch for ObjArchArm {
|
|||||||
.get(&SectionIndex(section_index))
|
.get(&SectionIndex(section_index))
|
||||||
.map(|x| x.as_slice())
|
.map(|x| x.as_slice())
|
||||||
.unwrap_or(&fallback_mappings);
|
.unwrap_or(&fallback_mappings);
|
||||||
let first_mapping_idx =
|
let first_mapping_idx = mapping_symbols
|
||||||
match mapping_symbols.binary_search_by_key(&start_addr, |x| x.address) {
|
.binary_search_by_key(&start_addr, |x| x.address)
|
||||||
Ok(idx) => idx,
|
.unwrap_or_else(|idx| idx - 1);
|
||||||
Err(idx) => idx - 1,
|
|
||||||
};
|
|
||||||
let first_mapping = mapping_symbols[first_mapping_idx].mapping;
|
let first_mapping = mapping_symbols[first_mapping_idx].mapping;
|
||||||
|
|
||||||
let mut mappings_iter =
|
let mut mappings_iter =
|
||||||
@@ -141,9 +139,9 @@ impl ObjArch for ObjArchArm {
|
|||||||
|
|
||||||
let version = match config.arm_arch_version {
|
let version = match config.arm_arch_version {
|
||||||
ArmArchVersion::Auto => self.detected_version.unwrap_or(ArmVersion::V5Te),
|
ArmArchVersion::Auto => self.detected_version.unwrap_or(ArmVersion::V5Te),
|
||||||
ArmArchVersion::V4T => ArmVersion::V4T,
|
ArmArchVersion::V4t => ArmVersion::V4T,
|
||||||
ArmArchVersion::V5TE => ArmVersion::V5Te,
|
ArmArchVersion::V5te => ArmVersion::V5Te,
|
||||||
ArmArchVersion::V6K => ArmVersion::V6K,
|
ArmArchVersion::V6k => ArmVersion::V6K,
|
||||||
};
|
};
|
||||||
let endian = match self.endianness {
|
let endian = match self.endianness {
|
||||||
object::Endianness::Little => unarm::Endian::Little,
|
object::Endianness::Little => unarm::Endian::Little,
|
||||||
@@ -215,7 +213,7 @@ impl ObjArch for ObjArchArm {
|
|||||||
address: address as u64,
|
address: address as u64,
|
||||||
size: (parser.address - address) as u8,
|
size: (parser.address - address) as u8,
|
||||||
op: ins.opcode_id(),
|
op: ins.opcode_id(),
|
||||||
mnemonic: parsed_ins.mnemonic.to_string(),
|
mnemonic: Cow::Borrowed(parsed_ins.mnemonic),
|
||||||
args,
|
args,
|
||||||
reloc,
|
reloc,
|
||||||
branch_dest,
|
branch_dest,
|
||||||
@@ -234,7 +232,7 @@ impl ObjArch for ObjArchArm {
|
|||||||
section: &ObjSection,
|
section: &ObjSection,
|
||||||
address: u64,
|
address: u64,
|
||||||
reloc: &Relocation,
|
reloc: &Relocation,
|
||||||
) -> anyhow::Result<i64> {
|
) -> Result<i64> {
|
||||||
let address = address as usize;
|
let address = address as usize;
|
||||||
Ok(match reloc.flags() {
|
Ok(match reloc.flags() {
|
||||||
// ARM calls
|
// ARM calls
|
||||||
@@ -278,6 +276,19 @@ impl ObjArch for ObjArchArm {
|
|||||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||||
Cow::Owned(format!("<{flags:?}>"))
|
Cow::Owned(format!("<{flags:?}>"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
||||||
|
match flags {
|
||||||
|
RelocationFlags::Elf { r_type } => match r_type {
|
||||||
|
elf::R_ARM_ABS32 => 4,
|
||||||
|
elf::R_ARM_REL32 => 4,
|
||||||
|
elf::R_ARM_ABS16 => 2,
|
||||||
|
elf::R_ARM_ABS8 => 1,
|
||||||
|
_ => 1,
|
||||||
|
},
|
||||||
|
_ => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user