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 {...@@ -57,6 +57,8 @@ const SystemLib = struct {
57 weak: bool = false,57 weak: bool = false,
58};58};
5959
60const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));
61
60base: File,62base: File,
6163
62/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.64/// 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 = .{},...@@ -256,6 +258,8 @@ unnamed_const_atoms: UnnamedConstTable = .{},
256/// TODO consolidate this.258/// TODO consolidate this.
257decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},259decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},
258260
261gc_roots: std.AutoHashMapUnmanaged(*Atom, void) = .{},
262
259const Entry = struct {263const Entry = struct {
260 target: Atom.Relocation.Target,264 target: Atom.Relocation.Target,
261 atom: *Atom,265 atom: *Atom,
...@@ -1165,6 +1169,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1165,6 +1169,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
11651169
1166 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;1170 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
1167 if (use_llvm or use_stage1) {1171 if (use_llvm or use_stage1) {
1172 self.logAtoms();
1173 try self.gcAtoms();
1168 try self.pruneAndSortSections();1174 try self.pruneAndSortSections();
1169 try self.allocateSegments();1175 try self.allocateSegments();
1170 try self.allocateLocals();1176 try self.allocateLocals();
...@@ -1173,9 +1179,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1173,9 +1179,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1173 try self.allocateSpecialSymbols();1179 try self.allocateSpecialSymbols();
1174 try self.allocateGlobals();1180 try self.allocateGlobals();
11751181
1176 if (build_options.enable_logging) {1182 if (build_options.enable_logging or true) {
1177 self.logSymtab();1183 self.logSymtab();
1178 self.logSectionOrdinals();1184 self.logSectionOrdinals();
1185 self.logAtoms();
1179 }1186 }
11801187
1181 if (use_llvm or use_stage1) {1188 if (use_llvm or use_stage1) {
...@@ -2177,6 +2184,7 @@ pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment:...@@ -2177,6 +2184,7 @@ pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment:
2177 try atom.code.resize(self.base.allocator, size_usize);2184 try atom.code.resize(self.base.allocator, size_usize);
2178 mem.set(u8, atom.code.items, 0);2185 mem.set(u8, atom.code.items, 0);
21792186
2187 try self.atom_by_index_table.putNoClobber(self.base.allocator, local_sym_index, atom);
2180 try self.managed_atoms.append(self.base.allocator, atom);2188 try self.managed_atoms.append(self.base.allocator, atom);
2181 return atom;2189 return atom;
2182}2190}
...@@ -3298,12 +3306,7 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -3298,12 +3306,7 @@ fn resolveDyldStubBinder(self: *MachO) !void {
3298 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);3306 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
3299 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });3307 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
3300 atom_sym.n_value = vaddr;3308 atom_sym.n_value = vaddr;
3301 } else {3309 } else try self.addAtomToSection(atom, match);
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 }
33073310
3308 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);3311 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
3309}3312}
...@@ -3564,6 +3567,7 @@ pub fn deinit(self: *MachO) void {...@@ -3564,6 +3567,7 @@ pub fn deinit(self: *MachO) void {
3564 self.symbol_resolver.deinit(self.base.allocator);3567 self.symbol_resolver.deinit(self.base.allocator);
3565 self.unresolved.deinit(self.base.allocator);3568 self.unresolved.deinit(self.base.allocator);
3566 self.tentatives.deinit(self.base.allocator);3569 self.tentatives.deinit(self.base.allocator);
3570 self.gc_roots.deinit(self.base.allocator);
35673571
3568 for (self.objects.items) |*object| {3572 for (self.objects.items) |*object| {
3569 object.deinit(self.base.allocator);3573 object.deinit(self.base.allocator);
...@@ -3916,7 +3920,6 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3916,7 +3920,6 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
3916 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);3920 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
3917 const local_sym_index = try self.allocateLocalSymbol();3921 const local_sym_index = try self.allocateLocalSymbol();
3918 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment));3922 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
3921 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{3924 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
3922 .parent_atom_index = local_sym_index,3925 .parent_atom_index = local_sym_index,
...@@ -5597,7 +5600,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*...@@ -5597,7 +5600,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*
5597 const old_idx = maybe_index.* orelse continue;5600 const old_idx = maybe_index.* orelse continue;
5598 const sect = sections[old_idx];5601 const sect = sections[old_idx];
5599 if (sect.size == 0) {5602 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() });
5601 maybe_index.* = null;5604 maybe_index.* = null;
5602 seg.inner.cmdsize -= @sizeOf(macho.section_64);5605 seg.inner.cmdsize -= @sizeOf(macho.section_64);
5603 seg.inner.nsects -= 1;5606 seg.inner.nsects -= 1;
...@@ -5630,7 +5633,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*...@@ -5630,7 +5633,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*
56305633
5631 if (seg.inner.nsects == 0 and !mem.eql(u8, "__TEXT", seg.inner.segName())) {5634 if (seg.inner.nsects == 0 and !mem.eql(u8, "__TEXT", seg.inner.segName())) {
5632 // Segment has now become empty, so mark it as such5635 // 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()});
5634 seg.inner.cmd = @intToEnum(macho.LC, 0);5637 seg.inner.cmd = @intToEnum(macho.LC, 0);
5635 maybe_seg_id.* = null;5638 maybe_seg_id.* = null;
5636 }5639 }
...@@ -5712,6 +5715,189 @@ fn pruneAndSortSections(self: *MachO) !void {...@@ -5712,6 +5715,189 @@ fn pruneAndSortSections(self: *MachO) !void {
5712 self.sections_order_dirty = false;5715 self.sections_order_dirty = false;
5713}5716}
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
5715fn updateSectionOrdinals(self: *MachO) !void {5901fn updateSectionOrdinals(self: *MachO) !void {
5716 if (!self.sections_order_dirty) return;5902 if (!self.sections_order_dirty) return;
57175903
...@@ -5776,8 +5962,11 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5776,8 +5962,11 @@ fn writeDyldInfoData(self: *MachO) !void {
5776 }5962 }
57775963
5778 const seg = self.load_commands.items[match.seg].segment;5964 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
5780 while (true) {5968 while (true) {
5969 log.warn(" ATOM %{d}", .{atom.local_sym_index});
5781 const sym = self.locals.items[atom.local_sym_index];5970 const sym = self.locals.items[atom.local_sym_index];
5782 const base_offset = sym.n_value - seg.inner.vmaddr;5971 const base_offset = sym.n_value - seg.inner.vmaddr;
57835972
...@@ -6217,10 +6406,19 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -6217,10 +6406,19 @@ fn writeSymbolTable(self: *MachO) !void {
62176406
6218 for (self.locals.items) |sym| {6407 for (self.locals.items) |sym| {
6219 if (sym.n_strx == 0) continue;6408 if (sym.n_strx == 0) continue;
6409 if (sym.n_desc == N_DESC_GCED) continue;
6220 if (self.symbol_resolver.get(sym.n_strx)) |_| continue;6410 if (self.symbol_resolver.get(sym.n_strx)) |_| continue;
6221 try locals.append(sym);6411 try locals.append(sym);
6222 }6412 }
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
6224 // TODO How do we handle null global symbols in incremental context?6422 // TODO How do we handle null global symbols in incremental context?
6225 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);6423 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
6226 defer undefs.deinit();6424 defer undefs.deinit();
...@@ -6291,7 +6489,7 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -6291,7 +6489,7 @@ fn writeSymbolTable(self: *MachO) !void {
6291 }6489 }
62926490
6293 const nlocals = locals.items.len;6491 const nlocals = locals.items.len;
6294 const nexports = self.globals.items.len;6492 const nexports = globals.items.len;
6295 const nundefs = undefs.items.len;6493 const nundefs = undefs.items.len;
62966494
6297 const locals_off = symtab.symoff;6495 const locals_off = symtab.symoff;
...@@ -6302,7 +6500,7 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -6302,7 +6500,7 @@ fn writeSymbolTable(self: *MachO) !void {
6302 const exports_off = locals_off + locals_size;6500 const exports_off = locals_off + locals_size;
6303 const exports_size = nexports * @sizeOf(macho.nlist_64);6501 const exports_size = nexports * @sizeOf(macho.nlist_64);
6304 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });6502 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
6307 const undefs_off = exports_off + exports_size;6505 const undefs_off = exports_off + exports_size;
6308 const undefs_size = nundefs * @sizeOf(macho.nlist_64);6506 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
...@@ -6898,55 +7096,55 @@ fn snapshotState(self: *MachO) !void {...@@ -6898,55 +7096,55 @@ fn snapshotState(self: *MachO) !void {
6898}7096}
68997097
6900fn logSymtab(self: MachO) void {7098fn logSymtab(self: MachO) void {
6901 log.debug("locals:", .{});7099 log.warn("locals:", .{});
6902 for (self.locals.items) |sym, id| {7100 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 });
6904 }7102 }
69057103
6906 log.debug("globals:", .{});7104 log.warn("globals:", .{});
6907 for (self.globals.items) |sym, id| {7105 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 });
6909 }7107 }
69107108
6911 log.debug("undefs:", .{});7109 log.warn("undefs:", .{});
6912 for (self.undefs.items) |sym, id| {7110 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 });
6914 }7112 }
69157113
6916 {7114 {
6917 log.debug("resolver:", .{});7115 log.warn("resolver:", .{});
6918 var it = self.symbol_resolver.iterator();7116 var it = self.symbol_resolver.iterator();
6919 while (it.next()) |entry| {7117 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.* });
6921 }7119 }
6922 }7120 }
69237121
6924 log.debug("GOT entries:", .{});7122 log.warn("GOT entries:", .{});
6925 for (self.got_entries_table.values()) |value| {7123 for (self.got_entries_table.values()) |value| {
6926 const key = self.got_entries.items[value].target;7124 const key = self.got_entries.items[value].target;
6927 const atom = self.got_entries.items[value].atom;7125 const atom = self.got_entries.items[value].atom;
6928 const n_value = self.locals.items[atom.local_sym_index].n_value;7126 const n_value = self.locals.items[atom.local_sym_index].n_value;
6929 switch (key) {7127 switch (key) {
6930 .local => |ndx| log.debug(" {d}: @{x}", .{ ndx, n_value }),7128 .local => |ndx| log.warn(" {d}: @{x}", .{ ndx, n_value }),
6931 .global => |n_strx| log.debug(" {s}: @{x}", .{ self.getString(n_strx), n_value }),7129 .global => |n_strx| log.warn(" {s}: @{x}", .{ self.getString(n_strx), n_value }),
6932 }7130 }
6933 }7131 }
69347132
6935 log.debug("__thread_ptrs entries:", .{});7133 log.warn("__thread_ptrs entries:", .{});
6936 for (self.tlv_ptr_entries_table.values()) |value| {7134 for (self.tlv_ptr_entries_table.values()) |value| {
6937 const key = self.tlv_ptr_entries.items[value].target;7135 const key = self.tlv_ptr_entries.items[value].target;
6938 const atom = self.tlv_ptr_entries.items[value].atom;7136 const atom = self.tlv_ptr_entries.items[value].atom;
6939 const n_value = self.locals.items[atom.local_sym_index].n_value;7137 const n_value = self.locals.items[atom.local_sym_index].n_value;
6940 assert(key == .global);7138 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 });
6942 }7140 }
69437141
6944 log.debug("stubs:", .{});7142 log.warn("stubs:", .{});
6945 for (self.stubs_table.keys()) |key| {7143 for (self.stubs_table.keys()) |key| {
6946 const value = self.stubs_table.get(key).?;7144 const value = self.stubs_table.get(key).?;
6947 const atom = self.stubs.items[value];7145 const atom = self.stubs.items[value];
6948 const sym = self.locals.items[atom.local_sym_index];7146 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 });
6950 }7148 }
6951}7149}
69527150
...@@ -6964,6 +7162,45 @@ fn logSectionOrdinals(self: MachO) void {...@@ -6964,6 +7162,45 @@ fn logSectionOrdinals(self: MachO) void {
6964 }7162 }
6965}7163}
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
6967/// Since `os.copy_file_range` cannot be used when copying overlapping ranges within the same file,7204/// Since `os.copy_file_range` cannot be used when copying overlapping ranges within the same file,
6968/// and since `File.copyRangeAll` uses `os.copy_file_range` under-the-hood, we use heap allocated7205/// and since `File.copyRangeAll` uses `os.copy_file_range` under-the-hood, we use heap allocated
6969/// buffers on all hosts except Linux (if `copy_file_range` syscall is available).7206/// 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 {...@@ -236,6 +236,7 @@ pub fn freeListEligible(self: Atom, macho_file: MachO) bool {
236236
237const RelocContext = struct {237const RelocContext = struct {
238 base_addr: u64 = 0,238 base_addr: u64 = 0,
239 base_offset: i32 = 0,
239 allocator: Allocator,240 allocator: Allocator,
240 object: *Object,241 object: *Object,
241 macho_file: *MachO,242 macho_file: *MachO,
...@@ -366,7 +367,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -366,7 +367,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
366 ) orelse unreachable;367 ) orelse unreachable;
367 break :target Relocation.Target{ .global = n_strx };368 break :target Relocation.Target{ .global = n_strx };
368 };369 };
369 const offset = @intCast(u32, rel.r_address);370 const offset = @intCast(u32, rel.r_address - context.base_offset);
370371
371 switch (arch) {372 switch (arch) {
372 .aarch64 => {373 .aarch64 => {
...@@ -487,7 +488,7 @@ fn addPtrBindingOrRebase(...@@ -487,7 +488,7 @@ fn addPtrBindingOrRebase(
487 .global => |n_strx| {488 .global => |n_strx| {
488 try self.bindings.append(context.allocator, .{489 try self.bindings.append(context.allocator, .{
489 .n_strx = n_strx,490 .n_strx = n_strx,
490 .offset = @intCast(u32, rel.r_address),491 .offset = @intCast(u32, rel.r_address - context.base_offset),
491 });492 });
492 },493 },
493 .local => {494 .local => {
...@@ -529,7 +530,10 @@ fn addPtrBindingOrRebase(...@@ -529,7 +530,10 @@ fn addPtrBindingOrRebase(
529 };530 };
530531
531 if (should_rebase) {532 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 );
533 }537 }
534 },538 },
535 }539 }
...@@ -650,6 +654,60 @@ fn addStub(target: Relocation.Target, context: RelocContext) !void {...@@ -650,6 +654,60 @@ fn addStub(target: Relocation.Target, context: RelocContext) !void {
650 context.macho_file.stubs.items[stub_index] = atom;654 context.macho_file.stubs.items[stub_index] = atom;
651}655}
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
653pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {711pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
654 const tracy = trace(@src());712 const tracy = trace(@src());
655 defer tracy.end();713 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 {...@@ -176,6 +176,13 @@ pub fn free(self: *Object, allocator: Allocator, macho_file: *MachO) void {
176 .n_desc = 0,176 .n_desc = 0,
177 .n_value = 0,177 .n_value = 0,
178 };178 };
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
179 atom.local_sym_index = 0;186 atom.local_sym_index = 0;
180 }187 }
181 if (atom == last_atom) {188 if (atom == last_atom) {
...@@ -346,7 +353,7 @@ const NlistWithIndex = struct {...@@ -346,7 +353,7 @@ const NlistWithIndex = struct {
346 }353 }
347 }354 }
348355
349 fn filterInSection(symbols: []NlistWithIndex, sect: macho.section_64) []NlistWithIndex {356 fn filterByAddress(symbols: []NlistWithIndex, start_addr: u64, end_addr: u64) []NlistWithIndex {
350 const Predicate = struct {357 const Predicate = struct {
351 addr: u64,358 addr: u64,
352359
...@@ -355,13 +362,36 @@ const NlistWithIndex = struct {...@@ -355,13 +362,36 @@ const NlistWithIndex = struct {
355 }362 }
356 };363 };
357364
358 const start = MachO.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr });365 const start = MachO.findFirst(NlistWithIndex, symbols, 0, Predicate{
359 const end = MachO.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size });366 .addr = start_addr,
367 });
368 const end = MachO.findFirst(NlistWithIndex, symbols, start, Predicate{
369 .addr = end_addr,
370 });
360371
361 return symbols[start..end];372 return symbols[start..end];
362 }373 }
363};374};
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
365fn filterDice(395fn filterDice(
366 dices: []const macho.data_in_code_entry,396 dices: []const macho.data_in_code_entry,
367 start_addr: u64,397 start_addr: u64,
...@@ -422,16 +452,13 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -422,16 +452,13 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
422 // We only care about defined symbols, so filter every other out.452 // We only care about defined symbols, so filter every other out.
423 const sorted_nlists = sorted_all_nlists.items[0..iundefsym];453 const sorted_nlists = sorted_all_nlists.items[0..iundefsym];
424454
425 const dead_strip = blk: {455 const dead_strip = macho_file.base.options.gc_sections orelse false;
426 const dead_strip = macho_file.base.options.gc_sections orelse break :blk false;456 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0 and
427 if (dead_strip or macho_file.base.options.optimize_mode != .Debug)457 (macho_file.base.options.optimize_mode != .Debug or dead_strip);
428 break :blk self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
429 break :blk false;
430 };
431458
432 for (seg.sections.items) |sect, id| {459 for (seg.sections.items) |sect, id| {
433 const sect_id = @intCast(u8, id);460 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
436 // Get matching segment/section in the final artifact.463 // Get matching segment/section in the final artifact.
437 const match = (try macho_file.getMatchingSection(sect)) orelse {464 const match = (try macho_file.getMatchingSection(sect)) orelse {
...@@ -455,7 +482,11 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -455,7 +482,11 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
455 );482 );
456483
457 // Symbols within this section only.484 // 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
460 macho_file.has_dices = macho_file.has_dices or blk: {491 macho_file.has_dices = macho_file.has_dices or blk: {
461 if (self.text_section_index) |index| {492 if (self.text_section_index) |index| {
...@@ -467,204 +498,123 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -467,204 +498,123 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
467 };498 };
468 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;499 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;
469500
470 if (dead_strip) blk: {501 if (subsections_via_symbols and filtered_nlists.len > 0) {
471 if (filtered_nlists.len == 0) break :blk; // nothing to split
472
473 // If the first nlist does not match the start of the section,502 // If the first nlist does not match the start of the section,
474 // then we need to encapsulate the memory range [section start, first symbol)503 // then we need to encapsulate the memory range [section start, first symbol)
475 // as a temporary symbol and insert the matching Atom.504 // as a temporary symbol and insert the matching Atom.
476 const first_nlist = filtered_nlists[0].nlist;505 const first_nlist = filtered_nlists[0].nlist;
477 if (first_nlist.n_value > sect.addr) {}506 if (first_nlist.n_value > sect.addr) {
478 }507 const local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
479508 const local_sym_index = @intCast(u32, macho_file.locals.items.len);
480 // If there is no symbol to refer to this atom, we create509 try macho_file.locals.append(allocator, .{
481 // a temp one, unless we already did that when working out the relocations510 .n_strx = 0,
482 // of other atoms.511 .n_type = macho.N_SECT,
483 const local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {512 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
484 const local_sym_index = @intCast(u32, macho_file.locals.items.len);513 .n_desc = 0,
485 try macho_file.locals.append(allocator, .{514 .n_value = sect.addr,
486 .n_strx = 0,515 });
487 .n_type = macho.N_SECT,516 try self.sections_as_symbols.putNoClobber(allocator, sect_id, local_sym_index);
488 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),517 break :blk local_sym_index;
489 .n_desc = 0,518 };
490 .n_value = sect.addr,519 const atom_size = first_nlist.n_value - sect.addr;
491 });520 const atom_code: ?[]const u8 = if (code) |cc|
492 try self.sections_as_symbols.putNoClobber(allocator, sect_id, local_sym_index);521 cc[0..atom_size]
493 break :blk local_sym_index;522 else
494 };523 null;
495 const atom = try self.parseIntoAtom(524 try self.parseIntoAtom(
496 allocator,525 allocator,
497 local_sym_index,526 local_sym_index,
498 sect.size,527 atom_size,
499 sect.@"align",528 sect.@"align",
500 code,529 atom_code,
501 relocs,530 relocs,
502 filtered_nlists,531 &.{},
503 match,532 match,
504 macho_file,533 sect,
505 );534 macho_file,
506535 );
507 if (!self.start_atoms.contains(match)) {536 }
508 try self.start_atoms.putNoClobber(allocator, match, atom);
509 }
510537
511 if (self.end_atoms.getPtr(match)) |last| {538 var next_nlist_count: usize = 0;
512 last.*.next = atom;539 while (next_nlist_count < filtered_nlists.len) {
513 atom.prev = last.*;540 const next_nlist = filtered_nlists[next_nlist_count];
514 last.* = atom;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 }
515 } else {586 } 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 );
517 }614 }
518 try self.contained_atoms.append(allocator, atom);
519 }615 }
520}616}
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
668fn parseIntoAtom(618fn parseIntoAtom(
669 self: *Object,619 self: *Object,
670 allocator: Allocator,620 allocator: Allocator,
...@@ -675,8 +625,9 @@ fn parseIntoAtom(...@@ -675,8 +625,9 @@ fn parseIntoAtom(
675 relocs: []const macho.relocation_info,625 relocs: []const macho.relocation_info,
676 nlists: []const NlistWithIndex,626 nlists: []const NlistWithIndex,
677 match: MatchingSection,627 match: MatchingSection,
628 sect: macho.section_64,
678 macho_file: *MachO,629 macho_file: *MachO,
679) !*Atom {630) !void {
680 const sym = macho_file.locals.items[local_sym_index];631 const sym = macho_file.locals.items[local_sym_index];
681 const align_pow_2 = try math.powi(u32, 2, alignment);632 const align_pow_2 = try math.powi(u32, 2, alignment);
682 const aligned_size = mem.alignForwardGeneric(u64, size, align_pow_2);633 const aligned_size = mem.alignForwardGeneric(u64, size, align_pow_2);
...@@ -686,8 +637,11 @@ fn parseIntoAtom(...@@ -686,8 +637,11 @@ fn parseIntoAtom(
686 mem.copy(u8, atom.code.items, cc);637 mem.copy(u8, atom.code.items, cc);
687 }638 }
688639
689 try atom.parseRelocs(relocs, .{640 const base_offset = sym.n_value - sect.addr;
690 .base_addr = sym.n_value,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),
691 .allocator = allocator,645 .allocator = allocator,
692 .object = self,646 .object = self,
693 .macho_file = macho_file,647 .macho_file = macho_file,
...@@ -740,9 +694,41 @@ fn parseIntoAtom(...@@ -740,9 +694,41 @@ fn parseIntoAtom(
740 .offset = nlist.n_value - sym.n_value,694 .offset = nlist.n_value - sym.n_value,
741 .stab = stab,695 .stab = stab,
742 });696 });
697
698 try macho_file.atom_by_index_table.putNoClobber(allocator, sym_index, atom);
743 }699 }
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);
746}732}
747733
748fn parseSymtab(self: *Object) void {734fn parseSymtab(self: *Object) void {
test/link/macho/objcpp/build.zig+1
...@@ -16,6 +16,7 @@ pub fn build(b: *Builder) void {...@@ -16,6 +16,7 @@ pub fn build(b: *Builder) void {
16 // TODO when we figure out how to ship framework stubs for cross-compilation,16 // TODO when we figure out how to ship framework stubs for cross-compilation,
17 // populate paths to the sysroot here.17 // populate paths to the sysroot here.
18 exe.linkFramework("Foundation");18 exe.linkFramework("Foundation");
19 exe.link_gc_sections = true;
1920
20 const run_cmd = exe.run();21 const run_cmd = exe.run();
21 run_cmd.expectStdOutEqual("Hello from C++ and Zig");22 run_cmd.expectStdOutEqual("Hello from C++ and Zig");