authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-22 18:18:24-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-02-22 18:18:24-05:00
log61b69a418db2f525c4be4e19029487f61fb3234e
tree8612f2d44ceac2de68f0493476e2bbbfb0c112cb
parent6ef2384c07ef6817f134ffc61e34bcea6e87df92
parent0499c731eabd2c88928ef740b488706c51a8a6a7
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #22659 from ifreund/linker-script-fix

link: fix ambiguous names in linker scripts

2 files changed, 188 insertions(+), 153 deletions(-)

src/link.zig+169-145
...@@ -1891,144 +1891,184 @@ pub fn resolveInputs(...@@ -1891,144 +1891,184 @@ pub fn resolveInputs(
1891 mem.reverse(UnresolvedInput, unresolved_inputs.items);1891 mem.reverse(UnresolvedInput, unresolved_inputs.items);
18921892
1893 syslib: while (unresolved_inputs.pop()) |unresolved_input| {1893 syslib: while (unresolved_inputs.pop()) |unresolved_input| {
1894 const name_query: UnresolvedInput.NameQuery = switch (unresolved_input) {1894 switch (unresolved_input) {
1895 .name_query => |nq| nq,1895 .name_query => |name_query| {
1896 .ambiguous_name => |an| an: {1896 const query = name_query.query;
1897 const lib_name, const link_mode = stripLibPrefixAndSuffix(an.name, target) orelse {1897
1898 try resolvePathInput(gpa, arena, unresolved_inputs, resolved_inputs, &ld_script_bytes, target, .{1898 // Checked in the first pass above while looking for libc libraries.
1899 .path = Path.initCwd(an.name),1899 assert(!fs.path.isAbsolute(name_query.name));
1900 .query = an.query,1900
1901 }, color);1901 checked_paths.clearRetainingCapacity();
1902 continue;1902
1903 };1903 switch (query.search_strategy) {
1904 break :an .{1904 .mode_first, .no_fallback => {
1905 .name = lib_name,1905 // check for preferred mode
1906 .query = .{1906 for (lib_directories) |lib_directory| switch (try resolveLibInput(
1907 .needed = an.query.needed,1907 gpa,
1908 .weak = an.query.weak,1908 arena,
1909 .reexport = an.query.reexport,1909 unresolved_inputs,
1910 .must_link = an.query.must_link,1910 resolved_inputs,
1911 .hidden = an.query.hidden,1911 &checked_paths,
1912 .allow_so_scripts = an.query.allow_so_scripts,1912 &ld_script_bytes,
1913 .preferred_mode = link_mode,1913 lib_directory,
1914 .search_strategy = .no_fallback,1914 name_query,
1915 target,
1916 query.preferred_mode,
1917 color,
1918 )) {
1919 .ok => continue :syslib,
1920 .no_match => {},
1921 };
1922 // check for fallback mode
1923 if (query.search_strategy == .no_fallback) {
1924 try failed_libs.append(arena, .{
1925 .name = name_query.name,
1926 .strategy = query.search_strategy,
1927 .checked_paths = try arena.dupe(u8, checked_paths.items),
1928 .preferred_mode = query.preferred_mode,
1929 });
1930 continue :syslib;
1931 }
1932 for (lib_directories) |lib_directory| switch (try resolveLibInput(
1933 gpa,
1934 arena,
1935 unresolved_inputs,
1936 resolved_inputs,
1937 &checked_paths,
1938 &ld_script_bytes,
1939 lib_directory,
1940 name_query,
1941 target,
1942 query.fallbackMode(),
1943 color,
1944 )) {
1945 .ok => continue :syslib,
1946 .no_match => {},
1947 };
1948 try failed_libs.append(arena, .{
1949 .name = name_query.name,
1950 .strategy = query.search_strategy,
1951 .checked_paths = try arena.dupe(u8, checked_paths.items),
1952 .preferred_mode = query.preferred_mode,
1953 });
1954 continue :syslib;
1915 },1955 },
1916 };1956 .paths_first => {
1917 },1957 for (lib_directories) |lib_directory| {
1918 .path_query => |pq| {1958 // check for preferred mode
1919 try resolvePathInput(gpa, arena, unresolved_inputs, resolved_inputs, &ld_script_bytes, target, pq, color);1959 switch (try resolveLibInput(
1920 continue;1960 gpa,
1921 },1961 arena,
1922 .dso_exact => |dso_exact| {1962 unresolved_inputs,
1923 try resolved_inputs.append(gpa, .{ .dso_exact = dso_exact });1963 resolved_inputs,
1924 continue;1964 &checked_paths,
1925 },1965 &ld_script_bytes,
1926 };1966 lib_directory,
1927 const query = name_query.query;1967 name_query,
19281968 target,
1929 // Checked in the first pass above while looking for libc libraries.1969 query.preferred_mode,
1930 assert(!fs.path.isAbsolute(name_query.name));1970 color,
19311971 )) {
1932 checked_paths.clearRetainingCapacity();1972 .ok => continue :syslib,
1973 .no_match => {},
1974 }
19331975
1934 switch (query.search_strategy) {1976 // check for fallback mode
1935 .mode_first, .no_fallback => {1977 switch (try resolveLibInput(
1936 // check for preferred mode1978 gpa,
1937 for (lib_directories) |lib_directory| switch (try resolveLibInput(1979 arena,
1980 unresolved_inputs,
1981 resolved_inputs,
1982 &checked_paths,
1983 &ld_script_bytes,
1984 lib_directory,
1985 name_query,
1986 target,
1987 query.fallbackMode(),
1988 color,
1989 )) {
1990 .ok => continue :syslib,
1991 .no_match => {},
1992 }
1993 }
1994 try failed_libs.append(arena, .{
1995 .name = name_query.name,
1996 .strategy = query.search_strategy,
1997 .checked_paths = try arena.dupe(u8, checked_paths.items),
1998 .preferred_mode = query.preferred_mode,
1999 });
2000 continue :syslib;
2001 },
2002 }
2003 },
2004 .ambiguous_name => |an| {
2005 // First check the path relative to the current working directory.
2006 // If the file is a library and is not found there, check the library search paths as well.
2007 // This is consistent with the behavior of GNU ld.
2008 if (try resolvePathInput(
1938 gpa,2009 gpa,
1939 arena,2010 arena,
1940 unresolved_inputs,2011 unresolved_inputs,
1941 resolved_inputs,2012 resolved_inputs,
1942 &checked_paths,
1943 &ld_script_bytes,2013 &ld_script_bytes,
1944 lib_directory,
1945 name_query,
1946 target,2014 target,
1947 query.preferred_mode,2015 .{
2016 .path = Path.initCwd(an.name),
2017 .query = an.query,
2018 },
1948 color,2019 color,
1949 )) {2020 )) |lib_result| {
1950 .ok => continue :syslib,2021 switch (lib_result) {
1951 .no_match => {},2022 .ok => continue :syslib,
1952 };2023 .no_match => {
1953 // check for fallback mode2024 for (lib_directories) |lib_directory| {
1954 if (query.search_strategy == .no_fallback) {2025 switch ((try resolvePathInput(
1955 try failed_libs.append(arena, .{2026 gpa,
1956 .name = name_query.name,2027 arena,
1957 .strategy = query.search_strategy,2028 unresolved_inputs,
1958 .checked_paths = try arena.dupe(u8, checked_paths.items),2029 resolved_inputs,
1959 .preferred_mode = query.preferred_mode,2030 &ld_script_bytes,
1960 });2031 target,
1961 continue :syslib;2032 .{
2033 .path = .{
2034 .root_dir = lib_directory,
2035 .sub_path = an.name,
2036 },
2037 .query = an.query,
2038 },
2039 color,
2040 )).?) {
2041 .ok => continue :syslib,
2042 .no_match => {},
2043 }
2044 }
2045 fatal("{s}: file listed in linker script not found", .{an.name});
2046 },
2047 }
1962 }2048 }
1963 for (lib_directories) |lib_directory| switch (try resolveLibInput(2049 continue;
2050 },
2051 .path_query => |pq| {
2052 if (try resolvePathInput(
1964 gpa,2053 gpa,
1965 arena,2054 arena,
1966 unresolved_inputs,2055 unresolved_inputs,
1967 resolved_inputs,2056 resolved_inputs,
1968 &checked_paths,
1969 &ld_script_bytes,2057 &ld_script_bytes,
1970 lib_directory,
1971 name_query,
1972 target,2058 target,
1973 query.fallbackMode(),2059 pq,
1974 color,2060 color,
1975 )) {2061 )) |lib_result| {
1976 .ok => continue :syslib,2062 switch (lib_result) {
1977 .no_match => {},2063 .ok => {},
1978 };2064 .no_match => fatal("{}: file not found", .{pq.path}),
1979 try failed_libs.append(arena, .{
1980 .name = name_query.name,
1981 .strategy = query.search_strategy,
1982 .checked_paths = try arena.dupe(u8, checked_paths.items),
1983 .preferred_mode = query.preferred_mode,
1984 });
1985 continue :syslib;
1986 },
1987 .paths_first => {
1988 for (lib_directories) |lib_directory| {
1989 // check for preferred mode
1990 switch (try resolveLibInput(
1991 gpa,
1992 arena,
1993 unresolved_inputs,
1994 resolved_inputs,
1995 &checked_paths,
1996 &ld_script_bytes,
1997 lib_directory,
1998 name_query,
1999 target,
2000 query.preferred_mode,
2001 color,
2002 )) {
2003 .ok => continue :syslib,
2004 .no_match => {},
2005 }
2006
2007 // check for fallback mode
2008 switch (try resolveLibInput(
2009 gpa,
2010 arena,
2011 unresolved_inputs,
2012 resolved_inputs,
2013 &checked_paths,
2014 &ld_script_bytes,
2015 lib_directory,
2016 name_query,
2017 target,
2018 query.fallbackMode(),
2019 color,
2020 )) {
2021 .ok => continue :syslib,
2022 .no_match => {},
2023 }2065 }
2024 }2066 }
2025 try failed_libs.append(arena, .{2067 continue;
2026 .name = name_query.name,2068 },
2027 .strategy = query.search_strategy,2069 .dso_exact => |dso_exact| {
2028 .checked_paths = try arena.dupe(u8, checked_paths.items),2070 try resolved_inputs.append(gpa, .{ .dso_exact = dso_exact });
2029 .preferred_mode = query.preferred_mode,2071 continue;
2030 });
2031 continue :syslib;
2032 },2072 },
2033 }2073 }
2034 @compileError("unreachable");2074 @compileError("unreachable");
...@@ -2178,10 +2218,10 @@ fn resolvePathInput(...@@ -2178,10 +2218,10 @@ fn resolvePathInput(
2178 target: std.Target,2218 target: std.Target,
2179 pq: UnresolvedInput.PathQuery,2219 pq: UnresolvedInput.PathQuery,
2180 color: std.zig.Color,2220 color: std.zig.Color,
2181) Allocator.Error!void {2221) Allocator.Error!?ResolveLibInputResult {
2182 switch (switch (Compilation.classifyFileExt(pq.path.sub_path)) {2222 switch (Compilation.classifyFileExt(pq.path.sub_path)) {
2183 .static_library => try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color),2223 .static_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color),
2184 .shared_library => try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color),2224 .shared_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color),
2185 .object => {2225 .object => {
2186 var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|2226 var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|
2187 fatal("failed to open object {}: {s}", .{ pq.path, @errorName(err) });2227 fatal("failed to open object {}: {s}", .{ pq.path, @errorName(err) });
...@@ -2192,7 +2232,7 @@ fn resolvePathInput(...@@ -2192,7 +2232,7 @@ fn resolvePathInput(
2192 .must_link = pq.query.must_link,2232 .must_link = pq.query.must_link,
2193 .hidden = pq.query.hidden,2233 .hidden = pq.query.hidden,
2194 } });2234 } });
2195 return;2235 return null;
2196 },2236 },
2197 .res => {2237 .res => {
2198 var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|2238 var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|
...@@ -2202,12 +2242,9 @@ fn resolvePathInput(...@@ -2202,12 +2242,9 @@ fn resolvePathInput(
2202 .path = pq.path,2242 .path = pq.path,
2203 .file = file,2243 .file = file,
2204 } });2244 } });
2205 return;2245 return null;
2206 },2246 },
2207 else => fatal("{}: unrecognized file extension", .{pq.path}),2247 else => fatal("{}: unrecognized file extension", .{pq.path}),
2208 }) {
2209 .ok => {},
2210 .no_match => fatal("{}: file not found", .{pq.path}),
2211 }2248 }
2212}2249}
22132250
...@@ -2228,9 +2265,11 @@ fn resolvePathInputLib(...@@ -2228,9 +2265,11 @@ fn resolvePathInputLib(
2228 try resolved_inputs.ensureUnusedCapacity(gpa, 1);2265 try resolved_inputs.ensureUnusedCapacity(gpa, 1);
22292266
2230 const test_path: Path = pq.path;2267 const test_path: Path = pq.path;
2231 // In the case of .so files, they might actually be "linker scripts"2268 // In the case of shared libraries, they might actually be "linker scripts"
2232 // that contain references to other libraries.2269 // that contain references to other libraries.
2233 if (pq.query.allow_so_scripts and target.ofmt == .elf and mem.endsWith(u8, test_path.sub_path, ".so")) {2270 if (pq.query.allow_so_scripts and target.ofmt == .elf and
2271 Compilation.classifyFileExt(test_path.sub_path) == .shared_library)
2272 {
2234 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {2273 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
2235 error.FileNotFound => return .no_match,2274 error.FileNotFound => return .no_match,
2236 else => |e| fatal("unable to search for {s} library '{'}': {s}", .{2275 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...@@ -2356,21 +2395,6 @@ pub fn openDsoInput(diags: *Diags, path: Path, needed: bool, weak: bool, reexpor
2356 } };2395 } };
2357}2396}
23582397
2359fn stripLibPrefixAndSuffix(path: []const u8, target: std.Target) ?struct { []const u8, std.builtin.LinkMode } {
2360 const prefix = target.libPrefix();
2361 const static_suffix = target.staticLibSuffix();
2362 const dynamic_suffix = target.dynamicLibSuffix();
2363 const basename = fs.path.basename(path);
2364 const unlibbed = if (mem.startsWith(u8, basename, prefix)) basename[prefix.len..] else return null;
2365 if (mem.endsWith(u8, unlibbed, static_suffix)) return .{
2366 unlibbed[0 .. unlibbed.len - static_suffix.len], .static,
2367 };
2368 if (mem.endsWith(u8, unlibbed, dynamic_suffix)) return .{
2369 unlibbed[0 .. unlibbed.len - dynamic_suffix.len], .dynamic,
2370 };
2371 return null;
2372}
2373
2374/// Returns true if and only if there is at least one input of type object,2398/// Returns true if and only if there is at least one input of type object,
2375/// archive, or Windows resource file.2399/// archive, or Windows resource file.
2376pub fn anyObjectInputs(inputs: []const Input) bool {2400pub fn anyObjectInputs(inputs: []const Input) bool {
test/link/elf.zig+19-8
...@@ -2123,24 +2123,35 @@ fn testLinkOrder(b: *Build, opts: Options) *Step {...@@ -2123,24 +2123,35 @@ fn testLinkOrder(b: *Build, opts: Options) *Step {
2123fn testLdScript(b: *Build, opts: Options) *Step {2123fn testLdScript(b: *Build, opts: Options) *Step {
2124 const test_step = addTestStep(b, "ld-script", opts);2124 const test_step = addTestStep(b, "ld-script", opts);
21252125
2126 const dso = addSharedLibrary(b, opts, .{ .name = "bar" });2126 const bar = addSharedLibrary(b, opts, .{ .name = "bar" });
2127 addCSourceBytes(dso, "int foo() { return 42; }", &.{});2127 addCSourceBytes(bar, "int bar() { return 42; }", &.{});
2128
2129 const baz = addSharedLibrary(b, opts, .{ .name = "baz" });
2130 addCSourceBytes(baz, "int baz() { return 42; }", &.{});
21282131
2129 const scripts = WriteFile.create(b);2132 const scripts = WriteFile.create(b);
2130 _ = scripts.add("liba.so", "INPUT(libfoo.so)");2133 _ = scripts.add("liba.so", "INPUT(libfoo.so libfoo2.so.1)");
2131 _ = scripts.add("libfoo.so", "GROUP(AS_NEEDED(-lbar))");2134 _ = scripts.add("libfoo.so", "GROUP(AS_NEEDED(-lbar))");
21322135
2136 // Check finding a versioned .so file that is elsewhere in the library search paths.
2137 const scripts2 = WriteFile.create(b);
2138 _ = scripts2.add("libfoo2.so.1", "GROUP(AS_NEEDED(-lbaz))");
2139
2133 const exe = addExecutable(b, opts, .{ .name = "main" });2140 const exe = addExecutable(b, opts, .{ .name = "main" });
2134 addCSourceBytes(exe,2141 addCSourceBytes(exe,
2135 \\int foo();2142 \\int bar();
2143 \\int baz();
2136 \\int main() {2144 \\int main() {
2137 \\ return foo() - 42;2145 \\ return bar() - baz();
2138 \\}2146 \\}
2139 , &.{});2147 , &.{});
2140 exe.linkSystemLibrary2("a", .{});2148 exe.linkSystemLibrary2("a", .{});
2141 exe.addLibraryPath(scripts.getDirectory());2149 exe.addLibraryPath(scripts.getDirectory());
2142 exe.addLibraryPath(dso.getEmittedBinDirectory());2150 exe.addLibraryPath(scripts2.getDirectory());
2143 exe.addRPath(dso.getEmittedBinDirectory());2151 exe.addLibraryPath(bar.getEmittedBinDirectory());
2152 exe.addLibraryPath(baz.getEmittedBinDirectory());
2153 exe.addRPath(bar.getEmittedBinDirectory());
2154 exe.addRPath(baz.getEmittedBinDirectory());
2144 exe.linkLibC();2155 exe.linkLibC();
2145 exe.allow_so_scripts = true;2156 exe.allow_so_scripts = true;
21462157
...@@ -2167,7 +2178,7 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {...@@ -2167,7 +2178,7 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
2167 // TODO: A future enhancement could make this error message also mention2178 // TODO: A future enhancement could make this error message also mention
2168 // the file that references the missing library.2179 // the file that references the missing library.
2169 expectLinkErrors(exe, test_step, .{2180 expectLinkErrors(exe, test_step, .{
2170 .stderr_contains = "error: unable to find dynamic system library 'foo' using strategy 'no_fallback'. searched paths:",2181 .stderr_contains = "error: libfoo.so: file listed in linker script not found",
2171 });2182 });
21722183
2173 return test_step;2184 return test_step;