| ... | ... | @@ -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 | 1 | base: File, |
| 98 | 2 | |
| 99 | 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 | 57 | |
| 154 | 58 | got_table: TableSection(SymbolWithLoc) = .{}, |
| 155 | 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 | 65 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 158 | 66 | misc_errors: std.ArrayListUnmanaged(File.ErrorMsg) = .{}, |
| ... | ... | @@ -225,101 +133,6 @@ tlv_table: TlvSymbolTable = .{}, |
| 225 | 133 | /// Hot-code swapping state. |
| 226 | 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 | 136 | pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { |
| 324 | 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 | 435 | defer actions.deinit(); |
| 623 | 436 | try self.resolveSymbolsInDylibs(&actions); |
| 624 | 437 | |
| 438 | if (self.getEntryPoint() == null) { |
| 439 | self.error_flags.no_entry_point_found = true; |
| 440 | } |
| 441 | |
| 625 | 442 | if (self.unresolved.count() > 0) { |
| 626 | 443 | for (self.unresolved.keys()) |index| { |
| 627 | 444 | // TODO: convert into compiler errors. |
| ... | ... | @@ -641,6 +458,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 641 | 458 | try self.createDyldPrivateAtom(); |
| 642 | 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 | 471 | try self.allocateSpecialSymbols(); |
| 645 | 472 | |
| 646 | 473 | for (self.relocs.keys()) |atom_index| { |
| ... | ... | @@ -732,16 +559,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 732 | 559 | .Exe => blk: { |
| 733 | 560 | const seg_id = self.header_segment_cmd_index.?; |
| 734 | 561 | const seg = self.segments.items[seg_id]; |
| 735 | | const global = self.getEntryPoint() catch |err| switch (err) { |
| 736 | | error.MissingMainEntrypoint => { |
| 737 | | self.error_flags.no_entry_point_found = true; |
| 738 | | break :blk; |
| 739 | | }, |
| 740 | | else => |e| return e, |
| 741 | | }; |
| 562 | const global = self.getEntryPoint() orelse break :blk; |
| 742 | 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 | 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 | 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 | 1625 | |
| 1797 | 1626 | self.got_table.deinit(gpa); |
| 1798 | 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 | 1636 | self.strtab.deinit(gpa); |
| 1800 | 1637 | |
| 1801 | 1638 | self.locals.deinit(gpa); |
| ... | ... | @@ -4019,14 +3856,29 @@ pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.In |
| 4019 | 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 | 3878 | /// Asserts output mode is executable. |
| 4024 | | pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc { |
| 3879 | pub fn getEntryPoint(self: MachO) ?SymbolWithLoc { |
| 4025 | 3880 | const entry_name = self.base.options.entry orelse load_commands.default_entry_point; |
| 4026 | | const global = self.getGlobal(entry_name) orelse { |
| 4027 | | log.err("entrypoint '{s}' not found", .{entry_name}); |
| 4028 | | return error.MissingMainEntrypoint; |
| 4029 | | }; |
| 3881 | const global = self.getGlobal(entry_name) orelse return null; |
| 4030 | 3882 | return global; |
| 4031 | 3883 | } |
| 4032 | 3884 | |
| ... | ... | @@ -4258,3 +4110,195 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index) void { |
| 4258 | 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; |