Rust · 49365 bytes Raw Blame History
1 pub mod code_sig;
2 pub mod dyld_info;
3 pub mod got;
4 pub mod stubs;
5 pub mod tlv;
6 pub mod unwind;
7
8 use std::collections::{HashMap, HashSet};
9 use std::fmt;
10 use std::path::PathBuf;
11
12 use crate::atom::{Atom, AtomSection, AtomTable};
13 use crate::input::ObjectFile;
14 use crate::layout::LayoutInput;
15 use crate::macho::constants::{
16 S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS, S_LAZY_SYMBOL_POINTERS,
17 S_NON_LAZY_SYMBOL_POINTERS, S_REGULAR, S_SYMBOL_STUBS, S_THREAD_LOCAL_VARIABLE_POINTERS,
18 };
19 use crate::reloc::{
20 parse_raw_relocs, parse_relocs, ParsedRelocCache, Referent, Reloc, RelocKind, RelocLength,
21 };
22 use crate::resolve::{
23 AtomId, DylibId, DylibInput, InputId, InsertOutcome, Symbol, SymbolId, SymbolTable,
24 };
25 use crate::section::{OutputSection, SectionKind};
26
27 use self::got::GotSection;
28 use self::stubs::{
29 LazyPointerSection, StubsSection, DYLD_PRIVATE_SIZE, STUB_HELPER_ENTRY_SIZE,
30 STUB_HELPER_HEADER_SIZE,
31 };
32 use self::tlv::{ThreadPointerSection, THREAD_POINTER_SIZE};
33
34 const COMPACT_UNWIND_PERSONALITY_FIELD_OFFSET: u32 = 16;
35
36 #[derive(Debug, Clone, PartialEq, Eq)]
37 pub struct SyntheticPlan {
38 pub got: GotSection,
39 pub stubs: StubsSection,
40 pub lazy_pointers: LazyPointerSection,
41 pub thread_pointers: ThreadPointerSection,
42 pub direct_binds: Vec<DirectBind>,
43 pub binder_symbol: Option<SymbolId>,
44 pub tlv_bootstrap_symbol: Option<SymbolId>,
45 pub needs_dyld_private: bool,
46 }
47
48 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
49 pub struct DirectBind {
50 pub atom: AtomId,
51 pub atom_offset: u32,
52 pub symbol: SymbolId,
53 pub addend: i64,
54 }
55
56 #[derive(Debug, Clone, PartialEq, Eq)]
57 pub struct SynthError {
58 pub input: PathBuf,
59 pub atom: crate::resolve::AtomId,
60 pub reloc_offset: u32,
61 pub kind: RelocKind,
62 pub detail: String,
63 }
64
65 impl fmt::Display for SynthError {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 write!(
68 f,
69 "{}: synthetic planning for {:?} at atom {:?}+0x{:x}: {}",
70 self.input.display(),
71 self.kind,
72 self.atom,
73 self.reloc_offset,
74 self.detail
75 )
76 }
77 }
78
79 impl std::error::Error for SynthError {}
80
81 impl SyntheticPlan {
82 pub fn build(
83 inputs: &[LayoutInput<'_>],
84 atoms: &AtomTable,
85 sym_table: &mut SymbolTable,
86 dylibs: &[DylibInput],
87 ) -> Result<Self, SynthError> {
88 Self::build_filtered(inputs, atoms, sym_table, dylibs, None)
89 }
90
91 pub fn build_filtered(
92 inputs: &[LayoutInput<'_>],
93 atoms: &AtomTable,
94 sym_table: &mut SymbolTable,
95 dylibs: &[DylibInput],
96 live_atoms: Option<&HashSet<AtomId>>,
97 ) -> Result<Self, SynthError> {
98 let reloc_cache = build_synthetic_reloc_cache(inputs)?;
99 Self::build_filtered_with_relocs(inputs, atoms, sym_table, dylibs, live_atoms, &reloc_cache)
100 }
101
102 pub fn build_filtered_with_relocs(
103 inputs: &[LayoutInput<'_>],
104 atoms: &AtomTable,
105 sym_table: &mut SymbolTable,
106 dylibs: &[DylibInput],
107 live_atoms: Option<&HashSet<AtomId>>,
108 parsed_relocs: &ParsedRelocCache,
109 ) -> Result<Self, SynthError> {
110 let input_map: HashMap<InputId, &ObjectFile> = inputs
111 .iter()
112 .map(|input| (input.id, input.object))
113 .collect();
114 let input_symbol_index = build_input_symbol_index(inputs, sym_table, parsed_relocs);
115 let reloc_index = build_sorted_reloc_index(parsed_relocs);
116
117 let mut got = GotSection::default();
118 let mut stubs = StubsSection::default();
119 let mut lazy_pointers = LazyPointerSection::default();
120 let mut thread_pointers = ThreadPointerSection::default();
121 let mut direct_binds = Vec::new();
122
123 for (atom_id, atom) in atoms.iter() {
124 if live_atoms.is_some_and(|live_atoms| !live_atoms.contains(&atom_id)) {
125 continue;
126 }
127 let obj = input_map.get(&atom.origin).ok_or_else(|| SynthError {
128 input: PathBuf::from("<missing object>"),
129 atom: atom_id,
130 reloc_offset: 0,
131 kind: RelocKind::Unsigned,
132 detail: "missing parsed object".to_string(),
133 })?;
134 let relocs = reloc_index
135 .get(&(atom.origin, atom.input_section))
136 .map(Vec::as_slice)
137 .unwrap_or(&[]);
138 let input_symbols = input_symbol_index.get(&atom.origin).map(Vec::as_slice);
139 for reloc in relocs_for_atom(relocs, atom) {
140 if atom.section == AtomSection::CompactUnwind
141 && reloc.kind == RelocKind::Unsigned
142 && reloc.offset == atom.input_offset + COMPACT_UNWIND_PERSONALITY_FIELD_OFFSET
143 {
144 if let Some(symbol_id) =
145 dylib_import_referent(obj, reloc.referent, sym_table, input_symbols)
146 {
147 got.intern(symbol_id, dylib_import_is_weak(sym_table, symbol_id));
148 }
149 continue;
150 }
151 match reloc.kind {
152 RelocKind::Unsigned => {
153 // `__thread_vars` descriptors carry their own dedicated
154 // bind encoding later in the writer; don't double-count
155 // those dylib imports as generic bound pointer slots.
156 if matches!(atom.section, AtomSection::ThreadLocalVariables) {
157 continue;
158 }
159 let Some(symbol_id) =
160 dylib_import_referent(obj, reloc.referent, sym_table, input_symbols)
161 else {
162 continue;
163 };
164 if direct_import_bind_supported(reloc) {
165 direct_binds.push(DirectBind {
166 atom: atom_id,
167 atom_offset: reloc.offset.saturating_sub(atom.input_offset),
168 symbol: symbol_id,
169 addend: reloc.addend,
170 });
171 }
172 }
173 RelocKind::GotLoadPage21
174 | RelocKind::GotLoadPageOff12
175 | RelocKind::PointerToGot => {
176 let Some(symbol_id) =
177 symbol_referent_id(obj, reloc.referent, sym_table, input_symbols)
178 else {
179 continue;
180 };
181 let needs_slot = match reloc.kind {
182 RelocKind::PointerToGot => {
183 !matches!(sym_table.get(symbol_id), Symbol::LazyArchive { .. })
184 }
185 RelocKind::GotLoadPage21 | RelocKind::GotLoadPageOff12 => {
186 got_page_symbol_needs_slot(sym_table, atoms, symbol_id)
187 }
188 _ => false,
189 };
190 if needs_slot {
191 got.intern(symbol_id, dylib_import_is_weak(sym_table, symbol_id));
192 continue;
193 }
194 }
195 RelocKind::Branch26 => {
196 let Some(symbol_id) =
197 dylib_import_referent(obj, reloc.referent, sym_table, input_symbols)
198 else {
199 continue;
200 };
201 let dylib = match sym_table.get(symbol_id) {
202 Symbol::DylibImport { dylib, .. } => *dylib,
203 _ => continue,
204 };
205 stubs.intern(symbol_id, dylib, dylib_import_is_weak(sym_table, symbol_id));
206 lazy_pointers.intern(
207 symbol_id,
208 dylib,
209 dylib_import_is_weak(sym_table, symbol_id),
210 );
211 }
212 RelocKind::TlvpLoadPage21 | RelocKind::TlvpLoadPageOff12 => {
213 if let Some(symbol_id) =
214 symbol_referent_id(obj, reloc.referent, sym_table, input_symbols)
215 {
216 if tlv_symbol_needs_got(sym_table, symbol_id) {
217 got.intern(symbol_id, dylib_import_is_weak(sym_table, symbol_id));
218 } else if tlv_symbol_needs_thread_pointer(sym_table, symbol_id) {
219 thread_pointers.intern(symbol_id);
220 }
221 }
222 }
223 _ => {}
224 }
225 }
226 }
227
228 sort_symbol_indexed_entries(
229 &mut got.entries,
230 &mut got.index,
231 |entry| entry.symbol,
232 sym_table,
233 );
234 sort_symbol_indexed_entries(
235 &mut stubs.entries,
236 &mut stubs.index,
237 |entry| entry.symbol,
238 sym_table,
239 );
240 sort_symbol_indexed_entries(
241 &mut lazy_pointers.entries,
242 &mut lazy_pointers.index,
243 |entry| entry.symbol,
244 sym_table,
245 );
246 sort_symbol_indexed_entries(
247 &mut thread_pointers.entries,
248 &mut thread_pointers.index,
249 |entry| entry.symbol,
250 sym_table,
251 );
252
253 let mut binder_symbol = None;
254 let mut tlv_bootstrap_symbol = None;
255 let mut needs_dyld_private = false;
256 if !stubs.entries.is_empty() {
257 let binder = ensure_stub_helper_support(sym_table, dylibs, &mut got)?;
258 binder_symbol = Some(binder);
259 needs_dyld_private = true;
260 }
261 if !thread_pointers.entries.is_empty() || inputs_have_tlv_descriptors(inputs) {
262 tlv_bootstrap_symbol = ensure_tlv_support(sym_table, dylibs)?;
263 }
264
265 Ok(SyntheticPlan {
266 got,
267 stubs,
268 lazy_pointers,
269 thread_pointers,
270 direct_binds,
271 binder_symbol,
272 tlv_bootstrap_symbol,
273 needs_dyld_private,
274 })
275 }
276
277 pub fn output_sections(&self) -> Vec<OutputSection> {
278 let mut out = Vec::new();
279 if !self.stubs.entries.is_empty() {
280 out.push(OutputSection {
281 segment: "__TEXT".into(),
282 name: "__stubs".into(),
283 kind: SectionKind::SymbolStubs,
284 align_pow2: 2,
285 flags: S_SYMBOL_STUBS | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS,
286 reserved1: 0,
287 reserved2: stubs::STUB_SIZE,
288 reserved3: 0,
289 atoms: Vec::new(),
290 synthetic_offset: 0,
291 synthetic_data: vec![0; self.stubs.entries.len() * stubs::STUB_SIZE as usize],
292 addr: 0,
293 size: (self.stubs.entries.len() as u64) * stubs::STUB_SIZE as u64,
294 file_off: 0,
295 });
296 }
297 if self.binder_symbol.is_some() {
298 out.push(OutputSection {
299 segment: "__TEXT".into(),
300 name: "__stub_helper".into(),
301 kind: SectionKind::Text,
302 align_pow2: 2,
303 flags: S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS,
304 reserved1: 0,
305 reserved2: 0,
306 reserved3: 0,
307 atoms: Vec::new(),
308 synthetic_offset: 0,
309 synthetic_data: vec![
310 0;
311 STUB_HELPER_HEADER_SIZE as usize
312 + self.lazy_pointers.entries.len()
313 * STUB_HELPER_ENTRY_SIZE as usize
314 ],
315 addr: 0,
316 size: STUB_HELPER_HEADER_SIZE as u64
317 + (self.lazy_pointers.entries.len() as u64) * STUB_HELPER_ENTRY_SIZE as u64,
318 file_off: 0,
319 });
320 }
321 if !self.got.entries.is_empty() {
322 out.push(OutputSection {
323 segment: "__DATA_CONST".into(),
324 name: "__got".into(),
325 kind: SectionKind::NonLazySymbolPointers,
326 align_pow2: 3,
327 flags: S_NON_LAZY_SYMBOL_POINTERS,
328 reserved1: 0,
329 reserved2: 0,
330 reserved3: 0,
331 atoms: Vec::new(),
332 synthetic_offset: 0,
333 synthetic_data: vec![0; self.got.entries.len() * 8],
334 addr: 0,
335 size: (self.got.entries.len() as u64) * 8,
336 file_off: 0,
337 });
338 }
339 if self.needs_dyld_private {
340 out.push(OutputSection {
341 segment: "__DATA".into(),
342 name: "__data".into(),
343 kind: SectionKind::Data,
344 align_pow2: 3,
345 flags: S_REGULAR,
346 reserved1: 0,
347 reserved2: 0,
348 reserved3: 0,
349 atoms: Vec::new(),
350 synthetic_offset: 0,
351 synthetic_data: vec![0; DYLD_PRIVATE_SIZE as usize],
352 addr: 0,
353 size: DYLD_PRIVATE_SIZE as u64,
354 file_off: 0,
355 });
356 }
357 if !self.lazy_pointers.entries.is_empty() {
358 out.push(OutputSection {
359 segment: "__DATA".into(),
360 name: "__la_symbol_ptr".into(),
361 kind: SectionKind::LazySymbolPointers,
362 align_pow2: 3,
363 flags: S_LAZY_SYMBOL_POINTERS,
364 reserved1: 0,
365 reserved2: 0,
366 reserved3: 0,
367 atoms: Vec::new(),
368 synthetic_offset: 0,
369 synthetic_data: vec![0; self.lazy_pointers.entries.len() * 8],
370 addr: 0,
371 size: (self.lazy_pointers.entries.len() as u64) * 8,
372 file_off: 0,
373 });
374 }
375 if !self.thread_pointers.entries.is_empty() {
376 out.push(OutputSection {
377 segment: "__DATA".into(),
378 name: "__thread_ptrs".into(),
379 kind: SectionKind::ThreadLocalVariablePointers,
380 align_pow2: 3,
381 flags: S_THREAD_LOCAL_VARIABLE_POINTERS,
382 reserved1: 0,
383 reserved2: 0,
384 reserved3: 0,
385 atoms: Vec::new(),
386 synthetic_offset: 0,
387 synthetic_data: vec![
388 0;
389 self.thread_pointers.entries.len()
390 * THREAD_POINTER_SIZE as usize
391 ],
392 addr: 0,
393 size: (self.thread_pointers.entries.len() as u64) * THREAD_POINTER_SIZE as u64,
394 file_off: 0,
395 });
396 }
397 out
398 }
399 }
400
401 fn sort_symbol_indexed_entries<T, F>(
402 entries: &mut [T],
403 index: &mut HashMap<SymbolId, usize>,
404 symbol_of: F,
405 sym_table: &SymbolTable,
406 ) where
407 F: Fn(&T) -> SymbolId,
408 {
409 entries.sort_by(|lhs, rhs| {
410 let lhs_name = sym_table
411 .interner
412 .resolve(sym_table.get(symbol_of(lhs)).name());
413 let rhs_name = sym_table
414 .interner
415 .resolve(sym_table.get(symbol_of(rhs)).name());
416 lhs_name.cmp(rhs_name)
417 });
418 index.clear();
419 for (idx, entry) in entries.iter().enumerate() {
420 index.insert(symbol_of(entry), idx);
421 }
422 }
423
424 fn build_synthetic_reloc_cache(inputs: &[LayoutInput<'_>]) -> Result<ParsedRelocCache, SynthError> {
425 let mut reloc_cache = ParsedRelocCache::new();
426 for input in inputs {
427 for (sect_idx, section) in input.object.sections.iter().enumerate() {
428 let relocs = if section.nreloc == 0 {
429 Vec::new()
430 } else {
431 let raws =
432 parse_raw_relocs(&section.raw_relocs, 0, section.nreloc).map_err(|err| {
433 SynthError {
434 input: input.object.path.clone(),
435 atom: crate::resolve::AtomId(0),
436 reloc_offset: 0,
437 kind: RelocKind::Unsigned,
438 detail: err.to_string(),
439 }
440 })?;
441 parse_relocs(&raws).map_err(|err| SynthError {
442 input: input.object.path.clone(),
443 atom: crate::resolve::AtomId(0),
444 reloc_offset: 0,
445 kind: RelocKind::Unsigned,
446 detail: err.to_string(),
447 })?
448 };
449 reloc_cache.insert((input.id, (sect_idx + 1) as u8), relocs);
450 }
451 }
452 Ok(reloc_cache)
453 }
454
455 fn build_input_symbol_index(
456 inputs: &[LayoutInput<'_>],
457 sym_table: &SymbolTable,
458 parsed_relocs: &ParsedRelocCache,
459 ) -> HashMap<InputId, Vec<Option<SymbolId>>> {
460 let mut referenced = HashMap::<InputId, HashSet<u32>>::new();
461 for ((input_id, _), relocs) in parsed_relocs {
462 let referenced = referenced.entry(*input_id).or_default();
463 for reloc in relocs {
464 if let Referent::Symbol(sym_idx) = reloc.referent {
465 referenced.insert(sym_idx);
466 }
467 if let Some(Referent::Symbol(sym_idx)) = reloc.subtrahend {
468 referenced.insert(sym_idx);
469 }
470 }
471 }
472
473 let mut index = HashMap::new();
474 for input in inputs {
475 let mut symbols = vec![None; input.object.symbols.len()];
476 if let Some(referenced) = referenced.get(&input.id) {
477 for sym_idx in referenced {
478 let Some(input_sym) = input.object.symbols.get(*sym_idx as usize) else {
479 continue;
480 };
481 let Some(slot) = symbols.get_mut(*sym_idx as usize) else {
482 continue;
483 };
484 *slot = input
485 .object
486 .symbol_name(input_sym)
487 .ok()
488 .and_then(|name| sym_table.lookup_str(name));
489 }
490 }
491 index.insert(input.id, symbols);
492 }
493 index
494 }
495
496 fn build_sorted_reloc_index(parsed_relocs: &ParsedRelocCache) -> ParsedRelocCache {
497 let mut index = ParsedRelocCache::new();
498 for (key, relocs) in parsed_relocs {
499 if relocs.is_empty() {
500 continue;
501 }
502 let mut sorted = relocs.clone();
503 sorted.sort_by_key(|reloc| reloc.offset);
504 index.insert(*key, sorted);
505 }
506 index
507 }
508
509 fn relocs_for_atom<'a>(relocs: &'a [Reloc], atom: &Atom) -> impl Iterator<Item = Reloc> + 'a {
510 let start = atom.input_offset;
511 let end = atom.input_offset + atom.size;
512 let first = relocs.partition_point(|reloc| reloc.offset < start);
513 let last = relocs.partition_point(|reloc| reloc.offset < end);
514 relocs[first..last].iter().copied().filter(move |reloc| {
515 let reloc_end = reloc.offset + reloc.width_for_planning();
516 reloc_end <= end
517 })
518 }
519
520 trait RelocPlanningWidth {
521 fn width_for_planning(self) -> u32;
522 }
523
524 impl RelocPlanningWidth for Reloc {
525 fn width_for_planning(self) -> u32 {
526 match self.kind {
527 RelocKind::Subtractor => 8,
528 _ => match self.length {
529 crate::reloc::RelocLength::Byte => 1,
530 crate::reloc::RelocLength::Half => 2,
531 crate::reloc::RelocLength::Word => 4,
532 crate::reloc::RelocLength::Quad => 8,
533 },
534 }
535 }
536 }
537
538 fn dylib_import_referent(
539 obj: &ObjectFile,
540 referent: Referent,
541 sym_table: &SymbolTable,
542 input_symbols: Option<&[Option<SymbolId>]>,
543 ) -> Option<SymbolId> {
544 let symbol_id = symbol_referent_id(obj, referent, sym_table, input_symbols)?;
545 matches!(sym_table.get(symbol_id), Symbol::DylibImport { .. }).then_some(symbol_id)
546 }
547
548 fn symbol_referent_id(
549 obj: &ObjectFile,
550 referent: Referent,
551 sym_table: &SymbolTable,
552 input_symbols: Option<&[Option<SymbolId>]>,
553 ) -> Option<SymbolId> {
554 let Referent::Symbol(sym_idx) = referent else {
555 return None;
556 };
557 if let Some(symbols) = input_symbols {
558 return symbols.get(sym_idx as usize).copied().flatten();
559 }
560 let input_sym = obj.symbols.get(sym_idx as usize)?;
561 let name = obj.symbol_name(input_sym).ok()?;
562 let (symbol_id, _) = sym_table
563 .iter()
564 .find(|(_, symbol)| sym_table.interner.resolve(symbol.name()) == name)?;
565 Some(symbol_id)
566 }
567
568 fn dylib_import_is_weak(sym_table: &SymbolTable, symbol_id: SymbolId) -> bool {
569 matches!(
570 sym_table.get(symbol_id),
571 Symbol::DylibImport {
572 weak_import: true,
573 ..
574 }
575 )
576 }
577
578 fn tlv_symbol_needs_thread_pointer(sym_table: &SymbolTable, symbol_id: SymbolId) -> bool {
579 matches!(
580 sym_table.get(symbol_id),
581 Symbol::LazyArchive { .. } | Symbol::LazyObject { .. }
582 )
583 }
584
585 fn tlv_symbol_needs_got(sym_table: &SymbolTable, symbol_id: SymbolId) -> bool {
586 matches!(sym_table.get(symbol_id), Symbol::DylibImport { .. })
587 }
588
589 fn got_page_symbol_needs_slot(
590 sym_table: &SymbolTable,
591 atoms: &AtomTable,
592 symbol_id: SymbolId,
593 ) -> bool {
594 match sym_table.get(symbol_id) {
595 Symbol::DylibImport { .. } => true,
596 Symbol::Defined {
597 atom,
598 private_extern,
599 ..
600 } => {
601 atom.0 != 0 && !*private_extern && matches!(atoms.get(*atom).section, AtomSection::Data)
602 }
603 _ => false,
604 }
605 }
606
607 fn direct_import_bind_supported(reloc: Reloc) -> bool {
608 matches!(reloc.length, RelocLength::Quad) && !reloc.pcrel && reloc.subtrahend.is_none()
609 }
610
611 fn inputs_have_tlv_descriptors(inputs: &[LayoutInput<'_>]) -> bool {
612 inputs.iter().any(|input| {
613 input
614 .object
615 .sections
616 .iter()
617 .any(|section| section.kind == SectionKind::ThreadLocalVariables)
618 })
619 }
620
621 fn ensure_stub_helper_support(
622 sym_table: &mut SymbolTable,
623 dylibs: &[DylibInput],
624 got: &mut GotSection,
625 ) -> Result<SymbolId, SynthError> {
626 let (libsystem_id, libsystem) = find_libsystem_input(dylibs).ok_or_else(|| SynthError {
627 input: PathBuf::from("<synthetic stubs>"),
628 atom: crate::resolve::AtomId(0),
629 reloc_offset: 0,
630 kind: RelocKind::Branch26,
631 detail: "stub helper requires a libSystem dylib/TBD input for `dyld_stub_binder`"
632 .to_string(),
633 })?;
634
635 let name = sym_table.intern("dyld_stub_binder");
636 let symbol_id = if let Some(id) = sym_table.lookup(name) {
637 match sym_table.get(id) {
638 Symbol::DylibImport { .. } => id,
639 other => {
640 return Err(SynthError {
641 input: PathBuf::from("<synthetic stubs>"),
642 atom: crate::resolve::AtomId(0),
643 reloc_offset: 0,
644 kind: RelocKind::Branch26,
645 detail: format!(
646 "`dyld_stub_binder` already exists as unsupported symbol kind {:?}",
647 other.kind()
648 ),
649 });
650 }
651 }
652 } else {
653 match sym_table
654 .insert(Symbol::DylibImport {
655 name,
656 dylib: DylibId(libsystem_id as u32),
657 ordinal: libsystem.ordinal,
658 weak_import: false,
659 })
660 .map_err(|err| SynthError {
661 input: PathBuf::from("<synthetic stubs>"),
662 atom: crate::resolve::AtomId(0),
663 reloc_offset: 0,
664 kind: RelocKind::Branch26,
665 detail: format!("{err:?}"),
666 })? {
667 InsertOutcome::Inserted(id)
668 | InsertOutcome::Kept(id)
669 | InsertOutcome::PendingArchiveFetch { id, .. }
670 | InsertOutcome::PendingObjectLoad { id, .. }
671 | InsertOutcome::CommonCoalesced { id }
672 | InsertOutcome::Replaced { id, .. } => id,
673 }
674 };
675
676 got.intern(symbol_id, false);
677 Ok(symbol_id)
678 }
679
680 fn ensure_tlv_support(
681 sym_table: &mut SymbolTable,
682 dylibs: &[DylibInput],
683 ) -> Result<Option<SymbolId>, SynthError> {
684 let Some((libsystem_id, libsystem)) = find_libsystem_input(dylibs) else {
685 return Ok(None);
686 };
687
688 let name = sym_table.intern("__tlv_bootstrap");
689 let symbol_id = if let Some(id) = sym_table.lookup(name) {
690 match sym_table.get(id) {
691 Symbol::DylibImport { .. } => id,
692 other => {
693 return Err(SynthError {
694 input: PathBuf::from("<synthetic tlv>"),
695 atom: crate::resolve::AtomId(0),
696 reloc_offset: 0,
697 kind: RelocKind::TlvpLoadPage21,
698 detail: format!(
699 "`__tlv_bootstrap` already exists as unsupported symbol kind {:?}",
700 other.kind()
701 ),
702 });
703 }
704 }
705 } else {
706 match sym_table
707 .insert(Symbol::DylibImport {
708 name,
709 dylib: DylibId(libsystem_id as u32),
710 ordinal: libsystem.ordinal,
711 weak_import: false,
712 })
713 .map_err(|err| SynthError {
714 input: PathBuf::from("<synthetic tlv>"),
715 atom: crate::resolve::AtomId(0),
716 reloc_offset: 0,
717 kind: RelocKind::TlvpLoadPage21,
718 detail: format!("{err:?}"),
719 })? {
720 InsertOutcome::Inserted(id)
721 | InsertOutcome::Kept(id)
722 | InsertOutcome::PendingArchiveFetch { id, .. }
723 | InsertOutcome::PendingObjectLoad { id, .. }
724 | InsertOutcome::CommonCoalesced { id }
725 | InsertOutcome::Replaced { id, .. } => id,
726 }
727 };
728
729 Ok(Some(symbol_id))
730 }
731
732 fn find_libsystem_input(dylibs: &[DylibInput]) -> Option<(usize, &DylibInput)> {
733 dylibs
734 .iter()
735 .enumerate()
736 .find(|(_, dylib)| dylib.load_install_name == "/usr/lib/libSystem.B.dylib")
737 .or_else(|| {
738 dylibs
739 .iter()
740 .enumerate()
741 .find(|(_, dylib)| dylib.load_install_name.contains("libSystem"))
742 })
743 .or_else(|| {
744 dylibs
745 .iter()
746 .enumerate()
747 .find(|(_, dylib)| dylib.path.to_string_lossy().contains("libSystem.tbd"))
748 })
749 }
750
751 #[cfg(test)]
752 mod tests {
753 use std::path::PathBuf;
754
755 use crate::atom::{Atom, AtomFlags, AtomSection, AtomTable};
756 use crate::input::ObjectFile;
757 use crate::layout::LayoutInput;
758 use crate::macho::constants::{
759 CPU_SUBTYPE_ARM64_ALL, CPU_TYPE_ARM64, MH_DYLIB, MH_MAGIC_64, MH_OBJECT, N_EXT, N_UNDF,
760 S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS, S_REGULAR,
761 };
762 use crate::macho::dylib::DylibFile;
763 use crate::macho::exports::Exports;
764 use crate::macho::reader::{LoadCommand, MachHeader64};
765 use crate::reloc::{write_raw_relocs, write_relocs, Referent, Reloc, RelocKind, RelocLength};
766 use crate::resolve::{DylibId, DylibInput, InputId, Symbol, SymbolTable};
767 use crate::section::{InputSection, SectionKind};
768 use crate::string_table::StringTable;
769 use crate::symbol::{InputSymbol, RawNlist};
770
771 use super::*;
772
773 #[test]
774 fn synthetic_plan_collects_got_and_stub_needs_for_imports() {
775 let mut sym_table = SymbolTable::new();
776 let name = sym_table.intern("_printf");
777 let input_id = InputId(0);
778 sym_table
779 .insert(Symbol::DylibImport {
780 name,
781 dylib: DylibId(2),
782 ordinal: 3,
783 weak_import: false,
784 })
785 .unwrap();
786
787 let relocs = vec![
788 Reloc {
789 offset: 0,
790 kind: RelocKind::Branch26,
791 length: RelocLength::Word,
792 pcrel: true,
793 referent: Referent::Symbol(0),
794 addend: 0,
795 subtrahend: None,
796 },
797 Reloc {
798 offset: 4,
799 kind: RelocKind::GotLoadPage21,
800 length: RelocLength::Word,
801 pcrel: true,
802 referent: Referent::Symbol(0),
803 addend: 0,
804 subtrahend: None,
805 },
806 Reloc {
807 offset: 8,
808 kind: RelocKind::GotLoadPageOff12,
809 length: RelocLength::Word,
810 pcrel: false,
811 referent: Referent::Symbol(0),
812 addend: 0,
813 subtrahend: None,
814 },
815 ];
816 let raw_relocs = encode_raw_relocs(&relocs);
817 let object = synth_object("_printf", raw_relocs);
818
819 let mut atoms = AtomTable::new();
820 atoms.push(Atom {
821 id: crate::resolve::AtomId(0),
822 origin: input_id,
823 input_section: 1,
824 section: AtomSection::Text,
825 input_offset: 0,
826 size: 12,
827 align_pow2: 2,
828 owner: None,
829 alt_entries: Vec::new(),
830 data: vec![0; 12],
831 flags: AtomFlags::default().with(AtomFlags::PURE_INSTRUCTIONS),
832 parent_of: None,
833 });
834 let dylibs = vec![libsystem_input()];
835
836 let plan = SyntheticPlan::build(
837 &[LayoutInput {
838 id: input_id,
839 object: &object,
840 load_order: 0,
841 archive_member_offset: None,
842 }],
843 &atoms,
844 &mut sym_table,
845 &dylibs,
846 )
847 .unwrap();
848
849 assert_eq!(plan.got.entries.len(), 2);
850 assert_eq!(plan.stubs.entries.len(), 1);
851 assert_eq!(plan.lazy_pointers.entries.len(), 1);
852 assert!(plan.thread_pointers.entries.is_empty());
853 assert!(plan.direct_binds.is_empty());
854 assert!(plan.binder_symbol.is_some());
855 assert!(plan.tlv_bootstrap_symbol.is_none());
856 assert!(plan.needs_dyld_private);
857 assert_eq!(plan.stubs.entries[0].dylib, DylibId(2));
858 assert_eq!(
859 plan.lazy_pointers.entries[0].symbol,
860 plan.stubs.entries[0].symbol
861 );
862 }
863
864 #[test]
865 fn synthetic_plan_ignores_defined_targets() {
866 let mut sym_table = SymbolTable::new();
867 let name = sym_table.intern("_local");
868 let input_id = InputId(0);
869 sym_table
870 .insert(Symbol::Defined {
871 name,
872 origin: input_id,
873 atom: crate::resolve::AtomId(1),
874 value: 0,
875 weak: false,
876 private_extern: false,
877 no_dead_strip: false,
878 })
879 .unwrap();
880
881 let relocs = vec![Reloc {
882 offset: 0,
883 kind: RelocKind::Branch26,
884 length: RelocLength::Word,
885 pcrel: true,
886 referent: Referent::Symbol(0),
887 addend: 0,
888 subtrahend: None,
889 }];
890 let object = synth_object("_local", encode_raw_relocs(&relocs));
891
892 let mut atoms = AtomTable::new();
893 atoms.push(Atom {
894 id: crate::resolve::AtomId(0),
895 origin: input_id,
896 input_section: 1,
897 section: AtomSection::Text,
898 input_offset: 0,
899 size: 4,
900 align_pow2: 2,
901 owner: None,
902 alt_entries: Vec::new(),
903 data: vec![0; 4],
904 flags: AtomFlags::default().with(AtomFlags::PURE_INSTRUCTIONS),
905 parent_of: None,
906 });
907
908 let plan = SyntheticPlan::build(
909 &[LayoutInput {
910 id: input_id,
911 object: &object,
912 load_order: 0,
913 archive_member_offset: None,
914 }],
915 &atoms,
916 &mut sym_table,
917 &[],
918 )
919 .unwrap();
920 assert!(plan.got.entries.is_empty());
921 assert!(plan.stubs.entries.is_empty());
922 assert!(plan.lazy_pointers.entries.is_empty());
923 assert!(plan.thread_pointers.entries.is_empty());
924 assert!(plan.binder_symbol.is_none());
925 assert!(plan.tlv_bootstrap_symbol.is_none());
926 assert!(!plan.needs_dyld_private);
927 }
928
929 #[test]
930 fn synthetic_plan_keeps_local_tlvp_on_descriptors() {
931 let mut sym_table = SymbolTable::new();
932 let name = sym_table.intern("_tlsvar");
933 let input_id = InputId(0);
934 sym_table
935 .insert(Symbol::Defined {
936 name,
937 origin: input_id,
938 atom: crate::resolve::AtomId(1),
939 value: 0,
940 weak: false,
941 private_extern: false,
942 no_dead_strip: false,
943 })
944 .unwrap();
945
946 let relocs = vec![
947 Reloc {
948 offset: 0,
949 kind: RelocKind::TlvpLoadPage21,
950 length: RelocLength::Word,
951 pcrel: true,
952 referent: Referent::Symbol(0),
953 addend: 0,
954 subtrahend: None,
955 },
956 Reloc {
957 offset: 4,
958 kind: RelocKind::TlvpLoadPageOff12,
959 length: RelocLength::Word,
960 pcrel: false,
961 referent: Referent::Symbol(0),
962 addend: 0,
963 subtrahend: None,
964 },
965 ];
966 let object = tlvp_object("_tlsvar", encode_raw_relocs(&relocs));
967
968 let mut atoms = AtomTable::new();
969 atoms.push(Atom {
970 id: crate::resolve::AtomId(0),
971 origin: input_id,
972 input_section: 1,
973 section: AtomSection::Text,
974 input_offset: 0,
975 size: 8,
976 align_pow2: 2,
977 owner: None,
978 alt_entries: Vec::new(),
979 data: vec![0; 8],
980 flags: AtomFlags::default().with(AtomFlags::PURE_INSTRUCTIONS),
981 parent_of: None,
982 });
983
984 let plan = SyntheticPlan::build(
985 &[LayoutInput {
986 id: input_id,
987 object: &object,
988 load_order: 0,
989 archive_member_offset: None,
990 }],
991 &atoms,
992 &mut sym_table,
993 &[libsystem_input()],
994 )
995 .unwrap();
996
997 assert!(plan.thread_pointers.entries.is_empty());
998 assert!(plan.binder_symbol.is_none());
999 assert!(plan.tlv_bootstrap_symbol.is_some());
1000
1001 let sections = plan.output_sections();
1002 assert!(sections
1003 .iter()
1004 .all(|section| !(section.segment == "__DATA" && section.name == "__thread_ptrs")));
1005 }
1006
1007 #[test]
1008 fn synthetic_plan_routes_imported_tlvp_through_got() {
1009 let mut sym_table = SymbolTable::new();
1010 let name = sym_table.intern("_ext_tls");
1011 let input_id = InputId(0);
1012 let import = match sym_table
1013 .insert(Symbol::DylibImport {
1014 name,
1015 dylib: DylibId(0),
1016 ordinal: 2,
1017 weak_import: false,
1018 })
1019 .unwrap()
1020 {
1021 crate::resolve::InsertOutcome::Inserted(id) => id,
1022 other => panic!("unexpected insert outcome: {other:?}"),
1023 };
1024
1025 let relocs = vec![
1026 Reloc {
1027 offset: 0,
1028 kind: RelocKind::TlvpLoadPage21,
1029 length: RelocLength::Word,
1030 pcrel: true,
1031 referent: Referent::Symbol(0),
1032 addend: 0,
1033 subtrahend: None,
1034 },
1035 Reloc {
1036 offset: 4,
1037 kind: RelocKind::TlvpLoadPageOff12,
1038 length: RelocLength::Word,
1039 pcrel: false,
1040 referent: Referent::Symbol(0),
1041 addend: 0,
1042 subtrahend: None,
1043 },
1044 ];
1045 let object = synth_object("_ext_tls", encode_raw_relocs(&relocs));
1046
1047 let mut atoms = AtomTable::new();
1048 atoms.push(Atom {
1049 id: crate::resolve::AtomId(0),
1050 origin: input_id,
1051 input_section: 1,
1052 section: AtomSection::Text,
1053 input_offset: 0,
1054 size: 12,
1055 align_pow2: 2,
1056 owner: None,
1057 alt_entries: Vec::new(),
1058 data: vec![0; 12],
1059 flags: AtomFlags::default().with(AtomFlags::PURE_INSTRUCTIONS),
1060 parent_of: None,
1061 });
1062
1063 let plan = SyntheticPlan::build(
1064 &[LayoutInput {
1065 id: input_id,
1066 object: &object,
1067 load_order: 0,
1068 archive_member_offset: None,
1069 }],
1070 &atoms,
1071 &mut sym_table,
1072 &[libsystem_input()],
1073 )
1074 .unwrap();
1075
1076 assert_eq!(plan.got.entries.len(), 1);
1077 assert_eq!(plan.got.entries[0].symbol, import);
1078 assert!(plan.thread_pointers.entries.is_empty());
1079 assert!(plan.direct_binds.is_empty());
1080 assert!(plan.tlv_bootstrap_symbol.is_none());
1081 }
1082
1083 #[test]
1084 fn synthetic_plan_collects_direct_import_bind_sites() {
1085 let mut sym_table = SymbolTable::new();
1086 let name = sym_table.intern("_ext_data");
1087 let input_id = InputId(0);
1088 let import = match sym_table
1089 .insert(Symbol::DylibImport {
1090 name,
1091 dylib: DylibId(0),
1092 ordinal: 2,
1093 weak_import: false,
1094 })
1095 .unwrap()
1096 {
1097 crate::resolve::InsertOutcome::Inserted(id) => id,
1098 other => panic!("unexpected insert outcome: {other:?}"),
1099 };
1100
1101 let relocs = vec![Reloc {
1102 offset: 0,
1103 kind: RelocKind::Unsigned,
1104 length: RelocLength::Quad,
1105 pcrel: false,
1106 referent: Referent::Symbol(0),
1107 addend: 0,
1108 subtrahend: None,
1109 }];
1110 let object = synth_object("_ext_data", encode_raw_relocs(&relocs));
1111
1112 let mut atoms = AtomTable::new();
1113 let atom_id = atoms.push(Atom {
1114 id: crate::resolve::AtomId(0),
1115 origin: input_id,
1116 input_section: 1,
1117 section: AtomSection::Data,
1118 input_offset: 0,
1119 size: 8,
1120 align_pow2: 3,
1121 owner: None,
1122 alt_entries: Vec::new(),
1123 data: vec![0; 8],
1124 flags: AtomFlags::default(),
1125 parent_of: None,
1126 });
1127
1128 let plan = SyntheticPlan::build(
1129 &[LayoutInput {
1130 id: input_id,
1131 object: &object,
1132 load_order: 0,
1133 archive_member_offset: None,
1134 }],
1135 &atoms,
1136 &mut sym_table,
1137 &[libsystem_input()],
1138 )
1139 .unwrap();
1140
1141 assert!(plan.got.entries.is_empty());
1142 assert_eq!(plan.direct_binds.len(), 1);
1143 assert_eq!(plan.direct_binds[0].atom, atom_id);
1144 assert_eq!(plan.direct_binds[0].atom_offset, 0);
1145 assert_eq!(plan.direct_binds[0].symbol, import);
1146 }
1147
1148 #[test]
1149 fn synthetic_plan_collects_got_for_compact_unwind_personality_import() {
1150 let mut sym_table = SymbolTable::new();
1151 let name = sym_table.intern("___gxx_personality_v0");
1152 let input_id = InputId(0);
1153 let import = match sym_table
1154 .insert(Symbol::DylibImport {
1155 name,
1156 dylib: DylibId(0),
1157 ordinal: 2,
1158 weak_import: false,
1159 })
1160 .unwrap()
1161 {
1162 crate::resolve::InsertOutcome::Inserted(id) => id,
1163 other => panic!("unexpected insert outcome: {other:?}"),
1164 };
1165
1166 let relocs = vec![Reloc {
1167 offset: 16,
1168 kind: RelocKind::Unsigned,
1169 length: RelocLength::Quad,
1170 pcrel: false,
1171 referent: Referent::Symbol(0),
1172 addend: 0,
1173 subtrahend: None,
1174 }];
1175 let object = compact_unwind_object("___gxx_personality_v0", encode_raw_relocs(&relocs));
1176
1177 let mut atoms = AtomTable::new();
1178 atoms.push(Atom {
1179 id: crate::resolve::AtomId(0),
1180 origin: input_id,
1181 input_section: 1,
1182 section: AtomSection::CompactUnwind,
1183 input_offset: 0,
1184 size: 32,
1185 align_pow2: 3,
1186 owner: None,
1187 alt_entries: Vec::new(),
1188 data: vec![0; 32],
1189 flags: AtomFlags::default(),
1190 parent_of: None,
1191 });
1192
1193 let plan = SyntheticPlan::build(
1194 &[LayoutInput {
1195 id: input_id,
1196 object: &object,
1197 load_order: 0,
1198 archive_member_offset: None,
1199 }],
1200 &atoms,
1201 &mut sym_table,
1202 &[libsystem_input()],
1203 )
1204 .unwrap();
1205
1206 assert_eq!(plan.got.entries.len(), 1);
1207 assert_eq!(plan.got.entries[0].symbol, import);
1208 assert!(plan.stubs.entries.is_empty());
1209 assert!(plan.lazy_pointers.entries.is_empty());
1210 }
1211
1212 fn libsystem_input() -> DylibInput {
1213 DylibInput {
1214 path: PathBuf::from("/tmp/libSystem.tbd"),
1215 load_install_name: "/usr/lib/libSystem.B.dylib".into(),
1216 load_current_version: 0,
1217 load_compatibility_version: 0,
1218 file: DylibFile {
1219 path: PathBuf::from("/tmp/libSystem.tbd"),
1220 header: MachHeader64 {
1221 magic: MH_MAGIC_64,
1222 cputype: CPU_TYPE_ARM64,
1223 cpusubtype: CPU_SUBTYPE_ARM64_ALL,
1224 filetype: MH_DYLIB,
1225 ncmds: 0,
1226 sizeofcmds: 0,
1227 flags: 0,
1228 reserved: 0,
1229 },
1230 commands: Vec::<LoadCommand>::new(),
1231 install_name: "/usr/lib/libSystem.B.dylib".into(),
1232 current_version: 0,
1233 compatibility_version: 0,
1234 dependencies: Vec::new(),
1235 rpaths: Vec::new(),
1236 symtab: None,
1237 exports: Exports::empty(),
1238 },
1239 ordinal: 1,
1240 }
1241 }
1242
1243 fn encode_raw_relocs(relocs: &[Reloc]) -> Vec<u8> {
1244 let raws = write_relocs(relocs).unwrap();
1245 let mut bytes = Vec::new();
1246 write_raw_relocs(&raws, &mut bytes);
1247 bytes
1248 }
1249
1250 fn synth_object(symbol_name: &str, raw_relocs: Vec<u8>) -> ObjectFile {
1251 let mut strings = vec![0];
1252 let strx = strings.len() as u32;
1253 strings.extend_from_slice(symbol_name.as_bytes());
1254 strings.push(0);
1255 ObjectFile {
1256 path: PathBuf::from("/tmp/synth-got.o"),
1257 header: MachHeader64 {
1258 magic: MH_MAGIC_64,
1259 cputype: CPU_TYPE_ARM64,
1260 cpusubtype: CPU_SUBTYPE_ARM64_ALL,
1261 filetype: MH_OBJECT,
1262 ncmds: 0,
1263 sizeofcmds: 0,
1264 flags: 0,
1265 reserved: 0,
1266 },
1267 commands: Vec::new(),
1268 sections: vec![InputSection {
1269 segname: "__TEXT".into(),
1270 sectname: "__text".into(),
1271 kind: SectionKind::Text,
1272 addr: 0,
1273 size: 12,
1274 align_pow2: 2,
1275 flags: S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS,
1276 offset: 0,
1277 reloff: 0,
1278 nreloc: (raw_relocs.len() / 8) as u32,
1279 reserved1: 0,
1280 reserved2: 0,
1281 reserved3: 0,
1282 data: vec![0; 12],
1283 raw_relocs,
1284 }],
1285 symbols: vec![InputSymbol::from_raw(RawNlist {
1286 strx,
1287 n_type: N_UNDF | N_EXT,
1288 n_sect: 0,
1289 n_desc: 0,
1290 n_value: 0,
1291 })],
1292 strings: StringTable::from_bytes(strings),
1293 symtab: None,
1294 dysymtab: None,
1295 loh: Vec::new(),
1296 data_in_code: Vec::new(),
1297 }
1298 }
1299
1300 fn tlvp_object(symbol_name: &str, raw_relocs: Vec<u8>) -> ObjectFile {
1301 let mut strings = vec![0];
1302 let strx = strings.len() as u32;
1303 strings.extend_from_slice(symbol_name.as_bytes());
1304 strings.push(0);
1305 ObjectFile {
1306 path: PathBuf::from("/tmp/synth-tlv.o"),
1307 header: MachHeader64 {
1308 magic: MH_MAGIC_64,
1309 cputype: CPU_TYPE_ARM64,
1310 cpusubtype: CPU_SUBTYPE_ARM64_ALL,
1311 filetype: MH_OBJECT,
1312 ncmds: 0,
1313 sizeofcmds: 0,
1314 flags: 0,
1315 reserved: 0,
1316 },
1317 commands: Vec::new(),
1318 sections: vec![
1319 InputSection {
1320 segname: "__TEXT".into(),
1321 sectname: "__text".into(),
1322 kind: SectionKind::Text,
1323 addr: 0,
1324 size: 8,
1325 align_pow2: 2,
1326 flags: S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS,
1327 offset: 0,
1328 reloff: 0,
1329 nreloc: (raw_relocs.len() / 8) as u32,
1330 reserved1: 0,
1331 reserved2: 0,
1332 reserved3: 0,
1333 data: vec![0; 8],
1334 raw_relocs,
1335 },
1336 InputSection {
1337 segname: "__DATA".into(),
1338 sectname: "__thread_vars".into(),
1339 kind: SectionKind::ThreadLocalVariables,
1340 addr: 0,
1341 size: 24,
1342 align_pow2: 3,
1343 flags: crate::macho::constants::S_THREAD_LOCAL_VARIABLES,
1344 offset: 0,
1345 reloff: 0,
1346 nreloc: 0,
1347 reserved1: 0,
1348 reserved2: 0,
1349 reserved3: 0,
1350 data: vec![0; 24],
1351 raw_relocs: Vec::new(),
1352 },
1353 InputSection {
1354 segname: "__DATA".into(),
1355 sectname: "__thread_data".into(),
1356 kind: SectionKind::ThreadLocalRegular,
1357 addr: 0,
1358 size: 8,
1359 align_pow2: 3,
1360 flags: crate::macho::constants::S_THREAD_LOCAL_REGULAR,
1361 offset: 0,
1362 reloff: 0,
1363 nreloc: 0,
1364 reserved1: 0,
1365 reserved2: 0,
1366 reserved3: 0,
1367 data: vec![0; 8],
1368 raw_relocs: Vec::new(),
1369 },
1370 ],
1371 symbols: vec![InputSymbol::from_raw(RawNlist {
1372 strx,
1373 n_type: N_EXT | crate::macho::constants::N_SECT,
1374 n_sect: 2,
1375 n_desc: 0,
1376 n_value: 0,
1377 })],
1378 strings: StringTable::from_bytes(strings),
1379 symtab: None,
1380 dysymtab: None,
1381 loh: Vec::new(),
1382 data_in_code: Vec::new(),
1383 }
1384 }
1385
1386 fn compact_unwind_object(symbol_name: &str, raw_relocs: Vec<u8>) -> ObjectFile {
1387 let mut strings = vec![0];
1388 let strx = strings.len() as u32;
1389 strings.extend_from_slice(symbol_name.as_bytes());
1390 strings.push(0);
1391 ObjectFile {
1392 path: PathBuf::from("/tmp/synth-compact-unwind.o"),
1393 header: MachHeader64 {
1394 magic: MH_MAGIC_64,
1395 cputype: CPU_TYPE_ARM64,
1396 cpusubtype: CPU_SUBTYPE_ARM64_ALL,
1397 filetype: MH_OBJECT,
1398 ncmds: 0,
1399 sizeofcmds: 0,
1400 flags: 0,
1401 reserved: 0,
1402 },
1403 commands: Vec::new(),
1404 sections: vec![InputSection {
1405 segname: "__LD".into(),
1406 sectname: "__compact_unwind".into(),
1407 kind: SectionKind::CompactUnwind,
1408 addr: 0,
1409 size: 32,
1410 align_pow2: 3,
1411 flags: crate::macho::constants::S_REGULAR,
1412 offset: 0,
1413 reloff: 0,
1414 nreloc: (raw_relocs.len() / 8) as u32,
1415 reserved1: 0,
1416 reserved2: 0,
1417 reserved3: 0,
1418 data: vec![0; 32],
1419 raw_relocs,
1420 }],
1421 symbols: vec![InputSymbol::from_raw(RawNlist {
1422 strx,
1423 n_type: N_UNDF | N_EXT,
1424 n_sect: 0,
1425 n_desc: 0,
1426 n_value: 0,
1427 })],
1428 strings: StringTable::from_bytes(strings),
1429 symtab: None,
1430 dysymtab: None,
1431 loh: Vec::new(),
1432 data_in_code: Vec::new(),
1433 }
1434 }
1435 }
1436