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