| ... | ... | @@ -10,6 +10,7 @@ const fs = std.fs; |
| 10 | 10 | const macho = std.macho; |
| 11 | 11 | const math = std.math; |
| 12 | 12 | const log = std.log.scoped(.zld); |
| 13 | const aarch64 = @import("../../codegen/aarch64.zig"); |
| 13 | 14 | |
| 14 | 15 | const Allocator = mem.Allocator; |
| 15 | 16 | const CodeSignature = @import("CodeSignature.zig"); |
| ... | ... | @@ -19,7 +20,6 @@ const Trie = @import("Trie.zig"); |
| 19 | 20 | |
| 20 | 21 | usingnamespace @import("commands.zig"); |
| 21 | 22 | usingnamespace @import("bind.zig"); |
| 22 | | usingnamespace @import("reloc.zig"); |
| 23 | 23 | |
| 24 | 24 | allocator: *Allocator, |
| 25 | 25 | |
| ... | ... | @@ -968,27 +968,27 @@ fn writeStubHelperCommon(self: *Zld) !void { |
| 968 | 968 | data_blk: { |
| 969 | 969 | const displacement = math.cast(i21, target_addr - this_addr) catch |_| break :data_blk; |
| 970 | 970 | // adr x17, disp |
| 971 | | mem.writeIntLittle(u32, code[0..4], Arm64.adr(17, @bitCast(u21, displacement)).toU32()); |
| 971 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32()); |
| 972 | 972 | // nop |
| 973 | | mem.writeIntLittle(u32, code[4..8], Arm64.nop().toU32()); |
| 973 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); |
| 974 | 974 | break :data_blk_outer; |
| 975 | 975 | } |
| 976 | 976 | data_blk: { |
| 977 | 977 | const new_this_addr = this_addr + @sizeOf(u32); |
| 978 | 978 | const displacement = math.cast(i21, target_addr - new_this_addr) catch |_| break :data_blk; |
| 979 | 979 | // nop |
| 980 | | mem.writeIntLittle(u32, code[0..4], Arm64.nop().toU32()); |
| 980 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); |
| 981 | 981 | // adr x17, disp |
| 982 | | mem.writeIntLittle(u32, code[4..8], Arm64.adr(17, @bitCast(u21, displacement)).toU32()); |
| 982 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32()); |
| 983 | 983 | break :data_blk_outer; |
| 984 | 984 | } |
| 985 | 985 | // Jump is too big, replace adr with adrp and add. |
| 986 | 986 | const this_page = @intCast(i32, this_addr >> 12); |
| 987 | 987 | const target_page = @intCast(i32, target_addr >> 12); |
| 988 | | const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); |
| 989 | | mem.writeIntLittle(u32, code[0..4], Arm64.adrp(17, pages).toU32()); |
| 988 | const pages = @intCast(i21, target_page - this_page); |
| 989 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32()); |
| 990 | 990 | const narrowed = @truncate(u12, target_addr); |
| 991 | | mem.writeIntLittle(u32, code[4..8], Arm64.add(17, 17, narrowed, 1).toU32()); |
| 991 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32()); |
| 992 | 992 | } |
| 993 | 993 | // stp x16, x17, [sp, #-16]! |
| 994 | 994 | code[8] = 0xf0; |
| ... | ... | @@ -1003,9 +1003,11 @@ fn writeStubHelperCommon(self: *Zld) !void { |
| 1003 | 1003 | const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :binder_blk; |
| 1004 | 1004 | const literal = math.cast(u18, displacement) catch |_| break :binder_blk; |
| 1005 | 1005 | // ldr x16, label |
| 1006 | | mem.writeIntLittle(u32, code[12..16], Arm64.ldr(16, literal, 1).toU32()); |
| 1006 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{ |
| 1007 | .literal = literal, |
| 1008 | }).toU32()); |
| 1007 | 1009 | // nop |
| 1008 | | mem.writeIntLittle(u32, code[16..20], Arm64.nop().toU32()); |
| 1010 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32()); |
| 1009 | 1011 | break :binder_blk_outer; |
| 1010 | 1012 | } |
| 1011 | 1013 | binder_blk: { |
| ... | ... | @@ -1015,19 +1017,26 @@ fn writeStubHelperCommon(self: *Zld) !void { |
| 1015 | 1017 | log.debug("2: disp=0x{x}, literal=0x{x}", .{ displacement, literal }); |
| 1016 | 1018 | // Pad with nop to please division. |
| 1017 | 1019 | // nop |
| 1018 | | mem.writeIntLittle(u32, code[12..16], Arm64.nop().toU32()); |
| 1020 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32()); |
| 1019 | 1021 | // ldr x16, label |
| 1020 | | mem.writeIntLittle(u32, code[16..20], Arm64.ldr(16, literal, 1).toU32()); |
| 1022 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ |
| 1023 | .literal = literal, |
| 1024 | }).toU32()); |
| 1021 | 1025 | break :binder_blk_outer; |
| 1022 | 1026 | } |
| 1023 | 1027 | // Use adrp followed by ldr(immediate). |
| 1024 | 1028 | const this_page = @intCast(i32, this_addr >> 12); |
| 1025 | 1029 | const target_page = @intCast(i32, target_addr >> 12); |
| 1026 | | const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); |
| 1027 | | mem.writeIntLittle(u32, code[12..16], Arm64.adrp(16, pages).toU32()); |
| 1030 | const pages = @intCast(i21, target_page - this_page); |
| 1031 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32()); |
| 1028 | 1032 | const narrowed = @truncate(u12, target_addr); |
| 1029 | 1033 | const offset = try math.divExact(u12, narrowed, 8); |
| 1030 | | mem.writeIntLittle(u32, code[16..20], Arm64.ldrq(16, 16, offset).toU32()); |
| 1034 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ |
| 1035 | .register = .{ |
| 1036 | .rn = .x16, |
| 1037 | .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), |
| 1038 | }, |
| 1039 | }).toU32()); |
| 1031 | 1040 | } |
| 1032 | 1041 | // br x16 |
| 1033 | 1042 | code[20] = 0x00; |
| ... | ... | @@ -1099,9 +1108,11 @@ fn writeStub(self: *Zld, index: u32) !void { |
| 1099 | 1108 | const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :inner; |
| 1100 | 1109 | const literal = math.cast(u18, displacement) catch |_| break :inner; |
| 1101 | 1110 | // ldr x16, literal |
| 1102 | | mem.writeIntLittle(u32, code[0..4], Arm64.ldr(16, literal, 1).toU32()); |
| 1111 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ |
| 1112 | .literal = literal, |
| 1113 | }).toU32()); |
| 1103 | 1114 | // nop |
| 1104 | | mem.writeIntLittle(u32, code[4..8], Arm64.nop().toU32()); |
| 1115 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); |
| 1105 | 1116 | break :outer; |
| 1106 | 1117 | } |
| 1107 | 1118 | inner: { |
| ... | ... | @@ -1109,22 +1120,29 @@ fn writeStub(self: *Zld, index: u32) !void { |
| 1109 | 1120 | const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :inner; |
| 1110 | 1121 | const literal = math.cast(u18, displacement) catch |_| break :inner; |
| 1111 | 1122 | // nop |
| 1112 | | mem.writeIntLittle(u32, code[0..4], Arm64.nop().toU32()); |
| 1123 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); |
| 1113 | 1124 | // ldr x16, literal |
| 1114 | | mem.writeIntLittle(u32, code[4..8], Arm64.ldr(16, literal, 1).toU32()); |
| 1125 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{ |
| 1126 | .literal = literal, |
| 1127 | }).toU32()); |
| 1115 | 1128 | break :outer; |
| 1116 | 1129 | } |
| 1117 | 1130 | // Use adrp followed by ldr(immediate). |
| 1118 | 1131 | const this_page = @intCast(i32, this_addr >> 12); |
| 1119 | 1132 | const target_page = @intCast(i32, target_addr >> 12); |
| 1120 | | const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); |
| 1121 | | mem.writeIntLittle(u32, code[0..4], Arm64.adrp(16, pages).toU32()); |
| 1133 | const pages = @intCast(i21, target_page - this_page); |
| 1134 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32()); |
| 1122 | 1135 | const narrowed = @truncate(u12, target_addr); |
| 1123 | 1136 | const offset = try math.divExact(u12, narrowed, 8); |
| 1124 | | mem.writeIntLittle(u32, code[4..8], Arm64.ldrq(16, 16, offset).toU32()); |
| 1137 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{ |
| 1138 | .register = .{ |
| 1139 | .rn = .x16, |
| 1140 | .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), |
| 1141 | }, |
| 1142 | }).toU32()); |
| 1125 | 1143 | } |
| 1126 | 1144 | // br x16 |
| 1127 | | mem.writeIntLittle(u32, code[8..12], Arm64.br(16).toU32()); |
| 1145 | mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32()); |
| 1128 | 1146 | }, |
| 1129 | 1147 | else => unreachable, |
| 1130 | 1148 | } |
| ... | ... | @@ -1160,9 +1178,11 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void { |
| 1160 | 1178 | const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4); |
| 1161 | 1179 | const literal = @divExact(stub_size - @sizeOf(u32), 4); |
| 1162 | 1180 | // ldr w16, literal |
| 1163 | | mem.writeIntLittle(u32, code[0..4], Arm64.ldr(16, literal, 0).toU32()); |
| 1181 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ |
| 1182 | .literal = literal, |
| 1183 | }).toU32()); |
| 1164 | 1184 | // b disp |
| 1165 | | mem.writeIntLittle(u32, code[4..8], Arm64.b(displacement).toU32()); |
| 1185 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32()); |
| 1166 | 1186 | mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. |
| 1167 | 1187 | }, |
| 1168 | 1188 | else => unreachable, |
| ... | ... | @@ -1486,9 +1506,18 @@ fn doRelocs(self: *Zld) !void { |
| 1486 | 1506 | .ARM64_RELOC_BRANCH26 => { |
| 1487 | 1507 | assert(rel.r_length == 2); |
| 1488 | 1508 | const inst = code[off..][0..4]; |
| 1489 | | const displacement = @intCast(i28, @intCast(i64, target_addr) - @intCast(i64, this_addr)); |
| 1490 | | var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Branch), inst); |
| 1491 | | parsed.disp = @truncate(u26, @bitCast(u28, displacement) >> 2); |
| 1509 | const displacement = @intCast( |
| 1510 | i28, |
| 1511 | @intCast(i64, target_addr) - @intCast(i64, this_addr), |
| 1512 | ); |
| 1513 | var parsed = mem.bytesAsValue( |
| 1514 | meta.TagPayload( |
| 1515 | aarch64.Instruction, |
| 1516 | aarch64.Instruction.UnconditionalBranchImmediate, |
| 1517 | ), |
| 1518 | inst, |
| 1519 | ); |
| 1520 | parsed.imm26 = @truncate(u26, @bitCast(u28, displacement) >> 2); |
| 1492 | 1521 | }, |
| 1493 | 1522 | .ARM64_RELOC_PAGE21, |
| 1494 | 1523 | .ARM64_RELOC_GOT_LOAD_PAGE21, |
| ... | ... | @@ -1501,7 +1530,13 @@ fn doRelocs(self: *Zld) !void { |
| 1501 | 1530 | const target_page = @intCast(i32, ta >> 12); |
| 1502 | 1531 | const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); |
| 1503 | 1532 | log.debug(" | moving by {} pages", .{pages}); |
| 1504 | | var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Address), inst); |
| 1533 | var parsed = mem.bytesAsValue( |
| 1534 | meta.TagPayload( |
| 1535 | aarch64.Instruction, |
| 1536 | aarch64.Instruction.PCRelativeAddress, |
| 1537 | ), |
| 1538 | inst, |
| 1539 | ); |
| 1505 | 1540 | parsed.immhi = @truncate(u19, pages >> 2); |
| 1506 | 1541 | parsed.immlo = @truncate(u2, pages); |
| 1507 | 1542 | addend = null; |
| ... | ... | @@ -1510,17 +1545,29 @@ fn doRelocs(self: *Zld) !void { |
| 1510 | 1545 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, |
| 1511 | 1546 | => { |
| 1512 | 1547 | const inst = code[off..][0..4]; |
| 1513 | | if (Arm64.isArithmetic(inst)) { |
| 1548 | if (aarch64IsArithmetic(inst)) { |
| 1514 | 1549 | log.debug(" | detected ADD opcode", .{}); |
| 1515 | 1550 | // add |
| 1516 | | var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Add), inst); |
| 1551 | var parsed = mem.bytesAsValue( |
| 1552 | meta.TagPayload( |
| 1553 | aarch64.Instruction, |
| 1554 | aarch64.Instruction.AddSubtractImmediate, |
| 1555 | ), |
| 1556 | inst, |
| 1557 | ); |
| 1517 | 1558 | const ta = if (addend) |a| target_addr + a else target_addr; |
| 1518 | 1559 | const narrowed = @truncate(u12, ta); |
| 1519 | | parsed.offset = narrowed; |
| 1560 | parsed.imm12 = narrowed; |
| 1520 | 1561 | } else { |
| 1521 | 1562 | log.debug(" | detected LDR/STR opcode", .{}); |
| 1522 | 1563 | // ldr/str |
| 1523 | | var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.LoadRegister), inst); |
| 1564 | var parsed = mem.bytesAsValue( |
| 1565 | meta.TagPayload( |
| 1566 | aarch64.Instruction, |
| 1567 | aarch64.Instruction.LoadStoreRegister, |
| 1568 | ), |
| 1569 | inst, |
| 1570 | ); |
| 1524 | 1571 | const ta = if (addend) |a| target_addr + a else target_addr; |
| 1525 | 1572 | const narrowed = @truncate(u12, ta); |
| 1526 | 1573 | const offset: u12 = blk: { |
| ... | ... | @@ -1541,27 +1588,43 @@ fn doRelocs(self: *Zld) !void { |
| 1541 | 1588 | addend = null; |
| 1542 | 1589 | }, |
| 1543 | 1590 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => { |
| 1544 | | // TODO why is this necessary? |
| 1545 | 1591 | const RegInfo = struct { |
| 1546 | | rt: u5, |
| 1592 | rd: u5, |
| 1547 | 1593 | rn: u5, |
| 1548 | 1594 | size: u1, |
| 1549 | 1595 | }; |
| 1550 | 1596 | const inst = code[off..][0..4]; |
| 1551 | 1597 | const parsed: RegInfo = blk: { |
| 1552 | | if (Arm64.isArithmetic(inst)) { |
| 1553 | | const curr = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Add), inst); |
| 1554 | | break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = curr.size }; |
| 1598 | if (aarch64IsArithmetic(inst)) { |
| 1599 | const curr = mem.bytesAsValue( |
| 1600 | meta.TagPayload( |
| 1601 | aarch64.Instruction, |
| 1602 | aarch64.Instruction.AddSubtractImmediate, |
| 1603 | ), |
| 1604 | inst, |
| 1605 | ); |
| 1606 | break :blk .{ .rd = curr.rd, .rn = curr.rn, .size = curr.sf }; |
| 1555 | 1607 | } else { |
| 1556 | | const curr = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.LoadRegister), inst); |
| 1557 | | break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = @truncate(u1, curr.size) }; |
| 1608 | const curr = mem.bytesAsValue( |
| 1609 | meta.TagPayload( |
| 1610 | aarch64.Instruction, |
| 1611 | aarch64.Instruction.LoadStoreRegister, |
| 1612 | ), |
| 1613 | inst, |
| 1614 | ); |
| 1615 | break :blk .{ .rd = curr.rt, .rn = curr.rn, .size = @truncate(u1, curr.size) }; |
| 1558 | 1616 | } |
| 1559 | 1617 | }; |
| 1560 | 1618 | const ta = if (addend) |a| target_addr + a else target_addr; |
| 1561 | 1619 | const narrowed = @truncate(u12, ta); |
| 1562 | 1620 | log.debug(" | rewriting TLV access to ADD opcode", .{}); |
| 1563 | 1621 | // For TLV, we always generate an add instruction. |
| 1564 | | mem.writeIntLittle(u32, inst, Arm64.add(parsed.rt, parsed.rn, narrowed, parsed.size).toU32()); |
| 1622 | mem.writeIntLittle(u32, inst, aarch64.Instruction.add( |
| 1623 | @intToEnum(aarch64.Register, parsed.rd), |
| 1624 | @intToEnum(aarch64.Register, parsed.rn), |
| 1625 | narrowed, |
| 1626 | false, |
| 1627 | ).toU32()); |
| 1565 | 1628 | }, |
| 1566 | 1629 | .ARM64_RELOC_SUBTRACTOR => { |
| 1567 | 1630 | sub = @intCast(i64, target_addr); |
| ... | ... | @@ -2965,3 +3028,8 @@ fn isExtern(sym: *const macho.nlist_64) callconv(.Inline) bool { |
| 2965 | 3028 | fn isWeakDef(sym: *const macho.nlist_64) callconv(.Inline) bool { |
| 2966 | 3029 | return (sym.n_desc & macho.N_WEAK_DEF) != 0; |
| 2967 | 3030 | } |
| 3031 | |
| 3032 | fn aarch64IsArithmetic(inst: *const [4]u8) callconv(.Inline) bool { |
| 3033 | const group_decode = @truncate(u5, inst[3]); |
| 3034 | return ((group_decode >> 2) == 4); |
| 3035 | } |