#pragma once #include "macho.h" #include "lto.h" #include "../mold.h" #include #include #include #include #include #include #include #include #include #include #include #if MOLD_DEBUG_X86_64_ONLY # define INSTANTIATE_ALL INSTANTIATE(X86_64) #elif MOLD_DEBUG_ARM64_ONLY # define INSTANTIATE_ALL INSTANTIATE(ARM64) #else # define INSTANTIATE_ALL \ INSTANTIATE(X86_64); \ INSTANTIATE(ARM64); #endif namespace mold::macho { static constexpr i64 COMMON_PAGE_SIZE = 0x4000; static constexpr i64 SHA256_SIZE = 32; template class Chunk; template class InputSection; template class OutputSection; template class Subsection; template struct Context; template struct Symbol; // // input-files.cc // template struct Relocation { u32 offset = 0; u8 type = -1; u8 p2size = 0; bool is_pcrel : 1 = false; bool needs_dynrel : 1 = false; i64 addend = 0; Symbol *sym = nullptr; Subsection *subsec = nullptr; // For range extension thunks i32 thunk_idx = -1; i32 thunk_sym_idx = -1; }; template std::ostream &operator<<(std::ostream &out, const Relocation &rel) { out << rel_to_string(rel.type); return out; } template struct UnwindRecord { UnwindRecord(u32 len, u32 enc) : code_len(len), encoding(enc) {} u64 get_func_addr(Context &ctx) const { return subsec->get_addr(ctx) + offset; } Subsection *subsec = nullptr; Symbol *personality = nullptr; Subsection *lsda = nullptr; u32 offset = 0; u32 code_len = 0; u32 encoding = 0; u32 lsda_offset = 0; }; template class InputFile { public: virtual ~InputFile() = default; virtual void resolve_symbols(Context &ctx) = 0; void clear_symbols(); MappedFile> *mf = nullptr; std::string_view filename; std::vector *> syms; i64 priority = 0; std::atomic_bool is_alive = false; bool is_dylib = false; bool is_hidden = false; bool is_weak = false; std::string archive_name; protected: InputFile(MappedFile> *mf) : mf(mf), filename(mf->name) {} InputFile() : filename("") {} }; template class ObjectFile : public InputFile { public: ObjectFile() = default; ObjectFile(MappedFile> *mf) : InputFile(mf) {} static ObjectFile *create(Context &ctx, MappedFile> *mf, std::string archive_name); void parse(Context &ctx); Subsection *find_subsection(Context &ctx, u32 addr); Symbol *find_symbol(Context &ctx, u32 addr); std::vector get_linker_options(Context &ctx); void parse_compact_unwind(Context &ctx, MachSection &hdr); void resolve_symbols(Context &ctx) override; bool is_objc_object(Context &ctx); void mark_live_objects(Context &ctx, std::function *)> feeder); void convert_common_symbols(Context &ctx); void check_duplicate_symbols(Context &ctx); Relocation read_reloc(Context &ctx, const MachSection &hdr, MachRel r); std::vector>> sections; std::vector *> subsections; std::vector *> sym_to_subsec; std::span mach_syms; std::vector> local_syms; std::vector> unwind_records; std::span data_in_code_entries; LTOModule *lto_module = nullptr; // For the internal file and LTO object files std::vector mach_syms2; private: void parse_sections(Context &ctx); void parse_symbols(Context &ctx); void split_subsections_via_symbols(Context &ctx); void init_subsections(Context &ctx); void fix_subsec_members(Context &ctx); void parse_data_in_code(Context &ctx); LoadCommand *find_load_command(Context &ctx, u32 type); InputSection *get_common_sec(Context &ctx); void parse_lto_symbols(Context &ctx); MachSection *unwind_sec = nullptr; std::unique_ptr common_hdr; InputSection *common_sec = nullptr; std::vector *> subsec_pool; }; template class DylibFile : public InputFile { public: static DylibFile *create(Context &ctx, MappedFile> *mf); void parse(Context &ctx); void resolve_symbols(Context &ctx) override; std::string_view install_name; i64 dylib_idx = 0; bool is_reexported = false; std::vector reexported_libs; std::set exports; std::set weak_exports; private: DylibFile(Context &ctx, MappedFile> *mf); void parse_tapi(Context &ctx); void parse_dylib(Context &ctx); void read_trie(Context &ctx, u8 *start, i64 offset = 0, const std::string &prefix = ""); std::vector is_weak_symbol; }; template std::ostream &operator<<(std::ostream &out, const InputFile &file); // // input-sections.cc // template class InputSection { public: InputSection(Context &ctx, ObjectFile &file, const MachSection &hdr); void parse_relocations(Context &ctx); ObjectFile &file; const MachSection &hdr; OutputSection &osec; std::string_view contents; std::vector *> syms; std::vector> rels; }; template std::ostream &operator<<(std::ostream &out, const InputSection &sec); template class Subsection { public: inline u64 get_addr(Context &ctx) const; std::string_view get_contents() { assert(isec.hdr.type != S_ZEROFILL); return isec.contents.substr(input_offset, input_size); } std::span> get_unwind_records() { return std::span>(isec.file.unwind_records) .subspan(unwind_offset, nunwind); } std::span> get_rels() const { return std::span>(isec.rels).subspan(rel_offset, nrels); } void scan_relocations(Context &ctx); void apply_reloc(Context &ctx, u8 *buf); InputSection &isec; u32 input_offset = 0; u32 input_size = 0; u32 input_addr = 0; u32 output_offset = -1; u32 rel_offset = 0; u32 nrels = 0; u32 unwind_offset = 0; u32 nunwind = 0; Subsection *replacer; // Used if is_coalesced is true std::atomic_uint8_t p2align = 0; std::atomic_bool is_alive = true; bool is_coalesced : 1 = false; bool added_to_osec : 1 = false; }; template std::vector> read_relocations(Context &ctx, ObjectFile &file, const MachSection &hdr); // // Symbol // enum { NEEDS_GOT = 1 << 0, NEEDS_STUB = 1 << 1, NEEDS_THREAD_PTR = 1 << 2, NEEDS_RANGE_EXTN_THUNK = 1 << 3, }; enum { SCOPE_LOCAL, // input file scope SCOPE_PRIVATE_EXTERN, // output file scope (non-exported symbol) SCOPE_EXTERN, // global scope (exported symbol) }; template struct Symbol { Symbol() = default; Symbol(std::string_view name) : name(name) {} Symbol(const Symbol &other) : name(other.name) {} std::string_view name; InputFile *file = nullptr; Subsection *subsec = nullptr; u64 value = 0; i32 stub_idx = -1; i32 got_idx = -1; i32 tlv_idx = -1; tbb::spin_mutex mu; std::atomic_uint8_t flags = 0; u8 scope : 2 = SCOPE_LOCAL; bool is_common : 1 = false; bool is_weak : 1 = false; bool is_imported : 1 = false; bool referenced_dynamically : 1 = false; // For range extension thunks i32 thunk_idx = -1; i32 thunk_sym_idx = -1; inline u64 get_addr(Context &ctx) const; inline u64 get_got_addr(Context &ctx) const; inline u64 get_tlv_addr(Context &ctx) const; }; template std::ostream &operator<<(std::ostream &out, const Symbol &sym); // This operator defines a total order over symbols. This is used to // make the output deterministic. template inline bool operator<(const Symbol &a, const Symbol &b) { return std::tuple{a.file->priority, a.value} < std::tuple{b.file->priority, b.value}; } // // output-chunks.cc // template class OutputSegment { public: static OutputSegment * get_instance(Context &ctx, std::string_view name); void set_offset(Context &ctx, i64 fileoff, u64 vmaddr); SegmentCommand cmd = {}; i32 seg_idx = -1; std::vector *> chunks; private: void set_offset_regular(Context &ctx, i64 fileoff, u64 vmaddr); void set_offset_linkedit(Context &ctx, i64 fileoff, u64 vmaddr); OutputSegment(std::string_view name); }; template class Chunk { public: Chunk(Context &ctx, std::string_view segname, std::string_view sectname) { ctx.chunks.push_back(this); hdr.set_segname(segname); hdr.set_sectname(sectname); } virtual ~Chunk() = default; virtual void compute_size(Context &ctx) {} virtual void copy_buf(Context &ctx) {} MachSection hdr = {}; u32 sect_idx = 0; bool is_hidden = false; bool is_output_section = false; }; template std::ostream &operator<<(std::ostream &out, const Chunk &chunk); template class OutputMachHeader : public Chunk { public: OutputMachHeader(Context &ctx) : Chunk(ctx, "__TEXT", "__mach_header") { this->is_hidden = true; } void compute_size(Context &ctx) override; void copy_buf(Context &ctx) override; }; template class RangeExtensionThunk {}; template <> class RangeExtensionThunk { public: RangeExtensionThunk(OutputSection &osec) : output_section(osec) {} i64 size() const { return symbols.size() * ENTRY_SIZE; } u64 get_addr(i64 idx) const; void copy_buf(Context &ctx); static constexpr i64 ENTRY_SIZE = 12; OutputSection &output_section; i32 thunk_idx = -1; i64 offset = -1; std::mutex mu; std::vector *> symbols; }; template class OutputSection : public Chunk { public: static OutputSection * get_instance(Context &ctx, std::string_view segname, std::string_view sectname); OutputSection(Context &ctx, std::string_view segname, std::string_view sectname) : Chunk(ctx, segname, sectname) { this->is_output_section = true; } void compute_size(Context &ctx) override; void copy_buf(Context &ctx) override; void add_subsec(Subsection *subsec) { members.push_back(subsec); this->hdr.p2align = std::max(this->hdr.p2align, subsec->p2align); this->hdr.attr |= subsec->isec.hdr.attr; this->hdr.type = subsec->isec.hdr.type; assert(!subsec->added_to_osec); subsec->added_to_osec = true; } std::vector *> members; std::vector>> thunks; }; class RebaseEncoder { public: RebaseEncoder(); void add(i64 seg_idx, i64 offset); void flush(); void finish(); std::vector buf; private: i64 cur_seg = -1; i64 cur_off = 0; i64 times = 0; }; template class RebaseSection : public Chunk { public: RebaseSection(Context &ctx) : Chunk(ctx, "__LINKEDIT", "__rebase") { this->is_hidden = true; } void compute_size(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector contents; }; class BindEncoder { public: BindEncoder(); template void add(Symbol &sym, i64 seg_idx, i64 offset); void finish(); std::vector buf; private: std::string_view last_name; i64 last_flags = -1; i64 last_dylib = -1; i64 last_seg = -1; i64 last_off = -1; }; template class BindSection : public Chunk { public: BindSection(Context &ctx) : Chunk(ctx, "__LINKEDIT", "__binding") { this->is_hidden = true; } void compute_size(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector contents; }; template class LazyBindSection : public Chunk { public: LazyBindSection(Context &ctx) : Chunk(ctx, "__LINKEDIT", "__lazy_binding") { this->is_hidden = true; this->hdr.p2align = 3; } void add(Context &ctx, Symbol &sym); void compute_size(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector contents; }; class ExportEncoder { public: i64 finish(); struct Entry { std::string_view name; u32 flags; u64 addr; }; struct TrieNode { std::string_view prefix; std::vector> children; u64 addr = 0; u32 flags = 0; u32 offset = -1; bool is_leaf = false; }; void construct_trie(TrieNode &node, std::span entries, i64 len, tbb::task_group *tg, i64 grain_size, bool divide); static i64 set_offset(TrieNode &node, i64 offset); void write_trie(u8 *buf, TrieNode &node); TrieNode root; std::vector entries; }; template class ExportSection : public Chunk { public: ExportSection(Context &ctx) : Chunk(ctx, "__LINKEDIT", "__export") { this->is_hidden = true; } void compute_size(Context &ctx) override; void copy_buf(Context &ctx) override; private: ExportEncoder enc; }; template class FunctionStartsSection : public Chunk { public: FunctionStartsSection(Context &ctx) : Chunk(ctx, "__LINKEDIT", "__func_starts") { this->is_hidden = true; } void compute_size(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector contents; }; template class SymtabSection : public Chunk { public: SymtabSection(Context &ctx) : Chunk(ctx, "__LINKEDIT", "__symbol_table") { this->is_hidden = true; this->hdr.p2align = 3; } void compute_size(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector symtab_offsets; std::vector strtab_offsets; i64 num_locals = 0; i64 num_globals = 0; i64 num_undefs = 0; }; template class StrtabSection : public Chunk { public: StrtabSection(Context &ctx) : Chunk(ctx, "__LINKEDIT", "__string_table") { this->is_hidden = true; this->hdr.p2align = 3; this->hdr.size = 1; } }; template class CodeSignatureSection : public Chunk { public: CodeSignatureSection(Context &ctx) : Chunk(ctx, "__LINKEDIT", "__code_signature") { this->is_hidden = true; this->hdr.p2align = std::countr_zero(16U); } void compute_size(Context &ctx) override; void write_signature(Context &ctx); static constexpr i64 BLOCK_SIZE = 4096; }; template class DataInCodeSection : public Chunk { public: DataInCodeSection(Context &ctx) : Chunk(ctx, "__LINKEDIT", "__data_in_code") { this->is_hidden = true; this->hdr.p2align = std::countr_zero(alignof(DataInCodeEntry)); } void compute_size(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector contents; }; template class StubsSection : public Chunk { public: StubsSection(Context &ctx) : Chunk(ctx, "__TEXT", "__stubs") { this->hdr.p2align = std::countr_zero(2U); this->hdr.type = S_SYMBOL_STUBS; this->hdr.attr = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS; this->hdr.reserved1 = 0; this->hdr.reserved2 = E::stub_size; } void add(Context &ctx, Symbol *sym); void copy_buf(Context &ctx) override; std::vector *> syms; std::vector bind_offsets; }; template class StubHelperSection : public Chunk { public: StubHelperSection(Context &ctx) : Chunk(ctx, "__TEXT", "__stub_helper") { this->hdr.p2align = std::countr_zero(4U); this->hdr.attr = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS; } void copy_buf(Context &ctx) override; }; template class UnwindInfoSection : public Chunk { public: UnwindInfoSection(Context &ctx) : Chunk(ctx, "__TEXT", "__unwind_info") { this->hdr.p2align = std::countr_zero(4U); } void compute_size(Context &ctx) override; void copy_buf(Context &ctx) override; std::vector contents; }; template class GotSection : public Chunk { public: GotSection(Context &ctx) : Chunk(ctx, "__DATA_CONST", "__got") { this->hdr.p2align = 3; this->hdr.type = S_NON_LAZY_SYMBOL_POINTERS; } void add(Context &ctx, Symbol *sym); void copy_buf(Context &ctx) override; std::vector *> syms; std::vector *> subsections; }; template class LazySymbolPtrSection : public Chunk { public: LazySymbolPtrSection(Context &ctx) : Chunk(ctx, "__DATA", "__la_symbol_ptr") { this->hdr.p2align = 3; this->hdr.type = S_LAZY_SYMBOL_POINTERS; } void copy_buf(Context &ctx) override; }; template class ThreadPtrsSection : public Chunk { public: ThreadPtrsSection(Context &ctx) : Chunk(ctx, "__DATA", "__thread_ptrs") { this->hdr.p2align = 3; this->hdr.type = S_THREAD_LOCAL_VARIABLE_POINTERS; } void add(Context &ctx, Symbol *sym); void copy_buf(Context &ctx) override; std::vector *> syms; }; template class SectCreateSection : public Chunk { public: SectCreateSection(Context &ctx, std::string_view seg, std::string_view sect, std::string_view contents); void copy_buf(Context &ctx) override; std::string_view contents; }; // // mapfile.cc // template void print_map(Context &ctx); // // yaml.cc // struct YamlNode { std::variant, std::map> data; }; struct YamlError { std::string msg; i64 pos; }; std::variant, YamlError> parse_yaml(std::string_view str); // // tapi.cc // struct TextDylib { std::string_view install_name; std::vector reexported_libs; std::set exports; std::set weak_exports; }; template TextDylib parse_tbd(Context &ctx, MappedFile> *mf); // // cmdline.cc // template i64 parse_version(Context &ctx, std::string_view arg); template std::vector parse_nonpositional_args(Context &ctx); // // dead-strip.cc // template void dead_strip(Context &ctx); // // lto.cc // template void load_lto_plugin(Context &ctx); template void do_lto(Context &ctx); // // arch-arm64.cc // void create_range_extension_thunks(Context &ctx, OutputSection &osec); // // main.cc // enum UuidKind { UUID_NONE, UUID_HASH, UUID_RANDOM }; struct AddEmptySectionOption { std::string_view segname; std::string_view sectname; }; struct SectCreateOption { std::string_view segname; std::string_view sectname; std::string_view filename; }; template struct Context { Context() { text_seg = OutputSegment::get_instance(*this, "__TEXT"); data_const_seg = OutputSegment::get_instance(*this, "__DATA_CONST"); data_seg = OutputSegment::get_instance(*this, "__DATA"); linkedit_seg = OutputSegment::get_instance(*this, "__LINKEDIT"); text = OutputSection::get_instance(*this, "__TEXT", "__text"); data = OutputSection::get_instance(*this, "__DATA", "__data"); bss = OutputSection::get_instance(*this, "__DATA", "__bss"); cstring = OutputSection::get_instance(*this, "__TEXT", "__cstring"); common = OutputSection::get_instance(*this, "__DATA", "__common"); bss->hdr.type = S_ZEROFILL; cstring->hdr.type = S_CSTRING_LITERALS; common->hdr.type = S_ZEROFILL; } Context(const Context &) = delete; void checkpoint() { if (has_error) { cleanup(); _exit(1); } } // Command-line arguments struct { MultiGlob exported_symbols_list; MultiGlob unexported_symbols_list; Symbol *entry = nullptr; UuidKind uuid = UUID_HASH; bool ObjC = false; bool adhoc_codesign = std::is_same_v; bool application_extension = false; bool color_diagnostics = false; bool dead_strip = false; bool dead_strip_dylibs = false; bool deduplicate = true; bool demangle = true; bool dylib = false; bool dynamic = true; bool export_dynamic = false; bool fatal_warnings = false; bool noinhibit_exec = false; bool perf = false; bool quick_exit = true; bool search_paths_first = true; bool stats = false; bool trace = false; i64 arch = CPU_TYPE_ARM64; i64 compatibility_version = 0; i64 current_version = 0; i64 filler = 0; i64 headerpad = 256; i64 pagezero_size = 0; i64 platform = PLATFORM_MACOS; i64 platform_min_version = 0; i64 platform_sdk_version = 0; i64 stack_size = 0; i64 thread_count = 0; std::string chroot; std::string final_output; std::string install_name; std::string lto_library; std::string map; std::string object_path_lto; std::string output = "a.out"; std::vector add_empty_section; std::vector sectcreate; std::vector U; std::vector framework_paths; std::vector library_paths; std::vector mllvm; std::vector order_file; std::vector rpath; std::vector syslibroot; std::vector u; } arg; std::vector cmdline_args; u32 output_type = MH_EXECUTE; i64 file_priority = 10000; bool all_load = false; bool needed_l = false; bool hidden_l = false; bool weak_l = false; bool reexport_l = false; std::unordered_set loaded_archives; u8 uuid[16] = {}; bool has_error = false; u64 tls_begin = 0; LTOPlugin lto = {}; std::once_flag lto_plugin_loaded; tbb::concurrent_hash_map, HashCmp> symbol_map; std::unique_ptr>> output_file; u8 *buf; bool overwrite_output_file = false; tbb::concurrent_vector>> obj_pool; tbb::concurrent_vector>> dylib_pool; tbb::concurrent_vector> string_pool; tbb::concurrent_vector>>> mf_pool; std::vector>> chunk_pool; tbb::concurrent_vector> timer_records; std::vector *> objs; std::vector *> dylibs; OutputSegment *text_seg = nullptr; OutputSegment *data_const_seg = nullptr; OutputSegment *data_seg = nullptr; OutputSegment *linkedit_seg = nullptr; std::vector>> segments; std::vector *> chunks; OutputMachHeader mach_hdr{*this}; StubsSection stubs{*this}; StubHelperSection stub_helper{*this}; UnwindInfoSection unwind_info{*this}; GotSection got{*this}; LazySymbolPtrSection lazy_symbol_ptr{*this}; DataInCodeSection data_in_code{*this}; ThreadPtrsSection thread_ptrs{*this}; RebaseSection rebase{*this}; BindSection bind{*this}; LazyBindSection lazy_bind{*this}; ExportSection export_{*this}; FunctionStartsSection function_starts{*this}; SymtabSection symtab{*this}; StrtabSection strtab{*this}; std::unique_ptr> code_sig; OutputSection *text = nullptr; OutputSection *data = nullptr; OutputSection *bss = nullptr; OutputSection *cstring = nullptr; OutputSection *common = nullptr; }; int main(int argc, char **argv); // // Inline functions // template std::ostream &operator<<(std::ostream &out, const InputSection &sec) { out << sec.file << "(" << sec.hdr.get_segname() << "," << sec.hdr.get_sectname() << ")"; return out; } template u64 Subsection::get_addr(Context &ctx) const { return isec.osec.hdr.addr + output_offset; } template u64 Symbol::get_addr(Context &ctx) const { if (subsec) { assert(subsec->is_alive); return subsec->get_addr(ctx) + value; } if (stub_idx != -1) return ctx.stubs.hdr.addr + stub_idx * E::stub_size; return value; } template u64 Symbol::get_got_addr(Context &ctx) const { assert(got_idx != -1); return ctx.got.hdr.addr + got_idx * E::word_size; } template u64 Symbol::get_tlv_addr(Context &ctx) const { assert(tlv_idx != -1); return ctx.thread_ptrs.hdr.addr + tlv_idx * E::word_size; } template inline Symbol *get_symbol(Context &ctx, std::string_view name) { typename decltype(ctx.symbol_map)::const_accessor acc; ctx.symbol_map.insert(acc, {name, Symbol(name)}); return (Symbol *)(&acc->second); } template inline std::ostream &operator<<(std::ostream &out, const Symbol &sym) { if (opt_demangle && sym.name.starts_with("__Z")) out << demangle(sym.name.substr(1)); else out << sym.name; return out; } inline u64 RangeExtensionThunk::get_addr(i64 idx) const { return output_section.hdr.addr + offset + idx * ENTRY_SIZE; } } // namespace mold::macho