authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-21 13:30:15+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-22 16:58:21+02:00
logca746566851aa5b12120fc76c69a0a2278a31f4e
treec561d73fcbc22b9af9bccdbd879c4ab67cee9310
parent7345976261d4381ed48807f2003709e7ff609b0c

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).

5 files changed, 327 insertions(+), 284 deletions(-)

CMakeLists.txt+2
...@@ -758,10 +758,12 @@ set(ZIG_STAGE2_SOURCES...@@ -758,10 +758,12 @@ set(ZIG_STAGE2_SOURCES
758 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"758 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
759 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"759 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
760 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"760 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
761 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
761 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"762 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"
762 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"763 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
763 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"764 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
764 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"765 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
766 "${CMAKE_SOURCE_DIR}/src/link/strtab.zig"
765 "${CMAKE_SOURCE_DIR}/src/link/tapi.zig"767 "${CMAKE_SOURCE_DIR}/src/link/tapi.zig"
766 "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig"768 "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig"
767 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig"769 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig"
src/link/MachO.zig+12-243
...@@ -16,6 +16,7 @@ const meta = std.meta;...@@ -16,6 +16,7 @@ const meta = std.meta;
16const aarch64 = @import("../arch/aarch64/bits.zig");16const aarch64 = @import("../arch/aarch64/bits.zig");
17const bind = @import("MachO/bind.zig");17const bind = @import("MachO/bind.zig");
18const codegen = @import("../codegen.zig");18const codegen = @import("../codegen.zig");
19const dead_strip = @import("MachO/dead_strip.zig");
19const link = @import("../link.zig");20const link = @import("../link.zig");
20const llvm_backend = @import("../codegen/llvm.zig");21const llvm_backend = @import("../codegen/llvm.zig");
21const target_util = @import("../target.zig");22const target_util = @import("../target.zig");
...@@ -709,7 +710,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -709,7 +710,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
709 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;710 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
710 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;711 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
711 const stack_size = self.base.options.stack_size_override orelse 0;712 const stack_size = self.base.options.stack_size_override orelse 0;
712 const dead_strip = self.base.options.gc_sections orelse false;713 const gc_sections = self.base.options.gc_sections orelse false;
713714
714 const id_symlink_basename = "zld.id";715 const id_symlink_basename = "zld.id";
715716
...@@ -741,7 +742,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -741,7 +742,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
741 man.hash.addOptional(self.base.options.search_strategy);742 man.hash.addOptional(self.base.options.search_strategy);
742 man.hash.addOptional(self.base.options.headerpad_size);743 man.hash.addOptional(self.base.options.headerpad_size);
743 man.hash.add(self.base.options.headerpad_max_install_names);744 man.hash.add(self.base.options.headerpad_max_install_names);
744 man.hash.add(dead_strip);745 man.hash.add(gc_sections);
745 man.hash.add(self.base.options.dead_strip_dylibs);746 man.hash.add(self.base.options.dead_strip_dylibs);
746 man.hash.add(self.base.options.strip);747 man.hash.add(self.base.options.strip);
747 man.hash.addListOfBytes(self.base.options.lib_dirs);748 man.hash.addListOfBytes(self.base.options.lib_dirs);
...@@ -1068,7 +1069,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1068,7 +1069,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1068 try argv.append("-headerpad_max_install_names");1069 try argv.append("-headerpad_max_install_names");
1069 }1070 }
10701071
1071 if (dead_strip) {1072 if (gc_sections) {
1072 try argv.append("-dead_strip");1073 try argv.append("-dead_strip");
1073 }1074 }
10741075
...@@ -1186,19 +1187,12 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1186,19 +1187,12 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
11861187
1187 try self.createTentativeDefAtoms();1188 try self.createTentativeDefAtoms();
11881189
1189 if (dead_strip) {1190 for (self.objects.items) |*object, object_id| {
1190 var gc_roots = std.AutoHashMap(*Atom, void).init(gpa);1191 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id));
1191 defer gc_roots.deinit();1192 }
1192
1193 for (self.objects.items) |*object, object_id| {
1194 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id), &gc_roots);
1195 }
11961193
1197 try self.gcAtoms(&gc_roots);1194 if (gc_sections) {
1198 } else {1195 try dead_strip.gcAtoms(self);
1199 for (self.objects.items) |*object, object_id| {
1200 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id), null);
1201 }
1202 }1196 }
12031197
1204 try self.pruneAndSortSections();1198 try self.pruneAndSortSections();
...@@ -5504,227 +5498,6 @@ fn pruneAndSortSections(self: *MachO) !void {...@@ -5504,227 +5498,6 @@ fn pruneAndSortSections(self: *MachO) !void {
5504 self.sections_order_dirty = false;5498 self.sections_order_dirty = false;
5505}5499}
55065500
5507fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void {
5508 assert(self.base.options.gc_sections.?);
5509
5510 const gpa = self.base.allocator;
5511
5512 if (self.base.options.output_mode == .Exe) {
5513 // Add entrypoint as GC root
5514 const global = try self.getEntryPoint();
5515 const atom = self.getAtomForSymbol(global).?; // panic here means fatal error
5516 _ = try gc_roots.getOrPut(atom);
5517 } else {
5518 assert(self.base.options.output_mode == .Lib);
5519 // Add exports as GC roots
5520 for (self.globals.values()) |global| {
5521 const sym = self.getSymbol(global);
5522 if (!sym.sect()) continue;
5523 const atom = self.getAtomForSymbol(global) orelse {
5524 log.debug("skipping {s}", .{self.getSymbolName(global)});
5525 continue;
5526 };
5527 _ = try gc_roots.getOrPut(atom);
5528 }
5529 }
5530 // TODO just a temp until we learn how to parse unwind records
5531 if (self.globals.get("___gxx_personality_v0")) |global| {
5532 if (self.getAtomForSymbol(global)) |atom| {
5533 _ = try gc_roots.getOrPut(atom);
5534 }
5535 }
5536
5537 var stack = std.ArrayList(*Atom).init(gpa);
5538 defer stack.deinit();
5539 try stack.ensureUnusedCapacity(gc_roots.count());
5540
5541 var alive = std.AutoHashMap(*Atom, void).init(gpa);
5542 defer alive.deinit();
5543 try alive.ensureUnusedCapacity(gc_roots.count());
5544
5545 log.debug("GC roots:", .{});
5546 var gc_roots_it = gc_roots.keyIterator();
5547 while (gc_roots_it.next()) |gc_root| {
5548 self.logAtom(gc_root.*);
5549 stack.appendAssumeCapacity(gc_root.*);
5550 alive.putAssumeCapacity(gc_root.*, {});
5551 }
5552
5553 while (stack.popOrNull()) |source_atom| {
5554 for (source_atom.relocs.items) |rel| {
5555 if (rel.getTargetAtom(self)) |target_atom| {
5556 const gop = try alive.getOrPut(target_atom);
5557 if (!gop.found_existing) {
5558 log.debug(" retained ATOM(%{d}, '{s}') in object({d})", .{
5559 target_atom.sym_index,
5560 target_atom.getName(self),
5561 target_atom.file,
5562 });
5563 log.debug(" referenced by ATOM(%{d}, '{s}') in object({d})", .{
5564 source_atom.sym_index,
5565 source_atom.getName(self),
5566 source_atom.file,
5567 });
5568 try stack.append(target_atom);
5569 }
5570 }
5571 }
5572 }
5573 // TODO live support
5574
5575 // Any section that ends up here will be updated, that is,
5576 // its size and alignment recalculated.
5577 var gc_sections = std.AutoHashMap(MatchingSection, void).init(gpa);
5578 defer gc_sections.deinit();
5579
5580 var loop: bool = true;
5581 while (loop) {
5582 loop = false;
5583
5584 for (self.objects.items) |object| {
5585 for (object.getSourceSymtab()) |_, source_index| {
5586 const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;
5587 if (alive.contains(atom)) continue;
5588
5589 const global = atom.getSymbolWithLoc();
5590 const sym = atom.getSymbolPtr(self);
5591 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
5592
5593 if (sym.n_desc == N_DESC_GCED) continue;
5594 if (!sym.ext()) {
5595 for (atom.relocs.items) |rel| {
5596 if (rel.getTargetAtom(self)) |target_atom| {
5597 const target_sym = target_atom.getSymbol(self);
5598 if (target_sym.n_desc == N_DESC_GCED) break;
5599 }
5600 } else continue;
5601 }
5602
5603 self.logAtom(atom);
5604 sym.n_desc = N_DESC_GCED;
5605 self.removeAtomFromSection(atom, match);
5606 _ = try gc_sections.put(match, {});
5607
5608 for (atom.contained.items) |sym_off| {
5609 const inner = self.getSymbolPtr(.{
5610 .sym_index = sym_off.sym_index,
5611 .file = atom.file,
5612 });
5613 inner.n_desc = N_DESC_GCED;
5614 }
5615
5616 if (self.got_entries_table.contains(global)) {
5617 const got_atom = self.getGotAtomForSymbol(global).?;
5618 const got_sym = got_atom.getSymbolPtr(self);
5619 got_sym.n_desc = N_DESC_GCED;
5620 }
5621
5622 if (self.stubs_table.contains(global)) {
5623 const stubs_atom = self.getStubsAtomForSymbol(global).?;
5624 const stubs_sym = stubs_atom.getSymbolPtr(self);
5625 stubs_sym.n_desc = N_DESC_GCED;
5626 }
5627
5628 if (self.tlv_ptr_entries_table.contains(global)) {
5629 const tlv_ptr_atom = self.getTlvPtrAtomForSymbol(global).?;
5630 const tlv_ptr_sym = tlv_ptr_atom.getSymbolPtr(self);
5631 tlv_ptr_sym.n_desc = N_DESC_GCED;
5632 }
5633
5634 loop = true;
5635 }
5636 }
5637 }
5638
5639 for (self.got_entries.items) |entry| {
5640 const sym = entry.getSymbol(self);
5641 if (sym.n_desc != N_DESC_GCED) continue;
5642
5643 // TODO tombstone
5644 const atom = entry.getAtom(self);
5645 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
5646 self.removeAtomFromSection(atom, match);
5647 _ = try gc_sections.put(match, {});
5648 _ = self.got_entries_table.remove(entry.target);
5649 }
5650
5651 for (self.stubs.items) |entry| {
5652 const sym = entry.getSymbol(self);
5653 if (sym.n_desc != N_DESC_GCED) continue;
5654
5655 // TODO tombstone
5656 const atom = entry.getAtom(self);
5657 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
5658 self.removeAtomFromSection(atom, match);
5659 _ = try gc_sections.put(match, {});
5660 _ = self.stubs_table.remove(entry.target);
5661 }
5662
5663 for (self.tlv_ptr_entries.items) |entry| {
5664 const sym = entry.getSymbol(self);
5665 if (sym.n_desc != N_DESC_GCED) continue;
5666
5667 // TODO tombstone
5668 const atom = entry.getAtom(self);
5669 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
5670 self.removeAtomFromSection(atom, match);
5671 _ = try gc_sections.put(match, {});
5672 _ = self.tlv_ptr_entries_table.remove(entry.target);
5673 }
5674
5675 var gc_sections_it = gc_sections.iterator();
5676 while (gc_sections_it.next()) |entry| {
5677 const match = entry.key_ptr.*;
5678 const sect = self.getSectionPtr(match);
5679 if (sect.size == 0) continue; // Pruning happens automatically in next step.
5680
5681 sect.@"align" = 0;
5682 sect.size = 0;
5683
5684 var atom = self.atoms.get(match).?;
5685
5686 while (atom.prev) |prev| {
5687 atom = prev;
5688 }
5689
5690 while (true) {
5691 const atom_alignment = try math.powi(u32, 2, atom.alignment);
5692 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);
5693 const padding = aligned_end_addr - sect.size;
5694 sect.size += padding + atom.size;
5695 sect.@"align" = @maximum(sect.@"align", atom.alignment);
5696
5697 if (atom.next) |next| {
5698 atom = next;
5699 } else break;
5700 }
5701 }
5702}
5703
5704fn removeAtomFromSection(self: *MachO, atom: *Atom, match: MatchingSection) void {
5705 const sect = self.getSectionPtr(match);
5706
5707 // If we want to enable GC for incremental codepath, we need to take into
5708 // account any padding that might have been left here.
5709 sect.size -= atom.size;
5710
5711 if (atom.prev) |prev| {
5712 prev.next = atom.next;
5713 }
5714 if (atom.next) |next| {
5715 next.prev = atom.prev;
5716 } else {
5717 const last = self.atoms.getPtr(match).?;
5718 if (atom.prev) |prev| {
5719 last.* = prev;
5720 } else {
5721 // The section will be GCed in the next step.
5722 last.* = undefined;
5723 sect.size = 0;
5724 }
5725 }
5726}
5727
5728fn updateSectionOrdinals(self: *MachO) !void {5501fn updateSectionOrdinals(self: *MachO) !void {
5729 if (!self.sections_order_dirty) return;5502 if (!self.sections_order_dirty) return;
57305503
...@@ -6217,20 +5990,18 @@ fn writeDataInCode(self: *MachO) !void {...@@ -6217,20 +5990,18 @@ fn writeDataInCode(self: *MachO) !void {
62175990
6218 for (self.objects.items) |object| {5991 for (self.objects.items) |object| {
6219 const dice = object.parseDataInCode() orelse continue;5992 const dice = object.parseDataInCode() orelse continue;
6220 const source_symtab = object.getSourceSymtab();
6221 try out_dice.ensureUnusedCapacity(dice.len);5993 try out_dice.ensureUnusedCapacity(dice.len);
62225994
6223 for (object.managed_atoms.items) |atom| {5995 for (object.managed_atoms.items) |atom| {
6224 const sym = atom.getSymbol(self);5996 const sym = atom.getSymbol(self);
6225 if (sym.n_desc == N_DESC_GCED) continue;5997 if (sym.n_desc == N_DESC_GCED) continue;
6226 if (atom.sym_index >= source_symtab.len) continue; // synthetic, linker generated
62275998
6228 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);5999 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
6229 if (match.seg != self.text_segment_cmd_index.? and match.sect != self.text_section_index.?) {6000 if (match.seg != self.text_segment_cmd_index.? and match.sect != self.text_section_index.?) {
6230 continue;6001 continue;
6231 }6002 }
62326003
6233 const source_sym = source_symtab[atom.sym_index];6004 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
6234 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;6005 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
6235 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);6006 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
6236 const base = math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset) orelse6007 const base = math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset) orelse
...@@ -6886,16 +6657,14 @@ fn generateSymbolStabsForSymbol(...@@ -6886,16 +6657,14 @@ fn generateSymbolStabsForSymbol(
6886) ![]const macho.nlist_64 {6657) ![]const macho.nlist_64 {
6887 const gpa = self.base.allocator;6658 const gpa = self.base.allocator;
6888 const object = self.objects.items[sym_loc.file.?];6659 const object = self.objects.items[sym_loc.file.?];
6889 const source_symtab = object.getSourceSymtab();
6890 const sym = self.getSymbol(sym_loc);6660 const sym = self.getSymbol(sym_loc);
6891 const sym_name = self.getSymbolName(sym_loc);6661 const sym_name = self.getSymbolName(sym_loc);
68926662
6893 if (sym.n_strx == 0) return buf[0..0];6663 if (sym.n_strx == 0) return buf[0..0];
6894 if (sym.n_desc == N_DESC_GCED) return buf[0..0];6664 if (sym.n_desc == N_DESC_GCED) return buf[0..0];
6895 if (self.symbolIsTemp(sym_loc)) return buf[0..0];6665 if (self.symbolIsTemp(sym_loc)) return buf[0..0];
6896 if (sym_loc.sym_index >= source_symtab.len) return buf[0..0]; // synthetic, linker generated
68976666
6898 const source_sym = source_symtab[sym_loc.sym_index];6667 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
6899 const size: ?u64 = size: {6668 const size: ?u64 = size: {
6900 if (source_sym.tentative()) break :size null;6669 if (source_sym.tentative()) break :size null;
6901 for (debug_info.inner.func_list.items) |func| {6670 for (debug_info.inner.func_list.items) |func| {
...@@ -7353,7 +7122,7 @@ fn logAtoms(self: *MachO) void {...@@ -7353,7 +7122,7 @@ fn logAtoms(self: *MachO) void {
7353 }7122 }
7354}7123}
73557124
7356fn logAtom(self: *MachO, atom: *const Atom) void {7125pub fn logAtom(self: *MachO, atom: *const Atom) void {
7357 const sym = atom.getSymbol(self);7126 const sym = atom.getSymbol(self);
7358 const sym_name = atom.getName(self);7127 const sym_name = atom.getName(self);
7359 log.debug(" ATOM(%{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({d}) in sect({d})", .{7128 log.debug(" ATOM(%{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({d}) in sect({d})", .{
src/link/MachO/Atom.zig+4-4
...@@ -308,7 +308,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -308,7 +308,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
308 if (rel.r_extern == 0) {308 if (rel.r_extern == 0) {
309 const sect_id = @intCast(u16, rel.r_symbolnum - 1);309 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
310 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {310 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
311 const sect = object.getSection(sect_id);311 const sect = object.getSourceSection(sect_id);
312 const match = (try context.macho_file.getMatchingSection(sect)) orelse312 const match = (try context.macho_file.getMatchingSection(sect)) orelse
313 unreachable;313 unreachable;
314 const sym_index = @intCast(u32, object.symtab.items.len);314 const sym_index = @intCast(u32, object.symtab.items.len);
...@@ -360,7 +360,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -360,7 +360,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
360 else360 else
361 mem.readIntLittle(i32, self.code.items[offset..][0..4]);361 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
362 if (rel.r_extern == 0) {362 if (rel.r_extern == 0) {
363 const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr;363 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
364 addend -= @intCast(i64, target_sect_base_addr);364 addend -= @intCast(i64, target_sect_base_addr);
365 }365 }
366 try self.addPtrBindingOrRebase(rel, target, context);366 try self.addPtrBindingOrRebase(rel, target, context);
...@@ -392,7 +392,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -392,7 +392,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
392 else392 else
393 mem.readIntLittle(i32, self.code.items[offset..][0..4]);393 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
394 if (rel.r_extern == 0) {394 if (rel.r_extern == 0) {
395 const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr;395 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
396 addend -= @intCast(i64, target_sect_base_addr);396 addend -= @intCast(i64, target_sect_base_addr);
397 }397 }
398 try self.addPtrBindingOrRebase(rel, target, context);398 try self.addPtrBindingOrRebase(rel, target, context);
...@@ -413,7 +413,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -413,7 +413,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
413 if (rel.r_extern == 0) {413 if (rel.r_extern == 0) {
414 // Note for the future self: when r_extern == 0, we should subtract correction from the414 // Note for the future self: when r_extern == 0, we should subtract correction from the
415 // addend.415 // addend.
416 const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr;416 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
417 // We need to add base_offset, i.e., offset of this atom wrt to the source417 // We need to add base_offset, i.e., offset of this atom wrt to the source
418 // section. Otherwise, the addend will over-/under-shoot.418 // section. Otherwise, the addend will over-/under-shoot.
419 addend += @intCast(i64, context.base_addr + offset + 4) -419 addend += @intCast(i64, context.base_addr + offset + 4) -
src/link/MachO/Object.zig+16-37
...@@ -285,12 +285,7 @@ fn filterRelocs(...@@ -285,12 +285,7 @@ fn filterRelocs(
285}285}
286286
287/// Splits object into atoms assuming one-shot linking mode.287/// Splits object into atoms assuming one-shot linking mode.
288pub fn splitIntoAtomsOneShot(288pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {
289 self: *Object,
290 macho_file: *MachO,
291 object_id: u32,
292 gc_roots: ?*std.AutoHashMap(*Atom, void),
293) !void {
294 assert(macho_file.mode == .one_shot);289 assert(macho_file.mode == .one_shot);
295290
296 const tracy = trace(@src());291 const tracy = trace(@src());
...@@ -338,10 +333,7 @@ pub fn splitIntoAtomsOneShot(...@@ -338,10 +333,7 @@ pub fn splitIntoAtomsOneShot(
338333
339 // We only care about defined symbols, so filter every other out.334 // We only care about defined symbols, so filter every other out.
340 const sorted_syms = sorted_all_syms.items[0..iundefsym];335 const sorted_syms = sorted_all_syms.items[0..iundefsym];
341 const dead_strip = macho_file.base.options.gc_sections orelse false;336 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
342 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0 and
343 (macho_file.base.options.optimize_mode != .Debug or dead_strip);
344 // const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
345337
346 for (seg.sections.items) |sect, id| {338 for (seg.sections.items) |sect, id| {
347 const sect_id = @intCast(u8, id);339 const sect_id = @intCast(u8, id);
...@@ -417,7 +409,6 @@ pub fn splitIntoAtomsOneShot(...@@ -417,7 +409,6 @@ pub fn splitIntoAtomsOneShot(
417 &.{},409 &.{},
418 match,410 match,
419 sect,411 sect,
420 gc_roots,
421 );412 );
422 try macho_file.addAtomToSection(atom, match);413 try macho_file.addAtomToSection(atom, match);
423 }414 }
...@@ -473,7 +464,6 @@ pub fn splitIntoAtomsOneShot(...@@ -473,7 +464,6 @@ pub fn splitIntoAtomsOneShot(
473 sorted_atom_syms.items[1..],464 sorted_atom_syms.items[1..],
474 match,465 match,
475 sect,466 sect,
476 gc_roots,
477 );467 );
478468
479 if (arch == .x86_64 and addr == sect.addr) {469 if (arch == .x86_64 and addr == sect.addr) {
...@@ -528,7 +518,6 @@ pub fn splitIntoAtomsOneShot(...@@ -528,7 +518,6 @@ pub fn splitIntoAtomsOneShot(
528 filtered_syms,518 filtered_syms,
529 match,519 match,
530 sect,520 sect,
531 gc_roots,
532 );521 );
533 try macho_file.addAtomToSection(atom, match);522 try macho_file.addAtomToSection(atom, match);
534 }523 }
...@@ -547,7 +536,6 @@ fn createAtomFromSubsection(...@@ -547,7 +536,6 @@ fn createAtomFromSubsection(
547 indexes: []const SymbolAtIndex,536 indexes: []const SymbolAtIndex,
548 match: MatchingSection,537 match: MatchingSection,
549 sect: macho.section_64,538 sect: macho.section_64,
550 gc_roots: ?*std.AutoHashMap(*Atom, void),
551) !*Atom {539) !*Atom {
552 const gpa = macho_file.base.allocator;540 const gpa = macho_file.base.allocator;
553 const sym = self.symtab.items[sym_index];541 const sym = self.symtab.items[sym_index];
...@@ -597,21 +585,6 @@ fn createAtomFromSubsection(...@@ -597,21 +585,6 @@ fn createAtomFromSubsection(
597 try self.atom_by_index_table.putNoClobber(gpa, inner_sym_index.index, atom);585 try self.atom_by_index_table.putNoClobber(gpa, inner_sym_index.index, atom);
598 }586 }
599587
600 if (gc_roots) |gcr| {
601 const is_gc_root = blk: {
602 if (sect.isDontDeadStrip()) break :blk true;
603 switch (sect.type_()) {
604 macho.S_MOD_INIT_FUNC_POINTERS,
605 macho.S_MOD_TERM_FUNC_POINTERS,
606 => break :blk true,
607 else => break :blk false,
608 }
609 };
610 if (is_gc_root) {
611 try gcr.putNoClobber(atom, {});
612 }
613 }
614
615 return atom;588 return atom;
616}589}
617590
...@@ -633,6 +606,18 @@ pub fn getSourceSymtab(self: Object) []const macho.nlist_64 {...@@ -633,6 +606,18 @@ pub fn getSourceSymtab(self: Object) []const macho.nlist_64 {
633 );606 );
634}607}
635608
609pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
610 const symtab = self.getSourceSymtab();
611 if (index >= symtab.len) return null;
612 return symtab[index];
613}
614
615pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
616 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
617 assert(index < seg.sections.items.len);
618 return seg.sections.items[index];
619}
620
636pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {621pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
637 const index = self.data_in_code_cmd_index orelse return null;622 const index = self.data_in_code_cmd_index orelse return null;
638 const data_in_code = self.load_commands.items[index].linkedit_data;623 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 {...@@ -643,8 +628,8 @@ pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
643 );628 );
644}629}
645630
646pub fn getSectionContents(self: Object, sect_id: u16) error{Overflow}![]const u8 {631pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
647 const sect = self.getSection(sect_id);632 const sect = self.getSourceSection(index);
648 const size = math.cast(usize, sect.size) orelse return error.Overflow;633 const size = math.cast(usize, sect.size) orelse return error.Overflow;
649 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{634 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{
650 sect.segName(),635 sect.segName(),
...@@ -660,12 +645,6 @@ pub fn getString(self: Object, off: u32) []const u8 {...@@ -660,12 +645,6 @@ pub fn getString(self: Object, off: u32) []const u8 {
660 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0);645 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0);
661}646}
662647
663pub fn getSection(self: Object, n_sect: u16) macho.section_64 {
664 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
665 assert(n_sect < seg.sections.items.len);
666 return seg.sections.items[n_sect];
667}
668
669pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {648pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {
670 return self.atom_by_index_table.get(sym_index);649 return self.atom_by_index_table.get(sym_index);
671}650}
src/link/MachO/dead_strip.zig created+293
...@@ -0,0 +1,293 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const log = std.log.scoped(.dead_strip);
4const macho = std.macho;
5const math = std.math;
6const mem = std.mem;
7
8const Allocator = mem.Allocator;
9const Atom = @import("Atom.zig");
10const MachO = @import("../MachO.zig");
11const MatchingSection = MachO.MatchingSection;
12
13pub fn gcAtoms(macho_file: *MachO) !void {
14 assert(macho_file.base.options.gc_sections.?);
15
16 const gpa = macho_file.base.allocator;
17 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
18 defer arena_allocator.deinit();
19 const arena = arena_allocator.allocator();
20
21 var roots = std.AutoHashMap(*Atom, void).init(arena);
22 try collectRoots(&roots, macho_file);
23
24 var alive = std.AutoHashMap(*Atom, void).init(arena);
25 try mark(roots, &alive, macho_file);
26
27 try prune(arena, alive, macho_file);
28}
29
30fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO) void {
31 const sect = macho_file.getSectionPtr(match);
32
33 // If we want to enable GC for incremental codepath, we need to take into
34 // account any padding that might have been left here.
35 sect.size -= atom.size;
36
37 if (atom.prev) |prev| {
38 prev.next = atom.next;
39 }
40 if (atom.next) |next| {
41 next.prev = atom.prev;
42 } else {
43 const last = macho_file.atoms.getPtr(match).?;
44 if (atom.prev) |prev| {
45 last.* = prev;
46 } else {
47 // The section will be GCed in the next step.
48 last.* = undefined;
49 sect.size = 0;
50 }
51 }
52}
53
54fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
55 const output_mode = macho_file.base.options.output_mode;
56
57 switch (output_mode) {
58 .Exe => {
59 // Add entrypoint as GC root
60 const global = try macho_file.getEntryPoint();
61 const atom = macho_file.getAtomForSymbol(global).?; // panic here means fatal error
62 _ = try roots.getOrPut(atom);
63 },
64 else => |other| {
65 assert(other == .Lib);
66 // Add exports as GC roots
67 for (macho_file.globals.values()) |global| {
68 const sym = macho_file.getSymbol(global);
69 if (!sym.sect()) continue;
70 const atom = macho_file.getAtomForSymbol(global) orelse {
71 log.debug("skipping {s}", .{macho_file.getSymbolName(global)});
72 continue;
73 };
74 _ = try roots.getOrPut(atom);
75 log.debug("adding root", .{});
76 macho_file.logAtom(atom);
77 }
78 },
79 }
80
81 // TODO just a temp until we learn how to parse unwind records
82 if (macho_file.globals.get("___gxx_personality_v0")) |global| {
83 if (macho_file.getAtomForSymbol(global)) |atom| {
84 _ = try roots.getOrPut(atom);
85 log.debug("adding root", .{});
86 macho_file.logAtom(atom);
87 }
88 }
89
90 for (macho_file.objects.items) |object| {
91 for (object.managed_atoms.items) |atom| {
92 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
93 if (source_sym.tentative()) continue;
94 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
95 const is_gc_root = blk: {
96 if (source_sect.isDontDeadStrip()) break :blk true;
97 switch (source_sect.type_()) {
98 macho.S_MOD_INIT_FUNC_POINTERS,
99 macho.S_MOD_TERM_FUNC_POINTERS,
100 => break :blk true,
101 else => break :blk false,
102 }
103 };
104 if (is_gc_root) {
105 try roots.putNoClobber(atom, {});
106 log.debug("adding root", .{});
107 macho_file.logAtom(atom);
108 }
109 }
110 }
111}
112
113fn markLive(atom: *Atom, alive: *std.AutoHashMap(*Atom, void), macho_file: *MachO) anyerror!void {
114 const gop = try alive.getOrPut(atom);
115 if (gop.found_existing) return;
116
117 log.debug("marking live", .{});
118 macho_file.logAtom(atom);
119
120 for (atom.relocs.items) |rel| {
121 const target_atom = rel.getTargetAtom(macho_file) orelse continue;
122 try markLive(target_atom, alive, macho_file);
123 }
124}
125
126fn refersLive(atom: *Atom, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) bool {
127 for (atom.relocs.items) |rel| {
128 const target_atom = rel.getTargetAtom(macho_file) orelse continue;
129 if (alive.contains(target_atom)) return true;
130 }
131 return false;
132}
133
134fn refersDead(atom: *Atom, macho_file: *MachO) bool {
135 for (atom.relocs.items) |rel| {
136 const target_atom = rel.getTargetAtom(macho_file) orelse continue;
137 const target_sym = target_atom.getSymbol(macho_file);
138 if (target_sym.n_desc == MachO.N_DESC_GCED) return true;
139 }
140 return false;
141}
142
143fn mark(
144 roots: std.AutoHashMap(*Atom, void),
145 alive: *std.AutoHashMap(*Atom, void),
146 macho_file: *MachO,
147) !void {
148 try alive.ensureUnusedCapacity(roots.count());
149
150 var it = roots.keyIterator();
151 while (it.next()) |root| {
152 try markLive(root.*, alive, macho_file);
153 }
154
155 var loop: bool = true;
156 while (loop) {
157 loop = false;
158
159 for (macho_file.objects.items) |object| {
160 for (object.managed_atoms.items) |atom| {
161 if (alive.contains(atom)) continue;
162 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
163 if (source_sym.tentative()) continue;
164 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
165 if (source_sect.isDontDeadStripIfReferencesLive() and refersLive(atom, alive.*, macho_file)) {
166 try markLive(atom, alive, macho_file);
167 loop = true;
168 }
169 }
170 }
171 }
172}
173
174fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
175 // Any section that ends up here will be updated, that is,
176 // its size and alignment recalculated.
177 var gc_sections = std.AutoHashMap(MatchingSection, void).init(arena);
178 var loop: bool = true;
179 while (loop) {
180 loop = false;
181
182 for (macho_file.objects.items) |object| {
183 for (object.getSourceSymtab()) |_, source_index| {
184 const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;
185 if (alive.contains(atom)) continue;
186
187 const global = atom.getSymbolWithLoc();
188 const sym = atom.getSymbolPtr(macho_file);
189 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
190
191 if (sym.n_desc == MachO.N_DESC_GCED) continue;
192 if (!sym.ext() and !refersDead(atom, macho_file)) continue;
193
194 macho_file.logAtom(atom);
195 sym.n_desc = MachO.N_DESC_GCED;
196 removeAtomFromSection(atom, match, macho_file);
197 _ = try gc_sections.put(match, {});
198
199 for (atom.contained.items) |sym_off| {
200 const inner = macho_file.getSymbolPtr(.{
201 .sym_index = sym_off.sym_index,
202 .file = atom.file,
203 });
204 inner.n_desc = MachO.N_DESC_GCED;
205 }
206
207 if (macho_file.got_entries_table.contains(global)) {
208 const got_atom = macho_file.getGotAtomForSymbol(global).?;
209 const got_sym = got_atom.getSymbolPtr(macho_file);
210 got_sym.n_desc = MachO.N_DESC_GCED;
211 }
212
213 if (macho_file.stubs_table.contains(global)) {
214 const stubs_atom = macho_file.getStubsAtomForSymbol(global).?;
215 const stubs_sym = stubs_atom.getSymbolPtr(macho_file);
216 stubs_sym.n_desc = MachO.N_DESC_GCED;
217 }
218
219 if (macho_file.tlv_ptr_entries_table.contains(global)) {
220 const tlv_ptr_atom = macho_file.getTlvPtrAtomForSymbol(global).?;
221 const tlv_ptr_sym = tlv_ptr_atom.getSymbolPtr(macho_file);
222 tlv_ptr_sym.n_desc = MachO.N_DESC_GCED;
223 }
224
225 loop = true;
226 }
227 }
228 }
229
230 for (macho_file.got_entries.items) |entry| {
231 const sym = entry.getSymbol(macho_file);
232 if (sym.n_desc != MachO.N_DESC_GCED) continue;
233
234 // TODO tombstone
235 const atom = entry.getAtom(macho_file);
236 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
237 removeAtomFromSection(atom, match, macho_file);
238 _ = try gc_sections.put(match, {});
239 _ = macho_file.got_entries_table.remove(entry.target);
240 }
241
242 for (macho_file.stubs.items) |entry| {
243 const sym = entry.getSymbol(macho_file);
244 if (sym.n_desc != MachO.N_DESC_GCED) continue;
245
246 // TODO tombstone
247 const atom = entry.getAtom(macho_file);
248 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
249 removeAtomFromSection(atom, match, macho_file);
250 _ = try gc_sections.put(match, {});
251 _ = macho_file.stubs_table.remove(entry.target);
252 }
253
254 for (macho_file.tlv_ptr_entries.items) |entry| {
255 const sym = entry.getSymbol(macho_file);
256 if (sym.n_desc != MachO.N_DESC_GCED) continue;
257
258 // TODO tombstone
259 const atom = entry.getAtom(macho_file);
260 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
261 removeAtomFromSection(atom, match, macho_file);
262 _ = try gc_sections.put(match, {});
263 _ = macho_file.tlv_ptr_entries_table.remove(entry.target);
264 }
265
266 var gc_sections_it = gc_sections.iterator();
267 while (gc_sections_it.next()) |entry| {
268 const match = entry.key_ptr.*;
269 const sect = macho_file.getSectionPtr(match);
270 if (sect.size == 0) continue; // Pruning happens automatically in next step.
271
272 sect.@"align" = 0;
273 sect.size = 0;
274
275 var atom = macho_file.atoms.get(match).?;
276
277 while (atom.prev) |prev| {
278 atom = prev;
279 }
280
281 while (true) {
282 const atom_alignment = try math.powi(u32, 2, atom.alignment);
283 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);
284 const padding = aligned_end_addr - sect.size;
285 sect.size += padding + atom.size;
286 sect.@"align" = @maximum(sect.@"align", atom.alignment);
287
288 if (atom.next) |next| {
289 atom = next;
290 } else break;
291 }
292 }
293}