master
   1data: std.ArrayList(u8) = .empty,
   2/// Externally owned memory.
   3basename: []const u8,
   4index: File.Index,
   5
   6symtab: std.MultiArrayList(Nlist) = .{},
   7strtab: StringTable = .{},
   8
   9symbols: std.ArrayList(Symbol) = .empty,
  10symbols_extra: std.ArrayList(u32) = .empty,
  11globals: std.ArrayList(MachO.SymbolResolver.Index) = .empty,
  12/// Maps string index (so name) into nlist index for the global symbol defined within this
  13/// module.
  14globals_lookup: std.AutoHashMapUnmanaged(u32, u32) = .empty,
  15atoms: std.ArrayList(Atom) = .empty,
  16atoms_indexes: std.ArrayList(Atom.Index) = .empty,
  17atoms_extra: std.ArrayList(u32) = .empty,
  18
  19/// Table of tracked LazySymbols.
  20lazy_syms: LazySymbolTable = .{},
  21
  22/// Table of tracked Navs.
  23navs: NavTable = .{},
  24
  25/// Table of tracked Uavs.
  26uavs: UavTable = .{},
  27
  28/// TLV initializers indexed by Atom.Index.
  29tlv_initializers: TlvInitializerTable = .{},
  30
  31/// A table of relocations.
  32relocs: RelocationTable = .{},
  33
  34dwarf: ?Dwarf = null,
  35
  36output_symtab_ctx: MachO.SymtabCtx = .{},
  37output_ar_state: Archive.ArState = .{},
  38
  39debug_strtab_dirty: bool = false,
  40debug_abbrev_dirty: bool = false,
  41debug_aranges_dirty: bool = false,
  42debug_info_header_dirty: bool = false,
  43debug_line_header_dirty: bool = false,
  44
  45pub fn init(self: *ZigObject, macho_file: *MachO) !void {
  46    const tracy = trace(@src());
  47    defer tracy.end();
  48
  49    const comp = macho_file.base.comp;
  50    const gpa = comp.gpa;
  51
  52    try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) }); // null input section
  53    try self.strtab.buffer.append(gpa, 0);
  54
  55    switch (comp.config.debug_format) {
  56        .strip => {},
  57        .dwarf => |v| {
  58            self.dwarf = Dwarf.init(&macho_file.base, v);
  59            self.debug_strtab_dirty = true;
  60            self.debug_abbrev_dirty = true;
  61            self.debug_aranges_dirty = true;
  62            self.debug_info_header_dirty = true;
  63            self.debug_line_header_dirty = true;
  64        },
  65        .code_view => unreachable,
  66    }
  67}
  68
  69pub fn deinit(self: *ZigObject, allocator: Allocator) void {
  70    self.data.deinit(allocator);
  71    self.symtab.deinit(allocator);
  72    self.strtab.deinit(allocator);
  73    self.symbols.deinit(allocator);
  74    self.symbols_extra.deinit(allocator);
  75    self.globals.deinit(allocator);
  76    self.globals_lookup.deinit(allocator);
  77    self.atoms.deinit(allocator);
  78    self.atoms_indexes.deinit(allocator);
  79    self.atoms_extra.deinit(allocator);
  80
  81    for (self.navs.values()) |*meta| {
  82        meta.exports.deinit(allocator);
  83    }
  84    self.navs.deinit(allocator);
  85
  86    self.lazy_syms.deinit(allocator);
  87
  88    for (self.uavs.values()) |*meta| {
  89        meta.exports.deinit(allocator);
  90    }
  91    self.uavs.deinit(allocator);
  92
  93    for (self.relocs.items) |*list| {
  94        list.deinit(allocator);
  95    }
  96    self.relocs.deinit(allocator);
  97
  98    for (self.tlv_initializers.values()) |*tlv_init| {
  99        tlv_init.deinit(allocator);
 100    }
 101    self.tlv_initializers.deinit(allocator);
 102
 103    if (self.dwarf) |*dwarf| {
 104        dwarf.deinit();
 105    }
 106}
 107
 108fn newSymbol(self: *ZigObject, allocator: Allocator, name: MachO.String, args: struct {
 109    type: u8 = macho.N_UNDF | macho.N_EXT,
 110    desc: u16 = 0,
 111}) !Symbol.Index {
 112    try self.symtab.ensureUnusedCapacity(allocator, 1);
 113    try self.symbols.ensureUnusedCapacity(allocator, 1);
 114    try self.symbols_extra.ensureUnusedCapacity(allocator, @sizeOf(Symbol.Extra));
 115    try self.globals.ensureUnusedCapacity(allocator, 1);
 116
 117    const index = self.addSymbolAssumeCapacity();
 118    const symbol = &self.symbols.items[index];
 119    symbol.name = name;
 120    symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
 121
 122    const nlist_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity());
 123    self.symtab.set(nlist_idx, .{
 124        .nlist = .{
 125            .n_strx = name.pos,
 126            .n_type = @bitCast(args.type),
 127            .n_sect = 0,
 128            .n_desc = @bitCast(args.desc),
 129            .n_value = 0,
 130        },
 131        .size = 0,
 132        .atom = 0,
 133    });
 134    symbol.nlist_idx = nlist_idx;
 135
 136    self.globals.appendAssumeCapacity(0);
 137
 138    return index;
 139}
 140
 141fn newAtom(self: *ZigObject, allocator: Allocator, name: MachO.String, macho_file: *MachO) !Atom.Index {
 142    try self.atoms.ensureUnusedCapacity(allocator, 1);
 143    try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
 144    try self.atoms_indexes.ensureUnusedCapacity(allocator, 1);
 145    try self.relocs.ensureUnusedCapacity(allocator, 1);
 146
 147    const index = self.addAtomAssumeCapacity();
 148    self.atoms_indexes.appendAssumeCapacity(index);
 149    const atom = self.getAtom(index).?;
 150    atom.name = name;
 151
 152    const relocs_index = @as(u32, @intCast(self.relocs.items.len));
 153    self.relocs.addOneAssumeCapacity().* = .{};
 154    atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file);
 155
 156    return index;
 157}
 158
 159fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name: MachO.String, macho_file: *MachO) !Symbol.Index {
 160    const atom_index = try self.newAtom(allocator, name, macho_file);
 161    const sym_index = try self.newSymbol(allocator, name, .{ .type = macho.N_SECT });
 162    const sym = &self.symbols.items[sym_index];
 163    sym.atom_ref = .{ .index = atom_index, .file = self.index };
 164    self.symtab.items(.atom)[sym.nlist_idx] = atom_index;
 165    return sym_index;
 166}
 167
 168pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8) !void {
 169    assert(atom.file == self.index);
 170    assert(atom.size == buffer.len);
 171    const isec = atom.getInputSection(macho_file);
 172    assert(!isec.isZerofill());
 173
 174    switch (isec.type()) {
 175        macho.S_THREAD_LOCAL_REGULAR => {
 176            const tlv = self.tlv_initializers.get(atom.atom_index).?;
 177            @memcpy(buffer, tlv.data);
 178        },
 179        macho.S_THREAD_LOCAL_VARIABLES => {
 180            @memset(buffer, 0);
 181        },
 182        else => {
 183            const sect = macho_file.sections.items(.header)[atom.out_n_sect];
 184            const file_offset = sect.offset + atom.value;
 185            const amt = try macho_file.base.file.?.preadAll(buffer, file_offset);
 186            if (amt != buffer.len) return error.InputOutput;
 187        },
 188    }
 189}
 190
 191pub fn getAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) []const Relocation {
 192    const extra = atom.getExtra(macho_file);
 193    const relocs = self.relocs.items[extra.rel_index];
 194    return relocs.items[0..extra.rel_count];
 195}
 196
 197pub fn freeAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) void {
 198    const extra = atom.getExtra(macho_file);
 199    self.relocs.items[extra.rel_index].clearRetainingCapacity();
 200}
 201
 202pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) !void {
 203    const tracy = trace(@src());
 204    defer tracy.end();
 205
 206    const gpa = macho_file.base.comp.gpa;
 207
 208    for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| {
 209        if (!nlist.n_type.bits.ext) continue;
 210        if (nlist.n_type.bits.type == .sect) {
 211            const atom = self.getAtom(atom_index).?;
 212            if (!atom.isAlive()) continue;
 213        }
 214
 215        const gop = try macho_file.resolver.getOrPut(gpa, .{
 216            .index = @intCast(i),
 217            .file = self.index,
 218        }, macho_file);
 219        if (!gop.found_existing) {
 220            gop.ref.* = .{ .index = 0, .file = 0 };
 221        }
 222        global.* = gop.index;
 223
 224        if (nlist.n_type.bits.type == .undf and !nlist.tentative()) continue;
 225        if (gop.ref.getFile(macho_file) == null) {
 226            gop.ref.* = .{ .index = @intCast(i), .file = self.index };
 227            continue;
 228        }
 229
 230        if (self.asFile().getSymbolRank(.{
 231            .archive = false,
 232            .weak = nlist.n_desc.weak_def_or_ref_to_weak,
 233            .tentative = nlist.tentative(),
 234        }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
 235            gop.ref.* = .{ .index = @intCast(i), .file = self.index };
 236        }
 237    }
 238}
 239
 240pub fn markLive(self: *ZigObject, macho_file: *MachO) void {
 241    const tracy = trace(@src());
 242    defer tracy.end();
 243
 244    for (0..self.symbols.items.len) |i| {
 245        const nlist = self.symtab.items(.nlist)[i];
 246        if (!nlist.n_type.bits.ext) continue;
 247
 248        const ref = self.getSymbolRef(@intCast(i), macho_file);
 249        const file = ref.getFile(macho_file) orelse continue;
 250        const sym = ref.getSymbol(macho_file).?;
 251        const should_keep = nlist.n_type.bits.type == .undf or (nlist.tentative() and !sym.flags.tentative);
 252        if (should_keep and file == .object and !file.object.alive) {
 253            file.object.alive = true;
 254            file.object.markLive(macho_file);
 255        }
 256    }
 257}
 258
 259pub fn mergeSymbolVisibility(self: *ZigObject, macho_file: *MachO) void {
 260    const tracy = trace(@src());
 261    defer tracy.end();
 262
 263    for (self.symbols.items, 0..) |sym, i| {
 264        const ref = self.getSymbolRef(@intCast(i), macho_file);
 265        const global = ref.getSymbol(macho_file) orelse continue;
 266        if (sym.visibility.rank() < global.visibility.rank()) {
 267            global.visibility = sym.visibility;
 268        }
 269        if (sym.flags.weak_ref) {
 270            global.flags.weak_ref = true;
 271        }
 272    }
 273}
 274
 275pub fn resolveLiterals(self: *ZigObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
 276    _ = self;
 277    _ = lp;
 278    _ = macho_file;
 279    // TODO
 280}
 281
 282pub fn dedupLiterals(self: *ZigObject, lp: MachO.LiteralPool, macho_file: *MachO) void {
 283    _ = self;
 284    _ = lp;
 285    _ = macho_file;
 286    // TODO
 287}
 288
 289/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
 290/// We need this so that we can write to an archive.
 291/// TODO implement writing ZigObject data directly to a buffer instead.
 292pub fn readFileContents(self: *ZigObject, macho_file: *MachO) !void {
 293    const diags = &macho_file.base.comp.link_diags;
 294    // Size of the output object file is always the offset + size of the strtab
 295    const size = macho_file.symtab_cmd.stroff + macho_file.symtab_cmd.strsize;
 296    const gpa = macho_file.base.comp.gpa;
 297    try self.data.resize(gpa, size);
 298    const amt = macho_file.base.file.?.preadAll(self.data.items, 0) catch |err|
 299        return diags.fail("failed to read output file: {s}", .{@errorName(err)});
 300    if (amt != size)
 301        return diags.fail("unexpected EOF reading from output file", .{});
 302}
 303
 304pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {
 305    const gpa = macho_file.base.comp.gpa;
 306    for (self.symbols.items, 0..) |sym, i| {
 307        const ref = self.getSymbolRef(@intCast(i), macho_file);
 308        const file = ref.getFile(macho_file).?;
 309        assert(file.getIndex() == self.index);
 310        if (!sym.flags.@"export") continue;
 311        const off = try ar_symtab.strtab.insert(gpa, sym.getName(macho_file));
 312        try ar_symtab.entries.append(gpa, .{ .off = off, .file = self.index });
 313    }
 314}
 315
 316pub fn updateArSize(self: *ZigObject) void {
 317    self.output_ar_state.size = self.data.items.len;
 318}
 319
 320pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !void {
 321    // Header
 322    const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
 323    try Archive.writeHeader(self.basename, size, ar_format, writer);
 324    // Data
 325    try writer.writeAll(self.data.items);
 326}
 327
 328pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void {
 329    const tracy = trace(@src());
 330    defer tracy.end();
 331
 332    for (self.symbols.items, 0..) |*sym, i| {
 333        const nlist = self.symtab.items(.nlist)[i];
 334        if (!nlist.n_type.bits.ext) continue;
 335        if (nlist.n_type.bits.type != .undf) continue;
 336
 337        if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
 338
 339        const is_import = switch (macho_file.undefined_treatment) {
 340            .@"error" => false,
 341            .warn, .suppress => nlist.weakRef(),
 342            .dynamic_lookup => true,
 343        };
 344        if (is_import) {
 345            sym.value = 0;
 346            sym.atom_ref = .{ .index = 0, .file = 0 };
 347            sym.flags.weak = false;
 348            sym.flags.weak_ref = nlist.weakRef();
 349            sym.flags.import = is_import;
 350            sym.visibility = .global;
 351
 352            const idx = self.globals.items[i];
 353            macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
 354        }
 355    }
 356}
 357
 358pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
 359    for (self.getAtoms()) |atom_index| {
 360        const atom = self.getAtom(atom_index) orelse continue;
 361        if (!atom.isAlive()) continue;
 362        const sect = atom.getInputSection(macho_file);
 363        if (sect.isZerofill()) continue;
 364        try atom.scanRelocs(macho_file);
 365    }
 366}
 367
 368pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
 369    const gpa = macho_file.base.comp.gpa;
 370    const diags = &macho_file.base.comp.link_diags;
 371
 372    var has_error = false;
 373    for (self.getAtoms()) |atom_index| {
 374        const atom = self.getAtom(atom_index) orelse continue;
 375        if (!atom.isAlive()) continue;
 376        const sect = &macho_file.sections.items(.header)[atom.out_n_sect];
 377        if (sect.isZerofill()) continue;
 378        if (!macho_file.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately
 379        if (atom.getRelocs(macho_file).len == 0) continue;
 380        // TODO: we will resolve and write ZigObject's TLS data twice:
 381        // once here, and once in writeAtoms
 382        const atom_size = try macho_file.cast(usize, atom.size);
 383        const code = try gpa.alloc(u8, atom_size);
 384        defer gpa.free(code);
 385        self.getAtomData(macho_file, atom.*, code) catch |err| {
 386            switch (err) {
 387                error.InputOutput => return diags.fail("fetching code for '{s}' failed", .{
 388                    atom.getName(macho_file),
 389                }),
 390                else => |e| return diags.fail("failed to fetch code for '{s}': {s}", .{
 391                    atom.getName(macho_file), @errorName(e),
 392                }),
 393            }
 394            has_error = true;
 395            continue;
 396        };
 397        const file_offset = sect.offset + atom.value;
 398        atom.resolveRelocs(macho_file, code) catch |err| {
 399            switch (err) {
 400                error.ResolveFailed => {},
 401                else => |e| return diags.fail("failed to resolve relocations: {s}", .{@errorName(e)}),
 402            }
 403            has_error = true;
 404            continue;
 405        };
 406        try macho_file.pwriteAll(code, file_offset);
 407    }
 408
 409    if (has_error) return error.ResolveFailed;
 410}
 411
 412pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {
 413    for (self.getAtoms()) |atom_index| {
 414        const atom = self.getAtom(atom_index) orelse continue;
 415        if (!atom.isAlive()) continue;
 416        const header = &macho_file.sections.items(.header)[atom.out_n_sect];
 417        if (header.isZerofill()) continue;
 418        if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
 419        const nreloc = atom.calcNumRelocs(macho_file);
 420        atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file);
 421        header.nreloc += nreloc;
 422    }
 423}
 424
 425pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) error{ LinkFailure, OutOfMemory }!void {
 426    const gpa = macho_file.base.comp.gpa;
 427    const diags = &macho_file.base.comp.link_diags;
 428
 429    for (self.getAtoms()) |atom_index| {
 430        const atom = self.getAtom(atom_index) orelse continue;
 431        if (!atom.isAlive()) continue;
 432        const header = macho_file.sections.items(.header)[atom.out_n_sect];
 433        const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
 434        if (header.isZerofill()) continue;
 435        if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
 436        if (atom.getRelocs(macho_file).len == 0) continue;
 437        const extra = atom.getExtra(macho_file);
 438        const atom_size = try macho_file.cast(usize, atom.size);
 439        const code = try gpa.alloc(u8, atom_size);
 440        defer gpa.free(code);
 441        self.getAtomData(macho_file, atom.*, code) catch |err|
 442            return diags.fail("failed to fetch code for '{s}': {s}", .{ atom.getName(macho_file), @errorName(err) });
 443        const file_offset = header.offset + atom.value;
 444        try atom.writeRelocs(macho_file, code, relocs[extra.rel_out_index..][0..extra.rel_out_count]);
 445        try macho_file.pwriteAll(code, file_offset);
 446    }
 447}
 448
 449// TODO we need this because not everything gets written out incrementally.
 450// For example, TLS data gets written out via traditional route.
 451// Is there any better way of handling this?
 452pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void {
 453    const tracy = trace(@src());
 454    defer tracy.end();
 455
 456    for (self.getAtoms()) |atom_index| {
 457        const atom = self.getAtom(atom_index) orelse continue;
 458        if (!atom.isAlive()) continue;
 459        const sect = atom.getInputSection(macho_file);
 460        if (sect.isZerofill()) continue;
 461        if (macho_file.isZigSection(atom.out_n_sect)) continue;
 462        if (atom.getRelocs(macho_file).len == 0) continue;
 463        const off = try macho_file.cast(usize, atom.value);
 464        const size = try macho_file.cast(usize, atom.size);
 465        const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
 466        try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
 467        const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
 468        const extra = atom.getExtra(macho_file);
 469        try atom.writeRelocs(macho_file, buffer[off..][0..size], relocs[extra.rel_out_index..][0..extra.rel_out_count]);
 470    }
 471}
 472
 473// TODO we need this because not everything gets written out incrementally.
 474// For example, TLS data gets written out via traditional route.
 475// Is there any better way of handling this?
 476pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void {
 477    const tracy = trace(@src());
 478    defer tracy.end();
 479
 480    for (self.getAtoms()) |atom_index| {
 481        const atom = self.getAtom(atom_index) orelse continue;
 482        if (!atom.isAlive()) continue;
 483        const sect = atom.getInputSection(macho_file);
 484        if (sect.isZerofill()) continue;
 485        if (macho_file.isZigSection(atom.out_n_sect)) continue;
 486        const off = try macho_file.cast(usize, atom.value);
 487        const size = try macho_file.cast(usize, atom.size);
 488        const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
 489        try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
 490        try atom.resolveRelocs(macho_file, buffer[off..][0..size]);
 491    }
 492}
 493
 494pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void {
 495    const tracy = trace(@src());
 496    defer tracy.end();
 497
 498    for (self.symbols.items, 0..) |*sym, i| {
 499        const ref = self.getSymbolRef(@intCast(i), macho_file);
 500        const file = ref.getFile(macho_file) orelse continue;
 501        if (file.getIndex() != self.index) continue;
 502        if (sym.getAtom(macho_file)) |atom| if (!atom.isAlive()) continue;
 503        if (macho_file.discard_local_symbols and sym.isLocal()) continue;
 504        const name = sym.getName(macho_file);
 505        assert(name.len > 0);
 506        sym.flags.output_symtab = true;
 507        if (sym.isLocal()) {
 508            sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
 509            self.output_symtab_ctx.nlocals += 1;
 510        } else if (sym.flags.@"export") {
 511            sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
 512            self.output_symtab_ctx.nexports += 1;
 513        } else {
 514            assert(sym.flags.import);
 515            sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
 516            self.output_symtab_ctx.nimports += 1;
 517        }
 518        self.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + 1));
 519    }
 520}
 521
 522pub fn writeSymtab(self: ZigObject, macho_file: *MachO, ctx: anytype) void {
 523    const tracy = trace(@src());
 524    defer tracy.end();
 525
 526    var n_strx = self.output_symtab_ctx.stroff;
 527    for (self.symbols.items, 0..) |sym, i| {
 528        const ref = self.getSymbolRef(@intCast(i), macho_file);
 529        const file = ref.getFile(macho_file) orelse continue;
 530        if (file.getIndex() != self.index) continue;
 531        const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
 532        const out_sym = &ctx.symtab.items[idx];
 533        out_sym.n_strx = n_strx;
 534        sym.setOutputSym(macho_file, out_sym);
 535        const name = sym.getName(macho_file);
 536        @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
 537        n_strx += @intCast(name.len);
 538        ctx.strtab.items[n_strx] = 0;
 539        n_strx += 1;
 540    }
 541}
 542
 543pub fn getInputSection(self: ZigObject, atom: Atom, macho_file: *MachO) macho.section_64 {
 544    _ = self;
 545    var sect = macho_file.sections.items(.header)[atom.out_n_sect];
 546    sect.addr = 0;
 547    sect.offset = 0;
 548    sect.size = atom.size;
 549    sect.@"align" = atom.alignment.toLog2Units();
 550    return sect;
 551}
 552
 553pub fn flush(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.File.FlushError!void {
 554    const diags = &macho_file.base.comp.link_diags;
 555
 556    // Handle any lazy symbols that were emitted by incremental compilation.
 557    if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| {
 558        const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid);
 559        defer pt.deactivate();
 560
 561        // Most lazy symbols can be updated on first use, but
 562        // anyerror needs to wait for everything to be flushed.
 563        if (metadata.text_state != .unused) self.updateLazySymbol(
 564            macho_file,
 565            pt,
 566            .{ .kind = .code, .ty = .anyerror_type },
 567            metadata.text_symbol_index,
 568        ) catch |err| switch (err) {
 569            error.OutOfMemory => return error.OutOfMemory,
 570            error.LinkFailure => return error.LinkFailure,
 571            else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}),
 572        };
 573        if (metadata.const_state != .unused) self.updateLazySymbol(
 574            macho_file,
 575            pt,
 576            .{ .kind = .const_data, .ty = .anyerror_type },
 577            metadata.const_symbol_index,
 578        ) catch |err| switch (err) {
 579            error.OutOfMemory => return error.OutOfMemory,
 580            error.LinkFailure => return error.LinkFailure,
 581            else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}),
 582        };
 583    }
 584    for (self.lazy_syms.values()) |*metadata| {
 585        if (metadata.text_state != .unused) metadata.text_state = .flushed;
 586        if (metadata.const_state != .unused) metadata.const_state = .flushed;
 587    }
 588
 589    if (self.dwarf) |*dwarf| {
 590        const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid);
 591        defer pt.deactivate();
 592        dwarf.flush(pt) catch |err| switch (err) {
 593            error.OutOfMemory => return error.OutOfMemory,
 594            else => |e| return diags.fail("failed to flush dwarf module: {s}", .{@errorName(e)}),
 595        };
 596
 597        self.debug_abbrev_dirty = false;
 598        self.debug_aranges_dirty = false;
 599        self.debug_strtab_dirty = false;
 600    }
 601
 602    // The point of flush() is to commit changes, so in theory, nothing should
 603    // be dirty after this. However, it is possible for some things to remain
 604    // dirty because they fail to be written in the event of compile errors,
 605    // such as debug_line_header_dirty and debug_info_header_dirty.
 606    assert(!self.debug_abbrev_dirty);
 607    assert(!self.debug_aranges_dirty);
 608    assert(!self.debug_strtab_dirty);
 609}
 610
 611pub fn getNavVAddr(
 612    self: *ZigObject,
 613    macho_file: *MachO,
 614    pt: Zcu.PerThread,
 615    nav_index: InternPool.Nav.Index,
 616    reloc_info: link.File.RelocInfo,
 617) !u64 {
 618    const zcu = pt.zcu;
 619    const ip = &zcu.intern_pool;
 620    const nav = ip.getNav(nav_index);
 621    log.debug("getNavVAddr {f}({d})", .{ nav.fqn.fmt(ip), nav_index });
 622    const sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
 623        macho_file,
 624        nav.name.toSlice(ip),
 625        @"extern".lib_name.toSlice(ip),
 626    ) else try self.getOrCreateMetadataForNav(macho_file, nav_index);
 627    const sym = self.symbols.items[sym_index];
 628    const vaddr = sym.getAddress(.{}, macho_file);
 629    switch (reloc_info.parent) {
 630        .none => unreachable,
 631        .atom_index => |atom_index| {
 632            const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?;
 633            try parent_atom.addReloc(macho_file, .{
 634                .tag = .@"extern",
 635                .offset = @intCast(reloc_info.offset),
 636                .target = sym_index,
 637                .addend = reloc_info.addend,
 638                .type = .unsigned,
 639                .meta = .{
 640                    .pcrel = false,
 641                    .has_subtractor = false,
 642                    .length = 3,
 643                    .symbolnum = @intCast(sym.nlist_idx),
 644                },
 645            });
 646        },
 647        .debug_output => |debug_output| switch (debug_output) {
 648            .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
 649                .source_off = @intCast(reloc_info.offset),
 650                .target_sym = sym_index,
 651                .target_off = reloc_info.addend,
 652            }),
 653            .none => unreachable,
 654        },
 655    }
 656    return vaddr;
 657}
 658
 659pub fn getUavVAddr(
 660    self: *ZigObject,
 661    macho_file: *MachO,
 662    uav: InternPool.Index,
 663    reloc_info: link.File.RelocInfo,
 664) !u64 {
 665    const sym_index = self.uavs.get(uav).?.symbol_index;
 666    const sym = self.symbols.items[sym_index];
 667    const vaddr = sym.getAddress(.{}, macho_file);
 668    switch (reloc_info.parent) {
 669        .none => unreachable,
 670        .atom_index => |atom_index| {
 671            const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?;
 672            try parent_atom.addReloc(macho_file, .{
 673                .tag = .@"extern",
 674                .offset = @intCast(reloc_info.offset),
 675                .target = sym_index,
 676                .addend = reloc_info.addend,
 677                .type = .unsigned,
 678                .meta = .{
 679                    .pcrel = false,
 680                    .has_subtractor = false,
 681                    .length = 3,
 682                    .symbolnum = @intCast(sym.nlist_idx),
 683                },
 684            });
 685        },
 686        .debug_output => |debug_output| switch (debug_output) {
 687            .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
 688                .source_off = @intCast(reloc_info.offset),
 689                .target_sym = sym_index,
 690                .target_off = reloc_info.addend,
 691            }),
 692            .none => unreachable,
 693        },
 694    }
 695    return vaddr;
 696}
 697
 698pub fn lowerUav(
 699    self: *ZigObject,
 700    macho_file: *MachO,
 701    pt: Zcu.PerThread,
 702    uav: InternPool.Index,
 703    explicit_alignment: Atom.Alignment,
 704    src_loc: Zcu.LazySrcLoc,
 705) !codegen.SymbolResult {
 706    const zcu = pt.zcu;
 707    const gpa = zcu.gpa;
 708    const val = Value.fromInterned(uav);
 709    const uav_alignment = switch (explicit_alignment) {
 710        .none => val.typeOf(zcu).abiAlignment(zcu),
 711        else => explicit_alignment,
 712    };
 713    if (self.uavs.get(uav)) |metadata| {
 714        const sym = self.symbols.items[metadata.symbol_index];
 715        const existing_alignment = sym.getAtom(macho_file).?.alignment;
 716        if (uav_alignment.order(existing_alignment).compare(.lte))
 717            return .{ .sym_index = metadata.symbol_index };
 718    }
 719
 720    var name_buf: [32]u8 = undefined;
 721    const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{
 722        @intFromEnum(uav),
 723    }) catch unreachable;
 724    const res = self.lowerConst(
 725        macho_file,
 726        pt,
 727        name,
 728        val,
 729        uav_alignment,
 730        macho_file.zig_const_sect_index.?,
 731        src_loc,
 732    ) catch |err| switch (err) {
 733        error.OutOfMemory => return error.OutOfMemory,
 734        else => |e| return .{ .fail = try Zcu.ErrorMsg.create(
 735            gpa,
 736            src_loc,
 737            "unable to lower constant value: {s}",
 738            .{@errorName(e)},
 739        ) },
 740    };
 741    switch (res) {
 742        .sym_index => |sym_index| try self.uavs.put(gpa, uav, .{ .symbol_index = sym_index }),
 743        .fail => {},
 744    }
 745    return res;
 746}
 747
 748fn freeNavMetadata(self: *ZigObject, macho_file: *MachO, sym_index: Symbol.Index) void {
 749    const sym = self.symbols.items[sym_index];
 750    sym.getAtom(macho_file).?.free(macho_file);
 751    log.debug("adding %{d} to local symbols free list", .{sym_index});
 752    // TODO redo this
 753    // TODO free GOT entry here
 754}
 755
 756pub fn freeNav(self: *ZigObject, macho_file: *MachO, nav_index: InternPool.Nav.Index) void {
 757    const gpa = macho_file.base.comp.gpa;
 758    log.debug("freeNav 0x{x}", .{nav_index});
 759
 760    if (self.navs.fetchRemove(nav_index)) |const_kv| {
 761        var kv = const_kv;
 762        const sym_index = kv.value.symbol_index;
 763        self.freeNavMetadata(macho_file, sym_index);
 764        kv.value.exports.deinit(gpa);
 765    }
 766
 767    // TODO free decl in dSYM
 768}
 769
 770pub fn updateFunc(
 771    self: *ZigObject,
 772    macho_file: *MachO,
 773    pt: Zcu.PerThread,
 774    func_index: InternPool.Index,
 775    mir: *const codegen.AnyMir,
 776) link.File.UpdateNavError!void {
 777    const tracy = trace(@src());
 778    defer tracy.end();
 779
 780    const zcu = pt.zcu;
 781    const gpa = zcu.gpa;
 782    const func = zcu.funcInfo(func_index);
 783
 784    const sym_index = try self.getOrCreateMetadataForNav(macho_file, func.owner_nav);
 785    self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
 786
 787    var aw: std.Io.Writer.Allocating = .init(gpa);
 788    defer aw.deinit();
 789
 790    var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;
 791    defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
 792
 793    codegen.emitFunction(
 794        &macho_file.base,
 795        pt,
 796        zcu.navSrcLoc(func.owner_nav),
 797        func_index,
 798        sym_index,
 799        mir,
 800        &aw.writer,
 801        if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none,
 802    ) catch |err| switch (err) {
 803        error.WriteFailed => return error.OutOfMemory,
 804        else => |e| return e,
 805    };
 806    const code = aw.written();
 807
 808    const sect_index = try self.getNavOutputSection(macho_file, zcu, func.owner_nav, code);
 809    const old_rva, const old_alignment = blk: {
 810        const atom = self.symbols.items[sym_index].getAtom(macho_file).?;
 811        break :blk .{ atom.value, atom.alignment };
 812    };
 813    try self.updateNavCode(macho_file, pt, func.owner_nav, sym_index, sect_index, code);
 814    const new_rva, const new_alignment = blk: {
 815        const atom = self.symbols.items[sym_index].getAtom(macho_file).?;
 816        break :blk .{ atom.value, atom.alignment };
 817    };
 818
 819    if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNavFunc(pt, func.owner_nav, code.len, wip_nav) catch |err|
 820        return macho_file.base.cgFail(func.owner_nav, "falied to finish dwarf function: {s}", .{@errorName(err)});
 821
 822    // Exports will be updated by `Zcu.processExports` after the update.
 823    if (old_rva != new_rva and old_rva > 0) {
 824        // If we had to reallocate the function, we re-use the existing slot for a trampoline.
 825        // In the rare case that the function has been further overaligned we skip creating a
 826        // trampoline and update all symbols referring this function.
 827        if (old_alignment.order(new_alignment) == .lt) {
 828            @panic("TODO update all symbols referring this function");
 829        }
 830
 831        // Create a trampoline to the new location at `old_rva`.
 832        if (!self.symbols.items[sym_index].flags.trampoline) {
 833            const name = try std.fmt.allocPrint(gpa, "{s}$trampoline", .{
 834                self.symbols.items[sym_index].getName(macho_file),
 835            });
 836            defer gpa.free(name);
 837            const name_off = try self.addString(gpa, name);
 838            const tr_size = trampolineSize(macho_file.getTarget().cpu.arch);
 839            const tr_sym_index = try self.newSymbolWithAtom(gpa, name_off, macho_file);
 840            const tr_sym = &self.symbols.items[tr_sym_index];
 841            tr_sym.out_n_sect = macho_file.zig_text_sect_index.?;
 842            const tr_nlist = &self.symtab.items(.nlist)[tr_sym.nlist_idx];
 843            tr_nlist.n_sect = macho_file.zig_text_sect_index.? + 1;
 844            const tr_atom = tr_sym.getAtom(macho_file).?;
 845            tr_atom.value = old_rva;
 846            tr_atom.setAlive(true);
 847            tr_atom.alignment = old_alignment;
 848            tr_atom.out_n_sect = macho_file.zig_text_sect_index.?;
 849            tr_atom.size = tr_size;
 850            self.symtab.items(.size)[tr_sym.nlist_idx] = tr_size;
 851            const target_sym = &self.symbols.items[sym_index];
 852            target_sym.addExtra(.{ .trampoline = tr_sym_index }, macho_file);
 853            target_sym.flags.trampoline = true;
 854        }
 855        const target_sym = self.symbols.items[sym_index];
 856        const source_sym = self.symbols.items[target_sym.getExtra(macho_file).trampoline];
 857        writeTrampoline(source_sym, target_sym, macho_file) catch |err|
 858            return macho_file.base.cgFail(func.owner_nav, "failed to write trampoline: {s}", .{@errorName(err)});
 859    }
 860}
 861
 862pub fn updateNav(
 863    self: *ZigObject,
 864    macho_file: *MachO,
 865    pt: Zcu.PerThread,
 866    nav_index: InternPool.Nav.Index,
 867) link.File.UpdateNavError!void {
 868    const tracy = trace(@src());
 869    defer tracy.end();
 870
 871    const zcu = pt.zcu;
 872    const ip = &zcu.intern_pool;
 873    const nav = ip.getNav(nav_index);
 874
 875    const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
 876        .func => .none,
 877        .variable => |variable| variable.init,
 878        .@"extern" => |@"extern"| {
 879            // Extern variable gets a __got entry only
 880            const name = @"extern".name.toSlice(ip);
 881            const lib_name = @"extern".lib_name.toSlice(ip);
 882            const sym_index = try self.getGlobalSymbol(macho_file, name, lib_name);
 883            if (@"extern".is_threadlocal and macho_file.base.comp.config.any_non_single_threaded) self.symbols.items[sym_index].flags.tlv = true;
 884            if (self.dwarf) |*dwarf| {
 885                var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index);
 886                defer debug_wip_nav.deinit();
 887                dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) {
 888                    error.OutOfMemory => return error.OutOfMemory,
 889                    error.Overflow => return error.Overflow,
 890                    else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
 891                };
 892            }
 893            return;
 894        },
 895        else => nav.status.fully_resolved.val,
 896    };
 897
 898    if (nav_init != .none and Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {
 899        const sym_index = try self.getOrCreateMetadataForNav(macho_file, nav_index);
 900        self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
 901
 902        var aw: std.Io.Writer.Allocating = .init(zcu.gpa);
 903        defer aw.deinit();
 904
 905        var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;
 906        defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
 907
 908        codegen.generateSymbol(
 909            &macho_file.base,
 910            pt,
 911            zcu.navSrcLoc(nav_index),
 912            Value.fromInterned(nav_init),
 913            &aw.writer,
 914            .{ .atom_index = sym_index },
 915        ) catch |err| switch (err) {
 916            error.WriteFailed => return error.OutOfMemory,
 917            else => |e| return e,
 918        };
 919        const code = aw.written();
 920
 921        const sect_index = try self.getNavOutputSection(macho_file, zcu, nav_index, code);
 922        if (isThreadlocal(macho_file, nav_index))
 923            try self.updateTlv(macho_file, pt, nav_index, sym_index, sect_index, code)
 924        else
 925            try self.updateNavCode(macho_file, pt, nav_index, sym_index, sect_index, code);
 926
 927        if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNav(pt, nav_index, wip_nav) catch |err| switch (err) {
 928            error.OutOfMemory => return error.OutOfMemory,
 929            error.Overflow => return error.Overflow,
 930            else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
 931        };
 932    } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index);
 933
 934    // Exports will be updated by `Zcu.processExports` after the update.
 935}
 936
 937fn updateNavCode(
 938    self: *ZigObject,
 939    macho_file: *MachO,
 940    pt: Zcu.PerThread,
 941    nav_index: InternPool.Nav.Index,
 942    sym_index: Symbol.Index,
 943    sect_index: u8,
 944    code: []const u8,
 945) link.File.UpdateNavError!void {
 946    const zcu = pt.zcu;
 947    const gpa = zcu.gpa;
 948    const ip = &zcu.intern_pool;
 949    const nav = ip.getNav(nav_index);
 950
 951    log.debug("updateNavCode {f} 0x{x}", .{ nav.fqn.fmt(ip), nav_index });
 952
 953    const mod = zcu.navFileScope(nav_index).mod.?;
 954    const target = &mod.resolved_target.result;
 955    const required_alignment = switch (nav.status.fully_resolved.alignment) {
 956        .none => switch (mod.optimize_mode) {
 957            .Debug, .ReleaseSafe, .ReleaseFast => target_util.defaultFunctionAlignment(target),
 958            .ReleaseSmall => target_util.minFunctionAlignment(target),
 959        },
 960        else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
 961    };
 962
 963    const sect = &macho_file.sections.items(.header)[sect_index];
 964    const sym = &self.symbols.items[sym_index];
 965    const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
 966    const atom = sym.getAtom(macho_file).?;
 967
 968    sym.out_n_sect = sect_index;
 969    atom.out_n_sect = sect_index;
 970
 971    const sym_name = try std.fmt.allocPrintSentinel(gpa, "_{s}", .{nav.fqn.toSlice(ip)}, 0);
 972    defer gpa.free(sym_name);
 973    sym.name = try self.addString(gpa, sym_name);
 974    atom.setAlive(true);
 975    atom.name = sym.name;
 976    nlist.n_strx = sym.name.pos;
 977    nlist.n_type = .{ .bits = .{ .ext = false, .type = .sect, .pext = false, .is_stab = 0 } };
 978    nlist.n_sect = sect_index + 1;
 979    self.symtab.items(.size)[sym.nlist_idx] = code.len;
 980
 981    const old_size = atom.size;
 982    const old_vaddr = atom.value;
 983    atom.alignment = required_alignment;
 984    atom.size = code.len;
 985
 986    if (old_size > 0) {
 987        const capacity = atom.capacity(macho_file);
 988        const need_realloc = code.len > capacity or !required_alignment.check(atom.value);
 989
 990        if (need_realloc) {
 991            atom.grow(macho_file) catch |err|
 992                return macho_file.base.cgFail(nav_index, "failed to grow atom: {s}", .{@errorName(err)});
 993            log.debug("growing {f} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom.value });
 994            if (old_vaddr != atom.value) {
 995                sym.value = 0;
 996                nlist.n_value = 0;
 997            }
 998        } else if (code.len < old_size) {
 999            atom.shrink(macho_file);
1000        } else if (self.getAtom(atom.next_index) == null) {
1001            const needed_size = atom.value + code.len;
1002            sect.size = needed_size;
1003        }
1004    } else {
1005        atom.allocate(macho_file) catch |err|
1006            return macho_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)});
1007        errdefer self.freeNavMetadata(macho_file, sym_index);
1008
1009        sym.value = 0;
1010        nlist.n_value = 0;
1011    }
1012
1013    if (!sect.isZerofill()) {
1014        const file_offset = sect.offset + atom.value;
1015        macho_file.base.file.?.pwriteAll(code, file_offset) catch |err|
1016            return macho_file.base.cgFail(nav_index, "failed to write output file: {s}", .{@errorName(err)});
1017    }
1018}
1019
1020/// Lowering a TLV on macOS involves two stages:
1021/// 1. first we lower the initializer into appopriate section (__thread_data or __thread_bss)
1022/// 2. next, we create a corresponding threadlocal variable descriptor in __thread_vars
1023fn updateTlv(
1024    self: *ZigObject,
1025    macho_file: *MachO,
1026    pt: Zcu.PerThread,
1027    nav_index: InternPool.Nav.Index,
1028    sym_index: Symbol.Index,
1029    sect_index: u8,
1030    code: []const u8,
1031) !void {
1032    const ip = &pt.zcu.intern_pool;
1033    const nav = ip.getNav(nav_index);
1034
1035    log.debug("updateTlv {f} (0x{x})", .{ nav.fqn.fmt(ip), nav_index });
1036
1037    // 1. Lower TLV initializer
1038    const init_sym_index = try self.createTlvInitializer(
1039        macho_file,
1040        nav.fqn.toSlice(ip),
1041        pt.navAlignment(nav_index),
1042        sect_index,
1043        code,
1044    );
1045
1046    // 2. Create TLV descriptor
1047    try self.createTlvDescriptor(macho_file, sym_index, init_sym_index, nav.fqn.toSlice(ip));
1048}
1049
1050fn createTlvInitializer(
1051    self: *ZigObject,
1052    macho_file: *MachO,
1053    name: []const u8,
1054    alignment: Atom.Alignment,
1055    sect_index: u8,
1056    code: []const u8,
1057) !Symbol.Index {
1058    const gpa = macho_file.base.comp.gpa;
1059    const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name});
1060    defer gpa.free(sym_name);
1061    const string = try self.addString(gpa, sym_name);
1062
1063    const sym_index = try self.newSymbolWithAtom(gpa, string, macho_file);
1064    const sym = &self.symbols.items[sym_index];
1065    const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1066    const atom = sym.getAtom(macho_file).?;
1067    sym.out_n_sect = sect_index;
1068    atom.out_n_sect = sect_index;
1069    atom.setAlive(true);
1070    atom.alignment = alignment;
1071    atom.size = code.len;
1072    nlist.n_sect = sect_index + 1;
1073    self.symtab.items(.size)[sym.nlist_idx] = code.len;
1074
1075    const slice = macho_file.sections.slice();
1076    const header = slice.items(.header)[sect_index];
1077
1078    const gop = try self.tlv_initializers.getOrPut(gpa, atom.atom_index);
1079    assert(!gop.found_existing); // TODO incremental updates
1080    gop.value_ptr.* = .{ .symbol_index = sym_index };
1081
1082    // We only store the data for the TLV if it's non-zerofill.
1083    if (!header.isZerofill()) {
1084        gop.value_ptr.data = try gpa.dupe(u8, code);
1085    }
1086
1087    return sym_index;
1088}
1089
1090fn createTlvDescriptor(
1091    self: *ZigObject,
1092    macho_file: *MachO,
1093    sym_index: Symbol.Index,
1094    init_sym_index: Symbol.Index,
1095    name: []const u8,
1096) !void {
1097    const gpa = macho_file.base.comp.gpa;
1098
1099    const sym = &self.symbols.items[sym_index];
1100    const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1101    const atom = sym.getAtom(macho_file).?;
1102    const alignment = Atom.Alignment.fromNonzeroByteUnits(@alignOf(u64));
1103    const size: u64 = @sizeOf(u64) * 3;
1104
1105    const sect_index = macho_file.getSectionByName("__DATA", "__thread_vars") orelse
1106        try macho_file.addSection("__DATA", "__thread_vars", .{
1107            .flags = macho.S_THREAD_LOCAL_VARIABLES,
1108        });
1109    sym.out_n_sect = sect_index;
1110    atom.out_n_sect = sect_index;
1111
1112    sym.value = 0;
1113    sym.name = try self.addString(gpa, name);
1114    atom.setAlive(true);
1115    atom.name = sym.name;
1116    nlist.n_strx = sym.name.pos;
1117    nlist.n_sect = sect_index + 1;
1118    nlist.n_type = .{ .bits = .{ .ext = false, .type = .sect, .pext = false, .is_stab = 0 } };
1119    nlist.n_value = 0;
1120    self.symtab.items(.size)[sym.nlist_idx] = size;
1121
1122    atom.alignment = alignment;
1123    atom.size = size;
1124
1125    const tlv_bootstrap_index = try self.getGlobalSymbol(macho_file, "_tlv_bootstrap", null);
1126    try atom.addReloc(macho_file, .{
1127        .tag = .@"extern",
1128        .offset = 0,
1129        .target = tlv_bootstrap_index,
1130        .addend = 0,
1131        .type = .unsigned,
1132        .meta = .{
1133            .pcrel = false,
1134            .has_subtractor = false,
1135            .length = 3,
1136            .symbolnum = @intCast(tlv_bootstrap_index),
1137        },
1138    });
1139    try atom.addReloc(macho_file, .{
1140        .tag = .@"extern",
1141        .offset = 16,
1142        .target = init_sym_index,
1143        .addend = 0,
1144        .type = .unsigned,
1145        .meta = .{
1146            .pcrel = false,
1147            .has_subtractor = false,
1148            .length = 3,
1149            .symbolnum = @intCast(init_sym_index),
1150        },
1151    });
1152}
1153
1154fn getNavOutputSection(
1155    self: *ZigObject,
1156    macho_file: *MachO,
1157    zcu: *Zcu,
1158    nav_index: InternPool.Nav.Index,
1159    code: []const u8,
1160) error{OutOfMemory}!u8 {
1161    _ = self;
1162    const ip = &zcu.intern_pool;
1163    const nav_val = zcu.navValue(nav_index);
1164    if (ip.isFunctionType(nav_val.typeOf(zcu).toIntern())) return macho_file.zig_text_sect_index.?;
1165    const is_const, const is_threadlocal, const nav_init = switch (ip.indexToKey(nav_val.toIntern())) {
1166        .variable => |variable| .{ false, variable.is_threadlocal, variable.init },
1167        .@"extern" => |@"extern"| .{ @"extern".is_const, @"extern".is_threadlocal, .none },
1168        else => .{ true, false, nav_val.toIntern() },
1169    };
1170    if (is_threadlocal and macho_file.base.comp.config.any_non_single_threaded) {
1171        for (code) |byte| {
1172            if (byte != 0) break;
1173        } else return macho_file.getSectionByName("__DATA", "__thread_bss") orelse try macho_file.addSection(
1174            "__DATA",
1175            "__thread_bss",
1176            .{ .flags = macho.S_THREAD_LOCAL_ZEROFILL },
1177        );
1178        return macho_file.getSectionByName("__DATA", "__thread_data") orelse try macho_file.addSection(
1179            "__DATA",
1180            "__thread_data",
1181            .{ .flags = macho.S_THREAD_LOCAL_REGULAR },
1182        );
1183    }
1184    if (is_const) return macho_file.zig_const_sect_index.?;
1185    if (nav_init != .none and Value.fromInterned(nav_init).isUndef(zcu))
1186        return switch (zcu.navFileScope(nav_index).mod.?.optimize_mode) {
1187            .Debug, .ReleaseSafe => macho_file.zig_data_sect_index.?,
1188            .ReleaseFast, .ReleaseSmall => macho_file.zig_bss_sect_index.?,
1189        };
1190    for (code) |byte| {
1191        if (byte != 0) break;
1192    } else return macho_file.zig_bss_sect_index.?;
1193    return macho_file.zig_data_sect_index.?;
1194}
1195
1196fn lowerConst(
1197    self: *ZigObject,
1198    macho_file: *MachO,
1199    pt: Zcu.PerThread,
1200    name: []const u8,
1201    val: Value,
1202    required_alignment: Atom.Alignment,
1203    output_section_index: u8,
1204    src_loc: Zcu.LazySrcLoc,
1205) !codegen.SymbolResult {
1206    const gpa = macho_file.base.comp.gpa;
1207
1208    var aw: std.Io.Writer.Allocating = .init(gpa);
1209    defer aw.deinit();
1210
1211    const name_str = try self.addString(gpa, name);
1212    const sym_index = try self.newSymbolWithAtom(gpa, name_str, macho_file);
1213
1214    codegen.generateSymbol(
1215        &macho_file.base,
1216        pt,
1217        src_loc,
1218        val,
1219        &aw.writer,
1220        .{ .atom_index = sym_index },
1221    ) catch |err| switch (err) {
1222        error.WriteFailed => return error.OutOfMemory,
1223        else => |e| return e,
1224    };
1225    const code = aw.written();
1226
1227    const sym = &self.symbols.items[sym_index];
1228    sym.out_n_sect = output_section_index;
1229
1230    const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1231    nlist.n_sect = output_section_index + 1;
1232    self.symtab.items(.size)[sym.nlist_idx] = code.len;
1233
1234    const atom = sym.getAtom(macho_file).?;
1235    atom.setAlive(true);
1236    atom.alignment = required_alignment;
1237    atom.size = code.len;
1238    atom.out_n_sect = output_section_index;
1239
1240    try atom.allocate(macho_file);
1241    // TODO rename and re-audit this method
1242    errdefer self.freeNavMetadata(macho_file, sym_index);
1243
1244    const sect = macho_file.sections.items(.header)[output_section_index];
1245    const file_offset = sect.offset + atom.value;
1246    try macho_file.pwriteAll(code, file_offset);
1247
1248    return .{ .sym_index = sym_index };
1249}
1250
1251pub fn updateExports(
1252    self: *ZigObject,
1253    macho_file: *MachO,
1254    pt: Zcu.PerThread,
1255    exported: Zcu.Exported,
1256    export_indices: []const Zcu.Export.Index,
1257) link.File.UpdateExportsError!void {
1258    const tracy = trace(@src());
1259    defer tracy.end();
1260
1261    const zcu = pt.zcu;
1262    const gpa = macho_file.base.comp.gpa;
1263    const metadata = switch (exported) {
1264        .nav => |nav| blk: {
1265            _ = try self.getOrCreateMetadataForNav(macho_file, nav);
1266            break :blk self.navs.getPtr(nav).?;
1267        },
1268        .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1269            const first_exp = export_indices[0].ptr(zcu);
1270            const res = try self.lowerUav(macho_file, pt, uav, .none, first_exp.src);
1271            switch (res) {
1272                .sym_index => {},
1273                .fail => |em| {
1274                    // TODO maybe it's enough to return an error here and let Zcu.processExportsInner
1275                    // handle the error?
1276                    try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
1277                    zcu.failed_exports.putAssumeCapacityNoClobber(export_indices[0], em);
1278                    return;
1279                },
1280            }
1281            break :blk self.uavs.getPtr(uav).?;
1282        },
1283    };
1284    const sym_index = metadata.symbol_index;
1285    const nlist_idx = self.symbols.items[sym_index].nlist_idx;
1286    const nlist = self.symtab.items(.nlist)[nlist_idx];
1287
1288    for (export_indices) |export_idx| {
1289        const exp = export_idx.ptr(zcu);
1290        if (exp.opts.section.unwrap()) |section_name| {
1291            if (!section_name.eqlSlice("__text", &zcu.intern_pool)) {
1292                try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
1293                zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, try Zcu.ErrorMsg.create(
1294                    gpa,
1295                    exp.src,
1296                    "Unimplemented: ExportOptions.section",
1297                    .{},
1298                ));
1299                continue;
1300            }
1301        }
1302        if (exp.opts.linkage == .link_once) {
1303            try zcu.failed_exports.putNoClobber(zcu.gpa, export_idx, try Zcu.ErrorMsg.create(
1304                gpa,
1305                exp.src,
1306                "Unimplemented: GlobalLinkage.link_once",
1307                .{},
1308            ));
1309            continue;
1310        }
1311
1312        const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
1313        const global_nlist_index = if (metadata.@"export"(self, exp_name)) |exp_index|
1314            exp_index.*
1315        else blk: {
1316            const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null);
1317            try metadata.exports.append(gpa, global_nlist_index);
1318            break :blk global_nlist_index;
1319        };
1320        const global_nlist = &self.symtab.items(.nlist)[global_nlist_index];
1321        const atom_index = self.symtab.items(.atom)[nlist_idx];
1322        const global_sym = &self.symbols.items[global_nlist_index];
1323        global_nlist.n_value = nlist.n_value;
1324        global_nlist.n_sect = nlist.n_sect;
1325        global_nlist.n_type = .{ .bits = .{ .ext = true, .type = .sect, .pext = false, .is_stab = 0 } };
1326        self.symtab.items(.size)[global_nlist_index] = self.symtab.items(.size)[nlist_idx];
1327        self.symtab.items(.atom)[global_nlist_index] = atom_index;
1328        global_sym.atom_ref = .{ .index = atom_index, .file = self.index };
1329
1330        switch (exp.opts.linkage) {
1331            .internal => {
1332                // Symbol should be hidden, or in MachO lingo, private extern.
1333                global_nlist.n_type.bits.pext = true;
1334                global_sym.visibility = .hidden;
1335            },
1336            .strong => {
1337                global_sym.visibility = .global;
1338            },
1339            .weak => {
1340                // Weak linkage is specified as part of n_desc field.
1341                // Symbol's n_type is like for a symbol with strong linkage.
1342                global_nlist.n_desc.weak_def_or_ref_to_weak = true;
1343                global_sym.visibility = .global;
1344                global_sym.flags.weak = true;
1345            },
1346            else => unreachable,
1347        }
1348    }
1349}
1350
1351fn updateLazySymbol(
1352    self: *ZigObject,
1353    macho_file: *MachO,
1354    pt: Zcu.PerThread,
1355    lazy_sym: link.File.LazySymbol,
1356    symbol_index: Symbol.Index,
1357) !void {
1358    const zcu = pt.zcu;
1359    const gpa = zcu.gpa;
1360
1361    var required_alignment: Atom.Alignment = .none;
1362    var aw: std.Io.Writer.Allocating = .init(gpa);
1363    defer aw.deinit();
1364
1365    const name_str = blk: {
1366        const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{
1367            @tagName(lazy_sym.kind),
1368            Type.fromInterned(lazy_sym.ty).fmt(pt),
1369        });
1370        defer gpa.free(name);
1371        break :blk try self.addString(gpa, name);
1372    };
1373
1374    const src = Type.fromInterned(lazy_sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;
1375    try codegen.generateLazySymbol(
1376        &macho_file.base,
1377        pt,
1378        src,
1379        lazy_sym,
1380        &required_alignment,
1381        &aw.writer,
1382        .none,
1383        .{ .atom_index = symbol_index },
1384    );
1385    const code = aw.written();
1386
1387    const output_section_index = switch (lazy_sym.kind) {
1388        .code => macho_file.zig_text_sect_index.?,
1389        .const_data => macho_file.zig_const_sect_index.?,
1390    };
1391    const sym = &self.symbols.items[symbol_index];
1392    sym.name = name_str;
1393    sym.out_n_sect = output_section_index;
1394
1395    const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1396    nlist.n_strx = name_str.pos;
1397    nlist.n_type = .{ .bits = .{ .ext = false, .type = .sect, .pext = false, .is_stab = 0 } };
1398    nlist.n_sect = output_section_index + 1;
1399    self.symtab.items(.size)[sym.nlist_idx] = code.len;
1400
1401    const atom = sym.getAtom(macho_file).?;
1402    atom.setAlive(true);
1403    atom.name = name_str;
1404    atom.alignment = required_alignment;
1405    atom.size = code.len;
1406    atom.out_n_sect = output_section_index;
1407
1408    try atom.allocate(macho_file);
1409    errdefer self.freeNavMetadata(macho_file, symbol_index);
1410
1411    sym.value = 0;
1412    nlist.n_value = 0;
1413
1414    const sect = macho_file.sections.items(.header)[output_section_index];
1415    const file_offset = sect.offset + atom.value;
1416    try macho_file.pwriteAll(code, file_offset);
1417}
1418
1419pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
1420    if (self.dwarf) |*dwarf| {
1421        const comp = dwarf.bin_file.comp;
1422        const diags = &comp.link_diags;
1423        dwarf.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) {
1424            error.Overflow => return error.Overflow,
1425            error.OutOfMemory => return error.OutOfMemory,
1426            else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}),
1427        };
1428    }
1429}
1430
1431pub fn deleteExport(
1432    self: *ZigObject,
1433    macho_file: *MachO,
1434    exported: Zcu.Exported,
1435    name: InternPool.NullTerminatedString,
1436) void {
1437    const zcu = macho_file.base.comp.zcu.?;
1438
1439    const metadata = switch (exported) {
1440        .nav => |nav| self.navs.getPtr(nav),
1441        .uav => |uav| self.uavs.getPtr(uav),
1442    } orelse return;
1443    const nlist_index = metadata.@"export"(self, name.toSlice(&zcu.intern_pool)) orelse return;
1444
1445    log.debug("deleting export '{f}'", .{name.fmt(&zcu.intern_pool)});
1446
1447    const nlist = &self.symtab.items(.nlist)[nlist_index.*];
1448    self.symtab.items(.size)[nlist_index.*] = 0;
1449    _ = self.globals_lookup.remove(nlist.n_strx);
1450    // TODO actually remove the export
1451    // const sym_index = macho_file.globals.get(nlist.n_strx).?;
1452    // const sym = &self.symbols.items[sym_index];
1453    // if (sym.file == self.index) {
1454    //     sym.* = .{};
1455    // }
1456    nlist.* = MachO.null_sym;
1457}
1458
1459pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {
1460    _ = lib_name;
1461    const gpa = macho_file.base.comp.gpa;
1462    const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
1463    defer gpa.free(sym_name);
1464    const name_str = try self.addString(gpa, sym_name);
1465    const lookup_gop = try self.globals_lookup.getOrPut(gpa, name_str.pos);
1466    if (!lookup_gop.found_existing) {
1467        const sym_index = try self.newSymbol(gpa, name_str, .{});
1468        const sym = &self.symbols.items[sym_index];
1469        lookup_gop.value_ptr.* = sym.nlist_idx;
1470    }
1471    return lookup_gop.value_ptr.*;
1472}
1473
1474const max_trampoline_len = 12;
1475
1476fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) u64 {
1477    const len = switch (cpu_arch) {
1478        .x86_64 => 5, // jmp rel32
1479        else => @panic("TODO implement trampoline size for this CPU arch"),
1480    };
1481    comptime assert(len <= max_trampoline_len);
1482    return len;
1483}
1484
1485fn writeTrampoline(tr_sym: Symbol, target: Symbol, macho_file: *MachO) !void {
1486    const atom = tr_sym.getAtom(macho_file).?;
1487    const header = macho_file.sections.items(.header)[atom.out_n_sect];
1488    const fileoff = header.offset + atom.value;
1489    const source_addr = tr_sym.getAddress(.{}, macho_file);
1490    const target_addr = target.getAddress(.{ .trampoline = false }, macho_file);
1491    var buf: [max_trampoline_len]u8 = undefined;
1492    const out = switch (macho_file.getTarget().cpu.arch) {
1493        .x86_64 => try x86_64.writeTrampolineCode(source_addr, target_addr, &buf),
1494        else => @panic("TODO implement write trampoline for this CPU arch"),
1495    };
1496    try macho_file.base.file.?.pwriteAll(out, fileoff);
1497}
1498
1499pub fn getOrCreateMetadataForNav(
1500    self: *ZigObject,
1501    macho_file: *MachO,
1502    nav_index: InternPool.Nav.Index,
1503) !Symbol.Index {
1504    const gpa = macho_file.base.comp.gpa;
1505    const gop = try self.navs.getOrPut(gpa, nav_index);
1506    if (!gop.found_existing) {
1507        const sym_index = try self.newSymbolWithAtom(gpa, .{}, macho_file);
1508        const sym = &self.symbols.items[sym_index];
1509        if (isThreadlocal(macho_file, nav_index)) {
1510            sym.flags.tlv = true;
1511        }
1512        gop.value_ptr.* = .{ .symbol_index = sym_index };
1513    }
1514    return gop.value_ptr.symbol_index;
1515}
1516
1517pub fn getOrCreateMetadataForLazySymbol(
1518    self: *ZigObject,
1519    macho_file: *MachO,
1520    pt: Zcu.PerThread,
1521    lazy_sym: link.File.LazySymbol,
1522) !Symbol.Index {
1523    const gop = try self.lazy_syms.getOrPut(pt.zcu.gpa, lazy_sym.ty);
1524    errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
1525    if (!gop.found_existing) gop.value_ptr.* = .{};
1526    const symbol_index_ptr, const state_ptr = switch (lazy_sym.kind) {
1527        .code => .{ &gop.value_ptr.text_symbol_index, &gop.value_ptr.text_state },
1528        .const_data => .{ &gop.value_ptr.const_symbol_index, &gop.value_ptr.const_state },
1529    };
1530    switch (state_ptr.*) {
1531        .unused => symbol_index_ptr.* = try self.newSymbolWithAtom(pt.zcu.gpa, .{}, macho_file),
1532        .pending_flush => return symbol_index_ptr.*,
1533        .flushed => {},
1534    }
1535    state_ptr.* = .pending_flush;
1536    const symbol_index = symbol_index_ptr.*;
1537    // anyerror needs to be deferred until flush
1538    if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbol(macho_file, pt, lazy_sym, symbol_index);
1539    return symbol_index;
1540}
1541
1542fn isThreadlocal(macho_file: *MachO, nav_index: InternPool.Nav.Index) bool {
1543    if (!macho_file.base.comp.config.any_non_single_threaded)
1544        return false;
1545    const ip = &macho_file.base.comp.zcu.?.intern_pool;
1546    return ip.getNav(nav_index).isThreadlocal(ip);
1547}
1548
1549fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {
1550    try self.atoms.ensureUnusedCapacity(allocator, 1);
1551    try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
1552    return self.addAtomAssumeCapacity();
1553}
1554
1555fn addAtomAssumeCapacity(self: *ZigObject) Atom.Index {
1556    const atom_index: Atom.Index = @intCast(self.atoms.items.len);
1557    const atom = self.atoms.addOneAssumeCapacity();
1558    atom.* = .{
1559        .file = self.index,
1560        .atom_index = atom_index,
1561        .extra = self.addAtomExtraAssumeCapacity(.{}),
1562    };
1563    return atom_index;
1564}
1565
1566pub fn getAtom(self: *ZigObject, atom_index: Atom.Index) ?*Atom {
1567    if (atom_index == 0) return null;
1568    assert(atom_index < self.atoms.items.len);
1569    return &self.atoms.items[atom_index];
1570}
1571
1572pub fn getAtoms(self: *ZigObject) []const Atom.Index {
1573    return self.atoms_indexes.items;
1574}
1575
1576fn addAtomExtra(self: *ZigObject, allocator: Allocator, extra: Atom.Extra) !u32 {
1577    const fields = @typeInfo(Atom.Extra).@"struct".fields;
1578    try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
1579    return self.addAtomExtraAssumeCapacity(extra);
1580}
1581
1582fn addAtomExtraAssumeCapacity(self: *ZigObject, extra: Atom.Extra) u32 {
1583    const index = @as(u32, @intCast(self.atoms_extra.items.len));
1584    const fields = @typeInfo(Atom.Extra).@"struct".fields;
1585    inline for (fields) |field| {
1586        self.atoms_extra.appendAssumeCapacity(switch (field.type) {
1587            u32 => @field(extra, field.name),
1588            else => @compileError("bad field type"),
1589        });
1590    }
1591    return index;
1592}
1593
1594pub fn getAtomExtra(self: ZigObject, index: u32) Atom.Extra {
1595    const fields = @typeInfo(Atom.Extra).@"struct".fields;
1596    var i: usize = index;
1597    var result: Atom.Extra = undefined;
1598    inline for (fields) |field| {
1599        @field(result, field.name) = switch (field.type) {
1600            u32 => self.atoms_extra.items[i],
1601            else => @compileError("bad field type"),
1602        };
1603        i += 1;
1604    }
1605    return result;
1606}
1607
1608pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void {
1609    assert(index > 0);
1610    const fields = @typeInfo(Atom.Extra).@"struct".fields;
1611    inline for (fields, 0..) |field, i| {
1612        self.atoms_extra.items[index + i] = switch (field.type) {
1613            u32 => @field(extra, field.name),
1614            else => @compileError("bad field type"),
1615        };
1616    }
1617}
1618
1619fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index {
1620    try self.symbols.ensureUnusedCapacity(allocator, 1);
1621    return self.addSymbolAssumeCapacity();
1622}
1623
1624fn addSymbolAssumeCapacity(self: *ZigObject) Symbol.Index {
1625    const index: Symbol.Index = @intCast(self.symbols.items.len);
1626    const symbol = self.symbols.addOneAssumeCapacity();
1627    symbol.* = .{ .file = self.index };
1628    return index;
1629}
1630
1631pub fn getSymbolRef(self: ZigObject, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
1632    const global_index = self.globals.items[index];
1633    if (macho_file.resolver.get(global_index)) |ref| return ref;
1634    return .{ .index = index, .file = self.index };
1635}
1636
1637pub fn addSymbolExtra(self: *ZigObject, allocator: Allocator, extra: Symbol.Extra) !u32 {
1638    const fields = @typeInfo(Symbol.Extra).@"struct".fields;
1639    try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
1640    return self.addSymbolExtraAssumeCapacity(extra);
1641}
1642
1643fn addSymbolExtraAssumeCapacity(self: *ZigObject, extra: Symbol.Extra) u32 {
1644    const index = @as(u32, @intCast(self.symbols_extra.items.len));
1645    const fields = @typeInfo(Symbol.Extra).@"struct".fields;
1646    inline for (fields) |field| {
1647        self.symbols_extra.appendAssumeCapacity(switch (field.type) {
1648            u32 => @field(extra, field.name),
1649            else => @compileError("bad field type"),
1650        });
1651    }
1652    return index;
1653}
1654
1655pub fn getSymbolExtra(self: ZigObject, index: u32) Symbol.Extra {
1656    const fields = @typeInfo(Symbol.Extra).@"struct".fields;
1657    var i: usize = index;
1658    var result: Symbol.Extra = undefined;
1659    inline for (fields) |field| {
1660        @field(result, field.name) = switch (field.type) {
1661            u32 => self.symbols_extra.items[i],
1662            else => @compileError("bad field type"),
1663        };
1664        i += 1;
1665    }
1666    return result;
1667}
1668
1669pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {
1670    const fields = @typeInfo(Symbol.Extra).@"struct".fields;
1671    inline for (fields, 0..) |field, i| {
1672        self.symbols_extra.items[index + i] = switch (field.type) {
1673            u32 => @field(extra, field.name),
1674            else => @compileError("bad field type"),
1675        };
1676    }
1677}
1678
1679fn addString(self: *ZigObject, allocator: Allocator, string: []const u8) !MachO.String {
1680    const off = try self.strtab.insert(allocator, string);
1681    return .{ .pos = off, .len = @intCast(string.len + 1) };
1682}
1683
1684pub fn getString(self: ZigObject, string: MachO.String) [:0]const u8 {
1685    if (string.len == 0) return "";
1686    return self.strtab.buffer.items[string.pos..][0 .. string.len - 1 :0];
1687}
1688
1689pub fn asFile(self: *ZigObject) File {
1690    return .{ .zig_object = self };
1691}
1692
1693pub fn fmtSymtab(self: *ZigObject, macho_file: *MachO) std.fmt.Alt(Format, Format.symtab) {
1694    return .{ .data = .{
1695        .self = self,
1696        .macho_file = macho_file,
1697    } };
1698}
1699
1700const Format = struct {
1701    self: *ZigObject,
1702    macho_file: *MachO,
1703
1704    fn symtab(f: Format, w: *Writer) Writer.Error!void {
1705        try w.writeAll("  symbols\n");
1706        const self = f.self;
1707        const macho_file = f.macho_file;
1708        for (self.symbols.items, 0..) |sym, i| {
1709            const ref = self.getSymbolRef(@intCast(i), macho_file);
1710            if (ref.getFile(macho_file) == null) {
1711                // TODO any better way of handling this?
1712                try w.print("    {s} : unclaimed\n", .{sym.getName(macho_file)});
1713            } else {
1714                try w.print("    {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
1715            }
1716        }
1717    }
1718
1719    fn atoms(f: Format, w: *Writer) Writer.Error!void {
1720        const self = f.self;
1721        const macho_file = f.macho_file;
1722        try w.writeAll("  atoms\n");
1723        for (self.getAtoms()) |atom_index| {
1724            const atom = self.getAtom(atom_index) orelse continue;
1725            try w.print("    {f}\n", .{atom.fmt(macho_file)});
1726        }
1727    }
1728};
1729
1730pub fn fmtAtoms(self: *ZigObject, macho_file: *MachO) std.fmt.Alt(Format, Format.atoms) {
1731    return .{ .data = .{
1732        .self = self,
1733        .macho_file = macho_file,
1734    } };
1735}
1736
1737const AvMetadata = struct {
1738    symbol_index: Symbol.Index,
1739    /// A list of all exports aliases of this Av.
1740    exports: std.ArrayList(Symbol.Index) = .empty,
1741
1742    fn @"export"(m: AvMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 {
1743        for (m.exports.items) |*exp| {
1744            const nlist = zig_object.symtab.items(.nlist)[exp.*];
1745            const exp_name = zig_object.strtab.getAssumeExists(nlist.n_strx);
1746            if (mem.eql(u8, name, exp_name)) return exp;
1747        }
1748        return null;
1749    }
1750};
1751
1752const LazySymbolMetadata = struct {
1753    const State = enum { unused, pending_flush, flushed };
1754    text_symbol_index: Symbol.Index = undefined,
1755    const_symbol_index: Symbol.Index = undefined,
1756    text_state: State = .unused,
1757    const_state: State = .unused,
1758};
1759
1760const TlvInitializer = struct {
1761    symbol_index: Symbol.Index,
1762    data: []const u8 = &[0]u8{},
1763
1764    fn deinit(tlv_init: *TlvInitializer, allocator: Allocator) void {
1765        allocator.free(tlv_init.data);
1766    }
1767};
1768
1769const NavTable = std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, AvMetadata);
1770const UavTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, AvMetadata);
1771const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, LazySymbolMetadata);
1772const RelocationTable = std.ArrayList(std.ArrayList(Relocation));
1773const TlvInitializerTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlvInitializer);
1774
1775const x86_64 = struct {
1776    fn writeTrampolineCode(source_addr: u64, target_addr: u64, buf: *[max_trampoline_len]u8) ![]u8 {
1777        const disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr)) - 5;
1778        var bytes = [_]u8{
1779            0xe9, 0x00, 0x00, 0x00, 0x00, // jmp rel32
1780        };
1781        assert(bytes.len == trampolineSize(.x86_64));
1782        mem.writeInt(i32, bytes[1..][0..4], @intCast(disp), .little);
1783        @memcpy(buf[0..bytes.len], &bytes);
1784        return buf[0..bytes.len];
1785    }
1786};
1787
1788const assert = std.debug.assert;
1789const builtin = @import("builtin");
1790const codegen = @import("../../codegen.zig");
1791const link = @import("../../link.zig");
1792const log = std.log.scoped(.link);
1793const macho = std.macho;
1794const mem = std.mem;
1795const target_util = @import("../../target.zig");
1796const trace = @import("../../tracy.zig").trace;
1797const std = @import("std");
1798const Writer = std.Io.Writer;
1799
1800const Allocator = std.mem.Allocator;
1801const Archive = @import("Archive.zig");
1802const Atom = @import("Atom.zig");
1803const Dwarf = @import("../Dwarf.zig");
1804const File = @import("file.zig").File;
1805const InternPool = @import("../../InternPool.zig");
1806const MachO = @import("../MachO.zig");
1807const Nlist = Object.Nlist;
1808const Zcu = @import("../../Zcu.zig");
1809const Object = @import("Object.zig");
1810const Relocation = @import("Relocation.zig");
1811const Symbol = @import("Symbol.zig");
1812const StringTable = @import("../StringTable.zig");
1813const Type = @import("../../Type.zig");
1814const Value = @import("../../Value.zig");
1815const AnalUnit = InternPool.AnalUnit;
1816const ZigObject = @This();