| ... | ... | @@ -60,14 +60,20 @@ globals_lookup: []i64 = undefined, |
| 60 | 60 | /// Can be undefined as set together with in_symtab. |
| 61 | 61 | relocs_lookup: []RelocEntry = undefined, |
| 62 | 62 | |
| 63 | /// All relocations sorted and flatened. |
| 64 | relocations: std.ArrayListUnmanaged(macho.relocation_info) = .{}, |
| 65 | /// Beginning index to the relocations array for each input section |
| 66 | /// defined within this Object file. |
| 67 | section_relocs_lookup: std.ArrayListUnmanaged(u32) = .{}, |
| 68 | |
| 63 | 69 | atoms: std.ArrayListUnmanaged(AtomIndex) = .{}, |
| 64 | 70 | exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{}, |
| 65 | 71 | |
| 66 | | eh_frame_sect: ?macho.section_64 = null, |
| 72 | eh_frame_sect_id: ?u8 = null, |
| 67 | 73 | eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{}, |
| 68 | 74 | eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(AtomIndex, u32) = .{}, |
| 69 | 75 | |
| 70 | | unwind_info_sect: ?macho.section_64 = null, |
| 76 | unwind_info_sect_id: ?u8 = null, |
| 71 | 77 | unwind_relocs_lookup: []Record = undefined, |
| 72 | 78 | unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{}, |
| 73 | 79 | |
| ... | ... | @@ -100,6 +106,8 @@ pub fn deinit(self: *Object, gpa: Allocator) void { |
| 100 | 106 | gpa.free(self.unwind_relocs_lookup); |
| 101 | 107 | } |
| 102 | 108 | self.unwind_records_lookup.deinit(gpa); |
| 109 | self.relocations.deinit(gpa); |
| 110 | self.section_relocs_lookup.deinit(gpa); |
| 103 | 111 | } |
| 104 | 112 | |
| 105 | 113 | pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void { |
| ... | ... | @@ -137,15 +145,18 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) |
| 137 | 145 | .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds], |
| 138 | 146 | }; |
| 139 | 147 | const nsects = self.getSourceSections().len; |
| 148 | |
| 149 | // Prepopulate relocations per section lookup table. |
| 150 | try self.section_relocs_lookup.resize(allocator, nsects); |
| 151 | mem.set(u32, self.section_relocs_lookup.items, 0); |
| 152 | |
| 153 | // Parse symtab. |
| 140 | 154 | const symtab = while (it.next()) |cmd| switch (cmd.cmd()) { |
| 141 | 155 | .SYMTAB => break cmd.cast(macho.symtab_command).?, |
| 142 | 156 | else => {}, |
| 143 | 157 | } else return; |
| 144 | 158 | |
| 145 | | self.in_symtab = @ptrCast( |
| 146 | | [*]const macho.nlist_64, |
| 147 | | @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]), |
| 148 | | )[0..symtab.nsyms]; |
| 159 | self.in_symtab = @ptrCast([*]align(1) const macho.nlist_64, self.contents.ptr + symtab.symoff)[0..symtab.nsyms]; |
| 149 | 160 | self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize]; |
| 150 | 161 | |
| 151 | 162 | self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects); |
| ... | ... | @@ -212,10 +223,10 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) |
| 212 | 223 | } |
| 213 | 224 | |
| 214 | 225 | // Parse __TEXT,__eh_frame header if one exists |
| 215 | | self.eh_frame_sect = self.getSourceSectionByName("__TEXT", "__eh_frame"); |
| 226 | self.eh_frame_sect_id = self.getSourceSectionIndexByName("__TEXT", "__eh_frame"); |
| 216 | 227 | |
| 217 | 228 | // Parse __LD,__compact_unwind header if one exists |
| 218 | | self.unwind_info_sect = self.getSourceSectionByName("__LD", "__compact_unwind"); |
| 229 | self.unwind_info_sect_id = self.getSourceSectionIndexByName("__LD", "__compact_unwind"); |
| 219 | 230 | if (self.hasUnwindRecords()) { |
| 220 | 231 | self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len); |
| 221 | 232 | mem.set(Record, self.unwind_relocs_lookup, .{ |
| ... | ... | @@ -452,6 +463,8 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { |
| 452 | 463 | zld.sections.items(.header)[out_sect_id].sectName(), |
| 453 | 464 | }); |
| 454 | 465 | |
| 466 | try self.parseRelocs(gpa, section.id); |
| 467 | |
| 455 | 468 | const cpu_arch = zld.options.target.cpu.arch; |
| 456 | 469 | const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1); |
| 457 | 470 | const sect_start_index = sect_sym_index + sect_loc.index; |
| ... | ... | @@ -623,25 +636,36 @@ fn filterRelocs( |
| 623 | 636 | return .{ .start = @intCast(u32, start), .len = @intCast(u32, len) }; |
| 624 | 637 | } |
| 625 | 638 | |
| 639 | /// Parse all relocs for the input section, and sort in descending order. |
| 640 | /// Previously, I have wrongly assumed the compilers output relocations for each |
| 641 | /// section in a sorted manner which is simply not true. |
| 642 | fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void { |
| 643 | const section = self.getSourceSection(sect_id); |
| 644 | const start = @intCast(u32, self.relocations.items.len); |
| 645 | if (self.getSourceRelocs(section)) |relocs| { |
| 646 | try self.relocations.ensureUnusedCapacity(gpa, relocs.len); |
| 647 | self.relocations.appendUnalignedSliceAssumeCapacity(relocs); |
| 648 | std.sort.sort(macho.relocation_info, self.relocations.items[start..], {}, relocGreaterThan); |
| 649 | } |
| 650 | self.section_relocs_lookup.items[sect_id] = start; |
| 651 | } |
| 652 | |
| 626 | 653 | fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void { |
| 627 | 654 | const atom = zld.getAtom(atom_index); |
| 628 | 655 | |
| 629 | | const source_sect = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: { |
| 630 | | const source_sect = self.getSourceSection(source_sym.n_sect - 1); |
| 631 | | assert(!source_sect.isZerofill()); |
| 632 | | break :blk source_sect; |
| 656 | const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: { |
| 657 | break :blk source_sym.n_sect - 1; |
| 633 | 658 | } else blk: { |
| 634 | 659 | // If there was no matching symbol present in the source symtab, this means |
| 635 | 660 | // we are dealing with either an entire section, or part of it, but also |
| 636 | 661 | // starting at the beginning. |
| 637 | 662 | const nbase = @intCast(u32, self.in_symtab.?.len); |
| 638 | | const sect_id = @intCast(u16, atom.sym_index - nbase); |
| 639 | | const source_sect = self.getSourceSection(sect_id); |
| 640 | | assert(!source_sect.isZerofill()); |
| 641 | | break :blk source_sect; |
| 663 | const sect_id = @intCast(u8, atom.sym_index - nbase); |
| 664 | break :blk sect_id; |
| 642 | 665 | }; |
| 643 | | |
| 644 | | const relocs = self.getRelocs(source_sect); |
| 666 | const source_sect = self.getSourceSection(source_sect_id); |
| 667 | assert(!source_sect.isZerofill()); |
| 668 | const relocs = self.getRelocs(source_sect_id); |
| 645 | 669 | |
| 646 | 670 | self.relocs_lookup[atom.sym_index] = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: { |
| 647 | 671 | const offset = source_sym.n_value - source_sect.addr; |
| ... | ... | @@ -649,8 +673,14 @@ fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void { |
| 649 | 673 | } else filterRelocs(relocs, 0, atom.size); |
| 650 | 674 | } |
| 651 | 675 | |
| 676 | fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool { |
| 677 | _ = ctx; |
| 678 | return lhs.r_address > rhs.r_address; |
| 679 | } |
| 680 | |
| 652 | 681 | fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 653 | | const sect = self.eh_frame_sect orelse return; |
| 682 | const sect_id = self.eh_frame_sect_id orelse return; |
| 683 | const sect = self.getSourceSection(sect_id); |
| 654 | 684 | |
| 655 | 685 | log.debug("parsing __TEXT,__eh_frame section", .{}); |
| 656 | 686 | |
| ... | ... | @@ -660,7 +690,8 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 660 | 690 | |
| 661 | 691 | const gpa = zld.gpa; |
| 662 | 692 | const cpu_arch = zld.options.target.cpu.arch; |
| 663 | | const relocs = self.getRelocs(sect); |
| 693 | try self.parseRelocs(gpa, sect_id); |
| 694 | const relocs = self.getRelocs(sect_id); |
| 664 | 695 | |
| 665 | 696 | var it = self.getEhFrameRecordsIterator(); |
| 666 | 697 | var record_count: u32 = 0; |
| ... | ... | @@ -728,12 +759,12 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 728 | 759 | } |
| 729 | 760 | |
| 730 | 761 | fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { |
| 731 | | const sect = self.unwind_info_sect orelse { |
| 762 | const sect_id = self.unwind_info_sect_id orelse { |
| 732 | 763 | // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`, |
| 733 | 764 | // we will try fully synthesising unwind info records to somewhat match Apple ld's |
| 734 | 765 | // approach. However, we will only synthesise DWARF records and nothing more. For this reason, |
| 735 | 766 | // we still create the output `__TEXT,__unwind_info` section. |
| 736 | | if (self.eh_frame_sect != null) { |
| 767 | if (self.hasEhFrameRecords()) { |
| 737 | 768 | if (zld.getSectionByName("__TEXT", "__unwind_info") == null) { |
| 738 | 769 | _ = try zld.initSection("__TEXT", "__unwind_info", .{}); |
| 739 | 770 | } |
| ... | ... | @@ -758,15 +789,15 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { |
| 758 | 789 | if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true; |
| 759 | 790 | } else false; |
| 760 | 791 | |
| 761 | | if (needs_eh_frame) { |
| 762 | | if (self.eh_frame_sect == null) { |
| 763 | | log.err("missing __TEXT,__eh_frame section", .{}); |
| 764 | | log.err(" in object {s}", .{self.name}); |
| 765 | | return error.MissingSection; |
| 766 | | } |
| 792 | if (needs_eh_frame and !self.hasEhFrameRecords()) { |
| 793 | log.err("missing __TEXT,__eh_frame section", .{}); |
| 794 | log.err(" in object {s}", .{self.name}); |
| 795 | return error.MissingSection; |
| 767 | 796 | } |
| 768 | 797 | |
| 769 | | const relocs = self.getRelocs(sect); |
| 798 | try self.parseRelocs(gpa, sect_id); |
| 799 | const relocs = self.getRelocs(sect_id); |
| 800 | |
| 770 | 801 | for (unwind_records) |record, record_id| { |
| 771 | 802 | const offset = record_id * @sizeOf(macho.compact_unwind_entry); |
| 772 | 803 | const rel_pos = filterRelocs( |
| ... | ... | @@ -806,25 +837,23 @@ pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 { |
| 806 | 837 | return symtab[mapped_index]; |
| 807 | 838 | } |
| 808 | 839 | |
| 809 | | pub fn getSourceSection(self: Object, index: u16) macho.section_64 { |
| 840 | pub fn getSourceSection(self: Object, index: u8) macho.section_64 { |
| 810 | 841 | const sections = self.getSourceSections(); |
| 811 | 842 | assert(index < sections.len); |
| 812 | 843 | return sections[index]; |
| 813 | 844 | } |
| 814 | 845 | |
| 815 | 846 | pub fn getSourceSectionByName(self: Object, segname: []const u8, sectname: []const u8) ?macho.section_64 { |
| 847 | const index = self.getSourceSectionIndexByName(segname, sectname) orelse return null; |
| 816 | 848 | const sections = self.getSourceSections(); |
| 817 | | for (sections) |sect| { |
| 818 | | if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName())) |
| 819 | | return sect; |
| 820 | | } else return null; |
| 849 | return sections[index]; |
| 821 | 850 | } |
| 822 | 851 | |
| 823 | 852 | pub fn getSourceSectionIndexByName(self: Object, segname: []const u8, sectname: []const u8) ?u8 { |
| 824 | 853 | const sections = self.getSourceSections(); |
| 825 | 854 | for (sections) |sect, i| { |
| 826 | 855 | if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName())) |
| 827 | | return @intCast(u8, i + 1); |
| 856 | return @intCast(u8, i); |
| 828 | 857 | } else return null; |
| 829 | 858 | } |
| 830 | 859 | |
| ... | ... | @@ -914,11 +943,18 @@ pub fn getSectionAliasSymbolPtr(self: *Object, sect_id: u8) *macho.nlist_64 { |
| 914 | 943 | return &self.symtab[self.getSectionAliasSymbolIndex(sect_id)]; |
| 915 | 944 | } |
| 916 | 945 | |
| 917 | | pub fn getRelocs(self: Object, sect: macho.section_64) []align(1) const macho.relocation_info { |
| 918 | | if (sect.nreloc == 0) return &[0]macho.relocation_info{}; |
| 946 | fn getSourceRelocs(self: Object, sect: macho.section_64) ?[]align(1) const macho.relocation_info { |
| 947 | if (sect.nreloc == 0) return null; |
| 919 | 948 | return @ptrCast([*]align(1) const macho.relocation_info, self.contents.ptr + sect.reloff)[0..sect.nreloc]; |
| 920 | 949 | } |
| 921 | 950 | |
| 951 | pub fn getRelocs(self: Object, sect_id: u8) []const macho.relocation_info { |
| 952 | const sect = self.getSourceSection(sect_id); |
| 953 | const start = self.section_relocs_lookup.items[sect_id]; |
| 954 | const len = sect.nreloc; |
| 955 | return self.relocations.items[start..][0..len]; |
| 956 | } |
| 957 | |
| 922 | 958 | pub fn getSymbolName(self: Object, index: u32) []const u8 { |
| 923 | 959 | const strtab = self.in_strtab.?; |
| 924 | 960 | const sym = self.symtab[index]; |
| ... | ... | @@ -976,22 +1012,24 @@ pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex { |
| 976 | 1012 | } |
| 977 | 1013 | |
| 978 | 1014 | pub fn hasUnwindRecords(self: Object) bool { |
| 979 | | return self.unwind_info_sect != null; |
| 1015 | return self.unwind_info_sect_id != null; |
| 980 | 1016 | } |
| 981 | 1017 | |
| 982 | 1018 | pub fn getUnwindRecords(self: Object) []align(1) const macho.compact_unwind_entry { |
| 983 | | const sect = self.unwind_info_sect orelse return &[0]macho.compact_unwind_entry{}; |
| 1019 | const sect_id = self.unwind_info_sect_id orelse return &[0]macho.compact_unwind_entry{}; |
| 1020 | const sect = self.getSourceSection(sect_id); |
| 984 | 1021 | const data = self.getSectionContents(sect); |
| 985 | 1022 | const num_entries = @divExact(data.len, @sizeOf(macho.compact_unwind_entry)); |
| 986 | 1023 | return @ptrCast([*]align(1) const macho.compact_unwind_entry, data)[0..num_entries]; |
| 987 | 1024 | } |
| 988 | 1025 | |
| 989 | 1026 | pub fn hasEhFrameRecords(self: Object) bool { |
| 990 | | return self.eh_frame_sect != null; |
| 1027 | return self.eh_frame_sect_id != null; |
| 991 | 1028 | } |
| 992 | 1029 | |
| 993 | 1030 | pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator { |
| 994 | | const sect = self.eh_frame_sect orelse return .{ .data = &[0]u8{} }; |
| 1031 | const sect_id = self.eh_frame_sect_id orelse return .{ .data = &[0]u8{} }; |
| 1032 | const sect = self.getSourceSection(sect_id); |
| 995 | 1033 | const data = self.getSectionContents(sect); |
| 996 | 1034 | return .{ .data = data }; |
| 997 | 1035 | } |