| ... | ... | @@ -329,14 +329,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 329 | 329 | } |
| 330 | 330 | |
| 331 | 331 | var libs = std.StringArrayHashMap(link.SystemLib).init(arena); |
| 332 | | try resolveLibSystem( |
| 333 | | arena, |
| 334 | | comp, |
| 335 | | self.base.options.sysroot, |
| 336 | | self.base.options.target, |
| 337 | | &.{}, |
| 338 | | &libs, |
| 339 | | ); |
| 332 | try self.resolveLibSystem(arena, comp, &.{}, &libs); |
| 340 | 333 | |
| 341 | 334 | const id_symlink_basename = "link.id"; |
| 342 | 335 | |
| ... | ... | @@ -640,77 +633,104 @@ inline fn conformUuid(out: *[Md5.digest_length]u8) void { |
| 640 | 633 | } |
| 641 | 634 | |
| 642 | 635 | pub fn resolveLibSystem( |
| 636 | self: *MachO, |
| 643 | 637 | arena: Allocator, |
| 644 | 638 | comp: *Compilation, |
| 645 | | syslibroot: ?[]const u8, |
| 646 | | target: std.Target, |
| 647 | 639 | search_dirs: []const []const u8, |
| 648 | 640 | out_libs: anytype, |
| 649 | 641 | ) !void { |
| 650 | | // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}. |
| 651 | | if (syslibroot) |root| { |
| 652 | | const full_dir_path = try std.fs.path.join(arena, &.{ root, "usr", "lib" }); |
| 653 | | if (try resolveLibSystemInDirs(arena, &.{full_dir_path}, out_libs)) return; |
| 654 | | } |
| 642 | var tmp_arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); |
| 643 | defer tmp_arena_allocator.deinit(); |
| 644 | const tmp_arena = tmp_arena_allocator.allocator(); |
| 655 | 645 | |
| 656 | | // Next, try input search dirs if we are linking on a custom host such as Nix. |
| 657 | | if (try resolveLibSystemInDirs(arena, search_dirs, out_libs)) return; |
| 646 | var test_path = std.ArrayList(u8).init(tmp_arena); |
| 647 | var checked_paths = std.ArrayList([]const u8).init(tmp_arena); |
| 658 | 648 | |
| 659 | | // As a fallback, try linking against Zig shipped stub. |
| 660 | | const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{ |
| 661 | | target.os.version_range.semver.min.major, |
| 662 | | }); |
| 663 | | const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 664 | | "libc", "darwin", libsystem_name, |
| 665 | | }); |
| 666 | | try out_libs.put(full_path, .{ |
| 649 | success: { |
| 650 | if (self.base.options.sysroot) |root| { |
| 651 | const dir = try fs.path.join(tmp_arena, &[_][]const u8{ root, "usr", "lib" }); |
| 652 | if (try accessLibPath(tmp_arena, &test_path, &checked_paths, dir, "libSystem")) break :success; |
| 653 | } |
| 654 | |
| 655 | for (search_dirs) |dir| if (try accessLibPath( |
| 656 | tmp_arena, |
| 657 | &test_path, |
| 658 | &checked_paths, |
| 659 | dir, |
| 660 | "libSystem", |
| 661 | )) break :success; |
| 662 | |
| 663 | const dir = try comp.zig_lib_directory.join(tmp_arena, &[_][]const u8{ "libc", "darwin" }); |
| 664 | const lib_name = try std.fmt.allocPrint(tmp_arena, "libSystem.{d}", .{ |
| 665 | self.base.options.target.os.version_range.semver.min.major, |
| 666 | }); |
| 667 | if (try accessLibPath(tmp_arena, &test_path, &checked_paths, dir, lib_name)) break :success; |
| 668 | |
| 669 | try self.reportMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{}); |
| 670 | return; |
| 671 | } |
| 672 | |
| 673 | const libsystem_path = try arena.dupe(u8, test_path.items); |
| 674 | try out_libs.put(libsystem_path, .{ |
| 667 | 675 | .needed = true, |
| 668 | 676 | .weak = false, |
| 669 | | .path = full_path, |
| 677 | .path = libsystem_path, |
| 670 | 678 | }); |
| 671 | | } |
| 672 | 679 | |
| 673 | | fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs: anytype) !bool { |
| 674 | | // Try stub file first. If we hit it, then we're done as the stub file |
| 675 | | // re-exports every single symbol definition. |
| 676 | | for (dirs) |dir| { |
| 677 | | if (try resolveLib(arena, dir, "libSystem", ".tbd")) |full_path| { |
| 678 | | try out_libs.put(full_path, .{ .needed = true, .weak = false, .path = full_path }); |
| 679 | | return true; |
| 680 | | } |
| 681 | | } |
| 682 | | // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib |
| 683 | | // doesn't export libc.dylib which we'll need to resolve subsequently also. |
| 684 | | for (dirs) |dir| { |
| 685 | | if (try resolveLib(arena, dir, "libSystem", ".dylib")) |libsystem_path| { |
| 686 | | if (try resolveLib(arena, dir, "libc", ".dylib")) |libc_path| { |
| 687 | | try out_libs.put(libsystem_path, .{ .needed = true, .weak = false, .path = libsystem_path }); |
| 688 | | try out_libs.put(libc_path, .{ .needed = true, .weak = false, .path = libc_path }); |
| 689 | | return true; |
| 680 | const ext = fs.path.extension(libsystem_path); |
| 681 | if (mem.eql(u8, ext, ".dylib")) { |
| 682 | // We found 'libSystem.dylib', so now we also need to look for 'libc.dylib'. |
| 683 | success: { |
| 684 | if (self.base.options.sysroot) |root| { |
| 685 | const dir = try fs.path.join(tmp_arena, &[_][]const u8{ root, "usr", "lib" }); |
| 686 | if (try accessLibPath(tmp_arena, &test_path, &checked_paths, dir, "libc")) break :success; |
| 690 | 687 | } |
| 688 | |
| 689 | for (search_dirs) |dir| if (try accessLibPath( |
| 690 | tmp_arena, |
| 691 | &test_path, |
| 692 | &checked_paths, |
| 693 | dir, |
| 694 | "libc", |
| 695 | )) break :success; |
| 696 | |
| 697 | try self.reportMissingLibraryError(checked_paths.items, "unable to find libc system library", .{}); |
| 691 | 698 | } |
| 692 | 699 | } |
| 693 | | |
| 694 | | return false; |
| 695 | 700 | } |
| 696 | 701 | |
| 697 | | fn resolveLib( |
| 698 | | arena: Allocator, |
| 702 | fn accessLibPath( |
| 703 | gpa: Allocator, |
| 704 | test_path: *std.ArrayList(u8), |
| 705 | checked_paths: *std.ArrayList([]const u8), |
| 699 | 706 | search_dir: []const u8, |
| 700 | | name: []const u8, |
| 701 | | ext: []const u8, |
| 702 | | ) !?[]const u8 { |
| 703 | | const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext }); |
| 704 | | const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, search_name }); |
| 705 | | |
| 706 | | // Check if the file exists. |
| 707 | | const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { |
| 708 | | error.FileNotFound => return null, |
| 709 | | else => |e| return e, |
| 710 | | }; |
| 711 | | defer tmp.close(); |
| 707 | lib_name: []const u8, |
| 708 | ) !bool { |
| 709 | const sep = fs.path.sep_str; |
| 710 | |
| 711 | tbd: { |
| 712 | test_path.clearRetainingCapacity(); |
| 713 | try test_path.writer().print("{s}" ++ sep ++ "{s}.tbd", .{ search_dir, lib_name }); |
| 714 | try checked_paths.append(try gpa.dupe(u8, test_path.items)); |
| 715 | fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { |
| 716 | error.FileNotFound => break :tbd, |
| 717 | else => |e| return e, |
| 718 | }; |
| 719 | return true; |
| 720 | } |
| 721 | |
| 722 | dylib: { |
| 723 | test_path.clearRetainingCapacity(); |
| 724 | try test_path.writer().print("{s}" ++ sep ++ "{s}.dylib", .{ search_dir, lib_name }); |
| 725 | try checked_paths.append(try gpa.dupe(u8, test_path.items)); |
| 726 | fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { |
| 727 | error.FileNotFound => break :dylib, |
| 728 | else => |e| return e, |
| 729 | }; |
| 730 | return true; |
| 731 | } |
| 712 | 732 | |
| 713 | | return full_path; |
| 733 | return false; |
| 714 | 734 | } |
| 715 | 735 | |
| 716 | 736 | const ParseError = error{ |
| ... | ... | @@ -1084,9 +1104,6 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { |
| 1084 | 1104 | // TODO this should not be performed if the user specifies `-flat_namespace` flag. |
| 1085 | 1105 | // See ld64 manpages. |
| 1086 | 1106 | const gpa = self.base.allocator; |
| 1087 | | var arena_alloc = std.heap.ArenaAllocator.init(gpa); |
| 1088 | | const arena = arena_alloc.allocator(); |
| 1089 | | defer arena_alloc.deinit(); |
| 1090 | 1107 | |
| 1091 | 1108 | while (dependent_libs.readItem()) |dep_id| { |
| 1092 | 1109 | defer dep_id.id.deinit(gpa); |
| ... | ... | @@ -1095,38 +1112,33 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { |
| 1095 | 1112 | |
| 1096 | 1113 | const parent = &self.dylibs.items[dep_id.parent]; |
| 1097 | 1114 | const weak = parent.weak; |
| 1098 | | const has_ext = blk: { |
| 1099 | | const basename = fs.path.basename(dep_id.id.name); |
| 1100 | | break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; |
| 1101 | | }; |
| 1102 | | const extension = if (has_ext) fs.path.extension(dep_id.id.name) else ""; |
| 1103 | | const without_ext = if (has_ext) blk: { |
| 1104 | | const index = mem.lastIndexOfScalar(u8, dep_id.id.name, '.') orelse unreachable; |
| 1105 | | break :blk dep_id.id.name[0..index]; |
| 1106 | | } else dep_id.id.name; |
| 1115 | const dirname = fs.path.dirname(dep_id.id.name) orelse ""; |
| 1116 | const stem = fs.path.stem(dep_id.id.name); |
| 1107 | 1117 | |
| 1108 | | const maybe_full_path = full_path: { |
| 1109 | | if (self.base.options.sysroot) |root| { |
| 1110 | | for (&[_][]const u8{ extension, ".tbd" }) |ext| { |
| 1111 | | if (try resolveLib(arena, root, without_ext, ext)) |full_path| break :full_path full_path; |
| 1112 | | } |
| 1113 | | } |
| 1118 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); |
| 1119 | defer arena_allocator.deinit(); |
| 1120 | const arena = arena_allocator.allocator(); |
| 1114 | 1121 | |
| 1115 | | for (&[_][]const u8{ extension, ".tbd" }) |ext| { |
| 1116 | | if (try resolveLib(arena, "", without_ext, ext)) |full_path| break :full_path full_path; |
| 1122 | var test_path = std.ArrayList(u8).init(arena); |
| 1123 | var checked_paths = std.ArrayList([]const u8).init(arena); |
| 1124 | |
| 1125 | success: { |
| 1126 | if (self.base.options.sysroot) |root| { |
| 1127 | const dir = try fs.path.join(arena, &[_][]const u8{ root, dirname }); |
| 1128 | if (try accessLibPath(gpa, &test_path, &checked_paths, dir, stem)) break :success; |
| 1117 | 1129 | } |
| 1118 | 1130 | |
| 1119 | | break :full_path null; |
| 1120 | | }; |
| 1131 | if (try accessLibPath(gpa, &test_path, &checked_paths, dirname, stem)) break :success; |
| 1121 | 1132 | |
| 1122 | | const full_path = maybe_full_path orelse { |
| 1123 | | const parent_name = if (parent.id) |id| id.name else parent.path; |
| 1124 | | try self.reportDependencyError(parent_name, null, "missing dynamic library dependency: '{s}'", .{ |
| 1125 | | dep_id.id.name, |
| 1126 | | }); |
| 1133 | try self.reportMissingLibraryError( |
| 1134 | checked_paths.items, |
| 1135 | "missing dynamic library dependency: '{s}'", |
| 1136 | .{dep_id.id.name}, |
| 1137 | ); |
| 1127 | 1138 | continue; |
| 1128 | | }; |
| 1139 | } |
| 1129 | 1140 | |
| 1141 | const full_path = test_path.items; |
| 1130 | 1142 | const file = try std.fs.cwd().openFile(full_path, .{}); |
| 1131 | 1143 | defer file.close(); |
| 1132 | 1144 | |
| ... | ... | @@ -4942,6 +4954,25 @@ pub fn handleAndReportParseError( |
| 4942 | 4954 | } |
| 4943 | 4955 | } |
| 4944 | 4956 | |
| 4957 | fn reportMissingLibraryError( |
| 4958 | self: *MachO, |
| 4959 | checked_paths: []const []const u8, |
| 4960 | comptime format: []const u8, |
| 4961 | args: anytype, |
| 4962 | ) error{OutOfMemory}!void { |
| 4963 | const gpa = self.base.allocator; |
| 4964 | try self.misc_errors.ensureUnusedCapacity(gpa, 1); |
| 4965 | var notes = try gpa.alloc(File.ErrorMsg, checked_paths.len); |
| 4966 | errdefer gpa.free(notes); |
| 4967 | for (checked_paths, notes) |path, *note| { |
| 4968 | note.* = .{ .msg = try std.fmt.allocPrint(gpa, "tried {s}", .{path}) }; |
| 4969 | } |
| 4970 | self.misc_errors.appendAssumeCapacity(.{ |
| 4971 | .msg = try std.fmt.allocPrint(gpa, format, args), |
| 4972 | .notes = notes, |
| 4973 | }); |
| 4974 | } |
| 4975 | |
| 4945 | 4976 | fn reportDependencyError( |
| 4946 | 4977 | self: *MachO, |
| 4947 | 4978 | parent: []const u8, |