authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-18 22:23:30+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-07-18 22:23:30+02:00
log16604a93b9159fafec3528457366ca146bf29ce5
treec502d73aae538298e45f5703d43a7a96a73afe11
parente4f5dada615c103bfe656c9fcd8f28df5f5ef9d1
parentf5a941b3d6238222f43da568c96cfa5b3e56ce5d
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #20650 from ziglang/parallel-macho

The tale of parallel MachO: part 1

28 files changed, 4696 insertions(+), 3356 deletions(-)

build.zig+1-1
......@@ -620,7 +620,7 @@ fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.St
620620 .root_source_file = b.path("src/main.zig"),
621621 .target = options.target,
622622 .optimize = options.optimize,
623 .max_rss = 7_000_000_000,
623 .max_rss = 7_100_000_000,
624624 .strip = options.strip,
625625 .sanitize_thread = options.sanitize_thread,
626626 .single_threaded = options.single_threaded,
src/Sema.zig+1-2
......@@ -17787,8 +17787,7 @@ fn zirBuiltinSrc(
1778717787 };
1778817788
1778917789 const file_name_val = v: {
17790 // The compiler must not call realpath anywhere.
17791 const file_name = try fn_owner_decl.getFileScope(mod).fullPath(sema.arena);
17790 const file_name = fn_owner_decl.getFileScope(mod).sub_file_path;
1779217791 const array_ty = try pt.intern(.{ .array_type = .{
1779317792 .len = file_name.len,
1779417793 .sentinel = .zero_u8,
src/arch/x86_64/CodeGen.zig+6-4
......@@ -12362,8 +12362,9 @@ fn genCall(self: *Self, info: union(enum) {
1236212362 try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }, .{});
1236312363 try self.asmRegister(.{ ._, .call }, .rax);
1236412364 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
12365 const sym_index = try macho_file.getZigObject().?.getOrCreateMetadataForDecl(macho_file, func.owner_decl);
12366 const sym = macho_file.getSymbol(sym_index);
12365 const zo = macho_file.getZigObject().?;
12366 const sym_index = try zo.getOrCreateMetadataForDecl(macho_file, func.owner_decl);
12367 const sym = zo.symbols.items[sym_index];
1236712368 try self.genSetReg(
1236812369 .rax,
1236912370 Type.usize,
......@@ -15396,9 +15397,10 @@ fn genLazySymbolRef(
1539615397 else => unreachable,
1539715398 }
1539815399 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
15399 const sym_index = macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_sym) catch |err|
15400 const zo = macho_file.getZigObject().?;
15401 const sym_index = zo.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_sym) catch |err|
1540015402 return self.fail("{s} creating lazy symbol", .{@errorName(err)});
15401 const sym = macho_file.getSymbol(sym_index);
15403 const sym = zo.symbols.items[sym_index];
1540215404 switch (tag) {
1540315405 .lea, .call => try self.genSetReg(
1540415406 reg,
src/arch/x86_64/Emit.zig+8-8
......@@ -51,12 +51,12 @@ pub fn emitMir(emit: *Emit) Error!void {
5151 });
5252 } else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| {
5353 // Add relocation to the decl.
54 const atom = macho_file.getSymbol(symbol.atom_index).getAtom(macho_file).?;
55 const sym_index = macho_file.getZigObject().?.symbols.items[symbol.sym_index];
54 const zo = macho_file.getZigObject().?;
55 const atom = zo.symbols.items[symbol.atom_index].getAtom(macho_file).?;
5656 try atom.addReloc(macho_file, .{
5757 .tag = .@"extern",
5858 .offset = end_offset - 4,
59 .target = sym_index,
59 .target = symbol.sym_index,
6060 .addend = 0,
6161 .type = .branch,
6262 .meta = .{
......@@ -160,11 +160,11 @@ pub fn emitMir(emit: *Emit) Error!void {
160160 .Obj => true,
161161 .Lib => emit.lower.link_mode == .static,
162162 };
163 const atom = macho_file.getSymbol(data.atom_index).getAtom(macho_file).?;
164 const sym_index = macho_file.getZigObject().?.symbols.items[data.sym_index];
165 const sym = macho_file.getSymbol(sym_index);
163 const zo = macho_file.getZigObject().?;
164 const atom = zo.symbols.items[data.atom_index].getAtom(macho_file).?;
165 const sym = &zo.symbols.items[data.sym_index];
166166 if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
167 _ = try sym.getOrCreateZigGotEntry(sym_index, macho_file);
167 _ = try sym.getOrCreateZigGotEntry(data.sym_index, macho_file);
168168 }
169169 const @"type": link.File.MachO.Relocation.Type = if (sym.flags.needs_zig_got and !is_obj_or_static_lib)
170170 .zig_got_load
......@@ -179,7 +179,7 @@ pub fn emitMir(emit: *Emit) Error!void {
179179 try atom.addReloc(macho_file, .{
180180 .tag = .@"extern",
181181 .offset = @intCast(end_offset - 4),
182 .target = sym_index,
182 .target = data.sym_index,
183183 .addend = 0,
184184 .type = @"type",
185185 .meta = .{
src/arch/x86_64/Lower.zig+2-2
......@@ -425,8 +425,8 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
425425 else => unreachable,
426426 };
427427 } else if (lower.bin_file.cast(link.File.MachO)) |macho_file| {
428 const sym_index = macho_file.getZigObject().?.symbols.items[sym.sym_index];
429 const macho_sym = macho_file.getSymbol(sym_index);
428 const zo = macho_file.getZigObject().?;
429 const macho_sym = zo.symbols.items[sym.sym_index];
430430
431431 if (macho_sym.flags.tlv) {
432432 _ = lower.reloc(.{ .linker_reloc = sym });
src/codegen.zig+5-4
......@@ -901,15 +901,16 @@ fn genDeclRef(
901901 }
902902 return GenResult.mcv(.{ .load_symbol = sym.esym_index });
903903 } else if (lf.cast(link.File.MachO)) |macho_file| {
904 const zo = macho_file.getZigObject().?;
904905 if (is_extern) {
905906 const name = decl.name.toSlice(ip);
906907 const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null;
907908 const sym_index = try macho_file.getGlobalSymbol(name, lib_name);
908 macho_file.getSymbol(macho_file.getZigObject().?.symbols.items[sym_index]).flags.needs_got = true;
909 zo.symbols.items[sym_index].flags.needs_got = true;
909910 return GenResult.mcv(.{ .load_symbol = sym_index });
910911 }
911 const sym_index = try macho_file.getZigObject().?.getOrCreateMetadataForDecl(macho_file, decl_index);
912 const sym = macho_file.getSymbol(sym_index);
912 const sym_index = try zo.getOrCreateMetadataForDecl(macho_file, decl_index);
913 const sym = zo.symbols.items[sym_index];
913914 if (is_threadlocal) {
914915 return GenResult.mcv(.{ .load_tlv = sym.nlist_idx });
915916 }
......@@ -956,7 +957,7 @@ fn genUnnamedConst(
956957 },
957958 .macho => {
958959 const macho_file = lf.cast(link.File.MachO).?;
959 const local = macho_file.getSymbol(local_sym_index);
960 const local = macho_file.getZigObject().?.symbols.items[local_sym_index];
960961 return GenResult.mcv(.{ .load_symbol = local.nlist_idx });
961962 },
962963 .coff => {
src/link/MachO.zig+664-1001
......@@ -22,16 +22,11 @@ dylibs: std.ArrayListUnmanaged(File.Index) = .{},
2222segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
2323sections: std.MultiArrayList(Section) = .{},
2424
25symbols: std.ArrayListUnmanaged(Symbol) = .{},
26symbols_extra: std.ArrayListUnmanaged(u32) = .{},
27symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
28globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
25resolver: SymbolResolver = .{},
2926/// This table will be populated after `scanRelocs` has run.
3027/// Key is symbol index.
31undefs: std.AutoHashMapUnmanaged(Symbol.Index, std.ArrayListUnmanaged(Atom.Index)) = .{},
32/// Global symbols we need to resolve for the link to succeed.
33undefined_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
34boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
28undefs: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{},
29dupes: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{},
3530
3631dyld_info_cmd: macho.dyld_info_command = .{},
3732symtab_cmd: macho.symtab_command = .{},
......@@ -55,22 +50,7 @@ eh_frame_sect_index: ?u8 = null,
5550unwind_info_sect_index: ?u8 = null,
5651objc_stubs_sect_index: ?u8 = null,
5752
58mh_execute_header_index: ?Symbol.Index = null,
59mh_dylib_header_index: ?Symbol.Index = null,
60dyld_private_index: ?Symbol.Index = null,
61dyld_stub_binder_index: ?Symbol.Index = null,
62dso_handle_index: ?Symbol.Index = null,
63objc_msg_send_index: ?Symbol.Index = null,
64entry_index: ?Symbol.Index = null,
65
66/// List of atoms that are either synthetic or map directly to the Zig source program.
67atoms: std.ArrayListUnmanaged(Atom) = .{},
68atoms_extra: std.ArrayListUnmanaged(u32) = .{},
6953thunks: std.ArrayListUnmanaged(Thunk) = .{},
70unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{},
71
72/// String interning table
73strings: StringTable = .{},
7454
7555/// Output synthetic sections
7656symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
......@@ -83,12 +63,13 @@ stubs_helper: StubsHelperSection = .{},
8363objc_stubs: ObjcStubsSection = .{},
8464la_symbol_ptr: LaSymbolPtrSection = .{},
8565tlv_ptr: TlvPtrSection = .{},
86rebase: RebaseSection = .{},
87bind: BindSection = .{},
88weak_bind: WeakBindSection = .{},
89lazy_bind: LazyBindSection = .{},
90export_trie: ExportTrieSection = .{},
66rebase: Rebase = .{},
67bind: Bind = .{},
68weak_bind: WeakBind = .{},
69lazy_bind: LazyBind = .{},
70export_trie: ExportTrie = .{},
9171unwind_info: UnwindInfo = .{},
72data_in_code: DataInCode = .{},
9273
9374/// Tracked loadable segments during incremental linking.
9475zig_text_seg_index: ?u8 = null,
......@@ -245,15 +226,8 @@ pub fn createEmpty(
245226
246227 // Append null file
247228 try self.files.append(gpa, .null);
248 // Atom at index 0 is reserved as null atom
249 try self.atoms.append(gpa, .{});
250 try self.atoms_extra.append(gpa, 0);
251229 // Append empty string to string tables
252 try self.strings.buffer.append(gpa, 0);
253230 try self.strtab.append(gpa, 0);
254 // Append null symbols
255 try self.symbols.append(gpa, .{});
256 try self.symbols_extra.append(gpa, 0);
257231
258232 if (opt_zcu) |zcu| {
259233 if (!use_llvm) {
......@@ -317,15 +291,20 @@ pub fn deinit(self: *MachO) void {
317291 self.dylibs.deinit(gpa);
318292
319293 self.segments.deinit(gpa);
320 for (self.sections.items(.atoms)) |*list| {
321 list.deinit(gpa);
294 for (
295 self.sections.items(.atoms),
296 self.sections.items(.out),
297 self.sections.items(.thunks),
298 self.sections.items(.relocs),
299 ) |*atoms, *out, *thnks, *relocs| {
300 atoms.deinit(gpa);
301 out.deinit(gpa);
302 thnks.deinit(gpa);
303 relocs.deinit(gpa);
322304 }
323305 self.sections.deinit(gpa);
324306
325 self.symbols.deinit(gpa);
326 self.symbols_extra.deinit(gpa);
327 self.symbols_free_list.deinit(gpa);
328 self.globals.deinit(gpa);
307 self.resolver.deinit(gpa);
329308 {
330309 var it = self.undefs.iterator();
331310 while (it.next()) |entry| {
......@@ -333,10 +312,14 @@ pub fn deinit(self: *MachO) void {
333312 }
334313 self.undefs.deinit(gpa);
335314 }
336 self.undefined_symbols.deinit(gpa);
337 self.boundary_symbols.deinit(gpa);
315 {
316 var it = self.dupes.iterator();
317 while (it.next()) |entry| {
318 entry.value_ptr.deinit(gpa);
319 }
320 self.dupes.deinit(gpa);
321 }
338322
339 self.strings.deinit(gpa);
340323 self.symtab.deinit(gpa);
341324 self.strtab.deinit(gpa);
342325 self.got.deinit(gpa);
......@@ -350,14 +333,9 @@ pub fn deinit(self: *MachO) void {
350333 self.lazy_bind.deinit(gpa);
351334 self.export_trie.deinit(gpa);
352335 self.unwind_info.deinit(gpa);
336 self.data_in_code.deinit(gpa);
353337
354 self.atoms.deinit(gpa);
355 self.atoms_extra.deinit(gpa);
356 for (self.thunks.items) |*thunk| {
357 thunk.deinit(gpa);
358 }
359338 self.thunks.deinit(gpa);
360 self.unwind_records.deinit(gpa);
361339}
362340
363341pub fn flush(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
......@@ -535,17 +513,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
535513 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
536514 self.files.set(index, .{ .internal = .{ .index = index } });
537515 self.internal_object = index;
516 const object = self.getInternalObject().?;
517 try object.init(gpa);
518 try object.initSymbols(self);
538519 }
539520
540 try self.addUndefinedGlobals();
541521 try self.resolveSymbols();
542 try self.parseDebugInfo();
543 try self.resolveSyntheticSymbols();
544
545 try self.convertTentativeDefinitions();
546 try self.createObjcSections();
522 try self.convertTentativeDefsAndResolveSpecialSymbols();
547523 try self.dedupLiterals();
548 try self.claimUnresolved();
549524
550525 if (self.base.gc_sections) {
551526 try dead_strip.gcAtoms(self);
......@@ -567,6 +542,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
567542 dylib.ordinal = @intCast(ord);
568543 }
569544
545 self.claimUnresolved();
546
570547 self.scanRelocs() catch |err| switch (err) {
571548 error.HasUndefinedSymbols => return error.FlushFailure,
572549 else => |e| {
......@@ -580,92 +557,38 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
580557 try self.sortSections();
581558 try self.addAtomsToSections();
582559 try self.calcSectionSizes();
560
583561 try self.generateUnwindInfo();
584 try self.initSegments();
585562
563 try self.initSegments();
586564 try self.allocateSections();
587565 self.allocateSegments();
588566 self.allocateSyntheticSymbols();
589 try self.allocateLinkeditSegment();
590567
591568 if (build_options.enable_logging) {
592569 state_log.debug("{}", .{self.dumpState()});
593570 }
594571
595 try self.initDyldInfoSections();
596
597572 // Beyond this point, everything has been allocated a virtual address and we can resolve
598573 // the relocations, and commit objects to file.
599 if (self.getZigObject()) |zo| {
600 var has_resolve_error = false;
601
602 for (zo.atoms.items) |atom_index| {
603 const atom = self.getAtom(atom_index) orelse continue;
604 if (!atom.flags.alive) continue;
605 const sect = &self.sections.items(.header)[atom.out_n_sect];
606 if (sect.isZerofill()) continue;
607 if (!self.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately
608 if (atom.getRelocs(self).len == 0) continue;
609 // TODO: we will resolve and write ZigObject's TLS data twice:
610 // once here, and once in writeAtoms
611 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
612 const code = try gpa.alloc(u8, atom_size);
613 defer gpa.free(code);
614 atom.getData(self, code) catch |err| switch (err) {
615 error.InputOutput => {
616 try self.reportUnexpectedError("fetching code for '{s}' failed", .{
617 atom.getName(self),
618 });
619 return error.FlushFailure;
620 },
621 else => |e| {
622 try self.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
623 atom.getName(self),
624 @errorName(e),
625 });
626 return error.FlushFailure;
627 },
628 };
629 const file_offset = sect.offset + atom.value;
630 atom.resolveRelocs(self, code) catch |err| switch (err) {
631 error.ResolveFailed => has_resolve_error = true,
632 else => |e| {
633 try self.reportUnexpectedError("unexpected error while resolving relocations", .{});
634 return e;
635 },
636 };
637 try self.base.file.?.pwriteAll(code, file_offset);
638 }
574 try self.resizeSections();
639575
640 if (has_resolve_error) return error.FlushFailure;
576 if (self.getZigObject()) |zo| {
577 zo.resolveRelocs(self) catch |err| switch (err) {
578 error.ResolveFailed => return error.FlushFailure,
579 else => |e| return e,
580 };
641581 }
642
643 self.writeAtoms() catch |err| switch (err) {
644 error.ResolveFailed => return error.FlushFailure,
645 else => |e| {
646 try self.reportUnexpectedError("unexpected error while resolving relocations", .{});
647 return e;
648 },
582 self.writeSectionsAndUpdateLinkeditSizes() catch |err| {
583 switch (err) {
584 error.ResolveFailed => return error.FlushFailure,
585 else => |e| return e,
586 }
649587 };
650 try self.writeUnwindInfo();
651 try self.finalizeDyldInfoSections();
652 try self.writeSyntheticSections();
653
654 var off = math.cast(u32, self.getLinkeditSegment().fileoff) orelse return error.Overflow;
655 off = try self.writeDyldInfoSections(off);
656 off = mem.alignForward(u32, off, @alignOf(u64));
657 off = try self.writeFunctionStarts(off);
658 off = mem.alignForward(u32, off, @alignOf(u64));
659 off = try self.writeDataInCode(self.getTextSegment().vmaddr, off);
660 try self.calcSymtabSize();
661 off = mem.alignForward(u32, off, @alignOf(u64));
662 off = try self.writeSymtab(off);
663 off = mem.alignForward(u32, off, @alignOf(u32));
664 off = try self.writeIndsymtab(off);
665 off = mem.alignForward(u32, off, @alignOf(u64));
666 off = try self.writeStrtab(off);
667588
668 self.getLinkeditSegment().filesize = off - self.getLinkeditSegment().fileoff;
589 try self.writeSectionsToFile();
590 try self.allocateLinkeditSegment();
591 try self.writeLinkeditSectionsToFile();
669592
670593 var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: {
671594 // Preallocate space for the code signature.
......@@ -982,12 +905,15 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void {
982905 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
983906 };
984907 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
985 self.files.set(index, .{ .object = .{
986 .path = try gpa.dupe(u8, path),
987 .file_handle = handle,
988 .mtime = mtime,
989 .index = index,
990 } });
908 self.files.set(index, .{
909 .object = .{
910 .offset = 0, // TODO FAT objects
911 .path = try gpa.dupe(u8, path),
912 .file_handle = handle,
913 .mtime = mtime,
914 .index = index,
915 },
916 });
991917 try self.objects.append(gpa, index);
992918
993919 const object = self.getFile(index).?.object;
......@@ -1327,35 +1253,6 @@ fn parseDependentDylibs(self: *MachO) !void {
13271253 if (has_errors) return error.MissingLibraryDependencies;
13281254}
13291255
1330pub fn addUndefinedGlobals(self: *MachO) !void {
1331 const gpa = self.base.comp.gpa;
1332
1333 try self.undefined_symbols.ensureUnusedCapacity(gpa, self.base.comp.force_undefined_symbols.keys().len);
1334 for (self.base.comp.force_undefined_symbols.keys()) |name| {
1335 const off = try self.strings.insert(gpa, name);
1336 const gop = try self.getOrCreateGlobal(off);
1337 self.undefined_symbols.appendAssumeCapacity(gop.index);
1338 }
1339
1340 if (!self.base.isDynLib() and self.entry_name != null) {
1341 const off = try self.strings.insert(gpa, self.entry_name.?);
1342 const gop = try self.getOrCreateGlobal(off);
1343 self.entry_index = gop.index;
1344 }
1345
1346 {
1347 const off = try self.strings.insert(gpa, "dyld_stub_binder");
1348 const gop = try self.getOrCreateGlobal(off);
1349 self.dyld_stub_binder_index = gop.index;
1350 }
1351
1352 {
1353 const off = try self.strings.insert(gpa, "_objc_msgSend");
1354 const gop = try self.getOrCreateGlobal(off);
1355 self.objc_msg_send_index = gop.index;
1356 }
1357}
1358
13591256/// When resolving symbols, we approach the problem similarly to `mold`.
13601257/// 1. Resolve symbols across all objects (including those preemptively extracted archives).
13611258/// 2. Resolve symbols across all shared objects.
......@@ -1368,18 +1265,17 @@ pub fn resolveSymbols(self: *MachO) !void {
13681265 defer tracy.end();
13691266
13701267 // Resolve symbols in the ZigObject. For now, we assume that it's always live.
1371 if (self.getZigObject()) |zo| zo.asFile().resolveSymbols(self);
1268 if (self.getZigObject()) |zo| try zo.asFile().resolveSymbols(self);
13721269 // Resolve symbols on the set of all objects and shared objects (even if some are unneeded).
1373 for (self.objects.items) |index| self.getFile(index).?.resolveSymbols(self);
1374 for (self.dylibs.items) |index| self.getFile(index).?.resolveSymbols(self);
1270 for (self.objects.items) |index| try self.getFile(index).?.resolveSymbols(self);
1271 for (self.dylibs.items) |index| try self.getFile(index).?.resolveSymbols(self);
1272 if (self.getInternalObject()) |obj| try obj.resolveSymbols(self);
13751273
13761274 // Mark live objects.
13771275 self.markLive();
13781276
13791277 // Reset state of all globals after marking live objects.
1380 if (self.getZigObject()) |zo| zo.asFile().resetGlobals(self);
1381 for (self.objects.items) |index| self.getFile(index).?.resetGlobals(self);
1382 for (self.dylibs.items) |index| self.getFile(index).?.resetGlobals(self);
1278 self.resolver.reset();
13831279
13841280 // Prune dead objects.
13851281 var i: usize = 0;
......@@ -1393,37 +1289,26 @@ pub fn resolveSymbols(self: *MachO) !void {
13931289 }
13941290
13951291 // Re-resolve the symbols.
1396 if (self.getZigObject()) |zo| zo.resolveSymbols(self);
1397 for (self.objects.items) |index| self.getFile(index).?.resolveSymbols(self);
1398 for (self.dylibs.items) |index| self.getFile(index).?.resolveSymbols(self);
1292 if (self.getZigObject()) |zo| try zo.resolveSymbols(self);
1293 for (self.objects.items) |index| try self.getFile(index).?.resolveSymbols(self);
1294 for (self.dylibs.items) |index| try self.getFile(index).?.resolveSymbols(self);
1295 if (self.getInternalObject()) |obj| try obj.resolveSymbols(self);
1296
1297 // Merge symbol visibility
1298 if (self.getZigObject()) |zo| zo.mergeSymbolVisibility(self);
1299 for (self.objects.items) |index| self.getFile(index).?.object.mergeSymbolVisibility(self);
13991300}
14001301
14011302fn markLive(self: *MachO) void {
14021303 const tracy = trace(@src());
14031304 defer tracy.end();
14041305
1405 for (self.undefined_symbols.items) |index| {
1406 if (self.getSymbol(index).getFile(self)) |file| {
1407 if (file == .object) file.object.alive = true;
1408 }
1409 }
1410 if (self.entry_index) |index| {
1411 const sym = self.getSymbol(index);
1412 if (sym.getFile(self)) |file| {
1413 if (file == .object) file.object.alive = true;
1414 }
1415 }
14161306 if (self.getZigObject()) |zo| zo.markLive(self);
14171307 for (self.objects.items) |index| {
14181308 const object = self.getFile(index).?.object;
14191309 if (object.alive) object.markLive(self);
14201310 }
1421}
1422
1423pub fn parseDebugInfo(self: *MachO) !void {
1424 for (self.objects.items) |index| {
1425 try self.getFile(index).?.object.parseDebugInfo(self);
1426 }
1311 if (self.getInternalObject()) |obj| obj.markLive(self);
14271312}
14281313
14291314fn resolveSyntheticSymbols(self: *MachO) !void {
......@@ -1473,10 +1358,14 @@ fn resolveSyntheticSymbols(self: *MachO) !void {
14731358 }
14741359}
14751360
1476fn convertTentativeDefinitions(self: *MachO) !void {
1361fn convertTentativeDefsAndResolveSpecialSymbols(self: *MachO) !void {
14771362 for (self.objects.items) |index| {
14781363 try self.getFile(index).?.object.convertTentativeDefinitions(self);
14791364 }
1365 if (self.getInternalObject()) |obj| {
1366 try obj.resolveBoundarySymbols(self);
1367 try obj.resolveObjcMsgSendSymbols(self);
1368 }
14801369}
14811370
14821371fn createObjcSections(self: *MachO) !void {
......@@ -1514,6 +1403,9 @@ fn createObjcSections(self: *MachO) !void {
15141403}
15151404
15161405pub fn dedupLiterals(self: *MachO) !void {
1406 const tracy = trace(@src());
1407 defer tracy.end();
1408
15171409 const gpa = self.base.comp.gpa;
15181410 var lp: LiteralPool = .{};
15191411 defer lp.deinit(gpa);
......@@ -1539,80 +1431,46 @@ pub fn dedupLiterals(self: *MachO) !void {
15391431 }
15401432}
15411433
1542fn claimUnresolved(self: *MachO) error{OutOfMemory}!void {
1434fn claimUnresolved(self: *MachO) void {
15431435 if (self.getZigObject()) |zo| {
1544 try zo.asFile().claimUnresolved(self);
1436 zo.asFile().claimUnresolved(self);
15451437 }
15461438 for (self.objects.items) |index| {
1547 try self.getFile(index).?.claimUnresolved(self);
1439 self.getFile(index).?.claimUnresolved(self);
15481440 }
15491441}
15501442
15511443fn checkDuplicates(self: *MachO) !void {
1552 const gpa = self.base.comp.gpa;
1553
1554 var dupes = std.AutoArrayHashMap(Symbol.Index, std.ArrayListUnmanaged(File.Index)).init(gpa);
1555 defer {
1556 for (dupes.values()) |*list| {
1557 list.deinit(gpa);
1558 }
1559 dupes.deinit();
1560 }
1561
15621444 if (self.getZigObject()) |zo| {
1563 try zo.checkDuplicates(&dupes, self);
1445 try zo.asFile().checkDuplicates(self);
15641446 }
1565
15661447 for (self.objects.items) |index| {
1567 try self.getFile(index).?.object.checkDuplicates(&dupes, self);
1448 try self.getFile(index).?.checkDuplicates(self);
15681449 }
1569
1570 try self.reportDuplicates(dupes);
1450 if (self.getInternalObject()) |obj| {
1451 try obj.asFile().checkDuplicates(self);
1452 }
1453 try self.reportDuplicates();
15711454}
15721455
15731456fn markImportsAndExports(self: *MachO) void {
1457 const tracy = trace(@src());
1458 defer tracy.end();
1459
15741460 if (self.getZigObject()) |zo| {
15751461 zo.asFile().markImportsExports(self);
15761462 }
15771463 for (self.objects.items) |index| {
15781464 self.getFile(index).?.markImportsExports(self);
15791465 }
1580
1581 for (self.undefined_symbols.items) |index| {
1582 const sym = self.getSymbol(index);
1583 if (sym.getFile(self)) |file| {
1584 if (sym.visibility != .global) continue;
1585 if (file == .dylib and !sym.flags.abs) sym.flags.import = true;
1586 }
1587 }
1588
1589 for (&[_]?Symbol.Index{
1590 self.entry_index,
1591 self.dyld_stub_binder_index,
1592 self.objc_msg_send_index,
1593 }) |index| {
1594 if (index) |idx| {
1595 const sym = self.getSymbol(idx);
1596 if (sym.getFile(self)) |file| {
1597 if (file == .dylib) sym.flags.import = true;
1598 }
1599 }
1466 if (self.getInternalObject()) |obj| {
1467 obj.asFile().markImportsExports(self);
16001468 }
16011469}
16021470
16031471fn deadStripDylibs(self: *MachO) void {
1604 for (&[_]?Symbol.Index{
1605 self.entry_index,
1606 self.dyld_stub_binder_index,
1607 self.objc_msg_send_index,
1608 }) |index| {
1609 if (index) |idx| {
1610 const sym = self.getSymbol(idx);
1611 if (sym.getFile(self)) |file| {
1612 if (file == .dylib) file.dylib.referenced = true;
1613 }
1614 }
1615 }
1472 const tracy = trace(@src());
1473 defer tracy.end();
16161474
16171475 for (self.dylibs.items) |index| {
16181476 self.getFile(index).?.dylib.markReferenced(self);
......@@ -1633,50 +1491,29 @@ fn scanRelocs(self: *MachO) !void {
16331491 const tracy = trace(@src());
16341492 defer tracy.end();
16351493
1636 if (self.getZigObject()) |zo| try zo.scanRelocs(self);
1637
1494 if (self.getZigObject()) |zo| {
1495 try zo.scanRelocs(self);
1496 }
16381497 for (self.objects.items) |index| {
16391498 try self.getFile(index).?.object.scanRelocs(self);
16401499 }
1500 if (self.getInternalObject()) |obj| {
1501 obj.scanRelocs(self);
1502 }
16411503
16421504 try self.reportUndefs();
16431505
1644 if (self.entry_index) |index| {
1645 const sym = self.getSymbol(index);
1646 if (sym.getFile(self) != null) {
1647 if (sym.flags.import) sym.flags.stubs = true;
1648 }
1506 if (self.getZigObject()) |zo| {
1507 try zo.asFile().createSymbolIndirection(self);
16491508 }
1650
1651 if (self.dyld_stub_binder_index) |index| {
1652 const sym = self.getSymbol(index);
1653 if (sym.getFile(self) != null) sym.flags.needs_got = true;
1509 for (self.objects.items) |index| {
1510 try self.getFile(index).?.createSymbolIndirection(self);
16541511 }
1655
1656 if (self.objc_msg_send_index) |index| {
1657 const sym = self.getSymbol(index);
1658 if (sym.getFile(self) != null)
1659 sym.flags.needs_got = true; // TODO is it always needed, or only if we are synthesising fast stubs?
1512 for (self.dylibs.items) |index| {
1513 try self.getFile(index).?.createSymbolIndirection(self);
16601514 }
1661
1662 for (self.symbols.items, 0..) |*symbol, i| {
1663 const index = @as(Symbol.Index, @intCast(i));
1664 if (symbol.flags.needs_got) {
1665 log.debug("'{s}' needs GOT", .{symbol.getName(self)});
1666 try self.got.addSymbol(index, self);
1667 }
1668 if (symbol.flags.stubs) {
1669 log.debug("'{s}' needs STUBS", .{symbol.getName(self)});
1670 try self.stubs.addSymbol(index, self);
1671 }
1672 if (symbol.flags.tlv_ptr) {
1673 log.debug("'{s}' needs TLV pointer", .{symbol.getName(self)});
1674 try self.tlv_ptr.addSymbol(index, self);
1675 }
1676 if (symbol.flags.objc_stubs) {
1677 log.debug("'{s}' needs OBJC STUBS", .{symbol.getName(self)});
1678 try self.objc_stubs.addSymbol(index, self);
1679 }
1515 if (self.getInternalObject()) |obj| {
1516 try obj.asFile().createSymbolIndirection(self);
16801517 }
16811518}
16821519
......@@ -1684,17 +1521,15 @@ fn reportUndefs(self: *MachO) !void {
16841521 const tracy = trace(@src());
16851522 defer tracy.end();
16861523
1687 switch (self.undefined_treatment) {
1688 .dynamic_lookup, .suppress => return,
1689 .@"error", .warn => {},
1690 }
1524 if (self.undefined_treatment == .suppress or
1525 self.undefined_treatment == .dynamic_lookup) return;
16911526
16921527 const max_notes = 4;
16931528
16941529 var has_undefs = false;
16951530 var it = self.undefs.iterator();
16961531 while (it.next()) |entry| {
1697 const undef_sym = self.getSymbol(entry.key_ptr.*);
1532 const undef_sym = self.resolver.keys.items[entry.key_ptr.* - 1];
16981533 const notes = entry.value_ptr.*;
16991534 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
17001535
......@@ -1704,8 +1539,9 @@ fn reportUndefs(self: *MachO) !void {
17041539
17051540 var inote: usize = 0;
17061541 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
1707 const atom = self.getAtom(notes.items[inote]).?;
1708 const file = atom.getFile(self);
1542 const note = notes.items[inote];
1543 const file = self.getFile(note.file).?;
1544 const atom = note.getAtom(self).?;
17091545 try err.addNote(self, "referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
17101546 }
17111547
......@@ -1714,64 +1550,18 @@ fn reportUndefs(self: *MachO) !void {
17141550 try err.addNote(self, "referenced {d} more times", .{remaining});
17151551 }
17161552 }
1717
1718 for (self.undefined_symbols.items) |index| {
1719 const sym = self.getSymbol(index);
1720 if (sym.getFile(self) != null) continue; // If undefined in an object file, will be reported above
1721 has_undefs = true;
1722 var err = try self.addErrorWithNotes(1);
1723 try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
1724 try err.addNote(self, "-u command line option", .{});
1725 }
1726
1727 if (self.entry_index) |index| {
1728 const sym = self.getSymbol(index);
1729 if (sym.getFile(self) == null) {
1730 has_undefs = true;
1731 var err = try self.addErrorWithNotes(1);
1732 try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
1733 try err.addNote(self, "implicit entry/start for main executable", .{});
1734 }
1735 }
1736
1737 if (self.dyld_stub_binder_index) |index| {
1738 const sym = self.getSymbol(index);
1739 if (sym.getFile(self) == null and self.stubs_sect_index != null) {
1740 has_undefs = true;
1741 var err = try self.addErrorWithNotes(1);
1742 try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
1743 try err.addNote(self, "implicit -u command line option", .{});
1744 }
1745 }
1746
1747 if (self.objc_msg_send_index) |index| {
1748 const sym = self.getSymbol(index);
1749 if (sym.getFile(self) == null and self.objc_stubs_sect_index != null) {
1750 has_undefs = true;
1751 var err = try self.addErrorWithNotes(1);
1752 try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
1753 try err.addNote(self, "implicit -u command line option", .{});
1754 }
1755 }
1756
17571553 if (has_undefs) return error.HasUndefinedSymbols;
17581554}
17591555
17601556fn initOutputSections(self: *MachO) !void {
1557 const tracy = trace(@src());
1558 defer tracy.end();
1559
17611560 for (self.objects.items) |index| {
1762 const object = self.getFile(index).?.object;
1763 for (object.atoms.items) |atom_index| {
1764 const atom = self.getAtom(atom_index) orelse continue;
1765 if (!atom.flags.alive) continue;
1766 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self);
1767 }
1561 try self.getFile(index).?.initOutputSections(self);
17681562 }
1769 if (self.getInternalObject()) |object| {
1770 for (object.atoms.items) |atom_index| {
1771 const atom = self.getAtom(atom_index) orelse continue;
1772 if (!atom.flags.alive) continue;
1773 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self);
1774 }
1563 if (self.getInternalObject()) |obj| {
1564 try obj.asFile().initOutputSections(self);
17751565 }
17761566 self.text_sect_index = self.getSectionByName("__TEXT", "__text") orelse
17771567 try self.addSection("__TEXT", "__text", .{
......@@ -1844,46 +1634,50 @@ fn initSyntheticSections(self: *MachO) !void {
18441634 self.eh_frame_sect_index = try self.addSection("__TEXT", "__eh_frame", .{});
18451635 }
18461636
1847 for (self.boundary_symbols.items) |sym_index| {
1637 if (self.getInternalObject()) |obj| {
18481638 const gpa = self.base.comp.gpa;
1849 const sym = self.getSymbol(sym_index);
1850 const name = sym.getName(self);
1851
1852 if (eatPrefix(name, "segment$start$")) |segname| {
1853 if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
1854 const prot = getSegmentProt(segname);
1855 _ = try self.segments.append(gpa, .{
1856 .cmdsize = @sizeOf(macho.segment_command_64),
1857 .segname = makeStaticString(segname),
1858 .initprot = prot,
1859 .maxprot = prot,
1860 });
1861 }
1862 } else if (eatPrefix(name, "segment$stop$")) |segname| {
1863 if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
1864 const prot = getSegmentProt(segname);
1865 _ = try self.segments.append(gpa, .{
1866 .cmdsize = @sizeOf(macho.segment_command_64),
1867 .segname = makeStaticString(segname),
1868 .initprot = prot,
1869 .maxprot = prot,
1870 });
1871 }
1872 } else if (eatPrefix(name, "section$start$")) |actual_name| {
1873 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
1874 const segname = actual_name[0..sep]; // TODO check segname is valid
1875 const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
1876 if (self.getSectionByName(segname, sectname) == null) {
1877 _ = try self.addSection(segname, sectname, .{});
1878 }
1879 } else if (eatPrefix(name, "section$stop$")) |actual_name| {
1880 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
1881 const segname = actual_name[0..sep]; // TODO check segname is valid
1882 const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
1883 if (self.getSectionByName(segname, sectname) == null) {
1884 _ = try self.addSection(segname, sectname, .{});
1885 }
1886 } else unreachable;
1639
1640 for (obj.boundary_symbols.items) |sym_index| {
1641 const ref = obj.getSymbolRef(sym_index, self);
1642 const sym = ref.getSymbol(self).?;
1643 const name = sym.getName(self);
1644
1645 if (eatPrefix(name, "segment$start$")) |segname| {
1646 if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
1647 const prot = getSegmentProt(segname);
1648 _ = try self.segments.append(gpa, .{
1649 .cmdsize = @sizeOf(macho.segment_command_64),
1650 .segname = makeStaticString(segname),
1651 .initprot = prot,
1652 .maxprot = prot,
1653 });
1654 }
1655 } else if (eatPrefix(name, "segment$stop$")) |segname| {
1656 if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
1657 const prot = getSegmentProt(segname);
1658 _ = try self.segments.append(gpa, .{
1659 .cmdsize = @sizeOf(macho.segment_command_64),
1660 .segname = makeStaticString(segname),
1661 .initprot = prot,
1662 .maxprot = prot,
1663 });
1664 }
1665 } else if (eatPrefix(name, "section$start$")) |actual_name| {
1666 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
1667 const segname = actual_name[0..sep]; // TODO check segname is valid
1668 const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
1669 if (self.getSectionByName(segname, sectname) == null) {
1670 _ = try self.addSection(segname, sectname, .{});
1671 }
1672 } else if (eatPrefix(name, "section$stop$")) |actual_name| {
1673 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
1674 const segname = actual_name[0..sep]; // TODO check segname is valid
1675 const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
1676 if (self.getSectionByName(segname, sectname) == null) {
1677 _ = try self.addSection(segname, sectname, .{});
1678 }
1679 } else unreachable;
1680 }
18871681 }
18881682}
18891683
......@@ -1990,38 +1784,25 @@ pub fn sortSections(self: *MachO) !void {
19901784 }
19911785
19921786 if (self.getZigObject()) |zo| {
1993 for (zo.atoms.items) |atom_index| {
1994 const atom = self.getAtom(atom_index) orelse continue;
1787 for (zo.getAtoms()) |atom_index| {
1788 const atom = zo.getAtom(atom_index) orelse continue;
19951789 if (!atom.flags.alive) continue;
19961790 atom.out_n_sect = backlinks[atom.out_n_sect];
19971791 }
1998
1999 for (zo.symtab.items(.nlist)) |*sym| {
2000 if (sym.sect()) {
2001 sym.n_sect = backlinks[sym.n_sect - 1] + 1;
2002 }
2003 }
2004
2005 for (zo.symbols.items) |sym_index| {
2006 const sym = self.getSymbol(sym_index);
2007 const atom = sym.getAtom(self) orelse continue;
2008 if (!atom.flags.alive) continue;
2009 if (sym.getFile(self).?.getIndex() != zo.index) continue;
2010 sym.out_n_sect = backlinks[sym.out_n_sect];
2011 }
20121792 }
20131793
20141794 for (self.objects.items) |index| {
2015 for (self.getFile(index).?.object.atoms.items) |atom_index| {
2016 const atom = self.getAtom(atom_index) orelse continue;
1795 const file = self.getFile(index).?;
1796 for (file.getAtoms()) |atom_index| {
1797 const atom = file.getAtom(atom_index) orelse continue;
20171798 if (!atom.flags.alive) continue;
20181799 atom.out_n_sect = backlinks[atom.out_n_sect];
20191800 }
20201801 }
20211802
20221803 if (self.getInternalObject()) |object| {
2023 for (object.atoms.items) |atom_index| {
2024 const atom = self.getAtom(atom_index) orelse continue;
1804 for (object.getAtoms()) |atom_index| {
1805 const atom = object.getAtom(atom_index) orelse continue;
20251806 if (!atom.flags.alive) continue;
20261807 atom.out_n_sect = backlinks[atom.out_n_sect];
20271808 }
......@@ -2058,35 +1839,32 @@ pub fn addAtomsToSections(self: *MachO) !void {
20581839 const tracy = trace(@src());
20591840 defer tracy.end();
20601841
2061 for (self.objects.items) |index| {
2062 const object = self.getFile(index).?.object;
2063 for (object.atoms.items) |atom_index| {
2064 const atom = self.getAtom(atom_index) orelse continue;
1842 const gpa = self.base.comp.gpa;
1843
1844 if (self.getZigObject()) |zo| {
1845 for (zo.getAtoms()) |atom_index| {
1846 const atom = zo.getAtom(atom_index) orelse continue;
20651847 if (!atom.flags.alive) continue;
1848 if (self.isZigSection(atom.out_n_sect)) continue;
20661849 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
2067 try atoms.append(self.base.comp.gpa, atom_index);
1850 try atoms.append(gpa, .{ .index = atom_index, .file = zo.index });
20681851 }
2069 for (object.symbols.items) |sym_index| {
2070 const sym = self.getSymbol(sym_index);
2071 const atom = sym.getAtom(self) orelse continue;
1852 }
1853 for (self.objects.items) |index| {
1854 const file = self.getFile(index).?;
1855 for (file.getAtoms()) |atom_index| {
1856 const atom = file.getAtom(atom_index) orelse continue;
20721857 if (!atom.flags.alive) continue;
2073 if (sym.getFile(self).?.getIndex() != index) continue;
2074 sym.out_n_sect = atom.out_n_sect;
1858 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
1859 try atoms.append(gpa, .{ .index = atom_index, .file = index });
20751860 }
20761861 }
20771862 if (self.getInternalObject()) |object| {
2078 for (object.atoms.items) |atom_index| {
2079 const atom = self.getAtom(atom_index) orelse continue;
1863 for (object.getAtoms()) |atom_index| {
1864 const atom = object.getAtom(atom_index) orelse continue;
20801865 if (!atom.flags.alive) continue;
20811866 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
2082 try atoms.append(self.base.comp.gpa, atom_index);
2083 }
2084 for (object.symbols.items) |sym_index| {
2085 const sym = self.getSymbol(sym_index);
2086 const atom = sym.getAtom(self) orelse continue;
2087 if (!atom.flags.alive) continue;
2088 if (sym.getFile(self).?.getIndex() != object.index) continue;
2089 sym.out_n_sect = atom.out_n_sect;
1867 try atoms.append(gpa, .{ .index = atom_index, .file = object.index });
20901868 }
20911869 }
20921870}
......@@ -2108,8 +1886,8 @@ fn calcSectionSizes(self: *MachO) !void {
21081886 if (atoms.items.len == 0) continue;
21091887 if (self.requiresThunks() and header.isCode()) continue;
21101888
2111 for (atoms.items) |atom_index| {
2112 const atom = self.getAtom(atom_index).?;
1889 for (atoms.items) |ref| {
1890 const atom = ref.getAtom(self).?;
21131891 const atom_alignment = atom.alignment.toByteUnits() orelse 1;
21141892 const offset = mem.alignForward(u64, header.size, atom_alignment);
21151893 const padding = offset - header.size;
......@@ -2129,6 +1907,22 @@ fn calcSectionSizes(self: *MachO) !void {
21291907 }
21301908 }
21311909
1910 // At this point, we can also calculate symtab and data-in-code linkedit section sizes
1911 if (self.getZigObject()) |zo| {
1912 zo.asFile().calcSymtabSize(self);
1913 }
1914 for (self.objects.items) |index| {
1915 self.getFile(index).?.calcSymtabSize(self);
1916 }
1917 for (self.dylibs.items) |index| {
1918 self.getFile(index).?.calcSymtabSize(self);
1919 }
1920 if (self.getInternalObject()) |obj| {
1921 obj.asFile().calcSymtabSize(self);
1922 }
1923
1924 try self.calcSymtabSize();
1925
21321926 if (self.got_sect_index) |idx| {
21331927 const header = &self.sections.items(.header)[idx];
21341928 header.size = self.got.size();
......@@ -2409,77 +2203,61 @@ fn allocateSegments(self: *MachO) void {
24092203}
24102204
24112205fn allocateSyntheticSymbols(self: *MachO) void {
2412 const text_seg = self.getTextSegment();
2413
2414 if (self.mh_execute_header_index) |index| {
2415 const global = self.getSymbol(index);
2416 global.value = text_seg.vmaddr;
2417 }
2206 if (self.getInternalObject()) |obj| {
2207 obj.allocateSyntheticSymbols(self);
24182208
2419 if (self.data_sect_index) |idx| {
2420 const sect = self.sections.items(.header)[idx];
2421 for (&[_]?Symbol.Index{
2422 self.dso_handle_index,
2423 self.mh_dylib_header_index,
2424 self.dyld_private_index,
2425 }) |maybe_index| {
2426 if (maybe_index) |index| {
2427 const global = self.getSymbol(index);
2428 global.value = sect.addr;
2429 global.out_n_sect = idx;
2430 }
2431 }
2432 }
2209 const text_seg = self.getTextSegment();
24332210
2434 for (self.boundary_symbols.items) |sym_index| {
2435 const sym = self.getSymbol(sym_index);
2436 const name = sym.getName(self);
2211 for (obj.boundary_symbols.items) |sym_index| {
2212 const ref = obj.getSymbolRef(sym_index, self);
2213 const sym = ref.getSymbol(self).?;
2214 const name = sym.getName(self);
24372215
2438 sym.flags.@"export" = false;
2439 sym.value = text_seg.vmaddr;
2216 sym.value = text_seg.vmaddr;
24402217
2441 if (mem.startsWith(u8, name, "segment$start$")) {
2442 const segname = name["segment$start$".len..];
2443 if (self.getSegmentByName(segname)) |seg_id| {
2444 const seg = self.segments.items[seg_id];
2445 sym.value = seg.vmaddr;
2446 }
2447 } else if (mem.startsWith(u8, name, "segment$stop$")) {
2448 const segname = name["segment$stop$".len..];
2449 if (self.getSegmentByName(segname)) |seg_id| {
2450 const seg = self.segments.items[seg_id];
2451 sym.value = seg.vmaddr + seg.vmsize;
2452 }
2453 } else if (mem.startsWith(u8, name, "section$start$")) {
2454 const actual_name = name["section$start$".len..];
2455 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
2456 const segname = actual_name[0..sep];
2457 const sectname = actual_name[sep + 1 ..];
2458 if (self.getSectionByName(segname, sectname)) |sect_id| {
2459 const sect = self.sections.items(.header)[sect_id];
2460 sym.value = sect.addr;
2461 sym.out_n_sect = sect_id;
2462 }
2463 } else if (mem.startsWith(u8, name, "section$stop$")) {
2464 const actual_name = name["section$stop$".len..];
2465 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
2466 const segname = actual_name[0..sep];
2467 const sectname = actual_name[sep + 1 ..];
2468 if (self.getSectionByName(segname, sectname)) |sect_id| {
2469 const sect = self.sections.items(.header)[sect_id];
2470 sym.value = sect.addr + sect.size;
2471 sym.out_n_sect = sect_id;
2472 }
2473 } else unreachable;
2474 }
2218 if (mem.startsWith(u8, name, "segment$start$")) {
2219 const segname = name["segment$start$".len..];
2220 if (self.getSegmentByName(segname)) |seg_id| {
2221 const seg = self.segments.items[seg_id];
2222 sym.value = seg.vmaddr;
2223 }
2224 } else if (mem.startsWith(u8, name, "segment$stop$")) {
2225 const segname = name["segment$stop$".len..];
2226 if (self.getSegmentByName(segname)) |seg_id| {
2227 const seg = self.segments.items[seg_id];
2228 sym.value = seg.vmaddr + seg.vmsize;
2229 }
2230 } else if (mem.startsWith(u8, name, "section$start$")) {
2231 const actual_name = name["section$start$".len..];
2232 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
2233 const segname = actual_name[0..sep];
2234 const sectname = actual_name[sep + 1 ..];
2235 if (self.getSectionByName(segname, sectname)) |sect_id| {
2236 const sect = self.sections.items(.header)[sect_id];
2237 sym.value = sect.addr;
2238 sym.out_n_sect = sect_id;
2239 }
2240 } else if (mem.startsWith(u8, name, "section$stop$")) {
2241 const actual_name = name["section$stop$".len..];
2242 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
2243 const segname = actual_name[0..sep];
2244 const sectname = actual_name[sep + 1 ..];
2245 if (self.getSectionByName(segname, sectname)) |sect_id| {
2246 const sect = self.sections.items(.header)[sect_id];
2247 sym.value = sect.addr + sect.size;
2248 sym.out_n_sect = sect_id;
2249 }
2250 } else unreachable;
2251 }
24752252
2476 if (self.objc_stubs.symbols.items.len > 0) {
2477 const addr = self.sections.items(.header)[self.objc_stubs_sect_index.?].addr;
2253 if (self.objc_stubs.symbols.items.len > 0) {
2254 const addr = self.sections.items(.header)[self.objc_stubs_sect_index.?].addr;
24782255
2479 for (self.objc_stubs.symbols.items, 0..) |sym_index, idx| {
2480 const sym = self.getSymbol(sym_index);
2481 sym.value = addr + idx * ObjcStubsSection.entrySize(self.getTarget().cpu.arch);
2482 sym.out_n_sect = self.objc_stubs_sect_index.?;
2256 for (self.objc_stubs.symbols.items, 0..) |ref, idx| {
2257 const sym = ref.getSymbol(self).?;
2258 sym.value = addr + idx * ObjcStubsSection.entrySize(self.getTarget().cpu.arch);
2259 sym.out_n_sect = self.objc_stubs_sect_index.?;
2260 }
24832261 }
24842262 }
24852263}
......@@ -2497,269 +2275,228 @@ fn allocateLinkeditSegment(self: *MachO) !void {
24972275 const seg = self.getLinkeditSegment();
24982276 seg.vmaddr = mem.alignForward(u64, vmaddr, page_size);
24992277 seg.fileoff = mem.alignForward(u64, fileoff, page_size);
2500}
2501
2502fn initDyldInfoSections(self: *MachO) !void {
2503 const tracy = trace(@src());
2504 defer tracy.end();
2505
2506 const gpa = self.base.comp.gpa;
25072278
2508 if (self.zig_got_sect_index != null) try self.zig_got.addDyldRelocs(self);
2509 if (self.got_sect_index != null) try self.got.addDyldRelocs(self);
2510 if (self.tlv_ptr_sect_index != null) try self.tlv_ptr.addDyldRelocs(self);
2511 if (self.la_symbol_ptr_sect_index != null) try self.la_symbol_ptr.addDyldRelocs(self);
2512 try self.initExportTrie();
2513
2514 var objects = try std.ArrayList(File.Index).initCapacity(gpa, self.objects.items.len + 1);
2515 defer objects.deinit();
2516 if (self.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
2517 objects.appendSliceAssumeCapacity(self.objects.items);
2518
2519 var nrebases: usize = 0;
2520 var nbinds: usize = 0;
2521 var nweak_binds: usize = 0;
2522 for (objects.items) |index| {
2523 const ctx = switch (self.getFile(index).?) {
2524 .zig_object => |x| x.dynamic_relocs,
2525 .object => |x| x.dynamic_relocs,
2526 else => unreachable,
2527 };
2528 nrebases += ctx.rebase_relocs;
2529 nbinds += ctx.bind_relocs;
2530 nweak_binds += ctx.weak_bind_relocs;
2531 }
2532 if (self.getInternalObject()) |int| {
2533 nrebases += int.num_rebase_relocs;
2279 var off = math.cast(u32, seg.fileoff) orelse return error.Overflow;
2280 // DYLD_INFO_ONLY
2281 {
2282 const cmd = &self.dyld_info_cmd;
2283 cmd.rebase_off = off;
2284 off += cmd.rebase_size;
2285 cmd.bind_off = off;
2286 off += cmd.bind_size;
2287 cmd.weak_bind_off = off;
2288 off += cmd.weak_bind_size;
2289 cmd.lazy_bind_off = off;
2290 off += cmd.lazy_bind_size;
2291 cmd.export_off = off;
2292 off += cmd.export_size;
2293 off = mem.alignForward(u32, off, @alignOf(u64));
2294 }
2295
2296 // FUNCTION_STARTS
2297 {
2298 const cmd = &self.function_starts_cmd;
2299 cmd.dataoff = off;
2300 off += cmd.datasize;
2301 off = mem.alignForward(u32, off, @alignOf(u64));
25342302 }
2535 try self.rebase.entries.ensureUnusedCapacity(gpa, nrebases);
2536 try self.bind.entries.ensureUnusedCapacity(gpa, nbinds);
2537 try self.weak_bind.entries.ensureUnusedCapacity(gpa, nweak_binds);
2538}
2539
2540fn initExportTrie(self: *MachO) !void {
2541 const tracy = trace(@src());
2542 defer tracy.end();
25432303
2544 const gpa = self.base.comp.gpa;
2545 try self.export_trie.init(gpa);
2304 // DATA_IN_CODE
2305 {
2306 const cmd = &self.data_in_code_cmd;
2307 cmd.dataoff = off;
2308 off += cmd.datasize;
2309 off = mem.alignForward(u32, off, @alignOf(u64));
2310 }
25462311
2547 const seg = self.getTextSegment();
2548 for (self.objects.items) |index| {
2549 for (self.getFile(index).?.getSymbols()) |sym_index| {
2550 const sym = self.getSymbol(sym_index);
2551 if (!sym.flags.@"export") continue;
2552 if (sym.getAtom(self)) |atom| if (!atom.flags.alive) continue;
2553 if (sym.getFile(self).?.getIndex() != index) continue;
2554 var flags: u64 = if (sym.flags.abs)
2555 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
2556 else if (sym.flags.tlv)
2557 macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL
2558 else
2559 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
2560 if (sym.flags.weak) {
2561 flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
2562 self.weak_defines = true;
2563 self.binds_to_weak = true;
2564 }
2565 try self.export_trie.put(gpa, .{
2566 .name = sym.getName(self),
2567 .vmaddr_offset = sym.getAddress(.{ .stubs = false }, self) - seg.vmaddr,
2568 .export_flags = flags,
2569 });
2570 }
2312 // SYMTAB (symtab)
2313 {
2314 const cmd = &self.symtab_cmd;
2315 cmd.symoff = off;
2316 off += cmd.nsyms * @sizeOf(macho.nlist_64);
2317 off = mem.alignForward(u32, off, @alignOf(u32));
25712318 }
25722319
2573 if (self.mh_execute_header_index) |index| {
2574 const sym = self.getSymbol(index);
2575 try self.export_trie.put(gpa, .{
2576 .name = sym.getName(self),
2577 .vmaddr_offset = sym.getAddress(.{}, self) - seg.vmaddr,
2578 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
2579 });
2320 // DYSYMTAB
2321 {
2322 const cmd = &self.dysymtab_cmd;
2323 cmd.indirectsymoff = off;
2324 off += cmd.nindirectsyms * @sizeOf(u32);
2325 off = mem.alignForward(u32, off, @alignOf(u64));
25802326 }
2581}
25822327
2583fn writeAtoms(self: *MachO) !void {
2584 const tracy = trace(@src());
2585 defer tracy.end();
2328 // SYMTAB (strtab)
2329 {
2330 const cmd = &self.symtab_cmd;
2331 cmd.stroff = off;
2332 off += cmd.strsize;
2333 }
25862334
2587 const gpa = self.base.comp.gpa;
2588 var arena = std.heap.ArenaAllocator.init(gpa);
2589 defer arena.deinit();
2335 seg.filesize = off - seg.fileoff;
2336}
25902337
2591 const cpu_arch = self.getTarget().cpu.arch;
2338fn resizeSections(self: *MachO) !void {
25922339 const slice = self.sections.slice();
2593
2594 var has_resolve_error = false;
2595 for (slice.items(.header), slice.items(.atoms)) |header, atoms| {
2596 if (atoms.items.len == 0) continue;
2340 for (slice.items(.header), slice.items(.out), 0..) |header, *out, n_sect| {
25972341 if (header.isZerofill()) continue;
2598
2342 if (self.isZigSection(@intCast(n_sect))) continue; // TODO this is horrible
2343 const cpu_arch = self.getTarget().cpu.arch;
25992344 const size = math.cast(usize, header.size) orelse return error.Overflow;
2600 const buffer = try gpa.alloc(u8, size);
2601 defer gpa.free(buffer);
2345 try out.resize(self.base.comp.gpa, size);
26022346 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
2603 @memset(buffer, padding_byte);
2604
2605 for (atoms.items) |atom_index| {
2606 const atom = self.getAtom(atom_index).?;
2607 assert(atom.flags.alive);
2608 const off = math.cast(usize, atom.value) orelse return error.Overflow;
2609 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
2610 try atom.getData(self, buffer[off..][0..atom_size]);
2611 atom.resolveRelocs(self, buffer[off..][0..atom_size]) catch |err| switch (err) {
2612 error.ResolveFailed => has_resolve_error = true,
2613 else => |e| return e,
2614 };
2615 }
2616
2617 try self.base.file.?.pwriteAll(buffer, header.offset);
2347 @memset(out.items, padding_byte);
26182348 }
2349}
2350
2351fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {
2352 const gpa = self.base.comp.gpa;
26192353
2354 const cmd = self.symtab_cmd;
2355 try self.symtab.resize(gpa, cmd.nsyms);
2356 try self.strtab.resize(gpa, cmd.strsize);
2357 self.strtab.items[0] = 0;
2358
2359 for (self.objects.items) |index| {
2360 try self.getFile(index).?.writeAtoms(self);
2361 }
2362 if (self.getZigObject()) |zo| {
2363 try zo.writeAtoms(self);
2364 }
2365 if (self.getInternalObject()) |obj| {
2366 try obj.asFile().writeAtoms(self);
2367 }
26202368 for (self.thunks.items) |thunk| {
2621 const header = slice.items(.header)[thunk.out_n_sect];
2622 const offset = thunk.value + header.offset;
2623 const buffer = try gpa.alloc(u8, thunk.size());
2624 defer gpa.free(buffer);
2625 var stream = std.io.fixedBufferStream(buffer);
2369 const out = self.sections.items(.out)[thunk.out_n_sect].items;
2370 const off = math.cast(usize, thunk.value) orelse return error.Overflow;
2371 const size = thunk.size();
2372 var stream = std.io.fixedBufferStream(out[off..][0..size]);
26262373 try thunk.write(self, stream.writer());
2627 try self.base.file.?.pwriteAll(buffer, offset);
26282374 }
26292375
2630 if (has_resolve_error) return error.ResolveFailed;
2631}
2376 const slice = self.sections.slice();
2377 for (&[_]?u8{
2378 self.eh_frame_sect_index,
2379 self.unwind_info_sect_index,
2380 self.got_sect_index,
2381 self.stubs_sect_index,
2382 self.la_symbol_ptr_sect_index,
2383 self.tlv_ptr_sect_index,
2384 self.objc_stubs_sect_index,
2385 }) |maybe_sect_id| {
2386 if (maybe_sect_id) |sect_id| {
2387 const out = slice.items(.out)[sect_id].items;
2388 try self.writeSyntheticSection(sect_id, out);
2389 }
2390 }
26322391
2633fn writeUnwindInfo(self: *MachO) !void {
2634 const tracy = trace(@src());
2635 defer tracy.end();
2392 if (self.la_symbol_ptr_sect_index) |_| {
2393 try self.updateLazyBindSize();
2394 }
26362395
2637 const gpa = self.base.comp.gpa;
2396 try self.rebase.updateSize(self);
2397 try self.bind.updateSize(self);
2398 try self.weak_bind.updateSize(self);
2399 try self.export_trie.updateSize(self);
2400 try self.data_in_code.updateSize(self);
26382401
2639 if (self.eh_frame_sect_index) |index| {
2640 const header = self.sections.items(.header)[index];
2641 const size = math.cast(usize, header.size) orelse return error.Overflow;
2642 const buffer = try gpa.alloc(u8, size);
2643 defer gpa.free(buffer);
2644 eh_frame.write(self, buffer);
2645 try self.base.file.?.pwriteAll(buffer, header.offset);
2402 if (self.getZigObject()) |zo| {
2403 zo.asFile().writeSymtab(self, self);
26462404 }
2647
2648 if (self.unwind_info_sect_index) |index| {
2649 const header = self.sections.items(.header)[index];
2650 const size = math.cast(usize, header.size) orelse return error.Overflow;
2651 const buffer = try gpa.alloc(u8, size);
2652 defer gpa.free(buffer);
2653 try self.unwind_info.write(self, buffer);
2654 try self.base.file.?.pwriteAll(buffer, header.offset);
2405 for (self.objects.items) |index| {
2406 self.getFile(index).?.writeSymtab(self, self);
2407 }
2408 for (self.dylibs.items) |index| {
2409 self.getFile(index).?.writeSymtab(self, self);
2410 }
2411 if (self.getInternalObject()) |obj| {
2412 obj.asFile().writeSymtab(self, self);
26552413 }
26562414}
26572415
2658fn finalizeDyldInfoSections(self: *MachO) !void {
2416fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {
26592417 const tracy = trace(@src());
26602418 defer tracy.end();
2661 const gpa = self.base.comp.gpa;
26622419
2663 try self.rebase.finalize(gpa);
2664 try self.bind.finalize(gpa, self);
2665 try self.weak_bind.finalize(gpa, self);
2666 try self.lazy_bind.finalize(gpa, self);
2667 try self.export_trie.finalize(gpa);
2420 const Tag = enum {
2421 eh_frame,
2422 unwind_info,
2423 got,
2424 stubs,
2425 la_symbol_ptr,
2426 tlv_ptr,
2427 objc_stubs,
2428 };
2429
2430 const tag: Tag = tag: {
2431 if (self.eh_frame_sect_index != null and
2432 self.eh_frame_sect_index.? == sect_id) break :tag .eh_frame;
2433 if (self.unwind_info_sect_index != null and
2434 self.unwind_info_sect_index.? == sect_id) break :tag .unwind_info;
2435 if (self.got_sect_index != null and
2436 self.got_sect_index.? == sect_id) break :tag .got;
2437 if (self.stubs_sect_index != null and
2438 self.stubs_sect_index.? == sect_id) break :tag .stubs;
2439 if (self.la_symbol_ptr_sect_index != null and
2440 self.la_symbol_ptr_sect_index.? == sect_id) break :tag .la_symbol_ptr;
2441 if (self.tlv_ptr_sect_index != null and
2442 self.tlv_ptr_sect_index.? == sect_id) break :tag .tlv_ptr;
2443 if (self.objc_stubs_sect_index != null and
2444 self.objc_stubs_sect_index.? == sect_id) break :tag .objc_stubs;
2445 unreachable;
2446 };
2447 var stream = std.io.fixedBufferStream(out);
2448 switch (tag) {
2449 .eh_frame => eh_frame.write(self, out),
2450 .unwind_info => try self.unwind_info.write(self, out),
2451 .got => try self.got.write(self, stream.writer()),
2452 .stubs => try self.stubs.write(self, stream.writer()),
2453 .la_symbol_ptr => try self.la_symbol_ptr.write(self, stream.writer()),
2454 .tlv_ptr => try self.tlv_ptr.write(self, stream.writer()),
2455 .objc_stubs => try self.objc_stubs.write(self, stream.writer()),
2456 }
26682457}
26692458
2670fn writeSyntheticSections(self: *MachO) !void {
2459fn updateLazyBindSize(self: *MachO) !void {
26712460 const tracy = trace(@src());
26722461 defer tracy.end();
2462 try self.lazy_bind.updateSize(self);
2463 const sect_id = self.stubs_helper_sect_index.?;
2464 const out = &self.sections.items(.out)[sect_id];
2465 var stream = std.io.fixedBufferStream(out.items);
2466 try self.stubs_helper.write(self, stream.writer());
2467}
26732468
2674 const gpa = self.base.comp.gpa;
2675
2676 if (self.got_sect_index) |sect_id| {
2677 const header = self.sections.items(.header)[sect_id];
2678 const size = math.cast(usize, header.size) orelse return error.Overflow;
2679 var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
2680 defer buffer.deinit();
2681 try self.got.write(self, buffer.writer());
2682 assert(buffer.items.len == header.size);
2683 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2684 }
2685
2686 if (self.stubs_sect_index) |sect_id| {
2687 const header = self.sections.items(.header)[sect_id];
2688 const size = math.cast(usize, header.size) orelse return error.Overflow;
2689 var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
2690 defer buffer.deinit();
2691 try self.stubs.write(self, buffer.writer());
2692 assert(buffer.items.len == header.size);
2693 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2694 }
2695
2696 if (self.stubs_helper_sect_index) |sect_id| {
2697 const header = self.sections.items(.header)[sect_id];
2698 const size = math.cast(usize, header.size) orelse return error.Overflow;
2699 var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
2700 defer buffer.deinit();
2701 try self.stubs_helper.write(self, buffer.writer());
2702 assert(buffer.items.len == header.size);
2703 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2704 }
2705
2706 if (self.la_symbol_ptr_sect_index) |sect_id| {
2707 const header = self.sections.items(.header)[sect_id];
2708 const size = math.cast(usize, header.size) orelse return error.Overflow;
2709 var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
2710 defer buffer.deinit();
2711 try self.la_symbol_ptr.write(self, buffer.writer());
2712 assert(buffer.items.len == header.size);
2713 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2714 }
2469fn writeSectionsToFile(self: *MachO) !void {
2470 const tracy = trace(@src());
2471 defer tracy.end();
27152472
2716 if (self.tlv_ptr_sect_index) |sect_id| {
2717 const header = self.sections.items(.header)[sect_id];
2718 const size = math.cast(usize, header.size) orelse return error.Overflow;
2719 var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
2720 defer buffer.deinit();
2721 try self.tlv_ptr.write(self, buffer.writer());
2722 assert(buffer.items.len == header.size);
2723 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2473 const slice = self.sections.slice();
2474 for (slice.items(.header), slice.items(.out)) |header, out| {
2475 try self.base.file.?.pwriteAll(out.items, header.offset);
27242476 }
2477}
27252478
2726 if (self.objc_stubs_sect_index) |sect_id| {
2727 const header = self.sections.items(.header)[sect_id];
2728 const size = math.cast(usize, header.size) orelse return error.Overflow;
2729 var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
2730 defer buffer.deinit();
2731 try self.objc_stubs.write(self, buffer.writer());
2732 assert(buffer.items.len == header.size);
2733 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2734 }
2479fn writeLinkeditSectionsToFile(self: *MachO) !void {
2480 const tracy = trace(@src());
2481 defer tracy.end();
2482 try self.writeDyldInfo();
2483 try self.writeDataInCode();
2484 try self.writeSymtabToFile();
2485 try self.writeIndsymtab();
27352486}
27362487
2737fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {
2488fn writeDyldInfo(self: *MachO) !void {
27382489 const tracy = trace(@src());
27392490 defer tracy.end();
27402491
27412492 const gpa = self.base.comp.gpa;
2742 const cmd = &self.dyld_info_cmd;
2493 const base_off = self.getLinkeditSegment().fileoff;
2494 const cmd = self.dyld_info_cmd;
27432495 var needed_size: u32 = 0;
2744
2745 cmd.rebase_off = needed_size;
2746 cmd.rebase_size = mem.alignForward(u32, @intCast(self.rebase.size()), @alignOf(u64));
27472496 needed_size += cmd.rebase_size;
2748
2749 cmd.bind_off = needed_size;
2750 cmd.bind_size = mem.alignForward(u32, @intCast(self.bind.size()), @alignOf(u64));
27512497 needed_size += cmd.bind_size;
2752
2753 cmd.weak_bind_off = needed_size;
2754 cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.weak_bind.size()), @alignOf(u64));
27552498 needed_size += cmd.weak_bind_size;
2756
2757 cmd.lazy_bind_off = needed_size;
2758 cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.lazy_bind.size()), @alignOf(u64));
27592499 needed_size += cmd.lazy_bind_size;
2760
2761 cmd.export_off = needed_size;
2762 cmd.export_size = mem.alignForward(u32, @intCast(self.export_trie.size), @alignOf(u64));
27632500 needed_size += cmd.export_size;
27642501
27652502 const buffer = try gpa.alloc(u8, needed_size);
......@@ -2770,89 +2507,78 @@ fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {
27702507 const writer = stream.writer();
27712508
27722509 try self.rebase.write(writer);
2773 try stream.seekTo(cmd.bind_off);
2510 try stream.seekTo(cmd.bind_off - base_off);
27742511 try self.bind.write(writer);
2775 try stream.seekTo(cmd.weak_bind_off);
2512 try stream.seekTo(cmd.weak_bind_off - base_off);
27762513 try self.weak_bind.write(writer);
2777 try stream.seekTo(cmd.lazy_bind_off);
2514 try stream.seekTo(cmd.lazy_bind_off - base_off);
27782515 try self.lazy_bind.write(writer);
2779 try stream.seekTo(cmd.export_off);
2516 try stream.seekTo(cmd.export_off - base_off);
27802517 try self.export_trie.write(writer);
2518 try self.base.file.?.pwriteAll(buffer, cmd.rebase_off);
2519}
27812520
2782 cmd.rebase_off += off;
2783 cmd.bind_off += off;
2784 cmd.weak_bind_off += off;
2785 cmd.lazy_bind_off += off;
2786 cmd.export_off += off;
2787
2788 try self.base.file.?.pwriteAll(buffer, off);
2521pub fn writeDataInCode(self: *MachO) !void {
2522 const tracy = trace(@src());
2523 defer tracy.end();
2524 const gpa = self.base.comp.gpa;
2525 const cmd = self.data_in_code_cmd;
2526 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.data_in_code.size());
2527 defer buffer.deinit();
2528 try self.data_in_code.write(self, buffer.writer());
2529 try self.base.file.?.pwriteAll(buffer.items, cmd.dataoff);
2530}
27892531
2790 return off + needed_size;
2532fn writeIndsymtab(self: *MachO) !void {
2533 const tracy = trace(@src());
2534 defer tracy.end();
2535 const gpa = self.base.comp.gpa;
2536 const cmd = self.dysymtab_cmd;
2537 const needed_size = cmd.nindirectsyms * @sizeOf(u32);
2538 var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size);
2539 defer buffer.deinit();
2540 try self.indsymtab.write(self, buffer.writer());
2541 try self.base.file.?.pwriteAll(buffer.items, cmd.indirectsymoff);
27912542}
27922543
2793fn writeFunctionStarts(self: *MachO, off: u32) !u32 {
2794 // TODO actually write it out
2795 const cmd = &self.function_starts_cmd;
2796 cmd.dataoff = off;
2797 return off;
2544pub fn writeSymtabToFile(self: *MachO) !void {
2545 const tracy = trace(@src());
2546 defer tracy.end();
2547 const cmd = self.symtab_cmd;
2548 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
2549 try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff);
27982550}
27992551
2800pub fn writeDataInCode(self: *MachO, base_address: u64, off: u32) !u32 {
2801 const cmd = &self.data_in_code_cmd;
2802 cmd.dataoff = off;
2552fn writeUnwindInfo(self: *MachO) !void {
2553 const tracy = trace(@src());
2554 defer tracy.end();
28032555
28042556 const gpa = self.base.comp.gpa;
2805 var dices = std.ArrayList(macho.data_in_code_entry).init(gpa);
2806 defer dices.deinit();
28072557
2808 for (self.objects.items) |index| {
2809 const object = self.getFile(index).?.object;
2810 const in_dices = object.getDataInCode();
2811
2812 try dices.ensureUnusedCapacity(in_dices.len);
2813
2814 var next_dice: usize = 0;
2815 for (object.atoms.items) |atom_index| {
2816 if (next_dice >= in_dices.len) break;
2817 const atom = self.getAtom(atom_index) orelse continue;
2818 const start_off = atom.getInputAddress(self);
2819 const end_off = start_off + atom.size;
2820 const start_dice = next_dice;
2821
2822 if (end_off < in_dices[next_dice].offset) continue;
2823
2824 while (next_dice < in_dices.len and
2825 in_dices[next_dice].offset < end_off) : (next_dice += 1)
2826 {}
2827
2828 if (atom.flags.alive) for (in_dices[start_dice..next_dice]) |dice| {
2829 dices.appendAssumeCapacity(.{
2830 .offset = @intCast(atom.getAddress(self) + dice.offset - start_off - base_address),
2831 .length = dice.length,
2832 .kind = dice.kind,
2833 });
2834 };
2835 }
2558 if (self.eh_frame_sect_index) |index| {
2559 const header = self.sections.items(.header)[index];
2560 const size = math.cast(usize, header.size) orelse return error.Overflow;
2561 const buffer = try gpa.alloc(u8, size);
2562 defer gpa.free(buffer);
2563 eh_frame.write(self, buffer);
2564 try self.base.file.?.pwriteAll(buffer, header.offset);
28362565 }
28372566
2838 const needed_size = math.cast(u32, dices.items.len * @sizeOf(macho.data_in_code_entry)) orelse return error.Overflow;
2839 cmd.datasize = needed_size;
2840
2841 try self.base.file.?.pwriteAll(mem.sliceAsBytes(dices.items), cmd.dataoff);
2842
2843 return off + needed_size;
2567 if (self.unwind_info_sect_index) |index| {
2568 const header = self.sections.items(.header)[index];
2569 const size = math.cast(usize, header.size) orelse return error.Overflow;
2570 const buffer = try gpa.alloc(u8, size);
2571 defer gpa.free(buffer);
2572 try self.unwind_info.write(self, buffer);
2573 try self.base.file.?.pwriteAll(buffer, header.offset);
2574 }
28442575}
28452576
2846pub fn calcSymtabSize(self: *MachO) !void {
2577fn calcSymtabSize(self: *MachO) !void {
28472578 const tracy = trace(@src());
28482579 defer tracy.end();
2849 const gpa = self.base.comp.gpa;
28502580
2851 var nlocals: u32 = 0;
2852 var nstabs: u32 = 0;
2853 var nexports: u32 = 0;
2854 var nimports: u32 = 0;
2855 var strsize: u32 = 0;
2581 const gpa = self.base.comp.gpa;
28562582
28572583 var files = std.ArrayList(File.Index).init(gpa);
28582584 defer files.deinit();
......@@ -2862,6 +2588,12 @@ pub fn calcSymtabSize(self: *MachO) !void {
28622588 for (self.dylibs.items) |index| files.appendAssumeCapacity(index);
28632589 if (self.internal_object) |index| files.appendAssumeCapacity(index);
28642590
2591 var nlocals: u32 = 0;
2592 var nstabs: u32 = 0;
2593 var nexports: u32 = 0;
2594 var nimports: u32 = 0;
2595 var strsize: u32 = 1;
2596
28652597 for (files.items) |index| {
28662598 const file = self.getFile(index).?;
28672599 const ctx = switch (file) {
......@@ -2871,7 +2603,7 @@ pub fn calcSymtabSize(self: *MachO) !void {
28712603 ctx.istab = nstabs;
28722604 ctx.iexport = nexports;
28732605 ctx.iimport = nimports;
2874 try file.calcSymtabSize(self);
2606 ctx.stroff = strsize;
28752607 nlocals += ctx.nlocals;
28762608 nstabs += ctx.nstabs;
28772609 nexports += ctx.nexports;
......@@ -2889,10 +2621,12 @@ pub fn calcSymtabSize(self: *MachO) !void {
28892621 ctx.iimport += nlocals + nstabs + nexports;
28902622 }
28912623
2624 try self.indsymtab.updateSize(self);
2625
28922626 {
28932627 const cmd = &self.symtab_cmd;
28942628 cmd.nsyms = nlocals + nstabs + nexports + nimports;
2895 cmd.strsize = strsize + 1;
2629 cmd.strsize = strsize;
28962630 }
28972631
28982632 {
......@@ -2906,60 +2640,6 @@ pub fn calcSymtabSize(self: *MachO) !void {
29062640 }
29072641}
29082642
2909pub fn writeSymtab(self: *MachO, off: u32) !u32 {
2910 const tracy = trace(@src());
2911 defer tracy.end();
2912 const gpa = self.base.comp.gpa;
2913 const cmd = &self.symtab_cmd;
2914 cmd.symoff = off;
2915
2916 try self.symtab.resize(gpa, cmd.nsyms);
2917 try self.strtab.ensureUnusedCapacity(gpa, cmd.strsize - 1);
2918
2919 if (self.getZigObject()) |zo| {
2920 zo.writeSymtab(self, self);
2921 }
2922 for (self.objects.items) |index| {
2923 try self.getFile(index).?.writeSymtab(self, self);
2924 }
2925 for (self.dylibs.items) |index| {
2926 try self.getFile(index).?.writeSymtab(self, self);
2927 }
2928 if (self.getInternalObject()) |internal| {
2929 internal.writeSymtab(self, self);
2930 }
2931
2932 assert(self.strtab.items.len == cmd.strsize);
2933
2934 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
2935
2936 return off + cmd.nsyms * @sizeOf(macho.nlist_64);
2937}
2938
2939fn writeIndsymtab(self: *MachO, off: u32) !u32 {
2940 const gpa = self.base.comp.gpa;
2941 const cmd = &self.dysymtab_cmd;
2942 cmd.indirectsymoff = off;
2943 cmd.nindirectsyms = self.indsymtab.nsyms(self);
2944
2945 const needed_size = cmd.nindirectsyms * @sizeOf(u32);
2946 var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size);
2947 defer buffer.deinit();
2948 try self.indsymtab.write(self, buffer.writer());
2949
2950 try self.base.file.?.pwriteAll(buffer.items, cmd.indirectsymoff);
2951 assert(buffer.items.len == needed_size);
2952
2953 return off + needed_size;
2954}
2955
2956pub fn writeStrtab(self: *MachO, off: u32) !u32 {
2957 const cmd = &self.symtab_cmd;
2958 cmd.stroff = off;
2959 try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff);
2960 return off + cmd.strsize;
2961}
2962
29632643fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
29642644 const comp = self.base.comp;
29652645 const gpa = comp.gpa;
......@@ -2999,18 +2679,20 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
29992679 try load_commands.writeDylinkerLC(writer);
30002680 ncmds += 1;
30012681
3002 if (self.entry_index) |global_index| {
3003 const sym = self.getSymbol(global_index);
3004 const seg = self.getTextSegment();
3005 const entryoff: u32 = if (sym.getFile(self) == null)
3006 0
3007 else
3008 @as(u32, @intCast(sym.getAddress(.{ .stubs = true }, self) - seg.vmaddr));
3009 try writer.writeStruct(macho.entry_point_command{
3010 .entryoff = entryoff,
3011 .stacksize = self.base.stack_size,
3012 });
3013 ncmds += 1;
2682 if (self.getInternalObject()) |obj| {
2683 if (obj.getEntryRef(self)) |ref| {
2684 const sym = ref.getSymbol(self).?;
2685 const seg = self.getTextSegment();
2686 const entryoff: u32 = if (sym.getFile(self) == null)
2687 0
2688 else
2689 @as(u32, @intCast(sym.getAddress(.{ .stubs = true }, self) - seg.vmaddr));
2690 try writer.writeStruct(macho.entry_point_command{
2691 .entryoff = entryoff,
2692 .stacksize = self.base.stack_size,
2693 });
2694 ncmds += 1;
2695 }
30142696 }
30152697
30162698 if (self.base.isDynLib()) {
......@@ -3919,157 +3601,6 @@ pub fn getFileHandle(self: MachO, index: File.HandleIndex) File.Handle {
39193601 return self.file_handles.items[index];
39203602}
39213603
3922pub fn addAtom(self: *MachO) error{OutOfMemory}!Atom.Index {
3923 const index = @as(Atom.Index, @intCast(self.atoms.items.len));
3924 const atom = try self.atoms.addOne(self.base.comp.gpa);
3925 atom.* = .{};
3926 return index;
3927}
3928
3929pub fn getAtom(self: *MachO, index: Atom.Index) ?*Atom {
3930 if (index == 0) return null;
3931 assert(index < self.atoms.items.len);
3932 return &self.atoms.items[index];
3933}
3934
3935pub fn addAtomExtra(self: *MachO, extra: Atom.Extra) !u32 {
3936 const fields = @typeInfo(Atom.Extra).Struct.fields;
3937 try self.atoms_extra.ensureUnusedCapacity(self.base.comp.gpa, fields.len);
3938 return self.addAtomExtraAssumeCapacity(extra);
3939}
3940
3941pub fn addAtomExtraAssumeCapacity(self: *MachO, extra: Atom.Extra) u32 {
3942 const index = @as(u32, @intCast(self.atoms_extra.items.len));
3943 const fields = @typeInfo(Atom.Extra).Struct.fields;
3944 inline for (fields) |field| {
3945 self.atoms_extra.appendAssumeCapacity(switch (field.type) {
3946 u32 => @field(extra, field.name),
3947 else => @compileError("bad field type"),
3948 });
3949 }
3950 return index;
3951}
3952
3953pub fn getAtomExtra(self: *MachO, index: u32) ?Atom.Extra {
3954 if (index == 0) return null;
3955 const fields = @typeInfo(Atom.Extra).Struct.fields;
3956 var i: usize = index;
3957 var result: Atom.Extra = undefined;
3958 inline for (fields) |field| {
3959 @field(result, field.name) = switch (field.type) {
3960 u32 => self.atoms_extra.items[i],
3961 else => @compileError("bad field type"),
3962 };
3963 i += 1;
3964 }
3965 return result;
3966}
3967
3968pub fn setAtomExtra(self: *MachO, index: u32, extra: Atom.Extra) void {
3969 assert(index > 0);
3970 const fields = @typeInfo(Atom.Extra).Struct.fields;
3971 inline for (fields, 0..) |field, i| {
3972 self.atoms_extra.items[index + i] = switch (field.type) {
3973 u32 => @field(extra, field.name),
3974 else => @compileError("bad field type"),
3975 };
3976 }
3977}
3978
3979pub fn addSymbol(self: *MachO) !Symbol.Index {
3980 const index = @as(Symbol.Index, @intCast(self.symbols.items.len));
3981 const symbol = try self.symbols.addOne(self.base.comp.gpa);
3982 symbol.* = .{};
3983 return index;
3984}
3985
3986pub fn getSymbol(self: *MachO, index: Symbol.Index) *Symbol {
3987 assert(index < self.symbols.items.len);
3988 return &self.symbols.items[index];
3989}
3990
3991pub fn addSymbolExtra(self: *MachO, extra: Symbol.Extra) !u32 {
3992 const fields = @typeInfo(Symbol.Extra).Struct.fields;
3993 try self.symbols_extra.ensureUnusedCapacity(self.base.comp.gpa, fields.len);
3994 return self.addSymbolExtraAssumeCapacity(extra);
3995}
3996
3997pub fn addSymbolExtraAssumeCapacity(self: *MachO, extra: Symbol.Extra) u32 {
3998 const index = @as(u32, @intCast(self.symbols_extra.items.len));
3999 const fields = @typeInfo(Symbol.Extra).Struct.fields;
4000 inline for (fields) |field| {
4001 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
4002 u32 => @field(extra, field.name),
4003 else => @compileError("bad field type"),
4004 });
4005 }
4006 return index;
4007}
4008
4009pub fn getSymbolExtra(self: MachO, index: u32) ?Symbol.Extra {
4010 if (index == 0) return null;
4011 const fields = @typeInfo(Symbol.Extra).Struct.fields;
4012 var i: usize = index;
4013 var result: Symbol.Extra = undefined;
4014 inline for (fields) |field| {
4015 @field(result, field.name) = switch (field.type) {
4016 u32 => self.symbols_extra.items[i],
4017 else => @compileError("bad field type"),
4018 };
4019 i += 1;
4020 }
4021 return result;
4022}
4023
4024pub fn setSymbolExtra(self: *MachO, index: u32, extra: Symbol.Extra) void {
4025 assert(index > 0);
4026 const fields = @typeInfo(Symbol.Extra).Struct.fields;
4027 inline for (fields, 0..) |field, i| {
4028 self.symbols_extra.items[index + i] = switch (field.type) {
4029 u32 => @field(extra, field.name),
4030 else => @compileError("bad field type"),
4031 };
4032 }
4033}
4034
4035const GetOrCreateGlobalResult = struct {
4036 found_existing: bool,
4037 index: Symbol.Index,
4038};
4039
4040pub fn getOrCreateGlobal(self: *MachO, off: u32) !GetOrCreateGlobalResult {
4041 const gpa = self.base.comp.gpa;
4042 const gop = try self.globals.getOrPut(gpa, off);
4043 if (!gop.found_existing) {
4044 const index = try self.addSymbol();
4045 const global = self.getSymbol(index);
4046 global.name = off;
4047 global.flags.global = true;
4048 gop.value_ptr.* = index;
4049 }
4050 return .{
4051 .found_existing = gop.found_existing,
4052 .index = gop.value_ptr.*,
4053 };
4054}
4055
4056pub fn getGlobalByName(self: *MachO, name: []const u8) ?Symbol.Index {
4057 const off = self.strings.getOffset(name) orelse return null;
4058 return self.globals.get(off);
4059}
4060
4061pub fn addUnwindRecord(self: *MachO) !UnwindInfo.Record.Index {
4062 const index = @as(UnwindInfo.Record.Index, @intCast(self.unwind_records.items.len));
4063 const rec = try self.unwind_records.addOne(self.base.comp.gpa);
4064 rec.* = .{};
4065 return index;
4066}
4067
4068pub fn getUnwindRecord(self: *MachO, index: UnwindInfo.Record.Index) *UnwindInfo.Record {
4069 assert(index < self.unwind_records.items.len);
4070 return &self.unwind_records.items[index];
4071}
4072
40733604pub fn addThunk(self: *MachO) !Thunk.Index {
40743605 const index = @as(Thunk.Index, @intCast(self.thunks.items.len));
40753606 const thunk = try self.thunks.addOne(self.base.comp.gpa);
......@@ -4208,22 +3739,23 @@ pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: an
42083739 try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
42093740}
42103741
4211fn reportDuplicates(self: *MachO, dupes: anytype) error{ HasDuplicates, OutOfMemory }!void {
3742fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
42123743 const tracy = trace(@src());
42133744 defer tracy.end();
42143745
42153746 const max_notes = 3;
42163747
42173748 var has_dupes = false;
4218 var it = dupes.iterator();
3749 var it = self.dupes.iterator();
42193750 while (it.next()) |entry| {
4220 const sym = self.getSymbol(entry.key_ptr.*);
3751 const sym = self.resolver.keys.items[entry.key_ptr.* - 1];
42213752 const notes = entry.value_ptr.*;
42223753 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
42233754
42243755 var err = try self.addErrorWithNotes(nnotes + 1);
42253756 try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.getName(self)});
42263757 try err.addNote(self, "defined by {}", .{sym.getFile(self).?.fmtPath()});
3758 has_dupes = true;
42273759
42283760 var inote: usize = 0;
42293761 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
......@@ -4235,10 +3767,7 @@ fn reportDuplicates(self: *MachO, dupes: anytype) error{ HasDuplicates, OutOfMem
42353767 const remaining = notes.items.len - max_notes;
42363768 try err.addNote(self, "defined {d} more times", .{remaining});
42373769 }
4238
4239 has_dupes = true;
42403770 }
4241
42423771 if (has_dupes) return error.HasDuplicates;
42433772}
42443773
......@@ -4435,15 +3964,18 @@ pub const base_tag: link.File.Tag = link.File.Tag.macho;
44353964const Section = struct {
44363965 header: macho.section_64,
44373966 segment_id: u8,
4438 atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
3967 atoms: std.ArrayListUnmanaged(Ref) = .{},
44393968 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
44403969 last_atom_index: Atom.Index = 0,
3970 thunks: std.ArrayListUnmanaged(Thunk.Index) = .{},
3971 out: std.ArrayListUnmanaged(u8) = .{},
3972 relocs: std.ArrayListUnmanaged(macho.relocation_info) = .{},
44413973};
44423974
44433975pub const LiteralPool = struct {
44443976 table: std.AutoArrayHashMapUnmanaged(void, void) = .{},
44453977 keys: std.ArrayListUnmanaged(Key) = .{},
4446 values: std.ArrayListUnmanaged(Atom.Index) = .{},
3978 values: std.ArrayListUnmanaged(MachO.Ref) = .{},
44473979 data: std.ArrayListUnmanaged(u8) = .{},
44483980
44493981 pub fn deinit(lp: *LiteralPool, allocator: Allocator) void {
......@@ -4453,17 +3985,21 @@ pub const LiteralPool = struct {
44533985 lp.data.deinit(allocator);
44543986 }
44553987
4456 pub fn getAtom(lp: LiteralPool, index: Index, macho_file: *MachO) *Atom {
4457 assert(index < lp.values.items.len);
4458 return macho_file.getAtom(lp.values.items[index]).?;
4459 }
4460
44613988 const InsertResult = struct {
44623989 found_existing: bool,
44633990 index: Index,
4464 atom: *Atom.Index,
3991 ref: *MachO.Ref,
44653992 };
44663993
3994 pub fn getSymbolRef(lp: LiteralPool, index: Index) MachO.Ref {
3995 assert(index < lp.values.items.len);
3996 return lp.values.items[index];
3997 }
3998
3999 pub fn getSymbol(lp: LiteralPool, index: Index, macho_file: *MachO) *Symbol {
4000 return lp.getSymbolRef(index).getSymbol(macho_file).?;
4001 }
4002
44674003 pub fn insert(lp: *LiteralPool, allocator: Allocator, @"type": u8, string: []const u8) !InsertResult {
44684004 const size: u32 = @intCast(string.len);
44694005 try lp.data.ensureUnusedCapacity(allocator, size);
......@@ -4479,7 +4015,7 @@ pub const LiteralPool = struct {
44794015 return .{
44804016 .found_existing = gop.found_existing,
44814017 .index = @intCast(gop.index),
4482 .atom = &lp.values.items[gop.index],
4018 .ref = &lp.values.items[gop.index],
44834019 };
44844020 }
44854021
......@@ -4525,12 +4061,6 @@ const HotUpdateState = struct {
45254061 mach_task: ?std.c.MachTask = null,
45264062};
45274063
4528pub const DynamicRelocs = struct {
4529 rebase_relocs: u32 = 0,
4530 bind_relocs: u32 = 0,
4531 weak_bind_relocs: u32 = 0,
4532};
4533
45344064pub const SymtabCtx = struct {
45354065 ilocal: u32 = 0,
45364066 istab: u32 = 0,
......@@ -4540,6 +4070,7 @@ pub const SymtabCtx = struct {
45404070 nstabs: u32 = 0,
45414071 nexports: u32 = 0,
45424072 nimports: u32 = 0,
4073 stroff: u32 = 0,
45434074 strsize: u32 = 0,
45444075};
45454076
......@@ -4826,6 +4357,136 @@ const UndefinedTreatment = enum {
48264357 dynamic_lookup,
48274358};
48284359
4360/// A reference to atom or symbol in an input file.
4361/// If file == 0, symbol is an undefined global.
4362pub const Ref = struct {
4363 index: u32,
4364 file: File.Index,
4365
4366 pub fn eql(ref: Ref, other: Ref) bool {
4367 return ref.index == other.index and ref.file == other.file;
4368 }
4369
4370 pub fn getFile(ref: Ref, macho_file: *MachO) ?File {
4371 return macho_file.getFile(ref.file);
4372 }
4373
4374 pub fn getAtom(ref: Ref, macho_file: *MachO) ?*Atom {
4375 const file = ref.getFile(macho_file) orelse return null;
4376 return file.getAtom(ref.index);
4377 }
4378
4379 pub fn getSymbol(ref: Ref, macho_file: *MachO) ?*Symbol {
4380 const file = ref.getFile(macho_file) orelse return null;
4381 return switch (file) {
4382 inline else => |x| &x.symbols.items[ref.index],
4383 };
4384 }
4385
4386 pub fn format(
4387 ref: Ref,
4388 comptime unused_fmt_string: []const u8,
4389 options: std.fmt.FormatOptions,
4390 writer: anytype,
4391 ) !void {
4392 _ = unused_fmt_string;
4393 _ = options;
4394 try writer.print("%{d} in file({d})", .{ ref.index, ref.file });
4395 }
4396};
4397
4398pub const SymbolResolver = struct {
4399 keys: std.ArrayListUnmanaged(Key) = .{},
4400 values: std.ArrayListUnmanaged(Ref) = .{},
4401 table: std.AutoArrayHashMapUnmanaged(void, void) = .{},
4402
4403 const Result = struct {
4404 found_existing: bool,
4405 index: Index,
4406 ref: *Ref,
4407 };
4408
4409 pub fn deinit(resolver: *SymbolResolver, allocator: Allocator) void {
4410 resolver.keys.deinit(allocator);
4411 resolver.values.deinit(allocator);
4412 resolver.table.deinit(allocator);
4413 }
4414
4415 pub fn getOrPut(
4416 resolver: *SymbolResolver,
4417 allocator: Allocator,
4418 ref: Ref,
4419 macho_file: *MachO,
4420 ) !Result {
4421 const adapter = Adapter{ .keys = resolver.keys.items, .macho_file = macho_file };
4422 const key = Key{ .index = ref.index, .file = ref.file };
4423 const gop = try resolver.table.getOrPutAdapted(allocator, key, adapter);
4424 if (!gop.found_existing) {
4425 try resolver.keys.append(allocator, key);
4426 _ = try resolver.values.addOne(allocator);
4427 }
4428 return .{
4429 .found_existing = gop.found_existing,
4430 .index = @intCast(gop.index + 1),
4431 .ref = &resolver.values.items[gop.index],
4432 };
4433 }
4434
4435 pub fn get(resolver: SymbolResolver, index: Index) ?Ref {
4436 if (index == 0) return null;
4437 return resolver.values.items[index - 1];
4438 }
4439
4440 pub fn reset(resolver: *SymbolResolver) void {
4441 resolver.keys.clearRetainingCapacity();
4442 resolver.values.clearRetainingCapacity();
4443 resolver.table.clearRetainingCapacity();
4444 }
4445
4446 const Key = struct {
4447 index: Symbol.Index,
4448 file: File.Index,
4449
4450 fn getName(key: Key, macho_file: *MachO) [:0]const u8 {
4451 const ref = Ref{ .index = key.index, .file = key.file };
4452 return ref.getSymbol(macho_file).?.getName(macho_file);
4453 }
4454
4455 pub fn getFile(key: Key, macho_file: *MachO) ?File {
4456 const ref = Ref{ .index = key.index, .file = key.file };
4457 return ref.getFile(macho_file);
4458 }
4459
4460 fn eql(key: Key, other: Key, macho_file: *MachO) bool {
4461 const key_name = key.getName(macho_file);
4462 const other_name = other.getName(macho_file);
4463 return mem.eql(u8, key_name, other_name);
4464 }
4465
4466 fn hash(key: Key, macho_file: *MachO) u32 {
4467 const name = key.getName(macho_file);
4468 return @truncate(Hash.hash(0, name));
4469 }
4470 };
4471
4472 const Adapter = struct {
4473 keys: []const Key,
4474 macho_file: *MachO,
4475
4476 pub fn eql(ctx: @This(), key: Key, b_void: void, b_map_index: usize) bool {
4477 _ = b_void;
4478 const other = ctx.keys[b_map_index];
4479 return key.eql(other, ctx.macho_file);
4480 }
4481
4482 pub fn hash(ctx: @This(), key: Key) u32 {
4483 return key.hash(ctx.macho_file);
4484 }
4485 };
4486
4487 pub const Index = u32;
4488};
4489
48294490const MachO = @This();
48304491
48314492const std = @import("std");
......@@ -4842,6 +4503,7 @@ const mem = std.mem;
48424503const meta = std.meta;
48434504
48444505const aarch64 = @import("../arch/aarch64/bits.zig");
4506const bind = @import("MachO/dyld_info/bind.zig");
48454507const calcUuid = @import("MachO/uuid.zig").calcUuid;
48464508const codegen = @import("../codegen.zig");
48474509const dead_strip = @import("MachO/dead_strip.zig");
......@@ -4862,13 +4524,14 @@ const Alignment = Atom.Alignment;
48624524const Allocator = mem.Allocator;
48634525const Archive = @import("MachO/Archive.zig");
48644526pub const Atom = @import("MachO/Atom.zig");
4865const BindSection = synthetic.BindSection;
4527const Bind = bind.Bind;
48664528const Cache = std.Build.Cache;
48674529const CodeSignature = @import("MachO/CodeSignature.zig");
48684530const Compilation = @import("../Compilation.zig");
4531const DataInCode = synthetic.DataInCode;
48694532pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
48704533const Dylib = @import("MachO/Dylib.zig");
4871const ExportTrieSection = synthetic.ExportTrieSection;
4534const ExportTrie = @import("MachO/dyld_info/Trie.zig");
48724535const File = @import("MachO/file.zig").File;
48734536const GotSection = synthetic.GotSection;
48744537const Hash = std.hash.Wyhash;
......@@ -4876,7 +4539,7 @@ const Indsymtab = synthetic.Indsymtab;
48764539const InternalObject = @import("MachO/InternalObject.zig");
48774540const ObjcStubsSection = synthetic.ObjcStubsSection;
48784541const Object = @import("MachO/Object.zig");
4879const LazyBindSection = synthetic.LazyBindSection;
4542const LazyBind = bind.LazyBind;
48804543const LaSymbolPtrSection = synthetic.LaSymbolPtrSection;
48814544const LibStub = tapi.LibStub;
48824545const Liveness = @import("../Liveness.zig");
......@@ -4886,7 +4549,7 @@ const Zcu = @import("../Zcu.zig");
48864549/// Deprecated.
48874550const Module = Zcu;
48884551const InternPool = @import("../InternPool.zig");
4889const RebaseSection = synthetic.RebaseSection;
4552const Rebase = @import("MachO/dyld_info/Rebase.zig");
48904553pub const Relocation = @import("MachO/Relocation.zig");
48914554const StringTable = @import("StringTable.zig");
48924555const StubsSection = synthetic.StubsSection;
......@@ -4896,6 +4559,6 @@ const Thunk = thunks.Thunk;
48964559const TlvPtrSection = synthetic.TlvPtrSection;
48974560const Value = @import("../Value.zig");
48984561const UnwindInfo = @import("MachO/UnwindInfo.zig");
4899const WeakBindSection = synthetic.WeakBindSection;
4562const WeakBind = bind.WeakBind;
49004563const ZigGotSection = synthetic.ZigGotSection;
49014564const ZigObject = @import("MachO/ZigObject.zig");
src/link/MachO/Archive.zig+2-2
......@@ -67,9 +67,9 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
6767 mem.eql(u8, name, SYMDEF64_SORTED)) continue;
6868
6969 const object = Object{
70 .archive = .{
70 .offset = pos,
71 .in_archive = .{
7172 .path = try gpa.dupe(u8, path),
72 .offset = pos,
7373 .size = hdr_size,
7474 },
7575 .path = try gpa.dupe(u8, name),
src/link/MachO/Atom.zig+116-156
......@@ -47,16 +47,6 @@ pub fn getFile(self: Atom, macho_file: *MachO) File {
4747 return macho_file.getFile(self.file).?;
4848}
4949
50pub fn getData(self: Atom, macho_file: *MachO, buffer: []u8) !void {
51 assert(buffer.len == self.size);
52 switch (self.getFile(macho_file)) {
53 .internal => |x| try x.getAtomData(self, buffer),
54 .object => |x| try x.getAtomData(macho_file, self, buffer),
55 .zig_object => |x| try x.getAtomData(macho_file, self, buffer),
56 else => unreachable,
57 }
58}
59
6050pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation {
6151 return switch (self.getFile(macho_file)) {
6252 .dylib => unreachable,
......@@ -88,17 +78,18 @@ pub fn getPriority(self: Atom, macho_file: *MachO) u64 {
8878}
8979
9080pub fn getUnwindRecords(self: Atom, macho_file: *MachO) []const UnwindInfo.Record.Index {
91 if (!self.flags.unwind) return &[0]UnwindInfo.Record.Index{};
92 const extra = self.getExtra(macho_file).?;
81 const extra = self.getExtra(macho_file);
9382 return switch (self.getFile(macho_file)) {
94 .dylib, .zig_object, .internal => unreachable,
95 .object => |x| x.unwind_records.items[extra.unwind_index..][0..extra.unwind_count],
83 .dylib => unreachable,
84 .zig_object, .internal => &[0]UnwindInfo.Record.Index{},
85 .object => |x| x.unwind_records_indexes.items[extra.unwind_index..][0..extra.unwind_count],
9686 };
9787}
9888
9989pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
90 const object = self.getFile(macho_file).object;
10091 for (self.getUnwindRecords(macho_file)) |cu_index| {
101 const cu = macho_file.getUnwindRecord(cu_index);
92 const cu = object.getUnwindRecord(cu_index);
10293 cu.alive = false;
10394
10495 if (cu.getFdePtr(macho_file)) |fde| {
......@@ -108,44 +99,39 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
10899}
109100
110101pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
111 assert(self.flags.thunk);
112 const extra = self.getExtra(macho_file).?;
102 const extra = self.getExtra(macho_file);
113103 return macho_file.getThunk(extra.thunk);
114104}
115105
116pub fn getLiteralPoolIndex(self: Atom, macho_file: *MachO) ?MachO.LiteralPool.Index {
117 if (!self.flags.literal_pool) return null;
118 return self.getExtra(macho_file).?.literal_index;
119}
120
121106const AddExtraOpts = struct {
122107 thunk: ?u32 = null,
123108 rel_index: ?u32 = null,
124109 rel_count: ?u32 = null,
110 rel_out_index: ?u32 = null,
111 rel_out_count: ?u32 = null,
125112 unwind_index: ?u32 = null,
126113 unwind_count: ?u32 = null,
127 literal_index: ?u32 = null,
114 literal_pool_index: ?u32 = null,
115 literal_symbol_index: ?u32 = null,
128116};
129117
130pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) !void {
131 if (atom.getExtra(macho_file) == null) {
132 atom.extra = try macho_file.addAtomExtra(.{});
133 }
134 var extra = atom.getExtra(macho_file).?;
118pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) void {
119 const file = atom.getFile(macho_file);
120 var extra = file.getAtomExtra(atom.extra);
135121 inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
136122 if (@field(opts, field.name)) |x| {
137123 @field(extra, field.name) = x;
138124 }
139125 }
140 atom.setExtra(extra, macho_file);
126 file.setAtomExtra(atom.extra, extra);
141127}
142128
143pub inline fn getExtra(atom: Atom, macho_file: *MachO) ?Extra {
144 return macho_file.getAtomExtra(atom.extra);
129pub inline fn getExtra(atom: Atom, macho_file: *MachO) Extra {
130 return atom.getFile(macho_file).getAtomExtra(atom.extra);
145131}
146132
147133pub inline fn setExtra(atom: Atom, extra: Extra, macho_file: *MachO) void {
148 macho_file.setAtomExtra(atom.extra, extra);
134 atom.getFile(macho_file).setAtomExtra(atom.extra, extra);
149135}
150136
151137pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
......@@ -227,7 +213,8 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
227213/// File offset relocation happens transparently, so it is not included in
228214/// this calculation.
229215pub fn capacity(self: Atom, macho_file: *MachO) u64 {
230 const next_addr = if (macho_file.getAtom(self.next_index)) |next|
216 const zo = macho_file.getZigObject().?;
217 const next_addr = if (zo.getAtom(self.next_index)) |next|
231218 next.getAddress(macho_file)
232219 else
233220 std.math.maxInt(u32);
......@@ -236,7 +223,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {
236223
237224pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
238225 // No need to keep a free list node for the last block.
239 const next = macho_file.getAtom(self.next_index) orelse return false;
226 const zo = macho_file.getZigObject().?;
227 const next = zo.getAtom(self.next_index) orelse return false;
240228 const cap = next.getAddress(macho_file) - self.getAddress(macho_file);
241229 const ideal_cap = MachO.padToIdeal(self.size);
242230 if (cap <= ideal_cap) return false;
......@@ -245,6 +233,7 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
245233}
246234
247235pub fn allocate(self: *Atom, macho_file: *MachO) !void {
236 const zo = macho_file.getZigObject().?;
248237 const sect = &macho_file.sections.items(.header)[self.out_n_sect];
249238 const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect];
250239 const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect];
......@@ -264,7 +253,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
264253 var i: usize = free_list.items.len;
265254 while (i < free_list.items.len) {
266255 const big_atom_index = free_list.items[i];
267 const big_atom = macho_file.getAtom(big_atom_index).?;
256 const big_atom = zo.getAtom(big_atom_index).?;
268257 // We now have a pointer to a live atom that has too much capacity.
269258 // Is it enough that we could fit this new atom?
270259 const cap = big_atom.capacity(macho_file);
......@@ -296,7 +285,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
296285 free_list_removal = i;
297286 }
298287 break :blk new_start_vaddr;
299 } else if (macho_file.getAtom(last_atom_index.*)) |last| {
288 } else if (zo.getAtom(last_atom_index.*)) |last| {
300289 const ideal_capacity = MachO.padToIdeal(last.size);
301290 const ideal_capacity_end_vaddr = last.value + ideal_capacity;
302291 const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr);
......@@ -316,7 +305,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
316305 });
317306
318307 const expand_section = if (atom_placement) |placement_index|
319 macho_file.getAtom(placement_index).?.next_index == 0
308 zo.getAtom(placement_index).?.next_index == 0
320309 else
321310 true;
322311 if (expand_section) {
......@@ -341,15 +330,15 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
341330 // This function can also reallocate an atom.
342331 // In this case we need to "unplug" it from its previous location before
343332 // plugging it in to its new location.
344 if (macho_file.getAtom(self.prev_index)) |prev| {
333 if (zo.getAtom(self.prev_index)) |prev| {
345334 prev.next_index = self.next_index;
346335 }
347 if (macho_file.getAtom(self.next_index)) |next| {
336 if (zo.getAtom(self.next_index)) |next| {
348337 next.prev_index = self.prev_index;
349338 }
350339
351340 if (atom_placement) |big_atom_index| {
352 const big_atom = macho_file.getAtom(big_atom_index).?;
341 const big_atom = zo.getAtom(big_atom_index).?;
353342 self.prev_index = big_atom_index;
354343 self.next_index = big_atom.next_index;
355344 big_atom.next_index = self.atom_index;
......@@ -379,6 +368,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void {
379368
380369 const comp = macho_file.base.comp;
381370 const gpa = comp.gpa;
371 const zo = macho_file.getZigObject().?;
382372 const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect];
383373 const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect];
384374 var already_have_free_list_node = false;
......@@ -397,9 +387,9 @@ pub fn free(self: *Atom, macho_file: *MachO) void {
397387 }
398388 }
399389
400 if (macho_file.getAtom(last_atom_index.*)) |last_atom| {
390 if (zo.getAtom(last_atom_index.*)) |last_atom| {
401391 if (last_atom.atom_index == self.atom_index) {
402 if (macho_file.getAtom(self.prev_index)) |_| {
392 if (zo.getAtom(self.prev_index)) |_| {
403393 // TODO shrink the section size here
404394 last_atom_index.* = self.prev_index;
405395 } else {
......@@ -408,7 +398,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void {
408398 }
409399 }
410400
411 if (macho_file.getAtom(self.prev_index)) |prev| {
401 if (zo.getAtom(self.prev_index)) |prev| {
412402 prev.next_index = self.next_index;
413403 if (!already_have_free_list_node and prev.*.freeListEligible(macho_file)) {
414404 // The free list is heuristics, it doesn't have to be perfect, so we can
......@@ -419,7 +409,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void {
419409 self.prev_index = 0;
420410 }
421411
422 if (macho_file.getAtom(self.next_index)) |next| {
412 if (zo.getAtom(self.next_index)) |next| {
423413 next.prev_index = self.prev_index;
424414 } else {
425415 self.next_index = 0;
......@@ -437,8 +427,7 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {
437427 const gpa = macho_file.base.comp.gpa;
438428 const file = self.getFile(macho_file);
439429 assert(file == .zig_object);
440 assert(self.flags.relocs);
441 var extra = self.getExtra(macho_file).?;
430 var extra = self.getExtra(macho_file);
442431 const rels = &file.zig_object.relocs.items[extra.rel_index];
443432 try rels.append(gpa, reloc);
444433 extra.rel_count += 1;
......@@ -446,9 +435,8 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {
446435}
447436
448437pub fn freeRelocs(self: *Atom, macho_file: *MachO) void {
449 if (!self.flags.relocs) return;
450438 self.getFile(macho_file).zig_object.freeAtomRelocs(self.*, macho_file);
451 var extra = self.getExtra(macho_file).?;
439 var extra = self.getExtra(macho_file);
452440 extra.rel_count = 0;
453441 self.setExtra(extra, macho_file);
454442}
......@@ -458,11 +446,6 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
458446 defer tracy.end();
459447 assert(self.flags.alive);
460448
461 const dynrel_ctx = switch (self.getFile(macho_file)) {
462 .zig_object => |x| &x.dynamic_relocs,
463 .object => |x| &x.dynamic_relocs,
464 else => unreachable,
465 };
466449 const relocs = self.getRelocs(macho_file);
467450
468451 for (relocs) |rel| {
......@@ -470,7 +453,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
470453
471454 switch (rel.type) {
472455 .branch => {
473 const symbol = rel.getTargetSymbol(macho_file);
456 const symbol = rel.getTargetSymbol(self, macho_file);
474457 if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) {
475458 symbol.flags.stubs = true;
476459 if (symbol.flags.weak) {
......@@ -485,7 +468,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
485468 .got_load_page,
486469 .got_load_pageoff,
487470 => {
488 const symbol = rel.getTargetSymbol(macho_file);
471 const symbol = rel.getTargetSymbol(self, macho_file);
489472 if (symbol.flags.import or
490473 (symbol.flags.@"export" and symbol.flags.weak) or
491474 symbol.flags.interposable or
......@@ -499,18 +482,18 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
499482 },
500483
501484 .zig_got_load => {
502 assert(rel.getTargetSymbol(macho_file).flags.has_zig_got);
485 assert(rel.getTargetSymbol(self, macho_file).flags.has_zig_got);
503486 },
504487
505488 .got => {
506 rel.getTargetSymbol(macho_file).flags.needs_got = true;
489 rel.getTargetSymbol(self, macho_file).flags.needs_got = true;
507490 },
508491
509492 .tlv,
510493 .tlvp_page,
511494 .tlvp_pageoff,
512495 => {
513 const symbol = rel.getTargetSymbol(macho_file);
496 const symbol = rel.getTargetSymbol(self, macho_file);
514497 if (!symbol.flags.tlv) {
515498 try macho_file.reportParseError2(
516499 self.getFile(macho_file).getIndex(),
......@@ -529,27 +512,21 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
529512 .unsigned => {
530513 if (rel.meta.length == 3) { // TODO this really should check if this is pointer width
531514 if (rel.tag == .@"extern") {
532 const symbol = rel.getTargetSymbol(macho_file);
515 const symbol = rel.getTargetSymbol(self, macho_file);
533516 if (symbol.isTlvInit(macho_file)) {
534517 macho_file.has_tlv = true;
535518 continue;
536519 }
537520 if (symbol.flags.import) {
538 dynrel_ctx.bind_relocs += 1;
539521 if (symbol.flags.weak) {
540 dynrel_ctx.weak_bind_relocs += 1;
541522 macho_file.binds_to_weak = true;
542523 }
543524 continue;
544525 }
545526 if (symbol.flags.@"export" and symbol.flags.weak) {
546 dynrel_ctx.weak_bind_relocs += 1;
547527 macho_file.binds_to_weak = true;
548 } else if (symbol.flags.interposable) {
549 dynrel_ctx.bind_relocs += 1;
550528 }
551529 }
552 dynrel_ctx.rebase_relocs += 1;
553530 }
554531 },
555532
......@@ -568,14 +545,15 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
568545fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
569546 if (rel.tag == .local) return false;
570547
571 const sym = rel.getTargetSymbol(macho_file);
572 if (sym.getFile(macho_file) == null) {
548 const file = self.getFile(macho_file);
549 const ref = file.getSymbolRef(rel.target, macho_file);
550 if (ref.getFile(macho_file) == null) {
573551 const gpa = macho_file.base.comp.gpa;
574 const gop = try macho_file.undefs.getOrPut(gpa, rel.target);
552 const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]);
575553 if (!gop.found_existing) {
576554 gop.value_ptr.* = .{};
577555 }
578 try gop.value_ptr.append(gpa, self.atom_index);
556 try gop.value_ptr.append(gpa, .{ .index = self.atom_index, .file = self.file });
579557 return true;
580558 }
581559
......@@ -591,7 +569,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
591569 const name = self.getName(macho_file);
592570 const relocs = self.getRelocs(macho_file);
593571
594 relocs_log.debug("{x}: {s}", .{ self.getAddress(macho_file), name });
572 relocs_log.debug("{x}: {s}", .{ self.value, name });
595573
596574 var has_error = false;
597575 var stream = std.io.fixedBufferStream(buffer);
......@@ -602,7 +580,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
602580 const subtractor = if (rel.meta.has_subtractor) relocs[i - 1] else null;
603581
604582 if (rel.tag == .@"extern") {
605 if (rel.getTargetSymbol(macho_file).getFile(macho_file) == null) continue;
583 if (rel.getTargetSymbol(self, macho_file).getFile(macho_file) == null) continue;
606584 }
607585
608586 try stream.seekTo(rel_offset);
......@@ -610,13 +588,19 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
610588 switch (err) {
611589 error.RelaxFail => {
612590 const target = switch (rel.tag) {
613 .@"extern" => rel.getTargetSymbol(macho_file).getName(macho_file),
614 .local => rel.getTargetAtom(macho_file).getName(macho_file),
591 .@"extern" => rel.getTargetSymbol(self, macho_file).getName(macho_file),
592 .local => rel.getTargetAtom(self, macho_file).getName(macho_file),
615593 };
616594 try macho_file.reportParseError2(
617595 file.getIndex(),
618 "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {s}, target {s}",
619 .{ name, self.getAddress(macho_file), rel.offset, @tagName(rel.type), target },
596 "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {}, target {s}",
597 .{
598 name,
599 self.getAddress(macho_file),
600 rel.offset,
601 rel.fmtPretty(macho_file.getTarget().cpu.arch),
602 target,
603 },
620604 );
621605 has_error = true;
622606 },
......@@ -649,14 +633,12 @@ fn resolveRelocInner(
649633) ResolveError!void {
650634 const cpu_arch = macho_file.getTarget().cpu.arch;
651635 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
652 const seg_id = macho_file.sections.items(.segment_id)[self.out_n_sect];
653 const seg = macho_file.segments.items[seg_id];
654636 const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset));
655637 const A = rel.addend + rel.getRelocAddend(cpu_arch);
656 const S: i64 = @intCast(rel.getTargetAddress(macho_file));
657 const G: i64 = @intCast(rel.getGotTargetAddress(macho_file));
638 const S: i64 = @intCast(rel.getTargetAddress(self, macho_file));
639 const G: i64 = @intCast(rel.getGotTargetAddress(self, macho_file));
658640 const TLS = @as(i64, @intCast(macho_file.getTlsAddress()));
659 const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(macho_file))) else 0;
641 const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(self, macho_file))) else 0;
660642 // Address of the __got_zig table entry if any.
661643 const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(macho_file)));
662644
......@@ -674,21 +656,21 @@ fn resolveRelocInner(
674656 }.divExact;
675657
676658 switch (rel.tag) {
677 .local => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] atom({d})", .{
659 .local => relocs_log.debug(" {x}<+{d}>: {}: [=> {x}] atom({d})", .{
678660 P,
679661 rel_offset,
680 @tagName(rel.type),
662 rel.fmtPretty(cpu_arch),
681663 S + A - SUB,
682 rel.getTargetAtom(macho_file).atom_index,
664 rel.getTargetAtom(self, macho_file).atom_index,
683665 }),
684 .@"extern" => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] G({x}) ZG({x}) ({s})", .{
666 .@"extern" => relocs_log.debug(" {x}<+{d}>: {}: [=> {x}] G({x}) ZG({x}) ({s})", .{
685667 P,
686668 rel_offset,
687 @tagName(rel.type),
669 rel.fmtPretty(cpu_arch),
688670 S + A - SUB,
689671 G + A,
690672 ZIG_GOT + A,
691 rel.getTargetSymbol(macho_file).getName(macho_file),
673 rel.getTargetSymbol(self, macho_file).getName(macho_file),
692674 }),
693675 }
694676
......@@ -699,34 +681,13 @@ fn resolveRelocInner(
699681 assert(!rel.meta.pcrel);
700682 if (rel.meta.length == 3) {
701683 if (rel.tag == .@"extern") {
702 const sym = rel.getTargetSymbol(macho_file);
684 const sym = rel.getTargetSymbol(self, macho_file);
703685 if (sym.isTlvInit(macho_file)) {
704686 try writer.writeInt(u64, @intCast(S - TLS), .little);
705687 return;
706688 }
707 const entry = bind.Entry{
708 .target = rel.target,
709 .offset = @as(u64, @intCast(P)) - seg.vmaddr,
710 .segment_id = seg_id,
711 .addend = A,
712 };
713 if (sym.flags.import) {
714 macho_file.bind.entries.appendAssumeCapacity(entry);
715 if (sym.flags.weak) {
716 macho_file.weak_bind.entries.appendAssumeCapacity(entry);
717 }
718 return;
719 }
720 if (sym.flags.@"export" and sym.flags.weak) {
721 macho_file.weak_bind.entries.appendAssumeCapacity(entry);
722 } else if (sym.flags.interposable) {
723 macho_file.bind.entries.appendAssumeCapacity(entry);
724 }
689 if (sym.flags.import) return;
725690 }
726 macho_file.rebase.entries.appendAssumeCapacity(.{
727 .offset = @as(u64, @intCast(P)) - seg.vmaddr,
728 .segment_id = seg_id,
729 });
730691 try writer.writeInt(u64, @bitCast(S + A - SUB), .little);
731692 } else if (rel.meta.length == 2) {
732693 try writer.writeInt(u32, @bitCast(@as(i32, @truncate(S + A - SUB))), .little);
......@@ -750,7 +711,7 @@ fn resolveRelocInner(
750711 .aarch64 => {
751712 const disp: i28 = math.cast(i28, S + A - P) orelse blk: {
752713 const thunk = self.getThunk(macho_file);
753 const S_: i64 = @intCast(thunk.getTargetAddress(rel.target, macho_file));
714 const S_: i64 = @intCast(thunk.getTargetAddress(rel.getTargetSymbolRef(self, macho_file), macho_file));
754715 break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;
755716 };
756717 aarch64.writeBranchImm(disp, code[rel_offset..][0..4]);
......@@ -763,7 +724,7 @@ fn resolveRelocInner(
763724 assert(rel.tag == .@"extern");
764725 assert(rel.meta.length == 2);
765726 assert(rel.meta.pcrel);
766 if (rel.getTargetSymbol(macho_file).flags.has_got) {
727 if (rel.getTargetSymbol(self, macho_file).flags.has_got) {
767728 try writer.writeInt(i32, @intCast(G + A - P), .little);
768729 } else {
769730 try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file);
......@@ -786,7 +747,7 @@ fn resolveRelocInner(
786747 assert(rel.tag == .@"extern");
787748 assert(rel.meta.length == 2);
788749 assert(rel.meta.pcrel);
789 const sym = rel.getTargetSymbol(macho_file);
750 const sym = rel.getTargetSymbol(self, macho_file);
790751 if (sym.flags.tlv_ptr) {
791752 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
792753 try writer.writeInt(i32, @intCast(S_ + A - P), .little);
......@@ -809,7 +770,7 @@ fn resolveRelocInner(
809770 assert(rel.tag == .@"extern");
810771 assert(rel.meta.length == 2);
811772 assert(rel.meta.pcrel);
812 const sym = rel.getTargetSymbol(macho_file);
773 const sym = rel.getTargetSymbol(self, macho_file);
813774 const source = math.cast(u64, P) orelse return error.Overflow;
814775 const target = target: {
815776 const target = switch (rel.type) {
......@@ -868,7 +829,7 @@ fn resolveRelocInner(
868829 assert(rel.meta.length == 2);
869830 assert(!rel.meta.pcrel);
870831
871 const sym = rel.getTargetSymbol(macho_file);
832 const sym = rel.getTargetSymbol(self, macho_file);
872833 const target = target: {
873834 const target = if (sym.flags.tlv_ptr) blk: {
874835 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
......@@ -945,11 +906,11 @@ const x86_64 = struct {
945906 },
946907 else => |x| {
947908 var err = try macho_file.addErrorWithNotes(2);
948 try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {s}", .{
909 try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{
949910 self.getName(macho_file),
950911 self.getAddress(macho_file),
951912 rel.offset,
952 @tagName(rel.type),
913 rel.fmtPretty(.x86_64),
953914 });
954915 try err.addNote(macho_file, "expected .mov instruction but found .{s}", .{@tagName(x)});
955916 try err.addNote(macho_file, "while parsing {}", .{self.getFile(macho_file).fmtPath()});
......@@ -1012,48 +973,49 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {
1012973 }
1013974}
1014975
1015pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.ArrayList(macho.relocation_info)) !void {
976pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) !void {
1016977 const tracy = trace(@src());
1017978 defer tracy.end();
1018979
1019980 const cpu_arch = macho_file.getTarget().cpu.arch;
1020981 const relocs = self.getRelocs(macho_file);
1021 var stream = std.io.fixedBufferStream(code);
1022982
983 var i: usize = 0;
1023984 for (relocs) |rel| {
1024 const rel_offset = rel.offset - self.off;
985 defer i += 1;
986 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
1025987 const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow;
988 assert(r_address >= 0);
1026989 const r_symbolnum = r_symbolnum: {
1027990 const r_symbolnum: u32 = switch (rel.tag) {
1028 .local => rel.getTargetAtom(macho_file).out_n_sect + 1,
1029 .@"extern" => rel.getTargetSymbol(macho_file).getOutputSymtabIndex(macho_file).?,
991 .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1,
992 .@"extern" => rel.getTargetSymbol(self, macho_file).getOutputSymtabIndex(macho_file).?,
1030993 };
1031994 break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow;
1032995 };
1033996 const r_extern = rel.tag == .@"extern";
1034997 var addend = rel.addend + rel.getRelocAddend(cpu_arch);
1035998 if (rel.tag == .local) {
1036 const target: i64 = @intCast(rel.getTargetAddress(macho_file));
999 const target: i64 = @intCast(rel.getTargetAddress(self, macho_file));
10371000 addend += target;
10381001 }
10391002
1040 try stream.seekTo(rel_offset);
1041
10421003 switch (cpu_arch) {
10431004 .aarch64 => {
10441005 if (rel.type == .unsigned) switch (rel.meta.length) {
10451006 0, 1 => unreachable,
1046 2 => try stream.writer().writeInt(i32, @truncate(addend), .little),
1047 3 => try stream.writer().writeInt(i64, addend, .little),
1007 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little),
1008 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little),
10481009 } else if (addend > 0) {
1049 buffer.appendAssumeCapacity(.{
1010 buffer[i] = .{
10501011 .r_address = r_address,
10511012 .r_symbolnum = @bitCast(math.cast(i24, addend) orelse return error.Overflow),
10521013 .r_pcrel = 0,
10531014 .r_length = 2,
10541015 .r_extern = 0,
10551016 .r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND),
1056 });
1017 };
1018 i += 1;
10571019 }
10581020
10591021 const r_type: macho.reloc_type_arm64 = switch (rel.type) {
......@@ -1077,14 +1039,14 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
10771039 .tlv,
10781040 => unreachable,
10791041 };
1080 buffer.appendAssumeCapacity(.{
1042 buffer[i] = .{
10811043 .r_address = r_address,
10821044 .r_symbolnum = r_symbolnum,
10831045 .r_pcrel = @intFromBool(rel.meta.pcrel),
10841046 .r_extern = @intFromBool(r_extern),
10851047 .r_length = rel.meta.length,
10861048 .r_type = @intFromEnum(r_type),
1087 });
1049 };
10881050 },
10891051 .x86_64 => {
10901052 if (rel.meta.pcrel) {
......@@ -1096,8 +1058,8 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
10961058 }
10971059 switch (rel.meta.length) {
10981060 0, 1 => unreachable,
1099 2 => try stream.writer().writeInt(i32, @truncate(addend), .little),
1100 3 => try stream.writer().writeInt(i64, addend, .little),
1061 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little),
1062 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little),
11011063 }
11021064
11031065 const r_type: macho.reloc_type_x86_64 = switch (rel.type) {
......@@ -1121,18 +1083,20 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
11211083 .tlvp_pageoff,
11221084 => unreachable,
11231085 };
1124 buffer.appendAssumeCapacity(.{
1086 buffer[i] = .{
11251087 .r_address = r_address,
11261088 .r_symbolnum = r_symbolnum,
11271089 .r_pcrel = @intFromBool(rel.meta.pcrel),
11281090 .r_extern = @intFromBool(r_extern),
11291091 .r_length = rel.meta.length,
11301092 .r_type = @intFromEnum(r_type),
1131 });
1093 };
11321094 },
11331095 else => unreachable,
11341096 }
11351097 }
1098
1099 assert(i == buffer.len);
11361100}
11371101
11381102pub fn format(
......@@ -1170,18 +1134,18 @@ fn format2(
11701134 _ = unused_fmt_string;
11711135 const atom = ctx.atom;
11721136 const macho_file = ctx.macho_file;
1173 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d})", .{
1174 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
1175 atom.out_n_sect, atom.alignment, atom.size,
1176 atom.getRelocs(macho_file).len,
1137 const file = atom.getFile(macho_file);
1138 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{
1139 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
1140 atom.out_n_sect, atom.alignment, atom.size,
1141 atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,
11771142 });
1178 if (atom.flags.thunk) try writer.print(" : thunk({d})", .{atom.getExtra(macho_file).?.thunk});
11791143 if (!atom.flags.alive) try writer.writeAll(" : [*]");
1180 if (atom.flags.unwind) {
1144 if (atom.getUnwindRecords(macho_file).len > 0) {
11811145 try writer.writeAll(" : unwind{ ");
1182 const extra = atom.getExtra(macho_file).?;
1146 const extra = atom.getExtra(macho_file);
11831147 for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| {
1184 const rec = macho_file.getUnwindRecord(index);
1148 const rec = file.object.getUnwindRecord(index);
11851149 try writer.print("{d}", .{index});
11861150 if (!rec.alive) try writer.writeAll("([*])");
11871151 if (i < extra.unwind_index + extra.unwind_count - 1) try writer.writeAll(", ");
......@@ -1198,18 +1162,6 @@ pub const Flags = packed struct {
11981162
11991163 /// Specifies if this atom has been visited during garbage collection.
12001164 visited: bool = false,
1201
1202 /// Whether this atom has a range extension thunk.
1203 thunk: bool = false,
1204
1205 /// Whether this atom has any relocations.
1206 relocs: bool = false,
1207
1208 /// Whether this atom has any unwind records.
1209 unwind: bool = false,
1210
1211 /// Whether this atom has LiteralPool entry.
1212 literal_pool: bool = false,
12131165};
12141166
12151167pub const Extra = struct {
......@@ -1222,6 +1174,12 @@ pub const Extra = struct {
12221174 /// Count of relocations belonging to this atom.
12231175 rel_count: u32 = 0,
12241176
1177 /// Start index of relocations being written out to file for this atom.
1178 rel_out_index: u32 = 0,
1179
1180 /// Count of relocations written out to file for this atom.
1181 rel_out_count: u32 = 0,
1182
12251183 /// Start index of relocations belonging to this atom.
12261184 unwind_index: u32 = 0,
12271185
......@@ -1229,14 +1187,16 @@ pub const Extra = struct {
12291187 unwind_count: u32 = 0,
12301188
12311189 /// Index into LiteralPool entry for this atom.
1232 literal_index: u32 = 0,
1190 literal_pool_index: u32 = 0,
1191
1192 /// Index into the File's symbol table for local symbol representing this literal atom.
1193 literal_symbol_index: u32 = 0,
12331194};
12341195
12351196pub const Alignment = @import("../../InternPool.zig").Alignment;
12361197
12371198const aarch64 = @import("../aarch64.zig");
12381199const assert = std.debug.assert;
1239const bind = @import("dyld_info/bind.zig");
12401200const macho = std.macho;
12411201const math = std.math;
12421202const mem = std.mem;
src/link/MachO/DebugSymbols.zig+6-6
......@@ -175,8 +175,9 @@ fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64
175175}
176176
177177pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
178 const zo = macho_file.getZigObject().?;
178179 for (self.relocs.items) |*reloc| {
179 const sym = macho_file.getSymbol(reloc.target);
180 const sym = zo.symbols.items[reloc.target];
180181 const sym_name = sym.getName(macho_file);
181182 const addr = switch (reloc.type) {
182183 .direct_load => sym.getAddress(.{}, macho_file),
......@@ -382,23 +383,22 @@ pub fn writeSymtab(self: *DebugSymbols, off: u32, macho_file: *MachO) !u32 {
382383 cmd.symoff = off;
383384
384385 try self.symtab.resize(gpa, cmd.nsyms);
385 try self.strtab.ensureUnusedCapacity(gpa, cmd.strsize - 1);
386 try self.strtab.resize(gpa, cmd.strsize);
387 self.strtab.items[0] = 0;
386388
387389 if (macho_file.getZigObject()) |zo| {
388390 zo.writeSymtab(macho_file, self);
389391 }
390392 for (macho_file.objects.items) |index| {
391 try macho_file.getFile(index).?.writeSymtab(macho_file, self);
393 macho_file.getFile(index).?.writeSymtab(macho_file, self);
392394 }
393395 for (macho_file.dylibs.items) |index| {
394 try macho_file.getFile(index).?.writeSymtab(macho_file, self);
396 macho_file.getFile(index).?.writeSymtab(macho_file, self);
395397 }
396398 if (macho_file.getInternalObject()) |internal| {
397399 internal.writeSymtab(macho_file, self);
398400 }
399401
400 assert(self.strtab.items.len == cmd.strsize);
401
402402 try self.file.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
403403
404404 return off + cmd.nsyms * @sizeOf(macho.nlist_64);
src/link/MachO/Dylib.zig+136-60
......@@ -6,7 +6,9 @@ strtab: std.ArrayListUnmanaged(u8) = .{},
66id: ?Id = null,
77ordinal: u16 = 0,
88
9symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
9symbols: std.ArrayListUnmanaged(Symbol) = .{},
10symbols_extra: std.ArrayListUnmanaged(u32) = .{},
11globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
1012dependents: std.ArrayListUnmanaged(Id) = .{},
1113rpaths: std.StringArrayHashMapUnmanaged(void) = .{},
1214umbrella: File.Index = 0,
......@@ -37,6 +39,8 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
3739 self.strtab.deinit(allocator);
3840 if (self.id) |*id| id.deinit(allocator);
3941 self.symbols.deinit(allocator);
42 self.symbols_extra.deinit(allocator);
43 self.globals.deinit(allocator);
4044 for (self.dependents.items) |*id| {
4145 id.deinit(allocator);
4246 }
......@@ -494,53 +498,55 @@ fn addObjCExport(
494498pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
495499 const gpa = macho_file.base.comp.gpa;
496500
497 try self.symbols.ensureTotalCapacityPrecise(gpa, self.exports.items(.name).len);
498
499 for (self.exports.items(.name)) |noff| {
500 const name = self.getString(noff);
501 const off = try macho_file.strings.insert(gpa, name);
502 const gop = try macho_file.getOrCreateGlobal(off);
503 self.symbols.addOneAssumeCapacity().* = gop.index;
501 const nsyms = self.exports.items(.name).len;
502 try self.symbols.ensureTotalCapacityPrecise(gpa, nsyms);
503 try self.symbols_extra.ensureTotalCapacityPrecise(gpa, nsyms * @sizeOf(Symbol.Extra));
504 try self.globals.ensureTotalCapacityPrecise(gpa, nsyms);
505 self.globals.resize(gpa, nsyms) catch unreachable;
506 @memset(self.globals.items, 0);
507
508 for (self.exports.items(.name), self.exports.items(.flags)) |noff, flags| {
509 const index = self.addSymbolAssumeCapacity();
510 const symbol = &self.symbols.items[index];
511 symbol.name = noff;
512 symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
513 symbol.flags.weak = flags.weak;
514 symbol.flags.tlv = flags.tlv;
515 symbol.visibility = .global;
504516 }
505517}
506518
507pub fn resolveSymbols(self: *Dylib, macho_file: *MachO) void {
519pub fn resolveSymbols(self: *Dylib, macho_file: *MachO) !void {
508520 const tracy = trace(@src());
509521 defer tracy.end();
510522
511523 if (!self.explicit and !self.hoisted) return;
512524
513 for (self.symbols.items, self.exports.items(.flags)) |index, flags| {
514 const global = macho_file.getSymbol(index);
525 const gpa = macho_file.base.comp.gpa;
526
527 for (self.exports.items(.flags), self.globals.items, 0..) |flags, *global, i| {
528 const gop = try macho_file.resolver.getOrPut(gpa, .{
529 .index = @intCast(i),
530 .file = self.index,
531 }, macho_file);
532 if (!gop.found_existing) {
533 gop.ref.* = .{ .index = 0, .file = 0 };
534 }
535 global.* = gop.index;
536
537 if (gop.ref.getFile(macho_file) == null) {
538 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
539 continue;
540 }
541
515542 if (self.asFile().getSymbolRank(.{
516543 .weak = flags.weak,
517 }) < global.getSymbolRank(macho_file)) {
518 global.value = 0;
519 global.atom = 0;
520 global.nlist_idx = 0;
521 global.file = self.index;
522 global.flags.weak = flags.weak;
523 global.flags.tlv = flags.tlv;
524 global.flags.dyn_ref = false;
525 global.flags.tentative = false;
526 global.visibility = .global;
544 }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
545 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
527546 }
528547 }
529548}
530549
531pub fn resetGlobals(self: *Dylib, macho_file: *MachO) void {
532 for (self.symbols.items) |sym_index| {
533 const sym = macho_file.getSymbol(sym_index);
534 const name = sym.name;
535 const global = sym.flags.global;
536 const weak_ref = sym.flags.weak_ref;
537 sym.* = .{};
538 sym.name = name;
539 sym.flags.global = global;
540 sym.flags.weak_ref = weak_ref;
541 }
542}
543
544550pub fn isAlive(self: Dylib, macho_file: *MachO) bool {
545551 if (!macho_file.dead_strip_dylibs) return self.explicit or self.referenced or self.needed;
546552 return self.referenced or self.needed;
......@@ -550,30 +556,31 @@ pub fn markReferenced(self: *Dylib, macho_file: *MachO) void {
550556 const tracy = trace(@src());
551557 defer tracy.end();
552558
553 for (self.symbols.items) |global_index| {
554 const global = macho_file.getSymbol(global_index);
555 const file_ptr = global.getFile(macho_file) orelse continue;
556 if (file_ptr.getIndex() != self.index) continue;
559 for (0..self.symbols.items.len) |i| {
560 const ref = self.getSymbolRef(@intCast(i), macho_file);
561 const file = ref.getFile(macho_file) orelse continue;
562 if (file.getIndex() != self.index) continue;
563 const global = ref.getSymbol(macho_file).?;
557564 if (global.isLocal()) continue;
558565 self.referenced = true;
559566 break;
560567 }
561568}
562569
563pub fn calcSymtabSize(self: *Dylib, macho_file: *MachO) !void {
570pub fn calcSymtabSize(self: *Dylib, macho_file: *MachO) void {
564571 const tracy = trace(@src());
565572 defer tracy.end();
566573
567 for (self.symbols.items) |global_index| {
568 const global = macho_file.getSymbol(global_index);
569 const file_ptr = global.getFile(macho_file) orelse continue;
570 if (file_ptr.getIndex() != self.index) continue;
571 if (global.isLocal()) continue;
572 assert(global.flags.import);
573 global.flags.output_symtab = true;
574 try global.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
574 for (self.symbols.items, 0..) |*sym, i| {
575 const ref = self.getSymbolRef(@intCast(i), macho_file);
576 const file = ref.getFile(macho_file) orelse continue;
577 if (file.getIndex() != self.index) continue;
578 if (sym.isLocal()) continue;
579 assert(sym.flags.import);
580 sym.flags.output_symtab = true;
581 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
575582 self.output_symtab_ctx.nimports += 1;
576 self.output_symtab_ctx.strsize += @as(u32, @intCast(global.getName(macho_file).len + 1));
583 self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
577584 }
578585}
579586
......@@ -581,17 +588,20 @@ pub fn writeSymtab(self: Dylib, macho_file: *MachO, ctx: anytype) void {
581588 const tracy = trace(@src());
582589 defer tracy.end();
583590
584 for (self.symbols.items) |global_index| {
585 const global = macho_file.getSymbol(global_index);
586 const file = global.getFile(macho_file) orelse continue;
591 var n_strx = self.output_symtab_ctx.stroff;
592 for (self.symbols.items, 0..) |sym, i| {
593 const ref = self.getSymbolRef(@intCast(i), macho_file);
594 const file = ref.getFile(macho_file) orelse continue;
587595 if (file.getIndex() != self.index) continue;
588 const idx = global.getOutputSymtabIndex(macho_file) orelse continue;
589 const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
590 ctx.strtab.appendSliceAssumeCapacity(global.getName(macho_file));
591 ctx.strtab.appendAssumeCapacity(0);
596 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
592597 const out_sym = &ctx.symtab.items[idx];
593598 out_sym.n_strx = n_strx;
594 global.setOutputSym(macho_file, out_sym);
599 sym.setOutputSym(macho_file, out_sym);
600 const name = sym.getName(macho_file);
601 @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
602 n_strx += @intCast(name.len);
603 ctx.strtab.items[n_strx] = 0;
604 n_strx += 1;
595605 }
596606}
597607
......@@ -605,7 +615,7 @@ fn addString(self: *Dylib, allocator: Allocator, name: []const u8) !u32 {
605615 return off;
606616}
607617
608pub inline fn getString(self: Dylib, off: u32) [:0]const u8 {
618pub fn getString(self: Dylib, off: u32) [:0]const u8 {
609619 assert(off < self.strtab.items.len);
610620 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
611621}
......@@ -614,6 +624,66 @@ pub fn asFile(self: *Dylib) File {
614624 return .{ .dylib = self };
615625}
616626
627fn addSymbol(self: *Dylib, allocator: Allocator) !Symbol.Index {
628 try self.symbols.ensureUnusedCapacity(allocator, 1);
629 return self.addSymbolAssumeCapacity();
630}
631
632fn addSymbolAssumeCapacity(self: *Dylib) Symbol.Index {
633 const index: Symbol.Index = @intCast(self.symbols.items.len);
634 const symbol = self.symbols.addOneAssumeCapacity();
635 symbol.* = .{ .file = self.index };
636 return index;
637}
638
639pub fn getSymbolRef(self: Dylib, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
640 const global_index = self.globals.items[index];
641 if (macho_file.resolver.get(global_index)) |ref| return ref;
642 return .{ .index = index, .file = self.index };
643}
644
645pub fn addSymbolExtra(self: *Dylib, allocator: Allocator, extra: Symbol.Extra) !u32 {
646 const fields = @typeInfo(Symbol.Extra).Struct.fields;
647 try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
648 return self.addSymbolExtraAssumeCapacity(extra);
649}
650
651fn addSymbolExtraAssumeCapacity(self: *Dylib, extra: Symbol.Extra) u32 {
652 const index = @as(u32, @intCast(self.symbols_extra.items.len));
653 const fields = @typeInfo(Symbol.Extra).Struct.fields;
654 inline for (fields) |field| {
655 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
656 u32 => @field(extra, field.name),
657 else => @compileError("bad field type"),
658 });
659 }
660 return index;
661}
662
663pub fn getSymbolExtra(self: Dylib, index: u32) Symbol.Extra {
664 const fields = @typeInfo(Symbol.Extra).Struct.fields;
665 var i: usize = index;
666 var result: Symbol.Extra = undefined;
667 inline for (fields) |field| {
668 @field(result, field.name) = switch (field.type) {
669 u32 => self.symbols_extra.items[i],
670 else => @compileError("bad field type"),
671 };
672 i += 1;
673 }
674 return result;
675}
676
677pub fn setSymbolExtra(self: *Dylib, index: u32, extra: Symbol.Extra) void {
678 const fields = @typeInfo(Symbol.Extra).Struct.fields;
679 inline for (fields, 0..) |field, i| {
680 self.symbols_extra.items[index + i] = switch (field.type) {
681 u32 => @field(extra, field.name),
682 else => @compileError("bad field type"),
683 };
684 }
685}
686
617687pub fn format(
618688 self: *Dylib,
619689 comptime unused_fmt_string: []const u8,
......@@ -648,10 +718,16 @@ fn formatSymtab(
648718 _ = unused_fmt_string;
649719 _ = options;
650720 const dylib = ctx.dylib;
721 const macho_file = ctx.macho_file;
651722 try writer.writeAll(" globals\n");
652 for (dylib.symbols.items) |index| {
653 const global = ctx.macho_file.getSymbol(index);
654 try writer.print(" {}\n", .{global.fmt(ctx.macho_file)});
723 for (dylib.symbols.items, 0..) |sym, i| {
724 const ref = dylib.getSymbolRef(@intCast(i), macho_file);
725 if (ref.getFile(macho_file) == null) {
726 // TODO any better way of handling this?
727 try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
728 } else {
729 try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
730 }
655731 }
656732}
657733
src/link/MachO/InternalObject.zig+652-143
......@@ -1,13 +1,28 @@
11index: File.Index,
22
33sections: std.MultiArrayList(Section) = .{},
4atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
5symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
4atoms: std.ArrayListUnmanaged(Atom) = .{},
5atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
6atoms_extra: std.ArrayListUnmanaged(u32) = .{},
7symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
8strtab: std.ArrayListUnmanaged(u8) = .{},
9symbols: std.ArrayListUnmanaged(Symbol) = .{},
10symbols_extra: std.ArrayListUnmanaged(u32) = .{},
11globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
612
713objc_methnames: std.ArrayListUnmanaged(u8) = .{},
814objc_selrefs: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64),
915
10num_rebase_relocs: u32 = 0,
16force_undefined: std.ArrayListUnmanaged(Symbol.Index) = .{},
17entry_index: ?Symbol.Index = null,
18dyld_stub_binder_index: ?Symbol.Index = null,
19dyld_private_index: ?Symbol.Index = null,
20objc_msg_send_index: ?Symbol.Index = null,
21mh_execute_header_index: ?Symbol.Index = null,
22mh_dylib_header_index: ?Symbol.Index = null,
23dso_handle_index: ?Symbol.Index = null,
24boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
25
1126output_symtab_ctx: MachO.SymtabCtx = .{},
1227
1328pub fn deinit(self: *InternalObject, allocator: Allocator) void {
......@@ -16,39 +31,224 @@ pub fn deinit(self: *InternalObject, allocator: Allocator) void {
1631 }
1732 self.sections.deinit(allocator);
1833 self.atoms.deinit(allocator);
34 self.atoms_indexes.deinit(allocator);
35 self.atoms_extra.deinit(allocator);
36 self.symtab.deinit(allocator);
37 self.strtab.deinit(allocator);
1938 self.symbols.deinit(allocator);
39 self.symbols_extra.deinit(allocator);
40 self.globals.deinit(allocator);
2041 self.objc_methnames.deinit(allocator);
42 self.force_undefined.deinit(allocator);
43 self.boundary_symbols.deinit(allocator);
44}
45
46pub fn init(self: *InternalObject, allocator: Allocator) !void {
47 // Atom at index 0 is reserved as null atom.
48 try self.atoms.append(allocator, .{});
49 try self.atoms_extra.append(allocator, 0);
50 // Null byte in strtab
51 try self.strtab.append(allocator, 0);
2152}
2253
23pub fn addSymbol(self: *InternalObject, name: [:0]const u8, macho_file: *MachO) !Symbol.Index {
54pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void {
55 const newSymbolAssumeCapacity = struct {
56 fn newSymbolAssumeCapacity(obj: *InternalObject, name: u32, args: struct {
57 type: u8 = macho.N_UNDF | macho.N_EXT,
58 desc: u16 = 0,
59 }) Symbol.Index {
60 const index = obj.addSymbolAssumeCapacity();
61 const symbol = &obj.symbols.items[index];
62 symbol.name = name;
63 symbol.extra = obj.addSymbolExtraAssumeCapacity(.{});
64 symbol.flags.dyn_ref = args.desc & macho.REFERENCED_DYNAMICALLY != 0;
65 symbol.visibility = if (args.type & macho.N_EXT != 0) blk: {
66 break :blk if (args.type & macho.N_PEXT != 0) .hidden else .global;
67 } else .local;
68
69 const nlist_idx: u32 = @intCast(obj.symtab.items.len);
70 const nlist = obj.symtab.addOneAssumeCapacity();
71 nlist.* = .{
72 .n_strx = name,
73 .n_type = args.type,
74 .n_sect = 0,
75 .n_desc = args.desc,
76 .n_value = 0,
77 };
78 symbol.nlist_idx = nlist_idx;
79 return index;
80 }
81 }.newSymbolAssumeCapacity;
82
2483 const gpa = macho_file.base.comp.gpa;
25 try self.symbols.ensureUnusedCapacity(gpa, 1);
26 const off = try macho_file.strings.insert(gpa, name);
27 const gop = try macho_file.getOrCreateGlobal(off);
28 self.symbols.addOneAssumeCapacity().* = gop.index;
29 const sym = macho_file.getSymbol(gop.index);
30 sym.file = self.index;
31 sym.value = 0;
32 sym.atom = 0;
33 sym.nlist_idx = 0;
34 sym.flags = .{ .global = true };
35 return gop.index;
84 var nsyms = macho_file.base.comp.force_undefined_symbols.keys().len;
85 nsyms += 1; // dyld_stub_binder
86 nsyms += 1; // _objc_msgSend
87 if (!macho_file.base.isDynLib()) {
88 nsyms += 1; // entry
89 nsyms += 1; // __mh_execute_header
90 } else {
91 nsyms += 1; // __mh_dylib_header
92 }
93 nsyms += 1; // ___dso_handle
94 nsyms += 1; // dyld_private
95
96 try self.symbols.ensureTotalCapacityPrecise(gpa, nsyms);
97 try self.symbols_extra.ensureTotalCapacityPrecise(gpa, nsyms * @sizeOf(Symbol.Extra));
98 try self.symtab.ensureTotalCapacityPrecise(gpa, nsyms);
99 try self.globals.ensureTotalCapacityPrecise(gpa, nsyms);
100 self.globals.resize(gpa, nsyms) catch unreachable;
101 @memset(self.globals.items, 0);
102
103 try self.force_undefined.ensureTotalCapacityPrecise(gpa, macho_file.base.comp.force_undefined_symbols.keys().len);
104 for (macho_file.base.comp.force_undefined_symbols.keys()) |name| {
105 self.force_undefined.addOneAssumeCapacity().* = newSymbolAssumeCapacity(self, try self.addString(gpa, name), .{});
106 }
107
108 self.dyld_stub_binder_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "dyld_stub_binder"), .{});
109 self.objc_msg_send_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "_objc_msgSend"), .{});
110
111 if (!macho_file.base.isDynLib()) {
112 self.entry_index = newSymbolAssumeCapacity(self, try self.addString(gpa, macho_file.entry_name orelse "_main"), .{});
113 self.mh_execute_header_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__mh_execute_header"), .{
114 .type = macho.N_SECT | macho.N_EXT,
115 .desc = macho.REFERENCED_DYNAMICALLY,
116 });
117 } else {
118 self.mh_dylib_header_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__mh_dylib_header"), .{
119 .type = macho.N_SECT | macho.N_EXT,
120 });
121 }
122
123 self.dso_handle_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "___dso_handle"), .{
124 .type = macho.N_SECT | macho.N_EXT,
125 });
126 self.dyld_private_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "dyld_private"), .{
127 .type = macho.N_SECT,
128 });
36129}
37130
38/// Creates a fake input sections __TEXT,__objc_methname and __DATA,__objc_selrefs.
39pub fn addObjcMsgsendSections(self: *InternalObject, sym_name: []const u8, macho_file: *MachO) !Atom.Index {
40 const methname_atom_index = try self.addObjcMethnameSection(sym_name, macho_file);
41 return try self.addObjcSelrefsSection(methname_atom_index, macho_file);
131pub fn resolveSymbols(self: *InternalObject, macho_file: *MachO) !void {
132 const tracy = trace(@src());
133 defer tracy.end();
134
135 const gpa = macho_file.base.comp.gpa;
136
137 for (self.symtab.items, self.globals.items, 0..) |nlist, *global, i| {
138 const gop = try macho_file.resolver.getOrPut(gpa, .{
139 .index = @intCast(i),
140 .file = self.index,
141 }, macho_file);
142 if (!gop.found_existing) {
143 gop.ref.* = .{ .index = 0, .file = 0 };
144 }
145 global.* = gop.index;
146
147 if (nlist.undf()) continue;
148 if (gop.ref.getFile(macho_file) == null) {
149 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
150 continue;
151 }
152
153 if (self.asFile().getSymbolRank(.{
154 .archive = false,
155 .weak = false,
156 .tentative = false,
157 }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
158 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
159 }
160 }
42161}
43162
44fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_file: *MachO) !Atom.Index {
163pub fn resolveBoundarySymbols(self: *InternalObject, macho_file: *MachO) !void {
164 const tracy = trace(@src());
165 defer tracy.end();
166
45167 const gpa = macho_file.base.comp.gpa;
46 const atom_index = try macho_file.addAtom();
47 try self.atoms.append(gpa, atom_index);
168 var boundary_symbols = std.StringArrayHashMap(MachO.Ref).init(gpa);
169 defer boundary_symbols.deinit();
170
171 for (macho_file.objects.items) |index| {
172 const object = macho_file.getFile(index).?.object;
173 for (object.symbols.items, 0..) |sym, i| {
174 const nlist = object.symtab.items(.nlist)[i];
175 if (!nlist.undf() or !nlist.ext()) continue;
176 const ref = object.getSymbolRef(@intCast(i), macho_file);
177 if (ref.getFile(macho_file) != null) continue;
178 const name = sym.getName(macho_file);
179 if (mem.startsWith(u8, name, "segment$start$") or
180 mem.startsWith(u8, name, "segment$stop$") or
181 mem.startsWith(u8, name, "section$start$") or
182 mem.startsWith(u8, name, "section$stop$"))
183 {
184 const gop = try boundary_symbols.getOrPut(name);
185 if (!gop.found_existing) {
186 gop.value_ptr.* = .{ .index = @intCast(i), .file = index };
187 }
188 }
189 }
190 }
191
192 const nsyms = boundary_symbols.values().len;
193 try self.boundary_symbols.ensureTotalCapacityPrecise(gpa, nsyms);
194 try self.symbols.ensureUnusedCapacity(gpa, nsyms);
195 try self.symtab.ensureUnusedCapacity(gpa, nsyms);
196 try self.symbols_extra.ensureUnusedCapacity(gpa, nsyms * @sizeOf(Symbol.Extra));
197 try self.globals.ensureUnusedCapacity(gpa, nsyms);
198
199 for (boundary_symbols.keys(), boundary_symbols.values()) |name, ref| {
200 const name_off = try self.addString(gpa, name);
201 const sym_index = self.addSymbolAssumeCapacity();
202 self.boundary_symbols.appendAssumeCapacity(sym_index);
203 const sym = &self.symbols.items[sym_index];
204 sym.name = name_off;
205 sym.visibility = .local;
206 const nlist_idx: u32 = @intCast(self.symtab.items.len);
207 const nlist = self.symtab.addOneAssumeCapacity();
208 nlist.* = .{
209 .n_strx = name_off,
210 .n_type = macho.N_SECT,
211 .n_sect = 0,
212 .n_desc = 0,
213 .n_value = 0,
214 };
215 sym.nlist_idx = nlist_idx;
216 sym.extra = self.addSymbolExtraAssumeCapacity(.{});
217
218 const idx = ref.getFile(macho_file).?.object.globals.items[ref.index];
219 self.globals.addOneAssumeCapacity().* = idx;
220 macho_file.resolver.values.items[idx - 1] = .{ .index = sym_index, .file = self.index };
221 }
222}
223
224pub fn markLive(self: *InternalObject, macho_file: *MachO) void {
225 const tracy = trace(@src());
226 defer tracy.end();
227
228 for (0..self.symbols.items.len) |i| {
229 const nlist = self.symtab.items[i];
230 if (!nlist.ext()) continue;
231
232 const ref = self.getSymbolRef(@intCast(i), macho_file);
233 const file = ref.getFile(macho_file) orelse continue;
234 if (file == .object and !file.object.alive) {
235 file.object.alive = true;
236 file.object.markLive(macho_file);
237 }
238 }
239}
48240
49 const atom = macho_file.getAtom(atom_index).?;
50 atom.atom_index = atom_index;
51 atom.file = self.index;
241/// Creates a fake input sections __TEXT,__objc_methname and __DATA,__objc_selrefs.
242pub fn addObjcMsgsendSections(self: *InternalObject, sym_name: []const u8, macho_file: *MachO) !Symbol.Index {
243 const methname_sym_index = try self.addObjcMethnameSection(sym_name, macho_file);
244 return try self.addObjcSelrefsSection(methname_sym_index, macho_file);
245}
246
247fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_file: *MachO) !Symbol.Index {
248 const gpa = macho_file.base.comp.gpa;
249 const atom_index = try self.addAtom(gpa);
250 try self.atoms_indexes.append(gpa, atom_index);
251 const atom = self.getAtom(atom_index).?;
52252 atom.size = methname.len + 1;
53253 atom.alignment = .@"1";
54254
......@@ -64,17 +264,32 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil
64264 try self.objc_methnames.ensureUnusedCapacity(gpa, methname.len + 1);
65265 self.objc_methnames.writer(gpa).print("{s}\x00", .{methname}) catch unreachable;
66266
267 const name_str = try self.addString(gpa, "ltmp");
268 const sym_index = try self.addSymbol(gpa);
269 const sym = &self.symbols.items[sym_index];
270 sym.name = name_str;
271 sym.atom_ref = .{ .index = atom_index, .file = self.index };
272 sym.extra = try self.addSymbolExtra(gpa, .{});
273 const nlist_idx: u32 = @intCast(self.symtab.items.len);
274 const nlist = try self.symtab.addOne(gpa);
275 nlist.* = .{
276 .n_strx = name_str,
277 .n_type = macho.N_SECT,
278 .n_sect = @intCast(n_sect + 1),
279 .n_desc = 0,
280 .n_value = 0,
281 };
282 sym.nlist_idx = nlist_idx;
283 try self.globals.append(gpa, 0);
284
67285 return atom_index;
68286}
69287
70fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index, macho_file: *MachO) !Atom.Index {
288fn addObjcSelrefsSection(self: *InternalObject, methname_sym_index: Symbol.Index, macho_file: *MachO) !Symbol.Index {
71289 const gpa = macho_file.base.comp.gpa;
72 const atom_index = try macho_file.addAtom();
73 try self.atoms.append(gpa, atom_index);
74
75 const atom = macho_file.getAtom(atom_index).?;
76 atom.atom_index = atom_index;
77 atom.file = self.index;
290 const atom_index = try self.addAtom(gpa);
291 try self.atoms_indexes.append(gpa, atom_index);
292 const atom = self.getAtom(atom_index).?;
78293 atom.size = @sizeOf(u64);
79294 atom.alignment = .@"8";
80295
......@@ -90,9 +305,9 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index,
90305 const relocs = &self.sections.items(.relocs)[n_sect];
91306 try relocs.ensureUnusedCapacity(gpa, 1);
92307 relocs.appendAssumeCapacity(.{
93 .tag = .local,
308 .tag = .@"extern",
94309 .offset = 0,
95 .target = methname_atom_index,
310 .target = methname_sym_index,
96311 .addend = 0,
97312 .type = .unsigned,
98313 .meta = .{
......@@ -102,140 +317,285 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index,
102317 .has_subtractor = false,
103318 },
104319 });
105 try atom.addExtra(.{ .rel_index = 0, .rel_count = 1 }, macho_file);
106 atom.flags.relocs = true;
107 self.num_rebase_relocs += 1;
320 atom.addExtra(.{ .rel_index = 0, .rel_count = 1 }, macho_file);
321
322 const sym_index = try self.addSymbol(gpa);
323 const sym = &self.symbols.items[sym_index];
324 sym.atom_ref = .{ .index = atom_index, .file = self.index };
325 sym.extra = try self.addSymbolExtra(gpa, .{});
326 const nlist_idx: u32 = @intCast(self.symtab.items.len);
327 const nlist = try self.symtab.addOne(gpa);
328 nlist.* = .{
329 .n_strx = 0,
330 .n_type = macho.N_SECT,
331 .n_sect = @intCast(n_sect + 1),
332 .n_desc = 0,
333 .n_value = 0,
334 };
335 sym.nlist_idx = nlist_idx;
336 try self.globals.append(gpa, 0);
337 atom.addExtra(.{ .literal_symbol_index = sym_index }, macho_file);
108338
109 return atom_index;
339 return sym_index;
110340}
111341
112pub fn resolveLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
342pub fn resolveObjcMsgSendSymbols(self: *InternalObject, macho_file: *MachO) !void {
343 const tracy = trace(@src());
344 defer tracy.end();
345
346 const gpa = macho_file.base.comp.gpa;
347
348 var objc_msgsend_syms = std.StringArrayHashMap(MachO.Ref).init(gpa);
349 defer objc_msgsend_syms.deinit();
350
351 for (macho_file.objects.items) |index| {
352 const object = macho_file.getFile(index).?.object;
353
354 for (object.symbols.items, 0..) |sym, i| {
355 const nlist = object.symtab.items(.nlist)[i];
356 if (!nlist.ext()) continue;
357 if (!nlist.undf()) continue;
358
359 const ref = object.getSymbolRef(@intCast(i), macho_file);
360 if (ref.getFile(macho_file) != null) continue;
361
362 const name = sym.getName(macho_file);
363 if (mem.startsWith(u8, name, "_objc_msgSend$")) {
364 const gop = try objc_msgsend_syms.getOrPut(name);
365 if (!gop.found_existing) {
366 gop.value_ptr.* = .{ .index = @intCast(i), .file = index };
367 }
368 }
369 }
370 }
371
372 for (objc_msgsend_syms.keys(), objc_msgsend_syms.values()) |sym_name, ref| {
373 const name = MachO.eatPrefix(sym_name, "_objc_msgSend$").?;
374 const selrefs_index = try self.addObjcMsgsendSections(name, macho_file);
375
376 const name_off = try self.addString(gpa, sym_name);
377 const sym_index = try self.addSymbol(gpa);
378 const sym = &self.symbols.items[sym_index];
379 sym.name = name_off;
380 sym.visibility = .hidden;
381 const nlist_idx: u32 = @intCast(self.symtab.items.len);
382 const nlist = try self.symtab.addOne(gpa);
383 nlist.* = .{
384 .n_strx = name_off,
385 .n_type = macho.N_SECT | macho.N_EXT | macho.N_PEXT,
386 .n_sect = 0,
387 .n_desc = 0,
388 .n_value = 0,
389 };
390 sym.nlist_idx = nlist_idx;
391 sym.extra = try self.addSymbolExtra(gpa, .{ .objc_selrefs = selrefs_index });
392 sym.flags.objc_stubs = true;
393
394 const idx = ref.getFile(macho_file).?.object.globals.items[ref.index];
395 try self.globals.append(gpa, idx);
396 macho_file.resolver.values.items[idx - 1] = .{ .index = sym_index, .file = self.index };
397 }
398}
399
400pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
401 const tracy = trace(@src());
402 defer tracy.end();
403
113404 const gpa = macho_file.base.comp.gpa;
114405
115406 var buffer = std.ArrayList(u8).init(gpa);
116407 defer buffer.deinit();
117408
118409 const slice = self.sections.slice();
119 for (slice.items(.header), self.atoms.items, 0..) |header, atom_index, n_sect| {
120 if (Object.isCstringLiteral(header) or Object.isFixedSizeLiteral(header)) {
121 const data = try self.getSectionData(@intCast(n_sect));
122 const atom = macho_file.getAtom(atom_index).?;
123 const res = try lp.insert(gpa, header.type(), data);
124 if (!res.found_existing) {
125 res.atom.* = atom_index;
126 }
127 atom.flags.literal_pool = true;
128 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
129 } else if (Object.isPtrLiteral(header)) {
130 const atom = macho_file.getAtom(atom_index).?;
131 const relocs = atom.getRelocs(macho_file);
132 assert(relocs.len == 1);
133 const rel = relocs[0];
134 assert(rel.tag == .local);
135 const target = macho_file.getAtom(rel.target).?;
136 const addend = std.math.cast(u32, rel.addend) orelse return error.Overflow;
137 const target_size = std.math.cast(usize, target.size) orelse return error.Overflow;
138 try buffer.ensureUnusedCapacity(target_size);
139 buffer.resize(target_size) catch unreachable;
140 try target.getData(macho_file, buffer.items);
141 const res = try lp.insert(gpa, header.type(), buffer.items[addend..]);
142 buffer.clearRetainingCapacity();
143 if (!res.found_existing) {
144 res.atom.* = atom_index;
145 }
146 atom.flags.literal_pool = true;
147 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
410 for (slice.items(.header), self.getAtoms()) |header, atom_index| {
411 if (!Object.isPtrLiteral(header)) continue;
412 const atom = self.getAtom(atom_index).?;
413 const relocs = atom.getRelocs(macho_file);
414 assert(relocs.len == 1);
415 const rel = relocs[0];
416 assert(rel.tag == .@"extern");
417 const target = rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?;
418 const target_size = std.math.cast(usize, target.size) orelse return error.Overflow;
419 try buffer.ensureUnusedCapacity(target_size);
420 buffer.resize(target_size) catch unreachable;
421 @memcpy(buffer.items, try self.getSectionData(target.n_sect));
422 const res = try lp.insert(gpa, header.type(), buffer.items);
423 buffer.clearRetainingCapacity();
424 if (!res.found_existing) {
425 res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
426 } else {
427 const lp_sym = lp.getSymbol(res.index, macho_file);
428 const lp_atom = lp_sym.getAtom(macho_file).?;
429 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
430 atom.flags.alive = false;
148431 }
432 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
149433 }
150434}
151435
152pub fn dedupLiterals(self: InternalObject, lp: MachO.LiteralPool, macho_file: *MachO) void {
153 for (self.atoms.items) |atom_index| {
154 const atom = macho_file.getAtom(atom_index) orelse continue;
436pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: *MachO) void {
437 const tracy = trace(@src());
438 defer tracy.end();
439
440 for (self.getAtoms()) |atom_index| {
441 const atom = self.getAtom(atom_index) orelse continue;
155442 if (!atom.flags.alive) continue;
156 if (!atom.flags.relocs) continue;
157443
158444 const relocs = blk: {
159 const extra = atom.getExtra(macho_file).?;
445 const extra = atom.getExtra(macho_file);
160446 const relocs = self.sections.items(.relocs)[atom.n_sect].items;
161447 break :blk relocs[extra.rel_index..][0..extra.rel_count];
162448 };
163 for (relocs) |*rel| switch (rel.tag) {
164 .local => {
165 const target = macho_file.getAtom(rel.target).?;
166 if (target.getLiteralPoolIndex(macho_file)) |lp_index| {
167 const lp_atom = lp.getAtom(lp_index, macho_file);
168 if (target.atom_index != lp_atom.atom_index) {
169 lp_atom.alignment = lp_atom.alignment.max(target.alignment);
170 target.flags.alive = false;
171 rel.target = lp_atom.atom_index;
172 }
173 }
174 },
175 .@"extern" => {
176 const target_sym = rel.getTargetSymbol(macho_file);
177 if (target_sym.getAtom(macho_file)) |target_atom| {
178 if (target_atom.getLiteralPoolIndex(macho_file)) |lp_index| {
179 const lp_atom = lp.getAtom(lp_index, macho_file);
180 if (target_atom.atom_index != lp_atom.atom_index) {
181 lp_atom.alignment = lp_atom.alignment.max(target_atom.alignment);
182 target_atom.flags.alive = false;
183 target_sym.atom = lp_atom.atom_index;
184 }
185 }
186 }
187 },
188 };
449 for (relocs) |*rel| {
450 if (rel.tag != .@"extern") continue;
451 const target_sym_ref = rel.getTargetSymbolRef(atom.*, macho_file);
452 const file = target_sym_ref.getFile(macho_file) orelse continue;
453 if (file.getIndex() != self.index) continue;
454 const target_sym = target_sym_ref.getSymbol(macho_file).?;
455 const target_atom = target_sym.getAtom(macho_file) orelse continue;
456 if (!Object.isPtrLiteral(target_atom.getInputSection(macho_file))) continue;
457 const lp_index = target_atom.getExtra(macho_file).literal_pool_index;
458 const lp_sym = lp.getSymbol(lp_index, macho_file);
459 const lp_atom_ref = lp_sym.atom_ref;
460 if (target_atom.atom_index != lp_atom_ref.index or target_atom.file != lp_atom_ref.file) {
461 target_sym.atom_ref = lp_atom_ref;
462 }
463 }
189464 }
190465
191 for (self.symbols.items) |sym_index| {
192 const sym = macho_file.getSymbol(sym_index);
466 for (self.symbols.items) |*sym| {
193467 if (!sym.flags.objc_stubs) continue;
194 var extra = sym.getExtra(macho_file).?;
195 const atom = macho_file.getAtom(extra.objc_selrefs).?;
196 if (atom.getLiteralPoolIndex(macho_file)) |lp_index| {
197 const lp_atom = lp.getAtom(lp_index, macho_file);
198 if (atom.atom_index != lp_atom.atom_index) {
199 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
200 atom.flags.alive = false;
201 extra.objc_selrefs = lp_atom.atom_index;
202 sym.setExtra(extra, macho_file);
468 const extra = sym.getExtra(macho_file);
469 const file = sym.getFile(macho_file).?;
470 if (file.getIndex() != self.index) continue;
471 const tsym = switch (file) {
472 .dylib => unreachable,
473 inline else => |x| &x.symbols.items[extra.objc_selrefs],
474 };
475 const atom = tsym.getAtom(macho_file) orelse continue;
476 if (!Object.isPtrLiteral(atom.getInputSection(macho_file))) continue;
477 const lp_index = atom.getExtra(macho_file).literal_pool_index;
478 const lp_sym = lp.getSymbol(lp_index, macho_file);
479 const lp_atom_ref = lp_sym.atom_ref;
480 if (atom.atom_index != lp_atom_ref.index or atom.file != lp_atom_ref.file) {
481 tsym.atom_ref = lp_atom_ref;
482 }
483 }
484}
485
486pub fn scanRelocs(self: *InternalObject, macho_file: *MachO) void {
487 const tracy = trace(@src());
488 defer tracy.end();
489
490 if (self.getEntryRef(macho_file)) |ref| {
491 if (ref.getFile(macho_file) != null) {
492 const sym = ref.getSymbol(macho_file).?;
493 if (sym.flags.import) sym.flags.stubs = true;
494 }
495 }
496 if (self.getDyldStubBinderRef(macho_file)) |ref| {
497 if (ref.getFile(macho_file) != null) {
498 const sym = ref.getSymbol(macho_file).?;
499 sym.flags.needs_got = true;
500 }
501 }
502 if (self.getObjcMsgSendRef(macho_file)) |ref| {
503 if (ref.getFile(macho_file) != null) {
504 const sym = ref.getSymbol(macho_file).?;
505 // TODO is it always needed, or only if we are synthesising fast stubs
506 sym.flags.needs_got = true;
507 }
508 }
509}
510
511pub fn allocateSyntheticSymbols(self: *InternalObject, macho_file: *MachO) void {
512 const text_seg = macho_file.getTextSegment();
513
514 if (self.mh_execute_header_index) |index| {
515 const ref = self.getSymbolRef(index, macho_file);
516 if (ref.getFile(macho_file)) |file| {
517 if (file.getIndex() == self.index) {
518 const sym = &self.symbols.items[index];
519 sym.value = text_seg.vmaddr;
520 }
521 }
522 }
523
524 if (macho_file.data_sect_index) |idx| {
525 const sect = macho_file.sections.items(.header)[idx];
526 for (&[_]?Symbol.Index{
527 self.dso_handle_index,
528 self.mh_dylib_header_index,
529 self.dyld_private_index,
530 }) |maybe_index| {
531 if (maybe_index) |index| {
532 const ref = self.getSymbolRef(index, macho_file);
533 if (ref.getFile(macho_file)) |file| {
534 if (file.getIndex() == self.index) {
535 const sym = &self.symbols.items[index];
536 sym.value = sect.addr;
537 sym.out_n_sect = idx;
538 }
539 }
203540 }
204541 }
205542 }
206543}
207544
208pub fn calcSymtabSize(self: *InternalObject, macho_file: *MachO) !void {
209 for (self.symbols.items) |sym_index| {
210 const sym = macho_file.getSymbol(sym_index);
211 if (sym.getFile(macho_file)) |file| if (file.getIndex() != self.index) continue;
545pub fn calcSymtabSize(self: *InternalObject, macho_file: *MachO) void {
546 for (self.symbols.items, 0..) |*sym, i| {
547 const ref = self.getSymbolRef(@intCast(i), macho_file);
548 const file = ref.getFile(macho_file) orelse continue;
549 if (file.getIndex() != self.index) continue;
550 if (sym.getName(macho_file).len == 0) continue;
212551 sym.flags.output_symtab = true;
213552 if (sym.isLocal()) {
214 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
553 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
215554 self.output_symtab_ctx.nlocals += 1;
216555 } else if (sym.flags.@"export") {
217 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
556 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
218557 self.output_symtab_ctx.nexports += 1;
219558 } else {
220559 assert(sym.flags.import);
221 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
560 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
222561 self.output_symtab_ctx.nimports += 1;
223562 }
224563 self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
225564 }
226565}
227566
567pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void {
568 const tracy = trace(@src());
569 defer tracy.end();
570
571 for (self.getAtoms()) |atom_index| {
572 const atom = self.getAtom(atom_index) orelse continue;
573 if (!atom.flags.alive) continue;
574 const sect = atom.getInputSection(macho_file);
575 if (sect.isZerofill()) continue;
576 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
577 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
578 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items[off..][0..size];
579 @memcpy(buffer, try self.getSectionData(atom.n_sect));
580 try atom.resolveRelocs(macho_file, buffer);
581 }
582}
583
228584pub fn writeSymtab(self: InternalObject, macho_file: *MachO, ctx: anytype) void {
229 for (self.symbols.items) |sym_index| {
230 const sym = macho_file.getSymbol(sym_index);
231 if (sym.getFile(macho_file)) |file| if (file.getIndex() != self.index) continue;
585 var n_strx = self.output_symtab_ctx.stroff;
586 for (self.symbols.items, 0..) |sym, i| {
587 const ref = self.getSymbolRef(@intCast(i), macho_file);
588 const file = ref.getFile(macho_file) orelse continue;
589 if (file.getIndex() != self.index) continue;
232590 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
233 const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
234 ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file));
235 ctx.strtab.appendAssumeCapacity(0);
236591 const out_sym = &ctx.symtab.items[idx];
237592 out_sym.n_strx = n_strx;
238593 sym.setOutputSym(macho_file, out_sym);
594 const name = sym.getName(macho_file);
595 @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
596 n_strx += @intCast(name.len);
597 ctx.strtab.items[n_strx] = 0;
598 n_strx += 1;
239599 }
240600}
241601
......@@ -264,30 +624,171 @@ fn getSectionData(self: *const InternalObject, index: u32) error{Overflow}![]con
264624 @panic("ref to non-existent section");
265625}
266626
267pub fn getAtomData(self: *const InternalObject, atom: Atom, buffer: []u8) error{Overflow}!void {
268 assert(buffer.len == atom.size);
269 const data = try self.getSectionData(atom.n_sect);
270 const off = std.math.cast(usize, atom.off) orelse return error.Overflow;
271 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
272 @memcpy(buffer, data[off..][0..size]);
627pub fn addString(self: *InternalObject, allocator: Allocator, name: []const u8) !u32 {
628 const off: u32 = @intCast(self.strtab.items.len);
629 try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);
630 self.strtab.appendSliceAssumeCapacity(name);
631 self.strtab.appendAssumeCapacity(0);
632 return off;
633}
634
635pub fn getString(self: InternalObject, off: u32) [:0]const u8 {
636 assert(off < self.strtab.items.len);
637 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
638}
639
640pub fn asFile(self: *InternalObject) File {
641 return .{ .internal = self };
273642}
274643
275644pub fn getAtomRelocs(self: *const InternalObject, atom: Atom, macho_file: *MachO) []const Relocation {
276 if (!atom.flags.relocs) return &[0]Relocation{};
277 const extra = atom.getExtra(macho_file).?;
645 const extra = atom.getExtra(macho_file);
278646 const relocs = self.sections.items(.relocs)[atom.n_sect];
279647 return relocs.items[extra.rel_index..][0..extra.rel_count];
280648}
281649
282pub fn getString(self: InternalObject, off: u32) [:0]const u8 {
283 _ = self;
284 _ = off;
285 // We don't have any local strings for synthetic atoms.
286 return "";
650fn addAtom(self: *InternalObject, allocator: Allocator) !Atom.Index {
651 const atom_index: Atom.Index = @intCast(self.atoms.items.len);
652 const atom = try self.atoms.addOne(allocator);
653 atom.* = .{
654 .file = self.index,
655 .atom_index = atom_index,
656 .extra = try self.addAtomExtra(allocator, .{}),
657 };
658 return atom_index;
287659}
288660
289pub fn asFile(self: *InternalObject) File {
290 return .{ .internal = self };
661pub fn getAtom(self: *InternalObject, atom_index: Atom.Index) ?*Atom {
662 if (atom_index == 0) return null;
663 assert(atom_index < self.atoms.items.len);
664 return &self.atoms.items[atom_index];
665}
666
667pub fn getAtoms(self: InternalObject) []const Atom.Index {
668 return self.atoms_indexes.items;
669}
670
671fn addAtomExtra(self: *InternalObject, allocator: Allocator, extra: Atom.Extra) !u32 {
672 const fields = @typeInfo(Atom.Extra).Struct.fields;
673 try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
674 return self.addAtomExtraAssumeCapacity(extra);
675}
676
677fn addAtomExtraAssumeCapacity(self: *InternalObject, extra: Atom.Extra) u32 {
678 const index = @as(u32, @intCast(self.atoms_extra.items.len));
679 const fields = @typeInfo(Atom.Extra).Struct.fields;
680 inline for (fields) |field| {
681 self.atoms_extra.appendAssumeCapacity(switch (field.type) {
682 u32 => @field(extra, field.name),
683 else => @compileError("bad field type"),
684 });
685 }
686 return index;
687}
688
689pub fn getAtomExtra(self: InternalObject, index: u32) Atom.Extra {
690 const fields = @typeInfo(Atom.Extra).Struct.fields;
691 var i: usize = index;
692 var result: Atom.Extra = undefined;
693 inline for (fields) |field| {
694 @field(result, field.name) = switch (field.type) {
695 u32 => self.atoms_extra.items[i],
696 else => @compileError("bad field type"),
697 };
698 i += 1;
699 }
700 return result;
701}
702
703pub fn setAtomExtra(self: *InternalObject, index: u32, extra: Atom.Extra) void {
704 assert(index > 0);
705 const fields = @typeInfo(Atom.Extra).Struct.fields;
706 inline for (fields, 0..) |field, i| {
707 self.atoms_extra.items[index + i] = switch (field.type) {
708 u32 => @field(extra, field.name),
709 else => @compileError("bad field type"),
710 };
711 }
712}
713
714pub fn getEntryRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref {
715 const index = self.entry_index orelse return null;
716 return self.getSymbolRef(index, macho_file);
717}
718
719pub fn getDyldStubBinderRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref {
720 const index = self.dyld_stub_binder_index orelse return null;
721 return self.getSymbolRef(index, macho_file);
722}
723
724pub fn getDyldPrivateRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref {
725 const index = self.dyld_private_index orelse return null;
726 return self.getSymbolRef(index, macho_file);
727}
728
729pub fn getObjcMsgSendRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref {
730 const index = self.objc_msg_send_index orelse return null;
731 return self.getSymbolRef(index, macho_file);
732}
733
734pub fn addSymbol(self: *InternalObject, allocator: Allocator) !Symbol.Index {
735 try self.symbols.ensureUnusedCapacity(allocator, 1);
736 return self.addSymbolAssumeCapacity();
737}
738
739pub fn addSymbolAssumeCapacity(self: *InternalObject) Symbol.Index {
740 const index: Symbol.Index = @intCast(self.symbols.items.len);
741 const symbol = self.symbols.addOneAssumeCapacity();
742 symbol.* = .{ .file = self.index };
743 return index;
744}
745
746pub fn getSymbolRef(self: InternalObject, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
747 const global_index = self.globals.items[index];
748 if (macho_file.resolver.get(global_index)) |ref| return ref;
749 return .{ .index = index, .file = self.index };
750}
751
752pub fn addSymbolExtra(self: *InternalObject, allocator: Allocator, extra: Symbol.Extra) !u32 {
753 const fields = @typeInfo(Symbol.Extra).Struct.fields;
754 try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
755 return self.addSymbolExtraAssumeCapacity(extra);
756}
757
758fn addSymbolExtraAssumeCapacity(self: *InternalObject, extra: Symbol.Extra) u32 {
759 const index = @as(u32, @intCast(self.symbols_extra.items.len));
760 const fields = @typeInfo(Symbol.Extra).Struct.fields;
761 inline for (fields) |field| {
762 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
763 u32 => @field(extra, field.name),
764 else => @compileError("bad field type"),
765 });
766 }
767 return index;
768}
769
770pub fn getSymbolExtra(self: InternalObject, index: u32) Symbol.Extra {
771 const fields = @typeInfo(Symbol.Extra).Struct.fields;
772 var i: usize = index;
773 var result: Symbol.Extra = undefined;
774 inline for (fields) |field| {
775 @field(result, field.name) = switch (field.type) {
776 u32 => self.symbols_extra.items[i],
777 else => @compileError("bad field type"),
778 };
779 i += 1;
780 }
781 return result;
782}
783
784pub fn setSymbolExtra(self: *InternalObject, index: u32, extra: Symbol.Extra) void {
785 const fields = @typeInfo(Symbol.Extra).Struct.fields;
786 inline for (fields, 0..) |field, i| {
787 self.symbols_extra.items[index + i] = switch (field.type) {
788 u32 => @field(extra, field.name),
789 else => @compileError("bad field type"),
790 };
791 }
291792}
292793
293794const FormatContext = struct {
......@@ -311,8 +812,8 @@ fn formatAtoms(
311812 _ = unused_fmt_string;
312813 _ = options;
313814 try writer.writeAll(" atoms\n");
314 for (ctx.self.atoms.items) |atom_index| {
315 const atom = ctx.macho_file.getAtom(atom_index).?;
815 for (ctx.self.getAtoms()) |atom_index| {
816 const atom = ctx.self.getAtom(atom_index) orelse continue;
316817 try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});
317818 }
318819}
......@@ -332,10 +833,17 @@ fn formatSymtab(
332833) !void {
333834 _ = unused_fmt_string;
334835 _ = options;
836 const macho_file = ctx.macho_file;
837 const self = ctx.self;
335838 try writer.writeAll(" symbols\n");
336 for (ctx.self.symbols.items) |index| {
337 const global = ctx.macho_file.getSymbol(index);
338 try writer.print(" {}\n", .{global.fmt(ctx.macho_file)});
839 for (self.symbols.items, 0..) |sym, i| {
840 const ref = self.getSymbolRef(@intCast(i), macho_file);
841 if (ref.getFile(macho_file) == null) {
842 // TODO any better way of handling this?
843 try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
844 } else {
845 try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
846 }
339847 }
340848}
341849
......@@ -354,6 +862,7 @@ const assert = std.debug.assert;
354862const macho = std.macho;
355863const mem = std.mem;
356864const std = @import("std");
865const trace = @import("../../tracy.zig").trace;
357866
358867const Allocator = std.mem.Allocator;
359868const Atom = @import("Atom.zig");
src/link/MachO/Object.zig+1005-488
......@@ -1,16 +1,22 @@
1archive: ?InArchive = null,
1/// Non-zero for fat object files or archives
2offset: u64,
23path: []const u8,
34file_handle: File.HandleIndex,
45mtime: u64,
56index: File.Index,
7in_archive: ?InArchive = null,
68
79header: ?macho.mach_header_64 = null,
810sections: std.MultiArrayList(Section) = .{},
911symtab: std.MultiArrayList(Nlist) = .{},
1012strtab: std.ArrayListUnmanaged(u8) = .{},
1113
12symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
13atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
14symbols: std.ArrayListUnmanaged(Symbol) = .{},
15symbols_extra: std.ArrayListUnmanaged(u32) = .{},
16globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
17atoms: std.ArrayListUnmanaged(Atom) = .{},
18atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
19atoms_extra: std.ArrayListUnmanaged(u32) = .{},
1420
1521platform: ?MachO.Platform = null,
1622compile_unit: ?CompileUnit = null,
......@@ -21,13 +27,14 @@ compact_unwind_sect_index: ?u8 = null,
2127cies: std.ArrayListUnmanaged(Cie) = .{},
2228fdes: std.ArrayListUnmanaged(Fde) = .{},
2329eh_frame_data: std.ArrayListUnmanaged(u8) = .{},
24unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record.Index) = .{},
30unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{},
31unwind_records_indexes: std.ArrayListUnmanaged(UnwindInfo.Record.Index) = .{},
2532data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
2633
2734alive: bool = true,
2835hidden: bool = false,
2936
30dynamic_relocs: MachO.DynamicRelocs = .{},
37compact_unwind_ctx: CompactUnwindCtx = .{},
3138output_symtab_ctx: MachO.SymtabCtx = .{},
3239output_ar_state: Archive.ArState = .{},
3340
......@@ -39,7 +46,7 @@ pub fn isObject(path: []const u8) !bool {
3946}
4047
4148pub fn deinit(self: *Object, allocator: Allocator) void {
42 if (self.archive) |*ar| allocator.free(ar.path);
49 if (self.in_archive) |*ar| allocator.free(ar.path);
4350 allocator.free(self.path);
4451 for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| {
4552 relocs.deinit(allocator);
......@@ -49,11 +56,16 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
4956 self.symtab.deinit(allocator);
5057 self.strtab.deinit(allocator);
5158 self.symbols.deinit(allocator);
59 self.symbols_extra.deinit(allocator);
60 self.globals.deinit(allocator);
5261 self.atoms.deinit(allocator);
62 self.atoms_indexes.deinit(allocator);
63 self.atoms_extra.deinit(allocator);
5364 self.cies.deinit(allocator);
5465 self.fdes.deinit(allocator);
5566 self.eh_frame_data.deinit(allocator);
5667 self.unwind_records.deinit(allocator);
68 self.unwind_records_indexes.deinit(allocator);
5769 for (self.stab_files.items) |*sf| {
5870 sf.stabs.deinit(allocator);
5971 }
......@@ -65,13 +77,18 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
6577 const tracy = trace(@src());
6678 defer tracy.end();
6779
80 log.debug("parsing {}", .{self.fmtPath()});
81
6882 const gpa = macho_file.base.comp.gpa;
69 const offset = if (self.archive) |ar| ar.offset else 0;
7083 const handle = macho_file.getFileHandle(self.file_handle);
84 const cpu_arch = macho_file.getTarget().cpu.arch;
85
86 // Atom at index 0 is reserved as null atom
87 try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) });
7188
7289 var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
7390 {
74 const amt = try handle.preadAll(&header_buffer, offset);
91 const amt = try handle.preadAll(&header_buffer, self.offset);
7592 if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput;
7693 }
7794 self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*;
......@@ -84,7 +101,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
84101 return error.InvalidCpuArch;
85102 },
86103 };
87 if (macho_file.getTarget().cpu.arch != this_cpu_arch) {
104 if (cpu_arch != this_cpu_arch) {
88105 try macho_file.reportParseError2(self.index, "invalid cpu architecture: {s}", .{@tagName(this_cpu_arch)});
89106 return error.InvalidCpuArch;
90107 }
......@@ -92,7 +109,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
92109 const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds);
93110 defer gpa.free(lc_buffer);
94111 {
95 const amt = try handle.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64));
112 const amt = try handle.preadAll(lc_buffer, self.offset + @sizeOf(macho.mach_header_64));
96113 if (amt != self.header.?.sizeofcmds) return error.InputOutput;
97114 }
98115
......@@ -119,14 +136,14 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
119136 const cmd = lc.cast(macho.symtab_command).?;
120137 try self.strtab.resize(gpa, cmd.strsize);
121138 {
122 const amt = try handle.preadAll(self.strtab.items, cmd.stroff + offset);
139 const amt = try handle.preadAll(self.strtab.items, cmd.stroff + self.offset);
123140 if (amt != self.strtab.items.len) return error.InputOutput;
124141 }
125142
126143 const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64));
127144 defer gpa.free(symtab_buffer);
128145 {
129 const amt = try handle.preadAll(symtab_buffer, cmd.symoff + offset);
146 const amt = try handle.preadAll(symtab_buffer, cmd.symoff + self.offset);
130147 if (amt != symtab_buffer.len) return error.InputOutput;
131148 }
132149 const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms];
......@@ -144,7 +161,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
144161 const buffer = try gpa.alloc(u8, cmd.datasize);
145162 defer gpa.free(buffer);
146163 {
147 const amt = try handle.preadAll(buffer, offset + cmd.dataoff);
164 const amt = try handle.preadAll(buffer, self.offset + cmd.dataoff);
148165 if (amt != buffer.len) return error.InputOutput;
149166 }
150167 const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry));
......@@ -196,39 +213,39 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
196213 mem.sort(NlistIdx, nlists.items, self, NlistIdx.lessThan);
197214
198215 if (self.hasSubsections()) {
199 try self.initSubsections(nlists.items, macho_file);
216 try self.initSubsections(gpa, nlists.items);
200217 } else {
201 try self.initSections(nlists.items, macho_file);
218 try self.initSections(gpa, nlists.items);
202219 }
203220
204 try self.initCstringLiterals(macho_file);
205 try self.initFixedSizeLiterals(macho_file);
206 try self.initPointerLiterals(macho_file);
221 try self.initCstringLiterals(gpa, handle, macho_file);
222 try self.initFixedSizeLiterals(gpa, macho_file);
223 try self.initPointerLiterals(gpa, macho_file);
207224 try self.linkNlistToAtom(macho_file);
208225
209226 try self.sortAtoms(macho_file);
210 try self.initSymbols(macho_file);
211 try self.initSymbolStabs(nlists.items, macho_file);
212 try self.initRelocs(macho_file);
227 try self.initSymbols(gpa, macho_file);
228 try self.initSymbolStabs(gpa, nlists.items, macho_file);
229 try self.initRelocs(handle, cpu_arch, macho_file);
213230
214231 // Parse DWARF __TEXT,__eh_frame section
215232 if (self.eh_frame_sect_index) |index| {
216 try self.initEhFrameRecords(index, macho_file);
233 try self.initEhFrameRecords(gpa, index, handle, macho_file);
217234 }
218235
219236 // Parse Apple's __LD,__compact_unwind section
220237 if (self.compact_unwind_sect_index) |index| {
221 try self.initUnwindRecords(index, macho_file);
238 try self.initUnwindRecords(gpa, index, handle, macho_file);
222239 }
223240
224241 if (self.hasUnwindRecords() or self.hasEhFrameRecords()) {
225 try self.parseUnwindRecords(macho_file);
242 try self.parseUnwindRecords(gpa, cpu_arch, macho_file);
226243 }
227244
228245 if (self.platform) |platform| {
229246 if (!macho_file.platform.eqlTarget(platform)) {
230247 try macho_file.reportParseError2(self.index, "invalid platform: {}", .{
231 platform.fmtTarget(macho_file.getTarget().cpu.arch),
248 platform.fmtTarget(cpu_arch),
232249 });
233250 return error.InvalidTarget;
234251 }
......@@ -244,8 +261,10 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
244261 // }
245262 }
246263
247 for (self.atoms.items) |atom_index| {
248 const atom = macho_file.getAtom(atom_index).?;
264 try self.parseDebugInfo(macho_file);
265
266 for (self.getAtoms()) |atom_index| {
267 const atom = self.getAtom(atom_index) orelse continue;
249268 const isec = atom.getInputSection(macho_file);
250269 if (mem.eql(u8, isec.sectName(), "__eh_frame") or
251270 mem.eql(u8, isec.sectName(), "__compact_unwind") or
......@@ -274,10 +293,9 @@ pub fn isPtrLiteral(sect: macho.section_64) bool {
274293 return sect.type() == macho.S_LITERAL_POINTERS;
275294}
276295
277fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
296fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
278297 const tracy = trace(@src());
279298 defer tracy.end();
280 const gpa = macho_file.base.comp.gpa;
281299 const slice = self.sections.slice();
282300 for (slice.items(.header), slice.items(.subsections), 0..) |sect, *subsections, n_sect| {
283301 if (isCstringLiteral(sect)) continue;
......@@ -292,17 +310,18 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
292310 } else nlists.len;
293311
294312 if (nlist_start == nlist_end or nlists[nlist_start].nlist.n_value > sect.addr) {
295 const name = try std.fmt.allocPrintZ(gpa, "{s}${s}", .{ sect.segName(), sect.sectName() });
296 defer gpa.free(name);
313 const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() });
314 defer allocator.free(name);
297315 const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr;
298 const atom_index = try self.addAtom(.{
299 .name = try self.addString(gpa, name),
316 const atom_index = try self.addAtom(allocator, .{
317 .name = try self.addString(allocator, name),
300318 .n_sect = @intCast(n_sect),
301319 .off = 0,
302320 .size = size,
303321 .alignment = sect.@"align",
304 }, macho_file);
305 try subsections.append(gpa, .{
322 });
323 try self.atoms_indexes.append(allocator, atom_index);
324 try subsections.append(allocator, .{
306325 .atom = atom_index,
307326 .off = 0,
308327 });
......@@ -325,14 +344,15 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
325344 @min(@ctz(nlist.nlist.n_value), sect.@"align")
326345 else
327346 sect.@"align";
328 const atom_index = try self.addAtom(.{
347 const atom_index = try self.addAtom(allocator, .{
329348 .name = nlist.nlist.n_strx,
330349 .n_sect = @intCast(n_sect),
331350 .off = nlist.nlist.n_value - sect.addr,
332351 .size = size,
333352 .alignment = alignment,
334 }, macho_file);
335 try subsections.append(gpa, .{
353 });
354 try self.atoms_indexes.append(allocator, atom_index);
355 try subsections.append(allocator, .{
336356 .atom = atom_index,
337357 .off = nlist.nlist.n_value - sect.addr,
338358 });
......@@ -344,30 +364,31 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
344364 }
345365}
346366
347fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
367fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void {
348368 const tracy = trace(@src());
349369 defer tracy.end();
350 const gpa = macho_file.base.comp.gpa;
351370 const slice = self.sections.slice();
352371
353 try self.atoms.ensureUnusedCapacity(gpa, self.sections.items(.header).len);
372 try self.atoms.ensureUnusedCapacity(allocator, self.sections.items(.header).len);
373 try self.atoms_indexes.ensureUnusedCapacity(allocator, self.sections.items(.header).len);
354374
355375 for (slice.items(.header), 0..) |sect, n_sect| {
356376 if (isCstringLiteral(sect)) continue;
357377 if (isFixedSizeLiteral(sect)) continue;
358378 if (isPtrLiteral(sect)) continue;
359379
360 const name = try std.fmt.allocPrintZ(gpa, "{s}${s}", .{ sect.segName(), sect.sectName() });
361 defer gpa.free(name);
380 const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() });
381 defer allocator.free(name);
362382
363 const atom_index = try self.addAtom(.{
364 .name = try self.addString(gpa, name),
383 const atom_index = try self.addAtom(allocator, .{
384 .name = try self.addString(allocator, name),
365385 .n_sect = @intCast(n_sect),
366386 .off = 0,
367387 .size = sect.size,
368388 .alignment = sect.@"align",
369 }, macho_file);
370 try slice.items(.subsections)[n_sect].append(gpa, .{ .atom = atom_index, .off = 0 });
389 });
390 try self.atoms_indexes.append(allocator, atom_index);
391 try slice.items(.subsections)[n_sect].append(allocator, .{ .atom = atom_index, .off = 0 });
371392
372393 const nlist_start = for (nlists, 0..) |nlist, i| {
373394 if (nlist.nlist.n_sect - 1 == n_sect) break i;
......@@ -396,21 +417,25 @@ fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
396417 }
397418}
398419
399fn initCstringLiterals(self: *Object, macho_file: *MachO) !void {
420fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, macho_file: *MachO) !void {
400421 const tracy = trace(@src());
401422 defer tracy.end();
402423
403 const gpa = macho_file.base.comp.gpa;
404424 const slice = self.sections.slice();
405425
406426 for (slice.items(.header), 0..) |sect, n_sect| {
407427 if (!isCstringLiteral(sect)) continue;
408428
409 const data = try self.getSectionData(@intCast(n_sect), macho_file);
410 defer gpa.free(data);
429 const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
430 const data = try allocator.alloc(u8, sect_size);
431 defer allocator.free(data);
432 const amt = try file.preadAll(data, sect.offset + self.offset);
433 if (amt != data.len) return error.InputOutput;
411434
435 var count: u32 = 0;
412436 var start: u32 = 0;
413437 while (start < data.len) {
438 defer count += 1;
414439 var end = start;
415440 while (end < data.len - 1 and data[end] != 0) : (end += 1) {}
416441 if (data[end] != 0) {
......@@ -423,32 +448,52 @@ fn initCstringLiterals(self: *Object, macho_file: *MachO) !void {
423448 }
424449 end += 1;
425450
426 const atom_index = try self.addAtom(.{
427 .name = 0,
451 const name = try std.fmt.allocPrintZ(allocator, "l._str{d}", .{count});
452 defer allocator.free(name);
453 const name_str = try self.addString(allocator, name);
454
455 const atom_index = try self.addAtom(allocator, .{
456 .name = name_str,
428457 .n_sect = @intCast(n_sect),
429458 .off = start,
430459 .size = end - start,
431460 .alignment = sect.@"align",
432 }, macho_file);
433 try slice.items(.subsections)[n_sect].append(gpa, .{
461 });
462 try self.atoms_indexes.append(allocator, atom_index);
463 try slice.items(.subsections)[n_sect].append(allocator, .{
434464 .atom = atom_index,
435465 .off = start,
436466 });
437467
468 const atom = self.getAtom(atom_index).?;
469 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
470 self.symtab.set(nlist_index, .{
471 .nlist = .{
472 .n_strx = name_str,
473 .n_type = macho.N_SECT,
474 .n_sect = @intCast(atom.n_sect + 1),
475 .n_desc = 0,
476 .n_value = atom.getInputAddress(macho_file),
477 },
478 .size = atom.size,
479 .atom = atom_index,
480 });
481 atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
482
438483 start = end;
439484 }
440485 }
441486}
442487
443fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void {
488fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
444489 const tracy = trace(@src());
445490 defer tracy.end();
446491
447 const gpa = macho_file.base.comp.gpa;
448492 const slice = self.sections.slice();
449493
450494 for (slice.items(.header), 0..) |sect, n_sect| {
451495 if (!isFixedSizeLiteral(sect)) continue;
496
452497 const rec_size: u8 = switch (sect.type()) {
453498 macho.S_4BYTE_LITERALS => 4,
454499 macho.S_8BYTE_LITERALS => 8,
......@@ -463,28 +508,52 @@ fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void {
463508 );
464509 return error.MalformedObject;
465510 }
511
466512 var pos: u32 = 0;
467 while (pos < sect.size) : (pos += rec_size) {
468 const atom_index = try self.addAtom(.{
469 .name = 0,
513 var count: u32 = 0;
514 while (pos < sect.size) : ({
515 pos += rec_size;
516 count += 1;
517 }) {
518 const name = try std.fmt.allocPrintZ(allocator, "l._literal{d}", .{count});
519 defer allocator.free(name);
520 const name_str = try self.addString(allocator, name);
521
522 const atom_index = try self.addAtom(allocator, .{
523 .name = name_str,
470524 .n_sect = @intCast(n_sect),
471525 .off = pos,
472526 .size = rec_size,
473527 .alignment = sect.@"align",
474 }, macho_file);
475 try slice.items(.subsections)[n_sect].append(gpa, .{
528 });
529 try self.atoms_indexes.append(allocator, atom_index);
530 try slice.items(.subsections)[n_sect].append(allocator, .{
476531 .atom = atom_index,
477532 .off = pos,
478533 });
534
535 const atom = self.getAtom(atom_index).?;
536 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
537 self.symtab.set(nlist_index, .{
538 .nlist = .{
539 .n_strx = name_str,
540 .n_type = macho.N_SECT,
541 .n_sect = @intCast(atom.n_sect + 1),
542 .n_desc = 0,
543 .n_value = atom.getInputAddress(macho_file),
544 },
545 .size = atom.size,
546 .atom = atom_index,
547 });
548 atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
479549 }
480550 }
481551}
482552
483fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {
553fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
484554 const tracy = trace(@src());
485555 defer tracy.end();
486556
487 const gpa = macho_file.base.comp.gpa;
488557 const slice = self.sections.slice();
489558
490559 for (slice.items(.header), 0..) |sect, n_sect| {
......@@ -503,134 +572,171 @@ fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {
503572
504573 for (0..num_ptrs) |i| {
505574 const pos: u32 = @as(u32, @intCast(i)) * rec_size;
506 const atom_index = try self.addAtom(.{
507 .name = 0,
575
576 const name = try std.fmt.allocPrintZ(allocator, "l._ptr{d}", .{i});
577 defer allocator.free(name);
578 const name_str = try self.addString(allocator, name);
579
580 const atom_index = try self.addAtom(allocator, .{
581 .name = name_str,
508582 .n_sect = @intCast(n_sect),
509583 .off = pos,
510584 .size = rec_size,
511585 .alignment = sect.@"align",
512 }, macho_file);
513 try slice.items(.subsections)[n_sect].append(gpa, .{
586 });
587 try self.atoms_indexes.append(allocator, atom_index);
588 try slice.items(.subsections)[n_sect].append(allocator, .{
514589 .atom = atom_index,
515590 .off = pos,
516591 });
592
593 const atom = self.getAtom(atom_index).?;
594 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
595 self.symtab.set(nlist_index, .{
596 .nlist = .{
597 .n_strx = name_str,
598 .n_type = macho.N_SECT,
599 .n_sect = @intCast(atom.n_sect + 1),
600 .n_desc = 0,
601 .n_value = atom.getInputAddress(macho_file),
602 },
603 .size = atom.size,
604 .atom = atom_index,
605 });
606 atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
517607 }
518608 }
519609}
520610
521pub fn resolveLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
611pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
612 const tracy = trace(@src());
613 defer tracy.end();
614
522615 const gpa = macho_file.base.comp.gpa;
616 const file = macho_file.getFileHandle(self.file_handle);
523617
524618 var buffer = std.ArrayList(u8).init(gpa);
525619 defer buffer.deinit();
526620
621 var sections_data = std.AutoHashMap(u32, []const u8).init(gpa);
622 try sections_data.ensureTotalCapacity(@intCast(self.sections.items(.header).len));
623 defer {
624 var it = sections_data.iterator();
625 while (it.next()) |entry| {
626 gpa.free(entry.value_ptr.*);
627 }
628 sections_data.deinit();
629 }
630
527631 const slice = self.sections.slice();
528 for (slice.items(.header), slice.items(.subsections), 0..) |header, subs, n_sect| {
632 for (slice.items(.header), slice.items(.subsections)) |header, subs| {
529633 if (isCstringLiteral(header) or isFixedSizeLiteral(header)) {
530 const data = try self.getSectionData(@intCast(n_sect), macho_file);
634 const sect_size = math.cast(usize, header.size) orelse return error.Overflow;
635 const data = try gpa.alloc(u8, sect_size);
531636 defer gpa.free(data);
637 const amt = try file.preadAll(data, header.offset + self.offset);
638 if (amt != data.len) return error.InputOutput;
532639
533640 for (subs.items) |sub| {
534 const atom = macho_file.getAtom(sub.atom).?;
641 const atom = self.getAtom(sub.atom).?;
535642 const atom_off = math.cast(usize, atom.off) orelse return error.Overflow;
536643 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
537644 const atom_data = data[atom_off..][0..atom_size];
538645 const res = try lp.insert(gpa, header.type(), atom_data);
539646 if (!res.found_existing) {
540 res.atom.* = sub.atom;
647 res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
648 } else {
649 const lp_sym = lp.getSymbol(res.index, macho_file);
650 const lp_atom = lp_sym.getAtom(macho_file).?;
651 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
652 atom.flags.alive = false;
541653 }
542 atom.flags.literal_pool = true;
543 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
654 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
544655 }
545656 } else if (isPtrLiteral(header)) {
546657 for (subs.items) |sub| {
547 const atom = macho_file.getAtom(sub.atom).?;
658 const atom = self.getAtom(sub.atom).?;
548659 const relocs = atom.getRelocs(macho_file);
549660 assert(relocs.len == 1);
550661 const rel = relocs[0];
551662 const target = switch (rel.tag) {
552 .local => rel.target,
553 .@"extern" => rel.getTargetSymbol(macho_file).atom,
663 .local => rel.getTargetAtom(atom.*, macho_file),
664 .@"extern" => rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?,
554665 };
555666 const addend = math.cast(u32, rel.addend) orelse return error.Overflow;
556 const target_atom = macho_file.getAtom(target).?;
557 const target_atom_size = math.cast(usize, target_atom.size) orelse return error.Overflow;
558 try buffer.ensureUnusedCapacity(target_atom_size);
559 buffer.resize(target_atom_size) catch unreachable;
560 try target_atom.getData(macho_file, buffer.items);
667 const target_size = math.cast(usize, target.size) orelse return error.Overflow;
668 try buffer.ensureUnusedCapacity(target_size);
669 buffer.resize(target_size) catch unreachable;
670 const gop = try sections_data.getOrPut(target.n_sect);
671 if (!gop.found_existing) {
672 const target_sect = slice.items(.header)[target.n_sect];
673 const target_sect_size = math.cast(usize, target_sect.size) orelse return error.Overflow;
674 const data = try gpa.alloc(u8, target_sect_size);
675 const amt = try file.preadAll(data, target_sect.offset + self.offset);
676 if (amt != data.len) return error.InputOutput;
677 gop.value_ptr.* = data;
678 }
679 const data = gop.value_ptr.*;
680 const target_off = math.cast(usize, target.off) orelse return error.Overflow;
681 @memcpy(buffer.items, data[target_off..][0..target_size]);
561682 const res = try lp.insert(gpa, header.type(), buffer.items[addend..]);
562683 buffer.clearRetainingCapacity();
563684 if (!res.found_existing) {
564 res.atom.* = sub.atom;
685 res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
686 } else {
687 const lp_sym = lp.getSymbol(res.index, macho_file);
688 const lp_atom = lp_sym.getAtom(macho_file).?;
689 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
690 atom.flags.alive = false;
565691 }
566 atom.flags.literal_pool = true;
567 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
692 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
568693 }
569694 }
570695 }
571696}
572697
573pub fn dedupLiterals(self: Object, lp: MachO.LiteralPool, macho_file: *MachO) void {
574 for (self.atoms.items) |atom_index| {
575 const atom = macho_file.getAtom(atom_index) orelse continue;
698pub fn dedupLiterals(self: *Object, lp: MachO.LiteralPool, macho_file: *MachO) void {
699 const tracy = trace(@src());
700 defer tracy.end();
701
702 for (self.getAtoms()) |atom_index| {
703 const atom = self.getAtom(atom_index) orelse continue;
576704 if (!atom.flags.alive) continue;
577 if (!atom.flags.relocs) continue;
578705
579706 const relocs = blk: {
580 const extra = atom.getExtra(macho_file).?;
707 const extra = atom.getExtra(macho_file);
581708 const relocs = self.sections.items(.relocs)[atom.n_sect].items;
582709 break :blk relocs[extra.rel_index..][0..extra.rel_count];
583710 };
584 for (relocs) |*rel| switch (rel.tag) {
585 .local => {
586 const target = macho_file.getAtom(rel.target).?;
587 if (target.getLiteralPoolIndex(macho_file)) |lp_index| {
588 const lp_atom = lp.getAtom(lp_index, macho_file);
589 if (target.atom_index != lp_atom.atom_index) {
590 lp_atom.alignment = lp_atom.alignment.max(target.alignment);
591 target.flags.alive = false;
592 rel.target = lp_atom.atom_index;
593 }
594 }
595 },
596 .@"extern" => {
597 const target_sym = rel.getTargetSymbol(macho_file);
598 if (target_sym.getAtom(macho_file)) |target_atom| {
599 if (target_atom.getLiteralPoolIndex(macho_file)) |lp_index| {
600 const lp_atom = lp.getAtom(lp_index, macho_file);
601 if (target_atom.atom_index != lp_atom.atom_index) {
602 lp_atom.alignment = lp_atom.alignment.max(target_atom.alignment);
603 target_atom.flags.alive = false;
604 target_sym.atom = lp_atom.atom_index;
605 }
606 }
607 }
608 },
609 };
711 for (relocs) |*rel| {
712 if (rel.tag != .@"extern") continue;
713 const target_sym_ref = rel.getTargetSymbolRef(atom.*, macho_file);
714 const file = target_sym_ref.getFile(macho_file) orelse continue;
715 if (file.getIndex() != self.index) continue;
716 const target_sym = target_sym_ref.getSymbol(macho_file).?;
717 const target_atom = target_sym.getAtom(macho_file) orelse continue;
718 const isec = target_atom.getInputSection(macho_file);
719 if (!Object.isCstringLiteral(isec) and !Object.isFixedSizeLiteral(isec) and !Object.isPtrLiteral(isec)) continue;
720 const lp_index = target_atom.getExtra(macho_file).literal_pool_index;
721 const lp_sym = lp.getSymbol(lp_index, macho_file);
722 const lp_atom_ref = lp_sym.atom_ref;
723 if (target_atom.atom_index != lp_atom_ref.index or target_atom.file != lp_atom_ref.file) {
724 target_sym.atom_ref = lp_atom_ref;
725 }
726 }
610727 }
611}
612728
613const AddAtomArgs = struct {
614 name: u32,
615 n_sect: u8,
616 off: u64,
617 size: u64,
618 alignment: u32,
619};
620
621fn addAtom(self: *Object, args: AddAtomArgs, macho_file: *MachO) !Atom.Index {
622 const gpa = macho_file.base.comp.gpa;
623 const atom_index = try macho_file.addAtom();
624 const atom = macho_file.getAtom(atom_index).?;
625 atom.file = self.index;
626 atom.atom_index = atom_index;
627 atom.name = args.name;
628 atom.n_sect = args.n_sect;
629 atom.size = args.size;
630 atom.alignment = Atom.Alignment.fromLog2Units(args.alignment);
631 atom.off = args.off;
632 try self.atoms.append(gpa, atom_index);
633 return atom_index;
729 for (self.symbols.items) |*sym| {
730 const atom = sym.getAtom(macho_file) orelse continue;
731 const isec = atom.getInputSection(macho_file);
732 if (!Object.isCstringLiteral(isec) and !Object.isFixedSizeLiteral(isec) and !Object.isPtrLiteral(isec)) continue;
733 const lp_index = atom.getExtra(macho_file).literal_pool_index;
734 const lp_sym = lp.getSymbol(lp_index, macho_file);
735 const lp_atom_ref = lp_sym.atom_ref;
736 if (atom.atom_index != lp_atom_ref.index or self.index != lp_atom_ref.file) {
737 sym.atom_ref = lp_atom_ref;
738 }
739 }
634740}
635741
636742pub fn findAtom(self: Object, addr: u64) ?Atom.Index {
......@@ -702,55 +808,61 @@ fn linkNlistToAtom(self: *Object, macho_file: *MachO) !void {
702808 }
703809}
704810
705fn initSymbols(self: *Object, macho_file: *MachO) !void {
811fn initSymbols(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
706812 const tracy = trace(@src());
707813 defer tracy.end();
708 const gpa = macho_file.base.comp.gpa;
814
709815 const slice = self.symtab.slice();
816 const nsyms = slice.items(.nlist).len;
710817
711 try self.symbols.ensureUnusedCapacity(gpa, slice.items(.nlist).len);
818 try self.symbols.ensureTotalCapacityPrecise(allocator, nsyms);
819 try self.symbols_extra.ensureTotalCapacityPrecise(allocator, nsyms * @sizeOf(Symbol.Extra));
820 try self.globals.ensureTotalCapacityPrecise(allocator, nsyms);
821 self.globals.resize(allocator, nsyms) catch unreachable;
822 @memset(self.globals.items, 0);
712823
713824 for (slice.items(.nlist), slice.items(.atom), 0..) |nlist, atom_index, i| {
714 if (nlist.ext()) {
715 const name = self.getString(nlist.n_strx);
716 const off = try macho_file.strings.insert(gpa, name);
717 const gop = try macho_file.getOrCreateGlobal(off);
718 self.symbols.addOneAssumeCapacity().* = gop.index;
719 if (nlist.undf() and nlist.weakRef()) {
720 macho_file.getSymbol(gop.index).flags.weak_ref = true;
721 }
722 continue;
723 }
724
725 const index = try macho_file.addSymbol();
726 self.symbols.appendAssumeCapacity(index);
727 const symbol = macho_file.getSymbol(index);
728 symbol.* = .{
729 .value = nlist.n_value,
730 .name = nlist.n_strx,
731 .nlist_idx = @intCast(i),
732 .atom = 0,
733 .file = self.index,
734 };
735
736 if (macho_file.getAtom(atom_index)) |atom| {
825 const index = self.addSymbolAssumeCapacity();
826 const symbol = &self.symbols.items[index];
827 symbol.value = nlist.n_value;
828 symbol.name = nlist.n_strx;
829 symbol.nlist_idx = @intCast(i);
830 symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
831
832 if (self.getAtom(atom_index)) |atom| {
737833 assert(!nlist.abs());
738834 symbol.value -= atom.getInputAddress(macho_file);
739 symbol.atom = atom_index;
835 symbol.atom_ref = .{ .index = atom_index, .file = self.index };
740836 }
741837
838 symbol.flags.weak = nlist.weakDef();
742839 symbol.flags.abs = nlist.abs();
840 symbol.flags.tentative = nlist.tentative();
743841 symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
842 symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
843 symbol.flags.interposable = false;
844 // TODO
845 // symbol.flags.interposable = nlist.ext() and (nlist.sect() or nlist.abs()) and macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext();
744846
745847 if (nlist.sect() and
746848 self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
747849 {
748850 symbol.flags.tlv = true;
749851 }
852
853 if (nlist.ext()) {
854 if (nlist.undf()) {
855 symbol.flags.weak_ref = nlist.weakRef();
856 } else if (nlist.pext() or (nlist.weakDef() and nlist.weakRef()) or self.hidden) {
857 symbol.visibility = .hidden;
858 } else {
859 symbol.visibility = .global;
860 }
861 }
750862 }
751863}
752864
753fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
865fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_file: *MachO) !void {
754866 const tracy = trace(@src());
755867 defer tracy.end();
756868
......@@ -761,7 +873,7 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
761873 fn find(fs: @This(), addr: u64) ?Symbol.Index {
762874 // TODO binary search since we have the list sorted
763875 for (fs.entries) |nlist| {
764 if (nlist.nlist.n_value == addr) return fs.ctx.symbols.items[nlist.idx];
876 if (nlist.nlist.n_value == addr) return @intCast(nlist.idx);
765877 }
766878 return null;
767879 }
......@@ -776,14 +888,13 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
776888
777889 if (start == end) return;
778890
779 const gpa = macho_file.base.comp.gpa;
780891 const syms = self.symtab.items(.nlist);
781892 const sym_lookup = SymbolLookup{ .ctx = self, .entries = nlists };
782893
783894 // We need to cache nlists by name so that we can properly resolve local N_GSYM stabs.
784895 // What happens is `ld -r` will emit an N_GSYM stab for a symbol that may be either an
785896 // external or private external.
786 var addr_lookup = std.StringHashMap(u64).init(gpa);
897 var addr_lookup = std.StringHashMap(u64).init(allocator);
787898 defer addr_lookup.deinit();
788899 for (syms) |sym| {
789900 if (sym.sect() and (sym.ext() or sym.pext())) {
......@@ -813,17 +924,17 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
813924 switch (nlist.n_type) {
814925 macho.N_BNSYM => {
815926 stab.is_func = true;
816 stab.symbol = sym_lookup.find(nlist.n_value);
927 stab.index = sym_lookup.find(nlist.n_value);
817928 // TODO validate
818929 i += 3;
819930 },
820931 macho.N_GSYM => {
821932 stab.is_func = false;
822 stab.symbol = sym_lookup.find(addr_lookup.get(self.getString(nlist.n_strx)).?);
933 stab.index = sym_lookup.find(addr_lookup.get(self.getString(nlist.n_strx)).?);
823934 },
824935 macho.N_STSYM => {
825936 stab.is_func = false;
826 stab.symbol = sym_lookup.find(nlist.n_value);
937 stab.index = sym_lookup.find(nlist.n_value);
827938 },
828939 else => {
829940 try macho_file.reportParseError2(self.index, "unhandled symbol stab type 0x{x}", .{
......@@ -832,29 +943,35 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
832943 return error.MalformedObject;
833944 },
834945 }
835 try sf.stabs.append(gpa, stab);
946 try sf.stabs.append(allocator, stab);
836947 }
837948
838 try self.stab_files.append(gpa, sf);
949 try self.stab_files.append(allocator, sf);
839950 }
840951}
841952
842953fn sortAtoms(self: *Object, macho_file: *MachO) !void {
843 const lessThanAtom = struct {
844 fn lessThanAtom(ctx: *MachO, lhs: Atom.Index, rhs: Atom.Index) bool {
845 return ctx.getAtom(lhs).?.getInputAddress(ctx) < ctx.getAtom(rhs).?.getInputAddress(ctx);
954 const Ctx = struct {
955 object: *Object,
956 mfile: *MachO,
957
958 fn lessThanAtom(ctx: @This(), lhs: Atom.Index, rhs: Atom.Index) bool {
959 return ctx.object.getAtom(lhs).?.getInputAddress(ctx.mfile) <
960 ctx.object.getAtom(rhs).?.getInputAddress(ctx.mfile);
846961 }
847 }.lessThanAtom;
848 mem.sort(Atom.Index, self.atoms.items, macho_file, lessThanAtom);
962 };
963 mem.sort(Atom.Index, self.atoms_indexes.items, Ctx{
964 .object = self,
965 .mfile = macho_file,
966 }, Ctx.lessThanAtom);
849967}
850968
851fn initRelocs(self: *Object, macho_file: *MachO) !void {
969fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, macho_file: *MachO) !void {
852970 const tracy = trace(@src());
853971 defer tracy.end();
854 const cpu_arch = macho_file.getTarget().cpu.arch;
855972 const slice = self.sections.slice();
856973
857 for (slice.items(.header), slice.items(.relocs), 0..) |sect, *out, n_sect| {
974 for (slice.items(.header), slice.items(.relocs)) |sect, *out| {
858975 if (sect.nreloc == 0) continue;
859976 // We skip relocs for __DWARF since even in -r mode, the linker is expected to emit
860977 // debug symbol stabs in the relocatable. This made me curious why that is. For now,
......@@ -863,8 +980,8 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
863980 !mem.eql(u8, sect.sectName(), "__compact_unwind")) continue;
864981
865982 switch (cpu_arch) {
866 .x86_64 => try x86_64.parseRelocs(self, @intCast(n_sect), sect, out, macho_file),
867 .aarch64 => try aarch64.parseRelocs(self, @intCast(n_sect), sect, out, macho_file),
983 .x86_64 => try x86_64.parseRelocs(self, sect, out, file, macho_file),
984 .aarch64 => try aarch64.parseRelocs(self, sect, out, file, macho_file),
868985 else => unreachable,
869986 }
870987
......@@ -876,7 +993,7 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
876993
877994 var next_reloc: u32 = 0;
878995 for (subsections.items) |subsection| {
879 const atom = macho_file.getAtom(subsection.atom).?;
996 const atom = self.getAtom(subsection.atom).?;
880997 if (!atom.flags.alive) continue;
881998 if (next_reloc >= relocs.items.len) break;
882999 const end_addr = atom.off + atom.size;
......@@ -885,27 +1002,23 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
8851002 while (next_reloc < relocs.items.len and relocs.items[next_reloc].offset < end_addr) : (next_reloc += 1) {}
8861003
8871004 const rel_count = next_reloc - rel_index;
888 try atom.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, macho_file);
889 atom.flags.relocs = true;
1005 atom.addExtra(.{ .rel_index = @intCast(rel_index), .rel_count = @intCast(rel_count) }, macho_file);
8901006 }
8911007 }
8921008}
8931009
894fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
1010fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: File.Handle, macho_file: *MachO) !void {
8951011 const tracy = trace(@src());
8961012 defer tracy.end();
897 const gpa = macho_file.base.comp.gpa;
8981013 const nlists = self.symtab.items(.nlist);
8991014 const slice = self.sections.slice();
9001015 const sect = slice.items(.header)[sect_id];
9011016 const relocs = slice.items(.relocs)[sect_id];
9021017
903 // TODO: read into buffer directly
904 const data = try self.getSectionData(sect_id, macho_file);
905 defer gpa.free(data);
906
907 try self.eh_frame_data.ensureTotalCapacityPrecise(gpa, data.len);
908 self.eh_frame_data.appendSliceAssumeCapacity(data);
1018 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1019 try self.eh_frame_data.resize(allocator, size);
1020 const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset);
1021 if (amt != self.eh_frame_data.items.len) return error.InputOutput;
9091022
9101023 // Check for non-personality relocs in FDEs and apply them
9111024 for (relocs.items, 0..) |rel, i| {
......@@ -937,12 +1050,12 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
9371050 var it = eh_frame.Iterator{ .data = self.eh_frame_data.items };
9381051 while (try it.next()) |rec| {
9391052 switch (rec.tag) {
940 .cie => try self.cies.append(gpa, .{
1053 .cie => try self.cies.append(allocator, .{
9411054 .offset = rec.offset,
9421055 .size = rec.size,
9431056 .file = self.index,
9441057 }),
945 .fde => try self.fdes.append(gpa, .{
1058 .fde => try self.fdes.append(allocator, .{
9461059 .offset = rec.offset,
9471060 .size = rec.size,
9481061 .cie = undefined,
......@@ -987,7 +1100,7 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
9871100 }
9881101}
9891102
990fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
1103fn initUnwindRecords(self: *Object, allocator: Allocator, sect_id: u8, file: File.Handle, macho_file: *MachO) !void {
9911104 const tracy = trace(@src());
9921105 defer tracy.end();
9931106
......@@ -995,27 +1108,31 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
9951108 ctx: *const Object,
9961109
9971110 fn find(fs: @This(), addr: u64) ?Symbol.Index {
998 for (fs.ctx.symbols.items, 0..) |sym_index, i| {
1111 for (0..fs.ctx.symbols.items.len) |i| {
9991112 const nlist = fs.ctx.symtab.items(.nlist)[i];
1000 if (nlist.ext() and nlist.n_value == addr) return sym_index;
1113 if (nlist.ext() and nlist.n_value == addr) return @intCast(i);
10011114 }
10021115 return null;
10031116 }
10041117 };
10051118
1006 const gpa = macho_file.base.comp.gpa;
1007 const data = try self.getSectionData(sect_id, macho_file);
1008 defer gpa.free(data);
1119 const header = self.sections.items(.header)[sect_id];
1120 const size = math.cast(usize, header.size) orelse return error.Overflow;
1121 const data = try allocator.alloc(u8, size);
1122 defer allocator.free(data);
1123 const amt = try file.preadAll(data, header.offset + self.offset);
1124 if (amt != data.len) return error.InputOutput;
1125
10091126 const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
10101127 const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs];
10111128 const sym_lookup = SymbolLookup{ .ctx = self };
10121129
1013 try self.unwind_records.resize(gpa, nrecs);
1130 try self.unwind_records.ensureTotalCapacityPrecise(allocator, nrecs);
1131 try self.unwind_records_indexes.ensureTotalCapacityPrecise(allocator, nrecs);
10141132
1015 const header = self.sections.items(.header)[sect_id];
10161133 const relocs = self.sections.items(.relocs)[sect_id].items;
10171134 var reloc_idx: usize = 0;
1018 for (recs, self.unwind_records.items, 0..) |rec, *out_index, rec_idx| {
1135 for (recs, 0..) |rec, rec_idx| {
10191136 const rec_start = rec_idx * @sizeOf(macho.compact_unwind_entry);
10201137 const rec_end = rec_start + @sizeOf(macho.compact_unwind_entry);
10211138 const reloc_start = reloc_idx;
......@@ -1023,11 +1140,11 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
10231140 relocs[reloc_idx].offset < rec_end) : (reloc_idx += 1)
10241141 {}
10251142
1026 out_index.* = try macho_file.addUnwindRecord();
1027 const out = macho_file.getUnwindRecord(out_index.*);
1143 const out_index = self.addUnwindRecordAssumeCapacity();
1144 self.unwind_records_indexes.appendAssumeCapacity(out_index);
1145 const out = self.getUnwindRecord(out_index);
10281146 out.length = rec.rangeLength;
10291147 out.enc = .{ .enc = rec.compactUnwindEncoding };
1030 out.file = self.index;
10311148
10321149 for (relocs[reloc_start..reloc_idx]) |rel| {
10331150 if (rel.type != .unsigned or rel.meta.length != 3) {
......@@ -1090,7 +1207,7 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
10901207 }
10911208}
10921209
1093fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
1210fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch, macho_file: *MachO) !void {
10941211 // Synthesise missing unwind records.
10951212 // The logic here is as follows:
10961213 // 1. if an atom has unwind info record that is not DWARF, FDE is marked dead
......@@ -1100,8 +1217,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
11001217
11011218 const Superposition = struct { atom: Atom.Index, size: u64, cu: ?UnwindInfo.Record.Index = null, fde: ?Fde.Index = null };
11021219
1103 const gpa = macho_file.base.comp.gpa;
1104 var superposition = std.AutoArrayHashMap(u64, Superposition).init(gpa);
1220 var superposition = std.AutoArrayHashMap(u64, Superposition).init(allocator);
11051221 defer superposition.deinit();
11061222
11071223 const slice = self.symtab.slice();
......@@ -1119,8 +1235,8 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
11191235 }
11201236 }
11211237
1122 for (self.unwind_records.items) |rec_index| {
1123 const rec = macho_file.getUnwindRecord(rec_index);
1238 for (self.unwind_records_indexes.items) |rec_index| {
1239 const rec = self.getUnwindRecord(rec_index);
11241240 const atom = rec.getAtom(macho_file);
11251241 const addr = atom.getInputAddress(macho_file) + rec.atom_offset;
11261242 superposition.getPtr(addr).?.cu = rec_index;
......@@ -1137,7 +1253,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
11371253 const fde = &self.fdes.items[fde_index];
11381254
11391255 if (meta.cu) |rec_index| {
1140 const rec = macho_file.getUnwindRecord(rec_index);
1256 const rec = self.getUnwindRecord(rec_index);
11411257 if (!rec.enc.isDwarf(macho_file)) {
11421258 // Mark FDE dead
11431259 fde.alive = false;
......@@ -1147,15 +1263,14 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
11471263 }
11481264 } else {
11491265 // Synthesise new unwind info record
1150 const rec_index = try macho_file.addUnwindRecord();
1151 const rec = macho_file.getUnwindRecord(rec_index);
1152 try self.unwind_records.append(gpa, rec_index);
1266 const rec_index = try self.addUnwindRecord(allocator);
1267 const rec = self.getUnwindRecord(rec_index);
1268 try self.unwind_records_indexes.append(allocator, rec_index);
11531269 rec.length = @intCast(meta.size);
11541270 rec.atom = fde.atom;
11551271 rec.atom_offset = fde.atom_offset;
11561272 rec.fde = fde_index;
1157 rec.file = fde.file;
1158 switch (macho_file.getTarget().cpu.arch) {
1273 switch (cpu_arch) {
11591274 .x86_64 => rec.enc.setMode(macho.UNWIND_X86_64_MODE.DWARF),
11601275 .aarch64 => rec.enc.setMode(macho.UNWIND_ARM64_MODE.DWARF),
11611276 else => unreachable,
......@@ -1163,10 +1278,10 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
11631278 }
11641279 } else if (meta.cu == null and meta.fde == null) {
11651280 // Create a null record
1166 const rec_index = try macho_file.addUnwindRecord();
1167 const rec = macho_file.getUnwindRecord(rec_index);
1168 const atom = macho_file.getAtom(meta.atom).?;
1169 try self.unwind_records.append(gpa, rec_index);
1281 const rec_index = try self.addUnwindRecord(allocator);
1282 const rec = self.getUnwindRecord(rec_index);
1283 const atom = self.getAtom(meta.atom).?;
1284 try self.unwind_records_indexes.append(allocator, rec_index);
11701285 rec.length = @intCast(meta.size);
11711286 rec.atom = meta.atom;
11721287 rec.atom_offset = @intCast(addr - atom.getInputAddress(macho_file));
......@@ -1174,30 +1289,35 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
11741289 }
11751290 }
11761291
1177 const sortFn = struct {
1178 fn sortFn(ctx: *MachO, lhs_index: UnwindInfo.Record.Index, rhs_index: UnwindInfo.Record.Index) bool {
1179 const lhs = ctx.getUnwindRecord(lhs_index);
1180 const rhs = ctx.getUnwindRecord(rhs_index);
1181 const lhsa = lhs.getAtom(ctx);
1182 const rhsa = rhs.getAtom(ctx);
1183 return lhsa.getInputAddress(ctx) + lhs.atom_offset < rhsa.getInputAddress(ctx) + rhs.atom_offset;
1292 const SortCtx = struct {
1293 object: *Object,
1294 mfile: *MachO,
1295
1296 fn sort(ctx: @This(), lhs_index: UnwindInfo.Record.Index, rhs_index: UnwindInfo.Record.Index) bool {
1297 const lhs = ctx.object.getUnwindRecord(lhs_index);
1298 const rhs = ctx.object.getUnwindRecord(rhs_index);
1299 const lhsa = lhs.getAtom(ctx.mfile);
1300 const rhsa = rhs.getAtom(ctx.mfile);
1301 return lhsa.getInputAddress(ctx.mfile) + lhs.atom_offset < rhsa.getInputAddress(ctx.mfile) + rhs.atom_offset;
11841302 }
1185 }.sortFn;
1186 mem.sort(UnwindInfo.Record.Index, self.unwind_records.items, macho_file, sortFn);
1303 };
1304 mem.sort(UnwindInfo.Record.Index, self.unwind_records_indexes.items, SortCtx{
1305 .object = self,
1306 .mfile = macho_file,
1307 }, SortCtx.sort);
11871308
11881309 // Associate unwind records to atoms
11891310 var next_cu: u32 = 0;
1190 while (next_cu < self.unwind_records.items.len) {
1311 while (next_cu < self.unwind_records_indexes.items.len) {
11911312 const start = next_cu;
1192 const rec_index = self.unwind_records.items[start];
1193 const rec = macho_file.getUnwindRecord(rec_index);
1194 while (next_cu < self.unwind_records.items.len and
1195 macho_file.getUnwindRecord(self.unwind_records.items[next_cu]).atom == rec.atom) : (next_cu += 1)
1313 const rec_index = self.unwind_records_indexes.items[start];
1314 const rec = self.getUnwindRecord(rec_index);
1315 while (next_cu < self.unwind_records_indexes.items.len and
1316 self.getUnwindRecord(self.unwind_records_indexes.items[next_cu]).atom == rec.atom) : (next_cu += 1)
11961317 {}
11971318
11981319 const atom = rec.getAtom(macho_file);
1199 try atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file);
1200 atom.flags.unwind = true;
1320 atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file);
12011321 }
12021322}
12031323
......@@ -1205,7 +1325,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
12051325/// and record that so that we can emit symbol stabs.
12061326/// TODO in the future, we want parse debug info and debug line sections so that
12071327/// we can provide nice error locations to the user.
1208pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
1328fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
12091329 const tracy = trace(@src());
12101330 defer tracy.end();
12111331
......@@ -1224,11 +1344,34 @@ pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
12241344
12251345 if (debug_info_index == null or debug_abbrev_index == null) return;
12261346
1227 const debug_info = try self.getSectionData(@intCast(debug_info_index.?), macho_file);
1347 const slice = self.sections.slice();
1348 const file = macho_file.getFileHandle(self.file_handle);
1349 const debug_info = blk: {
1350 const sect = slice.items(.header)[debug_info_index.?];
1351 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1352 const data = try gpa.alloc(u8, size);
1353 const amt = try file.preadAll(data, sect.offset + self.offset);
1354 if (amt != data.len) return error.InputOutput;
1355 break :blk data;
1356 };
12281357 defer gpa.free(debug_info);
1229 const debug_abbrev = try self.getSectionData(@intCast(debug_abbrev_index.?), macho_file);
1358 const debug_abbrev = blk: {
1359 const sect = slice.items(.header)[debug_abbrev_index.?];
1360 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1361 const data = try gpa.alloc(u8, size);
1362 const amt = try file.preadAll(data, sect.offset + self.offset);
1363 if (amt != data.len) return error.InputOutput;
1364 break :blk data;
1365 };
12301366 defer gpa.free(debug_abbrev);
1231 const debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index), macho_file) else &[0]u8{};
1367 const debug_str = if (debug_str_index) |sid| blk: {
1368 const sect = slice.items(.header)[sid];
1369 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1370 const data = try gpa.alloc(u8, size);
1371 const amt = try file.preadAll(data, sect.offset + self.offset);
1372 if (amt != data.len) return error.InputOutput;
1373 break :blk data;
1374 } else &[0]u8{};
12321375 defer gpa.free(debug_str);
12331376
12341377 self.compile_unit = self.findCompileUnit(.{
......@@ -1334,87 +1477,55 @@ fn findCompileUnit(self: *Object, args: struct {
13341477 };
13351478}
13361479
1337pub fn resolveSymbols(self: *Object, macho_file: *MachO) void {
1480pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void {
13381481 const tracy = trace(@src());
13391482 defer tracy.end();
13401483
1341 for (self.symbols.items, 0..) |index, i| {
1342 const nlist_idx = @as(Symbol.Index, @intCast(i));
1343 const nlist = self.symtab.items(.nlist)[nlist_idx];
1344 const atom_index = self.symtab.items(.atom)[nlist_idx];
1484 const gpa = macho_file.base.comp.gpa;
13451485
1486 for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| {
13461487 if (!nlist.ext()) continue;
1347 if (nlist.undf() and !nlist.tentative()) continue;
13481488 if (nlist.sect()) {
1349 const atom = macho_file.getAtom(atom_index).?;
1489 const atom = self.getAtom(atom_index).?;
13501490 if (!atom.flags.alive) continue;
13511491 }
13521492
1353 const symbol = macho_file.getSymbol(index);
1493 const gop = try macho_file.resolver.getOrPut(gpa, .{
1494 .index = @intCast(i),
1495 .file = self.index,
1496 }, macho_file);
1497 if (!gop.found_existing) {
1498 gop.ref.* = .{ .index = 0, .file = 0 };
1499 }
1500 global.* = gop.index;
1501
1502 if (nlist.undf() and !nlist.tentative()) continue;
1503 if (gop.ref.getFile(macho_file) == null) {
1504 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
1505 continue;
1506 }
1507
13541508 if (self.asFile().getSymbolRank(.{
13551509 .archive = !self.alive,
13561510 .weak = nlist.weakDef(),
13571511 .tentative = nlist.tentative(),
1358 }) < symbol.getSymbolRank(macho_file)) {
1359 const value = if (nlist.sect()) blk: {
1360 const atom = macho_file.getAtom(atom_index).?;
1361 break :blk nlist.n_value - atom.getInputAddress(macho_file);
1362 } else nlist.n_value;
1363 symbol.value = value;
1364 symbol.atom = atom_index;
1365 symbol.nlist_idx = nlist_idx;
1366 symbol.file = self.index;
1367 symbol.flags.weak = nlist.weakDef();
1368 symbol.flags.abs = nlist.abs();
1369 symbol.flags.tentative = nlist.tentative();
1370 symbol.flags.weak_ref = false;
1371 symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
1372 symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
1373 // TODO: symbol.flags.interposable = macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext();
1374 symbol.flags.interposable = false;
1375
1376 if (nlist.sect() and
1377 self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
1378 {
1379 symbol.flags.tlv = true;
1380 }
1381 }
1382
1383 // Regardless of who the winner is, we still merge symbol visibility here.
1384 if (nlist.pext() or (nlist.weakDef() and nlist.weakRef()) or self.hidden) {
1385 if (symbol.visibility != .global) {
1386 symbol.visibility = .hidden;
1387 }
1388 } else {
1389 symbol.visibility = .global;
1512 }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
1513 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
13901514 }
13911515 }
13921516}
13931517
1394pub fn resetGlobals(self: *Object, macho_file: *MachO) void {
1395 for (self.symbols.items, 0..) |sym_index, nlist_idx| {
1396 if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue;
1397 const sym = macho_file.getSymbol(sym_index);
1398 const name = sym.name;
1399 const global = sym.flags.global;
1400 const weak_ref = sym.flags.weak_ref;
1401 sym.* = .{};
1402 sym.name = name;
1403 sym.flags.global = global;
1404 sym.flags.weak_ref = weak_ref;
1405 }
1406}
1407
14081518pub fn markLive(self: *Object, macho_file: *MachO) void {
14091519 const tracy = trace(@src());
14101520 defer tracy.end();
14111521
1412 for (self.symbols.items, 0..) |index, nlist_idx| {
1413 const nlist = self.symtab.items(.nlist)[nlist_idx];
1522 for (0..self.symbols.items.len) |i| {
1523 const nlist = self.symtab.items(.nlist)[i];
14141524 if (!nlist.ext()) continue;
14151525
1416 const sym = macho_file.getSymbol(index);
1417 const file = sym.getFile(macho_file) orelse continue;
1526 const ref = self.getSymbolRef(@intCast(i), macho_file);
1527 const file = ref.getFile(macho_file) orelse continue;
1528 const sym = ref.getSymbol(macho_file).?;
14181529 const should_keep = nlist.undf() or (nlist.tentative() and !sym.flags.tentative);
14191530 if (should_keep and file == .object and !file.object.alive) {
14201531 file.object.alive = true;
......@@ -1423,38 +1534,36 @@ pub fn markLive(self: *Object, macho_file: *MachO) void {
14231534 }
14241535}
14251536
1426pub fn checkDuplicates(self: *Object, dupes: anytype, macho_file: *MachO) error{OutOfMemory}!void {
1427 for (self.symbols.items, 0..) |index, nlist_idx| {
1428 const sym = macho_file.getSymbol(index);
1429 if (sym.visibility != .global) continue;
1430 const file = sym.getFile(macho_file) orelse continue;
1431 if (file.getIndex() == self.index) continue;
1537pub fn mergeSymbolVisibility(self: *Object, macho_file: *MachO) void {
1538 const tracy = trace(@src());
1539 defer tracy.end();
14321540
1433 const nlist = self.symtab.items(.nlist)[nlist_idx];
1434 if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) {
1435 const gop = try dupes.getOrPut(index);
1436 if (!gop.found_existing) {
1437 gop.value_ptr.* = .{};
1438 }
1439 try gop.value_ptr.append(macho_file.base.comp.gpa, self.index);
1541 for (self.symbols.items, 0..) |sym, i| {
1542 const ref = self.getSymbolRef(@intCast(i), macho_file);
1543 const global = ref.getSymbol(macho_file) orelse continue;
1544 if (sym.visibility.rank() < global.visibility.rank()) {
1545 global.visibility = sym.visibility;
1546 }
1547 if (sym.flags.weak_ref) {
1548 global.flags.weak_ref = true;
14401549 }
14411550 }
14421551}
14431552
1444pub fn scanRelocs(self: Object, macho_file: *MachO) !void {
1553pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {
14451554 const tracy = trace(@src());
14461555 defer tracy.end();
14471556
1448 for (self.atoms.items) |atom_index| {
1449 const atom = macho_file.getAtom(atom_index).?;
1557 for (self.getAtoms()) |atom_index| {
1558 const atom = self.getAtom(atom_index) orelse continue;
14501559 if (!atom.flags.alive) continue;
14511560 const sect = atom.getInputSection(macho_file);
14521561 if (sect.isZerofill()) continue;
14531562 try atom.scanRelocs(macho_file);
14541563 }
14551564
1456 for (self.unwind_records.items) |rec_index| {
1457 const rec = macho_file.getUnwindRecord(rec_index);
1565 for (self.unwind_records_indexes.items) |rec_index| {
1566 const rec = self.getUnwindRecord(rec_index);
14581567 if (!rec.alive) continue;
14591568 if (rec.getFde(macho_file)) |fde| {
14601569 if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| {
......@@ -1471,38 +1580,35 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
14711580 defer tracy.end();
14721581 const gpa = macho_file.base.comp.gpa;
14731582
1474 for (self.symbols.items, 0..) |index, i| {
1475 const sym = macho_file.getSymbol(index);
1583 for (self.symbols.items, self.globals.items, 0..) |*sym, off, i| {
14761584 if (!sym.flags.tentative) continue;
1477 const sym_file = sym.getFile(macho_file).?;
1478 if (sym_file.getIndex() != self.index) continue;
1585 if (macho_file.resolver.get(off).?.file != self.index) continue;
14791586
14801587 const nlist_idx = @as(Symbol.Index, @intCast(i));
14811588 const nlist = &self.symtab.items(.nlist)[nlist_idx];
14821589 const nlist_atom = &self.symtab.items(.atom)[nlist_idx];
14831590
1484 const atom_index = try macho_file.addAtom();
1485 try self.atoms.append(gpa, atom_index);
1486
14871591 const name = try std.fmt.allocPrintZ(gpa, "__DATA$__common${s}", .{sym.getName(macho_file)});
14881592 defer gpa.free(name);
1489 const atom = macho_file.getAtom(atom_index).?;
1490 atom.atom_index = atom_index;
1491 atom.name = try self.addString(gpa, name);
1492 atom.file = self.index;
1493 atom.size = nlist.n_value;
1494 atom.alignment = Atom.Alignment.fromLog2Units((nlist.n_desc >> 8) & 0x0f);
14951593
1594 const alignment = (nlist.n_desc >> 8) & 0x0f;
14961595 const n_sect = try self.addSection(gpa, "__DATA", "__common");
1596 const atom_index = try self.addAtom(gpa, .{
1597 .name = try self.addString(gpa, name),
1598 .n_sect = n_sect,
1599 .off = 0,
1600 .size = nlist.n_value,
1601 .alignment = alignment,
1602 });
1603 try self.atoms_indexes.append(gpa, atom_index);
1604
14971605 const sect = &self.sections.items(.header)[n_sect];
14981606 sect.flags = macho.S_ZEROFILL;
1499 sect.size = atom.size;
1500 sect.@"align" = atom.alignment.toLog2Units();
1501 atom.n_sect = n_sect;
1607 sect.size = nlist.n_value;
1608 sect.@"align" = alignment;
15021609
15031610 sym.value = 0;
1504 sym.atom = atom_index;
1505 sym.flags.global = true;
1611 sym.atom_ref = .{ .index = atom_index, .file = self.index };
15061612 sym.flags.weak = false;
15071613 sym.flags.weak_ref = false;
15081614 sym.flags.tentative = false;
......@@ -1516,8 +1622,8 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
15161622 }
15171623}
15181624
1519fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u32 {
1520 const n_sect = @as(u32, @intCast(try self.sections.addOne(allocator)));
1625fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u8 {
1626 const n_sect = @as(u8, @intCast(try self.sections.addOne(allocator)));
15211627 self.sections.set(n_sect, .{
15221628 .header = .{
15231629 .sectname = MachO.makeStaticString(sectname),
......@@ -1532,12 +1638,11 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void {
15321638 defer tracy.end();
15331639
15341640 const gpa = macho_file.base.comp.gpa;
1535 const offset = if (self.archive) |ar| ar.offset else 0;
15361641 const handle = macho_file.getFileHandle(self.file_handle);
15371642
15381643 var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
15391644 {
1540 const amt = try handle.preadAll(&header_buffer, offset);
1645 const amt = try handle.preadAll(&header_buffer, self.offset);
15411646 if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput;
15421647 }
15431648 self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*;
......@@ -1558,7 +1663,7 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void {
15581663 const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds);
15591664 defer gpa.free(lc_buffer);
15601665 {
1561 const amt = try handle.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64));
1666 const amt = try handle.preadAll(lc_buffer, self.offset + @sizeOf(macho.mach_header_64));
15621667 if (amt != self.header.?.sizeofcmds) return error.InputOutput;
15631668 }
15641669
......@@ -1571,14 +1676,14 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void {
15711676 const cmd = lc.cast(macho.symtab_command).?;
15721677 try self.strtab.resize(gpa, cmd.strsize);
15731678 {
1574 const amt = try handle.preadAll(self.strtab.items, cmd.stroff + offset);
1679 const amt = try handle.preadAll(self.strtab.items, cmd.stroff + self.offset);
15751680 if (amt != self.strtab.items.len) return error.InputOutput;
15761681 }
15771682
15781683 const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64));
15791684 defer gpa.free(symtab_buffer);
15801685 {
1581 const amt = try handle.preadAll(symtab_buffer, cmd.symoff + offset);
1686 const amt = try handle.preadAll(symtab_buffer, cmd.symoff + self.offset);
15821687 if (amt != symtab_buffer.len) return error.InputOutput;
15831688 }
15841689 const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms];
......@@ -1613,7 +1718,7 @@ pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, macho_file: *M
16131718}
16141719
16151720pub fn updateArSize(self: *Object, macho_file: *MachO) !void {
1616 self.output_ar_state.size = if (self.archive) |ar| ar.size else size: {
1721 self.output_ar_state.size = if (self.in_archive) |ar| ar.size else size: {
16171722 const file = macho_file.getFileHandle(self.file_handle);
16181723 break :size (try file.stat()).size;
16191724 };
......@@ -1622,7 +1727,6 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void {
16221727pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {
16231728 // Header
16241729 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
1625 const offset: u64 = if (self.archive) |ar| ar.offset else 0;
16261730 try Archive.writeHeader(self.path, size, ar_format, writer);
16271731 // Data
16281732 const file = macho_file.getFileHandle(self.file_handle);
......@@ -1630,72 +1734,75 @@ pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writ
16301734 const gpa = macho_file.base.comp.gpa;
16311735 const data = try gpa.alloc(u8, size);
16321736 defer gpa.free(data);
1633 const amt = try file.preadAll(data, offset);
1737 const amt = try file.preadAll(data, self.offset);
16341738 if (amt != size) return error.InputOutput;
16351739 try writer.writeAll(data);
16361740}
16371741
1638pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void {
1742pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void {
16391743 const tracy = trace(@src());
16401744 defer tracy.end();
16411745
1642 for (self.symbols.items) |sym_index| {
1643 const sym = macho_file.getSymbol(sym_index);
1644 const file = sym.getFile(macho_file) orelse continue;
1746 const is_obj = macho_file.base.isObject();
1747
1748 for (self.symbols.items, 0..) |*sym, i| {
1749 const ref = self.getSymbolRef(@intCast(i), macho_file);
1750 const file = ref.getFile(macho_file) orelse continue;
16451751 if (file.getIndex() != self.index) continue;
16461752 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
16471753 if (sym.isSymbolStab(macho_file)) continue;
16481754 const name = sym.getName(macho_file);
1755 if (name.len == 0) continue;
16491756 // TODO in -r mode, we actually want to merge symbol names and emit only one
16501757 // work it out when emitting relocs
1651 if (name.len > 0 and
1652 (name[0] == 'L' or name[0] == 'l' or
1758 if ((name[0] == 'L' or name[0] == 'l' or
16531759 mem.startsWith(u8, name, "_OBJC_SELECTOR_REFERENCES_")) and
1654 !macho_file.base.isObject()) continue;
1760 !is_obj)
1761 continue;
16551762 sym.flags.output_symtab = true;
16561763 if (sym.isLocal()) {
1657 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
1764 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
16581765 self.output_symtab_ctx.nlocals += 1;
16591766 } else if (sym.flags.@"export") {
1660 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
1767 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
16611768 self.output_symtab_ctx.nexports += 1;
16621769 } else {
16631770 assert(sym.flags.import);
1664 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
1771 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
16651772 self.output_symtab_ctx.nimports += 1;
16661773 }
16671774 self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
16681775 }
16691776
16701777 if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo())
1671 try self.calcStabsSize(macho_file);
1778 self.calcStabsSize(macho_file);
16721779}
16731780
1674pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
1781pub fn calcStabsSize(self: *Object, macho_file: *MachO) void {
16751782 if (self.compile_unit) |cu| {
1676 const comp_dir = cu.getCompDir(self);
1677 const tu_name = cu.getTuName(self);
1783 const comp_dir = cu.getCompDir(self.*);
1784 const tu_name = cu.getTuName(self.*);
16781785
16791786 self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO
16801787 self.output_symtab_ctx.strsize += @as(u32, @intCast(comp_dir.len + 1)); // comp_dir
16811788 self.output_symtab_ctx.strsize += @as(u32, @intCast(tu_name.len + 1)); // tu_name
16821789
1683 if (self.archive) |ar| {
1790 if (self.in_archive) |ar| {
16841791 self.output_symtab_ctx.strsize += @as(u32, @intCast(ar.path.len + 1 + self.path.len + 1 + 1));
16851792 } else {
16861793 self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1));
16871794 }
16881795
1689 for (self.symbols.items) |sym_index| {
1690 const sym = macho_file.getSymbol(sym_index);
1691 const file = sym.getFile(macho_file) orelse continue;
1796 for (self.symbols.items, 0..) |sym, i| {
1797 const ref = self.getSymbolRef(@intCast(i), macho_file);
1798 const file = ref.getFile(macho_file) orelse continue;
16921799 if (file.getIndex() != self.index) continue;
16931800 if (!sym.flags.output_symtab) continue;
16941801 if (macho_file.base.isObject()) {
16951802 const name = sym.getName(macho_file);
16961803 if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;
16971804 }
1698 const sect = macho_file.sections.items(.header)[sym.out_n_sect];
1805 const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)];
16991806 if (sect.isCode()) {
17001807 self.output_symtab_ctx.nstabs += 4; // N_BNSYM, N_FUN, N_FUN, N_ENSYM
17011808 } else if (sym.visibility == .global) {
......@@ -1709,12 +1816,12 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
17091816
17101817 for (self.stab_files.items) |sf| {
17111818 self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO
1712 self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getCompDir(self).len + 1)); // comp_dir
1713 self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getTuName(self).len + 1)); // tu_name
1714 self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getOsoPath(self).len + 1)); // path
1819 self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getCompDir(self.*).len + 1)); // comp_dir
1820 self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getTuName(self.*).len + 1)); // tu_name
1821 self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getOsoPath(self.*).len + 1)); // path
17151822
17161823 for (sf.stabs.items) |stab| {
1717 const sym = stab.getSymbol(macho_file) orelse continue;
1824 const sym = stab.getSymbol(self.*) orelse continue;
17181825 const file = sym.getFile(macho_file).?;
17191826 if (file.getIndex() != self.index) continue;
17201827 if (!sym.flags.output_symtab) continue;
......@@ -1725,28 +1832,212 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
17251832 }
17261833}
17271834
1728pub fn writeSymtab(self: Object, macho_file: *MachO, ctx: anytype) error{Overflow}!void {
1835pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {
17291836 const tracy = trace(@src());
17301837 defer tracy.end();
17311838
1732 for (self.symbols.items) |sym_index| {
1733 const sym = macho_file.getSymbol(sym_index);
1734 const file = sym.getFile(macho_file) orelse continue;
1839 const gpa = macho_file.base.comp.gpa;
1840 const headers = self.sections.items(.header);
1841 const sections_data = try gpa.alloc([]const u8, headers.len);
1842 defer {
1843 for (sections_data) |data| {
1844 gpa.free(data);
1845 }
1846 gpa.free(sections_data);
1847 }
1848 @memset(sections_data, &[0]u8{});
1849 const file = macho_file.getFileHandle(self.file_handle);
1850
1851 for (headers, 0..) |header, n_sect| {
1852 if (header.isZerofill()) continue;
1853 const size = math.cast(usize, header.size) orelse return error.Overflow;
1854 const data = try gpa.alloc(u8, size);
1855 const amt = try file.preadAll(data, header.offset + self.offset);
1856 if (amt != data.len) return error.InputOutput;
1857 sections_data[n_sect] = data;
1858 }
1859 for (self.getAtoms()) |atom_index| {
1860 const atom = self.getAtom(atom_index) orelse continue;
1861 if (!atom.flags.alive) continue;
1862 const sect = atom.getInputSection(macho_file);
1863 if (sect.isZerofill()) continue;
1864 const value = math.cast(usize, atom.value) orelse return error.Overflow;
1865 const off = math.cast(usize, atom.off) orelse return error.Overflow;
1866 const size = math.cast(usize, atom.size) orelse return error.Overflow;
1867 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
1868 const data = sections_data[atom.n_sect];
1869 @memcpy(buffer[value..][0..size], data[off..][0..size]);
1870 try atom.resolveRelocs(macho_file, buffer[value..][0..size]);
1871 }
1872}
1873
1874pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
1875 const tracy = trace(@src());
1876 defer tracy.end();
1877
1878 const gpa = macho_file.base.comp.gpa;
1879 const headers = self.sections.items(.header);
1880 const sections_data = try gpa.alloc([]const u8, headers.len);
1881 defer {
1882 for (sections_data) |data| {
1883 gpa.free(data);
1884 }
1885 gpa.free(sections_data);
1886 }
1887 @memset(sections_data, &[0]u8{});
1888 const file = macho_file.getFileHandle(self.file_handle);
1889
1890 for (headers, 0..) |header, n_sect| {
1891 if (header.isZerofill()) continue;
1892 const size = math.cast(usize, header.size) orelse return error.Overflow;
1893 const data = try gpa.alloc(u8, size);
1894 const amt = try file.preadAll(data, header.offset + self.offset);
1895 if (amt != data.len) return error.InputOutput;
1896 sections_data[n_sect] = data;
1897 }
1898 for (self.getAtoms()) |atom_index| {
1899 const atom = self.getAtom(atom_index) orelse continue;
1900 if (!atom.flags.alive) continue;
1901 const sect = atom.getInputSection(macho_file);
1902 if (sect.isZerofill()) continue;
1903 const value = math.cast(usize, atom.value) orelse return error.Overflow;
1904 const off = math.cast(usize, atom.off) orelse return error.Overflow;
1905 const size = math.cast(usize, atom.size) orelse return error.Overflow;
1906 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
1907 const data = sections_data[atom.n_sect];
1908 @memcpy(buffer[value..][0..size], data[off..][0..size]);
1909 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
1910 const extra = atom.getExtra(macho_file);
1911 try atom.writeRelocs(macho_file, buffer[value..][0..size], relocs[extra.rel_out_index..][0..extra.rel_out_count]);
1912 }
1913}
1914
1915pub fn calcCompactUnwindSizeRelocatable(self: *Object, macho_file: *MachO) void {
1916 const tracy = trace(@src());
1917 defer tracy.end();
1918
1919 const ctx = &self.compact_unwind_ctx;
1920
1921 for (self.unwind_records_indexes.items) |irec| {
1922 const rec = self.getUnwindRecord(irec);
1923 if (!rec.alive) continue;
1924
1925 ctx.rec_count += 1;
1926 ctx.reloc_count += 1;
1927 if (rec.getPersonality(macho_file)) |_| {
1928 ctx.reloc_count += 1;
1929 }
1930 if (rec.getLsdaAtom(macho_file)) |_| {
1931 ctx.reloc_count += 1;
1932 }
1933 }
1934}
1935
1936pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
1937 const tracy = trace(@src());
1938 defer tracy.end();
1939
1940 const cpu_arch = macho_file.getTarget().cpu.arch;
1941
1942 const addReloc = struct {
1943 fn addReloc(offset: u32, arch: std.Target.Cpu.Arch) !macho.relocation_info {
1944 return .{
1945 .r_address = math.cast(i32, offset) orelse return error.Overflow,
1946 .r_symbolnum = 0,
1947 .r_pcrel = 0,
1948 .r_length = 3,
1949 .r_extern = 0,
1950 .r_type = switch (arch) {
1951 .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1952 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1953 else => unreachable,
1954 },
1955 };
1956 }
1957 }.addReloc;
1958
1959 const nsect = macho_file.unwind_info_sect_index.?;
1960 const buffer = macho_file.sections.items(.out)[nsect].items;
1961 const relocs = macho_file.sections.items(.relocs)[nsect].items;
1962
1963 var rec_index: u32 = self.compact_unwind_ctx.rec_index;
1964 var reloc_index: u32 = self.compact_unwind_ctx.reloc_index;
1965
1966 for (self.unwind_records_indexes.items) |irec| {
1967 const rec = self.getUnwindRecord(irec);
1968 if (!rec.alive) continue;
1969
1970 var out: macho.compact_unwind_entry = .{
1971 .rangeStart = 0,
1972 .rangeLength = rec.length,
1973 .compactUnwindEncoding = rec.enc.enc,
1974 .personalityFunction = 0,
1975 .lsda = 0,
1976 };
1977 defer rec_index += 1;
1978
1979 const offset = rec_index * @sizeOf(macho.compact_unwind_entry);
1980
1981 {
1982 // Function address
1983 const atom = rec.getAtom(macho_file);
1984 const addr = rec.getAtomAddress(macho_file);
1985 out.rangeStart = addr;
1986 var reloc = try addReloc(offset, cpu_arch);
1987 reloc.r_symbolnum = atom.out_n_sect + 1;
1988 relocs[reloc_index] = reloc;
1989 reloc_index += 1;
1990 }
1991
1992 // Personality function
1993 if (rec.getPersonality(macho_file)) |sym| {
1994 const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
1995 var reloc = try addReloc(offset + 16, cpu_arch);
1996 reloc.r_symbolnum = r_symbolnum;
1997 reloc.r_extern = 1;
1998 relocs[reloc_index] = reloc;
1999 reloc_index += 1;
2000 }
2001
2002 // LSDA address
2003 if (rec.getLsdaAtom(macho_file)) |atom| {
2004 const addr = rec.getLsdaAddress(macho_file);
2005 out.lsda = addr;
2006 var reloc = try addReloc(offset + 24, cpu_arch);
2007 reloc.r_symbolnum = atom.out_n_sect + 1;
2008 relocs[reloc_index] = reloc;
2009 reloc_index += 1;
2010 }
2011
2012 @memcpy(buffer[offset..][0..@sizeOf(macho.compact_unwind_entry)], mem.asBytes(&out));
2013 }
2014}
2015
2016pub fn writeSymtab(self: Object, macho_file: *MachO, ctx: anytype) void {
2017 const tracy = trace(@src());
2018 defer tracy.end();
2019
2020 var n_strx = self.output_symtab_ctx.stroff;
2021 for (self.symbols.items, 0..) |sym, i| {
2022 const ref = self.getSymbolRef(@intCast(i), macho_file);
2023 const file = ref.getFile(macho_file) orelse continue;
17352024 if (file.getIndex() != self.index) continue;
17362025 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
1737 const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1738 ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file));
1739 ctx.strtab.appendAssumeCapacity(0);
17402026 const out_sym = &ctx.symtab.items[idx];
17412027 out_sym.n_strx = n_strx;
17422028 sym.setOutputSym(macho_file, out_sym);
2029 const name = sym.getName(macho_file);
2030 @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
2031 n_strx += @intCast(name.len);
2032 ctx.strtab.items[n_strx] = 0;
2033 n_strx += 1;
17432034 }
17442035
17452036 if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo())
1746 try self.writeStabs(macho_file, ctx);
2037 self.writeStabs(n_strx, macho_file, ctx);
17472038}
17482039
1749pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{Overflow}!void {
2040pub fn writeStabs(self: Object, stroff: u32, macho_file: *MachO, ctx: anytype) void {
17502041 const writeFuncStab = struct {
17512042 inline fn writeFuncStab(
17522043 n_strx: u32,
......@@ -1788,6 +2079,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
17882079 }.writeFuncStab;
17892080
17902081 var index = self.output_symtab_ctx.istab;
2082 var n_strx = stroff;
17912083
17922084 if (self.compile_unit) |cu| {
17932085 const comp_dir = cu.getCompDir(self);
......@@ -1795,9 +2087,6 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
17952087
17962088 // Open scope
17972089 // N_SO comp_dir
1798 var n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1799 ctx.strtab.appendSliceAssumeCapacity(comp_dir);
1800 ctx.strtab.appendAssumeCapacity(0);
18012090 ctx.symtab.items[index] = .{
18022091 .n_strx = n_strx,
18032092 .n_type = macho.N_SO,
......@@ -1806,11 +2095,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
18062095 .n_value = 0,
18072096 };
18082097 index += 1;
2098 @memcpy(ctx.strtab.items[n_strx..][0..comp_dir.len], comp_dir);
2099 n_strx += @intCast(comp_dir.len);
2100 ctx.strtab.items[n_strx] = 0;
2101 n_strx += 1;
18092102 // N_SO tu_name
1810 n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1811 ctx.strtab.appendSliceAssumeCapacity(tu_name);
1812 ctx.strtab.appendAssumeCapacity(0);
1813 ctx.symtab.items[index] = .{
2103 macho_file.symtab.items[index] = .{
18142104 .n_strx = n_strx,
18152105 .n_type = macho.N_SO,
18162106 .n_sect = 0,
......@@ -1818,18 +2108,11 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
18182108 .n_value = 0,
18192109 };
18202110 index += 1;
2111 @memcpy(ctx.strtab.items[n_strx..][0..tu_name.len], tu_name);
2112 n_strx += @intCast(tu_name.len);
2113 ctx.strtab.items[n_strx] = 0;
2114 n_strx += 1;
18212115 // N_OSO path
1822 n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1823 if (self.archive) |ar| {
1824 ctx.strtab.appendSliceAssumeCapacity(ar.path);
1825 ctx.strtab.appendAssumeCapacity('(');
1826 ctx.strtab.appendSliceAssumeCapacity(self.path);
1827 ctx.strtab.appendAssumeCapacity(')');
1828 ctx.strtab.appendAssumeCapacity(0);
1829 } else {
1830 ctx.strtab.appendSliceAssumeCapacity(self.path);
1831 ctx.strtab.appendAssumeCapacity(0);
1832 }
18332116 ctx.symtab.items[index] = .{
18342117 .n_strx = n_strx,
18352118 .n_type = macho.N_OSO,
......@@ -1838,23 +2121,40 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
18382121 .n_value = self.mtime,
18392122 };
18402123 index += 1;
2124 if (self.in_archive) |ar| {
2125 @memcpy(ctx.strtab.items[n_strx..][0..ar.path.len], ar.path);
2126 n_strx += @intCast(ar.path.len);
2127 ctx.strtab.items[n_strx] = '(';
2128 n_strx += 1;
2129 @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path);
2130 n_strx += @intCast(self.path.len);
2131 ctx.strtab.items[n_strx] = ')';
2132 n_strx += 1;
2133 ctx.strtab.items[n_strx] = 0;
2134 n_strx += 1;
2135 } else {
2136 @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path);
2137 n_strx += @intCast(self.path.len);
2138 ctx.strtab.items[n_strx] = 0;
2139 n_strx += 1;
2140 }
18412141
1842 for (self.symbols.items) |sym_index| {
1843 const sym = macho_file.getSymbol(sym_index);
1844 const file = sym.getFile(macho_file) orelse continue;
2142 for (self.symbols.items, 0..) |sym, i| {
2143 const ref = self.getSymbolRef(@intCast(i), macho_file);
2144 const file = ref.getFile(macho_file) orelse continue;
18452145 if (file.getIndex() != self.index) continue;
18462146 if (!sym.flags.output_symtab) continue;
18472147 if (macho_file.base.isObject()) {
18482148 const name = sym.getName(macho_file);
18492149 if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;
18502150 }
1851 const sect = macho_file.sections.items(.header)[sym.out_n_sect];
2151 const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)];
18522152 const sym_n_strx = n_strx: {
18532153 const symtab_index = sym.getOutputSymtabIndex(macho_file).?;
18542154 const osym = ctx.symtab.items[symtab_index];
18552155 break :n_strx osym.n_strx;
18562156 };
1857 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0;
2157 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0;
18582158 const sym_n_value = sym.getAddress(.{}, macho_file);
18592159 const sym_size = sym.getSize(macho_file);
18602160 if (sect.isCode()) {
......@@ -1894,11 +2194,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
18942194 assert(self.hasSymbolStabs());
18952195
18962196 for (self.stab_files.items) |sf| {
2197 const comp_dir = sf.getCompDir(self);
2198 const tu_name = sf.getTuName(self);
2199 const oso_path = sf.getOsoPath(self);
2200
18972201 // Open scope
18982202 // N_SO comp_dir
1899 var n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1900 ctx.strtab.appendSliceAssumeCapacity(sf.getCompDir(self));
1901 ctx.strtab.appendAssumeCapacity(0);
19022203 ctx.symtab.items[index] = .{
19032204 .n_strx = n_strx,
19042205 .n_type = macho.N_SO,
......@@ -1907,10 +2208,11 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19072208 .n_value = 0,
19082209 };
19092210 index += 1;
2211 @memcpy(ctx.strtab.items[n_strx..][0..comp_dir.len], comp_dir);
2212 n_strx += @intCast(comp_dir.len);
2213 ctx.strtab.items[n_strx] = 0;
2214 n_strx += 1;
19102215 // N_SO tu_name
1911 n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1912 ctx.strtab.appendSliceAssumeCapacity(sf.getTuName(self));
1913 ctx.strtab.appendAssumeCapacity(0);
19142216 ctx.symtab.items[index] = .{
19152217 .n_strx = n_strx,
19162218 .n_type = macho.N_SO,
......@@ -1919,10 +2221,11 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19192221 .n_value = 0,
19202222 };
19212223 index += 1;
2224 @memcpy(ctx.strtab.items[n_strx..][0..tu_name.len], tu_name);
2225 n_strx += @intCast(tu_name.len);
2226 ctx.strtab.items[n_strx] = 0;
2227 n_strx += 1;
19222228 // N_OSO path
1923 n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1924 ctx.strtab.appendSliceAssumeCapacity(sf.getOsoPath(self));
1925 ctx.strtab.appendAssumeCapacity(0);
19262229 ctx.symtab.items[index] = .{
19272230 .n_strx = n_strx,
19282231 .n_type = macho.N_OSO,
......@@ -1931,9 +2234,13 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19312234 .n_value = sf.getOsoModTime(self),
19322235 };
19332236 index += 1;
2237 @memcpy(ctx.strtab.items[n_strx..][0..oso_path.len], oso_path);
2238 n_strx += @intCast(oso_path.len);
2239 ctx.strtab.items[n_strx] = 0;
2240 n_strx += 1;
19342241
19352242 for (sf.stabs.items) |stab| {
1936 const sym = stab.getSymbol(macho_file) orelse continue;
2243 const sym = stab.getSymbol(self) orelse continue;
19372244 const file = sym.getFile(macho_file).?;
19382245 if (file.getIndex() != self.index) continue;
19392246 if (!sym.flags.output_symtab) continue;
......@@ -1942,7 +2249,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19422249 const osym = ctx.symtab.items[symtab_index];
19432250 break :n_strx osym.n_strx;
19442251 };
1945 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0;
2252 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0;
19462253 const sym_n_value = sym.getAddress(.{}, macho_file);
19472254 const sym_size = sym.getSize(macho_file);
19482255 if (stab.is_func) {
......@@ -1983,34 +2290,8 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
19832290 }
19842291}
19852292
1986fn getSectionData(self: *const Object, index: u32, macho_file: *MachO) ![]u8 {
1987 const gpa = macho_file.base.comp.gpa;
1988 const slice = self.sections.slice();
1989 assert(index < slice.items(.header).len);
1990 const sect = slice.items(.header)[index];
1991 const handle = macho_file.getFileHandle(self.file_handle);
1992 const offset = if (self.archive) |ar| ar.offset else 0;
1993 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1994 const buffer = try gpa.alloc(u8, size);
1995 errdefer gpa.free(buffer);
1996 const amt = try handle.preadAll(buffer, sect.offset + offset);
1997 if (amt != buffer.len) return error.InputOutput;
1998 return buffer;
1999}
2000
2001pub fn getAtomData(self: *const Object, macho_file: *MachO, atom: Atom, buffer: []u8) !void {
2002 assert(buffer.len == atom.size);
2003 const slice = self.sections.slice();
2004 const handle = macho_file.getFileHandle(self.file_handle);
2005 const offset = if (self.archive) |ar| ar.offset else 0;
2006 const sect = slice.items(.header)[atom.n_sect];
2007 const amt = try handle.preadAll(buffer, sect.offset + offset + atom.off);
2008 if (amt != buffer.len) return error.InputOutput;
2009}
2010
20112293pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation {
2012 if (!atom.flags.relocs) return &[0]Relocation{};
2013 const extra = atom.getExtra(macho_file).?;
2294 const extra = atom.getExtra(macho_file);
20142295 const relocs = self.sections.items(.relocs)[atom.n_sect];
20152296 return relocs.items[extra.rel_index..][0..extra.rel_count];
20162297}
......@@ -2068,6 +2349,160 @@ pub fn asFile(self: *Object) File {
20682349 return .{ .object = self };
20692350}
20702351
2352const AddAtomArgs = struct {
2353 name: u32,
2354 n_sect: u8,
2355 off: u64,
2356 size: u64,
2357 alignment: u32,
2358};
2359
2360fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index {
2361 const atom_index: Atom.Index = @intCast(self.atoms.items.len);
2362 const atom = try self.atoms.addOne(allocator);
2363 atom.* = .{
2364 .file = self.index,
2365 .atom_index = atom_index,
2366 .name = args.name,
2367 .n_sect = args.n_sect,
2368 .size = args.size,
2369 .off = args.off,
2370 .extra = try self.addAtomExtra(allocator, .{}),
2371 .alignment = Atom.Alignment.fromLog2Units(args.alignment),
2372 };
2373 return atom_index;
2374}
2375
2376pub fn getAtom(self: *Object, atom_index: Atom.Index) ?*Atom {
2377 if (atom_index == 0) return null;
2378 assert(atom_index < self.atoms.items.len);
2379 return &self.atoms.items[atom_index];
2380}
2381
2382pub fn getAtoms(self: *Object) []const Atom.Index {
2383 return self.atoms_indexes.items;
2384}
2385
2386fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 {
2387 const fields = @typeInfo(Atom.Extra).Struct.fields;
2388 try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
2389 return self.addAtomExtraAssumeCapacity(extra);
2390}
2391
2392fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 {
2393 const index = @as(u32, @intCast(self.atoms_extra.items.len));
2394 const fields = @typeInfo(Atom.Extra).Struct.fields;
2395 inline for (fields) |field| {
2396 self.atoms_extra.appendAssumeCapacity(switch (field.type) {
2397 u32 => @field(extra, field.name),
2398 else => @compileError("bad field type"),
2399 });
2400 }
2401 return index;
2402}
2403
2404pub fn getAtomExtra(self: Object, index: u32) Atom.Extra {
2405 const fields = @typeInfo(Atom.Extra).Struct.fields;
2406 var i: usize = index;
2407 var result: Atom.Extra = undefined;
2408 inline for (fields) |field| {
2409 @field(result, field.name) = switch (field.type) {
2410 u32 => self.atoms_extra.items[i],
2411 else => @compileError("bad field type"),
2412 };
2413 i += 1;
2414 }
2415 return result;
2416}
2417
2418pub fn setAtomExtra(self: *Object, index: u32, extra: Atom.Extra) void {
2419 assert(index > 0);
2420 const fields = @typeInfo(Atom.Extra).Struct.fields;
2421 inline for (fields, 0..) |field, i| {
2422 self.atoms_extra.items[index + i] = switch (field.type) {
2423 u32 => @field(extra, field.name),
2424 else => @compileError("bad field type"),
2425 };
2426 }
2427}
2428
2429fn addSymbol(self: *Object, allocator: Allocator) !Symbol.Index {
2430 try self.symbols.ensureUnusedCapacity(allocator, 1);
2431 return self.addSymbolAssumeCapacity();
2432}
2433
2434fn addSymbolAssumeCapacity(self: *Object) Symbol.Index {
2435 const index: Symbol.Index = @intCast(self.symbols.items.len);
2436 const symbol = self.symbols.addOneAssumeCapacity();
2437 symbol.* = .{ .file = self.index };
2438 return index;
2439}
2440
2441pub fn getSymbolRef(self: Object, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
2442 const global_index = self.globals.items[index];
2443 if (macho_file.resolver.get(global_index)) |ref| return ref;
2444 return .{ .index = index, .file = self.index };
2445}
2446
2447pub fn addSymbolExtra(self: *Object, allocator: Allocator, extra: Symbol.Extra) !u32 {
2448 const fields = @typeInfo(Symbol.Extra).Struct.fields;
2449 try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
2450 return self.addSymbolExtraAssumeCapacity(extra);
2451}
2452
2453fn addSymbolExtraAssumeCapacity(self: *Object, extra: Symbol.Extra) u32 {
2454 const index = @as(u32, @intCast(self.symbols_extra.items.len));
2455 const fields = @typeInfo(Symbol.Extra).Struct.fields;
2456 inline for (fields) |field| {
2457 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
2458 u32 => @field(extra, field.name),
2459 else => @compileError("bad field type"),
2460 });
2461 }
2462 return index;
2463}
2464
2465pub fn getSymbolExtra(self: Object, index: u32) Symbol.Extra {
2466 const fields = @typeInfo(Symbol.Extra).Struct.fields;
2467 var i: usize = index;
2468 var result: Symbol.Extra = undefined;
2469 inline for (fields) |field| {
2470 @field(result, field.name) = switch (field.type) {
2471 u32 => self.symbols_extra.items[i],
2472 else => @compileError("bad field type"),
2473 };
2474 i += 1;
2475 }
2476 return result;
2477}
2478
2479pub fn setSymbolExtra(self: *Object, index: u32, extra: Symbol.Extra) void {
2480 const fields = @typeInfo(Symbol.Extra).Struct.fields;
2481 inline for (fields, 0..) |field, i| {
2482 self.symbols_extra.items[index + i] = switch (field.type) {
2483 u32 => @field(extra, field.name),
2484 else => @compileError("bad field type"),
2485 };
2486 }
2487}
2488
2489fn addUnwindRecord(self: *Object, allocator: Allocator) !UnwindInfo.Record.Index {
2490 try self.unwind_records.ensureUnusedCapacity(allocator, 1);
2491 return self.addUnwindRecordAssumeCapacity();
2492}
2493
2494fn addUnwindRecordAssumeCapacity(self: *Object) UnwindInfo.Record.Index {
2495 const index = @as(UnwindInfo.Record.Index, @intCast(self.unwind_records.items.len));
2496 const rec = self.unwind_records.addOneAssumeCapacity();
2497 rec.* = .{ .file = self.index };
2498 return index;
2499}
2500
2501pub fn getUnwindRecord(self: *Object, index: UnwindInfo.Record.Index) *UnwindInfo.Record {
2502 assert(index < self.unwind_records.items.len);
2503 return &self.unwind_records.items[index];
2504}
2505
20712506pub fn format(
20722507 self: *Object,
20732508 comptime unused_fmt_string: []const u8,
......@@ -2102,10 +2537,11 @@ fn formatAtoms(
21022537 _ = unused_fmt_string;
21032538 _ = options;
21042539 const object = ctx.object;
2540 const macho_file = ctx.macho_file;
21052541 try writer.writeAll(" atoms\n");
2106 for (object.atoms.items) |atom_index| {
2107 const atom = ctx.macho_file.getAtom(atom_index).?;
2108 try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});
2542 for (object.getAtoms()) |atom_index| {
2543 const atom = object.getAtom(atom_index) orelse continue;
2544 try writer.print(" {}\n", .{atom.fmt(macho_file)});
21092545 }
21102546}
21112547
......@@ -2171,8 +2607,8 @@ fn formatUnwindRecords(
21712607 const object = ctx.object;
21722608 const macho_file = ctx.macho_file;
21732609 try writer.writeAll(" unwind records\n");
2174 for (object.unwind_records.items) |rec| {
2175 try writer.print(" rec({d}) : {}\n", .{ rec, macho_file.getUnwindRecord(rec).fmt(macho_file) });
2610 for (object.unwind_records_indexes.items) |rec| {
2611 try writer.print(" rec({d}) : {}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) });
21762612 }
21772613}
21782614
......@@ -2192,10 +2628,26 @@ fn formatSymtab(
21922628 _ = unused_fmt_string;
21932629 _ = options;
21942630 const object = ctx.object;
2631 const macho_file = ctx.macho_file;
21952632 try writer.writeAll(" symbols\n");
2196 for (object.symbols.items) |index| {
2197 const sym = ctx.macho_file.getSymbol(index);
2198 try writer.print(" {}\n", .{sym.fmt(ctx.macho_file)});
2633 for (object.symbols.items, 0..) |sym, i| {
2634 const ref = object.getSymbolRef(@intCast(i), macho_file);
2635 if (ref.getFile(macho_file) == null) {
2636 // TODO any better way of handling this?
2637 try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
2638 } else {
2639 try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
2640 }
2641 }
2642 for (object.stab_files.items) |sf| {
2643 try writer.print(" stabs({s},{s},{s})\n", .{
2644 sf.getCompDir(object.*),
2645 sf.getTuName(object.*),
2646 sf.getOsoPath(object.*),
2647 });
2648 for (sf.stabs.items) |stab| {
2649 try writer.print(" {}", .{stab.fmt(object.*)});
2650 }
21992651 }
22002652}
22012653
......@@ -2211,7 +2663,7 @@ fn formatPath(
22112663) !void {
22122664 _ = unused_fmt_string;
22132665 _ = options;
2214 if (object.archive) |ar| {
2666 if (object.in_archive) |ar| {
22152667 try writer.writeAll(ar.path);
22162668 try writer.writeByte('(');
22172669 try writer.writeAll(object.path);
......@@ -2240,32 +2692,71 @@ const StabFile = struct {
22402692 comp_dir: u32,
22412693 stabs: std.ArrayListUnmanaged(Stab) = .{},
22422694
2243 fn getCompDir(sf: StabFile, object: *const Object) [:0]const u8 {
2695 fn getCompDir(sf: StabFile, object: Object) [:0]const u8 {
22442696 const nlist = object.symtab.items(.nlist)[sf.comp_dir];
22452697 return object.getString(nlist.n_strx);
22462698 }
22472699
2248 fn getTuName(sf: StabFile, object: *const Object) [:0]const u8 {
2700 fn getTuName(sf: StabFile, object: Object) [:0]const u8 {
22492701 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 1];
22502702 return object.getString(nlist.n_strx);
22512703 }
22522704
2253 fn getOsoPath(sf: StabFile, object: *const Object) [:0]const u8 {
2705 fn getOsoPath(sf: StabFile, object: Object) [:0]const u8 {
22542706 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2];
22552707 return object.getString(nlist.n_strx);
22562708 }
22572709
2258 fn getOsoModTime(sf: StabFile, object: *const Object) u64 {
2710 fn getOsoModTime(sf: StabFile, object: Object) u64 {
22592711 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2];
22602712 return nlist.n_value;
22612713 }
22622714
22632715 const Stab = struct {
22642716 is_func: bool = true,
2265 symbol: ?Symbol.Index = null,
2717 index: ?Symbol.Index = null,
2718
2719 fn getSymbol(stab: Stab, object: Object) ?Symbol {
2720 const index = stab.index orelse return null;
2721 return object.symbols.items[index];
2722 }
2723
2724 pub fn format(
2725 stab: Stab,
2726 comptime unused_fmt_string: []const u8,
2727 options: std.fmt.FormatOptions,
2728 writer: anytype,
2729 ) !void {
2730 _ = stab;
2731 _ = unused_fmt_string;
2732 _ = options;
2733 _ = writer;
2734 @compileError("do not format stabs directly");
2735 }
22662736
2267 fn getSymbol(stab: Stab, macho_file: *MachO) ?*Symbol {
2268 return if (stab.symbol) |s| macho_file.getSymbol(s) else null;
2737 const StabFormatContext = struct { Stab, Object };
2738
2739 pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(format2) {
2740 return .{ .data = .{ stab, object } };
2741 }
2742
2743 fn format2(
2744 ctx: StabFormatContext,
2745 comptime unused_fmt_string: []const u8,
2746 options: std.fmt.FormatOptions,
2747 writer: anytype,
2748 ) !void {
2749 _ = unused_fmt_string;
2750 _ = options;
2751 const stab, const object = ctx;
2752 const sym = stab.getSymbol(object).?;
2753 if (stab.is_func) {
2754 try writer.print("func({d})", .{stab.index.?});
2755 } else if (sym.visibility == .global) {
2756 try writer.print("gsym({d})", .{stab.index.?});
2757 } else {
2758 try writer.print("stsym({d})", .{stab.index.?});
2759 }
22692760 }
22702761 };
22712762};
......@@ -2274,43 +2765,52 @@ const CompileUnit = struct {
22742765 comp_dir: u32,
22752766 tu_name: u32,
22762767
2277 fn getCompDir(cu: CompileUnit, object: *const Object) [:0]const u8 {
2768 fn getCompDir(cu: CompileUnit, object: Object) [:0]const u8 {
22782769 return object.getString(cu.comp_dir);
22792770 }
22802771
2281 fn getTuName(cu: CompileUnit, object: *const Object) [:0]const u8 {
2772 fn getTuName(cu: CompileUnit, object: Object) [:0]const u8 {
22822773 return object.getString(cu.tu_name);
22832774 }
22842775};
22852776
22862777const InArchive = struct {
22872778 path: []const u8,
2288 offset: u64,
22892779 size: u32,
22902780};
22912781
2782const CompactUnwindCtx = struct {
2783 rec_index: u32 = 0,
2784 rec_count: u32 = 0,
2785 reloc_index: u32 = 0,
2786 reloc_count: u32 = 0,
2787};
2788
22922789const x86_64 = struct {
22932790 fn parseRelocs(
2294 self: *const Object,
2295 n_sect: u8,
2791 self: *Object,
22962792 sect: macho.section_64,
22972793 out: *std.ArrayListUnmanaged(Relocation),
2794 handle: File.Handle,
22982795 macho_file: *MachO,
22992796 ) !void {
23002797 const gpa = macho_file.base.comp.gpa;
23012798
2302 const handle = macho_file.getFileHandle(self.file_handle);
2303 const offset = if (self.archive) |ar| ar.offset else 0;
23042799 const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
23052800 defer gpa.free(relocs_buffer);
23062801 {
2307 const amt = try handle.preadAll(relocs_buffer, sect.reloff + offset);
2802 const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset);
23082803 if (amt != relocs_buffer.len) return error.InputOutput;
23092804 }
23102805 const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];
23112806
2312 const code = try self.getSectionData(@intCast(n_sect), macho_file);
2807 const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
2808 const code = try gpa.alloc(u8, sect_size);
23132809 defer gpa.free(code);
2810 {
2811 const amt = try handle.preadAll(code, sect.offset + self.offset);
2812 if (amt != code.len) return error.InputOutput;
2813 }
23142814
23152815 try out.ensureTotalCapacityPrecise(gpa, relocs.len);
23162816
......@@ -2332,8 +2832,9 @@ const x86_64 = struct {
23322832 .X86_64_RELOC_SIGNED_4 => 4,
23332833 else => 0,
23342834 };
2835 var is_extern = rel.r_extern == 1;
23352836
2336 const target = if (rel.r_extern == 0) blk: {
2837 const target = if (!is_extern) blk: {
23372838 const nsect = rel.r_symbolnum - 1;
23382839 const taddr: i64 = if (rel.r_pcrel == 1)
23392840 @as(i64, @intCast(sect.addr)) + rel.r_address + addend + 4
......@@ -2345,9 +2846,15 @@ const x86_64 = struct {
23452846 });
23462847 return error.MalformedObject;
23472848 };
2348 addend = taddr - @as(i64, @intCast(macho_file.getAtom(target).?.getInputAddress(macho_file)));
2849 const target_atom = self.getAtom(target).?;
2850 addend = taddr - @as(i64, @intCast(target_atom.getInputAddress(macho_file)));
2851 const isec = target_atom.getInputSection(macho_file);
2852 if (isCstringLiteral(isec) or isFixedSizeLiteral(isec) or isPtrLiteral(isec)) {
2853 is_extern = true;
2854 break :blk target_atom.getExtra(macho_file).literal_symbol_index;
2855 }
23492856 break :blk target;
2350 } else self.symbols.items[rel.r_symbolnum];
2857 } else rel.r_symbolnum;
23512858
23522859 const has_subtractor = if (i > 0 and
23532860 @as(macho.reloc_type_x86_64, @enumFromInt(relocs[i - 1].r_type)) == .X86_64_RELOC_SUBTRACTOR)
......@@ -2361,7 +2868,7 @@ const x86_64 = struct {
23612868 break :blk true;
23622869 } else false;
23632870
2364 const @"type": Relocation.Type = validateRelocType(rel, rel_type) catch |err| {
2871 const @"type": Relocation.Type = validateRelocType(rel, rel_type, is_extern) catch |err| {
23652872 switch (err) {
23662873 error.Pcrel => try macho_file.reportParseError2(
23672874 self.index,
......@@ -2388,7 +2895,7 @@ const x86_64 = struct {
23882895 };
23892896
23902897 out.appendAssumeCapacity(.{
2391 .tag = if (rel.r_extern == 1) .@"extern" else .local,
2898 .tag = if (is_extern) .@"extern" else .local,
23922899 .offset = @as(u32, @intCast(rel.r_address)),
23932900 .target = target,
23942901 .addend = addend,
......@@ -2403,7 +2910,7 @@ const x86_64 = struct {
24032910 }
24042911 }
24052912
2406 fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_x86_64) !Relocation.Type {
2913 fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_x86_64, is_extern: bool) !Relocation.Type {
24072914 switch (rel_type) {
24082915 .X86_64_RELOC_UNSIGNED => {
24092916 if (rel.r_pcrel == 1) return error.Pcrel;
......@@ -2423,7 +2930,7 @@ const x86_64 = struct {
24232930 => {
24242931 if (rel.r_pcrel == 0) return error.NonPcrel;
24252932 if (rel.r_length != 2) return error.InvalidLength;
2426 if (rel.r_extern == 0) return error.NonExtern;
2933 if (!is_extern) return error.NonExtern;
24272934 return switch (rel_type) {
24282935 .X86_64_RELOC_BRANCH => .branch,
24292936 .X86_64_RELOC_GOT_LOAD => .got_load,
......@@ -2454,26 +2961,29 @@ const x86_64 = struct {
24542961
24552962const aarch64 = struct {
24562963 fn parseRelocs(
2457 self: *const Object,
2458 n_sect: u8,
2964 self: *Object,
24592965 sect: macho.section_64,
24602966 out: *std.ArrayListUnmanaged(Relocation),
2967 handle: File.Handle,
24612968 macho_file: *MachO,
24622969 ) !void {
24632970 const gpa = macho_file.base.comp.gpa;
24642971
2465 const handle = macho_file.getFileHandle(self.file_handle);
2466 const offset = if (self.archive) |ar| ar.offset else 0;
24672972 const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
24682973 defer gpa.free(relocs_buffer);
24692974 {
2470 const amt = try handle.preadAll(relocs_buffer, sect.reloff + offset);
2975 const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset);
24712976 if (amt != relocs_buffer.len) return error.InputOutput;
24722977 }
24732978 const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];
24742979
2475 const code = try self.getSectionData(@intCast(n_sect), macho_file);
2980 const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
2981 const code = try gpa.alloc(u8, sect_size);
24762982 defer gpa.free(code);
2983 {
2984 const amt = try handle.preadAll(code, sect.offset + self.offset);
2985 if (amt != code.len) return error.InputOutput;
2986 }
24772987
24782988 try out.ensureTotalCapacityPrecise(gpa, relocs.len);
24792989
......@@ -2519,8 +3029,9 @@ const aarch64 = struct {
25193029 }
25203030
25213031 const rel_type: macho.reloc_type_arm64 = @enumFromInt(rel.r_type);
3032 var is_extern = rel.r_extern == 1;
25223033
2523 const target = if (rel.r_extern == 0) blk: {
3034 const target = if (!is_extern) blk: {
25243035 const nsect = rel.r_symbolnum - 1;
25253036 const taddr: i64 = if (rel.r_pcrel == 1)
25263037 @as(i64, @intCast(sect.addr)) + rel.r_address + addend
......@@ -2532,9 +3043,15 @@ const aarch64 = struct {
25323043 });
25333044 return error.MalformedObject;
25343045 };
2535 addend = taddr - @as(i64, @intCast(macho_file.getAtom(target).?.getInputAddress(macho_file)));
3046 const target_atom = self.getAtom(target).?;
3047 addend = taddr - @as(i64, @intCast(target_atom.getInputAddress(macho_file)));
3048 const isec = target_atom.getInputSection(macho_file);
3049 if (isCstringLiteral(isec) or isFixedSizeLiteral(isec) or isPtrLiteral(isec)) {
3050 is_extern = true;
3051 break :blk target_atom.getExtra(macho_file).literal_symbol_index;
3052 }
25363053 break :blk target;
2537 } else self.symbols.items[rel.r_symbolnum];
3054 } else rel.r_symbolnum;
25383055
25393056 const has_subtractor = if (i > 0 and
25403057 @as(macho.reloc_type_arm64, @enumFromInt(relocs[i - 1].r_type)) == .ARM64_RELOC_SUBTRACTOR)
......@@ -2548,7 +3065,7 @@ const aarch64 = struct {
25483065 break :blk true;
25493066 } else false;
25503067
2551 const @"type": Relocation.Type = validateRelocType(rel, rel_type) catch |err| {
3068 const @"type": Relocation.Type = validateRelocType(rel, rel_type, is_extern) catch |err| {
25523069 switch (err) {
25533070 error.Pcrel => try macho_file.reportParseError2(
25543071 self.index,
......@@ -2575,7 +3092,7 @@ const aarch64 = struct {
25753092 };
25763093
25773094 out.appendAssumeCapacity(.{
2578 .tag = if (rel.r_extern == 1) .@"extern" else .local,
3095 .tag = if (is_extern) .@"extern" else .local,
25793096 .offset = @as(u32, @intCast(rel.r_address)),
25803097 .target = target,
25813098 .addend = addend,
......@@ -2590,7 +3107,7 @@ const aarch64 = struct {
25903107 }
25913108 }
25923109
2593 fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_arm64) !Relocation.Type {
3110 fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_arm64, is_extern: bool) !Relocation.Type {
25943111 switch (rel_type) {
25953112 .ARM64_RELOC_UNSIGNED => {
25963113 if (rel.r_pcrel == 1) return error.Pcrel;
......@@ -2611,7 +3128,7 @@ const aarch64 = struct {
26113128 => {
26123129 if (rel.r_pcrel == 0) return error.NonPcrel;
26133130 if (rel.r_length != 2) return error.InvalidLength;
2614 if (rel.r_extern == 0) return error.NonExtern;
3131 if (!is_extern) return error.NonExtern;
26153132 return switch (rel_type) {
26163133 .ARM64_RELOC_BRANCH26 => .branch,
26173134 .ARM64_RELOC_PAGE21 => .page,
......@@ -2628,7 +3145,7 @@ const aarch64 = struct {
26283145 => {
26293146 if (rel.r_pcrel == 1) return error.Pcrel;
26303147 if (rel.r_length != 2) return error.InvalidLength;
2631 if (rel.r_extern == 0) return error.NonExtern;
3148 if (!is_extern) return error.NonExtern;
26323149 return switch (rel_type) {
26333150 .ARM64_RELOC_PAGEOFF12 => .pageoff,
26343151 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got_load_pageoff,
src/link/MachO/Relocation.zig+23-11
......@@ -1,4 +1,4 @@
1tag: enum { @"extern", local },
1tag: Tag,
22offset: u32,
33target: u32,
44addend: i64,
......@@ -10,34 +10,44 @@ meta: packed struct {
1010 symbolnum: u24,
1111},
1212
13pub fn getTargetSymbol(rel: Relocation, macho_file: *MachO) *Symbol {
13pub fn getTargetSymbolRef(rel: Relocation, atom: Atom, macho_file: *MachO) MachO.Ref {
1414 assert(rel.tag == .@"extern");
15 return macho_file.getSymbol(rel.target);
15 return atom.getFile(macho_file).getSymbolRef(rel.target, macho_file);
1616}
1717
18pub fn getTargetAtom(rel: Relocation, macho_file: *MachO) *Atom {
18pub fn getTargetSymbol(rel: Relocation, atom: Atom, macho_file: *MachO) *Symbol {
19 assert(rel.tag == .@"extern");
20 const ref = atom.getFile(macho_file).getSymbolRef(rel.target, macho_file);
21 return ref.getSymbol(macho_file).?;
22}
23
24pub fn getTargetAtom(rel: Relocation, atom: Atom, macho_file: *MachO) *Atom {
1925 assert(rel.tag == .local);
20 return macho_file.getAtom(rel.target).?;
26 return atom.getFile(macho_file).getAtom(rel.target).?;
2127}
2228
23pub fn getTargetAddress(rel: Relocation, macho_file: *MachO) u64 {
29pub fn getTargetAddress(rel: Relocation, atom: Atom, macho_file: *MachO) u64 {
2430 return switch (rel.tag) {
25 .local => rel.getTargetAtom(macho_file).getAddress(macho_file),
26 .@"extern" => rel.getTargetSymbol(macho_file).getAddress(.{}, macho_file),
31 .local => rel.getTargetAtom(atom, macho_file).getAddress(macho_file),
32 .@"extern" => rel.getTargetSymbol(atom, macho_file).getAddress(.{}, macho_file),
2733 };
2834}
2935
30pub fn getGotTargetAddress(rel: Relocation, macho_file: *MachO) u64 {
36pub fn getGotTargetAddress(rel: Relocation, atom: Atom, macho_file: *MachO) u64 {
3137 return switch (rel.tag) {
3238 .local => 0,
33 .@"extern" => rel.getTargetSymbol(macho_file).getGotAddress(macho_file),
39 .@"extern" => rel.getTargetSymbol(atom, macho_file).getGotAddress(macho_file),
3440 };
3541}
3642
3743pub fn getZigGotTargetAddress(rel: Relocation, macho_file: *MachO) u64 {
44 const zo = macho_file.getZigObject() orelse return 0;
3845 return switch (rel.tag) {
3946 .local => 0,
40 .@"extern" => rel.getTargetSymbol(macho_file).getZigGotAddress(macho_file),
47 .@"extern" => {
48 const ref = zo.getSymbolRef(rel.target, macho_file);
49 return ref.getSymbol(macho_file).?.getZigGotAddress(macho_file);
50 },
4151 };
4252}
4353
......@@ -155,6 +165,8 @@ pub const Type = enum {
155165 unsigned,
156166};
157167
168const Tag = enum { local, @"extern" };
169
158170const assert = std.debug.assert;
159171const macho = std.macho;
160172const math = std.math;
src/link/MachO/Symbol.zig+52-37
......@@ -9,17 +9,16 @@ name: u32 = 0,
99/// File where this symbol is defined.
1010file: File.Index = 0,
1111
12/// Atom containing this symbol if any.
13/// Index of 0 means there is no associated atom with this symbol.
12/// Reference to Atom containing this symbol if any.
1413/// Use `getAtom` to get the pointer to the atom.
15atom: Atom.Index = 0,
14atom_ref: MachO.Ref = .{ .index = 0, .file = 0 },
1615
1716/// Assigned output section index for this symbol.
1817out_n_sect: u8 = 0,
1918
2019/// Index of the source nlist this symbol references.
2120/// Use `getNlist` to pull the nlist from the relevant file.
22nlist_idx: Index = 0,
21nlist_idx: u32 = 0,
2322
2423/// Misc flags for the symbol packaged as packed struct for compression.
2524flags: Flags = .{},
......@@ -55,16 +54,19 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {
5554}
5655
5756pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {
58 if (symbol.flags.global) return macho_file.strings.getAssumeExists(symbol.name);
5957 return switch (symbol.getFile(macho_file).?) {
60 .dylib => unreachable, // There are no local symbols for dylibs
6158 .zig_object => |x| x.strtab.getAssumeExists(symbol.name),
6259 inline else => |x| x.getString(symbol.name),
6360 };
6461}
6562
6663pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom {
67 return macho_file.getAtom(symbol.atom);
64 return symbol.atom_ref.getAtom(macho_file);
65}
66
67pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 {
68 if (symbol.getAtom(macho_file)) |atom| return atom.out_n_sect;
69 return symbol.out_n_sect;
6870}
6971
7072pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
......@@ -75,8 +77,10 @@ pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
7577pub fn getNlist(symbol: Symbol, macho_file: *MachO) macho.nlist_64 {
7678 const file = symbol.getFile(macho_file).?;
7779 return switch (file) {
80 .dylib => unreachable,
81 .zig_object => unreachable,
7882 .object => |x| x.symtab.items(.nlist)[symbol.nlist_idx],
79 else => unreachable,
83 .internal => |x| x.symtab.items[symbol.nlist_idx],
8084 };
8185}
8286
......@@ -124,33 +128,35 @@ pub fn getAddress(symbol: Symbol, opts: struct {
124128
125129pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
126130 if (!symbol.flags.has_got) return 0;
127 const extra = symbol.getExtra(macho_file).?;
131 const extra = symbol.getExtra(macho_file);
128132 return macho_file.got.getAddress(extra.got, macho_file);
129133}
130134
131135pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
132136 if (!symbol.flags.stubs) return 0;
133 const extra = symbol.getExtra(macho_file).?;
137 const extra = symbol.getExtra(macho_file);
134138 return macho_file.stubs.getAddress(extra.stubs, macho_file);
135139}
136140
137141pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
138142 if (!symbol.flags.objc_stubs) return 0;
139 const extra = symbol.getExtra(macho_file).?;
143 const extra = symbol.getExtra(macho_file);
140144 return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);
141145}
142146
143147pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
144148 if (!symbol.flags.objc_stubs) return 0;
145 const extra = symbol.getExtra(macho_file).?;
146 const atom = macho_file.getAtom(extra.objc_selrefs).?;
147 assert(atom.flags.alive);
148 return atom.getAddress(macho_file);
149 const extra = symbol.getExtra(macho_file);
150 const file = symbol.getFile(macho_file).?;
151 return switch (file) {
152 .dylib, .zig_object => unreachable,
153 inline else => |x| x.symbols.items[extra.objc_selrefs].getAddress(.{}, macho_file),
154 };
149155}
150156
151157pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
152158 if (!symbol.flags.tlv_ptr) return 0;
153 const extra = symbol.getExtra(macho_file).?;
159 const extra = symbol.getExtra(macho_file);
154160 return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
155161}
156162
......@@ -162,14 +168,14 @@ const GetOrCreateZigGotEntryResult = struct {
162168pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult {
163169 assert(!macho_file.base.isRelocatable());
164170 assert(symbol.flags.needs_zig_got);
165 if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).?.zig_got };
171 if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got };
166172 const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file);
167173 return .{ .found_existing = false, .index = index };
168174}
169175
170176pub fn getZigGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
171177 if (!symbol.flags.has_zig_got) return 0;
172 const extras = symbol.getExtra(macho_file).?;
178 const extras = symbol.getExtra(macho_file);
173179 return macho_file.zig_got.entryAddress(extras.zig_got, macho_file);
174180}
175181
......@@ -180,7 +186,7 @@ pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 {
180186 const symtab_ctx = switch (file) {
181187 inline else => |x| x.output_symtab_ctx,
182188 };
183 var idx = symbol.getExtra(macho_file).?.symtab;
189 var idx = symbol.getExtra(macho_file).symtab;
184190 if (symbol.isLocal()) {
185191 idx += symtab_ctx.ilocal;
186192 } else if (symbol.flags.@"export") {
......@@ -202,11 +208,8 @@ const AddExtraOpts = struct {
202208 symtab: ?u32 = null,
203209};
204210
205pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void {
206 if (symbol.getExtra(macho_file) == null) {
207 symbol.extra = try macho_file.addSymbolExtra(.{});
208 }
209 var extra = symbol.getExtra(macho_file).?;
211pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) void {
212 var extra = symbol.getExtra(macho_file);
210213 inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
211214 if (@field(opts, field.name)) |x| {
212215 @field(extra, field.name) = x;
......@@ -215,18 +218,22 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void {
215218 symbol.setExtra(extra, macho_file);
216219}
217220
218pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) ?Extra {
219 return macho_file.getSymbolExtra(symbol.extra);
221pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) Extra {
222 return switch (symbol.getFile(macho_file).?) {
223 inline else => |x| x.getSymbolExtra(symbol.extra),
224 };
220225}
221226
222227pub inline fn setExtra(symbol: Symbol, extra: Extra, macho_file: *MachO) void {
223 macho_file.setSymbolExtra(symbol.extra, extra);
228 return switch (symbol.getFile(macho_file).?) {
229 inline else => |x| x.setSymbolExtra(symbol.extra, extra),
230 };
224231}
225232
226233pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) void {
227234 if (symbol.isLocal()) {
228235 out.n_type = if (symbol.flags.abs) macho.N_ABS else macho.N_SECT;
229 out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.out_n_sect + 1);
236 out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
230237 out.n_desc = 0;
231238 out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
232239
......@@ -238,7 +245,7 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo
238245 assert(symbol.visibility == .global);
239246 out.n_type = macho.N_EXT;
240247 out.n_type |= if (symbol.flags.abs) macho.N_ABS else macho.N_SECT;
241 out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.out_n_sect + 1);
248 out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
242249 out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
243250 out.n_desc = 0;
244251
......@@ -318,15 +325,20 @@ fn format2(
318325 symbol.getAddress(.{}, ctx.macho_file),
319326 });
320327 if (symbol.getFile(ctx.macho_file)) |file| {
321 if (symbol.out_n_sect != 0) {
322 try writer.print(" : sect({d})", .{symbol.out_n_sect});
328 if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) {
329 try writer.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)});
323330 }
324331 if (symbol.getAtom(ctx.macho_file)) |atom| {
325332 try writer.print(" : atom({d})", .{atom.atom_index});
326333 }
327 var buf: [2]u8 = .{'_'} ** 2;
334 var buf: [3]u8 = .{'_'} ** 3;
328335 if (symbol.flags.@"export") buf[0] = 'E';
329336 if (symbol.flags.import) buf[1] = 'I';
337 switch (symbol.visibility) {
338 .local => buf[2] = 'L',
339 .hidden => buf[2] = 'H',
340 .global => buf[2] = 'G',
341 }
330342 try writer.print(" : {s}", .{&buf});
331343 if (symbol.flags.weak) try writer.writeAll(" : weak");
332344 if (symbol.isSymbolStab(ctx.macho_file)) try writer.writeAll(" : stab");
......@@ -346,11 +358,6 @@ pub const Flags = packed struct {
346358 /// Whether the symbol is exported at runtime.
347359 @"export": bool = false,
348360
349 /// Whether the symbol is effectively an extern and takes part in global
350 /// symbol resolution. Then, its name will be saved in global string interning
351 /// table.
352 global: bool = false,
353
354361 /// Whether this symbol is weak.
355362 weak: bool = false,
356363
......@@ -400,6 +407,14 @@ pub const Visibility = enum {
400407 global,
401408 hidden,
402409 local,
410
411 pub fn rank(vis: Visibility) u2 {
412 return switch (vis) {
413 .local => 2,
414 .hidden => 1,
415 .global => 0,
416 };
417 }
403418};
404419
405420pub const Extra = struct {
src/link/MachO/UnwindInfo.zig+67-45
......@@ -1,10 +1,10 @@
11/// List of all unwind records gathered from all objects and sorted
22/// by allocated relative function address within the section.
3records: std.ArrayListUnmanaged(Record.Index) = .{},
3records: std.ArrayListUnmanaged(Record.Ref) = .{},
44
55/// List of all personalities referenced by either unwind info entries
66/// or __eh_frame entries.
7personalities: [max_personalities]Symbol.Index = undefined,
7personalities: [max_personalities]MachO.Ref = undefined,
88personalities_count: u2 = 0,
99
1010/// List of common encodings sorted in descending order with the most common first.
......@@ -25,10 +25,10 @@ pub fn deinit(info: *UnwindInfo, allocator: Allocator) void {
2525 info.lsdas_lookup.deinit(allocator);
2626}
2727
28fn canFold(macho_file: *MachO, lhs_index: Record.Index, rhs_index: Record.Index) bool {
28fn canFold(macho_file: *MachO, lhs_ref: Record.Ref, rhs_ref: Record.Ref) bool {
2929 const cpu_arch = macho_file.getTarget().cpu.arch;
30 const lhs = macho_file.getUnwindRecord(lhs_index);
31 const rhs = macho_file.getUnwindRecord(rhs_index);
30 const lhs = lhs_ref.getUnwindRecord(macho_file);
31 const rhs = rhs_ref.getUnwindRecord(macho_file);
3232 if (cpu_arch == .x86_64) {
3333 if (lhs.enc.getMode() == @intFromEnum(macho.UNWIND_X86_64_MODE.STACK_IND) or
3434 rhs.enc.getMode() == @intFromEnum(macho.UNWIND_X86_64_MODE.STACK_IND)) return false;
......@@ -42,27 +42,31 @@ fn canFold(macho_file: *MachO, lhs_index: Record.Index, rhs_index: Record.Index)
4242}
4343
4444pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
45 const tracy = trace(@src());
46 defer tracy.end();
47
4548 const gpa = macho_file.base.comp.gpa;
4649
4750 log.debug("generating unwind info", .{});
4851
4952 // Collect all unwind records
5053 for (macho_file.sections.items(.atoms)) |atoms| {
51 for (atoms.items) |atom_index| {
52 const atom = macho_file.getAtom(atom_index) orelse continue;
54 for (atoms.items) |ref| {
55 const atom = ref.getAtom(macho_file) orelse continue;
5356 if (!atom.flags.alive) continue;
5457 const recs = atom.getUnwindRecords(macho_file);
58 const file = atom.getFile(macho_file);
5559 try info.records.ensureUnusedCapacity(gpa, recs.len);
5660 for (recs) |rec| {
57 if (!macho_file.getUnwindRecord(rec).alive) continue;
58 info.records.appendAssumeCapacity(rec);
61 if (!file.object.getUnwindRecord(rec).alive) continue;
62 info.records.appendAssumeCapacity(.{ .record = rec, .file = file.getIndex() });
5963 }
6064 }
6165 }
6266
6367 // Encode records
64 for (info.records.items) |index| {
65 const rec = macho_file.getUnwindRecord(index);
68 for (info.records.items) |ref| {
69 const rec = ref.getUnwindRecord(macho_file);
6670 if (rec.getFde(macho_file)) |fde| {
6771 rec.enc.setDwarfSectionOffset(@intCast(fde.out_offset));
6872 if (fde.getLsdaAtom(macho_file)) |lsda| {
......@@ -72,27 +76,31 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
7276 }
7377 const cie = fde.getCie(macho_file);
7478 if (cie.getPersonality(macho_file)) |_| {
75 const personality_index = try info.getOrPutPersonalityFunction(cie.personality.?.index); // TODO handle error
79 const object = cie.getObject(macho_file);
80 const sym_ref = object.getSymbolRef(cie.personality.?.index, macho_file);
81 const personality_index = try info.getOrPutPersonalityFunction(sym_ref); // TODO handle error
7682 rec.enc.setPersonalityIndex(personality_index + 1);
7783 }
7884 } else if (rec.getPersonality(macho_file)) |_| {
79 const personality_index = try info.getOrPutPersonalityFunction(rec.personality.?); // TODO handle error
85 const object = rec.getObject(macho_file);
86 const sym_ref = object.getSymbolRef(rec.personality.?, macho_file);
87 const personality_index = try info.getOrPutPersonalityFunction(sym_ref); // TODO handle error
8088 rec.enc.setPersonalityIndex(personality_index + 1);
8189 }
8290 }
8391
8492 // Sort by assigned relative address within each output section
8593 const sortFn = struct {
86 fn sortFn(ctx: *MachO, lhs_index: Record.Index, rhs_index: Record.Index) bool {
87 const lhs = ctx.getUnwindRecord(lhs_index);
88 const rhs = ctx.getUnwindRecord(rhs_index);
94 fn sortFn(ctx: *MachO, lhs_ref: Record.Ref, rhs_ref: Record.Ref) bool {
95 const lhs = lhs_ref.getUnwindRecord(ctx);
96 const rhs = rhs_ref.getUnwindRecord(ctx);
8997 const lhsa = lhs.getAtom(ctx);
9098 const rhsa = rhs.getAtom(ctx);
9199 if (lhsa.out_n_sect == rhsa.out_n_sect) return lhs.getAtomAddress(ctx) < rhs.getAtomAddress(ctx);
92100 return lhsa.out_n_sect < rhsa.out_n_sect;
93101 }
94102 }.sortFn;
95 mem.sort(Record.Index, info.records.items, macho_file, sortFn);
103 mem.sort(Record.Ref, info.records.items, macho_file, sortFn);
96104
97105 // Fold the records
98106 // Any adjacent two records that share encoding can be folded into one.
......@@ -101,8 +109,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
101109 var j: usize = 1;
102110 while (j < info.records.items.len) : (j += 1) {
103111 if (canFold(macho_file, info.records.items[i], info.records.items[j])) {
104 const rec = macho_file.getUnwindRecord(info.records.items[i]);
105 rec.length += macho_file.getUnwindRecord(info.records.items[j]).length + 1;
112 const rec = info.records.items[i].getUnwindRecord(macho_file);
113 rec.length += info.records.items[j].getUnwindRecord(macho_file).length + 1;
106114 } else {
107115 i += 1;
108116 info.records.items[i] = info.records.items[j];
......@@ -111,14 +119,15 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
111119 info.records.shrinkAndFree(gpa, i + 1);
112120 }
113121
114 for (info.records.items) |rec_index| {
115 const rec = macho_file.getUnwindRecord(rec_index);
122 for (info.records.items) |ref| {
123 const rec = ref.getUnwindRecord(macho_file);
116124 const atom = rec.getAtom(macho_file);
117 log.debug("@{x}-{x} : {s} : rec({d}) : {}", .{
125 log.debug("@{x}-{x} : {s} : rec({d}) : object({d}) : {}", .{
118126 rec.getAtomAddress(macho_file),
119127 rec.getAtomAddress(macho_file) + rec.length,
120128 atom.getName(macho_file),
121 rec_index,
129 ref.record,
130 ref.file,
122131 rec.enc,
123132 });
124133 }
......@@ -161,8 +170,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
161170 ).init(gpa);
162171 defer common_encodings_counts.deinit();
163172
164 for (info.records.items) |rec_index| {
165 const rec = macho_file.getUnwindRecord(rec_index);
173 for (info.records.items) |ref| {
174 const rec = ref.getUnwindRecord(macho_file);
166175 if (rec.enc.isDwarf(macho_file)) continue;
167176 const gop = try common_encodings_counts.getOrPut(rec.enc);
168177 if (!gop.found_existing) {
......@@ -190,7 +199,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
190199 {
191200 var i: u32 = 0;
192201 while (i < info.records.items.len) {
193 const rec = macho_file.getUnwindRecord(info.records.items[i]);
202 const rec = info.records.items[i].getUnwindRecord(macho_file);
194203 const range_start_max: u64 = rec.getAtomAddress(macho_file) + compressed_entry_func_offset_mask;
195204 var encoding_count: u9 = info.common_encodings_count;
196205 var space_left: u32 = second_level_page_words -
......@@ -202,7 +211,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
202211 };
203212
204213 while (space_left >= 1 and i < info.records.items.len) {
205 const next = macho_file.getUnwindRecord(info.records.items[i]);
214 const next = info.records.items[i].getUnwindRecord(macho_file);
206215 const is_dwarf = next.enc.isDwarf(macho_file);
207216
208217 if (next.getAtomAddress(macho_file) >= range_start_max) {
......@@ -244,8 +253,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
244253 // Save records having an LSDA pointer
245254 log.debug("LSDA pointers:", .{});
246255 try info.lsdas_lookup.ensureTotalCapacityPrecise(gpa, info.records.items.len);
247 for (info.records.items, 0..) |index, i| {
248 const rec = macho_file.getUnwindRecord(index);
256 for (info.records.items, 0..) |ref, i| {
257 const rec = ref.getUnwindRecord(macho_file);
249258 info.lsdas_lookup.appendAssumeCapacity(@intCast(info.lsdas.items.len));
250259 if (rec.getLsdaAtom(macho_file)) |lsda| {
251260 log.debug(" @{x} => lsda({d})", .{ rec.getAtomAddress(macho_file), lsda.atom_index });
......@@ -255,6 +264,9 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
255264}
256265
257266pub fn calcSize(info: UnwindInfo) usize {
267 const tracy = trace(@src());
268 defer tracy.end();
269
258270 var total_size: usize = 0;
259271 total_size += @sizeOf(macho.unwind_info_section_header);
260272 total_size +=
......@@ -291,8 +303,8 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
291303
292304 try writer.writeAll(mem.sliceAsBytes(info.common_encodings[0..info.common_encodings_count]));
293305
294 for (info.personalities[0..info.personalities_count]) |sym_index| {
295 const sym = macho_file.getSymbol(sym_index);
306 for (info.personalities[0..info.personalities_count]) |ref| {
307 const sym = ref.getSymbol(macho_file).?;
296308 try writer.writeInt(u32, @intCast(sym.getGotAddress(macho_file) - seg.vmaddr), .little);
297309 }
298310
......@@ -301,7 +313,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
301313 (info.lsdas.items.len * @sizeOf(macho.unwind_info_section_header_lsda_index_entry))));
302314 for (info.pages.items, 0..) |page, i| {
303315 assert(page.count > 0);
304 const rec = macho_file.getUnwindRecord(info.records.items[page.start]);
316 const rec = info.records.items[page.start].getUnwindRecord(macho_file);
305317 try writer.writeStruct(macho.unwind_info_section_header_index_entry{
306318 .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),
307319 .secondLevelPagesSectionOffset = @as(u32, @intCast(pages_base_offset + i * second_level_page_bytes)),
......@@ -310,7 +322,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
310322 });
311323 }
312324
313 const last_rec = macho_file.getUnwindRecord(info.records.items[info.records.items.len - 1]);
325 const last_rec = info.records.items[info.records.items.len - 1].getUnwindRecord(macho_file);
314326 const sentinel_address = @as(u32, @intCast(last_rec.getAtomAddress(macho_file) + last_rec.length - seg.vmaddr));
315327 try writer.writeStruct(macho.unwind_info_section_header_index_entry{
316328 .functionOffset = sentinel_address,
......@@ -320,7 +332,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
320332 });
321333
322334 for (info.lsdas.items) |index| {
323 const rec = macho_file.getUnwindRecord(info.records.items[index]);
335 const rec = info.records.items[index].getUnwindRecord(macho_file);
324336 try writer.writeStruct(macho.unwind_info_section_header_lsda_index_entry{
325337 .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),
326338 .lsdaOffset = @as(u32, @intCast(rec.getLsdaAddress(macho_file) - seg.vmaddr)),
......@@ -340,13 +352,13 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
340352 @memset(buffer[stream.pos..], 0);
341353}
342354
343fn getOrPutPersonalityFunction(info: *UnwindInfo, sym_index: Symbol.Index) error{TooManyPersonalities}!u2 {
355fn getOrPutPersonalityFunction(info: *UnwindInfo, ref: MachO.Ref) error{TooManyPersonalities}!u2 {
344356 comptime var index: u2 = 0;
345357 inline while (index < max_personalities) : (index += 1) {
346 if (info.personalities[index] == sym_index) {
358 if (info.personalities[index].eql(ref)) {
347359 return index;
348360 } else if (index == info.personalities_count) {
349 info.personalities[index] = sym_index;
361 info.personalities[index] = ref;
350362 info.personalities_count += 1;
351363 return index;
352364 }
......@@ -461,16 +473,17 @@ pub const Record = struct {
461473 }
462474
463475 pub fn getAtom(rec: Record, macho_file: *MachO) *Atom {
464 return macho_file.getAtom(rec.atom).?;
476 return rec.getObject(macho_file).getAtom(rec.atom).?;
465477 }
466478
467479 pub fn getLsdaAtom(rec: Record, macho_file: *MachO) ?*Atom {
468 return macho_file.getAtom(rec.lsda);
480 return rec.getObject(macho_file).getAtom(rec.lsda);
469481 }
470482
471483 pub fn getPersonality(rec: Record, macho_file: *MachO) ?*Symbol {
472484 const personality = rec.personality orelse return null;
473 return macho_file.getSymbol(personality);
485 const object = rec.getObject(macho_file);
486 return object.getSymbolRef(personality, macho_file).getSymbol(macho_file);
474487 }
475488
476489 pub fn getFde(rec: Record, macho_file: *MachO) ?Fde {
......@@ -537,6 +550,15 @@ pub const Record = struct {
537550 }
538551
539552 pub const Index = u32;
553
554 const Ref = struct {
555 record: Index,
556 file: File.Index,
557
558 pub fn getUnwindRecord(ref: Ref, macho_file: *MachO) *Record {
559 return macho_file.getFile(ref.file).?.object.getUnwindRecord(ref.record);
560 }
561 };
540562};
541563
542564const max_personalities = 3;
......@@ -635,8 +657,8 @@ const Page = struct {
635657 .entryCount = page.count,
636658 });
637659
638 for (info.records.items[page.start..][0..page.count]) |index| {
639 const rec = macho_file.getUnwindRecord(index);
660 for (info.records.items[page.start..][0..page.count]) |ref| {
661 const rec = ref.getUnwindRecord(macho_file);
640662 try writer.writeStruct(macho.unwind_info_regular_second_level_entry{
641663 .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),
642664 .encoding = rec.enc.enc,
......@@ -658,9 +680,9 @@ const Page = struct {
658680 }
659681
660682 assert(page.count > 0);
661 const first_rec = macho_file.getUnwindRecord(info.records.items[page.start]);
662 for (info.records.items[page.start..][0..page.count]) |index| {
663 const rec = macho_file.getUnwindRecord(index);
683 const first_rec = info.records.items[page.start].getUnwindRecord(macho_file);
684 for (info.records.items[page.start..][0..page.count]) |ref| {
685 const rec = ref.getUnwindRecord(macho_file);
664686 const enc_index = blk: {
665687 if (info.getCommonEncoding(rec.enc)) |id| break :blk id;
666688 const ncommon = info.common_encodings_count;
src/link/MachO/ZigObject.zig+522-230
......@@ -6,9 +6,15 @@ index: File.Index,
66symtab: std.MultiArrayList(Nlist) = .{},
77strtab: StringTable = .{},
88
9symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
10atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
11globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
9symbols: std.ArrayListUnmanaged(Symbol) = .{},
10symbols_extra: std.ArrayListUnmanaged(u32) = .{},
11globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
12/// Maps string index (so name) into nlist index for the global symbol defined within this
13/// module.
14globals_lookup: std.AutoHashMapUnmanaged(u32, u32) = .{},
15atoms: std.ArrayListUnmanaged(Atom) = .{},
16atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
17atoms_extra: std.ArrayListUnmanaged(u32) = .{},
1218
1319/// Table of tracked LazySymbols.
1420lazy_syms: LazySymbolTable = .{},
......@@ -48,7 +54,6 @@ relocs: RelocationTable = .{},
4854
4955dwarf: ?Dwarf = null,
5056
51dynamic_relocs: MachO.DynamicRelocs = .{},
5257output_symtab_ctx: MachO.SymtabCtx = .{},
5358output_ar_state: Archive.ArState = .{},
5459
......@@ -59,10 +64,13 @@ debug_info_header_dirty: bool = false,
5964debug_line_header_dirty: bool = false,
6065
6166pub fn init(self: *ZigObject, macho_file: *MachO) !void {
67 const tracy = trace(@src());
68 defer tracy.end();
69
6270 const comp = macho_file.base.comp;
6371 const gpa = comp.gpa;
6472
65 try self.atoms.append(gpa, 0); // null input section
73 try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) }); // null input section
6674 try self.strtab.buffer.append(gpa, 0);
6775
6876 switch (comp.config.debug_format) {
......@@ -85,8 +93,12 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
8593 self.symtab.deinit(allocator);
8694 self.strtab.deinit(allocator);
8795 self.symbols.deinit(allocator);
88 self.atoms.deinit(allocator);
96 self.symbols_extra.deinit(allocator);
97 self.globals.deinit(allocator);
8998 self.globals_lookup.deinit(allocator);
99 self.atoms.deinit(allocator);
100 self.atoms_indexes.deinit(allocator);
101 self.atoms_extra.deinit(allocator);
90102
91103 {
92104 var it = self.decls.iterator();
......@@ -129,53 +141,73 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
129141 }
130142}
131143
132fn addNlist(self: *ZigObject, allocator: Allocator) !Symbol.Index {
144fn newSymbol(self: *ZigObject, allocator: Allocator, name: u32, args: struct {
145 type: u8 = macho.N_UNDF | macho.N_EXT,
146 desc: u16 = 0,
147}) !Symbol.Index {
133148 try self.symtab.ensureUnusedCapacity(allocator, 1);
134 const index = @as(Symbol.Index, @intCast(self.symtab.addOneAssumeCapacity()));
135 self.symtab.set(index, .{
136 .nlist = MachO.null_sym,
149 try self.symbols.ensureUnusedCapacity(allocator, 1);
150 try self.symbols_extra.ensureUnusedCapacity(allocator, @sizeOf(Symbol.Extra));
151 try self.globals.ensureUnusedCapacity(allocator, 1);
152
153 const index = self.addSymbolAssumeCapacity();
154 const symbol = &self.symbols.items[index];
155 symbol.name = name;
156 symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
157
158 const nlist_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity());
159 self.symtab.set(nlist_idx, .{
160 .nlist = .{
161 .n_strx = name,
162 .n_type = args.type,
163 .n_sect = 0,
164 .n_desc = args.desc,
165 .n_value = 0,
166 },
137167 .size = 0,
138168 .atom = 0,
139169 });
140 return index;
141}
142
143pub fn addAtom(self: *ZigObject, macho_file: *MachO) !Symbol.Index {
144 const gpa = macho_file.base.comp.gpa;
145 const atom_index = try macho_file.addAtom();
146 const symbol_index = try macho_file.addSymbol();
147 const nlist_index = try self.addNlist(gpa);
170 symbol.nlist_idx = nlist_idx;
148171
149 try self.atoms.append(gpa, atom_index);
150 try self.symbols.append(gpa, symbol_index);
172 self.globals.appendAssumeCapacity(0);
151173
152 const atom = macho_file.getAtom(atom_index).?;
153 atom.file = self.index;
154 atom.atom_index = atom_index;
174 return index;
175}
155176
156 const symbol = macho_file.getSymbol(symbol_index);
157 symbol.file = self.index;
158 symbol.atom = atom_index;
177fn newAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO) !Atom.Index {
178 try self.atoms.ensureUnusedCapacity(allocator, 1);
179 try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
180 try self.atoms_indexes.ensureUnusedCapacity(allocator, 1);
181 try self.relocs.ensureUnusedCapacity(allocator, 1);
159182
160 self.symtab.items(.atom)[nlist_index] = atom_index;
161 symbol.nlist_idx = nlist_index;
183 const index = self.addAtomAssumeCapacity();
184 self.atoms_indexes.appendAssumeCapacity(index);
185 const atom = self.getAtom(index).?;
186 atom.name = name;
162187
163188 const relocs_index = @as(u32, @intCast(self.relocs.items.len));
164 const relocs = try self.relocs.addOne(gpa);
165 relocs.* = .{};
166 try atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file);
167 atom.flags.relocs = true;
189 self.relocs.addOneAssumeCapacity().* = .{};
190 atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file);
168191
169 return symbol_index;
192 return index;
193}
194
195fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO) !Symbol.Index {
196 const atom_index = try self.newAtom(allocator, name, macho_file);
197 const sym_index = try self.newSymbol(allocator, name, .{ .type = macho.N_SECT });
198 const sym = &self.symbols.items[sym_index];
199 sym.atom_ref = .{ .index = atom_index, .file = self.index };
200 self.symtab.items(.atom)[sym.nlist_idx] = atom_index;
201 return sym_index;
170202}
171203
172204pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8) !void {
173205 assert(atom.file == self.index);
174206 assert(atom.size == buffer.len);
175 const sect = macho_file.sections.items(.header)[atom.out_n_sect];
176 assert(!sect.isZerofill());
207 const isec = atom.getInputSection(macho_file);
208 assert(!isec.isZerofill());
177209
178 switch (sect.type()) {
210 switch (isec.type()) {
179211 macho.S_THREAD_LOCAL_REGULAR => {
180212 const tlv = self.tlv_initializers.get(atom.atom_index).?;
181213 @memcpy(buffer, tlv.data);
......@@ -184,6 +216,7 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8
184216 @memset(buffer, 0);
185217 },
186218 else => {
219 const sect = macho_file.sections.items(.header)[atom.out_n_sect];
187220 const file_offset = sect.offset + atom.value;
188221 const amt = try macho_file.base.file.?.preadAll(buffer, file_offset);
189222 if (amt != buffer.len) return error.InputOutput;
......@@ -192,102 +225,65 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8
192225}
193226
194227pub fn getAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) []const Relocation {
195 if (!atom.flags.relocs) return &[0]Relocation{};
196 const extra = atom.getExtra(macho_file).?;
228 const extra = atom.getExtra(macho_file);
197229 const relocs = self.relocs.items[extra.rel_index];
198230 return relocs.items[0..extra.rel_count];
199231}
200232
201233pub fn freeAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) void {
202 if (atom.flags.relocs) {
203 const extra = atom.getExtra(macho_file).?;
204 self.relocs.items[extra.rel_index].clearRetainingCapacity();
205 }
234 const extra = atom.getExtra(macho_file);
235 self.relocs.items[extra.rel_index].clearRetainingCapacity();
206236}
207237
208pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) void {
238pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) !void {
209239 const tracy = trace(@src());
210240 defer tracy.end();
211241
212 for (self.symbols.items, 0..) |index, i| {
213 const nlist_idx = @as(Symbol.Index, @intCast(i));
214 const nlist = self.symtab.items(.nlist)[nlist_idx];
215 const atom_index = self.symtab.items(.atom)[nlist_idx];
242 const gpa = macho_file.base.comp.gpa;
216243
244 for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| {
217245 if (!nlist.ext()) continue;
218 if (nlist.undf() and !nlist.tentative()) continue;
219246 if (nlist.sect()) {
220 const atom = macho_file.getAtom(atom_index).?;
247 const atom = self.getAtom(atom_index).?;
221248 if (!atom.flags.alive) continue;
222249 }
223250
224 const symbol = macho_file.getSymbol(index);
251 const gop = try macho_file.resolver.getOrPut(gpa, .{
252 .index = @intCast(i),
253 .file = self.index,
254 }, macho_file);
255 if (!gop.found_existing) {
256 gop.ref.* = .{ .index = 0, .file = 0 };
257 }
258 global.* = gop.index;
259
260 if (nlist.undf() and !nlist.tentative()) continue;
261 if (gop.ref.getFile(macho_file) == null) {
262 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
263 continue;
264 }
265
225266 if (self.asFile().getSymbolRank(.{
226267 .archive = false,
227268 .weak = nlist.weakDef(),
228269 .tentative = nlist.tentative(),
229 }) < symbol.getSymbolRank(macho_file)) {
230 const value = if (nlist.sect()) blk: {
231 const atom = macho_file.getAtom(atom_index).?;
232 break :blk nlist.n_value - atom.getInputAddress(macho_file);
233 } else nlist.n_value;
234 const out_n_sect = if (nlist.sect()) macho_file.getAtom(atom_index).?.out_n_sect else 0;
235 symbol.value = value;
236 symbol.atom = atom_index;
237 symbol.out_n_sect = out_n_sect;
238 symbol.nlist_idx = nlist_idx;
239 symbol.file = self.index;
240 symbol.flags.weak = nlist.weakDef();
241 symbol.flags.abs = nlist.abs();
242 symbol.flags.tentative = nlist.tentative();
243 symbol.flags.weak_ref = false;
244 symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
245 symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
246 // TODO: symbol.flags.interposable = macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext();
247 symbol.flags.interposable = false;
248
249 if (nlist.sect() and
250 macho_file.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
251 {
252 symbol.flags.tlv = true;
253 }
254 }
255
256 // Regardless of who the winner is, we still merge symbol visibility here.
257 if (nlist.pext() or (nlist.weakDef() and nlist.weakRef())) {
258 if (symbol.visibility != .global) {
259 symbol.visibility = .hidden;
260 }
261 } else {
262 symbol.visibility = .global;
270 }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
271 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
263272 }
264273 }
265274}
266275
267pub fn resetGlobals(self: *ZigObject, macho_file: *MachO) void {
268 for (self.symbols.items, 0..) |sym_index, nlist_idx| {
269 if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue;
270 const sym = macho_file.getSymbol(sym_index);
271 const name = sym.name;
272 const global = sym.flags.global;
273 const weak_ref = sym.flags.weak_ref;
274 sym.* = .{};
275 sym.name = name;
276 sym.flags.global = global;
277 sym.flags.weak_ref = weak_ref;
278 }
279}
280
281276pub fn markLive(self: *ZigObject, macho_file: *MachO) void {
282277 const tracy = trace(@src());
283278 defer tracy.end();
284279
285 for (self.symbols.items, 0..) |index, nlist_idx| {
286 const nlist = self.symtab.items(.nlist)[nlist_idx];
280 for (0..self.symbols.items.len) |i| {
281 const nlist = self.symtab.items(.nlist)[i];
287282 if (!nlist.ext()) continue;
288283
289 const sym = macho_file.getSymbol(index);
290 const file = sym.getFile(macho_file) orelse continue;
284 const ref = self.getSymbolRef(@intCast(i), macho_file);
285 const file = ref.getFile(macho_file) orelse continue;
286 const sym = ref.getSymbol(macho_file).?;
291287 const should_keep = nlist.undf() or (nlist.tentative() and !sym.flags.tentative);
292288 if (should_keep and file == .object and !file.object.alive) {
293289 file.object.alive = true;
......@@ -296,20 +292,18 @@ pub fn markLive(self: *ZigObject, macho_file: *MachO) void {
296292 }
297293}
298294
299pub fn checkDuplicates(self: *ZigObject, dupes: anytype, macho_file: *MachO) !void {
300 for (self.symbols.items, 0..) |index, nlist_idx| {
301 const sym = macho_file.getSymbol(index);
302 if (sym.visibility != .global) continue;
303 const file = sym.getFile(macho_file) orelse continue;
304 if (file.getIndex() == self.index) continue;
295pub fn mergeSymbolVisibility(self: *ZigObject, macho_file: *MachO) void {
296 const tracy = trace(@src());
297 defer tracy.end();
305298
306 const nlist = self.symtab.items(.nlist)[nlist_idx];
307 if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) {
308 const gop = try dupes.getOrPut(index);
309 if (!gop.found_existing) {
310 gop.value_ptr.* = .{};
311 }
312 try gop.value_ptr.append(macho_file.base.comp.gpa, self.index);
299 for (self.symbols.items, 0..) |sym, i| {
300 const ref = self.getSymbolRef(@intCast(i), macho_file);
301 const global = ref.getSymbol(macho_file) orelse continue;
302 if (sym.visibility.rank() < global.visibility.rank()) {
303 global.visibility = sym.visibility;
304 }
305 if (sym.flags.weak_ref) {
306 global.flags.weak_ref = true;
313307 }
314308 }
315309}
......@@ -331,7 +325,9 @@ pub fn dedupLiterals(self: *ZigObject, lp: MachO.LiteralPool, macho_file: *MachO
331325/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
332326/// We need this so that we can write to an archive.
333327/// TODO implement writing ZigObject data directly to a buffer instead.
334pub fn readFileContents(self: *ZigObject, size: usize, macho_file: *MachO) !void {
328pub fn readFileContents(self: *ZigObject, macho_file: *MachO) !void {
329 // Size of the output object file is always the offset + size of the strtab
330 const size = macho_file.symtab_cmd.stroff + macho_file.symtab_cmd.strsize;
335331 const gpa = macho_file.base.comp.gpa;
336332 try self.data.resize(gpa, size);
337333 const amt = try macho_file.base.file.?.preadAll(self.data.items, 0);
......@@ -340,9 +336,9 @@ pub fn readFileContents(self: *ZigObject, size: usize, macho_file: *MachO) !void
340336
341337pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {
342338 const gpa = macho_file.base.comp.gpa;
343 for (self.symbols.items) |sym_index| {
344 const sym = macho_file.getSymbol(sym_index);
345 const file = sym.getFile(macho_file).?;
339 for (self.symbols.items, 0..) |sym, i| {
340 const ref = self.getSymbolRef(@intCast(i), macho_file);
341 const file = ref.getFile(macho_file).?;
346342 assert(file.getIndex() == self.index);
347343 if (!sym.flags.@"export") continue;
348344 const off = try ar_symtab.strtab.insert(gpa, sym.getName(macho_file));
......@@ -362,9 +358,39 @@ pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !voi
362358 try writer.writeAll(self.data.items);
363359}
364360
361pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void {
362 const tracy = trace(@src());
363 defer tracy.end();
364
365 for (self.symbols.items, 0..) |*sym, i| {
366 const nlist = self.symtab.items(.nlist)[i];
367 if (!nlist.ext()) continue;
368 if (!nlist.undf()) continue;
369
370 if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
371
372 const is_import = switch (macho_file.undefined_treatment) {
373 .@"error" => false,
374 .warn, .suppress => nlist.weakRef(),
375 .dynamic_lookup => true,
376 };
377 if (is_import) {
378 sym.value = 0;
379 sym.atom_ref = .{ .index = 0, .file = 0 };
380 sym.flags.weak = false;
381 sym.flags.weak_ref = nlist.weakRef();
382 sym.flags.import = is_import;
383 sym.visibility = .global;
384
385 const idx = self.globals.items[i];
386 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
387 }
388 }
389}
390
365391pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
366 for (self.atoms.items) |atom_index| {
367 const atom = macho_file.getAtom(atom_index) orelse continue;
392 for (self.getAtoms()) |atom_index| {
393 const atom = self.getAtom(atom_index) orelse continue;
368394 if (!atom.flags.alive) continue;
369395 const sect = atom.getInputSection(macho_file);
370396 if (sect.isZerofill()) continue;
......@@ -372,25 +398,168 @@ pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
372398 }
373399}
374400
375pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) !void {
401pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
402 const gpa = macho_file.base.comp.gpa;
403 var has_error = false;
404 for (self.getAtoms()) |atom_index| {
405 const atom = self.getAtom(atom_index) orelse continue;
406 if (!atom.flags.alive) continue;
407 const sect = &macho_file.sections.items(.header)[atom.out_n_sect];
408 if (sect.isZerofill()) continue;
409 if (!macho_file.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately
410 if (atom.getRelocs(macho_file).len == 0) continue;
411 // TODO: we will resolve and write ZigObject's TLS data twice:
412 // once here, and once in writeAtoms
413 const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow;
414 const code = try gpa.alloc(u8, atom_size);
415 defer gpa.free(code);
416 self.getAtomData(macho_file, atom.*, code) catch |err| {
417 switch (err) {
418 error.InputOutput => {
419 try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
420 atom.getName(macho_file),
421 });
422 },
423 else => |e| {
424 try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
425 atom.getName(macho_file),
426 @errorName(e),
427 });
428 },
429 }
430 has_error = true;
431 continue;
432 };
433 const file_offset = sect.offset + atom.value;
434 atom.resolveRelocs(macho_file, code) catch |err| {
435 switch (err) {
436 error.ResolveFailed => {},
437 else => |e| {
438 try macho_file.reportUnexpectedError("unexpected error while resolving relocations: {s}", .{@errorName(e)});
439 },
440 }
441 has_error = true;
442 continue;
443 };
444 try macho_file.base.file.?.pwriteAll(code, file_offset);
445 }
446
447 if (has_error) return error.ResolveFailed;
448}
449
450pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {
451 for (self.getAtoms()) |atom_index| {
452 const atom = self.getAtom(atom_index) orelse continue;
453 if (!atom.flags.alive) continue;
454 const header = &macho_file.sections.items(.header)[atom.out_n_sect];
455 if (header.isZerofill()) continue;
456 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
457 const nreloc = atom.calcNumRelocs(macho_file);
458 atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file);
459 header.nreloc += nreloc;
460 }
461}
462
463pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {
464 const gpa = macho_file.base.comp.gpa;
465
466 for (self.getAtoms()) |atom_index| {
467 const atom = self.getAtom(atom_index) orelse continue;
468 if (!atom.flags.alive) continue;
469 const header = macho_file.sections.items(.header)[atom.out_n_sect];
470 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
471 if (header.isZerofill()) continue;
472 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
473 if (atom.getRelocs(macho_file).len == 0) continue;
474 const extra = atom.getExtra(macho_file);
475 const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow;
476 const code = try gpa.alloc(u8, atom_size);
477 defer gpa.free(code);
478 self.getAtomData(macho_file, atom.*, code) catch |err| switch (err) {
479 error.InputOutput => {
480 try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
481 atom.getName(macho_file),
482 });
483 return error.FlushFailure;
484 },
485 else => |e| {
486 try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
487 atom.getName(macho_file),
488 @errorName(e),
489 });
490 return error.FlushFailure;
491 },
492 };
493 const file_offset = header.offset + atom.value;
494 try atom.writeRelocs(macho_file, code, relocs[extra.rel_out_index..][0..extra.rel_out_count]);
495 try macho_file.base.file.?.pwriteAll(code, file_offset);
496 }
497}
498
499// TODO we need this because not everything gets written out incrementally.
500// For example, TLS data gets written out via traditional route.
501// Is there any better way of handling this?
502pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void {
503 const tracy = trace(@src());
504 defer tracy.end();
505
506 for (self.getAtoms()) |atom_index| {
507 const atom = self.getAtom(atom_index) orelse continue;
508 if (!atom.flags.alive) continue;
509 const sect = atom.getInputSection(macho_file);
510 if (sect.isZerofill()) continue;
511 if (macho_file.isZigSection(atom.out_n_sect)) continue;
512 if (atom.getRelocs(macho_file).len == 0) continue;
513 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
514 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
515 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
516 try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
517 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
518 const extra = atom.getExtra(macho_file);
519 try atom.writeRelocs(macho_file, buffer[off..][0..size], relocs[extra.rel_out_index..][0..extra.rel_out_count]);
520 }
521}
522
523// TODO we need this because not everything gets written out incrementally.
524// For example, TLS data gets written out via traditional route.
525// Is there any better way of handling this?
526pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void {
527 const tracy = trace(@src());
528 defer tracy.end();
529
530 for (self.getAtoms()) |atom_index| {
531 const atom = self.getAtom(atom_index) orelse continue;
532 if (!atom.flags.alive) continue;
533 const sect = atom.getInputSection(macho_file);
534 if (sect.isZerofill()) continue;
535 if (macho_file.isZigSection(atom.out_n_sect)) continue;
536 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
537 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
538 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
539 try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
540 try atom.resolveRelocs(macho_file, buffer[off..][0..size]);
541 }
542}
543
544pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void {
376545 const tracy = trace(@src());
377546 defer tracy.end();
378547
379 for (self.symbols.items) |sym_index| {
380 const sym = macho_file.getSymbol(sym_index);
381 const file = sym.getFile(macho_file) orelse continue;
548 for (self.symbols.items, 0..) |*sym, i| {
549 const ref = self.getSymbolRef(@intCast(i), macho_file);
550 const file = ref.getFile(macho_file) orelse continue;
382551 if (file.getIndex() != self.index) continue;
383552 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
384553 sym.flags.output_symtab = true;
385554 if (sym.isLocal()) {
386 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
555 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
387556 self.output_symtab_ctx.nlocals += 1;
388557 } else if (sym.flags.@"export") {
389 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
558 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
390559 self.output_symtab_ctx.nexports += 1;
391560 } else {
392561 assert(sym.flags.import);
393 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
562 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
394563 self.output_symtab_ctx.nimports += 1;
395564 }
396565 self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
......@@ -401,17 +570,20 @@ pub fn writeSymtab(self: ZigObject, macho_file: *MachO, ctx: anytype) void {
401570 const tracy = trace(@src());
402571 defer tracy.end();
403572
404 for (self.symbols.items) |sym_index| {
405 const sym = macho_file.getSymbol(sym_index);
406 const file = sym.getFile(macho_file) orelse continue;
573 var n_strx = self.output_symtab_ctx.stroff;
574 for (self.symbols.items, 0..) |sym, i| {
575 const ref = self.getSymbolRef(@intCast(i), macho_file);
576 const file = ref.getFile(macho_file) orelse continue;
407577 if (file.getIndex() != self.index) continue;
408578 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
409 const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
410 ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file));
411 ctx.strtab.appendAssumeCapacity(0);
412579 const out_sym = &ctx.symtab.items[idx];
413580 out_sym.n_strx = n_strx;
414581 sym.setOutputSym(macho_file, out_sym);
582 const name = sym.getName(macho_file);
583 @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
584 n_strx += @intCast(name.len);
585 ctx.strtab.items[n_strx] = 0;
586 n_strx += 1;
415587 }
416588}
417589
......@@ -524,9 +696,9 @@ pub fn getDeclVAddr(
524696 reloc_info: link.File.RelocInfo,
525697) !u64 {
526698 const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index);
527 const sym = macho_file.getSymbol(sym_index);
699 const sym = self.symbols.items[sym_index];
528700 const vaddr = sym.getAddress(.{}, macho_file);
529 const parent_atom = macho_file.getSymbol(reloc_info.parent_atom_index).getAtom(macho_file).?;
701 const parent_atom = self.symbols.items[reloc_info.parent_atom_index].getAtom(macho_file).?;
530702 try parent_atom.addReloc(macho_file, .{
531703 .tag = .@"extern",
532704 .offset = @intCast(reloc_info.offset),
......@@ -550,9 +722,9 @@ pub fn getAnonDeclVAddr(
550722 reloc_info: link.File.RelocInfo,
551723) !u64 {
552724 const sym_index = self.anon_decls.get(decl_val).?.symbol_index;
553 const sym = macho_file.getSymbol(sym_index);
725 const sym = self.symbols.items[sym_index];
554726 const vaddr = sym.getAddress(.{}, macho_file);
555 const parent_atom = macho_file.getSymbol(reloc_info.parent_atom_index).getAtom(macho_file).?;
727 const parent_atom = self.symbols.items[reloc_info.parent_atom_index].getAtom(macho_file).?;
556728 try parent_atom.addReloc(macho_file, .{
557729 .tag = .@"extern",
558730 .offset = @intCast(reloc_info.offset),
......@@ -585,7 +757,7 @@ pub fn lowerAnonDecl(
585757 else => explicit_alignment,
586758 };
587759 if (self.anon_decls.get(decl_val)) |metadata| {
588 const existing_alignment = macho_file.getSymbol(metadata.symbol_index).getAtom(macho_file).?.alignment;
760 const existing_alignment = self.symbols.items[metadata.symbol_index].getAtom(macho_file).?.alignment;
589761 if (decl_alignment.order(existing_alignment).compare(.lte))
590762 return .ok;
591763 }
......@@ -629,13 +801,10 @@ fn freeUnnamedConsts(self: *ZigObject, macho_file: *MachO, decl_index: InternPoo
629801}
630802
631803fn freeDeclMetadata(self: *ZigObject, macho_file: *MachO, sym_index: Symbol.Index) void {
632 _ = self;
633 const gpa = macho_file.base.comp.gpa;
634 const sym = macho_file.getSymbol(sym_index);
804 const sym = self.symbols.items[sym_index];
635805 sym.getAtom(macho_file).?.free(macho_file);
636806 log.debug("adding %{d} to local symbols free list", .{sym_index});
637 macho_file.symbols_free_list.append(gpa, sym_index) catch {};
638 macho_file.symbols.items[sym_index] = .{};
807 // TODO redo this
639808 // TODO free GOT entry here
640809}
641810
......@@ -676,7 +845,7 @@ pub fn updateFunc(
676845
677846 const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index);
678847 self.freeUnnamedConsts(macho_file, decl_index);
679 macho_file.getSymbol(sym_index).getAtom(macho_file).?.freeRelocs(macho_file);
848 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
680849
681850 var code_buffer = std.ArrayList(u8).init(gpa);
682851 defer code_buffer.deinit();
......@@ -709,7 +878,7 @@ pub fn updateFunc(
709878 try self.updateDeclCode(macho_file, pt, decl_index, sym_index, sect_index, code);
710879
711880 if (decl_state) |*ds| {
712 const sym = macho_file.getSymbol(sym_index);
881 const sym = self.symbols.items[sym_index];
713882 try self.dwarf.?.commitDeclState(
714883 pt,
715884 decl_index,
......@@ -744,13 +913,13 @@ pub fn updateDecl(
744913 const name = decl.name.toSlice(&mod.intern_pool);
745914 const lib_name = variable.lib_name.toSlice(&mod.intern_pool);
746915 const index = try self.getGlobalSymbol(macho_file, name, lib_name);
747 const actual_index = self.symbols.items[index];
748 macho_file.getSymbol(actual_index).flags.needs_got = true;
916 const sym = &self.symbols.items[index];
917 sym.flags.needs_got = true;
749918 return;
750919 }
751920
752921 const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index);
753 macho_file.getSymbol(sym_index).getAtom(macho_file).?.freeRelocs(macho_file);
922 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
754923
755924 const gpa = macho_file.base.comp.gpa;
756925 var code_buffer = std.ArrayList(u8).init(gpa);
......@@ -773,19 +942,16 @@ pub fn updateDecl(
773942 return;
774943 },
775944 };
776 const sect_index = try self.getDeclOutputSection(macho_file, decl, code);
777 const is_threadlocal = switch (macho_file.sections.items(.header)[sect_index].type()) {
778 macho.S_THREAD_LOCAL_ZEROFILL, macho.S_THREAD_LOCAL_REGULAR => true,
779 else => false,
780 };
781 if (is_threadlocal) {
945 if (isThreadlocal(macho_file, decl_index)) {
946 const sect_index = try self.getDeclOutputSection(macho_file, decl, code);
782947 try self.updateTlv(macho_file, pt, decl_index, sym_index, sect_index, code);
783948 } else {
949 const sect_index = try self.getDeclOutputSection(macho_file, decl, code);
784950 try self.updateDeclCode(macho_file, pt, decl_index, sym_index, sect_index, code);
785951 }
786952
787953 if (decl_state) |*ds| {
788 const sym = macho_file.getSymbol(sym_index);
954 const sym = self.symbols.items[sym_index];
789955 try self.dwarf.?.commitDeclState(
790956 pt,
791957 decl_index,
......@@ -817,14 +983,16 @@ fn updateDeclCode(
817983 const required_alignment = decl.getAlignment(pt);
818984
819985 const sect = &macho_file.sections.items(.header)[sect_index];
820 const sym = macho_file.getSymbol(sym_index);
986 const sym = &self.symbols.items[sym_index];
821987 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
822988 const atom = sym.getAtom(macho_file).?;
823989
824990 sym.out_n_sect = sect_index;
825991 atom.out_n_sect = sect_index;
826992
827 sym.name = try self.strtab.insert(gpa, decl.fqn.toSlice(ip));
993 const sym_name = try std.fmt.allocPrintZ(gpa, "_{s}", .{decl.fqn.toSlice(ip)});
994 defer gpa.free(sym_name);
995 sym.name = try self.strtab.insert(gpa, sym_name);
828996 atom.flags.alive = true;
829997 atom.name = sym.name;
830998 nlist.n_strx = sym.name;
......@@ -851,13 +1019,13 @@ fn updateDeclCode(
8511019 if (!macho_file.base.isRelocatable()) {
8521020 log.debug(" (updating offset table entry)", .{});
8531021 assert(sym.flags.has_zig_got);
854 const extra = sym.getExtra(macho_file).?;
1022 const extra = sym.getExtra(macho_file);
8551023 try macho_file.zig_got.writeOne(macho_file, extra.zig_got);
8561024 }
8571025 }
8581026 } else if (code.len < old_size) {
8591027 atom.shrink(macho_file);
860 } else if (macho_file.getAtom(atom.next_index) == null) {
1028 } else if (self.getAtom(atom.next_index) == null) {
8611029 const needed_size = atom.value + code.len;
8621030 sect.size = needed_size;
8631031 }
......@@ -922,31 +1090,22 @@ fn createTlvInitializer(
9221090 const gpa = macho_file.base.comp.gpa;
9231091 const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name});
9241092 defer gpa.free(sym_name);
1093 const off = try self.strtab.insert(gpa, sym_name);
9251094
926 const sym_index = try self.addAtom(macho_file);
927 const sym = macho_file.getSymbol(sym_index);
1095 const sym_index = try self.newSymbolWithAtom(gpa, off, macho_file);
1096 const sym = &self.symbols.items[sym_index];
9281097 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
9291098 const atom = sym.getAtom(macho_file).?;
930
9311099 sym.out_n_sect = sect_index;
9321100 atom.out_n_sect = sect_index;
933
934 sym.value = 0;
935 sym.name = try self.strtab.insert(gpa, sym_name);
9361101 atom.flags.alive = true;
937 atom.name = sym.name;
938 nlist.n_strx = sym.name;
939 nlist.n_sect = sect_index + 1;
940 nlist.n_type = macho.N_SECT;
941 nlist.n_value = 0;
942 self.symtab.items(.size)[sym.nlist_idx] = code.len;
943
9441102 atom.alignment = alignment;
9451103 atom.size = code.len;
1104 nlist.n_sect = sect_index + 1;
1105 self.symtab.items(.size)[sym.nlist_idx] = code.len;
9461106
9471107 const slice = macho_file.sections.slice();
9481108 const header = slice.items(.header)[sect_index];
949 const atoms = &slice.items(.atoms)[sect_index];
9501109
9511110 const gop = try self.tlv_initializers.getOrPut(gpa, atom.atom_index);
9521111 assert(!gop.found_existing); // TODO incremental updates
......@@ -957,8 +1116,6 @@ fn createTlvInitializer(
9571116 gop.value_ptr.data = try gpa.dupe(u8, code);
9581117 }
9591118
960 try atoms.append(gpa, atom.atom_index);
961
9621119 return sym_index;
9631120}
9641121
......@@ -971,7 +1128,7 @@ fn createTlvDescriptor(
9711128) !void {
9721129 const gpa = macho_file.base.comp.gpa;
9731130
974 const sym = macho_file.getSymbol(sym_index);
1131 const sym = &self.symbols.items[sym_index];
9751132 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
9761133 const atom = sym.getAtom(macho_file).?;
9771134 const alignment = Atom.Alignment.fromNonzeroByteUnits(@alignOf(u64));
......@@ -1001,7 +1158,7 @@ fn createTlvDescriptor(
10011158 try atom.addReloc(macho_file, .{
10021159 .tag = .@"extern",
10031160 .offset = 0,
1004 .target = self.symbols.items[tlv_bootstrap_index],
1161 .target = tlv_bootstrap_index,
10051162 .addend = 0,
10061163 .type = .unsigned,
10071164 .meta = .{
......@@ -1021,11 +1178,9 @@ fn createTlvDescriptor(
10211178 .pcrel = false,
10221179 .has_subtractor = false,
10231180 .length = 3,
1024 .symbolnum = @intCast(macho_file.getSymbol(init_sym_index).nlist_idx),
1181 .symbolnum = @intCast(init_sym_index),
10251182 },
10261183 });
1027
1028 try macho_file.sections.items(.atoms)[sect_index].append(gpa, atom.atom_index);
10291184}
10301185
10311186fn getDeclOutputSection(
......@@ -1116,8 +1271,8 @@ pub fn lowerUnnamedConst(
11161271 return error.CodegenFail;
11171272 },
11181273 };
1119 const sym = macho_file.getSymbol(sym_index);
1120 try unnamed_consts.append(gpa, sym.atom);
1274 const sym = self.symbols.items[sym_index];
1275 try unnamed_consts.append(gpa, sym.atom_ref.index);
11211276 return sym_index;
11221277}
11231278
......@@ -1141,7 +1296,8 @@ fn lowerConst(
11411296 var code_buffer = std.ArrayList(u8).init(gpa);
11421297 defer code_buffer.deinit();
11431298
1144 const sym_index = try self.addAtom(macho_file);
1299 const name_str_index = try self.strtab.insert(gpa, name);
1300 const sym_index = try self.newSymbolWithAtom(gpa, name_str_index, macho_file);
11451301
11461302 const res = try codegen.generateSymbol(&macho_file.base, pt, src_loc, val, &code_buffer, .{
11471303 .none = {},
......@@ -1153,20 +1309,15 @@ fn lowerConst(
11531309 .fail => |em| return .{ .fail = em },
11541310 };
11551311
1156 const sym = macho_file.getSymbol(sym_index);
1157 const name_str_index = try self.strtab.insert(gpa, name);
1158 sym.name = name_str_index;
1312 const sym = &self.symbols.items[sym_index];
11591313 sym.out_n_sect = output_section_index;
11601314
11611315 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1162 nlist.n_strx = name_str_index;
1163 nlist.n_type = macho.N_SECT;
11641316 nlist.n_sect = output_section_index + 1;
11651317 self.symtab.items(.size)[sym.nlist_idx] = code.len;
11661318
11671319 const atom = sym.getAtom(macho_file).?;
11681320 atom.flags.alive = true;
1169 atom.name = name_str_index;
11701321 atom.alignment = required_alignment;
11711322 atom.size = code.len;
11721323 atom.out_n_sect = output_section_index;
......@@ -1175,9 +1326,6 @@ fn lowerConst(
11751326 // TODO rename and re-audit this method
11761327 errdefer self.freeDeclMetadata(macho_file, sym_index);
11771328
1178 sym.value = 0;
1179 nlist.n_value = 0;
1180
11811329 const sect = macho_file.sections.items(.header)[output_section_index];
11821330 const file_offset = sect.offset + atom.value;
11831331 try macho_file.base.file.?.pwriteAll(code, file_offset);
......@@ -1219,7 +1367,7 @@ pub fn updateExports(
12191367 },
12201368 };
12211369 const sym_index = metadata.symbol_index;
1222 const nlist_idx = macho_file.getSymbol(sym_index).nlist_idx;
1370 const nlist_idx = self.symbols.items[sym_index].nlist_idx;
12231371 const nlist = self.symtab.items(.nlist)[nlist_idx];
12241372
12251373 for (export_indices) |export_idx| {
......@@ -1255,22 +1403,30 @@ pub fn updateExports(
12551403 break :blk global_nlist_index;
12561404 };
12571405 const global_nlist = &self.symtab.items(.nlist)[global_nlist_index];
1406 const atom_index = self.symtab.items(.atom)[nlist_idx];
1407 const global_sym = &self.symbols.items[global_nlist_index];
12581408 global_nlist.n_value = nlist.n_value;
12591409 global_nlist.n_sect = nlist.n_sect;
12601410 global_nlist.n_type = macho.N_EXT | macho.N_SECT;
12611411 self.symtab.items(.size)[global_nlist_index] = self.symtab.items(.size)[nlist_idx];
1262 self.symtab.items(.atom)[global_nlist_index] = self.symtab.items(.atom)[nlist_idx];
1412 self.symtab.items(.atom)[global_nlist_index] = atom_index;
1413 global_sym.atom_ref = .{ .index = atom_index, .file = self.index };
12631414
12641415 switch (exp.opts.linkage) {
12651416 .internal => {
12661417 // Symbol should be hidden, or in MachO lingo, private extern.
12671418 global_nlist.n_type |= macho.N_PEXT;
1419 global_sym.visibility = .hidden;
1420 },
1421 .strong => {
1422 global_sym.visibility = .global;
12681423 },
1269 .strong => {},
12701424 .weak => {
12711425 // Weak linkage is specified as part of n_desc field.
12721426 // Symbol's n_type is like for a symbol with strong linkage.
12731427 global_nlist.n_desc |= macho.N_WEAK_DEF;
1428 global_sym.visibility = .global;
1429 global_sym.flags.weak = true;
12741430 },
12751431 else => unreachable,
12761432 }
......@@ -1323,7 +1479,7 @@ fn updateLazySymbol(
13231479 .code => macho_file.zig_text_sect_index.?,
13241480 .const_data => macho_file.zig_const_sect_index.?,
13251481 };
1326 const sym = macho_file.getSymbol(symbol_index);
1482 const sym = &self.symbols.items[symbol_index];
13271483 sym.name = name_str_index;
13281484 sym.out_n_sect = output_section_index;
13291485
......@@ -1383,12 +1539,12 @@ pub fn deleteExport(
13831539 const nlist = &self.symtab.items(.nlist)[nlist_index.*];
13841540 self.symtab.items(.size)[nlist_index.*] = 0;
13851541 _ = self.globals_lookup.remove(nlist.n_strx);
1386 const sym_index = macho_file.globals.get(nlist.n_strx).?;
1387 const sym = macho_file.getSymbol(sym_index);
1388 if (sym.file == self.index) {
1389 _ = macho_file.globals.swapRemove(nlist.n_strx);
1390 sym.* = .{};
1391 }
1542 // TODO actually remove the export
1543 // const sym_index = macho_file.globals.get(nlist.n_strx).?;
1544 // const sym = &self.symbols.items[sym_index];
1545 // if (sym.file == self.index) {
1546 // sym.* = .{};
1547 // }
13921548 nlist.* = MachO.null_sym;
13931549}
13941550
......@@ -1400,14 +1556,9 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l
14001556 const off = try self.strtab.insert(gpa, sym_name);
14011557 const lookup_gop = try self.globals_lookup.getOrPut(gpa, off);
14021558 if (!lookup_gop.found_existing) {
1403 const nlist_index = try self.addNlist(gpa);
1404 const nlist = &self.symtab.items(.nlist)[nlist_index];
1405 nlist.n_strx = off;
1406 nlist.n_type = macho.N_EXT;
1407 lookup_gop.value_ptr.* = nlist_index;
1408 const global_name_off = try macho_file.strings.insert(gpa, sym_name);
1409 const gop = try macho_file.getOrCreateGlobal(global_name_off);
1410 try self.symbols.append(gpa, gop.index);
1559 const sym_index = try self.newSymbol(gpa, off, .{});
1560 const sym = &self.symbols.items[sym_index];
1561 lookup_gop.value_ptr.* = sym.nlist_idx;
14111562 }
14121563 return lookup_gop.value_ptr.*;
14131564}
......@@ -1420,17 +1571,11 @@ pub fn getOrCreateMetadataForDecl(
14201571 const gpa = macho_file.base.comp.gpa;
14211572 const gop = try self.decls.getOrPut(gpa, decl_index);
14221573 if (!gop.found_existing) {
1423 const any_non_single_threaded = macho_file.base.comp.config.any_non_single_threaded;
1424 const sym_index = try self.addAtom(macho_file);
1425 const mod = macho_file.base.comp.module.?;
1426 const decl = mod.declPtr(decl_index);
1427 const sym = macho_file.getSymbol(sym_index);
1428 if (decl.getOwnedVariable(mod)) |variable| {
1429 if (variable.is_threadlocal and any_non_single_threaded) {
1430 sym.flags.tlv = true;
1431 }
1432 }
1433 if (!sym.flags.tlv) {
1574 const sym_index = try self.newSymbolWithAtom(gpa, 0, macho_file);
1575 const sym = &self.symbols.items[sym_index];
1576 if (isThreadlocal(macho_file, decl_index)) {
1577 sym.flags.tlv = true;
1578 } else {
14341579 sym.flags.needs_zig_got = true;
14351580 }
14361581 gop.value_ptr.* = .{ .symbol_index = sym_index };
......@@ -1464,8 +1609,8 @@ pub fn getOrCreateMetadataForLazySymbol(
14641609 };
14651610 switch (metadata.state.*) {
14661611 .unused => {
1467 const symbol_index = try self.addAtom(macho_file);
1468 const sym = macho_file.getSymbol(symbol_index);
1612 const symbol_index = try self.newSymbolWithAtom(gpa, 0, macho_file);
1613 const sym = &self.symbols.items[symbol_index];
14691614 sym.flags.needs_zig_got = true;
14701615 metadata.symbol_index.* = symbol_index;
14711616 },
......@@ -1479,6 +1624,144 @@ pub fn getOrCreateMetadataForLazySymbol(
14791624 return symbol_index;
14801625}
14811626
1627fn isThreadlocal(macho_file: *MachO, decl_index: InternPool.DeclIndex) bool {
1628 const any_non_single_threaded = macho_file.base.comp.config.any_non_single_threaded;
1629 const zcu = macho_file.base.comp.module.?;
1630 const decl = zcu.declPtr(decl_index);
1631 const variable = decl.getOwnedVariable(zcu) orelse return false;
1632 return variable.is_threadlocal and any_non_single_threaded;
1633}
1634
1635fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {
1636 try self.atoms.ensureUnusedCapacity(allocator, 1);
1637 try self.atoms_extra.ensureUnusedCapacity(allocator, 1);
1638 return self.addAtomAssumeCapacity();
1639}
1640
1641fn addAtomAssumeCapacity(self: *ZigObject) Atom.Index {
1642 const atom_index: Atom.Index = @intCast(self.atoms.items.len);
1643 const atom = self.atoms.addOneAssumeCapacity();
1644 atom.* = .{
1645 .file = self.index,
1646 .atom_index = atom_index,
1647 .extra = self.addAtomExtraAssumeCapacity(.{}),
1648 };
1649 return atom_index;
1650}
1651
1652pub fn getAtom(self: *ZigObject, atom_index: Atom.Index) ?*Atom {
1653 if (atom_index == 0) return null;
1654 assert(atom_index < self.atoms.items.len);
1655 return &self.atoms.items[atom_index];
1656}
1657
1658pub fn getAtoms(self: *ZigObject) []const Atom.Index {
1659 return self.atoms_indexes.items;
1660}
1661
1662fn addAtomExtra(self: *ZigObject, allocator: Allocator, extra: Atom.Extra) !u32 {
1663 const fields = @typeInfo(Atom.Extra).Struct.fields;
1664 try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
1665 return self.addAtomExtraAssumeCapacity(extra);
1666}
1667
1668fn addAtomExtraAssumeCapacity(self: *ZigObject, extra: Atom.Extra) u32 {
1669 const index = @as(u32, @intCast(self.atoms_extra.items.len));
1670 const fields = @typeInfo(Atom.Extra).Struct.fields;
1671 inline for (fields) |field| {
1672 self.atoms_extra.appendAssumeCapacity(switch (field.type) {
1673 u32 => @field(extra, field.name),
1674 else => @compileError("bad field type"),
1675 });
1676 }
1677 return index;
1678}
1679
1680pub fn getAtomExtra(self: ZigObject, index: u32) Atom.Extra {
1681 const fields = @typeInfo(Atom.Extra).Struct.fields;
1682 var i: usize = index;
1683 var result: Atom.Extra = undefined;
1684 inline for (fields) |field| {
1685 @field(result, field.name) = switch (field.type) {
1686 u32 => self.atoms_extra.items[i],
1687 else => @compileError("bad field type"),
1688 };
1689 i += 1;
1690 }
1691 return result;
1692}
1693
1694pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void {
1695 assert(index > 0);
1696 const fields = @typeInfo(Atom.Extra).Struct.fields;
1697 inline for (fields, 0..) |field, i| {
1698 self.atoms_extra.items[index + i] = switch (field.type) {
1699 u32 => @field(extra, field.name),
1700 else => @compileError("bad field type"),
1701 };
1702 }
1703}
1704
1705fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index {
1706 try self.symbols.ensureUnusedCapacity(allocator, 1);
1707 return self.addSymbolAssumeCapacity();
1708}
1709
1710fn addSymbolAssumeCapacity(self: *ZigObject) Symbol.Index {
1711 const index: Symbol.Index = @intCast(self.symbols.items.len);
1712 const symbol = self.symbols.addOneAssumeCapacity();
1713 symbol.* = .{ .file = self.index };
1714 return index;
1715}
1716
1717pub fn getSymbolRef(self: ZigObject, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
1718 const global_index = self.globals.items[index];
1719 if (macho_file.resolver.get(global_index)) |ref| return ref;
1720 return .{ .index = index, .file = self.index };
1721}
1722
1723pub fn addSymbolExtra(self: *ZigObject, allocator: Allocator, extra: Symbol.Extra) !u32 {
1724 const fields = @typeInfo(Symbol.Extra).Struct.fields;
1725 try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
1726 return self.addSymbolExtraAssumeCapacity(extra);
1727}
1728
1729fn addSymbolExtraAssumeCapacity(self: *ZigObject, extra: Symbol.Extra) u32 {
1730 const index = @as(u32, @intCast(self.symbols_extra.items.len));
1731 const fields = @typeInfo(Symbol.Extra).Struct.fields;
1732 inline for (fields) |field| {
1733 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
1734 u32 => @field(extra, field.name),
1735 else => @compileError("bad field type"),
1736 });
1737 }
1738 return index;
1739}
1740
1741pub fn getSymbolExtra(self: ZigObject, index: u32) Symbol.Extra {
1742 const fields = @typeInfo(Symbol.Extra).Struct.fields;
1743 var i: usize = index;
1744 var result: Symbol.Extra = undefined;
1745 inline for (fields) |field| {
1746 @field(result, field.name) = switch (field.type) {
1747 u32 => self.symbols_extra.items[i],
1748 else => @compileError("bad field type"),
1749 };
1750 i += 1;
1751 }
1752 return result;
1753}
1754
1755pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {
1756 const fields = @typeInfo(Symbol.Extra).Struct.fields;
1757 inline for (fields, 0..) |field, i| {
1758 self.symbols_extra.items[index + i] = switch (field.type) {
1759 u32 => @field(extra, field.name),
1760 else => @compileError("bad field type"),
1761 };
1762 }
1763}
1764
14821765pub fn asFile(self: *ZigObject) File {
14831766 return .{ .zig_object = self };
14841767}
......@@ -1504,9 +1787,16 @@ fn formatSymtab(
15041787 _ = unused_fmt_string;
15051788 _ = options;
15061789 try writer.writeAll(" symbols\n");
1507 for (ctx.self.symbols.items) |index| {
1508 const sym = ctx.macho_file.getSymbol(index);
1509 try writer.print(" {}\n", .{sym.fmt(ctx.macho_file)});
1790 const self = ctx.self;
1791 const macho_file = ctx.macho_file;
1792 for (self.symbols.items, 0..) |sym, i| {
1793 const ref = self.getSymbolRef(@intCast(i), macho_file);
1794 if (ref.getFile(macho_file) == null) {
1795 // TODO any better way of handling this?
1796 try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
1797 } else {
1798 try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
1799 }
15101800 }
15111801}
15121802
......@@ -1525,10 +1815,12 @@ fn formatAtoms(
15251815) !void {
15261816 _ = unused_fmt_string;
15271817 _ = options;
1818 const self = ctx.self;
1819 const macho_file = ctx.macho_file;
15281820 try writer.writeAll(" atoms\n");
1529 for (ctx.self.atoms.items) |atom_index| {
1530 const atom = ctx.macho_file.getAtom(atom_index) orelse continue;
1531 try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});
1821 for (self.getAtoms()) |atom_index| {
1822 const atom = self.getAtom(atom_index) orelse continue;
1823 try writer.print(" {}\n", .{atom.fmt(macho_file)});
15321824 }
15331825}
15341826
src/link/MachO/dead_strip.zig+45-27
......@@ -17,16 +17,16 @@ pub fn gcAtoms(macho_file: *MachO) !void {
1717fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho_file: *MachO) !void {
1818 for (objects) |index| {
1919 const object = macho_file.getFile(index).?;
20 for (object.getSymbols()) |sym_index| {
21 const sym = macho_file.getSymbol(sym_index);
22 const file = sym.getFile(macho_file) orelse continue;
20 for (object.getSymbols(), 0..) |*sym, i| {
21 const ref = object.getSymbolRef(@intCast(i), macho_file);
22 const file = ref.getFile(macho_file) orelse continue;
2323 if (file.getIndex() != index) continue;
2424 if (sym.flags.no_dead_strip or (macho_file.base.isDynLib() and sym.visibility == .global))
2525 try markSymbol(sym, roots, macho_file);
2626 }
2727
2828 for (object.getAtoms()) |atom_index| {
29 const atom = macho_file.getAtom(atom_index).?;
29 const atom = object.getAtom(atom_index) orelse continue;
3030 const isec = atom.getInputSection(macho_file);
3131 switch (isec.type()) {
3232 macho.S_MOD_INIT_FUNC_POINTERS,
......@@ -41,8 +41,9 @@ fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho
4141 }
4242
4343 for (macho_file.objects.items) |index| {
44 for (macho_file.getFile(index).?.object.unwind_records.items) |cu_index| {
45 const cu = macho_file.getUnwindRecord(cu_index);
44 const object = macho_file.getFile(index).?.object;
45 for (object.unwind_records_indexes.items) |cu_index| {
46 const cu = object.getUnwindRecord(cu_index);
4647 if (!cu.alive) continue;
4748 if (cu.getFde(macho_file)) |fde| {
4849 if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| try markSymbol(sym, roots, macho_file);
......@@ -50,19 +51,27 @@ fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho
5051 }
5152 }
5253
53 for (macho_file.undefined_symbols.items) |sym_index| {
54 const sym = macho_file.getSymbol(sym_index);
55 try markSymbol(sym, roots, macho_file);
56 }
54 if (macho_file.getInternalObject()) |obj| {
55 for (obj.force_undefined.items) |sym_index| {
56 const ref = obj.getSymbolRef(sym_index, macho_file);
57 if (ref.getFile(macho_file) != null) {
58 const sym = ref.getSymbol(macho_file).?;
59 try markSymbol(sym, roots, macho_file);
60 }
61 }
5762
58 for (&[_]?Symbol.Index{
59 macho_file.entry_index,
60 macho_file.dyld_stub_binder_index,
61 macho_file.objc_msg_send_index,
62 }) |index| {
63 if (index) |idx| {
64 const sym = macho_file.getSymbol(idx);
65 try markSymbol(sym, roots, macho_file);
63 for (&[_]?Symbol.Index{
64 obj.entry_index,
65 obj.dyld_stub_binder_index,
66 obj.objc_msg_send_index,
67 }) |index| {
68 if (index) |idx| {
69 const ref = obj.getSymbolRef(idx, macho_file);
70 if (ref.getFile(macho_file) != null) {
71 const sym = ref.getSymbol(macho_file).?;
72 try markSymbol(sym, roots, macho_file);
73 }
74 }
6675 }
6776 }
6877}
......@@ -88,8 +97,9 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {
8897 loop = false;
8998
9099 for (objects) |index| {
91 for (macho_file.getFile(index).?.getAtoms()) |atom_index| {
92 const atom = macho_file.getAtom(atom_index).?;
100 const file = macho_file.getFile(index).?;
101 for (file.getAtoms()) |atom_index| {
102 const atom = file.getAtom(atom_index) orelse continue;
93103 const isec = atom.getInputSection(macho_file);
94104 if (isec.isDontDeadStripIfReferencesLive() and
95105 !(mem.eql(u8, isec.sectName(), "__eh_frame") or
......@@ -119,16 +129,20 @@ fn markLive(atom: *Atom, macho_file: *MachO) void {
119129
120130 for (atom.getRelocs(macho_file)) |rel| {
121131 const target_atom = switch (rel.tag) {
122 .local => rel.getTargetAtom(macho_file),
123 .@"extern" => rel.getTargetSymbol(macho_file).getAtom(macho_file),
132 .local => rel.getTargetAtom(atom.*, macho_file),
133 .@"extern" => blk: {
134 const ref = rel.getTargetSymbolRef(atom.*, macho_file);
135 break :blk if (ref.getSymbol(macho_file)) |sym| sym.getAtom(macho_file) else null;
136 },
124137 };
125138 if (target_atom) |ta| {
126139 if (markAtom(ta)) markLive(ta, macho_file);
127140 }
128141 }
129142
143 const file = atom.getFile(macho_file);
130144 for (atom.getUnwindRecords(macho_file)) |cu_index| {
131 const cu = macho_file.getUnwindRecord(cu_index);
145 const cu = file.object.getUnwindRecord(cu_index);
132146 const cu_atom = cu.getAtom(macho_file);
133147 if (markAtom(cu_atom)) markLive(cu_atom, macho_file);
134148
......@@ -149,8 +163,11 @@ fn markLive(atom: *Atom, macho_file: *MachO) void {
149163fn refersLive(atom: *Atom, macho_file: *MachO) bool {
150164 for (atom.getRelocs(macho_file)) |rel| {
151165 const target_atom = switch (rel.tag) {
152 .local => rel.getTargetAtom(macho_file),
153 .@"extern" => rel.getTargetSymbol(macho_file).getAtom(macho_file),
166 .local => rel.getTargetAtom(atom.*, macho_file),
167 .@"extern" => blk: {
168 const ref = rel.getTargetSymbolRef(atom.*, macho_file);
169 break :blk if (ref.getSymbol(macho_file)) |sym| sym.getAtom(macho_file) else null;
170 },
154171 };
155172 if (target_atom) |ta| {
156173 if (ta.flags.alive) return true;
......@@ -161,8 +178,9 @@ fn refersLive(atom: *Atom, macho_file: *MachO) bool {
161178
162179fn prune(objects: []const File.Index, macho_file: *MachO) void {
163180 for (objects) |index| {
164 for (macho_file.getFile(index).?.getAtoms()) |atom_index| {
165 const atom = macho_file.getAtom(atom_index).?;
181 const file = macho_file.getFile(index).?;
182 for (file.getAtoms()) |atom_index| {
183 const atom = file.getAtom(atom_index) orelse continue;
166184 if (atom.flags.alive and !atom.flags.visited) {
167185 atom.flags.alive = false;
168186 atom.markUnwindRecordsDead(macho_file);
src/link/MachO/dyld_info/Rebase.zig+113-15
......@@ -1,14 +1,3 @@
1const Rebase = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const leb = std.leb;
6const log = std.log.scoped(.link_dyld_info);
7const macho = std.macho;
8const testing = std.testing;
9
10const Allocator = std.mem.Allocator;
11
121entries: std.ArrayListUnmanaged(Entry) = .{},
132buffer: std.ArrayListUnmanaged(u8) = .{},
143
......@@ -30,11 +19,107 @@ pub fn deinit(rebase: *Rebase, gpa: Allocator) void {
3019 rebase.buffer.deinit(gpa);
3120}
3221
33pub fn size(rebase: Rebase) u64 {
34 return @as(u64, @intCast(rebase.buffer.items.len));
22pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void {
23 const tracy = trace(@src());
24 defer tracy.end();
25
26 const gpa = macho_file.base.comp.gpa;
27
28 var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 2);
29 defer objects.deinit();
30 objects.appendSliceAssumeCapacity(macho_file.objects.items);
31 if (macho_file.getZigObject()) |obj| objects.appendAssumeCapacity(obj.index);
32 if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index);
33
34 for (objects.items) |index| {
35 const file = macho_file.getFile(index).?;
36 for (file.getAtoms()) |atom_index| {
37 const atom = file.getAtom(atom_index) orelse continue;
38 if (!atom.flags.alive) continue;
39 if (atom.getInputSection(macho_file).isZerofill()) continue;
40 const atom_addr = atom.getAddress(macho_file);
41 const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
42 const seg = macho_file.segments.items[seg_id];
43 for (atom.getRelocs(macho_file)) |rel| {
44 if (rel.type != .unsigned or rel.meta.length != 3) continue;
45 if (rel.tag == .@"extern") {
46 const sym = rel.getTargetSymbol(atom.*, macho_file);
47 if (sym.isTlvInit(macho_file)) continue;
48 if (sym.flags.import) continue;
49 }
50 const rel_offset = rel.offset - atom.off;
51 try rebase.entries.append(gpa, .{
52 .offset = atom_addr + rel_offset - seg.vmaddr,
53 .segment_id = seg_id,
54 });
55 }
56 }
57 }
58
59 if (macho_file.zig_got_sect_index) |sid| {
60 const seg_id = macho_file.sections.items(.segment_id)[sid];
61 const seg = macho_file.segments.items[seg_id];
62 for (0..macho_file.zig_got.entries.items.len) |idx| {
63 const addr = macho_file.zig_got.entryAddress(@intCast(idx), macho_file);
64 try rebase.entries.append(gpa, .{
65 .offset = addr - seg.vmaddr,
66 .segment_id = seg_id,
67 });
68 }
69 }
70
71 if (macho_file.got_sect_index) |sid| {
72 const seg_id = macho_file.sections.items(.segment_id)[sid];
73 const seg = macho_file.segments.items[seg_id];
74 for (macho_file.got.symbols.items, 0..) |ref, idx| {
75 const sym = ref.getSymbol(macho_file).?;
76 const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
77 if (!sym.flags.import) {
78 try rebase.entries.append(gpa, .{
79 .offset = addr - seg.vmaddr,
80 .segment_id = seg_id,
81 });
82 }
83 }
84 }
85
86 if (macho_file.la_symbol_ptr_sect_index) |sid| {
87 const sect = macho_file.sections.items(.header)[sid];
88 const seg_id = macho_file.sections.items(.segment_id)[sid];
89 const seg = macho_file.segments.items[seg_id];
90 for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
91 const sym = ref.getSymbol(macho_file).?;
92 const addr = sect.addr + idx * @sizeOf(u64);
93 const rebase_entry = Rebase.Entry{
94 .offset = addr - seg.vmaddr,
95 .segment_id = seg_id,
96 };
97 if ((sym.flags.import and !sym.flags.weak) or !sym.flags.import) {
98 try rebase.entries.append(gpa, rebase_entry);
99 }
100 }
101 }
102
103 if (macho_file.tlv_ptr_sect_index) |sid| {
104 const seg_id = macho_file.sections.items(.segment_id)[sid];
105 const seg = macho_file.segments.items[seg_id];
106 for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
107 const sym = ref.getSymbol(macho_file).?;
108 const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
109 if (!sym.flags.import) {
110 try rebase.entries.append(gpa, .{
111 .offset = addr - seg.vmaddr,
112 .segment_id = seg_id,
113 });
114 }
115 }
116 }
117
118 try rebase.finalize(gpa);
119 macho_file.dyld_info_cmd.rebase_size = mem.alignForward(u32, @intCast(rebase.buffer.items.len), @alignOf(u64));
35120}
36121
37pub fn finalize(rebase: *Rebase, gpa: Allocator) !void {
122fn finalize(rebase: *Rebase, gpa: Allocator) !void {
38123 if (rebase.entries.items.len == 0) return;
39124
40125 const writer = rebase.buffer.writer(gpa);
......@@ -198,7 +283,6 @@ fn done(writer: anytype) !void {
198283}
199284
200285pub fn write(rebase: Rebase, writer: anytype) !void {
201 if (rebase.size() == 0) return;
202286 try writer.writeAll(rebase.buffer.items);
203287}
204288
......@@ -574,3 +658,17 @@ test "rebase - composite" {
574658 macho.REBASE_OPCODE_DONE,
575659 }, rebase.buffer.items);
576660}
661
662const std = @import("std");
663const assert = std.debug.assert;
664const leb = std.leb;
665const log = std.log.scoped(.link_dyld_info);
666const macho = std.macho;
667const mem = std.mem;
668const testing = std.testing;
669const trace = @import("../../../tracy.zig").trace;
670
671const Allocator = mem.Allocator;
672const File = @import("../file.zig").File;
673const MachO = @import("../../MachO.zig");
674const Rebase = @This();
src/link/MachO/dyld_info/Trie.zig+262-453
......@@ -28,463 +28,310 @@
2828//! After the optional exported symbol information is a byte of how many edges (0-255) that
2929//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of
3030//! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to.
31const Trie = @This();
32
33const std = @import("std");
34const mem = std.mem;
35const leb = std.leb;
36const log = std.log.scoped(.macho);
37const macho = std.macho;
38const testing = std.testing;
39const assert = std.debug.assert;
40const Allocator = mem.Allocator;
41
42pub const Node = struct {
43 base: *Trie,
44
45 /// Terminal info associated with this node.
46 /// If this node is not a terminal node, info is null.
47 terminal_info: ?struct {
48 /// Export flags associated with this exported symbol.
49 export_flags: u64,
50 /// VM address offset wrt to the section this symbol is defined against.
51 vmaddr_offset: u64,
52 } = null,
53
54 /// Offset of this node in the trie output byte stream.
55 trie_offset: ?u64 = null,
56
57 /// List of all edges originating from this node.
58 edges: std.ArrayListUnmanaged(Edge) = .{},
59
60 node_dirty: bool = true,
61
62 /// Edge connecting to nodes in the trie.
63 pub const Edge = struct {
64 from: *Node,
65 to: *Node,
66 label: []u8,
67
68 fn deinit(self: *Edge, allocator: Allocator) void {
69 self.to.deinit(allocator);
70 allocator.destroy(self.to);
71 allocator.free(self.label);
72 self.from = undefined;
73 self.to = undefined;
74 self.label = undefined;
75 }
76 };
77
78 fn deinit(self: *Node, allocator: Allocator) void {
79 for (self.edges.items) |*edge| {
80 edge.deinit(allocator);
81 }
82 self.edges.deinit(allocator);
83 }
84
85 /// Inserts a new node starting from `self`.
86 fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node {
87 // Check for match with edges from this node.
88 for (self.edges.items) |*edge| {
89 const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
90 if (match == 0) continue;
91 if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
92
93 // Found a match, need to splice up nodes.
94 // From: A -> B
95 // To: A -> C -> B
96 const mid = try allocator.create(Node);
97 mid.* = .{ .base = self.base };
98 const to_label = try allocator.dupe(u8, edge.label[match..]);
99 allocator.free(edge.label);
100 const to_node = edge.to;
101 edge.to = mid;
102 edge.label = try allocator.dupe(u8, label[0..match]);
103 self.base.node_count += 1;
104
105 try mid.edges.append(allocator, .{
106 .from = mid,
107 .to = to_node,
108 .label = to_label,
109 });
110
111 return if (match == label.len) mid else mid.put(allocator, label[match..]);
112 }
113
114 // Add a new node.
115 const node = try allocator.create(Node);
116 node.* = .{ .base = self.base };
117 self.base.node_count += 1;
11831
119 try self.edges.append(allocator, .{
120 .from = self,
121 .to = node,
122 .label = try allocator.dupe(u8, label),
123 });
124
125 return node;
126 }
127
128 /// Recursively parses the node from the input byte stream.
129 fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize {
130 self.node_dirty = true;
131 const trie_offset = try reader.context.getPos();
132 self.trie_offset = trie_offset;
133
134 var nread: usize = 0;
135
136 const node_size = try leb.readUleb128(u64, reader);
137 if (node_size > 0) {
138 const export_flags = try leb.readUleb128(u64, reader);
139 // TODO Parse special flags.
140 assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
141 export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
142
143 const vmaddr_offset = try leb.readUleb128(u64, reader);
144
145 self.terminal_info = .{
146 .export_flags = export_flags,
147 .vmaddr_offset = vmaddr_offset,
148 };
149 }
150
151 const nedges = try reader.readByte();
152 self.base.node_count += nedges;
153
154 nread += (try reader.context.getPos()) - trie_offset;
155
156 var i: usize = 0;
157 while (i < nedges) : (i += 1) {
158 const edge_start_pos = try reader.context.getPos();
159
160 const label = blk: {
161 var label_buf = std.ArrayList(u8).init(allocator);
162 while (true) {
163 const next = try reader.readByte();
164 if (next == @as(u8, 0))
165 break;
166 try label_buf.append(next);
167 }
168 break :blk try label_buf.toOwnedSlice();
169 };
170
171 const seek_to = try leb.readUleb128(u64, reader);
172 const return_pos = try reader.context.getPos();
173
174 nread += return_pos - edge_start_pos;
175 try reader.context.seekTo(seek_to);
176
177 const node = try allocator.create(Node);
178 node.* = .{ .base = self.base };
179
180 nread += try node.read(allocator, reader);
181 try self.edges.append(allocator, .{
182 .from = self,
183 .to = node,
184 .label = label,
185 });
186 try reader.context.seekTo(return_pos);
187 }
32/// The root node of the trie.
33root: ?Node.Index = null,
34buffer: std.ArrayListUnmanaged(u8) = .{},
35nodes: std.MultiArrayList(Node) = .{},
36edges: std.ArrayListUnmanaged(Edge) = .{},
18837
189 return nread;
190 }
38/// Insert a symbol into the trie, updating the prefixes in the process.
39/// This operation may change the layout of the trie by splicing edges in
40/// certain circumstances.
41fn put(self: *Trie, allocator: Allocator, symbol: ExportSymbol) !void {
42 // const tracy = trace(@src());
43 // defer tracy.end();
44
45 const node_index = try self.putNode(self.root.?, allocator, symbol.name);
46 const slice = self.nodes.slice();
47 slice.items(.is_terminal)[node_index] = true;
48 slice.items(.vmaddr_offset)[node_index] = symbol.vmaddr_offset;
49 slice.items(.export_flags)[node_index] = symbol.export_flags;
50}
19151
192 /// Writes this node to a byte stream.
193 /// The children of this node *are* not written to the byte stream
194 /// recursively. To write all nodes to a byte stream in sequence,
195 /// iterate over `Trie.ordered_nodes` and call this method on each node.
196 /// This is one of the requirements of the MachO.
197 /// Panics if `finalize` was not called before calling this method.
198 fn write(self: Node, writer: anytype) !void {
199 assert(!self.node_dirty);
200 if (self.terminal_info) |info| {
201 // Terminal node info: encode export flags and vmaddr offset of this symbol.
202 var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
203 var info_stream = std.io.fixedBufferStream(&info_buf);
204 // TODO Implement for special flags.
205 assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
206 info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
207 try leb.writeUleb128(info_stream.writer(), info.export_flags);
208 try leb.writeUleb128(info_stream.writer(), info.vmaddr_offset);
209
210 // Encode the size of the terminal node info.
211 var size_buf: [@sizeOf(u64)]u8 = undefined;
212 var size_stream = std.io.fixedBufferStream(&size_buf);
213 try leb.writeUleb128(size_stream.writer(), info_stream.pos);
214
215 // Now, write them to the output stream.
216 try writer.writeAll(size_buf[0..size_stream.pos]);
217 try writer.writeAll(info_buf[0..info_stream.pos]);
218 } else {
219 // Non-terminal node is delimited by 0 byte.
220 try writer.writeByte(0);
221 }
222 // Write number of edges (max legal number of edges is 256).
223 try writer.writeByte(@as(u8, @intCast(self.edges.items.len)));
224
225 for (self.edges.items) |edge| {
226 // Write edge label and offset to next node in trie.
227 try writer.writeAll(edge.label);
228 try writer.writeByte(0);
229 try leb.writeUleb128(writer, edge.to.trie_offset.?);
230 }
52/// Inserts a new node starting at `node_index`.
53fn putNode(self: *Trie, node_index: Node.Index, allocator: Allocator, label: []const u8) !Node.Index {
54 // Check for match with edges from this node.
55 for (self.nodes.items(.edges)[node_index].items) |edge_index| {
56 const edge = &self.edges.items[edge_index];
57 const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.node;
58 if (match == 0) continue;
59 if (match == edge.label.len) return self.putNode(edge.node, allocator, label[match..]);
60
61 // Found a match, need to splice up nodes.
62 // From: A -> B
63 // To: A -> C -> B
64 const mid_index = try self.addNode(allocator);
65 const to_label = edge.label[match..];
66 const to_node = edge.node;
67 edge.node = mid_index;
68 edge.label = label[0..match];
69
70 const new_edge_index = try self.addEdge(allocator);
71 const new_edge = &self.edges.items[new_edge_index];
72 new_edge.node = to_node;
73 new_edge.label = to_label;
74 try self.nodes.items(.edges)[mid_index].append(allocator, new_edge_index);
75
76 return if (match == label.len) mid_index else self.putNode(mid_index, allocator, label[match..]);
23177 }
23278
233 const FinalizeResult = struct {
234 /// Current size of this node in bytes.
235 node_size: u64,
236
237 /// True if the trie offset of this node in the output byte stream
238 /// would need updating; false otherwise.
239 updated: bool,
240 };
79 // Add a new node.
80 const new_node_index = try self.addNode(allocator);
81 const new_edge_index = try self.addEdge(allocator);
82 const new_edge = &self.edges.items[new_edge_index];
83 new_edge.node = new_node_index;
84 new_edge.label = label;
85 try self.nodes.items(.edges)[node_index].append(allocator, new_edge_index);
24186
242 /// Updates offset of this node in the output byte stream.
243 fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult {
244 var stream = std.io.countingWriter(std.io.null_writer);
245 const writer = stream.writer();
246
247 var node_size: u64 = 0;
248 if (self.terminal_info) |info| {
249 try leb.writeUleb128(writer, info.export_flags);
250 try leb.writeUleb128(writer, info.vmaddr_offset);
251 try leb.writeUleb128(writer, stream.bytes_written);
252 } else {
253 node_size += 1; // 0x0 for non-terminal nodes
254 }
255 node_size += 1; // 1 byte for edge count
87 return new_node_index;
88}
25689
257 for (self.edges.items) |edge| {
258 const next_node_offset = edge.to.trie_offset orelse 0;
259 node_size += edge.label.len + 1;
260 try leb.writeUleb128(writer, next_node_offset);
90pub fn updateSize(self: *Trie, macho_file: *MachO) !void {
91 const tracy = trace(@src());
92 defer tracy.end();
93
94 const gpa = macho_file.base.comp.gpa;
95
96 try self.init(gpa);
97 try self.nodes.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2);
98 try self.edges.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2);
99
100 const seg = macho_file.getTextSegment();
101 for (macho_file.resolver.values.items) |ref| {
102 if (ref.getFile(macho_file) == null) continue;
103 const sym = ref.getSymbol(macho_file).?;
104 if (!sym.flags.@"export") continue;
105 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
106 var flags: u64 = if (sym.flags.abs)
107 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
108 else if (sym.flags.tlv)
109 macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL
110 else
111 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
112 if (sym.flags.weak) {
113 flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
114 macho_file.weak_defines = true;
115 macho_file.binds_to_weak = true;
261116 }
262
263 const trie_offset = self.trie_offset orelse 0;
264 const updated = offset_in_trie != trie_offset;
265 self.trie_offset = offset_in_trie;
266 self.node_dirty = false;
267 node_size += stream.bytes_written;
268
269 return FinalizeResult{ .node_size = node_size, .updated = updated };
117 try self.put(gpa, .{
118 .name = sym.getName(macho_file),
119 .vmaddr_offset = sym.getAddress(.{ .stubs = false }, macho_file) - seg.vmaddr,
120 .export_flags = flags,
121 });
270122 }
271};
272
273/// The root node of the trie.
274root: ?*Node = null,
275123
276/// If you want to access nodes ordered in DFS fashion,
277/// you should call `finalize` first since the nodes
278/// in this container are not guaranteed to not be stale
279/// if more insertions took place after the last `finalize`
280/// call.
281ordered_nodes: std.ArrayListUnmanaged(*Node) = .{},
124 try self.finalize(gpa);
282125
283/// The size of the trie in bytes.
284/// This value may be outdated if there were additional
285/// insertions performed after `finalize` was called.
286/// Call `finalize` before accessing this value to ensure
287/// it is up-to-date.
288size: u64 = 0,
289
290/// Number of nodes currently in the trie.
291node_count: usize = 0,
292
293trie_dirty: bool = true,
294
295/// Export symbol that is to be placed in the trie.
296pub const ExportSymbol = struct {
297 /// Name of the symbol.
298 name: []const u8,
299
300 /// Offset of this symbol's virtual memory address from the beginning
301 /// of the __TEXT segment.
302 vmaddr_offset: u64,
303
304 /// Export flags of this exported symbol.
305 export_flags: u64,
306};
307
308/// Insert a symbol into the trie, updating the prefixes in the process.
309/// This operation may change the layout of the trie by splicing edges in
310/// certain circumstances.
311pub fn put(self: *Trie, allocator: Allocator, symbol: ExportSymbol) !void {
312 const node = try self.root.?.put(allocator, symbol.name);
313 node.terminal_info = .{
314 .vmaddr_offset = symbol.vmaddr_offset,
315 .export_flags = symbol.export_flags,
316 };
317 self.trie_dirty = true;
126 macho_file.dyld_info_cmd.export_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
318127}
319128
320129/// Finalizes this trie for writing to a byte stream.
321130/// This step performs multiple passes through the trie ensuring
322131/// there are no gaps after every `Node` is ULEB128 encoded.
323132/// Call this method before trying to `write` the trie to a byte stream.
324pub fn finalize(self: *Trie, allocator: Allocator) !void {
325 if (!self.trie_dirty) return;
133fn finalize(self: *Trie, allocator: Allocator) !void {
134 const tracy = trace(@src());
135 defer tracy.end();
326136
327 self.ordered_nodes.shrinkRetainingCapacity(0);
328 try self.ordered_nodes.ensureTotalCapacity(allocator, self.node_count);
137 var ordered_nodes = std.ArrayList(Node.Index).init(allocator);
138 defer ordered_nodes.deinit();
139 try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len);
329140
330 var fifo = std.fifo.LinearFifo(*Node, .Dynamic).init(allocator);
141 var fifo = std.fifo.LinearFifo(Node.Index, .Dynamic).init(allocator);
331142 defer fifo.deinit();
332143
333144 try fifo.writeItem(self.root.?);
334145
335 while (fifo.readItem()) |next| {
336 for (next.edges.items) |*edge| {
337 try fifo.writeItem(edge.to);
146 while (fifo.readItem()) |next_index| {
147 const edges = &self.nodes.items(.edges)[next_index];
148 for (edges.items) |edge_index| {
149 const edge = self.edges.items[edge_index];
150 try fifo.writeItem(edge.node);
338151 }
339 self.ordered_nodes.appendAssumeCapacity(next);
152 ordered_nodes.appendAssumeCapacity(next_index);
340153 }
341154
342155 var more: bool = true;
156 var size: u32 = 0;
343157 while (more) {
344 self.size = 0;
158 size = 0;
345159 more = false;
346 for (self.ordered_nodes.items) |node| {
347 const res = try node.finalize(self.size);
348 self.size += res.node_size;
160 for (ordered_nodes.items) |node_index| {
161 const res = try self.finalizeNode(node_index, size);
162 size += res.node_size;
349163 if (res.updated) more = true;
350164 }
351165 }
352166
353 self.trie_dirty = false;
167 try self.buffer.ensureTotalCapacityPrecise(allocator, size);
168 for (ordered_nodes.items) |node_index| {
169 try self.writeNode(node_index, self.buffer.writer(allocator));
170 }
354171}
355172
356const ReadError = error{
357 OutOfMemory,
358 EndOfStream,
359 Overflow,
173const FinalizeNodeResult = struct {
174 /// Current size of this node in bytes.
175 node_size: u32,
176
177 /// True if the trie offset of this node in the output byte stream
178 /// would need updating; false otherwise.
179 updated: bool,
360180};
361181
362/// Parse the trie from a byte stream.
363pub fn read(self: *Trie, allocator: Allocator, reader: anytype) ReadError!usize {
364 return self.root.?.read(allocator, reader);
365}
182/// Updates offset of this node in the output byte stream.
183fn finalizeNode(self: *Trie, node_index: Node.Index, offset_in_trie: u32) !FinalizeNodeResult {
184 var stream = std.io.countingWriter(std.io.null_writer);
185 const writer = stream.writer();
186 const slice = self.nodes.slice();
187
188 var node_size: u32 = 0;
189 if (slice.items(.is_terminal)[node_index]) {
190 const export_flags = slice.items(.export_flags)[node_index];
191 const vmaddr_offset = slice.items(.vmaddr_offset)[node_index];
192 try leb.writeULEB128(writer, export_flags);
193 try leb.writeULEB128(writer, vmaddr_offset);
194 try leb.writeULEB128(writer, stream.bytes_written);
195 } else {
196 node_size += 1; // 0x0 for non-terminal nodes
197 }
198 node_size += 1; // 1 byte for edge count
366199
367/// Write the trie to a byte stream.
368/// Panics if the trie was not finalized using `finalize` before calling this method.
369pub fn write(self: Trie, writer: anytype) !void {
370 assert(!self.trie_dirty);
371 for (self.ordered_nodes.items) |node| {
372 try node.write(writer);
200 for (slice.items(.edges)[node_index].items) |edge_index| {
201 const edge = &self.edges.items[edge_index];
202 const next_node_offset = slice.items(.trie_offset)[edge.node];
203 node_size += @intCast(edge.label.len + 1);
204 try leb.writeULEB128(writer, next_node_offset);
373205 }
206
207 const trie_offset = slice.items(.trie_offset)[node_index];
208 const updated = offset_in_trie != trie_offset;
209 slice.items(.trie_offset)[node_index] = offset_in_trie;
210 node_size += @intCast(stream.bytes_written);
211
212 return .{ .node_size = node_size, .updated = updated };
374213}
375214
376pub fn init(self: *Trie, allocator: Allocator) !void {
215fn init(self: *Trie, allocator: Allocator) !void {
377216 assert(self.root == null);
378 const root = try allocator.create(Node);
379 root.* = .{ .base = self };
380 self.root = root;
381 self.node_count += 1;
217 self.root = try self.addNode(allocator);
382218}
383219
384220pub fn deinit(self: *Trie, allocator: Allocator) void {
385 if (self.root) |root| {
386 root.deinit(allocator);
387 allocator.destroy(root);
221 for (self.nodes.items(.edges)) |*edges| {
222 edges.deinit(allocator);
388223 }
389 self.ordered_nodes.deinit(allocator);
224 self.nodes.deinit(allocator);
225 self.edges.deinit(allocator);
226 self.buffer.deinit(allocator);
390227}
391228
392test "Trie node count" {
393 const gpa = testing.allocator;
394 var trie: Trie = .{};
395 defer trie.deinit(gpa);
396 try trie.init(gpa);
229pub fn write(self: Trie, writer: anytype) !void {
230 if (self.buffer.items.len == 0) return;
231 try writer.writeAll(self.buffer.items);
232}
397233
398 try testing.expectEqual(@as(usize, 1), trie.node_count);
399 try testing.expect(trie.root != null);
234/// Writes this node to a byte stream.
235/// The children of this node *are* not written to the byte stream
236/// recursively. To write all nodes to a byte stream in sequence,
237/// iterate over `Trie.ordered_nodes` and call this method on each node.
238/// This is one of the requirements of the MachO.
239/// Panics if `finalize` was not called before calling this method.
240fn writeNode(self: *Trie, node_index: Node.Index, writer: anytype) !void {
241 const slice = self.nodes.slice();
242 const edges = slice.items(.edges)[node_index];
243 const is_terminal = slice.items(.is_terminal)[node_index];
244 const export_flags = slice.items(.export_flags)[node_index];
245 const vmaddr_offset = slice.items(.vmaddr_offset)[node_index];
246
247 if (is_terminal) {
248 // Terminal node info: encode export flags and vmaddr offset of this symbol.
249 var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
250 var info_stream = std.io.fixedBufferStream(&info_buf);
251 // TODO Implement for special flags.
252 assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
253 export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
254 try leb.writeULEB128(info_stream.writer(), export_flags);
255 try leb.writeULEB128(info_stream.writer(), vmaddr_offset);
256
257 // Encode the size of the terminal node info.
258 var size_buf: [@sizeOf(u64)]u8 = undefined;
259 var size_stream = std.io.fixedBufferStream(&size_buf);
260 try leb.writeULEB128(size_stream.writer(), info_stream.pos);
261
262 // Now, write them to the output stream.
263 try writer.writeAll(size_buf[0..size_stream.pos]);
264 try writer.writeAll(info_buf[0..info_stream.pos]);
265 } else {
266 // Non-terminal node is delimited by 0 byte.
267 try writer.writeByte(0);
268 }
269 // Write number of edges (max legal number of edges is 256).
270 try writer.writeByte(@as(u8, @intCast(edges.items.len)));
271
272 for (edges.items) |edge_index| {
273 const edge = self.edges.items[edge_index];
274 // Write edge label and offset to next node in trie.
275 try writer.writeAll(edge.label);
276 try writer.writeByte(0);
277 try leb.writeULEB128(writer, slice.items(.trie_offset)[edge.node]);
278 }
279}
400280
401 try trie.put(gpa, .{
402 .name = "_main",
403 .vmaddr_offset = 0,
404 .export_flags = 0,
405 });
406 try testing.expectEqual(@as(usize, 2), trie.node_count);
281fn addNode(self: *Trie, allocator: Allocator) !Node.Index {
282 const index: Node.Index = @intCast(try self.nodes.addOne(allocator));
283 self.nodes.set(index, .{});
284 return index;
285}
407286
408 // Inserting the same node shouldn't update the trie.
409 try trie.put(gpa, .{
410 .name = "_main",
411 .vmaddr_offset = 0,
412 .export_flags = 0,
413 });
414 try testing.expectEqual(@as(usize, 2), trie.node_count);
287fn addEdge(self: *Trie, allocator: Allocator) !Edge.Index {
288 const index: Edge.Index = @intCast(self.edges.items.len);
289 const edge = try self.edges.addOne(allocator);
290 edge.* = .{};
291 return index;
292}
415293
416 try trie.put(gpa, .{
417 .name = "__mh_execute_header",
418 .vmaddr_offset = 0x1000,
419 .export_flags = 0,
420 });
421 try testing.expectEqual(@as(usize, 4), trie.node_count);
294/// Export symbol that is to be placed in the trie.
295pub const ExportSymbol = struct {
296 /// Name of the symbol.
297 name: []const u8,
422298
423 // Inserting the same node shouldn't update the trie.
424 try trie.put(gpa, .{
425 .name = "__mh_execute_header",
426 .vmaddr_offset = 0x1000,
427 .export_flags = 0,
428 });
429 try testing.expectEqual(@as(usize, 4), trie.node_count);
430 try trie.put(gpa, .{
431 .name = "_main",
432 .vmaddr_offset = 0,
433 .export_flags = 0,
434 });
435 try testing.expectEqual(@as(usize, 4), trie.node_count);
436}
299 /// Offset of this symbol's virtual memory address from the beginning
300 /// of the __TEXT segment.
301 vmaddr_offset: u64,
437302
438test "Trie basic" {
439 const gpa = testing.allocator;
440 var trie: Trie = .{};
441 defer trie.deinit(gpa);
442 try trie.init(gpa);
303 /// Export flags of this exported symbol.
304 export_flags: u64,
305};
443306
444 // root --- _st ---> node
445 try trie.put(gpa, .{
446 .name = "_st",
447 .vmaddr_offset = 0,
448 .export_flags = 0,
449 });
450 try testing.expect(trie.root.?.edges.items.len == 1);
451 try testing.expect(mem.eql(u8, trie.root.?.edges.items[0].label, "_st"));
452
453 {
454 // root --- _st ---> node --- art ---> node
455 try trie.put(gpa, .{
456 .name = "_start",
457 .vmaddr_offset = 0,
458 .export_flags = 0,
459 });
460 try testing.expect(trie.root.?.edges.items.len == 1);
307const Node = struct {
308 is_terminal: bool = false,
461309
462 const nextEdge = &trie.root.?.edges.items[0];
463 try testing.expect(mem.eql(u8, nextEdge.label, "_st"));
464 try testing.expect(nextEdge.to.edges.items.len == 1);
465 try testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "art"));
466 }
467 {
468 // root --- _ ---> node --- st ---> node --- art ---> node
469 // |
470 // | --- main ---> node
471 try trie.put(gpa, .{
472 .name = "_main",
473 .vmaddr_offset = 0,
474 .export_flags = 0,
475 });
476 try testing.expect(trie.root.?.edges.items.len == 1);
310 /// Export flags associated with this exported symbol.
311 export_flags: u64 = 0,
477312
478 const nextEdge = &trie.root.?.edges.items[0];
479 try testing.expect(mem.eql(u8, nextEdge.label, "_"));
480 try testing.expect(nextEdge.to.edges.items.len == 2);
481 try testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "st"));
482 try testing.expect(mem.eql(u8, nextEdge.to.edges.items[1].label, "main"));
313 /// VM address offset wrt to the section this symbol is defined against.
314 vmaddr_offset: u64 = 0,
483315
484 const nextNextEdge = &nextEdge.to.edges.items[0];
485 try testing.expect(mem.eql(u8, nextNextEdge.to.edges.items[0].label, "art"));
486 }
487}
316 /// Offset of this node in the trie output byte stream.
317 trie_offset: u32 = 0,
318
319 /// List of all edges originating from this node.
320 edges: std.ArrayListUnmanaged(Edge.Index) = .{},
321
322 const Index = u32;
323};
324
325/// Edge connecting nodes in the trie.
326const Edge = struct {
327 /// Target node in the trie.
328 node: Node.Index = 0,
329
330 /// Matching prefix.
331 label: []const u8 = "",
332
333 const Index = u32;
334};
488335
489336fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {
490337 assert(expected.len > 0);
......@@ -502,7 +349,7 @@ fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {
502349}
503350
504351test "write Trie to a byte stream" {
505 var gpa = testing.allocator;
352 const gpa = testing.allocator;
506353 var trie: Trie = .{};
507354 defer trie.deinit(gpa);
508355 try trie.init(gpa);
......@@ -519,7 +366,6 @@ test "write Trie to a byte stream" {
519366 });
520367
521368 try trie.finalize(gpa);
522 try trie.finalize(gpa); // Finalizing mulitple times is a nop subsequently unless we add new nodes.
523369
524370 const exp_buffer = [_]u8{
525371 0x0, 0x1, // node root
......@@ -531,51 +377,7 @@ test "write Trie to a byte stream" {
531377 0x2, 0x0, 0x0, 0x0, // terminal node
532378 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node
533379 };
534
535 const buffer = try gpa.alloc(u8, trie.size);
536 defer gpa.free(buffer);
537 var stream = std.io.fixedBufferStream(buffer);
538 {
539 _ = try trie.write(stream.writer());
540 try expectEqualHexStrings(&exp_buffer, buffer);
541 }
542 {
543 // Writing finalized trie again should yield the same result.
544 try stream.seekTo(0);
545 _ = try trie.write(stream.writer());
546 try expectEqualHexStrings(&exp_buffer, buffer);
547 }
548}
549
550test "parse Trie from byte stream" {
551 const gpa = testing.allocator;
552
553 const in_buffer = [_]u8{
554 0x0, 0x1, // node root
555 0x5f, 0x0, 0x5, // edge '_'
556 0x0, 0x2, // non-terminal node
557 0x5f, 0x6d, 0x68, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, // edge '_mh_execute_header'
558 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0, 0x21, // edge '_mh_execute_header'
559 0x6d, 0x61, 0x69, 0x6e, 0x0, 0x25, // edge 'main'
560 0x2, 0x0, 0x0, 0x0, // terminal node
561 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node
562 };
563
564 var in_stream = std.io.fixedBufferStream(&in_buffer);
565 var trie: Trie = .{};
566 defer trie.deinit(gpa);
567 try trie.init(gpa);
568 const nread = try trie.read(gpa, in_stream.reader());
569
570 try testing.expect(nread == in_buffer.len);
571
572 try trie.finalize(gpa);
573
574 const out_buffer = try gpa.alloc(u8, trie.size);
575 defer gpa.free(out_buffer);
576 var out_stream = std.io.fixedBufferStream(out_buffer);
577 _ = try trie.write(out_stream.writer());
578 try expectEqualHexStrings(&in_buffer, out_buffer);
380 try expectEqualHexStrings(&exp_buffer, trie.buffer.items);
579381}
580382
581383test "ordering bug" {
......@@ -602,11 +404,18 @@ test "ordering bug" {
602404 0x88, 0x80, 0x02, 0x01, 0x73, 0x53, 0x74, 0x72,
603405 0x00, 0x12, 0x03, 0x00, 0xD8, 0x0A, 0x00,
604406 };
605
606 const buffer = try gpa.alloc(u8, trie.size);
607 defer gpa.free(buffer);
608 var stream = std.io.fixedBufferStream(buffer);
609 // Writing finalized trie again should yield the same result.
610 _ = try trie.write(stream.writer());
611 try expectEqualHexStrings(&exp_buffer, buffer);
407 try expectEqualHexStrings(&exp_buffer, trie.buffer.items);
612408}
409
410const assert = std.debug.assert;
411const leb = std.leb;
412const log = std.log.scoped(.macho);
413const macho = std.macho;
414const mem = std.mem;
415const std = @import("std");
416const testing = std.testing;
417const trace = @import("../../../tracy.zig").trace;
418
419const Allocator = mem.Allocator;
420const MachO = @import("../../MachO.zig");
421const Trie = @This();
src/link/MachO/dyld_info/bind.zig+259-34
......@@ -1,28 +1,19 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const leb = std.leb;
4const log = std.log.scoped(.link_dyld_info);
5const macho = std.macho;
6const testing = std.testing;
7
8const Allocator = std.mem.Allocator;
9const MachO = @import("../../MachO.zig");
10const Symbol = @import("../Symbol.zig");
11
121pub const Entry = struct {
13 target: Symbol.Index,
2 target: MachO.Ref,
143 offset: u64,
154 segment_id: u8,
165 addend: i64,
176
187 pub fn lessThan(ctx: *MachO, entry: Entry, other: Entry) bool {
8 _ = ctx;
199 if (entry.segment_id == other.segment_id) {
20 if (entry.target == other.target) {
10 if (entry.target.eql(other.target)) {
2111 return entry.offset < other.offset;
2212 }
23 const entry_name = ctx.getSymbol(entry.target).getName(ctx);
24 const other_name = ctx.getSymbol(other.target).getName(ctx);
25 return std.mem.lessThan(u8, entry_name, other_name);
13 if (entry.target.file == other.target.file) {
14 return entry.target.index < other.target.index;
15 }
16 return entry.target.file < other.target.file;
2617 }
2718 return entry.segment_id < other.segment_id;
2819 }
......@@ -39,11 +30,109 @@ pub const Bind = struct {
3930 self.buffer.deinit(gpa);
4031 }
4132
42 pub fn size(self: Self) u64 {
43 return @intCast(self.buffer.items.len);
33 pub fn updateSize(self: *Self, macho_file: *MachO) !void {
34 const tracy = trace(@src());
35 defer tracy.end();
36
37 const gpa = macho_file.base.comp.gpa;
38 const cpu_arch = macho_file.getTarget().cpu.arch;
39
40 var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 2);
41 defer objects.deinit();
42 objects.appendSliceAssumeCapacity(macho_file.objects.items);
43 if (macho_file.getZigObject()) |obj| objects.appendAssumeCapacity(obj.index);
44 if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index);
45
46 for (objects.items) |index| {
47 const file = macho_file.getFile(index).?;
48 for (file.getAtoms()) |atom_index| {
49 const atom = file.getAtom(atom_index) orelse continue;
50 if (!atom.flags.alive) continue;
51 if (atom.getInputSection(macho_file).isZerofill()) continue;
52 const atom_addr = atom.getAddress(macho_file);
53 const relocs = atom.getRelocs(macho_file);
54 const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
55 const seg = macho_file.segments.items[seg_id];
56 for (relocs) |rel| {
57 if (rel.type != .unsigned or rel.meta.length != 3 or rel.tag != .@"extern") continue;
58 const rel_offset = rel.offset - atom.off;
59 const addend = rel.addend + rel.getRelocAddend(cpu_arch);
60 const sym = rel.getTargetSymbol(atom.*, macho_file);
61 if (sym.isTlvInit(macho_file)) continue;
62 const entry = Entry{
63 .target = rel.getTargetSymbolRef(atom.*, macho_file),
64 .offset = atom_addr + rel_offset - seg.vmaddr,
65 .segment_id = seg_id,
66 .addend = addend,
67 };
68 if (sym.flags.import or (!(sym.flags.@"export" and sym.flags.weak) and sym.flags.interposable)) {
69 try self.entries.append(gpa, entry);
70 }
71 }
72 }
73 }
74
75 if (macho_file.got_sect_index) |sid| {
76 const seg_id = macho_file.sections.items(.segment_id)[sid];
77 const seg = macho_file.segments.items[seg_id];
78 for (macho_file.got.symbols.items, 0..) |ref, idx| {
79 const sym = ref.getSymbol(macho_file).?;
80 const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
81 const entry = Entry{
82 .target = ref,
83 .offset = addr - seg.vmaddr,
84 .segment_id = seg_id,
85 .addend = 0,
86 };
87 if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) {
88 try self.entries.append(gpa, entry);
89 }
90 }
91 }
92
93 if (macho_file.la_symbol_ptr_sect_index) |sid| {
94 const sect = macho_file.sections.items(.header)[sid];
95 const seg_id = macho_file.sections.items(.segment_id)[sid];
96 const seg = macho_file.segments.items[seg_id];
97 for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
98 const sym = ref.getSymbol(macho_file).?;
99 const addr = sect.addr + idx * @sizeOf(u64);
100 const bind_entry = Entry{
101 .target = ref,
102 .offset = addr - seg.vmaddr,
103 .segment_id = seg_id,
104 .addend = 0,
105 };
106 if (sym.flags.import and sym.flags.weak) {
107 try self.entries.append(gpa, bind_entry);
108 }
109 }
110 }
111
112 if (macho_file.tlv_ptr_sect_index) |sid| {
113 const seg_id = macho_file.sections.items(.segment_id)[sid];
114 const seg = macho_file.segments.items[seg_id];
115
116 for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
117 const sym = ref.getSymbol(macho_file).?;
118 const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
119 const entry = Entry{
120 .target = ref,
121 .offset = addr - seg.vmaddr,
122 .segment_id = seg_id,
123 .addend = 0,
124 };
125 if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) {
126 try self.entries.append(gpa, entry);
127 }
128 }
129 }
130
131 try self.finalize(gpa, macho_file);
132 macho_file.dyld_info_cmd.bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
44133 }
45134
46 pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
135 fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
47136 if (self.entries.items.len == 0) return;
48137
49138 const writer = self.buffer.writer(gpa);
......@@ -75,7 +164,7 @@ pub const Bind = struct {
75164 var addend: i64 = 0;
76165 var count: usize = 0;
77166 var skip: u64 = 0;
78 var target: ?Symbol.Index = null;
167 var target: ?MachO.Ref = null;
79168
80169 var state: enum {
81170 start,
......@@ -86,7 +175,7 @@ pub const Bind = struct {
86175 var i: usize = 0;
87176 while (i < entries.len) : (i += 1) {
88177 const current = entries[i];
89 if (target == null or target.? != current.target) {
178 if (target == null or !target.?.eql(current.target)) {
90179 switch (state) {
91180 .start => {},
92181 .bind_single => try doBind(writer),
......@@ -95,7 +184,7 @@ pub const Bind = struct {
95184 state = .start;
96185 target = current.target;
97186
98 const sym = ctx.getSymbol(current.target);
187 const sym = current.target.getSymbol(ctx).?;
99188 const name = sym.getName(ctx);
100189 const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
101190 const ordinal: i16 = ord: {
......@@ -178,7 +267,6 @@ pub const Bind = struct {
178267 }
179268
180269 pub fn write(self: Self, writer: anytype) !void {
181 if (self.size() == 0) return;
182270 try writer.writeAll(self.buffer.items);
183271 }
184272};
......@@ -194,11 +282,110 @@ pub const WeakBind = struct {
194282 self.buffer.deinit(gpa);
195283 }
196284
197 pub fn size(self: Self) u64 {
198 return @intCast(self.buffer.items.len);
285 pub fn updateSize(self: *Self, macho_file: *MachO) !void {
286 const tracy = trace(@src());
287 defer tracy.end();
288
289 const gpa = macho_file.base.comp.gpa;
290 const cpu_arch = macho_file.getTarget().cpu.arch;
291
292 var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 2);
293 defer objects.deinit();
294 objects.appendSliceAssumeCapacity(macho_file.objects.items);
295 if (macho_file.getZigObject()) |obj| objects.appendAssumeCapacity(obj.index);
296 if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index);
297
298 for (objects.items) |index| {
299 const file = macho_file.getFile(index).?;
300 for (file.getAtoms()) |atom_index| {
301 const atom = file.getAtom(atom_index) orelse continue;
302 if (!atom.flags.alive) continue;
303 if (atom.getInputSection(macho_file).isZerofill()) continue;
304 const atom_addr = atom.getAddress(macho_file);
305 const relocs = atom.getRelocs(macho_file);
306 const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
307 const seg = macho_file.segments.items[seg_id];
308 for (relocs) |rel| {
309 if (rel.type != .unsigned or rel.meta.length != 3 or rel.tag != .@"extern") continue;
310 const rel_offset = rel.offset - atom.off;
311 const addend = rel.addend + rel.getRelocAddend(cpu_arch);
312 const sym = rel.getTargetSymbol(atom.*, macho_file);
313 if (sym.isTlvInit(macho_file)) continue;
314 const entry = Entry{
315 .target = rel.getTargetSymbolRef(atom.*, macho_file),
316 .offset = atom_addr + rel_offset - seg.vmaddr,
317 .segment_id = seg_id,
318 .addend = addend,
319 };
320 if (!sym.isLocal() and sym.flags.weak) {
321 try self.entries.append(gpa, entry);
322 }
323 }
324 }
325 }
326
327 if (macho_file.got_sect_index) |sid| {
328 const seg_id = macho_file.sections.items(.segment_id)[sid];
329 const seg = macho_file.segments.items[seg_id];
330 for (macho_file.got.symbols.items, 0..) |ref, idx| {
331 const sym = ref.getSymbol(macho_file).?;
332 const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
333 const entry = Entry{
334 .target = ref,
335 .offset = addr - seg.vmaddr,
336 .segment_id = seg_id,
337 .addend = 0,
338 };
339 if (sym.flags.weak) {
340 try self.entries.append(gpa, entry);
341 }
342 }
343 }
344
345 if (macho_file.la_symbol_ptr_sect_index) |sid| {
346 const sect = macho_file.sections.items(.header)[sid];
347 const seg_id = macho_file.sections.items(.segment_id)[sid];
348 const seg = macho_file.segments.items[seg_id];
349
350 for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
351 const sym = ref.getSymbol(macho_file).?;
352 const addr = sect.addr + idx * @sizeOf(u64);
353 const bind_entry = Entry{
354 .target = ref,
355 .offset = addr - seg.vmaddr,
356 .segment_id = seg_id,
357 .addend = 0,
358 };
359 if (sym.flags.weak) {
360 try self.entries.append(gpa, bind_entry);
361 }
362 }
363 }
364
365 if (macho_file.tlv_ptr_sect_index) |sid| {
366 const seg_id = macho_file.sections.items(.segment_id)[sid];
367 const seg = macho_file.segments.items[seg_id];
368
369 for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
370 const sym = ref.getSymbol(macho_file).?;
371 const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
372 const entry = Entry{
373 .target = ref,
374 .offset = addr - seg.vmaddr,
375 .segment_id = seg_id,
376 .addend = 0,
377 };
378 if (sym.flags.weak) {
379 try self.entries.append(gpa, entry);
380 }
381 }
382 }
383
384 try self.finalize(gpa, macho_file);
385 macho_file.dyld_info_cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
199386 }
200387
201 pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
388 fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
202389 if (self.entries.items.len == 0) return;
203390
204391 const writer = self.buffer.writer(gpa);
......@@ -230,7 +417,7 @@ pub const WeakBind = struct {
230417 var addend: i64 = 0;
231418 var count: usize = 0;
232419 var skip: u64 = 0;
233 var target: ?Symbol.Index = null;
420 var target: ?MachO.Ref = null;
234421
235422 var state: enum {
236423 start,
......@@ -241,7 +428,7 @@ pub const WeakBind = struct {
241428 var i: usize = 0;
242429 while (i < entries.len) : (i += 1) {
243430 const current = entries[i];
244 if (target == null or target.? != current.target) {
431 if (target == null or !target.?.eql(current.target)) {
245432 switch (state) {
246433 .start => {},
247434 .bind_single => try doBind(writer),
......@@ -250,7 +437,7 @@ pub const WeakBind = struct {
250437 state = .start;
251438 target = current.target;
252439
253 const sym = ctx.getSymbol(current.target);
440 const sym = current.target.getSymbol(ctx).?;
254441 const name = sym.getName(ctx);
255442 const flags: u8 = 0; // TODO NON_WEAK_DEFINITION
256443
......@@ -322,7 +509,6 @@ pub const WeakBind = struct {
322509 }
323510
324511 pub fn write(self: Self, writer: anytype) !void {
325 if (self.size() == 0) return;
326512 try writer.writeAll(self.buffer.items);
327513 }
328514};
......@@ -340,11 +526,36 @@ pub const LazyBind = struct {
340526 self.offsets.deinit(gpa);
341527 }
342528
343 pub fn size(self: Self) u64 {
344 return @intCast(self.buffer.items.len);
529 pub fn updateSize(self: *Self, macho_file: *MachO) !void {
530 const tracy = trace(@src());
531 defer tracy.end();
532
533 const gpa = macho_file.base.comp.gpa;
534
535 const sid = macho_file.la_symbol_ptr_sect_index.?;
536 const sect = macho_file.sections.items(.header)[sid];
537 const seg_id = macho_file.sections.items(.segment_id)[sid];
538 const seg = macho_file.segments.items[seg_id];
539
540 for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
541 const sym = ref.getSymbol(macho_file).?;
542 const addr = sect.addr + idx * @sizeOf(u64);
543 const bind_entry = Entry{
544 .target = ref,
545 .offset = addr - seg.vmaddr,
546 .segment_id = seg_id,
547 .addend = 0,
548 };
549 if ((sym.flags.import and !sym.flags.weak) or (sym.flags.interposable and !sym.flags.weak)) {
550 try self.entries.append(gpa, bind_entry);
551 }
552 }
553
554 try self.finalize(gpa, macho_file);
555 macho_file.dyld_info_cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
345556 }
346557
347 pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
558 fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
348559 try self.offsets.ensureTotalCapacityPrecise(gpa, self.entries.items.len);
349560
350561 const writer = self.buffer.writer(gpa);
......@@ -356,7 +567,7 @@ pub const LazyBind = struct {
356567 for (self.entries.items) |entry| {
357568 self.offsets.appendAssumeCapacity(@intCast(self.buffer.items.len));
358569
359 const sym = ctx.getSymbol(entry.target);
570 const sym = entry.target.getSymbol(ctx).?;
360571 const name = sym.getName(ctx);
361572 const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
362573 const ordinal: i16 = ord: {
......@@ -474,3 +685,17 @@ fn done(writer: anytype) !void {
474685 log.debug(">>> done", .{});
475686 try writer.writeByte(macho.BIND_OPCODE_DONE);
476687}
688
689const assert = std.debug.assert;
690const leb = std.leb;
691const log = std.log.scoped(.link_dyld_info);
692const macho = std.macho;
693const mem = std.mem;
694const testing = std.testing;
695const trace = @import("../../../tracy.zig").trace;
696const std = @import("std");
697
698const Allocator = mem.Allocator;
699const File = @import("../file.zig").File;
700const MachO = @import("../../MachO.zig");
701const Symbol = @import("../Symbol.zig");
src/link/MachO/eh_frame.zig+11-6
......@@ -68,7 +68,8 @@ pub const Cie = struct {
6868
6969 pub fn getPersonality(cie: Cie, macho_file: *MachO) ?*Symbol {
7070 const personality = cie.personality orelse return null;
71 return macho_file.getSymbol(personality.index);
71 const object = cie.getObject(macho_file);
72 return object.getSymbolRef(personality.index, macho_file).getSymbol(macho_file);
7273 }
7374
7475 pub fn eql(cie: Cie, other: Cie, macho_file: *MachO) bool {
......@@ -223,11 +224,11 @@ pub const Fde = struct {
223224 }
224225
225226 pub fn getAtom(fde: Fde, macho_file: *MachO) *Atom {
226 return macho_file.getAtom(fde.atom).?;
227 return fde.getObject(macho_file).getAtom(fde.atom).?;
227228 }
228229
229230 pub fn getLsdaAtom(fde: Fde, macho_file: *MachO) ?*Atom {
230 return macho_file.getAtom(fde.lsda);
231 return fde.getObject(macho_file).getAtom(fde.lsda);
231232 }
232233
233234 pub fn format(
......@@ -448,7 +449,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {
448449 }
449450}
450451
451pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.relocation_info)) error{Overflow}!void {
452pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: []macho.relocation_info) error{Overflow}!void {
452453 const tracy = trace(@src());
453454 defer tracy.end();
454455
......@@ -459,6 +460,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
459460 else => 0,
460461 };
461462
463 var i: usize = 0;
462464 for (macho_file.objects.items) |index| {
463465 const object = macho_file.getFile(index).?.object;
464466 for (object.cies.items) |cie| {
......@@ -469,7 +471,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
469471 if (cie.getPersonality(macho_file)) |sym| {
470472 const r_address = math.cast(i32, cie.out_offset + cie.personality.?.offset) orelse return error.Overflow;
471473 const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
472 relocs.appendAssumeCapacity(.{
474 relocs[i] = .{
473475 .r_address = r_address,
474476 .r_symbolnum = r_symbolnum,
475477 .r_length = 2,
......@@ -480,7 +482,8 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
480482 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
481483 else => unreachable,
482484 },
483 });
485 };
486 i += 1;
484487 }
485488 }
486489 }
......@@ -531,6 +534,8 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
531534 }
532535 }
533536 }
537
538 assert(relocs.len == i);
534539}
535540
536541pub const EH_PE = struct {
src/link/MachO/file.zig+224-97
......@@ -24,41 +24,194 @@ pub const File = union(enum) {
2424 _ = options;
2525 switch (file) {
2626 .zig_object => |x| try writer.writeAll(x.path),
27 .internal => try writer.writeAll(""),
27 .internal => try writer.writeAll("internal"),
2828 .object => |x| try writer.print("{}", .{x.fmtPath()}),
2929 .dylib => |x| try writer.writeAll(x.path),
3030 }
3131 }
3232
33 pub fn resolveSymbols(file: File, macho_file: *MachO) void {
34 switch (file) {
35 .internal => unreachable,
33 pub fn resolveSymbols(file: File, macho_file: *MachO) !void {
34 return switch (file) {
3635 inline else => |x| x.resolveSymbols(macho_file),
37 }
36 };
3837 }
3938
40 pub fn resetGlobals(file: File, macho_file: *MachO) void {
39 pub fn scanRelocs(file: File, macho_file: *MachO) !void {
4140 switch (file) {
42 .internal => unreachable,
43 inline else => |x| x.resetGlobals(macho_file),
41 .dylib => unreachable,
42 .internal => |x| x.scanRelocs(macho_file),
43 inline else => |x| x.scanRelocs(macho_file),
44 }
45 }
46
47 /// Encodes symbol rank so that the following ordering applies:
48 /// * strong in object
49 /// * weak in object
50 /// * tentative in object
51 /// * strong in archive/dylib
52 /// * weak in archive/dylib
53 /// * tentative in archive
54 /// * unclaimed
55 pub fn getSymbolRank(file: File, args: struct {
56 archive: bool = false,
57 weak: bool = false,
58 tentative: bool = false,
59 }) u32 {
60 if (file != .dylib and !args.archive) {
61 const base: u32 = blk: {
62 if (args.tentative) break :blk 3;
63 break :blk if (args.weak) 2 else 1;
64 };
65 return (base << 16) + file.getIndex();
66 }
67 const base: u32 = blk: {
68 if (args.tentative) break :blk 3;
69 break :blk if (args.weak) 2 else 1;
70 };
71 return base + (file.getIndex() << 24);
72 }
73
74 pub fn getAtom(file: File, atom_index: Atom.Index) ?*Atom {
75 return switch (file) {
76 .dylib => unreachable,
77 inline else => |x| x.getAtom(atom_index),
78 };
79 }
80
81 pub fn getAtoms(file: File) []const Atom.Index {
82 return switch (file) {
83 .dylib => unreachable,
84 inline else => |x| x.getAtoms(),
85 };
86 }
87
88 pub fn addAtomExtra(file: File, allocator: Allocator, extra: Atom.Extra) !u32 {
89 return switch (file) {
90 .dylib => unreachable,
91 inline else => |x| x.addAtomExtra(allocator, extra),
92 };
93 }
94
95 pub fn getAtomExtra(file: File, index: u32) Atom.Extra {
96 return switch (file) {
97 .dylib => unreachable,
98 inline else => |x| x.getAtomExtra(index),
99 };
100 }
101
102 pub fn setAtomExtra(file: File, index: u32, extra: Atom.Extra) void {
103 return switch (file) {
104 .dylib => unreachable,
105 inline else => |x| x.setAtomExtra(index, extra),
106 };
107 }
108
109 pub fn getSymbols(file: File) []Symbol {
110 return switch (file) {
111 inline else => |x| x.symbols.items,
112 };
113 }
114
115 pub fn getSymbolRef(file: File, sym_index: Symbol.Index, macho_file: *MachO) MachO.Ref {
116 return switch (file) {
117 inline else => |x| x.getSymbolRef(sym_index, macho_file),
118 };
119 }
120
121 pub fn getNlists(file: File) []macho.nlist_64 {
122 return switch (file) {
123 .dylib => unreachable,
124 .internal => |x| x.symtab.items,
125 inline else => |x| x.symtab.items(.nlist),
126 };
127 }
128
129 pub fn getGlobals(file: File) []MachO.SymbolResolver.Index {
130 return switch (file) {
131 inline else => |x| x.globals.items,
132 };
133 }
134
135 pub fn markImportsExports(file: File, macho_file: *MachO) void {
136 const tracy = trace(@src());
137 defer tracy.end();
138
139 const nsyms = switch (file) {
140 .dylib => unreachable,
141 inline else => |x| x.symbols.items.len,
142 };
143 for (0..nsyms) |i| {
144 const ref = file.getSymbolRef(@intCast(i), macho_file);
145 if (ref.getFile(macho_file) == null) continue;
146 const sym = ref.getSymbol(macho_file).?;
147 if (sym.visibility != .global) continue;
148 if (sym.getFile(macho_file).? == .dylib and !sym.flags.abs) {
149 sym.flags.import = true;
150 continue;
151 }
152 if (file.getIndex() == ref.file) {
153 sym.flags.@"export" = true;
154 }
44155 }
45156 }
46157
47 pub fn claimUnresolved(file: File, macho_file: *MachO) error{OutOfMemory}!void {
158 pub fn markExportsRelocatable(file: File, macho_file: *MachO) void {
159 const tracy = trace(@src());
160 defer tracy.end();
161
48162 assert(file == .object or file == .zig_object);
49163
50 for (file.getSymbols(), 0..) |sym_index, i| {
51 const nlist_idx = @as(Symbol.Index, @intCast(i));
52 const nlist = switch (file) {
53 .object => |x| x.symtab.items(.nlist)[nlist_idx],
54 .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],
55 else => unreachable,
56 };
164 for (file.getSymbols(), 0..) |*sym, i| {
165 const ref = file.getSymbolRef(@intCast(i), macho_file);
166 const other_file = ref.getFile(macho_file) orelse continue;
167 if (other_file.getIndex() != file.getIndex()) continue;
168 if (sym.visibility != .global) continue;
169 sym.flags.@"export" = true;
170 }
171 }
172
173 pub fn createSymbolIndirection(file: File, macho_file: *MachO) !void {
174 const tracy = trace(@src());
175 defer tracy.end();
176
177 const nsyms = switch (file) {
178 inline else => |x| x.symbols.items.len,
179 };
180 for (0..nsyms) |i| {
181 const ref = file.getSymbolRef(@intCast(i), macho_file);
182 if (ref.getFile(macho_file) == null) continue;
183 if (ref.file != file.getIndex()) continue;
184 const sym = ref.getSymbol(macho_file).?;
185 if (sym.flags.needs_got) {
186 log.debug("'{s}' needs GOT", .{sym.getName(macho_file)});
187 try macho_file.got.addSymbol(ref, macho_file);
188 }
189 if (sym.flags.stubs) {
190 log.debug("'{s}' needs STUBS", .{sym.getName(macho_file)});
191 try macho_file.stubs.addSymbol(ref, macho_file);
192 }
193 if (sym.flags.tlv_ptr) {
194 log.debug("'{s}' needs TLV pointer", .{sym.getName(macho_file)});
195 try macho_file.tlv_ptr.addSymbol(ref, macho_file);
196 }
197 if (sym.flags.objc_stubs) {
198 log.debug("'{s}' needs OBJC STUBS", .{sym.getName(macho_file)});
199 try macho_file.objc_stubs.addSymbol(ref, macho_file);
200 }
201 }
202 }
203
204 pub fn claimUnresolved(file: File, macho_file: *MachO) void {
205 const tracy = trace(@src());
206 defer tracy.end();
207
208 assert(file == .object or file == .zig_object);
209
210 for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| {
57211 if (!nlist.ext()) continue;
58212 if (!nlist.undf()) continue;
59213
60 const sym = macho_file.getSymbol(sym_index);
61 if (sym.getFile(macho_file) != null) continue;
214 if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
62215
63216 const is_import = switch (macho_file.undefined_treatment) {
64217 .@"error" => false,
......@@ -67,111 +220,95 @@ pub const File = union(enum) {
67220 };
68221 if (is_import) {
69222 sym.value = 0;
70 sym.atom = 0;
71 sym.nlist_idx = 0;
72 sym.file = macho_file.internal_object.?;
223 sym.atom_ref = .{ .index = 0, .file = 0 };
73224 sym.flags.weak = false;
74225 sym.flags.weak_ref = nlist.weakRef();
75226 sym.flags.import = is_import;
76227 sym.visibility = .global;
77 try macho_file.getInternalObject().?.symbols.append(macho_file.base.comp.gpa, sym_index);
228
229 const idx = file.getGlobals()[i];
230 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() };
78231 }
79232 }
80233 }
81234
82235 pub fn claimUnresolvedRelocatable(file: File, macho_file: *MachO) void {
236 const tracy = trace(@src());
237 defer tracy.end();
238
83239 assert(file == .object or file == .zig_object);
84240
85 for (file.getSymbols(), 0..) |sym_index, i| {
86 const nlist_idx = @as(Symbol.Index, @intCast(i));
87 const nlist = switch (file) {
88 .object => |x| x.symtab.items(.nlist)[nlist_idx],
89 .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],
90 else => unreachable,
91 };
241 for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| {
92242 if (!nlist.ext()) continue;
93243 if (!nlist.undf()) continue;
94
95 const sym = macho_file.getSymbol(sym_index);
96 if (sym.getFile(macho_file) != null) continue;
244 if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
97245
98246 sym.value = 0;
99 sym.atom = 0;
100 sym.nlist_idx = nlist_idx;
101 sym.file = file.getIndex();
247 sym.atom_ref = .{ .index = 0, .file = 0 };
102248 sym.flags.weak_ref = nlist.weakRef();
103249 sym.flags.import = true;
104250 sym.visibility = .global;
251
252 const idx = file.getGlobals()[i];
253 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() };
105254 }
106255 }
107256
108 pub fn markImportsExports(file: File, macho_file: *MachO) void {
109 assert(file == .object or file == .zig_object);
257 pub fn checkDuplicates(file: File, macho_file: *MachO) !void {
258 const tracy = trace(@src());
259 defer tracy.end();
260
261 const gpa = macho_file.base.comp.gpa;
110262
111 for (file.getSymbols()) |sym_index| {
112 const sym = macho_file.getSymbol(sym_index);
113 const other_file = sym.getFile(macho_file) orelse continue;
263 for (file.getSymbols(), file.getNlists(), 0..) |sym, nlist, i| {
114264 if (sym.visibility != .global) continue;
115 if (other_file == .dylib and !sym.flags.abs) {
116 sym.flags.import = true;
117 continue;
118 }
119 if (other_file.getIndex() == file.getIndex()) {
120 sym.flags.@"export" = true;
265 if (sym.flags.weak) continue;
266 if (nlist.undf()) continue;
267 const ref = file.getSymbolRef(@intCast(i), macho_file);
268 const ref_file = ref.getFile(macho_file) orelse continue;
269 if (ref_file.getIndex() == file.getIndex()) continue;
270
271 const gop = try macho_file.dupes.getOrPut(gpa, file.getGlobals()[i]);
272 if (!gop.found_existing) {
273 gop.value_ptr.* = .{};
121274 }
275 try gop.value_ptr.append(gpa, file.getIndex());
122276 }
123277 }
124278
125 pub fn markExportsRelocatable(file: File, macho_file: *MachO) void {
126 assert(file == .object or file == .zig_object);
127
128 for (file.getSymbols()) |sym_index| {
129 const sym = macho_file.getSymbol(sym_index);
130 const other_file = sym.getFile(macho_file) orelse continue;
131 if (sym.visibility != .global) continue;
132 if (other_file.getIndex() == file.getIndex()) {
133 sym.flags.@"export" = true;
134 }
279 pub fn initOutputSections(file: File, macho_file: *MachO) !void {
280 const tracy = trace(@src());
281 defer tracy.end();
282 for (file.getAtoms()) |atom_index| {
283 const atom = file.getAtom(atom_index) orelse continue;
284 if (!atom.flags.alive) continue;
285 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
135286 }
136287 }
137288
138 /// Encodes symbol rank so that the following ordering applies:
139 /// * strong in object
140 /// * weak in object
141 /// * tentative in object
142 /// * strong in archive/dylib
143 /// * weak in archive/dylib
144 /// * tentative in archive
145 /// * unclaimed
146 pub fn getSymbolRank(file: File, args: struct {
147 archive: bool = false,
148 weak: bool = false,
149 tentative: bool = false,
150 }) u32 {
151 if (file == .object and !args.archive) {
152 const base: u32 = blk: {
153 if (args.tentative) break :blk 3;
154 break :blk if (args.weak) 2 else 1;
155 };
156 return (base << 16) + file.getIndex();
157 }
158 const base: u32 = blk: {
159 if (args.tentative) break :blk 3;
160 break :blk if (args.weak) 2 else 1;
289 pub fn dedupLiterals(file: File, lp: MachO.LiteralPool, macho_file: *MachO) void {
290 return switch (file) {
291 .dylib => unreachable,
292 inline else => |x| x.dedupLiterals(lp, macho_file),
161293 };
162 return base + (file.getIndex() << 24);
163294 }
164295
165 pub fn getSymbols(file: File) []const Symbol.Index {
296 pub fn writeAtoms(file: File, macho_file: *MachO) !void {
166297 return switch (file) {
167 inline else => |x| x.symbols.items,
298 .dylib, .zig_object => unreachable,
299 inline else => |x| x.writeAtoms(macho_file),
168300 };
169301 }
170302
171 pub fn getAtoms(file: File) []const Atom.Index {
303 pub fn calcSymtabSize(file: File, macho_file: *MachO) void {
172304 return switch (file) {
173 .dylib => unreachable,
174 inline else => |x| x.atoms.items,
305 inline else => |x| x.calcSymtabSize(macho_file),
306 };
307 }
308
309 pub fn writeSymtab(file: File, macho_file: *MachO, ctx: anytype) void {
310 return switch (file) {
311 inline else => |x| x.writeSymtab(macho_file, ctx),
175312 };
176313 }
177314
......@@ -198,18 +335,6 @@ pub const File = union(enum) {
198335 };
199336 }
200337
201 pub fn calcSymtabSize(file: File, macho_file: *MachO) !void {
202 return switch (file) {
203 inline else => |x| x.calcSymtabSize(macho_file),
204 };
205 }
206
207 pub fn writeSymtab(file: File, macho_file: *MachO, ctx: anytype) !void {
208 return switch (file) {
209 inline else => |x| x.writeSymtab(macho_file, ctx),
210 };
211 }
212
213338 pub const Index = u32;
214339
215340 pub const Entry = union(enum) {
......@@ -225,8 +350,10 @@ pub const File = union(enum) {
225350};
226351
227352const assert = std.debug.assert;
353const log = std.log.scoped(.link);
228354const macho = std.macho;
229355const std = @import("std");
356const trace = @import("../../tracy.zig").trace;
230357
231358const Allocator = std.mem.Allocator;
232359const Archive = @import("Archive.zig");
src/link/MachO/relocatable.zig+222-286
......@@ -44,9 +44,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
4444
4545 if (comp.link_errors.items.len > 0) return error.FlushFailure;
4646
47 try macho_file.addUndefinedGlobals();
4847 try macho_file.resolveSymbols();
49 try macho_file.parseDebugInfo();
5048 try macho_file.dedupLiterals();
5149 markExports(macho_file);
5250 claimUnresolved(macho_file);
......@@ -59,28 +57,13 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
5957 try allocateSections(macho_file);
6058 allocateSegment(macho_file);
6159
62 var off = off: {
63 const seg = macho_file.segments.items[0];
64 const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow;
65 break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info));
66 };
67 off = allocateSectionsRelocs(macho_file, off);
68
6960 if (build_options.enable_logging) {
7061 state_log.debug("{}", .{macho_file.dumpState()});
7162 }
7263
73 try macho_file.calcSymtabSize();
74 try writeAtoms(macho_file);
75 try writeCompactUnwind(macho_file);
76 try writeEhFrame(macho_file);
77
78 off = mem.alignForward(u32, off, @alignOf(u64));
79 off = try macho_file.writeDataInCode(0, off);
80 off = mem.alignForward(u32, off, @alignOf(u64));
81 off = try macho_file.writeSymtab(off);
82 off = mem.alignForward(u32, off, @alignOf(u64));
83 off = try macho_file.writeStrtab(off);
64 try writeSections(macho_file);
65 sortRelocs(macho_file);
66 try writeSectionsToFile(macho_file);
8467
8568 // In order to please Apple ld (and possibly other MachO linkers in the wild),
8669 // we will now sanitize segment names of Zig-specific segments.
......@@ -129,7 +112,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
129112
130113 // First, we flush relocatable object file generated with our backends.
131114 if (macho_file.getZigObject()) |zo| {
132 zo.resolveSymbols(macho_file);
115 try zo.resolveSymbols(macho_file);
133116 zo.asFile().markExportsRelocatable(macho_file);
134117 zo.asFile().claimUnresolvedRelocatable(macho_file);
135118 try macho_file.sortSections();
......@@ -139,26 +122,13 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
139122 try allocateSections(macho_file);
140123 allocateSegment(macho_file);
141124
142 var off = off: {
143 const seg = macho_file.segments.items[0];
144 const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow;
145 break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info));
146 };
147 off = allocateSectionsRelocs(macho_file, off);
148
149125 if (build_options.enable_logging) {
150126 state_log.debug("{}", .{macho_file.dumpState()});
151127 }
152128
153 try macho_file.calcSymtabSize();
154 try writeAtoms(macho_file);
155
156 off = mem.alignForward(u32, off, @alignOf(u64));
157 off = try macho_file.writeDataInCode(0, off);
158 off = mem.alignForward(u32, off, @alignOf(u64));
159 off = try macho_file.writeSymtab(off);
160 off = mem.alignForward(u32, off, @alignOf(u64));
161 off = try macho_file.writeStrtab(off);
129 try writeSections(macho_file);
130 sortRelocs(macho_file);
131 try writeSectionsToFile(macho_file);
162132
163133 // In order to please Apple ld (and possibly other MachO linkers in the wild),
164134 // we will now sanitize segment names of Zig-specific segments.
......@@ -169,7 +139,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
169139
170140 // TODO we can avoid reading in the file contents we just wrote if we give the linker
171141 // ability to write directly to a buffer.
172 try zo.readFileContents(off, macho_file);
142 try zo.readFileContents(macho_file);
173143 }
174144
175145 var files = std.ArrayList(File.Index).init(gpa);
......@@ -286,12 +256,15 @@ fn parseObject(macho_file: *MachO, path: []const u8) MachO.ParseError!void {
286256 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
287257 };
288258 const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
289 macho_file.files.set(index, .{ .object = .{
290 .path = try gpa.dupe(u8, path),
291 .file_handle = handle,
292 .mtime = mtime,
293 .index = index,
294 } });
259 macho_file.files.set(index, .{
260 .object = .{
261 .offset = 0, // TODO FAT objects
262 .path = try gpa.dupe(u8, path),
263 .file_handle = handle,
264 .mtime = mtime,
265 .index = index,
266 },
267 });
295268 try macho_file.objects.append(gpa, index);
296269
297270 const object = macho_file.getFile(index).?.object;
......@@ -347,9 +320,9 @@ pub fn claimUnresolved(macho_file: *MachO) void {
347320
348321fn initOutputSections(macho_file: *MachO) !void {
349322 for (macho_file.objects.items) |index| {
350 const object = macho_file.getFile(index).?.object;
351 for (object.atoms.items) |atom_index| {
352 const atom = macho_file.getAtom(atom_index) orelse continue;
323 const file = macho_file.getFile(index).?;
324 for (file.getAtoms()) |atom_index| {
325 const atom = file.getAtom(atom_index) orelse continue;
353326 if (!atom.flags.alive) continue;
354327 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
355328 }
......@@ -377,69 +350,147 @@ fn calcSectionSizes(macho_file: *MachO) !void {
377350 const tracy = trace(@src());
378351 defer tracy.end();
379352
380 const slice = macho_file.sections.slice();
381 for (slice.items(.header), slice.items(.atoms)) |*header, atoms| {
353 for (macho_file.sections.items(.atoms), 0..) |atoms, i| {
382354 if (atoms.items.len == 0) continue;
383 for (atoms.items) |atom_index| {
384 const atom = macho_file.getAtom(atom_index).?;
385 const atom_alignment = atom.alignment.toByteUnits() orelse 1;
386 const offset = mem.alignForward(u64, header.size, atom_alignment);
387 const padding = offset - header.size;
388 atom.value = offset;
389 header.size += padding + atom.size;
390 header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
391 header.nreloc += atom.calcNumRelocs(macho_file);
392 }
355 calcSectionSize(macho_file, @intCast(i));
393356 }
394357
395 if (macho_file.unwind_info_sect_index) |index| {
396 calcCompactUnwindSize(macho_file, index);
358 if (macho_file.getZigObject()) |zo| {
359 // TODO this will create a race
360 zo.calcNumRelocs(macho_file);
361 zo.calcSymtabSize(macho_file);
397362 }
398363
399 if (macho_file.eh_frame_sect_index) |index| {
400 const sect = &macho_file.sections.items(.header)[index];
401 sect.size = try eh_frame.calcSize(macho_file);
402 sect.@"align" = 3;
403 sect.nreloc = eh_frame.calcNumRelocs(macho_file);
364 if (macho_file.eh_frame_sect_index) |_| {
365 try calcEhFrameSize(macho_file);
404366 }
405367
406 if (macho_file.getZigObject()) |zo| {
407 for (zo.atoms.items) |atom_index| {
408 const atom = macho_file.getAtom(atom_index) orelse continue;
409 if (!atom.flags.alive) continue;
410 const header = &macho_file.sections.items(.header)[atom.out_n_sect];
411 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
412 header.nreloc += atom.calcNumRelocs(macho_file);
368 for (macho_file.objects.items) |index| {
369 if (macho_file.unwind_info_sect_index) |_| {
370 macho_file.getFile(index).?.object.calcCompactUnwindSizeRelocatable(macho_file);
413371 }
372 macho_file.getFile(index).?.calcSymtabSize(macho_file);
414373 }
374
375 try macho_file.data_in_code.updateSize(macho_file);
376
377 if (macho_file.unwind_info_sect_index) |_| {
378 calcCompactUnwindSize(macho_file);
379 }
380 try calcSymtabSize(macho_file);
381}
382
383fn calcSectionSize(macho_file: *MachO, sect_id: u8) void {
384 const tracy = trace(@src());
385 defer tracy.end();
386
387 const slice = macho_file.sections.slice();
388 const header = &slice.items(.header)[sect_id];
389 const atoms = slice.items(.atoms)[sect_id].items;
390 for (atoms) |ref| {
391 const atom = ref.getAtom(macho_file).?;
392 const atom_alignment = atom.alignment.toByteUnits() orelse 1;
393 const offset = mem.alignForward(u64, header.size, atom_alignment);
394 const padding = offset - header.size;
395 atom.value = offset;
396 header.size += padding + atom.size;
397 header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
398 const nreloc = atom.calcNumRelocs(macho_file);
399 atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file);
400 header.nreloc += nreloc;
401 }
402}
403
404fn calcEhFrameSize(macho_file: *MachO) !void {
405 const tracy = trace(@src());
406 defer tracy.end();
407
408 const header = &macho_file.sections.items(.header)[macho_file.eh_frame_sect_index.?];
409 header.size = try eh_frame.calcSize(macho_file);
410 header.@"align" = 3;
411 header.nreloc = eh_frame.calcNumRelocs(macho_file);
415412}
416413
417fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {
418 var size: u32 = 0;
414fn calcCompactUnwindSize(macho_file: *MachO) void {
415 const tracy = trace(@src());
416 defer tracy.end();
417
418 var nrec: u32 = 0;
419419 var nreloc: u32 = 0;
420420
421421 for (macho_file.objects.items) |index| {
422 const object = macho_file.getFile(index).?.object;
423 for (object.unwind_records.items) |irec| {
424 const rec = macho_file.getUnwindRecord(irec);
425 if (!rec.alive) continue;
426 size += @sizeOf(macho.compact_unwind_entry);
427 nreloc += 1;
428 if (rec.getPersonality(macho_file)) |_| {
429 nreloc += 1;
430 }
431 if (rec.getLsdaAtom(macho_file)) |_| {
432 nreloc += 1;
433 }
434 }
422 const ctx = &macho_file.getFile(index).?.object.compact_unwind_ctx;
423 ctx.rec_index = nrec;
424 ctx.reloc_index = nreloc;
425 nrec += ctx.rec_count;
426 nreloc += ctx.reloc_count;
435427 }
436428
437 const sect = &macho_file.sections.items(.header)[sect_index];
438 sect.size = size;
429 const sect = &macho_file.sections.items(.header)[macho_file.unwind_info_sect_index.?];
430 sect.size = nrec * @sizeOf(macho.compact_unwind_entry);
439431 sect.nreloc = nreloc;
440432 sect.@"align" = 3;
441433}
442434
435fn calcSymtabSize(macho_file: *MachO) error{OutOfMemory}!void {
436 const tracy = trace(@src());
437 defer tracy.end();
438
439 const gpa = macho_file.base.comp.gpa;
440
441 var nlocals: u32 = 0;
442 var nstabs: u32 = 0;
443 var nexports: u32 = 0;
444 var nimports: u32 = 0;
445 var strsize: u32 = 1;
446
447 var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1);
448 defer objects.deinit();
449 if (macho_file.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
450 objects.appendSliceAssumeCapacity(macho_file.objects.items);
451
452 for (objects.items) |index| {
453 const ctx = switch (macho_file.getFile(index).?) {
454 inline else => |x| &x.output_symtab_ctx,
455 };
456 ctx.ilocal = nlocals;
457 ctx.istab = nstabs;
458 ctx.iexport = nexports;
459 ctx.iimport = nimports;
460 ctx.stroff = strsize;
461 nlocals += ctx.nlocals;
462 nstabs += ctx.nstabs;
463 nexports += ctx.nexports;
464 nimports += ctx.nimports;
465 strsize += ctx.strsize;
466 }
467
468 for (objects.items) |index| {
469 const ctx = switch (macho_file.getFile(index).?) {
470 inline else => |x| &x.output_symtab_ctx,
471 };
472 ctx.istab += nlocals;
473 ctx.iexport += nlocals + nstabs;
474 ctx.iimport += nlocals + nstabs + nexports;
475 }
476
477 {
478 const cmd = &macho_file.symtab_cmd;
479 cmd.nsyms = nlocals + nstabs + nexports + nimports;
480 cmd.strsize = strsize;
481 }
482
483 {
484 const cmd = &macho_file.dysymtab_cmd;
485 cmd.ilocalsym = 0;
486 cmd.nlocalsym = nlocals + nstabs;
487 cmd.iextdefsym = nlocals + nstabs;
488 cmd.nextdefsym = nexports;
489 cmd.iundefsym = nlocals + nstabs + nexports;
490 cmd.nundefsym = nimports;
491 }
492}
493
443494fn allocateSections(macho_file: *MachO) !void {
444495 const slice = macho_file.sections.slice();
445496 for (slice.items(.header)) |*header| {
......@@ -457,6 +508,37 @@ fn allocateSections(macho_file: *MachO) !void {
457508 }
458509 header.size = needed_size;
459510 }
511
512 var fileoff: u32 = 0;
513 for (slice.items(.header)) |header| {
514 fileoff = @max(fileoff, header.offset + @as(u32, @intCast(header.size)));
515 }
516
517 for (slice.items(.header)) |*header| {
518 if (header.nreloc == 0) continue;
519 header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
520 fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
521 }
522
523 // In -r mode, there is no LINKEDIT segment and so we allocate required LINKEDIT commands
524 // as if they were detached or part of the single segment.
525
526 // DATA_IN_CODE
527 {
528 const cmd = &macho_file.data_in_code_cmd;
529 cmd.dataoff = fileoff;
530 fileoff += cmd.datasize;
531 fileoff = mem.alignForward(u32, fileoff, @alignOf(u64));
532 }
533
534 // SYMTAB
535 {
536 const cmd = &macho_file.symtab_cmd;
537 cmd.symoff = fileoff;
538 fileoff += cmd.nsyms * @sizeOf(macho.nlist_64);
539 fileoff = mem.alignForward(u32, fileoff, @alignOf(u32));
540 cmd.stroff = fileoff;
541 }
460542}
461543
462544/// Renames segment names in Zig sections to standard MachO segment names such as
......@@ -519,232 +601,86 @@ fn allocateSegment(macho_file: *MachO) void {
519601 seg.filesize = fileoff - seg.fileoff;
520602}
521603
522fn allocateSectionsRelocs(macho_file: *MachO, off: u32) u32 {
523 var fileoff = off;
524 const slice = macho_file.sections.slice();
525 for (slice.items(.header)) |*header| {
526 if (header.nreloc == 0) continue;
527 header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
528 fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
529 }
530 return fileoff;
531}
532
533604// We need to sort relocations in descending order to be compatible with Apple's linker.
534605fn sortReloc(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool {
535606 _ = ctx;
536607 return lhs.r_address > rhs.r_address;
537608}
538609
539fn writeAtoms(macho_file: *MachO) !void {
610fn sortRelocs(macho_file: *MachO) void {
611 const tracy = trace(@src());
612 defer tracy.end();
613
614 for (macho_file.sections.items(.relocs)) |*relocs| {
615 mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
616 }
617}
618
619fn writeSections(macho_file: *MachO) !void {
540620 const tracy = trace(@src());
541621 defer tracy.end();
542622
543623 const gpa = macho_file.base.comp.gpa;
544624 const cpu_arch = macho_file.getTarget().cpu.arch;
545625 const slice = macho_file.sections.slice();
546
547 var relocs = std.ArrayList(macho.relocation_info).init(gpa);
548 defer relocs.deinit();
549
550 for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {
551 if (atoms.items.len == 0) continue;
626 for (slice.items(.header), slice.items(.out), slice.items(.relocs), 0..) |header, *out, *relocs, n_sect| {
552627 if (header.isZerofill()) continue;
553 if (macho_file.isZigSection(@intCast(i)) or macho_file.isDebugSection(@intCast(i))) continue;
554
555 const size = math.cast(usize, header.size) orelse return error.Overflow;
556 const code = try gpa.alloc(u8, size);
557 defer gpa.free(code);
558 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
559 @memset(code, padding_byte);
560
561 try relocs.ensureTotalCapacity(header.nreloc);
562
563 for (atoms.items) |atom_index| {
564 const atom = macho_file.getAtom(atom_index).?;
565 assert(atom.flags.alive);
566 const off = math.cast(usize, atom.value) orelse return error.Overflow;
567 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
568 try atom.getData(macho_file, code[off..][0..atom_size]);
569 try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs);
628 if (!macho_file.isZigSection(@intCast(n_sect))) { // TODO this is wrong; what about debug sections?
629 const size = math.cast(usize, header.size) orelse return error.Overflow;
630 try out.resize(gpa, size);
631 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
632 @memset(out.items, padding_byte);
570633 }
634 try relocs.resize(gpa, header.nreloc);
635 }
571636
572 assert(relocs.items.len == header.nreloc);
573
574 mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
575
576 // TODO scattered writes?
577 try macho_file.base.file.?.pwriteAll(code, header.offset);
578 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
637 const cmd = macho_file.symtab_cmd;
638 try macho_file.symtab.resize(gpa, cmd.nsyms);
639 try macho_file.strtab.resize(gpa, cmd.strsize);
640 macho_file.strtab.items[0] = 0;
579641
580 relocs.clearRetainingCapacity();
642 for (macho_file.objects.items) |index| {
643 try macho_file.getFile(index).?.object.writeAtomsRelocatable(macho_file);
644 macho_file.getFile(index).?.writeSymtab(macho_file, macho_file);
581645 }
582646
583647 if (macho_file.getZigObject()) |zo| {
584 // TODO: this is ugly; perhaps we should aggregrate before?
585 var zo_relocs = std.AutoArrayHashMap(u8, std.ArrayList(macho.relocation_info)).init(gpa);
586 defer {
587 for (zo_relocs.values()) |*list| {
588 list.deinit();
589 }
590 zo_relocs.deinit();
591 }
592
593 for (macho_file.sections.items(.header), 0..) |header, n_sect| {
594 if (header.isZerofill()) continue;
595 if (!macho_file.isZigSection(@intCast(n_sect)) and !macho_file.isDebugSection(@intCast(n_sect))) continue;
596 const gop = try zo_relocs.getOrPut(@intCast(n_sect));
597 if (gop.found_existing) continue;
598 gop.value_ptr.* = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
599 }
600
601 for (zo.atoms.items) |atom_index| {
602 const atom = macho_file.getAtom(atom_index) orelse continue;
603 if (!atom.flags.alive) continue;
604 const header = macho_file.sections.items(.header)[atom.out_n_sect];
605 if (header.isZerofill()) continue;
606 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
607 if (atom.getRelocs(macho_file).len == 0) continue;
608 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
609 const code = try gpa.alloc(u8, atom_size);
610 defer gpa.free(code);
611 atom.getData(macho_file, code) catch |err| switch (err) {
612 error.InputOutput => {
613 try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
614 atom.getName(macho_file),
615 });
616 return error.FlushFailure;
617 },
618 else => |e| {
619 try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
620 atom.getName(macho_file),
621 @errorName(e),
622 });
623 return error.FlushFailure;
624 },
625 };
626 const file_offset = header.offset + atom.value;
627 const rels = zo_relocs.getPtr(atom.out_n_sect).?;
628 try atom.writeRelocs(macho_file, code, rels);
629 try macho_file.base.file.?.pwriteAll(code, file_offset);
630 }
631
632 for (zo_relocs.keys(), zo_relocs.values()) |sect_id, rels| {
633 const header = macho_file.sections.items(.header)[sect_id];
634 assert(rels.items.len == header.nreloc);
635 mem.sort(macho.relocation_info, rels.items, {}, sortReloc);
636 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(rels.items), header.reloff);
637 }
648 try zo.writeRelocs(macho_file);
649 try zo.writeAtomsRelocatable(macho_file);
650 zo.writeSymtab(macho_file, macho_file);
638651 }
639}
640
641fn writeCompactUnwind(macho_file: *MachO) !void {
642 const sect_index = macho_file.unwind_info_sect_index orelse return;
643 const gpa = macho_file.base.comp.gpa;
644 const header = macho_file.sections.items(.header)[sect_index];
645
646 const nrecs = math.cast(usize, @divExact(header.size, @sizeOf(macho.compact_unwind_entry))) orelse return error.Overflow;
647 var entries = try std.ArrayList(macho.compact_unwind_entry).initCapacity(gpa, nrecs);
648 defer entries.deinit();
649
650 var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
651 defer relocs.deinit();
652
653 const addReloc = struct {
654 fn addReloc(offset: i32, cpu_arch: std.Target.Cpu.Arch) macho.relocation_info {
655 return .{
656 .r_address = offset,
657 .r_symbolnum = 0,
658 .r_pcrel = 0,
659 .r_length = 3,
660 .r_extern = 0,
661 .r_type = switch (cpu_arch) {
662 .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
663 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
664 else => unreachable,
665 },
666 };
667 }
668 }.addReloc;
669
670 var offset: i32 = 0;
671 for (macho_file.objects.items) |index| {
672 const object = macho_file.getFile(index).?.object;
673 for (object.unwind_records.items) |irec| {
674 const rec = macho_file.getUnwindRecord(irec);
675 if (!rec.alive) continue;
676
677 var out: macho.compact_unwind_entry = .{
678 .rangeStart = 0,
679 .rangeLength = rec.length,
680 .compactUnwindEncoding = rec.enc.enc,
681 .personalityFunction = 0,
682 .lsda = 0,
683 };
684
685 {
686 // Function address
687 const atom = rec.getAtom(macho_file);
688 const addr = rec.getAtomAddress(macho_file);
689 out.rangeStart = addr;
690 var reloc = addReloc(offset, macho_file.getTarget().cpu.arch);
691 reloc.r_symbolnum = atom.out_n_sect + 1;
692 relocs.appendAssumeCapacity(reloc);
693 }
694652
695 // Personality function
696 if (rec.getPersonality(macho_file)) |sym| {
697 const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
698 var reloc = addReloc(offset + 16, macho_file.getTarget().cpu.arch);
699 reloc.r_symbolnum = r_symbolnum;
700 reloc.r_extern = 1;
701 relocs.appendAssumeCapacity(reloc);
702 }
703
704 // LSDA address
705 if (rec.getLsdaAtom(macho_file)) |atom| {
706 const addr = rec.getLsdaAddress(macho_file);
707 out.lsda = addr;
708 var reloc = addReloc(offset + 24, macho_file.getTarget().cpu.arch);
709 reloc.r_symbolnum = atom.out_n_sect + 1;
710 relocs.appendAssumeCapacity(reloc);
711 }
653 if (macho_file.eh_frame_sect_index) |_| {
654 try writeEhFrame(macho_file);
655 }
712656
713 entries.appendAssumeCapacity(out);
714 offset += @sizeOf(macho.compact_unwind_entry);
657 if (macho_file.unwind_info_sect_index) |_| {
658 for (macho_file.objects.items) |index| {
659 try macho_file.getFile(index).?.object.writeCompactUnwindRelocatable(macho_file);
715660 }
716661 }
717
718 assert(entries.items.len == nrecs);
719 assert(relocs.items.len == header.nreloc);
720
721 mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
722
723 // TODO scattered writes?
724 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(entries.items), header.offset);
725 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
726662}
727663
728664fn writeEhFrame(macho_file: *MachO) !void {
729 const sect_index = macho_file.eh_frame_sect_index orelse return;
730 const gpa = macho_file.base.comp.gpa;
731 const header = macho_file.sections.items(.header)[sect_index];
732 const size = math.cast(usize, header.size) orelse return error.Overflow;
733
734 const code = try gpa.alloc(u8, size);
735 defer gpa.free(code);
736
737 var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
738 defer relocs.deinit();
665 const sect_index = macho_file.eh_frame_sect_index.?;
666 const buffer = macho_file.sections.items(.out)[sect_index];
667 const relocs = macho_file.sections.items(.relocs)[sect_index];
668 try eh_frame.writeRelocs(macho_file, buffer.items, relocs.items);
669}
739670
740 try eh_frame.writeRelocs(macho_file, code, &relocs);
741 assert(relocs.items.len == header.nreloc);
671fn writeSectionsToFile(macho_file: *MachO) !void {
672 const tracy = trace(@src());
673 defer tracy.end();
742674
743 mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
675 const slice = macho_file.sections.slice();
676 for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, out, relocs| {
677 try macho_file.base.file.?.pwriteAll(out.items, header.offset);
678 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
679 }
744680
745 // TODO scattered writes?
746 try macho_file.base.file.?.pwriteAll(code, header.offset);
747 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
681 try macho_file.writeDataInCode();
682 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff);
683 try macho_file.base.file.?.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff);
748684}
749685
750686fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {
src/link/MachO/synthetic.zig+151-204
......@@ -18,15 +18,15 @@ pub const ZigGotSection = struct {
1818 }
1919
2020 pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, macho_file: *MachO) !Index {
21 const comp = macho_file.base.comp;
22 const gpa = comp.gpa;
21 const gpa = macho_file.base.comp.gpa;
22 const zo = macho_file.getZigObject().?;
2323 const index = try zig_got.allocateEntry(gpa);
2424 const entry = &zig_got.entries.items[index];
2525 entry.* = sym_index;
26 const symbol = macho_file.getSymbol(sym_index);
26 const symbol = &zo.symbols.items[sym_index];
2727 assert(symbol.flags.needs_zig_got);
2828 symbol.flags.has_zig_got = true;
29 try symbol.addExtra(.{ .zig_got = index }, macho_file);
29 symbol.addExtra(.{ .zig_got = index }, macho_file);
3030 return index;
3131 }
3232
......@@ -53,9 +53,10 @@ pub const ZigGotSection = struct {
5353 try macho_file.growSection(macho_file.zig_got_sect_index.?, needed_size);
5454 zig_got.dirty = false;
5555 }
56 const zo = macho_file.getZigObject().?;
5657 const off = zig_got.entryOffset(index, macho_file);
5758 const entry = zig_got.entries.items[index];
58 const value = macho_file.getSymbol(entry).getAddress(.{ .stubs = false }, macho_file);
59 const value = zo.symbols.items[entry].getAddress(.{ .stubs = false }, macho_file);
5960
6061 var buf: [8]u8 = undefined;
6162 std.mem.writeInt(u64, &buf, value, .little);
......@@ -63,29 +64,14 @@ pub const ZigGotSection = struct {
6364 }
6465
6566 pub fn writeAll(zig_got: ZigGotSection, macho_file: *MachO, writer: anytype) !void {
67 const zo = macho_file.getZigObject().?;
6668 for (zig_got.entries.items) |entry| {
67 const symbol = macho_file.getSymbol(entry);
69 const symbol = zo.symbols.items[entry];
6870 const value = symbol.address(.{ .stubs = false }, macho_file);
6971 try writer.writeInt(u64, value, .little);
7072 }
7173 }
7274
73 pub fn addDyldRelocs(zig_got: ZigGotSection, macho_file: *MachO) !void {
74 const tracy = trace(@src());
75 defer tracy.end();
76 const gpa = macho_file.base.comp.gpa;
77 const seg_id = macho_file.sections.items(.segment_id)[macho_file.zig_got_sect_index.?];
78 const seg = macho_file.segments.items[seg_id];
79
80 for (0..zig_got.entries.items.len) |idx| {
81 const addr = zig_got.entryAddress(@intCast(idx), macho_file);
82 try macho_file.rebase.entries.append(gpa, .{
83 .offset = addr - seg.vmaddr,
84 .segment_id = seg_id,
85 });
86 }
87 }
88
8975 const FormatCtx = struct {
9076 zig_got: ZigGotSection,
9177 macho_file: *MachO,
......@@ -103,22 +89,25 @@ pub const ZigGotSection = struct {
10389 ) !void {
10490 _ = options;
10591 _ = unused_fmt_string;
92 const zig_got = ctx.zig_got;
93 const macho_file = ctx.macho_file;
10694 try writer.writeAll("__zig_got\n");
107 for (ctx.zig_got.entries.items, 0..) |entry, index| {
108 const symbol = ctx.macho_file.getSymbol(entry);
95 for (zig_got.entries.items, 0..) |entry, index| {
96 const zo = macho_file.getZigObject().?;
97 const symbol = zo.symbols.items[entry];
10998 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
11099 index,
111 ctx.zig_got.entryAddress(@intCast(index), ctx.macho_file),
100 zig_got.entryAddress(@intCast(index), macho_file),
112101 entry,
113 symbol.getAddress(.{}, ctx.macho_file),
114 symbol.getName(ctx.macho_file),
102 symbol.getAddress(.{}, macho_file),
103 symbol.getName(macho_file),
115104 });
116105 }
117106 }
118107};
119108
120109pub const GotSection = struct {
121 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
110 symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
122111
123112 pub const Index = u32;
124113
......@@ -126,14 +115,14 @@ pub const GotSection = struct {
126115 got.symbols.deinit(allocator);
127116 }
128117
129 pub fn addSymbol(got: *GotSection, sym_index: Symbol.Index, macho_file: *MachO) !void {
118 pub fn addSymbol(got: *GotSection, ref: MachO.Ref, macho_file: *MachO) !void {
130119 const gpa = macho_file.base.comp.gpa;
131120 const index = @as(Index, @intCast(got.symbols.items.len));
132121 const entry = try got.symbols.addOne(gpa);
133 entry.* = sym_index;
134 const symbol = macho_file.getSymbol(sym_index);
122 entry.* = ref;
123 const symbol = ref.getSymbol(macho_file).?;
135124 symbol.flags.has_got = true;
136 try symbol.addExtra(.{ .got = index }, macho_file);
125 symbol.addExtra(.{ .got = index }, macho_file);
137126 }
138127
139128 pub fn getAddress(got: GotSection, index: Index, macho_file: *MachO) u64 {
......@@ -146,46 +135,11 @@ pub const GotSection = struct {
146135 return got.symbols.items.len * @sizeOf(u64);
147136 }
148137
149 pub fn addDyldRelocs(got: GotSection, macho_file: *MachO) !void {
150 const tracy = trace(@src());
151 defer tracy.end();
152 const gpa = macho_file.base.comp.gpa;
153 const seg_id = macho_file.sections.items(.segment_id)[macho_file.got_sect_index.?];
154 const seg = macho_file.segments.items[seg_id];
155
156 for (got.symbols.items, 0..) |sym_index, idx| {
157 const sym = macho_file.getSymbol(sym_index);
158 const addr = got.getAddress(@intCast(idx), macho_file);
159 const entry = bind.Entry{
160 .target = sym_index,
161 .offset = addr - seg.vmaddr,
162 .segment_id = seg_id,
163 .addend = 0,
164 };
165 if (sym.flags.import) {
166 try macho_file.bind.entries.append(gpa, entry);
167 if (sym.flags.weak) {
168 try macho_file.weak_bind.entries.append(gpa, entry);
169 }
170 } else {
171 try macho_file.rebase.entries.append(gpa, .{
172 .offset = addr - seg.vmaddr,
173 .segment_id = seg_id,
174 });
175 if (sym.flags.weak) {
176 try macho_file.weak_bind.entries.append(gpa, entry);
177 } else if (sym.flags.interposable) {
178 try macho_file.bind.entries.append(gpa, entry);
179 }
180 }
181 }
182 }
183
184138 pub fn write(got: GotSection, macho_file: *MachO, writer: anytype) !void {
185139 const tracy = trace(@src());
186140 defer tracy.end();
187 for (got.symbols.items) |sym_index| {
188 const sym = macho_file.getSymbol(sym_index);
141 for (got.symbols.items) |ref| {
142 const sym = ref.getSymbol(macho_file).?;
189143 const value = if (sym.flags.import) @as(u64, 0) else sym.getAddress(.{}, macho_file);
190144 try writer.writeInt(u64, value, .little);
191145 }
......@@ -208,12 +162,12 @@ pub const GotSection = struct {
208162 ) !void {
209163 _ = options;
210164 _ = unused_fmt_string;
211 for (ctx.got.symbols.items, 0..) |entry, i| {
212 const symbol = ctx.macho_file.getSymbol(entry);
165 for (ctx.got.symbols.items, 0..) |ref, i| {
166 const symbol = ref.getSymbol(ctx.macho_file).?;
213167 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
214168 i,
215169 symbol.getGotAddress(ctx.macho_file),
216 entry,
170 ref,
217171 symbol.getAddress(.{}, ctx.macho_file),
218172 symbol.getName(ctx.macho_file),
219173 });
......@@ -222,7 +176,7 @@ pub const GotSection = struct {
222176};
223177
224178pub const StubsSection = struct {
225 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
179 symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
226180
227181 pub const Index = u32;
228182
......@@ -230,13 +184,13 @@ pub const StubsSection = struct {
230184 stubs.symbols.deinit(allocator);
231185 }
232186
233 pub fn addSymbol(stubs: *StubsSection, sym_index: Symbol.Index, macho_file: *MachO) !void {
187 pub fn addSymbol(stubs: *StubsSection, ref: MachO.Ref, macho_file: *MachO) !void {
234188 const gpa = macho_file.base.comp.gpa;
235189 const index = @as(Index, @intCast(stubs.symbols.items.len));
236190 const entry = try stubs.symbols.addOne(gpa);
237 entry.* = sym_index;
238 const symbol = macho_file.getSymbol(sym_index);
239 try symbol.addExtra(.{ .stubs = index }, macho_file);
191 entry.* = ref;
192 const symbol = ref.getSymbol(macho_file).?;
193 symbol.addExtra(.{ .stubs = index }, macho_file);
240194 }
241195
242196 pub fn getAddress(stubs: StubsSection, index: Index, macho_file: *MachO) u64 {
......@@ -256,8 +210,8 @@ pub const StubsSection = struct {
256210 const cpu_arch = macho_file.getTarget().cpu.arch;
257211 const laptr_sect = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_sect_index.?];
258212
259 for (stubs.symbols.items, 0..) |sym_index, idx| {
260 const sym = macho_file.getSymbol(sym_index);
213 for (stubs.symbols.items, 0..) |ref, idx| {
214 const sym = ref.getSymbol(macho_file).?;
261215 const source = sym.getAddress(.{ .stubs = true }, macho_file);
262216 const target = laptr_sect.addr + idx * @sizeOf(u64);
263217 switch (cpu_arch) {
......@@ -299,12 +253,12 @@ pub const StubsSection = struct {
299253 ) !void {
300254 _ = options;
301255 _ = unused_fmt_string;
302 for (ctx.stubs.symbols.items, 0..) |entry, i| {
303 const symbol = ctx.macho_file.getSymbol(entry);
256 for (ctx.stubs.symbols.items, 0..) |ref, i| {
257 const symbol = ref.getSymbol(ctx.macho_file).?;
304258 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
305259 i,
306260 symbol.getStubsAddress(ctx.macho_file),
307 entry,
261 ref,
308262 symbol.getAddress(.{}, ctx.macho_file),
309263 symbol.getName(ctx.macho_file),
310264 });
......@@ -335,8 +289,8 @@ pub const StubsHelperSection = struct {
335289 _ = stubs_helper;
336290 const cpu_arch = macho_file.getTarget().cpu.arch;
337291 var s: usize = preambleSize(cpu_arch);
338 for (macho_file.stubs.symbols.items) |sym_index| {
339 const sym = macho_file.getSymbol(sym_index);
292 for (macho_file.stubs.symbols.items) |ref| {
293 const sym = ref.getSymbol(macho_file).?;
340294 if (sym.flags.weak) continue;
341295 s += entrySize(cpu_arch);
342296 }
......@@ -355,8 +309,8 @@ pub const StubsHelperSection = struct {
355309 const entry_size = entrySize(cpu_arch);
356310
357311 var idx: usize = 0;
358 for (macho_file.stubs.symbols.items) |sym_index| {
359 const sym = macho_file.getSymbol(sym_index);
312 for (macho_file.stubs.symbols.items) |ref| {
313 const sym = ref.getSymbol(macho_file).?;
360314 if (sym.flags.weak) continue;
361315 const offset = macho_file.lazy_bind.offsets.items[idx];
362316 const source: i64 = @intCast(sect.addr + preamble_size + entry_size * idx);
......@@ -390,14 +344,15 @@ pub const StubsHelperSection = struct {
390344
391345 fn writePreamble(stubs_helper: StubsHelperSection, macho_file: *MachO, writer: anytype) !void {
392346 _ = stubs_helper;
347 const obj = macho_file.getInternalObject().?;
393348 const cpu_arch = macho_file.getTarget().cpu.arch;
394349 const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?];
395350 const dyld_private_addr = target: {
396 const sym = macho_file.getSymbol(macho_file.dyld_private_index.?);
351 const sym = obj.getDyldPrivateRef(macho_file).?.getSymbol(macho_file).?;
397352 break :target sym.getAddress(.{}, macho_file);
398353 };
399354 const dyld_stub_binder_addr = target: {
400 const sym = macho_file.getSymbol(macho_file.dyld_stub_binder_index.?);
355 const sym = obj.getDyldStubBinderRef(macho_file).?.getSymbol(macho_file).?;
401356 break :target sym.getGotAddress(macho_file);
402357 };
403358 switch (cpu_arch) {
......@@ -446,49 +401,6 @@ pub const LaSymbolPtrSection = struct {
446401 return macho_file.stubs.symbols.items.len * @sizeOf(u64);
447402 }
448403
449 pub fn addDyldRelocs(laptr: LaSymbolPtrSection, macho_file: *MachO) !void {
450 const tracy = trace(@src());
451 defer tracy.end();
452 _ = laptr;
453 const gpa = macho_file.base.comp.gpa;
454
455 const sect = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_sect_index.?];
456 const seg_id = macho_file.sections.items(.segment_id)[macho_file.la_symbol_ptr_sect_index.?];
457 const seg = macho_file.segments.items[seg_id];
458
459 for (macho_file.stubs.symbols.items, 0..) |sym_index, idx| {
460 const sym = macho_file.getSymbol(sym_index);
461 const addr = sect.addr + idx * @sizeOf(u64);
462 const rebase_entry = Rebase.Entry{
463 .offset = addr - seg.vmaddr,
464 .segment_id = seg_id,
465 };
466 const bind_entry = bind.Entry{
467 .target = sym_index,
468 .offset = addr - seg.vmaddr,
469 .segment_id = seg_id,
470 .addend = 0,
471 };
472 if (sym.flags.import) {
473 if (sym.flags.weak) {
474 try macho_file.bind.entries.append(gpa, bind_entry);
475 try macho_file.weak_bind.entries.append(gpa, bind_entry);
476 } else {
477 try macho_file.lazy_bind.entries.append(gpa, bind_entry);
478 try macho_file.rebase.entries.append(gpa, rebase_entry);
479 }
480 } else {
481 if (sym.flags.weak) {
482 try macho_file.rebase.entries.append(gpa, rebase_entry);
483 try macho_file.weak_bind.entries.append(gpa, bind_entry);
484 } else if (sym.flags.interposable) {
485 try macho_file.lazy_bind.entries.append(gpa, bind_entry);
486 try macho_file.rebase.entries.append(gpa, rebase_entry);
487 }
488 }
489 }
490 }
491
492404 pub fn write(laptr: LaSymbolPtrSection, macho_file: *MachO, writer: anytype) !void {
493405 const tracy = trace(@src());
494406 defer tracy.end();
......@@ -496,8 +408,8 @@ pub const LaSymbolPtrSection = struct {
496408 const cpu_arch = macho_file.getTarget().cpu.arch;
497409 const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?];
498410 var stub_helper_idx: u32 = 0;
499 for (macho_file.stubs.symbols.items) |sym_index| {
500 const sym = macho_file.getSymbol(sym_index);
411 for (macho_file.stubs.symbols.items) |ref| {
412 const sym = ref.getSymbol(macho_file).?;
501413 if (sym.flags.weak) {
502414 const value = sym.getAddress(.{ .stubs = false }, macho_file);
503415 try writer.writeInt(u64, @intCast(value), .little);
......@@ -512,7 +424,7 @@ pub const LaSymbolPtrSection = struct {
512424};
513425
514426pub const TlvPtrSection = struct {
515 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
427 symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
516428
517429 pub const Index = u32;
518430
......@@ -520,13 +432,13 @@ pub const TlvPtrSection = struct {
520432 tlv.symbols.deinit(allocator);
521433 }
522434
523 pub fn addSymbol(tlv: *TlvPtrSection, sym_index: Symbol.Index, macho_file: *MachO) !void {
435 pub fn addSymbol(tlv: *TlvPtrSection, ref: MachO.Ref, macho_file: *MachO) !void {
524436 const gpa = macho_file.base.comp.gpa;
525437 const index = @as(Index, @intCast(tlv.symbols.items.len));
526438 const entry = try tlv.symbols.addOne(gpa);
527 entry.* = sym_index;
528 const symbol = macho_file.getSymbol(sym_index);
529 try symbol.addExtra(.{ .tlv_ptr = index }, macho_file);
439 entry.* = ref;
440 const symbol = ref.getSymbol(macho_file).?;
441 symbol.addExtra(.{ .tlv_ptr = index }, macho_file);
530442 }
531443
532444 pub fn getAddress(tlv: TlvPtrSection, index: Index, macho_file: *MachO) u64 {
......@@ -539,47 +451,12 @@ pub const TlvPtrSection = struct {
539451 return tlv.symbols.items.len * @sizeOf(u64);
540452 }
541453
542 pub fn addDyldRelocs(tlv: TlvPtrSection, macho_file: *MachO) !void {
543 const tracy = trace(@src());
544 defer tracy.end();
545 const gpa = macho_file.base.comp.gpa;
546 const seg_id = macho_file.sections.items(.segment_id)[macho_file.tlv_ptr_sect_index.?];
547 const seg = macho_file.segments.items[seg_id];
548
549 for (tlv.symbols.items, 0..) |sym_index, idx| {
550 const sym = macho_file.getSymbol(sym_index);
551 const addr = tlv.getAddress(@intCast(idx), macho_file);
552 const entry = bind.Entry{
553 .target = sym_index,
554 .offset = addr - seg.vmaddr,
555 .segment_id = seg_id,
556 .addend = 0,
557 };
558 if (sym.flags.import) {
559 try macho_file.bind.entries.append(gpa, entry);
560 if (sym.flags.weak) {
561 try macho_file.weak_bind.entries.append(gpa, entry);
562 }
563 } else {
564 try macho_file.rebase.entries.append(gpa, .{
565 .offset = addr - seg.vmaddr,
566 .segment_id = seg_id,
567 });
568 if (sym.flags.weak) {
569 try macho_file.weak_bind.entries.append(gpa, entry);
570 } else if (sym.flags.interposable) {
571 try macho_file.bind.entries.append(gpa, entry);
572 }
573 }
574 }
575 }
576
577454 pub fn write(tlv: TlvPtrSection, macho_file: *MachO, writer: anytype) !void {
578455 const tracy = trace(@src());
579456 defer tracy.end();
580457
581 for (tlv.symbols.items) |sym_index| {
582 const sym = macho_file.getSymbol(sym_index);
458 for (tlv.symbols.items) |ref| {
459 const sym = ref.getSymbol(macho_file).?;
583460 if (sym.flags.import) {
584461 try writer.writeInt(u64, 0, .little);
585462 } else {
......@@ -605,12 +482,12 @@ pub const TlvPtrSection = struct {
605482 ) !void {
606483 _ = options;
607484 _ = unused_fmt_string;
608 for (ctx.tlv.symbols.items, 0..) |entry, i| {
609 const symbol = ctx.macho_file.getSymbol(entry);
485 for (ctx.tlv.symbols.items, 0..) |ref, i| {
486 const symbol = ref.getSymbol(ctx.macho_file).?;
610487 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
611488 i,
612489 symbol.getTlvPtrAddress(ctx.macho_file),
613 entry,
490 ref,
614491 symbol.getAddress(.{}, ctx.macho_file),
615492 symbol.getName(ctx.macho_file),
616493 });
......@@ -619,7 +496,7 @@ pub const TlvPtrSection = struct {
619496};
620497
621498pub const ObjcStubsSection = struct {
622 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
499 symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
623500
624501 pub fn deinit(objc: *ObjcStubsSection, allocator: Allocator) void {
625502 objc.symbols.deinit(allocator);
......@@ -633,13 +510,13 @@ pub const ObjcStubsSection = struct {
633510 };
634511 }
635512
636 pub fn addSymbol(objc: *ObjcStubsSection, sym_index: Symbol.Index, macho_file: *MachO) !void {
513 pub fn addSymbol(objc: *ObjcStubsSection, ref: MachO.Ref, macho_file: *MachO) !void {
637514 const gpa = macho_file.base.comp.gpa;
638515 const index = @as(Index, @intCast(objc.symbols.items.len));
639516 const entry = try objc.symbols.addOne(gpa);
640 entry.* = sym_index;
641 const symbol = macho_file.getSymbol(sym_index);
642 try symbol.addExtra(.{ .objc_stubs = index }, macho_file);
517 entry.* = ref;
518 const symbol = ref.getSymbol(macho_file).?;
519 symbol.addExtra(.{ .objc_stubs = index }, macho_file);
643520 }
644521
645522 pub fn getAddress(objc: ObjcStubsSection, index: Index, macho_file: *MachO) u64 {
......@@ -656,8 +533,10 @@ pub const ObjcStubsSection = struct {
656533 const tracy = trace(@src());
657534 defer tracy.end();
658535
659 for (objc.symbols.items, 0..) |sym_index, idx| {
660 const sym = macho_file.getSymbol(sym_index);
536 const obj = macho_file.getInternalObject().?;
537
538 for (objc.symbols.items, 0..) |ref, idx| {
539 const sym = ref.getSymbol(macho_file).?;
661540 const addr = objc.getAddress(@intCast(idx), macho_file);
662541 switch (macho_file.getTarget().cpu.arch) {
663542 .x86_64 => {
......@@ -669,7 +548,7 @@ pub const ObjcStubsSection = struct {
669548 }
670549 try writer.writeAll(&.{ 0xff, 0x25 });
671550 {
672 const target_sym = macho_file.getSymbol(macho_file.objc_msg_send_index.?);
551 const target_sym = obj.getObjcMsgSendRef(macho_file).?.getSymbol(macho_file).?;
673552 const target = target_sym.getGotAddress(macho_file);
674553 const source = addr + 7;
675554 try writer.writeInt(i32, @intCast(target - source - 2 - 4), .little);
......@@ -689,7 +568,7 @@ pub const ObjcStubsSection = struct {
689568 );
690569 }
691570 {
692 const target_sym = macho_file.getSymbol(macho_file.objc_msg_send_index.?);
571 const target_sym = obj.getObjcMsgSendRef(macho_file).?.getSymbol(macho_file).?;
693572 const target = target_sym.getGotAddress(macho_file);
694573 const source = addr + 2 * @sizeOf(u32);
695574 const pages = try aarch64.calcNumberOfPages(@intCast(source), @intCast(target));
......@@ -728,12 +607,12 @@ pub const ObjcStubsSection = struct {
728607 ) !void {
729608 _ = options;
730609 _ = unused_fmt_string;
731 for (ctx.objc.symbols.items, 0..) |entry, i| {
732 const symbol = ctx.macho_file.getSymbol(entry);
610 for (ctx.objc.symbols.items, 0..) |ref, i| {
611 const symbol = ref.getSymbol(ctx.macho_file).?;
733612 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
734613 i,
735614 symbol.getObjcStubsAddress(ctx.macho_file),
736 entry,
615 ref,
737616 symbol.getAddress(.{}, ctx.macho_file),
738617 symbol.getName(ctx.macho_file),
739618 });
......@@ -749,44 +628,112 @@ pub const Indsymtab = struct {
749628 return @intCast(macho_file.stubs.symbols.items.len * 2 + macho_file.got.symbols.items.len);
750629 }
751630
631 pub fn updateSize(ind: *Indsymtab, macho_file: *MachO) !void {
632 macho_file.dysymtab_cmd.nindirectsyms = ind.nsyms(macho_file);
633 }
634
752635 pub fn write(ind: Indsymtab, macho_file: *MachO, writer: anytype) !void {
753636 const tracy = trace(@src());
754637 defer tracy.end();
755638
756639 _ = ind;
757640
758 for (macho_file.stubs.symbols.items) |sym_index| {
759 const sym = macho_file.getSymbol(sym_index);
641 for (macho_file.stubs.symbols.items) |ref| {
642 const sym = ref.getSymbol(macho_file).?;
760643 try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);
761644 }
762645
763 for (macho_file.got.symbols.items) |sym_index| {
764 const sym = macho_file.getSymbol(sym_index);
646 for (macho_file.got.symbols.items) |ref| {
647 const sym = ref.getSymbol(macho_file).?;
765648 try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);
766649 }
767650
768 for (macho_file.stubs.symbols.items) |sym_index| {
769 const sym = macho_file.getSymbol(sym_index);
651 for (macho_file.stubs.symbols.items) |ref| {
652 const sym = ref.getSymbol(macho_file).?;
770653 try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);
771654 }
772655 }
773656};
774657
775pub const RebaseSection = Rebase;
776pub const BindSection = bind.Bind;
777pub const WeakBindSection = bind.WeakBind;
778pub const LazyBindSection = bind.LazyBind;
779pub const ExportTrieSection = Trie;
658pub const DataInCode = struct {
659 entries: std.ArrayListUnmanaged(Entry) = .{},
660
661 pub fn deinit(dice: *DataInCode, allocator: Allocator) void {
662 dice.entries.deinit(allocator);
663 }
664
665 pub fn size(dice: DataInCode) usize {
666 return dice.entries.items.len * @sizeOf(macho.data_in_code_entry);
667 }
668
669 pub fn updateSize(dice: *DataInCode, macho_file: *MachO) !void {
670 const gpa = macho_file.base.comp.gpa;
671
672 for (macho_file.objects.items) |index| {
673 const object = macho_file.getFile(index).?.object;
674 const dices = object.getDataInCode();
675
676 try dice.entries.ensureUnusedCapacity(gpa, dices.len);
677
678 var next_dice: usize = 0;
679 for (object.getAtoms()) |atom_index| {
680 if (next_dice >= dices.len) break;
681 const atom = object.getAtom(atom_index) orelse continue;
682 const start_off = atom.getInputAddress(macho_file);
683 const end_off = start_off + atom.size;
684 const start_dice = next_dice;
685
686 if (end_off < dices[next_dice].offset) continue;
687
688 while (next_dice < dices.len and
689 dices[next_dice].offset < end_off) : (next_dice += 1)
690 {}
691
692 if (atom.flags.alive) for (dices[start_dice..next_dice]) |d| {
693 dice.entries.appendAssumeCapacity(.{
694 .atom_ref = .{ .index = atom_index, .file = index },
695 .offset = @intCast(d.offset - start_off),
696 .length = d.length,
697 .kind = d.kind,
698 });
699 };
700 }
701 }
702
703 macho_file.data_in_code_cmd.datasize = math.cast(u32, dice.size()) orelse return error.Overflow;
704 }
705
706 pub fn write(dice: DataInCode, macho_file: *MachO, writer: anytype) !void {
707 const base_address = if (!macho_file.base.isRelocatable())
708 macho_file.getTextSegment().vmaddr
709 else
710 0;
711 for (dice.entries.items) |entry| {
712 const atom_address = entry.atom_ref.getAtom(macho_file).?.getAddress(macho_file);
713 const offset = atom_address + entry.offset - base_address;
714 try writer.writeStruct(macho.data_in_code_entry{
715 .offset = @intCast(offset),
716 .length = entry.length,
717 .kind = entry.kind,
718 });
719 }
720 }
721
722 const Entry = struct {
723 atom_ref: MachO.Ref,
724 offset: u32,
725 length: u16,
726 kind: u16,
727 };
728};
780729
781730const aarch64 = @import("../aarch64.zig");
782731const assert = std.debug.assert;
783const bind = @import("dyld_info/bind.zig");
732const macho = std.macho;
784733const math = std.math;
785734const std = @import("std");
786735const trace = @import("../../tracy.zig").trace;
787736
788737const Allocator = std.mem.Allocator;
789738const MachO = @import("../MachO.zig");
790const Rebase = @import("dyld_info/Rebase.zig");
791739const Symbol = @import("Symbol.zig");
792const Trie = @import("dyld_info/Trie.zig");
src/link/MachO/thunks.zig+40-32
......@@ -5,55 +5,45 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {
55 const gpa = macho_file.base.comp.gpa;
66 const slice = macho_file.sections.slice();
77 const header = &slice.items(.header)[sect_id];
8 const thnks = &slice.items(.thunks)[sect_id];
89 const atoms = slice.items(.atoms)[sect_id].items;
910 assert(atoms.len > 0);
1011
11 for (atoms) |atom_index| {
12 macho_file.getAtom(atom_index).?.value = @bitCast(@as(i64, -1));
12 for (atoms) |ref| {
13 ref.getAtom(macho_file).?.value = @bitCast(@as(i64, -1));
1314 }
1415
1516 var i: usize = 0;
1617 while (i < atoms.len) {
1718 const start = i;
18 const start_atom = macho_file.getAtom(atoms[start]).?;
19 const start_atom = atoms[start].getAtom(macho_file).?;
1920 assert(start_atom.flags.alive);
20 start_atom.value = try advance(header, start_atom.size, start_atom.alignment);
21 start_atom.value = advance(header, start_atom.size, start_atom.alignment);
2122 i += 1;
2223
2324 while (i < atoms.len and
2425 header.size - start_atom.value < max_allowed_distance) : (i += 1)
2526 {
26 const atom_index = atoms[i];
27 const atom = macho_file.getAtom(atom_index).?;
27 const atom = atoms[i].getAtom(macho_file).?;
2828 assert(atom.flags.alive);
29 atom.value = try advance(header, atom.size, atom.alignment);
29 atom.value = advance(header, atom.size, atom.alignment);
3030 }
3131
3232 // Insert a thunk at the group end
3333 const thunk_index = try macho_file.addThunk();
3434 const thunk = macho_file.getThunk(thunk_index);
3535 thunk.out_n_sect = sect_id;
36 try thnks.append(gpa, thunk_index);
3637
3738 // Scan relocs in the group and create trampolines for any unreachable callsite
38 for (atoms[start..i]) |atom_index| {
39 const atom = macho_file.getAtom(atom_index).?;
40 log.debug("atom({d}) {s}", .{ atom_index, atom.getName(macho_file) });
41 for (atom.getRelocs(macho_file)) |rel| {
42 if (rel.type != .branch) continue;
43 if (isReachable(atom, rel, macho_file)) continue;
44 try thunk.symbols.put(gpa, rel.target, {});
45 }
46 try atom.addExtra(.{ .thunk = thunk_index }, macho_file);
47 atom.flags.thunk = true;
48 }
49
50 thunk.value = try advance(header, thunk.size(), .@"4");
39 try scanRelocs(thunk_index, gpa, atoms[start..i], macho_file);
40 thunk.value = advance(header, thunk.size(), .@"4");
5141
5242 log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) });
5343 }
5444}
5545
56fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) !u64 {
46fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) u64 {
5747 const offset = alignment.forward(sect.size);
5848 const padding = offset - sect.size;
5949 sect.size += padding + size;
......@@ -61,14 +51,32 @@ fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) !u64 {
6151 return offset;
6252}
6353
54fn scanRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref, macho_file: *MachO) !void {
55 const tracy = trace(@src());
56 defer tracy.end();
57
58 const thunk = macho_file.getThunk(thunk_index);
59
60 for (atoms) |ref| {
61 const atom = ref.getAtom(macho_file).?;
62 log.debug("atom({d}) {s}", .{ atom.atom_index, atom.getName(macho_file) });
63 for (atom.getRelocs(macho_file)) |rel| {
64 if (rel.type != .branch) continue;
65 if (isReachable(atom, rel, macho_file)) continue;
66 try thunk.symbols.put(gpa, rel.getTargetSymbolRef(atom.*, macho_file), {});
67 }
68 atom.addExtra(.{ .thunk = thunk_index }, macho_file);
69 }
70}
71
6472fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
65 const target = rel.getTargetSymbol(macho_file);
73 const target = rel.getTargetSymbol(atom.*, macho_file);
6674 if (target.flags.stubs or target.flags.objc_stubs) return false;
67 if (atom.out_n_sect != target.out_n_sect) return false;
75 if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false;
6876 const target_atom = target.getAtom(macho_file).?;
6977 if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;
7078 const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off));
71 const taddr: i64 = @intCast(rel.getTargetAddress(macho_file));
79 const taddr: i64 = @intCast(rel.getTargetAddress(atom.*, macho_file));
7280 _ = math.cast(i28, taddr + rel.addend - saddr) orelse return false;
7381 return true;
7482}
......@@ -76,7 +84,7 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
7684pub const Thunk = struct {
7785 value: u64 = 0,
7886 out_n_sect: u8 = 0,
79 symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{},
87 symbols: std.AutoArrayHashMapUnmanaged(MachO.Ref, void) = .{},
8088
8189 pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
8290 thunk.symbols.deinit(allocator);
......@@ -91,13 +99,13 @@ pub const Thunk = struct {
9199 return header.addr + thunk.value;
92100 }
93101
94 pub fn getTargetAddress(thunk: Thunk, sym_index: Symbol.Index, macho_file: *MachO) u64 {
95 return thunk.getAddress(macho_file) + thunk.symbols.getIndex(sym_index).? * trampoline_size;
102 pub fn getTargetAddress(thunk: Thunk, ref: MachO.Ref, macho_file: *MachO) u64 {
103 return thunk.getAddress(macho_file) + thunk.symbols.getIndex(ref).? * trampoline_size;
96104 }
97105
98106 pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void {
99 for (thunk.symbols.keys(), 0..) |sym_index, i| {
100 const sym = macho_file.getSymbol(sym_index);
107 for (thunk.symbols.keys(), 0..) |ref, i| {
108 const sym = ref.getSymbol(macho_file).?;
101109 const saddr = thunk.getAddress(macho_file) + i * trampoline_size;
102110 const taddr = sym.getAddress(.{}, macho_file);
103111 const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr));
......@@ -144,9 +152,9 @@ pub const Thunk = struct {
144152 const thunk = ctx.thunk;
145153 const macho_file = ctx.macho_file;
146154 try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });
147 for (thunk.symbols.keys()) |index| {
148 const sym = macho_file.getSymbol(index);
149 try writer.print(" %{d} : {s} : @{x}\n", .{ index, sym.getName(macho_file), sym.value });
155 for (thunk.symbols.keys()) |ref| {
156 const sym = ref.getSymbol(macho_file).?;
157 try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value });
150158 }
151159 }
152160
test/link/link.zig+2-1
......@@ -74,8 +74,9 @@ fn addCompileStep(
7474 .target = base.target,
7575 .optimize = base.optimize,
7676 .root_source_file = rsf: {
77 const name = b.fmt("{s}.zig", .{overlay.name});
7778 const bytes = overlay.zig_source_bytes orelse break :rsf null;
78 break :rsf b.addWriteFiles().add("a.zig", bytes);
79 break :rsf b.addWriteFiles().add(name, bytes);
7980 },
8081 .pic = overlay.pic,
8182 .strip = if (base.strip) |s| s else overlay.strip,
test/link/macho.zig+99-1
......@@ -25,9 +25,12 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
2525 macho_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = x86_64_target }));
2626 macho_step.dependOn(testReexportsZig(b, .{ .use_llvm = false, .target = x86_64_target }));
2727 macho_step.dependOn(testRelocatableZig(b, .{ .use_llvm = false, .target = x86_64_target }));
28 macho_step.dependOn(testTlsZig(b, .{ .use_llvm = false, .target = x86_64_target }));
29 macho_step.dependOn(testUnresolvedError(b, .{ .use_llvm = false, .target = x86_64_target }));
2830
2931 // Exercise linker with LLVM backend
3032 macho_step.dependOn(testDeadStrip(b, .{ .target = default_target }));
33 macho_step.dependOn(testDuplicateDefinitions(b, .{ .target = default_target }));
3134 macho_step.dependOn(testEmptyObject(b, .{ .target = default_target }));
3235 macho_step.dependOn(testEmptyZig(b, .{ .target = default_target }));
3336 macho_step.dependOn(testEntryPoint(b, .{ .target = default_target }));
......@@ -56,7 +59,9 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
5659 macho_step.dependOn(testTentative(b, .{ .target = default_target }));
5760 macho_step.dependOn(testThunks(b, .{ .target = aarch64_target }));
5861 macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target }));
62 macho_step.dependOn(testTlsZig(b, .{ .target = default_target }));
5963 macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target }));
64 macho_step.dependOn(testUnresolvedError(b, .{ .target = default_target }));
6065 macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target }));
6166 macho_step.dependOn(testUnwindInfoNoSubsectionsX64(b, .{ .target = x86_64_target }));
6267 macho_step.dependOn(testUnwindInfoNoSubsectionsArm64(b, .{ .target = aarch64_target }));
......@@ -178,6 +183,37 @@ fn testDeadStrip(b: *Build, opts: Options) *Step {
178183 return test_step;
179184}
180185
186fn testDuplicateDefinitions(b: *Build, opts: Options) *Step {
187 const test_step = addTestStep(b, "duplicate-definitions", opts);
188
189 const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
190 \\var x: usize = 1;
191 \\export fn strong() void { x += 1; }
192 \\export fn weak() void { x += 1; }
193 });
194
195 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
196 \\var x: usize = 1;
197 \\export fn strong() void { x += 1; }
198 \\comptime { @export(weakImpl, .{ .name = "weak", .linkage = .weak }); }
199 \\fn weakImpl() callconv(.C) void { x += 1; }
200 \\extern fn weak() void;
201 \\pub fn main() void {
202 \\ weak();
203 \\ strong();
204 \\}
205 });
206 exe.addObject(obj);
207
208 expectLinkErrors(exe, test_step, .{ .exact = &.{
209 "error: duplicate symbol definition: _strong",
210 "note: defined by /?/a.o",
211 "note: defined by /?/main.o",
212 } });
213
214 return test_step;
215}
216
181217fn testDeadStripDylibs(b: *Build, opts: Options) *Step {
182218 const test_step = addTestStep(b, "dead-strip-dylibs", opts);
183219
......@@ -912,7 +948,7 @@ fn testLinksection(b: *Build, opts: Options) *Step {
912948
913949 if (opts.optimize == .Debug) {
914950 check.checkInSymtab();
915 check.checkContains("(__TEXT,__TestGenFnA) _a.testGenericFn__anon_");
951 check.checkContains("(__TEXT,__TestGenFnA) _main.testGenericFn__anon_");
916952 }
917953
918954 test_step.dependOn(&check.step);
......@@ -2274,6 +2310,32 @@ fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
22742310 return test_step;
22752311}
22762312
2313fn testTlsZig(b: *Build, opts: Options) *Step {
2314 const test_step = addTestStep(b, "tls-zig", opts);
2315
2316 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
2317 \\const std = @import("std");
2318 \\threadlocal var x: i32 = 0;
2319 \\threadlocal var y: i32 = -1;
2320 \\pub fn main() void {
2321 \\ std.io.getStdOut().writer().print("{d} {d}\n", .{x, y}) catch unreachable;
2322 \\ x -= 1;
2323 \\ y += 1;
2324 \\ std.io.getStdOut().writer().print("{d} {d}\n", .{x, y}) catch unreachable;
2325 \\}
2326 });
2327
2328 const run = addRunArtifact(exe);
2329 run.expectStdOutEqual(
2330 \\0 -1
2331 \\-1 0
2332 \\
2333 );
2334 test_step.dependOn(&run.step);
2335
2336 return test_step;
2337}
2338
22772339fn testTwoLevelNamespace(b: *Build, opts: Options) *Step {
22782340 const test_step = addTestStep(b, "two-level-namespace", opts);
22792341
......@@ -2471,6 +2533,42 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step {
24712533 return test_step;
24722534}
24732535
2536fn testUnresolvedError(b: *Build, opts: Options) *Step {
2537 const test_step = addTestStep(b, "unresolved-error", opts);
2538
2539 const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
2540 \\extern fn foo() i32;
2541 \\export fn bar() i32 { return foo() + 1; }
2542 });
2543
2544 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
2545 \\const std = @import("std");
2546 \\extern fn foo() i32;
2547 \\extern fn bar() i32;
2548 \\pub fn main() void {
2549 \\ std.debug.print("foo() + bar() = {d}", .{foo() + bar()});
2550 \\}
2551 });
2552 exe.addObject(obj);
2553
2554 // TODO order should match across backends if possible
2555 if (opts.use_llvm) {
2556 expectLinkErrors(exe, test_step, .{ .exact = &.{
2557 "error: undefined symbol: _foo",
2558 "note: referenced by /?/a.o:_bar",
2559 "note: referenced by /?/main.o:_main.main",
2560 } });
2561 } else {
2562 expectLinkErrors(exe, test_step, .{ .exact = &.{
2563 "error: undefined symbol: _foo",
2564 "note: referenced by /?/main.o:_main.main",
2565 "note: referenced by /?/a.o:__TEXT$__text_zig",
2566 } });
2567 }
2568
2569 return test_step;
2570}
2571
24742572fn testUnwindInfo(b: *Build, opts: Options) *Step {
24752573 const test_step = addTestStep(b, "unwind-info", opts);
24762574