authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-14 22:24:46-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-23 16:27:38-07:00
log5b016e290a5ba335b295afeae104af6b3396a425
tree370ce44e11911967fefd986c42c53c12c22f320b
parenta4cc344aa0947f5b0d0e1a872e6d003b8e580976

move ld script processing to the frontend

along with the relevant logic, making the libraries within subject to the same search criteria as all the other libraries. this unfortunately means doing file system access on all .so files when targeting ELF to determine if they are linker scripts, however, I have a plan to address this.

6 files changed, 328 insertions(+), 297 deletions(-)

src/Compilation.zig+10-16
...@@ -1003,10 +1003,11 @@ pub const LinkObject = struct {...@@ -1003,10 +1003,11 @@ pub const LinkObject = struct {
1003 path: Path,1003 path: Path,
1004 must_link: bool = false,1004 must_link: bool = false,
1005 needed: bool = false,1005 needed: bool = false,
1006 // When the library is passed via a positional argument, it will be1006 weak: bool = false,
1007 // added as a full path. If it's `-l<lib>`, then just the basename.1007 /// When the library is passed via a positional argument, it will be
1008 //1008 /// added as a full path. If it's `-l<lib>`, then just the basename.
1009 // Consistent with `withLOption` variable name in lld ELF driver.1009 ///
1010 /// Consistent with `withLOption` variable name in lld ELF driver.
1010 loption: bool = false,1011 loption: bool = false,
10111012
1012 pub fn isObject(lo: LinkObject) bool {1013 pub fn isObject(lo: LinkObject) bool {
...@@ -1061,6 +1062,9 @@ pub const CreateOptions = struct {...@@ -1061,6 +1062,9 @@ pub const CreateOptions = struct {
1061 /// this flag would be set to disable this machinery to avoid false positives.1062 /// this flag would be set to disable this machinery to avoid false positives.
1062 disable_lld_caching: bool = false,1063 disable_lld_caching: bool = false,
1063 cache_mode: CacheMode = .incremental,1064 cache_mode: CacheMode = .incremental,
1065 /// This field is intended to be removed.
1066 /// The ELF implementation no longer uses this data, however the MachO and COFF
1067 /// implementations still do.
1064 lib_dirs: []const []const u8 = &[0][]const u8{},1068 lib_dirs: []const []const u8 = &[0][]const u8{},
1065 rpath_list: []const []const u8 = &[0][]const u8{},1069 rpath_list: []const []const u8 = &[0][]const u8{},
1066 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty,1070 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty,
...@@ -2563,6 +2567,7 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -2563,6 +2567,7 @@ fn addNonIncrementalStuffToCacheManifest(
2563 _ = try man.addFilePath(obj.path, null);2567 _ = try man.addFilePath(obj.path, null);
2564 man.hash.add(obj.must_link);2568 man.hash.add(obj.must_link);
2565 man.hash.add(obj.needed);2569 man.hash.add(obj.needed);
2570 man.hash.add(obj.weak);
2566 man.hash.add(obj.loption);2571 man.hash.add(obj.loption);
2567 }2572 }
25682573
...@@ -3219,18 +3224,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3219,18 +3224,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3219 }));3224 }));
3220 }3225 }
32213226
3222 for (comp.link_diags.msgs.items) |link_err| {3227 try comp.link_diags.addMessagesToBundle(&bundle);
3223 try bundle.addRootErrorMessage(.{
3224 .msg = try bundle.addString(link_err.msg),
3225 .notes_len = @intCast(link_err.notes.len),
3226 });
3227 const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
3228 for (link_err.notes, 0..) |note, i| {
3229 bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
3230 .msg = try bundle.addString(note.msg),
3231 }));
3232 }
3233 }
32343228
3235 if (comp.zcu) |zcu| {3229 if (comp.zcu) |zcu| {
3236 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {3230 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
src/link.zig+17
...@@ -24,6 +24,8 @@ const lldMain = @import("main.zig").lldMain;...@@ -24,6 +24,8 @@ const lldMain = @import("main.zig").lldMain;
24const Package = @import("Package.zig");24const Package = @import("Package.zig");
25const dev = @import("dev.zig");25const dev = @import("dev.zig");
2626
27pub const LdScript = @import("link/LdScript.zig");
28
27/// When adding a new field, remember to update `hashAddSystemLibs`.29/// When adding a new field, remember to update `hashAddSystemLibs`.
28/// These are *always* dynamically linked. Static libraries will be30/// These are *always* dynamically linked. Static libraries will be
29/// provided as positional arguments.31/// provided as positional arguments.
...@@ -336,6 +338,21 @@ pub const Diags = struct {...@@ -336,6 +338,21 @@ pub const Diags = struct {
336 log.debug("memory allocation failure", .{});338 log.debug("memory allocation failure", .{});
337 diags.flags.alloc_failure_occurred = true;339 diags.flags.alloc_failure_occurred = true;
338 }340 }
341
342 pub fn addMessagesToBundle(diags: *const Diags, bundle: *std.zig.ErrorBundle.Wip) Allocator.Error!void {
343 for (diags.msgs.items) |link_err| {
344 try bundle.addRootErrorMessage(.{
345 .msg = try bundle.addString(link_err.msg),
346 .notes_len = @intCast(link_err.notes.len),
347 });
348 const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
349 for (link_err.notes, 0..) |note, i| {
350 bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
351 .msg = try bundle.addString(note.msg),
352 }));
353 }
354 }
355 }
339};356};
340357
341pub fn hashAddSystemLibs(358pub fn hashAddSystemLibs(
src/link/Elf.zig+14-151
...@@ -1,5 +1,4 @@...@@ -1,5 +1,4 @@
1pub const Atom = @import("Elf/Atom.zig");1pub const Atom = @import("Elf/Atom.zig");
2pub const LdScript = @import("LdScript.zig");
32
4base: link.File,3base: link.File,
5rpath_table: std.StringArrayHashMapUnmanaged(void),4rpath_table: std.StringArrayHashMapUnmanaged(void),
...@@ -16,7 +15,6 @@ z_relro: bool,...@@ -16,7 +15,6 @@ z_relro: bool,
16z_common_page_size: ?u64,15z_common_page_size: ?u64,
17/// TODO make this non optional and resolve the default in open()16/// TODO make this non optional and resolve the default in open()
18z_max_page_size: ?u64,17z_max_page_size: ?u64,
19lib_dirs: []const []const u8,
20hash_style: HashStyle,18hash_style: HashStyle,
21compress_debug_sections: CompressDebugSections,19compress_debug_sections: CompressDebugSections,
22symbol_wrap_set: std.StringArrayHashMapUnmanaged(void),20symbol_wrap_set: std.StringArrayHashMapUnmanaged(void),
...@@ -329,7 +327,6 @@ pub fn createEmpty(...@@ -329,7 +327,6 @@ pub fn createEmpty(
329 .z_relro = options.z_relro,327 .z_relro = options.z_relro,
330 .z_common_page_size = options.z_common_page_size,328 .z_common_page_size = options.z_common_page_size,
331 .z_max_page_size = options.z_max_page_size,329 .z_max_page_size = options.z_max_page_size,
332 .lib_dirs = options.lib_dirs,
333 .hash_style = options.hash_style,330 .hash_style = options.hash_style,
334 .compress_debug_sections = options.compress_debug_sections,331 .compress_debug_sections = options.compress_debug_sections,
335 .symbol_wrap_set = options.symbol_wrap_set,332 .symbol_wrap_set = options.symbol_wrap_set,
...@@ -845,30 +842,17 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -845,30 +842,17 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
845 if (comp.libc_installation) |lc| {842 if (comp.libc_installation) |lc| {
846 const flags = target_util.libcFullLinkFlags(target);843 const flags = target_util.libcFullLinkFlags(target);
847844
848 var test_path = std.ArrayList(u8).init(arena);
849 var checked_paths = std.ArrayList([]const u8).init(arena);
850
851 for (flags) |flag| {845 for (flags) |flag| {
852 checked_paths.clearRetainingCapacity();846 assert(mem.startsWith(u8, flag, "-l"));
853 const lib_name = flag["-l".len..];847 const lib_name = flag["-l".len..];
854848 const suffix = switch (comp.config.link_mode) {
855 success: {849 .static => target.staticLibSuffix(),
856 if (!self.base.isStatic()) {850 .dynamic => target.dynamicLibSuffix(),
857 if (try self.accessLibPath(arena, &test_path, &checked_paths, lc.crt_dir.?, lib_name, .dynamic))851 };
858 break :success;852 const lib_path = try std.fmt.allocPrint(arena, "{s}/lib{s}{s}", .{
859 }853 lc.crt_dir.?, lib_name, suffix,
860 if (try self.accessLibPath(arena, &test_path, &checked_paths, lc.crt_dir.?, lib_name, .static))854 });
861 break :success;855 const resolved_path = Path.initCwd(lib_path);
862
863 diags.addMissingLibraryError(
864 checked_paths.items,
865 "missing system library: '{s}' was not found",
866 .{lib_name},
867 );
868 continue;
869 }
870
871 const resolved_path = Path.initCwd(try arena.dupe(u8, test_path.items));
872 parseInputReportingFailure(self, resolved_path, false, false);856 parseInputReportingFailure(self, resolved_path, false, false);
873 }857 }
874 } else if (target.isGnuLibC()) {858 } else if (target.isGnuLibC()) {
...@@ -1194,11 +1178,6 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {...@@ -1194,11 +1178,6 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
1194 if (csu.crti) |path| try argv.append(try path.toString(arena));1178 if (csu.crti) |path| try argv.append(try path.toString(arena));
1195 if (csu.crtbegin) |path| try argv.append(try path.toString(arena));1179 if (csu.crtbegin) |path| try argv.append(try path.toString(arena));
11961180
1197 for (self.lib_dirs) |lib_dir| {
1198 try argv.append("-L");
1199 try argv.append(lib_dir);
1200 }
1201
1202 if (comp.config.link_libc) {1181 if (comp.config.link_libc) {
1203 if (self.base.comp.libc_installation) |libc_installation| {1182 if (self.base.comp.libc_installation) |libc_installation| {
1204 try argv.append("-L");1183 try argv.append("-L");
...@@ -1340,7 +1319,7 @@ pub const ParseError = error{...@@ -1340,7 +1319,7 @@ pub const ParseError = error{
1340 NotSupported,1319 NotSupported,
1341 InvalidCharacter,1320 InvalidCharacter,
1342 UnknownFileType,1321 UnknownFileType,
1343} || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;1322} || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;
13441323
1345fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CrtFile) void {1324fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CrtFile) void {
1346 parseInputReportingFailure(self, crt_file.full_object_path, false, false);1325 parseInputReportingFailure(self, crt_file.full_object_path, false, false);
...@@ -1358,23 +1337,12 @@ pub fn parseInputReportingFailure(self: *Elf, path: Path, needed: bool, must_lin...@@ -1358,23 +1337,12 @@ pub fn parseInputReportingFailure(self: *Elf, path: Path, needed: bool, must_lin
1358 .needed = needed,1337 .needed = needed,
1359 }, &self.shared_objects, &self.files, target) catch |err| switch (err) {1338 }, &self.shared_objects, &self.files, target) catch |err| switch (err) {
1360 error.LinkFailure => return, // already reported1339 error.LinkFailure => return, // already reported
1361 error.BadMagic, error.UnexpectedEndOfFile => {
1362 // It could be a linker script.
1363 self.parseLdScript(.{ .path = path, .needed = needed }) catch |err2| switch (err2) {
1364 error.LinkFailure => return, // already reported
1365 else => |e| diags.addParseError(path, "failed to parse linker script: {s}", .{@errorName(e)}),
1366 };
1367 },
1368 else => |e| diags.addParseError(path, "failed to parse shared object: {s}", .{@errorName(e)}),1340 else => |e| diags.addParseError(path, "failed to parse shared object: {s}", .{@errorName(e)}),
1369 },1341 },
1370 .static_library => parseArchive(self, path, must_link) catch |err| switch (err) {1342 .static_library => parseArchive(self, path, must_link) catch |err| switch (err) {
1371 error.LinkFailure => return, // already reported1343 error.LinkFailure => return, // already reported
1372 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),1344 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1373 },1345 },
1374 .unknown => self.parseLdScript(.{ .path = path, .needed = needed }) catch |err| switch (err) {
1375 error.LinkFailure => return, // already reported
1376 else => |e| diags.addParseError(path, "failed to parse linker script: {s}", .{@errorName(e)}),
1377 },
1378 else => diags.addParseError(path, "unrecognized file type", .{}),1346 else => diags.addParseError(path, "unrecognized file type", .{}),
1379 }1347 }
1380}1348}
...@@ -1512,72 +1480,6 @@ fn parseSharedObject(...@@ -1512,72 +1480,6 @@ fn parseSharedObject(
1512 }1480 }
1513}1481}
15141482
1515fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
1516 const tracy = trace(@src());
1517 defer tracy.end();
1518
1519 const comp = self.base.comp;
1520 const gpa = comp.gpa;
1521 const diags = &comp.link_diags;
1522
1523 const in_file = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{});
1524 defer in_file.close();
1525 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
1526 defer gpa.free(data);
1527
1528 var script = try LdScript.parse(gpa, diags, lib.path, data);
1529 defer script.deinit(gpa);
1530
1531 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
1532 defer arena_allocator.deinit();
1533 const arena = arena_allocator.allocator();
1534
1535 var test_path = std.ArrayList(u8).init(arena);
1536 var checked_paths = std.ArrayList([]const u8).init(arena);
1537
1538 for (script.args) |script_arg| {
1539 checked_paths.clearRetainingCapacity();
1540
1541 success: {
1542 if (mem.startsWith(u8, script_arg.path, "-l")) {
1543 const lib_name = script_arg.path["-l".len..];
1544
1545 for (self.lib_dirs) |lib_dir| {
1546 if (!self.base.isStatic()) {
1547 if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, lib_name, .dynamic))
1548 break :success;
1549 }
1550 if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, lib_name, .static))
1551 break :success;
1552 }
1553 } else {
1554 var buffer: [fs.max_path_bytes]u8 = undefined;
1555 if (fs.realpath(script_arg.path, &buffer)) |path| {
1556 test_path.clearRetainingCapacity();
1557 try test_path.writer().writeAll(path);
1558 break :success;
1559 } else |_| {}
1560
1561 try checked_paths.append(try arena.dupe(u8, script_arg.path));
1562 for (self.lib_dirs) |lib_dir| {
1563 if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, script_arg.path, null))
1564 break :success;
1565 }
1566 }
1567
1568 diags.addMissingLibraryError(
1569 checked_paths.items,
1570 "missing library dependency: GNU ld script '{}' requires '{s}', but file not found",
1571 .{ @as(Path, lib.path), script_arg.path },
1572 );
1573 continue;
1574 }
1575
1576 const full_path = Path.initCwd(test_path.items);
1577 parseInputReportingFailure(self, full_path, script_arg.needed, false);
1578 }
1579}
1580
1581pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Word) !void {1483pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Word) !void {
1582 if (self.first_eflags == null) {1484 if (self.first_eflags == null) {
1583 self.first_eflags = e_flags;1485 self.first_eflags = e_flags;
...@@ -1618,39 +1520,6 @@ pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Word) !vo...@@ -1618,39 +1520,6 @@ pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Word) !vo
1618 }1520 }
1619}1521}
16201522
1621fn accessLibPath(
1622 self: *Elf,
1623 arena: Allocator,
1624 test_path: *std.ArrayList(u8),
1625 checked_paths: ?*std.ArrayList([]const u8),
1626 lib_dir_path: []const u8,
1627 lib_name: []const u8,
1628 link_mode: ?std.builtin.LinkMode,
1629) !bool {
1630 const sep = fs.path.sep_str;
1631 const target = self.getTarget();
1632 test_path.clearRetainingCapacity();
1633 const prefix = if (link_mode != null) "lib" else "";
1634 const suffix = if (link_mode) |mode| switch (mode) {
1635 .static => target.staticLibSuffix(),
1636 .dynamic => target.dynamicLibSuffix(),
1637 } else "";
1638 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
1639 lib_dir_path,
1640 prefix,
1641 lib_name,
1642 suffix,
1643 });
1644 if (checked_paths) |cpaths| {
1645 try cpaths.append(try arena.dupe(u8, test_path.items));
1646 }
1647 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1648 error.FileNotFound => return false,
1649 else => |e| return e,
1650 };
1651 return true;
1652}
1653
1654/// When resolving symbols, we approach the problem similarly to `mold`.1523/// When resolving symbols, we approach the problem similarly to `mold`.
1655/// 1. Resolve symbols across all objects (including those preemptively extracted archives).1524/// 1. Resolve symbols across all objects (including those preemptively extracted archives).
1656/// 2. Resolve symbols across all shared objects.1525/// 2. Resolve symbols across all shared objects.
...@@ -1840,7 +1709,7 @@ pub fn initOutputSection(self: *Elf, args: struct {...@@ -1840,7 +1709,7 @@ pub fn initOutputSection(self: *Elf, args: struct {
1840 ".dtors", ".gnu.warning",1709 ".dtors", ".gnu.warning",
1841 };1710 };
1842 inline for (name_prefixes) |prefix| {1711 inline for (name_prefixes) |prefix| {
1843 if (std.mem.eql(u8, args.name, prefix) or std.mem.startsWith(u8, args.name, prefix ++ ".")) {1712 if (mem.eql(u8, args.name, prefix) or mem.startsWith(u8, args.name, prefix ++ ".")) {
1844 break :blk prefix;1713 break :blk prefix;
1845 }1714 }
1846 }1715 }
...@@ -1852,9 +1721,9 @@ pub fn initOutputSection(self: *Elf, args: struct {...@@ -1852,9 +1721,9 @@ pub fn initOutputSection(self: *Elf, args: struct {
1852 switch (args.type) {1721 switch (args.type) {
1853 elf.SHT_NULL => unreachable,1722 elf.SHT_NULL => unreachable,
1854 elf.SHT_PROGBITS => {1723 elf.SHT_PROGBITS => {
1855 if (std.mem.eql(u8, args.name, ".init_array") or std.mem.startsWith(u8, args.name, ".init_array."))1724 if (mem.eql(u8, args.name, ".init_array") or mem.startsWith(u8, args.name, ".init_array."))
1856 break :tt elf.SHT_INIT_ARRAY;1725 break :tt elf.SHT_INIT_ARRAY;
1857 if (std.mem.eql(u8, args.name, ".fini_array") or std.mem.startsWith(u8, args.name, ".fini_array."))1726 if (mem.eql(u8, args.name, ".fini_array") or mem.startsWith(u8, args.name, ".fini_array."))
1858 break :tt elf.SHT_FINI_ARRAY;1727 break :tt elf.SHT_FINI_ARRAY;
1859 break :tt args.type;1728 break :tt args.type;
1860 },1729 },
...@@ -1971,7 +1840,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -1971,7 +1840,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
1971 man.hash.add(comp.link_eh_frame_hdr);1840 man.hash.add(comp.link_eh_frame_hdr);
1972 man.hash.add(self.emit_relocs);1841 man.hash.add(self.emit_relocs);
1973 man.hash.add(comp.config.rdynamic);1842 man.hash.add(comp.config.rdynamic);
1974 man.hash.addListOfBytes(self.lib_dirs);
1975 man.hash.addListOfBytes(self.rpath_table.keys());1843 man.hash.addListOfBytes(self.rpath_table.keys());
1976 if (output_mode == .Exe) {1844 if (output_mode == .Exe) {
1977 man.hash.add(self.base.stack_size);1845 man.hash.add(self.base.stack_size);
...@@ -2265,11 +2133,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -2265,11 +2133,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
2265 try argv.appendSlice(&.{ "-wrap", symbol_name });2133 try argv.appendSlice(&.{ "-wrap", symbol_name });
2266 }2134 }
22672135
2268 for (self.lib_dirs) |lib_dir| {
2269 try argv.append("-L");
2270 try argv.append(lib_dir);
2271 }
2272
2273 if (comp.config.link_libc) {2136 if (comp.config.link_libc) {
2274 if (comp.libc_installation) |libc_installation| {2137 if (comp.libc_installation) |libc_installation| {
2275 try argv.append("-L");2138 try argv.append("-L");
...@@ -4868,7 +4731,7 @@ fn shString(...@@ -4868,7 +4731,7 @@ fn shString(
4868 off: u32,4731 off: u32,
4869) [:0]const u8 {4732) [:0]const u8 {
4870 const slice = shstrtab[off..];4733 const slice = shstrtab[off..];
4871 return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];4734 return slice[0..mem.indexOfScalar(u8, slice, 0).? :0];
4872}4735}
48734736
4874pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 {4737pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 {
src/link/LdScript.zig-1
...@@ -14,7 +14,6 @@ pub fn deinit(ls: *LdScript, gpa: Allocator) void {...@@ -14,7 +14,6 @@ pub fn deinit(ls: *LdScript, gpa: Allocator) void {
1414
15pub const Error = error{15pub const Error = error{
16 LinkFailure,16 LinkFailure,
17 UnexpectedToken,
18 UnknownCpuArch,17 UnknownCpuArch,
19 OutOfMemory,18 OutOfMemory,
20};19};
src/main.zig+281-113
...@@ -3618,6 +3618,28 @@ fn buildOutputType(...@@ -3618,6 +3618,28 @@ fn buildOutputType(
3618 return cleanExit();3618 return cleanExit();
3619}3619}
36203620
3621const LinkerInput = union(enum) {
3622 /// An argument like: -l[name]
3623 named: Named,
3624 /// When a file path is provided.
3625 path: struct {
3626 path: Path,
3627 /// We still need all this info because the path may point to a .so
3628 /// file which may actually be a "linker script" that references
3629 /// library names which need to be resolved.
3630 info: SystemLib,
3631 },
3632 /// Put exactly this string in the dynamic section, no rpath.
3633 exact: struct {
3634 name: []const u8,
3635 },
3636
3637 const Named = struct {
3638 name: []const u8,
3639 info: SystemLib,
3640 };
3641};
3642
3621const CreateModule = struct {3643const CreateModule = struct {
3622 global_cache_directory: Cache.Directory,3644 global_cache_directory: Cache.Directory,
3623 modules: std.StringArrayHashMapUnmanaged(CliModule),3645 modules: std.StringArrayHashMapUnmanaged(CliModule),
...@@ -3760,10 +3782,7 @@ fn createModule(...@@ -3760,10 +3782,7 @@ fn createModule(
3760 // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.3782 // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.
3761 // We need to know whether the set of system libraries contains anything besides these3783 // We need to know whether the set of system libraries contains anything besides these
3762 // to decide whether to trigger native path detection logic.3784 // to decide whether to trigger native path detection logic.
3763 var external_system_libs: std.MultiArrayList(struct {3785 var external_linker_inputs: std.ArrayListUnmanaged(LinkerInput) = .empty;
3764 name: []const u8,
3765 info: SystemLib,
3766 }) = .{};
3767 for (create_module.system_libs.keys(), create_module.system_libs.values()) |lib_name, info| {3786 for (create_module.system_libs.keys(), create_module.system_libs.values()) |lib_name, info| {
3768 if (std.zig.target.isLibCLibName(target, lib_name)) {3787 if (std.zig.target.isLibCLibName(target, lib_name)) {
3769 create_module.opts.link_libc = true;3788 create_module.opts.link_libc = true;
...@@ -3815,13 +3834,13 @@ fn createModule(...@@ -3815,13 +3834,13 @@ fn createModule(
3815 }3834 }
3816 }3835 }
38173836
3818 try external_system_libs.append(arena, .{3837 try external_linker_inputs.append(arena, .{ .named = .{
3819 .name = lib_name,3838 .name = lib_name,
3820 .info = info,3839 .info = info,
3821 });3840 } });
3822 }3841 }
3823 // After this point, external_system_libs is used instead of system_libs.3842 // After this point, external_linker_inputs is used instead of system_libs.
3824 if (external_system_libs.len != 0)3843 if (external_linker_inputs.items.len != 0)
3825 create_module.want_native_include_dirs = true;3844 create_module.want_native_include_dirs = true;
38263845
3827 // Resolve the library path arguments with respect to sysroot.3846 // Resolve the library path arguments with respect to sysroot.
...@@ -3878,7 +3897,7 @@ fn createModule(...@@ -3878,7 +3897,7 @@ fn createModule(
3878 }3897 }
38793898
3880 if (builtin.target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and3899 if (builtin.target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and
3881 external_system_libs.len != 0)3900 external_linker_inputs.items.len != 0)
3882 {3901 {
3883 if (create_module.libc_installation == null) {3902 if (create_module.libc_installation == null) {
3884 create_module.libc_installation = LibCInstallation.findNative(.{3903 create_module.libc_installation = LibCInstallation.findNative(.{
...@@ -3899,20 +3918,67 @@ fn createModule(...@@ -3899,20 +3918,67 @@ fn createModule(
3899 // If any libs in this list are statically provided, we omit them from the3918 // If any libs in this list are statically provided, we omit them from the
3900 // resolved list and populate the link_objects array instead.3919 // resolved list and populate the link_objects array instead.
3901 {3920 {
3902 var test_path = std.ArrayList(u8).init(gpa);3921 var test_path: std.ArrayListUnmanaged(u8) = .empty;
3903 defer test_path.deinit();3922 defer test_path.deinit(gpa);
3923
3924 var checked_paths: std.ArrayListUnmanaged(u8) = .empty;
3925 defer checked_paths.deinit(gpa);
39043926
3905 var checked_paths = std.ArrayList(u8).init(gpa);3927 var ld_script_bytes: std.ArrayListUnmanaged(u8) = .empty;
3906 defer checked_paths.deinit();3928 defer ld_script_bytes.deinit(gpa);
39073929
3908 var failed_libs = std.ArrayList(struct {3930 var failed_libs: std.ArrayListUnmanaged(struct {
3909 name: []const u8,3931 name: []const u8,
3910 strategy: SystemLib.SearchStrategy,3932 strategy: SystemLib.SearchStrategy,
3911 checked_paths: []const u8,3933 checked_paths: []const u8,
3912 preferred_mode: std.builtin.LinkMode,3934 preferred_mode: std.builtin.LinkMode,
3913 }).init(arena);3935 }) = .empty;
3936
3937 // Convert external system libs into a stack so that items can be
3938 // pushed to it.
3939 //
3940 // This is necessary because shared objects might turn out to be
3941 // "linker scripts" that in fact resolve to one or more other
3942 // external system libs, including parameters such as "needed".
3943 //
3944 // Unfortunately, such files need to be detected immediately, so
3945 // that this library search logic can be applied to them.
3946 mem.reverse(LinkerInput, external_linker_inputs.items);
3947
3948 syslib: while (external_linker_inputs.popOrNull()) |external_linker_input| {
3949 const external_system_lib: LinkerInput.Named = switch (external_linker_input) {
3950 .named => |named| named,
3951 .path => |p| p: {
3952 if (fs.path.isAbsolute(p.path.sub_path)) {
3953 try create_module.link_objects.append(arena, .{
3954 .path = p.path,
3955 .needed = p.info.needed,
3956 .weak = p.info.weak,
3957 });
3958 continue;
3959 }
3960 const lib_name, const link_mode = stripLibPrefixAndSuffix(p.path.sub_path, target);
3961 break :p .{
3962 .name = lib_name,
3963 .info = .{
3964 .needed = p.info.needed,
3965 .weak = p.info.weak,
3966 .preferred_mode = link_mode,
3967 .search_strategy = .no_fallback,
3968 },
3969 };
3970 },
3971 .exact => |exact| {
3972 try create_module.link_objects.append(arena, .{
3973 .path = Path.initCwd(exact.name),
3974 .loption = true,
3975 });
3976 continue;
3977 },
3978 };
3979 const lib_name = external_system_lib.name;
3980 const info = external_system_lib.info;
39143981
3915 syslib: for (external_system_libs.items(.name), external_system_libs.items(.info)) |lib_name, info| {
3916 // Checked in the first pass above while looking for libc libraries.3982 // Checked in the first pass above while looking for libc libraries.
3917 assert(!fs.path.isAbsolute(lib_name));3983 assert(!fs.path.isAbsolute(lib_name));
39183984
...@@ -3921,33 +3987,26 @@ fn createModule(...@@ -3921,33 +3987,26 @@ fn createModule(
3921 switch (info.search_strategy) {3987 switch (info.search_strategy) {
3922 .mode_first, .no_fallback => {3988 .mode_first, .no_fallback => {
3923 // check for preferred mode3989 // check for preferred mode
3924 for (create_module.lib_dirs.items) |lib_dir_path| {3990 for (create_module.lib_dirs.items) |lib_dir_path| switch (try accessLibPath(
3925 if (try accessLibPath(3991 gpa,
3926 &test_path,3992 arena,
3927 &checked_paths,3993 &test_path,
3928 lib_dir_path,3994 &checked_paths,
3929 lib_name,3995 &external_linker_inputs,
3930 target,3996 create_module,
3931 info.preferred_mode,3997 &ld_script_bytes,
3932 )) {3998 lib_dir_path,
3933 const path = Path.initCwd(try arena.dupe(u8, test_path.items));3999 lib_name,
3934 switch (info.preferred_mode) {4000 target,
3935 .static => try create_module.link_objects.append(arena, .{ .path = path }),4001 info.preferred_mode,
3936 .dynamic => try create_module.resolved_system_libs.append(arena, .{4002 info,
3937 .name = lib_name,4003 )) {
3938 .lib = .{4004 .ok => continue :syslib,
3939 .needed = info.needed,4005 .no_match => {},
3940 .weak = info.weak,4006 };
3941 .path = path,
3942 },
3943 }),
3944 }
3945 continue :syslib;
3946 }
3947 }
3948 // check for fallback mode4007 // check for fallback mode
3949 if (info.search_strategy == .no_fallback) {4008 if (info.search_strategy == .no_fallback) {
3950 try failed_libs.append(.{4009 try failed_libs.append(arena, .{
3951 .name = lib_name,4010 .name = lib_name,
3952 .strategy = info.search_strategy,4011 .strategy = info.search_strategy,
3953 .checked_paths = try arena.dupe(u8, checked_paths.items),4012 .checked_paths = try arena.dupe(u8, checked_paths.items),
...@@ -3955,31 +4014,24 @@ fn createModule(...@@ -3955,31 +4014,24 @@ fn createModule(
3955 });4014 });
3956 continue :syslib;4015 continue :syslib;
3957 }4016 }
3958 for (create_module.lib_dirs.items) |lib_dir_path| {4017 for (create_module.lib_dirs.items) |lib_dir_path| switch (try accessLibPath(
3959 if (try accessLibPath(4018 gpa,
3960 &test_path,4019 arena,
3961 &checked_paths,4020 &test_path,
3962 lib_dir_path,4021 &checked_paths,
3963 lib_name,4022 &external_linker_inputs,
3964 target,4023 create_module,
3965 info.fallbackMode(),4024 &ld_script_bytes,
3966 )) {4025 lib_dir_path,
3967 const path = Path.initCwd(try arena.dupe(u8, test_path.items));4026 lib_name,
3968 switch (info.fallbackMode()) {4027 target,
3969 .static => try create_module.link_objects.append(arena, .{ .path = path }),4028 info.fallbackMode(),
3970 .dynamic => try create_module.resolved_system_libs.append(arena, .{4029 info,
3971 .name = lib_name,4030 )) {
3972 .lib = .{4031 .ok => continue :syslib,
3973 .needed = info.needed,4032 .no_match => {},
3974 .weak = info.weak,4033 };
3975 .path = path,4034 try failed_libs.append(arena, .{
3976 },
3977 }),
3978 }
3979 continue :syslib;
3980 }
3981 }
3982 try failed_libs.append(.{
3983 .name = lib_name,4035 .name = lib_name,
3984 .strategy = info.search_strategy,4036 .strategy = info.search_strategy,
3985 .checked_paths = try arena.dupe(u8, checked_paths.items),4037 .checked_paths = try arena.dupe(u8, checked_paths.items),
...@@ -3990,54 +4042,44 @@ fn createModule(...@@ -3990,54 +4042,44 @@ fn createModule(
3990 .paths_first => {4042 .paths_first => {
3991 for (create_module.lib_dirs.items) |lib_dir_path| {4043 for (create_module.lib_dirs.items) |lib_dir_path| {
3992 // check for preferred mode4044 // check for preferred mode
3993 if (try accessLibPath(4045 switch (try accessLibPath(
4046 gpa,
4047 arena,
3994 &test_path,4048 &test_path,
3995 &checked_paths,4049 &checked_paths,
4050 &external_linker_inputs,
4051 create_module,
4052 &ld_script_bytes,
3996 lib_dir_path,4053 lib_dir_path,
3997 lib_name,4054 lib_name,
3998 target,4055 target,
3999 info.preferred_mode,4056 info.preferred_mode,
4057 info,
4000 )) {4058 )) {
4001 const path = Path.initCwd(try arena.dupe(u8, test_path.items));4059 .ok => continue :syslib,
4002 switch (info.preferred_mode) {4060 .no_match => {},
4003 .static => try create_module.link_objects.append(arena, .{ .path = path }),
4004 .dynamic => try create_module.resolved_system_libs.append(arena, .{
4005 .name = lib_name,
4006 .lib = .{
4007 .needed = info.needed,
4008 .weak = info.weak,
4009 .path = path,
4010 },
4011 }),
4012 }
4013 continue :syslib;
4014 }4061 }
40154062
4016 // check for fallback mode4063 // check for fallback mode
4017 if (try accessLibPath(4064 switch (try accessLibPath(
4065 gpa,
4066 arena,
4018 &test_path,4067 &test_path,
4019 &checked_paths,4068 &checked_paths,
4069 &external_linker_inputs,
4070 create_module,
4071 &ld_script_bytes,
4020 lib_dir_path,4072 lib_dir_path,
4021 lib_name,4073 lib_name,
4022 target,4074 target,
4023 info.fallbackMode(),4075 info.fallbackMode(),
4076 info,
4024 )) {4077 )) {
4025 const path = Path.initCwd(try arena.dupe(u8, test_path.items));4078 .ok => continue :syslib,
4026 switch (info.fallbackMode()) {4079 .no_match => {},
4027 .static => try create_module.link_objects.append(arena, .{ .path = path }),
4028 .dynamic => try create_module.resolved_system_libs.append(arena, .{
4029 .name = lib_name,
4030 .lib = .{
4031 .needed = info.needed,
4032 .weak = info.weak,
4033 .path = path,
4034 },
4035 }),
4036 }
4037 continue :syslib;
4038 }4080 }
4039 }4081 }
4040 try failed_libs.append(.{4082 try failed_libs.append(arena, .{
4041 .name = lib_name,4083 .name = lib_name,
4042 .strategy = info.search_strategy,4084 .strategy = info.search_strategy,
4043 .checked_paths = try arena.dupe(u8, checked_paths.items),4085 .checked_paths = try arena.dupe(u8, checked_paths.items),
...@@ -4059,8 +4101,8 @@ fn createModule(...@@ -4059,8 +4101,8 @@ fn createModule(
4059 process.exit(1);4101 process.exit(1);
4060 }4102 }
4061 }4103 }
4062 // After this point, create_module.resolved_system_libs is used instead of4104 // After this point, create_module.resolved_system_libs is used instead
4063 // create_module.external_system_libs.4105 // of external_linker_inputs.
40644106
4065 if (create_module.resolved_system_libs.len != 0)4107 if (create_module.resolved_system_libs.len != 0)
4066 create_module.opts.any_dyn_libs = true;4108 create_module.opts.any_dyn_libs = true;
...@@ -6857,33 +6899,45 @@ const ClangSearchSanitizer = struct {...@@ -6857,33 +6899,45 @@ const ClangSearchSanitizer = struct {
6857 };6899 };
6858};6900};
68596901
6902const AccessLibPathResult = enum { ok, no_match };
6903
6860fn accessLibPath(6904fn accessLibPath(
6861 test_path: *std.ArrayList(u8),6905 gpa: Allocator,
6862 checked_paths: *std.ArrayList(u8),6906 arena: Allocator,
6907 /// Allocated via `gpa`.
6908 test_path: *std.ArrayListUnmanaged(u8),
6909 /// Allocated via `gpa`.
6910 checked_paths: *std.ArrayListUnmanaged(u8),
6911 /// Allocated via `arena`.
6912 external_linker_inputs: *std.ArrayListUnmanaged(LinkerInput),
6913 create_module: *CreateModule,
6914 /// Allocated via `gpa`.
6915 ld_script_bytes: *std.ArrayListUnmanaged(u8),
6863 lib_dir_path: []const u8,6916 lib_dir_path: []const u8,
6864 lib_name: []const u8,6917 lib_name: []const u8,
6865 target: std.Target,6918 target: std.Target,
6866 link_mode: std.builtin.LinkMode,6919 link_mode: std.builtin.LinkMode,
6867) !bool {6920 parent: SystemLib,
6921) Allocator.Error!AccessLibPathResult {
6868 const sep = fs.path.sep_str;6922 const sep = fs.path.sep_str;
68696923
6870 if (target.isDarwin() and link_mode == .dynamic) tbd: {6924 if (target.isDarwin() and link_mode == .dynamic) tbd: {
6871 // Prefer .tbd over .dylib.6925 // Prefer .tbd over .dylib.
6872 test_path.clearRetainingCapacity();6926 test_path.clearRetainingCapacity();
6873 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.tbd", .{ lib_dir_path, lib_name });6927 try test_path.writer(gpa).print("{s}" ++ sep ++ "lib{s}.tbd", .{ lib_dir_path, lib_name });
6874 try checked_paths.writer().print("\n {s}", .{test_path.items});6928 try checked_paths.writer(gpa).print("\n {s}", .{test_path.items});
6875 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {6929 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6876 error.FileNotFound => break :tbd,6930 error.FileNotFound => break :tbd,
6877 else => |e| fatal("unable to search for tbd library '{s}': {s}", .{6931 else => |e| fatal("unable to search for tbd library '{s}': {s}", .{
6878 test_path.items, @errorName(e),6932 test_path.items, @errorName(e),
6879 }),6933 }),
6880 };6934 };
6881 return true;6935 return finishAccessLibPath(arena, create_module, test_path, link_mode, parent, lib_name);
6882 }6936 }
68836937
6884 main_check: {6938 main_check: {
6885 test_path.clearRetainingCapacity();6939 test_path.clearRetainingCapacity();
6886 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{6940 try test_path.writer(gpa).print("{s}" ++ sep ++ "{s}{s}{s}", .{
6887 lib_dir_path,6941 lib_dir_path,
6888 target.libPrefix(),6942 target.libPrefix(),
6889 lib_name,6943 lib_name,
...@@ -6892,49 +6946,148 @@ fn accessLibPath(...@@ -6892,49 +6946,148 @@ fn accessLibPath(
6892 .dynamic => target.dynamicLibSuffix(),6946 .dynamic => target.dynamicLibSuffix(),
6893 },6947 },
6894 });6948 });
6895 try checked_paths.writer().print("\n {s}", .{test_path.items});6949 try checked_paths.writer(gpa).print("\n {s}", .{test_path.items});
6950
6951 // In the case of .so files, they might actually be "linker scripts"
6952 // that contain references to other libraries.
6953 if (target.ofmt == .elf and mem.endsWith(u8, test_path.items, ".so")) {
6954 var file = fs.cwd().openFile(test_path.items, .{}) catch |err| switch (err) {
6955 error.FileNotFound => break :main_check,
6956 else => |e| fatal("unable to search for {s} library '{s}': {s}", .{
6957 @tagName(link_mode), test_path.items, @errorName(e),
6958 }),
6959 };
6960 defer file.close();
6961 try ld_script_bytes.resize(gpa, @sizeOf(std.elf.Elf64_Ehdr));
6962 const n = file.readAll(ld_script_bytes.items) catch |err| fatal("failed to read {s}: {s}", .{
6963 test_path.items, @errorName(err),
6964 });
6965 elf_file: {
6966 if (n != ld_script_bytes.items.len) break :elf_file;
6967 if (!mem.eql(u8, ld_script_bytes.items[0..4], "\x7fELF")) break :elf_file;
6968 // Appears to be an ELF file.
6969 return finishAccessLibPath(arena, create_module, test_path, link_mode, parent, lib_name);
6970 }
6971 const stat = file.stat() catch |err|
6972 fatal("failed to stat {s}: {s}", .{ test_path.items, @errorName(err) });
6973 const size = std.math.cast(u32, stat.size) orelse
6974 fatal("{s}: linker script too big", .{test_path.items});
6975 try ld_script_bytes.resize(gpa, size);
6976 const buf = ld_script_bytes.items[n..];
6977 const n2 = file.readAll(buf) catch |err|
6978 fatal("failed to read {s}: {s}", .{ test_path.items, @errorName(err) });
6979 if (n2 != buf.len) fatal("failed to read {s}: unexpected end of file", .{test_path.items});
6980 var diags = link.Diags.init(gpa);
6981 defer diags.deinit();
6982 const ld_script_result = link.LdScript.parse(gpa, &diags, Path.initCwd(test_path.items), ld_script_bytes.items);
6983 if (diags.hasErrors()) {
6984 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6985 try wip_errors.init(gpa);
6986 defer wip_errors.deinit();
6987
6988 try diags.addMessagesToBundle(&wip_errors);
6989
6990 var error_bundle = try wip_errors.toOwnedBundle("");
6991 defer error_bundle.deinit(gpa);
6992
6993 const color: Color = .auto;
6994 error_bundle.renderToStdErr(color.renderOptions());
6995
6996 process.exit(1);
6997 }
6998
6999 var ld_script = ld_script_result catch |err|
7000 fatal("{s}: failed to parse linker script: {s}", .{ test_path.items, @errorName(err) });
7001 defer ld_script.deinit(gpa);
7002
7003 try external_linker_inputs.ensureUnusedCapacity(arena, ld_script.args.len);
7004 for (ld_script.args) |arg| {
7005 const syslib: SystemLib = .{
7006 .needed = arg.needed or parent.needed,
7007 .weak = parent.weak,
7008 .preferred_mode = parent.preferred_mode,
7009 .search_strategy = parent.search_strategy,
7010 };
7011 if (mem.startsWith(u8, arg.path, "-l")) {
7012 external_linker_inputs.appendAssumeCapacity(.{ .named = .{
7013 .name = try arena.dupe(u8, arg.path["-l".len..]),
7014 .info = syslib,
7015 } });
7016 } else {
7017 external_linker_inputs.appendAssumeCapacity(.{ .path = .{
7018 .path = Path.initCwd(try arena.dupe(u8, arg.path)),
7019 .info = syslib,
7020 } });
7021 }
7022 }
7023 return .ok;
7024 }
7025
6896 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {7026 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6897 error.FileNotFound => break :main_check,7027 error.FileNotFound => break :main_check,
6898 else => |e| fatal("unable to search for {s} library '{s}': {s}", .{7028 else => |e| fatal("unable to search for {s} library '{s}': {s}", .{
6899 @tagName(link_mode), test_path.items, @errorName(e),7029 @tagName(link_mode), test_path.items, @errorName(e),
6900 }),7030 }),
6901 };7031 };
6902 return true;7032 return finishAccessLibPath(arena, create_module, test_path, link_mode, parent, lib_name);
6903 }7033 }
69047034
6905 // In the case of Darwin, the main check will be .dylib, so here we7035 // In the case of Darwin, the main check will be .dylib, so here we
6906 // additionally check for .so files.7036 // additionally check for .so files.
6907 if (target.isDarwin() and link_mode == .dynamic) so: {7037 if (target.isDarwin() and link_mode == .dynamic) so: {
6908 test_path.clearRetainingCapacity();7038 test_path.clearRetainingCapacity();
6909 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, lib_name });7039 try test_path.writer(gpa).print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, lib_name });
6910 try checked_paths.writer().print("\n {s}", .{test_path.items});7040 try checked_paths.writer(gpa).print("\n {s}", .{test_path.items});
6911 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {7041 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6912 error.FileNotFound => break :so,7042 error.FileNotFound => break :so,
6913 else => |e| fatal("unable to search for so library '{s}': {s}", .{7043 else => |e| fatal("unable to search for so library '{s}': {s}", .{
6914 test_path.items, @errorName(e),7044 test_path.items, @errorName(e),
6915 }),7045 }),
6916 };7046 };
6917 return true;7047 return finishAccessLibPath(arena, create_module, test_path, link_mode, parent, lib_name);
6918 }7048 }
69197049
6920 // In the case of MinGW, the main check will be .lib but we also need to7050 // In the case of MinGW, the main check will be .lib but we also need to
6921 // look for `libfoo.a`.7051 // look for `libfoo.a`.
6922 if (target.isMinGW() and link_mode == .static) mingw: {7052 if (target.isMinGW() and link_mode == .static) mingw: {
6923 test_path.clearRetainingCapacity();7053 test_path.clearRetainingCapacity();
6924 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.a", .{7054 try test_path.writer(gpa).print("{s}" ++ sep ++ "lib{s}.a", .{
6925 lib_dir_path, lib_name,7055 lib_dir_path, lib_name,
6926 });7056 });
6927 try checked_paths.writer().print("\n {s}", .{test_path.items});7057 try checked_paths.writer(gpa).print("\n {s}", .{test_path.items});
6928 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {7058 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6929 error.FileNotFound => break :mingw,7059 error.FileNotFound => break :mingw,
6930 else => |e| fatal("unable to search for static library '{s}': {s}", .{7060 else => |e| fatal("unable to search for static library '{s}': {s}", .{
6931 test_path.items, @errorName(e),7061 test_path.items, @errorName(e),
6932 }),7062 }),
6933 };7063 };
6934 return true;7064 return finishAccessLibPath(arena, create_module, test_path, link_mode, parent, lib_name);
6935 }7065 }
69367066
6937 return false;7067 return .no_match;
7068}
7069
7070fn finishAccessLibPath(
7071 arena: Allocator,
7072 create_module: *CreateModule,
7073 test_path: *std.ArrayListUnmanaged(u8),
7074 link_mode: std.builtin.LinkMode,
7075 parent: SystemLib,
7076 lib_name: []const u8,
7077) Allocator.Error!AccessLibPathResult {
7078 const path = Path.initCwd(try arena.dupe(u8, test_path.items));
7079 switch (link_mode) {
7080 .static => try create_module.link_objects.append(arena, .{ .path = path }),
7081 .dynamic => try create_module.resolved_system_libs.append(arena, .{
7082 .name = lib_name,
7083 .lib = .{
7084 .needed = parent.needed,
7085 .weak = parent.weak,
7086 .path = path,
7087 },
7088 }),
7089 }
7090 return .ok;
6938}7091}
69397092
6940fn accessFrameworkPath(7093fn accessFrameworkPath(
...@@ -7634,3 +7787,18 @@ fn handleModArg(...@@ -7634,3 +7787,18 @@ fn handleModArg(
7634 c_source_files_owner_index.* = create_module.c_source_files.items.len;7787 c_source_files_owner_index.* = create_module.c_source_files.items.len;
7635 rc_source_files_owner_index.* = create_module.rc_source_files.items.len;7788 rc_source_files_owner_index.* = create_module.rc_source_files.items.len;
7636}7789}
7790
7791fn stripLibPrefixAndSuffix(path: []const u8, target: std.Target) struct { []const u8, std.builtin.LinkMode } {
7792 const prefix = target.libPrefix();
7793 const static_suffix = target.staticLibSuffix();
7794 const dynamic_suffix = target.dynamicLibSuffix();
7795 const basename = fs.path.basename(path);
7796 const unlibbed = if (mem.startsWith(u8, basename, prefix)) basename[prefix.len..] else basename;
7797 if (mem.endsWith(u8, unlibbed, static_suffix)) return .{
7798 unlibbed[0 .. unlibbed.len - static_suffix.len], .static,
7799 };
7800 if (mem.endsWith(u8, unlibbed, dynamic_suffix)) return .{
7801 unlibbed[0 .. unlibbed.len - dynamic_suffix.len], .dynamic,
7802 };
7803 fatal("unrecognized library path: {s}", .{path});
7804}
test/link/elf.zig+6-16
...@@ -2165,13 +2165,11 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {...@@ -2165,13 +2165,11 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
2165 exe.addLibraryPath(scripts.getDirectory());2165 exe.addLibraryPath(scripts.getDirectory());
2166 exe.linkLibC();2166 exe.linkLibC();
21672167
2168 expectLinkErrors(2168 // TODO: A future enhancement could make this error message also mention
2169 exe,2169 // the file that references the missing library.
2170 test_step,2170 expectLinkErrors(exe, test_step, .{
2171 .{2171 .stderr_contains = "error: unable to find dynamic system library 'foo' using strategy 'no_fallback'. searched paths:",
2172 .contains = "error: missing library dependency: GNU ld script '/?/liba.so' requires 'libfoo.so', but file not found",2172 });
2173 },
2174 );
21752173
2176 return test_step;2174 return test_step;
2177}2175}
...@@ -3907,16 +3905,8 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {...@@ -3907,16 +3905,8 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
3907 exe.linkLibrary(dylib);3905 exe.linkLibrary(dylib);
3908 exe.linkLibC();3906 exe.linkLibC();
39093907
3910 // TODO: improve the test harness to be able to selectively match lines in error output
3911 // while avoiding jankiness
3912 // expectLinkErrors(exe, test_step, .{ .exact = &.{
3913 // "error: invalid token in LD script: '\\x00\\x00\\x00\\x0c\\x00\\x00\\x00/usr/lib/dyld\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0d' (0:989)",
3914 // "note: while parsing /?/liba.dylib",
3915 // "error: unexpected error: parsing input file failed with error InvalidLdScript",
3916 // "note: while parsing /?/liba.dylib",
3917 // } });
3918 expectLinkErrors(exe, test_step, .{3908 expectLinkErrors(exe, test_step, .{
3919 .starts_with = "error: invalid token in LD script: '\\x00\\x00\\x00\\x0c\\x00\\x00\\x00/usr/lib/dyld\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0d' (",3909 .contains = "error: failed to parse shared object: BadMagic",
3920 });3910 });
39213911
3922 return test_step;3912 return test_step;