Compare commits

...

21 Commits

Author SHA1 Message Date
Luke Street ffb38d1bb0 Fix cargo deny advisories 2025-03-09 22:59:42 -06:00
Luke Street d56dda72f0 Version v3.0.0-beta.2 2025-03-09 22:56:28 -06:00
Luke Street c6971f3f2d Add initial support for x86-64 relocations 2025-03-09 22:51:43 -06:00
Luke Street 3965a035fa objdiff-cli diff: Show build errors/log 2025-03-09 22:51:43 -06:00
Luke Street f1fc29f77e Split report changes into separate proto 2025-03-08 14:39:15 -07:00
Luke Street 7c4f1c5d13 Fix left/right arch mismatches in diff code 2025-03-08 10:44:44 -07:00
Luke Street fa4a6cadbb Downgrade objdiff-wasm Rust edition temporarily 2025-03-04 23:27:17 -07:00
Luke Street 799971d54e Migrate to Rust edition 2024 2025-03-04 22:31:38 -07:00
Luke Street 8eef37e8df Version v3.0.0-beta.1 2025-03-04 22:24:55 -07:00
Luke Street 5f36916087 Fix unintended unwrap in load_font_if_needed 2025-03-04 22:24:20 -07:00
Luke Street ee667a2dde Update config-schema.json: PPC -> PowerPC 2025-03-04 22:23:45 -07:00
LagoLunatic cf5fc54cfa PPC: Reimplement pooled data reference calculation (#167)
* PPC: Calculate pooled relocations

Reimplements #140

The relocations are now generated when the object is first read in `parse`, right after the real relocations are read.

`resolve_relocation` was changed to take `obj.symbols` instead of `obj` as an argument, because `obj` itself doesn't exist yet at the time the relocations are being read.

* Improve readability of PPC pool relocs code

* Fix regression causing extern pool relocs to be ignored

* Fix showing incorrect diff when diffing weak stripped symbol with an addend

This is a regression that was introduced by #158 diffing addends in addition to symbol names. But it's not really a bug in that PR, rather it seems like I simply never added the offset into the addend when creating a fake pool relocation for an extern symbol. So this commit fixes that root issue instead.

* Add PPC "Calculate pooled data references" option

* Fix objdiff-wasm compilation errors

* Update PPC test snapshots
2025-03-04 20:40:34 -07:00
Luke Street 1cdfa1e857 Update rabbitizer & utilize use_dollar option 2025-03-04 09:38:59 -07:00
Luke Street 3f157f33a5 Reorder tooltip/context items slightly 2025-03-04 09:10:54 -07:00
LagoLunatic a1ea2919f8 Reimplement function data value tooltips, function data value diffing, and data relocation diffing (#166)
* Show data literal values on instruction hover

Reimplements #108

* Show reloc diffs in func view when data's content differs

Reimplements #153

* Data diff view: Show relocs on hover and in context menu

This reimplements #154

Note that colorizing the text depending on the kind of diff has still not been reimplemented yet

* Fix up some comments
2025-03-04 08:56:46 -07:00
Luke Street 9c31c82a37 cargo fmt 2025-03-03 21:08:36 -07:00
Luke Street 4f34dfa194 Don't infer sizes for labels within another symbol 2025-03-03 21:01:22 -07:00
Luke Street ae6d37a10b Version v3.0.0-alpha.2 2025-03-03 18:25:38 -07:00
Luke Street 06e54f3456 cargo fmt 2025-03-03 17:16:35 -07:00
Luke Street 6ed07bfaf1 MIPS relocation pairing, '$' prefix option & fixes
Implements MIPS relocation pairing logic.
New option for "Register '$' prefix", off by default.
Fixes various regressions introduced by refactoring.

Resolves #122
Resolves #156
2025-03-03 17:14:32 -07:00
Luke Street 80e939653a Build objdiff-gui against older glibc
Resolves #165
2025-03-03 13:34:05 -07:00
80 changed files with 8929 additions and 826 deletions
+18 -6
View File
@@ -148,7 +148,7 @@ jobs:
python3 -m venv .venv
. .venv/bin/activate
echo PATH=$PATH >> $GITHUB_ENV
pip install ziglang==0.13.0 cargo-zigbuild==0.19.1
pip install ziglang==0.13.0.post1 cargo-zigbuild==0.19.8
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
@@ -178,21 +178,26 @@ jobs:
matrix:
include:
- platform: ubuntu-latest
target: x86_64-unknown-linux-gnu
target: x86_64-unknown-linux-gnu.2.31
target_base: x86_64-unknown-linux-gnu
name: linux-x86_64
packages: libgtk-3-dev
build: zigbuild
features: default
- platform: windows-latest
target: x86_64-pc-windows-msvc
name: windows-x86_64
build: build
features: default
- platform: macos-latest
target: x86_64-apple-darwin
name: macos-x86_64
build: build
features: default
- platform: macos-latest
target: aarch64-apple-darwin
name: macos-arm64
build: build
features: default
fail-fast: false
runs-on: ${{ matrix.platform }}
@@ -204,25 +209,32 @@ jobs:
sudo apt-get -y install ${{ matrix.packages }}
- name: Checkout
uses: actions/checkout@v4
- name: Install cargo-zigbuild
if: matrix.build == 'zigbuild'
run: |
python3 -m venv .venv
. .venv/bin/activate
echo PATH=$PATH >> $GITHUB_ENV
pip install ziglang==0.13.0.post1 cargo-zigbuild==0.19.8
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
targets: ${{ matrix.target_base || matrix.target }}
- name: Cache Rust workspace
uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.target }}
- name: Cargo build
run: >
cargo build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
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
with:
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
path: |
target/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}
target/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
target/${{ matrix.target_base || matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}
target/${{ matrix.target_base || matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
if-no-files-found: error
build-wasm:
Generated
+9 -12
View File
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "ab_glyph"
@@ -2652,7 +2652,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
"windows-targets 0.52.6",
"windows-targets 0.48.5",
]
[[package]]
@@ -3268,7 +3268,7 @@ dependencies = [
[[package]]
name = "objdiff-cli"
version = "3.0.0-alpha.1"
version = "3.0.0-beta.2"
dependencies = [
"anyhow",
"argp",
@@ -3291,7 +3291,7 @@ dependencies = [
[[package]]
name = "objdiff-core"
version = "3.0.0-alpha.1"
version = "3.0.0-beta.2"
dependencies = [
"anyhow",
"arm-attr",
@@ -3344,13 +3344,12 @@ dependencies = [
[[package]]
name = "objdiff-gui"
version = "3.0.0-alpha.1"
version = "3.0.0-beta.2"
dependencies = [
"anyhow",
"cfg-if",
"const_format",
"cwdemangle",
"cwextab",
"dirs 6.0.0",
"eframe",
"egui",
@@ -3370,8 +3369,6 @@ dependencies = [
"rlwinmdec",
"ron",
"serde",
"serde_json",
"shell-escape",
"tauri-winres",
"time",
"tracing-subscriber",
@@ -3383,7 +3380,7 @@ dependencies = [
[[package]]
name = "objdiff-wasm"
version = "3.0.0-alpha.1"
version = "3.0.0-beta.2"
dependencies = [
"log",
"objdiff-core",
@@ -3897,7 +3894,7 @@ dependencies = [
[[package]]
name = "rabbitizer"
version = "2.0.0-dev0"
source = "git+https://github.com/Decompollaborate/rabbitizer.git?branch=🦀#706fd145b788ec3d068d55904dd112c7989e0412"
source = "git+https://github.com/Decompollaborate/rabbitizer.git?branch=%F0%9F%A6%80#06dc8b6c826c3d60e112d4c2cd70aa54e308be12"
dependencies = [
"bitflags 2.9.0",
]
@@ -4208,9 +4205,9 @@ dependencies = [
[[package]]
name = "ring"
version = "0.17.11"
version = "0.17.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73"
checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee"
dependencies = [
"cc",
"cfg-if",
+4 -4
View File
@@ -5,7 +5,7 @@ members = [
"objdiff-gui",
"objdiff-wasm",
]
resolver = "2"
resolver = "3"
[profile.release-lto]
inherits = "release"
@@ -14,9 +14,9 @@ strip = "debuginfo"
codegen-units = 1
[workspace.package]
version = "3.0.0-alpha.1"
version = "3.0.0-beta.2"
authors = ["Luke Street <luke@street.dev>"]
edition = "2021"
edition = "2024"
license = "MIT OR Apache-2.0"
repository = "https://github.com/encounter/objdiff"
rust-version = "1.82"
rust-version = "1.85"
+1
View File
@@ -73,6 +73,7 @@ ignore = [
#{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" },
#"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish
#{ 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" },
]
# 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.
+1 -1
View File
@@ -4,7 +4,7 @@
//! For now, this only adds a --version/-V option which causes early-exit.
use std::ffi::OsStr;
use argp::{parser::ParseGlobalOptions, EarlyExit, FromArgs, TopLevelCommand};
use argp::{EarlyExit, FromArgs, TopLevelCommand, parser::ParseGlobalOptions};
struct ArgsOrVersion<T>(T)
where T: FromArgs;
+25 -18
View File
@@ -3,40 +3,39 @@ use std::{
mem,
str::FromStr,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
atomic::{AtomicBool, Ordering},
},
task::{Wake, Waker},
time::Duration,
};
use anyhow::{anyhow, bail, Context, Result};
use anyhow::{Context, Result, anyhow, bail};
use argp::FromArgs;
use crossterm::{
event,
event::{DisableMouseCapture, EnableMouseCapture},
terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, SetTitle,
EnterAlternateScreen, LeaveAlternateScreen, SetTitle, disable_raw_mode, enable_raw_mode,
},
};
use objdiff_core::{
bindings::diff::DiffResult,
build::{
watcher::{create_watcher, Watcher},
BuildConfig,
BuildConfig, BuildStatus,
watcher::{Watcher, create_watcher},
},
config::{
build_globset,
ProjectConfig, ProjectObject, ProjectObjectMetadata, build_globset,
path::{check_path_buf, platform_path, platform_path_serde_option},
ProjectConfig, ProjectObject, ProjectObjectMetadata,
},
diff::{
self, ConfigEnum, ConfigPropertyId, ConfigPropertyKind, DiffObjConfig, MappingConfig,
ObjectDiff,
},
jobs::{
objdiff::{start_build, ObjDiffConfig},
Job, JobQueue, JobResult,
objdiff::{ObjDiffConfig, start_build},
},
obj::{self, Object},
};
@@ -45,10 +44,10 @@ use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
use crate::{
util::{
output::{write_output, OutputFormat},
output::{OutputFormat, write_output},
term::crossterm_panic_handler,
},
views::{function_diff::FunctionDiffUi, EventControlFlow, EventResult, UiView},
views::{EventControlFlow, EventResult, UiView, function_diff::FunctionDiffUi},
};
#[derive(FromArgs, PartialEq, Debug)]
@@ -252,6 +251,8 @@ pub struct AppState {
pub project_config: Option<ProjectConfig>,
pub target_path: Option<Utf8PlatformPathBuf>,
pub base_path: Option<Utf8PlatformPathBuf>,
pub left_status: Option<BuildStatus>,
pub right_status: Option<BuildStatus>,
pub left_obj: Option<(Object, ObjectDiff)>,
pub right_obj: Option<(Object, ObjectDiff)>,
pub prev_obj: Option<(Object, ObjectDiff)>,
@@ -349,6 +350,8 @@ impl AppState {
JobResult::None => unreachable!("Unexpected JobResult::None"),
JobResult::ObjDiff(result) => {
let result = result.unwrap();
self.left_status = Some(result.first_status);
self.right_status = Some(result.second_status);
self.left_obj = result.first_obj;
self.right_obj = result.second_obj;
self.reload_time = Some(result.time);
@@ -389,6 +392,8 @@ fn run_interactive(
project_config,
target_path,
base_path,
left_status: None,
right_status: None,
left_obj: None,
right_obj: None,
prev_obj: None,
@@ -426,15 +431,17 @@ fn run_interactive(
let mut result = EventResult { redraw: true, ..Default::default() };
'outer: loop {
if result.redraw {
terminal.draw(|f| loop {
result.redraw = false;
view.draw(&state, f, &mut result);
result.click_xy = None;
if !result.redraw {
break;
terminal.draw(|f| {
loop {
result.redraw = false;
view.draw(&state, f, &mut result);
result.click_xy = None;
if !result.redraw {
break;
}
// Clear buffer on redraw
f.buffer_mut().reset();
}
// Clear buffer on redraw
f.buffer_mut().reset();
})?;
}
loop {
+6 -15
View File
@@ -1,12 +1,11 @@
use std::{collections::HashSet, fs::File, io::Read, time::Instant};
use anyhow::{bail, Context, Result};
use anyhow::{Context, Result, bail};
use argp::FromArgs;
use objdiff_core::{
bindings::report::{
ChangeItem, ChangeItemInfo, ChangeUnit, Changes, ChangesInput, Measures, Report,
ReportCategory, ReportItem, ReportItemMetadata, ReportUnit, ReportUnitMetadata,
REPORT_VERSION,
ChangeItem, ChangeItemInfo, ChangeUnit, Changes, ChangesInput, Measures, REPORT_VERSION,
Report, ReportCategory, ReportItem, ReportItemMetadata, ReportUnit, ReportUnitMetadata,
},
config::path::platform_path,
diff, obj,
@@ -19,7 +18,7 @@ use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
use crate::{
cmd::diff::ObjectConfig,
util::output::{write_output, OutputFormat},
util::output::{OutputFormat, write_output},
};
#[derive(FromArgs, PartialEq, Debug)]
@@ -206,11 +205,7 @@ fn report_object(
let section_match_percent = section_diff.match_percent.unwrap_or_else(|| {
// Support cases where we don't have a target object,
// assume complete means 100% match
if object.complete.unwrap_or(false) {
100.0
} else {
0.0
}
if object.complete.unwrap_or(false) { 100.0 } else { 0.0 }
});
sections.push(ReportItem {
name: section.name.clone(),
@@ -251,11 +246,7 @@ fn report_object(
let match_percent = symbol_diff.match_percent.unwrap_or_else(|| {
// Support cases where we don't have a target object,
// assume complete means 100% match
if object.complete.unwrap_or(false) {
100.0
} else {
0.0
}
if object.complete.unwrap_or(false) { 100.0 } else { 0.0 }
});
measures.fuzzy_match_percent += match_percent * symbol.size as f32;
measures.total_code += symbol.size;
+1 -1
View File
@@ -17,7 +17,7 @@ use anyhow::{Error, Result};
use argp::{FromArgValue, FromArgs};
use enable_ansi_support::enable_ansi_support;
use supports_color::Stream;
use tracing_subscriber::{filter::LevelFilter, EnvFilter};
use tracing_subscriber::{EnvFilter, filter::LevelFilter};
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
enum LogLevel {
+1 -1
View File
@@ -5,7 +5,7 @@ use std::{
path::Path,
};
use anyhow::{bail, Context, Result};
use anyhow::{Context, Result, bail};
use tracing::info;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
+1 -1
View File
@@ -3,7 +3,7 @@ use std::{io::stdout, panic};
use crossterm::{
cursor::Show,
event::DisableMouseCapture,
terminal::{disable_raw_mode, LeaveAlternateScreen},
terminal::{LeaveAlternateScreen, disable_raw_mode},
};
pub fn crossterm_panic_handler() {
+27 -4
View File
@@ -1,18 +1,19 @@
use core::cmp::Ordering;
use anyhow::{bail, Result};
use anyhow::Result;
use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind};
use objdiff_core::{
build::BuildStatus,
diff::{
display::{display_row, DiffText, DiffTextColor, HighlightKind},
DiffObjConfig, FunctionRelocDiffs, InstructionDiffKind, ObjectDiff, SymbolDiff,
display::{DiffText, DiffTextColor, HighlightKind, display_row},
},
obj::Object,
};
use ratatui::{
Frame,
prelude::*,
widgets::{Block, Borders, Clear, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState},
Frame,
};
use super::{EventControlFlow, EventResult, UiView};
@@ -126,6 +127,11 @@ impl UiView for FunctionDiffUi {
);
max_width = max_width.max(text.width());
left_text = Some(text);
} else if let Some(status) = &state.left_status {
let mut text = Text::default();
self.print_build_status(&mut text, status);
max_width = max_width.max(text.width());
left_text = Some(text);
}
let mut right_text = None;
@@ -155,6 +161,11 @@ impl UiView for FunctionDiffUi {
let rect = content_chunks[1].inner(Margin::new(1, 1));
self.print_margin(&mut text, symbol_diff, rect);
margin_text = Some(text);
} else if let Some(status) = &state.right_status {
let mut text = Text::default();
self.print_build_status(&mut text, status);
max_width = max_width.max(text.width());
right_text = Some(text);
}
let mut prev_text = None;
@@ -453,7 +464,7 @@ impl UiView for FunctionDiffUi {
}
(Some((_l, _ls, ld)), None) => ld.instruction_rows.len(),
(None, Some((_r, _rs, rd))) => rd.instruction_rows.len(),
(None, None) => bail!("Symbol not found: {}", self.symbol_name),
(None, None) => 0,
};
self.left_sym = left_sym;
self.right_sym = right_sym;
@@ -596,6 +607,18 @@ impl FunctionDiffUi {
}
}
}
fn print_build_status<'a>(&self, out: &mut Text<'a>, status: &'a BuildStatus) {
if !status.cmdline.is_empty() {
out.lines.push(Line::styled(status.cmdline.clone(), Style::new().fg(Color::LightBlue)));
}
for line in status.stdout.lines() {
out.lines.push(Line::styled(line, Style::new().fg(Color::White)));
}
for line in status.stderr.lines() {
out.lines.push(Line::styled(line, Style::new().fg(Color::Red)));
}
}
}
pub const COLOR_ROTATION: [Color; 7] = [
+9 -1
View File
@@ -19,7 +19,15 @@ fn compile_protos() {
.map(|m| m.modified().unwrap())
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
let mut run_protoc = false;
let proto_files = vec![root.join("report.proto")];
let proto_files = root
.read_dir()
.unwrap()
.filter_map(|e| {
let e = e.unwrap();
let path = e.path();
(path.extension() == Some(std::ffi::OsStr::new("proto"))).then_some(path)
})
.collect::<Vec<_>>();
for proto_file in &proto_files {
println!("cargo:rerun-if-changed={}", proto_file.display());
let mtime = match std::fs::metadata(proto_file) {
+23 -1
View File
@@ -187,6 +187,20 @@
}
]
},
{
"id": "mips.registerPrefix",
"type": "boolean",
"default": false,
"name": "Register '$' prefix",
"description": "Display MIPS register names with a '$' prefix."
},
{
"id": "ppc.calculatePoolRelocations",
"type": "boolean",
"default": true,
"name": "Calculate pooled data references",
"description": "Display pooled data references in functions as fake relocations."
},
{
"id": "x86.formatter",
"type": "choice",
@@ -242,7 +256,15 @@
"name": "MIPS",
"properties": [
"mips.abi",
"mips.instrCategory"
"mips.instrCategory",
"mips.registerPrefix"
]
},
{
"id": "ppc",
"name": "PowerPC",
"properties": [
"ppc.calculatePoolRelocations"
]
},
{
+59
View File
@@ -0,0 +1,59 @@
syntax = "proto3";
import "report.proto";
package objdiff.report;
// A pair of reports to compare and generate changes
message ChangesInput {
// The previous report
Report from = 1;
// The current report
Report to = 2;
}
// Changes between two reports
message Changes {
// The progress info for the previous report
Measures from = 1;
// The progress info for the current report
Measures to = 2;
// Units that changed
repeated ChangeUnit units = 3;
}
// A changed unit
message ChangeUnit {
// The name of the unit
string name = 1;
// The previous progress info (omitted if new)
optional Measures from = 2;
// The current progress info (omitted if removed)
optional Measures to = 3;
// Sections that changed
repeated ChangeItem sections = 4;
// Functions that changed
repeated ChangeItem functions = 5;
// Extra metadata for this unit
optional ReportUnitMetadata metadata = 6;
}
// A changed section or function
message ChangeItem {
// The name of the item
string name = 1;
// The previous progress info (omitted if new)
optional ChangeItemInfo from = 2;
// The current progress info (omitted if removed)
optional ChangeItemInfo to = 3;
// Extra metadata for this item
optional ReportItemMetadata metadata = 4;
}
// Progress info for a section or function
message ChangeItemInfo {
// The overall match percent for this item
float fuzzy_match_percent = 1;
// The size of the item in bytes
uint64 size = 2;
}
Binary file not shown.
+12 -66
View File
@@ -2,6 +2,18 @@ syntax = "proto3";
package objdiff.report;
// Project progress report
message Report {
// Overall progress info
Measures measures = 1;
// Units within this report
repeated ReportUnit units = 2;
// Report version
uint32 version = 3;
// Progress categories
repeated ReportCategory categories = 4;
}
// Progress info for a report or unit
message Measures {
// Overall match percent, including partially matched functions and data
@@ -38,18 +50,6 @@ message Measures {
uint32 complete_units = 16;
}
// Project progress report
message Report {
// Overall progress info
Measures measures = 1;
// Units within this report
repeated ReportUnit units = 2;
// Report version
uint32 version = 3;
// Progress categories
repeated ReportCategory categories = 4;
}
message ReportCategory {
// The ID of the category
string id = 1;
@@ -108,57 +108,3 @@ message ReportItemMetadata {
// The virtual address of the function or section
optional uint64 virtual_address = 2;
}
// A pair of reports to compare and generate changes
message ChangesInput {
// The previous report
Report from = 1;
// The current report
Report to = 2;
}
// Changes between two reports
message Changes {
// The progress info for the previous report
Measures from = 1;
// The progress info for the current report
Measures to = 2;
// Units that changed
repeated ChangeUnit units = 3;
}
// A changed unit
message ChangeUnit {
// The name of the unit
string name = 1;
// The previous progress info (omitted if new)
optional Measures from = 2;
// The current progress info (omitted if removed)
optional Measures to = 3;
// Sections that changed
repeated ChangeItem sections = 4;
// Functions that changed
repeated ChangeItem functions = 5;
// Extra metadata for this unit
optional ReportUnitMetadata metadata = 6;
}
// A changed section or function
message ChangeItem {
// The name of the item
string name = 1;
// The previous progress info (omitted if new)
optional ChangeItemInfo from = 2;
// The current progress info (omitted if removed)
optional ChangeItemInfo to = 3;
// Extra metadata for this item
optional ReportItemMetadata metadata = 4;
}
// Progress info for a section or function
message ChangeItemInfo {
// The overall match percent for this item
float fuzzy_match_percent = 1;
// The size of the item in bytes
uint64 size = 2;
}
+6 -14
View File
@@ -5,14 +5,14 @@ use alloc::{
vec::Vec,
};
use anyhow::{bail, Result};
use arm_attr::{enums::CpuArch, tag::Tag, BuildAttrs};
use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _};
use anyhow::{Result, bail};
use arm_attr::{BuildAttrs, enums::CpuArch, tag::Tag};
use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, elf};
use unarm::{args, arm, thumb};
use crate::{
arch::Arch,
diff::{display::InstructionPart, ArmArchVersion, ArmR9Usage, DiffObjConfig},
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart},
obj::{
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
ScannedInstruction, SymbolFlag, SymbolFlagSet, SymbolKind,
@@ -58,11 +58,7 @@ impl ArchArm {
}
// Only checking first CpuArch tag. Others may exist, but that's very unlikely.
let cpu_arch = subsection.into_public_tag_iter()?.find_map(|(_, tag)| {
if let Tag::CpuArch(cpu_arch) = tag {
Some(cpu_arch)
} else {
None
}
if let Tag::CpuArch(cpu_arch) = tag { Some(cpu_arch) } else { None }
});
match cpu_arch {
Some(CpuArch::V4T) => return Ok(Some(unarm::ArmVersion::V4T)),
@@ -358,11 +354,7 @@ impl Arch for ArchArm {
}
fn symbol_address(&self, address: u64, kind: SymbolKind) -> u64 {
if kind == SymbolKind::Function {
address & !1
} else {
address
}
if kind == SymbolKind::Function { address & !1 } else { address }
}
fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet {
+5 -17
View File
@@ -5,7 +5,7 @@ use alloc::{
};
use core::cmp::Ordering;
use anyhow::{bail, Result};
use anyhow::{Result, bail};
use object::elf;
use yaxpeax_arch::{Arch as YaxpeaxArch, Decoder, Reader, U8Reader};
use yaxpeax_arm::armv8::a64::{
@@ -15,7 +15,7 @@ use yaxpeax_arm::armv8::a64::{
use crate::{
arch::Arch,
diff::{display::InstructionPart, DiffObjConfig},
diff::{DiffObjConfig, display::InstructionPart},
obj::{
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
ScannedInstruction,
@@ -216,11 +216,7 @@ where Cb: FnMut(InstructionPart<'static>) {
unreachable!("movn operand 1 is always ImmShift");
};
let imm = if let Operand::Register(size, _) = ins.operands[0] {
if size == SizeCode::W {
imm as u32 as u64
} else {
imm
}
if size == SizeCode::W { imm as u32 as u64 } else { imm }
} else {
unreachable!("movn operand 0 is always Register");
};
@@ -237,11 +233,7 @@ where Cb: FnMut(InstructionPart<'static>) {
unreachable!("movz operand is always ImmShift");
};
let imm = if let Operand::Register(size, _) = ins.operands[0] {
if size == SizeCode::W {
imm as u32 as u64
} else {
imm
}
if size == SizeCode::W { imm as u32 as u64 } else { imm }
} else {
unreachable!("movz operand 0 is always Register");
};
@@ -574,11 +566,7 @@ where Cb: FnMut(InstructionPart<'static>) {
{
if immr < imms {
let size = if let Operand::Register(size, _) = ins.operands[0] {
if size == SizeCode::W {
32
} else {
64
}
if size == SizeCode::W { 32 } else { 64 }
} else {
unreachable!("operand 0 is always a register");
};
+80 -22
View File
@@ -1,18 +1,18 @@
use alloc::{string::ToString, vec::Vec};
use alloc::{collections::BTreeMap, string::ToString, vec::Vec};
use core::ops::Range;
use anyhow::{bail, Result};
use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _};
use anyhow::{Result, bail};
use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, elf};
use rabbitizer::{
abi::Abi,
operands::{ValuedOperand, IU16},
registers_meta::Register,
IsaExtension, IsaVersion, Vram,
abi::Abi,
operands::{IU16, ValuedOperand},
registers_meta::Register,
};
use crate::{
arch::Arch,
diff::{display::InstructionPart, DiffObjConfig, MipsAbi, MipsInstrCategory},
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart},
obj::{
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction,
@@ -25,6 +25,7 @@ pub struct ArchMips {
pub abi: Abi,
pub isa_extension: Option<IsaExtension>,
pub ri_gp_value: i32,
pub paired_relocations: Vec<BTreeMap<u64, i64>>,
}
const EF_MIPS_ABI: u32 = 0x0000F000;
@@ -64,16 +65,60 @@ impl ArchMips {
// Parse the ri_gp_value stored in .reginfo to be able to correctly
// calculate R_MIPS_GPREL16 relocations later. The value is stored
// 0x14 bytes into .reginfo (on 32 bit platforms)
// 0x14 bytes into .reginfo (on 32-bit platforms)
let endianness = object.endianness();
let ri_gp_value = object
.section_by_name(".reginfo")
.and_then(|section| section.data().ok())
.and_then(|data| data.get(0x14..0x18))
.and_then(|s| s.try_into().ok())
.map(|bytes| object.endianness().read_i32_bytes(bytes))
.map(|bytes| endianness.read_i32_bytes(bytes))
.unwrap_or(0);
Ok(Self { endianness: object.endianness(), abi, isa_extension, ri_gp_value })
// Parse all relocations to pair R_MIPS_HI16 and R_MIPS_LO16. Since the instructions only
// have 16-bit immediate fields, the 32-bit addend is split across the two relocations.
// R_MIPS_LO16 relocations without an immediately preceding R_MIPS_HI16 use the last seen
// R_MIPS_HI16 addend.
// See https://refspecs.linuxfoundation.org/elf/mipsabi.pdf pages 4-17 and 4-18
let mut paired_relocations = Vec::with_capacity(object.sections().count() + 1);
for obj_section in object.sections() {
let data = obj_section.data()?;
let mut last_hi = None;
let mut last_hi_addend = 0;
let mut addends = BTreeMap::new();
for (addr, reloc) in obj_section.relocations() {
if !reloc.has_implicit_addend() {
continue;
}
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;
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 full_addend = (last_hi_addend + addend) as i64;
if let Some(hi_addr) = last_hi.take() {
addends.insert(hi_addr, full_addend);
}
addends.insert(addr, full_addend);
}
_ => {
last_hi = None;
}
}
}
let section_index = obj_section.index().0;
if section_index >= paired_relocations.len() {
paired_relocations.resize_with(section_index + 1, BTreeMap::new);
}
paired_relocations[section_index] = addends;
}
Ok(Self { endianness, abi, isa_extension, ri_gp_value, paired_relocations })
}
fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags {
@@ -99,9 +144,11 @@ impl ArchMips {
fn instruction_display_flags(
&self,
_diff_config: &DiffObjConfig,
diff_config: &DiffObjConfig,
) -> rabbitizer::InstructionDisplayFlags {
rabbitizer::InstructionDisplayFlags::default().with_unknown_instr_comment(false)
rabbitizer::InstructionDisplayFlags::default()
.with_unknown_instr_comment(false)
.with_use_dollar(diff_config.mips_register_prefix)
}
fn parse_ins_ref(
@@ -127,18 +174,16 @@ impl Arch for ArchMips {
diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
let instruction_flags = self.instruction_flags(diff_config);
let start_address = address;
let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4);
let mut cur_addr = start_address as u32;
let mut cur_addr = address as u32;
for chunk in code.chunks_exact(4) {
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
let vram = Vram::new(cur_addr);
let instruction = rabbitizer::Instruction::new(code, vram, instruction_flags);
let instruction =
rabbitizer::Instruction::new(code, Vram::new(cur_addr), instruction_flags);
let opcode = instruction.opcode() as u16;
let branch_dest =
instruction.get_branch_offset_generic().map(|o| (vram + o).inner() as u64);
let branch_dest = instruction.get_branch_vram_generic().map(|v| v.inner() as u64);
ops.push(ScannedInstruction {
ins_ref: InstructionRef { address, size: 4, opcode },
ins_ref: InstructionRef { address: cur_addr as u64, size: 4, opcode },
branch_dest,
});
cur_addr += 4;
@@ -177,6 +222,17 @@ impl Arch for ArchMips {
reloc: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64> {
// Check for paired R_MIPS_HI16 and R_MIPS_LO16 relocations.
if let RelocationFlags::Elf(elf::R_MIPS_HI16 | elf::R_MIPS_LO16) = flags {
if let Some(addend) = self
.paired_relocations
.get(section.index().0)
.and_then(|m| m.get(&address).copied())
{
return Ok(addend);
}
}
let data = section.data()?;
let code = data[address as usize..address as usize + 4].try_into()?;
let addend = self.endianness.read_u32_bytes(code);
@@ -305,9 +361,11 @@ fn push_args(
})))?;
}
arg_cb(InstructionPart::basic("("))?;
arg_cb(InstructionPart::opaque(
base.either_name(instruction.flags().abi(), display_flags.named_gpr()),
))?;
arg_cb(InstructionPart::opaque(base.either_name(
instruction.flags().abi(),
display_flags.named_gpr(),
!display_flags.use_dollar(),
)))?;
arg_cb(InstructionPart::basic(")"))?;
}
// ValuedOperand::r5900_immediate15(..) => match relocation {
+19 -6
View File
@@ -1,17 +1,17 @@
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec};
use core::{ffi::CStr, fmt, fmt::Debug};
use anyhow::{bail, Result};
use anyhow::{Result, bail};
use object::Endian as _;
use crate::{
diff::{
display::{ContextItem, HoverItem, InstructionPart},
DiffObjConfig,
display::{ContextItem, HoverItem, InstructionPart},
},
obj::{
InstructionArg, Object, ParsedInstruction, RelocationFlags, ResolvedInstructionRef,
ScannedInstruction, SymbolFlagSet, SymbolKind,
InstructionArg, Object, ParsedInstruction, Relocation, RelocationFlags,
ResolvedInstructionRef, ScannedInstruction, Symbol, SymbolFlagSet, SymbolKind,
},
util::ReallySigned,
};
@@ -66,7 +66,9 @@ impl DataType {
pub fn display_literals(&self, endian: object::Endianness, bytes: &[u8]) -> Vec<String> {
let mut strs = Vec::new();
if self.required_len().is_some_and(|l| bytes.len() < l) {
log::warn!("Failed to display a symbol value for a symbol whose size is too small for instruction referencing it.");
log::warn!(
"Failed to display a symbol value for a symbol whose size is too small for instruction referencing it."
);
return strs;
}
let mut bytes = bytes;
@@ -124,7 +126,7 @@ impl DataType {
}
DataType::Double => {
let bytes: [u8; 8] = bytes.try_into().unwrap();
strs.push(format!("{:?}f", match endian {
strs.push(format!("{:?}", match endian {
object::Endianness::Little => f64::from_le_bytes(bytes),
object::Endianness::Big => f64::from_be_bytes(bytes),
}));
@@ -212,6 +214,17 @@ pub trait Arch: Send + Sync + Debug {
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
) -> Result<()>;
/// Generate a list of fake relocations from the given code that represent pooled data accesses.
fn generate_pooled_relocations(
&self,
_address: u64,
_code: &[u8],
_relocations: &[Relocation],
_symbols: &[Symbol],
) -> Vec<Relocation> {
Vec::new()
}
fn implcit_addend(
&self,
file: &object::File<'_>,

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