aboutsummaryrefslogtreecommitdiff
path: root/src/link.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-02-22 18:18:24 -0500
committerGitHub <noreply@github.com>2025-02-22 18:18:24 -0500
commit61b69a418db2f525c4be4e19029487f61fb3234e (patch)
tree8612f2d44ceac2de68f0493476e2bbbfb0c112cb /src/link.zig
parent6ef2384c07ef6817f134ffc61e34bcea6e87df92 (diff)
parent0499c731eabd2c88928ef740b488706c51a8a6a7 (diff)
downloadzig-61b69a418db2f525c4be4e19029487f61fb3234e.tar.gz
zig-61b69a418db2f525c4be4e19029487f61fb3234e.zip
Merge pull request #22659 from ifreund/linker-script-fix
link: fix ambiguous names in linker scripts
Diffstat (limited to 'src/link.zig')
-rw-r--r--src/link.zig314
1 files changed, 169 insertions, 145 deletions
diff --git a/src/link.zig b/src/link.zig
index 99d7908be1..4ab71493d0 100644
--- a/src/link.zig
+++ b/src/link.zig
@@ -1891,144 +1891,184 @@ pub fn resolveInputs(
mem.reverse(UnresolvedInput, unresolved_inputs.items);
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, .{
- .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,
+ switch (unresolved_input) {
+ .name_query => |name_query| {
+ const query = name_query.query;
+
+ // Checked in the first pass above while looking for libc libraries.
+ assert(!fs.path.isAbsolute(name_query.name));
+
+ checked_paths.clearRetainingCapacity();
+
+ switch (query.search_strategy) {
+ .mode_first, .no_fallback => {
+ // check for preferred mode
+ for (lib_directories) |lib_directory| switch (try resolveLibInput(
+ gpa,
+ arena,
+ unresolved_inputs,
+ resolved_inputs,
+ &checked_paths,
+ &ld_script_bytes,
+ lib_directory,
+ name_query,
+ target,
+ query.preferred_mode,
+ color,
+ )) {
+ .ok => continue :syslib,
+ .no_match => {},
+ };
+ // check for fallback mode
+ if (query.search_strategy == .no_fallback) {
+ try failed_libs.append(arena, .{
+ .name = name_query.name,
+ .strategy = query.search_strategy,
+ .checked_paths = try arena.dupe(u8, checked_paths.items),
+ .preferred_mode = query.preferred_mode,
+ });
+ continue :syslib;
+ }
+ for (lib_directories) |lib_directory| switch (try resolveLibInput(
+ gpa,
+ arena,
+ unresolved_inputs,
+ resolved_inputs,
+ &checked_paths,
+ &ld_script_bytes,
+ lib_directory,
+ name_query,
+ target,
+ query.fallbackMode(),
+ color,
+ )) {
+ .ok => continue :syslib,
+ .no_match => {},
+ };
+ try failed_libs.append(arena, .{
+ .name = name_query.name,
+ .strategy = query.search_strategy,
+ .checked_paths = try arena.dupe(u8, checked_paths.items),
+ .preferred_mode = query.preferred_mode,
+ });
+ continue :syslib;
},
- };
- },
- .path_query => |pq| {
- try resolvePathInput(gpa, arena, unresolved_inputs, resolved_inputs, &ld_script_bytes, target, pq, color);
- continue;
- },
- .dso_exact => |dso_exact| {
- try resolved_inputs.append(gpa, .{ .dso_exact = dso_exact });
- continue;
- },
- };
- const query = name_query.query;
-
- // Checked in the first pass above while looking for libc libraries.
- assert(!fs.path.isAbsolute(name_query.name));
-
- checked_paths.clearRetainingCapacity();
+ .paths_first => {
+ for (lib_directories) |lib_directory| {
+ // check for preferred mode
+ switch (try resolveLibInput(
+ gpa,
+ arena,
+ unresolved_inputs,
+ resolved_inputs,
+ &checked_paths,
+ &ld_script_bytes,
+ lib_directory,
+ name_query,
+ target,
+ query.preferred_mode,
+ color,
+ )) {
+ .ok => continue :syslib,
+ .no_match => {},
+ }
- switch (query.search_strategy) {
- .mode_first, .no_fallback => {
- // check for preferred mode
- for (lib_directories) |lib_directory| switch (try resolveLibInput(
+ // check for fallback mode
+ switch (try resolveLibInput(
+ gpa,
+ arena,
+ unresolved_inputs,
+ resolved_inputs,
+ &checked_paths,
+ &ld_script_bytes,
+ lib_directory,
+ name_query,
+ target,
+ query.fallbackMode(),
+ color,
+ )) {
+ .ok => continue :syslib,
+ .no_match => {},
+ }
+ }
+ try failed_libs.append(arena, .{
+ .name = name_query.name,
+ .strategy = query.search_strategy,
+ .checked_paths = try arena.dupe(u8, checked_paths.items),
+ .preferred_mode = query.preferred_mode,
+ });
+ continue :syslib;
+ },
+ }
+ },
+ .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,
- &checked_paths,
&ld_script_bytes,
- lib_directory,
- name_query,
target,
- query.preferred_mode,
+ .{
+ .path = Path.initCwd(an.name),
+ .query = an.query,
+ },
color,
- )) {
- .ok => continue :syslib,
- .no_match => {},
- };
- // check for fallback mode
- if (query.search_strategy == .no_fallback) {
- try failed_libs.append(arena, .{
- .name = name_query.name,
- .strategy = query.search_strategy,
- .checked_paths = try arena.dupe(u8, checked_paths.items),
- .preferred_mode = query.preferred_mode,
- });
- continue :syslib;
+ )) |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});
+ },
+ }
}
- for (lib_directories) |lib_directory| switch (try resolveLibInput(
+ continue;
+ },
+ .path_query => |pq| {
+ if (try resolvePathInput(
gpa,
arena,
unresolved_inputs,
resolved_inputs,
- &checked_paths,
&ld_script_bytes,
- lib_directory,
- name_query,
target,
- query.fallbackMode(),
+ pq,
color,
- )) {
- .ok => continue :syslib,
- .no_match => {},
- };
- try failed_libs.append(arena, .{
- .name = name_query.name,
- .strategy = query.search_strategy,
- .checked_paths = try arena.dupe(u8, checked_paths.items),
- .preferred_mode = query.preferred_mode,
- });
- continue :syslib;
- },
- .paths_first => {
- for (lib_directories) |lib_directory| {
- // check for preferred mode
- switch (try resolveLibInput(
- gpa,
- arena,
- unresolved_inputs,
- resolved_inputs,
- &checked_paths,
- &ld_script_bytes,
- lib_directory,
- name_query,
- target,
- query.preferred_mode,
- color,
- )) {
- .ok => continue :syslib,
- .no_match => {},
- }
-
- // check for fallback mode
- switch (try resolveLibInput(
- gpa,
- arena,
- unresolved_inputs,
- resolved_inputs,
- &checked_paths,
- &ld_script_bytes,
- lib_directory,
- name_query,
- target,
- query.fallbackMode(),
- color,
- )) {
- .ok => continue :syslib,
- .no_match => {},
+ )) |lib_result| {
+ switch (lib_result) {
+ .ok => {},
+ .no_match => fatal("{}: file not found", .{pq.path}),
}
}
- try failed_libs.append(arena, .{
- .name = name_query.name,
- .strategy = query.search_strategy,
- .checked_paths = try arena.dupe(u8, checked_paths.items),
- .preferred_mode = query.preferred_mode,
- });
- continue :syslib;
+ continue;
+ },
+ .dso_exact => |dso_exact| {
+ try resolved_inputs.append(gpa, .{ .dso_exact = dso_exact });
+ continue;
},
}
@compileError("unreachable");
@@ -2178,10 +2218,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) });
@@ -2192,7 +2232,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|
@@ -2202,12 +2242,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}),
}
}
@@ -2228,9 +2265,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}", .{
@@ -2356,21 +2395,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 {