diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8092d94f79..2886ba3bda 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -8223,11 +8223,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const sym = elf_file.symbol(sym_index); sym.flags.needs_got = true; _ = try sym.getOrCreateGotEntry(sym_index, elf_file); - const got_addr = sym.gotAddress(elf_file); - try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ - .base = .{ .reg = .ds }, - .disp = @intCast(got_addr), - })); + _ = try self.addInst(.{ + .tag = .call, + .ops = .direct_got_reloc, + .data = .{ .reloc = .{ + .atom_index = try self.owner.getSymbolIndex(self), + .sym_index = sym.esym_index, + } }, + }); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const atom = try coff_file.getOrCreateAtomForDecl(owner_decl); const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; @@ -10290,12 +10293,24 @@ fn genLazySymbolRef( const sym = elf_file.symbol(sym_index); sym.flags.needs_got = true; _ = try sym.getOrCreateGotEntry(sym_index, elf_file); - const got_addr = sym.gotAddress(elf_file); - const got_mem = - Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr) }); + const reloc = Mir.Reloc{ + .atom_index = try self.owner.getSymbolIndex(self), + .sym_index = sym.esym_index, + }; switch (tag) { - .lea, .mov => try self.asmRegisterMemory(.{ ._, .mov }, reg.to64(), got_mem), - .call => try self.asmMemory(.{ ._, .call }, got_mem), + .lea, .mov => _ = try self.addInst(.{ + .tag = .mov, + .ops = .direct_got_reloc, + .data = .{ .rx = .{ + .r1 = reg.to64(), + .payload = try self.addExtra(reloc), + } }, + }), + .call => _ = try self.addInst(.{ + .tag = .call, + .ops = .direct_got_reloc, + .data = .{ .reloc = reloc }, + }), else => unreachable, } switch (tag) { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index ab1d63e64c..f17eda57eb 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -80,9 +80,21 @@ pub fn emitMir(emit: *Emit) Error!void { }), .linker_got, .linker_direct, + .linker_direct_got, .linker_import, .linker_tlv, - => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| { + => |symbol| if (emit.bin_file.cast(link.File.Elf)) |elf_file| { + const r_type: u32 = switch (lowered_relocs[0].target) { + .linker_direct_got => std.elf.R_X86_64_GOT32, + 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 = 0, + }); + } 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, .{ .type = switch (lowered_relocs[0].target) { diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index 53aa182957..b557520673 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -52,6 +52,7 @@ pub const Reloc = struct { linker_extern_fn: Mir.Reloc, linker_got: Mir.Reloc, linker_direct: Mir.Reloc, + linker_direct_got: Mir.Reloc, linker_import: Mir.Reloc, linker_tlv: Mir.Reloc, }; @@ -387,7 +388,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, .import_reloc, .tlv_reloc => ._, + .extern_fn_reloc, .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) { @@ -511,6 +512,26 @@ 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, + } + }, .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; diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 7753104b96..fc7744e99d 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -783,6 +783,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, // Pseudo instructions: diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 5b91dddff4..14c10bb244 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -518,6 +518,9 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { elf.R_X86_64_PC32, => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))), + elf.R_X86_64_GOT32 => try cwriter.writeIntLittle(u32, @as(u32, @intCast(G + GOT + A))), + elf.R_X86_64_GOT64 => try cwriter.writeIntLittle(u64, @as(u64, @intCast(G + GOT + A))), + elf.R_X86_64_GOTPCREL => try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P))), elf.R_X86_64_GOTPC32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(GOT + A - P))), elf.R_X86_64_GOTPC64 => try cwriter.writeIntLittle(i64, GOT + A - P),