Keep the internal object's synthetic inputs beside ObjectFile::internal

What the link synthesizes into the internal object was added from
four places: Context's add_synthetic_section and add_data_word,
objc.rs's add_placed_isec and add_data_blob with the DataBlob and
DataField records they make, and delay_init's add_cstring. A data blob
is no Objective-C record alone: bundle_hook's class table, the lazy
loads' flags and __dyld_private are blobs too. mold's internal object
is an ObjectFile that ObjectFile::internal makes, in input_files.rs;
these are functions of input_files.rs beside it now. They add
subsections to the link's arena and blobs to its list, not to the
object alone, so they take the context rather than being the
object's methods. Their callers bind the section header they add to
a local first.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Rui UeyamaandClaude Opus 5.5 committed 2026-10-08 18:19:57 +09:00
1 parent 9b9914967b
commit bc98b76f3b
13 files changed
+167 -158

No files matched your search

+4 -5
View File
@@ -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<E: Target>(ctx: &mut Context<E>) {
/// struct entry. Returns its subsection.
fn add_table<E: Target>(ctx: &mut Context<E>, libraries: &[(Vec<u8>, Vec<ObjcRef>)]) -> 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<E: Target>(ctx: &mut Context<E>, libraries: &[(Vec<u8>, Vec<ObjcRef
}
name_off += name.len() as u64 + 1;
}
crate::objc::add_data_blob(ctx, b"__data", S_REGULAR, fields)
add_data_blob(ctx, b"__data", S_REGULAR, fields)
}
/// The classes the hook is for, by library, as the table lists them: a
+1 -1
View File
@@ -7,8 +7,8 @@ use rayon::prelude::*;
use crate::arch::Target;
use crate::chunks::{ChunkHeader, ChunkId, OutputSectionId};
use crate::context::Context;
use crate::input_files::DataField;
use crate::input_sections::InputSectionId;
use crate::objc::DataField;
use crate::symbol_moves::MoveOption;
use crate::thunks::Thunk;
use crate::util::align_to;
+3 -2
View File
@@ -4,9 +4,10 @@
use crate::arch::Target;
use crate::chunks::{ChunkHeader, segment_and_offset};
use crate::context::Context;
use crate::input_files::DataField;
use crate::input_sections::{InputSection, Reloc};
use crate::macho::*;
use crate::objc::{DataField, ObjcRef};
use crate::objc::ObjcRef;
use crate::symbol::SymbolId;
use crate::util::encode_uleb;
@@ -194,7 +195,7 @@ fn rebase_ops<E: Target>(ctx: &Context<E>, locs: &[u64]) -> Vec<Op> {
}
/// The (address, target) of every pointer field of the synthesized
/// records (see objc::DataBlob).
/// records (see input_files::DataBlob).
fn data_blob_fields<E: Target>(ctx: &Context<E>) -> Vec<(u64, ObjcRef)> {
let mut out = Vec::new();
for b in &ctx.data_blobs {
+2 -2
View File
@@ -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;
+2 -1
View File
@@ -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<E: Target>(ctx: &mut Context<E>) {
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));
}
+1 -35
View File
@@ -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<E: Target> {
/// 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<crate::objc::DataBlob>,
pub data_blobs: Vec<crate::input_files::DataBlob>,
/// 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<E: Target> Context<E> {
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
+2 -23
View File
@@ -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<E: Target>(
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<E: Target>(
dlopen_of
}
/// Synthesizes a C string in __TEXT,__cstring, after the inputs', and
/// returns its subsection.
fn add_cstring<E: Target>(ctx: &mut Context<E>, 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<E: Target>(
+4 -2
View File
@@ -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<u8>]) -> Vec<u8> {
fn add_dof_section<E: Target>(ctx: &mut Context<E>, 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;
+127
View File
@@ -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<E: Target>(ctx: &mut Context<E>, 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<E: Target>(
ctx: &mut Context<E>,
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<u8>),
/// 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<DataField>,
}
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<E: Target>(
ctx: &mut Context<E>,
sect: &'static [u8],
flags: u32,
fields: Vec<DataField>,
) -> 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<E: Target>(ctx: &mut Context<E>, 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<E: Target>(ctx: &mut Context<E>, 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
+2 -2
View File
@@ -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<E: Target>(
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());
+2 -1
View File
@@ -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<E: Target> Builder<'_, E> {
/// The entries of the metadata the link made: the selector
+10 -78
View File
@@ -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<ObjcMethod>,
}
/// A field of a synthesized Objective-C data record.
#[derive(Clone, Debug)]
pub enum DataField {
Bytes(Vec<u8>),
/// 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<E: Target>(self, ctx: &Context<E>) -> Option<crate::symbol::SymbolId> {
@@ -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<DataField>,
}
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<E: Target>(
ctx: &mut Context<E>,
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<E: Target>(
ctx: &mut Context<E>,
sect: &'static [u8],
flags: u32,
fields: Vec<DataField>,
) -> 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<E: Target>(ctx: &mut Context<E>) -> (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<E: Target>(
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(),
+7 -6
View File
@@ -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<E: Target>(ctx: &mut Context<E>) {
(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<E: Target>(
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(),