Commit 45197ea7ad

Jakub Konka <kubkon@jakubkonka.com>
2023-10-15 09:10:46
codegen+elf: lower imported data refs
1 parent c71a79f
src/arch/aarch64/CodeGen.zig
@@ -4012,6 +4012,7 @@ 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,
+                                    .actual_got => unreachable,
                                 };
                                 const atom_index = switch (self.bin_file.tag) {
                                     .macho => blk: {
@@ -5531,6 +5532,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
                             .got => .load_memory_ptr_got,
                             .direct => .load_memory_ptr_direct,
                             .import => unreachable,
+                            .actual_got => unreachable,
                         };
                         const atom_index = switch (self.bin_file.tag) {
                             .macho => blk: {
@@ -5652,6 +5654,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
                 .got => .load_memory_got,
                 .direct => .load_memory_direct,
                 .import => .load_memory_import,
+                .actual_got => unreachable,
             };
             const atom_index = switch (self.bin_file.tag) {
                 .macho => blk: {
@@ -5849,6 +5852,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
                             .got => .load_memory_ptr_got,
                             .direct => .load_memory_ptr_direct,
                             .import => unreachable,
+                            .actual_got => unreachable,
                         };
                         const atom_index = switch (self.bin_file.tag) {
                             .macho => blk: {
@@ -6176,7 +6180,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_tlv => unreachable, // TODO
+            .load_actual_got, .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_direct, .load_tlv => unreachable, // TODO
+            .load_got, .load_actual_got, .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_direct, .load_tlv => unreachable, // TODO
+            .load_got, .load_actual_got, .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_direct, .load_tlv => unreachable, // TODO
+            .load_got, .load_actual_got, .load_direct, .load_tlv => unreachable, // TODO
             .immediate => |imm| .{ .immediate = imm },
             .memory => |addr| .{ .memory = addr },
         },
src/arch/x86_64/CodeGen.zig
@@ -207,6 +207,10 @@ 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,
+    /// TODO indirection via actual .got table
+    /// LOL Jakub, the king of naming...
+    load_actual_got: u32,
+    lea_actual_got: u32,
     /// The value is a threadlocal variable.
     /// Payload is a symbol index.
     load_tlv: u32,
@@ -295,6 +299,7 @@ pub const MCValue = union(enum) {
             .register_overflow,
             .lea_direct,
             .lea_got,
+            .lea_actual_got,
             .lea_tlv,
             .lea_frame,
             .reserved_frame,
@@ -308,6 +313,7 @@ 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_actual_got => |sym_index| .{ .lea_actual_got = sym_index },
             .load_frame => |frame_addr| .{ .lea_frame = frame_addr },
         };
     }
@@ -325,6 +331,7 @@ pub const MCValue = union(enum) {
             .indirect,
             .load_direct,
             .load_got,
+            .load_actual_got,
             .load_tlv,
             .load_frame,
             .reserved_frame,
@@ -335,6 +342,7 @@ 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_actual_got => |sym_index| .{ .load_actual_got = sym_index },
             .lea_tlv => |sym_index| .{ .load_tlv = sym_index },
             .lea_frame => |frame_addr| .{ .load_frame = frame_addr },
         };
@@ -358,6 +366,8 @@ pub const MCValue = union(enum) {
             .lea_direct,
             .load_got,
             .lea_got,
+            .load_actual_got,
+            .lea_actual_got,
             .load_tlv,
             .lea_tlv,
             .load_frame,
@@ -392,6 +402,8 @@ pub const MCValue = union(enum) {
             .lea_direct,
             .load_got,
             .lea_got,
+            .load_actual_got,
+            .lea_actual_got,
             .load_tlv,
             .lea_tlv,
             .lea_frame,
@@ -434,6 +446,8 @@ 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_actual_got => |pl| try writer.print("[actual_got:{d}]", .{pl}),
+            .lea_actual_got => |pl| try writer.print("actual_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 }),
@@ -461,6 +475,8 @@ const InstTracking = struct {
             .lea_direct,
             .load_got,
             .lea_got,
+            .load_actual_got,
+            .lea_actual_got,
             .load_tlv,
             .lea_tlv,
             .load_frame,
@@ -520,6 +536,8 @@ const InstTracking = struct {
             .lea_direct,
             .load_got,
             .lea_got,
+            .load_actual_got,
+            .lea_actual_got,
             .load_tlv,
             .lea_tlv,
             .load_frame,
@@ -555,6 +573,8 @@ const InstTracking = struct {
             .lea_direct,
             .load_got,
             .lea_got,
+            .load_actual_got,
+            .lea_actual_got,
             .load_tlv,
             .lea_tlv,
             .lea_frame,
@@ -4371,6 +4391,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
         .memory,
         .load_direct,
         .load_got,
+        .load_actual_got,
         .load_tlv,
         => try self.genSetReg(addr_reg, Type.usize, array.address()),
         .lea_direct, .lea_tlv => unreachable,
@@ -5851,6 +5872,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro
         .register_offset,
         .lea_direct,
         .lea_got,
+        .lea_actual_got,
         .lea_tlv,
         .lea_frame,
         => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()),
@@ -5858,6 +5880,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro
         .indirect,
         .load_direct,
         .load_got,
+        .load_actual_got,
         .load_tlv,
         .load_frame,
         => {
@@ -5996,6 +6019,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr
         .register_offset,
         .lea_direct,
         .lea_got,
+        .lea_actual_got,
         .lea_tlv,
         .lea_frame,
         => try self.genCopy(src_ty, ptr_mcv.deref(), src_mcv),
@@ -6003,6 +6027,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr
         .indirect,
         .load_direct,
         .load_got,
+        .load_actual_got,
         .load_tlv,
         .load_frame,
         => {
@@ -6424,6 +6449,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC
         .register_overflow,
         .lea_direct,
         .lea_got,
+        .lea_actual_got,
         .lea_tlv,
         .lea_frame,
         .reserved_frame,
@@ -6431,7 +6457,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC
         => unreachable, // unmodifiable destination
         .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)),
         .register_pair => unreachable, // unimplemented
-        .memory, .load_got, .load_direct, .load_tlv => {
+        .memory, .load_got, .load_actual_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);
@@ -7389,6 +7415,8 @@ fn genBinOp(
                         .lea_direct,
                         .load_got,
                         .lea_got,
+                        .load_actual_got,
+                        .lea_actual_got,
                         .load_tlv,
                         .lea_tlv,
                         .lea_frame,
@@ -7445,6 +7473,8 @@ fn genBinOp(
                         .lea_direct,
                         .load_got,
                         .lea_got,
+                        .load_actual_got,
+                        .lea_actual_got,
                         .load_tlv,
                         .lea_tlv,
                         .lea_frame,
@@ -8397,6 +8427,7 @@ fn genBinOpMir(
         .register_overflow,
         .lea_direct,
         .lea_got,
+        .lea_actual_got,
         .lea_tlv,
         .lea_frame,
         .reserved_frame,
@@ -8485,6 +8516,8 @@ fn genBinOpMir(
                     .lea_direct,
                     .load_got,
                     .lea_got,
+                    .load_actual_got,
+                    .lea_actual_got,
                     .load_tlv,
                     .lea_tlv,
                     .load_frame,
@@ -8517,6 +8550,7 @@ fn genBinOpMir(
                             .register_offset,
                             .lea_direct,
                             .lea_got,
+                            .lea_actual_got,
                             .lea_tlv,
                             .lea_frame,
                             => {
@@ -8532,6 +8566,7 @@ fn genBinOpMir(
                             .memory,
                             .load_direct,
                             .load_got,
+                            .load_actual_got,
                             .load_tlv,
                             => {
                                 const ptr_ty = try mod.singleConstPtrType(ty);
@@ -8552,13 +8587,13 @@ fn genBinOpMir(
                 }
             }
         },
-        .memory, .indirect, .load_got, .load_direct, .load_tlv, .load_frame => {
+        .memory, .indirect, .load_got, .load_actual_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_direct, .load_tlv => dst: {
+                .memory, .load_got, .load_actual_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);
@@ -8592,16 +8627,17 @@ fn genBinOpMir(
                 .indirect,
                 .lea_direct,
                 .lea_got,
+                .lea_actual_got,
                 .lea_tlv,
                 .load_frame,
                 .lea_frame,
                 => null,
-                .memory, .load_got, .load_direct, .load_tlv => src: {
+                .memory, .load_got, .load_actual_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_direct, .load_tlv => {},
+                        .load_got, .load_actual_got, .load_direct, .load_tlv => {},
                         else => unreachable,
                     }
 
@@ -8644,6 +8680,7 @@ fn genBinOpMir(
                     switch (dst_mcv) {
                         .memory,
                         .load_got,
+                        .load_actual_got,
                         .load_direct,
                         .load_tlv,
                         => .{ .base = .{ .reg = dst_info.?.addr_reg }, .disp = off },
@@ -8728,6 +8765,8 @@ fn genBinOpMir(
                     .lea_direct,
                     .load_got,
                     .lea_got,
+                    .load_actual_got,
+                    .lea_actual_got,
                     .load_tlv,
                     .lea_tlv,
                     .load_frame,
@@ -8743,6 +8782,7 @@ fn genBinOpMir(
                             .register_offset,
                             .lea_direct,
                             .lea_got,
+                            .lea_actual_got,
                             .lea_tlv,
                             .lea_frame,
                             => switch (limb_i) {
@@ -8792,6 +8832,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
         .register_overflow,
         .lea_direct,
         .lea_got,
+        .lea_actual_got,
         .lea_tlv,
         .lea_frame,
         .reserved_frame,
@@ -8840,6 +8881,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
                 .lea_direct,
                 .load_got,
                 .lea_got,
+                .load_actual_got,
+                .lea_actual_got,
                 .load_tlv,
                 .lea_tlv,
                 .lea_frame,
@@ -8878,7 +8921,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_tlv, .load_frame => {
+        .memory, .indirect, .load_direct, .load_got, .load_actual_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);
@@ -8971,6 +9014,7 @@ fn genVarDbgInfo(
                 //} },
                 .memory => |address| .{ .memory = address },
                 .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } },
+                .load_actual_got => |sym_index| .{ .linker_load = .{ .type = .actual_got, .sym_index = sym_index } },
                 .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },
                 .immediate => |x| .{ .immediate = x },
                 .undef => .undef,
@@ -9410,13 +9454,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
                                     .indirect,
                                     .lea_direct,
                                     .lea_got,
+                                    .lea_actual_got,
                                     .lea_tlv,
                                     .lea_frame,
                                     .reserved_frame,
                                     .air_ref,
                                     => unreachable,
                                     .register_pair, .load_frame => null,
-                                    .memory, .load_got, .load_direct, .load_tlv => dst: {
+                                    .memory, .load_got, .load_actual_got, .load_direct, .load_tlv => dst: {
                                         switch (resolved_dst_mcv) {
                                             .memory => |addr| if (math.cast(
                                                 i32,
@@ -9425,7 +9470,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_direct, .load_tlv => {},
+                                            .load_got, .load_actual_got, .load_direct, .load_tlv => {},
                                             else => unreachable,
                                         }
 
@@ -9468,13 +9513,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
                                     .indirect,
                                     .lea_direct,
                                     .lea_got,
+                                    .lea_actual_got,
                                     .lea_tlv,
                                     .lea_frame,
                                     .reserved_frame,
                                     .air_ref,
                                     => unreachable,
                                     .register_pair, .load_frame => null,
-                                    .memory, .load_got, .load_direct, .load_tlv => src: {
+                                    .memory, .load_got, .load_actual_got, .load_direct, .load_tlv => src: {
                                         switch (resolved_src_mcv) {
                                             .memory => |addr| if (math.cast(
                                                 i32,
@@ -9483,7 +9529,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_direct, .load_tlv => {},
+                                            .load_got, .load_actual_got, .load_direct, .load_tlv => {},
                                             else => unreachable,
                                         }
 
@@ -9902,6 +9948,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
         .register_overflow,
         .lea_direct,
         .lea_got,
+        .lea_actual_got,
         .lea_tlv,
         .lea_frame,
         .reserved_frame,
@@ -9928,6 +9975,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
 
         .memory,
         .load_got,
+        .load_actual_got,
         .load_direct,
         .load_tlv,
         => {
@@ -10485,7 +10533,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_tlv => {},
+                .load_direct, .load_got, .load_actual_got, .load_tlv => {},
                 else => {
                     const temp_mcv = try self.allocTempRegOrMem(ty, false);
                     try self.genCopy(ty, temp_mcv, input_mcv);
@@ -11146,6 +11194,7 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError
         .register_overflow,
         .lea_direct,
         .lea_got,
+        .lea_actual_got,
         .lea_tlv,
         .lea_frame,
         .reserved_frame,
@@ -11197,11 +11246,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_tlv => {
+        .memory, .load_direct, .load_got, .load_actual_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_tlv => {},
+                .load_direct, .load_got, .load_actual_got, .load_tlv => {},
                 else => unreachable,
             }
 
@@ -11363,7 +11412,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
                 else => unreachable,
             },
         )),
-        .memory, .load_direct, .load_got, .load_tlv => {
+        .memory, .load_direct, .load_got, .load_actual_got, .load_tlv => {
             switch (src_mcv) {
                 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr|
                     return (try self.moveStrategy(
@@ -11391,7 +11440,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
                     },
                     .Float, .Vector => {},
                 },
-                .load_got, .load_tlv => {},
+                .load_got, .load_actual_got, .load_tlv => {},
                 else => unreachable,
             }
 
@@ -11405,17 +11454,18 @@ 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 => |sym_index| {
+        .lea_direct, .lea_got, .lea_actual_got => |sym_index| {
             const atom_index = try self.owner.getSymbolIndex(self);
             _ = try self.addInst(.{
                 .tag = switch (src_mcv) {
                     .lea_direct => .lea,
-                    .lea_got => .mov,
+                    .lea_got, .lea_actual_got => .mov,
                     else => unreachable,
                 },
                 .ops = switch (src_mcv) {
                     .lea_direct => .direct_reloc,
                     .lea_got => .got_reloc,
+                    .lea_actual_got => .actual_got_reloc,
                     else => unreachable,
                 },
                 .data = .{ .rx = .{
@@ -11551,6 +11601,8 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal
         .lea_direct,
         .load_got,
         .lea_got,
+        .load_actual_got,
+        .lea_actual_got,
         .load_tlv,
         .lea_tlv,
         .load_frame,
@@ -13556,6 +13608,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
             .memory => |addr| .{ .memory = addr },
             .load_direct => |sym_index| .{ .load_direct = sym_index },
             .load_got => |sym_index| .{ .lea_got = sym_index },
+            .load_actual_got => |sym_index| .{ .lea_actual_got = sym_index },
             .load_tlv => |sym_index| .{ .lea_tlv = sym_index },
         },
         .fail => |msg| {
src/arch/x86_64/Emit.zig
@@ -79,6 +79,7 @@ pub fn emitMir(emit: *Emit) Error!void {
                     @tagName(emit.bin_file.tag),
                 }),
                 .linker_got,
+                .linker_actual_got,
                 .linker_direct,
                 .linker_direct_got,
                 .linker_import,
@@ -87,12 +88,13 @@ pub fn emitMir(emit: *Emit) Error!void {
                     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_actual_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_direct => -4,
+                        .linker_got, .linker_actual_got, .linker_direct => -4,
                         else => unreachable,
                     };
                     const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
src/arch/x86_64/Lower.zig
@@ -51,6 +51,7 @@ pub const Reloc = struct {
         inst: Mir.Inst.Index,
         linker_extern_fn: Mir.Reloc,
         linker_got: Mir.Reloc,
+        linker_actual_got: Mir.Reloc,
         linker_direct: Mir.Reloc,
         linker_direct_got: Mir.Reloc,
         linker_import: Mir.Reloc,
@@ -388,7 +389,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
         .rrmi_sib, .rrmi_rip => inst.data.rrix.fixes,
         .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, .got_reloc, .direct_reloc, .direct_got_reloc, .import_reloc, .tlv_reloc => ._,
+        .extern_fn_reloc, .got_reloc, .actual_got_reloc, .direct_reloc, .direct_got_reloc, .import_reloc, .tlv_reloc => ._,
         else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}),
     };
     try lower.emit(switch (fixes) {
@@ -532,11 +533,12 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
                 else => unreachable,
             }
         },
-        .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ops: {
+        .got_reloc, .actual_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 },
+                .actual_got_reloc => .{ .linker_actual_got = extra },
                 .direct_reloc => .{ .linker_direct = extra },
                 .import_reloc => .{ .linker_import = extra },
                 .tlv_reloc => .{ .linker_tlv = extra },
src/arch/x86_64/Mir.zig
@@ -783,6 +783,8 @@ pub const Inst = struct {
         /// Linker relocation - GOT indirection.
         /// Uses `rx` payload with extra data of type `Reloc`.
         got_reloc,
+        /// TODO
+        actual_got_reloc,
         /// Linker relocation - direct reference.
         /// Uses `rx` payload with extra data of type `Reloc`.
         direct_reloc,
src/link/Dwarf.zig
@@ -1389,6 +1389,7 @@ pub fn commitDeclState(
                     .prev_vaddr = 0,
                 });
             },
+            .elf => {}, // TODO
             else => unreachable,
         }
     }
src/link/Elf.zig
@@ -3196,12 +3196,16 @@ pub fn updateDecl(
     const decl = mod.declPtr(decl_index);
 
     if (decl.val.getExternFunc(mod)) |_| {
-        return; // TODO Should we do more when front-end analyzed extern decl?
+        return;
     }
-    if (decl.val.getVariable(mod)) |variable| {
-        if (variable.is_extern) {
-            return; // TODO Should we do more when front-end analyzed extern decl?
-        }
+
+    if (decl.isExtern(mod)) {
+        // Extern variable gets a .got entry only.
+        const variable = decl.getOwnedVariable(mod).?;
+        const name = mod.intern_pool.stringToSlice(decl.name);
+        const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name);
+        _ = try self.getGlobalSymbol(name, lib_name);
+        return;
     }
 
     const sym_index = try self.getOrCreateMetadataForDecl(decl_index);
src/codegen.zig
@@ -798,6 +798,7 @@ fn lowerDeclRef(
 pub const LinkerLoad = struct {
     type: enum {
         got,
+        actual_got,
         direct,
         import,
     },
@@ -827,6 +828,8 @@ pub const GenResult = union(enum) {
         load_got: u32,
         /// Direct by-address reference to memory location.
         memory: u64,
+        /// TODO LOL Jakub, the king of naming...
+        load_actual_got: u32,
     };
 
     fn mcv(val: MCValue) GenResult {
@@ -885,8 +888,15 @@ fn genDeclRef(
     try mod.markDeclAlive(decl);
 
     const is_threadlocal = tv.val.isPtrToThreadLocal(mod) and !bin_file.options.single_threaded;
+    const is_extern = decl.isExtern(mod);
 
     if (bin_file.cast(link.File.Elf)) |elf_file| {
+        if (is_extern) {
+            const variable = decl.getOwnedVariable(mod).?;
+            const name = mod.intern_pool.stringToSlice(decl.name);
+            const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name);
+            return GenResult.mcv(.{ .load_actual_got = try elf_file.getGlobalSymbol(name, lib_name) });
+        }
         const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index);
         const sym = elf_file.symbol(sym_index);
         _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);