diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2022-07-21 13:30:15 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2022-07-22 16:58:21 +0200 |
| commit | ca746566851aa5b12120fc76c69a0a2278a31f4e (patch) | |
| tree | c561d73fcbc22b9af9bccdbd879c4ab67cee9310 /src | |
| parent | 7345976261d4381ed48807f2003709e7ff609b0c (diff) | |
| download | zig-ca746566851aa5b12120fc76c69a0a2278a31f4e.tar.gz zig-ca746566851aa5b12120fc76c69a0a2278a31f4e.zip | |
macho: move GC code into dead_strip.zig module
Implement marking live atoms that reference other live atoms if
required by the compiler (via section attribute).
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO.zig | 255 | ||||
| -rw-r--r-- | src/link/MachO/Atom.zig | 8 | ||||
| -rw-r--r-- | src/link/MachO/Object.zig | 53 | ||||
| -rw-r--r-- | src/link/MachO/dead_strip.zig | 293 |
4 files changed, 325 insertions, 284 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 49f4c34bb4..4c344c6260 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -16,6 +16,7 @@ const meta = std.meta; const aarch64 = @import("../arch/aarch64/bits.zig"); const bind = @import("MachO/bind.zig"); const codegen = @import("../codegen.zig"); +const dead_strip = @import("MachO/dead_strip.zig"); const link = @import("../link.zig"); const llvm_backend = @import("../codegen/llvm.zig"); const target_util = @import("../target.zig"); @@ -709,7 +710,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; const stack_size = self.base.options.stack_size_override orelse 0; - const dead_strip = self.base.options.gc_sections orelse false; + const gc_sections = self.base.options.gc_sections orelse false; const id_symlink_basename = "zld.id"; @@ -741,7 +742,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) man.hash.addOptional(self.base.options.search_strategy); man.hash.addOptional(self.base.options.headerpad_size); man.hash.add(self.base.options.headerpad_max_install_names); - man.hash.add(dead_strip); + man.hash.add(gc_sections); man.hash.add(self.base.options.dead_strip_dylibs); man.hash.add(self.base.options.strip); man.hash.addListOfBytes(self.base.options.lib_dirs); @@ -1068,7 +1069,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) try argv.append("-headerpad_max_install_names"); } - if (dead_strip) { + if (gc_sections) { try argv.append("-dead_strip"); } @@ -1186,19 +1187,12 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) try self.createTentativeDefAtoms(); - if (dead_strip) { - var gc_roots = std.AutoHashMap(*Atom, void).init(gpa); - defer gc_roots.deinit(); - - for (self.objects.items) |*object, object_id| { - try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id), &gc_roots); - } + for (self.objects.items) |*object, object_id| { + try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id)); + } - try self.gcAtoms(&gc_roots); - } else { - for (self.objects.items) |*object, object_id| { - try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id), null); - } + if (gc_sections) { + try dead_strip.gcAtoms(self); } try self.pruneAndSortSections(); @@ -5504,227 +5498,6 @@ fn pruneAndSortSections(self: *MachO) !void { self.sections_order_dirty = false; } -fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void { - assert(self.base.options.gc_sections.?); - - const gpa = self.base.allocator; - - if (self.base.options.output_mode == .Exe) { - // Add entrypoint as GC root - const global = try self.getEntryPoint(); - const atom = self.getAtomForSymbol(global).?; // panic here means fatal error - _ = try gc_roots.getOrPut(atom); - } else { - assert(self.base.options.output_mode == .Lib); - // Add exports as GC roots - for (self.globals.values()) |global| { - const sym = self.getSymbol(global); - if (!sym.sect()) continue; - const atom = self.getAtomForSymbol(global) orelse { - log.debug("skipping {s}", .{self.getSymbolName(global)}); - continue; - }; - _ = try gc_roots.getOrPut(atom); - } - } - // TODO just a temp until we learn how to parse unwind records - if (self.globals.get("___gxx_personality_v0")) |global| { - if (self.getAtomForSymbol(global)) |atom| { - _ = try gc_roots.getOrPut(atom); - } - } - - var stack = std.ArrayList(*Atom).init(gpa); - defer stack.deinit(); - try stack.ensureUnusedCapacity(gc_roots.count()); - - var alive = std.AutoHashMap(*Atom, void).init(gpa); - defer alive.deinit(); - try alive.ensureUnusedCapacity(gc_roots.count()); - - log.debug("GC roots:", .{}); - var gc_roots_it = gc_roots.keyIterator(); - while (gc_roots_it.next()) |gc_root| { - self.logAtom(gc_root.*); - stack.appendAssumeCapacity(gc_root.*); - alive.putAssumeCapacity(gc_root.*, {}); - } - - while (stack.popOrNull()) |source_atom| { - for (source_atom.relocs.items) |rel| { - if (rel.getTargetAtom(self)) |target_atom| { - const gop = try alive.getOrPut(target_atom); - if (!gop.found_existing) { - log.debug(" retained ATOM(%{d}, '{s}') in object({d})", .{ - target_atom.sym_index, - target_atom.getName(self), - target_atom.file, - }); - log.debug(" referenced by ATOM(%{d}, '{s}') in object({d})", .{ - source_atom.sym_index, - source_atom.getName(self), - source_atom.file, - }); - try stack.append(target_atom); - } - } - } - } - // TODO live support - - // Any section that ends up here will be updated, that is, - // its size and alignment recalculated. - var gc_sections = std.AutoHashMap(MatchingSection, void).init(gpa); - defer gc_sections.deinit(); - - var loop: bool = true; - while (loop) { - loop = false; - - for (self.objects.items) |object| { - for (object.getSourceSymtab()) |_, source_index| { - const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue; - if (alive.contains(atom)) continue; - - const global = atom.getSymbolWithLoc(); - const sym = atom.getSymbolPtr(self); - const match = self.getMatchingSectionFromOrdinal(sym.n_sect); - - if (sym.n_desc == N_DESC_GCED) continue; - if (!sym.ext()) { - for (atom.relocs.items) |rel| { - if (rel.getTargetAtom(self)) |target_atom| { - const target_sym = target_atom.getSymbol(self); - if (target_sym.n_desc == N_DESC_GCED) break; - } - } else continue; - } - - self.logAtom(atom); - sym.n_desc = N_DESC_GCED; - self.removeAtomFromSection(atom, match); - _ = try gc_sections.put(match, {}); - - for (atom.contained.items) |sym_off| { - const inner = self.getSymbolPtr(.{ - .sym_index = sym_off.sym_index, - .file = atom.file, - }); - inner.n_desc = N_DESC_GCED; - } - - if (self.got_entries_table.contains(global)) { - const got_atom = self.getGotAtomForSymbol(global).?; - const got_sym = got_atom.getSymbolPtr(self); - got_sym.n_desc = N_DESC_GCED; - } - - if (self.stubs_table.contains(global)) { - const stubs_atom = self.getStubsAtomForSymbol(global).?; - const stubs_sym = stubs_atom.getSymbolPtr(self); - stubs_sym.n_desc = N_DESC_GCED; - } - - if (self.tlv_ptr_entries_table.contains(global)) { - const tlv_ptr_atom = self.getTlvPtrAtomForSymbol(global).?; - const tlv_ptr_sym = tlv_ptr_atom.getSymbolPtr(self); - tlv_ptr_sym.n_desc = N_DESC_GCED; - } - - loop = true; - } - } - } - - for (self.got_entries.items) |entry| { - const sym = entry.getSymbol(self); - if (sym.n_desc != N_DESC_GCED) continue; - - // TODO tombstone - const atom = entry.getAtom(self); - const match = self.getMatchingSectionFromOrdinal(sym.n_sect); - self.removeAtomFromSection(atom, match); - _ = try gc_sections.put(match, {}); - _ = self.got_entries_table.remove(entry.target); - } - - for (self.stubs.items) |entry| { - const sym = entry.getSymbol(self); - if (sym.n_desc != N_DESC_GCED) continue; - - // TODO tombstone - const atom = entry.getAtom(self); - const match = self.getMatchingSectionFromOrdinal(sym.n_sect); - self.removeAtomFromSection(atom, match); - _ = try gc_sections.put(match, {}); - _ = self.stubs_table.remove(entry.target); - } - - for (self.tlv_ptr_entries.items) |entry| { - const sym = entry.getSymbol(self); - if (sym.n_desc != N_DESC_GCED) continue; - - // TODO tombstone - const atom = entry.getAtom(self); - const match = self.getMatchingSectionFromOrdinal(sym.n_sect); - self.removeAtomFromSection(atom, match); - _ = try gc_sections.put(match, {}); - _ = self.tlv_ptr_entries_table.remove(entry.target); - } - - var gc_sections_it = gc_sections.iterator(); - while (gc_sections_it.next()) |entry| { - const match = entry.key_ptr.*; - const sect = self.getSectionPtr(match); - if (sect.size == 0) continue; // Pruning happens automatically in next step. - - sect.@"align" = 0; - sect.size = 0; - - var atom = self.atoms.get(match).?; - - while (atom.prev) |prev| { - atom = prev; - } - - while (true) { - const atom_alignment = try math.powi(u32, 2, atom.alignment); - const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment); - const padding = aligned_end_addr - sect.size; - sect.size += padding + atom.size; - sect.@"align" = @maximum(sect.@"align", atom.alignment); - - if (atom.next) |next| { - atom = next; - } else break; - } - } -} - -fn removeAtomFromSection(self: *MachO, atom: *Atom, match: MatchingSection) void { - const sect = self.getSectionPtr(match); - - // If we want to enable GC for incremental codepath, we need to take into - // account any padding that might have been left here. - sect.size -= atom.size; - - if (atom.prev) |prev| { - prev.next = atom.next; - } - if (atom.next) |next| { - next.prev = atom.prev; - } else { - const last = self.atoms.getPtr(match).?; - if (atom.prev) |prev| { - last.* = prev; - } else { - // The section will be GCed in the next step. - last.* = undefined; - sect.size = 0; - } - } -} - fn updateSectionOrdinals(self: *MachO) !void { if (!self.sections_order_dirty) return; @@ -6217,20 +5990,18 @@ fn writeDataInCode(self: *MachO) !void { for (self.objects.items) |object| { const dice = object.parseDataInCode() orelse continue; - const source_symtab = object.getSourceSymtab(); try out_dice.ensureUnusedCapacity(dice.len); for (object.managed_atoms.items) |atom| { const sym = atom.getSymbol(self); if (sym.n_desc == N_DESC_GCED) continue; - if (atom.sym_index >= source_symtab.len) continue; // synthetic, linker generated const match = self.getMatchingSectionFromOrdinal(sym.n_sect); if (match.seg != self.text_segment_cmd_index.? and match.sect != self.text_section_index.?) { continue; } - const source_sym = source_symtab[atom.sym_index]; + const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue; const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow; const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size); const base = math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset) orelse @@ -6886,16 +6657,14 @@ fn generateSymbolStabsForSymbol( ) ![]const macho.nlist_64 { const gpa = self.base.allocator; const object = self.objects.items[sym_loc.file.?]; - const source_symtab = object.getSourceSymtab(); const sym = self.getSymbol(sym_loc); const sym_name = self.getSymbolName(sym_loc); if (sym.n_strx == 0) return buf[0..0]; if (sym.n_desc == N_DESC_GCED) return buf[0..0]; if (self.symbolIsTemp(sym_loc)) return buf[0..0]; - if (sym_loc.sym_index >= source_symtab.len) return buf[0..0]; // synthetic, linker generated - const source_sym = source_symtab[sym_loc.sym_index]; + const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0]; const size: ?u64 = size: { if (source_sym.tentative()) break :size null; for (debug_info.inner.func_list.items) |func| { @@ -7353,7 +7122,7 @@ fn logAtoms(self: *MachO) void { } } -fn logAtom(self: *MachO, atom: *const Atom) void { +pub fn logAtom(self: *MachO, atom: *const Atom) void { const sym = atom.getSymbol(self); const sym_name = atom.getName(self); log.debug(" ATOM(%{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({d}) in sect({d})", .{ diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index acaeab7a88..2f60702423 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -308,7 +308,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context: if (rel.r_extern == 0) { const sect_id = @intCast(u16, rel.r_symbolnum - 1); const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: { - const sect = object.getSection(sect_id); + const sect = object.getSourceSection(sect_id); const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable; const sym_index = @intCast(u32, object.symtab.items.len); @@ -360,7 +360,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context: else mem.readIntLittle(i32, self.code.items[offset..][0..4]); if (rel.r_extern == 0) { - const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr; + const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr; addend -= @intCast(i64, target_sect_base_addr); } try self.addPtrBindingOrRebase(rel, target, context); @@ -392,7 +392,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context: else mem.readIntLittle(i32, self.code.items[offset..][0..4]); if (rel.r_extern == 0) { - const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr; + const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr; addend -= @intCast(i64, target_sect_base_addr); } try self.addPtrBindingOrRebase(rel, target, context); @@ -413,7 +413,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context: if (rel.r_extern == 0) { // Note for the future self: when r_extern == 0, we should subtract correction from the // addend. - const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr; + const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr; // We need to add base_offset, i.e., offset of this atom wrt to the source // section. Otherwise, the addend will over-/under-shoot. addend += @intCast(i64, context.base_addr + offset + 4) - diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index f6b50cd0ae..5e10c0c0a3 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -285,12 +285,7 @@ fn filterRelocs( } /// Splits object into atoms assuming one-shot linking mode. -pub fn splitIntoAtomsOneShot( - self: *Object, - macho_file: *MachO, - object_id: u32, - gc_roots: ?*std.AutoHashMap(*Atom, void), -) !void { +pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void { assert(macho_file.mode == .one_shot); const tracy = trace(@src()); @@ -338,10 +333,7 @@ pub fn splitIntoAtomsOneShot( // We only care about defined symbols, so filter every other out. const sorted_syms = sorted_all_syms.items[0..iundefsym]; - const dead_strip = macho_file.base.options.gc_sections orelse false; - const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0 and - (macho_file.base.options.optimize_mode != .Debug or dead_strip); - // const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; + const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; for (seg.sections.items) |sect, id| { const sect_id = @intCast(u8, id); @@ -417,7 +409,6 @@ pub fn splitIntoAtomsOneShot( &.{}, match, sect, - gc_roots, ); try macho_file.addAtomToSection(atom, match); } @@ -473,7 +464,6 @@ pub fn splitIntoAtomsOneShot( sorted_atom_syms.items[1..], match, sect, - gc_roots, ); if (arch == .x86_64 and addr == sect.addr) { @@ -528,7 +518,6 @@ pub fn splitIntoAtomsOneShot( filtered_syms, match, sect, - gc_roots, ); try macho_file.addAtomToSection(atom, match); } @@ -547,7 +536,6 @@ fn createAtomFromSubsection( indexes: []const SymbolAtIndex, match: MatchingSection, sect: macho.section_64, - gc_roots: ?*std.AutoHashMap(*Atom, void), ) !*Atom { const gpa = macho_file.base.allocator; const sym = self.symtab.items[sym_index]; @@ -597,21 +585,6 @@ fn createAtomFromSubsection( try self.atom_by_index_table.putNoClobber(gpa, inner_sym_index.index, atom); } - if (gc_roots) |gcr| { - const is_gc_root = blk: { - if (sect.isDontDeadStrip()) break :blk true; - switch (sect.type_()) { - macho.S_MOD_INIT_FUNC_POINTERS, - macho.S_MOD_TERM_FUNC_POINTERS, - => break :blk true, - else => break :blk false, - } - }; - if (is_gc_root) { - try gcr.putNoClobber(atom, {}); - } - } - return atom; } @@ -633,6 +606,18 @@ pub fn getSourceSymtab(self: Object) []const macho.nlist_64 { ); } +pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 { + const symtab = self.getSourceSymtab(); + if (index >= symtab.len) return null; + return symtab[index]; +} + +pub fn getSourceSection(self: Object, index: u16) macho.section_64 { + const seg = self.load_commands.items[self.segment_cmd_index.?].segment; + assert(index < seg.sections.items.len); + return seg.sections.items[index]; +} + pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry { const index = self.data_in_code_cmd_index orelse return null; const data_in_code = self.load_commands.items[index].linkedit_data; @@ -643,8 +628,8 @@ pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry { ); } -pub fn getSectionContents(self: Object, sect_id: u16) error{Overflow}![]const u8 { - const sect = self.getSection(sect_id); +pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 { + const sect = self.getSourceSection(index); const size = math.cast(usize, sect.size) orelse return error.Overflow; log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{ sect.segName(), @@ -660,12 +645,6 @@ pub fn getString(self: Object, off: u32) []const u8 { return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0); } -pub fn getSection(self: Object, n_sect: u16) macho.section_64 { - const seg = self.load_commands.items[self.segment_cmd_index.?].segment; - assert(n_sect < seg.sections.items.len); - return seg.sections.items[n_sect]; -} - pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom { return self.atom_by_index_table.get(sym_index); } diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig new file mode 100644 index 0000000000..a953e5bc19 --- /dev/null +++ b/src/link/MachO/dead_strip.zig @@ -0,0 +1,293 @@ +const std = @import("std"); +const assert = std.debug.assert; +const log = std.log.scoped(.dead_strip); +const macho = std.macho; +const math = std.math; +const mem = std.mem; + +const Allocator = mem.Allocator; +const Atom = @import("Atom.zig"); +const MachO = @import("../MachO.zig"); +const MatchingSection = MachO.MatchingSection; + +pub fn gcAtoms(macho_file: *MachO) !void { + assert(macho_file.base.options.gc_sections.?); + + const gpa = macho_file.base.allocator; + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + var roots = std.AutoHashMap(*Atom, void).init(arena); + try collectRoots(&roots, macho_file); + + var alive = std.AutoHashMap(*Atom, void).init(arena); + try mark(roots, &alive, macho_file); + + try prune(arena, alive, macho_file); +} + +fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO) void { + const sect = macho_file.getSectionPtr(match); + + // If we want to enable GC for incremental codepath, we need to take into + // account any padding that might have been left here. + sect.size -= atom.size; + + if (atom.prev) |prev| { + prev.next = atom.next; + } + if (atom.next) |next| { + next.prev = atom.prev; + } else { + const last = macho_file.atoms.getPtr(match).?; + if (atom.prev) |prev| { + last.* = prev; + } else { + // The section will be GCed in the next step. + last.* = undefined; + sect.size = 0; + } + } +} + +fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void { + const output_mode = macho_file.base.options.output_mode; + + switch (output_mode) { + .Exe => { + // Add entrypoint as GC root + const global = try macho_file.getEntryPoint(); + const atom = macho_file.getAtomForSymbol(global).?; // panic here means fatal error + _ = try roots.getOrPut(atom); + }, + else => |other| { + assert(other == .Lib); + // Add exports as GC roots + for (macho_file.globals.values()) |global| { + const sym = macho_file.getSymbol(global); + if (!sym.sect()) continue; + const atom = macho_file.getAtomForSymbol(global) orelse { + log.debug("skipping {s}", .{macho_file.getSymbolName(global)}); + continue; + }; + _ = try roots.getOrPut(atom); + log.debug("adding root", .{}); + macho_file.logAtom(atom); + } + }, + } + + // TODO just a temp until we learn how to parse unwind records + if (macho_file.globals.get("___gxx_personality_v0")) |global| { + if (macho_file.getAtomForSymbol(global)) |atom| { + _ = try roots.getOrPut(atom); + log.debug("adding root", .{}); + macho_file.logAtom(atom); + } + } + + for (macho_file.objects.items) |object| { + for (object.managed_atoms.items) |atom| { + const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue; + if (source_sym.tentative()) continue; + const source_sect = object.getSourceSection(source_sym.n_sect - 1); + const is_gc_root = blk: { + if (source_sect.isDontDeadStrip()) break :blk true; + switch (source_sect.type_()) { + macho.S_MOD_INIT_FUNC_POINTERS, + macho.S_MOD_TERM_FUNC_POINTERS, + => break :blk true, + else => break :blk false, + } + }; + if (is_gc_root) { + try roots.putNoClobber(atom, {}); + log.debug("adding root", .{}); + macho_file.logAtom(atom); + } + } + } +} + +fn markLive(atom: *Atom, alive: *std.AutoHashMap(*Atom, void), macho_file: *MachO) anyerror!void { + const gop = try alive.getOrPut(atom); + if (gop.found_existing) return; + + log.debug("marking live", .{}); + macho_file.logAtom(atom); + + for (atom.relocs.items) |rel| { + const target_atom = rel.getTargetAtom(macho_file) orelse continue; + try markLive(target_atom, alive, macho_file); + } +} + +fn refersLive(atom: *Atom, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) bool { + for (atom.relocs.items) |rel| { + const target_atom = rel.getTargetAtom(macho_file) orelse continue; + if (alive.contains(target_atom)) return true; + } + return false; +} + +fn refersDead(atom: *Atom, macho_file: *MachO) bool { + for (atom.relocs.items) |rel| { + const target_atom = rel.getTargetAtom(macho_file) orelse continue; + const target_sym = target_atom.getSymbol(macho_file); + if (target_sym.n_desc == MachO.N_DESC_GCED) return true; + } + return false; +} + +fn mark( + roots: std.AutoHashMap(*Atom, void), + alive: *std.AutoHashMap(*Atom, void), + macho_file: *MachO, +) !void { + try alive.ensureUnusedCapacity(roots.count()); + + var it = roots.keyIterator(); + while (it.next()) |root| { + try markLive(root.*, alive, macho_file); + } + + var loop: bool = true; + while (loop) { + loop = false; + + for (macho_file.objects.items) |object| { + for (object.managed_atoms.items) |atom| { + if (alive.contains(atom)) continue; + const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue; + if (source_sym.tentative()) continue; + const source_sect = object.getSourceSection(source_sym.n_sect - 1); + if (source_sect.isDontDeadStripIfReferencesLive() and refersLive(atom, alive.*, macho_file)) { + try markLive(atom, alive, macho_file); + loop = true; + } + } + } + } +} + +fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) !void { + // Any section that ends up here will be updated, that is, + // its size and alignment recalculated. + var gc_sections = std.AutoHashMap(MatchingSection, void).init(arena); + var loop: bool = true; + while (loop) { + loop = false; + + for (macho_file.objects.items) |object| { + for (object.getSourceSymtab()) |_, source_index| { + const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue; + if (alive.contains(atom)) continue; + + const global = atom.getSymbolWithLoc(); + const sym = atom.getSymbolPtr(macho_file); + const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect); + + if (sym.n_desc == MachO.N_DESC_GCED) continue; + if (!sym.ext() and !refersDead(atom, macho_file)) continue; + + macho_file.logAtom(atom); + sym.n_desc = MachO.N_DESC_GCED; + removeAtomFromSection(atom, match, macho_file); + _ = try gc_sections.put(match, {}); + + for (atom.contained.items) |sym_off| { + const inner = macho_file.getSymbolPtr(.{ + .sym_index = sym_off.sym_index, + .file = atom.file, + }); + inner.n_desc = MachO.N_DESC_GCED; + } + + if (macho_file.got_entries_table.contains(global)) { + const got_atom = macho_file.getGotAtomForSymbol(global).?; + const got_sym = got_atom.getSymbolPtr(macho_file); + got_sym.n_desc = MachO.N_DESC_GCED; + } + + if (macho_file.stubs_table.contains(global)) { + const stubs_atom = macho_file.getStubsAtomForSymbol(global).?; + const stubs_sym = stubs_atom.getSymbolPtr(macho_file); + stubs_sym.n_desc = MachO.N_DESC_GCED; + } + + if (macho_file.tlv_ptr_entries_table.contains(global)) { + const tlv_ptr_atom = macho_file.getTlvPtrAtomForSymbol(global).?; + const tlv_ptr_sym = tlv_ptr_atom.getSymbolPtr(macho_file); + tlv_ptr_sym.n_desc = MachO.N_DESC_GCED; + } + + loop = true; + } + } + } + + for (macho_file.got_entries.items) |entry| { + const sym = entry.getSymbol(macho_file); + if (sym.n_desc != MachO.N_DESC_GCED) continue; + + // TODO tombstone + const atom = entry.getAtom(macho_file); + const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect); + removeAtomFromSection(atom, match, macho_file); + _ = try gc_sections.put(match, {}); + _ = macho_file.got_entries_table.remove(entry.target); + } + + for (macho_file.stubs.items) |entry| { + const sym = entry.getSymbol(macho_file); + if (sym.n_desc != MachO.N_DESC_GCED) continue; + + // TODO tombstone + const atom = entry.getAtom(macho_file); + const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect); + removeAtomFromSection(atom, match, macho_file); + _ = try gc_sections.put(match, {}); + _ = macho_file.stubs_table.remove(entry.target); + } + + for (macho_file.tlv_ptr_entries.items) |entry| { + const sym = entry.getSymbol(macho_file); + if (sym.n_desc != MachO.N_DESC_GCED) continue; + + // TODO tombstone + const atom = entry.getAtom(macho_file); + const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect); + removeAtomFromSection(atom, match, macho_file); + _ = try gc_sections.put(match, {}); + _ = macho_file.tlv_ptr_entries_table.remove(entry.target); + } + + var gc_sections_it = gc_sections.iterator(); + while (gc_sections_it.next()) |entry| { + const match = entry.key_ptr.*; + const sect = macho_file.getSectionPtr(match); + if (sect.size == 0) continue; // Pruning happens automatically in next step. + + sect.@"align" = 0; + sect.size = 0; + + var atom = macho_file.atoms.get(match).?; + + while (atom.prev) |prev| { + atom = prev; + } + + while (true) { + const atom_alignment = try math.powi(u32, 2, atom.alignment); + const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment); + const padding = aligned_end_addr - sect.size; + sect.size += padding + atom.size; + sect.@"align" = @maximum(sect.@"align", atom.alignment); + + if (atom.next) |next| { + atom = next; + } else break; + } + } +} |
