diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-02-22 18:18:24 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-22 18:18:24 -0500 |
| commit | 61b69a418db2f525c4be4e19029487f61fb3234e (patch) | |
| tree | 8612f2d44ceac2de68f0493476e2bbbfb0c112cb /src/link.zig | |
| parent | 6ef2384c07ef6817f134ffc61e34bcea6e87df92 (diff) | |
| parent | 0499c731eabd2c88928ef740b488706c51a8a6a7 (diff) | |
| download | zig-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.zig | 314 |
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 { |
