Commit 17635e4f2a

Jakub Konka <kubkon@jakubkonka.com>
2023-10-14 09:04:08
x86_64: add -fPIC support targeting ELF
1 parent b3d98a4
Changed files (5)
src/arch/x86_64/CodeGen.zig
@@ -9190,14 +9190,19 @@ fn genCall(self: *Self, info: union(enum) {
                     const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl);
                     const sym = elf_file.symbol(sym_index);
                     _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
-                    _ = try self.addInst(.{
-                        .tag = .call,
-                        .ops = .direct_got_reloc,
-                        .data = .{ .reloc = .{
-                            .atom_index = try self.owner.getSymbolIndex(self),
-                            .sym_index = sym.esym_index,
-                        } },
-                    });
+                    if (self.bin_file.options.pic) {
+                        try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym.esym_index });
+                        try self.asmRegister(.{ ._, .call }, .rax);
+                    } else {
+                        _ = 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().?;
@@ -11637,34 +11642,48 @@ fn genLazySymbolRef(
             return self.fail("{s} creating lazy symbol", .{@errorName(err)});
         const sym = elf_file.symbol(sym_index);
         _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
-        const reloc = Mir.Reloc{
-            .atom_index = try self.owner.getSymbolIndex(self),
-            .sym_index = sym.esym_index,
-        };
-        switch (tag) {
-            .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) {
-            .lea, .call => {},
-            .mov => try self.asmRegisterMemory(
-                .{ ._, tag },
-                reg.to64(),
-                Memory.sib(.qword, .{ .base = .{ .reg = reg.to64() } }),
-            ),
-            else => unreachable,
+
+        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 }),
+                else => unreachable,
+            }
+            switch (tag) {
+                .lea, .mov => {},
+                .call => try self.asmRegister(.{ ._, .call }, reg),
+                else => unreachable,
+            }
+        } else {
+            const reloc = Mir.Reloc{
+                .atom_index = try self.owner.getSymbolIndex(self),
+                .sym_index = sym.esym_index,
+            };
+            switch (tag) {
+                .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) {
+                .lea, .call => {},
+                .mov => try self.asmRegisterMemory(
+                    .{ ._, tag },
+                    reg.to64(),
+                    Memory.sib(.qword, .{ .base = .{ .reg = reg.to64() } }),
+                ),
+                else => unreachable,
+            }
         }
     } else if (self.bin_file.cast(link.File.Plan9)) |p9_file| {
         const atom_index = p9_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|
src/arch/x86_64/Emit.zig
@@ -85,14 +85,21 @@ pub fn emitMir(emit: *Emit) Error!void {
                 .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 => std.elf.R_X86_64_GOT32,
+                        .linker_direct_got => link.File.Elf.R_X86_64_ZIG_GOT32,
+                        .linker_got => link.File.Elf.R_X86_64_ZIG_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,
                         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,
+                        .r_addend = r_addend,
                     });
                 } else if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
                     const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
src/link/Elf/Atom.zig
@@ -370,16 +370,6 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
                 try self.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
             },
 
-            // TODO I have temporarily repurposed those for handling .zig.got indirection
-            // but we should probably claim unused custom values for incremental linking
-            // that get rewritten to standard relocs when lowering to a relocatable object
-            // file.
-            elf.R_X86_64_GOT32,
-            elf.R_X86_64_GOT64,
-            => {
-                assert(symbol.flags.has_zig_got);
-            },
-
             elf.R_X86_64_GOTPC32,
             elf.R_X86_64_GOTPC64,
             elf.R_X86_64_GOTPCREL,
@@ -461,6 +451,13 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
             elf.R_X86_64_TLSDESC_CALL,
             => {},
 
+            // Zig custom relocations
+            Elf.R_X86_64_ZIG_GOT32,
+            Elf.R_X86_64_ZIG_GOTPCREL,
+            => {
+                assert(symbol.flags.has_zig_got);
+            },
+
             else => try self.reportUnhandledRelocError(rel, elf_file),
         }
     }
@@ -813,13 +810,6 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
             elf.R_X86_64_32 => try cwriter.writeIntLittle(u32, @as(u32, @truncate(@as(u64, @intCast(S + A))))),
             elf.R_X86_64_32S => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A))),
 
-            // TODO I have temporarily repurposed those for handling .zig.got indirection
-            // but we should probably claim unused custom values for incremental linking
-            // that get rewritten to standard relocs when lowering to a relocatable object
-            // file.
-            elf.R_X86_64_GOT32 => try cwriter.writeIntLittle(u32, @as(u32, @intCast(ZIG_GOT + A))),
-            elf.R_X86_64_GOT64 => try cwriter.writeIntLittle(u64, @as(u64, @intCast(ZIG_GOT + A))),
-
             elf.R_X86_64_TPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A - TP))),
             elf.R_X86_64_TPOFF64 => try cwriter.writeIntLittle(i64, S + A - TP),
 
@@ -888,6 +878,10 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
                 }
             },
 
+            // 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))),
+
             else => {},
         }
     }
@@ -1136,6 +1130,9 @@ fn formatRelocType(
         elf.R_X86_64_GOTPCRELX => "R_X86_64_GOTPCRELX",
         elf.R_X86_64_REX_GOTPCRELX => "R_X86_64_REX_GOTPCRELX",
         elf.R_X86_64_NUM => "R_X86_64_NUM",
+        // Zig custom relocations
+        Elf.R_X86_64_ZIG_GOT32 => "R_X86_64_ZIG_GOT32",
+        Elf.R_X86_64_ZIG_GOTPCREL => "R_X86_64_ZIG_GOTPCREL",
         else => "R_X86_64_UNKNOWN",
     };
     try writer.print("{s}", .{str});
src/link/Elf.zig
@@ -6074,6 +6074,9 @@ const SystemLib = struct {
     path: []const u8,
 };
 
+pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1;
+pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2;
+
 const std = @import("std");
 const build_options = @import("build_options");
 const builtin = @import("builtin");
src/codegen.zig
@@ -890,7 +890,11 @@ fn genDeclRef(
         const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index);
         const sym = elf_file.symbol(sym_index);
         _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
-        return GenResult.mcv(.{ .memory = sym.zigGotAddress(elf_file) });
+        if (bin_file.options.pic) {
+            return GenResult.mcv(.{ .load_got = sym.esym_index });
+        } else {
+            return GenResult.mcv(.{ .memory = sym.zigGotAddress(elf_file) });
+        }
     } 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().?;
@@ -925,7 +929,12 @@ fn genUnnamedConst(
         return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)});
     };
     if (bin_file.cast(link.File.Elf)) |elf_file| {
-        return GenResult.mcv(.{ .memory = elf_file.symbol(local_sym_index).value });
+        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 });
+        }
     } else if (bin_file.cast(link.File.MachO)) |_| {
         return GenResult.mcv(.{ .load_direct = local_sym_index });
     } else if (bin_file.cast(link.File.Coff)) |_| {