From 80b1041c21599f5d444373dd35eafe2dc68e3887 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 23 Nov 2020 13:49:43 +0100 Subject: stage2 macho: use RIP-relative for memory-set regs x86_64 --- src/codegen.zig | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'src/codegen.zig') 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. -- cgit v1.2.3 From ef5132c508f89ed8392143f6e8d03e8a2f121ba9 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 24 Nov 2020 18:46:43 +0100 Subject: stage2 macho: first, rough draft at trampolining --- src/codegen.zig | 63 +++++++++++++++------- src/link/MachO.zig | 109 +++++++++++++++------------------------ src/link/MachO/CodeSignature.zig | 2 - 3 files changed, 88 insertions(+), 86 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 6cef7d8d0a..85b039069a 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2767,24 +2767,51 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // 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, - }); + // The plan here is to use unconditional relative jump to GOT entry, where we store + // pre-calculated and stored effective address to load into the target register. + // We leave the actual displacement information empty (0-padded) and fixing it up + // later in the linker. + if (reg.id() == 0) { // %rax is special-cased + try self.code.ensureCapacity(self.code.items.len + 5); + try self.mod_fn.owner_decl.link.macho.addRipPosition(self.bin_file.allocator, .{ + .address = x, + .start = self.code.items.len, + .len = 5, + }); + // call [label] + self.code.appendSliceAssumeCapacity(&[_]u8{ + 0xE8, + 0x0, + 0x0, + 0x0, + 0x0, + }); + } else { + try self.code.ensureCapacity(self.code.items.len + 10); + // push %rax + self.code.appendSliceAssumeCapacity(&[_]u8{0x50}); + try self.mod_fn.owner_decl.link.macho.addRipPosition(self.bin_file.allocator, .{ + .address = x, + .start = self.code.items.len, + .len = 5, + }); + // call [label] + self.code.appendSliceAssumeCapacity(&[_]u8{ + 0xE8, + 0x0, + 0x0, + 0x0, + 0x0, + }); + // mov %r, %rax + self.code.appendSliceAssumeCapacity(&[_]u8{ + 0x48, + 0x89, + 0xC0 | @as(u8, reg.id()), + }); + // pop %rax + self.code.appendSliceAssumeCapacity(&[_]u8{0x58}); + } } 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. diff --git a/src/link/MachO.zig b/src/link/MachO.zig index dc6acfca48..bb4813a9df 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1020,8 +1020,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { 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("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); @@ -1201,7 +1201,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { .addr = text_segment.vmaddr + off, .size = file_size, .offset = off, - .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 1, // 2^2 for aarch64, 2^1 for x86_64 + .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, // 2^2 for aarch64, 2^0 for x86_64 .reloff = 0, .nreloc = 0, .flags = flags, @@ -1214,48 +1214,23 @@ pub fn populateMissingMetadata(self: *MachO) !void { text_segment.filesize = file_size + off; self.cmd_table_dirty = true; } - if (self.data_segment_cmd_index == null) { - self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); - const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; - const initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE; - try self.load_commands.append(self.base.allocator, .{ - .Segment = .{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString("__DATA"), - .vmaddr = text_segment.vmaddr + text_segment.vmsize, - .vmsize = 0, - .fileoff = text_segment.fileoff + text_segment.filesize, - .filesize = 0, - .maxprot = maxprot, - .initprot = initprot, - .nsects = 0, - .flags = 0, - }, - }); - self.cmd_table_dirty = true; - } if (self.got_section_index == null) { + const text_section = &self.sections.items[self.text_section_index.?]; self.got_section_index = @intCast(u16, self.sections.items.len); - const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - data_segment.cmdsize += @sizeOf(macho.section_64); - data_segment.nsects += 1; const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; // TODO looking for free space should be done *within* a segment it belongs to - // const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); - const off = @intCast(u32, data_segment.fileoff); + const off = @intCast(u32, text_section.offset + text_section.size); log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); try self.sections.append(self.base.allocator, .{ - .sectname = makeStaticString("__got"), - .segname = makeStaticString("__DATA"), - .addr = data_segment.vmaddr, + .sectname = makeStaticString("__ziggot"), + .segname = makeStaticString("__TEXT"), + .addr = text_section.addr + text_section.size, .size = file_size, .offset = off, - .@"align" = 3, // 2^3 = 8 + .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, .reloff = 0, .nreloc = 0, .flags = macho.S_REGULAR, @@ -1264,23 +1239,26 @@ pub fn populateMissingMetadata(self: *MachO) !void { .reserved3 = 0, }); - const segment_size = mem.alignForwardGeneric(u64, file_size, self.page_size); - data_segment.vmsize = segment_size; - data_segment.filesize = segment_size; + const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size); + const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; + text_segment.vmsize += added_size; + text_segment.filesize += added_size; + text_segment.cmdsize += @sizeOf(macho.section_64); + text_segment.nsects += 1; self.cmd_table_dirty = true; } if (self.linkedit_segment_cmd_index == null) { self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); - const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; const initprot = macho.VM_PROT_READ; - const off = data_segment.fileoff + data_segment.filesize; + const off = text_segment.fileoff + text_segment.filesize; try self.load_commands.append(self.base.allocator, .{ .Segment = .{ .cmd = macho.LC_SEGMENT_64, .cmdsize = @sizeOf(macho.segment_command_64), .segname = makeStaticString("__LINKEDIT"), - .vmaddr = data_segment.vmaddr + data_segment.vmsize, + .vmaddr = text_segment.vmaddr + text_segment.vmsize, .vmsize = 0, .fileoff = off, .filesize = 0, @@ -1671,11 +1649,24 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 { fn writeOffsetTableEntry(self: *MachO, index: usize) !void { const sect = &self.sections.items[self.got_section_index.?]; const endian = self.base.options.target.cpu.arch.endian(); - var buf: [@sizeOf(u64)]u8 = undefined; - mem.writeInt(u64, &buf, self.offset_table.items[index], endian); + const off = sect.offset + @sizeOf(u64) * index; + const vmaddr = sect.addr + @sizeOf(u64) * index; + const pos_symbol_off = @truncate(u31, vmaddr - self.offset_table.items[index] + 7); + const symbol_off = @intCast(i32, pos_symbol_off) * -1; + std.debug.print("vmaddr=0x{x},item=0x{x}\n", .{vmaddr, self.offset_table.items[index]}); + std.debug.print("posSymbolOff=0x{x},symbolOff=0x{x}\n", .{pos_symbol_off, @bitCast(u32, symbol_off)}); + + var code: [8]u8 = undefined; + // lea %rax, [rip - disp] + code[0] = 0x48; + code[1] = 0x8D; + code[2] = 0x5; + mem.writeInt(u32, code[3..7], @bitCast(u32, symbol_off), endian); + // ret + code[7] = 0xC3; log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); - try self.base.file.?.pwriteAll(&buf, off); + try self.base.file.?.pwriteAll(&code, off); } fn writeSymbolTable(self: *MachO) !void { @@ -1791,7 +1782,7 @@ fn writeExportTrie(self: *MachO) !void { if (export_size > buffer.items.len) { // Pad out to align(8). - try self.base.file.?.pwriteAll(&[_]u8{ 0 }, dyld_info.export_off + export_size); + try self.base.file.?.pwriteAll(&[_]u8{0}, dyld_info.export_off + export_size); } try self.base.file.?.pwriteAll(buffer.items, dyld_info.export_off); @@ -1816,7 +1807,7 @@ fn writeStringTable(self: *MachO) !void { if (symtab.strsize > needed_size) { // Pad out to align(8); - try self.base.file.?.pwriteAll(&[_]u8{ 0 }, symtab.stroff + symtab.strsize); + try self.base.file.?.pwriteAll(&[_]u8{0}, symtab.stroff + symtab.strsize); } try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff); @@ -1843,7 +1834,6 @@ fn writeCmdHeaders(self: *MachO) !void { last_cmd_offset += cmd.cmdsize(); } { - // write __text section header const off = if (self.text_segment_cmd_index) |text_segment_index| blk: { var i: usize = 0; var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); @@ -1856,27 +1846,14 @@ fn writeCmdHeaders(self: *MachO) !void { // only one, noname segment to append this section header to. return error.TODOImplementWritingObjFiles; }; - const idx = self.text_section_index.?; + // write __text section header + const id1 = self.text_section_index.?; log.debug("writing text section header at 0x{x}\n", .{off}); - try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); - } - { - // write __got section header - const off = if (self.data_segment_cmd_index) |data_segment_index| blk: { - var i: usize = 0; - var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); - while (i < data_segment_index) : (i += 1) { - cmdsize += self.load_commands.items[i].cmdsize(); - } - break :blk cmdsize; - } else { - // If we've landed in here, we are building a MachO object file, so we have - // only one, noname segment to append this section header to. - return error.TODOImplementWritingObjFiles; - }; - const idx = self.got_section_index.?; - log.debug("writing got section header at 0x{x}\n", .{off}); - try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[id1 .. id1 + 1]), off); + // write __ziggot section header + const id2 = self.got_section_index.?; + log.debug("writing got section header at 0x{x}\n", .{off + @sizeOf(macho.section_64)}); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[id2 .. id2 + 1]), off + @sizeOf(macho.section_64)); } } diff --git a/src/link/MachO/CodeSignature.zig b/src/link/MachO/CodeSignature.zig index a7097d4cc0..6262315eb8 100644 --- a/src/link/MachO/CodeSignature.zig +++ b/src/link/MachO/CodeSignature.zig @@ -67,8 +67,6 @@ pub fn init(alloc: *Allocator) CodeSignature { pub fn calcAdhocSignature(self: *CodeSignature, bin_file: *const MachO) !void { const text_segment = bin_file.load_commands.items[bin_file.text_segment_cmd_index.?].Segment; - const data_segment = bin_file.load_commands.items[bin_file.data_segment_cmd_index.?].Segment; - const linkedit_segment = bin_file.load_commands.items[bin_file.linkedit_segment_cmd_index.?].Segment; const code_sig_cmd = bin_file.load_commands.items[bin_file.code_signature_cmd_index.?].LinkeditData; const execSegBase: u64 = text_segment.fileoff; -- cgit v1.2.3 From 2cd84b1b3f0a6e3f030da723ec51f0be33b7a1ed Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 24 Nov 2020 20:32:09 +0100 Subject: stage2 macho: refactor PIE generation on x86_64 --- src/codegen.zig | 4 ++-- src/link/MachO.zig | 70 +++++++++++++++++++++++++----------------------------- 2 files changed, 34 insertions(+), 40 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 85b039069a..f75ad079ae 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2773,7 +2773,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // later in the linker. if (reg.id() == 0) { // %rax is special-cased try self.code.ensureCapacity(self.code.items.len + 5); - try self.mod_fn.owner_decl.link.macho.addRipPosition(self.bin_file.allocator, .{ + try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{ .address = x, .start = self.code.items.len, .len = 5, @@ -2790,7 +2790,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try self.code.ensureCapacity(self.code.items.len + 10); // push %rax self.code.appendSliceAssumeCapacity(&[_]u8{0x50}); - try self.mod_fn.owner_decl.link.macho.addRipPosition(self.bin_file.allocator, .{ + try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{ .address = x, .start = self.code.items.len, .len = 5, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index bb4813a9df..20d9c3e78e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -214,16 +214,15 @@ 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 + /// List of PIE fixups in the code. + /// This is a table of all position-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) = .{}, + pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, /// Points to the previous and next neighbours prev: ?*TextBlock, next: ?*TextBlock, - pub const RipPosition = struct { + pub const PieFixup = struct { address: u64, start: usize, len: usize, @@ -237,13 +236,12 @@ 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); + pub fn addPieFixup(self: *TextBlock, alloc: *Allocator, fixup: PieFixup) !void { + return self.pie_fixups.append(alloc, fixup); } fn deinit(self: *TextBlock, alloc: *Allocator) void { - self.rip_positions.deinit(alloc); + self.pie_fixups.deinit(alloc); } /// Returns how much room there is to grow in virtual address space. @@ -850,6 +848,9 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { } pub fn deinit(self: *MachO) void { + for (self.text_block_free_list.items) |tb| { + tb.deinit(self.base.allocator); + } self.text_block_free_list.deinit(self.base.allocator); self.offset_table.deinit(self.base.allocator); self.offset_table_free_list.deinit(self.base.allocator); @@ -892,7 +893,9 @@ fn freeTextBlock(self: *MachO, text_block: *TextBlock) void { if (!already_have_free_list_node and prev.freeListEligible(self.*)) { // The free list is heuristics, it doesn't have to be perfect, so we can ignore // the OOM here. - self.text_block_free_list.append(self.base.allocator, prev) catch {}; + self.text_block_free_list.append(self.base.allocator, prev) catch { + prev.deinit(self.base.allocator); + }; } } else { text_block.prev = null; @@ -982,7 +985,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, symbol.n_value, vaddr }); if (vaddr != symbol.n_value) { symbol.n_value = vaddr; - log.debug(" (writing new offset table entry)\n", .{}); self.offset_table.items[decl.link.macho.offset_table_index] = vaddr; try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); @@ -1013,17 +1015,13 @@ 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) + // Perform PIE 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)]; + while (decl.link.macho.pie_fixups.popOrNull()) |fixup| { + const target_addr = fixup.address; + const this_addr = symbol.n_value + fixup.start; + const displacement = @intCast(u32, target_addr - this_addr - fixup.len); + var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; mem.writeIntSliceLittle(u32, placeholder, displacement); } @@ -1185,8 +1183,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { if (self.text_section_index == null) { self.text_section_index = @intCast(u16, self.sections.items.len); const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; - text_segment.cmdsize += @sizeOf(macho.section_64); - text_segment.nsects += 1; const program_code_size_hint = self.base.options.program_code_size_hint; const file_size = mem.alignForwardGeneric(u64, program_code_size_hint, self.page_size); @@ -1212,11 +1208,13 @@ pub fn populateMissingMetadata(self: *MachO) !void { text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. text_segment.filesize = file_size + off; + text_segment.cmdsize += @sizeOf(macho.section_64); + text_segment.nsects += 1; self.cmd_table_dirty = true; } if (self.got_section_index == null) { - const text_section = &self.sections.items[self.text_section_index.?]; self.got_section_index = @intCast(u16, self.sections.items.len); + const text_section = &self.sections.items[self.text_section_index.?]; const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; // TODO looking for free space should be done *within* a segment it belongs to @@ -1225,7 +1223,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); try self.sections.append(self.base.allocator, .{ - .sectname = makeStaticString("__ziggot"), + .sectname = makeStaticString("__got"), .segname = makeStaticString("__TEXT"), .addr = text_section.addr + text_section.size, .size = file_size, @@ -1239,8 +1237,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { .reserved3 = 0, }); - const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size); const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size); text_segment.vmsize += added_size; text_segment.filesize += added_size; text_segment.cmdsize += @sizeOf(macho.section_64); @@ -1653,18 +1651,17 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { const off = sect.offset + @sizeOf(u64) * index; const vmaddr = sect.addr + @sizeOf(u64) * index; const pos_symbol_off = @truncate(u31, vmaddr - self.offset_table.items[index] + 7); - const symbol_off = @intCast(i32, pos_symbol_off) * -1; - std.debug.print("vmaddr=0x{x},item=0x{x}\n", .{vmaddr, self.offset_table.items[index]}); - std.debug.print("posSymbolOff=0x{x},symbolOff=0x{x}\n", .{pos_symbol_off, @bitCast(u32, symbol_off)}); + const symbol_off = @bitCast(u32, @intCast(i32, pos_symbol_off) * -1); var code: [8]u8 = undefined; // lea %rax, [rip - disp] code[0] = 0x48; code[1] = 0x8D; code[2] = 0x5; - mem.writeInt(u32, code[3..7], @bitCast(u32, symbol_off), endian); + mem.writeInt(u32, code[3..7], symbol_off, endian); // ret code[7] = 0xC3; + log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); try self.base.file.?.pwriteAll(&code, off); } @@ -1846,14 +1843,11 @@ fn writeCmdHeaders(self: *MachO) !void { // only one, noname segment to append this section header to. return error.TODOImplementWritingObjFiles; }; - // write __text section header - const id1 = self.text_section_index.?; - log.debug("writing text section header at 0x{x}\n", .{off}); - try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[id1 .. id1 + 1]), off); - // write __ziggot section header - const id2 = self.got_section_index.?; - log.debug("writing got section header at 0x{x}\n", .{off + @sizeOf(macho.section_64)}); - try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[id2 .. id2 + 1]), off + @sizeOf(macho.section_64)); + // write sections belonging to __TEXT segment + // TODO section indices should belong to each Segment, and we should iterate dynamically. + const id = self.text_section_index.?; + log.debug("writing __TEXT section headers at 0x{x}\n", .{off}); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[id .. id + 2]), off); } } -- cgit v1.2.3 From 10942e3f86c03d30833cc221371f30b78a4bd710 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 25 Nov 2020 11:10:17 +0100 Subject: stage2 macho: Hello, Silicon! --- src/codegen.zig | 26 ++++++++++++++++++++++---- src/link/MachO.zig | 43 ++++++++++++++++++++++++++++--------------- 2 files changed, 50 insertions(+), 19 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index f75ad079ae..1482e8de7e 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1689,6 +1689,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, .aarch64 => { try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr }); + // blr x30 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); }, else => unreachable, // unsupported architecture on MachO @@ -2583,10 +2584,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, .register => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}), .memory => |addr| { - // 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. - try self.genSetReg(src, reg, .{ .immediate = addr }); - mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .rn = reg }).toU32()); + 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. + // Instead, we need to make use of PC-relative addressing. + // if (reg.id() == 0) { // x0 is special-cased + try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{ + .address = addr, + .start = self.code.items.len, + .len = 4, + }); + // bl [label] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32()); + // } else { + // unreachable; // TODO + // } + } 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. + try self.genSetReg(src, reg, .{ .immediate = addr }); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .rn = reg }).toU32()); + } }, else => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}), }, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 20d9c3e78e..b6f5cb9ce8 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -7,6 +7,7 @@ const fs = std.fs; const log = std.log.scoped(.link); const macho = std.macho; const codegen = @import("../codegen.zig"); +const aarch64 = @import("../codegen/aarch64.zig"); const math = std.math; const mem = std.mem; @@ -1020,9 +1021,15 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { while (decl.link.macho.pie_fixups.popOrNull()) |fixup| { const target_addr = fixup.address; const this_addr = symbol.n_value + fixup.start; - const displacement = @intCast(u32, target_addr - this_addr - fixup.len); - var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; - mem.writeIntSliceLittle(u32, placeholder, displacement); + if (self.base.options.target.cpu.arch == .x86_64) { + const displacement = @intCast(u32, target_addr - this_addr - fixup.len); + var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; + mem.writeIntSliceLittle(u32, placeholder, displacement); + } else { + const displacement = @intCast(u27, target_addr - this_addr); + var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; + mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32()); + } } const text_section = self.sections.items[self.text_section_index.?]; @@ -1646,22 +1653,28 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 { fn writeOffsetTableEntry(self: *MachO, index: usize) !void { const sect = &self.sections.items[self.got_section_index.?]; - const endian = self.base.options.target.cpu.arch.endian(); - const off = sect.offset + @sizeOf(u64) * index; const vmaddr = sect.addr + @sizeOf(u64) * index; - const pos_symbol_off = @truncate(u31, vmaddr - self.offset_table.items[index] + 7); - const symbol_off = @bitCast(u32, @intCast(i32, pos_symbol_off) * -1); var code: [8]u8 = undefined; - // lea %rax, [rip - disp] - code[0] = 0x48; - code[1] = 0x8D; - code[2] = 0x5; - mem.writeInt(u32, code[3..7], symbol_off, endian); - // ret - code[7] = 0xC3; - + if (self.base.options.target.cpu.arch == .x86_64) { + const pos_symbol_off = @intCast(u31, vmaddr - self.offset_table.items[index] + 7); + const symbol_off = @bitCast(u32, @intCast(i32, pos_symbol_off) * -1); + // lea %rax, [rip - disp] + code[0] = 0x48; + code[1] = 0x8D; + code[2] = 0x5; + mem.writeIntLittle(u32, code[3..7], symbol_off); + // ret + code[7] = 0xC3; + } else { + const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]); + const symbol_off = @intCast(i21, pos_symbol_off) * -1; + // adr .x0 [-disp] + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x1, symbol_off).toU32()); + // ret + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(null).toU32()); + } log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); try self.base.file.?.pwriteAll(&code, off); } -- cgit v1.2.3 From c749b78df50160bedae40f90765442dd1f49de3a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 25 Nov 2020 19:58:15 +0100 Subject: stage2 macho: add orr and orn instructions --- src/codegen.zig | 59 ++++++++++++++++++++++++++++---- src/codegen/aarch64.zig | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ src/link/MachO.zig | 10 +++--- 3 files changed, 147 insertions(+), 11 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 1482e8de7e..35443ba0b1 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2588,17 +2588,64 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // For MachO, the binary, with the exception of object files, has to be a PIE. // Therefore we cannot load an absolute address. // Instead, we need to make use of PC-relative addressing. - // if (reg.id() == 0) { // x0 is special-cased + // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack + // like described here: + // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop) + // TODO As far as branching is concerned, instead of saving the return address + // in a register, I'm thinking here of immitating x86_64, and having the address + // passed on the stack. + if (reg.id() == 0) { // x0 is special-cased + // str x28, [sp, #-16] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{ + .offset = Instruction.Offset.imm_pre_index(-16), + }).toU32()); + // adr x28, #8 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{ .address = addr, .start = self.code.items.len, .len = 4, }); - // bl [label] - mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32()); - // } else { - // unreachable; // TODO - // } + // b [label] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32()); + // mov r, x0 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(reg, .x0, Instruction.RegisterShift.none()).toU32()); + // ldr x28, [sp], #16 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{ + .rn = Register.sp, + .offset = Instruction.Offset.imm_post_index(16), + }).toU32()); + } else { + // str x28, [sp, #-16] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{ + .offset = Instruction.Offset.imm_pre_index(-16), + }).toU32()); + // str x0, [sp, #-16] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x0, Register.sp, .{ + .offset = Instruction.Offset.imm_pre_index(-16), + }).toU32()); + // adr x28, #8 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); + try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{ + .address = addr, + .start = self.code.items.len, + .len = 4, + }); + // b [label] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32()); + // mov r, x0 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(reg, .x0, Instruction.RegisterShift.none()).toU32()); + // ldr x0, [sp], #16 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x0, .{ + .rn = Register.sp, + .offset = Instruction.Offset.imm_post_index(16), + }).toU32()); + // ldr x28, [sp], #16 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{ + .rn = Register.sp, + .offset = Instruction.Offset.imm_post_index(16), + }).toU32()); + } } 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. diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig index 18fccb823e..33b4a14eda 100644 --- a/src/codegen/aarch64.zig +++ b/src/codegen/aarch64.zig @@ -19,6 +19,8 @@ pub const Register = enum(u6) { w16, w17, w18, w19, w20, w21, w22, w23, w24, w25, w26, w27, w28, w29, w30, wzr, + pub const sp = .xzr; + pub fn id(self: Register) u5 { return @truncate(u5, @enumToInt(self)); } @@ -195,6 +197,17 @@ test "FloatingPointRegister.toX" { /// Represents an instruction in the AArch64 instruction set pub const Instruction = union(enum) { + OrShiftedRegister: packed struct { + rd: u5, + rn: u5, + imm6: u6, + rm: u5, + n: u1, + shift: u2, + fixed: u5 = 0b01010, + opc: u2 = 0b01, + sf: u1, + }, MoveWideImmediate: packed struct { rd: u5, imm16: u16, @@ -251,6 +264,7 @@ pub const Instruction = union(enum) { pub fn toU32(self: Instruction) u32 { return switch (self) { + .OrShiftedRegister => |v| @bitCast(u32, v), .MoveWideImmediate => |v| @bitCast(u32, v), .PCRelativeAddress => |v| @bitCast(u32, v), .LoadStoreRegister => |v| @bitCast(u32, v), @@ -379,8 +393,65 @@ pub const Instruction = union(enum) { } }; + pub const RegisterShift = struct { + rn: u5, + imm6: u6, + shift: enum(u2) { + Lsl = 0, + Lsr = 1, + Asr = 2, + Ror = 3, + }, + + pub fn none() RegisterShift { + return .{ + .rn = 0b11111, + .imm6 = 0, + .shift = .Lsl, + }; + } + }; + // Helper functions for assembly syntax functions + fn orShiftedRegister( + rd: Register, + rm: Register, + shift: RegisterShift, + invert: bool, + ) Instruction { + const n: u1 = if (invert) 1 else 0; + switch (rd.size()) { + 32 => { + return Instruction{ + .OrShiftedRegister = .{ + .rd = rd.id(), + .rn = shift.rn, + .imm6 = shift.imm6, + .rm = rm.id(), + .n = n, + .shift = @enumToInt(shift.shift), + .sf = 0, + }, + }; + }, + 64 => { + return Instruction{ + .OrShiftedRegister = .{ + .rd = rd.id(), + .rn = shift.rn, + .imm6 = shift.imm6, + .rm = rm.id(), + .n = n, + .shift = @enumToInt(shift.shift), + .sf = 1, + }, + }; + }, + else => unreachable, // unexpected register size + } + } + fn moveWideImmediate( opc: u2, rd: Register, @@ -543,6 +614,16 @@ pub const Instruction = union(enum) { }; } + // Bitwise (inclusive) OR of a register value + + pub fn orr(rd: Register, rm: Register, shift: RegisterShift) Instruction { + return orShiftedRegister(rd, rm, shift, false); + } + + pub fn orn(rd: Register, rm: Register, shift: RegisterShift) Instruction { + return orShiftedRegister(rd, rm, shift, true); + } + // Move wide (immediate) pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction { @@ -653,6 +734,14 @@ test "serialize instructions" { }; const testcases = [_]Testcase{ + .{ // orr x0 x1 + .inst = Instruction.orr(.x0, .x1, Instruction.RegisterShift.none()), + .expected = 0b1_01_01010_00_0_00001_000000_11111_00000, + }, + .{ // orn x0 x1 + .inst = Instruction.orn(.x0, .x1, Instruction.RegisterShift.none()), + .expected = 0b1_01_01010_00_1_00001_000000_11111_00000, + }, .{ // movz x1 #4 .inst = Instruction.movz(.x1, 4, 0), .expected = 0b1_10_100101_00_0000000000000100_00001, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index b6f5cb9ce8..e0d5cc0d3c 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1028,7 +1028,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { } else { const displacement = @intCast(u27, target_addr - this_addr); var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; - mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32()); + mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@intCast(i28, displacement)).toU32()); } } @@ -1670,10 +1670,10 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { } else { const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]); const symbol_off = @intCast(i21, pos_symbol_off) * -1; - // adr .x0 [-disp] - mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x1, symbol_off).toU32()); - // ret - mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(null).toU32()); + // adr x0, #-disp + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32()); + // ret x28 + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32()); } log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); try self.base.file.?.pwriteAll(&code, off); -- cgit v1.2.3 From 64eae8f39240109ce23c21e975c1d29191b4692a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 25 Nov 2020 22:02:59 +0100 Subject: stage2 macho: move PIE fixups to link file; fix tests --- src/codegen.zig | 8 ++++---- src/link/MachO.zig | 46 ++++++++++++++++++++-------------------------- 2 files changed, 24 insertions(+), 30 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 35443ba0b1..af91e7e909 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2601,7 +2601,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }).toU32()); // adr x28, #8 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); - try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{ + try macho_file.pie_fixups.append(self.bin_file.allocator, .{ .address = addr, .start = self.code.items.len, .len = 4, @@ -2626,7 +2626,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }).toU32()); // adr x28, #8 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); - try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{ + try macho_file.pie_fixups.append(self.bin_file.allocator, .{ .address = addr, .start = self.code.items.len, .len = 4, @@ -2838,7 +2838,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // later in the linker. if (reg.id() == 0) { // %rax is special-cased try self.code.ensureCapacity(self.code.items.len + 5); - try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{ + try macho_file.pie_fixups.append(self.bin_file.allocator, .{ .address = x, .start = self.code.items.len, .len = 5, @@ -2855,7 +2855,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try self.code.ensureCapacity(self.code.items.len + 10); // push %rax self.code.appendSliceAssumeCapacity(&[_]u8{0x50}); - try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{ + try macho_file.pie_fixups.append(self.bin_file.allocator, .{ .address = x, .start = self.code.items.len, .len = 5, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index e0d5cc0d3c..e66b6ab413 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -175,6 +175,22 @@ libsystem_cmd_dirty: bool = false, text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, /// Pointer to the last allocated text block last_text_block: ?*TextBlock = null, +/// 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. +pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, + +pub const PieFixup = struct { + /// Target address we wanted to address in absolute terms. + address: u64, + /// Where in the byte stream we should perform the fixup. + start: usize, + /// The length of the byte stream. For x86_64, this will be + /// variable. For aarch64, it will be fixed at 4 bytes. + len: usize, +}; /// `alloc_num / alloc_den` is the factor of padding when allocating. const alloc_num = 4; @@ -215,20 +231,10 @@ 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 PIE fixups in the code. - /// This is a table of all position-relative positions that will need fixups - /// after codegen when linker assigns addresses to GOT entries. - pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, /// Points to the previous and next neighbours prev: ?*TextBlock, next: ?*TextBlock, - pub const PieFixup = struct { - address: u64, - start: usize, - len: usize, - }; - pub const empty = TextBlock{ .local_sym_index = 0, .offset_table_index = undefined, @@ -237,14 +243,6 @@ pub const TextBlock = struct { .next = null, }; - pub fn addPieFixup(self: *TextBlock, alloc: *Allocator, fixup: PieFixup) !void { - return self.pie_fixups.append(alloc, fixup); - } - - fn deinit(self: *TextBlock, alloc: *Allocator) void { - self.pie_fixups.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. @@ -849,9 +847,7 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { } pub fn deinit(self: *MachO) void { - for (self.text_block_free_list.items) |tb| { - tb.deinit(self.base.allocator); - } + self.pie_fixups.deinit(self.base.allocator); self.text_block_free_list.deinit(self.base.allocator); self.offset_table.deinit(self.base.allocator); self.offset_table_free_list.deinit(self.base.allocator); @@ -894,9 +890,7 @@ fn freeTextBlock(self: *MachO, text_block: *TextBlock) void { if (!already_have_free_list_node and prev.freeListEligible(self.*)) { // The free list is heuristics, it doesn't have to be perfect, so we can ignore // the OOM here. - self.text_block_free_list.append(self.base.allocator, prev) catch { - prev.deinit(self.base.allocator); - }; + self.text_block_free_list.append(self.base.allocator, prev) catch {}; } } else { text_block.prev = null; @@ -1018,7 +1012,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { // Perform PIE fixups (if any) const got_section = self.sections.items[self.got_section_index.?]; - while (decl.link.macho.pie_fixups.popOrNull()) |fixup| { + while (self.pie_fixups.popOrNull()) |fixup| { const target_addr = fixup.address; const this_addr = symbol.n_value + fixup.start; if (self.base.options.target.cpu.arch == .x86_64) { @@ -1761,7 +1755,7 @@ fn writeCodeSignature(self: *MachO) !void { } fn writeExportTrie(self: *MachO) !void { - assert(self.global_symbols.items.len > 0); + if (self.global_symbols.items.len == 0) return; var trie: Trie = .{}; defer trie.deinit(self.base.allocator); -- cgit v1.2.3 From 02baaac506d0d64d1c946219335b7492222c4b64 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 27 Nov 2020 20:31:26 +0100 Subject: Update src/codegen.zig Co-authored-by: Andrew Kelley --- src/codegen.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index af91e7e909..8bd08af15c 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2584,7 +2584,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, .register => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}), .memory => |addr| { - if (self.bin_file.cast(link.File.MachO)) |macho_file| { + if (self.bin_file.options.pie) { // For MachO, the binary, with the exception of object files, has to be a PIE. // Therefore we cannot load an absolute address. // Instead, we need to make use of PC-relative addressing. -- cgit v1.2.3 From 5ed76268c9f0ecca8c5d62cb7e56da05aa8f1f7c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 27 Nov 2020 20:55:34 +0100 Subject: stage2 macho: apply more review comments --- src/codegen.zig | 58 ++++++++++++++++++++++++++++++++++-------------------- src/link/MachO.zig | 58 +++++++++++++++++++++++++++++++----------------------- 2 files changed, 70 insertions(+), 46 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 8bd08af15c..74800aa22e 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2601,11 +2601,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }).toU32()); // adr x28, #8 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); - try macho_file.pie_fixups.append(self.bin_file.allocator, .{ - .address = addr, - .start = self.code.items.len, - .len = 4, - }); + if (self.bin_file.cast(link.File.MachO)) |macho_file| { + try macho_file.pie_fixups.append(self.bin_file.allocator, .{ + .address = addr, + .start = self.code.items.len, + .len = 4, + }); + } else { + return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{}); + } // b [label] mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32()); // mov r, x0 @@ -2626,11 +2630,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }).toU32()); // adr x28, #8 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); - try macho_file.pie_fixups.append(self.bin_file.allocator, .{ - .address = addr, - .start = self.code.items.len, - .len = 4, - }); + if (self.bin_file.cast(link.File.MachO)) |macho_file| { + try macho_file.pie_fixups.append(self.bin_file.allocator, .{ + .address = addr, + .start = self.code.items.len, + .len = 4, + }); + } else { + return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{}); + } // b [label] mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32()); // mov r, x0 @@ -2828,7 +2836,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R }); }, .memory => |x| { - if (self.bin_file.cast(link.File.MachO)) |macho_file| { + if (self.bin_file.options.pie) { // 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. @@ -2838,11 +2846,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // later in the linker. if (reg.id() == 0) { // %rax is special-cased try self.code.ensureCapacity(self.code.items.len + 5); - try macho_file.pie_fixups.append(self.bin_file.allocator, .{ - .address = x, - .start = self.code.items.len, - .len = 5, - }); + if (self.bin_file.cast(link.File.MachO)) |macho_file| { + try macho_file.pie_fixups.append(self.bin_file.allocator, .{ + .address = x, + .start = self.code.items.len, + .len = 5, + }); + } else { + return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{}); + } // call [label] self.code.appendSliceAssumeCapacity(&[_]u8{ 0xE8, @@ -2855,11 +2867,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try self.code.ensureCapacity(self.code.items.len + 10); // push %rax self.code.appendSliceAssumeCapacity(&[_]u8{0x50}); - try macho_file.pie_fixups.append(self.bin_file.allocator, .{ - .address = x, - .start = self.code.items.len, - .len = 5, - }); + if (self.bin_file.cast(link.File.MachO)) |macho_file| { + try macho_file.pie_fixups.append(self.bin_file.allocator, .{ + .address = x, + .start = self.code.items.len, + .len = 5, + }); + } else { + return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{}); + } // call [label] self.code.appendSliceAssumeCapacity(&[_]u8{ 0xE8, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index e66b6ab413..d2a03cae18 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1015,14 +1015,18 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { while (self.pie_fixups.popOrNull()) |fixup| { const target_addr = fixup.address; const this_addr = symbol.n_value + fixup.start; - if (self.base.options.target.cpu.arch == .x86_64) { - const displacement = @intCast(u32, target_addr - this_addr - fixup.len); - var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; - mem.writeIntSliceLittle(u32, placeholder, displacement); - } else { - const displacement = @intCast(u27, target_addr - this_addr); - var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; - mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@intCast(i28, displacement)).toU32()); + switch (self.base.options.target.cpu.arch) { + .x86_64 => { + const displacement = @intCast(u32, target_addr - this_addr - fixup.len); + var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; + mem.writeIntSliceLittle(u32, placeholder, displacement); + }, + .aarch64 => { + const displacement = @intCast(u27, target_addr - this_addr); + var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; + mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@intCast(i28, displacement)).toU32()); + }, + else => unreachable, // unsupported target architecture } } @@ -1651,23 +1655,27 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { const vmaddr = sect.addr + @sizeOf(u64) * index; var code: [8]u8 = undefined; - if (self.base.options.target.cpu.arch == .x86_64) { - const pos_symbol_off = @intCast(u31, vmaddr - self.offset_table.items[index] + 7); - const symbol_off = @bitCast(u32, @intCast(i32, pos_symbol_off) * -1); - // lea %rax, [rip - disp] - code[0] = 0x48; - code[1] = 0x8D; - code[2] = 0x5; - mem.writeIntLittle(u32, code[3..7], symbol_off); - // ret - code[7] = 0xC3; - } else { - const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]); - const symbol_off = @intCast(i21, pos_symbol_off) * -1; - // adr x0, #-disp - mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32()); - // ret x28 - mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32()); + switch (self.base.options.target.cpu.arch) { + .x86_64 => { + const pos_symbol_off = @intCast(u31, vmaddr - self.offset_table.items[index] + 7); + const symbol_off = @bitCast(u32, @intCast(i32, pos_symbol_off) * -1); + // lea %rax, [rip - disp] + code[0] = 0x48; + code[1] = 0x8D; + code[2] = 0x5; + mem.writeIntLittle(u32, code[3..7], symbol_off); + // ret + code[7] = 0xC3; + }, + .aarch64 => { + const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]); + const symbol_off = @intCast(i21, pos_symbol_off) * -1; + // adr x0, #-disp + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32()); + // ret x28 + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32()); + }, + else => unreachable, // unsupported target architecture } log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); try self.base.file.?.pwriteAll(&code, off); -- cgit v1.2.3