authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-05 12:43:31+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-13 11:47:51+02:00
logef645ab1754d6117d06cf2296de689605546f5f7
treebd78f226f49a7c9dd07e0b1464431f180f7d237a
parent38ecaf3ab6c71aae213edbd38b8f661a03035b3a

macho: refactor common logic between synthetic tables


3 files changed, 147 insertions(+), 161 deletions(-)

src/link/MachO.zig+132-142
......@@ -5,7 +5,6 @@ const build_options = @import("build_options");
55const builtin = @import("builtin");
66const assert = std.debug.assert;
77const dwarf = std.dwarf;
8const fmt = std.fmt;
98const fs = std.fs;
109const log = std.log.scoped(.link);
1110const macho = std.macho;
......@@ -155,13 +154,9 @@ stub_helper_preamble_atom_index: ?Atom.Index = null,
155154
156155strtab: StringTable(.strtab) = .{},
157156
158got_entries: std.ArrayListUnmanaged(Entry) = .{},
159got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
160got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
161
162stubs: std.ArrayListUnmanaged(Entry) = .{},
163stubs_free_list: std.ArrayListUnmanaged(u32) = .{},
164stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
157got_table: SectionTable = .{},
158stubs_table: SectionTable = .{},
159tlvp_table: SectionTable = .{},
165160
166161error_flags: File.ErrorFlags = File.ErrorFlags{},
167162
......@@ -270,26 +265,120 @@ const DeclMetadata = struct {
270265 }
271266};
272267
273const Entry = struct {
274 target: SymbolWithLoc,
275 // Index into the synthetic symbol table (i.e., file == null).
276 sym_index: u32,
268const SectionTable = struct {
269 entries: std.ArrayListUnmanaged(Entry) = .{},
270 free_list: std.ArrayListUnmanaged(u32) = .{},
271 lookup: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
272
273 pub fn deinit(st: *ST, allocator: Allocator) void {
274 st.entries.deinit(allocator);
275 st.free_list.deinit(allocator);
276 st.lookup.deinit(allocator);
277 }
278
279 pub fn allocateEntry(st: *ST, allocator: Allocator, target: SymbolWithLoc) !u32 {
280 try st.entries.ensureUnusedCapacity(allocator, 1);
281 const index = blk: {
282 if (st.free_list.popOrNull()) |index| {
283 log.debug(" (reusing entry index {d})", .{index});
284 break :blk index;
285 } else {
286 log.debug(" (allocating entry at index {d})", .{st.entries.items.len});
287 const index = @intCast(u32, st.entries.items.len);
288 _ = st.entries.addOneAssumeCapacity();
289 break :blk index;
290 }
291 };
292 st.entries.items[index] = .{ .target = target, .sym_index = 0 };
293 try st.lookup.putNoClobber(allocator, target, index);
294 return index;
295 }
277296
278 pub fn getSymbol(entry: Entry, macho_file: *MachO) macho.nlist_64 {
279 return macho_file.getSymbol(.{ .sym_index = entry.sym_index, .file = null });
297 pub fn freeEntry(st: *ST, allocator: Allocator, target: SymbolWithLoc) void {
298 const index = st.lookup.get(target) orelse return;
299 st.free_list.append(allocator, index) catch {};
300 st.entries.items[index] = .{
301 .target = .{ .sym_index = 0 },
302 .sym_index = 0,
303 };
304 _ = st.lookup.remove(target);
280305 }
281306
282 pub fn getSymbolPtr(entry: Entry, macho_file: *MachO) *macho.nlist_64 {
283 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null });
307 pub fn getAtomIndex(st: *const ST, macho_file: *MachO, target: SymbolWithLoc) ?Atom.Index {
308 const index = st.lookup.get(target) orelse return null;
309 return st.entries.items[index].getAtomIndex(macho_file);
310 }
311
312 const FormatContext = struct {
313 macho_file: *MachO,
314 st: *const ST,
315 };
316
317 fn fmt(
318 ctx: FormatContext,
319 comptime unused_format_string: []const u8,
320 options: std.fmt.FormatOptions,
321 writer: anytype,
322 ) @TypeOf(writer).Error!void {
323 _ = options;
324 comptime assert(unused_format_string.len == 0);
325 try writer.writeAll("SectionTable:\n");
326 for (ctx.st.entries.items, 0..) |entry, i| {
327 const atom_sym = entry.getSymbol(ctx.macho_file);
328 const target_sym = ctx.macho_file.getSymbol(entry.target);
329 try writer.print(" {d}@{x} => ", .{ i, atom_sym.n_value });
330 if (target_sym.undf()) {
331 try writer.print("import('{s}')", .{
332 ctx.macho_file.getSymbolName(entry.target),
333 });
334 } else {
335 try writer.print("local(%{d}) in object({?d})", .{
336 entry.target.sym_index,
337 entry.target.file,
338 });
339 }
340 try writer.writeByte('\n');
341 }
284342 }
285343
286 pub fn getAtomIndex(entry: Entry, macho_file: *MachO) ?Atom.Index {
287 return macho_file.getAtomIndexForSymbol(.{ .sym_index = entry.sym_index, .file = null });
344 fn format(st: *const ST, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
345 _ = st;
346 _ = unused_format_string;
347 _ = options;
348 _ = writer;
349 @compileError("do not format SectionTable directly; use st.fmtDebug()");
288350 }
289351
290 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {
291 return macho_file.getSymbolName(.{ .sym_index = entry.sym_index, .file = null });
352 pub fn fmtDebug(st: *const ST, macho_file: *MachO) std.fmt.Formatter(fmt) {
353 return .{ .data = .{
354 .macho_file = macho_file,
355 .st = st,
356 } };
292357 }
358
359 const ST = @This();
360
361 const Entry = struct {
362 target: SymbolWithLoc,
363 // Index into the synthetic symbol table (i.e., file == null).
364 sym_index: u32,
365
366 pub fn getSymbol(entry: Entry, macho_file: *MachO) macho.nlist_64 {
367 return macho_file.getSymbol(.{ .sym_index = entry.sym_index });
368 }
369
370 pub fn getSymbolPtr(entry: Entry, macho_file: *MachO) *macho.nlist_64 {
371 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index });
372 }
373
374 pub fn getAtomIndex(entry: Entry, macho_file: *MachO) ?Atom.Index {
375 return macho_file.getAtomIndexForSymbol(.{ .sym_index = entry.sym_index });
376 }
377
378 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {
379 return macho_file.getSymbolName(.{ .sym_index = entry.sym_index });
380 }
381 };
293382};
294383
295384const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding));
......@@ -399,7 +488,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
399488 // Create dSYM bundle.
400489 log.debug("creating {s}.dSYM bundle", .{sub_path});
401490
402 const d_sym_path = try fmt.allocPrint(
491 const d_sym_path = try std.fmt.allocPrint(
403492 allocator,
404493 "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF",
405494 .{sub_path},
......@@ -613,9 +702,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
613702 if (self.dyld_stub_binder_index == null) {
614703 self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .add_got);
615704 }
616 if (!self.base.options.single_threaded) {
617 _ = try self.addUndefined("_tlv_bootstrap", .none);
618 }
705 // if (!self.base.options.single_threaded) {
706 // _ = try self.addUndefined("_tlv_bootstrap", .none);
707 // }
619708
620709 try self.createMhExecuteHeaderSymbol();
621710
......@@ -1757,12 +1846,9 @@ pub fn deinit(self: *MachO) void {
17571846 d_sym.deinit();
17581847 }
17591848
1760 self.got_entries.deinit(gpa);
1761 self.got_entries_free_list.deinit(gpa);
1762 self.got_entries_table.deinit(gpa);
1763 self.stubs.deinit(gpa);
1764 self.stubs_free_list.deinit(gpa);
1849 self.got_table.deinit(gpa);
17651850 self.stubs_table.deinit(gpa);
1851 self.tlvp_table.deinit(gpa);
17661852 self.strtab.deinit(gpa);
17671853
17681854 self.locals.deinit(gpa);
......@@ -1895,20 +1981,10 @@ fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
18951981 self.locals_free_list.append(gpa, sym_index) catch {};
18961982
18971983 // Try freeing GOT atom if this decl had one
1898 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1899 if (self.got_entries_table.get(got_target)) |got_index| {
1900 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
1901 self.got_entries.items[got_index] = .{
1902 .target = .{ .sym_index = 0, .file = null },
1903 .sym_index = 0,
1904 };
1905 _ = self.got_entries_table.remove(got_target);
1906
1907 if (self.d_sym) |*d_sym| {
1908 d_sym.swapRemoveRelocs(sym_index);
1909 }
1984 self.got_table.freeEntry(gpa, .{ .sym_index = sym_index });
19101985
1911 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
1986 if (self.d_sym) |*d_sym| {
1987 d_sym.swapRemoveRelocs(sym_index);
19121988 }
19131989
19141990 self.locals.items[sym_index].n_type = 0;
......@@ -1983,70 +2059,25 @@ fn allocateGlobal(self: *MachO) !u32 {
19832059 return index;
19842060}
19852061
1986fn allocateGotEntry(self: *MachO, target: SymbolWithLoc) !u32 {
1987 const gpa = self.base.allocator;
1988 try self.got_entries.ensureUnusedCapacity(gpa, 1);
1989
1990 const index = blk: {
1991 if (self.got_entries_free_list.popOrNull()) |index| {
1992 log.debug(" (reusing GOT entry index {d})", .{index});
1993 break :blk index;
1994 } else {
1995 log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len});
1996 const index = @intCast(u32, self.got_entries.items.len);
1997 _ = self.got_entries.addOneAssumeCapacity();
1998 break :blk index;
1999 }
2000 };
2001
2002 self.got_entries.items[index] = .{ .target = target, .sym_index = 0 };
2003 try self.got_entries_table.putNoClobber(gpa, target, index);
2004
2005 return index;
2006}
2007
20082062fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
2009 if (self.got_entries_table.contains(target)) return;
2010
2011 const got_index = try self.allocateGotEntry(target);
2063 if (self.got_table.lookup.contains(target)) return;
2064 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
20122065 const got_atom_index = try self.createGotAtom(target);
20132066 const got_atom = self.getAtom(got_atom_index);
2014 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
2067 self.got_table.entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
20152068 try self.writePtrWidthAtom(got_atom_index);
20162069}
20172070
2018fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 {
2019 try self.stubs.ensureUnusedCapacity(self.base.allocator, 1);
2020
2021 const index = blk: {
2022 if (self.stubs_free_list.popOrNull()) |index| {
2023 log.debug(" (reusing stub entry index {d})", .{index});
2024 break :blk index;
2025 } else {
2026 log.debug(" (allocating stub entry at index {d})", .{self.stubs.items.len});
2027 const index = @intCast(u32, self.stubs.items.len);
2028 _ = self.stubs.addOneAssumeCapacity();
2029 break :blk index;
2030 }
2031 };
2032
2033 self.stubs.items[index] = .{ .target = target, .sym_index = 0 };
2034 try self.stubs_table.putNoClobber(self.base.allocator, target, index);
2035
2036 return index;
2037}
2038
20392071fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
2040 if (self.stubs_table.contains(target)) return;
2041
2042 const stub_index = try self.allocateStubEntry(target);
2072 if (self.stubs_table.lookup.contains(target)) return;
2073 const stub_index = try self.stubs_table.allocateEntry(self.base.allocator, target);
20432074 const stub_helper_atom_index = try self.createStubHelperAtom();
20442075 const stub_helper_atom = self.getAtom(stub_helper_atom_index);
20452076 const laptr_atom_index = try self.createLazyPointerAtom(stub_helper_atom.getSymbolIndex().?, target);
20462077 const laptr_atom = self.getAtom(laptr_atom_index);
20472078 const stub_atom_index = try self.createStubAtom(laptr_atom.getSymbolIndex().?);
20482079 const stub_atom = self.getAtom(stub_atom_index);
2049 self.stubs.items[stub_index].sym_index = stub_atom.getSymbolIndex().?;
2080 self.stubs_table.entries.items[stub_index].sym_index = stub_atom.getSymbolIndex().?;
20502081 self.markRelocsDirtyByTarget(target);
20512082}
20522083
......@@ -2431,7 +2462,7 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64
24312462 sym.n_value = vaddr;
24322463 log.debug(" (updating GOT entry)", .{});
24332464 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
2434 const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?;
2465 const got_atom_index = self.got_table.getAtomIndex(self, got_target).?;
24352466 self.markRelocsDirtyByTarget(got_target);
24362467 try self.writePtrWidthAtom(got_atom_index);
24372468 }
......@@ -3481,8 +3512,8 @@ const SymtabCtx = struct {
34813512
34823513fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
34833514 const gpa = self.base.allocator;
3484 const nstubs = @intCast(u32, self.stubs_table.count());
3485 const ngot_entries = @intCast(u32, self.got_entries_table.count());
3515 const nstubs = @intCast(u32, self.stubs_table.lookup.count());
3516 const ngot_entries = @intCast(u32, self.got_table.lookup.count());
34863517 const nindirectsyms = nstubs * 2 + ngot_entries;
34873518 const iextdefsym = ctx.nlocalsym;
34883519 const iundefsym = iextdefsym + ctx.nextdefsym;
......@@ -3504,7 +3535,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
35043535 if (self.stubs_section_index) |sect_id| {
35053536 const stubs = &self.sections.items(.header)[sect_id];
35063537 stubs.reserved1 = 0;
3507 for (self.stubs.items) |entry| {
3538 for (self.stubs_table.entries.items) |entry| {
35083539 if (entry.sym_index == 0) continue;
35093540 const target_sym = self.getSymbol(entry.target);
35103541 assert(target_sym.undf());
......@@ -3515,7 +3546,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
35153546 if (self.got_section_index) |sect_id| {
35163547 const got = &self.sections.items(.header)[sect_id];
35173548 got.reserved1 = nstubs;
3518 for (self.got_entries.items) |entry| {
3549 for (self.got_table.entries.items) |entry| {
35193550 if (entry.sym_index == 0) continue;
35203551 const target_sym = self.getSymbol(entry.target);
35213552 if (target_sym.undf()) {
......@@ -3529,7 +3560,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
35293560 if (self.la_symbol_ptr_section_index) |sect_id| {
35303561 const la_symbol_ptr = &self.sections.items(.header)[sect_id];
35313562 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
3532 for (self.stubs.items) |entry| {
3563 for (self.stubs_table.entries.items) |entry| {
35333564 if (entry.sym_index == 0) continue;
35343565 const target_sym = self.getSymbol(entry.target);
35353566 assert(target_sym.undf());
......@@ -3874,20 +3905,6 @@ pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.In
38743905 return self.atom_by_index_table.get(sym_with_loc.sym_index);
38753906}
38763907
3877/// Returns GOT atom that references `sym_with_loc` if one exists.
3878/// Returns null otherwise.
3879pub fn getGotAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
3880 const got_index = self.got_entries_table.get(sym_with_loc) orelse return null;
3881 return self.got_entries.items[got_index].getAtomIndex(self);
3882}
3883
3884/// Returns stubs atom that references `sym_with_loc` if one exists.
3885/// Returns null otherwise.
3886pub fn getStubsAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
3887 const stubs_index = self.stubs_table.get(sym_with_loc) orelse return null;
3888 return self.stubs.items[stubs_index].getAtomIndex(self);
3889}
3890
38913908/// Returns symbol location corresponding to the set entrypoint.
38923909/// Asserts output mode is executable.
38933910pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc {
......@@ -4234,37 +4251,10 @@ pub fn logSymtab(self: *MachO) void {
42344251 }
42354252
42364253 log.debug("GOT entries:", .{});
4237 for (self.got_entries.items, 0..) |entry, i| {
4238 const atom_sym = entry.getSymbol(self);
4239 const target_sym = self.getSymbol(entry.target);
4240 if (target_sym.undf()) {
4241 log.debug(" {d}@{x} => import('{s}')", .{
4242 i,
4243 atom_sym.n_value,
4244 self.getSymbolName(entry.target),
4245 });
4246 } else {
4247 log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{
4248 i,
4249 atom_sym.n_value,
4250 entry.target.sym_index,
4251 entry.target.file,
4252 logSymAttributes(target_sym, &buf),
4253 });
4254 }
4255 }
4254 log.debug("{}", .{self.got_table.fmtDebug(self)});
42564255
42574256 log.debug("stubs entries:", .{});
4258 for (self.stubs.items, 0..) |entry, i| {
4259 const target_sym = self.getSymbol(entry.target);
4260 const atom_sym = entry.getSymbol(self);
4261 assert(target_sym.undf());
4262 log.debug(" {d}@{x} => import('{s}')", .{
4263 i,
4264 atom_sym.n_value,
4265 self.getSymbolName(entry.target),
4266 });
4267 }
4257 log.debug("{}", .{self.stubs_table.fmtDebug(self)});
42684258}
42694259
42704260pub fn logAtoms(self: *MachO) void {
src/link/MachO/DebugSymbols.zig+6-12
......@@ -226,26 +226,20 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
226226
227227 for (self.relocs.items) |*reloc| {
228228 const sym = switch (reloc.type) {
229 .direct_load => macho_file.getSymbol(.{ .sym_index = reloc.target, .file = null }),
229 .direct_load => macho_file.getSymbol(.{ .sym_index = reloc.target }),
230230 .got_load => blk: {
231 const got_index = macho_file.got_entries_table.get(.{
232 .sym_index = reloc.target,
233 .file = null,
234 }).?;
235 const got_entry = macho_file.got_entries.items[got_index];
231 const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;
232 const got_entry = macho_file.got_table.entries.items[got_index];
236233 break :blk got_entry.getSymbol(macho_file);
237234 },
238235 };
239236 if (sym.n_value == reloc.prev_vaddr) continue;
240237
241238 const sym_name = switch (reloc.type) {
242 .direct_load => macho_file.getSymbolName(.{ .sym_index = reloc.target, .file = null }),
239 .direct_load => macho_file.getSymbolName(.{ .sym_index = reloc.target }),
243240 .got_load => blk: {
244 const got_index = macho_file.got_entries_table.get(.{
245 .sym_index = reloc.target,
246 .file = null,
247 }).?;
248 const got_entry = macho_file.got_entries.items[got_index];
241 const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;
242 const got_entry = macho_file.got_table.entries.items[got_index];
249243 break :blk got_entry.getName(macho_file);
250244 },
251245 };
src/link/MachO/Relocation.zig+9-7
......@@ -46,13 +46,15 @@ pub fn isResolvable(self: Relocation, macho_file: *MachO) bool {
4646}
4747
4848pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index {
49 switch (self.type) {
50 .got, .got_page, .got_pageoff => return macho_file.getGotAtomIndexForSymbol(self.target),
51 .tlv, .tlv_page, .tlv_pageoff => return macho_file.getTlvpAtomIndexForSymbol(self.target),
52 else => {},
53 }
54 if (macho_file.getStubsAtomIndexForSymbol(self.target)) |stubs_atom| return stubs_atom;
55 return macho_file.getAtomIndexForSymbol(self.target);
49 return switch (self.type) {
50 .got, .got_page, .got_pageoff => macho_file.got_table.getAtomIndex(macho_file, self.target),
51 .tlv, .tlv_page, .tlv_pageoff => macho_file.tlvp_table.getAtomIndex(macho_file, self.target),
52 .branch => if (macho_file.stubs_table.getAtomIndex(macho_file, self.target)) |index|
53 index
54 else
55 macho_file.getAtomIndexForSymbol(self.target),
56 else => macho_file.getAtomIndexForSymbol(self.target),
57 };
5658}
5759
5860pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, code: []u8) void {