authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-02-06 13:23:03+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-02-06 13:23:08+01:00
logb32f5ee93283f7794c611ebd4a1fbc579b78d8ab
tree06d60c8a6011711279aa4ee971c7da68f42c9ca8
parenta5b34a61ab61882bf55d87e4cbc8186215ecf320

macho: downgrade alignment requirements for symtab in object files

Parse and sort relocations by address descending.

6 files changed, 104 insertions(+), 75 deletions(-)

src/link/MachO/Object.zig+79-41
......@@ -60,14 +60,20 @@ globals_lookup: []i64 = undefined,
6060/// Can be undefined as set together with in_symtab.
6161relocs_lookup: []RelocEntry = undefined,
6262
63/// All relocations sorted and flatened.
64relocations: std.ArrayListUnmanaged(macho.relocation_info) = .{},
65/// Beginning index to the relocations array for each input section
66/// defined within this Object file.
67section_relocs_lookup: std.ArrayListUnmanaged(u32) = .{},
68
6369atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
6470exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
6571
66eh_frame_sect: ?macho.section_64 = null,
72eh_frame_sect_id: ?u8 = null,
6773eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{},
6874eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(AtomIndex, u32) = .{},
6975
70unwind_info_sect: ?macho.section_64 = null,
76unwind_info_sect_id: ?u8 = null,
7177unwind_relocs_lookup: []Record = undefined,
7278unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{},
7379
......@@ -100,6 +106,8 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
100106 gpa.free(self.unwind_relocs_lookup);
101107 }
102108 self.unwind_records_lookup.deinit(gpa);
109 self.relocations.deinit(gpa);
110 self.section_relocs_lookup.deinit(gpa);
103111}
104112
105113pub 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)
137145 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
138146 };
139147 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.
140154 const symtab = while (it.next()) |cmd| switch (cmd.cmd()) {
141155 .SYMTAB => break cmd.cast(macho.symtab_command).?,
142156 else => {},
143157 } else return;
144158
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];
149160 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
150161
151162 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)
212223 }
213224
214225 // 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");
216227
217228 // 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");
219230 if (self.hasUnwindRecords()) {
220231 self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len);
221232 mem.set(Record, self.unwind_relocs_lookup, .{
......@@ -452,6 +463,8 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
452463 zld.sections.items(.header)[out_sect_id].sectName(),
453464 });
454465
466 try self.parseRelocs(gpa, section.id);
467
455468 const cpu_arch = zld.options.target.cpu.arch;
456469 const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);
457470 const sect_start_index = sect_sym_index + sect_loc.index;
......@@ -623,25 +636,36 @@ fn filterRelocs(
623636 return .{ .start = @intCast(u32, start), .len = @intCast(u32, len) };
624637}
625638
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.
642fn 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
626653fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {
627654 const atom = zld.getAtom(atom_index);
628655
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;
633658 } else blk: {
634659 // If there was no matching symbol present in the source symtab, this means
635660 // we are dealing with either an entire section, or part of it, but also
636661 // starting at the beginning.
637662 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;
642665 };
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);
645669
646670 self.relocs_lookup[atom.sym_index] = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
647671 const offset = source_sym.n_value - source_sect.addr;
......@@ -649,8 +673,14 @@ fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {
649673 } else filterRelocs(relocs, 0, atom.size);
650674}
651675
676fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool {
677 _ = ctx;
678 return lhs.r_address > rhs.r_address;
679}
680
652681fn 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);
654684
655685 log.debug("parsing __TEXT,__eh_frame section", .{});
656686
......@@ -660,7 +690,8 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
660690
661691 const gpa = zld.gpa;
662692 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);
664695
665696 var it = self.getEhFrameRecordsIterator();
666697 var record_count: u32 = 0;
......@@ -728,12 +759,12 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
728759}
729760
730761fn 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 {
732763 // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,
733764 // we will try fully synthesising unwind info records to somewhat match Apple ld's
734765 // approach. However, we will only synthesise DWARF records and nothing more. For this reason,
735766 // we still create the output `__TEXT,__unwind_info` section.
736 if (self.eh_frame_sect != null) {
767 if (self.hasEhFrameRecords()) {
737768 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
738769 _ = try zld.initSection("__TEXT", "__unwind_info", .{});
739770 }
......@@ -758,15 +789,15 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
758789 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;
759790 } else false;
760791
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;
767796 }
768797
769 const relocs = self.getRelocs(sect);
798 try self.parseRelocs(gpa, sect_id);
799 const relocs = self.getRelocs(sect_id);
800
770801 for (unwind_records) |record, record_id| {
771802 const offset = record_id * @sizeOf(macho.compact_unwind_entry);
772803 const rel_pos = filterRelocs(
......@@ -806,25 +837,23 @@ pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
806837 return symtab[mapped_index];
807838}
808839
809pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
840pub fn getSourceSection(self: Object, index: u8) macho.section_64 {
810841 const sections = self.getSourceSections();
811842 assert(index < sections.len);
812843 return sections[index];
813844}
814845
815846pub fn getSourceSectionByName(self: Object, segname: []const u8, sectname: []const u8) ?macho.section_64 {
847 const index = self.getSourceSectionIndexByName(segname, sectname) orelse return null;
816848 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];
821850}
822851
823852pub fn getSourceSectionIndexByName(self: Object, segname: []const u8, sectname: []const u8) ?u8 {
824853 const sections = self.getSourceSections();
825854 for (sections) |sect, i| {
826855 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);
828857 } else return null;
829858}
830859
......@@ -914,11 +943,18 @@ pub fn getSectionAliasSymbolPtr(self: *Object, sect_id: u8) *macho.nlist_64 {
914943 return &self.symtab[self.getSectionAliasSymbolIndex(sect_id)];
915944}
916945
917pub fn getRelocs(self: Object, sect: macho.section_64) []align(1) const macho.relocation_info {
918 if (sect.nreloc == 0) return &[0]macho.relocation_info{};
946fn getSourceRelocs(self: Object, sect: macho.section_64) ?[]align(1) const macho.relocation_info {
947 if (sect.nreloc == 0) return null;
919948 return @ptrCast([*]align(1) const macho.relocation_info, self.contents.ptr + sect.reloff)[0..sect.nreloc];
920949}
921950
951pub 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
922958pub fn getSymbolName(self: Object, index: u32) []const u8 {
923959 const strtab = self.in_strtab.?;
924960 const sym = self.symtab[index];
......@@ -976,22 +1012,24 @@ pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {
9761012}
9771013
9781014pub fn hasUnwindRecords(self: Object) bool {
979 return self.unwind_info_sect != null;
1015 return self.unwind_info_sect_id != null;
9801016}
9811017
9821018pub 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);
9841021 const data = self.getSectionContents(sect);
9851022 const num_entries = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
9861023 return @ptrCast([*]align(1) const macho.compact_unwind_entry, data)[0..num_entries];
9871024}
9881025
9891026pub fn hasEhFrameRecords(self: Object) bool {
990 return self.eh_frame_sect != null;
1027 return self.eh_frame_sect_id != null;
9911028}
9921029
9931030pub 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);
9951033 const data = self.getSectionContents(sect);
9961034 return .{ .data = data };
9971035}
src/link/MachO/UnwindInfo.zig+2-6
......@@ -703,15 +703,11 @@ pub fn parseRelocTarget(
703703 } else return sym_loc;
704704}
705705
706fn getRelocs(
707 zld: *Zld,
708 object_id: u32,
709 record_id: usize,
710) []align(1) const macho.relocation_info {
706fn getRelocs(zld: *Zld, object_id: u32, record_id: usize) []const macho.relocation_info {
711707 const object = &zld.objects.items[object_id];
712708 assert(object.hasUnwindRecords());
713709 const rel_pos = object.unwind_relocs_lookup[record_id].reloc;
714 const relocs = object.getRelocs(object.unwind_info_sect.?);
710 const relocs = object.getRelocs(object.unwind_info_sect_id.?);
715711 return relocs[rel_pos.start..][0..rel_pos.len];
716712}
717713
src/link/MachO/ZldAtom.zig+14-17
......@@ -143,7 +143,7 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u
143143 sym.n_value
144144 else blk: {
145145 const nbase = @intCast(u32, object.in_symtab.?.len);
146 const sect_id = @intCast(u16, atom.sym_index - nbase);
146 const sect_id = @intCast(u8, atom.sym_index - nbase);
147147 const source_sect = object.getSourceSection(sect_id);
148148 break :blk source_sect.addr;
149149 };
......@@ -180,7 +180,7 @@ pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext {
180180 };
181181 }
182182 const nbase = @intCast(u32, object.in_symtab.?.len);
183 const sect_id = @intCast(u16, atom.sym_index - nbase);
183 const sect_id = @intCast(u8, atom.sym_index - nbase);
184184 const source_sect = object.getSourceSection(sect_id);
185185 return .{
186186 .base_addr = source_sect.addr,
......@@ -724,7 +724,7 @@ fn resolveRelocsArm64(
724724
725725 if (rel.r_extern == 0) {
726726 const base_addr = if (target.sym_index > object.source_address_lookup.len)
727 @intCast(i64, object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr)
727 @intCast(i64, object.getSourceSection(@intCast(u8, rel.r_symbolnum - 1)).addr)
728728 else
729729 object.source_address_lookup[target.sym_index];
730730 ptr_addend -= base_addr;
......@@ -861,7 +861,7 @@ fn resolveRelocsX86(
861861
862862 if (rel.r_extern == 0) {
863863 const base_addr = if (target.sym_index > object.source_address_lookup.len)
864 @intCast(i64, object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr)
864 @intCast(i64, object.getSourceSection(@intCast(u8, rel.r_symbolnum - 1)).addr)
865865 else
866866 object.source_address_lookup[target.sym_index];
867867 addend += @intCast(i32, @intCast(i64, context.base_addr) + rel.r_address + 4 -
......@@ -884,7 +884,7 @@ fn resolveRelocsX86(
884884
885885 if (rel.r_extern == 0) {
886886 const base_addr = if (target.sym_index > object.source_address_lookup.len)
887 @intCast(i64, object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr)
887 @intCast(i64, object.getSourceSection(@intCast(u8, rel.r_symbolnum - 1)).addr)
888888 else
889889 object.source_address_lookup[target.sym_index];
890890 addend -= base_addr;
......@@ -928,7 +928,7 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
928928 // we are dealing with either an entire section, or part of it, but also
929929 // starting at the beginning.
930930 const nbase = @intCast(u32, object.in_symtab.?.len);
931 const sect_id = @intCast(u16, atom.sym_index - nbase);
931 const sect_id = @intCast(u8, atom.sym_index - nbase);
932932 const source_sect = object.getSourceSection(sect_id);
933933 assert(!source_sect.isZerofill());
934934 const code = object.getSectionContents(source_sect);
......@@ -943,28 +943,25 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
943943 return code[offset..][0..code_len];
944944}
945945
946pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []align(1) const macho.relocation_info {
946pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []const macho.relocation_info {
947947 const atom = zld.getAtom(atom_index);
948948 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
949949 const object = zld.objects.items[atom.getFile().?];
950950 const cache = object.relocs_lookup[atom.sym_index];
951951
952 const source_sect = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
953 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
954 assert(!source_sect.isZerofill());
955 break :blk source_sect;
952 const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
953 break :blk source_sym.n_sect - 1;
956954 } else blk: {
957955 // If there was no matching symbol present in the source symtab, this means
958956 // we are dealing with either an entire section, or part of it, but also
959957 // starting at the beginning.
960958 const nbase = @intCast(u32, object.in_symtab.?.len);
961 const sect_id = @intCast(u16, atom.sym_index - nbase);
962 const source_sect = object.getSourceSection(sect_id);
963 assert(!source_sect.isZerofill());
964 break :blk source_sect;
959 const sect_id = @intCast(u8, atom.sym_index - nbase);
960 break :blk sect_id;
965961 };
966
967 const relocs = object.getRelocs(source_sect);
962 const source_sect = object.getSourceSection(source_sect_id);
963 assert(!source_sect.isZerofill());
964 const relocs = object.getRelocs(source_sect_id);
968965 return relocs[cache.start..][0..cache.len];
969966}
970967
src/link/MachO/dead_strip.zig+4-3
......@@ -88,7 +88,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
8888 source_sym.n_sect - 1
8989 else sect_id: {
9090 const nbase = @intCast(u32, object.in_symtab.?.len);
91 const sect_id = @intCast(u16, atom.sym_index - nbase);
91 const sect_id = @intCast(u8, atom.sym_index - nbase);
9292 break :sect_id sect_id;
9393 };
9494 const source_sect = object.getSourceSection(sect_id);
......@@ -223,7 +223,7 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
223223 source_sym.n_sect - 1
224224 else blk: {
225225 const nbase = @intCast(u32, object.in_symtab.?.len);
226 const sect_id = @intCast(u16, atom.sym_index - nbase);
226 const sect_id = @intCast(u8, atom.sym_index - nbase);
227227 break :blk sect_id;
228228 };
229229 const source_sect = object.getSourceSection(sect_id);
......@@ -350,8 +350,9 @@ fn markEhFrameRecord(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *A
350350 }
351351 },
352352 .x86_64 => {
353 const sect = object.getSourceSection(object.eh_frame_sect_id.?);
353354 const lsda_ptr = try fde.getLsdaPointer(cie, .{
354 .base_addr = object.eh_frame_sect.?.addr,
355 .base_addr = sect.addr,
355356 .base_offset = fde_offset,
356357 });
357358 if (lsda_ptr) |lsda_address| {
src/link/MachO/eh_frame.zig+4-7
......@@ -171,8 +171,9 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
171171 const cie_record = eh_records.get(
172172 eh_frame_offset + 4 - fde_record.getCiePointer(),
173173 ).?;
174 const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?);
174175 const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
175 .base_addr = object.eh_frame_sect.?.addr,
176 .base_addr = eh_frame_sect.addr,
176177 .base_offset = fde_record_offset,
177178 });
178179 if (source_lsda_ptr) |ptr| {
......@@ -552,16 +553,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
552553 };
553554}
554555
555pub fn getRelocs(
556 zld: *Zld,
557 object_id: u32,
558 source_offset: u32,
559) []align(1) const macho.relocation_info {
556pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info {
560557 const object = &zld.objects.items[object_id];
561558 assert(object.hasEhFrameRecords());
562559 const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
563560 return &[0]macho.relocation_info{};
564 const all_relocs = object.getRelocs(object.eh_frame_sect.?);
561 const all_relocs = object.getRelocs(object.eh_frame_sect_id.?);
565562 return all_relocs[urel.reloc.start..][0..urel.reloc.len];
566563}
567564
src/link/MachO/zld.zig+1-1
......@@ -2406,7 +2406,7 @@ pub const Zld = struct {
24062406 source_sym.n_value
24072407 else blk: {
24082408 const nbase = @intCast(u32, object.in_symtab.?.len);
2409 const source_sect_id = @intCast(u16, atom.sym_index - nbase);
2409 const source_sect_id = @intCast(u8, atom.sym_index - nbase);
24102410 break :blk object.getSourceSection(source_sect_id).addr;
24112411 };
24122412 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);