#include "mold.h" #include "../archive-file.h" #include "../cmdline.h" #include #include #include #include #include #include #include namespace mold::macho { static void create_internal_file(Context &ctx) { ObjectFile *obj = new ObjectFile; ctx.obj_pool.push_back(std::unique_ptr(obj)); ctx.objs.push_back(obj); auto add = [&](std::string_view name) { Symbol *sym = intern(ctx, name); sym->file = obj; obj->syms.push_back(sym); return sym; }; add("__dyld_private"); Symbol *sym = add("__mh_execute_header"); sym->is_extern = true; sym->referenced_dynamically = true; sym->value = PAGE_ZERO_SIZE; } static bool compare_segments(const std::unique_ptr &a, const std::unique_ptr &b) { // We want to sort output segments in the following order: // __TEXT, __DATA_CONST, __DATA, , __LINKEDIT auto get_rank = [](std::string_view name) { if (name == "__TEXT") return 0; if (name == "__DATA_CONST") return 1; if (name == "__DATA") return 2; return INT_MAX; }; std::string_view na = a->cmd.get_segname(); std::string_view nb = b->cmd.get_segname(); i64 ra = get_rank(na); i64 rb = get_rank(nb); if (ra != INT_MAX || rb != INT_MAX) return ra < rb; if (na == "__LINKEDIT" || nb == "__LINKEDIT") return na != "__LINKEDIT"; return na < nb; } static bool compare_chunks(const Chunk *a, const Chunk *b) { assert(a->hdr.get_segname() == b->hdr.get_segname()); if ((a->hdr.type == S_ZEROFILL) != (b->hdr.type == S_ZEROFILL)) return a->hdr.type != S_ZEROFILL; static const std::string_view rank[] = { // __TEXT "__mach_header", "__text", "__stubs", "__stub_helper", "__gcc_except_tab", "__cstring", "__unwind_info", // __DATA_CONST "__got", // __DATA "__la_symbol_ptr" "__data", "__common", "__bss", // __LINKEDIT "__rebase", "__binding", "__lazy_binding", "__export", "__func_starts", "__symbol_table", "__ind_sym_tab", "__string_table", "__code_signature", }; auto get_rank = [](const Chunk *chunk) -> i64 { std::string_view name = chunk->hdr.get_sectname(); i64 i = 0; for (; i < sizeof(rank) / sizeof(rank[0]); i++) if (name == rank[i]) return i; return INT_MAX; }; i64 ra = get_rank(a); i64 rb = get_rank(b); if (ra == INT_MAX && rb == INT_MAX) return a->hdr.get_sectname() < b->hdr.get_sectname(); return ra < rb; } static void create_synthetic_chunks(Context &ctx) { for (ObjectFile *obj : ctx.objs) { for (std::unique_ptr &isec : obj->sections) { for (std::unique_ptr &subsec : isec->subsections) isec->osec.add_subsec(subsec.get()); isec->osec.hdr.attr |= isec->hdr.attr; } } for (Chunk *chunk : ctx.chunks) { if (chunk != ctx.data && chunk->is_regular && ((OutputSection *)chunk)->members.empty()) continue; OutputSegment *seg = OutputSegment::get_instance(ctx, chunk->hdr.get_segname()); seg->chunks.push_back(chunk); } sort(ctx.segments, compare_segments); for (std::unique_ptr &seg : ctx.segments) sort(seg->chunks, compare_chunks); } static void export_symbols(Context &ctx) { ctx.got.add(ctx, intern(ctx, "dyld_stub_binder")); for (ObjectFile *file : ctx.objs) for (Symbol *sym : file->syms) if (sym->file == file && sym->flags & NEEDS_GOT) ctx.got.add(ctx, sym); for (DylibFile *file : ctx.dylibs) { for (Symbol *sym : file->syms) { if (sym->file == file) if (sym->flags & NEEDS_STUB) ctx.stubs.add(ctx, sym); if (sym->flags & NEEDS_GOT) ctx.got.add(ctx, sym); } } } static i64 assign_offsets(Context &ctx) { i64 sect_idx = 1; for (std::unique_ptr &seg : ctx.segments) for (Chunk *chunk : seg->chunks) if (!chunk->is_hidden) chunk->sect_idx = sect_idx++; i64 fileoff = 0; i64 vmaddr = PAGE_ZERO_SIZE; for (std::unique_ptr &seg : ctx.segments) { seg->set_offset(ctx, fileoff, vmaddr); fileoff += seg->cmd.filesize; vmaddr += seg->cmd.vmsize; } return fileoff; } static void fix_synthetic_symbol_values(Context &ctx) { intern(ctx, "__dyld_private")->value = ctx.data->hdr.addr; } MappedFile *find_library(Context &ctx, std::string name) { for (std::string dir : ctx.arg.library_paths) { for (std::string ext : {".tbd", ".dylib", ".a"}) { std::string path = path_clean(ctx.arg.syslibroot + dir + "/lib" + name + ext); if (MappedFile *mf = MappedFile::open(ctx, path)) return mf; } } return nullptr; } static void read_file(Context &ctx, MappedFile *mf) { switch (get_file_type(mf)) { case FileType::TAPI: ctx.dylibs.push_back(DylibFile::create(ctx, mf)); break; case FileType::MACH_OBJ: ctx.objs.push_back(ObjectFile::create(ctx, mf, "")); break; case FileType::AR: for (MappedFile *child : read_archive_members(ctx, mf)) if (get_file_type(child) == FileType::MACH_OBJ) ctx.objs.push_back(ObjectFile::create(ctx, child, mf->name)); break; default: break; } } static void read_input_files(Context &ctx, std::span args) { for (std::string &arg : args) { if (arg.starts_with("-l")) { MappedFile *mf = find_library(ctx, arg.substr(2)); if (!mf) Fatal(ctx) << "library not found: " << arg; read_file(ctx, mf); } else { read_file(ctx, MappedFile::must_open(ctx, arg)); } } } int main(int argc, char **argv) { Context ctx; if (argc > 1 && std::string_view(argv[1]) == "-dump") { if (argc != 3) Fatal(ctx) << "usage: ld64.mold -dump \n"; dump_file(argv[2]); exit(0); } ctx.cmdline_args = expand_response_files(ctx, argv); std::vector file_args; parse_nonpositional_args(ctx, file_args); read_input_files(ctx, file_args); i64 priority = 1; for (ObjectFile *file : ctx.objs) file->priority = priority++; for (DylibFile *dylib : ctx.dylibs) dylib->priority = priority++; for (i64 i = 0; i < ctx.dylibs.size(); i++) ctx.dylibs[i]->dylib_idx = i + 1; for (ObjectFile *file : ctx.objs) file->parse(ctx); for (DylibFile *dylib : ctx.dylibs) dylib->parse(ctx); for (ObjectFile *file : ctx.objs) { if (file->archive_name.empty()) file->resolve_regular_symbols(ctx); else file->resolve_lazy_symbols(ctx); } std::vector live_objs; for (ObjectFile *file : ctx.objs) if (file->is_alive) live_objs.push_back(file); for (i64 i = 0; i < live_objs.size(); i++) append(live_objs, live_objs[i]->mark_live_objects(ctx)); for (DylibFile *dylib : ctx.dylibs) dylib->resolve_symbols(ctx); create_internal_file(ctx); for (ObjectFile *file : ctx.objs) file->convert_common_symbols(ctx); create_synthetic_chunks(ctx); for (i64 i = 0; i < ctx.segments.size(); i++) ctx.segments[i]->seg_idx = i + 1; for (ObjectFile *file : ctx.objs) for (std::unique_ptr &sec : file->sections) sec->scan_relocations(ctx); export_symbols(ctx); i64 output_size = assign_offsets(ctx); fix_synthetic_symbol_values(ctx); ctx.output_file = OutputFile::open(ctx, ctx.arg.output, output_size, 0777); ctx.buf = ctx.output_file->buf; for (std::unique_ptr &seg : ctx.segments) seg->copy_buf(ctx); ctx.output_file->close(ctx); ctx.checkpoint(); return 0; } }