Compare commits

..

7 Commits

Author SHA1 Message Date
c7a326b160 Update all dependencies (again) 2023-07-06 10:37:57 -04:00
100f8f8ac5 Update all dependencies 2023-05-11 02:47:57 -04:00
2f778932a4 Version 0.3.1 2023-02-06 17:40:42 -05:00
42601b4750 Update cwdemangle 2023-02-06 17:40:42 -05:00
636a8e00c5 Fix diffing across mismatched .text sections 2023-02-06 17:40:42 -05:00
Nick Condron
cd46be7726 Simplify common_symbols by using iterators (#28) 2023-01-26 00:19:20 -05:00
Nick Condron
019493f944 Remove LevEditType::Keep variant (#27) 2023-01-22 13:20:50 -05:00
13 changed files with 1536 additions and 1079 deletions

2282
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "objdiff"
version = "0.3.0"
version = "0.3.3"
edition = "2021"
rust-version = "1.65"
authors = ["Luke Street <luke@street.dev>"]
@@ -11,6 +11,7 @@ description = """
A local diffing tool for decompilation projects.
"""
publish = false
build = "build.rs"
[profile.release]
lto = "thin"
@@ -21,40 +22,40 @@ default = []
wgpu = ["eframe/wgpu"]
[dependencies]
anyhow = "1.0.68"
bytes = "1.3.0"
anyhow = "1.0.71"
bytes = "1.4.0"
cfg-if = "1.0.0"
const_format = "0.2.30"
cwdemangle = "0.1.4"
eframe = { version = "0.20.1", features = ["persistence"] }
egui = "0.20.1"
egui_extras = "0.20.0"
const_format = "0.2.31"
cwdemangle = "0.1.5"
eframe = { version = "0.22.0", features = ["persistence"] }
egui = "0.22.0"
egui_extras = "0.22.0"
flagset = "0.4.3"
log = "0.4.17"
memmap2 = "0.5.8"
notify = "5.0.0"
object = { version = "0.30.2", features = ["read_core", "std", "elf"], default-features = false }
png = "0.17.7"
log = "0.4.19"
memmap2 = "0.7.1"
notify = "6.0.1"
object = { version = "0.31.1", features = ["read_core", "std", "elf"], default-features = false }
png = "0.17.9"
ppc750cl = { git = "https://github.com/terorie/ppc750cl", rev = "9ae36eef34aa6d74e00972c7671f547a2acfd0aa" }
rabbitizer = "1.5.8"
rfd = { version = "0.10.0" } #, default-features = false, features = ['xdg-portal']
rabbitizer = "1.7.4"
rfd = { version = "0.11.4" } #, default-features = false, features = ['xdg-portal']
serde = { version = "1", features = ["derive"] }
tempfile = "3.3.0"
thiserror = "1.0.38"
time = { version = "0.3.17", features = ["formatting", "local-offset"] }
toml = "0.5.11"
tempfile = "3.6.0"
thiserror = "1.0.41"
time = { version = "0.3.22", features = ["formatting", "local-offset"] }
toml = "0.7.6"
twox-hash = "1.6.3"
byteorder = "1.4.3"
# For Linux static binaries, use rustls
[target.'cfg(target_os = "linux")'.dependencies]
reqwest = { version = "0.11.14", default-features = false, features = ["blocking", "json", "rustls"] }
self_update = { version = "0.34.0", default-features = false, features = ["rustls"] }
reqwest = { version = "0.11.18", default-features = false, features = ["blocking", "json", "rustls"] }
self_update = { version = "0.37.0", default-features = false, features = ["rustls"] }
# For all other platforms, use native TLS
[target.'cfg(not(target_os = "linux"))'.dependencies]
reqwest = "0.11.14"
self_update = "0.34.0"
reqwest = "0.11.18"
self_update = "0.37.0"
[target.'cfg(windows)'.dependencies]
path-slash = "0.2.1"
@@ -76,5 +77,5 @@ console_error_panic_hook = "0.1.7"
tracing-wasm = "0.2"
[build-dependencies]
anyhow = "1.0.68"
vergen = { version = "7.5.0", features = ["build", "cargo", "git"], default-features = false }
anyhow = "1.0.71"
vergen = { version = "8.2.4", features = ["build", "cargo", "git", "gitcl"] }

View File

@@ -1,10 +1,10 @@
use anyhow::Result;
use vergen::{vergen, Config};
use vergen::EmitBuilder;
fn main() -> Result<()> {
#[cfg(windows)]
{
winres::WindowsResource::new().set_icon("assets/icon.ico").compile()?;
}
vergen(Config::default())
EmitBuilder::builder().fail_on_error().all_build().all_cargo().all_git().emit()
}

View File

@@ -48,9 +48,11 @@ notice = "warn"
# A list of advisory IDs to ignore. Note that ignored advisories will still
# output a note when they are encountered.
ignore = [
# git2 (build dependency)
"RUSTSEC-2023-0002",
"RUSTSEC-2023-0003",
"RUSTSEC-2023-0022",
"RUSTSEC-2023-0023",
"RUSTSEC-2023-0024",
"RUSTSEC-2023-0034",
"RUSTSEC-2023-0044",
]
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
# lower than the range specified will be ignored. Note that ignored advisories
@@ -86,6 +88,7 @@ allow = [
"OFL-1.1",
"LicenseRef-UFL-1.0",
"OpenSSL",
"GPL-3.0",
]
# List of explictly disallowed licenses
# See https://spdx.org/licenses/ for list of possible licenses

View File

@@ -362,7 +362,7 @@ impl eframe::App for App {
ui.colored_label(Color32::LIGHT_BLUE, &demangled);
});
if ui.button("Copy").clicked() {
ui.output().copied_text = demangled;
ui.output_mut(|output| output.copied_text = demangled);
}
} else {
ui.scope(|ui| {
@@ -481,7 +481,7 @@ impl eframe::App for App {
if let Some(project_dir) = &config.project_dir {
match create_watcher(self.modified.clone(), project_dir) {
Ok(watcher) => self.watcher = Some(watcher),
Err(e) => eprintln!("Failed to create watcher: {e}"),
Err(e) => log::error!("Failed to create watcher: {e}"),
}
config.project_dir_change = false;
self.modified.store(true, Ordering::Relaxed);
@@ -532,7 +532,7 @@ fn create_watcher(
}
}
}
Err(e) => println!("watch error: {e:?}"),
Err(e) => log::error!("watch error: {e:?}"),
})?;
watcher.watch(project_dir, RecursiveMode::Recursive)?;
Ok(watcher)

View File

@@ -133,7 +133,6 @@ pub fn diff_code(
right_diff.push(ObjInsDiff::default());
cur_left = left_iter.next();
}
LevEditType::Keep => unreachable!(),
}
} else {
break;
@@ -363,20 +362,26 @@ fn compare_ins(
Ok(result)
}
fn find_symbol<'a>(symbols: &'a mut [ObjSymbol], name: &str) -> Option<&'a mut ObjSymbol> {
symbols.iter_mut().find(|s| s.name == name)
fn find_section_and_symbol(obj: &ObjInfo, name: &str) -> Option<(usize, usize)> {
for (section_idx, section) in obj.sections.iter().enumerate() {
let symbol_idx = match section.symbols.iter().position(|symbol| symbol.name == name) {
Some(symbol_idx) => symbol_idx,
None => continue,
};
return Some((section_idx, symbol_idx));
}
None
}
pub fn diff_objs(left: &mut ObjInfo, right: &mut ObjInfo, _diff_config: &DiffConfig) -> Result<()> {
for left_section in &mut left.sections {
let Some(right_section) = right.sections.iter_mut().find(|s| s.name == left_section.name) else {
continue;
};
if left_section.kind == ObjSectionKind::Code {
for left_symbol in &mut left_section.symbols {
if let Some(right_symbol) =
find_symbol(&mut right_section.symbols, &left_symbol.name)
if let Some((right_section_idx, right_symbol_idx)) =
find_section_and_symbol(right, &left_symbol.name)
{
let right_section = &mut right.sections[right_section_idx];
let right_symbol = &mut right_section.symbols[right_symbol_idx];
left_symbol.diff_symbol = Some(right_symbol.name.clone());
right_symbol.diff_symbol = Some(left_symbol.name.clone());
diff_code(
@@ -400,31 +405,38 @@ pub fn diff_objs(left: &mut ObjInfo, right: &mut ObjInfo, _diff_config: &DiffCon
)?;
}
}
for right_symbol in &mut right_section.symbols {
if right_symbol.instructions.is_empty() {
no_diff_code(
left.architecture,
&right_section.data,
right_symbol,
&right_section.relocations,
&left.line_info,
)?;
}
}
} else if left_section.kind == ObjSectionKind::Data {
} else {
let Some(right_section) = right.sections.iter_mut().find(|s| s.name == left_section.name) else {
continue;
};
if left_section.kind == ObjSectionKind::Data {
diff_data(left_section, right_section);
// diff_data_symbols(left_section, right_section)?;
} else if left_section.kind == ObjSectionKind::Bss {
diff_bss_symbols(&mut left_section.symbols, &mut right_section.symbols)?;
}
}
}
for right_section in right.sections.iter_mut().filter(|s| s.kind == ObjSectionKind::Code) {
for right_symbol in &mut right_section.symbols {
if right_symbol.instructions.is_empty() {
no_diff_code(
right.architecture,
&right_section.data,
right_symbol,
&right_section.relocations,
&right.line_info,
)?;
}
}
}
diff_bss_symbols(&mut left.common, &mut right.common)?;
Ok(())
}
fn diff_bss_symbols(left_symbols: &mut [ObjSymbol], right_symbols: &mut [ObjSymbol]) -> Result<()> {
for left_symbol in left_symbols {
if let Some(right_symbol) = find_symbol(right_symbols, &left_symbol.name) {
if let Some(right_symbol) = right_symbols.iter_mut().find(|s| s.name == left_symbol.name) {
left_symbol.diff_symbol = Some(right_symbol.name.clone());
right_symbol.diff_symbol = Some(left_symbol.name.clone());
let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
@@ -511,13 +523,12 @@ fn diff_data(left: &mut ObjSection, right: &mut ObjSection) {
let mut right_diff = Vec::<ObjDataDiff>::new();
let mut left_cur = 0usize;
let mut right_cur = 0usize;
let mut cur_op = LevEditType::Keep;
let mut cur_op = LevEditType::Replace;
let mut cur_left_data = Vec::<u8>::new();
let mut cur_right_data = Vec::<u8>::new();
for op in edit_ops {
if cur_op != op.op_type || left_cur < op.first_start || right_cur < op.second_start {
match cur_op {
LevEditType::Keep => {}
LevEditType::Replace => {
let left_data = take(&mut cur_left_data);
let right_data = take(&mut cur_right_data);
@@ -603,7 +614,6 @@ fn diff_data(left: &mut ObjSection, right: &mut ObjSection) {
cur_left_data.push(left.data[left_cur]);
left_cur += 1;
}
LevEditType::Keep => unreachable!(),
}
cur_op = op.op_type;
}
@@ -627,7 +637,6 @@ fn diff_data(left: &mut ObjSection, right: &mut ObjSection) {
// TODO: merge with above
match cur_op {
LevEditType::Keep => {}
LevEditType::Replace => {
let left_data = take(&mut cur_left_data);
let right_data = take(&mut cur_right_data);

View File

@@ -27,7 +27,6 @@
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum LevEditType {
Keep,
Replace,
Insert,
Delete,
@@ -77,98 +76,66 @@ where T: PartialEq {
cache_matrix[current + 1 + p] = x;
}
}
editops_from_cost_matrix(
first_string,
second_string,
matrix_columns,
matrix_rows,
prefix_len,
cache_matrix,
)
editops_from_cost_matrix(matrix_columns, matrix_rows, prefix_len, cache_matrix)
}
fn editops_from_cost_matrix<T>(
string1: &[T],
string2: &[T],
fn editops_from_cost_matrix(
len1: usize,
len2: usize,
prefix_len: usize,
cache_matrix: Vec<usize>,
) -> Vec<LevEditOp>
where
T: PartialEq,
{
) -> Vec<LevEditOp> {
let mut ops = Vec::with_capacity(cache_matrix[len1 * len2 - 1]);
let mut dir = 0;
let mut ops: Vec<LevEditOp> = vec![];
ops.reserve(cache_matrix[len1 * len2 - 1]);
let mut i = len1 - 1;
let mut j = len2 - 1;
let mut p = len1 * len2 - 1;
// let string1_chars: Vec<char> = string1.chars().collect();
// let string2_chars: Vec<char> = string2.chars().collect();
//TODO this is still pretty ugly
while i > 0 || j > 0 {
let current_value = cache_matrix[p];
let op_type;
// More than one operation can be possible at a time. We use `dir` to
// decide when ambiguous.
let is_insert = j > 0 && current_value == cache_matrix[p - 1] + 1;
let is_delete = i > 0 && current_value == cache_matrix[p - len2] + 1;
let is_replace = i > 0 && j > 0 && current_value == cache_matrix[p - len2 - 1] + 1;
if dir == -1 && j > 0 && current_value == cache_matrix[p - 1] + 1 {
op_type = LevEditType::Insert;
} else if dir == 1 && i > 0 && current_value == cache_matrix[p - len2] + 1 {
op_type = LevEditType::Delete;
} else if i > 0
&& j > 0
&& current_value == cache_matrix[p - len2 - 1]
&& string1[i - 1] == string2[j - 1]
{
op_type = LevEditType::Keep;
} else if i > 0 && j > 0 && current_value == cache_matrix[p - len2 - 1] + 1 {
op_type = LevEditType::Replace;
}
/* we can't turn directly from -1 to 1, in this case it would be better
* to go diagonally, but check it (dir == 0) */
else if dir == 0 && j > 0 && current_value == cache_matrix[p - 1] + 1 {
op_type = LevEditType::Insert;
} else if dir == 0 && i > 0 && current_value == cache_matrix[p - len2] + 1 {
op_type = LevEditType::Delete;
} else {
panic!("something went terribly wrong");
}
match op_type {
LevEditType::Insert => {
j -= 1;
p -= 1;
dir = -1;
}
LevEditType::Delete => {
i -= 1;
p -= len2;
dir = 1;
}
LevEditType::Replace => {
i -= 1;
j -= 1;
p -= len2 + 1;
dir = 0;
}
LevEditType::Keep => {
i -= 1;
j -= 1;
p -= len2 + 1;
dir = 0;
/* LevEditKeep does not has to be stored */
continue;
}
let (op_type, new_dir) = match (dir, is_insert, is_delete, is_replace) {
(_, false, false, false) => (None, 0),
(-1, true, _, _) => (Some(LevEditType::Insert), -1),
(1, _, true, _) => (Some(LevEditType::Delete), 1),
(_, _, _, true) => (Some(LevEditType::Replace), 0),
(0, true, _, _) => (Some(LevEditType::Insert), -1),
(0, _, true, _) => (Some(LevEditType::Delete), 1),
_ => panic!("something went terribly wrong"),
};
let edit_op =
LevEditOp { op_type, first_start: i + prefix_len, second_start: j + prefix_len };
ops.insert(0, edit_op);
match new_dir {
-1 => {
j -= 1;
p -= 1;
}
1 => {
i -= 1;
p -= len2;
}
0 => {
i -= 1;
j -= 1;
p -= len2 + 1;
}
_ => panic!("something went terribly wrong"),
};
dir = new_dir;
if let Some(op_type) = op_type {
ops.insert(0, LevEditOp {
op_type,
first_start: i + prefix_len,
second_start: j + prefix_len,
});
}
}
ops

View File

@@ -54,7 +54,8 @@ fn main() {
"objdiff",
native_options,
Box::new(move |cc| Box::new(objdiff::App::new(cc, utc_offset, exec_path_clone))),
);
)
.expect("Failed to run eframe application");
// Attempt to relaunch application from the updated path
if let Ok(mut guard) = exec_path.lock() {
@@ -64,7 +65,7 @@ fn main() {
let result = exec::Command::new(path)
.args(&std::env::args().collect::<Vec<String>>())
.exec();
eprintln!("Failed to relaunch: {result:?}");
log::error!("Failed to relaunch: {result:?}");
} else {
let result = std::process::Command::new(path)
.args(std::env::args())
@@ -72,7 +73,7 @@ fn main() {
.unwrap()
.wait();
if let Err(e) = result {
eprintln!("Failed to relaunch: {:?}", e);
log::error!("Failed to relaunch: {:?}", e);
}
}
}

View File

@@ -26,7 +26,7 @@ fn to_obj_section_kind(kind: SectionKind) -> Option<ObjSectionKind> {
fn to_obj_symbol(obj_file: &File<'_>, symbol: &Symbol<'_, '_>, addend: i64) -> Result<ObjSymbol> {
let mut name = symbol.name().context("Failed to process symbol name")?;
if name.is_empty() {
println!("Found empty sym: {symbol:?}");
log::warn!("Found empty sym: {symbol:?}");
name = "?";
}
let mut flags = ObjSymbolFlagSet(ObjSymbolFlags::none());
@@ -126,13 +126,11 @@ fn symbols_by_section(obj_file: &File<'_>, section: &ObjSection) -> Result<Vec<O
}
fn common_symbols(obj_file: &File<'_>) -> Result<Vec<ObjSymbol>> {
let mut result = Vec::<ObjSymbol>::new();
for symbol in obj_file.symbols() {
if symbol.is_common() {
result.push(to_obj_symbol(obj_file, &symbol, 0)?);
}
}
Ok(result)
obj_file
.symbols()
.filter(Symbol::is_common)
.map(|symbol| to_obj_symbol(obj_file, &symbol, 0))
.collect::<Result<Vec<ObjSymbol>>>()
}
fn find_section_symbol(
@@ -296,7 +294,7 @@ fn line_info(obj_file: &File<'_>) -> Result<Option<BTreeMap<u32, u32>>> {
let address_delta = reader.read_u32::<BigEndian>()?;
map.insert(base_address + address_delta, line_number);
}
println!("Line info: {map:#X?}");
log::debug!("Line info: {map:#X?}");
return Ok(Some(map));
}
Ok(None)

View File

@@ -74,7 +74,7 @@ pub fn config_ui(ui: &mut egui::Ui, config: &Arc<RwLock<AppConfig>>, view_state:
ui.label(formatcp!("Git branch: {}", env!("VERGEN_GIT_BRANCH")));
ui.label(formatcp!("Git commit: {}", env!("VERGEN_GIT_SHA")));
ui.label(formatcp!("Build target: {}", env!("VERGEN_CARGO_TARGET_TRIPLE")));
ui.label(formatcp!("Build type: {}", env!("VERGEN_CARGO_PROFILE")));
ui.label(formatcp!("Debug: {}", env!("VERGEN_CARGO_DEBUG")));
});
if let Some(state) = &view_state.check_update {
ui.label(format!("Latest version: {}", state.latest_release.version));
@@ -96,8 +96,10 @@ pub fn config_ui(ui: &mut egui::Ui, config: &Arc<RwLock<AppConfig>>, view_state:
.on_hover_text_at_pointer("Open a link to the latest release on GitHub")
.clicked()
{
ui.output().open_url =
Some(OpenUrl { url: RELEASE_URL.to_string(), new_tab: true });
ui.output_mut(|output| {
output.open_url =
Some(OpenUrl { url: RELEASE_URL.to_string(), new_tab: true })
});
}
});
}

View File

@@ -226,31 +226,31 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
match arg {
Argument::Uimm(v) => {
if ui.button(format!("Copy \"{v}\"")).clicked() {
ui.output().copied_text = format!("{v}");
ui.output_mut(|output| output.copied_text = format!("{v}"));
ui.close_menu();
}
if ui.button(format!("Copy \"{}\"", v.0)).clicked() {
ui.output().copied_text = format!("{}", v.0);
ui.output_mut(|output| output.copied_text = format!("{}", v.0));
ui.close_menu();
}
}
Argument::Simm(v) => {
if ui.button(format!("Copy \"{v}\"")).clicked() {
ui.output().copied_text = format!("{v}");
ui.output_mut(|output| output.copied_text = format!("{v}"));
ui.close_menu();
}
if ui.button(format!("Copy \"{}\"", v.0)).clicked() {
ui.output().copied_text = format!("{}", v.0);
ui.output_mut(|output| output.copied_text = format!("{}", v.0));
ui.close_menu();
}
}
Argument::Offset(v) => {
if ui.button(format!("Copy \"{v}\"")).clicked() {
ui.output().copied_text = format!("{v}");
ui.output_mut(|output| output.copied_text = format!("{v}"));
ui.close_menu();
}
if ui.button(format!("Copy \"{}\"", v.0)).clicked() {
ui.output().copied_text = format!("{}", v.0);
ui.output_mut(|output| output.copied_text = format!("{}", v.0));
ui.close_menu();
}
}
@@ -261,12 +261,12 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
if let Some(reloc) = &ins.reloc {
if let Some(name) = &reloc.target.demangled_name {
if ui.button(format!("Copy \"{name}\"")).clicked() {
ui.output().copied_text = name.clone();
ui.output_mut(|output| output.copied_text = name.clone());
ui.close_menu();
}
}
if ui.button(format!("Copy \"{}\"", reloc.target.name)).clicked() {
ui.output().copied_text = reloc.target.name.clone();
ui.output_mut(|output| output.copied_text = reloc.target.name.clone());
ui.close_menu();
}
}

View File

@@ -17,7 +17,7 @@ pub fn jobs_ui(ui: &mut egui::Ui, view_state: &mut ViewState) {
if job.handle.is_some() {
job.should_remove = true;
if let Err(e) = job.cancel.send(()) {
eprintln!("Failed to cancel job: {e:?}");
log::error!("Failed to cancel job: {e:?}");
}
} else {
remove_job = Some(idx);

View File

@@ -28,12 +28,12 @@ fn symbol_context_menu_ui(ui: &mut Ui, symbol: &ObjSymbol) {
if let Some(name) = &symbol.demangled_name {
if ui.button(format!("Copy \"{name}\"")).clicked() {
ui.output().copied_text = name.clone();
ui.output_mut(|output| output.copied_text = name.clone());
ui.close_menu();
}
}
if ui.button(format!("Copy \"{}\"", symbol.name)).clicked() {
ui.output().copied_text = symbol.name.clone();
ui.output_mut(|output| output.copied_text = symbol.name.clone());
ui.close_menu();
}
});
@@ -282,9 +282,9 @@ pub fn symbol_diff_ui(ui: &mut Ui, view_state: &mut ViewState) {
strip.strip(|builder| {
builder.sizes(Size::remainder(), 2).horizontal(|mut strip| {
strip.cell(|ui| {
ui.push_id("left", |ui| {
if result.first_status.success {
if let Some(obj) = &result.first_obj {
ui.push_id("left", |ui| {
symbol_list_ui(
ui,
obj,
@@ -294,16 +294,16 @@ pub fn symbol_diff_ui(ui: &mut Ui, view_state: &mut ViewState) {
&lower_search,
&view_state.view_config,
);
});
}
} else {
build_log_ui(ui, &result.first_status);
}
});
});
strip.cell(|ui| {
ui.push_id("right", |ui| {
if result.second_status.success {
if let Some(obj) = &result.second_obj {
ui.push_id("right", |ui| {
symbol_list_ui(
ui,
obj,
@@ -313,7 +313,6 @@ pub fn symbol_diff_ui(ui: &mut Ui, view_state: &mut ViewState) {
&lower_search,
&view_state.view_config,
);
});
}
} else {
build_log_ui(ui, &result.second_status);
@@ -322,4 +321,5 @@ pub fn symbol_diff_ui(ui: &mut Ui, view_state: &mut ViewState) {
});
});
});
});
}