Commit 586c704212

Jakub Konka <kubkon@jakubkonka.com>
2021-02-25 22:04:28
zld: pass stage2 tests linked with zld!
1 parent e825a15
Changed files (2)
src
link
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,
             },
         };
     }
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(&sect.sectname),
                 .segname = makeStaticString(&sect.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(&sect.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,