mirror of
https://github.com/rui314/mold.git
synced 2026-11-07 09:16:04 -10:00
Keep the chunks' layout helpers in their chunk modules
Some helpers of passes.rs were about one chunk: finish_unwind_info encodes __unwind_info again once every segment is placed, init_func makes an __init_offsets entry of a symbol, resolve_entry took the address LC_MAIN and LC_UNIXTHREAD name, and resolve_stub_binder finds the helper legacy LINKEDIT's stub helper jumps to. They are unwind_info::finish_unwind_info, InitFunc::new and stub_helper::resolve_stub_binder, beside ensure_stub_binder, now, and the entry point's address is chunks/mod.rs's entry_addr, as in mold, which the load commands ask rather than a Context field that a pass set after layout. An undefined entry point is still reported by a pass, check_entry_point, where the driver ran resolve_entry. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
309b8cd8df
commit
b6418cfff5
7 files changed
+79
-70
No files matched your search
@@ -25,6 +25,19 @@ pub enum InitFunc {
|
||||
Imported(SymbolId),
|
||||
}
|
||||
|
||||
impl InitFunc {
|
||||
/// The initializer symbol `id` is. One dyld binds, or an absolute
|
||||
/// one, has no offset in the image: the link fails as it is written
|
||||
/// (see copy_buf).
|
||||
pub fn new<E: Target>(ctx: &Context<E>, id: SymbolId) -> Self {
|
||||
let sym = &ctx.symbols[id];
|
||||
match sym.input_section() {
|
||||
Some(isec) => InitFunc::Local(ctx.isecs.resolve(isec as usize), sym.value),
|
||||
None => InitFunc::Imported(id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InitOffsetsSection {
|
||||
pub fn new() -> Self {
|
||||
let mut hdr = ChunkHeader::new(b"__TEXT", b"__init_offsets");
|
||||
|
||||
+20
-4
@@ -679,16 +679,32 @@ fn create_string_cmd(kind: u32, path: &[u8]) -> Vec<u8> {
|
||||
append_string(to_vec(&cmd), path)
|
||||
}
|
||||
|
||||
/// The address of the entry point symbol, or 0 for an image without
|
||||
/// one, or for one that is undefined (see passes::check_entry_point).
|
||||
fn entry_addr<E: Target>(ctx: &Context<E>) -> u64 {
|
||||
if !ctx.args.has_entry_point() {
|
||||
return 0;
|
||||
}
|
||||
match ctx.symbols.lookup(&ctx.args.entry) {
|
||||
// An entry point in a dylib (an app extension's
|
||||
// _NSExtensionMain): LC_MAIN must point into __TEXT, so it
|
||||
// names the symbol's stub, as ld64 does.
|
||||
Some(id) if ctx.symbols[id].is_imported() => ctx.symbols[id].stub_addr(ctx),
|
||||
Some(id) if ctx.symbols[id].is_defined() => ctx.symbols[id].addr(ctx),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_main_cmd<E: Target>(ctx: &Context<E>) -> Vec<u8> {
|
||||
// The entry point is a file offset into __TEXT, whose file offset
|
||||
// is zero. The layout sizes the command before the entry point has
|
||||
// an address (0) - also when it lays the segments out again, with
|
||||
// __TEXT placed by the first round.
|
||||
// its final address - also when it lays the segments out again,
|
||||
// with __TEXT placed by the first round.
|
||||
let text = ctx.segments.iter().find(|s| s.name == b"__TEXT").unwrap();
|
||||
let cmd = EntryPointCommand {
|
||||
cmd: LC_MAIN,
|
||||
cmdsize: size_of::<EntryPointCommand>() as u32,
|
||||
entryoff: ctx.entry_addr.saturating_sub(text.cmd.vmaddr),
|
||||
entryoff: entry_addr(ctx).saturating_sub(text.cmd.vmaddr),
|
||||
stacksize: ctx.args.stack_size,
|
||||
};
|
||||
to_vec(&cmd)
|
||||
@@ -721,7 +737,7 @@ fn create_unixthread_cmd<E: Target>(ctx: &Context<E>) -> Vec<u8> {
|
||||
}
|
||||
buf.resize(size, 0);
|
||||
let pc = 16 + E::THREAD_STATE_PC_OFFSET;
|
||||
buf[pc..pc + 8].copy_from_slice(&ctx.entry_addr.to_le_bytes());
|
||||
buf[pc..pc + 8].copy_from_slice(&entry_addr(ctx).to_le_bytes());
|
||||
if let Some(stack) = ctx.segments.iter().find(|seg| seg.name == b"__UNIXSTACK") {
|
||||
let sp = 16 + E::THREAD_STATE_SP_OFFSET;
|
||||
buf[sp..sp + 8].copy_from_slice(&(stack.cmd.vmaddr + stack.cmd.vmsize).to_le_bytes());
|
||||
|
||||
@@ -65,7 +65,7 @@ pub fn entry_offset<E: Target>(ctx: &Context<E>, idx: u32) -> u64 {
|
||||
/// synthesized. Once, on the first stub.
|
||||
pub fn ensure_stub_binder<E: Target>(ctx: &mut Context<E>) {
|
||||
// Legacy LINKEDIT's helper enters dyld through crt1.o's
|
||||
// dyld_stub_binding_helper instead (see passes::resolve_stub_binder).
|
||||
// dyld_stub_binding_helper instead (see resolve_stub_binder).
|
||||
if ctx.stub_helper.dyld_stub_binder.is_some() || ctx.args.legacy_linkedit {
|
||||
return;
|
||||
}
|
||||
@@ -80,6 +80,19 @@ pub fn ensure_stub_binder<E: Target>(ctx: &mut Context<E>) {
|
||||
ctx.extra_local_syms.push((b"__dyld_private", isec));
|
||||
}
|
||||
|
||||
/// Finds the helper legacy LINKEDIT's stub helper entries jump to. It
|
||||
/// binds no dyld_stub_binder: its entries go to dyld_stub_binding_helper,
|
||||
/// which crt1.o, dylib1.o or bundle1.o defines; no dylib exports it.
|
||||
/// (Otherwise dyld_stub_binder is bound once a stub needs it; see
|
||||
/// ensure_stub_binder.)
|
||||
pub fn resolve_stub_binder<E: Target>(ctx: &mut Context<E>) {
|
||||
if ctx.args.legacy_linkedit {
|
||||
let id = ctx.symbols.lookup(b"dyld_stub_binding_helper");
|
||||
let id = id.filter(|&id| ctx.symbols[id].input_section().is_some());
|
||||
ctx.stub_helper.binding_helper = id;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Target>(ctx: &mut Context<E>) {
|
||||
ctx.stub_helper.hdr.size =
|
||||
header_size(ctx) + ctx.stubs.lazy.len() as u64 * E::STUB_HELPER_ENTRY_SIZE;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::arch::Target;
|
||||
use crate::chunks::ChunkHeader;
|
||||
use crate::chunks::{ChunkHeader, ChunkId};
|
||||
use crate::context::Context;
|
||||
use crate::input_sections::{InputSection, UnwindRecord};
|
||||
use crate::macho::*;
|
||||
@@ -56,6 +56,24 @@ pub fn compute_size<E: Target>(ctx: &mut Context<E>) -> u64 {
|
||||
size
|
||||
}
|
||||
|
||||
/// __unwind_info is encoded as __TEXT is laid out, when only __TEXT's
|
||||
/// addresses are final. If it covers code or LSDAs in other segments
|
||||
/// too, this encodes it again now every segment has its address.
|
||||
/// Returns false if that encoding needs more room than __TEXT left the
|
||||
/// section; the layout is then done again with that much room (a
|
||||
/// smaller one leaves zeros after it).
|
||||
pub fn finish_unwind_info<E: Target>(ctx: &mut Context<E>) -> bool {
|
||||
if !ctx.chunks.contains(&ChunkId::UnwindInfo) || !covers_other_segments(ctx) {
|
||||
return true;
|
||||
}
|
||||
let size = compute_size(ctx);
|
||||
if size > ctx.unwind_info.hdr.size {
|
||||
ctx.unwind_info.min_size = size;
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Target>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let sec = &ctx.unwind_info;
|
||||
debug_assert!(sec.contents.len() as u64 <= sec.hdr.size);
|
||||
|
||||
@@ -244,8 +244,6 @@ pub struct Context<E: Target> {
|
||||
pub pointers32: std::sync::Mutex<Vec<(u32, u32)>>,
|
||||
/// The output's UUID, computed from its contents.
|
||||
pub uuid: std::sync::Mutex<[u8; 16]>,
|
||||
/// The resolved address of the entry point symbol.
|
||||
pub entry_addr: u64,
|
||||
/// The -init function, when LC_ROUTINES_64 names it (the image has
|
||||
/// no __init_offsets to run it first from).
|
||||
pub init_routine: Option<SymbolId>,
|
||||
@@ -338,7 +336,6 @@ impl<E: Target> Context<E> {
|
||||
text_relocs: std::sync::Mutex::new(Vec::new()),
|
||||
pointers32: std::sync::Mutex::new(Vec::new()),
|
||||
uuid: std::sync::Mutex::new([0; 16]),
|
||||
entry_addr: 0,
|
||||
init_routine: None,
|
||||
output_size: 0,
|
||||
timers,
|
||||
|
||||
+2
-2
@@ -247,7 +247,7 @@ pub fn link<E: Target>(cmdline: Cmdline) -> LinkResult {
|
||||
objc::merge_objc_categories(&mut ctx);
|
||||
// Synthetic stubs and unwind data can introduce library references
|
||||
// (notably dyld_stub_binder). Establish them before pruning dylibs.
|
||||
passes::resolve_stub_binder(&mut ctx);
|
||||
chunks::stub_helper::resolve_stub_binder(&mut ctx);
|
||||
crate::lazy_load::bind_dyld_lazy_load(&mut ctx);
|
||||
timed!("dead_strip_dylibs", passes::dead_strip_dylibs(&mut ctx));
|
||||
passes::bind_private_reexports_to_image(&mut ctx);
|
||||
@@ -301,7 +301,7 @@ pub fn link<E: Target>(cmdline: Cmdline) -> LinkResult {
|
||||
// of the parallel __LINKEDIT task group.
|
||||
timed!("set_osec_offsets", passes::set_osec_offsets(&mut ctx));
|
||||
passes::fix_synthetic_symbols(&mut ctx);
|
||||
passes::resolve_entry(&mut ctx);
|
||||
passes::check_entry_point(&ctx);
|
||||
crate::error::checkpoint();
|
||||
crate::mapfile::write_dependency_info(&ctx);
|
||||
crate::mapfile::print_map(&ctx);
|
||||
|
||||
+11
-59
@@ -1011,7 +1011,7 @@ pub fn convert_init_offsets<E: Target>(ctx: &mut Context<E>) {
|
||||
}
|
||||
// ld-prime makes it the first of the initializer offsets.
|
||||
if let Some(id) = init {
|
||||
let func = init_func(ctx, id);
|
||||
let func = InitFunc::new(ctx, id);
|
||||
ctx.init_offsets.init_funcs.push(func);
|
||||
}
|
||||
// ld-prime runs the hook for the classes of mergeable libraries (see
|
||||
@@ -1028,7 +1028,7 @@ pub fn convert_init_offsets<E: Target>(ctx: &mut Context<E>) {
|
||||
let obj = ctx.isecs[i].file as usize;
|
||||
for rel in initializer_relocs(ctx, i) {
|
||||
let func = match rel.target() {
|
||||
RelocTarget::Sym(idx) => init_func(ctx, ctx.objs[obj].symbols[idx as usize]),
|
||||
RelocTarget::Sym(idx) => InitFunc::new(ctx, ctx.objs[obj].symbols[idx as usize]),
|
||||
RelocTarget::Section(isec) => {
|
||||
InitFunc::Local(ctx.isecs.resolve(isec as usize), rel.addend as u64)
|
||||
}
|
||||
@@ -1039,17 +1039,6 @@ pub fn convert_init_offsets<E: Target>(ctx: &mut Context<E>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The initializer symbol `id` is. One dyld binds, or an absolute
|
||||
/// one, has no offset in the image: the link fails as it is written
|
||||
/// (see init_offsets::copy_buf).
|
||||
fn init_func<E: Target>(ctx: &Context<E>, id: crate::symbol::SymbolId) -> InitFunc {
|
||||
let sym = &ctx.symbols[id];
|
||||
match sym.input_section() {
|
||||
Some(isec) => InitFunc::Local(ctx.isecs.resolve(isec as usize), sym.value),
|
||||
None => InitFunc::Imported(id),
|
||||
}
|
||||
}
|
||||
|
||||
/// The function -init names, if it is defined. An undefined one is
|
||||
/// reported with the other initial undefines.
|
||||
fn init_function<E: Target>(ctx: &Context<E>) -> Option<crate::symbol::SymbolId> {
|
||||
@@ -4650,8 +4639,9 @@ fn mach_header_addr<E: Target>(ctx: &Context<E>) -> u64 {
|
||||
/// moved once all are sized - all but the mach header's segment
|
||||
/// (__TEXT), whose address is known up front (mach_header_addr) and
|
||||
/// whose __unwind_info encodes the final addresses of its functions
|
||||
/// (and of the others once they are placed: finish_unwind_info).
|
||||
/// __LINKEDIT comes last: its tables read every other address.
|
||||
/// (and of the others once they are placed: see
|
||||
/// unwind_info::finish_unwind_info). __LINKEDIT comes last: its tables
|
||||
/// read every other address.
|
||||
pub fn set_osec_offsets<E: Target>(ctx: &mut Context<E>) {
|
||||
let linkedit = ctx.segments.len() - 1;
|
||||
debug_assert_eq!(ctx.segments[linkedit].name, b"__LINKEDIT");
|
||||
@@ -4804,11 +4794,11 @@ fn lay_out_segments<E: Target>(ctx: &mut Context<E>) -> u64 {
|
||||
|
||||
/// Lays out every segment but __LINKEDIT as lay_out_segments does, and
|
||||
/// again until __unwind_info fits the room __TEXT leaves it (see
|
||||
/// finish_unwind_info). Returns the file offset past them.
|
||||
/// unwind_info::finish_unwind_info). Returns the file offset past them.
|
||||
fn lay_out_segments_with_unwind_info<E: Target>(ctx: &mut Context<E>) -> u64 {
|
||||
loop {
|
||||
let fileoff = lay_out_segments(ctx);
|
||||
if finish_unwind_info(ctx) {
|
||||
if chunks::unwind_info::finish_unwind_info(ctx) {
|
||||
return fileoff;
|
||||
}
|
||||
}
|
||||
@@ -4843,26 +4833,6 @@ fn segment_start_align<E: Target>(ctx: &Context<E>, seg_idx: usize) -> u64 {
|
||||
ctx.args.segment_align.max(1 << first)
|
||||
}
|
||||
|
||||
/// __unwind_info is encoded as __TEXT is laid out, when only __TEXT's
|
||||
/// addresses are final. If it covers code or LSDAs in other segments
|
||||
/// too, this encodes it again now every segment has its address.
|
||||
/// Returns false if that encoding needs more room than __TEXT left the
|
||||
/// section; the layout is then done again with that much room (a
|
||||
/// smaller one leaves zeros after it).
|
||||
fn finish_unwind_info<E: Target>(ctx: &mut Context<E>) -> bool {
|
||||
if !ctx.chunks.contains(&ChunkId::UnwindInfo)
|
||||
|| !chunks::unwind_info::covers_other_segments(ctx)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
let size = chunks::unwind_info::compute_size(ctx);
|
||||
if size > ctx.unwind_info.hdr.size {
|
||||
ctx.unwind_info.min_size = size;
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Lays out a segment's chunks from file offset `fileoff` and address
|
||||
/// `vmaddr`, and returns the file offset past the segment.
|
||||
fn layout_segment<E: Target>(
|
||||
@@ -5374,19 +5344,14 @@ fn build_fixups<E: Target>(ctx: &Context<E>) -> Fixups {
|
||||
Fixups::Classic { rebase, bind, weak_bind, lazy_bind, lazy_offsets }
|
||||
}
|
||||
|
||||
/// Resolves the entry point symbol.
|
||||
pub fn resolve_entry<E: Target>(ctx: &mut Context<E>) {
|
||||
/// Reports an entry point symbol that is undefined (see
|
||||
/// chunks::entry_addr).
|
||||
pub fn check_entry_point<E: Target>(ctx: &Context<E>) {
|
||||
if !ctx.args.has_entry_point() {
|
||||
return;
|
||||
}
|
||||
match ctx.symbols.lookup(&ctx.args.entry) {
|
||||
// An entry point in a dylib (an app extension's
|
||||
// _NSExtensionMain): LC_MAIN must point into __TEXT, so it
|
||||
// names the symbol's stub, as ld64 does.
|
||||
Some(id) if ctx.symbols[id].is_imported() => {
|
||||
ctx.entry_addr = ctx.symbols[id].stub_addr(ctx)
|
||||
}
|
||||
Some(id) if ctx.symbols[id].is_defined() => ctx.entry_addr = ctx.symbols[id].addr(ctx),
|
||||
Some(id) if ctx.symbols[id].is_imported() || ctx.symbols[id].is_defined() => {}
|
||||
_ => {
|
||||
error!(
|
||||
"undefined symbol for entry point: {}",
|
||||
@@ -5410,19 +5375,6 @@ pub fn add_entry_stub<E: Target>(ctx: &mut Context<E>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds the helper legacy LINKEDIT's stub helper entries jump to. It
|
||||
/// binds no dyld_stub_binder: its entries go to dyld_stub_binding_helper,
|
||||
/// which crt1.o, dylib1.o or bundle1.o defines; no dylib exports it.
|
||||
/// (Otherwise dyld_stub_binder is bound once a stub needs it; see
|
||||
/// chunks::stub_helper::ensure_stub_binder.)
|
||||
pub fn resolve_stub_binder<E: Target>(ctx: &mut Context<E>) {
|
||||
if ctx.args.legacy_linkedit {
|
||||
let id = ctx.symbols.lookup(b"dyld_stub_binding_helper");
|
||||
let id = id.filter(|&id| ctx.symbols[id].input_section().is_some());
|
||||
ctx.stub_helper.binding_helper = id;
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the UUID that identifies the build and writes it into the
|
||||
/// header's LC_UUID, and returns the SHA256 hashes of every 4KiB page
|
||||
/// before the code signature at `sig_start`, which the signature is
|
||||
|
||||
Reference in new issue
Block a user