mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-09 05:27:47 +00:00
Updates & initial MIPS support
This commit is contained in:
63
src/app.rs
63
src/app.rs
@@ -10,11 +10,12 @@ use std::{
|
||||
};
|
||||
|
||||
use eframe::Frame;
|
||||
use egui::Widget;
|
||||
use notify::{RecursiveMode, Watcher};
|
||||
|
||||
use crate::{
|
||||
jobs::{
|
||||
build::{queue_build, BuildResult},
|
||||
build::{queue_build, BuildResult, BuildStatus},
|
||||
Job, JobResult, JobState,
|
||||
},
|
||||
views::{
|
||||
@@ -30,6 +31,13 @@ pub enum View {
|
||||
FunctionDiff,
|
||||
}
|
||||
|
||||
#[derive(Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
pub enum DiffKind {
|
||||
#[default]
|
||||
SplitObj,
|
||||
WholeBinary,
|
||||
}
|
||||
|
||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct ViewState {
|
||||
@@ -43,17 +51,24 @@ pub struct ViewState {
|
||||
pub selected_symbol: Option<String>,
|
||||
#[serde(skip)]
|
||||
pub current_view: View,
|
||||
#[serde(skip)]
|
||||
pub show_config: bool,
|
||||
// Config
|
||||
pub diff_kind: DiffKind,
|
||||
pub reverse_fn_order: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct AppConfig {
|
||||
// Split obj
|
||||
pub project_dir: Option<PathBuf>,
|
||||
pub build_asm_dir: Option<PathBuf>,
|
||||
pub build_src_dir: Option<PathBuf>,
|
||||
pub build_obj: Option<String>,
|
||||
// Whole binary
|
||||
pub left_obj: Option<PathBuf>,
|
||||
pub right_obj: Option<PathBuf>,
|
||||
#[serde(skip)]
|
||||
pub project_dir_change: bool,
|
||||
}
|
||||
@@ -118,6 +133,9 @@ impl eframe::App for App {
|
||||
if ui.button("Quit").clicked() {
|
||||
frame.close();
|
||||
}
|
||||
if ui.button("Show config").clicked() {
|
||||
view_state.show_config = !view_state.show_config;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -148,6 +166,34 @@ impl eframe::App for App {
|
||||
});
|
||||
}
|
||||
|
||||
egui::Window::new("Config").open(&mut view_state.show_config).show(ctx, |ui| {
|
||||
ui.label("Diff type:");
|
||||
|
||||
if egui::RadioButton::new(
|
||||
view_state.diff_kind == DiffKind::SplitObj,
|
||||
"Split object diff",
|
||||
)
|
||||
.ui(ui)
|
||||
.on_hover_text("Compare individual object files")
|
||||
.clicked()
|
||||
{
|
||||
view_state.diff_kind = DiffKind::SplitObj;
|
||||
}
|
||||
|
||||
if egui::RadioButton::new(
|
||||
view_state.diff_kind == DiffKind::WholeBinary,
|
||||
"Whole binary diff",
|
||||
)
|
||||
.ui(ui)
|
||||
.on_hover_text("Compare two full binaries")
|
||||
.clicked()
|
||||
{
|
||||
view_state.diff_kind = DiffKind::WholeBinary;
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
});
|
||||
|
||||
if view_state.jobs.iter().any(|job| {
|
||||
if let Some(handle) = &job.handle {
|
||||
return !handle.is_finished();
|
||||
@@ -156,7 +202,6 @@ impl eframe::App for App {
|
||||
}) {
|
||||
ctx.request_repaint();
|
||||
} else {
|
||||
ctx.request_repaint();
|
||||
ctx.request_repaint_after(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
@@ -187,6 +232,20 @@ impl eframe::App for App {
|
||||
JobResult::Build(state) => {
|
||||
self.view_state.build = Some(state);
|
||||
}
|
||||
JobResult::BinDiff(state) => {
|
||||
self.view_state.build = Some(Box::new(BuildResult {
|
||||
first_status: BuildStatus {
|
||||
success: true,
|
||||
log: "".to_string(),
|
||||
},
|
||||
second_status: BuildStatus {
|
||||
success: true,
|
||||
log: "".to_string(),
|
||||
},
|
||||
first_obj: Some(state.first_obj),
|
||||
second_obj: Some(state.second_obj),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
||||
172
src/diff.rs
172
src/diff.rs
@@ -1,87 +1,18 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use ppc750cl::{disasm_iter, Argument};
|
||||
|
||||
use crate::{
|
||||
editops::{editops_find, LevEditType},
|
||||
obj::{
|
||||
ObjInfo, ObjIns, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff,
|
||||
ObjInsDiffKind, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
|
||||
ObjSymbolFlags,
|
||||
mips, ppc, ObjArchitecture, ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom,
|
||||
ObjInsBranchTo, ObjInsDiff, ObjInsDiffKind, ObjReloc, ObjSection, ObjSectionKind,
|
||||
ObjSymbol, ObjSymbolFlags,
|
||||
},
|
||||
};
|
||||
|
||||
// Relative relocation, can be Simm or BranchDest
|
||||
fn is_relative_arg(arg: &ObjInsArg) -> bool {
|
||||
matches!(arg, ObjInsArg::Arg(arg) if matches!(arg, Argument::Simm(_) | Argument::BranchDest(_)))
|
||||
}
|
||||
|
||||
// Relative or absolute relocation, can be Uimm, Simm or Offset
|
||||
fn is_rel_abs_arg(arg: &ObjInsArg) -> bool {
|
||||
matches!(arg, ObjInsArg::Arg(arg) if matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_)))
|
||||
}
|
||||
|
||||
fn is_offset_arg(arg: &ObjInsArg) -> bool { matches!(arg, ObjInsArg::Arg(Argument::Offset(_))) }
|
||||
|
||||
fn process_code(data: &[u8], address: u64, relocs: &[ObjReloc]) -> Result<(Vec<u8>, Vec<ObjIns>)> {
|
||||
let ins_count = data.len() / 4;
|
||||
let mut ops = Vec::<u8>::with_capacity(ins_count);
|
||||
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
||||
for mut ins in disasm_iter(data, address as u32) {
|
||||
let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == ins.addr);
|
||||
if let Some(reloc) = reloc {
|
||||
// Zero out relocations
|
||||
ins.code = match reloc.kind {
|
||||
ObjRelocKind::PpcEmbSda21 => ins.code & !0x1FFFFF,
|
||||
ObjRelocKind::PpcRel24 => ins.code & !0x3FFFFFC,
|
||||
ObjRelocKind::PpcRel14 => ins.code & !0xFFFC,
|
||||
ObjRelocKind::PpcAddr16Hi
|
||||
| ObjRelocKind::PpcAddr16Ha
|
||||
| ObjRelocKind::PpcAddr16Lo => ins.code & !0xFFFF,
|
||||
_ => ins.code,
|
||||
};
|
||||
}
|
||||
let simplified = ins.simplified();
|
||||
let mut args: Vec<ObjInsArg> =
|
||||
simplified.args.iter().map(|a| ObjInsArg::Arg(a.clone())).collect();
|
||||
if let Some(reloc) = reloc {
|
||||
match reloc.kind {
|
||||
ObjRelocKind::PpcEmbSda21 => {
|
||||
args = vec![args[0].clone(), ObjInsArg::Reloc];
|
||||
}
|
||||
ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => {
|
||||
let arg = args
|
||||
.iter_mut()
|
||||
.rfind(|a| is_relative_arg(a))
|
||||
.ok_or_else(|| anyhow::Error::msg("Failed to locate rel arg for reloc"))?;
|
||||
*arg = ObjInsArg::Reloc;
|
||||
}
|
||||
ObjRelocKind::PpcAddr16Hi
|
||||
| ObjRelocKind::PpcAddr16Ha
|
||||
| ObjRelocKind::PpcAddr16Lo => {
|
||||
let arg = args.iter_mut().rfind(|a| is_rel_abs_arg(a)).ok_or_else(|| {
|
||||
anyhow::Error::msg("Failed to locate rel/abs arg for reloc")
|
||||
})?;
|
||||
*arg =
|
||||
if is_offset_arg(arg) { ObjInsArg::RelocOffset } else { ObjInsArg::Reloc };
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
ops.push(simplified.ins.op as u8);
|
||||
let suffix = simplified.ins.suffix();
|
||||
insts.push(ObjIns {
|
||||
ins: simplified.ins,
|
||||
mnemonic: format!("{}{}", simplified.mnemonic, suffix),
|
||||
args,
|
||||
reloc: reloc.cloned(),
|
||||
});
|
||||
}
|
||||
Ok((ops, insts))
|
||||
}
|
||||
|
||||
pub fn diff_code(
|
||||
arch: ObjArchitecture,
|
||||
left_data: &[u8],
|
||||
right_data: &[u8],
|
||||
left_symbol: &mut ObjSymbol,
|
||||
@@ -89,12 +20,30 @@ pub fn diff_code(
|
||||
left_relocs: &[ObjReloc],
|
||||
right_relocs: &[ObjReloc],
|
||||
) -> Result<()> {
|
||||
let left_code =
|
||||
&left_data[left_symbol.address as usize..(left_symbol.address + left_symbol.size) as usize];
|
||||
let (left_ops, left_insts) = process_code(left_code, left_symbol.address, left_relocs)?;
|
||||
let right_code = &right_data
|
||||
[right_symbol.address as usize..(right_symbol.address + right_symbol.size) as usize];
|
||||
let (right_ops, right_insts) = process_code(right_code, right_symbol.address, right_relocs)?;
|
||||
let left_code = &left_data[left_symbol.section_address as usize
|
||||
..(left_symbol.section_address + left_symbol.size) as usize];
|
||||
let right_code = &right_data[right_symbol.section_address as usize
|
||||
..(right_symbol.section_address + right_symbol.size) as usize];
|
||||
let ((left_ops, left_insts), (right_ops, right_insts)) = match arch {
|
||||
ObjArchitecture::PowerPc => (
|
||||
ppc::process_code(left_code, left_symbol.address, left_relocs)?,
|
||||
ppc::process_code(right_code, right_symbol.address, right_relocs)?,
|
||||
),
|
||||
ObjArchitecture::Mips => (
|
||||
mips::process_code(
|
||||
left_code,
|
||||
left_symbol.address,
|
||||
left_symbol.address + left_symbol.size,
|
||||
left_relocs,
|
||||
)?,
|
||||
mips::process_code(
|
||||
right_code,
|
||||
right_symbol.address,
|
||||
left_symbol.address + left_symbol.size,
|
||||
right_relocs,
|
||||
)?,
|
||||
),
|
||||
};
|
||||
|
||||
let mut left_diff = Vec::<ObjInsDiff>::new();
|
||||
let mut right_diff = Vec::<ObjInsDiff>::new();
|
||||
@@ -111,7 +60,7 @@ pub fn diff_code(
|
||||
let left_addr = op.first_start as u32 * 4;
|
||||
let right_addr = op.second_start as u32 * 4;
|
||||
while let (Some(left), Some(right)) = (cur_left, cur_right) {
|
||||
if (left.ins.addr - left_symbol.address as u32) < left_addr {
|
||||
if (left.address - left_symbol.address as u32) < left_addr {
|
||||
left_diff.push(ObjInsDiff { ins: Some(left.clone()), ..ObjInsDiff::default() });
|
||||
right_diff
|
||||
.push(ObjInsDiff { ins: Some(right.clone()), ..ObjInsDiff::default() });
|
||||
@@ -122,10 +71,10 @@ pub fn diff_code(
|
||||
cur_right = right_iter.next();
|
||||
}
|
||||
if let (Some(left), Some(right)) = (cur_left, cur_right) {
|
||||
if (left.ins.addr - left_symbol.address as u32) != left_addr {
|
||||
if (left.address - left_symbol.address as u32) != left_addr {
|
||||
return Err(anyhow::Error::msg("Instruction address mismatch (left)"));
|
||||
}
|
||||
if (right.ins.addr - right_symbol.address as u32) != right_addr {
|
||||
if (right.address - right_symbol.address as u32) != right_addr {
|
||||
return Err(anyhow::Error::msg("Instruction address mismatch (right)"));
|
||||
}
|
||||
match op.op_type {
|
||||
@@ -178,7 +127,11 @@ pub fn diff_code(
|
||||
}
|
||||
|
||||
let total = left_insts.len();
|
||||
let percent = ((total - diff_state.diff_count) as f32 / total as f32) * 100.0;
|
||||
let percent = if diff_state.diff_count >= total {
|
||||
0.0
|
||||
} else {
|
||||
((total - diff_state.diff_count) as f32 / total as f32) * 100.0
|
||||
};
|
||||
left_symbol.match_percent = percent;
|
||||
right_symbol.match_percent = percent;
|
||||
|
||||
@@ -194,17 +147,22 @@ fn resolve_branches(vec: &mut [ObjInsDiff]) {
|
||||
let mut addr_map = BTreeMap::<u32, usize>::new();
|
||||
for (i, ins_diff) in vec.iter().enumerate() {
|
||||
if let Some(ins) = &ins_diff.ins {
|
||||
addr_map.insert(ins.ins.addr, i);
|
||||
addr_map.insert(ins.address, i);
|
||||
}
|
||||
}
|
||||
// Generate branches
|
||||
let mut branches = BTreeMap::<usize, ObjInsBranchFrom>::new();
|
||||
for (i, ins_diff) in vec.iter_mut().enumerate() {
|
||||
if let Some(ins) = &ins_diff.ins {
|
||||
if ins.ins.is_blr() || ins.reloc.is_some() {
|
||||
continue;
|
||||
}
|
||||
if let Some(ins_idx) = ins.ins.branch_dest().and_then(|dest| addr_map.get(&dest)) {
|
||||
// if ins.ins.is_blr() || ins.reloc.is_some() {
|
||||
// continue;
|
||||
// }
|
||||
if let Some(ins_idx) = ins
|
||||
.args
|
||||
.iter()
|
||||
.find_map(|a| if let ObjInsArg::BranchOffset(offs) = a { Some(offs) } else { None })
|
||||
.and_then(|offs| addr_map.get(&((ins.address as i32 + offs) as u32)))
|
||||
{
|
||||
if let Some(branch) = branches.get_mut(ins_idx) {
|
||||
ins_diff.branch_to =
|
||||
Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx: branch.branch_idx });
|
||||
@@ -253,15 +211,8 @@ fn arg_eq(
|
||||
right_diff: &ObjInsDiff,
|
||||
) -> bool {
|
||||
return match left {
|
||||
ObjInsArg::Arg(l) => match right {
|
||||
ObjInsArg::Arg(r) => match r {
|
||||
Argument::BranchDest(_) => {
|
||||
// Compare dest instruction idx after diffing
|
||||
left_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
||||
== right_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
||||
}
|
||||
_ => format!("{}", l) == format!("{}", r),
|
||||
},
|
||||
ObjInsArg::PpcArg(l) => match right {
|
||||
ObjInsArg::PpcArg(r) => format!("{}", l) == format!("{}", r),
|
||||
_ => false,
|
||||
},
|
||||
ObjInsArg::Reloc => {
|
||||
@@ -271,13 +222,21 @@ fn arg_eq(
|
||||
right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||
)
|
||||
}
|
||||
ObjInsArg::RelocOffset => {
|
||||
matches!(right, ObjInsArg::RelocOffset)
|
||||
ObjInsArg::RelocWithBase => {
|
||||
matches!(right, ObjInsArg::RelocWithBase)
|
||||
&& reloc_eq(
|
||||
left_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||
right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||
)
|
||||
}
|
||||
ObjInsArg::MipsArg(ls) => {
|
||||
matches!(right, ObjInsArg::MipsArg(rs) if ls == rs)
|
||||
}
|
||||
ObjInsArg::BranchOffset(_) => {
|
||||
// Compare dest instruction idx after diffing
|
||||
left_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
||||
== right_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -303,7 +262,7 @@ fn compare_ins(
|
||||
) -> Result<InsDiffResult> {
|
||||
let mut result = InsDiffResult::default();
|
||||
if let (Some(left_ins), Some(right_ins)) = (&left.ins, &right.ins) {
|
||||
if left_ins.args.len() != right_ins.args.len() || left_ins.ins.op != right_ins.ins.op {
|
||||
if left_ins.args.len() != right_ins.args.len() || left_ins.op != right_ins.op {
|
||||
// Totally different op
|
||||
result.kind = ObjInsDiffKind::Replace;
|
||||
state.diff_count += 1;
|
||||
@@ -324,8 +283,10 @@ fn compare_ins(
|
||||
state.diff_count += 1;
|
||||
}
|
||||
let a_str = match a {
|
||||
ObjInsArg::Arg(arg) => format!("{}", arg),
|
||||
ObjInsArg::Reloc | ObjInsArg::RelocOffset => String::new(),
|
||||
ObjInsArg::PpcArg(arg) => format!("{}", arg),
|
||||
ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(),
|
||||
ObjInsArg::MipsArg(str) => str.clone(),
|
||||
ObjInsArg::BranchOffset(arg) => format!("{}", arg),
|
||||
};
|
||||
let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) {
|
||||
ObjInsArgDiff { idx: *idx }
|
||||
@@ -336,8 +297,10 @@ fn compare_ins(
|
||||
ObjInsArgDiff { idx }
|
||||
};
|
||||
let b_str = match b {
|
||||
ObjInsArg::Arg(arg) => format!("{}", arg),
|
||||
ObjInsArg::Reloc | ObjInsArg::RelocOffset => String::new(),
|
||||
ObjInsArg::PpcArg(arg) => format!("{}", arg),
|
||||
ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(),
|
||||
ObjInsArg::MipsArg(str) => str.clone(),
|
||||
ObjInsArg::BranchOffset(arg) => format!("{}", arg),
|
||||
};
|
||||
let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) {
|
||||
ObjInsArgDiff { idx: *idx }
|
||||
@@ -380,6 +343,7 @@ pub fn diff_objs(left: &mut ObjInfo, right: &mut ObjInfo) -> Result<()> {
|
||||
right_symbol.diff_symbol = Some(left_symbol.name.clone());
|
||||
if left_section.kind == ObjSectionKind::Code {
|
||||
diff_code(
|
||||
left.architecture,
|
||||
&left_section.data,
|
||||
&right_section.data,
|
||||
left_symbol,
|
||||
|
||||
168
src/elf.rs
168
src/elf.rs
@@ -4,13 +4,13 @@ use anyhow::{Context, Result};
|
||||
use cwdemangle::demangle;
|
||||
use flagset::Flags;
|
||||
use object::{
|
||||
Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, SectionKind, SymbolKind,
|
||||
SymbolSection,
|
||||
Architecture, File, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget,
|
||||
SectionKind, Symbol, SymbolKind, SymbolSection,
|
||||
};
|
||||
|
||||
use crate::obj::{
|
||||
ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet,
|
||||
ObjSymbolFlags,
|
||||
ObjArchitecture, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
|
||||
ObjSymbolFlagSet, ObjSymbolFlags,
|
||||
};
|
||||
|
||||
fn to_obj_section_kind(kind: SectionKind) -> ObjSectionKind {
|
||||
@@ -22,7 +22,7 @@ fn to_obj_section_kind(kind: SectionKind) -> ObjSectionKind {
|
||||
}
|
||||
}
|
||||
|
||||
fn to_obj_symbol(symbol: &object::Symbol<'_, '_>) -> Result<ObjSymbol> {
|
||||
fn to_obj_symbol(obj_file: &File<'_>, symbol: &Symbol<'_, '_>) -> Result<ObjSymbol> {
|
||||
let mut name = symbol.name().context("Failed to process symbol name")?;
|
||||
if name.is_empty() {
|
||||
println!("Found empty sym: {:?}", symbol);
|
||||
@@ -41,10 +41,18 @@ fn to_obj_symbol(symbol: &object::Symbol<'_, '_>) -> Result<ObjSymbol> {
|
||||
if symbol.is_weak() {
|
||||
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Weak);
|
||||
}
|
||||
let section_address = if let Some(section) =
|
||||
symbol.section_index().and_then(|idx| obj_file.section_by_index(idx).ok())
|
||||
{
|
||||
symbol.address() - section.address()
|
||||
} else {
|
||||
symbol.address()
|
||||
};
|
||||
Ok(ObjSymbol {
|
||||
name: name.to_string(),
|
||||
demangled_name: demangle(name),
|
||||
address: symbol.address(),
|
||||
section_address,
|
||||
size: symbol.size(),
|
||||
size_known: symbol.size() != 0,
|
||||
flags,
|
||||
@@ -61,7 +69,11 @@ const R_PPC_REL24: u32 = 10;
|
||||
const R_PPC_REL14: u32 = 11;
|
||||
const R_PPC_EMB_SDA21: u32 = 109;
|
||||
|
||||
fn filter_sections(obj_file: &object::File<'_>) -> Result<Vec<ObjSection>> {
|
||||
const R_MIPS_26: u32 = 4;
|
||||
const R_MIPS_HI16: u32 = 5;
|
||||
const R_MIPS_LO16: u32 = 6;
|
||||
|
||||
fn filter_sections(obj_file: &File<'_>) -> Result<Vec<ObjSection>> {
|
||||
let mut result = Vec::<ObjSection>::new();
|
||||
for section in obj_file.sections() {
|
||||
if section.size() == 0 {
|
||||
@@ -91,7 +103,7 @@ fn filter_sections(obj_file: &object::File<'_>) -> Result<Vec<ObjSection>> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn symbols_by_section(obj_file: &object::File<'_>, section: &ObjSection) -> Result<Vec<ObjSymbol>> {
|
||||
fn symbols_by_section(obj_file: &File<'_>, section: &ObjSection) -> Result<Vec<ObjSymbol>> {
|
||||
let mut result = Vec::<ObjSymbol>::new();
|
||||
for symbol in obj_file.symbols() {
|
||||
if symbol.kind() == SymbolKind::Section {
|
||||
@@ -102,11 +114,11 @@ fn symbols_by_section(obj_file: &object::File<'_>, section: &ObjSection) -> Resu
|
||||
if symbol.is_local() && section.kind == ObjSectionKind::Code {
|
||||
// TODO strip local syms in diff?
|
||||
let name = symbol.name().context("Failed to process symbol name")?;
|
||||
if name.starts_with("lbl_") {
|
||||
if symbol.size() == 0 || name.starts_with("lbl_") {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
result.push(to_obj_symbol(&symbol)?);
|
||||
result.push(to_obj_symbol(obj_file, &symbol)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,38 +136,64 @@ fn symbols_by_section(obj_file: &object::File<'_>, section: &ObjSection) -> Resu
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn common_symbols(obj_file: &object::File<'_>) -> Result<Vec<ObjSymbol>> {
|
||||
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(&symbol)?);
|
||||
result.push(to_obj_symbol(obj_file, &symbol)?);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn locate_section_symbol(
|
||||
obj_file: &object::File<'_>,
|
||||
target: &object::Symbol<'_, '_>,
|
||||
fn find_section_symbol(
|
||||
obj_file: &File<'_>,
|
||||
target: &Symbol<'_, '_>,
|
||||
address: u64,
|
||||
) -> Result<ObjSymbol> {
|
||||
let section_index =
|
||||
target.section_index().ok_or_else(|| anyhow::Error::msg("Unknown section index"))?;
|
||||
let section = obj_file.section_by_index(section_index)?;
|
||||
let mut closest_symbol: Option<Symbol<'_, '_>> = None;
|
||||
for symbol in obj_file.symbols() {
|
||||
if !matches!(symbol.section_index(), Some(idx) if idx == section_index) {
|
||||
continue;
|
||||
}
|
||||
if symbol.kind() == SymbolKind::Section || symbol.address() != address {
|
||||
if symbol.address() < address
|
||||
&& symbol.size() != 0
|
||||
&& (closest_symbol.is_none()
|
||||
|| matches!(&closest_symbol, Some(s) if s.address() <= symbol.address()))
|
||||
{
|
||||
closest_symbol = Some(symbol);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return to_obj_symbol(&symbol);
|
||||
return to_obj_symbol(obj_file, &symbol);
|
||||
}
|
||||
Err(anyhow::Error::msg("Failed to locate reloc offset sym"))
|
||||
let (name, offset) = closest_symbol
|
||||
.and_then(|s| s.name().map(|n| (n, s.address())).ok())
|
||||
.or_else(|| section.name().map(|n| (n, section.address())).ok())
|
||||
.unwrap_or(("<unknown>", 0));
|
||||
let offset_addr = address - offset;
|
||||
Ok(ObjSymbol {
|
||||
name: if offset_addr > 0 { format!("{}+{:#X}", name, address) } else { name.to_string() },
|
||||
demangled_name: None,
|
||||
address,
|
||||
section_address: address - section.address(),
|
||||
size: 0,
|
||||
size_known: false,
|
||||
flags: Default::default(),
|
||||
diff_symbol: None,
|
||||
instructions: vec![],
|
||||
match_percent: 0.0,
|
||||
})
|
||||
}
|
||||
|
||||
fn relocations_by_section(
|
||||
obj_file: &object::File<'_>,
|
||||
section: &ObjSection,
|
||||
arch: ObjArchitecture,
|
||||
obj_file: &File<'_>,
|
||||
section: &mut ObjSection,
|
||||
) -> Result<Vec<ObjReloc>> {
|
||||
let obj_section = obj_file
|
||||
.section_by_name(§ion.name)
|
||||
@@ -175,19 +213,32 @@ fn relocations_by_section(
|
||||
};
|
||||
let kind = match reloc.kind() {
|
||||
RelocationKind::Absolute => ObjRelocKind::Absolute,
|
||||
RelocationKind::Elf(kind) => match kind {
|
||||
R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
|
||||
R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
|
||||
R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha,
|
||||
R_PPC_REL24 => ObjRelocKind::PpcRel24,
|
||||
R_PPC_REL14 => ObjRelocKind::PpcRel14,
|
||||
R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Unhandled ELF relocation type: {}",
|
||||
kind
|
||||
)))
|
||||
}
|
||||
RelocationKind::Elf(kind) => match arch {
|
||||
ObjArchitecture::PowerPc => match kind {
|
||||
R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
|
||||
R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
|
||||
R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha,
|
||||
R_PPC_REL24 => ObjRelocKind::PpcRel24,
|
||||
R_PPC_REL14 => ObjRelocKind::PpcRel14,
|
||||
R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Unhandled PPC relocation type: {}",
|
||||
kind
|
||||
)))
|
||||
}
|
||||
},
|
||||
ObjArchitecture::Mips => match kind {
|
||||
R_MIPS_26 => ObjRelocKind::Mips26,
|
||||
R_MIPS_HI16 => ObjRelocKind::MipsHi16,
|
||||
R_MIPS_LO16 => ObjRelocKind::MipsLo16,
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Unhandled MIPS relocation type: {}",
|
||||
kind
|
||||
)))
|
||||
}
|
||||
},
|
||||
},
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
@@ -198,23 +249,39 @@ fn relocations_by_section(
|
||||
};
|
||||
let target_section = match symbol.section() {
|
||||
SymbolSection::Common => Some(".comm".to_string()),
|
||||
SymbolSection::Section(idx) => obj_file
|
||||
.section_by_index(idx)
|
||||
.map(|s| s.name().map(|s| s.to_string()).ok())
|
||||
.ok()
|
||||
.flatten(),
|
||||
SymbolSection::Section(idx) => {
|
||||
obj_file.section_by_index(idx).and_then(|s| s.name().map(|s| s.to_string())).ok()
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
// println!("Reloc: {:?}", reloc.addend());
|
||||
// println!("Reloc: {:?}, symbol: {:?}", reloc, symbol);
|
||||
let target = match symbol.kind() {
|
||||
SymbolKind::Text | SymbolKind::Data | SymbolKind::Unknown => to_obj_symbol(&symbol),
|
||||
SymbolKind::Text | SymbolKind::Data | SymbolKind::Unknown => {
|
||||
to_obj_symbol(obj_file, &symbol)
|
||||
}
|
||||
SymbolKind::Section => {
|
||||
let addend = reloc.addend();
|
||||
if addend < 0 {
|
||||
Err(anyhow::Error::msg(format!("Negative addend in section reloc: {}", addend)))
|
||||
let addend = if reloc.has_implicit_addend() {
|
||||
let addend = u32::from_be_bytes(
|
||||
section.data[address as usize..address as usize + 4].try_into()?,
|
||||
);
|
||||
match kind {
|
||||
ObjRelocKind::MipsHi16 | ObjRelocKind::MipsLo16 => {
|
||||
(addend & 0x0000FFFF) * 4
|
||||
}
|
||||
ObjRelocKind::Mips26 => (addend & 0x03FFFFFF) * 4,
|
||||
_ => todo!(),
|
||||
}
|
||||
} else {
|
||||
locate_section_symbol(obj_file, &symbol, addend as u64)
|
||||
}
|
||||
let addend = reloc.addend();
|
||||
if addend < 0 {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Negative addend in section reloc: {}",
|
||||
addend
|
||||
)));
|
||||
}
|
||||
addend as u32
|
||||
};
|
||||
find_section_symbol(obj_file, &symbol, addend as u64)
|
||||
}
|
||||
_ => Err(anyhow::Error::msg(format!(
|
||||
"Unhandled relocation symbol type {:?}",
|
||||
@@ -228,15 +295,26 @@ fn relocations_by_section(
|
||||
|
||||
pub fn read(obj_path: &Path) -> Result<ObjInfo> {
|
||||
let bin_data = fs::read(obj_path)?;
|
||||
let obj_file = object::File::parse(&*bin_data)?;
|
||||
let obj_file = File::parse(&*bin_data)?;
|
||||
let architecture = match obj_file.architecture() {
|
||||
Architecture::PowerPc => ObjArchitecture::PowerPc,
|
||||
Architecture::Mips => ObjArchitecture::Mips,
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Unsupported architecture: {:?}",
|
||||
obj_file.architecture()
|
||||
)))
|
||||
}
|
||||
};
|
||||
let mut result = ObjInfo {
|
||||
architecture,
|
||||
path: obj_path.to_owned(),
|
||||
sections: filter_sections(&obj_file)?,
|
||||
common: common_symbols(&obj_file)?,
|
||||
};
|
||||
for section in &mut result.sections {
|
||||
section.symbols = symbols_by_section(&obj_file, section)?;
|
||||
section.relocations = relocations_by_section(&obj_file, section)?;
|
||||
section.relocations = relocations_by_section(architecture, &obj_file, section)?;
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
45
src/jobs/bindiff.rs
Normal file
45
src/jobs/bindiff.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use std::sync::{mpsc::Receiver, Arc, RwLock};
|
||||
|
||||
use anyhow::{Error, Result};
|
||||
|
||||
use crate::{
|
||||
app::AppConfig,
|
||||
diff::diff_objs,
|
||||
elf,
|
||||
jobs::{queue_job, update_status, Job, JobResult, JobState, Status},
|
||||
obj::ObjInfo,
|
||||
};
|
||||
|
||||
pub struct BinDiffResult {
|
||||
pub first_obj: ObjInfo,
|
||||
pub second_obj: ObjInfo,
|
||||
}
|
||||
|
||||
fn run_build(
|
||||
status: &Status,
|
||||
cancel: Receiver<()>,
|
||||
config: Arc<RwLock<AppConfig>>,
|
||||
) -> Result<Box<BinDiffResult>> {
|
||||
let config = config.read().map_err(|_| Error::msg("Failed to lock app config"))?.clone();
|
||||
let left_path = config.left_obj.as_ref().ok_or_else(|| Error::msg("Missing left obj path"))?;
|
||||
let right_path =
|
||||
config.right_obj.as_ref().ok_or_else(|| Error::msg("Missing right obj path"))?;
|
||||
|
||||
update_status(status, "Loading left obj".to_string(), 0, 3, &cancel)?;
|
||||
let mut left_obj = elf::read(left_path)?;
|
||||
|
||||
update_status(status, "Loading right obj".to_string(), 1, 3, &cancel)?;
|
||||
let mut right_obj = elf::read(right_path)?;
|
||||
|
||||
update_status(status, "Performing diff".to_string(), 2, 3, &cancel)?;
|
||||
diff_objs(&mut left_obj, &mut right_obj)?;
|
||||
|
||||
update_status(status, "Complete".to_string(), 3, 3, &cancel)?;
|
||||
Ok(Box::new(BinDiffResult { first_obj: left_obj, second_obj: right_obj }))
|
||||
}
|
||||
|
||||
pub fn queue_bindiff(config: Arc<RwLock<AppConfig>>) -> JobState {
|
||||
queue_job(Job::BinDiff, move |status, cancel| {
|
||||
run_build(status, cancel, config).map(JobResult::BinDiff)
|
||||
})
|
||||
}
|
||||
@@ -9,13 +9,15 @@ use std::{
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::jobs::build::BuildResult;
|
||||
use crate::jobs::{bindiff::BinDiffResult, build::BuildResult};
|
||||
|
||||
pub mod bindiff;
|
||||
pub mod build;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
pub enum Job {
|
||||
Build,
|
||||
BinDiff,
|
||||
}
|
||||
pub static JOB_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
pub struct JobState {
|
||||
@@ -37,6 +39,7 @@ pub struct JobStatus {
|
||||
pub enum JobResult {
|
||||
None,
|
||||
Build(Box<BuildResult>),
|
||||
BinDiff(Box<BinDiffResult>),
|
||||
}
|
||||
|
||||
fn should_cancel(rx: &Receiver<()>) -> bool {
|
||||
|
||||
24
src/obj.rs
24
src/obj.rs
@@ -1,3 +1,6 @@
|
||||
pub mod mips;
|
||||
pub mod ppc;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use flagset::{flags, FlagSet};
|
||||
@@ -31,9 +34,11 @@ pub struct ObjSection {
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ObjInsArg {
|
||||
Arg(ppc750cl::Argument),
|
||||
PpcArg(ppc750cl::Argument),
|
||||
MipsArg(String),
|
||||
Reloc,
|
||||
RelocOffset,
|
||||
RelocWithBase,
|
||||
BranchOffset(i32),
|
||||
}
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ObjInsArgDiff {
|
||||
@@ -66,10 +71,13 @@ pub enum ObjInsDiffKind {
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjIns {
|
||||
pub ins: ppc750cl::Ins,
|
||||
pub address: u32,
|
||||
pub code: u32,
|
||||
pub op: u8,
|
||||
pub mnemonic: String,
|
||||
pub args: Vec<ObjInsArg>,
|
||||
pub reloc: Option<ObjReloc>,
|
||||
pub branch_dest: Option<u32>,
|
||||
}
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ObjInsDiff {
|
||||
@@ -88,6 +96,7 @@ pub struct ObjSymbol {
|
||||
pub name: String,
|
||||
pub demangled_name: Option<String>,
|
||||
pub address: u64,
|
||||
pub section_address: u64,
|
||||
pub size: u64,
|
||||
pub size_known: bool,
|
||||
pub flags: ObjSymbolFlagSet,
|
||||
@@ -97,8 +106,14 @@ pub struct ObjSymbol {
|
||||
pub instructions: Vec<ObjInsDiff>,
|
||||
pub match_percent: f32,
|
||||
}
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ObjArchitecture {
|
||||
PowerPc,
|
||||
Mips,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjInfo {
|
||||
pub architecture: ObjArchitecture,
|
||||
pub path: PathBuf,
|
||||
pub sections: Vec<ObjSection>,
|
||||
pub common: Vec<ObjSymbol>,
|
||||
@@ -116,6 +131,9 @@ pub enum ObjRelocKind {
|
||||
// PpcAddr14,
|
||||
PpcRel14,
|
||||
PpcEmbSda21,
|
||||
Mips26,
|
||||
MipsHi16,
|
||||
MipsLo16,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjReloc {
|
||||
|
||||
74
src/obj/mips.rs
Normal file
74
src/obj/mips.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use anyhow::Result;
|
||||
use rabbitizer::{config_set_register_fpr_abi_names, Abi, Instruction, SimpleOperandType};
|
||||
|
||||
use crate::obj::{ObjIns, ObjInsArg, ObjReloc};
|
||||
|
||||
pub fn process_code(
|
||||
data: &[u8],
|
||||
start_address: u64,
|
||||
end_address: u64,
|
||||
relocs: &[ObjReloc],
|
||||
) -> Result<(Vec<u8>, Vec<ObjIns>)> {
|
||||
config_set_register_fpr_abi_names(Abi::RABBITIZER_ABI_O32);
|
||||
|
||||
let ins_count = data.len() / 4;
|
||||
let mut ops = Vec::<u8>::with_capacity(ins_count);
|
||||
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
||||
let mut cur_addr = start_address as u32;
|
||||
for chunk in data.chunks_exact(4) {
|
||||
let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == cur_addr);
|
||||
let code = u32::from_be_bytes(chunk.try_into()?);
|
||||
let mut instruction = Instruction::new(code, cur_addr);
|
||||
|
||||
let op = instruction.instr_id() as u8;
|
||||
ops.push(op);
|
||||
|
||||
let mnemonic = instruction.instr_id().get_opcode_name().unwrap_or_default().to_string();
|
||||
let is_branch = instruction.is_branch();
|
||||
let branch_offset = instruction.branch_offset();
|
||||
let branch_dest =
|
||||
if is_branch { Some((cur_addr as i32 + branch_offset) as u32) } else { None };
|
||||
let args = instruction
|
||||
.simple_operands()
|
||||
.iter()
|
||||
.map(|op| match op.kind {
|
||||
SimpleOperandType::Imm | SimpleOperandType::Label => {
|
||||
if is_branch {
|
||||
ObjInsArg::BranchOffset(branch_offset)
|
||||
} else if let Some(reloc) = reloc {
|
||||
if matches!(&reloc.target_section, Some(s) if s == ".text")
|
||||
&& reloc.target.address > start_address
|
||||
&& reloc.target.address < end_address
|
||||
{
|
||||
// Inter-function reloc, convert to branch offset
|
||||
ObjInsArg::BranchOffset(reloc.target.address as i32 - cur_addr as i32)
|
||||
} else {
|
||||
ObjInsArg::Reloc
|
||||
}
|
||||
} else {
|
||||
ObjInsArg::MipsArg(op.disassembled.clone())
|
||||
}
|
||||
}
|
||||
SimpleOperandType::ImmBase => {
|
||||
if reloc.is_some() {
|
||||
ObjInsArg::RelocWithBase
|
||||
} else {
|
||||
ObjInsArg::MipsArg(op.disassembled.clone())
|
||||
}
|
||||
}
|
||||
_ => ObjInsArg::MipsArg(op.disassembled.clone()),
|
||||
})
|
||||
.collect();
|
||||
insts.push(ObjIns {
|
||||
address: cur_addr,
|
||||
code,
|
||||
op,
|
||||
mnemonic,
|
||||
args,
|
||||
reloc: reloc.cloned(),
|
||||
branch_dest,
|
||||
});
|
||||
cur_addr += 4;
|
||||
}
|
||||
Ok((ops, insts))
|
||||
}
|
||||
89
src/obj/ppc.rs
Normal file
89
src/obj/ppc.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use anyhow::Result;
|
||||
use ppc750cl::{disasm_iter, Argument};
|
||||
|
||||
use crate::obj::{ObjIns, ObjInsArg, ObjReloc, ObjRelocKind};
|
||||
|
||||
// Relative relocation, can be Simm or BranchOffset
|
||||
fn is_relative_arg(arg: &ObjInsArg) -> bool {
|
||||
matches!(arg, ObjInsArg::PpcArg(Argument::Simm(_)) | ObjInsArg::BranchOffset(_))
|
||||
}
|
||||
|
||||
// Relative or absolute relocation, can be Uimm, Simm or Offset
|
||||
fn is_rel_abs_arg(arg: &ObjInsArg) -> bool {
|
||||
matches!(arg, ObjInsArg::PpcArg(arg) if matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_)))
|
||||
}
|
||||
|
||||
fn is_offset_arg(arg: &ObjInsArg) -> bool { matches!(arg, ObjInsArg::PpcArg(Argument::Offset(_))) }
|
||||
|
||||
pub fn process_code(
|
||||
data: &[u8],
|
||||
address: u64,
|
||||
relocs: &[ObjReloc],
|
||||
) -> Result<(Vec<u8>, Vec<ObjIns>)> {
|
||||
let ins_count = data.len() / 4;
|
||||
let mut ops = Vec::<u8>::with_capacity(ins_count);
|
||||
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
||||
for mut ins in disasm_iter(data, address as u32) {
|
||||
let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == ins.addr);
|
||||
if let Some(reloc) = reloc {
|
||||
// Zero out relocations
|
||||
ins.code = match reloc.kind {
|
||||
ObjRelocKind::PpcEmbSda21 => ins.code & !0x1FFFFF,
|
||||
ObjRelocKind::PpcRel24 => ins.code & !0x3FFFFFC,
|
||||
ObjRelocKind::PpcRel14 => ins.code & !0xFFFC,
|
||||
ObjRelocKind::PpcAddr16Hi
|
||||
| ObjRelocKind::PpcAddr16Ha
|
||||
| ObjRelocKind::PpcAddr16Lo => ins.code & !0xFFFF,
|
||||
_ => ins.code,
|
||||
};
|
||||
}
|
||||
let simplified = ins.simplified();
|
||||
let mut args: Vec<ObjInsArg> = simplified
|
||||
.args
|
||||
.iter()
|
||||
.map(|a| match a {
|
||||
Argument::BranchDest(dest) => ObjInsArg::BranchOffset(dest.0),
|
||||
_ => ObjInsArg::PpcArg(a.clone()),
|
||||
})
|
||||
.collect();
|
||||
if let Some(reloc) = reloc {
|
||||
match reloc.kind {
|
||||
ObjRelocKind::PpcEmbSda21 => {
|
||||
args = vec![args[0].clone(), ObjInsArg::Reloc];
|
||||
}
|
||||
ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => {
|
||||
let arg = args
|
||||
.iter_mut()
|
||||
.rfind(|a| is_relative_arg(a))
|
||||
.ok_or_else(|| anyhow::Error::msg("Failed to locate rel arg for reloc"))?;
|
||||
*arg = ObjInsArg::Reloc;
|
||||
}
|
||||
ObjRelocKind::PpcAddr16Hi
|
||||
| ObjRelocKind::PpcAddr16Ha
|
||||
| ObjRelocKind::PpcAddr16Lo => {
|
||||
let arg = args.iter_mut().rfind(|a| is_rel_abs_arg(a)).ok_or_else(|| {
|
||||
anyhow::Error::msg("Failed to locate rel/abs arg for reloc")
|
||||
})?;
|
||||
*arg = if is_offset_arg(arg) {
|
||||
ObjInsArg::RelocWithBase
|
||||
} else {
|
||||
ObjInsArg::Reloc
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
ops.push(simplified.ins.op as u8);
|
||||
let suffix = simplified.ins.suffix();
|
||||
insts.push(ObjIns {
|
||||
address: simplified.ins.addr,
|
||||
code: simplified.ins.code,
|
||||
mnemonic: format!("{}{}", simplified.mnemonic, suffix),
|
||||
args,
|
||||
reloc: reloc.cloned(),
|
||||
op: 0,
|
||||
branch_dest: None,
|
||||
});
|
||||
}
|
||||
Ok((ops, insts))
|
||||
}
|
||||
@@ -1,85 +1,124 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use crate::{
|
||||
app::{AppConfig, ViewState},
|
||||
jobs::build::queue_build,
|
||||
app::{AppConfig, DiffKind, ViewState},
|
||||
jobs::{bindiff::queue_bindiff, build::queue_build},
|
||||
};
|
||||
|
||||
pub fn config_ui(ui: &mut egui::Ui, config: &Arc<RwLock<AppConfig>>, view_state: &mut ViewState) {
|
||||
let mut config_guard = config.write().unwrap();
|
||||
let AppConfig { project_dir, project_dir_change, build_asm_dir, build_src_dir, build_obj } =
|
||||
&mut *config_guard;
|
||||
let AppConfig {
|
||||
project_dir,
|
||||
project_dir_change,
|
||||
build_asm_dir,
|
||||
build_src_dir,
|
||||
build_obj,
|
||||
left_obj,
|
||||
right_obj,
|
||||
} = &mut *config_guard;
|
||||
|
||||
if ui.button("Select project dir").clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new().pick_folder() {
|
||||
*project_dir = Some(path);
|
||||
*project_dir_change = true;
|
||||
*build_asm_dir = None;
|
||||
*build_src_dir = None;
|
||||
*build_obj = None;
|
||||
}
|
||||
}
|
||||
if let Some(dir) = project_dir {
|
||||
ui.label(dir.to_string_lossy());
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
if let Some(project_dir) = project_dir {
|
||||
if ui.button("Select asm build dir").clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder() {
|
||||
*build_asm_dir = Some(path);
|
||||
if view_state.diff_kind == DiffKind::SplitObj {
|
||||
if ui.button("Select project dir").clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new().pick_folder() {
|
||||
*project_dir = Some(path);
|
||||
*project_dir_change = true;
|
||||
*build_asm_dir = None;
|
||||
*build_src_dir = None;
|
||||
*build_obj = None;
|
||||
}
|
||||
}
|
||||
if let Some(dir) = build_asm_dir {
|
||||
if let Some(dir) = project_dir {
|
||||
ui.label(dir.to_string_lossy());
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
if ui.button("Select src build dir").clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder() {
|
||||
*build_src_dir = Some(path);
|
||||
*build_obj = None;
|
||||
if let Some(project_dir) = project_dir {
|
||||
if ui.button("Select asm build dir").clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder()
|
||||
{
|
||||
*build_asm_dir = Some(path);
|
||||
*build_obj = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(dir) = build_src_dir {
|
||||
ui.label(dir.to_string_lossy());
|
||||
if let Some(dir) = build_asm_dir {
|
||||
ui.label(dir.to_string_lossy());
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
if ui.button("Select src build dir").clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder()
|
||||
{
|
||||
*build_src_dir = Some(path);
|
||||
*build_obj = None;
|
||||
}
|
||||
}
|
||||
if let Some(dir) = build_src_dir {
|
||||
ui.label(dir.to_string_lossy());
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
}
|
||||
|
||||
if let Some(build_src_dir) = build_src_dir {
|
||||
if ui.button("Select obj").clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new()
|
||||
.set_directory(&build_src_dir)
|
||||
.add_filter("Object file", &["o"])
|
||||
.pick_file()
|
||||
{
|
||||
let mut new_build_obj: Option<String> = None;
|
||||
if let Ok(obj_path) = path.strip_prefix(&build_src_dir) {
|
||||
new_build_obj = Some(obj_path.display().to_string());
|
||||
} else if let Some(build_asm_dir) = build_asm_dir {
|
||||
if let Ok(obj_path) = path.strip_prefix(&build_asm_dir) {
|
||||
if let Some(build_src_dir) = build_src_dir {
|
||||
if ui.button("Select obj").clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new()
|
||||
.set_directory(&build_src_dir)
|
||||
.add_filter("Object file", &["o", "elf"])
|
||||
.pick_file()
|
||||
{
|
||||
let mut new_build_obj: Option<String> = None;
|
||||
if let Ok(obj_path) = path.strip_prefix(&build_src_dir) {
|
||||
new_build_obj = Some(obj_path.display().to_string());
|
||||
} else if let Some(build_asm_dir) = build_asm_dir {
|
||||
if let Ok(obj_path) = path.strip_prefix(&build_asm_dir) {
|
||||
new_build_obj = Some(obj_path.display().to_string());
|
||||
}
|
||||
}
|
||||
if let Some(new_build_obj) = new_build_obj {
|
||||
*build_obj = Some(new_build_obj.clone());
|
||||
view_state.jobs.push(queue_build(new_build_obj, config.clone()));
|
||||
}
|
||||
}
|
||||
if let Some(new_build_obj) = new_build_obj {
|
||||
*build_obj = Some(new_build_obj.clone());
|
||||
view_state.jobs.push(queue_build(new_build_obj, config.clone()));
|
||||
}
|
||||
if let Some(build_obj) = build_obj {
|
||||
ui.label(&*build_obj);
|
||||
if ui.button("Build").clicked() {
|
||||
view_state.jobs.push(queue_build(build_obj.clone(), config.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
}
|
||||
if let Some(build_obj) = build_obj {
|
||||
ui.label(&*build_obj);
|
||||
if ui.button("Build").clicked() {
|
||||
view_state.jobs.push(queue_build(build_obj.clone(), config.clone()));
|
||||
} else if view_state.diff_kind == DiffKind::WholeBinary {
|
||||
if ui.button("Select left obj").clicked() {
|
||||
if let Some(path) =
|
||||
rfd::FileDialog::new().add_filter("Object file", &["o", "elf"]).pick_file()
|
||||
{
|
||||
*left_obj = Some(path);
|
||||
}
|
||||
}
|
||||
if let Some(obj) = left_obj {
|
||||
ui.label(obj.to_string_lossy());
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
if ui.button("Select right obj").clicked() {
|
||||
if let Some(path) =
|
||||
rfd::FileDialog::new().add_filter("Object file", &["o", "elf"]).pick_file()
|
||||
{
|
||||
*right_obj = Some(path);
|
||||
}
|
||||
}
|
||||
if let Some(obj) = right_obj {
|
||||
ui.label(obj.to_string_lossy());
|
||||
}
|
||||
|
||||
if let (Some(_), Some(_)) = (left_obj, right_obj) {
|
||||
if ui.button("Build").clicked() {
|
||||
view_state.jobs.push(queue_bindiff(config.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.checkbox(&mut view_state.reverse_fn_order, "Reverse function order (deferred)");
|
||||
|
||||
@@ -25,13 +25,39 @@ fn write_text(str: &str, color: Color32, job: &mut LayoutJob) {
|
||||
|
||||
fn write_reloc(reloc: &ObjReloc, job: &mut LayoutJob) {
|
||||
let name = reloc.target.demangled_name.as_ref().unwrap_or(&reloc.target.name);
|
||||
write_text(name, Color32::LIGHT_GRAY, job);
|
||||
match reloc.kind {
|
||||
ObjRelocKind::PpcAddr16Lo => write_text("@l", Color32::GRAY, job),
|
||||
ObjRelocKind::PpcAddr16Hi => write_text("@h", Color32::GRAY, job),
|
||||
ObjRelocKind::PpcAddr16Ha => write_text("@ha", Color32::GRAY, job),
|
||||
ObjRelocKind::PpcEmbSda21 => write_text("@sda21", Color32::GRAY, job),
|
||||
_ => {}
|
||||
ObjRelocKind::PpcAddr16Lo => {
|
||||
write_text(name, Color32::LIGHT_GRAY, job);
|
||||
write_text("@l", Color32::GRAY, job);
|
||||
}
|
||||
ObjRelocKind::PpcAddr16Hi => {
|
||||
write_text(name, Color32::LIGHT_GRAY, job);
|
||||
write_text("@h", Color32::GRAY, job);
|
||||
}
|
||||
ObjRelocKind::PpcAddr16Ha => {
|
||||
write_text(name, Color32::LIGHT_GRAY, job);
|
||||
write_text("@ha", Color32::GRAY, job);
|
||||
}
|
||||
ObjRelocKind::PpcEmbSda21 => {
|
||||
write_text(name, Color32::LIGHT_GRAY, job);
|
||||
write_text("@sda21", Color32::GRAY, job);
|
||||
}
|
||||
ObjRelocKind::MipsHi16 => {
|
||||
write_text("%hi(", Color32::GRAY, job);
|
||||
write_text(name, Color32::LIGHT_GRAY, job);
|
||||
write_text(")", Color32::GRAY, job);
|
||||
}
|
||||
ObjRelocKind::MipsLo16 => {
|
||||
write_text("%lo(", Color32::GRAY, job);
|
||||
write_text(name, Color32::LIGHT_GRAY, job);
|
||||
write_text(")", Color32::GRAY, job);
|
||||
}
|
||||
ObjRelocKind::Absolute
|
||||
| ObjRelocKind::PpcRel24
|
||||
| ObjRelocKind::PpcRel14
|
||||
| ObjRelocKind::Mips26 => {
|
||||
write_text(name, Color32::LIGHT_GRAY, job);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,7 +77,7 @@ fn write_ins(
|
||||
ObjInsDiffKind::Insert => Color32::GREEN,
|
||||
};
|
||||
write_text(
|
||||
&ins.mnemonic,
|
||||
&format!("{:<11}", ins.mnemonic),
|
||||
match diff_kind {
|
||||
ObjInsDiffKind::OpMismatch => Color32::LIGHT_BLUE,
|
||||
_ => base_color,
|
||||
@@ -72,17 +98,13 @@ fn write_ins(
|
||||
base_color
|
||||
};
|
||||
match arg {
|
||||
ObjInsArg::Arg(arg) => match arg {
|
||||
ObjInsArg::PpcArg(arg) => match arg {
|
||||
Argument::Offset(val) => {
|
||||
write_text(&format!("{}", val), color, job);
|
||||
write_text("(", base_color, job);
|
||||
writing_offset = true;
|
||||
continue;
|
||||
}
|
||||
Argument::BranchDest(dest) => {
|
||||
let addr = dest.0 + ins.ins.addr as i32 - base_addr as i32;
|
||||
write_text(&format!("{:x}", addr), color, job);
|
||||
}
|
||||
Argument::Uimm(_) | Argument::Simm(_) => {
|
||||
write_text(&format!("{}", arg), color, job);
|
||||
}
|
||||
@@ -93,12 +115,19 @@ fn write_ins(
|
||||
ObjInsArg::Reloc => {
|
||||
write_reloc(ins.reloc.as_ref().unwrap(), job);
|
||||
}
|
||||
ObjInsArg::RelocOffset => {
|
||||
ObjInsArg::RelocWithBase => {
|
||||
write_reloc(ins.reloc.as_ref().unwrap(), job);
|
||||
write_text("(", base_color, job);
|
||||
writing_offset = true;
|
||||
continue;
|
||||
}
|
||||
ObjInsArg::MipsArg(str) => {
|
||||
write_text(str.strip_prefix('$').unwrap_or(str), color, job);
|
||||
}
|
||||
ObjInsArg::BranchOffset(offset) => {
|
||||
let addr = offset + ins.address as i32 - base_addr as i32;
|
||||
write_text(&format!("{:x}", addr), color, job);
|
||||
}
|
||||
}
|
||||
if writing_offset {
|
||||
write_text(")", base_color, job);
|
||||
@@ -112,10 +141,10 @@ fn ins_hover_ui(ui: &mut egui::Ui, ins: &ObjIns) {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap = Some(false);
|
||||
|
||||
ui.label(format!("{:02X?}", ins.ins.code.to_be_bytes()));
|
||||
ui.label(format!("{:02X?}", ins.code.to_be_bytes()));
|
||||
|
||||
for arg in &ins.args {
|
||||
if let ObjInsArg::Arg(arg) = arg {
|
||||
if let ObjInsArg::PpcArg(arg) = arg {
|
||||
match arg {
|
||||
Argument::Uimm(v) => {
|
||||
ui.label(format!("{} == {}", v, v.0));
|
||||
@@ -153,7 +182,7 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
|
||||
// if ui.button("Copy hex").clicked() {}
|
||||
|
||||
for arg in &ins.args {
|
||||
if let ObjInsArg::Arg(arg) = arg {
|
||||
if let ObjInsArg::PpcArg(arg) = arg {
|
||||
match arg {
|
||||
Argument::Uimm(v) => {
|
||||
if ui.button(format!("Copy \"{}\"", v)).clicked() {
|
||||
@@ -236,7 +265,7 @@ fn asm_row_ui(ui: &mut egui::Ui, ins_diff: &ObjInsDiff, symbol: &ObjSymbol) {
|
||||
ObjInsDiffKind::Insert => Color32::GREEN,
|
||||
};
|
||||
write_text(
|
||||
&format!("{:<6}", format!("{:x}:", ins.ins.addr - symbol.address as u32)),
|
||||
&format!("{:<6}", format!("{:x}:", ins.address - symbol.address as u32)),
|
||||
base_color,
|
||||
&mut job,
|
||||
);
|
||||
|
||||
@@ -66,21 +66,28 @@ fn symbol_ui(
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
job.append("] (", 0.0, TextFormat {
|
||||
font_id: font_id.clone(),
|
||||
color: Color32::GRAY,
|
||||
..Default::default()
|
||||
});
|
||||
job.append(&format!("{:.0}%", symbol.match_percent), 0.0, TextFormat {
|
||||
font_id: font_id.clone(),
|
||||
color: match_color_for_symbol(symbol),
|
||||
..Default::default()
|
||||
});
|
||||
job.append(") ", 0.0, TextFormat {
|
||||
job.append("] ", 0.0, TextFormat {
|
||||
font_id: font_id.clone(),
|
||||
color: Color32::GRAY,
|
||||
..Default::default()
|
||||
});
|
||||
if symbol.match_percent > 0.0 {
|
||||
job.append("(", 0.0, TextFormat {
|
||||
font_id: font_id.clone(),
|
||||
color: Color32::GRAY,
|
||||
..Default::default()
|
||||
});
|
||||
job.append(&format!("{:.0}%", symbol.match_percent), 0.0, TextFormat {
|
||||
font_id: font_id.clone(),
|
||||
color: match_color_for_symbol(symbol),
|
||||
..Default::default()
|
||||
});
|
||||
job.append(") ", 0.0, TextFormat {
|
||||
font_id: font_id.clone(),
|
||||
color: Color32::GRAY,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
job.append(name, 0.0, TextFormat { font_id, color: Color32::WHITE, ..Default::default() });
|
||||
let response = SelectableLabel::new(selected, job).ui(ui);
|
||||
if response.clicked() {
|
||||
|
||||
Reference in New Issue
Block a user