| author | |
| committer | |
| log | ca746566851aa5b12120fc76c69a0a2278a31f4e |
| tree | c561d73fcbc22b9af9bccdbd879c4ab67cee9310 |
| parent | 7345976261d4381ed48807f2003709e7ff609b0c |
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 | 758 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig" |
| 759 | 759 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig" |
| 760 | 760 | "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig" |
| 761 | "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig" | |
| 761 | 762 | "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig" |
| 762 | 763 | "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig" |
| 763 | 764 | "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig" |
| 764 | 765 | "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin" |
| 766 | "${CMAKE_SOURCE_DIR}/src/link/strtab.zig" | |
| 765 | 767 | "${CMAKE_SOURCE_DIR}/src/link/tapi.zig" |
| 766 | 768 | "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig" |
| 767 | 769 | "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig" |
src/link/MachO.zig+12-243| ... | ... | @@ -16,6 +16,7 @@ const meta = std.meta; |
| 16 | 16 | const aarch64 = @import("../arch/aarch64/bits.zig"); |
| 17 | 17 | const bind = @import("MachO/bind.zig"); |
| 18 | 18 | const codegen = @import("../codegen.zig"); |
| 19 | const dead_strip = @import("MachO/dead_strip.zig"); | |
| 19 | 20 | const link = @import("../link.zig"); |
| 20 | 21 | const llvm_backend = @import("../codegen/llvm.zig"); |
| 21 | 22 | const target_util = @import("../target.zig"); |
| ... | ... | @@ -709,7 +710,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) |
| 709 | 710 | const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; |
| 710 | 711 | const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; |
| 711 | 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; | |
| 713 | 714 | |
| 714 | 715 | const id_symlink_basename = "zld.id"; |
| 715 | 716 | |
| ... | ... | @@ -741,7 +742,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) |
| 741 | 742 | man.hash.addOptional(self.base.options.search_strategy); |
| 742 | 743 | man.hash.addOptional(self.base.options.headerpad_size); |
| 743 | 744 | man.hash.add(self.base.options.headerpad_max_install_names); |
| 744 | man.hash.add(dead_strip); | |
| 745 | man.hash.add(gc_sections); | |
| 745 | 746 | man.hash.add(self.base.options.dead_strip_dylibs); |
| 746 | 747 | man.hash.add(self.base.options.strip); |
| 747 | 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 | 1069 | try argv.append("-headerpad_max_install_names"); |
| 1069 | 1070 | } |
| 1070 | 1071 | |
| 1071 | if (dead_strip) { | |
| 1072 | if (gc_sections) { | |
| 1072 | 1073 | try argv.append("-dead_strip"); |
| 1073 | 1074 | } |
| 1074 | 1075 | |
| ... | ... | @@ -1186,19 +1187,12 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) |
| 1186 | 1187 | |
| 1187 | 1188 | try self.createTentativeDefAtoms(); |
| 1188 | 1189 | |
| 1189 | if (dead_strip) { | |
| 1190 | var gc_roots = std.AutoHashMap(*Atom, void).init(gpa); | |
| 1191 | defer gc_roots.deinit(); | |
| 1192 | ||
| 1193 | for (self.objects.items) |*object, object_id| { | |
| 1194 | try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id), &gc_roots); | |
| 1195 | } | |
| 1190 | for (self.objects.items) |*object, object_id| { | |
| 1191 | try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id)); | |
| 1192 | } | |
| 1196 | 1193 | |
| 1197 | try self.gcAtoms(&gc_roots); | |
| 1198 | } else { | |
| 1199 | for (self.objects.items) |*object, object_id| { | |
| 1200 | try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id), null); | |
| 1201 | } | |
| 1194 | if (gc_sections) { | |
| 1195 | try dead_strip.gcAtoms(self); | |
| 1202 | 1196 | } |
| 1203 | 1197 | |
| 1204 | 1198 | try self.pruneAndSortSections(); |
| ... | ... | @@ -5504,227 +5498,6 @@ fn pruneAndSortSections(self: *MachO) !void { |
| 5504 | 5498 | self.sections_order_dirty = false; |
| 5505 | 5499 | } |
| 5506 | 5500 | |
| 5507 | fn 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 | ||
| 5704 | fn 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 | ||
| 5728 | 5501 | fn updateSectionOrdinals(self: *MachO) !void { |
| 5729 | 5502 | if (!self.sections_order_dirty) return; |
| 5730 | 5503 | |
| ... | ... | @@ -6217,20 +5990,18 @@ fn writeDataInCode(self: *MachO) !void { |
| 6217 | 5990 | |
| 6218 | 5991 | for (self.objects.items) |object| { |
| 6219 | 5992 | const dice = object.parseDataInCode() orelse continue; |
| 6220 | const source_symtab = object.getSourceSymtab(); | |
| 6221 | 5993 | try out_dice.ensureUnusedCapacity(dice.len); |
| 6222 | 5994 | |
| 6223 | 5995 | for (object.managed_atoms.items) |atom| { |
| 6224 | 5996 | const sym = atom.getSymbol(self); |
| 6225 | 5997 | if (sym.n_desc == N_DESC_GCED) continue; |
| 6226 | if (atom.sym_index >= source_symtab.len) continue; // synthetic, linker generated | |
| 6227 | 5998 | |
| 6228 | 5999 | const match = self.getMatchingSectionFromOrdinal(sym.n_sect); |
| 6229 | 6000 | if (match.seg != self.text_segment_cmd_index.? and match.sect != self.text_section_index.?) { |
| 6230 | 6001 | continue; |
| 6231 | 6002 | } |
| 6232 | 6003 | |
| 6233 | const source_sym = source_symtab[atom.sym_index]; | |
| 6004 | const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue; | |
| 6234 | 6005 | const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow; |
| 6235 | 6006 | const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size); |
| 6236 | 6007 | const base = math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset) orelse |
| ... | ... | @@ -6886,16 +6657,14 @@ fn generateSymbolStabsForSymbol( |
| 6886 | 6657 | ) ![]const macho.nlist_64 { |
| 6887 | 6658 | const gpa = self.base.allocator; |
| 6888 | 6659 | const object = self.objects.items[sym_loc.file.?]; |
| 6889 | const source_symtab = object.getSourceSymtab(); | |
| 6890 | 6660 | const sym = self.getSymbol(sym_loc); |
| 6891 | 6661 | const sym_name = self.getSymbolName(sym_loc); |
| 6892 | 6662 | |
| 6893 | 6663 | if (sym.n_strx == 0) return buf[0..0]; |
| 6894 | 6664 | if (sym.n_desc == N_DESC_GCED) return buf[0..0]; |
| 6895 | 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 | |
| 6897 | 6666 | |
| 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 | 6668 | const size: ?u64 = size: { |
| 6900 | 6669 | if (source_sym.tentative()) break :size null; |
| 6901 | 6670 | for (debug_info.inner.func_list.items) |func| { |
| ... | ... | @@ -7353,7 +7122,7 @@ fn logAtoms(self: *MachO) void { |
| 7353 | 7122 | } |
| 7354 | 7123 | } |
| 7355 | 7124 | |
| 7356 | fn logAtom(self: *MachO, atom: *const Atom) void { | |
| 7125 | pub fn logAtom(self: *MachO, atom: *const Atom) void { | |
| 7357 | 7126 | const sym = atom.getSymbol(self); |
| 7358 | 7127 | const sym_name = atom.getName(self); |
| 7359 | 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 | 308 | if (rel.r_extern == 0) { |
| 309 | 309 | const sect_id = @intCast(u16, rel.r_symbolnum - 1); |
| 310 | 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 | 312 | const match = (try context.macho_file.getMatchingSection(sect)) orelse |
| 313 | 313 | unreachable; |
| 314 | 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 | 360 | else |
| 361 | 361 | mem.readIntLittle(i32, self.code.items[offset..][0..4]); |
| 362 | 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 | 364 | addend -= @intCast(i64, target_sect_base_addr); |
| 365 | 365 | } |
| 366 | 366 | try self.addPtrBindingOrRebase(rel, target, context); |
| ... | ... | @@ -392,7 +392,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context: |
| 392 | 392 | else |
| 393 | 393 | mem.readIntLittle(i32, self.code.items[offset..][0..4]); |
| 394 | 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 | 396 | addend -= @intCast(i64, target_sect_base_addr); |
| 397 | 397 | } |
| 398 | 398 | try self.addPtrBindingOrRebase(rel, target, context); |
| ... | ... | @@ -413,7 +413,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context: |
| 413 | 413 | if (rel.r_extern == 0) { |
| 414 | 414 | // Note for the future self: when r_extern == 0, we should subtract correction from the |
| 415 | 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 | 417 | // We need to add base_offset, i.e., offset of this atom wrt to the source |
| 418 | 418 | // section. Otherwise, the addend will over-/under-shoot. |
| 419 | 419 | addend += @intCast(i64, context.base_addr + offset + 4) - |
src/link/MachO/Object.zig+16-37| ... | ... | @@ -285,12 +285,7 @@ fn filterRelocs( |
| 285 | 285 | } |
| 286 | 286 | |
| 287 | 287 | /// Splits object into atoms assuming one-shot linking mode. |
| 288 | pub fn splitIntoAtomsOneShot( | |
| 289 | self: *Object, | |
| 290 | macho_file: *MachO, | |
| 291 | object_id: u32, | |
| 292 | gc_roots: ?*std.AutoHashMap(*Atom, void), | |
| 293 | ) !void { | |
| 288 | pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void { | |
| 294 | 289 | assert(macho_file.mode == .one_shot); |
| 295 | 290 | |
| 296 | 291 | const tracy = trace(@src()); |
| ... | ... | @@ -338,10 +333,7 @@ pub fn splitIntoAtomsOneShot( |
| 338 | 333 | |
| 339 | 334 | // We only care about defined symbols, so filter every other out. |
| 340 | 335 | const sorted_syms = sorted_all_syms.items[0..iundefsym]; |
| 341 | const dead_strip = macho_file.base.options.gc_sections orelse false; | |
| 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; | |
| 336 | const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; | |
| 345 | 337 | |
| 346 | 338 | for (seg.sections.items) |sect, id| { |
| 347 | 339 | const sect_id = @intCast(u8, id); |
| ... | ... | @@ -417,7 +409,6 @@ pub fn splitIntoAtomsOneShot( |
| 417 | 409 | &.{}, |
| 418 | 410 | match, |
| 419 | 411 | sect, |
| 420 | gc_roots, | |
| 421 | 412 | ); |
| 422 | 413 | try macho_file.addAtomToSection(atom, match); |
| 423 | 414 | } |
| ... | ... | @@ -473,7 +464,6 @@ pub fn splitIntoAtomsOneShot( |
| 473 | 464 | sorted_atom_syms.items[1..], |
| 474 | 465 | match, |
| 475 | 466 | sect, |
| 476 | gc_roots, | |
| 477 | 467 | ); |
| 478 | 468 | |
| 479 | 469 | if (arch == .x86_64 and addr == sect.addr) { |
| ... | ... | @@ -528,7 +518,6 @@ pub fn splitIntoAtomsOneShot( |
| 528 | 518 | filtered_syms, |
| 529 | 519 | match, |
| 530 | 520 | sect, |
| 531 | gc_roots, | |
| 532 | 521 | ); |
| 533 | 522 | try macho_file.addAtomToSection(atom, match); |
| 534 | 523 | } |
| ... | ... | @@ -547,7 +536,6 @@ fn createAtomFromSubsection( |
| 547 | 536 | indexes: []const SymbolAtIndex, |
| 548 | 537 | match: MatchingSection, |
| 549 | 538 | sect: macho.section_64, |
| 550 | gc_roots: ?*std.AutoHashMap(*Atom, void), | |
| 551 | 539 | ) !*Atom { |
| 552 | 540 | const gpa = macho_file.base.allocator; |
| 553 | 541 | const sym = self.symtab.items[sym_index]; |
| ... | ... | @@ -597,21 +585,6 @@ fn createAtomFromSubsection( |
| 597 | 585 | try self.atom_by_index_table.putNoClobber(gpa, inner_sym_index.index, atom); |
| 598 | 586 | } |
| 599 | 587 | |
| 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 | 588 | return atom; |
| 616 | 589 | } |
| 617 | 590 | |
| ... | ... | @@ -633,6 +606,18 @@ pub fn getSourceSymtab(self: Object) []const macho.nlist_64 { |
| 633 | 606 | ); |
| 634 | 607 | } |
| 635 | 608 | |
| 609 | pub 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 | ||
| 615 | pub 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 | ||
| 636 | 621 | pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry { |
| 637 | 622 | const index = self.data_in_code_cmd_index orelse return null; |
| 638 | 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 | 628 | ); |
| 644 | 629 | } |
| 645 | 630 | |
| 646 | pub fn getSectionContents(self: Object, sect_id: u16) error{Overflow}![]const u8 { | |
| 647 | const sect = self.getSection(sect_id); | |
| 631 | pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 { | |
| 632 | const sect = self.getSourceSection(index); | |
| 648 | 633 | const size = math.cast(usize, sect.size) orelse return error.Overflow; |
| 649 | 634 | log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{ |
| 650 | 635 | sect.segName(), |
| ... | ... | @@ -660,12 +645,6 @@ pub fn getString(self: Object, off: u32) []const u8 { |
| 660 | 645 | return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0); |
| 661 | 646 | } |
| 662 | 647 | |
| 663 | pub 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 | ||
| 669 | 648 | pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom { |
| 670 | 649 | return self.atom_by_index_table.get(sym_index); |
| 671 | 650 | } |
src/link/MachO/dead_strip.zig created+293| ... | ... | @@ -0,0 +1,293 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const log = std.log.scoped(.dead_strip); | |
| 4 | const macho = std.macho; | |
| 5 | const math = std.math; | |
| 6 | const mem = std.mem; | |
| 7 | ||
| 8 | const Allocator = mem.Allocator; | |
| 9 | const Atom = @import("Atom.zig"); | |
| 10 | const MachO = @import("../MachO.zig"); | |
| 11 | const MatchingSection = MachO.MatchingSection; | |
| 12 | ||
| 13 | pub 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 | ||
| 30 | fn 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 | ||
| 54 | fn 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 | ||
| 113 | fn 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 | ||
| 126 | fn 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 | ||
| 134 | fn 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 | ||
| 143 | fn 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 | ||
| 174 | fn 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 | } |