| 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,7 +586,6 @@ set(ZIG_STAGE2_SOURCES |
| 586 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig" | 586 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig" |
| 587 | "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig" | 587 | "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig" |
| 588 | "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig" | 588 | "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig" |
| 589 | "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc.zig" | ||
| 590 | "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig" | 589 | "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig" |
| 591 | "${CMAKE_SOURCE_DIR}/src/link/tapi.zig" | 590 | "${CMAKE_SOURCE_DIR}/src/link/tapi.zig" |
| 592 | "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig" | 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,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 | const NlistWithIndex = struct { | 293 | const NlistWithIndex = struct { |
| 307 | nlist: macho.nlist_64, | 294 | nlist: macho.nlist_64, |
| 308 | index: u32, | 295 | index: u32, |
| ... | @@ -315,44 +302,29 @@ const NlistWithIndex = struct { | ... | @@ -315,44 +302,29 @@ const NlistWithIndex = struct { |
| 315 | const Predicate = struct { | 302 | const Predicate = struct { |
| 316 | addr: u64, | 303 | addr: u64, |
| 317 | 304 | ||
| 318 | fn predicate(self: @This(), symbol: NlistWithIndex) bool { | 305 | pub fn predicate(self: @This(), symbol: NlistWithIndex) bool { |
| 319 | return symbol.nlist.n_value >= self.addr; | 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 }); | 310 | const start = Zld.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr }); |
| 324 | const end = findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size }); | 311 | const end = Zld.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size }); |
| 325 | 312 | ||
| 326 | return symbols[start..end]; | 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 | fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64) []macho.data_in_code_entry { | 317 | fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64) []macho.data_in_code_entry { |
| 346 | const Predicate = struct { | 318 | const Predicate = struct { |
| 347 | addr: u64, | 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 | return dice.offset >= self.addr; | 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 }); | 326 | const start = Zld.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 }); | 327 | const end = Zld.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr }); |
| 356 | 328 | ||
| 357 | return dices[start..end]; | 329 | return dices[start..end]; |
| 358 | } | 330 | } |
| ... | @@ -483,10 +455,10 @@ const TextBlockParser = struct { | ... | @@ -483,10 +455,10 @@ const TextBlockParser = struct { |
| 483 | } | 455 | } |
| 484 | } | 456 | } |
| 485 | 457 | ||
| 486 | const relocs = filterRelocs(self.relocs, start_addr, end_addr); | 458 | try block.parseRelocsFromObject(relocs, object, .{ |
| 487 | if (relocs.len > 0) { | 459 | .base_addr = start_addr, |
| 488 | try self.object.parseRelocs(self.zld, relocs, block, start_addr); | 460 | .zld = self.zld, |
| 489 | } | 461 | }); |
| 490 | 462 | ||
| 491 | if (self.zld.has_dices) { | 463 | if (self.zld.has_dices) { |
| 492 | const dices = filterDice( | 464 | const dices = filterDice( |
| ... | @@ -745,8 +717,6 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { | ... | @@ -745,8 +717,6 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 745 | .n_desc = 0, | 717 | .n_desc = 0, |
| 746 | .n_value = sect.addr, | 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 | const block = try self.allocator.create(TextBlock); | 721 | const block = try self.allocator.create(TextBlock); |
| 752 | errdefer self.allocator.destroy(block); | 722 | errdefer self.allocator.destroy(block); |
| ... | @@ -757,69 +727,10 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { | ... | @@ -757,69 +727,10 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 757 | block.size = sect.size; | 727 | block.size = sect.size; |
| 758 | block.alignment = sect.@"align"; | 728 | block.alignment = sect.@"align"; |
| 759 | 729 | ||
| 760 | try block.relocs.ensureTotalCapacity(relocs.len); | 730 | try block.parseRelocsFromObject(relocs, self, .{ |
| 761 | for (relocs) |rel| { | 731 | .base_addr = 0, |
| 762 | const out_rel: TextBlock.Relocation = outer: { | 732 | .zld = zld, |
| 763 | if (rel.r_extern == 0) { | 733 | }); |
| 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 | } | ||
| 823 | 734 | ||
| 824 | if (zld.has_dices) { | 735 | if (zld.has_dices) { |
| 825 | const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size); | 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,6 +1,7 @@ |
| 1 | const TextBlock = @This(); | 1 | const TextBlock = @This(); |
| 2 | 2 | ||
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const assert = std.debug.assert; | ||
| 4 | const commands = @import("commands.zig"); | 5 | const commands = @import("commands.zig"); |
| 5 | const log = std.log.scoped(.text_block); | 6 | const log = std.log.scoped(.text_block); |
| 6 | const macho = std.macho; | 7 | const macho = std.macho; |
| ... | @@ -8,6 +9,7 @@ const mem = std.mem; | ... | @@ -8,6 +9,7 @@ const mem = std.mem; |
| 8 | 9 | ||
| 9 | const Allocator = mem.Allocator; | 10 | const Allocator = mem.Allocator; |
| 10 | const Arch = std.Target.Cpu.Arch; | 11 | const Arch = std.Target.Cpu.Arch; |
| 12 | const Object = @import("Object.zig"); | ||
| 11 | const Zld = @import("Zld.zig"); | 13 | const Zld = @import("Zld.zig"); |
| 12 | 14 | ||
| 13 | allocator: *Allocator, | 15 | allocator: *Allocator, |
| ... | @@ -102,12 +104,396 @@ pub const Stab = union(enum) { | ... | @@ -102,12 +104,396 @@ pub const Stab = union(enum) { |
| 102 | }; | 104 | }; |
| 103 | 105 | ||
| 104 | pub const Relocation = struct { | 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 | where: enum { | 111 | where: enum { |
| 107 | local, | 112 | local, |
| 108 | import, | 113 | import, |
| 109 | }, | 114 | }, |
| 115 | |||
| 110 | where_index: u32, | 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 | pub fn init(allocator: *Allocator) TextBlock { | 499 | pub fn init(allocator: *Allocator) TextBlock { |
| ... | @@ -139,6 +525,462 @@ pub fn deinit(self: *TextBlock) void { | ... | @@ -139,6 +525,462 @@ pub fn deinit(self: *TextBlock) void { |
| 139 | self.dices.deinit(); | 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 | pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { | 984 | pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { |
| 143 | for (self.relocs.items) |rel| { | 985 | for (self.relocs.items) |rel| { |
| 144 | log.debug("relocating {}", .{rel}); | 986 | log.debug("relocating {}", .{rel}); |
| ... | @@ -148,7 +990,15 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { | ... | @@ -148,7 +990,15 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { |
| 148 | break :blk sym.n_value + rel.offset; | 990 | break :blk sym.n_value + rel.offset; |
| 149 | }; | 991 | }; |
| 150 | const target_addr = blk: { | 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 | const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment; | 1002 | const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment; |
| 153 | const got = dc_seg.sections.items[zld.got_section_index.?]; | 1003 | const got = dc_seg.sections.items[zld.got_section_index.?]; |
| 154 | const got_index = rel.target.got_index orelse { | 1004 | const got_index = rel.target.got_index orelse { |
| ... | @@ -270,21 +1120,40 @@ pub fn print(self: *const TextBlock, zld: *Zld) void { | ... | @@ -270,21 +1120,40 @@ pub fn print(self: *const TextBlock, zld: *Zld) void { |
| 270 | self.print_this(zld); | 1120 | self.print_this(zld); |
| 271 | } | 1121 | } |
| 272 | 1122 | ||
| 273 | fn isGotIndirection(rel: macho.relocation_info, arch: Arch) bool { | 1123 | const RelocIterator = struct { |
| 274 | return switch (arch) { | 1124 | buffer: []const macho.relocation_info, |
| 275 | .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { | 1125 | index: i32 = -1, |
| 276 | .ARM64_RELOC_POINTER_TO_GOT, | 1126 | |
| 277 | .ARM64_RELOC_GOT_LOAD_PAGE21, | 1127 | pub fn next(self: *RelocIterator) ?macho.relocation_info { |
| 278 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, | 1128 | self.index += 1; |
| 279 | => true, | 1129 | if (self.index < self.buffer.len) { |
| 280 | else => false, | 1130 | return self.buffer[@intCast(u32, self.index)]; |
| 281 | }, | 1131 | } |
| 282 | .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { | 1132 | return null; |
| 283 | .X86_64_RELOC_GOT, | 1133 | } |
| 284 | .X86_64_RELOC_GOT_LOAD, | 1134 | |
| 285 | => true, | 1135 | pub fn peek(self: RelocIterator) macho.relocation_info { |
| 286 | else => false, | 1136 | assert(self.index + 1 < self.buffer.len); |
| 287 | }, | 1137 | return self.buffer[@intCast(u32, self.index + 1)]; |
| 288 | else => unreachable, | 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,8 +108,8 @@ symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{}, |
| 108 | 108 | ||
| 109 | strtab: std.ArrayListUnmanaged(u8) = .{}, | 109 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 110 | 110 | ||
| 111 | // stubs: std.ArrayListUnmanaged(*Symbol) = .{}, | 111 | stubs: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, |
| 112 | got_entries: std.ArrayListUnmanaged(GotEntry) = .{}, | 112 | got_entries: std.AutoArrayHashMapUnmanaged(GotIndirectionKey, void) = .{}, |
| 113 | 113 | ||
| 114 | stub_helper_stubs_start_off: ?u64 = null, | 114 | stub_helper_stubs_start_off: ?u64 = null, |
| 115 | 115 | ||
| ... | @@ -131,20 +131,12 @@ const SymbolWithLoc = struct { | ... | @@ -131,20 +131,12 @@ const SymbolWithLoc = struct { |
| 131 | file: u16 = 0, | 131 | file: u16 = 0, |
| 132 | }; | 132 | }; |
| 133 | 133 | ||
| 134 | pub const GotEntry = struct { | 134 | pub const GotIndirectionKey = struct { |
| 135 | /// GOT entry can either be a local pointer or an extern (nonlazy) import. | 135 | where: enum { |
| 136 | kind: enum { | ||
| 137 | local, | 136 | local, |
| 138 | import, | 137 | import, |
| 139 | }, | 138 | }, |
| 140 | 139 | where_index: u32, | |
| 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, | ||
| 148 | }; | 140 | }; |
| 149 | 141 | ||
| 150 | pub const Output = struct { | 142 | pub const Output = struct { |
| ... | @@ -161,7 +153,7 @@ pub fn init(allocator: *Allocator) !Zld { | ... | @@ -161,7 +153,7 @@ pub fn init(allocator: *Allocator) !Zld { |
| 161 | } | 153 | } |
| 162 | 154 | ||
| 163 | pub fn deinit(self: *Zld) void { | 155 | pub fn deinit(self: *Zld) void { |
| 164 | // self.stubs.deinit(self.allocator); | 156 | self.stubs.deinit(self.allocator); |
| 165 | self.got_entries.deinit(self.allocator); | 157 | self.got_entries.deinit(self.allocator); |
| 166 | 158 | ||
| 167 | for (self.load_commands.items) |*lc| { | 159 | for (self.load_commands.items) |*lc| { |
| ... | @@ -3043,3 +3035,33 @@ pub fn sectionId(self: Zld, match: MatchingSection) u8 { | ... | @@ -3043,3 +3035,33 @@ pub fn sectionId(self: Zld, match: MatchingSection) u8 { |
| 3043 | } | 3035 | } |
| 3044 | return section; | 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 | } | ||