diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2020-11-23 13:49:43 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2020-11-26 11:50:09 +0100 |
| commit | 80b1041c21599f5d444373dd35eafe2dc68e3887 (patch) | |
| tree | a0f28058948aee386476be3da7e02183b78208dc /src | |
| parent | 59fe3d447d8cdd5dda34d7a77459c2c6761857ea (diff) | |
| download | zig-80b1041c21599f5d444373dd35eafe2dc68e3887.tar.gz zig-80b1041c21599f5d444373dd35eafe2dc68e3887.zip | |
stage2 macho: use RIP-relative for memory-set regs x86_64
Diffstat (limited to 'src')
| -rw-r--r-- | src/codegen.zig | 27 | ||||
| -rw-r--r-- | src/link/MachO.zig | 34 |
2 files changed, 57 insertions, 4 deletions
diff --git a/src/codegen.zig b/src/codegen.zig index 48fda6f0d5..6cef7d8d0a 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1683,11 +1683,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64); switch (arch) { .x86_64 => { - // Here, we store the got address in %rax, and then call %rax - // movabsq [addr], %rax try self.genSetReg(inst.base.src, .rax, .{ .memory = got_addr }); // callq *%rax - try self.code.ensureCapacity(self.code.items.len + 2); self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 }); }, .aarch64 => { @@ -2766,7 +2763,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R }); }, .memory => |x| { - if (x <= math.maxInt(u32)) { + if (self.bin_file.cast(link.File.MachO)) |macho_file| { + // For MachO, the binary, with the exception of object files, has to be a PIE. + // Therefore, we cannot load an absolute address. + assert(x > math.maxInt(u32)); // 32bit direct addressing is not supported by MachO. + // The plan here is to use RIP-relative addressing, but leaving the actual displacement + // information empty (0-padded) and fixing it up later in the linker. + try self.mod_fn.owner_decl.link.macho.addRipPosition(self.bin_file.allocator, .{ + .address = x, + .start = self.code.items.len, + .len = 7, + }); + try self.code.ensureCapacity(self.code.items.len + 9); + // leaq %r, [rip + disp] + self.code.appendSliceAssumeCapacity(&[_]u8{ + 0x48, + 0x8d, + 0x05 | (@as(u8, reg.id() & 0b111) << 3), // R + 0x0, + 0x0, + 0x0, + 0x0, + }); + } else if (x <= math.maxInt(u32)) { // Moving from memory to a register is a variant of `8B /r`. // Since we're using 64-bit moves, we require a REX. // This variant also requires a SIB, as it would otherwise be RIP-relative. diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 95b165f27c..dc6acfca48 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -214,10 +214,21 @@ pub const TextBlock = struct { /// Unlike in Elf, we need to store the size of this symbol as part of /// the TextBlock since macho.nlist_64 lacks this information. size: u64, + /// List of RIP-relative positions in the code + /// This is a table of all RIP-relative positions that will need fixups + /// after codegen when linker assigns addresses to GOT entries. + /// TODO handle freeing, shrinking and re-allocs + rip_positions: std.ArrayListUnmanaged(RipPosition) = .{}, /// Points to the previous and next neighbours prev: ?*TextBlock, next: ?*TextBlock, + pub const RipPosition = struct { + address: u64, + start: usize, + len: usize, + }; + pub const empty = TextBlock{ .local_sym_index = 0, .offset_table_index = undefined, @@ -226,6 +237,15 @@ pub const TextBlock = struct { .next = null, }; + pub fn addRipPosition(self: *TextBlock, alloc: *Allocator, rip: RipPosition) !void { + std.debug.print("text_block={}, rip={}\n", .{ self.local_sym_index, rip }); + return self.rip_positions.append(alloc, rip); + } + + fn deinit(self: *TextBlock, alloc: *Allocator) void { + self.rip_positions.deinit(alloc); + } + /// Returns how much room there is to grow in virtual address space. /// File offset relocation happens transparently, so it is not included in /// this calculation. @@ -993,6 +1013,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); } + // Perform RIP-relative fixups (if any) + const got_section = self.sections.items[self.got_section_index.?]; + for (decl.link.macho.rip_positions.items) |rip| { + std.debug.print("rip={}\n", .{rip}); + const target_addr = rip.address; + // const got_addr = got_section.addr + decl.link.macho.offset_table_index * @sizeOf(u64); + const this_addr = symbol.n_value + rip.start; + std.debug.print("target_addr=0x{x},this_addr=0x{x}\n", .{target_addr, this_addr}); + const displacement = @intCast(u32, target_addr - this_addr + rip.len); + std.debug.print("displacement=0x{x}\n", .{displacement}); + var placeholder = code_buffer.items[rip.start + rip.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; + mem.writeIntSliceLittle(u32, placeholder, displacement); + } + const text_section = self.sections.items[self.text_section_index.?]; const section_offset = symbol.n_value - text_section.addr; const file_offset = text_section.offset + section_offset; |
