Commit 819716b59f
src/link.zig
@@ -1891,30 +1891,69 @@ pub fn resolveInputs(
syslib: while (unresolved_inputs.pop()) |unresolved_input| {
const name_query: UnresolvedInput.NameQuery = switch (unresolved_input) {
.name_query => |nq| nq,
- .ambiguous_name => |an| an: {
- const lib_name, const link_mode = stripLibPrefixAndSuffix(an.name, target) orelse {
- try resolvePathInput(gpa, arena, unresolved_inputs, resolved_inputs, &ld_script_bytes, target, .{
+ .ambiguous_name => |an| {
+ // First check the path relative to the current working directory.
+ // If the file is a library and is not found there, check the library search paths as well.
+ // This is consistent with the behavior of GNU ld.
+ if (try resolvePathInput(
+ gpa,
+ arena,
+ unresolved_inputs,
+ resolved_inputs,
+ &ld_script_bytes,
+ target,
+ .{
.path = Path.initCwd(an.name),
.query = an.query,
- }, color);
- continue;
- };
- break :an .{
- .name = lib_name,
- .query = .{
- .needed = an.query.needed,
- .weak = an.query.weak,
- .reexport = an.query.reexport,
- .must_link = an.query.must_link,
- .hidden = an.query.hidden,
- .allow_so_scripts = an.query.allow_so_scripts,
- .preferred_mode = link_mode,
- .search_strategy = .no_fallback,
},
- };
+ color,
+ )) |lib_result| {
+ switch (lib_result) {
+ .ok => continue :syslib,
+ .no_match => {
+ for (lib_directories) |lib_directory| {
+ switch ((try resolvePathInput(
+ gpa,
+ arena,
+ unresolved_inputs,
+ resolved_inputs,
+ &ld_script_bytes,
+ target,
+ .{
+ .path = .{
+ .root_dir = lib_directory,
+ .sub_path = an.name,
+ },
+ .query = an.query,
+ },
+ color,
+ )).?) {
+ .ok => continue :syslib,
+ .no_match => {},
+ }
+ }
+ fatal("{s}: file listed in linker script not found", .{an.name});
+ },
+ }
+ }
+ continue;
},
.path_query => |pq| {
- try resolvePathInput(gpa, arena, unresolved_inputs, resolved_inputs, &ld_script_bytes, target, pq, color);
+ if (try resolvePathInput(
+ gpa,
+ arena,
+ unresolved_inputs,
+ resolved_inputs,
+ &ld_script_bytes,
+ target,
+ pq,
+ color,
+ )) |lib_result| {
+ switch (lib_result) {
+ .ok => {},
+ .no_match => fatal("{}: file not found", .{pq.path}),
+ }
+ }
continue;
},
.dso_exact => |dso_exact| {
@@ -2176,10 +2215,10 @@ fn resolvePathInput(
target: std.Target,
pq: UnresolvedInput.PathQuery,
color: std.zig.Color,
-) Allocator.Error!void {
- switch (switch (Compilation.classifyFileExt(pq.path.sub_path)) {
- .static_library => try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color),
- .shared_library => try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color),
+) Allocator.Error!?ResolveLibInputResult {
+ switch (Compilation.classifyFileExt(pq.path.sub_path)) {
+ .static_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color),
+ .shared_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color),
.object => {
var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|
fatal("failed to open object {}: {s}", .{ pq.path, @errorName(err) });
@@ -2190,7 +2229,7 @@ fn resolvePathInput(
.must_link = pq.query.must_link,
.hidden = pq.query.hidden,
} });
- return;
+ return null;
},
.res => {
var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|
@@ -2200,12 +2239,9 @@ fn resolvePathInput(
.path = pq.path,
.file = file,
} });
- return;
+ return null;
},
else => fatal("{}: unrecognized file extension", .{pq.path}),
- }) {
- .ok => {},
- .no_match => fatal("{}: file not found", .{pq.path}),
}
}
@@ -2226,9 +2262,11 @@ fn resolvePathInputLib(
try resolved_inputs.ensureUnusedCapacity(gpa, 1);
const test_path: Path = pq.path;
- // In the case of .so files, they might actually be "linker scripts"
+ // In the case of shared libraries, they might actually be "linker scripts"
// that contain references to other libraries.
- if (pq.query.allow_so_scripts and target.ofmt == .elf and mem.endsWith(u8, test_path.sub_path, ".so")) {
+ if (pq.query.allow_so_scripts and target.ofmt == .elf and
+ Compilation.classifyFileExt(test_path.sub_path) == .shared_library)
+ {
var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
error.FileNotFound => return .no_match,
else => |e| fatal("unable to search for {s} library '{'}': {s}", .{
@@ -2354,21 +2392,6 @@ pub fn openDsoInput(diags: *Diags, path: Path, needed: bool, weak: bool, reexpor
} };
}
-fn stripLibPrefixAndSuffix(path: []const u8, target: std.Target) ?struct { []const u8, std.builtin.LinkMode } {
- const prefix = target.libPrefix();
- const static_suffix = target.staticLibSuffix();
- const dynamic_suffix = target.dynamicLibSuffix();
- const basename = fs.path.basename(path);
- const unlibbed = if (mem.startsWith(u8, basename, prefix)) basename[prefix.len..] else return null;
- if (mem.endsWith(u8, unlibbed, static_suffix)) return .{
- unlibbed[0 .. unlibbed.len - static_suffix.len], .static,
- };
- if (mem.endsWith(u8, unlibbed, dynamic_suffix)) return .{
- unlibbed[0 .. unlibbed.len - dynamic_suffix.len], .dynamic,
- };
- return null;
-}
-
/// Returns true if and only if there is at least one input of type object,
/// archive, or Windows resource file.
pub fn anyObjectInputs(inputs: []const Input) bool {
test/link/elf.zig
@@ -2123,24 +2123,35 @@ fn testLinkOrder(b: *Build, opts: Options) *Step {
fn testLdScript(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "ld-script", opts);
- const dso = addSharedLibrary(b, opts, .{ .name = "bar" });
- addCSourceBytes(dso, "int foo() { return 42; }", &.{});
+ const bar = addSharedLibrary(b, opts, .{ .name = "bar" });
+ addCSourceBytes(bar, "int bar() { return 42; }", &.{});
+
+ const baz = addSharedLibrary(b, opts, .{ .name = "baz" });
+ addCSourceBytes(baz, "int baz() { return 42; }", &.{});
const scripts = WriteFile.create(b);
- _ = scripts.add("liba.so", "INPUT(libfoo.so)");
+ _ = scripts.add("liba.so", "INPUT(libfoo.so libfoo2.so.1)");
_ = scripts.add("libfoo.so", "GROUP(AS_NEEDED(-lbar))");
+ // Check finding a versioned .so file that is elsewhere in the library search paths.
+ const scripts2 = WriteFile.create(b);
+ _ = scripts2.add("libfoo2.so.1", "GROUP(AS_NEEDED(-lbaz))");
+
const exe = addExecutable(b, opts, .{ .name = "main" });
addCSourceBytes(exe,
- \\int foo();
+ \\int bar();
+ \\int baz();
\\int main() {
- \\ return foo() - 42;
+ \\ return bar() - baz();
\\}
, &.{});
exe.linkSystemLibrary2("a", .{});
exe.addLibraryPath(scripts.getDirectory());
- exe.addLibraryPath(dso.getEmittedBinDirectory());
- exe.addRPath(dso.getEmittedBinDirectory());
+ exe.addLibraryPath(scripts2.getDirectory());
+ exe.addLibraryPath(bar.getEmittedBinDirectory());
+ exe.addLibraryPath(baz.getEmittedBinDirectory());
+ exe.addRPath(bar.getEmittedBinDirectory());
+ exe.addRPath(baz.getEmittedBinDirectory());
exe.linkLibC();
exe.allow_so_scripts = true;
@@ -2167,7 +2178,7 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
// TODO: A future enhancement could make this error message also mention
// the file that references the missing library.
expectLinkErrors(exe, test_step, .{
- .stderr_contains = "error: unable to find dynamic system library 'foo' using strategy 'no_fallback'. searched paths:",
+ .stderr_contains = "error: libfoo.so: file listed in linker script not found",
});
return test_step;