Commit 845c906e6a

Jakub Konka <kubkon@jakubkonka.com>
2021-07-21 17:58:05
macho: add relocations for GOT cells
in self-hosted compiler.
1 parent 3bfde76
Changed files (2)
src/link/MachO.zig
@@ -203,14 +203,11 @@ blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
 /// TODO consolidate this.
 decls: std.ArrayListUnmanaged(*Module.Decl) = .{},
 
-/// A list of all PIE fixups required for this run of the linker.
-/// Warning, this is currently NOT thread-safe. See the TODO below.
-/// TODO Move this list inside `updateDecl` where it should be allocated
-/// prior to calling `generateSymbol`, and then immediately deallocated
-/// rather than sitting in the global scope.
-/// TODO We should also rewrite this using generic relocations common to all
-/// backends.
-pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{},
+/// Currently active Module.Decl.
+/// TODO this might not be necessary if we figure out how to pass Module.Decl instance
+/// to codegen.genSetReg() or alterntively move PIE displacement for MCValue{ .memory = x }
+/// somewhere else in the codegen.
+active_decl: ?*Module.Decl = null,
 
 const StringIndexContext = struct {
     strtab: *std.ArrayListUnmanaged(u8),
@@ -3279,7 +3276,6 @@ pub fn deinit(self: *MachO) void {
     }
 
     self.pending_updates.deinit(self.base.allocator);
-    self.pie_fixups.deinit(self.base.allocator);
     self.got_entries.deinit(self.base.allocator);
     self.got_entries_map.deinit(self.base.allocator);
     self.got_entries_free_list.deinit(self.base.allocator);
@@ -3497,6 +3493,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
         }
     }
 
+    self.active_decl = decl;
+
     const res = if (debug_buffers) |*dbg|
         try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
             .ty = decl.ty,
@@ -3522,8 +3520,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
                 break :blk decl.link.macho.code;
             },
             .fail => |em| {
-                // Clear any PIE fixups for this decl.
-                self.pie_fixups.shrinkRetainingCapacity(0);
                 decl.analysis = .codegen_failure;
                 try module.failed_decls.put(module.gpa, decl, em);
                 return;
@@ -3600,46 +3596,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
         try self.writeGotEntry(got_index);
     }
 
-    // Calculate displacements to target addr (if any).
-    while (self.pie_fixups.popOrNull()) |fixup| {
-        assert(fixup.size == 4);
-        const this_addr = symbol.n_value + fixup.offset;
-        const target_addr = fixup.target_addr;
-
-        switch (self.base.options.target.cpu.arch) {
-            .x86_64 => {
-                const displacement = try math.cast(u32, target_addr - this_addr - 4);
-                mem.writeIntLittle(u32, decl.link.macho.code[fixup.offset..][0..4], displacement);
-            },
-            .aarch64 => {
-                // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible).
-                {
-                    const inst = decl.link.macho.code[fixup.offset..][0..4];
-                    var parsed = mem.bytesAsValue(meta.TagPayload(
-                        aarch64.Instruction,
-                        aarch64.Instruction.pc_relative_address,
-                    ), inst);
-                    const this_page = @intCast(i32, this_addr >> 12);
-                    const target_page = @intCast(i32, target_addr >> 12);
-                    const pages = @bitCast(u21, @intCast(i21, target_page - this_page));
-                    parsed.immhi = @truncate(u19, pages >> 2);
-                    parsed.immlo = @truncate(u2, pages);
-                }
-                {
-                    const inst = decl.link.macho.code[fixup.offset + 4 ..][0..4];
-                    var parsed = mem.bytesAsValue(meta.TagPayload(
-                        aarch64.Instruction,
-                        aarch64.Instruction.load_store_register,
-                    ), inst);
-                    const narrowed = @truncate(u12, target_addr);
-                    const offset = try math.divExact(u12, narrowed, 8);
-                    parsed.offset = offset;
-                }
-            },
-            else => unreachable, // unsupported target architecture
-        }
-    }
-
     // Resolve relocations
     try decl.link.macho.resolveRelocs(self);
 
