| ... | @@ -401,26 +401,25 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -401,26 +401,25 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 401 | parent: u16, | 401 | parent: u16, |
| 402 | }, .Dynamic).init(arena); | 402 | }, .Dynamic).init(arena); |
| 403 | | 403 | |
| 404 | var parse_ctx = ParseErrorCtx.init(arena); | | |
| 405 | | | |
| 406 | for (libs.keys(), libs.values()) |path, lib| { | 404 | for (libs.keys(), libs.values()) |path, lib| { |
| 407 | const in_file = try std.fs.cwd().openFile(path, .{}); | 405 | const in_file = try std.fs.cwd().openFile(path, .{}); |
| 408 | defer in_file.close(); | 406 | defer in_file.close(); |
| 409 | defer parse_ctx.detected_targets.clearRetainingCapacity(); | 407 | |
| | 408 | var parse_ctx = ParseErrorCtx.init(self.base.allocator); |
| | 409 | defer parse_ctx.deinit(); |
| | 410 | |
| 410 | self.parseLibrary( | 411 | self.parseLibrary( |
| 411 | in_file, | 412 | in_file, |
| 412 | path, | 413 | path, |
| 413 | lib, | 414 | lib, |
| 414 | false, | 415 | false, |
| | 416 | false, |
| 415 | &dependent_libs, | 417 | &dependent_libs, |
| 416 | &parse_ctx, | 418 | &parse_ctx, |
| 417 | ) catch |err| try self.handleAndReportParseError(path, err, &parse_ctx); | 419 | ) catch |err| try self.handleAndReportParseError(path, err, &parse_ctx); |
| 418 | } | 420 | } |
| 419 | | 421 | |
| 420 | self.parseDependentLibs(&dependent_libs, &parse_ctx) catch |err| { | 422 | try self.parseDependentLibs(&dependent_libs); |
| 421 | // TODO convert to error | | |
| 422 | log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)}); | | |
| 423 | }; | | |
| 424 | } | 423 | } |
| 425 | | 424 | |
| 426 | var actions = std.ArrayList(ResolveAction).init(self.base.allocator); | 425 | var actions = std.ArrayList(ResolveAction).init(self.base.allocator); |
| ... | @@ -674,7 +673,7 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs: | ... | @@ -674,7 +673,7 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs: |
| 674 | // Try stub file first. If we hit it, then we're done as the stub file | 673 | // Try stub file first. If we hit it, then we're done as the stub file |
| 675 | // re-exports every single symbol definition. | 674 | // re-exports every single symbol definition. |
| 676 | for (dirs) |dir| { | 675 | for (dirs) |dir| { |
| 677 | if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| { | 676 | if (try resolveLib(arena, dir, "libSystem", ".tbd")) |full_path| { |
| 678 | try out_libs.put(full_path, .{ .needed = true, .weak = false, .path = full_path }); | 677 | try out_libs.put(full_path, .{ .needed = true, .weak = false, .path = full_path }); |
| 679 | return true; | 678 | return true; |
| 680 | } | 679 | } |
| ... | @@ -682,8 +681,8 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs: | ... | @@ -682,8 +681,8 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs: |
| 682 | // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib | 681 | // 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. | 682 | // doesn't export libc.dylib which we'll need to resolve subsequently also. |
| 684 | for (dirs) |dir| { | 683 | for (dirs) |dir| { |
| 685 | if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| { | 684 | if (try resolveLib(arena, dir, "libSystem", ".dylib")) |libsystem_path| { |
| 686 | if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| { | 685 | if (try resolveLib(arena, dir, "libc", ".dylib")) |libc_path| { |
| 687 | try out_libs.put(libsystem_path, .{ .needed = true, .weak = false, .path = libsystem_path }); | 686 | 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 }); | 687 | try out_libs.put(libc_path, .{ .needed = true, .weak = false, .path = libc_path }); |
| 689 | return true; | 688 | return true; |
| ... | @@ -700,7 +699,7 @@ fn resolveLib( | ... | @@ -700,7 +699,7 @@ fn resolveLib( |
| 700 | name: []const u8, | 699 | name: []const u8, |
| 701 | ext: []const u8, | 700 | ext: []const u8, |
| 702 | ) !?[]const u8 { | 701 | ) !?[]const u8 { |
| 703 | const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext }); | 702 | 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 }); | 703 | const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, search_name }); |
| 705 | | 704 | |
| 706 | // Check if the file exists. | 705 | // Check if the file exists. |
| ... | @@ -747,7 +746,7 @@ pub fn parsePositional( | ... | @@ -747,7 +746,7 @@ pub fn parsePositional( |
| 747 | .path = null, | 746 | .path = null, |
| 748 | .needed = false, | 747 | .needed = false, |
| 749 | .weak = false, | 748 | .weak = false, |
| 750 | }, must_link, dependent_libs, ctx); | 749 | }, must_link, false, dependent_libs, ctx); |
| 751 | } | 750 | } |
| 752 | } | 751 | } |
| 753 | | 752 | |
| ... | @@ -790,7 +789,7 @@ fn parseObject( | ... | @@ -790,7 +789,7 @@ fn parseObject( |
| 790 | (detected_platform != null and !detected_platform.?.eqlTarget(this_platform))) | 789 | (detected_platform != null and !detected_platform.?.eqlTarget(this_platform))) |
| 791 | { | 790 | { |
| 792 | const platform = detected_platform orelse this_platform; | 791 | const platform = detected_platform orelse this_platform; |
| 793 | try ctx.detected_targets.append(try platform.allocPrintTarget(ctx.arena, detected_cpu_arch)); | 792 | try ctx.detected_targets.append(try platform.allocPrintTarget(ctx.arena(), detected_cpu_arch)); |
| 794 | return error.InvalidTarget; | 793 | return error.InvalidTarget; |
| 795 | } | 794 | } |
| 796 | | 795 | |
| ... | @@ -803,6 +802,7 @@ pub fn parseLibrary( | ... | @@ -803,6 +802,7 @@ pub fn parseLibrary( |
| 803 | path: []const u8, | 802 | path: []const u8, |
| 804 | lib: link.SystemLib, | 803 | lib: link.SystemLib, |
| 805 | must_link: bool, | 804 | must_link: bool, |
| | 805 | is_dependent: bool, |
| 806 | dependent_libs: anytype, | 806 | dependent_libs: anytype, |
| 807 | ctx: *ParseErrorCtx, | 807 | ctx: *ParseErrorCtx, |
| 808 | ) ParseError!void { | 808 | ) ParseError!void { |
| ... | @@ -819,6 +819,7 @@ pub fn parseLibrary( | ... | @@ -819,6 +819,7 @@ pub fn parseLibrary( |
| 819 | try self.parseDylib(file, path, offset, dependent_libs, .{ | 819 | try self.parseDylib(file, path, offset, dependent_libs, .{ |
| 820 | .needed = lib.needed, | 820 | .needed = lib.needed, |
| 821 | .weak = lib.weak, | 821 | .weak = lib.weak, |
| | 822 | .dependent = is_dependent, |
| 822 | }, ctx); | 823 | }, ctx); |
| 823 | } else return error.UnknownFileType; | 824 | } else return error.UnknownFileType; |
| 824 | } else if (Archive.isArchive(file, 0)) { | 825 | } else if (Archive.isArchive(file, 0)) { |
| ... | @@ -827,11 +828,13 @@ pub fn parseLibrary( | ... | @@ -827,11 +828,13 @@ pub fn parseLibrary( |
| 827 | try self.parseDylib(file, path, 0, dependent_libs, .{ | 828 | try self.parseDylib(file, path, 0, dependent_libs, .{ |
| 828 | .needed = lib.needed, | 829 | .needed = lib.needed, |
| 829 | .weak = lib.weak, | 830 | .weak = lib.weak, |
| | 831 | .dependent = is_dependent, |
| 830 | }, ctx); | 832 | }, ctx); |
| 831 | } else { | 833 | } else { |
| 832 | self.parseLibStub(file, path, dependent_libs, .{ | 834 | self.parseLibStub(file, path, dependent_libs, .{ |
| 833 | .needed = lib.needed, | 835 | .needed = lib.needed, |
| 834 | .weak = lib.weak, | 836 | .weak = lib.weak, |
| | 837 | .dependent = is_dependent, |
| 835 | }, ctx) catch |err| switch (err) { | 838 | }, ctx) catch |err| switch (err) { |
| 836 | error.NotLibStub, error.UnexpectedToken => return error.UnknownFileType, | 839 | error.NotLibStub, error.UnexpectedToken => return error.UnknownFileType, |
| 837 | else => |e| return e, | 840 | else => |e| return e, |
| ... | @@ -853,9 +856,9 @@ pub fn parseFatLibrary( | ... | @@ -853,9 +856,9 @@ pub fn parseFatLibrary( |
| 853 | const offset = for (fat_archs) |arch| { | 856 | const offset = for (fat_archs) |arch| { |
| 854 | if (arch.tag == cpu_arch) break arch.offset; | 857 | if (arch.tag == cpu_arch) break arch.offset; |
| 855 | } else { | 858 | } else { |
| 856 | try ctx.detected_targets.ensureTotalCapacityPrecise(fat_archs.len); | 859 | try ctx.detected_targets.ensureUnusedCapacity(fat_archs.len); |
| 857 | for (fat_archs) |arch| { | 860 | for (fat_archs) |arch| { |
| 858 | ctx.detected_targets.appendAssumeCapacity(try ctx.arena.dupe(u8, @tagName(arch.tag))); | 861 | ctx.detected_targets.appendAssumeCapacity(try ctx.arena().dupe(u8, @tagName(arch.tag))); |
| 859 | } | 862 | } |
| 860 | return error.InvalidTargetFatLibrary; | 863 | return error.InvalidTargetFatLibrary; |
| 861 | }; | 864 | }; |
| ... | @@ -952,7 +955,7 @@ fn parseDylib( | ... | @@ -952,7 +955,7 @@ fn parseDylib( |
| 952 | const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null); | 955 | const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null); |
| 953 | defer gpa.free(contents); | 956 | defer gpa.free(contents); |
| 954 | | 957 | |
| 955 | var dylib = Dylib{ .weak = dylib_options.weak }; | 958 | var dylib = Dylib{ .path = try gpa.dupe(u8, path), .weak = dylib_options.weak }; |
| 956 | errdefer dylib.deinit(gpa); | 959 | errdefer dylib.deinit(gpa); |
| 957 | | 960 | |
| 958 | try dylib.parseFromBinary( | 961 | try dylib.parseFromBinary( |
| ... | @@ -976,7 +979,7 @@ fn parseDylib( | ... | @@ -976,7 +979,7 @@ fn parseDylib( |
| 976 | (detected_platform != null and !detected_platform.?.eqlTarget(this_platform))) | 979 | (detected_platform != null and !detected_platform.?.eqlTarget(this_platform))) |
| 977 | { | 980 | { |
| 978 | const platform = detected_platform orelse this_platform; | 981 | const platform = detected_platform orelse this_platform; |
| 979 | try ctx.detected_targets.append(try platform.allocPrintTarget(ctx.arena, detected_cpu_arch)); | 982 | try ctx.detected_targets.append(try platform.allocPrintTarget(ctx.arena(), detected_cpu_arch)); |
| 980 | return error.InvalidTarget; | 983 | return error.InvalidTarget; |
| 981 | } | 984 | } |
| 982 | | 985 | |
| ... | @@ -1014,13 +1017,13 @@ fn parseLibStub( | ... | @@ -1014,13 +1017,13 @@ fn parseLibStub( |
| 1014 | if (!matcher.matchesTarget(targets)) { | 1017 | if (!matcher.matchesTarget(targets)) { |
| 1015 | try ctx.detected_targets.ensureUnusedCapacity(targets.len); | 1018 | try ctx.detected_targets.ensureUnusedCapacity(targets.len); |
| 1016 | for (targets) |t| { | 1019 | for (targets) |t| { |
| 1017 | ctx.detected_targets.appendAssumeCapacity(try ctx.arena.dupe(u8, t)); | 1020 | ctx.detected_targets.appendAssumeCapacity(try ctx.arena().dupe(u8, t)); |
| 1018 | } | 1021 | } |
| 1019 | return error.InvalidTarget; | 1022 | return error.InvalidTarget; |
| 1020 | } | 1023 | } |
| 1021 | } | 1024 | } |
| 1022 | | 1025 | |
| 1023 | var dylib = Dylib{ .weak = dylib_options.weak }; | 1026 | var dylib = Dylib{ .path = try gpa.dupe(u8, path), .weak = dylib_options.weak }; |
| 1024 | errdefer dylib.deinit(gpa); | 1027 | errdefer dylib.deinit(gpa); |
| 1025 | | 1028 | |
| 1026 | try dylib.parseFromStub( | 1029 | try dylib.parseFromStub( |
| ... | @@ -1067,7 +1070,7 @@ fn addDylib(self: *MachO, dylib: Dylib, dylib_options: DylibOpts) ParseError!voi | ... | @@ -1067,7 +1070,7 @@ fn addDylib(self: *MachO, dylib: Dylib, dylib_options: DylibOpts) ParseError!voi |
| 1067 | } | 1070 | } |
| 1068 | } | 1071 | } |
| 1069 | | 1072 | |
| 1070 | pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, ctx: *ParseErrorCtx) ParseError!void { | 1073 | pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { |
| 1071 | const tracy = trace(@src()); | 1074 | const tracy = trace(@src()); |
| 1072 | defer tracy.end(); | 1075 | defer tracy.end(); |
| 1073 | | 1076 | |
| ... | @@ -1081,12 +1084,13 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, ctx: *ParseErro | ... | @@ -1081,12 +1084,13 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, ctx: *ParseErro |
| 1081 | const arena = arena_alloc.allocator(); | 1084 | const arena = arena_alloc.allocator(); |
| 1082 | defer arena_alloc.deinit(); | 1085 | defer arena_alloc.deinit(); |
| 1083 | | 1086 | |
| 1084 | outer: while (dependent_libs.readItem()) |dep_id| { | 1087 | while (dependent_libs.readItem()) |dep_id| { |
| 1085 | defer dep_id.id.deinit(gpa); | 1088 | defer dep_id.id.deinit(gpa); |
| 1086 | | 1089 | |
| 1087 | if (self.dylibs_map.contains(dep_id.id.name)) continue; | 1090 | if (self.dylibs_map.contains(dep_id.id.name)) continue; |
| 1088 | | 1091 | |
| 1089 | const weak = self.dylibs.items[dep_id.parent].weak; | 1092 | const parent = &self.dylibs.items[dep_id.parent]; |
| | 1093 | const weak = parent.weak; |
| 1090 | const has_ext = blk: { | 1094 | const has_ext = blk: { |
| 1091 | const basename = fs.path.basename(dep_id.id.name); | 1095 | const basename = fs.path.basename(dep_id.id.name); |
| 1092 | break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; | 1096 | break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; |
| ... | @@ -1097,46 +1101,50 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, ctx: *ParseErro | ... | @@ -1097,46 +1101,50 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, ctx: *ParseErro |
| 1097 | break :blk dep_id.id.name[0..index]; | 1101 | break :blk dep_id.id.name[0..index]; |
| 1098 | } else dep_id.id.name; | 1102 | } else dep_id.id.name; |
| 1099 | | 1103 | |
| 1100 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { | 1104 | const maybe_full_path = full_path: { |
| 1101 | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext }); | 1105 | if (self.base.options.sysroot) |root| { |
| 1102 | const full_path = if (self.base.options.sysroot) |root| | 1106 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { |
| 1103 | try fs.path.join(arena, &.{ root, with_ext }) | 1107 | if (try resolveLib(arena, root, without_ext, ext)) |full_path| break :full_path full_path; |
| 1104 | else | 1108 | } |
| 1105 | with_ext; | 1109 | } |
| 1106 | | 1110 | |
| 1107 | const file = std.fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { | 1111 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { |
| 1108 | error.FileNotFound => continue, | 1112 | if (try resolveLib(arena, "", without_ext, ext)) |full_path| break :full_path full_path; |
| 1109 | else => |e| return e, | 1113 | } |
| 1110 | }; | 1114 | |
| 1111 | defer file.close(); | 1115 | break :full_path null; |
| | 1116 | }; |
| | 1117 | |
| | 1118 | const full_path = maybe_full_path orelse { |
| | 1119 | try self.misc_errors.ensureUnusedCapacity(gpa, 1); |
| | 1120 | var notes = try gpa.alloc(File.ErrorMsg, 1); |
| | 1121 | errdefer gpa.free(notes); |
| | 1122 | const parent_name = if (parent.id) |id| id.name else parent.path; |
| | 1123 | notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "a dependency of {s}", .{parent_name}) }; |
| | 1124 | self.misc_errors.appendAssumeCapacity(.{ |
| | 1125 | .msg = try std.fmt.allocPrint(gpa, "missing dynamic library dependency: '{s}'", .{dep_id.id.name}), |
| | 1126 | .notes = notes, |
| | 1127 | }); |
| | 1128 | continue; |
| | 1129 | }; |
| 1112 | | 1130 | |
| 1113 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); | 1131 | const file = try std.fs.cwd().openFile(full_path, .{}); |
| | 1132 | defer file.close(); |
| 1114 | | 1133 | |
| 1115 | const offset: u64 = if (fat.isFatLibrary(file)) blk: { | 1134 | log.debug("parsing dependency {s} at fully resolved path {s}", .{ dep_id.id.name, full_path }); |
| 1116 | const offset = try self.parseFatLibrary(file, self.base.options.target.cpu.arch, ctx); | | |
| 1117 | try file.seekTo(offset); | | |
| 1118 | break :blk offset; | | |
| 1119 | } else 0; | | |
| 1120 | | 1135 | |
| 1121 | if (Dylib.isDylib(file, offset)) { | 1136 | var parse_ctx = ParseErrorCtx.init(gpa); |
| 1122 | try self.parseDylib(file, full_path, offset, dependent_libs, .{ | 1137 | defer parse_ctx.deinit(); |
| 1123 | .dependent = true, | 1138 | |
| 1124 | .weak = weak, | 1139 | self.parseLibrary(file, full_path, .{ |
| 1125 | }, ctx); | 1140 | .path = null, |
| 1126 | } else { | 1141 | .needed = false, |
| 1127 | self.parseLibStub(file, full_path, dependent_libs, .{ | 1142 | .weak = weak, |
| 1128 | .dependent = true, | 1143 | }, false, true, dependent_libs, &parse_ctx) catch |err| |
| 1129 | .weak = weak, | 1144 | try self.handleAndReportParseError(full_path, err, &parse_ctx); |
| 1130 | }, ctx) catch |err| switch (err) { | | |
| 1131 | error.NotLibStub, error.UnexpectedToken => continue, | | |
| 1132 | else => |e| return e, | | |
| 1133 | }; | | |
| 1134 | } | | |
| 1135 | continue :outer; | | |
| 1136 | } | | |
| 1137 | | 1145 | |
| 1138 | // TODO convert into an error | 1146 | // TODO I think that it would be nice to rewrite this error to include metadata for failed dependency |
| 1139 | log.err("{s}: unable to resolve dependency", .{dep_id.id.name}); | 1147 | // in addition to parsing error |
| 1140 | } | 1148 | } |
| 1141 | } | 1149 | } |
| 1142 | | 1150 | |
| ... | @@ -4854,11 +4862,23 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 { | ... | @@ -4854,11 +4862,23 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 { |
| 4854 | } | 4862 | } |
| 4855 | | 4863 | |
| 4856 | pub const ParseErrorCtx = struct { | 4864 | pub const ParseErrorCtx = struct { |
| 4857 | arena: Allocator, | 4865 | arena_allocator: std.heap.ArenaAllocator, |
| 4858 | detected_targets: std.ArrayList([]const u8), | 4866 | detected_targets: std.ArrayList([]const u8), |
| 4859 | | 4867 | |
| 4860 | pub fn init(arena: Allocator) ParseErrorCtx { | 4868 | pub fn init(gpa: Allocator) ParseErrorCtx { |
| 4861 | return .{ .arena = arena, .detected_targets = std.ArrayList([]const u8).init(arena) }; | 4869 | return .{ |
| | 4870 | .arena_allocator = std.heap.ArenaAllocator.init(gpa), |
| | 4871 | .detected_targets = std.ArrayList([]const u8).init(gpa), |
| | 4872 | }; |
| | 4873 | } |
| | 4874 | |
| | 4875 | pub fn deinit(ctx: *ParseErrorCtx) void { |
| | 4876 | ctx.arena_allocator.deinit(); |
| | 4877 | ctx.detected_targets.deinit(); |
| | 4878 | } |
| | 4879 | |
| | 4880 | pub fn arena(ctx: *ParseErrorCtx) Allocator { |
| | 4881 | return ctx.arena_allocator.allocator(); |
| 4862 | } | 4882 | } |
| 4863 | }; | 4883 | }; |
| 4864 | | 4884 | |
| ... | @@ -4890,7 +4910,7 @@ pub fn handleAndReportParseError( | ... | @@ -4890,7 +4910,7 @@ pub fn handleAndReportParseError( |
| 4890 | ), | 4910 | ), |
| 4891 | error.InvalidTargetFatLibrary => try self.reportParseError( | 4911 | error.InvalidTargetFatLibrary => try self.reportParseError( |
| 4892 | path, | 4912 | path, |
| 4893 | "invalid architecture in univeral library: expected '{s}', but found '{s}'", | 4913 | "invalid architecture in universal library: expected '{s}', but found '{s}'", |
| 4894 | .{ @tagName(cpu_arch), targets_string.items }, | 4914 | .{ @tagName(cpu_arch), targets_string.items }, |
| 4895 | ), | 4915 | ), |
| 4896 | else => unreachable, | 4916 | else => unreachable, |