From 586c704212b5453281cc27626f1ae46982a62d16 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 25 Feb 2021 22:04:28 +0100 Subject: [PATCH] zld: pass stage2 tests linked with zld! --- src/link/MachO/Zld.zig | 170 ++++++++++++++++++++++----------------- src/link/MachO/reloc.zig | 27 ++++++- 2 files changed, 119 insertions(+), 78 deletions(-) diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 838e16b0e548d533259c59df0e2b77a26e4abba9..0a587538fe35398f934d0f7351d1c3748e17b214 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -252,9 +252,9 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { try self.populateMetadata(); try self.parseInputFiles(files); try self.resolveImports(); - self.allocateTextSegment(); - self.allocateDataSegment(); - self.allocateLinkeditSegment(); + try self.allocateTextSegment(); + try self.allocateDataSegment(); + try self.allocateLinkeditSegment(); try self.writeStubHelperCommon(); try self.resolveSymbols(); try self.doRelocs(); @@ -317,13 +317,20 @@ fn parseObjectFile(self: *Zld, object: *const Object) !void { if (mem.eql(u8, sectname, "__thread_vars")) { self.tlv_section_index = sect_index; } + log.warn("{s} align 0x{x}", .{ sectname, sect.@"align" }); + const alignment = switch (sect.flags) { + macho.S_4BYTE_LITERALS => 2, + macho.S_8BYTE_LITERALS => 3, + macho.S_16BYTE_LITERALS => 4, + else => sect.@"align", + }; try seg.append(self.allocator, .{ .sectname = makeStaticString(§.sectname), .segname = makeStaticString(§.segname), .addr = 0, .size = 0, .offset = 0, - .@"align" = sect.@"align", + .@"align" = alignment, .reloff = 0, .nreloc = 0, .flags = sect.flags, @@ -429,7 +436,7 @@ fn resolveImports(self: *Zld) !void { }); } -fn allocateTextSegment(self: *Zld) void { +fn allocateTextSegment(self: *Zld) !void { const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; const nexterns = @intCast(u32, self.lazy_imports.items().len); @@ -450,10 +457,16 @@ fn allocateTextSegment(self: *Zld) void { sizeofcmds += lc.cmdsize(); } - self.allocateSegment(self.text_segment_cmd_index.?, 0, sizeofcmds, true); + try self.allocateSegment( + self.text_segment_cmd_index.?, + 0, + // sizeofcmds + 10 * 4 * @sizeOf(u32), + 3140, + true, + ); } -fn allocateDataSegment(self: *Zld) void { +fn allocateDataSegment(self: *Zld) !void { const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; const nonlazy = @intCast(u32, self.nonlazy_imports.items().len); const lazy = @intCast(u32, self.lazy_imports.items().len); @@ -470,16 +483,16 @@ fn allocateDataSegment(self: *Zld) void { const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; const offset = text_seg.inner.fileoff + text_seg.inner.filesize; - self.allocateSegment(self.data_segment_cmd_index.?, offset, 0, false); + try self.allocateSegment(self.data_segment_cmd_index.?, offset, 0, false); } -fn allocateLinkeditSegment(self: *Zld) void { +fn allocateLinkeditSegment(self: *Zld) !void { const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; const offset = data_seg.inner.fileoff + data_seg.inner.filesize; - self.allocateSegment(self.linkedit_segment_cmd_index.?, offset, 0, false); + try self.allocateSegment(self.linkedit_segment_cmd_index.?, offset, 0, false); } -fn allocateSegment(self: *Zld, index: u16, offset: u64, start: u64, reverse: bool) void { +fn allocateSegment(self: *Zld, index: u16, offset: u64, start: u64, reverse: bool) !void { const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize; const seg = &self.load_commands.items[index].Segment; @@ -495,23 +508,27 @@ fn allocateSegment(self: *Zld, index: u16, offset: u64, start: u64, reverse: boo seg.inner.filesize = aligned_size; // Allocate section offsets - if (reverse) { - var end_off: u64 = seg.inner.fileoff + seg.inner.filesize; - var count: usize = seg.sections.items.len; - while (count > 0) : (count -= 1) { - const sec = &seg.sections.items[count - 1]; - end_off -= mem.alignForwardGeneric(u64, sec.size, @sizeOf(u32)); // TODO Should we always align to 4? - sec.offset = @intCast(u32, end_off); - sec.addr = base_vmaddr + end_off; - } - } else { - var next_off: u64 = seg.inner.fileoff; - for (seg.sections.items) |*sect| { - sect.offset = @intCast(u32, next_off); - sect.addr = base_vmaddr + next_off; - next_off += mem.alignForwardGeneric(u64, sect.size, @sizeOf(u32)); // TODO Should we always align to 4? - } + // if (reverse) { + // var end_off: u64 = seg.inner.fileoff + seg.inner.filesize; + // var count: usize = seg.sections.items.len; + // while (count > 0) : (count -= 1) { + // const sec = &seg.sections.items[count - 1]; + // const alignment = math.max(@alignOf(u32), try std.math.powi(u32, 2, sec.@"align")); + // log.warn("{s} 0x{x} alignment = 0x{x}", .{ parseName(&sec.sectname), sec.@"align", alignment }); + // end_off -= mem.alignForwardGeneric(u64, sec.size, alignment); + // sec.offset = @intCast(u32, end_off); + // sec.addr = base_vmaddr + end_off; + // } + // } else { + var next_off: u64 = seg.inner.fileoff + start; + for (seg.sections.items) |*sect| { + const alignment = math.max(@alignOf(u32), try std.math.powi(u32, 2, sect.@"align")); + log.warn("{s} 0x{x} alignment = 0x{x}", .{ parseName(§.sectname), sect.@"align", alignment }); + sect.offset = @intCast(u32, next_off); + sect.addr = base_vmaddr + next_off; + next_off += mem.alignForwardGeneric(u64, sect.size, alignment); } + // } } fn writeStubHelperCommon(self: *Zld) !void { @@ -552,33 +569,48 @@ fn writeStubHelperCommon(self: *Zld) !void { break :blk stub_helper.offset + code_size; }, .aarch64 => { - var code: [4 * @sizeOf(u32)]u8 = undefined; + var code: [6 * @sizeOf(u32)]u8 = undefined; { const target_addr = data.addr + data.size - @sizeOf(u64); const displacement = @bitCast(u21, try math.cast(i21, target_addr - stub_helper.addr)); // adr x17, disp mem.writeIntLittle(u32, code[0..4], Arm64.adr(17, displacement).toU32()); + // TODO check if adr is enough and expand into adrp + add if not. + // nop in case we need to expand adr for adrp followed by add. + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); } // stp x16, x17, [sp, #-16]! - code[4] = 0xf0; - code[5] = 0x47; - code[6] = 0xbf; - code[7] = 0xa9; - { + code[8] = 0xf0; + code[9] = 0x47; + code[10] = 0xbf; + code[11] = 0xa9; + binder: { const dyld_stub_binder = self.nonlazy_imports.get("dyld_stub_binder").?; const addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64)); - const displacement = try math.divExact(u64, addr - stub_helper.addr - 2 * @sizeOf(u32), 4); - const literal = try math.cast(u19, displacement); + const displacement = math.divExact(u64, addr - stub_helper.addr - 3 * @sizeOf(u32), 4) catch |_| { + log.warn("0x{x}", .{addr - stub_helper.addr - 3 * @sizeOf(u32)}); + // Pad with nop to please division. + // nop + mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32()); + // ldr x16, label + const disp = try math.divExact(u64, addr - stub_helper.addr - 4 * @sizeOf(u32), 4); + const literal = try math.cast(u19, disp); // TODO use adrp + add if we exceed the range. + mem.writeIntLittle(u32, code[16..20], Arm64.ldr(16, literal, 1).toU32()); + break :binder; + }; + const literal = try math.cast(u19, displacement); // TODO use adrp + add if we exceed the range. // ldr x16, label - mem.writeIntLittle(u32, code[8..12], Arm64.ldr(16, literal, 1).toU32()); + mem.writeIntLittle(u32, code[12..16], Arm64.ldr(16, literal, 1).toU32()); + // nop + mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32()); } // br x16 - code[12] = 0x00; - code[13] = 0x02; - code[14] = 0x1f; - code[15] = 0xd6; + code[20] = 0x00; + code[21] = 0x02; + code[22] = 0x1f; + code[23] = 0xd6; try self.file.?.pwriteAll(&code, stub_helper.offset); - break :blk stub_helper.offset + 4 * @sizeOf(u32); + break :blk stub_helper.offset + 6 * @sizeOf(u32); }, else => unreachable, } @@ -635,12 +667,14 @@ fn writeStub(self: *Zld, index: u32) !void { }, .aarch64 => { assert(la_ptr_addr >= stub_addr); - const displacement = try math.divExact(u64, la_ptr_addr - stub_addr, 4); + const displacement = try math.divExact(u64, la_ptr_addr - stub_addr - @sizeOf(u32), 4); const literal = try math.cast(u19, displacement); + // nop + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); // ldr x16, literal - mem.writeIntLittle(u32, code[0..4], Arm64.ldr(16, literal, 1).toU32()); + mem.writeIntLittle(u32, code[4..8], Arm64.ldr(16, literal, 1).toU32()); // br x16 - mem.writeIntLittle(u32, code[4..8], Arm64.br(16).toU32()); + mem.writeIntLittle(u32, code[8..12], Arm64.br(16).toU32()); }, else => unreachable, } @@ -722,7 +756,8 @@ fn resolveSymbols(self: *Zld) !void { const sym_name = object.getString(sym.n_strx); if (isLocal(&sym) and self.locals.get(sym_name) != null) { - log.warn("symbol '{s}' already exists; skipping", .{sym_name}); + log.warn("local symbol '{s}' defined multiple times; removing", .{sym_name}); + self.locals.swapRemoveAssertDiscard(sym_name); continue; } @@ -978,12 +1013,6 @@ fn doRelocs(self: *Zld) !void { const this_page = @intCast(i32, this_addr >> 12); const target_page = @intCast(i32, ta >> 12); const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); - if (pages == 0) { - // No need to execute adrp. Instead, replace with a nop. - log.warn(" | replacing ADRP with NOP", .{}); - mem.writeIntLittle(u32, inst, aarch64.Instruction.nop().toU32()); - continue; - } log.warn(" | moving by {} pages", .{pages}); var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Address), inst); parsed.immhi = @truncate(u19, pages >> 2); @@ -1000,12 +1029,6 @@ fn doRelocs(self: *Zld) !void { var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Add), inst); const ta = if (addend) |a| target_addr + a else target_addr; const narrowed = @truncate(u12, ta); - if (narrowed == 0) { - // No need to execute add. Instead, replace with a nop. - log.warn(" | replacing ADD with NOP", .{}); - mem.writeIntLittle(u32, inst, aarch64.Instruction.nop().toU32()); - continue; - } parsed.offset = narrowed; } else { log.warn(" | detected LDR/STR opcode", .{}); @@ -1013,20 +1036,18 @@ fn doRelocs(self: *Zld) !void { var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.LoadRegister), inst); const ta = if (addend) |a| target_addr + a else target_addr; const narrowed = @truncate(u12, ta); - if (narrowed == 0) { - // No need to execute ldr/str. Instead, replace with a nop. - log.warn(" | replacing LDR/STR with NOP", .{}); - mem.writeIntLittle(u32, inst, aarch64.Instruction.nop().toU32()); - continue; - } - const denom: u12 = if (parsed.size == 1) 8 else 4; - const offset = math.divExact(u12, narrowed, denom) catch |_| { - // If we are here, then this means we are not able to divide the offset - // exactly by the required denominator. Therefore, we will use add instead of - // ldr as we expect ldr to follow this instruction nonetheless. - // TODO I believe ldr/str can only occur for GOT_LOAD_PAGEOFF12. - mem.writeIntLittle(u32, inst, Arm64.add(parsed.rn, parsed.rn, narrowed, parsed.size).toU32()); - continue; + const offset: u12 = blk: { + if (parsed.size == 0) { + break :blk narrowed; + } else { + const denom: u4 = try math.powi(u4, 2, parsed.size); + const offf = math.divExact(u12, narrowed, denom) catch |_| { + log.warn(" | narrowed 0x{x}", .{narrowed}); + log.warn(" | denom 0x{x}", .{denom}); + continue; + }; + break :blk offf; + } }; parsed.offset = offset; } @@ -1046,7 +1067,7 @@ fn doRelocs(self: *Zld) !void { break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = curr.size }; } else { const curr = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.LoadRegister), inst); - break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = curr.size }; + break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = @truncate(u1, curr.size) }; } }; const ta = if (addend) |a| target_addr + a else target_addr; @@ -1145,6 +1166,7 @@ fn relocTargetAddr(self: *Zld, object: Object, rel: macho.relocation_info, next_ .segname = source_sect.segname, .sectname = source_sect.sectname, }).?; + log.warn(" | symbol local to object", .{}); break :blk target_space.address + sym.n_value - source_sect.addr; } else if (isImport(&sym)) { // Relocate to either the artifact's local symbol, or an import from @@ -1267,7 +1289,7 @@ fn populateMetadata(self: *Zld) !void { }; const stub_size: u4 = switch (self.arch.?) { .x86_64 => 6, - .aarch64 => 2 * @sizeOf(u32), + .aarch64 => 3 * @sizeOf(u32), else => unreachable, // unhandled architecture type }; try text_seg.append(self.allocator, .{ @@ -1298,7 +1320,7 @@ fn populateMetadata(self: *Zld) !void { .aarch64 => 2, else => unreachable, // unhandled architecture type }; - const stub_helper_size: u5 = switch (self.arch.?) { + const stub_helper_size: u6 = switch (self.arch.?) { .x86_64 => 15, .aarch64 => 6 * @sizeOf(u32), else => unreachable, diff --git a/src/link/MachO/reloc.zig b/src/link/MachO/reloc.zig index deb07dc42a035249338132a4ea021d3ab29d64db..9809eddfb8d2253c758b6f0b5a9733a07aaeda69 100644 --- a/src/link/MachO/reloc.zig +++ b/src/link/MachO/reloc.zig @@ -26,8 +26,7 @@ pub const Arm64 = union(enum) { rn: u5, offset: u12, _1: u8 = 0b111_0_01_01, - size: u1, - _2: u1 = 0b1, + size: u2, }, LoadLiteral: packed struct { reg: u5, @@ -135,13 +134,33 @@ pub const Arm64 = union(enum) { }; } - pub fn ldrr(rt: u5, rn: u5, offset: u12, size: u1) Arm64 { + pub fn ldrq(rt: u5, rn: u5, offset: u12) Arm64 { return Arm64{ .LoadRegister = .{ .rt = rt, .rn = rn, .offset = offset, - .size = size, + .size = 0b11, + }, + }; + } + pub fn ldrh(rt: u5, rn: u5, offset: u12) Arm64 { + return Arm64{ + .LoadRegister = .{ + .rt = rt, + .rn = rn, + .offset = offset, + .size = 0b01, + }, + }; + } + pub fn ldrb(rt: u5, rn: u5, offset: u12) Arm64 { + return Arm64{ + .LoadRegister = .{ + .rt = rt, + .rn = rn, + .offset = offset, + .size = 0b00, }, }; } -- 2.54.0