authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-21 21:27:17+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-21 21:27:22+01:00
log1be86218153ae77109d785aafb29430f787adefd
tree6cc3b61a15f9e4f1215dab07c1494286ab766564
parentdc98009e36a344f8d0330af6b9e9226a2ba6a474

macho+zld: when finding by address, note the end of section symbols too

Previously, if we were looking for the very last symbol by address in some section, and the next symbol happened to also have the same address value but would reside in a different section, we would keep going finding the wrong symbol in the wrong section. This mechanism turns out vital for correct linking of Go binaries where the runtime looks for specially crafted synthetic symbols which mark the beginning and end of each section. In this case, we had an unfortunate clash between the end of PC marked machine code section (`_runtime.etext`) and beginning of read-only data (`_runtime.rodata`).

2 files changed, 39 insertions(+), 30 deletions(-)

src/link/MachO/Object.zig+37-29
......@@ -50,7 +50,7 @@ reverse_symtab_lookup: []u32 = undefined,
5050/// Can be undefined as set together with in_symtab.
5151source_address_lookup: []i64 = undefined,
5252/// Can be undefined as set together with in_symtab.
53source_section_index_lookup: []i64 = undefined,
53source_section_index_lookup: []Entry = undefined,
5454/// Can be undefined as set together with in_symtab.
5555strtab_lookup: []u32 = undefined,
5656/// Can be undefined as set together with in_symtab.
......@@ -58,7 +58,7 @@ atom_by_index_table: []AtomIndex = undefined,
5858/// Can be undefined as set together with in_symtab.
5959globals_lookup: []i64 = undefined,
6060/// Can be undefined as set together with in_symtab.
61relocs_lookup: []RelocEntry = undefined,
61relocs_lookup: []Entry = undefined,
6262
6363/// All relocations sorted and flatened, sorted by address descending
6464/// per section.
......@@ -81,11 +81,14 @@ unwind_info_sect_id: ?u8 = null,
8181unwind_relocs_lookup: []Record = undefined,
8282unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{},
8383
84const RelocEntry = struct { start: u32, len: u32 };
84const Entry = struct {
85 start: u32 = 0,
86 len: u32 = 0,
87};
8588
8689const Record = struct {
8790 dead: bool,
88 reloc: RelocEntry,
91 reloc: Entry,
8992};
9093
9194pub fn deinit(self: *Object, gpa: Allocator) void {
......@@ -170,11 +173,11 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
170173 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
171174 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
172175 self.atom_by_index_table = try allocator.alloc(AtomIndex, self.in_symtab.?.len + nsects);
173 self.relocs_lookup = try allocator.alloc(RelocEntry, self.in_symtab.?.len + nsects);
176 self.relocs_lookup = try allocator.alloc(Entry, self.in_symtab.?.len + nsects);
174177 // This is wasteful but we need to be able to lookup source symbol address after stripping and
175178 // allocating of sections.
176179 self.source_address_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
177 self.source_section_index_lookup = try allocator.alloc(i64, nsects);
180 self.source_section_index_lookup = try allocator.alloc(Entry, nsects);
178181
179182 for (self.symtab) |*sym| {
180183 sym.* = .{
......@@ -188,11 +191,8 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
188191
189192 mem.set(i64, self.globals_lookup, -1);
190193 mem.set(AtomIndex, self.atom_by_index_table, 0);
191 mem.set(i64, self.source_section_index_lookup, -1);
192 mem.set(RelocEntry, self.relocs_lookup, .{
193 .start = 0,
194 .len = 0,
195 });
194 mem.set(Entry, self.source_section_index_lookup, .{});
195 mem.set(Entry, self.relocs_lookup, .{});
196196
197197 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
198198 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
......@@ -211,13 +211,25 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
211211 // is kind enough to specify the symbols in the correct order.
212212 sort.sort(SymbolAtIndex, sorted_all_syms.items, self, SymbolAtIndex.lessThan);
213213
214 var prev_sect_id: u8 = 0;
215 var section_index_lookup: ?Entry = null;
214216 for (sorted_all_syms.items, 0..) |sym_id, i| {
215217 const sym = sym_id.getSymbol(self);
216218
217 if (sym.sect() and self.source_section_index_lookup[sym.n_sect - 1] == -1) {
218 self.source_section_index_lookup[sym.n_sect - 1] = @intCast(i64, i);
219 if (section_index_lookup) |*lookup| {
220 if (sym.n_sect != prev_sect_id or sym.undf()) {
221 self.source_section_index_lookup[prev_sect_id - 1] = lookup.*;
222 section_index_lookup = null;
223 } else {
224 lookup.len += 1;
225 }
226 }
227 if (sym.sect() and section_index_lookup == null) {
228 section_index_lookup = .{ .start = @intCast(u32, i), .len = 1 };
219229 }
220230
231 prev_sect_id = sym.n_sect;
232
221233 self.symtab[i] = sym;
222234 self.source_symtab_lookup[i] = sym_id.index;
223235 self.reverse_symtab_lookup[sym_id.index] = @intCast(u32, i);
......@@ -234,13 +246,7 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
234246 self.unwind_info_sect_id = self.getSourceSectionIndexByName("__LD", "__compact_unwind");
235247 if (self.hasUnwindRecords()) {
236248 self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len);
237 mem.set(Record, self.unwind_relocs_lookup, .{
238 .dead = true,
239 .reloc = .{
240 .start = 0,
241 .len = 0,
242 },
243 });
249 mem.set(Record, self.unwind_relocs_lookup, .{ .dead = true, .reloc = .{} });
244250 }
245251}
246252
......@@ -620,7 +626,7 @@ fn filterRelocs(
620626 relocs: []align(1) const macho.relocation_info,
621627 start_addr: u64,
622628 end_addr: u64,
623) RelocEntry {
629) Entry {
624630 const Predicate = struct {
625631 addr: u64,
626632
......@@ -712,9 +718,9 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
712718
713719 while (try it.next()) |record| {
714720 const offset = it.pos - record.getSize();
715 const rel_pos = switch (cpu_arch) {
721 const rel_pos: Entry = switch (cpu_arch) {
716722 .aarch64 => filterRelocs(relocs, offset, offset + record.getSize()),
717 .x86_64 => RelocEntry{ .start = 0, .len = 0 },
723 .x86_64 => .{},
718724 else => unreachable,
719725 };
720726 self.eh_frame_relocs_lookup.putAssumeCapacityNoClobber(offset, .{
......@@ -990,13 +996,15 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
990996 };
991997
992998 if (sect_hint) |sect_id| {
993 if (self.source_section_index_lookup[sect_id] > -1) {
994 const first_sym_index = @intCast(usize, self.source_section_index_lookup[sect_id]);
995 const target_sym_index = @import("zld.zig").lsearch(i64, self.source_address_lookup[first_sym_index..], Predicate{
996 .addr = @intCast(i64, addr),
997 });
999 if (self.source_section_index_lookup[sect_id].len > 0) {
1000 const lookup = self.source_section_index_lookup[sect_id];
1001 const target_sym_index = @import("zld.zig").lsearch(
1002 i64,
1003 self.source_address_lookup[lookup.start..][0..lookup.len],
1004 Predicate{ .addr = @intCast(i64, addr) },
1005 );
9981006 if (target_sym_index > 0) {
999 return @intCast(u32, first_sym_index + target_sym_index - 1);
1007 return @intCast(u32, lookup.start + target_sym_index - 1);
10001008 }
10011009 }
10021010 return self.getSectionAliasSymbolIndex(sect_id);
src/link/MachO/ZldAtom.zig+2-1
......@@ -790,10 +790,11 @@ fn resolveRelocsX86(
790790 const target = parseRelocTarget(zld, atom_index, rel);
791791 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);
792792
793 log.debug(" RELA({s}) @ {x} => %{d} in object({?})", .{
793 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
794794 @tagName(rel_type),
795795 rel.r_address,
796796 target.sym_index,
797 zld.getSymbolName(target),
797798 target.getFile(),
798799 });
799800