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,...@@ -70,10 +70,10 @@ d_sym: ?DebugSymbols = null,
70/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.70/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.
71page_size: u16,71page_size: u16,
7272
73/// If true, the linker will preallocate several sections and segments before starting the linking73/// Mode of operation: incremental - will preallocate segments/sections and is compatible with
74/// process. This is for example true for stage2 debug builds, however, this is false for stage174/// watch and HCS modes of operation; one_shot - will link relocatables in a traditional, one-shot
75/// and potentially stage2 release builds in the future.75/// fashion (default for LLVM backend).
76needs_prealloc: bool = true,76mode: enum { incremental, one_shot },
7777
78/// The absolute address of the entry point.78/// The absolute address of the entry point.
79entry_addr: ?u64 = null,79entry_addr: ?u64 = null,
...@@ -153,7 +153,7 @@ rustc_section_size: u64 = 0,...@@ -153,7 +153,7 @@ rustc_section_size: u64 = 0,
153153
154locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},154locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
155globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},155globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
156unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{},156unresolved: std.AutoArrayHashMapUnmanaged(u32, bool) = .{},
157157
158locals_free_list: std.ArrayListUnmanaged(u32) = .{},158locals_free_list: std.ArrayListUnmanaged(u32) = .{},
159159
...@@ -161,7 +161,7 @@ dyld_stub_binder_index: ?u32 = null,...@@ -161,7 +161,7 @@ dyld_stub_binder_index: ?u32 = null,
161dyld_private_atom: ?*Atom = null,161dyld_private_atom: ?*Atom = null,
162stub_helper_preamble_atom: ?*Atom = null,162stub_helper_preamble_atom: ?*Atom = null,
163163
164strtab: StringTable(.link) = .{},164strtab: StringTable(.strtab) = .{},
165165
166tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{},166tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{},
167tlv_ptr_entries_free_list: std.ArrayListUnmanaged(u32) = .{},167tlv_ptr_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
...@@ -182,6 +182,12 @@ sections_order_dirty: bool = false,...@@ -182,6 +182,12 @@ sections_order_dirty: bool = false,
182has_dices: bool = false,182has_dices: bool = false,
183has_stabs: bool = false,183has_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
185section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},191section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},
186192
187/// A list of atoms that have surplus capacity. This list can have false193/// 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 {...@@ -387,7 +393,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
387 const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator);393 const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator);
388 const use_llvm = build_options.have_llvm and options.use_llvm;394 const use_llvm = build_options.have_llvm and options.use_llvm;
389 const use_stage1 = build_options.is_stage1 and options.use_stage1;395 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
392 const self = try gpa.create(MachO);397 const self = try gpa.create(MachO);
393 errdefer gpa.destroy(self);398 errdefer gpa.destroy(self);
...@@ -400,8 +405,14 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {...@@ -400,8 +405,14 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
400 .file = null,405 .file = null,
401 },406 },
402 .page_size = page_size,407 .page_size = page_size,
403 .code_signature = if (requires_adhoc_codesig) CodeSignature.init(page_size) else null,408 .code_signature = if (requires_adhoc_codesig)
404 .needs_prealloc = needs_prealloc,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,
405 };416 };
406417
407 if (use_llvm and !use_stage1) {418 if (use_llvm and !use_stage1) {
...@@ -429,32 +440,198 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -429,32 +440,198 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !v
429 return error.TODOImplementWritingStaticLibFiles;440 return error.TODOImplementWritingStaticLibFiles;
430 }441 }
431 }442 }
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 }
433}448}
434449
435pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {450pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
436 const tracy = trace(@src());451 const tracy = trace(@src());
437 defer tracy.end();452 defer tracy.end();
438453
439 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;454 if (build_options.have_llvm) {
440
441 if (build_options.have_llvm and !use_stage1) {
442 if (self.llvm_object) |llvm_object| {455 if (self.llvm_object) |llvm_object| {
443 try llvm_object.flushModule(comp, prog_node);456 return 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 }
451 }457 }
452 }458 }
453459
460 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
461 defer arena_allocator.deinit();
462 const arena = arena_allocator.allocator();
463
454 var sub_prog_node = prog_node.start("MachO Flush", 0);464 var sub_prog_node = prog_node.start("MachO Flush", 0);
455 sub_prog_node.activate();465 sub_prog_node.activate();
456 defer sub_prog_node.end();466 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
458 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);635 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
459 defer arena_allocator.deinit();636 defer arena_allocator.deinit();
460 const arena = arena_allocator.allocator();637 const arena = arena_allocator.allocator();
...@@ -465,7 +642,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -465,7 +642,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
465 // If there is no Zig code to compile, then we should skip flushing the output file because it642 // If there is no Zig code to compile, then we should skip flushing the output file because it
466 // will not be part of the linker line anyway.643 // will not be part of the linker line anyway.
467 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {644 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
468 if (use_stage1) {645 if (self.base.options.use_stage1) {
469 const obj_basename = try std.zig.binNameAlloc(arena, .{646 const obj_basename = try std.zig.binNameAlloc(arena, .{
470 .root_name = self.base.options.root_name,647 .root_name = self.base.options.root_name,
471 .target = self.base.options.target,648 .target = self.base.options.target,
...@@ -482,20 +659,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -482,20 +659,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
482 }659 }
483 }660 }
484661
485 const obj_basename = self.base.intermediary_basename orelse break :blk null;662 try self.flushModule(comp, prog_node);
486663
487 if (fs.path.dirname(full_out_path)) |dirname| {664 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.? });
489 } else {666 } else {
490 break :blk obj_basename;667 break :blk self.base.intermediary_basename.?;
491 }668 }
492 } else null;669 } else null;
493670
494 if (self.d_sym) |*d_sym| {671 var sub_prog_node = prog_node.start("MachO Flush", 0);
495 if (self.base.options.module) |module| {672 sub_prog_node.activate();
496 try d_sym.dwarf.flushModule(&self.base, module);673 sub_prog_node.context.refresh();
497 }674 defer sub_prog_node.end();
498 }
499675
500 const is_lib = self.base.options.output_mode == .Lib;676 const is_lib = self.base.options.output_mode == .Lib;
501 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;677 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...@@ -503,25 +679,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
503 const stack_size = self.base.options.stack_size_override orelse 0;679 const stack_size = self.base.options.stack_size_override orelse 0;
504680
505 const id_symlink_basename = "zld.id";681 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
516 var man: Cache.Manifest = undefined;683 var man: Cache.Manifest = undefined;
517 defer if (!self.base.options.disable_lld_caching) man.deinit();684 defer if (!self.base.options.disable_lld_caching) man.deinit();
518685
519 var digest: [Cache.hex_digest_len]u8 = undefined;686 var digest: [Cache.hex_digest_len]u8 = undefined;
520687
521 cache: {688 if (!self.base.options.disable_lld_caching) {
522 if ((use_stage1 and self.base.options.disable_lld_caching) or self.base.options.cache_mode == .whole)
523 break :cache;
524
525 man = comp.cache_parent.obtain();689 man = comp.cache_parent.obtain();
526690
527 // We are about to obtain this lock, so here we give other processes a chance first.691 // 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...@@ -564,7 +728,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
564728
565 var prev_digest_buf: [digest.len]u8 = undefined;729 var prev_digest_buf: [digest.len]u8 = undefined;
566 const prev_digest: []u8 = Cache.readSmallFile(730 const prev_digest: []u8 = Cache.readSmallFile(
567 cache_dir_handle,731 directory.handle,
568 id_symlink_basename,732 id_symlink_basename,
569 &prev_digest_buf,733 &prev_digest_buf,
570 ) catch |err| blk: {734 ) catch |err| blk: {
...@@ -577,15 +741,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -577,15 +741,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
577 };741 };
578 if (mem.eql(u8, prev_digest, &digest)) {742 if (mem.eql(u8, prev_digest, &digest)) {
579 // Hot diggity dog! The output binary is already there.743 // Hot diggity dog! The output binary is already there.
580744 log.debug("MachO Zld digest={s} match - skipping invocation", .{
581 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;745 std.fmt.fmtSliceHexLower(&digest),
582 if (use_llvm or use_stage1) {746 });
583 log.debug("MachO Zld digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});747 self.base.lock = man.toOwnedLock();
584 self.base.lock = man.toOwnedLock();748 return;
585 return;
586 } else {
587 log.debug("MachO Zld digest={s} match", .{std.fmt.fmtSliceHexLower(&digest)});
588 }
589 }749 }
590 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{750 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
591 std.fmt.fmtSliceHexLower(prev_digest),751 std.fmt.fmtSliceHexLower(prev_digest),
...@@ -593,7 +753,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -593,7 +753,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
593 });753 });
594754
595 // We are about to change the output file to be different, so we invalidate the build hash now.755 // 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) {
597 error.FileNotFound => {},757 error.FileNotFound => {},
598 else => |e| return e,758 else => |e| return e,
599 };759 };
...@@ -624,24 +784,22 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -624,24 +784,22 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
624 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});784 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
625 }785 }
626 } else {786 } else {
627 if (use_stage1) {787 const sub_path = self.base.options.emit.?.sub_path;
628 const sub_path = self.base.options.emit.?.sub_path;788 self.base.file = try directory.handle.createFile(sub_path, .{
629 self.base.file = try cache_dir_handle.createFile(sub_path, .{789 .truncate = true,
630 .truncate = true,790 .read = true,
631 .read = true,791 .mode = link.determineMode(self.base.options),
632 .mode = link.determineMode(self.base.options),792 });
633 });793 // Index 0 is always a null symbol.
634 // Index 0 is always a null symbol.794 try self.locals.append(self.base.allocator, .{
635 try self.locals.append(self.base.allocator, .{795 .n_strx = 0,
636 .n_strx = 0,796 .n_type = 0,
637 .n_type = 0,797 .n_sect = 0,
638 .n_sect = 0,798 .n_desc = 0,
639 .n_desc = 0,799 .n_value = 0,
640 .n_value = 0,800 });
641 });801 try self.strtab.buffer.append(self.base.allocator, 0);
642 try self.strtab.buffer.append(self.base.allocator, 0);802 try self.populateMissingMetadata();
643 try self.populateMissingMetadata();
644 }
645803
646 var lib_not_found = false;804 var lib_not_found = false;
647 var framework_not_found = false;805 var framework_not_found = false;
...@@ -757,40 +915,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -757,40 +915,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
757 }915 }
758 }916 }
759917
760 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.918 try self.resolveLibSystem(arena, comp, lib_dirs.items, &libs);
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 }
794919
795 // frameworks920 // frameworks
796 var framework_dirs = std.ArrayList([]const u8).init(arena);921 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...@@ -1003,7 +1128,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1003 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);1128 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
1004 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);1129 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
10051130
1006 try self.createMhExecuteHeaderSymbol();
1007 for (self.objects.items) |*object, object_id| {1131 for (self.objects.items) |*object, object_id| {
1008 try self.resolveSymbolsInObject(object, @intCast(u16, object_id));1132 try self.resolveSymbolsInObject(object, @intCast(u16, object_id));
1009 }1133 }
...@@ -1013,6 +1137,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1013,6 +1137,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1013 try self.createDyldPrivateAtom();1137 try self.createDyldPrivateAtom();
1014 try self.createStubHelperPreambleAtom();1138 try self.createStubHelperPreambleAtom();
1015 try self.resolveSymbolsInDylibs();1139 try self.resolveSymbolsInDylibs();
1140 try self.createMhExecuteHeaderSymbol();
1016 try self.createDsoHandleSymbol();1141 try self.createDsoHandleSymbol();
1017 try self.addCodeSignatureLC();1142 try self.addCodeSignatureLC();
1018 try self.resolveSymbolsAtLoading();1143 try self.resolveSymbolsAtLoading();
...@@ -1029,20 +1154,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1029,20 +1154,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
10291154
1030 try self.createTentativeDefAtoms();1155 try self.createTentativeDefAtoms();
10311156
1032 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;1157 for (self.objects.items) |*object, object_id| {
1033 if (use_llvm or use_stage1) {1158 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id));
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
1044 }1159 }
10451160
1161 try self.gcAtoms();
1162 try self.pruneAndSortSections();
1163 try self.allocateSegments();
1164 try self.allocateSymbols();
1165
1046 try self.allocateSpecialSymbols();1166 try self.allocateSpecialSymbols();
10471167
1048 if (build_options.enable_logging) {1168 if (build_options.enable_logging) {
...@@ -1051,11 +1171,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1051,11 +1171,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1051 self.logAtoms();1171 self.logAtoms();
1052 }1172 }
10531173
1054 if (use_llvm or use_stage1) {1174 try self.writeAtomsOneShot();
1055 try self.writeAtomsWhole();
1056 } else {
1057 // try self.writeAtoms();
1058 }
10591175
1060 if (self.rustc_section_index) |id| {1176 if (self.rustc_section_index) |id| {
1061 const sect = self.getSectionPtr(.{1177 const sect = self.getSectionPtr(.{
...@@ -1066,14 +1182,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1066,14 +1182,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1066 }1182 }
10671183
1068 try self.setEntryPoint();1184 try self.setEntryPoint();
1069 try self.updateSectionOrdinals();
1070 try self.writeLinkeditSegment();1185 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
1077 if (self.code_signature) |*csig| {1187 if (self.code_signature) |*csig| {
1078 csig.clear(self.base.allocator);1188 csig.clear(self.base.allocator);
1079 csig.code_directory.ident = self.base.options.emit.?.sub_path;1189 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...@@ -1088,32 +1198,17 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1088 try self.writeLoadCommands();1198 try self.writeLoadCommands();
1089 try self.writeHeader();1199 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
1099 assert(!self.load_commands_dirty);1201 assert(!self.load_commands_dirty);
11001202
1101 if (self.code_signature) |*csig| {1203 if (self.code_signature) |*csig| {
1102 try self.writeCodeSignature(csig); // code signing always comes last1204 try self.writeCodeSignature(csig); // code signing always comes last
1103 }1205 }
1104
1105 // if (build_options.enable_link_snapshots) {
1106 // if (self.base.options.enable_link_snapshots)
1107 // try self.snapshotState();
1108 // }
1109 }1206 }
11101207
1111 cache: {1208 if (!self.base.options.disable_lld_caching) {
1112 if ((use_stage1 and self.base.options.disable_lld_caching) or self.base.options.cache_mode == .whole)
1113 break :cache;
1114 // Update the file with the digest. If it fails we can continue; it only1209 // Update the file with the digest. If it fails we can continue; it only
1115 // means that the next invocation will have an unnecessary cache miss.1210 // 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| {
1117 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});1212 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
1118 };1213 };
1119 // Again failure here only means an unnecessary cache miss.1214 // 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...@@ -1126,6 +1221,49 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1126 }1221 }
1127}1222}
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
1129fn resolveSearchDir(1267fn resolveSearchDir(
1130 arena: Allocator,1268 arena: Allocator,
1131 dir: []const u8,1269 dir: []const u8,
...@@ -1168,6 +1306,16 @@ fn resolveSearchDir(...@@ -1168,6 +1306,16 @@ fn resolveSearchDir(
1168 return null;1306 return null;
1169}1307}
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
1171fn resolveLib(1319fn resolveLib(
1172 arena: Allocator,1320 arena: Allocator,
1173 search_dir: []const u8,1321 search_dir: []const u8,
...@@ -2128,6 +2276,7 @@ fn allocateSpecialSymbols(self: *MachO) !void {...@@ -2128,6 +2276,7 @@ fn allocateSpecialSymbols(self: *MachO) !void {
2128 "__mh_execute_header",2276 "__mh_execute_header",
2129 }) |name| {2277 }) |name| {
2130 const global = self.globals.get(name) orelse continue;2278 const global = self.globals.get(name) orelse continue;
2279 if (global.file != null) continue;
2131 const sym = self.getSymbolPtr(global);2280 const sym = self.getSymbolPtr(global);
2132 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;2281 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
2133 sym.n_sect = self.getSectionOrdinal(.{2282 sym.n_sect = self.getSectionOrdinal(.{
...@@ -2143,7 +2292,9 @@ fn allocateSpecialSymbols(self: *MachO) !void {...@@ -2143,7 +2292,9 @@ fn allocateSpecialSymbols(self: *MachO) !void {
2143 }2292 }
2144}2293}
21452294
2146fn writeAtomsWhole(self: *MachO) !void {2295fn writeAtomsOneShot(self: *MachO) !void {
2296 assert(self.mode == .one_shot);
2297
2147 var it = self.atoms.iterator();2298 var it = self.atoms.iterator();
2148 while (it.next()) |entry| {2299 while (it.next()) |entry| {
2149 const sect = self.getSection(entry.key_ptr.*);2300 const sect = self.getSection(entry.key_ptr.*);
...@@ -2227,7 +2378,9 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty...@@ -2227,7 +2378,9 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
2227 }2378 }
2228}2379}
22292380
2230fn writeAtoms(self: *MachO) !void {2381fn writeAtomsIncremental(self: *MachO) !void {
2382 assert(self.mode == .incremental);
2383
2231 var it = self.atoms.iterator();2384 var it = self.atoms.iterator();
2232 while (it.next()) |entry| {2385 while (it.next()) |entry| {
2233 const match = entry.key_ptr.*;2386 const match = entry.key_ptr.*;
...@@ -2240,7 +2393,7 @@ fn writeAtoms(self: *MachO) !void {...@@ -2240,7 +2393,7 @@ fn writeAtoms(self: *MachO) !void {
2240 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });2393 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
22412394
2242 while (true) {2395 while (true) {
2243 if (atom.dirty or self.invalidate_relocs) {2396 if (atom.dirty) {
2244 try self.writeAtom(atom, match);2397 try self.writeAtom(atom, match);
2245 atom.dirty = false;2398 atom.dirty = false;
2246 }2399 }
...@@ -2746,11 +2899,13 @@ fn createTentativeDefAtoms(self: *MachO) !void {...@@ -2746,11 +2899,13 @@ fn createTentativeDefAtoms(self: *MachO) !void {
27462899
2747fn createMhExecuteHeaderSymbol(self: *MachO) !void {2900fn createMhExecuteHeaderSymbol(self: *MachO) !void {
2748 if (self.base.options.output_mode != .Exe) return;2901 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
2751 const gpa = self.base.allocator;2907 const gpa = self.base.allocator;
2752 const name = try gpa.dupe(u8, "__mh_execute_header");2908 const n_strx = try self.strtab.insert(gpa, "__mh_execute_header");
2753 const n_strx = try self.strtab.insert(gpa, name);
2754 const sym_index = @intCast(u32, self.locals.items.len);2909 const sym_index = @intCast(u32, self.locals.items.len);
2755 try self.locals.append(gpa, .{2910 try self.locals.append(gpa, .{
2756 .n_strx = n_strx,2911 .n_strx = n_strx,
...@@ -2759,10 +2914,14 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {...@@ -2759,10 +2914,14 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {
2759 .n_desc = 0,2914 .n_desc = 0,
2760 .n_value = 0,2915 .n_value = 0,
2761 });2916 });
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.* = .{
2763 .sym_index = sym_index,2922 .sym_index = sym_index,
2764 .file = null,2923 .file = null,
2765 });2924 };
2766}2925}
27672926
2768fn createDsoHandleSymbol(self: *MachO) !void {2927fn createDsoHandleSymbol(self: *MachO) !void {
...@@ -2787,9 +2946,68 @@ fn createDsoHandleSymbol(self: *MachO) !void {...@@ -2787,9 +2946,68 @@ fn createDsoHandleSymbol(self: *MachO) !void {
2787 _ = self.unresolved.swapRemove(@intCast(u32, self.globals.getIndex("___dso_handle").?));2946 _ = self.unresolved.swapRemove(@intCast(u32, self.globals.getIndex("___dso_handle").?));
2788}2947}
27892948
2790fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {2949fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
2791 const gpa = self.base.allocator;2950 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 {
2793 log.debug("resolving symbols in '{s}'", .{object.name});3011 log.debug("resolving symbols in '{s}'", .{object.name});
27943012
2795 for (object.symtab.items) |sym, index| {3013 for (object.symtab.items) |sym, index| {
...@@ -2825,72 +3043,18 @@ fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {...@@ -2825,72 +3043,18 @@ fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
2825 continue;3043 continue;
2826 }3044 }
28273045
2828 const name = try gpa.dupe(u8, sym_name);3046 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id };
2829 const global_index = @intCast(u32, self.globals.values().len);3047 self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) {
2830 const gop = try self.globals.getOrPut(gpa, name);3048 error.MultipleSymbolDefinitions => {
2831 defer if (gop.found_existing) gpa.free(name);3049 const global = self.globals.get(sym_name).?;
28323050 log.err("symbol '{s}' defined multiple times", .{sym_name});
2833 if (!gop.found_existing) {3051 if (global.file) |file| {
2834 gop.value_ptr.* = .{3052 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
2835 .sym_index = sym_index,3053 }
2836 .file = object_id,3054 log.err(" next definition in '{s}'", .{self.objects.items[object_id].name});
2837 };3055 return error.MultipleSymbolDefinitions;
2838 if (sym.undf() and !sym.tentative()) {3056 },
2839 try self.unresolved.putNoClobber(gpa, global_index, {});3057 else => |e| return e,
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,
2894 };3058 };
2895 }3059 }
2896}3060}
...@@ -2950,7 +3114,18 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -2950,7 +3114,18 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
2950 sym.n_desc |= macho.N_WEAK_REF;3114 sym.n_desc |= macho.N_WEAK_REF;
2951 }3115 }
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
2955 continue :loop;3130 continue :loop;
2956 }3131 }
...@@ -3272,7 +3447,7 @@ fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match...@@ -3272,7 +3447,7 @@ fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match
3272 return self.allocateAtom(atom, new_atom_size, alignment, match);3447 return self.allocateAtom(atom, new_atom_size, alignment, match);
3273}3448}
32743449
3275fn allocateLocalSymbol(self: *MachO) !u32 {3450fn allocateSymbol(self: *MachO) !u32 {
3276 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);3451 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
32773452
3278 const index = blk: {3453 const index = blk: {
...@@ -3366,12 +3541,9 @@ pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void {...@@ -3366,12 +3541,9 @@ pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void {
3366 const decl = self.base.options.module.?.declPtr(decl_index);3541 const decl = self.base.options.module.?.declPtr(decl_index);
3367 if (decl.link.macho.sym_index != 0) return;3542 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();
3370 try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.sym_index, &decl.link.macho);3545 try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.sym_index, &decl.link.macho);
3371 try self.decls.putNoClobber(self.base.allocator, decl_index, null);3546 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);
3375}3547}
33763548
3377pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {3549pub 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...@@ -3468,7 +3640,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
3468 log.debug("allocating symbol indexes for {s}", .{name});3640 log.debug("allocating symbol indexes for {s}", .{name});
34693641
3470 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);3642 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();
3472 const atom = try MachO.createEmptyAtom(3644 const atom = try MachO.createEmptyAtom(
3473 gpa,3645 gpa,
3474 sym_index,3646 sym_index,
...@@ -3787,6 +3959,12 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac...@@ -3787,6 +3959,12 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
3787 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, symbol.n_value, vaddr });3959 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, symbol.n_value, vaddr });
3788 log.debug(" (required alignment 0x{x})", .{required_alignment});3960 log.debug(" (required alignment 0x{x})", .{required_alignment});
3789 symbol.n_value = vaddr;3961 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;
3790 } else if (code_len < decl.link.macho.size) {3968 } else if (code_len < decl.link.macho.size) {
3791 self.shrinkAtom(&decl.link.macho, code_len, match);3969 self.shrinkAtom(&decl.link.macho, code_len, match);
3792 }3970 }
...@@ -3814,11 +3992,8 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac...@@ -3814,11 +3992,8 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
3814 .n_value = addr,3992 .n_value = addr,
3815 };3993 };
38163994
3817 const got_target = SymbolWithLoc{3995 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };
3818 .sym_index = decl.link.macho.sym_index,3996 const got_index = try self.allocateGotEntry(got_target);
3819 .file = null,
3820 };
3821 const got_index = self.got_entries_table.get(got_target).?;
3822 const got_atom = try self.createGotAtom(got_target);3997 const got_atom = try self.createGotAtom(got_target);
3823 self.got_entries.items[got_index].atom = got_atom;3998 self.got_entries.items[got_index].atom = got_atom;
3824 }3999 }
...@@ -3843,19 +4018,23 @@ pub fn updateDeclExports(...@@ -3843,19 +4018,23 @@ pub fn updateDeclExports(
3843 @panic("Attempted to compile for object format that was disabled by build configuration");4018 @panic("Attempted to compile for object format that was disabled by build configuration");
3844 }4019 }
3845 if (build_options.have_llvm) {4020 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);
3847 }4023 }
3848 const tracy = trace(@src());4024 const tracy = trace(@src());
3849 defer tracy.end();4025 defer tracy.end();
38504026
3851 try self.globals.ensureUnusedCapacity(self.base.allocator, exports.len);4027 const gpa = self.base.allocator;
4028
3852 const decl = module.declPtr(decl_index);4029 const decl = module.declPtr(decl_index);
3853 if (decl.link.macho.sym_index == 0) return;4030 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
3856 for (exports) |exp| {4033 for (exports) |exp| {
3857 const exp_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{exp.options.name});4034 const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{exp.options.name});
3858 defer self.base.allocator.free(exp_name);4035 defer gpa.free(exp_name);
4036
4037 log.debug("adding new export '{s}'", .{exp_name});
38594038
3860 if (exp.options.section) |section_name| {4039 if (exp.options.section) |section_name| {
3861 if (!mem.eql(u8, section_name, "__text")) {4040 if (!mem.eql(u8, section_name, "__text")) {
...@@ -3863,7 +4042,7 @@ pub fn updateDeclExports(...@@ -3863,7 +4042,7 @@ pub fn updateDeclExports(
3863 module.gpa,4042 module.gpa,
3864 exp,4043 exp,
3865 try Module.ErrorMsg.create(4044 try Module.ErrorMsg.create(
3866 self.base.allocator,4045 gpa,
3867 decl.srcLoc(),4046 decl.srcLoc(),
3868 "Unimplemented: ExportOptions.section",4047 "Unimplemented: ExportOptions.section",
3869 .{},4048 .{},
...@@ -3878,7 +4057,7 @@ pub fn updateDeclExports(...@@ -3878,7 +4057,7 @@ pub fn updateDeclExports(
3878 module.gpa,4057 module.gpa,
3879 exp,4058 exp,
3880 try Module.ErrorMsg.create(4059 try Module.ErrorMsg.create(
3881 self.base.allocator,4060 gpa,
3882 decl.srcLoc(),4061 decl.srcLoc(),
3883 "Unimplemented: GlobalLinkage.LinkOnce",4062 "Unimplemented: GlobalLinkage.LinkOnce",
3884 .{},4063 .{},
...@@ -3887,107 +4066,84 @@ pub fn updateDeclExports(...@@ -3887,107 +4066,84 @@ pub fn updateDeclExports(
3887 continue;4066 continue;
3888 }4067 }
38894068
3890 const is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;4069 const sym_index = exp.link.macho.sym_index orelse blk: {
3891 _ = is_weak;4070 const sym_index = try self.allocateSymbol();
3892 const n_strx = try self.strtab.insert(self.base.allocator, exp_name);4071 exp.link.macho.sym_index = sym_index;
3893 // if (self.symbol_resolver.getPtr(n_strx)) |resolv| {4072 break :blk sym_index;
3894 // switch (resolv.where) {4073 };
3895 // .global => {4074 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
3896 // if (resolv.sym_index == decl.link.macho.sym_index) continue;4075 const sym = self.getSymbolPtr(sym_loc);
38974076 sym.* = .{
3898 // const sym = &self.globals.items[resolv.where_index];4077 .n_strx = try self.strtab.insert(gpa, exp_name),
38994078 .n_type = macho.N_SECT | macho.N_EXT,
3900 // if (sym.tentative()) {4079 .n_sect = self.getSectionOrdinal(.{
3901 // assert(self.tentatives.swapRemove(resolv.where_index));4080 .seg = self.text_segment_cmd_index.?,
3902 // } else if (!is_weak and !(sym.weakDef() or sym.pext())) {4081 .sect = self.text_section_index.?, // TODO what if we export a variable?
3903 // _ = try module.failed_exports.put(4082 }),
3904 // module.gpa,4083 .n_desc = 0,
3905 // exp,4084 .n_value = decl_sym.n_value,
3906 // try Module.ErrorMsg.create(4085 };
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;
39354086
3936 switch (exp.options.linkage) {4087 switch (exp.options.linkage) {
3937 .Internal => {4088 .Internal => {
3938 // Symbol should be hidden, or in MachO lingo, private extern.4089 // Symbol should be hidden, or in MachO lingo, private extern.
3939 // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF.4090 // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF.
3940 // TODO work out when to add N_WEAK_REF.4091 sym.n_type |= macho.N_PEXT;
3941 n_type |= macho.N_PEXT;4092 sym.n_desc |= macho.N_WEAK_DEF;
3942 n_desc |= macho.N_WEAK_DEF;
3943 },4093 },
3944 .Strong => {},4094 .Strong => {},
3945 .Weak => {4095 .Weak => {
3946 // Weak linkage is specified as part of n_desc field.4096 // Weak linkage is specified as part of n_desc field.
3947 // Symbol's n_type is like for a symbol with strong linkage.4097 // 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;
3949 },4099 },
3950 else => unreachable,4100 else => unreachable,
3951 }4101 }
39524102
3953 const global_sym_index: u32 = 0;4103 self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) {
3954 // const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: {4104 error.MultipleSymbolDefinitions => {
3955 // const i = if (self.globals_free_list.popOrNull()) |i| i else inner: {4105 const global = self.globals.get(exp_name).?;
3956 // _ = self.globals.addOneAssumeCapacity();4106 if (sym_loc.sym_index != global.sym_index and global.file != null) {
3957 // break :inner @intCast(u32, self.globals.items.len - 1);4107 _ = try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
3958 // };4108 gpa,
3959 // break :blk i;4109 decl.srcLoc(),
3960 // };4110 \\LinkError: symbol '{s}' defined multiple times
3961 const sym = &self.locals.items[global_sym_index];4111 \\ first definition in '{s}'
3962 sym.* = .{4112 ,
3963 .n_strx = try self.strtab.insert(self.base.allocator, exp_name),4113 .{ exp_name, self.objects.items[global.file.?].name },
3964 .n_type = n_type,4114 ));
3965 .n_sect = @intCast(u8, self.text_section_index.?) + 1,4115 }
3966 .n_desc = n_desc,4116 },
3967 .n_value = decl_sym.n_value,4117 else => |e| return e,
3968 };4118 };
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 // });
3977 }4119 }
3978}4120}
39794121
3980pub fn deleteExport(self: *MachO, exp: Export) void {4122pub fn deleteExport(self: *MachO, exp: Export) void {
3981 if (self.llvm_object) |_| return;4123 if (self.llvm_object) |_| return;
3982 const sym_index = exp.sym_index orelse return;4124 const sym_index = exp.sym_index orelse return;
3983 _ = sym_index;4125
3984 // self.globals_free_list.append(self.base.allocator, sym_index) catch {};4126 const gpa = self.base.allocator;
3985 // const global = &self.globals.items[sym_index];4127
3986 // log.warn("deleting export '{s}': {}", .{ self.getString(global.n_strx), global });4128 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
3987 // assert(self.symbol_resolver.remove(global.n_strx));4129 const sym = self.getSymbolPtr(sym_loc);
3988 // global.n_type = 0;4130 const sym_name = self.getSymbolName(sym_loc);
3989 // global.n_strx = 0;4131 log.debug("deleting export '{s}'", .{sym_name});
3990 // global.n_value = 0;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 }
3991}4147}
39924148
3993fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {4149fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
...@@ -4026,7 +4182,10 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {...@@ -4026,7 +4182,10 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
4026 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };4182 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };
4027 if (self.got_entries_table.get(got_target)) |got_index| {4183 if (self.got_entries_table.get(got_target)) |got_index| {
4028 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};4184 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 };
4030 _ = self.got_entries_table.swapRemove(got_target);4189 _ = self.got_entries_table.swapRemove(got_target);
40314190
4032 if (self.d_sym) |*d_sym| {4191 if (self.d_sym) |*d_sym| {
...@@ -4102,7 +4261,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4102,7 +4261,7 @@ fn populateMissingMetadata(self: *MachO) !void {
41024261
4103 if (self.text_segment_cmd_index == null) {4262 if (self.text_segment_cmd_index == null) {
4104 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);4263 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: {
4106 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);4265 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
4107 const program_code_size_hint = self.base.options.program_code_size_hint;4266 const program_code_size_hint = self.base.options.program_code_size_hint;
4108 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;4267 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
...@@ -4133,7 +4292,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4133,7 +4292,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4133 .aarch64 => 2,4292 .aarch64 => 2,
4134 else => unreachable, // unhandled architecture type4293 else => unreachable, // unhandled architecture type
4135 };4294 };
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;
4137 self.text_section_index = try self.initSection(4296 self.text_section_index = try self.initSection(
4138 self.text_segment_cmd_index.?,4297 self.text_segment_cmd_index.?,
4139 "__text",4298 "__text",
...@@ -4156,7 +4315,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4156,7 +4315,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4156 .aarch64 => 3 * @sizeOf(u32),4315 .aarch64 => 3 * @sizeOf(u32),
4157 else => unreachable, // unhandled architecture type4316 else => unreachable, // unhandled architecture type
4158 };4317 };
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;
4160 self.stubs_section_index = try self.initSection(4319 self.stubs_section_index = try self.initSection(
4161 self.text_segment_cmd_index.?,4320 self.text_segment_cmd_index.?,
4162 "__stubs",4321 "__stubs",
...@@ -4185,7 +4344,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4185,7 +4344,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4185 .aarch64 => 3 * @sizeOf(u32),4344 .aarch64 => 3 * @sizeOf(u32),
4186 else => unreachable,4345 else => unreachable,
4187 };4346 };
4188 const needed_size = if (self.needs_prealloc)4347 const needed_size = if (self.mode == .incremental)
4189 stub_size * self.base.options.symbol_count_hint + preamble_size4348 stub_size * self.base.options.symbol_count_hint + preamble_size
4190 else4349 else
4191 0;4350 0;
...@@ -4205,7 +4364,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4205,7 +4364,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4205 var vmaddr: u64 = 0;4364 var vmaddr: u64 = 0;
4206 var fileoff: u64 = 0;4365 var fileoff: u64 = 0;
4207 var needed_size: u64 = 0;4366 var needed_size: u64 = 0;
4208 if (self.needs_prealloc) {4367 if (self.mode == .incremental) {
4209 const base = self.getSegmentAllocBase(&.{self.text_segment_cmd_index.?});4368 const base = self.getSegmentAllocBase(&.{self.text_segment_cmd_index.?});
4210 vmaddr = base.vmaddr;4369 vmaddr = base.vmaddr;
4211 fileoff = base.fileoff;4370 fileoff = base.fileoff;
...@@ -4234,7 +4393,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4234,7 +4393,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4234 }4393 }
42354394
4236 if (self.got_section_index == null) {4395 if (self.got_section_index == null) {
4237 const needed_size = if (self.needs_prealloc)4396 const needed_size = if (self.mode == .incremental)
4238 @sizeOf(u64) * self.base.options.symbol_count_hint4397 @sizeOf(u64) * self.base.options.symbol_count_hint
4239 else4398 else
4240 0;4399 0;
...@@ -4255,7 +4414,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4255,7 +4414,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4255 var vmaddr: u64 = 0;4414 var vmaddr: u64 = 0;
4256 var fileoff: u64 = 0;4415 var fileoff: u64 = 0;
4257 var needed_size: u64 = 0;4416 var needed_size: u64 = 0;
4258 if (self.needs_prealloc) {4417 if (self.mode == .incremental) {
4259 const base = self.getSegmentAllocBase(&.{self.data_const_segment_cmd_index.?});4418 const base = self.getSegmentAllocBase(&.{self.data_const_segment_cmd_index.?});
4260 vmaddr = base.vmaddr;4419 vmaddr = base.vmaddr;
4261 fileoff = base.fileoff;4420 fileoff = base.fileoff;
...@@ -4284,7 +4443,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4284,7 +4443,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4284 }4443 }
42854444
4286 if (self.la_symbol_ptr_section_index == null) {4445 if (self.la_symbol_ptr_section_index == null) {
4287 const needed_size = if (self.needs_prealloc)4446 const needed_size = if (self.mode == .incremental)
4288 @sizeOf(u64) * self.base.options.symbol_count_hint4447 @sizeOf(u64) * self.base.options.symbol_count_hint
4289 else4448 else
4290 0;4449 0;
...@@ -4301,7 +4460,10 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4301,7 +4460,10 @@ fn populateMissingMetadata(self: *MachO) !void {
4301 }4460 }
43024461
4303 if (self.data_section_index == null) {4462 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;
4305 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4467 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4306 self.data_section_index = try self.initSection(4468 self.data_section_index = try self.initSection(
4307 self.data_segment_cmd_index.?,4469 self.data_segment_cmd_index.?,
...@@ -4313,7 +4475,10 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4313,7 +4475,10 @@ fn populateMissingMetadata(self: *MachO) !void {
4313 }4475 }
43144476
4315 if (self.tlv_section_index == null) {4477 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;
4317 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4482 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4318 self.tlv_section_index = try self.initSection(4483 self.tlv_section_index = try self.initSection(
4319 self.data_segment_cmd_index.?,4484 self.data_segment_cmd_index.?,
...@@ -4327,7 +4492,10 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4327,7 +4492,10 @@ fn populateMissingMetadata(self: *MachO) !void {
4327 }4492 }
43284493
4329 if (self.tlv_data_section_index == null) {4494 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;
4331 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4499 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4332 self.tlv_data_section_index = try self.initSection(4500 self.tlv_data_section_index = try self.initSection(
4333 self.data_segment_cmd_index.?,4501 self.data_segment_cmd_index.?,
...@@ -4341,7 +4509,10 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4341,7 +4509,10 @@ fn populateMissingMetadata(self: *MachO) !void {
4341 }4509 }
43424510
4343 if (self.tlv_bss_section_index == null) {4511 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;
4345 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4516 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4346 self.tlv_bss_section_index = try self.initSection(4517 self.tlv_bss_section_index = try self.initSection(
4347 self.data_segment_cmd_index.?,4518 self.data_segment_cmd_index.?,
...@@ -4355,7 +4526,10 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4355,7 +4526,10 @@ fn populateMissingMetadata(self: *MachO) !void {
4355 }4526 }
43564527
4357 if (self.bss_section_index == null) {4528 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;
4359 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4533 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4360 self.bss_section_index = try self.initSection(4534 self.bss_section_index = try self.initSection(
4361 self.data_segment_cmd_index.?,4535 self.data_segment_cmd_index.?,
...@@ -4372,7 +4546,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4372,7 +4546,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4372 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);4546 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4373 var vmaddr: u64 = 0;4547 var vmaddr: u64 = 0;
4374 var fileoff: u64 = 0;4548 var fileoff: u64 = 0;
4375 if (self.needs_prealloc) {4549 if (self.mode == .incremental) {
4376 const base = self.getSegmentAllocBase(&.{self.data_segment_cmd_index.?});4550 const base = self.getSegmentAllocBase(&.{self.data_segment_cmd_index.?});
4377 vmaddr = base.vmaddr;4551 vmaddr = base.vmaddr;
4378 fileoff = base.fileoff;4552 fileoff = base.fileoff;
...@@ -4740,14 +4914,14 @@ fn initSection(...@@ -4740,14 +4914,14 @@ fn initSection(
4740 var sect = macho.section_64{4914 var sect = macho.section_64{
4741 .sectname = makeStaticString(sectname),4915 .sectname = makeStaticString(sectname),
4742 .segname = seg.inner.segname,4916 .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,
4744 .@"align" = alignment,4918 .@"align" = alignment,
4745 .flags = opts.flags,4919 .flags = opts.flags,
4746 .reserved1 = opts.reserved1,4920 .reserved1 = opts.reserved1,
4747 .reserved2 = opts.reserved2,4921 .reserved2 = opts.reserved2,
4748 };4922 };
47494923
4750 if (self.needs_prealloc) {4924 if (self.mode == .incremental) {
4751 const alignment_pow_2 = try math.powi(u32, 2, alignment);4925 const alignment_pow_2 = try math.powi(u32, 2, alignment);
4752 const padding: ?u32 = if (segment_id == self.text_segment_cmd_index.?)4926 const padding: ?u32 = if (segment_id == self.text_segment_cmd_index.?)
4753 @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size)4927 @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...@@ -4967,7 +5141,7 @@ fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u3
49675141
4968fn allocateAtomCommon(self: *MachO, atom: *Atom, match: MatchingSection) !void {5142fn allocateAtomCommon(self: *MachO, atom: *Atom, match: MatchingSection) !void {
4969 const sym = atom.getSymbolPtr(self);5143 const sym = atom.getSymbolPtr(self);
4970 if (self.needs_prealloc) {5144 if (self.mode == .incremental) {
4971 const size = atom.size;5145 const size = atom.size;
4972 const alignment = try math.powi(u32, 2, atom.alignment);5146 const alignment = try math.powi(u32, 2, atom.alignment);
4973 const vaddr = try self.allocateAtom(atom, size, alignment, match);5147 const vaddr = try self.allocateAtom(atom, size, alignment, match);
...@@ -5108,27 +5282,29 @@ pub fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void...@@ -5108,27 +5282,29 @@ pub fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void
5108pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {5282pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
5109 const gpa = self.base.allocator;5283 const gpa = self.base.allocator;
5110 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});5284 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| {5289 if (gop.found_existing) {
5114 return @intCast(u32, global_index);5290 return @intCast(u32, self.globals.getIndex(sym_name).?);
5115 }5291 }
51165292
5117 const n_strx = try self.strtab.insert(gpa, sym_name);
5118 const sym_index = @intCast(u32, self.locals.items.len);5293 const sym_index = @intCast(u32, self.locals.items.len);
5119 try self.locals.append(gpa, .{5294 try self.locals.append(gpa, .{
5120 .n_strx = n_strx,5295 .n_strx = try self.strtab.insert(gpa, sym_name),
5121 .n_type = macho.N_UNDF,5296 .n_type = macho.N_UNDF,
5122 .n_sect = 0,5297 .n_sect = 0,
5123 .n_desc = 0,5298 .n_desc = 0,
5124 .n_value = 0,5299 .n_value = 0,
5125 });5300 });
5126 try self.globals.putNoClobber(gpa, sym_name, .{5301 gop.value_ptr.* = .{
5127 .sym_index = sym_index,5302 .sym_index = sym_index,
5128 .file = null,5303 .file = null,
5129 });5304 };
5130 const global_index = self.globals.getIndex(sym_name).?;5305 try self.unresolved.putNoClobber(gpa, global_index, true);
5131 return @intCast(u32, global_index);5306
5307 return global_index;
5132}5308}
51335309
5134fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {5310fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {
...@@ -5927,7 +6103,7 @@ fn writeDices(self: *MachO) !void {...@@ -5927,7 +6103,7 @@ fn writeDices(self: *MachO) !void {
5927 self.load_commands_dirty = true;6103 self.load_commands_dirty = true;
5928}6104}
59296105
5930fn writeSymbolTable(self: *MachO) !void {6106fn writeSymtab(self: *MachO) !void {
5931 const tracy = trace(@src());6107 const tracy = trace(@src());
5932 defer tracy.end();6108 defer tracy.end();
59336109
...@@ -6143,7 +6319,7 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -6143,7 +6319,7 @@ fn writeSymbolTable(self: *MachO) !void {
6143 self.load_commands_dirty = true;6319 self.load_commands_dirty = true;
6144}6320}
61456321
6146fn writeStringTable(self: *MachO) !void {6322fn writeStrtab(self: *MachO) !void {
6147 const tracy = trace(@src());6323 const tracy = trace(@src());
6148 defer tracy.end();6324 defer tracy.end();
61496325
...@@ -6173,8 +6349,8 @@ fn writeLinkeditSegment(self: *MachO) !void {...@@ -6173,8 +6349,8 @@ fn writeLinkeditSegment(self: *MachO) !void {
6173 try self.writeDyldInfoData();6349 try self.writeDyldInfoData();
6174 try self.writeFunctionStarts();6350 try self.writeFunctionStarts();
6175 try self.writeDices();6351 try self.writeDices();
6176 try self.writeSymbolTable();6352 try self.writeSymtab();
6177 try self.writeStringTable();6353 try self.writeStrtab();
61786354
6179 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);6355 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
6180}6356}
...@@ -6391,6 +6567,27 @@ pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {...@@ -6391,6 +6567,27 @@ pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6391 }6567 }
6392}6568}
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
6394pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate: anytype) usize {6591pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate: anytype) usize {
6395 if (!@hasDecl(@TypeOf(predicate), "predicate"))6592 if (!@hasDecl(@TypeOf(predicate), "predicate"))
6396 @compileError("Predicate is required to define fn predicate(@This(), T) bool");6593 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
...@@ -6481,6 +6678,8 @@ fn snapshotState(self: *MachO) !void {...@@ -6481,6 +6678,8 @@ fn snapshotState(self: *MachO) !void {
6481 .payload = .{ .name = sect_name },6678 .payload = .{ .name = sect_name },
6482 });6679 });
64836680
6681 const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
6682
6484 var atom: *Atom = self.atoms.get(key) orelse {6683 var atom: *Atom = self.atoms.get(key) orelse {
6485 try nodes.append(.{6684 try nodes.append(.{
6486 .address = sect.addr + sect.size,6685 .address = sect.addr + sect.size,
...@@ -6495,35 +6694,23 @@ fn snapshotState(self: *MachO) !void {...@@ -6495,35 +6694,23 @@ fn snapshotState(self: *MachO) !void {
6495 }6694 }
64966695
6497 while (true) {6696 while (true) {
6498 const atom_sym = self.locals.items[atom.sym_index];6697 const atom_sym = atom.getSymbol(self);
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
6514 var node = Snapshot.Node{6698 var node = Snapshot.Node{
6515 .address = atom_sym.n_value,6699 .address = atom_sym.n_value,
6516 .tag = .atom_start,6700 .tag = .atom_start,
6517 .payload = .{6701 .payload = .{
6518 .name = self.getString(atom_sym.n_strx),6702 .name = atom.getName(self),
6519 .is_global = self.symbol_resolver.contains(atom_sym.n_strx),6703 .is_global = self.globals.contains(atom.getName(self)),
6520 },6704 },
6521 };6705 };
65226706
6523 var aliases = std.ArrayList([]const u8).init(arena);6707 var aliases = std.ArrayList([]const u8).init(arena);
6524 for (atom.contained.items) |sym_off| {6708 for (atom.contained.items) |sym_off| {
6525 if (sym_off.offset == 0) {6709 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 }));
6527 }6714 }
6528 }6715 }
6529 node.payload.aliases = aliases.toOwnedSlice();6716 node.payload.aliases = aliases.toOwnedSlice();
...@@ -6531,69 +6718,39 @@ fn snapshotState(self: *MachO) !void {...@@ -6531,69 +6718,39 @@ fn snapshotState(self: *MachO) !void {
65316718
6532 var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);6719 var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);
6533 for (atom.relocs.items) |rel| {6720 for (atom.relocs.items) |rel| {
6534 const arch = self.base.options.target.cpu.arch;
6535 const source_addr = blk: {6721 const source_addr = blk: {
6536 const sym = self.locals.items[atom.sym_index];6722 const source_sym = atom.getSymbol(self);
6537 break :blk sym.n_value + rel.offset;6723 break :blk source_sym.n_value + rel.offset;
6538 };6724 };
6539 const target_addr = blk: {6725 const target_addr = blk: {
6540 const is_via_got = got: {6726 const target_atom = (try rel.getTargetAtom(self)) orelse {
6541 switch (arch) {6727 // If there is no atom for target, we still need to check for special, atom-less
6542 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {6728 // symbols such as `___dso_handle`.
6543 .ARM64_RELOC_GOT_LOAD_PAGE21, .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => true,6729 const target_name = self.getSymbolName(rel.target);
6544 else => false,6730 if (self.globals.contains(target_name)) {
6545 },6731 const atomless_sym = self.getSymbol(rel.target);
6546 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {6732 break :blk atomless_sym.n_value;
6547 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
6548 else => false,
6549 },
6550 else => unreachable,
6551 }6733 }
6734 break :blk 0;
6552 };6735 };
65536736 const target_sym = if (target_atom.isSymbolContained(rel.target, self))
6554 if (is_via_got) {6737 self.getSymbol(rel.target)
6555 const got_index = self.got_entries_table.get(rel.target) orelse break :blk 0;6738 else
6556 const got_atom = self.got_entries.items[got_index].atom;6739 target_atom.getSymbol(self);
6557 break :blk self.locals.items[got_atom.sym_index].n_value;6740 const base_address: u64 = if (is_tlv) base_address: {
6558 }6741 const sect_id: u16 = sect_id: {
65596742 if (self.tlv_data_section_index) |i| {
6560 switch (rel.target) {6743 break :sect_id i;
6561 .local => |sym_index| {6744 } else if (self.tlv_bss_section_index) |i| {
6562 const sym = self.locals.items[sym_index];6745 break :sect_id i;
6563 const is_tlv = is_tlv: {6746 } else unreachable;
6564 const source_sym = self.locals.items[atom.sym_index];6747 };
6565 const match = self.section_ordinals.keys()[source_sym.n_sect - 1];6748 break :base_address self.getSection(.{
6566 const match_seg = self.load_commands.items[match.seg].segment;6749 .seg = self.data_segment_cmd_index.?,
6567 const match_sect = match_seg.sections.items[match.sect];6750 .sect = sect_id,
6568 break :is_tlv match_sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;6751 }).addr;
6569 };6752 } else 0;
6570 if (is_tlv) {6753 break :blk target_sym.n_value - base_address;
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 }
6597 };6754 };
65986755
6599 relocs.appendAssumeCapacity(.{6756 relocs.appendAssumeCapacity(.{
...@@ -6614,15 +6771,18 @@ fn snapshotState(self: *MachO) !void {...@@ -6614,15 +6771,18 @@ fn snapshotState(self: *MachO) !void {
6614 var next_i: usize = 0;6771 var next_i: usize = 0;
6615 var last_rel: usize = 0;6772 var last_rel: usize = 0;
6616 while (next_i < atom.contained.items.len) : (next_i += 1) {6773 while (next_i < atom.contained.items.len) : (next_i += 1) {
6617 const loc = atom.contained.items[next_i];6774 const loc = SymbolWithLoc{
6618 const cont_sym = self.locals.items[loc.sym_index];6775 .sym_index = atom.contained.items[next_i].sym_index,
6619 const cont_sym_name = self.getString(cont_sym.n_strx);6776 .file = atom.file,
6777 };
6778 const cont_sym = self.getSymbol(loc);
6779 const cont_sym_name = self.getSymbolName(loc);
6620 var contained_node = Snapshot.Node{6780 var contained_node = Snapshot.Node{
6621 .address = cont_sym.n_value,6781 .address = cont_sym.n_value,
6622 .tag = .atom_start,6782 .tag = .atom_start,
6623 .payload = .{6783 .payload = .{
6624 .name = cont_sym_name,6784 .name = cont_sym_name,
6625 .is_global = self.symbol_resolver.contains(cont_sym.n_strx),6785 .is_global = self.globals.contains(cont_sym_name),
6626 },6786 },
6627 };6787 };
66286788
...@@ -6630,10 +6790,14 @@ fn snapshotState(self: *MachO) !void {...@@ -6630,10 +6790,14 @@ fn snapshotState(self: *MachO) !void {
6630 var inner_aliases = std.ArrayList([]const u8).init(arena);6790 var inner_aliases = std.ArrayList([]const u8).init(arena);
6631 while (true) {6791 while (true) {
6632 if (next_i + 1 >= atom.contained.items.len) break;6792 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);
6634 if (next_sym.n_value != cont_sym.n_value) break;6798 if (next_sym.n_value != cont_sym.n_value) break;
6635 const next_sym_name = self.getString(next_sym.n_strx);6799 const next_sym_name = self.getSymbolName(next_sym_loc);
6636 if (self.symbol_resolver.contains(next_sym.n_strx)) {6800 if (self.globals.contains(next_sym_name)) {
6637 try inner_aliases.append(contained_node.payload.name);6801 try inner_aliases.append(contained_node.payload.name);
6638 contained_node.payload.name = next_sym_name;6802 contained_node.payload.name = next_sym_name;
6639 contained_node.payload.is_global = true;6803 contained_node.payload.is_global = true;
...@@ -6642,7 +6806,10 @@ fn snapshotState(self: *MachO) !void {...@@ -6642,7 +6806,10 @@ fn snapshotState(self: *MachO) !void {
6642 }6806 }
66436807
6644 const cont_size = if (next_i + 1 < atom.contained.items.len)6808 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_value6809 self.getSymbol(.{
6810 .sym_index = atom.contained.items[next_i + 1].sym_index,
6811 .file = atom.file,
6812 }).n_value - cont_sym.n_value
6646 else6813 else
6647 atom_sym.n_value + atom.size - cont_sym.n_value;6814 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 {...@@ -6695,7 +6862,11 @@ pub fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
6695 buf[0] = 's';6862 buf[0] = 's';
6696 }6863 }
6697 if (sym.ext()) {6864 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 }
6699 }6870 }
6700 if (sym.tentative()) {6871 if (sym.tentative()) {
6701 buf[2] = 't';6872 buf[2] = 't';
src/link/MachO/Atom.zig+9-9
...@@ -187,7 +187,7 @@ pub const Relocation = struct {...@@ -187,7 +187,7 @@ pub const Relocation = struct {
187187
188 const target_sym = macho_file.getSymbol(self.target);188 const target_sym = macho_file.getSymbol(self.target);
189 if (is_via_got) {189 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 {
191 log.err("expected GOT entry for symbol", .{});191 log.err("expected GOT entry for symbol", .{});
192 if (target_sym.undf()) {192 if (target_sym.undf()) {
193 log.err(" import('{s}')", .{macho_file.getSymbolName(self.target)});193 log.err(" import('{s}')", .{macho_file.getSymbolName(self.target)});
...@@ -197,14 +197,12 @@ pub const Relocation = struct {...@@ -197,14 +197,12 @@ pub const Relocation = struct {
197 log.err(" this is an internal linker error", .{});197 log.err(" this is an internal linker error", .{});
198 return error.FailedToResolveRelocationTarget;198 return error.FailedToResolveRelocationTarget;
199 };199 };
200 return macho_file.got_entries.items[got_index].atom;200 return got_atom;
201 }201 }
202202
203 if (macho_file.stubs_table.get(self.target)) |stub_index| {203 if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom;
204 return macho_file.stubs.items[stub_index].atom;204 if (macho_file.getTlvPtrAtomForSymbol(self.target)) |tlv_ptr_atom| return tlv_ptr_atom;
205 } else if (macho_file.tlv_ptr_entries_table.get(self.target)) |tlv_ptr_index| {205 return macho_file.getAtomForSymbol(self.target);
206 return macho_file.tlv_ptr_entries.items[tlv_ptr_index].atom;
207 } else return macho_file.getAtomForSymbol(self.target);
208 }206 }
209};207};
210208
...@@ -402,7 +400,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -402,7 +400,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
402 .n_type = macho.N_SECT,400 .n_type = macho.N_SECT,
403 .n_sect = context.macho_file.getSectionOrdinal(match),401 .n_sect = context.macho_file.getSectionOrdinal(match),
404 .n_desc = 0,402 .n_desc = 0,
405 .n_value = 0,403 .n_value = sect.addr,
406 });404 });
407 try object.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);405 try object.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
408 break :blk sym_index;406 break :blk sym_index;
...@@ -499,8 +497,10 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -499,8 +497,10 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
499 // Note for the future self: when r_extern == 0, we should subtract correction from the497 // Note for the future self: when r_extern == 0, we should subtract correction from the
500 // addend.498 // addend.
501 const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr;499 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.
502 addend += @intCast(i64, context.base_addr + offset + 4) -502 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;
504 }504 }
505 },505 },
506 .X86_64_RELOC_TLV => {506 .X86_64_RELOC_TLV => {
src/link/MachO/DebugSymbols.zig+2-2
...@@ -5,7 +5,7 @@ const build_options = @import("build_options");...@@ -5,7 +5,7 @@ const build_options = @import("build_options");
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const fs = std.fs;6const fs = std.fs;
7const link = @import("../../link.zig");7const link = @import("../../link.zig");
8const log = std.log.scoped(.link);8const log = std.log.scoped(.dsym);
9const macho = std.macho;9const macho = std.macho;
10const makeStaticString = MachO.makeStaticString;10const makeStaticString = MachO.makeStaticString;
11const math = std.math;11const math = std.math;
...@@ -60,7 +60,7 @@ debug_aranges_section_dirty: bool = false,...@@ -60,7 +60,7 @@ debug_aranges_section_dirty: bool = false,
60debug_info_header_dirty: bool = false,60debug_info_header_dirty: bool = false,
61debug_line_header_dirty: bool = false,61debug_line_header_dirty: bool = false,
6262
63strtab: StringTable(.link) = .{},63strtab: StringTable(.strtab) = .{},
6464
65relocs: std.ArrayListUnmanaged(Reloc) = .{},65relocs: std.ArrayListUnmanaged(Reloc) = .{},
6666
src/link/MachO/Object.zig+20-10
...@@ -270,7 +270,7 @@ const SymbolAtIndex = struct {...@@ -270,7 +270,7 @@ const SymbolAtIndex = struct {
270270
271 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {271 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {
272 const sym = self.getSymbol(ctx);272 const sym = self.getSymbol(ctx);
273 if (sym.n_strx == 0) return "";273 assert(sym.n_strx < ctx.strtab.len);
274 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.ptr + sym.n_strx), 0);274 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.ptr + sym.n_strx), 0);
275 }275 }
276276
...@@ -359,15 +359,17 @@ fn filterDice(...@@ -359,15 +359,17 @@ fn filterDice(
359 return dices[start..end];359 return dices[start..end];
360}360}
361361
362/// Splits object into atoms assuming whole cache mode aka traditional linking mode.362/// Splits object into atoms assuming one-shot linking mode.
363pub fn splitIntoAtomsWhole(self: *Object, macho_file: *MachO, object_id: u32) !void {363pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {
364 assert(macho_file.mode == .one_shot);
365
364 const tracy = trace(@src());366 const tracy = trace(@src());
365 defer tracy.end();367 defer tracy.end();
366368
367 const gpa = macho_file.base.allocator;369 const gpa = macho_file.base.allocator;
368 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;370 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
372 // You would expect that the symbol table is at least pre-sorted based on symbol's type:374 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
373 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,375 // 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...@@ -416,11 +418,11 @@ pub fn splitIntoAtomsWhole(self: *Object, macho_file: *MachO, object_id: u32) !v
416 log.debug(" unhandled section", .{});418 log.debug(" unhandled section", .{});
417 continue;419 continue;
418 };420 };
419 const target_sect = macho_file.getSection(match);421
420 log.debug(" output sect({d}, '{s},{s}')", .{422 log.debug(" output sect({d}, '{s},{s}')", .{
421 macho_file.getSectionOrdinal(match),423 macho_file.getSectionOrdinal(match),
422 target_sect.segName(),424 macho_file.getSection(match).segName(),
423 target_sect.sectName(),425 macho_file.getSection(match).sectName(),
424 });426 });
425427
426 const is_zerofill = blk: {428 const is_zerofill = blk: {
...@@ -585,10 +587,19 @@ fn createAtomFromSubsection(...@@ -585,10 +587,19 @@ fn createAtomFromSubsection(
585 sect: macho.section_64,587 sect: macho.section_64,
586) !*Atom {588) !*Atom {
587 const gpa = macho_file.base.allocator;589 const gpa = macho_file.base.allocator;
588 const sym = &self.symtab.items[sym_index];590 const sym = self.symtab.items[sym_index];
589 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);591 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
590 atom.file = object_id;592 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
593 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);604 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
594 try self.managed_atoms.append(gpa, atom);605 try self.managed_atoms.append(gpa, atom);
...@@ -669,7 +680,6 @@ fn createAtomFromSubsection(...@@ -669,7 +680,6 @@ fn createAtomFromSubsection(
669 // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;680 // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
670 break :blk .static;681 break :blk .static;
671 } else null;682 } else null;
672
673 atom.contained.appendAssumeCapacity(.{683 atom.contained.appendAssumeCapacity(.{
674 .sym_index = inner_sym_index.index,684 .sym_index = inner_sym_index.index,
675 .offset = inner_sym.n_value - sym.n_value,685 .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 {...@@ -16,7 +16,6 @@ pub fn build(b: *Builder) void {
16 // TODO when we figure out how to ship framework stubs for cross-compilation,16 // TODO when we figure out how to ship framework stubs for cross-compilation,
17 // populate paths to the sysroot here.17 // populate paths to the sysroot here.
18 exe.linkFramework("Foundation");18 exe.linkFramework("Foundation");
19 exe.link_gc_sections = true;
2019
21 const run_cmd = exe.run();20 const run_cmd = exe.run();
22 run_cmd.expectStdOutEqual("Hello from C++ and Zig");21 run_cmd.expectStdOutEqual("Hello from C++ and Zig");