authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-09-22 02:08:08+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-09-22 02:08:08+02:00
loge3381ce10ee1ffec2ab7b82dd1b87a5aedaeef97
tree7616c3532d4956bfb34b6c168274c68469fcc32e
parent48abf1a34e90acd42501f5296ba6a16c90f60fe9
parent77d47494c7ccbe045b713900b32a85c859f78ed0

Merge pull request 'linker: remove dso_exact, make `-l:` arguments use library search paths' (#36909) from dso-exact-name into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36909

11 files changed, 239 insertions(+), 148 deletions(-)

src/Compilation.zig+3-2
...@@ -2891,12 +2891,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE...@@ -2891,12 +2891,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
2891 }2891 }
2892 }2892 }
28932893
2894 for (comp.link_inputs) |input| if (input.path()) |path| {2894 for (comp.link_inputs) |input| {
2895 const path = input.path();
2895 try comp.appendFileSystemInput(try .fromUnresolved(arena, comp.dirs, &.{2896 try comp.appendFileSystemInput(try .fromUnresolved(arena, comp.dirs, &.{
2896 path.root_dir.path orelse ".",2897 path.root_dir.path orelse ".",
2897 path.sub_path,2898 path.sub_path,
2898 }));2899 }));
2899 };2900 }
29002901
2901 // For compiling Win32 resources, we rely on the cache hash system to avoid duplicating work.2902 // For compiling Win32 resources, we rely on the cache hash system to avoid duplicating work.
2902 // Add a Job for each Win32 resource file.2903 // Add a Job for each Win32 resource file.
src/link.zig+135-72
...@@ -995,11 +995,7 @@ pub const File = struct {...@@ -995,11 +995,7 @@ pub const File = struct {
995 .{},995 .{},
996 ) catch |err| {996 ) catch |err| {
997 const diags = &base.comp.link_diags;997 const diags = &base.comp.link_diags;
998 return diags.fail("failed to copy '{f}' to '{f}': {t}", .{998 return diags.fail("failed to copy {qf} to {qf}: {t}", .{ cached_pp_file_path, emit, err });
999 std.fmt.alt(@as(Path, cached_pp_file_path), .formatEscapeChar),
1000 std.fmt.alt(@as(Path, emit), .formatEscapeChar),
1001 err,
1002 });
1003 };999 };
1004 return;1000 return;
1005 }1001 }
...@@ -1510,7 +1506,6 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {...@@ -1510,7 +1506,6 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
1510 .object => |obj| diags.addParseError(obj.path, "failed to parse object: {t}", .{e}),1506 .object => |obj| diags.addParseError(obj.path, "failed to parse object: {t}", .{e}),
1511 .archive => |obj| diags.addParseError(obj.path, "failed to parse archive: {t}", .{e}),1507 .archive => |obj| diags.addParseError(obj.path, "failed to parse archive: {t}", .{e}),
1512 .res => |res| diags.addParseError(res.path, "failed to parse Windows resource: {t}", .{e}),1508 .res => |res| diags.addParseError(res.path, "failed to parse Windows resource: {t}", .{e}),
1513 .dso_exact => diags.addError("failed to handle dso_exact: {t}", .{e}),
1514 },1509 },
1515 };1510 };
1516 prog_node.completeOne();1511 prog_node.completeOne();
...@@ -1740,7 +1735,7 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void...@@ -1740,7 +1735,7 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void
1740 if (comp.bin_file) |lf| {1735 if (comp.bin_file) |lf| {
1741 lf.updateLineNumber(pt, line_update.inst, line_update.line) catch |err| switch (err) {1736 lf.updateLineNumber(pt, line_update.inst, line_update.line) catch |err| switch (err) {
1742 error.OutOfMemory => diags.setAllocFailure(),1737 error.OutOfMemory => diags.setAllocFailure(),
1743 else => |e| log.err("update line number failed: {s}", .{@errorName(e)}),1738 else => |e| log.err("update line number failed: {t}", .{e}),
1744 };1739 };
1745 }1740 }
1746 }1741 }
...@@ -1753,7 +1748,7 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void...@@ -1753,7 +1748,7 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void
1753 if (comp.bin_file) |lf| {1748 if (comp.bin_file) |lf| {
1754 lf.lostTracking(pt, ti) catch |err| switch (err) {1749 lf.lostTracking(pt, ti) catch |err| switch (err) {
1755 error.OutOfMemory => diags.setAllocFailure(),1750 error.OutOfMemory => diags.setAllocFailure(),
1756 else => |e| log.err("lost tracking failed: {s}", .{@errorName(e)}),1751 else => |e| log.err("lost tracking failed: {t}", .{e}),
1757 };1752 };
1758 }1753 }
1759 }1754 }
...@@ -1824,13 +1819,14 @@ pub const UnresolvedInput = union(enum) {...@@ -1824,13 +1819,14 @@ pub const UnresolvedInput = union(enum) {
1824 path_query: PathQuery,1819 path_query: PathQuery,
1825 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path?1820 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path?
1826 /// Who knows! Fuck around and find out.1821 /// Who knows! Fuck around and find out.
1827 ambiguous_name: NameQuery,1822 ambiguous_name: AmbiguousNameQuery,
1828 /// Put exactly this string in the dynamic section, no rpath.
1829 dso_exact: Input.DsoExact,
18301823
1831 pub const NameQuery = struct {1824 pub const NameQuery = struct {
1832 name: []const u8,1825 name: []const u8,
1833 query: Query,1826 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,
1834 };1830 };
18351831
1836 pub const PathQuery = struct {1832 pub const PathQuery = struct {
...@@ -1838,6 +1834,11 @@ pub const UnresolvedInput = union(enum) {...@@ -1838,6 +1834,11 @@ pub const UnresolvedInput = union(enum) {
1838 query: Query,1834 query: Query,
1839 };1835 };
18401836
1837 pub const AmbiguousNameQuery = struct {
1838 name: []const u8,
1839 query: Query,
1840 };
1841
1841 pub const Query = struct {1842 pub const Query = struct {
1842 needed: bool = false,1843 needed: bool = false,
1843 weak: bool = false,1844 weak: bool = false,
...@@ -1871,7 +1872,6 @@ pub const Input = union(enum) {...@@ -1871,7 +1872,6 @@ pub const Input = union(enum) {
1871 /// May not be a GNU ld script. Those are resolved when converting from1872 /// May not be a GNU ld script. Those are resolved when converting from
1872 /// `UnresolvedInput` to `Input` values.1873 /// `UnresolvedInput` to `Input` values.
1873 dso: Dso,1874 dso: Dso,
1874 dso_exact: DsoExact,
18751875
1876 pub const Object = struct {1876 pub const Object = struct {
1877 path: Path,1877 path: Path,
...@@ -1891,29 +1891,22 @@ pub const Input = union(enum) {...@@ -1891,29 +1891,22 @@ pub const Input = union(enum) {
1891 needed: bool,1891 needed: bool,
1892 weak: bool,1892 weak: bool,
1893 reexport: bool,1893 reexport: bool,
1894 };1894 fallback_soname: FallbackSoname,
18951895
1896 pub const DsoExact = struct {1896 pub const FallbackSoname = enum { basename, full_path };
1897 /// Includes the ":" prefix. This is intended to be put into the DSO
1898 /// section verbatim with no corresponding rpaths.
1899 name: []const u8,
1900 };1897 };
19011898
1902 /// Returns `null` in the case of `dso_exact`.1899 pub fn path(input: Input) Path {
1903 pub fn path(input: Input) ?Path {
1904 return switch (input) {1900 return switch (input) {
1905 .object, .archive => |obj| obj.path,1901 .object, .archive => |obj| obj.path,
1906 inline .res, .dso => |x| x.path,1902 inline .res, .dso => |x| x.path,
1907 .dso_exact => null,
1908 };1903 };
1909 }1904 }
19101905
1911 /// Returns `null` in the case of `dso_exact`.1906 pub fn pathAndFile(input: Input) struct { Path, Io.File } {
1912 pub fn pathAndFile(input: Input) ?struct { Path, Io.File } {
1913 return switch (input) {1907 return switch (input) {
1914 .object, .archive => |obj| .{ obj.path, obj.file },1908 .object, .archive => |obj| .{ obj.path, obj.file },
1915 inline .res, .dso => |x| .{ x.path, x.file },1909 inline .res, .dso => |x| .{ x.path, x.file },
1916 .dso_exact => null,
1917 };1910 };
1918 }1911 }
19191912
...@@ -1921,7 +1914,6 @@ pub const Input = union(enum) {...@@ -1921,7 +1914,6 @@ pub const Input = union(enum) {
1921 return switch (input) {1914 return switch (input) {
1922 .object, .archive => |obj| obj.path.basename(),1915 .object, .archive => |obj| obj.path.basename(),
1923 inline .res, .dso => |x| x.path.basename(),1916 inline .res, .dso => |x| x.path.basename(),
1924 .dso_exact => "dso_exact",
1925 };1917 };
1926 }1918 }
1927};1919};
...@@ -1952,9 +1944,7 @@ pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void {...@@ -1952,9 +1944,7 @@ pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void {
1952 man.hash.add(dso.needed);1944 man.hash.add(dso.needed);
1953 man.hash.add(dso.weak);1945 man.hash.add(dso.weak);
1954 man.hash.add(dso.reexport);1946 man.hash.add(dso.reexport);
1955 },1947 man.hash.add(dso.fallback_soname);
1956 .dso_exact => |dso_exact| {
1957 man.hash.addBytes(dso_exact.name);
1958 },1948 },
1959 }1949 }
1960 }1950 }
...@@ -2005,7 +1995,7 @@ pub fn resolveInputs(...@@ -2005,7 +1995,7 @@ pub fn resolveInputs(
2005 .name_query => |name_query| {1995 .name_query => |name_query| {
2006 const query = name_query.query;1996 const query = name_query.query;
20071997
2008 // 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.
2009 assert(!fs.path.isAbsolute(name_query.name));1999 assert(!fs.path.isAbsolute(name_query.name));
20102000
2011 checked_paths.clearRetainingCapacity();2001 checked_paths.clearRetainingCapacity();
...@@ -2190,10 +2180,6 @@ pub fn resolveInputs(...@@ -2190,10 +2180,6 @@ pub fn resolveInputs(
2190 }2180 }
2191 continue;2181 continue;
2192 },2182 },
2193 .dso_exact => |dso_exact| {
2194 try resolved_inputs.append(gpa, .{ .dso_exact = dso_exact });
2195 continue;
2196 },
2197 }2183 }
2198 comptime unreachable;2184 comptime unreachable;
2199 }2185 }
...@@ -2201,8 +2187,8 @@ pub fn resolveInputs(...@@ -2201,8 +2187,8 @@ pub fn resolveInputs(
2201 if (failed_libs.items.len > 0) {2187 if (failed_libs.items.len > 0) {
2202 for (failed_libs.items) |f| {2188 for (failed_libs.items) |f| {
2203 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;2189 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
2204 std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{2190 std.log.err("unable to find {t} system library {q} using strategy {t}. searched paths:{s}", .{
2205 @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths,2191 f.preferred_mode, f.name, f.strategy, searched_paths,
2206 });2192 });
2207 }2193 }
2208 std.process.exit(1);2194 std.process.exit(1);
...@@ -2237,7 +2223,7 @@ fn resolveLibInput(...@@ -2237,7 +2223,7 @@ fn resolveLibInput(
22372223
2238 const lib_name = name_query.name;2224 const lib_name = name_query.name;
22392225
2240 if (target.os.tag.isDarwin() and link_mode == .dynamic) tbd: {2226 if (target.os.tag.isDarwin() and link_mode == .dynamic and !name_query.name_done) tbd: {
2241 // Prefer .tbd over .dylib.2227 // Prefer .tbd over .dylib.
2242 const test_path: Path = .{2228 const test_path: Path = .{
2243 .root_dir = lib_directory,2229 .root_dir = lib_directory,
...@@ -2246,17 +2232,27 @@ fn resolveLibInput(...@@ -2246,17 +2232,27 @@ fn resolveLibInput(
2246 try checked_paths.print(gpa, "\n {f}", .{test_path});2232 try checked_paths.print(gpa, "\n {f}", .{test_path});
2247 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {2233 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2248 error.FileNotFound => break :tbd,2234 error.FileNotFound => break :tbd,
2249 else => |e| fatal("unable to search for tbd library '{f}': {s}", .{ test_path, @errorName(e) }),2235 else => |e| fatal("searching for tbd library {qf}: {t}", .{ test_path, e }),
2250 };2236 };
2251 errdefer file.close(io);2237 errdefer file.close(io);
2252 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 );
2253 }2248 }
22542249
2255 {2250 {
2256 const test_path: Path = .{2251 const test_path: Path = .{
2257 .root_dir = lib_directory,2252 .root_dir = lib_directory,
2258 .sub_path = 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}", .{
2259 target.libPrefix(), lib_name,2254 target.libPrefix(),
2255 lib_name,
2260 switch (link_mode) {2256 switch (link_mode) {
2261 .static => target.staticLibSuffix(),2257 .static => target.staticLibSuffix(),
2262 .dynamic => target.dynamicLibSuffix(),2258 .dynamic => target.dynamicLibSuffix(),
...@@ -2267,7 +2263,7 @@ fn resolveLibInput(...@@ -2267,7 +2263,7 @@ fn resolveLibInput(
2267 switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{2263 switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{
2268 .path = test_path,2264 .path = test_path,
2269 .query = name_query.query,2265 .query = name_query.query,
2270 }, link_mode, color)) {2266 }, link_mode, color, .basename)) {
2271 .no_match => {},2267 .no_match => {},
2272 .ok => return .ok,2268 .ok => return .ok,
2273 }2269 }
...@@ -2275,7 +2271,7 @@ fn resolveLibInput(...@@ -2275,7 +2271,7 @@ fn resolveLibInput(
22752271
2276 // In the case of Darwin, the main check will be .dylib, so here we2272 // In the case of Darwin, the main check will be .dylib, so here we
2277 // additionally check for .so files.2273 // additionally check for .so files.
2278 if (target.os.tag.isDarwin() and link_mode == .dynamic) so: {2274 if (target.os.tag.isDarwin() and link_mode == .dynamic and !name_query.name_done) so: {
2279 const test_path: Path = .{2275 const test_path: Path = .{
2280 .root_dir = lib_directory,2276 .root_dir = lib_directory,
2281 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.so", .{lib_name}),2277 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.so", .{lib_name}),
...@@ -2283,17 +2279,24 @@ fn resolveLibInput(...@@ -2283,17 +2279,24 @@ fn resolveLibInput(
2283 try checked_paths.print(gpa, "\n {f}", .{test_path});2279 try checked_paths.print(gpa, "\n {f}", .{test_path});
2284 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {2280 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2285 error.FileNotFound => break :so,2281 error.FileNotFound => break :so,
2286 else => |e| fatal("unable to search for so library '{f}': {s}", .{2282 else => |e| fatal("unable to search for so library {qf}: {t}", .{ test_path, e }),
2287 test_path, @errorName(e),
2288 }),
2289 };2283 };
2290 errdefer file.close(io);2284 errdefer file.close(io);
2291 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 );
2292 }2295 }
22932296
2294 // In the case of MinGW, the main check will be .lib but we also need to2297 // In the case of MinGW, the main check will be .lib but we also need to
2295 // look for `libfoo.a`.2298 // look for `libfoo.a`.
2296 if (target.isMinGW() and link_mode == .static) mingw: {2299 if (target.isMinGW() and link_mode == .static and !name_query.name_done) mingw: {
2297 const test_path: Path = .{2300 const test_path: Path = .{
2298 .root_dir = lib_directory,2301 .root_dir = lib_directory,
2299 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.a", .{lib_name}),2302 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.a", .{lib_name}),
...@@ -2301,21 +2304,32 @@ fn resolveLibInput(...@@ -2301,21 +2304,32 @@ fn resolveLibInput(
2301 try checked_paths.print(gpa, "\n {f}", .{test_path});2304 try checked_paths.print(gpa, "\n {f}", .{test_path});
2302 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {2305 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2303 error.FileNotFound => break :mingw,2306 error.FileNotFound => break :mingw,
2304 else => |e| fatal("unable to search for static library '{f}': {s}", .{ test_path, @errorName(e) }),2307 else => |e| fatal("unable to search for static library {qf}: {t}", .{ test_path, e }),
2305 };2308 };
2306 errdefer file.close(io);2309 errdefer file.close(io);
2307 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 );
2308 }2320 }
23092321
2310 // In the case of OpenBSD, dynamic libraries are always versioned, without2322 // In the case of OpenBSD, dynamic libraries are always versioned, without
2311 // unversioned symlinks. OpenBSD patches LLD to select the highest-versioned2323 // unversioned symlinks. OpenBSD patches LLD to select the highest-versioned
2312 // shared library, and this code is intended to match that upstream behavior.2324 // shared library, and this code is intended to match that upstream behavior.
2313 if (target.isOpenBSDLibC() and link_mode == .dynamic) versioned: {2325 if (target.isOpenBSDLibC() and link_mode == .dynamic and !name_query.name_done) versioned: {
2314 const prefix = try std.fmt.allocPrint(arena, "lib{s}.so.", .{lib_name});2326 const prefix = try std.fmt.allocPrint(arena, "lib{s}.so.", .{lib_name});
23152327
2316 var dir = lib_directory.handle.openDir(io, ".", .{ .iterate = true }) catch |err| switch (err) {2328 var dir = lib_directory.handle.openDir(io, ".", .{ .iterate = true }) catch |err| switch (err) {
2317 error.NotDir, error.FileNotFound => break :versioned,2329 error.NotDir, error.FileNotFound => break :versioned,
2318 else => |e| fatal("unable to search for shared library '{s}.*': {s}", .{ prefix, @errorName(e) }),2330 else => |e| fatal("unable to search for shared library \"{f}.*\": {t}", .{
2331 std.zig.fmtString(prefix), e,
2332 }),
2319 };2333 };
2320 defer dir.close(io);2334 defer dir.close(io);
23212335
...@@ -2325,7 +2339,7 @@ fn resolveLibInput(...@@ -2325,7 +2339,7 @@ fn resolveLibInput(
23252339
2326 var iter = dir.iterate();2340 var iter = dir.iterate();
2327 while (iter.next(io) catch |err| {2341 while (iter.next(io) catch |err| {
2328 fatal("unable to scan library directory '{s}'", .{@errorName(err)});2342 fatal("scanning library directory: {t}", .{err});
2329 }) |entry| {2343 }) |entry| {
2330 if (entry.kind != .file) continue;2344 if (entry.kind != .file) continue;
2331 if (!std.mem.startsWith(u8, entry.name, prefix)) continue;2345 if (!std.mem.startsWith(u8, entry.name, prefix)) continue;
...@@ -2354,7 +2368,7 @@ fn resolveLibInput(...@@ -2354,7 +2368,7 @@ fn resolveLibInput(
2354 switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{2368 switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{
2355 .path = test_path,2369 .path = test_path,
2356 .query = name_query.query,2370 .query = name_query.query,
2357 }, link_mode, color)) {2371 }, link_mode, color, .basename)) {
2358 .no_match => {},2372 .no_match => {},
2359 .ok => return .ok,2373 .ok => return .ok,
2360 }2374 }
...@@ -2395,6 +2409,7 @@ fn finishResolveLibInput(...@@ -2395,6 +2409,7 @@ fn finishResolveLibInput(
2395 file: Io.File,2409 file: Io.File,
2396 link_mode: std.lang.LinkMode,2410 link_mode: std.lang.LinkMode,
2397 query: UnresolvedInput.Query,2411 query: UnresolvedInput.Query,
2412 fallback_soname: Input.Dso.FallbackSoname,
2398) ResolveLibInputResult {2413) ResolveLibInputResult {
2399 switch (link_mode) {2414 switch (link_mode) {
2400 .static => {2415 .static => {
...@@ -2413,13 +2428,16 @@ fn finishResolveLibInput(...@@ -2413,13 +2428,16 @@ fn finishResolveLibInput(
2413 .hidden = query.hidden,2428 .hidden = query.hidden,
2414 } });2429 } });
2415 },2430 },
2416 .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{2431 .dynamic => resolved_inputs.appendAssumeCapacity(.{
2417 .path = path,2432 .dso = .{
2418 .file = file,2433 .path = path,
2419 .needed = query.needed,2434 .file = file,
2420 .weak = query.weak,2435 .needed = query.needed,
2421 .reexport = query.reexport,2436 .weak = query.weak,
2422 } }),2437 .reexport = query.reexport,
2438 .fallback_soname = fallback_soname,
2439 },
2440 }),
2423 }2441 }
2424 return .ok;2442 return .ok;
2425}2443}
...@@ -2441,11 +2459,37 @@ fn resolvePathInput(...@@ -2441,11 +2459,37 @@ fn resolvePathInput(
2441 color: std.zig.Color,2459 color: std.zig.Color,
2442) Allocator.Error!?ResolveLibInputResult {2460) Allocator.Error!?ResolveLibInputResult {
2443 switch (Compilation.classifyFileExt(pq.path.sub_path)) {2461 switch (Compilation.classifyFileExt(pq.path.sub_path)) {
2444 .static_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .static, color),2462 .static_library => return try resolvePathInputLib(
2445 .shared_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .dynamic, color),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 ),
2446 .object => {2490 .object => {
2447 var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err|2491 var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err|
2448 fatal("failed to open object {f}: {s}", .{ pq.path, @errorName(err) });2492 fatal("failed to open object {f}: {t}", .{ pq.path, err });
2449 errdefer file.close(io);2493 errdefer file.close(io);
2450 try resolved_inputs.append(gpa, .{ .object = .{2494 try resolved_inputs.append(gpa, .{ .object = .{
2451 .path = pq.path,2495 .path = pq.path,
...@@ -2457,7 +2501,7 @@ fn resolvePathInput(...@@ -2457,7 +2501,7 @@ fn resolvePathInput(
2457 },2501 },
2458 .res => {2502 .res => {
2459 var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err|2503 var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err|
2460 fatal("failed to open windows resource {f}: {s}", .{ pq.path, @errorName(err) });2504 fatal("failed to open windows resource {f}: {t}", .{ pq.path, err });
2461 errdefer file.close(io);2505 errdefer file.close(io);
2462 try resolved_inputs.append(gpa, .{ .res = .{2506 try resolved_inputs.append(gpa, .{ .res = .{
2463 .path = pq.path,2507 .path = pq.path,
...@@ -2485,6 +2529,7 @@ fn resolvePathInputLib(...@@ -2485,6 +2529,7 @@ fn resolvePathInputLib(
2485 pq: UnresolvedInput.PathQuery,2529 pq: UnresolvedInput.PathQuery,
2486 link_mode: std.lang.LinkMode,2530 link_mode: std.lang.LinkMode,
2487 color: std.zig.Color,2531 color: std.zig.Color,
2532 fallback_soname: Input.Dso.FallbackSoname,
2488) Allocator.Error!ResolveLibInputResult {2533) Allocator.Error!ResolveLibInputResult {
2489 try resolved_inputs.ensureUnusedCapacity(gpa, 1);2534 try resolved_inputs.ensureUnusedCapacity(gpa, 1);
2490 try archive_dedup.ensureUnusedCapacity(gpa, 1);2535 try archive_dedup.ensureUnusedCapacity(gpa, 1);
...@@ -2498,21 +2543,30 @@ fn resolvePathInputLib(...@@ -2498,21 +2543,30 @@ fn resolvePathInputLib(
2498 }) {2543 }) {
2499 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {2544 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2500 error.FileNotFound => return .no_match,2545 error.FileNotFound => return .no_match,
2501 else => |e| fatal("unable to search for {t} library '{f}': {t}", .{2546 else => |e| fatal("unable to search for {t} library {qf}: {t}", .{
2502 link_mode, std.fmt.alt(test_path, .formatEscapeChar), e,2547 link_mode, std.fmt.alt(test_path, .formatEscapeChar), e,
2503 }),2548 }),
2504 };2549 };
2505 errdefer file.close(io);2550 errdefer file.close(io);
2506 try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len));2551 try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len));
2507 const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err|2552 const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err|
2508 fatal("failed to read '{f}': {t}", .{ std.fmt.alt(test_path, .formatEscapeChar), err });2553 fatal("failed to read {qf}: {t}", .{ test_path, err });
2509 const buf = ld_script_bytes.items[0..n];2554 const buf = ld_script_bytes.items[0..n];
2510 if (mem.startsWith(u8, buf, std.elf.MAGIC) or2555 if (mem.startsWith(u8, buf, std.elf.MAGIC) or
2511 mem.startsWith(u8, buf, std.elf.ARMAG) or2556 mem.startsWith(u8, buf, std.elf.ARMAG) or
2512 mem.startsWith(u8, buf, std.elf.ARMAG_THIN))2557 mem.startsWith(u8, buf, std.elf.ARMAG_THIN))
2513 {2558 {
2514 // Appears to be an ELF or archive file.2559 // Appears to be an ELF or archive file.
2515 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 );
2516 }2570 }
2517 const stat = file.stat(io) catch |err|2571 const stat = file.stat(io) catch |err|
2518 fatal("failed to stat {f}: {t}", .{ test_path, err });2572 fatal("failed to stat {f}: {t}", .{ test_path, err });
...@@ -2563,6 +2617,7 @@ fn resolvePathInputLib(...@@ -2563,6 +2617,7 @@ fn resolvePathInputLib(
2563 unresolved_inputs.appendAssumeCapacity(.{ .name_query = .{2617 unresolved_inputs.appendAssumeCapacity(.{ .name_query = .{
2564 .name = try arena.dupe(u8, arg.path["-l".len..]),2618 .name = try arena.dupe(u8, arg.path["-l".len..]),
2565 .query = query,2619 .query = query,
2620 .name_done = false,
2566 } });2621 } });
2567 } else {2622 } else {
2568 unresolved_inputs.appendAssumeCapacity(.{ .ambiguous_name = .{2623 unresolved_inputs.appendAssumeCapacity(.{ .ambiguous_name = .{
...@@ -2577,12 +2632,19 @@ fn resolvePathInputLib(...@@ -2577,12 +2632,19 @@ fn resolvePathInputLib(
25772632
2578 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {2633 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) {
2579 error.FileNotFound => return .no_match,2634 error.FileNotFound => return .no_match,
2580 else => |e| fatal("unable to search for {s} library {f}: {s}", .{2635 else => |e| fatal("unable to search for {t} library {f}: {t}", .{ link_mode, test_path, e }),
2581 @tagName(link_mode), test_path, @errorName(e),
2582 }),
2583 };2636 };
2584 errdefer file.close(io);2637 errdefer file.close(io);
2585 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 );
2586}2648}
25872649
2588pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object {2650pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object {
...@@ -2605,6 +2667,7 @@ pub fn openDso(io: Io, path: Path, needed: bool, weak: bool, reexport: bool) !In...@@ -2605,6 +2667,7 @@ pub fn openDso(io: Io, path: Path, needed: bool, weak: bool, reexport: bool) !In
2605 .needed = needed,2667 .needed = needed,
2606 .weak = weak,2668 .weak = weak,
2607 .reexport = reexport,2669 .reexport = reexport,
2670 .fallback_soname = .full_path,
2608 };2671 };
2609}2672}
26102673
...@@ -2636,7 +2699,7 @@ pub fn anyObjectInputs(inputs: []const Input) bool {...@@ -2636,7 +2699,7 @@ pub fn anyObjectInputs(inputs: []const Input) bool {
2636pub fn countObjectInputs(inputs: []const Input) usize {2699pub fn countObjectInputs(inputs: []const Input) usize {
2637 var count: usize = 0;2700 var count: usize = 0;
2638 for (inputs) |input| switch (input) {2701 for (inputs) |input| switch (input) {
2639 .dso, .dso_exact => continue,2702 .dso => continue,
2640 .res, .object, .archive => count += 1,2703 .res, .object, .archive => count += 1,
2641 };2704 };
2642 return count;2705 return count;
...@@ -2646,7 +2709,7 @@ pub fn countObjectInputs(inputs: []const Input) usize {...@@ -2646,7 +2709,7 @@ pub fn countObjectInputs(inputs: []const Input) usize {
2646pub fn firstObjectInput(inputs: []const Input) ?Input.Object {2709pub fn firstObjectInput(inputs: []const Input) ?Input.Object {
2647 for (inputs) |input| switch (input) {2710 for (inputs) |input| switch (input) {
2648 .object, .archive => |obj| return obj,2711 .object, .archive => |obj| return obj,
2649 .res, .dso, .dso_exact => continue,2712 .res, .dso => continue,
2650 };2713 };
2651 return null;2714 return null;
2652}2715}
src/link/Coff.zig+1-2
...@@ -3905,7 +3905,7 @@ pub fn loadInput(coff: *Coff, input: link.Input) link.Error!void {...@@ -3905,7 +3905,7 @@ pub fn loadInput(coff: *Coff, input: link.Input) link.Error!void {
3905 const comp = coff.base.comp;3905 const comp = coff.base.comp;
3906 const io = comp.io;3906 const io = comp.io;
39073907
3908 const path = input.path() orelse unreachable;3908 const path = input.path();
3909 const gop = try coff.inputs.getOrPut(comp.gpa, path);3909 const gop = try coff.inputs.getOrPut(comp.gpa, path);
3910 if (gop.found_existing) return;3910 if (gop.found_existing) return;
3911 errdefer _ = coff.inputs.swapRemove(path);3911 errdefer _ = coff.inputs.swapRemove(path);
...@@ -3935,7 +3935,6 @@ pub fn loadInput(coff: *Coff, input: link.Input) link.Error!void {...@@ -3935,7 +3935,6 @@ pub fn loadInput(coff: *Coff, input: link.Input) link.Error!void {
3935 coff.loadDll(dso.path, &fr) catch |err|3935 coff.loadDll(dso.path, &fr) catch |err|
3936 return coff.failLoadInput(err, &fr, dso.path);3936 return coff.failLoadInput(err, &fr, dso.path);
3937 },3937 },
3938 .dso_exact => unreachable,
3939 }3938 }
3940}3939}
39413940
src/link/Elf.zig+9-4
...@@ -722,7 +722,6 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {...@@ -722,7 +722,6 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {
722 const argv = &self.dump_argv_list;722 const argv = &self.dump_argv_list;
723 switch (input) {723 switch (input) {
724 .res => unreachable,724 .res => unreachable,
725 .dso_exact => |dso_exact| try argv.appendSlice(gpa, &.{ "-l", dso_exact.name }),
726 .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)),725 .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)),
727 .dso => |dso| try argv.append(gpa, try dso.path.toString(comp.arena)),726 .dso => |dso| try argv.append(gpa, try dso.path.toString(comp.arena)),
728 }727 }
...@@ -730,14 +729,13 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {...@@ -730,14 +729,13 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {
730729
731 switch (input) {730 switch (input) {
732 .res => unreachable,731 .res => unreachable,
733 .dso_exact => @panic("TODO"),
734 .object => |obj| try parseObject(self, obj),732 .object => |obj| try parseObject(self, obj),
735 .archive => |obj| if (self.base.isStaticLib()) {733 .archive => |obj| if (self.base.isStaticLib()) {
736 // Ignore static library inputs when generating a static library.734 // Ignore static library inputs when generating a static library.
737 } else {735 } else {
738 try parseArchive(gpa, io, diags, &self.file_handles, &self.files, target, debug_fmt_strip, default_sym_version, &self.objects, obj);736 try parseArchive(gpa, io, diags, &self.file_handles, &self.files, target, debug_fmt_strip, default_sym_version, &self.objects, obj);
739 },737 },
740 .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),
741 }739 }
742}740}
743741
...@@ -1108,6 +1106,7 @@ fn parseArchive(...@@ -1108,6 +1106,7 @@ fn parseArchive(
11081106
1109fn parseDso(1107fn parseDso(
1110 gpa: Allocator,1108 gpa: Allocator,
1109 arena: Allocator,
1111 io: Io,1110 io: Io,
1112 diags: *Diags,1111 diags: *Diags,
1113 dso: link.Input.Dso,1112 dso: link.Input.Dso,
...@@ -1124,7 +1123,12 @@ fn parseDso(...@@ -1124,7 +1123,12 @@ fn parseDso(
1124 var header = try SharedObject.parseHeader(gpa, io, diags, dso.path, handle, stat, target);1123 var header = try SharedObject.parseHeader(gpa, io, diags, dso.path, handle, stat, target);
1125 defer header.deinit(gpa);1124 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
1129 const gop = try shared_objects.getOrPut(gpa, soname);1133 const gop = try shared_objects.getOrPut(gpa, soname);
1130 if (gop.found_existing) return;1134 if (gop.found_existing) return;
...@@ -1156,6 +1160,7 @@ fn parseDso(...@@ -1156,6 +1160,7 @@ fn parseDso(
1156 .symbols_extra = .empty,1160 .symbols_extra = .empty,
1157 .symbols_resolver = .empty,1161 .symbols_resolver = .empty,
1158 .output_symtab_ctx = .{},1162 .output_symtab_ctx = .{},
1163 .fallback_soname = fallback_soname,
1159 },1164 },
1160 });1165 });
1161 const so = fileLookup(files.*, index, null).?.shared_object;1166 const so = fileLookup(files.*, index, null).?.shared_object;
src/link/Elf/SharedObject.zig+3-1
...@@ -31,6 +31,8 @@ alive: bool,...@@ -31,6 +31,8 @@ alive: bool,
3131
32output_symtab_ctx: Elf.SymtabCtx,32output_symtab_ctx: Elf.SymtabCtx,
3333
34fallback_soname: []const u8,
35
34pub fn deinit(so: *SharedObject, gpa: Allocator) void {36pub fn deinit(so: *SharedObject, gpa: Allocator) void {
35 gpa.free(so.path.sub_path);37 gpa.free(so.path.sub_path);
36 so.parsed.deinit(gpa);38 so.parsed.deinit(gpa);
...@@ -427,7 +429,7 @@ pub fn asFile(self: *SharedObject) File {...@@ -427,7 +429,7 @@ pub fn asFile(self: *SharedObject) File {
427}429}
428430
429pub fn soname(self: *SharedObject) []const u8 {431pub fn soname(self: *SharedObject) []const u8 {
430 return self.parsed.soname() orelse self.path.basename();432 return self.parsed.soname() orelse self.fallback_soname;
431}433}
432434
433pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void {435pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void {
src/link/Elf2.zig+15-22
...@@ -6230,34 +6230,19 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void...@@ -6230,34 +6230,19 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void
6230 .dso => |dso| {6230 .dso => |dso| {
6231 try elf.needed.ensureUnusedCapacity(elf.base.comp.gpa, 1);6231 try elf.needed.ensureUnusedCapacity(elf.base.comp.gpa, 1);
6232 var fr = dso.file.reader(io, &buf);6232 var fr = dso.file.reader(io, &buf);
6233 elf.loadDso(dso.path, &fr) catch |err| switch (err) {6233 elf.loadDso(dso.path, dso.fallback_soname, &fr) catch |err| switch (err) {
6234 else => |e| return e,6234 else => |e| return e,
6235 error.EndOfStream => return diags.failParse(6235 error.EndOfStream => return diags.failParse(dso.path, "unexpected eof", .{}),
6236 dso.path,
6237 "unexpected eof",
6238 .{},
6239 ),
6240 error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail(6236 error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail(
6241 "failed to read \"{f}\": {t}",6237 "failed to read {qf}: {t}",
6242 .{ dso.path.fmtEscapeString(), e },6238 .{ dso.path, e },
6243 ),6239 ),
6244 error.ReadFailed => switch (fr.err.?) {6240 error.ReadFailed => switch (fr.err.?) {
6245 error.Canceled => |e| return e,6241 error.Canceled => |e| return e,
6246 else => |e| return diags.fail(6242 else => |e| return diags.fail("failed to read {qf}: {t}", .{ dso.path, e }),
6247 "failed to read \"{f}\": {t}",
6248 .{ dso.path.fmtEscapeString(), e },
6249 ),
6250 },6243 },
6251 };6244 };
6252 },6245 },
6253 .dso_exact => |dso_exact| {
6254 log.debug("load dso_exact '{f}'", .{std.zig.fmtString(dso_exact.name)});
6255 if (elf.shndx.dynamic != .UNDEF) {
6256 try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, dso_exact.name), {});
6257 }
6258 // TODO: we need to get a resolved file path from the frontend, because we need to read
6259 // the shared object to discover symbol types.
6260 },
6261 }6246 }
6262}6247}
6263fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {6248fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {
...@@ -6867,7 +6852,12 @@ fn populateArchiveMemberName(elf: *Elf, member_ar_hdr: *std.elf.ar_hdr, member_n...@@ -6867,7 +6852,12 @@ fn populateArchiveMemberName(elf: *Elf, member_ar_hdr: *std.elf.ar_hdr, member_n
6867 @memcpy(dest_slice[0 .. dest_slice.len - 2], member_name);6852 @memcpy(dest_slice[0 .. dest_slice.len - 2], member_name);
6868 @memcpy(dest_slice[dest_slice.len - 2 ..], "/\n"); // yes, the terminator is weird6853 @memcpy(dest_slice[dest_slice.len - 2 ..], "/\n"); // yes, the terminator is weird
6869}6854}
6870fn 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 {
6871 const comp = elf.base.comp;6861 const comp = elf.base.comp;
6872 const gpa = comp.gpa;6862 const gpa = comp.gpa;
6873 const diags = &comp.link_diags;6863 const diags = &comp.link_diags;
...@@ -6963,7 +6953,10 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars...@@ -6963,7 +6953,10 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
6963 }6953 }
6964 break std.mem.sliceTo(dynstr[@intCast(val)..], 0);6954 break std.mem.sliceTo(dynstr[@intCast(val)..], 0);
6965 }6955 }
6966 } 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 };
6967 try elf.needed.put(gpa, try elf.string(.dynstr, soname), {});6960 try elf.needed.put(gpa, try elf.string(.dynstr, soname), {});
69686961
6969 // Scan the symbol table and populate `elf.dso_globals`.6962 // Scan the symbol table and populate `elf.dso_globals`.
src/link/Lld.zig+21-14
...@@ -309,9 +309,9 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) link.Error!void {...@@ -309,9 +309,9 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) link.Error!void {
309309
310 try object_files.ensureUnusedCapacity(arena, comp.link_inputs.len);310 try object_files.ensureUnusedCapacity(arena, comp.link_inputs.len);
311 for (comp.link_inputs) |input| switch (input) {311 for (comp.link_inputs) |input| switch (input) {
312 .dso, .dso_exact, .archive => {}, // static archives should not contain shared libraries or other static archives312 .dso, .archive => {}, // static archives should not contain shared libraries or other static archives
313 .res, .object => {313 .res, .object => {
314 const path = try input.path().?.toStringZ(arena);314 const path = try input.path().toStringZ(arena);
315 object_files.appendAssumeCapacity(path);315 object_files.appendAssumeCapacity(path);
316 },316 },
317 };317 };
...@@ -551,7 +551,6 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {...@@ -551,7 +551,6 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
551551
552 try argv.ensureUnusedCapacity(comp.link_inputs.len);552 try argv.ensureUnusedCapacity(comp.link_inputs.len);
553 for (comp.link_inputs) |link_input| switch (link_input) {553 for (comp.link_inputs) |link_input| switch (link_input) {
554 .dso_exact => unreachable, // not applicable to PE/COFF
555 inline .dso, .res => |x| {554 inline .dso, .res => |x| {
556 argv.appendAssumeCapacity(try x.path.toString(arena));555 argv.appendAssumeCapacity(try x.path.toString(arena));
557 },556 },
...@@ -1114,10 +1113,6 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -1114,10 +1113,6 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
1114 }1113 }
1115 try argv.append(try obj.path.toString(arena));1114 try argv.append(try obj.path.toString(arena));
1116 },1115 },
1117 .dso_exact => |dso_exact| {
1118 assert(dso_exact.name[0] == ':');
1119 try argv.appendSlice(&.{ "-l", dso_exact.name });
1120 },
1121 };1116 };
11221117
1123 if (whole_archive) {1118 if (whole_archive) {
...@@ -1159,9 +1154,16 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -1159,9 +1154,16 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
1159 argv.appendAssumeCapacity("--as-needed");1154 argv.appendAssumeCapacity("--as-needed");
1160 var as_needed = true;1155 var as_needed = true;
11611156
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
1162 for (base.comp.link_inputs) |link_input| switch (link_input) {1164 for (base.comp.link_inputs) |link_input| switch (link_input) {
1163 .res => unreachable, // Windows-only1165 .res => unreachable, // Windows-only
1164 .object, .archive, .dso_exact => continue,1166 .object, .archive => continue,
1165 .dso => |dso| {1167 .dso => |dso| {
1166 const lib_as_needed = !dso.needed;1168 const lib_as_needed = !dso.needed;
1167 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {1169 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
...@@ -1176,11 +1178,17 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -1176,11 +1178,17 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
1176 },1178 },
1177 }1179 }
11781180
1179 // By this time, we depend on these libs being dynamically linked1181 // By this time, we depend on these libs being dynamically linked libraries and
1180 // libraries and not static libraries (the check for that needs to be earlier),1182 // not static libraries (the check for that needs to be earlier), but they could
1181 // but they could be full paths to .so files, in which case we1183 // be full file paths, in which case we don't want to use the "-l:" strategy.
1182 // want to avoid prepending "-l".1184 switch (dso.fallback_soname) {
1183 argv.appendAssumeCapacity(try dso.path.toString(arena));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)),
1191 }
1184 },1192 },
1185 };1193 };
11861194
...@@ -1588,7 +1596,6 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {...@@ -1588,7 +1596,6 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
1588 .dso => |dso| {1596 .dso => |dso| {
1589 try argv.append(try dso.path.toString(arena));1597 try argv.append(try dso.path.toString(arena));
1590 },1598 },
1591 .dso_exact => unreachable,
1592 .res => unreachable,1599 .res => unreachable,
1593 };1600 };
1594 if (whole_archive) {1601 if (whole_archive) {
src/link/MachO.zig+13-13
...@@ -372,7 +372,6 @@ pub fn flush(...@@ -372,7 +372,6 @@ pub fn flush(
372 for (comp.link_inputs) |link_input| switch (link_input) {372 for (comp.link_inputs) |link_input| switch (link_input) {
373 .dso => continue, // handled below373 .dso => continue, // handled below
374 .object, .archive => positionals.appendAssumeCapacity(link_input),374 .object, .archive => positionals.appendAssumeCapacity(link_input),
375 .dso_exact => @panic("TODO"),
376 .res => unreachable,375 .res => unreachable,
377 };376 };
378377
...@@ -406,7 +405,7 @@ pub fn flush(...@@ -406,7 +405,7 @@ pub fn flush(
406405
407 for (positionals.items) |link_input| {406 for (positionals.items) |link_input| {
408 self.classifyInputFile(link_input) catch |err|407 self.classifyInputFile(link_input) catch |err|
409 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});408 diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err});
410 }409 }
411410
412 var system_libs = std.array_list.Managed(SystemLib).init(gpa);411 var system_libs = std.array_list.Managed(SystemLib).init(gpa);
...@@ -447,11 +446,11 @@ pub fn flush(...@@ -447,11 +446,11 @@ pub fn flush(
447 };446 };
448447
449 for (comp.link_inputs) |link_input| switch (link_input) {448 for (comp.link_inputs) |link_input| switch (link_input) {
450 .object, .archive, .dso_exact => continue,449 .object, .archive => continue,
451 .res => unreachable,450 .res => unreachable,
452 .dso => {451 .dso => {
453 self.classifyInputFile(link_input) catch |err|452 self.classifyInputFile(link_input) catch |err|
454 diags.addParseError(link_input.path().?, "failed to parse input file: {s}", .{@errorName(err)});453 diags.addParseError(link_input.path(), "failed to parse input file: {t}", .{err});
455 },454 },
456 };455 };
457456
...@@ -655,8 +654,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -655,8 +654,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
655 for (comp.link_inputs) |link_input| switch (link_input) {654 for (comp.link_inputs) |link_input| switch (link_input) {
656 .object, .archive => |obj| try argv.append(try obj.path.toString(arena)),655 .object, .archive => |obj| try argv.append(try obj.path.toString(arena)),
657 .res => |res| try argv.append(try res.path.toString(arena)),656 .res => |res| try argv.append(try res.path.toString(arena)),
658 .dso => |dso| try argv.append(try dso.path.toString(arena)),657 .dso => |dso| {
659 .dso_exact => |dso_exact| try argv.appendSlice(&.{ "-l", dso_exact.name }),658 try argv.append(try dso.path.toString(arena));
659 },
660 };660 };
661661
662 for (comp.c_objects.items) |c_object| {662 for (comp.c_objects.items) |c_object| {
...@@ -748,7 +748,6 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -748,7 +748,6 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
748 if (obj.must_link) try argv.append("-force_load"); // TODO: verify this748 if (obj.must_link) try argv.append("-force_load"); // TODO: verify this
749 try argv.append(try obj.path.toString(arena));749 try argv.append(try obj.path.toString(arena));
750 },750 },
751 .dso_exact => |dso_exact| try argv.appendSlice(&.{ "-l", dso_exact.name }),
752 };751 };
753752
754 for (comp.c_objects.items) |c_object| {753 for (comp.c_objects.items) |c_object| {
...@@ -775,16 +774,18 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -775,16 +774,18 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
775 }774 }
776775
777 for (comp.link_inputs) |link_input| switch (link_input) {776 for (comp.link_inputs) |link_input| switch (link_input) {
778 .object, .archive, .dso_exact => continue, // handled above777 .object, .archive => continue, // handled above
779 .res => unreachable, // windows only778 .res => unreachable, // windows only
780 .dso => |dso| {779 .dso => |dso| {
780 try argv.ensureUnusedCapacity(2);
781 if (dso.needed) {781 if (dso.needed) {
782 try argv.appendSlice(&.{ "-needed-l", try dso.path.toString(arena) });782 argv.appendAssumeCapacity("-needed-l");
783 } else if (dso.weak) {783 } else if (dso.weak) {
784 try argv.appendSlice(&.{ "-weak-l", try dso.path.toString(arena) });784 argv.appendAssumeCapacity("-weak-l");
785 } else {785 } else {
786 try argv.appendSlice(&.{ "-l", try dso.path.toString(arena) });786 argv.appendAssumeCapacity("-l");
787 }787 }
788 argv.appendAssumeCapacity(try dso.path.toString(arena));
788 },789 },
789 };790 };
790791
...@@ -870,7 +871,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {...@@ -870,7 +871,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
870 const comp = self.base.comp;871 const comp = self.base.comp;
871 const io = comp.io;872 const io = comp.io;
872873
873 const path, const file = input.pathAndFile().?;874 const path, const file = input.pathAndFile();
874 // TODO don't classify now, it's too late. The input file has already been classified875 // TODO don't classify now, it's too late. The input file has already been classified
875 log.debug("classifying input file {f}", .{path});876 log.debug("classifying input file {f}", .{path});
876877
...@@ -4478,7 +4479,6 @@ const SystemLib = struct {...@@ -4478,7 +4479,6 @@ const SystemLib = struct {
44784479
4479 fn fromLinkInput(link_input: link.Input) SystemLib {4480 fn fromLinkInput(link_input: link.Input) SystemLib {
4480 return switch (link_input) {4481 return switch (link_input) {
4481 .dso_exact => unreachable,
4482 .res => unreachable,4482 .res => unreachable,
4483 .object, .archive => |obj| .{4483 .object, .archive => |obj| .{
4484 .path = obj.path,4484 .path = obj.path,
src/link/MachO/relocatable.zig+3-3
...@@ -19,7 +19,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -19,7 +19,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
19 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all19 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all
20 // debug info segments/sections (this is apparently by design by Apple), we copy20 // debug info segments/sections (this is apparently by design by Apple), we copy
21 // the *only* input file over.21 // the *only* input file over.
22 const path = positionals.items[0].path().?;22 const path = positionals.items[0].path();
23 const in_file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err|23 const in_file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err|
24 return diags.fail("failed to open {f}: {s}", .{ path, @errorName(err) });24 return diags.fail("failed to open {f}: {s}", .{ path, @errorName(err) });
25 const stat = in_file.stat(io) catch |err|25 const stat = in_file.stat(io) catch |err|
...@@ -31,7 +31,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -31,7 +31,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
3131
32 for (positionals.items) |link_input| {32 for (positionals.items) |link_input| {
33 macho_file.classifyInputFile(link_input) catch |err|33 macho_file.classifyInputFile(link_input) catch |err|
34 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});34 diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err});
35 }35 }
3636
37 if (diags.hasErrors()) return error.AlreadyReported;37 if (diags.hasErrors()) return error.AlreadyReported;
...@@ -102,7 +102,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -102,7 +102,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
102102
103 for (positionals.items) |link_input| {103 for (positionals.items) |link_input| {
104 macho_file.classifyInputFile(link_input) catch |err|104 macho_file.classifyInputFile(link_input) catch |err|
105 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});105 diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err});
106 }106 }
107107
108 if (diags.hasErrors()) return error.AlreadyReported;108 if (diags.hasErrors()) return error.AlreadyReported;
src/link/Wasm.zig-2
...@@ -3788,7 +3788,6 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {...@@ -3788,7 +3788,6 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
3788 const argv = &wasm.dump_argv_list;3788 const argv = &wasm.dump_argv_list;
3789 switch (input) {3789 switch (input) {
3790 .res => unreachable,3790 .res => unreachable,
3791 .dso_exact => unreachable,
3792 .dso => unreachable,3791 .dso => unreachable,
3793 .object, .archive => |obj| {3792 .object, .archive => |obj| {
3794 try argv.append(gpa, try obj.path.toString(comp.arena));3793 try argv.append(gpa, try obj.path.toString(comp.arena));
...@@ -3798,7 +3797,6 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {...@@ -3798,7 +3797,6 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
37983797
3799 switch (input) {3798 switch (input) {
3800 .res => unreachable,3799 .res => unreachable,
3801 .dso_exact => unreachable,
3802 .dso => unreachable,3800 .dso => unreachable,
3803 .object => |obj| try parseObject(wasm, obj),3801 .object => |obj| try parseObject(wasm, obj),
3804 .archive => |obj| try parseArchive(wasm, obj),3802 .archive => |obj| try parseArchive(wasm, obj),
src/main.zig+36-13
...@@ -70,10 +70,6 @@ var stdin_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;...@@ -70,10 +70,6 @@ var stdin_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;
70/// This can be global since stdout is a singleton.70/// This can be global since stdout is a singleton.
71var stdout_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;71var stdout_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;
7272
73/// Shaming all the locations that inappropriately use an O(N) search algorithm.
74/// Please delete this and fix the compilation errors!
75pub const @"bad O(N)" = void;
76
77const normal_usage =73const normal_usage =
78 \\Usage: zig [command] [options]74 \\Usage: zig [command] [options]
79 \\75 \\
...@@ -807,7 +803,7 @@ const compile_usage =...@@ -807,7 +803,7 @@ const compile_usage =
807 \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features803 \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features
808 \\ --debug-log [scope] Enable printing debug/info log messages for scope804 \\ --debug-log [scope] Enable printing debug/info log messages for scope
809 \\ --debug-compile-errors Crash with helpful diagnostics at the first compile error805 \\ --debug-compile-errors Crash with helpful diagnostics at the first compile error
810 \\ --debug-link-snapshot Enable dumping of the linker's state in JSON format806 \\ --debug-link-snapshot Dump linker state and output file information for troubleshooting
811 \\ --debug-rt[=mode] Build compiler runtime libraries with [mode] optimization807 \\ --debug-rt[=mode] Build compiler runtime libraries with [mode] optimization
812 \\ (debug if [=mode] is omitted)808 \\ (debug if [=mode] is omitted)
813 \\ --debug-incremental Enable incremental compilation debug features809 \\ --debug-incremental Enable incremental compilation debug features
...@@ -1355,6 +1351,7 @@ fn buildOutputType(...@@ -1355,6 +1351,7 @@ fn buildOutputType(
1355 .search_strategy = lib_search_strategy,1351 .search_strategy = lib_search_strategy,
1356 .allow_so_scripts = allow_so_scripts,1352 .allow_so_scripts = allow_so_scripts,
1357 },1353 },
1354 .name_done = false,
1358 } });1355 } });
1359 } else if (mem.eql(u8, arg, "--needed-library") or1356 } else if (mem.eql(u8, arg, "--needed-library") or
1360 mem.eql(u8, arg, "-needed-l") or1357 mem.eql(u8, arg, "-needed-l") or
...@@ -1370,6 +1367,7 @@ fn buildOutputType(...@@ -1370,6 +1367,7 @@ fn buildOutputType(
1370 .search_strategy = lib_search_strategy,1367 .search_strategy = lib_search_strategy,
1371 .allow_so_scripts = allow_so_scripts,1368 .allow_so_scripts = allow_so_scripts,
1372 },1369 },
1370 .name_done = false,
1373 } });1371 } });
1374 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {1372 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
1375 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{1373 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
...@@ -1381,6 +1379,7 @@ fn buildOutputType(...@@ -1381,6 +1379,7 @@ fn buildOutputType(
1381 .search_strategy = lib_search_strategy,1379 .search_strategy = lib_search_strategy,
1382 .allow_so_scripts = allow_so_scripts,1380 .allow_so_scripts = allow_so_scripts,
1383 },1381 },
1382 .name_done = false,
1384 } });1383 } });
1385 } else if (mem.eql(u8, arg, "-D")) {1384 } else if (mem.eql(u8, arg, "-D")) {
1386 try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });1385 try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });
...@@ -1843,6 +1842,7 @@ fn buildOutputType(...@@ -1843,6 +1842,7 @@ fn buildOutputType(
1843 .search_strategy = lib_search_strategy,1842 .search_strategy = lib_search_strategy,
1844 .allow_so_scripts = allow_so_scripts,1843 .allow_so_scripts = allow_so_scripts,
1845 },1844 },
1845 .name_done = false,
1846 } });1846 } });
1847 } else if (mem.cutPrefix(u8, arg, "-needed-l")) |name| {1847 } else if (mem.cutPrefix(u8, arg, "-needed-l")) |name| {
1848 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{1848 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
...@@ -1854,6 +1854,7 @@ fn buildOutputType(...@@ -1854,6 +1854,7 @@ fn buildOutputType(
1854 .search_strategy = lib_search_strategy,1854 .search_strategy = lib_search_strategy,
1855 .allow_so_scripts = allow_so_scripts,1855 .allow_so_scripts = allow_so_scripts,
1856 },1856 },
1857 .name_done = false,
1857 } });1858 } });
1858 } else if (mem.cutPrefix(u8, arg, "-weak-l")) |name| {1859 } else if (mem.cutPrefix(u8, arg, "-weak-l")) |name| {
1859 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{1860 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
...@@ -1865,6 +1866,7 @@ fn buildOutputType(...@@ -1865,6 +1866,7 @@ fn buildOutputType(
1865 .search_strategy = lib_search_strategy,1866 .search_strategy = lib_search_strategy,
1866 .allow_so_scripts = allow_so_scripts,1867 .allow_so_scripts = allow_so_scripts,
1867 },1868 },
1869 .name_done = false,
1868 } });1870 } });
1869 } else if (mem.startsWith(u8, arg, "-D")) {1871 } else if (mem.startsWith(u8, arg, "-D")) {
1870 try cc_argv.append(arena, arg);1872 try cc_argv.append(arena, arg);
...@@ -2083,12 +2085,24 @@ fn buildOutputType(...@@ -2083,12 +2085,24 @@ fn buildOutputType(
2083 // We don't know whether this library is part of libc or libc++ until2085 // We don't know whether this library is part of libc or libc++ until
2084 // we resolve the target, so we simply append to the list for now.2086 // we resolve the target, so we simply append to the list for now.
2085 if (mem.startsWith(u8, it.only_arg, ":")) {2087 if (mem.startsWith(u8, it.only_arg, ":")) {
2086 // -l :path/to/filename is used when callers need2088 // -l :path/to/filename indicates:
2087 // more control over what's in the resulting2089 // * No extra rpaths.
2088 // binary: no extra rpaths and DSO filename exactly2090 // * NEEDED entry should be exactly the string
2089 // as provided. CGo compilation depends on this.2091 // after the colon. No file system paths prepended.
2090 try create_module.cli_link_inputs.append(arena, .{ .dso_exact = .{2092 // * The DSO still must be found at compile/link
2091 .name = it.only_arg,2093 // time and its entries used to resolve symbols.
2094 // CGo compilation depends on this.
2095 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2096 .name = it.only_arg[1..],
2097 .query = .{
2098 .must_link = must_link,
2099 .needed = needed,
2100 .weak = false,
2101 .preferred_mode = lib_preferred_mode,
2102 .search_strategy = lib_search_strategy,
2103 .allow_so_scripts = allow_so_scripts,
2104 },
2105 .name_done = true,
2092 } });2106 } });
2093 } else {2107 } else {
2094 const compiler_rt_classification = target_util.classifyCompilerRtLibName(it.only_arg);2108 const compiler_rt_classification = target_util.classifyCompilerRtLibName(it.only_arg);
...@@ -2114,6 +2128,7 @@ fn buildOutputType(...@@ -2114,6 +2128,7 @@ fn buildOutputType(
2114 .search_strategy = lib_search_strategy,2128 .search_strategy = lib_search_strategy,
2115 .allow_so_scripts = allow_so_scripts,2129 .allow_so_scripts = allow_so_scripts,
2116 },2130 },
2131 .name_done = false,
2117 } });2132 } });
2118 }2133 }
2119 }2134 }
...@@ -2544,6 +2559,7 @@ fn buildOutputType(...@@ -2544,6 +2559,7 @@ fn buildOutputType(
2544 .search_strategy = lib_search_strategy,2559 .search_strategy = lib_search_strategy,
2545 .allow_so_scripts = allow_so_scripts,2560 .allow_so_scripts = allow_so_scripts,
2546 },2561 },
2562 .name_done = false,
2547 } }),2563 } }),
2548 .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),2564 .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),
2549 .headerpad_max_install_names => headerpad_max_install_names = true,2565 .headerpad_max_install_names => headerpad_max_install_names = true,
...@@ -2913,6 +2929,7 @@ fn buildOutputType(...@@ -2913,6 +2929,7 @@ fn buildOutputType(
2913 .search_strategy = lib_search_strategy,2929 .search_strategy = lib_search_strategy,
2914 .allow_so_scripts = allow_so_scripts,2930 .allow_so_scripts = allow_so_scripts,
2915 },2931 },
2932 .name_done = false,
2916 } });2933 } });
2917 } else if (mem.cutPrefix(u8, arg, "-weak-l")) |rest| {2934 } else if (mem.cutPrefix(u8, arg, "-weak-l")) |rest| {
2918 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{2935 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
...@@ -2924,6 +2941,7 @@ fn buildOutputType(...@@ -2924,6 +2941,7 @@ fn buildOutputType(
2924 .search_strategy = lib_search_strategy,2941 .search_strategy = lib_search_strategy,
2925 .allow_so_scripts = allow_so_scripts,2942 .allow_so_scripts = allow_so_scripts,
2926 },2943 },
2944 .name_done = false,
2927 } });2945 } });
2928 } else if (mem.eql(u8, arg, "-weak_library")) {2946 } else if (mem.eql(u8, arg, "-weak_library")) {
2929 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{2947 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
...@@ -2935,6 +2953,7 @@ fn buildOutputType(...@@ -2935,6 +2953,7 @@ fn buildOutputType(
2935 .search_strategy = lib_search_strategy,2953 .search_strategy = lib_search_strategy,
2936 .allow_so_scripts = allow_so_scripts,2954 .allow_so_scripts = allow_so_scripts,
2937 },2955 },
2956 .name_done = false,
2938 } });2957 } });
2939 } else if (mem.eql(u8, arg, "-compatibility_version")) {2958 } else if (mem.eql(u8, arg, "-compatibility_version")) {
2940 const compat_version = linker_args_it.nextOrFatal();2959 const compat_version = linker_args_it.nextOrFatal();
...@@ -4175,6 +4194,10 @@ fn createModule(...@@ -4175,6 +4194,10 @@ fn createModule(
4175 fatal("cannot use absolute path as a system library: {s}", .{lib_name});4194 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
4176 }4195 }
41774196
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
4178 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);4201 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
4179 any_name_queries_remaining = true;4202 any_name_queries_remaining = true;
4180 },4203 },
...@@ -4269,10 +4292,10 @@ fn createModule(...@@ -4269,10 +4292,10 @@ fn createModule(
4269 &create_module.link_inputs,4292 &create_module.link_inputs,
4270 create_module.lib_directories.items,4293 create_module.lib_directories.items,
4271 color,4294 color,
4272 ) catch |err| fatal("failed to resolve link inputs: {s}", .{@errorName(err)});4295 ) catch |err| fatal("failed to resolve link inputs: {t}", .{err});
42734296
4274 if (!create_module.opts.any_dyn_libs) for (create_module.link_inputs.items) |item| switch (item) {4297 if (!create_module.opts.any_dyn_libs) for (create_module.link_inputs.items) |item| switch (item) {
4275 .dso, .dso_exact => {4298 .dso => {
4276 create_module.opts.any_dyn_libs = true;4299 create_module.opts.any_dyn_libs = true;
4277 break;4300 break;
4278 },4301 },