authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-12 23:03:15+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-22 16:58:20+02:00
logd80fcc8a0b5594a6eb0fb409f4e5e5f949eec2fe
tree16dea5f36353ee793855006437134316969495e5
parenteeb6d8f0457b42cef560c1e4efeca69c0fe276fe

macho: rework symbol handling for incremental stage2 builds


5 files changed, 640 insertions(+), 460 deletions(-)

src/link/MachO.zig+609-438
......@@ -70,10 +70,10 @@ d_sym: ?DebugSymbols = null,
7070/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.
7171page_size: u16,
7272
73/// If true, the linker will preallocate several sections and segments before starting the linking
74/// process. This is for example true for stage2 debug builds, however, this is false for stage1
75/// and potentially stage2 release builds in the future.
76needs_prealloc: bool = true,
73/// Mode of operation: incremental - will preallocate segments/sections and is compatible with
74/// watch and HCS modes of operation; one_shot - will link relocatables in a traditional, one-shot
75/// fashion (default for LLVM backend).
76mode: enum { incremental, one_shot },
7777
7878/// The absolute address of the entry point.
7979entry_addr: ?u64 = null,
......@@ -153,7 +153,7 @@ rustc_section_size: u64 = 0,
153153
154154locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
155155globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
156unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
156unresolved: std.AutoArrayHashMapUnmanaged(u32, bool) = .{},
157157
158158locals_free_list: std.ArrayListUnmanaged(u32) = .{},
159159
......@@ -161,7 +161,7 @@ dyld_stub_binder_index: ?u32 = null,
161161dyld_private_atom: ?*Atom = null,
162162stub_helper_preamble_atom: ?*Atom = null,
163163
164strtab: StringTable(.link) = .{},
164strtab: StringTable(.strtab) = .{},
165165
166166tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{},
167167tlv_ptr_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
......@@ -182,6 +182,12 @@ sections_order_dirty: bool = false,
182182has_dices: bool = false,
183183has_stabs: bool = false,
184184
185/// A helper var to indicate if we are at the start of the incremental updates, or
186/// already somewhere further along the update-and-run chain.
187/// TODO once we add opening a prelinked output binary from file, this will become
188/// obsolete as we will carry on where we left off.
189cold_start: bool = true,
190
185191section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},
186192
187193/// A list of atoms that have surplus capacity. This list can have false
......@@ -387,7 +393,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
387393 const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator);
388394 const use_llvm = build_options.have_llvm and options.use_llvm;
389395 const use_stage1 = build_options.is_stage1 and options.use_stage1;
390 const needs_prealloc = !(use_stage1 or use_llvm or options.cache_mode == .whole);
391396
392397 const self = try gpa.create(MachO);
393398 errdefer gpa.destroy(self);
......@@ -400,8 +405,14 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
400405 .file = null,
401406 },
402407 .page_size = page_size,
403 .code_signature = if (requires_adhoc_codesig) CodeSignature.init(page_size) else null,
404 .needs_prealloc = needs_prealloc,
408 .code_signature = if (requires_adhoc_codesig)
409 CodeSignature.init(page_size)
410 else
411 null,
412 .mode = if (use_stage1 or use_llvm or options.cache_mode == .whole)
413 .one_shot
414 else
415 .incremental,
405416 };
406417
407418 if (use_llvm and !use_stage1) {
......@@ -429,32 +440,198 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !v
429440 return error.TODOImplementWritingStaticLibFiles;
430441 }
431442 }
432 return self.flushModule(comp, prog_node);
443
444 switch (self.mode) {
445 .one_shot => return self.linkOneShot(comp, prog_node),
446 .incremental => return self.flushModule(comp, prog_node),
447 }
433448}
434449
435450pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
436451 const tracy = trace(@src());
437452 defer tracy.end();
438453
439 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
440
441 if (build_options.have_llvm and !use_stage1) {
454 if (build_options.have_llvm) {
442455 if (self.llvm_object) |llvm_object| {
443 try llvm_object.flushModule(comp, prog_node);
444
445 llvm_object.destroy(self.base.allocator);
446 self.llvm_object = null;
447
448 if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) {
449 return;
450 }
456 return try llvm_object.flushModule(comp, prog_node);
451457 }
452458 }
453459
460 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
461 defer arena_allocator.deinit();
462 const arena = arena_allocator.allocator();
463
454464 var sub_prog_node = prog_node.start("MachO Flush", 0);
455465 sub_prog_node.activate();
456466 defer sub_prog_node.end();
457467
468 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
469
470 if (self.d_sym) |*d_sym| {
471 try d_sym.dwarf.flushModule(&self.base, module);
472 }
473
474 var libs = std.StringArrayHashMap(SystemLib).init(arena);
475 try self.resolveLibSystem(arena, comp, &.{}, &libs);
476
477 const id_symlink_basename = "zld.id";
478
479 const cache_dir_handle = module.zig_cache_artifact_directory.handle;
480 var man: Cache.Manifest = undefined;
481 defer if (!self.base.options.disable_lld_caching) man.deinit();
482
483 var digest: [Cache.hex_digest_len]u8 = undefined;
484 var cache_miss: bool = self.cold_start;
485
486 if (!self.base.options.disable_lld_caching) {
487 man = comp.cache_parent.obtain();
488 self.base.releaseLock();
489
490 man.hash.addListOfBytes(libs.keys());
491
492 _ = try man.hit();
493 digest = man.final();
494
495 var prev_digest_buf: [digest.len]u8 = undefined;
496 const prev_digest: []u8 = Cache.readSmallFile(
497 cache_dir_handle,
498 id_symlink_basename,
499 &prev_digest_buf,
500 ) catch |err| blk: {
501 log.debug("MachO Zld new_digest={s} error: {s}", .{
502 std.fmt.fmtSliceHexLower(&digest),
503 @errorName(err),
504 });
505 // Handle this as a cache miss.
506 break :blk prev_digest_buf[0..0];
507 };
508 if (mem.eql(u8, prev_digest, &digest)) {
509 log.debug("MachO Zld digest={s} match - skipping parsing linker line objects", .{
510 std.fmt.fmtSliceHexLower(&digest),
511 });
512 self.base.lock = man.toOwnedLock();
513 } else {
514 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
515 std.fmt.fmtSliceHexLower(prev_digest),
516 std.fmt.fmtSliceHexLower(&digest),
517 });
518 // We are about to change the output file to be different, so we invalidate the build hash now.
519 cache_dir_handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
520 error.FileNotFound => {},
521 else => |e| return e,
522 };
523 cache_miss = true;
524 }
525 }
526
527 if (cache_miss) {
528 var dependent_libs = std.fifo.LinearFifo(struct {
529 id: Dylib.Id,
530 parent: u16,
531 }, .Dynamic).init(self.base.allocator);
532 defer dependent_libs.deinit();
533 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
534 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
535 }
536
537 try self.createMhExecuteHeaderSymbol();
538 try self.resolveDyldStubBinder();
539 try self.createDyldPrivateAtom();
540 try self.createStubHelperPreambleAtom();
541 try self.resolveSymbolsInDylibs();
542 try self.addCodeSignatureLC();
543
544 if (self.unresolved.count() > 0) {
545 return error.UndefinedSymbolReference;
546 }
547
548 try self.allocateSpecialSymbols();
549
550 if (build_options.enable_logging) {
551 self.logSymtab();
552 self.logSectionOrdinals();
553 self.logAtoms();
554 }
555
556 try self.writeAtomsIncremental();
557
558 try self.setEntryPoint();
559 try self.updateSectionOrdinals();
560 try self.writeLinkeditSegment();
561
562 if (self.d_sym) |*d_sym| {
563 // Flush debug symbols bundle.
564 try d_sym.flushModule(self.base.allocator, self.base.options);
565 }
566
567 // code signature and entitlements
568 if (self.base.options.entitlements) |path| {
569 if (self.code_signature) |*csig| {
570 try csig.addEntitlements(self.base.allocator, path);
571 csig.code_directory.ident = self.base.options.emit.?.sub_path;
572 } else {
573 var csig = CodeSignature.init(self.page_size);
574 try csig.addEntitlements(self.base.allocator, path);
575 csig.code_directory.ident = self.base.options.emit.?.sub_path;
576 self.code_signature = csig;
577 }
578 }
579
580 if (self.code_signature) |*csig| {
581 csig.clear(self.base.allocator);
582 csig.code_directory.ident = self.base.options.emit.?.sub_path;
583 // Preallocate space for the code signature.
584 // We need to do this at this stage so that we have the load commands with proper values
585 // written out to the file.
586 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
587 // where the code signature goes into.
588 try self.writeCodeSignaturePadding(csig);
589 }
590
591 try self.writeLoadCommands();
592 try self.writeHeader();
593
594 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
595 log.debug("flushing. no_entry_point_found = true", .{});
596 self.error_flags.no_entry_point_found = true;
597 } else {
598 log.debug("flushing. no_entry_point_found = false", .{});
599 self.error_flags.no_entry_point_found = false;
600 }
601
602 assert(!self.load_commands_dirty);
603
604 if (self.code_signature) |*csig| {
605 try self.writeCodeSignature(csig); // code signing always comes last
606 }
607
608 if (build_options.enable_link_snapshots) {
609 if (self.base.options.enable_link_snapshots)
610 try self.snapshotState();
611 }
612
613 if (!self.base.options.disable_lld_caching and cache_miss) {
614 // Update the file with the digest. If it fails we can continue; it only
615 // means that the next invocation will have an unnecessary cache miss.
616 Cache.writeSmallFile(cache_dir_handle, id_symlink_basename, &digest) catch |err| {
617 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
618 };
619 // Again failure here only means an unnecessary cache miss.
620 man.writeManifest() catch |err| {
621 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
622 };
623 // We hang on to this lock so that the output file path can be used without
624 // other processes clobbering it.
625 self.base.lock = man.toOwnedLock();
626 }
627
628 self.cold_start = false;
629}
630
631fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
632 const tracy = trace(@src());
633 defer tracy.end();
634
458635 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
459636 defer arena_allocator.deinit();
460637 const arena = arena_allocator.allocator();
......@@ -465,7 +642,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
465642 // If there is no Zig code to compile, then we should skip flushing the output file because it
466643 // will not be part of the linker line anyway.
467644 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
468 if (use_stage1) {
645 if (self.base.options.use_stage1) {
469646 const obj_basename = try std.zig.binNameAlloc(arena, .{
470647 .root_name = self.base.options.root_name,
471648 .target = self.base.options.target,
......@@ -482,20 +659,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
482659 }
483660 }
484661
485 const obj_basename = self.base.intermediary_basename orelse break :blk null;
662 try self.flushModule(comp, prog_node);
486663
487664 if (fs.path.dirname(full_out_path)) |dirname| {
488 break :blk try fs.path.join(arena, &.{ dirname, obj_basename });
665 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
489666 } else {
490 break :blk obj_basename;
667 break :blk self.base.intermediary_basename.?;
491668 }
492669 } else null;
493670
494 if (self.d_sym) |*d_sym| {
495 if (self.base.options.module) |module| {
496 try d_sym.dwarf.flushModule(&self.base, module);
497 }
498 }
671 var sub_prog_node = prog_node.start("MachO Flush", 0);
672 sub_prog_node.activate();
673 sub_prog_node.context.refresh();
674 defer sub_prog_node.end();
499675
500676 const is_lib = self.base.options.output_mode == .Lib;
501677 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
......@@ -503,25 +679,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
503679 const stack_size = self.base.options.stack_size_override orelse 0;
504680
505681 const id_symlink_basename = "zld.id";
506 const cache_dir_handle = blk: {
507 if (use_stage1) {
508 break :blk directory.handle;
509 }
510 if (self.base.options.module) |module| {
511 break :blk module.zig_cache_artifact_directory.handle;
512 }
513 break :blk directory.handle;
514 };
515682
516683 var man: Cache.Manifest = undefined;
517684 defer if (!self.base.options.disable_lld_caching) man.deinit();
518685
519686 var digest: [Cache.hex_digest_len]u8 = undefined;
520687
521 cache: {
522 if ((use_stage1 and self.base.options.disable_lld_caching) or self.base.options.cache_mode == .whole)
523 break :cache;
524
688 if (!self.base.options.disable_lld_caching) {
525689 man = comp.cache_parent.obtain();
526690
527691 // We are about to obtain this lock, so here we give other processes a chance first.
......@@ -564,7 +728,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
564728
565729 var prev_digest_buf: [digest.len]u8 = undefined;
566730 const prev_digest: []u8 = Cache.readSmallFile(
567 cache_dir_handle,
731 directory.handle,
568732 id_symlink_basename,
569733 &prev_digest_buf,
570734 ) catch |err| blk: {
......@@ -577,15 +741,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
577741 };
578742 if (mem.eql(u8, prev_digest, &digest)) {
579743 // Hot diggity dog! The output binary is already there.
580
581 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
582 if (use_llvm or use_stage1) {
583 log.debug("MachO Zld digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
584 self.base.lock = man.toOwnedLock();
585 return;
586 } else {
587 log.debug("MachO Zld digest={s} match", .{std.fmt.fmtSliceHexLower(&digest)});
588 }
744 log.debug("MachO Zld digest={s} match - skipping invocation", .{
745 std.fmt.fmtSliceHexLower(&digest),
746 });
747 self.base.lock = man.toOwnedLock();
748 return;
589749 }
590750 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
591751 std.fmt.fmtSliceHexLower(prev_digest),
......@@ -593,7 +753,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
593753 });
594754
595755 // We are about to change the output file to be different, so we invalidate the build hash now.
596 cache_dir_handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
756 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
597757 error.FileNotFound => {},
598758 else => |e| return e,
599759 };
......@@ -624,24 +784,22 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
624784 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
625785 }
626786 } else {
627 if (use_stage1) {
628 const sub_path = self.base.options.emit.?.sub_path;
629 self.base.file = try cache_dir_handle.createFile(sub_path, .{
630 .truncate = true,
631 .read = true,
632 .mode = link.determineMode(self.base.options),
633 });
634 // Index 0 is always a null symbol.
635 try self.locals.append(self.base.allocator, .{
636 .n_strx = 0,
637 .n_type = 0,
638 .n_sect = 0,
639 .n_desc = 0,
640 .n_value = 0,
641 });
642 try self.strtab.buffer.append(self.base.allocator, 0);
643 try self.populateMissingMetadata();
644 }
787 const sub_path = self.base.options.emit.?.sub_path;
788 self.base.file = try directory.handle.createFile(sub_path, .{
789 .truncate = true,
790 .read = true,
791 .mode = link.determineMode(self.base.options),
792 });
793 // Index 0 is always a null symbol.
794 try self.locals.append(self.base.allocator, .{
795 .n_strx = 0,
796 .n_type = 0,
797 .n_sect = 0,
798 .n_desc = 0,
799 .n_value = 0,
800 });
801 try self.strtab.buffer.append(self.base.allocator, 0);
802 try self.populateMissingMetadata();
645803
646804 var lib_not_found = false;
647805 var framework_not_found = false;
......@@ -757,40 +915,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
757915 }
758916 }
759917
760 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
761 var libsystem_available = false;
762 if (self.base.options.sysroot != null) blk: {
763 // Try stub file first. If we hit it, then we're done as the stub file
764 // re-exports every single symbol definition.
765 for (lib_dirs.items) |dir| {
766 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
767 try libs.put(full_path, .{ .needed = true });
768 libsystem_available = true;
769 break :blk;
770 }
771 }
772 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
773 // doesn't export libc.dylib which we'll need to resolve subsequently also.
774 for (lib_dirs.items) |dir| {
775 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
776 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
777 try libs.put(libsystem_path, .{ .needed = true });
778 try libs.put(libc_path, .{ .needed = true });
779 libsystem_available = true;
780 break :blk;
781 }
782 }
783 }
784 }
785 if (!libsystem_available) {
786 const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{
787 self.base.options.target.os.version_range.semver.min.major,
788 });
789 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
790 "libc", "darwin", libsystem_name,
791 });
792 try libs.put(full_path, .{ .needed = true });
793 }
918 try self.resolveLibSystem(arena, comp, lib_dirs.items, &libs);
794919
795920 // frameworks
796921 var framework_dirs = std.ArrayList([]const u8).init(arena);
......@@ -1003,7 +1128,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
10031128 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
10041129 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
10051130
1006 try self.createMhExecuteHeaderSymbol();
10071131 for (self.objects.items) |*object, object_id| {
10081132 try self.resolveSymbolsInObject(object, @intCast(u16, object_id));
10091133 }
......@@ -1013,6 +1137,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
10131137 try self.createDyldPrivateAtom();
10141138 try self.createStubHelperPreambleAtom();
10151139 try self.resolveSymbolsInDylibs();
1140 try self.createMhExecuteHeaderSymbol();
10161141 try self.createDsoHandleSymbol();
10171142 try self.addCodeSignatureLC();
10181143 try self.resolveSymbolsAtLoading();
......@@ -1029,20 +1154,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
10291154
10301155 try self.createTentativeDefAtoms();
10311156
1032 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
1033 if (use_llvm or use_stage1) {
1034 for (self.objects.items) |*object, object_id| {
1035 try object.splitIntoAtomsWhole(self, @intCast(u32, object_id));
1036 }
1037
1038 try self.gcAtoms();
1039 try self.pruneAndSortSections();
1040 try self.allocateSegments();
1041 try self.allocateSymbols();
1042 } else {
1043 // TODO incremental mode: parsing objects into atoms
1157 for (self.objects.items) |*object, object_id| {
1158 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id));
10441159 }
10451160
1161 try self.gcAtoms();
1162 try self.pruneAndSortSections();
1163 try self.allocateSegments();
1164 try self.allocateSymbols();
1165
10461166 try self.allocateSpecialSymbols();
10471167
10481168 if (build_options.enable_logging) {
......@@ -1051,11 +1171,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
10511171 self.logAtoms();
10521172 }
10531173
1054 if (use_llvm or use_stage1) {
1055 try self.writeAtomsWhole();
1056 } else {
1057 // try self.writeAtoms();
1058 }
1174 try self.writeAtomsOneShot();
10591175
10601176 if (self.rustc_section_index) |id| {
10611177 const sect = self.getSectionPtr(.{
......@@ -1066,14 +1182,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
10661182 }
10671183
10681184 try self.setEntryPoint();
1069 try self.updateSectionOrdinals();
10701185 try self.writeLinkeditSegment();
10711186
1072 if (self.d_sym) |*d_sym| {
1073 // Flush debug symbols bundle.
1074 try d_sym.flushModule(self.base.allocator, self.base.options);
1075 }
1076
10771187 if (self.code_signature) |*csig| {
10781188 csig.clear(self.base.allocator);
10791189 csig.code_directory.ident = self.base.options.emit.?.sub_path;
......@@ -1088,32 +1198,17 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
10881198 try self.writeLoadCommands();
10891199 try self.writeHeader();
10901200
1091 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
1092 log.debug("flushing. no_entry_point_found = true", .{});
1093 self.error_flags.no_entry_point_found = true;
1094 } else {
1095 log.debug("flushing. no_entry_point_found = false", .{});
1096 self.error_flags.no_entry_point_found = false;
1097 }
1098
10991201 assert(!self.load_commands_dirty);
11001202
11011203 if (self.code_signature) |*csig| {
11021204 try self.writeCodeSignature(csig); // code signing always comes last
11031205 }
1104
1105 // if (build_options.enable_link_snapshots) {
1106 // if (self.base.options.enable_link_snapshots)
1107 // try self.snapshotState();
1108 // }
11091206 }
11101207
1111 cache: {
1112 if ((use_stage1 and self.base.options.disable_lld_caching) or self.base.options.cache_mode == .whole)
1113 break :cache;
1208 if (!self.base.options.disable_lld_caching) {
11141209 // Update the file with the digest. If it fails we can continue; it only
11151210 // means that the next invocation will have an unnecessary cache miss.
1116 Cache.writeSmallFile(cache_dir_handle, id_symlink_basename, &digest) catch |err| {
1211 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
11171212 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
11181213 };
11191214 // Again failure here only means an unnecessary cache miss.
......@@ -1126,6 +1221,49 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
11261221 }
11271222}
11281223
1224fn resolveLibSystem(
1225 self: *MachO,
1226 arena: Allocator,
1227 comp: *Compilation,
1228 search_dirs: []const []const u8,
1229 out_libs: anytype,
1230) !void {
1231 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
1232 var libsystem_available = false;
1233 if (self.base.options.sysroot != null) blk: {
1234 // Try stub file first. If we hit it, then we're done as the stub file
1235 // re-exports every single symbol definition.
1236 for (search_dirs) |dir| {
1237 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
1238 try out_libs.put(full_path, .{ .needed = true });
1239 libsystem_available = true;
1240 break :blk;
1241 }
1242 }
1243 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
1244 // doesn't export libc.dylib which we'll need to resolve subsequently also.
1245 for (search_dirs) |dir| {
1246 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
1247 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
1248 try out_libs.put(libsystem_path, .{ .needed = true });
1249 try out_libs.put(libc_path, .{ .needed = true });
1250 libsystem_available = true;
1251 break :blk;
1252 }
1253 }
1254 }
1255 }
1256 if (!libsystem_available) {
1257 const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{
1258 self.base.options.target.os.version_range.semver.min.major,
1259 });
1260 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
1261 "libc", "darwin", libsystem_name,
1262 });
1263 try out_libs.put(full_path, .{ .needed = true });
1264 }
1265}
1266
11291267fn resolveSearchDir(
11301268 arena: Allocator,
11311269 dir: []const u8,
......@@ -1168,6 +1306,16 @@ fn resolveSearchDir(
11681306 return null;
11691307}
11701308
1309fn resolveSearchDirs(arena: Allocator, dirs: []const []const u8, syslibroot: ?[]const u8, out_dirs: anytype) !void {
1310 for (dirs) |dir| {
1311 if (try resolveSearchDir(arena, dir, syslibroot)) |search_dir| {
1312 try out_dirs.append(search_dir);
1313 } else {
1314 log.warn("directory not found for '-L{s}'", .{dir});
1315 }
1316 }
1317}
1318
11711319fn resolveLib(
11721320 arena: Allocator,
11731321 search_dir: []const u8,
......@@ -2128,6 +2276,7 @@ fn allocateSpecialSymbols(self: *MachO) !void {
21282276 "__mh_execute_header",
21292277 }) |name| {
21302278 const global = self.globals.get(name) orelse continue;
2279 if (global.file != null) continue;
21312280 const sym = self.getSymbolPtr(global);
21322281 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
21332282 sym.n_sect = self.getSectionOrdinal(.{
......@@ -2143,7 +2292,9 @@ fn allocateSpecialSymbols(self: *MachO) !void {
21432292 }
21442293}
21452294
2146fn writeAtomsWhole(self: *MachO) !void {
2295fn writeAtomsOneShot(self: *MachO) !void {
2296 assert(self.mode == .one_shot);
2297
21472298 var it = self.atoms.iterator();
21482299 while (it.next()) |entry| {
21492300 const sect = self.getSection(entry.key_ptr.*);
......@@ -2227,7 +2378,9 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
22272378 }
22282379}
22292380
2230fn writeAtoms(self: *MachO) !void {
2381fn writeAtomsIncremental(self: *MachO) !void {
2382 assert(self.mode == .incremental);
2383
22312384 var it = self.atoms.iterator();
22322385 while (it.next()) |entry| {
22332386 const match = entry.key_ptr.*;
......@@ -2240,7 +2393,7 @@ fn writeAtoms(self: *MachO) !void {
22402393 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
22412394
22422395 while (true) {
2243 if (atom.dirty or self.invalidate_relocs) {
2396 if (atom.dirty) {
22442397 try self.writeAtom(atom, match);
22452398 atom.dirty = false;
22462399 }
......@@ -2746,11 +2899,13 @@ fn createTentativeDefAtoms(self: *MachO) !void {
27462899
27472900fn createMhExecuteHeaderSymbol(self: *MachO) !void {
27482901 if (self.base.options.output_mode != .Exe) return;
2749 if (self.globals.contains("__mh_execute_header")) return;
2902 if (self.globals.get("__mh_execute_header")) |global| {
2903 const sym = self.getSymbol(global);
2904 if (!sym.undf() and !(sym.pext() or sym.weakDef())) return;
2905 }
27502906
27512907 const gpa = self.base.allocator;
2752 const name = try gpa.dupe(u8, "__mh_execute_header");
2753 const n_strx = try self.strtab.insert(gpa, name);
2908 const n_strx = try self.strtab.insert(gpa, "__mh_execute_header");
27542909 const sym_index = @intCast(u32, self.locals.items.len);
27552910 try self.locals.append(gpa, .{
27562911 .n_strx = n_strx,
......@@ -2759,10 +2914,14 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {
27592914 .n_desc = 0,
27602915 .n_value = 0,
27612916 });
2762 try self.globals.putNoClobber(gpa, name, .{
2917
2918 const name = try gpa.dupe(u8, "__mh_execute_header");
2919 const gop = try self.globals.getOrPut(gpa, name);
2920 defer if (gop.found_existing) gpa.free(name);
2921 gop.value_ptr.* = .{
27632922 .sym_index = sym_index,
27642923 .file = null,
2765 });
2924 };
27662925}
27672926
27682927fn createDsoHandleSymbol(self: *MachO) !void {
......@@ -2787,9 +2946,68 @@ fn createDsoHandleSymbol(self: *MachO) !void {
27872946 _ = self.unresolved.swapRemove(@intCast(u32, self.globals.getIndex("___dso_handle").?));
27882947}
27892948
2790fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
2949fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
27912950 const gpa = self.base.allocator;
2951 const sym = self.getSymbol(current);
2952 const sym_name = self.getSymbolName(current);
2953
2954 const name = try gpa.dupe(u8, sym_name);
2955 const global_index = @intCast(u32, self.globals.values().len);
2956 const gop = try self.globals.getOrPut(gpa, name);
2957 defer if (gop.found_existing) gpa.free(name);
2958
2959 if (!gop.found_existing) {
2960 gop.value_ptr.* = current;
2961 if (sym.undf() and !sym.tentative()) {
2962 try self.unresolved.putNoClobber(gpa, global_index, false);
2963 }
2964 return;
2965 }
27922966
2967 const global = gop.value_ptr.*;
2968 const global_sym = self.getSymbol(global);
2969
2970 // Cases to consider: sym vs global_sym
2971 // 1. strong(sym) and strong(global_sym) => error
2972 // 2. strong(sym) and weak(global_sym) => sym
2973 // 3. strong(sym) and tentative(global_sym) => sym
2974 // 4. strong(sym) and undf(global_sym) => sym
2975 // 5. weak(sym) and strong(global_sym) => global_sym
2976 // 6. weak(sym) and tentative(global_sym) => sym
2977 // 7. weak(sym) and undf(global_sym) => sym
2978 // 8. tentative(sym) and strong(global_sym) => global_sym
2979 // 9. tentative(sym) and weak(global_sym) => global_sym
2980 // 10. tentative(sym) and tentative(global_sym) => pick larger
2981 // 11. tentative(sym) and undf(global_sym) => sym
2982 // 12. undf(sym) and * => global_sym
2983 //
2984 // Reduces to:
2985 // 1. strong(sym) and strong(global_sym) => error
2986 // 2. * and strong(global_sym) => global_sym
2987 // 3. weak(sym) and weak(global_sym) => global_sym
2988 // 4. tentative(sym) and tentative(global_sym) => pick larger
2989 // 5. undf(sym) and * => global_sym
2990 // 6. else => sym
2991
2992 const sym_is_strong = sym.sect() and !(sym.weakDef() or sym.pext());
2993 const global_is_strong = global_sym.sect() and !(global_sym.weakDef() or global_sym.pext());
2994 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
2995 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
2996
2997 if (sym_is_strong and global_is_strong) return error.MultipleSymbolDefinitions;
2998 if (global_is_strong) return;
2999 if (sym_is_weak and global_is_weak) return;
3000 if (sym.tentative() and global_sym.tentative()) {
3001 if (global_sym.n_value >= sym.n_value) return;
3002 }
3003 if (sym.undf() and !sym.tentative()) return;
3004
3005 _ = self.unresolved.swapRemove(@intCast(u32, self.globals.getIndex(name).?));
3006
3007 gop.value_ptr.* = current;
3008}
3009
3010fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
27933011 log.debug("resolving symbols in '{s}'", .{object.name});
27943012
27953013 for (object.symtab.items) |sym, index| {
......@@ -2825,72 +3043,18 @@ fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
28253043 continue;
28263044 }
28273045
2828 const name = try gpa.dupe(u8, sym_name);
2829 const global_index = @intCast(u32, self.globals.values().len);
2830 const gop = try self.globals.getOrPut(gpa, name);
2831 defer if (gop.found_existing) gpa.free(name);
2832
2833 if (!gop.found_existing) {
2834 gop.value_ptr.* = .{
2835 .sym_index = sym_index,
2836 .file = object_id,
2837 };
2838 if (sym.undf() and !sym.tentative()) {
2839 try self.unresolved.putNoClobber(gpa, global_index, {});
2840 }
2841 continue;
2842 }
2843
2844 const global = gop.value_ptr.*;
2845 const global_sym = self.getSymbol(global);
2846
2847 // Cases to consider: sym vs global_sym
2848 // 1. strong(sym) and strong(global_sym) => error
2849 // 2. strong(sym) and weak(global_sym) => sym
2850 // 3. strong(sym) and tentative(global_sym) => sym
2851 // 4. strong(sym) and undf(global_sym) => sym
2852 // 5. weak(sym) and strong(global_sym) => global_sym
2853 // 6. weak(sym) and tentative(global_sym) => sym
2854 // 7. weak(sym) and undf(global_sym) => sym
2855 // 8. tentative(sym) and strong(global_sym) => global_sym
2856 // 9. tentative(sym) and weak(global_sym) => global_sym
2857 // 10. tentative(sym) and tentative(global_sym) => pick larger
2858 // 11. tentative(sym) and undf(global_sym) => sym
2859 // 12. undf(sym) and * => global_sym
2860 //
2861 // Reduces to:
2862 // 1. strong(sym) and strong(global_sym) => error
2863 // 2. * and strong(global_sym) => global_sym
2864 // 3. weak(sym) and weak(global_sym) => global_sym
2865 // 4. tentative(sym) and tentative(global_sym) => pick larger
2866 // 5. undf(sym) and * => global_sym
2867 // 6. else => sym
2868
2869 const sym_is_strong = sym.sect() and !(sym.weakDef() or sym.pext());
2870 const global_is_strong = global_sym.sect() and !(global_sym.weakDef() or global_sym.pext());
2871 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
2872 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
2873
2874 if (sym_is_strong and global_is_strong) {
2875 log.err("symbol '{s}' defined multiple times", .{sym_name});
2876 if (global.file) |file| {
2877 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
2878 }
2879 log.err(" next definition in '{s}'", .{object.name});
2880 return error.MultipleSymbolDefinitions;
2881 }
2882 if (global_is_strong) continue;
2883 if (sym_is_weak and global_is_weak) continue;
2884 if (sym.tentative() and global_sym.tentative()) {
2885 if (global_sym.n_value >= sym.n_value) continue;
2886 }
2887 if (sym.undf() and !sym.tentative()) continue;
2888
2889 _ = self.unresolved.swapRemove(@intCast(u32, self.globals.getIndex(name).?));
2890
2891 gop.value_ptr.* = .{
2892 .sym_index = sym_index,
2893 .file = object_id,
3046 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id };
3047 self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) {
3048 error.MultipleSymbolDefinitions => {
3049 const global = self.globals.get(sym_name).?;
3050 log.err("symbol '{s}' defined multiple times", .{sym_name});
3051 if (global.file) |file| {
3052 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
3053 }
3054 log.err(" next definition in '{s}'", .{self.objects.items[object_id].name});
3055 return error.MultipleSymbolDefinitions;
3056 },
3057 else => |e| return e,
28943058 };
28953059 }
28963060}
......@@ -2950,7 +3114,18 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
29503114 sym.n_desc |= macho.N_WEAK_REF;
29513115 }
29523116
2953 assert(self.unresolved.swapRemove(global_index));
3117 if (self.unresolved.fetchSwapRemove(global_index)) |entry| blk: {
3118 if (!entry.value) break :blk;
3119 if (!sym.undf()) break :blk;
3120 if (self.stubs_table.contains(global)) break :blk;
3121
3122 const stub_index = try self.allocateStubEntry(global);
3123 const stub_helper_atom = try self.createStubHelperAtom();
3124 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);
3125 const stub_atom = try self.createStubAtom(laptr_atom.sym_index);
3126
3127 self.stubs.items[stub_index].atom = stub_atom;
3128 }
29543129
29553130 continue :loop;
29563131 }
......@@ -3272,7 +3447,7 @@ fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match
32723447 return self.allocateAtom(atom, new_atom_size, alignment, match);
32733448}
32743449
3275fn allocateLocalSymbol(self: *MachO) !u32 {
3450fn allocateSymbol(self: *MachO) !u32 {
32763451 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
32773452
32783453 const index = blk: {
......@@ -3366,12 +3541,9 @@ pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void {
33663541 const decl = self.base.options.module.?.declPtr(decl_index);
33673542 if (decl.link.macho.sym_index != 0) return;
33683543
3369 decl.link.macho.sym_index = try self.allocateLocalSymbol();
3544 decl.link.macho.sym_index = try self.allocateSymbol();
33703545 try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.sym_index, &decl.link.macho);
33713546 try self.decls.putNoClobber(self.base.allocator, decl_index, null);
3372
3373 const got_target = .{ .sym_index = decl.link.macho.sym_index, .file = null };
3374 _ = try self.allocateGotEntry(got_target);
33753547}
33763548
33773549pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
......@@ -3468,7 +3640,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
34683640 log.debug("allocating symbol indexes for {s}", .{name});
34693641
34703642 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
3471 const sym_index = try self.allocateLocalSymbol();
3643 const sym_index = try self.allocateSymbol();
34723644 const atom = try MachO.createEmptyAtom(
34733645 gpa,
34743646 sym_index,
......@@ -3787,6 +3959,12 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
37873959 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, symbol.n_value, vaddr });
37883960 log.debug(" (required alignment 0x{x})", .{required_alignment});
37893961 symbol.n_value = vaddr;
3962
3963 const got_atom = self.getGotAtomForSymbol(.{
3964 .sym_index = decl.link.macho.sym_index,
3965 .file = null,
3966 }).?;
3967 got_atom.dirty = true;
37903968 } else if (code_len < decl.link.macho.size) {
37913969 self.shrinkAtom(&decl.link.macho, code_len, match);
37923970 }
......@@ -3814,11 +3992,8 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
38143992 .n_value = addr,
38153993 };
38163994
3817 const got_target = SymbolWithLoc{
3818 .sym_index = decl.link.macho.sym_index,
3819 .file = null,
3820 };
3821 const got_index = self.got_entries_table.get(got_target).?;
3995 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };
3996 const got_index = try self.allocateGotEntry(got_target);
38223997 const got_atom = try self.createGotAtom(got_target);
38233998 self.got_entries.items[got_index].atom = got_atom;
38243999 }
......@@ -3843,19 +4018,23 @@ pub fn updateDeclExports(
38434018 @panic("Attempted to compile for object format that was disabled by build configuration");
38444019 }
38454020 if (build_options.have_llvm) {
3846 if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports);
4021 if (self.llvm_object) |llvm_object|
4022 return llvm_object.updateDeclExports(module, decl_index, exports);
38474023 }
38484024 const tracy = trace(@src());
38494025 defer tracy.end();
38504026
3851 try self.globals.ensureUnusedCapacity(self.base.allocator, exports.len);
4027 const gpa = self.base.allocator;
4028
38524029 const decl = module.declPtr(decl_index);
38534030 if (decl.link.macho.sym_index == 0) return;
3854 const decl_sym = &self.locals.items[decl.link.macho.sym_index];
4031 const decl_sym = decl.link.macho.getSymbol(self);
38554032
38564033 for (exports) |exp| {
3857 const exp_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{exp.options.name});
3858 defer self.base.allocator.free(exp_name);
4034 const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{exp.options.name});
4035 defer gpa.free(exp_name);
4036
4037 log.debug("adding new export '{s}'", .{exp_name});
38594038
38604039 if (exp.options.section) |section_name| {
38614040 if (!mem.eql(u8, section_name, "__text")) {
......@@ -3863,7 +4042,7 @@ pub fn updateDeclExports(
38634042 module.gpa,
38644043 exp,
38654044 try Module.ErrorMsg.create(
3866 self.base.allocator,
4045 gpa,
38674046 decl.srcLoc(),
38684047 "Unimplemented: ExportOptions.section",
38694048 .{},
......@@ -3878,7 +4057,7 @@ pub fn updateDeclExports(
38784057 module.gpa,
38794058 exp,
38804059 try Module.ErrorMsg.create(
3881 self.base.allocator,
4060 gpa,
38824061 decl.srcLoc(),
38834062 "Unimplemented: GlobalLinkage.LinkOnce",
38844063 .{},
......@@ -3887,107 +4066,84 @@ pub fn updateDeclExports(
38874066 continue;
38884067 }
38894068
3890 const is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;
3891 _ = is_weak;
3892 const n_strx = try self.strtab.insert(self.base.allocator, exp_name);
3893 // if (self.symbol_resolver.getPtr(n_strx)) |resolv| {
3894 // switch (resolv.where) {
3895 // .global => {
3896 // if (resolv.sym_index == decl.link.macho.sym_index) continue;
3897
3898 // const sym = &self.globals.items[resolv.where_index];
3899
3900 // if (sym.tentative()) {
3901 // assert(self.tentatives.swapRemove(resolv.where_index));
3902 // } else if (!is_weak and !(sym.weakDef() or sym.pext())) {
3903 // _ = try module.failed_exports.put(
3904 // module.gpa,
3905 // exp,
3906 // try Module.ErrorMsg.create(
3907 // self.base.allocator,
3908 // decl.srcLoc(),
3909 // \\LinkError: symbol '{s}' defined multiple times
3910 // \\ first definition in '{s}'
3911 // ,
3912 // .{ exp_name, self.objects.items[resolv.file.?].name },
3913 // ),
3914 // );
3915 // continue;
3916 // } else if (is_weak) continue; // Current symbol is weak, so skip it.
3917
3918 // // Otherwise, update the resolver and the global symbol.
3919 // sym.n_type = macho.N_SECT | macho.N_EXT;
3920 // resolv.sym_index = decl.link.macho.sym_index;
3921 // resolv.file = null;
3922 // exp.link.macho.sym_index = resolv.where_index;
3923
3924 // continue;
3925 // },
3926 // .undef => {
3927 // assert(self.unresolved.swapRemove(resolv.where_index));
3928 // _ = self.symbol_resolver.remove(n_strx);
3929 // },
3930 // }
3931 // }
3932
3933 var n_type: u8 = macho.N_SECT | macho.N_EXT;
3934 var n_desc: u16 = 0;
4069 const sym_index = exp.link.macho.sym_index orelse blk: {
4070 const sym_index = try self.allocateSymbol();
4071 exp.link.macho.sym_index = sym_index;
4072 break :blk sym_index;
4073 };
4074 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
4075 const sym = self.getSymbolPtr(sym_loc);
4076 sym.* = .{
4077 .n_strx = try self.strtab.insert(gpa, exp_name),
4078 .n_type = macho.N_SECT | macho.N_EXT,
4079 .n_sect = self.getSectionOrdinal(.{
4080 .seg = self.text_segment_cmd_index.?,
4081 .sect = self.text_section_index.?, // TODO what if we export a variable?
4082 }),
4083 .n_desc = 0,
4084 .n_value = decl_sym.n_value,
4085 };
39354086
39364087 switch (exp.options.linkage) {
39374088 .Internal => {
39384089 // Symbol should be hidden, or in MachO lingo, private extern.
39394090 // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF.
3940 // TODO work out when to add N_WEAK_REF.
3941 n_type |= macho.N_PEXT;
3942 n_desc |= macho.N_WEAK_DEF;
4091 sym.n_type |= macho.N_PEXT;
4092 sym.n_desc |= macho.N_WEAK_DEF;
39434093 },
39444094 .Strong => {},
39454095 .Weak => {
39464096 // Weak linkage is specified as part of n_desc field.
39474097 // Symbol's n_type is like for a symbol with strong linkage.
3948 n_desc |= macho.N_WEAK_DEF;
4098 sym.n_desc |= macho.N_WEAK_DEF;
39494099 },
39504100 else => unreachable,
39514101 }
39524102
3953 const global_sym_index: u32 = 0;
3954 // const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: {
3955 // const i = if (self.globals_free_list.popOrNull()) |i| i else inner: {
3956 // _ = self.globals.addOneAssumeCapacity();
3957 // break :inner @intCast(u32, self.globals.items.len - 1);
3958 // };
3959 // break :blk i;
3960 // };
3961 const sym = &self.locals.items[global_sym_index];
3962 sym.* = .{
3963 .n_strx = try self.strtab.insert(self.base.allocator, exp_name),
3964 .n_type = n_type,
3965 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
3966 .n_desc = n_desc,
3967 .n_value = decl_sym.n_value,
4103 self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) {
4104 error.MultipleSymbolDefinitions => {
4105 const global = self.globals.get(exp_name).?;
4106 if (sym_loc.sym_index != global.sym_index and global.file != null) {
4107 _ = try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
4108 gpa,
4109 decl.srcLoc(),
4110 \\LinkError: symbol '{s}' defined multiple times
4111 \\ first definition in '{s}'
4112 ,
4113 .{ exp_name, self.objects.items[global.file.?].name },
4114 ));
4115 }
4116 },
4117 else => |e| return e,
39684118 };
3969 exp.link.macho.sym_index = global_sym_index;
3970 _ = n_strx;
3971
3972 // try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
3973 // .where = .global,
3974 // .where_index = global_sym_index,
3975 // .sym_index = decl.link.macho.sym_index,
3976 // });
39774119 }
39784120}
39794121
39804122pub fn deleteExport(self: *MachO, exp: Export) void {
39814123 if (self.llvm_object) |_| return;
39824124 const sym_index = exp.sym_index orelse return;
3983 _ = sym_index;
3984 // self.globals_free_list.append(self.base.allocator, sym_index) catch {};
3985 // const global = &self.globals.items[sym_index];
3986 // log.warn("deleting export '{s}': {}", .{ self.getString(global.n_strx), global });
3987 // assert(self.symbol_resolver.remove(global.n_strx));
3988 // global.n_type = 0;
3989 // global.n_strx = 0;
3990 // global.n_value = 0;
4125
4126 const gpa = self.base.allocator;
4127
4128 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
4129 const sym = self.getSymbolPtr(sym_loc);
4130 const sym_name = self.getSymbolName(sym_loc);
4131 log.debug("deleting export '{s}'", .{sym_name});
4132 sym.* = .{
4133 .n_strx = 0,
4134 .n_type = 0,
4135 .n_sect = 0,
4136 .n_desc = 0,
4137 .n_value = 0,
4138 };
4139 self.locals_free_list.append(gpa, sym_index) catch {};
4140
4141 if (self.globals.get(sym_name)) |global| blk: {
4142 if (global.sym_index != sym_index) break :blk;
4143 if (global.file != null) break :blk;
4144 const kv = self.globals.fetchSwapRemove(sym_name);
4145 gpa.free(kv.?.key);
4146 }
39914147}
39924148
39934149fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
......@@ -4026,7 +4182,10 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
40264182 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };
40274183 if (self.got_entries_table.get(got_target)) |got_index| {
40284184 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};
4029 self.got_entries.items[got_index] = .{ .target = .{ .sym_index = 0, .file = null }, .atom = undefined };
4185 self.got_entries.items[got_index] = .{
4186 .target = .{ .sym_index = 0, .file = null },
4187 .atom = undefined,
4188 };
40304189 _ = self.got_entries_table.swapRemove(got_target);
40314190
40324191 if (self.d_sym) |*d_sym| {
......@@ -4102,7 +4261,7 @@ fn populateMissingMetadata(self: *MachO) !void {
41024261
41034262 if (self.text_segment_cmd_index == null) {
41044263 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4105 const needed_size = if (self.needs_prealloc) blk: {
4264 const needed_size = if (self.mode == .incremental) blk: {
41064265 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
41074266 const program_code_size_hint = self.base.options.program_code_size_hint;
41084267 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
......@@ -4133,7 +4292,7 @@ fn populateMissingMetadata(self: *MachO) !void {
41334292 .aarch64 => 2,
41344293 else => unreachable, // unhandled architecture type
41354294 };
4136 const needed_size = if (self.needs_prealloc) self.base.options.program_code_size_hint else 0;
4295 const needed_size = if (self.mode == .incremental) self.base.options.program_code_size_hint else 0;
41374296 self.text_section_index = try self.initSection(
41384297 self.text_segment_cmd_index.?,
41394298 "__text",
......@@ -4156,7 +4315,7 @@ fn populateMissingMetadata(self: *MachO) !void {
41564315 .aarch64 => 3 * @sizeOf(u32),
41574316 else => unreachable, // unhandled architecture type
41584317 };
4159 const needed_size = if (self.needs_prealloc) stub_size * self.base.options.symbol_count_hint else 0;
4318 const needed_size = if (self.mode == .incremental) stub_size * self.base.options.symbol_count_hint else 0;
41604319 self.stubs_section_index = try self.initSection(
41614320 self.text_segment_cmd_index.?,
41624321 "__stubs",
......@@ -4185,7 +4344,7 @@ fn populateMissingMetadata(self: *MachO) !void {
41854344 .aarch64 => 3 * @sizeOf(u32),
41864345 else => unreachable,
41874346 };
4188 const needed_size = if (self.needs_prealloc)
4347 const needed_size = if (self.mode == .incremental)
41894348 stub_size * self.base.options.symbol_count_hint + preamble_size
41904349 else
41914350 0;
......@@ -4205,7 +4364,7 @@ fn populateMissingMetadata(self: *MachO) !void {
42054364 var vmaddr: u64 = 0;
42064365 var fileoff: u64 = 0;
42074366 var needed_size: u64 = 0;
4208 if (self.needs_prealloc) {
4367 if (self.mode == .incremental) {
42094368 const base = self.getSegmentAllocBase(&.{self.text_segment_cmd_index.?});
42104369 vmaddr = base.vmaddr;
42114370 fileoff = base.fileoff;
......@@ -4234,7 +4393,7 @@ fn populateMissingMetadata(self: *MachO) !void {
42344393 }
42354394
42364395 if (self.got_section_index == null) {
4237 const needed_size = if (self.needs_prealloc)
4396 const needed_size = if (self.mode == .incremental)
42384397 @sizeOf(u64) * self.base.options.symbol_count_hint
42394398 else
42404399 0;
......@@ -4255,7 +4414,7 @@ fn populateMissingMetadata(self: *MachO) !void {
42554414 var vmaddr: u64 = 0;
42564415 var fileoff: u64 = 0;
42574416 var needed_size: u64 = 0;
4258 if (self.needs_prealloc) {
4417 if (self.mode == .incremental) {
42594418 const base = self.getSegmentAllocBase(&.{self.data_const_segment_cmd_index.?});
42604419 vmaddr = base.vmaddr;
42614420 fileoff = base.fileoff;
......@@ -4284,7 +4443,7 @@ fn populateMissingMetadata(self: *MachO) !void {
42844443 }
42854444
42864445 if (self.la_symbol_ptr_section_index == null) {
4287 const needed_size = if (self.needs_prealloc)
4446 const needed_size = if (self.mode == .incremental)
42884447 @sizeOf(u64) * self.base.options.symbol_count_hint
42894448 else
42904449 0;
......@@ -4301,7 +4460,10 @@ fn populateMissingMetadata(self: *MachO) !void {
43014460 }
43024461
43034462 if (self.data_section_index == null) {
4304 const needed_size = if (self.needs_prealloc) @sizeOf(u64) * self.base.options.symbol_count_hint else 0;
4463 const needed_size = if (self.mode == .incremental)
4464 @sizeOf(u64) * self.base.options.symbol_count_hint
4465 else
4466 0;
43054467 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
43064468 self.data_section_index = try self.initSection(
43074469 self.data_segment_cmd_index.?,
......@@ -4313,7 +4475,10 @@ fn populateMissingMetadata(self: *MachO) !void {
43134475 }
43144476
43154477 if (self.tlv_section_index == null) {
4316 const needed_size = if (self.needs_prealloc) @sizeOf(u64) * self.base.options.symbol_count_hint else 0;
4478 const needed_size = if (self.mode == .incremental)
4479 @sizeOf(u64) * self.base.options.symbol_count_hint
4480 else
4481 0;
43174482 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
43184483 self.tlv_section_index = try self.initSection(
43194484 self.data_segment_cmd_index.?,
......@@ -4327,7 +4492,10 @@ fn populateMissingMetadata(self: *MachO) !void {
43274492 }
43284493
43294494 if (self.tlv_data_section_index == null) {
4330 const needed_size = if (self.needs_prealloc) @sizeOf(u64) * self.base.options.symbol_count_hint else 0;
4495 const needed_size = if (self.mode == .incremental)
4496 @sizeOf(u64) * self.base.options.symbol_count_hint
4497 else
4498 0;
43314499 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
43324500 self.tlv_data_section_index = try self.initSection(
43334501 self.data_segment_cmd_index.?,
......@@ -4341,7 +4509,10 @@ fn populateMissingMetadata(self: *MachO) !void {
43414509 }
43424510
43434511 if (self.tlv_bss_section_index == null) {
4344 const needed_size = if (self.needs_prealloc) @sizeOf(u64) * self.base.options.symbol_count_hint else 0;
4512 const needed_size = if (self.mode == .incremental)
4513 @sizeOf(u64) * self.base.options.symbol_count_hint
4514 else
4515 0;
43454516 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
43464517 self.tlv_bss_section_index = try self.initSection(
43474518 self.data_segment_cmd_index.?,
......@@ -4355,7 +4526,10 @@ fn populateMissingMetadata(self: *MachO) !void {
43554526 }
43564527
43574528 if (self.bss_section_index == null) {
4358 const needed_size = if (self.needs_prealloc) @sizeOf(u64) * self.base.options.symbol_count_hint else 0;
4529 const needed_size = if (self.mode == .incremental)
4530 @sizeOf(u64) * self.base.options.symbol_count_hint
4531 else
4532 0;
43594533 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
43604534 self.bss_section_index = try self.initSection(
43614535 self.data_segment_cmd_index.?,
......@@ -4372,7 +4546,7 @@ fn populateMissingMetadata(self: *MachO) !void {
43724546 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
43734547 var vmaddr: u64 = 0;
43744548 var fileoff: u64 = 0;
4375 if (self.needs_prealloc) {
4549 if (self.mode == .incremental) {
43764550 const base = self.getSegmentAllocBase(&.{self.data_segment_cmd_index.?});
43774551 vmaddr = base.vmaddr;
43784552 fileoff = base.fileoff;
......@@ -4740,14 +4914,14 @@ fn initSection(
47404914 var sect = macho.section_64{
47414915 .sectname = makeStaticString(sectname),
47424916 .segname = seg.inner.segname,
4743 .size = if (self.needs_prealloc) @intCast(u32, size) else 0,
4917 .size = if (self.mode == .incremental) @intCast(u32, size) else 0,
47444918 .@"align" = alignment,
47454919 .flags = opts.flags,
47464920 .reserved1 = opts.reserved1,
47474921 .reserved2 = opts.reserved2,
47484922 };
47494923
4750 if (self.needs_prealloc) {
4924 if (self.mode == .incremental) {
47514925 const alignment_pow_2 = try math.powi(u32, 2, alignment);
47524926 const padding: ?u32 = if (segment_id == self.text_segment_cmd_index.?)
47534927 @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size)
......@@ -4967,7 +5141,7 @@ fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u3
49675141
49685142fn allocateAtomCommon(self: *MachO, atom: *Atom, match: MatchingSection) !void {
49695143 const sym = atom.getSymbolPtr(self);
4970 if (self.needs_prealloc) {
5144 if (self.mode == .incremental) {
49715145 const size = atom.size;
49725146 const alignment = try math.powi(u32, 2, atom.alignment);
49735147 const vaddr = try self.allocateAtom(atom, size, alignment, match);
......@@ -5108,27 +5282,29 @@ pub fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void
51085282pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
51095283 const gpa = self.base.allocator;
51105284 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
5111 defer gpa.free(sym_name);
5285 const global_index = @intCast(u32, self.globals.values().len);
5286 const gop = try self.globals.getOrPut(gpa, sym_name);
5287 defer if (gop.found_existing) gpa.free(sym_name);
51125288
5113 if (self.globals.getIndex(sym_name)) |global_index| {
5114 return @intCast(u32, global_index);
5289 if (gop.found_existing) {
5290 return @intCast(u32, self.globals.getIndex(sym_name).?);
51155291 }
51165292
5117 const n_strx = try self.strtab.insert(gpa, sym_name);
51185293 const sym_index = @intCast(u32, self.locals.items.len);
51195294 try self.locals.append(gpa, .{
5120 .n_strx = n_strx,
5295 .n_strx = try self.strtab.insert(gpa, sym_name),
51215296 .n_type = macho.N_UNDF,
51225297 .n_sect = 0,
51235298 .n_desc = 0,
51245299 .n_value = 0,
51255300 });
5126 try self.globals.putNoClobber(gpa, sym_name, .{
5301 gop.value_ptr.* = .{
51275302 .sym_index = sym_index,
51285303 .file = null,
5129 });
5130 const global_index = self.globals.getIndex(sym_name).?;
5131 return @intCast(u32, global_index);
5304 };
5305 try self.unresolved.putNoClobber(gpa, global_index, true);
5306
5307 return global_index;
51325308}
51335309
51345310fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {
......@@ -5927,7 +6103,7 @@ fn writeDices(self: *MachO) !void {
59276103 self.load_commands_dirty = true;
59286104}
59296105
5930fn writeSymbolTable(self: *MachO) !void {
6106fn writeSymtab(self: *MachO) !void {
59316107 const tracy = trace(@src());
59326108 defer tracy.end();
59336109
......@@ -6143,7 +6319,7 @@ fn writeSymbolTable(self: *MachO) !void {
61436319 self.load_commands_dirty = true;
61446320}
61456321
6146fn writeStringTable(self: *MachO) !void {
6322fn writeStrtab(self: *MachO) !void {
61476323 const tracy = trace(@src());
61486324 defer tracy.end();
61496325
......@@ -6173,8 +6349,8 @@ fn writeLinkeditSegment(self: *MachO) !void {
61736349 try self.writeDyldInfoData();
61746350 try self.writeFunctionStarts();
61756351 try self.writeDices();
6176 try self.writeSymbolTable();
6177 try self.writeStringTable();
6352 try self.writeSymtab();
6353 try self.writeStrtab();
61786354
61796355 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
61806356}
......@@ -6391,6 +6567,27 @@ pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
63916567 }
63926568}
63936569
6570/// Returns GOT atom that references `sym_with_loc` if one exists.
6571/// Returns null otherwise.
6572pub fn getGotAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6573 const got_index = self.got_entries_table.get(sym_with_loc) orelse return null;
6574 return self.got_entries.items[got_index].atom;
6575}
6576
6577/// Returns stubs atom that references `sym_with_loc` if one exists.
6578/// Returns null otherwise.
6579pub fn getStubsAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6580 const stubs_index = self.stubs_table.get(sym_with_loc) orelse return null;
6581 return self.stubs.items[stubs_index].atom;
6582}
6583
6584/// Returns TLV pointer atom that references `sym_with_loc` if one exists.
6585/// Returns null otherwise.
6586pub fn getTlvPtrAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6587 const tlv_ptr_index = self.tlv_ptr_entries_table.get(sym_with_loc) orelse return null;
6588 return self.tlv_ptr_entries.items[tlv_ptr_index].atom;
6589}
6590
63946591pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate: anytype) usize {
63956592 if (!@hasDecl(@TypeOf(predicate), "predicate"))
63966593 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
......@@ -6481,6 +6678,8 @@ fn snapshotState(self: *MachO) !void {
64816678 .payload = .{ .name = sect_name },
64826679 });
64836680
6681 const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
6682
64846683 var atom: *Atom = self.atoms.get(key) orelse {
64856684 try nodes.append(.{
64866685 .address = sect.addr + sect.size,
......@@ -6495,35 +6694,23 @@ fn snapshotState(self: *MachO) !void {
64956694 }
64966695
64976696 while (true) {
6498 const atom_sym = self.locals.items[atom.sym_index];
6499 const should_skip_atom: bool = blk: {
6500 if (self.mh_execute_header_index) |index| {
6501 if (index == atom.sym_index) break :blk true;
6502 }
6503 if (mem.eql(u8, self.getString(atom_sym.n_strx), "___dso_handle")) break :blk true;
6504 break :blk false;
6505 };
6506
6507 if (should_skip_atom) {
6508 if (atom.next) |next| {
6509 atom = next;
6510 } else break;
6511 continue;
6512 }
6513
6697 const atom_sym = atom.getSymbol(self);
65146698 var node = Snapshot.Node{
65156699 .address = atom_sym.n_value,
65166700 .tag = .atom_start,
65176701 .payload = .{
6518 .name = self.getString(atom_sym.n_strx),
6519 .is_global = self.symbol_resolver.contains(atom_sym.n_strx),
6702 .name = atom.getName(self),
6703 .is_global = self.globals.contains(atom.getName(self)),
65206704 },
65216705 };
65226706
65236707 var aliases = std.ArrayList([]const u8).init(arena);
65246708 for (atom.contained.items) |sym_off| {
65256709 if (sym_off.offset == 0) {
6526 try aliases.append(self.getString(self.locals.items[sym_off.sym_index].n_strx));
6710 try aliases.append(self.getSymbolName(.{
6711 .sym_index = sym_off.sym_index,
6712 .file = atom.file,
6713 }));
65276714 }
65286715 }
65296716 node.payload.aliases = aliases.toOwnedSlice();
......@@ -6531,69 +6718,39 @@ fn snapshotState(self: *MachO) !void {
65316718
65326719 var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);
65336720 for (atom.relocs.items) |rel| {
6534 const arch = self.base.options.target.cpu.arch;
65356721 const source_addr = blk: {
6536 const sym = self.locals.items[atom.sym_index];
6537 break :blk sym.n_value + rel.offset;
6722 const source_sym = atom.getSymbol(self);
6723 break :blk source_sym.n_value + rel.offset;
65386724 };
65396725 const target_addr = blk: {
6540 const is_via_got = got: {
6541 switch (arch) {
6542 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {
6543 .ARM64_RELOC_GOT_LOAD_PAGE21, .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => true,
6544 else => false,
6545 },
6546 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
6547 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
6548 else => false,
6549 },
6550 else => unreachable,
6726 const target_atom = (try rel.getTargetAtom(self)) orelse {
6727 // If there is no atom for target, we still need to check for special, atom-less
6728 // symbols such as `___dso_handle`.
6729 const target_name = self.getSymbolName(rel.target);
6730 if (self.globals.contains(target_name)) {
6731 const atomless_sym = self.getSymbol(rel.target);
6732 break :blk atomless_sym.n_value;
65516733 }
6734 break :blk 0;
65526735 };
6553
6554 if (is_via_got) {
6555 const got_index = self.got_entries_table.get(rel.target) orelse break :blk 0;
6556 const got_atom = self.got_entries.items[got_index].atom;
6557 break :blk self.locals.items[got_atom.sym_index].n_value;
6558 }
6559
6560 switch (rel.target) {
6561 .local => |sym_index| {
6562 const sym = self.locals.items[sym_index];
6563 const is_tlv = is_tlv: {
6564 const source_sym = self.locals.items[atom.sym_index];
6565 const match = self.section_ordinals.keys()[source_sym.n_sect - 1];
6566 const match_seg = self.load_commands.items[match.seg].segment;
6567 const match_sect = match_seg.sections.items[match.sect];
6568 break :is_tlv match_sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
6569 };
6570 if (is_tlv) {
6571 const match_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
6572 const base_address = inner: {
6573 if (self.tlv_data_section_index) |i| {
6574 break :inner match_seg.sections.items[i].addr;
6575 } else if (self.tlv_bss_section_index) |i| {
6576 break :inner match_seg.sections.items[i].addr;
6577 } else unreachable;
6578 };
6579 break :blk sym.n_value - base_address;
6580 }
6581 break :blk sym.n_value;
6582 },
6583 .global => |n_strx| {
6584 const resolv = self.symbol_resolver.get(n_strx).?;
6585 switch (resolv.where) {
6586 .global => break :blk self.globals.items[resolv.where_index].n_value,
6587 .undef => {
6588 if (self.stubs_table.get(n_strx)) |stub_index| {
6589 const stub_atom = self.stubs.items[stub_index];
6590 break :blk self.locals.items[stub_atom.sym_index].n_value;
6591 }
6592 break :blk 0;
6593 },
6594 }
6595 },
6596 }
6736 const target_sym = if (target_atom.isSymbolContained(rel.target, self))
6737 self.getSymbol(rel.target)
6738 else
6739 target_atom.getSymbol(self);
6740 const base_address: u64 = if (is_tlv) base_address: {
6741 const sect_id: u16 = sect_id: {
6742 if (self.tlv_data_section_index) |i| {
6743 break :sect_id i;
6744 } else if (self.tlv_bss_section_index) |i| {
6745 break :sect_id i;
6746 } else unreachable;
6747 };
6748 break :base_address self.getSection(.{
6749 .seg = self.data_segment_cmd_index.?,
6750 .sect = sect_id,
6751 }).addr;
6752 } else 0;
6753 break :blk target_sym.n_value - base_address;
65976754 };
65986755
65996756 relocs.appendAssumeCapacity(.{
......@@ -6614,15 +6771,18 @@ fn snapshotState(self: *MachO) !void {
66146771 var next_i: usize = 0;
66156772 var last_rel: usize = 0;
66166773 while (next_i < atom.contained.items.len) : (next_i += 1) {
6617 const loc = atom.contained.items[next_i];
6618 const cont_sym = self.locals.items[loc.sym_index];
6619 const cont_sym_name = self.getString(cont_sym.n_strx);
6774 const loc = SymbolWithLoc{
6775 .sym_index = atom.contained.items[next_i].sym_index,
6776 .file = atom.file,
6777 };
6778 const cont_sym = self.getSymbol(loc);
6779 const cont_sym_name = self.getSymbolName(loc);
66206780 var contained_node = Snapshot.Node{
66216781 .address = cont_sym.n_value,
66226782 .tag = .atom_start,
66236783 .payload = .{
66246784 .name = cont_sym_name,
6625 .is_global = self.symbol_resolver.contains(cont_sym.n_strx),
6785 .is_global = self.globals.contains(cont_sym_name),
66266786 },
66276787 };
66286788
......@@ -6630,10 +6790,14 @@ fn snapshotState(self: *MachO) !void {
66306790 var inner_aliases = std.ArrayList([]const u8).init(arena);
66316791 while (true) {
66326792 if (next_i + 1 >= atom.contained.items.len) break;
6633 const next_sym = self.locals.items[atom.contained.items[next_i + 1].sym_index];
6793 const next_sym_loc = SymbolWithLoc{
6794 .sym_index = atom.contained.items[next_i + 1].sym_index,
6795 .file = atom.file,
6796 };
6797 const next_sym = self.getSymbol(next_sym_loc);
66346798 if (next_sym.n_value != cont_sym.n_value) break;
6635 const next_sym_name = self.getString(next_sym.n_strx);
6636 if (self.symbol_resolver.contains(next_sym.n_strx)) {
6799 const next_sym_name = self.getSymbolName(next_sym_loc);
6800 if (self.globals.contains(next_sym_name)) {
66376801 try inner_aliases.append(contained_node.payload.name);
66386802 contained_node.payload.name = next_sym_name;
66396803 contained_node.payload.is_global = true;
......@@ -6642,7 +6806,10 @@ fn snapshotState(self: *MachO) !void {
66426806 }
66436807
66446808 const cont_size = if (next_i + 1 < atom.contained.items.len)
6645 self.locals.items[atom.contained.items[next_i + 1].sym_index].n_value - cont_sym.n_value
6809 self.getSymbol(.{
6810 .sym_index = atom.contained.items[next_i + 1].sym_index,
6811 .file = atom.file,
6812 }).n_value - cont_sym.n_value
66466813 else
66476814 atom_sym.n_value + atom.size - cont_sym.n_value;
66486815
......@@ -6695,7 +6862,11 @@ pub fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
66956862 buf[0] = 's';
66966863 }
66976864 if (sym.ext()) {
6698 buf[1] = 'e';
6865 if (sym.weakDef() or sym.pext()) {
6866 buf[1] = 'w';
6867 } else {
6868 buf[1] = 'e';
6869 }
66996870 }
67006871 if (sym.tentative()) {
67016872 buf[2] = 't';
src/link/MachO/Atom.zig+9-9
......@@ -187,7 +187,7 @@ pub const Relocation = struct {
187187
188188 const target_sym = macho_file.getSymbol(self.target);
189189 if (is_via_got) {
190 const got_index = macho_file.got_entries_table.get(self.target) orelse {
190 const got_atom = macho_file.getGotAtomForSymbol(self.target) orelse {
191191 log.err("expected GOT entry for symbol", .{});
192192 if (target_sym.undf()) {
193193 log.err(" import('{s}')", .{macho_file.getSymbolName(self.target)});
......@@ -197,14 +197,12 @@ pub const Relocation = struct {
197197 log.err(" this is an internal linker error", .{});
198198 return error.FailedToResolveRelocationTarget;
199199 };
200 return macho_file.got_entries.items[got_index].atom;
200 return got_atom;
201201 }
202202
203 if (macho_file.stubs_table.get(self.target)) |stub_index| {
204 return macho_file.stubs.items[stub_index].atom;
205 } else if (macho_file.tlv_ptr_entries_table.get(self.target)) |tlv_ptr_index| {
206 return macho_file.tlv_ptr_entries.items[tlv_ptr_index].atom;
207 } else return macho_file.getAtomForSymbol(self.target);
203 if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom;
204 if (macho_file.getTlvPtrAtomForSymbol(self.target)) |tlv_ptr_atom| return tlv_ptr_atom;
205 return macho_file.getAtomForSymbol(self.target);
208206 }
209207};
210208
......@@ -402,7 +400,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
402400 .n_type = macho.N_SECT,
403401 .n_sect = context.macho_file.getSectionOrdinal(match),
404402 .n_desc = 0,
405 .n_value = 0,
403 .n_value = sect.addr,
406404 });
407405 try object.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
408406 break :blk sym_index;
......@@ -499,8 +497,10 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
499497 // Note for the future self: when r_extern == 0, we should subtract correction from the
500498 // addend.
501499 const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
500 // We need to add base_offset, i.e., offset of this atom wrt to the source
501 // section. Otherwise, the addend will over-/under-shoot.
502502 addend += @intCast(i64, context.base_addr + offset + 4) -
503 @intCast(i64, target_sect_base_addr);
503 @intCast(i64, target_sect_base_addr) + context.base_offset;
504504 }
505505 },
506506 .X86_64_RELOC_TLV => {
src/link/MachO/DebugSymbols.zig+2-2
......@@ -5,7 +5,7 @@ const build_options = @import("build_options");
55const assert = std.debug.assert;
66const fs = std.fs;
77const link = @import("../../link.zig");
8const log = std.log.scoped(.link);
8const log = std.log.scoped(.dsym);
99const macho = std.macho;
1010const makeStaticString = MachO.makeStaticString;
1111const math = std.math;
......@@ -60,7 +60,7 @@ debug_aranges_section_dirty: bool = false,
6060debug_info_header_dirty: bool = false,
6161debug_line_header_dirty: bool = false,
6262
63strtab: StringTable(.link) = .{},
63strtab: StringTable(.strtab) = .{},
6464
6565relocs: std.ArrayListUnmanaged(Reloc) = .{},
6666
src/link/MachO/Object.zig+20-10
......@@ -270,7 +270,7 @@ const SymbolAtIndex = struct {
270270
271271 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {
272272 const sym = self.getSymbol(ctx);
273 if (sym.n_strx == 0) return "";
273 assert(sym.n_strx < ctx.strtab.len);
274274 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.ptr + sym.n_strx), 0);
275275 }
276276
......@@ -359,15 +359,17 @@ fn filterDice(
359359 return dices[start..end];
360360}
361361
362/// Splits object into atoms assuming whole cache mode aka traditional linking mode.
363pub fn splitIntoAtomsWhole(self: *Object, macho_file: *MachO, object_id: u32) !void {
362/// Splits object into atoms assuming one-shot linking mode.
363pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {
364 assert(macho_file.mode == .one_shot);
365
364366 const tracy = trace(@src());
365367 defer tracy.end();
366368
367369 const gpa = macho_file.base.allocator;
368370 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
369371
370 log.debug("splitting object({d}, {s}) into atoms: whole cache mode", .{ object_id, self.name });
372 log.debug("splitting object({d}, {s}) into atoms: one-shot mode", .{ object_id, self.name });
371373
372374 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
373375 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
......@@ -416,11 +418,11 @@ pub fn splitIntoAtomsWhole(self: *Object, macho_file: *MachO, object_id: u32) !v
416418 log.debug(" unhandled section", .{});
417419 continue;
418420 };
419 const target_sect = macho_file.getSection(match);
421
420422 log.debug(" output sect({d}, '{s},{s}')", .{
421423 macho_file.getSectionOrdinal(match),
422 target_sect.segName(),
423 target_sect.sectName(),
424 macho_file.getSection(match).segName(),
425 macho_file.getSection(match).sectName(),
424426 });
425427
426428 const is_zerofill = blk: {
......@@ -585,10 +587,19 @@ fn createAtomFromSubsection(
585587 sect: macho.section_64,
586588) !*Atom {
587589 const gpa = macho_file.base.allocator;
588 const sym = &self.symtab.items[sym_index];
590 const sym = self.symtab.items[sym_index];
589591 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
590592 atom.file = object_id;
591 sym.n_sect = macho_file.getSectionOrdinal(match);
593 self.symtab.items[sym_index].n_sect = macho_file.getSectionOrdinal(match);
594
595 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
596 sym_index,
597 self.getString(sym.n_strx),
598 macho_file.getSectionOrdinal(match),
599 macho_file.getSection(match).segName(),
600 macho_file.getSection(match).sectName(),
601 object_id,
602 });
592603
593604 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
594605 try self.managed_atoms.append(gpa, atom);
......@@ -669,7 +680,6 @@ fn createAtomFromSubsection(
669680 // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
670681 break :blk .static;
671682 } else null;
672
673683 atom.contained.appendAssumeCapacity(.{
674684 .sym_index = inner_sym_index.index,
675685 .offset = inner_sym.n_value - sym.n_value,
test/link/macho/objcpp/build.zig-1
......@@ -16,7 +16,6 @@ pub fn build(b: *Builder) void {
1616 // TODO when we figure out how to ship framework stubs for cross-compilation,
1717 // populate paths to the sysroot here.
1818 exe.linkFramework("Foundation");
19 exe.link_gc_sections = true;
2019
2120 const run_cmd = exe.run();
2221 run_cmd.expectStdOutEqual("Hello from C++ and Zig");