From 845c906e6a2ed9206840bd189d85bf9525687102 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 21 Jul 2021 17:58:05 +0200 Subject: [PATCH] macho: add relocations for GOT cells in self-hosted compiler. --- src/codegen.zig | 51 +++++++++++++++++++++++----------------- src/link/MachO.zig | 58 ++++++---------------------------------------- 2 files changed, 37 insertions(+), 72 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 7cb7119f0d..5d57cf0728 100644 --- a/src/codegen.zig +++ b/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, [] // 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", .{}); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 6fc5989a1d..52329e47d1 100644 --- a/src/link/MachO.zig +++ b/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);