Skip to content

Commit 48804dc

Browse files
authored
Merge pull request #245 from LagoLunatic/subi
PPC pooled data references: Add support for `subi`, `addis`, and `subis` instructions
2 parents 8cfa8b7 + 93a4d7e commit 48804dc

File tree

1 file changed

+52
-14
lines changed
  • objdiff-core/src/arch/ppc

1 file changed

+52
-14
lines changed

objdiff-core/src/arch/ppc/mod.rs

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -675,24 +675,28 @@ fn make_symbol_ref(symbol: &object::Symbol) -> Result<ExtabSymbolRef> {
675675
#[derive(Debug)]
676676
struct PoolReference {
677677
addr_src_gpr: powerpc::GPR,
678-
addr_offset: i16,
678+
addr_offset: i64,
679679
addr_dst_gpr: Option<powerpc::GPR>,
680680
}
681681

682682
// Given an instruction, check if it could be accessing pooled data at the address in a register.
683683
// If so, return information pertaining to where the instruction is getting that address from and
684684
// what it's doing with the address (e.g. copying it into another register, adding an offset, etc).
685685
fn get_pool_reference_for_inst(
686-
opcode: powerpc::Opcode,
686+
ins: powerpc::Ins,
687687
simplified: &powerpc::ParsedIns,
688688
) -> Option<PoolReference> {
689689
use powerpc::{Argument, Opcode};
690690
let args = &simplified.args;
691-
if flow_analysis::guess_data_type_from_load_store_inst_op(opcode).is_some() {
691+
if flow_analysis::guess_data_type_from_load_store_inst_op(ins.op).is_some() {
692692
match (args[1], args[2]) {
693693
(Argument::Offset(offset), Argument::GPR(addr_src_gpr)) => {
694694
// e.g. lwz. Immediate offset.
695-
Some(PoolReference { addr_src_gpr, addr_offset: offset.0, addr_dst_gpr: None })
695+
Some(PoolReference {
696+
addr_src_gpr,
697+
addr_offset: offset.0 as i64,
698+
addr_dst_gpr: None,
699+
})
696700
}
697701
(Argument::GPR(addr_src_gpr), Argument::GPR(_offset_gpr)) => {
698702
// e.g. lwzx. The offset is in a register and was likely calculated from an index.
@@ -712,17 +716,51 @@ fn get_pool_reference_for_inst(
712716
// If either of these match, we also want to return the destination register that the
713717
// address is being copied into so that we can detect any future references to that new
714718
// register as well.
715-
match (opcode, args[0], args[1], args[2]) {
719+
match (ins.op, args[0], args[1], args[2]) {
716720
(
721+
// `addi` or `subi`
717722
Opcode::Addi,
718723
Argument::GPR(addr_dst_gpr),
719724
Argument::GPR(addr_src_gpr),
720725
Argument::Simm(simm),
721-
) => Some(PoolReference {
722-
addr_src_gpr,
723-
addr_offset: simm.0,
724-
addr_dst_gpr: Some(addr_dst_gpr),
725-
}),
726+
) => {
727+
let offset = if simplified.mnemonic == "addi" { simm.0 } else { -simm.0 };
728+
Some(PoolReference {
729+
addr_src_gpr,
730+
addr_offset: offset as i64,
731+
addr_dst_gpr: Some(addr_dst_gpr),
732+
})
733+
}
734+
(
735+
// `addis`
736+
Opcode::Addis,
737+
Argument::GPR(addr_dst_gpr),
738+
Argument::GPR(addr_src_gpr),
739+
Argument::Uimm(uimm), // Note: `addis` uses UIMM, unlike `addi`, `subi`, and `subis`
740+
) => {
741+
assert_eq!(simplified.mnemonic, "addis");
742+
let offset = (uimm.0 as i64) << 16;
743+
Some(PoolReference {
744+
addr_src_gpr,
745+
addr_offset: offset,
746+
addr_dst_gpr: Some(addr_dst_gpr),
747+
})
748+
}
749+
(
750+
// `subis`
751+
Opcode::Addis,
752+
Argument::GPR(addr_dst_gpr),
753+
Argument::GPR(addr_src_gpr),
754+
Argument::Simm(simm),
755+
) => {
756+
assert_eq!(simplified.mnemonic, "subis");
757+
let offset = (simm.0 as i64) << 16;
758+
Some(PoolReference {
759+
addr_src_gpr,
760+
addr_offset: offset,
761+
addr_dst_gpr: Some(addr_dst_gpr),
762+
})
763+
}
726764
(
727765
// `mr` or `mr.`
728766
Opcode::Or,
@@ -777,13 +815,13 @@ fn clear_overwritten_gprs(ins: powerpc::Ins, gpr_pool_relocs: &mut BTreeMap<u8,
777815
// Also, if this instruction is accessing the middle of a symbol instead of the start, we add an
778816
// addend to indicate that.
779817
fn make_fake_pool_reloc(
780-
offset: i16,
818+
offset: i64,
781819
cur_addr: u32,
782820
pool_reloc: &Relocation,
783821
symbols: &[Symbol],
784822
) -> Option<Relocation> {
785823
let pool_reloc = resolve_relocation(symbols, pool_reloc);
786-
let offset_from_pool = pool_reloc.relocation.addend + offset as i64;
824+
let offset_from_pool = pool_reloc.relocation.addend + offset;
787825
let target_address = pool_reloc.symbol.address.checked_add_signed(offset_from_pool)?;
788826
let target_symbol;
789827
let addend;
@@ -946,7 +984,7 @@ fn generate_fake_pool_relocations_for_function(
946984
clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
947985
}
948986
}
949-
} else if let Some(pool_ref) = get_pool_reference_for_inst(ins.op, &simplified) {
987+
} else if let Some(pool_ref) = get_pool_reference_for_inst(ins, &simplified) {
950988
// This instruction doesn't have a real relocation, so it may be a reference to one of
951989
// the already-loaded pools.
952990
if let Some(pool_reloc) = gpr_pool_relocs.get(&pool_ref.addr_src_gpr.0) {
@@ -965,7 +1003,7 @@ fn generate_fake_pool_relocations_for_function(
9651003
// with the offset within the .data section of an array variable into r21.
9661004
// Then the body of the loop will `lwzx` one of the array elements from r21.
9671005
let mut new_reloc = pool_reloc.clone();
968-
new_reloc.addend += pool_ref.addr_offset as i64;
1006+
new_reloc.addend += pool_ref.addr_offset;
9691007
gpr_pool_relocs.insert(addr_dst_gpr.0, new_reloc);
9701008
} else {
9711009
clear_overwritten_gprs(ins, &mut gpr_pool_relocs);

0 commit comments

Comments
 (0)