From f3f89e71e29ade00b06c87ae48a2d2ece33703f6 Mon Sep 17 00:00:00 2001 From: Haydn Trigg Date: Sun, 14 Jun 2026 00:22:43 +0930 Subject: [PATCH] Mach-O Object Support --- objdiff-core/Cargo.toml | 2 +- objdiff-core/src/arch/ppc/mod.rs | 2 ++ objdiff-core/src/arch/x86.rs | 19 +++++++++++++++++++ objdiff-core/src/bindings/diff.rs | 1 + objdiff-core/src/obj/mod.rs | 1 + objdiff-core/src/obj/read.rs | 12 +++++++++--- 6 files changed, 33 insertions(+), 4 deletions(-) diff --git a/objdiff-core/Cargo.toml b/objdiff-core/Cargo.toml index cde4eeb9..59dc96db 100644 --- a/objdiff-core/Cargo.toml +++ b/objdiff-core/Cargo.toml @@ -132,7 +132,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 = { version = "0.39.1", default-features = false, features = ["read_core", "elf", "coff"] } +object = { version = "0.39.1", default-features = false, features = ["read_core", "elf", "coff", "macho"] } pbjson = { version = "0.8", default-features = false, optional = true } prost = { version = "0.14", default-features = false, features = ["derive"], optional = true } regex = { version = "1.12", default-features = false, features = [], optional = true } diff --git a/objdiff-core/src/arch/ppc/mod.rs b/objdiff-core/src/arch/ppc/mod.rs index 0cefda20..475fa3d7 100644 --- a/objdiff-core/src/arch/ppc/mod.rs +++ b/objdiff-core/src/arch/ppc/mod.rs @@ -332,6 +332,7 @@ impl Arch for ArchPpc { pe::IMAGE_REL_PPC_PAIR => Some("IMAGE_REL_PPC_PAIR"), _ => None, }, + RelocationFlags::MachO { .. } => None, } } @@ -346,6 +347,7 @@ impl Arch for ArchPpc { pe::IMAGE_REL_PPC_ADDR32 => 4, _ => 1, }, + RelocationFlags::MachO { .. } => 1, } } diff --git a/objdiff-core/src/arch/x86.rs b/objdiff-core/src/arch/x86.rs index b02bd0ee..6c43638c 100644 --- a/objdiff-core/src/arch/x86.rs +++ b/objdiff-core/src/arch/x86.rs @@ -77,6 +77,7 @@ impl ArchX86 { elf::R_386_16 => Some(2), _ => None, }, + RelocationFlags::MachO { r_length, .. } => Some(1 << r_length as usize), }, Architecture::X86_64 => match flags { RelocationFlags::Coff(typ) => match typ { @@ -96,6 +97,7 @@ impl ArchX86 { elf::R_X86_64_64 => Some(8), _ => None, }, + RelocationFlags::MachO { .. } => None, }, } } @@ -303,6 +305,22 @@ impl Arch for ArchX86 { section.data()?[address as usize..address as usize + 4].try_into()?; self.endianness.read_i32(data) as i64 } + object::RelocationFlags::MachO { r_length, .. } => { + let size = 1usize << r_length as usize; + match size { + 4 => { + let data = section.data()?[address as usize..address as usize + 4] + .try_into()?; + self.endianness.read_i32(data) as i64 + } + 8 => { + let data = section.data()?[address as usize..address as usize + 8] + .try_into()?; + self.endianness.read_i64(data) + } + _ => bail!("Unsupported MachO x86 implicit relocation length: {r_length}"), + } + } flags => bail!("Unsupported x86 implicit relocation {flags:?}"), }, Architecture::X86_64 => match relocation.flags() { @@ -352,6 +370,7 @@ impl Arch for ArchX86 { elf::R_386_16 => Some("R_386_16"), _ => None, }, + RelocationFlags::MachO { .. } => None, }, Architecture::X86_64 => match flags { RelocationFlags::Coff(typ) => match typ { diff --git a/objdiff-core/src/bindings/diff.rs b/objdiff-core/src/bindings/diff.rs index 0dd22b6c..68c7ace6 100644 --- a/objdiff-core/src/bindings/diff.rs +++ b/objdiff-core/src/bindings/diff.rs @@ -294,6 +294,7 @@ fn relocation_type(flags: obj::RelocationFlags) -> u32 { match flags { obj::RelocationFlags::Elf(r_type) => r_type, obj::RelocationFlags::Coff(typ) => typ as u32, + obj::RelocationFlags::MachO { r_type, .. } => r_type as u32, } } diff --git a/objdiff-core/src/obj/mod.rs b/objdiff-core/src/obj/mod.rs index 479186e1..0b00497a 100644 --- a/objdiff-core/src/obj/mod.rs +++ b/objdiff-core/src/obj/mod.rs @@ -374,6 +374,7 @@ pub struct Relocation { pub enum RelocationFlags { Elf(u32), Coff(u16), + MachO { r_type: u8, r_pcrel: bool, r_length: u8 }, } #[derive(Debug, Copy, Clone)] diff --git a/objdiff-core/src/obj/read.rs b/objdiff-core/src/obj/read.rs index 6046070d..9da2706d 100644 --- a/objdiff-core/src/obj/read.rs +++ b/objdiff-core/src/obj/read.rs @@ -133,7 +133,8 @@ fn map_symbol( if symbol.is_weak() { flags |= SymbolFlag::Weak; } - if file.format() == object::BinaryFormat::Elf && symbol.scope() == object::SymbolScope::Linkage + if matches!(file.format(), object::BinaryFormat::Elf | object::BinaryFormat::MachO) + && symbol.scope() == object::SymbolScope::Linkage { flags |= SymbolFlag::Hidden; } @@ -252,6 +253,7 @@ fn add_section_symbols(sections: &[Section], symbols: &mut Vec) { // `section.size` can include extra padding, so instead prefer using the address that the // last symbol ends at when there are any symbols in the section. + // Compute size as a section-relative offset (important for non-zero section.address, e.g. Mach-O). let size = symbols .iter() .filter(|s| { @@ -259,13 +261,14 @@ fn add_section_symbols(sections: &[Section], symbols: &mut Vec) { }) .map(|s| s.address + s.size) .max() - .unwrap_or(section.size); + .unwrap_or(section.address + section.size) + .saturating_sub(section.address); symbols.push(Symbol { name, demangled_name: None, normalized_name: None, - address: 0, + address: section.address, size, kind: SymbolKind::Section, section: Some(section_idx), @@ -579,6 +582,9 @@ fn map_section_relocations( let flags = match reloc.flags() { object::RelocationFlags::Elf { r_type } => RelocationFlags::Elf(r_type), object::RelocationFlags::Coff { typ } => RelocationFlags::Coff(typ), + object::RelocationFlags::MachO { r_type, r_pcrel, r_length } => { + RelocationFlags::MachO { r_type, r_pcrel, r_length } + } flags => bail!("Unhandled relocation flags: {:?}", flags), }; let target_symbol = match symbol_indices.get(symbol_index.0).copied() {