authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-04 20:40:10+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-22 16:58:20+02:00
log03feea0fb200f273dd74bf778997e6a6bead86cc
treec439ed641e01ec4d860abb181ee585a2a287494c
parentd042b88c112aa919386bc76294225d4f7bd9a7b3

macho: split section into subsections if requested and/or possible


4 files changed, 515 insertions(+), 233 deletions(-)

src/link/MachO.zig+264-27
......@@ -57,6 +57,8 @@ const SystemLib = struct {
5757 weak: bool = false,
5858};
5959
60const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));
61
6062base: File,
6163
6264/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
......@@ -256,6 +258,8 @@ unnamed_const_atoms: UnnamedConstTable = .{},
256258/// TODO consolidate this.
257259decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},
258260
261gc_roots: std.AutoHashMapUnmanaged(*Atom, void) = .{},
262
259263const Entry = struct {
260264 target: Atom.Relocation.Target,
261265 atom: *Atom,
......@@ -1165,6 +1169,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
11651169
11661170 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
11671171 if (use_llvm or use_stage1) {
1172 self.logAtoms();
1173 try self.gcAtoms();
11681174 try self.pruneAndSortSections();
11691175 try self.allocateSegments();
11701176 try self.allocateLocals();
......@@ -1173,9 +1179,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
11731179 try self.allocateSpecialSymbols();
11741180 try self.allocateGlobals();
11751181
1176 if (build_options.enable_logging) {
1182 if (build_options.enable_logging or true) {
11771183 self.logSymtab();
11781184 self.logSectionOrdinals();
1185 self.logAtoms();
11791186 }
11801187
11811188 if (use_llvm or use_stage1) {
......@@ -2177,6 +2184,7 @@ pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment:
21772184 try atom.code.resize(self.base.allocator, size_usize);
21782185 mem.set(u8, atom.code.items, 0);
21792186
2187 try self.atom_by_index_table.putNoClobber(self.base.allocator, local_sym_index, atom);
21802188 try self.managed_atoms.append(self.base.allocator, atom);
21812189 return atom;
21822190}
......@@ -3298,12 +3306,7 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32983306 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
32993307 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
33003308 atom_sym.n_value = vaddr;
3301 } else {
3302 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
3303 const sect = &seg.sections.items[self.got_section_index.?];
3304 sect.size += atom.size;
3305 try self.addAtomToSection(atom, match);
3306 }
3309 } else try self.addAtomToSection(atom, match);
33073310
33083311 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
33093312}
......@@ -3564,6 +3567,7 @@ pub fn deinit(self: *MachO) void {
35643567 self.symbol_resolver.deinit(self.base.allocator);
35653568 self.unresolved.deinit(self.base.allocator);
35663569 self.tentatives.deinit(self.base.allocator);
3570 self.gc_roots.deinit(self.base.allocator);
35673571
35683572 for (self.objects.items) |*object| {
35693573 object.deinit(self.base.allocator);
......@@ -3916,7 +3920,6 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
39163920 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
39173921 const local_sym_index = try self.allocateLocalSymbol();
39183922 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment));
3919 try self.atom_by_index_table.putNoClobber(self.base.allocator, local_sym_index, atom);
39203923
39213924 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
39223925 .parent_atom_index = local_sym_index,
......@@ -5597,7 +5600,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*
55975600 const old_idx = maybe_index.* orelse continue;
55985601 const sect = sections[old_idx];
55995602 if (sect.size == 0) {
5600 log.debug("pruning section {s},{s}", .{ sect.segName(), sect.sectName() });
5603 log.warn("pruning section {s},{s}", .{ sect.segName(), sect.sectName() });
56015604 maybe_index.* = null;
56025605 seg.inner.cmdsize -= @sizeOf(macho.section_64);
56035606 seg.inner.nsects -= 1;
......@@ -5630,7 +5633,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*
56305633
56315634 if (seg.inner.nsects == 0 and !mem.eql(u8, "__TEXT", seg.inner.segName())) {
56325635 // Segment has now become empty, so mark it as such
5633 log.debug("marking segment {s} as dead", .{seg.inner.segName()});
5636 log.warn("marking segment {s} as dead", .{seg.inner.segName()});
56345637 seg.inner.cmd = @intToEnum(macho.LC, 0);
56355638 maybe_seg_id.* = null;
56365639 }
......@@ -5712,6 +5715,189 @@ fn pruneAndSortSections(self: *MachO) !void {
57125715 self.sections_order_dirty = false;
57135716}
57145717
5718fn gcAtoms(self: *MachO) !void {
5719 const dead_strip = self.base.options.gc_sections orelse false;
5720 if (!dead_strip) return;
5721
5722 // Add all exports as GC roots
5723 for (self.globals.items) |sym| {
5724 if (sym.n_type == 0) continue;
5725 const resolv = self.symbol_resolver.get(sym.n_strx).?;
5726 assert(resolv.where == .global);
5727 const gc_root = self.atom_by_index_table.get(resolv.local_sym_index) orelse {
5728 log.warn("skipping {s}", .{self.getString(sym.n_strx)});
5729 continue;
5730 };
5731 _ = try self.gc_roots.getOrPut(self.base.allocator, gc_root);
5732 }
5733
5734 // if (self.tlv_ptrs_section_index) |sect| {
5735 // var atom = self.atoms.get(.{
5736 // .seg = self.data_segment_cmd_index.?,
5737 // .sect = sect,
5738 // }).?;
5739
5740 // while (true) {
5741 // _ = try self.gc_roots.getOrPut(self.base.allocator, atom);
5742
5743 // if (atom.prev) |prev| {
5744 // atom = prev;
5745 // } else break;
5746 // }
5747 // }
5748
5749 // Add any atom targeting an import as GC root
5750 var atoms_it = self.atoms.iterator();
5751 while (atoms_it.next()) |entry| {
5752 var atom = entry.value_ptr.*;
5753
5754 while (true) {
5755 for (atom.relocs.items) |rel| {
5756 if ((try Atom.getTargetAtom(rel, self)) == null) switch (rel.target) {
5757 .local => {},
5758 .global => |n_strx| {
5759 const resolv = self.symbol_resolver.get(n_strx).?;
5760 switch (resolv.where) {
5761 .global => {},
5762 .undef => {
5763 _ = try self.gc_roots.getOrPut(self.base.allocator, atom);
5764 break;
5765 },
5766 }
5767 },
5768 };
5769 }
5770
5771 if (atom.prev) |prev| {
5772 atom = prev;
5773 } else break;
5774 }
5775 }
5776
5777 var stack = std.ArrayList(*Atom).init(self.base.allocator);
5778 defer stack.deinit();
5779 try stack.ensureUnusedCapacity(self.gc_roots.count());
5780
5781 var retained = std.AutoHashMap(*Atom, void).init(self.base.allocator);
5782 defer retained.deinit();
5783 try retained.ensureUnusedCapacity(self.gc_roots.count());
5784
5785 log.warn("GC roots:", .{});
5786 var gc_roots_it = self.gc_roots.keyIterator();
5787 while (gc_roots_it.next()) |gc_root| {
5788 self.logAtom(gc_root.*);
5789
5790 stack.appendAssumeCapacity(gc_root.*);
5791 retained.putAssumeCapacityNoClobber(gc_root.*, {});
5792 }
5793
5794 log.warn("walking tree...", .{});
5795 while (stack.popOrNull()) |source_atom| {
5796 for (source_atom.relocs.items) |rel| {
5797 if (try Atom.getTargetAtom(rel, self)) |target_atom| {
5798 const gop = try retained.getOrPut(target_atom);
5799 if (!gop.found_existing) {
5800 log.warn(" RETAINED ATOM(%{d}) -> ATOM(%{d})", .{
5801 source_atom.local_sym_index,
5802 target_atom.local_sym_index,
5803 });
5804 try stack.append(target_atom);
5805 }
5806 }
5807 }
5808 }
5809
5810 atoms_it = self.atoms.iterator();
5811 while (atoms_it.next()) |entry| {
5812 const match = entry.key_ptr.*;
5813
5814 if (self.text_segment_cmd_index) |seg| {
5815 if (seg == match.seg) {
5816 if (self.eh_frame_section_index) |sect| {
5817 if (sect == match.sect) continue;
5818 }
5819 }
5820 }
5821
5822 if (self.data_segment_cmd_index) |seg| {
5823 if (seg == match.seg) {
5824 if (self.rustc_section_index) |sect| {
5825 if (sect == match.sect) continue;
5826 }
5827 }
5828 }
5829
5830 const seg = &self.load_commands.items[match.seg].segment;
5831 const sect = &seg.sections.items[match.sect];
5832 var atom = entry.value_ptr.*;
5833
5834 log.warn("GCing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
5835
5836 while (true) {
5837 const orig_prev = atom.prev;
5838
5839 if (!retained.contains(atom)) {
5840 // Dead atom; remove.
5841 log.warn(" DEAD ATOM(%{d})", .{atom.local_sym_index});
5842
5843 const sym = &self.locals.items[atom.local_sym_index];
5844 sym.n_desc = N_DESC_GCED;
5845
5846 if (self.symbol_resolver.getPtr(sym.n_strx)) |resolv| {
5847 if (resolv.local_sym_index == atom.local_sym_index) {
5848 const global = &self.globals.items[resolv.where_index];
5849 global.n_desc = N_DESC_GCED;
5850 }
5851 }
5852
5853 for (self.got_entries.items) |got_entry| {
5854 if (got_entry.atom == atom) {
5855 _ = self.got_entries_table.swapRemove(got_entry.target);
5856 break;
5857 }
5858 }
5859
5860 for (self.stubs.items) |stub, i| {
5861 if (stub == atom) {
5862 _ = self.stubs_table.swapRemove(@intCast(u32, i));
5863 break;
5864 }
5865 }
5866
5867 for (atom.contained.items) |sym_off| {
5868 const inner = &self.locals.items[sym_off.local_sym_index];
5869 inner.n_desc = N_DESC_GCED;
5870
5871 if (self.symbol_resolver.getPtr(inner.n_strx)) |resolv| {
5872 if (resolv.local_sym_index == atom.local_sym_index) {
5873 const global = &self.globals.items[resolv.where_index];
5874 global.n_desc = N_DESC_GCED;
5875 }
5876 }
5877 }
5878
5879 log.warn(" BEFORE size = {x}", .{sect.size});
5880 sect.size -= atom.size;
5881 log.warn(" AFTER size = {x}", .{sect.size});
5882 if (atom.prev) |prev| {
5883 prev.next = atom.next;
5884 }
5885 if (atom.next) |next| {
5886 next.prev = atom.prev;
5887 } else {
5888 // TODO I think a null would be better here.
5889 // The section will be GCed in the next step.
5890 entry.value_ptr.* = if (atom.prev) |prev| prev else undefined;
5891 }
5892 }
5893
5894 if (orig_prev) |prev| {
5895 atom = prev;
5896 } else break;
5897 }
5898 }
5899}
5900
57155901fn updateSectionOrdinals(self: *MachO) !void {
57165902 if (!self.sections_order_dirty) return;
57175903
......@@ -5776,8 +5962,11 @@ fn writeDyldInfoData(self: *MachO) !void {
57765962 }
57775963
57785964 const seg = self.load_commands.items[match.seg].segment;
5965 const sect = seg.sections.items[match.sect];
5966 log.warn("dyld info for {s},{s}", .{ sect.segName(), sect.sectName() });
57795967
57805968 while (true) {
5969 log.warn(" ATOM %{d}", .{atom.local_sym_index});
57815970 const sym = self.locals.items[atom.local_sym_index];
57825971 const base_offset = sym.n_value - seg.inner.vmaddr;
57835972
......@@ -6217,10 +6406,19 @@ fn writeSymbolTable(self: *MachO) !void {
62176406
62186407 for (self.locals.items) |sym| {
62196408 if (sym.n_strx == 0) continue;
6409 if (sym.n_desc == N_DESC_GCED) continue;
62206410 if (self.symbol_resolver.get(sym.n_strx)) |_| continue;
62216411 try locals.append(sym);
62226412 }
62236413
6414 var globals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
6415 defer globals.deinit();
6416
6417 for (self.globals.items) |sym| {
6418 if (sym.n_desc == N_DESC_GCED) continue;
6419 try globals.append(sym);
6420 }
6421
62246422 // TODO How do we handle null global symbols in incremental context?
62256423 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
62266424 defer undefs.deinit();
......@@ -6291,7 +6489,7 @@ fn writeSymbolTable(self: *MachO) !void {
62916489 }
62926490
62936491 const nlocals = locals.items.len;
6294 const nexports = self.globals.items.len;
6492 const nexports = globals.items.len;
62956493 const nundefs = undefs.items.len;
62966494
62976495 const locals_off = symtab.symoff;
......@@ -6302,7 +6500,7 @@ fn writeSymbolTable(self: *MachO) !void {
63026500 const exports_off = locals_off + locals_size;
63036501 const exports_size = nexports * @sizeOf(macho.nlist_64);
63046502 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
6305 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), exports_off);
6503 try self.base.file.?.pwriteAll(mem.sliceAsBytes(globals.items), exports_off);
63066504
63076505 const undefs_off = exports_off + exports_size;
63086506 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
......@@ -6898,55 +7096,55 @@ fn snapshotState(self: *MachO) !void {
68987096}
68997097
69007098fn logSymtab(self: MachO) void {
6901 log.debug("locals:", .{});
7099 log.warn("locals:", .{});
69027100 for (self.locals.items) |sym, id| {
6903 log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.n_strx), sym.n_value, sym.n_sect });
7101 log.warn(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.n_strx), sym.n_value, sym.n_sect });
69047102 }
69057103
6906 log.debug("globals:", .{});
7104 log.warn("globals:", .{});
69077105 for (self.globals.items) |sym, id| {
6908 log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.n_strx), sym.n_value, sym.n_sect });
7106 log.warn(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.n_strx), sym.n_value, sym.n_sect });
69097107 }
69107108
6911 log.debug("undefs:", .{});
7109 log.warn("undefs:", .{});
69127110 for (self.undefs.items) |sym, id| {
6913 log.debug(" {d}: {s}: in {d}", .{ id, self.getString(sym.n_strx), sym.n_desc });
7111 log.warn(" {d}: {s}: in {d}", .{ id, self.getString(sym.n_strx), sym.n_desc });
69147112 }
69157113
69167114 {
6917 log.debug("resolver:", .{});
7115 log.warn("resolver:", .{});
69187116 var it = self.symbol_resolver.iterator();
69197117 while (it.next()) |entry| {
6920 log.debug(" {s} => {}", .{ self.getString(entry.key_ptr.*), entry.value_ptr.* });
7118 log.warn(" {s} => {}", .{ self.getString(entry.key_ptr.*), entry.value_ptr.* });
69217119 }
69227120 }
69237121
6924 log.debug("GOT entries:", .{});
7122 log.warn("GOT entries:", .{});
69257123 for (self.got_entries_table.values()) |value| {
69267124 const key = self.got_entries.items[value].target;
69277125 const atom = self.got_entries.items[value].atom;
69287126 const n_value = self.locals.items[atom.local_sym_index].n_value;
69297127 switch (key) {
6930 .local => |ndx| log.debug(" {d}: @{x}", .{ ndx, n_value }),
6931 .global => |n_strx| log.debug(" {s}: @{x}", .{ self.getString(n_strx), n_value }),
7128 .local => |ndx| log.warn(" {d}: @{x}", .{ ndx, n_value }),
7129 .global => |n_strx| log.warn(" {s}: @{x}", .{ self.getString(n_strx), n_value }),
69327130 }
69337131 }
69347132
6935 log.debug("__thread_ptrs entries:", .{});
7133 log.warn("__thread_ptrs entries:", .{});
69367134 for (self.tlv_ptr_entries_table.values()) |value| {
69377135 const key = self.tlv_ptr_entries.items[value].target;
69387136 const atom = self.tlv_ptr_entries.items[value].atom;
69397137 const n_value = self.locals.items[atom.local_sym_index].n_value;
69407138 assert(key == .global);
6941 log.debug(" {s}: @{x}", .{ self.getString(key.global), n_value });
7139 log.warn(" {s}: @{x}", .{ self.getString(key.global), n_value });
69427140 }
69437141
6944 log.debug("stubs:", .{});
7142 log.warn("stubs:", .{});
69457143 for (self.stubs_table.keys()) |key| {
69467144 const value = self.stubs_table.get(key).?;
69477145 const atom = self.stubs.items[value];
69487146 const sym = self.locals.items[atom.local_sym_index];
6949 log.debug(" {s}: @{x}", .{ self.getString(key), sym.n_value });
7147 log.warn(" {s}: @{x}", .{ self.getString(key), sym.n_value });
69507148 }
69517149}
69527150
......@@ -6964,6 +7162,45 @@ fn logSectionOrdinals(self: MachO) void {
69647162 }
69657163}
69667164
7165fn logAtoms(self: MachO) void {
7166 log.warn("atoms:", .{});
7167 var it = self.atoms.iterator();
7168 while (it.next()) |entry| {
7169 const match = entry.key_ptr.*;
7170 var atom = entry.value_ptr.*;
7171
7172 while (atom.prev) |prev| {
7173 atom = prev;
7174 }
7175
7176 const seg = self.load_commands.items[match.seg].segment;
7177 const sect = seg.sections.items[match.sect];
7178 log.warn("{s},{s}", .{ sect.segName(), sect.sectName() });
7179
7180 while (true) {
7181 self.logAtom(atom);
7182
7183 if (atom.next) |next| {
7184 atom = next;
7185 } else break;
7186 }
7187 }
7188}
7189
7190fn logAtom(self: MachO, atom: *const Atom) void {
7191 const sym = self.locals.items[atom.local_sym_index];
7192 log.warn(" ATOM(%{d}) @ {x}", .{ atom.local_sym_index, sym.n_value });
7193
7194 for (atom.contained.items) |sym_off| {
7195 const inner_sym = self.locals.items[sym_off.local_sym_index];
7196 log.warn(" %{d} ('{s}') @ {x}", .{
7197 sym_off.local_sym_index,
7198 self.getString(inner_sym.n_strx),
7199 inner_sym.n_value,
7200 });
7201 }
7202}
7203
69677204/// Since `os.copy_file_range` cannot be used when copying overlapping ranges within the same file,
69687205/// and since `File.copyRangeAll` uses `os.copy_file_range` under-the-hood, we use heap allocated
69697206/// buffers on all hosts except Linux (if `copy_file_range` syscall is available).
src/link/MachO/Atom.zig+61-3
......@@ -236,6 +236,7 @@ pub fn freeListEligible(self: Atom, macho_file: MachO) bool {
236236
237237const RelocContext = struct {
238238 base_addr: u64 = 0,
239 base_offset: i32 = 0,
239240 allocator: Allocator,
240241 object: *Object,
241242 macho_file: *MachO,
......@@ -366,7 +367,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
366367 ) orelse unreachable;
367368 break :target Relocation.Target{ .global = n_strx };
368369 };
369 const offset = @intCast(u32, rel.r_address);
370 const offset = @intCast(u32, rel.r_address - context.base_offset);
370371
371372 switch (arch) {
372373 .aarch64 => {
......@@ -487,7 +488,7 @@ fn addPtrBindingOrRebase(
487488 .global => |n_strx| {
488489 try self.bindings.append(context.allocator, .{
489490 .n_strx = n_strx,
490 .offset = @intCast(u32, rel.r_address),
491 .offset = @intCast(u32, rel.r_address - context.base_offset),
491492 });
492493 },
493494 .local => {
......@@ -529,7 +530,10 @@ fn addPtrBindingOrRebase(
529530 };
530531
531532 if (should_rebase) {
532 try self.rebases.append(context.allocator, @intCast(u32, rel.r_address));
533 try self.rebases.append(
534 context.allocator,
535 @intCast(u32, rel.r_address - context.base_offset),
536 );
533537 }
534538 },
535539 }
......@@ -650,6 +654,60 @@ fn addStub(target: Relocation.Target, context: RelocContext) !void {
650654 context.macho_file.stubs.items[stub_index] = atom;
651655}
652656
657pub fn getTargetAtom(rel: Relocation, macho_file: *MachO) !?*Atom {
658 const is_via_got = got: {
659 switch (macho_file.base.options.target.cpu.arch) {
660 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {
661 .ARM64_RELOC_GOT_LOAD_PAGE21,
662 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
663 .ARM64_RELOC_POINTER_TO_GOT,
664 => true,
665 else => false,
666 },
667 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
668 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
669 else => false,
670 },
671 else => unreachable,
672 }
673 };
674
675 if (is_via_got) {
676 const got_index = macho_file.got_entries_table.get(rel.target) orelse {
677 log.err("expected GOT entry for symbol", .{});
678 switch (rel.target) {
679 .local => |sym_index| log.err(" local @{d}", .{sym_index}),
680 .global => |n_strx| log.err(" global @'{s}'", .{macho_file.getString(n_strx)}),
681 }
682 log.err(" this is an internal linker error", .{});
683 return error.FailedToResolveRelocationTarget;
684 };
685 return macho_file.got_entries.items[got_index].atom;
686 }
687
688 switch (rel.target) {
689 .local => |sym_index| {
690 return macho_file.atom_by_index_table.get(sym_index);
691 },
692 .global => |n_strx| {
693 const resolv = macho_file.symbol_resolver.get(n_strx).?;
694 switch (resolv.where) {
695 .global => return macho_file.atom_by_index_table.get(resolv.local_sym_index),
696 .undef => {
697 if (macho_file.stubs_table.get(n_strx)) |stub_index| {
698 return macho_file.stubs.items[stub_index];
699 } else {
700 if (macho_file.tlv_ptr_entries_table.get(rel.target)) |tlv_ptr_index| {
701 return macho_file.tlv_ptr_entries.items[tlv_ptr_index].atom;
702 }
703 return null;
704 }
705 },
706 }
707 },
708 }
709}
710
653711pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
654712 const tracy = trace(@src());
655713 defer tracy.end();
src/link/MachO/Object.zig+189-203
......@@ -176,6 +176,13 @@ pub fn free(self: *Object, allocator: Allocator, macho_file: *MachO) void {
176176 .n_desc = 0,
177177 .n_value = 0,
178178 };
179 _ = macho_file.atom_by_index_table.remove(atom.local_sym_index);
180 _ = macho_file.gc_roots.remove(atom);
181
182 for (atom.contained.items) |sym_off| {
183 _ = macho_file.atom_by_index_table.remove(sym_off.local_sym_index);
184 }
185
179186 atom.local_sym_index = 0;
180187 }
181188 if (atom == last_atom) {
......@@ -346,7 +353,7 @@ const NlistWithIndex = struct {
346353 }
347354 }
348355
349 fn filterInSection(symbols: []NlistWithIndex, sect: macho.section_64) []NlistWithIndex {
356 fn filterByAddress(symbols: []NlistWithIndex, start_addr: u64, end_addr: u64) []NlistWithIndex {
350357 const Predicate = struct {
351358 addr: u64,
352359
......@@ -355,13 +362,36 @@ const NlistWithIndex = struct {
355362 }
356363 };
357364
358 const start = MachO.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr });
359 const end = MachO.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size });
365 const start = MachO.findFirst(NlistWithIndex, symbols, 0, Predicate{
366 .addr = start_addr,
367 });
368 const end = MachO.findFirst(NlistWithIndex, symbols, start, Predicate{
369 .addr = end_addr,
370 });
360371
361372 return symbols[start..end];
362373 }
363374};
364375
376fn filterRelocs(
377 relocs: []const macho.relocation_info,
378 start_addr: u64,
379 end_addr: u64,
380) []const macho.relocation_info {
381 const Predicate = struct {
382 addr: u64,
383
384 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
385 return rel.r_address < self.addr;
386 }
387 };
388
389 const start = MachO.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr });
390 const end = MachO.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr });
391
392 return relocs[start..end];
393}
394
365395fn filterDice(
366396 dices: []const macho.data_in_code_entry,
367397 start_addr: u64,
......@@ -422,16 +452,13 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
422452 // We only care about defined symbols, so filter every other out.
423453 const sorted_nlists = sorted_all_nlists.items[0..iundefsym];
424454
425 const dead_strip = blk: {
426 const dead_strip = macho_file.base.options.gc_sections orelse break :blk false;
427 if (dead_strip or macho_file.base.options.optimize_mode != .Debug)
428 break :blk self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
429 break :blk false;
430 };
455 const dead_strip = macho_file.base.options.gc_sections orelse false;
456 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0 and
457 (macho_file.base.options.optimize_mode != .Debug or dead_strip);
431458
432459 for (seg.sections.items) |sect, id| {
433460 const sect_id = @intCast(u8, id);
434 log.debug("putting section '{s},{s}' as an Atom", .{ sect.segName(), sect.sectName() });
461 log.debug("parsing section '{s},{s}' into Atoms", .{ sect.segName(), sect.sectName() });
435462
436463 // Get matching segment/section in the final artifact.
437464 const match = (try macho_file.getMatchingSection(sect)) orelse {
......@@ -455,7 +482,11 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
455482 );
456483
457484 // Symbols within this section only.
458 const filtered_nlists = NlistWithIndex.filterInSection(sorted_nlists, sect);
485 const filtered_nlists = NlistWithIndex.filterByAddress(
486 sorted_nlists,
487 sect.addr,
488 sect.addr + sect.size,
489 );
459490
460491 macho_file.has_dices = macho_file.has_dices or blk: {
461492 if (self.text_section_index) |index| {
......@@ -467,204 +498,123 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
467498 };
468499 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;
469500
470 if (dead_strip) blk: {
471 if (filtered_nlists.len == 0) break :blk; // nothing to split
472
501 if (subsections_via_symbols and filtered_nlists.len > 0) {
473502 // If the first nlist does not match the start of the section,
474503 // then we need to encapsulate the memory range [section start, first symbol)
475504 // as a temporary symbol and insert the matching Atom.
476505 const first_nlist = filtered_nlists[0].nlist;
477 if (first_nlist.n_value > sect.addr) {}
478 }
479
480 // If there is no symbol to refer to this atom, we create
481 // a temp one, unless we already did that when working out the relocations
482 // of other atoms.
483 const local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
484 const local_sym_index = @intCast(u32, macho_file.locals.items.len);
485 try macho_file.locals.append(allocator, .{
486 .n_strx = 0,
487 .n_type = macho.N_SECT,
488 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
489 .n_desc = 0,
490 .n_value = sect.addr,
491 });
492 try self.sections_as_symbols.putNoClobber(allocator, sect_id, local_sym_index);
493 break :blk local_sym_index;
494 };
495 const atom = try self.parseIntoAtom(
496 allocator,
497 local_sym_index,
498 sect.size,
499 sect.@"align",
500 code,
501 relocs,
502 filtered_nlists,
503 match,
504 macho_file,
505 );
506
507 if (!self.start_atoms.contains(match)) {
508 try self.start_atoms.putNoClobber(allocator, match, atom);
509 }
506 if (first_nlist.n_value > sect.addr) {
507 const local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
508 const local_sym_index = @intCast(u32, macho_file.locals.items.len);
509 try macho_file.locals.append(allocator, .{
510 .n_strx = 0,
511 .n_type = macho.N_SECT,
512 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
513 .n_desc = 0,
514 .n_value = sect.addr,
515 });
516 try self.sections_as_symbols.putNoClobber(allocator, sect_id, local_sym_index);
517 break :blk local_sym_index;
518 };
519 const atom_size = first_nlist.n_value - sect.addr;
520 const atom_code: ?[]const u8 = if (code) |cc|
521 cc[0..atom_size]
522 else
523 null;
524 try self.parseIntoAtom(
525 allocator,
526 local_sym_index,
527 atom_size,
528 sect.@"align",
529 atom_code,
530 relocs,
531 &.{},
532 match,
533 sect,
534 macho_file,
535 );
536 }
510537
511 if (self.end_atoms.getPtr(match)) |last| {
512 last.*.next = atom;
513 atom.prev = last.*;
514 last.* = atom;
538 var next_nlist_count: usize = 0;
539 while (next_nlist_count < filtered_nlists.len) {
540 const next_nlist = filtered_nlists[next_nlist_count];
541 const addr = next_nlist.nlist.n_value;
542 const atom_nlists = NlistWithIndex.filterByAddress(
543 filtered_nlists[next_nlist_count..],
544 addr,
545 addr + 1,
546 );
547 next_nlist_count += atom_nlists.len;
548
549 const local_sym_index = @intCast(u32, macho_file.locals.items.len);
550 try macho_file.locals.append(allocator, .{
551 .n_strx = 0,
552 .n_type = macho.N_SECT,
553 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
554 .n_desc = 0,
555 .n_value = addr,
556 });
557
558 const atom_size = blk: {
559 const end_addr = if (next_nlist_count < filtered_nlists.len)
560 filtered_nlists[next_nlist_count].nlist.n_value
561 else
562 sect.addr + sect.size;
563 break :blk end_addr - addr;
564 };
565 const atom_code: ?[]const u8 = if (code) |cc|
566 cc[addr - sect.addr ..][0..atom_size]
567 else
568 null;
569 const atom_align = if (addr > 0)
570 math.min(@ctz(u64, addr), sect.@"align")
571 else
572 sect.@"align";
573 try self.parseIntoAtom(
574 allocator,
575 local_sym_index,
576 atom_size,
577 atom_align,
578 atom_code,
579 relocs,
580 atom_nlists,
581 match,
582 sect,
583 macho_file,
584 );
585 }
515586 } else {
516 try self.end_atoms.putNoClobber(allocator, match, atom);
587 // If there is no symbol to refer to this atom, we create
588 // a temp one, unless we already did that when working out the relocations
589 // of other atoms.
590 const local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
591 const local_sym_index = @intCast(u32, macho_file.locals.items.len);
592 try macho_file.locals.append(allocator, .{
593 .n_strx = 0,
594 .n_type = macho.N_SECT,
595 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
596 .n_desc = 0,
597 .n_value = sect.addr,
598 });
599 try self.sections_as_symbols.putNoClobber(allocator, sect_id, local_sym_index);
600 break :blk local_sym_index;
601 };
602 try self.parseIntoAtom(
603 allocator,
604 local_sym_index,
605 sect.size,
606 sect.@"align",
607 code,
608 relocs,
609 filtered_nlists,
610 match,
611 sect,
612 macho_file,
613 );
517614 }
518 try self.contained_atoms.append(allocator, atom);
519615 }
520616}
521617
522// const Context = struct {
523// allocator: *Allocator,
524// object: *Object,
525// macho_file: *MachO,
526// match: MachO.MatchingSection,
527// };
528
529// const AtomParser = struct {
530// section: macho.section_64,
531// code: []u8,
532// relocs: []macho.relocation_info,
533// nlists: []NlistWithIndex,
534// index: u32 = 0,
535
536// fn peek(self: AtomParser) ?NlistWithIndex {
537// return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;
538// }
539
540// fn lessThanBySeniority(context: Context, lhs: NlistWithIndex, rhs: NlistWithIndex) bool {
541// if (!MachO.symbolIsExt(rhs.nlist)) {
542// return MachO.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx));
543// } else if (MachO.symbolIsPext(rhs.nlist) or MachO.symbolIsWeakDef(rhs.nlist)) {
544// return !MachO.symbolIsExt(lhs.nlist);
545// } else {
546// return false;
547// }
548// }
549
550// pub fn next(self: *AtomParser, context: Context) !?*Atom {
551// if (self.index == self.nlists.len) return null;
552
553// const tracy = trace(@src());
554// defer tracy.end();
555
556// var aliases = std.ArrayList(NlistWithIndex).init(context.allocator);
557// defer aliases.deinit();
558
559// const next_nlist: ?NlistWithIndex = blk: while (true) {
560// const curr_nlist = self.nlists[self.index];
561// try aliases.append(curr_nlist);
562
563// if (self.peek()) |next_nlist| {
564// if (curr_nlist.nlist.n_value == next_nlist.nlist.n_value) {
565// self.index += 1;
566// continue;
567// }
568// break :blk next_nlist;
569// }
570// break :blk null;
571// } else null;
572
573// for (aliases.items) |*nlist_with_index| {
574// nlist_with_index.index = context.object.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
575// }
576
577// if (aliases.items.len > 1) {
578// // Bubble-up senior symbol as the main link to the atom.
579// sort.sort(
580// NlistWithIndex,
581// aliases.items,
582// context,
583// AtomParser.lessThanBySeniority,
584// );
585// }
586
587// const senior_nlist = aliases.pop();
588// const senior_sym = &context.macho_file.locals.items[senior_nlist.index];
589// senior_sym.n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(context.match).? + 1);
590
591// const start_addr = senior_nlist.nlist.n_value - self.section.addr;
592// const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size;
593
594// const code = self.code[start_addr..end_addr];
595// const size = code.len;
596
597// const max_align = self.section.@"align";
598// const actual_align = if (senior_nlist.nlist.n_value > 0)
599// math.min(@ctz(u64, senior_nlist.nlist.n_value), max_align)
600// else
601// max_align;
602
603// const stab: ?Atom.Stab = if (context.object.debug_info) |di| blk: {
604// // TODO there has to be a better to handle this.
605// for (di.inner.func_list.items) |func| {
606// if (func.pc_range) |range| {
607// if (senior_nlist.nlist.n_value >= range.start and senior_nlist.nlist.n_value < range.end) {
608// break :blk Atom.Stab{
609// .function = range.end - range.start,
610// };
611// }
612// }
613// }
614// // TODO
615// // if (self.macho_file.globals.contains(self.macho_file.getString(senior_sym.strx))) break :blk .global;
616// break :blk .static;
617// } else null;
618
619// const atom = try context.macho_file.createEmptyAtom(senior_nlist.index, size, actual_align);
620// atom.stab = stab;
621
622// const is_zerofill = blk: {
623// const section_type = commands.sectionType(self.section);
624// break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
625// };
626// if (!is_zerofill) {
627// mem.copy(u8, atom.code.items, code);
628// }
629
630// try atom.aliases.ensureTotalCapacity(context.allocator, aliases.items.len);
631// for (aliases.items) |alias| {
632// atom.aliases.appendAssumeCapacity(alias.index);
633// const sym = &context.macho_file.locals.items[alias.index];
634// sym.n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(context.match).? + 1);
635// }
636
637// try atom.parseRelocs(self.relocs, .{
638// .base_addr = self.section.addr,
639// .base_offset = start_addr,
640// .allocator = context.allocator,
641// .object = context.object,
642// .macho_file = context.macho_file,
643// });
644
645// if (context.macho_file.has_dices) {
646// const dices = filterDice(
647// context.object.data_in_code_entries.items,
648// senior_nlist.nlist.n_value,
649// senior_nlist.nlist.n_value + size,
650// );
651// try atom.dices.ensureTotalCapacity(context.allocator, dices.len);
652
653// for (dices) |dice| {
654// atom.dices.appendAssumeCapacity(.{
655// .offset = dice.offset - try math.cast(u32, senior_nlist.nlist.n_value),
656// .length = dice.length,
657// .kind = dice.kind,
658// });
659// }
660// }
661
662// self.index += 1;
663
664// return atom;
665// }
666// };
667
668618fn parseIntoAtom(
669619 self: *Object,
670620 allocator: Allocator,
......@@ -675,8 +625,9 @@ fn parseIntoAtom(
675625 relocs: []const macho.relocation_info,
676626 nlists: []const NlistWithIndex,
677627 match: MatchingSection,
628 sect: macho.section_64,
678629 macho_file: *MachO,
679) !*Atom {
630) !void {
680631 const sym = macho_file.locals.items[local_sym_index];
681632 const align_pow_2 = try math.powi(u32, 2, alignment);
682633 const aligned_size = mem.alignForwardGeneric(u64, size, align_pow_2);
......@@ -686,8 +637,11 @@ fn parseIntoAtom(
686637 mem.copy(u8, atom.code.items, cc);
687638 }
688639
689 try atom.parseRelocs(relocs, .{
690 .base_addr = sym.n_value,
640 const base_offset = sym.n_value - sect.addr;
641 const filtered_relocs = filterRelocs(relocs, base_offset, base_offset + size);
642 try atom.parseRelocs(filtered_relocs, .{
643 .base_addr = sect.addr,
644 .base_offset = @intCast(i32, base_offset),
691645 .allocator = allocator,
692646 .object = self,
693647 .macho_file = macho_file,
......@@ -740,9 +694,41 @@ fn parseIntoAtom(
740694 .offset = nlist.n_value - sym.n_value,
741695 .stab = stab,
742696 });
697
698 try macho_file.atom_by_index_table.putNoClobber(allocator, sym_index, atom);
743699 }
744700
745 return atom;
701 const is_gc_root = blk: {
702 if (sect.isDontDeadStrip()) break :blk true;
703 if (sect.isDontDeadStripIfReferencesLive()) {
704 // TODO if isDontDeadStripIfReferencesLive we should analyse the edges
705 // before making it a GC root
706 break :blk true;
707 }
708 if (mem.eql(u8, "__StaticInit", sect.sectName())) break :blk true;
709 switch (sect.type_()) {
710 macho.S_MOD_INIT_FUNC_POINTERS,
711 macho.S_MOD_TERM_FUNC_POINTERS,
712 => break :blk true,
713 else => break :blk false,
714 }
715 };
716 if (is_gc_root) {
717 try macho_file.gc_roots.putNoClobber(allocator, atom, {});
718 }
719
720 if (!self.start_atoms.contains(match)) {
721 try self.start_atoms.putNoClobber(allocator, match, atom);
722 }
723
724 if (self.end_atoms.getPtr(match)) |last| {
725 last.*.next = atom;
726 atom.prev = last.*;
727 last.* = atom;
728 } else {
729 try self.end_atoms.putNoClobber(allocator, match, atom);
730 }
731 try self.contained_atoms.append(allocator, atom);
746732}
747733
748734fn parseSymtab(self: *Object) void {
test/link/macho/objcpp/build.zig+1
......@@ -16,6 +16,7 @@ pub fn build(b: *Builder) void {
1616 // TODO when we figure out how to ship framework stubs for cross-compilation,
1717 // populate paths to the sysroot here.
1818 exe.linkFramework("Foundation");
19 exe.link_gc_sections = true;
1920
2021 const run_cmd = exe.run();
2122 run_cmd.expectStdOutEqual("Hello from C++ and Zig");