diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig index 8abc616e2f..d06abeac07 100644 --- a/src/codegen/aarch64.zig +++ b/src/codegen/aarch64.zig @@ -221,7 +221,8 @@ pub const Instruction = union(enum) { offset: u12, opc: u2, op1: u2, - fixed: u4 = 0b111_0, + v: u1, + fixed: u3 = 0b111, size: u2, }, LoadStorePairOfRegisters: packed struct { @@ -505,6 +506,7 @@ pub const Instruction = union(enum) { .offset = offset.toU12(), .opc = opc, .op1 = op1, + .v = 0, .size = 0b10, }, }; @@ -517,6 +519,7 @@ pub const Instruction = union(enum) { .offset = offset.toU12(), .opc = opc, .op1 = op1, + .v = 0, .size = 0b11, }, }; diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index ebddcebd26..f586fa06ed 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -10,6 +10,7 @@ const fs = std.fs; const macho = std.macho; const math = std.math; const log = std.log.scoped(.zld); +const aarch64 = @import("../../codegen/aarch64.zig"); const Allocator = mem.Allocator; const CodeSignature = @import("CodeSignature.zig"); @@ -19,7 +20,6 @@ const Trie = @import("Trie.zig"); usingnamespace @import("commands.zig"); usingnamespace @import("bind.zig"); -usingnamespace @import("reloc.zig"); allocator: *Allocator, @@ -968,27 +968,27 @@ fn writeStubHelperCommon(self: *Zld) !void { data_blk: { const displacement = math.cast(i21, target_addr - this_addr) catch |_| break :data_blk; // adr x17, disp - mem.writeIntLittle(u32, code[0..4], Arm64.adr(17, @bitCast(u21, displacement)).toU32()); + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32()); // nop - mem.writeIntLittle(u32, code[4..8], Arm64.nop().toU32()); + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); break :data_blk_outer; } data_blk: { const new_this_addr = this_addr + @sizeOf(u32); const displacement = math.cast(i21, target_addr - new_this_addr) catch |_| break :data_blk; // nop - mem.writeIntLittle(u32, code[0..4], Arm64.nop().toU32()); + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); // adr x17, disp - mem.writeIntLittle(u32, code[4..8], Arm64.adr(17, @bitCast(u21, displacement)).toU32()); + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32()); break :data_blk_outer; } // Jump is too big, replace adr with adrp and add. const this_page = @intCast(i32, this_addr >> 12); const target_page = @intCast(i32, target_addr >> 12); - const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); - mem.writeIntLittle(u32, code[0..4], Arm64.adrp(17, pages).toU32()); + const pages = @intCast(i21, target_page - this_page); + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32()); const narrowed = @truncate(u12, target_addr); - mem.writeIntLittle(u32, code[4..8], Arm64.add(17, 17, narrowed, 1).toU32()); + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32()); } // stp x16, x17, [sp, #-16]! code[8] = 0xf0; @@ -1003,9 +1003,11 @@ fn writeStubHelperCommon(self: *Zld) !void { const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :binder_blk; const literal = math.cast(u18, displacement) catch |_| break :binder_blk; // ldr x16, label - mem.writeIntLittle(u32, code[12..16], Arm64.ldr(16, literal, 1).toU32()); + mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{ + .literal = literal, + }).toU32()); // nop - mem.writeIntLittle(u32, code[16..20], Arm64.nop().toU32()); + mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32()); break :binder_blk_outer; } binder_blk: { @@ -1015,19 +1017,26 @@ fn writeStubHelperCommon(self: *Zld) !void { log.debug("2: disp=0x{x}, literal=0x{x}", .{ displacement, literal }); // Pad with nop to please division. // nop - mem.writeIntLittle(u32, code[12..16], Arm64.nop().toU32()); + mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32()); // ldr x16, label - mem.writeIntLittle(u32, code[16..20], Arm64.ldr(16, literal, 1).toU32()); + mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ + .literal = literal, + }).toU32()); break :binder_blk_outer; } // Use adrp followed by ldr(immediate). const this_page = @intCast(i32, this_addr >> 12); const target_page = @intCast(i32, target_addr >> 12); - const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); - mem.writeIntLittle(u32, code[12..16], Arm64.adrp(16, pages).toU32()); + const pages = @intCast(i21, target_page - this_page); + mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32()); const narrowed = @truncate(u12, target_addr); const offset = try math.divExact(u12, narrowed, 8); - mem.writeIntLittle(u32, code[16..20], Arm64.ldrq(16, 16, offset).toU32()); + mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ + .register = .{ + .rn = .x16, + .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), + }, + }).toU32()); } // br x16 code[20] = 0x00; @@ -1099,9 +1108,11 @@ fn writeStub(self: *Zld, index: u32) !void { const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :inner; const literal = math.cast(u18, displacement) catch |_| break :inner; // ldr x16, literal - mem.writeIntLittle(u32, code[0..4], Arm64.ldr(16, literal, 1).toU32()); + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ + .literal = literal, + }).toU32()); // nop - mem.writeIntLittle(u32, code[4..8], Arm64.nop().toU32()); + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); break :outer; } inner: { @@ -1109,22 +1120,29 @@ fn writeStub(self: *Zld, index: u32) !void { const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :inner; const literal = math.cast(u18, displacement) catch |_| break :inner; // nop - mem.writeIntLittle(u32, code[0..4], Arm64.nop().toU32()); + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); // ldr x16, literal - mem.writeIntLittle(u32, code[4..8], Arm64.ldr(16, literal, 1).toU32()); + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{ + .literal = literal, + }).toU32()); break :outer; } // Use adrp followed by ldr(immediate). const this_page = @intCast(i32, this_addr >> 12); const target_page = @intCast(i32, target_addr >> 12); - const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); - mem.writeIntLittle(u32, code[0..4], Arm64.adrp(16, pages).toU32()); + const pages = @intCast(i21, target_page - this_page); + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32()); const narrowed = @truncate(u12, target_addr); const offset = try math.divExact(u12, narrowed, 8); - mem.writeIntLittle(u32, code[4..8], Arm64.ldrq(16, 16, offset).toU32()); + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{ + .register = .{ + .rn = .x16, + .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), + }, + }).toU32()); } // br x16 - mem.writeIntLittle(u32, code[8..12], Arm64.br(16).toU32()); + mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32()); }, else => unreachable, } @@ -1160,9 +1178,11 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void { const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4); const literal = @divExact(stub_size - @sizeOf(u32), 4); // ldr w16, literal - mem.writeIntLittle(u32, code[0..4], Arm64.ldr(16, literal, 0).toU32()); + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ + .literal = literal, + }).toU32()); // b disp - mem.writeIntLittle(u32, code[4..8], Arm64.b(displacement).toU32()); + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32()); mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. }, else => unreachable, @@ -1486,9 +1506,18 @@ fn doRelocs(self: *Zld) !void { .ARM64_RELOC_BRANCH26 => { assert(rel.r_length == 2); const inst = code[off..][0..4]; - const displacement = @intCast(i28, @intCast(i64, target_addr) - @intCast(i64, this_addr)); - var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Branch), inst); - parsed.disp = @truncate(u26, @bitCast(u28, displacement) >> 2); + const displacement = @intCast( + i28, + @intCast(i64, target_addr) - @intCast(i64, this_addr), + ); + var parsed = mem.bytesAsValue( + meta.TagPayload( + aarch64.Instruction, + aarch64.Instruction.UnconditionalBranchImmediate, + ), + inst, + ); + parsed.imm26 = @truncate(u26, @bitCast(u28, displacement) >> 2); }, .ARM64_RELOC_PAGE21, .ARM64_RELOC_GOT_LOAD_PAGE21, @@ -1501,7 +1530,13 @@ fn doRelocs(self: *Zld) !void { const target_page = @intCast(i32, ta >> 12); const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); log.debug(" | moving by {} pages", .{pages}); - var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Address), inst); + var parsed = mem.bytesAsValue( + meta.TagPayload( + aarch64.Instruction, + aarch64.Instruction.PCRelativeAddress, + ), + inst, + ); parsed.immhi = @truncate(u19, pages >> 2); parsed.immlo = @truncate(u2, pages); addend = null; @@ -1510,17 +1545,29 @@ fn doRelocs(self: *Zld) !void { .ARM64_RELOC_GOT_LOAD_PAGEOFF12, => { const inst = code[off..][0..4]; - if (Arm64.isArithmetic(inst)) { + if (aarch64IsArithmetic(inst)) { log.debug(" | detected ADD opcode", .{}); // add - var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Add), inst); + var parsed = mem.bytesAsValue( + meta.TagPayload( + aarch64.Instruction, + aarch64.Instruction.AddSubtractImmediate, + ), + inst, + ); const ta = if (addend) |a| target_addr + a else target_addr; const narrowed = @truncate(u12, ta); - parsed.offset = narrowed; + parsed.imm12 = narrowed; } else { log.debug(" | detected LDR/STR opcode", .{}); // ldr/str - var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.LoadRegister), inst); + var parsed = mem.bytesAsValue( + meta.TagPayload( + aarch64.Instruction, + aarch64.Instruction.LoadStoreRegister, + ), + inst, + ); const ta = if (addend) |a| target_addr + a else target_addr; const narrowed = @truncate(u12, ta); const offset: u12 = blk: { @@ -1541,27 +1588,43 @@ fn doRelocs(self: *Zld) !void { addend = null; }, .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => { - // TODO why is this necessary? const RegInfo = struct { - rt: u5, + rd: u5, rn: u5, size: u1, }; const inst = code[off..][0..4]; const parsed: RegInfo = blk: { - if (Arm64.isArithmetic(inst)) { - const curr = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Add), inst); - break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = curr.size }; + if (aarch64IsArithmetic(inst)) { + const curr = mem.bytesAsValue( + meta.TagPayload( + aarch64.Instruction, + aarch64.Instruction.AddSubtractImmediate, + ), + inst, + ); + break :blk .{ .rd = curr.rd, .rn = curr.rn, .size = curr.sf }; } else { - const curr = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.LoadRegister), inst); - break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = @truncate(u1, curr.size) }; + const curr = mem.bytesAsValue( + meta.TagPayload( + aarch64.Instruction, + aarch64.Instruction.LoadStoreRegister, + ), + inst, + ); + break :blk .{ .rd = curr.rt, .rn = curr.rn, .size = @truncate(u1, curr.size) }; } }; const ta = if (addend) |a| target_addr + a else target_addr; const narrowed = @truncate(u12, ta); log.debug(" | rewriting TLV access to ADD opcode", .{}); // For TLV, we always generate an add instruction. - mem.writeIntLittle(u32, inst, Arm64.add(parsed.rt, parsed.rn, narrowed, parsed.size).toU32()); + mem.writeIntLittle(u32, inst, aarch64.Instruction.add( + @intToEnum(aarch64.Register, parsed.rd), + @intToEnum(aarch64.Register, parsed.rn), + narrowed, + false, + ).toU32()); }, .ARM64_RELOC_SUBTRACTOR => { sub = @intCast(i64, target_addr); @@ -2965,3 +3028,8 @@ fn isExtern(sym: *const macho.nlist_64) callconv(.Inline) bool { fn isWeakDef(sym: *const macho.nlist_64) callconv(.Inline) bool { return (sym.n_desc & macho.N_WEAK_DEF) != 0; } + +fn aarch64IsArithmetic(inst: *const [4]u8) callconv(.Inline) bool { + const group_decode = @truncate(u5, inst[3]); + return ((group_decode >> 2) == 4); +} diff --git a/src/link/MachO/reloc.zig b/src/link/MachO/reloc.zig deleted file mode 100644 index d428b191a6..0000000000 --- a/src/link/MachO/reloc.zig +++ /dev/null @@ -1,197 +0,0 @@ -const std = @import("std"); -const log = std.log.scoped(.reloc); - -pub const Arm64 = union(enum) { - Branch: packed struct { - disp: u26, - fixed: u5 = 0b00101, - link: u1, - }, - BranchRegister: packed struct { - _1: u5 = 0b0000_0, - reg: u5, - _2: u11 = 0b1111_1000_000, - link: u1, - _3: u10 = 0b1101_0110_00, - }, - Address: packed struct { - reg: u5, - immhi: u19, - _1: u5 = 0b10000, - immlo: u2, - page: u1, - }, - LoadRegister: packed struct { - rt: u5, - rn: u5, - offset: u12, - opc: u2, - _2: u2 = 0b01, - v: u1, - _1: u3 = 0b111, - size: u2, - }, - LoadLiteral: packed struct { - reg: u5, - literal: u19, - _1: u6 = 0b011_0_00, - size: u1, - _2: u1 = 0b0, - }, - Add: packed struct { - rt: u5, - rn: u5, - offset: u12, - _1: u9 = 0b0_0_100010_0, - size: u1, - }, - Nop: packed struct { - fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111, - }, - - pub fn toU32(self: Arm64) u32 { - const as_u32 = switch (self) { - .Branch => |x| @bitCast(u32, x), - .BranchRegister => |x| @bitCast(u32, x), - .Address => |x| @bitCast(u32, x), - .LoadRegister => |x| @bitCast(u32, x), - .LoadLiteral => |x| @bitCast(u32, x), - .Add => |x| @bitCast(u32, x), - .Nop => |x| @bitCast(u32, x), - }; - return as_u32; - } - - pub fn b(disp: i28) Arm64 { - return Arm64{ - .Branch = .{ - .disp = @truncate(u26, @bitCast(u28, disp) >> 2), - .link = 0, - }, - }; - } - - pub fn bl(disp: i28) Arm64 { - return Arm64{ - .Branch = .{ - .disp = @truncate(u26, @bitCast(u28, disp) >> 2), - .link = 1, - }, - }; - } - - pub fn br(reg: u5) Arm64 { - return Arm64{ - .BranchRegister = .{ - .reg = reg, - .link = 0, - }, - }; - } - - pub fn blr(reg: u5) Arm64 { - return Arm64{ - .BranchRegister = .{ - .reg = reg, - .link = 1, - }, - }; - } - - pub fn adr(reg: u5, disp: u21) Arm64 { - return Arm64{ - .Address = .{ - .reg = reg, - .immhi = @truncate(u19, disp >> 2), - .immlo = @truncate(u2, disp), - .page = 0, - }, - }; - } - - pub fn adrp(reg: u5, disp: u21) Arm64 { - return Arm64{ - .Address = .{ - .reg = reg, - .immhi = @truncate(u19, disp >> 2), - .immlo = @truncate(u2, disp), - .page = 1, - }, - }; - } - - pub fn ldr(reg: u5, literal: u19, size: u1) Arm64 { - return Arm64{ - .LoadLiteral = .{ - .reg = reg, - .literal = literal, - .size = size, - }, - }; - } - - pub fn add(rt: u5, rn: u5, offset: u12, size: u1) Arm64 { - return Arm64{ - .Add = .{ - .rt = rt, - .rn = rn, - .offset = offset, - .size = size, - }, - }; - } - - pub fn ldrq(rt: u5, rn: u5, offset: u12) Arm64 { - return Arm64{ - .LoadRegister = .{ - .rt = rt, - .rn = rn, - .offset = offset, - .opc = 0b01, - .v = 0b0, - .size = 0b11, - }, - }; - } - pub fn ldrh(rt: u5, rn: u5, offset: u12) Arm64 { - return Arm64{ - .LoadRegister = .{ - .rt = rt, - .rn = rn, - .offset = offset, - .opc = 0b01, - .v = 0b0, - .size = 0b01, - }, - }; - } - pub fn ldrb(rt: u5, rn: u5, offset: u12) Arm64 { - return Arm64{ - .LoadRegister = .{ - .rt = rt, - .rn = rn, - .offset = offset, - .opc = 0b01, - .v = 0b0, - .size = 0b00, - }, - }; - } - - pub fn nop() Arm64 { - return Arm64{ - .Nop = .{}, - }; - } - - pub fn isArithmetic(inst: *const [4]u8) bool { - const group_decode = @truncate(u5, inst[3]); - log.debug("{b}", .{group_decode}); - return ((group_decode >> 2) == 4); - // if ((group_decode >> 2) == 4) { - // log.debug("Arithmetic imm", .{}); - // } else if (((group_decode & 0b01010) >> 3) == 1) { - // log.debug("Load/store", .{}); - // } - } -};