| ... | ... | @@ -430,7 +430,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { |
| 430 | 430 | |
| 431 | 431 | pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 432 | 432 | if (build_options.have_llvm and self.base.options.use_lld) { |
| 433 | | return self.linkWithLLD(comp); |
| 433 | return self.linkWithZld(comp); |
| 434 | 434 | } else { |
| 435 | 435 | switch (self.base.options.effectiveOutputMode()) { |
| 436 | 436 | .Exe, .Obj => {}, |
| ... | ... | @@ -593,7 +593,7 @@ fn resolveFramework( |
| 593 | 593 | return null; |
| 594 | 594 | } |
| 595 | 595 | |
| 596 | | fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 596 | fn linkWithZld(self: *MachO, comp: *Compilation) !void { |
| 597 | 597 | const tracy = trace(@src()); |
| 598 | 598 | defer tracy.end(); |
| 599 | 599 | |
| ... | ... | @@ -626,7 +626,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 626 | 626 | |
| 627 | 627 | const is_lib = self.base.options.output_mode == .Lib; |
| 628 | 628 | const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; |
| 629 | | const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; |
| 629 | // const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; |
| 630 | 630 | const target = self.base.options.target; |
| 631 | 631 | const stack_size = self.base.options.stack_size_override orelse 0; |
| 632 | 632 | const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; |
| ... | ... | @@ -669,7 +669,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 669 | 669 | man.hash.addStringSet(self.base.options.system_libs); |
| 670 | 670 | man.hash.add(allow_shlib_undefined); |
| 671 | 671 | man.hash.add(self.base.options.bind_global_refs_locally); |
| 672 | | man.hash.add(self.base.options.system_linker_hack); |
| 673 | 672 | man.hash.addOptionalBytes(self.base.options.sysroot); |
| 674 | 673 | |
| 675 | 674 | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. |
| ... | ... | @@ -682,17 +681,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 682 | 681 | id_symlink_basename, |
| 683 | 682 | &prev_digest_buf, |
| 684 | 683 | ) catch |err| blk: { |
| 685 | | log.debug("MachO LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); |
| 684 | log.debug("MachO Zld new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); |
| 686 | 685 | // Handle this as a cache miss. |
| 687 | 686 | break :blk prev_digest_buf[0..0]; |
| 688 | 687 | }; |
| 689 | 688 | if (mem.eql(u8, prev_digest, &digest)) { |
| 690 | | log.debug("MachO LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); |
| 689 | log.debug("MachO Zld digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); |
| 691 | 690 | // Hot diggity dog! The output binary is already there. |
| 692 | 691 | self.base.lock = man.toOwnedLock(); |
| 693 | 692 | return; |
| 694 | 693 | } |
| 695 | | log.debug("MachO LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); |
| 694 | log.debug("MachO Zld prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); |
| 696 | 695 | |
| 697 | 696 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 698 | 697 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
| ... | ... | @@ -726,495 +725,199 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 726 | 725 | if (!mem.eql(u8, the_object_path, full_out_path)) { |
| 727 | 726 | try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); |
| 728 | 727 | } |
| 729 | | } else outer: { |
| 730 | | const use_zld = blk: { |
| 731 | | if (self.base.options.is_native_os and self.base.options.system_linker_hack) { |
| 732 | | // If the user forces the use of ld64, make sure we are running native! |
| 733 | | break :blk false; |
| 734 | | } |
| 735 | | |
| 736 | | if (self.base.options.target.cpu.arch == .aarch64) { |
| 737 | | // On aarch64, always use zld. |
| 738 | | break :blk true; |
| 739 | | } |
| 740 | | |
| 741 | | if (self.base.options.output_mode == .Lib or |
| 742 | | self.base.options.linker_script != null) |
| 743 | | { |
| 744 | | // Fallback to LLD in this handful of cases on x86_64 only. |
| 745 | | break :blk false; |
| 746 | | } |
| 728 | } else { |
| 729 | var zld = Zld.init(self.base.allocator); |
| 730 | defer { |
| 731 | zld.closeFiles(); |
| 732 | zld.deinit(); |
| 733 | } |
| 734 | zld.arch = target.cpu.arch; |
| 735 | zld.stack_size = stack_size; |
| 747 | 736 | |
| 748 | | break :blk true; |
| 749 | | }; |
| 737 | // Positional arguments to the linker such as object files and static archives. |
| 738 | var positionals = std.ArrayList([]const u8).init(arena); |
| 750 | 739 | |
| 751 | | if (use_zld) { |
| 752 | | var zld = Zld.init(self.base.allocator); |
| 753 | | defer { |
| 754 | | zld.closeFiles(); |
| 755 | | zld.deinit(); |
| 756 | | } |
| 757 | | zld.arch = target.cpu.arch; |
| 758 | | zld.stack_size = stack_size; |
| 740 | try positionals.appendSlice(self.base.options.objects); |
| 759 | 741 | |
| 760 | | // Positional arguments to the linker such as object files and static archives. |
| 761 | | var positionals = std.ArrayList([]const u8).init(arena); |
| 742 | for (comp.c_object_table.keys()) |key| { |
| 743 | try positionals.append(key.status.success.object_path); |
| 744 | } |
| 762 | 745 | |
| 763 | | try positionals.appendSlice(self.base.options.objects); |
| 746 | if (module_obj_path) |p| { |
| 747 | try positionals.append(p); |
| 748 | } |
| 764 | 749 | |
| 765 | | for (comp.c_object_table.keys()) |key| { |
| 766 | | try positionals.append(key.status.success.object_path); |
| 767 | | } |
| 750 | try positionals.append(comp.compiler_rt_static_lib.?.full_object_path); |
| 768 | 751 | |
| 769 | | if (module_obj_path) |p| { |
| 770 | | try positionals.append(p); |
| 771 | | } |
| 752 | // libc++ dep |
| 753 | if (self.base.options.link_libcpp) { |
| 754 | try positionals.append(comp.libcxxabi_static_lib.?.full_object_path); |
| 755 | try positionals.append(comp.libcxx_static_lib.?.full_object_path); |
| 756 | } |
| 772 | 757 | |
| 773 | | try positionals.append(comp.compiler_rt_static_lib.?.full_object_path); |
| 758 | // Shared and static libraries passed via `-l` flag. |
| 759 | var search_lib_names = std.ArrayList([]const u8).init(arena); |
| 774 | 760 | |
| 775 | | // libc++ dep |
| 776 | | if (self.base.options.link_libcpp) { |
| 777 | | try positionals.append(comp.libcxxabi_static_lib.?.full_object_path); |
| 778 | | try positionals.append(comp.libcxx_static_lib.?.full_object_path); |
| 761 | const system_libs = self.base.options.system_libs.keys(); |
| 762 | for (system_libs) |link_lib| { |
| 763 | // By this time, we depend on these libs being dynamically linked libraries and not static libraries |
| 764 | // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which |
| 765 | // case we want to avoid prepending "-l". |
| 766 | if (Compilation.classifyFileExt(link_lib) == .shared_library) { |
| 767 | try positionals.append(link_lib); |
| 768 | continue; |
| 779 | 769 | } |
| 780 | 770 | |
| 781 | | // Shared and static libraries passed via `-l` flag. |
| 782 | | var search_lib_names = std.ArrayList([]const u8).init(arena); |
| 783 | | |
| 784 | | const system_libs = self.base.options.system_libs.keys(); |
| 785 | | for (system_libs) |link_lib| { |
| 786 | | // By this time, we depend on these libs being dynamically linked libraries and not static libraries |
| 787 | | // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which |
| 788 | | // case we want to avoid prepending "-l". |
| 789 | | if (Compilation.classifyFileExt(link_lib) == .shared_library) { |
| 790 | | try positionals.append(link_lib); |
| 791 | | continue; |
| 792 | | } |
| 771 | try search_lib_names.append(link_lib); |
| 772 | } |
| 793 | 773 | |
| 794 | | try search_lib_names.append(link_lib); |
| 774 | var lib_dirs = std.ArrayList([]const u8).init(arena); |
| 775 | for (self.base.options.lib_dirs) |dir| { |
| 776 | if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| { |
| 777 | try lib_dirs.append(search_dir); |
| 778 | } else { |
| 779 | log.warn("directory not found for '-L{s}'", .{dir}); |
| 795 | 780 | } |
| 781 | } |
| 796 | 782 | |
| 797 | | var lib_dirs = std.ArrayList([]const u8).init(arena); |
| 798 | | for (self.base.options.lib_dirs) |dir| { |
| 799 | | if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| { |
| 800 | | try lib_dirs.append(search_dir); |
| 801 | | } else { |
| 802 | | log.warn("directory not found for '-L{s}'", .{dir}); |
| 783 | var libs = std.ArrayList([]const u8).init(arena); |
| 784 | var lib_not_found = false; |
| 785 | for (search_lib_names.items) |lib_name| { |
| 786 | // Assume ld64 default: -search_paths_first |
| 787 | // Look in each directory for a dylib (stub first), and then for archive |
| 788 | // TODO implement alternative: -search_dylibs_first |
| 789 | for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| { |
| 790 | if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| { |
| 791 | try libs.append(full_path); |
| 792 | break; |
| 803 | 793 | } |
| 794 | } else { |
| 795 | log.warn("library not found for '-l{s}'", .{lib_name}); |
| 796 | lib_not_found = true; |
| 804 | 797 | } |
| 798 | } |
| 805 | 799 | |
| 806 | | var libs = std.ArrayList([]const u8).init(arena); |
| 807 | | var lib_not_found = false; |
| 808 | | for (search_lib_names.items) |lib_name| { |
| 809 | | // Assume ld64 default: -search_paths_first |
| 810 | | // Look in each directory for a dylib (stub first), and then for archive |
| 811 | | // TODO implement alternative: -search_dylibs_first |
| 812 | | for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| { |
| 813 | | if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| { |
| 814 | | try libs.append(full_path); |
| 815 | | break; |
| 816 | | } |
| 817 | | } else { |
| 818 | | log.warn("library not found for '-l{s}'", .{lib_name}); |
| 819 | | lib_not_found = true; |
| 820 | | } |
| 800 | if (lib_not_found) { |
| 801 | log.warn("Library search paths:", .{}); |
| 802 | for (lib_dirs.items) |dir| { |
| 803 | log.warn(" {s}", .{dir}); |
| 821 | 804 | } |
| 805 | } |
| 822 | 806 | |
| 823 | | if (lib_not_found) { |
| 824 | | log.warn("Library search paths:", .{}); |
| 825 | | for (lib_dirs.items) |dir| { |
| 826 | | log.warn(" {s}", .{dir}); |
| 827 | | } |
| 807 | // If we're compiling native and we can find libSystem.B.{dylib, tbd}, |
| 808 | // we link against that instead of embedded libSystem.B.tbd file. |
| 809 | var native_libsystem_available = false; |
| 810 | if (self.base.options.is_native_os) blk: { |
| 811 | // Try stub file first. If we hit it, then we're done as the stub file |
| 812 | // re-exports every single symbol definition. |
| 813 | if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| { |
| 814 | try libs.append(full_path); |
| 815 | native_libsystem_available = true; |
| 816 | break :blk; |
| 828 | 817 | } |
| 829 | | |
| 830 | | // If we're compiling native and we can find libSystem.B.{dylib, tbd}, |
| 831 | | // we link against that instead of embedded libSystem.B.tbd file. |
| 832 | | var native_libsystem_available = false; |
| 833 | | if (self.base.options.is_native_os) blk: { |
| 834 | | // Try stub file first. If we hit it, then we're done as the stub file |
| 835 | | // re-exports every single symbol definition. |
| 836 | | if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| { |
| 837 | | try libs.append(full_path); |
| 818 | // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib |
| 819 | // doesn't export libc.dylib which we'll need to resolve subsequently also. |
| 820 | if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| { |
| 821 | if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| { |
| 822 | try libs.append(libsystem_path); |
| 823 | try libs.append(libc_path); |
| 838 | 824 | native_libsystem_available = true; |
| 839 | 825 | break :blk; |
| 840 | 826 | } |
| 841 | | // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib |
| 842 | | // doesn't export libc.dylib which we'll need to resolve subsequently also. |
| 843 | | if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| { |
| 844 | | if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| { |
| 845 | | try libs.append(libsystem_path); |
| 846 | | try libs.append(libc_path); |
| 847 | | native_libsystem_available = true; |
| 848 | | break :blk; |
| 849 | | } |
| 850 | | } |
| 851 | 827 | } |
| 852 | | if (!native_libsystem_available) { |
| 853 | | const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 854 | | "libc", "darwin", "libSystem.B.tbd", |
| 855 | | }); |
| 856 | | try libs.append(full_path); |
| 857 | | } |
| 858 | | |
| 859 | | // frameworks |
| 860 | | var framework_dirs = std.ArrayList([]const u8).init(arena); |
| 861 | | for (self.base.options.framework_dirs) |dir| { |
| 862 | | if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| { |
| 863 | | try framework_dirs.append(search_dir); |
| 864 | | } else { |
| 865 | | log.warn("directory not found for '-F{s}'", .{dir}); |
| 866 | | } |
| 867 | | } |
| 868 | | |
| 869 | | var framework_not_found = false; |
| 870 | | for (self.base.options.frameworks) |framework| { |
| 871 | | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { |
| 872 | | if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| { |
| 873 | | try libs.append(full_path); |
| 874 | | break; |
| 875 | | } |
| 876 | | } else { |
| 877 | | log.warn("framework not found for '-f{s}'", .{framework}); |
| 878 | | framework_not_found = true; |
| 879 | | } |
| 880 | | } |
| 881 | | |
| 882 | | if (framework_not_found) { |
| 883 | | log.warn("Framework search paths:", .{}); |
| 884 | | for (framework_dirs.items) |dir| { |
| 885 | | log.warn(" {s}", .{dir}); |
| 886 | | } |
| 887 | | } |
| 888 | | |
| 889 | | // rpaths |
| 890 | | var rpath_table = std.StringArrayHashMap(void).init(arena); |
| 891 | | for (self.base.options.rpath_list) |rpath| { |
| 892 | | if (rpath_table.contains(rpath)) continue; |
| 893 | | try rpath_table.putNoClobber(rpath, {}); |
| 894 | | } |
| 895 | | |
| 896 | | var rpaths = std.ArrayList([]const u8).init(arena); |
| 897 | | try rpaths.ensureCapacity(rpath_table.count()); |
| 898 | | for (rpath_table.keys()) |*key| { |
| 899 | | rpaths.appendAssumeCapacity(key.*); |
| 900 | | } |
| 901 | | |
| 902 | | if (self.base.options.verbose_link) { |
| 903 | | var argv = std.ArrayList([]const u8).init(arena); |
| 904 | | |
| 905 | | try argv.append("zig"); |
| 906 | | try argv.append("ld"); |
| 907 | | |
| 908 | | if (self.base.options.sysroot) |syslibroot| { |
| 909 | | try argv.append("-syslibroot"); |
| 910 | | try argv.append(syslibroot); |
| 911 | | } |
| 912 | | |
| 913 | | for (rpaths.items) |rpath| { |
| 914 | | try argv.append("-rpath"); |
| 915 | | try argv.append(rpath); |
| 916 | | } |
| 917 | | |
| 918 | | try argv.appendSlice(positionals.items); |
| 919 | | |
| 920 | | try argv.append("-o"); |
| 921 | | try argv.append(full_out_path); |
| 922 | | |
| 923 | | if (native_libsystem_available) { |
| 924 | | try argv.append("-lSystem"); |
| 925 | | try argv.append("-lc"); |
| 926 | | } |
| 927 | | |
| 928 | | for (search_lib_names.items) |l_name| { |
| 929 | | try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name})); |
| 930 | | } |
| 931 | | |
| 932 | | for (self.base.options.lib_dirs) |lib_dir| { |
| 933 | | try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir})); |
| 934 | | } |
| 935 | | |
| 936 | | Compilation.dump_argv(argv.items); |
| 937 | | } |
| 938 | | |
| 939 | | try zld.link(positionals.items, full_out_path, .{ |
| 940 | | .syslibroot = self.base.options.sysroot, |
| 941 | | .libs = libs.items, |
| 942 | | .rpaths = rpaths.items, |
| 828 | } |
| 829 | if (!native_libsystem_available) { |
| 830 | const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 831 | "libc", "darwin", "libSystem.B.tbd", |
| 943 | 832 | }); |
| 944 | | |
| 945 | | break :outer; |
| 833 | try libs.append(full_path); |
| 946 | 834 | } |
| 947 | 835 | |
| 948 | | // Create an LLD command line and invoke it. |
| 949 | | var argv = std.ArrayList([]const u8).init(self.base.allocator); |
| 950 | | defer argv.deinit(); |
| 951 | | |
| 952 | | // TODO https://github.com/ziglang/zig/issues/6971 |
| 953 | | // Note that there is no need to check if running natively since we do that already |
| 954 | | // when setting `system_linker_hack` in Compilation struct. |
| 955 | | if (self.base.options.system_linker_hack) { |
| 956 | | try argv.append("ld"); |
| 957 | | } else { |
| 958 | | // We will invoke ourselves as a child process to gain access to LLD. |
| 959 | | // This is necessary because LLD does not behave properly as a library - |
| 960 | | // it calls exit() and does not reset all global data between invocations. |
| 961 | | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld64.lld" }); |
| 962 | | |
| 963 | | try argv.append("-error-limit"); |
| 964 | | try argv.append("0"); |
| 836 | // frameworks |
| 837 | var framework_dirs = std.ArrayList([]const u8).init(arena); |
| 838 | for (self.base.options.framework_dirs) |dir| { |
| 839 | if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| { |
| 840 | try framework_dirs.append(search_dir); |
| 841 | } else { |
| 842 | log.warn("directory not found for '-F{s}'", .{dir}); |
| 843 | } |
| 965 | 844 | } |
| 966 | 845 | |
| 967 | | if (self.base.options.lto) { |
| 968 | | switch (self.base.options.optimize_mode) { |
| 969 | | .Debug => {}, |
| 970 | | .ReleaseSmall => try argv.append("-O2"), |
| 971 | | .ReleaseFast, .ReleaseSafe => try argv.append("-O3"), |
| 846 | var framework_not_found = false; |
| 847 | for (self.base.options.frameworks) |framework| { |
| 848 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { |
| 849 | if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| { |
| 850 | try libs.append(full_path); |
| 851 | break; |
| 852 | } |
| 853 | } else { |
| 854 | log.warn("framework not found for '-f{s}'", .{framework}); |
| 855 | framework_not_found = true; |
| 972 | 856 | } |
| 973 | 857 | } |
| 974 | | try argv.append("-demangle"); |
| 975 | 858 | |
| 976 | | if (self.base.options.rdynamic and !self.base.options.system_linker_hack) { |
| 977 | | try argv.append("--export-dynamic"); |
| 859 | if (framework_not_found) { |
| 860 | log.warn("Framework search paths:", .{}); |
| 861 | for (framework_dirs.items) |dir| { |
| 862 | log.warn(" {s}", .{dir}); |
| 863 | } |
| 978 | 864 | } |
| 979 | 865 | |
| 980 | | try argv.appendSlice(self.base.options.extra_lld_args); |
| 981 | | |
| 982 | | if (self.base.options.z_nodelete) { |
| 983 | | try argv.append("-z"); |
| 984 | | try argv.append("nodelete"); |
| 985 | | } |
| 986 | | if (self.base.options.z_defs) { |
| 987 | | try argv.append("-z"); |
| 988 | | try argv.append("defs"); |
| 866 | // rpaths |
| 867 | var rpath_table = std.StringArrayHashMap(void).init(arena); |
| 868 | for (self.base.options.rpath_list) |rpath| { |
| 869 | if (rpath_table.contains(rpath)) continue; |
| 870 | try rpath_table.putNoClobber(rpath, {}); |
| 989 | 871 | } |
| 990 | 872 | |
| 991 | | if (is_exe_or_dyn_lib) { |
| 992 | | try argv.append("-dynamic"); |
| 873 | var rpaths = std.ArrayList([]const u8).init(arena); |
| 874 | try rpaths.ensureCapacity(rpath_table.count()); |
| 875 | for (rpath_table.keys()) |*key| { |
| 876 | rpaths.appendAssumeCapacity(key.*); |
| 993 | 877 | } |
| 994 | 878 | |
| 995 | | if (is_dyn_lib) { |
| 996 | | try argv.append("-dylib"); |
| 879 | if (self.base.options.verbose_link) { |
| 880 | var argv = std.ArrayList([]const u8).init(arena); |
| 997 | 881 | |
| 998 | | if (self.base.options.version) |ver| { |
| 999 | | const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major}); |
| 1000 | | try argv.append("-compatibility_version"); |
| 1001 | | try argv.append(compat_vers); |
| 882 | try argv.append("zig"); |
| 883 | try argv.append("ld"); |
| 1002 | 884 | |
| 1003 | | const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); |
| 1004 | | try argv.append("-current_version"); |
| 1005 | | try argv.append(cur_vers); |
| 885 | if (self.base.options.sysroot) |syslibroot| { |
| 886 | try argv.append("-syslibroot"); |
| 887 | try argv.append(syslibroot); |
| 1006 | 888 | } |
| 1007 | 889 | |
| 1008 | | const dylib_install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{self.base.options.emit.?.sub_path}); |
| 1009 | | try argv.append("-install_name"); |
| 1010 | | try argv.append(dylib_install_name); |
| 1011 | | } |
| 1012 | | |
| 1013 | | try argv.append("-arch"); |
| 1014 | | try argv.append(darwinArchString(target.cpu.arch)); |
| 1015 | | |
| 1016 | | switch (target.os.tag) { |
| 1017 | | .macos => { |
| 1018 | | try argv.append("-macosx_version_min"); |
| 1019 | | }, |
| 1020 | | .ios, .tvos, .watchos => switch (target.cpu.arch) { |
| 1021 | | .i386, .x86_64 => { |
| 1022 | | try argv.append("-ios_simulator_version_min"); |
| 1023 | | }, |
| 1024 | | else => { |
| 1025 | | try argv.append("-iphoneos_version_min"); |
| 1026 | | }, |
| 1027 | | }, |
| 1028 | | else => unreachable, |
| 1029 | | } |
| 1030 | | const ver = target.os.version_range.semver.min; |
| 1031 | | const version_string = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); |
| 1032 | | try argv.append(version_string); |
| 1033 | | |
| 1034 | | try argv.append("-sdk_version"); |
| 1035 | | try argv.append(version_string); |
| 1036 | | |
| 1037 | | if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) { |
| 1038 | | try argv.append("-pie"); |
| 1039 | | } |
| 1040 | | |
| 1041 | | try argv.append("-o"); |
| 1042 | | try argv.append(full_out_path); |
| 1043 | | |
| 1044 | | // rpaths |
| 1045 | | var rpath_table = std.StringHashMap(void).init(self.base.allocator); |
| 1046 | | defer rpath_table.deinit(); |
| 1047 | | for (self.base.options.rpath_list) |rpath| { |
| 1048 | | if ((try rpath_table.fetchPut(rpath, {})) == null) { |
| 890 | for (rpaths.items) |rpath| { |
| 1049 | 891 | try argv.append("-rpath"); |
| 1050 | 892 | try argv.append(rpath); |
| 1051 | 893 | } |
| 1052 | | } |
| 1053 | | if (is_dyn_lib) { |
| 1054 | | if ((try rpath_table.fetchPut(full_out_path, {})) == null) { |
| 1055 | | try argv.append("-rpath"); |
| 1056 | | try argv.append(full_out_path); |
| 1057 | | } |
| 1058 | | } |
| 1059 | | |
| 1060 | | if (self.base.options.sysroot) |dir| { |
| 1061 | | try argv.append("-syslibroot"); |
| 1062 | | try argv.append(dir); |
| 1063 | | } |
| 1064 | | |
| 1065 | | for (self.base.options.lib_dirs) |lib_dir| { |
| 1066 | | try argv.append("-L"); |
| 1067 | | try argv.append(lib_dir); |
| 1068 | | } |
| 1069 | | |
| 1070 | | // Positional arguments to the linker such as object files. |
| 1071 | | try argv.appendSlice(self.base.options.objects); |
| 1072 | | |
| 1073 | | for (comp.c_object_table.keys()) |key| { |
| 1074 | | try argv.append(key.status.success.object_path); |
| 1075 | | } |
| 1076 | | if (module_obj_path) |p| { |
| 1077 | | try argv.append(p); |
| 1078 | | } |
| 1079 | | |
| 1080 | | // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce |
| 1081 | | if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) { |
| 1082 | | try argv.append(comp.compiler_rt_static_lib.?.full_object_path); |
| 1083 | | } |
| 1084 | 894 | |
| 1085 | | // Shared libraries. |
| 1086 | | const system_libs = self.base.options.system_libs.keys(); |
| 1087 | | try argv.ensureCapacity(argv.items.len + system_libs.len); |
| 1088 | | for (system_libs) |link_lib| { |
| 1089 | | // By this time, we depend on these libs being dynamically linked libraries and not static libraries |
| 1090 | | // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which |
| 1091 | | // case we want to avoid prepending "-l". |
| 1092 | | const ext = Compilation.classifyFileExt(link_lib); |
| 1093 | | const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib}); |
| 1094 | | argv.appendAssumeCapacity(arg); |
| 1095 | | } |
| 895 | try argv.appendSlice(positionals.items); |
| 1096 | 896 | |
| 1097 | | // libc++ dep |
| 1098 | | if (self.base.options.link_libcpp) { |
| 1099 | | try argv.append(comp.libcxxabi_static_lib.?.full_object_path); |
| 1100 | | try argv.append(comp.libcxx_static_lib.?.full_object_path); |
| 1101 | | } |
| 897 | try argv.append("-o"); |
| 898 | try argv.append(full_out_path); |
| 1102 | 899 | |
| 1103 | | // On Darwin, libSystem has libc in it, but also you have to use it |
| 1104 | | // to make syscalls because the syscall numbers are not documented |
| 1105 | | // and change between versions. So we always link against libSystem. |
| 1106 | | // LLD craps out if you do -lSystem cross compiling, so until that |
| 1107 | | // codebase gets some love from the new maintainers we're left with |
| 1108 | | // this dirty hack. |
| 1109 | | if (self.base.options.is_native_os) { |
| 1110 | | try argv.append("-lSystem"); |
| 1111 | | } |
| 1112 | | |
| 1113 | | for (self.base.options.framework_dirs) |framework_dir| { |
| 1114 | | try argv.append("-F"); |
| 1115 | | try argv.append(framework_dir); |
| 1116 | | } |
| 1117 | | for (self.base.options.frameworks) |framework| { |
| 1118 | | try argv.append("-framework"); |
| 1119 | | try argv.append(framework); |
| 1120 | | } |
| 1121 | | |
| 1122 | | if (allow_shlib_undefined) { |
| 1123 | | try argv.append("-undefined"); |
| 1124 | | try argv.append("dynamic_lookup"); |
| 1125 | | } |
| 1126 | | if (self.base.options.bind_global_refs_locally) { |
| 1127 | | try argv.append("-Bsymbolic"); |
| 1128 | | } |
| 1129 | | |
| 1130 | | if (self.base.options.verbose_link) { |
| 1131 | | // Potentially skip over our own name so that the LLD linker name is the first argv item. |
| 1132 | | const adjusted_argv = if (self.base.options.system_linker_hack) argv.items else argv.items[1..]; |
| 1133 | | Compilation.dump_argv(adjusted_argv); |
| 1134 | | } |
| 1135 | | |
| 1136 | | // TODO https://github.com/ziglang/zig/issues/6971 |
| 1137 | | // Note that there is no need to check if running natively since we do that already |
| 1138 | | // when setting `system_linker_hack` in Compilation struct. |
| 1139 | | if (self.base.options.system_linker_hack) { |
| 1140 | | const result = try std.ChildProcess.exec(.{ .allocator = self.base.allocator, .argv = argv.items }); |
| 1141 | | defer { |
| 1142 | | self.base.allocator.free(result.stdout); |
| 1143 | | self.base.allocator.free(result.stderr); |
| 1144 | | } |
| 1145 | | if (result.stdout.len != 0) { |
| 1146 | | log.warn("unexpected LD stdout: {s}", .{result.stdout}); |
| 900 | if (native_libsystem_available) { |
| 901 | try argv.append("-lSystem"); |
| 902 | try argv.append("-lc"); |
| 1147 | 903 | } |
| 1148 | | if (result.stderr.len != 0) { |
| 1149 | | log.warn("unexpected LD stderr: {s}", .{result.stderr}); |
| 1150 | | } |
| 1151 | | if (result.term != .Exited or result.term.Exited != 0) { |
| 1152 | | // TODO parse this output and surface with the Compilation API rather than |
| 1153 | | // directly outputting to stderr here. |
| 1154 | | log.err("{s}", .{result.stderr}); |
| 1155 | | return error.LDReportedFailure; |
| 1156 | | } |
| 1157 | | } else { |
| 1158 | | // Sadly, we must run LLD as a child process because it does not behave |
| 1159 | | // properly as a library. |
| 1160 | | const child = try std.ChildProcess.init(argv.items, arena); |
| 1161 | | defer child.deinit(); |
| 1162 | | |
| 1163 | | if (comp.clang_passthrough_mode) { |
| 1164 | | child.stdin_behavior = .Inherit; |
| 1165 | | child.stdout_behavior = .Inherit; |
| 1166 | | child.stderr_behavior = .Inherit; |
| 1167 | | |
| 1168 | | const term = child.spawnAndWait() catch |err| { |
| 1169 | | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); |
| 1170 | | return error.UnableToSpawnSelf; |
| 1171 | | }; |
| 1172 | | switch (term) { |
| 1173 | | .Exited => |code| { |
| 1174 | | if (code != 0) { |
| 1175 | | // TODO https://github.com/ziglang/zig/issues/6342 |
| 1176 | | std.process.exit(1); |
| 1177 | | } |
| 1178 | | }, |
| 1179 | | else => { |
| 1180 | | log.err("{s} terminated", .{argv.items[0]}); |
| 1181 | | return error.LLDCrashed; |
| 1182 | | }, |
| 1183 | | } |
| 1184 | | } else { |
| 1185 | | child.stdin_behavior = .Ignore; |
| 1186 | | child.stdout_behavior = .Ignore; |
| 1187 | | child.stderr_behavior = .Pipe; |
| 1188 | | |
| 1189 | | try child.spawn(); |
| 1190 | | |
| 1191 | | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); |
| 1192 | 904 | |
| 1193 | | const term = child.wait() catch |err| { |
| 1194 | | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); |
| 1195 | | return error.UnableToSpawnSelf; |
| 1196 | | }; |
| 1197 | | |
| 1198 | | switch (term) { |
| 1199 | | .Exited => |code| { |
| 1200 | | if (code != 0) { |
| 1201 | | // TODO parse this output and surface with the Compilation API rather than |
| 1202 | | // directly outputting to stderr here. |
| 1203 | | std.debug.print("{s}", .{stderr}); |
| 1204 | | return error.LLDReportedFailure; |
| 1205 | | } |
| 1206 | | }, |
| 1207 | | else => { |
| 1208 | | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); |
| 1209 | | return error.LLDCrashed; |
| 1210 | | }, |
| 1211 | | } |
| 905 | for (search_lib_names.items) |l_name| { |
| 906 | try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name})); |
| 907 | } |
| 1212 | 908 | |
| 1213 | | if (stderr.len != 0) { |
| 1214 | | log.warn("unexpected LLD stderr:\n{s}", .{stderr}); |
| 1215 | | } |
| 909 | for (self.base.options.lib_dirs) |lib_dir| { |
| 910 | try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir})); |
| 1216 | 911 | } |
| 912 | |
| 913 | Compilation.dump_argv(argv.items); |
| 1217 | 914 | } |
| 915 | |
| 916 | try zld.link(positionals.items, full_out_path, .{ |
| 917 | .syslibroot = self.base.options.sysroot, |
| 918 | .libs = libs.items, |
| 919 | .rpaths = rpaths.items, |
| 920 | }); |
| 1218 | 921 | } |
| 1219 | 922 | |
| 1220 | 923 | if (!self.base.options.disable_lld_caching) { |