authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-01-22 00:44:44+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-01-22 00:44:44+01:00
log562d52e23dce703b0eb45a212522588274afbd38
tree15f993f1eccd1b29bbb8423751e552ed80a4178a
parent7f635ae7bdf63da19d09763c7fdbdc61fa035282
parent241cabdf3df5568adf118f19fb0dda856c51ca27
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14397 from ziglang/macho-unwind-info

zld: handle parsing and synthesising unwind info in the MachO linker

18 files changed, 2468 insertions(+), 426 deletions(-)

CMakeLists.txt+2
......@@ -593,10 +593,12 @@ set(ZIG_STAGE2_SOURCES
593593 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
594594 "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
595595 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
596 "${CMAKE_SOURCE_DIR}/src/link/MachO/UnwindInfo.zig"
596597 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
597598 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/bind.zig"
598599 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/Rebase.zig"
599600 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
601 "${CMAKE_SOURCE_DIR}/src/link/MachO/eh_frame.zig"
600602 "${CMAKE_SOURCE_DIR}/src/link/MachO/fat.zig"
601603 "${CMAKE_SOURCE_DIR}/src/link/MachO/load_commands.zig"
602604 "${CMAKE_SOURCE_DIR}/src/link/MachO/thunks.zig"
lib/std/macho.zig+2
......@@ -2011,6 +2011,7 @@ pub const UNWIND_PERSONALITY_MASK: u32 = 0x30000000;
20112011// x86_64
20122012pub const UNWIND_X86_64_MODE_MASK: u32 = 0x0F000000;
20132013pub const UNWIND_X86_64_MODE = enum(u4) {
2014 OLD = 0,
20142015 RBP_FRAME = 1,
20152016 STACK_IMMD = 2,
20162017 STACK_IND = 3,
......@@ -2039,6 +2040,7 @@ pub const UNWIND_X86_64_REG = enum(u3) {
20392040// arm64
20402041pub const UNWIND_ARM64_MODE_MASK: u32 = 0x0F000000;
20412042pub const UNWIND_ARM64_MODE = enum(u4) {
2043 OLD = 0,
20422044 FRAMELESS = 2,
20432045 DWARF = 3,
20442046 FRAME = 4,
src/link.zig+3
......@@ -697,6 +697,7 @@ pub const File = struct {
697697 /// TODO audit this error set. most of these should be collapsed into one error,
698698 /// and ErrorFlags should be updated to convey the meaning to the user.
699699 pub const FlushError = error{
700 BadDwarfCfi,
700701 CacheUnavailable,
701702 CurrentWorkingDirectoryUnlinked,
702703 DivisionByZero,
......@@ -737,6 +738,8 @@ pub const File = struct {
737738 MissingEndForExpression,
738739 /// TODO: this should be removed from the error set in favor of using ErrorFlags
739740 MissingMainEntrypoint,
741 /// TODO: this should be removed from the error set in favor of using ErrorFlags
742 MissingSection,
740743 MissingSymbol,
741744 MissingTableSymbols,
742745 ModuleNameMismatch,
src/link/MachO/Object.zig+432-94
......@@ -8,6 +8,7 @@ const std = @import("std");
88const build_options = @import("build_options");
99const assert = std.debug.assert;
1010const dwarf = std.dwarf;
11const eh_frame = @import("eh_frame.zig");
1112const fs = std.fs;
1213const io = std.io;
1314const log = std.log.scoped(.link);
......@@ -24,6 +25,7 @@ const DwarfInfo = @import("DwarfInfo.zig");
2425const LoadCommandIterator = macho.LoadCommandIterator;
2526const Zld = @import("zld.zig").Zld;
2627const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
28const UnwindInfo = @import("UnwindInfo.zig");
2729
2830name: []const u8,
2931mtime: u64,
......@@ -44,6 +46,8 @@ symtab: []macho.nlist_64 = undefined,
4446/// Can be undefined as set together with in_symtab.
4547source_symtab_lookup: []u32 = undefined,
4648/// Can be undefined as set together with in_symtab.
49reverse_symtab_lookup: []u32 = undefined,
50/// Can be undefined as set together with in_symtab.
4751source_address_lookup: []i64 = undefined,
4852/// Can be undefined as set together with in_symtab.
4953source_section_index_lookup: []i64 = undefined,
......@@ -53,22 +57,49 @@ strtab_lookup: []u32 = undefined,
5357atom_by_index_table: []AtomIndex = undefined,
5458/// Can be undefined as set together with in_symtab.
5559globals_lookup: []i64 = undefined,
60/// Can be undefined as set together with in_symtab.
61relocs_lookup: []RelocEntry = undefined,
5662
5763atoms: 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
5981pub fn deinit(self: *Object, gpa: Allocator) void {
6082 self.atoms.deinit(gpa);
83 self.exec_atoms.deinit(gpa);
6184 gpa.free(self.name);
6285 gpa.free(self.contents);
6386 if (self.in_symtab) |_| {
6487 gpa.free(self.source_symtab_lookup);
88 gpa.free(self.reverse_symtab_lookup);
6589 gpa.free(self.source_address_lookup);
6690 gpa.free(self.source_section_index_lookup);
6791 gpa.free(self.strtab_lookup);
6892 gpa.free(self.symtab);
6993 gpa.free(self.atom_by_index_table);
7094 gpa.free(self.globals_lookup);
95 gpa.free(self.relocs_lookup);
7196 }
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);
72103}
73104
74105pub 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)
105136 .ncmds = self.header.ncmds,
106137 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
107138 };
108 while (it.next()) |cmd| {
109 switch (cmd.cmd()) {
110 .SYMTAB => {
111 const symtab = cmd.cast(macho.symtab_command).?;
112 self.in_symtab = @ptrCast(
113 [*]const macho.nlist_64,
114 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
115 )[0..symtab.nsyms];
116 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
117
118 const nsects = self.getSourceSections().len;
119
120 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);
122 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
123 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
124 self.atom_by_index_table = try allocator.alloc(AtomIndex, self.in_symtab.?.len + nsects);
125 // This is wasteful but we need to be able to lookup source symbol address after stripping and
126 // allocating of sections.
127 self.source_address_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
128 self.source_section_index_lookup = try allocator.alloc(i64, nsects);
129
130 for (self.symtab) |*sym| {
131 sym.* = .{
132 .n_value = 0,
133 .n_sect = 0,
134 .n_desc = 0,
135 .n_strx = 0,
136 .n_type = 0,
137 };
138 }
139 const nsects = self.getSourceSections().len;
140 const symtab = while (it.next()) |cmd| switch (cmd.cmd()) {
141 .SYMTAB => break cmd.cast(macho.symtab_command).?,
142 else => {},
143 } else return;
144
145 self.in_symtab = @ptrCast(
146 [*]const macho.nlist_64,
147 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
148 )[0..symtab.nsyms];
149 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
150
151 self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects);
152 self.source_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
153 self.reverse_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
154 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
155 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
156 self.atom_by_index_table = try allocator.alloc(AtomIndex, self.in_symtab.?.len + nsects);
157 self.relocs_lookup = try allocator.alloc(RelocEntry, self.in_symtab.?.len + nsects);
158 // This is wasteful but we need to be able to lookup source symbol address after stripping and
159 // allocating of sections.
160 self.source_address_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
161 self.source_section_index_lookup = try allocator.alloc(i64, nsects);
162
163 for (self.symtab) |*sym| {
164 sym.* = .{
165 .n_value = 0,
166 .n_sect = 0,
167 .n_desc = 0,
168 .n_strx = 0,
169 .n_type = 0,
170 };
171 }
139172
140 mem.set(i64, self.globals_lookup, -1);
141 mem.set(AtomIndex, self.atom_by_index_table, 0);
142 mem.set(i64, self.source_section_index_lookup, -1);
173 mem.set(i64, self.globals_lookup, -1);
174 mem.set(AtomIndex, self.atom_by_index_table, 0);
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:
145 // 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 type
147 // and address within.
148 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(allocator, self.in_symtab.?.len);
149 defer sorted_all_syms.deinit();
181 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
182 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
183 // the GO compiler does not necessarily respect that therefore we sort immediately by type
184 // and address within.
185 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(allocator, self.in_symtab.?.len);
186 defer sorted_all_syms.deinit();
150187
151 for (self.in_symtab.?) |_, index| {
152 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
153 }
188 for (self.in_symtab.?) |_, index| {
189 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
190 }
154191
155 // We sort by type: defined < undefined, and
156 // afterwards by address in each group. Normally, dysymtab should
157 // be enough to guarantee the sort, but turns out not every compiler
158 // is kind enough to specify the symbols in the correct order.
159 sort.sort(SymbolAtIndex, sorted_all_syms.items, self, SymbolAtIndex.lessThan);
192 // We sort by type: defined < undefined, and
193 // afterwards by address in each group. Normally, dysymtab should
194 // be enough to guarantee the sort, but turns out not every compiler
195 // is kind enough to specify the symbols in the correct order.
196 sort.sort(SymbolAtIndex, sorted_all_syms.items, self, SymbolAtIndex.lessThan);
160197
161 for (sorted_all_syms.items) |sym_id, i| {
162 const sym = sym_id.getSymbol(self);
198 for (sorted_all_syms.items) |sym_id, i| {
199 const sym = sym_id.getSymbol(self);
163200
164 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);
166 }
201 if (sym.sect() and self.source_section_index_lookup[sym.n_sect - 1] == -1) {
202 self.source_section_index_lookup[sym.n_sect - 1] = @intCast(i64, i);
203 }
167204
168 self.symtab[i] = sym;
169 self.source_symtab_lookup[i] = sym_id.index;
170 self.source_address_lookup[i] = if (sym.undf()) -1 else @intCast(i64, sym.n_value);
205 self.symtab[i] = sym;
206 self.source_symtab_lookup[i] = sym_id.index;
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;
173 self.strtab_lookup[i] = @intCast(u32, sym_name_len);
174 }
210 const sym_name_len = mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.?.ptr + sym.n_strx), 0).len + 1;
211 self.strtab_lookup[i] = @intCast(u32, sym_name_len);
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,
175226 },
176 else => {},
177 }
227 });
178228 }
179229}
180230
......@@ -192,6 +242,17 @@ const SymbolAtIndex = struct {
192242 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.in_strtab.?.ptr + off), 0);
193243 }
194244
245 fn getSymbolSeniority(self: SymbolAtIndex, ctx: Context) u2 {
246 const sym = self.getSymbol(ctx);
247 if (!sym.ext()) {
248 const sym_name = self.getSymbolName(ctx);
249 if (mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L")) return 0;
250 return 1;
251 }
252 if (sym.weakDef() or sym.pext()) return 2;
253 return 3;
254 }
255
195256 /// Performs lexicographic-like check.
196257 /// * lhs and rhs defined
197258 /// * if lhs == rhs
......@@ -206,23 +267,15 @@ const SymbolAtIndex = struct {
206267 if (lhs.sect() and rhs.sect()) {
207268 if (lhs.n_value == rhs.n_value) {
208269 if (lhs.n_sect == rhs.n_sect) {
209 if (lhs.ext() and rhs.ext()) {
210 if ((lhs.pext() or lhs.weakDef()) and (rhs.pext() or rhs.weakDef())) {
211 return false;
212 } else return rhs.pext() or rhs.weakDef();
213 } else {
214 const lhs_name = lhs_index.getSymbolName(ctx);
215 const lhs_temp = mem.startsWith(u8, lhs_name, "l") or mem.startsWith(u8, lhs_name, "L");
216 const rhs_name = rhs_index.getSymbolName(ctx);
217 const rhs_temp = mem.startsWith(u8, rhs_name, "l") or mem.startsWith(u8, rhs_name, "L");
218 if (lhs_temp and rhs_temp) {
219 return false;
220 } else return rhs_temp;
221 }
270 const lhs_senior = lhs_index.getSymbolSeniority(ctx);
271 const rhs_senior = rhs_index.getSymbolSeniority(ctx);
272 if (lhs_senior == rhs_senior) {
273 return lessThanByNStrx(ctx, lhs_index, rhs_index);
274 } else return lhs_senior < rhs_senior;
222275 } else return lhs.n_sect < rhs.n_sect;
223276 } else return lhs.n_value < rhs.n_value;
224277 } else if (lhs.undf() and rhs.undf()) {
225 return false;
278 return lessThanByNStrx(ctx, lhs_index, rhs_index);
226279 } else return rhs.undf();
227280 }
228281
......@@ -295,14 +348,20 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b
295348 return lhs.header.addr < rhs.header.addr;
296349}
297350
298/// Splits input sections into Atoms.
351pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void {
352 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
353
354 try self.splitRegularSections(zld, object_id);
355 try self.parseEhFrameSection(zld, object_id);
356 try self.parseUnwindInfo(zld, object_id);
357}
358
359/// Splits input regular sections into Atoms.
299360/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section
300361/// into subsections where each subsection then represents an Atom.
301pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
362pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
302363 const gpa = zld.gpa;
303364
304 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
305
306365 const sections = self.getSourceSections();
307366 for (sections) |sect, id| {
308367 if (sect.isDebug()) continue;
......@@ -418,6 +477,9 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
418477 sect.@"align",
419478 out_sect_id,
420479 );
480 if (!sect.isZerofill()) {
481 try self.cacheRelocs(zld, atom_index);
482 }
421483 zld.addAtomToSection(atom_index);
422484 }
423485
......@@ -431,7 +493,6 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
431493 const nsyms_trailing = atom_loc.len - 1;
432494 next_sym_index += atom_loc.len;
433495
434 // TODO: We want to bubble up the first externally defined symbol here.
435496 const atom_size = if (next_sym_index < sect_start_index + sect_loc.len)
436497 symtab[next_sym_index].n_value - addr
437498 else
......@@ -461,7 +522,9 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
461522 const alias_index = self.getSectionAliasSymbolIndex(sect_id);
462523 self.atom_by_index_table[alias_index] = atom_index;
463524 }
464
525 if (!sect.isZerofill()) {
526 try self.cacheRelocs(zld, atom_index);
527 }
465528 zld.addAtomToSection(atom_index);
466529 }
467530 } else {
......@@ -476,6 +539,9 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
476539 sect.@"align",
477540 out_sect_id,
478541 );
542 if (!sect.isZerofill()) {
543 try self.cacheRelocs(zld, atom_index);
544 }
479545 zld.addAtomToSection(atom_index);
480546 }
481547 }
......@@ -484,7 +550,7 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
484550fn createAtomFromSubsection(
485551 self: *Object,
486552 zld: *Zld,
487 object_id: u31,
553 object_id: u32,
488554 sym_index: u32,
489555 inner_sym_index: u32,
490556 inner_nsyms_trailing: u32,
......@@ -497,7 +563,7 @@ fn createAtomFromSubsection(
497563 const atom = zld.getAtomPtr(atom_index);
498564 atom.inner_sym_index = inner_sym_index;
499565 atom.inner_nsyms_trailing = inner_nsyms_trailing;
500 atom.file = object_id;
566 atom.file = object_id + 1;
501567 self.symtab[sym_index].n_sect = out_sect_id + 1;
502568
503569 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
......@@ -519,9 +585,220 @@ fn createAtomFromSubsection(
519585 self.atom_by_index_table[sym_loc.sym_index] = atom_index;
520586 }
521587
588 const out_sect = zld.sections.items(.header)[out_sect_id];
589 if (out_sect.isCode() and
590 mem.eql(u8, "__TEXT", out_sect.segName()) and
591 mem.eql(u8, "__text", out_sect.sectName()))
592 {
593 // TODO currently assuming a single section for executable machine code
594 try self.exec_atoms.append(gpa, atom_index);
595 }
596
522597 return atom_index;
523598}
524599
600fn filterRelocs(
601 relocs: []align(1) const macho.relocation_info,
602 start_addr: u64,
603 end_addr: u64,
604) RelocEntry {
605 const Predicate = struct {
606 addr: u64,
607
608 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
609 return rel.r_address >= self.addr;
610 }
611 };
612 const LPredicate = struct {
613 addr: u64,
614
615 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
616 return rel.r_address < self.addr;
617 }
618 };
619
620 const start = @import("zld.zig").bsearch(macho.relocation_info, relocs, Predicate{ .addr = end_addr });
621 const len = @import("zld.zig").lsearch(macho.relocation_info, relocs[start..], LPredicate{ .addr = start_addr });
622
623 return .{ .start = @intCast(u32, start), .len = @intCast(u32, len) };
624}
625
626fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {
627 const atom = zld.getAtom(atom_index);
628
629 const source_sect = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
630 const source_sect = self.getSourceSection(source_sym.n_sect - 1);
631 assert(!source_sect.isZerofill());
632 break :blk source_sect;
633 } else blk: {
634 // If there was no matching symbol present in the source symtab, this means
635 // we are dealing with either an entire section, or part of it, but also
636 // starting at the beginning.
637 const nbase = @intCast(u32, self.in_symtab.?.len);
638 const sect_id = @intCast(u16, atom.sym_index - nbase);
639 const source_sect = self.getSourceSection(sect_id);
640 assert(!source_sect.isZerofill());
641 break :blk source_sect;
642 };
643
644 const relocs = self.getRelocs(source_sect);
645
646 self.relocs_lookup[atom.sym_index] = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
647 const offset = source_sym.n_value - source_sect.addr;
648 break :blk filterRelocs(relocs, offset, offset + atom.size);
649 } else filterRelocs(relocs, 0, atom.size);
650}
651
652fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
653 const sect = self.eh_frame_sect orelse return;
654
655 log.debug("parsing __TEXT,__eh_frame section", .{});
656
657 if (zld.getSectionByName("__TEXT", "__eh_frame") == null) {
658 _ = try zld.initSection("__TEXT", "__eh_frame", .{});
659 }
660
661 const gpa = zld.gpa;
662 const cpu_arch = zld.options.target.cpu.arch;
663 const relocs = self.getRelocs(sect);
664
665 var it = self.getEhFrameRecordsIterator();
666 var record_count: u32 = 0;
667 while (try it.next()) |_| {
668 record_count += 1;
669 }
670
671 try self.eh_frame_relocs_lookup.ensureTotalCapacity(gpa, record_count);
672 try self.eh_frame_records_lookup.ensureTotalCapacity(gpa, record_count);
673
674 it.reset();
675
676 while (try it.next()) |record| {
677 const offset = it.pos - record.getSize();
678 const rel_pos = switch (cpu_arch) {
679 .aarch64 => filterRelocs(relocs, offset, offset + record.getSize()),
680 .x86_64 => RelocEntry{ .start = 0, .len = 0 },
681 else => unreachable,
682 };
683 self.eh_frame_relocs_lookup.putAssumeCapacityNoClobber(offset, .{
684 .dead = false,
685 .reloc = rel_pos,
686 });
687
688 if (record.tag == .fde) {
689 const target = blk: {
690 switch (cpu_arch) {
691 .aarch64 => {
692 assert(rel_pos.len > 0); // TODO convert to an error as the FDE eh frame is malformed
693 // Find function symbol that this record describes
694 const rel = relocs[rel_pos.start..][rel_pos.len - 1];
695 const target = UnwindInfo.parseRelocTarget(
696 zld,
697 object_id,
698 rel,
699 it.data[offset..],
700 @intCast(i32, offset),
701 );
702 break :blk target;
703 },
704 .x86_64 => {
705 const target_address = record.getTargetSymbolAddress(.{
706 .base_addr = sect.addr,
707 .base_offset = offset,
708 });
709 const target_sym_index = self.getSymbolByAddress(target_address, null);
710 const target = if (self.getGlobal(target_sym_index)) |global_index|
711 zld.globals.items[global_index]
712 else
713 SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 };
714 break :blk target;
715 },
716 else => unreachable,
717 }
718 };
719 log.debug("FDE at offset {x} tracks {s}", .{ offset, zld.getSymbolName(target) });
720 if (target.getFile() != object_id) {
721 self.eh_frame_relocs_lookup.getPtr(offset).?.dead = true;
722 } else {
723 const atom_index = self.getAtomIndexForSymbol(target.sym_index).?;
724 self.eh_frame_records_lookup.putAssumeCapacityNoClobber(atom_index, offset);
725 }
726 }
727 }
728}
729
730fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
731 const sect = self.unwind_info_sect orelse {
732 // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,
733 // we will try fully synthesising unwind info records to somewhat match Apple ld's
734 // approach. However, we will only synthesise DWARF records and nothing more. For this reason,
735 // we still create the output `__TEXT,__unwind_info` section.
736 if (self.eh_frame_sect != null) {
737 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
738 _ = try zld.initSection("__TEXT", "__unwind_info", .{});
739 }
740 }
741 return;
742 };
743
744 log.debug("parsing unwind info in {s}", .{self.name});
745
746 const gpa = zld.gpa;
747 const cpu_arch = zld.options.target.cpu.arch;
748
749 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
750 _ = try zld.initSection("__TEXT", "__unwind_info", .{});
751 }
752
753 try self.unwind_records_lookup.ensureTotalCapacity(gpa, @intCast(u32, self.exec_atoms.items.len));
754
755 const unwind_records = self.getUnwindRecords();
756
757 const needs_eh_frame = for (unwind_records) |record| {
758 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;
759 } else false;
760
761 if (needs_eh_frame) {
762 if (self.eh_frame_sect == null) {
763 log.err("missing __TEXT,__eh_frame section", .{});
764 log.err(" in object {s}", .{self.name});
765 return error.MissingSection;
766 }
767 }
768
769 const relocs = self.getRelocs(sect);
770 for (unwind_records) |record, record_id| {
771 const offset = record_id * @sizeOf(macho.compact_unwind_entry);
772 const rel_pos = filterRelocs(
773 relocs,
774 offset,
775 offset + @sizeOf(macho.compact_unwind_entry),
776 );
777 assert(rel_pos.len > 0); // TODO convert to an error as the unwind info is malformed
778 self.unwind_relocs_lookup[record_id] = .{
779 .dead = false,
780 .reloc = rel_pos,
781 };
782
783 // Find function symbol that this record describes
784 const rel = relocs[rel_pos.start..][rel_pos.len - 1];
785 const target = UnwindInfo.parseRelocTarget(
786 zld,
787 object_id,
788 rel,
789 mem.asBytes(&record),
790 @intCast(i32, offset),
791 );
792 log.debug("unwind record {d} tracks {s}", .{ record_id, zld.getSymbolName(target) });
793 if (target.getFile() != object_id) {
794 self.unwind_relocs_lookup[record_id].dead = true;
795 } else {
796 const atom_index = self.getAtomIndexForSymbol(target.sym_index).?;
797 self.unwind_records_lookup.putAssumeCapacityNoClobber(atom_index, @intCast(u32, record_id));
798 }
799 }
800}
801
525802pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
526803 const symtab = self.in_symtab.?;
527804 if (index >= symtab.len) return null;
......@@ -529,23 +806,28 @@ pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
529806 return symtab[mapped_index];
530807}
531808
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
543809pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
544810 const sections = self.getSourceSections();
545811 assert(index < sections.len);
546812 return sections[index];
547813}
548814
815pub fn getSourceSectionByName(self: Object, segname: []const u8, sectname: []const u8) ?macho.section_64 {
816 const sections = self.getSourceSections();
817 for (sections) |sect| {
818 if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName()))
819 return sect;
820 } else return null;
821}
822
823pub fn getSourceSectionIndexByName(self: Object, segname: []const u8, sectname: []const u8) ?u8 {
824 const sections = self.getSourceSections();
825 for (sections) |sect, i| {
826 if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName()))
827 return @intCast(u8, i + 1);
828 } else return null;
829}
830
549831pub fn getSourceSections(self: Object) []const macho.section_64 {
550832 var it = LoadCommandIterator{
551833 .ncmds = self.header.ncmds,
......@@ -652,8 +934,64 @@ pub fn getSymbolName(self: Object, index: u32) []const u8 {
652934 return strtab[start..][0 .. len - 1 :0];
653935}
654936
937pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
938 // Find containing atom
939 const Predicate = struct {
940 addr: i64,
941
942 pub fn predicate(pred: @This(), other: i64) bool {
943 return if (other == -1) true else other > pred.addr;
944 }
945 };
946
947 if (sect_hint) |sect_id| {
948 if (self.source_section_index_lookup[sect_id] > -1) {
949 const first_sym_index = @intCast(usize, self.source_section_index_lookup[sect_id]);
950 const target_sym_index = @import("zld.zig").lsearch(i64, self.source_address_lookup[first_sym_index..], Predicate{
951 .addr = @intCast(i64, addr),
952 });
953 if (target_sym_index > 0) {
954 return @intCast(u32, first_sym_index + target_sym_index - 1);
955 }
956 }
957 return self.getSectionAliasSymbolIndex(sect_id);
958 }
959
960 const target_sym_index = @import("zld.zig").lsearch(i64, self.source_address_lookup, Predicate{
961 .addr = @intCast(i64, addr),
962 });
963 assert(target_sym_index > 0);
964 return @intCast(u32, target_sym_index - 1);
965}
966
967pub fn getGlobal(self: Object, sym_index: u32) ?u32 {
968 if (self.globals_lookup[sym_index] == -1) return null;
969 return @intCast(u32, self.globals_lookup[sym_index]);
970}
971
655972pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {
656973 const atom_index = self.atom_by_index_table[sym_index];
657974 if (atom_index == 0) return null;
658975 return atom_index;
659976}
977
978pub fn hasUnwindRecords(self: Object) bool {
979 return self.unwind_info_sect != null;
980}
981
982pub fn getUnwindRecords(self: Object) []align(1) const macho.compact_unwind_entry {
983 const sect = self.unwind_info_sect orelse return &[0]macho.compact_unwind_entry{};
984 const data = self.getSectionContents(sect);
985 const num_entries = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
986 return @ptrCast([*]align(1) const macho.compact_unwind_entry, data)[0..num_entries];
987}
988
989pub fn hasEhFrameRecords(self: Object) bool {
990 return self.eh_frame_sect != null;
991}
992
993pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {
994 const sect = self.eh_frame_sect orelse return .{ .data = &[0]u8{} };
995 const data = self.getSectionContents(sect);
996 return .{ .data = data };
997}
src/link/MachO/UnwindInfo.zig created+845
......@@ -0,0 +1,845 @@
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 for (object.exec_atoms.items) |atom_index| {
257 var record = if (object.unwind_records_lookup.get(atom_index)) |record_id| blk: {
258 if (object.unwind_relocs_lookup[record_id].dead) continue;
259 var record = unwind_records[record_id];
260
261 if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
262 try info.collectPersonalityFromDwarf(zld, @intCast(u32, object_id), atom_index, &record);
263 } else {
264 if (getPersonalityFunctionReloc(
265 zld,
266 @intCast(u32, object_id),
267 record_id,
268 )) |rel| {
269 const target = parseRelocTarget(
270 zld,
271 @intCast(u32, object_id),
272 rel,
273 mem.asBytes(&record),
274 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
275 );
276 const personality_index = info.getPersonalityFunction(target) orelse inner: {
277 const personality_index = info.personalities_count;
278 info.personalities[personality_index] = target;
279 info.personalities_count += 1;
280 break :inner personality_index;
281 };
282
283 record.personalityFunction = personality_index + 1;
284 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
285 }
286
287 if (getLsdaReloc(zld, @intCast(u32, object_id), record_id)) |rel| {
288 const target = parseRelocTarget(
289 zld,
290 @intCast(u32, object_id),
291 rel,
292 mem.asBytes(&record),
293 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
294 );
295 record.lsda = @bitCast(u64, target);
296 }
297 }
298 break :blk record;
299 } else blk: {
300 const atom = zld.getAtom(atom_index);
301 const sym = zld.getSymbol(atom.getSymbolWithLoc());
302 if (sym.n_desc == N_DEAD) continue;
303
304 if (!object.hasUnwindRecords()) {
305 if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| {
306 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
307 var record = nullRecord();
308 try info.collectPersonalityFromDwarf(zld, @intCast(u32, object_id), atom_index, &record);
309 switch (cpu_arch) {
310 .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF),
311 .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF),
312 else => unreachable,
313 }
314 break :blk record;
315 }
316 }
317
318 break :blk nullRecord();
319 };
320
321 const atom = zld.getAtom(atom_index);
322 const sym_loc = atom.getSymbolWithLoc();
323 const sym = zld.getSymbol(sym_loc);
324 assert(sym.n_desc != N_DEAD);
325 record.rangeStart = sym.n_value;
326 record.rangeLength = @intCast(u32, atom.size);
327
328 records.appendAssumeCapacity(record);
329 atom_indexes.appendAssumeCapacity(atom_index);
330 }
331 }
332
333 // Fold records
334 try info.records.ensureTotalCapacity(info.gpa, records.items.len);
335 try info.records_lookup.ensureTotalCapacity(info.gpa, @intCast(u32, atom_indexes.items.len));
336
337 var maybe_prev: ?macho.compact_unwind_entry = null;
338 for (records.items) |record, i| {
339 const record_id = blk: {
340 if (maybe_prev) |prev| {
341 const is_dwarf = UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
342 if (is_dwarf or
343 (prev.compactUnwindEncoding != record.compactUnwindEncoding) or
344 (prev.personalityFunction != record.personalityFunction) or
345 record.lsda > 0)
346 {
347 const record_id = @intCast(RecordIndex, info.records.items.len);
348 info.records.appendAssumeCapacity(record);
349 maybe_prev = record;
350 break :blk record_id;
351 } else {
352 break :blk @intCast(RecordIndex, info.records.items.len - 1);
353 }
354 } else {
355 const record_id = @intCast(RecordIndex, info.records.items.len);
356 info.records.appendAssumeCapacity(record);
357 maybe_prev = record;
358 break :blk record_id;
359 }
360 };
361 info.records_lookup.putAssumeCapacityNoClobber(atom_indexes.items[i], record_id);
362 }
363
364 // Calculate common encodings
365 {
366 const CommonEncWithCount = struct {
367 enc: macho.compact_unwind_encoding_t,
368 count: u32,
369
370 fn greaterThan(ctx: void, lhs: @This(), rhs: @This()) bool {
371 _ = ctx;
372 return lhs.count > rhs.count;
373 }
374 };
375
376 const Context = struct {
377 pub fn hash(ctx: @This(), key: macho.compact_unwind_encoding_t) u32 {
378 _ = ctx;
379 return key;
380 }
381
382 pub fn eql(
383 ctx: @This(),
384 key1: macho.compact_unwind_encoding_t,
385 key2: macho.compact_unwind_encoding_t,
386 b_index: usize,
387 ) bool {
388 _ = ctx;
389 _ = b_index;
390 return key1 == key2;
391 }
392 };
393
394 var common_encodings_counts = std.ArrayHashMap(
395 macho.compact_unwind_encoding_t,
396 CommonEncWithCount,
397 Context,
398 false,
399 ).init(info.gpa);
400 defer common_encodings_counts.deinit();
401
402 for (info.records.items) |record| {
403 assert(!isNull(record));
404 if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) continue;
405 const enc = record.compactUnwindEncoding;
406 const gop = try common_encodings_counts.getOrPut(enc);
407 if (!gop.found_existing) {
408 gop.value_ptr.* = .{
409 .enc = enc,
410 .count = 0,
411 };
412 }
413 gop.value_ptr.count += 1;
414 }
415
416 var slice = common_encodings_counts.values();
417 std.sort.sort(CommonEncWithCount, slice, {}, CommonEncWithCount.greaterThan);
418
419 var i: u7 = 0;
420 while (i < slice.len) : (i += 1) {
421 if (i >= max_common_encodings) break;
422 if (slice[i].count < 2) continue;
423 info.appendCommonEncoding(slice[i].enc);
424 log.debug("adding common encoding: {d} => 0x{x:0>8}", .{ i, slice[i].enc });
425 }
426 }
427
428 // Compute page allocations
429 {
430 var i: u32 = 0;
431 while (i < info.records.items.len) {
432 const range_start_max: u64 =
433 info.records.items[i].rangeStart + compressed_entry_func_offset_mask;
434 var encoding_count: u9 = info.common_encodings_count;
435 var space_left: u32 = second_level_page_words -
436 @sizeOf(macho.unwind_info_compressed_second_level_page_header) / @sizeOf(u32);
437 var page = Page{
438 .kind = undefined,
439 .start = i,
440 .count = 0,
441 };
442
443 while (space_left >= 1 and i < info.records.items.len) {
444 const record = info.records.items[i];
445 const enc = record.compactUnwindEncoding;
446 const is_dwarf = UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
447
448 if (record.rangeStart >= range_start_max) {
449 break;
450 } else if (info.getCommonEncoding(enc) != null or
451 page.getPageEncoding(info, enc) != null and !is_dwarf)
452 {
453 i += 1;
454 space_left -= 1;
455 } else if (space_left >= 2 and encoding_count < max_compact_encodings) {
456 page.appendPageEncoding(i);
457 i += 1;
458 space_left -= 2;
459 encoding_count += 1;
460 } else {
461 break;
462 }
463 }
464
465 page.count = @intCast(u16, i - page.start);
466
467 if (i < info.records.items.len and page.count < max_regular_second_level_entries) {
468 page.kind = .regular;
469 page.count = @intCast(u16, @min(
470 max_regular_second_level_entries,
471 info.records.items.len - page.start,
472 ));
473 i = page.start + page.count;
474 } else {
475 page.kind = .compressed;
476 }
477
478 log.debug("{}", .{page.fmtDebug(info)});
479
480 try info.pages.append(info.gpa, page);
481 }
482 }
483
484 // Save indices of records requiring LSDA relocation
485 try info.lsdas_lookup.ensureTotalCapacity(info.gpa, @intCast(u32, info.records.items.len));
486 for (info.records.items) |rec, i| {
487 info.lsdas_lookup.putAssumeCapacityNoClobber(@intCast(RecordIndex, i), @intCast(u32, info.lsdas.items.len));
488 if (rec.lsda == 0) continue;
489 try info.lsdas.append(info.gpa, @intCast(RecordIndex, i));
490 }
491}
492
493fn collectPersonalityFromDwarf(
494 info: *UnwindInfo,
495 zld: *Zld,
496 object_id: u32,
497 atom_index: u32,
498 record: *macho.compact_unwind_entry,
499) !void {
500 const object = &zld.objects.items[object_id];
501 var it = object.getEhFrameRecordsIterator();
502 const fde_offset = object.eh_frame_records_lookup.get(atom_index).?;
503 it.seekTo(fde_offset);
504 const fde = (try it.next()).?;
505 const cie_ptr = fde.getCiePointer();
506 const cie_offset = fde_offset + 4 - cie_ptr;
507 it.seekTo(cie_offset);
508 const cie = (try it.next()).?;
509
510 if (cie.getPersonalityPointerReloc(
511 zld,
512 @intCast(u32, object_id),
513 cie_offset,
514 )) |target| {
515 const personality_index = info.getPersonalityFunction(target) orelse inner: {
516 const personality_index = info.personalities_count;
517 info.personalities[personality_index] = target;
518 info.personalities_count += 1;
519 break :inner personality_index;
520 };
521
522 record.personalityFunction = personality_index + 1;
523 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
524 }
525}
526
527pub fn calcSectionSize(info: UnwindInfo, zld: *Zld) !void {
528 const sect_id = zld.getSectionByName("__TEXT", "__unwind_info") orelse return;
529 const sect = &zld.sections.items(.header)[sect_id];
530 sect.@"align" = 2;
531 sect.size = info.calcRequiredSize();
532}
533
534fn calcRequiredSize(info: UnwindInfo) usize {
535 var total_size: usize = 0;
536 total_size += @sizeOf(macho.unwind_info_section_header);
537 total_size +=
538 @intCast(usize, info.common_encodings_count) * @sizeOf(macho.compact_unwind_encoding_t);
539 total_size += @intCast(usize, info.personalities_count) * @sizeOf(u32);
540 total_size += (info.pages.items.len + 1) * @sizeOf(macho.unwind_info_section_header_index_entry);
541 total_size += info.lsdas.items.len * @sizeOf(macho.unwind_info_section_header_lsda_index_entry);
542 total_size += info.pages.items.len * second_level_page_bytes;
543 return total_size;
544}
545
546pub fn write(info: *UnwindInfo, zld: *Zld) !void {
547 const sect_id = zld.getSectionByName("__TEXT", "__unwind_info") orelse return;
548 const sect = &zld.sections.items(.header)[sect_id];
549 const seg_id = zld.sections.items(.segment_index)[sect_id];
550 const seg = zld.segments.items[seg_id];
551
552 const text_sect_id = zld.getSectionByName("__TEXT", "__text").?;
553 const text_sect = zld.sections.items(.header)[text_sect_id];
554
555 var personalities: [max_personalities]u32 = undefined;
556 const cpu_arch = zld.options.target.cpu.arch;
557
558 log.debug("Personalities:", .{});
559 for (info.personalities[0..info.personalities_count]) |target, i| {
560 const atom_index = zld.getGotAtomIndexForSymbol(target).?;
561 const atom = zld.getAtom(atom_index);
562 const sym = zld.getSymbol(atom.getSymbolWithLoc());
563 personalities[i] = @intCast(u32, sym.n_value - seg.vmaddr);
564 log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], zld.getSymbolName(target) });
565 }
566
567 for (info.records.items) |*rec| {
568 // Finalize missing address values
569 rec.rangeStart += text_sect.addr - seg.vmaddr;
570 if (rec.personalityFunction > 0) {
571 const index = math.cast(usize, rec.personalityFunction - 1) orelse return error.Overflow;
572 rec.personalityFunction = personalities[index];
573 }
574
575 if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) {
576 const lsda_target = @bitCast(SymbolWithLoc, rec.lsda);
577 if (lsda_target.getFile()) |_| {
578 const sym = zld.getSymbol(lsda_target);
579 rec.lsda = sym.n_value - seg.vmaddr;
580 }
581 }
582 }
583
584 for (info.records.items) |record, i| {
585 log.debug("Unwind record at offset 0x{x}", .{i * @sizeOf(macho.compact_unwind_entry)});
586 log.debug(" start: 0x{x}", .{record.rangeStart});
587 log.debug(" length: 0x{x}", .{record.rangeLength});
588 log.debug(" compact encoding: 0x{x:0>8}", .{record.compactUnwindEncoding});
589 log.debug(" personality: 0x{x}", .{record.personalityFunction});
590 log.debug(" LSDA: 0x{x}", .{record.lsda});
591 }
592
593 var buffer = std.ArrayList(u8).init(info.gpa);
594 defer buffer.deinit();
595
596 const size = info.calcRequiredSize();
597 try buffer.ensureTotalCapacityPrecise(size);
598
599 var cwriter = std.io.countingWriter(buffer.writer());
600 const writer = cwriter.writer();
601
602 const common_encodings_offset: u32 = @sizeOf(macho.unwind_info_section_header);
603 const common_encodings_count: u32 = info.common_encodings_count;
604 const personalities_offset: u32 = common_encodings_offset + common_encodings_count * @sizeOf(u32);
605 const personalities_count: u32 = info.personalities_count;
606 const indexes_offset: u32 = personalities_offset + personalities_count * @sizeOf(u32);
607 const indexes_count: u32 = @intCast(u32, info.pages.items.len + 1);
608
609 try writer.writeStruct(macho.unwind_info_section_header{
610 .commonEncodingsArraySectionOffset = common_encodings_offset,
611 .commonEncodingsArrayCount = common_encodings_count,
612 .personalityArraySectionOffset = personalities_offset,
613 .personalityArrayCount = personalities_count,
614 .indexSectionOffset = indexes_offset,
615 .indexCount = indexes_count,
616 });
617
618 try writer.writeAll(mem.sliceAsBytes(info.common_encodings[0..info.common_encodings_count]));
619 try writer.writeAll(mem.sliceAsBytes(personalities[0..info.personalities_count]));
620
621 const pages_base_offset = @intCast(u32, size - (info.pages.items.len * second_level_page_bytes));
622 const lsda_base_offset = @intCast(u32, pages_base_offset -
623 (info.lsdas.items.len * @sizeOf(macho.unwind_info_section_header_lsda_index_entry)));
624 for (info.pages.items) |page, i| {
625 assert(page.count > 0);
626 const first_entry = info.records.items[page.start];
627 try writer.writeStruct(macho.unwind_info_section_header_index_entry{
628 .functionOffset = @intCast(u32, first_entry.rangeStart),
629 .secondLevelPagesSectionOffset = @intCast(u32, pages_base_offset + i * second_level_page_bytes),
630 .lsdaIndexArraySectionOffset = lsda_base_offset +
631 info.lsdas_lookup.get(page.start).? * @sizeOf(macho.unwind_info_section_header_lsda_index_entry),
632 });
633 }
634
635 const last_entry = info.records.items[info.records.items.len - 1];
636 const sentinel_address = @intCast(u32, last_entry.rangeStart + last_entry.rangeLength);
637 try writer.writeStruct(macho.unwind_info_section_header_index_entry{
638 .functionOffset = sentinel_address,
639 .secondLevelPagesSectionOffset = 0,
640 .lsdaIndexArraySectionOffset = lsda_base_offset +
641 @intCast(u32, info.lsdas.items.len) * @sizeOf(macho.unwind_info_section_header_lsda_index_entry),
642 });
643
644 for (info.lsdas.items) |record_id| {
645 const record = info.records.items[record_id];
646 try writer.writeStruct(macho.unwind_info_section_header_lsda_index_entry{
647 .functionOffset = @intCast(u32, record.rangeStart),
648 .lsdaOffset = @intCast(u32, record.lsda),
649 });
650 }
651
652 for (info.pages.items) |page| {
653 const start = cwriter.bytes_written;
654 try page.write(info, writer);
655 const nwritten = cwriter.bytes_written - start;
656 if (nwritten < second_level_page_bytes) {
657 const offset = math.cast(usize, second_level_page_bytes - nwritten) orelse return error.Overflow;
658 try writer.writeByteNTimes(0, offset);
659 }
660 }
661
662 const padding = buffer.items.len - cwriter.bytes_written;
663 if (padding > 0) {
664 const offset = math.cast(usize, cwriter.bytes_written) orelse return error.Overflow;
665 mem.set(u8, buffer.items[offset..], 0);
666 }
667
668 try zld.file.pwriteAll(buffer.items, sect.offset);
669}
670
671pub fn parseRelocTarget(
672 zld: *Zld,
673 object_id: u32,
674 rel: macho.relocation_info,
675 code: []const u8,
676 base_offset: i32,
677) SymbolWithLoc {
678 const tracy = trace(@src());
679 defer tracy.end();
680
681 const object = &zld.objects.items[object_id];
682
683 const sym_index = if (rel.r_extern == 0) blk: {
684 const sect_id = @intCast(u8, rel.r_symbolnum - 1);
685 const rel_offset = @intCast(u32, rel.r_address - base_offset);
686 assert(rel.r_pcrel == 0 and rel.r_length == 3);
687 const address_in_section = mem.readIntLittle(u64, code[rel_offset..][0..8]);
688 const sym_index = object.getSymbolByAddress(address_in_section, sect_id);
689 break :blk sym_index;
690 } else object.reverse_symtab_lookup[rel.r_symbolnum];
691
692 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id + 1 };
693 const sym = zld.getSymbol(sym_loc);
694
695 if (sym.sect() and !sym.ext()) {
696 // Make sure we are not dealing with a local alias.
697 const atom_index = object.getAtomIndexForSymbol(sym_index) orelse
698 return sym_loc;
699 const atom = zld.getAtom(atom_index);
700 return atom.getSymbolWithLoc();
701 } else if (object.getGlobal(sym_index)) |global_index| {
702 return zld.globals.items[global_index];
703 } else return sym_loc;
704}
705
706fn getRelocs(
707 zld: *Zld,
708 object_id: u32,
709 record_id: usize,
710) []align(1) const macho.relocation_info {
711 const object = &zld.objects.items[object_id];
712 assert(object.hasUnwindRecords());
713 const rel_pos = object.unwind_relocs_lookup[record_id].reloc;
714 const relocs = object.getRelocs(object.unwind_info_sect.?);
715 return relocs[rel_pos.start..][0..rel_pos.len];
716}
717
718fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool {
719 const base_offset = @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry));
720 const rel_offset = rel.r_address - base_offset;
721 return rel_offset == 16;
722}
723
724pub fn getPersonalityFunctionReloc(
725 zld: *Zld,
726 object_id: u32,
727 record_id: usize,
728) ?macho.relocation_info {
729 const relocs = getRelocs(zld, object_id, record_id);
730 for (relocs) |rel| {
731 if (isPersonalityFunction(record_id, rel)) return rel;
732 }
733 return null;
734}
735
736fn getPersonalityFunction(info: UnwindInfo, global_index: SymbolWithLoc) ?u2 {
737 comptime var index: u2 = 0;
738 inline while (index < max_personalities) : (index += 1) {
739 if (index >= info.personalities_count) return null;
740 if (info.personalities[index].eql(global_index)) {
741 return index;
742 }
743 }
744 return null;
745}
746
747fn isLsda(record_id: usize, rel: macho.relocation_info) bool {
748 const base_offset = @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry));
749 const rel_offset = rel.r_address - base_offset;
750 return rel_offset == 24;
751}
752
753pub fn getLsdaReloc(zld: *Zld, object_id: u32, record_id: usize) ?macho.relocation_info {
754 const relocs = getRelocs(zld, object_id, record_id);
755 for (relocs) |rel| {
756 if (isLsda(record_id, rel)) return rel;
757 }
758 return null;
759}
760
761pub fn isNull(rec: macho.compact_unwind_entry) bool {
762 return rec.rangeStart == 0 and
763 rec.rangeLength == 0 and
764 rec.compactUnwindEncoding == 0 and
765 rec.lsda == 0 and
766 rec.personalityFunction == 0;
767}
768
769inline fn nullRecord() macho.compact_unwind_entry {
770 return .{
771 .rangeStart = 0,
772 .rangeLength = 0,
773 .compactUnwindEncoding = 0,
774 .personalityFunction = 0,
775 .lsda = 0,
776 };
777}
778
779fn appendCommonEncoding(info: *UnwindInfo, enc: macho.compact_unwind_encoding_t) void {
780 assert(info.common_encodings_count <= max_common_encodings);
781 info.common_encodings[info.common_encodings_count] = enc;
782 info.common_encodings_count += 1;
783}
784
785fn getCommonEncoding(info: UnwindInfo, enc: macho.compact_unwind_encoding_t) ?u7 {
786 comptime var index: u7 = 0;
787 inline while (index < max_common_encodings) : (index += 1) {
788 if (index >= info.common_encodings_count) return null;
789 if (info.common_encodings[index] == enc) {
790 return index;
791 }
792 }
793 return null;
794}
795
796pub const UnwindEncoding = struct {
797 pub fn getMode(enc: macho.compact_unwind_encoding_t) u4 {
798 comptime assert(macho.UNWIND_ARM64_MODE_MASK == macho.UNWIND_X86_64_MODE_MASK);
799 return @truncate(u4, (enc & macho.UNWIND_ARM64_MODE_MASK) >> 24);
800 }
801
802 pub fn isDwarf(enc: macho.compact_unwind_encoding_t, cpu_arch: std.Target.Cpu.Arch) bool {
803 const mode = getMode(enc);
804 return switch (cpu_arch) {
805 .aarch64 => @intToEnum(macho.UNWIND_ARM64_MODE, mode) == .DWARF,
806 .x86_64 => @intToEnum(macho.UNWIND_X86_64_MODE, mode) == .DWARF,
807 else => unreachable,
808 };
809 }
810
811 pub fn setMode(enc: *macho.compact_unwind_encoding_t, mode: anytype) void {
812 enc.* |= @intCast(u32, @enumToInt(mode)) << 24;
813 }
814
815 pub fn hasLsda(enc: macho.compact_unwind_encoding_t) bool {
816 const has_lsda = @truncate(u1, (enc & macho.UNWIND_HAS_LSDA) >> 31);
817 return has_lsda == 1;
818 }
819
820 pub fn setHasLsda(enc: *macho.compact_unwind_encoding_t, has_lsda: bool) void {
821 const mask = @intCast(u32, @boolToInt(has_lsda)) << 31;
822 enc.* |= mask;
823 }
824
825 pub fn getPersonalityIndex(enc: macho.compact_unwind_encoding_t) u2 {
826 const index = @truncate(u2, (enc & macho.UNWIND_PERSONALITY_MASK) >> 28);
827 return index;
828 }
829
830 pub fn setPersonalityIndex(enc: *macho.compact_unwind_encoding_t, index: u2) void {
831 const mask = @intCast(u32, index) << 28;
832 enc.* |= mask;
833 }
834
835 pub fn getDwarfSectionOffset(enc: macho.compact_unwind_encoding_t, cpu_arch: std.Target.Cpu.Arch) u24 {
836 assert(isDwarf(enc, cpu_arch));
837 const offset = @truncate(u24, enc);
838 return offset;
839 }
840
841 pub fn setDwarfSectionOffset(enc: *macho.compact_unwind_encoding_t, cpu_arch: std.Target.Cpu.Arch, offset: u24) void {
842 assert(isDwarf(enc.*, cpu_arch));
843 enc.* |= offset;
844 }
845};
src/link/MachO/ZldAtom.zig+78-166
......@@ -29,11 +29,11 @@ const Zld = @import("zld.zig").Zld;
2929/// a stub trampoline, it can be found in the linkers `locals` arraylist.
3030sym_index: u32,
3131
32/// -1 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.
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 (indexing from 1).
3434/// Prefer using `getFile()` helper to get the file index out rather than using
3535/// the field directly.
36file: i32,
36file: u32,
3737
3838/// If this Atom is not a synthetic Atom, i.e., references a subsection in an
3939/// Object file, `inner_sym_index` and `inner_nsyms_trailing` tell where and if
......@@ -51,13 +51,6 @@ size: u64,
5151/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
5252alignment: 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
6154/// Points to the previous and next neighbours
6255next_index: ?AtomIndex,
6356prev_index: ?AtomIndex,
......@@ -66,20 +59,18 @@ pub const empty = Atom{
6659 .sym_index = 0,
6760 .inner_sym_index = 0,
6861 .inner_nsyms_trailing = 0,
69 .file = -1,
62 .file = 0,
7063 .size = 0,
7164 .alignment = 0,
72 .cached_relocs_start = -1,
73 .cached_relocs_len = 0,
7465 .prev_index = null,
7566 .next_index = null,
7667};
7768
7869/// Returns `null` if the Atom is a synthetic Atom.
7970/// Otherwise, returns an index into an array of Objects.
80pub inline fn getFile(self: Atom) ?u31 {
81 if (self.file == -1) return null;
82 return @intCast(u31, self.file);
71pub fn getFile(self: Atom) ?u32 {
72 if (self.file == 0) return null;
73 return self.file - 1;
8374}
8475
8576pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
......@@ -92,7 +83,7 @@ pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
9283const InnerSymIterator = struct {
9384 sym_index: u32,
9485 count: u32,
95 file: i32,
86 file: u32,
9687
9788 pub fn next(it: *@This()) ?SymbolWithLoc {
9889 if (it.count == 0) return null;
......@@ -159,19 +150,14 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u
159150 return source_sym.n_value - base_addr;
160151}
161152
162pub fn scanAtomRelocs(
163 zld: *Zld,
164 atom_index: AtomIndex,
165 relocs: []align(1) const macho.relocation_info,
166 reverse_lookup: []u32,
167) !void {
153pub fn scanAtomRelocs(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
168154 const arch = zld.options.target.cpu.arch;
169155 const atom = zld.getAtom(atom_index);
170156 assert(atom.getFile() != null); // synthetic atoms do not have relocs
171157
172158 return switch (arch) {
173 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs, reverse_lookup),
174 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs, reverse_lookup),
159 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs),
160 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs),
175161 else => unreachable,
176162 };
177163}
......@@ -202,16 +188,11 @@ pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext {
202188 };
203189}
204190
205pub fn parseRelocTarget(
206 zld: *Zld,
207 atom_index: AtomIndex,
208 rel: macho.relocation_info,
209 reverse_lookup: []u32,
210) SymbolWithLoc {
191pub fn parseRelocTarget(zld: *Zld, atom_index: AtomIndex, rel: macho.relocation_info) SymbolWithLoc {
211192 const atom = zld.getAtom(atom_index);
212193 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: {
215196 const sect_id = @intCast(u8, rel.r_symbolnum - 1);
216197 const ctx = getRelocContext(zld, atom_index);
217198 const atom_code = getAtomCode(zld, atom_index);
......@@ -219,9 +200,9 @@ pub fn parseRelocTarget(
219200
220201 const address_in_section = if (rel.r_pcrel == 0) blk: {
221202 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])
223204 else
224 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
205 mem.readIntLittle(u32, atom_code[rel_offset..][0..4]);
225206 } else blk: {
226207 const correction: u3 = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
227208 .X86_64_RELOC_SIGNED => 0,
......@@ -232,38 +213,14 @@ pub fn parseRelocTarget(
232213 };
233214 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
234215 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);
236217 };
237218
238219 // Find containing atom
239 const Predicate = struct {
240 addr: i64,
241
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 }
220 const sym_index = object.getSymbolByAddress(address_in_section, sect_id);
221 break :sym_index sym_index;
222 } else object.reverse_symtab_lookup[rel.r_symbolnum];
265223
266 const sym_index = reverse_lookup[rel.r_symbolnum];
267224 const sym_loc = SymbolWithLoc{
268225 .sym_index = sym_index,
269226 .file = atom.file,
......@@ -272,30 +229,12 @@ pub fn parseRelocTarget(
272229
273230 if (sym.sect() and !sym.ext()) {
274231 return sym_loc;
275 } else if (object.globals_lookup[sym_index] > -1) {
276 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
232 } else if (object.getGlobal(sym_index)) |global_index| {
277233 return zld.globals.items[global_index];
278234 } else return sym_loc;
279235}
280236
281pub fn getRelocTargetAtomIndex(zld: *Zld, rel: macho.relocation_info, target: SymbolWithLoc) ?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
237pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc, is_via_got: bool) ?AtomIndex {
299238 if (is_via_got) {
300239 return zld.getGotAtomIndexForSymbol(target).?; // panic means fatal error
301240 }
......@@ -314,12 +253,7 @@ pub fn getRelocTargetAtomIndex(zld: *Zld, rel: macho.relocation_info, target: Sy
314253 return object.getAtomIndexForSymbol(target.sym_index);
315254}
316255
317fn scanAtomRelocsArm64(
318 zld: *Zld,
319 atom_index: AtomIndex,
320 relocs: []align(1) const macho.relocation_info,
321 reverse_lookup: []u32,
322) !void {
256fn scanAtomRelocsArm64(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
323257 for (relocs) |rel| {
324258 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
325259
......@@ -332,7 +266,7 @@ fn scanAtomRelocsArm64(
332266
333267 const atom = zld.getAtom(atom_index);
334268 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];
336270 const sym_loc = SymbolWithLoc{
337271 .sym_index = sym_index,
338272 .file = atom.file,
......@@ -341,10 +275,10 @@ fn scanAtomRelocsArm64(
341275
342276 if (sym.sect() and !sym.ext()) continue;
343277
344 const target = if (object.globals_lookup[sym_index] > -1) blk: {
345 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
346 break :blk zld.globals.items[global_index];
347 } else sym_loc;
278 const target = if (object.getGlobal(sym_index)) |global_index|
279 zld.globals.items[global_index]
280 else
281 sym_loc;
348282
349283 switch (rel_type) {
350284 .ARM64_RELOC_BRANCH26 => {
......@@ -368,12 +302,7 @@ fn scanAtomRelocsArm64(
368302 }
369303}
370304
371fn scanAtomRelocsX86(
372 zld: *Zld,
373 atom_index: AtomIndex,
374 relocs: []align(1) const macho.relocation_info,
375 reverse_lookup: []u32,
376) !void {
305fn scanAtomRelocsX86(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
377306 for (relocs) |rel| {
378307 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
379308
......@@ -386,7 +315,7 @@ fn scanAtomRelocsX86(
386315
387316 const atom = zld.getAtom(atom_index);
388317 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];
390319 const sym_loc = SymbolWithLoc{
391320 .sym_index = sym_index,
392321 .file = atom.file,
......@@ -395,10 +324,10 @@ fn scanAtomRelocsX86(
395324
396325 if (sym.sect() and !sym.ext()) continue;
397326
398 const target = if (object.globals_lookup[sym_index] > -1) blk: {
399 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
400 break :blk zld.globals.items[global_index];
401 } else sym_loc;
327 const target = if (object.getGlobal(sym_index)) |global_index|
328 zld.globals.items[global_index]
329 else
330 sym_loc;
402331
403332 switch (rel_type) {
404333 .X86_64_RELOC_BRANCH => {
......@@ -432,7 +361,7 @@ fn addTlvPtrEntry(zld: *Zld, target: SymbolWithLoc) !void {
432361 try zld.tlv_ptr_table.putNoClobber(gpa, target, tlv_ptr_index);
433362}
434363
435fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
364pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
436365 if (zld.got_table.contains(target)) return;
437366 const gpa = zld.gpa;
438367 const atom_index = try zld.createGotAtom();
......@@ -466,7 +395,6 @@ pub fn resolveRelocs(
466395 atom_index: AtomIndex,
467396 atom_code: []u8,
468397 atom_relocs: []align(1) const macho.relocation_info,
469 reverse_lookup: []u32,
470398) !void {
471399 const arch = zld.options.target.cpu.arch;
472400 const atom = zld.getAtom(atom_index);
......@@ -480,14 +408,14 @@ pub fn resolveRelocs(
480408 const ctx = getRelocContext(zld, atom_index);
481409
482410 return switch (arch) {
483 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, reverse_lookup, ctx),
484 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, reverse_lookup, ctx),
411 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, ctx),
412 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, ctx),
485413 else => unreachable,
486414 };
487415}
488416
489pub fn getRelocTargetAddress(zld: *Zld, rel: macho.relocation_info, target: SymbolWithLoc, is_tlv: bool) !u64 {
490 const target_atom_index = getRelocTargetAtomIndex(zld, rel, target) orelse {
417pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_via_got: bool, is_tlv: bool) !u64 {
418 const target_atom_index = getRelocTargetAtomIndex(zld, target, is_via_got) orelse {
491419 // If there is no atom for target, we still need to check for special, atom-less
492420 // symbols such as `___dso_handle`.
493421 const target_name = zld.getSymbolName(target);
......@@ -499,7 +427,7 @@ pub fn getRelocTargetAddress(zld: *Zld, rel: macho.relocation_info, target: Symb
499427 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
500428 target_atom.sym_index,
501429 zld.getSymbolName(target_atom.getSymbolWithLoc()),
502 target_atom.file,
430 target_atom.getFile(),
503431 });
504432
505433 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
......@@ -541,7 +469,6 @@ fn resolveRelocsArm64(
541469 atom_index: AtomIndex,
542470 atom_code: []u8,
543471 atom_relocs: []align(1) const macho.relocation_info,
544 reverse_lookup: []u32,
545472 context: RelocContext,
546473) !void {
547474 const atom = zld.getAtom(atom_index);
......@@ -565,20 +492,20 @@ fn resolveRelocsArm64(
565492 .ARM64_RELOC_SUBTRACTOR => {
566493 assert(subtractor == null);
567494
568 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{
495 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
569496 @tagName(rel_type),
570497 rel.r_address,
571498 rel.r_symbolnum,
572 atom.file,
499 atom.getFile(),
573500 });
574501
575 subtractor = parseRelocTarget(zld, atom_index, rel, reverse_lookup);
502 subtractor = parseRelocTarget(zld, atom_index, rel);
576503 continue;
577504 },
578505 else => {},
579506 }
580507
581 const target = parseRelocTarget(zld, atom_index, rel, reverse_lookup);
508 const target = parseRelocTarget(zld, atom_index, rel);
582509 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);
583510
584511 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
......@@ -586,19 +513,20 @@ fn resolveRelocsArm64(
586513 rel.r_address,
587514 target.sym_index,
588515 zld.getSymbolName(target),
589 target.file,
516 target.getFile(),
590517 });
591518
592519 const source_addr = blk: {
593520 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
594521 break :blk source_sym.n_value + rel_offset;
595522 };
523 const is_via_got = relocRequiresGot(zld, rel);
596524 const is_tlv = is_tlv: {
597525 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
598526 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
599527 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
600528 };
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
603531 log.debug(" | source_addr = 0x{x}", .{source_addr});
604532
......@@ -610,9 +538,9 @@ fn resolveRelocsArm64(
610538 } else target;
611539 log.debug(" source {s} (object({?})), target {s} (object({?}))", .{
612540 zld.getSymbolName(atom.getSymbolWithLoc()),
613 atom.file,
541 atom.getFile(),
614542 zld.getSymbolName(target),
615 zld.getAtom(getRelocTargetAtomIndex(zld, rel, target).?).file,
543 zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(),
616544 });
617545
618546 const displacement = if (calcPcRelativeDisplacementArm64(
......@@ -628,7 +556,7 @@ fn resolveRelocsArm64(
628556 zld,
629557 actual_target,
630558 ).?);
631 log.debug(" | target_addr = 0x{x}", .{thunk_sym.n_value});
559 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value});
632560 break :blk try calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
633561 };
634562
......@@ -832,7 +760,6 @@ fn resolveRelocsX86(
832760 atom_index: AtomIndex,
833761 atom_code: []u8,
834762 atom_relocs: []align(1) const macho.relocation_info,
835 reverse_lookup: []u32,
836763 context: RelocContext,
837764) !void {
838765 const atom = zld.getAtom(atom_index);
......@@ -847,33 +774,34 @@ fn resolveRelocsX86(
847774 .X86_64_RELOC_SUBTRACTOR => {
848775 assert(subtractor == null);
849776
850 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{
777 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
851778 @tagName(rel_type),
852779 rel.r_address,
853780 rel.r_symbolnum,
854 atom.file,
781 atom.getFile(),
855782 });
856783
857 subtractor = parseRelocTarget(zld, atom_index, rel, reverse_lookup);
784 subtractor = parseRelocTarget(zld, atom_index, rel);
858785 continue;
859786 },
860787 else => {},
861788 }
862789
863 const target = parseRelocTarget(zld, atom_index, rel, reverse_lookup);
790 const target = parseRelocTarget(zld, atom_index, rel);
864791 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);
865792
866793 log.debug(" RELA({s}) @ {x} => %{d} in object({?})", .{
867794 @tagName(rel_type),
868795 rel.r_address,
869796 target.sym_index,
870 target.file,
797 target.getFile(),
871798 });
872799
873800 const source_addr = blk: {
874801 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
875802 break :blk source_sym.n_value + rel_offset;
876803 };
804 const is_via_got = relocRequiresGot(zld, rel);
877805 const is_tlv = is_tlv: {
878806 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
879807 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
......@@ -882,7 +810,7 @@ fn resolveRelocsX86(
882810
883811 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
887815 switch (rel_type) {
888816 .X86_64_RELOC_BRANCH => {
......@@ -1016,9 +944,10 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
1016944}
1017945
1018946pub 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);
1020948 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
1021949 const object = zld.objects.items[atom.getFile().?];
950 const cache = object.relocs_lookup[atom.sym_index];
1022951
1023952 const source_sect = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
1024953 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
1036965 };
1037966
1038967 const relocs = object.getRelocs(source_sect);
1039
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) };
968 return relocs[cache.start..][0..cache.len];
1076969}
1077970
1078971pub 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 {
11111004 .load_store_128 => try math.divExact(u12, narrowed, 16),
11121005 };
11131006}
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+166-51
......@@ -2,6 +2,7 @@
22
33const std = @import("std");
44const assert = std.debug.assert;
5const eh_frame = @import("eh_frame.zig");
56const log = std.log.scoped(.dead_strip);
67const macho = std.macho;
78const math = std.math;
......@@ -11,13 +12,14 @@ const Allocator = mem.Allocator;
1112const AtomIndex = @import("zld.zig").AtomIndex;
1213const Atom = @import("ZldAtom.zig");
1314const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
15const UnwindInfo = @import("UnwindInfo.zig");
1416const Zld = @import("zld.zig").Zld;
1517
1618const N_DEAD = @import("zld.zig").N_DEAD;
1719
1820const AtomTable = std.AutoHashMap(AtomIndex, void);
1921
20pub fn gcAtoms(zld: *Zld, reverse_lookups: [][]u32) Allocator.Error!void {
22pub fn gcAtoms(zld: *Zld) !void {
2123 const gpa = zld.gpa;
2224
2325 var arena = std.heap.ArenaAllocator.init(gpa);
......@@ -30,7 +32,7 @@ pub fn gcAtoms(zld: *Zld, reverse_lookups: [][]u32) Allocator.Error!void {
3032 try alive.ensureTotalCapacity(@intCast(u32, zld.atoms.items.len));
3133
3234 try collectRoots(zld, &roots);
33 mark(zld, roots, &alive, reverse_lookups);
35 try mark(zld, roots, &alive);
3436 prune(zld, alive);
3537}
3638
......@@ -45,10 +47,10 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
4547 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
4648 _ = try roots.getOrPut(atom_index);
4749
48 log.debug("root(ATOM({d}, %{d}, {d}))", .{
50 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
4951 atom_index,
5052 zld.getAtom(atom_index).sym_index,
51 zld.getAtom(atom_index).file,
53 zld.getAtom(atom_index).getFile(),
5254 });
5355 },
5456 else => |other| {
......@@ -63,32 +65,15 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
6365 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
6466 _ = try roots.getOrPut(atom_index);
6567
66 log.debug("root(ATOM({d}, %{d}, {d}))", .{
68 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
6769 atom_index,
6870 zld.getAtom(atom_index).sym_index,
69 zld.getAtom(atom_index).file,
71 zld.getAtom(atom_index).getFile(),
7072 });
7173 }
7274 },
7375 }
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
9277 for (zld.objects.items) |object| {
9378 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
9479
......@@ -119,28 +104,23 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
119104 if (is_gc_root) {
120105 try roots.putNoClobber(atom_index, {});
121106
122 log.debug("root(ATOM({d}, %{d}, {d}))", .{
107 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
123108 atom_index,
124109 zld.getAtom(atom_index).sym_index,
125 zld.getAtom(atom_index).file,
110 zld.getAtom(atom_index).getFile(),
126111 });
127112 }
128113 }
129114 }
130115}
131116
132fn markLive(
133 zld: *Zld,
134 atom_index: AtomIndex,
135 alive: *AtomTable,
136 reverse_lookups: [][]u32,
137) void {
117fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {
138118 if (alive.contains(atom_index)) return;
139119
140120 const atom = zld.getAtom(atom_index);
141121 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
145125 alive.putAssumeCapacityNoClobber(atom_index, {});
146126
......@@ -151,14 +131,13 @@ fn markLive(
151131 if (header.isZerofill()) return;
152132
153133 const relocs = Atom.getAtomRelocs(zld, atom_index);
154 const reverse_lookup = reverse_lookups[atom.getFile().?];
155134 for (relocs) |rel| {
156135 const target = switch (cpu_arch) {
157136 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
158137 .ARM64_RELOC_ADDEND => continue,
159 else => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),
138 else => Atom.parseRelocTarget(zld, atom_index, rel),
160139 },
161 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),
140 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel),
162141 else => unreachable,
163142 };
164143 const target_sym = zld.getSymbol(target);
......@@ -174,21 +153,21 @@ fn markLive(
174153
175154 const object = zld.objects.items[target.getFile().?];
176155 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})", .{
178157 target_atom_index,
179158 zld.getAtom(target_atom_index).sym_index,
180 zld.getAtom(target_atom_index).file,
159 zld.getAtom(target_atom_index).getFile(),
181160 });
182161
183 markLive(zld, target_atom_index, alive, reverse_lookups);
162 markLive(zld, target_atom_index, alive);
184163 }
185164}
186165
187fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookups: [][]u32) bool {
166fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
188167 const atom = zld.getAtom(atom_index);
189168 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
193172 const cpu_arch = zld.options.target.cpu.arch;
194173
......@@ -197,14 +176,13 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookup
197176 assert(!header.isZerofill());
198177
199178 const relocs = Atom.getAtomRelocs(zld, atom_index);
200 const reverse_lookup = reverse_lookups[atom.getFile().?];
201179 for (relocs) |rel| {
202180 const target = switch (cpu_arch) {
203181 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
204182 .ARM64_RELOC_ADDEND => continue,
205 else => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),
183 else => Atom.parseRelocTarget(zld, atom_index, rel),
206184 },
207 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),
185 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel),
208186 else => unreachable,
209187 };
210188
......@@ -214,10 +192,10 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookup
214192 continue;
215193 };
216194 if (alive.contains(target_atom_index)) {
217 log.debug(" refers live ATOM({d}, %{d}, {d})", .{
195 log.debug(" refers live ATOM({d}, %{d}, {?d})", .{
218196 target_atom_index,
219197 zld.getAtom(target_atom_index).sym_index,
220 zld.getAtom(target_atom_index).file,
198 zld.getAtom(target_atom_index).getFile(),
221199 });
222200 return true;
223201 }
......@@ -226,10 +204,10 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookup
226204 return false;
227205}
228206
229fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable, reverse_lookups: [][]u32) void {
207fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
230208 var it = roots.keyIterator();
231209 while (it.next()) |root| {
232 markLive(zld, root.*, alive, reverse_lookups);
210 markLive(zld, root.*, alive);
233211 }
234212
235213 var loop: bool = true;
......@@ -251,14 +229,151 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable, reverse_lookups: [][]u32
251229 const source_sect = object.getSourceSection(sect_id);
252230
253231 if (source_sect.isDontDeadStripIfReferencesLive()) {
254 if (refersLive(zld, atom_index, alive.*, reverse_lookups)) {
255 markLive(zld, atom_index, alive, reverse_lookups);
232 if (refersLive(zld, atom_index, alive.*)) {
233 markLive(zld, atom_index, alive);
256234 loop = true;
257235 }
258236 }
259237 }
260238 }
261239 }
240
241 for (zld.objects.items) |_, 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 try markUnwindRecords(zld, @intCast(u32, object_id), alive);
245 }
246}
247
248fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
249 const object = &zld.objects.items[object_id];
250 const cpu_arch = zld.options.target.cpu.arch;
251
252 const unwind_records = object.getUnwindRecords();
253
254 for (object.exec_atoms.items) |atom_index| {
255 if (!object.hasUnwindRecords()) {
256 if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| {
257 const ptr = object.eh_frame_relocs_lookup.getPtr(fde_offset).?;
258 if (ptr.dead) continue; // already marked
259 if (!alive.contains(atom_index)) {
260 // Mark dead and continue.
261 ptr.dead = true;
262 } else {
263 // Mark references live and continue.
264 try markEhFrameRecord(zld, object_id, atom_index, alive);
265 }
266 continue;
267 }
268 }
269
270 const record_id = object.unwind_records_lookup.get(atom_index) orelse continue;
271 if (object.unwind_relocs_lookup[record_id].dead) continue; // already marked, nothing to do
272 if (!alive.contains(atom_index)) {
273 // Mark the record dead and continue.
274 object.unwind_relocs_lookup[record_id].dead = true;
275 if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| {
276 object.eh_frame_relocs_lookup.getPtr(fde_offset).?.dead = true;
277 }
278 continue;
279 }
280
281 const record = unwind_records[record_id];
282 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
283 try markEhFrameRecord(zld, object_id, atom_index, alive);
284 } else {
285 if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| {
286 const target = UnwindInfo.parseRelocTarget(
287 zld,
288 object_id,
289 rel,
290 mem.asBytes(&record),
291 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
292 );
293 const target_sym = zld.getSymbol(target);
294 if (!target_sym.undf()) {
295 const target_object = zld.objects.items[target.getFile().?];
296 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
297 markLive(zld, target_atom_index, alive);
298 }
299 }
300
301 if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| {
302 const target = UnwindInfo.parseRelocTarget(
303 zld,
304 object_id,
305 rel,
306 mem.asBytes(&record),
307 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
308 );
309 const target_object = zld.objects.items[target.getFile().?];
310 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
311 markLive(zld, target_atom_index, alive);
312 }
313 }
314 }
315}
316
317fn markEhFrameRecord(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *AtomTable) !void {
318 const cpu_arch = zld.options.target.cpu.arch;
319 const object = &zld.objects.items[object_id];
320 var it = object.getEhFrameRecordsIterator();
321
322 const fde_offset = object.eh_frame_records_lookup.get(atom_index).?;
323 it.seekTo(fde_offset);
324 const fde = (try it.next()).?;
325
326 const cie_ptr = fde.getCiePointer();
327 const cie_offset = fde_offset + 4 - cie_ptr;
328 it.seekTo(cie_offset);
329 const cie = (try it.next()).?;
330
331 switch (cpu_arch) {
332 .aarch64 => {
333 // Mark FDE references which should include any referenced LSDA record
334 const relocs = eh_frame.getRelocs(zld, object_id, fde_offset);
335 for (relocs) |rel| {
336 const target = UnwindInfo.parseRelocTarget(
337 zld,
338 object_id,
339 rel,
340 fde.data,
341 @intCast(i32, fde_offset) + 4,
342 );
343 const target_sym = zld.getSymbol(target);
344 if (!target_sym.undf()) blk: {
345 const target_object = zld.objects.items[target.getFile().?];
346 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse
347 break :blk;
348 markLive(zld, target_atom_index, alive);
349 }
350 }
351 },
352 .x86_64 => {
353 const lsda_ptr = try fde.getLsdaPointer(cie, .{
354 .base_addr = object.eh_frame_sect.?.addr,
355 .base_offset = fde_offset,
356 });
357 if (lsda_ptr) |lsda_address| {
358 // Mark LSDA record as live
359 const sym_index = object.getSymbolByAddress(lsda_address, null);
360 const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;
361 markLive(zld, target_atom_index, alive);
362 }
363 },
364 else => unreachable,
365 }
366
367 // Mark CIE references which should include any referenced personalities
368 // that are defined locally.
369 if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| {
370 const target_sym = zld.getSymbol(target);
371 if (!target_sym.undf()) {
372 const target_object = zld.objects.items[target.getFile().?];
373 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
374 markLive(zld, target_atom_index, alive);
375 }
376 }
262377}
263378
264379fn prune(zld: *Zld, alive: AtomTable) void {
......@@ -275,10 +390,10 @@ fn prune(zld: *Zld, alive: AtomTable) void {
275390 const atom = zld.getAtom(atom_index);
276391 const sym_loc = atom.getSymbolWithLoc();
277392
278 log.debug("prune(ATOM({d}, %{d}, {d}))", .{
393 log.debug("prune(ATOM({d}, %{d}, {?d}))", .{
279394 atom_index,
280395 sym_loc.sym_index,
281 sym_loc.file,
396 sym_loc.getFile(),
282397 });
283398 log.debug(" {s} in {s}", .{ zld.getSymbolName(sym_loc), object.name });
284399
src/link/MachO/eh_frame.zig created+625
......@@ -0,0 +1,625 @@
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 relocate target symbol address ourselves.
163 const atom = zld.getAtom(atom_index);
164 const atom_sym = zld.getSymbol(atom.getSymbolWithLoc());
165 try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{
166 .base_addr = sect.addr,
167 .base_offset = eh_frame_offset,
168 });
169
170 // We need to parse LSDA pointer and relocate ourselves.
171 const cie_record = eh_records.get(
172 eh_frame_offset + 4 - fde_record.getCiePointer(),
173 ).?;
174 const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
175 .base_addr = object.eh_frame_sect.?.addr,
176 .base_offset = fde_record_offset,
177 });
178 if (source_lsda_ptr) |ptr| {
179 const sym_index = object.getSymbolByAddress(ptr, null);
180 const sym = object.symtab[sym_index];
181 try fde_record.setLsdaPointer(cie_record, sym.n_value, .{
182 .base_addr = sect.addr,
183 .base_offset = eh_frame_offset,
184 });
185 }
186 },
187 else => unreachable,
188 }
189
190 eh_records.putAssumeCapacityNoClobber(eh_frame_offset, fde_record);
191
192 UnwindInfo.UnwindEncoding.setDwarfSectionOffset(
193 &record.compactUnwindEncoding,
194 cpu_arch,
195 @intCast(u24, eh_frame_offset),
196 );
197
198 const cie_record = eh_records.get(
199 eh_frame_offset + 4 - fde_record.getCiePointer(),
200 ).?;
201 const lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
202 .base_addr = sect.addr,
203 .base_offset = eh_frame_offset,
204 });
205 if (lsda_ptr) |ptr| {
206 record.lsda = ptr - seg.vmaddr;
207 }
208
209 eh_frame_offset += fde_record.getSize();
210 }
211 }
212
213 var buffer = std.ArrayList(u8).init(gpa);
214 defer buffer.deinit();
215 const writer = buffer.writer();
216
217 for (eh_records.values()) |record| {
218 try writer.writeIntLittle(u32, record.size);
219 try buffer.appendSlice(record.data);
220 }
221
222 try zld.file.pwriteAll(buffer.items, sect.offset);
223}
224const EhFrameRecordTag = enum { cie, fde };
225
226pub fn EhFrameRecord(comptime is_mutable: bool) type {
227 return struct {
228 tag: EhFrameRecordTag,
229 size: u32,
230 data: if (is_mutable) []u8 else []const u8,
231
232 const Record = @This();
233
234 pub fn deinit(rec: *Record, gpa: Allocator) void {
235 comptime assert(is_mutable);
236 gpa.free(rec.data);
237 }
238
239 pub fn toOwned(rec: Record, gpa: Allocator) Allocator.Error!EhFrameRecord(true) {
240 const data = try gpa.dupe(u8, rec.data);
241 return EhFrameRecord(true){
242 .tag = rec.tag,
243 .size = rec.size,
244 .data = data,
245 };
246 }
247
248 pub inline fn getSize(rec: Record) u32 {
249 return 4 + rec.size;
250 }
251
252 pub fn scanRelocs(
253 rec: Record,
254 zld: *Zld,
255 object_id: u32,
256 source_offset: u32,
257 ) !void {
258 if (rec.getPersonalityPointerReloc(zld, object_id, source_offset)) |target| {
259 try Atom.addGotEntry(zld, target);
260 }
261 }
262
263 pub fn getTargetSymbolAddress(rec: Record, ctx: struct {
264 base_addr: u64,
265 base_offset: u64,
266 }) u64 {
267 assert(rec.tag == .fde);
268 const addend = mem.readIntLittle(i64, rec.data[4..][0..8]);
269 return @intCast(u64, @intCast(i64, ctx.base_addr + ctx.base_offset + 8) + addend);
270 }
271
272 pub fn setTargetSymbolAddress(rec: *Record, value: u64, ctx: struct {
273 base_addr: u64,
274 base_offset: u64,
275 }) !void {
276 assert(rec.tag == .fde);
277 const addend = @intCast(i64, value) - @intCast(i64, ctx.base_addr + ctx.base_offset + 8);
278 mem.writeIntLittle(i64, rec.data[4..][0..8], addend);
279 }
280
281 pub fn getPersonalityPointerReloc(
282 rec: Record,
283 zld: *Zld,
284 object_id: u32,
285 source_offset: u32,
286 ) ?SymbolWithLoc {
287 const cpu_arch = zld.options.target.cpu.arch;
288 const relocs = getRelocs(zld, object_id, source_offset);
289 for (relocs) |rel| {
290 switch (cpu_arch) {
291 .aarch64 => {
292 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
293 switch (rel_type) {
294 .ARM64_RELOC_SUBTRACTOR,
295 .ARM64_RELOC_UNSIGNED,
296 => continue,
297 .ARM64_RELOC_POINTER_TO_GOT => {},
298 else => unreachable,
299 }
300 },
301 .x86_64 => {
302 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
303 switch (rel_type) {
304 .X86_64_RELOC_GOT => {},
305 else => unreachable,
306 }
307 },
308 else => unreachable,
309 }
310 const target = UnwindInfo.parseRelocTarget(
311 zld,
312 object_id,
313 rel,
314 rec.data,
315 @intCast(i32, source_offset) + 4,
316 );
317 return target;
318 }
319 return null;
320 }
321
322 pub fn relocate(rec: *Record, zld: *Zld, object_id: u32, ctx: struct {
323 source_offset: u32,
324 out_offset: u32,
325 sect_addr: u64,
326 }) !void {
327 comptime assert(is_mutable);
328
329 const cpu_arch = zld.options.target.cpu.arch;
330 const relocs = getRelocs(zld, object_id, ctx.source_offset);
331
332 for (relocs) |rel| {
333 const target = UnwindInfo.parseRelocTarget(
334 zld,
335 object_id,
336 rel,
337 rec.data,
338 @intCast(i32, ctx.source_offset) + 4,
339 );
340 const rel_offset = @intCast(u32, rel.r_address - @intCast(i32, ctx.source_offset) - 4);
341 const source_addr = ctx.sect_addr + rel_offset + ctx.out_offset + 4;
342
343 switch (cpu_arch) {
344 .aarch64 => {
345 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
346 switch (rel_type) {
347 .ARM64_RELOC_SUBTRACTOR => {
348 // Address of the __eh_frame in the source object file
349 },
350 .ARM64_RELOC_POINTER_TO_GOT => {
351 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
352 const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse
353 return error.Overflow;
354 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);
355 },
356 .ARM64_RELOC_UNSIGNED => {
357 assert(rel.r_extern == 1);
358 const target_addr = try Atom.getRelocTargetAddress(zld, target, false, false);
359 const result = @intCast(i64, target_addr) - @intCast(i64, source_addr);
360 mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @intCast(i64, result));
361 },
362 else => unreachable,
363 }
364 },
365 .x86_64 => {
366 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
367 switch (rel_type) {
368 .X86_64_RELOC_GOT => {
369 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
370 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
371 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
372 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
373 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], disp);
374 },
375 else => unreachable,
376 }
377 },
378 else => unreachable,
379 }
380 }
381 }
382
383 pub fn getCiePointer(rec: Record) u32 {
384 assert(rec.tag == .fde);
385 return mem.readIntLittle(u32, rec.data[0..4]);
386 }
387
388 pub fn setCiePointer(rec: *Record, ptr: u32) void {
389 assert(rec.tag == .fde);
390 mem.writeIntLittle(u32, rec.data[0..4], ptr);
391 }
392
393 pub fn getAugmentationString(rec: Record) []const u8 {
394 assert(rec.tag == .cie);
395 return mem.sliceTo(@ptrCast([*:0]const u8, rec.data.ptr + 5), 0);
396 }
397
398 pub fn getPersonalityPointer(rec: Record, ctx: struct {
399 base_addr: u64,
400 base_offset: u64,
401 }) !?u64 {
402 assert(rec.tag == .cie);
403 const aug_str = rec.getAugmentationString();
404
405 var stream = std.io.fixedBufferStream(rec.data[9 + aug_str.len ..]);
406 var creader = std.io.countingReader(stream.reader());
407 const reader = creader.reader();
408
409 for (aug_str) |ch, i| switch (ch) {
410 'z' => if (i > 0) {
411 return error.BadDwarfCfi;
412 } else {
413 _ = try leb.readULEB128(u64, reader);
414 },
415 'R' => {
416 _ = try reader.readByte();
417 },
418 'P' => {
419 const enc = try reader.readByte();
420 const offset = ctx.base_offset + 13 + aug_str.len + creader.bytes_read;
421 const ptr = try getEncodedPointer(enc, @intCast(i64, ctx.base_addr + offset), reader);
422 return ptr;
423 },
424 'L' => {
425 _ = try reader.readByte();
426 },
427 'S', 'B', 'G' => {},
428 else => return error.BadDwarfCfi,
429 };
430
431 return null;
432 }
433
434 pub fn getLsdaPointer(rec: Record, cie: Record, ctx: struct {
435 base_addr: u64,
436 base_offset: u64,
437 }) !?u64 {
438 assert(rec.tag == .fde);
439 const enc = (try cie.getLsdaEncoding()) orelse return null;
440 var stream = std.io.fixedBufferStream(rec.data[20..]);
441 const reader = stream.reader();
442 _ = try reader.readByte();
443 const offset = ctx.base_offset + 25;
444 const ptr = try getEncodedPointer(enc, @intCast(i64, ctx.base_addr + offset), reader);
445 return ptr;
446 }
447
448 pub fn setLsdaPointer(rec: *Record, cie: Record, value: u64, ctx: struct {
449 base_addr: u64,
450 base_offset: u64,
451 }) !void {
452 assert(rec.tag == .fde);
453 const enc = (try cie.getLsdaEncoding()) orelse unreachable;
454 var stream = std.io.fixedBufferStream(rec.data[21..]);
455 const writer = stream.writer();
456 const offset = ctx.base_offset + 25;
457 try setEncodedPointer(enc, @intCast(i64, ctx.base_addr + offset), value, writer);
458 }
459
460 fn getLsdaEncoding(rec: Record) !?u8 {
461 assert(rec.tag == .cie);
462 const aug_str = rec.getAugmentationString();
463
464 const base_offset = 9 + aug_str.len;
465 var stream = std.io.fixedBufferStream(rec.data[base_offset..]);
466 var creader = std.io.countingReader(stream.reader());
467 const reader = creader.reader();
468
469 for (aug_str) |ch, i| switch (ch) {
470 'z' => if (i > 0) {
471 return error.BadDwarfCfi;
472 } else {
473 _ = try leb.readULEB128(u64, reader);
474 },
475 'R' => {
476 _ = try reader.readByte();
477 },
478 'P' => {
479 const enc = try reader.readByte();
480 _ = try getEncodedPointer(enc, 0, reader);
481 },
482 'L' => {
483 const enc = try reader.readByte();
484 return enc;
485 },
486 'S', 'B', 'G' => {},
487 else => return error.BadDwarfCfi,
488 };
489
490 return null;
491 }
492
493 fn getEncodedPointer(enc: u8, pcrel_offset: i64, reader: anytype) !?u64 {
494 if (enc == EH_PE.omit) return null;
495
496 var ptr: i64 = switch (enc & 0x0F) {
497 EH_PE.absptr => @bitCast(i64, try reader.readIntLittle(u64)),
498 EH_PE.udata2 => @bitCast(i16, try reader.readIntLittle(u16)),
499 EH_PE.udata4 => @bitCast(i32, try reader.readIntLittle(u32)),
500 EH_PE.udata8 => @bitCast(i64, try reader.readIntLittle(u64)),
501 EH_PE.uleb128 => @bitCast(i64, try leb.readULEB128(u64, reader)),
502 EH_PE.sdata2 => try reader.readIntLittle(i16),
503 EH_PE.sdata4 => try reader.readIntLittle(i32),
504 EH_PE.sdata8 => try reader.readIntLittle(i64),
505 EH_PE.sleb128 => try leb.readILEB128(i64, reader),
506 else => return null,
507 };
508
509 switch (enc & 0x70) {
510 EH_PE.absptr => {},
511 EH_PE.pcrel => ptr += pcrel_offset,
512 EH_PE.datarel,
513 EH_PE.textrel,
514 EH_PE.funcrel,
515 EH_PE.aligned,
516 => return null,
517 else => return null,
518 }
519
520 return @bitCast(u64, ptr);
521 }
522
523 fn setEncodedPointer(enc: u8, pcrel_offset: i64, value: u64, writer: anytype) !void {
524 if (enc == EH_PE.omit) return;
525
526 var actual = @intCast(i64, value);
527
528 switch (enc & 0x70) {
529 EH_PE.absptr => {},
530 EH_PE.pcrel => actual -= pcrel_offset,
531 EH_PE.datarel,
532 EH_PE.textrel,
533 EH_PE.funcrel,
534 EH_PE.aligned,
535 => unreachable,
536 else => unreachable,
537 }
538
539 switch (enc & 0x0F) {
540 EH_PE.absptr => try writer.writeIntLittle(u64, @bitCast(u64, actual)),
541 EH_PE.udata2 => try writer.writeIntLittle(u16, @bitCast(u16, @intCast(i16, actual))),
542 EH_PE.udata4 => try writer.writeIntLittle(u32, @bitCast(u32, @intCast(i32, actual))),
543 EH_PE.udata8 => try writer.writeIntLittle(u64, @bitCast(u64, actual)),
544 EH_PE.uleb128 => try leb.writeULEB128(writer, @bitCast(u64, actual)),
545 EH_PE.sdata2 => try writer.writeIntLittle(i16, @intCast(i16, actual)),
546 EH_PE.sdata4 => try writer.writeIntLittle(i32, @intCast(i32, actual)),
547 EH_PE.sdata8 => try writer.writeIntLittle(i64, actual),
548 EH_PE.sleb128 => try leb.writeILEB128(writer, actual),
549 else => unreachable,
550 }
551 }
552 };
553}
554
555pub fn getRelocs(
556 zld: *Zld,
557 object_id: u32,
558 source_offset: u32,
559) []align(1) const macho.relocation_info {
560 const object = &zld.objects.items[object_id];
561 assert(object.hasEhFrameRecords());
562 const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
563 return &[0]macho.relocation_info{};
564 const all_relocs = object.getRelocs(object.eh_frame_sect.?);
565 return all_relocs[urel.reloc.start..][0..urel.reloc.len];
566}
567
568pub const Iterator = struct {
569 data: []const u8,
570 pos: u32 = 0,
571
572 pub fn next(it: *Iterator) !?EhFrameRecord(false) {
573 if (it.pos >= it.data.len) return null;
574
575 var stream = std.io.fixedBufferStream(it.data[it.pos..]);
576 const reader = stream.reader();
577
578 var size = try reader.readIntLittle(u32);
579 if (size == 0xFFFFFFFF) {
580 log.err("MachO doesn't support 64bit DWARF CFI __eh_frame records", .{});
581 return error.BadDwarfCfi;
582 }
583
584 const id = try reader.readIntLittle(u32);
585 const tag: EhFrameRecordTag = if (id == 0) .cie else .fde;
586 const offset: u32 = 4;
587 const record = EhFrameRecord(false){
588 .tag = tag,
589 .size = size,
590 .data = it.data[it.pos + offset ..][0..size],
591 };
592
593 it.pos += size + offset;
594
595 return record;
596 }
597
598 pub fn reset(it: *Iterator) void {
599 it.pos = 0;
600 }
601
602 pub fn seekTo(it: *Iterator, pos: u32) void {
603 assert(pos >= 0 and pos < it.data.len);
604 it.pos = pos;
605 }
606};
607
608pub const EH_PE = struct {
609 pub const absptr = 0x00;
610 pub const uleb128 = 0x01;
611 pub const udata2 = 0x02;
612 pub const udata4 = 0x03;
613 pub const udata8 = 0x04;
614 pub const sleb128 = 0x09;
615 pub const sdata2 = 0x0A;
616 pub const sdata4 = 0x0B;
617 pub const sdata8 = 0x0C;
618 pub const pcrel = 0x10;
619 pub const textrel = 0x20;
620 pub const datarel = 0x30;
621 pub const funcrel = 0x40;
622 pub const aligned = 0x50;
623 pub const indirect = 0x80;
624 pub const omit = 0xFF;
625};
src/link/MachO/thunks.zig+4-5
......@@ -68,7 +68,7 @@ pub const Thunk = struct {
6868 }
6969};
7070
71pub fn createThunks(zld: *Zld, sect_id: u8, reverse_lookups: [][]u32) !void {
71pub fn createThunks(zld: *Zld, sect_id: u8) !void {
7272 const header = &zld.sections.items(.header)[sect_id];
7373 if (header.size == 0) return;
7474
......@@ -140,7 +140,6 @@ pub fn createThunks(zld: *Zld, sect_id: u8, reverse_lookups: [][]u32) !void {
140140 try scanRelocs(
141141 zld,
142142 atom_index,
143 reverse_lookups[atom.getFile().?],
144143 allocated,
145144 thunk_index,
146145 group_end,
......@@ -214,7 +213,6 @@ fn allocateThunk(
214213fn scanRelocs(
215214 zld: *Zld,
216215 atom_index: AtomIndex,
217 reverse_lookup: []u32,
218216 allocated: std.AutoHashMap(AtomIndex, void),
219217 thunk_index: ThunkIndex,
220218 group_end: AtomIndex,
......@@ -231,7 +229,7 @@ fn scanRelocs(
231229 for (relocs) |rel| {
232230 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);
235233 if (isReachable(zld, atom_index, rel, base_offset, target, allocated)) continue;
236234
237235 log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{
......@@ -308,7 +306,8 @@ fn isReachable(
308306 if (!allocated.contains(target_atom_index)) return false;
309307
310308 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;
312311 _ = Atom.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
313312 return false;
314313
src/link/MachO/zld.zig+123-104
......@@ -10,6 +10,7 @@ const mem = std.mem;
1010
1111const aarch64 = @import("../../arch/aarch64/bits.zig");
1212const dead_strip = @import("dead_strip.zig");
13const eh_frame = @import("eh_frame.zig");
1314const fat = @import("fat.zig");
1415const link = @import("../../link.zig");
1516const load_commands = @import("load_commands.zig");
......@@ -30,6 +31,7 @@ const LibStub = @import("../tapi.zig").LibStub;
3031const Object = @import("Object.zig");
3132const StringTable = @import("../strtab.zig").StringTable;
3233const Trie = @import("Trie.zig");
34const UnwindInfo = @import("UnwindInfo.zig");
3335
3436const Bind = @import("dyld_info/bind.zig").Bind(*const Zld, SymbolWithLoc);
3537const LazyBind = @import("dyld_info/bind.zig").LazyBind(*const Zld, SymbolWithLoc);
......@@ -389,6 +391,14 @@ pub const Zld = struct {
389391 break :blk null;
390392 }
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
392402 if (sect.isCode()) {
393403 break :blk self.getSectionByName("__TEXT", "__text") orelse try self.initSection(
394404 "__TEXT",
......@@ -402,12 +412,6 @@ pub const Zld = struct {
402412 }
403413
404414 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 }
411415 break :blk null;
412416 }
413417
......@@ -459,13 +463,6 @@ pub const Zld = struct {
459463 );
460464 },
461465 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 }
469466 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
470467 segname,
471468 sectname,
......@@ -937,7 +934,7 @@ pub const Zld = struct {
937934 }
938935 }
939936
940 fn resolveSymbolsInObject(self: *Zld, object_id: u16, resolver: *SymbolResolver) !void {
937 fn resolveSymbolsInObject(self: *Zld, object_id: u32, resolver: *SymbolResolver) !void {
941938 const object = &self.objects.items[object_id];
942939 const in_symtab = object.in_symtab orelse return;
943940
......@@ -977,7 +974,7 @@ pub const Zld = struct {
977974 continue;
978975 }
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
982979 const global_index = resolver.table.get(sym_name) orelse {
983980 const gpa = self.gpa;
......@@ -1378,7 +1375,7 @@ pub const Zld = struct {
13781375 }
13791376 }
13801377
1381 fn writeAtoms(self: *Zld, reverse_lookups: [][]u32) !void {
1378 fn writeAtoms(self: *Zld) !void {
13821379 const gpa = self.gpa;
13831380 const slice = self.sections.slice();
13841381
......@@ -1386,6 +1383,7 @@ pub const Zld = struct {
13861383 const header = slice.items(.header)[sect_id];
13871384 var atom_index = first_atom_index;
13881385
1386 if (atom_index == 0) continue;
13891387 if (header.isZerofill()) continue;
13901388
13911389 var buffer = std.ArrayList(u8).init(gpa);
......@@ -1407,7 +1405,7 @@ pub const Zld = struct {
14071405 log.debug(" (adding ATOM(%{d}, '{s}') from object({?}) to buffer)", .{
14081406 atom.sym_index,
14091407 self.getSymbolName(atom.getSymbolWithLoc()),
1410 atom.file,
1408 atom.getFile(),
14111409 });
14121410 if (padding_size > 0) {
14131411 log.debug(" (with padding {x})", .{padding_size});
......@@ -1460,7 +1458,6 @@ pub const Zld = struct {
14601458 atom_index,
14611459 buffer.items[offset..][0..size],
14621460 relocs,
1463 reverse_lookups[atom.getFile().?],
14641461 );
14651462 }
14661463
......@@ -1501,9 +1498,10 @@ pub const Zld = struct {
15011498 while (i < slice.len) : (i += 1) {
15021499 const section = self.sections.get(i);
15031500 if (section.header.size == 0) {
1504 log.debug("pruning section {s},{s}", .{
1501 log.debug("pruning section {s},{s} {d}", .{
15051502 section.header.segName(),
15061503 section.header.sectName(),
1504 section.first_atom_index,
15071505 });
15081506 continue;
15091507 }
......@@ -1519,7 +1517,7 @@ pub const Zld = struct {
15191517 }
15201518 }
15211519
1522 fn calcSectionSizes(self: *Zld, reverse_lookups: [][]u32) !void {
1520 fn calcSectionSizes(self: *Zld) !void {
15231521 const slice = self.sections.slice();
15241522 for (slice.items(.header)) |*header, sect_id| {
15251523 if (header.size == 0) continue;
......@@ -1528,6 +1526,8 @@ pub const Zld = struct {
15281526 }
15291527
15301528 var atom_index = slice.items(.first_atom_index)[sect_id];
1529 if (atom_index == 0) continue;
1530
15311531 header.size = 0;
15321532 header.@"align" = 0;
15331533
......@@ -1556,7 +1556,7 @@ pub const Zld = struct {
15561556 if (mem.eql(u8, header.sectName(), "__stub_helper")) continue;
15571557
15581558 // 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));
15601560 }
15611561 }
15621562 }
......@@ -1601,8 +1601,6 @@ pub const Zld = struct {
16011601
16021602 const slice = self.sections.slice();
16031603 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
16061604 const alignment = try math.powi(u32, 2, header.@"align");
16071605 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
16081606 const n_sect = @intCast(u8, indexes.start + sect_id + 1);
......@@ -1613,48 +1611,51 @@ pub const Zld = struct {
16131611 @intCast(u32, segment.fileoff + start_aligned);
16141612 header.addr = segment.vmaddr + start_aligned;
16151613
1616 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1617 n_sect,
1618 header.segName(),
1619 header.sectName(),
1620 });
1614 var atom_index = slice.items(.first_atom_index)[indexes.start + sect_id];
1615 if (atom_index > 0) {
1616 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1617 n_sect,
1618 header.segName(),
1619 header.sectName(),
1620 });
16211621
1622 while (true) {
1623 const atom = self.getAtom(atom_index);
1624 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1625 sym.n_value += header.addr;
1626 sym.n_sect = n_sect;
1622 while (true) {
1623 const atom = self.getAtom(atom_index);
1624 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1625 sym.n_value += header.addr;
1626 sym.n_sect = n_sect;
16271627
1628 log.debug(" ATOM(%{d}, '{s}') @{x}", .{
1629 atom.sym_index,
1630 self.getSymbolName(atom.getSymbolWithLoc()),
1631 sym.n_value,
1632 });
1628 log.debug(" ATOM(%{d}, '{s}') @{x}", .{
1629 atom.sym_index,
1630 self.getSymbolName(atom.getSymbolWithLoc()),
1631 sym.n_value,
1632 });
16331633
1634 if (atom.getFile() != null) {
1635 // Update each symbol contained within the atom
1636 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1637 while (it.next()) |sym_loc| {
1638 const inner_sym = self.getSymbolPtr(sym_loc);
1639 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1640 self,
1641 atom_index,
1642 sym_loc.sym_index,
1643 );
1644 inner_sym.n_sect = n_sect;
1645 }
1634 if (atom.getFile() != null) {
1635 // Update each symbol contained within the atom
1636 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1637 while (it.next()) |sym_loc| {
1638 const inner_sym = self.getSymbolPtr(sym_loc);
1639 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1640 self,
1641 atom_index,
1642 sym_loc.sym_index,
1643 );
1644 inner_sym.n_sect = n_sect;
1645 }
16461646
1647 // If there is a section alias, update it now too
1648 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
1649 const alias = self.getSymbolPtr(sym_loc);
1650 alias.n_value = sym.n_value;
1651 alias.n_sect = n_sect;
1647 // If there is a section alias, update it now too
1648 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
1649 const alias = self.getSymbolPtr(sym_loc);
1650 alias.n_value = sym.n_value;
1651 alias.n_sect = n_sect;
1652 }
16521653 }
1653 }
16541654
1655 if (atom.next_index) |next_index| {
1656 atom_index = next_index;
1657 } else break;
1655 if (atom.next_index) |next_index| {
1656 atom_index = next_index;
1657 } else break;
1658 }
16581659 }
16591660
16601661 start = start_aligned + header.size;
......@@ -1675,7 +1676,7 @@ pub const Zld = struct {
16751676 reserved2: u32 = 0,
16761677 };
16771678
1678 fn initSection(
1679 pub fn initSection(
16791680 self: *Zld,
16801681 segname: []const u8,
16811682 sectname: []const u8,
......@@ -1685,7 +1686,7 @@ pub const Zld = struct {
16851686 log.debug("creating section '{s},{s}'", .{ segname, sectname });
16861687 const index = @intCast(u8, self.sections.slice().len);
16871688 try self.sections.append(gpa, .{
1688 .segment_index = undefined,
1689 .segment_index = undefined, // Segments will be created automatically later down the pipeline
16891690 .header = .{
16901691 .sectname = makeStaticString(sectname),
16911692 .segname = makeStaticString(segname),
......@@ -1693,13 +1694,13 @@ pub const Zld = struct {
16931694 .reserved1 = opts.reserved1,
16941695 .reserved2 = opts.reserved2,
16951696 },
1696 .first_atom_index = undefined,
1697 .last_atom_index = undefined,
1697 .first_atom_index = 0,
1698 .last_atom_index = 0,
16981699 });
16991700 return index;
17001701 }
17011702
1702 inline fn getSegmentPrecedence(segname: []const u8) u4 {
1703 fn getSegmentPrecedence(segname: []const u8) u4 {
17031704 if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;
17041705 if (mem.eql(u8, segname, "__TEXT")) return 0x1;
17051706 if (mem.eql(u8, segname, "__DATA_CONST")) return 0x2;
......@@ -1708,14 +1709,14 @@ pub const Zld = struct {
17081709 return 0x4;
17091710 }
17101711
1711 inline fn getSegmentMemoryProtection(segname: []const u8) macho.vm_prot_t {
1712 fn getSegmentMemoryProtection(segname: []const u8) macho.vm_prot_t {
17121713 if (mem.eql(u8, segname, "__PAGEZERO")) return macho.PROT.NONE;
17131714 if (mem.eql(u8, segname, "__TEXT")) return macho.PROT.READ | macho.PROT.EXEC;
17141715 if (mem.eql(u8, segname, "__LINKEDIT")) return macho.PROT.READ;
17151716 return macho.PROT.READ | macho.PROT.WRITE;
17161717 }
17171718
1718 inline fn getSectionPrecedence(header: macho.section_64) u8 {
1719 fn getSectionPrecedence(header: macho.section_64) u8 {
17191720 const segment_precedence: u4 = getSegmentPrecedence(header.segName());
17201721 const section_precedence: u4 = blk: {
17211722 if (header.isCode()) {
......@@ -1732,10 +1733,11 @@ pub const Zld = struct {
17321733 macho.S_ZEROFILL => break :blk 0xf,
17331734 macho.S_THREAD_LOCAL_REGULAR => break :blk 0xd,
17341735 macho.S_THREAD_LOCAL_ZEROFILL => break :blk 0xe,
1735 else => if (mem.eql(u8, "__eh_frame", header.sectName()))
1736 break :blk 0xf
1737 else
1738 break :blk 0x3,
1736 else => {
1737 if (mem.eql(u8, "__unwind_info", header.sectName())) break :blk 0xe;
1738 if (mem.eql(u8, "__eh_frame", header.sectName())) break :blk 0xf;
1739 break :blk 0x3;
1740 },
17391741 }
17401742 };
17411743 return (@intCast(u8, segment_precedence) << 4) + section_precedence;
......@@ -1768,8 +1770,8 @@ pub const Zld = struct {
17681770 }
17691771 }
17701772
1771 fn writeLinkeditSegmentData(self: *Zld, reverse_lookups: [][]u32) !void {
1772 try self.writeDyldInfoData(reverse_lookups);
1773 fn writeLinkeditSegmentData(self: *Zld) !void {
1774 try self.writeDyldInfoData();
17731775 try self.writeFunctionStarts();
17741776 try self.writeDataInCode();
17751777 try self.writeSymtabs();
......@@ -1806,7 +1808,7 @@ pub const Zld = struct {
18061808 }
18071809 }
18081810
1809 fn collectRebaseData(self: *Zld, rebase: *Rebase, reverse_lookups: [][]u32) !void {
1811 fn collectRebaseData(self: *Zld, rebase: *Rebase) !void {
18101812 log.debug("collecting rebase data", .{});
18111813
18121814 // First, unpack GOT entries
......@@ -1862,6 +1864,7 @@ pub const Zld = struct {
18621864
18631865 const cpu_arch = self.options.target.cpu.arch;
18641866 var atom_index = slice.items(.first_atom_index)[sect_id];
1867 if (atom_index == 0) continue;
18651868
18661869 while (true) {
18671870 const atom = self.getAtom(atom_index);
......@@ -1899,7 +1902,7 @@ pub const Zld = struct {
18991902 },
19001903 else => unreachable,
19011904 }
1902 const target = Atom.parseRelocTarget(self, atom_index, rel, reverse_lookups[atom.getFile().?]);
1905 const target = Atom.parseRelocTarget(self, atom_index, rel);
19031906 const target_sym = self.getSymbol(target);
19041907 if (target_sym.undf()) continue;
19051908
......@@ -1962,7 +1965,10 @@ pub const Zld = struct {
19621965 }
19631966 }
19641967
1965 fn collectBindData(self: *Zld, bind: *Bind, reverse_lookups: [][]u32) !void {
1968 fn collectBindData(
1969 self: *Zld,
1970 bind: *Bind,
1971 ) !void {
19661972 log.debug("collecting bind data", .{});
19671973
19681974 // First, unpack GOT section
......@@ -1993,6 +1999,7 @@ pub const Zld = struct {
19931999
19942000 const cpu_arch = self.options.target.cpu.arch;
19952001 var atom_index = slice.items(.first_atom_index)[sect_id];
2002 if (atom_index == 0) continue;
19962003
19972004 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
19982005
......@@ -2033,7 +2040,7 @@ pub const Zld = struct {
20332040 else => unreachable,
20342041 }
20352042
2036 const global = Atom.parseRelocTarget(self, atom_index, rel, reverse_lookups[atom.getFile().?]);
2043 const global = Atom.parseRelocTarget(self, atom_index, rel);
20372044 const bind_sym_name = self.getSymbolName(global);
20382045 const bind_sym = self.getSymbol(global);
20392046 if (!bind_sym.undf()) continue;
......@@ -2164,16 +2171,18 @@ pub const Zld = struct {
21642171 try trie.finalize(gpa);
21652172 }
21662173
2167 fn writeDyldInfoData(self: *Zld, reverse_lookups: [][]u32) !void {
2174 fn writeDyldInfoData(
2175 self: *Zld,
2176 ) !void {
21682177 const gpa = self.gpa;
21692178
21702179 var rebase = Rebase{};
21712180 defer rebase.deinit(gpa);
2172 try self.collectRebaseData(&rebase, reverse_lookups);
2181 try self.collectRebaseData(&rebase);
21732182
21742183 var bind = Bind{};
21752184 defer bind.deinit(gpa);
2176 try self.collectBindData(&bind, reverse_lookups);
2185 try self.collectBindData(&bind);
21772186
21782187 var lazy_bind = LazyBind{};
21792188 defer lazy_bind.deinit(gpa);
......@@ -2873,12 +2882,12 @@ pub const Zld = struct {
28732882 return buf;
28742883 }
28752884
2876 pub inline fn getAtomPtr(self: *Zld, atom_index: AtomIndex) *Atom {
2885 pub fn getAtomPtr(self: *Zld, atom_index: AtomIndex) *Atom {
28772886 assert(atom_index < self.atoms.items.len);
28782887 return &self.atoms.items[atom_index];
28792888 }
28802889
2881 pub inline fn getAtom(self: Zld, atom_index: AtomIndex) Atom {
2890 pub fn getAtom(self: Zld, atom_index: AtomIndex) Atom {
28822891 assert(atom_index < self.atoms.items.len);
28832892 return self.atoms.items[atom_index];
28842893 }
......@@ -2889,17 +2898,17 @@ pub const Zld = struct {
28892898 } else return null;
28902899 }
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 {
28932902 const index = self.sections.items(.segment_index)[sect_id];
28942903 return self.segments.items[index];
28952904 }
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 {
28982907 const index = self.sections.items(.segment_index)[sect_id];
28992908 return &self.segments.items[index];
29002909 }
29012910
2902 pub inline fn getLinkeditSegmentPtr(self: *Zld) *macho.segment_command_64 {
2911 pub fn getLinkeditSegmentPtr(self: *Zld) *macho.segment_command_64 {
29032912 assert(self.segments.items.len > 0);
29042913 const seg = &self.segments.items[self.segments.items.len - 1];
29052914 assert(mem.eql(u8, seg.segName(), "__LINKEDIT"));
......@@ -3384,6 +3393,8 @@ pub const Zld = struct {
33843393 const slice = self.sections.slice();
33853394 for (slice.items(.first_atom_index)) |first_atom_index, sect_id| {
33863395 var atom_index = first_atom_index;
3396 if (atom_index == 0) continue;
3397
33873398 const header = slice.items(.header)[sect_id];
33883399
33893400 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
......@@ -3412,7 +3423,7 @@ pub const Zld = struct {
34123423 sym.n_value,
34133424 atom.size,
34143425 atom.alignment,
3415 atom.file,
3426 atom.getFile(),
34163427 sym.n_sect,
34173428 });
34183429
......@@ -3475,19 +3486,19 @@ const IndirectPointer = struct {
34753486 }
34763487};
34773488
3478pub const SymbolWithLoc = struct {
3489pub const SymbolWithLoc = extern struct {
34793490 // Index into the respective symbol table.
34803491 sym_index: u32,
34813492
3482 // -1 means it's a synthetic global.
3483 file: i32 = -1,
3493 // 0 means it's a synthetic global.
3494 file: u32 = 0,
34843495
3485 pub inline fn getFile(self: SymbolWithLoc) ?u31 {
3486 if (self.file == -1) return null;
3487 return @intCast(u31, self.file);
3496 pub fn getFile(self: SymbolWithLoc) ?u32 {
3497 if (self.file == 0) return null;
3498 return self.file - 1;
34883499 }
34893500
3490 pub inline fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {
3501 pub fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {
34913502 return self.file == other.file and self.sym_index == other.sym_index;
34923503 }
34933504};
......@@ -3965,7 +3976,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
39653976 };
39663977
39673978 for (zld.objects.items) |_, object_id| {
3968 try zld.resolveSymbolsInObject(@intCast(u16, object_id), &resolver);
3979 try zld.resolveSymbolsInObject(@intCast(u32, object_id), &resolver);
39693980 }
39703981
39713982 try zld.resolveSymbolsInArchives(&resolver);
......@@ -3995,16 +4006,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
39954006 }
39964007
39974008 for (zld.objects.items) |*object, object_id| {
3998 try object.splitIntoAtoms(&zld, @intCast(u31, 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);
4009 try object.splitIntoAtoms(&zld, @intCast(u32, object_id));
40044010 }
40054011
40064012 if (gc_sections) {
4007 try dead_strip.gcAtoms(&zld, reverse_lookups);
4013 try dead_strip.gcAtoms(&zld);
40084014 }
40094015
40104016 try zld.createDyldPrivateAtom();
......@@ -4019,13 +4025,24 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
40194025 if (header.isZerofill()) continue;
40204026
40214027 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);
40234029 }
40244030 }
40254031
4032 try eh_frame.scanRelocs(&zld);
4033 try UnwindInfo.scanRelocs(&zld);
4034
40264035 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
40294046 try zld.pruneAndSortSections();
40304047 try zld.createSegments();
40314048 try zld.allocateSegments();
......@@ -4039,8 +4056,10 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
40394056 zld.logAtoms();
40404057 }
40414058
4042 try zld.writeAtoms(reverse_lookups);
4043 try zld.writeLinkeditSegmentData(reverse_lookups);
4059 try zld.writeAtoms();
4060 try eh_frame.write(&zld, &unwind_info);
4061 try unwind_info.write(&zld);
4062 try zld.writeLinkeditSegmentData();
40444063
40454064 // If the last section of __DATA segment is zerofill section, we need to ensure
40464065 // that the free space between the end of the last non-zerofill section of __DATA
test/link.zig+5
......@@ -190,6 +190,11 @@ fn addMachOCases(cases: *tests.StandaloneContext) void {
190190 .requires_symlinks = true,
191191 });
192192
193 cases.addBuildFile("test/link/macho/unwind_info/build.zig", .{
194 .build_modes = true,
195 .requires_symlinks = true,
196 });
197
193198 cases.addBuildFile("test/link/macho/uuid/build.zig", .{
194199 .build_modes = false,
195200 .requires_symlinks = true,
test/link/macho/unwind_info/all.h created+41
......@@ -0,0 +1,41 @@
1#ifndef ALL
2#define ALL
3
4#include <cstddef>
5#include <string>
6#include <stdexcept>
7
8struct SimpleString {
9 SimpleString(size_t max_size);
10 ~SimpleString();
11
12 void print(const char* tag) const;
13 bool append_line(const char* x);
14
15private:
16 size_t max_size;
17 char* buffer;
18 size_t length;
19};
20
21struct SimpleStringOwner {
22 SimpleStringOwner(const char* x);
23 ~SimpleStringOwner();
24
25private:
26 SimpleString string;
27};
28
29class Error: public std::exception {
30public:
31 explicit Error(const char* msg) : msg{ msg } {}
32 virtual ~Error() noexcept {}
33 virtual const char* what() const noexcept {
34 return msg.c_str();
35 }
36
37protected:
38 std::string msg;
39};
40
41#endif
test/link/macho/unwind_info/build.zig created+68
......@@ -0,0 +1,68 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
5
6pub fn build(b: *Builder) void {
7 const mode = b.standardReleaseOptions();
8 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
9
10 const test_step = b.step("test", "Test the program");
11
12 testUnwindInfo(b, test_step, mode, target, false);
13 testUnwindInfo(b, test_step, mode, target, true);
14}
15
16fn testUnwindInfo(
17 b: *Builder,
18 test_step: *std.build.Step,
19 mode: std.builtin.Mode,
20 target: std.zig.CrossTarget,
21 dead_strip: bool,
22) void {
23 const exe = createScenario(b, mode, target);
24 exe.link_gc_sections = dead_strip;
25
26 const check = exe.checkObject(.macho);
27 check.checkStart("segname __TEXT");
28 check.checkNext("sectname __gcc_except_tab");
29 check.checkNext("sectname __unwind_info");
30
31 switch (builtin.cpu.arch) {
32 .aarch64 => {
33 check.checkNext("sectname __eh_frame");
34 },
35 .x86_64 => {}, // We do not expect `__eh_frame` section on x86_64 in this case
36 else => unreachable,
37 }
38
39 check.checkInSymtab();
40 check.checkNext("{*} (__TEXT,__text) external ___gxx_personality_v0");
41
42 const run_cmd = check.runAndCompare();
43 run_cmd.expectStdOutEqual(
44 \\Constructed: a
45 \\Constructed: b
46 \\About to destroy: b
47 \\About to destroy: a
48 \\Error: Not enough memory!
49 \\
50 );
51
52 test_step.dependOn(&run_cmd.step);
53}
54
55fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {
56 const exe = b.addExecutable("test", null);
57 b.default_step.dependOn(&exe.step);
58 exe.addIncludePath(".");
59 exe.addCSourceFiles(&[_][]const u8{
60 "main.cpp",
61 "simple_string.cpp",
62 "simple_string_owner.cpp",
63 }, &[0][]const u8{});
64 exe.setBuildMode(mode);
65 exe.setTarget(target);
66 exe.linkLibCpp();
67 return exe;
68}
test/link/macho/unwind_info/main.cpp created+24
......@@ -0,0 +1,24 @@
1#include "all.h"
2#include <cstdio>
3
4void fn_c() {
5 SimpleStringOwner c{ "cccccccccc" };
6}
7
8void fn_b() {
9 SimpleStringOwner b{ "b" };
10 fn_c();
11}
12
13int main() {
14 try {
15 SimpleStringOwner a{ "a" };
16 fn_b();
17 SimpleStringOwner d{ "d" };
18 } catch (const Error& e) {
19 printf("Error: %s\n", e.what());
20 } catch(const std::exception& e) {
21 printf("Exception: %s\n", e.what());
22 }
23 return 0;
24}
test/link/macho/unwind_info/simple_string.cpp created+30
......@@ -0,0 +1,30 @@
1#include "all.h"
2#include <cstdio>
3#include <cstring>
4
5SimpleString::SimpleString(size_t max_size)
6: max_size{ max_size }, length{} {
7 if (max_size == 0) {
8 throw Error{ "Max size must be at least 1." };
9 }
10 buffer = new char[max_size];
11 buffer[0] = 0;
12}
13
14SimpleString::~SimpleString() {
15 delete[] buffer;
16}
17
18void SimpleString::print(const char* tag) const {
19 printf("%s: %s", tag, buffer);
20}
21
22bool SimpleString::append_line(const char* x) {
23 const auto x_len = strlen(x);
24 if (x_len + length + 2 > max_size) return false;
25 std::strncpy(buffer + length, x, max_size - length);
26 length += x_len;
27 buffer[length++] = '\n';
28 buffer[length] = 0;
29 return true;
30}
test/link/macho/unwind_info/simple_string_owner.cpp created+12
......@@ -0,0 +1,12 @@
1#include "all.h"
2
3SimpleStringOwner::SimpleStringOwner(const char* x) : string{ 10 } {
4 if (!string.append_line(x)) {
5 throw Error{ "Not enough memory!" };
6 }
7 string.print("Constructed");
8}
9
10SimpleStringOwner::~SimpleStringOwner() {
11 string.print("About to destroy");
12}
test/link/macho/uuid/build.zig+6-6
......@@ -12,18 +12,18 @@ pub fn build(b: *Builder) void {
1212 .os_tag = .macos,
1313 };
1414
15 testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "af0f4c21a07c30daba59213d80262e45");
16 testUuid(b, test_step, .ReleaseFast, aarch64_macos, "af0f4c21a07c30daba59213d80262e45");
17 testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "af0f4c21a07c30daba59213d80262e45");
15 testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9");
16 testUuid(b, test_step, .ReleaseFast, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9");
17 testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9");
1818
1919 const x86_64_macos = std.zig.CrossTarget{
2020 .cpu_arch = .x86_64,
2121 .os_tag = .macos,
2222 };
2323
24 testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "63f47191c7153f5fba48bd63cb2f5f57");
25 testUuid(b, test_step, .ReleaseFast, x86_64_macos, "63f47191c7153f5fba48bd63cb2f5f57");
26 testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "e7bba66220e33eda9e73ab293ccf93d2");
24 testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "5b7071b4587c3071b0d2352fadce0e48");
25 testUuid(b, test_step, .ReleaseFast, x86_64_macos, "5b7071b4587c3071b0d2352fadce0e48");
26 testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "4b58f2583c383169bbe3a716bd240048");
2727}
2828
2929fn testUuid(
test/link/macho/weak_library/build.zig+2
......@@ -31,6 +31,8 @@ pub fn build(b: *Builder) void {
3131
3232 check.checkInSymtab();
3333 check.checkNext("(undefined) weak external _a (from liba)");
34
35 check.checkInSymtab();
3436 check.checkNext("(undefined) weak external _asStr (from liba)");
3537
3638 const run_cmd = check.runAndCompare();