use std::{borrow::Cow, collections::BTreeMap}; use anyhow::{bail, Result}; use object::{elf, File, Relocation, RelocationFlags}; use ppc750cl::{disasm_iter, Argument, SimplifiedIns, GPR}; use crate::{ arch::ObjArch, diff::{DiffObjConfig, ProcessCodeResult}, obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection}, }; // Relative relocation, can be Simm, Offset or BranchDest fn is_relative_arg(arg: &Argument) -> bool { matches!(arg, Argument::Simm(_) | Argument::Offset(_) | Argument::BranchDest(_)) } // Relative or absolute relocation, can be Uimm, Simm or Offset fn is_rel_abs_arg(arg: &Argument) -> bool { matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_)) } fn is_offset_arg(arg: &Argument) -> bool { matches!(arg, Argument::Offset(_)) } pub struct ObjArchPpc {} impl ObjArchPpc { pub fn new(_file: &File) -> Result { Ok(Self {}) } } impl ObjArch for ObjArchPpc { fn process_code( &self, config: &DiffObjConfig, data: &[u8], address: u64, relocs: &[ObjReloc], line_info: &Option>, ) -> Result { let ins_count = data.len() / 4; let mut ops = Vec::::with_capacity(ins_count); let mut insts = Vec::::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.flags { RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 } => ins.code & !0x1FFFFF, RelocationFlags::Elf { r_type: elf::R_PPC_REL24 } => ins.code & !0x3FFFFFC, RelocationFlags::Elf { r_type: elf::R_PPC_REL14 } => ins.code & !0xFFFC, RelocationFlags::Elf { r_type: elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO, } => ins.code & !0xFFFF, _ => ins.code, }; } let simplified = ins.clone().simplified(); let mut reloc_arg = None; if let Some(reloc) = reloc { match reloc.flags { RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 } => { reloc_arg = Some(1); } RelocationFlags::Elf { r_type: elf::R_PPC_REL24 | elf::R_PPC_REL14 } => { reloc_arg = simplified.args.iter().rposition(is_relative_arg); } RelocationFlags::Elf { r_type: elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO, } => { reloc_arg = simplified.args.iter().rposition(is_rel_abs_arg); } _ => {} } } let mut args = vec![]; let mut branch_dest = None; let mut writing_offset = false; for (idx, arg) in simplified.args.iter().enumerate() { if idx > 0 && !writing_offset { if config.space_between_args { args.push(ObjInsArg::PlainText(", ".to_string())); } else { args.push(ObjInsArg::PlainText(",".to_string())); } } if reloc_arg == Some(idx) { let reloc = reloc.unwrap(); push_reloc(&mut args, reloc)?; // For @sda21, we can omit the register argument if matches!(reloc.flags, RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 }) // Sanity check: the next argument should be r0 && matches!(simplified.args.get(idx + 1), Some(Argument::GPR(GPR(0)))) { break; } } else { match arg { Argument::Simm(simm) => { args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(simm.0 as i64))); } Argument::Uimm(uimm) => { args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(uimm.0 as u64))); } Argument::Offset(offset) => { args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(offset.0 as i64))); } Argument::BranchDest(dest) => { let dest = ins.addr.wrapping_add_signed(dest.0) as u64; args.push(ObjInsArg::BranchDest(dest)); branch_dest = Some(dest); } _ => { args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string()))); } }; } if writing_offset { args.push(ObjInsArg::PlainText(")".to_string())); writing_offset = false; } if is_offset_arg(arg) { args.push(ObjInsArg::PlainText("(".to_string())); writing_offset = true; } } ops.push(simplified.ins.op as u16); let line = line_info .as_ref() .and_then(|map| map.range(..=simplified.ins.addr as u64).last().map(|(_, &b)| b)); insts.push(ObjIns { address: simplified.ins.addr as u64, size: 4, mnemonic: format!("{}{}", simplified.mnemonic, simplified.suffix), args, reloc: reloc.cloned(), op: ins.op as u16, branch_dest, line, orig: Some(format!("{}", SimplifiedIns::basic_form(ins))), }); } Ok(ProcessCodeResult { ops, insts }) } fn implcit_addend( &self, _section: &ObjSection, address: u64, reloc: &Relocation, ) -> Result { bail!("Unsupported PPC implicit relocation {:#x}:{:?}", address, reloc.flags()) } fn demangle(&self, name: &str) -> Option { cwdemangle::demangle(name, &Default::default()) } fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> { match flags { RelocationFlags::Elf { r_type } => match r_type { elf::R_PPC_ADDR16_LO => Cow::Borrowed("R_PPC_ADDR16_LO"), elf::R_PPC_ADDR16_HI => Cow::Borrowed("R_PPC_ADDR16_HI"), elf::R_PPC_ADDR16_HA => Cow::Borrowed("R_PPC_ADDR16_HA"), elf::R_PPC_EMB_SDA21 => Cow::Borrowed("R_PPC_EMB_SDA21"), elf::R_PPC_ADDR32 => Cow::Borrowed("R_PPC_ADDR32"), elf::R_PPC_UADDR32 => Cow::Borrowed("R_PPC_UADDR32"), elf::R_PPC_REL24 => Cow::Borrowed("R_PPC_REL24"), elf::R_PPC_REL14 => Cow::Borrowed("R_PPC_REL14"), _ => Cow::Owned(format!("")), }, flags => Cow::Owned(format!("<{flags:?}>")), } } } fn push_reloc(args: &mut Vec, reloc: &ObjReloc) -> Result<()> { match reloc.flags { RelocationFlags::Elf { r_type } => match r_type { elf::R_PPC_ADDR16_LO => { args.push(ObjInsArg::Reloc); args.push(ObjInsArg::PlainText("@l".to_string())); } elf::R_PPC_ADDR16_HI => { args.push(ObjInsArg::Reloc); args.push(ObjInsArg::PlainText("@h".to_string())); } elf::R_PPC_ADDR16_HA => { args.push(ObjInsArg::Reloc); args.push(ObjInsArg::PlainText("@ha".to_string())); } elf::R_PPC_EMB_SDA21 => { args.push(ObjInsArg::Reloc); args.push(ObjInsArg::PlainText("@sda21".to_string())); } elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 | elf::R_PPC_REL24 | elf::R_PPC_REL14 => { args.push(ObjInsArg::Reloc); } _ => bail!("Unsupported ELF PPC relocation type {r_type}"), }, flags => bail!("Unsupported PPC relocation kind: {flags:?}"), }; Ok(()) }