| ... | @@ -1,99 +1,3 @@ | ... | @@ -1,99 +1,3 @@ |
| 1 | const MachO = @This(); | | |
| 2 | | | |
| 3 | const std = @import("std"); | | |
| 4 | const build_options = @import("build_options"); | | |
| 5 | const builtin = @import("builtin"); | | |
| 6 | const assert = std.debug.assert; | | |
| 7 | const dwarf = std.dwarf; | | |
| 8 | const fs = std.fs; | | |
| 9 | const log = std.log.scoped(.link); | | |
| 10 | const macho = std.macho; | | |
| 11 | const math = std.math; | | |
| 12 | const mem = std.mem; | | |
| 13 | const meta = std.meta; | | |
| 14 | | | |
| 15 | const aarch64 = @import("../arch/aarch64/bits.zig"); | | |
| 16 | const calcUuid = @import("MachO/uuid.zig").calcUuid; | | |
| 17 | const codegen = @import("../codegen.zig"); | | |
| 18 | const dead_strip = @import("MachO/dead_strip.zig"); | | |
| 19 | const fat = @import("MachO/fat.zig"); | | |
| 20 | const link = @import("../link.zig"); | | |
| 21 | const llvm_backend = @import("../codegen/llvm.zig"); | | |
| 22 | const load_commands = @import("MachO/load_commands.zig"); | | |
| 23 | const stubs = @import("MachO/stubs.zig"); | | |
| 24 | const target_util = @import("../target.zig"); | | |
| 25 | const trace = @import("../tracy.zig").trace; | | |
| 26 | const zld = @import("MachO/zld.zig"); | | |
| 27 | | | |
| 28 | const Air = @import("../Air.zig"); | | |
| 29 | const Allocator = mem.Allocator; | | |
| 30 | const Archive = @import("MachO/Archive.zig"); | | |
| 31 | pub const Atom = @import("MachO/Atom.zig"); | | |
| 32 | const Cache = std.Build.Cache; | | |
| 33 | const CodeSignature = @import("MachO/CodeSignature.zig"); | | |
| 34 | const Compilation = @import("../Compilation.zig"); | | |
| 35 | const Dwarf = File.Dwarf; | | |
| 36 | const Dylib = @import("MachO/Dylib.zig"); | | |
| 37 | const File = link.File; | | |
| 38 | const Object = @import("MachO/Object.zig"); | | |
| 39 | const LibStub = @import("tapi.zig").LibStub; | | |
| 40 | const Liveness = @import("../Liveness.zig"); | | |
| 41 | const LlvmObject = @import("../codegen/llvm.zig").Object; | | |
| 42 | const Md5 = std.crypto.hash.Md5; | | |
| 43 | const Module = @import("../Module.zig"); | | |
| 44 | const InternPool = @import("../InternPool.zig"); | | |
| 45 | const Relocation = @import("MachO/Relocation.zig"); | | |
| 46 | const StringTable = @import("strtab.zig").StringTable; | | |
| 47 | const TableSection = @import("table_section.zig").TableSection; | | |
| 48 | const Trie = @import("MachO/Trie.zig"); | | |
| 49 | const Type = @import("../type.zig").Type; | | |
| 50 | const TypedValue = @import("../TypedValue.zig"); | | |
| 51 | const Value = @import("../value.zig").Value; | | |
| 52 | | | |
| 53 | pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); | | |
| 54 | | | |
| 55 | const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, SymbolWithLoc); | | |
| 56 | const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, SymbolWithLoc); | | |
| 57 | const Rebase = @import("MachO/dyld_info/Rebase.zig"); | | |
| 58 | | | |
| 59 | pub const base_tag: File.Tag = File.Tag.macho; | | |
| 60 | pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1))); | | |
| 61 | | | |
| 62 | /// Mode of operation of the linker. | | |
| 63 | pub const Mode = enum { | | |
| 64 | /// Incremental mode will preallocate segments/sections and is compatible with | | |
| 65 | /// watch and HCS modes of operation. | | |
| 66 | incremental, | | |
| 67 | /// Zld mode will link relocatables in a traditional, one-shot | | |
| 68 | /// fashion (default for LLVM backend). It acts as a drop-in replacement for | | |
| 69 | /// LLD. | | |
| 70 | zld, | | |
| 71 | }; | | |
| 72 | | | |
| 73 | pub const Section = struct { | | |
| 74 | header: macho.section_64, | | |
| 75 | segment_index: u8, | | |
| 76 | first_atom_index: ?Atom.Index = null, | | |
| 77 | last_atom_index: ?Atom.Index = null, | | |
| 78 | | | |
| 79 | /// A list of atoms that have surplus capacity. This list can have false | | |
| 80 | /// positives, as functions grow and shrink over time, only sometimes being added | | |
| 81 | /// or removed from the freelist. | | |
| 82 | /// | | |
| 83 | /// An atom has surplus capacity when its overcapacity value is greater than | | |
| 84 | /// padToIdeal(minimum_atom_size). That is, when it has so | | |
| 85 | /// much extra capacity, that we could fit a small new symbol in it, itself with | | |
| 86 | /// ideal_capacity or more. | | |
| 87 | /// | | |
| 88 | /// Ideal capacity is defined by size + (size / ideal_factor). | | |
| 89 | /// | | |
| 90 | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that | | |
| 91 | /// overcapacity can be negative. A simple way to have negative overcapacity is to | | |
| 92 | /// allocate a fresh atom, which will have ideal capacity, and then grow it | | |
| 93 | /// by 1 byte. It will then have -1 overcapacity. | | |
| 94 | free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, | | |
| 95 | }; | | |
| 96 | | | |
| 97 | base: File, | 1 | base: File, |
| 98 | | 2 | |
| 99 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. | 3 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| ... | @@ -153,6 +57,10 @@ strtab: StringTable(.strtab) = .{}, | ... | @@ -153,6 +57,10 @@ strtab: StringTable(.strtab) = .{}, |
| 153 | | 57 | |
| 154 | got_table: TableSection(SymbolWithLoc) = .{}, | 58 | got_table: TableSection(SymbolWithLoc) = .{}, |
| 155 | stub_table: TableSection(SymbolWithLoc) = .{}, | 59 | stub_table: TableSection(SymbolWithLoc) = .{}, |
| | 60 | tlv_ptr_table: TableSection(SymbolWithLoc) = .{}, |
| | 61 | |
| | 62 | thunk_table: std.AutoHashMapUnmanaged(Atom.Index, thunks.Thunk.Index) = .{}, |
| | 63 | thunks: std.ArrayListUnmanaged(thunks.Thunk) = .{}, |
| 156 | | 64 | |
| 157 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | 65 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 158 | misc_errors: std.ArrayListUnmanaged(File.ErrorMsg) = .{}, | 66 | misc_errors: std.ArrayListUnmanaged(File.ErrorMsg) = .{}, |
| ... | @@ -225,101 +133,6 @@ tlv_table: TlvSymbolTable = .{}, | ... | @@ -225,101 +133,6 @@ tlv_table: TlvSymbolTable = .{}, |
| 225 | /// Hot-code swapping state. | 133 | /// Hot-code swapping state. |
| 226 | hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, | 134 | hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, |
| 227 | | 135 | |
| 228 | const is_hot_update_compatible = switch (builtin.target.os.tag) { | | |
| 229 | .macos => true, | | |
| 230 | else => false, | | |
| 231 | }; | | |
| 232 | | | |
| 233 | const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); | | |
| 234 | | | |
| 235 | const LazySymbolMetadata = struct { | | |
| 236 | const State = enum { unused, pending_flush, flushed }; | | |
| 237 | text_atom: Atom.Index = undefined, | | |
| 238 | data_const_atom: Atom.Index = undefined, | | |
| 239 | text_state: State = .unused, | | |
| 240 | data_const_state: State = .unused, | | |
| 241 | }; | | |
| 242 | | | |
| 243 | const TlvSymbolTable = std.AutoArrayHashMapUnmanaged(SymbolWithLoc, Atom.Index); | | |
| 244 | | | |
| 245 | const DeclMetadata = struct { | | |
| 246 | atom: Atom.Index, | | |
| 247 | section: u8, | | |
| 248 | /// A list of all exports aliases of this Decl. | | |
| 249 | /// TODO do we actually need this at all? | | |
| 250 | exports: std.ArrayListUnmanaged(u32) = .{}, | | |
| 251 | | | |
| 252 | fn getExport(m: DeclMetadata, macho_file: *const MachO, name: []const u8) ?u32 { | | |
| 253 | for (m.exports.items) |exp| { | | |
| 254 | if (mem.eql(u8, name, macho_file.getSymbolName(.{ .sym_index = exp }))) return exp; | | |
| 255 | } | | |
| 256 | return null; | | |
| 257 | } | | |
| 258 | | | |
| 259 | fn getExportPtr(m: *DeclMetadata, macho_file: *MachO, name: []const u8) ?*u32 { | | |
| 260 | for (m.exports.items) |*exp| { | | |
| 261 | if (mem.eql(u8, name, macho_file.getSymbolName(.{ .sym_index = exp.* }))) return exp; | | |
| 262 | } | | |
| 263 | return null; | | |
| 264 | } | | |
| 265 | }; | | |
| 266 | | | |
| 267 | const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding)); | | |
| 268 | const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); | | |
| 269 | const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); | | |
| 270 | const RelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation)); | | |
| 271 | | | |
| 272 | const ResolveAction = struct { | | |
| 273 | kind: Kind, | | |
| 274 | target: SymbolWithLoc, | | |
| 275 | | | |
| 276 | const Kind = enum { | | |
| 277 | none, | | |
| 278 | add_got, | | |
| 279 | add_stub, | | |
| 280 | }; | | |
| 281 | }; | | |
| 282 | | | |
| 283 | pub const SymbolWithLoc = extern struct { | | |
| 284 | // Index into the respective symbol table. | | |
| 285 | sym_index: u32, | | |
| 286 | | | |
| 287 | // 0 means it's a synthetic global. | | |
| 288 | file: u32 = 0, | | |
| 289 | | | |
| 290 | pub fn getFile(self: SymbolWithLoc) ?u32 { | | |
| 291 | if (self.file == 0) return null; | | |
| 292 | return self.file - 1; | | |
| 293 | } | | |
| 294 | | | |
| 295 | pub fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool { | | |
| 296 | return self.file == other.file and self.sym_index == other.sym_index; | | |
| 297 | } | | |
| 298 | }; | | |
| 299 | | | |
| 300 | const HotUpdateState = struct { | | |
| 301 | mach_task: ?std.os.darwin.MachTask = null, | | |
| 302 | }; | | |
| 303 | | | |
| 304 | /// When allocating, the ideal_capacity is calculated by | | |
| 305 | /// actual_capacity + (actual_capacity / ideal_factor) | | |
| 306 | const ideal_factor = 3; | | |
| 307 | | | |
| 308 | /// In order for a slice of bytes to be considered eligible to keep metadata pointing at | | |
| 309 | /// it as a possible place to put new symbols, it must have enough room for this many bytes | | |
| 310 | /// (plus extra for reserved capacity). | | |
| 311 | const minimum_text_block_size = 64; | | |
| 312 | pub const min_text_capacity = padToIdeal(minimum_text_block_size); | | |
| 313 | | | |
| 314 | /// Default virtual memory offset corresponds to the size of __PAGEZERO segment and | | |
| 315 | /// start of __TEXT segment. | | |
| 316 | pub const default_pagezero_vmsize: u64 = 0x100000000; | | |
| 317 | | | |
| 318 | /// We commit 0x1000 = 4096 bytes of space to the header and | | |
| 319 | /// the table of load commands. This should be plenty for any | | |
| 320 | /// potential future extensions. | | |
| 321 | pub const default_headerpad_size: u32 = 0x1000; | | |
| 322 | | | |
| 323 | pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { | 136 | pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { |
| 324 | assert(options.target.ofmt == .macho); | 137 | assert(options.target.ofmt == .macho); |
| 325 | | 138 | |
| ... | @@ -622,6 +435,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -622,6 +435,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 622 | defer actions.deinit(); | 435 | defer actions.deinit(); |
| 623 | try self.resolveSymbolsInDylibs(&actions); | 436 | try self.resolveSymbolsInDylibs(&actions); |
| 624 | | 437 | |
| | 438 | if (self.getEntryPoint() == null) { |
| | 439 | self.error_flags.no_entry_point_found = true; |
| | 440 | } |
| | 441 | |
| 625 | if (self.unresolved.count() > 0) { | 442 | if (self.unresolved.count() > 0) { |
| 626 | for (self.unresolved.keys()) |index| { | 443 | for (self.unresolved.keys()) |index| { |
| 627 | // TODO: convert into compiler errors. | 444 | // TODO: convert into compiler errors. |
| ... | @@ -641,6 +458,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -641,6 +458,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 641 | try self.createDyldPrivateAtom(); | 458 | try self.createDyldPrivateAtom(); |
| 642 | try self.writeStubHelperPreamble(); | 459 | try self.writeStubHelperPreamble(); |
| 643 | | 460 | |
| | 461 | if (self.base.options.output_mode == .Exe and self.getEntryPoint() != null) { |
| | 462 | const global = self.getEntryPoint().?; |
| | 463 | if (self.getSymbol(global).undf()) { |
| | 464 | // We do one additional check here in case the entry point was found in one of the dylibs. |
| | 465 | // (I actually have no idea what this would imply but it is a possible outcome and so we |
| | 466 | // support it.) |
| | 467 | try self.addStubEntry(global); |
| | 468 | } |
| | 469 | } |
| | 470 | |
| 644 | try self.allocateSpecialSymbols(); | 471 | try self.allocateSpecialSymbols(); |
| 645 | | 472 | |
| 646 | for (self.relocs.keys()) |atom_index| { | 473 | for (self.relocs.keys()) |atom_index| { |
| ... | @@ -732,16 +559,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -732,16 +559,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 732 | .Exe => blk: { | 559 | .Exe => blk: { |
| 733 | const seg_id = self.header_segment_cmd_index.?; | 560 | const seg_id = self.header_segment_cmd_index.?; |
| 734 | const seg = self.segments.items[seg_id]; | 561 | const seg = self.segments.items[seg_id]; |
| 735 | const global = self.getEntryPoint() catch |err| switch (err) { | 562 | const global = self.getEntryPoint() orelse break :blk; |
| 736 | error.MissingMainEntrypoint => { | | |
| 737 | self.error_flags.no_entry_point_found = true; | | |
| 738 | break :blk; | | |
| 739 | }, | | |
| 740 | else => |e| return e, | | |
| 741 | }; | | |
| 742 | const sym = self.getSymbol(global); | 563 | const sym = self.getSymbol(global); |
| | 564 | |
| | 565 | const addr: u64 = if (sym.undf()) |
| | 566 | // In this case, the symbol has been resolved in one of dylibs and so we point |
| | 567 | // to the stub as its vmaddr value. |
| | 568 | self.getStubsEntryAddress(global).? |
| | 569 | else |
| | 570 | sym.n_value; |
| | 571 | |
| 743 | try lc_writer.writeStruct(macho.entry_point_command{ | 572 | try lc_writer.writeStruct(macho.entry_point_command{ |
| 744 | .entryoff = @as(u32, @intCast(sym.n_value - seg.vmaddr)), | 573 | .entryoff = @as(u32, @intCast(addr - seg.vmaddr)), |
| 745 | .stacksize = self.base.options.stack_size_override orelse 0, | 574 | .stacksize = self.base.options.stack_size_override orelse 0, |
| 746 | }); | 575 | }); |
| 747 | }, | 576 | }, |
| ... | @@ -1796,6 +1625,14 @@ pub fn deinit(self: *MachO) void { | ... | @@ -1796,6 +1625,14 @@ pub fn deinit(self: *MachO) void { |
| 1796 | | 1625 | |
| 1797 | self.got_table.deinit(gpa); | 1626 | self.got_table.deinit(gpa); |
| 1798 | self.stub_table.deinit(gpa); | 1627 | self.stub_table.deinit(gpa); |
| | 1628 | self.tlv_ptr_table.deinit(gpa); |
| | 1629 | self.thunk_table.deinit(gpa); |
| | 1630 | |
| | 1631 | for (self.thunks.items) |*thunk| { |
| | 1632 | thunk.deinit(gpa); |
| | 1633 | } |
| | 1634 | self.thunks.deinit(gpa); |
| | 1635 | |
| 1799 | self.strtab.deinit(gpa); | 1636 | self.strtab.deinit(gpa); |
| 1800 | | 1637 | |
| 1801 | self.locals.deinit(gpa); | 1638 | self.locals.deinit(gpa); |
| ... | @@ -4019,14 +3856,29 @@ pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.In | ... | @@ -4019,14 +3856,29 @@ pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.In |
| 4019 | return self.atom_by_index_table.get(sym_with_loc.sym_index); | 3856 | return self.atom_by_index_table.get(sym_with_loc.sym_index); |
| 4020 | } | 3857 | } |
| 4021 | | 3858 | |
| 4022 | /// Returns symbol location corresponding to the set entrypoint. | 3859 | pub fn getGotEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 { |
| | 3860 | const index = self.got_table.lookup.get(sym_with_loc) orelse return null; |
| | 3861 | const header = self.sections.items(.header)[self.got_section_index.?]; |
| | 3862 | return header.addr + @sizeOf(u64) * index; |
| | 3863 | } |
| | 3864 | |
| | 3865 | pub fn getTlvPtrEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 { |
| | 3866 | const index = self.tlv_ptr_table.lookup.get(sym_with_loc) orelse return null; |
| | 3867 | const header = self.sections.items(.header)[self.tlv_ptr_section_index.?]; |
| | 3868 | return header.addr + @sizeOf(u64) * index; |
| | 3869 | } |
| | 3870 | |
| | 3871 | pub fn getStubsEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 { |
| | 3872 | const index = self.stub_table.lookup.get(sym_with_loc) orelse return null; |
| | 3873 | const header = self.sections.items(.header)[self.stubs_section_index.?]; |
| | 3874 | return header.addr + stubs.stubSize(self.base.options.target.cpu.arch) * index; |
| | 3875 | } |
| | 3876 | |
| | 3877 | /// Returns symbol location corresponding to the set entrypoint if any. |
| 4023 | /// Asserts output mode is executable. | 3878 | /// Asserts output mode is executable. |
| 4024 | pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc { | 3879 | pub fn getEntryPoint(self: MachO) ?SymbolWithLoc { |
| 4025 | const entry_name = self.base.options.entry orelse load_commands.default_entry_point; | 3880 | const entry_name = self.base.options.entry orelse load_commands.default_entry_point; |
| 4026 | const global = self.getGlobal(entry_name) orelse { | 3881 | const global = self.getGlobal(entry_name) orelse return null; |
| 4027 | log.err("entrypoint '{s}' not found", .{entry_name}); | | |
| 4028 | return error.MissingMainEntrypoint; | | |
| 4029 | }; | | |
| 4030 | return global; | 3882 | return global; |
| 4031 | } | 3883 | } |
| 4032 | | 3884 | |
| ... | @@ -4258,3 +4110,195 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index) void { | ... | @@ -4258,3 +4110,195 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index) void { |
| 4258 | sym.n_sect + 1, | 4110 | sym.n_sect + 1, |
| 4259 | }); | 4111 | }); |
| 4260 | } | 4112 | } |
| | 4113 | |
| | 4114 | const MachO = @This(); |
| | 4115 | |
| | 4116 | const std = @import("std"); |
| | 4117 | const build_options = @import("build_options"); |
| | 4118 | const builtin = @import("builtin"); |
| | 4119 | const assert = std.debug.assert; |
| | 4120 | const dwarf = std.dwarf; |
| | 4121 | const fs = std.fs; |
| | 4122 | const log = std.log.scoped(.link); |
| | 4123 | const macho = std.macho; |
| | 4124 | const math = std.math; |
| | 4125 | const mem = std.mem; |
| | 4126 | const meta = std.meta; |
| | 4127 | |
| | 4128 | const aarch64 = @import("../arch/aarch64/bits.zig"); |
| | 4129 | const calcUuid = @import("MachO/uuid.zig").calcUuid; |
| | 4130 | const codegen = @import("../codegen.zig"); |
| | 4131 | const dead_strip = @import("MachO/dead_strip.zig"); |
| | 4132 | const fat = @import("MachO/fat.zig"); |
| | 4133 | const link = @import("../link.zig"); |
| | 4134 | const llvm_backend = @import("../codegen/llvm.zig"); |
| | 4135 | const load_commands = @import("MachO/load_commands.zig"); |
| | 4136 | const stubs = @import("MachO/stubs.zig"); |
| | 4137 | const target_util = @import("../target.zig"); |
| | 4138 | const thunks = @import("MachO/thunks.zig"); |
| | 4139 | const trace = @import("../tracy.zig").trace; |
| | 4140 | const zld = @import("MachO/zld.zig"); |
| | 4141 | |
| | 4142 | const Air = @import("../Air.zig"); |
| | 4143 | const Allocator = mem.Allocator; |
| | 4144 | const Archive = @import("MachO/Archive.zig"); |
| | 4145 | pub const Atom = @import("MachO/Atom.zig"); |
| | 4146 | const Cache = std.Build.Cache; |
| | 4147 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| | 4148 | const Compilation = @import("../Compilation.zig"); |
| | 4149 | const Dwarf = File.Dwarf; |
| | 4150 | const Dylib = @import("MachO/Dylib.zig"); |
| | 4151 | const File = link.File; |
| | 4152 | const Object = @import("MachO/Object.zig"); |
| | 4153 | const LibStub = @import("tapi.zig").LibStub; |
| | 4154 | const Liveness = @import("../Liveness.zig"); |
| | 4155 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| | 4156 | const Md5 = std.crypto.hash.Md5; |
| | 4157 | const Module = @import("../Module.zig"); |
| | 4158 | const InternPool = @import("../InternPool.zig"); |
| | 4159 | const Relocation = @import("MachO/Relocation.zig"); |
| | 4160 | const StringTable = @import("strtab.zig").StringTable; |
| | 4161 | const TableSection = @import("table_section.zig").TableSection; |
| | 4162 | const Trie = @import("MachO/Trie.zig"); |
| | 4163 | const Type = @import("../type.zig").Type; |
| | 4164 | const TypedValue = @import("../TypedValue.zig"); |
| | 4165 | const Value = @import("../value.zig").Value; |
| | 4166 | |
| | 4167 | pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); |
| | 4168 | |
| | 4169 | const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, SymbolWithLoc); |
| | 4170 | const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, SymbolWithLoc); |
| | 4171 | const Rebase = @import("MachO/dyld_info/Rebase.zig"); |
| | 4172 | |
| | 4173 | pub const base_tag: File.Tag = File.Tag.macho; |
| | 4174 | pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1))); |
| | 4175 | |
| | 4176 | /// Mode of operation of the linker. |
| | 4177 | pub const Mode = enum { |
| | 4178 | /// Incremental mode will preallocate segments/sections and is compatible with |
| | 4179 | /// watch and HCS modes of operation. |
| | 4180 | incremental, |
| | 4181 | /// Zld mode will link relocatables in a traditional, one-shot |
| | 4182 | /// fashion (default for LLVM backend). It acts as a drop-in replacement for |
| | 4183 | /// LLD. |
| | 4184 | zld, |
| | 4185 | }; |
| | 4186 | |
| | 4187 | pub const Section = struct { |
| | 4188 | header: macho.section_64, |
| | 4189 | segment_index: u8, |
| | 4190 | first_atom_index: ?Atom.Index = null, |
| | 4191 | last_atom_index: ?Atom.Index = null, |
| | 4192 | |
| | 4193 | /// A list of atoms that have surplus capacity. This list can have false |
| | 4194 | /// positives, as functions grow and shrink over time, only sometimes being added |
| | 4195 | /// or removed from the freelist. |
| | 4196 | /// |
| | 4197 | /// An atom has surplus capacity when its overcapacity value is greater than |
| | 4198 | /// padToIdeal(minimum_atom_size). That is, when it has so |
| | 4199 | /// much extra capacity, that we could fit a small new symbol in it, itself with |
| | 4200 | /// ideal_capacity or more. |
| | 4201 | /// |
| | 4202 | /// Ideal capacity is defined by size + (size / ideal_factor). |
| | 4203 | /// |
| | 4204 | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that |
| | 4205 | /// overcapacity can be negative. A simple way to have negative overcapacity is to |
| | 4206 | /// allocate a fresh atom, which will have ideal capacity, and then grow it |
| | 4207 | /// by 1 byte. It will then have -1 overcapacity. |
| | 4208 | free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, |
| | 4209 | }; |
| | 4210 | |
| | 4211 | const is_hot_update_compatible = switch (builtin.target.os.tag) { |
| | 4212 | .macos => true, |
| | 4213 | else => false, |
| | 4214 | }; |
| | 4215 | |
| | 4216 | const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); |
| | 4217 | |
| | 4218 | const LazySymbolMetadata = struct { |
| | 4219 | const State = enum { unused, pending_flush, flushed }; |
| | 4220 | text_atom: Atom.Index = undefined, |
| | 4221 | data_const_atom: Atom.Index = undefined, |
| | 4222 | text_state: State = .unused, |
| | 4223 | data_const_state: State = .unused, |
| | 4224 | }; |
| | 4225 | |
| | 4226 | const TlvSymbolTable = std.AutoArrayHashMapUnmanaged(SymbolWithLoc, Atom.Index); |
| | 4227 | |
| | 4228 | const DeclMetadata = struct { |
| | 4229 | atom: Atom.Index, |
| | 4230 | section: u8, |
| | 4231 | /// A list of all exports aliases of this Decl. |
| | 4232 | /// TODO do we actually need this at all? |
| | 4233 | exports: std.ArrayListUnmanaged(u32) = .{}, |
| | 4234 | |
| | 4235 | fn getExport(m: DeclMetadata, macho_file: *const MachO, name: []const u8) ?u32 { |
| | 4236 | for (m.exports.items) |exp| { |
| | 4237 | if (mem.eql(u8, name, macho_file.getSymbolName(.{ .sym_index = exp }))) return exp; |
| | 4238 | } |
| | 4239 | return null; |
| | 4240 | } |
| | 4241 | |
| | 4242 | fn getExportPtr(m: *DeclMetadata, macho_file: *MachO, name: []const u8) ?*u32 { |
| | 4243 | for (m.exports.items) |*exp| { |
| | 4244 | if (mem.eql(u8, name, macho_file.getSymbolName(.{ .sym_index = exp.* }))) return exp; |
| | 4245 | } |
| | 4246 | return null; |
| | 4247 | } |
| | 4248 | }; |
| | 4249 | |
| | 4250 | const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding)); |
| | 4251 | const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); |
| | 4252 | const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); |
| | 4253 | const RelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation)); |
| | 4254 | |
| | 4255 | const ResolveAction = struct { |
| | 4256 | kind: Kind, |
| | 4257 | target: SymbolWithLoc, |
| | 4258 | |
| | 4259 | const Kind = enum { |
| | 4260 | none, |
| | 4261 | add_got, |
| | 4262 | add_stub, |
| | 4263 | }; |
| | 4264 | }; |
| | 4265 | |
| | 4266 | pub const SymbolWithLoc = extern struct { |
| | 4267 | // Index into the respective symbol table. |
| | 4268 | sym_index: u32, |
| | 4269 | |
| | 4270 | // 0 means it's a synthetic global. |
| | 4271 | file: u32 = 0, |
| | 4272 | |
| | 4273 | pub fn getFile(self: SymbolWithLoc) ?u32 { |
| | 4274 | if (self.file == 0) return null; |
| | 4275 | return self.file - 1; |
| | 4276 | } |
| | 4277 | |
| | 4278 | pub fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool { |
| | 4279 | return self.file == other.file and self.sym_index == other.sym_index; |
| | 4280 | } |
| | 4281 | }; |
| | 4282 | |
| | 4283 | const HotUpdateState = struct { |
| | 4284 | mach_task: ?std.os.darwin.MachTask = null, |
| | 4285 | }; |
| | 4286 | |
| | 4287 | /// When allocating, the ideal_capacity is calculated by |
| | 4288 | /// actual_capacity + (actual_capacity / ideal_factor) |
| | 4289 | const ideal_factor = 3; |
| | 4290 | |
| | 4291 | /// In order for a slice of bytes to be considered eligible to keep metadata pointing at |
| | 4292 | /// it as a possible place to put new symbols, it must have enough room for this many bytes |
| | 4293 | /// (plus extra for reserved capacity). |
| | 4294 | const minimum_text_block_size = 64; |
| | 4295 | pub const min_text_capacity = padToIdeal(minimum_text_block_size); |
| | 4296 | |
| | 4297 | /// Default virtual memory offset corresponds to the size of __PAGEZERO segment and |
| | 4298 | /// start of __TEXT segment. |
| | 4299 | pub const default_pagezero_vmsize: u64 = 0x100000000; |
| | 4300 | |
| | 4301 | /// We commit 0x1000 = 4096 bytes of space to the header and |
| | 4302 | /// the table of load commands. This should be plenty for any |
| | 4303 | /// potential future extensions. |
| | 4304 | pub const default_headerpad_size: u32 = 0x1000; |