From 3abdbbd97ad1948d9afdf29061b22cdec8fc3174 Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Tue, 10 Mar 2026 16:55:56 +1000 Subject: [PATCH] macho: AArch64 uses local temporary symbols for sections --- .../macho/base-aarch64-debug.o.objdump | 9 +++-- .../testfiles/macho/base-aarch64.o.objdump | 9 +++-- .../testfiles/macho/reloc-aarch64.o.objdump | 5 ++- src/read/macho/symbol.rs | 35 +++++++++++++------ src/write/macho.rs | 8 ++++- src/write/mod.rs | 3 ++ 6 files changed, 44 insertions(+), 25 deletions(-) diff --git a/crates/examples/testfiles/macho/base-aarch64-debug.o.objdump b/crates/examples/testfiles/macho/base-aarch64-debug.o.objdump index 0da26fd..7457a39 100644 --- a/crates/examples/testfiles/macho/base-aarch64-debug.o.objdump +++ b/crates/examples/testfiles/macho/base-aarch64-debug.o.objdump @@ -18,10 +18,10 @@ Segment { name: "", address: 0, size: 2ca, permissions: RWX } 11: Section { segment: "__DWARF", name: "__debug_line", address: 288, size: 42, align: 1, kind: Debug, flags: MachO { flags: 2000000 } } Symbols -0: Symbol { name: "ltmp0", address: 0, size: 0, kind: Text, section: Section(SectionIndex(1)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } +0: Symbol { name: "ltmp0", address: 0, size: 0, kind: Section, section: Section(SectionIndex(1)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } 1: Symbol { name: "l_.str", address: 34, size: 0, kind: Data, section: Section(SectionIndex(2)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } -2: Symbol { name: "ltmp1", address: 34, size: 0, kind: Data, section: Section(SectionIndex(2)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } -3: Symbol { name: "ltmp2", address: 268, size: 0, kind: Unknown, section: Section(SectionIndex(a)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } +2: Symbol { name: "ltmp1", address: 34, size: 0, kind: Section, section: Section(SectionIndex(2)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } +3: Symbol { name: "ltmp2", address: 268, size: 0, kind: Section, section: Section(SectionIndex(a)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } 4: Symbol { name: "_main", address: 0, size: 0, kind: Text, section: Section(SectionIndex(1)), scope: Dynamic, weak: false, flags: MachO { n_desc: 0 } } 5: Symbol { name: "_printf", address: 0, size: 0, kind: Unknown, section: Undefined, scope: Unknown, weak: false, flags: MachO { n_desc: 0 } } @@ -48,5 +48,4 @@ Export { name: "_main", address: 0 } Symbol map 0x0 "_main" -0x34 "ltmp1" -0x268 "ltmp2" +0x34 "l_.str" diff --git a/crates/examples/testfiles/macho/base-aarch64.o.objdump b/crates/examples/testfiles/macho/base-aarch64.o.objdump index 83213f6..1e8345b 100644 --- a/crates/examples/testfiles/macho/base-aarch64.o.objdump +++ b/crates/examples/testfiles/macho/base-aarch64.o.objdump @@ -10,10 +10,10 @@ Segment { name: "", address: 0, size: 68, permissions: RWX } 3: Section { segment: "__LD", name: "__compact_unwind", address: 48, size: 20, align: 8, kind: Unknown, flags: MachO { flags: 2000000 } } Symbols -0: Symbol { name: "ltmp0", address: 0, size: 0, kind: Text, section: Section(SectionIndex(1)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } +0: Symbol { name: "ltmp0", address: 0, size: 0, kind: Section, section: Section(SectionIndex(1)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } 1: Symbol { name: "l_.str", address: 34, size: 0, kind: Data, section: Section(SectionIndex(2)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } -2: Symbol { name: "ltmp1", address: 34, size: 0, kind: Data, section: Section(SectionIndex(2)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } -3: Symbol { name: "ltmp2", address: 48, size: 0, kind: Unknown, section: Section(SectionIndex(3)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } +2: Symbol { name: "ltmp1", address: 34, size: 0, kind: Section, section: Section(SectionIndex(2)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } +3: Symbol { name: "ltmp2", address: 48, size: 0, kind: Section, section: Section(SectionIndex(3)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } 4: Symbol { name: "_main", address: 0, size: 0, kind: Text, section: Section(SectionIndex(1)), scope: Dynamic, weak: false, flags: MachO { n_desc: 0 } } 5: Symbol { name: "_printf", address: 0, size: 0, kind: Unknown, section: Undefined, scope: Unknown, weak: false, flags: MachO { n_desc: 0 } } @@ -33,5 +33,4 @@ Export { name: "_main", address: 0 } Symbol map 0x0 "_main" -0x34 "ltmp1" -0x48 "ltmp2" +0x34 "l_.str" diff --git a/crates/examples/testfiles/macho/reloc-aarch64.o.objdump b/crates/examples/testfiles/macho/reloc-aarch64.o.objdump index 954e86e..de437c6 100644 --- a/crates/examples/testfiles/macho/reloc-aarch64.o.objdump +++ b/crates/examples/testfiles/macho/reloc-aarch64.o.objdump @@ -9,8 +9,8 @@ Segment { name: "", address: 0, size: 48, permissions: RWX } 2: Section { segment: "__DATA", name: "__data", address: 28, size: 20, align: 1, kind: Data, flags: MachO { flags: 0 } } Symbols -0: Symbol { name: "ltmp0", address: 0, size: 0, kind: Text, section: Section(SectionIndex(1)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } -1: Symbol { name: "ltmp1", address: 28, size: 0, kind: Data, section: Section(SectionIndex(2)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } +0: Symbol { name: "ltmp0", address: 0, size: 0, kind: Section, section: Section(SectionIndex(1)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } +1: Symbol { name: "ltmp1", address: 28, size: 0, kind: Section, section: Section(SectionIndex(2)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } 2: Symbol { name: "_data", address: 28, size: 0, kind: Data, section: Section(SectionIndex(2)), scope: Compilation, weak: false, flags: MachO { n_desc: 0 } } 3: Symbol { name: "_g0", address: 0, size: 0, kind: Unknown, section: Undefined, scope: Unknown, weak: false, flags: MachO { n_desc: 0 } } 4: Symbol { name: "_g1", address: 0, size: 0, kind: Unknown, section: Undefined, scope: Unknown, weak: false, flags: MachO { n_desc: 0 } } @@ -42,5 +42,4 @@ Import { library: "", name: "_g0" } Import { library: "", name: "_g1" } Symbol map -0x0 "ltmp0" 0x28 "_data" diff --git a/src/read/macho/symbol.rs b/src/read/macho/symbol.rs index cff93d3..6e5269e 100644 --- a/src/read/macho/symbol.rs +++ b/src/read/macho/symbol.rs @@ -12,7 +12,7 @@ use crate::read::{ SymbolMapEntry, SymbolScope, SymbolSection, }; -use super::{MachHeader, MachOFile}; +use super::{MachHeader, MachOFile, Section}; /// A table of symbol entries in a Mach-O file. /// @@ -370,17 +370,30 @@ where self.section() .index() .and_then(|index| self.file.section_internal(index).ok()) - .map(|section| match section.kind { - SectionKind::Text => SymbolKind::Text, - SectionKind::Data - | SectionKind::ReadOnlyData - | SectionKind::ReadOnlyString - | SectionKind::UninitializedData - | SectionKind::Common => SymbolKind::Data, - SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables => { - SymbolKind::Tls + .map(|section| { + if let Ok(name) = self.name_bytes() { + // Heuristic to match LLVM's convention for section symbols; may misclassify. + if self.is_local() + && name.len() > 4 + && name.starts_with(b"ltmp") + && name[4..].iter().all(|b| b.is_ascii_digit()) + && self.address() == section.section.addr(self.file.endian).into() + { + return SymbolKind::Section; + } + } + match section.kind { + SectionKind::Text => SymbolKind::Text, + SectionKind::Data + | SectionKind::ReadOnlyData + | SectionKind::ReadOnlyString + | SectionKind::UninitializedData + | SectionKind::Common => SymbolKind::Data, + SectionKind::Tls + | SectionKind::UninitializedTls + | SectionKind::TlsVariables => SymbolKind::Tls, + _ => SymbolKind::Unknown, } - _ => SymbolKind::Unknown, }) .unwrap_or(SymbolKind::Unknown) } diff --git a/src/write/macho.rs b/src/write/macho.rs index 09137c6..dc0cae0 100644 --- a/src/write/macho.rs +++ b/src/write/macho.rs @@ -483,7 +483,13 @@ impl<'a> Object<'a> { // Since we don't actually emit the symbol kind, we validate it here too. match symbol.kind { SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls | SymbolKind::Unknown => {} - SymbolKind::File | SymbolKind::Section => continue, + SymbolKind::Section => { + // AArch64 needs temporary section symbols for relative relocations. + if self.architecture != Architecture::Aarch64 { + continue; + } + } + SymbolKind::File => continue, SymbolKind::Label => { return Err(Error(format!( "unimplemented symbol `{}` kind {:?}", diff --git a/src/write/mod.rs b/src/write/mod.rs index b4ba993..b9d636f 100644 --- a/src/write/mod.rs +++ b/src/write/mod.rs @@ -572,6 +572,9 @@ impl<'a> Object<'a> { } let name = if self.format == BinaryFormat::Coff { section.name.clone() + } else if self.format == BinaryFormat::MachO { + // Use LLVM's convention for section symbols. + format!("ltmp{}", section_id.0).into_bytes() } else { Vec::new() };