authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-30 19:02:25+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-30 19:02:25+02:00
log5806e761bb676cdd537308f6c4a197e42228416d
treef7d0c2d7cc95695dfb07113a8e5bcba452daf595
parent22c81740ef611fe6e3b7ac2390fa9cf058f0ac6b

macho: improve error reporting for re-exports mismatch


4 files changed, 71 insertions(+), 40 deletions(-)

src/link.zig-2
......@@ -734,8 +734,6 @@ pub const File = struct {
734734 MissingEndForBody,
735735 MissingEndForExpression,
736736 /// TODO: this should be removed from the error set in favor of using ErrorFlags
737 MissingMainEntrypoint,
738 /// TODO: this should be removed from the error set in favor of using ErrorFlags
739737 MissingSection,
740738 MissingSymbol,
741739 MissingTableSymbols,
src/link/MachO.zig+67-32
......@@ -399,10 +399,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
399399 self.dylibs_map.clearRetainingCapacity();
400400 self.referenced_dylibs.clearRetainingCapacity();
401401
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);
406403
407404 for (libs.keys(), libs.values()) |path, lib| {
408405 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
417414 lib,
418415 false,
419416 false,
417 null,
420418 &dependent_libs,
421419 &parse_ctx,
422420 ) catch |err| try self.handleAndReportParseError(path, err, &parse_ctx);
......@@ -749,7 +747,7 @@ pub fn parsePositional(
749747 .path = null,
750748 .needed = false,
751749 .weak = false,
752 }, must_link, false, dependent_libs, ctx);
750 }, must_link, false, null, dependent_libs, ctx);
753751 }
754752}
755753
......@@ -806,6 +804,7 @@ pub fn parseLibrary(
806804 lib: link.SystemLib,
807805 must_link: bool,
808806 is_dependent: bool,
807 reexport_info: ?DylibReExportInfo,
809808 dependent_libs: anytype,
810809 ctx: *ParseErrorCtx,
811810) ParseError!void {
......@@ -823,6 +822,7 @@ pub fn parseLibrary(
823822 .needed = lib.needed,
824823 .weak = lib.weak,
825824 .dependent = is_dependent,
825 .reexport_info = reexport_info,
826826 }, ctx);
827827 } else return error.UnknownFileType;
828828 } else if (Archive.isArchive(file, 0)) {
......@@ -832,12 +832,14 @@ pub fn parseLibrary(
832832 .needed = lib.needed,
833833 .weak = lib.weak,
834834 .dependent = is_dependent,
835 .reexport_info = reexport_info,
835836 }, ctx);
836837 } else {
837838 self.parseLibStub(file, path, dependent_libs, .{
838839 .needed = lib.needed,
839840 .weak = lib.weak,
840841 .dependent = is_dependent,
842 .reexport_info = reexport_info,
841843 }, ctx) catch |err| switch (err) {
842844 error.NotLibStub, error.UnexpectedToken => return error.UnknownFileType,
843845 else => |e| return e,
......@@ -935,8 +937,13 @@ fn parseArchive(
935937 }
936938}
937939
940pub const DylibReExportInfo = struct {
941 id: Dylib.Id,
942 parent: u16,
943};
944
938945const DylibOpts = struct {
939 id: ?Dylib.Id = null,
946 reexport_info: ?DylibReExportInfo = null,
940947 dependent: bool = false,
941948 needed: bool = false,
942949 weak: bool = false,
......@@ -986,10 +993,7 @@ fn parseDylib(
986993 return error.InvalidTarget;
987994 }
988995
989 try self.addDylib(dylib, .{
990 .needed = dylib_options.needed,
991 .weak = dylib_options.weak,
992 });
996 try self.addDylib(dylib, dylib_options, ctx);
993997}
994998
995999fn parseLibStub(
......@@ -1038,20 +1042,17 @@ fn parseLibStub(
10381042 path,
10391043 );
10401044
1041 try self.addDylib(dylib, .{
1042 .needed = dylib_options.needed,
1043 .weak = dylib_options.weak,
1044 });
1045 try self.addDylib(dylib, dylib_options, ctx);
10451046}
10461047
1047fn 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});
1048fn 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 };
10551056 return error.IncompatibleDylibVersion;
10561057 }
10571058 }
......@@ -1119,14 +1120,9 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void {
11191120 };
11201121
11211122 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);
11251123 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,
11301126 });
11311127 continue;
11321128 };
......@@ -1143,7 +1139,7 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void {
11431139 .path = null,
11441140 .needed = false,
11451141 .weak = weak,
1146 }, false, true, dependent_libs, &parse_ctx) catch |err|
1142 }, false, true, dep_id, dependent_libs, &parse_ctx) catch |err|
11471143 try self.handleAndReportParseError(full_path, err, &parse_ctx);
11481144
11491145 // 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 {
44984494 header.cputype = macho.CPU_TYPE_X86_64;
44994495 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
45004496 },
4501 else => return error.UnsupportedCpuArchitecture,
4497 else => unreachable,
45024498 }
45034499
45044500 switch (self.base.options.output_mode) {
......@@ -4866,11 +4862,17 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 {
48664862
48674863pub const ParseErrorCtx = struct {
48684864 arena_allocator: std.heap.ArenaAllocator,
4865 detected_dylib_id: struct {
4866 parent: u16,
4867 required_version: u32,
4868 found_version: u32,
4869 },
48694870 detected_targets: std.ArrayList([]const u8),
48704871
48714872 pub fn init(gpa: Allocator) ParseErrorCtx {
48724873 return .{
48734874 .arena_allocator = std.heap.ArenaAllocator.init(gpa),
4875 .detected_dylib_id = undefined,
48744876 .detected_targets = std.ArrayList([]const u8).init(gpa),
48754877 };
48764878 }
......@@ -4894,6 +4896,18 @@ pub fn handleAndReportParseError(
48944896 const cpu_arch = self.base.options.target.cpu.arch;
48954897 switch (err) {
48964898 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 },
48974911 error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}),
48984912 error.InvalidTarget, error.InvalidTargetFatLibrary => {
48994913 var targets_string = std.ArrayList(u8).init(self.base.allocator);
......@@ -4923,7 +4937,28 @@ pub fn handleAndReportParseError(
49234937 }
49244938}
49254939
4926pub fn reportParseError(self: *MachO, path: []const u8, comptime format: []const u8, args: anytype) !void {
4940fn 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
4961fn reportParseError(self: *MachO, path: []const u8, comptime format: []const u8, args: anytype) !void {
49274962 const gpa = self.base.allocator;
49284963 try self.misc_errors.ensureUnusedCapacity(gpa, 1);
49294964 var notes = try gpa.alloc(File.ErrorMsg, 1);
src/link/MachO/load_commands.zig+2-2
......@@ -440,14 +440,14 @@ const supported_platforms = [_]SupportedPlatforms{
440440};
441441// zig fmt: on
442442
443pub inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 {
443inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 {
444444 const major = version.major;
445445 const minor = version.minor;
446446 const patch = version.patch;
447447 return (@as(u32, @intCast(major)) << 16) | (@as(u32, @intCast(minor)) << 8) | @as(u32, @intCast(patch));
448448}
449449
450inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion {
450pub inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion {
451451 return .{
452452 .major = @as(u16, @truncate(version >> 16)),
453453 .minor = @as(u8, @truncate(version >> 8)),
src/link/MachO/zld.zig+2-4
......@@ -340,10 +340,7 @@ pub fn linkWithZld(
340340 Compilation.dump_argv(argv.items);
341341 }
342342
343 var dependent_libs = std.fifo.LinearFifo(struct {
344 id: Dylib.Id,
345 parent: u16,
346 }, .Dynamic).init(arena);
343 var dependent_libs = std.fifo.LinearFifo(MachO.DylibReExportInfo, .Dynamic).init(arena);
347344
348345 for (positionals.items) |obj| {
349346 const in_file = try std.fs.cwd().openFile(obj.path, .{});
......@@ -374,6 +371,7 @@ pub fn linkWithZld(
374371 lib,
375372 false,
376373 false,
374 null,
377375 &dependent_libs,
378376 &parse_ctx,
379377 ) catch |err| try macho_file.handleAndReportParseError(path, err, &parse_ctx);