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,...@@ -50,7 +50,7 @@ reverse_symtab_lookup: []u32 = undefined,
50/// Can be undefined as set together with in_symtab.50/// Can be undefined as set together with in_symtab.
51source_address_lookup: []i64 = undefined,51source_address_lookup: []i64 = undefined,
52/// Can be undefined as set together with in_symtab.52/// Can be undefined as set together with in_symtab.
53source_section_index_lookup: []i64 = undefined,53source_section_index_lookup: []Entry = undefined,
54/// Can be undefined as set together with in_symtab.54/// Can be undefined as set together with in_symtab.
55strtab_lookup: []u32 = undefined,55strtab_lookup: []u32 = undefined,
56/// Can be undefined as set together with in_symtab.56/// Can be undefined as set together with in_symtab.
...@@ -58,7 +58,7 @@ atom_by_index_table: []AtomIndex = undefined,...@@ -58,7 +58,7 @@ atom_by_index_table: []AtomIndex = undefined,
58/// Can be undefined as set together with in_symtab.58/// Can be undefined as set together with in_symtab.
59globals_lookup: []i64 = undefined,59globals_lookup: []i64 = undefined,
60/// Can be undefined as set together with in_symtab.60/// Can be undefined as set together with in_symtab.
61relocs_lookup: []RelocEntry = undefined,61relocs_lookup: []Entry = undefined,
6262
63/// All relocations sorted and flatened, sorted by address descending63/// All relocations sorted and flatened, sorted by address descending
64/// per section.64/// per section.
...@@ -81,11 +81,14 @@ unwind_info_sect_id: ?u8 = null,...@@ -81,11 +81,14 @@ unwind_info_sect_id: ?u8 = null,
81unwind_relocs_lookup: []Record = undefined,81unwind_relocs_lookup: []Record = undefined,
82unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{},82unwind_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
86const Record = struct {89const Record = struct {
87 dead: bool,90 dead: bool,
88 reloc: RelocEntry,91 reloc: Entry,
89};92};
9093
91pub fn deinit(self: *Object, gpa: Allocator) void {94pub fn deinit(self: *Object, gpa: Allocator) void {
...@@ -170,11 +173,11 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)...@@ -170,11 +173,11 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
170 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);173 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
171 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);174 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
172 self.atom_by_index_table = try allocator.alloc(AtomIndex, self.in_symtab.?.len + nsects);175 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);
174 // This is wasteful but we need to be able to lookup source symbol address after stripping and177 // This is wasteful but we need to be able to lookup source symbol address after stripping and
175 // allocating of sections.178 // allocating of sections.
176 self.source_address_lookup = try allocator.alloc(i64, self.in_symtab.?.len);179 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
179 for (self.symtab) |*sym| {182 for (self.symtab) |*sym| {
180 sym.* = .{183 sym.* = .{
...@@ -188,11 +191,8 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)...@@ -188,11 +191,8 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
188191
189 mem.set(i64, self.globals_lookup, -1);192 mem.set(i64, self.globals_lookup, -1);
190 mem.set(AtomIndex, self.atom_by_index_table, 0);193 mem.set(AtomIndex, self.atom_by_index_table, 0);
191 mem.set(i64, self.source_section_index_lookup, -1);194 mem.set(Entry, self.source_section_index_lookup, .{});
192 mem.set(RelocEntry, self.relocs_lookup, .{195 mem.set(Entry, self.relocs_lookup, .{});
193 .start = 0,
194 .len = 0,
195 });
196196
197 // You would expect that the symbol table is at least pre-sorted based on symbol's type:197 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
198 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,198 // 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)...@@ -211,13 +211,25 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
211 // is kind enough to specify the symbols in the correct order.211 // is kind enough to specify the symbols in the correct order.
212 sort.sort(SymbolAtIndex, sorted_all_syms.items, self, SymbolAtIndex.lessThan);212 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;
214 for (sorted_all_syms.items, 0..) |sym_id, i| {216 for (sorted_all_syms.items, 0..) |sym_id, i| {
215 const sym = sym_id.getSymbol(self);217 const sym = sym_id.getSymbol(self);
216218
217 if (sym.sect() and self.source_section_index_lookup[sym.n_sect - 1] == -1) {219 if (section_index_lookup) |*lookup| {
218 self.source_section_index_lookup[sym.n_sect - 1] = @intCast(i64, i);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 };
219 }229 }
220230
231 prev_sect_id = sym.n_sect;
232
221 self.symtab[i] = sym;233 self.symtab[i] = sym;
222 self.source_symtab_lookup[i] = sym_id.index;234 self.source_symtab_lookup[i] = sym_id.index;
223 self.reverse_symtab_lookup[sym_id.index] = @intCast(u32, i);235 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)...@@ -234,13 +246,7 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
234 self.unwind_info_sect_id = self.getSourceSectionIndexByName("__LD", "__compact_unwind");246 self.unwind_info_sect_id = self.getSourceSectionIndexByName("__LD", "__compact_unwind");
235 if (self.hasUnwindRecords()) {247 if (self.hasUnwindRecords()) {
236 self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len);248 self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len);
237 mem.set(Record, self.unwind_relocs_lookup, .{249 mem.set(Record, self.unwind_relocs_lookup, .{ .dead = true, .reloc = .{} });
238 .dead = true,
239 .reloc = .{
240 .start = 0,
241 .len = 0,
242 },
243 });
244 }250 }
245}251}
246252
...@@ -620,7 +626,7 @@ fn filterRelocs(...@@ -620,7 +626,7 @@ fn filterRelocs(
620 relocs: []align(1) const macho.relocation_info,626 relocs: []align(1) const macho.relocation_info,
621 start_addr: u64,627 start_addr: u64,
622 end_addr: u64,628 end_addr: u64,
623) RelocEntry {629) Entry {
624 const Predicate = struct {630 const Predicate = struct {
625 addr: u64,631 addr: u64,
626632
...@@ -712,9 +718,9 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -712,9 +718,9 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
712718
713 while (try it.next()) |record| {719 while (try it.next()) |record| {
714 const offset = it.pos - record.getSize();720 const offset = it.pos - record.getSize();
715 const rel_pos = switch (cpu_arch) {721 const rel_pos: Entry = switch (cpu_arch) {
716 .aarch64 => filterRelocs(relocs, offset, offset + record.getSize()),722 .aarch64 => filterRelocs(relocs, offset, offset + record.getSize()),
717 .x86_64 => RelocEntry{ .start = 0, .len = 0 },723 .x86_64 => .{},
718 else => unreachable,724 else => unreachable,
719 };725 };
720 self.eh_frame_relocs_lookup.putAssumeCapacityNoClobber(offset, .{726 self.eh_frame_relocs_lookup.putAssumeCapacityNoClobber(offset, .{
...@@ -990,13 +996,15 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {...@@ -990,13 +996,15 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
990 };996 };
991997
992 if (sect_hint) |sect_id| {998 if (sect_hint) |sect_id| {
993 if (self.source_section_index_lookup[sect_id] > -1) {999 if (self.source_section_index_lookup[sect_id].len > 0) {
994 const first_sym_index = @intCast(usize, self.source_section_index_lookup[sect_id]);1000 const lookup = 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{1001 const target_sym_index = @import("zld.zig").lsearch(
996 .addr = @intCast(i64, addr),1002 i64,
997 });1003 self.source_address_lookup[lookup.start..][0..lookup.len],
1004 Predicate{ .addr = @intCast(i64, addr) },
1005 );
998 if (target_sym_index > 0) {1006 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);
1000 }1008 }
1001 }1009 }
1002 return self.getSectionAliasSymbolIndex(sect_id);1010 return self.getSectionAliasSymbolIndex(sect_id);
src/link/MachO/ZldAtom.zig+2-1
...@@ -790,10 +790,11 @@ fn resolveRelocsX86(...@@ -790,10 +790,11 @@ fn resolveRelocsX86(
790 const target = parseRelocTarget(zld, atom_index, rel);790 const target = parseRelocTarget(zld, atom_index, rel);
791 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);791 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({?})", .{
794 @tagName(rel_type),794 @tagName(rel_type),
795 rel.r_address,795 rel.r_address,
796 target.sym_index,796 target.sym_index,
797 zld.getSymbolName(target),
797 target.getFile(),798 target.getFile(),
798 });799 });
799800