| author | |
| committer | |
| log | dbd2eb7c7f9267e8ae508d0995c1d4c5a3b46309 |
| tree | 9f1749c1ed5cebc47e4813a6f3fceb3e0101ccb9 |
| parent | 15b85df3dd8a754bc26159ea2202781b748a613e |
6 files changed, 761 insertions(+), 1211 deletions(-)
CMakeLists.txt-2| ... | ... | @@ -588,8 +588,6 @@ set(ZIG_STAGE2_SOURCES |
| 588 | 588 | "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig" |
| 589 | 589 | "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig" |
| 590 | 590 | "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc.zig" |
| 591 | "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc/aarch64.zig" | |
| 592 | "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc/x86_64.zig" | |
| 593 | 591 | "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig" |
| 594 | 592 | "${CMAKE_SOURCE_DIR}/src/link/tapi.zig" |
| 595 | 593 | "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig" |
src/link/MachO/Object.zig+10-27| ... | ... | @@ -408,7 +408,6 @@ const TextBlockParser = struct { |
| 408 | 408 | |
| 409 | 409 | const start_addr = senior_nlist.nlist.n_value - self.section.addr; |
| 410 | 410 | const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size; |
| 411 | log.warn("{} - {}", .{ start_addr, end_addr }); | |
| 412 | 411 | |
| 413 | 412 | const code = self.code[start_addr..end_addr]; |
| 414 | 413 | const size = code.len; |
| ... | ... | @@ -430,7 +429,7 @@ const TextBlockParser = struct { |
| 430 | 429 | .aliases = alias_only_indices, |
| 431 | 430 | .references = std.AutoArrayHashMap(u32, void).init(self.allocator), |
| 432 | 431 | .code = try self.allocator.dupe(u8, code), |
| 433 | .relocs = std.ArrayList(*Relocation).init(self.allocator), | |
| 432 | .relocs = std.ArrayList(Relocation).init(self.allocator), | |
| 434 | 433 | .size = size, |
| 435 | 434 | .alignment = self.section.@"align", |
| 436 | 435 | }; |
| ... | ... | @@ -579,7 +578,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 579 | 578 | .local_sym_index = local_sym_index, |
| 580 | 579 | .references = std.AutoArrayHashMap(u32, void).init(self.allocator), |
| 581 | 580 | .code = try self.allocator.dupe(u8, code), |
| 582 | .relocs = std.ArrayList(*Relocation).init(self.allocator), | |
| 581 | .relocs = std.ArrayList(Relocation).init(self.allocator), | |
| 583 | 582 | .size = sect.size, |
| 584 | 583 | .alignment = sect.@"align", |
| 585 | 584 | }; |
| ... | ... | @@ -607,30 +606,14 @@ fn parseRelocs( |
| 607 | 606 | var it = reloc.RelocIterator{ |
| 608 | 607 | .buffer = relocs, |
| 609 | 608 | }; |
| 610 | ||
| 611 | switch (self.arch.?) { | |
| 612 | .aarch64 => { | |
| 613 | var parser = reloc.aarch64.Parser{ | |
| 614 | .object = self, | |
| 615 | .zld = zld, | |
| 616 | .it = &it, | |
| 617 | .block = block, | |
| 618 | .base_addr = base_addr, | |
| 619 | }; | |
| 620 | try parser.parse(); | |
| 621 | }, | |
| 622 | .x86_64 => { | |
| 623 | var parser = reloc.x86_64.Parser{ | |
| 624 | .object = self, | |
| 625 | .zld = zld, | |
| 626 | .it = &it, | |
| 627 | .block = block, | |
| 628 | .base_addr = base_addr, | |
| 629 | }; | |
| 630 | try parser.parse(); | |
| 631 | }, | |
| 632 | else => unreachable, | |
| 633 | } | |
| 609 | var parser = reloc.Parser{ | |
| 610 | .object = self, | |
| 611 | .zld = zld, | |
| 612 | .it = &it, | |
| 613 | .block = block, | |
| 614 | .base_addr = base_addr, | |
| 615 | }; | |
| 616 | try parser.parse(); | |
| 634 | 617 | } |
| 635 | 618 | |
| 636 | 619 | pub fn symbolFromReloc(self: *Object, rel: macho.relocation_info) !*Symbol { |
src/link/MachO/Zld.zig+2-60| ... | ... | @@ -137,7 +137,7 @@ pub const TextBlock = struct { |
| 137 | 137 | aliases: ?[]u32 = null, |
| 138 | 138 | references: std.AutoArrayHashMap(u32, void), |
| 139 | 139 | code: []u8, |
| 140 | relocs: std.ArrayList(*Relocation), | |
| 140 | relocs: std.ArrayList(Relocation), | |
| 141 | 141 | size: u64, |
| 142 | 142 | alignment: u32, |
| 143 | 143 | next: ?*TextBlock = null, |
| ... | ... | @@ -1604,7 +1604,7 @@ fn resolveSymbols(self: *Zld) !void { |
| 1604 | 1604 | .local_sym_index = local_sym_index, |
| 1605 | 1605 | .references = std.AutoArrayHashMap(u32, void).init(self.allocator), |
| 1606 | 1606 | .code = code, |
| 1607 | .relocs = std.ArrayList(*Relocation).init(self.allocator), | |
| 1607 | .relocs = std.ArrayList(Relocation).init(self.allocator), | |
| 1608 | 1608 | .size = size, |
| 1609 | 1609 | .alignment = alignment, |
| 1610 | 1610 | }; |
| ... | ... | @@ -1871,64 +1871,6 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { |
| 1871 | 1871 | } |
| 1872 | 1872 | } |
| 1873 | 1873 | |
| 1874 | fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.Target) !u64 { | |
| 1875 | const target_addr = blk: { | |
| 1876 | switch (target) { | |
| 1877 | .symbol => |sym_id| { | |
| 1878 | const sym = object.symbols.items[sym_id]; | |
| 1879 | switch (sym.payload) { | |
| 1880 | .regular => |reg| { | |
| 1881 | log.debug(" | regular '{s}'", .{sym.name}); | |
| 1882 | break :blk reg.address; | |
| 1883 | }, | |
| 1884 | .proxy => |proxy| { | |
| 1885 | if (mem.eql(u8, sym.name, "__tlv_bootstrap")) { | |
| 1886 | log.debug(" | symbol '__tlv_bootstrap'", .{}); | |
| 1887 | const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1888 | const tlv = segment.sections.items[self.tlv_section_index.?]; | |
| 1889 | break :blk tlv.addr; | |
| 1890 | } | |
| 1891 | ||
| 1892 | log.debug(" | symbol stub '{s}'", .{sym.name}); | |
| 1893 | const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1894 | const stubs = segment.sections.items[self.stubs_section_index.?]; | |
| 1895 | const stubs_index = sym.stubs_index orelse { | |
| 1896 | if (proxy.bind_info.items.len > 0) { | |
| 1897 | break :blk 0; // Dynamically bound by dyld. | |
| 1898 | } | |
| 1899 | log.err( | |
| 1900 | "expected stubs index or dynamic bind address when relocating symbol '{s}'", | |
| 1901 | .{sym.name}, | |
| 1902 | ); | |
| 1903 | log.err("this is an internal linker error", .{}); | |
| 1904 | return error.FailedToResolveRelocationTarget; | |
| 1905 | }; | |
| 1906 | break :blk stubs.addr + stubs_index * stubs.reserved2; | |
| 1907 | }, | |
| 1908 | else => { | |
| 1909 | log.err("failed to resolve symbol '{s}' as a relocation target", .{sym.name}); | |
| 1910 | log.err("this is an internal linker error", .{}); | |
| 1911 | return error.FailedToResolveRelocationTarget; | |
| 1912 | }, | |
| 1913 | } | |
| 1914 | }, | |
| 1915 | .section => |sect_id| { | |
| 1916 | log.debug(" | section offset", .{}); | |
| 1917 | const source_sect = object.sections.items[sect_id]; | |
| 1918 | log.debug(" | section '{s},{s}'", .{ | |
| 1919 | segmentName(source_sect.inner), | |
| 1920 | sectionName(source_sect.inner), | |
| 1921 | }); | |
| 1922 | const target_map = source_sect.target_map orelse unreachable; | |
| 1923 | const target_seg = self.load_commands.items[target_map.segment_id].Segment; | |
| 1924 | const target_sect = target_seg.sections.items[target_map.section_id]; | |
| 1925 | break :blk target_sect.addr + target_map.offset; | |
| 1926 | }, | |
| 1927 | } | |
| 1928 | }; | |
| 1929 | return target_addr; | |
| 1930 | } | |
| 1931 | ||
| 1932 | 1874 | fn populateMetadata(self: *Zld) !void { |
| 1933 | 1875 | if (self.pagezero_segment_cmd_index == null) { |
| 1934 | 1876 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
src/link/MachO/reloc.zig+749-119| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const aarch64 = @import("../../codegen/aarch64.zig"); | |
| 2 | 3 | const assert = std.debug.assert; |
| 3 | 4 | const log = std.log.scoped(.reloc); |
| 4 | 5 | const macho = std.macho; |
| ... | ... | @@ -6,141 +7,431 @@ const math = std.math; |
| 6 | 7 | const mem = std.mem; |
| 7 | 8 | const meta = std.meta; |
| 8 | 9 | |
| 9 | pub const aarch64 = @import("reloc/aarch64.zig"); | |
| 10 | pub const x86_64 = @import("reloc/x86_64.zig"); | |
| 11 | ||
| 12 | 10 | const Allocator = mem.Allocator; |
| 11 | const Arch = std.Target.Cpu.Arch; | |
| 12 | const Object = @import("Object.zig"); | |
| 13 | 13 | const Symbol = @import("Symbol.zig"); |
| 14 | const TextBlock = @import("Zld.zig").TextBlock; | |
| 14 | const TextBlock = Zld.TextBlock; | |
| 15 | const Zld = @import("Zld.zig"); | |
| 15 | 16 | |
| 16 | 17 | pub const Relocation = struct { |
| 17 | @"type": Type, | |
| 18 | /// Offset within the `block`s code buffer. | |
| 19 | /// Note relocation size can be inferred by relocation's kind. | |
| 18 | 20 | offset: u32, |
| 21 | ||
| 22 | /// Parent block containing this relocation. | |
| 19 | 23 | block: *TextBlock, |
| 24 | ||
| 25 | /// Target symbol: either a regular or a proxy. | |
| 20 | 26 | target: *Symbol, |
| 21 | 27 | |
| 22 | pub fn cast(base: *Relocation, comptime T: type) ?*T { | |
| 23 | if (base.@"type" != T.base_type) | |
| 24 | return null; | |
| 28 | payload: union(enum) { | |
| 29 | unsigned: Unsigned, | |
| 30 | branch: Branch, | |
| 31 | page: Page, | |
| 32 | page_off: PageOff, | |
| 33 | pointer_to_got: PointerToGot, | |
| 34 | signed: Signed, | |
| 35 | load: Load, | |
| 36 | }, | |
| 25 | 37 | |
| 26 | return @fieldParentPtr(T, "base", base); | |
| 27 | } | |
| 38 | pub const Unsigned = struct { | |
| 39 | subtractor: ?*Symbol = null, | |
| 40 | ||
| 41 | /// Addend embedded directly in the relocation slot | |
| 42 | addend: i64, | |
| 43 | ||
| 44 | /// Extracted from r_length: | |
| 45 | /// => 3 implies true | |
| 46 | /// => 2 implies false | |
| 47 | /// => * is unreachable | |
| 48 | is_64bit: bool, | |
| 49 | ||
| 50 | pub fn resolve(self: Unsigned, base: Relocation, source_addr: u64, target_addr: u64) !void { | |
| 51 | // const addend = if (unsigned.base.target == .section) | |
| 52 | // unsigned.addend - @intCast(i64, args.source_target_sect_addr.?) | |
| 53 | // else | |
| 54 | // unsigned.addend; | |
| 55 | ||
| 56 | // const result = if (args.subtractor) |subtractor| | |
| 57 | // @intCast(i64, args.target_addr) - @intCast(i64, subtractor) + addend | |
| 58 | // else | |
| 59 | // @intCast(i64, args.target_addr) + addend; | |
| 60 | ||
| 61 | // log.debug(" | calculated addend 0x{x}", .{addend}); | |
| 62 | // log.debug(" | calculated unsigned value 0x{x}", .{result}); | |
| 63 | ||
| 64 | // if (unsigned.is_64bit) { | |
| 65 | // mem.writeIntLittle( | |
| 66 | // u64, | |
| 67 | // unsigned.base.code[0..8], | |
| 68 | // @bitCast(u64, result), | |
| 69 | // ); | |
| 70 | // } else { | |
| 71 | // mem.writeIntLittle( | |
| 72 | // u32, | |
| 73 | // unsigned.base.code[0..4], | |
| 74 | // @truncate(u32, @bitCast(u64, result)), | |
| 75 | // ); | |
| 76 | // } | |
| 77 | } | |
| 28 | 78 | |
| 29 | // pub fn resolve(base: *Relocation) !void { | |
| 30 | // return switch (base.@"type") { | |
| 31 | // .unsigned => @fieldParentPtr(Unsigned, "base", base).resolve(), | |
| 32 | // .branch_aarch64 => @fieldParentPtr(aarch64.Branch, "base", base).resolve(), | |
| 33 | // .page => @fieldParentPtr(aarch64.Page, "base", base).resolve(), | |
| 34 | // .page_off => @fieldParentPtr(aarch64.PageOff, "base", base).resolve(), | |
| 35 | // .got_page => @fieldParentPtr(aarch64.GotPage, "base", base).resolve(), | |
| 36 | // .got_page_off => @fieldParentPtr(aarch64.GotPageOff, "base", base).resolve(), | |
| 37 | // .pointer_to_got => @fieldParentPtr(aarch64.PointerToGot, "base", base).resolve(), | |
| 38 | // .tlvp_page => @fieldParentPtr(aarch64.TlvpPage, "base", base).resolve(), | |
| 39 | // .tlvp_page_off => @fieldParentPtr(aarch64.TlvpPageOff, "base", base).resolve(), | |
| 40 | // .branch_x86_64 => @fieldParentPtr(x86_64.Branch, "base", base).resolve(), | |
| 41 | // .signed => @fieldParentPtr(x86_64.Signed, "base", base).resolve(), | |
| 42 | // .got_load => @fieldParentPtr(x86_64.GotLoad, "base", base).resolve(), | |
| 43 | // .got => @fieldParentPtr(x86_64.Got, "base", base).resolve(), | |
| 44 | // .tlv => @fieldParentPtr(x86_64.Tlv, "base", base).resolve(), | |
| 45 | // }; | |
| 46 | // } | |
| 47 | ||
| 48 | pub const Type = enum { | |
| 49 | branch_aarch64, | |
| 50 | unsigned, | |
| 51 | page, | |
| 52 | page_off, | |
| 53 | got_page, | |
| 54 | got_page_off, | |
| 55 | tlvp_page, | |
| 56 | pointer_to_got, | |
| 57 | tlvp_page_off, | |
| 58 | branch_x86_64, | |
| 59 | signed, | |
| 60 | got_load, | |
| 61 | got, | |
| 62 | tlv, | |
| 79 | pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 80 | _ = fmt; | |
| 81 | _ = options; | |
| 82 | try std.fmt.format(writer, "Unsigned {{ ", .{}); | |
| 83 | if (self.subtractor) |sub| { | |
| 84 | try std.fmt.format(writer, ".subtractor = {}, ", .{sub}); | |
| 85 | } | |
| 86 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 87 | const length: usize = if (self.is_64bit) 8 else 4; | |
| 88 | try std.fmt.format(writer, ".length = {}, ", .{length}); | |
| 89 | try std.fmt.format(writer, "}}", .{}); | |
| 90 | } | |
| 63 | 91 | }; |
| 64 | 92 | |
| 65 | pub fn format(base: *const Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 66 | try std.fmt.format(writer, "Relocation {{ ", .{}); | |
| 67 | try std.fmt.format(writer, ".type = {s}, ", .{base.@"type"}); | |
| 68 | try std.fmt.format(writer, ".offset = {}, ", .{base.offset}); | |
| 69 | try std.fmt.format(writer, ".block = {}", .{base.block.local_sym_index}); | |
| 70 | try std.fmt.format(writer, ".target = {}, ", .{base.target}); | |
| 71 | ||
| 72 | try switch (base.@"type") { | |
| 73 | .unsigned => @fieldParentPtr(Unsigned, "base", base).format(fmt, options, writer), | |
| 74 | .branch_aarch64 => @fieldParentPtr(aarch64.Branch, "base", base).format(fmt, options, writer), | |
| 75 | .page => @fieldParentPtr(aarch64.Page, "base", base).format(fmt, options, writer), | |
| 76 | .page_off => @fieldParentPtr(aarch64.PageOff, "base", base).format(fmt, options, writer), | |
| 77 | .got_page => @fieldParentPtr(aarch64.GotPage, "base", base).format(fmt, options, writer), | |
| 78 | .got_page_off => @fieldParentPtr(aarch64.GotPageOff, "base", base).format(fmt, options, writer), | |
| 79 | .pointer_to_got => @fieldParentPtr(aarch64.PointerToGot, "base", base).format(fmt, options, writer), | |
| 80 | .tlvp_page => @fieldParentPtr(aarch64.TlvpPage, "base", base).format(fmt, options, writer), | |
| 81 | .tlvp_page_off => @fieldParentPtr(aarch64.TlvpPageOff, "base", base).format(fmt, options, writer), | |
| 82 | .branch_x86_64 => @fieldParentPtr(x86_64.Branch, "base", base).format(fmt, options, writer), | |
| 83 | .signed => @fieldParentPtr(x86_64.Signed, "base", base).format(fmt, options, writer), | |
| 84 | .got_load => @fieldParentPtr(x86_64.GotLoad, "base", base).format(fmt, options, writer), | |
| 85 | .got => @fieldParentPtr(x86_64.Got, "base", base).format(fmt, options, writer), | |
| 86 | .tlv => @fieldParentPtr(x86_64.Tlv, "base", base).format(fmt, options, writer), | |
| 93 | pub const Branch = struct { | |
| 94 | arch: Arch, | |
| 95 | ||
| 96 | pub fn resolve(self: Branch, base: Relocation, source_addr: u64, target_addr: u64) !void { | |
| 97 | switch (arch) { | |
| 98 | .aarch64 => { | |
| 99 | const displacement = try math.cast(i28, @intCast(i64, target_addr) - @intCast(i64, source_addr)); | |
| 100 | var inst = aarch64.Instruction{ | |
| 101 | .unconditional_branch_immediate = mem.bytesToValue( | |
| 102 | meta.TagPayload( | |
| 103 | aarch.Instruction, | |
| 104 | aarch64.Instruction.unconditional_branch_immediate, | |
| 105 | ), | |
| 106 | base.block.code[base.offset..][0..4], | |
| 107 | ), | |
| 108 | }; | |
| 109 | inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); | |
| 110 | mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32()); | |
| 111 | }, | |
| 112 | .x86_64 => { | |
| 113 | const displacement = try math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4); | |
| 114 | mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, displacement)); | |
| 115 | }, | |
| 116 | else => return error.UnsupportedCpuArchitecture, | |
| 117 | } | |
| 118 | } | |
| 119 | ||
| 120 | pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 121 | _ = fmt; | |
| 122 | _ = options; | |
| 123 | try std.fmt.format(writer, "Branch {{}}", .{}); | |
| 124 | } | |
| 125 | }; | |
| 126 | ||
| 127 | pub const Page = struct { | |
| 128 | kind: enum { | |
| 129 | page, | |
| 130 | got, | |
| 131 | tlvp, | |
| 132 | }, | |
| 133 | addend: ?u32 = null, | |
| 134 | ||
| 135 | pub fn resolve(self: Page, base: Relocation, source_addr: u64, target_addr: u64) !void { | |
| 136 | const actual_target_addr = if (self.addend) |addend| target_addr + addend else target_addr; | |
| 137 | const source_page = @intCast(i32, source_addr >> 12); | |
| 138 | const target_page = @intCast(i32, actual_target_addr >> 12); | |
| 139 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 140 | ||
| 141 | var inst = aarch64.Instruction{ | |
| 142 | .pc_relative_address = mem.bytesToValue( | |
| 143 | meta.TagPayload( | |
| 144 | aarch64.Instruction, | |
| 145 | aarch64.Instruction.pc_relative_address, | |
| 146 | ), | |
| 147 | base.block.code[base.offset..][0..4], | |
| 148 | ), | |
| 149 | }; | |
| 150 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 151 | inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 152 | ||
| 153 | mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32()); | |
| 154 | } | |
| 155 | ||
| 156 | pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 157 | _ = fmt; | |
| 158 | _ = options; | |
| 159 | try std.fmt.format(writer, "Page {{ ", .{}); | |
| 160 | switch (self.kind) { | |
| 161 | .page => {}, | |
| 162 | .got => { | |
| 163 | try std.fmt.format(writer, ".got, ", .{}); | |
| 164 | }, | |
| 165 | .tlvp => { | |
| 166 | try std.fmt.format(writer, ".tlvp", .{}); | |
| 167 | }, | |
| 168 | } | |
| 169 | if (self.addend) |add| { | |
| 170 | try std.fmt.format(writer, ".addend = {}, ", .{add}); | |
| 171 | } | |
| 172 | try std.fmt.format(writer, "}}", .{}); | |
| 173 | } | |
| 174 | }; | |
| 175 | ||
| 176 | pub const PageOff = struct { | |
| 177 | kind: enum { | |
| 178 | page, | |
| 179 | got, | |
| 180 | tlvp, | |
| 181 | }, | |
| 182 | addend: ?u32 = null, | |
| 183 | op_kind: ?OpKind = null, | |
| 184 | ||
| 185 | pub const OpKind = enum { | |
| 186 | arithmetic, | |
| 187 | load, | |
| 87 | 188 | }; |
| 88 | 189 | |
| 89 | try std.fmt.format(writer, "}}", .{}); | |
| 190 | pub fn resolve(self: PageOff, base: Relocation, source_addr: u64, target_addr: u64) !void { | |
| 191 | switch (self.kind) { | |
| 192 | .page => { | |
| 193 | // const target_addr = if (page_off.addend) |addend| args.target_addr + addend else args.target_addr; | |
| 194 | // const narrowed = @truncate(u12, target_addr); | |
| 195 | ||
| 196 | // log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 197 | // log.debug(" | {s} opcode", .{page_off.op_kind}); | |
| 198 | ||
| 199 | // var inst = page_off.inst; | |
| 200 | // if (page_off.op_kind == .arithmetic) { | |
| 201 | // inst.add_subtract_immediate.imm12 = narrowed; | |
| 202 | // } else { | |
| 203 | // const offset: u12 = blk: { | |
| 204 | // if (inst.load_store_register.size == 0) { | |
| 205 | // if (inst.load_store_register.v == 1) { | |
| 206 | // // 128-bit SIMD is scaled by 16. | |
| 207 | // break :blk try math.divExact(u12, narrowed, 16); | |
| 208 | // } | |
| 209 | // // Otherwise, 8-bit SIMD or ldrb. | |
| 210 | // break :blk narrowed; | |
| 211 | // } else { | |
| 212 | // const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size); | |
| 213 | // break :blk try math.divExact(u12, narrowed, denom); | |
| 214 | // } | |
| 215 | // }; | |
| 216 | // inst.load_store_register.offset = offset; | |
| 217 | // } | |
| 218 | ||
| 219 | // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 220 | ||
| 221 | }, | |
| 222 | .got => { | |
| 223 | // const narrowed = @truncate(u12, args.target_addr); | |
| 224 | ||
| 225 | // log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 226 | ||
| 227 | // var inst = page_off.inst; | |
| 228 | // const offset = try math.divExact(u12, narrowed, 8); | |
| 229 | // inst.load_store_register.offset = offset; | |
| 230 | ||
| 231 | // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 232 | }, | |
| 233 | .tlvp => { | |
| 234 | ||
| 235 | // const narrowed = @truncate(u12, args.target_addr); | |
| 236 | ||
| 237 | // log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 238 | ||
| 239 | // var inst = page_off.inst; | |
| 240 | // inst.add_subtract_immediate.imm12 = narrowed; | |
| 241 | ||
| 242 | // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 243 | }, | |
| 244 | } | |
| 245 | } | |
| 246 | ||
| 247 | pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 248 | _ = fmt; | |
| 249 | _ = options; | |
| 250 | try std.fmt.format(writer, "PageOff {{ ", .{}); | |
| 251 | switch (self.kind) { | |
| 252 | .page => {}, | |
| 253 | .got => { | |
| 254 | try std.fmt.format(writer, ".got, ", .{}); | |
| 255 | }, | |
| 256 | .tlvp => { | |
| 257 | try std.fmt.format(writer, ".tlvp, ", .{}); | |
| 258 | }, | |
| 259 | } | |
| 260 | if (self.addend) |add| { | |
| 261 | try std.fmt.format(writer, ".addend = {}, ", .{add}); | |
| 262 | } | |
| 263 | if (self.op_kind) |op| { | |
| 264 | try std.fmt.format(writer, ".op_kind = {s}, ", .{op}); | |
| 265 | } | |
| 266 | try std.fmt.format(writer, "}}", .{}); | |
| 267 | } | |
| 268 | }; | |
| 269 | ||
| 270 | pub const PointerToGot = struct { | |
| 271 | pub fn resolve(self: PointerToGot, base: Relocation, source_addr: u64, target_addr: u64) !void { | |
| 272 | const result = try math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)); | |
| 273 | mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, result)); | |
| 274 | } | |
| 275 | ||
| 276 | pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 277 | _ = fmt; | |
| 278 | _ = options; | |
| 279 | try std.fmt.format(writer, "PointerToGot {{}}", .{}); | |
| 280 | } | |
| 281 | }; | |
| 282 | ||
| 283 | pub const Signed = struct { | |
| 284 | addend: i32, | |
| 285 | correction: i4, | |
| 286 | ||
| 287 | pub fn resolve(self: Signed, base: Relocation, source_addr: u64, target_addr: u64) !void { | |
| 288 | // const target_addr = target_addr: { | |
| 289 | // if (signed.base.target == .section) { | |
| 290 | // const source_target = @intCast(i64, args.source_source_sect_addr.?) + @intCast(i64, signed.base.offset) + signed.addend + 4; | |
| 291 | // const source_disp = source_target - @intCast(i64, args.source_target_sect_addr.?); | |
| 292 | // break :target_addr @intCast(i64, args.target_addr) + source_disp; | |
| 293 | // } | |
| 294 | // break :target_addr @intCast(i64, args.target_addr) + signed.addend; | |
| 295 | // }; | |
| 296 | // const displacement = try math.cast( | |
| 297 | // i32, | |
| 298 | // target_addr - @intCast(i64, args.source_addr) - signed.correction - 4, | |
| 299 | // ); | |
| 300 | ||
| 301 | // log.debug(" | addend 0x{x}", .{signed.addend}); | |
| 302 | // log.debug(" | correction 0x{x}", .{signed.correction}); | |
| 303 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 304 | ||
| 305 | // mem.writeIntLittle(u32, signed.base.code[0..4], @bitCast(u32, displacement)); | |
| 306 | } | |
| 307 | ||
| 308 | pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 309 | _ = fmt; | |
| 310 | _ = options; | |
| 311 | try std.fmt.format(writer, "Signed {{ ", .{}); | |
| 312 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 313 | try std.fmt.format(writer, ".correction = {}, ", .{self.correction}); | |
| 314 | try std.fmt.format(writer, "}}", .{}); | |
| 315 | } | |
| 316 | }; | |
| 317 | ||
| 318 | pub const Load = struct { | |
| 319 | kind: enum { | |
| 320 | got, | |
| 321 | tlvp, | |
| 322 | }, | |
| 323 | addend: ?i32 = null, | |
| 324 | ||
| 325 | pub fn resolve(self: Load, base: Relocation, source_addr: u64, target_addr: u64) !void { | |
| 326 | if (self.kind == .tlvp) { | |
| 327 | // We need to rewrite the opcode from movq to leaq. | |
| 328 | base.block.code[base.offset - 2] = 0x8d; | |
| 329 | } | |
| 330 | const addend = if (self.addend) |addend| addend else 0; | |
| 331 | const displacement = try math.cast( | |
| 332 | i32, | |
| 333 | @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + addend, | |
| 334 | ); | |
| 335 | mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, displacement)); | |
| 336 | } | |
| 337 | ||
| 338 | pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 339 | _ = fmt; | |
| 340 | _ = options; | |
| 341 | try std.fmt.format(writer, "Load {{ ", .{}); | |
| 342 | try std.fmt.format(writer, "{s}, ", .{self.kind}); | |
| 343 | if (self.addend) |addend| { | |
| 344 | try std.fmt.format(writer, ".addend = {}, ", .{addend}); | |
| 345 | } | |
| 346 | try std.fmt.format(writer, "}}", .{}); | |
| 347 | } | |
| 348 | }; | |
| 349 | ||
| 350 | pub fn resolve(self: Relocation, zld: *Zld) !void { | |
| 351 | const source_addr = blk: { | |
| 352 | const sym = zld.locals.items[self.block.local_sym_index]; | |
| 353 | break :blk sym.payload.regular.address; | |
| 354 | }; | |
| 355 | const target_addr = blk: { | |
| 356 | const is_via_got = inner: { | |
| 357 | switch (self.payload) { | |
| 358 | .pointer_to_got => break :inner true, | |
| 359 | .page => |page| page.kind == .got, | |
| 360 | .page_off => |page_off| page_off == .got, | |
| 361 | .load => {}, | |
| 362 | else => break :inner false, | |
| 363 | } | |
| 364 | }; | |
| 365 | ||
| 366 | if (is_via_got) { | |
| 367 | const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment; | |
| 368 | const got = dc_seg.sections.items[zld.got_section_index.?]; | |
| 369 | const got_index = self.target.got_index orelse { | |
| 370 | log.err("expected GOT entry for symbol '{s}'", .{self.target.name}); | |
| 371 | log.err(" this is an internal linker error", .{}); | |
| 372 | return error.FailedToResolveRelocationTarget; | |
| 373 | }; | |
| 374 | break :blk got.addr + got_index * @sizeOf(u64); | |
| 375 | } | |
| 376 | ||
| 377 | switch (self.target.payload) { | |
| 378 | .regular => |reg| break :blk reg.address, | |
| 379 | .proxy => |proxy| { | |
| 380 | if (mem.eql(u8, self.target.name, "__tlv_bootstrap")) { | |
| 381 | const segment = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment; | |
| 382 | const tlv = segment.sections.items[zld.tlv_section_index.?]; | |
| 383 | break :blk tlv.addr; | |
| 384 | } | |
| 385 | ||
| 386 | const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment; | |
| 387 | const stubs = segment.sections.items[zld.stubs_section_index.?]; | |
| 388 | const stubs_index = self.target.stubs_index orelse { | |
| 389 | if (proxy.bind_info.items.len > 0) { | |
| 390 | break :blk 0; // Dynamically bound by dyld. | |
| 391 | } | |
| 392 | log.err("expected stubs index or dynamic bind address for symbol '{s}'", .{ | |
| 393 | self.target.name, | |
| 394 | }); | |
| 395 | log.err(" this is an internal linker error", .{}); | |
| 396 | return error.FailedToResolveRelocationTarget; | |
| 397 | }; | |
| 398 | break :blk stubs.addr + stubs_index * stubs.reserved2; | |
| 399 | }, | |
| 400 | else => { | |
| 401 | log.err("failed to resolve symbol '{s}' as a relocation target", .{self.target.name}); | |
| 402 | log.err(" this is an internal linker error", .{}); | |
| 403 | return error.FailedToResolveRelocationTarget; | |
| 404 | }, | |
| 405 | } | |
| 406 | }; | |
| 407 | switch (self.payload) { | |
| 408 | .unsigned => |unsigned| try unsigned.resolve(self, source_addr, target_addr), | |
| 409 | .branch => |branch| try branch.resolve(self, source_addr, target_addr), | |
| 410 | .page => |page| try page.resolve(self, source_addr, target_addr), | |
| 411 | .page_off => |page_off| try page_off.resolve(self, source_addr, target_addr), | |
| 412 | .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(self, source_addr, target_addr), | |
| 413 | .signed => |signed| try signed.resolve(self, source_addr, target_addr), | |
| 414 | .load => |load| try load.resolve(self, source_addr, target_addr), | |
| 415 | } | |
| 90 | 416 | } |
| 91 | }; | |
| 92 | 417 | |
| 93 | pub const Unsigned = struct { | |
| 94 | base: Relocation, | |
| 95 | subtractor: ?*Symbol = 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) !void { | |
| 107 | // const addend = if (unsigned.base.target == .section) | |
| 108 | // unsigned.addend - @intCast(i64, args.source_target_sect_addr.?) | |
| 109 | // else | |
| 110 | // unsigned.addend; | |
| 111 | ||
| 112 | // const result = if (args.subtractor) |subtractor| | |
| 113 | // @intCast(i64, args.target_addr) - @intCast(i64, subtractor) + addend | |
| 114 | // else | |
| 115 | // @intCast(i64, args.target_addr) + addend; | |
| 116 | ||
| 117 | // log.debug(" | calculated addend 0x{x}", .{addend}); | |
| 118 | // log.debug(" | calculated unsigned value 0x{x}", .{result}); | |
| 119 | ||
| 120 | // if (unsigned.is_64bit) { | |
| 121 | // mem.writeIntLittle( | |
| 122 | // u64, | |
| 123 | // unsigned.base.code[0..8], | |
| 124 | // @bitCast(u64, result), | |
| 125 | // ); | |
| 126 | // } else { | |
| 127 | // mem.writeIntLittle( | |
| 128 | // u32, | |
| 129 | // unsigned.base.code[0..4], | |
| 130 | // @truncate(u32, @bitCast(u64, result)), | |
| 131 | // ); | |
| 132 | // } | |
| 133 | // } | |
| 134 | ||
| 135 | pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 136 | _ = fmt; | |
| 137 | _ = options; | |
| 138 | if (self.subtractor) |sub| { | |
| 139 | try std.fmt.format(writer, ".subtractor = {}, ", .{sub}); | |
| 140 | } | |
| 141 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 142 | const length: usize = if (self.is_64bit) 8 else 4; | |
| 143 | try std.fmt.format(writer, ".length = {}, ", .{length}); | |
| 418 | pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 419 | try std.fmt.format(writer, "Relocation {{ ", .{}); | |
| 420 | try std.fmt.format(writer, ".offset = {}, ", .{self.offset}); | |
| 421 | try std.fmt.format(writer, ".block = {}", .{self.block.local_sym_index}); | |
| 422 | try std.fmt.format(writer, ".target = {}, ", .{self.target}); | |
| 423 | ||
| 424 | switch (self.payload) { | |
| 425 | .unsigned => |unsigned| try unsigned.format(fmt, options, writer), | |
| 426 | .branch => |branch| try branch.format(fmt, options, writer), | |
| 427 | .page => |page| try page.format(fmt, options, writer), | |
| 428 | .page_off => |page_off| try page_off.format(fmt, options, writer), | |
| 429 | .pointer_to_got => |pointer_to_got| try pointer_to_got.format(fmt, options, writer), | |
| 430 | .signed => |signed| try signed.format(fmt, options, writer), | |
| 431 | .load => |load| try load.format(fmt, options, writer), | |
| 432 | } | |
| 433 | ||
| 434 | try std.fmt.format(writer, "}}", .{}); | |
| 144 | 435 | } |
| 145 | 436 | }; |
| 146 | 437 | |
| ... | ... | @@ -161,3 +452,342 @@ pub const RelocIterator = struct { |
| 161 | 452 | return self.buffer[@intCast(u32, self.index + 1)]; |
| 162 | 453 | } |
| 163 | 454 | }; |
| 455 | ||
| 456 | pub const Parser = struct { | |
| 457 | object: *Object, | |
| 458 | zld: *Zld, | |
| 459 | it: *RelocIterator, | |
| 460 | block: *TextBlock, | |
| 461 | ||
| 462 | /// Base address of the parsed text block in the source section. | |
| 463 | base_addr: u64, | |
| 464 | ||
| 465 | /// Used only when targeting aarch64 | |
| 466 | addend: ?u32 = null, | |
| 467 | ||
| 468 | /// Parsed subtractor symbol from _RELOC_SUBTRACTOR reloc type. | |
| 469 | subtractor: ?*Symbol = null, | |
| 470 | ||
| 471 | pub fn parse(self: *Parser) !void { | |
| 472 | while (self.it.next()) |rel| { | |
| 473 | const out_rel = blk: { | |
| 474 | switch (self.object.arch.?) { | |
| 475 | .aarch64 => { | |
| 476 | const out_rel = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { | |
| 477 | .ARM64_RELOC_BRANCH26 => try self.parseBranch(rel), | |
| 478 | .ARM64_RELOC_SUBTRACTOR => { | |
| 479 | // Subtractor is not a relocation with effect on the TextBlock, so | |
| 480 | // parse it and carry on. | |
| 481 | try self.parseSubtractor(rel); | |
| 482 | ||
| 483 | // Verify SUBTRACTOR is followed by UNSIGNED. | |
| 484 | const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type); | |
| 485 | if (next != .ARM64_RELOC_UNSIGNED) { | |
| 486 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | |
| 487 | return error.UnexpectedRelocationType; | |
| 488 | } | |
| 489 | continue; | |
| 490 | }, | |
| 491 | .ARM64_RELOC_UNSIGNED => try self.parseUnsigned(rel), | |
| 492 | .ARM64_RELOC_ADDEND => { | |
| 493 | // Addend is not a relocation with effect on the TextBlock, so | |
| 494 | // parse it and carry on. | |
| 495 | try self.parseAddend(rel); | |
| 496 | ||
| 497 | // Verify ADDEND is followed by a load. | |
| 498 | const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type); | |
| 499 | switch (next) { | |
| 500 | .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {}, | |
| 501 | else => { | |
| 502 | log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next}); | |
| 503 | return error.UnexpectedRelocationType; | |
| 504 | }, | |
| 505 | } | |
| 506 | continue; | |
| 507 | }, | |
| 508 | .ARM64_RELOC_PAGE21, | |
| 509 | .ARM64_RELOC_GOT_LOAD_PAGE21, | |
| 510 | .ARM64_RELOC_TLVP_LOAD_PAGE21, | |
| 511 | => try self.parsePage(rel), | |
| 512 | .ARM64_RELOC_PAGEOFF12, | |
| 513 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, | |
| 514 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12, | |
| 515 | => try self.parsePageOff(rel), | |
| 516 | .ARM64_RELOC_POINTER_TO_GOT => try self.parsePointerToGot(rel), | |
| 517 | }; | |
| 518 | break :blk out_rel; | |
| 519 | }, | |
| 520 | .x86_64 => { | |
| 521 | const out_rel = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { | |
| 522 | .X86_64_RELOC_BRANCH => try self.parseBranch(rel), | |
| 523 | .X86_64_RELOC_SUBTRACTOR => { | |
| 524 | // Subtractor is not a relocation with effect on the TextBlock, so | |
| 525 | // parse it and carry on. | |
| 526 | try self.parseSubtractor(rel); | |
| 527 | ||
| 528 | // Verify SUBTRACTOR is followed by UNSIGNED. | |
| 529 | const next = @intToEnum(macho.reloc_type_x86_64, self.it.peek().r_type); | |
| 530 | if (next != .X86_64_RELOC_UNSIGNED) { | |
| 531 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | |
| 532 | return error.UnexpectedRelocationType; | |
| 533 | } | |
| 534 | continue; | |
| 535 | }, | |
| 536 | .X86_64_RELOC_UNSIGNED => try self.parseUnsigned(rel), | |
| 537 | .X86_64_RELOC_SIGNED, | |
| 538 | .X86_64_RELOC_SIGNED_1, | |
| 539 | .X86_64_RELOC_SIGNED_2, | |
| 540 | .X86_64_RELOC_SIGNED_4, | |
| 541 | => try self.parseSigned(rel), | |
| 542 | .X86_64_RELOC_GOT_LOAD, | |
| 543 | .X86_64_RELOC_GOT, | |
| 544 | .X86_64_RELOC_TLV, | |
| 545 | => try self.parseLoad(rel), | |
| 546 | }; | |
| 547 | break :blk out_rel; | |
| 548 | }, | |
| 549 | else => unreachable, | |
| 550 | } | |
| 551 | }; | |
| 552 | try self.block.relocs.append(out_rel); | |
| 553 | ||
| 554 | if (out_rel.target.payload == .regular) { | |
| 555 | try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {}); | |
| 556 | } | |
| 557 | ||
| 558 | const is_via_got = switch (out_rel.payload) { | |
| 559 | .pointer_to_got => true, | |
| 560 | .load => |load| load.kind == .got, | |
| 561 | .page => |page| page.kind == .got, | |
| 562 | .page_off => |page_off| page_off.kind == .got, | |
| 563 | else => false, | |
| 564 | }; | |
| 565 | ||
| 566 | if (is_via_got and out_rel.target.got_index == null) { | |
| 567 | const index = @intCast(u32, self.zld.got_entries.items.len); | |
| 568 | out_rel.target.got_index = index; | |
| 569 | try self.zld.got_entries.append(self.zld.allocator, out_rel.target); | |
| 570 | log.debug("adding GOT entry for symbol {s} at index {}", .{ out_rel.target.name, index }); | |
| 571 | } | |
| 572 | ||
| 573 | if (out_rel.payload == .branch) { | |
| 574 | const sym = out_rel.target; | |
| 575 | ||
| 576 | if (sym.stubs_index != null) continue; | |
| 577 | if (sym.payload != .proxy) continue; | |
| 578 | ||
| 579 | const index = @intCast(u32, self.zld.stubs.items.len); | |
| 580 | sym.stubs_index = index; | |
| 581 | try self.zld.stubs.append(self.zld.allocator, sym); | |
| 582 | ||
| 583 | log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index }); | |
| 584 | } | |
| 585 | } | |
| 586 | } | |
| 587 | ||
| 588 | fn parseBaseRelInfo(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 589 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 590 | const target = try self.object.symbolFromReloc(rel); | |
| 591 | return Relocation{ | |
| 592 | .offset = offset, | |
| 593 | .target = target, | |
| 594 | .block = self.block, | |
| 595 | .payload = undefined, | |
| 596 | }; | |
| 597 | } | |
| 598 | ||
| 599 | fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 600 | defer { | |
| 601 | // Reset parser's subtractor state | |
| 602 | self.subtractor = null; | |
| 603 | } | |
| 604 | ||
| 605 | assert(rel.r_pcrel == 0); | |
| 606 | ||
| 607 | var parsed = try self.parseBaseRelInfo(rel); | |
| 608 | const is_64bit: bool = switch (rel.r_length) { | |
| 609 | 3 => true, | |
| 610 | 2 => false, | |
| 611 | else => unreachable, | |
| 612 | }; | |
| 613 | const addend: i64 = if (is_64bit) | |
| 614 | mem.readIntLittle(i64, self.block.code[parsed.offset..][0..8]) | |
| 615 | else | |
| 616 | mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]); | |
| 617 | ||
| 618 | parsed.payload = .{ | |
| 619 | .unsigned = .{ | |
| 620 | .subtractor = self.subtractor, | |
| 621 | .is_64bit = is_64bit, | |
| 622 | .addend = addend, | |
| 623 | }, | |
| 624 | }; | |
| 625 | ||
| 626 | return parsed; | |
| 627 | } | |
| 628 | ||
| 629 | fn parseBranch(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 630 | assert(rel.r_pcrel == 1); | |
| 631 | assert(rel.r_length == 2); | |
| 632 | ||
| 633 | var parsed = try self.parseBaseRelInfo(rel); | |
| 634 | parsed.payload = .{ | |
| 635 | .branch = .{ | |
| 636 | .arch = self.object.arch.?, | |
| 637 | }, | |
| 638 | }; | |
| 639 | return parsed; | |
| 640 | } | |
| 641 | ||
| 642 | fn parsePage(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 643 | assert(rel.r_pcrel == 1); | |
| 644 | assert(rel.r_length == 2); | |
| 645 | ||
| 646 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 647 | ||
| 648 | defer if (rel_type == .ARM64_RELOC_PAGE21) { | |
| 649 | // Reset parser's addend state | |
| 650 | self.addend = null; | |
| 651 | }; | |
| 652 | ||
| 653 | const addend = if (rel_type == .ARM64_RELOC_PAGE21) | |
| 654 | self.addend | |
| 655 | else | |
| 656 | null; | |
| 657 | ||
| 658 | var parsed = try self.parseBaseRelInfo(rel); | |
| 659 | parsed.payload = .{ | |
| 660 | .page = .{ | |
| 661 | .kind = switch (rel_type) { | |
| 662 | .ARM64_RELOC_PAGE21 => .page, | |
| 663 | .ARM64_RELOC_GOT_LOAD_PAGE21 => .got, | |
| 664 | .ARM64_RELOC_TLVP_LOAD_PAGE21 => .tlvp, | |
| 665 | else => unreachable, | |
| 666 | }, | |
| 667 | .addend = addend, | |
| 668 | }, | |
| 669 | }; | |
| 670 | return parsed; | |
| 671 | } | |
| 672 | ||
| 673 | fn parsePageOff(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 674 | assert(rel.r_pcrel == 0); | |
| 675 | assert(rel.r_length == 2); | |
| 676 | ||
| 677 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 678 | ||
| 679 | defer if (rel_type == .ARM64_RELOC_PAGEOFF12) { | |
| 680 | // Reset parser's addend state | |
| 681 | self.addend = null; | |
| 682 | }; | |
| 683 | ||
| 684 | const addend = if (rel_type == .ARM64_RELOC_PAGEOFF12) | |
| 685 | self.addend | |
| 686 | else | |
| 687 | null; | |
| 688 | ||
| 689 | var parsed = try self.parseBaseRelInfo(rel); | |
| 690 | const op_kind: ?Relocation.PageOff.OpKind = blk: { | |
| 691 | if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null; | |
| 692 | const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.block.code[parsed.offset..][0..4])) | |
| 693 | .arithmetic | |
| 694 | else | |
| 695 | .load; | |
| 696 | break :blk op_kind; | |
| 697 | }; | |
| 698 | ||
| 699 | parsed.payload = .{ | |
| 700 | .page_off = .{ | |
| 701 | .kind = switch (rel_type) { | |
| 702 | .ARM64_RELOC_PAGEOFF12 => .page, | |
| 703 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got, | |
| 704 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => .tlvp, | |
| 705 | else => unreachable, | |
| 706 | }, | |
| 707 | .addend = addend, | |
| 708 | .op_kind = op_kind, | |
| 709 | }, | |
| 710 | }; | |
| 711 | return parsed; | |
| 712 | } | |
| 713 | ||
| 714 | fn parsePointerToGot(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 715 | assert(rel.r_pcrel == 1); | |
| 716 | assert(rel.r_length == 2); | |
| 717 | ||
| 718 | var parsed = try self.parseBaseRelInfo(rel); | |
| 719 | parsed.payload = .{ | |
| 720 | .pointer_to_got = .{}, | |
| 721 | }; | |
| 722 | return parsed; | |
| 723 | } | |
| 724 | ||
| 725 | fn parseAddend(self: *Parser, rel: macho.relocation_info) !void { | |
| 726 | assert(rel.r_pcrel == 0); | |
| 727 | assert(rel.r_extern == 0); | |
| 728 | assert(self.addend == null); | |
| 729 | ||
| 730 | self.addend = rel.r_symbolnum; | |
| 731 | } | |
| 732 | ||
| 733 | fn parseSigned(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 734 | assert(rel.r_pcrel == 1); | |
| 735 | assert(rel.r_length == 2); | |
| 736 | ||
| 737 | var parsed = try self.parseBaseRelInfo(rel); | |
| 738 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 739 | const correction: i4 = switch (rel_type) { | |
| 740 | .X86_64_RELOC_SIGNED => 0, | |
| 741 | .X86_64_RELOC_SIGNED_1 => 1, | |
| 742 | .X86_64_RELOC_SIGNED_2 => 2, | |
| 743 | .X86_64_RELOC_SIGNED_4 => 4, | |
| 744 | else => unreachable, | |
| 745 | }; | |
| 746 | const addend = mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]) + correction; | |
| 747 | ||
| 748 | parsed.payload = .{ | |
| 749 | .signed = .{ | |
| 750 | .correction = correction, | |
| 751 | .addend = addend, | |
| 752 | }, | |
| 753 | }; | |
| 754 | ||
| 755 | return parsed; | |
| 756 | } | |
| 757 | ||
| 758 | fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void { | |
| 759 | assert(rel.r_pcrel == 0); | |
| 760 | assert(self.subtractor == null); | |
| 761 | ||
| 762 | self.subtractor = try self.object.symbolFromReloc(rel); | |
| 763 | } | |
| 764 | ||
| 765 | fn parseLoad(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 766 | assert(rel.r_pcrel == 1); | |
| 767 | assert(rel.r_length == 2); | |
| 768 | ||
| 769 | var parsed = try self.parseBaseRelInfo(rel); | |
| 770 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 771 | const addend = if (rel_type == .X86_64_RELOC_GOT) | |
| 772 | mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]) | |
| 773 | else | |
| 774 | null; | |
| 775 | ||
| 776 | parsed.payload = .{ | |
| 777 | .load = .{ | |
| 778 | .kind = switch (rel_type) { | |
| 779 | .X86_64_RELOC_GOT_LOAD, .X86_64_RELOC_GOT => .got, | |
| 780 | .X86_64_RELOC_TLV => .tlvp, | |
| 781 | else => unreachable, | |
| 782 | }, | |
| 783 | .addend = addend, | |
| 784 | }, | |
| 785 | }; | |
| 786 | return parsed; | |
| 787 | } | |
| 788 | }; | |
| 789 | ||
| 790 | inline fn isArithmeticOp(inst: *const [4]u8) bool { | |
| 791 | const group_decode = @truncate(u5, inst[3]); | |
| 792 | return ((group_decode >> 2) == 4); | |
| 793 | } |
src/link/MachO/reloc/aarch64.zig deleted-618| ... | ... | @@ -1,618 +0,0 @@ |
| 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 Object = @import("../Object.zig"); | |
| 13 | const Relocation = reloc.Relocation; | |
| 14 | const Symbol = @import("../Symbol.zig"); | |
| 15 | const TextBlock = Zld.TextBlock; | |
| 16 | const Zld = @import("../Zld.zig"); | |
| 17 | ||
| 18 | pub const Branch = struct { | |
| 19 | base: Relocation, | |
| 20 | /// Always .UnconditionalBranchImmediate | |
| 21 | // inst: aarch64.Instruction, | |
| 22 | ||
| 23 | pub const base_type: Relocation.Type = .branch_aarch64; | |
| 24 | ||
| 25 | // pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void { | |
| 26 | // const displacement = try math.cast(i28, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr)); | |
| 27 | ||
| 28 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 29 | ||
| 30 | // var inst = branch.inst; | |
| 31 | // inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); | |
| 32 | // mem.writeIntLittle(u32, branch.base.code[0..4], inst.toU32()); | |
| 33 | // } | |
| 34 | ||
| 35 | pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 36 | _ = self; | |
| 37 | _ = fmt; | |
| 38 | _ = options; | |
| 39 | _ = writer; | |
| 40 | } | |
| 41 | }; | |
| 42 | ||
| 43 | pub const Page = struct { | |
| 44 | base: Relocation, | |
| 45 | addend: ?u32 = null, | |
| 46 | /// Always .PCRelativeAddress | |
| 47 | // inst: aarch64.Instruction, | |
| 48 | ||
| 49 | pub const base_type: Relocation.Type = .page; | |
| 50 | ||
| 51 | // pub fn resolve(page: Page, args: Relocation.ResolveArgs) !void { | |
| 52 | // const target_addr = if (page.addend) |addend| args.target_addr + addend else args.target_addr; | |
| 53 | // const source_page = @intCast(i32, args.source_addr >> 12); | |
| 54 | // const target_page = @intCast(i32, target_addr >> 12); | |
| 55 | // const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 56 | ||
| 57 | // log.debug(" | calculated addend 0x{x}", .{page.addend}); | |
| 58 | // log.debug(" | moving by {} pages", .{pages}); | |
| 59 | ||
| 60 | // var inst = page.inst; | |
| 61 | // inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 62 | // inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 63 | ||
| 64 | // mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | |
| 65 | // } | |
| 66 | ||
| 67 | pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 68 | _ = fmt; | |
| 69 | _ = options; | |
| 70 | if (self.addend) |addend| { | |
| 71 | try std.fmt.format(writer, ".addend = {}, ", .{addend}); | |
| 72 | } | |
| 73 | } | |
| 74 | }; | |
| 75 | ||
| 76 | pub const PageOff = struct { | |
| 77 | base: Relocation, | |
| 78 | addend: ?u32 = null, | |
| 79 | op_kind: OpKind, | |
| 80 | // inst: aarch64.Instruction, | |
| 81 | ||
| 82 | pub const base_type: Relocation.Type = .page_off; | |
| 83 | ||
| 84 | pub const OpKind = enum { | |
| 85 | arithmetic, | |
| 86 | load_store, | |
| 87 | }; | |
| 88 | ||
| 89 | // pub fn resolve(page_off: PageOff, args: Relocation.ResolveArgs) !void { | |
| 90 | // const target_addr = if (page_off.addend) |addend| args.target_addr + addend else args.target_addr; | |
| 91 | // const narrowed = @truncate(u12, target_addr); | |
| 92 | ||
| 93 | // log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 94 | // log.debug(" | {s} opcode", .{page_off.op_kind}); | |
| 95 | ||
| 96 | // var inst = page_off.inst; | |
| 97 | // if (page_off.op_kind == .arithmetic) { | |
| 98 | // inst.add_subtract_immediate.imm12 = narrowed; | |
| 99 | // } else { | |
| 100 | // const offset: u12 = blk: { | |
| 101 | // if (inst.load_store_register.size == 0) { | |
| 102 | // if (inst.load_store_register.v == 1) { | |
| 103 | // // 128-bit SIMD is scaled by 16. | |
| 104 | // break :blk try math.divExact(u12, narrowed, 16); | |
| 105 | // } | |
| 106 | // // Otherwise, 8-bit SIMD or ldrb. | |
| 107 | // break :blk narrowed; | |
| 108 | // } else { | |
| 109 | // const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size); | |
| 110 | // break :blk try math.divExact(u12, narrowed, denom); | |
| 111 | // } | |
| 112 | // }; | |
| 113 | // inst.load_store_register.offset = offset; | |
| 114 | // } | |
| 115 | ||
| 116 | // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 117 | // } | |
| 118 | ||
| 119 | pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 120 | _ = fmt; | |
| 121 | _ = options; | |
| 122 | if (self.addend) |addend| { | |
| 123 | try std.fmt.format(writer, ".addend = {}, ", .{addend}); | |
| 124 | } | |
| 125 | try std.fmt.format(writer, ".op_kind = {s}, ", .{self.op_kind}); | |
| 126 | } | |
| 127 | }; | |
| 128 | ||
| 129 | pub const GotPage = struct { | |
| 130 | base: Relocation, | |
| 131 | /// Always .PCRelativeAddress | |
| 132 | // inst: aarch64.Instruction, | |
| 133 | ||
| 134 | pub const base_type: Relocation.Type = .got_page; | |
| 135 | ||
| 136 | // pub fn resolve(page: GotPage, args: Relocation.ResolveArgs) !void { | |
| 137 | // const source_page = @intCast(i32, args.source_addr >> 12); | |
| 138 | // const target_page = @intCast(i32, args.target_addr >> 12); | |
| 139 | // const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 140 | ||
| 141 | // log.debug(" | moving by {} pages", .{pages}); | |
| 142 | ||
| 143 | // var inst = page.inst; | |
| 144 | // inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 145 | // inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 146 | ||
| 147 | // mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | |
| 148 | // } | |
| 149 | ||
| 150 | pub fn format(self: GotPage, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 151 | _ = self; | |
| 152 | _ = fmt; | |
| 153 | _ = options; | |
| 154 | _ = writer; | |
| 155 | } | |
| 156 | }; | |
| 157 | ||
| 158 | pub const GotPageOff = struct { | |
| 159 | base: Relocation, | |
| 160 | /// Always .LoadStoreRegister with size = 3 for GOT indirection | |
| 161 | // inst: aarch64.Instruction, | |
| 162 | ||
| 163 | pub const base_type: Relocation.Type = .got_page_off; | |
| 164 | ||
| 165 | // pub fn resolve(page_off: GotPageOff, args: Relocation.ResolveArgs) !void { | |
| 166 | // const narrowed = @truncate(u12, args.target_addr); | |
| 167 | ||
| 168 | // log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 169 | ||
| 170 | // var inst = page_off.inst; | |
| 171 | // const offset = try math.divExact(u12, narrowed, 8); | |
| 172 | // inst.load_store_register.offset = offset; | |
| 173 | ||
| 174 | // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 175 | // } | |
| 176 | ||
| 177 | pub fn format(self: GotPageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 178 | _ = self; | |
| 179 | _ = fmt; | |
| 180 | _ = options; | |
| 181 | _ = writer; | |
| 182 | } | |
| 183 | }; | |
| 184 | ||
| 185 | pub const PointerToGot = struct { | |
| 186 | base: Relocation, | |
| 187 | ||
| 188 | pub const base_type: Relocation.Type = .pointer_to_got; | |
| 189 | ||
| 190 | // pub fn resolve(ptr_to_got: PointerToGot, args: Relocation.ResolveArgs) !void { | |
| 191 | // const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr)); | |
| 192 | ||
| 193 | // log.debug(" | calculated value 0x{x}", .{result}); | |
| 194 | ||
| 195 | // mem.writeIntLittle(u32, ptr_to_got.base.code[0..4], @bitCast(u32, result)); | |
| 196 | // } | |
| 197 | ||
| 198 | pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 199 | _ = self; | |
| 200 | _ = fmt; | |
| 201 | _ = options; | |
| 202 | _ = writer; | |
| 203 | } | |
| 204 | }; | |
| 205 | ||
| 206 | pub const TlvpPage = struct { | |
| 207 | base: Relocation, | |
| 208 | /// Always .PCRelativeAddress | |
| 209 | // inst: aarch64.Instruction, | |
| 210 | ||
| 211 | pub const base_type: Relocation.Type = .tlvp_page; | |
| 212 | ||
| 213 | // pub fn resolve(page: TlvpPage, args: Relocation.ResolveArgs) !void { | |
| 214 | // const source_page = @intCast(i32, args.source_addr >> 12); | |
| 215 | // const target_page = @intCast(i32, args.target_addr >> 12); | |
| 216 | // const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 217 | ||
| 218 | // log.debug(" | moving by {} pages", .{pages}); | |
| 219 | ||
| 220 | // var inst = page.inst; | |
| 221 | // inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 222 | // inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 223 | ||
| 224 | // mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | |
| 225 | // } | |
| 226 | ||
| 227 | pub fn format(self: TlvpPage, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 228 | _ = self; | |
| 229 | _ = fmt; | |
| 230 | _ = options; | |
| 231 | _ = writer; | |
| 232 | } | |
| 233 | }; | |
| 234 | ||
| 235 | pub const TlvpPageOff = struct { | |
| 236 | base: Relocation, | |
| 237 | /// Always .AddSubtractImmediate regardless of the source instruction. | |
| 238 | /// This means, we always rewrite the instruction to add even if the | |
| 239 | /// source instruction was an ldr. | |
| 240 | // inst: aarch64.Instruction, | |
| 241 | ||
| 242 | pub const base_type: Relocation.Type = .tlvp_page_off; | |
| 243 | ||
| 244 | // pub fn resolve(page_off: TlvpPageOff, args: Relocation.ResolveArgs) !void { | |
| 245 | // const narrowed = @truncate(u12, args.target_addr); | |
| 246 | ||
| 247 | // log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 248 | ||
| 249 | // var inst = page_off.inst; | |
| 250 | // inst.add_subtract_immediate.imm12 = narrowed; | |
| 251 | ||
| 252 | // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 253 | // } | |
| 254 | ||
| 255 | pub fn format(self: TlvpPageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 256 | _ = self; | |
| 257 | _ = fmt; | |
| 258 | _ = options; | |
| 259 | _ = writer; | |
| 260 | } | |
| 261 | }; | |
| 262 | ||
| 263 | pub const Parser = struct { | |
| 264 | object: *Object, | |
| 265 | zld: *Zld, | |
| 266 | it: *reloc.RelocIterator, | |
| 267 | block: *TextBlock, | |
| 268 | base_addr: u64, | |
| 269 | addend: ?u32 = null, | |
| 270 | subtractor: ?*Symbol = null, | |
| 271 | ||
| 272 | pub fn parse(self: *Parser) !void { | |
| 273 | while (self.it.next()) |rel| { | |
| 274 | const out_rel = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { | |
| 275 | .ARM64_RELOC_BRANCH26 => try self.parseBranch(rel), | |
| 276 | .ARM64_RELOC_SUBTRACTOR => { | |
| 277 | // Subtractor is not a relocation with effect on the TextBlock, so | |
| 278 | // parse it and carry on. | |
| 279 | try self.parseSubtractor(rel); | |
| 280 | continue; | |
| 281 | }, | |
| 282 | .ARM64_RELOC_UNSIGNED => try self.parseUnsigned(rel), | |
| 283 | .ARM64_RELOC_ADDEND => { | |
| 284 | // Addend is not a relocation with effect on the TextBlock, so | |
| 285 | // parse it and carry on. | |
| 286 | try self.parseAddend(rel); | |
| 287 | continue; | |
| 288 | }, | |
| 289 | .ARM64_RELOC_PAGE21, | |
| 290 | .ARM64_RELOC_GOT_LOAD_PAGE21, | |
| 291 | .ARM64_RELOC_TLVP_LOAD_PAGE21, | |
| 292 | => try self.parsePage(rel), | |
| 293 | .ARM64_RELOC_PAGEOFF12 => try self.parsePageOff(rel), | |
| 294 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => try self.parseGotLoadPageOff(rel), | |
| 295 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => try self.parseTlvpLoadPageOff(rel), | |
| 296 | .ARM64_RELOC_POINTER_TO_GOT => try self.parsePointerToGot(rel), | |
| 297 | }; | |
| 298 | try self.block.relocs.append(out_rel); | |
| 299 | ||
| 300 | if (out_rel.target.payload == .regular) { | |
| 301 | try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {}); | |
| 302 | } | |
| 303 | ||
| 304 | switch (out_rel.@"type") { | |
| 305 | .got_page, .got_page_off, .pointer_to_got => { | |
| 306 | const sym = out_rel.target; | |
| 307 | ||
| 308 | if (sym.got_index != null) continue; | |
| 309 | ||
| 310 | const index = @intCast(u32, self.zld.got_entries.items.len); | |
| 311 | sym.got_index = index; | |
| 312 | try self.zld.got_entries.append(self.zld.allocator, sym); | |
| 313 | ||
| 314 | log.debug("adding GOT entry for symbol {s} at index {}", .{ sym.name, index }); | |
| 315 | }, | |
| 316 | .branch_aarch64 => { | |
| 317 | const sym = out_rel.target; | |
| 318 | ||
| 319 | if (sym.stubs_index != null) continue; | |
| 320 | if (sym.payload != .proxy) continue; | |
| 321 | ||
| 322 | const index = @intCast(u32, self.zld.stubs.items.len); | |
| 323 | sym.stubs_index = index; | |
| 324 | try self.zld.stubs.append(self.zld.allocator, sym); | |
| 325 | ||
| 326 | log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index }); | |
| 327 | }, | |
| 328 | else => {}, | |
| 329 | } | |
| 330 | } | |
| 331 | } | |
| 332 | ||
| 333 | fn parseAddend(self: *Parser, rel: macho.relocation_info) !void { | |
| 334 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 335 | assert(rel_type == .ARM64_RELOC_ADDEND); | |
| 336 | assert(rel.r_pcrel == 0); | |
| 337 | assert(rel.r_extern == 0); | |
| 338 | assert(self.addend == null); | |
| 339 | ||
| 340 | self.addend = rel.r_symbolnum; | |
| 341 | ||
| 342 | // Verify ADDEND is followed by a load. | |
| 343 | const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type); | |
| 344 | switch (next) { | |
| 345 | .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {}, | |
| 346 | else => { | |
| 347 | log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next}); | |
| 348 | return error.UnexpectedRelocationType; | |
| 349 | }, | |
| 350 | } | |
| 351 | } | |
| 352 | ||
| 353 | fn parseBranch(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 354 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 355 | assert(rel_type == .ARM64_RELOC_BRANCH26); | |
| 356 | assert(rel.r_pcrel == 1); | |
| 357 | assert(rel.r_length == 2); | |
| 358 | ||
| 359 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 360 | const target = try self.object.symbolFromReloc(rel); | |
| 361 | ||
| 362 | var branch = try self.object.allocator.create(Branch); | |
| 363 | errdefer self.object.allocator.destroy(branch); | |
| 364 | ||
| 365 | branch.* = .{ | |
| 366 | .base = .{ | |
| 367 | .@"type" = .branch_aarch64, | |
| 368 | .offset = offset, | |
| 369 | .target = target, | |
| 370 | .block = self.block, | |
| 371 | }, | |
| 372 | }; | |
| 373 | ||
| 374 | return &branch.base; | |
| 375 | } | |
| 376 | ||
| 377 | fn parsePage(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 378 | assert(rel.r_pcrel == 1); | |
| 379 | assert(rel.r_length == 2); | |
| 380 | ||
| 381 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 382 | const target = try self.object.symbolFromReloc(rel); | |
| 383 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 384 | ||
| 385 | const ptr: *Relocation = ptr: { | |
| 386 | switch (rel_type) { | |
| 387 | .ARM64_RELOC_PAGE21 => { | |
| 388 | defer { | |
| 389 | // Reset parser's addend state | |
| 390 | self.addend = null; | |
| 391 | } | |
| 392 | var page = try self.object.allocator.create(Page); | |
| 393 | errdefer self.object.allocator.destroy(page); | |
| 394 | ||
| 395 | page.* = .{ | |
| 396 | .base = .{ | |
| 397 | .@"type" = .page, | |
| 398 | .offset = offset, | |
| 399 | .target = target, | |
| 400 | .block = self.block, | |
| 401 | }, | |
| 402 | .addend = self.addend, | |
| 403 | }; | |
| 404 | ||
| 405 | break :ptr &page.base; | |
| 406 | }, | |
| 407 | .ARM64_RELOC_GOT_LOAD_PAGE21 => { | |
| 408 | var page = try self.object.allocator.create(GotPage); | |
| 409 | errdefer self.object.allocator.destroy(page); | |
| 410 | ||
| 411 | page.* = .{ | |
| 412 | .base = .{ | |
| 413 | .@"type" = .got_page, | |
| 414 | .offset = offset, | |
| 415 | .target = target, | |
| 416 | .block = self.block, | |
| 417 | }, | |
| 418 | }; | |
| 419 | ||
| 420 | break :ptr &page.base; | |
| 421 | }, | |
| 422 | .ARM64_RELOC_TLVP_LOAD_PAGE21 => { | |
| 423 | var page = try self.object.allocator.create(TlvpPage); | |
| 424 | errdefer self.object.allocator.destroy(page); | |
| 425 | ||
| 426 | page.* = .{ | |
| 427 | .base = .{ | |
| 428 | .@"type" = .tlvp_page, | |
| 429 | .offset = offset, | |
| 430 | .target = target, | |
| 431 | .block = self.block, | |
| 432 | }, | |
| 433 | }; | |
| 434 | ||
| 435 | break :ptr &page.base; | |
| 436 | }, | |
| 437 | else => unreachable, | |
| 438 | } | |
| 439 | }; | |
| 440 | ||
| 441 | return ptr; | |
| 442 | } | |
| 443 | ||
| 444 | fn parsePageOff(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 445 | defer { | |
| 446 | // Reset parser's addend state | |
| 447 | self.addend = null; | |
| 448 | } | |
| 449 | ||
| 450 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 451 | assert(rel_type == .ARM64_RELOC_PAGEOFF12); | |
| 452 | assert(rel.r_pcrel == 0); | |
| 453 | assert(rel.r_length == 2); | |
| 454 | ||
| 455 | const target = try self.object.symbolFromReloc(rel); | |
| 456 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 457 | const op_kind: PageOff.OpKind = if (isArithmeticOp(self.block.code[offset..][0..4])) | |
| 458 | .arithmetic | |
| 459 | else | |
| 460 | .load_store; | |
| 461 | ||
| 462 | var page_off = try self.object.allocator.create(PageOff); | |
| 463 | errdefer self.object.allocator.destroy(page_off); | |
| 464 | ||
| 465 | page_off.* = .{ | |
| 466 | .base = .{ | |
| 467 | .@"type" = .page_off, | |
| 468 | .offset = offset, | |
| 469 | .target = target, | |
| 470 | .block = self.block, | |
| 471 | }, | |
| 472 | .op_kind = op_kind, | |
| 473 | .addend = self.addend, | |
| 474 | }; | |
| 475 | ||
| 476 | return &page_off.base; | |
| 477 | } | |
| 478 | ||
| 479 | fn parseGotLoadPageOff(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 480 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 481 | assert(rel_type == .ARM64_RELOC_GOT_LOAD_PAGEOFF12); | |
| 482 | assert(rel.r_pcrel == 0); | |
| 483 | assert(rel.r_length == 2); | |
| 484 | ||
| 485 | const target = try self.object.symbolFromReloc(rel); | |
| 486 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 487 | assert(!isArithmeticOp(self.block.code[offset..][0..4])); | |
| 488 | ||
| 489 | var page_off = try self.object.allocator.create(GotPageOff); | |
| 490 | errdefer self.object.allocator.destroy(page_off); | |
| 491 | ||
| 492 | page_off.* = .{ | |
| 493 | .base = .{ | |
| 494 | .@"type" = .got_page_off, | |
| 495 | .offset = offset, | |
| 496 | .target = target, | |
| 497 | .block = self.block, | |
| 498 | }, | |
| 499 | }; | |
| 500 | ||
| 501 | return &page_off.base; | |
| 502 | } | |
| 503 | ||
| 504 | fn parseTlvpLoadPageOff(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 505 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 506 | assert(rel_type == .ARM64_RELOC_TLVP_LOAD_PAGEOFF12); | |
| 507 | assert(rel.r_pcrel == 0); | |
| 508 | assert(rel.r_length == 2); | |
| 509 | ||
| 510 | const RegInfo = struct { | |
| 511 | rd: u5, | |
| 512 | rn: u5, | |
| 513 | size: u1, | |
| 514 | }; | |
| 515 | ||
| 516 | const target = try self.object.symbolFromReloc(rel); | |
| 517 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 518 | ||
| 519 | var page_off = try self.object.allocator.create(TlvpPageOff); | |
| 520 | errdefer self.object.allocator.destroy(page_off); | |
| 521 | ||
| 522 | page_off.* = .{ | |
| 523 | .base = .{ | |
| 524 | .@"type" = .tlvp_page_off, | |
| 525 | .offset = offset, | |
| 526 | .target = target, | |
| 527 | .block = self.block, | |
| 528 | }, | |
| 529 | }; | |
| 530 | ||
| 531 | return &page_off.base; | |
| 532 | } | |
| 533 | ||
| 534 | fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void { | |
| 535 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 536 | assert(rel_type == .ARM64_RELOC_SUBTRACTOR); | |
| 537 | assert(rel.r_pcrel == 0); | |
| 538 | assert(self.subtractor == null); | |
| 539 | ||
| 540 | self.subtractor = try self.object.symbolFromReloc(rel); | |
| 541 | ||
| 542 | // Verify SUBTRACTOR is followed by UNSIGNED. | |
| 543 | const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type); | |
| 544 | if (next != .ARM64_RELOC_UNSIGNED) { | |
| 545 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | |
| 546 | return error.UnexpectedRelocationType; | |
| 547 | } | |
| 548 | } | |
| 549 | ||
| 550 | fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 551 | defer { | |
| 552 | // Reset parser's subtractor state | |
| 553 | self.subtractor = null; | |
| 554 | } | |
| 555 | ||
| 556 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 557 | assert(rel_type == .ARM64_RELOC_UNSIGNED); | |
| 558 | assert(rel.r_pcrel == 0); | |
| 559 | ||
| 560 | const target = try self.object.symbolFromReloc(rel); | |
| 561 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 562 | const is_64bit: bool = switch (rel.r_length) { | |
| 563 | 3 => true, | |
| 564 | 2 => false, | |
| 565 | else => unreachable, | |
| 566 | }; | |
| 567 | const addend: i64 = if (is_64bit) | |
| 568 | mem.readIntLittle(i64, self.block.code[offset..][0..8]) | |
| 569 | else | |
| 570 | mem.readIntLittle(i32, self.block.code[offset..][0..4]); | |
| 571 | ||
| 572 | var unsigned = try self.object.allocator.create(reloc.Unsigned); | |
| 573 | errdefer self.object.allocator.destroy(unsigned); | |
| 574 | ||
| 575 | unsigned.* = .{ | |
| 576 | .base = .{ | |
| 577 | .@"type" = .unsigned, | |
| 578 | .offset = offset, | |
| 579 | .target = target, | |
| 580 | .block = self.block, | |
| 581 | }, | |
| 582 | .subtractor = self.subtractor, | |
| 583 | .is_64bit = is_64bit, | |
| 584 | .addend = addend, | |
| 585 | }; | |
| 586 | ||
| 587 | return &unsigned.base; | |
| 588 | } | |
| 589 | ||
| 590 | fn parsePointerToGot(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 591 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 592 | assert(rel_type == .ARM64_RELOC_POINTER_TO_GOT); | |
| 593 | assert(rel.r_pcrel == 1); | |
| 594 | assert(rel.r_length == 2); | |
| 595 | ||
| 596 | var ptr_to_got = try self.object.allocator.create(PointerToGot); | |
| 597 | errdefer self.object.allocator.destroy(ptr_to_got); | |
| 598 | ||
| 599 | const target = try self.object.symbolFromReloc(rel); | |
| 600 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 601 | ||
| 602 | ptr_to_got.* = .{ | |
| 603 | .base = .{ | |
| 604 | .@"type" = .pointer_to_got, | |
| 605 | .offset = offset, | |
| 606 | .target = target, | |
| 607 | .block = self.block, | |
| 608 | }, | |
| 609 | }; | |
| 610 | ||
| 611 | return &ptr_to_got.base; | |
| 612 | } | |
| 613 | }; | |
| 614 | ||
| 615 | inline fn isArithmeticOp(inst: *const [4]u8) bool { | |
| 616 | const group_decode = @truncate(u5, inst[3]); | |
| 617 | return ((group_decode >> 2) == 4); | |
| 618 | } |
src/link/MachO/reloc/x86_64.zig deleted-385| ... | ... | @@ -1,385 +0,0 @@ |
| 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 Object = @import("../Object.zig"); | |
| 12 | const Relocation = reloc.Relocation; | |
| 13 | const Symbol = @import("../Symbol.zig"); | |
| 14 | const TextBlock = Zld.TextBlock; | |
| 15 | const Zld = @import("../Zld.zig"); | |
| 16 | ||
| 17 | pub const Branch = struct { | |
| 18 | base: Relocation, | |
| 19 | ||
| 20 | pub const base_type: Relocation.Type = .branch_x86_64; | |
| 21 | ||
| 22 | // pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void { | |
| 23 | // const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | |
| 24 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 25 | // mem.writeIntLittle(u32, branch.base.code[0..4], @bitCast(u32, displacement)); | |
| 26 | // } | |
| 27 | ||
| 28 | pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 29 | _ = self; | |
| 30 | _ = fmt; | |
| 31 | _ = options; | |
| 32 | _ = writer; | |
| 33 | } | |
| 34 | }; | |
| 35 | ||
| 36 | pub const Signed = struct { | |
| 37 | base: Relocation, | |
| 38 | addend: i32, | |
| 39 | correction: i4, | |
| 40 | ||
| 41 | pub const base_type: Relocation.Type = .signed; | |
| 42 | ||
| 43 | // pub fn resolve(signed: Signed, args: Relocation.ResolveArgs) !void { | |
| 44 | // const target_addr = target_addr: { | |
| 45 | // if (signed.base.target == .section) { | |
| 46 | // const source_target = @intCast(i64, args.source_source_sect_addr.?) + @intCast(i64, signed.base.offset) + signed.addend + 4; | |
| 47 | // const source_disp = source_target - @intCast(i64, args.source_target_sect_addr.?); | |
| 48 | // break :target_addr @intCast(i64, args.target_addr) + source_disp; | |
| 49 | // } | |
| 50 | // break :target_addr @intCast(i64, args.target_addr) + signed.addend; | |
| 51 | // }; | |
| 52 | // const displacement = try math.cast( | |
| 53 | // i32, | |
| 54 | // target_addr - @intCast(i64, args.source_addr) - signed.correction - 4, | |
| 55 | // ); | |
| 56 | ||
| 57 | // log.debug(" | addend 0x{x}", .{signed.addend}); | |
| 58 | // log.debug(" | correction 0x{x}", .{signed.correction}); | |
| 59 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 60 | ||
| 61 | // mem.writeIntLittle(u32, signed.base.code[0..4], @bitCast(u32, displacement)); | |
| 62 | // } | |
| 63 | ||
| 64 | pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 65 | _ = fmt; | |
| 66 | _ = options; | |
| 67 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 68 | try std.fmt.format(writer, ".correction = {}, ", .{self.correction}); | |
| 69 | } | |
| 70 | }; | |
| 71 | ||
| 72 | pub const GotLoad = struct { | |
| 73 | base: Relocation, | |
| 74 | ||
| 75 | pub const base_type: Relocation.Type = .got_load; | |
| 76 | ||
| 77 | // pub fn resolve(got_load: GotLoad, args: Relocation.ResolveArgs) !void { | |
| 78 | // const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | |
| 79 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 80 | // mem.writeIntLittle(u32, got_load.base.code[0..4], @bitCast(u32, displacement)); | |
| 81 | // } | |
| 82 | ||
| 83 | pub fn format(self: GotLoad, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 84 | _ = self; | |
| 85 | _ = fmt; | |
| 86 | _ = options; | |
| 87 | _ = writer; | |
| 88 | } | |
| 89 | }; | |
| 90 | ||
| 91 | pub const Got = struct { | |
| 92 | base: Relocation, | |
| 93 | addend: i32, | |
| 94 | ||
| 95 | pub const base_type: Relocation.Type = .got; | |
| 96 | ||
| 97 | // pub fn resolve(got: Got, args: Relocation.ResolveArgs) !void { | |
| 98 | // const displacement = try math.cast( | |
| 99 | // i32, | |
| 100 | // @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + got.addend, | |
| 101 | // ); | |
| 102 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 103 | // mem.writeIntLittle(u32, got.base.code[0..4], @bitCast(u32, displacement)); | |
| 104 | // } | |
| 105 | ||
| 106 | pub fn format(self: Got, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 107 | _ = fmt; | |
| 108 | _ = options; | |
| 109 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 110 | } | |
| 111 | }; | |
| 112 | ||
| 113 | pub const Tlv = struct { | |
| 114 | base: Relocation, | |
| 115 | ||
| 116 | pub const base_type: Relocation.Type = .tlv; | |
| 117 | ||
| 118 | // pub fn resolve(tlv: Tlv, args: Relocation.ResolveArgs) !void { | |
| 119 | // // We need to rewrite the opcode from movq to leaq. | |
| 120 | // tlv.op.* = 0x8d; | |
| 121 | // log.debug(" | rewriting op to leaq", .{}); | |
| 122 | ||
| 123 | // const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | |
| 124 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 125 | ||
| 126 | // mem.writeIntLittle(u32, tlv.base.code[0..4], @bitCast(u32, displacement)); | |
| 127 | // } | |
| 128 | pub fn format(self: Tlv, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 129 | _ = self; | |
| 130 | _ = fmt; | |
| 131 | _ = options; | |
| 132 | _ = writer; | |
| 133 | } | |
| 134 | }; | |
| 135 | ||
| 136 | pub const Parser = struct { | |
| 137 | object: *Object, | |
| 138 | zld: *Zld, | |
| 139 | it: *reloc.RelocIterator, | |
| 140 | block: *TextBlock, | |
| 141 | base_addr: u64, | |
| 142 | subtractor: ?*Symbol = null, | |
| 143 | ||
| 144 | pub fn parse(self: *Parser) !void { | |
| 145 | while (self.it.next()) |rel| { | |
| 146 | const out_rel = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { | |
| 147 | .X86_64_RELOC_BRANCH => try self.parseBranch(rel), | |
| 148 | .X86_64_RELOC_SUBTRACTOR => { | |
| 149 | // Subtractor is not a relocation with effect on the TextBlock, so | |
| 150 | // parse it and carry on. | |
| 151 | try self.parseSubtractor(rel); | |
| 152 | continue; | |
| 153 | }, | |
| 154 | .X86_64_RELOC_UNSIGNED => try self.parseUnsigned(rel), | |
| 155 | .X86_64_RELOC_SIGNED, | |
| 156 | .X86_64_RELOC_SIGNED_1, | |
| 157 | .X86_64_RELOC_SIGNED_2, | |
| 158 | .X86_64_RELOC_SIGNED_4, | |
| 159 | => try self.parseSigned(rel), | |
| 160 | .X86_64_RELOC_GOT_LOAD => try self.parseGotLoad(rel), | |
| 161 | .X86_64_RELOC_GOT => try self.parseGot(rel), | |
| 162 | .X86_64_RELOC_TLV => try self.parseTlv(rel), | |
| 163 | }; | |
| 164 | try self.block.relocs.append(out_rel); | |
| 165 | ||
| 166 | if (out_rel.target.payload == .regular) { | |
| 167 | try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {}); | |
| 168 | } | |
| 169 | ||
| 170 | switch (out_rel.@"type") { | |
| 171 | .got_load, .got => { | |
| 172 | const sym = out_rel.target; | |
| 173 | ||
| 174 | if (sym.got_index != null) continue; | |
| 175 | ||
| 176 | const index = @intCast(u32, self.zld.got_entries.items.len); | |
| 177 | sym.got_index = index; | |
| 178 | try self.zld.got_entries.append(self.zld.allocator, sym); | |
| 179 | ||
| 180 | log.debug("adding GOT entry for symbol {s} at index {}", .{ sym.name, index }); | |
| 181 | }, | |
| 182 | .branch_x86_64 => { | |
| 183 | const sym = out_rel.target; | |
| 184 | ||
| 185 | if (sym.stubs_index != null) continue; | |
| 186 | if (sym.payload != .proxy) continue; | |
| 187 | ||
| 188 | const index = @intCast(u32, self.zld.stubs.items.len); | |
| 189 | sym.stubs_index = index; | |
| 190 | try self.zld.stubs.append(self.zld.allocator, sym); | |
| 191 | ||
| 192 | log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index }); | |
| 193 | }, | |
| 194 | else => {}, | |
| 195 | } | |
| 196 | } | |
| 197 | } | |
| 198 | ||
| 199 | fn parseBranch(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 200 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 201 | assert(rel_type == .X86_64_RELOC_BRANCH); | |
| 202 | assert(rel.r_pcrel == 1); | |
| 203 | assert(rel.r_length == 2); | |
| 204 | ||
| 205 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 206 | const target = try self.object.symbolFromReloc(rel); | |
| 207 | ||
| 208 | var branch = try self.object.allocator.create(Branch); | |
| 209 | errdefer self.object.allocator.destroy(branch); | |
| 210 | ||
| 211 | branch.* = .{ | |
| 212 | .base = .{ | |
| 213 | .@"type" = .branch_x86_64, | |
| 214 | .offset = offset, | |
| 215 | .target = target, | |
| 216 | .block = self.block, | |
| 217 | }, | |
| 218 | }; | |
| 219 | ||
| 220 | return &branch.base; | |
| 221 | } | |
| 222 | ||
| 223 | fn parseSigned(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 224 | assert(rel.r_pcrel == 1); | |
| 225 | assert(rel.r_length == 2); | |
| 226 | ||
| 227 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 228 | const target = try self.object.symbolFromReloc(rel); | |
| 229 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 230 | const correction: i4 = switch (rel_type) { | |
| 231 | .X86_64_RELOC_SIGNED => 0, | |
| 232 | .X86_64_RELOC_SIGNED_1 => 1, | |
| 233 | .X86_64_RELOC_SIGNED_2 => 2, | |
| 234 | .X86_64_RELOC_SIGNED_4 => 4, | |
| 235 | else => unreachable, | |
| 236 | }; | |
| 237 | const addend = mem.readIntLittle(i32, self.block.code[offset..][0..4]) + correction; | |
| 238 | ||
| 239 | var signed = try self.object.allocator.create(Signed); | |
| 240 | errdefer self.object.allocator.destroy(signed); | |
| 241 | ||
| 242 | signed.* = .{ | |
| 243 | .base = .{ | |
| 244 | .@"type" = .signed, | |
| 245 | .offset = offset, | |
| 246 | .target = target, | |
| 247 | .block = self.block, | |
| 248 | }, | |
| 249 | .addend = addend, | |
| 250 | .correction = correction, | |
| 251 | }; | |
| 252 | ||
| 253 | return &signed.base; | |
| 254 | } | |
| 255 | ||
| 256 | fn parseGotLoad(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 257 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 258 | assert(rel_type == .X86_64_RELOC_GOT_LOAD); | |
| 259 | assert(rel.r_pcrel == 1); | |
| 260 | assert(rel.r_length == 2); | |
| 261 | ||
| 262 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 263 | const target = try self.object.symbolFromReloc(rel); | |
| 264 | ||
| 265 | var got_load = try self.object.allocator.create(GotLoad); | |
| 266 | errdefer self.object.allocator.destroy(got_load); | |
| 267 | ||
| 268 | got_load.* = .{ | |
| 269 | .base = .{ | |
| 270 | .@"type" = .got_load, | |
| 271 | .offset = offset, | |
| 272 | .target = target, | |
| 273 | .block = self.block, | |
| 274 | }, | |
| 275 | }; | |
| 276 | ||
| 277 | return &got_load.base; | |
| 278 | } | |
| 279 | ||
| 280 | fn parseGot(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 281 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 282 | assert(rel_type == .X86_64_RELOC_GOT); | |
| 283 | assert(rel.r_pcrel == 1); | |
| 284 | assert(rel.r_length == 2); | |
| 285 | ||
| 286 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 287 | const target = try self.object.symbolFromReloc(rel); | |
| 288 | const addend = mem.readIntLittle(i32, self.block.code[offset..][0..4]); | |
| 289 | ||
| 290 | var got = try self.object.allocator.create(Got); | |
| 291 | errdefer self.object.allocator.destroy(got); | |
| 292 | ||
| 293 | got.* = .{ | |
| 294 | .base = .{ | |
| 295 | .@"type" = .got, | |
| 296 | .offset = offset, | |
| 297 | .target = target, | |
| 298 | .block = self.block, | |
| 299 | }, | |
| 300 | .addend = addend, | |
| 301 | }; | |
| 302 | ||
| 303 | return &got.base; | |
| 304 | } | |
| 305 | ||
| 306 | fn parseTlv(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 307 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 308 | assert(rel_type == .X86_64_RELOC_TLV); | |
| 309 | assert(rel.r_pcrel == 1); | |
| 310 | assert(rel.r_length == 2); | |
| 311 | ||
| 312 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 313 | const target = try self.object.symbolFromReloc(rel); | |
| 314 | ||
| 315 | var tlv = try self.object.allocator.create(Tlv); | |
| 316 | errdefer self.object.allocator.destroy(tlv); | |
| 317 | ||
| 318 | tlv.* = .{ | |
| 319 | .base = .{ | |
| 320 | .@"type" = .tlv, | |
| 321 | .offset = offset, | |
| 322 | .target = target, | |
| 323 | .block = self.block, | |
| 324 | }, | |
| 325 | }; | |
| 326 | ||
| 327 | return &tlv.base; | |
| 328 | } | |
| 329 | ||
| 330 | fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void { | |
| 331 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 332 | assert(rel_type == .X86_64_RELOC_SUBTRACTOR); | |
| 333 | assert(rel.r_pcrel == 0); | |
| 334 | assert(self.subtractor == null); | |
| 335 | ||
| 336 | self.subtractor = try self.object.symbolFromReloc(rel); | |
| 337 | ||
| 338 | // Verify SUBTRACTOR is followed by UNSIGNED. | |
| 339 | const next = @intToEnum(macho.reloc_type_x86_64, self.it.peek().r_type); | |
| 340 | if (next != .X86_64_RELOC_UNSIGNED) { | |
| 341 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | |
| 342 | return error.UnexpectedRelocationType; | |
| 343 | } | |
| 344 | } | |
| 345 | ||
| 346 | fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 347 | defer { | |
| 348 | // Reset parser's subtractor state | |
| 349 | self.subtractor = null; | |
| 350 | } | |
| 351 | ||
| 352 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 353 | assert(rel_type == .X86_64_RELOC_UNSIGNED); | |
| 354 | assert(rel.r_pcrel == 0); | |
| 355 | ||
| 356 | const target = try self.object.symbolFromReloc(rel); | |
| 357 | const is_64bit: bool = switch (rel.r_length) { | |
| 358 | 3 => true, | |
| 359 | 2 => false, | |
| 360 | else => unreachable, | |
| 361 | }; | |
| 362 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 363 | const addend: i64 = if (is_64bit) | |
| 364 | mem.readIntLittle(i64, self.block.code[offset..][0..8]) | |
| 365 | else | |
| 366 | mem.readIntLittle(i32, self.block.code[offset..][0..4]); | |
| 367 | ||
| 368 | var unsigned = try self.object.allocator.create(reloc.Unsigned); | |
| 369 | errdefer self.object.allocator.destroy(unsigned); | |
| 370 | ||
| 371 | unsigned.* = .{ | |
| 372 | .base = .{ | |
| 373 | .@"type" = .unsigned, | |
| 374 | .offset = offset, | |
| 375 | .target = target, | |
| 376 | .block = self.block, | |
| 377 | }, | |
| 378 | .subtractor = self.subtractor, | |
| 379 | .is_64bit = is_64bit, | |
| 380 | .addend = addend, | |
| 381 | }; | |
| 382 | ||
| 383 | return &unsigned.base; | |
| 384 | } | |
| 385 | }; |