diff --git a/objdiff-core/config-schema.json b/objdiff-core/config-schema.json index f46c730..1945504 100644 --- a/objdiff-core/config-schema.json +++ b/objdiff-core/config-schema.json @@ -194,6 +194,13 @@ "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", @@ -253,6 +260,13 @@ "mips.registerPrefix" ] }, + { + "id": "ppc", + "name": "PPC", + "properties": [ + "ppc.calculatePoolRelocations" + ] + }, { "id": "x86", "name": "x86", diff --git a/objdiff-core/src/arch/mod.rs b/objdiff-core/src/arch/mod.rs index cef20a7..fb592de 100644 --- a/objdiff-core/src/arch/mod.rs +++ b/objdiff-core/src/arch/mod.rs @@ -10,8 +10,8 @@ use crate::{ DiffObjConfig, }, obj::{ - InstructionArg, Object, ParsedInstruction, RelocationFlags, ResolvedInstructionRef, - ScannedInstruction, SymbolFlagSet, SymbolKind, + InstructionArg, Object, ParsedInstruction, Relocation, RelocationFlags, + ResolvedInstructionRef, ScannedInstruction, Symbol, SymbolFlagSet, SymbolKind, }, util::ReallySigned, }; @@ -212,6 +212,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 { + Vec::new() + } + fn implcit_addend( &self, file: &object::File<'_>, diff --git a/objdiff-core/src/arch/ppc.rs b/objdiff-core/src/arch/ppc.rs index d98ee6e..e24a208 100644 --- a/objdiff-core/src/arch/ppc.rs +++ b/objdiff-core/src/arch/ppc.rs @@ -1,5 +1,5 @@ use alloc::{ - collections::BTreeMap, + collections::{BTreeMap, BTreeSet}, string::{String, ToString}, vec, vec::Vec, @@ -13,12 +13,13 @@ use object::{elf, Object as _, ObjectSection as _, ObjectSymbol as _}; use crate::{ arch::{Arch, DataType}, diff::{ + data::resolve_relocation, display::{ContextItem, HoverItem, HoverItemColor, InstructionPart, SymbolNavigationKind}, DiffObjConfig, }, obj::{ InstructionRef, Object, Relocation, RelocationFlags, ResolvedInstructionRef, - ResolvedRelocation, ScannedInstruction, SymbolFlag, SymbolFlagSet, + ResolvedRelocation, ScannedInstruction, Symbol, SymbolFlag, SymbolFlagSet, }, }; @@ -157,6 +158,16 @@ impl Arch for ArchPpc { Ok(()) } + fn generate_pooled_relocations( + &self, + address: u64, + code: &[u8], + relocations: &[Relocation], + symbols: &[Symbol], + ) -> Vec { + generate_fake_pool_relocations_for_function(address, code, relocations, symbols) + } + fn implcit_addend( &self, _file: &object::File<'_>, @@ -501,21 +512,26 @@ fn guess_data_type_from_load_store_inst_op(inst_op: ppc750cl::Opcode) -> Option< } } -// Given an instruction, determine if it could accessing data at the address in a register. -// If so, return the offset added to the register's address, the register containing that address, -// and (optionally) which destination register the address is being copied into. -#[expect(unused)] -fn get_offset_and_addr_gpr_for_possible_pool_reference( +struct PoolReference { + addr_src_gpr: ppc750cl::GPR, + addr_offset: i16, + addr_dst_gpr: Option, +} + +// Given an instruction, check if it could be accessing pooled data at the address in a register. +// If so, return information pertaining to where the instruction is getting that address from and +// what it's doing with the address (e.g. copying it into another register, adding an offset, etc). +fn get_pool_reference_for_inst( opcode: ppc750cl::Opcode, simplified: &ppc750cl::ParsedIns, -) -> Option<(i16, ppc750cl::GPR, Option)> { +) -> Option { use ppc750cl::{Argument, Opcode}; let args = &simplified.args; if guess_data_type_from_load_store_inst_op(opcode).is_some() { match (args[1], args[2]) { (Argument::Offset(offset), Argument::GPR(addr_src_gpr)) => { // e.g. lwz. Immediate offset. - Some((offset.0, addr_src_gpr, None)) + Some(PoolReference { addr_src_gpr, addr_offset: offset.0, addr_dst_gpr: None }) } (Argument::GPR(addr_src_gpr), Argument::GPR(_offset_gpr)) => { // e.g. lwzx. The offset is in a register and was likely calculated from an index. @@ -523,7 +539,7 @@ fn get_offset_and_addr_gpr_for_possible_pool_reference( // It may be possible to show all elements by figuring out the stride of the array // from the calculations performed on the index before it's put into offset_gpr, but // this would be much more complicated, so it's not currently done. - Some((0, addr_src_gpr, None)) + Some(PoolReference { addr_src_gpr, addr_offset: 0, addr_dst_gpr: None }) } _ => None, } @@ -541,20 +557,32 @@ fn get_offset_and_addr_gpr_for_possible_pool_reference( Argument::GPR(addr_dst_gpr), Argument::GPR(addr_src_gpr), Argument::Simm(simm), - ) => Some((simm.0, addr_src_gpr, Some(addr_dst_gpr))), + ) => Some(PoolReference { + addr_src_gpr, + addr_offset: simm.0, + addr_dst_gpr: Some(addr_dst_gpr), + }), ( // `mr` or `mr.` Opcode::Or, Argument::GPR(addr_dst_gpr), Argument::GPR(addr_src_gpr), Argument::None, - ) => Some((0, addr_src_gpr, Some(addr_dst_gpr))), + ) => Some(PoolReference { + addr_src_gpr, + addr_offset: 0, + addr_dst_gpr: Some(addr_dst_gpr), + }), ( Opcode::Add, Argument::GPR(addr_dst_gpr), Argument::GPR(addr_src_gpr), Argument::GPR(_offset_gpr), - ) => Some((0, addr_src_gpr, Some(addr_dst_gpr))), + ) => Some(PoolReference { + addr_src_gpr, + addr_offset: 0, + addr_dst_gpr: Some(addr_dst_gpr), + }), _ => None, } } @@ -562,7 +590,6 @@ fn get_offset_and_addr_gpr_for_possible_pool_reference( // Remove the relocation we're keeping track of in a particular register when an instruction reuses // that register to hold some other value, unrelated to pool relocation addresses. -#[expect(unused)] fn clear_overwritten_gprs(ins: ppc750cl::Ins, gpr_pool_relocs: &mut BTreeMap) { use ppc750cl::{Argument, Arguments, Opcode}; let mut def_args = Arguments::default(); @@ -582,250 +609,238 @@ fn clear_overwritten_gprs(ins: ppc750cl::Ins, gpr_pool_relocs: &mut BTreeMap Option { -// let offset_from_pool = pool_reloc.addend + offset as i64; -// let target_address = pool_reloc.sy.address.checked_add_signed(offset_from_pool)?; -// let target; -// let addend; -// if pool_reloc.target.orig_section_index.is_some() { -// // If the target symbol is within this current object, then we also need to create a fake -// // target symbol to go inside our fake relocation. This is because we don't have access to -// // list of all symbols in this section, so we can't find the real symbol within the pool -// // based on its address yet. Instead we make a placeholder that has the correct -// // `orig_section_index` and `address` fields, and then later on when this information is -// // displayed to the user, we can find the real symbol by searching through the object's -// // section's symbols for one that contains this address. -// target = ObjSymbol { -// name: "".to_string(), -// demangled_name: None, -// address: target_address, -// section_address: 0, -// size: 0, -// size_known: false, -// kind: Default::default(), -// flags: Default::default(), -// orig_section_index: pool_reloc.target.orig_section_index, -// virtual_address: None, -// original_index: None, -// bytes: vec![], -// }; -// // The addend is also fake because we don't know yet if the `target_address` here is the exact -// // start of the symbol or if it's in the middle of it. -// addend = 0; -// } else { -// // But if the target symbol is in a different object (extern), then we simply copy the pool -// // relocation's target. This is because it won't be possible to locate the actual symbol -// // later on based only off of an offset without knowing the object or section it's in. And -// // doing that for external symbols would also be unnecessary, because when the compiler -// // generates an instruction that accesses an external "pool" plus some offset, that won't be -// // a normal pool that contains other symbols within it that we want to display. It will be -// // something like a vtable for a class with multiple inheritance (for example, dCcD_Cyl in -// // The Wind Waker). So just showing that vtable symbol plus an addend to represent the -// // offset into it works fine in this case, no fake symbol to hold an address is necessary. -// target = pool_reloc.target.clone(); -// addend = pool_reloc.addend; -// }; -// Some(ObjReloc { -// flags: RelocationFlags::Elf { r_type: elf::R_PPC_NONE }, -// address: cur_addr as u64, -// target, -// addend, -// }) -// } -// -// // Searches through all instructions in a function, determining which registers have the addresses -// // of pooled data relocations in them, finding which instructions load data from those addresses, -// // and constructing a mapping of the address of that instruction to a "fake pool relocation" that -// // simulates what that instruction's relocation would look like if data hadn't been pooled. -// // This method tries to follow the function's proper control flow. It keeps track of a queue of -// // states it hasn't traversed yet, where each state holds an instruction address and a HashMap of -// // which registers hold which pool relocations at that point. -// // When a conditional or unconditional branch is encountered, the destination of the branch is added -// // to the queue. Conditional branches will traverse both the path where the branch is taken and the -// // one where it's not. Unconditional branches only follow the branch, ignoring any code immediately -// // after the branch instruction. -// // Limitations: This method cannot read jump tables. This is because the jump tables are located in -// // the .data section, but ObjArch.process_code only has access to the .text section. In order to -// // work around this limitation and avoid completely missing most code inside switch statements that -// // use jump tables, we instead guess that any parts of a function we missed were switch cases, and -// // traverse them as if the last `bctr` before that address had branched there. This should be fairly -// // accurate in practice - in testing the only instructions it seems to miss are double branches that -// // the compiler generates in error which can never be reached during normal execution anyway. -// fn generate_fake_pool_reloc_for_addr_mapping( -// func_address: u64, -// code: &[u8], -// relocations: &[ObjReloc], -// ) -> BTreeMap { -// let mut visited_ins_addrs = BTreeSet::new(); -// let mut pool_reloc_for_addr = BTreeMap::new(); -// let mut ins_iters_with_gpr_state = -// vec![(InsIter::new(code, func_address as u32), BTreeMap::new())]; -// let mut gpr_state_at_bctr = BTreeMap::new(); -// while let Some((ins_iter, mut gpr_pool_relocs)) = ins_iters_with_gpr_state.pop() { -// for (cur_addr, ins) in ins_iter { -// if visited_ins_addrs.contains(&cur_addr) { -// // Avoid getting stuck in an infinite loop when following looping branches. -// break; -// } -// visited_ins_addrs.insert(cur_addr); -// -// let simplified = ins.simplified(); -// -// // First handle traversing the function's control flow. -// let mut branch_dest = None; -// for arg in simplified.args_iter() { -// if let Argument::BranchDest(dest) = arg { -// let dest = cur_addr.wrapping_add_signed(dest.0); -// branch_dest = Some(dest); -// break; -// } -// } -// if let Some(branch_dest) = branch_dest { -// if branch_dest >= func_address as u32 -// && (branch_dest - func_address as u32) < code.len() as u32 -// { -// let dest_offset_into_func = branch_dest - func_address as u32; -// let dest_code_slice = &code[dest_offset_into_func as usize..]; -// match ins.op { -// Opcode::Bc => { -// // Conditional branch. -// // Add the branch destination to the queue to do later. -// ins_iters_with_gpr_state.push(( -// InsIter::new(dest_code_slice, branch_dest), -// gpr_pool_relocs.clone(), -// )); -// // Then continue on with the current iterator. -// } -// Opcode::B => { -// if simplified.mnemonic != "bl" { -// // Unconditional branch. -// // Add the branch destination to the queue. -// ins_iters_with_gpr_state.push(( -// InsIter::new(dest_code_slice, branch_dest), -// gpr_pool_relocs.clone(), -// )); -// // Break out of the current iterator so we can do the newly added one. -// break; -// } -// } -// _ => unreachable!(), -// } -// } -// } -// if let Opcode::Bcctr = ins.op { -// if simplified.mnemonic == "bctr" { -// // Unconditional branch to count register. -// // Likely a jump table. -// gpr_state_at_bctr.insert(cur_addr, gpr_pool_relocs.clone()); -// } -// } -// -// // Then handle keeping track of which GPR contains which pool relocation. -// let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr); -// if let Some(reloc) = reloc { -// // This instruction has a real relocation, so it may be a pool load we want to keep -// // track of. -// let args = &simplified.args; -// match (ins.op, args[0], args[1], args[2]) { -// ( -// // `lis` + `addi` -// Opcode::Addi, -// Argument::GPR(addr_dst_gpr), -// Argument::GPR(_addr_src_gpr), -// Argument::Simm(_simm), -// ) => { -// gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone()); -// } -// ( -// // `lis` + `ori` -// Opcode::Ori, -// Argument::GPR(addr_dst_gpr), -// Argument::GPR(_addr_src_gpr), -// Argument::Uimm(_uimm), -// ) => { -// gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone()); -// } -// (Opcode::B, _, _, _) => { -// if simplified.mnemonic == "bl" { -// // When encountering a function call, clear any active pool relocations from -// // the volatile registers (r0, r3-r12), but not the nonvolatile registers. -// gpr_pool_relocs.remove(&0); -// for gpr in 3..12 { -// gpr_pool_relocs.remove(&gpr); -// } -// } -// } -// _ => { -// clear_overwritten_gprs(ins, &mut gpr_pool_relocs); -// } -// } -// } else if let Some((offset, addr_src_gpr, addr_dst_gpr)) = -// get_offset_and_addr_gpr_for_possible_pool_reference(ins.op, &simplified) -// { -// // This instruction doesn't have a real relocation, so it may be a reference to one of -// // the already-loaded pools. -// if let Some(pool_reloc) = gpr_pool_relocs.get(&addr_src_gpr.0) { -// if let Some(fake_pool_reloc) = -// make_fake_pool_reloc(offset, cur_addr, pool_reloc) -// { -// pool_reloc_for_addr.insert(cur_addr, fake_pool_reloc); -// } -// if let Some(addr_dst_gpr) = addr_dst_gpr { -// // If the address of the pool relocation got copied into another register, we -// // need to keep track of it in that register too as future instructions may -// // reference the symbol indirectly via this new register, instead of the -// // register the symbol's address was originally loaded into. -// // For example, the start of the function might `lis` + `addi` the start of the -// // ...data pool into r25, and then later the start of a loop will `addi` r25 -// // with the offset within the .data section of an array variable into r21. -// // Then the body of the loop will `lwzx` one of the array elements from r21. -// let mut new_reloc = pool_reloc.clone(); -// new_reloc.addend += offset as i64; -// gpr_pool_relocs.insert(addr_dst_gpr.0, new_reloc); -// } else { -// clear_overwritten_gprs(ins, &mut gpr_pool_relocs); -// } -// } else { -// clear_overwritten_gprs(ins, &mut gpr_pool_relocs); -// } -// } else { -// clear_overwritten_gprs(ins, &mut gpr_pool_relocs); -// } -// } -// -// // Finally, if we're about to finish the outer loop and don't have any more control flow to -// // follow, we check if there are any instruction addresses in this function that we missed. -// // If so, and if there were any `bctr` instructions before those points in this function, -// // then we try to traverse those missing spots as switch cases. -// if ins_iters_with_gpr_state.is_empty() { -// let unseen_addrs = (func_address as u32..func_address as u32 + code.len() as u32) -// .step_by(4) -// .filter(|addr| !visited_ins_addrs.contains(addr)); -// for unseen_addr in unseen_addrs { -// let prev_bctr_gpr_state = gpr_state_at_bctr -// .iter() -// .filter(|(&addr, _)| addr < unseen_addr) -// .min_by_key(|(&addr, _)| addr) -// .map(|(_, gpr_state)| gpr_state); -// if let Some(gpr_pool_relocs) = prev_bctr_gpr_state { -// let dest_offset_into_func = unseen_addr - func_address as u32; -// let dest_code_slice = &code[dest_offset_into_func as usize..]; -// ins_iters_with_gpr_state.push(( -// InsIter::new(dest_code_slice, unseen_addr), -// gpr_pool_relocs.clone(), -// )); -// break; -// } -// } -// } -// } -// -// pool_reloc_for_addr -// } +// We create a fake relocation for an instruction, vaguely simulating what the actual relocation +// might have looked like if it wasn't pooled. This is so minimal changes are needed to display +// pooled accesses vs non-pooled accesses. We set the relocation type to R_PPC_NONE to indicate that +// there isn't really a relocation here, as copying the pool relocation's type wouldn't make sense. +// Also, if this instruction is accessing the middle of a symbol instead of the start, we add an +// addend to indicate that. +fn make_fake_pool_reloc( + offset: i16, + cur_addr: u32, + pool_reloc: &Relocation, + symbols: &[Symbol], +) -> Option { + let pool_reloc = resolve_relocation(symbols, pool_reloc); + let offset_from_pool = pool_reloc.relocation.addend + offset as i64; + let target_address = pool_reloc.symbol.address.checked_add_signed(offset_from_pool)?; + let target_symbol; + let addend; + if let Some(section_index) = pool_reloc.symbol.section { + // Find the exact data symbol within the pool being accessed here based on the address. + target_symbol = symbols.iter().position(|s| { + s.section == Some(section_index) + && s.size > 0 + && (s.address..s.address + s.size).contains(&target_address) + })?; + addend = target_address.checked_sub(symbols[target_symbol].address)? as i64; + } else { + // If the target symbol is in a different object (extern), we simply copy the pool + // relocation's target. This is because it's not possible to locate the actual symbol if + // it's extern. And doing that for external symbols would also be unnecessary, because when + // the compiler generates an instruction that accesses an external "pool" plus some offset, + // that won't be a normal pool that contains other symbols within it that we want to + // display. It will be something like a vtable for a class with multiple inheritance (for + // example, dCcD_Cyl in The Wind Waker). So just showing that vtable symbol plus an addend + // to represent the offset into it works fine in this case. + target_symbol = pool_reloc.relocation.target_symbol; + addend = pool_reloc.relocation.addend + offset_from_pool; + } + Some(Relocation { + flags: RelocationFlags::Elf(elf::R_PPC_NONE), + address: cur_addr as u64, + target_symbol, + addend, + }) +} + +// Searches through all instructions in a function, determining which registers have the addresses +// of pooled data relocations in them, finding which instructions load data from those addresses, +// and returns a Vec of "fake pool relocations" that simulate what a relocation for that instruction +// would look like if data hadn't been pooled. +// This method tries to follow the function's proper control flow. It keeps track of a queue of +// states it hasn't traversed yet, where each state holds an instruction address and a HashMap of +// which registers hold which pool relocations at that point. +// When a conditional or unconditional branch is encountered, the destination of the branch is added +// to the queue. Conditional branches will traverse both the path where the branch is taken and the +// one where it's not. Unconditional branches only follow the branch, ignoring any code immediately +// after the branch instruction. +// Limitations: This method does not currently read switch statement jump tables. +// Instead, we guess that any parts of a function we missed were switch cases, and traverse them as +// if the last `bctr` before that address had branched there. This should be fairly accurate in +// practice - in testing the only instructions it seems to miss are double branches that the +// compiler generates in error which can never be reached during normal execution anyway. +// It should be possible to implement jump tables properly by reading them out of .data. But this +// will require keeping track of what value is loaded into each register so we can retrieve the jump +// table symbol when we encounter a `bctr`. +fn generate_fake_pool_relocations_for_function( + func_address: u64, + code: &[u8], + relocations: &[Relocation], + symbols: &[Symbol], +) -> Vec { + use ppc750cl::{Argument, InsIter, Opcode}; + let mut visited_ins_addrs = BTreeSet::new(); + let mut pool_reloc_for_addr = BTreeMap::new(); + let mut ins_iters_with_gpr_state = + vec![(InsIter::new(code, func_address as u32), BTreeMap::new())]; + let mut gpr_state_at_bctr = BTreeMap::new(); + while let Some((ins_iter, mut gpr_pool_relocs)) = ins_iters_with_gpr_state.pop() { + for (cur_addr, ins) in ins_iter { + if visited_ins_addrs.contains(&cur_addr) { + // Avoid getting stuck in an infinite loop when following looping branches. + break; + } + visited_ins_addrs.insert(cur_addr); + + let simplified = ins.simplified(); + + // First handle traversing the function's control flow. + let mut branch_dest = None; + for arg in simplified.args_iter() { + if let Argument::BranchDest(dest) = arg { + let dest = cur_addr.wrapping_add_signed(dest.0); + branch_dest = Some(dest); + break; + } + } + if let Some(branch_dest) = branch_dest { + if branch_dest >= func_address as u32 + && (branch_dest - func_address as u32) < code.len() as u32 + { + let dest_offset_into_func = branch_dest - func_address as u32; + let dest_code_slice = &code[dest_offset_into_func as usize..]; + match ins.op { + Opcode::Bc => { + // Conditional branch. + // Add the branch destination to the queue to do later. + ins_iters_with_gpr_state.push(( + InsIter::new(dest_code_slice, branch_dest), + gpr_pool_relocs.clone(), + )); + // Then continue on with the current iterator. + } + Opcode::B => { + if simplified.mnemonic != "bl" { + // Unconditional branch. + // Add the branch destination to the queue. + ins_iters_with_gpr_state.push(( + InsIter::new(dest_code_slice, branch_dest), + gpr_pool_relocs.clone(), + )); + // Break out of the current iterator so we can do the newly added one. + break; + } + } + _ => unreachable!(), + } + } + } + if let Opcode::Bcctr = ins.op { + if simplified.mnemonic == "bctr" { + // Unconditional branch to count register. + // Likely a jump table. + gpr_state_at_bctr.insert(cur_addr, gpr_pool_relocs.clone()); + } + } + + // Then handle keeping track of which GPR contains which pool relocation. + let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr); + if let Some(reloc) = reloc { + // This instruction has a real relocation, so it may be a pool load we want to keep + // track of. + let args = &simplified.args; + match (ins.op, args[0], args[1], args[2]) { + ( + // `lis` + `addi` + Opcode::Addi, + Argument::GPR(addr_dst_gpr), + Argument::GPR(_addr_src_gpr), + Argument::Simm(_simm), + ) => { + gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone()); + } + ( + // `lis` + `ori` + Opcode::Ori, + Argument::GPR(addr_dst_gpr), + Argument::GPR(_addr_src_gpr), + Argument::Uimm(_uimm), + ) => { + gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone()); + } + (Opcode::B, _, _, _) => { + if simplified.mnemonic == "bl" { + // When encountering a function call, clear any active pool relocations from + // the volatile registers (r0, r3-r12), but not the nonvolatile registers. + gpr_pool_relocs.remove(&0); + for gpr in 3..12 { + gpr_pool_relocs.remove(&gpr); + } + } + } + _ => { + clear_overwritten_gprs(ins, &mut gpr_pool_relocs); + } + } + } else if let Some(pool_ref) = get_pool_reference_for_inst(ins.op, &simplified) { + // This instruction doesn't have a real relocation, so it may be a reference to one of + // the already-loaded pools. + if let Some(pool_reloc) = gpr_pool_relocs.get(&pool_ref.addr_src_gpr.0) { + if let Some(fake_pool_reloc) = + make_fake_pool_reloc(pool_ref.addr_offset, cur_addr, pool_reloc, symbols) + { + pool_reloc_for_addr.insert(cur_addr, fake_pool_reloc); + } + if let Some(addr_dst_gpr) = pool_ref.addr_dst_gpr { + // If the address of the pool relocation got copied into another register, we + // need to keep track of it in that register too as future instructions may + // reference the symbol indirectly via this new register, instead of the + // register the symbol's address was originally loaded into. + // For example, the start of the function might `lis` + `addi` the start of the + // ...data pool into r25, and then later the start of a loop will `addi` r25 + // with the offset within the .data section of an array variable into r21. + // Then the body of the loop will `lwzx` one of the array elements from r21. + let mut new_reloc = pool_reloc.clone(); + new_reloc.addend += pool_ref.addr_offset as i64; + gpr_pool_relocs.insert(addr_dst_gpr.0, new_reloc); + } else { + clear_overwritten_gprs(ins, &mut gpr_pool_relocs); + } + } else { + clear_overwritten_gprs(ins, &mut gpr_pool_relocs); + } + } else { + clear_overwritten_gprs(ins, &mut gpr_pool_relocs); + } + } + + // Finally, if we're about to finish the outer loop and don't have any more control flow to + // follow, we check if there are any instruction addresses in this function that we missed. + // If so, and if there were any `bctr` instructions before those points in this function, + // then we try to traverse those missing spots as switch cases. + if ins_iters_with_gpr_state.is_empty() { + let unseen_addrs = (func_address as u32..func_address as u32 + code.len() as u32) + .step_by(4) + .filter(|addr| !visited_ins_addrs.contains(addr)); + for unseen_addr in unseen_addrs { + let prev_bctr_gpr_state = gpr_state_at_bctr + .iter() + .filter(|(&addr, _)| addr < unseen_addr) + .min_by_key(|(&addr, _)| addr) + .map(|(_, gpr_state)| gpr_state); + if let Some(gpr_pool_relocs) = prev_bctr_gpr_state { + let dest_offset_into_func = unseen_addr - func_address as u32; + let dest_code_slice = &code[dest_offset_into_func as usize..]; + ins_iters_with_gpr_state.push(( + InsIter::new(dest_code_slice, unseen_addr), + gpr_pool_relocs.clone(), + )); + break; + } + } + } + } + + pool_reloc_for_addr.values().cloned().collect() +} diff --git a/objdiff-core/src/diff/code.rs b/objdiff-core/src/diff/code.rs index 9e8e2ac..7b273fc 100644 --- a/objdiff-core/src/diff/code.rs +++ b/objdiff-core/src/diff/code.rs @@ -502,17 +502,3 @@ fn diff_instruction( Ok(InstructionDiffResult::new(InstructionDiffKind::None)) } - -// TODO -// fn find_symbol_matching_fake_symbol_in_sections( -// fake_symbol: &ObjSymbol, -// sections: &[ObjSection], -// ) -> Option { -// let orig_section_index = fake_symbol.orig_section_index?; -// let section = sections.iter().find(|s| s.orig_index == orig_section_index)?; -// let real_symbol = section -// .symbols -// .iter() -// .find(|s| s.size > 0 && (s.address..s.address + s.size).contains(&fake_symbol.address))?; -// Some(real_symbol.clone()) -// } diff --git a/objdiff-core/src/diff/data.rs b/objdiff-core/src/diff/data.rs index 742e38d..6275b31 100644 --- a/objdiff-core/src/diff/data.rs +++ b/objdiff-core/src/diff/data.rs @@ -8,7 +8,7 @@ use super::{ code::{address_eq, section_name_eq}, DataDiff, DataDiffKind, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff, }; -use crate::obj::{Object, Relocation, ResolvedRelocation, SymbolFlag, SymbolKind}; +use crate::obj::{Object, Relocation, ResolvedRelocation, Symbol, SymbolFlag, SymbolKind}; pub fn diff_bss_symbol( left_obj: &Object, @@ -64,10 +64,10 @@ fn reloc_eq( #[inline] pub fn resolve_relocation<'obj>( - obj: &'obj Object, + symbols: &'obj [Symbol], reloc: &'obj Relocation, ) -> ResolvedRelocation<'obj> { - let symbol = &obj.symbols[reloc.target_symbol]; + let symbol = &symbols[reloc.target_symbol]; ResolvedRelocation { relocation: reloc, symbol } } @@ -88,7 +88,7 @@ fn diff_data_relocs_for_range<'left, 'right>( continue; } let left_offset = left_reloc.address as usize - left_range.start; - let left_reloc = resolve_relocation(left_obj, left_reloc); + let left_reloc = resolve_relocation(&left_obj.symbols, left_reloc); let Some(right_reloc) = right_section.relocations.iter().find(|r| { if !right_range.contains(&(r.address as usize)) { return false; @@ -99,7 +99,7 @@ fn diff_data_relocs_for_range<'left, 'right>( diffs.push((DataDiffKind::Delete, Some(left_reloc), None)); continue; }; - let right_reloc = resolve_relocation(right_obj, right_reloc); + let right_reloc = resolve_relocation(&right_obj.symbols, right_reloc); if reloc_eq(left_obj, right_obj, left_reloc, right_reloc) { diffs.push((DataDiffKind::None, Some(left_reloc), Some(right_reloc))); } else { @@ -111,7 +111,7 @@ fn diff_data_relocs_for_range<'left, 'right>( continue; } let right_offset = right_reloc.address as usize - right_range.start; - let right_reloc = resolve_relocation(right_obj, right_reloc); + let right_reloc = resolve_relocation(&right_obj.symbols, right_reloc); let Some(_) = left_section.relocations.iter().find(|r| { if !left_range.contains(&(r.address as usize)) { return false; diff --git a/objdiff-core/src/obj/read.rs b/objdiff-core/src/obj/read.rs index 584f294..968191b 100644 --- a/objdiff-core/src/obj/read.rs +++ b/objdiff-core/src/obj/read.rs @@ -6,7 +6,7 @@ use alloc::{ }; use core::cmp::Ordering; -use anyhow::{bail, ensure, Context, Result}; +use anyhow::{anyhow, bail, ensure, Context, Result}; use object::{Object as _, ObjectSection as _, ObjectSymbol as _}; use crate::{ @@ -421,6 +421,44 @@ fn map_relocations( Ok(()) } +fn calculate_pooled_relocations( + arch: &dyn Arch, + sections: &mut [Section], + symbols: &[Symbol], +) -> Result<()> { + for (section_index, section) in sections.iter_mut().enumerate() { + if section.kind != SectionKind::Code { + continue; + } + let mut fake_pool_relocs = Vec::new(); + for symbol in symbols { + if symbol.section != Some(section_index) { + continue; + } + if symbol.kind != SymbolKind::Function { + continue; + } + let code = + section.data_range(symbol.address, symbol.size as usize).ok_or_else(|| { + anyhow!( + "Symbol data out of bounds: {:#x}..{:#x}", + symbol.address, + symbol.address + symbol.size + ) + })?; + fake_pool_relocs.append(&mut arch.generate_pooled_relocations( + symbol.address, + code, + §ion.relocations, + symbols, + )); + } + section.relocations.append(&mut fake_pool_relocs); + section.relocations.sort_by_key(|r| r.address); + } + Ok(()) +} + fn parse_line_info( obj_file: &object::File, sections: &mut [Section], @@ -812,6 +850,9 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result { let (mut symbols, symbol_indices) = map_symbols(arch.as_ref(), &obj_file, §ions, §ion_indices, split_meta.as_ref())?; map_relocations(arch.as_ref(), &obj_file, &mut sections, §ion_indices, &symbol_indices)?; + if config.ppc_calculate_pool_relocations { + calculate_pooled_relocations(arch.as_ref(), &mut sections, &symbols)?; + } parse_line_info(&obj_file, &mut sections, §ion_indices, data)?; if config.combine_data_sections || config.combine_text_sections { combine_sections(&mut sections, &mut symbols, config)?; diff --git a/objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap b/objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap index d6cd2f4..707c931 100644 --- a/objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap +++ b/objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap @@ -1,5 +1,6 @@ --- source: objdiff-core/tests/arch_ppc.rs +assertion_line: 70 expression: sections_display --- [ @@ -46,7 +47,7 @@ expression: sections_display name: ".text", size: 3060, match_percent: Some( - 59.02353, + 58.662746, ), symbols: [ SectionDisplaySymbol { diff --git a/objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap b/objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap index fb1ac3d..bebb0ba 100644 --- a/objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap +++ b/objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap @@ -1,5 +1,6 @@ --- source: objdiff-core/tests/arch_ppc.rs +assertion_line: 20 expression: output --- [(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("srwi", 60), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)] @@ -9,7 +10,7 @@ expression: output [(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(32), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)] [(Address(20), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] [(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] -[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)] +[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)] [(Address(32), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode("extrwi", 60), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Eol, Normal, 0)] [(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] [(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] @@ -18,7 +19,7 @@ expression: output [(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(68), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)] [(Address(56), Normal, 5), (Basic(" ~> "), Rotating(2), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] [(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] -[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)] +[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)] [(Address(68), Normal, 5), (Basic(" ~> "), Rotating(3), 0), (Opcode("extrwi", 60), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("16")), Normal, 0), (Eol, Normal, 0)] [(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] [(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] @@ -28,7 +29,7 @@ expression: output [(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(108), Normal, 0), (Basic(" ~>"), Rotating(5), 0), (Eol, Normal, 0)] [(Address(96), Normal, 5), (Basic(" ~> "), Rotating(4), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] [(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] -[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)] +[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)] [(Address(108), Normal, 5), (Basic(" ~> "), Rotating(5), 0), (Opcode("clrlwi", 60), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)] [(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] [(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] @@ -38,7 +39,7 @@ expression: output [(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(148), Normal, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)] [(Address(136), Normal, 5), (Basic(" ~> "), Rotating(6), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] [(Address(140), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] -[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)] +[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)] [(Address(148), Normal, 5), (Basic(" ~> "), Rotating(7), 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] [(Address(152), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)] [(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(3)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] @@ -47,22 +48,22 @@ expression: output [(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(45)), Normal, 0), (Eol, Normal, 0)] [(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbz", 162), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] -[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)] +[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)] [(Address(184), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)] [(Address(188), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(196), Normal, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)] [(Address(192), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(196), Normal, 5), (Basic(" ~> "), Rotating(8), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] -[(Address(200), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)] +[(Address(200), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)] [(Address(204), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)] [(Address(208), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(216), Normal, 0), (Basic(" ~>"), Rotating(9), 0), (Eol, Normal, 0)] [(Address(212), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(216), Normal, 5), (Basic(" ~> "), Rotating(9), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] -[(Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)] +[(Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)] [(Address(224), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)] [(Address(228), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(236), Normal, 0), (Basic(" ~>"), Rotating(10), 0), (Eol, Normal, 0)] [(Address(232), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(236), Normal, 5), (Basic(" ~> "), Rotating(10), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] -[(Address(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)] +[(Address(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)] [(Address(244), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)] [(Address(248), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(256), Normal, 0), (Basic(" ~>"), Rotating(11), 0), (Eol, Normal, 0)] [(Address(252), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] diff --git a/objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap b/objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap index fa4c16e..9367287 100644 --- a/objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap +++ b/objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap @@ -1,5 +1,6 @@ --- source: objdiff-core/tests/arch_ppc.rs +assertion_line: 14 expression: obj --- Object { @@ -183,6 +184,14 @@ Object { target_symbol: 8, addend: 0, }, + Relocation { + flags: Elf( + 0, + ), + address: 28, + target_symbol: 8, + addend: 0, + }, Relocation { flags: Elf( 109, @@ -207,6 +216,14 @@ Object { target_symbol: 8, addend: 0, }, + Relocation { + flags: Elf( + 0, + ), + address: 64, + target_symbol: 8, + addend: 0, + }, Relocation { flags: Elf( 109, @@ -231,6 +248,14 @@ Object { target_symbol: 8, addend: 0, }, + Relocation { + flags: Elf( + 0, + ), + address: 104, + target_symbol: 8, + addend: 0, + }, Relocation { flags: Elf( 109, @@ -255,6 +280,14 @@ Object { target_symbol: 8, addend: 0, }, + Relocation { + flags: Elf( + 0, + ), + address: 144, + target_symbol: 8, + addend: 0, + }, Relocation { flags: Elf( 109, @@ -287,6 +320,38 @@ Object { target_symbol: 5, addend: 0, }, + Relocation { + flags: Elf( + 0, + ), + address: 180, + target_symbol: 9, + addend: 0, + }, + Relocation { + flags: Elf( + 0, + ), + address: 200, + target_symbol: 9, + addend: 0, + }, + Relocation { + flags: Elf( + 0, + ), + address: 220, + target_symbol: 9, + addend: 0, + }, + Relocation { + flags: Elf( + 0, + ), + address: 240, + target_symbol: 9, + addend: 0, + }, Relocation { flags: Elf( 109, diff --git a/objdiff-gui/src/views/data_diff.rs b/objdiff-gui/src/views/data_diff.rs index df6ef4f..bd0a13b 100644 --- a/objdiff-gui/src/views/data_diff.rs +++ b/objdiff-gui/src/views/data_diff.rs @@ -32,7 +32,7 @@ fn data_row_hover(obj: &Object, diffs: &[(DataDiff, Vec)]) - // TODO: Change hover text color depending on Insert/Delete/Replace kind // let color = get_color_for_diff_kind(reloc_diff.kind, appearance); - let reloc = resolve_relocation(obj, reloc); + let reloc = resolve_relocation(&obj.symbols, reloc); out.append(&mut relocation_hover(obj, reloc)); } out @@ -55,7 +55,7 @@ fn data_row_context( } prev_reloc = Some(reloc); - let reloc = resolve_relocation(obj, reloc); + let reloc = resolve_relocation(&obj.symbols, reloc); out.append(&mut relocation_context(obj, reloc, None)); } out