Compare commits

..

18 Commits

Author SHA1 Message Date
Luke Street 6bcac60df8 ci: Re-enable cargo release 2026-07-10 00:33:21 -06:00
Luke Street b34108fa24 Bump a few more deps & fix advisories 2026-07-10 00:29:27 -06:00
Luke Street 5f9e9063f8 clippy fixes & cargo update 2026-07-10 00:22:48 -06:00
Luke Street a3e11149f7 Version 3.7.3 2026-07-09 23:48:39 -06:00
Haydn Trigg d22c785610 Update GitHub Actions (#357)
* Update build.yaml

* Update build.yaml
2026-07-09 23:46:59 -06:00
Anghelo Carvajal 5035f8ec36 Update rabbitizer integration for 2.0.0-alpha.9 (#347) 2026-07-09 23:46:05 -06:00
Dávid Balatoni b8f401b0ba Make the .mdebug line parser support EE-GCC (PS2) (#362) 2026-07-09 23:45:53 -06:00
Luke Street 282783c081 Suppress unused warnings in objdiff-cli for now 2026-07-09 23:45:10 -06:00
LagoLunatic f5ae76e64d ARM: Fix index out of bounds error when there are no mapping symbols (#359) 2026-07-09 23:30:03 -06:00
LagoLunatic dbabdbff11 ARM: Fix trailing relocations being ignored when inferring function sizes (#360)
* ARM: Fix relocations being ignored when inferring function sizes

* ARM: Add test for trailing relocations

* ARM: Fix trailing relocations being cut in half
2026-07-09 23:29:51 -06:00
Bruno Macabeus 2fdba8842e Add support for R_ARM_V4BX (#377) 2026-07-09 23:28:01 -06:00
Darxoon 0fe4fadbc3 Fix armcc symbols always being hidden (#364) 2026-07-09 23:27:40 -06:00
Michael Rieder 04be189e51 Honor symbol mappings in CLI diffs (#378) 2026-07-09 23:26:50 -06:00
Ethan Roseman 4d0a273d65 Remove git-based dependencies and update several (#379) 2026-07-09 23:25:34 -06:00
Dávid Balatoni 73917c12a8 Fix PS2 MWCC line number parsing (#363) 2026-06-17 20:23:34 -04:00
Haydn Trigg 2cd1e2e0ce Add missing x64 relocation types (#355)
* Add missing x64 relocation types

* Format x86.rs

* Update x86.rs
2026-06-01 21:50:05 -06:00
Haydn Trigg a6ddc506f8 Bump Object Crate to 0.39.1 (#348)
* Bump Object Crate to 0.39.0

* Bump Object Crate to 0.39.1
2026-06-01 21:49:52 -06:00
Haydn Trigg b8d7b3bc1a Strip trailing int3 instructions (alignment padding) for x64 (#354)
* Strip trailing int3 instructions (alignment padding) for x64

* Improve strip trailing int3 instructions

* Improve strip trailing int3 instructions again
2026-06-01 20:58:45 -06:00
41 changed files with 2294 additions and 1160 deletions
+17 -18
View File
@@ -28,7 +28,7 @@ jobs:
sudo apt-get update
sudo apt-get -y install libgtk-3-dev
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
@@ -47,7 +47,7 @@ jobs:
RUSTFLAGS: -D warnings
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Rust toolchain
# We use nightly options in rustfmt.toml
uses: dtolnay/rust-toolchain@nightly
@@ -67,7 +67,7 @@ jobs:
# Prevent new advisories from failing CI
continue-on-error: ${{ matrix.checks == 'advisories' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: EmbarkStudios/cargo-deny-action@v2
with:
command: check ${{ matrix.checks }}
@@ -86,7 +86,7 @@ jobs:
sudo apt-get update
sudo apt-get -y install libgtk-3-dev
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust workspace
@@ -145,10 +145,10 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install uv
if: matrix.build == 'zigbuild'
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@v7
- name: Install cargo-zigbuild
if: matrix.build == 'zigbuild'
run: |
@@ -167,7 +167,7 @@ jobs:
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
--bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
path: |
@@ -213,10 +213,10 @@ jobs:
sudo apt-get update
sudo apt-get -y install ${{ matrix.packages }}
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install uv
if: matrix.build == 'zigbuild'
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@v7
- name: Install cargo-zigbuild
if: matrix.build == 'zigbuild'
run: |
@@ -235,7 +235,7 @@ jobs:
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
--bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
path: |
@@ -248,7 +248,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@nightly
with:
@@ -256,7 +256,7 @@ jobs:
- name: Cache Rust workspace
uses: Swatinem/rust-cache@v2
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6.4.0
with:
node-version: lts/*
- name: Install dependencies
@@ -264,7 +264,7 @@ jobs:
- name: Build
run: npm -C objdiff-wasm run build
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: wasm
path: objdiff-wasm/dist/
@@ -277,7 +277,7 @@ jobs:
needs: [ build-cli, build-gui, build-wasm ]
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Check git tag against package versions
shell: bash
run: |
@@ -340,12 +340,11 @@ jobs:
release-cargo:
name: Release (Cargo)
if: 'false' # TODO re-enable when all dependencies are published
runs-on: ubuntu-latest
needs: [ check-version ]
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Publish
@@ -360,9 +359,9 @@ jobs:
needs: [ check-version ]
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6.4.0
with:
node-version: lts/*
registry-url: 'https://registry.npmjs.org'
Generated
+1734 -1002
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -14,7 +14,7 @@ default-members = [
resolver = "3"
[workspace.package]
version = "3.7.2"
version = "3.7.3"
authors = ["Luke Street <luke@street.dev>"]
edition = "2024"
license = "MIT OR Apache-2.0"
+6 -4
View File
@@ -75,9 +75,13 @@ ignore = [
#{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" },
{ id = "RUSTSEC-2024-0436", reason = "Unmaintained paste crate is an indirect dependency" },
{ id = "RUSTSEC-2025-0052", reason = "Unmaintained async-std crate is an indirect dependency" },
{ id = "RUSTSEC-2025-0119", reason = "Unmaintained number_prefix crate is an indirect dependency" },
{ id = "RUSTSEC-2025-0134", reason = "Unmaintained rustls-pemfile crate is an indirect dependency" },
{ id = "RUSTSEC-2026-0002", reason = "Unsound advisory in lru indirect dependency" },
{ id = "RUSTSEC-2026-0098", reason = "Build-time objdiff-wasm wit-deps dependency still uses reqwest 0.11; latest wit-deps has no safe upgrade path" },
{ id = "RUSTSEC-2026-0099", reason = "Build-time objdiff-wasm wit-deps dependency still uses reqwest 0.11; latest wit-deps has no safe upgrade path" },
{ id = "RUSTSEC-2026-0104", reason = "Build-time objdiff-wasm wit-deps dependency still uses reqwest 0.11; latest wit-deps has no safe upgrade path" },
{ id = "RUSTSEC-2026-0192", reason = "Unmaintained ttf-parser is an egui 0.33 indirect dependency; egui 0.35 requires raising MSRV from 1.88 to 1.92" },
{ id = "RUSTSEC-2026-0194", reason = "Build-time wayland-scanner dependency parses vendored protocol XML; no newer stable wayland-scanner release is available" },
{ id = "RUSTSEC-2026-0195", reason = "Build-time wayland-scanner dependency parses vendored protocol XML; no newer stable wayland-scanner release is available" },
]
# If this is true, then cargo deny will use the git executable to fetch advisory database.
# If this is false, then it uses a built-in git library.
@@ -245,8 +249,6 @@ allow-git = []
[sources.allow-org]
# github.com organizations to allow git sources for
github = [
"encounter",
"gimli-rs", # gimli
]
# gitlab.com organizations to allow git sources for
gitlab = []
+1 -1
View File
@@ -20,7 +20,7 @@ enable-ansi-support = "0.3"
memmap2 = "0.9"
objdiff-core = { path = "../objdiff-core", features = ["all"] }
prost = "0.14"
ratatui = "0.29"
ratatui = "0.30"
rayon = "1.11"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
+38 -10
View File
@@ -1,4 +1,6 @@
#![allow(unused)] // TODO
use std::{
collections::BTreeMap,
io::stdout,
mem,
sync::{
@@ -79,11 +81,13 @@ pub struct Args {
}
pub fn run(args: Args) -> Result<()> {
let (target_path, base_path, project_config, unit_options) =
let (target_path, base_path, project_config, unit_options, symbol_mappings) =
match (&args.target, &args.base, &args.project, &args.unit) {
(Some(_), Some(_), None, None)
| (Some(_), None, None, None)
| (None, Some(_), None, None) => (args.target.clone(), args.base.clone(), None, None),
| (None, Some(_), None, None) => {
(args.target.clone(), args.base.clone(), None, None, BTreeMap::new())
}
(None, None, p, u) => {
let project = match p {
Some(project) => project.clone(),
@@ -95,7 +99,7 @@ pub fn run(args: Args) -> Result<()> {
let Some((project_config, project_config_info)) =
objdiff_core::config::try_project_config(project.as_ref())
else {
bail!("Project config not found in {}", &project)
bail!("Project config not found in {}", project)
};
let project_config = project_config.with_context(|| {
format!("Reading project config {}", project_config_info.path.display())
@@ -163,15 +167,24 @@ pub fn run(args: Args) -> Result<()> {
let unit_options = units.get(unit_idx).and_then(|u| u.options().cloned());
let target_path = object.target_path.clone();
let base_path = object.base_path.clone();
(target_path, base_path, Some(project_config), unit_options)
let symbol_mappings = object.symbol_mappings.clone();
(target_path, base_path, Some(project_config), unit_options, symbol_mappings)
}
_ => bail!("Either target and base or project and unit must be specified"),
};
if let Some(output) = &args.output {
run_oneshot(&args, output, target_path.as_deref(), base_path.as_deref(), unit_options)
run_oneshot(
&args,
output,
target_path.as_deref(),
base_path.as_deref(),
project_config.as_ref(),
unit_options,
&symbol_mappings,
)
} else {
run_interactive(args, target_path, base_path, project_config, unit_options)
run_interactive(args, target_path, base_path, project_config, unit_options, symbol_mappings)
}
}
@@ -180,10 +193,13 @@ fn run_oneshot(
output: &Utf8PlatformPath,
target_path: Option<&Utf8PlatformPath>,
base_path: Option<&Utf8PlatformPath>,
project_config: Option<&ProjectConfig>,
unit_options: Option<ProjectOptions>,
symbol_mappings: &BTreeMap<String, String>,
) -> Result<()> {
let output_format = OutputFormat::from_option(args.format.as_deref())?;
let (diff_config, mapping_config) = build_config_from_args(args, None, unit_options.as_ref())?;
let (diff_config, mapping_config) =
build_config_from_args(args, project_config, unit_options.as_ref(), symbol_mappings)?;
let target = target_path
.map(|p| {
obj::read::read(p.as_ref(), &diff_config, DiffSide::Target)
@@ -209,6 +225,7 @@ fn build_config_from_args(
args: &Args,
project_config: Option<&ProjectConfig>,
unit_options: Option<&ProjectOptions>,
symbol_mappings: &BTreeMap<String, String>,
) -> Result<(DiffObjConfig, MappingConfig)> {
let mut diff_config = DiffObjConfig::default();
if let Some(options) = project_config.and_then(|config| config.options.as_ref()) {
@@ -218,7 +235,11 @@ fn build_config_from_args(
apply_project_options(&mut diff_config, options)?;
}
apply_config_args(&mut diff_config, &args.config)?;
Ok((diff_config, MappingConfig::default()))
Ok((diff_config, MappingConfig {
mappings: symbol_mappings.clone(),
selecting_left: None,
selecting_right: None,
}))
}
pub struct AppState {
@@ -279,6 +300,7 @@ pub struct ObjectConfig {
pub base_path: Option<Utf8PlatformPathBuf>,
pub metadata: ProjectObjectMetadata,
pub complete: Option<bool>,
pub symbol_mappings: BTreeMap<String, String>,
}
impl ObjectConfig {
@@ -308,6 +330,7 @@ impl ObjectConfig {
base_path,
metadata: object.metadata.clone().unwrap_or_default(),
complete: object.complete(),
symbol_mappings: object.symbol_mappings.clone().unwrap_or_default(),
}
}
}
@@ -358,12 +381,17 @@ fn run_interactive(
base_path: Option<Utf8PlatformPathBuf>,
project_config: Option<ProjectConfig>,
unit_options: Option<ProjectOptions>,
symbol_mappings: BTreeMap<String, String>,
) -> Result<()> {
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, project_config.as_ref(), unit_options.as_ref())?;
let (diff_obj_config, mapping_config) = build_config_from_args(
&args,
project_config.as_ref(),
unit_options.as_ref(),
&symbol_mappings,
)?;
let mut state = AppState {
jobs: Default::default(),
waker: Default::default(),
+5 -1
View File
@@ -210,7 +210,11 @@ fn report_object(
}
_ => {}
}
let mapping_config = diff::MappingConfig::default();
let mapping_config = diff::MappingConfig {
mappings: object.symbol_mappings.clone(),
selecting_left: None,
selecting_right: None,
};
let target = object
.target_path
.as_ref()
+1
View File
@@ -1,3 +1,4 @@
#![allow(unused)] // TODO
use core::cmp::Ordering;
use anyhow::Result;
+1
View File
@@ -1,3 +1,4 @@
#![allow(unused)] // TODO
use anyhow::Result;
use crossterm::event::Event;
use ratatui::Frame;
+11 -11
View File
@@ -128,16 +128,16 @@ features = ["all"]
anyhow = { version = "1.0", default-features = false }
filetime = { version = "0.2", optional = true }
flagset = { version = "0.4", default-features = false, optional = true }
itertools = { version = "0.14", default-features = false, features = ["use_alloc"] }
itertools = { version = "0.15", default-features = false, features = ["use_alloc"] }
log = { version = "0.4", default-features = false, optional = true }
memmap2 = { version = "0.9", optional = true }
num-traits = { version = "0.2", default-features = false, optional = true }
object = { version = "0.37", default-features = false, features = ["read_core", "elf", "coff"] }
pbjson = { version = "0.8", default-features = false, optional = true }
object = { version = "0.39.1", default-features = false, features = ["read_core", "elf", "coff"] }
pbjson = { version = "0.9", default-features = false, optional = true }
prost = { version = "0.14", default-features = false, features = ["derive"], optional = true }
regex = { version = "1.12", default-features = false, features = [], optional = true }
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
similar = { git = "https://github.com/encounter/similar.git", branch = "no_std", default-features = false, features = ["hashbrown"], optional = true }
similar = { version = "3.1", default-features = false, features = ["hashbrown"], optional = true }
typed-path = { version = "0.12", default-features = false, optional = true }
# config
@@ -146,7 +146,7 @@ semver = { version = "1.0", default-features = false, optional = true }
serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true }
# dwarf
gimli = { git = "https://github.com/gimli-rs/gimli", rev = "7335f00e7c39fd501511584fefb0ba974117c950", default-features = false, features = ["read"], optional = true }
gimli = { version = "0.34.0", default-features = false, features = ["read"], optional = true }
typed-arena = { version = "2.0", default-features = false, optional = true }
# ppc
@@ -155,7 +155,7 @@ powerpc = { version = "0.4", optional = true }
rlwinmdec = { version = "1.1", optional = true }
# mips
rabbitizer = { version = "2.0.0-alpha.7", default-features = false, features = ["all_extensions"], optional = true }
rabbitizer = { version = "2.0.0-alpha.9", default-features = false, features = ["all_extensions"], optional = true }
# x86
iced-x86 = { version = "1.21", default-features = false, features = ["decoder", "intel", "gas", "masm", "nasm", "exhaustive_enums", "no_std"], optional = true }
@@ -187,17 +187,17 @@ winapi = { version = "0.3", optional = true, features = ["winbase"] }
# 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 }
reqwest = { version = "0.13", default-features = false, features = ["blocking", "json", "multipart", "rustls"], optional = true }
self_update = { version = "1.0.0-rc.3", default-features = false, features = ["reqwest", "rustls", "github"], 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 }
reqwest = { version = "0.13", default-features = false, features = ["blocking", "json", "multipart", "default-tls"], optional = true }
self_update = { version = "1.0.0-rc.3", default-features = false, features = ["reqwest", "native-tls", "github"], optional = true }
[build-dependencies]
heck = { version = "0.5", optional = true }
pbjson-build = { version = "0.8", optional = true }
pbjson-build = { version = "0.9", optional = true }
prettyplease = { version = "0.2", optional = true }
proc-macro2 = { version = "1.0", optional = true }
prost-build = { version = "0.14", optional = true }
+8 -1
View File
@@ -345,6 +345,13 @@
"name": "Register '$' prefix",
"description": "Display MIPS register names with a '$' prefix."
},
{
"id": "mips.prodgSnAsInvertedRegs",
"type": "boolean",
"default": false,
"name": "ProDG SN AS VU register order",
"description": "Changes the register order for specific R5900EE instructions to match the syntax used by the SN AS from ProDG (-snas flag)."
},
{
"id": "ppc.calculatePoolRelocations",
"type": "boolean",
@@ -409,7 +416,7 @@
{
"id": "mips",
"name": "MIPS",
"properties": ["mips.abi", "mips.instrCategory", "mips.registerPrefix"]
"properties": ["mips.abi", "mips.instrCategory", "mips.registerPrefix", "mips.prodgSnAsInvertedRegs"]
},
{
"id": "ppc",
+22 -8
View File
@@ -183,7 +183,7 @@ impl Arch for ArchArm {
let mapping_symbols = self
.disasm_modes
.get(&section_index)
.map(|x| x.as_slice())
.map(|x| if x.is_empty() { &fallback_mappings } else { x.as_slice() })
.unwrap_or(&fallback_mappings);
let first_mapping_idx = mapping_symbols
.binary_search_by_key(&start_addr, |x| x.address)
@@ -343,6 +343,15 @@ impl Arch for ArchArm {
// Handle ELF implicit relocations
object::RelocationFlags::Elf { r_type } => {
if relocation.has_implicit_addend() {
// R_ARM_V4BX marks a `bx` instruction so the linker can optionally
// rewrite it for ARMv4 interworking. Drops the addend since it's meaningless.
if r_type == elf::R_ARM_V4BX {
return Ok(Some(RelocationOverride {
target: RelocationOverrideTarget::Skip,
addend: 0,
}));
}
let section_data = section.data()?;
let address = address as usize;
let addend = match r_type {
@@ -352,7 +361,7 @@ impl Arch for ArchArm {
| elf::R_ARM_CALL
| elf::R_ARM_JUMP24 => {
let data = section_data[address..address + 4].try_into()?;
let addend = self.endianness.read_i32_bytes(data);
let addend = self.endianness.read_i32(data);
let imm24 = addend & 0xffffff;
(imm24 << 2) << 8 >> 8
}
@@ -360,9 +369,9 @@ impl Arch for ArchArm {
// Thumb calls
elf::R_ARM_THM_PC22 | elf::R_ARM_THM_XPC22 => {
let data = section_data[address..address + 2].try_into()?;
let high = self.endianness.read_i16_bytes(data) as i32;
let high = self.endianness.read_i16(data) as i32;
let data = section_data[address + 2..address + 4].try_into()?;
let low = self.endianness.read_i16_bytes(data) as i32;
let low = self.endianness.read_i16(data) as i32;
let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff);
(imm22 << 1) << 9 >> 9
@@ -371,7 +380,7 @@ impl Arch for ArchArm {
// Thumb unconditional branch (B, 11-bit offset)
elf::R_ARM_THM_PC11 => {
let data = section_data[address..address + 2].try_into()?;
let insn = self.endianness.read_u16_bytes(data) as i32;
let insn = self.endianness.read_u16(data) as i32;
let imm11 = insn & 0x7ff;
(imm11 << 1) << 20 >> 20
}
@@ -379,7 +388,7 @@ impl Arch for ArchArm {
// Thumb conditional branch (B<cond>, 8-bit offset)
elf::R_ARM_THM_PC9 => {
let data = section_data[address..address + 2].try_into()?;
let insn = self.endianness.read_u16_bytes(data) as i32;
let insn = self.endianness.read_u16(data) as i32;
let imm8 = insn & 0xff;
(imm8 << 1) << 23 >> 23
}
@@ -387,7 +396,7 @@ impl Arch for ArchArm {
// Data
elf::R_ARM_ABS32 => {
let data = section_data[address..address + 4].try_into()?;
self.endianness.read_i32_bytes(data)
self.endianness.read_i32(data)
}
flags => bail!("Unsupported ARM implicit relocation {flags:?}"),
@@ -419,6 +428,7 @@ impl Arch for ArchArm {
elf::R_ARM_CALL => Some("R_ARM_CALL"),
elf::R_ARM_THM_PC11 => Some("R_ARM_THM_PC11"),
elf::R_ARM_THM_PC9 => Some("R_ARM_THM_PC9"),
elf::R_ARM_V4BX => Some("R_ARM_V4BX"),
_ => None,
},
_ => None,
@@ -471,9 +481,13 @@ impl Arch for ArchArm {
while next_address >= symbol.address + 2
&& let Some(data) = section.data_range(next_address - 2, 2)
&& data == [0u8; 2]
&& section.relocation_at(next_address - 2, 2).is_none()
{
next_address -= 2;
if let Some(relocation) = section.relocation_at(next_address, 2) {
// Avoid cutting trailing relocations in half.
next_address += self.data_reloc_size(relocation.flags) as u64;
break;
}
}
Ok(next_address.saturating_sub(symbol.address))
}
+11 -9
View File
@@ -77,7 +77,7 @@ impl ArchMips {
.and_then(|section| section.data().ok())
.and_then(|data| data.get(0x14..0x18))
.and_then(|s| s.try_into().ok())
.map(|bytes| endianness.read_i32_bytes(bytes))
.map(|bytes| endianness.read_i32(bytes))
.unwrap_or(0);
// Parse all relocations to pair R_MIPS_HI16 and R_MIPS_LO16. Since the instructions only
@@ -98,13 +98,13 @@ impl ArchMips {
match reloc.flags() {
object::RelocationFlags::Elf { r_type: elf::R_MIPS_HI16 } => {
let code = data[addr as usize..addr as usize + 4].try_into()?;
let addend = ((endianness.read_u32_bytes(code) & 0x0000FFFF) << 16) as i32;
let addend = ((endianness.read_u32(code) & 0x0000FFFF) << 16) as i32;
last_hi = Some(addr);
last_hi_addend = addend;
}
object::RelocationFlags::Elf { r_type: elf::R_MIPS_LO16 } => {
let code = data[addr as usize..addr as usize + 4].try_into()?;
let addend = (endianness.read_u32_bytes(code) & 0x0000FFFF) as i16 as i32;
let addend = (endianness.read_u32(code) & 0x0000FFFF) as i16 as i32;
let full_addend = (last_hi_addend + addend) as i64;
if let Some(hi_addr) = last_hi.take() {
addends.insert(hi_addr, full_addend);
@@ -188,6 +188,7 @@ impl ArchMips {
rabbitizer::InstructionDisplayFlags::default()
.with_unknown_instr_comment(false)
.with_use_dollar(diff_config.mips_register_prefix)
.with_r5900ee_prodg_sn_as_inverted_regs(diff_config.mips_prodg_sn_as_inverted_regs)
}
fn parse_ins_ref(
@@ -197,7 +198,7 @@ impl ArchMips {
diff_config: &DiffObjConfig,
) -> Result<rabbitizer::Instruction> {
Ok(rabbitizer::Instruction::new(
self.endianness.read_u32_bytes(code.try_into()?),
self.endianness.read_u32(code.try_into()?),
Vram::new(ins_ref.address as u32),
self.instruction_flags(diff_config),
))
@@ -216,8 +217,8 @@ impl Arch for ArchMips {
let instruction_flags = self.instruction_flags(diff_config);
let mut ops = Vec::<InstructionRef>::with_capacity(code.len() / 4);
let mut cur_addr = address as u32;
for chunk in code.chunks_exact(4) {
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
for chunk in code.as_chunks::<4>().0 {
let code = self.endianness.read_u32(*chunk);
let instruction =
rabbitizer::Instruction::new(code, Vram::new(cur_addr), instruction_flags);
let opcode = instruction.opcode() as u16;
@@ -237,7 +238,8 @@ impl Arch for ArchMips {
let instruction = self.parse_ins_ref(resolved.ins_ref, resolved.code, diff_config)?;
let display_flags = self.instruction_display_flags(diff_config);
let opcode = instruction.opcode();
cb(InstructionPart::opcode(opcode.name(), opcode as u16))?;
let mnemonic = instruction.mnemonic_display(&display_flags).to_string();
cb(InstructionPart::opcode(mnemonic, opcode as u16))?;
push_args(&instruction, resolved.relocation, &display_flags, cb)?;
Ok(())
}
@@ -269,7 +271,7 @@ impl Arch for ArchMips {
let data = section.data()?;
let code = self
.endianness
.read_u32_bytes(data[address as usize..address as usize + 4].try_into()?);
.read_u32(data[address as usize..address as usize + 4].try_into()?);
let addend = match r_type {
elf::R_MIPS_32 => code as i64,
elf::R_MIPS_26 => ((code & 0x03FFFFFF) << 2) as i64,
@@ -366,7 +368,7 @@ impl Arch for ArchMips {
&& new_address >= symbol.address + 4
&& let Some(data) = section.data_range(new_address - 4, 4)
&& let instruction = rabbitizer::Instruction::new(
self.endianness.read_u32_bytes(data.try_into().unwrap()),
self.endianness.read_u32(data.try_into().unwrap()),
Vram::new((new_address - 4) as u32),
self.default_instruction_flags(),
)
+3 -3
View File
@@ -151,7 +151,7 @@ impl DataType {
}
}
DataType::Int16 => {
let i = endian.read_i16_bytes(bytes.try_into().unwrap());
let i = endian.read_i16(bytes.try_into().unwrap());
strs.push(LiteralInfo { literal: format!("{i:#x}"), ..Default::default() });
if i < 0 {
@@ -162,7 +162,7 @@ impl DataType {
}
}
DataType::Int32 => {
let i = endian.read_i32_bytes(bytes.try_into().unwrap());
let i = endian.read_i32(bytes.try_into().unwrap());
strs.push(LiteralInfo { literal: format!("{i:#x}"), ..Default::default() });
if i < 0 {
@@ -173,7 +173,7 @@ impl DataType {
}
}
DataType::Int64 => {
let i = endian.read_i64_bytes(bytes.try_into().unwrap());
let i = endian.read_i64(bytes.try_into().unwrap());
strs.push(LiteralInfo { literal: format!("{i:#x}"), ..Default::default() });
if i < 0 {
+4 -4
View File
@@ -35,8 +35,8 @@ impl Arch for ArchSuperH {
let mut ops = Vec::<InstructionRef>::with_capacity(code.len() / 2);
let mut offset = address;
for chunk in code.chunks_exact(2) {
let opcode = u16::from_be_bytes(chunk.try_into().unwrap());
for chunk in code.as_chunks::<2>().0 {
let opcode = u16::from_be_bytes(*chunk);
let mut parts: Vec<InstructionPart> = vec![];
let resolved: ResolvedInstructionRef = Default::default();
let mut branch_dest: Option<u64> = None;
@@ -80,8 +80,8 @@ impl Arch for ArchSuperH {
let mut data_offsets = BTreeMap::<u64, DataInfo>::new();
let mut pos: u64 = 0;
for chunk in symbol_data.chunks_exact(2) {
let opcode = u16::from_be_bytes(chunk.try_into().unwrap());
for chunk in symbol_data.as_chunks::<2>().0 {
let opcode = u16::from_be_bytes(*chunk);
// mov.w
if (opcode & 0xf000) == 0x9000 {
let target = (opcode as u64 & 0xff) * 2 + 4 + pos;
+34 -7
View File
@@ -80,7 +80,14 @@ impl ArchX86 {
},
Architecture::X86_64 => match flags {
RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32 => Some(4),
pe::IMAGE_REL_AMD64_ADDR32NB
| pe::IMAGE_REL_AMD64_REL32
| pe::IMAGE_REL_AMD64_REL32_1
| pe::IMAGE_REL_AMD64_REL32_2
| pe::IMAGE_REL_AMD64_REL32_3
| pe::IMAGE_REL_AMD64_REL32_4
| pe::IMAGE_REL_AMD64_REL32_5
| pe::IMAGE_REL_AMD64_SECREL => Some(4),
pe::IMAGE_REL_AMD64_ADDR64 => Some(8),
_ => None,
},
@@ -181,6 +188,12 @@ impl Arch for ArchX86 {
branch_dest,
});
}
// Strip trailing int3 instructions (alignment padding) for x64
if matches!(self.arch, Architecture::X86_64) {
while out.last().is_some_and(|i| i.opcode == iced_x86::Mnemonic::Int3 as u16) {
out.pop();
}
}
Ok(out)
}
@@ -193,8 +206,8 @@ impl Arch for ArchX86 {
if resolved.ins_ref.opcode == OPCODE_DATA {
let (mnemonic, imm) = match resolved.ins_ref.size {
1 => (".byte", resolved.code[0] as u64),
2 => (".word", self.endianness.read_u16_bytes(resolved.code.try_into()?) as u64),
4 => (".dword", self.endianness.read_u32_bytes(resolved.code.try_into()?) as u64),
2 => (".word", self.endianness.read_u16(resolved.code.try_into()?) as u64),
4 => (".dword", self.endianness.read_u32(resolved.code.try_into()?) as u64),
_ => bail!("Unsupported x86 inline data size {}", resolved.ins_ref.size),
};
cb(InstructionPart::opcode(mnemonic, OPCODE_DATA))?;
@@ -288,24 +301,32 @@ impl Arch for ArchX86 {
} => {
let data =
section.data()?[address as usize..address as usize + 4].try_into()?;
self.endianness.read_i32_bytes(data) as i64
self.endianness.read_i32(data) as i64
}
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
},
Architecture::X86_64 => match relocation.flags() {
object::RelocationFlags::Coff {
typ: pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32,
typ:
pe::IMAGE_REL_AMD64_ADDR32NB
| pe::IMAGE_REL_AMD64_REL32
| pe::IMAGE_REL_AMD64_REL32_1
| pe::IMAGE_REL_AMD64_REL32_2
| pe::IMAGE_REL_AMD64_REL32_3
| pe::IMAGE_REL_AMD64_REL32_4
| pe::IMAGE_REL_AMD64_REL32_5
| pe::IMAGE_REL_AMD64_SECREL,
}
| object::RelocationFlags::Elf { r_type: elf::R_X86_64_32 | elf::R_X86_64_PC32 } => {
let data =
section.data()?[address as usize..address as usize + 4].try_into()?;
self.endianness.read_i32_bytes(data) as i64
self.endianness.read_i32(data) as i64
}
object::RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 }
| object::RelocationFlags::Elf { r_type: elf::R_X86_64_64 } => {
let data =
section.data()?[address as usize..address as usize + 8].try_into()?;
self.endianness.read_i64_bytes(data)
self.endianness.read_i64(data)
}
flags => bail!("Unsupported x86-64 implicit relocation {flags:?}"),
},
@@ -337,6 +358,12 @@ impl Arch for ArchX86 {
pe::IMAGE_REL_AMD64_ADDR64 => Some("IMAGE_REL_AMD64_ADDR64"),
pe::IMAGE_REL_AMD64_ADDR32NB => Some("IMAGE_REL_AMD64_ADDR32NB"),
pe::IMAGE_REL_AMD64_REL32 => Some("IMAGE_REL_AMD64_REL32"),
pe::IMAGE_REL_AMD64_REL32_1 => Some("IMAGE_REL_AMD64_REL32_1"),
pe::IMAGE_REL_AMD64_REL32_2 => Some("IMAGE_REL_AMD64_REL32_2"),
pe::IMAGE_REL_AMD64_REL32_3 => Some("IMAGE_REL_AMD64_REL32_3"),
pe::IMAGE_REL_AMD64_REL32_4 => Some("IMAGE_REL_AMD64_REL32_4"),
pe::IMAGE_REL_AMD64_REL32_5 => Some("IMAGE_REL_AMD64_REL32_5"),
pe::IMAGE_REL_AMD64_SECREL => Some("IMAGE_REL_AMD64_SECREL"),
_ => None,
},
_ => None,
+1 -1
View File
@@ -1,4 +1,4 @@
#![allow(clippy::needless_lifetimes)] // Generated serde code
#![allow(clippy::useless_borrows_in_formatting)] // Generated serde code
use alloc::{
string::{String, ToString},
+1 -1
View File
@@ -1,4 +1,4 @@
#![allow(clippy::needless_lifetimes)] // Generated serde code
#![allow(clippy::useless_borrows_in_formatting)] // Generated serde code
use alloc::{
string::{String, ToString},
+3 -3
View File
@@ -2,7 +2,7 @@ use alloc::{vec, vec::Vec};
use core::{cmp::Ordering, ops::Range};
use anyhow::{Result, anyhow};
use similar::{Algorithm, capture_diff_slices, get_diff_ratio};
use similar::{Algorithm, capture_diff_slices, diff_ratio};
use super::{
DataDiff, DataDiffKind, DataDiffRow, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff,
@@ -79,7 +79,7 @@ pub fn resolve_relocation<'obj>(
/// Compares the bytes within a certain data range.
fn diff_data_range(left_data: &[u8], right_data: &[u8]) -> (f32, Vec<DataDiff>, Vec<DataDiff>) {
let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
let bytes_match_ratio = get_diff_ratio(&ops, left_data.len(), right_data.len());
let bytes_match_ratio = diff_ratio(&ops, left_data.len(), right_data.len());
let mut left_data_diff = Vec::<DataDiff>::new();
let mut right_data_diff = Vec::<DataDiff>::new();
@@ -552,7 +552,7 @@ pub fn diff_bss_section(
.filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| (a, s.size)))
.collect::<Vec<_>>();
let ops = capture_diff_slices(Algorithm::Patience, &left_sizes, &right_sizes);
let mut match_percent = get_diff_ratio(&ops, left_sizes.len(), right_sizes.len()) * 100.0;
let mut match_percent = diff_ratio(&ops, left_sizes.len(), right_sizes.len()) * 100.0;
// Use the highest match percent between two options:
// - Left symbols matching right symbols by name
+5 -8
View File
@@ -1,10 +1,7 @@
use std::{sync::mpsc::Receiver, task::Waker};
use anyhow::{Context, Result};
use self_update::{
cargo_crate_version,
update::{Release, ReleaseUpdate},
};
use self_update::update::{Release, ReleaseUpdate};
use crate::jobs::{Job, JobContext, JobResult, JobState, start_job, update_status};
@@ -26,13 +23,13 @@ fn run_check_update(
) -> Result<Box<CheckUpdateResult>> {
update_status(context, "Fetching latest release".to_string(), 0, 1, &cancel)?;
let updater = (config.build_updater)().context("Failed to create release updater")?;
let latest_release = updater.get_latest_release()?;
let update_available =
self_update::version::bump_is_greater(cargo_crate_version!(), &latest_release.version)?;
let releases = updater.get_latest_release()?;
let update_available = releases.is_update_available()?;
let latest_release = releases.latest().context("No release found")?.clone();
// Find the binary name in the release assets
let mut found_binary = None;
for bin_name in &config.bin_names {
if latest_release.assets.iter().any(|a| &a.name == bin_name) {
if latest_release.assets().iter().any(|a| a.name() == bin_name) {
found_binary = Some(bin_name.clone());
break;
}

Some files were not shown because too many files have changed in this diff Show More