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 {...@@ -422,7 +422,7 @@ const Reloc = struct {
422 ///422 ///
423 /// This function does not perform length checks on `dest_overlong`; the caller guarantees423 /// This function does not perform length checks on `dest_overlong`; the caller guarantees
424 /// that it will be at least long enough to support this relocation type.424 /// 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 {
426 switch (t) {426 switch (t) {
427 .abs8,427 .abs8,
428 .abs16,428 .abs16,
...@@ -432,8 +432,8 @@ const Reloc = struct {...@@ -432,8 +432,8 @@ const Reloc = struct {
432 => return true,432 => return true,
433433
434 .arm64_branchrel26 => {434 .arm64_branchrel26 => {
435 const inst: *align(1) const aarch64.encoding.Instruction = @ptrCast(dest_overlong[0..4]);435 const inst: aarch64.encoding.Instruction = .read(dest_overlong[0..4]);
436 return switch (macho.targetLoad(inst).decode()) {436 return switch (inst.decode()) {
437 .branch_exception_generating_system => |begs| switch (begs.decode()) {437 .branch_exception_generating_system => |begs| switch (begs.decode()) {
438 .unconditional_branch_immediate => true,438 .unconditional_branch_immediate => true,
439 else => false,439 else => false,
...@@ -442,8 +442,8 @@ const Reloc = struct {...@@ -442,8 +442,8 @@ const Reloc = struct {
442 };442 };
443 },443 },
444 .arm64_pagerel21 => {444 .arm64_pagerel21 => {
445 const inst: *align(1) const aarch64.encoding.Instruction = @ptrCast(dest_overlong[0..4]);445 const inst: aarch64.encoding.Instruction = .read(dest_overlong[0..4]);
446 return switch (macho.targetLoad(inst).decode()) {446 return switch (inst.decode()) {
447 .data_processing_immediate => |dpi| switch (dpi.decode()) {447 .data_processing_immediate => |dpi| switch (dpi.decode()) {
448 .pc_relative_addressing => |pra| switch (pra.decode()) {448 .pc_relative_addressing => |pra| switch (pra.decode()) {
449 .adrp => true,449 .adrp => true,
...@@ -455,8 +455,8 @@ const Reloc = struct {...@@ -455,8 +455,8 @@ const Reloc = struct {
455 };455 };
456 },456 },
457 .arm64_pageoff12 => {457 .arm64_pageoff12 => {
458 const inst: *align(1) const aarch64.encoding.Instruction = @ptrCast(dest_overlong[0..4]);458 const inst: aarch64.encoding.Instruction = .read(dest_overlong[0..4]);
459 return switch (macho.targetLoad(inst).decode()) {459 return switch (inst.decode()) {
460 .data_processing_immediate => |dpi| switch (dpi.decode()) {460 .data_processing_immediate => |dpi| switch (dpi.decode()) {
461 .add_subtract_immediate => true,461 .add_subtract_immediate => true,
462 else => false,462 else => false,
...@@ -602,10 +602,9 @@ const Reloc = struct {...@@ -602,10 +602,9 @@ const Reloc = struct {
602 }602 }
603 const casted = std.math.cast(i26, shifted) orelse return error.RelocationOverflow;603 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]);605 var inst: aarch64.encoding.Instruction = .read(dest_slice[0..4]);
606 var x = macho.targetLoad(inst);606 inst.branch_exception_generating_system.unconditional_branch_immediate.group.imm26 = casted;
607 x.branch_exception_generating_system.unconditional_branch_immediate.group.imm26 = casted;607 inst.write(dest_slice[0..4]);
608 macho.targetStore(inst, x);
609 },608 },
610 .arm64_pagerel21 => {609 .arm64_pagerel21 => {
611 const cur_page: u53 = @intCast(dest_vaddr >> 12);610 const cur_page: u53 = @intCast(dest_vaddr >> 12);
...@@ -615,25 +614,21 @@ const Reloc = struct {...@@ -615,25 +614,21 @@ const Reloc = struct {
615 std.math.cast(i21, page_off) orelse return error.RelocationOverflow,614 std.math.cast(i21, page_off) orelse return error.RelocationOverflow,
616 );615 );
617616
618 const inst: *align(1) aarch64.encoding.Instruction = @ptrCast(dest_slice[0..4]);617 var inst: aarch64.encoding.Instruction = .read(dest_slice[0..4]);
619 var x = macho.targetLoad(inst);618 inst.data_processing_immediate.pc_relative_addressing.adrp.immhi = split.hi;
620 x.data_processing_immediate.pc_relative_addressing.adrp.immhi = split.hi;619 inst.data_processing_immediate.pc_relative_addressing.adrp.immlo = split.lo;
621 x.data_processing_immediate.pc_relative_addressing.adrp.immlo = split.lo;620 inst.write(dest_slice[0..4]);
622 macho.targetStore(inst, x);
623 },621 },
624 .arm64_pageoff12 => {622 .arm64_pageoff12 => {
625 const page_off: u12 = @truncate(target_val);623 const page_off: u12 = @truncate(target_val);
626624
627 // This could be one of several instructions: an `ADD`, an `LDR`, or an `STR`. The625 // 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 for626 // `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're627 // so we need to decode the instruction to figure out which case we're in.
630 // in by looking at the instruction encoding.628 var inst: aarch64.encoding.Instruction = .read(dest_slice[0..4]);
631 const inst: *align(1) aarch64.encoding.Instruction = @ptrCast(dest_slice[0..4]);629 switch (inst.decode()) {
632 switch (macho.targetLoad(inst).decode()) {
633 .data_processing_immediate => {630 .data_processing_immediate => {
634 var x = macho.targetLoad(inst);631 inst.data_processing_immediate.add_subtract_immediate.group.imm12 = page_off;
635 x.data_processing_immediate.add_subtract_immediate.group.imm12 = page_off;
636 macho.targetStore(inst, x);
637 },632 },
638 .load_store => |ls| {633 .load_store => |ls| {
639 const scale: u3 = switch (ls.register_unsigned_immediate.decode()) {634 const scale: u3 = switch (ls.register_unsigned_immediate.decode()) {
...@@ -644,12 +639,11 @@ const Reloc = struct {...@@ -644,12 +639,11 @@ const Reloc = struct {
644 if (shifted << scale != page_off) {639 if (shifted << scale != page_off) {
645 return error.RelocationMisaligned;640 return error.RelocationMisaligned;
646 }641 }
647 var x = macho.targetLoad(inst);642 inst.load_store.register_unsigned_immediate.group.imm12 = shifted;
648 x.load_store.register_unsigned_immediate.group.imm12 = shifted;
649 macho.targetStore(inst, x);
650 },643 },
651 else => unreachable,644 else => unreachable,
652 }645 }
646 inst.write(dest_slice[0..4]);
653 },647 },
654 }648 }
655 }649 }
...@@ -3033,7 +3027,7 @@ fn loadObject(...@@ -3033,7 +3027,7 @@ fn loadObject(
3033 };3027 };
30343028
3035 const dest_slice = atoms.items[atom_index].index.node(macho).sliceConst(&macho.mf)[offset_in_atom..];3029 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)) {
3037 return diags.failParse(path, "relocation '{s}' against invalid instruction", .{3031 return diags.failParse(path, "relocation '{s}' against invalid instruction", .{
3038 reloc_type_str,3032 reloc_type_str,
3039 });3033 });
...@@ -3719,57 +3713,23 @@ fn writeStubEntry(macho: *MachO, stub_index: u64, got_index: u64) void {...@@ -3719,57 +3713,23 @@ fn writeStubEntry(macho: *MachO, stub_index: u64, got_index: u64) void {
3719 .aarch64 => {3713 .aarch64 => {
3720 // The first instruction of the stub will be an ADRP for which we need to compute the3714 // The first instruction of the stub will be an ADRP for which we need to compute the
3721 // offset from that instruction's 4k page to the GOT entry's 4k page.3715 // offset from that instruction's 4k page to the GOT entry's 4k page.
3722 const Imm21 = packed union {3716 const cur_page_base = (new_stub_vaddr >> 12) << 12;
3723 raw: i21,3717 const target_page_base = (got_entry_vaddr >> 12) << 12;
3724 split: packed struct(u21) { lo: u2, hi: u19 },3718 const cur_page_to_target_page: i64 = @bitCast(target_page_base - cur_page_base);
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 };
37323719
3733 const Inst = packed union(u32) {3720 // Then we will use an LDR to reference the GOT entry on that page.
3734 raw: u32,3721 const target_page_off: u12 = @truncate(got_entry_vaddr);
3735 adrp: packed struct(u32) {3722
3736 reg: u5,3723 const insts: []align(4) aarch64.encoding.Instruction = @ptrCast(@alignCast(
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(
3750 macho.known_sections.stubs.node(macho).slice(&macho.mf)[@intCast(stub_offset)..][0..entry_size],3724 macho.known_sections.stubs.node(macho).slice(&macho.mf)[@intCast(stub_offset)..][0..entry_size],
3751 ));3725 ));
3752 @memcpy(insts, @as([]const Inst, &.{3726 @memcpy(insts, @as([]const aarch64.encoding.Instruction, &.{
3753 // adrp x16, got_entry@page3727 .adrp(.x16, @intCast(cur_page_to_target_page)),
3754 .{ .adrp = .{3728 .ldr(.x16, .{ .unsigned_offset = .{ .base = .x16, .offset = target_page_off } }),
3755 .reg = 0b10000,3729 .br(.x16),
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 },
3770 }));3730 }));
3771 if (macho.targetEndian() != std.lang.Endian.native) {3731 if (.little != std.lang.Endian.native) { // aarch64 instructions are little-endian
3772 std.mem.byteSwapAllElements(Inst, insts);3732 std.mem.byteSwapAllElements(aarch64.encoding.Instruction, insts);
3773 }3733 }
3774 },3734 },
3775 .x86_64 => {3735 .x86_64 => {