From 5ee2065ebfb87f705e4bf35e39aefa05bb3c7d05 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 25 Oct 2024 10:02:40 +0200 Subject: [PATCH 1/2] macho: handle -execute and -macos_version_min flags --- src/MachO/Options.zig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/MachO/Options.zig b/src/MachO/Options.zig index 30590b49..10d38c1b 100644 --- a/src/MachO/Options.zig +++ b/src/MachO/Options.zig @@ -34,6 +34,7 @@ const usage = \\-dylib Create dynamic library \\-dynamic Perform dynamic linking \\-e [name] Specifies the entry point of main executable + \\-execute Create an executable (default) \\-exported_symbol [name] Marks symbol [name] as global \\-exported_symbols_list [filename] Specifies which symbols are to be marked as global \\--entitlements Add path to entitlements file for embedding in code signature @@ -53,6 +54,7 @@ const usage = \\ -dylib_install_name \\-l[name] Link against library \\-L[path] Add search path for libraries + \\-macos_version_min [version] Set oldest macOS version that the output can be used on. \\-needed_framework [name] Link against framework (even if unused) \\-needed-l[name] Link against library (even if unused) \\ -needed_library [name] @@ -240,6 +242,8 @@ pub fn parse(arena: Allocator, args: []const []const u8, ctx: anytype) !Options error.FileNotFound => ctx.fatal("Specified file in -exported_symbols_list {s} does not exist\n", .{filename}), else => |e| return e, }; + } else if (p.flag1("execute")) { + opts.dylib = false; } else if (p.arg1("e")) |name| { opts.entry = name; } else if (p.arg1("undefined")) |treatment| { @@ -276,6 +280,10 @@ pub fn parse(arena: Allocator, args: []const []const u8, ctx: anytype) !Options } else { ctx.fatal("Could not parse CPU architecture from '{s}'\n", .{value}); } + } else if (p.arg1("macos_version_min")) |ver| { + const min_ver = Version.parse(ver) orelse + ctx.fatal("Unable to parse version from '{s}'\n", .{version}); + opts.platform = .{ .platform = .MACOS, .version = min_ver }; } else if (p.arg1("platform_version")) |platform_s| { // TODO clunky! const min_v = it.next() orelse From 13cc52470b805bbb33390c49aad0b9e057baeee8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 25 Oct 2024 10:03:00 +0200 Subject: [PATCH 2/2] ld: fix data race in reporting errors and warnings --- src/Coff.zig | 2 ++ src/Elf.zig | 7 +++++-- src/Ld.zig | 25 +++++++++++++++++++++---- src/MachO.zig | 7 ++++++- src/MachO/Dylib.zig | 1 + src/MachO/Object.zig | 1 + src/main.zig | 41 +++++++++++++++++++++-------------------- 7 files changed, 57 insertions(+), 27 deletions(-) diff --git a/src/Coff.zig b/src/Coff.zig index 17dd2dff..d38a8512 100644 --- a/src/Coff.zig +++ b/src/Coff.zig @@ -660,6 +660,7 @@ fn reportUndefs(self: *Coff) !void { const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes); const err = try self.base.addErrorWithNotes(nnotes); + defer err.unlock(); try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)}); has_undefs = true; @@ -681,6 +682,7 @@ fn reportUndefs(self: *Coff) !void { if (sym.getFile(self)) |_| continue; has_undefs = true; const err = try self.base.addErrorWithNotes(1); + defer err.unlock(); try err.addMsg("undefined symbol: {s}", .{sym.getName(self)}); try err.addNote("/force command line option", .{}); } diff --git a/src/Elf.zig b/src/Elf.zig index e7adf9bc..db402046 100644 --- a/src/Elf.zig +++ b/src/Elf.zig @@ -289,6 +289,7 @@ pub fn flush(self: *Elf) !void { if (has_parse_error) { const err = try self.base.addErrorWithNotes(search_dirs.items.len); + defer err.unlock(); try err.addMsg("library search paths", .{}); for (search_dirs.items) |dir| { try err.addNote(" {s}", .{dir}); @@ -1901,7 +1902,8 @@ fn reportDuplicates(self: *Elf) error{ HasDuplicates, OutOfMemory }!void { const sym = self.resolver.keys.items[key - 1]; const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes); - var err = try self.base.addErrorWithNotes(nnotes + 1); + const err = try self.base.addErrorWithNotes(nnotes + 1); + defer err.unlock(); try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)}); try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()}); @@ -1930,7 +1932,8 @@ fn reportUndefs(self: *Elf) !void { const nrefs = @min(refs.items.len, max_notes); const nnotes = nrefs + @intFromBool(refs.items.len > max_notes); - var err = try self.base.addErrorWithNotes(nnotes); + const err = try self.base.addErrorWithNotes(nnotes); + defer err.unlock(); try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)}); for (refs.items[0..nrefs]) |ref| { diff --git a/src/Ld.zig b/src/Ld.zig index f05dd182..a444619b 100644 --- a/src/Ld.zig +++ b/src/Ld.zig @@ -254,11 +254,13 @@ pub fn flush(base: *Ld) !void { pub fn warn(base: *Ld, comptime format: []const u8, args: anytype) void { const warning = base.addWarningWithNotes(0) catch return; + defer warning.unlock(); warning.addMsg(format, args) catch return; } pub fn fatal(base: *Ld, comptime format: []const u8, args: anytype) void { const err = base.addErrorWithNotes(0) catch return; + defer err.unlock(); err.addMsg(format, args) catch return; } @@ -266,6 +268,7 @@ pub const ErrorWithNotes = struct { err_index: usize, allocator: Allocator, errors: []ErrorMsg, + lock: *std.Thread.Mutex, pub fn addMsg(err: ErrorWithNotes, comptime format: []const u8, args: anytype) !void { const err_msg = err.getErrorMsg(); @@ -283,26 +286,40 @@ pub const ErrorWithNotes = struct { assert(err.err_index < err.errors.len); return &err.errors[err.err_index]; } + + pub fn unlock(err: ErrorWithNotes) void { + err.lock.unlock(); + } }; pub fn addErrorWithNotes(base: *Ld, note_count: usize) !ErrorWithNotes { base.errors_mutex.lock(); - defer base.errors_mutex.unlock(); + errdefer base.errors_mutex.unlock(); const err_index = base.errors.items.len; const err_msg = try base.errors.addOne(base.allocator); err_msg.* = .{ .msg = undefined }; try err_msg.notes.ensureTotalCapacityPrecise(base.allocator, note_count); - return .{ .err_index = err_index, .allocator = base.allocator, .errors = base.errors.items }; + return .{ + .err_index = err_index, + .allocator = base.allocator, + .errors = base.errors.items, + .lock = &base.errors_mutex, + }; } pub fn addWarningWithNotes(base: *Ld, note_count: usize) !ErrorWithNotes { base.warnings_mutex.lock(); - defer base.warnings_mutex.unlock(); + errdefer base.warnings_mutex.unlock(); const err_index = base.warnings.items.len; const err_msg = try base.warnings.addOne(base.allocator); err_msg.* = .{ .msg = undefined }; try err_msg.notes.ensureTotalCapacityPrecise(base.allocator, note_count); - return .{ .err_index = err_index, .allocator = base.allocator, .errors = base.warnings.items }; + return .{ + .err_index = err_index, + .allocator = base.allocator, + .errors = base.warnings.items, + .lock = &base.warnings_mutex, + }; } pub fn getAllWarningsAlloc(base: *Ld) !ErrorBundle { diff --git a/src/MachO.zig b/src/MachO.zig index f34d11fe..d16c602b 100644 --- a/src/MachO.zig +++ b/src/MachO.zig @@ -468,6 +468,7 @@ fn resolvePaths( .lib => { const full_path = (try self.resolveLib(arena, lib_dirs, obj.path)) orelse { const err = try self.base.addErrorWithNotes(lib_dirs.len); + defer err.unlock(); try err.addMsg("library not found for {}", .{obj}); for (lib_dirs) |dir| try err.addNote("tried {s}", .{dir}); has_resolve_error = true; @@ -478,6 +479,7 @@ fn resolvePaths( .framework => { const full_path = (try self.resolveFramework(arena, framework_dirs, obj.path)) orelse { const err = try self.base.addErrorWithNotes(framework_dirs.len); + defer err.unlock(); try err.addMsg("framework not found for {}", .{obj}); for (framework_dirs) |dir| try err.addNote("tried {s}", .{dir}); has_resolve_error = true; @@ -642,6 +644,7 @@ fn parseFatFile(self: *MachO, obj: LinkObject, file: std.fs.File) !?fat.Arch { return error.MissingArch; } else { const err = try self.base.addErrorWithNotes(1 + fat_archs.len); + defer err.unlock(); try err.addMsg("{s}: ignoring universal file as no architecture specified", .{obj.path}); for (fat_archs) |arch| { try err.addNote("universal file built for {s}", .{@tagName(arch.tag)}); @@ -1207,7 +1210,8 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void { const notes = self.dupes.get(key).?; const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes); - var err = try self.base.addErrorWithNotes(nnotes + 1); + const err = try self.base.addErrorWithNotes(nnotes + 1); + defer err.unlock(); try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)}); try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()}); @@ -1329,6 +1333,7 @@ fn reportUndefs(self: *MachO) !void { }; const err = try addFn(&self.base, nnotes); + defer err.unlock(); try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)}); switch (notes) { diff --git a/src/MachO/Dylib.zig b/src/MachO/Dylib.zig index f5c38684..f24b09f8 100644 --- a/src/MachO/Dylib.zig +++ b/src/MachO/Dylib.zig @@ -158,6 +158,7 @@ fn parseBinary(self: *Dylib, macho_file: *MachO) !void { } } else { const err = try macho_file.base.addErrorWithNotes(1 + platforms.items.len); + defer err.unlock(); try err.addMsg("{s}: object file was built for different platforms than required {s}", .{ self.path, @tagName(plat.platform), diff --git a/src/MachO/Object.zig b/src/MachO/Object.zig index 11396213..b1ed10b3 100644 --- a/src/MachO/Object.zig +++ b/src/MachO/Object.zig @@ -188,6 +188,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { } } else { const err = try macho_file.base.addErrorWithNotes(1 + platforms.items.len); + defer err.unlock(); try err.addMsg("{}: object file was built for different platforms than required {s}", .{ self.fmtPath(), @tagName(plat.platform), diff --git a/src/main.zig b/src/main.zig index 6149c666..7fbd37e7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -111,25 +111,26 @@ pub fn main() !void { const ld = try Ld.openPath(gpa, tag, opts, &thread_pool); defer ld.deinit(); - ld.flush() catch |err| switch (err) { - error.FlushFailed, - error.InferCpuFailed, - error.ParseFailed, - error.HasDuplicates, - error.UndefinedSymbols, - error.RelocError, - error.ResolveFailed, - error.Unimplemented, - => { - ld.reportWarnings(); - ld.reportErrors(); - std.process.exit(1); - }, - else => |e| { - ld.reportErrors(); - print("unexpected linker error: {s}\n", .{@errorName(e)}); - return e; - }, - }; + const res = ld.flush(); ld.reportWarnings(); + ld.reportErrors(); + res catch |err| { + switch (err) { + error.FlushFailed, + error.InferCpuFailed, + error.ParseFailed, + error.HasDuplicates, + error.UndefinedSymbols, + error.RelocError, + error.ResolveFailed, + error.Unimplemented, + => { + std.process.exit(1); + }, + else => |e| { + print("unexpected linker error: {s}\n", .{@errorName(e)}); + return e; + }, + } + }; }