Support PPC64 ELFs (PS3); refactor relocation processing

This commit is contained in:
Luke Street 2025-07-21 21:01:03 -06:00
parent 8fac63c42c
commit 00ad0d8094
10 changed files with 335 additions and 195 deletions

View File

@ -19,7 +19,7 @@ Supports:
- ARM (GBA, DS, 3DS) - ARM (GBA, DS, 3DS)
- ARM64 (Switch) - ARM64 (Switch)
- MIPS (N64, PS1, PS2, PSP) - MIPS (N64, PS1, PS2, PSP)
- PowerPC (GameCube, Wii, Xbox 360) - PowerPC (GameCube, Wii, PS3, Xbox 360)
- SuperH (Saturn, Dreamcast) - SuperH (Saturn, Dreamcast)
- x86, x86_64 (PC) - x86, x86_64 (PC)

View File

@ -128,7 +128,7 @@ itertools = { version = "0.14", default-features = false, features = ["use_alloc
log = { version = "0.4", default-features = false, optional = true } log = { version = "0.4", default-features = false, optional = true }
memmap2 = { version = "0.9", optional = true } memmap2 = { version = "0.9", optional = true }
num-traits = { version = "0.2", default-features = false, 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 } pbjson = { version = "0.7", default-features = false, optional = true }
prost = { version = "0.13", default-features = false, features = ["prost-derive"], optional = true } prost = { version = "0.13", default-features = false, features = ["prost-derive"], optional = true }
regex = { version = "1.11", default-features = false, features = [], optional = true } regex = { version = "1.11", default-features = false, features = [], optional = true }

View File

@ -11,7 +11,7 @@ use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, el
use unarm::{args, arm, thumb}; use unarm::{args, arm, thumb};
use crate::{ use crate::{
arch::Arch, arch::{Arch, RelocationOverride, RelocationOverrideTarget},
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart}, diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart},
obj::{ obj::{
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation, InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
@ -356,47 +356,57 @@ impl Arch for ArchArm {
Ok(()) Ok(())
} }
fn implcit_addend( fn relocation_override(
&self, &self,
_file: &object::File<'_>, _file: &object::File<'_>,
section: &object::Section, section: &object::Section,
address: u64, address: u64,
_relocation: &object::Relocation, relocation: &object::Relocation,
flags: RelocationFlags, ) -> Result<Option<RelocationOverride>> {
) -> Result<Option<i64>> { match relocation.flags() {
let section_data = section.data()?; // Handle ELF implicit relocations
let address = address as usize; object::RelocationFlags::Elf { r_type } => {
Ok(Some(match flags { if relocation.has_implicit_addend() {
// ARM calls let section_data = section.data()?;
RelocationFlags::Elf(elf::R_ARM_PC24) let address = address as usize;
| RelocationFlags::Elf(elf::R_ARM_XPC25) let addend = match r_type {
| RelocationFlags::Elf(elf::R_ARM_CALL) => { // ARM calls
let data = section_data[address..address + 4].try_into()?; elf::R_ARM_PC24 | elf::R_ARM_XPC25 | elf::R_ARM_CALL => {
let addend = self.endianness.read_i32_bytes(data); let data = section_data[address..address + 4].try_into()?;
let imm24 = addend & 0xffffff; let addend = self.endianness.read_i32_bytes(data);
(imm24 << 2) << 8 >> 8 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)
}
} }
_ => 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))
} }
fn demangle(&self, name: &str) -> Option<String> { fn demangle(&self, name: &str) -> Option<String> {

View File

@ -5,7 +5,7 @@ use alloc::{
}; };
use core::cmp::Ordering; use core::cmp::Ordering;
use anyhow::{Result, bail}; use anyhow::Result;
use object::elf; use object::elf;
use yaxpeax_arch::{Arch as YaxpeaxArch, Decoder, Reader, U8Reader}; use yaxpeax_arch::{Arch as YaxpeaxArch, Decoder, Reader, U8Reader};
use yaxpeax_arm::armv8::a64::{ use yaxpeax_arm::armv8::a64::{
@ -108,17 +108,6 @@ impl Arch for ArchArm64 {
Ok(()) Ok(())
} }
fn implcit_addend(
&self,
_file: &object::File<'_>,
_section: &object::Section,
address: u64,
_relocation: &object::Relocation,
flags: RelocationFlags,
) -> Result<Option<i64>> {
bail!("Unsupported ARM64 implicit relocation {:#x}:{:?}", address, flags)
}
fn demangle(&self, name: &str) -> Option<String> { fn demangle(&self, name: &str) -> Option<String> {
cpp_demangle::Symbol::new(name) cpp_demangle::Symbol::new(name)
.ok() .ok()

View File

@ -14,7 +14,7 @@ use rabbitizer::{
}; };
use crate::{ use crate::{
arch::Arch, arch::{Arch, RelocationOverride, RelocationOverrideTarget},
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart}, diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart},
obj::{ obj::{
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags, InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
@ -225,53 +225,67 @@ impl Arch for ArchMips {
Ok(()) Ok(())
} }
fn implcit_addend( fn relocation_override(
&self, &self,
file: &object::File<'_>, file: &object::File<'_>,
section: &object::Section, section: &object::Section,
address: u64, address: u64,
reloc: &object::Relocation, relocation: &object::Relocation,
flags: RelocationFlags, ) -> Result<Option<RelocationOverride>> {
) -> Result<Option<i64>> { match relocation.flags() {
// Check for paired R_MIPS_HI16 and R_MIPS_LO16 relocations. // Handle ELF implicit relocations
if let RelocationFlags::Elf(elf::R_MIPS_HI16 | elf::R_MIPS_LO16) = flags { object::RelocationFlags::Elf { r_type } => {
if let Some(addend) = self if relocation.has_implicit_addend() {
.paired_relocations // Check for paired R_MIPS_HI16 and R_MIPS_LO16 relocations.
.get(section.index().0) if let elf::R_MIPS_HI16 | elf::R_MIPS_LO16 = r_type {
.and_then(|m| m.get(&address).copied()) if let Some(addend) = self
{ .paired_relocations
return Ok(Some(addend)); .get(section.index().0)
} .and_then(|m| m.get(&address).copied())
} {
return Ok(Some(RelocationOverride {
target: RelocationOverrideTarget::Keep,
addend,
}));
}
}
let data = section.data()?; let data = section.data()?;
let code = data[address as usize..address as usize + 4].try_into()?; let code = self
let addend = self.endianness.read_u32_bytes(code); .endianness
Ok(Some(match flags { .read_u32_bytes(data[address as usize..address as usize + 4].try_into()?);
RelocationFlags::Elf(elf::R_MIPS_32) => addend as i64, let addend = match r_type {
RelocationFlags::Elf(elf::R_MIPS_26) => ((addend & 0x03FFFFFF) << 2) as i64, elf::R_MIPS_32 => code as i64,
RelocationFlags::Elf(elf::R_MIPS_HI16) => ((addend & 0x0000FFFF) << 16) as i32 as i64, elf::R_MIPS_26 => ((code & 0x03FFFFFF) << 2) as i64,
RelocationFlags::Elf(elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16) => { elf::R_MIPS_HI16 => ((code & 0x0000FFFF) << 16) as i32 as i64,
(addend & 0x0000FFFF) as i16 as i64 elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16 => {
} (code & 0x0000FFFF) as i16 as i64
RelocationFlags::Elf(elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL) => { }
let object::RelocationTarget::Symbol(idx) = reloc.target() else { elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL => {
bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol"); 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)?; };
let sym = file.symbol_by_index(idx)?;
// if the symbol we are relocating against is in a local section we need to add // 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. // the ri_gp_value from .reginfo to the addend.
if sym.section().index().is_some() { if sym.section().index().is_some() {
((addend & 0x0000FFFF) as i16 as i64) + self.ri_gp_value as i64 ((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 { } else {
(addend & 0x0000FFFF) as i16 as i64 Ok(None)
} }
} }
RelocationFlags::Elf(elf::R_MIPS_PC16) => 0, // PC-relative relocation _ => Ok(None),
RelocationFlags::Elf(R_MIPS15_S3) => ((addend & 0x001FFFC0) >> 3) as i64, }
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
}))
} }
fn demangle(&self, name: &str) -> Option<String> { fn demangle(&self, name: &str) -> Option<String> {

View File

@ -357,14 +357,15 @@ pub trait Arch: Send + Sync + Debug {
None None
} }
fn implcit_addend( fn relocation_override(
&self, &self,
file: &object::File<'_>, _file: &object::File<'_>,
section: &object::Section, _section: &object::Section,
address: u64, _address: u64,
relocation: &object::Relocation, _relocation: &object::Relocation,
flags: RelocationFlags, ) -> Result<Option<RelocationOverride>> {
) -> Result<Option<i64>>; Ok(None)
}
fn demangle(&self, _name: &str) -> Option<String> { None } fn demangle(&self, _name: &str) -> Option<String> { None }
@ -411,7 +412,9 @@ pub fn new_arch(object: &object::File) -> Result<Box<dyn Arch>> {
use object::Object as _; use object::Object as _;
Ok(match object.architecture() { Ok(match object.architecture() {
#[cfg(feature = "ppc")] #[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")] #[cfg(feature = "mips")]
object::Architecture::Mips => Box::new(mips::ArchMips::new(object)?), object::Architecture::Mips => Box::new(mips::ArchMips::new(object)?),
#[cfg(feature = "x86")] #[cfg(feature = "x86")]
@ -456,16 +459,17 @@ impl Arch for ArchDummy {
Ok(()) Ok(())
} }
fn implcit_addend(
&self,
_file: &object::File<'_>,
_section: &object::Section,
_address: u64,
_relocation: &object::Relocation,
_flags: RelocationFlags,
) -> Result<Option<i64>> {
Ok(Some(0))
}
fn data_reloc_size(&self, _flags: RelocationFlags) -> usize { 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,
}

View File

@ -12,7 +12,7 @@ use flagset::Flags;
use object::{Object as _, ObjectSection as _, ObjectSymbol as _, elf, pe}; use object::{Object as _, ObjectSection as _, ObjectSymbol as _, elf, pe};
use crate::{ use crate::{
arch::{Arch, DataType}, arch::{Arch, DataType, RelocationOverride, RelocationOverrideTarget},
diff::{ diff::{
DiffObjConfig, DiffObjConfig,
data::resolve_relocation, data::resolve_relocation,
@ -100,6 +100,7 @@ impl ArchPpc {
| RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI | pe::IMAGE_REL_PPC_REFLO) => { | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI | pe::IMAGE_REL_PPC_REFLO) => {
ins.args.iter().rposition(is_rel_abs_arg) ins.args.iter().rposition(is_rel_abs_arg)
} }
RelocationFlags::Elf(elf::R_PPC64_TOC16) => ins.args.iter().rposition(is_offset_arg),
_ => None, _ => None,
} }
} }
@ -208,18 +209,18 @@ impl Arch for ArchPpc {
Some(flow_analysis::ppc_data_flow_analysis(obj, symbol, code, relocations, self.extensions)) Some(flow_analysis::ppc_data_flow_analysis(obj, symbol, code, relocations, self.extensions))
} }
fn implcit_addend( fn relocation_override(
&self, &self,
_file: &object::File<'_>, file: &object::File<'_>,
section: &object::Section, section: &object::Section,
address: u64, address: u64,
_relocation: &object::Relocation, relocation: &object::Relocation,
flags: RelocationFlags, ) -> Result<Option<RelocationOverride>> {
) -> Result<Option<i64>> { match relocation.flags() {
match flags {
// IMAGE_REL_PPC_PAIR contains the REF{HI,LO} displacement instead of a symbol index // IMAGE_REL_PPC_PAIR contains the REF{HI,LO} displacement instead of a symbol index
RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI) object::RelocationFlags::Coff {
| RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFLO) => section typ: pe::IMAGE_REL_PPC_REFHI | pe::IMAGE_REL_PPC_REFLO,
} => section
.relocations() .relocations()
.skip_while(|&(a, _)| a < address) .skip_while(|&(a, _)| a < address)
.take_while(|&(a, _)| a == address) .take_while(|&(a, _)| a == address)
@ -228,23 +229,81 @@ impl Arch for ArchPpc {
typ: pe::IMAGE_REL_PPC_PAIR typ: pe::IMAGE_REL_PPC_PAIR
}) })
}) })
.map_or(Ok(Some(0)), |(_, reloc)| match reloc.target() { .map_or(Ok(None), |(_, reloc)| match reloc.target() {
object::RelocationTarget::Symbol(index) => { object::RelocationTarget::Symbol(index) => Ok(Some(RelocationOverride {
Ok(Some(index.0 as u16 as i16 as i64)) target: RelocationOverrideTarget::Keep,
} addend: index.0 as u16 as i16 as i64,
})),
target => Err(anyhow!("Unsupported IMAGE_REL_PPC_PAIR target {target:?}")), target => Err(anyhow!("Unsupported IMAGE_REL_PPC_PAIR target {target:?}")),
}), }),
// Skip PAIR relocations as they are handled by the previous case // Skip PAIR relocations as they are handled by the previous case
RelocationFlags::Coff(pe::IMAGE_REL_PPC_PAIR) => Ok(None), object::RelocationFlags::Coff { typ: pe::IMAGE_REL_PPC_PAIR } => {
RelocationFlags::Coff(_) => Ok(Some(0)), Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Skip, addend: 0 }))
flags => Err(anyhow!("Unsupported PPC implicit relocation {flags:?}")), }
// 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<String> { fn demangle(&self, mut name: &str) -> Option<String> {
if name.starts_with('?') { if name.starts_with('?') {
msvc_demangler::demangle(name, msvc_demangler::DemangleFlags::llvm()).ok() msvc_demangler::demangle(name, msvc_demangler::DemangleFlags::llvm()).ok()
} else { } else {
name = name.trim_start_matches('.');
cpp_demangle::Symbol::new(name) cpp_demangle::Symbol::new(name)
.ok() .ok()
.and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).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_UADDR32 => Some("R_PPC_UADDR32"),
elf::R_PPC_REL24 => Some("R_PPC_REL24"), elf::R_PPC_REL24 => Some("R_PPC_REL24"),
elf::R_PPC_REL14 => Some("R_PPC_REL14"), elf::R_PPC_REL14 => Some("R_PPC_REL14"),
elf::R_PPC64_TOC16 => Some("R_PPC64_TOC16"),
_ => None, _ => None,
}, },
RelocationFlags::Coff(r_type) => match r_type { RelocationFlags::Coff(r_type) => match r_type {

View File

@ -1,6 +1,6 @@
use alloc::{collections::BTreeMap, format, string::String, vec, vec::Vec}; use alloc::{collections::BTreeMap, format, string::String, vec, vec::Vec};
use anyhow::{Result, bail}; use anyhow::Result;
use object::elf; use object::elf;
use crate::{ use crate::{
@ -132,17 +132,6 @@ impl Arch for ArchSuperH {
Ok(()) Ok(())
} }
fn implcit_addend(
&self,
_file: &object::File<'_>,
_section: &object::Section,
address: u64,
_relocation: &object::Relocation,
flags: RelocationFlags,
) -> Result<Option<i64>> {
bail!("Unsupported SuperH implicit relocation {:#x}:{:?}", address, flags)
}
fn demangle(&self, name: &str) -> Option<String> { fn demangle(&self, name: &str) -> Option<String> {
cpp_demangle::Symbol::new(name) cpp_demangle::Symbol::new(name)
.ok() .ok()

View File

@ -9,7 +9,7 @@ use iced_x86::{
use object::{Endian as _, Object as _, ObjectSection as _, elf, pe}; use object::{Endian as _, Object as _, ObjectSection as _, elf, pe};
use crate::{ use crate::{
arch::Arch, arch::{Arch, RelocationOverride, RelocationOverrideTarget},
diff::{DiffObjConfig, X86Formatter, display::InstructionPart}, diff::{DiffObjConfig, X86Formatter, display::InstructionPart},
obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef}, obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef},
}; };
@ -225,40 +225,47 @@ impl Arch for ArchX86 {
Ok(()) Ok(())
} }
fn implcit_addend( fn relocation_override(
&self, &self,
_file: &object::File<'_>, _file: &object::File<'_>,
section: &object::Section, section: &object::Section,
address: u64, address: u64,
_relocation: &object::Relocation, relocation: &object::Relocation,
flags: RelocationFlags, ) -> Result<Option<RelocationOverride>> {
) -> Result<Option<i64>> { if !relocation.has_implicit_addend() {
match self.arch { return Ok(None);
Architecture::X86 => match flags { }
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) let addend = match self.arch {
| RelocationFlags::Elf(elf::R_386_32 | elf::R_386_PC32) => { 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 = let data =
section.data()?[address as usize..address as usize + 4].try_into()?; 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:?}"), flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
}, },
Architecture::X86_64 => match flags { Architecture::X86_64 => match relocation.flags() {
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32) object::RelocationFlags::Coff {
| RelocationFlags::Elf(elf::R_X86_64_32 | elf::R_X86_64_PC32) => { 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 = let data =
section.data()?[address as usize..address as usize + 4].try_into()?; 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) object::RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 }
| RelocationFlags::Elf(elf::R_X86_64_64) => { | object::RelocationFlags::Elf { r_type: elf::R_X86_64_64 } => {
let data = let data =
section.data()?[address as usize..address as usize + 8].try_into()?; 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:?}"), flags => bail!("Unsupported x86-64 implicit relocation {flags:?}"),
}, },
} };
Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend }))
} }
fn demangle(&self, name: &str) -> Option<String> { fn demangle(&self, name: &str) -> Option<String> {

View File

@ -11,7 +11,7 @@ use anyhow::{Context, Result, anyhow, bail, ensure};
use object::{Object as _, ObjectSection as _, ObjectSymbol as _}; use object::{Object as _, ObjectSection as _, ObjectSymbol as _};
use crate::{ use crate::{
arch::{Arch, new_arch}, arch::{Arch, RelocationOverride, RelocationOverrideTarget, new_arch},
diff::DiffObjConfig, diff::DiffObjConfig,
obj::{ obj::{
FlowAnalysisResult, Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag, FlowAnalysisResult, Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag,
@ -24,8 +24,13 @@ use crate::{
fn map_section_kind(section: &object::Section) -> SectionKind { fn map_section_kind(section: &object::Section) -> SectionKind {
match section.kind() { match section.kind() {
object::SectionKind::Text => SectionKind::Code, object::SectionKind::Text => SectionKind::Code,
object::SectionKind::Data | object::SectionKind::ReadOnlyData => SectionKind::Data, object::SectionKind::Data
object::SectionKind::UninitializedData => SectionKind::Bss, | object::SectionKind::ReadOnlyData
| object::SectionKind::ReadOnlyString
| object::SectionKind::Tls => SectionKind::Data,
object::SectionKind::UninitializedData
| object::SectionKind::UninitializedTls
| object::SectionKind::Common => SectionKind::Bss,
_ => SectionKind::Unknown, _ => SectionKind::Unknown,
} }
} }
@ -329,63 +334,125 @@ fn map_section_relocations(
) -> Result<Vec<Relocation>> { ) -> Result<Vec<Relocation>> {
let mut relocations = Vec::<Relocation>::with_capacity(obj_section.relocations().count()); let mut relocations = Vec::<Relocation>::with_capacity(obj_section.relocations().count());
for (address, reloc) in obj_section.relocations() { for (address, reloc) in obj_section.relocations() {
let flags = match reloc.flags() { let mut target_reloc = RelocationOverride {
object::RelocationFlags::Elf { r_type } => RelocationFlags::Elf(r_type), target: match reloc.target() {
object::RelocationFlags::Coff { typ } => RelocationFlags::Coff(typ), object::RelocationTarget::Symbol(symbol) => {
flags => { RelocationOverrideTarget::Symbol(symbol)
bail!("Unhandled relocation flags: {:?}", flags); }
} object::RelocationTarget::Section(section) => {
RelocationOverrideTarget::Section(section)
}
_ => RelocationOverrideTarget::Skip,
},
addend: reloc.addend(),
}; };
// TODO validate reloc here?
let mut addend = if reloc.has_implicit_addend() { // Allow the architecture to override the relocation target and addend
match arch.implcit_addend(obj_file, obj_section, address, &reloc, flags)? { match arch.relocation_override(obj_file, obj_section, address, &reloc)? {
Some(addend) => addend, Some(reloc_override) => {
None => continue, // Skip relocation (e.g. COFF PAIR relocations) match reloc_override.target {
RelocationOverrideTarget::Keep => {}
target => {
target_reloc.target = target;
}
}
target_reloc.addend = reloc_override.addend;
} }
} else { None => {
reloc.addend() ensure!(
}; !reloc.has_implicit_addend(),
let target_symbol = match reloc.target() { "Unsupported implicit relocation {:?}",
object::RelocationTarget::Symbol(idx) => { reloc.flags()
if idx.0 == u32::MAX as usize { );
// ??? }
}
// 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; continue;
} }
// If the target is a section symbol, try to resolve a better symbol as the target // 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 if let Some(section_symbol) = obj_file
.symbol_by_index(idx) .symbol_by_index(symbol_index)
.ok() .ok()
.filter(|s| s.kind() == object::SymbolKind::Section) .filter(|s| s.kind() == object::SymbolKind::Section)
{ {
let section_index = let section_index =
section_symbol.section_index().context("Section symbol without section")?; 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 if let Some((new_idx, addr)) = ordered_symbols
.get(section_index.0) .get(section_index.0)
.and_then(|symbols| best_symbol(symbols, target_address)) .and_then(|symbols| best_symbol(symbols, target_address))
{ {
addend = target_address.wrapping_sub(addr) as i64; (new_idx, target_address.wrapping_sub(addr) as i64)
new_idx
} else { } else {
idx (symbol_index, target_reloc.addend)
} }
} else { } else {
idx (symbol_index, target_reloc.addend)
};
match symbol_indices.get(idx.0).copied() {
Some(i) => i,
None => {
log::warn!("Invalid symbol index {}", idx.0);
continue;
}
} }
} }
object::RelocationTarget::Absolute => { RelocationOverrideTarget::Section(section_index) => {
let section_name = obj_section.name()?; let section = match obj_file.section_by_index(section_index) {
log::warn!("Ignoring absolute relocation @ {section_name}:{address:#x}"); 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; continue;
} }
_ => bail!("Unhandled relocation target: {:?}", reloc.target()),
}; };
relocations.push(Relocation { address, flags, target_symbol, addend }); relocations.push(Relocation { address, flags, target_symbol, addend });
} }