src/codegen.zig
@@ -2506,7 +2506,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                             }) orelse unreachable;
                             break :blk got.addr + got_index * @sizeOf(u64);
                         };
-                        log.debug("got_addr = 0x{x}", .{got_addr});
                         switch (arch) {
                             .x86_64 => {
                                 try self.genSetReg(inst.base.src, Type.initTag(.u64), .rax, .{ .memory = got_addr });
@@ -3864,19 +3863,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                     .memory => |addr| {
                         if (self.bin_file.options.pie) {
                             // PC-relative displacement to the entry in the GOT table.
-                            // TODO we should come up with our own, backend independent relocation types
-                            // which each backend (Elf, MachO, etc.) would then translate into an actual
-                            // fixup when linking.
-                            // adrp reg, pages
-                            if (self.bin_file.cast(link.File.MachO)) |macho_file| {
-                                try macho_file.pie_fixups.append(self.bin_file.allocator, .{
-                                    .target_addr = addr,
-                                    .offset = self.code.items.len,
-                                    .size = 4,
-                                });
-                            } else {
-                                return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
-                            }
+                            // adrp
+                            const offset = @intCast(u32, self.code.items.len);
                             mem.writeIntLittle(
                                 u32,
                                 try self.code.addManyAsArray(4),
@@ -3889,6 +3877,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                                     .offset = Instruction.LoadStoreOffset.imm(0),
                                 },
                             }).toU32());
+
+                            if (self.bin_file.cast(link.File.MachO)) |macho_file| {
+                                const decl = macho_file.active_decl.?;
+                                // Page reloc for adrp instruction.
+                                try decl.link.macho.relocs.append(self.bin_file.allocator, .{
+                                    .offset = offset,
+                                    .where = .local,
+                                    .where_index = decl.link.macho.local_sym_index,
+                                    .payload = .{ .page = .{ .kind = .got } },
+                                });
+                                // Pageoff reloc for adrp instruction.
+                                try decl.link.macho.relocs.append(self.bin_file.allocator, .{
+                                    .offset = offset + 4,
+                                    .where = .local,
+                                    .where_index = decl.link.macho.local_sym_index,
+                                    .payload = .{ .page_off = .{ .kind = .got } },
+                                });
+                            } else {
+                                return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
+                            }
                         } else {
                             // The value is in memory at a hard-coded address.
                             // If the type is a pointer, it means the pointer address is at this memory location.
@@ -4128,6 +4136,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                             const abi_size = ty.abiSize(self.target.*);
                             const encoder = try X8664Encoder.init(self.code, 10);
 
+                            const offset = @intCast(u32, self.code.items.len);
                             // LEA reg, [<offset>]
 
                             // We encode the instruction FIRST because prefixes may or may not appear.
@@ -4141,14 +4150,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                             encoder.modRm_RIPDisp32(reg.low_id());
                             encoder.disp32(0);
 
-                            // TODO we should come up with our own, backend independent relocation types
-                            // which each backend (Elf, MachO, etc.) would then translate into an actual
-                            // fixup when linking.
                             if (self.bin_file.cast(link.File.MachO)) |macho_file| {
-                                try macho_file.pie_fixups.append(self.bin_file.allocator, .{
-                                    .target_addr = x,
-                                    .offset = self.code.items.len - 4,
-                                    .size = 4,
+                                const decl = macho_file.active_decl.?;
+                                // Load reloc for LEA instruction.
+                                try decl.link.macho.relocs.append(self.bin_file.allocator, .{
+                                    .offset = offset,
+                                    .where = .local,
+                                    .where_index = decl.link.macho.local_sym_index,
+                                    .payload = .{ .load = .{ .kind = .got } },
                                 });
                             } else {
                                 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});