authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-10 15:04:16+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-18 10:00:03+02:00
log05d0c42894b40c530819b1ac15f8133bfd34cf47
tree948253fe8758bf709a1339e2ca3eee202c92209d
parent26af8d254af5277e36cc78e1ab99241dee199c37

macho: move main driver loop for one-shot into standalone zld module


3 files changed, 889 insertions(+), 912 deletions(-)

src/link/MachO.zig+44-911
...@@ -22,6 +22,7 @@ const link = @import("../link.zig");...@@ -22,6 +22,7 @@ const link = @import("../link.zig");
22const llvm_backend = @import("../codegen/llvm.zig");22const llvm_backend = @import("../codegen/llvm.zig");
23const target_util = @import("../target.zig");23const target_util = @import("../target.zig");
24const trace = @import("../tracy.zig").trace;24const trace = @import("../tracy.zig").trace;
25const zld = @import("MachO/zld.zig");
2526
26const Air = @import("../Air.zig");27const Air = @import("../Air.zig");
27const Allocator = mem.Allocator;28const Allocator = mem.Allocator;
...@@ -57,11 +58,6 @@ pub const SearchStrategy = enum {...@@ -57,11 +58,6 @@ pub const SearchStrategy = enum {
5758
58pub const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));59pub const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));
5960
60const SystemLib = struct {
61 needed: bool = false,
62 weak: bool = false,
63};
64
65const Section = struct {61const Section = struct {
66 header: macho.section_64,62 header: macho.section_64,
67 segment_index: u8,63 segment_index: u8,
...@@ -412,7 +408,7 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -412,7 +408,7 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !v
412 }408 }
413409
414 switch (self.mode) {410 switch (self.mode) {
415 .one_shot => return self.linkOneShot(comp, prog_node),411 .one_shot => return zld.linkWithZld(self, comp, prog_node),
416 .incremental => return self.flushModule(comp, prog_node),412 .incremental => return self.flushModule(comp, prog_node),
417 }413 }
418}414}
...@@ -441,7 +437,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -441,7 +437,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
441 try d_sym.dwarf.flushModule(&self.base, module);437 try d_sym.dwarf.flushModule(&self.base, module);
442 }438 }
443439
444 var libs = std.StringArrayHashMap(SystemLib).init(arena);440 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
445 try self.resolveLibSystem(arena, comp, &.{}, &libs);441 try self.resolveLibSystem(arena, comp, &.{}, &libs);
446442
447 const id_symlink_basename = "zld.id";443 const id_symlink_basename = "zld.id";
...@@ -531,7 +527,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -531,7 +527,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
531 self.logAtoms();527 self.logAtoms();
532 }528 }
533529
534 try self.writeAtomsIncremental();530 try self.writeAtoms();
535531
536 var lc_buffer = std.ArrayList(u8).init(arena);532 var lc_buffer = std.ArrayList(u8).init(arena);
537 const lc_writer = lc_buffer.writer();533 const lc_writer = lc_buffer.writer();
...@@ -631,635 +627,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -631,635 +627,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
631 self.cold_start = false;627 self.cold_start = false;
632}628}
633629
634fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {630pub fn resolveLibSystem(
635 const tracy = trace(@src());
636 defer tracy.end();
637
638 const gpa = self.base.allocator;
639 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
640 defer arena_allocator.deinit();
641 const arena = arena_allocator.allocator();
642
643 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
644 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
645
646 // If there is no Zig code to compile, then we should skip flushing the output file because it
647 // will not be part of the linker line anyway.
648 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
649 if (self.base.options.use_stage1) {
650 const obj_basename = try std.zig.binNameAlloc(arena, .{
651 .root_name = self.base.options.root_name,
652 .target = self.base.options.target,
653 .output_mode = .Obj,
654 });
655 switch (self.base.options.cache_mode) {
656 .incremental => break :blk try module.zig_cache_artifact_directory.join(
657 arena,
658 &[_][]const u8{obj_basename},
659 ),
660 .whole => break :blk try fs.path.join(arena, &.{
661 fs.path.dirname(full_out_path).?, obj_basename,
662 }),
663 }
664 }
665
666 try self.flushModule(comp, prog_node);
667
668 if (fs.path.dirname(full_out_path)) |dirname| {
669 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
670 } else {
671 break :blk self.base.intermediary_basename.?;
672 }
673 } else null;
674
675 var sub_prog_node = prog_node.start("MachO Flush", 0);
676 sub_prog_node.activate();
677 sub_prog_node.context.refresh();
678 defer sub_prog_node.end();
679
680 const cpu_arch = self.base.options.target.cpu.arch;
681 const os_tag = self.base.options.target.os.tag;
682 const abi = self.base.options.target.abi;
683 const is_lib = self.base.options.output_mode == .Lib;
684 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
685 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
686 const stack_size = self.base.options.stack_size_override orelse 0;
687 const is_debug_build = self.base.options.optimize_mode == .Debug;
688 const gc_sections = self.base.options.gc_sections orelse !is_debug_build;
689
690 const id_symlink_basename = "zld.id";
691
692 var man: Cache.Manifest = undefined;
693 defer if (!self.base.options.disable_lld_caching) man.deinit();
694
695 var digest: [Cache.hex_digest_len]u8 = undefined;
696
697 if (!self.base.options.disable_lld_caching) {
698 man = comp.cache_parent.obtain();
699
700 // We are about to obtain this lock, so here we give other processes a chance first.
701 self.base.releaseLock();
702
703 comptime assert(Compilation.link_hash_implementation_version == 7);
704
705 for (self.base.options.objects) |obj| {
706 _ = try man.addFile(obj.path, null);
707 man.hash.add(obj.must_link);
708 }
709 for (comp.c_object_table.keys()) |key| {
710 _ = try man.addFile(key.status.success.object_path, null);
711 }
712 try man.addOptionalFile(module_obj_path);
713 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
714 // installation sources because they are always a product of the compiler version + target information.
715 man.hash.add(stack_size);
716 man.hash.addOptional(self.base.options.pagezero_size);
717 man.hash.addOptional(self.base.options.search_strategy);
718 man.hash.addOptional(self.base.options.headerpad_size);
719 man.hash.add(self.base.options.headerpad_max_install_names);
720 man.hash.add(gc_sections);
721 man.hash.add(self.base.options.dead_strip_dylibs);
722 man.hash.add(self.base.options.strip);
723 man.hash.addListOfBytes(self.base.options.lib_dirs);
724 man.hash.addListOfBytes(self.base.options.framework_dirs);
725 link.hashAddSystemLibs(&man.hash, self.base.options.frameworks);
726 man.hash.addListOfBytes(self.base.options.rpath_list);
727 if (is_dyn_lib) {
728 man.hash.addOptionalBytes(self.base.options.install_name);
729 man.hash.addOptional(self.base.options.version);
730 }
731 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
732 man.hash.addOptionalBytes(self.base.options.sysroot);
733 try man.addOptionalFile(self.base.options.entitlements);
734
735 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
736 _ = try man.hit();
737 digest = man.final();
738
739 var prev_digest_buf: [digest.len]u8 = undefined;
740 const prev_digest: []u8 = Cache.readSmallFile(
741 directory.handle,
742 id_symlink_basename,
743 &prev_digest_buf,
744 ) catch |err| blk: {
745 log.debug("MachO Zld new_digest={s} error: {s}", .{
746 std.fmt.fmtSliceHexLower(&digest),
747 @errorName(err),
748 });
749 // Handle this as a cache miss.
750 break :blk prev_digest_buf[0..0];
751 };
752 if (mem.eql(u8, prev_digest, &digest)) {
753 // Hot diggity dog! The output binary is already there.
754 log.debug("MachO Zld digest={s} match - skipping invocation", .{
755 std.fmt.fmtSliceHexLower(&digest),
756 });
757 self.base.lock = man.toOwnedLock();
758 return;
759 }
760 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
761 std.fmt.fmtSliceHexLower(prev_digest),
762 std.fmt.fmtSliceHexLower(&digest),
763 });
764
765 // We are about to change the output file to be different, so we invalidate the build hash now.
766 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
767 error.FileNotFound => {},
768 else => |e| return e,
769 };
770 }
771
772 if (self.base.options.output_mode == .Obj) {
773 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy
774 // here. TODO: think carefully about how we can avoid this redundant operation when doing
775 // build-obj. See also the corresponding TODO in linkAsArchive.
776 const the_object_path = blk: {
777 if (self.base.options.objects.len != 0) {
778 break :blk self.base.options.objects[0].path;
779 }
780
781 if (comp.c_object_table.count() != 0)
782 break :blk comp.c_object_table.keys()[0].status.success.object_path;
783
784 if (module_obj_path) |p|
785 break :blk p;
786
787 // TODO I think this is unreachable. Audit this situation when solving the above TODO
788 // regarding eliding redundant object -> object transformations.
789 return error.NoObjectsToLink;
790 };
791 // This can happen when using --enable-cache and using the stage1 backend. In this case
792 // we can skip the file copy.
793 if (!mem.eql(u8, the_object_path, full_out_path)) {
794 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
795 }
796 } else {
797 const sub_path = self.base.options.emit.?.sub_path;
798 if (self.base.file == null) {
799 self.base.file = try directory.handle.createFile(sub_path, .{
800 .truncate = true,
801 .read = true,
802 .mode = link.determineMode(self.base.options),
803 });
804 }
805 // Index 0 is always a null symbol.
806 try self.locals.append(gpa, .{
807 .n_strx = 0,
808 .n_type = 0,
809 .n_sect = 0,
810 .n_desc = 0,
811 .n_value = 0,
812 });
813 try self.strtab.buffer.append(gpa, 0);
814 try self.populateMissingMetadata();
815
816 var lib_not_found = false;
817 var framework_not_found = false;
818
819 // Positional arguments to the linker such as object files and static archives.
820 var positionals = std.ArrayList([]const u8).init(arena);
821 try positionals.ensureUnusedCapacity(self.base.options.objects.len);
822
823 var must_link_archives = std.StringArrayHashMap(void).init(arena);
824 try must_link_archives.ensureUnusedCapacity(self.base.options.objects.len);
825
826 for (self.base.options.objects) |obj| {
827 if (must_link_archives.contains(obj.path)) continue;
828 if (obj.must_link) {
829 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
830 } else {
831 _ = positionals.appendAssumeCapacity(obj.path);
832 }
833 }
834
835 for (comp.c_object_table.keys()) |key| {
836 try positionals.append(key.status.success.object_path);
837 }
838
839 if (module_obj_path) |p| {
840 try positionals.append(p);
841 }
842
843 if (comp.compiler_rt_lib) |lib| {
844 try positionals.append(lib.full_object_path);
845 }
846
847 // libc++ dep
848 if (self.base.options.link_libcpp) {
849 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
850 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
851 }
852
853 // Shared and static libraries passed via `-l` flag.
854 var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena);
855
856 const system_lib_names = self.base.options.system_libs.keys();
857 for (system_lib_names) |system_lib_name| {
858 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
859 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
860 // case we want to avoid prepending "-l".
861 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
862 try positionals.append(system_lib_name);
863 continue;
864 }
865
866 const system_lib_info = self.base.options.system_libs.get(system_lib_name).?;
867 try candidate_libs.put(system_lib_name, .{
868 .needed = system_lib_info.needed,
869 .weak = system_lib_info.weak,
870 });
871 }
872
873 var lib_dirs = std.ArrayList([]const u8).init(arena);
874 for (self.base.options.lib_dirs) |dir| {
875 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
876 try lib_dirs.append(search_dir);
877 } else {
878 log.warn("directory not found for '-L{s}'", .{dir});
879 }
880 }
881
882 var libs = std.StringArrayHashMap(SystemLib).init(arena);
883
884 // Assume ld64 default -search_paths_first if no strategy specified.
885 const search_strategy = self.base.options.search_strategy orelse .paths_first;
886 outer: for (candidate_libs.keys()) |lib_name| {
887 switch (search_strategy) {
888 .paths_first => {
889 // Look in each directory for a dylib (stub first), and then for archive
890 for (lib_dirs.items) |dir| {
891 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
892 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
893 try libs.put(full_path, candidate_libs.get(lib_name).?);
894 continue :outer;
895 }
896 }
897 } else {
898 log.warn("library not found for '-l{s}'", .{lib_name});
899 lib_not_found = true;
900 }
901 },
902 .dylibs_first => {
903 // First, look for a dylib in each search dir
904 for (lib_dirs.items) |dir| {
905 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
906 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
907 try libs.put(full_path, candidate_libs.get(lib_name).?);
908 continue :outer;
909 }
910 }
911 } else for (lib_dirs.items) |dir| {
912 if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| {
913 try libs.put(full_path, candidate_libs.get(lib_name).?);
914 } else {
915 log.warn("library not found for '-l{s}'", .{lib_name});
916 lib_not_found = true;
917 }
918 }
919 },
920 }
921 }
922
923 if (lib_not_found) {
924 log.warn("Library search paths:", .{});
925 for (lib_dirs.items) |dir| {
926 log.warn(" {s}", .{dir});
927 }
928 }
929
930 try self.resolveLibSystem(arena, comp, lib_dirs.items, &libs);
931
932 // frameworks
933 var framework_dirs = std.ArrayList([]const u8).init(arena);
934 for (self.base.options.framework_dirs) |dir| {
935 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
936 try framework_dirs.append(search_dir);
937 } else {
938 log.warn("directory not found for '-F{s}'", .{dir});
939 }
940 }
941
942 outer: for (self.base.options.frameworks.keys()) |f_name| {
943 for (framework_dirs.items) |dir| {
944 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
945 if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {
946 const info = self.base.options.frameworks.get(f_name).?;
947 try libs.put(full_path, .{
948 .needed = info.needed,
949 .weak = info.weak,
950 });
951 continue :outer;
952 }
953 }
954 } else {
955 log.warn("framework not found for '-framework {s}'", .{f_name});
956 framework_not_found = true;
957 }
958 }
959
960 if (framework_not_found) {
961 log.warn("Framework search paths:", .{});
962 for (framework_dirs.items) |dir| {
963 log.warn(" {s}", .{dir});
964 }
965 }
966
967 if (self.base.options.verbose_link) {
968 var argv = std.ArrayList([]const u8).init(arena);
969
970 try argv.append("zig");
971 try argv.append("ld");
972
973 if (is_exe_or_dyn_lib) {
974 try argv.append("-dynamic");
975 }
976
977 if (is_dyn_lib) {
978 try argv.append("-dylib");
979
980 if (self.base.options.install_name) |install_name| {
981 try argv.append("-install_name");
982 try argv.append(install_name);
983 }
984 }
985
986 if (self.base.options.sysroot) |syslibroot| {
987 try argv.append("-syslibroot");
988 try argv.append(syslibroot);
989 }
990
991 for (self.base.options.rpath_list) |rpath| {
992 try argv.append("-rpath");
993 try argv.append(rpath);
994 }
995
996 if (self.base.options.pagezero_size) |pagezero_size| {
997 try argv.append("-pagezero_size");
998 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
999 }
1000
1001 if (self.base.options.search_strategy) |strat| switch (strat) {
1002 .paths_first => try argv.append("-search_paths_first"),
1003 .dylibs_first => try argv.append("-search_dylibs_first"),
1004 };
1005
1006 if (self.base.options.headerpad_size) |headerpad_size| {
1007 try argv.append("-headerpad_size");
1008 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
1009 }
1010
1011 if (self.base.options.headerpad_max_install_names) {
1012 try argv.append("-headerpad_max_install_names");
1013 }
1014
1015 if (gc_sections) {
1016 try argv.append("-dead_strip");
1017 }
1018
1019 if (self.base.options.dead_strip_dylibs) {
1020 try argv.append("-dead_strip_dylibs");
1021 }
1022
1023 if (self.base.options.entry) |entry| {
1024 try argv.append("-e");
1025 try argv.append(entry);
1026 }
1027
1028 for (self.base.options.objects) |obj| {
1029 try argv.append(obj.path);
1030 }
1031
1032 for (comp.c_object_table.keys()) |key| {
1033 try argv.append(key.status.success.object_path);
1034 }
1035
1036 if (module_obj_path) |p| {
1037 try argv.append(p);
1038 }
1039
1040 if (comp.compiler_rt_lib) |lib| {
1041 try argv.append(lib.full_object_path);
1042 }
1043
1044 if (self.base.options.link_libcpp) {
1045 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1046 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1047 }
1048
1049 try argv.append("-o");
1050 try argv.append(full_out_path);
1051
1052 try argv.append("-lSystem");
1053 try argv.append("-lc");
1054
1055 for (self.base.options.system_libs.keys()) |l_name| {
1056 const info = self.base.options.system_libs.get(l_name).?;
1057 const arg = if (info.needed)
1058 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
1059 else if (info.weak)
1060 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
1061 else
1062 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
1063 try argv.append(arg);
1064 }
1065
1066 for (self.base.options.lib_dirs) |lib_dir| {
1067 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
1068 }
1069
1070 for (self.base.options.frameworks.keys()) |framework| {
1071 const info = self.base.options.frameworks.get(framework).?;
1072 const arg = if (info.needed)
1073 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
1074 else if (info.weak)
1075 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
1076 else
1077 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
1078 try argv.append(arg);
1079 }
1080
1081 for (self.base.options.framework_dirs) |framework_dir| {
1082 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
1083 }
1084
1085 if (is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false)) {
1086 try argv.append("-undefined");
1087 try argv.append("dynamic_lookup");
1088 }
1089
1090 for (must_link_archives.keys()) |lib| {
1091 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
1092 }
1093
1094 Compilation.dump_argv(argv.items);
1095 }
1096
1097 var dependent_libs = std.fifo.LinearFifo(struct {
1098 id: Dylib.Id,
1099 parent: u16,
1100 }, .Dynamic).init(arena);
1101
1102 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
1103 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
1104 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
1105 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
1106
1107 for (self.objects.items) |_, object_id| {
1108 try self.resolveSymbolsInObject(@intCast(u16, object_id));
1109 }
1110
1111 try self.resolveSymbolsInArchives();
1112 try self.resolveDyldStubBinder();
1113 try self.resolveSymbolsInDylibs();
1114 try self.createMhExecuteHeaderSymbol();
1115 try self.createDsoHandleSymbol();
1116 try self.resolveSymbolsAtLoading();
1117
1118 if (self.unresolved.count() > 0) {
1119 return error.UndefinedSymbolReference;
1120 }
1121 if (lib_not_found) {
1122 return error.LibraryNotFound;
1123 }
1124 if (framework_not_found) {
1125 return error.FrameworkNotFound;
1126 }
1127
1128 for (self.objects.items) |*object| {
1129 try object.scanInputSections(self);
1130 }
1131
1132 try self.createDyldPrivateAtom();
1133 try self.createTentativeDefAtoms();
1134 try self.createStubHelperPreambleAtom();
1135
1136 for (self.objects.items) |*object, object_id| {
1137 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id));
1138 }
1139
1140 if (gc_sections) {
1141 try dead_strip.gcAtoms(self);
1142 }
1143
1144 try self.allocateSegments();
1145 try self.allocateSymbols();
1146
1147 try self.allocateSpecialSymbols();
1148
1149 if (build_options.enable_logging or true) {
1150 self.logSymtab();
1151 self.logSections();
1152 self.logAtoms();
1153 }
1154
1155 try self.writeAtomsOneShot();
1156
1157 var lc_buffer = std.ArrayList(u8).init(arena);
1158 const lc_writer = lc_buffer.writer();
1159 var ncmds: u32 = 0;
1160
1161 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
1162
1163 // If the last section of __DATA segment is zerofill section, we need to ensure
1164 // that the free space between the end of the last non-zerofill section of __DATA
1165 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
1166 // copy-paste this space into memory for quicker zerofill operation.
1167 if (self.data_segment_cmd_index) |data_seg_id| blk: {
1168 var physical_zerofill_start: u64 = 0;
1169 const section_indexes = self.getSectionIndexes(data_seg_id);
1170 for (self.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
1171 if (header.isZerofill() and header.size > 0) break;
1172 physical_zerofill_start = header.offset + header.size;
1173 } else break :blk;
1174 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
1175 const physical_zerofill_size = math.cast(usize, linkedit.fileoff - physical_zerofill_start) orelse
1176 return error.Overflow;
1177 if (physical_zerofill_size > 0) {
1178 var padding = try self.base.allocator.alloc(u8, physical_zerofill_size);
1179 defer self.base.allocator.free(padding);
1180 mem.set(u8, padding, 0);
1181 try self.base.file.?.pwriteAll(padding, physical_zerofill_start);
1182 }
1183 }
1184
1185 try writeDylinkerLC(&ncmds, lc_writer);
1186 try self.writeMainLC(&ncmds, lc_writer);
1187 try self.writeDylibIdLC(&ncmds, lc_writer);
1188 try self.writeRpathLCs(&ncmds, lc_writer);
1189
1190 {
1191 try lc_writer.writeStruct(macho.source_version_command{
1192 .cmdsize = @sizeOf(macho.source_version_command),
1193 .version = 0x0,
1194 });
1195 ncmds += 1;
1196 }
1197
1198 try self.writeBuildVersionLC(&ncmds, lc_writer);
1199
1200 {
1201 var uuid_lc = macho.uuid_command{
1202 .cmdsize = @sizeOf(macho.uuid_command),
1203 .uuid = undefined,
1204 };
1205 std.crypto.random.bytes(&uuid_lc.uuid);
1206 try lc_writer.writeStruct(uuid_lc);
1207 ncmds += 1;
1208 }
1209
1210 try self.writeLoadDylibLCs(&ncmds, lc_writer);
1211
1212 const requires_codesig = blk: {
1213 if (self.base.options.entitlements) |_| break :blk true;
1214 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
1215 break :blk false;
1216 };
1217 var codesig_offset: ?u32 = null;
1218 var codesig: ?CodeSignature = if (requires_codesig) blk: {
1219 // Preallocate space for the code signature.
1220 // We need to do this at this stage so that we have the load commands with proper values
1221 // written out to the file.
1222 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
1223 // where the code signature goes into.
1224 var codesig = CodeSignature.init(self.page_size);
1225 codesig.code_directory.ident = self.base.options.emit.?.sub_path;
1226 if (self.base.options.entitlements) |path| {
1227 try codesig.addEntitlements(arena, path);
1228 }
1229 codesig_offset = try self.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
1230 break :blk codesig;
1231 } else null;
1232
1233 var headers_buf = std.ArrayList(u8).init(arena);
1234 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());
1235
1236 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
1237 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
1238
1239 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
1240
1241 if (codesig) |*csig| {
1242 try self.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
1243 }
1244 }
1245
1246 if (!self.base.options.disable_lld_caching) {
1247 // Update the file with the digest. If it fails we can continue; it only
1248 // means that the next invocation will have an unnecessary cache miss.
1249 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
1250 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
1251 };
1252 // Again failure here only means an unnecessary cache miss.
1253 man.writeManifest() catch |err| {
1254 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
1255 };
1256 // We hang on to this lock so that the output file path can be used without
1257 // other processes clobbering it.
1258 self.base.lock = man.toOwnedLock();
1259 }
1260}
1261
1262fn resolveLibSystem(
1263 self: *MachO,631 self: *MachO,
1264 arena: Allocator,632 arena: Allocator,
1265 comp: *Compilation,633 comp: *Compilation,
...@@ -1302,7 +670,7 @@ fn resolveLibSystem(...@@ -1302,7 +670,7 @@ fn resolveLibSystem(
1302 }670 }
1303}671}
1304672
1305fn resolveSearchDir(673pub fn resolveSearchDir(
1306 arena: Allocator,674 arena: Allocator,
1307 dir: []const u8,675 dir: []const u8,
1308 syslibroot: ?[]const u8,676 syslibroot: ?[]const u8,
...@@ -1344,17 +712,7 @@ fn resolveSearchDir(...@@ -1344,17 +712,7 @@ fn resolveSearchDir(
1344 return null;712 return null;
1345}713}
1346714
1347fn resolveSearchDirs(arena: Allocator, dirs: []const []const u8, syslibroot: ?[]const u8, out_dirs: anytype) !void {715pub fn resolveLib(
1348 for (dirs) |dir| {
1349 if (try resolveSearchDir(arena, dir, syslibroot)) |search_dir| {
1350 try out_dirs.append(search_dir);
1351 } else {
1352 log.warn("directory not found for '-L{s}'", .{dir});
1353 }
1354 }
1355}
1356
1357fn resolveLib(
1358 arena: Allocator,716 arena: Allocator,
1359 search_dir: []const u8,717 search_dir: []const u8,
1360 name: []const u8,718 name: []const u8,
...@@ -1373,7 +731,7 @@ fn resolveLib(...@@ -1373,7 +731,7 @@ fn resolveLib(
1373 return full_path;731 return full_path;
1374}732}
1375733
1376fn resolveFramework(734pub fn resolveFramework(
1377 arena: Allocator,735 arena: Allocator,
1378 search_dir: []const u8,736 search_dir: []const u8,
1379 name: []const u8,737 name: []const u8,
...@@ -1583,7 +941,7 @@ pub fn parseDylib(...@@ -1583,7 +941,7 @@ pub fn parseDylib(
1583 return true;941 return true;
1584}942}
1585943
1586fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void {944pub fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
1587 for (files) |file_name| {945 for (files) |file_name| {
1588 const full_path = full_path: {946 const full_path = full_path: {
1589 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;947 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
...@@ -1601,7 +959,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const...@@ -1601,7 +959,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
1601 }959 }
1602}960}
1603961
1604fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !void {962pub fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !void {
1605 for (files) |file_name| {963 for (files) |file_name| {
1606 const full_path = full_path: {964 const full_path = full_path: {
1607 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;965 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
...@@ -1614,10 +972,10 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi...@@ -1614,10 +972,10 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
1614 }972 }
1615}973}
1616974
1617fn parseLibs(975pub fn parseLibs(
1618 self: *MachO,976 self: *MachO,
1619 lib_names: []const []const u8,977 lib_names: []const []const u8,
1620 lib_infos: []const SystemLib,978 lib_infos: []const link.SystemLib,
1621 syslibroot: ?[]const u8,979 syslibroot: ?[]const u8,
1622 dependent_libs: anytype,980 dependent_libs: anytype,
1623) !void {981) !void {
...@@ -1635,7 +993,7 @@ fn parseLibs(...@@ -1635,7 +993,7 @@ fn parseLibs(
1635 }993 }
1636}994}
1637995
1638fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: anytype) !void {996pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
1639 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:997 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:
1640 // 1) anything on the linker line is parsed first998 // 1) anything on the linker line is parsed first
1641 // 2) afterwards, we parse dependents of the included dylibs999 // 2) afterwards, we parse dependents of the included dylibs
...@@ -1935,55 +1293,7 @@ pub fn writeAtom(self: *MachO, atom: *Atom, sect_id: u8) !void {...@@ -1935,55 +1293,7 @@ pub fn writeAtom(self: *MachO, atom: *Atom, sect_id: u8) !void {
1935// }1293// }
1936// }1294// }
19371295
1938fn allocateSymbols(self: *MachO) !void {1296pub fn allocateSpecialSymbols(self: *MachO) !void {
1939 const slice = self.sections.slice();
1940 for (slice.items(.last_atom)) |last_atom, sect_id| {
1941 const header = slice.items(.header)[sect_id];
1942 var atom = last_atom orelse continue;
1943
1944 while (atom.prev) |prev| {
1945 atom = prev;
1946 }
1947
1948 const n_sect = @intCast(u8, sect_id + 1);
1949 var base_vaddr = header.addr;
1950
1951 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1952 n_sect,
1953 header.segName(),
1954 header.sectName(),
1955 });
1956
1957 while (true) {
1958 const alignment = try math.powi(u32, 2, atom.alignment);
1959 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
1960
1961 const sym = atom.getSymbolPtr(self);
1962 sym.n_value = base_vaddr;
1963 sym.n_sect = n_sect;
1964
1965 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(self), base_vaddr });
1966
1967 // Update each symbol contained within the atom
1968 for (atom.contained.items) |sym_at_off| {
1969 const contained_sym = self.getSymbolPtr(.{
1970 .sym_index = sym_at_off.sym_index,
1971 .file = atom.file,
1972 });
1973 contained_sym.n_value = base_vaddr + sym_at_off.offset;
1974 contained_sym.n_sect = n_sect;
1975 }
1976
1977 base_vaddr += atom.size;
1978
1979 if (atom.next) |next| {
1980 atom = next;
1981 } else break;
1982 }
1983 }
1984}
1985
1986fn allocateSpecialSymbols(self: *MachO) !void {
1987 for (&[_][]const u8{1297 for (&[_][]const u8{
1988 "___dso_handle",1298 "___dso_handle",
1989 "__mh_execute_header",1299 "__mh_execute_header",
...@@ -2002,96 +1312,7 @@ fn allocateSpecialSymbols(self: *MachO) !void {...@@ -2002,96 +1312,7 @@ fn allocateSpecialSymbols(self: *MachO) !void {
2002 }1312 }
2003}1313}
20041314
2005fn writeAtomsOneShot(self: *MachO) !void {1315fn writeAtoms(self: *MachO) !void {
2006 assert(self.mode == .one_shot);
2007
2008 const gpa = self.base.allocator;
2009 const slice = self.sections.slice();
2010
2011 for (slice.items(.last_atom)) |last_atom, sect_id| {
2012 const header = slice.items(.header)[sect_id];
2013 if (header.size == 0) continue;
2014 var atom = last_atom.?;
2015
2016 if (header.isZerofill()) continue;
2017
2018 var buffer = std.ArrayList(u8).init(gpa);
2019 defer buffer.deinit();
2020 try buffer.ensureTotalCapacity(math.cast(usize, header.size) orelse return error.Overflow);
2021
2022 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
2023
2024 while (atom.prev) |prev| {
2025 atom = prev;
2026 }
2027
2028 while (true) {
2029 const this_sym = atom.getSymbol(self);
2030 const padding_size: usize = if (atom.next) |next| blk: {
2031 const next_sym = next.getSymbol(self);
2032 const size = next_sym.n_value - (this_sym.n_value + atom.size);
2033 break :blk math.cast(usize, size) orelse return error.Overflow;
2034 } else 0;
2035
2036 log.debug(" (adding ATOM(%{d}, '{s}') from object({?d}) to buffer)", .{
2037 atom.sym_index,
2038 atom.getName(self),
2039 atom.file,
2040 });
2041 if (padding_size > 0) {
2042 log.debug(" (with padding {x})", .{padding_size});
2043 }
2044
2045 try atom.resolveRelocs(self);
2046 buffer.appendSliceAssumeCapacity(atom.code.items);
2047
2048 var i: usize = 0;
2049 while (i < padding_size) : (i += 1) {
2050 // TODO with NOPs
2051 buffer.appendAssumeCapacity(0);
2052 }
2053
2054 if (atom.next) |next| {
2055 atom = next;
2056 } else {
2057 assert(buffer.items.len == header.size);
2058 log.debug(" (writing at file offset 0x{x})", .{header.offset});
2059 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2060 break;
2061 }
2062 }
2063 }
2064}
2065
2066fn writePadding(self: *MachO, sect_id: u8, size: usize, writer: anytype) !void {
2067 const header = self.sections.items(.header)[sect_id];
2068 const min_alignment: u3 = if (!header.isCode())
2069 1
2070 else switch (self.base.options.target.cpu.arch) {
2071 .aarch64 => @sizeOf(u32),
2072 .x86_64 => @as(u3, 1),
2073 else => unreachable,
2074 };
2075
2076 const len = @divExact(size, min_alignment);
2077 var i: usize = 0;
2078 while (i < len) : (i += 1) {
2079 if (!header.isCode()) {
2080 try writer.writeByte(0);
2081 } else switch (self.base.options.target.cpu.arch) {
2082 .aarch64 => {
2083 const inst = aarch64.Instruction.nop();
2084 try writer.writeIntLittle(u32, inst.toU32());
2085 },
2086 .x86_64 => {
2087 try writer.writeByte(0x90);
2088 },
2089 else => unreachable,
2090 }
2091 }
2092}
2093
2094fn writeAtomsIncremental(self: *MachO) !void {
2095 assert(self.mode == .incremental);1316 assert(self.mode == .incremental);
20961317
2097 const slice = self.sections.slice();1318 const slice = self.sections.slice();
...@@ -2186,7 +1407,7 @@ pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {...@@ -2186,7 +1407,7 @@ pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2186 return atom;1407 return atom;
2187}1408}
21881409
2189fn createDyldPrivateAtom(self: *MachO) !void {1410pub fn createDyldPrivateAtom(self: *MachO) !void {
2190 if (self.dyld_stub_binder_index == null) return;1411 if (self.dyld_stub_binder_index == null) return;
2191 if (self.dyld_private_atom != null) return;1412 if (self.dyld_private_atom != null) return;
21921413
...@@ -2203,7 +1424,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {...@@ -2203,7 +1424,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {
2203 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1424 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2204}1425}
22051426
2206fn createStubHelperPreambleAtom(self: *MachO) !void {1427pub fn createStubHelperPreambleAtom(self: *MachO) !void {
2207 if (self.dyld_stub_binder_index == null) return;1428 if (self.dyld_stub_binder_index == null) return;
2208 if (self.stub_helper_preamble_atom != null) return;1429 if (self.stub_helper_preamble_atom != null) return;
22091430
...@@ -2509,7 +1730,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2509,7 +1730,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2509 return atom;1730 return atom;
2510}1731}
25111732
2512fn createTentativeDefAtoms(self: *MachO) !void {1733pub fn createTentativeDefAtoms(self: *MachO) !void {
2513 const gpa = self.base.allocator;1734 const gpa = self.base.allocator;
25141735
2515 for (self.globals.items) |global| {1736 for (self.globals.items) |global| {
...@@ -2554,7 +1775,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {...@@ -2554,7 +1775,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {
2554 }1775 }
2555}1776}
25561777
2557fn createMhExecuteHeaderSymbol(self: *MachO) !void {1778pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
2558 if (self.base.options.output_mode != .Exe) return;1779 if (self.base.options.output_mode != .Exe) return;
2559 if (self.getGlobal("__mh_execute_header")) |global| {1780 if (self.getGlobal("__mh_execute_header")) |global| {
2560 const sym = self.getSymbol(global);1781 const sym = self.getSymbol(global);
...@@ -2577,7 +1798,7 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {...@@ -2577,7 +1798,7 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {
2577 gop.value_ptr.* = sym_loc;1798 gop.value_ptr.* = sym_loc;
2578}1799}
25791800
2580fn createDsoHandleSymbol(self: *MachO) !void {1801pub fn createDsoHandleSymbol(self: *MachO) !void {
2581 const global = self.getGlobalPtr("___dso_handle") orelse return;1802 const global = self.getGlobalPtr("___dso_handle") orelse return;
2582 if (!self.getSymbol(global.*).undf()) return;1803 if (!self.getSymbol(global.*).undf()) return;
25831804
...@@ -2652,7 +1873,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {...@@ -2652,7 +1873,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
2652 gop.value_ptr.* = current;1873 gop.value_ptr.* = current;
2653}1874}
26541875
2655fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {1876pub fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2656 const object = &self.objects.items[object_id];1877 const object = &self.objects.items[object_id];
2657 log.debug("resolving symbols in '{s}'", .{object.name});1878 log.debug("resolving symbols in '{s}'", .{object.name});
26581879
...@@ -2705,7 +1926,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {...@@ -2705,7 +1926,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2705 }1926 }
2706}1927}
27071928
2708fn resolveSymbolsInArchives(self: *MachO) !void {1929pub fn resolveSymbolsInArchives(self: *MachO) !void {
2709 if (self.archives.items.len == 0) return;1930 if (self.archives.items.len == 0) return;
27101931
2711 const gpa = self.base.allocator;1932 const gpa = self.base.allocator;
...@@ -2736,7 +1957,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void {...@@ -2736,7 +1957,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
2736 }1957 }
2737}1958}
27381959
2739fn resolveSymbolsInDylibs(self: *MachO) !void {1960pub fn resolveSymbolsInDylibs(self: *MachO) !void {
2740 if (self.dylibs.items.len == 0) return;1961 if (self.dylibs.items.len == 0) return;
27411962
2742 const gpa = self.base.allocator;1963 const gpa = self.base.allocator;
...@@ -2782,7 +2003,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -2782,7 +2003,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
2782 }2003 }
2783}2004}
27842005
2785fn resolveSymbolsAtLoading(self: *MachO) !void {2006pub fn resolveSymbolsAtLoading(self: *MachO) !void {
2786 const is_lib = self.base.options.output_mode == .Lib;2007 const is_lib = self.base.options.output_mode == .Lib;
2787 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;2008 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
2788 const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);2009 const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);
...@@ -2825,7 +2046,7 @@ fn resolveSymbolsAtLoading(self: *MachO) !void {...@@ -2825,7 +2046,7 @@ fn resolveSymbolsAtLoading(self: *MachO) !void {
2825 }2046 }
2826}2047}
28272048
2828fn resolveDyldStubBinder(self: *MachO) !void {2049pub fn resolveDyldStubBinder(self: *MachO) !void {
2829 if (self.dyld_stub_binder_index != null) return;2050 if (self.dyld_stub_binder_index != null) return;
2830 if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports2051 if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports
28312052
...@@ -2872,7 +2093,7 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -2872,7 +2093,7 @@ fn resolveDyldStubBinder(self: *MachO) !void {
2872 self.got_entries.items[got_index].sym_index = got_atom.sym_index;2093 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
2873}2094}
28742095
2875fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {2096pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
2876 const name_len = mem.sliceTo(default_dyld_path, 0).len;2097 const name_len = mem.sliceTo(default_dyld_path, 0).len;
2877 const cmdsize = @intCast(u32, mem.alignForwardGeneric(2098 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2878 u64,2099 u64,
...@@ -2892,7 +2113,7 @@ fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {...@@ -2892,7 +2113,7 @@ fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
2892 ncmds.* += 1;2113 ncmds.* += 1;
2893}2114}
28942115
2895fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {2116pub fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2896 if (self.base.options.output_mode != .Exe) return;2117 if (self.base.options.output_mode != .Exe) return;
2897 const seg = self.segments.items[self.text_segment_cmd_index.?];2118 const seg = self.segments.items[self.text_segment_cmd_index.?];
2898 const global = try self.getEntryPoint();2119 const global = try self.getEntryPoint();
...@@ -2914,7 +2135,7 @@ const WriteDylibLCCtx = struct {...@@ -2914,7 +2135,7 @@ const WriteDylibLCCtx = struct {
2914 compatibility_version: u32 = 0x10000,2135 compatibility_version: u32 = 0x10000,
2915};2136};
29162137
2917fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {2138pub fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
2918 const name_len = ctx.name.len + 1;2139 const name_len = ctx.name.len + 1;
2919 const cmdsize = @intCast(u32, mem.alignForwardGeneric(2140 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2920 u64,2141 u64,
...@@ -2940,7 +2161,7 @@ fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {...@@ -2940,7 +2161,7 @@ fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
2940 ncmds.* += 1;2161 ncmds.* += 1;
2941}2162}
29422163
2943fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {2164pub fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2944 if (self.base.options.output_mode != .Lib) return;2165 if (self.base.options.output_mode != .Lib) return;
2945 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;2166 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
2946 const curr = self.base.options.version orelse std.builtin.Version{2167 const curr = self.base.options.version orelse std.builtin.Version{
...@@ -2986,7 +2207,7 @@ const RpathIterator = struct {...@@ -2986,7 +2207,7 @@ const RpathIterator = struct {
2986 }2207 }
2987};2208};
29882209
2989fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {2210pub fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2990 const gpa = self.base.allocator;2211 const gpa = self.base.allocator;
29912212
2992 var it = RpathIterator.init(gpa, self.base.options.rpath_list);2213 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
...@@ -3013,7 +2234,7 @@ fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {...@@ -3013,7 +2234,7 @@ fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3013 }2234 }
3014}2235}
30152236
3016fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {2237pub fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3017 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);2238 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
3018 const platform_version = blk: {2239 const platform_version = blk: {
3019 const ver = self.base.options.target.os.version_range.semver.min;2240 const ver = self.base.options.target.os.version_range.semver.min;
...@@ -3046,7 +2267,7 @@ fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {...@@ -3046,7 +2267,7 @@ fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3046 ncmds.* += 1;2267 ncmds.* += 1;
3047}2268}
30482269
3049fn writeLoadDylibLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {2270pub fn writeLoadDylibLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3050 for (self.referenced_dylibs.keys()) |id| {2271 for (self.referenced_dylibs.keys()) |id| {
3051 const dylib = self.dylibs.items[id];2272 const dylib = self.dylibs.items[id];
3052 const dylib_id = dylib.id orelse unreachable;2273 const dylib_id = dylib.id orelse unreachable;
...@@ -4035,7 +3256,7 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil...@@ -4035,7 +3256,7 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
4035 return 0;3256 return 0;
4036}3257}
40373258
4038fn populateMissingMetadata(self: *MachO) !void {3259pub fn populateMissingMetadata(self: *MachO) !void {
4039 const gpa = self.base.allocator;3260 const gpa = self.base.allocator;
4040 const cpu_arch = self.base.options.target.cpu.arch;3261 const cpu_arch = self.base.options.target.cpu.arch;
4041 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;3262 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
...@@ -4367,7 +3588,7 @@ fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {...@@ -4367,7 +3588,7 @@ fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {
4367 return @intCast(u32, sizeofcmds);3588 return @intCast(u32, sizeofcmds);
4368}3589}
43693590
4370fn calcMinHeaderPad(self: *MachO) !u64 {3591pub fn calcMinHeaderPad(self: *MachO) !u64 {
4371 var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 0);3592 var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 0);
4372 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});3593 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
43733594
...@@ -4384,94 +3605,6 @@ fn calcMinHeaderPad(self: *MachO) !u64 {...@@ -4384,94 +3605,6 @@ fn calcMinHeaderPad(self: *MachO) !u64 {
4384 return offset;3605 return offset;
4385}3606}
43863607
4387fn allocateSegments(self: *MachO) !void {
4388 try self.allocateSegment(self.text_segment_cmd_index, &.{
4389 self.pagezero_segment_cmd_index,
4390 }, try self.calcMinHeaderPad());
4391
4392 if (self.text_segment_cmd_index) |index| blk: {
4393 const indexes = self.getSectionIndexes(index);
4394 if (indexes.start == indexes.end) break :blk;
4395 const seg = self.segments.items[index];
4396
4397 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
4398 var min_alignment: u32 = 0;
4399 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4400 const alignment = try math.powi(u32, 2, header.@"align");
4401 min_alignment = math.max(min_alignment, alignment);
4402 }
4403
4404 assert(min_alignment > 0);
4405 const last_header = self.sections.items(.header)[indexes.end - 1];
4406 const shift: u32 = shift: {
4407 const diff = seg.filesize - last_header.offset - last_header.size;
4408 const factor = @divTrunc(diff, min_alignment);
4409 break :shift @intCast(u32, factor * min_alignment);
4410 };
4411
4412 if (shift > 0) {
4413 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header| {
4414 header.offset += shift;
4415 header.addr += shift;
4416 }
4417 }
4418 }
4419
4420 try self.allocateSegment(self.data_const_segment_cmd_index, &.{
4421 self.text_segment_cmd_index,
4422 self.pagezero_segment_cmd_index,
4423 }, 0);
4424
4425 try self.allocateSegment(self.data_segment_cmd_index, &.{
4426 self.data_const_segment_cmd_index,
4427 self.text_segment_cmd_index,
4428 self.pagezero_segment_cmd_index,
4429 }, 0);
4430
4431 try self.allocateSegment(self.linkedit_segment_cmd_index, &.{
4432 self.data_segment_cmd_index,
4433 self.data_const_segment_cmd_index,
4434 self.text_segment_cmd_index,
4435 self.pagezero_segment_cmd_index,
4436 }, 0);
4437}
4438
4439fn allocateSegment(self: *MachO, maybe_index: ?u8, indices: []const ?u8, init_size: u64) !void {
4440 const index = maybe_index orelse return;
4441 const seg = &self.segments.items[index];
4442
4443 const base = self.getSegmentAllocBase(indices);
4444 seg.vmaddr = base.vmaddr;
4445 seg.fileoff = base.fileoff;
4446 seg.filesize = init_size;
4447 seg.vmsize = init_size;
4448
4449 // Allocate the sections according to their alignment at the beginning of the segment.
4450 const indexes = self.getSectionIndexes(index);
4451 var start = init_size;
4452 const slice = self.sections.slice();
4453 for (slice.items(.header)[indexes.start..indexes.end]) |*header| {
4454 const alignment = try math.powi(u32, 2, header.@"align");
4455 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
4456
4457 header.offset = if (header.isZerofill())
4458 0
4459 else
4460 @intCast(u32, seg.fileoff + start_aligned);
4461 header.addr = seg.vmaddr + start_aligned;
4462
4463 start = start_aligned + header.size;
4464
4465 if (!header.isZerofill()) {
4466 seg.filesize = start;
4467 }
4468 seg.vmsize = start;
4469 }
4470
4471 seg.filesize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
4472 seg.vmsize = mem.alignForwardGeneric(u64, seg.vmsize, self.page_size);
4473}
4474
4475const InitSectionOpts = struct {3608const InitSectionOpts = struct {
4476 flags: u32 = macho.S_REGULAR,3609 flags: u32 = macho.S_REGULAR,
4477 reserved1: u32 = 0,3610 reserved1: u32 = 0,
...@@ -4956,7 +4089,7 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {...@@ -4956,7 +4089,7 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
4956 return global_index;4089 return global_index;
4957}4090}
49584091
4959fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u64, fileoff: u64 } {4092pub fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u64, fileoff: u64 } {
4960 for (indices) |maybe_prev_id| {4093 for (indices) |maybe_prev_id| {
4961 const prev_id = maybe_prev_id orelse continue;4094 const prev_id = maybe_prev_id orelse continue;
4962 const prev = self.segments.items[prev_id];4095 const prev = self.segments.items[prev_id];
...@@ -4968,7 +4101,7 @@ fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u64,...@@ -4968,7 +4101,7 @@ fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u64,
4968 return .{ .vmaddr = 0, .fileoff = 0 };4101 return .{ .vmaddr = 0, .fileoff = 0 };
4969}4102}
49704103
4971fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {4104pub fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
4972 for (self.segments.items) |seg, i| {4105 for (self.segments.items) |seg, i| {
4973 const indexes = self.getSectionIndexes(@intCast(u8, i));4106 const indexes = self.getSectionIndexes(@intCast(u8, i));
4974 var out_seg = seg;4107 var out_seg = seg;
...@@ -4997,7 +4130,7 @@ fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {...@@ -4997,7 +4130,7 @@ fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
4997 }4130 }
4998}4131}
49994132
5000fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {4133pub fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5001 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];4134 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5002 seg.filesize = 0;4135 seg.filesize = 0;
5003 seg.vmsize = 0;4136 seg.vmsize = 0;
...@@ -5010,7 +4143,7 @@ fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void...@@ -5010,7 +4143,7 @@ fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void
5010 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);4143 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
5011}4144}
50124145
5013fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {4146pub fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5014 const tracy = trace(@src());4147 const tracy = trace(@src());
5015 defer tracy.end();4148 defer tracy.end();
50164149
...@@ -5696,7 +4829,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi...@@ -5696,7 +4829,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi
5696 lc.nindirectsyms = nindirectsyms;4829 lc.nindirectsyms = nindirectsyms;
5697}4830}
56984831
5699fn writeCodeSignaturePadding(4832pub fn writeCodeSignaturePadding(
5700 self: *MachO,4833 self: *MachO,
5701 code_sig: *CodeSignature,4834 code_sig: *CodeSignature,
5702 ncmds: *u32,4835 ncmds: *u32,
...@@ -5725,7 +4858,7 @@ fn writeCodeSignaturePadding(...@@ -5725,7 +4858,7 @@ fn writeCodeSignaturePadding(
5725 return @intCast(u32, offset);4858 return @intCast(u32, offset);
5726}4859}
57274860
5728fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {4861pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {
5729 const seg = self.segments.items[self.text_segment_cmd_index.?];4862 const seg = self.segments.items[self.text_segment_cmd_index.?];
57304863
5731 var buffer = std.ArrayList(u8).init(self.base.allocator);4864 var buffer = std.ArrayList(u8).init(self.base.allocator);
...@@ -5749,7 +4882,7 @@ fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void...@@ -5749,7 +4882,7 @@ fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void
5749}4882}
57504883
5751/// Writes Mach-O file header.4884/// Writes Mach-O file header.
5752fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {4885pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
5753 var header: macho.mach_header_64 = .{};4886 var header: macho.mach_header_64 = .{};
5754 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;4887 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
57554888
...@@ -6371,7 +5504,7 @@ fn generateSymbolStabsForSymbol(...@@ -6371,7 +5504,7 @@ fn generateSymbolStabsForSymbol(
6371// try writer.writeByte(']');5504// try writer.writeByte(']');
6372// }5505// }
63735506
6374fn logSections(self: *MachO) void {5507pub fn logSections(self: *MachO) void {
6375 log.debug("sections:", .{});5508 log.debug("sections:", .{});
6376 for (self.sections.items(.header)) |header, i| {5509 for (self.sections.items(.header)) |header, i| {
6377 log.debug(" sect({d}): {s},{s} @{x}, sizeof({x})", .{5510 log.debug(" sect({d}): {s},{s} @{x}, sizeof({x})", .{
...@@ -6409,7 +5542,7 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {...@@ -6409,7 +5542,7 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
6409 return buf[0..];5542 return buf[0..];
6410}5543}
64115544
6412fn logSymtab(self: *MachO) void {5545pub fn logSymtab(self: *MachO) void {
6413 var buf: [9]u8 = undefined;5546 var buf: [9]u8 = undefined;
64145547
6415 log.debug("symtab:", .{});5548 log.debug("symtab:", .{});
...@@ -6502,7 +5635,7 @@ fn logSymtab(self: *MachO) void {...@@ -6502,7 +5635,7 @@ fn logSymtab(self: *MachO) void {
6502 }5635 }
6503}5636}
65045637
6505fn logAtoms(self: *MachO) void {5638pub fn logAtoms(self: *MachO) void {
6506 log.debug("atoms:", .{});5639 log.debug("atoms:", .{});
65075640
6508 const slice = self.sections.slice();5641 const slice = self.sections.slice();
src/link/MachO/Object.zig+1-1
...@@ -236,7 +236,7 @@ pub fn scanInputSections(self: Object, macho_file: *MachO) !void {...@@ -236,7 +236,7 @@ pub fn scanInputSections(self: Object, macho_file: *MachO) !void {
236}236}
237237
238/// Splits object into atoms assuming one-shot linking mode.238/// Splits object into atoms assuming one-shot linking mode.
239pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {239pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) !void {
240 assert(macho_file.mode == .one_shot);240 assert(macho_file.mode == .one_shot);
241241
242 const tracy = trace(@src());242 const tracy = trace(@src());
src/link/MachO/zld.zig created+844
...@@ -0,0 +1,844 @@
1const std = @import("std");
2const build_options = @import("build_options");
3const assert = std.debug.assert;
4const fs = std.fs;
5const log = std.log.scoped(.link);
6const macho = std.macho;
7const math = std.math;
8const mem = std.mem;
9
10const link = @import("../../link.zig");
11const trace = @import("../../tracy.zig").trace;
12
13const Cache = @import("../../Cache.zig");
14const CodeSignature = @import("CodeSignature.zig");
15const Compilation = @import("../../Compilation.zig");
16const Dylib = @import("Dylib.zig");
17const MachO = @import("../MachO.zig");
18
19const dead_strip = @import("dead_strip.zig");
20
21pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
22 const tracy = trace(@src());
23 defer tracy.end();
24
25 const gpa = macho_file.base.allocator;
26 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
27 defer arena_allocator.deinit();
28 const arena = arena_allocator.allocator();
29
30 const directory = macho_file.base.options.emit.?.directory; // Just an alias to make it shorter to type.
31 const full_out_path = try directory.join(arena, &[_][]const u8{macho_file.base.options.emit.?.sub_path});
32
33 // If there is no Zig code to compile, then we should skip flushing the output file because it
34 // will not be part of the linker line anyway.
35 const module_obj_path: ?[]const u8 = if (macho_file.base.options.module) |module| blk: {
36 if (macho_file.base.options.use_stage1) {
37 const obj_basename = try std.zig.binNameAlloc(arena, .{
38 .root_name = macho_file.base.options.root_name,
39 .target = macho_file.base.options.target,
40 .output_mode = .Obj,
41 });
42 switch (macho_file.base.options.cache_mode) {
43 .incremental => break :blk try module.zig_cache_artifact_directory.join(
44 arena,
45 &[_][]const u8{obj_basename},
46 ),
47 .whole => break :blk try fs.path.join(arena, &.{
48 fs.path.dirname(full_out_path).?, obj_basename,
49 }),
50 }
51 }
52
53 try macho_file.flushModule(comp, prog_node);
54
55 if (fs.path.dirname(full_out_path)) |dirname| {
56 break :blk try fs.path.join(arena, &.{ dirname, macho_file.base.intermediary_basename.? });
57 } else {
58 break :blk macho_file.base.intermediary_basename.?;
59 }
60 } else null;
61
62 var sub_prog_node = prog_node.start("MachO Flush", 0);
63 sub_prog_node.activate();
64 sub_prog_node.context.refresh();
65 defer sub_prog_node.end();
66
67 const cpu_arch = macho_file.base.options.target.cpu.arch;
68 const os_tag = macho_file.base.options.target.os.tag;
69 const abi = macho_file.base.options.target.abi;
70 const is_lib = macho_file.base.options.output_mode == .Lib;
71 const is_dyn_lib = macho_file.base.options.link_mode == .Dynamic and is_lib;
72 const is_exe_or_dyn_lib = is_dyn_lib or macho_file.base.options.output_mode == .Exe;
73 const stack_size = macho_file.base.options.stack_size_override orelse 0;
74 const is_debug_build = macho_file.base.options.optimize_mode == .Debug;
75 const gc_sections = macho_file.base.options.gc_sections orelse !is_debug_build;
76
77 const id_symlink_basename = "zld.id";
78
79 var man: Cache.Manifest = undefined;
80 defer if (!macho_file.base.options.disable_lld_caching) man.deinit();
81
82 var digest: [Cache.hex_digest_len]u8 = undefined;
83
84 if (!macho_file.base.options.disable_lld_caching) {
85 man = comp.cache_parent.obtain();
86
87 // We are about to obtain this lock, so here we give other processes a chance first.
88 macho_file.base.releaseLock();
89
90 comptime assert(Compilation.link_hash_implementation_version == 7);
91
92 for (macho_file.base.options.objects) |obj| {
93 _ = try man.addFile(obj.path, null);
94 man.hash.add(obj.must_link);
95 }
96 for (comp.c_object_table.keys()) |key| {
97 _ = try man.addFile(key.status.success.object_path, null);
98 }
99 try man.addOptionalFile(module_obj_path);
100 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
101 // installation sources because they are always a product of the compiler version + target information.
102 man.hash.add(stack_size);
103 man.hash.addOptional(macho_file.base.options.pagezero_size);
104 man.hash.addOptional(macho_file.base.options.search_strategy);
105 man.hash.addOptional(macho_file.base.options.headerpad_size);
106 man.hash.add(macho_file.base.options.headerpad_max_install_names);
107 man.hash.add(gc_sections);
108 man.hash.add(macho_file.base.options.dead_strip_dylibs);
109 man.hash.add(macho_file.base.options.strip);
110 man.hash.addListOfBytes(macho_file.base.options.lib_dirs);
111 man.hash.addListOfBytes(macho_file.base.options.framework_dirs);
112 link.hashAddSystemLibs(&man.hash, macho_file.base.options.frameworks);
113 man.hash.addListOfBytes(macho_file.base.options.rpath_list);
114 if (is_dyn_lib) {
115 man.hash.addOptionalBytes(macho_file.base.options.install_name);
116 man.hash.addOptional(macho_file.base.options.version);
117 }
118 link.hashAddSystemLibs(&man.hash, macho_file.base.options.system_libs);
119 man.hash.addOptionalBytes(macho_file.base.options.sysroot);
120 try man.addOptionalFile(macho_file.base.options.entitlements);
121
122 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
123 _ = try man.hit();
124 digest = man.final();
125
126 var prev_digest_buf: [digest.len]u8 = undefined;
127 const prev_digest: []u8 = Cache.readSmallFile(
128 directory.handle,
129 id_symlink_basename,
130 &prev_digest_buf,
131 ) catch |err| blk: {
132 log.debug("MachO Zld new_digest={s} error: {s}", .{
133 std.fmt.fmtSliceHexLower(&digest),
134 @errorName(err),
135 });
136 // Handle this as a cache miss.
137 break :blk prev_digest_buf[0..0];
138 };
139 if (mem.eql(u8, prev_digest, &digest)) {
140 // Hot diggity dog! The output binary is already there.
141 log.debug("MachO Zld digest={s} match - skipping invocation", .{
142 std.fmt.fmtSliceHexLower(&digest),
143 });
144 macho_file.base.lock = man.toOwnedLock();
145 return;
146 }
147 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
148 std.fmt.fmtSliceHexLower(prev_digest),
149 std.fmt.fmtSliceHexLower(&digest),
150 });
151
152 // We are about to change the output file to be different, so we invalidate the build hash now.
153 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
154 error.FileNotFound => {},
155 else => |e| return e,
156 };
157 }
158
159 if (macho_file.base.options.output_mode == .Obj) {
160 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy
161 // here. TODO: think carefully about how we can avoid this redundant operation when doing
162 // build-obj. See also the corresponding TODO in linkAsArchive.
163 const the_object_path = blk: {
164 if (macho_file.base.options.objects.len != 0) {
165 break :blk macho_file.base.options.objects[0].path;
166 }
167
168 if (comp.c_object_table.count() != 0)
169 break :blk comp.c_object_table.keys()[0].status.success.object_path;
170
171 if (module_obj_path) |p|
172 break :blk p;
173
174 // TODO I think this is unreachable. Audit this situation when solving the above TODO
175 // regarding eliding redundant object -> object transformations.
176 return error.NoObjectsToLink;
177 };
178 // This can happen when using --enable-cache and using the stage1 backend. In this case
179 // we can skip the file copy.
180 if (!mem.eql(u8, the_object_path, full_out_path)) {
181 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
182 }
183 } else {
184 const sub_path = macho_file.base.options.emit.?.sub_path;
185 if (macho_file.base.file == null) {
186 macho_file.base.file = try directory.handle.createFile(sub_path, .{
187 .truncate = true,
188 .read = true,
189 .mode = link.determineMode(macho_file.base.options),
190 });
191 }
192 // Index 0 is always a null symbol.
193 try macho_file.locals.append(gpa, .{
194 .n_strx = 0,
195 .n_type = 0,
196 .n_sect = 0,
197 .n_desc = 0,
198 .n_value = 0,
199 });
200 try macho_file.strtab.buffer.append(gpa, 0);
201 try macho_file.populateMissingMetadata();
202
203 var lib_not_found = false;
204 var framework_not_found = false;
205
206 // Positional arguments to the linker such as object files and static archives.
207 var positionals = std.ArrayList([]const u8).init(arena);
208 try positionals.ensureUnusedCapacity(macho_file.base.options.objects.len);
209
210 var must_link_archives = std.StringArrayHashMap(void).init(arena);
211 try must_link_archives.ensureUnusedCapacity(macho_file.base.options.objects.len);
212
213 for (macho_file.base.options.objects) |obj| {
214 if (must_link_archives.contains(obj.path)) continue;
215 if (obj.must_link) {
216 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
217 } else {
218 _ = positionals.appendAssumeCapacity(obj.path);
219 }
220 }
221
222 for (comp.c_object_table.keys()) |key| {
223 try positionals.append(key.status.success.object_path);
224 }
225
226 if (module_obj_path) |p| {
227 try positionals.append(p);
228 }
229
230 if (comp.compiler_rt_lib) |lib| {
231 try positionals.append(lib.full_object_path);
232 }
233
234 // libc++ dep
235 if (macho_file.base.options.link_libcpp) {
236 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
237 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
238 }
239
240 // Shared and static libraries passed via `-l` flag.
241 var candidate_libs = std.StringArrayHashMap(link.SystemLib).init(arena);
242
243 const system_lib_names = macho_file.base.options.system_libs.keys();
244 for (system_lib_names) |system_lib_name| {
245 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
246 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
247 // case we want to avoid prepending "-l".
248 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
249 try positionals.append(system_lib_name);
250 continue;
251 }
252
253 const system_lib_info = macho_file.base.options.system_libs.get(system_lib_name).?;
254 try candidate_libs.put(system_lib_name, .{
255 .needed = system_lib_info.needed,
256 .weak = system_lib_info.weak,
257 });
258 }
259
260 var lib_dirs = std.ArrayList([]const u8).init(arena);
261 for (macho_file.base.options.lib_dirs) |dir| {
262 if (try MachO.resolveSearchDir(arena, dir, macho_file.base.options.sysroot)) |search_dir| {
263 try lib_dirs.append(search_dir);
264 } else {
265 log.warn("directory not found for '-L{s}'", .{dir});
266 }
267 }
268
269 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
270
271 // Assume ld64 default -search_paths_first if no strategy specified.
272 const search_strategy = macho_file.base.options.search_strategy orelse .paths_first;
273 outer: for (candidate_libs.keys()) |lib_name| {
274 switch (search_strategy) {
275 .paths_first => {
276 // Look in each directory for a dylib (stub first), and then for archive
277 for (lib_dirs.items) |dir| {
278 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
279 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
280 try libs.put(full_path, candidate_libs.get(lib_name).?);
281 continue :outer;
282 }
283 }
284 } else {
285 log.warn("library not found for '-l{s}'", .{lib_name});
286 lib_not_found = true;
287 }
288 },
289 .dylibs_first => {
290 // First, look for a dylib in each search dir
291 for (lib_dirs.items) |dir| {
292 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
293 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
294 try libs.put(full_path, candidate_libs.get(lib_name).?);
295 continue :outer;
296 }
297 }
298 } else for (lib_dirs.items) |dir| {
299 if (try MachO.resolveLib(arena, dir, lib_name, ".a")) |full_path| {
300 try libs.put(full_path, candidate_libs.get(lib_name).?);
301 } else {
302 log.warn("library not found for '-l{s}'", .{lib_name});
303 lib_not_found = true;
304 }
305 }
306 },
307 }
308 }
309
310 if (lib_not_found) {
311 log.warn("Library search paths:", .{});
312 for (lib_dirs.items) |dir| {
313 log.warn(" {s}", .{dir});
314 }
315 }
316
317 try macho_file.resolveLibSystem(arena, comp, lib_dirs.items, &libs);
318
319 // frameworks
320 var framework_dirs = std.ArrayList([]const u8).init(arena);
321 for (macho_file.base.options.framework_dirs) |dir| {
322 if (try MachO.resolveSearchDir(arena, dir, macho_file.base.options.sysroot)) |search_dir| {
323 try framework_dirs.append(search_dir);
324 } else {
325 log.warn("directory not found for '-F{s}'", .{dir});
326 }
327 }
328
329 outer: for (macho_file.base.options.frameworks.keys()) |f_name| {
330 for (framework_dirs.items) |dir| {
331 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
332 if (try MachO.resolveFramework(arena, dir, f_name, ext)) |full_path| {
333 const info = macho_file.base.options.frameworks.get(f_name).?;
334 try libs.put(full_path, .{
335 .needed = info.needed,
336 .weak = info.weak,
337 });
338 continue :outer;
339 }
340 }
341 } else {
342 log.warn("framework not found for '-framework {s}'", .{f_name});
343 framework_not_found = true;
344 }
345 }
346
347 if (framework_not_found) {
348 log.warn("Framework search paths:", .{});
349 for (framework_dirs.items) |dir| {
350 log.warn(" {s}", .{dir});
351 }
352 }
353
354 if (macho_file.base.options.verbose_link) {
355 var argv = std.ArrayList([]const u8).init(arena);
356
357 try argv.append("zig");
358 try argv.append("ld");
359
360 if (is_exe_or_dyn_lib) {
361 try argv.append("-dynamic");
362 }
363
364 if (is_dyn_lib) {
365 try argv.append("-dylib");
366
367 if (macho_file.base.options.install_name) |install_name| {
368 try argv.append("-install_name");
369 try argv.append(install_name);
370 }
371 }
372
373 if (macho_file.base.options.sysroot) |syslibroot| {
374 try argv.append("-syslibroot");
375 try argv.append(syslibroot);
376 }
377
378 for (macho_file.base.options.rpath_list) |rpath| {
379 try argv.append("-rpath");
380 try argv.append(rpath);
381 }
382
383 if (macho_file.base.options.pagezero_size) |pagezero_size| {
384 try argv.append("-pagezero_size");
385 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
386 }
387
388 if (macho_file.base.options.search_strategy) |strat| switch (strat) {
389 .paths_first => try argv.append("-search_paths_first"),
390 .dylibs_first => try argv.append("-search_dylibs_first"),
391 };
392
393 if (macho_file.base.options.headerpad_size) |headerpad_size| {
394 try argv.append("-headerpad_size");
395 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
396 }
397
398 if (macho_file.base.options.headerpad_max_install_names) {
399 try argv.append("-headerpad_max_install_names");
400 }
401
402 if (gc_sections) {
403 try argv.append("-dead_strip");
404 }
405
406 if (macho_file.base.options.dead_strip_dylibs) {
407 try argv.append("-dead_strip_dylibs");
408 }
409
410 if (macho_file.base.options.entry) |entry| {
411 try argv.append("-e");
412 try argv.append(entry);
413 }
414
415 for (macho_file.base.options.objects) |obj| {
416 try argv.append(obj.path);
417 }
418
419 for (comp.c_object_table.keys()) |key| {
420 try argv.append(key.status.success.object_path);
421 }
422
423 if (module_obj_path) |p| {
424 try argv.append(p);
425 }
426
427 if (comp.compiler_rt_lib) |lib| {
428 try argv.append(lib.full_object_path);
429 }
430
431 if (macho_file.base.options.link_libcpp) {
432 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
433 try argv.append(comp.libcxx_static_lib.?.full_object_path);
434 }
435
436 try argv.append("-o");
437 try argv.append(full_out_path);
438
439 try argv.append("-lSystem");
440 try argv.append("-lc");
441
442 for (macho_file.base.options.system_libs.keys()) |l_name| {
443 const info = macho_file.base.options.system_libs.get(l_name).?;
444 const arg = if (info.needed)
445 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
446 else if (info.weak)
447 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
448 else
449 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
450 try argv.append(arg);
451 }
452
453 for (macho_file.base.options.lib_dirs) |lib_dir| {
454 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
455 }
456
457 for (macho_file.base.options.frameworks.keys()) |framework| {
458 const info = macho_file.base.options.frameworks.get(framework).?;
459 const arg = if (info.needed)
460 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
461 else if (info.weak)
462 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
463 else
464 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
465 try argv.append(arg);
466 }
467
468 for (macho_file.base.options.framework_dirs) |framework_dir| {
469 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
470 }
471
472 if (is_dyn_lib and (macho_file.base.options.allow_shlib_undefined orelse false)) {
473 try argv.append("-undefined");
474 try argv.append("dynamic_lookup");
475 }
476
477 for (must_link_archives.keys()) |lib| {
478 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
479 }
480
481 Compilation.dump_argv(argv.items);
482 }
483
484 var dependent_libs = std.fifo.LinearFifo(struct {
485 id: Dylib.Id,
486 parent: u16,
487 }, .Dynamic).init(arena);
488
489 try macho_file.parseInputFiles(positionals.items, macho_file.base.options.sysroot, &dependent_libs);
490 try macho_file.parseAndForceLoadStaticArchives(must_link_archives.keys());
491 try macho_file.parseLibs(libs.keys(), libs.values(), macho_file.base.options.sysroot, &dependent_libs);
492 try macho_file.parseDependentLibs(macho_file.base.options.sysroot, &dependent_libs);
493
494 for (macho_file.objects.items) |_, object_id| {
495 try macho_file.resolveSymbolsInObject(@intCast(u16, object_id));
496 }
497
498 try macho_file.resolveSymbolsInArchives();
499 try macho_file.resolveDyldStubBinder();
500 try macho_file.resolveSymbolsInDylibs();
501 try macho_file.createMhExecuteHeaderSymbol();
502 try macho_file.createDsoHandleSymbol();
503 try macho_file.resolveSymbolsAtLoading();
504
505 if (macho_file.unresolved.count() > 0) {
506 return error.UndefinedSymbolReference;
507 }
508 if (lib_not_found) {
509 return error.LibraryNotFound;
510 }
511 if (framework_not_found) {
512 return error.FrameworkNotFound;
513 }
514
515 for (macho_file.objects.items) |*object| {
516 try object.scanInputSections(macho_file);
517 }
518
519 try macho_file.createDyldPrivateAtom();
520 try macho_file.createTentativeDefAtoms();
521 try macho_file.createStubHelperPreambleAtom();
522
523 for (macho_file.objects.items) |*object, object_id| {
524 try object.splitIntoAtoms(macho_file, @intCast(u32, object_id));
525 }
526
527 if (gc_sections) {
528 try dead_strip.gcAtoms(macho_file);
529 }
530
531 try allocateSegments(macho_file);
532 try allocateSymbols(macho_file);
533
534 try macho_file.allocateSpecialSymbols();
535
536 if (build_options.enable_logging or true) {
537 macho_file.logSymtab();
538 macho_file.logSections();
539 macho_file.logAtoms();
540 }
541
542 try writeAtoms(macho_file);
543
544 var lc_buffer = std.ArrayList(u8).init(arena);
545 const lc_writer = lc_buffer.writer();
546 var ncmds: u32 = 0;
547
548 try macho_file.writeLinkeditSegmentData(&ncmds, lc_writer);
549
550 // If the last section of __DATA segment is zerofill section, we need to ensure
551 // that the free space between the end of the last non-zerofill section of __DATA
552 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
553 // copy-paste this space into memory for quicker zerofill operation.
554 if (macho_file.data_segment_cmd_index) |data_seg_id| blk: {
555 var physical_zerofill_start: u64 = 0;
556 const section_indexes = macho_file.getSectionIndexes(data_seg_id);
557 for (macho_file.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
558 if (header.isZerofill() and header.size > 0) break;
559 physical_zerofill_start = header.offset + header.size;
560 } else break :blk;
561 const linkedit = macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
562 const physical_zerofill_size = math.cast(usize, linkedit.fileoff - physical_zerofill_start) orelse
563 return error.Overflow;
564 if (physical_zerofill_size > 0) {
565 var padding = try macho_file.base.allocator.alloc(u8, physical_zerofill_size);
566 defer macho_file.base.allocator.free(padding);
567 mem.set(u8, padding, 0);
568 try macho_file.base.file.?.pwriteAll(padding, physical_zerofill_start);
569 }
570 }
571
572 try MachO.writeDylinkerLC(&ncmds, lc_writer);
573 try macho_file.writeMainLC(&ncmds, lc_writer);
574 try macho_file.writeDylibIdLC(&ncmds, lc_writer);
575 try macho_file.writeRpathLCs(&ncmds, lc_writer);
576
577 {
578 try lc_writer.writeStruct(macho.source_version_command{
579 .cmdsize = @sizeOf(macho.source_version_command),
580 .version = 0x0,
581 });
582 ncmds += 1;
583 }
584
585 try macho_file.writeBuildVersionLC(&ncmds, lc_writer);
586
587 {
588 var uuid_lc = macho.uuid_command{
589 .cmdsize = @sizeOf(macho.uuid_command),
590 .uuid = undefined,
591 };
592 std.crypto.random.bytes(&uuid_lc.uuid);
593 try lc_writer.writeStruct(uuid_lc);
594 ncmds += 1;
595 }
596
597 try macho_file.writeLoadDylibLCs(&ncmds, lc_writer);
598
599 const requires_codesig = blk: {
600 if (macho_file.base.options.entitlements) |_| break :blk true;
601 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
602 break :blk false;
603 };
604 var codesig_offset: ?u32 = null;
605 var codesig: ?CodeSignature = if (requires_codesig) blk: {
606 // Preallocate space for the code signature.
607 // We need to do this at this stage so that we have the load commands with proper values
608 // written out to the file.
609 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
610 // where the code signature goes into.
611 var codesig = CodeSignature.init(macho_file.page_size);
612 codesig.code_directory.ident = macho_file.base.options.emit.?.sub_path;
613 if (macho_file.base.options.entitlements) |path| {
614 try codesig.addEntitlements(arena, path);
615 }
616 codesig_offset = try macho_file.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
617 break :blk codesig;
618 } else null;
619
620 var headers_buf = std.ArrayList(u8).init(arena);
621 try macho_file.writeSegmentHeaders(&ncmds, headers_buf.writer());
622
623 try macho_file.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
624 try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
625
626 try macho_file.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
627
628 if (codesig) |*csig| {
629 try macho_file.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
630 }
631 }
632
633 if (!macho_file.base.options.disable_lld_caching) {
634 // Update the file with the digest. If it fails we can continue; it only
635 // means that the next invocation will have an unnecessary cache miss.
636 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
637 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
638 };
639 // Again failure here only means an unnecessary cache miss.
640 man.writeManifest() catch |err| {
641 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
642 };
643 // We hang on to this lock so that the output file path can be used without
644 // other processes clobbering it.
645 macho_file.base.lock = man.toOwnedLock();
646 }
647}
648
649fn writeAtoms(macho_file: *MachO) !void {
650 assert(macho_file.mode == .one_shot);
651
652 const gpa = macho_file.base.allocator;
653 const slice = macho_file.sections.slice();
654
655 for (slice.items(.last_atom)) |last_atom, sect_id| {
656 const header = slice.items(.header)[sect_id];
657 if (header.size == 0) continue;
658 var atom = last_atom.?;
659
660 if (header.isZerofill()) continue;
661
662 var buffer = std.ArrayList(u8).init(gpa);
663 defer buffer.deinit();
664 try buffer.ensureTotalCapacity(math.cast(usize, header.size) orelse return error.Overflow);
665
666 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
667
668 while (atom.prev) |prev| {
669 atom = prev;
670 }
671
672 while (true) {
673 const this_sym = atom.getSymbol(macho_file);
674 const padding_size: usize = if (atom.next) |next| blk: {
675 const next_sym = next.getSymbol(macho_file);
676 const size = next_sym.n_value - (this_sym.n_value + atom.size);
677 break :blk math.cast(usize, size) orelse return error.Overflow;
678 } else 0;
679
680 log.debug(" (adding ATOM(%{d}, '{s}') from object({?d}) to buffer)", .{
681 atom.sym_index,
682 atom.getName(macho_file),
683 atom.file,
684 });
685 if (padding_size > 0) {
686 log.debug(" (with padding {x})", .{padding_size});
687 }
688
689 try atom.resolveRelocs(macho_file);
690 buffer.appendSliceAssumeCapacity(atom.code.items);
691
692 var i: usize = 0;
693 while (i < padding_size) : (i += 1) {
694 // TODO with NOPs
695 buffer.appendAssumeCapacity(0);
696 }
697
698 if (atom.next) |next| {
699 atom = next;
700 } else {
701 assert(buffer.items.len == header.size);
702 log.debug(" (writing at file offset 0x{x})", .{header.offset});
703 try macho_file.base.file.?.pwriteAll(buffer.items, header.offset);
704 break;
705 }
706 }
707 }
708}
709
710fn allocateSegments(macho_file: *MachO) !void {
711 try allocateSegment(macho_file, macho_file.text_segment_cmd_index, &.{
712 macho_file.pagezero_segment_cmd_index,
713 }, try macho_file.calcMinHeaderPad());
714
715 if (macho_file.text_segment_cmd_index) |index| blk: {
716 const indexes = macho_file.getSectionIndexes(index);
717 if (indexes.start == indexes.end) break :blk;
718 const seg = macho_file.segments.items[index];
719
720 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
721 var min_alignment: u32 = 0;
722 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| {
723 const alignment = try math.powi(u32, 2, header.@"align");
724 min_alignment = math.max(min_alignment, alignment);
725 }
726
727 assert(min_alignment > 0);
728 const last_header = macho_file.sections.items(.header)[indexes.end - 1];
729 const shift: u32 = shift: {
730 const diff = seg.filesize - last_header.offset - last_header.size;
731 const factor = @divTrunc(diff, min_alignment);
732 break :shift @intCast(u32, factor * min_alignment);
733 };
734
735 if (shift > 0) {
736 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |*header| {
737 header.offset += shift;
738 header.addr += shift;
739 }
740 }
741 }
742
743 try allocateSegment(macho_file, macho_file.data_const_segment_cmd_index, &.{
744 macho_file.text_segment_cmd_index,
745 macho_file.pagezero_segment_cmd_index,
746 }, 0);
747
748 try allocateSegment(macho_file, macho_file.data_segment_cmd_index, &.{
749 macho_file.data_const_segment_cmd_index,
750 macho_file.text_segment_cmd_index,
751 macho_file.pagezero_segment_cmd_index,
752 }, 0);
753
754 try allocateSegment(macho_file, macho_file.linkedit_segment_cmd_index, &.{
755 macho_file.data_segment_cmd_index,
756 macho_file.data_const_segment_cmd_index,
757 macho_file.text_segment_cmd_index,
758 macho_file.pagezero_segment_cmd_index,
759 }, 0);
760}
761
762fn allocateSegment(macho_file: *MachO, maybe_index: ?u8, indices: []const ?u8, init_size: u64) !void {
763 const index = maybe_index orelse return;
764 const seg = &macho_file.segments.items[index];
765
766 const base = macho_file.getSegmentAllocBase(indices);
767 seg.vmaddr = base.vmaddr;
768 seg.fileoff = base.fileoff;
769 seg.filesize = init_size;
770 seg.vmsize = init_size;
771
772 // Allocate the sections according to their alignment at the beginning of the segment.
773 const indexes = macho_file.getSectionIndexes(index);
774 var start = init_size;
775 const slice = macho_file.sections.slice();
776 for (slice.items(.header)[indexes.start..indexes.end]) |*header| {
777 const alignment = try math.powi(u32, 2, header.@"align");
778 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
779
780 header.offset = if (header.isZerofill())
781 0
782 else
783 @intCast(u32, seg.fileoff + start_aligned);
784 header.addr = seg.vmaddr + start_aligned;
785
786 start = start_aligned + header.size;
787
788 if (!header.isZerofill()) {
789 seg.filesize = start;
790 }
791 seg.vmsize = start;
792 }
793
794 seg.filesize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size);
795 seg.vmsize = mem.alignForwardGeneric(u64, seg.vmsize, macho_file.page_size);
796}
797
798fn allocateSymbols(macho_file: *MachO) !void {
799 const slice = macho_file.sections.slice();
800 for (slice.items(.last_atom)) |last_atom, sect_id| {
801 const header = slice.items(.header)[sect_id];
802 var atom = last_atom orelse continue;
803
804 while (atom.prev) |prev| {
805 atom = prev;
806 }
807
808 const n_sect = @intCast(u8, sect_id + 1);
809 var base_vaddr = header.addr;
810
811 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
812 n_sect,
813 header.segName(),
814 header.sectName(),
815 });
816
817 while (true) {
818 const alignment = try math.powi(u32, 2, atom.alignment);
819 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
820
821 const sym = atom.getSymbolPtr(macho_file);
822 sym.n_value = base_vaddr;
823 sym.n_sect = n_sect;
824
825 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(macho_file), base_vaddr });
826
827 // Update each symbol contained within the atom
828 for (atom.contained.items) |sym_at_off| {
829 const contained_sym = macho_file.getSymbolPtr(.{
830 .sym_index = sym_at_off.sym_index,
831 .file = atom.file,
832 });
833 contained_sym.n_value = base_vaddr + sym_at_off.offset;
834 contained_sym.n_sect = n_sect;
835 }
836
837 base_vaddr += atom.size;
838
839 if (atom.next) |next| {
840 atom = next;
841 } else break;
842 }
843 }
844}