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{
142142pub const File = struct {
143143 prefixed_path: PrefixedPath,
144144 max_file_size: ?usize,
145 /// Populated if the user calls `addOpenedFile`.
146 /// The handle is not owned here.
147 handle: ?fs.File,
145148 stat: Stat,
146149 bin_digest: BinDigest,
147150 contents: ?[]const u8,
......@@ -173,6 +176,11 @@ pub const File = struct {
173176 const new = new_max_size orelse return;
174177 file.max_file_size = if (file.max_file_size) |old| @max(old, new) else new;
175178 }
179
180 pub fn updateHandle(file: *File, new_handle: ?fs.File) void {
181 const handle = new_handle orelse return;
182 file.handle = handle;
183 }
176184};
177185
178186pub const HashHelper = struct {
......@@ -363,15 +371,20 @@ pub const Manifest = struct {
363371 /// var file_contents = cache_hash.files.keys()[file_index].contents.?;
364372 /// ```
365373 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 {
366379 const gpa = m.cache.gpa;
367380 try m.files.ensureUnusedCapacity(gpa, 1);
368381 const resolved_path = try fs.path.resolve(gpa, &.{
369 file_path.root_dir.path orelse ".",
370 file_path.subPathOrDot(),
382 path.root_dir.path orelse ".",
383 path.subPathOrDot(),
371384 });
372385 errdefer gpa.free(resolved_path);
373386 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);
375388 }
376389
377390 /// Deprecated; use `addFilePath`.
......@@ -383,13 +396,14 @@ pub const Manifest = struct {
383396 const prefixed_path = try self.cache.findPrefix(file_path);
384397 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);
387400 }
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 {
390403 const gop = self.files.getOrPutAssumeCapacityAdapted(prefixed_path, FilesAdapter{});
391404 if (gop.found_existing) {
392405 gop.key_ptr.updateMaxSize(max_file_size);
406 gop.key_ptr.updateHandle(handle);
393407 return gop.index;
394408 }
395409 gop.key_ptr.* = .{
......@@ -398,6 +412,7 @@ pub const Manifest = struct {
398412 .max_file_size = max_file_size,
399413 .stat = undefined,
400414 .bin_digest = undefined,
415 .handle = handle,
401416 };
402417
403418 self.hash.add(prefixed_path.prefix);
......@@ -565,6 +580,7 @@ pub const Manifest = struct {
565580 },
566581 .contents = null,
567582 .max_file_size = null,
583 .handle = null,
568584 .stat = .{
569585 .size = stat_size,
570586 .inode = stat_inode,
......@@ -708,12 +724,19 @@ pub const Manifest = struct {
708724 }
709725
710726 fn populateFileHash(self: *Manifest, ch_file: *File) !void {
711 const pp = ch_file.prefixed_path;
712 const dir = self.cache.prefixes()[pp.prefix].handle;
713 const file = try dir.openFile(pp.sub_path, .{});
714 defer file.close();
727 if (ch_file.handle) |handle| {
728 return populateFileHashHandle(self, ch_file, handle);
729 } else {
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();
717740 ch_file.stat = .{
718741 .size = actual_stat.size,
719742 .mtime = actual_stat.mtime,
......@@ -739,8 +762,7 @@ pub const Manifest = struct {
739762 var hasher = hasher_init;
740763 var off: usize = 0;
741764 while (true) {
742 // give me everything you've got, captain
743 const bytes_read = try file.read(contents[off..]);
765 const bytes_read = try handle.pread(contents[off..], off);
744766 if (bytes_read == 0) break;
745767 hasher.update(contents[off..][0..bytes_read]);
746768 off += bytes_read;
......@@ -749,7 +771,7 @@ pub const Manifest = struct {
749771
750772 ch_file.contents = contents;
751773 } else {
752 try hashFile(file, &ch_file.bin_digest);
774 try hashFile(handle, &ch_file.bin_digest);
753775 }
754776
755777 self.hash.hasher.update(&ch_file.bin_digest);
......@@ -813,6 +835,7 @@ pub const Manifest = struct {
813835 gop.key_ptr.* = .{
814836 .prefixed_path = prefixed_path,
815837 .max_file_size = null,
838 .handle = null,
816839 .stat = undefined,
817840 .bin_digest = undefined,
818841 .contents = null,
......@@ -851,6 +874,7 @@ pub const Manifest = struct {
851874 new_file.* = .{
852875 .prefixed_path = prefixed_path,
853876 .max_file_size = null,
877 .handle = null,
854878 .stat = stat,
855879 .bin_digest = undefined,
856880 .contents = null,
......@@ -1067,6 +1091,7 @@ pub const Manifest = struct {
10671091 gop.key_ptr.* = .{
10681092 .prefixed_path = prefixed_path,
10691093 .max_file_size = file.max_file_size,
1094 .handle = file.handle,
10701095 .stat = file.stat,
10711096 .bin_digest = file.bin_digest,
10721097 .contents = null,
......@@ -1103,14 +1128,14 @@ pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void
11031128
11041129fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {
11051130 var buf: [1024]u8 = undefined;
1106
11071131 var hasher = hasher_init;
1132 var off: u64 = 0;
11081133 while (true) {
1109 const bytes_read = try file.read(&buf);
1134 const bytes_read = try file.pread(&buf, off);
11101135 if (bytes_read == 0) break;
11111136 hasher.update(buf[0..bytes_read]);
1137 off += bytes_read;
11121138 }
1113
11141139 hasher.final(bin_digest);
11151140}
11161141
src/Compilation.zig+32-72
......@@ -76,12 +76,13 @@ implib_emit: ?Path,
7676docs_emit: ?Path,
7777root_name: [:0]const u8,
7878include_compiler_rt: bool,
79objects: []Compilation.LinkObject,
79/// Resolved into known paths, any GNU ld scripts already resolved.
80link_inputs: []const link.Input,
8081/// Needed only for passing -F args to clang.
8182framework_dirs: []const []const u8,
82/// These are *always* dynamically linked. Static libraries will be
83/// provided as positional arguments.
84system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
83/// These are only for DLLs dependencies fulfilled by the `.def` files shipped
84/// with Zig. Static libraries are provided as `link.Input` values.
85windows_libs: std.StringArrayHashMapUnmanaged(void),
8586version: ?std.SemanticVersion,
8687libc_installation: ?*const LibCInstallation,
8788skip_linker_dependencies: bool,
......@@ -384,7 +385,7 @@ const Job = union(enum) {
384385 /// one of WASI libc static objects
385386 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`.
388389 windows_import_lib: usize,
389390
390391 const Tag = @typeInfo(Job).@"union".tag_type.?;
......@@ -999,25 +1000,6 @@ const CacheUse = union(CacheMode) {
9991000 }
10001001};
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
10211003pub const CreateOptions = struct {
10221004 zig_lib_directory: Directory,
10231005 local_cache_directory: Directory,
......@@ -1065,18 +1047,17 @@ pub const CreateOptions = struct {
10651047 /// This field is intended to be removed.
10661048 /// The ELF implementation no longer uses this data, however the MachO and COFF
10671049 /// implementations still do.
1068 lib_dirs: []const []const u8 = &[0][]const u8{},
1050 lib_directories: []const Directory = &.{},
10691051 rpath_list: []const []const u8 = &[0][]const u8{},
10701052 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty,
10711053 c_source_files: []const CSourceFile = &.{},
10721054 rc_source_files: []const RcSourceFile = &.{},
10731055 manifest_file: ?[]const u8 = null,
10741056 rc_includes: RcIncludes = .any,
1075 link_objects: []LinkObject = &[0]LinkObject{},
1057 link_inputs: []const link.Input = &.{},
10761058 framework_dirs: []const []const u8 = &[0][]const u8{},
10771059 frameworks: []const Framework = &.{},
1078 system_lib_names: []const []const u8 = &.{},
1079 system_lib_infos: []const SystemLib = &.{},
1060 windows_lib_names: []const []const u8 = &.{},
10801061 /// These correspond to the WASI libc emulated subcomponents including:
10811062 /// * process clocks
10821063 /// * getpid
......@@ -1459,12 +1440,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
14591440 };
14601441 errdefer if (opt_zcu) |zcu| zcu.deinit();
14611442
1462 var system_libs = try std.StringArrayHashMapUnmanaged(SystemLib).init(
1463 gpa,
1464 options.system_lib_names,
1465 options.system_lib_infos,
1466 );
1467 errdefer system_libs.deinit(gpa);
1443 var windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{});
1444 errdefer windows_libs.deinit(gpa);
14681445
14691446 comp.* = .{
14701447 .gpa = gpa,
......@@ -1526,11 +1503,11 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
15261503 .libcxx_abi_version = options.libcxx_abi_version,
15271504 .root_name = root_name,
15281505 .sysroot = sysroot,
1529 .system_libs = system_libs,
1506 .windows_libs = windows_libs,
15301507 .version = options.version,
15311508 .libc_installation = libc_dirs.libc_installation,
15321509 .include_compiler_rt = include_compiler_rt,
1533 .objects = options.link_objects,
1510 .link_inputs = options.link_inputs,
15341511 .framework_dirs = options.framework_dirs,
15351512 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
15361513 .skip_linker_dependencies = options.skip_linker_dependencies,
......@@ -1568,7 +1545,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
15681545 .z_max_page_size = options.linker_z_max_page_size,
15691546 .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,
15701547 .frameworks = options.frameworks,
1571 .lib_dirs = options.lib_dirs,
1548 .lib_directories = options.lib_directories,
15721549 .framework_dirs = options.framework_dirs,
15731550 .rpath_list = options.rpath_list,
15741551 .symbol_wrap_set = options.symbol_wrap_set,
......@@ -1851,17 +1828,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
18511828 });
18521829
18531830 // When linking mingw-w64 there are some import libs we always need.
1854 for (mingw.always_link_libs) |name| {
1855 try comp.system_libs.put(comp.gpa, name, .{
1856 .needed = false,
1857 .weak = false,
1858 .path = null,
1859 });
1860 }
1831 try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);
1832 for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {});
18611833 }
18621834 // Generate Windows import libs.
18631835 if (target.os.tag == .windows) {
1864 const count = comp.system_libs.count();
1836 const count = comp.windows_libs.count();
18651837 for (0..count) |i| {
18661838 try comp.queueJob(.{ .windows_import_lib = i });
18671839 }
......@@ -1930,7 +1902,7 @@ pub fn destroy(comp: *Compilation) void {
19301902 comp.embed_file_work_queue.deinit();
19311903
19321904 const gpa = comp.gpa;
1933 comp.system_libs.deinit(gpa);
1905 comp.windows_libs.deinit(gpa);
19341906
19351907 {
19361908 var it = comp.crt_files.iterator();
......@@ -2563,13 +2535,7 @@ fn addNonIncrementalStuffToCacheManifest(
25632535 cache_helpers.addModule(&man.hash, comp.root_mod);
25642536 }
25652537
2566 for (comp.objects) |obj| {
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 }
2538 try link.hashInputs(man, comp.link_inputs);
25732539
25742540 for (comp.c_object_table.keys()) |key| {
25752541 _ = try man.addFile(key.src.src_path, null);
......@@ -2606,7 +2572,7 @@ fn addNonIncrementalStuffToCacheManifest(
26062572 man.hash.add(comp.rc_includes);
26072573 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
26082574 man.hash.addListOfBytes(comp.framework_dirs);
2609 try link.hashAddSystemLibs(man, comp.system_libs);
2575 man.hash.addListOfBytes(comp.windows_libs.keys());
26102576
26112577 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
26122578 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
......@@ -2625,12 +2591,16 @@ fn addNonIncrementalStuffToCacheManifest(
26252591 man.hash.addOptional(opts.image_base);
26262592 man.hash.addOptional(opts.gc_sections);
26272593 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 }
26292600 man.hash.addListOfBytes(opts.rpath_list);
26302601 man.hash.addListOfBytes(opts.symbol_wrap_set.keys());
26312602 if (comp.config.link_libc) {
26322603 man.hash.add(comp.libc_installation != null);
2633 const target = comp.root_mod.resolved_target.result;
26342604 if (comp.libc_installation) |libc_installation| {
26352605 man.hash.addOptionalBytes(libc_installation.crt_dir);
26362606 if (target.abi == .msvc or target.abi == .itanium) {
......@@ -3798,7 +3768,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
37983768 const named_frame = tracy.namedFrame("windows_import_lib");
37993769 defer named_frame.end();
38003770
3801 const link_lib = comp.system_libs.keys()[index];
3771 const link_lib = comp.windows_libs.keys()[index];
38023772 mingw.buildImportLib(comp, link_lib) catch |err| {
38033773 // TODO Surface more error details.
38043774 comp.lockAndSetMiscFailure(
......@@ -4711,7 +4681,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
47114681 // file and building an object we need to link them together, but with just one it should go
47124682 // directly to the output file.
47134683 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);
47154685 const o_basename_noext = if (direct_o)
47164686 comp.root_name
47174687 else
......@@ -6516,24 +6486,14 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
65166486 // then when we create a sub-Compilation for zig libc, it also tries to
65176487 // build kernel32.lib.
65186488 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
65206492 // This happens when an `extern "foo"` function is referenced.
65216493 // If we haven't seen this library yet and we're targeting Windows, we need
65226494 // 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);
6524 if (!gop.found_existing) {
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 }
6495 const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name);
6496 if (!gop.found_existing) try comp.queueJob(.{ .windows_import_lib = comp.windows_libs.count() - 1 });
65376497}
65386498
65396499/// This decides the optimization mode for all zig-provided libraries, including
src/Sema.zig+1-1
......@@ -9595,7 +9595,7 @@ fn resolveGenericBody(
95959595}
95969596
95979597/// Given a library name, examines if the library name should end up in
9598/// `link.File.Options.system_libs` table (for example, libc is always
9598/// `link.File.Options.windows_libs` table (for example, libc is always
95999599/// specified via dedicated flag `link_libc` instead),
96009600/// and puts it there if it doesn't exist.
96019601/// 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");
1212const Allocator = std.mem.Allocator;
1313const Cache = std.Build.Cache;
1414const Path = std.Build.Cache.Path;
15const Directory = std.Build.Cache.Directory;
1516const Compilation = @import("Compilation.zig");
1617const LibCInstallation = std.zig.LibCInstallation;
1718const Liveness = @import("Liveness.zig");
......@@ -26,19 +27,6 @@ const dev = @import("dev.zig");
2627
2728pub 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
4230pub const Diags = struct {
4331 /// Stored here so that function definitions can distinguish between
4432 /// needing an allocator for things besides error reporting.
......@@ -355,19 +343,6 @@ pub const Diags = struct {
355343 }
356344};
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
371346pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version;
372347
373348pub const File = struct {
......@@ -455,7 +430,7 @@ pub const File = struct {
455430 compatibility_version: ?std.SemanticVersion,
456431
457432 // TODO: remove this. libraries are resolved by the frontend.
458 lib_dirs: []const []const u8,
433 lib_directories: []const Directory,
459434 framework_dirs: []const []const u8,
460435 rpath_list: []const []const u8,
461436
......@@ -1027,7 +1002,6 @@ pub const File = struct {
10271002 defer tracy.end();
10281003
10291004 const comp = base.comp;
1030 const gpa = comp.gpa;
10311005
10321006 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
10331007 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
......@@ -1059,7 +1033,7 @@ pub const File = struct {
10591033 var man: Cache.Manifest = undefined;
10601034 defer if (!base.disable_lld_caching) man.deinit();
10611035
1062 const objects = comp.objects;
1036 const link_inputs = comp.link_inputs;
10631037
10641038 var digest: [Cache.hex_digest_len]u8 = undefined;
10651039
......@@ -1069,11 +1043,8 @@ pub const File = struct {
10691043 // We are about to obtain this lock, so here we give other processes a chance first.
10701044 base.releaseLock();
10711045
1072 for (objects) |obj| {
1073 _ = try man.addFilePath(obj.path, null);
1074 man.hash.add(obj.must_link);
1075 man.hash.add(obj.loption);
1076 }
1046 try hashInputs(&man, link_inputs);
1047
10771048 for (comp.c_object_table.keys()) |key| {
10781049 _ = try man.addFilePath(key.status.success.object_path, null);
10791050 }
......@@ -1109,26 +1080,24 @@ pub const File = struct {
11091080 };
11101081 }
11111082
1112 const win32_resource_table_len = comp.win32_resource_table.count();
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();
1083 var object_files: std.ArrayListUnmanaged([*:0]const u8) = .empty;
11161084
1117 for (objects) |obj| {
1118 object_files.appendAssumeCapacity(try obj.path.toStringZ(arena));
1085 try object_files.ensureUnusedCapacity(arena, link_inputs.len);
1086 for (link_inputs) |input| {
1087 object_files.appendAssumeCapacity(try input.path().?.toStringZ(arena));
11191088 }
1089
1090 try object_files.ensureUnusedCapacity(arena, comp.c_object_table.count() +
1091 comp.win32_resource_table.count() + 2);
1092
11201093 for (comp.c_object_table.keys()) |key| {
11211094 object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena));
11221095 }
11231096 for (comp.win32_resource_table.keys()) |key| {
11241097 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));
11251098 }
1126 if (zcu_obj_path) |p| {
1127 object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
1128 }
1129 if (compiler_rt_path) |p| {
1130 object_files.appendAssumeCapacity(try p.toStringZ(arena));
1131 }
1099 if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
1100 if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
11321101
11331102 if (comp.verbose_link) {
11341103 std.debug.print("ar rcs {s}", .{full_out_path_z});
......@@ -1404,3 +1373,676 @@ pub fn spawnLld(
14041373
14051374 if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});
14061375}
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,
1616/// default or populated together. They should not be separate fields.
1717major_subsystem_version: u16,
1818minor_subsystem_version: u16,
19lib_dirs: []const []const u8,
19lib_directories: []const Directory,
2020entry: link.File.OpenOptions.Entry,
2121entry_addr: ?u32,
2222module_definition_file: ?[]const u8,
......@@ -297,7 +297,7 @@ pub fn createEmpty(
297297 .dynamicbase = options.dynamicbase,
298298 .major_subsystem_version = options.major_subsystem_version orelse 6,
299299 .minor_subsystem_version = options.minor_subsystem_version orelse 0,
300 .lib_dirs = options.lib_dirs,
300 .lib_directories = options.lib_directories,
301301 .entry_addr = math.cast(u32, options.entry_addr orelse 0) orelse
302302 return error.EntryAddressTooBig,
303303 .module_definition_file = options.module_definition_file,
......@@ -2727,6 +2727,7 @@ const mem = std.mem;
27272727
27282728const Allocator = std.mem.Allocator;
27292729const Path = std.Build.Cache.Path;
2730const Directory = std.Build.Cache.Directory;
27302731
27312732const codegen = @import("../codegen.zig");
27322733const link = @import("../link.zig");
src/link/Coff/lld.zig+32-27
......@@ -8,6 +8,7 @@ const log = std.log.scoped(.link);
88const mem = std.mem;
99const Cache = std.Build.Cache;
1010const Path = std.Build.Cache.Path;
11const Directory = std.Build.Cache.Directory;
1112
1213const mingw = @import("../../mingw.zig");
1314const link = @import("../../link.zig");
......@@ -74,10 +75,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
7475
7576 comptime assert(Compilation.link_hash_implementation_version == 14);
7677
77 for (comp.objects) |obj| {
78 _ = try man.addFilePath(obj.path, null);
79 man.hash.add(obj.must_link);
80 }
78 try link.hashInputs(&man, comp.link_inputs);
8179 for (comp.c_object_table.keys()) |key| {
8280 _ = try man.addFilePath(key.status.success.object_path, null);
8381 }
......@@ -88,7 +86,10 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
8886 man.hash.addOptionalBytes(entry_name);
8987 man.hash.add(self.base.stack_size);
9088 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 }
9293 man.hash.add(comp.skip_linker_dependencies);
9394 if (comp.config.link_libc) {
9495 man.hash.add(comp.libc_installation != null);
......@@ -100,7 +101,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
100101 }
101102 }
102103 }
103 try link.hashAddSystemLibs(&man, comp.system_libs);
104 man.hash.addListOfBytes(comp.windows_libs.keys());
104105 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
105106 man.hash.addOptional(self.subsystem);
106107 man.hash.add(comp.config.is_test);
......@@ -148,8 +149,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
148149 // here. TODO: think carefully about how we can avoid this redundant operation when doing
149150 // build-obj. See also the corresponding TODO in linkAsArchive.
150151 const the_object_path = blk: {
151 if (comp.objects.len != 0)
152 break :blk comp.objects[0].path;
152 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
153153
154154 if (comp.c_object_table.count() != 0)
155155 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
266266 }
267267 }
268268
269 for (self.lib_dirs) |lib_dir| {
270 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir}));
269 for (self.lib_directories) |lib_directory| {
270 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_directory.path orelse "."}));
271271 }
272272
273 try argv.ensureUnusedCapacity(comp.objects.len);
274 for (comp.objects) |obj| {
275 if (obj.must_link) {
276 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)}));
277 } else {
278 argv.appendAssumeCapacity(try obj.path.toString(arena));
279 }
280 }
273 try argv.ensureUnusedCapacity(comp.link_inputs.len);
274 for (comp.link_inputs) |link_input| switch (link_input) {
275 .dso_exact => unreachable, // not applicable to PE/COFF
276 inline .dso, .res => |x| {
277 argv.appendAssumeCapacity(try x.path.toString(arena));
278 },
279 .object, .archive => |obj| {
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
282288 for (comp.c_object_table.keys()) |key| {
283289 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
484490 if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));
485491 }
486492
487 try argv.ensureUnusedCapacity(comp.system_libs.count());
488 for (comp.system_libs.keys()) |key| {
493 try argv.ensureUnusedCapacity(comp.windows_libs.count());
494 for (comp.windows_libs.keys()) |key| {
489495 const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
490496 if (comp.crt_files.get(lib_basename)) |crt_file| {
491497 argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena));
492498 continue;
493499 }
494 if (try findLib(arena, lib_basename, self.lib_dirs)) |full_path| {
500 if (try findLib(arena, lib_basename, self.lib_directories)) |full_path| {
495501 argv.appendAssumeCapacity(full_path);
496502 continue;
497503 }
498504 if (target.abi.isGnu()) {
499505 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| {
501507 argv.appendAssumeCapacity(full_path);
502508 continue;
503509 }
......@@ -530,14 +536,13 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
530536 }
531537}
532538
533fn findLib(arena: Allocator, name: []const u8, lib_dirs: []const []const u8) !?[]const u8 {
534 for (lib_dirs) |lib_dir| {
535 const full_path = try fs.path.join(arena, &.{ lib_dir, name });
536 fs.cwd().access(full_path, .{}) catch |err| switch (err) {
539fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Directory) !?[]const u8 {
540 for (lib_directories) |lib_directory| {
541 lib_directory.handle.access(name, .{}) catch |err| switch (err) {
537542 error.FileNotFound => continue,
538543 else => |e| return e,
539544 };
540 return full_path;
545 return try lib_directory.join(arena, &.{name});
541546 }
542547 return null;
543548}
src/link/Elf.zig+223-149
......@@ -796,44 +796,55 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
796796 const csu = try comp.getCrtPaths(arena);
797797
798798 // csu prelude
799 if (csu.crt0) |path| parseObjectReportingFailure(self, path);
800 if (csu.crti) |path| parseObjectReportingFailure(self, path);
801 if (csu.crtbegin) |path| parseObjectReportingFailure(self, path);
802
803 for (comp.objects) |obj| {
804 parseInputReportingFailure(self, obj.path, obj.needed, obj.must_link);
805 }
799 if (csu.crt0) |path| openParseObjectReportingFailure(self, path);
800 if (csu.crti) |path| openParseObjectReportingFailure(self, path);
801 if (csu.crtbegin) |path| openParseObjectReportingFailure(self, path);
802
803 // objects and archives
804 for (comp.link_inputs) |link_input| switch (link_input) {
805 .object, .archive => parseInputReportingFailure(self, link_input),
806 .dso_exact => @panic("TODO"),
807 .dso => continue, // handled below
808 .res => unreachable,
809 };
806810
807811 // This is a set of object files emitted by clang in a single `build-exe` invocation.
808812 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
809813 // in this set.
810814 for (comp.c_object_table.keys()) |key| {
811 parseObjectReportingFailure(self, key.status.success.object_path);
815 openParseObjectReportingFailure(self, key.status.success.object_path);
812816 }
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.?);
817 if (comp.config.any_fuzz) parseCrtFileReportingFailure(self, comp.fuzzer_lib.?);
820 if (comp.config.any_sanitize_thread)
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
819826 // libc
820827 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);
822830 }
823831
824 for (comp.system_libs.values()) |lib_info| {
825 parseInputReportingFailure(self, lib_info.path.?, lib_info.needed, false);
826 }
832 // dynamic libraries
833 for (comp.link_inputs) |link_input| switch (link_input) {
834 .object, .archive, .dso_exact => continue, // handled above
835 .dso => parseInputReportingFailure(self, link_input),
836 .res => unreachable,
837 };
827838
828839 // libc++ dep
829840 if (comp.config.link_libcpp) {
830 parseInputReportingFailure(self, comp.libcxxabi_static_lib.?.full_object_path, false, false);
831 parseInputReportingFailure(self, comp.libcxx_static_lib.?.full_object_path, false, false);
841 openParseArchiveReportingFailure(self, comp.libcxxabi_static_lib.?.full_object_path);
842 openParseArchiveReportingFailure(self, comp.libcxx_static_lib.?.full_object_path);
832843 }
833844
834845 // libunwind dep
835846 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);
837848 }
838849
839850 // libc dep
......@@ -853,7 +864,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
853864 lc.crt_dir.?, lib_name, suffix,
854865 });
855866 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 }
857871 }
858872 } else if (target.isGnuLibC()) {
859873 for (glibc.libs) |lib| {
......@@ -864,15 +878,19 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
864878 const lib_path = Path.initCwd(try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
865879 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
866880 }));
867 parseInputReportingFailure(self, lib_path, false, false);
881 openParseDsoReportingFailure(self, lib_path);
868882 }
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);
870885 } else if (target.isMusl()) {
871886 const path = try comp.get_libc_crt_file(arena, switch (link_mode) {
872887 .static => "libc.a",
873888 .dynamic => "libc.so",
874889 });
875 parseInputReportingFailure(self, path, false, false);
890 switch (link_mode) {
891 .static => openParseArchiveReportingFailure(self, path),
892 .dynamic => openParseDsoReportingFailure(self, path),
893 }
876894 } else {
877895 diags.flags.missing_libc = true;
878896 }
......@@ -884,14 +902,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
884902 // to be after the shared libraries, so they are picked up from the shared
885903 // libraries, not libcompiler_rt.
886904 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);
888906 } else if (comp.compiler_rt_obj) |crt_file| {
889 parseObjectReportingFailure(self, crt_file.full_object_path);
907 openParseObjectReportingFailure(self, crt_file.full_object_path);
890908 }
891909
892910 // csu postlude
893 if (csu.crtend) |path| parseObjectReportingFailure(self, path);
894 if (csu.crtn) |path| parseObjectReportingFailure(self, path);
911 if (csu.crtend) |path| openParseObjectReportingFailure(self, path);
912 if (csu.crtn) |path| openParseObjectReportingFailure(self, path);
895913
896914 if (diags.hasErrors()) return error.FlushFailure;
897915
......@@ -1087,9 +1105,15 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
10871105 try argv.append(full_out_path);
10881106
10891107 if (self.base.isRelocatable()) {
1090 for (comp.objects) |obj| {
1091 try argv.append(try obj.path.toString(arena));
1092 }
1108 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
1109 .res => unreachable,
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
10941118 for (comp.c_object_table.keys()) |key| {
10951119 try argv.append(try key.status.success.object_path.toString(arena));
......@@ -1186,20 +1210,26 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
11861210 }
11871211
11881212 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) {
1199 try argv.append("-l");
1200 }
1201 try argv.append(try obj.path.toString(arena));
1202 }
1214 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
1215 .res => unreachable,
1216 .dso => continue,
1217 .object, .archive => |obj| {
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
12031233 if (whole_archive) {
12041234 try argv.append("-no-whole-archive");
12051235 whole_archive = false;
......@@ -1231,25 +1261,28 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
12311261 // Shared libraries.
12321262 // Worst-case, we need an --as-needed argument for every lib, as well
12331263 // as one before and one after.
1234 try argv.ensureUnusedCapacity(self.base.comp.system_libs.keys().len * 2 + 2);
12351264 argv.appendAssumeCapacity("--as-needed");
12361265 var as_needed = true;
12371266
1238 for (self.base.comp.system_libs.values()) |lib_info| {
1239 const lib_as_needed = !lib_info.needed;
1240 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
1241 0b00, 0b11 => {},
1242 0b01 => {
1243 argv.appendAssumeCapacity("--no-as-needed");
1244 as_needed = false;
1245 },
1246 0b10 => {
1247 argv.appendAssumeCapacity("--as-needed");
1248 as_needed = true;
1249 },
1250 }
1251 argv.appendAssumeCapacity(try lib_info.path.?.toString(arena));
1252 }
1267 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
1268 .object, .archive, .dso_exact => continue,
1269 .dso => |dso| {
1270 const lib_as_needed = !dso.needed;
1271 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
1272 0b00, 0b11 => {},
1273 0b01 => {
1274 try argv.append("--no-as-needed");
1275 as_needed = false;
1276 },
1277 0b10 => {
1278 try argv.append("--as-needed");
1279 as_needed = true;
1280 },
1281 }
1282 argv.appendAssumeCapacity(try dso.path.toString(arena));
1283 },
1284 .res => unreachable,
1285 };
12531286
12541287 if (!as_needed) {
12551288 argv.appendAssumeCapacity("--as-needed");
......@@ -1321,59 +1354,51 @@ pub const ParseError = error{
13211354 UnknownFileType,
13221355} || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;
13231356
1324fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CrtFile) 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 {
1357pub fn parseInputReportingFailure(self: *Elf, input: link.Input) void {
13291358 const gpa = self.base.comp.gpa;
13301359 const diags = &self.base.comp.link_diags;
13311360 const target = self.getTarget();
13321361
1333 switch (Compilation.classifyFileExt(path.sub_path)) {
1334 .object => parseObjectReportingFailure(self, path),
1335 .shared_library => parseSharedObject(gpa, diags, .{
1336 .path = path,
1337 .needed = needed,
1338 }, &self.shared_objects, &self.files, target) catch |err| switch (err) {
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", .{}),
1362 switch (input) {
1363 .res => unreachable,
1364 .dso_exact => unreachable,
1365 .object => |obj| parseObjectReportingFailure(self, obj),
1366 .archive => |obj| parseArchiveReportingFailure(self, obj),
1367 .dso => |dso| parseDsoReportingFailure(gpa, diags, dso, &self.shared_objects, &self.files, target),
13531368 }
13541369}
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 {
13571382 const diags = &self.base.comp.link_diags;
1358 self.parseObject(path) catch |err| switch (err) {
1383 self.parseObject(obj) catch |err| switch (err) {
13591384 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)}),
13611386 };
13621387}
13631388
1364fn parseObject(self: *Elf, path: Path) ParseError!void {
1389fn parseObject(self: *Elf, obj: link.Input.Object) ParseError!void {
13651390 const tracy = trace(@src());
13661391 defer tracy.end();
13671392
13681393 const gpa = self.base.comp.gpa;
1369 const handle = try path.root_dir.handle.openFile(path.sub_path, .{});
1394 const handle = obj.file;
13701395 const fh = try self.addFileHandle(handle);
13711396
13721397 const index: File.Index = @intCast(try self.files.addOne(gpa));
13731398 self.files.set(index, .{ .object = .{
13741399 .path = .{
1375 .root_dir = path.root_dir,
1376 .sub_path = try gpa.dupe(u8, path.sub_path),
1400 .root_dir = obj.path.root_dir,
1401 .sub_path = try gpa.dupe(u8, obj.path.sub_path),
13771402 },
13781403 .file_handle = fh,
13791404 .index = index,
......@@ -1384,17 +1409,35 @@ fn parseObject(self: *Elf, path: Path) ParseError!void {
13841409 try object.parse(self);
13851410}
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 {
13881431 const tracy = trace(@src());
13891432 defer tracy.end();
13901433
13911434 const gpa = self.base.comp.gpa;
1392 const handle = try path.root_dir.handle.openFile(path.sub_path, .{});
1435 const handle = obj.file;
13931436 const fh = try self.addFileHandle(handle);
13941437
13951438 var archive: Archive = .{};
13961439 defer archive.deinit(gpa);
1397 try archive.parse(self, path, fh);
1440 try archive.parse(self, obj.path, fh);
13981441
13991442 const objects = try archive.objects.toOwnedSlice(gpa);
14001443 defer gpa.free(objects);
......@@ -1404,16 +1447,48 @@ fn parseArchive(self: *Elf, path: Path, must_link: bool) ParseError!void {
14041447 self.files.set(index, .{ .object = extracted });
14051448 const object = &self.files.items(.data)[index].object;
14061449 object.index = index;
1407 object.alive = must_link;
1450 object.alive = obj.must_link;
14081451 try object.parse(self);
14091452 try self.objects.append(gpa, index);
14101453 }
14111454}
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(
14141489 gpa: Allocator,
14151490 diags: *Diags,
1416 lib: SystemLib,
1491 dso: link.Input.Dso,
14171492 shared_objects: *std.StringArrayHashMapUnmanaged(File.Index),
14181493 files: *std.MultiArrayList(File.Entry),
14191494 target: std.Target,
......@@ -1421,14 +1496,14 @@ fn parseSharedObject(
14211496 const tracy = trace(@src());
14221497 defer tracy.end();
14231498
1424 const handle = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{});
1499 const handle = dso.file;
14251500 defer handle.close();
14261501
14271502 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);
14291504 defer header.deinit(gpa);
14301505
1431 const soname = header.soname() orelse lib.path.basename();
1506 const soname = header.soname() orelse dso.path.basename();
14321507
14331508 const gop = try shared_objects.getOrPut(gpa, soname);
14341509 if (gop.found_existing) {
......@@ -1446,8 +1521,8 @@ fn parseSharedObject(
14461521 errdefer parsed.deinit(gpa);
14471522
14481523 const duped_path: Path = .{
1449 .root_dir = lib.path.root_dir,
1450 .sub_path = try gpa.dupe(u8, lib.path.sub_path),
1524 .root_dir = dso.path.root_dir,
1525 .sub_path = try gpa.dupe(u8, dso.path.sub_path),
14511526 };
14521527 errdefer gpa.free(duped_path.sub_path);
14531528
......@@ -1456,8 +1531,8 @@ fn parseSharedObject(
14561531 .parsed = parsed,
14571532 .path = duped_path,
14581533 .index = index,
1459 .needed = lib.needed,
1460 .alive = lib.needed,
1534 .needed = dso.needed,
1535 .alive = dso.needed,
14611536 .aliases = null,
14621537 .symbols = .empty,
14631538 .symbols_extra = .empty,
......@@ -1824,11 +1899,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
18241899 try man.addOptionalFile(self.version_script);
18251900 man.hash.add(self.allow_undefined_version);
18261901 man.hash.addOptional(self.enable_new_dtags);
1827 for (comp.objects) |obj| {
1828 _ = try man.addFilePath(obj.path, null);
1829 man.hash.add(obj.must_link);
1830 man.hash.add(obj.loption);
1831 }
1902 try link.hashInputs(&man, comp.link_inputs);
18321903 for (comp.c_object_table.keys()) |key| {
18331904 _ = try man.addFilePath(key.status.success.object_path, null);
18341905 }
......@@ -1875,7 +1946,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
18751946 }
18761947 man.hash.addOptionalBytes(self.soname);
18771948 man.hash.addOptional(comp.version);
1878 try link.hashAddSystemLibs(&man, comp.system_libs);
18791949 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
18801950 man.hash.add(self.base.allow_shlib_undefined);
18811951 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
19221992 // here. TODO: think carefully about how we can avoid this redundant operation when doing
19231993 // build-obj. See also the corresponding TODO in linkAsArchive.
19241994 const the_object_path = blk: {
1925 if (comp.objects.len != 0)
1926 break :blk comp.objects[0].path;
1995 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
19271996
19281997 if (comp.c_object_table.count() != 0)
19291998 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
21782247
21792248 // Positional arguments to the linker such as object files.
21802249 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) {
2191 assert(obj.path.sub_path[0] == ':');
2192 try argv.append("-l");
2193 }
2194 try argv.append(try obj.path.toString(arena));
2195 }
2251 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
2252 .res => unreachable, // Windows-only
2253 .dso => continue,
2254 .object, .archive => |obj| {
2255 if (obj.must_link and !whole_archive) {
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
21962270 if (whole_archive) {
21972271 try argv.append("-no-whole-archive");
21982272 whole_archive = false;
......@@ -2228,35 +2302,35 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
22282302
22292303 // Shared libraries.
22302304 if (is_exe_or_dyn_lib) {
2231 const system_libs = comp.system_libs.keys();
2232 const system_libs_values = comp.system_libs.values();
2233
22342305 // Worst-case, we need an --as-needed argument for every lib, as well
22352306 // as one before and one after.
2236 try argv.ensureUnusedCapacity(system_libs.len * 2 + 2);
2237 argv.appendAssumeCapacity("--as-needed");
2307 try argv.append("--as-needed");
22382308 var as_needed = true;
22392309
2240 for (system_libs_values) |lib_info| {
2241 const lib_as_needed = !lib_info.needed;
2242 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
2243 0b00, 0b11 => {},
2244 0b01 => {
2245 argv.appendAssumeCapacity("--no-as-needed");
2246 as_needed = false;
2247 },
2248 0b10 => {
2249 argv.appendAssumeCapacity("--as-needed");
2250 as_needed = true;
2251 },
2252 }
2310 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
2311 .res => unreachable, // Windows-only
2312 .object, .archive, .dso_exact => continue,
2313 .dso => |dso| {
2314 const lib_as_needed = !dso.needed;
2315 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
2316 0b00, 0b11 => {},
2317 0b01 => {
2318 argv.appendAssumeCapacity("--no-as-needed");
2319 as_needed = false;
2320 },
2321 0b10 => {
2322 argv.appendAssumeCapacity("--as-needed");
2323 as_needed = true;
2324 },
2325 }
22532326
2254 // 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),
2256 // but they could be full paths to .so files, in which case we
2257 // want to avoid prepending "-l".
2258 argv.appendAssumeCapacity(try lib_info.path.?.toString(arena));
2259 }
2327 // By this time, we depend on these libs being dynamically linked
2328 // libraries and not static libraries (the check for that needs to be earlier),
2329 // but they could be full paths to .so files, in which case we
2330 // want to avoid prepending "-l".
2331 argv.appendAssumeCapacity(try dso.path.toString(arena));
2332 },
2333 };
22602334
22612335 if (!as_needed) {
22622336 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
22 const gpa = comp.gpa;
33 const diags = &comp.link_diags;
44
5 for (comp.objects) |obj| {
6 switch (Compilation.classifyFileExt(obj.path.sub_path)) {
7 .object => parseObjectStaticLibReportingFailure(elf_file, obj.path),
8 .static_library => parseArchiveStaticLibReportingFailure(elf_file, obj.path),
9 else => diags.addParseError(obj.path, "unrecognized file extension", .{}),
10 }
11 }
5 for (comp.link_inputs) |link_input| switch (link_input) {
6 .object => |obj| parseObjectStaticLibReportingFailure(elf_file, obj.path),
7 .archive => |obj| parseArchiveStaticLibReportingFailure(elf_file, obj.path),
8 .dso_exact => unreachable,
9 .res => unreachable,
10 .dso => unreachable,
11 };
1212
1313 for (comp.c_object_table.keys()) |key| {
1414 parseObjectStaticLibReportingFailure(elf_file, key.status.success.object_path);
......@@ -153,18 +153,18 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path
153153pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void {
154154 const diags = &comp.link_diags;
155155
156 for (comp.objects) |obj| {
157 elf_file.parseInputReportingFailure(obj.path, false, obj.must_link);
156 for (comp.link_inputs) |link_input| {
157 elf_file.parseInputReportingFailure(link_input);
158158 }
159159
160160 // This is a set of object files emitted by clang in a single `build-exe` invocation.
161161 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
162162 // in this set.
163163 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);
165165 }
166166
167 if (module_obj_path) |path| elf_file.parseObjectReportingFailure(path);
167 if (module_obj_path) |path| elf_file.openParseObjectReportingFailure(path);
168168
169169 if (diags.hasErrors()) return error.FlushFailure;
170170
src/link/MachO.zig+108-73
......@@ -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
15base: link.File,
26
37rpath_list: []const []const u8,
......@@ -114,8 +118,8 @@ headerpad_max_install_names: bool,
114118dead_strip_dylibs: bool,
115119/// Treatment of undefined symbols
116120undefined_treatment: UndefinedTreatment,
117/// Resolved list of library search directories
118lib_dirs: []const []const u8,
121/// TODO: delete this, libraries need to be resolved by the frontend instead
122lib_directories: []const Directory,
119123/// Resolved list of framework search directories
120124framework_dirs: []const []const u8,
121125/// List of input frameworks
......@@ -213,7 +217,8 @@ pub fn createEmpty(
213217 .platform = Platform.fromTarget(target),
214218 .sdk_version = if (options.darwin_sdk_layout) |layout| inferSdkVersion(comp, layout) else null,
215219 .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,
217222 .framework_dirs = options.framework_dirs,
218223 .force_load_objc = options.force_load_objc,
219224 };
......@@ -371,48 +376,44 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
371376 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
372377 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);
375380 defer positionals.deinit();
376381
377 try positionals.ensureUnusedCapacity(comp.objects.len);
378 positionals.appendSliceAssumeCapacity(comp.objects);
382 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
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
380391 // This is a set of object files emitted by clang in a single `build-exe` invocation.
381392 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
382393 // in this set.
383394 try positionals.ensureUnusedCapacity(comp.c_object_table.keys().len);
384395 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));
386397 }
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
390401 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));
392403 }
393404
394405 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));
396407 }
397408
398 for (positionals.items) |obj| {
399 self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err|
400 diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)});
409 for (positionals.items) |link_input| {
410 self.classifyInputFile(link_input) catch |err|
411 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
401412 }
402413
403414 var system_libs = std.ArrayList(SystemLib).init(gpa);
404415 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
416417 // frameworks
417418 try system_libs.ensureUnusedCapacity(self.frameworks.len);
418419 for (self.frameworks) |info| {
......@@ -436,20 +437,24 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
436437 else => |e| return e, // TODO: convert into an error
437438 };
438439
439 for (system_libs.items) |lib| {
440 self.classifyInputFile(lib.path, lib, false) catch |err|
441 diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)});
442 }
440 for (comp.link_inputs) |link_input| switch (link_input) {
441 .object, .archive, .dso_exact => continue,
442 .res => unreachable,
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
444449 // Finally, link against compiler_rt.
445 const compiler_rt_path: ?Path = blk: {
446 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
447 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
448 break :blk null;
449 };
450 if (compiler_rt_path) |path| {
451 self.classifyInputFile(path, .{ .path = path }, false) catch |err|
452 diags.addParseError(path, "failed to parse input file: {s}", .{@errorName(err)});
450 if (comp.compiler_rt_lib) |crt_file| {
451 const path = crt_file.full_object_path;
452 self.classifyInputFile(try link.openArchiveInput(diags, path)) catch |err|
453 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
454 } else if (comp.compiler_rt_obj) |crt_file| {
455 const path = crt_file.full_object_path;
456 self.classifyInputFile(try link.openObjectInput(diags, path)) catch |err|
457 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
453458 }
454459
455460 try self.parseInputFiles();
......@@ -596,9 +601,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
596601 }
597602
598603 if (self.base.isRelocatable()) {
599 for (comp.objects) |obj| {
600 try argv.append(try obj.path.toString(arena));
601 }
604 for (comp.link_inputs) |link_input| switch (link_input) {
605 .object, .archive => |obj| try argv.append(try obj.path.toString(arena)),
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
603611 for (comp.c_object_table.keys()) |key| {
604612 try argv.append(try key.status.success.object_path.toString(arena));
......@@ -678,13 +686,15 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
678686 try argv.append("dynamic_lookup");
679687 }
680688
681 for (comp.objects) |obj| {
682 // TODO: verify this
683 if (obj.must_link) {
684 try argv.append("-force_load");
685 }
686 try argv.append(try obj.path.toString(arena));
687 }
689 for (comp.link_inputs) |link_input| switch (link_input) {
690 .dso => continue, // handled below
691 .res => unreachable, // windows only
692 .object, .archive => |obj| {
693 if (obj.must_link) try argv.append("-force_load"); // TODO: verify this
694 try argv.append(try obj.path.toString(arena));
695 },
696 .dso_exact => |dso_exact| try argv.appendSlice(&.{ "-l", dso_exact.name }),
697 };
688698
689699 for (comp.c_object_table.keys()) |key| {
690700 try argv.append(try key.status.success.object_path.toString(arena));
......@@ -703,21 +713,25 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
703713 try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));
704714 }
705715
706 for (self.lib_dirs) |lib_dir| {
707 const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir});
716 for (self.lib_directories) |lib_directory| {
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 "."});
708719 try argv.append(arg);
709720 }
710721
711 for (comp.system_libs.keys()) |l_name| {
712 const info = comp.system_libs.get(l_name).?;
713 const arg = if (info.needed)
714 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
715 else if (info.weak)
716 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
717 else
718 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
719 try argv.append(arg);
720 }
722 for (comp.link_inputs) |link_input| switch (link_input) {
723 .object, .archive, .dso_exact => continue, // handled above
724 .res => unreachable, // windows only
725 .dso => |dso| {
726 if (dso.needed) {
727 try argv.appendSlice(&.{ "-needed-l", try dso.path.toString(arena) });
728 } else if (dso.weak) {
729 try argv.appendSlice(&.{ "-weak-l", try dso.path.toString(arena) });
730 } else {
731 try argv.appendSlice(&.{ "-l", try dso.path.toString(arena) });
732 }
733 },
734 };
721735
722736 for (self.framework_dirs) |f_dir| {
723737 try argv.append("-F");
......@@ -751,6 +765,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
751765 Compilation.dump_argv(argv.items);
752766}
753767
768/// TODO delete this, libsystem must be resolved when setting up the compilationt pipeline
754769pub fn resolveLibSystem(
755770 self: *MachO,
756771 arena: Allocator,
......@@ -774,8 +789,8 @@ pub fn resolveLibSystem(
774789 },
775790 };
776791
777 for (self.lib_dirs) |dir| {
778 if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success;
792 for (self.lib_directories) |directory| {
793 if (try accessLibPath(arena, &test_path, &checked_paths, directory.path orelse ".", "System")) break :success;
779794 }
780795
781796 diags.addMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{});
......@@ -789,13 +804,14 @@ pub fn resolveLibSystem(
789804 });
790805}
791806
792pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bool) !void {
807pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
793808 const tracy = trace(@src());
794809 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
796813 log.debug("classifying input file {}", .{path});
797814
798 const file = try path.root_dir.handle.openFile(path.sub_path, .{});
799815 const fh = try self.addFileHandle(file);
800816 var buffer: [Archive.SARMAG]u8 = undefined;
801817
......@@ -806,17 +822,17 @@ pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bo
806822 if (h.magic != macho.MH_MAGIC_64) break :blk;
807823 switch (h.filetype) {
808824 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),
810826 else => return error.UnknownFileType,
811827 }
812828 return;
813829 }
814830 if (readArMagic(file, offset, &buffer) catch null) |ar_magic| blk: {
815831 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);
817833 return;
818834 }
819 _ = try self.addTbd(lib, true, fh);
835 _ = try self.addTbd(.fromLinkInput(input), true, fh);
820836}
821837
822838fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch {
......@@ -903,7 +919,7 @@ fn parseInputFileWorker(self: *MachO, file: File) void {
903919 };
904920}
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 {
907923 const tracy = trace(@src());
908924 defer tracy.end();
909925
......@@ -918,7 +934,7 @@ fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.Handle
918934 self.files.set(index, .{ .object = unpacked });
919935 const object = &self.files.items(.data)[index].object;
920936 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;
922938 object.hidden = lib.hidden;
923939 try self.objects.append(gpa, index);
924940 }
......@@ -993,6 +1009,7 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
9931009 return false;
9941010}
9951011
1012/// TODO delete this, libraries must be instead resolved when instantiating the compilation pipeline
9961013fn accessLibPath(
9971014 arena: Allocator,
9981015 test_path: *std.ArrayList(u8),
......@@ -1051,9 +1068,11 @@ fn parseDependentDylibs(self: *MachO) !void {
10511068 if (self.dylibs.items.len == 0) return;
10521069
10531070 const gpa = self.base.comp.gpa;
1054 const lib_dirs = self.lib_dirs;
10551071 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
10571076 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
10581077 defer arena_alloc.deinit();
10591078 const arena = arena_alloc.allocator();
......@@ -1094,9 +1113,9 @@ fn parseDependentDylibs(self: *MachO) !void {
10941113
10951114 // Library
10961115 const lib_name = eatPrefix(stem, "lib") orelse stem;
1097 for (lib_dirs) |dir| {
1116 for (lib_directories) |lib_directory| {
10981117 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;
11001119 }
11011120 }
11021121
......@@ -4366,6 +4385,24 @@ const SystemLib = struct {
43664385 hidden: bool = false,
43674386 reexport: bool = false,
43684387 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 }
43694406};
43704407
43714408pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout;
......@@ -5303,17 +5340,16 @@ const Air = @import("../Air.zig");
53035340const Alignment = Atom.Alignment;
53045341const Allocator = mem.Allocator;
53055342const Archive = @import("MachO/Archive.zig");
5306pub const Atom = @import("MachO/Atom.zig");
53075343const AtomicBool = std.atomic.Value(bool);
53085344const Bind = bind.Bind;
53095345const Cache = std.Build.Cache;
5310const Path = Cache.Path;
53115346const CodeSignature = @import("MachO/CodeSignature.zig");
53125347const Compilation = @import("../Compilation.zig");
53135348const DataInCode = synthetic.DataInCode;
5314pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
5349const Directory = Cache.Directory;
53155350const Dylib = @import("MachO/Dylib.zig");
53165351const ExportTrie = @import("MachO/dyld_info/Trie.zig");
5352const Path = Cache.Path;
53175353const File = @import("MachO/file.zig").File;
53185354const GotSection = synthetic.GotSection;
53195355const Hash = std.hash.Wyhash;
......@@ -5329,7 +5365,6 @@ const Md5 = std.crypto.hash.Md5;
53295365const Zcu = @import("../Zcu.zig");
53305366const InternPool = @import("../InternPool.zig");
53315367const Rebase = @import("MachO/dyld_info/Rebase.zig");
5332pub const Relocation = @import("MachO/Relocation.zig");
53335368const StringTable = @import("StringTable.zig");
53345369const StubsSection = synthetic.StubsSection;
53355370const 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
33 const diags = &macho_file.base.comp.link_diags;
44
55 // 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);
77 defer positionals.deinit();
8 try positionals.ensureUnusedCapacity(comp.objects.len);
9 positionals.appendSliceAssumeCapacity(comp.objects);
8 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
9 positionals.appendSliceAssumeCapacity(comp.link_inputs);
1010
1111 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));
1313 }
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
1717 if (macho_file.getZigObject() == null and positionals.items.len == 1) {
1818 // 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
2020 // the *only* input file over.
2121 // TODO: in the future, when we implement `dsymutil` alternative directly in the Zig
2222 // 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().?;
2424 const in_file = try path.root_dir.handle.openFile(path.sub_path, .{});
2525 const stat = try in_file.stat();
2626 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
2828 return;
2929 }
3030
31 for (positionals.items) |obj| {
32 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err|
33 diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)});
31 for (positionals.items) |link_input| {
32 macho_file.classifyInputFile(link_input) catch |err|
33 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
3434 }
3535
3636 if (diags.hasErrors()) return error.FlushFailure;
......@@ -72,25 +72,25 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
7272 const gpa = comp.gpa;
7373 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);
7676 defer positionals.deinit();
7777
78 try positionals.ensureUnusedCapacity(comp.objects.len);
79 positionals.appendSliceAssumeCapacity(comp.objects);
78 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
79 positionals.appendSliceAssumeCapacity(comp.link_inputs);
8080
8181 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));
8383 }
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
8787 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));
8989 }
9090
91 for (positionals.items) |obj| {
92 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err|
93 diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)});
91 for (positionals.items) |link_input| {
92 macho_file.classifyInputFile(link_input) catch |err|
93 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
9494 }
9595
9696 if (diags.hasErrors()) return error.FlushFailure;
......@@ -745,20 +745,15 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {
745745 try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0);
746746}
747747
748const std = @import("std");
749const Path = std.Build.Cache.Path;
750const WaitGroup = std.Thread.WaitGroup;
748751const 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");
754752const log = std.log.scoped(.link);
755753const macho = std.macho;
756754const math = std.math;
757755const mem = std.mem;
758756const state_log = std.log.scoped(.link_state);
759const std = @import("std");
760const trace = @import("../../tracy.zig").trace;
761const Path = std.Build.Cache.Path;
762757
763758const Archive = @import("Archive.zig");
764759const Atom = @import("Atom.zig");
......@@ -767,3 +762,9 @@ const File = @import("file.zig").File;
767762const MachO = @import("../MachO.zig");
768763const Object = @import("Object.zig");
769764const 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) !
637637 return loc;
638638}
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
648640/// Parses the object file from given path. Returns true when the given file was an object
649641/// file and parsed successfully. Returns false when file is not an object file.
650642/// 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
25222514 // Positional arguments to the linker such as object files and static archives.
25232515 // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.
25242516 var positionals = std.ArrayList([]const u8).init(arena);
2525 try positionals.ensureUnusedCapacity(comp.objects.len);
2517 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
25262518
25272519 const target = comp.root_mod.resolved_target.result;
25282520 const output_mode = comp.config.output_mode;
......@@ -2566,9 +2558,12 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
25662558 try positionals.append(path);
25672559 }
25682560
2569 for (comp.objects) |object| {
2570 try positionals.append(try object.path.toString(arena));
2571 }
2561 for (comp.link_inputs) |link_input| switch (link_input) {
2562 .object, .archive => |obj| try positionals.append(try obj.path.toString(arena)),
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
25732568 for (comp.c_object_table.keys()) |c_object| {
25742569 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
25772572 if (comp.compiler_rt_lib) |lib| try positionals.append(try lib.full_object_path.toString(arena));
25782573 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
25822581 if (wasm.zig_object_index != .null) {
25832582 try wasm.resolveSymbolsInObject(wasm.zig_object_index);
......@@ -3401,10 +3400,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
34013400
34023401 comptime assert(Compilation.link_hash_implementation_version == 14);
34033402
3404 for (comp.objects) |obj| {
3405 _ = try man.addFilePath(obj.path, null);
3406 man.hash.add(obj.must_link);
3407 }
3403 try link.hashInputs(&man, comp.link_inputs);
34083404 for (comp.c_object_table.keys()) |key| {
34093405 _ = try man.addFilePath(key.status.success.object_path, null);
34103406 }
......@@ -3458,8 +3454,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
34583454 // here. TODO: think carefully about how we can avoid this redundant operation when doing
34593455 // build-obj. See also the corresponding TODO in linkAsArchive.
34603456 const the_object_path = blk: {
3461 if (comp.objects.len != 0)
3462 break :blk comp.objects[0].path;
3457 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
34633458
34643459 if (comp.c_object_table.count() != 0)
34653460 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:
36213616
36223617 // Positional arguments to the linker such as object files.
36233618 var whole_archive = false;
3624 for (comp.objects) |obj| {
3625 if (obj.must_link and !whole_archive) {
3626 try argv.append("-whole-archive");
3627 whole_archive = true;
3628 } else if (!obj.must_link and whole_archive) {
3629 try argv.append("-no-whole-archive");
3630 whole_archive = false;
3631 }
3632 try argv.append(try obj.path.toString(arena));
3633 }
3619 for (comp.link_inputs) |link_input| switch (link_input) {
3620 .object, .archive => |obj| {
3621 if (obj.must_link and !whole_archive) {
3622 try argv.append("-whole-archive");
3623 whole_archive = true;
3624 } else if (!obj.must_link and whole_archive) {
3625 try argv.append("-no-whole-archive");
3626 whole_archive = false;
3627 }
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 };
36343636 if (whole_archive) {
36353637 try argv.append("-no-whole-archive");
36363638 whole_archive = false;
src/main.zig+319-638
......@@ -15,6 +15,7 @@ const cleanExit = std.process.cleanExit;
1515const native_os = builtin.os.tag;
1616const Cache = std.Build.Cache;
1717const Path = std.Build.Cache.Path;
18const Directory = std.Build.Cache.Directory;
1819const EnvVar = std.zig.EnvVar;
1920const LibCInstallation = std.zig.LibCInstallation;
2021const AstGen = std.zig.AstGen;
......@@ -55,7 +56,7 @@ pub fn wasi_cwd() std.os.wasi.fd_t {
5556 return cwd_fd;
5657}
5758
58fn getWasiPreopen(name: []const u8) Compilation.Directory {
59fn getWasiPreopen(name: []const u8) Directory {
5960 return .{
6061 .path = name,
6162 .handle = .{
......@@ -768,27 +769,6 @@ const ArgsIterator = struct {
768769 }
769770};
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
792772/// Similar to `link.Framework` except it doesn't store yet unresolved
793773/// path to the framework.
794774const Framework = struct {
......@@ -869,6 +849,7 @@ fn buildOutputType(
869849 var linker_gc_sections: ?bool = null;
870850 var linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null;
871851 var linker_allow_shlib_undefined: ?bool = null;
852 var allow_so_scripts: bool = false;
872853 var linker_bind_global_refs_locally: ?bool = null;
873854 var linker_import_symbols: bool = false;
874855 var linker_import_table: bool = false;
......@@ -921,7 +902,7 @@ fn buildOutputType(
921902 var hash_style: link.File.Elf.HashStyle = .both;
922903 var entitlements: ?[]const u8 = null;
923904 var pagezero_size: ?u64 = null;
924 var lib_search_strategy: SystemLib.SearchStrategy = .paths_first;
905 var lib_search_strategy: link.UnresolvedInput.SearchStrategy = .paths_first;
925906 var lib_preferred_mode: std.builtin.LinkMode = .dynamic;
926907 var headerpad_size: ?u32 = null;
927908 var headerpad_max_install_names: bool = false;
......@@ -985,8 +966,10 @@ fn buildOutputType(
985966 // Populated in the call to `createModule` for the root module.
986967 .resolved_options = undefined,
987968
988 .system_libs = .{},
989 .resolved_system_libs = .{},
969 .cli_link_inputs = .empty,
970 .windows_libs = .empty,
971 .link_inputs = .empty,
972
990973 .wasi_emulated_libs = .{},
991974
992975 .c_source_files = .{},
......@@ -994,7 +977,7 @@ fn buildOutputType(
994977
995978 .llvm_m_args = .{},
996979 .sysroot = null,
997 .lib_dirs = .{}, // populated by createModule()
980 .lib_directories = .{}, // populated by createModule()
998981 .lib_dir_args = .{}, // populated from CLI arg parsing
999982 .libc_installation = null,
1000983 .want_native_include_dirs = false,
......@@ -1003,9 +986,7 @@ fn buildOutputType(
1003986 .rpath_list = .{},
1004987 .each_lib_rpath = null,
1005988 .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena),
1006 .link_objects = .{},
1007989 .native_system_include_paths = &.{},
1008 .allow_so_scripts = false,
1009990 };
1010991
1011992 // before arg parsing, check for the NO_COLOR and CLICOLOR_FORCE environment variables
......@@ -1240,30 +1221,42 @@ fn buildOutputType(
12401221 // We don't know whether this library is part of libc
12411222 // or libc++ until we resolve the target, so we append
12421223 // to the list for now.
1243 try create_module.system_libs.put(arena, args_iter.nextOrFatal(), .{
1244 .needed = false,
1245 .weak = false,
1246 .preferred_mode = lib_preferred_mode,
1247 .search_strategy = lib_search_strategy,
1248 });
1224 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1225 .name = args_iter.nextOrFatal(),
1226 .query = .{
1227 .needed = false,
1228 .weak = false,
1229 .preferred_mode = lib_preferred_mode,
1230 .search_strategy = lib_search_strategy,
1231 .allow_so_scripts = allow_so_scripts,
1232 },
1233 } });
12491234 } else if (mem.eql(u8, arg, "--needed-library") or
12501235 mem.eql(u8, arg, "-needed-l") or
12511236 mem.eql(u8, arg, "-needed_library"))
12521237 {
12531238 const next_arg = args_iter.nextOrFatal();
1254 try create_module.system_libs.put(arena, next_arg, .{
1255 .needed = true,
1256 .weak = false,
1257 .preferred_mode = lib_preferred_mode,
1258 .search_strategy = lib_search_strategy,
1259 });
1239 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1240 .name = next_arg,
1241 .query = .{
1242 .needed = true,
1243 .weak = false,
1244 .preferred_mode = lib_preferred_mode,
1245 .search_strategy = lib_search_strategy,
1246 .allow_so_scripts = allow_so_scripts,
1247 },
1248 } });
12601249 } 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(), .{
1262 .needed = false,
1263 .weak = true,
1264 .preferred_mode = lib_preferred_mode,
1265 .search_strategy = lib_search_strategy,
1266 });
1250 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1251 .name = args_iter.nextOrFatal(),
1252 .query = .{
1253 .needed = false,
1254 .weak = true,
1255 .preferred_mode = lib_preferred_mode,
1256 .search_strategy = lib_search_strategy,
1257 .allow_so_scripts = allow_so_scripts,
1258 },
1259 } });
12671260 } else if (mem.eql(u8, arg, "-D")) {
12681261 try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });
12691262 } else if (mem.eql(u8, arg, "-I")) {
......@@ -1577,9 +1570,9 @@ fn buildOutputType(
15771570 } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) {
15781571 linker_allow_shlib_undefined = false;
15791572 } else if (mem.eql(u8, arg, "-fallow-so-scripts")) {
1580 create_module.allow_so_scripts = true;
1573 allow_so_scripts = true;
15811574 } else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {
1582 create_module.allow_so_scripts = false;
1575 allow_so_scripts = false;
15831576 } else if (mem.eql(u8, arg, "-z")) {
15841577 const z_arg = args_iter.nextOrFatal();
15851578 if (mem.eql(u8, z_arg, "nodelete")) {
......@@ -1687,26 +1680,38 @@ fn buildOutputType(
16871680 // We don't know whether this library is part of libc
16881681 // or libc++ until we resolve the target, so we append
16891682 // to the list for now.
1690 try create_module.system_libs.put(arena, arg["-l".len..], .{
1691 .needed = false,
1692 .weak = false,
1693 .preferred_mode = lib_preferred_mode,
1694 .search_strategy = lib_search_strategy,
1695 });
1683 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1684 .name = arg["-l".len..],
1685 .query = .{
1686 .needed = false,
1687 .weak = false,
1688 .preferred_mode = lib_preferred_mode,
1689 .search_strategy = lib_search_strategy,
1690 .allow_so_scripts = allow_so_scripts,
1691 },
1692 } });
16961693 } else if (mem.startsWith(u8, arg, "-needed-l")) {
1697 try create_module.system_libs.put(arena, arg["-needed-l".len..], .{
1698 .needed = true,
1699 .weak = false,
1700 .preferred_mode = lib_preferred_mode,
1701 .search_strategy = lib_search_strategy,
1702 });
1694 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1695 .name = arg["-needed-l".len..],
1696 .query = .{
1697 .needed = true,
1698 .weak = false,
1699 .preferred_mode = lib_preferred_mode,
1700 .search_strategy = lib_search_strategy,
1701 .allow_so_scripts = allow_so_scripts,
1702 },
1703 } });
17031704 } else if (mem.startsWith(u8, arg, "-weak-l")) {
1704 try create_module.system_libs.put(arena, arg["-weak-l".len..], .{
1705 .needed = false,
1706 .weak = true,
1707 .preferred_mode = lib_preferred_mode,
1708 .search_strategy = lib_search_strategy,
1709 });
1705 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1706 .name = arg["-weak-l".len..],
1707 .query = .{
1708 .needed = false,
1709 .weak = true,
1710 .preferred_mode = lib_preferred_mode,
1711 .search_strategy = lib_search_strategy,
1712 .allow_so_scripts = allow_so_scripts,
1713 },
1714 } });
17101715 } else if (mem.startsWith(u8, arg, "-D")) {
17111716 try cc_argv.append(arena, arg);
17121717 } else if (mem.startsWith(u8, arg, "-I")) {
......@@ -1731,15 +1736,28 @@ fn buildOutputType(
17311736 fatal("unrecognized parameter: '{s}'", .{arg});
17321737 }
17331738 } else switch (file_ext orelse Compilation.classifyFileExt(arg)) {
1734 .shared_library => {
1735 try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) });
1736 create_module.opts.any_dyn_libs = true;
1737 },
1738 .object, .static_library => {
1739 try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) });
1739 .shared_library, .object, .static_library => {
1740 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1741 .path = Path.initCwd(arg),
1742 .query = .{
1743 .preferred_mode = lib_preferred_mode,
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.
17401751 },
17411752 .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 } });
17431761 contains_res_file = true;
17441762 },
17451763 .manifest => {
......@@ -1792,6 +1810,7 @@ fn buildOutputType(
17921810 // some functionality that depend on it, such as C++ exceptions and
17931811 // DWARF-based stack traces.
17941812 link_eh_frame_hdr = true;
1813 allow_so_scripts = true;
17951814
17961815 const COutMode = enum {
17971816 link,
......@@ -1851,24 +1870,32 @@ fn buildOutputType(
18511870 .ext = file_ext, // duped while parsing the args.
18521871 });
18531872 },
1854 .shared_library => {
1855 try create_module.link_objects.append(arena, .{
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, .{
1873 .unknown, .object, .static_library, .shared_library => {
1874 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
18631875 .path = Path.initCwd(it.only_arg),
1864 .must_link = must_link,
1865 });
1876 .query = .{
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.
18661887 },
18671888 .res => {
1868 try create_module.link_objects.append(arena, .{
1889 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
18691890 .path = Path.initCwd(it.only_arg),
1870 .must_link = must_link,
1871 });
1891 .query = .{
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 } });
18721899 contains_res_file = true;
18731900 },
18741901 .manifest => {
......@@ -1900,19 +1927,21 @@ fn buildOutputType(
19001927 // -l :path/to/filename is used when callers need
19011928 // more control over what's in the resulting
19021929 // binary: no extra rpaths and DSO filename exactly
1903 // as provided. Hello, Go.
1904 try create_module.link_objects.append(arena, .{
1905 .path = Path.initCwd(it.only_arg),
1906 .must_link = must_link,
1907 .loption = true,
1908 });
1930 // as provided. CGo compilation depends on this.
1931 try create_module.cli_link_inputs.append(arena, .{ .dso_exact = .{
1932 .name = it.only_arg,
1933 } });
19091934 } else {
1910 try create_module.system_libs.put(arena, it.only_arg, .{
1911 .needed = needed,
1912 .weak = false,
1913 .preferred_mode = lib_preferred_mode,
1914 .search_strategy = lib_search_strategy,
1915 });
1935 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1936 .name = it.only_arg,
1937 .query = .{
1938 .needed = needed,
1939 .weak = false,
1940 .preferred_mode = lib_preferred_mode,
1941 .search_strategy = lib_search_strategy,
1942 .allow_so_scripts = allow_so_scripts,
1943 },
1944 } });
19161945 }
19171946 },
19181947 .ignore => {},
......@@ -2181,12 +2210,16 @@ fn buildOutputType(
21812210 },
21822211 .force_load_objc => force_load_objc = true,
21832212 .mingw_unicode_entry_point => mingw_unicode_entry_point = true,
2184 .weak_library => try create_module.system_libs.put(arena, it.only_arg, .{
2185 .needed = false,
2186 .weak = true,
2187 .preferred_mode = lib_preferred_mode,
2188 .search_strategy = lib_search_strategy,
2189 }),
2213 .weak_library => try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2214 .name = it.only_arg,
2215 .query = .{
2216 .needed = false,
2217 .weak = true,
2218 .preferred_mode = lib_preferred_mode,
2219 .search_strategy = lib_search_strategy,
2220 .allow_so_scripts = allow_so_scripts,
2221 },
2222 } }),
21902223 .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),
21912224 .headerpad_max_install_names => headerpad_max_install_names = true,
21922225 .compress_debug_sections => {
......@@ -2489,26 +2522,38 @@ fn buildOutputType(
24892522 } else if (mem.eql(u8, arg, "-needed_framework")) {
24902523 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true });
24912524 } else if (mem.eql(u8, arg, "-needed_library")) {
2492 try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{
2493 .weak = false,
2494 .needed = true,
2495 .preferred_mode = lib_preferred_mode,
2496 .search_strategy = lib_search_strategy,
2497 });
2525 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2526 .name = linker_args_it.nextOrFatal(),
2527 .query = .{
2528 .weak = false,
2529 .needed = true,
2530 .preferred_mode = lib_preferred_mode,
2531 .search_strategy = lib_search_strategy,
2532 .allow_so_scripts = allow_so_scripts,
2533 },
2534 } });
24982535 } else if (mem.startsWith(u8, arg, "-weak-l")) {
2499 try create_module.system_libs.put(arena, arg["-weak-l".len..], .{
2500 .weak = true,
2501 .needed = false,
2502 .preferred_mode = lib_preferred_mode,
2503 .search_strategy = lib_search_strategy,
2504 });
2536 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2537 .name = arg["-weak-l".len..],
2538 .query = .{
2539 .weak = true,
2540 .needed = false,
2541 .preferred_mode = lib_preferred_mode,
2542 .search_strategy = lib_search_strategy,
2543 .allow_so_scripts = allow_so_scripts,
2544 },
2545 } });
25052546 } else if (mem.eql(u8, arg, "-weak_library")) {
2506 try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{
2507 .weak = true,
2508 .needed = false,
2509 .preferred_mode = lib_preferred_mode,
2510 .search_strategy = lib_search_strategy,
2511 });
2547 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2548 .name = linker_args_it.nextOrFatal(),
2549 .query = .{
2550 .weak = true,
2551 .needed = false,
2552 .preferred_mode = lib_preferred_mode,
2553 .search_strategy = lib_search_strategy,
2554 .allow_so_scripts = allow_so_scripts,
2555 },
2556 } });
25122557 } else if (mem.eql(u8, arg, "-compatibility_version")) {
25132558 const compat_version = linker_args_it.nextOrFatal();
25142559 compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {
......@@ -2539,10 +2584,14 @@ fn buildOutputType(
25392584 } else if (mem.eql(u8, arg, "-install_name")) {
25402585 install_name = linker_args_it.nextOrFatal();
25412586 } 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 = .{
25432588 .path = Path.initCwd(linker_args_it.nextOrFatal()),
2544 .must_link = true,
2545 });
2589 .query = .{
2590 .must_link = true,
2591 .preferred_mode = .static,
2592 .search_strategy = .no_fallback,
2593 },
2594 } });
25462595 } else if (mem.eql(u8, arg, "-hash-style") or
25472596 mem.eql(u8, arg, "--hash-style"))
25482597 {
......@@ -2672,7 +2721,7 @@ fn buildOutputType(
26722721 },
26732722 }
26742723 if (create_module.c_source_files.items.len == 0 and
2675 create_module.link_objects.items.len == 0 and
2724 !link.anyObjectInputs(create_module.link_inputs.items) and
26762725 root_src_file == null)
26772726 {
26782727 // For example `zig cc` and no args should print the "no input files" message.
......@@ -2714,8 +2763,9 @@ fn buildOutputType(
27142763 if (create_module.c_source_files.items.len >= 1)
27152764 break :b create_module.c_source_files.items[0].src_path;
27162765
2717 if (create_module.link_objects.items.len >= 1)
2718 break :b create_module.link_objects.items[0].path.sub_path;
2766 for (create_module.link_inputs.items) |link_input| {
2767 if (link_input.path()) |path| break :b path.sub_path;
2768 }
27192769
27202770 if (emit_bin == .yes)
27212771 break :b emit_bin.yes;
......@@ -2801,7 +2851,7 @@ fn buildOutputType(
28012851 fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
28022852 };
28032853
2804 var zig_lib_directory: Compilation.Directory = d: {
2854 var zig_lib_directory: Directory = d: {
28052855 if (override_lib_dir) |unresolved_lib_dir| {
28062856 const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
28072857 break :d .{
......@@ -2822,7 +2872,7 @@ fn buildOutputType(
28222872 };
28232873 defer zig_lib_directory.handle.close();
28242874
2825 var global_cache_directory: Compilation.Directory = l: {
2875 var global_cache_directory: Directory = l: {
28262876 if (override_global_cache_dir) |p| {
28272877 break :l .{
28282878 .handle = try fs.cwd().makeOpenPath(p, .{}),
......@@ -2852,7 +2902,7 @@ fn buildOutputType(
28522902
28532903 var builtin_modules: std.StringHashMapUnmanaged(*Package.Module) = .empty;
28542904 // `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);
28562906 for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {
28572907 if (cli_mod.resolved == null)
28582908 fatal("module '{s}' declared but not used", .{key});
......@@ -2946,7 +2996,6 @@ fn buildOutputType(
29462996 }
29472997 }
29482998
2949 // We now repeat part of the process for frameworks.
29502999 var resolved_frameworks = std.ArrayList(Compilation.Framework).init(arena);
29513000
29523001 if (create_module.frameworks.keys().len > 0) {
......@@ -3003,7 +3052,7 @@ fn buildOutputType(
30033052 const total_obj_count = create_module.c_source_files.items.len +
30043053 @intFromBool(root_src_file != null) +
30053054 create_module.rc_source_files.items.len +
3006 create_module.link_objects.items.len;
3055 link.countObjectInputs(create_module.link_inputs.items);
30073056 if (total_obj_count > 1) {
30083057 fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)});
30093058 }
......@@ -3219,7 +3268,7 @@ fn buildOutputType(
32193268 var cleanup_local_cache_dir: ?fs.Dir = null;
32203269 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: {
32233272 if (override_local_cache_dir) |local_cache_dir_path| {
32243273 const dir = try fs.cwd().makeOpenPath(local_cache_dir_path, .{});
32253274 cleanup_local_cache_dir = dir;
......@@ -3356,7 +3405,7 @@ fn buildOutputType(
33563405 .emit_llvm_bc = emit_llvm_bc_resolved.data,
33573406 .emit_docs = emit_docs_resolved.data,
33583407 .emit_implib = emit_implib_resolved.data,
3359 .lib_dirs = create_module.lib_dirs.items,
3408 .lib_directories = create_module.lib_directories.items,
33603409 .rpath_list = create_module.rpath_list.items,
33613410 .symbol_wrap_set = symbol_wrap_set,
33623411 .c_source_files = create_module.c_source_files.items,
......@@ -3364,11 +3413,10 @@ fn buildOutputType(
33643413 .manifest_file = manifest_file,
33653414 .rc_includes = rc_includes,
33663415 .mingw_unicode_entry_point = mingw_unicode_entry_point,
3367 .link_objects = create_module.link_objects.items,
3416 .link_inputs = create_module.link_inputs.items,
33683417 .framework_dirs = create_module.framework_dirs.items,
33693418 .frameworks = resolved_frameworks.items,
3370 .system_lib_names = create_module.resolved_system_libs.items(.name),
3371 .system_lib_infos = create_module.resolved_system_libs.items(.lib),
3419 .windows_lib_names = create_module.windows_libs.keys(),
33723420 .wasi_emulated_libs = create_module.wasi_emulated_libs.items,
33733421 .want_compiler_rt = want_compiler_rt,
33743422 .hash_style = hash_style,
......@@ -3625,28 +3673,6 @@ fn buildOutputType(
36253673 return cleanExit();
36263674}
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
36503676const CreateModule = struct {
36513677 global_cache_directory: Cache.Directory,
36523678 modules: std.StringArrayHashMapUnmanaged(CliModule),
......@@ -3659,12 +3685,14 @@ const CreateModule = struct {
36593685 /// This one is used while collecting CLI options. The set of libs is used
36603686 /// directly after computing the target and used to compute link_libc,
36613687 /// link_libcpp, and then the libraries are filtered into
3662 /// `external_system_libs` and `resolved_system_libs`.
3663 system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
3664 resolved_system_libs: std.MultiArrayList(struct {
3665 name: []const u8,
3666 lib: Compilation.SystemLib,
3667 }),
3688 /// `unresolved_linker_inputs` and `windows_libs`.
3689 cli_link_inputs: std.ArrayListUnmanaged(link.UnresolvedInput),
3690 windows_libs: std.StringArrayHashMapUnmanaged(void),
3691 /// The local variable `unresolved_link_inputs` is fed into library
3692 /// resolution, mutating the input array, and producing this data as
3693 /// output. Allocated with gpa.
3694 link_inputs: std.ArrayListUnmanaged(link.Input),
3695
36683696 wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile),
36693697
36703698 c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile),
......@@ -3675,7 +3703,7 @@ const CreateModule = struct {
36753703 /// CPU features.
36763704 llvm_m_args: std.ArrayListUnmanaged([]const u8),
36773705 sysroot: ?[]const u8,
3678 lib_dirs: std.ArrayListUnmanaged([]const u8),
3706 lib_directories: std.ArrayListUnmanaged(Directory),
36793707 lib_dir_args: std.ArrayListUnmanaged([]const u8),
36803708 libc_installation: ?LibCInstallation,
36813709 want_native_include_dirs: bool,
......@@ -3685,8 +3713,6 @@ const CreateModule = struct {
36853713 rpath_list: std.ArrayListUnmanaged([]const u8),
36863714 each_lib_rpath: ?bool,
36873715 libc_paths_file: ?[]const u8,
3688 link_objects: std.ArrayListUnmanaged(Compilation.LinkObject),
3689 allow_so_scripts: bool,
36903716};
36913717
36923718fn createModule(
......@@ -3697,6 +3723,7 @@ fn createModule(
36973723 parent: ?*Package.Module,
36983724 zig_lib_directory: Cache.Directory,
36993725 builtin_modules: *std.StringHashMapUnmanaged(*Package.Module),
3726 color: std.zig.Color,
37003727) Allocator.Error!*Package.Module {
37013728 const cli_mod = &create_module.modules.values()[index];
37023729 if (cli_mod.resolved) |m| return m;
......@@ -3790,82 +3817,101 @@ fn createModule(
37903817 // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.
37913818 // We need to know whether the set of system libraries contains anything besides these
37923819 // to decide whether to trigger native path detection logic.
3793 var external_linker_inputs: std.ArrayListUnmanaged(LinkerInput) = .empty;
3794 for (create_module.system_libs.keys(), create_module.system_libs.values()) |lib_name, info| {
3795 if (std.zig.target.isLibCLibName(target, lib_name)) {
3796 create_module.opts.link_libc = true;
3797 continue;
3798 }
3799 if (std.zig.target.isLibCxxLibName(target, lib_name)) {
3800 create_module.opts.link_libcpp = true;
3801 continue;
3802 }
3803 switch (target_util.classifyCompilerRtLibName(target, lib_name)) {
3804 .none => {},
3805 .only_libunwind, .both => {
3806 create_module.opts.link_libunwind = true;
3820 // Preserves linker input order.
3821 var unresolved_link_inputs: std.ArrayListUnmanaged(link.UnresolvedInput) = .empty;
3822 try unresolved_link_inputs.ensureUnusedCapacity(arena, create_module.cli_link_inputs.items.len);
3823 var any_name_queries_remaining = false;
3824 for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) {
3825 .name_query => |nq| {
3826 const lib_name = nq.name;
3827 if (std.zig.target.isLibCLibName(target, lib_name)) {
3828 create_module.opts.link_libc = true;
38073829 continue;
3808 },
3809 .only_compiler_rt => {
3810 warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});
3830 }
3831 if (std.zig.target.isLibCxxLibName(target, lib_name)) {
3832 create_module.opts.link_libcpp = true;
38113833 continue;
3812 },
3813 }
3834 }
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()) {
3816 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}", .{
3818 @errorName(err),
3819 });
3820 };
3821 if (exists) {
3822 try create_module.resolved_system_libs.append(arena, .{
3823 .name = lib_name,
3824 .lib = .{
3825 .needed = true,
3826 .weak = false,
3827 .path = null,
3828 },
3829 });
3830 continue;
3847 if (target.isMinGW()) {
3848 const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| {
3849 fatal("failed to check zig installation for DLL import libs: {s}", .{
3850 @errorName(err),
3851 });
3852 };
3853 if (exists) {
3854 try create_module.windows_libs.put(arena, lib_name, {});
3855 continue;
3856 }
38313857 }
3832 }
38333858
3834 if (fs.path.isAbsolute(lib_name)) {
3835 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
3836 }
3859 if (fs.path.isAbsolute(lib_name)) {
3860 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
3861 }
38373862
3838 if (target.os.tag == .wasi) {
3839 if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| {
3840 try create_module.wasi_emulated_libs.append(arena, crt_file);
3841 continue;
3863 if (target.os.tag == .wasi) {
3864 if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| {
3865 try create_module.wasi_emulated_libs.append(arena, crt_file);
3866 continue;
3867 }
38423868 }
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 = .{
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;
3877 if (any_name_queries_remaining) create_module.want_native_include_dirs = true;
38533878
38543879 // Resolve the library path arguments with respect to sysroot.
3880 try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len);
38553881 if (create_module.sysroot) |root| {
3856 try create_module.lib_dirs.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len * 2);
3857 for (create_module.lib_dir_args.items) |dir| {
3858 if (fs.path.isAbsolute(dir)) {
3859 const stripped_dir = dir[fs.path.diskDesignator(dir).len..];
3882 for (create_module.lib_dir_args.items) |lib_dir_arg| {
3883 if (fs.path.isAbsolute(lib_dir_arg)) {
3884 const stripped_dir = lib_dir_arg[fs.path.diskDesignator(lib_dir_arg).len..];
38603885 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 });
38623901 }
3863 create_module.lib_dirs.appendAssumeCapacity(dir);
38643902 }
38653903 } 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 }
38673913 }
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
38703916 if (resolved_target.is_native_os and target.isDarwin()) {
38713917 // If we want to link against frameworks, we need system headers.
......@@ -3874,7 +3920,10 @@ fn createModule(
38743920 }
38753921
38763922 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 }
38783927 }
38793928
38803929 // Trigger native system library path detection if necessary.
......@@ -3892,8 +3941,18 @@ fn createModule(
38923941 create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena);
38933942
38943943 try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items);
3895 try create_module.lib_dirs.appendSlice(arena, paths.lib_dirs.items);
38963944 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 }
38973956 }
38983957
38993958 if (create_module.libc_paths_file) |paths_file| {
......@@ -3905,7 +3964,7 @@ fn createModule(
39053964 }
39063965
39073966 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)
39093968 {
39103969 if (create_module.libc_installation == null) {
39113970 create_module.libc_installation = LibCInstallation.findNative(.{
......@@ -3916,204 +3975,32 @@ fn createModule(
39163975 fatal("unable to find native libc installation: {s}", .{@errorName(err)});
39173976 };
39183977
3919 try create_module.lib_dirs.appendSlice(arena, &.{
3978 try create_module.lib_directories.appendSlice(arena, &.{
39203979 create_module.libc_installation.?.msvc_lib_dir.?,
39213980 create_module.libc_installation.?.kernel32_lib_dir.?,
39223981 });
39233982 }
39243983 }
39253984
3926 // If any libs in this list are statically provided, we omit them from the
3927 // resolved list and populate the link_objects array instead.
3928 {
3929 var test_path: std.ArrayListUnmanaged(u8) = .empty;
3930 defer test_path.deinit(gpa);
3931
3932 var checked_paths: std.ArrayListUnmanaged(u8) = .empty;
3933 defer checked_paths.deinit(gpa);
3934
3935 var ld_script_bytes: std.ArrayListUnmanaged(u8) = .empty;
3936 defer ld_script_bytes.deinit(gpa);
3937
3938 var failed_libs: std.ArrayListUnmanaged(struct {
3939 name: []const u8,
3940 strategy: SystemLib.SearchStrategy,
3941 checked_paths: []const u8,
3942 preferred_mode: std.builtin.LinkMode,
3943 }) = .empty;
3944
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;
3985 // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`.
3986 link.resolveInputs(
3987 gpa,
3988 arena,
3989 target,
3990 &unresolved_link_inputs,
3991 &create_module.link_inputs,
3992 create_module.lib_directories.items,
3993 color,
3994 ) catch |err| fatal("failed to resolve link inputs: {s}", .{@errorName(err)});
3995
3996 if (create_module.windows_libs.count() != 0) create_module.opts.any_dyn_libs = true;
3997 if (!create_module.opts.any_dyn_libs) for (create_module.link_inputs.items) |item| switch (item) {
3998 .dso, .dso_exact => {
3999 create_module.opts.any_dyn_libs = true;
4000 break;
4001 },
4002 else => {},
4003 };
41174004
41184005 create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) {
41194006 error.WasiExecModelRequiresWasi => fatal("only WASI OS targets support execution model", .{}),
......@@ -4181,7 +4068,7 @@ fn createModule(
41814068 for (cli_mod.deps) |dep| {
41824069 const dep_index = create_module.modules.getIndex(dep.value) orelse
41834070 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);
41854072 try mod.deps.put(arena, dep.key, dep_mod);
41864073 }
41874074
......@@ -5046,7 +4933,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
50464933
50474934 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| .{
50504937 .path = lib_dir,
50514938 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
50524939 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 {
50654952 });
50664953 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: {
50694956 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
50704957 break :l .{
50714958 .handle = try fs.cwd().makeOpenPath(p, .{}),
......@@ -5076,7 +4963,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
50764963
50774964 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: {
50804967 if (override_local_cache_dir) |local_cache_dir_path| {
50814968 break :l .{
50824969 .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}),
......@@ -5510,7 +5397,7 @@ fn jitCmd(
55105397 const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
55115398 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| .{
55145401 .path = lib_dir,
55155402 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
55165403 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });
......@@ -5520,7 +5407,7 @@ fn jitCmd(
55205407 };
55215408 defer zig_lib_directory.handle.close();
55225409
5523 var global_cache_directory: Compilation.Directory = l: {
5410 var global_cache_directory: Directory = l: {
55245411 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
55255412 break :l .{
55265413 .handle = try fs.cwd().makeOpenPath(p, .{}),
......@@ -6907,197 +6794,6 @@ const ClangSearchSanitizer = struct {
69076794 };
69086795};
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
71016797fn accessFrameworkPath(
71026798 test_path: *std.ArrayList(u8),
71036799 checked_paths: *std.ArrayList(u8),
......@@ -7218,7 +6914,7 @@ fn cmdFetch(
72186914 });
72196915 defer root_prog_node.end();
72206916
7221 var global_cache_directory: Compilation.Directory = l: {
6917 var global_cache_directory: Directory = l: {
72226918 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
72236919 break :l .{
72246920 .handle = try fs.cwd().makeOpenPath(p, .{}),
......@@ -7795,18 +7491,3 @@ fn handleModArg(
77957491 c_source_files_owner_index.* = create_module.c_source_files.items.len;
77967492 rc_source_files_owner_index.* = create_module.rc_source_files.items.len;
77977493}
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}