Commit a6a10d9c2b

Jakub Konka <kubkon@jakubkonka.com>
2023-10-25 16:49:56
x86_64: do not hardcode memory passed by Elf linker
1 parent a440cf6
Changed files (11)
src/arch/aarch64/CodeGen.zig
@@ -4012,7 +4012,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
                                     .got => .load_memory_ptr_got,
                                     .direct => .load_memory_ptr_direct,
                                     .import => unreachable,
-                                    .extern_got => unreachable,
                                 };
                                 const atom_index = switch (self.bin_file.tag) {
                                     .macho => blk: {
@@ -5532,7 +5531,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
                             .got => .load_memory_ptr_got,
                             .direct => .load_memory_ptr_direct,
                             .import => unreachable,
-                            .extern_got => unreachable,
                         };
                         const atom_index = switch (self.bin_file.tag) {
                             .macho => blk: {
@@ -5654,7 +5652,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
                 .got => .load_memory_got,
                 .direct => .load_memory_direct,
                 .import => .load_memory_import,
-                .extern_got => unreachable,
             };
             const atom_index = switch (self.bin_file.tag) {
                 .macho => blk: {
@@ -5852,7 +5849,6 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
                             .got => .load_memory_ptr_got,
                             .direct => .load_memory_ptr_direct,
                             .import => unreachable,
-                            .extern_got => unreachable,
                         };
                         const atom_index = switch (self.bin_file.tag) {
                             .macho => blk: {
@@ -6180,7 +6176,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
             .memory => |addr| .{ .memory = addr },
             .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } },
             .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },
-            .load_extern_got, .load_tlv => unreachable, // TODO
+            .load_memory, .lea_memory, .load_tlv => unreachable, // TODO
         },
         .fail => |msg| {
             self.err_msg = msg;
src/arch/arm/CodeGen.zig
@@ -6135,7 +6135,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
         .mcv => |mcv| switch (mcv) {
             .none => .none,
             .undef => .undef,
-            .load_got, .load_extern_got, .load_direct, .load_tlv => unreachable, // TODO
+            .load_got, .load_memory, .lea_memory, .load_direct, .load_tlv => unreachable, // TODO
             .immediate => |imm| .{ .immediate = @as(u32, @truncate(imm)) },
             .memory => |addr| .{ .memory = addr },
         },
src/arch/riscv64/CodeGen.zig
@@ -2591,7 +2591,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
         .mcv => |mcv| switch (mcv) {
             .none => .none,
             .undef => .undef,
-            .load_got, .load_extern_got, .load_direct, .load_tlv => unreachable, // TODO
+            .load_got, .load_memory, .lea_memory, .load_direct, .load_tlv => unreachable, // TODO
             .immediate => |imm| .{ .immediate = imm },
             .memory => |addr| .{ .memory = addr },
         },
src/arch/sparc64/CodeGen.zig
@@ -4137,7 +4137,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
         .mcv => |mcv| switch (mcv) {
             .none => .none,
             .undef => .undef,
-            .load_got, .load_extern_got, .load_direct, .load_tlv => unreachable, // TODO
+            .load_got, .load_memory, .lea_memory, .load_direct, .load_tlv => unreachable, // TODO
             .immediate => |imm| .{ .immediate = imm },
             .memory => |addr| .{ .memory = addr },
         },
src/arch/x86_64/CodeGen.zig
@@ -193,6 +193,11 @@ pub const MCValue = union(enum) {
     /// The value is in memory at a hard-coded address.
     /// If the type is a pointer, it means the pointer address is stored at this memory location.
     memory: u64,
+    /// The value is in memory at an address not-yet-allocated by the linker.
+    /// This traditionally corresponds to a relocation emitted in a relocatable object file.
+    load_memory: u32,
+    /// The address of the memory location not-yet-allocated by the linker.
+    lea_memory: u32,
     /// The value is in memory at a constant offset from the address in a register.
     indirect: RegisterOffset,
     /// The value is in memory.
@@ -207,12 +212,6 @@ pub const MCValue = union(enum) {
     /// The value is a pointer to a value referenced indirectly via GOT.
     /// Payload is a symbol index.
     lea_got: u32,
-    /// The value is an extern variable referenced via GOT.
-    /// Payload is a symbol index.
-    load_extern_got: u32,
-    /// The value is a pointer to an extern variable referenced via GOT.
-    /// Payload is a symbol index.
-    lea_extern_got: u32,
     /// The value is a threadlocal variable.
     /// Payload is a symbol index.
     load_tlv: u32,
@@ -299,9 +298,9 @@ pub const MCValue = union(enum) {
             .register_pair,
             .register_offset,
             .register_overflow,
+            .lea_memory,
             .lea_direct,
             .lea_got,
-            .lea_extern_got,
             .lea_tlv,
             .lea_frame,
             .reserved_frame,
@@ -315,8 +314,8 @@ pub const MCValue = union(enum) {
             .load_direct => |sym_index| .{ .lea_direct = sym_index },
             .load_got => |sym_index| .{ .lea_got = sym_index },
             .load_tlv => |sym_index| .{ .lea_tlv = sym_index },
-            .load_extern_got => |sym_index| .{ .lea_extern_got = sym_index },
             .load_frame => |frame_addr| .{ .lea_frame = frame_addr },
+            .load_memory => |sym_index| .{ .lea_memory = sym_index },
         };
     }
 
@@ -333,9 +332,9 @@ pub const MCValue = union(enum) {
             .indirect,
             .load_direct,
             .load_got,
-            .load_extern_got,
             .load_tlv,
             .load_frame,
+            .load_memory,
             .reserved_frame,
             .air_ref,
             => unreachable, // not dereferenceable
@@ -344,9 +343,9 @@ pub const MCValue = union(enum) {
             .register_offset => |reg_off| .{ .indirect = reg_off },
             .lea_direct => |sym_index| .{ .load_direct = sym_index },
             .lea_got => |sym_index| .{ .load_got = sym_index },
-            .lea_extern_got => |sym_index| .{ .load_extern_got = sym_index },
             .lea_tlv => |sym_index| .{ .load_tlv = sym_index },
             .lea_frame => |frame_addr| .{ .load_frame = frame_addr },
+            .lea_memory => |sym_index| .{ .load_memory = sym_index },
         };
     }
 
@@ -368,11 +367,11 @@ pub const MCValue = union(enum) {
             .lea_direct,
             .load_got,
             .lea_got,
-            .load_extern_got,
-            .lea_extern_got,
             .load_tlv,
             .lea_tlv,
             .load_frame,
+            .load_memory,
+            .lea_memory,
             => switch (off) {
                 0 => mcv,
                 else => unreachable, // not offsettable
@@ -404,13 +403,13 @@ pub const MCValue = union(enum) {
             .lea_direct,
             .load_got,
             .lea_got,
-            .load_extern_got,
-            .lea_extern_got,
             .load_tlv,
             .lea_tlv,
             .lea_frame,
             .reserved_frame,
             .air_ref,
+            .load_memory,
+            .lea_memory,
             => unreachable,
             .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr|
                 Memory.sib(ptr_size, .{ .base = .{ .reg = .ds }, .disp = small_addr })
@@ -448,14 +447,14 @@ pub const MCValue = union(enum) {
             .lea_direct => |pl| try writer.print("direct:{d}", .{pl}),
             .load_got => |pl| try writer.print("[got:{d}]", .{pl}),
             .lea_got => |pl| try writer.print("got:{d}", .{pl}),
-            .load_extern_got => |pl| try writer.print("[extern_got:{d}]", .{pl}),
-            .lea_extern_got => |pl| try writer.print("extern_got:{d}", .{pl}),
             .load_tlv => |pl| try writer.print("[tlv:{d}]", .{pl}),
             .lea_tlv => |pl| try writer.print("tlv:{d}", .{pl}),
             .load_frame => |pl| try writer.print("[{} + 0x{x}]", .{ pl.index, pl.off }),
             .lea_frame => |pl| try writer.print("{} + 0x{x}", .{ pl.index, pl.off }),
             .reserved_frame => |pl| try writer.print("(dead:{})", .{pl}),
             .air_ref => |pl| try writer.print("(air:0x{x})", .{@intFromEnum(pl)}),
+            .load_memory => |pl| try writer.print("[mem:{d}]", .{pl}),
+            .lea_memory => |pl| try writer.print("mem:{d}", .{pl}),
         }
     }
 };
@@ -477,12 +476,12 @@ const InstTracking = struct {
             .lea_direct,
             .load_got,
             .lea_got,
-            .load_extern_got,
-            .lea_extern_got,
             .load_tlv,
             .lea_tlv,
             .load_frame,
             .lea_frame,
+            .load_memory,
+            .lea_memory,
             => result,
             .dead,
             .reserved_frame,
@@ -538,12 +537,12 @@ const InstTracking = struct {
             .lea_direct,
             .load_got,
             .lea_got,
-            .load_extern_got,
-            .lea_extern_got,
             .load_tlv,
             .lea_tlv,
             .load_frame,
             .lea_frame,
+            .load_memory,
+            .lea_memory,
             => self.long,
             .dead,
             .eflags,
@@ -575,11 +574,11 @@ const InstTracking = struct {
             .lea_direct,
             .load_got,
             .lea_got,
-            .load_extern_got,
-            .lea_extern_got,
             .load_tlv,
             .lea_tlv,
             .lea_frame,
+            .load_memory,
+            .lea_memory,
             => assert(std.meta.eql(self.long, target.long)),
             .load_frame,
             .reserved_frame,
@@ -883,6 +882,7 @@ pub fn generate(
 
     var emit = Emit{
         .lower = .{
+            .bin_file = bin_file,
             .allocator = bin_file.allocator,
             .mir = mir,
             .cc = cc,
@@ -970,6 +970,7 @@ pub fn generateLazy(
 
     var emit = Emit{
         .lower = .{
+            .bin_file = bin_file,
             .allocator = bin_file.allocator,
             .mir = mir,
             .cc = abi.resolveCallingConvention(.Unspecified, function.target.*),
@@ -1060,6 +1061,7 @@ fn formatWipMir(
     writer: anytype,
 ) @TypeOf(writer).Error!void {
     var lower = Lower{
+        .bin_file = data.self.bin_file,
         .allocator = data.self.gpa,
         .mir = .{
             .instructions = data.self.mir_instructions.slice(),
@@ -4729,12 +4731,12 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
             Memory.sib(.qword, .{ .base = .{ .frame = frame_addr.index }, .disp = frame_addr.off }),
         ),
         .memory,
+        .load_memory,
         .load_direct,
         .load_got,
-        .load_extern_got,
         .load_tlv,
         => try self.genSetReg(addr_reg, Type.usize, array.address()),
-        .lea_direct, .lea_tlv => unreachable,
+        .lea_memory, .lea_direct, .lea_tlv => unreachable,
         else => return self.fail("TODO implement array_elem_val when array is {}", .{array}),
     }
 
@@ -6327,17 +6329,17 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro
         .immediate,
         .register,
         .register_offset,
+        .lea_memory,
         .lea_direct,
         .lea_got,
-        .lea_extern_got,
         .lea_tlv,
         .lea_frame,
         => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()),
         .memory,
         .indirect,
+        .load_memory,
         .load_direct,
         .load_got,
-        .load_extern_got,
         .load_tlv,
         .load_frame,
         => {
@@ -6476,17 +6478,17 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr
         .immediate,
         .register,
         .register_offset,
+        .lea_memory,
         .lea_direct,
         .lea_got,
-        .lea_extern_got,
         .lea_tlv,
         .lea_frame,
         => try self.genCopy(src_ty, ptr_mcv.deref(), src_mcv),
         .memory,
         .indirect,
+        .load_memory,
         .load_direct,
         .load_got,
-        .load_extern_got,
         .load_tlv,
         .load_frame,
         => {
@@ -6913,15 +6915,15 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC
         .register_overflow,
         .lea_direct,
         .lea_got,
-        .lea_extern_got,
         .lea_tlv,
         .lea_frame,
         .reserved_frame,
         .air_ref,
+        .lea_memory,
         => unreachable, // unmodifiable destination
         .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)),
         .register_pair => unreachable, // unimplemented
-        .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => {
+        .memory, .load_memory, .load_got, .load_direct, .load_tlv => {
             const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
             const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
             defer self.register_manager.unlockReg(addr_reg_lock);
@@ -7973,12 +7975,12 @@ fn genBinOp(
                         .immediate,
                         .eflags,
                         .register_offset,
+                        .load_memory,
+                        .lea_memory,
                         .load_direct,
                         .lea_direct,
                         .load_got,
                         .lea_got,
-                        .load_extern_got,
-                        .lea_extern_got,
                         .load_tlv,
                         .lea_tlv,
                         .lea_frame,
@@ -8031,12 +8033,12 @@ fn genBinOp(
                         .register_pair,
                         .register_offset,
                         .register_overflow,
+                        .load_memory,
+                        .lea_memory,
                         .load_direct,
                         .lea_direct,
                         .load_got,
                         .lea_got,
-                        .load_extern_got,
-                        .lea_extern_got,
                         .load_tlv,
                         .lea_tlv,
                         .lea_frame,
@@ -9164,9 +9166,9 @@ fn genBinOpMir(
         .register_overflow,
         .lea_direct,
         .lea_got,
-        .lea_extern_got,
         .lea_tlv,
         .lea_frame,
+        .lea_memory,
         .reserved_frame,
         .air_ref,
         => unreachable, // unmodifiable destination
@@ -9249,12 +9251,12 @@ fn genBinOpMir(
                     .register_offset,
                     .memory,
                     .indirect,
+                    .load_memory,
+                    .lea_memory,
                     .load_direct,
                     .lea_direct,
                     .load_got,
                     .lea_got,
-                    .load_extern_got,
-                    .lea_extern_got,
                     .load_tlv,
                     .lea_tlv,
                     .load_frame,
@@ -9285,9 +9287,9 @@ fn genBinOpMir(
                         switch (src_mcv) {
                             .eflags,
                             .register_offset,
+                            .lea_memory,
                             .lea_direct,
                             .lea_got,
-                            .lea_extern_got,
                             .lea_tlv,
                             .lea_frame,
                             => {
@@ -9301,9 +9303,9 @@ fn genBinOpMir(
                                 );
                             },
                             .memory,
+                            .load_memory,
                             .load_direct,
                             .load_got,
-                            .load_extern_got,
                             .load_tlv,
                             => {
                                 const ptr_ty = try mod.singleConstPtrType(ty);
@@ -9324,13 +9326,13 @@ fn genBinOpMir(
                 }
             }
         },
-        .memory, .indirect, .load_got, .load_extern_got, .load_direct, .load_tlv, .load_frame => {
+        .memory, .indirect, .load_memory, .load_got, .load_direct, .load_tlv, .load_frame => {
             const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock };
             const limb_abi_size: u32 = @min(abi_size, 8);
 
             const dst_info: OpInfo = switch (dst_mcv) {
                 else => unreachable,
-                .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => dst: {
+                .memory, .load_memory, .load_got, .load_direct, .load_tlv => dst: {
                     const dst_addr_reg =
                         (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64();
                     const dst_addr_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg);
@@ -9364,17 +9366,17 @@ fn genBinOpMir(
                 .indirect,
                 .lea_direct,
                 .lea_got,
-                .lea_extern_got,
                 .lea_tlv,
                 .load_frame,
                 .lea_frame,
+                .lea_memory,
                 => null,
-                .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => src: {
+                .memory, .load_memory, .load_got, .load_direct, .load_tlv => src: {
                     switch (resolved_src_mcv) {
                         .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr))) != null and
                             math.cast(i32, @as(i64, @bitCast(addr)) + abi_size - limb_abi_size) != null)
                             break :src null,
-                        .load_got, .load_extern_got, .load_direct, .load_tlv => {},
+                        .load_memory, .load_got, .load_direct, .load_tlv => {},
                         else => unreachable,
                     }
 
@@ -9416,8 +9418,8 @@ fn genBinOpMir(
                     Memory.PtrSize.fromSize(limb_abi_size),
                     switch (dst_mcv) {
                         .memory,
+                        .load_memory,
                         .load_got,
-                        .load_extern_got,
                         .load_direct,
                         .load_tlv,
                         => .{ .base = .{ .reg = dst_info.?.addr_reg }, .disp = off },
@@ -9498,12 +9500,12 @@ fn genBinOpMir(
                     .eflags,
                     .memory,
                     .indirect,
+                    .load_memory,
+                    .lea_memory,
                     .load_direct,
                     .lea_direct,
                     .load_got,
                     .lea_got,
-                    .load_extern_got,
-                    .lea_extern_got,
                     .load_tlv,
                     .lea_tlv,
                     .load_frame,
@@ -9517,9 +9519,9 @@ fn genBinOpMir(
                             },
                             .eflags,
                             .register_offset,
+                            .lea_memory,
                             .lea_direct,
                             .lea_got,
-                            .lea_extern_got,
                             .lea_tlv,
                             .lea_frame,
                             => switch (limb_i) {
@@ -9568,9 +9570,9 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
         .eflags,
         .register_offset,
         .register_overflow,
+        .lea_memory,
         .lea_direct,
         .lea_got,
-        .lea_extern_got,
         .lea_tlv,
         .lea_frame,
         .reserved_frame,
@@ -9615,12 +9617,12 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
                 },
                 .register_offset,
                 .eflags,
+                .load_memory,
+                .lea_memory,
                 .load_direct,
                 .lea_direct,
                 .load_got,
                 .lea_got,
-                .load_extern_got,
-                .lea_extern_got,
                 .load_tlv,
                 .lea_tlv,
                 .lea_frame,
@@ -9659,7 +9661,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
             }
         },
         .register_pair => unreachable, // unimplemented
-        .memory, .indirect, .load_direct, .load_got, .load_extern_got, .load_tlv, .load_frame => {
+        .memory, .indirect, .load_memory, .load_direct, .load_got, .load_tlv, .load_frame => {
             const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
             const tmp_mcv = MCValue{ .register = tmp_reg };
             const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
@@ -9759,8 +9761,8 @@ fn genVarDbgInfo(
                 //    .offset = -off,
                 //} },
                 .memory => |address| .{ .memory = address },
+                .load_memory => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } }, // TODO
                 .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } },
-                .load_extern_got => |sym_index| .{ .linker_load = .{ .type = .extern_got, .sym_index = sym_index } },
                 .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },
                 .immediate => |x| .{ .immediate = x },
                 .undef => .undef,
@@ -10011,12 +10013,12 @@ fn genCall(self: *Self, info: union(enum) {
                         const sym = elf_file.symbol(sym_index);
                         _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
                         if (self.bin_file.options.pic) {
-                            try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym.esym_index });
+                            try self.genSetReg(.rax, Type.usize, .{ .lea_memory = sym.esym_index });
                             try self.asmRegister(.{ ._, .call }, .rax);
                         } else {
                             _ = try self.addInst(.{
                                 .tag = .call,
-                                .ops = .direct_got_reloc,
+                                .ops = .linker_reloc,
                                 .data = .{ .reloc = .{
                                     .atom_index = try self.owner.getSymbolIndex(self),
                                     .sym_index = sym.esym_index,
@@ -10241,14 +10243,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
                                     .indirect,
                                     .lea_direct,
                                     .lea_got,
-                                    .lea_extern_got,
                                     .lea_tlv,
                                     .lea_frame,
+                                    .lea_memory,
                                     .reserved_frame,
                                     .air_ref,
                                     => unreachable,
                                     .register_pair, .load_frame => null,
-                                    .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => dst: {
+                                    .memory, .load_memory, .load_got, .load_direct, .load_tlv => dst: {
                                         switch (resolved_dst_mcv) {
                                             .memory => |addr| if (math.cast(
                                                 i32,
@@ -10257,7 +10259,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
                                                 i32,
                                                 @as(i64, @bitCast(addr)) + abi_size - 8,
                                             ) != null) break :dst null,
-                                            .load_got, .load_extern_got, .load_direct, .load_tlv => {},
+                                            .load_memory, .load_got, .load_direct, .load_tlv => {},
                                             else => unreachable,
                                         }
 
@@ -10298,16 +10300,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
                                     .register_offset,
                                     .register_overflow,
                                     .indirect,
+                                    .lea_memory,
                                     .lea_direct,
                                     .lea_got,
-                                    .lea_extern_got,
                                     .lea_tlv,
                                     .lea_frame,
                                     .reserved_frame,
                                     .air_ref,
                                     => unreachable,
                                     .register_pair, .load_frame => null,
-                                    .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => src: {
+                                    .memory, .load_memory, .load_got, .load_direct, .load_tlv => src: {
                                         switch (resolved_src_mcv) {
                                             .memory => |addr| if (math.cast(
                                                 i32,
@@ -10316,7 +10318,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
                                                 i32,
                                                 @as(i64, @bitCast(addr)) + abi_size - 8,
                                             ) != null) break :src null,
-                                            .load_got, .load_extern_got, .load_direct, .load_tlv => {},
+                                            .load_memory, .load_got, .load_direct, .load_tlv => {},
                                             else => unreachable,
                                         }
 
@@ -10739,9 +10741,9 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
         .register_overflow,
         .lea_direct,
         .lea_got,
-        .lea_extern_got,
         .lea_tlv,
         .lea_frame,
+        .lea_memory,
         .reserved_frame,
         .air_ref,
         => unreachable,
@@ -10765,8 +10767,8 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
         },
 
         .memory,
+        .load_memory,
         .load_got,
-        .load_extern_got,
         .load_direct,
         .load_tlv,
         => {
@@ -11363,7 +11365,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
                 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_|
                     break :arg input_mcv,
                 .indirect, .load_frame => break :arg input_mcv,
-                .load_direct, .load_got, .load_extern_got, .load_tlv => {},
+                .load_memory, .load_direct, .load_got, .load_tlv => {},
                 else => {
                     const temp_mcv = try self.allocTempRegOrMem(ty, false);
                     try self.genCopy(ty, temp_mcv, input_mcv);
@@ -11625,6 +11627,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
                         .{ .reg = try self.copyToTmpRegister(Type.usize, .{ .lea_got = sym_index }) }
                     else
                         return self.fail("invalid modifier: '{s}'", .{modifier}),
+                    .lea_memory => |sym_index| if (mem.eql(u8, modifier, "P"))
+                        .{ .reg = try self.copyToTmpRegister(Type.usize, .{ .lea_memory = sym_index }) }
+                    else
+                        return self.fail("invalid modifier: '{s}'", .{modifier}),
                     else => return self.fail("invalid constraint: '{s}'", .{op_str}),
                 };
             } else if (mem.startsWith(u8, op_str, "$")) {
@@ -12178,9 +12184,9 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError
         .register_overflow,
         .lea_direct,
         .lea_got,
-        .lea_extern_got,
         .lea_tlv,
         .lea_frame,
+        .lea_memory,
         .reserved_frame,
         .air_ref,
         => unreachable, // unmodifiable destination
@@ -12236,6 +12242,12 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError
                         class_ty,
                         .{ .register = src_regs[dst_reg_i] },
                     ),
+                    .load_memory => {
+                        const addr_reg = try self.copyToTmpRegister(Type.usize, src_mcv.address());
+                        const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
+                        defer self.register_manager.unlockReg(addr_lock);
+                        try self.genCopy(ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } });
+                    },
                     .memory, .indirect, .load_frame => try self.genSetReg(
                         dst_reg,
                         class_ty,
@@ -12251,11 +12263,11 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError
             }
         },
         .indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv),
-        .memory, .load_direct, .load_got, .load_extern_got, .load_tlv => {
+        .memory, .load_memory, .load_direct, .load_got, .load_tlv => {
             switch (dst_mcv) {
                 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr|
                     return self.genSetMem(.{ .reg = .ds }, small_addr, ty, src_mcv),
-                .load_direct, .load_got, .load_extern_got, .load_tlv => {},
+                .load_memory, .load_direct, .load_got, .load_tlv => {},
                 else => unreachable,
             }
 
@@ -12431,7 +12443,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
                 else => unreachable,
             },
         )),
-        .memory, .load_direct, .load_got, .load_extern_got, .load_tlv => {
+        .memory, .load_memory, .load_direct, .load_got, .load_tlv => {
             switch (src_mcv) {
                 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr|
                     return (try self.moveStrategy(
@@ -12461,7 +12473,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
                     .segment, .mmx => unreachable,
                     .x87, .sse => {},
                 },
-                .load_got, .load_extern_got, .load_tlv => {},
+                .load_memory, .load_got, .load_tlv => {},
                 else => unreachable,
             }
 
@@ -12475,28 +12487,42 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
                 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .{ .reg = addr_reg } }),
             );
         },
-        .lea_direct, .lea_got, .lea_extern_got => |sym_index| {
+        .lea_memory, .lea_direct, .lea_got => |sym_index| {
             const atom_index = try self.owner.getSymbolIndex(self);
-            _ = try self.addInst(.{
-                .tag = switch (src_mcv) {
-                    .lea_direct => .lea,
-                    .lea_got, .lea_extern_got => .mov,
-                    else => unreachable,
-                },
-                .ops = switch (src_mcv) {
-                    .lea_direct => .direct_reloc,
-                    .lea_got => .got_reloc,
-                    .lea_extern_got => .extern_got_reloc,
-                    else => unreachable,
-                },
-                .data = .{ .rx = .{
-                    .r1 = dst_reg.to64(),
-                    .payload = try self.addExtra(Mir.Reloc{
-                        .atom_index = atom_index,
-                        .sym_index = sym_index,
-                    }),
-                } },
-            });
+            if (self.bin_file.cast(link.File.Elf)) |elf_file| {
+                const sym = elf_file.symbol(elf_file.zigModulePtr().symbol(sym_index));
+                _ = try self.addInst(.{
+                    .tag = if (sym.flags.has_zig_got) .mov else .lea,
+                    .ops = .linker_reloc,
+                    .data = .{ .rx = .{
+                        .r1 = dst_reg.to64(),
+                        .payload = try self.addExtra(Mir.Reloc{
+                            .atom_index = atom_index,
+                            .sym_index = sym_index,
+                        }),
+                    } },
+                });
+            } else {
+                _ = try self.addInst(.{
+                    .tag = switch (src_mcv) {
+                        .lea_direct => .lea,
+                        .lea_got => .mov,
+                        else => unreachable,
+                    },
+                    .ops = switch (src_mcv) {
+                        .lea_direct => .direct_reloc,
+                        .lea_got => .got_reloc,
+                        else => unreachable,
+                    },
+                    .data = .{ .rx = .{
+                        .r1 = dst_reg.to64(),
+                        .payload = try self.addExtra(Mir.Reloc{
+                            .atom_index = atom_index,
+                            .sym_index = sym_index,
+                        }),
+                    } },
+                });
+            }
         },
         .lea_tlv => |sym_index| {
             const atom_index = try self.owner.getSymbolIndex(self);
@@ -12645,12 +12671,12 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal
         .lea_direct,
         .load_got,
         .lea_got,
-        .load_extern_got,
-        .lea_extern_got,
         .load_tlv,
         .lea_tlv,
         .load_frame,
         .lea_frame,
+        .load_memory,
+        .lea_memory,
         => switch (abi_size) {
             0 => {},
             1, 2, 4, 8 => {
@@ -12741,8 +12767,8 @@ fn genLazySymbolRef(
 
         if (self.bin_file.options.pic) {
             switch (tag) {
-                .lea, .call => try self.genSetReg(reg, Type.usize, .{ .lea_got = sym.esym_index }),
-                .mov => try self.genSetReg(reg, Type.usize, .{ .load_got = sym.esym_index }),
+                .lea, .call => try self.genSetReg(reg, Type.usize, .{ .lea_memory = sym.esym_index }),
+                .mov => try self.genSetReg(reg, Type.usize, .{ .load_memory = sym.esym_index }),
                 else => unreachable,
             }
             switch (tag) {
@@ -12758,7 +12784,7 @@ fn genLazySymbolRef(
             switch (tag) {
                 .lea, .mov => _ = try self.addInst(.{
                     .tag = .mov,
-                    .ops = .direct_got_reloc,
+                    .ops = .linker_reloc,
                     .data = .{ .rx = .{
                         .r1 = reg.to64(),
                         .payload = try self.addExtra(reloc),
@@ -12766,7 +12792,7 @@ fn genLazySymbolRef(
                 }),
                 .call => _ = try self.addInst(.{
                     .tag = .call,
-                    .ops = .direct_got_reloc,
+                    .ops = .linker_reloc,
                     .data = .{ .reloc = reloc },
                 }),
                 else => unreachable,
@@ -14716,9 +14742,10 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
             .undef => .undef,
             .immediate => |imm| .{ .immediate = imm },
             .memory => |addr| .{ .memory = addr },
+            .load_memory => |sym_index| .{ .load_memory = sym_index },
+            .lea_memory => |sym_index| .{ .lea_memory = sym_index },
             .load_direct => |sym_index| .{ .load_direct = sym_index },
             .load_got => |sym_index| .{ .lea_got = sym_index },
-            .load_extern_got => |sym_index| .{ .lea_extern_got = sym_index },
             .load_tlv => |sym_index| .{ .lea_tlv = sym_index },
         },
         .fail => |msg| {
src/arch/x86_64/Emit.zig
@@ -78,31 +78,41 @@ pub fn emitMir(emit: *Emit) Error!void {
                 } else return emit.fail("TODO implement extern reloc for {s}", .{
                     @tagName(emit.bin_file.tag),
                 }),
+                .linker_reloc => |data| if (emit.bin_file.cast(link.File.Elf)) |elf_file| {
+                    const atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
+                    const sym = elf_file.symbol(elf_file.zigModulePtr().symbol(data.sym_index));
+                    if (emit.bin_file.options.pic) {
+                        const r_type: u32 = if (sym.flags.has_zig_got)
+                            link.File.Elf.R_X86_64_ZIG_GOTPCREL
+                        else if (sym.flags.has_got)
+                            std.elf.R_X86_64_GOTPCREL
+                        else
+                            std.elf.R_X86_64_PC32;
+                        try atom.addReloc(elf_file, .{
+                            .r_offset = end_offset - 4,
+                            .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
+                            .r_addend = -4,
+                        });
+                    } else {
+                        const r_type: u32 = if (sym.flags.has_zig_got)
+                            link.File.Elf.R_X86_64_ZIG_GOT32
+                        else if (sym.flags.has_got)
+                            std.elf.R_X86_64_GOT32
+                        else
+                            std.elf.R_X86_64_32;
+                        try atom.addReloc(elf_file, .{
+                            .r_offset = end_offset - 4,
+                            .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
+                            .r_addend = 0,
+                        });
+                    }
+                } else unreachable,
                 .linker_got,
-                .linker_extern_got,
                 .linker_direct,
-                .linker_direct_got,
                 .linker_import,
                 .linker_tlv,
-                => |symbol| if (emit.bin_file.cast(link.File.Elf)) |elf_file| {
-                    const r_type: u32 = switch (lowered_relocs[0].target) {
-                        .linker_direct_got => link.File.Elf.R_X86_64_ZIG_GOT32,
-                        .linker_got => link.File.Elf.R_X86_64_ZIG_GOTPCREL,
-                        .linker_extern_got => std.elf.R_X86_64_GOTPCREL,
-                        .linker_direct => std.elf.R_X86_64_PC32,
-                        else => unreachable,
-                    };
-                    const r_addend: i64 = switch (lowered_relocs[0].target) {
-                        .linker_direct_got => 0,
-                        .linker_got, .linker_extern_got, .linker_direct => -4,
-                        else => unreachable,
-                    };
-                    const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
-                    try atom_ptr.addReloc(elf_file, .{
-                        .r_offset = end_offset - 4,
-                        .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
-                        .r_addend = r_addend,
-                    });
+                => |symbol| if (emit.bin_file.cast(link.File.Elf)) |_| {
+                    unreachable;
                 } else if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
                     const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
                     try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
src/arch/x86_64/Lower.zig
@@ -1,5 +1,6 @@
 //! This file contains the functionality for lowering x86_64 MIR to Instructions
 
+bin_file: *link.File,
 allocator: Allocator,
 mir: Mir,
 cc: std.builtin.CallingConvention,
@@ -49,11 +50,10 @@ pub const Reloc = struct {
 
     const Target = union(enum) {
         inst: Mir.Inst.Index,
+        linker_reloc: Mir.Reloc,
         linker_extern_fn: Mir.Reloc,
         linker_got: Mir.Reloc,
-        linker_extern_got: Mir.Reloc,
         linker_direct: Mir.Reloc,
-        linker_direct_got: Mir.Reloc,
         linker_import: Mir.Reloc,
         linker_tlv: Mir.Reloc,
     };
@@ -407,10 +407,9 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
         .mi_sib_u, .mi_rip_u, .mi_sib_s, .mi_rip_s => inst.data.x.fixes,
         .m_sib, .m_rip, .rax_moffs, .moffs_rax => inst.data.x.fixes,
         .extern_fn_reloc,
+        .linker_reloc,
         .got_reloc,
-        .extern_got_reloc,
         .direct_reloc,
-        .direct_got_reloc,
         .import_reloc,
         .tlv_reloc,
         => ._,
@@ -545,32 +544,50 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
         .extern_fn_reloc => &.{
             .{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc }) },
         },
-        .direct_got_reloc => ops: {
-            switch (inst.tag) {
-                .call => {
-                    _ = lower.reloc(.{ .linker_direct_got = inst.data.reloc });
-                    break :ops &.{
-                        .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0 }) },
-                    };
-                },
-                .mov => {
-                    const reg = inst.data.rx.r1;
-                    const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;
-                    _ = lower.reloc(.{ .linker_direct_got = extra });
-                    break :ops &.{
-                        .{ .reg = reg },
-                        .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0 }) },
-                    };
-                },
-                else => unreachable,
+        .linker_reloc => ops: {
+            if (lower.bin_file.options.pic) {
+                const reg = inst.data.rx.r1;
+                const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;
+                _ = lower.reloc(.{ .linker_reloc = extra });
+                break :ops &.{
+                    .{ .reg = reg },
+                    .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
+                };
+            } else {
+                switch (inst.tag) {
+                    .call => {
+                        _ = lower.reloc(.{ .linker_reloc = inst.data.reloc });
+                        break :ops &.{
+                            .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0 }) },
+                        };
+                    },
+                    .mov => {
+                        const reg = inst.data.rx.r1;
+                        const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;
+                        _ = lower.reloc(.{ .linker_reloc = extra });
+                        break :ops &.{
+                            .{ .reg = reg },
+                            .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0 }) },
+                        };
+                    },
+                    .lea => {
+                        const reg = inst.data.rx.r1;
+                        const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;
+                        _ = lower.reloc(.{ .linker_reloc = extra });
+                        break :ops &.{
+                            .{ .reg = reg },
+                            .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0 }) },
+                        };
+                    },
+                    else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
+                }
             }
         },
-        .got_reloc, .extern_got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ops: {
+        .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ops: {
             const reg = inst.data.rx.r1;
             const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;
             _ = lower.reloc(switch (inst.ops) {
                 .got_reloc => .{ .linker_got = extra },
-                .extern_got_reloc => .{ .linker_extern_got = extra },
                 .direct_reloc => .{ .linker_direct = extra },
                 .import_reloc => .{ .linker_import = extra },
                 .tlv_reloc => .{ .linker_tlv = extra },
@@ -601,6 +618,7 @@ const abi = @import("abi.zig");
 const assert = std.debug.assert;
 const bits = @import("bits.zig");
 const encoder = @import("encoder.zig");
+const link = @import("../../link.zig");
 const std = @import("std");
 
 const Air = @import("../../Air.zig");
src/arch/x86_64/Mir.zig
@@ -854,9 +854,6 @@ pub const Inst = struct {
         /// Linker relocation - GOT indirection.
         /// Uses `rx` payload with extra data of type `Reloc`.
         got_reloc,
-        /// Linker relocation - reference to an extern variable via GOT.
-        /// Uses `rx` payload with extra data of type `Reloc`.
-        extern_got_reloc,
         /// Linker relocation - direct reference.
         /// Uses `rx` payload with extra data of type `Reloc`.
         direct_reloc,
@@ -866,9 +863,9 @@ pub const Inst = struct {
         /// Linker relocation - threadlocal variable via GOT indirection.
         /// Uses `rx` payload with extra data of type `Reloc`.
         tlv_reloc,
-        /// Linker relocation - non-PIC direct reference to GOT cell.
-        /// Uses `reloc` payload if tag is `call`, `rx` otherwise.
-        direct_got_reloc,
+        /// Linker relocation.
+        /// Uses `rx` payload with extra data of type `Reloc`.
+        linker_reloc,
 
         // Pseudo instructions:
 
src/link/Elf/Atom.zig
@@ -370,6 +370,7 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
                 try self.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
             },
 
+            elf.R_X86_64_GOT32,
             elf.R_X86_64_GOTPC32,
             elf.R_X86_64_GOTPC64,
             elf.R_X86_64_GOTPCREL,
@@ -879,6 +880,8 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
                 }
             },
 
+            elf.R_X86_64_GOT32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A))),
+
             // Zig custom relocations
             Elf.R_X86_64_ZIG_GOT32 => try cwriter.writeIntLittle(u32, @as(u32, @intCast(ZIG_GOT + A))),
             Elf.R_X86_64_ZIG_GOTPCREL => try cwriter.writeIntLittle(i32, @as(i32, @intCast(ZIG_GOT + A - P))),
src/link/Elf.zig
@@ -5955,6 +5955,12 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32
     return lookup_gop.value_ptr.*;
 }
 
+pub fn zigModulePtr(self: *Elf) *ZigModule {
+    assert(self.zig_module_index != null);
+    const file_ptr = self.file(self.zig_module_index.?).?;
+    return file_ptr.zig_module;
+}
+
 const GetOrCreateComdatGroupOwnerResult = struct {
     found_existing: bool,
     index: ComdatGroupOwner.Index,
src/codegen.zig
@@ -791,13 +791,11 @@ fn lowerDeclRef(
 
 /// Helper struct to denote that the value is in memory but requires a linker relocation fixup:
 /// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc)
-/// * extern_got - pointer to extern variable referenced via GOT
 /// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc)
 /// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc)
 pub const LinkerLoad = struct {
     type: enum {
         got,
-        extern_got,
         direct,
         import,
     },
@@ -827,8 +825,10 @@ pub const GenResult = union(enum) {
         load_got: u32,
         /// Direct by-address reference to memory location.
         memory: u64,
-        /// Pointer to extern variable via GOT.
-        load_extern_got: u32,
+        /// Reference to memory location but deferred until linker allocated the Decl in memory.
+        /// Traditionally, this corresponds to emitting a relocation in a relocatable object file.
+        load_memory: u32,
+        lea_memory: u32,
     };
 
     fn mcv(val: MCValue) GenResult {
@@ -903,16 +903,13 @@ fn genDeclRef(
                 mod.intern_pool.stringToSliceUnwrap(ov.lib_name)
             else
                 null;
-            return GenResult.mcv(.{ .load_extern_got = try elf_file.getGlobalSymbol(name, lib_name) });
+            const sym_index = try elf_file.getGlobalSymbol(name, lib_name);
+            return GenResult.mcv(.{ .lea_memory = sym_index });
         }
         const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index);
         const sym = elf_file.symbol(sym_index);
         _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
-        if (bin_file.options.pic) {
-            return GenResult.mcv(.{ .load_got = sym.esym_index });
-        } else {
-            return GenResult.mcv(.{ .memory = sym.zigGotAddress(elf_file) });
-        }
+        return GenResult.mcv(.{ .lea_memory = sym.esym_index });
     } else if (bin_file.cast(link.File.MachO)) |macho_file| {
         const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
         const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
@@ -948,11 +945,7 @@ fn genUnnamedConst(
     };
     if (bin_file.cast(link.File.Elf)) |elf_file| {
         const local = elf_file.symbol(local_sym_index);
-        if (bin_file.options.pic) {
-            return GenResult.mcv(.{ .load_direct = local.esym_index });
-        } else {
-            return GenResult.mcv(.{ .memory = local.value });
-        }
+        return GenResult.mcv(.{ .load_memory = local.esym_index });
     } else if (bin_file.cast(link.File.MachO)) |_| {
         return GenResult.mcv(.{ .load_direct = local_sym_index });
     } else if (bin_file.cast(link.File.Coff)) |_| {