authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-16 12:14:19-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-23 16:27:38-07:00
loge567abb339e1edaf5a3c86fe632522a3b8005275
tree63ffbcb21c3dd7d8740e292467852341ac1c5fad
parent4706ec81d4f864bc08804d9600937848ff9e4290

rework linker inputs

* Compilation.objects changes to Compilation.link_inputs which stores objects, archives, windows resources, shared objects, and strings intended to be put directly into the dynamic section. Order is now preserved between all of these kinds of linker inputs. If it is determined the order does not matter for a particular kind of linker input, that item should be moved to a different array. * rename system_libs to windows_libs * untangle library lookup from CLI types * when doing library lookup, instead of using access syscalls, go ahead and open the files and keep the handles around for passing to the cache system and the linker. * during library lookup and cache file hashing, use positioned reads to avoid affecting the file seek position. * library directories are opened in the CLI and converted to Directory objects, warnings emitted for those that cannot be opened.

12 files changed, 1516 insertions(+), 1090 deletions(-)

lib/std/Build/Cache.zig+41-16
...@@ -142,6 +142,9 @@ pub const hasher_init: Hasher = Hasher.init(&[_]u8{...@@ -142,6 +142,9 @@ pub const hasher_init: Hasher = Hasher.init(&[_]u8{
142pub const File = struct {142pub const File = struct {
143 prefixed_path: PrefixedPath,143 prefixed_path: PrefixedPath,
144 max_file_size: ?usize,144 max_file_size: ?usize,
145 /// Populated if the user calls `addOpenedFile`.
146 /// The handle is not owned here.
147 handle: ?fs.File,
145 stat: Stat,148 stat: Stat,
146 bin_digest: BinDigest,149 bin_digest: BinDigest,
147 contents: ?[]const u8,150 contents: ?[]const u8,
...@@ -173,6 +176,11 @@ pub const File = struct {...@@ -173,6 +176,11 @@ pub const File = struct {
173 const new = new_max_size orelse return;176 const new = new_max_size orelse return;
174 file.max_file_size = if (file.max_file_size) |old| @max(old, new) else new;177 file.max_file_size = if (file.max_file_size) |old| @max(old, new) else new;
175 }178 }
179
180 pub fn updateHandle(file: *File, new_handle: ?fs.File) void {
181 const handle = new_handle orelse return;
182 file.handle = handle;
183 }
176};184};
177185
178pub const HashHelper = struct {186pub const HashHelper = struct {
...@@ -363,15 +371,20 @@ pub const Manifest = struct {...@@ -363,15 +371,20 @@ pub const Manifest = struct {
363 /// var file_contents = cache_hash.files.keys()[file_index].contents.?;371 /// var file_contents = cache_hash.files.keys()[file_index].contents.?;
364 /// ```372 /// ```
365 pub fn addFilePath(m: *Manifest, file_path: Path, max_file_size: ?usize) !usize {373 pub fn addFilePath(m: *Manifest, file_path: Path, max_file_size: ?usize) !usize {
374 return addOpenedFile(m, file_path, null, max_file_size);
375 }
376
377 /// Same as `addFilePath` except the file has already been opened.
378 pub fn addOpenedFile(m: *Manifest, path: Path, handle: ?fs.File, max_file_size: ?usize) !usize {
366 const gpa = m.cache.gpa;379 const gpa = m.cache.gpa;
367 try m.files.ensureUnusedCapacity(gpa, 1);380 try m.files.ensureUnusedCapacity(gpa, 1);
368 const resolved_path = try fs.path.resolve(gpa, &.{381 const resolved_path = try fs.path.resolve(gpa, &.{
369 file_path.root_dir.path orelse ".",382 path.root_dir.path orelse ".",
370 file_path.subPathOrDot(),383 path.subPathOrDot(),
371 });384 });
372 errdefer gpa.free(resolved_path);385 errdefer gpa.free(resolved_path);
373 const prefixed_path = try m.cache.findPrefixResolved(resolved_path);386 const prefixed_path = try m.cache.findPrefixResolved(resolved_path);
374 return addFileInner(m, prefixed_path, max_file_size);387 return addFileInner(m, prefixed_path, handle, max_file_size);
375 }388 }
376389
377 /// Deprecated; use `addFilePath`.390 /// Deprecated; use `addFilePath`.
...@@ -383,13 +396,14 @@ pub const Manifest = struct {...@@ -383,13 +396,14 @@ pub const Manifest = struct {
383 const prefixed_path = try self.cache.findPrefix(file_path);396 const prefixed_path = try self.cache.findPrefix(file_path);
384 errdefer gpa.free(prefixed_path.sub_path);397 errdefer gpa.free(prefixed_path.sub_path);
385398
386 return addFileInner(self, prefixed_path, max_file_size);399 return addFileInner(self, prefixed_path, null, max_file_size);
387 }400 }
388401
389 fn addFileInner(self: *Manifest, prefixed_path: PrefixedPath, max_file_size: ?usize) !usize {402 fn addFileInner(self: *Manifest, prefixed_path: PrefixedPath, handle: ?fs.File, max_file_size: ?usize) usize {
390 const gop = self.files.getOrPutAssumeCapacityAdapted(prefixed_path, FilesAdapter{});403 const gop = self.files.getOrPutAssumeCapacityAdapted(prefixed_path, FilesAdapter{});
391 if (gop.found_existing) {404 if (gop.found_existing) {
392 gop.key_ptr.updateMaxSize(max_file_size);405 gop.key_ptr.updateMaxSize(max_file_size);
406 gop.key_ptr.updateHandle(handle);
393 return gop.index;407 return gop.index;
394 }408 }
395 gop.key_ptr.* = .{409 gop.key_ptr.* = .{
...@@ -398,6 +412,7 @@ pub const Manifest = struct {...@@ -398,6 +412,7 @@ pub const Manifest = struct {
398 .max_file_size = max_file_size,412 .max_file_size = max_file_size,
399 .stat = undefined,413 .stat = undefined,
400 .bin_digest = undefined,414 .bin_digest = undefined,
415 .handle = handle,
401 };416 };
402417
403 self.hash.add(prefixed_path.prefix);418 self.hash.add(prefixed_path.prefix);
...@@ -565,6 +580,7 @@ pub const Manifest = struct {...@@ -565,6 +580,7 @@ pub const Manifest = struct {
565 },580 },
566 .contents = null,581 .contents = null,
567 .max_file_size = null,582 .max_file_size = null,
583 .handle = null,
568 .stat = .{584 .stat = .{
569 .size = stat_size,585 .size = stat_size,
570 .inode = stat_inode,586 .inode = stat_inode,
...@@ -708,12 +724,19 @@ pub const Manifest = struct {...@@ -708,12 +724,19 @@ pub const Manifest = struct {
708 }724 }
709725
710 fn populateFileHash(self: *Manifest, ch_file: *File) !void {726 fn populateFileHash(self: *Manifest, ch_file: *File) !void {
711 const pp = ch_file.prefixed_path;727 if (ch_file.handle) |handle| {
712 const dir = self.cache.prefixes()[pp.prefix].handle;728 return populateFileHashHandle(self, ch_file, handle);
713 const file = try dir.openFile(pp.sub_path, .{});729 } else {
714 defer file.close();730 const pp = ch_file.prefixed_path;
731 const dir = self.cache.prefixes()[pp.prefix].handle;
732 const handle = try dir.openFile(pp.sub_path, .{});
733 defer handle.close();
734 return populateFileHashHandle(self, ch_file, handle);
735 }
736 }
715737
716 const actual_stat = try file.stat();738 fn populateFileHashHandle(self: *Manifest, ch_file: *File, handle: fs.File) !void {
739 const actual_stat = try handle.stat();
717 ch_file.stat = .{740 ch_file.stat = .{
718 .size = actual_stat.size,741 .size = actual_stat.size,
719 .mtime = actual_stat.mtime,742 .mtime = actual_stat.mtime,
...@@ -739,8 +762,7 @@ pub const Manifest = struct {...@@ -739,8 +762,7 @@ pub const Manifest = struct {
739 var hasher = hasher_init;762 var hasher = hasher_init;
740 var off: usize = 0;763 var off: usize = 0;
741 while (true) {764 while (true) {
742 // give me everything you've got, captain765 const bytes_read = try handle.pread(contents[off..], off);
743 const bytes_read = try file.read(contents[off..]);
744 if (bytes_read == 0) break;766 if (bytes_read == 0) break;
745 hasher.update(contents[off..][0..bytes_read]);767 hasher.update(contents[off..][0..bytes_read]);
746 off += bytes_read;768 off += bytes_read;
...@@ -749,7 +771,7 @@ pub const Manifest = struct {...@@ -749,7 +771,7 @@ pub const Manifest = struct {
749771
750 ch_file.contents = contents;772 ch_file.contents = contents;
751 } else {773 } else {
752 try hashFile(file, &ch_file.bin_digest);774 try hashFile(handle, &ch_file.bin_digest);
753 }775 }
754776
755 self.hash.hasher.update(&ch_file.bin_digest);777 self.hash.hasher.update(&ch_file.bin_digest);
...@@ -813,6 +835,7 @@ pub const Manifest = struct {...@@ -813,6 +835,7 @@ pub const Manifest = struct {
813 gop.key_ptr.* = .{835 gop.key_ptr.* = .{
814 .prefixed_path = prefixed_path,836 .prefixed_path = prefixed_path,
815 .max_file_size = null,837 .max_file_size = null,
838 .handle = null,
816 .stat = undefined,839 .stat = undefined,
817 .bin_digest = undefined,840 .bin_digest = undefined,
818 .contents = null,841 .contents = null,
...@@ -851,6 +874,7 @@ pub const Manifest = struct {...@@ -851,6 +874,7 @@ pub const Manifest = struct {
851 new_file.* = .{874 new_file.* = .{
852 .prefixed_path = prefixed_path,875 .prefixed_path = prefixed_path,
853 .max_file_size = null,876 .max_file_size = null,
877 .handle = null,
854 .stat = stat,878 .stat = stat,
855 .bin_digest = undefined,879 .bin_digest = undefined,
856 .contents = null,880 .contents = null,
...@@ -1067,6 +1091,7 @@ pub const Manifest = struct {...@@ -1067,6 +1091,7 @@ pub const Manifest = struct {
1067 gop.key_ptr.* = .{1091 gop.key_ptr.* = .{
1068 .prefixed_path = prefixed_path,1092 .prefixed_path = prefixed_path,
1069 .max_file_size = file.max_file_size,1093 .max_file_size = file.max_file_size,
1094 .handle = file.handle,
1070 .stat = file.stat,1095 .stat = file.stat,
1071 .bin_digest = file.bin_digest,1096 .bin_digest = file.bin_digest,
1072 .contents = null,1097 .contents = null,
...@@ -1103,14 +1128,14 @@ pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void...@@ -1103,14 +1128,14 @@ pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void
11031128
1104fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {1129fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {
1105 var buf: [1024]u8 = undefined;1130 var buf: [1024]u8 = undefined;
1106
1107 var hasher = hasher_init;1131 var hasher = hasher_init;
1132 var off: u64 = 0;
1108 while (true) {1133 while (true) {
1109 const bytes_read = try file.read(&buf);1134 const bytes_read = try file.pread(&buf, off);
1110 if (bytes_read == 0) break;1135 if (bytes_read == 0) break;
1111 hasher.update(buf[0..bytes_read]);1136 hasher.update(buf[0..bytes_read]);
1137 off += bytes_read;
1112 }1138 }
1113
1114 hasher.final(bin_digest);1139 hasher.final(bin_digest);
1115}1140}
11161141
src/Compilation.zig+32-72
...@@ -76,12 +76,13 @@ implib_emit: ?Path,...@@ -76,12 +76,13 @@ implib_emit: ?Path,
76docs_emit: ?Path,76docs_emit: ?Path,
77root_name: [:0]const u8,77root_name: [:0]const u8,
78include_compiler_rt: bool,78include_compiler_rt: bool,
79objects: []Compilation.LinkObject,79/// Resolved into known paths, any GNU ld scripts already resolved.
80link_inputs: []const link.Input,
80/// Needed only for passing -F args to clang.81/// Needed only for passing -F args to clang.
81framework_dirs: []const []const u8,82framework_dirs: []const []const u8,
82/// These are *always* dynamically linked. Static libraries will be83/// These are only for DLLs dependencies fulfilled by the `.def` files shipped
83/// provided as positional arguments.84/// with Zig. Static libraries are provided as `link.Input` values.
84system_libs: std.StringArrayHashMapUnmanaged(SystemLib),85windows_libs: std.StringArrayHashMapUnmanaged(void),
85version: ?std.SemanticVersion,86version: ?std.SemanticVersion,
86libc_installation: ?*const LibCInstallation,87libc_installation: ?*const LibCInstallation,
87skip_linker_dependencies: bool,88skip_linker_dependencies: bool,
...@@ -384,7 +385,7 @@ const Job = union(enum) {...@@ -384,7 +385,7 @@ const Job = union(enum) {
384 /// one of WASI libc static objects385 /// one of WASI libc static objects
385 wasi_libc_crt_file: wasi_libc.CrtFile,386 wasi_libc_crt_file: wasi_libc.CrtFile,
386387
387 /// The value is the index into `system_libs`.388 /// The value is the index into `windows_libs`.
388 windows_import_lib: usize,389 windows_import_lib: usize,
389390
390 const Tag = @typeInfo(Job).@"union".tag_type.?;391 const Tag = @typeInfo(Job).@"union".tag_type.?;
...@@ -999,25 +1000,6 @@ const CacheUse = union(CacheMode) {...@@ -999,25 +1000,6 @@ const CacheUse = union(CacheMode) {
999 }1000 }
1000};1001};
10011002
1002pub const LinkObject = struct {
1003 path: Path,
1004 must_link: bool = false,
1005 needed: bool = false,
1006 weak: bool = false,
1007 /// When the library is passed via a positional argument, it will be
1008 /// added as a full path. If it's `-l<lib>`, then just the basename.
1009 ///
1010 /// Consistent with `withLOption` variable name in lld ELF driver.
1011 loption: bool = false,
1012
1013 pub fn isObject(lo: LinkObject) bool {
1014 return switch (classifyFileExt(lo.path.sub_path)) {
1015 .object => true,
1016 else => false,
1017 };
1018 }
1019};
1020
1021pub const CreateOptions = struct {1003pub const CreateOptions = struct {
1022 zig_lib_directory: Directory,1004 zig_lib_directory: Directory,
1023 local_cache_directory: Directory,1005 local_cache_directory: Directory,
...@@ -1065,18 +1047,17 @@ pub const CreateOptions = struct {...@@ -1065,18 +1047,17 @@ pub const CreateOptions = struct {
1065 /// This field is intended to be removed.1047 /// This field is intended to be removed.
1066 /// The ELF implementation no longer uses this data, however the MachO and COFF1048 /// The ELF implementation no longer uses this data, however the MachO and COFF
1067 /// implementations still do.1049 /// implementations still do.
1068 lib_dirs: []const []const u8 = &[0][]const u8{},1050 lib_directories: []const Directory = &.{},
1069 rpath_list: []const []const u8 = &[0][]const u8{},1051 rpath_list: []const []const u8 = &[0][]const u8{},
1070 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty,1052 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty,
1071 c_source_files: []const CSourceFile = &.{},1053 c_source_files: []const CSourceFile = &.{},
1072 rc_source_files: []const RcSourceFile = &.{},1054 rc_source_files: []const RcSourceFile = &.{},
1073 manifest_file: ?[]const u8 = null,1055 manifest_file: ?[]const u8 = null,
1074 rc_includes: RcIncludes = .any,1056 rc_includes: RcIncludes = .any,
1075 link_objects: []LinkObject = &[0]LinkObject{},1057 link_inputs: []const link.Input = &.{},
1076 framework_dirs: []const []const u8 = &[0][]const u8{},1058 framework_dirs: []const []const u8 = &[0][]const u8{},
1077 frameworks: []const Framework = &.{},1059 frameworks: []const Framework = &.{},
1078 system_lib_names: []const []const u8 = &.{},1060 windows_lib_names: []const []const u8 = &.{},
1079 system_lib_infos: []const SystemLib = &.{},
1080 /// These correspond to the WASI libc emulated subcomponents including:1061 /// These correspond to the WASI libc emulated subcomponents including:
1081 /// * process clocks1062 /// * process clocks
1082 /// * getpid1063 /// * getpid
...@@ -1459,12 +1440,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1459,12 +1440,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1459 };1440 };
1460 errdefer if (opt_zcu) |zcu| zcu.deinit();1441 errdefer if (opt_zcu) |zcu| zcu.deinit();
14611442
1462 var system_libs = try std.StringArrayHashMapUnmanaged(SystemLib).init(1443 var windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{});
1463 gpa,1444 errdefer windows_libs.deinit(gpa);
1464 options.system_lib_names,
1465 options.system_lib_infos,
1466 );
1467 errdefer system_libs.deinit(gpa);
14681445
1469 comp.* = .{1446 comp.* = .{
1470 .gpa = gpa,1447 .gpa = gpa,
...@@ -1526,11 +1503,11 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1526,11 +1503,11 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1526 .libcxx_abi_version = options.libcxx_abi_version,1503 .libcxx_abi_version = options.libcxx_abi_version,
1527 .root_name = root_name,1504 .root_name = root_name,
1528 .sysroot = sysroot,1505 .sysroot = sysroot,
1529 .system_libs = system_libs,1506 .windows_libs = windows_libs,
1530 .version = options.version,1507 .version = options.version,
1531 .libc_installation = libc_dirs.libc_installation,1508 .libc_installation = libc_dirs.libc_installation,
1532 .include_compiler_rt = include_compiler_rt,1509 .include_compiler_rt = include_compiler_rt,
1533 .objects = options.link_objects,1510 .link_inputs = options.link_inputs,
1534 .framework_dirs = options.framework_dirs,1511 .framework_dirs = options.framework_dirs,
1535 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,1512 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
1536 .skip_linker_dependencies = options.skip_linker_dependencies,1513 .skip_linker_dependencies = options.skip_linker_dependencies,
...@@ -1568,7 +1545,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1568,7 +1545,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1568 .z_max_page_size = options.linker_z_max_page_size,1545 .z_max_page_size = options.linker_z_max_page_size,
1569 .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,1546 .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,
1570 .frameworks = options.frameworks,1547 .frameworks = options.frameworks,
1571 .lib_dirs = options.lib_dirs,1548 .lib_directories = options.lib_directories,
1572 .framework_dirs = options.framework_dirs,1549 .framework_dirs = options.framework_dirs,
1573 .rpath_list = options.rpath_list,1550 .rpath_list = options.rpath_list,
1574 .symbol_wrap_set = options.symbol_wrap_set,1551 .symbol_wrap_set = options.symbol_wrap_set,
...@@ -1851,17 +1828,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1851,17 +1828,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1851 });1828 });
18521829
1853 // When linking mingw-w64 there are some import libs we always need.1830 // When linking mingw-w64 there are some import libs we always need.
1854 for (mingw.always_link_libs) |name| {1831 try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);
1855 try comp.system_libs.put(comp.gpa, name, .{1832 for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {});
1856 .needed = false,
1857 .weak = false,
1858 .path = null,
1859 });
1860 }
1861 }1833 }
1862 // Generate Windows import libs.1834 // Generate Windows import libs.
1863 if (target.os.tag == .windows) {1835 if (target.os.tag == .windows) {
1864 const count = comp.system_libs.count();1836 const count = comp.windows_libs.count();
1865 for (0..count) |i| {1837 for (0..count) |i| {
1866 try comp.queueJob(.{ .windows_import_lib = i });1838 try comp.queueJob(.{ .windows_import_lib = i });
1867 }1839 }
...@@ -1930,7 +1902,7 @@ pub fn destroy(comp: *Compilation) void {...@@ -1930,7 +1902,7 @@ pub fn destroy(comp: *Compilation) void {
1930 comp.embed_file_work_queue.deinit();1902 comp.embed_file_work_queue.deinit();
19311903
1932 const gpa = comp.gpa;1904 const gpa = comp.gpa;
1933 comp.system_libs.deinit(gpa);1905 comp.windows_libs.deinit(gpa);
19341906
1935 {1907 {
1936 var it = comp.crt_files.iterator();1908 var it = comp.crt_files.iterator();
...@@ -2563,13 +2535,7 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -2563,13 +2535,7 @@ fn addNonIncrementalStuffToCacheManifest(
2563 cache_helpers.addModule(&man.hash, comp.root_mod);2535 cache_helpers.addModule(&man.hash, comp.root_mod);
2564 }2536 }
25652537
2566 for (comp.objects) |obj| {2538 try link.hashInputs(man, comp.link_inputs);
2567 _ = try man.addFilePath(obj.path, null);
2568 man.hash.add(obj.must_link);
2569 man.hash.add(obj.needed);
2570 man.hash.add(obj.weak);
2571 man.hash.add(obj.loption);
2572 }
25732539
2574 for (comp.c_object_table.keys()) |key| {2540 for (comp.c_object_table.keys()) |key| {
2575 _ = try man.addFile(key.src.src_path, null);2541 _ = try man.addFile(key.src.src_path, null);
...@@ -2606,7 +2572,7 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -2606,7 +2572,7 @@ fn addNonIncrementalStuffToCacheManifest(
2606 man.hash.add(comp.rc_includes);2572 man.hash.add(comp.rc_includes);
2607 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());2573 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
2608 man.hash.addListOfBytes(comp.framework_dirs);2574 man.hash.addListOfBytes(comp.framework_dirs);
2609 try link.hashAddSystemLibs(man, comp.system_libs);2575 man.hash.addListOfBytes(comp.windows_libs.keys());
26102576
2611 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);2577 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
2612 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);2578 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
...@@ -2625,12 +2591,16 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -2625,12 +2591,16 @@ fn addNonIncrementalStuffToCacheManifest(
2625 man.hash.addOptional(opts.image_base);2591 man.hash.addOptional(opts.image_base);
2626 man.hash.addOptional(opts.gc_sections);2592 man.hash.addOptional(opts.gc_sections);
2627 man.hash.add(opts.emit_relocs);2593 man.hash.add(opts.emit_relocs);
2628 man.hash.addListOfBytes(opts.lib_dirs);2594 const target = comp.root_mod.resolved_target.result;
2595 if (target.ofmt == .macho or target.ofmt == .coff) {
2596 // TODO remove this, libraries need to be resolved by the frontend. this is already
2597 // done by ELF.
2598 for (opts.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path);
2599 }
2629 man.hash.addListOfBytes(opts.rpath_list);2600 man.hash.addListOfBytes(opts.rpath_list);
2630 man.hash.addListOfBytes(opts.symbol_wrap_set.keys());2601 man.hash.addListOfBytes(opts.symbol_wrap_set.keys());
2631 if (comp.config.link_libc) {2602 if (comp.config.link_libc) {
2632 man.hash.add(comp.libc_installation != null);2603 man.hash.add(comp.libc_installation != null);
2633 const target = comp.root_mod.resolved_target.result;
2634 if (comp.libc_installation) |libc_installation| {2604 if (comp.libc_installation) |libc_installation| {
2635 man.hash.addOptionalBytes(libc_installation.crt_dir);2605 man.hash.addOptionalBytes(libc_installation.crt_dir);
2636 if (target.abi == .msvc or target.abi == .itanium) {2606 if (target.abi == .msvc or target.abi == .itanium) {
...@@ -3798,7 +3768,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre...@@ -3798,7 +3768,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
3798 const named_frame = tracy.namedFrame("windows_import_lib");3768 const named_frame = tracy.namedFrame("windows_import_lib");
3799 defer named_frame.end();3769 defer named_frame.end();
38003770
3801 const link_lib = comp.system_libs.keys()[index];3771 const link_lib = comp.windows_libs.keys()[index];
3802 mingw.buildImportLib(comp, link_lib) catch |err| {3772 mingw.buildImportLib(comp, link_lib) catch |err| {
3803 // TODO Surface more error details.3773 // TODO Surface more error details.
3804 comp.lockAndSetMiscFailure(3774 comp.lockAndSetMiscFailure(
...@@ -4711,7 +4681,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr...@@ -4711,7 +4681,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
4711 // file and building an object we need to link them together, but with just one it should go4681 // file and building an object we need to link them together, but with just one it should go
4712 // directly to the output file.4682 // directly to the output file.
4713 const direct_o = comp.c_source_files.len == 1 and comp.zcu == null and4683 const direct_o = comp.c_source_files.len == 1 and comp.zcu == null and
4714 comp.config.output_mode == .Obj and comp.objects.len == 0;4684 comp.config.output_mode == .Obj and !link.anyObjectInputs(comp.link_inputs);
4715 const o_basename_noext = if (direct_o)4685 const o_basename_noext = if (direct_o)
4716 comp.root_name4686 comp.root_name
4717 else4687 else
...@@ -6516,24 +6486,14 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {...@@ -6516,24 +6486,14 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
6516 // then when we create a sub-Compilation for zig libc, it also tries to6486 // then when we create a sub-Compilation for zig libc, it also tries to
6517 // build kernel32.lib.6487 // build kernel32.lib.
6518 if (comp.skip_linker_dependencies) return;6488 if (comp.skip_linker_dependencies) return;
6489 const target = comp.root_mod.resolved_target.result;
6490 if (target.os.tag != .windows or target.ofmt == .c) return;
65196491
6520 // This happens when an `extern "foo"` function is referenced.6492 // This happens when an `extern "foo"` function is referenced.
6521 // If we haven't seen this library yet and we're targeting Windows, we need6493 // If we haven't seen this library yet and we're targeting Windows, we need
6522 // to queue up a work item to produce the DLL import library for this.6494 // to queue up a work item to produce the DLL import library for this.
6523 const gop = try comp.system_libs.getOrPut(comp.gpa, lib_name);6495 const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name);
6524 if (!gop.found_existing) {6496 if (!gop.found_existing) try comp.queueJob(.{ .windows_import_lib = comp.windows_libs.count() - 1 });
6525 gop.value_ptr.* = .{
6526 .needed = true,
6527 .weak = false,
6528 .path = null,
6529 };
6530 const target = comp.root_mod.resolved_target.result;
6531 if (target.os.tag == .windows and target.ofmt != .c) {
6532 try comp.queueJob(.{
6533 .windows_import_lib = comp.system_libs.count() - 1,
6534 });
6535 }
6536 }
6537}6497}
65386498
6539/// This decides the optimization mode for all zig-provided libraries, including6499/// This decides the optimization mode for all zig-provided libraries, including
src/Sema.zig+1-1
...@@ -9595,7 +9595,7 @@ fn resolveGenericBody(...@@ -9595,7 +9595,7 @@ fn resolveGenericBody(
9595}9595}
95969596
9597/// Given a library name, examines if the library name should end up in9597/// Given a library name, examines if the library name should end up in
9598/// `link.File.Options.system_libs` table (for example, libc is always9598/// `link.File.Options.windows_libs` table (for example, libc is always
9599/// specified via dedicated flag `link_libc` instead),9599/// specified via dedicated flag `link_libc` instead),
9600/// and puts it there if it doesn't exist.9600/// and puts it there if it doesn't exist.
9601/// It also dupes the library name which can then be saved as part of the9601/// It also dupes the library name which can then be saved as part of the
src/link.zig+688-46
...@@ -12,6 +12,7 @@ const Air = @import("Air.zig");...@@ -12,6 +12,7 @@ const Air = @import("Air.zig");
12const Allocator = std.mem.Allocator;12const Allocator = std.mem.Allocator;
13const Cache = std.Build.Cache;13const Cache = std.Build.Cache;
14const Path = std.Build.Cache.Path;14const Path = std.Build.Cache.Path;
15const Directory = std.Build.Cache.Directory;
15const Compilation = @import("Compilation.zig");16const Compilation = @import("Compilation.zig");
16const LibCInstallation = std.zig.LibCInstallation;17const LibCInstallation = std.zig.LibCInstallation;
17const Liveness = @import("Liveness.zig");18const Liveness = @import("Liveness.zig");
...@@ -26,19 +27,6 @@ const dev = @import("dev.zig");...@@ -26,19 +27,6 @@ const dev = @import("dev.zig");
2627
27pub const LdScript = @import("link/LdScript.zig");28pub const LdScript = @import("link/LdScript.zig");
2829
29/// When adding a new field, remember to update `hashAddSystemLibs`.
30/// These are *always* dynamically linked. Static libraries will be
31/// provided as positional arguments.
32pub const SystemLib = struct {
33 needed: bool,
34 weak: bool,
35 /// This can be null in two cases right now:
36 /// 1. Windows DLLs that zig ships such as advapi32.
37 /// 2. extern "foo" fn declarations where we find out about libraries too late
38 /// TODO: make this non-optional and resolve those two cases somehow.
39 path: ?Path,
40};
41
42pub const Diags = struct {30pub const Diags = struct {
43 /// Stored here so that function definitions can distinguish between31 /// Stored here so that function definitions can distinguish between
44 /// needing an allocator for things besides error reporting.32 /// needing an allocator for things besides error reporting.
...@@ -355,19 +343,6 @@ pub const Diags = struct {...@@ -355,19 +343,6 @@ pub const Diags = struct {
355 }343 }
356};344};
357345
358pub fn hashAddSystemLibs(
359 man: *Cache.Manifest,
360 hm: std.StringArrayHashMapUnmanaged(SystemLib),
361) !void {
362 const keys = hm.keys();
363 man.hash.addListOfBytes(keys);
364 for (hm.values()) |value| {
365 man.hash.add(value.needed);
366 man.hash.add(value.weak);
367 if (value.path) |p| _ = try man.addFilePath(p, null);
368 }
369}
370
371pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version;346pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version;
372347
373pub const File = struct {348pub const File = struct {
...@@ -455,7 +430,7 @@ pub const File = struct {...@@ -455,7 +430,7 @@ pub const File = struct {
455 compatibility_version: ?std.SemanticVersion,430 compatibility_version: ?std.SemanticVersion,
456431
457 // TODO: remove this. libraries are resolved by the frontend.432 // TODO: remove this. libraries are resolved by the frontend.
458 lib_dirs: []const []const u8,433 lib_directories: []const Directory,
459 framework_dirs: []const []const u8,434 framework_dirs: []const []const u8,
460 rpath_list: []const []const u8,435 rpath_list: []const []const u8,
461436
...@@ -1027,7 +1002,6 @@ pub const File = struct {...@@ -1027,7 +1002,6 @@ pub const File = struct {
1027 defer tracy.end();1002 defer tracy.end();
10281003
1029 const comp = base.comp;1004 const comp = base.comp;
1030 const gpa = comp.gpa;
10311005
1032 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.1006 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
1033 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});1007 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
...@@ -1059,7 +1033,7 @@ pub const File = struct {...@@ -1059,7 +1033,7 @@ pub const File = struct {
1059 var man: Cache.Manifest = undefined;1033 var man: Cache.Manifest = undefined;
1060 defer if (!base.disable_lld_caching) man.deinit();1034 defer if (!base.disable_lld_caching) man.deinit();
10611035
1062 const objects = comp.objects;1036 const link_inputs = comp.link_inputs;
10631037
1064 var digest: [Cache.hex_digest_len]u8 = undefined;1038 var digest: [Cache.hex_digest_len]u8 = undefined;
10651039
...@@ -1069,11 +1043,8 @@ pub const File = struct {...@@ -1069,11 +1043,8 @@ pub const File = struct {
1069 // We are about to obtain this lock, so here we give other processes a chance first.1043 // We are about to obtain this lock, so here we give other processes a chance first.
1070 base.releaseLock();1044 base.releaseLock();
10711045
1072 for (objects) |obj| {1046 try hashInputs(&man, link_inputs);
1073 _ = try man.addFilePath(obj.path, null);1047
1074 man.hash.add(obj.must_link);
1075 man.hash.add(obj.loption);
1076 }
1077 for (comp.c_object_table.keys()) |key| {1048 for (comp.c_object_table.keys()) |key| {
1078 _ = try man.addFilePath(key.status.success.object_path, null);1049 _ = try man.addFilePath(key.status.success.object_path, null);
1079 }1050 }
...@@ -1109,26 +1080,24 @@ pub const File = struct {...@@ -1109,26 +1080,24 @@ pub const File = struct {
1109 };1080 };
1110 }1081 }
11111082
1112 const win32_resource_table_len = comp.win32_resource_table.count();1083 var object_files: std.ArrayListUnmanaged([*:0]const u8) = .empty;
1113 const num_object_files = objects.len + comp.c_object_table.count() + win32_resource_table_len + 2;
1114 var object_files = try std.ArrayList([*:0]const u8).initCapacity(gpa, num_object_files);
1115 defer object_files.deinit();
11161084
1117 for (objects) |obj| {1085 try object_files.ensureUnusedCapacity(arena, link_inputs.len);
1118 object_files.appendAssumeCapacity(try obj.path.toStringZ(arena));1086 for (link_inputs) |input| {
1087 object_files.appendAssumeCapacity(try input.path().?.toStringZ(arena));
1119 }1088 }
1089
1090 try object_files.ensureUnusedCapacity(arena, comp.c_object_table.count() +
1091 comp.win32_resource_table.count() + 2);
1092
1120 for (comp.c_object_table.keys()) |key| {1093 for (comp.c_object_table.keys()) |key| {
1121 object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena));1094 object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena));
1122 }1095 }
1123 for (comp.win32_resource_table.keys()) |key| {1096 for (comp.win32_resource_table.keys()) |key| {
1124 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));1097 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));
1125 }1098 }
1126 if (zcu_obj_path) |p| {1099 if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
1127 object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));1100 if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
1128 }
1129 if (compiler_rt_path) |p| {
1130 object_files.appendAssumeCapacity(try p.toStringZ(arena));
1131 }
11321101
1133 if (comp.verbose_link) {1102 if (comp.verbose_link) {
1134 std.debug.print("ar rcs {s}", .{full_out_path_z});1103 std.debug.print("ar rcs {s}", .{full_out_path_z});
...@@ -1404,3 +1373,676 @@ pub fn spawnLld(...@@ -1404,3 +1373,676 @@ pub fn spawnLld(
14041373
1405 if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});1374 if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1406}1375}
1376
1377/// Provided by the CLI, processed into `LinkInput` instances at the start of
1378/// the compilation pipeline.
1379pub const UnresolvedInput = union(enum) {
1380 /// A library name that could potentially be dynamic or static depending on
1381 /// query parameters, resolved according to library directories.
1382 /// This could potentially resolve to a GNU ld script, resulting in more
1383 /// library dependencies.
1384 name_query: NameQuery,
1385 /// When a file path is provided, query info is still needed because the
1386 /// path may point to a .so file which may actually be a GNU ld script that
1387 /// references library names which need to be resolved.
1388 path_query: PathQuery,
1389 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path?
1390 /// Who knows! Fuck around and find out.
1391 ambiguous_name: NameQuery,
1392 /// Put exactly this string in the dynamic section, no rpath.
1393 dso_exact: Input.DsoExact,
1394
1395 ///// Relocatable.
1396 //object: Input.Object,
1397 ///// Static library.
1398 //archive: Input.Object,
1399 ///// Windows resource file.
1400 //winres: Path,
1401
1402 pub const NameQuery = struct {
1403 name: []const u8,
1404 query: Query,
1405 };
1406
1407 pub const PathQuery = struct {
1408 path: Path,
1409 query: Query,
1410 };
1411
1412 pub const Query = struct {
1413 needed: bool = false,
1414 weak: bool = false,
1415 reexport: bool = false,
1416 must_link: bool = false,
1417 hidden: bool = false,
1418 allow_so_scripts: bool = false,
1419 preferred_mode: std.builtin.LinkMode,
1420 search_strategy: SearchStrategy,
1421
1422 fn fallbackMode(q: Query) std.builtin.LinkMode {
1423 assert(q.search_strategy != .no_fallback);
1424 return switch (q.preferred_mode) {
1425 .dynamic => .static,
1426 .static => .dynamic,
1427 };
1428 }
1429 };
1430
1431 pub const SearchStrategy = enum {
1432 paths_first,
1433 mode_first,
1434 no_fallback,
1435 };
1436};
1437
1438pub const Input = union(enum) {
1439 object: Object,
1440 archive: Object,
1441 res: Res,
1442 /// May not be a GNU ld script. Those are resolved when converting from
1443 /// `UnresolvedInput` to `Input` values.
1444 dso: Dso,
1445 dso_exact: DsoExact,
1446
1447 pub const Object = struct {
1448 path: Path,
1449 file: fs.File,
1450 must_link: bool,
1451 hidden: bool,
1452 };
1453
1454 pub const Res = struct {
1455 path: Path,
1456 file: fs.File,
1457 };
1458
1459 pub const Dso = struct {
1460 path: Path,
1461 file: fs.File,
1462 needed: bool,
1463 weak: bool,
1464 reexport: bool,
1465 };
1466
1467 pub const DsoExact = struct {
1468 /// Includes the ":" prefix. This is intended to be put into the DSO
1469 /// section verbatim with no corresponding rpaths.
1470 name: []const u8,
1471 };
1472
1473 /// Returns `null` in the case of `dso_exact`.
1474 pub fn path(input: Input) ?Path {
1475 return switch (input) {
1476 .object, .archive => |obj| obj.path,
1477 inline .res, .dso => |x| x.path,
1478 .dso_exact => null,
1479 };
1480 }
1481
1482 /// Returns `null` in the case of `dso_exact`.
1483 pub fn pathAndFile(input: Input) ?struct { Path, fs.File } {
1484 return switch (input) {
1485 .object, .archive => |obj| .{ obj.path, obj.file },
1486 inline .res, .dso => |x| .{ x.path, x.file },
1487 .dso_exact => null,
1488 };
1489 }
1490};
1491
1492pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void {
1493 for (link_inputs) |link_input| {
1494 man.hash.add(@as(@typeInfo(Input).@"union".tag_type.?, link_input));
1495 switch (link_input) {
1496 .object, .archive => |obj| {
1497 _ = try man.addOpenedFile(obj.path, obj.file, null);
1498 man.hash.add(obj.must_link);
1499 man.hash.add(obj.hidden);
1500 },
1501 .res => |res| {
1502 _ = try man.addOpenedFile(res.path, res.file, null);
1503 },
1504 .dso => |dso| {
1505 _ = try man.addOpenedFile(dso.path, dso.file, null);
1506 man.hash.add(dso.needed);
1507 man.hash.add(dso.weak);
1508 man.hash.add(dso.reexport);
1509 },
1510 .dso_exact => |dso_exact| {
1511 man.hash.addBytes(dso_exact.name);
1512 },
1513 }
1514 }
1515}
1516
1517pub fn resolveInputs(
1518 gpa: Allocator,
1519 arena: Allocator,
1520 target: std.Target,
1521 /// This function mutates this array but does not take ownership.
1522 /// Allocated with `gpa`.
1523 unresolved_inputs: *std.ArrayListUnmanaged(UnresolvedInput),
1524 /// Allocated with `gpa`.
1525 resolved_inputs: *std.ArrayListUnmanaged(Input),
1526 lib_directories: []const Cache.Directory,
1527 color: std.zig.Color,
1528) Allocator.Error!void {
1529 var checked_paths: std.ArrayListUnmanaged(u8) = .empty;
1530 defer checked_paths.deinit(gpa);
1531
1532 var ld_script_bytes: std.ArrayListUnmanaged(u8) = .empty;
1533 defer ld_script_bytes.deinit(gpa);
1534
1535 var failed_libs: std.ArrayListUnmanaged(struct {
1536 name: []const u8,
1537 strategy: UnresolvedInput.SearchStrategy,
1538 checked_paths: []const u8,
1539 preferred_mode: std.builtin.LinkMode,
1540 }) = .empty;
1541
1542 // Convert external system libs into a stack so that items can be
1543 // pushed to it.
1544 //
1545 // This is necessary because shared objects might turn out to be
1546 // "linker scripts" that in fact resolve to one or more other
1547 // external system libs, including parameters such as "needed".
1548 //
1549 // Unfortunately, such files need to be detected immediately, so
1550 // that this library search logic can be applied to them.
1551 mem.reverse(UnresolvedInput, unresolved_inputs.items);
1552
1553 syslib: while (unresolved_inputs.popOrNull()) |unresolved_input| {
1554 const name_query: UnresolvedInput.NameQuery = switch (unresolved_input) {
1555 .name_query => |nq| nq,
1556 .ambiguous_name => |an| an: {
1557 const lib_name, const link_mode = stripLibPrefixAndSuffix(an.name, target) orelse {
1558 try resolvePathInput(gpa, arena, unresolved_inputs, resolved_inputs, &ld_script_bytes, target, .{
1559 .path = Path.initCwd(an.name),
1560 .query = an.query,
1561 }, color);
1562 continue;
1563 };
1564 break :an .{
1565 .name = lib_name,
1566 .query = .{
1567 .needed = an.query.needed,
1568 .weak = an.query.weak,
1569 .reexport = an.query.reexport,
1570 .must_link = an.query.must_link,
1571 .hidden = an.query.hidden,
1572 .preferred_mode = link_mode,
1573 .search_strategy = .no_fallback,
1574 },
1575 };
1576 },
1577 .path_query => |pq| {
1578 try resolvePathInput(gpa, arena, unresolved_inputs, resolved_inputs, &ld_script_bytes, target, pq, color);
1579 continue;
1580 },
1581 .dso_exact => |dso_exact| {
1582 try resolved_inputs.append(gpa, .{ .dso_exact = dso_exact });
1583 continue;
1584 },
1585 };
1586 const query = name_query.query;
1587
1588 // Checked in the first pass above while looking for libc libraries.
1589 assert(!fs.path.isAbsolute(name_query.name));
1590
1591 checked_paths.clearRetainingCapacity();
1592
1593 switch (query.search_strategy) {
1594 .mode_first, .no_fallback => {
1595 // check for preferred mode
1596 for (lib_directories) |lib_directory| switch (try resolveLibInput(
1597 gpa,
1598 arena,
1599 unresolved_inputs,
1600 resolved_inputs,
1601 &checked_paths,
1602 &ld_script_bytes,
1603 lib_directory,
1604 name_query,
1605 target,
1606 query.preferred_mode,
1607 color,
1608 )) {
1609 .ok => continue :syslib,
1610 .no_match => {},
1611 };
1612 // check for fallback mode
1613 if (query.search_strategy == .no_fallback) {
1614 try failed_libs.append(arena, .{
1615 .name = name_query.name,
1616 .strategy = query.search_strategy,
1617 .checked_paths = try arena.dupe(u8, checked_paths.items),
1618 .preferred_mode = query.preferred_mode,
1619 });
1620 continue :syslib;
1621 }
1622 for (lib_directories) |lib_directory| switch (try resolveLibInput(
1623 gpa,
1624 arena,
1625 unresolved_inputs,
1626 resolved_inputs,
1627 &checked_paths,
1628 &ld_script_bytes,
1629 lib_directory,
1630 name_query,
1631 target,
1632 query.fallbackMode(),
1633 color,
1634 )) {
1635 .ok => continue :syslib,
1636 .no_match => {},
1637 };
1638 try failed_libs.append(arena, .{
1639 .name = name_query.name,
1640 .strategy = query.search_strategy,
1641 .checked_paths = try arena.dupe(u8, checked_paths.items),
1642 .preferred_mode = query.preferred_mode,
1643 });
1644 continue :syslib;
1645 },
1646 .paths_first => {
1647 for (lib_directories) |lib_directory| {
1648 // check for preferred mode
1649 switch (try resolveLibInput(
1650 gpa,
1651 arena,
1652 unresolved_inputs,
1653 resolved_inputs,
1654 &checked_paths,
1655 &ld_script_bytes,
1656 lib_directory,
1657 name_query,
1658 target,
1659 query.preferred_mode,
1660 color,
1661 )) {
1662 .ok => continue :syslib,
1663 .no_match => {},
1664 }
1665
1666 // check for fallback mode
1667 switch (try resolveLibInput(
1668 gpa,
1669 arena,
1670 unresolved_inputs,
1671 resolved_inputs,
1672 &checked_paths,
1673 &ld_script_bytes,
1674 lib_directory,
1675 name_query,
1676 target,
1677 query.fallbackMode(),
1678 color,
1679 )) {
1680 .ok => continue :syslib,
1681 .no_match => {},
1682 }
1683 }
1684 try failed_libs.append(arena, .{
1685 .name = name_query.name,
1686 .strategy = query.search_strategy,
1687 .checked_paths = try arena.dupe(u8, checked_paths.items),
1688 .preferred_mode = query.preferred_mode,
1689 });
1690 continue :syslib;
1691 },
1692 }
1693 @compileError("unreachable");
1694 }
1695
1696 if (failed_libs.items.len > 0) {
1697 for (failed_libs.items) |f| {
1698 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
1699 std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{
1700 @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths,
1701 });
1702 }
1703 std.process.exit(1);
1704 }
1705}
1706
1707const AccessLibPathResult = enum { ok, no_match };
1708const fatal = std.process.fatal;
1709
1710fn resolveLibInput(
1711 gpa: Allocator,
1712 arena: Allocator,
1713 /// Allocated via `gpa`.
1714 unresolved_inputs: *std.ArrayListUnmanaged(UnresolvedInput),
1715 /// Allocated via `gpa`.
1716 resolved_inputs: *std.ArrayListUnmanaged(Input),
1717 /// Allocated via `gpa`.
1718 checked_paths: *std.ArrayListUnmanaged(u8),
1719 /// Allocated via `gpa`.
1720 ld_script_bytes: *std.ArrayListUnmanaged(u8),
1721 lib_directory: Directory,
1722 name_query: UnresolvedInput.NameQuery,
1723 target: std.Target,
1724 link_mode: std.builtin.LinkMode,
1725 color: std.zig.Color,
1726) Allocator.Error!AccessLibPathResult {
1727 try resolved_inputs.ensureUnusedCapacity(gpa, 1);
1728
1729 const lib_name = name_query.name;
1730
1731 if (target.isDarwin() and link_mode == .dynamic) tbd: {
1732 // Prefer .tbd over .dylib.
1733 const test_path: Path = .{
1734 .root_dir = lib_directory,
1735 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.tbd", .{lib_name}),
1736 };
1737 try checked_paths.writer(gpa).print("\n {}", .{test_path});
1738 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
1739 error.FileNotFound => break :tbd,
1740 else => |e| fatal("unable to search for tbd library '{}': {s}", .{ test_path, @errorName(e) }),
1741 };
1742 errdefer file.close();
1743 return finishAccessLibPath(resolved_inputs, test_path, file, link_mode, name_query.query);
1744 }
1745
1746 {
1747 const test_path: Path = .{
1748 .root_dir = lib_directory,
1749 .sub_path = try std.fmt.allocPrint(arena, "{s}{s}{s}", .{
1750 target.libPrefix(), lib_name, switch (link_mode) {
1751 .static => target.staticLibSuffix(),
1752 .dynamic => target.dynamicLibSuffix(),
1753 },
1754 }),
1755 };
1756 try checked_paths.writer(gpa).print("\n {}", .{test_path});
1757 switch (try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, .{
1758 .path = test_path,
1759 .query = name_query.query,
1760 }, link_mode, color)) {
1761 .no_match => {},
1762 .ok => return .ok,
1763 }
1764 }
1765
1766 // In the case of Darwin, the main check will be .dylib, so here we
1767 // additionally check for .so files.
1768 if (target.isDarwin() and link_mode == .dynamic) so: {
1769 const test_path: Path = .{
1770 .root_dir = lib_directory,
1771 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.so", .{lib_name}),
1772 };
1773 try checked_paths.writer(gpa).print("\n {}", .{test_path});
1774 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
1775 error.FileNotFound => break :so,
1776 else => |e| fatal("unable to search for so library '{}': {s}", .{
1777 test_path, @errorName(e),
1778 }),
1779 };
1780 errdefer file.close();
1781 return finishAccessLibPath(resolved_inputs, test_path, file, link_mode, name_query.query);
1782 }
1783
1784 // In the case of MinGW, the main check will be .lib but we also need to
1785 // look for `libfoo.a`.
1786 if (target.isMinGW() and link_mode == .static) mingw: {
1787 const test_path: Path = .{
1788 .root_dir = lib_directory,
1789 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.a", .{lib_name}),
1790 };
1791 try checked_paths.writer(gpa).print("\n {}", .{test_path});
1792 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
1793 error.FileNotFound => break :mingw,
1794 else => |e| fatal("unable to search for static library '{}': {s}", .{ test_path, @errorName(e) }),
1795 };
1796 errdefer file.close();
1797 return finishAccessLibPath(resolved_inputs, test_path, file, link_mode, name_query.query);
1798 }
1799
1800 return .no_match;
1801}
1802
1803fn finishAccessLibPath(
1804 resolved_inputs: *std.ArrayListUnmanaged(Input),
1805 path: Path,
1806 file: std.fs.File,
1807 link_mode: std.builtin.LinkMode,
1808 query: UnresolvedInput.Query,
1809) AccessLibPathResult {
1810 switch (link_mode) {
1811 .static => resolved_inputs.appendAssumeCapacity(.{ .archive = .{
1812 .path = path,
1813 .file = file,
1814 .must_link = query.must_link,
1815 .hidden = query.hidden,
1816 } }),
1817 .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{
1818 .path = path,
1819 .file = file,
1820 .needed = query.needed,
1821 .weak = query.weak,
1822 .reexport = query.reexport,
1823 } }),
1824 }
1825 return .ok;
1826}
1827
1828fn resolvePathInput(
1829 gpa: Allocator,
1830 arena: Allocator,
1831 /// Allocated with `gpa`.
1832 unresolved_inputs: *std.ArrayListUnmanaged(UnresolvedInput),
1833 /// Allocated with `gpa`.
1834 resolved_inputs: *std.ArrayListUnmanaged(Input),
1835 /// Allocated via `gpa`.
1836 ld_script_bytes: *std.ArrayListUnmanaged(u8),
1837 target: std.Target,
1838 pq: UnresolvedInput.PathQuery,
1839 color: std.zig.Color,
1840) Allocator.Error!void {
1841 switch (switch (Compilation.classifyFileExt(pq.path.sub_path)) {
1842 .static_library => try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color),
1843 .shared_library => try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color),
1844 .object => {
1845 var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|
1846 fatal("failed to open object {}: {s}", .{ pq.path, @errorName(err) });
1847 errdefer file.close();
1848 try resolved_inputs.append(gpa, .{ .object = .{
1849 .path = pq.path,
1850 .file = file,
1851 .must_link = pq.query.must_link,
1852 .hidden = pq.query.hidden,
1853 } });
1854 return;
1855 },
1856 .res => {
1857 var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|
1858 fatal("failed to open windows resource {}: {s}", .{ pq.path, @errorName(err) });
1859 errdefer file.close();
1860 try resolved_inputs.append(gpa, .{ .res = .{
1861 .path = pq.path,
1862 .file = file,
1863 } });
1864 return;
1865 },
1866 else => fatal("{}: unrecognized file extension", .{pq.path}),
1867 }) {
1868 .ok => {},
1869 .no_match => fatal("{}: file not found", .{pq.path}),
1870 }
1871}
1872
1873fn resolvePathInputLib(
1874 gpa: Allocator,
1875 arena: Allocator,
1876 /// Allocated with `gpa`.
1877 unresolved_inputs: *std.ArrayListUnmanaged(UnresolvedInput),
1878 /// Allocated with `gpa`.
1879 resolved_inputs: *std.ArrayListUnmanaged(Input),
1880 /// Allocated via `gpa`.
1881 ld_script_bytes: *std.ArrayListUnmanaged(u8),
1882 target: std.Target,
1883 pq: UnresolvedInput.PathQuery,
1884 link_mode: std.builtin.LinkMode,
1885 color: std.zig.Color,
1886) Allocator.Error!AccessLibPathResult {
1887 const test_path: Path = pq.path;
1888 // In the case of .so files, they might actually be "linker scripts"
1889 // that contain references to other libraries.
1890 if (pq.query.allow_so_scripts and target.ofmt == .elf and mem.endsWith(u8, test_path.sub_path, ".so")) {
1891 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
1892 error.FileNotFound => return .no_match,
1893 else => |e| fatal("unable to search for {s} library '{'}': {s}", .{
1894 @tagName(link_mode), test_path, @errorName(e),
1895 }),
1896 };
1897 errdefer file.close();
1898 try ld_script_bytes.resize(gpa, @sizeOf(std.elf.Elf64_Ehdr));
1899 const n = file.preadAll(ld_script_bytes.items, 0) catch |err| fatal("failed to read '{'}': {s}", .{
1900 test_path, @errorName(err),
1901 });
1902 elf_file: {
1903 if (n != ld_script_bytes.items.len) break :elf_file;
1904 if (!mem.eql(u8, ld_script_bytes.items[0..4], "\x7fELF")) break :elf_file;
1905 // Appears to be an ELF file.
1906 return finishAccessLibPath(resolved_inputs, test_path, file, link_mode, pq.query);
1907 }
1908 const stat = file.stat() catch |err|
1909 fatal("failed to stat {}: {s}", .{ test_path, @errorName(err) });
1910 const size = std.math.cast(u32, stat.size) orelse
1911 fatal("{}: linker script too big", .{test_path});
1912 try ld_script_bytes.resize(gpa, size);
1913 const buf = ld_script_bytes.items[n..];
1914 const n2 = file.preadAll(buf, n) catch |err|
1915 fatal("failed to read {}: {s}", .{ test_path, @errorName(err) });
1916 if (n2 != buf.len) fatal("failed to read {}: unexpected end of file", .{test_path});
1917 var diags = Diags.init(gpa);
1918 defer diags.deinit();
1919 const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items);
1920 if (diags.hasErrors()) {
1921 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
1922 try wip_errors.init(gpa);
1923 defer wip_errors.deinit();
1924
1925 try diags.addMessagesToBundle(&wip_errors);
1926
1927 var error_bundle = try wip_errors.toOwnedBundle("");
1928 defer error_bundle.deinit(gpa);
1929
1930 error_bundle.renderToStdErr(color.renderOptions());
1931
1932 std.process.exit(1);
1933 }
1934
1935 var ld_script = ld_script_result catch |err|
1936 fatal("{}: failed to parse linker script: {s}", .{ test_path, @errorName(err) });
1937 defer ld_script.deinit(gpa);
1938
1939 try unresolved_inputs.ensureUnusedCapacity(gpa, ld_script.args.len);
1940 for (ld_script.args) |arg| {
1941 const query: UnresolvedInput.Query = .{
1942 .needed = arg.needed or pq.query.needed,
1943 .weak = pq.query.weak,
1944 .reexport = pq.query.reexport,
1945 .preferred_mode = pq.query.preferred_mode,
1946 .search_strategy = pq.query.search_strategy,
1947 .allow_so_scripts = pq.query.allow_so_scripts,
1948 };
1949 if (mem.startsWith(u8, arg.path, "-l")) {
1950 unresolved_inputs.appendAssumeCapacity(.{ .name_query = .{
1951 .name = try arena.dupe(u8, arg.path["-l".len..]),
1952 .query = query,
1953 } });
1954 } else {
1955 unresolved_inputs.appendAssumeCapacity(.{ .ambiguous_name = .{
1956 .name = try arena.dupe(u8, arg.path),
1957 .query = query,
1958 } });
1959 }
1960 }
1961 file.close();
1962 return .ok;
1963 }
1964
1965 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
1966 error.FileNotFound => return .no_match,
1967 else => |e| fatal("unable to search for {s} library {}: {s}", .{
1968 @tagName(link_mode), test_path, @errorName(e),
1969 }),
1970 };
1971 errdefer file.close();
1972 return finishAccessLibPath(resolved_inputs, test_path, file, link_mode, pq.query);
1973}
1974
1975pub fn openObject(path: Path, must_link: bool, hidden: bool) !Input.Object {
1976 var file = try path.root_dir.handle.openFile(path.sub_path, .{});
1977 errdefer file.close();
1978 return .{
1979 .path = path,
1980 .file = file,
1981 .must_link = must_link,
1982 .hidden = hidden,
1983 };
1984}
1985
1986pub fn openDso(path: Path, needed: bool, weak: bool, reexport: bool) !Input.Dso {
1987 var file = try path.root_dir.handle.openFile(path.sub_path, .{});
1988 errdefer file.close();
1989 return .{
1990 .path = path,
1991 .file = file,
1992 .needed = needed,
1993 .weak = weak,
1994 .reexport = reexport,
1995 };
1996}
1997
1998pub fn openObjectInput(diags: *Diags, path: Path) error{LinkFailure}!Input {
1999 return .{ .object = openObject(path, false, false) catch |err| {
2000 return diags.failParse(path, "failed to open {}: {s}", .{ path, @errorName(err) });
2001 } };
2002}
2003
2004pub fn openArchiveInput(diags: *Diags, path: Path) error{LinkFailure}!Input {
2005 return .{ .archive = openObject(path, false, false) catch |err| {
2006 return diags.failParse(path, "failed to open {}: {s}", .{ path, @errorName(err) });
2007 } };
2008}
2009
2010fn stripLibPrefixAndSuffix(path: []const u8, target: std.Target) ?struct { []const u8, std.builtin.LinkMode } {
2011 const prefix = target.libPrefix();
2012 const static_suffix = target.staticLibSuffix();
2013 const dynamic_suffix = target.dynamicLibSuffix();
2014 const basename = fs.path.basename(path);
2015 const unlibbed = if (mem.startsWith(u8, basename, prefix)) basename[prefix.len..] else return null;
2016 if (mem.endsWith(u8, unlibbed, static_suffix)) return .{
2017 unlibbed[0 .. unlibbed.len - static_suffix.len], .static,
2018 };
2019 if (mem.endsWith(u8, unlibbed, dynamic_suffix)) return .{
2020 unlibbed[0 .. unlibbed.len - dynamic_suffix.len], .dynamic,
2021 };
2022 return null;
2023}
2024
2025/// Returns true if and only if there is at least one input of type object,
2026/// archive, or Windows resource file.
2027pub fn anyObjectInputs(inputs: []const Input) bool {
2028 return countObjectInputs(inputs) != 0;
2029}
2030
2031/// Returns the number of inputs of type object, archive, or Windows resource file.
2032pub fn countObjectInputs(inputs: []const Input) usize {
2033 var count: usize = 0;
2034 for (inputs) |input| switch (input) {
2035 .dso, .dso_exact => continue,
2036 .res, .object, .archive => count += 1,
2037 };
2038 return count;
2039}
2040
2041/// Returns the first input of type object or archive.
2042pub fn firstObjectInput(inputs: []const Input) ?Input.Object {
2043 for (inputs) |input| switch (input) {
2044 .object, .archive => |obj| return obj,
2045 .res, .dso, .dso_exact => continue,
2046 };
2047 return null;
2048}
src/link/Coff.zig+3-2
...@@ -16,7 +16,7 @@ dynamicbase: bool,...@@ -16,7 +16,7 @@ dynamicbase: bool,
16/// default or populated together. They should not be separate fields.16/// default or populated together. They should not be separate fields.
17major_subsystem_version: u16,17major_subsystem_version: u16,
18minor_subsystem_version: u16,18minor_subsystem_version: u16,
19lib_dirs: []const []const u8,19lib_directories: []const Directory,
20entry: link.File.OpenOptions.Entry,20entry: link.File.OpenOptions.Entry,
21entry_addr: ?u32,21entry_addr: ?u32,
22module_definition_file: ?[]const u8,22module_definition_file: ?[]const u8,
...@@ -297,7 +297,7 @@ pub fn createEmpty(...@@ -297,7 +297,7 @@ pub fn createEmpty(
297 .dynamicbase = options.dynamicbase,297 .dynamicbase = options.dynamicbase,
298 .major_subsystem_version = options.major_subsystem_version orelse 6,298 .major_subsystem_version = options.major_subsystem_version orelse 6,
299 .minor_subsystem_version = options.minor_subsystem_version orelse 0,299 .minor_subsystem_version = options.minor_subsystem_version orelse 0,
300 .lib_dirs = options.lib_dirs,300 .lib_directories = options.lib_directories,
301 .entry_addr = math.cast(u32, options.entry_addr orelse 0) orelse301 .entry_addr = math.cast(u32, options.entry_addr orelse 0) orelse
302 return error.EntryAddressTooBig,302 return error.EntryAddressTooBig,
303 .module_definition_file = options.module_definition_file,303 .module_definition_file = options.module_definition_file,
...@@ -2727,6 +2727,7 @@ const mem = std.mem;...@@ -2727,6 +2727,7 @@ const mem = std.mem;
27272727
2728const Allocator = std.mem.Allocator;2728const Allocator = std.mem.Allocator;
2729const Path = std.Build.Cache.Path;2729const Path = std.Build.Cache.Path;
2730const Directory = std.Build.Cache.Directory;
27302731
2731const codegen = @import("../codegen.zig");2732const codegen = @import("../codegen.zig");
2732const link = @import("../link.zig");2733const link = @import("../link.zig");
src/link/Coff/lld.zig+32-27
...@@ -8,6 +8,7 @@ const log = std.log.scoped(.link);...@@ -8,6 +8,7 @@ const log = std.log.scoped(.link);
8const mem = std.mem;8const mem = std.mem;
9const Cache = std.Build.Cache;9const Cache = std.Build.Cache;
10const Path = std.Build.Cache.Path;10const Path = std.Build.Cache.Path;
11const Directory = std.Build.Cache.Directory;
1112
12const mingw = @import("../../mingw.zig");13const mingw = @import("../../mingw.zig");
13const link = @import("../../link.zig");14const link = @import("../../link.zig");
...@@ -74,10 +75,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -74,10 +75,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
7475
75 comptime assert(Compilation.link_hash_implementation_version == 14);76 comptime assert(Compilation.link_hash_implementation_version == 14);
7677
77 for (comp.objects) |obj| {78 try link.hashInputs(&man, comp.link_inputs);
78 _ = try man.addFilePath(obj.path, null);
79 man.hash.add(obj.must_link);
80 }
81 for (comp.c_object_table.keys()) |key| {79 for (comp.c_object_table.keys()) |key| {
82 _ = try man.addFilePath(key.status.success.object_path, null);80 _ = try man.addFilePath(key.status.success.object_path, null);
83 }81 }
...@@ -88,7 +86,10 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -88,7 +86,10 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
88 man.hash.addOptionalBytes(entry_name);86 man.hash.addOptionalBytes(entry_name);
89 man.hash.add(self.base.stack_size);87 man.hash.add(self.base.stack_size);
90 man.hash.add(self.image_base);88 man.hash.add(self.image_base);
91 man.hash.addListOfBytes(self.lib_dirs);89 {
90 // TODO remove this, libraries must instead be resolved by the frontend.
91 for (self.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path);
92 }
92 man.hash.add(comp.skip_linker_dependencies);93 man.hash.add(comp.skip_linker_dependencies);
93 if (comp.config.link_libc) {94 if (comp.config.link_libc) {
94 man.hash.add(comp.libc_installation != null);95 man.hash.add(comp.libc_installation != null);
...@@ -100,7 +101,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -100,7 +101,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
100 }101 }
101 }102 }
102 }103 }
103 try link.hashAddSystemLibs(&man, comp.system_libs);104 man.hash.addListOfBytes(comp.windows_libs.keys());
104 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());105 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
105 man.hash.addOptional(self.subsystem);106 man.hash.addOptional(self.subsystem);
106 man.hash.add(comp.config.is_test);107 man.hash.add(comp.config.is_test);
...@@ -148,8 +149,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -148,8 +149,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
148 // here. TODO: think carefully about how we can avoid this redundant operation when doing149 // here. TODO: think carefully about how we can avoid this redundant operation when doing
149 // build-obj. See also the corresponding TODO in linkAsArchive.150 // build-obj. See also the corresponding TODO in linkAsArchive.
150 const the_object_path = blk: {151 const the_object_path = blk: {
151 if (comp.objects.len != 0)152 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
152 break :blk comp.objects[0].path;
153153
154 if (comp.c_object_table.count() != 0)154 if (comp.c_object_table.count() != 0)
155 break :blk comp.c_object_table.keys()[0].status.success.object_path;155 break :blk comp.c_object_table.keys()[0].status.success.object_path;
...@@ -266,18 +266,24 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -266,18 +266,24 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
266 }266 }
267 }267 }
268268
269 for (self.lib_dirs) |lib_dir| {269 for (self.lib_directories) |lib_directory| {
270 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir}));270 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_directory.path orelse "."}));
271 }271 }
272272
273 try argv.ensureUnusedCapacity(comp.objects.len);273 try argv.ensureUnusedCapacity(comp.link_inputs.len);
274 for (comp.objects) |obj| {274 for (comp.link_inputs) |link_input| switch (link_input) {
275 if (obj.must_link) {275 .dso_exact => unreachable, // not applicable to PE/COFF
276 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)}));276 inline .dso, .res => |x| {
277 } else {277 argv.appendAssumeCapacity(try x.path.toString(arena));
278 argv.appendAssumeCapacity(try obj.path.toString(arena));278 },
279 }279 .object, .archive => |obj| {
280 }280 if (obj.must_link) {
281 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)}));
282 } else {
283 argv.appendAssumeCapacity(try obj.path.toString(arena));
284 }
285 },
286 };
281287
282 for (comp.c_object_table.keys()) |key| {288 for (comp.c_object_table.keys()) |key| {
283 try argv.append(try key.status.success.object_path.toString(arena));289 try argv.append(try key.status.success.object_path.toString(arena));
...@@ -484,20 +490,20 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -484,20 +490,20 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
484 if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));490 if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));
485 }491 }
486492
487 try argv.ensureUnusedCapacity(comp.system_libs.count());493 try argv.ensureUnusedCapacity(comp.windows_libs.count());
488 for (comp.system_libs.keys()) |key| {494 for (comp.windows_libs.keys()) |key| {
489 const lib_basename = try allocPrint(arena, "{s}.lib", .{key});495 const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
490 if (comp.crt_files.get(lib_basename)) |crt_file| {496 if (comp.crt_files.get(lib_basename)) |crt_file| {
491 argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena));497 argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena));
492 continue;498 continue;
493 }499 }
494 if (try findLib(arena, lib_basename, self.lib_dirs)) |full_path| {500 if (try findLib(arena, lib_basename, self.lib_directories)) |full_path| {
495 argv.appendAssumeCapacity(full_path);501 argv.appendAssumeCapacity(full_path);
496 continue;502 continue;
497 }503 }
498 if (target.abi.isGnu()) {504 if (target.abi.isGnu()) {
499 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});505 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});
500 if (try findLib(arena, fallback_name, self.lib_dirs)) |full_path| {506 if (try findLib(arena, fallback_name, self.lib_directories)) |full_path| {
501 argv.appendAssumeCapacity(full_path);507 argv.appendAssumeCapacity(full_path);
502 continue;508 continue;
503 }509 }
...@@ -530,14 +536,13 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -530,14 +536,13 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
530 }536 }
531}537}
532538
533fn findLib(arena: Allocator, name: []const u8, lib_dirs: []const []const u8) !?[]const u8 {539fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Directory) !?[]const u8 {
534 for (lib_dirs) |lib_dir| {540 for (lib_directories) |lib_directory| {
535 const full_path = try fs.path.join(arena, &.{ lib_dir, name });541 lib_directory.handle.access(name, .{}) catch |err| switch (err) {
536 fs.cwd().access(full_path, .{}) catch |err| switch (err) {
537 error.FileNotFound => continue,542 error.FileNotFound => continue,
538 else => |e| return e,543 else => |e| return e,
539 };544 };
540 return full_path;545 return try lib_directory.join(arena, &.{name});
541 }546 }
542 return null;547 return null;
543}548}
src/link/Elf.zig+223-149
...@@ -796,44 +796,55 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -796,44 +796,55 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
796 const csu = try comp.getCrtPaths(arena);796 const csu = try comp.getCrtPaths(arena);
797797
798 // csu prelude798 // csu prelude
799 if (csu.crt0) |path| parseObjectReportingFailure(self, path);799 if (csu.crt0) |path| openParseObjectReportingFailure(self, path);
800 if (csu.crti) |path| parseObjectReportingFailure(self, path);800 if (csu.crti) |path| openParseObjectReportingFailure(self, path);
801 if (csu.crtbegin) |path| parseObjectReportingFailure(self, path);801 if (csu.crtbegin) |path| openParseObjectReportingFailure(self, path);
802802
803 for (comp.objects) |obj| {803 // objects and archives
804 parseInputReportingFailure(self, obj.path, obj.needed, obj.must_link);804 for (comp.link_inputs) |link_input| switch (link_input) {
805 }805 .object, .archive => parseInputReportingFailure(self, link_input),
806 .dso_exact => @panic("TODO"),
807 .dso => continue, // handled below
808 .res => unreachable,
809 };
806810
807 // This is a set of object files emitted by clang in a single `build-exe` invocation.811 // This is a set of object files emitted by clang in a single `build-exe` invocation.
808 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up812 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
809 // in this set.813 // in this set.
810 for (comp.c_object_table.keys()) |key| {814 for (comp.c_object_table.keys()) |key| {
811 parseObjectReportingFailure(self, key.status.success.object_path);815 openParseObjectReportingFailure(self, key.status.success.object_path);
812 }816 }
813817
814 if (module_obj_path) |path| parseObjectReportingFailure(self, path);818 if (module_obj_path) |path| openParseObjectReportingFailure(self, path);
815819
816 if (comp.config.any_sanitize_thread) parseCrtFileReportingFailure(self, comp.tsan_lib.?);820 if (comp.config.any_sanitize_thread)
817 if (comp.config.any_fuzz) parseCrtFileReportingFailure(self, comp.fuzzer_lib.?);821 openParseArchiveReportingFailure(self, comp.tsan_lib.?.full_object_path);
822
823 if (comp.config.any_fuzz)
824 openParseArchiveReportingFailure(self, comp.fuzzer_lib.?.full_object_path);
818825
819 // libc826 // libc
820 if (!comp.skip_linker_dependencies and !comp.config.link_libc) {827 if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
821 if (comp.libc_static_lib) |lib| parseCrtFileReportingFailure(self, lib);828 if (comp.libc_static_lib) |lib|
829 openParseArchiveReportingFailure(self, lib.full_object_path);
822 }830 }
823831
824 for (comp.system_libs.values()) |lib_info| {832 // dynamic libraries
825 parseInputReportingFailure(self, lib_info.path.?, lib_info.needed, false);833 for (comp.link_inputs) |link_input| switch (link_input) {
826 }834 .object, .archive, .dso_exact => continue, // handled above
835 .dso => parseInputReportingFailure(self, link_input),
836 .res => unreachable,
837 };
827838
828 // libc++ dep839 // libc++ dep
829 if (comp.config.link_libcpp) {840 if (comp.config.link_libcpp) {
830 parseInputReportingFailure(self, comp.libcxxabi_static_lib.?.full_object_path, false, false);841 openParseArchiveReportingFailure(self, comp.libcxxabi_static_lib.?.full_object_path);
831 parseInputReportingFailure(self, comp.libcxx_static_lib.?.full_object_path, false, false);842 openParseArchiveReportingFailure(self, comp.libcxx_static_lib.?.full_object_path);
832 }843 }
833844
834 // libunwind dep845 // libunwind dep
835 if (comp.config.link_libunwind) {846 if (comp.config.link_libunwind) {
836 parseInputReportingFailure(self, comp.libunwind_static_lib.?.full_object_path, false, false);847 openParseArchiveReportingFailure(self, comp.libunwind_static_lib.?.full_object_path);
837 }848 }
838849
839 // libc dep850 // libc dep
...@@ -853,7 +864,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -853,7 +864,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
853 lc.crt_dir.?, lib_name, suffix,864 lc.crt_dir.?, lib_name, suffix,
854 });865 });
855 const resolved_path = Path.initCwd(lib_path);866 const resolved_path = Path.initCwd(lib_path);
856 parseInputReportingFailure(self, resolved_path, false, false);867 switch (comp.config.link_mode) {
868 .static => openParseArchiveReportingFailure(self, resolved_path),
869 .dynamic => openParseDsoReportingFailure(self, resolved_path),
870 }
857 }871 }
858 } else if (target.isGnuLibC()) {872 } else if (target.isGnuLibC()) {
859 for (glibc.libs) |lib| {873 for (glibc.libs) |lib| {
...@@ -864,15 +878,19 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -864,15 +878,19 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
864 const lib_path = Path.initCwd(try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{878 const lib_path = Path.initCwd(try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
865 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,879 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
866 }));880 }));
867 parseInputReportingFailure(self, lib_path, false, false);881 openParseDsoReportingFailure(self, lib_path);
868 }882 }
869 parseInputReportingFailure(self, try comp.get_libc_crt_file(arena, "libc_nonshared.a"), false, false);883 const crt_file_path = try comp.get_libc_crt_file(arena, "libc_nonshared.a");
884 openParseArchiveReportingFailure(self, crt_file_path);
870 } else if (target.isMusl()) {885 } else if (target.isMusl()) {
871 const path = try comp.get_libc_crt_file(arena, switch (link_mode) {886 const path = try comp.get_libc_crt_file(arena, switch (link_mode) {
872 .static => "libc.a",887 .static => "libc.a",
873 .dynamic => "libc.so",888 .dynamic => "libc.so",
874 });889 });
875 parseInputReportingFailure(self, path, false, false);890 switch (link_mode) {
891 .static => openParseArchiveReportingFailure(self, path),
892 .dynamic => openParseDsoReportingFailure(self, path),
893 }
876 } else {894 } else {
877 diags.flags.missing_libc = true;895 diags.flags.missing_libc = true;
878 }896 }
...@@ -884,14 +902,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -884,14 +902,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
884 // to be after the shared libraries, so they are picked up from the shared902 // to be after the shared libraries, so they are picked up from the shared
885 // libraries, not libcompiler_rt.903 // libraries, not libcompiler_rt.
886 if (comp.compiler_rt_lib) |crt_file| {904 if (comp.compiler_rt_lib) |crt_file| {
887 parseInputReportingFailure(self, crt_file.full_object_path, false, false);905 openParseArchiveReportingFailure(self, crt_file.full_object_path);
888 } else if (comp.compiler_rt_obj) |crt_file| {906 } else if (comp.compiler_rt_obj) |crt_file| {
889 parseObjectReportingFailure(self, crt_file.full_object_path);907 openParseObjectReportingFailure(self, crt_file.full_object_path);
890 }908 }
891909
892 // csu postlude910 // csu postlude
893 if (csu.crtend) |path| parseObjectReportingFailure(self, path);911 if (csu.crtend) |path| openParseObjectReportingFailure(self, path);
894 if (csu.crtn) |path| parseObjectReportingFailure(self, path);912 if (csu.crtn) |path| openParseObjectReportingFailure(self, path);
895913
896 if (diags.hasErrors()) return error.FlushFailure;914 if (diags.hasErrors()) return error.FlushFailure;
897915
...@@ -1087,9 +1105,15 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {...@@ -1087,9 +1105,15 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
1087 try argv.append(full_out_path);1105 try argv.append(full_out_path);
10881106
1089 if (self.base.isRelocatable()) {1107 if (self.base.isRelocatable()) {
1090 for (comp.objects) |obj| {1108 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
1091 try argv.append(try obj.path.toString(arena));1109 .res => unreachable,
1092 }1110 .dso => |dso| try argv.append(try dso.path.toString(arena)),
1111 .object, .archive => |obj| try argv.append(try obj.path.toString(arena)),
1112 .dso_exact => |dso_exact| {
1113 assert(dso_exact.name[0] == ':');
1114 try argv.appendSlice(&.{ "-l", dso_exact.name });
1115 },
1116 };
10931117
1094 for (comp.c_object_table.keys()) |key| {1118 for (comp.c_object_table.keys()) |key| {
1095 try argv.append(try key.status.success.object_path.toString(arena));1119 try argv.append(try key.status.success.object_path.toString(arena));
...@@ -1186,20 +1210,26 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {...@@ -1186,20 +1210,26 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
1186 }1210 }
11871211
1188 var whole_archive = false;1212 var whole_archive = false;
1189 for (comp.objects) |obj| {
1190 if (obj.must_link and !whole_archive) {
1191 try argv.append("-whole-archive");
1192 whole_archive = true;
1193 } else if (!obj.must_link and whole_archive) {
1194 try argv.append("-no-whole-archive");
1195 whole_archive = false;
1196 }
11971213
1198 if (obj.loption) {1214 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
1199 try argv.append("-l");1215 .res => unreachable,
1200 }1216 .dso => continue,
1201 try argv.append(try obj.path.toString(arena));1217 .object, .archive => |obj| {
1202 }1218 if (obj.must_link and !whole_archive) {
1219 try argv.append("-whole-archive");
1220 whole_archive = true;
1221 } else if (!obj.must_link and whole_archive) {
1222 try argv.append("-no-whole-archive");
1223 whole_archive = false;
1224 }
1225 try argv.append(try obj.path.toString(arena));
1226 },
1227 .dso_exact => |dso_exact| {
1228 assert(dso_exact.name[0] == ':');
1229 try argv.appendSlice(&.{ "-l", dso_exact.name });
1230 },
1231 };
1232
1203 if (whole_archive) {1233 if (whole_archive) {
1204 try argv.append("-no-whole-archive");1234 try argv.append("-no-whole-archive");
1205 whole_archive = false;1235 whole_archive = false;
...@@ -1231,25 +1261,28 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {...@@ -1231,25 +1261,28 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
1231 // Shared libraries.1261 // Shared libraries.
1232 // Worst-case, we need an --as-needed argument for every lib, as well1262 // Worst-case, we need an --as-needed argument for every lib, as well
1233 // as one before and one after.1263 // as one before and one after.
1234 try argv.ensureUnusedCapacity(self.base.comp.system_libs.keys().len * 2 + 2);
1235 argv.appendAssumeCapacity("--as-needed");1264 argv.appendAssumeCapacity("--as-needed");
1236 var as_needed = true;1265 var as_needed = true;
12371266
1238 for (self.base.comp.system_libs.values()) |lib_info| {1267 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
1239 const lib_as_needed = !lib_info.needed;1268 .object, .archive, .dso_exact => continue,
1240 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {1269 .dso => |dso| {
1241 0b00, 0b11 => {},1270 const lib_as_needed = !dso.needed;
1242 0b01 => {1271 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
1243 argv.appendAssumeCapacity("--no-as-needed");1272 0b00, 0b11 => {},
1244 as_needed = false;1273 0b01 => {
1245 },1274 try argv.append("--no-as-needed");
1246 0b10 => {1275 as_needed = false;
1247 argv.appendAssumeCapacity("--as-needed");1276 },
1248 as_needed = true;1277 0b10 => {
1249 },1278 try argv.append("--as-needed");
1250 }1279 as_needed = true;
1251 argv.appendAssumeCapacity(try lib_info.path.?.toString(arena));1280 },
1252 }1281 }
1282 argv.appendAssumeCapacity(try dso.path.toString(arena));
1283 },
1284 .res => unreachable,
1285 };
12531286
1254 if (!as_needed) {1287 if (!as_needed) {
1255 argv.appendAssumeCapacity("--as-needed");1288 argv.appendAssumeCapacity("--as-needed");
...@@ -1321,59 +1354,51 @@ pub const ParseError = error{...@@ -1321,59 +1354,51 @@ pub const ParseError = error{
1321 UnknownFileType,1354 UnknownFileType,
1322} || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;1355} || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;
13231356
1324fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CrtFile) void {1357pub fn parseInputReportingFailure(self: *Elf, input: link.Input) void {
1325 parseInputReportingFailure(self, crt_file.full_object_path, false, false);
1326}
1327
1328pub fn parseInputReportingFailure(self: *Elf, path: Path, needed: bool, must_link: bool) void {
1329 const gpa = self.base.comp.gpa;1358 const gpa = self.base.comp.gpa;
1330 const diags = &self.base.comp.link_diags;1359 const diags = &self.base.comp.link_diags;
1331 const target = self.getTarget();1360 const target = self.getTarget();
13321361
1333 switch (Compilation.classifyFileExt(path.sub_path)) {1362 switch (input) {
1334 .object => parseObjectReportingFailure(self, path),1363 .res => unreachable,
1335 .shared_library => parseSharedObject(gpa, diags, .{1364 .dso_exact => unreachable,
1336 .path = path,1365 .object => |obj| parseObjectReportingFailure(self, obj),
1337 .needed = needed,1366 .archive => |obj| parseArchiveReportingFailure(self, obj),
1338 }, &self.shared_objects, &self.files, target) catch |err| switch (err) {1367 .dso => |dso| parseDsoReportingFailure(gpa, diags, dso, &self.shared_objects, &self.files, target),
1339 error.LinkFailure => return, // already reported
1340 error.BadMagic, error.UnexpectedEndOfFile => {
1341 var notes = diags.addErrorWithNotes(2) catch return diags.setAllocFailure();
1342 notes.addMsg("failed to parse shared object: {s}", .{@errorName(err)}) catch return diags.setAllocFailure();
1343 notes.addNote("while parsing {}", .{path}) catch return diags.setAllocFailure();
1344 notes.addNote("{s}", .{@as([]const u8, "the file may be a GNU ld script, in which case it is not an ELF file but a text file referencing other libraries to link. In this case, avoid depending on the library, convince your system administrators to refrain from using this kind of file, or pass -fallow-so-scripts to force the compiler to check every shared library in case it is an ld script.")}) catch return diags.setAllocFailure();
1345 },
1346 else => |e| diags.addParseError(path, "failed to parse shared object: {s}", .{@errorName(e)}),
1347 },
1348 .static_library => parseArchive(self, path, must_link) catch |err| switch (err) {
1349 error.LinkFailure => return, // already reported
1350 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1351 },
1352 else => diags.addParseError(path, "unrecognized file type", .{}),
1353 }1368 }
1354}1369}
13551370
1356pub fn parseObjectReportingFailure(self: *Elf, path: Path) void {1371pub fn openParseObjectReportingFailure(self: *Elf, path: Path) void {
1372 const diags = &self.base.comp.link_diags;
1373 const obj = link.openObject(path, false, false) catch |err| {
1374 switch (diags.failParse(path, "failed to open object {}: {s}", .{ path, @errorName(err) })) {
1375 error.LinkFailure => return,
1376 }
1377 };
1378 self.parseObjectReportingFailure(obj);
1379}
1380
1381pub fn parseObjectReportingFailure(self: *Elf, obj: link.Input.Object) void {
1357 const diags = &self.base.comp.link_diags;1382 const diags = &self.base.comp.link_diags;
1358 self.parseObject(path) catch |err| switch (err) {1383 self.parseObject(obj) catch |err| switch (err) {
1359 error.LinkFailure => return, // already reported1384 error.LinkFailure => return, // already reported
1360 else => |e| diags.addParseError(path, "unable to parse object: {s}", .{@errorName(e)}),1385 else => |e| diags.addParseError(obj.path, "failed to parse object: {s}", .{@errorName(e)}),
1361 };1386 };
1362}1387}
13631388
1364fn parseObject(self: *Elf, path: Path) ParseError!void {1389fn parseObject(self: *Elf, obj: link.Input.Object) ParseError!void {
1365 const tracy = trace(@src());1390 const tracy = trace(@src());
1366 defer tracy.end();1391 defer tracy.end();
13671392
1368 const gpa = self.base.comp.gpa;1393 const gpa = self.base.comp.gpa;
1369 const handle = try path.root_dir.handle.openFile(path.sub_path, .{});1394 const handle = obj.file;
1370 const fh = try self.addFileHandle(handle);1395 const fh = try self.addFileHandle(handle);
13711396
1372 const index: File.Index = @intCast(try self.files.addOne(gpa));1397 const index: File.Index = @intCast(try self.files.addOne(gpa));
1373 self.files.set(index, .{ .object = .{1398 self.files.set(index, .{ .object = .{
1374 .path = .{1399 .path = .{
1375 .root_dir = path.root_dir,1400 .root_dir = obj.path.root_dir,
1376 .sub_path = try gpa.dupe(u8, path.sub_path),1401 .sub_path = try gpa.dupe(u8, obj.path.sub_path),
1377 },1402 },
1378 .file_handle = fh,1403 .file_handle = fh,
1379 .index = index,1404 .index = index,
...@@ -1384,17 +1409,35 @@ fn parseObject(self: *Elf, path: Path) ParseError!void {...@@ -1384,17 +1409,35 @@ fn parseObject(self: *Elf, path: Path) ParseError!void {
1384 try object.parse(self);1409 try object.parse(self);
1385}1410}
13861411
1387fn parseArchive(self: *Elf, path: Path, must_link: bool) ParseError!void {1412pub fn openParseArchiveReportingFailure(self: *Elf, path: Path) void {
1413 const diags = &self.base.comp.link_diags;
1414 const obj = link.openObject(path, false, false) catch |err| {
1415 switch (diags.failParse(path, "failed to open archive {}: {s}", .{ path, @errorName(err) })) {
1416 error.LinkFailure => return,
1417 }
1418 };
1419 parseArchiveReportingFailure(self, obj);
1420}
1421
1422pub fn parseArchiveReportingFailure(self: *Elf, obj: link.Input.Object) void {
1423 const diags = &self.base.comp.link_diags;
1424 self.parseArchive(obj) catch |err| switch (err) {
1425 error.LinkFailure => return, // already reported
1426 else => |e| diags.addParseError(obj.path, "failed to parse archive: {s}", .{@errorName(e)}),
1427 };
1428}
1429
1430fn parseArchive(self: *Elf, obj: link.Input.Object) ParseError!void {
1388 const tracy = trace(@src());1431 const tracy = trace(@src());
1389 defer tracy.end();1432 defer tracy.end();
13901433
1391 const gpa = self.base.comp.gpa;1434 const gpa = self.base.comp.gpa;
1392 const handle = try path.root_dir.handle.openFile(path.sub_path, .{});1435 const handle = obj.file;
1393 const fh = try self.addFileHandle(handle);1436 const fh = try self.addFileHandle(handle);
13941437
1395 var archive: Archive = .{};1438 var archive: Archive = .{};
1396 defer archive.deinit(gpa);1439 defer archive.deinit(gpa);
1397 try archive.parse(self, path, fh);1440 try archive.parse(self, obj.path, fh);
13981441
1399 const objects = try archive.objects.toOwnedSlice(gpa);1442 const objects = try archive.objects.toOwnedSlice(gpa);
1400 defer gpa.free(objects);1443 defer gpa.free(objects);
...@@ -1404,16 +1447,48 @@ fn parseArchive(self: *Elf, path: Path, must_link: bool) ParseError!void {...@@ -1404,16 +1447,48 @@ fn parseArchive(self: *Elf, path: Path, must_link: bool) ParseError!void {
1404 self.files.set(index, .{ .object = extracted });1447 self.files.set(index, .{ .object = extracted });
1405 const object = &self.files.items(.data)[index].object;1448 const object = &self.files.items(.data)[index].object;
1406 object.index = index;1449 object.index = index;
1407 object.alive = must_link;1450 object.alive = obj.must_link;
1408 try object.parse(self);1451 try object.parse(self);
1409 try self.objects.append(gpa, index);1452 try self.objects.append(gpa, index);
1410 }1453 }
1411}1454}
14121455
1413fn parseSharedObject(1456fn openParseDsoReportingFailure(self: *Elf, path: Path) void {
1457 const diags = &self.base.comp.link_diags;
1458 const target = self.getTarget();
1459 const dso = link.openDso(path, false, false, false) catch |err| {
1460 switch (diags.failParse(path, "failed to open shared object {}: {s}", .{ path, @errorName(err) })) {
1461 error.LinkFailure => return,
1462 }
1463 };
1464 const gpa = self.base.comp.gpa;
1465 parseDsoReportingFailure(gpa, diags, dso, &self.shared_objects, &self.files, target);
1466}
1467
1468fn parseDsoReportingFailure(
1469 gpa: Allocator,
1470 diags: *Diags,
1471 dso: link.Input.Dso,
1472 shared_objects: *std.StringArrayHashMapUnmanaged(File.Index),
1473 files: *std.MultiArrayList(File.Entry),
1474 target: std.Target,
1475) void {
1476 parseDso(gpa, diags, dso, shared_objects, files, target) catch |err| switch (err) {
1477 error.LinkFailure => return, // already reported
1478 error.BadMagic, error.UnexpectedEndOfFile => {
1479 var notes = diags.addErrorWithNotes(2) catch return diags.setAllocFailure();
1480 notes.addMsg("failed to parse shared object: {s}", .{@errorName(err)}) catch return diags.setAllocFailure();
1481 notes.addNote("while parsing {}", .{dso.path}) catch return diags.setAllocFailure();
1482 notes.addNote("{s}", .{@as([]const u8, "the file may be a GNU ld script, in which case it is not an ELF file but a text file referencing other libraries to link. In this case, avoid depending on the library, convince your system administrators to refrain from using this kind of file, or pass -fallow-so-scripts to force the compiler to check every shared library in case it is an ld script.")}) catch return diags.setAllocFailure();
1483 },
1484 else => |e| diags.addParseError(dso.path, "failed to parse shared object: {s}", .{@errorName(e)}),
1485 };
1486}
1487
1488fn parseDso(
1414 gpa: Allocator,1489 gpa: Allocator,
1415 diags: *Diags,1490 diags: *Diags,
1416 lib: SystemLib,1491 dso: link.Input.Dso,
1417 shared_objects: *std.StringArrayHashMapUnmanaged(File.Index),1492 shared_objects: *std.StringArrayHashMapUnmanaged(File.Index),
1418 files: *std.MultiArrayList(File.Entry),1493 files: *std.MultiArrayList(File.Entry),
1419 target: std.Target,1494 target: std.Target,
...@@ -1421,14 +1496,14 @@ fn parseSharedObject(...@@ -1421,14 +1496,14 @@ fn parseSharedObject(
1421 const tracy = trace(@src());1496 const tracy = trace(@src());
1422 defer tracy.end();1497 defer tracy.end();
14231498
1424 const handle = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{});1499 const handle = dso.file;
1425 defer handle.close();1500 defer handle.close();
14261501
1427 const stat = Stat.fromFs(try handle.stat());1502 const stat = Stat.fromFs(try handle.stat());
1428 var header = try SharedObject.parseHeader(gpa, diags, lib.path, handle, stat, target);1503 var header = try SharedObject.parseHeader(gpa, diags, dso.path, handle, stat, target);
1429 defer header.deinit(gpa);1504 defer header.deinit(gpa);
14301505
1431 const soname = header.soname() orelse lib.path.basename();1506 const soname = header.soname() orelse dso.path.basename();
14321507
1433 const gop = try shared_objects.getOrPut(gpa, soname);1508 const gop = try shared_objects.getOrPut(gpa, soname);
1434 if (gop.found_existing) {1509 if (gop.found_existing) {
...@@ -1446,8 +1521,8 @@ fn parseSharedObject(...@@ -1446,8 +1521,8 @@ fn parseSharedObject(
1446 errdefer parsed.deinit(gpa);1521 errdefer parsed.deinit(gpa);
14471522
1448 const duped_path: Path = .{1523 const duped_path: Path = .{
1449 .root_dir = lib.path.root_dir,1524 .root_dir = dso.path.root_dir,
1450 .sub_path = try gpa.dupe(u8, lib.path.sub_path),1525 .sub_path = try gpa.dupe(u8, dso.path.sub_path),
1451 };1526 };
1452 errdefer gpa.free(duped_path.sub_path);1527 errdefer gpa.free(duped_path.sub_path);
14531528
...@@ -1456,8 +1531,8 @@ fn parseSharedObject(...@@ -1456,8 +1531,8 @@ fn parseSharedObject(
1456 .parsed = parsed,1531 .parsed = parsed,
1457 .path = duped_path,1532 .path = duped_path,
1458 .index = index,1533 .index = index,
1459 .needed = lib.needed,1534 .needed = dso.needed,
1460 .alive = lib.needed,1535 .alive = dso.needed,
1461 .aliases = null,1536 .aliases = null,
1462 .symbols = .empty,1537 .symbols = .empty,
1463 .symbols_extra = .empty,1538 .symbols_extra = .empty,
...@@ -1824,11 +1899,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -1824,11 +1899,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
1824 try man.addOptionalFile(self.version_script);1899 try man.addOptionalFile(self.version_script);
1825 man.hash.add(self.allow_undefined_version);1900 man.hash.add(self.allow_undefined_version);
1826 man.hash.addOptional(self.enable_new_dtags);1901 man.hash.addOptional(self.enable_new_dtags);
1827 for (comp.objects) |obj| {1902 try link.hashInputs(&man, comp.link_inputs);
1828 _ = try man.addFilePath(obj.path, null);
1829 man.hash.add(obj.must_link);
1830 man.hash.add(obj.loption);
1831 }
1832 for (comp.c_object_table.keys()) |key| {1903 for (comp.c_object_table.keys()) |key| {
1833 _ = try man.addFilePath(key.status.success.object_path, null);1904 _ = try man.addFilePath(key.status.success.object_path, null);
1834 }1905 }
...@@ -1875,7 +1946,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -1875,7 +1946,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
1875 }1946 }
1876 man.hash.addOptionalBytes(self.soname);1947 man.hash.addOptionalBytes(self.soname);
1877 man.hash.addOptional(comp.version);1948 man.hash.addOptional(comp.version);
1878 try link.hashAddSystemLibs(&man, comp.system_libs);
1879 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());1949 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
1880 man.hash.add(self.base.allow_shlib_undefined);1950 man.hash.add(self.base.allow_shlib_undefined);
1881 man.hash.add(self.bind_global_refs_locally);1951 man.hash.add(self.bind_global_refs_locally);
...@@ -1922,8 +1992,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -1922,8 +1992,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
1922 // here. TODO: think carefully about how we can avoid this redundant operation when doing1992 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1923 // build-obj. See also the corresponding TODO in linkAsArchive.1993 // build-obj. See also the corresponding TODO in linkAsArchive.
1924 const the_object_path = blk: {1994 const the_object_path = blk: {
1925 if (comp.objects.len != 0)1995 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
1926 break :blk comp.objects[0].path;
19271996
1928 if (comp.c_object_table.count() != 0)1997 if (comp.c_object_table.count() != 0)
1929 break :blk comp.c_object_table.keys()[0].status.success.object_path;1998 break :blk comp.c_object_table.keys()[0].status.success.object_path;
...@@ -2178,21 +2247,26 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -2178,21 +2247,26 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
21782247
2179 // Positional arguments to the linker such as object files.2248 // Positional arguments to the linker such as object files.
2180 var whole_archive = false;2249 var whole_archive = false;
2181 for (comp.objects) |obj| {
2182 if (obj.must_link and !whole_archive) {
2183 try argv.append("-whole-archive");
2184 whole_archive = true;
2185 } else if (!obj.must_link and whole_archive) {
2186 try argv.append("-no-whole-archive");
2187 whole_archive = false;
2188 }
21892250
2190 if (obj.loption) {2251 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
2191 assert(obj.path.sub_path[0] == ':');2252 .res => unreachable, // Windows-only
2192 try argv.append("-l");2253 .dso => continue,
2193 }2254 .object, .archive => |obj| {
2194 try argv.append(try obj.path.toString(arena));2255 if (obj.must_link and !whole_archive) {
2195 }2256 try argv.append("-whole-archive");
2257 whole_archive = true;
2258 } else if (!obj.must_link and whole_archive) {
2259 try argv.append("-no-whole-archive");
2260 whole_archive = false;
2261 }
2262 try argv.append(try obj.path.toString(arena));
2263 },
2264 .dso_exact => |dso_exact| {
2265 assert(dso_exact.name[0] == ':');
2266 try argv.appendSlice(&.{ "-l", dso_exact.name });
2267 },
2268 };
2269
2196 if (whole_archive) {2270 if (whole_archive) {
2197 try argv.append("-no-whole-archive");2271 try argv.append("-no-whole-archive");
2198 whole_archive = false;2272 whole_archive = false;
...@@ -2228,35 +2302,35 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -2228,35 +2302,35 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
22282302
2229 // Shared libraries.2303 // Shared libraries.
2230 if (is_exe_or_dyn_lib) {2304 if (is_exe_or_dyn_lib) {
2231 const system_libs = comp.system_libs.keys();
2232 const system_libs_values = comp.system_libs.values();
2233
2234 // Worst-case, we need an --as-needed argument for every lib, as well2305 // Worst-case, we need an --as-needed argument for every lib, as well
2235 // as one before and one after.2306 // as one before and one after.
2236 try argv.ensureUnusedCapacity(system_libs.len * 2 + 2);2307 try argv.append("--as-needed");
2237 argv.appendAssumeCapacity("--as-needed");
2238 var as_needed = true;2308 var as_needed = true;
22392309
2240 for (system_libs_values) |lib_info| {2310 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
2241 const lib_as_needed = !lib_info.needed;2311 .res => unreachable, // Windows-only
2242 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {2312 .object, .archive, .dso_exact => continue,
2243 0b00, 0b11 => {},2313 .dso => |dso| {
2244 0b01 => {2314 const lib_as_needed = !dso.needed;
2245 argv.appendAssumeCapacity("--no-as-needed");2315 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
2246 as_needed = false;2316 0b00, 0b11 => {},
2247 },2317 0b01 => {
2248 0b10 => {2318 argv.appendAssumeCapacity("--no-as-needed");
2249 argv.appendAssumeCapacity("--as-needed");2319 as_needed = false;
2250 as_needed = true;2320 },
2251 },2321 0b10 => {
2252 }2322 argv.appendAssumeCapacity("--as-needed");
2323 as_needed = true;
2324 },
2325 }
22532326
2254 // By this time, we depend on these libs being dynamically linked2327 // By this time, we depend on these libs being dynamically linked
2255 // libraries and not static libraries (the check for that needs to be earlier),2328 // libraries and not static libraries (the check for that needs to be earlier),
2256 // but they could be full paths to .so files, in which case we2329 // but they could be full paths to .so files, in which case we
2257 // want to avoid prepending "-l".2330 // want to avoid prepending "-l".
2258 argv.appendAssumeCapacity(try lib_info.path.?.toString(arena));2331 argv.appendAssumeCapacity(try dso.path.toString(arena));
2259 }2332 },
2333 };
22602334
2261 if (!as_needed) {2335 if (!as_needed) {
2262 argv.appendAssumeCapacity("--as-needed");2336 argv.appendAssumeCapacity("--as-needed");
src/link/Elf/relocatable.zig+11-11
...@@ -2,13 +2,13 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path...@@ -2,13 +2,13 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path
2 const gpa = comp.gpa;2 const gpa = comp.gpa;
3 const diags = &comp.link_diags;3 const diags = &comp.link_diags;
44
5 for (comp.objects) |obj| {5 for (comp.link_inputs) |link_input| switch (link_input) {
6 switch (Compilation.classifyFileExt(obj.path.sub_path)) {6 .object => |obj| parseObjectStaticLibReportingFailure(elf_file, obj.path),
7 .object => parseObjectStaticLibReportingFailure(elf_file, obj.path),7 .archive => |obj| parseArchiveStaticLibReportingFailure(elf_file, obj.path),
8 .static_library => parseArchiveStaticLibReportingFailure(elf_file, obj.path),8 .dso_exact => unreachable,
9 else => diags.addParseError(obj.path, "unrecognized file extension", .{}),9 .res => unreachable,
10 }10 .dso => unreachable,
11 }11 };
1212
13 for (comp.c_object_table.keys()) |key| {13 for (comp.c_object_table.keys()) |key| {
14 parseObjectStaticLibReportingFailure(elf_file, key.status.success.object_path);14 parseObjectStaticLibReportingFailure(elf_file, key.status.success.object_path);
...@@ -153,18 +153,18 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path...@@ -153,18 +153,18 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path
153pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void {153pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void {
154 const diags = &comp.link_diags;154 const diags = &comp.link_diags;
155155
156 for (comp.objects) |obj| {156 for (comp.link_inputs) |link_input| {
157 elf_file.parseInputReportingFailure(obj.path, false, obj.must_link);157 elf_file.parseInputReportingFailure(link_input);
158 }158 }
159159
160 // This is a set of object files emitted by clang in a single `build-exe` invocation.160 // This is a set of object files emitted by clang in a single `build-exe` invocation.
161 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up161 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
162 // in this set.162 // in this set.
163 for (comp.c_object_table.keys()) |key| {163 for (comp.c_object_table.keys()) |key| {
164 elf_file.parseObjectReportingFailure(key.status.success.object_path);164 elf_file.openParseObjectReportingFailure(key.status.success.object_path);
165 }165 }
166166
167 if (module_obj_path) |path| elf_file.parseObjectReportingFailure(path);167 if (module_obj_path) |path| elf_file.openParseObjectReportingFailure(path);
168168
169 if (diags.hasErrors()) return error.FlushFailure;169 if (diags.hasErrors()) return error.FlushFailure;
170170
src/link/MachO.zig+108-73
...@@ -1,3 +1,7 @@...@@ -1,3 +1,7 @@
1pub const Atom = @import("MachO/Atom.zig");
2pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
3pub const Relocation = @import("MachO/Relocation.zig");
4
1base: link.File,5base: link.File,
26
3rpath_list: []const []const u8,7rpath_list: []const []const u8,
...@@ -114,8 +118,8 @@ headerpad_max_install_names: bool,...@@ -114,8 +118,8 @@ headerpad_max_install_names: bool,
114dead_strip_dylibs: bool,118dead_strip_dylibs: bool,
115/// Treatment of undefined symbols119/// Treatment of undefined symbols
116undefined_treatment: UndefinedTreatment,120undefined_treatment: UndefinedTreatment,
117/// Resolved list of library search directories121/// TODO: delete this, libraries need to be resolved by the frontend instead
118lib_dirs: []const []const u8,122lib_directories: []const Directory,
119/// Resolved list of framework search directories123/// Resolved list of framework search directories
120framework_dirs: []const []const u8,124framework_dirs: []const []const u8,
121/// List of input frameworks125/// List of input frameworks
...@@ -213,7 +217,8 @@ pub fn createEmpty(...@@ -213,7 +217,8 @@ pub fn createEmpty(
213 .platform = Platform.fromTarget(target),217 .platform = Platform.fromTarget(target),
214 .sdk_version = if (options.darwin_sdk_layout) |layout| inferSdkVersion(comp, layout) else null,218 .sdk_version = if (options.darwin_sdk_layout) |layout| inferSdkVersion(comp, layout) else null,
215 .undefined_treatment = if (allow_shlib_undefined) .dynamic_lookup else .@"error",219 .undefined_treatment = if (allow_shlib_undefined) .dynamic_lookup else .@"error",
216 .lib_dirs = options.lib_dirs,220 // TODO delete this, directories must instead be resolved by the frontend
221 .lib_directories = options.lib_directories,
217 .framework_dirs = options.framework_dirs,222 .framework_dirs = options.framework_dirs,
218 .force_load_objc = options.force_load_objc,223 .force_load_objc = options.force_load_objc,
219 };224 };
...@@ -371,48 +376,44 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -371,48 +376,44 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
371 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);376 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
372 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);377 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
373378
374 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);379 var positionals = std.ArrayList(link.Input).init(gpa);
375 defer positionals.deinit();380 defer positionals.deinit();
376381
377 try positionals.ensureUnusedCapacity(comp.objects.len);382 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
378 positionals.appendSliceAssumeCapacity(comp.objects);383
384 for (comp.link_inputs) |link_input| switch (link_input) {
385 .dso => continue, // handled below
386 .object, .archive => positionals.appendAssumeCapacity(link_input),
387 .dso_exact => @panic("TODO"),
388 .res => unreachable,
389 };
379390
380 // This is a set of object files emitted by clang in a single `build-exe` invocation.391 // This is a set of object files emitted by clang in a single `build-exe` invocation.
381 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up392 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
382 // in this set.393 // in this set.
383 try positionals.ensureUnusedCapacity(comp.c_object_table.keys().len);394 try positionals.ensureUnusedCapacity(comp.c_object_table.keys().len);
384 for (comp.c_object_table.keys()) |key| {395 for (comp.c_object_table.keys()) |key| {
385 positionals.appendAssumeCapacity(.{ .path = key.status.success.object_path });396 positionals.appendAssumeCapacity(try link.openObjectInput(diags, key.status.success.object_path));
386 }397 }
387398
388 if (module_obj_path) |path| try positionals.append(.{ .path = path });399 if (module_obj_path) |path| try positionals.append(try link.openObjectInput(diags, path));
389400
390 if (comp.config.any_sanitize_thread) {401 if (comp.config.any_sanitize_thread) {
391 try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path });402 try positionals.append(try link.openObjectInput(diags, comp.tsan_lib.?.full_object_path));
392 }403 }
393404
394 if (comp.config.any_fuzz) {405 if (comp.config.any_fuzz) {
395 try positionals.append(.{ .path = comp.fuzzer_lib.?.full_object_path });406 try positionals.append(try link.openObjectInput(diags, comp.fuzzer_lib.?.full_object_path));
396 }407 }
397408
398 for (positionals.items) |obj| {409 for (positionals.items) |link_input| {
399 self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err|410 self.classifyInputFile(link_input) catch |err|
400 diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)});411 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
401 }412 }
402413
403 var system_libs = std.ArrayList(SystemLib).init(gpa);414 var system_libs = std.ArrayList(SystemLib).init(gpa);
404 defer system_libs.deinit();415 defer system_libs.deinit();
405416
406 // libs
407 try system_libs.ensureUnusedCapacity(comp.system_libs.values().len);
408 for (comp.system_libs.values()) |info| {
409 system_libs.appendAssumeCapacity(.{
410 .needed = info.needed,
411 .weak = info.weak,
412 .path = info.path.?,
413 });
414 }
415
416 // frameworks417 // frameworks
417 try system_libs.ensureUnusedCapacity(self.frameworks.len);418 try system_libs.ensureUnusedCapacity(self.frameworks.len);
418 for (self.frameworks) |info| {419 for (self.frameworks) |info| {
...@@ -436,20 +437,24 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -436,20 +437,24 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
436 else => |e| return e, // TODO: convert into an error437 else => |e| return e, // TODO: convert into an error
437 };438 };
438439
439 for (system_libs.items) |lib| {440 for (comp.link_inputs) |link_input| switch (link_input) {
440 self.classifyInputFile(lib.path, lib, false) catch |err|441 .object, .archive, .dso_exact => continue,
441 diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)});442 .res => unreachable,
442 }443 .dso => {
444 self.classifyInputFile(link_input) catch |err|
445 diags.addParseError(link_input.path().?, "failed to parse input file: {s}", .{@errorName(err)});
446 },
447 };
443448
444 // Finally, link against compiler_rt.449 // Finally, link against compiler_rt.
445 const compiler_rt_path: ?Path = blk: {450 if (comp.compiler_rt_lib) |crt_file| {
446 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;451 const path = crt_file.full_object_path;
447 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;452 self.classifyInputFile(try link.openArchiveInput(diags, path)) catch |err|
448 break :blk null;453 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
449 };454 } else if (comp.compiler_rt_obj) |crt_file| {
450 if (compiler_rt_path) |path| {455 const path = crt_file.full_object_path;
451 self.classifyInputFile(path, .{ .path = path }, false) catch |err|456 self.classifyInputFile(try link.openObjectInput(diags, path)) catch |err|
452 diags.addParseError(path, "failed to parse input file: {s}", .{@errorName(err)});457 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
453 }458 }
454459
455 try self.parseInputFiles();460 try self.parseInputFiles();
...@@ -596,9 +601,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -596,9 +601,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
596 }601 }
597602
598 if (self.base.isRelocatable()) {603 if (self.base.isRelocatable()) {
599 for (comp.objects) |obj| {604 for (comp.link_inputs) |link_input| switch (link_input) {
600 try argv.append(try obj.path.toString(arena));605 .object, .archive => |obj| try argv.append(try obj.path.toString(arena)),
601 }606 .res => |res| try argv.append(try res.path.toString(arena)),
607 .dso => |dso| try argv.append(try dso.path.toString(arena)),
608 .dso_exact => |dso_exact| try argv.appendSlice(&.{ "-l", dso_exact.name }),
609 };
602610
603 for (comp.c_object_table.keys()) |key| {611 for (comp.c_object_table.keys()) |key| {
604 try argv.append(try key.status.success.object_path.toString(arena));612 try argv.append(try key.status.success.object_path.toString(arena));
...@@ -678,13 +686,15 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -678,13 +686,15 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
678 try argv.append("dynamic_lookup");686 try argv.append("dynamic_lookup");
679 }687 }
680688
681 for (comp.objects) |obj| {689 for (comp.link_inputs) |link_input| switch (link_input) {
682 // TODO: verify this690 .dso => continue, // handled below
683 if (obj.must_link) {691 .res => unreachable, // windows only
684 try argv.append("-force_load");692 .object, .archive => |obj| {
685 }693 if (obj.must_link) try argv.append("-force_load"); // TODO: verify this
686 try argv.append(try obj.path.toString(arena));694 try argv.append(try obj.path.toString(arena));
687 }695 },
696 .dso_exact => |dso_exact| try argv.appendSlice(&.{ "-l", dso_exact.name }),
697 };
688698
689 for (comp.c_object_table.keys()) |key| {699 for (comp.c_object_table.keys()) |key| {
690 try argv.append(try key.status.success.object_path.toString(arena));700 try argv.append(try key.status.success.object_path.toString(arena));
...@@ -703,21 +713,25 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -703,21 +713,25 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
703 try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));713 try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));
704 }714 }
705715
706 for (self.lib_dirs) |lib_dir| {716 for (self.lib_directories) |lib_directory| {
707 const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir});717 // TODO delete this, directories must instead be resolved by the frontend
718 const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_directory.path orelse "."});
708 try argv.append(arg);719 try argv.append(arg);
709 }720 }
710721
711 for (comp.system_libs.keys()) |l_name| {722 for (comp.link_inputs) |link_input| switch (link_input) {
712 const info = comp.system_libs.get(l_name).?;723 .object, .archive, .dso_exact => continue, // handled above
713 const arg = if (info.needed)724 .res => unreachable, // windows only
714 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})725 .dso => |dso| {
715 else if (info.weak)726 if (dso.needed) {
716 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})727 try argv.appendSlice(&.{ "-needed-l", try dso.path.toString(arena) });
717 else728 } else if (dso.weak) {
718 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});729 try argv.appendSlice(&.{ "-weak-l", try dso.path.toString(arena) });
719 try argv.append(arg);730 } else {
720 }731 try argv.appendSlice(&.{ "-l", try dso.path.toString(arena) });
732 }
733 },
734 };
721735
722 for (self.framework_dirs) |f_dir| {736 for (self.framework_dirs) |f_dir| {
723 try argv.append("-F");737 try argv.append("-F");
...@@ -751,6 +765,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -751,6 +765,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
751 Compilation.dump_argv(argv.items);765 Compilation.dump_argv(argv.items);
752}766}
753767
768/// TODO delete this, libsystem must be resolved when setting up the compilationt pipeline
754pub fn resolveLibSystem(769pub fn resolveLibSystem(
755 self: *MachO,770 self: *MachO,
756 arena: Allocator,771 arena: Allocator,
...@@ -774,8 +789,8 @@ pub fn resolveLibSystem(...@@ -774,8 +789,8 @@ pub fn resolveLibSystem(
774 },789 },
775 };790 };
776791
777 for (self.lib_dirs) |dir| {792 for (self.lib_directories) |directory| {
778 if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success;793 if (try accessLibPath(arena, &test_path, &checked_paths, directory.path orelse ".", "System")) break :success;
779 }794 }
780795
781 diags.addMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{});796 diags.addMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{});
...@@ -789,13 +804,14 @@ pub fn resolveLibSystem(...@@ -789,13 +804,14 @@ pub fn resolveLibSystem(
789 });804 });
790}805}
791806
792pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bool) !void {807pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
793 const tracy = trace(@src());808 const tracy = trace(@src());
794 defer tracy.end();809 defer tracy.end();
795810
811 const path, const file = input.pathAndFile().?;
812 // TODO don't classify now, it's too late. The input file has already been classified
796 log.debug("classifying input file {}", .{path});813 log.debug("classifying input file {}", .{path});
797814
798 const file = try path.root_dir.handle.openFile(path.sub_path, .{});
799 const fh = try self.addFileHandle(file);815 const fh = try self.addFileHandle(file);
800 var buffer: [Archive.SARMAG]u8 = undefined;816 var buffer: [Archive.SARMAG]u8 = undefined;
801817
...@@ -806,17 +822,17 @@ pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bo...@@ -806,17 +822,17 @@ pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bo
806 if (h.magic != macho.MH_MAGIC_64) break :blk;822 if (h.magic != macho.MH_MAGIC_64) break :blk;
807 switch (h.filetype) {823 switch (h.filetype) {
808 macho.MH_OBJECT => try self.addObject(path, fh, offset),824 macho.MH_OBJECT => try self.addObject(path, fh, offset),
809 macho.MH_DYLIB => _ = try self.addDylib(lib, true, fh, offset),825 macho.MH_DYLIB => _ = try self.addDylib(.fromLinkInput(input), true, fh, offset),
810 else => return error.UnknownFileType,826 else => return error.UnknownFileType,
811 }827 }
812 return;828 return;
813 }829 }
814 if (readArMagic(file, offset, &buffer) catch null) |ar_magic| blk: {830 if (readArMagic(file, offset, &buffer) catch null) |ar_magic| blk: {
815 if (!mem.eql(u8, ar_magic, Archive.ARMAG)) break :blk;831 if (!mem.eql(u8, ar_magic, Archive.ARMAG)) break :blk;
816 try self.addArchive(lib, must_link, fh, fat_arch);832 try self.addArchive(input.archive, fh, fat_arch);
817 return;833 return;
818 }834 }
819 _ = try self.addTbd(lib, true, fh);835 _ = try self.addTbd(.fromLinkInput(input), true, fh);
820}836}
821837
822fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch {838fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch {
...@@ -903,7 +919,7 @@ fn parseInputFileWorker(self: *MachO, file: File) void {...@@ -903,7 +919,7 @@ fn parseInputFileWorker(self: *MachO, file: File) void {
903 };919 };
904}920}
905921
906fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.HandleIndex, fat_arch: ?fat.Arch) !void {922fn addArchive(self: *MachO, lib: link.Input.Object, handle: File.HandleIndex, fat_arch: ?fat.Arch) !void {
907 const tracy = trace(@src());923 const tracy = trace(@src());
908 defer tracy.end();924 defer tracy.end();
909925
...@@ -918,7 +934,7 @@ fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.Handle...@@ -918,7 +934,7 @@ fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.Handle
918 self.files.set(index, .{ .object = unpacked });934 self.files.set(index, .{ .object = unpacked });
919 const object = &self.files.items(.data)[index].object;935 const object = &self.files.items(.data)[index].object;
920 object.index = index;936 object.index = index;
921 object.alive = must_link or lib.needed; // TODO: or self.options.all_load;937 object.alive = lib.must_link; // TODO: or self.options.all_load;
922 object.hidden = lib.hidden;938 object.hidden = lib.hidden;
923 try self.objects.append(gpa, index);939 try self.objects.append(gpa, index);
924 }940 }
...@@ -993,6 +1009,7 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {...@@ -993,6 +1009,7 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
993 return false;1009 return false;
994}1010}
9951011
1012/// TODO delete this, libraries must be instead resolved when instantiating the compilation pipeline
996fn accessLibPath(1013fn accessLibPath(
997 arena: Allocator,1014 arena: Allocator,
998 test_path: *std.ArrayList(u8),1015 test_path: *std.ArrayList(u8),
...@@ -1051,9 +1068,11 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1051,9 +1068,11 @@ fn parseDependentDylibs(self: *MachO) !void {
1051 if (self.dylibs.items.len == 0) return;1068 if (self.dylibs.items.len == 0) return;
10521069
1053 const gpa = self.base.comp.gpa;1070 const gpa = self.base.comp.gpa;
1054 const lib_dirs = self.lib_dirs;
1055 const framework_dirs = self.framework_dirs;1071 const framework_dirs = self.framework_dirs;
10561072
1073 // TODO delete this, directories must instead be resolved by the frontend
1074 const lib_directories = self.lib_directories;
1075
1057 var arena_alloc = std.heap.ArenaAllocator.init(gpa);1076 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
1058 defer arena_alloc.deinit();1077 defer arena_alloc.deinit();
1059 const arena = arena_alloc.allocator();1078 const arena = arena_alloc.allocator();
...@@ -1094,9 +1113,9 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1094,9 +1113,9 @@ fn parseDependentDylibs(self: *MachO) !void {
10941113
1095 // Library1114 // Library
1096 const lib_name = eatPrefix(stem, "lib") orelse stem;1115 const lib_name = eatPrefix(stem, "lib") orelse stem;
1097 for (lib_dirs) |dir| {1116 for (lib_directories) |lib_directory| {
1098 test_path.clearRetainingCapacity();1117 test_path.clearRetainingCapacity();
1099 if (try accessLibPath(arena, &test_path, &checked_paths, dir, lib_name)) break :full_path test_path.items;1118 if (try accessLibPath(arena, &test_path, &checked_paths, lib_directory.path orelse ".", lib_name)) break :full_path test_path.items;
1100 }1119 }
1101 }1120 }
11021121
...@@ -4366,6 +4385,24 @@ const SystemLib = struct {...@@ -4366,6 +4385,24 @@ const SystemLib = struct {
4366 hidden: bool = false,4385 hidden: bool = false,
4367 reexport: bool = false,4386 reexport: bool = false,
4368 must_link: bool = false,4387 must_link: bool = false,
4388
4389 fn fromLinkInput(link_input: link.Input) SystemLib {
4390 return switch (link_input) {
4391 .dso_exact => unreachable,
4392 .res => unreachable,
4393 .object, .archive => |obj| .{
4394 .path = obj.path,
4395 .must_link = obj.must_link,
4396 .hidden = obj.hidden,
4397 },
4398 .dso => |dso| .{
4399 .path = dso.path,
4400 .needed = dso.needed,
4401 .weak = dso.weak,
4402 .reexport = dso.reexport,
4403 },
4404 };
4405 }
4369};4406};
43704407
4371pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout;4408pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout;
...@@ -5303,17 +5340,16 @@ const Air = @import("../Air.zig");...@@ -5303,17 +5340,16 @@ const Air = @import("../Air.zig");
5303const Alignment = Atom.Alignment;5340const Alignment = Atom.Alignment;
5304const Allocator = mem.Allocator;5341const Allocator = mem.Allocator;
5305const Archive = @import("MachO/Archive.zig");5342const Archive = @import("MachO/Archive.zig");
5306pub const Atom = @import("MachO/Atom.zig");
5307const AtomicBool = std.atomic.Value(bool);5343const AtomicBool = std.atomic.Value(bool);
5308const Bind = bind.Bind;5344const Bind = bind.Bind;
5309const Cache = std.Build.Cache;5345const Cache = std.Build.Cache;
5310const Path = Cache.Path;
5311const CodeSignature = @import("MachO/CodeSignature.zig");5346const CodeSignature = @import("MachO/CodeSignature.zig");
5312const Compilation = @import("../Compilation.zig");5347const Compilation = @import("../Compilation.zig");
5313const DataInCode = synthetic.DataInCode;5348const DataInCode = synthetic.DataInCode;
5314pub const DebugSymbols = @import("MachO/DebugSymbols.zig");5349const Directory = Cache.Directory;
5315const Dylib = @import("MachO/Dylib.zig");5350const Dylib = @import("MachO/Dylib.zig");
5316const ExportTrie = @import("MachO/dyld_info/Trie.zig");5351const ExportTrie = @import("MachO/dyld_info/Trie.zig");
5352const Path = Cache.Path;
5317const File = @import("MachO/file.zig").File;5353const File = @import("MachO/file.zig").File;
5318const GotSection = synthetic.GotSection;5354const GotSection = synthetic.GotSection;
5319const Hash = std.hash.Wyhash;5355const Hash = std.hash.Wyhash;
...@@ -5329,7 +5365,6 @@ const Md5 = std.crypto.hash.Md5;...@@ -5329,7 +5365,6 @@ const Md5 = std.crypto.hash.Md5;
5329const Zcu = @import("../Zcu.zig");5365const Zcu = @import("../Zcu.zig");
5330const InternPool = @import("../InternPool.zig");5366const InternPool = @import("../InternPool.zig");
5331const Rebase = @import("MachO/dyld_info/Rebase.zig");5367const Rebase = @import("MachO/dyld_info/Rebase.zig");
5332pub const Relocation = @import("MachO/Relocation.zig");
5333const StringTable = @import("StringTable.zig");5368const StringTable = @import("StringTable.zig");
5334const StubsSection = synthetic.StubsSection;5369const StubsSection = synthetic.StubsSection;
5335const StubsHelperSection = synthetic.StubsHelperSection;5370const StubsHelperSection = synthetic.StubsHelperSection;
src/link/MachO/relocatable.zig+27-26
...@@ -3,16 +3,16 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -3,16 +3,16 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
3 const diags = &macho_file.base.comp.link_diags;3 const diags = &macho_file.base.comp.link_diags;
44
5 // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.5 // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.
6 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);6 var positionals = std.ArrayList(link.Input).init(gpa);
7 defer positionals.deinit();7 defer positionals.deinit();
8 try positionals.ensureUnusedCapacity(comp.objects.len);8 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
9 positionals.appendSliceAssumeCapacity(comp.objects);9 positionals.appendSliceAssumeCapacity(comp.link_inputs);
1010
11 for (comp.c_object_table.keys()) |key| {11 for (comp.c_object_table.keys()) |key| {
12 try positionals.append(.{ .path = key.status.success.object_path });12 try positionals.append(try link.openObjectInput(diags, key.status.success.object_path));
13 }13 }
1414
15 if (module_obj_path) |path| try positionals.append(.{ .path = path });15 if (module_obj_path) |path| try positionals.append(try link.openObjectInput(diags, path));
1616
17 if (macho_file.getZigObject() == null and positionals.items.len == 1) {17 if (macho_file.getZigObject() == null and positionals.items.len == 1) {
18 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all18 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all
...@@ -20,7 +20,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -20,7 +20,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
20 // the *only* input file over.20 // the *only* input file over.
21 // TODO: in the future, when we implement `dsymutil` alternative directly in the Zig21 // TODO: in the future, when we implement `dsymutil` alternative directly in the Zig
22 // compiler, investigate if we can get rid of this `if` prong here.22 // compiler, investigate if we can get rid of this `if` prong here.
23 const path = positionals.items[0].path;23 const path = positionals.items[0].path().?;
24 const in_file = try path.root_dir.handle.openFile(path.sub_path, .{});24 const in_file = try path.root_dir.handle.openFile(path.sub_path, .{});
25 const stat = try in_file.stat();25 const stat = try in_file.stat();
26 const amt = try in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size);26 const amt = try in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size);
...@@ -28,9 +28,9 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -28,9 +28,9 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
28 return;28 return;
29 }29 }
3030
31 for (positionals.items) |obj| {31 for (positionals.items) |link_input| {
32 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err|32 macho_file.classifyInputFile(link_input) catch |err|
33 diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)});33 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
34 }34 }
3535
36 if (diags.hasErrors()) return error.FlushFailure;36 if (diags.hasErrors()) return error.FlushFailure;
...@@ -72,25 +72,25 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -72,25 +72,25 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
72 const gpa = comp.gpa;72 const gpa = comp.gpa;
73 const diags = &macho_file.base.comp.link_diags;73 const diags = &macho_file.base.comp.link_diags;
7474
75 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);75 var positionals = std.ArrayList(link.Input).init(gpa);
76 defer positionals.deinit();76 defer positionals.deinit();
7777
78 try positionals.ensureUnusedCapacity(comp.objects.len);78 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
79 positionals.appendSliceAssumeCapacity(comp.objects);79 positionals.appendSliceAssumeCapacity(comp.link_inputs);
8080
81 for (comp.c_object_table.keys()) |key| {81 for (comp.c_object_table.keys()) |key| {
82 try positionals.append(.{ .path = key.status.success.object_path });82 try positionals.append(try link.openObjectInput(diags, key.status.success.object_path));
83 }83 }
8484
85 if (module_obj_path) |path| try positionals.append(.{ .path = path });85 if (module_obj_path) |path| try positionals.append(try link.openObjectInput(diags, path));
8686
87 if (comp.include_compiler_rt) {87 if (comp.include_compiler_rt) {
88 try positionals.append(.{ .path = comp.compiler_rt_obj.?.full_object_path });88 try positionals.append(try link.openObjectInput(diags, comp.compiler_rt_obj.?.full_object_path));
89 }89 }
9090
91 for (positionals.items) |obj| {91 for (positionals.items) |link_input| {
92 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err|92 macho_file.classifyInputFile(link_input) catch |err|
93 diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)});93 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
94 }94 }
9595
96 if (diags.hasErrors()) return error.FlushFailure;96 if (diags.hasErrors()) return error.FlushFailure;
...@@ -745,20 +745,15 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {...@@ -745,20 +745,15 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {
745 try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0);745 try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0);
746}746}
747747
748const std = @import("std");
749const Path = std.Build.Cache.Path;
750const WaitGroup = std.Thread.WaitGroup;
748const assert = std.debug.assert;751const assert = std.debug.assert;
749const build_options = @import("build_options");
750const eh_frame = @import("eh_frame.zig");
751const fat = @import("fat.zig");
752const link = @import("../../link.zig");
753const load_commands = @import("load_commands.zig");
754const log = std.log.scoped(.link);752const log = std.log.scoped(.link);
755const macho = std.macho;753const macho = std.macho;
756const math = std.math;754const math = std.math;
757const mem = std.mem;755const mem = std.mem;
758const state_log = std.log.scoped(.link_state);756const state_log = std.log.scoped(.link_state);
759const std = @import("std");
760const trace = @import("../../tracy.zig").trace;
761const Path = std.Build.Cache.Path;
762757
763const Archive = @import("Archive.zig");758const Archive = @import("Archive.zig");
764const Atom = @import("Atom.zig");759const Atom = @import("Atom.zig");
...@@ -767,3 +762,9 @@ const File = @import("file.zig").File;...@@ -767,3 +762,9 @@ const File = @import("file.zig").File;
767const MachO = @import("../MachO.zig");762const MachO = @import("../MachO.zig");
768const Object = @import("Object.zig");763const Object = @import("Object.zig");
769const Symbol = @import("Symbol.zig");764const Symbol = @import("Symbol.zig");
765const build_options = @import("build_options");
766const eh_frame = @import("eh_frame.zig");
767const fat = @import("fat.zig");
768const link = @import("../../link.zig");
769const load_commands = @import("load_commands.zig");
770const trace = @import("../../tracy.zig").trace;
src/link/Wasm.zig+31-29
...@@ -637,14 +637,6 @@ fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !...@@ -637,14 +637,6 @@ fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !
637 return loc;637 return loc;
638}638}
639639
640fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void {
641 for (files) |path| {
642 if (try wasm.parseObjectFile(path)) continue;
643 if (try wasm.parseArchive(path, false)) continue; // load archives lazily
644 log.warn("Unexpected file format at path: '{s}'", .{path});
645 }
646}
647
648/// Parses the object file from given path. Returns true when the given file was an object640/// Parses the object file from given path. Returns true when the given file was an object
649/// file and parsed successfully. Returns false when file is not an object file.641/// file and parsed successfully. Returns false when file is not an object file.
650/// May return an error instead when parsing failed.642/// May return an error instead when parsing failed.
...@@ -2522,7 +2514,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2522,7 +2514,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2522 // Positional arguments to the linker such as object files and static archives.2514 // Positional arguments to the linker such as object files and static archives.
2523 // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.2515 // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.
2524 var positionals = std.ArrayList([]const u8).init(arena);2516 var positionals = std.ArrayList([]const u8).init(arena);
2525 try positionals.ensureUnusedCapacity(comp.objects.len);2517 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
25262518
2527 const target = comp.root_mod.resolved_target.result;2519 const target = comp.root_mod.resolved_target.result;
2528 const output_mode = comp.config.output_mode;2520 const output_mode = comp.config.output_mode;
...@@ -2566,9 +2558,12 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2566,9 +2558,12 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2566 try positionals.append(path);2558 try positionals.append(path);
2567 }2559 }
25682560
2569 for (comp.objects) |object| {2561 for (comp.link_inputs) |link_input| switch (link_input) {
2570 try positionals.append(try object.path.toString(arena));2562 .object, .archive => |obj| try positionals.append(try obj.path.toString(arena)),
2571 }2563 .dso => |dso| try positionals.append(try dso.path.toString(arena)),
2564 .dso_exact => unreachable, // forbidden by frontend
2565 .res => unreachable, // windows only
2566 };
25722567
2573 for (comp.c_object_table.keys()) |c_object| {2568 for (comp.c_object_table.keys()) |c_object| {
2574 try positionals.append(try c_object.status.success.object_path.toString(arena));2569 try positionals.append(try c_object.status.success.object_path.toString(arena));
...@@ -2577,7 +2572,11 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2577,7 +2572,11 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2577 if (comp.compiler_rt_lib) |lib| try positionals.append(try lib.full_object_path.toString(arena));2572 if (comp.compiler_rt_lib) |lib| try positionals.append(try lib.full_object_path.toString(arena));
2578 if (comp.compiler_rt_obj) |obj| try positionals.append(try obj.full_object_path.toString(arena));2573 if (comp.compiler_rt_obj) |obj| try positionals.append(try obj.full_object_path.toString(arena));
25792574
2580 try wasm.parseInputFiles(positionals.items);2575 for (positionals.items) |path| {
2576 if (try wasm.parseObjectFile(path)) continue;
2577 if (try wasm.parseArchive(path, false)) continue; // load archives lazily
2578 log.warn("Unexpected file format at path: '{s}'", .{path});
2579 }
25812580
2582 if (wasm.zig_object_index != .null) {2581 if (wasm.zig_object_index != .null) {
2583 try wasm.resolveSymbolsInObject(wasm.zig_object_index);2582 try wasm.resolveSymbolsInObject(wasm.zig_object_index);
...@@ -3401,10 +3400,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3401,10 +3400,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
34013400
3402 comptime assert(Compilation.link_hash_implementation_version == 14);3401 comptime assert(Compilation.link_hash_implementation_version == 14);
34033402
3404 for (comp.objects) |obj| {3403 try link.hashInputs(&man, comp.link_inputs);
3405 _ = try man.addFilePath(obj.path, null);
3406 man.hash.add(obj.must_link);
3407 }
3408 for (comp.c_object_table.keys()) |key| {3404 for (comp.c_object_table.keys()) |key| {
3409 _ = try man.addFilePath(key.status.success.object_path, null);3405 _ = try man.addFilePath(key.status.success.object_path, null);
3410 }3406 }
...@@ -3458,8 +3454,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3458,8 +3454,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
3458 // here. TODO: think carefully about how we can avoid this redundant operation when doing3454 // here. TODO: think carefully about how we can avoid this redundant operation when doing
3459 // build-obj. See also the corresponding TODO in linkAsArchive.3455 // build-obj. See also the corresponding TODO in linkAsArchive.
3460 const the_object_path = blk: {3456 const the_object_path = blk: {
3461 if (comp.objects.len != 0)3457 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
3462 break :blk comp.objects[0].path;
34633458
3464 if (comp.c_object_table.count() != 0)3459 if (comp.c_object_table.count() != 0)
3465 break :blk comp.c_object_table.keys()[0].status.success.object_path;3460 break :blk comp.c_object_table.keys()[0].status.success.object_path;
...@@ -3621,16 +3616,23 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3621,16 +3616,23 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
36213616
3622 // Positional arguments to the linker such as object files.3617 // Positional arguments to the linker such as object files.
3623 var whole_archive = false;3618 var whole_archive = false;
3624 for (comp.objects) |obj| {3619 for (comp.link_inputs) |link_input| switch (link_input) {
3625 if (obj.must_link and !whole_archive) {3620 .object, .archive => |obj| {
3626 try argv.append("-whole-archive");3621 if (obj.must_link and !whole_archive) {
3627 whole_archive = true;3622 try argv.append("-whole-archive");
3628 } else if (!obj.must_link and whole_archive) {3623 whole_archive = true;
3629 try argv.append("-no-whole-archive");3624 } else if (!obj.must_link and whole_archive) {
3630 whole_archive = false;3625 try argv.append("-no-whole-archive");
3631 }3626 whole_archive = false;
3632 try argv.append(try obj.path.toString(arena));3627 }
3633 }3628 try argv.append(try obj.path.toString(arena));
3629 },
3630 .dso => |dso| {
3631 try argv.append(try dso.path.toString(arena));
3632 },
3633 .dso_exact => unreachable,
3634 .res => unreachable,
3635 };
3634 if (whole_archive) {3636 if (whole_archive) {
3635 try argv.append("-no-whole-archive");3637 try argv.append("-no-whole-archive");
3636 whole_archive = false;3638 whole_archive = false;
src/main.zig+319-638
...@@ -15,6 +15,7 @@ const cleanExit = std.process.cleanExit;...@@ -15,6 +15,7 @@ const cleanExit = std.process.cleanExit;
15const native_os = builtin.os.tag;15const native_os = builtin.os.tag;
16const Cache = std.Build.Cache;16const Cache = std.Build.Cache;
17const Path = std.Build.Cache.Path;17const Path = std.Build.Cache.Path;
18const Directory = std.Build.Cache.Directory;
18const EnvVar = std.zig.EnvVar;19const EnvVar = std.zig.EnvVar;
19const LibCInstallation = std.zig.LibCInstallation;20const LibCInstallation = std.zig.LibCInstallation;
20const AstGen = std.zig.AstGen;21const AstGen = std.zig.AstGen;
...@@ -55,7 +56,7 @@ pub fn wasi_cwd() std.os.wasi.fd_t {...@@ -55,7 +56,7 @@ pub fn wasi_cwd() std.os.wasi.fd_t {
55 return cwd_fd;56 return cwd_fd;
56}57}
5758
58fn getWasiPreopen(name: []const u8) Compilation.Directory {59fn getWasiPreopen(name: []const u8) Directory {
59 return .{60 return .{
60 .path = name,61 .path = name,
61 .handle = .{62 .handle = .{
...@@ -768,27 +769,6 @@ const ArgsIterator = struct {...@@ -768,27 +769,6 @@ const ArgsIterator = struct {
768 }769 }
769};770};
770771
771/// In contrast to `link.SystemLib`, this stores arguments that may need to be
772/// resolved into static libraries so that we can pass only dynamic libraries
773/// as system libs to `Compilation`.
774const SystemLib = struct {
775 needed: bool,
776 weak: bool,
777
778 preferred_mode: std.builtin.LinkMode,
779 search_strategy: SearchStrategy,
780
781 const SearchStrategy = enum { paths_first, mode_first, no_fallback };
782
783 fn fallbackMode(this: SystemLib) std.builtin.LinkMode {
784 assert(this.search_strategy != .no_fallback);
785 return switch (this.preferred_mode) {
786 .dynamic => .static,
787 .static => .dynamic,
788 };
789 }
790};
791
792/// Similar to `link.Framework` except it doesn't store yet unresolved772/// Similar to `link.Framework` except it doesn't store yet unresolved
793/// path to the framework.773/// path to the framework.
794const Framework = struct {774const Framework = struct {
...@@ -869,6 +849,7 @@ fn buildOutputType(...@@ -869,6 +849,7 @@ fn buildOutputType(
869 var linker_gc_sections: ?bool = null;849 var linker_gc_sections: ?bool = null;
870 var linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null;850 var linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null;
871 var linker_allow_shlib_undefined: ?bool = null;851 var linker_allow_shlib_undefined: ?bool = null;
852 var allow_so_scripts: bool = false;
872 var linker_bind_global_refs_locally: ?bool = null;853 var linker_bind_global_refs_locally: ?bool = null;
873 var linker_import_symbols: bool = false;854 var linker_import_symbols: bool = false;
874 var linker_import_table: bool = false;855 var linker_import_table: bool = false;
...@@ -921,7 +902,7 @@ fn buildOutputType(...@@ -921,7 +902,7 @@ fn buildOutputType(
921 var hash_style: link.File.Elf.HashStyle = .both;902 var hash_style: link.File.Elf.HashStyle = .both;
922 var entitlements: ?[]const u8 = null;903 var entitlements: ?[]const u8 = null;
923 var pagezero_size: ?u64 = null;904 var pagezero_size: ?u64 = null;
924 var lib_search_strategy: SystemLib.SearchStrategy = .paths_first;905 var lib_search_strategy: link.UnresolvedInput.SearchStrategy = .paths_first;
925 var lib_preferred_mode: std.builtin.LinkMode = .dynamic;906 var lib_preferred_mode: std.builtin.LinkMode = .dynamic;
926 var headerpad_size: ?u32 = null;907 var headerpad_size: ?u32 = null;
927 var headerpad_max_install_names: bool = false;908 var headerpad_max_install_names: bool = false;
...@@ -985,8 +966,10 @@ fn buildOutputType(...@@ -985,8 +966,10 @@ fn buildOutputType(
985 // Populated in the call to `createModule` for the root module.966 // Populated in the call to `createModule` for the root module.
986 .resolved_options = undefined,967 .resolved_options = undefined,
987968
988 .system_libs = .{},969 .cli_link_inputs = .empty,
989 .resolved_system_libs = .{},970 .windows_libs = .empty,
971 .link_inputs = .empty,
972
990 .wasi_emulated_libs = .{},973 .wasi_emulated_libs = .{},
991974
992 .c_source_files = .{},975 .c_source_files = .{},
...@@ -994,7 +977,7 @@ fn buildOutputType(...@@ -994,7 +977,7 @@ fn buildOutputType(
994977
995 .llvm_m_args = .{},978 .llvm_m_args = .{},
996 .sysroot = null,979 .sysroot = null,
997 .lib_dirs = .{}, // populated by createModule()980 .lib_directories = .{}, // populated by createModule()
998 .lib_dir_args = .{}, // populated from CLI arg parsing981 .lib_dir_args = .{}, // populated from CLI arg parsing
999 .libc_installation = null,982 .libc_installation = null,
1000 .want_native_include_dirs = false,983 .want_native_include_dirs = false,
...@@ -1003,9 +986,7 @@ fn buildOutputType(...@@ -1003,9 +986,7 @@ fn buildOutputType(
1003 .rpath_list = .{},986 .rpath_list = .{},
1004 .each_lib_rpath = null,987 .each_lib_rpath = null,
1005 .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena),988 .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena),
1006 .link_objects = .{},
1007 .native_system_include_paths = &.{},989 .native_system_include_paths = &.{},
1008 .allow_so_scripts = false,
1009 };990 };
1010991
1011 // before arg parsing, check for the NO_COLOR and CLICOLOR_FORCE environment variables992 // before arg parsing, check for the NO_COLOR and CLICOLOR_FORCE environment variables
...@@ -1240,30 +1221,42 @@ fn buildOutputType(...@@ -1240,30 +1221,42 @@ fn buildOutputType(
1240 // We don't know whether this library is part of libc1221 // We don't know whether this library is part of libc
1241 // or libc++ until we resolve the target, so we append1222 // or libc++ until we resolve the target, so we append
1242 // to the list for now.1223 // to the list for now.
1243 try create_module.system_libs.put(arena, args_iter.nextOrFatal(), .{1224 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1244 .needed = false,1225 .name = args_iter.nextOrFatal(),
1245 .weak = false,1226 .query = .{
1246 .preferred_mode = lib_preferred_mode,1227 .needed = false,
1247 .search_strategy = lib_search_strategy,1228 .weak = false,
1248 });1229 .preferred_mode = lib_preferred_mode,
1230 .search_strategy = lib_search_strategy,
1231 .allow_so_scripts = allow_so_scripts,
1232 },
1233 } });
1249 } else if (mem.eql(u8, arg, "--needed-library") or1234 } else if (mem.eql(u8, arg, "--needed-library") or
1250 mem.eql(u8, arg, "-needed-l") or1235 mem.eql(u8, arg, "-needed-l") or
1251 mem.eql(u8, arg, "-needed_library"))1236 mem.eql(u8, arg, "-needed_library"))
1252 {1237 {
1253 const next_arg = args_iter.nextOrFatal();1238 const next_arg = args_iter.nextOrFatal();
1254 try create_module.system_libs.put(arena, next_arg, .{1239 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1255 .needed = true,1240 .name = next_arg,
1256 .weak = false,1241 .query = .{
1257 .preferred_mode = lib_preferred_mode,1242 .needed = true,
1258 .search_strategy = lib_search_strategy,1243 .weak = false,
1259 });1244 .preferred_mode = lib_preferred_mode,
1245 .search_strategy = lib_search_strategy,
1246 .allow_so_scripts = allow_so_scripts,
1247 },
1248 } });
1260 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {1249 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
1261 try create_module.system_libs.put(arena, args_iter.nextOrFatal(), .{1250 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1262 .needed = false,1251 .name = args_iter.nextOrFatal(),
1263 .weak = true,1252 .query = .{
1264 .preferred_mode = lib_preferred_mode,1253 .needed = false,
1265 .search_strategy = lib_search_strategy,1254 .weak = true,
1266 });1255 .preferred_mode = lib_preferred_mode,
1256 .search_strategy = lib_search_strategy,
1257 .allow_so_scripts = allow_so_scripts,
1258 },
1259 } });
1267 } else if (mem.eql(u8, arg, "-D")) {1260 } else if (mem.eql(u8, arg, "-D")) {
1268 try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });1261 try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });
1269 } else if (mem.eql(u8, arg, "-I")) {1262 } else if (mem.eql(u8, arg, "-I")) {
...@@ -1577,9 +1570,9 @@ fn buildOutputType(...@@ -1577,9 +1570,9 @@ fn buildOutputType(
1577 } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) {1570 } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) {
1578 linker_allow_shlib_undefined = false;1571 linker_allow_shlib_undefined = false;
1579 } else if (mem.eql(u8, arg, "-fallow-so-scripts")) {1572 } else if (mem.eql(u8, arg, "-fallow-so-scripts")) {
1580 create_module.allow_so_scripts = true;1573 allow_so_scripts = true;
1581 } else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {1574 } else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {
1582 create_module.allow_so_scripts = false;1575 allow_so_scripts = false;
1583 } else if (mem.eql(u8, arg, "-z")) {1576 } else if (mem.eql(u8, arg, "-z")) {
1584 const z_arg = args_iter.nextOrFatal();1577 const z_arg = args_iter.nextOrFatal();
1585 if (mem.eql(u8, z_arg, "nodelete")) {1578 if (mem.eql(u8, z_arg, "nodelete")) {
...@@ -1687,26 +1680,38 @@ fn buildOutputType(...@@ -1687,26 +1680,38 @@ fn buildOutputType(
1687 // We don't know whether this library is part of libc1680 // We don't know whether this library is part of libc
1688 // or libc++ until we resolve the target, so we append1681 // or libc++ until we resolve the target, so we append
1689 // to the list for now.1682 // to the list for now.
1690 try create_module.system_libs.put(arena, arg["-l".len..], .{1683 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1691 .needed = false,1684 .name = arg["-l".len..],
1692 .weak = false,1685 .query = .{
1693 .preferred_mode = lib_preferred_mode,1686 .needed = false,
1694 .search_strategy = lib_search_strategy,1687 .weak = false,
1695 });1688 .preferred_mode = lib_preferred_mode,
1689 .search_strategy = lib_search_strategy,
1690 .allow_so_scripts = allow_so_scripts,
1691 },
1692 } });
1696 } else if (mem.startsWith(u8, arg, "-needed-l")) {1693 } else if (mem.startsWith(u8, arg, "-needed-l")) {
1697 try create_module.system_libs.put(arena, arg["-needed-l".len..], .{1694 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1698 .needed = true,1695 .name = arg["-needed-l".len..],
1699 .weak = false,1696 .query = .{
1700 .preferred_mode = lib_preferred_mode,1697 .needed = true,
1701 .search_strategy = lib_search_strategy,1698 .weak = false,
1702 });1699 .preferred_mode = lib_preferred_mode,
1700 .search_strategy = lib_search_strategy,
1701 .allow_so_scripts = allow_so_scripts,
1702 },
1703 } });
1703 } else if (mem.startsWith(u8, arg, "-weak-l")) {1704 } else if (mem.startsWith(u8, arg, "-weak-l")) {
1704 try create_module.system_libs.put(arena, arg["-weak-l".len..], .{1705 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1705 .needed = false,1706 .name = arg["-weak-l".len..],
1706 .weak = true,1707 .query = .{
1707 .preferred_mode = lib_preferred_mode,1708 .needed = false,
1708 .search_strategy = lib_search_strategy,1709 .weak = true,
1709 });1710 .preferred_mode = lib_preferred_mode,
1711 .search_strategy = lib_search_strategy,
1712 .allow_so_scripts = allow_so_scripts,
1713 },
1714 } });
1710 } else if (mem.startsWith(u8, arg, "-D")) {1715 } else if (mem.startsWith(u8, arg, "-D")) {
1711 try cc_argv.append(arena, arg);1716 try cc_argv.append(arena, arg);
1712 } else if (mem.startsWith(u8, arg, "-I")) {1717 } else if (mem.startsWith(u8, arg, "-I")) {
...@@ -1731,15 +1736,28 @@ fn buildOutputType(...@@ -1731,15 +1736,28 @@ fn buildOutputType(
1731 fatal("unrecognized parameter: '{s}'", .{arg});1736 fatal("unrecognized parameter: '{s}'", .{arg});
1732 }1737 }
1733 } else switch (file_ext orelse Compilation.classifyFileExt(arg)) {1738 } else switch (file_ext orelse Compilation.classifyFileExt(arg)) {
1734 .shared_library => {1739 .shared_library, .object, .static_library => {
1735 try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) });1740 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1736 create_module.opts.any_dyn_libs = true;1741 .path = Path.initCwd(arg),
1737 },1742 .query = .{
1738 .object, .static_library => {1743 .preferred_mode = lib_preferred_mode,
1739 try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) });1744 .search_strategy = lib_search_strategy,
1745 .allow_so_scripts = allow_so_scripts,
1746 },
1747 } });
1748 // We do not set `any_dyn_libs` yet because a .so file
1749 // may actually resolve to a GNU ld script which ends
1750 // up being a static library.
1740 },1751 },
1741 .res => {1752 .res => {
1742 try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) });1753 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1754 .path = Path.initCwd(arg),
1755 .query = .{
1756 .preferred_mode = lib_preferred_mode,
1757 .search_strategy = lib_search_strategy,
1758 .allow_so_scripts = allow_so_scripts,
1759 },
1760 } });
1743 contains_res_file = true;1761 contains_res_file = true;
1744 },1762 },
1745 .manifest => {1763 .manifest => {
...@@ -1792,6 +1810,7 @@ fn buildOutputType(...@@ -1792,6 +1810,7 @@ fn buildOutputType(
1792 // some functionality that depend on it, such as C++ exceptions and1810 // some functionality that depend on it, such as C++ exceptions and
1793 // DWARF-based stack traces.1811 // DWARF-based stack traces.
1794 link_eh_frame_hdr = true;1812 link_eh_frame_hdr = true;
1813 allow_so_scripts = true;
17951814
1796 const COutMode = enum {1815 const COutMode = enum {
1797 link,1816 link,
...@@ -1851,24 +1870,32 @@ fn buildOutputType(...@@ -1851,24 +1870,32 @@ fn buildOutputType(
1851 .ext = file_ext, // duped while parsing the args.1870 .ext = file_ext, // duped while parsing the args.
1852 });1871 });
1853 },1872 },
1854 .shared_library => {1873 .unknown, .object, .static_library, .shared_library => {
1855 try create_module.link_objects.append(arena, .{1874 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1856 .path = Path.initCwd(it.only_arg),
1857 .must_link = must_link,
1858 });
1859 create_module.opts.any_dyn_libs = true;
1860 },
1861 .unknown, .object, .static_library => {
1862 try create_module.link_objects.append(arena, .{
1863 .path = Path.initCwd(it.only_arg),1875 .path = Path.initCwd(it.only_arg),
1864 .must_link = must_link,1876 .query = .{
1865 });1877 .must_link = must_link,
1878 .needed = needed,
1879 .preferred_mode = lib_preferred_mode,
1880 .search_strategy = lib_search_strategy,
1881 .allow_so_scripts = allow_so_scripts,
1882 },
1883 } });
1884 // We do not set `any_dyn_libs` yet because a .so file
1885 // may actually resolve to a GNU ld script which ends
1886 // up being a static library.
1866 },1887 },
1867 .res => {1888 .res => {
1868 try create_module.link_objects.append(arena, .{1889 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1869 .path = Path.initCwd(it.only_arg),1890 .path = Path.initCwd(it.only_arg),
1870 .must_link = must_link,1891 .query = .{
1871 });1892 .must_link = must_link,
1893 .needed = needed,
1894 .preferred_mode = lib_preferred_mode,
1895 .search_strategy = lib_search_strategy,
1896 .allow_so_scripts = allow_so_scripts,
1897 },
1898 } });
1872 contains_res_file = true;1899 contains_res_file = true;
1873 },1900 },
1874 .manifest => {1901 .manifest => {
...@@ -1900,19 +1927,21 @@ fn buildOutputType(...@@ -1900,19 +1927,21 @@ fn buildOutputType(
1900 // -l :path/to/filename is used when callers need1927 // -l :path/to/filename is used when callers need
1901 // more control over what's in the resulting1928 // more control over what's in the resulting
1902 // binary: no extra rpaths and DSO filename exactly1929 // binary: no extra rpaths and DSO filename exactly
1903 // as provided. Hello, Go.1930 // as provided. CGo compilation depends on this.
1904 try create_module.link_objects.append(arena, .{1931 try create_module.cli_link_inputs.append(arena, .{ .dso_exact = .{
1905 .path = Path.initCwd(it.only_arg),1932 .name = it.only_arg,
1906 .must_link = must_link,1933 } });
1907 .loption = true,
1908 });
1909 } else {1934 } else {
1910 try create_module.system_libs.put(arena, it.only_arg, .{1935 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1911 .needed = needed,1936 .name = it.only_arg,
1912 .weak = false,1937 .query = .{
1913 .preferred_mode = lib_preferred_mode,1938 .needed = needed,
1914 .search_strategy = lib_search_strategy,1939 .weak = false,
1915 });1940 .preferred_mode = lib_preferred_mode,
1941 .search_strategy = lib_search_strategy,
1942 .allow_so_scripts = allow_so_scripts,
1943 },
1944 } });
1916 }1945 }
1917 },1946 },
1918 .ignore => {},1947 .ignore => {},
...@@ -2181,12 +2210,16 @@ fn buildOutputType(...@@ -2181,12 +2210,16 @@ fn buildOutputType(
2181 },2210 },
2182 .force_load_objc => force_load_objc = true,2211 .force_load_objc => force_load_objc = true,
2183 .mingw_unicode_entry_point => mingw_unicode_entry_point = true,2212 .mingw_unicode_entry_point => mingw_unicode_entry_point = true,
2184 .weak_library => try create_module.system_libs.put(arena, it.only_arg, .{2213 .weak_library => try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2185 .needed = false,2214 .name = it.only_arg,
2186 .weak = true,2215 .query = .{
2187 .preferred_mode = lib_preferred_mode,2216 .needed = false,
2188 .search_strategy = lib_search_strategy,2217 .weak = true,
2189 }),2218 .preferred_mode = lib_preferred_mode,
2219 .search_strategy = lib_search_strategy,
2220 .allow_so_scripts = allow_so_scripts,
2221 },
2222 } }),
2190 .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),2223 .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),
2191 .headerpad_max_install_names => headerpad_max_install_names = true,2224 .headerpad_max_install_names => headerpad_max_install_names = true,
2192 .compress_debug_sections => {2225 .compress_debug_sections => {
...@@ -2489,26 +2522,38 @@ fn buildOutputType(...@@ -2489,26 +2522,38 @@ fn buildOutputType(
2489 } else if (mem.eql(u8, arg, "-needed_framework")) {2522 } else if (mem.eql(u8, arg, "-needed_framework")) {
2490 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true });2523 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true });
2491 } else if (mem.eql(u8, arg, "-needed_library")) {2524 } else if (mem.eql(u8, arg, "-needed_library")) {
2492 try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{2525 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2493 .weak = false,2526 .name = linker_args_it.nextOrFatal(),
2494 .needed = true,2527 .query = .{
2495 .preferred_mode = lib_preferred_mode,2528 .weak = false,
2496 .search_strategy = lib_search_strategy,2529 .needed = true,
2497 });2530 .preferred_mode = lib_preferred_mode,
2531 .search_strategy = lib_search_strategy,
2532 .allow_so_scripts = allow_so_scripts,
2533 },
2534 } });
2498 } else if (mem.startsWith(u8, arg, "-weak-l")) {2535 } else if (mem.startsWith(u8, arg, "-weak-l")) {
2499 try create_module.system_libs.put(arena, arg["-weak-l".len..], .{2536 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2500 .weak = true,2537 .name = arg["-weak-l".len..],
2501 .needed = false,2538 .query = .{
2502 .preferred_mode = lib_preferred_mode,2539 .weak = true,
2503 .search_strategy = lib_search_strategy,2540 .needed = false,
2504 });2541 .preferred_mode = lib_preferred_mode,
2542 .search_strategy = lib_search_strategy,
2543 .allow_so_scripts = allow_so_scripts,
2544 },
2545 } });
2505 } else if (mem.eql(u8, arg, "-weak_library")) {2546 } else if (mem.eql(u8, arg, "-weak_library")) {
2506 try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{2547 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2507 .weak = true,2548 .name = linker_args_it.nextOrFatal(),
2508 .needed = false,2549 .query = .{
2509 .preferred_mode = lib_preferred_mode,2550 .weak = true,
2510 .search_strategy = lib_search_strategy,2551 .needed = false,
2511 });2552 .preferred_mode = lib_preferred_mode,
2553 .search_strategy = lib_search_strategy,
2554 .allow_so_scripts = allow_so_scripts,
2555 },
2556 } });
2512 } else if (mem.eql(u8, arg, "-compatibility_version")) {2557 } else if (mem.eql(u8, arg, "-compatibility_version")) {
2513 const compat_version = linker_args_it.nextOrFatal();2558 const compat_version = linker_args_it.nextOrFatal();
2514 compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {2559 compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {
...@@ -2539,10 +2584,14 @@ fn buildOutputType(...@@ -2539,10 +2584,14 @@ fn buildOutputType(
2539 } else if (mem.eql(u8, arg, "-install_name")) {2584 } else if (mem.eql(u8, arg, "-install_name")) {
2540 install_name = linker_args_it.nextOrFatal();2585 install_name = linker_args_it.nextOrFatal();
2541 } else if (mem.eql(u8, arg, "-force_load")) {2586 } else if (mem.eql(u8, arg, "-force_load")) {
2542 try create_module.link_objects.append(arena, .{2587 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
2543 .path = Path.initCwd(linker_args_it.nextOrFatal()),2588 .path = Path.initCwd(linker_args_it.nextOrFatal()),
2544 .must_link = true,2589 .query = .{
2545 });2590 .must_link = true,
2591 .preferred_mode = .static,
2592 .search_strategy = .no_fallback,
2593 },
2594 } });
2546 } else if (mem.eql(u8, arg, "-hash-style") or2595 } else if (mem.eql(u8, arg, "-hash-style") or
2547 mem.eql(u8, arg, "--hash-style"))2596 mem.eql(u8, arg, "--hash-style"))
2548 {2597 {
...@@ -2672,7 +2721,7 @@ fn buildOutputType(...@@ -2672,7 +2721,7 @@ fn buildOutputType(
2672 },2721 },
2673 }2722 }
2674 if (create_module.c_source_files.items.len == 0 and2723 if (create_module.c_source_files.items.len == 0 and
2675 create_module.link_objects.items.len == 0 and2724 !link.anyObjectInputs(create_module.link_inputs.items) and
2676 root_src_file == null)2725 root_src_file == null)
2677 {2726 {
2678 // For example `zig cc` and no args should print the "no input files" message.2727 // For example `zig cc` and no args should print the "no input files" message.
...@@ -2714,8 +2763,9 @@ fn buildOutputType(...@@ -2714,8 +2763,9 @@ fn buildOutputType(
2714 if (create_module.c_source_files.items.len >= 1)2763 if (create_module.c_source_files.items.len >= 1)
2715 break :b create_module.c_source_files.items[0].src_path;2764 break :b create_module.c_source_files.items[0].src_path;
27162765
2717 if (create_module.link_objects.items.len >= 1)2766 for (create_module.link_inputs.items) |link_input| {
2718 break :b create_module.link_objects.items[0].path.sub_path;2767 if (link_input.path()) |path| break :b path.sub_path;
2768 }
27192769
2720 if (emit_bin == .yes)2770 if (emit_bin == .yes)
2721 break :b emit_bin.yes;2771 break :b emit_bin.yes;
...@@ -2801,7 +2851,7 @@ fn buildOutputType(...@@ -2801,7 +2851,7 @@ fn buildOutputType(
2801 fatal("unable to find zig self exe path: {s}", .{@errorName(err)});2851 fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
2802 };2852 };
28032853
2804 var zig_lib_directory: Compilation.Directory = d: {2854 var zig_lib_directory: Directory = d: {
2805 if (override_lib_dir) |unresolved_lib_dir| {2855 if (override_lib_dir) |unresolved_lib_dir| {
2806 const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);2856 const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
2807 break :d .{2857 break :d .{
...@@ -2822,7 +2872,7 @@ fn buildOutputType(...@@ -2822,7 +2872,7 @@ fn buildOutputType(
2822 };2872 };
2823 defer zig_lib_directory.handle.close();2873 defer zig_lib_directory.handle.close();
28242874
2825 var global_cache_directory: Compilation.Directory = l: {2875 var global_cache_directory: Directory = l: {
2826 if (override_global_cache_dir) |p| {2876 if (override_global_cache_dir) |p| {
2827 break :l .{2877 break :l .{
2828 .handle = try fs.cwd().makeOpenPath(p, .{}),2878 .handle = try fs.cwd().makeOpenPath(p, .{}),
...@@ -2852,7 +2902,7 @@ fn buildOutputType(...@@ -2852,7 +2902,7 @@ fn buildOutputType(
28522902
2853 var builtin_modules: std.StringHashMapUnmanaged(*Package.Module) = .empty;2903 var builtin_modules: std.StringHashMapUnmanaged(*Package.Module) = .empty;
2854 // `builtin_modules` allocated into `arena`, so no deinit2904 // `builtin_modules` allocated into `arena`, so no deinit
2855 const main_mod = try createModule(gpa, arena, &create_module, 0, null, zig_lib_directory, &builtin_modules);2905 const main_mod = try createModule(gpa, arena, &create_module, 0, null, zig_lib_directory, &builtin_modules, color);
2856 for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {2906 for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {
2857 if (cli_mod.resolved == null)2907 if (cli_mod.resolved == null)
2858 fatal("module '{s}' declared but not used", .{key});2908 fatal("module '{s}' declared but not used", .{key});
...@@ -2946,7 +2996,6 @@ fn buildOutputType(...@@ -2946,7 +2996,6 @@ fn buildOutputType(
2946 }2996 }
2947 }2997 }
29482998
2949 // We now repeat part of the process for frameworks.
2950 var resolved_frameworks = std.ArrayList(Compilation.Framework).init(arena);2999 var resolved_frameworks = std.ArrayList(Compilation.Framework).init(arena);
29513000
2952 if (create_module.frameworks.keys().len > 0) {3001 if (create_module.frameworks.keys().len > 0) {
...@@ -3003,7 +3052,7 @@ fn buildOutputType(...@@ -3003,7 +3052,7 @@ fn buildOutputType(
3003 const total_obj_count = create_module.c_source_files.items.len +3052 const total_obj_count = create_module.c_source_files.items.len +
3004 @intFromBool(root_src_file != null) +3053 @intFromBool(root_src_file != null) +
3005 create_module.rc_source_files.items.len +3054 create_module.rc_source_files.items.len +
3006 create_module.link_objects.items.len;3055 link.countObjectInputs(create_module.link_inputs.items);
3007 if (total_obj_count > 1) {3056 if (total_obj_count > 1) {
3008 fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)});3057 fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)});
3009 }3058 }
...@@ -3219,7 +3268,7 @@ fn buildOutputType(...@@ -3219,7 +3268,7 @@ fn buildOutputType(
3219 var cleanup_local_cache_dir: ?fs.Dir = null;3268 var cleanup_local_cache_dir: ?fs.Dir = null;
3220 defer if (cleanup_local_cache_dir) |*dir| dir.close();3269 defer if (cleanup_local_cache_dir) |*dir| dir.close();
32213270
3222 var local_cache_directory: Compilation.Directory = l: {3271 var local_cache_directory: Directory = l: {
3223 if (override_local_cache_dir) |local_cache_dir_path| {3272 if (override_local_cache_dir) |local_cache_dir_path| {
3224 const dir = try fs.cwd().makeOpenPath(local_cache_dir_path, .{});3273 const dir = try fs.cwd().makeOpenPath(local_cache_dir_path, .{});
3225 cleanup_local_cache_dir = dir;3274 cleanup_local_cache_dir = dir;
...@@ -3356,7 +3405,7 @@ fn buildOutputType(...@@ -3356,7 +3405,7 @@ fn buildOutputType(
3356 .emit_llvm_bc = emit_llvm_bc_resolved.data,3405 .emit_llvm_bc = emit_llvm_bc_resolved.data,
3357 .emit_docs = emit_docs_resolved.data,3406 .emit_docs = emit_docs_resolved.data,
3358 .emit_implib = emit_implib_resolved.data,3407 .emit_implib = emit_implib_resolved.data,
3359 .lib_dirs = create_module.lib_dirs.items,3408 .lib_directories = create_module.lib_directories.items,
3360 .rpath_list = create_module.rpath_list.items,3409 .rpath_list = create_module.rpath_list.items,
3361 .symbol_wrap_set = symbol_wrap_set,3410 .symbol_wrap_set = symbol_wrap_set,
3362 .c_source_files = create_module.c_source_files.items,3411 .c_source_files = create_module.c_source_files.items,
...@@ -3364,11 +3413,10 @@ fn buildOutputType(...@@ -3364,11 +3413,10 @@ fn buildOutputType(
3364 .manifest_file = manifest_file,3413 .manifest_file = manifest_file,
3365 .rc_includes = rc_includes,3414 .rc_includes = rc_includes,
3366 .mingw_unicode_entry_point = mingw_unicode_entry_point,3415 .mingw_unicode_entry_point = mingw_unicode_entry_point,
3367 .link_objects = create_module.link_objects.items,3416 .link_inputs = create_module.link_inputs.items,
3368 .framework_dirs = create_module.framework_dirs.items,3417 .framework_dirs = create_module.framework_dirs.items,
3369 .frameworks = resolved_frameworks.items,3418 .frameworks = resolved_frameworks.items,
3370 .system_lib_names = create_module.resolved_system_libs.items(.name),3419 .windows_lib_names = create_module.windows_libs.keys(),
3371 .system_lib_infos = create_module.resolved_system_libs.items(.lib),
3372 .wasi_emulated_libs = create_module.wasi_emulated_libs.items,3420 .wasi_emulated_libs = create_module.wasi_emulated_libs.items,
3373 .want_compiler_rt = want_compiler_rt,3421 .want_compiler_rt = want_compiler_rt,
3374 .hash_style = hash_style,3422 .hash_style = hash_style,
...@@ -3625,28 +3673,6 @@ fn buildOutputType(...@@ -3625,28 +3673,6 @@ fn buildOutputType(
3625 return cleanExit();3673 return cleanExit();
3626}3674}
36273675
3628const LinkerInput = union(enum) {
3629 /// An argument like: -l[name]
3630 named: Named,
3631 /// When a file path is provided.
3632 path: struct {
3633 path: Path,
3634 /// We still need all this info because the path may point to a .so
3635 /// file which may actually be a "linker script" that references
3636 /// library names which need to be resolved.
3637 info: SystemLib,
3638 },
3639 /// Put exactly this string in the dynamic section, no rpath.
3640 exact: struct {
3641 name: []const u8,
3642 },
3643
3644 const Named = struct {
3645 name: []const u8,
3646 info: SystemLib,
3647 };
3648};
3649
3650const CreateModule = struct {3676const CreateModule = struct {
3651 global_cache_directory: Cache.Directory,3677 global_cache_directory: Cache.Directory,
3652 modules: std.StringArrayHashMapUnmanaged(CliModule),3678 modules: std.StringArrayHashMapUnmanaged(CliModule),
...@@ -3659,12 +3685,14 @@ const CreateModule = struct {...@@ -3659,12 +3685,14 @@ const CreateModule = struct {
3659 /// This one is used while collecting CLI options. The set of libs is used3685 /// This one is used while collecting CLI options. The set of libs is used
3660 /// directly after computing the target and used to compute link_libc,3686 /// directly after computing the target and used to compute link_libc,
3661 /// link_libcpp, and then the libraries are filtered into3687 /// link_libcpp, and then the libraries are filtered into
3662 /// `external_system_libs` and `resolved_system_libs`.3688 /// `unresolved_linker_inputs` and `windows_libs`.
3663 system_libs: std.StringArrayHashMapUnmanaged(SystemLib),3689 cli_link_inputs: std.ArrayListUnmanaged(link.UnresolvedInput),
3664 resolved_system_libs: std.MultiArrayList(struct {3690 windows_libs: std.StringArrayHashMapUnmanaged(void),
3665 name: []const u8,3691 /// The local variable `unresolved_link_inputs` is fed into library
3666 lib: Compilation.SystemLib,3692 /// resolution, mutating the input array, and producing this data as
3667 }),3693 /// output. Allocated with gpa.
3694 link_inputs: std.ArrayListUnmanaged(link.Input),
3695
3668 wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile),3696 wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile),
36693697
3670 c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile),3698 c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile),
...@@ -3675,7 +3703,7 @@ const CreateModule = struct {...@@ -3675,7 +3703,7 @@ const CreateModule = struct {
3675 /// CPU features.3703 /// CPU features.
3676 llvm_m_args: std.ArrayListUnmanaged([]const u8),3704 llvm_m_args: std.ArrayListUnmanaged([]const u8),
3677 sysroot: ?[]const u8,3705 sysroot: ?[]const u8,
3678 lib_dirs: std.ArrayListUnmanaged([]const u8),3706 lib_directories: std.ArrayListUnmanaged(Directory),
3679 lib_dir_args: std.ArrayListUnmanaged([]const u8),3707 lib_dir_args: std.ArrayListUnmanaged([]const u8),
3680 libc_installation: ?LibCInstallation,3708 libc_installation: ?LibCInstallation,
3681 want_native_include_dirs: bool,3709 want_native_include_dirs: bool,
...@@ -3685,8 +3713,6 @@ const CreateModule = struct {...@@ -3685,8 +3713,6 @@ const CreateModule = struct {
3685 rpath_list: std.ArrayListUnmanaged([]const u8),3713 rpath_list: std.ArrayListUnmanaged([]const u8),
3686 each_lib_rpath: ?bool,3714 each_lib_rpath: ?bool,
3687 libc_paths_file: ?[]const u8,3715 libc_paths_file: ?[]const u8,
3688 link_objects: std.ArrayListUnmanaged(Compilation.LinkObject),
3689 allow_so_scripts: bool,
3690};3716};
36913717
3692fn createModule(3718fn createModule(
...@@ -3697,6 +3723,7 @@ fn createModule(...@@ -3697,6 +3723,7 @@ fn createModule(
3697 parent: ?*Package.Module,3723 parent: ?*Package.Module,
3698 zig_lib_directory: Cache.Directory,3724 zig_lib_directory: Cache.Directory,
3699 builtin_modules: *std.StringHashMapUnmanaged(*Package.Module),3725 builtin_modules: *std.StringHashMapUnmanaged(*Package.Module),
3726 color: std.zig.Color,
3700) Allocator.Error!*Package.Module {3727) Allocator.Error!*Package.Module {
3701 const cli_mod = &create_module.modules.values()[index];3728 const cli_mod = &create_module.modules.values()[index];
3702 if (cli_mod.resolved) |m| return m;3729 if (cli_mod.resolved) |m| return m;
...@@ -3790,82 +3817,101 @@ fn createModule(...@@ -3790,82 +3817,101 @@ fn createModule(
3790 // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.3817 // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.
3791 // We need to know whether the set of system libraries contains anything besides these3818 // We need to know whether the set of system libraries contains anything besides these
3792 // to decide whether to trigger native path detection logic.3819 // to decide whether to trigger native path detection logic.
3793 var external_linker_inputs: std.ArrayListUnmanaged(LinkerInput) = .empty;3820 // Preserves linker input order.
3794 for (create_module.system_libs.keys(), create_module.system_libs.values()) |lib_name, info| {3821 var unresolved_link_inputs: std.ArrayListUnmanaged(link.UnresolvedInput) = .empty;
3795 if (std.zig.target.isLibCLibName(target, lib_name)) {3822 try unresolved_link_inputs.ensureUnusedCapacity(arena, create_module.cli_link_inputs.items.len);
3796 create_module.opts.link_libc = true;3823 var any_name_queries_remaining = false;
3797 continue;3824 for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) {
3798 }3825 .name_query => |nq| {
3799 if (std.zig.target.isLibCxxLibName(target, lib_name)) {3826 const lib_name = nq.name;
3800 create_module.opts.link_libcpp = true;3827 if (std.zig.target.isLibCLibName(target, lib_name)) {
3801 continue;3828 create_module.opts.link_libc = true;
3802 }
3803 switch (target_util.classifyCompilerRtLibName(target, lib_name)) {
3804 .none => {},
3805 .only_libunwind, .both => {
3806 create_module.opts.link_libunwind = true;
3807 continue;3829 continue;
3808 },3830 }
3809 .only_compiler_rt => {3831 if (std.zig.target.isLibCxxLibName(target, lib_name)) {
3810 warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});3832 create_module.opts.link_libcpp = true;
3811 continue;3833 continue;
3812 },3834 }
3813 }3835 switch (target_util.classifyCompilerRtLibName(target, lib_name)) {
3836 .none => {},
3837 .only_libunwind, .both => {
3838 create_module.opts.link_libunwind = true;
3839 continue;
3840 },
3841 .only_compiler_rt => {
3842 warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});
3843 continue;
3844 },
3845 }
38143846
3815 if (target.isMinGW()) {3847 if (target.isMinGW()) {
3816 const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| {3848 const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| {
3817 fatal("failed to check zig installation for DLL import libs: {s}", .{3849 fatal("failed to check zig installation for DLL import libs: {s}", .{
3818 @errorName(err),3850 @errorName(err),
3819 });3851 });
3820 };3852 };
3821 if (exists) {3853 if (exists) {
3822 try create_module.resolved_system_libs.append(arena, .{3854 try create_module.windows_libs.put(arena, lib_name, {});
3823 .name = lib_name,3855 continue;
3824 .lib = .{3856 }
3825 .needed = true,
3826 .weak = false,
3827 .path = null,
3828 },
3829 });
3830 continue;
3831 }3857 }
3832 }
38333858
3834 if (fs.path.isAbsolute(lib_name)) {3859 if (fs.path.isAbsolute(lib_name)) {
3835 fatal("cannot use absolute path as a system library: {s}", .{lib_name});3860 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
3836 }3861 }
38373862
3838 if (target.os.tag == .wasi) {3863 if (target.os.tag == .wasi) {
3839 if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| {3864 if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| {
3840 try create_module.wasi_emulated_libs.append(arena, crt_file);3865 try create_module.wasi_emulated_libs.append(arena, crt_file);
3841 continue;3866 continue;
3867 }
3842 }3868 }
3843 }3869 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
3870 any_name_queries_remaining = true;
3871 },
3872 else => {
3873 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
3874 },
3875 }; // After this point, unresolved_link_inputs is used instead of cli_link_inputs.
38443876
3845 try external_linker_inputs.append(arena, .{ .named = .{3877 if (any_name_queries_remaining) create_module.want_native_include_dirs = true;
3846 .name = lib_name,
3847 .info = info,
3848 } });
3849 }
3850 // After this point, external_linker_inputs is used instead of system_libs.
3851 if (external_linker_inputs.items.len != 0)
3852 create_module.want_native_include_dirs = true;
38533878
3854 // Resolve the library path arguments with respect to sysroot.3879 // Resolve the library path arguments with respect to sysroot.
3880 try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len);
3855 if (create_module.sysroot) |root| {3881 if (create_module.sysroot) |root| {
3856 try create_module.lib_dirs.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len * 2);3882 for (create_module.lib_dir_args.items) |lib_dir_arg| {
3857 for (create_module.lib_dir_args.items) |dir| {3883 if (fs.path.isAbsolute(lib_dir_arg)) {
3858 if (fs.path.isAbsolute(dir)) {3884 const stripped_dir = lib_dir_arg[fs.path.diskDesignator(lib_dir_arg).len..];
3859 const stripped_dir = dir[fs.path.diskDesignator(dir).len..];
3860 const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });3885 const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });
3861 create_module.lib_dirs.appendAssumeCapacity(full_path);3886 create_module.lib_directories.appendAssumeCapacity(.{
3887 .handle = fs.cwd().openDir(full_path, .{}) catch |err| {
3888 warn("unable to open library directory {s}: {s}", .{ full_path, @errorName(err) });
3889 continue;
3890 },
3891 .path = full_path,
3892 });
3893 } else {
3894 create_module.lib_directories.appendAssumeCapacity(.{
3895 .handle = fs.cwd().openDir(lib_dir_arg, .{}) catch |err| {
3896 warn("unable to open library directory {s}: {s}", .{ lib_dir_arg, @errorName(err) });
3897 continue;
3898 },
3899 .path = lib_dir_arg,
3900 });
3862 }3901 }
3863 create_module.lib_dirs.appendAssumeCapacity(dir);
3864 }3902 }
3865 } else {3903 } else {
3866 create_module.lib_dirs = create_module.lib_dir_args;3904 for (create_module.lib_dir_args.items) |lib_dir_arg| {
3905 create_module.lib_directories.appendAssumeCapacity(.{
3906 .handle = fs.cwd().openDir(lib_dir_arg, .{}) catch |err| {
3907 warn("unable to open library directory {s}: {s}", .{ lib_dir_arg, @errorName(err) });
3908 continue;
3909 },
3910 .path = lib_dir_arg,
3911 });
3912 }
3867 }3913 }
3868 create_module.lib_dir_args = undefined; // From here we use lib_dirs instead.3914 create_module.lib_dir_args = undefined; // From here we use lib_directories instead.
38693915
3870 if (resolved_target.is_native_os and target.isDarwin()) {3916 if (resolved_target.is_native_os and target.isDarwin()) {
3871 // If we want to link against frameworks, we need system headers.3917 // If we want to link against frameworks, we need system headers.
...@@ -3874,7 +3920,10 @@ fn createModule(...@@ -3874,7 +3920,10 @@ fn createModule(
3874 }3920 }
38753921
3876 if (create_module.each_lib_rpath orelse resolved_target.is_native_os) {3922 if (create_module.each_lib_rpath orelse resolved_target.is_native_os) {
3877 try create_module.rpath_list.appendSlice(arena, create_module.lib_dirs.items);3923 try create_module.rpath_list.ensureUnusedCapacity(arena, create_module.lib_directories.items.len);
3924 for (create_module.lib_directories.items) |lib_directory| {
3925 create_module.rpath_list.appendAssumeCapacity(lib_directory.path.?);
3926 }
3878 }3927 }
38793928
3880 // Trigger native system library path detection if necessary.3929 // Trigger native system library path detection if necessary.
...@@ -3892,8 +3941,18 @@ fn createModule(...@@ -3892,8 +3941,18 @@ fn createModule(
3892 create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena);3941 create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena);
38933942
3894 try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items);3943 try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items);
3895 try create_module.lib_dirs.appendSlice(arena, paths.lib_dirs.items);
3896 try create_module.rpath_list.appendSlice(arena, paths.rpaths.items);3944 try create_module.rpath_list.appendSlice(arena, paths.rpaths.items);
3945
3946 try create_module.lib_directories.ensureUnusedCapacity(arena, paths.lib_dirs.items.len);
3947 for (paths.lib_dirs.items) |lib_dir| {
3948 create_module.lib_directories.appendAssumeCapacity(.{
3949 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
3950 warn("unable to open library directory {s}: {s}", .{ lib_dir, @errorName(err) });
3951 continue;
3952 },
3953 .path = lib_dir,
3954 });
3955 }
3897 }3956 }
38983957
3899 if (create_module.libc_paths_file) |paths_file| {3958 if (create_module.libc_paths_file) |paths_file| {
...@@ -3905,7 +3964,7 @@ fn createModule(...@@ -3905,7 +3964,7 @@ fn createModule(
3905 }3964 }
39063965
3907 if (builtin.target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and3966 if (builtin.target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and
3908 external_linker_inputs.items.len != 0)3967 any_name_queries_remaining)
3909 {3968 {
3910 if (create_module.libc_installation == null) {3969 if (create_module.libc_installation == null) {
3911 create_module.libc_installation = LibCInstallation.findNative(.{3970 create_module.libc_installation = LibCInstallation.findNative(.{
...@@ -3916,204 +3975,32 @@ fn createModule(...@@ -3916,204 +3975,32 @@ fn createModule(
3916 fatal("unable to find native libc installation: {s}", .{@errorName(err)});3975 fatal("unable to find native libc installation: {s}", .{@errorName(err)});
3917 };3976 };
39183977
3919 try create_module.lib_dirs.appendSlice(arena, &.{3978 try create_module.lib_directories.appendSlice(arena, &.{
3920 create_module.libc_installation.?.msvc_lib_dir.?,3979 create_module.libc_installation.?.msvc_lib_dir.?,
3921 create_module.libc_installation.?.kernel32_lib_dir.?,3980 create_module.libc_installation.?.kernel32_lib_dir.?,
3922 });3981 });
3923 }3982 }
3924 }3983 }
39253984
3926 // If any libs in this list are statically provided, we omit them from the3985 // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`.
3927 // resolved list and populate the link_objects array instead.3986 link.resolveInputs(
3928 {3987 gpa,
3929 var test_path: std.ArrayListUnmanaged(u8) = .empty;3988 arena,
3930 defer test_path.deinit(gpa);3989 target,
39313990 &unresolved_link_inputs,
3932 var checked_paths: std.ArrayListUnmanaged(u8) = .empty;3991 &create_module.link_inputs,
3933 defer checked_paths.deinit(gpa);3992 create_module.lib_directories.items,
39343993 color,
3935 var ld_script_bytes: std.ArrayListUnmanaged(u8) = .empty;3994 ) catch |err| fatal("failed to resolve link inputs: {s}", .{@errorName(err)});
3936 defer ld_script_bytes.deinit(gpa);3995
39373996 if (create_module.windows_libs.count() != 0) create_module.opts.any_dyn_libs = true;
3938 var failed_libs: std.ArrayListUnmanaged(struct {3997 if (!create_module.opts.any_dyn_libs) for (create_module.link_inputs.items) |item| switch (item) {
3939 name: []const u8,3998 .dso, .dso_exact => {
3940 strategy: SystemLib.SearchStrategy,3999 create_module.opts.any_dyn_libs = true;
3941 checked_paths: []const u8,4000 break;
3942 preferred_mode: std.builtin.LinkMode,4001 },
3943 }) = .empty;4002 else => {},
39444003 };
3945 // Convert external system libs into a stack so that items can be
3946 // pushed to it.
3947 //
3948 // This is necessary because shared objects might turn out to be
3949 // "linker scripts" that in fact resolve to one or more other
3950 // external system libs, including parameters such as "needed".
3951 //
3952 // Unfortunately, such files need to be detected immediately, so
3953 // that this library search logic can be applied to them.
3954 mem.reverse(LinkerInput, external_linker_inputs.items);
3955
3956 syslib: while (external_linker_inputs.popOrNull()) |external_linker_input| {
3957 const external_system_lib: LinkerInput.Named = switch (external_linker_input) {
3958 .named => |named| named,
3959 .path => |p| p: {
3960 if (fs.path.isAbsolute(p.path.sub_path)) {
3961 try create_module.link_objects.append(arena, .{
3962 .path = p.path,
3963 .needed = p.info.needed,
3964 .weak = p.info.weak,
3965 });
3966 continue;
3967 }
3968 const lib_name, const link_mode = stripLibPrefixAndSuffix(p.path.sub_path, target);
3969 break :p .{
3970 .name = lib_name,
3971 .info = .{
3972 .needed = p.info.needed,
3973 .weak = p.info.weak,
3974 .preferred_mode = link_mode,
3975 .search_strategy = .no_fallback,
3976 },
3977 };
3978 },
3979 .exact => |exact| {
3980 try create_module.link_objects.append(arena, .{
3981 .path = Path.initCwd(exact.name),
3982 .loption = true,
3983 });
3984 continue;
3985 },
3986 };
3987 const lib_name = external_system_lib.name;
3988 const info = external_system_lib.info;
3989
3990 // Checked in the first pass above while looking for libc libraries.
3991 assert(!fs.path.isAbsolute(lib_name));
3992
3993 checked_paths.clearRetainingCapacity();
3994
3995 switch (info.search_strategy) {
3996 .mode_first, .no_fallback => {
3997 // check for preferred mode
3998 for (create_module.lib_dirs.items) |lib_dir_path| switch (try accessLibPath(
3999 gpa,
4000 arena,
4001 &test_path,
4002 &checked_paths,
4003 &external_linker_inputs,
4004 create_module,
4005 &ld_script_bytes,
4006 lib_dir_path,
4007 lib_name,
4008 target,
4009 info.preferred_mode,
4010 info,
4011 )) {
4012 .ok => continue :syslib,
4013 .no_match => {},
4014 };
4015 // check for fallback mode
4016 if (info.search_strategy == .no_fallback) {
4017 try failed_libs.append(arena, .{
4018 .name = lib_name,
4019 .strategy = info.search_strategy,
4020 .checked_paths = try arena.dupe(u8, checked_paths.items),
4021 .preferred_mode = info.preferred_mode,
4022 });
4023 continue :syslib;
4024 }
4025 for (create_module.lib_dirs.items) |lib_dir_path| switch (try accessLibPath(
4026 gpa,
4027 arena,
4028 &test_path,
4029 &checked_paths,
4030 &external_linker_inputs,
4031 create_module,
4032 &ld_script_bytes,
4033 lib_dir_path,
4034 lib_name,
4035 target,
4036 info.fallbackMode(),
4037 info,
4038 )) {
4039 .ok => continue :syslib,
4040 .no_match => {},
4041 };
4042 try failed_libs.append(arena, .{
4043 .name = lib_name,
4044 .strategy = info.search_strategy,
4045 .checked_paths = try arena.dupe(u8, checked_paths.items),
4046 .preferred_mode = info.preferred_mode,
4047 });
4048 continue :syslib;
4049 },
4050 .paths_first => {
4051 for (create_module.lib_dirs.items) |lib_dir_path| {
4052 // check for preferred mode
4053 switch (try accessLibPath(
4054 gpa,
4055 arena,
4056 &test_path,
4057 &checked_paths,
4058 &external_linker_inputs,
4059 create_module,
4060 &ld_script_bytes,
4061 lib_dir_path,
4062 lib_name,
4063 target,
4064 info.preferred_mode,
4065 info,
4066 )) {
4067 .ok => continue :syslib,
4068 .no_match => {},
4069 }
4070
4071 // check for fallback mode
4072 switch (try accessLibPath(
4073 gpa,
4074 arena,
4075 &test_path,
4076 &checked_paths,
4077 &external_linker_inputs,
4078 create_module,
4079 &ld_script_bytes,
4080 lib_dir_path,
4081 lib_name,
4082 target,
4083 info.fallbackMode(),
4084 info,
4085 )) {
4086 .ok => continue :syslib,
4087 .no_match => {},
4088 }
4089 }
4090 try failed_libs.append(arena, .{
4091 .name = lib_name,
4092 .strategy = info.search_strategy,
4093 .checked_paths = try arena.dupe(u8, checked_paths.items),
4094 .preferred_mode = info.preferred_mode,
4095 });
4096 continue :syslib;
4097 },
4098 }
4099 @compileError("unreachable");
4100 }
4101
4102 if (failed_libs.items.len > 0) {
4103 for (failed_libs.items) |f| {
4104 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
4105 std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{
4106 @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths,
4107 });
4108 }
4109 process.exit(1);
4110 }
4111 }
4112 // After this point, create_module.resolved_system_libs is used instead
4113 // of external_linker_inputs.
4114
4115 if (create_module.resolved_system_libs.len != 0)
4116 create_module.opts.any_dyn_libs = true;
41174004
4118 create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) {4005 create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) {
4119 error.WasiExecModelRequiresWasi => fatal("only WASI OS targets support execution model", .{}),4006 error.WasiExecModelRequiresWasi => fatal("only WASI OS targets support execution model", .{}),
...@@ -4181,7 +4068,7 @@ fn createModule(...@@ -4181,7 +4068,7 @@ fn createModule(
4181 for (cli_mod.deps) |dep| {4068 for (cli_mod.deps) |dep| {
4182 const dep_index = create_module.modules.getIndex(dep.value) orelse4069 const dep_index = create_module.modules.getIndex(dep.value) orelse
4183 fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key });4070 fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key });
4184 const dep_mod = try createModule(gpa, arena, create_module, dep_index, mod, zig_lib_directory, builtin_modules);4071 const dep_mod = try createModule(gpa, arena, create_module, dep_index, mod, zig_lib_directory, builtin_modules, color);
4185 try mod.deps.put(arena, dep.key, dep_mod);4072 try mod.deps.put(arena, dep.key, dep_mod);
4186 }4073 }
41874074
...@@ -5046,7 +4933,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5046,7 +4933,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
50464933
5047 process.raiseFileDescriptorLimit();4934 process.raiseFileDescriptorLimit();
50484935
5049 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{4936 var zig_lib_directory: Directory = if (override_lib_dir) |lib_dir| .{
5050 .path = lib_dir,4937 .path = lib_dir,
5051 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {4938 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
5052 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });4939 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });
...@@ -5065,7 +4952,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5065,7 +4952,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5065 });4952 });
5066 child_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path;4953 child_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path;
50674954
5068 var global_cache_directory: Compilation.Directory = l: {4955 var global_cache_directory: Directory = l: {
5069 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);4956 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
5070 break :l .{4957 break :l .{
5071 .handle = try fs.cwd().makeOpenPath(p, .{}),4958 .handle = try fs.cwd().makeOpenPath(p, .{}),
...@@ -5076,7 +4963,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5076,7 +4963,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
50764963
5077 child_argv.items[argv_index_global_cache_dir] = global_cache_directory.path orelse cwd_path;4964 child_argv.items[argv_index_global_cache_dir] = global_cache_directory.path orelse cwd_path;
50784965
5079 var local_cache_directory: Compilation.Directory = l: {4966 var local_cache_directory: Directory = l: {
5080 if (override_local_cache_dir) |local_cache_dir_path| {4967 if (override_local_cache_dir) |local_cache_dir_path| {
5081 break :l .{4968 break :l .{
5082 .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}),4969 .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}),
...@@ -5510,7 +5397,7 @@ fn jitCmd(...@@ -5510,7 +5397,7 @@ fn jitCmd(
5510 const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);5397 const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
5511 const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);5398 const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
55125399
5513 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{5400 var zig_lib_directory: Directory = if (override_lib_dir) |lib_dir| .{
5514 .path = lib_dir,5401 .path = lib_dir,
5515 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {5402 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
5516 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });5403 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });
...@@ -5520,7 +5407,7 @@ fn jitCmd(...@@ -5520,7 +5407,7 @@ fn jitCmd(
5520 };5407 };
5521 defer zig_lib_directory.handle.close();5408 defer zig_lib_directory.handle.close();
55225409
5523 var global_cache_directory: Compilation.Directory = l: {5410 var global_cache_directory: Directory = l: {
5524 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);5411 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
5525 break :l .{5412 break :l .{
5526 .handle = try fs.cwd().makeOpenPath(p, .{}),5413 .handle = try fs.cwd().makeOpenPath(p, .{}),
...@@ -6907,197 +6794,6 @@ const ClangSearchSanitizer = struct {...@@ -6907,197 +6794,6 @@ const ClangSearchSanitizer = struct {
6907 };6794 };
6908};6795};
69096796
6910const AccessLibPathResult = enum { ok, no_match };
6911
6912fn accessLibPath(
6913 gpa: Allocator,
6914 arena: Allocator,
6915 /// Allocated via `gpa`.
6916 test_path: *std.ArrayListUnmanaged(u8),
6917 /// Allocated via `gpa`.
6918 checked_paths: *std.ArrayListUnmanaged(u8),
6919 /// Allocated via `arena`.
6920 external_linker_inputs: *std.ArrayListUnmanaged(LinkerInput),
6921 create_module: *CreateModule,
6922 /// Allocated via `gpa`.
6923 ld_script_bytes: *std.ArrayListUnmanaged(u8),
6924 lib_dir_path: []const u8,
6925 lib_name: []const u8,
6926 target: std.Target,
6927 link_mode: std.builtin.LinkMode,
6928 parent: SystemLib,
6929) Allocator.Error!AccessLibPathResult {
6930 const sep = fs.path.sep_str;
6931
6932 if (target.isDarwin() and link_mode == .dynamic) tbd: {
6933 // Prefer .tbd over .dylib.
6934 test_path.clearRetainingCapacity();
6935 try test_path.writer(gpa).print("{s}" ++ sep ++ "lib{s}.tbd", .{ lib_dir_path, lib_name });
6936 try checked_paths.writer(gpa).print("\n {s}", .{test_path.items});
6937 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6938 error.FileNotFound => break :tbd,
6939 else => |e| fatal("unable to search for tbd library '{s}': {s}", .{
6940 test_path.items, @errorName(e),
6941 }),
6942 };
6943 return finishAccessLibPath(arena, create_module, test_path, link_mode, parent, lib_name);
6944 }
6945
6946 main_check: {
6947 test_path.clearRetainingCapacity();
6948 try test_path.writer(gpa).print("{s}" ++ sep ++ "{s}{s}{s}", .{
6949 lib_dir_path,
6950 target.libPrefix(),
6951 lib_name,
6952 switch (link_mode) {
6953 .static => target.staticLibSuffix(),
6954 .dynamic => target.dynamicLibSuffix(),
6955 },
6956 });
6957 try checked_paths.writer(gpa).print("\n {s}", .{test_path.items});
6958
6959 // In the case of .so files, they might actually be "linker scripts"
6960 // that contain references to other libraries.
6961 if (create_module.allow_so_scripts and target.ofmt == .elf and mem.endsWith(u8, test_path.items, ".so")) {
6962 var file = fs.cwd().openFile(test_path.items, .{}) catch |err| switch (err) {
6963 error.FileNotFound => break :main_check,
6964 else => |e| fatal("unable to search for {s} library '{s}': {s}", .{
6965 @tagName(link_mode), test_path.items, @errorName(e),
6966 }),
6967 };
6968 defer file.close();
6969 try ld_script_bytes.resize(gpa, @sizeOf(std.elf.Elf64_Ehdr));
6970 const n = file.readAll(ld_script_bytes.items) catch |err| fatal("failed to read {s}: {s}", .{
6971 test_path.items, @errorName(err),
6972 });
6973 elf_file: {
6974 if (n != ld_script_bytes.items.len) break :elf_file;
6975 if (!mem.eql(u8, ld_script_bytes.items[0..4], "\x7fELF")) break :elf_file;
6976 // Appears to be an ELF file.
6977 return finishAccessLibPath(arena, create_module, test_path, link_mode, parent, lib_name);
6978 }
6979 const stat = file.stat() catch |err|
6980 fatal("failed to stat {s}: {s}", .{ test_path.items, @errorName(err) });
6981 const size = std.math.cast(u32, stat.size) orelse
6982 fatal("{s}: linker script too big", .{test_path.items});
6983 try ld_script_bytes.resize(gpa, size);
6984 const buf = ld_script_bytes.items[n..];
6985 const n2 = file.readAll(buf) catch |err|
6986 fatal("failed to read {s}: {s}", .{ test_path.items, @errorName(err) });
6987 if (n2 != buf.len) fatal("failed to read {s}: unexpected end of file", .{test_path.items});
6988 var diags = link.Diags.init(gpa);
6989 defer diags.deinit();
6990 const ld_script_result = link.LdScript.parse(gpa, &diags, Path.initCwd(test_path.items), ld_script_bytes.items);
6991 if (diags.hasErrors()) {
6992 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6993 try wip_errors.init(gpa);
6994 defer wip_errors.deinit();
6995
6996 try diags.addMessagesToBundle(&wip_errors);
6997
6998 var error_bundle = try wip_errors.toOwnedBundle("");
6999 defer error_bundle.deinit(gpa);
7000
7001 const color: Color = .auto;
7002 error_bundle.renderToStdErr(color.renderOptions());
7003
7004 process.exit(1);
7005 }
7006
7007 var ld_script = ld_script_result catch |err|
7008 fatal("{s}: failed to parse linker script: {s}", .{ test_path.items, @errorName(err) });
7009 defer ld_script.deinit(gpa);
7010
7011 try external_linker_inputs.ensureUnusedCapacity(arena, ld_script.args.len);
7012 for (ld_script.args) |arg| {
7013 const syslib: SystemLib = .{
7014 .needed = arg.needed or parent.needed,
7015 .weak = parent.weak,
7016 .preferred_mode = parent.preferred_mode,
7017 .search_strategy = parent.search_strategy,
7018 };
7019 if (mem.startsWith(u8, arg.path, "-l")) {
7020 external_linker_inputs.appendAssumeCapacity(.{ .named = .{
7021 .name = try arena.dupe(u8, arg.path["-l".len..]),
7022 .info = syslib,
7023 } });
7024 } else {
7025 external_linker_inputs.appendAssumeCapacity(.{ .path = .{
7026 .path = Path.initCwd(try arena.dupe(u8, arg.path)),
7027 .info = syslib,
7028 } });
7029 }
7030 }
7031 return .ok;
7032 }
7033
7034 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
7035 error.FileNotFound => break :main_check,
7036 else => |e| fatal("unable to search for {s} library '{s}': {s}", .{
7037 @tagName(link_mode), test_path.items, @errorName(e),
7038 }),
7039 };
7040 return finishAccessLibPath(arena, create_module, test_path, link_mode, parent, lib_name);
7041 }
7042
7043 // In the case of Darwin, the main check will be .dylib, so here we
7044 // additionally check for .so files.
7045 if (target.isDarwin() and link_mode == .dynamic) so: {
7046 test_path.clearRetainingCapacity();
7047 try test_path.writer(gpa).print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, lib_name });
7048 try checked_paths.writer(gpa).print("\n {s}", .{test_path.items});
7049 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
7050 error.FileNotFound => break :so,
7051 else => |e| fatal("unable to search for so library '{s}': {s}", .{
7052 test_path.items, @errorName(e),
7053 }),
7054 };
7055 return finishAccessLibPath(arena, create_module, test_path, link_mode, parent, lib_name);
7056 }
7057
7058 // In the case of MinGW, the main check will be .lib but we also need to
7059 // look for `libfoo.a`.
7060 if (target.isMinGW() and link_mode == .static) mingw: {
7061 test_path.clearRetainingCapacity();
7062 try test_path.writer(gpa).print("{s}" ++ sep ++ "lib{s}.a", .{
7063 lib_dir_path, lib_name,
7064 });
7065 try checked_paths.writer(gpa).print("\n {s}", .{test_path.items});
7066 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
7067 error.FileNotFound => break :mingw,
7068 else => |e| fatal("unable to search for static library '{s}': {s}", .{
7069 test_path.items, @errorName(e),
7070 }),
7071 };
7072 return finishAccessLibPath(arena, create_module, test_path, link_mode, parent, lib_name);
7073 }
7074
7075 return .no_match;
7076}
7077
7078fn finishAccessLibPath(
7079 arena: Allocator,
7080 create_module: *CreateModule,
7081 test_path: *std.ArrayListUnmanaged(u8),
7082 link_mode: std.builtin.LinkMode,
7083 parent: SystemLib,
7084 lib_name: []const u8,
7085) Allocator.Error!AccessLibPathResult {
7086 const path = Path.initCwd(try arena.dupe(u8, test_path.items));
7087 switch (link_mode) {
7088 .static => try create_module.link_objects.append(arena, .{ .path = path }),
7089 .dynamic => try create_module.resolved_system_libs.append(arena, .{
7090 .name = lib_name,
7091 .lib = .{
7092 .needed = parent.needed,
7093 .weak = parent.weak,
7094 .path = path,
7095 },
7096 }),
7097 }
7098 return .ok;
7099}
7100
7101fn accessFrameworkPath(6797fn accessFrameworkPath(
7102 test_path: *std.ArrayList(u8),6798 test_path: *std.ArrayList(u8),
7103 checked_paths: *std.ArrayList(u8),6799 checked_paths: *std.ArrayList(u8),
...@@ -7218,7 +6914,7 @@ fn cmdFetch(...@@ -7218,7 +6914,7 @@ fn cmdFetch(
7218 });6914 });
7219 defer root_prog_node.end();6915 defer root_prog_node.end();
72206916
7221 var global_cache_directory: Compilation.Directory = l: {6917 var global_cache_directory: Directory = l: {
7222 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);6918 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
7223 break :l .{6919 break :l .{
7224 .handle = try fs.cwd().makeOpenPath(p, .{}),6920 .handle = try fs.cwd().makeOpenPath(p, .{}),
...@@ -7795,18 +7491,3 @@ fn handleModArg(...@@ -7795,18 +7491,3 @@ fn handleModArg(
7795 c_source_files_owner_index.* = create_module.c_source_files.items.len;7491 c_source_files_owner_index.* = create_module.c_source_files.items.len;
7796 rc_source_files_owner_index.* = create_module.rc_source_files.items.len;7492 rc_source_files_owner_index.* = create_module.rc_source_files.items.len;
7797}7493}
7798
7799fn stripLibPrefixAndSuffix(path: []const u8, target: std.Target) struct { []const u8, std.builtin.LinkMode } {
7800 const prefix = target.libPrefix();
7801 const static_suffix = target.staticLibSuffix();
7802 const dynamic_suffix = target.dynamicLibSuffix();
7803 const basename = fs.path.basename(path);
7804 const unlibbed = if (mem.startsWith(u8, basename, prefix)) basename[prefix.len..] else basename;
7805 if (mem.endsWith(u8, unlibbed, static_suffix)) return .{
7806 unlibbed[0 .. unlibbed.len - static_suffix.len], .static,
7807 };
7808 if (mem.endsWith(u8, unlibbed, dynamic_suffix)) return .{
7809 unlibbed[0 .. unlibbed.len - dynamic_suffix.len], .dynamic,
7810 };
7811 fatal("unrecognized library path: {s}", .{path});
7812}