authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-27 23:56:45+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-28 09:18:54+02:00
logf353b59efac62dd8bc2ec966e6ea46131a64b19a
tree4cf7eb20d7eefa46ae573a8dba664790357b361d
parent1188415f4cb73440fde05047cd8e6b79a2255efa

macho: discriminate between normal and weak dylibs

Parse `-weak-lx` and `-weak_framework x` in the CLI.

5 files changed, 134 insertions(+), 51 deletions(-)

lib/std/macho.zig+3-1
......@@ -2085,11 +2085,13 @@ pub fn GenericCommandWithData(comptime Cmd: type) type {
20852085
20862086pub fn createLoadDylibCommand(
20872087 allocator: Allocator,
2088 cmd_id: LC,
20882089 name: []const u8,
20892090 timestamp: u32,
20902091 current_version: u32,
20912092 compatibility_version: u32,
20922093) !GenericCommandWithData(dylib_command) {
2094 assert(cmd_id == .LOAD_DYLIB or cmd_id == .LOAD_WEAK_DYLIB or cmd_id == .REEXPORT_DYLIB or cmd_id == .ID_DYLIB);
20932095 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
20942096 u64,
20952097 @sizeOf(dylib_command) + name.len + 1, // +1 for nul
......@@ -2097,7 +2099,7 @@ pub fn createLoadDylibCommand(
20972099 ));
20982100
20992101 var dylib_cmd = emptyGenericCommandWithData(dylib_command{
2100 .cmd = .LOAD_DYLIB,
2102 .cmd = cmd_id,
21012103 .cmdsize = cmdsize,
21022104 .dylib = .{
21032105 .name = @sizeOf(dylib_command),
src/link.zig+1
......@@ -21,6 +21,7 @@ const TypedValue = @import("TypedValue.zig");
2121
2222pub const SystemLib = struct {
2323 needed: bool = false,
24 weak: bool = false,
2425};
2526
2627pub const CacheMode = enum { incremental, whole };
src/link/MachO.zig+75-38
......@@ -52,6 +52,11 @@ pub const SearchStrategy = enum {
5252 dylibs_first,
5353};
5454
55const SystemLib = struct {
56 needed: bool = false,
57 weak: bool = false,
58};
59
5560base: File,
5661
5762/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
......@@ -768,7 +773,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
768773 }
769774
770775 // Shared and static libraries passed via `-l` flag.
771 var candidate_libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena);
776 var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena);
772777
773778 const system_lib_names = self.base.options.system_libs.keys();
774779 for (system_lib_names) |system_lib_name| {
......@@ -781,7 +786,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
781786 }
782787
783788 const system_lib_info = self.base.options.system_libs.get(system_lib_name).?;
784 try candidate_libs.put(system_lib_name, system_lib_info);
789 try candidate_libs.put(system_lib_name, .{
790 .needed = system_lib_info.needed,
791 .weak = system_lib_info.weak,
792 });
785793 }
786794
787795 var lib_dirs = std.ArrayList([]const u8).init(arena);
......@@ -793,7 +801,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
793801 }
794802 }
795803
796 var libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena);
804 var libs = std.StringArrayHashMap(SystemLib).init(arena);
797805
798806 // Assume ld64 default -search_paths_first if no strategy specified.
799807 const search_strategy = self.base.options.search_strategy orelse .paths_first;
......@@ -890,7 +898,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
890898 for (framework_dirs.items) |dir| {
891899 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
892900 if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {
893 try libs.put(full_path, self.base.options.frameworks.get(f_name).?);
901 const info = self.base.options.frameworks.get(f_name).?;
902 try libs.put(full_path, .{
903 .needed = info.needed,
904 .weak = info.weak,
905 });
894906 continue :outer;
895907 }
896908 }
......@@ -1026,9 +1038,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
10261038 try argv.append("-lc");
10271039
10281040 for (self.base.options.system_libs.keys()) |l_name| {
1029 const needed = self.base.options.system_libs.get(l_name).?.needed;
1030 const arg = if (needed)
1041 const info = self.base.options.system_libs.get(l_name).?;
1042 const arg = if (info.needed)
10311043 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
1044 else if (info.weak)
1045 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
10321046 else
10331047 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
10341048 try argv.append(arg);
......@@ -1039,9 +1053,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
10391053 }
10401054
10411055 for (self.base.options.frameworks.keys()) |framework| {
1042 const needed = self.base.options.frameworks.get(framework).?.needed;
1043 const arg = if (needed)
1056 const info = self.base.options.frameworks.get(framework).?;
1057 const arg = if (info.needed)
10441058 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
1059 else if (info.weak)
1060 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
10451061 else
10461062 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
10471063 try argv.append(arg);
......@@ -1063,7 +1079,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
10631079 Compilation.dump_argv(argv.items);
10641080 }
10651081
1066 var dependent_libs = std.fifo.LinearFifo(Dylib.Id, .Dynamic).init(self.base.allocator);
1082 var dependent_libs = std.fifo.LinearFifo(struct {
1083 id: Dylib.Id,
1084 parent: u16,
1085 }, .Dynamic).init(self.base.allocator);
10671086 defer dependent_libs.deinit();
10681087 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
10691088 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
......@@ -1389,13 +1408,18 @@ const ParseDylibError = error{
13891408
13901409const DylibCreateOpts = struct {
13911410 syslibroot: ?[]const u8,
1392 dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic),
13931411 id: ?Dylib.Id = null,
1394 is_dependent: bool = false,
1395 is_needed: bool = false,
1412 dependent: bool = false,
1413 needed: bool = false,
1414 weak: bool = false,
13961415};
13971416
1398pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDylibError!bool {
1417pub fn parseDylib(
1418 self: *MachO,
1419 path: []const u8,
1420 dependent_libs: anytype,
1421 opts: DylibCreateOpts,
1422) ParseDylibError!bool {
13991423 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
14001424 error.FileNotFound => return false,
14011425 else => |e| return e,
......@@ -1405,12 +1429,19 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
14051429 const name = try self.base.allocator.dupe(u8, path);
14061430 errdefer self.base.allocator.free(name);
14071431
1432 const dylib_id = @intCast(u16, self.dylibs.items.len);
14081433 var dylib = Dylib{
14091434 .name = name,
14101435 .file = file,
1436 .weak = opts.weak,
14111437 };
14121438
1413 dylib.parse(self.base.allocator, self.base.options.target, opts.dependent_libs) catch |err| switch (err) {
1439 dylib.parse(
1440 self.base.allocator,
1441 self.base.options.target,
1442 dylib_id,
1443 dependent_libs,
1444 ) catch |err| switch (err) {
14141445 error.EndOfStream, error.NotDylib => {
14151446 try file.seekTo(0);
14161447
......@@ -1420,7 +1451,13 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
14201451 };
14211452 defer lib_stub.deinit();
14221453
1423 try dylib.parseFromStub(self.base.allocator, self.base.options.target, lib_stub, opts.dependent_libs);
1454 try dylib.parseFromStub(
1455 self.base.allocator,
1456 self.base.options.target,
1457 lib_stub,
1458 dylib_id,
1459 dependent_libs,
1460 );
14241461 },
14251462 else => |e| return e,
14261463 };
......@@ -1438,13 +1475,12 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
14381475 }
14391476 }
14401477
1441 const dylib_id = @intCast(u16, self.dylibs.items.len);
14421478 try self.dylibs.append(self.base.allocator, dylib);
14431479 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);
14441480
14451481 const should_link_dylib_even_if_unreachable = blk: {
1446 if (self.base.options.dead_strip_dylibs and !opts.is_needed) break :blk false;
1447 break :blk !(opts.is_dependent or self.referenced_dylibs.contains(dylib_id));
1482 if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;
1483 break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id));
14481484 };
14491485
14501486 if (should_link_dylib_even_if_unreachable) {
......@@ -1467,9 +1503,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
14671503
14681504 if (try self.parseObject(full_path)) continue;
14691505 if (try self.parseArchive(full_path, false)) continue;
1470 if (try self.parseDylib(full_path, .{
1506 if (try self.parseDylib(full_path, dependent_libs, .{
14711507 .syslibroot = syslibroot,
1472 .dependent_libs = dependent_libs,
14731508 })) continue;
14741509
14751510 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
......@@ -1494,17 +1529,17 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
14941529fn parseLibs(
14951530 self: *MachO,
14961531 lib_names: []const []const u8,
1497 lib_infos: []const Compilation.SystemLib,
1532 lib_infos: []const SystemLib,
14981533 syslibroot: ?[]const u8,
14991534 dependent_libs: anytype,
15001535) !void {
15011536 for (lib_names) |lib, i| {
15021537 const lib_info = lib_infos[i];
15031538 log.debug("parsing lib path '{s}'", .{lib});
1504 if (try self.parseDylib(lib, .{
1539 if (try self.parseDylib(lib, dependent_libs, .{
15051540 .syslibroot = syslibroot,
1506 .dependent_libs = dependent_libs,
1507 .is_needed = lib_info.needed,
1541 .needed = lib_info.needed,
1542 .weak = lib_info.weak,
15081543 })) continue;
15091544 if (try self.parseArchive(lib, false)) continue;
15101545
......@@ -1522,20 +1557,21 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
15221557 const arena = arena_alloc.allocator();
15231558 defer arena_alloc.deinit();
15241559
1525 while (dependent_libs.readItem()) |*id| {
1526 defer id.deinit(self.base.allocator);
1560 while (dependent_libs.readItem()) |*dep_id| {
1561 defer dep_id.id.deinit(self.base.allocator);
15271562
1528 if (self.dylibs_map.contains(id.name)) continue;
1563 if (self.dylibs_map.contains(dep_id.id.name)) continue;
15291564
1565 const weak = self.dylibs.items[dep_id.parent].weak;
15301566 const has_ext = blk: {
1531 const basename = fs.path.basename(id.name);
1567 const basename = fs.path.basename(dep_id.id.name);
15321568 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
15331569 };
1534 const extension = if (has_ext) fs.path.extension(id.name) else "";
1570 const extension = if (has_ext) fs.path.extension(dep_id.id.name) else "";
15351571 const without_ext = if (has_ext) blk: {
1536 const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable;
1537 break :blk id.name[0..index];
1538 } else id.name;
1572 const index = mem.lastIndexOfScalar(u8, dep_id.id.name, '.') orelse unreachable;
1573 break :blk dep_id.id.name[0..index];
1574 } else dep_id.id.name;
15391575
15401576 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
15411577 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext });
......@@ -1543,15 +1579,15 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
15431579
15441580 log.debug("trying dependency at fully resolved path {s}", .{full_path});
15451581
1546 const did_parse_successfully = try self.parseDylib(full_path, .{
1547 .id = id.*,
1582 const did_parse_successfully = try self.parseDylib(full_path, dependent_libs, .{
1583 .id = dep_id.id,
15481584 .syslibroot = syslibroot,
1549 .is_dependent = true,
1550 .dependent_libs = dependent_libs,
1585 .dependent = true,
1586 .weak = weak,
15511587 });
15521588 if (did_parse_successfully) break;
15531589 } else {
1554 log.warn("unable to resolve dependency {s}", .{id.name});
1590 log.warn("unable to resolve dependency {s}", .{dep_id.id.name});
15551591 }
15561592 }
15571593}
......@@ -3441,6 +3477,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void {
34413477 const dylib_id = dylib.id orelse unreachable;
34423478 var dylib_cmd = try macho.createLoadDylibCommand(
34433479 self.base.allocator,
3480 if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
34443481 dylib_id.name,
34453482 dylib_id.timestamp,
34463483 dylib_id.current_version,
......@@ -4885,13 +4922,13 @@ fn populateMissingMetadata(self: *MachO) !void {
48854922 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
48864923 var dylib_cmd = try macho.createLoadDylibCommand(
48874924 self.base.allocator,
4925 .ID_DYLIB,
48884926 install_name,
48894927 2,
48904928 current_version.major << 16 | current_version.minor << 8 | current_version.patch,
48914929 compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch,
48924930 );
48934931 errdefer dylib_cmd.deinit(self.base.allocator);
4894 dylib_cmd.inner.cmd = .ID_DYLIB;
48954932 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });
48964933 self.load_commands_dirty = true;
48974934 }
src/link/MachO/Dylib.zig+20-6
......@@ -30,6 +30,7 @@ dysymtab_cmd_index: ?u16 = null,
3030id_cmd_index: ?u16 = null,
3131
3232id: ?Id = null,
33weak: bool = false,
3334
3435/// Parsed symbol table represented as hash map of symbols'
3536/// names. We can and should defer creating *Symbols until
......@@ -141,7 +142,13 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
141142 }
142143}
143144
144pub fn parse(self: *Dylib, allocator: Allocator, target: std.Target, dependent_libs: anytype) !void {
145pub fn parse(
146 self: *Dylib,
147 allocator: Allocator,
148 target: std.Target,
149 dylib_id: u16,
150 dependent_libs: anytype,
151) !void {
145152 log.debug("parsing shared library '{s}'", .{self.name});
146153
147154 self.library_offset = try fat.getLibraryOffset(self.file.reader(), target);
......@@ -163,12 +170,18 @@ pub fn parse(self: *Dylib, allocator: Allocator, target: std.Target, dependent_l
163170 return error.MismatchedCpuArchitecture;
164171 }
165172
166 try self.readLoadCommands(allocator, reader, dependent_libs);
173 try self.readLoadCommands(allocator, reader, dylib_id, dependent_libs);
167174 try self.parseId(allocator);
168175 try self.parseSymbols(allocator);
169176}
170177
171fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, dependent_libs: anytype) !void {
178fn readLoadCommands(
179 self: *Dylib,
180 allocator: Allocator,
181 reader: anytype,
182 dylib_id: u16,
183 dependent_libs: anytype,
184) !void {
172185 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
173186
174187 try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds);
......@@ -190,7 +203,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende
190203 if (should_lookup_reexports) {
191204 // Parse install_name to dependent dylib.
192205 var id = try Id.fromLoadCommand(allocator, cmd.dylib);
193 try dependent_libs.writeItem(id);
206 try dependent_libs.writeItem(.{ .id = id, .parent = dylib_id });
194207 }
195208 },
196209 else => {
......@@ -338,6 +351,7 @@ pub fn parseFromStub(
338351 allocator: Allocator,
339352 target: std.Target,
340353 lib_stub: LibStub,
354 dylib_id: u16,
341355 dependent_libs: anytype,
342356) !void {
343357 if (lib_stub.inner.len == 0) return error.EmptyStubFile;
......@@ -417,7 +431,7 @@ pub fn parseFromStub(
417431 log.debug(" (found re-export '{s}')", .{lib});
418432
419433 var dep_id = try Id.default(allocator, lib);
420 try dependent_libs.writeItem(dep_id);
434 try dependent_libs.writeItem(.{ .id = dep_id, .parent = dylib_id });
421435 }
422436 }
423437 }
......@@ -522,7 +536,7 @@ pub fn parseFromStub(
522536 log.debug(" (found re-export '{s}')", .{lib});
523537
524538 var dep_id = try Id.default(allocator, lib);
525 try dependent_libs.writeItem(dep_id);
539 try dependent_libs.writeItem(.{ .id = dep_id, .parent = dylib_id });
526540 }
527541 }
528542 }
src/main.zig+35-6
......@@ -443,9 +443,12 @@ const usage_build_generic =
443443 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker
444444 \\ --stack [size] Override default stack size
445445 \\ --image-base [addr] Set base address for executable image
446 \\ -weak-l[lib] (Darwin) link against system library and mark it and all referenced symbols as weak
447 \\ -weak_library [lib]
446448 \\ -framework [name] (Darwin) link against framework
447449 \\ -needed_framework [name] (Darwin) link against framework (even if unused)
448450 \\ -needed_library [lib] (Darwin) link against system library (even if unused)
451 \\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak
449452 \\ -F[dir] (Darwin) add search path for frameworks
450453 \\ -install_name=[value] (Darwin) add dylib's install name
451454 \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
......@@ -916,7 +919,12 @@ fn buildOutputType(
916919 const path = args_iter.next() orelse {
917920 fatal("expected parameter after {s}", .{arg});
918921 };
919 try frameworks.put(gpa, path, .{ .needed = false });
922 try frameworks.put(gpa, path, .{});
923 } else if (mem.eql(u8, arg, "-weak_framework")) {
924 const path = args_iter.next() orelse {
925 fatal("expected parameter after {s}", .{arg});
926 };
927 try frameworks.put(gpa, path, .{ .weak = true });
920928 } else if (mem.eql(u8, arg, "-needed_framework")) {
921929 const path = args_iter.next() orelse {
922930 fatal("expected parameter after {s}", .{arg});
......@@ -962,7 +970,7 @@ fn buildOutputType(
962970 };
963971 // We don't know whether this library is part of libc or libc++ until
964972 // we resolve the target, so we simply append to the list for now.
965 try system_libs.put(next_arg, .{ .needed = false });
973 try system_libs.put(next_arg, .{});
966974 } else if (mem.eql(u8, arg, "--needed-library") or
967975 mem.eql(u8, arg, "-needed-l") or
968976 mem.eql(u8, arg, "-needed_library"))
......@@ -971,6 +979,11 @@ fn buildOutputType(
971979 fatal("expected parameter after {s}", .{arg});
972980 };
973981 try system_libs.put(next_arg, .{ .needed = true });
982 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
983 const next_arg = args_iter.next() orelse {
984 fatal("expected parameter after {s}", .{arg});
985 };
986 try system_libs.put(next_arg, .{ .weak = true });
974987 } else if (mem.eql(u8, arg, "-D") or
975988 mem.eql(u8, arg, "-isystem") or
976989 mem.eql(u8, arg, "-I") or
......@@ -1300,9 +1313,11 @@ fn buildOutputType(
13001313 } else if (mem.startsWith(u8, arg, "-l")) {
13011314 // We don't know whether this library is part of libc or libc++ until
13021315 // we resolve the target, so we simply append to the list for now.
1303 try system_libs.put(arg["-l".len..], .{ .needed = false });
1316 try system_libs.put(arg["-l".len..], .{});
13041317 } else if (mem.startsWith(u8, arg, "-needed-l")) {
13051318 try system_libs.put(arg["-needed-l".len..], .{ .needed = true });
1319 } else if (mem.startsWith(u8, arg, "-weak-l")) {
1320 try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
13061321 } else if (mem.startsWith(u8, arg, "-D") or
13071322 mem.startsWith(u8, arg, "-I"))
13081323 {
......@@ -1596,7 +1611,7 @@ fn buildOutputType(
15961611 try clang_argv.appendSlice(it.other_args);
15971612 },
15981613 .framework_dir => try framework_dirs.append(it.only_arg),
1599 .framework => try frameworks.put(gpa, it.only_arg, .{ .needed = false }),
1614 .framework => try frameworks.put(gpa, it.only_arg, .{}),
16001615 .nostdlibinc => want_native_include_dirs = false,
16011616 .strip => strip = true,
16021617 .exec_model => {
......@@ -1879,12 +1894,18 @@ fn buildOutputType(
18791894 ) catch |err| {
18801895 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
18811896 };
1882 } else if (mem.eql(u8, arg, "-framework") or mem.eql(u8, arg, "-weak_framework")) {
1897 } else if (mem.eql(u8, arg, "-framework")) {
1898 i += 1;
1899 if (i >= linker_args.items.len) {
1900 fatal("expected linker arg after '{s}'", .{arg});
1901 }
1902 try frameworks.put(gpa, linker_args.items[i], .{});
1903 } else if (mem.eql(u8, arg, "-weak_framework")) {
18831904 i += 1;
18841905 if (i >= linker_args.items.len) {
18851906 fatal("expected linker arg after '{s}'", .{arg});
18861907 }
1887 try frameworks.put(gpa, linker_args.items[i], .{ .needed = false });
1908 try frameworks.put(gpa, linker_args.items[i], .{ .weak = true });
18881909 } else if (mem.eql(u8, arg, "-needed_framework")) {
18891910 i += 1;
18901911 if (i >= linker_args.items.len) {
......@@ -1897,6 +1918,14 @@ fn buildOutputType(
18971918 fatal("expected linker arg after '{s}'", .{arg});
18981919 }
18991920 try system_libs.put(linker_args.items[i], .{ .needed = true });
1921 } else if (mem.startsWith(u8, arg, "-weak-l")) {
1922 try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
1923 } else if (mem.eql(u8, arg, "-weak_library")) {
1924 i += 1;
1925 if (i >= linker_args.items.len) {
1926 fatal("expected linker arg after '{s}'", .{arg});
1927 }
1928 try system_libs.put(linker_args.items[i], .{ .weak = true });
19001929 } else if (mem.eql(u8, arg, "-compatibility_version")) {
19011930 i += 1;
19021931 if (i >= linker_args.items.len) {