mirror of
https://github.com/encounter/objdiff.git
synced 2025-07-03 19:56:03 +00:00
No more scattered relocation handling and feature checks. Everything will go through the ObjArch trait, which makes it easier to add new architectures going forward.
208 lines
8.3 KiB
Rust
208 lines
8.3 KiB
Rust
use std::{borrow::Cow, collections::BTreeMap};
|
|
|
|
use anyhow::{bail, Result};
|
|
use object::{elf, Endian, Endianness, File, Object, Relocation, RelocationFlags};
|
|
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
|
|
|
use crate::{
|
|
arch::ObjArch,
|
|
diff::{DiffObjConfig, ProcessCodeResult},
|
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
|
};
|
|
|
|
fn configure_rabbitizer() {
|
|
unsafe {
|
|
config::RabbitizerConfig_Cfg.reg_names.fpr_abi_names = Abi::O32;
|
|
}
|
|
}
|
|
|
|
pub struct ObjArchMips {
|
|
pub endianness: Endianness,
|
|
}
|
|
|
|
impl ObjArchMips {
|
|
pub fn new(object: &File) -> Result<Self> { Ok(Self { endianness: object.endianness() }) }
|
|
}
|
|
|
|
impl ObjArch for ObjArchMips {
|
|
fn process_code(
|
|
&self,
|
|
config: &DiffObjConfig,
|
|
data: &[u8],
|
|
start_address: u64,
|
|
relocs: &[ObjReloc],
|
|
line_info: &Option<BTreeMap<u64, u64>>,
|
|
) -> Result<ProcessCodeResult> {
|
|
configure_rabbitizer();
|
|
|
|
let end_address = start_address + data.len() as u64;
|
|
let ins_count = data.len() / 4;
|
|
let mut ops = Vec::<u16>::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 = self.endianness.read_u32_bytes(chunk.try_into()?);
|
|
let instruction = Instruction::new(code, cur_addr, InstrCategory::CPU);
|
|
|
|
let op = instruction.unique_id as u16;
|
|
ops.push(op);
|
|
|
|
let mnemonic = instruction.opcode_name().to_string();
|
|
let is_branch = instruction.is_branch();
|
|
let branch_offset = instruction.branch_offset();
|
|
let branch_dest = if is_branch {
|
|
cur_addr.checked_add_signed(branch_offset).map(|a| a as u64)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let operands = instruction.get_operands_slice();
|
|
let mut args = Vec::with_capacity(operands.len() + 1);
|
|
for (idx, op) in operands.iter().enumerate() {
|
|
if idx > 0 {
|
|
if config.space_between_args {
|
|
args.push(ObjInsArg::PlainText(", ".to_string()));
|
|
} else {
|
|
args.push(ObjInsArg::PlainText(",".to_string()));
|
|
}
|
|
}
|
|
|
|
match op {
|
|
OperandType::cpu_immediate
|
|
| OperandType::cpu_label
|
|
| OperandType::cpu_branch_target_label => {
|
|
if let Some(branch_dest) = branch_dest {
|
|
args.push(ObjInsArg::BranchDest(branch_dest));
|
|
} 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
|
|
{
|
|
args.push(ObjInsArg::BranchDest(reloc.target.address));
|
|
} else {
|
|
push_reloc(&mut args, reloc)?;
|
|
}
|
|
} else {
|
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
|
op.disassemble(&instruction, None),
|
|
)));
|
|
}
|
|
}
|
|
OperandType::cpu_immediate_base => {
|
|
if let Some(reloc) = reloc {
|
|
push_reloc(&mut args, reloc)?;
|
|
} else {
|
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
|
OperandType::cpu_immediate.disassemble(&instruction, None),
|
|
)));
|
|
}
|
|
args.push(ObjInsArg::PlainText("(".to_string()));
|
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
|
OperandType::cpu_rs.disassemble(&instruction, None),
|
|
)));
|
|
args.push(ObjInsArg::PlainText(")".to_string()));
|
|
}
|
|
_ => {
|
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
|
op.disassemble(&instruction, None),
|
|
)));
|
|
}
|
|
}
|
|
}
|
|
let line = line_info
|
|
.as_ref()
|
|
.and_then(|map| map.range(..=cur_addr as u64).last().map(|(_, &b)| b));
|
|
insts.push(ObjIns {
|
|
address: cur_addr as u64,
|
|
size: 4,
|
|
op,
|
|
mnemonic,
|
|
args,
|
|
reloc: reloc.cloned(),
|
|
branch_dest,
|
|
line,
|
|
orig: None,
|
|
});
|
|
cur_addr += 4;
|
|
}
|
|
Ok(ProcessCodeResult { ops, insts })
|
|
}
|
|
|
|
fn implcit_addend(
|
|
&self,
|
|
section: &ObjSection,
|
|
address: u64,
|
|
reloc: &Relocation,
|
|
) -> Result<i64> {
|
|
let data = section.data[address as usize..address as usize + 4].try_into()?;
|
|
let addend = self.endianness.read_u32_bytes(data);
|
|
Ok(match reloc.flags() {
|
|
RelocationFlags::Elf { r_type: elf::R_MIPS_32 } => addend as i64,
|
|
RelocationFlags::Elf { r_type: elf::R_MIPS_HI16 } => {
|
|
((addend & 0x0000FFFF) << 16) as i32 as i64
|
|
}
|
|
RelocationFlags::Elf {
|
|
r_type:
|
|
elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16 | elf::R_MIPS_GPREL16,
|
|
} => (addend & 0x0000FFFF) as i16 as i64,
|
|
RelocationFlags::Elf { r_type: elf::R_MIPS_26 } => ((addend & 0x03FFFFFF) << 2) as i64,
|
|
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
|
})
|
|
}
|
|
|
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
|
match flags {
|
|
RelocationFlags::Elf { r_type } => match r_type {
|
|
elf::R_MIPS_HI16 => Cow::Borrowed("R_MIPS_HI16"),
|
|
elf::R_MIPS_LO16 => Cow::Borrowed("R_MIPS_LO16"),
|
|
elf::R_MIPS_GOT16 => Cow::Borrowed("R_MIPS_GOT16"),
|
|
elf::R_MIPS_CALL16 => Cow::Borrowed("R_MIPS_CALL16"),
|
|
elf::R_MIPS_GPREL16 => Cow::Borrowed("R_MIPS_GPREL16"),
|
|
elf::R_MIPS_32 => Cow::Borrowed("R_MIPS_32"),
|
|
elf::R_MIPS_26 => Cow::Borrowed("R_MIPS_26"),
|
|
_ => Cow::Owned(format!("<Elf {r_type:?}>")),
|
|
},
|
|
flags => Cow::Owned(format!("<{flags:?}>")),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn push_reloc(args: &mut Vec<ObjInsArg>, reloc: &ObjReloc) -> Result<()> {
|
|
match reloc.flags {
|
|
RelocationFlags::Elf { r_type } => match r_type {
|
|
elf::R_MIPS_HI16 => {
|
|
args.push(ObjInsArg::PlainText("%hi(".to_string()));
|
|
args.push(ObjInsArg::Reloc);
|
|
args.push(ObjInsArg::PlainText(")".to_string()));
|
|
}
|
|
elf::R_MIPS_LO16 => {
|
|
args.push(ObjInsArg::PlainText("%lo(".to_string()));
|
|
args.push(ObjInsArg::Reloc);
|
|
args.push(ObjInsArg::PlainText(")".to_string()));
|
|
}
|
|
elf::R_MIPS_GOT16 => {
|
|
args.push(ObjInsArg::PlainText("%got(".to_string()));
|
|
args.push(ObjInsArg::Reloc);
|
|
args.push(ObjInsArg::PlainText(")".to_string()));
|
|
}
|
|
elf::R_MIPS_CALL16 => {
|
|
args.push(ObjInsArg::PlainText("%call16(".to_string()));
|
|
args.push(ObjInsArg::Reloc);
|
|
args.push(ObjInsArg::PlainText(")".to_string()));
|
|
}
|
|
elf::R_MIPS_GPREL16 => {
|
|
args.push(ObjInsArg::PlainText("%gp_rel(".to_string()));
|
|
args.push(ObjInsArg::Reloc);
|
|
args.push(ObjInsArg::PlainText(")".to_string()));
|
|
}
|
|
elf::R_MIPS_32 | elf::R_MIPS_26 => {
|
|
args.push(ObjInsArg::Reloc);
|
|
}
|
|
_ => bail!("Unsupported ELF MIPS relocation type {r_type}"),
|
|
},
|
|
flags => panic!("Unsupported MIPS relocation flags {flags:?}"),
|
|
}
|
|
Ok(())
|
|
}
|