diff --git a/macho/bundle_hook.rs b/macho/bundle_hook.rs index c15d134c3..b938052e9 100644 --- a/macho/bundle_hook.rs +++ b/macho/bundle_hook.rs @@ -23,10 +23,10 @@ use crate::arch::Target; use crate::cmdline::Args; use crate::context::Context; -use crate::input_files::FileId; +use crate::input_files::{DataField, FileId, add_data_blob}; use crate::macho::*; use crate::mapped_file::MappedFile; -use crate::objc::{DataField, ObjcRef}; +use crate::objc::ObjcRef; use crate::symbol::SymbolId; /// The hook, built by c/build-bundle-hook.sh. @@ -143,8 +143,7 @@ pub fn create_class_table(ctx: &mut Context) { /// struct entry. Returns its subsection. fn add_table(ctx: &mut Context, libraries: &[(Vec, Vec)]) -> u32 { let names = libraries.iter().flat_map(|(name, _)| [&name[..], b"\0"].concat()).collect(); - let names = - crate::objc::add_data_blob(ctx, b"__data", S_REGULAR, vec![DataField::Bytes(names)]); + let names = add_data_blob(ctx, b"__data", S_REGULAR, vec![DataField::Bytes(names)]); let count: usize = libraries.iter().map(|(_, classes)| classes.len()).sum(); let mut fields = vec![DataField::Bytes((count as u64).to_le_bytes().to_vec())]; let mut name_off = 0; @@ -155,7 +154,7 @@ fn add_table(ctx: &mut Context, libraries: &[(Vec, Vec(ctx: &Context, locs: &[u64]) -> Vec { } /// The (address, target) of every pointer field of the synthesized -/// records (see objc::DataBlob). +/// records (see input_files::DataBlob). fn data_blob_fields(ctx: &Context) -> Vec<(u64, ObjcRef)> { let mut out = Vec::new(); for b in &ctx.data_blobs { diff --git a/macho/chunks/split_info.rs b/macho/chunks/split_info.rs index 8a8b89770..58d066263 100644 --- a/macho/chunks/split_info.rs +++ b/macho/chunks/split_info.rs @@ -23,10 +23,10 @@ use crate::arch::{SplitRef, Target}; use crate::chunks::init_offsets::InitFunc; use crate::chunks::{ChunkHeader, ChunkId, delay_init, objc_stubs, stub_helper, stubs}; use crate::context::Context; -use crate::input_files::FileId; +use crate::input_files::{DataField, FileId}; use crate::input_sections::{InputSection, Reloc, RelocTarget}; use crate::macho_consts::*; -use crate::objc::{DataField, ObjcRef}; +use crate::objc::ObjcRef; use crate::symbol::SymbolId; use crate::util::encode_uleb; diff --git a/macho/chunks/stub_helper.rs b/macho/chunks/stub_helper.rs index 6aa7665af..735946878 100644 --- a/macho/chunks/stub_helper.rs +++ b/macho/chunks/stub_helper.rs @@ -6,6 +6,7 @@ use crate::arch::Target; use crate::chunks::ChunkHeader; use crate::context::Context; +use crate::input_files::add_data_word; use crate::macho::*; use crate::symbol::SymbolId; @@ -74,7 +75,7 @@ pub fn ensure_stub_binder(ctx: &mut Context) { ctx.symbols[id].set_used(true); crate::chunks::got::add_got_symbol(ctx, id); ctx.stub_helper.dyld_stub_binder = Some(id); - let isec = ctx.add_data_word(8); + let isec = add_data_word(ctx, 8); ctx.stub_helper.dyld_private_isec = isec; ctx.extra_local_syms.push((b"__dyld_private", isec)); } diff --git a/macho/context.rs b/macho/context.rs index 77ca95482..d4484bef0 100644 --- a/macho/context.rs +++ b/macho/context.rs @@ -40,9 +40,6 @@ use crate::chunks::{ }; use crate::cmdline::Args; use crate::input_files::{DylibFile, FileId, ObjectFile}; -use crate::input_sections::InputSection; -use crate::macho::{MachSection, bytes_to_name}; -use crate::objc::{DataBlob, DataField}; use crate::symbol::{SymbolId, SymbolTable}; use crate::util::perf::Timers; @@ -204,7 +201,7 @@ pub struct Context { /// Objective-C data records the linker synthesized (see /// merge_objc_categories) and the table of bundle_hook, each placed /// as the tail of the output section it names. - pub data_blobs: Vec, + pub data_blobs: Vec, /// Local symbols the linker names itself, on synthesized data: /// ld64's __OBJC_$_INSTANCE_METHODS_Foo(A|B) on a merged method /// list, and the like. (name, subsection). @@ -437,37 +434,6 @@ impl Context { self.args.dead_strip || (!self.args.relocatable && !self.lto_inputs.is_empty()) } - /// Adds a section the linker synthesizes to the internal object, - /// returning the (file, shndx) pair a subsection standing for it - /// carries. - pub fn add_synthetic_section(&mut self, hdr: crate::macho::MachSection) -> (u32, u32) { - let file = self.internal_obj.expect("internal object not created yet"); - let hdrs = self.objs[file].sect_hdrs.to_mut(); - hdrs.push(hdr); - (file as u32, (hdrs.len() - 1) as u32) - } - - /// Synthesizes a zero word of `size` bytes, aligned to its size, in - /// __DATA,__data (after the inputs'), and returns its subsection. - pub fn add_data_word(&mut self, size: u32) -> u32 { - let p2align = size.trailing_zeros() as u8; - let (file, shndx) = self.add_synthetic_section(MachSection { - sectname: bytes_to_name(b"__data"), - segname: bytes_to_name(b"__DATA"), - p2align: p2align as u32, - flags: 0, - ..Default::default() - }); - self.isecs.push(InputSection { - flags: InputSection::flags_placed(), - ..InputSection::new(file, shndx, p2align, size, &[]) - }); - let isec = (self.isecs.len() - 1) as u32; - let fields = vec![DataField::Bytes(vec![0; size as usize])]; - self.data_blobs.push(DataBlob { sect: b"__data", isec, fields }); - isec - } - /// Binds a symbol the linker's own code calls (dyld_stub_binder, /// __dyld_lazy_load), unless something in the link defines it, to /// the first loaded dylib that exports it - or, if none does and diff --git a/macho/delay_init.rs b/macho/delay_init.rs index dbc828f74..84d2927b7 100644 --- a/macho/delay_init.rs +++ b/macho/delay_init.rs @@ -10,9 +10,8 @@ use crate::arch::{LazyRef, Target}; use crate::chunks::delay_init::{DelayHelper, DelayStub, DelayUse, DlopenHelper}; use crate::context::Context; -use crate::input_files::FileId; +use crate::input_files::{FileId, add_cstring, add_data_word}; use crate::lazy_load::{LazyUseSite, import_uses, load_helper_name}; -use crate::macho::*; use crate::symbol::SymbolId; use crate::util::leak_bytes; @@ -96,7 +95,7 @@ fn create_dlopen_helpers( let leaf = install_name.rsplit(|&c| c == b'/').next().unwrap_or(&install_name); let name = leak_bytes([b"_dlopenHelper$", leaf].concat()); let flag_name = leak_bytes([b"_dlopenHelperFlag$", leaf].concat()); - let flag = ctx.add_data_word(4); + let flag = add_data_word(ctx, 4); ctx.extra_local_syms.push((flag_name, flag)); let string = add_cstring(ctx, &install_name); dlopen_of.insert(install_name.clone(), i as u32); @@ -107,26 +106,6 @@ fn create_dlopen_helpers( dlopen_of } -/// Synthesizes a C string in __TEXT,__cstring, after the inputs', and -/// returns its subsection. -fn add_cstring(ctx: &mut Context, s: &[u8]) -> u32 { - let (file, shndx) = ctx.add_synthetic_section(MachSection { - sectname: bytes_to_name(b"__cstring"), - segname: bytes_to_name(b"__TEXT"), - flags: S_CSTRING_LITERALS, - ..Default::default() - }); - let mut bytes = s.to_vec(); - bytes.push(0); - let bytes: &'static [u8] = Vec::leak(bytes); - use crate::input_sections::InputSection; - ctx.isecs.push(InputSection { - flags: InputSection::flags_alive_no_modulus(), - ..InputSection::new(file, shndx, 0, bytes.len() as u32, bytes) - }); - (ctx.isecs.len() - 1) as u32 -} - /// Makes a stub for each symbol something calls, by name, which jumps /// through the symbol's __got slot. fn create_delay_stubs( diff --git a/macho/dtrace.rs b/macho/dtrace.rs index 68dbbad63..a42a4510a 100644 --- a/macho/dtrace.rs +++ b/macho/dtrace.rs @@ -30,6 +30,7 @@ use rayon::prelude::*; use crate::arch::Target; use crate::context::Context; use crate::error::raw; +use crate::input_files::add_synthetic_section; use crate::input_sections::{InputSection, Reloc, RelocTarget}; use crate::macho::*; use crate::symbol::SymbolId; @@ -238,12 +239,13 @@ fn section_name(provider: &[u8], taken: &[Vec]) -> Vec { fn add_dof_section(ctx: &mut Context, name: &[u8], dof: Dof, sites: &[&Site]) { let mut sectname = [0; 16]; sectname[..name.len()].copy_from_slice(name); - let (file, shndx) = ctx.add_synthetic_section(MachSection { + let hdr = MachSection { sectname, segname: bytes_to_name(b"__TEXT"), flags: S_DTRACE_DOF, ..Default::default() - }); + }; + let (file, shndx) = add_synthetic_section(ctx, hdr); let id = ctx.isecs.len() as u32; let relocs = &mut ctx.objs[file as usize].relocs; let rel_offset = relocs.len() as u32; diff --git a/macho/input_files.rs b/macho/input_files.rs index a0b0dfb37..4ea07d1c9 100644 --- a/macho/input_files.rs +++ b/macho/input_files.rs @@ -19,6 +19,7 @@ use crate::input_sections::{ }; use crate::macho::*; use crate::mapped_file::{MappedFile, unreadable_file}; +use crate::objc::ObjcRef; use crate::symbol::{Symbol, SymbolId}; use crate::tapi; use crate::tapi::{LdDirectives, LdSymbols, MovedExport, interpret_ld_symbols}; @@ -297,6 +298,132 @@ impl ObjectFile { } } +/// Adds a section the linker synthesizes to the internal object, +/// returning the (file, shndx) pair a subsection standing for it +/// carries. +pub fn add_synthetic_section(ctx: &mut Context, hdr: MachSection) -> (u32, u32) { + let file = ctx.internal_obj.expect("internal object not created yet"); + let hdrs = ctx.objs[file].sect_hdrs.to_mut(); + hdrs.push(hdr); + (file as u32, (hdrs.len() - 1) as u32) +} + +/// Appends a live synthetic subsection of `sect`, a section of the +/// internal object as add_synthetic_section returns it, and returns +/// it. Its output section and offset are set by hand (IS_PLACED), not +/// by create_output_sections; `offset` is its offset if already known. +pub(crate) fn add_placed_isec( + ctx: &mut Context, + sect: (u32, u32), + p2align: u8, + size: u64, + offset: u64, +) -> u32 { + let (file, shndx) = sect; + ctx.isecs.push(InputSection { + offset: offset as u32, + flags: InputSection::flags_placed(), + ..InputSection::new(file, shndx, p2align, size as u32, &[]) + }); + (ctx.isecs.len() - 1) as u32 +} + +/// A field of a synthesized data record. +#[derive(Clone, Debug)] +pub enum DataField { + Bytes(Vec), + /// An 8-byte pointer, rebased at load (or null), or bound if to an + /// import. + Ptr(ObjcRef), +} + +/// A synthesized data record (an Objective-C one, or the table of +/// bundle_hook), placed in the tail of the output section `sect` +/// (mapped to its segment like an input section of that name) as the +/// synthetic subsection `isec`. +#[derive(Debug)] +pub struct DataBlob { + pub sect: &'static [u8], + pub isec: u32, + pub fields: Vec, +} + +impl DataBlob { + pub fn size(&self) -> u64 { + self.fields + .iter() + .map(|f| match f { + DataField::Bytes(b) => b.len() as u64, + DataField::Ptr(_) => 8, + }) + .sum() + } +} + +/// Appends a synthesized record to the tail of __DATA,`sect` (a section +/// with the given flags) and returns its subsection. +pub(crate) fn add_data_blob( + ctx: &mut Context, + sect: &'static [u8], + flags: u32, + fields: Vec, +) -> u32 { + let hdr = MachSection { + sectname: bytes_to_name(sect), + segname: bytes_to_name(b"__DATA"), + p2align: 3, + flags, + ..Default::default() + }; + let (file, shndx) = add_synthetic_section(ctx, hdr); + let blob = DataBlob { sect, isec: 0, fields }; + let isec = add_placed_isec(ctx, (file, shndx), 3, blob.size(), 0); + ctx.data_blobs.push(DataBlob { isec, ..blob }); + isec +} + +/// Synthesizes a zero word of `size` bytes, aligned to its size, in +/// __DATA,__data (after the inputs'), and returns its subsection. +pub fn add_data_word(ctx: &mut Context, size: u32) -> u32 { + let p2align = size.trailing_zeros() as u8; + let hdr = MachSection { + sectname: bytes_to_name(b"__data"), + segname: bytes_to_name(b"__DATA"), + p2align: p2align as u32, + flags: 0, + ..Default::default() + }; + let (file, shndx) = add_synthetic_section(ctx, hdr); + ctx.isecs.push(InputSection { + flags: InputSection::flags_placed(), + ..InputSection::new(file, shndx, p2align, size, &[]) + }); + let isec = (ctx.isecs.len() - 1) as u32; + let fields = vec![DataField::Bytes(vec![0; size as usize])]; + ctx.data_blobs.push(DataBlob { sect: b"__data", isec, fields }); + isec +} + +/// Synthesizes a C string in __TEXT,__cstring, after the inputs', and +/// returns its subsection. +pub(crate) fn add_cstring(ctx: &mut Context, s: &[u8]) -> u32 { + let hdr = MachSection { + sectname: bytes_to_name(b"__cstring"), + segname: bytes_to_name(b"__TEXT"), + flags: S_CSTRING_LITERALS, + ..Default::default() + }; + let (file, shndx) = add_synthetic_section(ctx, hdr); + let mut bytes = s.to_vec(); + bytes.push(0); + let bytes: &'static [u8] = Vec::leak(bytes); + ctx.isecs.push(InputSection { + flags: InputSection::flags_alive_no_modulus(), + ..InputSection::new(file, shndx, 0, bytes.len() as u32, bytes) + }); + (ctx.isecs.len() - 1) as u32 +} + /// Whether a label is one a compiler or assembler makes for itself: an /// assembler temporary (L...) or a linker-private label (l...) - the /// compiler's lCPI0_0 constant-pool and l_.str string labels, the arm64 diff --git a/macho/lazy_load.rs b/macho/lazy_load.rs index d7573da53..833b4ee6f 100644 --- a/macho/lazy_load.rs +++ b/macho/lazy_load.rs @@ -13,7 +13,7 @@ use crate::chunks::lazy_helpers::{LazyHelper, LazyUse}; use crate::chunks::lazy_load_info::{LazyDylib, record_size}; use crate::context::Context; use crate::error::raw; -use crate::input_files::FileId; +use crate::input_files::{FileId, add_data_word}; use crate::symbol::SymbolId; use crate::util::leak_bytes; @@ -168,7 +168,7 @@ fn create_lazy_load_slots( let mut slots = Vec::new(); let mut offset = 0; for d in used { - let flag = ctx.add_data_word(4); + let flag = add_data_word(ctx, 4); let install_name = &ctx.dylibs[d].install_name; let leaf = install_name.rsplit(|&c| c == b'/').next().unwrap_or(install_name); let name = leak_bytes([b"_lazyLoadFlag$", leaf].concat()); diff --git a/macho/make_mergeable/objc.rs b/macho/make_mergeable/objc.rs index 65c21b560..c9e764774 100644 --- a/macho/make_mergeable/objc.rs +++ b/macho/make_mergeable/objc.rs @@ -9,8 +9,9 @@ use hashbrown::HashSet; use super::{Builder, Content, OutEntry, OutFixup, To, record_size}; use crate::arch::Target; +use crate::input_files::DataField; use crate::mergeable::{ctype, fk, kind, scope, standard_content_type}; -use crate::objc::{DataField, ObjcRef}; +use crate::objc::ObjcRef; impl Builder<'_, E> { /// The entries of the metadata the link made: the selector diff --git a/macho/objc.rs b/macho/objc.rs index dba2196dc..60fdc8125 100644 --- a/macho/objc.rs +++ b/macho/objc.rs @@ -23,7 +23,10 @@ use rayon::prelude::*; use crate::arch::Target; use crate::context::Context; -use crate::input_files::{FileId, redirect_symbols_to_replacements}; +use crate::input_files::{ + DataField, FileId, add_data_blob, add_placed_isec, add_synthetic_section, + redirect_symbols_to_replacements, +}; use crate::input_sections::{InputSection, Reloc, RelocTarget}; use crate::macho::*; use crate::symbol::{NEEDS_GOT, NEEDS_STUB}; @@ -60,15 +63,6 @@ pub struct ObjcMethList { pub methods: Vec, } -/// A field of a synthesized Objective-C data record. -#[derive(Clone, Debug)] -pub enum DataField { - Bytes(Vec), - /// An 8-byte pointer, rebased at load (or null), or bound if to an - /// import. - Ptr(ObjcRef), -} - impl ObjcRef { /// The import a reference is to, which dyld binds. pub fn import(self, ctx: &Context) -> Option { @@ -90,80 +84,17 @@ impl ObjcRef { } } -/// A synthesized data record (an Objective-C one, or the table of -/// bundle_hook), placed in the tail of the output section `sect` -/// (mapped to its segment like an input section of that name) as the -/// synthetic subsection `isec`. -#[derive(Debug)] -pub struct DataBlob { - pub sect: &'static [u8], - pub isec: u32, - pub fields: Vec, -} - -impl DataBlob { - pub fn size(&self) -> u64 { - self.fields - .iter() - .map(|f| match f { - DataField::Bytes(b) => b.len() as u64, - DataField::Ptr(_) => 8, - }) - .sum() - } -} - -/// Appends a live synthetic subsection of `sect`, a section of the -/// internal object as add_synthetic_section returns it, and returns -/// it. Its output section and offset are set by hand (IS_PLACED), not -/// by create_output_sections; `offset` is its offset if already known. -fn add_placed_isec( - ctx: &mut Context, - sect: (u32, u32), - p2align: u8, - size: u64, - offset: u64, -) -> u32 { - let (file, shndx) = sect; - ctx.isecs.push(InputSection { - offset: offset as u32, - flags: InputSection::flags_placed(), - ..InputSection::new(file, shndx, p2align, size as u32, &[]) - }); - (ctx.isecs.len() - 1) as u32 -} - -/// Appends a synthesized record to the tail of __DATA,`sect` (a section -/// with the given flags) and returns its subsection. -pub(crate) fn add_data_blob( - ctx: &mut Context, - sect: &'static [u8], - flags: u32, - fields: Vec, -) -> u32 { - let hdr = ctx.add_synthetic_section(MachSection { - sectname: bytes_to_name(sect), - segname: bytes_to_name(b"__DATA"), - p2align: 3, - flags, - ..Default::default() - }); - let blob = DataBlob { sect, isec: 0, fields }; - let isec = add_placed_isec(ctx, hdr, 3, blob.size(), 0); - ctx.data_blobs.push(DataBlob { isec, ..blob }); - isec -} - /// A new __TEXT,__objc_methlist section of the internal object, for /// method lists rewritten in the relative form. fn add_methlist_section(ctx: &mut Context) -> (u32, u32) { - ctx.add_synthetic_section(MachSection { + let hdr = MachSection { sectname: bytes_to_name(b"__objc_methlist"), segname: bytes_to_name(b"__TEXT"), p2align: 2, flags: S_REGULAR, ..Default::default() - }) + }; + add_synthetic_section(ctx, hdr) } /// Appends a method list in the relative form, at `*offset` in `sect` @@ -779,13 +710,14 @@ fn add_classref_stand_ins( if kept.is_empty() { return; } - let (file, shndx) = ctx.add_synthetic_section(MachSection { + let hdr = MachSection { sectname: bytes_to_name(b"__got"), segname: bytes_to_name(b"__DATA"), p2align: 3, flags: S_NON_LAZY_SYMBOL_POINTERS, ..Default::default() - }); + }; + let (file, shndx) = add_synthetic_section(ctx, hdr); for (slot, class) in kept { ctx.isecs.push(InputSection { output_section: crate::chunks::ChunkId::Got.pack(), diff --git a/macho/passes.rs b/macho/passes.rs index b4eba42b6..fb7bd5b88 100644 --- a/macho/passes.rs +++ b/macho/passes.rs @@ -23,12 +23,11 @@ use crate::error::RawPath; use crate::error::raw; use crate::fatal; use crate::input_files; -use crate::input_files::{FileId, ObjcImageInfo, SymbolSlots}; +use crate::input_files::{DataBlob, FileId, ObjcImageInfo, SymbolSlots, add_synthetic_section}; use crate::input_files::{is_class_or_protocol_ref_name, standard_section_flags}; use crate::input_sections::{InputSection, InputSectionId, NO_REPLACEMENT, RelocTarget}; use crate::macho::*; use crate::mapped_file::MappedFile; -use crate::objc::DataBlob; use crate::symbol::{NEEDS_GOT, NEEDS_STUB, Symbol, SymbolId}; use crate::symbol_moves::{Move, MoveOption}; use crate::util::worker_local::WorkerLocal; @@ -1217,14 +1216,15 @@ pub fn convert_common_symbols(ctx: &mut Context) { (size.next_power_of_two().trailing_zeros() as u8).min(ctx.args.max_default_common_align) }; - let (file, shndx) = ctx.add_synthetic_section(MachSection { + let hdr = MachSection { sectname: bytes_to_name(b"__common"), segname: bytes_to_name(b"__DATA"), size, p2align: p2align as u32, flags: S_ZEROFILL, ..Default::default() - }); + }; + let (file, shndx) = add_synthetic_section(ctx, hdr); ctx.isecs.push(InputSection::new(file, shndx, p2align, size as u32, &[])); let sym = &mut ctx.symbols[i]; @@ -3649,12 +3649,13 @@ fn add_sectcreate_isec( data: &'static [u8], ) -> u32 { let sc = &ctx.args.sectcreate[i]; - let (file, shndx) = ctx.add_synthetic_section(MachSection { + let hdr = MachSection { sectname: bytes_to_name(&sc.sectname), segname: bytes_to_name(&sc.segname), size: data.len() as u64, ..Default::default() - }); + }; + let (file, shndx) = add_synthetic_section(ctx, hdr); let id = ctx.isecs.len() as u32; ctx.isecs.push(InputSection { output_section: ChunkId::Output(osec).pack(),