| author | |
| committer | |
| log | 1119970d22f67112159682ab103c767088cbf4b8 |
| tree | 8c120994ad987f0d62de5a58b86a70a4f82f07f2 |
| parent | b1f9db75f3380c9a563972a197c5d1c363c6f215 |
6 files changed, 1056 insertions(+), 658 deletions(-)
CMakeLists.txt+2| ... | @@ -575,6 +575,8 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -575,6 +575,8 @@ set(ZIG_STAGE2_SOURCES |
| 575 | "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig" | 575 | "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig" |
| 576 | "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig" | 576 | "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig" |
| 577 | "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc.zig" | 577 | "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc.zig" |
| 578 | "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc/aarch64.zig" | ||
| 579 | "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc/x86_64.zig" | ||
| 578 | "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig" | 580 | "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig" |
| 579 | "${CMAKE_SOURCE_DIR}/src/link/C/zig.h" | 581 | "${CMAKE_SOURCE_DIR}/src/link/C/zig.h" |
| 580 | "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin" | 582 | "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin" |
src/link/MachO/Object.zig+1| ... | @@ -310,6 +310,7 @@ pub fn parseSections(self: *Object) !void { | ... | @@ -310,6 +310,7 @@ pub fn parseSections(self: *Object) !void { |
| 310 | 310 | ||
| 311 | break :relocs try reloc.parse( | 311 | break :relocs try reloc.parse( |
| 312 | self.allocator, | 312 | self.allocator, |
| 313 | self.arch.?, | ||
| 313 | section.code, | 314 | section.code, |
| 314 | mem.bytesAsSlice(macho.relocation_info, raw_relocs), | 315 | mem.bytesAsSlice(macho.relocation_info, raw_relocs), |
| 315 | ); | 316 | ); |
src/link/MachO/Zld.zig+21-12| ... | @@ -10,12 +10,12 @@ const macho = std.macho; | ... | @@ -10,12 +10,12 @@ const macho = std.macho; |
| 10 | const math = std.math; | 10 | const math = std.math; |
| 11 | const log = std.log.scoped(.zld); | 11 | const log = std.log.scoped(.zld); |
| 12 | const aarch64 = @import("../../codegen/aarch64.zig"); | 12 | const aarch64 = @import("../../codegen/aarch64.zig"); |
| 13 | const reloc = @import("reloc.zig"); | ||
| 13 | 14 | ||
| 14 | const Allocator = mem.Allocator; | 15 | const Allocator = mem.Allocator; |
| 15 | const Archive = @import("Archive.zig"); | 16 | const Archive = @import("Archive.zig"); |
| 16 | const CodeSignature = @import("CodeSignature.zig"); | 17 | const CodeSignature = @import("CodeSignature.zig"); |
| 17 | const Object = @import("Object.zig"); | 18 | const Object = @import("Object.zig"); |
| 18 | const Relocation = @import("reloc.zig").Relocation; | ||
| 19 | const Symbol = @import("Symbol.zig"); | 19 | const Symbol = @import("Symbol.zig"); |
| 20 | const Trie = @import("Trie.zig"); | 20 | const Trie = @import("Trie.zig"); |
| 21 | 21 | ||
| ... | @@ -1360,11 +1360,11 @@ fn resolveStubsAndGotEntries(self: *Zld) !void { | ... | @@ -1360,11 +1360,11 @@ fn resolveStubsAndGotEntries(self: *Zld) !void { |
| 1360 | 1360 | ||
| 1361 | for (object.sections.items) |sect| { | 1361 | for (object.sections.items) |sect| { |
| 1362 | const relocs = sect.relocs orelse continue; | 1362 | const relocs = sect.relocs orelse continue; |
| 1363 | for (relocs) |reloc| { | 1363 | for (relocs) |rel| { |
| 1364 | switch (reloc.@"type") { | 1364 | switch (rel.@"type") { |
| 1365 | .unsigned => continue, | 1365 | .unsigned => continue, |
| 1366 | .got_page, .got_page_off => { | 1366 | .got_page, .got_page_off, .got_load, .got => { |
| 1367 | const sym = object.symtab.items[reloc.target.symbol]; | 1367 | const sym = object.symtab.items[rel.target.symbol]; |
| 1368 | const sym_name = object.getString(sym.n_strx); | 1368 | const sym_name = object.getString(sym.n_strx); |
| 1369 | 1369 | ||
| 1370 | if (self.got_entries.contains(sym_name)) continue; | 1370 | if (self.got_entries.contains(sym_name)) continue; |
| ... | @@ -1383,7 +1383,9 @@ fn resolveStubsAndGotEntries(self: *Zld) !void { | ... | @@ -1383,7 +1383,9 @@ fn resolveStubsAndGotEntries(self: *Zld) !void { |
| 1383 | log.debug(" | found GOT entry {s}: {}", .{ sym_name, self.got_entries.get(sym_name) }); | 1383 | log.debug(" | found GOT entry {s}: {}", .{ sym_name, self.got_entries.get(sym_name) }); |
| 1384 | }, | 1384 | }, |
| 1385 | else => { | 1385 | else => { |
| 1386 | const sym = object.symtab.items[reloc.target.symbol]; | 1386 | if (rel.target != .symbol) continue; |
| 1387 | |||
| 1388 | const sym = object.symtab.items[rel.target.symbol]; | ||
| 1387 | const sym_name = object.getString(sym.n_strx); | 1389 | const sym_name = object.getString(sym.n_strx); |
| 1388 | 1390 | ||
| 1389 | if (!Symbol.isUndef(sym)) continue; | 1391 | if (!Symbol.isUndef(sym)) continue; |
| ... | @@ -1442,20 +1444,23 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { | ... | @@ -1442,20 +1444,23 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { |
| 1442 | for (relocs) |rel| { | 1444 | for (relocs) |rel| { |
| 1443 | const source_addr = target_sect_addr + rel.offset; | 1445 | const source_addr = target_sect_addr + rel.offset; |
| 1444 | 1446 | ||
| 1445 | var args: Relocation.ResolveArgs = .{ | 1447 | var args: reloc.Relocation.ResolveArgs = .{ |
| 1446 | .source_addr = source_addr, | 1448 | .source_addr = source_addr, |
| 1447 | .target_addr = undefined, | 1449 | .target_addr = undefined, |
| 1448 | .subtractor = null, | ||
| 1449 | }; | 1450 | }; |
| 1450 | 1451 | ||
| 1451 | switch (rel.@"type") { | 1452 | switch (rel.@"type") { |
| 1452 | .unsigned => { | 1453 | .unsigned => { |
| 1453 | args.target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel.target); | 1454 | args.target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel.target); |
| 1454 | 1455 | ||
| 1455 | const unsigned = rel.cast(Relocation.Unsigned) orelse unreachable; | 1456 | const unsigned = rel.cast(reloc.Unsigned) orelse unreachable; |
| 1456 | if (unsigned.subtractor) |subtractor| { | 1457 | if (unsigned.subtractor) |subtractor| { |
| 1457 | args.subtractor = try self.relocTargetAddr(@intCast(u16, object_id), subtractor); | 1458 | args.subtractor = try self.relocTargetAddr(@intCast(u16, object_id), subtractor); |
| 1458 | } | 1459 | } |
| 1460 | if (rel.target == .section) { | ||
| 1461 | const source_sect = object.sections.items[rel.target.section]; | ||
| 1462 | args.source_sect_addr = source_sect.inner.addr; | ||
| 1463 | } | ||
| 1459 | 1464 | ||
| 1460 | rebases: { | 1465 | rebases: { |
| 1461 | var hit: bool = false; | 1466 | var hit: bool = false; |
| ... | @@ -1500,7 +1505,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { | ... | @@ -1500,7 +1505,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { |
| 1500 | try self.threadlocal_offsets.append(self.allocator, args.target_addr - base_addr); | 1505 | try self.threadlocal_offsets.append(self.allocator, args.target_addr - base_addr); |
| 1501 | } | 1506 | } |
| 1502 | }, | 1507 | }, |
| 1503 | .got_page, .got_page_off => { | 1508 | .got_page, .got_page_off, .got_load, .got => { |
| 1504 | const dc_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | 1509 | const dc_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 1505 | const got = dc_seg.sections.items[self.got_section_index.?]; | 1510 | const got = dc_seg.sections.items[self.got_section_index.?]; |
| 1506 | const sym = object.symtab.items[rel.target.symbol]; | 1511 | const sym = object.symtab.items[rel.target.symbol]; |
| ... | @@ -1508,7 +1513,11 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { | ... | @@ -1508,7 +1513,11 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { |
| 1508 | const entry = self.got_entries.get(sym_name) orelse unreachable; | 1513 | const entry = self.got_entries.get(sym_name) orelse unreachable; |
| 1509 | args.target_addr = got.addr + entry.index * @sizeOf(u64); | 1514 | args.target_addr = got.addr + entry.index * @sizeOf(u64); |
| 1510 | }, | 1515 | }, |
| 1511 | else => { | 1516 | else => |tt| { |
| 1517 | if (tt == .signed and rel.target == .section) { | ||
| 1518 | const source_sect = object.sections.items[rel.target.section]; | ||
| 1519 | args.source_sect_addr = source_sect.inner.addr; | ||
| 1520 | } | ||
| 1512 | args.target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel.target); | 1521 | args.target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel.target); |
| 1513 | }, | 1522 | }, |
| 1514 | } | 1523 | } |
| ... | @@ -1547,7 +1556,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { | ... | @@ -1547,7 +1556,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { |
| 1547 | } | 1556 | } |
| 1548 | } | 1557 | } |
| 1549 | 1558 | ||
| 1550 | fn relocTargetAddr(self: *Zld, object_id: u16, target: Relocation.Target) !u64 { | 1559 | fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target) !u64 { |
| 1551 | const object = self.objects.items[object_id]; | 1560 | const object = self.objects.items[object_id]; |
| 1552 | const target_addr = blk: { | 1561 | const target_addr = blk: { |
| 1553 | switch (target) { | 1562 | switch (target) { |
src/link/MachO/reloc.zig+102-646| ... | @@ -1,5 +1,4 @@ | ... | @@ -1,5 +1,4 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const aarch64 = @import("../../codegen/aarch64.zig"); | ||
| 3 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 4 | const log = std.log.scoped(.reloc); | 3 | const log = std.log.scoped(.reloc); |
| 5 | const macho = std.macho; | 4 | const macho = std.macho; |
| ... | @@ -7,6 +6,9 @@ const math = std.math; | ... | @@ -7,6 +6,9 @@ const math = std.math; |
| 7 | const mem = std.mem; | 6 | const mem = std.mem; |
| 8 | const meta = std.meta; | 7 | const meta = std.meta; |
| 9 | 8 | ||
| 9 | const aarch64 = @import("reloc/aarch64.zig"); | ||
| 10 | const x86_64 = @import("reloc/x86_64.zig"); | ||
| 11 | |||
| 10 | const Allocator = mem.Allocator; | 12 | const Allocator = mem.Allocator; |
| 11 | 13 | ||
| 12 | pub const Relocation = struct { | 14 | pub const Relocation = struct { |
| ... | @@ -25,7 +27,8 @@ pub const Relocation = struct { | ... | @@ -25,7 +27,8 @@ pub const Relocation = struct { |
| 25 | pub const ResolveArgs = struct { | 27 | pub const ResolveArgs = struct { |
| 26 | source_addr: u64, | 28 | source_addr: u64, |
| 27 | target_addr: u64, | 29 | target_addr: u64, |
| 28 | subtractor: ?u64, | 30 | subtractor: ?u64 = null, |
| 31 | source_sect_addr: ?u64 = null, | ||
| 29 | }; | 32 | }; |
| 30 | 33 | ||
| 31 | pub fn resolve(base: *Relocation, args: ResolveArgs) !void { | 34 | pub fn resolve(base: *Relocation, args: ResolveArgs) !void { |
| ... | @@ -35,21 +38,28 @@ pub const Relocation = struct { | ... | @@ -35,21 +38,28 @@ pub const Relocation = struct { |
| 35 | log.debug(" | target address 0x{x}", .{args.target_addr}); | 38 | log.debug(" | target address 0x{x}", .{args.target_addr}); |
| 36 | if (args.subtractor) |sub| | 39 | if (args.subtractor) |sub| |
| 37 | log.debug(" | subtractor address 0x{x}", .{sub}); | 40 | log.debug(" | subtractor address 0x{x}", .{sub}); |
| 41 | if (args.source_sect_addr) |addr| | ||
| 42 | log.debug(" | source section address 0x{x}", .{addr}); | ||
| 38 | 43 | ||
| 39 | return switch (base.@"type") { | 44 | return switch (base.@"type") { |
| 40 | .branch => @fieldParentPtr(Branch, "base", base).resolve(args.source_addr, args.target_addr), | 45 | .unsigned => @fieldParentPtr(Unsigned, "base", base).resolve(args), |
| 41 | .unsigned => @fieldParentPtr(Unsigned, "base", base).resolve(args.target_addr, args.subtractor), | 46 | .branch_aarch64 => @fieldParentPtr(aarch64.Branch, "base", base).resolve(args), |
| 42 | .page => @fieldParentPtr(Page, "base", base).resolve(args.source_addr, args.target_addr), | 47 | .page => @fieldParentPtr(aarch64.Page, "base", base).resolve(args), |
| 43 | .page_off => @fieldParentPtr(PageOff, "base", base).resolve(args.target_addr), | 48 | .page_off => @fieldParentPtr(aarch64.PageOff, "base", base).resolve(args), |
| 44 | .got_page => @fieldParentPtr(GotPage, "base", base).resolve(args.source_addr, args.target_addr), | 49 | .got_page => @fieldParentPtr(aarch64.GotPage, "base", base).resolve(args), |
| 45 | .got_page_off => @fieldParentPtr(GotPageOff, "base", base).resolve(args.target_addr), | 50 | .got_page_off => @fieldParentPtr(aarch64.GotPageOff, "base", base).resolve(args), |
| 46 | .tlvp_page => @fieldParentPtr(TlvpPage, "base", base).resolve(args.source_addr, args.target_addr), | 51 | .tlvp_page => @fieldParentPtr(aarch64.TlvpPage, "base", base).resolve(args), |
| 47 | .tlvp_page_off => @fieldParentPtr(TlvpPageOff, "base", base).resolve(args.target_addr), | 52 | .tlvp_page_off => @fieldParentPtr(aarch64.TlvpPageOff, "base", base).resolve(args), |
| 53 | .branch_x86_64 => @fieldParentPtr(x86_64.Branch, "base", base).resolve(args), | ||
| 54 | .signed => @fieldParentPtr(x86_64.Signed, "base", base).resolve(args), | ||
| 55 | .got_load => @fieldParentPtr(x86_64.GotLoad, "base", base).resolve(args), | ||
| 56 | .got => @fieldParentPtr(x86_64.Got, "base", base).resolve(args), | ||
| 57 | .tlv => @fieldParentPtr(x86_64.Tlv, "base", base).resolve(args), | ||
| 48 | }; | 58 | }; |
| 49 | } | 59 | } |
| 50 | 60 | ||
| 51 | pub const Type = enum { | 61 | pub const Type = enum { |
| 52 | branch, | 62 | branch_aarch64, |
| 53 | unsigned, | 63 | unsigned, |
| 54 | page, | 64 | page, |
| 55 | page_off, | 65 | page_off, |
| ... | @@ -57,6 +67,11 @@ pub const Relocation = struct { | ... | @@ -57,6 +67,11 @@ pub const Relocation = struct { |
| 57 | got_page_off, | 67 | got_page_off, |
| 58 | tlvp_page, | 68 | tlvp_page, |
| 59 | tlvp_page_off, | 69 | tlvp_page_off, |
| 70 | branch_x86_64, | ||
| 71 | signed, | ||
| 72 | got_load, | ||
| 73 | got, | ||
| 74 | tlv, | ||
| 60 | }; | 75 | }; |
| 61 | 76 | ||
| 62 | pub const Target = union(enum) { | 77 | pub const Target = union(enum) { |
| ... | @@ -71,236 +86,91 @@ pub const Relocation = struct { | ... | @@ -71,236 +86,91 @@ pub const Relocation = struct { |
| 71 | }; | 86 | }; |
| 72 | } | 87 | } |
| 73 | }; | 88 | }; |
| 89 | }; | ||
| 74 | 90 | ||
| 75 | pub const Branch = struct { | 91 | pub const Unsigned = struct { |
| 76 | base: Relocation, | 92 | base: Relocation, |
| 77 | /// Always .UnconditionalBranchImmediate | 93 | subtractor: ?Relocation.Target = null, |
| 78 | inst: aarch64.Instruction, | 94 | /// Addend embedded directly in the relocation slot |
| 79 | 95 | addend: i64, | |
| 80 | pub const base_type: Relocation.Type = .branch; | 96 | /// Extracted from r_length: |
| 81 | 97 | /// => 3 implies true | |
| 82 | pub fn resolve(branch: Branch, source_addr: u64, target_addr: u64) !void { | 98 | /// => 2 implies false |
| 83 | const displacement = try math.cast(i28, @intCast(i64, target_addr) - @intCast(i64, source_addr)); | 99 | /// => * is unreachable |
| 84 | 100 | is_64bit: bool, | |
| 85 | log.debug(" | displacement 0x{x}", .{displacement}); | 101 | |
| 86 | 102 | pub const base_type: Relocation.Type = .unsigned; | |
| 87 | var inst = branch.inst; | 103 | |
| 88 | inst.UnconditionalBranchImmediate.imm26 = @truncate(u26, @bitCast(u28, displacement) >> 2); | 104 | pub fn resolve(unsigned: Unsigned, args: Relocation.ResolveArgs) !void { |
| 89 | mem.writeIntLittle(u32, branch.base.code[0..4], inst.toU32()); | 105 | const addend = if (unsigned.base.target == .section) |
| 90 | } | 106 | unsigned.addend - @intCast(i64, args.source_sect_addr.?) |
| 91 | }; | 107 | else |
| 92 | 108 | unsigned.addend; | |
| 93 | pub const Unsigned = struct { | ||
| 94 | base: Relocation, | ||
| 95 | subtractor: ?Target = null, | ||
| 96 | /// Addend embedded directly in the relocation slot | ||
| 97 | addend: i64, | ||
| 98 | /// Extracted from r_length: | ||
| 99 | /// => 3 implies true | ||
| 100 | /// => 2 implies false | ||
| 101 | /// => * is unreachable | ||
| 102 | is_64bit: bool, | ||
| 103 | |||
| 104 | pub const base_type: Relocation.Type = .unsigned; | ||
| 105 | |||
| 106 | pub fn resolve(unsigned: Unsigned, target_addr: u64, subtractor: ?u64) !void { | ||
| 107 | const result = if (subtractor) |sub| | ||
| 108 | @intCast(i64, target_addr) - @intCast(i64, sub) + unsigned.addend | ||
| 109 | else | ||
| 110 | @intCast(i64, target_addr) + unsigned.addend; | ||
| 111 | |||
| 112 | log.debug(" | calculated addend 0x{x}", .{unsigned.addend}); | ||
| 113 | log.debug(" | calculated unsigned value 0x{x}", .{result}); | ||
| 114 | |||
| 115 | if (unsigned.is_64bit) { | ||
| 116 | mem.writeIntLittle( | ||
| 117 | u64, | ||
| 118 | unsigned.base.code[0..8], | ||
| 119 | @bitCast(u64, result), | ||
| 120 | ); | ||
| 121 | } else { | ||
| 122 | mem.writeIntLittle( | ||
| 123 | u32, | ||
| 124 | unsigned.base.code[0..4], | ||
| 125 | @truncate(u32, @bitCast(u64, result)), | ||
| 126 | ); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | }; | ||
| 130 | |||
| 131 | pub const Page = struct { | ||
| 132 | base: Relocation, | ||
| 133 | addend: ?u32 = null, | ||
| 134 | /// Always .PCRelativeAddress | ||
| 135 | inst: aarch64.Instruction, | ||
| 136 | |||
| 137 | pub const base_type: Relocation.Type = .page; | ||
| 138 | |||
| 139 | pub fn resolve(page: Page, source_addr: u64, target_addr: u64) !void { | ||
| 140 | const ta = if (page.addend) |a| target_addr + a else target_addr; | ||
| 141 | const source_page = @intCast(i32, source_addr >> 12); | ||
| 142 | const target_page = @intCast(i32, ta >> 12); | ||
| 143 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | ||
| 144 | |||
| 145 | log.debug(" | calculated addend 0x{x}", .{page.addend}); | ||
| 146 | log.debug(" | moving by {} pages", .{pages}); | ||
| 147 | |||
| 148 | var inst = page.inst; | ||
| 149 | inst.PCRelativeAddress.immhi = @truncate(u19, pages >> 2); | ||
| 150 | inst.PCRelativeAddress.immlo = @truncate(u2, pages); | ||
| 151 | |||
| 152 | mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | ||
| 153 | } | ||
| 154 | }; | ||
| 155 | |||
| 156 | pub const PageOff = struct { | ||
| 157 | base: Relocation, | ||
| 158 | addend: ?u32 = null, | ||
| 159 | op_kind: OpKind, | ||
| 160 | inst: aarch64.Instruction, | ||
| 161 | |||
| 162 | pub const base_type: Relocation.Type = .page_off; | ||
| 163 | |||
| 164 | pub const OpKind = enum { | ||
| 165 | arithmetic, | ||
| 166 | load_store, | ||
| 167 | }; | ||
| 168 | |||
| 169 | pub fn resolve(page_off: PageOff, target_addr: u64) !void { | ||
| 170 | const ta = if (page_off.addend) |a| target_addr + a else target_addr; | ||
| 171 | const narrowed = @truncate(u12, ta); | ||
| 172 | |||
| 173 | log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | ||
| 174 | log.debug(" | {s} opcode", .{page_off.op_kind}); | ||
| 175 | |||
| 176 | var inst = page_off.inst; | ||
| 177 | if (page_off.op_kind == .arithmetic) { | ||
| 178 | inst.AddSubtractImmediate.imm12 = narrowed; | ||
| 179 | } else { | ||
| 180 | const offset: u12 = blk: { | ||
| 181 | if (inst.LoadStoreRegister.size == 0) { | ||
| 182 | if (inst.LoadStoreRegister.v == 1) { | ||
| 183 | // 128-bit SIMD is scaled by 16. | ||
| 184 | break :blk try math.divExact(u12, narrowed, 16); | ||
| 185 | } | ||
| 186 | // Otherwise, 8-bit SIMD or ldrb. | ||
| 187 | break :blk narrowed; | ||
| 188 | } else { | ||
| 189 | const denom: u4 = try math.powi(u4, 2, inst.LoadStoreRegister.size); | ||
| 190 | break :blk try math.divExact(u12, narrowed, denom); | ||
| 191 | } | ||
| 192 | }; | ||
| 193 | inst.LoadStoreRegister.offset = offset; | ||
| 194 | } | ||
| 195 | |||
| 196 | mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | ||
| 197 | } | ||
| 198 | }; | ||
| 199 | |||
| 200 | pub const GotPage = struct { | ||
| 201 | base: Relocation, | ||
| 202 | /// Always .PCRelativeAddress | ||
| 203 | inst: aarch64.Instruction, | ||
| 204 | |||
| 205 | pub const base_type: Relocation.Type = .got_page; | ||
| 206 | |||
| 207 | pub fn resolve(page: GotPage, source_addr: u64, target_addr: u64) !void { | ||
| 208 | const source_page = @intCast(i32, source_addr >> 12); | ||
| 209 | const target_page = @intCast(i32, target_addr >> 12); | ||
| 210 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | ||
| 211 | |||
| 212 | log.debug(" | moving by {} pages", .{pages}); | ||
| 213 | |||
| 214 | var inst = page.inst; | ||
| 215 | inst.PCRelativeAddress.immhi = @truncate(u19, pages >> 2); | ||
| 216 | inst.PCRelativeAddress.immlo = @truncate(u2, pages); | ||
| 217 | |||
| 218 | mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | ||
| 219 | } | ||
| 220 | }; | ||
| 221 | |||
| 222 | pub const GotPageOff = struct { | ||
| 223 | base: Relocation, | ||
| 224 | /// Always .LoadStoreRegister with size = 3 for GOT indirection | ||
| 225 | inst: aarch64.Instruction, | ||
| 226 | |||
| 227 | pub const base_type: Relocation.Type = .got_page_off; | ||
| 228 | |||
| 229 | pub fn resolve(page_off: GotPageOff, target_addr: u64) !void { | ||
| 230 | const narrowed = @truncate(u12, target_addr); | ||
| 231 | |||
| 232 | log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | ||
| 233 | |||
| 234 | var inst = page_off.inst; | ||
| 235 | const offset = try math.divExact(u12, narrowed, 8); | ||
| 236 | inst.LoadStoreRegister.offset = offset; | ||
| 237 | |||
| 238 | mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | ||
| 239 | } | ||
| 240 | }; | ||
| 241 | |||
| 242 | pub const TlvpPage = struct { | ||
| 243 | base: Relocation, | ||
| 244 | /// Always .PCRelativeAddress | ||
| 245 | inst: aarch64.Instruction, | ||
| 246 | |||
| 247 | pub const base_type: Relocation.Type = .tlvp_page; | ||
| 248 | |||
| 249 | pub fn resolve(page: TlvpPage, source_addr: u64, target_addr: u64) !void { | ||
| 250 | const source_page = @intCast(i32, source_addr >> 12); | ||
| 251 | const target_page = @intCast(i32, target_addr >> 12); | ||
| 252 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | ||
| 253 | |||
| 254 | log.debug(" | moving by {} pages", .{pages}); | ||
| 255 | |||
| 256 | var inst = page.inst; | ||
| 257 | inst.PCRelativeAddress.immhi = @truncate(u19, pages >> 2); | ||
| 258 | inst.PCRelativeAddress.immlo = @truncate(u2, pages); | ||
| 259 | |||
| 260 | mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | ||
| 261 | } | ||
| 262 | }; | ||
| 263 | |||
| 264 | pub const TlvpPageOff = struct { | ||
| 265 | base: Relocation, | ||
| 266 | /// Always .AddSubtractImmediate regardless of the source instruction. | ||
| 267 | /// This means, we always rewrite the instruction to add even if the | ||
| 268 | /// source instruction was an ldr. | ||
| 269 | inst: aarch64.Instruction, | ||
| 270 | |||
| 271 | pub const base_type: Relocation.Type = .tlvp_page_off; | ||
| 272 | |||
| 273 | pub fn resolve(page_off: TlvpPageOff, target_addr: u64) !void { | ||
| 274 | const narrowed = @truncate(u12, target_addr); | ||
| 275 | 109 | ||
| 276 | log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | 110 | const result = if (args.subtractor) |subtractor| |
| 111 | @intCast(i64, args.target_addr) - @intCast(i64, subtractor) + addend | ||
| 112 | else | ||
| 113 | @intCast(i64, args.target_addr) + addend; | ||
| 277 | 114 | ||
| 278 | var inst = page_off.inst; | 115 | log.debug(" | calculated addend 0x{x}", .{addend}); |
| 279 | inst.AddSubtractImmediate.imm12 = narrowed; | 116 | log.debug(" | calculated unsigned value 0x{x}", .{result}); |
| 280 | 117 | ||
| 281 | mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | 118 | if (unsigned.is_64bit) { |
| 119 | mem.writeIntLittle( | ||
| 120 | u64, | ||
| 121 | unsigned.base.code[0..8], | ||
| 122 | @bitCast(u64, result), | ||
| 123 | ); | ||
| 124 | } else { | ||
| 125 | mem.writeIntLittle( | ||
| 126 | u32, | ||
| 127 | unsigned.base.code[0..4], | ||
| 128 | @truncate(u32, @bitCast(u64, result)), | ||
| 129 | ); | ||
| 282 | } | 130 | } |
| 283 | }; | 131 | } |
| 284 | }; | 132 | }; |
| 285 | 133 | ||
| 286 | pub fn parse(allocator: *Allocator, code: []u8, relocs: []const macho.relocation_info) ![]*Relocation { | 134 | pub fn parse( |
| 135 | allocator: *Allocator, | ||
| 136 | arch: std.Target.Cpu.Arch, | ||
| 137 | code: []u8, | ||
| 138 | relocs: []const macho.relocation_info, | ||
| 139 | ) ![]*Relocation { | ||
| 287 | var it = RelocIterator{ | 140 | var it = RelocIterator{ |
| 288 | .buffer = relocs, | 141 | .buffer = relocs, |
| 289 | }; | 142 | }; |
| 290 | 143 | ||
| 291 | var parser = Parser{ | 144 | switch (arch) { |
| 292 | .allocator = allocator, | 145 | .aarch64 => { |
| 293 | .it = &it, | 146 | var parser = aarch64.Parser{ |
| 294 | .code = code, | 147 | .allocator = allocator, |
| 295 | .parsed = std.ArrayList(*Relocation).init(allocator), | 148 | .it = &it, |
| 296 | }; | 149 | .code = code, |
| 297 | defer parser.deinit(); | 150 | .parsed = std.ArrayList(*Relocation).init(allocator), |
| 298 | try parser.parse(); | 151 | }; |
| 152 | defer parser.deinit(); | ||
| 153 | try parser.parse(); | ||
| 154 | |||
| 155 | return parser.parsed.toOwnedSlice(); | ||
| 156 | }, | ||
| 157 | .x86_64 => { | ||
| 158 | var parser = x86_64.Parser{ | ||
| 159 | .allocator = allocator, | ||
| 160 | .it = &it, | ||
| 161 | .code = code, | ||
| 162 | .parsed = std.ArrayList(*Relocation).init(allocator), | ||
| 163 | }; | ||
| 164 | defer parser.deinit(); | ||
| 165 | try parser.parse(); | ||
| 299 | 166 | ||
| 300 | return parser.parsed.toOwnedSlice(); | 167 | return parser.parsed.toOwnedSlice(); |
| 168 | }, | ||
| 169 | else => unreachable, | ||
| 170 | } | ||
| 301 | } | 171 | } |
| 302 | 172 | ||
| 303 | const RelocIterator = struct { | 173 | pub const RelocIterator = struct { |
| 304 | buffer: []const macho.relocation_info, | 174 | buffer: []const macho.relocation_info, |
| 305 | index: i64 = -1, | 175 | index: i64 = -1, |
| 306 | 176 | ||
| ... | @@ -308,7 +178,8 @@ const RelocIterator = struct { | ... | @@ -308,7 +178,8 @@ const RelocIterator = struct { |
| 308 | self.index += 1; | 178 | self.index += 1; |
| 309 | if (self.index < self.buffer.len) { | 179 | if (self.index < self.buffer.len) { |
| 310 | const reloc = self.buffer[@intCast(u64, self.index)]; | 180 | const reloc = self.buffer[@intCast(u64, self.index)]; |
| 311 | log.debug("{s}", .{@intToEnum(macho.reloc_type_arm64, reloc.r_type)}); | 181 | log.debug("relocation", .{}); |
| 182 | log.debug(" | type = {}", .{reloc.r_type}); | ||
| 312 | log.debug(" | offset = {}", .{reloc.r_address}); | 183 | log.debug(" | offset = {}", .{reloc.r_address}); |
| 313 | log.debug(" | PC = {}", .{reloc.r_pcrel == 1}); | 184 | log.debug(" | PC = {}", .{reloc.r_pcrel == 1}); |
| 314 | log.debug(" | length = {}", .{reloc.r_length}); | 185 | log.debug(" | length = {}", .{reloc.r_length}); |
| ... | @@ -319,423 +190,8 @@ const RelocIterator = struct { | ... | @@ -319,423 +190,8 @@ const RelocIterator = struct { |
| 319 | return null; | 190 | return null; |
| 320 | } | 191 | } |
| 321 | 192 | ||
| 322 | pub fn peek(self: *RelocIterator) ?macho.reloc_type_arm64 { | 193 | pub fn peek(self: RelocIterator) macho.relocation_info { |
| 323 | if (self.index + 1 < self.buffer.len) { | 194 | assert(self.index + 1 < self.buffer.len); |
| 324 | const reloc = self.buffer[@intCast(u64, self.index + 1)]; | 195 | return self.buffer[@intCast(u64, self.index + 1)]; |
| 325 | const tt = @intToEnum(macho.reloc_type_arm64, reloc.r_type); | ||
| 326 | return tt; | ||
| 327 | } | ||
| 328 | return null; | ||
| 329 | } | 196 | } |
| 330 | }; | 197 | }; |
| 331 | |||
| 332 | const Parser = struct { | ||
| 333 | allocator: *Allocator, | ||
| 334 | it: *RelocIterator, | ||
| 335 | code: []u8, | ||
| 336 | parsed: std.ArrayList(*Relocation), | ||
| 337 | addend: ?u32 = null, | ||
| 338 | subtractor: ?Relocation.Target = null, | ||
| 339 | |||
| 340 | fn deinit(parser: *Parser) void { | ||
| 341 | parser.parsed.deinit(); | ||
| 342 | } | ||
| 343 | |||
| 344 | fn parse(parser: *Parser) !void { | ||
| 345 | while (parser.it.next()) |reloc| { | ||
| 346 | switch (@intToEnum(macho.reloc_type_arm64, reloc.r_type)) { | ||
| 347 | .ARM64_RELOC_BRANCH26 => { | ||
| 348 | try parser.parseBranch(reloc); | ||
| 349 | }, | ||
| 350 | .ARM64_RELOC_SUBTRACTOR => { | ||
| 351 | try parser.parseSubtractor(reloc); | ||
| 352 | }, | ||
| 353 | .ARM64_RELOC_UNSIGNED => { | ||
| 354 | try parser.parseUnsigned(reloc); | ||
| 355 | }, | ||
| 356 | .ARM64_RELOC_ADDEND => { | ||
| 357 | try parser.parseAddend(reloc); | ||
| 358 | }, | ||
| 359 | .ARM64_RELOC_PAGE21, | ||
| 360 | .ARM64_RELOC_GOT_LOAD_PAGE21, | ||
| 361 | .ARM64_RELOC_TLVP_LOAD_PAGE21, | ||
| 362 | => { | ||
| 363 | try parser.parsePage(reloc); | ||
| 364 | }, | ||
| 365 | .ARM64_RELOC_PAGEOFF12 => { | ||
| 366 | try parser.parsePageOff(reloc); | ||
| 367 | }, | ||
| 368 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => { | ||
| 369 | try parser.parseGotLoadPageOff(reloc); | ||
| 370 | }, | ||
| 371 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => { | ||
| 372 | try parser.parseTlvpLoadPageOff(reloc); | ||
| 373 | }, | ||
| 374 | .ARM64_RELOC_POINTER_TO_GOT => { | ||
| 375 | return error.ToDoRelocPointerToGot; | ||
| 376 | }, | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | fn parseAddend(parser: *Parser, reloc: macho.relocation_info) !void { | ||
| 382 | const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type); | ||
| 383 | assert(reloc_type == .ARM64_RELOC_ADDEND); | ||
| 384 | assert(reloc.r_pcrel == 0); | ||
| 385 | assert(reloc.r_extern == 0); | ||
| 386 | assert(parser.addend == null); | ||
| 387 | |||
| 388 | parser.addend = reloc.r_symbolnum; | ||
| 389 | |||
| 390 | // Verify ADDEND is followed by a load. | ||
| 391 | if (parser.it.peek()) |tt| { | ||
| 392 | switch (tt) { | ||
| 393 | .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {}, | ||
| 394 | else => |other| { | ||
| 395 | log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{other}); | ||
| 396 | return error.UnexpectedRelocationType; | ||
| 397 | }, | ||
| 398 | } | ||
| 399 | } else { | ||
| 400 | log.err("unexpected end of stream", .{}); | ||
| 401 | return error.UnexpectedEndOfStream; | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | fn parseBranch(parser: *Parser, reloc: macho.relocation_info) !void { | ||
| 406 | const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type); | ||
| 407 | assert(reloc_type == .ARM64_RELOC_BRANCH26); | ||
| 408 | assert(reloc.r_pcrel == 1); | ||
| 409 | assert(reloc.r_length == 2); | ||
| 410 | |||
| 411 | const offset = @intCast(u32, reloc.r_address); | ||
| 412 | const inst = parser.code[offset..][0..4]; | ||
| 413 | const parsed_inst = aarch64.Instruction{ .UnconditionalBranchImmediate = mem.bytesToValue( | ||
| 414 | meta.TagPayload( | ||
| 415 | aarch64.Instruction, | ||
| 416 | aarch64.Instruction.UnconditionalBranchImmediate, | ||
| 417 | ), | ||
| 418 | inst, | ||
| 419 | ) }; | ||
| 420 | |||
| 421 | var branch = try parser.allocator.create(Relocation.Branch); | ||
| 422 | errdefer parser.allocator.destroy(branch); | ||
| 423 | |||
| 424 | const target = Relocation.Target.from_reloc(reloc); | ||
| 425 | |||
| 426 | branch.* = .{ | ||
| 427 | .base = .{ | ||
| 428 | .@"type" = .branch, | ||
| 429 | .code = inst, | ||
| 430 | .offset = @intCast(u32, reloc.r_address), | ||
| 431 | .target = target, | ||
| 432 | }, | ||
| 433 | .inst = parsed_inst, | ||
| 434 | }; | ||
| 435 | |||
| 436 | log.debug(" | emitting {}", .{branch}); | ||
| 437 | try parser.parsed.append(&branch.base); | ||
| 438 | } | ||
| 439 | |||
| 440 | fn parsePage(parser: *Parser, reloc: macho.relocation_info) !void { | ||
| 441 | assert(reloc.r_pcrel == 1); | ||
| 442 | assert(reloc.r_length == 2); | ||
| 443 | |||
| 444 | const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type); | ||
| 445 | const target = Relocation.Target.from_reloc(reloc); | ||
| 446 | |||
| 447 | const offset = @intCast(u32, reloc.r_address); | ||
| 448 | const inst = parser.code[offset..][0..4]; | ||
| 449 | const parsed_inst = aarch64.Instruction{ .PCRelativeAddress = mem.bytesToValue(meta.TagPayload( | ||
| 450 | aarch64.Instruction, | ||
| 451 | aarch64.Instruction.PCRelativeAddress, | ||
| 452 | ), inst) }; | ||
| 453 | |||
| 454 | const ptr: *Relocation = ptr: { | ||
| 455 | switch (reloc_type) { | ||
| 456 | .ARM64_RELOC_PAGE21 => { | ||
| 457 | defer { | ||
| 458 | // Reset parser's addend state | ||
| 459 | parser.addend = null; | ||
| 460 | } | ||
| 461 | var page = try parser.allocator.create(Relocation.Page); | ||
| 462 | errdefer parser.allocator.destroy(page); | ||
| 463 | |||
| 464 | page.* = .{ | ||
| 465 | .base = .{ | ||
| 466 | .@"type" = .page, | ||
| 467 | .code = inst, | ||
| 468 | .offset = offset, | ||
| 469 | .target = target, | ||
| 470 | }, | ||
| 471 | .addend = parser.addend, | ||
| 472 | .inst = parsed_inst, | ||
| 473 | }; | ||
| 474 | |||
| 475 | log.debug(" | emitting {}", .{page}); | ||
| 476 | |||
| 477 | break :ptr &page.base; | ||
| 478 | }, | ||
| 479 | .ARM64_RELOC_GOT_LOAD_PAGE21 => { | ||
| 480 | var page = try parser.allocator.create(Relocation.GotPage); | ||
| 481 | errdefer parser.allocator.destroy(page); | ||
| 482 | |||
| 483 | page.* = .{ | ||
| 484 | .base = .{ | ||
| 485 | .@"type" = .got_page, | ||
| 486 | .code = inst, | ||
| 487 | .offset = offset, | ||
| 488 | .target = target, | ||
| 489 | }, | ||
| 490 | .inst = parsed_inst, | ||
| 491 | }; | ||
| 492 | |||
| 493 | log.debug(" | emitting {}", .{page}); | ||
| 494 | |||
| 495 | break :ptr &page.base; | ||
| 496 | }, | ||
| 497 | .ARM64_RELOC_TLVP_LOAD_PAGE21 => { | ||
| 498 | var page = try parser.allocator.create(Relocation.TlvpPage); | ||
| 499 | errdefer parser.allocator.destroy(page); | ||
| 500 | |||
| 501 | page.* = .{ | ||
| 502 | .base = .{ | ||
| 503 | .@"type" = .tlvp_page, | ||
| 504 | .code = inst, | ||
| 505 | .offset = offset, | ||
| 506 | .target = target, | ||
| 507 | }, | ||
| 508 | .inst = parsed_inst, | ||
| 509 | }; | ||
| 510 | |||
| 511 | log.debug(" | emitting {}", .{page}); | ||
| 512 | |||
| 513 | break :ptr &page.base; | ||
| 514 | }, | ||
| 515 | else => unreachable, | ||
| 516 | } | ||
| 517 | }; | ||
| 518 | |||
| 519 | try parser.parsed.append(ptr); | ||
| 520 | } | ||
| 521 | |||
| 522 | fn parsePageOff(parser: *Parser, reloc: macho.relocation_info) !void { | ||
| 523 | defer { | ||
| 524 | // Reset parser's addend state | ||
| 525 | parser.addend = null; | ||
| 526 | } | ||
| 527 | |||
| 528 | const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type); | ||
| 529 | assert(reloc_type == .ARM64_RELOC_PAGEOFF12); | ||
| 530 | assert(reloc.r_pcrel == 0); | ||
| 531 | assert(reloc.r_length == 2); | ||
| 532 | |||
| 533 | const offset = @intCast(u32, reloc.r_address); | ||
| 534 | const inst = parser.code[offset..][0..4]; | ||
| 535 | |||
| 536 | var op_kind: Relocation.PageOff.OpKind = undefined; | ||
| 537 | var parsed_inst: aarch64.Instruction = undefined; | ||
| 538 | if (isArithmeticOp(inst)) { | ||
| 539 | op_kind = .arithmetic; | ||
| 540 | parsed_inst = .{ .AddSubtractImmediate = mem.bytesToValue(meta.TagPayload( | ||
| 541 | aarch64.Instruction, | ||
| 542 | aarch64.Instruction.AddSubtractImmediate, | ||
| 543 | ), inst) }; | ||
| 544 | } else { | ||
| 545 | op_kind = .load_store; | ||
| 546 | parsed_inst = .{ .LoadStoreRegister = mem.bytesToValue(meta.TagPayload( | ||
| 547 | aarch64.Instruction, | ||
| 548 | aarch64.Instruction.LoadStoreRegister, | ||
| 549 | ), inst) }; | ||
| 550 | } | ||
| 551 | const target = Relocation.Target.from_reloc(reloc); | ||
| 552 | |||
| 553 | var page_off = try parser.allocator.create(Relocation.PageOff); | ||
| 554 | errdefer parser.allocator.destroy(page_off); | ||
| 555 | |||
| 556 | page_off.* = .{ | ||
| 557 | .base = .{ | ||
| 558 | .@"type" = .page_off, | ||
| 559 | .code = inst, | ||
| 560 | .offset = offset, | ||
| 561 | .target = target, | ||
| 562 | }, | ||
| 563 | .op_kind = op_kind, | ||
| 564 | .inst = parsed_inst, | ||
| 565 | .addend = parser.addend, | ||
| 566 | }; | ||
| 567 | |||
| 568 | log.debug(" | emitting {}", .{page_off}); | ||
| 569 | try parser.parsed.append(&page_off.base); | ||
| 570 | } | ||
| 571 | |||
| 572 | fn parseGotLoadPageOff(parser: *Parser, reloc: macho.relocation_info) !void { | ||
| 573 | const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type); | ||
| 574 | assert(reloc_type == .ARM64_RELOC_GOT_LOAD_PAGEOFF12); | ||
| 575 | assert(reloc.r_pcrel == 0); | ||
| 576 | assert(reloc.r_length == 2); | ||
| 577 | |||
| 578 | const offset = @intCast(u32, reloc.r_address); | ||
| 579 | const inst = parser.code[offset..][0..4]; | ||
| 580 | assert(!isArithmeticOp(inst)); | ||
| 581 | |||
| 582 | const parsed_inst = mem.bytesToValue(meta.TagPayload( | ||
| 583 | aarch64.Instruction, | ||
| 584 | aarch64.Instruction.LoadStoreRegister, | ||
| 585 | ), inst); | ||
| 586 | assert(parsed_inst.size == 3); | ||
| 587 | |||
| 588 | const target = Relocation.Target.from_reloc(reloc); | ||
| 589 | |||
| 590 | var page_off = try parser.allocator.create(Relocation.GotPageOff); | ||
| 591 | errdefer parser.allocator.destroy(page_off); | ||
| 592 | |||
| 593 | page_off.* = .{ | ||
| 594 | .base = .{ | ||
| 595 | .@"type" = .got_page_off, | ||
| 596 | .code = inst, | ||
| 597 | .offset = offset, | ||
| 598 | .target = target, | ||
| 599 | }, | ||
| 600 | .inst = .{ | ||
| 601 | .LoadStoreRegister = parsed_inst, | ||
| 602 | }, | ||
| 603 | }; | ||
| 604 | |||
| 605 | log.debug(" | emitting {}", .{page_off}); | ||
| 606 | try parser.parsed.append(&page_off.base); | ||
| 607 | } | ||
| 608 | |||
| 609 | fn parseTlvpLoadPageOff(parser: *Parser, reloc: macho.relocation_info) !void { | ||
| 610 | const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type); | ||
| 611 | assert(reloc_type == .ARM64_RELOC_TLVP_LOAD_PAGEOFF12); | ||
| 612 | assert(reloc.r_pcrel == 0); | ||
| 613 | assert(reloc.r_length == 2); | ||
| 614 | |||
| 615 | const RegInfo = struct { | ||
| 616 | rd: u5, | ||
| 617 | rn: u5, | ||
| 618 | size: u1, | ||
| 619 | }; | ||
| 620 | |||
| 621 | const offset = @intCast(u32, reloc.r_address); | ||
| 622 | const inst = parser.code[offset..][0..4]; | ||
| 623 | const parsed: RegInfo = parsed: { | ||
| 624 | if (isArithmeticOp(inst)) { | ||
| 625 | const parsed_inst = mem.bytesAsValue(meta.TagPayload( | ||
| 626 | aarch64.Instruction, | ||
| 627 | aarch64.Instruction.AddSubtractImmediate, | ||
| 628 | ), inst); | ||
| 629 | break :parsed .{ | ||
| 630 | .rd = parsed_inst.rd, | ||
| 631 | .rn = parsed_inst.rn, | ||
| 632 | .size = parsed_inst.sf, | ||
| 633 | }; | ||
| 634 | } else { | ||
| 635 | const parsed_inst = mem.bytesAsValue(meta.TagPayload( | ||
| 636 | aarch64.Instruction, | ||
| 637 | aarch64.Instruction.LoadStoreRegister, | ||
| 638 | ), inst); | ||
| 639 | break :parsed .{ | ||
| 640 | .rd = parsed_inst.rt, | ||
| 641 | .rn = parsed_inst.rn, | ||
| 642 | .size = @truncate(u1, parsed_inst.size), | ||
| 643 | }; | ||
| 644 | } | ||
| 645 | }; | ||
| 646 | |||
| 647 | const target = Relocation.Target.from_reloc(reloc); | ||
| 648 | |||
| 649 | var page_off = try parser.allocator.create(Relocation.TlvpPageOff); | ||
| 650 | errdefer parser.allocator.destroy(page_off); | ||
| 651 | |||
| 652 | page_off.* = .{ | ||
| 653 | .base = .{ | ||
| 654 | .@"type" = .tlvp_page_off, | ||
| 655 | .code = inst, | ||
| 656 | .offset = @intCast(u32, reloc.r_address), | ||
| 657 | .target = target, | ||
| 658 | }, | ||
| 659 | .inst = .{ | ||
| 660 | .AddSubtractImmediate = .{ | ||
| 661 | .rd = parsed.rd, | ||
| 662 | .rn = parsed.rn, | ||
| 663 | .imm12 = 0, // This will be filled when target addresses are known. | ||
| 664 | .sh = 0, | ||
| 665 | .s = 0, | ||
| 666 | .op = 0, | ||
| 667 | .sf = parsed.size, | ||
| 668 | }, | ||
| 669 | }, | ||
| 670 | }; | ||
| 671 | |||
| 672 | log.debug(" | emitting {}", .{page_off}); | ||
| 673 | try parser.parsed.append(&page_off.base); | ||
| 674 | } | ||
| 675 | |||
| 676 | fn parseSubtractor(parser: *Parser, reloc: macho.relocation_info) !void { | ||
| 677 | const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type); | ||
| 678 | assert(reloc_type == .ARM64_RELOC_SUBTRACTOR); | ||
| 679 | assert(reloc.r_pcrel == 0); | ||
| 680 | assert(parser.subtractor == null); | ||
| 681 | |||
| 682 | parser.subtractor = Relocation.Target.from_reloc(reloc); | ||
| 683 | |||
| 684 | // Verify SUBTRACTOR is followed by UNSIGNED. | ||
| 685 | if (parser.it.peek()) |tt| { | ||
| 686 | if (tt != .ARM64_RELOC_UNSIGNED) { | ||
| 687 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{tt}); | ||
| 688 | return error.UnexpectedRelocationType; | ||
| 689 | } | ||
| 690 | } else { | ||
| 691 | log.err("unexpected end of stream", .{}); | ||
| 692 | return error.UnexpectedEndOfStream; | ||
| 693 | } | ||
| 694 | } | ||
| 695 | |||
| 696 | fn parseUnsigned(parser: *Parser, reloc: macho.relocation_info) !void { | ||
| 697 | defer { | ||
| 698 | // Reset parser's subtractor state | ||
| 699 | parser.subtractor = null; | ||
| 700 | } | ||
| 701 | |||
| 702 | const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type); | ||
| 703 | assert(reloc_type == .ARM64_RELOC_UNSIGNED); | ||
| 704 | assert(reloc.r_pcrel == 0); | ||
| 705 | |||
| 706 | var unsigned = try parser.allocator.create(Relocation.Unsigned); | ||
| 707 | errdefer parser.allocator.destroy(unsigned); | ||
| 708 | |||
| 709 | const target = Relocation.Target.from_reloc(reloc); | ||
| 710 | const is_64bit: bool = switch (reloc.r_length) { | ||
| 711 | 3 => true, | ||
| 712 | 2 => false, | ||
| 713 | else => unreachable, | ||
| 714 | }; | ||
| 715 | const offset = @intCast(u32, reloc.r_address); | ||
| 716 | const addend: i64 = if (is_64bit) | ||
| 717 | mem.readIntLittle(i64, parser.code[offset..][0..8]) | ||
| 718 | else | ||
| 719 | mem.readIntLittle(i32, parser.code[offset..][0..4]); | ||
| 720 | |||
| 721 | unsigned.* = .{ | ||
| 722 | .base = .{ | ||
| 723 | .@"type" = .unsigned, | ||
| 724 | .code = if (is_64bit) parser.code[offset..][0..8] else parser.code[offset..][0..4], | ||
| 725 | .offset = offset, | ||
| 726 | .target = target, | ||
| 727 | }, | ||
| 728 | .subtractor = parser.subtractor, | ||
| 729 | .is_64bit = is_64bit, | ||
| 730 | .addend = addend, | ||
| 731 | }; | ||
| 732 | |||
| 733 | log.debug(" | emitting {}", .{unsigned}); | ||
| 734 | try parser.parsed.append(&unsigned.base); | ||
| 735 | } | ||
| 736 | }; | ||
| 737 | |||
| 738 | fn isArithmeticOp(inst: *const [4]u8) callconv(.Inline) bool { | ||
| 739 | const group_decode = @truncate(u5, inst[3]); | ||
| 740 | return ((group_decode >> 2) == 4); | ||
| 741 | } |
src/link/MachO/reloc/aarch64.zig created+587| ... | @@ -0,0 +1,587 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const aarch64 = @import("../../../codegen/aarch64.zig"); | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const log = std.log.scoped(.reloc); | ||
| 5 | const macho = std.macho; | ||
| 6 | const math = std.math; | ||
| 7 | const mem = std.mem; | ||
| 8 | const meta = std.meta; | ||
| 9 | const reloc = @import("../reloc.zig"); | ||
| 10 | |||
| 11 | const Allocator = mem.Allocator; | ||
| 12 | const Relocation = reloc.Relocation; | ||
| 13 | |||
| 14 | pub const Branch = struct { | ||
| 15 | base: Relocation, | ||
| 16 | /// Always .UnconditionalBranchImmediate | ||
| 17 | inst: aarch64.Instruction, | ||
| 18 | |||
| 19 | pub const base_type: Relocation.Type = .branch_aarch64; | ||
| 20 | |||
| 21 | pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void { | ||
| 22 | const displacement = try math.cast(i28, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr)); | ||
| 23 | |||
| 24 | log.debug(" | displacement 0x{x}", .{displacement}); | ||
| 25 | |||
| 26 | var inst = branch.inst; | ||
| 27 | inst.UnconditionalBranchImmediate.imm26 = @truncate(u26, @bitCast(u28, displacement) >> 2); | ||
| 28 | mem.writeIntLittle(u32, branch.base.code[0..4], inst.toU32()); | ||
| 29 | } | ||
| 30 | }; | ||
| 31 | |||
| 32 | pub const Page = struct { | ||
| 33 | base: Relocation, | ||
| 34 | addend: ?u32 = null, | ||
| 35 | /// Always .PCRelativeAddress | ||
| 36 | inst: aarch64.Instruction, | ||
| 37 | |||
| 38 | pub const base_type: Relocation.Type = .page; | ||
| 39 | |||
| 40 | pub fn resolve(page: Page, args: Relocation.ResolveArgs) !void { | ||
| 41 | const target_addr = if (page.addend) |addend| args.target_addr + addend else args.target_addr; | ||
| 42 | const source_page = @intCast(i32, args.source_addr >> 12); | ||
| 43 | const target_page = @intCast(i32, target_addr >> 12); | ||
| 44 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | ||
| 45 | |||
| 46 | log.debug(" | calculated addend 0x{x}", .{page.addend}); | ||
| 47 | log.debug(" | moving by {} pages", .{pages}); | ||
| 48 | |||
| 49 | var inst = page.inst; | ||
| 50 | inst.PCRelativeAddress.immhi = @truncate(u19, pages >> 2); | ||
| 51 | inst.PCRelativeAddress.immlo = @truncate(u2, pages); | ||
| 52 | |||
| 53 | mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | ||
| 54 | } | ||
| 55 | }; | ||
| 56 | |||
| 57 | pub const PageOff = struct { | ||
| 58 | base: Relocation, | ||
| 59 | addend: ?u32 = null, | ||
| 60 | op_kind: OpKind, | ||
| 61 | inst: aarch64.Instruction, | ||
| 62 | |||
| 63 | pub const base_type: Relocation.Type = .page_off; | ||
| 64 | |||
| 65 | pub const OpKind = enum { | ||
| 66 | arithmetic, | ||
| 67 | load_store, | ||
| 68 | }; | ||
| 69 | |||
| 70 | pub fn resolve(page_off: PageOff, args: Relocation.ResolveArgs) !void { | ||
| 71 | const target_addr = if (page_off.addend) |addend| args.target_addr + addend else args.target_addr; | ||
| 72 | const narrowed = @truncate(u12, target_addr); | ||
| 73 | |||
| 74 | log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | ||
| 75 | log.debug(" | {s} opcode", .{page_off.op_kind}); | ||
| 76 | |||
| 77 | var inst = page_off.inst; | ||
| 78 | if (page_off.op_kind == .arithmetic) { | ||
| 79 | inst.AddSubtractImmediate.imm12 = narrowed; | ||
| 80 | } else { | ||
| 81 | const offset: u12 = blk: { | ||
| 82 | if (inst.LoadStoreRegister.size == 0) { | ||
| 83 | if (inst.LoadStoreRegister.v == 1) { | ||
| 84 | // 128-bit SIMD is scaled by 16. | ||
| 85 | break :blk try math.divExact(u12, narrowed, 16); | ||
| 86 | } | ||
| 87 | // Otherwise, 8-bit SIMD or ldrb. | ||
| 88 | break :blk narrowed; | ||
| 89 | } else { | ||
| 90 | const denom: u4 = try math.powi(u4, 2, inst.LoadStoreRegister.size); | ||
| 91 | break :blk try math.divExact(u12, narrowed, denom); | ||
| 92 | } | ||
| 93 | }; | ||
| 94 | inst.LoadStoreRegister.offset = offset; | ||
| 95 | } | ||
| 96 | |||
| 97 | mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | ||
| 98 | } | ||
| 99 | }; | ||
| 100 | |||
| 101 | pub const GotPage = struct { | ||
| 102 | base: Relocation, | ||
| 103 | /// Always .PCRelativeAddress | ||
| 104 | inst: aarch64.Instruction, | ||
| 105 | |||
| 106 | pub const base_type: Relocation.Type = .got_page; | ||
| 107 | |||
| 108 | pub fn resolve(page: GotPage, args: Relocation.ResolveArgs) !void { | ||
| 109 | const source_page = @intCast(i32, args.source_addr >> 12); | ||
| 110 | const target_page = @intCast(i32, args.target_addr >> 12); | ||
| 111 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | ||
| 112 | |||
| 113 | log.debug(" | moving by {} pages", .{pages}); | ||
| 114 | |||
| 115 | var inst = page.inst; | ||
| 116 | inst.PCRelativeAddress.immhi = @truncate(u19, pages >> 2); | ||
| 117 | inst.PCRelativeAddress.immlo = @truncate(u2, pages); | ||
| 118 | |||
| 119 | mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | ||
| 120 | } | ||
| 121 | }; | ||
| 122 | |||
| 123 | pub const GotPageOff = struct { | ||
| 124 | base: Relocation, | ||
| 125 | /// Always .LoadStoreRegister with size = 3 for GOT indirection | ||
| 126 | inst: aarch64.Instruction, | ||
| 127 | |||
| 128 | pub const base_type: Relocation.Type = .got_page_off; | ||
| 129 | |||
| 130 | pub fn resolve(page_off: GotPageOff, args: Relocation.ResolveArgs) !void { | ||
| 131 | const narrowed = @truncate(u12, args.target_addr); | ||
| 132 | |||
| 133 | log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | ||
| 134 | |||
| 135 | var inst = page_off.inst; | ||
| 136 | const offset = try math.divExact(u12, narrowed, 8); | ||
| 137 | inst.LoadStoreRegister.offset = offset; | ||
| 138 | |||
| 139 | mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | ||
| 140 | } | ||
| 141 | }; | ||
| 142 | |||
| 143 | pub const TlvpPage = struct { | ||
| 144 | base: Relocation, | ||
| 145 | /// Always .PCRelativeAddress | ||
| 146 | inst: aarch64.Instruction, | ||
| 147 | |||
| 148 | pub const base_type: Relocation.Type = .tlvp_page; | ||
| 149 | |||
| 150 | pub fn resolve(page: TlvpPage, args: Relocation.ResolveArgs) !void { | ||
| 151 | const source_page = @intCast(i32, args.source_addr >> 12); | ||
| 152 | const target_page = @intCast(i32, args.target_addr >> 12); | ||
| 153 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | ||
| 154 | |||
| 155 | log.debug(" | moving by {} pages", .{pages}); | ||
| 156 | |||
| 157 | var inst = page.inst; | ||
| 158 | inst.PCRelativeAddress.immhi = @truncate(u19, pages >> 2); | ||
| 159 | inst.PCRelativeAddress.immlo = @truncate(u2, pages); | ||
| 160 | |||
| 161 | mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | ||
| 162 | } | ||
| 163 | }; | ||
| 164 | |||
| 165 | pub const TlvpPageOff = struct { | ||
| 166 | base: Relocation, | ||
| 167 | /// Always .AddSubtractImmediate regardless of the source instruction. | ||
| 168 | /// This means, we always rewrite the instruction to add even if the | ||
| 169 | /// source instruction was an ldr. | ||
| 170 | inst: aarch64.Instruction, | ||
| 171 | |||
| 172 | pub const base_type: Relocation.Type = .tlvp_page_off; | ||
| 173 | |||
| 174 | pub fn resolve(page_off: TlvpPageOff, args: Relocation.ResolveArgs) !void { | ||
| 175 | const narrowed = @truncate(u12, args.target_addr); | ||
| 176 | |||
| 177 | log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | ||
| 178 | |||
| 179 | var inst = page_off.inst; | ||
| 180 | inst.AddSubtractImmediate.imm12 = narrowed; | ||
| 181 | |||
| 182 | mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | ||
| 183 | } | ||
| 184 | }; | ||
| 185 | |||
| 186 | pub const Parser = struct { | ||
| 187 | allocator: *Allocator, | ||
| 188 | it: *reloc.RelocIterator, | ||
| 189 | code: []u8, | ||
| 190 | parsed: std.ArrayList(*Relocation), | ||
| 191 | addend: ?u32 = null, | ||
| 192 | subtractor: ?Relocation.Target = null, | ||
| 193 | |||
| 194 | pub fn deinit(parser: *Parser) void { | ||
| 195 | parser.parsed.deinit(); | ||
| 196 | } | ||
| 197 | |||
| 198 | pub fn parse(parser: *Parser) !void { | ||
| 199 | while (parser.it.next()) |rel| { | ||
| 200 | switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { | ||
| 201 | .ARM64_RELOC_BRANCH26 => { | ||
| 202 | try parser.parseBranch(rel); | ||
| 203 | }, | ||
| 204 | .ARM64_RELOC_SUBTRACTOR => { | ||
| 205 | try parser.parseSubtractor(rel); | ||
| 206 | }, | ||
| 207 | .ARM64_RELOC_UNSIGNED => { | ||
| 208 | try parser.parseUnsigned(rel); | ||
| 209 | }, | ||
| 210 | .ARM64_RELOC_ADDEND => { | ||
| 211 | try parser.parseAddend(rel); | ||
| 212 | }, | ||
| 213 | .ARM64_RELOC_PAGE21, | ||
| 214 | .ARM64_RELOC_GOT_LOAD_PAGE21, | ||
| 215 | .ARM64_RELOC_TLVP_LOAD_PAGE21, | ||
| 216 | => { | ||
| 217 | try parser.parsePage(rel); | ||
| 218 | }, | ||
| 219 | .ARM64_RELOC_PAGEOFF12 => { | ||
| 220 | try parser.parsePageOff(rel); | ||
| 221 | }, | ||
| 222 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => { | ||
| 223 | try parser.parseGotLoadPageOff(rel); | ||
| 224 | }, | ||
| 225 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => { | ||
| 226 | try parser.parseTlvpLoadPageOff(rel); | ||
| 227 | }, | ||
| 228 | .ARM64_RELOC_POINTER_TO_GOT => { | ||
| 229 | return error.ToDoRelocPointerToGot; | ||
| 230 | }, | ||
| 231 | } | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | fn parseAddend(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 236 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | ||
| 237 | assert(rel_type == .ARM64_RELOC_ADDEND); | ||
| 238 | assert(rel.r_pcrel == 0); | ||
| 239 | assert(rel.r_extern == 0); | ||
| 240 | assert(parser.addend == null); | ||
| 241 | |||
| 242 | parser.addend = rel.r_symbolnum; | ||
| 243 | |||
| 244 | // Verify ADDEND is followed by a load. | ||
| 245 | const next = @intToEnum(macho.reloc_type_arm64, parser.it.peek().r_type); | ||
| 246 | switch (next) { | ||
| 247 | .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {}, | ||
| 248 | else => { | ||
| 249 | log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next}); | ||
| 250 | return error.UnexpectedRelocationType; | ||
| 251 | }, | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | fn parseBranch(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 256 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | ||
| 257 | assert(rel_type == .ARM64_RELOC_BRANCH26); | ||
| 258 | assert(rel.r_pcrel == 1); | ||
| 259 | assert(rel.r_length == 2); | ||
| 260 | |||
| 261 | const offset = @intCast(u32, rel.r_address); | ||
| 262 | const inst = parser.code[offset..][0..4]; | ||
| 263 | const parsed_inst = aarch64.Instruction{ .UnconditionalBranchImmediate = mem.bytesToValue( | ||
| 264 | meta.TagPayload( | ||
| 265 | aarch64.Instruction, | ||
| 266 | aarch64.Instruction.UnconditionalBranchImmediate, | ||
| 267 | ), | ||
| 268 | inst, | ||
| 269 | ) }; | ||
| 270 | |||
| 271 | var branch = try parser.allocator.create(Branch); | ||
| 272 | errdefer parser.allocator.destroy(branch); | ||
| 273 | |||
| 274 | const target = Relocation.Target.from_reloc(rel); | ||
| 275 | |||
| 276 | branch.* = .{ | ||
| 277 | .base = .{ | ||
| 278 | .@"type" = .branch_aarch64, | ||
| 279 | .code = inst, | ||
| 280 | .offset = offset, | ||
| 281 | .target = target, | ||
| 282 | }, | ||
| 283 | .inst = parsed_inst, | ||
| 284 | }; | ||
| 285 | |||
| 286 | log.debug(" | emitting {}", .{branch}); | ||
| 287 | try parser.parsed.append(&branch.base); | ||
| 288 | } | ||
| 289 | |||
| 290 | fn parsePage(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 291 | assert(rel.r_pcrel == 1); | ||
| 292 | assert(rel.r_length == 2); | ||
| 293 | |||
| 294 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | ||
| 295 | const target = Relocation.Target.from_reloc(rel); | ||
| 296 | |||
| 297 | const offset = @intCast(u32, rel.r_address); | ||
| 298 | const inst = parser.code[offset..][0..4]; | ||
| 299 | const parsed_inst = aarch64.Instruction{ .PCRelativeAddress = mem.bytesToValue(meta.TagPayload( | ||
| 300 | aarch64.Instruction, | ||
| 301 | aarch64.Instruction.PCRelativeAddress, | ||
| 302 | ), inst) }; | ||
| 303 | |||
| 304 | const ptr: *Relocation = ptr: { | ||
| 305 | switch (rel_type) { | ||
| 306 | .ARM64_RELOC_PAGE21 => { | ||
| 307 | defer { | ||
| 308 | // Reset parser's addend state | ||
| 309 | parser.addend = null; | ||
| 310 | } | ||
| 311 | var page = try parser.allocator.create(Page); | ||
| 312 | errdefer parser.allocator.destroy(page); | ||
| 313 | |||
| 314 | page.* = .{ | ||
| 315 | .base = .{ | ||
| 316 | .@"type" = .page, | ||
| 317 | .code = inst, | ||
| 318 | .offset = offset, | ||
| 319 | .target = target, | ||
| 320 | }, | ||
| 321 | .addend = parser.addend, | ||
| 322 | .inst = parsed_inst, | ||
| 323 | }; | ||
| 324 | |||
| 325 | log.debug(" | emitting {}", .{page}); | ||
| 326 | |||
| 327 | break :ptr &page.base; | ||
| 328 | }, | ||
| 329 | .ARM64_RELOC_GOT_LOAD_PAGE21 => { | ||
| 330 | var page = try parser.allocator.create(GotPage); | ||
| 331 | errdefer parser.allocator.destroy(page); | ||
| 332 | |||
| 333 | page.* = .{ | ||
| 334 | .base = .{ | ||
| 335 | .@"type" = .got_page, | ||
| 336 | .code = inst, | ||
| 337 | .offset = offset, | ||
| 338 | .target = target, | ||
| 339 | }, | ||
| 340 | .inst = parsed_inst, | ||
| 341 | }; | ||
| 342 | |||
| 343 | log.debug(" | emitting {}", .{page}); | ||
| 344 | |||
| 345 | break :ptr &page.base; | ||
| 346 | }, | ||
| 347 | .ARM64_RELOC_TLVP_LOAD_PAGE21 => { | ||
| 348 | var page = try parser.allocator.create(TlvpPage); | ||
| 349 | errdefer parser.allocator.destroy(page); | ||
| 350 | |||
| 351 | page.* = .{ | ||
| 352 | .base = .{ | ||
| 353 | .@"type" = .tlvp_page, | ||
| 354 | .code = inst, | ||
| 355 | .offset = offset, | ||
| 356 | .target = target, | ||
| 357 | }, | ||
| 358 | .inst = parsed_inst, | ||
| 359 | }; | ||
| 360 | |||
| 361 | log.debug(" | emitting {}", .{page}); | ||
| 362 | |||
| 363 | break :ptr &page.base; | ||
| 364 | }, | ||
| 365 | else => unreachable, | ||
| 366 | } | ||
| 367 | }; | ||
| 368 | |||
| 369 | try parser.parsed.append(ptr); | ||
| 370 | } | ||
| 371 | |||
| 372 | fn parsePageOff(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 373 | defer { | ||
| 374 | // Reset parser's addend state | ||
| 375 | parser.addend = null; | ||
| 376 | } | ||
| 377 | |||
| 378 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | ||
| 379 | assert(rel_type == .ARM64_RELOC_PAGEOFF12); | ||
| 380 | assert(rel.r_pcrel == 0); | ||
| 381 | assert(rel.r_length == 2); | ||
| 382 | |||
| 383 | const offset = @intCast(u32, rel.r_address); | ||
| 384 | const inst = parser.code[offset..][0..4]; | ||
| 385 | |||
| 386 | var op_kind: PageOff.OpKind = undefined; | ||
| 387 | var parsed_inst: aarch64.Instruction = undefined; | ||
| 388 | if (isArithmeticOp(inst)) { | ||
| 389 | op_kind = .arithmetic; | ||
| 390 | parsed_inst = .{ .AddSubtractImmediate = mem.bytesToValue(meta.TagPayload( | ||
| 391 | aarch64.Instruction, | ||
| 392 | aarch64.Instruction.AddSubtractImmediate, | ||
| 393 | ), inst) }; | ||
| 394 | } else { | ||
| 395 | op_kind = .load_store; | ||
| 396 | parsed_inst = .{ .LoadStoreRegister = mem.bytesToValue(meta.TagPayload( | ||
| 397 | aarch64.Instruction, | ||
| 398 | aarch64.Instruction.LoadStoreRegister, | ||
| 399 | ), inst) }; | ||
| 400 | } | ||
| 401 | const target = Relocation.Target.from_reloc(rel); | ||
| 402 | |||
| 403 | var page_off = try parser.allocator.create(PageOff); | ||
| 404 | errdefer parser.allocator.destroy(page_off); | ||
| 405 | |||
| 406 | page_off.* = .{ | ||
| 407 | .base = .{ | ||
| 408 | .@"type" = .page_off, | ||
| 409 | .code = inst, | ||
| 410 | .offset = offset, | ||
| 411 | .target = target, | ||
| 412 | }, | ||
| 413 | .op_kind = op_kind, | ||
| 414 | .inst = parsed_inst, | ||
| 415 | .addend = parser.addend, | ||
| 416 | }; | ||
| 417 | |||
| 418 | log.debug(" | emitting {}", .{page_off}); | ||
| 419 | try parser.parsed.append(&page_off.base); | ||
| 420 | } | ||
| 421 | |||
| 422 | fn parseGotLoadPageOff(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 423 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | ||
| 424 | assert(rel_type == .ARM64_RELOC_GOT_LOAD_PAGEOFF12); | ||
| 425 | assert(rel.r_pcrel == 0); | ||
| 426 | assert(rel.r_length == 2); | ||
| 427 | |||
| 428 | const offset = @intCast(u32, rel.r_address); | ||
| 429 | const inst = parser.code[offset..][0..4]; | ||
| 430 | assert(!isArithmeticOp(inst)); | ||
| 431 | |||
| 432 | const parsed_inst = mem.bytesToValue(meta.TagPayload( | ||
| 433 | aarch64.Instruction, | ||
| 434 | aarch64.Instruction.LoadStoreRegister, | ||
| 435 | ), inst); | ||
| 436 | assert(parsed_inst.size == 3); | ||
| 437 | |||
| 438 | const target = Relocation.Target.from_reloc(rel); | ||
| 439 | |||
| 440 | var page_off = try parser.allocator.create(GotPageOff); | ||
| 441 | errdefer parser.allocator.destroy(page_off); | ||
| 442 | |||
| 443 | page_off.* = .{ | ||
| 444 | .base = .{ | ||
| 445 | .@"type" = .got_page_off, | ||
| 446 | .code = inst, | ||
| 447 | .offset = offset, | ||
| 448 | .target = target, | ||
| 449 | }, | ||
| 450 | .inst = .{ | ||
| 451 | .LoadStoreRegister = parsed_inst, | ||
| 452 | }, | ||
| 453 | }; | ||
| 454 | |||
| 455 | log.debug(" | emitting {}", .{page_off}); | ||
| 456 | try parser.parsed.append(&page_off.base); | ||
| 457 | } | ||
| 458 | |||
| 459 | fn parseTlvpLoadPageOff(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 460 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | ||
| 461 | assert(rel_type == .ARM64_RELOC_TLVP_LOAD_PAGEOFF12); | ||
| 462 | assert(rel.r_pcrel == 0); | ||
| 463 | assert(rel.r_length == 2); | ||
| 464 | |||
| 465 | const RegInfo = struct { | ||
| 466 | rd: u5, | ||
| 467 | rn: u5, | ||
| 468 | size: u1, | ||
| 469 | }; | ||
| 470 | |||
| 471 | const offset = @intCast(u32, rel.r_address); | ||
| 472 | const inst = parser.code[offset..][0..4]; | ||
| 473 | const parsed: RegInfo = parsed: { | ||
| 474 | if (isArithmeticOp(inst)) { | ||
| 475 | const parsed_inst = mem.bytesAsValue(meta.TagPayload( | ||
| 476 | aarch64.Instruction, | ||
| 477 | aarch64.Instruction.AddSubtractImmediate, | ||
| 478 | ), inst); | ||
| 479 | break :parsed .{ | ||
| 480 | .rd = parsed_inst.rd, | ||
| 481 | .rn = parsed_inst.rn, | ||
| 482 | .size = parsed_inst.sf, | ||
| 483 | }; | ||
| 484 | } else { | ||
| 485 | const parsed_inst = mem.bytesAsValue(meta.TagPayload( | ||
| 486 | aarch64.Instruction, | ||
| 487 | aarch64.Instruction.LoadStoreRegister, | ||
| 488 | ), inst); | ||
| 489 | break :parsed .{ | ||
| 490 | .rd = parsed_inst.rt, | ||
| 491 | .rn = parsed_inst.rn, | ||
| 492 | .size = @truncate(u1, parsed_inst.size), | ||
| 493 | }; | ||
| 494 | } | ||
| 495 | }; | ||
| 496 | |||
| 497 | const target = Relocation.Target.from_reloc(rel); | ||
| 498 | |||
| 499 | var page_off = try parser.allocator.create(TlvpPageOff); | ||
| 500 | errdefer parser.allocator.destroy(page_off); | ||
| 501 | |||
| 502 | page_off.* = .{ | ||
| 503 | .base = .{ | ||
| 504 | .@"type" = .tlvp_page_off, | ||
| 505 | .code = inst, | ||
| 506 | .offset = offset, | ||
| 507 | .target = target, | ||
| 508 | }, | ||
| 509 | .inst = .{ | ||
| 510 | .AddSubtractImmediate = .{ | ||
| 511 | .rd = parsed.rd, | ||
| 512 | .rn = parsed.rn, | ||
| 513 | .imm12 = 0, // This will be filled when target addresses are known. | ||
| 514 | .sh = 0, | ||
| 515 | .s = 0, | ||
| 516 | .op = 0, | ||
| 517 | .sf = parsed.size, | ||
| 518 | }, | ||
| 519 | }, | ||
| 520 | }; | ||
| 521 | |||
| 522 | log.debug(" | emitting {}", .{page_off}); | ||
| 523 | try parser.parsed.append(&page_off.base); | ||
| 524 | } | ||
| 525 | |||
| 526 | fn parseSubtractor(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 527 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | ||
| 528 | assert(rel_type == .ARM64_RELOC_SUBTRACTOR); | ||
| 529 | assert(rel.r_pcrel == 0); | ||
| 530 | assert(parser.subtractor == null); | ||
| 531 | |||
| 532 | parser.subtractor = Relocation.Target.from_reloc(rel); | ||
| 533 | |||
| 534 | // Verify SUBTRACTOR is followed by UNSIGNED. | ||
| 535 | const next = @intToEnum(macho.reloc_type_arm64, parser.it.peek().r_type); | ||
| 536 | if (next != .ARM64_RELOC_UNSIGNED) { | ||
| 537 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | ||
| 538 | return error.UnexpectedRelocationType; | ||
| 539 | } | ||
| 540 | } | ||
| 541 | |||
| 542 | fn parseUnsigned(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 543 | defer { | ||
| 544 | // Reset parser's subtractor state | ||
| 545 | parser.subtractor = null; | ||
| 546 | } | ||
| 547 | |||
| 548 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | ||
| 549 | assert(rel_type == .ARM64_RELOC_UNSIGNED); | ||
| 550 | assert(rel.r_pcrel == 0); | ||
| 551 | |||
| 552 | var unsigned = try parser.allocator.create(reloc.Unsigned); | ||
| 553 | errdefer parser.allocator.destroy(unsigned); | ||
| 554 | |||
| 555 | const target = Relocation.Target.from_reloc(rel); | ||
| 556 | const is_64bit: bool = switch (rel.r_length) { | ||
| 557 | 3 => true, | ||
| 558 | 2 => false, | ||
| 559 | else => unreachable, | ||
| 560 | }; | ||
| 561 | const offset = @intCast(u32, rel.r_address); | ||
| 562 | const addend: i64 = if (is_64bit) | ||
| 563 | mem.readIntLittle(i64, parser.code[offset..][0..8]) | ||
| 564 | else | ||
| 565 | mem.readIntLittle(i32, parser.code[offset..][0..4]); | ||
| 566 | |||
| 567 | unsigned.* = .{ | ||
| 568 | .base = .{ | ||
| 569 | .@"type" = .unsigned, | ||
| 570 | .code = if (is_64bit) parser.code[offset..][0..8] else parser.code[offset..][0..4], | ||
| 571 | .offset = offset, | ||
| 572 | .target = target, | ||
| 573 | }, | ||
| 574 | .subtractor = parser.subtractor, | ||
| 575 | .is_64bit = is_64bit, | ||
| 576 | .addend = addend, | ||
| 577 | }; | ||
| 578 | |||
| 579 | log.debug(" | emitting {}", .{unsigned}); | ||
| 580 | try parser.parsed.append(&unsigned.base); | ||
| 581 | } | ||
| 582 | }; | ||
| 583 | |||
| 584 | fn isArithmeticOp(inst: *const [4]u8) callconv(.Inline) bool { | ||
| 585 | const group_decode = @truncate(u5, inst[3]); | ||
| 586 | return ((group_decode >> 2) == 4); | ||
| 587 | } | ||
src/link/MachO/reloc/x86_64.zig created+343| ... | @@ -0,0 +1,343 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const log = std.log.scoped(.reloc); | ||
| 4 | const macho = std.macho; | ||
| 5 | const math = std.math; | ||
| 6 | const mem = std.mem; | ||
| 7 | const meta = std.meta; | ||
| 8 | const reloc = @import("../reloc.zig"); | ||
| 9 | |||
| 10 | const Allocator = mem.Allocator; | ||
| 11 | const Relocation = reloc.Relocation; | ||
| 12 | |||
| 13 | pub const Branch = struct { | ||
| 14 | base: Relocation, | ||
| 15 | |||
| 16 | pub const base_type: Relocation.Type = .branch_x86_64; | ||
| 17 | |||
| 18 | pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void { | ||
| 19 | const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | ||
| 20 | log.debug(" | displacement 0x{x}", .{displacement}); | ||
| 21 | mem.writeIntLittle(u32, branch.base.code[0..4], @bitCast(u32, displacement)); | ||
| 22 | } | ||
| 23 | }; | ||
| 24 | |||
| 25 | pub const Signed = struct { | ||
| 26 | base: Relocation, | ||
| 27 | addend: i32, | ||
| 28 | correction: i4, | ||
| 29 | |||
| 30 | pub const base_type: Relocation.Type = .signed; | ||
| 31 | |||
| 32 | pub fn resolve(signed: Signed, args: Relocation.ResolveArgs) !void { | ||
| 33 | const target_addr = target_addr: { | ||
| 34 | if (signed.base.target == .section) { | ||
| 35 | const source_target = @intCast(i64, signed.base.offset) + signed.addend + 4 + signed.correction; | ||
| 36 | const source_disp = source_target - @intCast(i64, args.source_sect_addr.?); | ||
| 37 | break :target_addr @intCast(i64, args.target_addr) + source_disp; | ||
| 38 | } | ||
| 39 | break :target_addr @intCast(i64, args.target_addr) + signed.addend; | ||
| 40 | }; | ||
| 41 | const displacement = try math.cast(i32, target_addr - @intCast(i64, args.source_addr) - signed.correction - 4); | ||
| 42 | |||
| 43 | log.debug(" | calculated addend 0x{x}", .{signed.addend}); | ||
| 44 | log.debug(" | calculated correction 0x{x}", .{signed.correction}); | ||
| 45 | log.debug(" | displacement 0x{x}", .{displacement}); | ||
| 46 | |||
| 47 | mem.writeIntLittle(u32, signed.base.code[0..4], @bitCast(u32, displacement)); | ||
| 48 | } | ||
| 49 | }; | ||
| 50 | |||
| 51 | pub const GotLoad = struct { | ||
| 52 | base: Relocation, | ||
| 53 | op: *u8, | ||
| 54 | |||
| 55 | pub const base_type: Relocation.Type = .got_load; | ||
| 56 | |||
| 57 | pub fn resolve(got_load: GotLoad, args: Relocation.ResolveArgs) !void { | ||
| 58 | const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | ||
| 59 | log.debug(" | displacement 0x{x}", .{displacement}); | ||
| 60 | mem.writeIntLittle(u32, got_load.base.code[0..4], @bitCast(u32, displacement)); | ||
| 61 | } | ||
| 62 | }; | ||
| 63 | |||
| 64 | pub const Got = struct { | ||
| 65 | base: Relocation, | ||
| 66 | |||
| 67 | pub const base_type: Relocation.Type = .got; | ||
| 68 | |||
| 69 | pub fn resolve(got: Got, args: Relocation.ResolveArgs) !void { | ||
| 70 | const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | ||
| 71 | log.debug(" | displacement 0x{x}", .{displacement}); | ||
| 72 | mem.writeIntLittle(u32, got.base.code[0..4], @bitCast(u32, displacement)); | ||
| 73 | } | ||
| 74 | }; | ||
| 75 | |||
| 76 | pub const Tlv = struct { | ||
| 77 | base: Relocation, | ||
| 78 | op: *u8, | ||
| 79 | |||
| 80 | pub const base_type: Relocation.Type = .tlv; | ||
| 81 | |||
| 82 | pub fn resolve(tlv: Tlv, args: Relocation.ResolveArgs) !void { | ||
| 83 | // We need to rewrite the opcode from movq to leaq. | ||
| 84 | tlv.op.* = 0x8d; | ||
| 85 | log.debug(" | rewriting op to leaq", .{}); | ||
| 86 | |||
| 87 | const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | ||
| 88 | log.debug(" | displacement 0x{x}", .{displacement}); | ||
| 89 | |||
| 90 | mem.writeIntLittle(u32, tlv.base.code[0..4], @bitCast(u32, displacement)); | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | |||
| 94 | pub const Parser = struct { | ||
| 95 | allocator: *Allocator, | ||
| 96 | it: *reloc.RelocIterator, | ||
| 97 | code: []u8, | ||
| 98 | parsed: std.ArrayList(*Relocation), | ||
| 99 | subtractor: ?Relocation.Target = null, | ||
| 100 | |||
| 101 | pub fn deinit(parser: *Parser) void { | ||
| 102 | parser.parsed.deinit(); | ||
| 103 | } | ||
| 104 | |||
| 105 | pub fn parse(parser: *Parser) !void { | ||
| 106 | while (parser.it.next()) |rel| { | ||
| 107 | switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { | ||
| 108 | .X86_64_RELOC_BRANCH => { | ||
| 109 | try parser.parseBranch(rel); | ||
| 110 | }, | ||
| 111 | .X86_64_RELOC_SUBTRACTOR => { | ||
| 112 | try parser.parseSubtractor(rel); | ||
| 113 | }, | ||
| 114 | .X86_64_RELOC_UNSIGNED => { | ||
| 115 | try parser.parseUnsigned(rel); | ||
| 116 | }, | ||
| 117 | .X86_64_RELOC_SIGNED, | ||
| 118 | .X86_64_RELOC_SIGNED_1, | ||
| 119 | .X86_64_RELOC_SIGNED_2, | ||
| 120 | .X86_64_RELOC_SIGNED_4, | ||
| 121 | => { | ||
| 122 | try parser.parseSigned(rel); | ||
| 123 | }, | ||
| 124 | .X86_64_RELOC_GOT_LOAD => { | ||
| 125 | try parser.parseGotLoad(rel); | ||
| 126 | }, | ||
| 127 | .X86_64_RELOC_GOT => { | ||
| 128 | try parser.parseGot(rel); | ||
| 129 | }, | ||
| 130 | .X86_64_RELOC_TLV => { | ||
| 131 | try parser.parseTlv(rel); | ||
| 132 | }, | ||
| 133 | } | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | fn parseBranch(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 138 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | ||
| 139 | assert(rel_type == .X86_64_RELOC_BRANCH); | ||
| 140 | assert(rel.r_pcrel == 1); | ||
| 141 | assert(rel.r_length == 2); | ||
| 142 | |||
| 143 | const offset = @intCast(u32, rel.r_address); | ||
| 144 | const inst = parser.code[offset..][0..4]; | ||
| 145 | |||
| 146 | var branch = try parser.allocator.create(Branch); | ||
| 147 | errdefer parser.allocator.destroy(branch); | ||
| 148 | |||
| 149 | const target = Relocation.Target.from_reloc(rel); | ||
| 150 | |||
| 151 | branch.* = .{ | ||
| 152 | .base = .{ | ||
| 153 | .@"type" = .branch_x86_64, | ||
| 154 | .code = inst, | ||
| 155 | .offset = offset, | ||
| 156 | .target = target, | ||
| 157 | }, | ||
| 158 | }; | ||
| 159 | |||
| 160 | log.debug(" | emitting {}", .{branch}); | ||
| 161 | try parser.parsed.append(&branch.base); | ||
| 162 | } | ||
| 163 | |||
| 164 | fn parseSigned(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 165 | assert(rel.r_pcrel == 1); | ||
| 166 | assert(rel.r_length == 2); | ||
| 167 | |||
| 168 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | ||
| 169 | const target = Relocation.Target.from_reloc(rel); | ||
| 170 | const is_extern = rel.r_extern == 1; | ||
| 171 | |||
| 172 | const offset = @intCast(u32, rel.r_address); | ||
| 173 | const inst = parser.code[offset..][0..4]; | ||
| 174 | const addend = mem.readIntLittle(i32, inst); | ||
| 175 | |||
| 176 | const correction: i4 = correction: { | ||
| 177 | if (is_extern) break :correction 0; | ||
| 178 | |||
| 179 | const corr: i4 = switch (rel_type) { | ||
| 180 | .X86_64_RELOC_SIGNED => 0, | ||
| 181 | .X86_64_RELOC_SIGNED_1 => 1, | ||
| 182 | .X86_64_RELOC_SIGNED_2 => 2, | ||
| 183 | .X86_64_RELOC_SIGNED_4 => 4, | ||
| 184 | else => unreachable, | ||
| 185 | }; | ||
| 186 | break :correction corr; | ||
| 187 | }; | ||
| 188 | |||
| 189 | var signed = try parser.allocator.create(Signed); | ||
| 190 | errdefer parser.allocator.destroy(signed); | ||
| 191 | |||
| 192 | signed.* = .{ | ||
| 193 | .base = .{ | ||
| 194 | .@"type" = .signed, | ||
| 195 | .code = inst, | ||
| 196 | .offset = offset, | ||
| 197 | .target = target, | ||
| 198 | }, | ||
| 199 | .addend = addend, | ||
| 200 | .correction = correction, | ||
| 201 | }; | ||
| 202 | |||
| 203 | log.debug(" | emitting {}", .{signed}); | ||
| 204 | try parser.parsed.append(&signed.base); | ||
| 205 | } | ||
| 206 | |||
| 207 | fn parseGotLoad(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 208 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | ||
| 209 | assert(rel_type == .X86_64_RELOC_GOT_LOAD); | ||
| 210 | assert(rel.r_pcrel == 1); | ||
| 211 | assert(rel.r_length == 2); | ||
| 212 | |||
| 213 | const offset = @intCast(u32, rel.r_address); | ||
| 214 | const inst = parser.code[offset..][0..4]; | ||
| 215 | const target = Relocation.Target.from_reloc(rel); | ||
| 216 | |||
| 217 | var got_load = try parser.allocator.create(GotLoad); | ||
| 218 | errdefer parser.allocator.destroy(got_load); | ||
| 219 | |||
| 220 | got_load.* = .{ | ||
| 221 | .base = .{ | ||
| 222 | .@"type" = .got_load, | ||
| 223 | .code = inst, | ||
| 224 | .offset = offset, | ||
| 225 | .target = target, | ||
| 226 | }, | ||
| 227 | .op = &parser.code[offset - 2], | ||
| 228 | }; | ||
| 229 | |||
| 230 | log.debug(" | emitting {}", .{got_load}); | ||
| 231 | try parser.parsed.append(&got_load.base); | ||
| 232 | } | ||
| 233 | |||
| 234 | fn parseGot(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 235 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | ||
| 236 | assert(rel_type == .X86_64_RELOC_GOT); | ||
| 237 | assert(rel.r_pcrel == 1); | ||
| 238 | assert(rel.r_length == 2); | ||
| 239 | |||
| 240 | const offset = @intCast(u32, rel.r_address); | ||
| 241 | const inst = parser.code[offset..][0..4]; | ||
| 242 | const target = Relocation.Target.from_reloc(rel); | ||
| 243 | |||
| 244 | var got = try parser.allocator.create(Got); | ||
| 245 | errdefer parser.allocator.destroy(got); | ||
| 246 | |||
| 247 | got.* = .{ | ||
| 248 | .base = .{ | ||
| 249 | .@"type" = .got, | ||
| 250 | .code = inst, | ||
| 251 | .offset = offset, | ||
| 252 | .target = target, | ||
| 253 | }, | ||
| 254 | }; | ||
| 255 | |||
| 256 | log.debug(" | emitting {}", .{got}); | ||
| 257 | try parser.parsed.append(&got.base); | ||
| 258 | } | ||
| 259 | |||
| 260 | fn parseTlv(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 261 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | ||
| 262 | assert(rel_type == .X86_64_RELOC_TLV); | ||
| 263 | assert(rel.r_pcrel == 1); | ||
| 264 | assert(rel.r_length == 2); | ||
| 265 | |||
| 266 | const offset = @intCast(u32, rel.r_address); | ||
| 267 | const inst = parser.code[offset..][0..4]; | ||
| 268 | const target = Relocation.Target.from_reloc(rel); | ||
| 269 | |||
| 270 | var tlv = try parser.allocator.create(Tlv); | ||
| 271 | errdefer parser.allocator.destroy(tlv); | ||
| 272 | |||
| 273 | tlv.* = .{ | ||
| 274 | .base = .{ | ||
| 275 | .@"type" = .tlv, | ||
| 276 | .code = inst, | ||
| 277 | .offset = offset, | ||
| 278 | .target = target, | ||
| 279 | }, | ||
| 280 | .op = &parser.code[offset - 2], | ||
| 281 | }; | ||
| 282 | |||
| 283 | log.debug(" | emitting {}", .{tlv}); | ||
| 284 | try parser.parsed.append(&tlv.base); | ||
| 285 | } | ||
| 286 | |||
| 287 | fn parseSubtractor(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 288 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | ||
| 289 | assert(rel_type == .X86_64_RELOC_SUBTRACTOR); | ||
| 290 | assert(rel.r_pcrel == 0); | ||
| 291 | assert(parser.subtractor == null); | ||
| 292 | |||
| 293 | parser.subtractor = Relocation.Target.from_reloc(rel); | ||
| 294 | |||
| 295 | // Verify SUBTRACTOR is followed by UNSIGNED. | ||
| 296 | const next = @intToEnum(macho.reloc_type_x86_64, parser.it.peek().r_type); | ||
| 297 | if (next != .X86_64_RELOC_UNSIGNED) { | ||
| 298 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | ||
| 299 | return error.UnexpectedRelocationType; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | fn parseUnsigned(parser: *Parser, rel: macho.relocation_info) !void { | ||
| 304 | defer { | ||
| 305 | // Reset parser's subtractor state | ||
| 306 | parser.subtractor = null; | ||
| 307 | } | ||
| 308 | |||
| 309 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | ||
| 310 | assert(rel_type == .X86_64_RELOC_UNSIGNED); | ||
| 311 | assert(rel.r_pcrel == 0); | ||
| 312 | |||
| 313 | var unsigned = try parser.allocator.create(reloc.Unsigned); | ||
| 314 | errdefer parser.allocator.destroy(unsigned); | ||
| 315 | |||
| 316 | const target = Relocation.Target.from_reloc(rel); | ||
| 317 | const is_64bit: bool = switch (rel.r_length) { | ||
| 318 | 3 => true, | ||
| 319 | 2 => false, | ||
| 320 | else => unreachable, | ||
| 321 | }; | ||
| 322 | const offset = @intCast(u32, rel.r_address); | ||
| 323 | const addend: i64 = if (is_64bit) | ||
| 324 | mem.readIntLittle(i64, parser.code[offset..][0..8]) | ||
| 325 | else | ||
| 326 | mem.readIntLittle(i32, parser.code[offset..][0..4]); | ||
| 327 | |||
| 328 | unsigned.* = .{ | ||
| 329 | .base = .{ | ||
| 330 | .@"type" = .unsigned, | ||
| 331 | .code = if (is_64bit) parser.code[offset..][0..8] else parser.code[offset..][0..4], | ||
| 332 | .offset = offset, | ||
| 333 | .target = target, | ||
| 334 | }, | ||
| 335 | .subtractor = parser.subtractor, | ||
| 336 | .is_64bit = is_64bit, | ||
| 337 | .addend = addend, | ||
| 338 | }; | ||
| 339 | |||
| 340 | log.debug(" | emitting {}", .{unsigned}); | ||
| 341 | try parser.parsed.append(&unsigned.base); | ||
| 342 | } | ||
| 343 | }; | ||