Commit 96441bd829

Jakub Konka <kubkon@jakubkonka.com>
2024-08-16 14:52:55
macho: update codegen and linker to distributed jump table approach
1 parent bb70501
src/arch/x86_64/CodeGen.zig
@@ -12364,13 +12364,10 @@ fn genCall(self: *Self, info: union(enum) {
                         const zo = macho_file.getZigObject().?;
                         const sym_index = try zo.getOrCreateMetadataForNav(macho_file, func.owner_nav);
                         const sym = zo.symbols.items[sym_index];
-                        try self.genSetReg(
-                            .rax,
-                            Type.usize,
-                            .{ .load_symbol = .{ .sym = sym.nlist_idx } },
-                            .{},
-                        );
-                        try self.asmRegister(.{ ._, .call }, .rax);
+                        try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
+                            .atom_index = try self.owner.getSymbolIndex(self),
+                            .sym_index = sym.nlist_idx,
+                        }));
                     } else if (self.bin_file.cast(.plan9)) |p9| {
                         const atom_index = try p9.seeNav(pt, func.owner_nav);
                         const atom = p9.getAtom(atom_index);
@@ -12392,6 +12389,15 @@ fn genCall(self: *Self, info: union(enum) {
                         .atom_index = try self.owner.getSymbolIndex(self),
                         .sym_index = target_sym_index,
                     }));
+                } else if (self.bin_file.cast(.macho)) |macho_file| {
+                    const target_sym_index = try macho_file.getGlobalSymbol(
+                        @"extern".name.toSlice(ip),
+                        @"extern".lib_name.toSlice(ip),
+                    );
+                    try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
+                        .atom_index = try self.owner.getSymbolIndex(self),
+                        .sym_index = target_sym_index,
+                    }));
                 } else try self.genExternSymbolRef(
                     .call,
                     @"extern".lib_name.toSlice(ip),
@@ -12410,6 +12416,12 @@ fn genCall(self: *Self, info: union(enum) {
                 .atom_index = try self.owner.getSymbolIndex(self),
                 .sym_index = target_sym_index,
             }));
+        } else if (self.bin_file.cast(.macho)) |macho_file| {
+            const target_sym_index = try macho_file.getGlobalSymbol(lib.callee, lib.lib);
+            try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
+                .atom_index = try self.owner.getSymbolIndex(self),
+                .sym_index = target_sym_index,
+            }));
         } else try self.genExternSymbolRef(.call, lib.lib, lib.callee),
     }
     return call_info.return_value.short;
@@ -15335,15 +15347,6 @@ fn genExternSymbolRef(
             .call => try self.asmRegister(.{ ._, .call }, .rax),
             else => unreachable,
         }
-    } else if (self.bin_file.cast(.macho)) |macho_file| {
-        _ = try self.addInst(.{
-            .tag = .call,
-            .ops = .extern_fn_reloc,
-            .data = .{ .reloc = .{
-                .atom_index = atom_index,
-                .sym_index = try macho_file.getGlobalSymbol(callee, lib),
-            } },
-        });
     } else return self.fail("TODO implement calling extern functions", .{});
 }
 
