From 00ad0d80947a19eecfc3cef7a3ec1eeb7ed42299 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Mon, 21 Jul 2025 21:01:03 -0600 Subject: [PATCH] Support PPC64 ELFs (PS3); refactor relocation processing --- README.md | 2 +- objdiff-core/Cargo.toml | 2 +- objdiff-core/src/arch/arm.rs | 84 +++++++++------- objdiff-core/src/arch/arm64.rs | 13 +-- objdiff-core/src/arch/mips.rs | 92 ++++++++++-------- objdiff-core/src/arch/mod.rs | 42 ++++---- objdiff-core/src/arch/ppc/mod.rs | 94 ++++++++++++++---- objdiff-core/src/arch/superh/mod.rs | 13 +-- objdiff-core/src/arch/x86.rs | 43 +++++---- objdiff-core/src/obj/read.rs | 145 ++++++++++++++++++++-------- 10 files changed, 335 insertions(+), 195 deletions(-) diff --git a/README.md b/README.md index 3cee7ed..5700d76 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Supports: - ARM (GBA, DS, 3DS) - ARM64 (Switch) - MIPS (N64, PS1, PS2, PSP) -- PowerPC (GameCube, Wii, Xbox 360) +- PowerPC (GameCube, Wii, PS3, Xbox 360) - SuperH (Saturn, Dreamcast) - x86, x86_64 (PC) diff --git a/objdiff-core/Cargo.toml b/objdiff-core/Cargo.toml index 2aa62ce..4acd73b 100644 --- a/objdiff-core/Cargo.toml +++ b/objdiff-core/Cargo.toml @@ -128,7 +128,7 @@ itertools = { version = "0.14", default-features = false, features = ["use_alloc log = { version = "0.4", default-features = false, optional = true } memmap2 = { version = "0.9", optional = true } num-traits = { version = "0.2", default-features = false, optional = true } -object = { git = "https://github.com/gimli-rs/object", rev = "16ff70aa6fbd97d6bb7b92375929f4d72414c32b", default-features = false, features = ["read_core", "elf", "pe"] } +object = { git = "https://github.com/gimli-rs/object", rev = "16ff70aa6fbd97d6bb7b92375929f4d72414c32b", default-features = false, features = ["read_core", "elf", "coff"] } pbjson = { version = "0.7", default-features = false, optional = true } prost = { version = "0.13", default-features = false, features = ["prost-derive"], optional = true } regex = { version = "1.11", default-features = false, features = [], optional = true } diff --git a/objdiff-core/src/arch/arm.rs b/objdiff-core/src/arch/arm.rs index fffc9cb..8ce6901 100644 --- a/objdiff-core/src/arch/arm.rs +++ b/objdiff-core/src/arch/arm.rs @@ -11,7 +11,7 @@ use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, el use unarm::{args, arm, thumb}; use crate::{ - arch::Arch, + arch::{Arch, RelocationOverride, RelocationOverrideTarget}, diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart}, obj::{ InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation, @@ -356,47 +356,57 @@ impl Arch for ArchArm { Ok(()) } - fn implcit_addend( + fn relocation_override( &self, _file: &object::File<'_>, section: &object::Section, address: u64, - _relocation: &object::Relocation, - flags: RelocationFlags, - ) -> Result> { - let section_data = section.data()?; - let address = address as usize; - Ok(Some(match flags { - // ARM calls - RelocationFlags::Elf(elf::R_ARM_PC24) - | RelocationFlags::Elf(elf::R_ARM_XPC25) - | RelocationFlags::Elf(elf::R_ARM_CALL) => { - let data = section_data[address..address + 4].try_into()?; - let addend = self.endianness.read_i32_bytes(data); - let imm24 = addend & 0xffffff; - (imm24 << 2) << 8 >> 8 + relocation: &object::Relocation, + ) -> Result> { + match relocation.flags() { + // Handle ELF implicit relocations + object::RelocationFlags::Elf { r_type } => { + if relocation.has_implicit_addend() { + let section_data = section.data()?; + let address = address as usize; + let addend = match r_type { + // ARM calls + elf::R_ARM_PC24 | elf::R_ARM_XPC25 | elf::R_ARM_CALL => { + let data = section_data[address..address + 4].try_into()?; + let addend = self.endianness.read_i32_bytes(data); + let imm24 = addend & 0xffffff; + (imm24 << 2) << 8 >> 8 + } + + // Thumb calls + elf::R_ARM_THM_PC22 | elf::R_ARM_THM_XPC22 => { + let data = section_data[address..address + 2].try_into()?; + let high = self.endianness.read_i16_bytes(data) as i32; + let data = section_data[address + 2..address + 4].try_into()?; + let low = self.endianness.read_i16_bytes(data) as i32; + + let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff); + (imm22 << 1) << 9 >> 9 + } + + // Data + elf::R_ARM_ABS32 => { + let data = section_data[address..address + 4].try_into()?; + self.endianness.read_i32_bytes(data) + } + + flags => bail!("Unsupported ARM implicit relocation {flags:?}"), + }; + Ok(Some(RelocationOverride { + target: RelocationOverrideTarget::Keep, + addend: addend as i64, + })) + } else { + Ok(None) + } } - - // Thumb calls - RelocationFlags::Elf(elf::R_ARM_THM_PC22) - | RelocationFlags::Elf(elf::R_ARM_THM_XPC22) => { - let data = section_data[address..address + 2].try_into()?; - let high = self.endianness.read_i16_bytes(data) as i32; - let data = section_data[address + 2..address + 4].try_into()?; - let low = self.endianness.read_i16_bytes(data) as i32; - - let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff); - (imm22 << 1) << 9 >> 9 - } - - // Data - RelocationFlags::Elf(elf::R_ARM_ABS32) => { - let data = section_data[address..address + 4].try_into()?; - self.endianness.read_i32_bytes(data) - } - - flags => bail!("Unsupported ARM implicit relocation {flags:?}"), - } as i64)) + _ => Ok(None), + } } fn demangle(&self, name: &str) -> Option { diff --git a/objdiff-core/src/arch/arm64.rs b/objdiff-core/src/arch/arm64.rs index 5c4d972..40fa342 100644 --- a/objdiff-core/src/arch/arm64.rs +++ b/objdiff-core/src/arch/arm64.rs @@ -5,7 +5,7 @@ use alloc::{ }; use core::cmp::Ordering; -use anyhow::{Result, bail}; +use anyhow::Result; use object::elf; use yaxpeax_arch::{Arch as YaxpeaxArch, Decoder, Reader, U8Reader}; use yaxpeax_arm::armv8::a64::{ @@ -108,17 +108,6 @@ impl Arch for ArchArm64 { Ok(()) } - fn implcit_addend( - &self, - _file: &object::File<'_>, - _section: &object::Section, - address: u64, - _relocation: &object::Relocation, - flags: RelocationFlags, - ) -> Result> { - bail!("Unsupported ARM64 implicit relocation {:#x}:{:?}", address, flags) - } - fn demangle(&self, name: &str) -> Option { cpp_demangle::Symbol::new(name) .ok() diff --git a/objdiff-core/src/arch/mips.rs b/objdiff-core/src/arch/mips.rs index fa6404b..d0c4db3 100644 --- a/objdiff-core/src/arch/mips.rs +++ b/objdiff-core/src/arch/mips.rs @@ -14,7 +14,7 @@ use rabbitizer::{ }; use crate::{ - arch::Arch, + arch::{Arch, RelocationOverride, RelocationOverrideTarget}, diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart}, obj::{ InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags, @@ -225,53 +225,67 @@ impl Arch for ArchMips { Ok(()) } - fn implcit_addend( + fn relocation_override( &self, file: &object::File<'_>, section: &object::Section, address: u64, - reloc: &object::Relocation, - flags: RelocationFlags, - ) -> Result> { - // Check for paired R_MIPS_HI16 and R_MIPS_LO16 relocations. - if let RelocationFlags::Elf(elf::R_MIPS_HI16 | elf::R_MIPS_LO16) = flags { - if let Some(addend) = self - .paired_relocations - .get(section.index().0) - .and_then(|m| m.get(&address).copied()) - { - return Ok(Some(addend)); - } - } + relocation: &object::Relocation, + ) -> Result> { + match relocation.flags() { + // Handle ELF implicit relocations + object::RelocationFlags::Elf { r_type } => { + if relocation.has_implicit_addend() { + // Check for paired R_MIPS_HI16 and R_MIPS_LO16 relocations. + if let elf::R_MIPS_HI16 | elf::R_MIPS_LO16 = r_type { + if let Some(addend) = self + .paired_relocations + .get(section.index().0) + .and_then(|m| m.get(&address).copied()) + { + return Ok(Some(RelocationOverride { + target: RelocationOverrideTarget::Keep, + addend, + })); + } + } - let data = section.data()?; - let code = data[address as usize..address as usize + 4].try_into()?; - let addend = self.endianness.read_u32_bytes(code); - Ok(Some(match flags { - RelocationFlags::Elf(elf::R_MIPS_32) => addend as i64, - RelocationFlags::Elf(elf::R_MIPS_26) => ((addend & 0x03FFFFFF) << 2) as i64, - RelocationFlags::Elf(elf::R_MIPS_HI16) => ((addend & 0x0000FFFF) << 16) as i32 as i64, - RelocationFlags::Elf(elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16) => { - (addend & 0x0000FFFF) as i16 as i64 - } - RelocationFlags::Elf(elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL) => { - let object::RelocationTarget::Symbol(idx) = reloc.target() else { - bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol"); - }; - let sym = file.symbol_by_index(idx)?; + let data = section.data()?; + let code = self + .endianness + .read_u32_bytes(data[address as usize..address as usize + 4].try_into()?); + let addend = match r_type { + elf::R_MIPS_32 => code as i64, + elf::R_MIPS_26 => ((code & 0x03FFFFFF) << 2) as i64, + elf::R_MIPS_HI16 => ((code & 0x0000FFFF) << 16) as i32 as i64, + elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16 => { + (code & 0x0000FFFF) as i16 as i64 + } + elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL => { + let object::RelocationTarget::Symbol(idx) = relocation.target() else { + bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol"); + }; + let sym = file.symbol_by_index(idx)?; - // if the symbol we are relocating against is in a local section we need to add - // the ri_gp_value from .reginfo to the addend. - if sym.section().index().is_some() { - ((addend & 0x0000FFFF) as i16 as i64) + self.ri_gp_value as i64 + // if the symbol we are relocating against is in a local section we need to add + // the ri_gp_value from .reginfo to the addend. + if sym.section().index().is_some() { + ((code & 0x0000FFFF) as i16 as i64) + self.ri_gp_value as i64 + } else { + (code & 0x0000FFFF) as i16 as i64 + } + } + elf::R_MIPS_PC16 => 0, // PC-relative relocation + R_MIPS15_S3 => ((code & 0x001FFFC0) >> 3) as i64, + flags => bail!("Unsupported MIPS implicit relocation {flags:?}"), + }; + Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend })) } else { - (addend & 0x0000FFFF) as i16 as i64 + Ok(None) } } - RelocationFlags::Elf(elf::R_MIPS_PC16) => 0, // PC-relative relocation - RelocationFlags::Elf(R_MIPS15_S3) => ((addend & 0x001FFFC0) >> 3) as i64, - flags => bail!("Unsupported MIPS implicit relocation {flags:?}"), - })) + _ => Ok(None), + } } fn demangle(&self, name: &str) -> Option { diff --git a/objdiff-core/src/arch/mod.rs b/objdiff-core/src/arch/mod.rs index 6cd0c4f..93e0ff8 100644 --- a/objdiff-core/src/arch/mod.rs +++ b/objdiff-core/src/arch/mod.rs @@ -357,14 +357,15 @@ pub trait Arch: Send + Sync + Debug { None } - fn implcit_addend( + fn relocation_override( &self, - file: &object::File<'_>, - section: &object::Section, - address: u64, - relocation: &object::Relocation, - flags: RelocationFlags, - ) -> Result>; + _file: &object::File<'_>, + _section: &object::Section, + _address: u64, + _relocation: &object::Relocation, + ) -> Result> { + Ok(None) + } fn demangle(&self, _name: &str) -> Option { None } @@ -411,7 +412,9 @@ pub fn new_arch(object: &object::File) -> Result> { use object::Object as _; Ok(match object.architecture() { #[cfg(feature = "ppc")] - object::Architecture::PowerPc => Box::new(ppc::ArchPpc::new(object)?), + object::Architecture::PowerPc | object::Architecture::PowerPc64 => { + Box::new(ppc::ArchPpc::new(object)?) + } #[cfg(feature = "mips")] object::Architecture::Mips => Box::new(mips::ArchMips::new(object)?), #[cfg(feature = "x86")] @@ -456,16 +459,17 @@ impl Arch for ArchDummy { Ok(()) } - fn implcit_addend( - &self, - _file: &object::File<'_>, - _section: &object::Section, - _address: u64, - _relocation: &object::Relocation, - _flags: RelocationFlags, - ) -> Result> { - Ok(Some(0)) - } - fn data_reloc_size(&self, _flags: RelocationFlags) -> usize { 0 } } + +pub enum RelocationOverrideTarget { + Keep, + Skip, + Symbol(object::SymbolIndex), + Section(object::SectionIndex), +} + +pub struct RelocationOverride { + pub target: RelocationOverrideTarget, + pub addend: i64, +} diff --git a/objdiff-core/src/arch/ppc/mod.rs b/objdiff-core/src/arch/ppc/mod.rs index 83730c0..573cbab 100644 --- a/objdiff-core/src/arch/ppc/mod.rs +++ b/objdiff-core/src/arch/ppc/mod.rs @@ -12,7 +12,7 @@ use flagset::Flags; use object::{Object as _, ObjectSection as _, ObjectSymbol as _, elf, pe}; use crate::{ - arch::{Arch, DataType}, + arch::{Arch, DataType, RelocationOverride, RelocationOverrideTarget}, diff::{ DiffObjConfig, data::resolve_relocation, @@ -100,6 +100,7 @@ impl ArchPpc { | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI | pe::IMAGE_REL_PPC_REFLO) => { ins.args.iter().rposition(is_rel_abs_arg) } + RelocationFlags::Elf(elf::R_PPC64_TOC16) => ins.args.iter().rposition(is_offset_arg), _ => None, } } @@ -208,18 +209,18 @@ impl Arch for ArchPpc { Some(flow_analysis::ppc_data_flow_analysis(obj, symbol, code, relocations, self.extensions)) } - fn implcit_addend( + fn relocation_override( &self, - _file: &object::File<'_>, + file: &object::File<'_>, section: &object::Section, address: u64, - _relocation: &object::Relocation, - flags: RelocationFlags, - ) -> Result> { - match flags { + relocation: &object::Relocation, + ) -> Result> { + match relocation.flags() { // IMAGE_REL_PPC_PAIR contains the REF{HI,LO} displacement instead of a symbol index - RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI) - | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFLO) => section + object::RelocationFlags::Coff { + typ: pe::IMAGE_REL_PPC_REFHI | pe::IMAGE_REL_PPC_REFLO, + } => section .relocations() .skip_while(|&(a, _)| a < address) .take_while(|&(a, _)| a == address) @@ -228,23 +229,81 @@ impl Arch for ArchPpc { typ: pe::IMAGE_REL_PPC_PAIR }) }) - .map_or(Ok(Some(0)), |(_, reloc)| match reloc.target() { - object::RelocationTarget::Symbol(index) => { - Ok(Some(index.0 as u16 as i16 as i64)) - } + .map_or(Ok(None), |(_, reloc)| match reloc.target() { + object::RelocationTarget::Symbol(index) => Ok(Some(RelocationOverride { + target: RelocationOverrideTarget::Keep, + addend: index.0 as u16 as i16 as i64, + })), target => Err(anyhow!("Unsupported IMAGE_REL_PPC_PAIR target {target:?}")), }), // Skip PAIR relocations as they are handled by the previous case - RelocationFlags::Coff(pe::IMAGE_REL_PPC_PAIR) => Ok(None), - RelocationFlags::Coff(_) => Ok(Some(0)), - flags => Err(anyhow!("Unsupported PPC implicit relocation {flags:?}")), + object::RelocationFlags::Coff { typ: pe::IMAGE_REL_PPC_PAIR } => { + Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Skip, addend: 0 })) + } + // Any other COFF relocation has an addend of 0 + object::RelocationFlags::Coff { .. } => { + Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend: 0 })) + } + // Handle ELF implicit relocations + flags @ object::RelocationFlags::Elf { r_type } => { + ensure!( + !relocation.has_implicit_addend(), + "Unsupported implicit relocation {:?}", + flags + ); + match r_type { + elf::R_PPC64_TOC16 => { + let offset = u64::try_from(relocation.addend()) + .map_err(|_| anyhow!("Negative addend for R_PPC64_TOC16 relocation"))?; + let Some(toc_section) = file.section_by_name(".toc") else { + bail!("Missing .toc section for R_PPC64_TOC16 relocation"); + }; + // If TOC target is a relocation, replace it with the target symbol + let Some((_, toc_relocation)) = + toc_section.relocations().find(|&(a, _)| a == offset) + else { + return Ok(None); + }; + if toc_relocation.has_implicit_addend() { + log::warn!( + "Unsupported implicit addend for R_PPC64_TOC16 relocation: {toc_relocation:?}" + ); + return Ok(None); + } + let addend = toc_relocation.addend(); + match toc_relocation.target() { + object::RelocationTarget::Symbol(symbol_index) => { + Ok(Some(RelocationOverride { + target: RelocationOverrideTarget::Symbol(symbol_index), + addend, + })) + } + object::RelocationTarget::Section(section_index) => { + Ok(Some(RelocationOverride { + target: RelocationOverrideTarget::Section(section_index), + addend, + })) + } + target => { + log::warn!( + "Unsupported R_PPC64_TOC16 relocation target {target:?}" + ); + Ok(None) + } + } + } + _ => Ok(None), + } + } + _ => Ok(None), } } - fn demangle(&self, name: &str) -> Option { + fn demangle(&self, mut name: &str) -> Option { if name.starts_with('?') { msvc_demangler::demangle(name, msvc_demangler::DemangleFlags::llvm()).ok() } else { + name = name.trim_start_matches('.'); cpp_demangle::Symbol::new(name) .ok() .and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).ok()) @@ -264,6 +323,7 @@ impl Arch for ArchPpc { elf::R_PPC_UADDR32 => Some("R_PPC_UADDR32"), elf::R_PPC_REL24 => Some("R_PPC_REL24"), elf::R_PPC_REL14 => Some("R_PPC_REL14"), + elf::R_PPC64_TOC16 => Some("R_PPC64_TOC16"), _ => None, }, RelocationFlags::Coff(r_type) => match r_type { diff --git a/objdiff-core/src/arch/superh/mod.rs b/objdiff-core/src/arch/superh/mod.rs index 8c48953..39fc453 100644 --- a/objdiff-core/src/arch/superh/mod.rs +++ b/objdiff-core/src/arch/superh/mod.rs @@ -1,6 +1,6 @@ use alloc::{collections::BTreeMap, format, string::String, vec, vec::Vec}; -use anyhow::{Result, bail}; +use anyhow::Result; use object::elf; use crate::{ @@ -132,17 +132,6 @@ impl Arch for ArchSuperH { Ok(()) } - fn implcit_addend( - &self, - _file: &object::File<'_>, - _section: &object::Section, - address: u64, - _relocation: &object::Relocation, - flags: RelocationFlags, - ) -> Result> { - bail!("Unsupported SuperH implicit relocation {:#x}:{:?}", address, flags) - } - fn demangle(&self, name: &str) -> Option { cpp_demangle::Symbol::new(name) .ok() diff --git a/objdiff-core/src/arch/x86.rs b/objdiff-core/src/arch/x86.rs index 6e3de36..4188719 100644 --- a/objdiff-core/src/arch/x86.rs +++ b/objdiff-core/src/arch/x86.rs @@ -9,7 +9,7 @@ use iced_x86::{ use object::{Endian as _, Object as _, ObjectSection as _, elf, pe}; use crate::{ - arch::Arch, + arch::{Arch, RelocationOverride, RelocationOverrideTarget}, diff::{DiffObjConfig, X86Formatter, display::InstructionPart}, obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef}, }; @@ -225,40 +225,47 @@ impl Arch for ArchX86 { Ok(()) } - fn implcit_addend( + fn relocation_override( &self, _file: &object::File<'_>, section: &object::Section, address: u64, - _relocation: &object::Relocation, - flags: RelocationFlags, - ) -> Result> { - match self.arch { - Architecture::X86 => match flags { - RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) - | RelocationFlags::Elf(elf::R_386_32 | elf::R_386_PC32) => { + relocation: &object::Relocation, + ) -> Result> { + if !relocation.has_implicit_addend() { + return Ok(None); + } + let addend = match self.arch { + Architecture::X86 => match relocation.flags() { + object::RelocationFlags::Coff { + typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32, + } + | object::RelocationFlags::Elf { r_type: elf::R_386_32 | elf::R_386_PC32 } => { let data = section.data()?[address as usize..address as usize + 4].try_into()?; - Ok(Some(self.endianness.read_i32_bytes(data) as i64)) + self.endianness.read_i32_bytes(data) as i64 } flags => bail!("Unsupported x86 implicit relocation {flags:?}"), }, - Architecture::X86_64 => match flags { - RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32) - | RelocationFlags::Elf(elf::R_X86_64_32 | elf::R_X86_64_PC32) => { + Architecture::X86_64 => match relocation.flags() { + object::RelocationFlags::Coff { + typ: pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32, + } + | object::RelocationFlags::Elf { r_type: elf::R_X86_64_32 | elf::R_X86_64_PC32 } => { let data = section.data()?[address as usize..address as usize + 4].try_into()?; - Ok(Some(self.endianness.read_i32_bytes(data) as i64)) + self.endianness.read_i32_bytes(data) as i64 } - RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR64) - | RelocationFlags::Elf(elf::R_X86_64_64) => { + object::RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 } + | object::RelocationFlags::Elf { r_type: elf::R_X86_64_64 } => { let data = section.data()?[address as usize..address as usize + 8].try_into()?; - Ok(Some(self.endianness.read_i64_bytes(data))) + self.endianness.read_i64_bytes(data) } flags => bail!("Unsupported x86-64 implicit relocation {flags:?}"), }, - } + }; + Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend })) } fn demangle(&self, name: &str) -> Option { diff --git a/objdiff-core/src/obj/read.rs b/objdiff-core/src/obj/read.rs index 51b863f..1eb40be 100644 --- a/objdiff-core/src/obj/read.rs +++ b/objdiff-core/src/obj/read.rs @@ -11,7 +11,7 @@ use anyhow::{Context, Result, anyhow, bail, ensure}; use object::{Object as _, ObjectSection as _, ObjectSymbol as _}; use crate::{ - arch::{Arch, new_arch}, + arch::{Arch, RelocationOverride, RelocationOverrideTarget, new_arch}, diff::DiffObjConfig, obj::{ FlowAnalysisResult, Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag, @@ -24,8 +24,13 @@ use crate::{ fn map_section_kind(section: &object::Section) -> SectionKind { match section.kind() { object::SectionKind::Text => SectionKind::Code, - object::SectionKind::Data | object::SectionKind::ReadOnlyData => SectionKind::Data, - object::SectionKind::UninitializedData => SectionKind::Bss, + object::SectionKind::Data + | object::SectionKind::ReadOnlyData + | object::SectionKind::ReadOnlyString + | object::SectionKind::Tls => SectionKind::Data, + object::SectionKind::UninitializedData + | object::SectionKind::UninitializedTls + | object::SectionKind::Common => SectionKind::Bss, _ => SectionKind::Unknown, } } @@ -329,63 +334,125 @@ fn map_section_relocations( ) -> Result> { let mut relocations = Vec::::with_capacity(obj_section.relocations().count()); for (address, reloc) in obj_section.relocations() { - let flags = match reloc.flags() { - object::RelocationFlags::Elf { r_type } => RelocationFlags::Elf(r_type), - object::RelocationFlags::Coff { typ } => RelocationFlags::Coff(typ), - flags => { - bail!("Unhandled relocation flags: {:?}", flags); - } + let mut target_reloc = RelocationOverride { + target: match reloc.target() { + object::RelocationTarget::Symbol(symbol) => { + RelocationOverrideTarget::Symbol(symbol) + } + object::RelocationTarget::Section(section) => { + RelocationOverrideTarget::Section(section) + } + _ => RelocationOverrideTarget::Skip, + }, + addend: reloc.addend(), }; - // TODO validate reloc here? - let mut addend = if reloc.has_implicit_addend() { - match arch.implcit_addend(obj_file, obj_section, address, &reloc, flags)? { - Some(addend) => addend, - None => continue, // Skip relocation (e.g. COFF PAIR relocations) + + // Allow the architecture to override the relocation target and addend + match arch.relocation_override(obj_file, obj_section, address, &reloc)? { + Some(reloc_override) => { + match reloc_override.target { + RelocationOverrideTarget::Keep => {} + target => { + target_reloc.target = target; + } + } + target_reloc.addend = reloc_override.addend; } - } else { - reloc.addend() - }; - let target_symbol = match reloc.target() { - object::RelocationTarget::Symbol(idx) => { - if idx.0 == u32::MAX as usize { - // ??? + None => { + ensure!( + !reloc.has_implicit_addend(), + "Unsupported implicit relocation {:?}", + reloc.flags() + ); + } + } + + // Resolve the relocation target symbol + let (symbol_index, addend) = match target_reloc.target { + RelocationOverrideTarget::Keep => unreachable!(), + RelocationOverrideTarget::Skip => continue, + RelocationOverrideTarget::Symbol(symbol_index) => { + // Sometimes used to indicate "absolute" + if symbol_index.0 == u32::MAX as usize { continue; } + // If the target is a section symbol, try to resolve a better symbol as the target - let idx = if let Some(section_symbol) = obj_file - .symbol_by_index(idx) + if let Some(section_symbol) = obj_file + .symbol_by_index(symbol_index) .ok() .filter(|s| s.kind() == object::SymbolKind::Section) { let section_index = section_symbol.section_index().context("Section symbol without section")?; - let target_address = section_symbol.address().wrapping_add_signed(addend); + let target_address = + section_symbol.address().wrapping_add_signed(target_reloc.addend); if let Some((new_idx, addr)) = ordered_symbols .get(section_index.0) .and_then(|symbols| best_symbol(symbols, target_address)) { - addend = target_address.wrapping_sub(addr) as i64; - new_idx + (new_idx, target_address.wrapping_sub(addr) as i64) } else { - idx + (symbol_index, target_reloc.addend) } } else { - idx - }; - match symbol_indices.get(idx.0).copied() { - Some(i) => i, - None => { - log::warn!("Invalid symbol index {}", idx.0); - continue; - } + (symbol_index, target_reloc.addend) } } - object::RelocationTarget::Absolute => { - let section_name = obj_section.name()?; - log::warn!("Ignoring absolute relocation @ {section_name}:{address:#x}"); + RelocationOverrideTarget::Section(section_index) => { + let section = match obj_file.section_by_index(section_index) { + Ok(section) => section, + Err(e) => { + log::warn!("Invalid relocation section: {e}"); + continue; + } + }; + let Ok(target_address) = u64::try_from(target_reloc.addend) else { + log::warn!( + "Negative section relocation addend: {}{}", + section.name()?, + target_reloc.addend + ); + continue; + }; + let Some(symbols) = ordered_symbols.get(section_index.0) else { + log::warn!( + "Couldn't resolve relocation target symbol for section {} (no symbols)", + section.name()? + ); + continue; + }; + // Attempt to resolve a target symbol for the relocation + if let Some((new_idx, addr)) = best_symbol(symbols, target_address) { + (new_idx, target_address.wrapping_sub(addr) as i64) + } else if let Some(section_symbol) = + symbols.iter().find(|s| s.kind() == object::SymbolKind::Section) + { + ( + section_symbol.index(), + target_address.wrapping_sub(section_symbol.address()) as i64, + ) + } else { + log::warn!( + "Couldn't resolve relocation target symbol for section {}", + section.name()? + ); + continue; + } + } + }; + + let flags = match reloc.flags() { + object::RelocationFlags::Elf { r_type } => RelocationFlags::Elf(r_type), + object::RelocationFlags::Coff { typ } => RelocationFlags::Coff(typ), + flags => bail!("Unhandled relocation flags: {:?}", flags), + }; + let target_symbol = match symbol_indices.get(symbol_index.0).copied() { + Some(i) => i, + None => { + log::warn!("Invalid symbol index {}", symbol_index.0); continue; } - _ => bail!("Unhandled relocation target: {:?}", reloc.target()), }; relocations.push(Relocation { address, flags, target_symbol, addend }); }