Commit e8eb9778cc

Jakub Konka <kubkon@jakubkonka.com>
2022-03-01 22:03:18
codegen: lower field_ptr to memory across linking backends
This requires generating an addend for the target relocation as the field pointer might point at a field inner to the container.
1 parent 543bee0
Changed files (8)
src/arch/x86_64/CodeGen.zig
@@ -2565,7 +2565,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
 
                     const payload = try self.addExtra(Mir.ImmPair{
                         .dest_off = 0,
-                        .operand = @intCast(u32, imm),
+                        // TODO check if this logic is correct
+                        .operand = @truncate(u32, imm),
                     });
                     const flags: u2 = switch (abi_size) {
                         1 => 0b00,
src/link/Coff.zig
@@ -724,10 +724,12 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
-    const res = try codegen.generateSymbol(&self.base, 0, decl.srcLoc(), .{
+    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
         .ty = decl.ty,
         .val = decl.val,
-    }, &code_buffer, .none);
+    }, &code_buffer, .none, .{
+        .parent_atom_index = 0,
+    });
     const code = switch (res) {
         .externally_managed => |x| x,
         .appended => code_buffer.items,
@@ -1463,9 +1465,8 @@ fn findLib(self: *Coff, arena: Allocator, name: []const u8) !?[]const u8 {
     return null;
 }
 
-pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 {
-    _ = parent_atom_index;
-    _ = offset;
+pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl, reloc_info: link.File.RelocInfo) !u64 {
+    _ = reloc_info;
     assert(self.llvm_object == null);
     return self.text_section_virtual_address + decl.link.coff.text_offset;
 }
src/link/Elf.zig
@@ -188,6 +188,7 @@ relocs: RelocTable = .{},
 const Reloc = struct {
     target: u32,
     offset: u64,
+    addend: u32,
     prev_vaddr: u64,
 };
 
@@ -421,20 +422,21 @@ pub fn deinit(self: *Elf) void {
     self.atom_by_index_table.deinit(self.base.allocator);
 }
 
-pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 {
+pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.RelocInfo) !u64 {
     assert(self.llvm_object == null);
     assert(decl.link.elf.local_sym_index != 0);
 
     const target = decl.link.elf.local_sym_index;
     const vaddr = self.local_symbols.items[target].st_value;
-    const atom = self.atom_by_index_table.get(parent_atom_index).?;
+    const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;
     const gop = try self.relocs.getOrPut(self.base.allocator, atom);
     if (!gop.found_existing) {
         gop.value_ptr.* = .{};
     }
     try gop.value_ptr.append(self.base.allocator, .{
         .target = target,
-        .offset = offset,
+        .offset = reloc_info.offset,
+        .addend = reloc_info.addend,
         .prev_vaddr = vaddr,
     });
 
@@ -1039,7 +1041,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
 
             for (relocs.items) |*reloc| {
                 const target_sym = self.local_symbols.items[reloc.target];
-                const target_vaddr = target_sym.st_value;
+                const target_vaddr = target_sym.st_value + reloc.addend;
 
                 if (target_vaddr == reloc.prev_vaddr) continue;
 
@@ -3074,7 +3076,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
 
     // TODO implement .debug_info for global variables
     const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
-    const res = try codegen.generateSymbol(&self.base, decl.link.elf.local_sym_index, decl.srcLoc(), .{
+    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
         .ty = decl.ty,
         .val = decl_val,
     }, &code_buffer, .{
@@ -3083,6 +3085,8 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
             .dbg_info = &dbg_info_buffer,
             .dbg_info_type_relocs = &dbg_info_type_relocs,
         },
+    }, .{
+        .parent_atom_index = decl.link.elf.local_sym_index,
     });
     const code = switch (res) {
         .externally_managed => |x| x,
@@ -3130,8 +3134,10 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl
     atom.local_sym_index = try self.allocateLocalSymbol();
     try self.atom_by_index_table.putNoClobber(self.base.allocator, atom.local_sym_index, atom);
 
-    const res = try codegen.generateSymbol(&self.base, atom.local_sym_index, decl.srcLoc(), typed_value, &code_buffer, .{
+    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{
         .none = .{},
+    }, .{
+        .parent_atom_index = atom.local_sym_index,
     });
     const code = switch (res) {
         .externally_managed => |x| x,
src/link/MachO.zig
@@ -3781,8 +3781,10 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De
     const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment));
     try self.atom_by_index_table.putNoClobber(self.base.allocator, local_sym_index, atom);
 
-    const res = try codegen.generateSymbol(&self.base, local_sym_index, decl.srcLoc(), typed_value, &code_buffer, .{
+    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{
         .none = .{},
+    }, .{
+        .parent_atom_index = local_sym_index,
     });
     const code = switch (res) {
         .externally_managed => |x| x,
@@ -3790,6 +3792,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De
         .fail => |em| {
             decl.analysis = .codegen_failure;
             try module.failed_decls.put(module.gpa, decl, em);
+            log.err("{s}", .{em.msg});
             return error.AnalysisFail;
         },
     };
@@ -3860,7 +3863,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
 
     const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
     const res = if (debug_buffers) |dbg|
-        try codegen.generateSymbol(&self.base, decl.link.macho.local_sym_index, decl.srcLoc(), .{
+        try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
             .ty = decl.ty,
             .val = decl_val,
         }, &code_buffer, .{
@@ -3869,12 +3872,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
                 .dbg_info = &dbg.dbg_info_buffer,
                 .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
             },
+        }, .{
+            .parent_atom_index = decl.link.macho.local_sym_index,
         })
     else
-        try codegen.generateSymbol(&self.base, decl.link.macho.local_sym_index, decl.srcLoc(), .{
+        try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
             .ty = decl.ty,
             .val = decl_val,
-        }, &code_buffer, .none);
+        }, &code_buffer, .none, .{
+            .parent_atom_index = decl.link.macho.local_sym_index,
+        });
 
     const code = blk: {
         switch (res) {
@@ -4357,15 +4364,15 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
     }
 }
 
-pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 {
+pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl, reloc_info: File.RelocInfo) !u64 {
     assert(self.llvm_object == null);
     assert(decl.link.macho.local_sym_index != 0);
 
-    const atom = self.atom_by_index_table.get(parent_atom_index).?;
+    const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;
     try atom.relocs.append(self.base.allocator, .{
-        .offset = @intCast(u32, offset),
+        .offset = @intCast(u32, reloc_info.offset),
         .target = .{ .local = decl.link.macho.local_sym_index },
-        .addend = 0,
+        .addend = reloc_info.addend,
         .subtractor = null,
         .pcrel = false,
         .length = 3,
@@ -4375,7 +4382,7 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl, parent_atom_index: u
             else => unreachable,
         },
     });
-    try atom.rebases.append(self.base.allocator, offset);
+    try atom.rebases.append(self.base.allocator, reloc_info.offset);
 
     return 0;
 }