@@ -15434,13 +15437,12 @@ fn genLazySymbolRef(
             return self.fail("{s} creating lazy symbol", .{@errorName(err)});
         const sym = zo.symbols.items[sym_index];
         switch (tag) {
-            .lea, .call => try self.genSetReg(
-                reg,
-                Type.usize,
-                .{ .load_symbol = .{ .sym = sym.nlist_idx } },
-                .{},
-            ),
-            .mov => try self.genSetReg(reg, Type.usize, .{ .load_symbol = .{ .sym = sym.nlist_idx } }, .{}),
+            .lea, .call => try self.genSetReg(reg, Type.usize, .{
+                .lea_symbol = .{ .sym = sym.nlist_idx },
+            }, .{}),
+            .mov => try self.genSetReg(reg, Type.usize, .{
+                .load_symbol = .{ .sym = sym.nlist_idx },
+            }, .{}),
             else => unreachable,
         }
         switch (tag) {
src/arch/x86_64/Emit.zig
@@ -135,23 +135,11 @@ pub fn emitMir(emit: *Emit) Error!void {
                         });
                     }
                 } else if (emit.lower.bin_file.cast(.macho)) |macho_file| {
-                    const is_obj_or_static_lib = switch (emit.lower.output_mode) {
-                        .Exe => false,
-                        .Obj => true,
-                        .Lib => emit.lower.link_mode == .static,
-                    };
                     const zo = macho_file.getZigObject().?;
                     const atom = zo.symbols.items[data.atom_index].getAtom(macho_file).?;
                     const sym = &zo.symbols.items[data.sym_index];
-                    if (sym.getSectionFlags().needs_zig_got and !is_obj_or_static_lib) {
-                        _ = try sym.getOrCreateZigGotEntry(data.sym_index, macho_file);
-                    }
-                    const @"type": link.File.MachO.Relocation.Type = if (sym.getSectionFlags().needs_zig_got and !is_obj_or_static_lib)
-                        .zig_got_load
-                    else if (sym.getSectionFlags().needs_got)
-                        // TODO: it is possible to emit .got_load here that can potentially be relaxed
-                        // however this requires always to use a MOVQ mnemonic
-                        .got
+                    const @"type": link.File.MachO.Relocation.Type = if (sym.flags.is_extern_ptr)
+                        .got_load
                     else if (sym.flags.tlv)
                         .tlv
                     else
src/arch/x86_64/Lower.zig
@@ -328,12 +328,6 @@ fn reloc(lower: *Lower, target: Reloc.Target) Immediate {
 }
 
 fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
-    const is_obj_or_static_lib = switch (lower.output_mode) {
-        .Exe => false,
-        .Obj => true,
-        .Lib => lower.link_mode == .static,
-    };
-
     const emit_prefix = prefix;
     var emit_mnemonic = mnemonic;
     var emit_ops_storage: [4]Operand = undefined;
@@ -455,10 +449,22 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
                         _ = lower.reloc(.{ .linker_reloc = sym });
                         break :op switch (mnemonic) {
                             .lea => {
+                                if (macho_sym.flags.is_extern_ptr) emit_mnemonic = .mov;
                                 break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
                             },
                             .mov => {
-                                if (is_obj_or_static_lib and macho_sym.getSectionFlags().needs_zig_got) emit_mnemonic = .lea;
+                                if (macho_sym.flags.is_extern_ptr) {
+                                    const reg = ops[0].reg;
+                                    lower.result_insts[lower.result_insts_len] =
+                                        try Instruction.new(.none, .mov, &[_]Operand{
+                                        .{ .reg = reg.to64() },
+                                        .{ .mem = Memory.rip(.qword, 0) },
+                                    });
+                                    lower.result_insts_len += 1;
+                                    break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{
+                                        .reg = reg.to64(),
+                                    } }) };
+                                }
                                 break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
                             },
                             else => unreachable,
src/link/MachO/dyld_info/Rebase.zig
@@ -56,18 +56,6 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void {
         }
     }
 
-    if (macho_file.zig_got_sect_index) |sid| {
-        const seg_id = macho_file.sections.items(.segment_id)[sid];
-        const seg = macho_file.segments.items[seg_id];
-        for (0..macho_file.zig_got.entries.items.len) |idx| {
-            const addr = macho_file.zig_got.entryAddress(@intCast(idx), macho_file);
-            try rebase.entries.append(gpa, .{
-                .offset = addr - seg.vmaddr,
-                .segment_id = seg_id,
-            });
-        }
-    }
-
     if (macho_file.got_sect_index) |sid| {
         const seg_id = macho_file.sections.items(.segment_id)[sid];
         const seg = macho_file.segments.items[seg_id];
src/link/MachO/Atom.zig
@@ -492,10 +492,6 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
                 }
             },
 
-            .zig_got_load => {
-                assert(rel.getTargetSymbol(self, macho_file).getSectionFlags().has_zig_got);
-            },
-
             .got => {
                 rel.getTargetSymbol(self, macho_file).setSectionFlags(.{ .needs_got = true });
             },
@@ -652,8 +648,6 @@ fn resolveRelocInner(
     const G: i64 = @intCast(rel.getGotTargetAddress(self, macho_file));
     const TLS = @as(i64, @intCast(macho_file.getTlsAddress()));
     const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(self, macho_file))) else 0;
