| author | |
| committer | |
| log | 03feea0fb200f273dd74bf778997e6a6bead86cc |
| tree | c439ed641e01ec4d860abb181ee585a2a287494c |
| parent | d042b88c112aa919386bc76294225d4f7bd9a7b3 |
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 | }; |
| 59 | 59 | ||
| 60 | const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1)); | ||
| 61 | |||
| 60 | base: File, | 62 | base: File, |
| 61 | 63 | ||
| 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. |
| 257 | decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{}, | 259 | decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{}, |
| 258 | 260 | ||
| 261 | gc_roots: std.AutoHashMapUnmanaged(*Atom, void) = .{}, | ||
| 262 | |||
| 259 | const Entry = struct { | 263 | const 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 |
| 1165 | 1169 | ||
| 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(); |
| 1175 | 1181 | ||
| 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 | } |
| 1180 | 1187 | ||
| 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); |
| 2179 | 2186 | ||
| 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 | } | ||
| 3307 | 3310 | ||
| 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); | ||
| 3567 | 3571 | ||
| 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); | ||
| 3920 | 3923 | ||
| 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: []* |
| 5630 | 5633 | ||
| 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 such | 5635 | // 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 | } |
| 5714 | 5717 | ||
| 5718 | fn 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 | |||
| 5715 | fn updateSectionOrdinals(self: *MachO) !void { | 5901 | fn updateSectionOrdinals(self: *MachO) !void { |
| 5716 | if (!self.sections_order_dirty) return; | 5902 | if (!self.sections_order_dirty) return; |
| 5717 | 5903 | ||
| ... | @@ -5776,8 +5962,11 @@ fn writeDyldInfoData(self: *MachO) !void { | ... | @@ -5776,8 +5962,11 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 5776 | } | 5962 | } |
| 5777 | 5963 | ||
| 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() }); | ||
| 5779 | 5967 | ||
| 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; |
| 5783 | 5972 | ||
| ... | @@ -6217,10 +6406,19 @@ fn writeSymbolTable(self: *MachO) !void { | ... | @@ -6217,10 +6406,19 @@ fn writeSymbolTable(self: *MachO) !void { |
| 6217 | 6406 | ||
| 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 | } |
| 6223 | 6413 | ||
| 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 | } |
| 6292 | 6490 | ||
| 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; |
| 6296 | 6494 | ||
| 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); |
| 6306 | 6504 | ||
| 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 | } |
| 6899 | 7097 | ||
| 6900 | fn logSymtab(self: MachO) void { | 7098 | fn 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 | } |
| 6905 | 7103 | ||
| 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 | } |
| 6910 | 7108 | ||
| 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 | } |
| 6915 | 7113 | ||
| 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 | } |
| 6923 | 7121 | ||
| 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 | } |
| 6934 | 7132 | ||
| 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 | } |
| 6943 | 7141 | ||
| 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 | } |
| 6952 | 7150 | ||
| ... | @@ -6964,6 +7162,45 @@ fn logSectionOrdinals(self: MachO) void { | ... | @@ -6964,6 +7162,45 @@ fn logSectionOrdinals(self: MachO) void { |
| 6964 | } | 7162 | } |
| 6965 | } | 7163 | } |
| 6966 | 7164 | ||
| 7165 | fn 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 | |||
| 7190 | fn 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 allocated | 7205 | /// 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 { |
| 236 | 236 | ||
| 237 | const RelocContext = struct { | 237 | const 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); |
| 370 | 371 | ||
| 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 | }; |
| 530 | 531 | ||
| 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 | } |
| 652 | 656 | ||
| 657 | pub 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 | |||
| 653 | pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { | 711 | pub 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 | } |
| 348 | 355 | ||
| 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, |
| 352 | 359 | ||
| ... | @@ -355,13 +362,36 @@ const NlistWithIndex = struct { | ... | @@ -355,13 +362,36 @@ const NlistWithIndex = struct { |
| 355 | } | 362 | } |
| 356 | }; | 363 | }; |
| 357 | 364 | ||
| 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 | }); | ||
| 360 | 371 | ||
| 361 | return symbols[start..end]; | 372 | return symbols[start..end]; |
| 362 | } | 373 | } |
| 363 | }; | 374 | }; |
| 364 | 375 | ||
| 376 | fn 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 | |||
| 365 | fn filterDice( | 395 | fn 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]; |
| 424 | 454 | ||
| 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 | }; | ||
| 431 | 458 | ||
| 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() }); |
| 435 | 462 | ||
| 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 | ); |
| 456 | 483 | ||
| 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 | ); | ||
| 459 | 490 | ||
| 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; |
| 469 | 500 | ||
| 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: { |
| 479 | 508 | const local_sym_index = @intCast(u32, macho_file.locals.items.len); | |
| 480 | // If there is no symbol to refer to this atom, we create | 509 | try macho_file.locals.append(allocator, .{ |
| 481 | // a temp one, unless we already did that when working out the relocations | 510 | .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, |
| 506 | 535 | ); | |
| 507 | if (!self.start_atoms.contains(match)) { | 536 | } |
| 508 | try self.start_atoms.putNoClobber(allocator, match, atom); | ||
| 509 | } | ||
| 510 | 537 | ||
| 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 | } |
| 521 | 617 | ||
| 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 | |||
| 668 | fn parseIntoAtom( | 618 | fn 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 | } |
| 688 | 639 | ||
| 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 | } |
| 744 | 700 | ||
| 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 | } |
| 747 | 733 | ||
| 748 | fn parseSymtab(self: *Object) void { | 734 | fn 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; | ||
| 19 | 20 | ||
| 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"); |