authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-16 09:44:59+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-16 09:56:43+01:00
logeaf90d33ed1bd5c2eab446af5fb1b81e232d05b7
tree74084759d0107b09f3db4ae58e417683d2468173
parent5e4275346c522ced19f0baacf8a375177bc684a8
signature Commit is signed but in an unrecognized format.

MachO2: aarch64 encoding fixes


1 files changed, 35 insertions(+), 75 deletions(-)

src/link/MachO2.zig+35-75
......@@ -422,7 +422,7 @@ const Reloc = struct {
422422 ///
423423 /// This function does not perform length checks on `dest_overlong`; the caller guarantees
424424 /// that it will be at least long enough to support this relocation type.
425 fn validateInst(t: Type, macho: *const MachO, dest_overlong: []const u8) bool {
425 fn validateInst(t: Type, dest_overlong: []const u8) bool {
426426 switch (t) {
427427 .abs8,
428428 .abs16,
......@@ -432,8 +432,8 @@ const Reloc = struct {
432432 => return true,
433433
434434 .arm64_branchrel26 => {
435 const inst: *align(1) const aarch64.encoding.Instruction = @ptrCast(dest_overlong[0..4]);
436 return switch (macho.targetLoad(inst).decode()) {
435 const inst: aarch64.encoding.Instruction = .read(dest_overlong[0..4]);
436 return switch (inst.decode()) {
437437 .branch_exception_generating_system => |begs| switch (begs.decode()) {
438438 .unconditional_branch_immediate => true,
439439 else => false,
......@@ -442,8 +442,8 @@ const Reloc = struct {
442442 };
443443 },
444444 .arm64_pagerel21 => {
445 const inst: *align(1) const aarch64.encoding.Instruction = @ptrCast(dest_overlong[0..4]);
446 return switch (macho.targetLoad(inst).decode()) {
445 const inst: aarch64.encoding.Instruction = .read(dest_overlong[0..4]);
446 return switch (inst.decode()) {
447447 .data_processing_immediate => |dpi| switch (dpi.decode()) {
448448 .pc_relative_addressing => |pra| switch (pra.decode()) {
449449 .adrp => true,
......@@ -455,8 +455,8 @@ const Reloc = struct {
455455 };
456456 },
457457 .arm64_pageoff12 => {
458 const inst: *align(1) const aarch64.encoding.Instruction = @ptrCast(dest_overlong[0..4]);
459 return switch (macho.targetLoad(inst).decode()) {
458 const inst: aarch64.encoding.Instruction = .read(dest_overlong[0..4]);
459 return switch (inst.decode()) {
460460 .data_processing_immediate => |dpi| switch (dpi.decode()) {
461461 .add_subtract_immediate => true,
462462 else => false,
......@@ -602,10 +602,9 @@ const Reloc = struct {
602602 }
603603 const casted = std.math.cast(i26, shifted) orelse return error.RelocationOverflow;
604604
605 const inst: *align(1) aarch64.encoding.Instruction = @ptrCast(dest_slice[0..4]);
606 var x = macho.targetLoad(inst);
607 x.branch_exception_generating_system.unconditional_branch_immediate.group.imm26 = casted;
608 macho.targetStore(inst, x);
605 var inst: aarch64.encoding.Instruction = .read(dest_slice[0..4]);
606 inst.branch_exception_generating_system.unconditional_branch_immediate.group.imm26 = casted;
607 inst.write(dest_slice[0..4]);
609608 },
610609 .arm64_pagerel21 => {
611610 const cur_page: u53 = @intCast(dest_vaddr >> 12);
......@@ -615,25 +614,21 @@ const Reloc = struct {
615614 std.math.cast(i21, page_off) orelse return error.RelocationOverflow,
616615 );
617616
618 const inst: *align(1) aarch64.encoding.Instruction = @ptrCast(dest_slice[0..4]);
619 var x = macho.targetLoad(inst);
620 x.data_processing_immediate.pc_relative_addressing.adrp.immhi = split.hi;
621 x.data_processing_immediate.pc_relative_addressing.adrp.immlo = split.lo;
622 macho.targetStore(inst, x);
617 var inst: aarch64.encoding.Instruction = .read(dest_slice[0..4]);
618 inst.data_processing_immediate.pc_relative_addressing.adrp.immhi = split.hi;
619 inst.data_processing_immediate.pc_relative_addressing.adrp.immlo = split.lo;
620 inst.write(dest_slice[0..4]);
623621 },
624622 .arm64_pageoff12 => {
625623 const page_off: u12 = @truncate(target_val);
626624
627625 // This could be one of several instructions: an `ADD`, an `LDR`, or an `STR`. The
628 // `LDR` and `STR` instructions have scale factors which we need to compensate for
629 // by dividing the value we store. Therefore, we need to figure out which case we're
630 // in by looking at the instruction encoding.
631 const inst: *align(1) aarch64.encoding.Instruction = @ptrCast(dest_slice[0..4]);
632 switch (macho.targetLoad(inst).decode()) {
626 // `LDR` and `STR` instructions have scale factors which we need to compensate for,
627 // so we need to decode the instruction to figure out which case we're in.
628 var inst: aarch64.encoding.Instruction = .read(dest_slice[0..4]);
629 switch (inst.decode()) {
633630 .data_processing_immediate => {
634 var x = macho.targetLoad(inst);
635 x.data_processing_immediate.add_subtract_immediate.group.imm12 = page_off;
636 macho.targetStore(inst, x);
631 inst.data_processing_immediate.add_subtract_immediate.group.imm12 = page_off;
637632 },
638633 .load_store => |ls| {
639634 const scale: u3 = switch (ls.register_unsigned_immediate.decode()) {
......@@ -644,12 +639,11 @@ const Reloc = struct {
644639 if (shifted << scale != page_off) {
645640 return error.RelocationMisaligned;
646641 }
647 var x = macho.targetLoad(inst);
648 x.load_store.register_unsigned_immediate.group.imm12 = shifted;
649 macho.targetStore(inst, x);
642 inst.load_store.register_unsigned_immediate.group.imm12 = shifted;
650643 },
651644 else => unreachable,
652645 }
646 inst.write(dest_slice[0..4]);
653647 },
654648 }
655649 }
......@@ -3033,7 +3027,7 @@ fn loadObject(
30333027 };
30343028
30353029 const dest_slice = atoms.items[atom_index].index.node(macho).sliceConst(&macho.mf)[offset_in_atom..];
3036 if (!internal_type.validateInst(macho, dest_slice)) {
3030 if (!internal_type.validateInst(dest_slice)) {
30373031 return diags.failParse(path, "relocation '{s}' against invalid instruction", .{
30383032 reloc_type_str,
30393033 });
......@@ -3719,57 +3713,23 @@ fn writeStubEntry(macho: *MachO, stub_index: u64, got_index: u64) void {
37193713 .aarch64 => {
37203714 // The first instruction of the stub will be an ADRP for which we need to compute the
37213715 // offset from that instruction's 4k page to the GOT entry's 4k page.
3722 const Imm21 = packed union {
3723 raw: i21,
3724 split: packed struct(u21) { lo: u2, hi: u19 },
3725 };
3726 const page_off: Imm21 = page_off: {
3727 const cur_page: u53 = @intCast((new_stub_vaddr + 4) >> 12); // first instruction, so pc = stub addr + 4
3728 const target_page: u53 = @intCast(got_entry_vaddr >> 12);
3729 const off: i53 = @bitCast(target_page -% cur_page);
3730 break :page_off .{ .raw = @intCast(off) };
3731 };
3716 const cur_page_base = (new_stub_vaddr >> 12) << 12;
3717 const target_page_base = (got_entry_vaddr >> 12) << 12;
3718 const cur_page_to_target_page: i64 = @bitCast(target_page_base - cur_page_base);
37323719
3733 const Inst = packed union(u32) {
3734 raw: u32,
3735 adrp: packed struct(u32) {
3736 reg: u5,
3737 immhi: u19,
3738 other0: u5,
3739 immlo: u2,
3740 other1: u1,
3741 },
3742 ldr: packed struct(u32) {
3743 reg_t: u5,
3744 reg_n: u5,
3745 imm12: u12,
3746 other0: u10,
3747 },
3748 };
3749 const insts: []align(4) Inst = @ptrCast(@alignCast(
3720 // Then we will use an LDR to reference the GOT entry on that page.
3721 const target_page_off: u12 = @truncate(got_entry_vaddr);
3722
3723 const insts: []align(4) aarch64.encoding.Instruction = @ptrCast(@alignCast(
37503724 macho.known_sections.stubs.node(macho).slice(&macho.mf)[@intCast(stub_offset)..][0..entry_size],
37513725 ));
3752 @memcpy(insts, @as([]const Inst, &.{
3753 // adrp x16, got_entry@page
3754 .{ .adrp = .{
3755 .reg = 0b10000,
3756 .immhi = page_off.split.hi,
3757 .other0 = 0b100_00,
3758 .immlo = page_off.split.lo,
3759 .other1 = 0b1,
3760 } },
3761 // ldr x16, [x16, got_entry@pageoff]
3762 .{ .ldr = .{
3763 .reg_t = 0b10000,
3764 .reg_n = 0b10000,
3765 .imm12 = @divExact(@as(u12, @truncate(got_entry_vaddr)), 8),
3766 .other0 = 0b11_111_0_01_01,
3767 } },
3768 // br x16
3769 .{ .raw = 0xd61f0200 },
3726 @memcpy(insts, @as([]const aarch64.encoding.Instruction, &.{
3727 .adrp(.x16, @intCast(cur_page_to_target_page)),
3728 .ldr(.x16, .{ .unsigned_offset = .{ .base = .x16, .offset = target_page_off } }),
3729 .br(.x16),
37703730 }));
3771 if (macho.targetEndian() != std.lang.Endian.native) {
3772 std.mem.byteSwapAllElements(Inst, insts);
3731 if (.little != std.lang.Endian.native) { // aarch64 instructions are little-endian
3732 std.mem.byteSwapAllElements(aarch64.encoding.Instruction, insts);
37733733 }
37743734 },
37753735 .x86_64 => {