| author | |
| committer | |
| log | 407745a5e91685d52189548620d112a4b34c8127 |
| tree | e1927fa0fb277ec135b8e1d76dc8c40647734778 |
| parent | 54a403d4ff9e20daf1843725012ae44ec828a833 |
It makes sense to have them as a dependent type since they only ever
deal with TextBlocks. Simplify Relocations to rely on symbol indices
and symbol resolver rather than pointers.5 files changed, 937 insertions(+), 976 deletions(-)
CMakeLists.txt-1| ... | ... | @@ -586,7 +586,6 @@ set(ZIG_STAGE2_SOURCES |
| 586 | 586 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig" |
| 587 | 587 | "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig" |
| 588 | 588 | "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig" |
| 589 | "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc.zig" | |
| 590 | 589 | "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig" |
| 591 | 590 | "${CMAKE_SOURCE_DIR}/src/link/tapi.zig" |
| 592 | 591 | "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig" |
src/link/MachO/Object.zig+14-103| ... | ... | @@ -290,19 +290,6 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void { |
| 290 | 290 | } |
| 291 | 291 | } |
| 292 | 292 | |
| 293 | fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anytype) usize { | |
| 294 | if (!@hasDecl(@TypeOf(predicate), "predicate")) | |
| 295 | @compileError("Predicate is required to define fn predicate(@This(), T) bool"); | |
| 296 | ||
| 297 | if (start == haystack.len) return start; | |
| 298 | ||
| 299 | var i = start; | |
| 300 | while (i < haystack.len) : (i += 1) { | |
| 301 | if (predicate.predicate(haystack[i])) break; | |
| 302 | } | |
| 303 | return i; | |
| 304 | } | |
| 305 | ||
| 306 | 293 | const NlistWithIndex = struct { |
| 307 | 294 | nlist: macho.nlist_64, |
| 308 | 295 | index: u32, |
| ... | ... | @@ -315,44 +302,29 @@ const NlistWithIndex = struct { |
| 315 | 302 | const Predicate = struct { |
| 316 | 303 | addr: u64, |
| 317 | 304 | |
| 318 | fn predicate(self: @This(), symbol: NlistWithIndex) bool { | |
| 305 | pub fn predicate(self: @This(), symbol: NlistWithIndex) bool { | |
| 319 | 306 | return symbol.nlist.n_value >= self.addr; |
| 320 | 307 | } |
| 321 | 308 | }; |
| 322 | 309 | |
| 323 | const start = findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr }); | |
| 324 | const end = findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size }); | |
| 310 | const start = Zld.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr }); | |
| 311 | const end = Zld.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size }); | |
| 325 | 312 | |
| 326 | 313 | return symbols[start..end]; |
| 327 | 314 | } |
| 328 | 315 | }; |
| 329 | 316 | |
| 330 | fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64) []macho.relocation_info { | |
| 331 | const Predicate = struct { | |
| 332 | addr: u64, | |
| 333 | ||
| 334 | fn predicate(self: @This(), rel: macho.relocation_info) bool { | |
| 335 | return rel.r_address < self.addr; | |
| 336 | } | |
| 337 | }; | |
| 338 | ||
| 339 | const start = findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr }); | |
| 340 | const end = findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr }); | |
| 341 | ||
| 342 | return relocs[start..end]; | |
| 343 | } | |
| 344 | ||
| 345 | 317 | fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64) []macho.data_in_code_entry { |
| 346 | 318 | const Predicate = struct { |
| 347 | 319 | addr: u64, |
| 348 | 320 | |
| 349 | fn predicate(self: @This(), dice: macho.data_in_code_entry) bool { | |
| 321 | pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool { | |
| 350 | 322 | return dice.offset >= self.addr; |
| 351 | 323 | } |
| 352 | 324 | }; |
| 353 | 325 | |
| 354 | const start = findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr }); | |
| 355 | const end = findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr }); | |
| 326 | const start = Zld.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr }); | |
| 327 | const end = Zld.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr }); | |
| 356 | 328 | |
| 357 | 329 | return dices[start..end]; |
| 358 | 330 | } |
| ... | ... | @@ -483,10 +455,10 @@ const TextBlockParser = struct { |
| 483 | 455 | } |
| 484 | 456 | } |
| 485 | 457 | |
| 486 | const relocs = filterRelocs(self.relocs, start_addr, end_addr); | |
| 487 | if (relocs.len > 0) { | |
| 488 | try self.object.parseRelocs(self.zld, relocs, block, start_addr); | |
| 489 | } | |
| 458 | try block.parseRelocsFromObject(relocs, object, .{ | |
| 459 | .base_addr = start_addr, | |
| 460 | .zld = self.zld, | |
| 461 | }); | |
| 490 | 462 | |
| 491 | 463 | if (self.zld.has_dices) { |
| 492 | 464 | const dices = filterDice( |
| ... | ... | @@ -745,8 +717,6 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 745 | 717 | .n_desc = 0, |
| 746 | 718 | .n_value = sect.addr, |
| 747 | 719 | }); |
| 748 | const block_local = &zld.locals.items[block_local_sym_index]; | |
| 749 | block_local.n_sect = zld.sectionId(match); | |
| 750 | 720 | |
| 751 | 721 | const block = try self.allocator.create(TextBlock); |
| 752 | 722 | errdefer self.allocator.destroy(block); |
| ... | ... | @@ -757,69 +727,10 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 757 | 727 | block.size = sect.size; |
| 758 | 728 | block.alignment = sect.@"align"; |
| 759 | 729 | |
| 760 | try block.relocs.ensureTotalCapacity(relocs.len); | |
| 761 | for (relocs) |rel| { | |
| 762 | const out_rel: TextBlock.Relocation = outer: { | |
| 763 | if (rel.r_extern == 0) { | |
| 764 | const rel_sect_id = @intCast(u16, rel.r_symbolnum - 1); | |
| 765 | const sect_sym_index = self.sections_as_symbols.get(rel_sect_id) orelse blk: { | |
| 766 | const sect_sym_index = @intCast(u32, zld.locals.items.len); | |
| 767 | const sect_sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ | |
| 768 | self.name.?, | |
| 769 | segmentName(sect), | |
| 770 | sectionName(sect), | |
| 771 | }); | |
| 772 | defer self.allocator.free(sect_sym_name); | |
| 773 | try zld.locals.append(zld.allocator, .{ | |
| 774 | .n_strx = try zld.makeString(sect_sym_name), | |
| 775 | .n_type = macho.N_SECT, | |
| 776 | .n_sect = 0, | |
| 777 | .n_desc = 0, | |
| 778 | .n_value = 0, | |
| 779 | }); | |
| 780 | try self.sections_as_symbols.putNoClobber(self.allocator, rel_sect_id, sect_sym_index); | |
| 781 | break :blk sect_sym_index; | |
| 782 | }; | |
| 783 | break :outer .{ | |
| 784 | .inner = rel, | |
| 785 | .where = .local, | |
| 786 | .where_index = sect_sym_index, | |
| 787 | }; | |
| 788 | } | |
| 789 | ||
| 790 | const rel_sym = self.symtab.items[rel.r_symbolnum]; | |
| 791 | const rel_sym_name = self.getString(rel_sym.n_strx); | |
| 792 | ||
| 793 | if (Zld.symbolIsSect(rel_sym) and !Zld.symbolIsExt(rel_sym)) { | |
| 794 | const where_index = self.symbol_mapping.get(rel.r_symbolnum) orelse unreachable; | |
| 795 | break :outer .{ | |
| 796 | .inner = rel, | |
| 797 | .where = .local, | |
| 798 | .where_index = where_index, | |
| 799 | }; | |
| 800 | } | |
| 801 | ||
| 802 | const resolv = zld.symbol_resolver.get(rel_sym_name) orelse unreachable; | |
| 803 | switch (resolv.where) { | |
| 804 | .global => { | |
| 805 | break :outer .{ | |
| 806 | .inner = rel, | |
| 807 | .where = .local, | |
| 808 | .where_index = resolv.local_sym_index, | |
| 809 | }; | |
| 810 | }, | |
| 811 | .import => { | |
| 812 | break :outer .{ | |
| 813 | .inner = rel, | |
| 814 | .where = .import, | |
| 815 | .where_index = resolv.where_index, | |
| 816 | }; | |
| 817 | }, | |
| 818 | else => unreachable, | |
| 819 | } | |
| 820 | }; | |
| 821 | block.relocs.appendAssumeCapacity(out_rel); | |
| 822 | } | |
| 730 | try block.parseRelocsFromObject(relocs, self, .{ | |
| 731 | .base_addr = 0, | |
| 732 | .zld = zld, | |
| 733 | }); | |
| 823 | 734 | |
| 824 | 735 | if (zld.has_dices) { |
| 825 | 736 | const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size); |
src/link/MachO/TextBlock.zig+887-18| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | const TextBlock = @This(); |
| 2 | 2 | |
| 3 | 3 | const std = @import("std"); |
| 4 | const assert = std.debug.assert; | |
| 4 | 5 | const commands = @import("commands.zig"); |
| 5 | 6 | const log = std.log.scoped(.text_block); |
| 6 | 7 | const macho = std.macho; |
| ... | ... | @@ -8,6 +9,7 @@ const mem = std.mem; |
| 8 | 9 | |
| 9 | 10 | const Allocator = mem.Allocator; |
| 10 | 11 | const Arch = std.Target.Cpu.Arch; |
| 12 | const Object = @import("Object.zig"); | |
| 11 | 13 | const Zld = @import("Zld.zig"); |
| 12 | 14 | |
| 13 | 15 | allocator: *Allocator, |
| ... | ... | @@ -102,12 +104,396 @@ pub const Stab = union(enum) { |
| 102 | 104 | }; |
| 103 | 105 | |
| 104 | 106 | pub const Relocation = struct { |
| 105 | inner: macho.relocation_info, | |
| 107 | /// Offset within the `block`s code buffer. | |
| 108 | /// Note relocation size can be inferred by relocation's kind. | |
| 109 | offset: u32, | |
| 110 | ||
| 106 | 111 | where: enum { |
| 107 | 112 | local, |
| 108 | 113 | import, |
| 109 | 114 | }, |
| 115 | ||
| 110 | 116 | where_index: u32, |
| 117 | ||
| 118 | payload: union(enum) { | |
| 119 | unsigned: Unsigned, | |
| 120 | branch: Branch, | |
| 121 | page: Page, | |
| 122 | page_off: PageOff, | |
| 123 | pointer_to_got: PointerToGot, | |
| 124 | signed: Signed, | |
| 125 | load: Load, | |
| 126 | }, | |
| 127 | ||
| 128 | const ResolveArgs = struct { | |
| 129 | block: *TextBlock, | |
| 130 | offset: u32, | |
| 131 | source_addr: u64, | |
| 132 | target_addr: u64, | |
| 133 | zld: *Zld, | |
| 134 | }; | |
| 135 | ||
| 136 | pub const Unsigned = struct { | |
| 137 | subtractor: ?u32, | |
| 138 | ||
| 139 | /// Addend embedded directly in the relocation slot | |
| 140 | addend: i64, | |
| 141 | ||
| 142 | /// Extracted from r_length: | |
| 143 | /// => 3 implies true | |
| 144 | /// => 2 implies false | |
| 145 | /// => * is unreachable | |
| 146 | is_64bit: bool, | |
| 147 | ||
| 148 | pub fn resolve(self: Unsigned, args: ResolveArgs) !void { | |
| 149 | const result = if (self.subtractor) |subtractor| | |
| 150 | @intCast(i64, args.target_addr) - @intCast(i64, subtractor.payload.regular.address) + self.addend | |
| 151 | else | |
| 152 | @intCast(i64, args.target_addr) + self.addend; | |
| 153 | ||
| 154 | if (self.is_64bit) { | |
| 155 | mem.writeIntLittle(u64, args.block.code[args.offset..][0..8], @bitCast(u64, result)); | |
| 156 | } else { | |
| 157 | mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @truncate(u32, @bitCast(u64, result))); | |
| 158 | } | |
| 159 | } | |
| 160 | ||
| 161 | pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 162 | _ = fmt; | |
| 163 | _ = options; | |
| 164 | try std.fmt.format(writer, "Unsigned {{ ", .{}); | |
| 165 | if (self.subtractor) |sub| { | |
| 166 | try std.fmt.format(writer, ".subtractor = {}, ", .{sub}); | |
| 167 | } | |
| 168 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 169 | const length: usize = if (self.is_64bit) 8 else 4; | |
| 170 | try std.fmt.format(writer, ".length = {}, ", .{length}); | |
| 171 | try std.fmt.format(writer, "}}", .{}); | |
| 172 | } | |
| 173 | }; | |
| 174 | ||
| 175 | pub const Branch = struct { | |
| 176 | arch: Arch, | |
| 177 | ||
| 178 | pub fn resolve(self: Branch, args: ResolveArgs) !void { | |
| 179 | switch (self.arch) { | |
| 180 | .aarch64 => { | |
| 181 | const displacement = try math.cast( | |
| 182 | i28, | |
| 183 | @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr), | |
| 184 | ); | |
| 185 | const code = args.block.code[args.offset..][0..4]; | |
| 186 | var inst = aarch64.Instruction{ | |
| 187 | .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload( | |
| 188 | aarch64.Instruction, | |
| 189 | aarch64.Instruction.unconditional_branch_immediate, | |
| 190 | ), code), | |
| 191 | }; | |
| 192 | inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); | |
| 193 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 194 | }, | |
| 195 | .x86_64 => { | |
| 196 | const displacement = try math.cast( | |
| 197 | i32, | |
| 198 | @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4, | |
| 199 | ); | |
| 200 | mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement)); | |
| 201 | }, | |
| 202 | else => return error.UnsupportedCpuArchitecture, | |
| 203 | } | |
| 204 | } | |
| 205 | ||
| 206 | pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 207 | _ = self; | |
| 208 | _ = fmt; | |
| 209 | _ = options; | |
| 210 | try std.fmt.format(writer, "Branch {{}}", .{}); | |
| 211 | } | |
| 212 | }; | |
| 213 | ||
| 214 | pub const Page = struct { | |
| 215 | kind: enum { | |
| 216 | page, | |
| 217 | got, | |
| 218 | tlvp, | |
| 219 | }, | |
| 220 | addend: u32 = 0, | |
| 221 | ||
| 222 | pub fn resolve(self: Page, args: ResolveArgs) !void { | |
| 223 | const target_addr = args.target_addr + self.addend; | |
| 224 | const source_page = @intCast(i32, args.source_addr >> 12); | |
| 225 | const target_page = @intCast(i32, target_addr >> 12); | |
| 226 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 227 | ||
| 228 | const code = args.block.code[args.offset..][0..4]; | |
| 229 | var inst = aarch64.Instruction{ | |
| 230 | .pc_relative_address = mem.bytesToValue(meta.TagPayload( | |
| 231 | aarch64.Instruction, | |
| 232 | aarch64.Instruction.pc_relative_address, | |
| 233 | ), code), | |
| 234 | }; | |
| 235 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 236 | inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 237 | ||
| 238 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 239 | } | |
| 240 | ||
| 241 | pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 242 | _ = fmt; | |
| 243 | _ = options; | |
| 244 | try std.fmt.format(writer, "Page {{ ", .{}); | |
| 245 | switch (self.kind) { | |
| 246 | .page => {}, | |
| 247 | .got => { | |
| 248 | try std.fmt.format(writer, ".got, ", .{}); | |
| 249 | }, | |
| 250 | .tlvp => { | |
| 251 | try std.fmt.format(writer, ".tlvp", .{}); | |
| 252 | }, | |
| 253 | } | |
| 254 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 255 | try std.fmt.format(writer, "}}", .{}); | |
| 256 | } | |
| 257 | }; | |
| 258 | ||
| 259 | pub const PageOff = struct { | |
| 260 | kind: enum { | |
| 261 | page, | |
| 262 | got, | |
| 263 | tlvp, | |
| 264 | }, | |
| 265 | addend: u32 = 0, | |
| 266 | op_kind: ?OpKind = null, | |
| 267 | ||
| 268 | pub const OpKind = enum { | |
| 269 | arithmetic, | |
| 270 | load, | |
| 271 | }; | |
| 272 | ||
| 273 | pub fn resolve(self: PageOff, args: ResolveArgs) !void { | |
| 274 | const code = args.block.code[args.offset..][0..4]; | |
| 275 | ||
| 276 | switch (self.kind) { | |
| 277 | .page => { | |
| 278 | const target_addr = args.target_addr + self.addend; | |
| 279 | const narrowed = @truncate(u12, target_addr); | |
| 280 | ||
| 281 | const op_kind = self.op_kind orelse unreachable; | |
| 282 | var inst: aarch64.Instruction = blk: { | |
| 283 | switch (op_kind) { | |
| 284 | .arithmetic => { | |
| 285 | break :blk .{ | |
| 286 | .add_subtract_immediate = mem.bytesToValue(meta.TagPayload( | |
| 287 | aarch64.Instruction, | |
| 288 | aarch64.Instruction.add_subtract_immediate, | |
| 289 | ), code), | |
| 290 | }; | |
| 291 | }, | |
| 292 | .load => { | |
| 293 | break :blk .{ | |
| 294 | .load_store_register = mem.bytesToValue(meta.TagPayload( | |
| 295 | aarch64.Instruction, | |
| 296 | aarch64.Instruction.load_store_register, | |
| 297 | ), code), | |
| 298 | }; | |
| 299 | }, | |
| 300 | } | |
| 301 | }; | |
| 302 | ||
| 303 | if (op_kind == .arithmetic) { | |
| 304 | inst.add_subtract_immediate.imm12 = narrowed; | |
| 305 | } else { | |
| 306 | const offset: u12 = blk: { | |
| 307 | if (inst.load_store_register.size == 0) { | |
| 308 | if (inst.load_store_register.v == 1) { | |
| 309 | // 128-bit SIMD is scaled by 16. | |
| 310 | break :blk try math.divExact(u12, narrowed, 16); | |
| 311 | } | |
| 312 | // Otherwise, 8-bit SIMD or ldrb. | |
| 313 | break :blk narrowed; | |
| 314 | } else { | |
| 315 | const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size); | |
| 316 | break :blk try math.divExact(u12, narrowed, denom); | |
| 317 | } | |
| 318 | }; | |
| 319 | inst.load_store_register.offset = offset; | |
| 320 | } | |
| 321 | ||
| 322 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 323 | }, | |
| 324 | .got => { | |
| 325 | const narrowed = @truncate(u12, args.target_addr); | |
| 326 | var inst: aarch64.Instruction = .{ | |
| 327 | .load_store_register = mem.bytesToValue(meta.TagPayload( | |
| 328 | aarch64.Instruction, | |
| 329 | aarch64.Instruction.load_store_register, | |
| 330 | ), code), | |
| 331 | }; | |
| 332 | const offset = try math.divExact(u12, narrowed, 8); | |
| 333 | inst.load_store_register.offset = offset; | |
| 334 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 335 | }, | |
| 336 | .tlvp => { | |
| 337 | const RegInfo = struct { | |
| 338 | rd: u5, | |
| 339 | rn: u5, | |
| 340 | size: u1, | |
| 341 | }; | |
| 342 | const reg_info: RegInfo = blk: { | |
| 343 | if (isArithmeticOp(code)) { | |
| 344 | const inst = mem.bytesToValue(meta.TagPayload( | |
| 345 | aarch64.Instruction, | |
| 346 | aarch64.Instruction.add_subtract_immediate, | |
| 347 | ), code); | |
| 348 | break :blk .{ | |
| 349 | .rd = inst.rd, | |
| 350 | .rn = inst.rn, | |
| 351 | .size = inst.sf, | |
| 352 | }; | |
| 353 | } else { | |
| 354 | const inst = mem.bytesToValue(meta.TagPayload( | |
| 355 | aarch64.Instruction, | |
| 356 | aarch64.Instruction.load_store_register, | |
| 357 | ), code); | |
| 358 | break :blk .{ | |
| 359 | .rd = inst.rt, | |
| 360 | .rn = inst.rn, | |
| 361 | .size = @truncate(u1, inst.size), | |
| 362 | }; | |
| 363 | } | |
| 364 | }; | |
| 365 | const narrowed = @truncate(u12, args.target_addr); | |
| 366 | var inst = aarch64.Instruction{ | |
| 367 | .add_subtract_immediate = .{ | |
| 368 | .rd = reg_info.rd, | |
| 369 | .rn = reg_info.rn, | |
| 370 | .imm12 = narrowed, | |
| 371 | .sh = 0, | |
| 372 | .s = 0, | |
| 373 | .op = 0, | |
| 374 | .sf = reg_info.size, | |
| 375 | }, | |
| 376 | }; | |
| 377 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 378 | }, | |
| 379 | } | |
| 380 | } | |
| 381 | ||
| 382 | pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 383 | _ = fmt; | |
| 384 | _ = options; | |
| 385 | try std.fmt.format(writer, "PageOff {{ ", .{}); | |
| 386 | switch (self.kind) { | |
| 387 | .page => {}, | |
| 388 | .got => { | |
| 389 | try std.fmt.format(writer, ".got, ", .{}); | |
| 390 | }, | |
| 391 | .tlvp => { | |
| 392 | try std.fmt.format(writer, ".tlvp, ", .{}); | |
| 393 | }, | |
| 394 | } | |
| 395 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 396 | try std.fmt.format(writer, ".op_kind = {s}, ", .{self.op_kind}); | |
| 397 | try std.fmt.format(writer, "}}", .{}); | |
| 398 | } | |
| 399 | }; | |
| 400 | ||
| 401 | pub const PointerToGot = struct { | |
| 402 | pub fn resolve(_: PointerToGot, args: ResolveArgs) !void { | |
| 403 | const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr)); | |
| 404 | mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, result)); | |
| 405 | } | |
| 406 | ||
| 407 | pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 408 | _ = self; | |
| 409 | _ = fmt; | |
| 410 | _ = options; | |
| 411 | try std.fmt.format(writer, "PointerToGot {{}}", .{}); | |
| 412 | } | |
| 413 | }; | |
| 414 | ||
| 415 | pub const Signed = struct { | |
| 416 | addend: i64, | |
| 417 | correction: i4, | |
| 418 | ||
| 419 | pub fn resolve(self: Signed, args: ResolveArgs) !void { | |
| 420 | const target_addr = @intCast(i64, args.target_addr) + self.addend; | |
| 421 | const displacement = try math.cast( | |
| 422 | i32, | |
| 423 | target_addr - @intCast(i64, args.source_addr) - self.correction - 4, | |
| 424 | ); | |
| 425 | mem.writeIntLittle(u32, block.code[offset..][0..4], @bitCast(u32, displacement)); | |
| 426 | } | |
| 427 | ||
| 428 | pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 429 | _ = fmt; | |
| 430 | _ = options; | |
| 431 | try std.fmt.format(writer, "Signed {{ ", .{}); | |
| 432 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 433 | try std.fmt.format(writer, ".correction = {}, ", .{self.correction}); | |
| 434 | try std.fmt.format(writer, "}}", .{}); | |
| 435 | } | |
| 436 | }; | |
| 437 | ||
| 438 | pub const Load = struct { | |
| 439 | kind: enum { | |
| 440 | got, | |
| 441 | tlvp, | |
| 442 | }, | |
| 443 | addend: i32 = 0, | |
| 444 | ||
| 445 | pub fn resolve(self: Load, block: *TextBlock, offset: u32, args: ResolveArgs) !void { | |
| 446 | if (self.kind == .tlvp) { | |
| 447 | // We need to rewrite the opcode from movq to leaq. | |
| 448 | block.code[offset - 2] = 0x8d; | |
| 449 | } | |
| 450 | const displacement = try math.cast( | |
| 451 | i32, | |
| 452 | @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + self.addend, | |
| 453 | ); | |
| 454 | mem.writeIntLittle(u32, block.code[offset..][0..4], @bitCast(u32, displacement)); | |
| 455 | } | |
| 456 | ||
| 457 | pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 458 | _ = fmt; | |
| 459 | _ = options; | |
| 460 | try std.fmt.format(writer, "Load {{ ", .{}); | |
| 461 | try std.fmt.format(writer, "{s}, ", .{self.kind}); | |
| 462 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 463 | try std.fmt.format(writer, "}}", .{}); | |
| 464 | } | |
| 465 | }; | |
| 466 | ||
| 467 | pub fn resolve(self: Relocation, block: *TextBlock, args: ResolveArgs) !void { | |
| 468 | switch (self.payload) { | |
| 469 | .unsigned => |unsigned| try unsigned.resolve(block, self.offset, args), | |
| 470 | .branch => |branch| try branch.resolve(block, self.offset, args), | |
| 471 | .page => |page| try page.resolve(block, self.offset, args), | |
| 472 | .page_off => |page_off| try page_off.resolve(block, self.offset, args), | |
| 473 | .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(block, self.offset, args), | |
| 474 | .signed => |signed| try signed.resolve(block, self.offset, args), | |
| 475 | .load => |load| try load.resolve(block, self.offset, args), | |
| 476 | } | |
| 477 | } | |
| 478 | ||
| 479 | pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 480 | try std.fmt.format(writer, "Relocation {{ ", .{}); | |
| 481 | try std.fmt.format(writer, ".offset = {}, ", .{self.offset}); | |
| 482 | try std.fmt.format(writer, ".where = {}, ", .{self.where}); | |
| 483 | try std.fmt.format(writer, ".where_index = {d}, ", .{self.where_index}); | |
| 484 | ||
| 485 | switch (self.payload) { | |
| 486 | .unsigned => |unsigned| try unsigned.format(fmt, options, writer), | |
| 487 | .branch => |branch| try branch.format(fmt, options, writer), | |
| 488 | .page => |page| try page.format(fmt, options, writer), | |
| 489 | .page_off => |page_off| try page_off.format(fmt, options, writer), | |
| 490 | .pointer_to_got => |pointer_to_got| try pointer_to_got.format(fmt, options, writer), | |
| 491 | .signed => |signed| try signed.format(fmt, options, writer), | |
| 492 | .load => |load| try load.format(fmt, options, writer), | |
| 493 | } | |
| 494 | ||
| 495 | try std.fmt.format(writer, "}}", .{}); | |
| 496 | } | |
| 111 | 497 | }; |
| 112 | 498 | |
| 113 | 499 | pub fn init(allocator: *Allocator) TextBlock { |
| ... | ... | @@ -139,6 +525,462 @@ pub fn deinit(self: *TextBlock) void { |
| 139 | 525 | self.dices.deinit(); |
| 140 | 526 | } |
| 141 | 527 | |
| 528 | const RelocContext = struct { | |
| 529 | base_addr: u64 = 0, | |
| 530 | zld: *Zld, | |
| 531 | }; | |
| 532 | ||
| 533 | fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocContext) !Relocation { | |
| 534 | var parsed_rel = Relocation{ | |
| 535 | .offset = @intCast(u32, @intCast(u64, rel.r_address) - ctx.base_addr), | |
| 536 | .where = undefined, | |
| 537 | .where_index = undefined, | |
| 538 | .payload = undefined, | |
| 539 | }; | |
| 540 | ||
| 541 | if (rel.r_extern == 0) { | |
| 542 | const sect_id = @intCast(u16, rel.r_symbolnum - 1); | |
| 543 | ||
| 544 | const local_sym_index = object.sections_as_symbols.get(sect_id) orelse blk: { | |
| 545 | const seg = object.load_commands.items[object.segment_cmd_index.?].Segment; | |
| 546 | const sect = seg.sections.items[sect_id]; | |
| 547 | const local_sym_index = @intCast(u32, ctx.zld.locals.items.len); | |
| 548 | const sym_name = try std.fmt.allocPrint(ctx.zld.allocator, "l_{s}_{s}_{s}", .{ | |
| 549 | object.name.?, | |
| 550 | commands.segmentName(sect), | |
| 551 | commands.sectionName(sect), | |
| 552 | }); | |
| 553 | defer ctx.zld.allocator.free(sym_name); | |
| 554 | ||
| 555 | try ctx.zld.locals.append(ctx.zld.allocator, .{ | |
| 556 | .n_strx = try ctx.zld.makeString(sym_name), | |
| 557 | .n_type = macho.N_SECT, | |
| 558 | .n_sect = 0, | |
| 559 | .n_desc = 0, | |
| 560 | .n_value = 0, | |
| 561 | }); | |
| 562 | try object.sections_as_symbols.putNoClobber(object.allocator, sect_id, local_sym_index); | |
| 563 | break :blk local_sym_index; | |
| 564 | }; | |
| 565 | ||
| 566 | parsed_rel.where = .local; | |
| 567 | parsed_rel.where_index = local_sym_index; | |
| 568 | } else { | |
| 569 | const sym = object.symtab.items[rel.r_symbolnum]; | |
| 570 | const sym_name = object.getString(sym.n_strx); | |
| 571 | ||
| 572 | if (Zld.symbolIsSect(sym) and !Zld.symbolIsExt(sym)) { | |
| 573 | const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable; | |
| 574 | parsed_rel.where = .local; | |
| 575 | parsed_rel.where_index = where_index; | |
| 576 | } else { | |
| 577 | const resolv = ctx.zld.symbol_resolver.get(sym_name) orelse unreachable; | |
| 578 | switch (resolv.where) { | |
| 579 | .global => { | |
| 580 | parsed_rel.where = .local; | |
| 581 | parsed_rel.where_index = resolv.local_sym_index; | |
| 582 | }, | |
| 583 | .import => { | |
| 584 | parsed_rel.where = .import; | |
| 585 | parsed_rel.where_index = resolv.where_index; | |
| 586 | }, | |
| 587 | else => unreachable, | |
| 588 | } | |
| 589 | } | |
| 590 | } | |
| 591 | ||
| 592 | return parsed_rel; | |
| 593 | } | |
| 594 | ||
| 595 | pub fn parseRelocsFromObject( | |
| 596 | self: *TextBlock, | |
| 597 | relocs: []macho.relocation_info, | |
| 598 | object: *Object, | |
| 599 | ctx: RelocContext, | |
| 600 | ) !void { | |
| 601 | const filtered_relocs = filterRelocs(relocs, ctx.base_addr, ctx.base_addr + self.size); | |
| 602 | var it = RelocIterator{ | |
| 603 | .buffer = filtered_relocs, | |
| 604 | }; | |
| 605 | ||
| 606 | var addend: u32 = 0; | |
| 607 | var subtractor: ?u32 = null; | |
| 608 | ||
| 609 | while (it.next()) |rel| { | |
| 610 | if (isAddend(rel, object.arch.?)) { | |
| 611 | // Addend is not a relocation with effect on the TextBlock, so | |
| 612 | // parse it and carry on. | |
| 613 | assert(addend == 0); // Oh no, addend was not reset! | |
| 614 | addend = rel.r_symbolnum; | |
| 615 | ||
| 616 | // Verify ADDEND is followed by a load. | |
| 617 | const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type); | |
| 618 | switch (next) { | |
| 619 | .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {}, | |
| 620 | else => { | |
| 621 | log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next}); | |
| 622 | return error.UnexpectedRelocationType; | |
| 623 | }, | |
| 624 | } | |
| 625 | continue; | |
| 626 | } | |
| 627 | ||
| 628 | if (isSubtractor(rel, object.arch.?)) { | |
| 629 | // Subtractor is not a relocation with effect on the TextBlock, so | |
| 630 | // parse it and carry on. | |
| 631 | assert(subtractor == null); // Oh no, subtractor was not reset! | |
| 632 | assert(rel.r_extern == 1); | |
| 633 | const sym = object.symtab.items[rel.r_symbolnum]; | |
| 634 | const sym_name = object.getString(sym.n_strx); | |
| 635 | ||
| 636 | if (Zld.symbolIsSect(sym) and !Zld.symbolIsExt(sym)) { | |
| 637 | const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable; | |
| 638 | subtractor = where_index; | |
| 639 | } else { | |
| 640 | const resolv = ctx.zld.symbol_resolver.get(sym_name) orelse unreachable; | |
| 641 | assert(resolv.where == .global); | |
| 642 | subtractor = resolv.local_sym_index; | |
| 643 | } | |
| 644 | ||
| 645 | // Verify SUBTRACTOR is followed by UNSIGNED. | |
| 646 | switch (object.arch.?) { | |
| 647 | .aarch64 => { | |
| 648 | const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type); | |
| 649 | if (next != .ARM64_RELOC_UNSIGNED) { | |
| 650 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | |
| 651 | return error.UnexpectedRelocationType; | |
| 652 | } | |
| 653 | }, | |
| 654 | .x86_64 => { | |
| 655 | const next = @intToEnum(macho.reloc_type_x86_64, it.peek().r_type); | |
| 656 | if (next != .X86_64_RELOC_UNSIGNED) { | |
| 657 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | |
| 658 | return error.UnexpectedRelocationType; | |
| 659 | } | |
| 660 | }, | |
| 661 | else => unreachable, | |
| 662 | } | |
| 663 | continue; | |
| 664 | } | |
| 665 | ||
| 666 | var parsed_rel = try initRelocFromObject(rel, object, ctx); | |
| 667 | ||
| 668 | switch (object.arch.?) { | |
| 669 | .aarch64 => { | |
| 670 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 671 | switch (rel_type) { | |
| 672 | .ARM64_RELOC_ADDEND => unreachable, | |
| 673 | .ARM64_RELOC_SUBTRACTOR => unreachable, | |
| 674 | .ARM64_RELOC_BRANCH26 => { | |
| 675 | self.parseBranch(rel, &parsed_rel, ctx); | |
| 676 | }, | |
| 677 | .ARM64_RELOC_UNSIGNED => { | |
| 678 | self.parseUnsigned(rel, &parsed_rel, subtractor, ctx); | |
| 679 | subtractor = null; | |
| 680 | }, | |
| 681 | .ARM64_RELOC_PAGE21, | |
| 682 | .ARM64_RELOC_GOT_LOAD_PAGE21, | |
| 683 | .ARM64_RELOC_TLVP_LOAD_PAGE21, | |
| 684 | => { | |
| 685 | self.parsePage(rel, &parsed_rel, addend); | |
| 686 | if (rel_type == .ARM64_RELOC_PAGE21) | |
| 687 | addend = 0; | |
| 688 | }, | |
| 689 | .ARM64_RELOC_PAGEOFF12, | |
| 690 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, | |
| 691 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12, | |
| 692 | => { | |
| 693 | self.parsePageOff(rel, &parsed_rel, addend, ctx); | |
| 694 | if (rel_type == .ARM64_RELOC_PAGEOFF12) | |
| 695 | addend = 0; | |
| 696 | }, | |
| 697 | .ARM64_RELOC_POINTER_TO_GOT => { | |
| 698 | self.parsePointerToGot(rel, &parsed_rel); | |
| 699 | }, | |
| 700 | } | |
| 701 | }, | |
| 702 | .x86_64 => { | |
| 703 | switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { | |
| 704 | .X86_64_RELOC_SUBTRACTOR => unreachable, | |
| 705 | .X86_64_RELOC_BRANCH => { | |
| 706 | self.parseBranch(rel, &parsed_rel, ctx); | |
| 707 | }, | |
| 708 | .X86_64_RELOC_UNSIGNED => { | |
| 709 | self.parseUnsigned(rel, &parsed_rel, subtractor, ctx); | |
| 710 | subtractor = null; | |
| 711 | }, | |
| 712 | .X86_64_RELOC_SIGNED, | |
| 713 | .X86_64_RELOC_SIGNED_1, | |
| 714 | .X86_64_RELOC_SIGNED_2, | |
| 715 | .X86_64_RELOC_SIGNED_4, | |
| 716 | => { | |
| 717 | self.parseSigned(rel, &parsed_rel, ctx); | |
| 718 | }, | |
| 719 | .X86_64_RELOC_GOT_LOAD, | |
| 720 | .X86_64_RELOC_GOT, | |
| 721 | .X86_64_RELOC_TLV, | |
| 722 | => { | |
| 723 | self.parseLoad(rel, &parsed_rel); | |
| 724 | }, | |
| 725 | } | |
| 726 | }, | |
| 727 | else => unreachable, | |
| 728 | } | |
| 729 | ||
| 730 | try self.relocs.append(parsed_rel); | |
| 731 | ||
| 732 | if (parsed_rel.where == .local) { | |
| 733 | try self.references.put(parsed_rel.where_index, {}); | |
| 734 | } | |
| 735 | ||
| 736 | const is_via_got = switch (parsed_rel.payload) { | |
| 737 | .pointer_to_got => true, | |
| 738 | .load => |load| load.kind == .got, | |
| 739 | .page => |page| page.kind == .got, | |
| 740 | .page_off => |page_off| page_off.kind == .got, | |
| 741 | else => false, | |
| 742 | }; | |
| 743 | ||
| 744 | if (is_via_got) blk: { | |
| 745 | const key = Zld.GotIndirectionKey{ | |
| 746 | .where = switch (parsed_rel.where) { | |
| 747 | .local => .local, | |
| 748 | .import => .import, | |
| 749 | }, | |
| 750 | .where_index = parsed_rel.where_index, | |
| 751 | }; | |
| 752 | if (ctx.zld.got_entries.contains(key)) break :blk; | |
| 753 | ||
| 754 | try ctx.zld.got_entries.putNoClobber(ctx.zld.allocator, key, {}); | |
| 755 | } else if (parsed_rel.payload == .unsigned) { | |
| 756 | switch (parsed_rel.where) { | |
| 757 | .import => { | |
| 758 | log.warn("WAT {s}", .{ctx.zld.getString(ctx.zld.imports.items[parsed_rel.where_index].n_strx)}); | |
| 759 | try self.bindings.append(.{ | |
| 760 | .local_sym_index = parsed_rel.where_index, | |
| 761 | .offset = parsed_rel.offset, | |
| 762 | }); | |
| 763 | }, | |
| 764 | .local => { | |
| 765 | const source_sym = ctx.zld.locals.items[self.local_sym_index]; | |
| 766 | const match = ctx.zld.unpackSectionId(source_sym.n_sect); | |
| 767 | const seg = ctx.zld.load_commands.items[match.seg].Segment; | |
| 768 | const sect = seg.sections.items[match.sect]; | |
| 769 | const sect_type = commands.sectionType(sect); | |
| 770 | ||
| 771 | const should_rebase = rebase: { | |
| 772 | if (!parsed_rel.payload.unsigned.is_64bit) break :rebase false; | |
| 773 | ||
| 774 | // TODO actually, a check similar to what dyld is doing, that is, verifying | |
| 775 | // that the segment is writable should be enough here. | |
| 776 | const is_right_segment = blk: { | |
| 777 | if (ctx.zld.data_segment_cmd_index) |idx| { | |
| 778 | if (match.seg == idx) { | |
| 779 | break :blk true; | |
| 780 | } | |
| 781 | } | |
| 782 | if (ctx.zld.data_const_segment_cmd_index) |idx| { | |
| 783 | if (match.seg == idx) { | |
| 784 | break :blk true; | |
| 785 | } | |
| 786 | } | |
| 787 | break :blk false; | |
| 788 | }; | |
| 789 | ||
| 790 | if (!is_right_segment) break :rebase false; | |
| 791 | if (sect_type != macho.S_LITERAL_POINTERS and | |
| 792 | sect_type != macho.S_REGULAR and | |
| 793 | sect_type != macho.S_MOD_INIT_FUNC_POINTERS and | |
| 794 | sect_type != macho.S_MOD_TERM_FUNC_POINTERS) | |
| 795 | { | |
| 796 | break :rebase false; | |
| 797 | } | |
| 798 | ||
| 799 | break :rebase true; | |
| 800 | }; | |
| 801 | ||
| 802 | if (should_rebase) { | |
| 803 | try self.rebases.append(parsed_rel.offset); | |
| 804 | } | |
| 805 | }, | |
| 806 | } | |
| 807 | } else if (parsed_rel.payload == .branch) blk: { | |
| 808 | if (parsed_rel.where != .import) break :blk; | |
| 809 | if (ctx.zld.stubs.contains(parsed_rel.where_index)) break :blk; | |
| 810 | ||
| 811 | try ctx.zld.stubs.putNoClobber(ctx.zld.allocator, parsed_rel.where_index, {}); | |
| 812 | } | |
| 813 | } | |
| 814 | } | |
| 815 | ||
| 816 | fn isAddend(rel: macho.relocation_info, arch: Arch) bool { | |
| 817 | if (arch != .aarch64) return false; | |
| 818 | return @intToEnum(macho.reloc_type_arm64, rel.r_type) == .ARM64_RELOC_ADDEND; | |
| 819 | } | |
| 820 | ||
| 821 | fn isSubtractor(rel: macho.relocation_info, arch: Arch) bool { | |
| 822 | return switch (arch) { | |
| 823 | .aarch64 => @intToEnum(macho.reloc_type_arm64, rel.r_type) == .ARM64_RELOC_SUBTRACTOR, | |
| 824 | .x86_64 => @intToEnum(macho.reloc_type_x86_64, rel.r_type) == .X86_64_RELOC_SUBTRACTOR, | |
| 825 | else => unreachable, | |
| 826 | }; | |
| 827 | } | |
| 828 | ||
| 829 | fn parseUnsigned( | |
| 830 | self: TextBlock, | |
| 831 | rel: macho.relocation_info, | |
| 832 | out: *Relocation, | |
| 833 | subtractor: ?u32, | |
| 834 | ctx: RelocContext, | |
| 835 | ) void { | |
| 836 | assert(rel.r_pcrel == 0); | |
| 837 | ||
| 838 | const is_64bit: bool = switch (rel.r_length) { | |
| 839 | 3 => true, | |
| 840 | 2 => false, | |
| 841 | else => unreachable, | |
| 842 | }; | |
| 843 | ||
| 844 | var addend: i64 = if (is_64bit) | |
| 845 | mem.readIntLittle(i64, self.code[out.offset..][0..8]) | |
| 846 | else | |
| 847 | mem.readIntLittle(i32, self.code[out.offset..][0..4]); | |
| 848 | ||
| 849 | if (rel.r_extern == 0) { | |
| 850 | assert(out.where == .local); | |
| 851 | const target_sym = ctx.zld.locals.items[out.where_index]; | |
| 852 | addend -= @intCast(i64, target_sym.n_value); | |
| 853 | } | |
| 854 | ||
| 855 | out.payload = .{ | |
| 856 | .unsigned = .{ | |
| 857 | .subtractor = subtractor, | |
| 858 | .is_64bit = is_64bit, | |
| 859 | .addend = addend, | |
| 860 | }, | |
| 861 | }; | |
| 862 | } | |
| 863 | ||
| 864 | fn parseBranch(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ctx: RelocContext) void { | |
| 865 | assert(rel.r_pcrel == 1); | |
| 866 | assert(rel.r_length == 2); | |
| 867 | ||
| 868 | out.payload = .{ | |
| 869 | .branch = .{ | |
| 870 | .arch = ctx.zld.target.?.cpu.arch, | |
| 871 | }, | |
| 872 | }; | |
| 873 | } | |
| 874 | ||
| 875 | fn parsePage(self: TextBlock, rel: macho.relocation_info, out: *Relocation, addend: u32) void { | |
| 876 | _ = self; | |
| 877 | assert(rel.r_pcrel == 1); | |
| 878 | assert(rel.r_length == 2); | |
| 879 | ||
| 880 | out.payload = .{ | |
| 881 | .page = .{ | |
| 882 | .kind = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { | |
| 883 | .ARM64_RELOC_PAGE21 => .page, | |
| 884 | .ARM64_RELOC_GOT_LOAD_PAGE21 => .got, | |
| 885 | .ARM64_RELOC_TLVP_LOAD_PAGE21 => .tlvp, | |
| 886 | else => unreachable, | |
| 887 | }, | |
| 888 | .addend = addend, | |
| 889 | }, | |
| 890 | }; | |
| 891 | } | |
| 892 | ||
| 893 | fn parsePageOff(self: TextBlock, rel: macho.relocation_info, out: *Relocation, addend: u32, ctx: RelocContext) void { | |
| 894 | assert(rel.r_pcrel == 0); | |
| 895 | assert(rel.r_length == 2); | |
| 896 | ||
| 897 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 898 | const op_kind: ?Relocation.PageOff.OpKind = blk: { | |
| 899 | if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null; | |
| 900 | const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.code[out.offset..][0..4])) | |
| 901 | .arithmetic | |
| 902 | else | |
| 903 | .load; | |
| 904 | break :blk op_kind; | |
| 905 | }; | |
| 906 | ||
| 907 | out.payload = .{ | |
| 908 | .page_off = .{ | |
| 909 | .kind = switch (rel_type) { | |
| 910 | .ARM64_RELOC_PAGEOFF12 => .page, | |
| 911 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got, | |
| 912 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => .tlvp, | |
| 913 | else => unreachable, | |
| 914 | }, | |
| 915 | .addend = addend, | |
| 916 | .op_kind = op_kind, | |
| 917 | }, | |
| 918 | }; | |
| 919 | } | |
| 920 | ||
| 921 | fn parsePointerToGot(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void { | |
| 922 | _ = self; | |
| 923 | assert(rel.r_pcrel == 1); | |
| 924 | assert(rel.r_length == 2); | |
| 925 | ||
| 926 | out.payload = .{ | |
| 927 | .pointer_to_got = .{}, | |
| 928 | }; | |
| 929 | } | |
| 930 | ||
| 931 | fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ctx: RelocContext) void { | |
| 932 | assert(rel.r_pcrel == 1); | |
| 933 | assert(rel.r_length == 2); | |
| 934 | ||
| 935 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 936 | const correction: i4 = switch (rel_type) { | |
| 937 | .X86_64_RELOC_SIGNED => 0, | |
| 938 | .X86_64_RELOC_SIGNED_1 => 1, | |
| 939 | .X86_64_RELOC_SIGNED_2 => 2, | |
| 940 | .X86_64_RELOC_SIGNED_4 => 4, | |
| 941 | else => unreachable, | |
| 942 | }; | |
| 943 | var addend: i64 = mem.readIntLittle(i32, self.code[out.offset..][0..4]) + correction; | |
| 944 | ||
| 945 | if (rel.r_extern == 0) { | |
| 946 | const source_sym = ctx.zld.locals.items[self.local_sym_index]; | |
| 947 | const target_sym = switch (out.where) { | |
| 948 | .local => ctx.zld.locals.items[out.where_index], | |
| 949 | .import => ctx.zld.imports.items[out.where_index], | |
| 950 | }; | |
| 951 | addend = @intCast(i64, source_sym.n_value + out.offset + 4) + addend - @intCast(i64, target_sym.n_value); | |
| 952 | } | |
| 953 | ||
| 954 | out.payload = .{ | |
| 955 | .signed = .{ | |
| 956 | .correction = correction, | |
| 957 | .addend = addend, | |
| 958 | }, | |
| 959 | }; | |
| 960 | } | |
| 961 | ||
| 962 | fn parseLoad(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void { | |
| 963 | assert(rel.r_pcrel == 1); | |
| 964 | assert(rel.r_length == 2); | |
| 965 | ||
| 966 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 967 | const addend: i32 = if (rel_type == .X86_64_RELOC_GOT) | |
| 968 | mem.readIntLittle(i32, self.code[out.offset..][0..4]) | |
| 969 | else | |
| 970 | 0; | |
| 971 | ||
| 972 | out.payload = .{ | |
| 973 | .load = .{ | |
| 974 | .kind = switch (rel_type) { | |
| 975 | .X86_64_RELOC_GOT_LOAD, .X86_64_RELOC_GOT => .got, | |
| 976 | .X86_64_RELOC_TLV => .tlvp, | |
| 977 | else => unreachable, | |
| 978 | }, | |
| 979 | .addend = addend, | |
| 980 | }, | |
| 981 | }; | |
| 982 | } | |
| 983 | ||
| 142 | 984 | pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { |
| 143 | 985 | for (self.relocs.items) |rel| { |
| 144 | 986 | log.debug("relocating {}", .{rel}); |
| ... | ... | @@ -148,7 +990,15 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { |
| 148 | 990 | break :blk sym.n_value + rel.offset; |
| 149 | 991 | }; |
| 150 | 992 | const target_addr = blk: { |
| 151 | if (isGotIndirection(rel, zld.target.?.cpu.arch)) { | |
| 993 | const is_via_got = switch (rel.payload) { | |
| 994 | .pointer_to_got => true, | |
| 995 | .page => |page| page.kind == .got, | |
| 996 | .page_off => |page_off| page_off.kind == .got, | |
| 997 | .load => |load| load.kind == .got, | |
| 998 | else => false, | |
| 999 | }; | |
| 1000 | ||
| 1001 | if (is_via_got) { | |
| 152 | 1002 | const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment; |
| 153 | 1003 | const got = dc_seg.sections.items[zld.got_section_index.?]; |
| 154 | 1004 | const got_index = rel.target.got_index orelse { |
| ... | ... | @@ -270,21 +1120,40 @@ pub fn print(self: *const TextBlock, zld: *Zld) void { |
| 270 | 1120 | self.print_this(zld); |
| 271 | 1121 | } |
| 272 | 1122 | |
| 273 | fn isGotIndirection(rel: macho.relocation_info, arch: Arch) bool { | |
| 274 | return switch (arch) { | |
| 275 | .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { | |
| 276 | .ARM64_RELOC_POINTER_TO_GOT, | |
| 277 | .ARM64_RELOC_GOT_LOAD_PAGE21, | |
| 278 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, | |
| 279 | => true, | |
| 280 | else => false, | |
| 281 | }, | |
| 282 | .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { | |
| 283 | .X86_64_RELOC_GOT, | |
| 284 | .X86_64_RELOC_GOT_LOAD, | |
| 285 | => true, | |
| 286 | else => false, | |
| 287 | }, | |
| 288 | else => unreachable, | |
| 1123 | const RelocIterator = struct { | |
| 1124 | buffer: []const macho.relocation_info, | |
| 1125 | index: i32 = -1, | |
| 1126 | ||
| 1127 | pub fn next(self: *RelocIterator) ?macho.relocation_info { | |
| 1128 | self.index += 1; | |
| 1129 | if (self.index < self.buffer.len) { | |
| 1130 | return self.buffer[@intCast(u32, self.index)]; | |
| 1131 | } | |
| 1132 | return null; | |
| 1133 | } | |
| 1134 | ||
| 1135 | pub fn peek(self: RelocIterator) macho.relocation_info { | |
| 1136 | assert(self.index + 1 < self.buffer.len); | |
| 1137 | return self.buffer[@intCast(u32, self.index + 1)]; | |
| 1138 | } | |
| 1139 | }; | |
| 1140 | ||
| 1141 | fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64) []macho.relocation_info { | |
| 1142 | const Predicate = struct { | |
| 1143 | addr: u64, | |
| 1144 | ||
| 1145 | pub fn predicate(self: @This(), rel: macho.relocation_info) bool { | |
| 1146 | return rel.r_address < self.addr; | |
| 1147 | } | |
| 289 | 1148 | }; |
| 1149 | ||
| 1150 | const start = Zld.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr }); | |
| 1151 | const end = Zld.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr }); | |
| 1152 | ||
| 1153 | return relocs[start..end]; | |
| 1154 | } | |
| 1155 | ||
| 1156 | inline fn isArithmeticOp(inst: *const [4]u8) bool { | |
| 1157 | const group_decode = @truncate(u5, inst[3]); | |
| 1158 | return ((group_decode >> 2) == 4); | |
| 290 | 1159 | } |
src/link/MachO/Zld.zig+36-14| ... | ... | @@ -108,8 +108,8 @@ symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{}, |
| 108 | 108 | |
| 109 | 109 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 110 | 110 | |
| 111 | // stubs: std.ArrayListUnmanaged(*Symbol) = .{}, | |
| 112 | got_entries: std.ArrayListUnmanaged(GotEntry) = .{}, | |
| 111 | stubs: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, | |
| 112 | got_entries: std.AutoArrayHashMapUnmanaged(GotIndirectionKey, void) = .{}, | |
| 113 | 113 | |
| 114 | 114 | stub_helper_stubs_start_off: ?u64 = null, |
| 115 | 115 | |
| ... | ... | @@ -131,20 +131,12 @@ const SymbolWithLoc = struct { |
| 131 | 131 | file: u16 = 0, |
| 132 | 132 | }; |
| 133 | 133 | |
| 134 | pub const GotEntry = struct { | |
| 135 | /// GOT entry can either be a local pointer or an extern (nonlazy) import. | |
| 136 | kind: enum { | |
| 134 | pub const GotIndirectionKey = struct { | |
| 135 | where: enum { | |
| 137 | 136 | local, |
| 138 | 137 | import, |
| 139 | 138 | }, |
| 140 | ||
| 141 | /// Id to the macho.nlist_64 from the respective table: either locals or nonlazy imports. | |
| 142 | /// TODO I'm more and more inclined to just manage a single, max two symbol tables | |
| 143 | /// rather than 4 as we currently do, but I'll follow up in the future PR. | |
| 144 | local_sym_index: u32, | |
| 145 | ||
| 146 | /// Index of this entry in the GOT. | |
| 147 | got_index: u32, | |
| 139 | where_index: u32, | |
| 148 | 140 | }; |
| 149 | 141 | |
| 150 | 142 | pub const Output = struct { |
| ... | ... | @@ -161,7 +153,7 @@ pub fn init(allocator: *Allocator) !Zld { |
| 161 | 153 | } |
| 162 | 154 | |
| 163 | 155 | pub fn deinit(self: *Zld) void { |
| 164 | // self.stubs.deinit(self.allocator); | |
| 156 | self.stubs.deinit(self.allocator); | |
| 165 | 157 | self.got_entries.deinit(self.allocator); |
| 166 | 158 | |
| 167 | 159 | for (self.load_commands.items) |*lc| { |
| ... | ... | @@ -3043,3 +3035,33 @@ pub fn sectionId(self: Zld, match: MatchingSection) u8 { |
| 3043 | 3035 | } |
| 3044 | 3036 | return section; |
| 3045 | 3037 | } |
| 3038 | ||
| 3039 | pub fn unpackSectionId(self: Zld, section_id: u8) MatchingSection { | |
| 3040 | var match: MatchingSection = undefined; | |
| 3041 | var section: u8 = 0; | |
| 3042 | outer: for (self.load_commands.items) |cmd, cmd_id| { | |
| 3043 | assert(cmd == .Segment); | |
| 3044 | for (cmd.Segment.sections.items) |_, sect_id| { | |
| 3045 | section += 1; | |
| 3046 | if (section_id == section) { | |
| 3047 | match.seg = @intCast(u16, cmd_id); | |
| 3048 | match.sect = @intCast(u16, sect_id); | |
| 3049 | break :outer; | |
| 3050 | } | |
| 3051 | } | |
| 3052 | } | |
| 3053 | return match; | |
| 3054 | } | |
| 3055 | ||
| 3056 | pub fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anytype) usize { | |
| 3057 | if (!@hasDecl(@TypeOf(predicate), "predicate")) | |
| 3058 | @compileError("Predicate is required to define fn predicate(@This(), T) bool"); | |
| 3059 | ||
| 3060 | if (start == haystack.len) return start; | |
| 3061 | ||
| 3062 | var i = start; | |
| 3063 | while (i < haystack.len) : (i += 1) { | |
| 3064 | if (predicate.predicate(haystack[i])) break; | |
| 3065 | } | |
| 3066 | return i; | |
| 3067 | } |
src/link/MachO/reloc.zig deleted-840| ... | ... | @@ -1,840 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const aarch64 = @import("../../codegen/aarch64.zig"); | |
| 3 | const assert = std.debug.assert; | |
| 4 | const commands = @import("commands.zig"); | |
| 5 | const log = std.log.scoped(.reloc); | |
| 6 | const macho = std.macho; | |
| 7 | const math = std.math; | |
| 8 | const mem = std.mem; | |
| 9 | const meta = std.meta; | |
| 10 | ||
| 11 | const Allocator = mem.Allocator; | |
| 12 | const Arch = std.Target.Cpu.Arch; | |
| 13 | const Object = @import("Object.zig"); | |
| 14 | const Symbol = @import("Symbol.zig"); | |
| 15 | const TextBlock = @import("TextBlock.zig"); | |
| 16 | const Zld = @import("Zld.zig"); | |
| 17 | ||
| 18 | pub const Relocation = struct { | |
| 19 | /// Offset within the `block`s code buffer. | |
| 20 | /// Note relocation size can be inferred by relocation's kind. | |
| 21 | offset: u32, | |
| 22 | ||
| 23 | /// Target symbol: either a regular or a proxy. | |
| 24 | target: *Symbol, | |
| 25 | ||
| 26 | payload: union(enum) { | |
| 27 | unsigned: Unsigned, | |
| 28 | branch: Branch, | |
| 29 | page: Page, | |
| 30 | page_off: PageOff, | |
| 31 | pointer_to_got: PointerToGot, | |
| 32 | signed: Signed, | |
| 33 | load: Load, | |
| 34 | }, | |
| 35 | ||
| 36 | const ResolveArgs = struct { | |
| 37 | block: *TextBlock, | |
| 38 | offset: u32, | |
| 39 | source_addr: u64, | |
| 40 | target_addr: u64, | |
| 41 | }; | |
| 42 | ||
| 43 | pub const Unsigned = struct { | |
| 44 | subtractor: ?*Symbol = null, | |
| 45 | ||
| 46 | /// Addend embedded directly in the relocation slot | |
| 47 | addend: i64, | |
| 48 | ||
| 49 | /// Extracted from r_length: | |
| 50 | /// => 3 implies true | |
| 51 | /// => 2 implies false | |
| 52 | /// => * is unreachable | |
| 53 | is_64bit: bool, | |
| 54 | ||
| 55 | pub fn resolve(self: Unsigned, args: ResolveArgs) !void { | |
| 56 | const result = if (self.subtractor) |subtractor| | |
| 57 | @intCast(i64, args.target_addr) - @intCast(i64, subtractor.payload.regular.address) + self.addend | |
| 58 | else | |
| 59 | @intCast(i64, args.target_addr) + self.addend; | |
| 60 | ||
| 61 | if (self.is_64bit) { | |
| 62 | mem.writeIntLittle(u64, args.block.code[args.offset..][0..8], @bitCast(u64, result)); | |
| 63 | } else { | |
| 64 | mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @truncate(u32, @bitCast(u64, result))); | |
| 65 | } | |
| 66 | } | |
| 67 | ||
| 68 | pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 69 | _ = fmt; | |
| 70 | _ = options; | |
| 71 | try std.fmt.format(writer, "Unsigned {{ ", .{}); | |
| 72 | if (self.subtractor) |sub| { | |
| 73 | try std.fmt.format(writer, ".subtractor = {}, ", .{sub}); | |
| 74 | } | |
| 75 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 76 | const length: usize = if (self.is_64bit) 8 else 4; | |
| 77 | try std.fmt.format(writer, ".length = {}, ", .{length}); | |
| 78 | try std.fmt.format(writer, "}}", .{}); | |
| 79 | } | |
| 80 | }; | |
| 81 | ||
| 82 | pub const Branch = struct { | |
| 83 | arch: Arch, | |
| 84 | ||
| 85 | pub fn resolve(self: Branch, args: ResolveArgs) !void { | |
| 86 | switch (self.arch) { | |
| 87 | .aarch64 => { | |
| 88 | const displacement = try math.cast( | |
| 89 | i28, | |
| 90 | @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr), | |
| 91 | ); | |
| 92 | const code = args.block.code[args.offset..][0..4]; | |
| 93 | var inst = aarch64.Instruction{ | |
| 94 | .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload( | |
| 95 | aarch64.Instruction, | |
| 96 | aarch64.Instruction.unconditional_branch_immediate, | |
| 97 | ), code), | |
| 98 | }; | |
| 99 | inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); | |
| 100 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 101 | }, | |
| 102 | .x86_64 => { | |
| 103 | const displacement = try math.cast( | |
| 104 | i32, | |
| 105 | @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4, | |
| 106 | ); | |
| 107 | mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement)); | |
| 108 | }, | |
| 109 | else => return error.UnsupportedCpuArchitecture, | |
| 110 | } | |
| 111 | } | |
| 112 | ||
| 113 | pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 114 | _ = self; | |
| 115 | _ = fmt; | |
| 116 | _ = options; | |
| 117 | try std.fmt.format(writer, "Branch {{}}", .{}); | |
| 118 | } | |
| 119 | }; | |
| 120 | ||
| 121 | pub const Page = struct { | |
| 122 | kind: enum { | |
| 123 | page, | |
| 124 | got, | |
| 125 | tlvp, | |
| 126 | }, | |
| 127 | addend: ?u32 = null, | |
| 128 | ||
| 129 | pub fn resolve(self: Page, args: ResolveArgs) !void { | |
| 130 | const target_addr = if (self.addend) |addend| args.target_addr + addend else args.target_addr; | |
| 131 | const source_page = @intCast(i32, args.source_addr >> 12); | |
| 132 | const target_page = @intCast(i32, target_addr >> 12); | |
| 133 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 134 | ||
| 135 | const code = args.block.code[args.offset..][0..4]; | |
| 136 | var inst = aarch64.Instruction{ | |
| 137 | .pc_relative_address = mem.bytesToValue(meta.TagPayload( | |
| 138 | aarch64.Instruction, | |
| 139 | aarch64.Instruction.pc_relative_address, | |
| 140 | ), code), | |
| 141 | }; | |
| 142 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 143 | inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 144 | ||
| 145 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 146 | } | |
| 147 | ||
| 148 | pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 149 | _ = fmt; | |
| 150 | _ = options; | |
| 151 | try std.fmt.format(writer, "Page {{ ", .{}); | |
| 152 | switch (self.kind) { | |
| 153 | .page => {}, | |
| 154 | .got => { | |
| 155 | try std.fmt.format(writer, ".got, ", .{}); | |
| 156 | }, | |
| 157 | .tlvp => { | |
| 158 | try std.fmt.format(writer, ".tlvp", .{}); | |
| 159 | }, | |
| 160 | } | |
| 161 | if (self.addend) |add| { | |
| 162 | try std.fmt.format(writer, ".addend = {}, ", .{add}); | |
| 163 | } | |
| 164 | try std.fmt.format(writer, "}}", .{}); | |
| 165 | } | |
| 166 | }; | |
| 167 | ||
| 168 | pub const PageOff = struct { | |
| 169 | kind: enum { | |
| 170 | page, | |
| 171 | got, | |
| 172 | tlvp, | |
| 173 | }, | |
| 174 | addend: ?u32 = null, | |
| 175 | op_kind: ?OpKind = null, | |
| 176 | ||
| 177 | pub const OpKind = enum { | |
| 178 | arithmetic, | |
| 179 | load, | |
| 180 | }; | |
| 181 | ||
| 182 | pub fn resolve(self: PageOff, args: ResolveArgs) !void { | |
| 183 | const code = args.block.code[args.offset..][0..4]; | |
| 184 | ||
| 185 | switch (self.kind) { | |
| 186 | .page => { | |
| 187 | const target_addr = if (self.addend) |addend| args.target_addr + addend else args.target_addr; | |
| 188 | const narrowed = @truncate(u12, target_addr); | |
| 189 | ||
| 190 | const op_kind = self.op_kind orelse unreachable; | |
| 191 | var inst: aarch64.Instruction = blk: { | |
| 192 | switch (op_kind) { | |
| 193 | .arithmetic => { | |
| 194 | break :blk .{ | |
| 195 | .add_subtract_immediate = mem.bytesToValue(meta.TagPayload( | |
| 196 | aarch64.Instruction, | |
| 197 | aarch64.Instruction.add_subtract_immediate, | |
| 198 | ), code), | |
| 199 | }; | |
| 200 | }, | |
| 201 | .load => { | |
| 202 | break :blk .{ | |
| 203 | .load_store_register = mem.bytesToValue(meta.TagPayload( | |
| 204 | aarch64.Instruction, | |
| 205 | aarch64.Instruction.load_store_register, | |
| 206 | ), code), | |
| 207 | }; | |
| 208 | }, | |
| 209 | } | |
| 210 | }; | |
| 211 | ||
| 212 | if (op_kind == .arithmetic) { | |
| 213 | inst.add_subtract_immediate.imm12 = narrowed; | |
| 214 | } else { | |
| 215 | const offset: u12 = blk: { | |
| 216 | if (inst.load_store_register.size == 0) { | |
| 217 | if (inst.load_store_register.v == 1) { | |
| 218 | // 128-bit SIMD is scaled by 16. | |
| 219 | break :blk try math.divExact(u12, narrowed, 16); | |
| 220 | } | |
| 221 | // Otherwise, 8-bit SIMD or ldrb. | |
| 222 | break :blk narrowed; | |
| 223 | } else { | |
| 224 | const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size); | |
| 225 | break :blk try math.divExact(u12, narrowed, denom); | |
| 226 | } | |
| 227 | }; | |
| 228 | inst.load_store_register.offset = offset; | |
| 229 | } | |
| 230 | ||
| 231 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 232 | }, | |
| 233 | .got => { | |
| 234 | const narrowed = @truncate(u12, args.target_addr); | |
| 235 | var inst: aarch64.Instruction = .{ | |
| 236 | .load_store_register = mem.bytesToValue(meta.TagPayload( | |
| 237 | aarch64.Instruction, | |
| 238 | aarch64.Instruction.load_store_register, | |
| 239 | ), code), | |
| 240 | }; | |
| 241 | const offset = try math.divExact(u12, narrowed, 8); | |
| 242 | inst.load_store_register.offset = offset; | |
| 243 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 244 | }, | |
| 245 | .tlvp => { | |
| 246 | const RegInfo = struct { | |
| 247 | rd: u5, | |
| 248 | rn: u5, | |
| 249 | size: u1, | |
| 250 | }; | |
| 251 | const reg_info: RegInfo = blk: { | |
| 252 | if (isArithmeticOp(code)) { | |
| 253 | const inst = mem.bytesToValue(meta.TagPayload( | |
| 254 | aarch64.Instruction, | |
| 255 | aarch64.Instruction.add_subtract_immediate, | |
| 256 | ), code); | |
| 257 | break :blk .{ | |
| 258 | .rd = inst.rd, | |
| 259 | .rn = inst.rn, | |
| 260 | .size = inst.sf, | |
| 261 | }; | |
| 262 | } else { | |
| 263 | const inst = mem.bytesToValue(meta.TagPayload( | |
| 264 | aarch64.Instruction, | |
| 265 | aarch64.Instruction.load_store_register, | |
| 266 | ), code); | |
| 267 | break :blk .{ | |
| 268 | .rd = inst.rt, | |
| 269 | .rn = inst.rn, | |
| 270 | .size = @truncate(u1, inst.size), | |
| 271 | }; | |
| 272 | } | |
| 273 | }; | |
| 274 | const narrowed = @truncate(u12, args.target_addr); | |
| 275 | var inst = aarch64.Instruction{ | |
| 276 | .add_subtract_immediate = .{ | |
| 277 | .rd = reg_info.rd, | |
| 278 | .rn = reg_info.rn, | |
| 279 | .imm12 = narrowed, | |
| 280 | .sh = 0, | |
| 281 | .s = 0, | |
| 282 | .op = 0, | |
| 283 | .sf = reg_info.size, | |
| 284 | }, | |
| 285 | }; | |
| 286 | mem.writeIntLittle(u32, code, inst.toU32()); | |
| 287 | }, | |
| 288 | } | |
| 289 | } | |
| 290 | ||
| 291 | pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 292 | _ = fmt; | |
| 293 | _ = options; | |
| 294 | try std.fmt.format(writer, "PageOff {{ ", .{}); | |
| 295 | switch (self.kind) { | |
| 296 | .page => {}, | |
| 297 | .got => { | |
| 298 | try std.fmt.format(writer, ".got, ", .{}); | |
| 299 | }, | |
| 300 | .tlvp => { | |
| 301 | try std.fmt.format(writer, ".tlvp, ", .{}); | |
| 302 | }, | |
| 303 | } | |
| 304 | if (self.addend) |add| { | |
| 305 | try std.fmt.format(writer, ".addend = {}, ", .{add}); | |
| 306 | } | |
| 307 | if (self.op_kind) |op| { | |
| 308 | try std.fmt.format(writer, ".op_kind = {s}, ", .{op}); | |
| 309 | } | |
| 310 | try std.fmt.format(writer, "}}", .{}); | |
| 311 | } | |
| 312 | }; | |
| 313 | ||
| 314 | pub const PointerToGot = struct { | |
| 315 | pub fn resolve(_: PointerToGot, args: ResolveArgs) !void { | |
| 316 | const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr)); | |
| 317 | mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, result)); | |
| 318 | } | |
| 319 | ||
| 320 | pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 321 | _ = self; | |
| 322 | _ = fmt; | |
| 323 | _ = options; | |
| 324 | try std.fmt.format(writer, "PointerToGot {{}}", .{}); | |
| 325 | } | |
| 326 | }; | |
| 327 | ||
| 328 | pub const Signed = struct { | |
| 329 | addend: i64, | |
| 330 | correction: i4, | |
| 331 | ||
| 332 | pub fn resolve(self: Signed, args: ResolveArgs) !void { | |
| 333 | const target_addr = @intCast(i64, args.target_addr) + self.addend; | |
| 334 | const displacement = try math.cast( | |
| 335 | i32, | |
| 336 | target_addr - @intCast(i64, args.source_addr) - self.correction - 4, | |
| 337 | ); | |
| 338 | mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement)); | |
| 339 | } | |
| 340 | ||
| 341 | pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 342 | _ = fmt; | |
| 343 | _ = options; | |
| 344 | try std.fmt.format(writer, "Signed {{ ", .{}); | |
| 345 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 346 | try std.fmt.format(writer, ".correction = {}, ", .{self.correction}); | |
| 347 | try std.fmt.format(writer, "}}", .{}); | |
| 348 | } | |
| 349 | }; | |
| 350 | ||
| 351 | pub const Load = struct { | |
| 352 | kind: enum { | |
| 353 | got, | |
| 354 | tlvp, | |
| 355 | }, | |
| 356 | addend: ?i32 = null, | |
| 357 | ||
| 358 | pub fn resolve(self: Load, args: ResolveArgs) !void { | |
| 359 | if (self.kind == .tlvp) { | |
| 360 | // We need to rewrite the opcode from movq to leaq. | |
| 361 | args.block.code[args.offset - 2] = 0x8d; | |
| 362 | } | |
| 363 | const addend = if (self.addend) |addend| addend else 0; | |
| 364 | const displacement = try math.cast( | |
| 365 | i32, | |
| 366 | @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + addend, | |
| 367 | ); | |
| 368 | mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement)); | |
| 369 | } | |
| 370 | ||
| 371 | pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 372 | _ = fmt; | |
| 373 | _ = options; | |
| 374 | try std.fmt.format(writer, "Load {{ ", .{}); | |
| 375 | try std.fmt.format(writer, "{s}, ", .{self.kind}); | |
| 376 | if (self.addend) |addend| { | |
| 377 | try std.fmt.format(writer, ".addend = {}, ", .{addend}); | |
| 378 | } | |
| 379 | try std.fmt.format(writer, "}}", .{}); | |
| 380 | } | |
| 381 | }; | |
| 382 | ||
| 383 | pub fn resolve(self: Relocation, block: *TextBlock, source_addr: u64, target_addr: u64) !void { | |
| 384 | const args = ResolveArgs{ | |
| 385 | .block = block, | |
| 386 | .offset = self.offset, | |
| 387 | .source_addr = source_addr, | |
| 388 | .target_addr = target_addr, | |
| 389 | }; | |
| 390 | switch (self.payload) { | |
| 391 | .unsigned => |unsigned| try unsigned.resolve(args), | |
| 392 | .branch => |branch| try branch.resolve(args), | |
| 393 | .page => |page| try page.resolve(args), | |
| 394 | .page_off => |page_off| try page_off.resolve(args), | |
| 395 | .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(args), | |
| 396 | .signed => |signed| try signed.resolve(args), | |
| 397 | .load => |load| try load.resolve(args), | |
| 398 | } | |
| 399 | } | |
| 400 | ||
| 401 | pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 402 | try std.fmt.format(writer, "Relocation {{ ", .{}); | |
| 403 | try std.fmt.format(writer, ".offset = {}, ", .{self.offset}); | |
| 404 | try std.fmt.format(writer, ".target = {}, ", .{self.target}); | |
| 405 | ||
| 406 | switch (self.payload) { | |
| 407 | .unsigned => |unsigned| try unsigned.format(fmt, options, writer), | |
| 408 | .branch => |branch| try branch.format(fmt, options, writer), | |
| 409 | .page => |page| try page.format(fmt, options, writer), | |
| 410 | .page_off => |page_off| try page_off.format(fmt, options, writer), | |
| 411 | .pointer_to_got => |pointer_to_got| try pointer_to_got.format(fmt, options, writer), | |
| 412 | .signed => |signed| try signed.format(fmt, options, writer), | |
| 413 | .load => |load| try load.format(fmt, options, writer), | |
| 414 | } | |
| 415 | ||
| 416 | try std.fmt.format(writer, "}}", .{}); | |
| 417 | } | |
| 418 | }; | |
| 419 | ||
| 420 | pub const RelocIterator = struct { | |
| 421 | buffer: []const macho.relocation_info, | |
| 422 | index: i32 = -1, | |
| 423 | ||
| 424 | pub fn next(self: *RelocIterator) ?macho.relocation_info { | |
| 425 | self.index += 1; | |
| 426 | if (self.index < self.buffer.len) { | |
| 427 | return self.buffer[@intCast(u32, self.index)]; | |
| 428 | } | |
| 429 | return null; | |
| 430 | } | |
| 431 | ||
| 432 | pub fn peek(self: RelocIterator) macho.relocation_info { | |
| 433 | assert(self.index + 1 < self.buffer.len); | |
| 434 | return self.buffer[@intCast(u32, self.index + 1)]; | |
| 435 | } | |
| 436 | }; | |
| 437 | ||
| 438 | pub const Parser = struct { | |
| 439 | object: *Object, | |
| 440 | zld: *Zld, | |
| 441 | it: *RelocIterator, | |
| 442 | block: *TextBlock, | |
| 443 | ||
| 444 | /// Base address of the parsed text block in the source section. | |
| 445 | base_addr: u64, | |
| 446 | ||
| 447 | /// Used only when targeting aarch64 | |
| 448 | addend: ?u32 = null, | |
| 449 | ||
| 450 | /// Parsed subtractor symbol from _RELOC_SUBTRACTOR reloc type. | |
| 451 | subtractor: ?*Symbol = null, | |
| 452 | ||
| 453 | pub fn parse(self: *Parser) !void { | |
| 454 | while (self.it.next()) |rel| { | |
| 455 | const out_rel = blk: { | |
| 456 | switch (self.object.arch.?) { | |
| 457 | .aarch64 => { | |
| 458 | const out_rel = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { | |
| 459 | .ARM64_RELOC_BRANCH26 => try self.parseBranch(rel), | |
| 460 | .ARM64_RELOC_SUBTRACTOR => { | |
| 461 | // Subtractor is not a relocation with effect on the TextBlock, so | |
| 462 | // parse it and carry on. | |
| 463 | try self.parseSubtractor(rel); | |
| 464 | ||
| 465 | // Verify SUBTRACTOR is followed by UNSIGNED. | |
| 466 | const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type); | |
| 467 | if (next != .ARM64_RELOC_UNSIGNED) { | |
| 468 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | |
| 469 | return error.UnexpectedRelocationType; | |
| 470 | } | |
| 471 | continue; | |
| 472 | }, | |
| 473 | .ARM64_RELOC_UNSIGNED => try self.parseUnsigned(rel), | |
| 474 | .ARM64_RELOC_ADDEND => { | |
| 475 | // Addend is not a relocation with effect on the TextBlock, so | |
| 476 | // parse it and carry on. | |
| 477 | try self.parseAddend(rel); | |
| 478 | ||
| 479 | // Verify ADDEND is followed by a load. | |
| 480 | const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type); | |
| 481 | switch (next) { | |
| 482 | .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {}, | |
| 483 | else => { | |
| 484 | log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next}); | |
| 485 | return error.UnexpectedRelocationType; | |
| 486 | }, | |
| 487 | } | |
| 488 | continue; | |
| 489 | }, | |
| 490 | .ARM64_RELOC_PAGE21, | |
| 491 | .ARM64_RELOC_GOT_LOAD_PAGE21, | |
| 492 | .ARM64_RELOC_TLVP_LOAD_PAGE21, | |
| 493 | => try self.parsePage(rel), | |
| 494 | .ARM64_RELOC_PAGEOFF12, | |
| 495 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, | |
| 496 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12, | |
| 497 | => try self.parsePageOff(rel), | |
| 498 | .ARM64_RELOC_POINTER_TO_GOT => try self.parsePointerToGot(rel), | |
| 499 | }; | |
| 500 | break :blk out_rel; | |
| 501 | }, | |
| 502 | .x86_64 => { | |
| 503 | const out_rel = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { | |
| 504 | .X86_64_RELOC_BRANCH => try self.parseBranch(rel), | |
| 505 | .X86_64_RELOC_SUBTRACTOR => { | |
| 506 | // Subtractor is not a relocation with effect on the TextBlock, so | |
| 507 | // parse it and carry on. | |
| 508 | try self.parseSubtractor(rel); | |
| 509 | ||
| 510 | // Verify SUBTRACTOR is followed by UNSIGNED. | |
| 511 | const next = @intToEnum(macho.reloc_type_x86_64, self.it.peek().r_type); | |
| 512 | if (next != .X86_64_RELOC_UNSIGNED) { | |
| 513 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); | |
| 514 | return error.UnexpectedRelocationType; | |
| 515 | } | |
| 516 | continue; | |
| 517 | }, | |
| 518 | .X86_64_RELOC_UNSIGNED => try self.parseUnsigned(rel), | |
| 519 | .X86_64_RELOC_SIGNED, | |
| 520 | .X86_64_RELOC_SIGNED_1, | |
| 521 | .X86_64_RELOC_SIGNED_2, | |
| 522 | .X86_64_RELOC_SIGNED_4, | |
| 523 | => try self.parseSigned(rel), | |
| 524 | .X86_64_RELOC_GOT_LOAD, | |
| 525 | .X86_64_RELOC_GOT, | |
| 526 | .X86_64_RELOC_TLV, | |
| 527 | => try self.parseLoad(rel), | |
| 528 | }; | |
| 529 | break :blk out_rel; | |
| 530 | }, | |
| 531 | else => unreachable, | |
| 532 | } | |
| 533 | }; | |
| 534 | try self.block.relocs.append(out_rel); | |
| 535 | ||
| 536 | if (out_rel.target.payload == .regular) { | |
| 537 | try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {}); | |
| 538 | } | |
| 539 | ||
| 540 | const is_via_got = switch (out_rel.payload) { | |
| 541 | .pointer_to_got => true, | |
| 542 | .load => |load| load.kind == .got, | |
| 543 | .page => |page| page.kind == .got, | |
| 544 | .page_off => |page_off| page_off.kind == .got, | |
| 545 | else => false, | |
| 546 | }; | |
| 547 | ||
| 548 | if (is_via_got and out_rel.target.got_index == null) { | |
| 549 | const index = @intCast(u32, self.zld.got_entries.items.len); | |
| 550 | out_rel.target.got_index = index; | |
| 551 | try self.zld.got_entries.append(self.zld.allocator, out_rel.target); | |
| 552 | ||
| 553 | log.debug("adding GOT entry for symbol {s} at index {}", .{ | |
| 554 | self.zld.getString(out_rel.target.strx), | |
| 555 | index, | |
| 556 | }); | |
| 557 | } else if (out_rel.payload == .unsigned) { | |
| 558 | const sym = out_rel.target; | |
| 559 | switch (sym.payload) { | |
| 560 | .proxy => |proxy| { | |
| 561 | try self.block.bindings.append(.{ | |
| 562 | .local_sym_index = proxy.local_sym_index, | |
| 563 | .offset = out_rel.offset, | |
| 564 | }); | |
| 565 | }, | |
| 566 | else => { | |
| 567 | const source_sym = self.zld.locals.items[self.block.local_sym_index]; | |
| 568 | const source_reg = &source_sym.payload.regular; | |
| 569 | const seg = self.zld.load_commands.items[source_reg.segment_id].Segment; | |
| 570 | const sect = seg.sections.items[source_reg.section_id]; | |
| 571 | const sect_type = commands.sectionType(sect); | |
| 572 | ||
| 573 | const should_rebase = rebase: { | |
| 574 | if (!out_rel.payload.unsigned.is_64bit) break :rebase false; | |
| 575 | ||
| 576 | // TODO actually, a check similar to what dyld is doing, that is, verifying | |
| 577 | // that the segment is writable should be enough here. | |
| 578 | const is_right_segment = blk: { | |
| 579 | if (self.zld.data_segment_cmd_index) |idx| { | |
| 580 | if (source_reg.segment_id == idx) { | |
| 581 | break :blk true; | |
| 582 | } | |
| 583 | } | |
| 584 | if (self.zld.data_const_segment_cmd_index) |idx| { | |
| 585 | if (source_reg.segment_id == idx) { | |
| 586 | break :blk true; | |
| 587 | } | |
| 588 | } | |
| 589 | break :blk false; | |
| 590 | }; | |
| 591 | ||
| 592 | if (!is_right_segment) break :rebase false; | |
| 593 | if (sect_type != macho.S_LITERAL_POINTERS and | |
| 594 | sect_type != macho.S_REGULAR and | |
| 595 | sect_type != macho.S_MOD_INIT_FUNC_POINTERS and | |
| 596 | sect_type != macho.S_MOD_TERM_FUNC_POINTERS) | |
| 597 | { | |
| 598 | break :rebase false; | |
| 599 | } | |
| 600 | ||
| 601 | break :rebase true; | |
| 602 | }; | |
| 603 | ||
| 604 | if (should_rebase) { | |
| 605 | try self.block.rebases.append(out_rel.offset); | |
| 606 | } | |
| 607 | }, | |
| 608 | } | |
| 609 | } else if (out_rel.payload == .branch) blk: { | |
| 610 | const sym = out_rel.target; | |
| 611 | ||
| 612 | if (sym.stubs_index != null) break :blk; | |
| 613 | if (sym.payload != .proxy) break :blk; | |
| 614 | ||
| 615 | const index = @intCast(u32, self.zld.stubs.items.len); | |
| 616 | sym.stubs_index = index; | |
| 617 | try self.zld.stubs.append(self.zld.allocator, sym); | |
| 618 | ||
| 619 | log.debug("adding stub entry for symbol {s} at index {}", .{ self.zld.getString(sym.strx), index }); | |
| 620 | } | |
| 621 | } | |
| 622 | } | |
| 623 | ||
| 624 | fn parseBaseRelInfo(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 625 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 626 | const target = try self.object.symbolFromReloc(self.zld, rel); | |
| 627 | return Relocation{ | |
| 628 | .offset = offset, | |
| 629 | .target = target, | |
| 630 | .payload = undefined, | |
| 631 | }; | |
| 632 | } | |
| 633 | ||
| 634 | fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 635 | defer { | |
| 636 | // Reset parser's subtractor state | |
| 637 | self.subtractor = null; | |
| 638 | } | |
| 639 | ||
| 640 | assert(rel.r_pcrel == 0); | |
| 641 | ||
| 642 | var parsed = try self.parseBaseRelInfo(rel); | |
| 643 | const is_64bit: bool = switch (rel.r_length) { | |
| 644 | 3 => true, | |
| 645 | 2 => false, | |
| 646 | else => unreachable, | |
| 647 | }; | |
| 648 | ||
| 649 | var addend: i64 = if (is_64bit) | |
| 650 | mem.readIntLittle(i64, self.block.code[parsed.offset..][0..8]) | |
| 651 | else | |
| 652 | mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]); | |
| 653 | ||
| 654 | if (rel.r_extern == 0) { | |
| 655 | addend -= @intCast(i64, parsed.target.payload.regular.address); | |
| 656 | } | |
| 657 | ||
| 658 | parsed.payload = .{ | |
| 659 | .unsigned = .{ | |
| 660 | .subtractor = self.subtractor, | |
| 661 | .is_64bit = is_64bit, | |
| 662 | .addend = addend, | |
| 663 | }, | |
| 664 | }; | |
| 665 | ||
| 666 | return parsed; | |
| 667 | } | |
| 668 | ||
| 669 | fn parseBranch(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 670 | assert(rel.r_pcrel == 1); | |
| 671 | assert(rel.r_length == 2); | |
| 672 | ||
| 673 | var parsed = try self.parseBaseRelInfo(rel); | |
| 674 | parsed.payload = .{ | |
| 675 | .branch = .{ | |
| 676 | .arch = self.object.arch.?, | |
| 677 | }, | |
| 678 | }; | |
| 679 | return parsed; | |
| 680 | } | |
| 681 | ||
| 682 | fn parsePage(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 683 | assert(rel.r_pcrel == 1); | |
| 684 | assert(rel.r_length == 2); | |
| 685 | ||
| 686 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 687 | ||
| 688 | defer if (rel_type == .ARM64_RELOC_PAGE21) { | |
| 689 | // Reset parser's addend state | |
| 690 | self.addend = null; | |
| 691 | }; | |
| 692 | ||
| 693 | const addend = if (rel_type == .ARM64_RELOC_PAGE21) | |
| 694 | self.addend | |
| 695 | else | |
| 696 | null; | |
| 697 | ||
| 698 | var parsed = try self.parseBaseRelInfo(rel); | |
| 699 | parsed.payload = .{ | |
| 700 | .page = .{ | |
| 701 | .kind = switch (rel_type) { | |
| 702 | .ARM64_RELOC_PAGE21 => .page, | |
| 703 | .ARM64_RELOC_GOT_LOAD_PAGE21 => .got, | |
| 704 | .ARM64_RELOC_TLVP_LOAD_PAGE21 => .tlvp, | |
| 705 | else => unreachable, | |
| 706 | }, | |
| 707 | .addend = addend, | |
| 708 | }, | |
| 709 | }; | |
| 710 | return parsed; | |
| 711 | } | |
| 712 | ||
| 713 | fn parsePageOff(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 714 | assert(rel.r_pcrel == 0); | |
| 715 | assert(rel.r_length == 2); | |
| 716 | ||
| 717 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); | |
| 718 | ||
| 719 | defer if (rel_type == .ARM64_RELOC_PAGEOFF12) { | |
| 720 | // Reset parser's addend state | |
| 721 | self.addend = null; | |
| 722 | }; | |
| 723 | ||
| 724 | const addend = if (rel_type == .ARM64_RELOC_PAGEOFF12) | |
| 725 | self.addend | |
| 726 | else | |
| 727 | null; | |
| 728 | ||
| 729 | var parsed = try self.parseBaseRelInfo(rel); | |
| 730 | const op_kind: ?Relocation.PageOff.OpKind = blk: { | |
| 731 | if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null; | |
| 732 | const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.block.code[parsed.offset..][0..4])) | |
| 733 | .arithmetic | |
| 734 | else | |
| 735 | .load; | |
| 736 | break :blk op_kind; | |
| 737 | }; | |
| 738 | ||
| 739 | parsed.payload = .{ | |
| 740 | .page_off = .{ | |
| 741 | .kind = switch (rel_type) { | |
| 742 | .ARM64_RELOC_PAGEOFF12 => .page, | |
| 743 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got, | |
| 744 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => .tlvp, | |
| 745 | else => unreachable, | |
| 746 | }, | |
| 747 | .addend = addend, | |
| 748 | .op_kind = op_kind, | |
| 749 | }, | |
| 750 | }; | |
| 751 | return parsed; | |
| 752 | } | |
| 753 | ||
| 754 | fn parsePointerToGot(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 755 | assert(rel.r_pcrel == 1); | |
| 756 | assert(rel.r_length == 2); | |
| 757 | ||
| 758 | var parsed = try self.parseBaseRelInfo(rel); | |
| 759 | parsed.payload = .{ | |
| 760 | .pointer_to_got = .{}, | |
| 761 | }; | |
| 762 | return parsed; | |
| 763 | } | |
| 764 | ||
| 765 | fn parseAddend(self: *Parser, rel: macho.relocation_info) !void { | |
| 766 | assert(rel.r_pcrel == 0); | |
| 767 | assert(rel.r_extern == 0); | |
| 768 | assert(self.addend == null); | |
| 769 | ||
| 770 | self.addend = rel.r_symbolnum; | |
| 771 | } | |
| 772 | ||
| 773 | fn parseSigned(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 774 | assert(rel.r_pcrel == 1); | |
| 775 | assert(rel.r_length == 2); | |
| 776 | ||
| 777 | var parsed = try self.parseBaseRelInfo(rel); | |
| 778 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 779 | const correction: i4 = switch (rel_type) { | |
| 780 | .X86_64_RELOC_SIGNED => 0, | |
| 781 | .X86_64_RELOC_SIGNED_1 => 1, | |
| 782 | .X86_64_RELOC_SIGNED_2 => 2, | |
| 783 | .X86_64_RELOC_SIGNED_4 => 4, | |
| 784 | else => unreachable, | |
| 785 | }; | |
| 786 | var addend: i64 = mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]) + correction; | |
| 787 | ||
| 788 | if (rel.r_extern == 0) { | |
| 789 | const source_sym = self.zld.locals.items[self.block.local_sym_index].payload.regular; | |
| 790 | const source_addr = source_sym.address + parsed.offset + 4; | |
| 791 | const target_sym = parsed.target.payload.regular; | |
| 792 | addend = @intCast(i64, source_addr) + addend - @intCast(i64, target_sym.address); | |
| 793 | } | |
| 794 | ||
| 795 | parsed.payload = .{ | |
| 796 | .signed = .{ | |
| 797 | .correction = correction, | |
| 798 | .addend = addend, | |
| 799 | }, | |
| 800 | }; | |
| 801 | ||
| 802 | return parsed; | |
| 803 | } | |
| 804 | ||
| 805 | fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void { | |
| 806 | assert(rel.r_pcrel == 0); | |
| 807 | assert(self.subtractor == null); | |
| 808 | ||
| 809 | self.subtractor = try self.object.symbolFromReloc(self.zld, rel); | |
| 810 | } | |
| 811 | ||
| 812 | fn parseLoad(self: *Parser, rel: macho.relocation_info) !Relocation { | |
| 813 | assert(rel.r_pcrel == 1); | |
| 814 | assert(rel.r_length == 2); | |
| 815 | ||
| 816 | var parsed = try self.parseBaseRelInfo(rel); | |
| 817 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); | |
| 818 | const addend = if (rel_type == .X86_64_RELOC_GOT) | |
| 819 | mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]) | |
| 820 | else | |
| 821 | null; | |
| 822 | ||
| 823 | parsed.payload = .{ | |
| 824 | .load = .{ | |
| 825 | .kind = switch (rel_type) { | |
| 826 | .X86_64_RELOC_GOT_LOAD, .X86_64_RELOC_GOT => .got, | |
| 827 | .X86_64_RELOC_TLV => .tlvp, | |
| 828 | else => unreachable, | |
| 829 | }, | |
| 830 | .addend = addend, | |
| 831 | }, | |
| 832 | }; | |
| 833 | return parsed; | |
| 834 | } | |
| 835 | }; | |
| 836 | ||
| 837 | inline fn isArithmeticOp(inst: *const [4]u8) bool { | |
| 838 | const group_decode = @truncate(u5, inst[3]); | |
| 839 | return ((group_decode >> 2) == 4); | |
| 840 | } |