authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-01-20 18:26:21+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-01-20 18:43:16+01:00
log835a60a34fe2bc3d35e4524caee455a4743a5022
tree41e1c40e393397096209ee670304d2e7a510ec19
parent74b72a766de96c7c70fc8a02d3e2ee3cd353f225

zld: parse, synthesise and emit unwind records


9 files changed, 2211 insertions(+), 406 deletions(-)

CMakeLists.txt+2
...@@ -593,10 +593,12 @@ set(ZIG_STAGE2_SOURCES...@@ -593,10 +593,12 @@ set(ZIG_STAGE2_SOURCES
593 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"593 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
594 "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"594 "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
595 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"595 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
596 "${CMAKE_SOURCE_DIR}/src/link/MachO/UnwindInfo.zig"
596 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"597 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
597 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/bind.zig"598 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/bind.zig"
598 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/Rebase.zig"599 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/Rebase.zig"
599 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"600 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
601 "${CMAKE_SOURCE_DIR}/src/link/MachO/eh_frame.zig"
600 "${CMAKE_SOURCE_DIR}/src/link/MachO/fat.zig"602 "${CMAKE_SOURCE_DIR}/src/link/MachO/fat.zig"
601 "${CMAKE_SOURCE_DIR}/src/link/MachO/load_commands.zig"603 "${CMAKE_SOURCE_DIR}/src/link/MachO/load_commands.zig"
602 "${CMAKE_SOURCE_DIR}/src/link/MachO/thunks.zig"604 "${CMAKE_SOURCE_DIR}/src/link/MachO/thunks.zig"
src/link.zig+3
...@@ -697,6 +697,7 @@ pub const File = struct {...@@ -697,6 +697,7 @@ pub const File = struct {
697 /// TODO audit this error set. most of these should be collapsed into one error,697 /// TODO audit this error set. most of these should be collapsed into one error,
698 /// and ErrorFlags should be updated to convey the meaning to the user.698 /// and ErrorFlags should be updated to convey the meaning to the user.
699 pub const FlushError = error{699 pub const FlushError = error{
700 BadDwarfCfi,
700 CacheUnavailable,701 CacheUnavailable,
701 CurrentWorkingDirectoryUnlinked,702 CurrentWorkingDirectoryUnlinked,
702 DivisionByZero,703 DivisionByZero,
...@@ -737,6 +738,8 @@ pub const File = struct {...@@ -737,6 +738,8 @@ pub const File = struct {
737 MissingEndForExpression,738 MissingEndForExpression,
738 /// TODO: this should be removed from the error set in favor of using ErrorFlags739 /// TODO: this should be removed from the error set in favor of using ErrorFlags
739 MissingMainEntrypoint,740 MissingMainEntrypoint,
741 /// TODO: this should be removed from the error set in favor of using ErrorFlags
742 MissingSection,
740 MissingSymbol,743 MissingSymbol,
741 MissingTableSymbols,744 MissingTableSymbols,
742 ModuleNameMismatch,745 ModuleNameMismatch,
src/link/MachO/Object.zig+403-80
...@@ -8,6 +8,7 @@ const std = @import("std");...@@ -8,6 +8,7 @@ const std = @import("std");
8const build_options = @import("build_options");8const build_options = @import("build_options");
9const assert = std.debug.assert;9const assert = std.debug.assert;
10const dwarf = std.dwarf;10const dwarf = std.dwarf;
11const eh_frame = @import("eh_frame.zig");
11const fs = std.fs;12const fs = std.fs;
12const io = std.io;13const io = std.io;
13const log = std.log.scoped(.link);14const log = std.log.scoped(.link);
...@@ -24,6 +25,7 @@ const DwarfInfo = @import("DwarfInfo.zig");...@@ -24,6 +25,7 @@ const DwarfInfo = @import("DwarfInfo.zig");
24const LoadCommandIterator = macho.LoadCommandIterator;25const LoadCommandIterator = macho.LoadCommandIterator;
25const Zld = @import("zld.zig").Zld;26const Zld = @import("zld.zig").Zld;
26const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;27const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
28const UnwindInfo = @import("UnwindInfo.zig");
2729
28name: []const u8,30name: []const u8,
29mtime: u64,31mtime: u64,
...@@ -44,6 +46,8 @@ symtab: []macho.nlist_64 = undefined,...@@ -44,6 +46,8 @@ symtab: []macho.nlist_64 = undefined,
44/// Can be undefined as set together with in_symtab.46/// Can be undefined as set together with in_symtab.
45source_symtab_lookup: []u32 = undefined,47source_symtab_lookup: []u32 = undefined,
46/// Can be undefined as set together with in_symtab.48/// Can be undefined as set together with in_symtab.
49reverse_symtab_lookup: []u32 = undefined,
50/// Can be undefined as set together with in_symtab.
47source_address_lookup: []i64 = undefined,51source_address_lookup: []i64 = undefined,
48/// Can be undefined as set together with in_symtab.52/// Can be undefined as set together with in_symtab.
49source_section_index_lookup: []i64 = undefined,53source_section_index_lookup: []i64 = undefined,
...@@ -53,22 +57,49 @@ strtab_lookup: []u32 = undefined,...@@ -53,22 +57,49 @@ strtab_lookup: []u32 = undefined,
53atom_by_index_table: []AtomIndex = undefined,57atom_by_index_table: []AtomIndex = undefined,
54/// Can be undefined as set together with in_symtab.58/// Can be undefined as set together with in_symtab.
55globals_lookup: []i64 = undefined,59globals_lookup: []i64 = undefined,
60/// Can be undefined as set together with in_symtab.
61relocs_lookup: []RelocEntry = undefined,
5662
57atoms: std.ArrayListUnmanaged(AtomIndex) = .{},63atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
64exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
65
66eh_frame_sect: ?macho.section_64 = null,
67eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{},
68eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(AtomIndex, u32) = .{},
69
70unwind_info_sect: ?macho.section_64 = null,
71unwind_relocs_lookup: []Record = undefined,
72unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{},
73
74const RelocEntry = struct { start: u32, len: u32 };
75
76const Record = struct {
77 dead: bool,
78 reloc: RelocEntry,
79};
5880
59pub fn deinit(self: *Object, gpa: Allocator) void {81pub fn deinit(self: *Object, gpa: Allocator) void {
60 self.atoms.deinit(gpa);82 self.atoms.deinit(gpa);
83 self.exec_atoms.deinit(gpa);
61 gpa.free(self.name);84 gpa.free(self.name);
62 gpa.free(self.contents);85 gpa.free(self.contents);
63 if (self.in_symtab) |_| {86 if (self.in_symtab) |_| {
64 gpa.free(self.source_symtab_lookup);87 gpa.free(self.source_symtab_lookup);
88 gpa.free(self.reverse_symtab_lookup);
65 gpa.free(self.source_address_lookup);89 gpa.free(self.source_address_lookup);
66 gpa.free(self.source_section_index_lookup);90 gpa.free(self.source_section_index_lookup);
67 gpa.free(self.strtab_lookup);91 gpa.free(self.strtab_lookup);
68 gpa.free(self.symtab);92 gpa.free(self.symtab);
69 gpa.free(self.atom_by_index_table);93 gpa.free(self.atom_by_index_table);
70 gpa.free(self.globals_lookup);94 gpa.free(self.globals_lookup);
95 gpa.free(self.relocs_lookup);
71 }96 }
97 self.eh_frame_relocs_lookup.deinit(gpa);
98 self.eh_frame_records_lookup.deinit(gpa);
99 if (self.hasUnwindRecords()) {
100 gpa.free(self.unwind_relocs_lookup);
101 }
102 self.unwind_records_lookup.deinit(gpa);
72}103}
73104
74pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {105pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
...@@ -105,76 +136,95 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)...@@ -105,76 +136,95 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
105 .ncmds = self.header.ncmds,136 .ncmds = self.header.ncmds,
106 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],137 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
107 };138 };
108 while (it.next()) |cmd| {139 const nsects = self.getSourceSections().len;
109 switch (cmd.cmd()) {140 const symtab = while (it.next()) |cmd| switch (cmd.cmd()) {
110 .SYMTAB => {141 .SYMTAB => break cmd.cast(macho.symtab_command).?,
111 const symtab = cmd.cast(macho.symtab_command).?;142 else => {},
112 self.in_symtab = @ptrCast(143 } else return;
113 [*]const macho.nlist_64,144
114 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),145 self.in_symtab = @ptrCast(
115 )[0..symtab.nsyms];146 [*]const macho.nlist_64,
116 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];147 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
117148 )[0..symtab.nsyms];
118 const nsects = self.getSourceSections().len;149 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
119150
120 self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects);151 self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects);
121 self.source_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);152 self.source_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
122 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);153 self.reverse_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
123 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);154 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
124 self.atom_by_index_table = try allocator.alloc(AtomIndex, self.in_symtab.?.len + nsects);155 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
125 // This is wasteful but we need to be able to lookup source symbol address after stripping and156 self.atom_by_index_table = try allocator.alloc(AtomIndex, self.in_symtab.?.len + nsects);
126 // allocating of sections.157 self.relocs_lookup = try allocator.alloc(RelocEntry, self.in_symtab.?.len + nsects);
127 self.source_address_lookup = try allocator.alloc(i64, self.in_symtab.?.len);158 // This is wasteful but we need to be able to lookup source symbol address after stripping and
128 self.source_section_index_lookup = try allocator.alloc(i64, nsects);159 // allocating of sections.
129160 self.source_address_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
130 for (self.symtab) |*sym| {161 self.source_section_index_lookup = try allocator.alloc(i64, nsects);
131 sym.* = .{162
132 .n_value = 0,163 for (self.symtab) |*sym| {
133 .n_sect = 0,164 sym.* = .{
134 .n_desc = 0,165 .n_value = 0,
135 .n_strx = 0,166 .n_sect = 0,
136 .n_type = 0,167 .n_desc = 0,
137 };168 .n_strx = 0,
138 }169 .n_type = 0,
170 };
171 }
139172
140 mem.set(i64, self.globals_lookup, -1);173 mem.set(i64, self.globals_lookup, -1);
141 mem.set(AtomIndex, self.atom_by_index_table, 0);174 mem.set(AtomIndex, self.atom_by_index_table, 0);
142 mem.set(i64, self.source_section_index_lookup, -1);175 mem.set(i64, self.source_section_index_lookup, -1);
176 mem.set(RelocEntry, self.relocs_lookup, .{
177 .start = 0,
178 .len = 0,
179 });
143180
144 // You would expect that the symbol table is at least pre-sorted based on symbol's type:181 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
145 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,182 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
146 // the GO compiler does not necessarily respect that therefore we sort immediately by type183 // the GO compiler does not necessarily respect that therefore we sort immediately by type
147 // and address within.184 // and address within.
148 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(allocator, self.in_symtab.?.len);185 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(allocator, self.in_symtab.?.len);
149 defer sorted_all_syms.deinit();186 defer sorted_all_syms.deinit();
150187
151 for (self.in_symtab.?) |_, index| {188 for (self.in_symtab.?) |_, index| {
152 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });189 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
153 }190 }
154191
155 // We sort by type: defined < undefined, and192 // We sort by type: defined < undefined, and
156 // afterwards by address in each group. Normally, dysymtab should193 // afterwards by address in each group. Normally, dysymtab should
157 // be enough to guarantee the sort, but turns out not every compiler194 // be enough to guarantee the sort, but turns out not every compiler
158 // is kind enough to specify the symbols in the correct order.195 // is kind enough to specify the symbols in the correct order.
159 sort.sort(SymbolAtIndex, sorted_all_syms.items, self, SymbolAtIndex.lessThan);196 sort.sort(SymbolAtIndex, sorted_all_syms.items, self, SymbolAtIndex.lessThan);
160197
161 for (sorted_all_syms.items) |sym_id, i| {198 for (sorted_all_syms.items) |sym_id, i| {
162 const sym = sym_id.getSymbol(self);199 const sym = sym_id.getSymbol(self);
163200
164 if (sym.sect() and self.source_section_index_lookup[sym.n_sect - 1] == -1) {201 if (sym.sect() and self.source_section_index_lookup[sym.n_sect - 1] == -1) {
165 self.source_section_index_lookup[sym.n_sect - 1] = @intCast(i64, i);202 self.source_section_index_lookup[sym.n_sect - 1] = @intCast(i64, i);
166 }203 }
167204
168 self.symtab[i] = sym;205 self.symtab[i] = sym;
169 self.source_symtab_lookup[i] = sym_id.index;206 self.source_symtab_lookup[i] = sym_id.index;
170 self.source_address_lookup[i] = if (sym.undf()) -1 else @intCast(i64, sym.n_value);207 self.reverse_symtab_lookup[sym_id.index] = @intCast(u32, i);
208 self.source_address_lookup[i] = if (sym.undf()) -1 else @intCast(i64, sym.n_value);
171209
172 const sym_name_len = mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.?.ptr + sym.n_strx), 0).len + 1;210 const sym_name_len = mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.?.ptr + sym.n_strx), 0).len + 1;
173 self.strtab_lookup[i] = @intCast(u32, sym_name_len);211 self.strtab_lookup[i] = @intCast(u32, sym_name_len);
174 }212 }
213
214 // Parse __TEXT,__eh_frame header if one exists
215 self.eh_frame_sect = self.getSourceSectionByName("__TEXT", "__eh_frame");
216
217 // Parse __LD,__compact_unwind header if one exists
218 self.unwind_info_sect = self.getSourceSectionByName("__LD", "__compact_unwind");
219 if (self.hasUnwindRecords()) {
220 self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len);
221 mem.set(Record, self.unwind_relocs_lookup, .{
222 .dead = true,
223 .reloc = .{
224 .start = 0,
225 .len = 0,
175 },226 },
176 else => {},227 });
177 }
178 }228 }
179}229}
180230
...@@ -295,14 +345,20 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b...@@ -295,14 +345,20 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b
295 return lhs.header.addr < rhs.header.addr;345 return lhs.header.addr < rhs.header.addr;
296}346}
297347
298/// Splits input sections into Atoms.348pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void {
349 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
350
351 try self.splitRegularSections(zld, object_id);
352 try self.parseEhFrameSection(zld, object_id);
353 try self.parseUnwindInfo(zld, object_id);
354}
355
356/// Splits input regular sections into Atoms.
299/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section357/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section
300/// into subsections where each subsection then represents an Atom.358/// into subsections where each subsection then represents an Atom.
301pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {359pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
302 const gpa = zld.gpa;360 const gpa = zld.gpa;
303361
304 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
305
306 const sections = self.getSourceSections();362 const sections = self.getSourceSections();
307 for (sections) |sect, id| {363 for (sections) |sect, id| {
308 if (sect.isDebug()) continue;364 if (sect.isDebug()) continue;
...@@ -418,6 +474,9 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {...@@ -418,6 +474,9 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
418 sect.@"align",474 sect.@"align",
419 out_sect_id,475 out_sect_id,
420 );476 );
477 if (!sect.isZerofill()) {
478 try self.cacheRelocs(zld, atom_index);
479 }
421 zld.addAtomToSection(atom_index);480 zld.addAtomToSection(atom_index);
422 }481 }
423482
...@@ -431,7 +490,6 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {...@@ -431,7 +490,6 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
431 const nsyms_trailing = atom_loc.len - 1;490 const nsyms_trailing = atom_loc.len - 1;
432 next_sym_index += atom_loc.len;491 next_sym_index += atom_loc.len;
433492
434 // TODO: We want to bubble up the first externally defined symbol here.
435 const atom_size = if (next_sym_index < sect_start_index + sect_loc.len)493 const atom_size = if (next_sym_index < sect_start_index + sect_loc.len)
436 symtab[next_sym_index].n_value - addr494 symtab[next_sym_index].n_value - addr
437 else495 else
...@@ -461,7 +519,9 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {...@@ -461,7 +519,9 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
461 const alias_index = self.getSectionAliasSymbolIndex(sect_id);519 const alias_index = self.getSectionAliasSymbolIndex(sect_id);
462 self.atom_by_index_table[alias_index] = atom_index;520 self.atom_by_index_table[alias_index] = atom_index;
463 }521 }
464522 if (!sect.isZerofill()) {
523 try self.cacheRelocs(zld, atom_index);
524 }
465 zld.addAtomToSection(atom_index);525 zld.addAtomToSection(atom_index);
466 }526 }
467 } else {527 } else {
...@@ -476,6 +536,9 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {...@@ -476,6 +536,9 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
476 sect.@"align",536 sect.@"align",
477 out_sect_id,537 out_sect_id,
478 );538 );
539 if (!sect.isZerofill()) {
540 try self.cacheRelocs(zld, atom_index);
541 }
479 zld.addAtomToSection(atom_index);542 zld.addAtomToSection(atom_index);
480 }543 }
481 }544 }
...@@ -484,7 +547,7 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {...@@ -484,7 +547,7 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
484fn createAtomFromSubsection(547fn createAtomFromSubsection(
485 self: *Object,548 self: *Object,
486 zld: *Zld,549 zld: *Zld,
487 object_id: u31,550 object_id: u32,
488 sym_index: u32,551 sym_index: u32,
489 inner_sym_index: u32,552 inner_sym_index: u32,
490 inner_nsyms_trailing: u32,553 inner_nsyms_trailing: u32,
...@@ -497,7 +560,7 @@ fn createAtomFromSubsection(...@@ -497,7 +560,7 @@ fn createAtomFromSubsection(
497 const atom = zld.getAtomPtr(atom_index);560 const atom = zld.getAtomPtr(atom_index);
498 atom.inner_sym_index = inner_sym_index;561 atom.inner_sym_index = inner_sym_index;
499 atom.inner_nsyms_trailing = inner_nsyms_trailing;562 atom.inner_nsyms_trailing = inner_nsyms_trailing;
500 atom.file = object_id;563 atom.file = object_id + 1;
501 self.symtab[sym_index].n_sect = out_sect_id + 1;564 self.symtab[sym_index].n_sect = out_sect_id + 1;
502565
503 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{566 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
...@@ -519,9 +582,208 @@ fn createAtomFromSubsection(...@@ -519,9 +582,208 @@ fn createAtomFromSubsection(
519 self.atom_by_index_table[sym_loc.sym_index] = atom_index;582 self.atom_by_index_table[sym_loc.sym_index] = atom_index;
520 }583 }
521584
585 const out_sect = zld.sections.items(.header)[out_sect_id];
586 if (out_sect.isCode() and
587 mem.eql(u8, "__TEXT", out_sect.segName()) and
588 mem.eql(u8, "__text", out_sect.sectName()))
589 {
590 // TODO currently assuming a single section for executable machine code
591 try self.exec_atoms.append(gpa, atom_index);
592 }
593
522 return atom_index;594 return atom_index;
523}595}
524596
597fn filterRelocs(
598 relocs: []align(1) const macho.relocation_info,
599 start_addr: u64,
600 end_addr: u64,
601) RelocEntry {
602 const Predicate = struct {
603 addr: u64,
604
605 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
606 return rel.r_address >= self.addr;
607 }
608 };
609 const LPredicate = struct {
610 addr: u64,
611
612 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
613 return rel.r_address < self.addr;
614 }
615 };
616
617 const start = @import("zld.zig").bsearch(macho.relocation_info, relocs, Predicate{ .addr = end_addr });
618 const len = @import("zld.zig").lsearch(macho.relocation_info, relocs[start..], LPredicate{ .addr = start_addr });
619
620 return .{ .start = @intCast(u32, start), .len = @intCast(u32, len) };
621}
622
623fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {
624 const atom = zld.getAtom(atom_index);
625
626 const source_sect = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
627 const source_sect = self.getSourceSection(source_sym.n_sect - 1);
628 assert(!source_sect.isZerofill());
629 break :blk source_sect;
630 } else blk: {
631 // If there was no matching symbol present in the source symtab, this means
632 // we are dealing with either an entire section, or part of it, but also
633 // starting at the beginning.
634 const nbase = @intCast(u32, self.in_symtab.?.len);
635 const sect_id = @intCast(u16, atom.sym_index - nbase);
636 const source_sect = self.getSourceSection(sect_id);
637 assert(!source_sect.isZerofill());
638 break :blk source_sect;
639 };
640
641 const relocs = self.getRelocs(source_sect);
642
643 self.relocs_lookup[atom.sym_index] = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
644 const offset = source_sym.n_value - source_sect.addr;
645 break :blk filterRelocs(relocs, offset, offset + atom.size);
646 } else filterRelocs(relocs, 0, atom.size);
647}
648
649fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
650 const sect = self.eh_frame_sect orelse return;
651
652 log.debug("parsing __TEXT,__eh_frame section", .{});
653
654 if (zld.getSectionByName("__TEXT", "__eh_frame") == null) {
655 _ = try zld.initSection("__TEXT", "__eh_frame", .{});
656 }
657
658 const gpa = zld.gpa;
659 const cpu_arch = zld.options.target.cpu.arch;
660 const relocs = self.getRelocs(sect);
661
662 var it = self.getEhFrameRecordsIterator();
663 var record_count: u32 = 0;
664 while (try it.next()) |_| {
665 record_count += 1;
666 }
667
668 try self.eh_frame_relocs_lookup.ensureTotalCapacity(gpa, record_count);
669 try self.eh_frame_records_lookup.ensureTotalCapacity(gpa, record_count);
670
671 it.reset();
672
673 while (try it.next()) |record| {
674 const offset = it.pos - record.getSize();
675 const rel_pos = switch (cpu_arch) {
676 .aarch64 => filterRelocs(relocs, offset, offset + record.getSize()),
677 .x86_64 => RelocEntry{ .start = 0, .len = 0 },
678 else => unreachable,
679 };
680 self.eh_frame_relocs_lookup.putAssumeCapacityNoClobber(offset, .{
681 .dead = false,
682 .reloc = rel_pos,
683 });
684
685 if (record.tag == .fde) {
686 const target = blk: {
687 switch (cpu_arch) {
688 .aarch64 => {
689 assert(rel_pos.len > 0); // TODO convert to an error as the FDE eh frame is malformed
690 // Find function symbol that this record describes
691 const rel = relocs[rel_pos.start..][rel_pos.len - 1];
692 const target = UnwindInfo.parseRelocTarget(
693 zld,
694 object_id,
695 rel,
696 it.data[offset..],
697 @intCast(i32, offset),
698 );
699 break :blk target;
700 },
701 .x86_64 => {
702 const target_address = record.getTargetSymbolAddress(.{
703 .base_addr = sect.addr,
704 .base_offset = offset,
705 });
706 const target_sym_index = self.getSymbolByAddress(target_address, null);
707 const target = if (self.getGlobal(target_sym_index)) |global_index|
708 zld.globals.items[global_index]
709 else
710 SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 };
711 break :blk target;
712 },
713 else => unreachable,
714 }
715 };
716 log.debug("FDE at offset {x} tracks {s}", .{ offset, zld.getSymbolName(target) });
717 if (target.getFile() != object_id) {
718 self.eh_frame_relocs_lookup.getPtr(offset).?.dead = true;
719 } else {
720 const atom_index = self.getAtomIndexForSymbol(target.sym_index).?;
721 self.eh_frame_records_lookup.putAssumeCapacityNoClobber(atom_index, offset);
722 }
723 }
724 }
725}
726
727fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
728 const sect = self.unwind_info_sect orelse return;
729
730 log.debug("parsing unwind info in {s}", .{self.name});
731
732 const gpa = zld.gpa;
733 const cpu_arch = zld.options.target.cpu.arch;
734
735 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
736 _ = try zld.initSection("__TEXT", "__unwind_info", .{});
737 }
738
739 try self.unwind_records_lookup.ensureTotalCapacity(gpa, @intCast(u32, self.exec_atoms.items.len));
740
741 const unwind_records = self.getUnwindRecords();
742
743 const needs_eh_frame = for (unwind_records) |record| {
744 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;
745 } else false;
746
747 if (needs_eh_frame) {
748 if (self.eh_frame_sect == null) {
749 log.err("missing __TEXT,__eh_frame section", .{});
750 log.err(" in object {s}", .{self.name});
751 return error.MissingSection;
752 }
753 }
754
755 const relocs = self.getRelocs(sect);
756 for (unwind_records) |record, record_id| {
757 const offset = record_id * @sizeOf(macho.compact_unwind_entry);
758 const rel_pos = filterRelocs(
759 relocs,
760 offset,
761 offset + @sizeOf(macho.compact_unwind_entry),
762 );
763 assert(rel_pos.len > 0); // TODO convert to an error as the unwind info is malformed
764 self.unwind_relocs_lookup[record_id] = .{
765 .dead = false,
766 .reloc = rel_pos,
767 };
768
769 // Find function symbol that this record describes
770 const rel = relocs[rel_pos.start..][rel_pos.len - 1];
771 const target = UnwindInfo.parseRelocTarget(
772 zld,
773 object_id,
774 rel,
775 mem.asBytes(&record),
776 @intCast(i32, offset),
777 );
778 if (target.getFile() != object_id) {
779 self.unwind_relocs_lookup[record_id].dead = true;
780 } else {
781 const atom_index = self.getAtomIndexForSymbol(target.sym_index).?;
782 self.unwind_records_lookup.putAssumeCapacityNoClobber(atom_index, @intCast(u32, record_id));
783 }
784 }
785}
786
525pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {787pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
526 const symtab = self.in_symtab.?;788 const symtab = self.in_symtab.?;
527 if (index >= symtab.len) return null;789 if (index >= symtab.len) return null;
...@@ -529,23 +791,28 @@ pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {...@@ -529,23 +791,28 @@ pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
529 return symtab[mapped_index];791 return symtab[mapped_index];
530}792}
531793
532/// Expects an arena allocator.
533/// Caller owns memory.
534pub fn createReverseSymbolLookup(self: Object, arena: Allocator) ![]u32 {
535 const symtab = self.in_symtab orelse return &[0]u32{};
536 const lookup = try arena.alloc(u32, symtab.len);
537 for (self.source_symtab_lookup) |source_id, id| {
538 lookup[source_id] = @intCast(u32, id);
539 }
540 return lookup;
541}
542
543pub fn getSourceSection(self: Object, index: u16) macho.section_64 {794pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
544 const sections = self.getSourceSections();795 const sections = self.getSourceSections();
545 assert(index < sections.len);796 assert(index < sections.len);
546 return sections[index];797 return sections[index];
547}798}
548799
800pub fn getSourceSectionByName(self: Object, segname: []const u8, sectname: []const u8) ?macho.section_64 {
801 const sections = self.getSourceSections();
802 for (sections) |sect| {
803 if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName()))
804 return sect;
805 } else return null;
806}
807
808pub fn getSourceSectionIndexByName(self: Object, segname: []const u8, sectname: []const u8) ?u8 {
809 const sections = self.getSourceSections();
810 for (sections) |sect, i| {
811 if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName()))
812 return @intCast(u8, i + 1);
813 } else return null;
814}
815
549pub fn getSourceSections(self: Object) []const macho.section_64 {816pub fn getSourceSections(self: Object) []const macho.section_64 {
550 var it = LoadCommandIterator{817 var it = LoadCommandIterator{
551 .ncmds = self.header.ncmds,818 .ncmds = self.header.ncmds,
...@@ -652,8 +919,64 @@ pub fn getSymbolName(self: Object, index: u32) []const u8 {...@@ -652,8 +919,64 @@ pub fn getSymbolName(self: Object, index: u32) []const u8 {
652 return strtab[start..][0 .. len - 1 :0];919 return strtab[start..][0 .. len - 1 :0];
653}920}
654921
922pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
923 // Find containing atom
924 const Predicate = struct {
925 addr: i64,
926
927 pub fn predicate(pred: @This(), other: i64) bool {
928 return if (other == -1) true else other > pred.addr;
929 }
930 };
931
932 if (sect_hint) |sect_id| {
933 if (self.source_section_index_lookup[sect_id] > -1) {
934 const first_sym_index = @intCast(usize, self.source_section_index_lookup[sect_id]);
935 const target_sym_index = @import("zld.zig").lsearch(i64, self.source_address_lookup[first_sym_index..], Predicate{
936 .addr = @intCast(i64, addr),
937 });
938 if (target_sym_index > 0) {
939 return @intCast(u32, first_sym_index + target_sym_index - 1);
940 }
941 }
942 return self.getSectionAliasSymbolIndex(sect_id);
943 }
944
945 const target_sym_index = @import("zld.zig").lsearch(i64, self.source_address_lookup, Predicate{
946 .addr = @intCast(i64, addr),
947 });
948 assert(target_sym_index > 0);
949 return @intCast(u32, target_sym_index - 1);
950}
951
952pub fn getGlobal(self: Object, sym_index: u32) ?u32 {
953 if (self.globals_lookup[sym_index] == -1) return null;
954 return @intCast(u32, self.globals_lookup[sym_index]);
955}
956
655pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {957pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {
656 const atom_index = self.atom_by_index_table[sym_index];958 const atom_index = self.atom_by_index_table[sym_index];
657 if (atom_index == 0) return null;959 if (atom_index == 0) return null;
658 return atom_index;960 return atom_index;
659}961}
962
963pub fn hasUnwindRecords(self: Object) bool {
964 return self.unwind_info_sect != null;
965}
966
967pub fn getUnwindRecords(self: Object) []align(1) const macho.compact_unwind_entry {
968 const sect = self.unwind_info_sect orelse return &[0]macho.compact_unwind_entry{};
969 const data = self.getSectionContents(sect);
970 const num_entries = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
971 return @ptrCast([*]align(1) const macho.compact_unwind_entry, data)[0..num_entries];
972}
973
974pub fn hasEhFrameRecords(self: Object) bool {
975 return self.eh_frame_sect != null;
976}
977
978pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {
979 const sect = self.eh_frame_sect orelse return .{ .data = &[0]u8{} };
980 const data = self.getSectionContents(sect);
981 return .{ .data = data };
982}
src/link/MachO/UnwindInfo.zig created+831
...@@ -0,0 +1,831 @@
1const UnwindInfo = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const eh_frame = @import("eh_frame.zig");
6const fs = std.fs;
7const leb = std.leb;
8const log = std.log.scoped(.unwind_info);
9const macho = std.macho;
10const math = std.math;
11const mem = std.mem;
12const trace = @import("../../tracy.zig").trace;
13
14const Allocator = mem.Allocator;
15const Atom = @import("ZldAtom.zig");
16const AtomIndex = @import("zld.zig").AtomIndex;
17const EhFrameRecord = eh_frame.EhFrameRecord;
18const Object = @import("Object.zig");
19const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
20const Zld = @import("zld.zig").Zld;
21
22const N_DEAD = @import("zld.zig").N_DEAD;
23
24gpa: Allocator,
25
26/// List of all unwind records gathered from all objects and sorted
27/// by source function address.
28records: std.ArrayListUnmanaged(macho.compact_unwind_entry) = .{},
29records_lookup: std.AutoHashMapUnmanaged(AtomIndex, RecordIndex) = .{},
30
31/// List of all personalities referenced by either unwind info entries
32/// or __eh_frame entries.
33personalities: [max_personalities]SymbolWithLoc = undefined,
34personalities_count: u2 = 0,
35
36/// List of common encodings sorted in descending order with the most common first.
37common_encodings: [max_common_encodings]macho.compact_unwind_encoding_t = undefined,
38common_encodings_count: u7 = 0,
39
40/// List of record indexes containing an LSDA pointer.
41lsdas: std.ArrayListUnmanaged(RecordIndex) = .{},
42lsdas_lookup: std.AutoHashMapUnmanaged(RecordIndex, u32) = .{},
43
44/// List of second level pages.
45pages: std.ArrayListUnmanaged(Page) = .{},
46
47const RecordIndex = u32;
48
49const max_personalities = 3;
50const max_common_encodings = 127;
51const max_compact_encodings = 256;
52
53const second_level_page_bytes = 0x1000;
54const second_level_page_words = second_level_page_bytes / @sizeOf(u32);
55
56const max_regular_second_level_entries =
57 (second_level_page_bytes - @sizeOf(macho.unwind_info_regular_second_level_page_header)) /
58 @sizeOf(macho.unwind_info_regular_second_level_entry);
59
60const max_compressed_second_level_entries =
61 (second_level_page_bytes - @sizeOf(macho.unwind_info_compressed_second_level_page_header)) /
62 @sizeOf(u32);
63
64const compressed_entry_func_offset_mask = ~@as(u24, 0);
65
66const Page = struct {
67 kind: enum { regular, compressed },
68 start: RecordIndex,
69 count: u16,
70 page_encodings: [max_compact_encodings]RecordIndex = undefined,
71 page_encodings_count: u8 = 0,
72
73 fn appendPageEncoding(page: *Page, record_id: RecordIndex) void {
74 assert(page.page_encodings_count <= max_compact_encodings);
75 page.page_encodings[page.page_encodings_count] = record_id;
76 page.page_encodings_count += 1;
77 }
78
79 fn getPageEncoding(
80 page: *const Page,
81 info: *const UnwindInfo,
82 enc: macho.compact_unwind_encoding_t,
83 ) ?u8 {
84 comptime var index: u8 = 0;
85 inline while (index < max_compact_encodings) : (index += 1) {
86 if (index >= page.page_encodings_count) return null;
87 const record_id = page.page_encodings[index];
88 const record = info.records.items[record_id];
89 if (record.compactUnwindEncoding == enc) {
90 return index;
91 }
92 }
93 return null;
94 }
95
96 fn format(
97 page: *const Page,
98 comptime unused_format_string: []const u8,
99 options: std.fmt.FormatOptions,
100 writer: anytype,
101 ) !void {
102 _ = page;
103 _ = unused_format_string;
104 _ = options;
105 _ = writer;
106 @compileError("do not format Page directly; use page.fmtDebug()");
107 }
108
109 const DumpCtx = struct {
110 page: *const Page,
111 info: *const UnwindInfo,
112 };
113
114 fn dump(
115 ctx: DumpCtx,
116 comptime unused_format_string: []const u8,
117 options: std.fmt.FormatOptions,
118 writer: anytype,
119 ) @TypeOf(writer).Error!void {
120 _ = options;
121 comptime assert(unused_format_string.len == 0);
122 try writer.writeAll("Page:\n");
123 try writer.print(" kind: {s}\n", .{@tagName(ctx.page.kind)});
124 try writer.print(" entries: {d} - {d}\n", .{
125 ctx.page.start,
126 ctx.page.start + ctx.page.count,
127 });
128 try writer.print(" encodings (count = {d})\n", .{ctx.page.page_encodings_count});
129 for (ctx.page.page_encodings[0..ctx.page.page_encodings_count]) |record_id, i| {
130 const record = ctx.info.records.items[record_id];
131 const enc = record.compactUnwindEncoding;
132 try writer.print(" {d}: 0x{x:0>8}\n", .{ ctx.info.common_encodings_count + i, enc });
133 }
134 }
135
136 fn fmtDebug(page: *const Page, info: *const UnwindInfo) std.fmt.Formatter(dump) {
137 return .{ .data = .{
138 .page = page,
139 .info = info,
140 } };
141 }
142
143 fn write(page: *const Page, info: *const UnwindInfo, writer: anytype) !void {
144 switch (page.kind) {
145 .regular => {
146 try writer.writeStruct(macho.unwind_info_regular_second_level_page_header{
147 .entryPageOffset = @sizeOf(macho.unwind_info_regular_second_level_page_header),
148 .entryCount = page.count,
149 });
150
151 for (info.records.items[page.start..][0..page.count]) |record| {
152 try writer.writeStruct(macho.unwind_info_regular_second_level_entry{
153 .functionOffset = @intCast(u32, record.rangeStart),
154 .encoding = record.compactUnwindEncoding,
155 });
156 }
157 },
158 .compressed => {
159 const entry_offset = @sizeOf(macho.unwind_info_compressed_second_level_page_header) +
160 @intCast(u16, page.page_encodings_count) * @sizeOf(u32);
161 try writer.writeStruct(macho.unwind_info_compressed_second_level_page_header{
162 .entryPageOffset = entry_offset,
163 .entryCount = page.count,
164 .encodingsPageOffset = @sizeOf(
165 macho.unwind_info_compressed_second_level_page_header,
166 ),
167 .encodingsCount = page.page_encodings_count,
168 });
169
170 for (page.page_encodings[0..page.page_encodings_count]) |record_id| {
171 const enc = info.records.items[record_id].compactUnwindEncoding;
172 try writer.writeIntLittle(u32, enc);
173 }
174
175 assert(page.count > 0);
176 const first_entry = info.records.items[page.start];
177 for (info.records.items[page.start..][0..page.count]) |record| {
178 const enc_index = blk: {
179 if (info.getCommonEncoding(record.compactUnwindEncoding)) |id| {
180 break :blk id;
181 }
182 const ncommon = info.common_encodings_count;
183 break :blk ncommon + page.getPageEncoding(info, record.compactUnwindEncoding).?;
184 };
185 const compressed = macho.UnwindInfoCompressedEntry{
186 .funcOffset = @intCast(u24, record.rangeStart - first_entry.rangeStart),
187 .encodingIndex = @intCast(u8, enc_index),
188 };
189 try writer.writeStruct(compressed);
190 }
191 },
192 }
193 }
194};
195
196pub fn deinit(info: *UnwindInfo) void {
197 info.records.deinit(info.gpa);
198 info.records_lookup.deinit(info.gpa);
199 info.pages.deinit(info.gpa);
200 info.lsdas.deinit(info.gpa);
201 info.lsdas_lookup.deinit(info.gpa);
202}
203
204pub fn scanRelocs(zld: *Zld) !void {
205 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) return;
206
207 const cpu_arch = zld.options.target.cpu.arch;
208 for (zld.objects.items) |*object, object_id| {
209 const unwind_records = object.getUnwindRecords();
210 for (object.exec_atoms.items) |atom_index| {
211 const record_id = object.unwind_records_lookup.get(atom_index) orelse continue;
212 if (object.unwind_relocs_lookup[record_id].dead) continue;
213 const record = unwind_records[record_id];
214 if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
215 if (getPersonalityFunctionReloc(
216 zld,
217 @intCast(u32, object_id),
218 record_id,
219 )) |rel| {
220 // Personality function; add GOT pointer.
221 const target = parseRelocTarget(
222 zld,
223 @intCast(u32, object_id),
224 rel,
225 mem.asBytes(&record),
226 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
227 );
228 try Atom.addGotEntry(zld, target);
229 }
230 }
231 }
232 }
233}
234
235pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
236 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) return;
237
238 const cpu_arch = zld.options.target.cpu.arch;
239
240 var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa);
241 defer records.deinit();
242
243 var atom_indexes = std.ArrayList(AtomIndex).init(info.gpa);
244 defer atom_indexes.deinit();
245
246 // TODO handle dead stripping
247 for (zld.objects.items) |*object, object_id| {
248 log.debug("collecting unwind records in {s} ({d})", .{ object.name, object_id });
249 const unwind_records = object.getUnwindRecords();
250
251 // Contents of unwind records does not have to cover all symbol in executable section
252 // so we need insert them ourselves.
253 try records.ensureUnusedCapacity(object.exec_atoms.items.len);
254 try atom_indexes.ensureUnusedCapacity(object.exec_atoms.items.len);
255
256 var it = object.getEhFrameRecordsIterator();
257
258 for (object.exec_atoms.items) |atom_index| {
259 var record = if (object.unwind_records_lookup.get(atom_index)) |record_id| blk: {
260 if (object.unwind_relocs_lookup[record_id].dead) continue;
261 var record = unwind_records[record_id];
262
263 if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
264 const fde_offset = object.eh_frame_records_lookup.get(atom_index).?;
265 it.seekTo(fde_offset);
266 const fde = (try it.next()).?;
267 const cie_ptr = fde.getCiePointer();
268 const cie_offset = fde_offset + 4 - cie_ptr;
269 it.seekTo(cie_offset);
270 const cie = (try it.next()).?;
271
272 if (cie.getPersonalityPointerReloc(
273 zld,
274 @intCast(u32, object_id),
275 cie_offset,
276 )) |target| {
277 const personality_index = info.getPersonalityFunction(target) orelse inner: {
278 const personality_index = info.personalities_count;
279 info.personalities[personality_index] = target;
280 info.personalities_count += 1;
281 break :inner personality_index;
282 };
283
284 record.personalityFunction = personality_index + 1;
285 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
286 }
287 } else {
288 if (getPersonalityFunctionReloc(
289 zld,
290 @intCast(u32, object_id),
291 record_id,
292 )) |rel| {
293 const target = parseRelocTarget(
294 zld,
295 @intCast(u32, object_id),
296 rel,
297 mem.asBytes(&record),
298 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
299 );
300 const personality_index = info.getPersonalityFunction(target) orelse inner: {
301 const personality_index = info.personalities_count;
302 info.personalities[personality_index] = target;
303 info.personalities_count += 1;
304 break :inner personality_index;
305 };
306
307 record.personalityFunction = personality_index + 1;
308 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
309 }
310
311 if (getLsdaReloc(zld, @intCast(u32, object_id), record_id)) |rel| {
312 const target = parseRelocTarget(
313 zld,
314 @intCast(u32, object_id),
315 rel,
316 mem.asBytes(&record),
317 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
318 );
319 record.lsda = @bitCast(u64, target);
320 }
321 }
322 break :blk record;
323 } else blk: {
324 const atom = zld.getAtom(atom_index);
325 const sym = zld.getSymbol(atom.getSymbolWithLoc());
326 if (sym.n_desc == N_DEAD) continue;
327 break :blk nullRecord();
328 };
329
330 const atom = zld.getAtom(atom_index);
331 const sym_loc = atom.getSymbolWithLoc();
332 const sym = zld.getSymbol(sym_loc);
333 assert(sym.n_desc != N_DEAD);
334 record.rangeStart = sym.n_value;
335 record.rangeLength = @intCast(u32, atom.size);
336
337 records.appendAssumeCapacity(record);
338 atom_indexes.appendAssumeCapacity(atom_index);
339 }
340 }
341
342 // Fold records
343 try info.records.ensureTotalCapacity(info.gpa, records.items.len);
344 try info.records_lookup.ensureTotalCapacity(info.gpa, @intCast(u32, atom_indexes.items.len));
345
346 var maybe_prev: ?macho.compact_unwind_entry = null;
347 for (records.items) |record, i| {
348 const record_id = blk: {
349 if (maybe_prev) |prev| {
350 const is_dwarf = UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
351 if (is_dwarf or
352 (prev.compactUnwindEncoding != record.compactUnwindEncoding) or
353 (prev.personalityFunction != record.personalityFunction) or
354 record.lsda > 0)
355 {
356 const record_id = @intCast(RecordIndex, info.records.items.len);
357 info.records.appendAssumeCapacity(record);
358 maybe_prev = record;
359 break :blk record_id;
360 } else {
361 break :blk @intCast(RecordIndex, info.records.items.len - 1);
362 }
363 } else {
364 const record_id = @intCast(RecordIndex, info.records.items.len);
365 info.records.appendAssumeCapacity(record);
366 maybe_prev = record;
367 break :blk record_id;
368 }
369 };
370 info.records_lookup.putAssumeCapacityNoClobber(atom_indexes.items[i], record_id);
371 }
372
373 // Calculate common encodings
374 {
375 const CommonEncWithCount = struct {
376 enc: macho.compact_unwind_encoding_t,
377 count: u32,
378
379 fn greaterThan(ctx: void, lhs: @This(), rhs: @This()) bool {
380 _ = ctx;
381 return lhs.count > rhs.count;
382 }
383 };
384
385 const Context = struct {
386 pub fn hash(ctx: @This(), key: macho.compact_unwind_encoding_t) u32 {
387 _ = ctx;
388 return key;
389 }
390
391 pub fn eql(
392 ctx: @This(),
393 key1: macho.compact_unwind_encoding_t,
394 key2: macho.compact_unwind_encoding_t,
395 b_index: usize,
396 ) bool {
397 _ = ctx;
398 _ = b_index;
399 return key1 == key2;
400 }
401 };
402
403 var common_encodings_counts = std.ArrayHashMap(
404 macho.compact_unwind_encoding_t,
405 CommonEncWithCount,
406 Context,
407 false,
408 ).init(info.gpa);
409 defer common_encodings_counts.deinit();
410
411 for (info.records.items) |record| {
412 assert(!isNull(record));
413 if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) continue;
414 const enc = record.compactUnwindEncoding;
415 const gop = try common_encodings_counts.getOrPut(enc);
416 if (!gop.found_existing) {
417 gop.value_ptr.* = .{
418 .enc = enc,
419 .count = 0,
420 };
421 }
422 gop.value_ptr.count += 1;
423 }
424
425 var slice = common_encodings_counts.values();
426 std.sort.sort(CommonEncWithCount, slice, {}, CommonEncWithCount.greaterThan);
427
428 var i: u7 = 0;
429 while (i < slice.len) : (i += 1) {
430 if (i >= max_common_encodings) break;
431 if (slice[i].count < 2) continue;
432 info.appendCommonEncoding(slice[i].enc);
433 log.debug("adding common encoding: {d} => 0x{x:0>8}", .{ i, slice[i].enc });
434 }
435 }
436
437 // Compute page allocations
438 {
439 var i: u32 = 0;
440 while (i < info.records.items.len) {
441 const range_start_max: u64 =
442 info.records.items[i].rangeStart + compressed_entry_func_offset_mask;
443 var encoding_count: u9 = info.common_encodings_count;
444 var space_left: u32 = second_level_page_words -
445 @sizeOf(macho.unwind_info_compressed_second_level_page_header) / @sizeOf(u32);
446 var page = Page{
447 .kind = undefined,
448 .start = i,
449 .count = 0,
450 };
451
452 while (space_left >= 1 and i < info.records.items.len) {
453 const record = info.records.items[i];
454 const enc = record.compactUnwindEncoding;
455 const is_dwarf = UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
456
457 if (record.rangeStart >= range_start_max) {
458 break;
459 } else if (info.getCommonEncoding(enc) != null or
460 page.getPageEncoding(info, enc) != null and !is_dwarf)
461 {
462 i += 1;
463 space_left -= 1;
464 } else if (space_left >= 2 and encoding_count < max_compact_encodings) {
465 page.appendPageEncoding(i);
466 i += 1;
467 space_left -= 2;
468 encoding_count += 1;
469 } else {
470 break;
471 }
472 }
473
474 page.count = @intCast(u16, i - page.start);
475
476 if (i < info.records.items.len and page.count < max_regular_second_level_entries) {
477 page.kind = .regular;
478 page.count = @intCast(u16, @min(
479 max_regular_second_level_entries,
480 info.records.items.len - page.start,
481 ));
482 i = page.start + page.count;
483 } else {
484 page.kind = .compressed;
485 }
486
487 log.debug("{}", .{page.fmtDebug(info)});
488
489 try info.pages.append(info.gpa, page);
490 }
491 }
492
493 // Save indices of records requiring LSDA relocation
494 try info.lsdas_lookup.ensureTotalCapacity(info.gpa, @intCast(u32, info.records.items.len));
495 for (info.records.items) |rec, i| {
496 info.lsdas_lookup.putAssumeCapacityNoClobber(@intCast(RecordIndex, i), @intCast(u32, info.lsdas.items.len));
497 if (rec.lsda == 0) continue;
498 try info.lsdas.append(info.gpa, @intCast(RecordIndex, i));
499 }
500}
501
502pub fn calcSectionSize(info: UnwindInfo, zld: *Zld) !void {
503 const sect_id = zld.getSectionByName("__TEXT", "__unwind_info") orelse return;
504 const sect = &zld.sections.items(.header)[sect_id];
505 sect.@"align" = 2;
506 sect.size = info.calcRequiredSize();
507}
508
509fn calcRequiredSize(info: UnwindInfo) usize {
510 var total_size: usize = 0;
511 total_size += @sizeOf(macho.unwind_info_section_header);
512 total_size +=
513 @intCast(usize, info.common_encodings_count) * @sizeOf(macho.compact_unwind_encoding_t);
514 total_size += @intCast(usize, info.personalities_count) * @sizeOf(u32);
515 total_size += (info.pages.items.len + 1) * @sizeOf(macho.unwind_info_section_header_index_entry);
516 total_size += info.lsdas.items.len * @sizeOf(macho.unwind_info_section_header_lsda_index_entry);
517 total_size += info.pages.items.len * second_level_page_bytes;
518 return total_size;
519}
520
521pub fn write(info: *UnwindInfo, zld: *Zld) !void {
522 const sect_id = zld.getSectionByName("__TEXT", "__unwind_info") orelse return;
523 const sect = &zld.sections.items(.header)[sect_id];
524 const seg_id = zld.sections.items(.segment_index)[sect_id];
525 const seg = zld.segments.items[seg_id];
526
527 const text_sect_id = zld.getSectionByName("__TEXT", "__text").?;
528 const text_sect = zld.sections.items(.header)[text_sect_id];
529
530 var personalities: [max_personalities]u32 = undefined;
531 const cpu_arch = zld.options.target.cpu.arch;
532
533 log.debug("Personalities:", .{});
534 for (info.personalities[0..info.personalities_count]) |target, i| {
535 const atom_index = zld.getGotAtomIndexForSymbol(target).?;
536 const atom = zld.getAtom(atom_index);
537 const sym = zld.getSymbol(atom.getSymbolWithLoc());
538 personalities[i] = @intCast(u32, sym.n_value - seg.vmaddr);
539 log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], zld.getSymbolName(target) });
540 }
541
542 for (info.records.items) |*rec| {
543 // Finalize missing address values
544 rec.rangeStart += text_sect.addr - seg.vmaddr;
545 if (rec.personalityFunction > 0) {
546 rec.personalityFunction = personalities[rec.personalityFunction - 1];
547 }
548
549 if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) {
550 const lsda_target = @bitCast(SymbolWithLoc, rec.lsda);
551 if (lsda_target.getFile()) |_| {
552 const sym = zld.getSymbol(lsda_target);
553 rec.lsda = sym.n_value - seg.vmaddr;
554 }
555 }
556 }
557
558 for (info.records.items) |record, i| {
559 log.debug("Unwind record at offset 0x{x}", .{i * @sizeOf(macho.compact_unwind_entry)});
560 log.debug(" start: 0x{x}", .{record.rangeStart});
561 log.debug(" length: 0x{x}", .{record.rangeLength});
562 log.debug(" compact encoding: 0x{x:0>8}", .{record.compactUnwindEncoding});
563 log.debug(" personality: 0x{x}", .{record.personalityFunction});
564 log.debug(" LSDA: 0x{x}", .{record.lsda});
565 }
566
567 var buffer = std.ArrayList(u8).init(info.gpa);
568 defer buffer.deinit();
569
570 const size = info.calcRequiredSize();
571 try buffer.ensureTotalCapacityPrecise(size);
572
573 var cwriter = std.io.countingWriter(buffer.writer());
574 const writer = cwriter.writer();
575
576 const common_encodings_offset: u32 = @sizeOf(macho.unwind_info_section_header);
577 const common_encodings_count: u32 = info.common_encodings_count;
578 const personalities_offset: u32 = common_encodings_offset + common_encodings_count * @sizeOf(u32);
579 const personalities_count: u32 = info.personalities_count;
580 const indexes_offset: u32 = personalities_offset + personalities_count * @sizeOf(u32);
581 const indexes_count: u32 = @intCast(u32, info.pages.items.len + 1);
582
583 try writer.writeStruct(macho.unwind_info_section_header{
584 .commonEncodingsArraySectionOffset = common_encodings_offset,
585 .commonEncodingsArrayCount = common_encodings_count,
586 .personalityArraySectionOffset = personalities_offset,
587 .personalityArrayCount = personalities_count,
588 .indexSectionOffset = indexes_offset,
589 .indexCount = indexes_count,
590 });
591
592 try writer.writeAll(mem.sliceAsBytes(info.common_encodings[0..info.common_encodings_count]));
593 try writer.writeAll(mem.sliceAsBytes(personalities[0..info.personalities_count]));
594
595 const pages_base_offset = @intCast(u32, size - (info.pages.items.len * second_level_page_bytes));
596 const lsda_base_offset = @intCast(u32, pages_base_offset -
597 (info.lsdas.items.len * @sizeOf(macho.unwind_info_section_header_lsda_index_entry)));
598 for (info.pages.items) |page, i| {
599 assert(page.count > 0);
600 const first_entry = info.records.items[page.start];
601 try writer.writeStruct(macho.unwind_info_section_header_index_entry{
602 .functionOffset = @intCast(u32, first_entry.rangeStart),
603 .secondLevelPagesSectionOffset = @intCast(u32, pages_base_offset + i * second_level_page_bytes),
604 .lsdaIndexArraySectionOffset = lsda_base_offset +
605 info.lsdas_lookup.get(page.start).? * @sizeOf(macho.unwind_info_section_header_lsda_index_entry),
606 });
607 }
608
609 const last_entry = info.records.items[info.records.items.len - 1];
610 const sentinel_address = @intCast(u32, last_entry.rangeStart + last_entry.rangeLength);
611 try writer.writeStruct(macho.unwind_info_section_header_index_entry{
612 .functionOffset = sentinel_address,
613 .secondLevelPagesSectionOffset = 0,
614 .lsdaIndexArraySectionOffset = lsda_base_offset +
615 @intCast(u32, info.lsdas.items.len) * @sizeOf(macho.unwind_info_section_header_lsda_index_entry),
616 });
617
618 for (info.lsdas.items) |record_id| {
619 const record = info.records.items[record_id];
620 try writer.writeStruct(macho.unwind_info_section_header_lsda_index_entry{
621 .functionOffset = @intCast(u32, record.rangeStart),
622 .lsdaOffset = @intCast(u32, record.lsda),
623 });
624 }
625
626 for (info.pages.items) |page| {
627 const start = cwriter.bytes_written;
628 try page.write(info, writer);
629 const nwritten = cwriter.bytes_written - start;
630 if (nwritten < second_level_page_bytes) {
631 try writer.writeByteNTimes(0, second_level_page_bytes - nwritten);
632 }
633 }
634
635 const padding = buffer.items.len - cwriter.bytes_written;
636 if (padding > 0) {
637 mem.set(u8, buffer.items[cwriter.bytes_written..], 0);
638 }
639
640 try zld.file.pwriteAll(buffer.items, sect.offset);
641}
642
643pub fn parseRelocTarget(
644 zld: *Zld,
645 object_id: u32,
646 rel: macho.relocation_info,
647 code: []const u8,
648 base_offset: i32,
649) SymbolWithLoc {
650 const tracy = trace(@src());
651 defer tracy.end();
652
653 const object = &zld.objects.items[object_id];
654
655 const sym_index = if (rel.r_extern == 0) blk: {
656 const sect_id = @intCast(u8, rel.r_symbolnum - 1);
657 const rel_offset = @intCast(u32, rel.r_address - base_offset);
658 assert(rel.r_pcrel == 0 and rel.r_length == 3);
659 const address_in_section = mem.readIntLittle(u64, code[rel_offset..][0..8]);
660 const sym_index = object.getSymbolByAddress(address_in_section, sect_id);
661 break :blk sym_index;
662 } else object.reverse_symtab_lookup[rel.r_symbolnum];
663
664 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id + 1 };
665 const sym = zld.getSymbol(sym_loc);
666
667 if (sym.sect() and !sym.ext()) {
668 // Make sure we are not dealing with a local alias.
669 const atom_index = object.getAtomIndexForSymbol(sym_index) orelse
670 return sym_loc;
671 const atom = zld.getAtom(atom_index);
672 return atom.getSymbolWithLoc();
673 } else if (object.getGlobal(sym_index)) |global_index| {
674 return zld.globals.items[global_index];
675 } else return sym_loc;
676}
677
678fn getRelocs(
679 zld: *Zld,
680 object_id: u32,
681 record_id: usize,
682) []align(1) const macho.relocation_info {
683 const object = &zld.objects.items[object_id];
684 assert(object.hasUnwindRecords());
685 const rel_pos = object.unwind_relocs_lookup[record_id].reloc;
686 const relocs = object.getRelocs(object.unwind_info_sect.?);
687 return relocs[rel_pos.start..][0..rel_pos.len];
688}
689
690fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool {
691 const base_offset = @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry));
692 const rel_offset = rel.r_address - base_offset;
693 return rel_offset == 16;
694}
695
696pub fn getPersonalityFunctionReloc(
697 zld: *Zld,
698 object_id: u32,
699 record_id: usize,
700) ?macho.relocation_info {
701 const relocs = getRelocs(zld, object_id, record_id);
702 for (relocs) |rel| {
703 if (isPersonalityFunction(record_id, rel)) return rel;
704 }
705 return null;
706}
707
708fn getPersonalityFunction(info: UnwindInfo, global_index: SymbolWithLoc) ?u2 {
709 comptime var index: u2 = 0;
710 inline while (index < max_personalities) : (index += 1) {
711 if (index >= info.personalities_count) return null;
712 if (info.personalities[index].eql(global_index)) {
713 return index;
714 }
715 }
716 return null;
717}
718
719fn isLsda(record_id: usize, rel: macho.relocation_info) bool {
720 const base_offset = @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry));
721 const rel_offset = rel.r_address - base_offset;
722 return rel_offset == 24;
723}
724
725pub fn getLsdaReloc(zld: *Zld, object_id: u32, record_id: usize) ?macho.relocation_info {
726 const relocs = getRelocs(zld, object_id, record_id);
727 for (relocs) |rel| {
728 if (isLsda(record_id, rel)) return rel;
729 }
730 return null;
731}
732
733pub fn isNull(rec: macho.compact_unwind_entry) bool {
734 return rec.rangeStart == 0 and
735 rec.rangeLength == 0 and
736 rec.compactUnwindEncoding == 0 and
737 rec.lsda == 0 and
738 rec.personalityFunction == 0;
739}
740
741inline fn nullRecord() macho.compact_unwind_entry {
742 return .{
743 .rangeStart = 0,
744 .rangeLength = 0,
745 .compactUnwindEncoding = 0,
746 .personalityFunction = 0,
747 .lsda = 0,
748 };
749}
750
751fn appendCommonEncoding(info: *UnwindInfo, enc: macho.compact_unwind_encoding_t) void {
752 assert(info.common_encodings_count <= max_common_encodings);
753 info.common_encodings[info.common_encodings_count] = enc;
754 info.common_encodings_count += 1;
755}
756
757fn getCommonEncoding(info: UnwindInfo, enc: macho.compact_unwind_encoding_t) ?u7 {
758 comptime var index: u7 = 0;
759 inline while (index < max_common_encodings) : (index += 1) {
760 if (index >= info.common_encodings_count) return null;
761 if (info.common_encodings[index] == enc) {
762 return index;
763 }
764 }
765 return null;
766}
767
768pub const UnwindEncoding = struct {
769 pub const UNWIND_X86_64_MODE = enum(u4) {
770 none = 0,
771 ebp_frame = 1,
772 stack_immd = 2,
773 stack_ind = 3,
774 dwarf = 4,
775 };
776
777 pub const UNWIND_ARM64_MODE = enum(u4) {
778 none = 0,
779 frameless = 2,
780 dwarf = 3,
781 frame = 4,
782 };
783
784 pub const UNWIND_MODE_MASK: u32 = 0x0F000000;
785 pub const UNWIND_PERSONALITY_INDEX_MASK: u32 = 0x30000000;
786 pub const UNWIND_HAS_LSDA_MASK: u32 = 0x40000000;
787
788 pub fn getMode(enc: macho.compact_unwind_encoding_t) u4 {
789 const mode = @truncate(u4, (enc & UNWIND_MODE_MASK) >> 24);
790 return mode;
791 }
792
793 pub fn isDwarf(enc: macho.compact_unwind_encoding_t, cpu_arch: std.Target.Cpu.Arch) bool {
794 switch (cpu_arch) {
795 .aarch64 => return @intToEnum(UNWIND_ARM64_MODE, getMode(enc)) == .dwarf,
796 .x86_64 => return @intToEnum(UNWIND_X86_64_MODE, getMode(enc)) == .dwarf,
797 else => unreachable,
798 }
799 }
800
801 pub fn hasLsda(enc: macho.compact_unwind_encoding_t) bool {
802 const has_lsda = @truncate(u1, (enc & UNWIND_HAS_LSDA_MASK) >> 31);
803 return has_lsda == 1;
804 }
805
806 pub fn setHasLsda(enc: *macho.compact_unwind_encoding_t, has_lsda: bool) void {
807 const mask = @intCast(u32, @boolToInt(has_lsda)) << 31;
808 enc.* |= mask;
809 }
810
811 pub fn getPersonalityIndex(enc: macho.compact_unwind_encoding_t) u2 {
812 const index = @truncate(u2, (enc & UNWIND_PERSONALITY_INDEX_MASK) >> 28);
813 return index;
814 }
815
816 pub fn setPersonalityIndex(enc: *macho.compact_unwind_encoding_t, index: u2) void {
817 const mask = @intCast(u32, index) << 28;
818 enc.* |= mask;
819 }
820
821 pub fn getDwarfSectionOffset(enc: macho.compact_unwind_encoding_t, cpu_arch: std.Target.Cpu.Arch) u24 {
822 assert(isDwarf(enc, cpu_arch));
823 const offset = @truncate(u24, enc);
824 return offset;
825 }
826
827 pub fn setDwarfSectionOffset(enc: *macho.compact_unwind_encoding_t, cpu_arch: std.Target.Cpu.Arch, offset: u24) void {
828 assert(isDwarf(enc.*, cpu_arch));
829 enc.* |= offset;
830 }
831};
src/link/MachO/ZldAtom.zig+78-166
...@@ -29,11 +29,11 @@ const Zld = @import("zld.zig").Zld;...@@ -29,11 +29,11 @@ const Zld = @import("zld.zig").Zld;
29/// a stub trampoline, it can be found in the linkers `locals` arraylist.29/// a stub trampoline, it can be found in the linkers `locals` arraylist.
30sym_index: u32,30sym_index: u32,
3131
32/// -1 means an Atom is a synthetic Atom such as a GOT cell defined by the linker.32/// 0 means an Atom is a synthetic Atom such as a GOT cell defined by the linker.
33/// Otherwise, it is the index into appropriate object file.33/// Otherwise, it is the index into appropriate object file (indexing from 1).
34/// Prefer using `getFile()` helper to get the file index out rather than using34/// Prefer using `getFile()` helper to get the file index out rather than using
35/// the field directly.35/// the field directly.
36file: i32,36file: u32,
3737
38/// If this Atom is not a synthetic Atom, i.e., references a subsection in an38/// If this Atom is not a synthetic Atom, i.e., references a subsection in an
39/// Object file, `inner_sym_index` and `inner_nsyms_trailing` tell where and if39/// Object file, `inner_sym_index` and `inner_nsyms_trailing` tell where and if
...@@ -51,13 +51,6 @@ size: u64,...@@ -51,13 +51,6 @@ size: u64,
51/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.51/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
52alignment: u32,52alignment: u32,
5353
54/// Cached index and length into the relocations records array that correspond to
55/// this Atom and need to be resolved before the Atom can be committed into the
56/// final linked image.
57/// Do not use these fields directly. Instead, use `getAtomRelocs()` helper.
58cached_relocs_start: i32,
59cached_relocs_len: u32,
60
61/// Points to the previous and next neighbours54/// Points to the previous and next neighbours
62next_index: ?AtomIndex,55next_index: ?AtomIndex,
63prev_index: ?AtomIndex,56prev_index: ?AtomIndex,
...@@ -66,20 +59,18 @@ pub const empty = Atom{...@@ -66,20 +59,18 @@ pub const empty = Atom{
66 .sym_index = 0,59 .sym_index = 0,
67 .inner_sym_index = 0,60 .inner_sym_index = 0,
68 .inner_nsyms_trailing = 0,61 .inner_nsyms_trailing = 0,
69 .file = -1,62 .file = 0,
70 .size = 0,63 .size = 0,
71 .alignment = 0,64 .alignment = 0,
72 .cached_relocs_start = -1,
73 .cached_relocs_len = 0,
74 .prev_index = null,65 .prev_index = null,
75 .next_index = null,66 .next_index = null,
76};67};
7768
78/// Returns `null` if the Atom is a synthetic Atom.69/// Returns `null` if the Atom is a synthetic Atom.
79/// Otherwise, returns an index into an array of Objects.70/// Otherwise, returns an index into an array of Objects.
80pub inline fn getFile(self: Atom) ?u31 {71pub fn getFile(self: Atom) ?u32 {
81 if (self.file == -1) return null;72 if (self.file == 0) return null;
82 return @intCast(u31, self.file);73 return self.file - 1;
83}74}
8475
85pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {76pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
...@@ -92,7 +83,7 @@ pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {...@@ -92,7 +83,7 @@ pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
92const InnerSymIterator = struct {83const InnerSymIterator = struct {
93 sym_index: u32,84 sym_index: u32,
94 count: u32,85 count: u32,
95 file: i32,86 file: u32,
9687
97 pub fn next(it: *@This()) ?SymbolWithLoc {88 pub fn next(it: *@This()) ?SymbolWithLoc {
98 if (it.count == 0) return null;89 if (it.count == 0) return null;
...@@ -159,19 +150,14 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u...@@ -159,19 +150,14 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u
159 return source_sym.n_value - base_addr;150 return source_sym.n_value - base_addr;
160}151}
161152
162pub fn scanAtomRelocs(153pub fn scanAtomRelocs(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
163 zld: *Zld,
164 atom_index: AtomIndex,
165 relocs: []align(1) const macho.relocation_info,
166 reverse_lookup: []u32,
167) !void {
168 const arch = zld.options.target.cpu.arch;154 const arch = zld.options.target.cpu.arch;
169 const atom = zld.getAtom(atom_index);155 const atom = zld.getAtom(atom_index);
170 assert(atom.getFile() != null); // synthetic atoms do not have relocs156 assert(atom.getFile() != null); // synthetic atoms do not have relocs
171157
172 return switch (arch) {158 return switch (arch) {
173 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs, reverse_lookup),159 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs),
174 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs, reverse_lookup),160 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs),
175 else => unreachable,161 else => unreachable,
176 };162 };
177}163}
...@@ -202,16 +188,11 @@ pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext {...@@ -202,16 +188,11 @@ pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext {
202 };188 };
203}189}
204190
205pub fn parseRelocTarget(191pub fn parseRelocTarget(zld: *Zld, atom_index: AtomIndex, rel: macho.relocation_info) SymbolWithLoc {
206 zld: *Zld,
207 atom_index: AtomIndex,
208 rel: macho.relocation_info,
209 reverse_lookup: []u32,
210) SymbolWithLoc {
211 const atom = zld.getAtom(atom_index);192 const atom = zld.getAtom(atom_index);
212 const object = &zld.objects.items[atom.getFile().?];193 const object = &zld.objects.items[atom.getFile().?];
213194
214 if (rel.r_extern == 0) {195 const sym_index = if (rel.r_extern == 0) sym_index: {
215 const sect_id = @intCast(u8, rel.r_symbolnum - 1);196 const sect_id = @intCast(u8, rel.r_symbolnum - 1);
216 const ctx = getRelocContext(zld, atom_index);197 const ctx = getRelocContext(zld, atom_index);
217 const atom_code = getAtomCode(zld, atom_index);198 const atom_code = getAtomCode(zld, atom_index);
...@@ -219,9 +200,9 @@ pub fn parseRelocTarget(...@@ -219,9 +200,9 @@ pub fn parseRelocTarget(
219200
220 const address_in_section = if (rel.r_pcrel == 0) blk: {201 const address_in_section = if (rel.r_pcrel == 0) blk: {
221 break :blk if (rel.r_length == 3)202 break :blk if (rel.r_length == 3)
222 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])203 mem.readIntLittle(u64, atom_code[rel_offset..][0..8])
223 else204 else
224 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);205 mem.readIntLittle(u32, atom_code[rel_offset..][0..4]);
225 } else blk: {206 } else blk: {
226 const correction: u3 = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {207 const correction: u3 = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
227 .X86_64_RELOC_SIGNED => 0,208 .X86_64_RELOC_SIGNED => 0,
...@@ -232,38 +213,14 @@ pub fn parseRelocTarget(...@@ -232,38 +213,14 @@ pub fn parseRelocTarget(
232 };213 };
233 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);214 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
234 const target_address = @intCast(i64, ctx.base_addr) + rel.r_address + 4 + correction + addend;215 const target_address = @intCast(i64, ctx.base_addr) + rel.r_address + 4 + correction + addend;
235 break :blk target_address;216 break :blk @intCast(u64, target_address);
236 };217 };
237218
238 // Find containing atom219 // Find containing atom
239 const Predicate = struct {220 const sym_index = object.getSymbolByAddress(address_in_section, sect_id);
240 addr: i64,221 break :sym_index sym_index;
241222 } else object.reverse_symtab_lookup[rel.r_symbolnum];
242 pub fn predicate(pred: @This(), other: i64) bool {
243 return if (other == -1) true else other > pred.addr;
244 }
245 };
246
247 if (object.source_section_index_lookup[sect_id] > -1) {
248 const first_sym_index = @intCast(usize, object.source_section_index_lookup[sect_id]);
249 const target_sym_index = @import("zld.zig").lsearch(i64, object.source_address_lookup[first_sym_index..], Predicate{
250 .addr = address_in_section,
251 });
252
253 if (target_sym_index > 0) {
254 return SymbolWithLoc{
255 .sym_index = @intCast(u32, first_sym_index + target_sym_index - 1),
256 .file = atom.file,
257 };
258 }
259 }
260
261 // Start of section is not contained anywhere, return synthetic atom.
262 const sym_index = object.getSectionAliasSymbolIndex(sect_id);
263 return SymbolWithLoc{ .sym_index = sym_index, .file = atom.file };
264 }
265223
266 const sym_index = reverse_lookup[rel.r_symbolnum];
267 const sym_loc = SymbolWithLoc{224 const sym_loc = SymbolWithLoc{
268 .sym_index = sym_index,225 .sym_index = sym_index,
269 .file = atom.file,226 .file = atom.file,
...@@ -272,30 +229,12 @@ pub fn parseRelocTarget(...@@ -272,30 +229,12 @@ pub fn parseRelocTarget(
272229
273 if (sym.sect() and !sym.ext()) {230 if (sym.sect() and !sym.ext()) {
274 return sym_loc;231 return sym_loc;
275 } else if (object.globals_lookup[sym_index] > -1) {232 } else if (object.getGlobal(sym_index)) |global_index| {
276 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
277 return zld.globals.items[global_index];233 return zld.globals.items[global_index];
278 } else return sym_loc;234 } else return sym_loc;
279}235}
280236
281pub fn getRelocTargetAtomIndex(zld: *Zld, rel: macho.relocation_info, target: SymbolWithLoc) ?AtomIndex {237pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc, is_via_got: bool) ?AtomIndex {
282 const is_via_got = got: {
283 switch (zld.options.target.cpu.arch) {
284 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
285 .ARM64_RELOC_GOT_LOAD_PAGE21,
286 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
287 .ARM64_RELOC_POINTER_TO_GOT,
288 => true,
289 else => false,
290 },
291 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
292 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
293 else => false,
294 },
295 else => unreachable,
296 }
297 };
298
299 if (is_via_got) {238 if (is_via_got) {
300 return zld.getGotAtomIndexForSymbol(target).?; // panic means fatal error239 return zld.getGotAtomIndexForSymbol(target).?; // panic means fatal error
301 }240 }
...@@ -314,12 +253,7 @@ pub fn getRelocTargetAtomIndex(zld: *Zld, rel: macho.relocation_info, target: Sy...@@ -314,12 +253,7 @@ pub fn getRelocTargetAtomIndex(zld: *Zld, rel: macho.relocation_info, target: Sy
314 return object.getAtomIndexForSymbol(target.sym_index);253 return object.getAtomIndexForSymbol(target.sym_index);
315}254}
316255
317fn scanAtomRelocsArm64(256fn scanAtomRelocsArm64(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
318 zld: *Zld,
319 atom_index: AtomIndex,
320 relocs: []align(1) const macho.relocation_info,
321 reverse_lookup: []u32,
322) !void {
323 for (relocs) |rel| {257 for (relocs) |rel| {
324 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);258 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
325259
...@@ -332,7 +266,7 @@ fn scanAtomRelocsArm64(...@@ -332,7 +266,7 @@ fn scanAtomRelocsArm64(
332266
333 const atom = zld.getAtom(atom_index);267 const atom = zld.getAtom(atom_index);
334 const object = &zld.objects.items[atom.getFile().?];268 const object = &zld.objects.items[atom.getFile().?];
335 const sym_index = reverse_lookup[rel.r_symbolnum];269 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
336 const sym_loc = SymbolWithLoc{270 const sym_loc = SymbolWithLoc{
337 .sym_index = sym_index,271 .sym_index = sym_index,
338 .file = atom.file,272 .file = atom.file,
...@@ -341,10 +275,10 @@ fn scanAtomRelocsArm64(...@@ -341,10 +275,10 @@ fn scanAtomRelocsArm64(
341275
342 if (sym.sect() and !sym.ext()) continue;276 if (sym.sect() and !sym.ext()) continue;
343277
344 const target = if (object.globals_lookup[sym_index] > -1) blk: {278 const target = if (object.getGlobal(sym_index)) |global_index|
345 const global_index = @intCast(u32, object.globals_lookup[sym_index]);279 zld.globals.items[global_index]
346 break :blk zld.globals.items[global_index];280 else
347 } else sym_loc;281 sym_loc;
348282
349 switch (rel_type) {283 switch (rel_type) {
350 .ARM64_RELOC_BRANCH26 => {284 .ARM64_RELOC_BRANCH26 => {
...@@ -368,12 +302,7 @@ fn scanAtomRelocsArm64(...@@ -368,12 +302,7 @@ fn scanAtomRelocsArm64(
368 }302 }
369}303}
370304
371fn scanAtomRelocsX86(305fn scanAtomRelocsX86(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
372 zld: *Zld,
373 atom_index: AtomIndex,
374 relocs: []align(1) const macho.relocation_info,
375 reverse_lookup: []u32,
376) !void {
377 for (relocs) |rel| {306 for (relocs) |rel| {
378 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);307 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
379308
...@@ -386,7 +315,7 @@ fn scanAtomRelocsX86(...@@ -386,7 +315,7 @@ fn scanAtomRelocsX86(
386315
387 const atom = zld.getAtom(atom_index);316 const atom = zld.getAtom(atom_index);
388 const object = &zld.objects.items[atom.getFile().?];317 const object = &zld.objects.items[atom.getFile().?];
389 const sym_index = reverse_lookup[rel.r_symbolnum];318 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
390 const sym_loc = SymbolWithLoc{319 const sym_loc = SymbolWithLoc{
391 .sym_index = sym_index,320 .sym_index = sym_index,
392 .file = atom.file,321 .file = atom.file,
...@@ -395,10 +324,10 @@ fn scanAtomRelocsX86(...@@ -395,10 +324,10 @@ fn scanAtomRelocsX86(
395324
396 if (sym.sect() and !sym.ext()) continue;325 if (sym.sect() and !sym.ext()) continue;
397326
398 const target = if (object.globals_lookup[sym_index] > -1) blk: {327 const target = if (object.getGlobal(sym_index)) |global_index|
399 const global_index = @intCast(u32, object.globals_lookup[sym_index]);328 zld.globals.items[global_index]
400 break :blk zld.globals.items[global_index];329 else
401 } else sym_loc;330 sym_loc;
402331
403 switch (rel_type) {332 switch (rel_type) {
404 .X86_64_RELOC_BRANCH => {333 .X86_64_RELOC_BRANCH => {
...@@ -432,7 +361,7 @@ fn addTlvPtrEntry(zld: *Zld, target: SymbolWithLoc) !void {...@@ -432,7 +361,7 @@ fn addTlvPtrEntry(zld: *Zld, target: SymbolWithLoc) !void {
432 try zld.tlv_ptr_table.putNoClobber(gpa, target, tlv_ptr_index);361 try zld.tlv_ptr_table.putNoClobber(gpa, target, tlv_ptr_index);
433}362}
434363
435fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {364pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
436 if (zld.got_table.contains(target)) return;365 if (zld.got_table.contains(target)) return;
437 const gpa = zld.gpa;366 const gpa = zld.gpa;
438 const atom_index = try zld.createGotAtom();367 const atom_index = try zld.createGotAtom();
...@@ -466,7 +395,6 @@ pub fn resolveRelocs(...@@ -466,7 +395,6 @@ pub fn resolveRelocs(
466 atom_index: AtomIndex,395 atom_index: AtomIndex,
467 atom_code: []u8,396 atom_code: []u8,
468 atom_relocs: []align(1) const macho.relocation_info,397 atom_relocs: []align(1) const macho.relocation_info,
469 reverse_lookup: []u32,
470) !void {398) !void {
471 const arch = zld.options.target.cpu.arch;399 const arch = zld.options.target.cpu.arch;
472 const atom = zld.getAtom(atom_index);400 const atom = zld.getAtom(atom_index);
...@@ -480,14 +408,14 @@ pub fn resolveRelocs(...@@ -480,14 +408,14 @@ pub fn resolveRelocs(
480 const ctx = getRelocContext(zld, atom_index);408 const ctx = getRelocContext(zld, atom_index);
481409
482 return switch (arch) {410 return switch (arch) {
483 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, reverse_lookup, ctx),411 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, ctx),
484 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, reverse_lookup, ctx),412 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, ctx),
485 else => unreachable,413 else => unreachable,
486 };414 };
487}415}
488416
489pub fn getRelocTargetAddress(zld: *Zld, rel: macho.relocation_info, target: SymbolWithLoc, is_tlv: bool) !u64 {417pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_via_got: bool, is_tlv: bool) !u64 {
490 const target_atom_index = getRelocTargetAtomIndex(zld, rel, target) orelse {418 const target_atom_index = getRelocTargetAtomIndex(zld, target, is_via_got) orelse {
491 // If there is no atom for target, we still need to check for special, atom-less419 // If there is no atom for target, we still need to check for special, atom-less
492 // symbols such as `___dso_handle`.420 // symbols such as `___dso_handle`.
493 const target_name = zld.getSymbolName(target);421 const target_name = zld.getSymbolName(target);
...@@ -499,7 +427,7 @@ pub fn getRelocTargetAddress(zld: *Zld, rel: macho.relocation_info, target: Symb...@@ -499,7 +427,7 @@ pub fn getRelocTargetAddress(zld: *Zld, rel: macho.relocation_info, target: Symb
499 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{427 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
500 target_atom.sym_index,428 target_atom.sym_index,
501 zld.getSymbolName(target_atom.getSymbolWithLoc()),429 zld.getSymbolName(target_atom.getSymbolWithLoc()),
502 target_atom.file,430 target_atom.getFile(),
503 });431 });
504432
505 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());433 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
...@@ -541,7 +469,6 @@ fn resolveRelocsArm64(...@@ -541,7 +469,6 @@ fn resolveRelocsArm64(
541 atom_index: AtomIndex,469 atom_index: AtomIndex,
542 atom_code: []u8,470 atom_code: []u8,
543 atom_relocs: []align(1) const macho.relocation_info,471 atom_relocs: []align(1) const macho.relocation_info,
544 reverse_lookup: []u32,
545 context: RelocContext,472 context: RelocContext,
546) !void {473) !void {
547 const atom = zld.getAtom(atom_index);474 const atom = zld.getAtom(atom_index);
...@@ -565,20 +492,20 @@ fn resolveRelocsArm64(...@@ -565,20 +492,20 @@ fn resolveRelocsArm64(
565 .ARM64_RELOC_SUBTRACTOR => {492 .ARM64_RELOC_SUBTRACTOR => {
566 assert(subtractor == null);493 assert(subtractor == null);
567494
568 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{495 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
569 @tagName(rel_type),496 @tagName(rel_type),
570 rel.r_address,497 rel.r_address,
571 rel.r_symbolnum,498 rel.r_symbolnum,
572 atom.file,499 atom.getFile(),
573 });500 });
574501
575 subtractor = parseRelocTarget(zld, atom_index, rel, reverse_lookup);502 subtractor = parseRelocTarget(zld, atom_index, rel);
576 continue;503 continue;
577 },504 },
578 else => {},505 else => {},
579 }506 }
580507
581 const target = parseRelocTarget(zld, atom_index, rel, reverse_lookup);508 const target = parseRelocTarget(zld, atom_index, rel);
582 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);509 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);
583510
584 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{511 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
...@@ -586,19 +513,20 @@ fn resolveRelocsArm64(...@@ -586,19 +513,20 @@ fn resolveRelocsArm64(
586 rel.r_address,513 rel.r_address,
587 target.sym_index,514 target.sym_index,
588 zld.getSymbolName(target),515 zld.getSymbolName(target),
589 target.file,516 target.getFile(),
590 });517 });
591518
592 const source_addr = blk: {519 const source_addr = blk: {
593 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());520 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
594 break :blk source_sym.n_value + rel_offset;521 break :blk source_sym.n_value + rel_offset;
595 };522 };
523 const is_via_got = relocRequiresGot(zld, rel);
596 const is_tlv = is_tlv: {524 const is_tlv = is_tlv: {
597 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());525 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
598 const header = zld.sections.items(.header)[source_sym.n_sect - 1];526 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
599 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;527 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
600 };528 };
601 const target_addr = try getRelocTargetAddress(zld, rel, target, is_tlv);529 const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
602530
603 log.debug(" | source_addr = 0x{x}", .{source_addr});531 log.debug(" | source_addr = 0x{x}", .{source_addr});
604532
...@@ -610,9 +538,9 @@ fn resolveRelocsArm64(...@@ -610,9 +538,9 @@ fn resolveRelocsArm64(
610 } else target;538 } else target;
611 log.debug(" source {s} (object({?})), target {s} (object({?}))", .{539 log.debug(" source {s} (object({?})), target {s} (object({?}))", .{
612 zld.getSymbolName(atom.getSymbolWithLoc()),540 zld.getSymbolName(atom.getSymbolWithLoc()),
613 atom.file,541 atom.getFile(),
614 zld.getSymbolName(target),542 zld.getSymbolName(target),
615 zld.getAtom(getRelocTargetAtomIndex(zld, rel, target).?).file,543 zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(),
616 });544 });
617545
618 const displacement = if (calcPcRelativeDisplacementArm64(546 const displacement = if (calcPcRelativeDisplacementArm64(
...@@ -628,7 +556,7 @@ fn resolveRelocsArm64(...@@ -628,7 +556,7 @@ fn resolveRelocsArm64(
628 zld,556 zld,
629 actual_target,557 actual_target,
630 ).?);558 ).?);
631 log.debug(" | target_addr = 0x{x}", .{thunk_sym.n_value});559 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value});
632 break :blk try calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);560 break :blk try calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
633 };561 };
634562
...@@ -832,7 +760,6 @@ fn resolveRelocsX86(...@@ -832,7 +760,6 @@ fn resolveRelocsX86(
832 atom_index: AtomIndex,760 atom_index: AtomIndex,
833 atom_code: []u8,761 atom_code: []u8,
834 atom_relocs: []align(1) const macho.relocation_info,762 atom_relocs: []align(1) const macho.relocation_info,
835 reverse_lookup: []u32,
836 context: RelocContext,763 context: RelocContext,
837) !void {764) !void {
838 const atom = zld.getAtom(atom_index);765 const atom = zld.getAtom(atom_index);
...@@ -847,33 +774,34 @@ fn resolveRelocsX86(...@@ -847,33 +774,34 @@ fn resolveRelocsX86(
847 .X86_64_RELOC_SUBTRACTOR => {774 .X86_64_RELOC_SUBTRACTOR => {
848 assert(subtractor == null);775 assert(subtractor == null);
849776
850 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{777 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
851 @tagName(rel_type),778 @tagName(rel_type),
852 rel.r_address,779 rel.r_address,
853 rel.r_symbolnum,780 rel.r_symbolnum,
854 atom.file,781 atom.getFile(),
855 });782 });
856783
857 subtractor = parseRelocTarget(zld, atom_index, rel, reverse_lookup);784 subtractor = parseRelocTarget(zld, atom_index, rel);
858 continue;785 continue;
859 },786 },
860 else => {},787 else => {},
861 }788 }
862789
863 const target = parseRelocTarget(zld, atom_index, rel, reverse_lookup);790 const target = parseRelocTarget(zld, atom_index, rel);
864 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);791 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);
865792
866 log.debug(" RELA({s}) @ {x} => %{d} in object({?})", .{793 log.debug(" RELA({s}) @ {x} => %{d} in object({?})", .{
867 @tagName(rel_type),794 @tagName(rel_type),
868 rel.r_address,795 rel.r_address,
869 target.sym_index,796 target.sym_index,
870 target.file,797 target.getFile(),
871 });798 });
872799
873 const source_addr = blk: {800 const source_addr = blk: {
874 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());801 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
875 break :blk source_sym.n_value + rel_offset;802 break :blk source_sym.n_value + rel_offset;
876 };803 };
804 const is_via_got = relocRequiresGot(zld, rel);
877 const is_tlv = is_tlv: {805 const is_tlv = is_tlv: {
878 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());806 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
879 const header = zld.sections.items(.header)[source_sym.n_sect - 1];807 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
...@@ -882,7 +810,7 @@ fn resolveRelocsX86(...@@ -882,7 +810,7 @@ fn resolveRelocsX86(
882810
883 log.debug(" | source_addr = 0x{x}", .{source_addr});811 log.debug(" | source_addr = 0x{x}", .{source_addr});
884812
885 const target_addr = try getRelocTargetAddress(zld, rel, target, is_tlv);813 const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
886814
887 switch (rel_type) {815 switch (rel_type) {
888 .X86_64_RELOC_BRANCH => {816 .X86_64_RELOC_BRANCH => {
...@@ -1016,9 +944,10 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {...@@ -1016,9 +944,10 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
1016}944}
1017945
1018pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []align(1) const macho.relocation_info {946pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []align(1) const macho.relocation_info {
1019 const atom = zld.getAtomPtr(atom_index);947 const atom = zld.getAtom(atom_index);
1020 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.948 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
1021 const object = zld.objects.items[atom.getFile().?];949 const object = zld.objects.items[atom.getFile().?];
950 const cache = object.relocs_lookup[atom.sym_index];
1022951
1023 const source_sect = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {952 const source_sect = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
1024 const source_sect = object.getSourceSection(source_sym.n_sect - 1);953 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
...@@ -1036,43 +965,7 @@ pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []align(1) const macho.re...@@ -1036,43 +965,7 @@ pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []align(1) const macho.re
1036 };965 };
1037966
1038 const relocs = object.getRelocs(source_sect);967 const relocs = object.getRelocs(source_sect);
1039968 return relocs[cache.start..][0..cache.len];
1040 if (atom.cached_relocs_start == -1) {
1041 const indexes = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
1042 const offset = source_sym.n_value - source_sect.addr;
1043 break :blk filterRelocs(relocs, offset, offset + atom.size);
1044 } else filterRelocs(relocs, 0, atom.size);
1045 atom.cached_relocs_start = indexes.start;
1046 atom.cached_relocs_len = indexes.len;
1047 }
1048
1049 return relocs[@intCast(u32, atom.cached_relocs_start)..][0..atom.cached_relocs_len];
1050}
1051
1052fn filterRelocs(
1053 relocs: []align(1) const macho.relocation_info,
1054 start_addr: u64,
1055 end_addr: u64,
1056) struct { start: i32, len: u32 } {
1057 const Predicate = struct {
1058 addr: u64,
1059
1060 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
1061 return rel.r_address >= self.addr;
1062 }
1063 };
1064 const LPredicate = struct {
1065 addr: u64,
1066
1067 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
1068 return rel.r_address < self.addr;
1069 }
1070 };
1071
1072 const start = @import("zld.zig").bsearch(macho.relocation_info, relocs, Predicate{ .addr = end_addr });
1073 const len = @import("zld.zig").lsearch(macho.relocation_info, relocs[start..], LPredicate{ .addr = start_addr });
1074
1075 return .{ .start = @intCast(i32, start), .len = @intCast(u32, len) };
1076}969}
1077970
1078pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 {971pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 {
...@@ -1111,3 +1004,22 @@ pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 {...@@ -1111,3 +1004,22 @@ pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 {
1111 .load_store_128 => try math.divExact(u12, narrowed, 16),1004 .load_store_128 => try math.divExact(u12, narrowed, 16),
1112 };1005 };
1113}1006}
1007
1008pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
1009 switch (zld.options.target.cpu.arch) {
1010 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
1011 .ARM64_RELOC_GOT_LOAD_PAGE21,
1012 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
1013 .ARM64_RELOC_POINTER_TO_GOT,
1014 => return true,
1015 else => return false,
1016 },
1017 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
1018 .X86_64_RELOC_GOT,
1019 .X86_64_RELOC_GOT_LOAD,
1020 => return true,
1021 else => return false,
1022 },
1023 else => unreachable,
1024 }
1025}
src/link/MachO/dead_strip.zig+150-51
...@@ -2,6 +2,7 @@...@@ -2,6 +2,7 @@
22
3const std = @import("std");3const std = @import("std");
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const eh_frame = @import("eh_frame.zig");
5const log = std.log.scoped(.dead_strip);6const log = std.log.scoped(.dead_strip);
6const macho = std.macho;7const macho = std.macho;
7const math = std.math;8const math = std.math;
...@@ -11,13 +12,14 @@ const Allocator = mem.Allocator;...@@ -11,13 +12,14 @@ const Allocator = mem.Allocator;
11const AtomIndex = @import("zld.zig").AtomIndex;12const AtomIndex = @import("zld.zig").AtomIndex;
12const Atom = @import("ZldAtom.zig");13const Atom = @import("ZldAtom.zig");
13const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;14const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
15const UnwindInfo = @import("UnwindInfo.zig");
14const Zld = @import("zld.zig").Zld;16const Zld = @import("zld.zig").Zld;
1517
16const N_DEAD = @import("zld.zig").N_DEAD;18const N_DEAD = @import("zld.zig").N_DEAD;
1719
18const AtomTable = std.AutoHashMap(AtomIndex, void);20const AtomTable = std.AutoHashMap(AtomIndex, void);
1921
20pub fn gcAtoms(zld: *Zld, reverse_lookups: [][]u32) Allocator.Error!void {22pub fn gcAtoms(zld: *Zld) !void {
21 const gpa = zld.gpa;23 const gpa = zld.gpa;
2224
23 var arena = std.heap.ArenaAllocator.init(gpa);25 var arena = std.heap.ArenaAllocator.init(gpa);
...@@ -30,7 +32,7 @@ pub fn gcAtoms(zld: *Zld, reverse_lookups: [][]u32) Allocator.Error!void {...@@ -30,7 +32,7 @@ pub fn gcAtoms(zld: *Zld, reverse_lookups: [][]u32) Allocator.Error!void {
30 try alive.ensureTotalCapacity(@intCast(u32, zld.atoms.items.len));32 try alive.ensureTotalCapacity(@intCast(u32, zld.atoms.items.len));
3133
32 try collectRoots(zld, &roots);34 try collectRoots(zld, &roots);
33 mark(zld, roots, &alive, reverse_lookups);35 try mark(zld, roots, &alive);
34 prune(zld, alive);36 prune(zld, alive);
35}37}
3638
...@@ -45,10 +47,10 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {...@@ -45,10 +47,10 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
45 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error47 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
46 _ = try roots.getOrPut(atom_index);48 _ = try roots.getOrPut(atom_index);
4749
48 log.debug("root(ATOM({d}, %{d}, {d}))", .{50 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
49 atom_index,51 atom_index,
50 zld.getAtom(atom_index).sym_index,52 zld.getAtom(atom_index).sym_index,
51 zld.getAtom(atom_index).file,53 zld.getAtom(atom_index).getFile(),
52 });54 });
53 },55 },
54 else => |other| {56 else => |other| {
...@@ -63,32 +65,15 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {...@@ -63,32 +65,15 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
63 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error65 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
64 _ = try roots.getOrPut(atom_index);66 _ = try roots.getOrPut(atom_index);
6567
66 log.debug("root(ATOM({d}, %{d}, {d}))", .{68 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
67 atom_index,69 atom_index,
68 zld.getAtom(atom_index).sym_index,70 zld.getAtom(atom_index).sym_index,
69 zld.getAtom(atom_index).file,71 zld.getAtom(atom_index).getFile(),
70 });72 });
71 }73 }
72 },74 },
73 }75 }
7476
75 // TODO just a temp until we learn how to parse unwind records
76 for (zld.globals.items) |global| {
77 if (mem.eql(u8, "___gxx_personality_v0", zld.getSymbolName(global))) {
78 const object = zld.objects.items[global.getFile().?];
79 if (object.getAtomIndexForSymbol(global.sym_index)) |atom_index| {
80 _ = try roots.getOrPut(atom_index);
81
82 log.debug("root(ATOM({d}, %{d}, {d}))", .{
83 atom_index,
84 zld.getAtom(atom_index).sym_index,
85 zld.getAtom(atom_index).file,
86 });
87 }
88 break;
89 }
90 }
91
92 for (zld.objects.items) |object| {77 for (zld.objects.items) |object| {
93 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;78 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
9479
...@@ -119,28 +104,23 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {...@@ -119,28 +104,23 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
119 if (is_gc_root) {104 if (is_gc_root) {
120 try roots.putNoClobber(atom_index, {});105 try roots.putNoClobber(atom_index, {});
121106
122 log.debug("root(ATOM({d}, %{d}, {d}))", .{107 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
123 atom_index,108 atom_index,
124 zld.getAtom(atom_index).sym_index,109 zld.getAtom(atom_index).sym_index,
125 zld.getAtom(atom_index).file,110 zld.getAtom(atom_index).getFile(),
126 });111 });
127 }112 }
128 }113 }
129 }114 }
130}115}
131116
132fn markLive(117fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {
133 zld: *Zld,
134 atom_index: AtomIndex,
135 alive: *AtomTable,
136 reverse_lookups: [][]u32,
137) void {
138 if (alive.contains(atom_index)) return;118 if (alive.contains(atom_index)) return;
139119
140 const atom = zld.getAtom(atom_index);120 const atom = zld.getAtom(atom_index);
141 const sym_loc = atom.getSymbolWithLoc();121 const sym_loc = atom.getSymbolWithLoc();
142122
143 log.debug("mark(ATOM({d}, %{d}, {d}))", .{ atom_index, sym_loc.sym_index, sym_loc.file });123 log.debug("mark(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
144124
145 alive.putAssumeCapacityNoClobber(atom_index, {});125 alive.putAssumeCapacityNoClobber(atom_index, {});
146126
...@@ -151,14 +131,13 @@ fn markLive(...@@ -151,14 +131,13 @@ fn markLive(
151 if (header.isZerofill()) return;131 if (header.isZerofill()) return;
152132
153 const relocs = Atom.getAtomRelocs(zld, atom_index);133 const relocs = Atom.getAtomRelocs(zld, atom_index);
154 const reverse_lookup = reverse_lookups[atom.getFile().?];
155 for (relocs) |rel| {134 for (relocs) |rel| {
156 const target = switch (cpu_arch) {135 const target = switch (cpu_arch) {
157 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {136 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
158 .ARM64_RELOC_ADDEND => continue,137 .ARM64_RELOC_ADDEND => continue,
159 else => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),138 else => Atom.parseRelocTarget(zld, atom_index, rel),
160 },139 },
161 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),140 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel),
162 else => unreachable,141 else => unreachable,
163 };142 };
164 const target_sym = zld.getSymbol(target);143 const target_sym = zld.getSymbol(target);
...@@ -174,21 +153,21 @@ fn markLive(...@@ -174,21 +153,21 @@ fn markLive(
174153
175 const object = zld.objects.items[target.getFile().?];154 const object = zld.objects.items[target.getFile().?];
176 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;155 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;
177 log.debug(" following ATOM({d}, %{d}, {d})", .{156 log.debug(" following ATOM({d}, %{d}, {?d})", .{
178 target_atom_index,157 target_atom_index,
179 zld.getAtom(target_atom_index).sym_index,158 zld.getAtom(target_atom_index).sym_index,
180 zld.getAtom(target_atom_index).file,159 zld.getAtom(target_atom_index).getFile(),
181 });160 });
182161
183 markLive(zld, target_atom_index, alive, reverse_lookups);162 markLive(zld, target_atom_index, alive);
184 }163 }
185}164}
186165
187fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookups: [][]u32) bool {166fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
188 const atom = zld.getAtom(atom_index);167 const atom = zld.getAtom(atom_index);
189 const sym_loc = atom.getSymbolWithLoc();168 const sym_loc = atom.getSymbolWithLoc();
190169
191 log.debug("refersLive(ATOM({d}, %{d}, {d}))", .{ atom_index, sym_loc.sym_index, sym_loc.file });170 log.debug("refersLive(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
192171
193 const cpu_arch = zld.options.target.cpu.arch;172 const cpu_arch = zld.options.target.cpu.arch;
194173
...@@ -197,14 +176,13 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookup...@@ -197,14 +176,13 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookup
197 assert(!header.isZerofill());176 assert(!header.isZerofill());
198177
199 const relocs = Atom.getAtomRelocs(zld, atom_index);178 const relocs = Atom.getAtomRelocs(zld, atom_index);
200 const reverse_lookup = reverse_lookups[atom.getFile().?];
201 for (relocs) |rel| {179 for (relocs) |rel| {
202 const target = switch (cpu_arch) {180 const target = switch (cpu_arch) {
203 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {181 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
204 .ARM64_RELOC_ADDEND => continue,182 .ARM64_RELOC_ADDEND => continue,
205 else => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),183 else => Atom.parseRelocTarget(zld, atom_index, rel),
206 },184 },
207 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),185 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel),
208 else => unreachable,186 else => unreachable,
209 };187 };
210188
...@@ -214,10 +192,10 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookup...@@ -214,10 +192,10 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookup
214 continue;192 continue;
215 };193 };
216 if (alive.contains(target_atom_index)) {194 if (alive.contains(target_atom_index)) {
217 log.debug(" refers live ATOM({d}, %{d}, {d})", .{195 log.debug(" refers live ATOM({d}, %{d}, {?d})", .{
218 target_atom_index,196 target_atom_index,
219 zld.getAtom(target_atom_index).sym_index,197 zld.getAtom(target_atom_index).sym_index,
220 zld.getAtom(target_atom_index).file,198 zld.getAtom(target_atom_index).getFile(),
221 });199 });
222 return true;200 return true;
223 }201 }
...@@ -226,10 +204,10 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookup...@@ -226,10 +204,10 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookup
226 return false;204 return false;
227}205}
228206
229fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable, reverse_lookups: [][]u32) void {207fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
230 var it = roots.keyIterator();208 var it = roots.keyIterator();
231 while (it.next()) |root| {209 while (it.next()) |root| {
232 markLive(zld, root.*, alive, reverse_lookups);210 markLive(zld, root.*, alive);
233 }211 }
234212
235 var loop: bool = true;213 var loop: bool = true;
...@@ -251,14 +229,135 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable, reverse_lookups: [][]u32...@@ -251,14 +229,135 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable, reverse_lookups: [][]u32
251 const source_sect = object.getSourceSection(sect_id);229 const source_sect = object.getSourceSection(sect_id);
252230
253 if (source_sect.isDontDeadStripIfReferencesLive()) {231 if (source_sect.isDontDeadStripIfReferencesLive()) {
254 if (refersLive(zld, atom_index, alive.*, reverse_lookups)) {232 if (refersLive(zld, atom_index, alive.*)) {
255 markLive(zld, atom_index, alive, reverse_lookups);233 markLive(zld, atom_index, alive);
256 loop = true;234 loop = true;
257 }235 }
258 }236 }
259 }237 }
260 }238 }
261 }239 }
240
241 for (zld.objects.items) |object, object_id| {
242 // Traverse unwind and eh_frame records noting if the source symbol has been marked, and if so,
243 // marking all references as live.
244 // TODO I am currently assuming there will always be __unwind_info section emitted which implies
245 // we will not traverse __eh_frame in isolation. This however is only true for more recent versions
246 // of macOS so if there is a feature request to handle earlier versions of macOS, the following
247 // bit code needs updating as well.
248 if (object.hasUnwindRecords()) {
249 try markUnwindRecords(zld, @intCast(u32, object_id), alive);
250 }
251 }
252}
253
254fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
255 const object = &zld.objects.items[object_id];
256 const cpu_arch = zld.options.target.cpu.arch;
257
258 const unwind_records = object.getUnwindRecords();
259 var it = object.getEhFrameRecordsIterator();
260
261 for (object.exec_atoms.items) |atom_index| {
262 const record_id = object.unwind_records_lookup.get(atom_index) orelse continue;
263 if (object.unwind_relocs_lookup[record_id].dead) continue; // already marked, nothing to do
264 if (!alive.contains(atom_index)) {
265 // Mark the record dead and continue.
266 object.unwind_relocs_lookup[record_id].dead = true;
267 if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| {
268 object.eh_frame_relocs_lookup.getPtr(fde_offset).?.dead = true;
269 }
270 continue;
271 }
272
273 const record = unwind_records[record_id];
274 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
275 const fde_offset = object.eh_frame_records_lookup.get(atom_index).?;
276 it.seekTo(fde_offset);
277 const fde = (try it.next()).?;
278
279 const cie_ptr = fde.getCiePointer();
280 const cie_offset = fde_offset + 4 - cie_ptr;
281 it.seekTo(cie_offset);
282 const cie = (try it.next()).?;
283
284 switch (cpu_arch) {
285 .aarch64 => {
286 // Mark FDE references which should include any referenced LSDA record
287 const relocs = eh_frame.getRelocs(zld, object_id, fde_offset);
288 for (relocs) |rel| {
289 const target = UnwindInfo.parseRelocTarget(
290 zld,
291 object_id,
292 rel,
293 fde.data,
294 @intCast(i32, fde_offset) + 4,
295 );
296 const target_sym = zld.getSymbol(target);
297 if (!target_sym.undf()) blk: {
298 const target_object = zld.objects.items[target.getFile().?];
299 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse
300 break :blk;
301 markLive(zld, target_atom_index, alive);
302 }
303 }
304 },
305 .x86_64 => {
306 const lsda_ptr = try fde.getLsdaPointer(cie, .{
307 .base_addr = object.eh_frame_sect.?.addr,
308 .base_offset = fde_offset,
309 });
310 if (lsda_ptr) |lsda_address| {
311 // Mark LSDA record as live
312 const sym_index = object.getSymbolByAddress(lsda_address, null);
313 const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;
314 markLive(zld, target_atom_index, alive);
315 }
316 },
317 else => unreachable,
318 }
319
320 // Mark CIE references which should include any referenced personalities
321 // that are defined locally.
322 if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| {
323 const target_sym = zld.getSymbol(target);
324 if (!target_sym.undf()) {
325 const target_object = zld.objects.items[target.getFile().?];
326 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
327 markLive(zld, target_atom_index, alive);
328 }
329 }
330 } else {
331 if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| {
332 const target = UnwindInfo.parseRelocTarget(
333 zld,
334 object_id,
335 rel,
336 mem.asBytes(&record),
337 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
338 );
339 const target_sym = zld.getSymbol(target);
340 if (!target_sym.undf()) {
341 const target_object = zld.objects.items[target.getFile().?];
342 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
343 markLive(zld, target_atom_index, alive);
344 }
345 }
346
347 if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| {
348 const target = UnwindInfo.parseRelocTarget(
349 zld,
350 object_id,
351 rel,
352 mem.asBytes(&record),
353 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
354 );
355 const target_object = zld.objects.items[target.getFile().?];
356 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
357 markLive(zld, target_atom_index, alive);
358 }
359 }
360 }
262}361}
263362
264fn prune(zld: *Zld, alive: AtomTable) void {363fn prune(zld: *Zld, alive: AtomTable) void {
...@@ -275,10 +374,10 @@ fn prune(zld: *Zld, alive: AtomTable) void {...@@ -275,10 +374,10 @@ fn prune(zld: *Zld, alive: AtomTable) void {
275 const atom = zld.getAtom(atom_index);374 const atom = zld.getAtom(atom_index);
276 const sym_loc = atom.getSymbolWithLoc();375 const sym_loc = atom.getSymbolWithLoc();
277376
278 log.debug("prune(ATOM({d}, %{d}, {d}))", .{377 log.debug("prune(ATOM({d}, %{d}, {?d}))", .{
279 atom_index,378 atom_index,
280 sym_loc.sym_index,379 sym_loc.sym_index,
281 sym_loc.file,380 sym_loc.getFile(),
282 });381 });
283 log.debug(" {s} in {s}", .{ zld.getSymbolName(sym_loc), object.name });382 log.debug(" {s} in {s}", .{ zld.getSymbolName(sym_loc), object.name });
284383
src/link/MachO/eh_frame.zig created+617
...@@ -0,0 +1,617 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const macho = std.macho;
4const math = std.math;
5const mem = std.mem;
6const leb = std.leb;
7const log = std.log.scoped(.eh_frame);
8
9const Allocator = mem.Allocator;
10const AtomIndex = @import("zld.zig").AtomIndex;
11const Atom = @import("ZldAtom.zig");
12const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
13const UnwindInfo = @import("UnwindInfo.zig");
14const Zld = @import("zld.zig").Zld;
15
16pub fn scanRelocs(zld: *Zld) !void {
17 const gpa = zld.gpa;
18
19 for (zld.objects.items) |*object, object_id| {
20 var cies = std.AutoHashMap(u32, void).init(gpa);
21 defer cies.deinit();
22
23 var it = object.getEhFrameRecordsIterator();
24
25 for (object.exec_atoms.items) |atom_index| {
26 const fde_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue;
27 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
28 it.seekTo(fde_offset);
29 const fde = (try it.next()).?;
30
31 const cie_ptr = fde.getCiePointer();
32 const cie_offset = fde_offset + 4 - cie_ptr;
33
34 if (!cies.contains(cie_offset)) {
35 try cies.putNoClobber(cie_offset, {});
36 it.seekTo(cie_offset);
37 const cie = (try it.next()).?;
38 try cie.scanRelocs(zld, @intCast(u32, object_id), cie_offset);
39 }
40 }
41 }
42}
43
44pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
45 const sect_id = zld.getSectionByName("__TEXT", "__eh_frame") orelse return;
46 const sect = &zld.sections.items(.header)[sect_id];
47 sect.@"align" = 3;
48 sect.size = 0;
49
50 const cpu_arch = zld.options.target.cpu.arch;
51 const gpa = zld.gpa;
52 var size: u32 = 0;
53
54 for (zld.objects.items) |*object| {
55 var cies = std.AutoHashMap(u32, u32).init(gpa);
56 defer cies.deinit();
57
58 var eh_it = object.getEhFrameRecordsIterator();
59
60 for (object.exec_atoms.items) |atom_index| {
61 const fde_record_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue;
62 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
63
64 const record_id = unwind_info.records_lookup.get(atom_index) orelse continue;
65 const record = unwind_info.records.items[record_id];
66
67 // TODO skip this check if no __compact_unwind is present
68 const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
69 if (!is_dwarf) continue;
70
71 eh_it.seekTo(fde_record_offset);
72 const source_fde_record = (try eh_it.next()).?;
73
74 const cie_ptr = source_fde_record.getCiePointer();
75 const cie_offset = fde_record_offset + 4 - cie_ptr;
76
77 const gop = try cies.getOrPut(cie_offset);
78 if (!gop.found_existing) {
79 eh_it.seekTo(cie_offset);
80 const source_cie_record = (try eh_it.next()).?;
81 gop.value_ptr.* = size;
82 size += source_cie_record.getSize();
83 }
84
85 size += source_fde_record.getSize();
86 }
87 }
88
89 sect.size = size;
90}
91
92pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
93 const sect_id = zld.getSectionByName("__TEXT", "__eh_frame") orelse return;
94 const sect = zld.sections.items(.header)[sect_id];
95 const seg_id = zld.sections.items(.segment_index)[sect_id];
96 const seg = zld.segments.items[seg_id];
97
98 const cpu_arch = zld.options.target.cpu.arch;
99 const gpa = zld.gpa;
100
101 var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa);
102 defer {
103 for (eh_records.values()) |*rec| {
104 rec.deinit(gpa);
105 }
106 eh_records.deinit();
107 }
108
109 var eh_frame_offset: u32 = 0;
110
111 for (zld.objects.items) |*object, object_id| {
112 try eh_records.ensureUnusedCapacity(2 * @intCast(u32, object.exec_atoms.items.len));
113
114 var cies = std.AutoHashMap(u32, u32).init(gpa);
115 defer cies.deinit();
116
117 var eh_it = object.getEhFrameRecordsIterator();
118
119 for (object.exec_atoms.items) |atom_index| {
120 const fde_record_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue;
121 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
122
123 const record_id = unwind_info.records_lookup.get(atom_index) orelse continue;
124 const record = &unwind_info.records.items[record_id];
125
126 // TODO skip this check if no __compact_unwind is present
127 const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
128 if (!is_dwarf) continue;
129
130 eh_it.seekTo(fde_record_offset);
131 const source_fde_record = (try eh_it.next()).?;
132
133 const cie_ptr = source_fde_record.getCiePointer();
134 const cie_offset = fde_record_offset + 4 - cie_ptr;
135
136 const gop = try cies.getOrPut(cie_offset);
137 if (!gop.found_existing) {
138 eh_it.seekTo(cie_offset);
139 const source_cie_record = (try eh_it.next()).?;
140 var cie_record = try source_cie_record.toOwned(gpa);
141 try cie_record.relocate(zld, @intCast(u32, object_id), .{
142 .source_offset = cie_offset,
143 .out_offset = eh_frame_offset,
144 .sect_addr = sect.addr,
145 });
146 eh_records.putAssumeCapacityNoClobber(eh_frame_offset, cie_record);
147 gop.value_ptr.* = eh_frame_offset;
148 eh_frame_offset += cie_record.getSize();
149 }
150
151 var fde_record = try source_fde_record.toOwned(gpa);
152 fde_record.setCiePointer(eh_frame_offset + 4 - gop.value_ptr.*);
153 try fde_record.relocate(zld, @intCast(u32, object_id), .{
154 .source_offset = fde_record_offset,
155 .out_offset = eh_frame_offset,
156 .sect_addr = sect.addr,
157 });
158
159 switch (cpu_arch) {
160 .aarch64 => {}, // relocs take care of LSDA pointers
161 .x86_64 => {
162 // We need to parse LSDA pointer and relocate ourselves.
163 const cie_record = eh_records.get(
164 eh_frame_offset + 4 - fde_record.getCiePointer(),
165 ).?;
166 const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
167 .base_addr = object.eh_frame_sect.?.addr,
168 .base_offset = fde_record_offset,
169 });
170 if (source_lsda_ptr) |ptr| {
171 const sym_index = object.getSymbolByAddress(ptr, null);
172 const sym = object.symtab[sym_index];
173 try fde_record.setLsdaPointer(cie_record, sym.n_value, .{
174 .base_addr = sect.addr,
175 .base_offset = eh_frame_offset,
176 });
177 }
178 },
179 else => unreachable,
180 }
181
182 eh_records.putAssumeCapacityNoClobber(eh_frame_offset, fde_record);
183
184 UnwindInfo.UnwindEncoding.setDwarfSectionOffset(
185 &record.compactUnwindEncoding,
186 cpu_arch,
187 @intCast(u24, eh_frame_offset),
188 );
189
190 const cie_record = eh_records.get(
191 eh_frame_offset + 4 - fde_record.getCiePointer(),
192 ).?;
193 const lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
194 .base_addr = sect.addr,
195 .base_offset = eh_frame_offset,
196 });
197 if (lsda_ptr) |ptr| {
198 record.lsda = ptr - seg.vmaddr;
199 }
200
201 eh_frame_offset += fde_record.getSize();
202 }
203 }
204
205 var buffer = std.ArrayList(u8).init(gpa);
206 defer buffer.deinit();
207 const writer = buffer.writer();
208
209 for (eh_records.values()) |record| {
210 try writer.writeIntLittle(u32, record.size);
211 try buffer.appendSlice(record.data);
212 }
213
214 try zld.file.pwriteAll(buffer.items, sect.offset);
215}
216const EhFrameRecordTag = enum { cie, fde };
217
218pub fn EhFrameRecord(comptime is_mutable: bool) type {
219 return struct {
220 tag: EhFrameRecordTag,
221 size: u32,
222 data: if (is_mutable) []u8 else []const u8,
223
224 const Record = @This();
225
226 pub fn deinit(rec: *Record, gpa: Allocator) void {
227 comptime assert(is_mutable);
228 gpa.free(rec.data);
229 }
230
231 pub fn toOwned(rec: Record, gpa: Allocator) Allocator.Error!EhFrameRecord(true) {
232 const data = try gpa.dupe(u8, rec.data);
233 return EhFrameRecord(true){
234 .tag = rec.tag,
235 .size = rec.size,
236 .data = data,
237 };
238 }
239
240 pub inline fn getSize(rec: Record) u32 {
241 return 4 + rec.size;
242 }
243
244 pub fn scanRelocs(
245 rec: Record,
246 zld: *Zld,
247 object_id: u32,
248 source_offset: u32,
249 ) !void {
250 if (rec.getPersonalityPointerReloc(zld, object_id, source_offset)) |target| {
251 try Atom.addGotEntry(zld, target);
252 }
253 }
254
255 pub fn getTargetSymbolAddress(rec: Record, ctx: struct {
256 base_addr: u64,
257 base_offset: u64,
258 }) u64 {
259 assert(rec.tag == .fde);
260 const addend = mem.readIntLittle(i64, rec.data[4..][0..8]);
261 return @intCast(u64, @intCast(i64, ctx.base_addr + ctx.base_offset + 8) + addend);
262 }
263
264 pub fn setTargetSymbolAddress(rec: *Record, value: u64, ctx: struct {
265 base_addr: u64,
266 base_offset: u64,
267 }) !void {
268 assert(rec.tag == .fde);
269 const addend = @intCast(i64, value) - @intCast(i64, ctx.base_addr + ctx.base_offset + 8);
270 mem.writeIntLittle(i64, addend, rec.data[4..][0..8]);
271 }
272
273 pub fn getPersonalityPointerReloc(
274 rec: Record,
275 zld: *Zld,
276 object_id: u32,
277 source_offset: u32,
278 ) ?SymbolWithLoc {
279 const cpu_arch = zld.options.target.cpu.arch;
280 const relocs = getRelocs(zld, object_id, source_offset);
281 for (relocs) |rel| {
282 switch (cpu_arch) {
283 .aarch64 => {
284 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
285 switch (rel_type) {
286 .ARM64_RELOC_SUBTRACTOR,
287 .ARM64_RELOC_UNSIGNED,
288 => continue,
289 .ARM64_RELOC_POINTER_TO_GOT => {},
290 else => unreachable,
291 }
292 },
293 .x86_64 => {
294 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
295 switch (rel_type) {
296 .X86_64_RELOC_GOT => {},
297 else => unreachable,
298 }
299 },
300 else => unreachable,
301 }
302 const target = UnwindInfo.parseRelocTarget(
303 zld,
304 object_id,
305 rel,
306 rec.data,
307 @intCast(i32, source_offset) + 4,
308 );
309 return target;
310 }
311 return null;
312 }
313
314 pub fn relocate(rec: *Record, zld: *Zld, object_id: u32, ctx: struct {
315 source_offset: u32,
316 out_offset: u32,
317 sect_addr: u64,
318 }) !void {
319 comptime assert(is_mutable);
320
321 const cpu_arch = zld.options.target.cpu.arch;
322 const relocs = getRelocs(zld, object_id, ctx.source_offset);
323
324 for (relocs) |rel| {
325 const target = UnwindInfo.parseRelocTarget(
326 zld,
327 object_id,
328 rel,
329 rec.data,
330 @intCast(i32, ctx.source_offset) + 4,
331 );
332 const rel_offset = @intCast(u32, rel.r_address - @intCast(i32, ctx.source_offset) - 4);
333 const source_addr = ctx.sect_addr + rel_offset + ctx.out_offset + 4;
334
335 switch (cpu_arch) {
336 .aarch64 => {
337 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
338 switch (rel_type) {
339 .ARM64_RELOC_SUBTRACTOR => {
340 // Address of the __eh_frame in the source object file
341 },
342 .ARM64_RELOC_POINTER_TO_GOT => {
343 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
344 const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse
345 return error.Overflow;
346 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);
347 },
348 .ARM64_RELOC_UNSIGNED => {
349 assert(rel.r_extern == 1);
350 const target_addr = try Atom.getRelocTargetAddress(zld, target, false, false);
351 const result = @intCast(i64, target_addr) - @intCast(i64, source_addr);
352 mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @intCast(i64, result));
353 },
354 else => unreachable,
355 }
356 },
357 .x86_64 => {
358 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
359 switch (rel_type) {
360 .X86_64_RELOC_GOT => {
361 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
362 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
363 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
364 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
365 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], disp);
366 },
367 else => unreachable,
368 }
369 },
370 else => unreachable,
371 }
372 }
373 }
374
375 pub fn getCiePointer(rec: Record) u32 {
376 assert(rec.tag == .fde);
377 return mem.readIntLittle(u32, rec.data[0..4]);
378 }
379
380 pub fn setCiePointer(rec: *Record, ptr: u32) void {
381 assert(rec.tag == .fde);
382 mem.writeIntLittle(u32, rec.data[0..4], ptr);
383 }
384
385 pub fn getAugmentationString(rec: Record) []const u8 {
386 assert(rec.tag == .cie);
387 return mem.sliceTo(@ptrCast([*:0]const u8, rec.data.ptr + 5), 0);
388 }
389
390 pub fn getPersonalityPointer(rec: Record, ctx: struct {
391 base_addr: u64,
392 base_offset: u64,
393 }) !?u64 {
394 assert(rec.tag == .cie);
395 const aug_str = rec.getAugmentationString();
396
397 var stream = std.io.fixedBufferStream(rec.data[9 + aug_str.len ..]);
398 var creader = std.io.countingReader(stream.reader());
399 const reader = creader.reader();
400
401 for (aug_str) |ch, i| switch (ch) {
402 'z' => if (i > 0) {
403 return error.BadDwarfCfi;
404 } else {
405 _ = try leb.readULEB128(u64, reader);
406 },
407 'R' => {
408 _ = try reader.readByte();
409 },
410 'P' => {
411 const enc = try reader.readByte();
412 const offset = ctx.base_offset + 13 + aug_str.len + creader.bytes_read;
413 const ptr = try getEncodedPointer(enc, @intCast(i64, ctx.base_addr + offset), reader);
414 return ptr;
415 },
416 'L' => {
417 _ = try reader.readByte();
418 },
419 'S', 'B', 'G' => {},
420 else => return error.BadDwarfCfi,
421 };
422
423 return null;
424 }
425
426 pub fn getLsdaPointer(rec: Record, cie: Record, ctx: struct {
427 base_addr: u64,
428 base_offset: u64,
429 }) !?u64 {
430 assert(rec.tag == .fde);
431 const enc = (try cie.getLsdaEncoding()) orelse return null;
432 var stream = std.io.fixedBufferStream(rec.data[20..]);
433 const reader = stream.reader();
434 _ = try reader.readByte();
435 const offset = ctx.base_offset + 25;
436 const ptr = try getEncodedPointer(enc, @intCast(i64, ctx.base_addr + offset), reader);
437 return ptr;
438 }
439
440 pub fn setLsdaPointer(rec: *Record, cie: Record, value: u64, ctx: struct {
441 base_addr: u64,
442 base_offset: u64,
443 }) !void {
444 assert(rec.tag == .fde);
445 const enc = (try cie.getLsdaEncoding()) orelse unreachable;
446 var stream = std.io.fixedBufferStream(rec.data[21..]);
447 const writer = stream.writer();
448 const offset = ctx.base_offset + 25;
449 try setEncodedPointer(enc, @intCast(i64, ctx.base_addr + offset), value, writer);
450 }
451
452 fn getLsdaEncoding(rec: Record) !?u8 {
453 assert(rec.tag == .cie);
454 const aug_str = rec.getAugmentationString();
455
456 const base_offset = 9 + aug_str.len;
457 var stream = std.io.fixedBufferStream(rec.data[base_offset..]);
458 var creader = std.io.countingReader(stream.reader());
459 const reader = creader.reader();
460
461 for (aug_str) |ch, i| switch (ch) {
462 'z' => if (i > 0) {
463 return error.BadDwarfCfi;
464 } else {
465 _ = try leb.readULEB128(u64, reader);
466 },
467 'R' => {
468 _ = try reader.readByte();
469 },
470 'P' => {
471 const enc = try reader.readByte();
472 _ = try getEncodedPointer(enc, 0, reader);
473 },
474 'L' => {
475 const enc = try reader.readByte();
476 return enc;
477 },
478 'S', 'B', 'G' => {},
479 else => return error.BadDwarfCfi,
480 };
481
482 return null;
483 }
484
485 fn getEncodedPointer(enc: u8, pcrel_offset: i64, reader: anytype) !?u64 {
486 if (enc == EH_PE.omit) return null;
487
488 var ptr: i64 = switch (enc & 0x0F) {
489 EH_PE.absptr => @bitCast(i64, try reader.readIntLittle(u64)),
490 EH_PE.udata2 => @bitCast(i16, try reader.readIntLittle(u16)),
491 EH_PE.udata4 => @bitCast(i32, try reader.readIntLittle(u32)),
492 EH_PE.udata8 => @bitCast(i64, try reader.readIntLittle(u64)),
493 EH_PE.uleb128 => @bitCast(i64, try leb.readULEB128(u64, reader)),
494 EH_PE.sdata2 => try reader.readIntLittle(i16),
495 EH_PE.sdata4 => try reader.readIntLittle(i32),
496 EH_PE.sdata8 => try reader.readIntLittle(i64),
497 EH_PE.sleb128 => try leb.readILEB128(i64, reader),
498 else => return null,
499 };
500
501 switch (enc & 0x70) {
502 EH_PE.absptr => {},
503 EH_PE.pcrel => ptr += pcrel_offset,
504 EH_PE.datarel,
505 EH_PE.textrel,
506 EH_PE.funcrel,
507 EH_PE.aligned,
508 => return null,
509 else => return null,
510 }
511
512 return @bitCast(u64, ptr);
513 }
514
515 fn setEncodedPointer(enc: u8, pcrel_offset: i64, value: u64, writer: anytype) !void {
516 if (enc == EH_PE.omit) return;
517
518 var actual = @intCast(i64, value);
519
520 switch (enc & 0x70) {
521 EH_PE.absptr => {},
522 EH_PE.pcrel => actual -= pcrel_offset,
523 EH_PE.datarel,
524 EH_PE.textrel,
525 EH_PE.funcrel,
526 EH_PE.aligned,
527 => unreachable,
528 else => unreachable,
529 }
530
531 switch (enc & 0x0F) {
532 EH_PE.absptr => try writer.writeIntLittle(u64, @bitCast(u64, actual)),
533 EH_PE.udata2 => try writer.writeIntLittle(u16, @bitCast(u16, @intCast(i16, actual))),
534 EH_PE.udata4 => try writer.writeIntLittle(u32, @bitCast(u32, @intCast(i32, actual))),
535 EH_PE.udata8 => try writer.writeIntLittle(u64, @bitCast(u64, actual)),
536 EH_PE.uleb128 => try leb.writeULEB128(writer, @bitCast(u64, actual)),
537 EH_PE.sdata2 => try writer.writeIntLittle(i16, @intCast(i16, actual)),
538 EH_PE.sdata4 => try writer.writeIntLittle(i32, @intCast(i32, actual)),
539 EH_PE.sdata8 => try writer.writeIntLittle(i64, actual),
540 EH_PE.sleb128 => try leb.writeILEB128(writer, actual),
541 else => unreachable,
542 }
543 }
544 };
545}
546
547pub fn getRelocs(
548 zld: *Zld,
549 object_id: u32,
550 source_offset: u32,
551) []align(1) const macho.relocation_info {
552 const object = &zld.objects.items[object_id];
553 assert(object.hasEhFrameRecords());
554 const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
555 return &[0]macho.relocation_info{};
556 const all_relocs = object.getRelocs(object.eh_frame_sect.?);
557 return all_relocs[urel.reloc.start..][0..urel.reloc.len];
558}
559
560pub const Iterator = struct {
561 data: []const u8,
562 pos: u32 = 0,
563
564 pub fn next(it: *Iterator) !?EhFrameRecord(false) {
565 if (it.pos >= it.data.len) return null;
566
567 var stream = std.io.fixedBufferStream(it.data[it.pos..]);
568 const reader = stream.reader();
569
570 var size = try reader.readIntLittle(u32);
571 if (size == 0xFFFFFFFF) {
572 log.err("MachO doesn't support 64bit DWARF CFI __eh_frame records", .{});
573 return error.BadDwarfCfi;
574 }
575
576 const id = try reader.readIntLittle(u32);
577 const tag: EhFrameRecordTag = if (id == 0) .cie else .fde;
578 const offset: u32 = 4;
579 const record = EhFrameRecord(false){
580 .tag = tag,
581 .size = size,
582 .data = it.data[it.pos + offset ..][0..size],
583 };
584
585 it.pos += size + offset;
586
587 return record;
588 }
589
590 pub fn reset(it: *Iterator) void {
591 it.pos = 0;
592 }
593
594 pub fn seekTo(it: *Iterator, pos: u32) void {
595 assert(pos >= 0 and pos < it.data.len);
596 it.pos = pos;
597 }
598};
599
600pub const EH_PE = struct {
601 pub const absptr = 0x00;
602 pub const uleb128 = 0x01;
603 pub const udata2 = 0x02;
604 pub const udata4 = 0x03;
605 pub const udata8 = 0x04;
606 pub const sleb128 = 0x09;
607 pub const sdata2 = 0x0A;
608 pub const sdata4 = 0x0B;
609 pub const sdata8 = 0x0C;
610 pub const pcrel = 0x10;
611 pub const textrel = 0x20;
612 pub const datarel = 0x30;
613 pub const funcrel = 0x40;
614 pub const aligned = 0x50;
615 pub const indirect = 0x80;
616 pub const omit = 0xFF;
617};
src/link/MachO/thunks.zig+4-5
...@@ -68,7 +68,7 @@ pub const Thunk = struct {...@@ -68,7 +68,7 @@ pub const Thunk = struct {
68 }68 }
69};69};
7070
71pub fn createThunks(zld: *Zld, sect_id: u8, reverse_lookups: [][]u32) !void {71pub fn createThunks(zld: *Zld, sect_id: u8) !void {
72 const header = &zld.sections.items(.header)[sect_id];72 const header = &zld.sections.items(.header)[sect_id];
73 if (header.size == 0) return;73 if (header.size == 0) return;
7474
...@@ -140,7 +140,6 @@ pub fn createThunks(zld: *Zld, sect_id: u8, reverse_lookups: [][]u32) !void {...@@ -140,7 +140,6 @@ pub fn createThunks(zld: *Zld, sect_id: u8, reverse_lookups: [][]u32) !void {
140 try scanRelocs(140 try scanRelocs(
141 zld,141 zld,
142 atom_index,142 atom_index,
143 reverse_lookups[atom.getFile().?],
144 allocated,143 allocated,
145 thunk_index,144 thunk_index,
146 group_end,145 group_end,
...@@ -214,7 +213,6 @@ fn allocateThunk(...@@ -214,7 +213,6 @@ fn allocateThunk(
214fn scanRelocs(213fn scanRelocs(
215 zld: *Zld,214 zld: *Zld,
216 atom_index: AtomIndex,215 atom_index: AtomIndex,
217 reverse_lookup: []u32,
218 allocated: std.AutoHashMap(AtomIndex, void),216 allocated: std.AutoHashMap(AtomIndex, void),
219 thunk_index: ThunkIndex,217 thunk_index: ThunkIndex,
220 group_end: AtomIndex,218 group_end: AtomIndex,
...@@ -231,7 +229,7 @@ fn scanRelocs(...@@ -231,7 +229,7 @@ fn scanRelocs(
231 for (relocs) |rel| {229 for (relocs) |rel| {
232 if (!relocNeedsThunk(rel)) continue;230 if (!relocNeedsThunk(rel)) continue;
233231
234 const target = Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup);232 const target = Atom.parseRelocTarget(zld, atom_index, rel);
235 if (isReachable(zld, atom_index, rel, base_offset, target, allocated)) continue;233 if (isReachable(zld, atom_index, rel, base_offset, target, allocated)) continue;
236234
237 log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{235 log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{
...@@ -308,7 +306,8 @@ fn isReachable(...@@ -308,7 +306,8 @@ fn isReachable(
308 if (!allocated.contains(target_atom_index)) return false;306 if (!allocated.contains(target_atom_index)) return false;
309307
310 const source_addr = source_sym.n_value + @intCast(u32, rel.r_address - base_offset);308 const source_addr = source_sym.n_value + @intCast(u32, rel.r_address - base_offset);
311 const target_addr = Atom.getRelocTargetAddress(zld, rel, target, false) catch unreachable;309 const is_via_got = Atom.relocRequiresGot(zld, rel);
310 const target_addr = Atom.getRelocTargetAddress(zld, target, is_via_got, false) catch unreachable;
312 _ = Atom.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch311 _ = Atom.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
313 return false;312 return false;
314313
src/link/MachO/zld.zig+123-104
...@@ -10,6 +10,7 @@ const mem = std.mem;...@@ -10,6 +10,7 @@ const mem = std.mem;
1010
11const aarch64 = @import("../../arch/aarch64/bits.zig");11const aarch64 = @import("../../arch/aarch64/bits.zig");
12const dead_strip = @import("dead_strip.zig");12const dead_strip = @import("dead_strip.zig");
13const eh_frame = @import("eh_frame.zig");
13const fat = @import("fat.zig");14const fat = @import("fat.zig");
14const link = @import("../../link.zig");15const link = @import("../../link.zig");
15const load_commands = @import("load_commands.zig");16const load_commands = @import("load_commands.zig");
...@@ -30,6 +31,7 @@ const LibStub = @import("../tapi.zig").LibStub;...@@ -30,6 +31,7 @@ const LibStub = @import("../tapi.zig").LibStub;
30const Object = @import("Object.zig");31const Object = @import("Object.zig");
31const StringTable = @import("../strtab.zig").StringTable;32const StringTable = @import("../strtab.zig").StringTable;
32const Trie = @import("Trie.zig");33const Trie = @import("Trie.zig");
34const UnwindInfo = @import("UnwindInfo.zig");
3335
34const Bind = @import("dyld_info/bind.zig").Bind(*const Zld, SymbolWithLoc);36const Bind = @import("dyld_info/bind.zig").Bind(*const Zld, SymbolWithLoc);
35const LazyBind = @import("dyld_info/bind.zig").LazyBind(*const Zld, SymbolWithLoc);37const LazyBind = @import("dyld_info/bind.zig").LazyBind(*const Zld, SymbolWithLoc);
...@@ -389,6 +391,14 @@ pub const Zld = struct {...@@ -389,6 +391,14 @@ pub const Zld = struct {
389 break :blk null;391 break :blk null;
390 }392 }
391393
394 // We handle unwind info separately.
395 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
396 break :blk null;
397 }
398 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
399 break :blk null;
400 }
401
392 if (sect.isCode()) {402 if (sect.isCode()) {
393 break :blk self.getSectionByName("__TEXT", "__text") orelse try self.initSection(403 break :blk self.getSectionByName("__TEXT", "__text") orelse try self.initSection(
394 "__TEXT",404 "__TEXT",
...@@ -402,12 +412,6 @@ pub const Zld = struct {...@@ -402,12 +412,6 @@ pub const Zld = struct {
402 }412 }
403413
404 if (sect.isDebug()) {414 if (sect.isDebug()) {
405 // TODO debug attributes
406 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
407 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
408 sect.flags, segname, sectname,
409 });
410 }
411 break :blk null;415 break :blk null;
412 }416 }
413417
...@@ -459,13 +463,6 @@ pub const Zld = struct {...@@ -459,13 +463,6 @@ pub const Zld = struct {
459 );463 );
460 },464 },
461 macho.S_COALESCED => {465 macho.S_COALESCED => {
462 // TODO unwind info
463 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
464 log.debug("TODO eh frame section: type 0x{x}, name '{s},{s}'", .{
465 sect.flags, segname, sectname,
466 });
467 break :blk null;
468 }
469 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(466 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
470 segname,467 segname,
471 sectname,468 sectname,
...@@ -937,7 +934,7 @@ pub const Zld = struct {...@@ -937,7 +934,7 @@ pub const Zld = struct {
937 }934 }
938 }935 }
939936
940 fn resolveSymbolsInObject(self: *Zld, object_id: u16, resolver: *SymbolResolver) !void {937 fn resolveSymbolsInObject(self: *Zld, object_id: u32, resolver: *SymbolResolver) !void {
941 const object = &self.objects.items[object_id];938 const object = &self.objects.items[object_id];
942 const in_symtab = object.in_symtab orelse return;939 const in_symtab = object.in_symtab orelse return;
943940
...@@ -977,7 +974,7 @@ pub const Zld = struct {...@@ -977,7 +974,7 @@ pub const Zld = struct {
977 continue;974 continue;
978 }975 }
979976
980 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id };977 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id + 1 };
981978
982 const global_index = resolver.table.get(sym_name) orelse {979 const global_index = resolver.table.get(sym_name) orelse {
983 const gpa = self.gpa;980 const gpa = self.gpa;
...@@ -1378,7 +1375,7 @@ pub const Zld = struct {...@@ -1378,7 +1375,7 @@ pub const Zld = struct {
1378 }1375 }
1379 }1376 }
13801377
1381 fn writeAtoms(self: *Zld, reverse_lookups: [][]u32) !void {1378 fn writeAtoms(self: *Zld) !void {
1382 const gpa = self.gpa;1379 const gpa = self.gpa;
1383 const slice = self.sections.slice();1380 const slice = self.sections.slice();
13841381
...@@ -1386,6 +1383,7 @@ pub const Zld = struct {...@@ -1386,6 +1383,7 @@ pub const Zld = struct {
1386 const header = slice.items(.header)[sect_id];1383 const header = slice.items(.header)[sect_id];
1387 var atom_index = first_atom_index;1384 var atom_index = first_atom_index;
13881385
1386 if (atom_index == 0) continue;
1389 if (header.isZerofill()) continue;1387 if (header.isZerofill()) continue;
13901388
1391 var buffer = std.ArrayList(u8).init(gpa);1389 var buffer = std.ArrayList(u8).init(gpa);
...@@ -1407,7 +1405,7 @@ pub const Zld = struct {...@@ -1407,7 +1405,7 @@ pub const Zld = struct {
1407 log.debug(" (adding ATOM(%{d}, '{s}') from object({?}) to buffer)", .{1405 log.debug(" (adding ATOM(%{d}, '{s}') from object({?}) to buffer)", .{
1408 atom.sym_index,1406 atom.sym_index,
1409 self.getSymbolName(atom.getSymbolWithLoc()),1407 self.getSymbolName(atom.getSymbolWithLoc()),
1410 atom.file,1408 atom.getFile(),
1411 });1409 });
1412 if (padding_size > 0) {1410 if (padding_size > 0) {
1413 log.debug(" (with padding {x})", .{padding_size});1411 log.debug(" (with padding {x})", .{padding_size});
...@@ -1460,7 +1458,6 @@ pub const Zld = struct {...@@ -1460,7 +1458,6 @@ pub const Zld = struct {
1460 atom_index,1458 atom_index,
1461 buffer.items[offset..][0..size],1459 buffer.items[offset..][0..size],
1462 relocs,1460 relocs,
1463 reverse_lookups[atom.getFile().?],
1464 );1461 );
1465 }1462 }
14661463
...@@ -1501,9 +1498,10 @@ pub const Zld = struct {...@@ -1501,9 +1498,10 @@ pub const Zld = struct {
1501 while (i < slice.len) : (i += 1) {1498 while (i < slice.len) : (i += 1) {
1502 const section = self.sections.get(i);1499 const section = self.sections.get(i);
1503 if (section.header.size == 0) {1500 if (section.header.size == 0) {
1504 log.debug("pruning section {s},{s}", .{1501 log.debug("pruning section {s},{s} {d}", .{
1505 section.header.segName(),1502 section.header.segName(),
1506 section.header.sectName(),1503 section.header.sectName(),
1504 section.first_atom_index,
1507 });1505 });
1508 continue;1506 continue;
1509 }1507 }
...@@ -1519,7 +1517,7 @@ pub const Zld = struct {...@@ -1519,7 +1517,7 @@ pub const Zld = struct {
1519 }1517 }
1520 }1518 }
15211519
1522 fn calcSectionSizes(self: *Zld, reverse_lookups: [][]u32) !void {1520 fn calcSectionSizes(self: *Zld) !void {
1523 const slice = self.sections.slice();1521 const slice = self.sections.slice();
1524 for (slice.items(.header)) |*header, sect_id| {1522 for (slice.items(.header)) |*header, sect_id| {
1525 if (header.size == 0) continue;1523 if (header.size == 0) continue;
...@@ -1528,6 +1526,8 @@ pub const Zld = struct {...@@ -1528,6 +1526,8 @@ pub const Zld = struct {
1528 }1526 }
15291527
1530 var atom_index = slice.items(.first_atom_index)[sect_id];1528 var atom_index = slice.items(.first_atom_index)[sect_id];
1529 if (atom_index == 0) continue;
1530
1531 header.size = 0;1531 header.size = 0;
1532 header.@"align" = 0;1532 header.@"align" = 0;
15331533
...@@ -1556,7 +1556,7 @@ pub const Zld = struct {...@@ -1556,7 +1556,7 @@ pub const Zld = struct {
1556 if (mem.eql(u8, header.sectName(), "__stub_helper")) continue;1556 if (mem.eql(u8, header.sectName(), "__stub_helper")) continue;
15571557
1558 // Create jump/branch range extenders if needed.1558 // Create jump/branch range extenders if needed.
1559 try thunks.createThunks(self, @intCast(u8, sect_id), reverse_lookups);1559 try thunks.createThunks(self, @intCast(u8, sect_id));
1560 }1560 }
1561 }1561 }
1562 }1562 }
...@@ -1601,8 +1601,6 @@ pub const Zld = struct {...@@ -1601,8 +1601,6 @@ pub const Zld = struct {
16011601
1602 const slice = self.sections.slice();1602 const slice = self.sections.slice();
1603 for (slice.items(.header)[indexes.start..indexes.end]) |*header, sect_id| {1603 for (slice.items(.header)[indexes.start..indexes.end]) |*header, sect_id| {
1604 var atom_index = slice.items(.first_atom_index)[indexes.start + sect_id];
1605
1606 const alignment = try math.powi(u32, 2, header.@"align");1604 const alignment = try math.powi(u32, 2, header.@"align");
1607 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);1605 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
1608 const n_sect = @intCast(u8, indexes.start + sect_id + 1);1606 const n_sect = @intCast(u8, indexes.start + sect_id + 1);
...@@ -1613,48 +1611,51 @@ pub const Zld = struct {...@@ -1613,48 +1611,51 @@ pub const Zld = struct {
1613 @intCast(u32, segment.fileoff + start_aligned);1611 @intCast(u32, segment.fileoff + start_aligned);
1614 header.addr = segment.vmaddr + start_aligned;1612 header.addr = segment.vmaddr + start_aligned;
16151613
1616 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{1614 var atom_index = slice.items(.first_atom_index)[indexes.start + sect_id];
1617 n_sect,1615 if (atom_index > 0) {
1618 header.segName(),1616 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1619 header.sectName(),1617 n_sect,
1620 });1618 header.segName(),
1619 header.sectName(),
1620 });
16211621
1622 while (true) {1622 while (true) {
1623 const atom = self.getAtom(atom_index);1623 const atom = self.getAtom(atom_index);
1624 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());1624 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1625 sym.n_value += header.addr;1625 sym.n_value += header.addr;
1626 sym.n_sect = n_sect;1626 sym.n_sect = n_sect;
16271627
1628 log.debug(" ATOM(%{d}, '{s}') @{x}", .{1628 log.debug(" ATOM(%{d}, '{s}') @{x}", .{
1629 atom.sym_index,1629 atom.sym_index,
1630 self.getSymbolName(atom.getSymbolWithLoc()),1630 self.getSymbolName(atom.getSymbolWithLoc()),
1631 sym.n_value,1631 sym.n_value,
1632 });1632 });
16331633
1634 if (atom.getFile() != null) {1634 if (atom.getFile() != null) {
1635 // Update each symbol contained within the atom1635 // Update each symbol contained within the atom
1636 var it = Atom.getInnerSymbolsIterator(self, atom_index);1636 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1637 while (it.next()) |sym_loc| {1637 while (it.next()) |sym_loc| {
1638 const inner_sym = self.getSymbolPtr(sym_loc);1638 const inner_sym = self.getSymbolPtr(sym_loc);
1639 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(1639 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1640 self,1640 self,
1641 atom_index,1641 atom_index,
1642 sym_loc.sym_index,1642 sym_loc.sym_index,
1643 );1643 );
1644 inner_sym.n_sect = n_sect;1644 inner_sym.n_sect = n_sect;
1645 }1645 }
16461646
1647 // If there is a section alias, update it now too1647 // If there is a section alias, update it now too
1648 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {1648 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
1649 const alias = self.getSymbolPtr(sym_loc);1649 const alias = self.getSymbolPtr(sym_loc);
1650 alias.n_value = sym.n_value;1650 alias.n_value = sym.n_value;
1651 alias.n_sect = n_sect;1651 alias.n_sect = n_sect;
1652 }
1652 }1653 }
1653 }
16541654
1655 if (atom.next_index) |next_index| {1655 if (atom.next_index) |next_index| {
1656 atom_index = next_index;1656 atom_index = next_index;
1657 } else break;1657 } else break;
1658 }
1658 }1659 }
16591660
1660 start = start_aligned + header.size;1661 start = start_aligned + header.size;
...@@ -1675,7 +1676,7 @@ pub const Zld = struct {...@@ -1675,7 +1676,7 @@ pub const Zld = struct {
1675 reserved2: u32 = 0,1676 reserved2: u32 = 0,
1676 };1677 };
16771678
1678 fn initSection(1679 pub fn initSection(
1679 self: *Zld,1680 self: *Zld,
1680 segname: []const u8,1681 segname: []const u8,
1681 sectname: []const u8,1682 sectname: []const u8,
...@@ -1685,7 +1686,7 @@ pub const Zld = struct {...@@ -1685,7 +1686,7 @@ pub const Zld = struct {
1685 log.debug("creating section '{s},{s}'", .{ segname, sectname });1686 log.debug("creating section '{s},{s}'", .{ segname, sectname });
1686 const index = @intCast(u8, self.sections.slice().len);1687 const index = @intCast(u8, self.sections.slice().len);
1687 try self.sections.append(gpa, .{1688 try self.sections.append(gpa, .{
1688 .segment_index = undefined,1689 .segment_index = undefined, // Segments will be created automatically later down the pipeline
1689 .header = .{1690 .header = .{
1690 .sectname = makeStaticString(sectname),1691 .sectname = makeStaticString(sectname),
1691 .segname = makeStaticString(segname),1692 .segname = makeStaticString(segname),
...@@ -1693,13 +1694,13 @@ pub const Zld = struct {...@@ -1693,13 +1694,13 @@ pub const Zld = struct {
1693 .reserved1 = opts.reserved1,1694 .reserved1 = opts.reserved1,
1694 .reserved2 = opts.reserved2,1695 .reserved2 = opts.reserved2,
1695 },1696 },
1696 .first_atom_index = undefined,1697 .first_atom_index = 0,
1697 .last_atom_index = undefined,1698 .last_atom_index = 0,
1698 });1699 });
1699 return index;1700 return index;
1700 }1701 }
17011702
1702 inline fn getSegmentPrecedence(segname: []const u8) u4 {1703 fn getSegmentPrecedence(segname: []const u8) u4 {
1703 if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;1704 if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;
1704 if (mem.eql(u8, segname, "__TEXT")) return 0x1;1705 if (mem.eql(u8, segname, "__TEXT")) return 0x1;
1705 if (mem.eql(u8, segname, "__DATA_CONST")) return 0x2;1706 if (mem.eql(u8, segname, "__DATA_CONST")) return 0x2;
...@@ -1708,14 +1709,14 @@ pub const Zld = struct {...@@ -1708,14 +1709,14 @@ pub const Zld = struct {
1708 return 0x4;1709 return 0x4;
1709 }1710 }
17101711
1711 inline fn getSegmentMemoryProtection(segname: []const u8) macho.vm_prot_t {1712 fn getSegmentMemoryProtection(segname: []const u8) macho.vm_prot_t {
1712 if (mem.eql(u8, segname, "__PAGEZERO")) return macho.PROT.NONE;1713 if (mem.eql(u8, segname, "__PAGEZERO")) return macho.PROT.NONE;
1713 if (mem.eql(u8, segname, "__TEXT")) return macho.PROT.READ | macho.PROT.EXEC;1714 if (mem.eql(u8, segname, "__TEXT")) return macho.PROT.READ | macho.PROT.EXEC;
1714 if (mem.eql(u8, segname, "__LINKEDIT")) return macho.PROT.READ;1715 if (mem.eql(u8, segname, "__LINKEDIT")) return macho.PROT.READ;
1715 return macho.PROT.READ | macho.PROT.WRITE;1716 return macho.PROT.READ | macho.PROT.WRITE;
1716 }1717 }
17171718
1718 inline fn getSectionPrecedence(header: macho.section_64) u8 {1719 fn getSectionPrecedence(header: macho.section_64) u8 {
1719 const segment_precedence: u4 = getSegmentPrecedence(header.segName());1720 const segment_precedence: u4 = getSegmentPrecedence(header.segName());
1720 const section_precedence: u4 = blk: {1721 const section_precedence: u4 = blk: {
1721 if (header.isCode()) {1722 if (header.isCode()) {
...@@ -1732,10 +1733,11 @@ pub const Zld = struct {...@@ -1732,10 +1733,11 @@ pub const Zld = struct {
1732 macho.S_ZEROFILL => break :blk 0xf,1733 macho.S_ZEROFILL => break :blk 0xf,
1733 macho.S_THREAD_LOCAL_REGULAR => break :blk 0xd,1734 macho.S_THREAD_LOCAL_REGULAR => break :blk 0xd,
1734 macho.S_THREAD_LOCAL_ZEROFILL => break :blk 0xe,1735 macho.S_THREAD_LOCAL_ZEROFILL => break :blk 0xe,
1735 else => if (mem.eql(u8, "__eh_frame", header.sectName()))1736 else => {
1736 break :blk 0xf1737 if (mem.eql(u8, "__unwind_info", header.sectName())) break :blk 0xe;
1737 else1738 if (mem.eql(u8, "__eh_frame", header.sectName())) break :blk 0xf;
1738 break :blk 0x3,1739 break :blk 0x3;
1740 },
1739 }1741 }
1740 };1742 };
1741 return (@intCast(u8, segment_precedence) << 4) + section_precedence;1743 return (@intCast(u8, segment_precedence) << 4) + section_precedence;
...@@ -1768,8 +1770,8 @@ pub const Zld = struct {...@@ -1768,8 +1770,8 @@ pub const Zld = struct {
1768 }1770 }
1769 }1771 }
17701772
1771 fn writeLinkeditSegmentData(self: *Zld, reverse_lookups: [][]u32) !void {1773 fn writeLinkeditSegmentData(self: *Zld) !void {
1772 try self.writeDyldInfoData(reverse_lookups);1774 try self.writeDyldInfoData();
1773 try self.writeFunctionStarts();1775 try self.writeFunctionStarts();
1774 try self.writeDataInCode();1776 try self.writeDataInCode();
1775 try self.writeSymtabs();1777 try self.writeSymtabs();
...@@ -1806,7 +1808,7 @@ pub const Zld = struct {...@@ -1806,7 +1808,7 @@ pub const Zld = struct {
1806 }1808 }
1807 }1809 }
18081810
1809 fn collectRebaseData(self: *Zld, rebase: *Rebase, reverse_lookups: [][]u32) !void {1811 fn collectRebaseData(self: *Zld, rebase: *Rebase) !void {
1810 log.debug("collecting rebase data", .{});1812 log.debug("collecting rebase data", .{});
18111813
1812 // First, unpack GOT entries1814 // First, unpack GOT entries
...@@ -1862,6 +1864,7 @@ pub const Zld = struct {...@@ -1862,6 +1864,7 @@ pub const Zld = struct {
18621864
1863 const cpu_arch = self.options.target.cpu.arch;1865 const cpu_arch = self.options.target.cpu.arch;
1864 var atom_index = slice.items(.first_atom_index)[sect_id];1866 var atom_index = slice.items(.first_atom_index)[sect_id];
1867 if (atom_index == 0) continue;
18651868
1866 while (true) {1869 while (true) {
1867 const atom = self.getAtom(atom_index);1870 const atom = self.getAtom(atom_index);
...@@ -1899,7 +1902,7 @@ pub const Zld = struct {...@@ -1899,7 +1902,7 @@ pub const Zld = struct {
1899 },1902 },
1900 else => unreachable,1903 else => unreachable,
1901 }1904 }
1902 const target = Atom.parseRelocTarget(self, atom_index, rel, reverse_lookups[atom.getFile().?]);1905 const target = Atom.parseRelocTarget(self, atom_index, rel);
1903 const target_sym = self.getSymbol(target);1906 const target_sym = self.getSymbol(target);
1904 if (target_sym.undf()) continue;1907 if (target_sym.undf()) continue;
19051908
...@@ -1962,7 +1965,10 @@ pub const Zld = struct {...@@ -1962,7 +1965,10 @@ pub const Zld = struct {
1962 }1965 }
1963 }1966 }
19641967
1965 fn collectBindData(self: *Zld, bind: *Bind, reverse_lookups: [][]u32) !void {1968 fn collectBindData(
1969 self: *Zld,
1970 bind: *Bind,
1971 ) !void {
1966 log.debug("collecting bind data", .{});1972 log.debug("collecting bind data", .{});
19671973
1968 // First, unpack GOT section1974 // First, unpack GOT section
...@@ -1993,6 +1999,7 @@ pub const Zld = struct {...@@ -1993,6 +1999,7 @@ pub const Zld = struct {
19931999
1994 const cpu_arch = self.options.target.cpu.arch;2000 const cpu_arch = self.options.target.cpu.arch;
1995 var atom_index = slice.items(.first_atom_index)[sect_id];2001 var atom_index = slice.items(.first_atom_index)[sect_id];
2002 if (atom_index == 0) continue;
19962003
1997 log.debug("{s},{s}", .{ header.segName(), header.sectName() });2004 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
19982005
...@@ -2033,7 +2040,7 @@ pub const Zld = struct {...@@ -2033,7 +2040,7 @@ pub const Zld = struct {
2033 else => unreachable,2040 else => unreachable,
2034 }2041 }
20352042
2036 const global = Atom.parseRelocTarget(self, atom_index, rel, reverse_lookups[atom.getFile().?]);2043 const global = Atom.parseRelocTarget(self, atom_index, rel);
2037 const bind_sym_name = self.getSymbolName(global);2044 const bind_sym_name = self.getSymbolName(global);
2038 const bind_sym = self.getSymbol(global);2045 const bind_sym = self.getSymbol(global);
2039 if (!bind_sym.undf()) continue;2046 if (!bind_sym.undf()) continue;
...@@ -2164,16 +2171,18 @@ pub const Zld = struct {...@@ -2164,16 +2171,18 @@ pub const Zld = struct {
2164 try trie.finalize(gpa);2171 try trie.finalize(gpa);
2165 }2172 }
21662173
2167 fn writeDyldInfoData(self: *Zld, reverse_lookups: [][]u32) !void {2174 fn writeDyldInfoData(
2175 self: *Zld,
2176 ) !void {
2168 const gpa = self.gpa;2177 const gpa = self.gpa;
21692178
2170 var rebase = Rebase{};2179 var rebase = Rebase{};
2171 defer rebase.deinit(gpa);2180 defer rebase.deinit(gpa);
2172 try self.collectRebaseData(&rebase, reverse_lookups);2181 try self.collectRebaseData(&rebase);
21732182
2174 var bind = Bind{};2183 var bind = Bind{};
2175 defer bind.deinit(gpa);2184 defer bind.deinit(gpa);
2176 try self.collectBindData(&bind, reverse_lookups);2185 try self.collectBindData(&bind);
21772186
2178 var lazy_bind = LazyBind{};2187 var lazy_bind = LazyBind{};
2179 defer lazy_bind.deinit(gpa);2188 defer lazy_bind.deinit(gpa);
...@@ -2873,12 +2882,12 @@ pub const Zld = struct {...@@ -2873,12 +2882,12 @@ pub const Zld = struct {
2873 return buf;2882 return buf;
2874 }2883 }
28752884
2876 pub inline fn getAtomPtr(self: *Zld, atom_index: AtomIndex) *Atom {2885 pub fn getAtomPtr(self: *Zld, atom_index: AtomIndex) *Atom {
2877 assert(atom_index < self.atoms.items.len);2886 assert(atom_index < self.atoms.items.len);
2878 return &self.atoms.items[atom_index];2887 return &self.atoms.items[atom_index];
2879 }2888 }
28802889
2881 pub inline fn getAtom(self: Zld, atom_index: AtomIndex) Atom {2890 pub fn getAtom(self: Zld, atom_index: AtomIndex) Atom {
2882 assert(atom_index < self.atoms.items.len);2891 assert(atom_index < self.atoms.items.len);
2883 return self.atoms.items[atom_index];2892 return self.atoms.items[atom_index];
2884 }2893 }
...@@ -2889,17 +2898,17 @@ pub const Zld = struct {...@@ -2889,17 +2898,17 @@ pub const Zld = struct {
2889 } else return null;2898 } else return null;
2890 }2899 }
28912900
2892 pub inline fn getSegment(self: Zld, sect_id: u8) macho.segment_command_64 {2901 pub fn getSegment(self: Zld, sect_id: u8) macho.segment_command_64 {
2893 const index = self.sections.items(.segment_index)[sect_id];2902 const index = self.sections.items(.segment_index)[sect_id];
2894 return self.segments.items[index];2903 return self.segments.items[index];
2895 }2904 }
28962905
2897 pub inline fn getSegmentPtr(self: *Zld, sect_id: u8) *macho.segment_command_64 {2906 pub fn getSegmentPtr(self: *Zld, sect_id: u8) *macho.segment_command_64 {
2898 const index = self.sections.items(.segment_index)[sect_id];2907 const index = self.sections.items(.segment_index)[sect_id];
2899 return &self.segments.items[index];2908 return &self.segments.items[index];
2900 }2909 }
29012910
2902 pub inline fn getLinkeditSegmentPtr(self: *Zld) *macho.segment_command_64 {2911 pub fn getLinkeditSegmentPtr(self: *Zld) *macho.segment_command_64 {
2903 assert(self.segments.items.len > 0);2912 assert(self.segments.items.len > 0);
2904 const seg = &self.segments.items[self.segments.items.len - 1];2913 const seg = &self.segments.items[self.segments.items.len - 1];
2905 assert(mem.eql(u8, seg.segName(), "__LINKEDIT"));2914 assert(mem.eql(u8, seg.segName(), "__LINKEDIT"));
...@@ -3384,6 +3393,8 @@ pub const Zld = struct {...@@ -3384,6 +3393,8 @@ pub const Zld = struct {
3384 const slice = self.sections.slice();3393 const slice = self.sections.slice();
3385 for (slice.items(.first_atom_index)) |first_atom_index, sect_id| {3394 for (slice.items(.first_atom_index)) |first_atom_index, sect_id| {
3386 var atom_index = first_atom_index;3395 var atom_index = first_atom_index;
3396 if (atom_index == 0) continue;
3397
3387 const header = slice.items(.header)[sect_id];3398 const header = slice.items(.header)[sect_id];
33883399
3389 log.debug("{s},{s}", .{ header.segName(), header.sectName() });3400 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
...@@ -3412,7 +3423,7 @@ pub const Zld = struct {...@@ -3412,7 +3423,7 @@ pub const Zld = struct {
3412 sym.n_value,3423 sym.n_value,
3413 atom.size,3424 atom.size,
3414 atom.alignment,3425 atom.alignment,
3415 atom.file,3426 atom.getFile(),
3416 sym.n_sect,3427 sym.n_sect,
3417 });3428 });
34183429
...@@ -3475,19 +3486,19 @@ const IndirectPointer = struct {...@@ -3475,19 +3486,19 @@ const IndirectPointer = struct {
3475 }3486 }
3476};3487};
34773488
3478pub const SymbolWithLoc = struct {3489pub const SymbolWithLoc = extern struct {
3479 // Index into the respective symbol table.3490 // Index into the respective symbol table.
3480 sym_index: u32,3491 sym_index: u32,
34813492
3482 // -1 means it's a synthetic global.3493 // 0 means it's a synthetic global.
3483 file: i32 = -1,3494 file: u32 = 0,
34843495
3485 pub inline fn getFile(self: SymbolWithLoc) ?u31 {3496 pub fn getFile(self: SymbolWithLoc) ?u32 {
3486 if (self.file == -1) return null;3497 if (self.file == 0) return null;
3487 return @intCast(u31, self.file);3498 return self.file - 1;
3488 }3499 }
34893500
3490 pub inline fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {3501 pub fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {
3491 return self.file == other.file and self.sym_index == other.sym_index;3502 return self.file == other.file and self.sym_index == other.sym_index;
3492 }3503 }
3493};3504};
...@@ -3965,7 +3976,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3965,7 +3976,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3965 };3976 };
39663977
3967 for (zld.objects.items) |_, object_id| {3978 for (zld.objects.items) |_, object_id| {
3968 try zld.resolveSymbolsInObject(@intCast(u16, object_id), &resolver);3979 try zld.resolveSymbolsInObject(@intCast(u32, object_id), &resolver);
3969 }3980 }
39703981
3971 try zld.resolveSymbolsInArchives(&resolver);3982 try zld.resolveSymbolsInArchives(&resolver);
...@@ -3995,16 +4006,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3995,16 +4006,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3995 }4006 }
39964007
3997 for (zld.objects.items) |*object, object_id| {4008 for (zld.objects.items) |*object, object_id| {
3998 try object.splitIntoAtoms(&zld, @intCast(u31, object_id));4009 try object.splitIntoAtoms(&zld, @intCast(u32, object_id));
3999 }
4000
4001 var reverse_lookups: [][]u32 = try arena.alloc([]u32, zld.objects.items.len);
4002 for (zld.objects.items) |object, i| {
4003 reverse_lookups[i] = try object.createReverseSymbolLookup(arena);
4004 }4010 }
40054011
4006 if (gc_sections) {4012 if (gc_sections) {
4007 try dead_strip.gcAtoms(&zld, reverse_lookups);4013 try dead_strip.gcAtoms(&zld);
4008 }4014 }
40094015
4010 try zld.createDyldPrivateAtom();4016 try zld.createDyldPrivateAtom();
...@@ -4019,13 +4025,24 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -4019,13 +4025,24 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
4019 if (header.isZerofill()) continue;4025 if (header.isZerofill()) continue;
40204026
4021 const relocs = Atom.getAtomRelocs(&zld, atom_index);4027 const relocs = Atom.getAtomRelocs(&zld, atom_index);
4022 try Atom.scanAtomRelocs(&zld, atom_index, relocs, reverse_lookups[atom.getFile().?]);4028 try Atom.scanAtomRelocs(&zld, atom_index, relocs);
4023 }4029 }
4024 }4030 }
40254031
4032 try eh_frame.scanRelocs(&zld);
4033 try UnwindInfo.scanRelocs(&zld);
4034
4026 try zld.createDyldStubBinderGotAtom();4035 try zld.createDyldStubBinderGotAtom();
40274036
4028 try zld.calcSectionSizes(reverse_lookups);4037 try zld.calcSectionSizes();
4038
4039 var unwind_info = UnwindInfo{ .gpa = zld.gpa };
4040 defer unwind_info.deinit();
4041 try unwind_info.collect(&zld);
4042
4043 try eh_frame.calcSectionSize(&zld, &unwind_info);
4044 try unwind_info.calcSectionSize(&zld);
4045
4029 try zld.pruneAndSortSections();4046 try zld.pruneAndSortSections();
4030 try zld.createSegments();4047 try zld.createSegments();
4031 try zld.allocateSegments();4048 try zld.allocateSegments();
...@@ -4039,8 +4056,10 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -4039,8 +4056,10 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
4039 zld.logAtoms();4056 zld.logAtoms();
4040 }4057 }
40414058
4042 try zld.writeAtoms(reverse_lookups);4059 try zld.writeAtoms();
4043 try zld.writeLinkeditSegmentData(reverse_lookups);4060 try eh_frame.write(&zld, &unwind_info);
4061 try unwind_info.write(&zld);
4062 try zld.writeLinkeditSegmentData();
40444063
4045 // If the last section of __DATA segment is zerofill section, we need to ensure4064 // If the last section of __DATA segment is zerofill section, we need to ensure
4046 // that the free space between the end of the last non-zerofill section of __DATA4065 // that the free space between the end of the last non-zerofill section of __DATA