| author | |
| committer | |
| log | 0a1843452e6d39d4a2adc9c7feefce274a5d30c6 |
| tree | 7012d3b9006801793eed12f69dd0b2bbd8fc11aa |
| parent | cf33f1003fd02abaf91078725d9c972dd51fc5e1 |
For determining the `DT_NEEDED` value:
* the `DT_SONAME` always takes precedence
* otherwise:
* `-l` arguments use the resolved path's basename
* raw `/path/to/libfoo.so` arguments use the full path
All ELF linkers, including LLD, now implement exactly those semantics.
The operand to `-l` is not allowed to contain path separators, because
the behavior of this in other linkers is confusing and likely not
supported. (This is not the case for `-l :path/to/libfoo.so`.)7 files changed, 161 insertions(+), 82 deletions(-)
src/link.zig+103-37| ... | ... | @@ -1819,11 +1819,14 @@ pub const UnresolvedInput = union(enum) { |
| 1819 | 1819 | path_query: PathQuery, |
| 1820 | 1820 | /// Strings that come from GNU ld scripts. Is it a filename? Is it a path? |
| 1821 | 1821 | /// Who knows! Fuck around and find out. |
| 1822 | ambiguous_name: NameQuery, | |
| 1822 | ambiguous_name: AmbiguousNameQuery, | |
| 1823 | 1823 | |
| 1824 | 1824 | pub const NameQuery = struct { |
| 1825 | 1825 | name: []const u8, |
| 1826 | 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, | |
| 1827 | 1830 | }; |
| 1828 | 1831 | |
| 1829 | 1832 | pub const PathQuery = struct { |
| ... | ... | @@ -1831,6 +1834,11 @@ pub const UnresolvedInput = union(enum) { |
| 1831 | 1834 | query: Query, |
| 1832 | 1835 | }; |
| 1833 | 1836 | |
| 1837 | pub const AmbiguousNameQuery = struct { | |
| 1838 | name: []const u8, | |
| 1839 | query: Query, | |
| 1840 | }; | |
| 1841 | ||
| 1834 | 1842 | pub const Query = struct { |
| 1835 | 1843 | needed: bool = false, |
| 1836 | 1844 | weak: bool = false, |
| ... | ... | @@ -1838,14 +1846,6 @@ pub const UnresolvedInput = union(enum) { |
| 1838 | 1846 | must_link: bool = false, |
| 1839 | 1847 | hidden: bool = false, |
| 1840 | 1848 | allow_so_scripts: bool = false, |
| 1841 | // Corresponds to GNU ld `-l :path/to/filename`: | |
| 1842 | // * No extra rpaths. | |
| 1843 | // * NEEDED entry should be exactly the string | |
| 1844 | // after the colon. No file system paths prepended. | |
| 1845 | // * The DSO still must be found at compile/link | |
| 1846 | // time and its entries used to resolve symbols. | |
| 1847 | // CGo compilation depends on this. | |
| 1848 | name_done: bool = false, | |
| 1849 | 1849 | preferred_mode: std.lang.LinkMode, |
| 1850 | 1850 | search_strategy: SearchStrategy, |
| 1851 | 1851 | |
| ... | ... | @@ -1891,15 +1891,9 @@ pub const Input = union(enum) { |
| 1891 | 1891 | needed: bool, |
| 1892 | 1892 | weak: bool, |
| 1893 | 1893 | reexport: bool, |
| 1894 | /// Does not include any ":" prefix. Indicates that this string, exactly, should go into the NEEDED | |
| 1895 | /// entry, without affecting any rpaths. | |
| 1896 | exact_name: ?[]const u8, | |
| 1897 | }; | |
| 1894 | fallback_soname: FallbackSoname, | |
| 1898 | 1895 | |
| 1899 | pub const DsoExact = struct { | |
| 1900 | /// Includes the ":" prefix. This is intended to be put into the DSO | |
| 1901 | /// section verbatim with no corresponding rpaths. | |
| 1902 | name: []const u8, | |
| 1896 | pub const FallbackSoname = enum { basename, full_path }; | |
| 1903 | 1897 | }; |
| 1904 | 1898 | |
| 1905 | 1899 | pub fn path(input: Input) Path { |
| ... | ... | @@ -1950,7 +1944,7 @@ pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void { |
| 1950 | 1944 | man.hash.add(dso.needed); |
| 1951 | 1945 | man.hash.add(dso.weak); |
| 1952 | 1946 | man.hash.add(dso.reexport); |
| 1953 | man.hash.addOptionalBytes(dso.exact_name); | |
| 1947 | man.hash.add(dso.fallback_soname); | |
| 1954 | 1948 | }, |
| 1955 | 1949 | } |
| 1956 | 1950 | } |
| ... | ... | @@ -2001,7 +1995,7 @@ pub fn resolveInputs( |
| 2001 | 1995 | .name_query => |name_query| { |
| 2002 | 1996 | const query = name_query.query; |
| 2003 | 1997 | |
| 2004 | // 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. | |
| 2005 | 1999 | assert(!fs.path.isAbsolute(name_query.name)); |
| 2006 | 2000 | |
| 2007 | 2001 | checked_paths.clearRetainingCapacity(); |
| ... | ... | @@ -2229,7 +2223,7 @@ fn resolveLibInput( |
| 2229 | 2223 | |
| 2230 | 2224 | const lib_name = name_query.name; |
| 2231 | 2225 | |
| 2232 | if (target.os.tag.isDarwin() and link_mode == .dynamic and !name_query.query.name_done) tbd: { | |
| 2226 | if (target.os.tag.isDarwin() and link_mode == .dynamic and !name_query.name_done) tbd: { | |
| 2233 | 2227 | // Prefer .tbd over .dylib. |
| 2234 | 2228 | const test_path: Path = .{ |
| 2235 | 2229 | .root_dir = lib_directory, |
| ... | ... | @@ -2241,13 +2235,22 @@ fn resolveLibInput( |
| 2241 | 2235 | else => |e| fatal("searching for tbd library {qf}: {t}", .{ test_path, e }), |
| 2242 | 2236 | }; |
| 2243 | 2237 | errdefer file.close(io); |
| 2244 | 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 | ); | |
| 2245 | 2248 | } |
| 2246 | 2249 | |
| 2247 | 2250 | { |
| 2248 | 2251 | const test_path: Path = .{ |
| 2249 | 2252 | .root_dir = lib_directory, |
| 2250 | .sub_path = if (name_query.query.name_done) lib_name else 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}", .{ | |
| 2251 | 2254 | target.libPrefix(), |
| 2252 | 2255 | lib_name, |
| 2253 | 2256 | switch (link_mode) { |
| ... | ... | @@ -2260,7 +2263,7 @@ fn resolveLibInput( |
| 2260 | 2263 | switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{ |
| 2261 | 2264 | .path = test_path, |
| 2262 | 2265 | .query = name_query.query, |
| 2263 | }, link_mode, color)) { | |
| 2266 | }, link_mode, color, .basename)) { | |
| 2264 | 2267 | .no_match => {}, |
| 2265 | 2268 | .ok => return .ok, |
| 2266 | 2269 | } |
| ... | ... | @@ -2268,7 +2271,7 @@ fn resolveLibInput( |
| 2268 | 2271 | |
| 2269 | 2272 | // In the case of Darwin, the main check will be .dylib, so here we |
| 2270 | 2273 | // additionally check for .so files. |
| 2271 | if (target.os.tag.isDarwin() and link_mode == .dynamic and !name_query.query.name_done) so: { | |
| 2274 | if (target.os.tag.isDarwin() and link_mode == .dynamic and !name_query.name_done) so: { | |
| 2272 | 2275 | const test_path: Path = .{ |
| 2273 | 2276 | .root_dir = lib_directory, |
| 2274 | 2277 | .sub_path = try std.fmt.allocPrint(arena, "lib{s}.so", .{lib_name}), |
| ... | ... | @@ -2279,12 +2282,21 @@ fn resolveLibInput( |
| 2279 | 2282 | else => |e| fatal("unable to search for so library {qf}: {t}", .{ test_path, e }), |
| 2280 | 2283 | }; |
| 2281 | 2284 | errdefer file.close(io); |
| 2282 | 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 | ); | |
| 2283 | 2295 | } |
| 2284 | 2296 | |
| 2285 | 2297 | // In the case of MinGW, the main check will be .lib but we also need to |
| 2286 | 2298 | // look for `libfoo.a`. |
| 2287 | if (target.isMinGW() and link_mode == .static and !name_query.query.name_done) mingw: { | |
| 2299 | if (target.isMinGW() and link_mode == .static and !name_query.name_done) mingw: { | |
| 2288 | 2300 | const test_path: Path = .{ |
| 2289 | 2301 | .root_dir = lib_directory, |
| 2290 | 2302 | .sub_path = try std.fmt.allocPrint(arena, "lib{s}.a", .{lib_name}), |
| ... | ... | @@ -2295,13 +2307,22 @@ fn resolveLibInput( |
| 2295 | 2307 | else => |e| fatal("unable to search for static library {qf}: {t}", .{ test_path, e }), |
| 2296 | 2308 | }; |
| 2297 | 2309 | errdefer file.close(io); |
| 2298 | 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 | ); | |
| 2299 | 2320 | } |
| 2300 | 2321 | |
| 2301 | 2322 | // In the case of OpenBSD, dynamic libraries are always versioned, without |
| 2302 | 2323 | // unversioned symlinks. OpenBSD patches LLD to select the highest-versioned |
| 2303 | 2324 | // shared library, and this code is intended to match that upstream behavior. |
| 2304 | if (target.isOpenBSDLibC() and link_mode == .dynamic and !name_query.query.name_done) versioned: { | |
| 2325 | if (target.isOpenBSDLibC() and link_mode == .dynamic and !name_query.name_done) versioned: { | |
| 2305 | 2326 | const prefix = try std.fmt.allocPrint(arena, "lib{s}.so.", .{lib_name}); |
| 2306 | 2327 | |
| 2307 | 2328 | var dir = lib_directory.handle.openDir(io, ".", .{ .iterate = true }) catch |err| switch (err) { |
| ... | ... | @@ -2347,7 +2368,7 @@ fn resolveLibInput( |
| 2347 | 2368 | switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{ |
| 2348 | 2369 | .path = test_path, |
| 2349 | 2370 | .query = name_query.query, |
| 2350 | }, link_mode, color)) { | |
| 2371 | }, link_mode, color, .basename)) { | |
| 2351 | 2372 | .no_match => {}, |
| 2352 | 2373 | .ok => return .ok, |
| 2353 | 2374 | } |
| ... | ... | @@ -2388,6 +2409,7 @@ fn finishResolveLibInput( |
| 2388 | 2409 | file: Io.File, |
| 2389 | 2410 | link_mode: std.lang.LinkMode, |
| 2390 | 2411 | query: UnresolvedInput.Query, |
| 2412 | fallback_soname: Input.Dso.FallbackSoname, | |
| 2391 | 2413 | ) ResolveLibInputResult { |
| 2392 | 2414 | switch (link_mode) { |
| 2393 | 2415 | .static => { |
| ... | ... | @@ -2413,9 +2435,7 @@ fn finishResolveLibInput( |
| 2413 | 2435 | .needed = query.needed, |
| 2414 | 2436 | .weak = query.weak, |
| 2415 | 2437 | .reexport = query.reexport, |
| 2416 | // Here we assume that `Path.sub_path` is usable as `exact_name`, however the code after this point | |
| 2417 | // has no business making that assumption. | |
| 2418 | .exact_name = if (query.name_done) path.sub_path else null, | |
| 2438 | .fallback_soname = fallback_soname, | |
| 2419 | 2439 | }, |
| 2420 | 2440 | }), |
| 2421 | 2441 | } |
| ... | ... | @@ -2439,8 +2459,34 @@ fn resolvePathInput( |
| 2439 | 2459 | color: std.zig.Color, |
| 2440 | 2460 | ) Allocator.Error!?ResolveLibInputResult { |
| 2441 | 2461 | switch (Compilation.classifyFileExt(pq.path.sub_path)) { |
| 2442 | .static_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .static, color), | |
| 2443 | .shared_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .dynamic, color), | |
| 2462 | .static_library => return try resolvePathInputLib( | |
| 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 | ), | |
| 2444 | 2490 | .object => { |
| 2445 | 2491 | var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err| |
| 2446 | 2492 | fatal("failed to open object {f}: {t}", .{ pq.path, err }); |
| ... | ... | @@ -2483,6 +2529,7 @@ fn resolvePathInputLib( |
| 2483 | 2529 | pq: UnresolvedInput.PathQuery, |
| 2484 | 2530 | link_mode: std.lang.LinkMode, |
| 2485 | 2531 | color: std.zig.Color, |
| 2532 | fallback_soname: Input.Dso.FallbackSoname, | |
| 2486 | 2533 | ) Allocator.Error!ResolveLibInputResult { |
| 2487 | 2534 | try resolved_inputs.ensureUnusedCapacity(gpa, 1); |
| 2488 | 2535 | try archive_dedup.ensureUnusedCapacity(gpa, 1); |
| ... | ... | @@ -2510,7 +2557,16 @@ fn resolvePathInputLib( |
| 2510 | 2557 | mem.startsWith(u8, buf, std.elf.ARMAG_THIN)) |
| 2511 | 2558 | { |
| 2512 | 2559 | // Appears to be an ELF or archive file. |
| 2513 | 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 | ); | |
| 2514 | 2570 | } |
| 2515 | 2571 | const stat = file.stat(io) catch |err| |
| 2516 | 2572 | fatal("failed to stat {f}: {t}", .{ test_path, err }); |
| ... | ... | @@ -2561,6 +2617,7 @@ fn resolvePathInputLib( |
| 2561 | 2617 | unresolved_inputs.appendAssumeCapacity(.{ .name_query = .{ |
| 2562 | 2618 | .name = try arena.dupe(u8, arg.path["-l".len..]), |
| 2563 | 2619 | .query = query, |
| 2620 | .name_done = false, | |
| 2564 | 2621 | } }); |
| 2565 | 2622 | } else { |
| 2566 | 2623 | unresolved_inputs.appendAssumeCapacity(.{ .ambiguous_name = .{ |
| ... | ... | @@ -2578,7 +2635,16 @@ fn resolvePathInputLib( |
| 2578 | 2635 | else => |e| fatal("unable to search for {t} library {f}: {t}", .{ link_mode, test_path, e }), |
| 2579 | 2636 | }; |
| 2580 | 2637 | errdefer file.close(io); |
| 2581 | 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 | ); | |
| 2582 | 2648 | } |
| 2583 | 2649 | |
| 2584 | 2650 | pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object { |
| ... | ... | @@ -2601,7 +2667,7 @@ pub fn openDso(io: Io, path: Path, needed: bool, weak: bool, reexport: bool) !In |
| 2601 | 2667 | .needed = needed, |
| 2602 | 2668 | .weak = weak, |
| 2603 | 2669 | .reexport = reexport, |
| 2604 | .exact_name = null, | |
| 2670 | .fallback_soname = .full_path, | |
| 2605 | 2671 | }; |
| 2606 | 2672 | } |
| 2607 | 2673 |
src/link/Elf.zig+9-4| ... | ... | @@ -735,7 +735,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void { |
| 735 | 735 | } else { |
| 736 | 736 | try parseArchive(gpa, io, diags, &self.file_handles, &self.files, target, debug_fmt_strip, default_sym_version, &self.objects, obj); |
| 737 | 737 | }, |
| 738 | .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), | |
| 739 | 739 | } |
| 740 | 740 | } |
| 741 | 741 | |
| ... | ... | @@ -1106,6 +1106,7 @@ fn parseArchive( |
| 1106 | 1106 | |
| 1107 | 1107 | fn parseDso( |
| 1108 | 1108 | gpa: Allocator, |
| 1109 | arena: Allocator, | |
| 1109 | 1110 | io: Io, |
| 1110 | 1111 | diags: *Diags, |
| 1111 | 1112 | dso: link.Input.Dso, |
| ... | ... | @@ -1118,13 +1119,16 @@ fn parseDso( |
| 1118 | 1119 | |
| 1119 | 1120 | const handle = dso.file; |
| 1120 | 1121 | |
| 1121 | if (dso.exact_name != null) @panic("TODO"); | |
| 1122 | ||
| 1123 | 1122 | const stat: Stat = .init(try handle.stat(io)); |
| 1124 | 1123 | var header = try SharedObject.parseHeader(gpa, io, diags, dso.path, handle, stat, target); |
| 1125 | 1124 | defer header.deinit(gpa); |
| 1126 | 1125 | |
| 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; | |
| 1128 | 1132 | |
| 1129 | 1133 | const gop = try shared_objects.getOrPut(gpa, soname); |
| 1130 | 1134 | if (gop.found_existing) return; |
| ... | ... | @@ -1156,6 +1160,7 @@ fn parseDso( |
| 1156 | 1160 | .symbols_extra = .empty, |
| 1157 | 1161 | .symbols_resolver = .empty, |
| 1158 | 1162 | .output_symtab_ctx = .{}, |
| 1163 | .fallback_soname = fallback_soname, | |
| 1159 | 1164 | }, |
| 1160 | 1165 | }); |
| 1161 | 1166 | const so = fileLookup(files.*, index, null).?.shared_object; |
src/link/Elf/SharedObject.zig+3-1| ... | ... | @@ -31,6 +31,8 @@ alive: bool, |
| 31 | 31 | |
| 32 | 32 | output_symtab_ctx: Elf.SymtabCtx, |
| 33 | 33 | |
| 34 | fallback_soname: []const u8, | |
| 35 | ||
| 34 | 36 | pub fn deinit(so: *SharedObject, gpa: Allocator) void { |
| 35 | 37 | gpa.free(so.path.sub_path); |
| 36 | 38 | so.parsed.deinit(gpa); |
| ... | ... | @@ -427,7 +429,7 @@ pub fn asFile(self: *SharedObject) File { |
| 427 | 429 | } |
| 428 | 430 | |
| 429 | 431 | pub 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 | } |
| 432 | 434 | |
| 433 | 435 | pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { |
src/link/Elf2.zig+11-11| ... | ... | @@ -6228,17 +6228,9 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void |
| 6228 | 6228 | }, |
| 6229 | 6229 | .res => unreachable, |
| 6230 | 6230 | .dso => |dso| { |
| 6231 | if (dso.exact_name) |exact_name| { | |
| 6232 | // This means the path field is still valid, but the NEEDED entry must be set to | |
| 6233 | // exactly the `exact_name` string. | |
| 6234 | std.debug.panic("load dso_exact {q}", .{exact_name}); | |
| 6235 | if (elf.shndx.dynamic != .UNDEF) { | |
| 6236 | try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, exact_name), {}); | |
| 6237 | } | |
| 6238 | } | |
| 6239 | 6231 | try elf.needed.ensureUnusedCapacity(elf.base.comp.gpa, 1); |
| 6240 | 6232 | var fr = dso.file.reader(io, &buf); |
| 6241 | elf.loadDso(dso.path, &fr) catch |err| switch (err) { | |
| 6233 | elf.loadDso(dso.path, dso.fallback_soname, &fr) catch |err| switch (err) { | |
| 6242 | 6234 | else => |e| return e, |
| 6243 | 6235 | error.EndOfStream => return diags.failParse(dso.path, "unexpected eof", .{}), |
| 6244 | 6236 | error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail( |
| ... | ... | @@ -6860,7 +6852,12 @@ fn populateArchiveMemberName(elf: *Elf, member_ar_hdr: *std.elf.ar_hdr, member_n |
| 6860 | 6852 | @memcpy(dest_slice[0 .. dest_slice.len - 2], member_name); |
| 6861 | 6853 | @memcpy(dest_slice[dest_slice.len - 2 ..], "/\n"); // yes, the terminator is weird |
| 6862 | 6854 | } |
| 6863 | fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void { | |
| 6855 | fn 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 { | |
| 6864 | 6861 | const comp = elf.base.comp; |
| 6865 | 6862 | const gpa = comp.gpa; |
| 6866 | 6863 | const diags = &comp.link_diags; |
| ... | ... | @@ -6956,7 +6953,10 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars |
| 6956 | 6953 | } |
| 6957 | 6954 | break std.mem.sliceTo(dynstr[@intCast(val)..], 0); |
| 6958 | 6955 | } |
| 6959 | } 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 | }; | |
| 6960 | 6960 | try elf.needed.put(gpa, try elf.string(.dynstr, soname), {}); |
| 6961 | 6961 | |
| 6962 | 6962 | // Scan the symbol table and populate `elf.dso_globals`. |
src/link/Lld.zig+17-18| ... | ... | @@ -1154,6 +1154,13 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { |
| 1154 | 1154 | argv.appendAssumeCapacity("--as-needed"); |
| 1155 | 1155 | var as_needed = true; |
| 1156 | 1156 | |
| 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 | ||
| 1157 | 1164 | for (base.comp.link_inputs) |link_input| switch (link_input) { |
| 1158 | 1165 | .res => unreachable, // Windows-only |
| 1159 | 1166 | .object, .archive => continue, |
| ... | ... | @@ -1171,24 +1178,16 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { |
| 1171 | 1178 | }, |
| 1172 | 1179 | } |
| 1173 | 1180 | |
| 1174 | if (dso.exact_name) |exact_name| { | |
| 1175 | // LLD does not have a way to specify full path to the DSO while also specifying | |
| 1176 | // the exact value for NEEDED, so we have to use a combination of -L and -l args. | |
| 1177 | assert(mem.endsWith(u8, dso.path.sub_path, exact_name)); | |
| 1178 | const truncated_path: Cache.Path = .{ | |
| 1179 | .root_dir = dso.path.root_dir, | |
| 1180 | .sub_path = dso.path.sub_path[0 .. dso.path.sub_path.len - exact_name.len], | |
| 1181 | }; | |
| 1182 | try argv.ensureUnusedCapacity(3); | |
| 1183 | argv.appendAssumeCapacity("-L"); | |
| 1184 | argv.appendAssumeCapacity(try truncated_path.toString(arena)); | |
| 1185 | argv.appendAssumeCapacity(try arena.print("-l:{s}", .{exact_name})); | |
| 1186 | } else { | |
| 1187 | // By this time, we depend on these libs being dynamically linked | |
| 1188 | // libraries and not static libraries (the check for that needs to be earlier), | |
| 1189 | // but they could be full paths to .so files, in which case we | |
| 1190 | // want to avoid prepending "-l". | |
| 1191 | argv.appendAssumeCapacity(try dso.path.toString(arena)); | |
| 1181 | // By this time, we depend on these libs being dynamically linked libraries and | |
| 1182 | // not static libraries (the check for that needs to be earlier), but they could | |
| 1183 | // be full file paths, in which case we don't want to use the "-l:" strategy. | |
| 1184 | switch (dso.fallback_soname) { | |
| 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)), | |
| 1192 | 1191 | } |
| 1193 | 1192 | }, |
| 1194 | 1193 | }; |
src/link/MachO.zig+2-10| ... | ... | @@ -655,11 +655,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 655 | 655 | .object, .archive => |obj| try argv.append(try obj.path.toString(arena)), |
| 656 | 656 | .res => |res| try argv.append(try res.path.toString(arena)), |
| 657 | 657 | .dso => |dso| { |
| 658 | if (dso.exact_name) |exact_name| { | |
| 659 | try argv.appendSlice(&.{ "-l", try arena.print(":{s}", .{exact_name}) }); | |
| 660 | } else { | |
| 661 | try argv.append(try dso.path.toString(arena)); | |
| 662 | } | |
| 658 | try argv.append(try dso.path.toString(arena)); | |
| 663 | 659 | }, |
| 664 | 660 | }; |
| 665 | 661 | |
| ... | ... | @@ -789,11 +785,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 789 | 785 | } else { |
| 790 | 786 | argv.appendAssumeCapacity("-l"); |
| 791 | 787 | } |
| 792 | if (dso.exact_name) |exact_name| { | |
| 793 | argv.appendAssumeCapacity(try arena.print(":{s}", .{exact_name})); | |
| 794 | } else { | |
| 795 | argv.appendAssumeCapacity(try dso.path.toString(arena)); | |
| 796 | } | |
| 788 | argv.appendAssumeCapacity(try dso.path.toString(arena)); | |
| 797 | 789 | }, |
| 798 | 790 | }; |
| 799 | 791 |
src/main.zig+16-1| ... | ... | @@ -1351,6 +1351,7 @@ fn buildOutputType( |
| 1351 | 1351 | .search_strategy = lib_search_strategy, |
| 1352 | 1352 | .allow_so_scripts = allow_so_scripts, |
| 1353 | 1353 | }, |
| 1354 | .name_done = false, | |
| 1354 | 1355 | } }); |
| 1355 | 1356 | } else if (mem.eql(u8, arg, "--needed-library") or |
| 1356 | 1357 | mem.eql(u8, arg, "-needed-l") or |
| ... | ... | @@ -1366,6 +1367,7 @@ fn buildOutputType( |
| 1366 | 1367 | .search_strategy = lib_search_strategy, |
| 1367 | 1368 | .allow_so_scripts = allow_so_scripts, |
| 1368 | 1369 | }, |
| 1370 | .name_done = false, | |
| 1369 | 1371 | } }); |
| 1370 | 1372 | } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) { |
| 1371 | 1373 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ |
| ... | ... | @@ -1377,6 +1379,7 @@ fn buildOutputType( |
| 1377 | 1379 | .search_strategy = lib_search_strategy, |
| 1378 | 1380 | .allow_so_scripts = allow_so_scripts, |
| 1379 | 1381 | }, |
| 1382 | .name_done = false, | |
| 1380 | 1383 | } }); |
| 1381 | 1384 | } else if (mem.eql(u8, arg, "-D")) { |
| 1382 | 1385 | try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() }); |
| ... | ... | @@ -1839,6 +1842,7 @@ fn buildOutputType( |
| 1839 | 1842 | .search_strategy = lib_search_strategy, |
| 1840 | 1843 | .allow_so_scripts = allow_so_scripts, |
| 1841 | 1844 | }, |
| 1845 | .name_done = false, | |
| 1842 | 1846 | } }); |
| 1843 | 1847 | } else if (mem.cutPrefix(u8, arg, "-needed-l")) |name| { |
| 1844 | 1848 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ |
| ... | ... | @@ -1850,6 +1854,7 @@ fn buildOutputType( |
| 1850 | 1854 | .search_strategy = lib_search_strategy, |
| 1851 | 1855 | .allow_so_scripts = allow_so_scripts, |
| 1852 | 1856 | }, |
| 1857 | .name_done = false, | |
| 1853 | 1858 | } }); |
| 1854 | 1859 | } else if (mem.cutPrefix(u8, arg, "-weak-l")) |name| { |
| 1855 | 1860 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ |
| ... | ... | @@ -1861,6 +1866,7 @@ fn buildOutputType( |
| 1861 | 1866 | .search_strategy = lib_search_strategy, |
| 1862 | 1867 | .allow_so_scripts = allow_so_scripts, |
| 1863 | 1868 | }, |
| 1869 | .name_done = false, | |
| 1864 | 1870 | } }); |
| 1865 | 1871 | } else if (mem.startsWith(u8, arg, "-D")) { |
| 1866 | 1872 | try cc_argv.append(arena, arg); |
| ... | ... | @@ -2095,8 +2101,8 @@ fn buildOutputType( |
| 2095 | 2101 | .preferred_mode = lib_preferred_mode, |
| 2096 | 2102 | .search_strategy = lib_search_strategy, |
| 2097 | 2103 | .allow_so_scripts = allow_so_scripts, |
| 2098 | .name_done = true, | |
| 2099 | 2104 | }, |
| 2105 | .name_done = true, | |
| 2100 | 2106 | } }); |
| 2101 | 2107 | } else { |
| 2102 | 2108 | const compiler_rt_classification = target_util.classifyCompilerRtLibName(it.only_arg); |
| ... | ... | @@ -2122,6 +2128,7 @@ fn buildOutputType( |
| 2122 | 2128 | .search_strategy = lib_search_strategy, |
| 2123 | 2129 | .allow_so_scripts = allow_so_scripts, |
| 2124 | 2130 | }, |
| 2131 | .name_done = false, | |
| 2125 | 2132 | } }); |
| 2126 | 2133 | } |
| 2127 | 2134 | } |
| ... | ... | @@ -2552,6 +2559,7 @@ fn buildOutputType( |
| 2552 | 2559 | .search_strategy = lib_search_strategy, |
| 2553 | 2560 | .allow_so_scripts = allow_so_scripts, |
| 2554 | 2561 | }, |
| 2562 | .name_done = false, | |
| 2555 | 2563 | } }), |
| 2556 | 2564 | .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }), |
| 2557 | 2565 | .headerpad_max_install_names => headerpad_max_install_names = true, |
| ... | ... | @@ -2921,6 +2929,7 @@ fn buildOutputType( |
| 2921 | 2929 | .search_strategy = lib_search_strategy, |
| 2922 | 2930 | .allow_so_scripts = allow_so_scripts, |
| 2923 | 2931 | }, |
| 2932 | .name_done = false, | |
| 2924 | 2933 | } }); |
| 2925 | 2934 | } else if (mem.cutPrefix(u8, arg, "-weak-l")) |rest| { |
| 2926 | 2935 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ |
| ... | ... | @@ -2932,6 +2941,7 @@ fn buildOutputType( |
| 2932 | 2941 | .search_strategy = lib_search_strategy, |
| 2933 | 2942 | .allow_so_scripts = allow_so_scripts, |
| 2934 | 2943 | }, |
| 2944 | .name_done = false, | |
| 2935 | 2945 | } }); |
| 2936 | 2946 | } else if (mem.eql(u8, arg, "-weak_library")) { |
| 2937 | 2947 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ |
| ... | ... | @@ -2943,6 +2953,7 @@ fn buildOutputType( |
| 2943 | 2953 | .search_strategy = lib_search_strategy, |
| 2944 | 2954 | .allow_so_scripts = allow_so_scripts, |
| 2945 | 2955 | }, |
| 2956 | .name_done = false, | |
| 2946 | 2957 | } }); |
| 2947 | 2958 | } else if (mem.eql(u8, arg, "-compatibility_version")) { |
| 2948 | 2959 | const compat_version = linker_args_it.nextOrFatal(); |
| ... | ... | @@ -4183,6 +4194,10 @@ fn createModule( |
| 4183 | 4194 | fatal("cannot use absolute path as a system library: {s}", .{lib_name}); |
| 4184 | 4195 | } |
| 4185 | 4196 | |
| 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 | ||
| 4186 | 4201 | unresolved_link_inputs.appendAssumeCapacity(cli_link_input); |
| 4187 | 4202 | any_name_queries_remaining = true; |
| 4188 | 4203 | }, |