-    // Address of the __got_zig table entry if any.
-    const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(macho_file)));
 
     const divExact = struct {
         fn divExact(atom: Atom, r: Relocation, num: u12, den: u12, ctx: *MachO) !u12 {
@@ -676,13 +670,12 @@ fn resolveRelocInner(
             S + A - SUB,
             rel.getTargetAtom(self, macho_file).atom_index,
         }),
-        .@"extern" => relocs_log.debug("  {x}<+{d}>: {}: [=> {x}] G({x}) ZG({x}) ({s})", .{
+        .@"extern" => relocs_log.debug("  {x}<+{d}>: {}: [=> {x}] G({x}) ({s})", .{
             P,
             rel_offset,
             rel.fmtPretty(cpu_arch),
             S + A - SUB,
             G + A,
-            ZIG_GOT + A,
             rel.getTargetSymbol(self, macho_file).getName(macho_file),
         }),
     }
@@ -745,17 +738,6 @@ fn resolveRelocInner(
             }
         },
 
-        .zig_got_load => {
-            assert(rel.tag == .@"extern");
-            assert(rel.meta.length == 2);
-            assert(rel.meta.pcrel);
-            switch (cpu_arch) {
-                .x86_64 => try writer.writeInt(i32, @intCast(ZIG_GOT + A - P), .little),
-                .aarch64 => @panic("TODO resolve __got_zig indirection reloc"),
-                else => unreachable,
-            }
-        },
-
         .tlv => {
             assert(rel.tag == .@"extern");
             assert(rel.meta.length == 2);
@@ -1065,7 +1047,6 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r
                     .subtractor => .ARM64_RELOC_SUBTRACTOR,
                     .unsigned => .ARM64_RELOC_UNSIGNED,
 
-                    .zig_got_load,
                     .signed,
                     .signed1,
                     .signed2,
@@ -1109,7 +1090,6 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r
                     .subtractor => .X86_64_RELOC_SUBTRACTOR,
                     .unsigned => .X86_64_RELOC_UNSIGNED,
 
-                    .zig_got_load,
                     .page,
                     .pageoff,
                     .got_load_page,
src/link/MachO/Relocation.zig
@@ -92,7 +92,6 @@ fn formatPretty(
         .signed4 => "X86_64_RELOC_SIGNED_4",
         .got_load => "X86_64_RELOC_GOT_LOAD",
         .tlv => "X86_64_RELOC_TLV",
-        .zig_got_load => "ZIG_GOT_LOAD",
         .page => "ARM64_RELOC_PAGE21",
         .pageoff => "ARM64_RELOC_PAGEOFF12",
         .got_load_page => "ARM64_RELOC_GOT_LOAD_PAGE21",
@@ -137,8 +136,6 @@ pub const Type = enum {
     got_load,
     /// RIP-relative TLV load (X86_64_RELOC_TLV)
     tlv,
-    /// Zig-specific __got_zig indirection
-    zig_got_load,
 
     // arm64
     /// PC-relative load (distance to page, ARM64_RELOC_PAGE21)
src/link/MachO/Symbol.zig
@@ -123,6 +123,7 @@ pub fn getSymbolRank(symbol: Symbol, macho_file: *MachO) u32 {
 
 pub fn getAddress(symbol: Symbol, opts: struct {
     stubs: bool = true,
+    trampoline: bool = true,
 }, macho_file: *MachO) u64 {
     if (opts.stubs) {
         if (symbol.getSectionFlags().stubs) {
@@ -131,6 +132,9 @@ pub fn getAddress(symbol: Symbol, opts: struct {
             return symbol.getObjcStubsAddress(macho_file);
         }
     }
+    if (symbol.flags.trampoline and opts.trampoline) {
+        return symbol.getTrampolineAddress(macho_file);
+    }
     if (symbol.getAtom(macho_file)) |atom| return atom.getAddress(macho_file) + symbol.value;
     return symbol.value;
 }
@@ -169,23 +173,11 @@ pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
     return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
 }
 
-const GetOrCreateZigGotEntryResult = struct {
-    found_existing: bool,
-    index: ZigGotSection.Index,
-};
-
-pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult {
-    assert(!macho_file.base.isRelocatable());
-    assert(symbol.getSectionFlags().needs_zig_got);
-    if (symbol.getSectionFlags().has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got };
-    const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file);
-    return .{ .found_existing = false, .index = index };
-}
-
-pub fn getZigGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
-    if (!symbol.getSectionFlags().has_zig_got) return 0;
-    const extras = symbol.getExtra(macho_file);
-    return macho_file.zig_got.entryAddress(extras.zig_got, macho_file);
+pub fn getTrampolineAddress(symbol: Symbol, macho_file: *MachO) u64 {
+    if (!symbol.flags.trampoline) return 0;
+    const zo = macho_file.getZigObject().?;
+    const index = symbol.getExtra(macho_file).trampoline;
+    return zo.symbols.items[index].getAddress(.{}, macho_file);
 }
 
 pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 {
@@ -209,12 +201,12 @@ pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 {
 
 const AddExtraOpts = struct {
     got: ?u32 = null,
-    zig_got: ?u32 = null,
     stubs: ?u32 = null,
     objc_stubs: ?u32 = null,
     objc_selrefs: ?u32 = null,
     tlv_ptr: ?u32 = null,
     symtab: ?u32 = null,
+    trampoline: ?u32 = null,
 };
 
 pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) void {
@@ -393,6 +385,13 @@ pub const Flags = packed struct {
 
     /// Whether the symbol makes into the output symtab or not.
     output_symtab: bool = false,
+
+    /// ZigObject specific flags
+    /// Whether the symbol has a trampoline
+    trampoline: bool = false,
+
+    /// Whether the symbol is an extern pointer (as opposed to function).
+    is_extern_ptr: bool = false,
 };
 
 pub const SectionFlags = packed struct(u8) {
@@ -400,10 +399,6 @@ pub const SectionFlags = packed struct(u8) {
     needs_got: bool = false,
     has_got: bool = false,
 
-    /// Whether the symbol contains __got_zig indirection.
-    needs_zig_got: bool = false,
-    has_zig_got: bool = false,
-
     /// Whether the symbols contains __stubs indirection.
     stubs: bool = false,
 
@@ -413,7 +408,7 @@ pub const SectionFlags = packed struct(u8) {
     /// Whether the symbol contains __objc_stubs indirection.
     objc_stubs: bool = false,
 
-    _: u1 = 0,
+    _: u3 = 0,
 };
 
 pub const Visibility = enum {
@@ -432,12 +427,12 @@ pub const Visibility = enum {
 
 pub const Extra = struct {
     got: u32 = 0,
-    zig_got: u32 = 0,
     stubs: u32 = 0,
     objc_stubs: u32 = 0,
     objc_selrefs: u32 = 0,
     tlv_ptr: u32 = 0,
     symtab: u32 = 0,
+    trampoline: u32 = 0,
 };
 
 pub const Index = u32;
src/link/MachO/synthetic.zig
@@ -1,111 +1,3 @@
-pub const ZigGotSection = struct {
-    entries: std.ArrayListUnmanaged(Symbol.Index) = .{},
-    dirty: bool = false,
-
-    pub const Index = u32;
-
-    pub fn deinit(zig_got: *ZigGotSection, allocator: Allocator) void {
-        zig_got.entries.deinit(allocator);
-    }
-
-    fn allocateEntry(zig_got: *ZigGotSection, allocator: Allocator) !Index {
-        try zig_got.entries.ensureUnusedCapacity(allocator, 1);
-        // TODO add free list
-        const index = @as(Index, @intCast(zig_got.entries.items.len));
-        _ = zig_got.entries.addOneAssumeCapacity();
-        zig_got.dirty = true;
-        return index;
-    }
-
-    pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, macho_file: *MachO) !Index {
-        const gpa = macho_file.base.comp.gpa;
-        const zo = macho_file.getZigObject().?;
-        const index = try zig_got.allocateEntry(gpa);
-        const entry = &zig_got.entries.items[index];
-        entry.* = sym_index;
-        const symbol = &zo.symbols.items[sym_index];
-        assert(symbol.getSectionFlags().needs_zig_got);
-        symbol.setSectionFlags(.{ .has_zig_got = true });
-        symbol.addExtra(.{ .zig_got = index }, macho_file);
-        return index;
-    }
-
-    pub fn entryOffset(zig_got: ZigGotSection, index: Index, macho_file: *MachO) u64 {
-        _ = zig_got;
-        const sect = macho_file.sections.items(.header)[macho_file.zig_got_sect_index.?];
-        return sect.offset + @sizeOf(u64) * index;
-    }
-
-    pub fn entryAddress(zig_got: ZigGotSection, index: Index, macho_file: *MachO) u64 {
-        _ = zig_got;
-        const sect = macho_file.sections.items(.header)[macho_file.zig_got_sect_index.?];
-        return sect.addr + @sizeOf(u64) * index;
-    }
-
-    pub fn size(zig_got: ZigGotSection, macho_file: *MachO) usize {
-        _ = macho_file;
-        return @sizeOf(u64) * zig_got.entries.items.len;
-    }
-
-    pub fn writeOne(zig_got: *ZigGotSection, macho_file: *MachO, index: Index) !void {
-        if (zig_got.dirty) {
-            const needed_size = zig_got.size(macho_file);
-            try macho_file.growSection(macho_file.zig_got_sect_index.?, needed_size);
-            zig_got.dirty = false;
-        }
-        const zo = macho_file.getZigObject().?;
-        const off = zig_got.entryOffset(index, macho_file);
-        const entry = zig_got.entries.items[index];
-        const value = zo.symbols.items[entry].getAddress(.{ .stubs = false }, macho_file);
-
-        var buf: [8]u8 = undefined;
-        std.mem.writeInt(u64, &buf, value, .little);
-        try macho_file.base.file.?.pwriteAll(&buf, off);
-    }
-
-    pub fn writeAll(zig_got: ZigGotSection, macho_file: *MachO, writer: anytype) !void {
-        const zo = macho_file.getZigObject().?;
-        for (zig_got.entries.items) |entry| {
-            const symbol = zo.symbols.items[entry];
-            const value = symbol.address(.{ .stubs = false }, macho_file);
-            try writer.writeInt(u64, value, .little);
-        }
-    }
-
-    const FormatCtx = struct {
-        zig_got: ZigGotSection,
-        macho_file: *MachO,
-    };
-
-    pub fn fmt(zig_got: ZigGotSection, macho_file: *MachO) std.fmt.Formatter(format2) {
-        return .{ .data = .{ .zig_got = zig_got, .macho_file = macho_file } };
-    }
-
-    pub fn format2(
-        ctx: FormatCtx,
-        comptime unused_fmt_string: []const u8,
-        options: std.fmt.FormatOptions,
-        writer: anytype,
-    ) !void {
-        _ = options;
-        _ = unused_fmt_string;
-        const zig_got = ctx.zig_got;
-        const macho_file = ctx.macho_file;
-        try writer.writeAll("__zig_got\n");
-        for (zig_got.entries.items, 0..) |entry, index| {
-            const zo = macho_file.getZigObject().?;
-            const symbol = zo.symbols.items[entry];
-            try writer.print("  {d}@0x{x} => {d}@0x{x} ({s})\n", .{
-                index,
-                zig_got.entryAddress(@intCast(index), macho_file),
-                entry,
-                symbol.getAddress(.{}, macho_file),
-                symbol.getName(macho_file),
-            });
-        }
-    }
-};
-
 pub const GotSection = struct {
     symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
 
src/link/MachO/ZigObject.zig
@@ -795,7 +795,15 @@ pub fn updateFunc(
     };
 
     const sect_index = try self.getNavOutputSection(macho_file, zcu, func.owner_nav, code);
+    const old_rva, const old_alignment = blk: {
+        const atom = self.symbols.items[sym_index].getAtom(macho_file).?;
+        break :blk .{ atom.value, atom.alignment };
+    };
     try self.updateNavCode(macho_file, pt, func.owner_nav, sym_index, sect_index, code);
+    const new_rva, const new_alignment = blk: {
+        const atom = self.symbols.items[sym_index].getAtom(macho_file).?;
+        break :blk .{ atom.value, atom.alignment };
+    };
 
     if (dwarf_wip_nav) |*wip_nav| {
         const sym = self.symbols.items[sym_index];
@@ -812,6 +820,42 @@ pub fn updateFunc(
     }
 
     // Exports will be updated by `Zcu.processExports` after the update.
+    if (old_rva != new_rva and old_rva > 0) {
+        // If we had to reallocate the function, we re-use the existing slot for a trampoline.
+        // In the rare case that the function has been further overaligned we skip creating a
+        // trampoline and update all symbols referring this function.
+        if (old_alignment.order(new_alignment) == .lt) {
+            @panic("TODO update all symbols referring this function");
+        }
+
+        // Create a trampoline to the new location at `old_rva`.
+        if (!self.symbols.items[sym_index].flags.trampoline) {
+            const name = try std.fmt.allocPrint(gpa, "{s}$trampoline", .{
+                self.symbols.items[sym_index].getName(macho_file),
+            });
+            defer gpa.free(name);
+            const name_off = try self.addString(gpa, name);
+            const tr_size = trampolineSize(macho_file.getTarget().cpu.arch);
+            const tr_sym_index = try self.newSymbolWithAtom(gpa, name_off, macho_file);
+            const tr_sym = &self.symbols.items[tr_sym_index];
+            tr_sym.out_n_sect = macho_file.zig_text_sect_index.?;
+            const tr_nlist = &self.symtab.items(.nlist)[tr_sym.nlist_idx];
+            tr_nlist.n_sect = macho_file.zig_text_sect_index.? + 1;
+            const tr_atom = tr_sym.getAtom(macho_file).?;
+            tr_atom.value = old_rva;
+            tr_atom.setAlive(true);
+            tr_atom.alignment = old_alignment;
+            tr_atom.out_n_sect = macho_file.zig_text_sect_index.?;
+            tr_atom.size = tr_size;
+            self.symtab.items(.size)[tr_sym.nlist_idx] = tr_size;
+            const target_sym = &self.symbols.items[sym_index];
+            target_sym.addExtra(.{ .trampoline = tr_sym_index }, macho_file);
+            target_sym.flags.trampoline = true;
+        }
+        const target_sym = self.symbols.items[sym_index];
+        const source_sym = self.symbols.items[target_sym.getExtra(macho_file).trampoline];
+        try writeTrampoline(source_sym, target_sym, macho_file);
+    }
 }
 
 pub fn updateNav(
@@ -836,7 +880,7 @@ pub fn updateNav(
             const lib_name = @"extern".lib_name.toSlice(ip);
             const index = try self.getGlobalSymbol(macho_file, name, lib_name);
             const sym = &self.symbols.items[index];
-            sym.setSectionFlags(.{ .needs_got = true });
+            sym.flags.is_extern_ptr = true;
             return;
         },
         else => nav_val,
@@ -946,13 +990,6 @@ fn updateNavCode(
             if (old_vaddr != atom.value) {
                 sym.value = 0;
                 nlist.n_value = 0;
-
-                if (!macho_file.base.isRelocatable()) {
-                    log.debug("  (updating offset table entry)", .{});
-                    assert(sym.getSectionFlags().has_zig_got);
-                    const extra = sym.getExtra(macho_file);
-                    try macho_file.zig_got.writeOne(macho_file, extra.zig_got);
-                }
             }
         } else if (code.len < old_size) {
             atom.shrink(macho_file);
@@ -965,13 +1002,7 @@ fn updateNavCode(
         errdefer self.freeNavMetadata(macho_file, sym_index);
 
         sym.value = 0;
-        sym.setSectionFlags(.{ .needs_zig_got = true });
         nlist.n_value = 0;
-
-        if (!macho_file.base.isRelocatable()) {
-            const gop = try sym.getOrCreateZigGotEntry(sym_index, macho_file);
-            try macho_file.zig_got.writeOne(macho_file, gop.index);
-        }
     }
 
     if (!sect.isZerofill()) {
@@ -1381,14 +1412,8 @@ fn updateLazySymbol(
     errdefer self.freeNavMetadata(macho_file, symbol_index);
 
     sym.value = 0;
-    sym.setSectionFlags(.{ .needs_zig_got = true });
     nlist.n_value = 0;
 
-    if (!macho_file.base.isRelocatable()) {
-        const gop = try sym.getOrCreateZigGotEntry(symbol_index, macho_file);
-        try macho_file.zig_got.writeOne(macho_file, gop.index);
-    }
-
     const sect = macho_file.sections.items(.header)[output_section_index];
     const file_offset = sect.offset + atom.value;
     try macho_file.base.file.?.pwriteAll(code, file_offset);
@@ -1448,6 +1473,31 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l
     return lookup_gop.value_ptr.*;
 }
 
+const max_trampoline_len = 12;
+
+fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) u64 {
+    const len = switch (cpu_arch) {
+        .x86_64 => 5, // jmp rel32
+        else => @panic("TODO implement trampoline size for this CPU arch"),
+    };
+    comptime assert(len <= max_trampoline_len);
+    return len;
+}
+
+fn writeTrampoline(tr_sym: Symbol, target: Symbol, macho_file: *MachO) !void {
+    const atom = tr_sym.getAtom(macho_file).?;
+    const header = macho_file.sections.items(.header)[atom.out_n_sect];
+    const fileoff = header.offset + atom.value;
+    const source_addr = tr_sym.getAddress(.{}, macho_file);
+    const target_addr = target.getAddress(.{ .trampoline = false }, macho_file);
+    var buf: [max_trampoline_len]u8 = undefined;
+    const out = switch (macho_file.getTarget().cpu.arch) {
+        .x86_64 => try x86_64.writeTrampolineCode(source_addr, target_addr, &buf),
+        else => @panic("TODO implement write trampoline for this CPU arch"),
+    };
+    try macho_file.base.file.?.pwriteAll(out, fileoff);
+}
+
 pub fn getOrCreateMetadataForNav(
     self: *ZigObject,
     macho_file: *MachO,
@@ -1460,8 +1510,6 @@ pub fn getOrCreateMetadataForNav(
         const sym = &self.symbols.items[sym_index];
         if (isThreadlocal(macho_file, nav_index)) {
             sym.flags.tlv = true;
-        } else {
-            sym.setSectionFlags(.{ .needs_zig_got = true });
         }
         gop.value_ptr.* = .{ .symbol_index = sym_index };
     }
@@ -1482,12 +1530,7 @@ pub fn getOrCreateMetadataForLazySymbol(
         .const_data => .{ &gop.value_ptr.const_symbol_index, &gop.value_ptr.const_state },
     };
     switch (state_ptr.*) {
-        .unused => {
-            const symbol_index = try self.newSymbolWithAtom(pt.zcu.gpa, .{}, macho_file);
-            const sym = &self.symbols.items[symbol_index];
-            sym.setSectionFlags(.{ .needs_zig_got = true });
-            symbol_index_ptr.* = symbol_index;
-        },
+        .unused => symbol_index_ptr.* = try self.newSymbolWithAtom(pt.zcu.gpa, .{}, macho_file),
         .pending_flush => return symbol_index_ptr.*,
         .flushed => {},
     }
@@ -1749,6 +1792,19 @@ const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, LazySymb
 const RelocationTable = std.ArrayListUnmanaged(std.ArrayListUnmanaged(Relocation));
 const TlvInitializerTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlvInitializer);
 
+const x86_64 = struct {
+    fn writeTrampolineCode(source_addr: u64, target_addr: u64, buf: *[max_trampoline_len]u8) ![]u8 {
+        const disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr)) - 5;
+        var bytes = [_]u8{
+            0xe9, 0x00, 0x00, 0x00, 0x00, // jmp rel32
+        };
+        assert(bytes.len == trampolineSize(.x86_64));
+        mem.writeInt(i32, bytes[1..][0..4], @intCast(disp), .little);
+        @memcpy(buf[0..bytes.len], &bytes);
+        return buf[0..bytes.len];
+    }
+};
+
 const assert = std.debug.assert;
 const builtin = @import("builtin");
 const codegen = @import("../../codegen.zig");
src/link/MachO.zig
@@ -59,7 +59,6 @@ symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
 strtab: std.ArrayListUnmanaged(u8) = .{},
 indsymtab: Indsymtab = .{},
 got: GotSection = .{},
-zig_got: ZigGotSection = .{},
 stubs: StubsSection = .{},
 stubs_helper: StubsHelperSection = .{},
 objc_stubs: ObjcStubsSection = .{},
@@ -75,14 +74,12 @@ data_in_code: DataInCode = .{},
 
 /// Tracked loadable segments during incremental linking.
 zig_text_seg_index: ?u8 = null,
-zig_got_seg_index: ?u8 = null,
 zig_const_seg_index: ?u8 = null,
 zig_data_seg_index: ?u8 = null,
 zig_bss_seg_index: ?u8 = null,
 
 /// Tracked section headers with incremental updates to Zig object.
 zig_text_sect_index: ?u8 = null,
-zig_got_sect_index: ?u8 = null,
 zig_const_sect_index: ?u8 = null,
 zig_data_sect_index: ?u8 = null,
 zig_bss_sect_index: ?u8 = null,
@@ -324,7 +321,6 @@ pub fn deinit(self: *MachO) void {
     self.symtab.deinit(gpa);
     self.strtab.deinit(gpa);
     self.got.deinit(gpa);
-    self.zig_got.deinit(gpa);
     self.stubs.deinit(gpa);
     self.objc_stubs.deinit(gpa);
     self.tlv_ptr.deinit(gpa);
@@ -1796,7 +1792,6 @@ pub fn sortSections(self: *MachO) !void {
         &self.data_sect_index,
         &self.got_sect_index,
         &self.zig_text_sect_index,
-        &self.zig_got_sect_index,
         &self.zig_const_sect_index,
         &self.zig_data_sect_index,
         &self.zig_bss_sect_index,
@@ -2114,7 +2109,6 @@ fn initSegments(self: *MachO) !void {
         &self.text_seg_index,
         &self.linkedit_seg_index,
         &self.zig_text_seg_index,
-        &self.zig_got_seg_index,
         &self.zig_const_seg_index,
         &self.zig_data_seg_index,
         &self.zig_bss_seg_index,
@@ -3231,18 +3225,6 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
             });
         }
 
-        {
-            const filesize = options.symbol_count_hint * @sizeOf(u64);
-            const off = try self.findFreeSpace(filesize, self.getPageSize());
-            self.zig_got_seg_index = try self.addSegment("__GOT_ZIG", .{
-                .fileoff = off,
-                .filesize = filesize,
-                .vmaddr = base_vmaddr + 0x4000000,
-                .vmsize = filesize,
-                .prot = macho.PROT.READ | macho.PROT.WRITE,
-            });
-        }
-
         {
             const filesize: u64 = 1024;
             const off = try self.findFreeSpace(filesize, self.getPageSize());
@@ -3343,13 +3325,6 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
         }
     }
 
-    if (!self.base.isRelocatable()) {
-        self.zig_got_sect_index = try self.addSection("__GOT_ZIG", "__got_zig", .{
-            .alignment = 3,
-        });
-        appendSect(self, self.zig_got_sect_index.?, self.zig_got_seg_index.?);
-    }
-
     {
         self.zig_const_sect_index = try self.addSection("__CONST_ZIG", "__const_zig", .{});
         if (self.base.isRelocatable()) {
@@ -3572,7 +3547,6 @@ inline fn requiresThunks(self: MachO) bool {
 pub fn isZigSegment(self: MachO, seg_id: u8) bool {
     inline for (&[_]?u8{
         self.zig_text_seg_index,
-        self.zig_got_seg_index,
         self.zig_const_seg_index,
         self.zig_data_seg_index,
         self.zig_bss_seg_index,
@@ -3587,7 +3561,6 @@ pub fn isZigSegment(self: MachO, seg_id: u8) bool {
 pub fn isZigSection(self: MachO, sect_id: u8) bool {
     inline for (&[_]?u8{
         self.zig_text_sect_index,
-        self.zig_got_sect_index,
         self.zig_const_sect_index,
         self.zig_data_sect_index,
         self.zig_bss_sect_index,
@@ -3957,7 +3930,6 @@ fn fmtDumpState(
     try writer.print("stubs\n{}\n", .{self.stubs.fmt(self)});
     try writer.print("objc_stubs\n{}\n", .{self.objc_stubs.fmt(self)});
     try writer.print("got\n{}\n", .{self.got.fmt(self)});
-    try writer.print("zig_got\n{}\n", .{self.zig_got.fmt(self)});
     try writer.print("tlv_ptr\n{}\n", .{self.tlv_ptr.fmt(self)});
     try writer.writeByte('\n');
     try writer.print("sections\n{}\n", .{self.fmtSections()});
@@ -4665,7 +4637,6 @@ const Value = @import("../Value.zig");
 const UnwindInfo = @import("MachO/UnwindInfo.zig");
 const WaitGroup = std.Thread.WaitGroup;
 const WeakBind = bind.WeakBind;
-const ZigGotSection = synthetic.ZigGotSection;
 const ZigObject = @import("MachO/ZigObject.zig");
 const dev = @import("../dev.zig");
 
src/codegen.zig
@@ -910,15 +910,15 @@ fn genNavRef(
         const zo = macho_file.getZigObject().?;
         if (is_extern) {
             const sym_index = try macho_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip));
-            zo.symbols.items[sym_index].setSectionFlags(.{ .needs_got = true });
-            return .{ .mcv = .{ .load_symbol = sym_index } };
+            zo.symbols.items[sym_index].flags.is_extern_ptr = true;
+            return GenResult.mcv(.{ .lea_symbol = sym_index });
         }
         const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index);
         const sym = zo.symbols.items[sym_index];
         if (!single_threaded and is_threadlocal) {
             return .{ .mcv = .{ .load_tlv = sym.nlist_idx } };
         }
-        return .{ .mcv = .{ .load_symbol = sym.nlist_idx } };
+        return GenResult.mcv(.{ .lea_symbol = sym.nlist_idx });
     } else if (lf.cast(.coff)) |coff_file| {
         if (is_extern) {
             // TODO audit this