| ... | ... | @@ -399,10 +399,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 399 | 399 | self.dylibs_map.clearRetainingCapacity(); |
| 400 | 400 | self.referenced_dylibs.clearRetainingCapacity(); |
| 401 | 401 | |
| 402 | | var dependent_libs = std.fifo.LinearFifo(struct { |
| 403 | | id: Dylib.Id, |
| 404 | | parent: u16, |
| 405 | | }, .Dynamic).init(arena); |
| 402 | var dependent_libs = std.fifo.LinearFifo(DylibReExportInfo, .Dynamic).init(arena); |
| 406 | 403 | |
| 407 | 404 | for (libs.keys(), libs.values()) |path, lib| { |
| 408 | 405 | const in_file = try std.fs.cwd().openFile(path, .{}); |
| ... | ... | @@ -417,6 +414,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 417 | 414 | lib, |
| 418 | 415 | false, |
| 419 | 416 | false, |
| 417 | null, |
| 420 | 418 | &dependent_libs, |
| 421 | 419 | &parse_ctx, |
| 422 | 420 | ) catch |err| try self.handleAndReportParseError(path, err, &parse_ctx); |
| ... | ... | @@ -749,7 +747,7 @@ pub fn parsePositional( |
| 749 | 747 | .path = null, |
| 750 | 748 | .needed = false, |
| 751 | 749 | .weak = false, |
| 752 | | }, must_link, false, dependent_libs, ctx); |
| 750 | }, must_link, false, null, dependent_libs, ctx); |
| 753 | 751 | } |
| 754 | 752 | } |
| 755 | 753 | |
| ... | ... | @@ -806,6 +804,7 @@ pub fn parseLibrary( |
| 806 | 804 | lib: link.SystemLib, |
| 807 | 805 | must_link: bool, |
| 808 | 806 | is_dependent: bool, |
| 807 | reexport_info: ?DylibReExportInfo, |
| 809 | 808 | dependent_libs: anytype, |
| 810 | 809 | ctx: *ParseErrorCtx, |
| 811 | 810 | ) ParseError!void { |
| ... | ... | @@ -823,6 +822,7 @@ pub fn parseLibrary( |
| 823 | 822 | .needed = lib.needed, |
| 824 | 823 | .weak = lib.weak, |
| 825 | 824 | .dependent = is_dependent, |
| 825 | .reexport_info = reexport_info, |
| 826 | 826 | }, ctx); |
| 827 | 827 | } else return error.UnknownFileType; |
| 828 | 828 | } else if (Archive.isArchive(file, 0)) { |
| ... | ... | @@ -832,12 +832,14 @@ pub fn parseLibrary( |
| 832 | 832 | .needed = lib.needed, |
| 833 | 833 | .weak = lib.weak, |
| 834 | 834 | .dependent = is_dependent, |
| 835 | .reexport_info = reexport_info, |
| 835 | 836 | }, ctx); |
| 836 | 837 | } else { |
| 837 | 838 | self.parseLibStub(file, path, dependent_libs, .{ |
| 838 | 839 | .needed = lib.needed, |
| 839 | 840 | .weak = lib.weak, |
| 840 | 841 | .dependent = is_dependent, |
| 842 | .reexport_info = reexport_info, |
| 841 | 843 | }, ctx) catch |err| switch (err) { |
| 842 | 844 | error.NotLibStub, error.UnexpectedToken => return error.UnknownFileType, |
| 843 | 845 | else => |e| return e, |
| ... | ... | @@ -935,8 +937,13 @@ fn parseArchive( |
| 935 | 937 | } |
| 936 | 938 | } |
| 937 | 939 | |
| 940 | pub const DylibReExportInfo = struct { |
| 941 | id: Dylib.Id, |
| 942 | parent: u16, |
| 943 | }; |
| 944 | |
| 938 | 945 | const DylibOpts = struct { |
| 939 | | id: ?Dylib.Id = null, |
| 946 | reexport_info: ?DylibReExportInfo = null, |
| 940 | 947 | dependent: bool = false, |
| 941 | 948 | needed: bool = false, |
| 942 | 949 | weak: bool = false, |
| ... | ... | @@ -986,10 +993,7 @@ fn parseDylib( |
| 986 | 993 | return error.InvalidTarget; |
| 987 | 994 | } |
| 988 | 995 | |
| 989 | | try self.addDylib(dylib, .{ |
| 990 | | .needed = dylib_options.needed, |
| 991 | | .weak = dylib_options.weak, |
| 992 | | }); |
| 996 | try self.addDylib(dylib, dylib_options, ctx); |
| 993 | 997 | } |
| 994 | 998 | |
| 995 | 999 | fn parseLibStub( |
| ... | ... | @@ -1038,20 +1042,17 @@ fn parseLibStub( |
| 1038 | 1042 | path, |
| 1039 | 1043 | ); |
| 1040 | 1044 | |
| 1041 | | try self.addDylib(dylib, .{ |
| 1042 | | .needed = dylib_options.needed, |
| 1043 | | .weak = dylib_options.weak, |
| 1044 | | }); |
| 1045 | try self.addDylib(dylib, dylib_options, ctx); |
| 1045 | 1046 | } |
| 1046 | 1047 | |
| 1047 | | fn addDylib(self: *MachO, dylib: Dylib, dylib_options: DylibOpts) ParseError!void { |
| 1048 | | if (dylib_options.id) |id| { |
| 1049 | | if (dylib.id.?.current_version < id.compatibility_version) { |
| 1050 | | // TODO convert into an error |
| 1051 | | log.warn("found dylib is incompatible with the required minimum version", .{}); |
| 1052 | | log.warn(" dylib: {s}", .{id.name}); |
| 1053 | | log.warn(" required minimum version: {}", .{id.compatibility_version}); |
| 1054 | | log.warn(" dylib version: {}", .{dylib.id.?.current_version}); |
| 1048 | fn addDylib(self: *MachO, dylib: Dylib, dylib_options: DylibOpts, ctx: *ParseErrorCtx) ParseError!void { |
| 1049 | if (dylib_options.reexport_info) |reexport_info| { |
| 1050 | if (dylib.id.?.current_version < reexport_info.id.compatibility_version) { |
| 1051 | ctx.detected_dylib_id = .{ |
| 1052 | .parent = reexport_info.parent, |
| 1053 | .required_version = reexport_info.id.compatibility_version, |
| 1054 | .found_version = dylib.id.?.current_version, |
| 1055 | }; |
| 1055 | 1056 | return error.IncompatibleDylibVersion; |
| 1056 | 1057 | } |
| 1057 | 1058 | } |
| ... | ... | @@ -1119,14 +1120,9 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { |
| 1119 | 1120 | }; |
| 1120 | 1121 | |
| 1121 | 1122 | const full_path = maybe_full_path orelse { |
| 1122 | | try self.misc_errors.ensureUnusedCapacity(gpa, 1); |
| 1123 | | var notes = try gpa.alloc(File.ErrorMsg, 1); |
| 1124 | | errdefer gpa.free(notes); |
| 1125 | 1123 | const parent_name = if (parent.id) |id| id.name else parent.path; |
| 1126 | | notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "a dependency of {s}", .{parent_name}) }; |
| 1127 | | self.misc_errors.appendAssumeCapacity(.{ |
| 1128 | | .msg = try std.fmt.allocPrint(gpa, "missing dynamic library dependency: '{s}'", .{dep_id.id.name}), |
| 1129 | | .notes = notes, |
| 1124 | try self.reportDependencyError(parent_name, null, "missing dynamic library dependency: '{s}'", .{ |
| 1125 | dep_id.id.name, |
| 1130 | 1126 | }); |
| 1131 | 1127 | continue; |
| 1132 | 1128 | }; |
| ... | ... | @@ -1143,7 +1139,7 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { |
| 1143 | 1139 | .path = null, |
| 1144 | 1140 | .needed = false, |
| 1145 | 1141 | .weak = weak, |
| 1146 | | }, false, true, dependent_libs, &parse_ctx) catch |err| |
| 1142 | }, false, true, dep_id, dependent_libs, &parse_ctx) catch |err| |
| 1147 | 1143 | try self.handleAndReportParseError(full_path, err, &parse_ctx); |
| 1148 | 1144 | |
| 1149 | 1145 | // TODO I think that it would be nice to rewrite this error to include metadata for failed dependency |
| ... | ... | @@ -4498,7 +4494,7 @@ pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { |
| 4498 | 4494 | header.cputype = macho.CPU_TYPE_X86_64; |
| 4499 | 4495 | header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL; |
| 4500 | 4496 | }, |
| 4501 | | else => return error.UnsupportedCpuArchitecture, |
| 4497 | else => unreachable, |
| 4502 | 4498 | } |
| 4503 | 4499 | |
| 4504 | 4500 | switch (self.base.options.output_mode) { |
| ... | ... | @@ -4866,11 +4862,17 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 { |
| 4866 | 4862 | |
| 4867 | 4863 | pub const ParseErrorCtx = struct { |
| 4868 | 4864 | arena_allocator: std.heap.ArenaAllocator, |
| 4865 | detected_dylib_id: struct { |
| 4866 | parent: u16, |
| 4867 | required_version: u32, |
| 4868 | found_version: u32, |
| 4869 | }, |
| 4869 | 4870 | detected_targets: std.ArrayList([]const u8), |
| 4870 | 4871 | |
| 4871 | 4872 | pub fn init(gpa: Allocator) ParseErrorCtx { |
| 4872 | 4873 | return .{ |
| 4873 | 4874 | .arena_allocator = std.heap.ArenaAllocator.init(gpa), |
| 4875 | .detected_dylib_id = undefined, |
| 4874 | 4876 | .detected_targets = std.ArrayList([]const u8).init(gpa), |
| 4875 | 4877 | }; |
| 4876 | 4878 | } |
| ... | ... | @@ -4894,6 +4896,18 @@ pub fn handleAndReportParseError( |
| 4894 | 4896 | const cpu_arch = self.base.options.target.cpu.arch; |
| 4895 | 4897 | switch (err) { |
| 4896 | 4898 | error.DylibAlreadyExists => {}, |
| 4899 | error.IncompatibleDylibVersion => { |
| 4900 | const parent = &self.dylibs.items[ctx.detected_dylib_id.parent]; |
| 4901 | try self.reportDependencyError( |
| 4902 | if (parent.id) |id| id.name else parent.path, |
| 4903 | path, |
| 4904 | "incompatible dylib version: expected at least '{}', but found '{}'", |
| 4905 | .{ |
| 4906 | load_commands.appleVersionToSemanticVersion(ctx.detected_dylib_id.required_version), |
| 4907 | load_commands.appleVersionToSemanticVersion(ctx.detected_dylib_id.found_version), |
| 4908 | }, |
| 4909 | ); |
| 4910 | }, |
| 4897 | 4911 | error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}), |
| 4898 | 4912 | error.InvalidTarget, error.InvalidTargetFatLibrary => { |
| 4899 | 4913 | var targets_string = std.ArrayList(u8).init(self.base.allocator); |
| ... | ... | @@ -4923,7 +4937,28 @@ pub fn handleAndReportParseError( |
| 4923 | 4937 | } |
| 4924 | 4938 | } |
| 4925 | 4939 | |
| 4926 | | pub fn reportParseError(self: *MachO, path: []const u8, comptime format: []const u8, args: anytype) !void { |
| 4940 | fn reportDependencyError( |
| 4941 | self: *MachO, |
| 4942 | parent: []const u8, |
| 4943 | path: ?[]const u8, |
| 4944 | comptime format: []const u8, |
| 4945 | args: anytype, |
| 4946 | ) !void { |
| 4947 | const gpa = self.base.allocator; |
| 4948 | try self.misc_errors.ensureUnusedCapacity(gpa, 1); |
| 4949 | var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2); |
| 4950 | defer notes.deinit(); |
| 4951 | if (path) |p| { |
| 4952 | notes.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, "while parsing {s}", .{p}) }); |
| 4953 | } |
| 4954 | notes.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, "a dependency of {s}", .{parent}) }); |
| 4955 | self.misc_errors.appendAssumeCapacity(.{ |
| 4956 | .msg = try std.fmt.allocPrint(gpa, format, args), |
| 4957 | .notes = try notes.toOwnedSlice(), |
| 4958 | }); |
| 4959 | } |
| 4960 | |
| 4961 | fn reportParseError(self: *MachO, path: []const u8, comptime format: []const u8, args: anytype) !void { |
| 4927 | 4962 | const gpa = self.base.allocator; |
| 4928 | 4963 | try self.misc_errors.ensureUnusedCapacity(gpa, 1); |
| 4929 | 4964 | var notes = try gpa.alloc(File.ErrorMsg, 1); |