| author | |
| committer | |
| log | 15b85df3dd8a754bc26159ea2202781b748a613e |
| tree | 0d39fefe78b408358a1f9ac223126b7887debf4a |
| parent | 54888c6f4699b07eadd21b744d797006fb96a284 |
5 files changed, 672 insertions(+), 632 deletions(-)
src/link/MachO/Object.zig+106-16| ... | ... | @@ -53,6 +53,7 @@ initializers: std.ArrayListUnmanaged(u32) = .{}, |
| 53 | 53 | data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, |
| 54 | 54 | |
| 55 | 55 | symbols: std.ArrayListUnmanaged(*Symbol) = .{}, |
| 56 | sections_as_symbols: std.AutoHashMapUnmanaged(u8, *Symbol) = .{}, | |
| 56 | 57 | |
| 57 | 58 | const DebugInfo = struct { |
| 58 | 59 | inner: dwarf.DwarfInfo, |
| ... | ... | @@ -160,6 +161,7 @@ pub fn deinit(self: *Object) void { |
| 160 | 161 | self.symtab.deinit(self.allocator); |
| 161 | 162 | self.strtab.deinit(self.allocator); |
| 162 | 163 | self.symbols.deinit(self.allocator); |
| 164 | self.sections_as_symbols.deinit(self.allocator); | |
| 163 | 165 | |
| 164 | 166 | if (self.name) |n| { |
| 165 | 167 | self.allocator.free(n); |
| ... | ... | @@ -312,7 +314,7 @@ fn filterRelocs(relocs: []macho.relocation_info, start: u64, end: u64) []macho.r |
| 312 | 314 | |
| 313 | 315 | while (true) { |
| 314 | 316 | var change = false; |
| 315 | if (relocs[start_id].r_address > end) { | |
| 317 | if (relocs[start_id].r_address >= end) { | |
| 316 | 318 | start_id += 1; |
| 317 | 319 | change = true; |
| 318 | 320 | } |
| ... | ... | @@ -332,6 +334,7 @@ const TextBlockParser = struct { |
| 332 | 334 | allocator: *Allocator, |
| 333 | 335 | section: macho.section_64, |
| 334 | 336 | code: []u8, |
| 337 | relocs: []macho.relocation_info, | |
| 335 | 338 | object: *Object, |
| 336 | 339 | zld: *Zld, |
| 337 | 340 | nlists: []NlistWithIndex, |
| ... | ... | @@ -405,6 +408,7 @@ const TextBlockParser = struct { |
| 405 | 408 | |
| 406 | 409 | const start_addr = senior_nlist.nlist.n_value - self.section.addr; |
| 407 | 410 | const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size; |
| 411 | log.warn("{} - {}", .{ start_addr, end_addr }); | |
| 408 | 412 | |
| 409 | 413 | const code = self.code[start_addr..end_addr]; |
| 410 | 414 | const size = code.len; |
| ... | ... | @@ -424,11 +428,18 @@ const TextBlockParser = struct { |
| 424 | 428 | block.* = .{ |
| 425 | 429 | .local_sym_index = senior_nlist.index, |
| 426 | 430 | .aliases = alias_only_indices, |
| 427 | .code = code, | |
| 431 | .references = std.AutoArrayHashMap(u32, void).init(self.allocator), | |
| 432 | .code = try self.allocator.dupe(u8, code), | |
| 433 | .relocs = std.ArrayList(*Relocation).init(self.allocator), | |
| 428 | 434 | .size = size, |
| 429 | 435 | .alignment = self.section.@"align", |
| 430 | 436 | }; |
| 431 | 437 | |
| 438 | const relocs = filterRelocs(self.relocs, start_addr, end_addr); | |
| 439 | if (relocs.len > 0) { | |
| 440 | try self.object.parseRelocs(self.zld, relocs, block, start_addr); | |
| 441 | } | |
| 442 | ||
| 432 | 443 | self.index += 1; |
| 433 | 444 | |
| 434 | 445 | return block; |
| ... | ... | @@ -457,7 +468,8 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 457 | 468 | |
| 458 | 469 | sort.sort(NlistWithIndex, sorted_nlists.items, {}, NlistWithIndex.lessThan); |
| 459 | 470 | |
| 460 | for (seg.sections.items) |sect, sect_id| { | |
| 471 | for (seg.sections.items) |sect, id| { | |
| 472 | const sect_id = @intCast(u8, id); | |
| 461 | 473 | log.warn("putting section '{s},{s}' as a TextBlock", .{ |
| 462 | 474 | segmentName(sect), |
| 463 | 475 | sectionName(sect), |
| ... | ... | @@ -474,6 +486,12 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 474 | 486 | defer self.allocator.free(code); |
| 475 | 487 | _ = try self.file.?.preadAll(code, sect.offset); |
| 476 | 488 | |
| 489 | // Read section's list of relocations | |
| 490 | var raw_relocs = try self.allocator.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); | |
| 491 | defer self.allocator.free(raw_relocs); | |
| 492 | _ = try self.file.?.preadAll(raw_relocs, sect.reloff); | |
| 493 | const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs); | |
| 494 | ||
| 477 | 495 | // Is there any padding between symbols within the section? |
| 478 | 496 | const is_padded = self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; |
| 479 | 497 | |
| ... | ... | @@ -481,7 +499,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 481 | 499 | if (is_padded) blocks: { |
| 482 | 500 | const filtered_nlists = NlistWithIndex.filterInSection( |
| 483 | 501 | sorted_nlists.items, |
| 484 | @intCast(u8, sect_id + 1), | |
| 502 | sect_id + 1, | |
| 485 | 503 | ); |
| 486 | 504 | |
| 487 | 505 | if (filtered_nlists.len == 0) break :blocks; |
| ... | ... | @@ -490,6 +508,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 490 | 508 | .allocator = self.allocator, |
| 491 | 509 | .section = sect, |
| 492 | 510 | .code = code, |
| 511 | .relocs = relocs, | |
| 493 | 512 | .object = self, |
| 494 | 513 | .zld = zld, |
| 495 | 514 | .nlists = filtered_nlists, |
| ... | ... | @@ -518,8 +537,6 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 518 | 537 | } |
| 519 | 538 | } |
| 520 | 539 | |
| 521 | // TODO parse relocs | |
| 522 | ||
| 523 | 540 | if (zld.last_text_block) |last| { |
| 524 | 541 | last.next = block; |
| 525 | 542 | block.prev = last; |
| ... | ... | @@ -531,14 +548,19 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 531 | 548 | } |
| 532 | 549 | |
| 533 | 550 | // Since there is no symbol to refer to this block, we create |
| 534 | // a temp one. | |
| 535 | const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ | |
| 536 | self.name.?, | |
| 537 | segmentName(sect), | |
| 538 | sectionName(sect), | |
| 539 | }); | |
| 540 | defer self.allocator.free(name); | |
| 541 | const symbol = try Symbol.new(self.allocator, name); | |
| 551 | // a temp one, unless we already did that when working out the relocations | |
| 552 | // of other text blocks. | |
| 553 | const symbol = self.sections_as_symbols.get(sect_id) orelse symbol: { | |
| 554 | const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ | |
| 555 | self.name.?, | |
| 556 | segmentName(sect), | |
| 557 | sectionName(sect), | |
| 558 | }); | |
| 559 | defer self.allocator.free(name); | |
| 560 | const symbol = try Symbol.new(self.allocator, name); | |
| 561 | try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol); | |
| 562 | break :symbol symbol; | |
| 563 | }; | |
| 542 | 564 | symbol.payload = .{ |
| 543 | 565 | .regular = .{ |
| 544 | 566 | .linkage = .translation_unit, |
| ... | ... | @@ -555,12 +577,16 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 555 | 577 | |
| 556 | 578 | block.* = .{ |
| 557 | 579 | .local_sym_index = local_sym_index, |
| 558 | .code = code, | |
| 580 | .references = std.AutoArrayHashMap(u32, void).init(self.allocator), | |
| 581 | .code = try self.allocator.dupe(u8, code), | |
| 582 | .relocs = std.ArrayList(*Relocation).init(self.allocator), | |
| 559 | 583 | .size = sect.size, |
| 560 | 584 | .alignment = sect.@"align", |
| 561 | 585 | }; |
| 562 | 586 | |
| 563 | // TODO parse relocs | |
| 587 | if (relocs.len > 0) { | |
| 588 | try self.parseRelocs(zld, relocs, block, 0); | |
| 589 | } | |
| 564 | 590 | |
| 565 | 591 | if (zld.last_text_block) |last| { |
| 566 | 592 | last.next = block; |
| ... | ... | @@ -571,6 +597,70 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 571 | 597 | } |
| 572 | 598 | } |
| 573 | 599 | |
| 600 | fn parseRelocs( | |
| 601 | self: *Object, | |
| 602 | zld: *Zld, | |
| 603 | relocs: []const macho.relocation_info, | |
| 604 | block: *TextBlock, | |
| 605 | base_addr: u64, | |
| 606 | ) !void { | |
| 607 | var it = reloc.RelocIterator{ | |
| 608 | .buffer = relocs, | |
| 609 | }; | |
| 610 | ||
| 611 | switch (self.arch.?) { | |
| 612 | .aarch64 => { | |
| 613 | var parser = reloc.aarch64.Parser{ | |
| 614 | .object = self, | |
| 615 | .zld = zld, | |
| 616 | .it = &it, | |
| 617 | .block = block, | |
| 618 | .base_addr = base_addr, | |
| 619 | }; | |
| 620 | try parser.parse(); | |
| 621 | }, | |
| 622 | .x86_64 => { | |
| 623 | var parser = reloc.x86_64.Parser{ | |
| 624 | .object = self, | |
| 625 | .zld = zld, | |
| 626 | .it = &it, | |
| 627 | .block = block, | |
| 628 | .base_addr = base_addr, | |
| 629 | }; | |
| 630 | try parser.parse(); | |
| 631 | }, | |
| 632 | else => unreachable, | |
| 633 | } | |
| 634 | } | |
| 635 | ||
| 636 | pub fn symbolFromReloc(self: *Object, rel: macho.relocation_info) !*Symbol { | |
| 637 | const symbol = blk: { | |
| 638 | if (rel.r_extern == 1) { | |
| 639 | break :blk self.symbols.items[rel.r_symbolnum]; | |
| 640 | } else { | |
| 641 | const sect_id = @intCast(u8, rel.r_symbolnum - 1); | |
| 642 | const symbol = self.sections_as_symbols.get(sect_id) orelse symbol: { | |
| 643 | // We need a valid pointer to Symbol even if there is no symbol, so we create a | |
| 644 | // dummy symbol upfront which will later be populated when created a TextBlock from | |
| 645 | // the target section here. | |
| 646 | const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; | |
| 647 | const sect = seg.sections.items[sect_id]; | |
| 648 | const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ | |
| 649 | self.name.?, | |
| 650 | segmentName(sect), | |
| 651 | sectionName(sect), | |
| 652 | }); | |
| 653 | defer self.allocator.free(name); | |
| 654 | const symbol = try Symbol.new(self.allocator, name); | |
| 655 | try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol); | |
| 656 | break :symbol symbol; | |
| 657 | }; | |
| 658 | break :blk symbol; | |
| 659 | } | |
| 660 | }; | |
| 661 | return symbol; | |
| 662 | } | |
| 663 | ||
| 574 | 664 | pub fn parseInitializers(self: *Object) !void { |
| 575 | 665 | const index = self.mod_init_func_section_index orelse return; |
| 576 | 666 | const section = self.sections.items[index]; |
src/link/MachO/Zld.zig+18-62| ... | ... | @@ -135,9 +135,9 @@ const TlvOffset = struct { |
| 135 | 135 | pub const TextBlock = struct { |
| 136 | 136 | local_sym_index: u32, |
| 137 | 137 | aliases: ?[]u32 = null, |
| 138 | references: ?[]u32 = null, | |
| 138 | references: std.AutoArrayHashMap(u32, void), | |
| 139 | 139 | code: []u8, |
| 140 | relocs: ?[]*Relocation = null, | |
| 140 | relocs: std.ArrayList(*Relocation), | |
| 141 | 141 | size: u64, |
| 142 | 142 | alignment: u32, |
| 143 | 143 | next: ?*TextBlock = null, |
| ... | ... | @@ -147,15 +147,8 @@ pub const TextBlock = struct { |
| 147 | 147 | if (block.aliases) |aliases| { |
| 148 | 148 | allocator.free(aliases); |
| 149 | 149 | } |
| 150 | if (block.references) |references| { | |
| 151 | allocator.free(references); | |
| 152 | } | |
| 153 | for (block.relocs.items) |reloc| { | |
| 154 | allocator.destroy(reloc); | |
| 155 | } | |
| 156 | if (block.relocs) |relocs| { | |
| 157 | allocator.free(relocs); | |
| 158 | } | |
| 150 | block.relocs.deinit(); | |
| 151 | block.references.deinit(); | |
| 159 | 152 | allocator.free(code); |
| 160 | 153 | } |
| 161 | 154 | |
| ... | ... | @@ -168,12 +161,19 @@ pub const TextBlock = struct { |
| 168 | 161 | log.warn(" | {}: {}", .{ index, zld.locals.items[index] }); |
| 169 | 162 | } |
| 170 | 163 | } |
| 171 | if (self.references) |references| { | |
| 164 | if (self.references.count() > 0) { | |
| 172 | 165 | log.warn(" | References:", .{}); |
| 173 | for (references) |index| { | |
| 166 | for (self.references.keys()) |index| { | |
| 174 | 167 | log.warn(" | {}: {}", .{ index, zld.locals.items[index] }); |
| 175 | 168 | } |
| 176 | 169 | } |
| 170 | log.warn(" | code.len = {}", .{self.code.len}); | |
| 171 | if (self.relocs.items.len > 0) { | |
| 172 | log.warn("Relocations:", .{}); | |
| 173 | for (self.relocs.items) |rel| { | |
| 174 | log.warn(" | {}", .{rel}); | |
| 175 | } | |
| 176 | } | |
| 177 | 177 | log.warn(" | size = {}", .{self.size}); |
| 178 | 178 | log.warn(" | align = {}", .{self.alignment}); |
| 179 | 179 | } |
| ... | ... | @@ -280,7 +280,6 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg |
| 280 | 280 | try self.resolveSymbols(); |
| 281 | 281 | try self.parseTextBlocks(); |
| 282 | 282 | return error.TODO; |
| 283 | // try self.resolveStubsAndGotEntries(); | |
| 284 | 283 | // try self.updateMetadata(); |
| 285 | 284 | // try self.sortSections(); |
| 286 | 285 | // try self.addRpaths(args.rpaths); |
| ... | ... | @@ -1603,7 +1602,9 @@ fn resolveSymbols(self: *Zld) !void { |
| 1603 | 1602 | |
| 1604 | 1603 | block.* = .{ |
| 1605 | 1604 | .local_sym_index = local_sym_index, |
| 1605 | .references = std.AutoArrayHashMap(u32, void).init(self.allocator), | |
| 1606 | 1606 | .code = code, |
| 1607 | .relocs = std.ArrayList(*Relocation).init(self.allocator), | |
| 1607 | 1608 | .size = size, |
| 1608 | 1609 | .alignment = alignment, |
| 1609 | 1610 | }; |
| ... | ... | @@ -1624,6 +1625,9 @@ fn resolveSymbols(self: *Zld) !void { |
| 1624 | 1625 | { |
| 1625 | 1626 | // Put dyld_stub_binder as an undefined special symbol. |
| 1626 | 1627 | const symbol = try Symbol.new(self.allocator, "dyld_stub_binder"); |
| 1628 | const index = @intCast(u32, self.got_entries.items.len); | |
| 1629 | symbol.got_index = index; | |
| 1630 | try self.got_entries.append(self.allocator, symbol); | |
| 1627 | 1631 | try self.globals.putNoClobber(self.allocator, symbol.name, symbol); |
| 1628 | 1632 | } |
| 1629 | 1633 | |
| ... | ... | @@ -1699,54 +1703,6 @@ fn parseTextBlocks(self: *Zld) !void { |
| 1699 | 1703 | } |
| 1700 | 1704 | } |
| 1701 | 1705 | |
| 1702 | fn resolveStubsAndGotEntries(self: *Zld) !void { | |
| 1703 | for (self.objects.items) |object| { | |
| 1704 | log.debug("resolving stubs and got entries from {s}", .{object.name}); | |
| 1705 | ||
| 1706 | for (object.sections.items) |sect| { | |
| 1707 | const relocs = sect.relocs orelse continue; | |
| 1708 | for (relocs) |rel| { | |
| 1709 | switch (rel.@"type") { | |
| 1710 | .unsigned => continue, | |
| 1711 | .got_page, .got_page_off, .got_load, .got, .pointer_to_got => { | |
| 1712 | const sym = object.symbols.items[rel.target.symbol]; | |
| 1713 | if (sym.got_index != null) continue; | |
| 1714 | ||
| 1715 | const index = @intCast(u32, self.got_entries.items.len); | |
| 1716 | sym.got_index = index; | |
| 1717 | try self.got_entries.append(self.allocator, sym); | |
| 1718 | ||
| 1719 | log.debug(" | found GOT entry {s}: {*}", .{ sym.name, sym }); | |
| 1720 | }, | |
| 1721 | else => { | |
| 1722 | if (rel.target != .symbol) continue; | |
| 1723 | ||
| 1724 | const sym = object.symbols.items[rel.target.symbol]; | |
| 1725 | assert(sym.payload != .undef); | |
| 1726 | ||
| 1727 | if (sym.stubs_index != null) continue; | |
| 1728 | if (sym.payload != .proxy) continue; | |
| 1729 | ||
| 1730 | const index = @intCast(u32, self.stubs.items.len); | |
| 1731 | sym.stubs_index = index; | |
| 1732 | try self.stubs.append(self.allocator, sym); | |
| 1733 | ||
| 1734 | log.debug(" | found stub {s}: {*}", .{ sym.name, sym }); | |
| 1735 | }, | |
| 1736 | } | |
| 1737 | } | |
| 1738 | } | |
| 1739 | } | |
| 1740 | ||
| 1741 | // Finally, put dyld_stub_binder as the final GOT entry | |
| 1742 | const sym = self.globals.get("dyld_stub_binder") orelse unreachable; | |
| 1743 | const index = @intCast(u32, self.got_entries.items.len); | |
| 1744 | sym.got_index = index; | |
| 1745 | try self.got_entries.append(self.allocator, sym); | |
| 1746 | ||
| 1747 | log.debug(" | found GOT entry {s}: {*}", .{ sym.name, sym }); | |
| 1748 | } | |
| 1749 | ||
| 1750 | 1706 | fn resolveRelocsAndWriteSections(self: *Zld) !void { |
| 1751 | 1707 | for (self.objects.items) |object| { |
| 1752 | 1708 | log.debug("relocating object {s}", .{object.name}); |
src/link/MachO/reloc.zig+88-127| ... | ... | @@ -6,16 +6,18 @@ const math = std.math; |
| 6 | 6 | const mem = std.mem; |
| 7 | 7 | const meta = std.meta; |
| 8 | 8 | |
| 9 | const aarch64 = @import("reloc/aarch64.zig"); | |
| 10 | const x86_64 = @import("reloc/x86_64.zig"); | |
| 9 | pub const aarch64 = @import("reloc/aarch64.zig"); | |
| 10 | pub const x86_64 = @import("reloc/x86_64.zig"); | |
| 11 | 11 | |
| 12 | 12 | const Allocator = mem.Allocator; |
| 13 | const Symbol = @import("Symbol.zig"); | |
| 14 | const TextBlock = @import("Zld.zig").TextBlock; | |
| 13 | 15 | |
| 14 | 16 | pub const Relocation = struct { |
| 15 | 17 | @"type": Type, |
| 16 | code: []u8, | |
| 17 | 18 | offset: u32, |
| 18 | target: Target, | |
| 19 | block: *TextBlock, | |
| 20 | target: *Symbol, | |
| 19 | 21 | |
| 20 | 22 | pub fn cast(base: *Relocation, comptime T: type) ?*T { |
| 21 | 23 | if (base.@"type" != T.base_type) |
| ... | ... | @@ -24,43 +26,24 @@ pub const Relocation = struct { |
| 24 | 26 | return @fieldParentPtr(T, "base", base); |
| 25 | 27 | } |
| 26 | 28 | |
| 27 | pub const ResolveArgs = struct { | |
| 28 | source_addr: u64, | |
| 29 | target_addr: u64, | |
| 30 | subtractor: ?u64 = null, | |
| 31 | source_source_sect_addr: ?u64 = null, | |
| 32 | source_target_sect_addr: ?u64 = null, | |
| 33 | }; | |
| 34 | ||
| 35 | pub fn resolve(base: *Relocation, args: ResolveArgs) !void { | |
| 36 | log.debug("{s}", .{base.@"type"}); | |
| 37 | log.debug(" | offset 0x{x}", .{base.offset}); | |
| 38 | log.debug(" | source address 0x{x}", .{args.source_addr}); | |
| 39 | log.debug(" | target address 0x{x}", .{args.target_addr}); | |
| 40 | if (args.subtractor) |sub| | |
| 41 | log.debug(" | subtractor address 0x{x}", .{sub}); | |
| 42 | if (args.source_source_sect_addr) |addr| | |
| 43 | log.debug(" | source source section address 0x{x}", .{addr}); | |
| 44 | if (args.source_target_sect_addr) |addr| | |
| 45 | log.debug(" | source target section address 0x{x}", .{addr}); | |
| 46 | ||
| 47 | return switch (base.@"type") { | |
| 48 | .unsigned => @fieldParentPtr(Unsigned, "base", base).resolve(args), | |
| 49 | .branch_aarch64 => @fieldParentPtr(aarch64.Branch, "base", base).resolve(args), | |
| 50 | .page => @fieldParentPtr(aarch64.Page, "base", base).resolve(args), | |
| 51 | .page_off => @fieldParentPtr(aarch64.PageOff, "base", base).resolve(args), | |
| 52 | .got_page => @fieldParentPtr(aarch64.GotPage, "base", base).resolve(args), | |
| 53 | .got_page_off => @fieldParentPtr(aarch64.GotPageOff, "base", base).resolve(args), | |
| 54 | .pointer_to_got => @fieldParentPtr(aarch64.PointerToGot, "base", base).resolve(args), | |
| 55 | .tlvp_page => @fieldParentPtr(aarch64.TlvpPage, "base", base).resolve(args), | |
| 56 | .tlvp_page_off => @fieldParentPtr(aarch64.TlvpPageOff, "base", base).resolve(args), | |
| 57 | .branch_x86_64 => @fieldParentPtr(x86_64.Branch, "base", base).resolve(args), | |
| 58 | .signed => @fieldParentPtr(x86_64.Signed, "base", base).resolve(args), | |
| 59 | .got_load => @fieldParentPtr(x86_64.GotLoad, "base", base).resolve(args), | |
| 60 | .got => @fieldParentPtr(x86_64.Got, "base", base).resolve(args), | |
| 61 | .tlv => @fieldParentPtr(x86_64.Tlv, "base", base).resolve(args), | |
| 62 | }; | |
| 63 | } | |
| 29 | // pub fn resolve(base: *Relocation) !void { | |
| 30 | // return switch (base.@"type") { | |
| 31 | // .unsigned => @fieldParentPtr(Unsigned, "base", base).resolve(), | |
| 32 | // .branch_aarch64 => @fieldParentPtr(aarch64.Branch, "base", base).resolve(), | |
| 33 | // .page => @fieldParentPtr(aarch64.Page, "base", base).resolve(), | |
| 34 | // .page_off => @fieldParentPtr(aarch64.PageOff, "base", base).resolve(), | |
| 35 | // .got_page => @fieldParentPtr(aarch64.GotPage, "base", base).resolve(), | |
| 36 | // .got_page_off => @fieldParentPtr(aarch64.GotPageOff, "base", base).resolve(), | |
| 37 | // .pointer_to_got => @fieldParentPtr(aarch64.PointerToGot, "base", base).resolve(), | |
| 38 | // .tlvp_page => @fieldParentPtr(aarch64.TlvpPage, "base", base).resolve(), | |
| 39 | // .tlvp_page_off => @fieldParentPtr(aarch64.TlvpPageOff, "base", base).resolve(), | |
| 40 | // .branch_x86_64 => @fieldParentPtr(x86_64.Branch, "base", base).resolve(), | |
| 41 | // .signed => @fieldParentPtr(x86_64.Signed, "base", base).resolve(), | |
| 42 | // .got_load => @fieldParentPtr(x86_64.GotLoad, "base", base).resolve(), | |
| 43 | // .got => @fieldParentPtr(x86_64.Got, "base", base).resolve(), | |
| 44 | // .tlv => @fieldParentPtr(x86_64.Tlv, "base", base).resolve(), | |
| 45 | // }; | |
| 46 | // } | |
| 64 | 47 | |
| 65 | 48 | pub const Type = enum { |
| 66 | 49 | branch_aarch64, |
| ... | ... | @@ -79,23 +62,37 @@ pub const Relocation = struct { |
| 79 | 62 | tlv, |
| 80 | 63 | }; |
| 81 | 64 | |
| 82 | pub const Target = union(enum) { | |
| 83 | symbol: u32, | |
| 84 | section: u16, | |
| 65 | pub fn format(base: *const Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 66 | try std.fmt.format(writer, "Relocation {{ ", .{}); | |
| 67 | try std.fmt.format(writer, ".type = {s}, ", .{base.@"type"}); | |
| 68 | try std.fmt.format(writer, ".offset = {}, ", .{base.offset}); | |
| 69 | try std.fmt.format(writer, ".block = {}", .{base.block.local_sym_index}); | |
| 70 | try std.fmt.format(writer, ".target = {}, ", .{base.target}); | |
| 71 | ||
| 72 | try switch (base.@"type") { | |
| 73 | .unsigned => @fieldParentPtr(Unsigned, "base", base).format(fmt, options, writer), | |
| 74 | .branch_aarch64 => @fieldParentPtr(aarch64.Branch, "base", base).format(fmt, options, writer), | |
| 75 | .page => @fieldParentPtr(aarch64.Page, "base", base).format(fmt, options, writer), | |
| 76 | .page_off => @fieldParentPtr(aarch64.PageOff, "base", base).format(fmt, options, writer), | |
| 77 | .got_page => @fieldParentPtr(aarch64.GotPage, "base", base).format(fmt, options, writer), | |
| 78 | .got_page_off => @fieldParentPtr(aarch64.GotPageOff, "base", base).format(fmt, options, writer), | |
| 79 | .pointer_to_got => @fieldParentPtr(aarch64.PointerToGot, "base", base).format(fmt, options, writer), | |
| 80 | .tlvp_page => @fieldParentPtr(aarch64.TlvpPage, "base", base).format(fmt, options, writer), | |
| 81 | .tlvp_page_off => @fieldParentPtr(aarch64.TlvpPageOff, "base", base).format(fmt, options, writer), | |
| 82 | .branch_x86_64 => @fieldParentPtr(x86_64.Branch, "base", base).format(fmt, options, writer), | |
| 83 | .signed => @fieldParentPtr(x86_64.Signed, "base", base).format(fmt, options, writer), | |
| 84 | .got_load => @fieldParentPtr(x86_64.GotLoad, "base", base).format(fmt, options, writer), | |
| 85 | .got => @fieldParentPtr(x86_64.Got, "base", base).format(fmt, options, writer), | |
| 86 | .tlv => @fieldParentPtr(x86_64.Tlv, "base", base).format(fmt, options, writer), | |
| 87 | }; | |
| 85 | 88 | |
| 86 | pub fn fromReloc(reloc: macho.relocation_info) Target { | |
| 87 | return if (reloc.r_extern == 1) .{ | |
| 88 | .symbol = reloc.r_symbolnum, | |
| 89 | } else .{ | |
| 90 | .section = @intCast(u16, reloc.r_symbolnum - 1), | |
| 91 | }; | |
| 92 | } | |
| 93 | }; | |
| 89 | try std.fmt.format(writer, "}}", .{}); | |
| 90 | } | |
| 94 | 91 | }; |
| 95 | 92 | |
| 96 | 93 | pub const Unsigned = struct { |
| 97 | 94 | base: Relocation, |
| 98 | subtractor: ?Relocation.Target = null, | |
| 95 | subtractor: ?*Symbol = null, | |
| 99 | 96 | /// Addend embedded directly in the relocation slot |
| 100 | 97 | addend: i64, |
| 101 | 98 | /// Extracted from r_length: |
| ... | ... | @@ -106,75 +103,47 @@ pub const Unsigned = struct { |
| 106 | 103 | |
| 107 | 104 | pub const base_type: Relocation.Type = .unsigned; |
| 108 | 105 | |
| 109 | pub fn resolve(unsigned: Unsigned, args: Relocation.ResolveArgs) !void { | |
| 110 | const addend = if (unsigned.base.target == .section) | |
| 111 | unsigned.addend - @intCast(i64, args.source_target_sect_addr.?) | |
| 112 | else | |
| 113 | unsigned.addend; | |
| 114 | ||
| 115 | const result = if (args.subtractor) |subtractor| | |
| 116 | @intCast(i64, args.target_addr) - @intCast(i64, subtractor) + addend | |
| 117 | else | |
| 118 | @intCast(i64, args.target_addr) + addend; | |
| 119 | ||
| 120 | log.debug(" | calculated addend 0x{x}", .{addend}); | |
| 121 | log.debug(" | calculated unsigned value 0x{x}", .{result}); | |
| 122 | ||
| 123 | if (unsigned.is_64bit) { | |
| 124 | mem.writeIntLittle( | |
| 125 | u64, | |
| 126 | unsigned.base.code[0..8], | |
| 127 | @bitCast(u64, result), | |
| 128 | ); | |
| 129 | } else { | |
| 130 | mem.writeIntLittle( | |
| 131 | u32, | |
| 132 | unsigned.base.code[0..4], | |
| 133 | @truncate(u32, @bitCast(u64, result)), | |
| 134 | ); | |
| 106 | // pub fn resolve(unsigned: Unsigned) !void { | |
| 107 | // const addend = if (unsigned.base.target == .section) | |
| 108 | // unsigned.addend - @intCast(i64, args.source_target_sect_addr.?) | |
| 109 | // else | |
| 110 | // unsigned.addend; | |
| 111 | ||
| 112 | // const result = if (args.subtractor) |subtractor| | |
| 113 | // @intCast(i64, args.target_addr) - @intCast(i64, subtractor) + addend | |
| 114 | // else | |
| 115 | // @intCast(i64, args.target_addr) + addend; | |
| 116 | ||
| 117 | // log.debug(" | calculated addend 0x{x}", .{addend}); | |
| 118 | // log.debug(" | calculated unsigned value 0x{x}", .{result}); | |
| 119 | ||
| 120 | // if (unsigned.is_64bit) { | |
| 121 | // mem.writeIntLittle( | |
| 122 | // u64, | |
| 123 | // unsigned.base.code[0..8], | |
| 124 | // @bitCast(u64, result), | |
| 125 | // ); | |
| 126 | // } else { | |
| 127 | // mem.writeIntLittle( | |
| 128 | // u32, | |
| 129 | // unsigned.base.code[0..4], | |
| 130 | // @truncate(u32, @bitCast(u64, result)), | |
| 131 | // ); | |
| 132 | // } | |
| 133 | // } | |
| 134 | ||
| 135 | pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 136 | _ = fmt; | |
| 137 | _ = options; | |
| 138 | if (self.subtractor) |sub| { | |
| 139 | try std.fmt.format(writer, ".subtractor = {}, ", .{sub}); | |
| 135 | 140 | } |
| 141 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 142 | const length: usize = if (self.is_64bit) 8 else 4; | |
| 143 | try std.fmt.format(writer, ".length = {}, ", .{length}); | |
| 136 | 144 | } |
| 137 | 145 | }; |
| 138 | 146 | |
| 139 | pub fn parse( | |
| 140 | allocator: *Allocator, | |
| 141 | arch: std.Target.Cpu.Arch, | |
| 142 | code: []u8, | |
| 143 | relocs: []const macho.relocation_info, | |
| 144 | ) ![]*Relocation { | |
| 145 | var it = RelocIterator{ | |
| 146 | .buffer = relocs, | |
| 147 | }; | |
| 148 | ||
| 149 | switch (arch) { | |
| 150 | .aarch64 => { | |
| 151 | var parser = aarch64.Parser{ | |
| 152 | .allocator = allocator, | |
| 153 | .it = &it, | |
| 154 | .code = code, | |
| 155 | .parsed = std.ArrayList(*Relocation).init(allocator), | |
| 156 | }; | |
| 157 | defer parser.deinit(); | |
| 158 | try parser.parse(); | |
| 159 | ||
| 160 | return parser.parsed.toOwnedSlice(); | |
| 161 | }, | |
| 162 | .x86_64 => { | |
| 163 | var parser = x86_64.Parser{ | |
| 164 | .allocator = allocator, | |
| 165 | .it = &it, | |
| 166 | .code = code, | |
| 167 | .parsed = std.ArrayList(*Relocation).init(allocator), | |
| 168 | }; | |
| 169 | defer parser.deinit(); | |
| 170 | try parser.parse(); | |
| 171 | ||
| 172 | return parser.parsed.toOwnedSlice(); | |
| 173 | }, | |
| 174 | else => unreachable, | |
| 175 | } | |
| 176 | } | |
| 177 | ||
| 178 | 147 | pub const RelocIterator = struct { |
| 179 | 148 | buffer: []const macho.relocation_info, |
| 180 | 149 | index: i32 = -1, |
| ... | ... | @@ -182,15 +151,7 @@ pub const RelocIterator = struct { |
| 182 | 151 | pub fn next(self: *RelocIterator) ?macho.relocation_info { |
| 183 | 152 | self.index += 1; |
| 184 | 153 | if (self.index < self.buffer.len) { |
| 185 | const reloc = self.buffer[@intCast(u32, self.index)]; | |
| 186 | log.debug("relocation", .{}); | |
| 187 | log.debug(" | type = {}", .{reloc.r_type}); | |
| 188 | log.debug(" | offset = {}", .{reloc.r_address}); | |
| 189 | log.debug(" | PC = {}", .{reloc.r_pcrel == 1}); | |
| 190 | log.debug(" | length = {}", .{reloc.r_length}); | |
| 191 | log.debug(" | symbolnum = {}", .{reloc.r_symbolnum}); | |
| 192 | log.debug(" | extern = {}", .{reloc.r_extern == 1}); | |
| 193 | return reloc; | |
| 154 | return self.buffer[@intCast(u32, self.index)]; | |
| 194 | 155 | } |
| 195 | 156 | return null; |
| 196 | 157 | } |
src/link/MachO/reloc/aarch64.zig+278-287| ... | ... | @@ -9,24 +9,34 @@ const meta = std.meta; |
| 9 | 9 | const reloc = @import("../reloc.zig"); |
| 10 | 10 | |
| 11 | 11 | const Allocator = mem.Allocator; |
| 12 | const Object = @import("../Object.zig"); | |
| 12 | 13 | const Relocation = reloc.Relocation; |
| 13 | 14 | const Symbol = @import("../Symbol.zig"); |
| 15 | const TextBlock = Zld.TextBlock; | |
| 16 | const Zld = @import("../Zld.zig"); | |
| 14 | 17 | |
| 15 | 18 | pub const Branch = struct { |
| 16 | 19 | base: Relocation, |
| 17 | 20 | /// Always .UnconditionalBranchImmediate |
| 18 | inst: aarch64.Instruction, | |
| 21 | // inst: aarch64.Instruction, | |
| 19 | 22 | |
| 20 | 23 | pub const base_type: Relocation.Type = .branch_aarch64; |
| 21 | 24 | |
| 22 | pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void { | |
| 23 | const displacement = try math.cast(i28, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr)); | |
| 25 | // pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void { | |
| 26 | // const displacement = try math.cast(i28, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr)); | |
| 24 | 27 | |
| 25 | log.debug(" | displacement 0x{x}", .{displacement}); | |
| 28 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 26 | 29 | |
| 27 | var inst = branch.inst; | |
| 28 | inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); | |
| 29 | mem.writeIntLittle(u32, branch.base.code[0..4], inst.toU32()); | |
| 30 | // var inst = branch.inst; | |
| 31 | // inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); | |
| 32 | // mem.writeIntLittle(u32, branch.base.code[0..4], inst.toU32()); | |
| 33 | // } | |
| 34 | ||
| 35 | pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 36 | _ = self; | |
| 37 | _ = fmt; | |
| 38 | _ = options; | |
| 39 | _ = writer; | |
| 30 | 40 | } |
| 31 | 41 | }; |
| 32 | 42 | |
| ... | ... | @@ -34,24 +44,32 @@ pub const Page = struct { |
| 34 | 44 | base: Relocation, |
| 35 | 45 | addend: ?u32 = null, |
| 36 | 46 | /// Always .PCRelativeAddress |
| 37 | inst: aarch64.Instruction, | |
| 47 | // inst: aarch64.Instruction, | |
| 38 | 48 | |
| 39 | 49 | pub const base_type: Relocation.Type = .page; |
| 40 | 50 | |
| 41 | pub fn resolve(page: Page, args: Relocation.ResolveArgs) !void { | |
| 42 | const target_addr = if (page.addend) |addend| args.target_addr + addend else args.target_addr; | |
| 43 | const source_page = @intCast(i32, args.source_addr >> 12); | |
| 44 | const target_page = @intCast(i32, target_addr >> 12); | |
| 45 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 51 | // pub fn resolve(page: Page, args: Relocation.ResolveArgs) !void { | |
| 52 | // const target_addr = if (page.addend) |addend| args.target_addr + addend else args.target_addr; | |
| 53 | // const source_page = @intCast(i32, args.source_addr >> 12); | |
| 54 | // const target_page = @intCast(i32, target_addr >> 12); | |
| 55 | // const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 56 | ||
| 57 | // log.debug(" | calculated addend 0x{x}", .{page.addend}); | |
| 58 | // log.debug(" | moving by {} pages", .{pages}); | |
| 46 | 59 | |
| 47 | log.debug(" | calculated addend 0x{x}", .{page.addend}); | |
| 48 | log.debug(" | moving by {} pages", .{pages}); | |
| 60 | // var inst = page.inst; | |
| 61 | // inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 62 | // inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 49 | 63 | |
| 50 | var inst = page.inst; | |
| 51 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 52 | inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 64 | // mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | |
| 65 | // } | |
| 53 | 66 | |
| 54 | mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | |
| 67 | pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 68 | _ = fmt; | |
| 69 | _ = options; | |
| 70 | if (self.addend) |addend| { | |
| 71 | try std.fmt.format(writer, ".addend = {}, ", .{addend}); | |
| 72 | } | |
| 55 | 73 | } |
| 56 | 74 | }; |
| 57 | 75 | |
| ... | ... | @@ -59,7 +77,7 @@ pub const PageOff = struct { |
| 59 | 77 | base: Relocation, |
| 60 | 78 | addend: ?u32 = null, |
| 61 | 79 | op_kind: OpKind, |
| 62 | inst: aarch64.Instruction, | |
| 80 | // inst: aarch64.Instruction, | |
| 63 | 81 | |
| 64 | 82 | pub const base_type: Relocation.Type = .page_off; |
| 65 | 83 | |
| ... | ... | @@ -68,76 +86,99 @@ pub const PageOff = struct { |
| 68 | 86 | load_store, |
| 69 | 87 | }; |
| 70 | 88 | |
| 71 | pub fn resolve(page_off: PageOff, args: Relocation.ResolveArgs) !void { | |
| 72 | const target_addr = if (page_off.addend) |addend| args.target_addr + addend else args.target_addr; | |
| 73 | const narrowed = @truncate(u12, target_addr); | |
| 74 | ||
| 75 | log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 76 | log.debug(" | {s} opcode", .{page_off.op_kind}); | |
| 77 | ||
| 78 | var inst = page_off.inst; | |
| 79 | if (page_off.op_kind == .arithmetic) { | |
| 80 | inst.add_subtract_immediate.imm12 = narrowed; | |
| 81 | } else { | |
| 82 | const offset: u12 = blk: { | |
| 83 | if (inst.load_store_register.size == 0) { | |
| 84 | if (inst.load_store_register.v == 1) { | |
| 85 | // 128-bit SIMD is scaled by 16. | |
| 86 | break :blk try math.divExact(u12, narrowed, 16); | |
| 87 | } | |
| 88 | // Otherwise, 8-bit SIMD or ldrb. | |
| 89 | break :blk narrowed; | |
| 90 | } else { | |
| 91 | const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size); | |
| 92 | break :blk try math.divExact(u12, narrowed, denom); | |
| 93 | } | |
| 94 | }; | |
| 95 | inst.load_store_register.offset = offset; | |
| 89 | // pub fn resolve(page_off: PageOff, args: Relocation.ResolveArgs) !void { | |
| 90 | // const target_addr = if (page_off.addend) |addend| args.target_addr + addend else args.target_addr; | |
| 91 | // const narrowed = @truncate(u12, target_addr); | |
| 92 | ||
| 93 | // log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 94 | // log.debug(" | {s} opcode", .{page_off.op_kind}); | |
| 95 | ||
| 96 | // var inst = page_off.inst; | |
| 97 | // if (page_off.op_kind == .arithmetic) { | |
| 98 | // inst.add_subtract_immediate.imm12 = narrowed; | |
| 99 | // } else { | |
| 100 | // const offset: u12 = blk: { | |
| 101 | // if (inst.load_store_register.size == 0) { | |
| 102 | // if (inst.load_store_register.v == 1) { | |
| 103 | // // 128-bit SIMD is scaled by 16. | |
| 104 | // break :blk try math.divExact(u12, narrowed, 16); | |
| 105 | // } | |
| 106 | // // Otherwise, 8-bit SIMD or ldrb. | |
| 107 | // break :blk narrowed; | |
| 108 | // } else { | |
| 109 | // const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size); | |
| 110 | // break :blk try math.divExact(u12, narrowed, denom); | |
| 111 | // } | |
| 112 | // }; | |
| 113 | // inst.load_store_register.offset = offset; | |
| 114 | // } | |
| 115 | ||
| 116 | // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 117 | // } | |
| 118 | ||
| 119 | pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 120 | _ = fmt; | |
| 121 | _ = options; | |
| 122 | if (self.addend) |addend| { | |
| 123 | try std.fmt.format(writer, ".addend = {}, ", .{addend}); | |
| 96 | 124 | } |
| 97 | ||
| 98 | mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 125 | try std.fmt.format(writer, ".op_kind = {s}, ", .{self.op_kind}); | |
| 99 | 126 | } |
| 100 | 127 | }; |
| 101 | 128 | |
| 102 | 129 | pub const GotPage = struct { |
| 103 | 130 | base: Relocation, |
| 104 | 131 | /// Always .PCRelativeAddress |
| 105 | inst: aarch64.Instruction, | |
| 132 | // inst: aarch64.Instruction, | |
| 106 | 133 | |
| 107 | 134 | pub const base_type: Relocation.Type = .got_page; |
| 108 | 135 | |
| 109 | pub fn resolve(page: GotPage, args: Relocation.ResolveArgs) !void { | |
| 110 | const source_page = @intCast(i32, args.source_addr >> 12); | |
| 111 | const target_page = @intCast(i32, args.target_addr >> 12); | |
| 112 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 136 | // pub fn resolve(page: GotPage, args: Relocation.ResolveArgs) !void { | |
| 137 | // const source_page = @intCast(i32, args.source_addr >> 12); | |
| 138 | // const target_page = @intCast(i32, args.target_addr >> 12); | |
| 139 | // const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 113 | 140 | |
| 114 | log.debug(" | moving by {} pages", .{pages}); | |
| 141 | // log.debug(" | moving by {} pages", .{pages}); | |
| 115 | 142 | |
| 116 | var inst = page.inst; | |
| 117 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 118 | inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 143 | // var inst = page.inst; | |
| 144 | // inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 145 | // inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 119 | 146 | |
| 120 | mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | |
| 147 | // mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | |
| 148 | // } | |
| 149 | ||
| 150 | pub fn format(self: GotPage, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 151 | _ = self; | |
| 152 | _ = fmt; | |
| 153 | _ = options; | |
| 154 | _ = writer; | |
| 121 | 155 | } |
| 122 | 156 | }; |
| 123 | 157 | |
| 124 | 158 | pub const GotPageOff = struct { |
| 125 | 159 | base: Relocation, |
| 126 | 160 | /// Always .LoadStoreRegister with size = 3 for GOT indirection |
| 127 | inst: aarch64.Instruction, | |
| 161 | // inst: aarch64.Instruction, | |
| 128 | 162 | |
| 129 | 163 | pub const base_type: Relocation.Type = .got_page_off; |
| 130 | 164 | |
| 131 | pub fn resolve(page_off: GotPageOff, args: Relocation.ResolveArgs) !void { | |
| 132 | const narrowed = @truncate(u12, args.target_addr); | |
| 165 | // pub fn resolve(page_off: GotPageOff, args: Relocation.ResolveArgs) !void { | |
| 166 | // const narrowed = @truncate(u12, args.target_addr); | |
| 167 | ||
| 168 | // log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 133 | 169 | |
| 134 | log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 170 | // var inst = page_off.inst; | |
| 171 | // const offset = try math.divExact(u12, narrowed, 8); | |
| 172 | // inst.load_store_register.offset = offset; | |
| 135 | 173 | |
| 136 | var inst = page_off.inst; | |
| 137 | const offset = try math.divExact(u12, narrowed, 8); | |
| 138 | inst.load_store_register.offset = offset; | |
| 174 | // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 175 | // } | |
| 139 | 176 | |
| 140 | mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 177 | pub fn format(self: GotPageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 178 | _ = self; | |
| 179 | _ = fmt; | |
| 180 | _ = options; | |
| 181 | _ = writer; | |
| 141 | 182 | } |
| 142 | 183 | }; |
| 143 | 184 | |
| ... | ... | @@ -146,34 +187,48 @@ pub const PointerToGot = struct { |
| 146 | 187 | |
| 147 | 188 | pub const base_type: Relocation.Type = .pointer_to_got; |
| 148 | 189 | |
| 149 | pub fn resolve(ptr_to_got: PointerToGot, args: Relocation.ResolveArgs) !void { | |
| 150 | const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr)); | |
| 190 | // pub fn resolve(ptr_to_got: PointerToGot, args: Relocation.ResolveArgs) !void { | |
| 191 | // const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr)); | |
| 192 | ||
| 193 | // log.debug(" | calculated value 0x{x}", .{result}); | |
| 151 | 194 | |
| 152 | log.debug(" | calculated value 0x{x}", .{result}); | |
| 195 | // mem.writeIntLittle(u32, ptr_to_got.base.code[0..4], @bitCast(u32, result)); | |
| 196 | // } | |
| 153 | 197 | |
| 154 | mem.writeIntLittle(u32, ptr_to_got.base.code[0..4], @bitCast(u32, result)); | |
| 198 | pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 199 | _ = self; | |
| 200 | _ = fmt; | |
| 201 | _ = options; | |
| 202 | _ = writer; | |
| 155 | 203 | } |
| 156 | 204 | }; |
| 157 | 205 | |
| 158 | 206 | pub const TlvpPage = struct { |
| 159 | 207 | base: Relocation, |
| 160 | 208 | /// Always .PCRelativeAddress |
| 161 | inst: aarch64.Instruction, | |
| 209 | // inst: aarch64.Instruction, | |
| 162 | 210 | |
| 163 | 211 | pub const base_type: Relocation.Type = .tlvp_page; |
| 164 | 212 | |
| 165 | pub fn resolve(page: TlvpPage, args: Relocation.ResolveArgs) !void { | |
| 166 | const source_page = @intCast(i32, args.source_addr >> 12); | |
| 167 | const target_page = @intCast(i32, args.target_addr >> 12); | |
| 168 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 213 | // pub fn resolve(page: TlvpPage, args: Relocation.ResolveArgs) !void { | |
| 214 | // const source_page = @intCast(i32, args.source_addr >> 12); | |
| 215 | // const target_page = @intCast(i32, args.target_addr >> 12); | |
| 216 | // const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); | |
| 169 | 217 | |
| 170 | log.debug(" | moving by {} pages", .{pages}); | |
| 218 | // log.debug(" | moving by {} pages", .{pages}); | |
| 171 | 219 | |
| 172 | var inst = page.inst; | |
| 173 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 174 | inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 220 | // var inst = page.inst; | |
| 221 | // inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | |
| 222 | // inst.pc_relative_address.immlo = @truncate(u2, pages); | |
| 175 | 223 | |
| 176 | mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | |
| 224 | // mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32()); | |
| 225 | // } | |
| 226 | ||
| 227 | pub fn format(self: TlvpPage, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 228 | _ = self; | |
| 229 | _ = fmt; | |
| 230 | _ = options; | |
| 231 | _ = writer; | |
| 177 | 232 | } |
| 178 | 233 | }; |
| 179 | 234 | |
| ... | ... | @@ -182,82 +237,110 @@ pub const TlvpPageOff = struct { |
| 182 | 237 | /// Always .AddSubtractImmediate regardless of the source instruction. |
| 183 | 238 | /// This means, we always rewrite the instruction to add even if the |
| 184 | 239 | /// source instruction was an ldr. |
| 185 | inst: aarch64.Instruction, | |
| 240 | // inst: aarch64.Instruction, | |
| 186 | 241 | |
| 187 | 242 | pub const base_type: Relocation.Type = .tlvp_page_off; |
| 188 | 243 | |
| 189 | pub fn resolve(page_off: TlvpPageOff, args: Relocation.ResolveArgs) !void { | |
| 190 | const narrowed = @truncate(u12, args.target_addr); | |
| 244 | // pub fn resolve(page_off: TlvpPageOff, args: Relocation.ResolveArgs) !void { | |
| 245 | // const narrowed = @truncate(u12, args.target_addr); | |
| 246 | ||
| 247 | // log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 191 | 248 | |
| 192 | log.debug(" | narrowed address within the page 0x{x}", .{narrowed}); | |
| 249 | // var inst = page_off.inst; | |
| 250 | // inst.add_subtract_immediate.imm12 = narrowed; | |
| 193 | 251 | |
| 194 | var inst = page_off.inst; | |
| 195 | inst.add_subtract_immediate.imm12 = narrowed; | |
| 252 | // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 253 | // } | |
| 196 | 254 | |
| 197 | mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32()); | |
| 255 | pub fn format(self: TlvpPageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 256 | _ = self; | |
| 257 | _ = fmt; | |
| 258 | _ = options; | |
| 259 | _ = writer; | |
| 198 | 260 | } |
| 199 | 261 | }; |
| 200 | 262 | |
| 201 | 263 | pub const Parser = struct { |
| 202 | allocator: *Allocator, | |
| 264 | object: *Object, | |
| 265 | zld: *Zld, | |
| 203 | 266 | it: *reloc.RelocIterator, |
| 204 | code: []u8, | |
| 205 | parsed: std.ArrayList(*Relocation), | |
| 267 | block: *TextBlock, | |
| 268 | base_addr: u64, | |
| 206 | 269 | addend: ?u32 = null, |
| 207 | subtractor: ?Relocation.Target = null, | |
| 270 | subtractor: ?*Symbol = null, | |
| 208 | 271 | |
| 209 | pub fn deinit(parser: *Parser) void { | |
| 210 | parser.parsed.deinit(); | |
| 211 | } | |
| 212 | ||
| 213 | pub fn parse(parser: *Parser) !void { | |
| 214 | while (parser.it.next()) |rel| { | |
| 215 | switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { | |
| 216 | .ARM64_RELOC_BRANCH26 => { | |
| 217 | try parser.parseBranch(rel); | |
| 218 | }, | |
| 272 | pub fn parse(self: *Parser) !void { | |
| 273 | while (self.it.next()) |rel| { | |
| 274 | const out_rel = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { | |
| 275 | .ARM64_RELOC_BRANCH26 => try self.parseBranch(rel), | |
| 219 | 276 | .ARM64_RELOC_SUBTRACTOR => { |
| 220 | try parser.parseSubtractor(rel); | |
| 221 | }, | |
| 222 | .ARM64_RELOC_UNSIGNED => { | |
| 223 | try parser.parseUnsigned(rel); | |
| 277 | // Subtractor is not a relocation with effect on the TextBlock, so | |
| 278 | // parse it and carry on. | |
| 279 | try self.parseSubtractor(rel); | |
| 280 | continue; | |
| 224 | 281 | }, |
| 282 | .ARM64_RELOC_UNSIGNED => try self.parseUnsigned(rel), | |
| 225 | 283 | .ARM64_RELOC_ADDEND => { |
| 226 | try parser.parseAddend(rel); | |
| 284 | // Addend is not a relocation with effect on the TextBlock, so | |
| 285 | // parse it and carry on. | |
| 286 | try self.parseAddend(rel); | |
| 287 | continue; | |
| 227 | 288 | }, |
| 228 | 289 | .ARM64_RELOC_PAGE21, |
| 229 | 290 | .ARM64_RELOC_GOT_LOAD_PAGE21, |
| 230 | 291 | .ARM64_RELOC_TLVP_LOAD_PAGE21, |
| 231 | => { | |
| 232 | try parser.parsePage(rel); | |
| 233 | }, | |
| 234 | .ARM64_RELOC_PAGEOFF12 => { | |
| 235 | try parser.parsePageOff(rel); | |
| 236 | }, | |
| 237 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => { | |
| 238 | try parser.parseGotLoadPageOff(rel); | |
| 239 | }, | |
| 240 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => { | |
| 241 | try parser.parseTlvpLoadPageOff(rel); | |
| 292 | => try self.parsePage(rel), | |
| 293 | .ARM64_RELOC_PAGEOFF12 => try self.parsePageOff(rel), | |
| 294 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => try self.parseGotLoadPageOff(rel), | |
| 295 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => try self.parseTlvpLoadPageOff(rel), | |
| 296 | .ARM64_RELOC_POINTER_TO_GOT => try self.parsePointerToGot(rel), | |
| 297 | }; | |
| 298 | try self.block.relocs.append(out_rel); | |
| 299 | ||
| 300 | if (out_rel.target.payload == .regular) { | |
| 301 | try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {}); | |
| 302 | } | |
| 303 | ||
| 304 | switch (out_rel.@"type") { | |
| 305 | .got_page, .got_page_off, .pointer_to_got => { | |
| 306 | const sym = out_rel.target; | |
| 307 | ||
| 308 | if (sym.got_index != null) continue; | |
| 309 | ||
| 310 | const index = @intCast(u32, self.zld.got_entries.items.len); | |
| 311 | sym.got_index = index; | |
| 312 | try self.zld.got_entries.append(self.zld.allocator, sym); | |
| 313 | ||
| 314 | log.debug("adding GOT entry for symbol {s} at index {}", .{ sym.name, index }); | |
| 242 | 315 | }, |
| 243 | .ARM64_RELOC_POINTER_TO_GOT => { | |
| 244 | try parser.parsePointerToGot(rel); | |
| 316 | .branch_aarch64 => { | |
| 317 | const sym = out_rel.target; | |
| 318 | ||
| 319 | if (sym.stubs_index != null) continue; | |
| 320 | if (sym.payload != .proxy) continue; | |
| 321 | ||
| 322 | const index = @intCast(u32, self.zld.stubs.items.len); | |
| 323 | sym.stubs_index = index; | |
| 324 | try self.zld.stubs.append(self.zld.allocator, sym); | |
| 325 | ||
| 326 | log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index }); | |
| 245 | 327 | }, |
| 328 | else => {}, | |
| 246 | 329 | } |
| 247 | 330 | } |
| 248 | 331 | } |
| 249 | 332 | |
| 250 | fn parseAddend(parser: *Parser, rel: macho.relocation_info) !void { | |
| 333 | fn parseAddend(self: *Parser, rel: macho.relocation_info) !void { | |
| 251 | 334 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); |
| 252 | 335 | assert(rel_type == .ARM64_RELOC_ADDEND); |
| 253 | 336 | assert(rel.r_pcrel == 0); |
| 254 | 337 | assert(rel.r_extern == 0); |
| 255 | assert(parser.addend == null); | |
| 338 | assert(self.addend == null); | |
| 256 | 339 | |
| 257 | parser.addend = rel.r_symbolnum; | |
| 340 | self.addend = rel.r_symbolnum; | |
| 258 | 341 | |
| 259 | 342 | // Verify ADDEND is followed by a load. |
| 260 | const next = @intToEnum(macho.reloc_type_arm64, parser.it.peek().r_type); | |
| 343 | const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type); | |
| 261 | 344 | switch (next) { |
| 262 | 345 | .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {}, |
| 263 | 346 | else => { |
| ... | ... | @@ -267,127 +350,101 @@ pub const Parser = struct { |
| 267 | 350 | } |
| 268 | 351 | } |
| 269 | 352 | |
| 270 | fn parseBranch(parser: *Parser, rel: macho.relocation_info) !void { | |
| 353 | fn parseBranch(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 271 | 354 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); |
| 272 | 355 | assert(rel_type == .ARM64_RELOC_BRANCH26); |
| 273 | 356 | assert(rel.r_pcrel == 1); |
| 274 | 357 | assert(rel.r_length == 2); |
| 275 | 358 | |
| 276 | const offset = @intCast(u32, rel.r_address); | |
| 277 | const inst = parser.code[offset..][0..4]; | |
| 278 | const parsed_inst = aarch64.Instruction{ .unconditional_branch_immediate = mem.bytesToValue( | |
| 279 | meta.TagPayload( | |
| 280 | aarch64.Instruction, | |
| 281 | aarch64.Instruction.unconditional_branch_immediate, | |
| 282 | ), | |
| 283 | inst, | |
| 284 | ) }; | |
| 285 | ||
| 286 | var branch = try parser.allocator.create(Branch); | |
| 287 | errdefer parser.allocator.destroy(branch); | |
| 359 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 360 | const target = try self.object.symbolFromReloc(rel); | |
| 288 | 361 | |
| 289 | const target = Relocation.Target.fromReloc(rel); | |
| 362 | var branch = try self.object.allocator.create(Branch); | |
| 363 | errdefer self.object.allocator.destroy(branch); | |
| 290 | 364 | |
| 291 | 365 | branch.* = .{ |
| 292 | 366 | .base = .{ |
| 293 | 367 | .@"type" = .branch_aarch64, |
| 294 | .code = inst, | |
| 295 | 368 | .offset = offset, |
| 296 | 369 | .target = target, |
| 370 | .block = self.block, | |
| 297 | 371 | }, |
| 298 | .inst = parsed_inst, | |
| 299 | 372 | }; |
| 300 | 373 | |
| 301 | log.debug(" | emitting {}", .{branch}); | |
| 302 | try parser.parsed.append(&branch.base); | |
| 374 | return &branch.base; | |
| 303 | 375 | } |
| 304 | 376 | |
| 305 | fn parsePage(parser: *Parser, rel: macho.relocation_info) !void { | |
| 377 | fn parsePage(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 306 | 378 | assert(rel.r_pcrel == 1); |
| 307 | 379 | assert(rel.r_length == 2); |
| 308 | 380 | |
| 309 | 381 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); |
| 310 | const target = Relocation.Target.fromReloc(rel); | |
| 311 | ||
| 312 | const offset = @intCast(u32, rel.r_address); | |
| 313 | const inst = parser.code[offset..][0..4]; | |
| 314 | const parsed_inst = aarch64.Instruction{ .pc_relative_address = mem.bytesToValue(meta.TagPayload( | |
| 315 | aarch64.Instruction, | |
| 316 | aarch64.Instruction.pc_relative_address, | |
| 317 | ), inst) }; | |
| 382 | const target = try self.object.symbolFromReloc(rel); | |
| 383 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 318 | 384 | |
| 319 | 385 | const ptr: *Relocation = ptr: { |
| 320 | 386 | switch (rel_type) { |
| 321 | 387 | .ARM64_RELOC_PAGE21 => { |
| 322 | 388 | defer { |
| 323 | 389 | // Reset parser's addend state |
| 324 | parser.addend = null; | |
| 390 | self.addend = null; | |
| 325 | 391 | } |
| 326 | var page = try parser.allocator.create(Page); | |
| 327 | errdefer parser.allocator.destroy(page); | |
| 392 | var page = try self.object.allocator.create(Page); | |
| 393 | errdefer self.object.allocator.destroy(page); | |
| 328 | 394 | |
| 329 | 395 | page.* = .{ |
| 330 | 396 | .base = .{ |
| 331 | 397 | .@"type" = .page, |
| 332 | .code = inst, | |
| 333 | 398 | .offset = offset, |
| 334 | 399 | .target = target, |
| 400 | .block = self.block, | |
| 335 | 401 | }, |
| 336 | .addend = parser.addend, | |
| 337 | .inst = parsed_inst, | |
| 402 | .addend = self.addend, | |
| 338 | 403 | }; |
| 339 | 404 | |
| 340 | log.debug(" | emitting {}", .{page}); | |
| 341 | ||
| 342 | 405 | break :ptr &page.base; |
| 343 | 406 | }, |
| 344 | 407 | .ARM64_RELOC_GOT_LOAD_PAGE21 => { |
| 345 | var page = try parser.allocator.create(GotPage); | |
| 346 | errdefer parser.allocator.destroy(page); | |
| 408 | var page = try self.object.allocator.create(GotPage); | |
| 409 | errdefer self.object.allocator.destroy(page); | |
| 347 | 410 | |
| 348 | 411 | page.* = .{ |
| 349 | 412 | .base = .{ |
| 350 | 413 | .@"type" = .got_page, |
| 351 | .code = inst, | |
| 352 | 414 | .offset = offset, |
| 353 | 415 | .target = target, |
| 416 | .block = self.block, | |
| 354 | 417 | }, |
| 355 | .inst = parsed_inst, | |
| 356 | 418 | }; |
| 357 | 419 | |
| 358 | log.debug(" | emitting {}", .{page}); | |
| 359 | ||
| 360 | 420 | break :ptr &page.base; |
| 361 | 421 | }, |
| 362 | 422 | .ARM64_RELOC_TLVP_LOAD_PAGE21 => { |
| 363 | var page = try parser.allocator.create(TlvpPage); | |
| 364 | errdefer parser.allocator.destroy(page); | |
| 423 | var page = try self.object.allocator.create(TlvpPage); | |
| 424 | errdefer self.object.allocator.destroy(page); | |
| 365 | 425 | |
| 366 | 426 | page.* = .{ |
| 367 | 427 | .base = .{ |
| 368 | 428 | .@"type" = .tlvp_page, |
| 369 | .code = inst, | |
| 370 | 429 | .offset = offset, |
| 371 | 430 | .target = target, |
| 431 | .block = self.block, | |
| 372 | 432 | }, |
| 373 | .inst = parsed_inst, | |
| 374 | 433 | }; |
| 375 | 434 | |
| 376 | log.debug(" | emitting {}", .{page}); | |
| 377 | ||
| 378 | 435 | break :ptr &page.base; |
| 379 | 436 | }, |
| 380 | 437 | else => unreachable, |
| 381 | 438 | } |
| 382 | 439 | }; |
| 383 | 440 | |
| 384 | try parser.parsed.append(ptr); | |
| 441 | return ptr; | |
| 385 | 442 | } |
| 386 | 443 | |
| 387 | fn parsePageOff(parser: *Parser, rel: macho.relocation_info) !void { | |
| 444 | fn parsePageOff(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 388 | 445 | defer { |
| 389 | 446 | // Reset parser's addend state |
| 390 | parser.addend = null; | |
| 447 | self.addend = null; | |
| 391 | 448 | } |
| 392 | 449 | |
| 393 | 450 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); |
| ... | ... | @@ -395,83 +452,56 @@ pub const Parser = struct { |
| 395 | 452 | assert(rel.r_pcrel == 0); |
| 396 | 453 | assert(rel.r_length == 2); |
| 397 | 454 | |
| 398 | const offset = @intCast(u32, rel.r_address); | |
| 399 | const inst = parser.code[offset..][0..4]; | |
| 400 | ||
| 401 | var op_kind: PageOff.OpKind = undefined; | |
| 402 | var parsed_inst: aarch64.Instruction = undefined; | |
| 403 | if (isArithmeticOp(inst)) { | |
| 404 | op_kind = .arithmetic; | |
| 405 | parsed_inst = .{ .add_subtract_immediate = mem.bytesToValue(meta.TagPayload( | |
| 406 | aarch64.Instruction, | |
| 407 | aarch64.Instruction.add_subtract_immediate, | |
| 408 | ), inst) }; | |
| 409 | } else { | |
| 410 | op_kind = .load_store; | |
| 411 | parsed_inst = .{ .load_store_register = mem.bytesToValue(meta.TagPayload( | |
| 412 | aarch64.Instruction, | |
| 413 | aarch64.Instruction.load_store_register, | |
| 414 | ), inst) }; | |
| 415 | } | |
| 416 | const target = Relocation.Target.fromReloc(rel); | |
| 455 | const target = try self.object.symbolFromReloc(rel); | |
| 456 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 457 | const op_kind: PageOff.OpKind = if (isArithmeticOp(self.block.code[offset..][0..4])) | |
| 458 | .arithmetic | |
| 459 | else | |
| 460 | .load_store; | |
| 417 | 461 | |
| 418 | var page_off = try parser.allocator.create(PageOff); | |
| 419 | errdefer parser.allocator.destroy(page_off); | |
| 462 | var page_off = try self.object.allocator.create(PageOff); | |
| 463 | errdefer self.object.allocator.destroy(page_off); | |
| 420 | 464 | |
| 421 | 465 | page_off.* = .{ |
| 422 | 466 | .base = .{ |
| 423 | 467 | .@"type" = .page_off, |
| 424 | .code = inst, | |
| 425 | 468 | .offset = offset, |
| 426 | 469 | .target = target, |
| 470 | .block = self.block, | |
| 427 | 471 | }, |
| 428 | 472 | .op_kind = op_kind, |
| 429 | .inst = parsed_inst, | |
| 430 | .addend = parser.addend, | |
| 473 | .addend = self.addend, | |
| 431 | 474 | }; |
| 432 | 475 | |
| 433 | log.debug(" | emitting {}", .{page_off}); | |
| 434 | try parser.parsed.append(&page_off.base); | |
| 476 | return &page_off.base; | |
| 435 | 477 | } |
| 436 | 478 | |
| 437 | fn parseGotLoadPageOff(parser: *Parser, rel: macho.relocation_info) !void { | |
| 479 | fn parseGotLoadPageOff(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 438 | 480 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); |
| 439 | 481 | assert(rel_type == .ARM64_RELOC_GOT_LOAD_PAGEOFF12); |
| 440 | 482 | assert(rel.r_pcrel == 0); |
| 441 | 483 | assert(rel.r_length == 2); |
| 442 | 484 | |
| 443 | const offset = @intCast(u32, rel.r_address); | |
| 444 | const inst = parser.code[offset..][0..4]; | |
| 445 | assert(!isArithmeticOp(inst)); | |
| 446 | ||
| 447 | const parsed_inst = mem.bytesToValue(meta.TagPayload( | |
| 448 | aarch64.Instruction, | |
| 449 | aarch64.Instruction.load_store_register, | |
| 450 | ), inst); | |
| 451 | assert(parsed_inst.size == 3); | |
| 452 | ||
| 453 | const target = Relocation.Target.fromReloc(rel); | |
| 485 | const target = try self.object.symbolFromReloc(rel); | |
| 486 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 487 | assert(!isArithmeticOp(self.block.code[offset..][0..4])); | |
| 454 | 488 | |
| 455 | var page_off = try parser.allocator.create(GotPageOff); | |
| 456 | errdefer parser.allocator.destroy(page_off); | |
| 489 | var page_off = try self.object.allocator.create(GotPageOff); | |
| 490 | errdefer self.object.allocator.destroy(page_off); | |
| 457 | 491 | |
| 458 | 492 | page_off.* = .{ |
| 459 | 493 | .base = .{ |
| 460 | 494 | .@"type" = .got_page_off, |
| 461 | .code = inst, | |
| 462 | 495 | .offset = offset, |
| 463 | 496 | .target = target, |
| 464 | }, | |
| 465 | .inst = .{ | |
| 466 | .load_store_register = parsed_inst, | |
| 497 | .block = self.block, | |
| 467 | 498 | }, |
| 468 | 499 | }; |
| 469 | 500 | |
| 470 | log.debug(" | emitting {}", .{page_off}); | |
| 471 | try parser.parsed.append(&page_off.base); | |
| 501 | return &page_off.base; | |
| 472 | 502 | } |
| 473 | 503 | |
| 474 | fn parseTlvpLoadPageOff(parser: *Parser, rel: macho.relocation_info) !void { | |
| 504 | fn parseTlvpLoadPageOff(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 475 | 505 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); |
| 476 | 506 | assert(rel_type == .ARM64_RELOC_TLVP_LOAD_PAGEOFF12); |
| 477 | 507 | assert(rel.r_pcrel == 0); |
| ... | ... | @@ -483,141 +513,102 @@ pub const Parser = struct { |
| 483 | 513 | size: u1, |
| 484 | 514 | }; |
| 485 | 515 | |
| 486 | const offset = @intCast(u32, rel.r_address); | |
| 487 | const inst = parser.code[offset..][0..4]; | |
| 488 | const parsed: RegInfo = parsed: { | |
| 489 | if (isArithmeticOp(inst)) { | |
| 490 | const parsed_inst = mem.bytesAsValue(meta.TagPayload( | |
| 491 | aarch64.Instruction, | |
| 492 | aarch64.Instruction.add_subtract_immediate, | |
| 493 | ), inst); | |
| 494 | break :parsed .{ | |
| 495 | .rd = parsed_inst.rd, | |
| 496 | .rn = parsed_inst.rn, | |
| 497 | .size = parsed_inst.sf, | |
| 498 | }; | |
| 499 | } else { | |
| 500 | const parsed_inst = mem.bytesAsValue(meta.TagPayload( | |
| 501 | aarch64.Instruction, | |
| 502 | aarch64.Instruction.load_store_register, | |
| 503 | ), inst); | |
| 504 | break :parsed .{ | |
| 505 | .rd = parsed_inst.rt, | |
| 506 | .rn = parsed_inst.rn, | |
| 507 | .size = @truncate(u1, parsed_inst.size), | |
| 508 | }; | |
| 509 | } | |
| 510 | }; | |
| 516 | const target = try self.object.symbolFromReloc(rel); | |
| 517 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 511 | 518 | |
| 512 | const target = Relocation.Target.fromReloc(rel); | |
| 513 | ||
| 514 | var page_off = try parser.allocator.create(TlvpPageOff); | |
| 515 | errdefer parser.allocator.destroy(page_off); | |
| 519 | var page_off = try self.object.allocator.create(TlvpPageOff); | |
| 520 | errdefer self.object.allocator.destroy(page_off); | |
| 516 | 521 | |
| 517 | 522 | page_off.* = .{ |
| 518 | 523 | .base = .{ |
| 519 | 524 | .@"type" = .tlvp_page_off, |
| 520 | .code = inst, | |
| 521 | 525 | .offset = offset, |
| 522 | 526 | .target = target, |
| 523 | }, | |
| 524 | .inst = .{ | |
| 525 | .add_subtract_immediate = .{ | |
| 526 | .rd = parsed.rd, | |
| 527 | .rn = parsed.rn, | |
| 528 | .imm12 = 0, // This will be filled when target addresses are known. | |
| 529 | .sh = 0, | |
| 530 | .s = 0, | |
| 531 | .op = 0, | |
| 532 | .sf = parsed.size, | |
| 533 | }, | |
| 527 | .block = self.block, | |
| 534 | 528 | }, |
| 535 | 529 | }; |
| 536 | 530 | |
| 537 | log.debug(" | emitting {}", .{page_off}); | |
| 538 | try parser.parsed.append(&page_off.base); | |
| 531 | return &page_off.base; | |
| 539 | 532 | } |
| 540 | 533 | |
| 541 | fn parseSubtractor(parser: *Parser, rel: macho.relocation_info) !void { | |
| 534 | fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void { | |
| 542 | 535 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); |
| 543 | 536 | assert(rel_type == .ARM64_RELOC_SUBTRACTOR); |
| 544 | 537 | assert(rel.r_pcrel == 0); |
| 545 | assert(parser.subtractor == null); | |
| 538 | assert(self.subtractor == null); | |
| 546 | 539 | |
| 547 | parser.subtractor = Relocation.Target.fromReloc(rel); | |
| 540 | self.subtractor = try self.object.symbolFromReloc(rel); | |
| 548 | 541 | |
| 549 | 542 | // Verify SUBTRACTOR is followed by UNSIGNED. |
| 550 | const next = @intToEnum(macho.reloc_type_arm64, parser.it.peek().r_type); | |
| 543 | const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type); | |
| 551 | 544 | if (next != .ARM64_RELOC_UNSIGNED) { |
| 552 | 545 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); |
| 553 | 546 | return error.UnexpectedRelocationType; |
| 554 | 547 | } |
| 555 | 548 | } |
| 556 | 549 | |
| 557 | fn parseUnsigned(parser: *Parser, rel: macho.relocation_info) !void { | |
| 550 | fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 558 | 551 | defer { |
| 559 | 552 | // Reset parser's subtractor state |
| 560 | parser.subtractor = null; | |
| 553 | self.subtractor = null; | |
| 561 | 554 | } |
| 562 | 555 | |
| 563 | 556 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); |
| 564 | 557 | assert(rel_type == .ARM64_RELOC_UNSIGNED); |
| 565 | 558 | assert(rel.r_pcrel == 0); |
| 566 | 559 | |
| 567 | var unsigned = try parser.allocator.create(reloc.Unsigned); | |
| 568 | errdefer parser.allocator.destroy(unsigned); | |
| 569 | ||
| 570 | const target = Relocation.Target.fromReloc(rel); | |
| 560 | const target = try self.object.symbolFromReloc(rel); | |
| 561 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 571 | 562 | const is_64bit: bool = switch (rel.r_length) { |
| 572 | 563 | 3 => true, |
| 573 | 564 | 2 => false, |
| 574 | 565 | else => unreachable, |
| 575 | 566 | }; |
| 576 | const offset = @intCast(u32, rel.r_address); | |
| 577 | 567 | const addend: i64 = if (is_64bit) |
| 578 | mem.readIntLittle(i64, parser.code[offset..][0..8]) | |
| 568 | mem.readIntLittle(i64, self.block.code[offset..][0..8]) | |
| 579 | 569 | else |
| 580 | mem.readIntLittle(i32, parser.code[offset..][0..4]); | |
| 570 | mem.readIntLittle(i32, self.block.code[offset..][0..4]); | |
| 571 | ||
| 572 | var unsigned = try self.object.allocator.create(reloc.Unsigned); | |
| 573 | errdefer self.object.allocator.destroy(unsigned); | |
| 581 | 574 | |
| 582 | 575 | unsigned.* = .{ |
| 583 | 576 | .base = .{ |
| 584 | 577 | .@"type" = .unsigned, |
| 585 | .code = if (is_64bit) parser.code[offset..][0..8] else parser.code[offset..][0..4], | |
| 586 | 578 | .offset = offset, |
| 587 | 579 | .target = target, |
| 580 | .block = self.block, | |
| 588 | 581 | }, |
| 589 | .subtractor = parser.subtractor, | |
| 582 | .subtractor = self.subtractor, | |
| 590 | 583 | .is_64bit = is_64bit, |
| 591 | 584 | .addend = addend, |
| 592 | 585 | }; |
| 593 | 586 | |
| 594 | log.debug(" | emitting {}", .{unsigned}); | |
| 595 | try parser.parsed.append(&unsigned.base); | |
| 587 | return &unsigned.base; | |
| 596 | 588 | } |
| 597 | 589 | |
| 598 | fn parsePointerToGot(parser: *Parser, rel: macho.relocation_info) !void { | |
| 590 | fn parsePointerToGot(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 599 | 591 | const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); |
| 600 | 592 | assert(rel_type == .ARM64_RELOC_POINTER_TO_GOT); |
| 601 | 593 | assert(rel.r_pcrel == 1); |
| 602 | 594 | assert(rel.r_length == 2); |
| 603 | 595 | |
| 604 | var ptr_to_got = try parser.allocator.create(PointerToGot); | |
| 605 | errdefer parser.allocator.destroy(ptr_to_got); | |
| 596 | var ptr_to_got = try self.object.allocator.create(PointerToGot); | |
| 597 | errdefer self.object.allocator.destroy(ptr_to_got); | |
| 606 | 598 | |
| 607 | const target = Relocation.Target.fromReloc(rel); | |
| 608 | const offset = @intCast(u32, rel.r_address); | |
| 599 | const target = try self.object.symbolFromReloc(rel); | |
| 600 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 609 | 601 | |
| 610 | 602 | ptr_to_got.* = .{ |
| 611 | 603 | .base = .{ |
| 612 | 604 | .@"type" = .pointer_to_got, |
| 613 | .code = parser.code[offset..][0..4], | |
| 614 | 605 | .offset = offset, |
| 615 | 606 | .target = target, |
| 607 | .block = self.block, | |
| 616 | 608 | }, |
| 617 | 609 | }; |
| 618 | 610 | |
| 619 | log.debug(" | emitting {}", .{ptr_to_got}); | |
| 620 | try parser.parsed.append(&ptr_to_got.base); | |
| 611 | return &ptr_to_got.base; | |
| 621 | 612 | } |
| 622 | 613 | }; |
| 623 | 614 |
src/link/MachO/reloc/x86_64.zig+182-140| ... | ... | @@ -8,17 +8,28 @@ const meta = std.meta; |
| 8 | 8 | const reloc = @import("../reloc.zig"); |
| 9 | 9 | |
| 10 | 10 | const Allocator = mem.Allocator; |
| 11 | const Object = @import("../Object.zig"); | |
| 11 | 12 | const Relocation = reloc.Relocation; |
| 13 | const Symbol = @import("../Symbol.zig"); | |
| 14 | const TextBlock = Zld.TextBlock; | |
| 15 | const Zld = @import("../Zld.zig"); | |
| 12 | 16 | |
| 13 | 17 | pub const Branch = struct { |
| 14 | 18 | base: Relocation, |
| 15 | 19 | |
| 16 | 20 | pub const base_type: Relocation.Type = .branch_x86_64; |
| 17 | 21 | |
| 18 | pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void { | |
| 19 | const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | |
| 20 | log.debug(" | displacement 0x{x}", .{displacement}); | |
| 21 | mem.writeIntLittle(u32, branch.base.code[0..4], @bitCast(u32, displacement)); | |
| 22 | // pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void { | |
| 23 | // const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | |
| 24 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 25 | // mem.writeIntLittle(u32, branch.base.code[0..4], @bitCast(u32, displacement)); | |
| 26 | // } | |
| 27 | ||
| 28 | pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 29 | _ = self; | |
| 30 | _ = fmt; | |
| 31 | _ = options; | |
| 32 | _ = writer; | |
| 22 | 33 | } |
| 23 | 34 | }; |
| 24 | 35 | |
| ... | ... | @@ -29,25 +40,32 @@ pub const Signed = struct { |
| 29 | 40 | |
| 30 | 41 | pub const base_type: Relocation.Type = .signed; |
| 31 | 42 | |
| 32 | pub fn resolve(signed: Signed, args: Relocation.ResolveArgs) !void { | |
| 33 | const target_addr = target_addr: { | |
| 34 | if (signed.base.target == .section) { | |
| 35 | const source_target = @intCast(i64, args.source_source_sect_addr.?) + @intCast(i64, signed.base.offset) + signed.addend + 4; | |
| 36 | const source_disp = source_target - @intCast(i64, args.source_target_sect_addr.?); | |
| 37 | break :target_addr @intCast(i64, args.target_addr) + source_disp; | |
| 38 | } | |
| 39 | break :target_addr @intCast(i64, args.target_addr) + signed.addend; | |
| 40 | }; | |
| 41 | const displacement = try math.cast( | |
| 42 | i32, | |
| 43 | target_addr - @intCast(i64, args.source_addr) - signed.correction - 4, | |
| 44 | ); | |
| 45 | ||
| 46 | log.debug(" | addend 0x{x}", .{signed.addend}); | |
| 47 | log.debug(" | correction 0x{x}", .{signed.correction}); | |
| 48 | log.debug(" | displacement 0x{x}", .{displacement}); | |
| 49 | ||
| 50 | mem.writeIntLittle(u32, signed.base.code[0..4], @bitCast(u32, displacement)); | |
| 43 | // pub fn resolve(signed: Signed, args: Relocation.ResolveArgs) !void { | |
| 44 | // const target_addr = target_addr: { | |
| 45 | // if (signed.base.target == .section) { | |
| 46 | // const source_target = @intCast(i64, args.source_source_sect_addr.?) + @intCast(i64, signed.base.offset) + signed.addend + 4; | |
| 47 | // const source_disp = source_target - @intCast(i64, args.source_target_sect_addr.?); | |
| 48 | // break :target_addr @intCast(i64, args.target_addr) + source_disp; | |
| 49 | // } | |
| 50 | // break :target_addr @intCast(i64, args.target_addr) + signed.addend; | |
| 51 | // }; | |
| 52 | // const displacement = try math.cast( | |
| 53 | // i32, | |
| 54 | // target_addr - @intCast(i64, args.source_addr) - signed.correction - 4, | |
| 55 | // ); | |
| 56 | ||
| 57 | // log.debug(" | addend 0x{x}", .{signed.addend}); | |
| 58 | // log.debug(" | correction 0x{x}", .{signed.correction}); | |
| 59 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 60 | ||
| 61 | // mem.writeIntLittle(u32, signed.base.code[0..4], @bitCast(u32, displacement)); | |
| 62 | // } | |
| 63 | ||
| 64 | pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 65 | _ = fmt; | |
| 66 | _ = options; | |
| 67 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 68 | try std.fmt.format(writer, ".correction = {}, ", .{self.correction}); | |
| 51 | 69 | } |
| 52 | 70 | }; |
| 53 | 71 | |
| ... | ... | @@ -56,10 +74,17 @@ pub const GotLoad = struct { |
| 56 | 74 | |
| 57 | 75 | pub const base_type: Relocation.Type = .got_load; |
| 58 | 76 | |
| 59 | pub fn resolve(got_load: GotLoad, args: Relocation.ResolveArgs) !void { | |
| 60 | const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | |
| 61 | log.debug(" | displacement 0x{x}", .{displacement}); | |
| 62 | mem.writeIntLittle(u32, got_load.base.code[0..4], @bitCast(u32, displacement)); | |
| 77 | // pub fn resolve(got_load: GotLoad, args: Relocation.ResolveArgs) !void { | |
| 78 | // const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | |
| 79 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 80 | // mem.writeIntLittle(u32, got_load.base.code[0..4], @bitCast(u32, displacement)); | |
| 81 | // } | |
| 82 | ||
| 83 | pub fn format(self: GotLoad, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 84 | _ = self; | |
| 85 | _ = fmt; | |
| 86 | _ = options; | |
| 87 | _ = writer; | |
| 63 | 88 | } |
| 64 | 89 | }; |
| 65 | 90 | |
| ... | ... | @@ -69,113 +94,139 @@ pub const Got = struct { |
| 69 | 94 | |
| 70 | 95 | pub const base_type: Relocation.Type = .got; |
| 71 | 96 | |
| 72 | pub fn resolve(got: Got, args: Relocation.ResolveArgs) !void { | |
| 73 | const displacement = try math.cast( | |
| 74 | i32, | |
| 75 | @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + got.addend, | |
| 76 | ); | |
| 77 | log.debug(" | displacement 0x{x}", .{displacement}); | |
| 78 | mem.writeIntLittle(u32, got.base.code[0..4], @bitCast(u32, displacement)); | |
| 97 | // pub fn resolve(got: Got, args: Relocation.ResolveArgs) !void { | |
| 98 | // const displacement = try math.cast( | |
| 99 | // i32, | |
| 100 | // @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + got.addend, | |
| 101 | // ); | |
| 102 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 103 | // mem.writeIntLittle(u32, got.base.code[0..4], @bitCast(u32, displacement)); | |
| 104 | // } | |
| 105 | ||
| 106 | pub fn format(self: Got, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 107 | _ = fmt; | |
| 108 | _ = options; | |
| 109 | try std.fmt.format(writer, ".addend = {}, ", .{self.addend}); | |
| 79 | 110 | } |
| 80 | 111 | }; |
| 81 | 112 | |
| 82 | 113 | pub const Tlv = struct { |
| 83 | 114 | base: Relocation, |
| 84 | op: *u8, | |
| 85 | 115 | |
| 86 | 116 | pub const base_type: Relocation.Type = .tlv; |
| 87 | 117 | |
| 88 | pub fn resolve(tlv: Tlv, args: Relocation.ResolveArgs) !void { | |
| 89 | // We need to rewrite the opcode from movq to leaq. | |
| 90 | tlv.op.* = 0x8d; | |
| 91 | log.debug(" | rewriting op to leaq", .{}); | |
| 92 | ||
| 93 | const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | |
| 94 | log.debug(" | displacement 0x{x}", .{displacement}); | |
| 95 | ||
| 96 | mem.writeIntLittle(u32, tlv.base.code[0..4], @bitCast(u32, displacement)); | |
| 118 | // pub fn resolve(tlv: Tlv, args: Relocation.ResolveArgs) !void { | |
| 119 | // // We need to rewrite the opcode from movq to leaq. | |
| 120 | // tlv.op.* = 0x8d; | |
| 121 | // log.debug(" | rewriting op to leaq", .{}); | |
| 122 | ||
| 123 | // const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4); | |
| 124 | // log.debug(" | displacement 0x{x}", .{displacement}); | |
| 125 | ||
| 126 | // mem.writeIntLittle(u32, tlv.base.code[0..4], @bitCast(u32, displacement)); | |
| 127 | // } | |
| 128 | pub fn format(self: Tlv, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 129 | _ = self; | |
| 130 | _ = fmt; | |
| 131 | _ = options; | |
| 132 | _ = writer; | |
| 97 | 133 | } |
| 98 | 134 | }; |
| 99 | 135 | |
| 100 | 136 | pub const Parser = struct { |
| 101 | allocator: *Allocator, | |
| 137 | object: *Object, | |
| 138 | zld: *Zld, | |
| 102 | 139 | it: *reloc.RelocIterator, |
| 103 | code: []u8, | |
| 104 | parsed: std.ArrayList(*Relocation), | |
| 105 | subtractor: ?Relocation.Target = null, | |
| 106 | ||
| 107 | pub fn deinit(parser: *Parser) void { | |
| 108 | parser.parsed.deinit(); | |
| 109 | } | |
| 110 | ||
| 111 | pub fn parse(parser: *Parser) !void { | |
| 112 | while (parser.it.next()) |rel| { | |
| 113 | switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { | |
| 114 | .X86_64_RELOC_BRANCH => { | |
| 115 | try parser.parseBranch(rel); | |
| 116 | }, | |
| 140 | block: *TextBlock, | |
| 141 | base_addr: u64, | |
| 142 | subtractor: ?*Symbol = null, | |
| 143 | ||
| 144 | pub fn parse(self: *Parser) !void { | |
| 145 | while (self.it.next()) |rel| { | |
| 146 | const out_rel = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { | |
| 147 | .X86_64_RELOC_BRANCH => try self.parseBranch(rel), | |
| 117 | 148 | .X86_64_RELOC_SUBTRACTOR => { |
| 118 | try parser.parseSubtractor(rel); | |
| 119 | }, | |
| 120 | .X86_64_RELOC_UNSIGNED => { | |
| 121 | try parser.parseUnsigned(rel); | |
| 149 | // Subtractor is not a relocation with effect on the TextBlock, so | |
| 150 | // parse it and carry on. | |
| 151 | try self.parseSubtractor(rel); | |
| 152 | continue; | |
| 122 | 153 | }, |
| 154 | .X86_64_RELOC_UNSIGNED => try self.parseUnsigned(rel), | |
| 123 | 155 | .X86_64_RELOC_SIGNED, |
| 124 | 156 | .X86_64_RELOC_SIGNED_1, |
| 125 | 157 | .X86_64_RELOC_SIGNED_2, |
| 126 | 158 | .X86_64_RELOC_SIGNED_4, |
| 127 | => { | |
| 128 | try parser.parseSigned(rel); | |
| 129 | }, | |
| 130 | .X86_64_RELOC_GOT_LOAD => { | |
| 131 | try parser.parseGotLoad(rel); | |
| 132 | }, | |
| 133 | .X86_64_RELOC_GOT => { | |
| 134 | try parser.parseGot(rel); | |
| 159 | => try self.parseSigned(rel), | |
| 160 | .X86_64_RELOC_GOT_LOAD => try self.parseGotLoad(rel), | |
| 161 | .X86_64_RELOC_GOT => try self.parseGot(rel), | |
| 162 | .X86_64_RELOC_TLV => try self.parseTlv(rel), | |
| 163 | }; | |
| 164 | try self.block.relocs.append(out_rel); | |
| 165 | ||
| 166 | if (out_rel.target.payload == .regular) { | |
| 167 | try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {}); | |
| 168 | } | |
| 169 | ||
| 170 | switch (out_rel.@"type") { | |
| 171 | .got_load, .got => { | |
| 172 | const sym = out_rel.target; | |
| 173 | ||
| 174 | if (sym.got_index != null) continue; | |
| 175 | ||
| 176 | const index = @intCast(u32, self.zld.got_entries.items.len); | |
| 177 | sym.got_index = index; | |
| 178 | try self.zld.got_entries.append(self.zld.allocator, sym); | |
| 179 | ||
| 180 | log.debug("adding GOT entry for symbol {s} at index {}", .{ sym.name, index }); | |
| 135 | 181 | }, |
| 136 | .X86_64_RELOC_TLV => { | |
| 137 | try parser.parseTlv(rel); | |
| 182 | .branch_x86_64 => { | |
| 183 | const sym = out_rel.target; | |
| 184 | ||
| 185 | if (sym.stubs_index != null) continue; | |
| 186 | if (sym.payload != .proxy) continue; | |
| 187 | ||
| 188 | const index = @intCast(u32, self.zld.stubs.items.len); | |
| 189 | sym.stubs_index = index; | |
| 190 | try self.zld.stubs.append(self.zld.allocator, sym); | |
| 191 | ||
| 192 | log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index }); | |
| 138 | 193 | }, |
| 194 | else => {}, | |
| 139 | 195 | } |
| 140 | 196 | } |
| 141 | 197 | } |
| 142 | 198 | |
| 143 | fn parseBranch(parser: *Parser, rel: macho.relocation_info) !void { | |
| 199 | fn parseBranch(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 144 | 200 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); |
| 145 | 201 | assert(rel_type == .X86_64_RELOC_BRANCH); |
| 146 | 202 | assert(rel.r_pcrel == 1); |
| 147 | 203 | assert(rel.r_length == 2); |
| 148 | 204 | |
| 149 | const offset = @intCast(u32, rel.r_address); | |
| 150 | const inst = parser.code[offset..][0..4]; | |
| 151 | ||
| 152 | var branch = try parser.allocator.create(Branch); | |
| 153 | errdefer parser.allocator.destroy(branch); | |
| 205 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 206 | const target = try self.object.symbolFromReloc(rel); | |
| 154 | 207 | |
| 155 | const target = Relocation.Target.fromReloc(rel); | |
| 208 | var branch = try self.object.allocator.create(Branch); | |
| 209 | errdefer self.object.allocator.destroy(branch); | |
| 156 | 210 | |
| 157 | 211 | branch.* = .{ |
| 158 | 212 | .base = .{ |
| 159 | 213 | .@"type" = .branch_x86_64, |
| 160 | .code = inst, | |
| 161 | 214 | .offset = offset, |
| 162 | 215 | .target = target, |
| 216 | .block = self.block, | |
| 163 | 217 | }, |
| 164 | 218 | }; |
| 165 | 219 | |
| 166 | log.debug(" | emitting {}", .{branch}); | |
| 167 | try parser.parsed.append(&branch.base); | |
| 220 | return &branch.base; | |
| 168 | 221 | } |
| 169 | 222 | |
| 170 | fn parseSigned(parser: *Parser, rel: macho.relocation_info) !void { | |
| 223 | fn parseSigned(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 171 | 224 | assert(rel.r_pcrel == 1); |
| 172 | 225 | assert(rel.r_length == 2); |
| 173 | 226 | |
| 174 | 227 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); |
| 175 | const target = Relocation.Target.fromReloc(rel); | |
| 176 | ||
| 177 | const offset = @intCast(u32, rel.r_address); | |
| 178 | const inst = parser.code[offset..][0..4]; | |
| 228 | const target = try self.object.symbolFromReloc(rel); | |
| 229 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 179 | 230 | const correction: i4 = switch (rel_type) { |
| 180 | 231 | .X86_64_RELOC_SIGNED => 0, |
| 181 | 232 | .X86_64_RELOC_SIGNED_1 => 1, |
| ... | ... | @@ -183,161 +234,152 @@ pub const Parser = struct { |
| 183 | 234 | .X86_64_RELOC_SIGNED_4 => 4, |
| 184 | 235 | else => unreachable, |
| 185 | 236 | }; |
| 186 | const addend = mem.readIntLittle(i32, inst) + correction; | |
| 237 | const addend = mem.readIntLittle(i32, self.block.code[offset..][0..4]) + correction; | |
| 187 | 238 | |
| 188 | var signed = try parser.allocator.create(Signed); | |
| 189 | errdefer parser.allocator.destroy(signed); | |
| 239 | var signed = try self.object.allocator.create(Signed); | |
| 240 | errdefer self.object.allocator.destroy(signed); | |
| 190 | 241 | |
| 191 | 242 | signed.* = .{ |
| 192 | 243 | .base = .{ |
| 193 | 244 | .@"type" = .signed, |
| 194 | .code = inst, | |
| 195 | 245 | .offset = offset, |
| 196 | 246 | .target = target, |
| 247 | .block = self.block, | |
| 197 | 248 | }, |
| 198 | 249 | .addend = addend, |
| 199 | 250 | .correction = correction, |
| 200 | 251 | }; |
| 201 | 252 | |
| 202 | log.debug(" | emitting {}", .{signed}); | |
| 203 | try parser.parsed.append(&signed.base); | |
| 253 | return &signed.base; | |
| 204 | 254 | } |
| 205 | 255 | |
| 206 | fn parseGotLoad(parser: *Parser, rel: macho.relocation_info) !void { | |
| 256 | fn parseGotLoad(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 207 | 257 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); |
| 208 | 258 | assert(rel_type == .X86_64_RELOC_GOT_LOAD); |
| 209 | 259 | assert(rel.r_pcrel == 1); |
| 210 | 260 | assert(rel.r_length == 2); |
| 211 | 261 | |
| 212 | const offset = @intCast(u32, rel.r_address); | |
| 213 | const inst = parser.code[offset..][0..4]; | |
| 214 | const target = Relocation.Target.fromReloc(rel); | |
| 262 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 263 | const target = try self.object.symbolFromReloc(rel); | |
| 215 | 264 | |
| 216 | var got_load = try parser.allocator.create(GotLoad); | |
| 217 | errdefer parser.allocator.destroy(got_load); | |
| 265 | var got_load = try self.object.allocator.create(GotLoad); | |
| 266 | errdefer self.object.allocator.destroy(got_load); | |
| 218 | 267 | |
| 219 | 268 | got_load.* = .{ |
| 220 | 269 | .base = .{ |
| 221 | 270 | .@"type" = .got_load, |
| 222 | .code = inst, | |
| 223 | 271 | .offset = offset, |
| 224 | 272 | .target = target, |
| 273 | .block = self.block, | |
| 225 | 274 | }, |
| 226 | 275 | }; |
| 227 | 276 | |
| 228 | log.debug(" | emitting {}", .{got_load}); | |
| 229 | try parser.parsed.append(&got_load.base); | |
| 277 | return &got_load.base; | |
| 230 | 278 | } |
| 231 | 279 | |
| 232 | fn parseGot(parser: *Parser, rel: macho.relocation_info) !void { | |
| 280 | fn parseGot(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 233 | 281 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); |
| 234 | 282 | assert(rel_type == .X86_64_RELOC_GOT); |
| 235 | 283 | assert(rel.r_pcrel == 1); |
| 236 | 284 | assert(rel.r_length == 2); |
| 237 | 285 | |
| 238 | const offset = @intCast(u32, rel.r_address); | |
| 239 | const inst = parser.code[offset..][0..4]; | |
| 240 | const target = Relocation.Target.fromReloc(rel); | |
| 241 | const addend = mem.readIntLittle(i32, inst); | |
| 286 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 287 | const target = try self.object.symbolFromReloc(rel); | |
| 288 | const addend = mem.readIntLittle(i32, self.block.code[offset..][0..4]); | |
| 242 | 289 | |
| 243 | var got = try parser.allocator.create(Got); | |
| 244 | errdefer parser.allocator.destroy(got); | |
| 290 | var got = try self.object.allocator.create(Got); | |
| 291 | errdefer self.object.allocator.destroy(got); | |
| 245 | 292 | |
| 246 | 293 | got.* = .{ |
| 247 | 294 | .base = .{ |
| 248 | 295 | .@"type" = .got, |
| 249 | .code = inst, | |
| 250 | 296 | .offset = offset, |
| 251 | 297 | .target = target, |
| 298 | .block = self.block, | |
| 252 | 299 | }, |
| 253 | 300 | .addend = addend, |
| 254 | 301 | }; |
| 255 | 302 | |
| 256 | log.debug(" | emitting {}", .{got}); | |
| 257 | try parser.parsed.append(&got.base); | |
| 303 | return &got.base; | |
| 258 | 304 | } |
| 259 | 305 | |
| 260 | fn parseTlv(parser: *Parser, rel: macho.relocation_info) !void { | |
| 306 | fn parseTlv(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 261 | 307 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); |
| 262 | 308 | assert(rel_type == .X86_64_RELOC_TLV); |
| 263 | 309 | assert(rel.r_pcrel == 1); |
| 264 | 310 | assert(rel.r_length == 2); |
| 265 | 311 | |
| 266 | const offset = @intCast(u32, rel.r_address); | |
| 267 | const inst = parser.code[offset..][0..4]; | |
| 268 | const target = Relocation.Target.fromReloc(rel); | |
| 312 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 313 | const target = try self.object.symbolFromReloc(rel); | |
| 269 | 314 | |
| 270 | var tlv = try parser.allocator.create(Tlv); | |
| 271 | errdefer parser.allocator.destroy(tlv); | |
| 315 | var tlv = try self.object.allocator.create(Tlv); | |
| 316 | errdefer self.object.allocator.destroy(tlv); | |
| 272 | 317 | |
| 273 | 318 | tlv.* = .{ |
| 274 | 319 | .base = .{ |
| 275 | 320 | .@"type" = .tlv, |
| 276 | .code = inst, | |
| 277 | 321 | .offset = offset, |
| 278 | 322 | .target = target, |
| 323 | .block = self.block, | |
| 279 | 324 | }, |
| 280 | .op = &parser.code[offset - 2], | |
| 281 | 325 | }; |
| 282 | 326 | |
| 283 | log.debug(" | emitting {}", .{tlv}); | |
| 284 | try parser.parsed.append(&tlv.base); | |
| 327 | return &tlv.base; | |
| 285 | 328 | } |
| 286 | 329 | |
| 287 | fn parseSubtractor(parser: *Parser, rel: macho.relocation_info) !void { | |
| 330 | fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void { | |
| 288 | 331 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); |
| 289 | 332 | assert(rel_type == .X86_64_RELOC_SUBTRACTOR); |
| 290 | 333 | assert(rel.r_pcrel == 0); |
| 291 | assert(parser.subtractor == null); | |
| 334 | assert(self.subtractor == null); | |
| 292 | 335 | |
| 293 | parser.subtractor = Relocation.Target.fromReloc(rel); | |
| 336 | self.subtractor = try self.object.symbolFromReloc(rel); | |
| 294 | 337 | |
| 295 | 338 | // Verify SUBTRACTOR is followed by UNSIGNED. |
| 296 | const next = @intToEnum(macho.reloc_type_x86_64, parser.it.peek().r_type); | |
| 339 | const next = @intToEnum(macho.reloc_type_x86_64, self.it.peek().r_type); | |
| 297 | 340 | if (next != .X86_64_RELOC_UNSIGNED) { |
| 298 | 341 | log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next}); |
| 299 | 342 | return error.UnexpectedRelocationType; |
| 300 | 343 | } |
| 301 | 344 | } |
| 302 | 345 | |
| 303 | fn parseUnsigned(parser: *Parser, rel: macho.relocation_info) !void { | |
| 346 | fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !*Relocation { | |
| 304 | 347 | defer { |
| 305 | 348 | // Reset parser's subtractor state |
| 306 | parser.subtractor = null; | |
| 349 | self.subtractor = null; | |
| 307 | 350 | } |
| 308 | 351 | |
| 309 | 352 | const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); |
| 310 | 353 | assert(rel_type == .X86_64_RELOC_UNSIGNED); |
| 311 | 354 | assert(rel.r_pcrel == 0); |
| 312 | 355 | |
| 313 | var unsigned = try parser.allocator.create(reloc.Unsigned); | |
| 314 | errdefer parser.allocator.destroy(unsigned); | |
| 315 | ||
| 316 | const target = Relocation.Target.fromReloc(rel); | |
| 356 | const target = try self.object.symbolFromReloc(rel); | |
| 317 | 357 | const is_64bit: bool = switch (rel.r_length) { |
| 318 | 358 | 3 => true, |
| 319 | 359 | 2 => false, |
| 320 | 360 | else => unreachable, |
| 321 | 361 | }; |
| 322 | const offset = @intCast(u32, rel.r_address); | |
| 362 | const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr); | |
| 323 | 363 | const addend: i64 = if (is_64bit) |
| 324 | mem.readIntLittle(i64, parser.code[offset..][0..8]) | |
| 364 | mem.readIntLittle(i64, self.block.code[offset..][0..8]) | |
| 325 | 365 | else |
| 326 | mem.readIntLittle(i32, parser.code[offset..][0..4]); | |
| 366 | mem.readIntLittle(i32, self.block.code[offset..][0..4]); | |
| 367 | ||
| 368 | var unsigned = try self.object.allocator.create(reloc.Unsigned); | |
| 369 | errdefer self.object.allocator.destroy(unsigned); | |
| 327 | 370 | |
| 328 | 371 | unsigned.* = .{ |
| 329 | 372 | .base = .{ |
| 330 | 373 | .@"type" = .unsigned, |
| 331 | .code = if (is_64bit) parser.code[offset..][0..8] else parser.code[offset..][0..4], | |
| 332 | 374 | .offset = offset, |
| 333 | 375 | .target = target, |
| 376 | .block = self.block, | |
| 334 | 377 | }, |
| 335 | .subtractor = parser.subtractor, | |
| 378 | .subtractor = self.subtractor, | |
| 336 | 379 | .is_64bit = is_64bit, |
| 337 | 380 | .addend = addend, |
| 338 | 381 | }; |
| 339 | 382 | |
| 340 | log.debug(" | emitting {}", .{unsigned}); | |
| 341 | try parser.parsed.append(&unsigned.base); | |
| 383 | return &unsigned.base; | |
| 342 | 384 | } |
| 343 | 385 | }; |