authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-21 21:03:55+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-09-21 13:08:49-07:00
log0a1843452e6d39d4a2adc9c7feefce274a5d30c6
tree7012d3b9006801793eed12f69dd0b2bbd8fc11aa
parentcf33f1003fd02abaf91078725d9c972dd51fc5e1

link: clean up and unify shared library input rules

For determining the `DT_NEEDED` value: * the `DT_SONAME` always takes precedence * otherwise: * `-l` arguments use the resolved path's basename * raw `/path/to/libfoo.so` arguments use the full path All ELF linkers, including LLD, now implement exactly those semantics. The operand to `-l` is not allowed to contain path separators, because the behavior of this in other linkers is confusing and likely not supported. (This is not the case for `-l :path/to/libfoo.so`.)

7 files changed, 161 insertions(+), 82 deletions(-)

src/link.zig+103-37
......@@ -1819,11 +1819,14 @@ pub const UnresolvedInput = union(enum) {
18191819 path_query: PathQuery,
18201820 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path?
18211821 /// Who knows! Fuck around and find out.
1822 ambiguous_name: NameQuery,
1822 ambiguous_name: AmbiguousNameQuery,
18231823
18241824 pub const NameQuery = struct {
18251825 name: []const u8,
18261826 query: Query,
1827 // Corresponds to GNU ld `-l :path/to/filename`, meaning that `name` is a path relative to
1828 // the library search path rather than just a library name.
1829 name_done: bool,
18271830 };
18281831
18291832 pub const PathQuery = struct {
......@@ -1831,6 +1834,11 @@ pub const UnresolvedInput = union(enum) {
18311834 query: Query,
18321835 };
18331836
1837 pub const AmbiguousNameQuery = struct {
1838 name: []const u8,
1839 query: Query,
1840 };
1841
18341842 pub const Query = struct {
18351843 needed: bool = false,
18361844 weak: bool = false,
......@@ -1838,14 +1846,6 @@ pub const UnresolvedInput = union(enum) {
18381846 must_link: bool = false,
18391847 hidden: bool = false,
18401848 allow_so_scripts: bool = false,
1841 // Corresponds to GNU ld `-l :path/to/filename`:
1842 // * No extra rpaths.
1843 // * NEEDED entry should be exactly the string
1844 // after the colon. No file system paths prepended.
1845 // * The DSO still must be found at compile/link
1846 // time and its entries used to resolve symbols.
1847 // CGo compilation depends on this.
1848 name_done: bool = false,
18491849 preferred_mode: std.lang.LinkMode,
18501850 search_strategy: SearchStrategy,
18511851
......@@ -1891,15 +1891,9 @@ pub const Input = union(enum) {
18911891 needed: bool,
18921892 weak: bool,
18931893 reexport: bool,
1894 /// Does not include any ":" prefix. Indicates that this string, exactly, should go into the NEEDED
1895 /// entry, without affecting any rpaths.
1896 exact_name: ?[]const u8,
1897 };
1894 fallback_soname: FallbackSoname,
18981895
1899 pub const DsoExact = struct {
1900 /// Includes the ":" prefix. This is intended to be put into the DSO
1901 /// section verbatim with no corresponding rpaths.
1902 name: []const u8,
1896 pub const FallbackSoname = enum { basename, full_path };
19031897 };
19041898
19051899 pub fn path(input: Input) Path {
......@@ -1950,7 +1944,7 @@ pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void {
19501944 man.hash.add(dso.needed);
19511945 man.hash.add(dso.weak);
19521946 man.hash.add(dso.reexport);
1953 man.hash.addOptionalBytes(dso.exact_name);
1947 man.hash.add(dso.fallback_soname);
19541948 },
19551949 }
19561950 }
......@@ -2001,7 +1995,7 @@ pub fn resolveInputs(
20011995 .name_query => |name_query| {
20021996 const query = name_query.query;
20031997
2004 // Checked in the first pass above while looking for libc libraries.
1998 // Checked in the first pass in `main.zig` while looking for libc libraries.
20051999 assert(!fs.path.isAbsolute(name_query.name));
20062000
20072001 checked_paths.clearRetainingCapacity();
......@@ -2229,7 +2223,7 @@ fn resolveLibInput(
22292223
22302224 const lib_name = name_query.name;
22312225
2232 if (target.os.tag.isDarwin() and link_mode == .dynamic and !name_query.query.name_done) tbd: {
2226 if (target.os.tag.isDarwin() and link_mode == .dynamic and !name_query.name_done) tbd: {
22332227 // Prefer .tbd over .dylib.
22342228 const test_path: Path = .{
22352229 .root_dir = lib_directory,
......@@ -2241,13 +2235,22 @@ fn resolveLibInput(
22412235 else => |e| fatal("searching for tbd library {qf}: {t}", .{ test_path, e }),
22422236 };
22432237 errdefer file.close(io);
2244 return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query);
2238 return finishResolveLibInput(
2239 io,
2240 resolved_inputs,
2241 archive_dedup,
2242 test_path,
2243 file,
2244 link_mode,
2245 name_query.query,
2246 .basename,
2247 );
22452248 }
22462249
22472250 {
22482251 const test_path: Path = .{
22492252 .root_dir = lib_directory,
2250 .sub_path = if (name_query.query.name_done) lib_name else try std.fmt.allocPrint(arena, "{s}{s}{s}", .{
2253 .sub_path = if (name_query.name_done) lib_name else try std.fmt.allocPrint(arena, "{s}{s}{s}", .{
22512254 target.libPrefix(),
22522255 lib_name,
22532256 switch (link_mode) {
......@@ -2260,7 +2263,7 @@ fn resolveLibInput(
22602263 switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{
22612264 .path = test_path,
22622265 .query = name_query.query,
2263 }, link_mode, color)) {
2266 }, link_mode, color, .basename)) {
22642267 .no_match => {},
22652268 .ok => return .ok,
22662269 }
......@@ -2268,7 +2271,7 @@ fn resolveLibInput(
22682271
22692272 // In the case of Darwin, the main check will be .dylib, so here we
22702273 // additionally check for .so files.
2271 if (target.os.tag.isDarwin() and link_mode == .dynamic and !name_query.query.name_done) so: {
2274 if (target.os.tag.isDarwin() and link_mode == .dynamic and !name_query.name_done) so: {
22722275 const test_path: Path = .{
22732276 .root_dir = lib_directory,
22742277 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.so", .{lib_name}),
......@@ -2279,12 +2282,21 @@ fn resolveLibInput(
22792282 else => |e| fatal("unable to search for so library {qf}: {t}", .{ test_path, e }),
22802283 };
22812284 errdefer file.close(io);
2282 return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query);
2285 return finishResolveLibInput(
2286 io,
2287 resolved_inputs,
2288 archive_dedup,
2289 test_path,
2290 file,
2291 link_mode,
2292 name_query.query,
2293 .basename,
2294 );
22832295 }
22842296
22852297 // In the case of MinGW, the main check will be .lib but we also need to
22862298 // look for `libfoo.a`.
2287 if (target.isMinGW() and link_mode == .static and !name_query.query.name_done) mingw: {
2299 if (target.isMinGW() and link_mode == .static and !name_query.name_done) mingw: {
22882300 const test_path: Path = .{
22892301 .root_dir = lib_directory,
22902302 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.a", .{lib_name}),
......@@ -2295,13 +2307,22 @@ fn resolveLibInput(
22952307 else => |e| fatal("unable to search for static library {qf}: {t}", .{ test_path, e }),
22962308 };
22972309 errdefer file.close(io);
2298 return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query);
2310 return finishResolveLibInput(
2311 io,
2312 resolved_inputs,
2313 archive_dedup,
2314 test_path,
2315 file,
2316 link_mode,
2317 name_query.query,
2318 .basename,
2319 );
22992320 }
23002321
23012322 // In the case of OpenBSD, dynamic libraries are always versioned, without
23022323 // unversioned symlinks. OpenBSD patches LLD to select the highest-versioned
23032324 // shared library, and this code is intended to match that upstream behavior.
2304 if (target.isOpenBSDLibC() and link_mode == .dynamic and !name_query.query.name_done) versioned: {
2325 if (target.isOpenBSDLibC() and link_mode == .dynamic and !name_query.name_done) versioned: {
23052326 const prefix = try std.fmt.allocPrint(arena, "lib{s}.so.", .{lib_name});
23062327
23072328 var dir = lib_directory.handle.openDir(io, ".", .{ .iterate = true }) catch |err| switch (err) {
......@@ -2347,7 +2368,7 @@ fn resolveLibInput(
23472368 switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{
23482369 .path = test_path,
23492370 .query = name_query.query,
2350 }, link_mode, color)) {
2371 }, link_mode, color, .basename)) {
23512372 .no_match => {},
23522373 .ok => return .ok,
23532374 }
......@@ -2388,6 +2409,7 @@ fn finishResolveLibInput(
23882409 file: Io.File,
23892410 link_mode: std.lang.LinkMode,
23902411 query: UnresolvedInput.Query,
2412 fallback_soname: Input.Dso.FallbackSoname,
23912413) ResolveLibInputResult {
23922414 switch (link_mode) {
23932415 .static => {
......@@ -2413,9 +2435,7 @@ fn finishResolveLibInput(
24132435 .needed = query.needed,
24142436 .weak = query.weak,
24152437 .reexport = query.reexport,
2416 // Here we assume that `Path.sub_path` is usable as `exact_name`, however the code after this point
2417 // has no business making that assumption.
2418 .exact_name = if (query.name_done) path.sub_path else null,
2438 .fallback_soname = fallback_soname,
24192439 },
24202440 }),
24212441 }
......@@ -2439,8 +2459,34 @@ fn resolvePathInput(
24392459 color: std.zig.Color,
24402460) Allocator.Error!?ResolveLibInputResult {
24412461 switch (Compilation.classifyFileExt(pq.path.sub_path)) {
2442 .static_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .static, color),
2443 .shared_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .dynamic, color),
2462 .static_library => return try resolvePathInputLib(
2463 gpa,
2464 arena,
2465 io,
2466 unresolved_inputs,
2467 resolved_inputs,
2468 ld_script_bytes,
2469 archive_dedup,
2470 target,
2471 pq,
2472 .static,
2473 color,
2474 .full_path,
2475 ),
2476 .shared_library => return try resolvePathInputLib(
2477 gpa,
2478 arena,
2479 io,
2480 unresolved_inputs,
2481 resolved_inputs,
2482 ld_script_bytes,
2483 archive_dedup,
2484 target,
2485 pq,
2486 .dynamic,
2487 color,
2488 .full_path,
2489 ),
24442490 .object => {
24452491 var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err|
24462492 fatal("failed to open object {f}: {t}", .{ pq.path, err });
......@@ -2483,6 +2529,7 @@ fn resolvePathInputLib(
24832529 pq: UnresolvedInput.PathQuery,
24842530 link_mode: std.lang.LinkMode,
24852531 color: std.zig.Color,
2532 fallback_soname: Input.Dso.FallbackSoname,
24862533) Allocator.Error!ResolveLibInputResult {
24872534 try resolved_inputs.ensureUnusedCapacity(gpa, 1);
24882535 try archive_dedup.ensureUnusedCapacity(gpa, 1);
......@@ -2510,7 +2557,16 @@ fn resolvePathInputLib(
25102557 mem.startsWith(u8, buf, std.elf.ARMAG_THIN))
25112558 {
25122559 // Appears to be an ELF or archive file.
2513 return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, pq.query);
2560 return finishResolveLibInput(
2561 io,
2562 resolved_inputs,
2563 archive_dedup,
2564 test_path,
2565 file,
2566 link_mode,
2567 pq.query,
2568 fallback_soname,
2569 );
25142570 }
25152571 const stat = file.stat(io) catch |err|
25162572 fatal("failed to stat {f}: {t}", .{ test_path, err });
......@@ -2561,6 +2617,7 @@ fn resolvePathInputLib(
25612617 unresolved_inputs.appendAssumeCapacity(.{ .name_query = .{
25622618 .name = try arena.dupe(u8, arg.path["-l".len..]),
25632619 .query = query,
2620 .name_done = false,
25642621 } });
25652622 } else {
25662623 unresolved_inputs.appendAssumeCapacity(.{ .ambiguous_name = .{
......@@ -2578,7 +2635,16 @@ fn resolvePathInputLib(
25782635 else => |e| fatal("unable to search for {t} library {f}: {t}", .{ link_mode, test_path, e }),
25792636 };
25802637 errdefer file.close(io);
2581 return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, pq.query);
2638 return finishResolveLibInput(
2639 io,
2640 resolved_inputs,
2641 archive_dedup,
2642 test_path,
2643 file,
2644 link_mode,
2645 pq.query,
2646 fallback_soname,
2647 );
25822648}
25832649
25842650pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object {
......@@ -2601,7 +2667,7 @@ pub fn openDso(io: Io, path: Path, needed: bool, weak: bool, reexport: bool) !In
26012667 .needed = needed,
26022668 .weak = weak,
26032669 .reexport = reexport,
2604 .exact_name = null,
2670 .fallback_soname = .full_path,
26052671 };
26062672}
26072673
src/link/Elf.zig+9-4
......@@ -735,7 +735,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {
735735 } else {
736736 try parseArchive(gpa, io, diags, &self.file_handles, &self.files, target, debug_fmt_strip, default_sym_version, &self.objects, obj);
737737 },
738 .dso => |dso| try parseDso(gpa, io, diags, dso, &self.shared_objects, &self.files, target),
738 .dso => |dso| try parseDso(gpa, comp.arena, io, diags, dso, &self.shared_objects, &self.files, target),
739739 }
740740}
741741
......@@ -1106,6 +1106,7 @@ fn parseArchive(
11061106
11071107fn parseDso(
11081108 gpa: Allocator,
1109 arena: Allocator,
11091110 io: Io,
11101111 diags: *Diags,
11111112 dso: link.Input.Dso,
......@@ -1118,13 +1119,16 @@ fn parseDso(
11181119
11191120 const handle = dso.file;
11201121
1121 if (dso.exact_name != null) @panic("TODO");
1122
11231122 const stat: Stat = .init(try handle.stat(io));
11241123 var header = try SharedObject.parseHeader(gpa, io, diags, dso.path, handle, stat, target);
11251124 defer header.deinit(gpa);
11261125
1127 const soname = header.soname() orelse dso.path.basename();
1126 const fallback_soname: []const u8 = switch (dso.fallback_soname) {
1127 .full_path => try dso.path.toString(arena),
1128 .basename => fs.path.basename(dso.path.sub_path),
1129 };
1130
1131 const soname = header.soname() orelse fallback_soname;
11281132
11291133 const gop = try shared_objects.getOrPut(gpa, soname);
11301134 if (gop.found_existing) return;
......@@ -1156,6 +1160,7 @@ fn parseDso(
11561160 .symbols_extra = .empty,
11571161 .symbols_resolver = .empty,
11581162 .output_symtab_ctx = .{},
1163 .fallback_soname = fallback_soname,
11591164 },
11601165 });
11611166 const so = fileLookup(files.*, index, null).?.shared_object;
src/link/Elf/SharedObject.zig+3-1
......@@ -31,6 +31,8 @@ alive: bool,
3131
3232output_symtab_ctx: Elf.SymtabCtx,
3333
34fallback_soname: []const u8,
35
3436pub fn deinit(so: *SharedObject, gpa: Allocator) void {
3537 gpa.free(so.path.sub_path);
3638 so.parsed.deinit(gpa);
......@@ -427,7 +429,7 @@ pub fn asFile(self: *SharedObject) File {
427429}
428430
429431pub fn soname(self: *SharedObject) []const u8 {
430 return self.parsed.soname() orelse self.path.basename();
432 return self.parsed.soname() orelse self.fallback_soname;
431433}
432434
433435pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void {
src/link/Elf2.zig+11-11
......@@ -6228,17 +6228,9 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void
62286228 },
62296229 .res => unreachable,
62306230 .dso => |dso| {
6231 if (dso.exact_name) |exact_name| {
6232 // This means the path field is still valid, but the NEEDED entry must be set to
6233 // exactly the `exact_name` string.
6234 std.debug.panic("load dso_exact {q}", .{exact_name});
6235 if (elf.shndx.dynamic != .UNDEF) {
6236 try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, exact_name), {});
6237 }
6238 }
62396231 try elf.needed.ensureUnusedCapacity(elf.base.comp.gpa, 1);
62406232 var fr = dso.file.reader(io, &buf);
6241 elf.loadDso(dso.path, &fr) catch |err| switch (err) {
6233 elf.loadDso(dso.path, dso.fallback_soname, &fr) catch |err| switch (err) {
62426234 else => |e| return e,
62436235 error.EndOfStream => return diags.failParse(dso.path, "unexpected eof", .{}),
62446236 error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail(
......@@ -6860,7 +6852,12 @@ fn populateArchiveMemberName(elf: *Elf, member_ar_hdr: *std.elf.ar_hdr, member_n
68606852 @memcpy(dest_slice[0 .. dest_slice.len - 2], member_name);
68616853 @memcpy(dest_slice[dest_slice.len - 2 ..], "/\n"); // yes, the terminator is weird
68626854}
6863fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {
6855fn loadDso(
6856 elf: *Elf,
6857 path: std.Build.Cache.Path,
6858 fallback_soname: link.Input.Dso.FallbackSoname,
6859 fr: *Io.File.Reader,
6860) (LoadParseInputError || error{BadMagic})!void {
68646861 const comp = elf.base.comp;
68656862 const gpa = comp.gpa;
68666863 const diags = &comp.link_diags;
......@@ -6956,7 +6953,10 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
69566953 }
69576954 break std.mem.sliceTo(dynstr[@intCast(val)..], 0);
69586955 }
6959 } else std.fs.path.basename(path.sub_path);
6956 } else switch (fallback_soname) {
6957 .basename => std.fs.path.basename(path.sub_path),
6958 .full_path => try path.toString(comp.arena),
6959 };
69606960 try elf.needed.put(gpa, try elf.string(.dynstr, soname), {});
69616961
69626962 // Scan the symbol table and populate `elf.dso_globals`.
src/link/Lld.zig+17-18
......@@ -1154,6 +1154,13 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
11541154 argv.appendAssumeCapacity("--as-needed");
11551155 var as_needed = true;
11561156
1157 // When we have a DSO input, in order to trick LLD into putting the basename in its
1158 // `DT_NEEDED` entry while still allowing us to tell it the exact path to the shared
1159 // object, we pass it on the CLI as "-l:/absolute/path/to/libfoo.so". This will treat
1160 // the given path not actually as an absolute path, but as relative to the library
1161 // search path, so the root directory must therefore be the only library search path.
1162 try argv.append("-L/");
1163
11571164 for (base.comp.link_inputs) |link_input| switch (link_input) {
11581165 .res => unreachable, // Windows-only
11591166 .object, .archive => continue,
......@@ -1171,24 +1178,16 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
11711178 },
11721179 }
11731180
1174 if (dso.exact_name) |exact_name| {
1175 // LLD does not have a way to specify full path to the DSO while also specifying
1176 // the exact value for NEEDED, so we have to use a combination of -L and -l args.
1177 assert(mem.endsWith(u8, dso.path.sub_path, exact_name));
1178 const truncated_path: Cache.Path = .{
1179 .root_dir = dso.path.root_dir,
1180 .sub_path = dso.path.sub_path[0 .. dso.path.sub_path.len - exact_name.len],
1181 };
1182 try argv.ensureUnusedCapacity(3);
1183 argv.appendAssumeCapacity("-L");
1184 argv.appendAssumeCapacity(try truncated_path.toString(arena));
1185 argv.appendAssumeCapacity(try arena.print("-l:{s}", .{exact_name}));
1186 } else {
1187 // By this time, we depend on these libs being dynamically linked
1188 // libraries and not static libraries (the check for that needs to be earlier),
1189 // but they could be full paths to .so files, in which case we
1190 // want to avoid prepending "-l".
1191 argv.appendAssumeCapacity(try dso.path.toString(arena));
1181 // By this time, we depend on these libs being dynamically linked libraries and
1182 // not static libraries (the check for that needs to be earlier), but they could
1183 // be full file paths, in which case we don't want to use the "-l:" strategy.
1184 switch (dso.fallback_soname) {
1185 .basename => try argv.append(try arena.print("-l:{s}", .{try fs.path.resolve(arena, &.{
1186 comp.dirs.cwd,
1187 dso.path.root_dir.path orelse ".",
1188 dso.path.sub_path,
1189 })})),
1190 .full_path => try argv.append(try dso.path.toString(arena)),
11921191 }
11931192 },
11941193 };
src/link/MachO.zig+2-10
......@@ -655,11 +655,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
655655 .object, .archive => |obj| try argv.append(try obj.path.toString(arena)),
656656 .res => |res| try argv.append(try res.path.toString(arena)),
657657 .dso => |dso| {
658 if (dso.exact_name) |exact_name| {
659 try argv.appendSlice(&.{ "-l", try arena.print(":{s}", .{exact_name}) });
660 } else {
661 try argv.append(try dso.path.toString(arena));
662 }
658 try argv.append(try dso.path.toString(arena));
663659 },
664660 };
665661
......@@ -789,11 +785,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
789785 } else {
790786 argv.appendAssumeCapacity("-l");
791787 }
792 if (dso.exact_name) |exact_name| {
793 argv.appendAssumeCapacity(try arena.print(":{s}", .{exact_name}));
794 } else {
795 argv.appendAssumeCapacity(try dso.path.toString(arena));
796 }
788 argv.appendAssumeCapacity(try dso.path.toString(arena));
797789 },
798790 };
799791
src/main.zig+16-1
......@@ -1351,6 +1351,7 @@ fn buildOutputType(
13511351 .search_strategy = lib_search_strategy,
13521352 .allow_so_scripts = allow_so_scripts,
13531353 },
1354 .name_done = false,
13541355 } });
13551356 } else if (mem.eql(u8, arg, "--needed-library") or
13561357 mem.eql(u8, arg, "-needed-l") or
......@@ -1366,6 +1367,7 @@ fn buildOutputType(
13661367 .search_strategy = lib_search_strategy,
13671368 .allow_so_scripts = allow_so_scripts,
13681369 },
1370 .name_done = false,
13691371 } });
13701372 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
13711373 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
......@@ -1377,6 +1379,7 @@ fn buildOutputType(
13771379 .search_strategy = lib_search_strategy,
13781380 .allow_so_scripts = allow_so_scripts,
13791381 },
1382 .name_done = false,
13801383 } });
13811384 } else if (mem.eql(u8, arg, "-D")) {
13821385 try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });
......@@ -1839,6 +1842,7 @@ fn buildOutputType(
18391842 .search_strategy = lib_search_strategy,
18401843 .allow_so_scripts = allow_so_scripts,
18411844 },
1845 .name_done = false,
18421846 } });
18431847 } else if (mem.cutPrefix(u8, arg, "-needed-l")) |name| {
18441848 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
......@@ -1850,6 +1854,7 @@ fn buildOutputType(
18501854 .search_strategy = lib_search_strategy,
18511855 .allow_so_scripts = allow_so_scripts,
18521856 },
1857 .name_done = false,
18531858 } });
18541859 } else if (mem.cutPrefix(u8, arg, "-weak-l")) |name| {
18551860 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
......@@ -1861,6 +1866,7 @@ fn buildOutputType(
18611866 .search_strategy = lib_search_strategy,
18621867 .allow_so_scripts = allow_so_scripts,
18631868 },
1869 .name_done = false,
18641870 } });
18651871 } else if (mem.startsWith(u8, arg, "-D")) {
18661872 try cc_argv.append(arena, arg);
......@@ -2095,8 +2101,8 @@ fn buildOutputType(
20952101 .preferred_mode = lib_preferred_mode,
20962102 .search_strategy = lib_search_strategy,
20972103 .allow_so_scripts = allow_so_scripts,
2098 .name_done = true,
20992104 },
2105 .name_done = true,
21002106 } });
21012107 } else {
21022108 const compiler_rt_classification = target_util.classifyCompilerRtLibName(it.only_arg);
......@@ -2122,6 +2128,7 @@ fn buildOutputType(
21222128 .search_strategy = lib_search_strategy,
21232129 .allow_so_scripts = allow_so_scripts,
21242130 },
2131 .name_done = false,
21252132 } });
21262133 }
21272134 }
......@@ -2552,6 +2559,7 @@ fn buildOutputType(
25522559 .search_strategy = lib_search_strategy,
25532560 .allow_so_scripts = allow_so_scripts,
25542561 },
2562 .name_done = false,
25552563 } }),
25562564 .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),
25572565 .headerpad_max_install_names => headerpad_max_install_names = true,
......@@ -2921,6 +2929,7 @@ fn buildOutputType(
29212929 .search_strategy = lib_search_strategy,
29222930 .allow_so_scripts = allow_so_scripts,
29232931 },
2932 .name_done = false,
29242933 } });
29252934 } else if (mem.cutPrefix(u8, arg, "-weak-l")) |rest| {
29262935 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
......@@ -2932,6 +2941,7 @@ fn buildOutputType(
29322941 .search_strategy = lib_search_strategy,
29332942 .allow_so_scripts = allow_so_scripts,
29342943 },
2944 .name_done = false,
29352945 } });
29362946 } else if (mem.eql(u8, arg, "-weak_library")) {
29372947 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
......@@ -2943,6 +2953,7 @@ fn buildOutputType(
29432953 .search_strategy = lib_search_strategy,
29442954 .allow_so_scripts = allow_so_scripts,
29452955 },
2956 .name_done = false,
29462957 } });
29472958 } else if (mem.eql(u8, arg, "-compatibility_version")) {
29482959 const compat_version = linker_args_it.nextOrFatal();
......@@ -4183,6 +4194,10 @@ fn createModule(
41834194 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
41844195 }
41854196
4197 if (!nq.name_done and std.mem.findScalar(u8, nq.name, '/') != null) {
4198 fatal("cannot use path separator in system library name: {s}", .{lib_name});
4199 }
4200
41864201 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
41874202 any_name_queries_remaining = true;
41884203 },