src/link/Plan9.zig
@@ -304,10 +304,12 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
     const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
     // TODO we need the symbol index for symbol in the table of locals for the containing atom
     const sym_index = decl.link.plan9.sym_index orelse 0;
-    const res = try codegen.generateSymbol(&self.base, @intCast(u32, sym_index), decl.srcLoc(), .{
+    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
         .ty = decl.ty,
         .val = decl_val,
-    }, &code_buffer, .{ .none = .{} });
+    }, &code_buffer, .{ .none = .{} }, .{
+        .parent_atom_index = @intCast(u32, sym_index),
+    });
     const code = switch (res) {
         .externally_managed => |x| x,
         .appended => code_buffer.items,
@@ -752,9 +754,8 @@ pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {
     _ = self;
     _ = decl;
 }
-pub fn getDeclVAddr(self: *Plan9, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 {
-    _ = parent_atom_index;
-    _ = offset;
+pub fn getDeclVAddr(self: *Plan9, decl: *const Module.Decl, reloc_info: link.File.RelocInfo) !u64 {
+    _ = reloc_info;
     if (decl.ty.zigTypeTag() == .Fn) {
         var start = self.bases.text;
         var it_file = self.fn_decl_table.iterator();
src/codegen.zig
@@ -142,11 +142,11 @@ pub fn generateFunction(
 
 pub fn generateSymbol(
     bin_file: *link.File,
-    parent_atom_index: u32,
     src_loc: Module.SrcLoc,
     typed_value: TypedValue,
     code: *std.ArrayList(u8),
     debug_output: DebugInfoOutput,
+    reloc_info: RelocInfo,
 ) GenerateSymbolError!Result {
     const tracy = trace(@src());
     defer tracy.end();
@@ -178,10 +178,10 @@ pub fn generateSymbol(
                 if (typed_value.ty.sentinel()) |sentinel| {
                     try code.ensureUnusedCapacity(payload.data.len + 1);
                     code.appendSliceAssumeCapacity(payload.data);
-                    switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                    switch (try generateSymbol(bin_file, src_loc, .{
                         .ty = typed_value.ty.elemType(),
                         .val = sentinel,
-                    }, code, debug_output)) {
+                    }, code, debug_output, reloc_info)) {
                         .appended => return Result{ .appended = {} },
                         .externally_managed => |slice| {
                             code.appendSliceAssumeCapacity(slice);
@@ -198,10 +198,10 @@ pub fn generateSymbol(
                 const elem_vals = typed_value.val.castTag(.array).?.data;
                 const elem_ty = typed_value.ty.elemType();
                 for (elem_vals) |elem_val| {
-                    switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                    switch (try generateSymbol(bin_file, src_loc, .{
                         .ty = elem_ty,
                         .val = elem_val,
-                    }, code, debug_output)) {
+                    }, code, debug_output, reloc_info)) {
                         .appended => {},
                         .externally_managed => |slice| {
                             code.appendSliceAssumeCapacity(slice);
@@ -219,10 +219,10 @@ pub fn generateSymbol(
 
                 var index: u64 = 0;
                 while (index < len) : (index += 1) {
-                    switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                    switch (try generateSymbol(bin_file, src_loc, .{
                         .ty = elem_ty,
                         .val = array,
-                    }, code, debug_output)) {
+                    }, code, debug_output, reloc_info)) {
                         .appended => {},
                         .externally_managed => |slice| {
                             code.appendSliceAssumeCapacity(slice);
@@ -232,10 +232,10 @@ pub fn generateSymbol(
                 }
 
                 if (sentinel) |sentinel_val| {
-                    switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                    switch (try generateSymbol(bin_file, src_loc, .{
                         .ty = elem_ty,
                         .val = sentinel_val,
-                    }, code, debug_output)) {
+                    }, code, debug_output, reloc_info)) {
                         .appended => {},
                         .externally_managed => |slice| {
                             code.appendSliceAssumeCapacity(slice);
@@ -249,10 +249,10 @@ pub fn generateSymbol(
             .empty_array_sentinel => {
                 const elem_ty = typed_value.ty.childType();
                 const sentinel_val = typed_value.ty.sentinel().?;
-                switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = elem_ty,
                     .val = sentinel_val,
-                }, code, debug_output)) {
+                }, code, debug_output, reloc_info)) {
                     .appended => {},
                     .externally_managed => |slice| {
                         code.appendSliceAssumeCapacity(slice);
@@ -273,11 +273,11 @@ pub fn generateSymbol(
         .Pointer => switch (typed_value.val.tag()) {
             .variable => {
                 const decl = typed_value.val.castTag(.variable).?.data.owner_decl;
-                return lowerDeclRef(bin_file, parent_atom_index, src_loc, typed_value, decl, code, debug_output);
+                return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output, reloc_info);
             },
             .decl_ref => {
                 const decl = typed_value.val.castTag(.decl_ref).?.data;
-                return lowerDeclRef(bin_file, parent_atom_index, src_loc, typed_value, decl, code, debug_output);
+                return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output, reloc_info);
             },
             .slice => {
                 const slice = typed_value.val.castTag(.slice).?.data;
@@ -285,10 +285,10 @@ pub fn generateSymbol(
                 // generate ptr
                 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
                 const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf);
-                switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = slice_ptr_field_type,
                     .val = slice.ptr,
-                }, code, debug_output)) {
+                }, code, debug_output, reloc_info)) {
                     .appended => {},
                     .externally_managed => |external_slice| {
                         code.appendSliceAssumeCapacity(external_slice);
@@ -297,10 +297,10 @@ pub fn generateSymbol(
                 }
 
                 // generate length
-                switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = Type.initTag(.usize),
                     .val = slice.len,
-                }, code, debug_output)) {
+                }, code, debug_output, reloc_info)) {
                     .appended => {},
                     .externally_managed => |external_slice| {
                         code.appendSliceAssumeCapacity(external_slice);
@@ -310,6 +310,45 @@ pub fn generateSymbol(
 
                 return Result{ .appended = {} };
             },
+            .field_ptr => {
+                const target = bin_file.options.target;
+                const field_ptr = typed_value.val.castTag(.field_ptr).?.data;
+                const container_ptr = field_ptr.container_ptr;
+
+                switch (container_ptr.tag()) {
+                    .decl_ref => {
+                        const decl = container_ptr.castTag(.decl_ref).?.data;
+                        const addend = blk: {
+                            switch (decl.ty.tag()) {
+                                .@"struct" => {
+                                    const addend = decl.ty.structFieldOffset(field_ptr.field_index, target);
+                                    break :blk @intCast(u32, addend);
+                                },
+                                else => return Result{
+                                    .fail = try ErrorMsg.create(
+                                        bin_file.allocator,
+                                        src_loc,
+                                        "TODO implement generateSymbol for pointer type value: '{s}'",
+                                        .{@tagName(typed_value.val.tag())},
+                                    ),
+                                },
+                            }
+                        };
+                        return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output, .{
+                            .parent_atom_index = reloc_info.parent_atom_index,
+                            .addend = (reloc_info.addend orelse 0) + addend,
+                        });
+                    },
+                    else => return Result{
+                        .fail = try ErrorMsg.create(
+                            bin_file.allocator,
+                            src_loc,
+                            "TODO implement generateSymbol for pointer type value: '{s}'",
+                            .{@tagName(typed_value.val.tag())},
+                        ),
+                    },
+                }
+            },
             else => return Result{
                 .fail = try ErrorMsg.create(
                     bin_file.allocator,
@@ -441,10 +480,10 @@ pub fn generateSymbol(
                 const field_ty = typed_value.ty.structFieldType(index);
                 if (!field_ty.hasRuntimeBits()) continue;
 
-                switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = field_ty,
                     .val = field_val,
-                }, code, debug_output)) {
+                }, code, debug_output, reloc_info)) {
                     .appended => {},
                     .externally_managed => |external_slice| {
                         code.appendSliceAssumeCapacity(external_slice);
@@ -472,10 +511,10 @@ pub fn generateSymbol(
             const layout = typed_value.ty.unionGetLayout(target);
 
             if (layout.payload_size == 0) {
-                switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = typed_value.ty.unionTagType().?,
                     .val = union_obj.tag,
-                }, code, debug_output)) {
+                }, code, debug_output, reloc_info)) {
                     .appended => {},
                     .externally_managed => |external_slice| {
                         code.appendSliceAssumeCapacity(external_slice);
@@ -486,10 +525,10 @@ pub fn generateSymbol(
 
             // Check if we should store the tag first.
             if (layout.tag_align >= layout.payload_align) {
-                switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = typed_value.ty.unionTagType().?,
                     .val = union_obj.tag,
-                }, code, debug_output)) {
+                }, code, debug_output, reloc_info)) {
                     .appended => {},
                     .externally_managed => |external_slice| {
                         code.appendSliceAssumeCapacity(external_slice);
@@ -505,10 +544,10 @@ pub fn generateSymbol(
             if (!field_ty.hasRuntimeBits()) {
                 try code.writer().writeByteNTimes(0xaa, try math.cast(usize, layout.payload_size));
             } else {
-                switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = field_ty,
                     .val = union_obj.val,
-                }, code, debug_output)) {
+                }, code, debug_output, reloc_info)) {
                     .appended => {},
                     .externally_managed => |external_slice| {
                         code.appendSliceAssumeCapacity(external_slice);
@@ -523,10 +562,10 @@ pub fn generateSymbol(
             }
 
             if (layout.tag_size > 0) {
-                switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = union_ty.tag_ty,
                     .val = union_obj.tag,
-                }, code, debug_output)) {
+                }, code, debug_output, reloc_info)) {
                     .appended => {},
                     .externally_managed => |external_slice| {
                         code.appendSliceAssumeCapacity(external_slice);
@@ -555,10 +594,10 @@ pub fn generateSymbol(
 
             const error_val = if (!is_payload) typed_value.val else Value.initTag(.zero);
             const begin = code.items.len;
-            switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+            switch (try generateSymbol(bin_file, src_loc, .{
                 .ty = error_ty,
                 .val = error_val,
-            }, code, debug_output)) {
+            }, code, debug_output, reloc_info)) {
                 .appended => {},
                 .externally_managed => |external_slice| {
                     code.appendSliceAssumeCapacity(external_slice);
@@ -568,10 +607,10 @@ pub fn generateSymbol(
 
             if (payload_ty.hasRuntimeBits()) {
                 const payload_val = if (typed_value.val.castTag(.eu_payload)) |val| val.data else Value.initTag(.undef);
-                switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+                switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = payload_ty,
                     .val = payload_val,
-                }, code, debug_output)) {
+                }, code, debug_output, reloc_info)) {
                     .appended => {},
                     .externally_managed => |external_slice| {
                         code.appendSliceAssumeCapacity(external_slice);
@@ -618,23 +657,28 @@ pub fn generateSymbol(
     }
 }
 
+const RelocInfo = struct {
+    parent_atom_index: u32,
+    addend: ?u32 = null,
+};
+
 fn lowerDeclRef(
     bin_file: *link.File,
-    parent_atom_index: u32,
     src_loc: Module.SrcLoc,
     typed_value: TypedValue,
     decl: *Module.Decl,
     code: *std.ArrayList(u8),
     debug_output: DebugInfoOutput,
+    reloc_info: RelocInfo,
 ) GenerateSymbolError!Result {
     if (typed_value.ty.isSlice()) {
         // generate ptr
         var buf: Type.SlicePtrFieldTypeBuffer = undefined;
         const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf);
-        switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+        switch (try generateSymbol(bin_file, src_loc, .{
             .ty = slice_ptr_field_type,
             .val = typed_value.val,
-        }, code, debug_output)) {
+        }, code, debug_output, reloc_info)) {
             .appended => {},
             .externally_managed => |external_slice| {
                 code.appendSliceAssumeCapacity(external_slice);
@@ -647,10 +691,10 @@ fn lowerDeclRef(
             .base = .{ .tag = .int_u64 },
             .data = typed_value.val.sliceLen(),
         };
-        switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+        switch (try generateSymbol(bin_file, src_loc, .{
             .ty = Type.usize,
             .val = Value.initPayload(&slice_len.base),
-        }, code, debug_output)) {
+        }, code, debug_output, reloc_info)) {
             .appended => {},
             .externally_managed => |external_slice| {
                 code.appendSliceAssumeCapacity(external_slice);
@@ -670,7 +714,11 @@ fn lowerDeclRef(
     }
 
     decl.markAlive();
-    const vaddr = try bin_file.getDeclVAddr(decl, parent_atom_index, code.items.len);
+    const vaddr = try bin_file.getDeclVAddr(decl, .{
+        .parent_atom_index = reloc_info.parent_atom_index,
+        .offset = code.items.len,
+        .addend = reloc_info.addend orelse 0,
+    });
     const endian = target.cpu.arch.endian();
     switch (ptr_width) {
         16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(u16, vaddr), endian),
src/link.zig
@@ -685,16 +685,22 @@ pub const File = struct {
         }
     }
 
+    pub const RelocInfo = struct {
+        parent_atom_index: u32,
+        offset: u64,
+        addend: u32,
+    };
+
     /// Get allocated `Decl`'s address in virtual memory.
     /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's
     /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the
     /// `Decl`'s address was not yet resolved, or the containing atom gets moved in virtual memory.
-    pub fn getDeclVAddr(base: *File, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 {
+    pub fn getDeclVAddr(base: *File, decl: *const Module.Decl, reloc_info: RelocInfo) !u64 {
         switch (base.tag) {
-            .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl, parent_atom_index, offset),
-            .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl, parent_atom_index, offset),
-            .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl, parent_atom_index, offset),
-            .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl, parent_atom_index, offset),
+            .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl, reloc_info),
+            .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl, reloc_info),
+            .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl, reloc_info),
+            .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl, reloc_info),
             .c => unreachable,
             .wasm => unreachable,
             .spirv => unreachable,
test/behavior/bugs/3046.zig
@@ -13,7 +13,6 @@ fn couldFail() anyerror!i32 {
 var some_struct: SomeStruct = undefined;
 
 test "fixed" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;