| ... | @@ -329,14 +329,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -329,14 +329,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 329 | } | 329 | } |
| 330 | | 330 | |
| 331 | var libs = std.StringArrayHashMap(link.SystemLib).init(arena); | 331 | var libs = std.StringArrayHashMap(link.SystemLib).init(arena); |
| 332 | try resolveLibSystem( | 332 | try self.resolveLibSystem(arena, comp, &.{}, &libs); |
| 333 | arena, | | |
| 334 | comp, | | |
| 335 | self.base.options.sysroot, | | |
| 336 | self.base.options.target, | | |
| 337 | &.{}, | | |
| 338 | &libs, | | |
| 339 | ); | | |
| 340 | | 333 | |
| 341 | const id_symlink_basename = "link.id"; | 334 | const id_symlink_basename = "link.id"; |
| 342 | | 335 | |
| ... | @@ -640,77 +633,104 @@ inline fn conformUuid(out: *[Md5.digest_length]u8) void { | ... | @@ -640,77 +633,104 @@ inline fn conformUuid(out: *[Md5.digest_length]u8) void { |
| 640 | } | 633 | } |
| 641 | | 634 | |
| 642 | pub fn resolveLibSystem( | 635 | pub fn resolveLibSystem( |
| | 636 | self: *MachO, |
| 643 | arena: Allocator, | 637 | arena: Allocator, |
| 644 | comp: *Compilation, | 638 | comp: *Compilation, |
| 645 | syslibroot: ?[]const u8, | | |
| 646 | target: std.Target, | | |
| 647 | search_dirs: []const []const u8, | 639 | search_dirs: []const []const u8, |
| 648 | out_libs: anytype, | 640 | out_libs: anytype, |
| 649 | ) !void { | 641 | ) !void { |
| 650 | // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}. | 642 | var tmp_arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); |
| 651 | if (syslibroot) |root| { | 643 | defer tmp_arena_allocator.deinit(); |
| 652 | const full_dir_path = try std.fs.path.join(arena, &.{ root, "usr", "lib" }); | 644 | const tmp_arena = tmp_arena_allocator.allocator(); |
| 653 | if (try resolveLibSystemInDirs(arena, &.{full_dir_path}, out_libs)) return; | | |
| 654 | } | | |
| 655 | | 645 | |
| 656 | // Next, try input search dirs if we are linking on a custom host such as Nix. | 646 | var test_path = std.ArrayList(u8).init(tmp_arena); |
| 657 | if (try resolveLibSystemInDirs(arena, search_dirs, out_libs)) return; | 647 | var checked_paths = std.ArrayList([]const u8).init(tmp_arena); |
| 658 | | 648 | |
| 659 | // As a fallback, try linking against Zig shipped stub. | 649 | success: { |
| 660 | const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{ | 650 | if (self.base.options.sysroot) |root| { |
| 661 | target.os.version_range.semver.min.major, | 651 | const dir = try fs.path.join(tmp_arena, &[_][]const u8{ root, "usr", "lib" }); |
| 662 | }); | 652 | if (try accessLibPath(tmp_arena, &test_path, &checked_paths, dir, "libSystem")) break :success; |
| 663 | const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 653 | } |
| 664 | "libc", "darwin", libsystem_name, | 654 | |
| 665 | }); | 655 | for (search_dirs) |dir| if (try accessLibPath( |
| 666 | try out_libs.put(full_path, .{ | 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 | .needed = true, | 675 | .needed = true, |
| 668 | .weak = false, | 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 { | 680 | const ext = fs.path.extension(libsystem_path); |
| 674 | // Try stub file first. If we hit it, then we're done as the stub file | 681 | if (mem.eql(u8, ext, ".dylib")) { |
| 675 | // re-exports every single symbol definition. | 682 | // We found 'libSystem.dylib', so now we also need to look for 'libc.dylib'. |
| 676 | for (dirs) |dir| { | 683 | success: { |
| 677 | if (try resolveLib(arena, dir, "libSystem", ".tbd")) |full_path| { | 684 | if (self.base.options.sysroot) |root| { |
| 678 | try out_libs.put(full_path, .{ .needed = true, .weak = false, .path = full_path }); | 685 | const dir = try fs.path.join(tmp_arena, &[_][]const u8{ root, "usr", "lib" }); |
| 679 | return true; | 686 | if (try accessLibPath(tmp_arena, &test_path, &checked_paths, dir, "libc")) break :success; |
| 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; | | |
| 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( | 702 | fn accessLibPath( |
| 698 | arena: Allocator, | 703 | gpa: Allocator, |
| | 704 | test_path: *std.ArrayList(u8), |
| | 705 | checked_paths: *std.ArrayList([]const u8), |
| 699 | search_dir: []const u8, | 706 | search_dir: []const u8, |
| 700 | name: []const u8, | 707 | lib_name: []const u8, |
| 701 | ext: []const u8, | 708 | ) !bool { |
| 702 | ) !?[]const u8 { | 709 | const sep = fs.path.sep_str; |
| 703 | const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext }); | 710 | |
| 704 | const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, search_name }); | 711 | tbd: { |
| 705 | | 712 | test_path.clearRetainingCapacity(); |
| 706 | // Check if the file exists. | 713 | try test_path.writer().print("{s}" ++ sep ++ "{s}.tbd", .{ search_dir, lib_name }); |
| 707 | const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { | 714 | try checked_paths.append(try gpa.dupe(u8, test_path.items)); |
| 708 | error.FileNotFound => return null, | 715 | fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { |
| 709 | else => |e| return e, | 716 | error.FileNotFound => break :tbd, |
| 710 | }; | 717 | else => |e| return e, |
| 711 | defer tmp.close(); | 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 | const ParseError = error{ | 736 | const ParseError = error{ |
| ... | @@ -1084,9 +1104,6 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { | ... | @@ -1084,9 +1104,6 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { |
| 1084 | // TODO this should not be performed if the user specifies `-flat_namespace` flag. | 1104 | // TODO this should not be performed if the user specifies `-flat_namespace` flag. |
| 1085 | // See ld64 manpages. | 1105 | // See ld64 manpages. |
| 1086 | const gpa = self.base.allocator; | 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 | while (dependent_libs.readItem()) |dep_id| { | 1108 | while (dependent_libs.readItem()) |dep_id| { |
| 1092 | defer dep_id.id.deinit(gpa); | 1109 | defer dep_id.id.deinit(gpa); |
| ... | @@ -1095,38 +1112,33 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { | ... | @@ -1095,38 +1112,33 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { |
| 1095 | | 1112 | |
| 1096 | const parent = &self.dylibs.items[dep_id.parent]; | 1113 | const parent = &self.dylibs.items[dep_id.parent]; |
| 1097 | const weak = parent.weak; | 1114 | const weak = parent.weak; |
| 1098 | const has_ext = blk: { | 1115 | const dirname = fs.path.dirname(dep_id.id.name) orelse ""; |
| 1099 | const basename = fs.path.basename(dep_id.id.name); | 1116 | const stem = fs.path.stem(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; | | |
| 1107 | | 1117 | |
| 1108 | const maybe_full_path = full_path: { | 1118 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); |
| 1109 | if (self.base.options.sysroot) |root| { | 1119 | defer arena_allocator.deinit(); |
| 1110 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { | 1120 | const arena = arena_allocator.allocator(); |
| 1111 | if (try resolveLib(arena, root, without_ext, ext)) |full_path| break :full_path full_path; | | |
| 1112 | } | | |
| 1113 | } | | |
| 1114 | | 1121 | |
| 1115 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { | 1122 | var test_path = std.ArrayList(u8).init(arena); |
| 1116 | if (try resolveLib(arena, "", without_ext, ext)) |full_path| break :full_path full_path; | 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; | 1131 | if (try accessLibPath(gpa, &test_path, &checked_paths, dirname, stem)) break :success; |
| 1120 | }; | | |
| 1121 | | 1132 | |
| 1122 | const full_path = maybe_full_path orelse { | 1133 | try self.reportMissingLibraryError( |
| 1123 | const parent_name = if (parent.id) |id| id.name else parent.path; | 1134 | checked_paths.items, |
| 1124 | try self.reportDependencyError(parent_name, null, "missing dynamic library dependency: '{s}'", .{ | 1135 | "missing dynamic library dependency: '{s}'", |
| 1125 | dep_id.id.name, | 1136 | .{dep_id.id.name}, |
| 1126 | }); | 1137 | ); |
| 1127 | continue; | 1138 | continue; |
| 1128 | }; | 1139 | } |
| 1129 | | 1140 | |
| | 1141 | const full_path = test_path.items; |
| 1130 | const file = try std.fs.cwd().openFile(full_path, .{}); | 1142 | const file = try std.fs.cwd().openFile(full_path, .{}); |
| 1131 | defer file.close(); | 1143 | defer file.close(); |
| 1132 | | 1144 | |
| ... | @@ -4942,6 +4954,25 @@ pub fn handleAndReportParseError( | ... | @@ -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 | fn reportDependencyError( | 4976 | fn reportDependencyError( |
| 4946 | self: *MachO, | 4977 | self: *MachO, |
| 4947 | parent: []const u8, | 4978 | parent: []const u8, |