Commit 586c704212
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(§.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,