| ... | @@ -430,7 +430,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { | ... | @@ -430,7 +430,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { |
| 430 | | 430 | |
| 431 | pub fn flush(self: *MachO, comp: *Compilation) !void { | 431 | pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 432 | if (build_options.have_llvm and self.base.options.use_lld) { | 432 | if (build_options.have_llvm and self.base.options.use_lld) { |
| 433 | return self.linkWithLLD(comp); | 433 | return self.linkWithZld(comp); |
| 434 | } else { | 434 | } else { |
| 435 | switch (self.base.options.effectiveOutputMode()) { | 435 | switch (self.base.options.effectiveOutputMode()) { |
| 436 | .Exe, .Obj => {}, | 436 | .Exe, .Obj => {}, |
| ... | @@ -593,7 +593,7 @@ fn resolveFramework( | ... | @@ -593,7 +593,7 @@ fn resolveFramework( |
| 593 | return null; | 593 | return null; |
| 594 | } | 594 | } |
| 595 | | 595 | |
| 596 | fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | 596 | fn linkWithZld(self: *MachO, comp: *Compilation) !void { |
| 597 | const tracy = trace(@src()); | 597 | const tracy = trace(@src()); |
| 598 | defer tracy.end(); | 598 | defer tracy.end(); |
| 599 | | 599 | |
| ... | @@ -626,7 +626,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -626,7 +626,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 626 | | 626 | |
| 627 | const is_lib = self.base.options.output_mode == .Lib; | 627 | const is_lib = self.base.options.output_mode == .Lib; |
| 628 | const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; | 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 | const target = self.base.options.target; | 630 | const target = self.base.options.target; |
| 631 | const stack_size = self.base.options.stack_size_override orelse 0; | 631 | const stack_size = self.base.options.stack_size_override orelse 0; |
| 632 | const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; | 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,7 +669,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 669 | man.hash.addStringSet(self.base.options.system_libs); | 669 | man.hash.addStringSet(self.base.options.system_libs); |
| 670 | man.hash.add(allow_shlib_undefined); | 670 | man.hash.add(allow_shlib_undefined); |
| 671 | man.hash.add(self.base.options.bind_global_refs_locally); | 671 | man.hash.add(self.base.options.bind_global_refs_locally); |
| 672 | man.hash.add(self.base.options.system_linker_hack); | | |
| 673 | man.hash.addOptionalBytes(self.base.options.sysroot); | 672 | man.hash.addOptionalBytes(self.base.options.sysroot); |
| 674 | | 673 | |
| 675 | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. | 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,17 +681,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 682 | id_symlink_basename, | 681 | id_symlink_basename, |
| 683 | &prev_digest_buf, | 682 | &prev_digest_buf, |
| 684 | ) catch |err| blk: { | 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 | // Handle this as a cache miss. | 685 | // Handle this as a cache miss. |
| 687 | break :blk prev_digest_buf[0..0]; | 686 | break :blk prev_digest_buf[0..0]; |
| 688 | }; | 687 | }; |
| 689 | if (mem.eql(u8, prev_digest, &digest)) { | 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 | // Hot diggity dog! The output binary is already there. | 690 | // Hot diggity dog! The output binary is already there. |
| 692 | self.base.lock = man.toOwnedLock(); | 691 | self.base.lock = man.toOwnedLock(); |
| 693 | return; | 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 | // We are about to change the output file to be different, so we invalidate the build hash now. | 696 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 698 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | 697 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
| ... | @@ -726,495 +725,199 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -726,495 +725,199 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 726 | if (!mem.eql(u8, the_object_path, full_out_path)) { | 725 | if (!mem.eql(u8, the_object_path, full_out_path)) { |
| 727 | try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); | 726 | try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); |
| 728 | } | 727 | } |
| 729 | } else outer: { | 728 | } else { |
| 730 | const use_zld = blk: { | 729 | var zld = Zld.init(self.base.allocator); |
| 731 | if (self.base.options.is_native_os and self.base.options.system_linker_hack) { | 730 | defer { |
| 732 | // If the user forces the use of ld64, make sure we are running native! | 731 | zld.closeFiles(); |
| 733 | break :blk false; | 732 | zld.deinit(); |
| 734 | } | 733 | } |
| 735 | | 734 | zld.arch = target.cpu.arch; |
| 736 | if (self.base.options.target.cpu.arch == .aarch64) { | 735 | zld.stack_size = stack_size; |
| 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 | } | | |
| 747 | | 736 | |
| 748 | break :blk true; | 737 | // Positional arguments to the linker such as object files and static archives. |
| 749 | }; | 738 | var positionals = std.ArrayList([]const u8).init(arena); |
| 750 | | 739 | |
| 751 | if (use_zld) { | 740 | try positionals.appendSlice(self.base.options.objects); |
| 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; | | |
| 759 | | 741 | |
| 760 | // Positional arguments to the linker such as object files and static archives. | 742 | for (comp.c_object_table.keys()) |key| { |
| 761 | var positionals = std.ArrayList([]const u8).init(arena); | 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| { | 750 | try positionals.append(comp.compiler_rt_static_lib.?.full_object_path); |
| 766 | try positionals.append(key.status.success.object_path); | | |
| 767 | } | | |
| 768 | | 751 | |
| 769 | if (module_obj_path) |p| { | 752 | // libc++ dep |
| 770 | try positionals.append(p); | 753 | if (self.base.options.link_libcpp) { |
| 771 | } | 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 | 761 | const system_libs = self.base.options.system_libs.keys(); |
| 776 | if (self.base.options.link_libcpp) { | 762 | for (system_libs) |link_lib| { |
| 777 | try positionals.append(comp.libcxxabi_static_lib.?.full_object_path); | 763 | // By this time, we depend on these libs being dynamically linked libraries and not static libraries |
| 778 | try positionals.append(comp.libcxx_static_lib.?.full_object_path); | 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. | 771 | try search_lib_names.append(link_lib); |
| 782 | var search_lib_names = std.ArrayList([]const u8).init(arena); | 772 | } |
| 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 | } | | |
| 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); | 783 | var libs = std.ArrayList([]const u8).init(arena); |
| 798 | for (self.base.options.lib_dirs) |dir| { | 784 | var lib_not_found = false; |
| 799 | if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| { | 785 | for (search_lib_names.items) |lib_name| { |
| 800 | try lib_dirs.append(search_dir); | 786 | // Assume ld64 default: -search_paths_first |
| 801 | } else { | 787 | // Look in each directory for a dylib (stub first), and then for archive |
| 802 | log.warn("directory not found for '-L{s}'", .{dir}); | 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); | 800 | if (lib_not_found) { |
| 807 | var lib_not_found = false; | 801 | log.warn("Library search paths:", .{}); |
| 808 | for (search_lib_names.items) |lib_name| { | 802 | for (lib_dirs.items) |dir| { |
| 809 | // Assume ld64 default: -search_paths_first | 803 | log.warn(" {s}", .{dir}); |
| 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 | } | | |
| 821 | } | 804 | } |
| | 805 | } |
| 822 | | 806 | |
| 823 | if (lib_not_found) { | 807 | // If we're compiling native and we can find libSystem.B.{dylib, tbd}, |
| 824 | log.warn("Library search paths:", .{}); | 808 | // we link against that instead of embedded libSystem.B.tbd file. |
| 825 | for (lib_dirs.items) |dir| { | 809 | var native_libsystem_available = false; |
| 826 | log.warn(" {s}", .{dir}); | 810 | if (self.base.options.is_native_os) blk: { |
| 827 | } | 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 | | 818 | // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib |
| 830 | // If we're compiling native and we can find libSystem.B.{dylib, tbd}, | 819 | // doesn't export libc.dylib which we'll need to resolve subsequently also. |
| 831 | // we link against that instead of embedded libSystem.B.tbd file. | 820 | if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| { |
| 832 | var native_libsystem_available = false; | 821 | if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| { |
| 833 | if (self.base.options.is_native_os) blk: { | 822 | try libs.append(libsystem_path); |
| 834 | // Try stub file first. If we hit it, then we're done as the stub file | 823 | try libs.append(libc_path); |
| 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); | | |
| 838 | native_libsystem_available = true; | 824 | native_libsystem_available = true; |
| 839 | break :blk; | 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) { | 828 | } |
| 853 | const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 829 | if (!native_libsystem_available) { |
| 854 | "libc", "darwin", "libSystem.B.tbd", | 830 | const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 855 | }); | 831 | "libc", "darwin", "libSystem.B.tbd", |
| 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, | | |
| 943 | }); | 832 | }); |
| 944 | | 833 | try libs.append(full_path); |
| 945 | break :outer; | | |
| 946 | } | 834 | } |
| 947 | | 835 | |
| 948 | // Create an LLD command line and invoke it. | 836 | // frameworks |
| 949 | var argv = std.ArrayList([]const u8).init(self.base.allocator); | 837 | var framework_dirs = std.ArrayList([]const u8).init(arena); |
| 950 | defer argv.deinit(); | 838 | for (self.base.options.framework_dirs) |dir| { |
| 951 | | 839 | if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| { |
| 952 | // TODO https://github.com/ziglang/zig/issues/6971 | 840 | try framework_dirs.append(search_dir); |
| 953 | // Note that there is no need to check if running natively since we do that already | 841 | } else { |
| 954 | // when setting `system_linker_hack` in Compilation struct. | 842 | log.warn("directory not found for '-F{s}'", .{dir}); |
| 955 | if (self.base.options.system_linker_hack) { | 843 | } |
| 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"); | | |
| 965 | } | 844 | } |
| 966 | | 845 | |
| 967 | if (self.base.options.lto) { | 846 | var framework_not_found = false; |
| 968 | switch (self.base.options.optimize_mode) { | 847 | for (self.base.options.frameworks) |framework| { |
| 969 | .Debug => {}, | 848 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { |
| 970 | .ReleaseSmall => try argv.append("-O2"), | 849 | if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| { |
| 971 | .ReleaseFast, .ReleaseSafe => try argv.append("-O3"), | 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) { | 859 | if (framework_not_found) { |
| 977 | try argv.append("--export-dynamic"); | 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); | 866 | // rpaths |
| 981 | | 867 | var rpath_table = std.StringArrayHashMap(void).init(arena); |
| 982 | if (self.base.options.z_nodelete) { | 868 | for (self.base.options.rpath_list) |rpath| { |
| 983 | try argv.append("-z"); | 869 | if (rpath_table.contains(rpath)) continue; |
| 984 | try argv.append("nodelete"); | 870 | try rpath_table.putNoClobber(rpath, {}); |
| 985 | } | | |
| 986 | if (self.base.options.z_defs) { | | |
| 987 | try argv.append("-z"); | | |
| 988 | try argv.append("defs"); | | |
| 989 | } | 871 | } |
| 990 | | 872 | |
| 991 | if (is_exe_or_dyn_lib) { | 873 | var rpaths = std.ArrayList([]const u8).init(arena); |
| 992 | try argv.append("-dynamic"); | 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) { | 879 | if (self.base.options.verbose_link) { |
| 996 | try argv.append("-dylib"); | 880 | var argv = std.ArrayList([]const u8).init(arena); |
| 997 | | 881 | |
| 998 | if (self.base.options.version) |ver| { | 882 | try argv.append("zig"); |
| 999 | const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major}); | 883 | try argv.append("ld"); |
| 1000 | try argv.append("-compatibility_version"); | | |
| 1001 | try argv.append(compat_vers); | | |
| 1002 | | 884 | |
| 1003 | const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); | 885 | if (self.base.options.sysroot) |syslibroot| { |
| 1004 | try argv.append("-current_version"); | 886 | try argv.append("-syslibroot"); |
| 1005 | try argv.append(cur_vers); | 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}); | 890 | for (rpaths.items) |rpath| { |
| 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) { | | |
| 1049 | try argv.append("-rpath"); | 891 | try argv.append("-rpath"); |
| 1050 | try argv.append(rpath); | 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. | 895 | try argv.appendSlice(positionals.items); |
| 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 | } | | |
| 1096 | | 896 | |
| 1097 | // libc++ dep | 897 | try argv.append("-o"); |
| 1098 | if (self.base.options.link_libcpp) { | 898 | try argv.append(full_out_path); |
| 1099 | try argv.append(comp.libcxxabi_static_lib.?.full_object_path); | | |
| 1100 | try argv.append(comp.libcxx_static_lib.?.full_object_path); | | |
| 1101 | } | | |
| 1102 | | 899 | |
| 1103 | // On Darwin, libSystem has libc in it, but also you have to use it | 900 | if (native_libsystem_available) { |
| 1104 | // to make syscalls because the syscall numbers are not documented | 901 | try argv.append("-lSystem"); |
| 1105 | // and change between versions. So we always link against libSystem. | 902 | try argv.append("-lc"); |
| 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}); | | |
| 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| { | 905 | for (search_lib_names.items) |l_name| { |
| 1194 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | 906 | try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name})); |
| 1195 | return error.UnableToSpawnSelf; | 907 | } |
| 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 | } | | |
| 1212 | | 908 | |
| 1213 | if (stderr.len != 0) { | 909 | for (self.base.options.lib_dirs) |lib_dir| { |
| 1214 | log.warn("unexpected LLD stderr:\n{s}", .{stderr}); | 910 | try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir})); |
| 1215 | } | | |
| 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 | if (!self.base.options.disable_lld_caching) { | 923 | if (!self.base.options.disable_lld_caching) { |