| ... | ... | @@ -60,14 +60,24 @@ 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, sorted by address descending |
| 64 | /// per section. |
| 65 | relocations: std.ArrayListUnmanaged(macho.relocation_info) = .{}, |
| 66 | /// Beginning index to the relocations array for each input section |
| 67 | /// defined within this Object file. |
| 68 | section_relocs_lookup: std.ArrayListUnmanaged(u32) = .{}, |
| 69 | |
| 70 | /// Data-in-code records sorted by address. |
| 71 | data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, |
| 72 | |
| 63 | 73 | atoms: std.ArrayListUnmanaged(AtomIndex) = .{}, |
| 64 | 74 | exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{}, |
| 65 | 75 | |
| 66 | | eh_frame_sect: ?macho.section_64 = null, |
| 76 | eh_frame_sect_id: ?u8 = null, |
| 67 | 77 | eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{}, |
| 68 | 78 | eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(AtomIndex, u32) = .{}, |
| 69 | 79 | |
| 70 | | unwind_info_sect: ?macho.section_64 = null, |
| 80 | unwind_info_sect_id: ?u8 = null, |
| 71 | 81 | unwind_relocs_lookup: []Record = undefined, |
| 72 | 82 | unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{}, |
| 73 | 83 | |
| ... | ... | @@ -100,6 +110,9 @@ pub fn deinit(self: *Object, gpa: Allocator) void { |
| 100 | 110 | gpa.free(self.unwind_relocs_lookup); |
| 101 | 111 | } |
| 102 | 112 | self.unwind_records_lookup.deinit(gpa); |
| 113 | self.relocations.deinit(gpa); |
| 114 | self.section_relocs_lookup.deinit(gpa); |
| 115 | self.data_in_code.deinit(gpa); |
| 103 | 116 | } |
| 104 | 117 | |
| 105 | 118 | pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void { |
| ... | ... | @@ -137,15 +150,18 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) |
| 137 | 150 | .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds], |
| 138 | 151 | }; |
| 139 | 152 | const nsects = self.getSourceSections().len; |
| 153 | |
| 154 | // Prepopulate relocations per section lookup table. |
| 155 | try self.section_relocs_lookup.resize(allocator, nsects); |
| 156 | mem.set(u32, self.section_relocs_lookup.items, 0); |
| 157 | |
| 158 | // Parse symtab. |
| 140 | 159 | const symtab = while (it.next()) |cmd| switch (cmd.cmd()) { |
| 141 | 160 | .SYMTAB => break cmd.cast(macho.symtab_command).?, |
| 142 | 161 | else => {}, |
| 143 | 162 | } else return; |
| 144 | 163 | |
| 145 | | self.in_symtab = @ptrCast( |
| 146 | | [*]const macho.nlist_64, |
| 147 | | @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]), |
| 148 | | )[0..symtab.nsyms]; |
| 164 | self.in_symtab = @ptrCast([*]align(1) const macho.nlist_64, self.contents.ptr + symtab.symoff)[0..symtab.nsyms]; |
| 149 | 165 | self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize]; |
| 150 | 166 | |
| 151 | 167 | self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects); |
| ... | ... | @@ -212,10 +228,10 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) |
| 212 | 228 | } |
| 213 | 229 | |
| 214 | 230 | // Parse __TEXT,__eh_frame header if one exists |
| 215 | | self.eh_frame_sect = self.getSourceSectionByName("__TEXT", "__eh_frame"); |
| 231 | self.eh_frame_sect_id = self.getSourceSectionIndexByName("__TEXT", "__eh_frame"); |
| 216 | 232 | |
| 217 | 233 | // Parse __LD,__compact_unwind header if one exists |
| 218 | | self.unwind_info_sect = self.getSourceSectionByName("__LD", "__compact_unwind"); |
| 234 | self.unwind_info_sect_id = self.getSourceSectionIndexByName("__LD", "__compact_unwind"); |
| 219 | 235 | if (self.hasUnwindRecords()) { |
| 220 | 236 | self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len); |
| 221 | 237 | mem.set(Record, self.unwind_relocs_lookup, .{ |
| ... | ... | @@ -354,6 +370,7 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void { |
| 354 | 370 | try self.splitRegularSections(zld, object_id); |
| 355 | 371 | try self.parseEhFrameSection(zld, object_id); |
| 356 | 372 | try self.parseUnwindInfo(zld, object_id); |
| 373 | try self.parseDataInCode(zld.gpa); |
| 357 | 374 | } |
| 358 | 375 | |
| 359 | 376 | /// Splits input regular sections into Atoms. |
| ... | ... | @@ -452,6 +469,8 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { |
| 452 | 469 | zld.sections.items(.header)[out_sect_id].sectName(), |
| 453 | 470 | }); |
| 454 | 471 | |
| 472 | try self.parseRelocs(gpa, section.id); |
| 473 | |
| 455 | 474 | const cpu_arch = zld.options.target.cpu.arch; |
| 456 | 475 | const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1); |
| 457 | 476 | const sect_start_index = sect_sym_index + sect_loc.index; |
| ... | ... | @@ -623,25 +642,36 @@ fn filterRelocs( |
| 623 | 642 | return .{ .start = @intCast(u32, start), .len = @intCast(u32, len) }; |
| 624 | 643 | } |
| 625 | 644 | |
| 645 | /// Parse all relocs for the input section, and sort in descending order. |
| 646 | /// Previously, I have wrongly assumed the compilers output relocations for each |
| 647 | /// section in a sorted manner which is simply not true. |
| 648 | fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void { |
| 649 | const section = self.getSourceSection(sect_id); |
| 650 | const start = @intCast(u32, self.relocations.items.len); |
| 651 | if (self.getSourceRelocs(section)) |relocs| { |
| 652 | try self.relocations.ensureUnusedCapacity(gpa, relocs.len); |
| 653 | self.relocations.appendUnalignedSliceAssumeCapacity(relocs); |
| 654 | std.sort.sort(macho.relocation_info, self.relocations.items[start..], {}, relocGreaterThan); |
| 655 | } |
| 656 | self.section_relocs_lookup.items[sect_id] = start; |
| 657 | } |
| 658 | |
| 626 | 659 | fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void { |
| 627 | 660 | const atom = zld.getAtom(atom_index); |
| 628 | 661 | |
| 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; |
| 662 | const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: { |
| 663 | break :blk source_sym.n_sect - 1; |
| 633 | 664 | } else blk: { |
| 634 | 665 | // If there was no matching symbol present in the source symtab, this means |
| 635 | 666 | // we are dealing with either an entire section, or part of it, but also |
| 636 | 667 | // starting at the beginning. |
| 637 | 668 | 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; |
| 669 | const sect_id = @intCast(u8, atom.sym_index - nbase); |
| 670 | break :blk sect_id; |
| 642 | 671 | }; |
| 643 | | |
| 644 | | const relocs = self.getRelocs(source_sect); |
| 672 | const source_sect = self.getSourceSection(source_sect_id); |
| 673 | assert(!source_sect.isZerofill()); |
| 674 | const relocs = self.getRelocs(source_sect_id); |
| 645 | 675 | |
| 646 | 676 | self.relocs_lookup[atom.sym_index] = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: { |
| 647 | 677 | const offset = source_sym.n_value - source_sect.addr; |
| ... | ... | @@ -649,8 +679,14 @@ fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void { |
| 649 | 679 | } else filterRelocs(relocs, 0, atom.size); |
| 650 | 680 | } |
| 651 | 681 | |
| 682 | fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool { |
| 683 | _ = ctx; |
| 684 | return lhs.r_address > rhs.r_address; |
| 685 | } |
| 686 | |
| 652 | 687 | fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 653 | | const sect = self.eh_frame_sect orelse return; |
| 688 | const sect_id = self.eh_frame_sect_id orelse return; |
| 689 | const sect = self.getSourceSection(sect_id); |
| 654 | 690 | |
| 655 | 691 | log.debug("parsing __TEXT,__eh_frame section", .{}); |
| 656 | 692 | |
| ... | ... | @@ -660,7 +696,8 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 660 | 696 | |
| 661 | 697 | const gpa = zld.gpa; |
| 662 | 698 | const cpu_arch = zld.options.target.cpu.arch; |
| 663 | | const relocs = self.getRelocs(sect); |
| 699 | try self.parseRelocs(gpa, sect_id); |
| 700 | const relocs = self.getRelocs(sect_id); |
| 664 | 701 | |
| 665 | 702 | var it = self.getEhFrameRecordsIterator(); |
| 666 | 703 | var record_count: u32 = 0; |
| ... | ... | @@ -728,12 +765,12 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 728 | 765 | } |
| 729 | 766 | |
| 730 | 767 | fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { |
| 731 | | const sect = self.unwind_info_sect orelse { |
| 768 | const sect_id = self.unwind_info_sect_id orelse { |
| 732 | 769 | // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`, |
| 733 | 770 | // we will try fully synthesising unwind info records to somewhat match Apple ld's |
| 734 | 771 | // approach. However, we will only synthesise DWARF records and nothing more. For this reason, |
| 735 | 772 | // we still create the output `__TEXT,__unwind_info` section. |
| 736 | | if (self.eh_frame_sect != null) { |
| 773 | if (self.hasEhFrameRecords()) { |
| 737 | 774 | if (zld.getSectionByName("__TEXT", "__unwind_info") == null) { |
| 738 | 775 | _ = try zld.initSection("__TEXT", "__unwind_info", .{}); |
| 739 | 776 | } |
| ... | ... | @@ -758,15 +795,15 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { |
| 758 | 795 | if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true; |
| 759 | 796 | } else false; |
| 760 | 797 | |
| 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 | | } |
| 798 | if (needs_eh_frame and !self.hasEhFrameRecords()) { |
| 799 | log.err("missing __TEXT,__eh_frame section", .{}); |
| 800 | log.err(" in object {s}", .{self.name}); |
| 801 | return error.MissingSection; |
| 767 | 802 | } |
| 768 | 803 | |
| 769 | | const relocs = self.getRelocs(sect); |
| 804 | try self.parseRelocs(gpa, sect_id); |
| 805 | const relocs = self.getRelocs(sect_id); |
| 806 | |
| 770 | 807 | for (unwind_records) |record, record_id| { |
| 771 | 808 | const offset = record_id * @sizeOf(macho.compact_unwind_entry); |
| 772 | 809 | const rel_pos = filterRelocs( |
| ... | ... | @@ -806,25 +843,23 @@ pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 { |
| 806 | 843 | return symtab[mapped_index]; |
| 807 | 844 | } |
| 808 | 845 | |
| 809 | | pub fn getSourceSection(self: Object, index: u16) macho.section_64 { |
| 846 | pub fn getSourceSection(self: Object, index: u8) macho.section_64 { |
| 810 | 847 | const sections = self.getSourceSections(); |
| 811 | 848 | assert(index < sections.len); |
| 812 | 849 | return sections[index]; |
| 813 | 850 | } |
| 814 | 851 | |
| 815 | 852 | pub fn getSourceSectionByName(self: Object, segname: []const u8, sectname: []const u8) ?macho.section_64 { |
| 853 | const index = self.getSourceSectionIndexByName(segname, sectname) orelse return null; |
| 816 | 854 | 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; |
| 855 | return sections[index]; |
| 821 | 856 | } |
| 822 | 857 | |
| 823 | 858 | pub fn getSourceSectionIndexByName(self: Object, segname: []const u8, sectname: []const u8) ?u8 { |
| 824 | 859 | const sections = self.getSourceSections(); |
| 825 | 860 | for (sections) |sect, i| { |
| 826 | 861 | if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName())) |
| 827 | | return @intCast(u8, i + 1); |
| 862 | return @intCast(u8, i); |
| 828 | 863 | } else return null; |
| 829 | 864 | } |
| 830 | 865 | |
| ... | ... | @@ -841,24 +876,27 @@ pub fn getSourceSections(self: Object) []const macho.section_64 { |
| 841 | 876 | } else unreachable; |
| 842 | 877 | } |
| 843 | 878 | |
| 844 | | pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry { |
| 879 | pub fn parseDataInCode(self: *Object, gpa: Allocator) !void { |
| 845 | 880 | var it = LoadCommandIterator{ |
| 846 | 881 | .ncmds = self.header.ncmds, |
| 847 | 882 | .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds], |
| 848 | 883 | }; |
| 849 | | while (it.next()) |cmd| { |
| 884 | const cmd = while (it.next()) |cmd| { |
| 850 | 885 | switch (cmd.cmd()) { |
| 851 | | .DATA_IN_CODE => { |
| 852 | | const dice = cmd.cast(macho.linkedit_data_command).?; |
| 853 | | const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry)); |
| 854 | | return @ptrCast( |
| 855 | | [*]const macho.data_in_code_entry, |
| 856 | | @alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]), |
| 857 | | )[0..ndice]; |
| 858 | | }, |
| 886 | .DATA_IN_CODE => break cmd.cast(macho.linkedit_data_command).?, |
| 859 | 887 | else => {}, |
| 860 | 888 | } |
| 861 | | } else return null; |
| 889 | } else return; |
| 890 | const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry)); |
| 891 | const dice = @ptrCast([*]align(1) const macho.data_in_code_entry, self.contents.ptr + cmd.dataoff)[0..ndice]; |
| 892 | try self.data_in_code.ensureTotalCapacityPrecise(gpa, dice.len); |
| 893 | self.data_in_code.appendUnalignedSliceAssumeCapacity(dice); |
| 894 | std.sort.sort(macho.data_in_code_entry, self.data_in_code.items, {}, diceLessThan); |
| 895 | } |
| 896 | |
| 897 | fn diceLessThan(ctx: void, lhs: macho.data_in_code_entry, rhs: macho.data_in_code_entry) bool { |
| 898 | _ = ctx; |
| 899 | return lhs.offset < rhs.offset; |
| 862 | 900 | } |
| 863 | 901 | |
| 864 | 902 | fn parseDysymtab(self: Object) ?macho.dysymtab_command { |
| ... | ... | @@ -914,11 +952,18 @@ pub fn getSectionAliasSymbolPtr(self: *Object, sect_id: u8) *macho.nlist_64 { |
| 914 | 952 | return &self.symtab[self.getSectionAliasSymbolIndex(sect_id)]; |
| 915 | 953 | } |
| 916 | 954 | |
| 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{}; |
| 955 | fn getSourceRelocs(self: Object, sect: macho.section_64) ?[]align(1) const macho.relocation_info { |
| 956 | if (sect.nreloc == 0) return null; |
| 919 | 957 | return @ptrCast([*]align(1) const macho.relocation_info, self.contents.ptr + sect.reloff)[0..sect.nreloc]; |
| 920 | 958 | } |
| 921 | 959 | |
| 960 | pub fn getRelocs(self: Object, sect_id: u8) []const macho.relocation_info { |
| 961 | const sect = self.getSourceSection(sect_id); |
| 962 | const start = self.section_relocs_lookup.items[sect_id]; |
| 963 | const len = sect.nreloc; |
| 964 | return self.relocations.items[start..][0..len]; |
| 965 | } |
| 966 | |
| 922 | 967 | pub fn getSymbolName(self: Object, index: u32) []const u8 { |
| 923 | 968 | const strtab = self.in_strtab.?; |
| 924 | 969 | const sym = self.symtab[index]; |
| ... | ... | @@ -976,22 +1021,28 @@ pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex { |
| 976 | 1021 | } |
| 977 | 1022 | |
| 978 | 1023 | pub fn hasUnwindRecords(self: Object) bool { |
| 979 | | return self.unwind_info_sect != null; |
| 1024 | return self.unwind_info_sect_id != null; |
| 980 | 1025 | } |
| 981 | 1026 | |
| 982 | 1027 | 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{}; |
| 1028 | const sect_id = self.unwind_info_sect_id orelse return &[0]macho.compact_unwind_entry{}; |
| 1029 | const sect = self.getSourceSection(sect_id); |
| 984 | 1030 | const data = self.getSectionContents(sect); |
| 985 | 1031 | const num_entries = @divExact(data.len, @sizeOf(macho.compact_unwind_entry)); |
| 986 | 1032 | return @ptrCast([*]align(1) const macho.compact_unwind_entry, data)[0..num_entries]; |
| 987 | 1033 | } |
| 988 | 1034 | |
| 989 | 1035 | pub fn hasEhFrameRecords(self: Object) bool { |
| 990 | | return self.eh_frame_sect != null; |
| 1036 | return self.eh_frame_sect_id != null; |
| 991 | 1037 | } |
| 992 | 1038 | |
| 993 | 1039 | pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator { |
| 994 | | const sect = self.eh_frame_sect orelse return .{ .data = &[0]u8{} }; |
| 1040 | const sect_id = self.eh_frame_sect_id orelse return .{ .data = &[0]u8{} }; |
| 1041 | const sect = self.getSourceSection(sect_id); |
| 995 | 1042 | const data = self.getSectionContents(sect); |
| 996 | 1043 | return .{ .data = data }; |
| 997 | 1044 | } |
| 1045 | |
| 1046 | pub fn hasDataInCode(self: Object) bool { |
| 1047 | return self.data_in_code.items.len > 0; |
| 1048 | } |