authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-23 22:56:04-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-10-23 22:56:04-07:00
logc563ba6b15b65ecdc1cb538c9437e11dfb330453
tree99dd968efc3daea52a1d3628b7d8cedba53e84b7
parent33d07f4b6efe461ee3fbfa32cb18f60aac8c2827
parent4bdc2d38717b5655acd862a5762e069419b158c7
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21700 from ziglang/cli-lib-dirs

move linker input file parsing to the frontend

40 files changed, 3260 insertions(+), 2665 deletions(-)

CMakeLists.txt+2-1
......@@ -522,6 +522,7 @@ set(ZIG_STAGE2_SOURCES
522522 src/Sema.zig
523523 src/Sema/bitcast.zig
524524 src/Sema/comptime_ptr_access.zig
525 src/ThreadSafeQueue.zig
525526 src/Type.zig
526527 src/Value.zig
527528 src/Zcu.zig
......@@ -601,7 +602,6 @@ set(ZIG_STAGE2_SOURCES
601602 src/link/Elf/Archive.zig
602603 src/link/Elf/Atom.zig
603604 src/link/Elf/AtomList.zig
604 src/link/Elf/LdScript.zig
605605 src/link/Elf/LinkerDefined.zig
606606 src/link/Elf/Merge.zig
607607 src/link/Elf/Object.zig
......@@ -615,6 +615,7 @@ set(ZIG_STAGE2_SOURCES
615615 src/link/Elf/relocatable.zig
616616 src/link/Elf/relocation.zig
617617 src/link/Elf/synthetic_sections.zig
618 src/link/LdScript.zig
618619 src/link/MachO.zig
619620 src/link/MachO/Archive.zig
620621 src/link/MachO/Atom.zig
lib/c.zig-4
......@@ -38,8 +38,6 @@ comptime {
3838 @export(&strncpy, .{ .name = "strncpy", .linkage = .strong });
3939 @export(&strcat, .{ .name = "strcat", .linkage = .strong });
4040 @export(&strncat, .{ .name = "strncat", .linkage = .strong });
41 } else if (is_msvc) {
42 @export(&_fltused, .{ .name = "_fltused", .linkage = .strong });
4341 }
4442}
4543
......@@ -62,8 +60,6 @@ fn wasm_start() callconv(.C) void {
6260 _ = main(0, undefined);
6361}
6462
65var _fltused: c_int = 1;
66
6763fn strcpy(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 {
6864 var i: usize = 0;
6965 while (src[i] != 0) : (i += 1) {
lib/compiler/build_runner.zig+6
......@@ -280,6 +280,10 @@ pub fn main() !void {
280280 builder.enable_darling = true;
281281 } else if (mem.eql(u8, arg, "-fno-darling")) {
282282 builder.enable_darling = false;
283 } else if (mem.eql(u8, arg, "-fallow-so-scripts")) {
284 graph.allow_so_scripts = true;
285 } else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {
286 graph.allow_so_scripts = false;
283287 } else if (mem.eql(u8, arg, "-freference-trace")) {
284288 builder.reference_trace = 256;
285289 } else if (mem.startsWith(u8, arg, "-freference-trace=")) {
......@@ -1341,6 +1345,8 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
13411345 \\Advanced Options:
13421346 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
13431347 \\ -fno-reference-trace Disable reference trace
1348 \\ -fallow-so-scripts Allows .so files to be GNU ld scripts
1349 \\ -fno-allow-so-scripts (default) .so files must be ELF files
13441350 \\ --build-file [file] Override path to build.zig
13451351 \\ --cache-dir [path] Override path to local Zig cache directory
13461352 \\ --global-cache-dir [path] Override path to global Zig cache directory
lib/compiler_rt.zig+8-1
......@@ -1,6 +1,7 @@
11const builtin = @import("builtin");
2const common = @import("compiler_rt/common.zig");
23
3pub const panic = @import("compiler_rt/common.zig").panic;
4pub const panic = common.panic;
45
56comptime {
67 // Integer routines
......@@ -236,4 +237,10 @@ comptime {
236237 _ = @import("compiler_rt/bcmp.zig");
237238 _ = @import("compiler_rt/ssp.zig");
238239 }
240
241 if (!builtin.link_libc and builtin.abi == .msvc) {
242 @export(&_fltused, .{ .name = "_fltused", .linkage = common.linkage, .visibility = common.visibility });
243 }
239244}
245
246var _fltused: c_int = 1;
lib/std/Build.zig+1
......@@ -123,6 +123,7 @@ pub const Graph = struct {
123123 incremental: ?bool = null,
124124 random_seed: u32 = 0,
125125 dependency_cache: InitializedDepMap = .empty,
126 allow_so_scripts: ?bool = null,
126127};
127128
128129const AvailableDeps = []const struct { []const u8, []const u8 };
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
lib/std/Build/Step/Compile.zig+42-8
......@@ -186,6 +186,15 @@ want_lto: ?bool = null,
186186use_llvm: ?bool,
187187use_lld: ?bool,
188188
189/// Corresponds to the `-fallow-so-scripts` / `-fno-allow-so-scripts` CLI
190/// flags, overriding the global user setting provided to the `zig build`
191/// command.
192///
193/// The compiler defaults this value to off so that users whose system shared
194/// libraries are all ELF files don't have to pay the cost of checking every
195/// file to find out if it is a text file instead.
196allow_so_scripts: ?bool = null,
197
189198/// This is an advanced setting that can change the intent of this Compile step.
190199/// If this value is non-null, it means that this Compile step exists to
191200/// check for compile errors and return *success* if they match, and failure
......@@ -236,6 +245,7 @@ pub const ExpectedCompileErrors = union(enum) {
236245 contains: []const u8,
237246 exact: []const []const u8,
238247 starts_with: []const u8,
248 stderr_contains: []const u8,
239249};
240250
241251pub const Entry = union(enum) {
......@@ -1035,6 +1045,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
10351045 if (b.reference_trace) |some| {
10361046 try zig_args.append(try std.fmt.allocPrint(arena, "-freference-trace={d}", .{some}));
10371047 }
1048 try addFlag(&zig_args, "allow-so-scripts", compile.allow_so_scripts orelse b.graph.allow_so_scripts);
10381049
10391050 try addFlag(&zig_args, "llvm", compile.use_llvm);
10401051 try addFlag(&zig_args, "lld", compile.use_lld);
......@@ -1945,24 +1956,24 @@ fn checkCompileErrors(compile: *Compile) !void {
19451956
19461957 const arena = compile.step.owner.allocator;
19471958
1948 var actual_stderr_list = std.ArrayList(u8).init(arena);
1959 var actual_errors_list = std.ArrayList(u8).init(arena);
19491960 try actual_eb.renderToWriter(.{
19501961 .ttyconf = .no_color,
19511962 .include_reference_trace = false,
19521963 .include_source_line = false,
1953 }, actual_stderr_list.writer());
1954 const actual_stderr = try actual_stderr_list.toOwnedSlice();
1964 }, actual_errors_list.writer());
1965 const actual_errors = try actual_errors_list.toOwnedSlice();
19551966
19561967 // Render the expected lines into a string that we can compare verbatim.
19571968 var expected_generated = std.ArrayList(u8).init(arena);
19581969 const expect_errors = compile.expect_errors.?;
19591970
1960 var actual_line_it = mem.splitScalar(u8, actual_stderr, '\n');
1971 var actual_line_it = mem.splitScalar(u8, actual_errors, '\n');
19611972
19621973 // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile
19631974 switch (expect_errors) {
19641975 .starts_with => |expect_starts_with| {
1965 if (std.mem.startsWith(u8, actual_stderr, expect_starts_with)) return;
1976 if (std.mem.startsWith(u8, actual_errors, expect_starts_with)) return;
19661977 return compile.step.fail(
19671978 \\
19681979 \\========= should start with: ============
......@@ -1970,7 +1981,7 @@ fn checkCompileErrors(compile: *Compile) !void {
19701981 \\========= but not found: ================
19711982 \\{s}
19721983 \\=========================================
1973 , .{ expect_starts_with, actual_stderr });
1984 , .{ expect_starts_with, actual_errors });
19741985 },
19751986 .contains => |expect_line| {
19761987 while (actual_line_it.next()) |actual_line| {
......@@ -1978,6 +1989,29 @@ fn checkCompileErrors(compile: *Compile) !void {
19781989 return;
19791990 }
19801991
1992 return compile.step.fail(
1993 \\
1994 \\========= should contain: ===============
1995 \\{s}
1996 \\========= but not found: ================
1997 \\{s}
1998 \\=========================================
1999 , .{ expect_line, actual_errors });
2000 },
2001 .stderr_contains => |expect_line| {
2002 const actual_stderr: []const u8 = if (compile.step.result_error_msgs.items.len > 0)
2003 compile.step.result_error_msgs.items[0]
2004 else
2005 &.{};
2006 compile.step.result_error_msgs.clearRetainingCapacity();
2007
2008 var stderr_line_it = mem.splitScalar(u8, actual_stderr, '\n');
2009
2010 while (stderr_line_it.next()) |actual_line| {
2011 if (!matchCompileError(actual_line, expect_line)) continue;
2012 return;
2013 }
2014
19812015 return compile.step.fail(
19822016 \\
19832017 \\========= should contain: ===============
......@@ -2003,7 +2037,7 @@ fn checkCompileErrors(compile: *Compile) !void {
20032037 try expected_generated.append('\n');
20042038 }
20052039
2006 if (mem.eql(u8, expected_generated.items, actual_stderr)) return;
2040 if (mem.eql(u8, expected_generated.items, actual_errors)) return;
20072041
20082042 return compile.step.fail(
20092043 \\
......@@ -2012,7 +2046,7 @@ fn checkCompileErrors(compile: *Compile) !void {
20122046 \\========= but found: ====================
20132047 \\{s}
20142048 \\=========================================
2015 , .{ expected_generated.items, actual_stderr });
2049 , .{ expected_generated.items, actual_errors });
20162050 },
20172051 }
20182052}
lib/std/Thread/WaitGroup.zig+5
......@@ -14,6 +14,11 @@ pub fn start(self: *WaitGroup) void {
1414 assert((state / one_pending) < (std.math.maxInt(usize) / one_pending));
1515}
1616
17pub fn startMany(self: *WaitGroup, n: usize) void {
18 const state = self.state.fetchAdd(one_pending * n, .monotonic);
19 assert((state / one_pending) < (std.math.maxInt(usize) / one_pending));
20}
21
1722pub fn finish(self: *WaitGroup) void {
1823 const state = self.state.fetchSub(one_pending, .acq_rel);
1924 assert((state / one_pending) > 0);
src/Compilation.zig+297-388
......@@ -10,6 +10,7 @@ const Target = std.Target;
1010const ThreadPool = std.Thread.Pool;
1111const WaitGroup = std.Thread.WaitGroup;
1212const ErrorBundle = std.zig.ErrorBundle;
13const Path = Cache.Path;
1314
1415const Value = @import("Value.zig");
1516const Type = @import("Type.zig");
......@@ -39,15 +40,17 @@ const Air = @import("Air.zig");
3940const Builtin = @import("Builtin.zig");
4041const LlvmObject = @import("codegen/llvm.zig").Object;
4142const dev = @import("dev.zig");
42pub const Directory = Cache.Directory;
43const Path = Cache.Path;
43const ThreadSafeQueue = @import("ThreadSafeQueue.zig").ThreadSafeQueue;
4444
45pub const Directory = Cache.Directory;
4546pub const Config = @import("Compilation/Config.zig");
4647
4748/// General-purpose allocator. Used for both temporary and long-term storage.
4849gpa: Allocator,
4950/// Arena-allocated memory, mostly used during initialization. However, it can
5051/// be used for other things requiring the same lifetime as the `Compilation`.
52/// Not thread-safe - lock `mutex` if potentially accessing from multiple
53/// threads at once.
5154arena: Allocator,
5255/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
5356zcu: ?*Zcu,
......@@ -76,12 +79,13 @@ implib_emit: ?Path,
7679docs_emit: ?Path,
7780root_name: [:0]const u8,
7881include_compiler_rt: bool,
79objects: []Compilation.LinkObject,
82/// Resolved into known paths, any GNU ld scripts already resolved.
83link_inputs: []const link.Input,
8084/// Needed only for passing -F args to clang.
8185framework_dirs: []const []const u8,
82/// These are *always* dynamically linked. Static libraries will be
83/// provided as positional arguments.
84system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
86/// These are only for DLLs dependencies fulfilled by the `.def` files shipped
87/// with Zig. Static libraries are provided as `link.Input` values.
88windows_libs: std.StringArrayHashMapUnmanaged(void),
8589version: ?std.SemanticVersion,
8690libc_installation: ?*const LibCInstallation,
8791skip_linker_dependencies: bool,
......@@ -107,6 +111,9 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa
107111} = .{},
108112
109113link_diags: link.Diags,
114link_task_queue: ThreadSafeQueue(link.Task) = .empty,
115/// Ensure only 1 simultaneous call to `flushTaskQueue`.
116link_task_queue_safety: std.debug.SafetyLock = .{},
110117
111118work_queues: [
112119 len: {
......@@ -118,14 +125,6 @@ work_queues: [
118125 }
119126]std.fifo.LinearFifo(Job, .Dynamic),
120127
121codegen_work: if (InternPool.single_threaded) void else struct {
122 mutex: std.Thread.Mutex,
123 cond: std.Thread.Condition,
124 queue: std.fifo.LinearFifo(CodegenJob, .Dynamic),
125 job_error: ?JobError,
126 done: bool,
127},
128
129128/// These jobs are to invoke the Clang compiler to create an object file, which
130129/// gets linked with the Compilation.
131130c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic),
......@@ -262,6 +261,9 @@ emit_asm: ?EmitLoc,
262261emit_llvm_ir: ?EmitLoc,
263262emit_llvm_bc: ?EmitLoc,
264263
264link_task_wait_group: WaitGroup = .{},
265work_queue_progress_node: std.Progress.Node = .none,
266
265267llvm_opt_bisect_limit: c_int,
266268
267269file_system_inputs: ?*std.ArrayListUnmanaged(u8),
......@@ -339,16 +341,14 @@ pub const RcIncludes = enum {
339341};
340342
341343const Job = union(enum) {
342 /// Write the constant value for a Decl to the output file.
344 /// Corresponds to the task in `link.Task`.
345 /// Only needed for backends that haven't yet been updated to not race against Sema.
343346 codegen_nav: InternPool.Nav.Index,
344 /// Write the machine code for a function to the output file.
345 codegen_func: struct {
346 /// This will either be a non-generic `func_decl` or a `func_instance`.
347 func: InternPool.Index,
348 /// This `Air` is owned by the `Job` and allocated with `gpa`.
349 /// It must be deinited when the job is processed.
350 air: Air,
351 },
347 /// Corresponds to the task in `link.Task`.
348 /// Only needed for backends that haven't yet been updated to not race against Sema.
349 codegen_func: link.Task.CodegenFunc,
350 /// Corresponds to the task in `link.Task`.
351 /// Only needed for backends that haven't yet been updated to not race against Sema.
352352 codegen_type: InternPool.Index,
353353 /// The `Cau` must be semantically analyzed (and possibly export itself).
354354 /// This may be its first time being analyzed, or it may be outdated.
......@@ -357,9 +357,6 @@ const Job = union(enum) {
357357 /// After analysis, a `codegen_func` job will be queued.
358358 /// These must be separate jobs to ensure any needed type resolution occurs *before* codegen.
359359 analyze_func: InternPool.Index,
360 /// The source file containing the Decl has been updated, and so the
361 /// Decl may need its line number information updated in the debug info.
362 update_line_number: void, // TODO
363360 /// The main source file for the module needs to be analyzed.
364361 analyze_mod: *Package.Module,
365362 /// Fully resolve the given `struct` or `union` type.
......@@ -373,6 +370,7 @@ const Job = union(enum) {
373370 musl_crt_file: musl.CrtFile,
374371 /// one of the mingw-w64 static objects
375372 mingw_crt_file: mingw.CrtFile,
373
376374 /// libunwind.a, usually needed when linking libc
377375 libunwind: void,
378376 libcxx: void,
......@@ -384,7 +382,7 @@ const Job = union(enum) {
384382 /// one of WASI libc static objects
385383 wasi_libc_crt_file: wasi_libc.CrtFile,
386384
387 /// The value is the index into `system_libs`.
385 /// The value is the index into `windows_libs`.
388386 windows_import_lib: usize,
389387
390388 const Tag = @typeInfo(Job).@"union".tag_type.?;
......@@ -402,17 +400,6 @@ const Job = union(enum) {
402400 }
403401};
404402
405const CodegenJob = union(enum) {
406 nav: InternPool.Nav.Index,
407 func: struct {
408 func: InternPool.Index,
409 /// This `Air` is owned by the `Job` and allocated with `gpa`.
410 /// It must be deinited when the job is processed.
411 air: Air,
412 },
413 type: InternPool.Index,
414};
415
416403pub const CObject = struct {
417404 /// Relative to cwd. Owned by arena.
418405 src: CSourceFile,
......@@ -999,24 +986,6 @@ const CacheUse = union(CacheMode) {
999986 }
1000987};
1001988
1002pub const LinkObject = struct {
1003 path: Path,
1004 must_link: bool = false,
1005 needed: bool = false,
1006 // When the library is passed via a positional argument, it will be
1007 // added as a full path. If it's `-l<lib>`, then just the basename.
1008 //
1009 // Consistent with `withLOption` variable name in lld ELF driver.
1010 loption: bool = false,
1011
1012 pub fn isObject(lo: LinkObject) bool {
1013 return switch (classifyFileExt(lo.path.sub_path)) {
1014 .object => true,
1015 else => false,
1016 };
1017 }
1018};
1019
1020989pub const CreateOptions = struct {
1021990 zig_lib_directory: Directory,
1022991 local_cache_directory: Directory,
......@@ -1061,18 +1030,20 @@ pub const CreateOptions = struct {
10611030 /// this flag would be set to disable this machinery to avoid false positives.
10621031 disable_lld_caching: bool = false,
10631032 cache_mode: CacheMode = .incremental,
1064 lib_dirs: []const []const u8 = &[0][]const u8{},
1033 /// This field is intended to be removed.
1034 /// The ELF implementation no longer uses this data, however the MachO and COFF
1035 /// implementations still do.
1036 lib_directories: []const Directory = &.{},
10651037 rpath_list: []const []const u8 = &[0][]const u8{},
10661038 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty,
10671039 c_source_files: []const CSourceFile = &.{},
10681040 rc_source_files: []const RcSourceFile = &.{},
10691041 manifest_file: ?[]const u8 = null,
10701042 rc_includes: RcIncludes = .any,
1071 link_objects: []LinkObject = &[0]LinkObject{},
1043 link_inputs: []const link.Input = &.{},
10721044 framework_dirs: []const []const u8 = &[0][]const u8{},
10731045 frameworks: []const Framework = &.{},
1074 system_lib_names: []const []const u8 = &.{},
1075 system_lib_infos: []const SystemLib = &.{},
1046 windows_lib_names: []const []const u8 = &.{},
10761047 /// These correspond to the WASI libc emulated subcomponents including:
10771048 /// * process clocks
10781049 /// * getpid
......@@ -1455,12 +1426,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
14551426 };
14561427 errdefer if (opt_zcu) |zcu| zcu.deinit();
14571428
1458 var system_libs = try std.StringArrayHashMapUnmanaged(SystemLib).init(
1459 gpa,
1460 options.system_lib_names,
1461 options.system_lib_infos,
1462 );
1463 errdefer system_libs.deinit(gpa);
1429 var windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{});
1430 errdefer windows_libs.deinit(gpa);
14641431
14651432 comp.* = .{
14661433 .gpa = gpa,
......@@ -1479,13 +1446,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
14791446 .emit_llvm_ir = options.emit_llvm_ir,
14801447 .emit_llvm_bc = options.emit_llvm_bc,
14811448 .work_queues = .{std.fifo.LinearFifo(Job, .Dynamic).init(gpa)} ** @typeInfo(std.meta.FieldType(Compilation, .work_queues)).array.len,
1482 .codegen_work = if (InternPool.single_threaded) {} else .{
1483 .mutex = .{},
1484 .cond = .{},
1485 .queue = std.fifo.LinearFifo(CodegenJob, .Dynamic).init(gpa),
1486 .job_error = null,
1487 .done = false,
1488 },
14891449 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),
14901450 .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa) else .{},
14911451 .astgen_work_queue = std.fifo.LinearFifo(Zcu.File.Index, .Dynamic).init(gpa),
......@@ -1522,11 +1482,11 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
15221482 .libcxx_abi_version = options.libcxx_abi_version,
15231483 .root_name = root_name,
15241484 .sysroot = sysroot,
1525 .system_libs = system_libs,
1485 .windows_libs = windows_libs,
15261486 .version = options.version,
15271487 .libc_installation = libc_dirs.libc_installation,
15281488 .include_compiler_rt = include_compiler_rt,
1529 .objects = options.link_objects,
1489 .link_inputs = options.link_inputs,
15301490 .framework_dirs = options.framework_dirs,
15311491 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
15321492 .skip_linker_dependencies = options.skip_linker_dependencies,
......@@ -1564,7 +1524,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
15641524 .z_max_page_size = options.linker_z_max_page_size,
15651525 .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,
15661526 .frameworks = options.frameworks,
1567 .lib_dirs = options.lib_dirs,
1527 .lib_directories = options.lib_directories,
15681528 .framework_dirs = options.framework_dirs,
15691529 .rpath_list = options.rpath_list,
15701530 .symbol_wrap_set = options.symbol_wrap_set,
......@@ -1776,157 +1736,175 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
17761736 .incremental => comp.bin_file != null,
17771737 };
17781738
1779 if (have_bin_emit and !comp.skip_linker_dependencies and target.ofmt != .c) {
1780 if (target.isDarwin()) {
1781 switch (target.abi) {
1782 .none,
1783 .simulator,
1784 .macabi,
1785 => {},
1786 else => return error.LibCUnavailable,
1787 }
1788 }
1789 // If we need to build glibc for the target, add work items for it.
1790 // We go through the work queue so that building can be done in parallel.
1791 if (comp.wantBuildGLibCFromSource()) {
1792 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
1793
1794 if (glibc.needsCrtiCrtn(target)) {
1795 try comp.queueJobs(&[_]Job{
1796 .{ .glibc_crt_file = .crti_o },
1797 .{ .glibc_crt_file = .crtn_o },
1798 });
1799 }
1800 try comp.queueJobs(&[_]Job{
1801 .{ .glibc_crt_file = .scrt1_o },
1802 .{ .glibc_crt_file = .libc_nonshared_a },
1803 .{ .glibc_shared_objects = {} },
1804 });
1805 }
1806 if (comp.wantBuildMuslFromSource()) {
1807 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
1808
1809 if (musl.needsCrtiCrtn(target)) {
1810 try comp.queueJobs(&[_]Job{
1811 .{ .musl_crt_file = .crti_o },
1812 .{ .musl_crt_file = .crtn_o },
1813 });
1814 }
1815 try comp.queueJobs(&[_]Job{
1816 .{ .musl_crt_file = .crt1_o },
1817 .{ .musl_crt_file = .scrt1_o },
1818 .{ .musl_crt_file = .rcrt1_o },
1819 switch (comp.config.link_mode) {
1820 .static => .{ .musl_crt_file = .libc_a },
1821 .dynamic => .{ .musl_crt_file = .libc_so },
1822 },
1823 });
1824 }
1739 if (have_bin_emit and target.ofmt != .c) {
1740 if (!comp.skip_linker_dependencies) {
1741 // If we need to build libc for the target, add work items for it.
1742 // We go through the work queue so that building can be done in parallel.
1743 // If linking against host libc installation, instead queue up jobs
1744 // for loading those files in the linker.
1745 if (comp.config.link_libc and is_exe_or_dyn_lib) {
1746 // If the "is darwin" check is moved below the libc_installation check below,
1747 // error.LibCInstallationMissingCrtDir is returned from lci.resolveCrtPaths().
1748 if (target.isDarwin()) {
1749 switch (target.abi) {
1750 .none, .simulator, .macabi => {},
1751 else => return error.LibCUnavailable,
1752 }
1753 // TODO delete logic from MachO flush() and queue up tasks here instead.
1754 } else if (comp.libc_installation) |lci| {
1755 const basenames = LibCInstallation.CrtBasenames.get(.{
1756 .target = target,
1757 .link_libc = comp.config.link_libc,
1758 .output_mode = comp.config.output_mode,
1759 .link_mode = comp.config.link_mode,
1760 .pie = comp.config.pie,
1761 });
1762 const paths = try lci.resolveCrtPaths(arena, basenames, target);
18251763
1826 if (comp.wantBuildWasiLibcFromSource()) {
1827 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
1764 const fields = @typeInfo(@TypeOf(paths)).@"struct".fields;
1765 try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, fields.len + 1);
1766 inline for (fields) |field| {
1767 if (@field(paths, field.name)) |path| {
1768 comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path });
1769 }
1770 }
1771 // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`.
1772 comp.link_task_queue.shared.appendAssumeCapacity(.load_host_libc);
1773 } else if (target.isMusl() and !target.isWasm()) {
1774 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
1775
1776 if (musl.needsCrtiCrtn(target)) {
1777 try comp.queueJobs(&[_]Job{
1778 .{ .musl_crt_file = .crti_o },
1779 .{ .musl_crt_file = .crtn_o },
1780 });
1781 }
1782 if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| {
1783 try comp.queueJobs(&.{.{ .musl_crt_file = f }});
1784 }
1785 try comp.queueJobs(&.{.{ .musl_crt_file = switch (comp.config.link_mode) {
1786 .static => .libc_a,
1787 .dynamic => .libc_so,
1788 } }});
1789 } else if (target.isGnuLibC()) {
1790 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
1791
1792 if (glibc.needsCrtiCrtn(target)) {
1793 try comp.queueJobs(&[_]Job{
1794 .{ .glibc_crt_file = .crti_o },
1795 .{ .glibc_crt_file = .crtn_o },
1796 });
1797 }
1798 if (!is_dyn_lib) {
1799 try comp.queueJob(.{ .glibc_crt_file = .scrt1_o });
1800 }
1801 try comp.queueJobs(&[_]Job{
1802 .{ .glibc_shared_objects = {} },
1803 .{ .glibc_crt_file = .libc_nonshared_a },
1804 });
1805 } else if (target.isWasm() and target.os.tag == .wasi) {
1806 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
18281807
1829 for (comp.wasi_emulated_libs) |crt_file| {
1830 try comp.queueJob(.{
1831 .wasi_libc_crt_file = crt_file,
1832 });
1833 }
1834 try comp.queueJobs(&[_]Job{
1835 .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) },
1836 .{ .wasi_libc_crt_file = .libc_a },
1837 });
1838 }
1808 for (comp.wasi_emulated_libs) |crt_file| {
1809 try comp.queueJob(.{
1810 .wasi_libc_crt_file = crt_file,
1811 });
1812 }
1813 try comp.queueJobs(&[_]Job{
1814 .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) },
1815 .{ .wasi_libc_crt_file = .libc_a },
1816 });
1817 } else if (target.isMinGW()) {
1818 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
18391819
1840 if (comp.wantBuildMinGWFromSource()) {
1841 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
1820 const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o };
1821 try comp.queueJobs(&.{
1822 .{ .mingw_crt_file = .mingw32_lib },
1823 crt_job,
1824 });
18421825
1843 const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o };
1844 try comp.queueJobs(&.{
1845 .{ .mingw_crt_file = .mingw32_lib },
1846 crt_job,
1847 });
1826 // When linking mingw-w64 there are some import libs we always need.
1827 try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);
1828 for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {});
1829 } else if (target.isDarwin()) {
1830 switch (target.abi) {
1831 .none, .simulator, .macabi => {},
1832 else => return error.LibCUnavailable,
1833 }
1834 } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) {
1835 try comp.queueJob(.{ .zig_libc = {} });
1836 } else {
1837 return error.LibCUnavailable;
1838 }
1839 }
18481840
1849 // When linking mingw-w64 there are some import libs we always need.
1850 for (mingw.always_link_libs) |name| {
1851 try comp.system_libs.put(comp.gpa, name, .{
1852 .needed = false,
1853 .weak = false,
1854 .path = null,
1855 });
1841 // Generate Windows import libs.
1842 if (target.os.tag == .windows) {
1843 const count = comp.windows_libs.count();
1844 for (0..count) |i| {
1845 try comp.queueJob(.{ .windows_import_lib = i });
1846 }
18561847 }
1857 }
1858 // Generate Windows import libs.
1859 if (target.os.tag == .windows) {
1860 const count = comp.system_libs.count();
1861 for (0..count) |i| {
1862 try comp.queueJob(.{ .windows_import_lib = i });
1848 if (comp.wantBuildLibUnwindFromSource()) {
1849 try comp.queueJob(.{ .libunwind = {} });
1850 }
1851 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
1852 try comp.queueJob(.libcxx);
1853 try comp.queueJob(.libcxxabi);
1854 }
1855 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) {
1856 try comp.queueJob(.libtsan);
18631857 }
1864 }
1865 if (comp.wantBuildLibUnwindFromSource()) {
1866 try comp.queueJob(.{ .libunwind = {} });
1867 }
1868 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
1869 try comp.queueJob(.libcxx);
1870 try comp.queueJob(.libcxxabi);
1871 }
1872 if (build_options.have_llvm and comp.config.any_sanitize_thread) {
1873 try comp.queueJob(.libtsan);
1874 }
18751858
1876 if (target.isMinGW() and comp.config.any_non_single_threaded) {
1877 // LLD might drop some symbols as unused during LTO and GCing, therefore,
1878 // we force mark them for resolution here.
1859 if (target.isMinGW() and comp.config.any_non_single_threaded) {
1860 // LLD might drop some symbols as unused during LTO and GCing, therefore,
1861 // we force mark them for resolution here.
18791862
1880 const tls_index_sym = switch (target.cpu.arch) {
1881 .x86 => "__tls_index",
1882 else => "_tls_index",
1883 };
1863 const tls_index_sym = switch (target.cpu.arch) {
1864 .x86 => "__tls_index",
1865 else => "_tls_index",
1866 };
18841867
1885 try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});
1886 }
1868 try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});
1869 }
18871870
1888 if (comp.include_compiler_rt and capable_of_building_compiler_rt) {
1889 if (is_exe_or_dyn_lib) {
1890 log.debug("queuing a job to build compiler_rt_lib", .{});
1891 comp.job_queued_compiler_rt_lib = true;
1892 } else if (output_mode != .Obj) {
1893 log.debug("queuing a job to build compiler_rt_obj", .{});
1894 // In this case we are making a static library, so we ask
1895 // for a compiler-rt object to put in it.
1896 comp.job_queued_compiler_rt_obj = true;
1871 if (comp.include_compiler_rt and capable_of_building_compiler_rt) {
1872 if (is_exe_or_dyn_lib) {
1873 log.debug("queuing a job to build compiler_rt_lib", .{});
1874 comp.job_queued_compiler_rt_lib = true;
1875 } else if (output_mode != .Obj) {
1876 log.debug("queuing a job to build compiler_rt_obj", .{});
1877 // In this case we are making a static library, so we ask
1878 // for a compiler-rt object to put in it.
1879 comp.job_queued_compiler_rt_obj = true;
1880 }
18971881 }
1898 }
18991882
1900 if (comp.config.any_fuzz and capable_of_building_compiler_rt) {
1901 if (is_exe_or_dyn_lib) {
1883 if (is_exe_or_dyn_lib and comp.config.any_fuzz and capable_of_building_compiler_rt) {
19021884 log.debug("queuing a job to build libfuzzer", .{});
19031885 comp.job_queued_fuzzer_lib = true;
19041886 }
19051887 }
19061888
1907 if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and
1908 !comp.config.link_libc and capable_of_building_zig_libc)
1909 {
1910 try comp.queueJob(.{ .zig_libc = {} });
1911 }
1889 try comp.link_task_queue.shared.append(gpa, .load_explicitly_provided);
19121890 }
19131891
19141892 return comp;
19151893}
19161894
19171895pub fn destroy(comp: *Compilation) void {
1896 const gpa = comp.gpa;
1897
19181898 if (comp.bin_file) |lf| lf.destroy();
19191899 if (comp.zcu) |zcu| zcu.deinit();
19201900 comp.cache_use.deinit();
19211901 for (comp.work_queues) |work_queue| work_queue.deinit();
1922 if (!InternPool.single_threaded) comp.codegen_work.queue.deinit();
19231902 comp.c_object_work_queue.deinit();
19241903 comp.win32_resource_work_queue.deinit();
19251904 comp.astgen_work_queue.deinit();
19261905 comp.embed_file_work_queue.deinit();
19271906
1928 const gpa = comp.gpa;
1929 comp.system_libs.deinit(gpa);
1907 comp.windows_libs.deinit(gpa);
19301908
19311909 {
19321910 var it = comp.crt_files.iterator();
......@@ -2559,12 +2537,7 @@ fn addNonIncrementalStuffToCacheManifest(
25592537 cache_helpers.addModule(&man.hash, comp.root_mod);
25602538 }
25612539
2562 for (comp.objects) |obj| {
2563 _ = try man.addFilePath(obj.path, null);
2564 man.hash.add(obj.must_link);
2565 man.hash.add(obj.needed);
2566 man.hash.add(obj.loption);
2567 }
2540 try link.hashInputs(man, comp.link_inputs);
25682541
25692542 for (comp.c_object_table.keys()) |key| {
25702543 _ = try man.addFile(key.src.src_path, null);
......@@ -2601,7 +2574,7 @@ fn addNonIncrementalStuffToCacheManifest(
26012574 man.hash.add(comp.rc_includes);
26022575 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
26032576 man.hash.addListOfBytes(comp.framework_dirs);
2604 try link.hashAddSystemLibs(man, comp.system_libs);
2577 man.hash.addListOfBytes(comp.windows_libs.keys());
26052578
26062579 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
26072580 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
......@@ -2620,12 +2593,16 @@ fn addNonIncrementalStuffToCacheManifest(
26202593 man.hash.addOptional(opts.image_base);
26212594 man.hash.addOptional(opts.gc_sections);
26222595 man.hash.add(opts.emit_relocs);
2623 man.hash.addListOfBytes(opts.lib_dirs);
2596 const target = comp.root_mod.resolved_target.result;
2597 if (target.ofmt == .macho or target.ofmt == .coff) {
2598 // TODO remove this, libraries need to be resolved by the frontend. this is already
2599 // done by ELF.
2600 for (opts.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path);
2601 }
26242602 man.hash.addListOfBytes(opts.rpath_list);
26252603 man.hash.addListOfBytes(opts.symbol_wrap_set.keys());
26262604 if (comp.config.link_libc) {
26272605 man.hash.add(comp.libc_installation != null);
2628 const target = comp.root_mod.resolved_target.result;
26292606 if (comp.libc_installation) |libc_installation| {
26302607 man.hash.addOptionalBytes(libc_installation.crt_dir);
26312608 if (target.abi == .msvc or target.abi == .itanium) {
......@@ -3219,18 +3196,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
32193196 }));
32203197 }
32213198
3222 for (comp.link_diags.msgs.items) |link_err| {
3223 try bundle.addRootErrorMessage(.{
3224 .msg = try bundle.addString(link_err.msg),
3225 .notes_len = @intCast(link_err.notes.len),
3226 });
3227 const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
3228 for (link_err.notes, 0..) |note, i| {
3229 bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
3230 .msg = try bundle.addString(note.msg),
3231 }));
3232 }
3233 }
3199 try comp.link_diags.addMessagesToBundle(&bundle);
32343200
32353201 if (comp.zcu) |zcu| {
32363202 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
......@@ -3482,6 +3448,9 @@ pub fn performAllTheWork(
34823448 comp: *Compilation,
34833449 main_progress_node: std.Progress.Node,
34843450) JobError!void {
3451 comp.work_queue_progress_node = main_progress_node;
3452 defer comp.work_queue_progress_node = .none;
3453
34853454 defer if (comp.zcu) |zcu| {
34863455 zcu.sema_prog_node.end();
34873456 zcu.sema_prog_node = std.Progress.Node.none;
......@@ -3491,7 +3460,6 @@ pub fn performAllTheWork(
34913460 zcu.generation += 1;
34923461 };
34933462 try comp.performAllTheWorkInner(main_progress_node);
3494 if (!InternPool.single_threaded) if (comp.codegen_work.job_error) |job_error| return job_error;
34953463}
34963464
34973465fn performAllTheWorkInner(
......@@ -3506,12 +3474,34 @@ fn performAllTheWorkInner(
35063474 var work_queue_wait_group: WaitGroup = .{};
35073475 defer work_queue_wait_group.wait();
35083476
3477 comp.link_task_wait_group.reset();
3478 defer comp.link_task_wait_group.wait();
3479
3480 if (comp.link_task_queue.start()) {
3481 comp.thread_pool.spawnWgId(&comp.link_task_wait_group, link.flushTaskQueue, .{comp});
3482 }
3483
35093484 if (comp.docs_emit != null) {
35103485 dev.check(.docs_emit);
35113486 comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp});
35123487 work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node });
35133488 }
35143489
3490 if (comp.job_queued_compiler_rt_lib) {
3491 comp.job_queued_compiler_rt_lib = false;
3492 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Lib, &comp.compiler_rt_lib, main_progress_node });
3493 }
3494
3495 if (comp.job_queued_compiler_rt_obj) {
3496 comp.job_queued_compiler_rt_obj = false;
3497 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Obj, &comp.compiler_rt_obj, main_progress_node });
3498 }
3499
3500 if (comp.job_queued_fuzzer_lib) {
3501 comp.job_queued_fuzzer_lib = false;
3502 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "fuzzer.zig", .libfuzzer, .Lib, &comp.fuzzer_lib, main_progress_node });
3503 }
3504
35153505 {
35163506 const astgen_frame = tracy.namedFrame("astgen");
35173507 defer astgen_frame.end();
......@@ -3574,22 +3564,18 @@ fn performAllTheWorkInner(
35743564 }
35753565
35763566 while (comp.c_object_work_queue.readItem()) |c_object| {
3577 comp.thread_pool.spawnWg(&work_queue_wait_group, workerUpdateCObject, .{
3567 comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateCObject, .{
35783568 comp, c_object, main_progress_node,
35793569 });
35803570 }
35813571
35823572 while (comp.win32_resource_work_queue.readItem()) |win32_resource| {
3583 comp.thread_pool.spawnWg(&work_queue_wait_group, workerUpdateWin32Resource, .{
3573 comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateWin32Resource, .{
35843574 comp, win32_resource, main_progress_node,
35853575 });
35863576 }
35873577 }
35883578
3589 if (comp.job_queued_compiler_rt_lib) work_queue_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Lib, &comp.compiler_rt_lib, main_progress_node });
3590 if (comp.job_queued_compiler_rt_obj) work_queue_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Obj, &comp.compiler_rt_obj, main_progress_node });
3591 if (comp.job_queued_fuzzer_lib) work_queue_wait_group.spawnManager(buildRt, .{ comp, "fuzzer.zig", .libfuzzer, .Lib, &comp.fuzzer_lib, main_progress_node });
3592
35933579 if (comp.zcu) |zcu| {
35943580 const pt: Zcu.PerThread = .{ .zcu = zcu, .tid = .main };
35953581 if (comp.incremental) {
......@@ -3604,18 +3590,12 @@ fn performAllTheWorkInner(
36043590 zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);
36053591 }
36063592
3607 if (!InternPool.single_threaded) {
3608 comp.codegen_work.done = false; // may be `true` from a prior update
3609 comp.thread_pool.spawnWgId(&work_queue_wait_group, codegenThread, .{comp});
3593 if (!comp.separateCodegenThreadOk()) {
3594 // Waits until all input files have been parsed.
3595 comp.link_task_wait_group.wait();
3596 comp.link_task_wait_group.reset();
3597 std.log.scoped(.link).debug("finished waiting for link_task_wait_group", .{});
36103598 }
3611 defer if (!InternPool.single_threaded) {
3612 {
3613 comp.codegen_work.mutex.lock();
3614 defer comp.codegen_work.mutex.unlock();
3615 comp.codegen_work.done = true;
3616 }
3617 comp.codegen_work.cond.signal();
3618 };
36193599
36203600 work: while (true) {
36213601 for (&comp.work_queues) |*work_queue| if (work_queue.readItem()) |job| {
......@@ -3659,16 +3639,14 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
36593639 }
36603640 }
36613641 assert(nav.status == .resolved);
3662 try comp.queueCodegenJob(tid, .{ .nav = nav_index });
3642 comp.dispatchCodegenTask(tid, .{ .codegen_nav = nav_index });
36633643 },
36643644 .codegen_func => |func| {
3665 // This call takes ownership of `func.air`.
3666 try comp.queueCodegenJob(tid, .{ .func = .{
3667 .func = func.func,
3668 .air = func.air,
3669 } });
3645 comp.dispatchCodegenTask(tid, .{ .codegen_func = func });
3646 },
3647 .codegen_type => |ty| {
3648 comp.dispatchCodegenTask(tid, .{ .codegen_type = ty });
36703649 },
3671 .codegen_type => |ty| try comp.queueCodegenJob(tid, .{ .type = ty }),
36723650 .analyze_func => |func| {
36733651 const named_frame = tracy.namedFrame("analyze_func");
36743652 defer named_frame.end();
......@@ -3715,31 +3693,6 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
37153693 error.AnalysisFail => return,
37163694 };
37173695 },
3718 .update_line_number => |decl_index| {
3719 const named_frame = tracy.namedFrame("update_line_number");
3720 defer named_frame.end();
3721
3722 if (true) @panic("TODO: update_line_number");
3723
3724 const gpa = comp.gpa;
3725 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
3726 const decl = pt.zcu.declPtr(decl_index);
3727 const lf = comp.bin_file.?;
3728 lf.updateDeclLineNumber(pt, decl_index) catch |err| {
3729 try pt.zcu.failed_analysis.ensureUnusedCapacity(gpa, 1);
3730 pt.zcu.failed_analysis.putAssumeCapacityNoClobber(
3731 InternPool.AnalUnit.wrap(.{ .decl = decl_index }),
3732 try Zcu.ErrorMsg.create(
3733 gpa,
3734 decl.navSrcLoc(pt.zcu),
3735 "unable to update line number: {s}",
3736 .{@errorName(err)},
3737 ),
3738 );
3739 decl.analysis = .codegen_failure;
3740 try pt.zcu.retryable_failures.append(gpa, InternPool.AnalUnit.wrap(.{ .decl = decl_index }));
3741 };
3742 },
37433696 .analyze_mod => |mod| {
37443697 const named_frame = tracy.namedFrame("analyze_mod");
37453698 defer named_frame.end();
......@@ -3804,7 +3757,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
38043757 const named_frame = tracy.namedFrame("windows_import_lib");
38053758 defer named_frame.end();
38063759
3807 const link_lib = comp.system_libs.keys()[index];
3760 const link_lib = comp.windows_libs.keys()[index];
38083761 mingw.buildImportLib(comp, link_lib) catch |err| {
38093762 // TODO Surface more error details.
38103763 comp.lockAndSetMiscFailure(
......@@ -3906,66 +3859,20 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
39063859 }
39073860}
39083861
3909fn queueCodegenJob(comp: *Compilation, tid: usize, codegen_job: CodegenJob) !void {
3910 if (InternPool.single_threaded or
3911 !comp.zcu.?.backendSupportsFeature(.separate_thread))
3912 return processOneCodegenJob(tid, comp, codegen_job);
3913
3914 {
3915 comp.codegen_work.mutex.lock();
3916 defer comp.codegen_work.mutex.unlock();
3917 try comp.codegen_work.queue.writeItem(codegen_job);
3862/// The reason for the double-queue here is that the first queue ensures any
3863/// resolve_type_fully tasks are complete before this dispatch function is called.
3864fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) void {
3865 if (comp.separateCodegenThreadOk()) {
3866 comp.queueLinkTasks(&.{link_task});
3867 } else {
3868 link.doTask(comp, tid, link_task);
39183869 }
3919 comp.codegen_work.cond.signal();
39203870}
39213871
3922fn codegenThread(tid: usize, comp: *Compilation) void {
3923 comp.codegen_work.mutex.lock();
3924 defer comp.codegen_work.mutex.unlock();
3925
3926 while (true) {
3927 if (comp.codegen_work.queue.readItem()) |codegen_job| {
3928 comp.codegen_work.mutex.unlock();
3929 defer comp.codegen_work.mutex.lock();
3930
3931 processOneCodegenJob(tid, comp, codegen_job) catch |job_error| {
3932 comp.codegen_work.job_error = job_error;
3933 break;
3934 };
3935 continue;
3936 }
3937
3938 if (comp.codegen_work.done) break;
3939
3940 comp.codegen_work.cond.wait(&comp.codegen_work.mutex);
3941 }
3942}
3943
3944fn processOneCodegenJob(tid: usize, comp: *Compilation, codegen_job: CodegenJob) JobError!void {
3945 switch (codegen_job) {
3946 .nav => |nav_index| {
3947 const named_frame = tracy.namedFrame("codegen_nav");
3948 defer named_frame.end();
3949
3950 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
3951 try pt.linkerUpdateNav(nav_index);
3952 },
3953 .func => |func| {
3954 const named_frame = tracy.namedFrame("codegen_func");
3955 defer named_frame.end();
3956
3957 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
3958 // This call takes ownership of `func.air`.
3959 try pt.linkerUpdateFunc(func.func, func.air);
3960 },
3961 .type => |ty| {
3962 const named_frame = tracy.namedFrame("codegen_type");
3963 defer named_frame.end();
3964
3965 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
3966 try pt.linkerUpdateContainerType(ty);
3967 },
3968 }
3872fn separateCodegenThreadOk(comp: *const Compilation) bool {
3873 if (InternPool.single_threaded) return false;
3874 const zcu = comp.zcu orelse return true;
3875 return zcu.backendSupportsFeature(.separate_thread);
39693876}
39703877
39713878fn workerDocsCopy(comp: *Compilation) void {
......@@ -4717,7 +4624,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
47174624 // file and building an object we need to link them together, but with just one it should go
47184625 // directly to the output file.
47194626 const direct_o = comp.c_source_files.len == 1 and comp.zcu == null and
4720 comp.config.output_mode == .Obj and comp.objects.len == 0;
4627 comp.config.output_mode == .Obj and !link.anyObjectInputs(comp.link_inputs);
47214628 const o_basename_noext = if (direct_o)
47224629 comp.root_name
47234630 else
......@@ -4956,7 +4863,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
49564863 // the contents were the same, we hit the cache but the manifest is dirty and we need to update
49574864 // it to prevent doing a full file content comparison the next time around.
49584865 man.writeManifest() catch |err| {
4959 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ c_object.src.src_path, @errorName(err) });
4866 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{
4867 c_object.src.src_path, @errorName(err),
4868 });
49604869 };
49614870 }
49624871
......@@ -4971,6 +4880,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
49714880 .lock = man.toOwnedLock(),
49724881 },
49734882 };
4883
4884 comp.queueLinkTasks(&.{.{ .load_object = c_object.status.success.object_path }});
49744885}
49754886
49764887fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void {
......@@ -6075,8 +5986,8 @@ test "classifyFileExt" {
60755986 try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));
60765987}
60775988
6078pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Path {
6079 return (try crtFilePath(comp, basename)) orelse {
5989fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Path {
5990 return (try crtFilePath(&comp.crt_files, basename)) orelse {
60805991 const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable;
60815992 const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir;
60825993 const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
......@@ -6089,40 +6000,11 @@ pub fn crtFileAsString(comp: *Compilation, arena: Allocator, basename: []const u
60896000 return path.toString(arena);
60906001}
60916002
6092pub fn crtFilePath(comp: *Compilation, basename: []const u8) Allocator.Error!?Path {
6093 const crt_file = comp.crt_files.get(basename) orelse return null;
6003fn crtFilePath(crt_files: *std.StringHashMapUnmanaged(CrtFile), basename: []const u8) Allocator.Error!?Path {
6004 const crt_file = crt_files.get(basename) orelse return null;
60946005 return crt_file.full_object_path;
60956006}
60966007
6097fn wantBuildLibCFromSource(comp: Compilation) bool {
6098 const is_exe_or_dyn_lib = switch (comp.config.output_mode) {
6099 .Obj => false,
6100 .Lib => comp.config.link_mode == .dynamic,
6101 .Exe => true,
6102 };
6103 const ofmt = comp.root_mod.resolved_target.result.ofmt;
6104 return comp.config.link_libc and is_exe_or_dyn_lib and
6105 comp.libc_installation == null and ofmt != .c;
6106}
6107
6108fn wantBuildGLibCFromSource(comp: Compilation) bool {
6109 return comp.wantBuildLibCFromSource() and comp.getTarget().isGnuLibC();
6110}
6111
6112fn wantBuildMuslFromSource(comp: Compilation) bool {
6113 return comp.wantBuildLibCFromSource() and comp.getTarget().isMusl() and
6114 !comp.getTarget().isWasm();
6115}
6116
6117fn wantBuildWasiLibcFromSource(comp: Compilation) bool {
6118 return comp.wantBuildLibCFromSource() and comp.getTarget().isWasm() and
6119 comp.getTarget().os.tag == .wasi;
6120}
6121
6122fn wantBuildMinGWFromSource(comp: Compilation) bool {
6123 return comp.wantBuildLibCFromSource() and comp.getTarget().isMinGW();
6124}
6125
61266008fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
61276009 const is_exe_or_dyn_lib = switch (comp.config.output_mode) {
61286010 .Obj => false,
......@@ -6133,7 +6015,7 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
61336015 return is_exe_or_dyn_lib and comp.config.link_libunwind and ofmt != .c;
61346016}
61356017
6136fn setAllocFailure(comp: *Compilation) void {
6018pub fn setAllocFailure(comp: *Compilation) void {
61376019 @branchHint(.cold);
61386020 log.debug("memory allocation failure", .{});
61396021 comp.alloc_failure_occurred = true;
......@@ -6370,9 +6252,11 @@ fn buildOutputFromZig(
63706252
63716253 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
63726254
6373 // Under incremental compilation, `out` may already be populated from a prior update.
6374 assert(out.* == null or comp.incremental);
6375 out.* = try sub_compilation.toCrtFile();
6255 const crt_file = try sub_compilation.toCrtFile();
6256 assert(out.* == null);
6257 out.* = crt_file;
6258
6259 comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
63766260}
63776261
63786262pub fn build_crt_file(
......@@ -6479,8 +6363,33 @@ pub fn build_crt_file(
64796363
64806364 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
64816365
6482 try comp.crt_files.ensureUnusedCapacity(gpa, 1);
6483 comp.crt_files.putAssumeCapacityNoClobber(basename, try sub_compilation.toCrtFile());
6366 const crt_file = try sub_compilation.toCrtFile();
6367 comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
6368
6369 {
6370 comp.mutex.lock();
6371 defer comp.mutex.unlock();
6372 try comp.crt_files.ensureUnusedCapacity(gpa, 1);
6373 comp.crt_files.putAssumeCapacityNoClobber(basename, crt_file);
6374 }
6375}
6376
6377pub fn queueLinkTaskMode(comp: *Compilation, path: Path, output_mode: std.builtin.OutputMode) void {
6378 comp.queueLinkTasks(switch (output_mode) {
6379 .Exe => unreachable,
6380 .Obj => &.{.{ .load_object = path }},
6381 .Lib => &.{.{ .load_archive = path }},
6382 });
6383}
6384
6385/// Only valid to call during `update`. Automatically handles queuing up a
6386/// linker worker task if there is not already one.
6387pub fn queueLinkTasks(comp: *Compilation, tasks: []const link.Task) void {
6388 if (comp.link_task_queue.enqueue(comp.gpa, tasks) catch |err| switch (err) {
6389 error.OutOfMemory => return comp.setAllocFailure(),
6390 }) {
6391 comp.thread_pool.spawnWgId(&comp.link_task_wait_group, link.flushTaskQueue, .{comp});
6392 }
64846393}
64856394
64866395pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile {
......@@ -6498,21 +6407,31 @@ pub fn getCrtPaths(
64986407 arena: Allocator,
64996408) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {
65006409 const target = comp.root_mod.resolved_target.result;
6410 return getCrtPathsInner(arena, target, comp.config, comp.libc_installation, &comp.crt_files);
6411}
6412
6413fn getCrtPathsInner(
6414 arena: Allocator,
6415 target: std.Target,
6416 config: Config,
6417 libc_installation: ?*const LibCInstallation,
6418 crt_files: *std.StringHashMapUnmanaged(CrtFile),
6419) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {
65016420 const basenames = LibCInstallation.CrtBasenames.get(.{
65026421 .target = target,
6503 .link_libc = comp.config.link_libc,
6504 .output_mode = comp.config.output_mode,
6505 .link_mode = comp.config.link_mode,
6506 .pie = comp.config.pie,
6422 .link_libc = config.link_libc,
6423 .output_mode = config.output_mode,
6424 .link_mode = config.link_mode,
6425 .pie = config.pie,
65076426 });
6508 if (comp.libc_installation) |lci| return lci.resolveCrtPaths(arena, basenames, target);
6427 if (libc_installation) |lci| return lci.resolveCrtPaths(arena, basenames, target);
65096428
65106429 return .{
6511 .crt0 = if (basenames.crt0) |basename| try comp.crtFilePath(basename) else null,
6512 .crti = if (basenames.crti) |basename| try comp.crtFilePath(basename) else null,
6513 .crtbegin = if (basenames.crtbegin) |basename| try comp.crtFilePath(basename) else null,
6514 .crtend = if (basenames.crtend) |basename| try comp.crtFilePath(basename) else null,
6515 .crtn = if (basenames.crtn) |basename| try comp.crtFilePath(basename) else null,
6430 .crt0 = if (basenames.crt0) |basename| try crtFilePath(crt_files, basename) else null,
6431 .crti = if (basenames.crti) |basename| try crtFilePath(crt_files, basename) else null,
6432 .crtbegin = if (basenames.crtbegin) |basename| try crtFilePath(crt_files, basename) else null,
6433 .crtend = if (basenames.crtend) |basename| try crtFilePath(crt_files, basename) else null,
6434 .crtn = if (basenames.crtn) |basename| try crtFilePath(crt_files, basename) else null,
65166435 };
65176436}
65186437
......@@ -6522,24 +6441,14 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
65226441 // then when we create a sub-Compilation for zig libc, it also tries to
65236442 // build kernel32.lib.
65246443 if (comp.skip_linker_dependencies) return;
6444 const target = comp.root_mod.resolved_target.result;
6445 if (target.os.tag != .windows or target.ofmt == .c) return;
65256446
65266447 // This happens when an `extern "foo"` function is referenced.
65276448 // If we haven't seen this library yet and we're targeting Windows, we need
65286449 // to queue up a work item to produce the DLL import library for this.
6529 const gop = try comp.system_libs.getOrPut(comp.gpa, lib_name);
6530 if (!gop.found_existing) {
6531 gop.value_ptr.* = .{
6532 .needed = true,
6533 .weak = false,
6534 .path = null,
6535 };
6536 const target = comp.root_mod.resolved_target.result;
6537 if (target.os.tag == .windows and target.ofmt != .c) {
6538 try comp.queueJob(.{
6539 .windows_import_lib = comp.system_libs.count() - 1,
6540 });
6541 }
6542 }
6450 const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name);
6451 if (!gop.found_existing) try comp.queueJob(.{ .windows_import_lib = comp.windows_libs.count() - 1 });
65436452}
65446453
65456454/// This decides the optimization mode for all zig-provided libraries, including
src/Sema.zig+8-1
......@@ -2899,6 +2899,7 @@ fn zirStructDecl(
28992899 codegen_type: {
29002900 if (zcu.comp.config.use_llvm) break :codegen_type;
29012901 if (block.ownerModule().strip) break :codegen_type;
2902 // This job depends on any resolve_type_fully jobs queued up before it.
29022903 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
29032904 }
29042905 try sema.declareDependency(.{ .interned = wip_ty.index });
......@@ -3149,6 +3150,7 @@ fn zirEnumDecl(
31493150 codegen_type: {
31503151 if (zcu.comp.config.use_llvm) break :codegen_type;
31513152 if (block.ownerModule().strip) break :codegen_type;
3153 // This job depends on any resolve_type_fully jobs queued up before it.
31523154 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
31533155 }
31543156 return Air.internedToRef(wip_ty.index);
......@@ -3272,6 +3274,7 @@ fn zirUnionDecl(
32723274 codegen_type: {
32733275 if (zcu.comp.config.use_llvm) break :codegen_type;
32743276 if (block.ownerModule().strip) break :codegen_type;
3277 // This job depends on any resolve_type_fully jobs queued up before it.
32753278 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
32763279 }
32773280 try sema.declareDependency(.{ .interned = wip_ty.index });
......@@ -3357,6 +3360,7 @@ fn zirOpaqueDecl(
33573360 codegen_type: {
33583361 if (zcu.comp.config.use_llvm) break :codegen_type;
33593362 if (block.ownerModule().strip) break :codegen_type;
3363 // This job depends on any resolve_type_fully jobs queued up before it.
33603364 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
33613365 }
33623366 try sema.addTypeReferenceEntry(src, wip_ty.index);
......@@ -9595,7 +9599,7 @@ fn resolveGenericBody(
95959599}
95969600
95979601/// 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
9602/// `link.File.Options.windows_libs` table (for example, libc is always
95999603/// specified via dedicated flag `link_libc` instead),
96009604/// and puts it there if it doesn't exist.
96019605/// It also dupes the library name which can then be saved as part of the
......@@ -22456,6 +22460,7 @@ fn reifyEnum(
2245622460 codegen_type: {
2245722461 if (zcu.comp.config.use_llvm) break :codegen_type;
2245822462 if (block.ownerModule().strip) break :codegen_type;
22463 // This job depends on any resolve_type_fully jobs queued up before it.
2245922464 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
2246022465 }
2246122466 return Air.internedToRef(wip_ty.index);
......@@ -22713,6 +22718,7 @@ fn reifyUnion(
2271322718 codegen_type: {
2271422719 if (zcu.comp.config.use_llvm) break :codegen_type;
2271522720 if (block.ownerModule().strip) break :codegen_type;
22721 // This job depends on any resolve_type_fully jobs queued up before it.
2271622722 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
2271722723 }
2271822724 try sema.declareDependency(.{ .interned = wip_ty.index });
......@@ -22997,6 +23003,7 @@ fn reifyStruct(
2299723003 codegen_type: {
2299823004 if (zcu.comp.config.use_llvm) break :codegen_type;
2299923005 if (block.ownerModule().strip) break :codegen_type;
23006 // This job depends on any resolve_type_fully jobs queued up before it.
2300023007 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
2300123008 }
2300223009 try sema.declareDependency(.{ .interned = wip_ty.index });
src/ThreadSafeQueue.zig created+72
......@@ -0,0 +1,72 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const Allocator = std.mem.Allocator;
4
5pub fn ThreadSafeQueue(comptime T: type) type {
6 return struct {
7 worker_owned: std.ArrayListUnmanaged(T),
8 /// Protected by `mutex`.
9 shared: std.ArrayListUnmanaged(T),
10 mutex: std.Thread.Mutex,
11 state: State,
12
13 const Self = @This();
14
15 pub const State = enum { wait, run };
16
17 pub const empty: Self = .{
18 .worker_owned = .empty,
19 .shared = .empty,
20 .mutex = .{},
21 .state = .wait,
22 };
23
24 pub fn deinit(self: *Self, gpa: Allocator) void {
25 self.worker_owned.deinit(gpa);
26 self.shared.deinit(gpa);
27 self.* = undefined;
28 }
29
30 /// Must be called from the worker thread.
31 pub fn check(self: *Self) ?[]T {
32 assert(self.worker_owned.items.len == 0);
33 {
34 self.mutex.lock();
35 defer self.mutex.unlock();
36 assert(self.state == .run);
37 if (self.shared.items.len == 0) {
38 self.state = .wait;
39 return null;
40 }
41 std.mem.swap(std.ArrayListUnmanaged(T), &self.worker_owned, &self.shared);
42 }
43 const result = self.worker_owned.items;
44 self.worker_owned.clearRetainingCapacity();
45 return result;
46 }
47
48 /// Adds items to the queue, returning true if and only if the worker
49 /// thread is waiting. Thread-safe.
50 /// Not safe to call from the worker thread.
51 pub fn enqueue(self: *Self, gpa: Allocator, items: []const T) error{OutOfMemory}!bool {
52 self.mutex.lock();
53 defer self.mutex.unlock();
54 try self.shared.appendSlice(gpa, items);
55 return switch (self.state) {
56 .run => false,
57 .wait => {
58 self.state = .run;
59 return true;
60 },
61 };
62 }
63
64 /// Safe only to call exactly once when initially starting the worker.
65 pub fn start(self: *Self) bool {
66 assert(self.state == .wait);
67 if (self.shared.items.len == 0) return false;
68 self.state = .run;
69 return true;
70 }
71 };
72}
src/Zcu/PerThread.zig+5-1
......@@ -845,6 +845,7 @@ fn ensureFuncBodyAnalyzedInner(
845845 return .{ .ies_outdated = ies_outdated };
846846 }
847847
848 // This job depends on any resolve_type_fully jobs queued up before it.
848849 try comp.queueJob(.{ .codegen_func = .{
849850 .func = func_index,
850851 .air = air,
......@@ -1016,6 +1017,7 @@ fn createFileRootStruct(
10161017 codegen_type: {
10171018 if (zcu.comp.config.use_llvm) break :codegen_type;
10181019 if (file.mod.strip) break :codegen_type;
1020 // This job depends on any resolve_type_fully jobs queued up before it.
10191021 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
10201022 }
10211023 zcu.setFileRootType(file_index, wip_ty.index);
......@@ -1362,6 +1364,7 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
13621364 if (file.mod.strip) break :queue_codegen;
13631365 }
13641366
1367 // This job depends on any resolve_type_fully jobs queued up before it.
13651368 try zcu.comp.queueJob(.{ .codegen_nav = nav_index });
13661369 }
13671370
......@@ -2593,7 +2596,7 @@ pub fn populateTestFunctions(
25932596 }
25942597}
25952598
2596pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
2599pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{OutOfMemory}!void {
25972600 const zcu = pt.zcu;
25982601 const comp = zcu.comp;
25992602 const ip = &zcu.intern_pool;
......@@ -3163,6 +3166,7 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator.
31633166pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!InternPool.Index {
31643167 const result = try pt.zcu.intern_pool.getExtern(pt.zcu.gpa, pt.tid, key);
31653168 if (result.new_nav.unwrap()) |nav| {
3169 // This job depends on any resolve_type_fully jobs queued up before it.
31663170 try pt.zcu.comp.queueJob(.{ .codegen_nav = nav });
31673171 }
31683172 return result.index;
src/arch/riscv64/CodeGen.zig+2-2
......@@ -133,7 +133,7 @@ const Owner = union(enum) {
133133 switch (owner) {
134134 .nav_index => |nav_index| {
135135 const elf_file = func.bin_file.cast(.elf).?;
136 return elf_file.zigObjectPtr().?.getOrCreateMetadataForNav(elf_file, nav_index);
136 return elf_file.zigObjectPtr().?.getOrCreateMetadataForNav(pt.zcu, nav_index);
137137 },
138138 .lazy_sym => |lazy_sym| {
139139 const elf_file = func.bin_file.cast(.elf).?;
......@@ -5002,7 +5002,7 @@ fn genCall(
50025002 .func => |func_val| {
50035003 if (func.bin_file.cast(.elf)) |elf_file| {
50045004 const zo = elf_file.zigObjectPtr().?;
5005 const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func_val.owner_nav);
5005 const sym_index = try zo.getOrCreateMetadataForNav(zcu, func_val.owner_nav);
50065006
50075007 if (func.mod.pic) {
50085008 return func.fail("TODO: genCall pic", .{});
src/arch/x86_64/CodeGen.zig+2-2
......@@ -126,7 +126,7 @@ const Owner = union(enum) {
126126 const pt = ctx.pt;
127127 switch (owner) {
128128 .nav_index => |nav_index| if (ctx.bin_file.cast(.elf)) |elf_file| {
129 return elf_file.zigObjectPtr().?.getOrCreateMetadataForNav(elf_file, nav_index);
129 return elf_file.zigObjectPtr().?.getOrCreateMetadataForNav(pt.zcu, nav_index);
130130 } else if (ctx.bin_file.cast(.macho)) |macho_file| {
131131 return macho_file.getZigObject().?.getOrCreateMetadataForNav(macho_file, nav_index);
132132 } else if (ctx.bin_file.cast(.coff)) |coff_file| {
......@@ -12605,7 +12605,7 @@ fn genCall(self: *Self, info: union(enum) {
1260512605 .func => |func| {
1260612606 if (self.bin_file.cast(.elf)) |elf_file| {
1260712607 const zo = elf_file.zigObjectPtr().?;
12608 const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav);
12608 const sym_index = try zo.getOrCreateMetadataForNav(zcu, func.owner_nav);
1260912609 try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = sym_index }));
1261012610 } else if (self.bin_file.cast(.coff)) |coff_file| {
1261112611 const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav);
src/codegen.zig+1-1
......@@ -866,7 +866,7 @@ fn genNavRef(
866866 zo.symbol(sym_index).flags.is_extern_ptr = true;
867867 return .{ .mcv = .{ .lea_symbol = sym_index } };
868868 }
869 const sym_index = try zo.getOrCreateMetadataForNav(elf_file, nav_index);
869 const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index);
870870 if (!single_threaded and is_threadlocal) {
871871 return .{ .mcv = .{ .load_tlv = sym_index } };
872872 }
src/glibc.zig+53-18
......@@ -6,12 +6,14 @@ const fs = std.fs;
66const path = fs.path;
77const assert = std.debug.assert;
88const Version = std.SemanticVersion;
9const Path = std.Build.Cache.Path;
910
1011const Compilation = @import("Compilation.zig");
1112const build_options = @import("build_options");
1213const trace = @import("tracy.zig").trace;
1314const Cache = std.Build.Cache;
1415const Module = @import("Package/Module.zig");
16const link = @import("link.zig");
1517
1618pub const Lib = struct {
1719 name: []const u8,
......@@ -717,11 +719,11 @@ fn lib_path(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const
717719
718720pub const BuiltSharedObjects = struct {
719721 lock: Cache.Lock,
720 dir_path: []u8,
722 dir_path: Path,
721723
722724 pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator) void {
723725 self.lock.release();
724 gpa.free(self.dir_path);
726 gpa.free(self.dir_path.sub_path);
725727 self.* = undefined;
726728 }
727729};
......@@ -742,7 +744,9 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
742744 return error.ZigCompilerNotBuiltWithLLVMExtensions;
743745 }
744746
745 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
747 const gpa = comp.gpa;
748
749 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
746750 defer arena_allocator.deinit();
747751 const arena = arena_allocator.allocator();
748752
......@@ -751,7 +755,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
751755
752756 // Use the global cache directory.
753757 var cache: Cache = .{
754 .gpa = comp.gpa,
758 .gpa = gpa,
755759 .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}),
756760 };
757761 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
......@@ -772,12 +776,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
772776 if (try man.hit()) {
773777 const digest = man.final();
774778
775 assert(comp.glibc_so_files == null);
776 comp.glibc_so_files = BuiltSharedObjects{
779 return queueSharedObjects(comp, .{
777780 .lock = man.toOwnedLock(),
778 .dir_path = try comp.global_cache_directory.join(comp.gpa, &.{ "o", &digest }),
779 };
780 return;
781 .dir_path = .{
782 .root_dir = comp.global_cache_directory,
783 .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest),
784 },
785 });
781786 }
782787
783788 const digest = man.final();
......@@ -790,8 +795,8 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
790795 defer o_directory.handle.close();
791796
792797 const abilists_contents = man.files.keys()[abilists_index].contents.?;
793 const metadata = try loadMetaData(comp.gpa, abilists_contents);
794 defer metadata.destroy(comp.gpa);
798 const metadata = try loadMetaData(gpa, abilists_contents);
799 defer metadata.destroy(gpa);
795800
796801 const target_targ_index = for (metadata.all_targets, 0..) |targ, i| {
797802 if (targ.arch == target.cpu.arch and
......@@ -835,7 +840,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
835840 map_contents.deinit(); // The most recent allocation of an arena can be freed :)
836841 }
837842
838 var stubs_asm = std.ArrayList(u8).init(comp.gpa);
843 var stubs_asm = std.ArrayList(u8).init(gpa);
839844 defer stubs_asm.deinit();
840845
841846 for (libs, 0..) |lib, lib_i| {
......@@ -1195,7 +1200,6 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
11951200 var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
11961201 const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
11971202 try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items });
1198
11991203 try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib, prog_node);
12001204 }
12011205
......@@ -1203,14 +1207,45 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
12031207 log.warn("failed to write cache manifest for glibc stubs: {s}", .{@errorName(err)});
12041208 };
12051209
1206 assert(comp.glibc_so_files == null);
1207 comp.glibc_so_files = BuiltSharedObjects{
1210 return queueSharedObjects(comp, .{
12081211 .lock = man.toOwnedLock(),
1209 .dir_path = try comp.global_cache_directory.join(comp.gpa, &.{ "o", &digest }),
1210 };
1212 .dir_path = .{
1213 .root_dir = comp.global_cache_directory,
1214 .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest),
1215 },
1216 });
12111217}
12121218
1213// zig fmt: on
1219fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
1220 const target_version = comp.getTarget().os.version_range.linux.glibc;
1221
1222 assert(comp.glibc_so_files == null);
1223 comp.glibc_so_files = so_files;
1224
1225 var task_buffer: [libs.len]link.Task = undefined;
1226 var task_buffer_i: usize = 0;
1227
1228 {
1229 comp.mutex.lock(); // protect comp.arena
1230 defer comp.mutex.unlock();
1231
1232 for (libs) |lib| {
1233 if (lib.removed_in) |rem_in| {
1234 if (target_version.order(rem_in) != .lt) continue;
1235 }
1236 const so_path: Path = .{
1237 .root_dir = so_files.dir_path.root_dir,
1238 .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so.{d}", .{
1239 so_files.dir_path.sub_path, fs.path.sep, lib.name, lib.sover,
1240 }) catch return comp.setAllocFailure(),
1241 };
1242 task_buffer[task_buffer_i] = .{ .load_dso = so_path };
1243 task_buffer_i += 1;
1244 }
1245 }
1246
1247 comp.queueLinkTasks(task_buffer[0..task_buffer_i]);
1248}
12141249
12151250fn buildSharedLib(
12161251 comp: *Compilation,
src/libcxx.zig+6-2
......@@ -355,7 +355,9 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: std.Progress.Node) BuildError!
355355 };
356356
357357 assert(comp.libcxx_static_lib == null);
358 comp.libcxx_static_lib = try sub_compilation.toCrtFile();
358 const crt_file = try sub_compilation.toCrtFile();
359 comp.libcxx_static_lib = crt_file;
360 comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
359361}
360362
361363pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
......@@ -584,7 +586,9 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
584586 };
585587
586588 assert(comp.libcxxabi_static_lib == null);
587 comp.libcxxabi_static_lib = try sub_compilation.toCrtFile();
589 const crt_file = try sub_compilation.toCrtFile();
590 comp.libcxxabi_static_lib = crt_file;
591 comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
588592}
589593
590594pub fn hardeningModeFlag(optimize_mode: std.builtin.OptimizeMode) []const u8 {
src/libtsan.zig+3-1
......@@ -342,8 +342,10 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
342342 },
343343 };
344344
345 const crt_file = try sub_compilation.toCrtFile();
346 comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
345347 assert(comp.tsan_lib == null);
346 comp.tsan_lib = try sub_compilation.toCrtFile();
348 comp.tsan_lib = crt_file;
347349}
348350
349351const tsan_sources = [_][]const u8{
src/libunwind.zig+3-1
......@@ -199,8 +199,10 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
199199 },
200200 };
201201
202 const crt_file = try sub_compilation.toCrtFile();
203 comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
202204 assert(comp.libunwind_static_lib == null);
203 comp.libunwind_static_lib = try sub_compilation.toCrtFile();
205 comp.libunwind_static_lib = crt_file;
204206}
205207
206208const unwind_src_list = [_][]const u8{
src/link.zig+1000-44
......@@ -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");
......@@ -23,19 +24,10 @@ const LlvmObject = @import("codegen/llvm.zig").Object;
2324const lldMain = @import("main.zig").lldMain;
2425const Package = @import("Package.zig");
2526const dev = @import("dev.zig");
27const ThreadSafeQueue = @import("ThreadSafeQueue.zig").ThreadSafeQueue;
28const target_util = @import("target.zig");
2629
27/// When adding a new field, remember to update `hashAddSystemLibs`.
28/// These are *always* dynamically linked. Static libraries will be
29/// provided as positional arguments.
30pub const SystemLib = struct {
31 needed: bool,
32 weak: bool,
33 /// This can be null in two cases right now:
34 /// 1. Windows DLLs that zig ships such as advapi32.
35 /// 2. extern "foo" fn declarations where we find out about libraries too late
36 /// TODO: make this non-optional and resolve those two cases somehow.
37 path: ?Path,
38};
30pub const LdScript = @import("link/LdScript.zig");
3931
4032pub const Diags = struct {
4133 /// Stored here so that function definitions can distinguish between
......@@ -336,20 +328,22 @@ pub const Diags = struct {
336328 log.debug("memory allocation failure", .{});
337329 diags.flags.alloc_failure_occurred = true;
338330 }
339};
340331
341pub fn hashAddSystemLibs(
342 man: *Cache.Manifest,
343 hm: std.StringArrayHashMapUnmanaged(SystemLib),
344) !void {
345 const keys = hm.keys();
346 man.hash.addListOfBytes(keys);
347 for (hm.values()) |value| {
348 man.hash.add(value.needed);
349 man.hash.add(value.weak);
350 if (value.path) |p| _ = try man.addFilePath(p, null);
332 pub fn addMessagesToBundle(diags: *const Diags, bundle: *std.zig.ErrorBundle.Wip) Allocator.Error!void {
333 for (diags.msgs.items) |link_err| {
334 try bundle.addRootErrorMessage(.{
335 .msg = try bundle.addString(link_err.msg),
336 .notes_len = @intCast(link_err.notes.len),
337 });
338 const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
339 for (link_err.notes, 0..) |note, i| {
340 bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
341 .msg = try bundle.addString(note.msg),
342 }));
343 }
344 }
351345 }
352}
346};
353347
354348pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version;
355349
......@@ -438,7 +432,7 @@ pub const File = struct {
438432 compatibility_version: ?std.SemanticVersion,
439433
440434 // TODO: remove this. libraries are resolved by the frontend.
441 lib_dirs: []const []const u8,
435 lib_directories: []const Directory,
442436 framework_dirs: []const []const u8,
443437 rpath_list: []const []const u8,
444438
......@@ -1003,6 +997,102 @@ pub const File = struct {
1003997 }
1004998 }
1005999
1000 /// Opens a path as an object file and parses it into the linker.
1001 fn openLoadObject(base: *File, path: Path) anyerror!void {
1002 const diags = &base.comp.link_diags;
1003 const input = try openObjectInput(diags, path);
1004 errdefer input.object.file.close();
1005 try loadInput(base, input);
1006 }
1007
1008 /// Opens a path as a static library and parses it into the linker.
1009 /// If `query` is non-null, allows GNU ld scripts.
1010 fn openLoadArchive(base: *File, path: Path, opt_query: ?UnresolvedInput.Query) anyerror!void {
1011 if (opt_query) |query| {
1012 const archive = try openObject(path, query.must_link, query.hidden);
1013 errdefer archive.file.close();
1014 loadInput(base, .{ .archive = archive }) catch |err| switch (err) {
1015 error.BadMagic, error.UnexpectedEndOfFile => {
1016 if (base.tag != .elf) return err;
1017 try loadGnuLdScript(base, path, query, archive.file);
1018 archive.file.close();
1019 return;
1020 },
1021 else => return err,
1022 };
1023 } else {
1024 const archive = try openObject(path, false, false);
1025 errdefer archive.file.close();
1026 try loadInput(base, .{ .archive = archive });
1027 }
1028 }
1029
1030 /// Opens a path as a shared library and parses it into the linker.
1031 /// Handles GNU ld scripts.
1032 fn openLoadDso(base: *File, path: Path, query: UnresolvedInput.Query) anyerror!void {
1033 const dso = try openDso(path, query.needed, query.weak, query.reexport);
1034 errdefer dso.file.close();
1035 loadInput(base, .{ .dso = dso }) catch |err| switch (err) {
1036 error.BadMagic, error.UnexpectedEndOfFile => {
1037 if (base.tag != .elf) return err;
1038 try loadGnuLdScript(base, path, query, dso.file);
1039 dso.file.close();
1040 return;
1041 },
1042 else => return err,
1043 };
1044 }
1045
1046 fn loadGnuLdScript(base: *File, path: Path, parent_query: UnresolvedInput.Query, file: fs.File) anyerror!void {
1047 const diags = &base.comp.link_diags;
1048 const gpa = base.comp.gpa;
1049 const stat = try file.stat();
1050 const size = std.math.cast(u32, stat.size) orelse return error.FileTooBig;
1051 const buf = try gpa.alloc(u8, size);
1052 defer gpa.free(buf);
1053 const n = try file.preadAll(buf, 0);
1054 if (buf.len != n) return error.UnexpectedEndOfFile;
1055 var ld_script = try LdScript.parse(gpa, diags, path, buf);
1056 defer ld_script.deinit(gpa);
1057 for (ld_script.args) |arg| {
1058 const query: UnresolvedInput.Query = .{
1059 .needed = arg.needed or parent_query.needed,
1060 .weak = parent_query.weak,
1061 .reexport = parent_query.reexport,
1062 .preferred_mode = parent_query.preferred_mode,
1063 .search_strategy = parent_query.search_strategy,
1064 .allow_so_scripts = parent_query.allow_so_scripts,
1065 };
1066 if (mem.startsWith(u8, arg.path, "-l")) {
1067 @panic("TODO");
1068 } else {
1069 if (fs.path.isAbsolute(arg.path)) {
1070 const new_path = Path.initCwd(try gpa.dupe(u8, arg.path));
1071 switch (Compilation.classifyFileExt(arg.path)) {
1072 .shared_library => try openLoadDso(base, new_path, query),
1073 .object => try openLoadObject(base, new_path),
1074 .static_library => try openLoadArchive(base, new_path, query),
1075 else => diags.addParseError(path, "GNU ld script references file with unrecognized extension: {s}", .{arg.path}),
1076 }
1077 } else {
1078 @panic("TODO");
1079 }
1080 }
1081 }
1082 }
1083
1084 pub fn loadInput(base: *File, input: Input) anyerror!void {
1085 const use_lld = build_options.have_llvm and base.comp.config.use_lld;
1086 if (use_lld) return;
1087 switch (base.tag) {
1088 inline .elf => |tag| {
1089 dev.check(tag.devFeature());
1090 return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input);
1091 },
1092 else => {},
1093 }
1094 }
1095
10061096 pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void {
10071097 dev.check(.lld_linker);
10081098
......@@ -1010,7 +1100,6 @@ pub const File = struct {
10101100 defer tracy.end();
10111101
10121102 const comp = base.comp;
1013 const gpa = comp.gpa;
10141103
10151104 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
10161105 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
......@@ -1042,7 +1131,7 @@ pub const File = struct {
10421131 var man: Cache.Manifest = undefined;
10431132 defer if (!base.disable_lld_caching) man.deinit();
10441133
1045 const objects = comp.objects;
1134 const link_inputs = comp.link_inputs;
10461135
10471136 var digest: [Cache.hex_digest_len]u8 = undefined;
10481137
......@@ -1052,11 +1141,8 @@ pub const File = struct {
10521141 // We are about to obtain this lock, so here we give other processes a chance first.
10531142 base.releaseLock();
10541143
1055 for (objects) |obj| {
1056 _ = try man.addFilePath(obj.path, null);
1057 man.hash.add(obj.must_link);
1058 man.hash.add(obj.loption);
1059 }
1144 try hashInputs(&man, link_inputs);
1145
10601146 for (comp.c_object_table.keys()) |key| {
10611147 _ = try man.addFilePath(key.status.success.object_path, null);
10621148 }
......@@ -1092,26 +1178,24 @@ pub const File = struct {
10921178 };
10931179 }
10941180
1095 const win32_resource_table_len = comp.win32_resource_table.count();
1096 const num_object_files = objects.len + comp.c_object_table.count() + win32_resource_table_len + 2;
1097 var object_files = try std.ArrayList([*:0]const u8).initCapacity(gpa, num_object_files);
1098 defer object_files.deinit();
1181 var object_files: std.ArrayListUnmanaged([*:0]const u8) = .empty;
10991182
1100 for (objects) |obj| {
1101 object_files.appendAssumeCapacity(try obj.path.toStringZ(arena));
1183 try object_files.ensureUnusedCapacity(arena, link_inputs.len);
1184 for (link_inputs) |input| {
1185 object_files.appendAssumeCapacity(try input.path().?.toStringZ(arena));
11021186 }
1187
1188 try object_files.ensureUnusedCapacity(arena, comp.c_object_table.count() +
1189 comp.win32_resource_table.count() + 2);
1190
11031191 for (comp.c_object_table.keys()) |key| {
11041192 object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena));
11051193 }
11061194 for (comp.win32_resource_table.keys()) |key| {
11071195 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));
11081196 }
1109 if (zcu_obj_path) |p| {
1110 object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
1111 }
1112 if (compiler_rt_path) |p| {
1113 object_files.appendAssumeCapacity(try p.toStringZ(arena));
1114 }
1197 if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
1198 if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
11151199
11161200 if (comp.verbose_link) {
11171201 std.debug.print("ar rcs {s}", .{full_out_path_z});
......@@ -1277,6 +1361,195 @@ pub const File = struct {
12771361 pub const Dwarf = @import("link/Dwarf.zig");
12781362};
12791363
1364/// Does all the tasks in the queue. Runs in exactly one separate thread
1365/// from the rest of compilation. All tasks performed here are
1366/// single-threaded with respect to one another.
1367pub fn flushTaskQueue(tid: usize, comp: *Compilation) void {
1368 // As soon as check() is called, another `flushTaskQueue` call could occur,
1369 // so the safety lock must go after the check.
1370 while (comp.link_task_queue.check()) |tasks| {
1371 comp.link_task_queue_safety.lock();
1372 defer comp.link_task_queue_safety.unlock();
1373 for (tasks) |task| doTask(comp, tid, task);
1374 }
1375}
1376
1377pub const Task = union(enum) {
1378 /// Loads the objects, shared objects, and archives that are already
1379 /// known from the command line.
1380 load_explicitly_provided,
1381 /// Loads the shared objects and archives by resolving
1382 /// `target_util.libcFullLinkFlags()` against the host libc
1383 /// installation.
1384 load_host_libc,
1385 /// Tells the linker to load an object file by path.
1386 load_object: Path,
1387 /// Tells the linker to load a static library by path.
1388 load_archive: Path,
1389 /// Tells the linker to load a shared library, possibly one that is a
1390 /// GNU ld script.
1391 load_dso: Path,
1392 /// Tells the linker to load an input which could be an object file,
1393 /// archive, or shared library.
1394 load_input: Input,
1395
1396 /// Write the constant value for a Decl to the output file.
1397 codegen_nav: InternPool.Nav.Index,
1398 /// Write the machine code for a function to the output file.
1399 codegen_func: CodegenFunc,
1400 codegen_type: InternPool.Index,
1401
1402 pub const CodegenFunc = struct {
1403 /// This will either be a non-generic `func_decl` or a `func_instance`.
1404 func: InternPool.Index,
1405 /// This `Air` is owned by the `Job` and allocated with `gpa`.
1406 /// It must be deinited when the job is processed.
1407 air: Air,
1408 };
1409};
1410
1411pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
1412 const diags = &comp.link_diags;
1413 switch (task) {
1414 .load_explicitly_provided => if (comp.bin_file) |base| {
1415 const prog_node = comp.work_queue_progress_node.start("Parse Linker Inputs", comp.link_inputs.len);
1416 defer prog_node.end();
1417 for (comp.link_inputs) |input| {
1418 base.loadInput(input) catch |err| switch (err) {
1419 error.LinkFailure => return, // error reported via diags
1420 else => |e| switch (input) {
1421 .dso => |dso| diags.addParseError(dso.path, "failed to parse shared library: {s}", .{@errorName(e)}),
1422 .object => |obj| diags.addParseError(obj.path, "failed to parse object: {s}", .{@errorName(e)}),
1423 .archive => |obj| diags.addParseError(obj.path, "failed to parse archive: {s}", .{@errorName(e)}),
1424 .res => |res| diags.addParseError(res.path, "failed to parse Windows resource: {s}", .{@errorName(e)}),
1425 .dso_exact => diags.addError("failed to handle dso_exact: {s}", .{@errorName(e)}),
1426 },
1427 };
1428 prog_node.completeOne();
1429 }
1430 },
1431 .load_host_libc => if (comp.bin_file) |base| {
1432 const prog_node = comp.work_queue_progress_node.start("Linker Parse Host libc", 0);
1433 defer prog_node.end();
1434
1435 const target = comp.root_mod.resolved_target.result;
1436 const flags = target_util.libcFullLinkFlags(target);
1437 const crt_dir = comp.libc_installation.?.crt_dir.?;
1438 const sep = std.fs.path.sep_str;
1439 for (flags) |flag| {
1440 assert(mem.startsWith(u8, flag, "-l"));
1441 const lib_name = flag["-l".len..];
1442 switch (comp.config.link_mode) {
1443 .dynamic => {
1444 const dso_path = Path.initCwd(
1445 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1446 crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix(),
1447 }) catch return diags.setAllocFailure(),
1448 );
1449 base.openLoadDso(dso_path, .{
1450 .preferred_mode = .dynamic,
1451 .search_strategy = .paths_first,
1452 }) catch |err| switch (err) {
1453 error.FileNotFound => {
1454 // Also try static.
1455 const archive_path = Path.initCwd(
1456 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1457 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1458 }) catch return diags.setAllocFailure(),
1459 );
1460 base.openLoadArchive(archive_path, .{
1461 .preferred_mode = .dynamic,
1462 .search_strategy = .paths_first,
1463 }) catch |archive_err| switch (archive_err) {
1464 error.LinkFailure => return, // error reported via diags
1465 else => |e| diags.addParseError(dso_path, "failed to parse archive {}: {s}", .{ archive_path, @errorName(e) }),
1466 };
1467 },
1468 error.LinkFailure => return, // error reported via diags
1469 else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}),
1470 };
1471 },
1472 .static => {
1473 const path = Path.initCwd(
1474 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1475 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1476 }) catch return diags.setAllocFailure(),
1477 );
1478 // glibc sometimes makes even archive files GNU ld scripts.
1479 base.openLoadArchive(path, .{
1480 .preferred_mode = .static,
1481 .search_strategy = .no_fallback,
1482 }) catch |err| switch (err) {
1483 error.LinkFailure => return, // error reported via diags
1484 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1485 };
1486 },
1487 }
1488 }
1489 },
1490 .load_object => |path| if (comp.bin_file) |base| {
1491 const prog_node = comp.work_queue_progress_node.start("Linker Parse Object", 0);
1492 defer prog_node.end();
1493 base.openLoadObject(path) catch |err| switch (err) {
1494 error.LinkFailure => return, // error reported via diags
1495 else => |e| diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}),
1496 };
1497 },
1498 .load_archive => |path| if (comp.bin_file) |base| {
1499 const prog_node = comp.work_queue_progress_node.start("Linker Parse Archive", 0);
1500 defer prog_node.end();
1501 base.openLoadArchive(path, null) catch |err| switch (err) {
1502 error.LinkFailure => return, // error reported via link_diags
1503 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1504 };
1505 },
1506 .load_dso => |path| if (comp.bin_file) |base| {
1507 const prog_node = comp.work_queue_progress_node.start("Linker Parse Shared Library", 0);
1508 defer prog_node.end();
1509 base.openLoadDso(path, .{
1510 .preferred_mode = .dynamic,
1511 .search_strategy = .paths_first,
1512 }) catch |err| switch (err) {
1513 error.LinkFailure => return, // error reported via link_diags
1514 else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}),
1515 };
1516 },
1517 .load_input => |input| if (comp.bin_file) |base| {
1518 const prog_node = comp.work_queue_progress_node.start("Linker Parse Input", 0);
1519 defer prog_node.end();
1520 base.loadInput(input) catch |err| switch (err) {
1521 error.LinkFailure => return, // error reported via link_diags
1522 else => |e| {
1523 if (input.path()) |path| {
1524 diags.addParseError(path, "failed to parse linker input: {s}", .{@errorName(e)});
1525 } else {
1526 diags.addError("failed to {s}: {s}", .{ input.taskName(), @errorName(e) });
1527 }
1528 },
1529 };
1530 },
1531 .codegen_nav => |nav_index| {
1532 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
1533 pt.linkerUpdateNav(nav_index) catch |err| switch (err) {
1534 error.OutOfMemory => diags.setAllocFailure(),
1535 };
1536 },
1537 .codegen_func => |func| {
1538 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
1539 // This call takes ownership of `func.air`.
1540 pt.linkerUpdateFunc(func.func, func.air) catch |err| switch (err) {
1541 error.OutOfMemory => diags.setAllocFailure(),
1542 };
1543 },
1544 .codegen_type => |ty| {
1545 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
1546 pt.linkerUpdateContainerType(ty) catch |err| switch (err) {
1547 error.OutOfMemory => diags.setAllocFailure(),
1548 };
1549 },
1550 }
1551}
1552
12801553pub fn spawnLld(
12811554 comp: *Compilation,
12821555 arena: Allocator,
......@@ -1387,3 +1660,686 @@ pub fn spawnLld(
13871660
13881661 if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});
13891662}
1663
1664/// Provided by the CLI, processed into `LinkInput` instances at the start of
1665/// the compilation pipeline.
1666pub const UnresolvedInput = union(enum) {
1667 /// A library name that could potentially be dynamic or static depending on
1668 /// query parameters, resolved according to library directories.
1669 /// This could potentially resolve to a GNU ld script, resulting in more
1670 /// library dependencies.
1671 name_query: NameQuery,
1672 /// When a file path is provided, query info is still needed because the
1673 /// path may point to a .so file which may actually be a GNU ld script that
1674 /// references library names which need to be resolved.
1675 path_query: PathQuery,
1676 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path?
1677 /// Who knows! Fuck around and find out.
1678 ambiguous_name: NameQuery,
1679 /// Put exactly this string in the dynamic section, no rpath.
1680 dso_exact: Input.DsoExact,
1681
1682 pub const NameQuery = struct {
1683 name: []const u8,
1684 query: Query,
1685 };
1686
1687 pub const PathQuery = struct {
1688 path: Path,
1689 query: Query,
1690 };
1691
1692 pub const Query = struct {
1693 needed: bool = false,
1694 weak: bool = false,
1695 reexport: bool = false,
1696 must_link: bool = false,
1697 hidden: bool = false,
1698 allow_so_scripts: bool = false,
1699 preferred_mode: std.builtin.LinkMode,
1700 search_strategy: SearchStrategy,
1701
1702 fn fallbackMode(q: Query) std.builtin.LinkMode {
1703 assert(q.search_strategy != .no_fallback);
1704 return switch (q.preferred_mode) {
1705 .dynamic => .static,
1706 .static => .dynamic,
1707 };
1708 }
1709 };
1710
1711 pub const SearchStrategy = enum {
1712 paths_first,
1713 mode_first,
1714 no_fallback,
1715 };
1716};
1717
1718pub const Input = union(enum) {
1719 object: Object,
1720 archive: Object,
1721 res: Res,
1722 /// May not be a GNU ld script. Those are resolved when converting from
1723 /// `UnresolvedInput` to `Input` values.
1724 dso: Dso,
1725 dso_exact: DsoExact,
1726
1727 pub const Object = struct {
1728 path: Path,
1729 file: fs.File,
1730 must_link: bool,
1731 hidden: bool,
1732 };
1733
1734 pub const Res = struct {
1735 path: Path,
1736 file: fs.File,
1737 };
1738
1739 pub const Dso = struct {
1740 path: Path,
1741 file: fs.File,
1742 needed: bool,
1743 weak: bool,
1744 reexport: bool,
1745 };
1746
1747 pub const DsoExact = struct {
1748 /// Includes the ":" prefix. This is intended to be put into the DSO
1749 /// section verbatim with no corresponding rpaths.
1750 name: []const u8,
1751 };
1752
1753 /// Returns `null` in the case of `dso_exact`.
1754 pub fn path(input: Input) ?Path {
1755 return switch (input) {
1756 .object, .archive => |obj| obj.path,
1757 inline .res, .dso => |x| x.path,
1758 .dso_exact => null,
1759 };
1760 }
1761
1762 /// Returns `null` in the case of `dso_exact`.
1763 pub fn pathAndFile(input: Input) ?struct { Path, fs.File } {
1764 return switch (input) {
1765 .object, .archive => |obj| .{ obj.path, obj.file },
1766 inline .res, .dso => |x| .{ x.path, x.file },
1767 .dso_exact => null,
1768 };
1769 }
1770
1771 pub fn taskName(input: Input) []const u8 {
1772 return switch (input) {
1773 .object, .archive => |obj| obj.path.basename(),
1774 inline .res, .dso => |x| x.path.basename(),
1775 .dso_exact => "dso_exact",
1776 };
1777 }
1778};
1779
1780pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void {
1781 for (link_inputs) |link_input| {
1782 man.hash.add(@as(@typeInfo(Input).@"union".tag_type.?, link_input));
1783 switch (link_input) {
1784 .object, .archive => |obj| {
1785 _ = try man.addOpenedFile(obj.path, obj.file, null);
1786 man.hash.add(obj.must_link);
1787 man.hash.add(obj.hidden);
1788 },
1789 .res => |res| {
1790 _ = try man.addOpenedFile(res.path, res.file, null);
1791 },
1792 .dso => |dso| {
1793 _ = try man.addOpenedFile(dso.path, dso.file, null);
1794 man.hash.add(dso.needed);
1795 man.hash.add(dso.weak);
1796 man.hash.add(dso.reexport);
1797 },
1798 .dso_exact => |dso_exact| {
1799 man.hash.addBytes(dso_exact.name);
1800 },
1801 }
1802 }
1803}
1804
1805pub fn resolveInputs(
1806 gpa: Allocator,
1807 arena: Allocator,
1808 target: std.Target,
1809 /// This function mutates this array but does not take ownership.
1810 /// Allocated with `gpa`.
1811 unresolved_inputs: *std.ArrayListUnmanaged(UnresolvedInput),
1812 /// Allocated with `gpa`.
1813 resolved_inputs: *std.ArrayListUnmanaged(Input),
1814 lib_directories: []const Cache.Directory,
1815 color: std.zig.Color,
1816) Allocator.Error!void {
1817 var checked_paths: std.ArrayListUnmanaged(u8) = .empty;
1818 defer checked_paths.deinit(gpa);
1819
1820 var ld_script_bytes: std.ArrayListUnmanaged(u8) = .empty;
1821 defer ld_script_bytes.deinit(gpa);
1822
1823 var failed_libs: std.ArrayListUnmanaged(struct {
1824 name: []const u8,
1825 strategy: UnresolvedInput.SearchStrategy,
1826 checked_paths: []const u8,
1827 preferred_mode: std.builtin.LinkMode,
1828 }) = .empty;
1829
1830 // Convert external system libs into a stack so that items can be
1831 // pushed to it.
1832 //
1833 // This is necessary because shared objects might turn out to be
1834 // "linker scripts" that in fact resolve to one or more other
1835 // external system libs, including parameters such as "needed".
1836 //
1837 // Unfortunately, such files need to be detected immediately, so
1838 // that this library search logic can be applied to them.
1839 mem.reverse(UnresolvedInput, unresolved_inputs.items);
1840
1841 syslib: while (unresolved_inputs.popOrNull()) |unresolved_input| {
1842 const name_query: UnresolvedInput.NameQuery = switch (unresolved_input) {
1843 .name_query => |nq| nq,
1844 .ambiguous_name => |an| an: {
1845 const lib_name, const link_mode = stripLibPrefixAndSuffix(an.name, target) orelse {
1846 try resolvePathInput(gpa, arena, unresolved_inputs, resolved_inputs, &ld_script_bytes, target, .{
1847 .path = Path.initCwd(an.name),
1848 .query = an.query,
1849 }, color);
1850 continue;
1851 };
1852 break :an .{
1853 .name = lib_name,
1854 .query = .{
1855 .needed = an.query.needed,
1856 .weak = an.query.weak,
1857 .reexport = an.query.reexport,
1858 .must_link = an.query.must_link,
1859 .hidden = an.query.hidden,
1860 .allow_so_scripts = an.query.allow_so_scripts,
1861 .preferred_mode = link_mode,
1862 .search_strategy = .no_fallback,
1863 },
1864 };
1865 },
1866 .path_query => |pq| {
1867 try resolvePathInput(gpa, arena, unresolved_inputs, resolved_inputs, &ld_script_bytes, target, pq, color);
1868 continue;
1869 },
1870 .dso_exact => |dso_exact| {
1871 try resolved_inputs.append(gpa, .{ .dso_exact = dso_exact });
1872 continue;
1873 },
1874 };
1875 const query = name_query.query;
1876
1877 // Checked in the first pass above while looking for libc libraries.
1878 assert(!fs.path.isAbsolute(name_query.name));
1879
1880 checked_paths.clearRetainingCapacity();
1881
1882 switch (query.search_strategy) {
1883 .mode_first, .no_fallback => {
1884 // check for preferred mode
1885 for (lib_directories) |lib_directory| switch (try resolveLibInput(
1886 gpa,
1887 arena,
1888 unresolved_inputs,
1889 resolved_inputs,
1890 &checked_paths,
1891 &ld_script_bytes,
1892 lib_directory,
1893 name_query,
1894 target,
1895 query.preferred_mode,
1896 color,
1897 )) {
1898 .ok => continue :syslib,
1899 .no_match => {},
1900 };
1901 // check for fallback mode
1902 if (query.search_strategy == .no_fallback) {
1903 try failed_libs.append(arena, .{
1904 .name = name_query.name,
1905 .strategy = query.search_strategy,
1906 .checked_paths = try arena.dupe(u8, checked_paths.items),
1907 .preferred_mode = query.preferred_mode,
1908 });
1909 continue :syslib;
1910 }
1911 for (lib_directories) |lib_directory| switch (try resolveLibInput(
1912 gpa,
1913 arena,
1914 unresolved_inputs,
1915 resolved_inputs,
1916 &checked_paths,
1917 &ld_script_bytes,
1918 lib_directory,
1919 name_query,
1920 target,
1921 query.fallbackMode(),
1922 color,
1923 )) {
1924 .ok => continue :syslib,
1925 .no_match => {},
1926 };
1927 try failed_libs.append(arena, .{
1928 .name = name_query.name,
1929 .strategy = query.search_strategy,
1930 .checked_paths = try arena.dupe(u8, checked_paths.items),
1931 .preferred_mode = query.preferred_mode,
1932 });
1933 continue :syslib;
1934 },
1935 .paths_first => {
1936 for (lib_directories) |lib_directory| {
1937 // check for preferred mode
1938 switch (try resolveLibInput(
1939 gpa,
1940 arena,
1941 unresolved_inputs,
1942 resolved_inputs,
1943 &checked_paths,
1944 &ld_script_bytes,
1945 lib_directory,
1946 name_query,
1947 target,
1948 query.preferred_mode,
1949 color,
1950 )) {
1951 .ok => continue :syslib,
1952 .no_match => {},
1953 }
1954
1955 // check for fallback mode
1956 switch (try resolveLibInput(
1957 gpa,
1958 arena,
1959 unresolved_inputs,
1960 resolved_inputs,
1961 &checked_paths,
1962 &ld_script_bytes,
1963 lib_directory,
1964 name_query,
1965 target,
1966 query.fallbackMode(),
1967 color,
1968 )) {
1969 .ok => continue :syslib,
1970 .no_match => {},
1971 }
1972 }
1973 try failed_libs.append(arena, .{
1974 .name = name_query.name,
1975 .strategy = query.search_strategy,
1976 .checked_paths = try arena.dupe(u8, checked_paths.items),
1977 .preferred_mode = query.preferred_mode,
1978 });
1979 continue :syslib;
1980 },
1981 }
1982 @compileError("unreachable");
1983 }
1984
1985 if (failed_libs.items.len > 0) {
1986 for (failed_libs.items) |f| {
1987 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
1988 std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{
1989 @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths,
1990 });
1991 }
1992 std.process.exit(1);
1993 }
1994}
1995
1996const ResolveLibInputResult = enum { ok, no_match };
1997const fatal = std.process.fatal;
1998
1999fn resolveLibInput(
2000 gpa: Allocator,
2001 arena: Allocator,
2002 /// Allocated via `gpa`.
2003 unresolved_inputs: *std.ArrayListUnmanaged(UnresolvedInput),
2004 /// Allocated via `gpa`.
2005 resolved_inputs: *std.ArrayListUnmanaged(Input),
2006 /// Allocated via `gpa`.
2007 checked_paths: *std.ArrayListUnmanaged(u8),
2008 /// Allocated via `gpa`.
2009 ld_script_bytes: *std.ArrayListUnmanaged(u8),
2010 lib_directory: Directory,
2011 name_query: UnresolvedInput.NameQuery,
2012 target: std.Target,
2013 link_mode: std.builtin.LinkMode,
2014 color: std.zig.Color,
2015) Allocator.Error!ResolveLibInputResult {
2016 try resolved_inputs.ensureUnusedCapacity(gpa, 1);
2017
2018 const lib_name = name_query.name;
2019
2020 if (target.isDarwin() and link_mode == .dynamic) tbd: {
2021 // Prefer .tbd over .dylib.
2022 const test_path: Path = .{
2023 .root_dir = lib_directory,
2024 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.tbd", .{lib_name}),
2025 };
2026 try checked_paths.writer(gpa).print("\n {}", .{test_path});
2027 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
2028 error.FileNotFound => break :tbd,
2029 else => |e| fatal("unable to search for tbd library '{}': {s}", .{ test_path, @errorName(e) }),
2030 };
2031 errdefer file.close();
2032 return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
2033 }
2034
2035 {
2036 const test_path: Path = .{
2037 .root_dir = lib_directory,
2038 .sub_path = try std.fmt.allocPrint(arena, "{s}{s}{s}", .{
2039 target.libPrefix(), lib_name, switch (link_mode) {
2040 .static => target.staticLibSuffix(),
2041 .dynamic => target.dynamicLibSuffix(),
2042 },
2043 }),
2044 };
2045 try checked_paths.writer(gpa).print("\n {}", .{test_path});
2046 switch (try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, .{
2047 .path = test_path,
2048 .query = name_query.query,
2049 }, link_mode, color)) {
2050 .no_match => {},
2051 .ok => return .ok,
2052 }
2053 }
2054
2055 // In the case of Darwin, the main check will be .dylib, so here we
2056 // additionally check for .so files.
2057 if (target.isDarwin() and link_mode == .dynamic) so: {
2058 const test_path: Path = .{
2059 .root_dir = lib_directory,
2060 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.so", .{lib_name}),
2061 };
2062 try checked_paths.writer(gpa).print("\n {}", .{test_path});
2063 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
2064 error.FileNotFound => break :so,
2065 else => |e| fatal("unable to search for so library '{}': {s}", .{
2066 test_path, @errorName(e),
2067 }),
2068 };
2069 errdefer file.close();
2070 return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
2071 }
2072
2073 // In the case of MinGW, the main check will be .lib but we also need to
2074 // look for `libfoo.a`.
2075 if (target.isMinGW() and link_mode == .static) mingw: {
2076 const test_path: Path = .{
2077 .root_dir = lib_directory,
2078 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.a", .{lib_name}),
2079 };
2080 try checked_paths.writer(gpa).print("\n {}", .{test_path});
2081 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
2082 error.FileNotFound => break :mingw,
2083 else => |e| fatal("unable to search for static library '{}': {s}", .{ test_path, @errorName(e) }),
2084 };
2085 errdefer file.close();
2086 return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
2087 }
2088
2089 return .no_match;
2090}
2091
2092fn finishResolveLibInput(
2093 resolved_inputs: *std.ArrayListUnmanaged(Input),
2094 path: Path,
2095 file: std.fs.File,
2096 link_mode: std.builtin.LinkMode,
2097 query: UnresolvedInput.Query,
2098) ResolveLibInputResult {
2099 switch (link_mode) {
2100 .static => resolved_inputs.appendAssumeCapacity(.{ .archive = .{
2101 .path = path,
2102 .file = file,
2103 .must_link = query.must_link,
2104 .hidden = query.hidden,
2105 } }),
2106 .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2107 .path = path,
2108 .file = file,
2109 .needed = query.needed,
2110 .weak = query.weak,
2111 .reexport = query.reexport,
2112 } }),
2113 }
2114 return .ok;
2115}
2116
2117fn resolvePathInput(
2118 gpa: Allocator,
2119 arena: Allocator,
2120 /// Allocated with `gpa`.
2121 unresolved_inputs: *std.ArrayListUnmanaged(UnresolvedInput),
2122 /// Allocated with `gpa`.
2123 resolved_inputs: *std.ArrayListUnmanaged(Input),
2124 /// Allocated via `gpa`.
2125 ld_script_bytes: *std.ArrayListUnmanaged(u8),
2126 target: std.Target,
2127 pq: UnresolvedInput.PathQuery,
2128 color: std.zig.Color,
2129) Allocator.Error!void {
2130 switch (switch (Compilation.classifyFileExt(pq.path.sub_path)) {
2131 .static_library => try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color),
2132 .shared_library => try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color),
2133 .object => {
2134 var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|
2135 fatal("failed to open object {}: {s}", .{ pq.path, @errorName(err) });
2136 errdefer file.close();
2137 try resolved_inputs.append(gpa, .{ .object = .{
2138 .path = pq.path,
2139 .file = file,
2140 .must_link = pq.query.must_link,
2141 .hidden = pq.query.hidden,
2142 } });
2143 return;
2144 },
2145 .res => {
2146 var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|
2147 fatal("failed to open windows resource {}: {s}", .{ pq.path, @errorName(err) });
2148 errdefer file.close();
2149 try resolved_inputs.append(gpa, .{ .res = .{
2150 .path = pq.path,
2151 .file = file,
2152 } });
2153 return;
2154 },
2155 else => fatal("{}: unrecognized file extension", .{pq.path}),
2156 }) {
2157 .ok => {},
2158 .no_match => fatal("{}: file not found", .{pq.path}),
2159 }
2160}
2161
2162fn resolvePathInputLib(
2163 gpa: Allocator,
2164 arena: Allocator,
2165 /// Allocated with `gpa`.
2166 unresolved_inputs: *std.ArrayListUnmanaged(UnresolvedInput),
2167 /// Allocated with `gpa`.
2168 resolved_inputs: *std.ArrayListUnmanaged(Input),
2169 /// Allocated via `gpa`.
2170 ld_script_bytes: *std.ArrayListUnmanaged(u8),
2171 target: std.Target,
2172 pq: UnresolvedInput.PathQuery,
2173 link_mode: std.builtin.LinkMode,
2174 color: std.zig.Color,
2175) Allocator.Error!ResolveLibInputResult {
2176 try resolved_inputs.ensureUnusedCapacity(gpa, 1);
2177
2178 const test_path: Path = pq.path;
2179 // In the case of .so files, they might actually be "linker scripts"
2180 // that contain references to other libraries.
2181 if (pq.query.allow_so_scripts and target.ofmt == .elf and mem.endsWith(u8, test_path.sub_path, ".so")) {
2182 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
2183 error.FileNotFound => return .no_match,
2184 else => |e| fatal("unable to search for {s} library '{'}': {s}", .{
2185 @tagName(link_mode), test_path, @errorName(e),
2186 }),
2187 };
2188 errdefer file.close();
2189 try ld_script_bytes.resize(gpa, @sizeOf(std.elf.Elf64_Ehdr));
2190 const n = file.preadAll(ld_script_bytes.items, 0) catch |err| fatal("failed to read '{'}': {s}", .{
2191 test_path, @errorName(err),
2192 });
2193 elf_file: {
2194 if (n != ld_script_bytes.items.len) break :elf_file;
2195 if (!mem.eql(u8, ld_script_bytes.items[0..4], "\x7fELF")) break :elf_file;
2196 // Appears to be an ELF file.
2197 return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query);
2198 }
2199 const stat = file.stat() catch |err|
2200 fatal("failed to stat {}: {s}", .{ test_path, @errorName(err) });
2201 const size = std.math.cast(u32, stat.size) orelse
2202 fatal("{}: linker script too big", .{test_path});
2203 try ld_script_bytes.resize(gpa, size);
2204 const buf = ld_script_bytes.items[n..];
2205 const n2 = file.preadAll(buf, n) catch |err|
2206 fatal("failed to read {}: {s}", .{ test_path, @errorName(err) });
2207 if (n2 != buf.len) fatal("failed to read {}: unexpected end of file", .{test_path});
2208 var diags = Diags.init(gpa);
2209 defer diags.deinit();
2210 const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items);
2211 if (diags.hasErrors()) {
2212 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
2213 try wip_errors.init(gpa);
2214 defer wip_errors.deinit();
2215
2216 try diags.addMessagesToBundle(&wip_errors);
2217
2218 var error_bundle = try wip_errors.toOwnedBundle("");
2219 defer error_bundle.deinit(gpa);
2220
2221 error_bundle.renderToStdErr(color.renderOptions());
2222
2223 std.process.exit(1);
2224 }
2225
2226 var ld_script = ld_script_result catch |err|
2227 fatal("{}: failed to parse linker script: {s}", .{ test_path, @errorName(err) });
2228 defer ld_script.deinit(gpa);
2229
2230 try unresolved_inputs.ensureUnusedCapacity(gpa, ld_script.args.len);
2231 for (ld_script.args) |arg| {
2232 const query: UnresolvedInput.Query = .{
2233 .needed = arg.needed or pq.query.needed,
2234 .weak = pq.query.weak,
2235 .reexport = pq.query.reexport,
2236 .preferred_mode = pq.query.preferred_mode,
2237 .search_strategy = pq.query.search_strategy,
2238 .allow_so_scripts = pq.query.allow_so_scripts,
2239 };
2240 if (mem.startsWith(u8, arg.path, "-l")) {
2241 unresolved_inputs.appendAssumeCapacity(.{ .name_query = .{
2242 .name = try arena.dupe(u8, arg.path["-l".len..]),
2243 .query = query,
2244 } });
2245 } else {
2246 unresolved_inputs.appendAssumeCapacity(.{ .ambiguous_name = .{
2247 .name = try arena.dupe(u8, arg.path),
2248 .query = query,
2249 } });
2250 }
2251 }
2252 file.close();
2253 return .ok;
2254 }
2255
2256 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
2257 error.FileNotFound => return .no_match,
2258 else => |e| fatal("unable to search for {s} library {}: {s}", .{
2259 @tagName(link_mode), test_path, @errorName(e),
2260 }),
2261 };
2262 errdefer file.close();
2263 return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query);
2264}
2265
2266pub fn openObject(path: Path, must_link: bool, hidden: bool) !Input.Object {
2267 var file = try path.root_dir.handle.openFile(path.sub_path, .{});
2268 errdefer file.close();
2269 return .{
2270 .path = path,
2271 .file = file,
2272 .must_link = must_link,
2273 .hidden = hidden,
2274 };
2275}
2276
2277pub fn openDso(path: Path, needed: bool, weak: bool, reexport: bool) !Input.Dso {
2278 var file = try path.root_dir.handle.openFile(path.sub_path, .{});
2279 errdefer file.close();
2280 return .{
2281 .path = path,
2282 .file = file,
2283 .needed = needed,
2284 .weak = weak,
2285 .reexport = reexport,
2286 };
2287}
2288
2289pub fn openObjectInput(diags: *Diags, path: Path) error{LinkFailure}!Input {
2290 return .{ .object = openObject(path, false, false) catch |err| {
2291 return diags.failParse(path, "failed to open {}: {s}", .{ path, @errorName(err) });
2292 } };
2293}
2294
2295pub fn openArchiveInput(diags: *Diags, path: Path, must_link: bool, hidden: bool) error{LinkFailure}!Input {
2296 return .{ .archive = openObject(path, must_link, hidden) catch |err| {
2297 return diags.failParse(path, "failed to open {}: {s}", .{ path, @errorName(err) });
2298 } };
2299}
2300
2301pub fn openDsoInput(diags: *Diags, path: Path, needed: bool, weak: bool, reexport: bool) error{LinkFailure}!Input {
2302 return .{ .dso = openDso(path, needed, weak, reexport) catch |err| {
2303 return diags.failParse(path, "failed to open {}: {s}", .{ path, @errorName(err) });
2304 } };
2305}
2306
2307fn stripLibPrefixAndSuffix(path: []const u8, target: std.Target) ?struct { []const u8, std.builtin.LinkMode } {
2308 const prefix = target.libPrefix();
2309 const static_suffix = target.staticLibSuffix();
2310 const dynamic_suffix = target.dynamicLibSuffix();
2311 const basename = fs.path.basename(path);
2312 const unlibbed = if (mem.startsWith(u8, basename, prefix)) basename[prefix.len..] else return null;
2313 if (mem.endsWith(u8, unlibbed, static_suffix)) return .{
2314 unlibbed[0 .. unlibbed.len - static_suffix.len], .static,
2315 };
2316 if (mem.endsWith(u8, unlibbed, dynamic_suffix)) return .{
2317 unlibbed[0 .. unlibbed.len - dynamic_suffix.len], .dynamic,
2318 };
2319 return null;
2320}
2321
2322/// Returns true if and only if there is at least one input of type object,
2323/// archive, or Windows resource file.
2324pub fn anyObjectInputs(inputs: []const Input) bool {
2325 return countObjectInputs(inputs) != 0;
2326}
2327
2328/// Returns the number of inputs of type object, archive, or Windows resource file.
2329pub fn countObjectInputs(inputs: []const Input) usize {
2330 var count: usize = 0;
2331 for (inputs) |input| switch (input) {
2332 .dso, .dso_exact => continue,
2333 .res, .object, .archive => count += 1,
2334 };
2335 return count;
2336}
2337
2338/// Returns the first input of type object or archive.
2339pub fn firstObjectInput(inputs: []const Input) ?Input.Object {
2340 for (inputs) |input| switch (input) {
2341 .object, .archive => |obj| return obj,
2342 .res, .dso, .dso_exact => continue,
2343 };
2344 return null;
2345}
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+233-631
......@@ -1,6 +1,7 @@
11pub const Atom = @import("Elf/Atom.zig");
22
33base: link.File,
4zig_object: ?*ZigObject,
45rpath_table: std.StringArrayHashMapUnmanaged(void),
56image_base: u64,
67emit_relocs: bool,
......@@ -15,7 +16,6 @@ z_relro: bool,
1516z_common_page_size: ?u64,
1617/// TODO make this non optional and resolve the default in open()
1718z_max_page_size: ?u64,
18lib_dirs: []const []const u8,
1919hash_style: HashStyle,
2020compress_debug_sections: CompressDebugSections,
2121symbol_wrap_set: std.StringArrayHashMapUnmanaged(void),
......@@ -36,8 +36,7 @@ ptr_width: PtrWidth,
3636llvm_object: ?LlvmObject.Ptr = null,
3737
3838/// A list of all input files.
39/// Index of each input file also encodes the priority or precedence of one input file
40/// over another.
39/// First index is a special "null file". Order is otherwise not observed.
4140files: std.MultiArrayList(File.Entry) = .{},
4241/// Long-lived list of all file descriptors.
4342/// We store them globally rather than per actual File so that we can re-use
......@@ -116,6 +115,10 @@ comment_merge_section_index: ?Merge.Section.Index = null,
116115
117116first_eflags: ?elf.Word = null,
118117
118/// `--verbose-link` output.
119/// Initialized on creation, appended to as inputs are added, printed during `flush`.
120dump_argv_list: std.ArrayListUnmanaged([]const u8),
121
119122const SectionIndexes = struct {
120123 copy_rel: ?u32 = null,
121124 dynamic: ?u32 = null,
......@@ -297,6 +300,7 @@ pub fn createEmpty(
297300 .disable_lld_caching = options.disable_lld_caching,
298301 .build_id = options.build_id,
299302 },
303 .zig_object = null,
300304 .rpath_table = rpath_table,
301305 .ptr_width = ptr_width,
302306 .page_size = page_size,
......@@ -328,7 +332,6 @@ pub fn createEmpty(
328332 .z_relro = options.z_relro,
329333 .z_common_page_size = options.z_common_page_size,
330334 .z_max_page_size = options.z_max_page_size,
331 .lib_dirs = options.lib_dirs,
332335 .hash_style = options.hash_style,
333336 .compress_debug_sections = options.compress_debug_sections,
334337 .symbol_wrap_set = options.symbol_wrap_set,
......@@ -341,6 +344,7 @@ pub fn createEmpty(
341344 .enable_new_dtags = options.enable_new_dtags,
342345 .print_icf_sections = options.print_icf_sections,
343346 .print_map = options.print_map,
347 .dump_argv_list = .empty,
344348 };
345349 if (use_llvm and comp.config.have_zcu) {
346350 self.llvm_object = try LlvmObject.create(arena, comp);
......@@ -352,6 +356,9 @@ pub fn createEmpty(
352356 return self;
353357 }
354358
359 // --verbose-link
360 if (comp.verbose_link) try dumpArgvInit(self, arena);
361
355362 const is_obj = output_mode == .Obj;
356363 const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static);
357364
......@@ -418,14 +425,17 @@ pub fn createEmpty(
418425 if (opt_zcu) |zcu| {
419426 if (!use_llvm) {
420427 const index: File.Index = @intCast(try self.files.addOne(gpa));
421 self.files.set(index, .{ .zig_object = .{
428 self.files.set(index, .zig_object);
429 self.zig_object_index = index;
430 const zig_object = try arena.create(ZigObject);
431 self.zig_object = zig_object;
432 zig_object.* = .{
422433 .index = index,
423434 .basename = try std.fmt.allocPrint(arena, "{s}.o", .{
424435 fs.path.stem(zcu.main_mod.root_src_path),
425436 }),
426 } });
427 self.zig_object_index = index;
428 try self.zigObjectPtr().?.init(self, .{
437 };
438 try zig_object.init(self, .{
429439 .symbol_count_hint = options.symbol_count_hint,
430440 .program_code_size_hint = options.program_code_size_hint,
431441 });
......@@ -457,12 +467,14 @@ pub fn deinit(self: *Elf) void {
457467 self.file_handles.deinit(gpa);
458468
459469 for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {
460 .null => {},
461 .zig_object => data.zig_object.deinit(gpa),
470 .null, .zig_object => {},
462471 .linker_defined => data.linker_defined.deinit(gpa),
463472 .object => data.object.deinit(gpa),
464473 .shared_object => data.shared_object.deinit(gpa),
465474 };
475 if (self.zig_object) |zig_object| {
476 zig_object.deinit(gpa);
477 }
466478 self.files.deinit(gpa);
467479 self.objects.deinit(gpa);
468480 self.shared_objects.deinit(gpa);
......@@ -501,6 +513,7 @@ pub fn deinit(self: *Elf) void {
501513 self.rela_dyn.deinit(gpa);
502514 self.rela_plt.deinit(gpa);
503515 self.comdat_group_sections.deinit(gpa);
516 self.dump_argv_list.deinit(gpa);
504517}
505518
506519pub fn getNavVAddr(self: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 {
......@@ -752,6 +765,37 @@ pub fn allocateChunk(self: *Elf, args: struct {
752765 return res;
753766}
754767
768pub fn loadInput(self: *Elf, input: link.Input) !void {
769 const comp = self.base.comp;
770 const gpa = comp.gpa;
771 const diags = &comp.link_diags;
772 const target = self.getTarget();
773 const debug_fmt_strip = comp.config.debug_format == .strip;
774 const default_sym_version = self.default_sym_version;
775 const is_static_lib = self.base.isStaticLib();
776
777 if (comp.verbose_link) {
778 comp.mutex.lock(); // protect comp.arena
779 defer comp.mutex.unlock();
780
781 const argv = &self.dump_argv_list;
782 switch (input) {
783 .res => unreachable,
784 .dso_exact => |dso_exact| try argv.appendSlice(gpa, &.{ "-l", dso_exact.name }),
785 .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)),
786 .dso => |dso| try argv.append(gpa, try dso.path.toString(comp.arena)),
787 }
788 }
789
790 switch (input) {
791 .res => unreachable,
792 .dso_exact => @panic("TODO"),
793 .object => |obj| try parseObject(self, obj),
794 .archive => |obj| try parseArchive(gpa, diags, &self.file_handles, &self.files, &self.first_eflags, target, debug_fmt_strip, default_sym_version, &self.objects, obj, is_static_lib),
795 .dso => |dso| try parseDso(gpa, diags, dso, &self.shared_objects, &self.files, target),
796 }
797}
798
755799pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
756800 const use_lld = build_options.have_llvm and self.base.comp.config.use_lld;
757801 if (use_lld) {
......@@ -774,11 +818,11 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
774818 if (use_lld) return;
775819 }
776820
821 if (comp.verbose_link) Compilation.dump_argv(self.dump_argv_list.items);
822
777823 const sub_prog_node = prog_node.start("ELF Flush", 0);
778824 defer sub_prog_node.end();
779825
780 const target = self.getTarget();
781 const link_mode = comp.config.link_mode;
782826 const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
783827 const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{
784828 .root_dir = directory,
......@@ -788,126 +832,19 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
788832 path,
789833 } else null;
790834
791 // --verbose-link
792 if (comp.verbose_link) try self.dumpArgv(comp);
793
794835 if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid);
795 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
796 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
797
798 const csu = try comp.getCrtPaths(arena);
799836
800 // csu prelude
801 if (csu.crt0) |path| parseObjectReportingFailure(self, path);
802 if (csu.crti) |path| parseObjectReportingFailure(self, path);
803 if (csu.crtbegin) |path| parseObjectReportingFailure(self, path);
804
805 for (comp.objects) |obj| {
806 parseInputReportingFailure(self, obj.path, obj.needed, obj.must_link);
807 }
808
809 // This is a set of object files emitted by clang in a single `build-exe` invocation.
810 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
811 // in this set.
812 for (comp.c_object_table.keys()) |key| {
813 parseObjectReportingFailure(self, key.status.success.object_path);
814 }
815
816 if (module_obj_path) |path| parseObjectReportingFailure(self, path);
817
818 if (comp.config.any_sanitize_thread) parseCrtFileReportingFailure(self, comp.tsan_lib.?);
819 if (comp.config.any_fuzz) parseCrtFileReportingFailure(self, comp.fuzzer_lib.?);
820
821 // libc
822 if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
823 if (comp.libc_static_lib) |lib| parseCrtFileReportingFailure(self, lib);
824 }
837 if (module_obj_path) |path| openParseObjectReportingFailure(self, path);
825838
826 for (comp.system_libs.values()) |lib_info| {
827 parseInputReportingFailure(self, lib_info.path.?, lib_info.needed, false);
828 }
829
830 // libc++ dep
831 if (comp.config.link_libcpp) {
832 parseInputReportingFailure(self, comp.libcxxabi_static_lib.?.full_object_path, false, false);
833 parseInputReportingFailure(self, comp.libcxx_static_lib.?.full_object_path, false, false);
834 }
835
836 // libunwind dep
837 if (comp.config.link_libunwind) {
838 parseInputReportingFailure(self, comp.libunwind_static_lib.?.full_object_path, false, false);
839 }
840
841 // libc dep
842 diags.flags.missing_libc = false;
843 if (comp.config.link_libc) {
844 if (comp.libc_installation) |lc| {
845 const flags = target_util.libcFullLinkFlags(target);
846
847 var test_path = std.ArrayList(u8).init(arena);
848 var checked_paths = std.ArrayList([]const u8).init(arena);
849
850 for (flags) |flag| {
851 checked_paths.clearRetainingCapacity();
852 const lib_name = flag["-l".len..];
853
854 success: {
855 if (!self.base.isStatic()) {
856 if (try self.accessLibPath(arena, &test_path, &checked_paths, lc.crt_dir.?, lib_name, .dynamic))
857 break :success;
858 }
859 if (try self.accessLibPath(arena, &test_path, &checked_paths, lc.crt_dir.?, lib_name, .static))
860 break :success;
861
862 diags.addMissingLibraryError(
863 checked_paths.items,
864 "missing system library: '{s}' was not found",
865 .{lib_name},
866 );
867 continue;
868 }
869
870 const resolved_path = Path.initCwd(try arena.dupe(u8, test_path.items));
871 parseInputReportingFailure(self, resolved_path, false, false);
872 }
873 } else if (target.isGnuLibC()) {
874 for (glibc.libs) |lib| {
875 if (lib.removed_in) |rem_in| {
876 if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue;
877 }
878
879 const lib_path = Path.initCwd(try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
880 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
881 }));
882 parseInputReportingFailure(self, lib_path, false, false);
883 }
884 parseInputReportingFailure(self, try comp.get_libc_crt_file(arena, "libc_nonshared.a"), false, false);
885 } else if (target.isMusl()) {
886 const path = try comp.get_libc_crt_file(arena, switch (link_mode) {
887 .static => "libc.a",
888 .dynamic => "libc.so",
889 });
890 parseInputReportingFailure(self, path, false, false);
891 } else {
892 diags.flags.missing_libc = true;
893 }
894 }
895
896 // Finally, as the last input objects we add compiler_rt and CSU postlude (if any).
897
898 // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
899 // to be after the shared libraries, so they are picked up from the shared
900 // libraries, not libcompiler_rt.
901 if (comp.compiler_rt_lib) |crt_file| {
902 parseInputReportingFailure(self, crt_file.full_object_path, false, false);
903 } else if (comp.compiler_rt_obj) |crt_file| {
904 parseObjectReportingFailure(self, crt_file.full_object_path);
839 switch (comp.config.output_mode) {
840 .Obj => return relocatable.flushObject(self, comp),
841 .Lib => switch (comp.config.link_mode) {
842 .dynamic => {},
843 .static => return relocatable.flushStaticLib(self, comp),
844 },
845 .Exe => {},
905846 }
906847
907 // csu postlude
908 if (csu.crtend) |path| parseObjectReportingFailure(self, path);
909 if (csu.crtn) |path| parseObjectReportingFailure(self, path);
910
911848 if (diags.hasErrors()) return error.FlushFailure;
912849
913850 // If we haven't already, create a linker-generated input file comprising of
......@@ -1058,347 +995,150 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
1058995 if (diags.hasErrors()) return error.FlushFailure;
1059996}
1060997
1061/// --verbose-link output
1062fn dumpArgv(self: *Elf, comp: *Compilation) !void {
1063 const gpa = self.base.comp.gpa;
1064 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
1065 defer arena_allocator.deinit();
1066 const arena = arena_allocator.allocator();
1067
998fn dumpArgvInit(self: *Elf, arena: Allocator) !void {
999 const comp = self.base.comp;
1000 const gpa = comp.gpa;
10681001 const target = self.getTarget();
1069 const link_mode = self.base.comp.config.link_mode;
1070 const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
1071 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
1072 const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: {
1073 if (fs.path.dirname(full_out_path)) |dirname| {
1074 break :blk try fs.path.join(arena, &.{ dirname, path });
1075 } else {
1076 break :blk path;
1077 }
1078 } else null;
1079
1080 const csu = try comp.getCrtPaths(arena);
1081 const compiler_rt_path: ?[]const u8 = blk: {
1082 if (comp.compiler_rt_lib) |x| break :blk try x.full_object_path.toString(arena);
1083 if (comp.compiler_rt_obj) |x| break :blk try x.full_object_path.toString(arena);
1084 break :blk null;
1085 };
1002 const full_out_path = try self.base.emit.root_dir.join(arena, &[_][]const u8{self.base.emit.sub_path});
10861003
1087 var argv = std.ArrayList([]const u8).init(arena);
1004 const argv = &self.dump_argv_list;
10881005
1089 try argv.append("zig");
1006 try argv.append(gpa, "zig");
10901007
10911008 if (self.base.isStaticLib()) {
1092 try argv.append("ar");
1009 try argv.append(gpa, "ar");
10931010 } else {
1094 try argv.append("ld");
1011 try argv.append(gpa, "ld");
10951012 }
10961013
10971014 if (self.base.isObject()) {
1098 try argv.append("-r");
1015 try argv.append(gpa, "-r");
10991016 }
11001017
1101 try argv.append("-o");
1102 try argv.append(full_out_path);
1018 try argv.append(gpa, "-o");
1019 try argv.append(gpa, full_out_path);
11031020
1104 if (self.base.isRelocatable()) {
1105 for (comp.objects) |obj| {
1106 try argv.append(try obj.path.toString(arena));
1107 }
1108
1109 for (comp.c_object_table.keys()) |key| {
1110 try argv.append(try key.status.success.object_path.toString(arena));
1111 }
1112
1113 if (module_obj_path) |p| {
1114 try argv.append(p);
1115 }
1116 } else {
1021 if (!self.base.isRelocatable()) {
11171022 if (!self.base.isStatic()) {
11181023 if (target.dynamic_linker.get()) |path| {
1119 try argv.append("-dynamic-linker");
1120 try argv.append(path);
1024 try argv.appendSlice(gpa, &.{ "-dynamic-linker", try arena.dupe(u8, path) });
11211025 }
11221026 }
11231027
11241028 if (self.base.isDynLib()) {
11251029 if (self.soname) |name| {
1126 try argv.append("-soname");
1127 try argv.append(name);
1030 try argv.append(gpa, "-soname");
1031 try argv.append(gpa, name);
11281032 }
11291033 }
11301034
11311035 if (self.entry_name) |name| {
1132 try argv.appendSlice(&.{ "--entry", name });
1036 try argv.appendSlice(gpa, &.{ "--entry", name });
11331037 }
11341038
11351039 for (self.rpath_table.keys()) |rpath| {
1136 try argv.appendSlice(&.{ "-rpath", rpath });
1040 try argv.appendSlice(gpa, &.{ "-rpath", rpath });
11371041 }
11381042
1139 try argv.appendSlice(&.{
1043 try argv.appendSlice(gpa, &.{
11401044 "-z",
11411045 try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}),
11421046 });
11431047
1144 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base}));
1048 try argv.append(gpa, try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base}));
11451049
11461050 if (self.base.gc_sections) {
1147 try argv.append("--gc-sections");
1051 try argv.append(gpa, "--gc-sections");
11481052 }
11491053
11501054 if (self.base.print_gc_sections) {
1151 try argv.append("--print-gc-sections");
1055 try argv.append(gpa, "--print-gc-sections");
11521056 }
11531057
11541058 if (comp.link_eh_frame_hdr) {
1155 try argv.append("--eh-frame-hdr");
1059 try argv.append(gpa, "--eh-frame-hdr");
11561060 }
11571061
11581062 if (comp.config.rdynamic) {
1159 try argv.append("--export-dynamic");
1063 try argv.append(gpa, "--export-dynamic");
11601064 }
11611065
11621066 if (self.z_notext) {
1163 try argv.append("-z");
1164 try argv.append("notext");
1067 try argv.append(gpa, "-z");
1068 try argv.append(gpa, "notext");
11651069 }
11661070
11671071 if (self.z_nocopyreloc) {
1168 try argv.append("-z");
1169 try argv.append("nocopyreloc");
1072 try argv.append(gpa, "-z");
1073 try argv.append(gpa, "nocopyreloc");
11701074 }
11711075
11721076 if (self.z_now) {
1173 try argv.append("-z");
1174 try argv.append("now");
1077 try argv.append(gpa, "-z");
1078 try argv.append(gpa, "now");
11751079 }
11761080
11771081 if (self.base.isStatic()) {
1178 try argv.append("-static");
1082 try argv.append(gpa, "-static");
11791083 } else if (self.isEffectivelyDynLib()) {
1180 try argv.append("-shared");
1084 try argv.append(gpa, "-shared");
11811085 }
11821086
11831087 if (comp.config.pie and self.base.isExe()) {
1184 try argv.append("-pie");
1088 try argv.append(gpa, "-pie");
11851089 }
11861090
11871091 if (comp.config.debug_format == .strip) {
1188 try argv.append("-s");
1189 }
1190
1191 // csu prelude
1192 if (csu.crt0) |path| try argv.append(try path.toString(arena));
1193 if (csu.crti) |path| try argv.append(try path.toString(arena));
1194 if (csu.crtbegin) |path| try argv.append(try path.toString(arena));
1195
1196 for (self.lib_dirs) |lib_dir| {
1197 try argv.append("-L");
1198 try argv.append(lib_dir);
1199 }
1200
1201 if (comp.config.link_libc) {
1202 if (self.base.comp.libc_installation) |libc_installation| {
1203 try argv.append("-L");
1204 try argv.append(libc_installation.crt_dir.?);
1205 }
1206 }
1207
1208 var whole_archive = false;
1209 for (comp.objects) |obj| {
1210 if (obj.must_link and !whole_archive) {
1211 try argv.append("-whole-archive");
1212 whole_archive = true;
1213 } else if (!obj.must_link and whole_archive) {
1214 try argv.append("-no-whole-archive");
1215 whole_archive = false;
1216 }
1217
1218 if (obj.loption) {
1219 try argv.append("-l");
1220 }
1221 try argv.append(try obj.path.toString(arena));
1222 }
1223 if (whole_archive) {
1224 try argv.append("-no-whole-archive");
1225 whole_archive = false;
1226 }
1227
1228 for (comp.c_object_table.keys()) |key| {
1229 try argv.append(try key.status.success.object_path.toString(arena));
1230 }
1231
1232 if (module_obj_path) |p| {
1233 try argv.append(p);
1234 }
1235
1236 if (comp.config.any_sanitize_thread) {
1237 try argv.append(try comp.tsan_lib.?.full_object_path.toString(arena));
1092 try argv.append(gpa, "-s");
12381093 }
12391094
1240 if (comp.config.any_fuzz) {
1241 try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));
1242 }
1243
1244 // libc
1245 if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
1246 if (comp.libc_static_lib) |lib| {
1247 try argv.append(try lib.full_object_path.toString(arena));
1248 }
1249 }
1250
1251 // Shared libraries.
1252 // Worst-case, we need an --as-needed argument for every lib, as well
1253 // as one before and one after.
1254 try argv.ensureUnusedCapacity(self.base.comp.system_libs.keys().len * 2 + 2);
1255 argv.appendAssumeCapacity("--as-needed");
1256 var as_needed = true;
1257
1258 for (self.base.comp.system_libs.values()) |lib_info| {
1259 const lib_as_needed = !lib_info.needed;
1260 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
1261 0b00, 0b11 => {},
1262 0b01 => {
1263 argv.appendAssumeCapacity("--no-as-needed");
1264 as_needed = false;
1265 },
1266 0b10 => {
1267 argv.appendAssumeCapacity("--as-needed");
1268 as_needed = true;
1269 },
1270 }
1271 argv.appendAssumeCapacity(try lib_info.path.?.toString(arena));
1272 }
1273
1274 if (!as_needed) {
1275 argv.appendAssumeCapacity("--as-needed");
1276 as_needed = true;
1277 }
1278
1279 // libc++ dep
1280 if (comp.config.link_libcpp) {
1281 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
1282 try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
1283 }
1284
1285 // libunwind dep
1286 if (comp.config.link_libunwind) {
1287 try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
1288 }
1289
1290 // libc dep
12911095 if (comp.config.link_libc) {
1292 if (self.base.comp.libc_installation != null) {
1293 const needs_grouping = link_mode == .static;
1294 if (needs_grouping) try argv.append("--start-group");
1295 try argv.appendSlice(target_util.libcFullLinkFlags(target));
1296 if (needs_grouping) try argv.append("--end-group");
1297 } else if (target.isGnuLibC()) {
1298 for (glibc.libs) |lib| {
1299 if (lib.removed_in) |rem_in| {
1300 if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue;
1301 }
1302
1303 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
1304 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1305 });
1306 try argv.append(lib_path);
1307 }
1308 try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a"));
1309 } else if (target.isMusl()) {
1310 try argv.append(try comp.crtFileAsString(arena, switch (link_mode) {
1311 .static => "libc.a",
1312 .dynamic => "libc.so",
1313 }));
1096 if (self.base.comp.libc_installation) |lci| {
1097 try argv.append(gpa, "-L");
1098 try argv.append(gpa, lci.crt_dir.?);
13141099 }
13151100 }
1316
1317 // compiler-rt
1318 if (compiler_rt_path) |p| {
1319 try argv.append(p);
1320 }
1321
1322 // crt postlude
1323 if (csu.crtend) |path| try argv.append(try path.toString(arena));
1324 if (csu.crtn) |path| try argv.append(try path.toString(arena));
13251101 }
1326
1327 Compilation.dump_argv(argv.items);
13281102}
13291103
1330pub const ParseError = error{
1331 /// Indicates the error is already reported on `Compilation.link_diags`.
1332 LinkFailure,
1333
1334 OutOfMemory,
1335 Overflow,
1336 InputOutput,
1337 EndOfStream,
1338 FileSystem,
1339 NotSupported,
1340 InvalidCharacter,
1341 UnknownFileType,
1342} || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;
1343
1344fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CrtFile) void {
1345 parseInputReportingFailure(self, crt_file.full_object_path, false, false);
1346}
1347
1348pub fn parseInputReportingFailure(self: *Elf, path: Path, needed: bool, must_link: bool) void {
1349 const gpa = self.base.comp.gpa;
1104pub fn openParseObjectReportingFailure(self: *Elf, path: Path) void {
13501105 const diags = &self.base.comp.link_diags;
1351 const target = self.getTarget();
1352
1353 switch (Compilation.classifyFileExt(path.sub_path)) {
1354 .object => parseObjectReportingFailure(self, path),
1355 .shared_library => parseSharedObject(gpa, diags, .{
1356 .path = path,
1357 .needed = needed,
1358 }, &self.shared_objects, &self.files, target) catch |err| switch (err) {
1359 error.LinkFailure => return, // already reported
1360 error.BadMagic, error.UnexpectedEndOfFile => {
1361 // It could be a linker script.
1362 self.parseLdScript(.{ .path = path, .needed = needed }) catch |err2| switch (err2) {
1363 error.LinkFailure => return, // already reported
1364 else => |e| diags.addParseError(path, "failed to parse linker script: {s}", .{@errorName(e)}),
1365 };
1366 },
1367 else => |e| diags.addParseError(path, "failed to parse shared object: {s}", .{@errorName(e)}),
1368 },
1369 .static_library => parseArchive(self, path, must_link) catch |err| switch (err) {
1370 error.LinkFailure => return, // already reported
1371 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1372 },
1373 .unknown => self.parseLdScript(.{ .path = path, .needed = needed }) catch |err| switch (err) {
1374 error.LinkFailure => return, // already reported
1375 else => |e| diags.addParseError(path, "failed to parse linker script: {s}", .{@errorName(e)}),
1376 },
1377 else => diags.addParseError(path, "unrecognized file type", .{}),
1378 }
1106 const obj = link.openObject(path, false, false) catch |err| {
1107 switch (diags.failParse(path, "failed to open object {}: {s}", .{ path, @errorName(err) })) {
1108 error.LinkFailure => return,
1109 }
1110 };
1111 self.parseObjectReportingFailure(obj);
13791112}
13801113
1381pub fn parseObjectReportingFailure(self: *Elf, path: Path) void {
1114fn parseObjectReportingFailure(self: *Elf, obj: link.Input.Object) void {
13821115 const diags = &self.base.comp.link_diags;
1383 self.parseObject(path) catch |err| switch (err) {
1116 self.parseObject(obj) catch |err| switch (err) {
13841117 error.LinkFailure => return, // already reported
1385 else => |e| diags.addParseError(path, "unable to parse object: {s}", .{@errorName(e)}),
1118 else => |e| diags.addParseError(obj.path, "failed to parse object: {s}", .{@errorName(e)}),
13861119 };
13871120}
13881121
1389fn parseObject(self: *Elf, path: Path) ParseError!void {
1122fn parseObject(self: *Elf, obj: link.Input.Object) !void {
13901123 const tracy = trace(@src());
13911124 defer tracy.end();
13921125
13931126 const gpa = self.base.comp.gpa;
1394 const handle = try path.root_dir.handle.openFile(path.sub_path, .{});
1395 const fh = try self.addFileHandle(handle);
1127 const diags = &self.base.comp.link_diags;
1128 const first_eflags = &self.first_eflags;
1129 const target = self.base.comp.root_mod.resolved_target.result;
1130 const debug_fmt_strip = self.base.comp.config.debug_format == .strip;
1131 const default_sym_version = self.default_sym_version;
1132 const file_handles = &self.file_handles;
1133
1134 const handle = obj.file;
1135 const fh = try addFileHandle(gpa, file_handles, handle);
13961136
13971137 const index: File.Index = @intCast(try self.files.addOne(gpa));
13981138 self.files.set(index, .{ .object = .{
13991139 .path = .{
1400 .root_dir = path.root_dir,
1401 .sub_path = try gpa.dupe(u8, path.sub_path),
1140 .root_dir = obj.path.root_dir,
1141 .sub_path = try gpa.dupe(u8, obj.path.sub_path),
14021142 },
14031143 .file_handle = fh,
14041144 .index = index,
......@@ -1406,39 +1146,51 @@ fn parseObject(self: *Elf, path: Path) ParseError!void {
14061146 try self.objects.append(gpa, index);
14071147
14081148 const object = self.file(index).?.object;
1409 try object.parse(self);
1149 try object.parseCommon(gpa, diags, obj.path, handle, target, first_eflags);
1150 if (!self.base.isStaticLib()) {
1151 try object.parse(gpa, diags, obj.path, handle, target, debug_fmt_strip, default_sym_version);
1152 }
14101153}
14111154
1412fn parseArchive(self: *Elf, path: Path, must_link: bool) ParseError!void {
1155fn parseArchive(
1156 gpa: Allocator,
1157 diags: *Diags,
1158 file_handles: *std.ArrayListUnmanaged(File.Handle),
1159 files: *std.MultiArrayList(File.Entry),
1160 first_eflags: *?elf.Word,
1161 target: std.Target,
1162 debug_fmt_strip: bool,
1163 default_sym_version: elf.Versym,
1164 objects: *std.ArrayListUnmanaged(File.Index),
1165 obj: link.Input.Object,
1166 is_static_lib: bool,
1167) !void {
14131168 const tracy = trace(@src());
14141169 defer tracy.end();
14151170
1416 const gpa = self.base.comp.gpa;
1417 const handle = try path.root_dir.handle.openFile(path.sub_path, .{});
1418 const fh = try self.addFileHandle(handle);
1419
1420 var archive: Archive = .{};
1171 const fh = try addFileHandle(gpa, file_handles, obj.file);
1172 var archive = try Archive.parse(gpa, diags, file_handles, obj.path, fh);
14211173 defer archive.deinit(gpa);
1422 try archive.parse(self, path, fh);
14231174
1424 const objects = try archive.objects.toOwnedSlice(gpa);
1425 defer gpa.free(objects);
1175 const init_alive = if (is_static_lib) true else obj.must_link;
14261176
1427 for (objects) |extracted| {
1428 const index: File.Index = @intCast(try self.files.addOne(gpa));
1429 self.files.set(index, .{ .object = extracted });
1430 const object = &self.files.items(.data)[index].object;
1177 for (archive.objects) |extracted| {
1178 const index: File.Index = @intCast(try files.addOne(gpa));
1179 files.set(index, .{ .object = extracted });
1180 const object = &files.items(.data)[index].object;
14311181 object.index = index;
1432 object.alive = must_link;
1433 try object.parse(self);
1434 try self.objects.append(gpa, index);
1182 object.alive = init_alive;
1183 try object.parseCommon(gpa, diags, obj.path, obj.file, target, first_eflags);
1184 if (!is_static_lib)
1185 try object.parse(gpa, diags, obj.path, obj.file, target, debug_fmt_strip, default_sym_version);
1186 try objects.append(gpa, index);
14351187 }
14361188}
14371189
1438fn parseSharedObject(
1190fn parseDso(
14391191 gpa: Allocator,
14401192 diags: *Diags,
1441 lib: SystemLib,
1193 dso: link.Input.Dso,
14421194 shared_objects: *std.StringArrayHashMapUnmanaged(File.Index),
14431195 files: *std.MultiArrayList(File.Entry),
14441196 target: std.Target,
......@@ -1446,20 +1198,16 @@ fn parseSharedObject(
14461198 const tracy = trace(@src());
14471199 defer tracy.end();
14481200
1449 const handle = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{});
1450 defer handle.close();
1201 const handle = dso.file;
14511202
14521203 const stat = Stat.fromFs(try handle.stat());
1453 var header = try SharedObject.parseHeader(gpa, diags, lib.path, handle, stat, target);
1204 var header = try SharedObject.parseHeader(gpa, diags, dso.path, handle, stat, target);
14541205 defer header.deinit(gpa);
14551206
1456 const soname = header.soname() orelse lib.path.basename();
1207 const soname = header.soname() orelse dso.path.basename();
14571208
14581209 const gop = try shared_objects.getOrPut(gpa, soname);
1459 if (gop.found_existing) {
1460 header.deinit(gpa);
1461 return;
1462 }
1210 if (gop.found_existing) return;
14631211 errdefer _ = shared_objects.pop();
14641212
14651213 const index: File.Index = @intCast(try files.addOne(gpa));
......@@ -1471,8 +1219,8 @@ fn parseSharedObject(
14711219 errdefer parsed.deinit(gpa);
14721220
14731221 const duped_path: Path = .{
1474 .root_dir = lib.path.root_dir,
1475 .sub_path = try gpa.dupe(u8, lib.path.sub_path),
1222 .root_dir = dso.path.root_dir,
1223 .sub_path = try gpa.dupe(u8, dso.path.sub_path),
14761224 };
14771225 errdefer gpa.free(duped_path.sub_path);
14781226
......@@ -1481,8 +1229,8 @@ fn parseSharedObject(
14811229 .parsed = parsed,
14821230 .path = duped_path,
14831231 .index = index,
1484 .needed = lib.needed,
1485 .alive = lib.needed,
1232 .needed = dso.needed,
1233 .alive = dso.needed,
14861234 .aliases = null,
14871235 .symbols = .empty,
14881236 .symbols_extra = .empty,
......@@ -1490,7 +1238,7 @@ fn parseSharedObject(
14901238 .output_symtab_ctx = .{},
14911239 },
14921240 });
1493 const so = fileLookup(files.*, index).?.shared_object;
1241 const so = fileLookup(files.*, index, null).?.shared_object;
14941242
14951243 // TODO: save this work for later
14961244 const nsyms = parsed.symbols.len;
......@@ -1511,148 +1259,6 @@ fn parseSharedObject(
15111259 }
15121260}
15131261
1514fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
1515 const tracy = trace(@src());
1516 defer tracy.end();
1517
1518 const comp = self.base.comp;
1519 const gpa = comp.gpa;
1520 const diags = &comp.link_diags;
1521
1522 const in_file = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{});
1523 defer in_file.close();
1524 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
1525 defer gpa.free(data);
1526
1527 var script: LdScript = .{ .path = lib.path };
1528 defer script.deinit(gpa);
1529 try script.parse(data, self);
1530
1531 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
1532 defer arena_allocator.deinit();
1533 const arena = arena_allocator.allocator();
1534
1535 var test_path = std.ArrayList(u8).init(arena);
1536 var checked_paths = std.ArrayList([]const u8).init(arena);
1537
1538 for (script.args.items) |script_arg| {
1539 checked_paths.clearRetainingCapacity();
1540
1541 success: {
1542 if (mem.startsWith(u8, script_arg.path, "-l")) {
1543 const lib_name = script_arg.path["-l".len..];
1544
1545 // TODO I think technically we should re-use the mechanism used by the frontend here.
1546 // Maybe we should hoist search-strategy all the way here?
1547 for (self.lib_dirs) |lib_dir| {
1548 if (!self.base.isStatic()) {
1549 if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, lib_name, .dynamic))
1550 break :success;
1551 }
1552 if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, lib_name, .static))
1553 break :success;
1554 }
1555 } else {
1556 var buffer: [fs.max_path_bytes]u8 = undefined;
1557 if (fs.realpath(script_arg.path, &buffer)) |path| {
1558 test_path.clearRetainingCapacity();
1559 try test_path.writer().writeAll(path);
1560 break :success;
1561 } else |_| {}
1562
1563 try checked_paths.append(try arena.dupe(u8, script_arg.path));
1564 for (self.lib_dirs) |lib_dir| {
1565 if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, script_arg.path, null))
1566 break :success;
1567 }
1568 }
1569
1570 diags.addMissingLibraryError(
1571 checked_paths.items,
1572 "missing library dependency: GNU ld script '{}' requires '{s}', but file not found",
1573 .{ @as(Path, lib.path), script_arg.path },
1574 );
1575 continue;
1576 }
1577
1578 const full_path = Path.initCwd(test_path.items);
1579 parseInputReportingFailure(self, full_path, script_arg.needed, false);
1580 }
1581}
1582
1583pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Word) !void {
1584 if (self.first_eflags == null) {
1585 self.first_eflags = e_flags;
1586 return; // there isn't anything to conflict with yet
1587 }
1588 const self_eflags: *elf.Word = &self.first_eflags.?;
1589
1590 switch (self.getTarget().cpu.arch) {
1591 .riscv64 => {
1592 if (e_flags != self_eflags.*) {
1593 const riscv_eflags: riscv.RiscvEflags = @bitCast(e_flags);
1594 const self_riscv_eflags: *riscv.RiscvEflags = @ptrCast(self_eflags);
1595
1596 self_riscv_eflags.rvc = self_riscv_eflags.rvc or riscv_eflags.rvc;
1597 self_riscv_eflags.tso = self_riscv_eflags.tso or riscv_eflags.tso;
1598
1599 var any_errors: bool = false;
1600 if (self_riscv_eflags.fabi != riscv_eflags.fabi) {
1601 any_errors = true;
1602 try self.addFileError(
1603 file_index,
1604 "cannot link object files with different float-point ABIs",
1605 .{},
1606 );
1607 }
1608 if (self_riscv_eflags.rve != riscv_eflags.rve) {
1609 any_errors = true;
1610 try self.addFileError(
1611 file_index,
1612 "cannot link object files with different RVEs",
1613 .{},
1614 );
1615 }
1616 if (any_errors) return error.LinkFailure;
1617 }
1618 },
1619 else => {},
1620 }
1621}
1622
1623fn accessLibPath(
1624 self: *Elf,
1625 arena: Allocator,
1626 test_path: *std.ArrayList(u8),
1627 checked_paths: ?*std.ArrayList([]const u8),
1628 lib_dir_path: []const u8,
1629 lib_name: []const u8,
1630 link_mode: ?std.builtin.LinkMode,
1631) !bool {
1632 const sep = fs.path.sep_str;
1633 const target = self.getTarget();
1634 test_path.clearRetainingCapacity();
1635 const prefix = if (link_mode != null) "lib" else "";
1636 const suffix = if (link_mode) |mode| switch (mode) {
1637 .static => target.staticLibSuffix(),
1638 .dynamic => target.dynamicLibSuffix(),
1639 } else "";
1640 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
1641 lib_dir_path,
1642 prefix,
1643 lib_name,
1644 suffix,
1645 });
1646 if (checked_paths) |cpaths| {
1647 try cpaths.append(try arena.dupe(u8, test_path.items));
1648 }
1649 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1650 error.FileNotFound => return false,
1651 else => |e| return e,
1652 };
1653 return true;
1654}
1655
16561262/// When resolving symbols, we approach the problem similarly to `mold`.
16571263/// 1. Resolve symbols across all objects (including those preemptively extracted archives).
16581264/// 2. Resolve symbols across all shared objects.
......@@ -1842,7 +1448,7 @@ pub fn initOutputSection(self: *Elf, args: struct {
18421448 ".dtors", ".gnu.warning",
18431449 };
18441450 inline for (name_prefixes) |prefix| {
1845 if (std.mem.eql(u8, args.name, prefix) or std.mem.startsWith(u8, args.name, prefix ++ ".")) {
1451 if (mem.eql(u8, args.name, prefix) or mem.startsWith(u8, args.name, prefix ++ ".")) {
18461452 break :blk prefix;
18471453 }
18481454 }
......@@ -1854,9 +1460,9 @@ pub fn initOutputSection(self: *Elf, args: struct {
18541460 switch (args.type) {
18551461 elf.SHT_NULL => unreachable,
18561462 elf.SHT_PROGBITS => {
1857 if (std.mem.eql(u8, args.name, ".init_array") or std.mem.startsWith(u8, args.name, ".init_array."))
1463 if (mem.eql(u8, args.name, ".init_array") or mem.startsWith(u8, args.name, ".init_array."))
18581464 break :tt elf.SHT_INIT_ARRAY;
1859 if (std.mem.eql(u8, args.name, ".fini_array") or std.mem.startsWith(u8, args.name, ".fini_array."))
1465 if (mem.eql(u8, args.name, ".fini_array") or mem.startsWith(u8, args.name, ".fini_array."))
18601466 break :tt elf.SHT_FINI_ARRAY;
18611467 break :tt args.type;
18621468 },
......@@ -1951,11 +1557,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
19511557 try man.addOptionalFile(self.version_script);
19521558 man.hash.add(self.allow_undefined_version);
19531559 man.hash.addOptional(self.enable_new_dtags);
1954 for (comp.objects) |obj| {
1955 _ = try man.addFilePath(obj.path, null);
1956 man.hash.add(obj.must_link);
1957 man.hash.add(obj.loption);
1958 }
1560 try link.hashInputs(&man, comp.link_inputs);
19591561 for (comp.c_object_table.keys()) |key| {
19601562 _ = try man.addFilePath(key.status.success.object_path, null);
19611563 }
......@@ -1973,7 +1575,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
19731575 man.hash.add(comp.link_eh_frame_hdr);
19741576 man.hash.add(self.emit_relocs);
19751577 man.hash.add(comp.config.rdynamic);
1976 man.hash.addListOfBytes(self.lib_dirs);
19771578 man.hash.addListOfBytes(self.rpath_table.keys());
19781579 if (output_mode == .Exe) {
19791580 man.hash.add(self.base.stack_size);
......@@ -2003,7 +1604,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
20031604 }
20041605 man.hash.addOptionalBytes(self.soname);
20051606 man.hash.addOptional(comp.version);
2006 try link.hashAddSystemLibs(&man, comp.system_libs);
20071607 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
20081608 man.hash.add(self.base.allow_shlib_undefined);
20091609 man.hash.add(self.bind_global_refs_locally);
......@@ -2050,8 +1650,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
20501650 // here. TODO: think carefully about how we can avoid this redundant operation when doing
20511651 // build-obj. See also the corresponding TODO in linkAsArchive.
20521652 const the_object_path = blk: {
2053 if (comp.objects.len != 0)
2054 break :blk comp.objects[0].path;
1653 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
20551654
20561655 if (comp.c_object_table.count() != 0)
20571656 break :blk comp.c_object_table.keys()[0].status.success.object_path;
......@@ -2267,11 +1866,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
22671866 try argv.appendSlice(&.{ "-wrap", symbol_name });
22681867 }
22691868
2270 for (self.lib_dirs) |lib_dir| {
2271 try argv.append("-L");
2272 try argv.append(lib_dir);
2273 }
2274
22751869 if (comp.config.link_libc) {
22761870 if (comp.libc_installation) |libc_installation| {
22771871 try argv.append("-L");
......@@ -2311,21 +1905,26 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
23111905
23121906 // Positional arguments to the linker such as object files.
23131907 var whole_archive = false;
2314 for (comp.objects) |obj| {
2315 if (obj.must_link and !whole_archive) {
2316 try argv.append("-whole-archive");
2317 whole_archive = true;
2318 } else if (!obj.must_link and whole_archive) {
2319 try argv.append("-no-whole-archive");
2320 whole_archive = false;
2321 }
23221908
2323 if (obj.loption) {
2324 assert(obj.path.sub_path[0] == ':');
2325 try argv.append("-l");
2326 }
2327 try argv.append(try obj.path.toString(arena));
2328 }
1909 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
1910 .res => unreachable, // Windows-only
1911 .dso => continue,
1912 .object, .archive => |obj| {
1913 if (obj.must_link and !whole_archive) {
1914 try argv.append("-whole-archive");
1915 whole_archive = true;
1916 } else if (!obj.must_link and whole_archive) {
1917 try argv.append("-no-whole-archive");
1918 whole_archive = false;
1919 }
1920 try argv.append(try obj.path.toString(arena));
1921 },
1922 .dso_exact => |dso_exact| {
1923 assert(dso_exact.name[0] == ':');
1924 try argv.appendSlice(&.{ "-l", dso_exact.name });
1925 },
1926 };
1927
23291928 if (whole_archive) {
23301929 try argv.append("-no-whole-archive");
23311930 whole_archive = false;
......@@ -2361,35 +1960,35 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
23611960
23621961 // Shared libraries.
23631962 if (is_exe_or_dyn_lib) {
2364 const system_libs = comp.system_libs.keys();
2365 const system_libs_values = comp.system_libs.values();
2366
23671963 // Worst-case, we need an --as-needed argument for every lib, as well
23681964 // as one before and one after.
2369 try argv.ensureUnusedCapacity(system_libs.len * 2 + 2);
2370 argv.appendAssumeCapacity("--as-needed");
1965 try argv.append("--as-needed");
23711966 var as_needed = true;
23721967
2373 for (system_libs_values) |lib_info| {
2374 const lib_as_needed = !lib_info.needed;
2375 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
2376 0b00, 0b11 => {},
2377 0b01 => {
2378 argv.appendAssumeCapacity("--no-as-needed");
2379 as_needed = false;
2380 },
2381 0b10 => {
2382 argv.appendAssumeCapacity("--as-needed");
2383 as_needed = true;
2384 },
2385 }
1968 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
1969 .res => unreachable, // Windows-only
1970 .object, .archive, .dso_exact => continue,
1971 .dso => |dso| {
1972 const lib_as_needed = !dso.needed;
1973 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
1974 0b00, 0b11 => {},
1975 0b01 => {
1976 argv.appendAssumeCapacity("--no-as-needed");
1977 as_needed = false;
1978 },
1979 0b10 => {
1980 argv.appendAssumeCapacity("--as-needed");
1981 as_needed = true;
1982 },
1983 }
23861984
2387 // By this time, we depend on these libs being dynamically linked
2388 // libraries and not static libraries (the check for that needs to be earlier),
2389 // but they could be full paths to .so files, in which case we
2390 // want to avoid prepending "-l".
2391 argv.appendAssumeCapacity(try lib_info.path.?.toString(arena));
2392 }
1985 // By this time, we depend on these libs being dynamically linked
1986 // libraries and not static libraries (the check for that needs to be earlier),
1987 // but they could be full paths to .so files, in which case we
1988 // want to avoid prepending "-l".
1989 argv.appendAssumeCapacity(try dso.path.toString(arena));
1990 },
1991 };
23931992
23941993 if (!as_needed) {
23951994 argv.appendAssumeCapacity("--as-needed");
......@@ -2421,7 +2020,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
24212020 if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue;
24222021 }
24232022
2424 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
2023 const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
24252024 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
24262025 });
24272026 try argv.append(lib_path);
......@@ -3469,8 +3068,14 @@ pub fn sortShdrs(
34693068 };
34703069
34713070 pub fn lessThan(ctx: Context, lhs: @This(), rhs: @This()) bool {
3472 return shdrRank(ctx.shdrs[lhs.shndx], ctx.shstrtab) <
3473 shdrRank(ctx.shdrs[rhs.shndx], ctx.shstrtab);
3071 const lhs_rank = shdrRank(ctx.shdrs[lhs.shndx], ctx.shstrtab);
3072 const rhs_rank = shdrRank(ctx.shdrs[rhs.shndx], ctx.shstrtab);
3073 if (lhs_rank == rhs_rank) {
3074 const lhs_name = shString(ctx.shstrtab, ctx.shdrs[lhs.shndx].sh_name);
3075 const rhs_name = shString(ctx.shstrtab, ctx.shdrs[rhs.shndx].sh_name);
3076 return std.mem.lessThan(u8, lhs_name, rhs_name);
3077 }
3078 return lhs_rank < rhs_rank;
34743079 }
34753080 };
34763081
......@@ -3486,7 +3091,7 @@ pub fn sortShdrs(
34863091 .shdrs = shdrs,
34873092 .shstrtab = shstrtab,
34883093 };
3489 mem.sort(Entry, entries, sort_context, Entry.lessThan);
3094 mem.sortUnstable(Entry, entries, sort_context, Entry.lessThan);
34903095
34913096 const backlinks = try gpa.alloc(u32, entries.len);
34923097 defer gpa.free(backlinks);
......@@ -3515,7 +3120,7 @@ pub fn sortShdrs(
35153120 for (slice.items(.shdr), slice.items(.atom_list_2)) |*shdr, *atom_list| {
35163121 atom_list.output_section_index = backlinks[atom_list.output_section_index];
35173122 for (atom_list.atoms.keys()) |ref| {
3518 fileLookup(files, ref.file).?.atom(ref.index).?.output_section_index = atom_list.output_section_index;
3123 fileLookup(files, ref.file, zig_object_ptr).?.atom(ref.index).?.output_section_index = atom_list.output_section_index;
35193124 }
35203125 if (shdr.sh_type == elf.SHT_RELA) {
35213126 // FIXME:JK we should spin up .symtab potentially earlier, or set all non-dynamic RELA sections
......@@ -4745,30 +4350,30 @@ pub fn thunk(self: *Elf, index: Thunk.Index) *Thunk {
47454350}
47464351
47474352pub fn file(self: *Elf, index: File.Index) ?File {
4748 return fileLookup(self.files, index);
4353 return fileLookup(self.files, index, self.zig_object);
47494354}
47504355
4751fn fileLookup(files: std.MultiArrayList(File.Entry), index: File.Index) ?File {
4356fn fileLookup(files: std.MultiArrayList(File.Entry), index: File.Index, zig_object: ?*ZigObject) ?File {
47524357 const tag = files.items(.tags)[index];
47534358 return switch (tag) {
47544359 .null => null,
47554360 .linker_defined => .{ .linker_defined = &files.items(.data)[index].linker_defined },
4756 .zig_object => .{ .zig_object = &files.items(.data)[index].zig_object },
4361 .zig_object => .{ .zig_object = zig_object.? },
47574362 .object => .{ .object = &files.items(.data)[index].object },
47584363 .shared_object => .{ .shared_object = &files.items(.data)[index].shared_object },
47594364 };
47604365}
47614366
4762pub fn addFileHandle(self: *Elf, handle: fs.File) !File.HandleIndex {
4763 const gpa = self.base.comp.gpa;
4764 const index: File.HandleIndex = @intCast(self.file_handles.items.len);
4765 const fh = try self.file_handles.addOne(gpa);
4766 fh.* = handle;
4767 return index;
4367pub fn addFileHandle(
4368 gpa: Allocator,
4369 file_handles: *std.ArrayListUnmanaged(File.Handle),
4370 handle: fs.File,
4371) Allocator.Error!File.HandleIndex {
4372 try file_handles.append(gpa, handle);
4373 return @intCast(file_handles.items.len - 1);
47684374}
47694375
47704376pub fn fileHandle(self: Elf, index: File.HandleIndex) File.Handle {
4771 assert(index < self.file_handles.items.len);
47724377 return self.file_handles.items[index];
47734378}
47744379
......@@ -4791,8 +4396,7 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32
47914396}
47924397
47934398pub fn zigObjectPtr(self: *Elf) ?*ZigObject {
4794 const index = self.zig_object_index orelse return null;
4795 return self.file(index).?.zig_object;
4399 return self.zig_object;
47964400}
47974401
47984402pub fn linkerDefinedPtr(self: *Elf) ?*LinkerDefined {
......@@ -4870,7 +4474,7 @@ fn shString(
48704474 off: u32,
48714475) [:0]const u8 {
48724476 const slice = shstrtab[off..];
4873 return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
4477 return slice[0..mem.indexOfScalar(u8, slice, 0).? :0];
48744478}
48754479
48764480pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 {
......@@ -5628,7 +5232,6 @@ const GnuHashSection = synthetic_sections.GnuHashSection;
56285232const GotSection = synthetic_sections.GotSection;
56295233const GotPltSection = synthetic_sections.GotPltSection;
56305234const HashSection = synthetic_sections.HashSection;
5631const LdScript = @import("Elf/LdScript.zig");
56325235const LinkerDefined = @import("Elf/LinkerDefined.zig");
56335236const Liveness = @import("../Liveness.zig");
56345237const LlvmObject = @import("../codegen/llvm.zig").Object;
......@@ -5644,4 +5247,3 @@ const Thunk = @import("Elf/Thunk.zig");
56445247const Value = @import("../Value.zig");
56455248const VerneedSection = synthetic_sections.VerneedSection;
56465249const ZigObject = @import("Elf/ZigObject.zig");
5647const riscv = @import("riscv.zig");
src/link/Elf/Archive.zig+50-28
......@@ -1,29 +1,46 @@
1objects: std.ArrayListUnmanaged(Object) = .empty,
2strtab: std.ArrayListUnmanaged(u8) = .empty,
3
4pub fn deinit(self: *Archive, allocator: Allocator) void {
5 self.objects.deinit(allocator);
6 self.strtab.deinit(allocator);
1objects: []const Object,
2/// '\n'-delimited
3strtab: []const u8,
4
5pub fn deinit(a: *Archive, gpa: Allocator) void {
6 gpa.free(a.objects);
7 gpa.free(a.strtab);
8 a.* = undefined;
79}
810
9pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.HandleIndex) !void {
10 const comp = elf_file.base.comp;
11 const gpa = comp.gpa;
12 const diags = &comp.link_diags;
13 const handle = elf_file.fileHandle(handle_index);
11pub fn parse(
12 gpa: Allocator,
13 diags: *Diags,
14 file_handles: *const std.ArrayListUnmanaged(File.Handle),
15 path: Path,
16 handle_index: File.HandleIndex,
17) !Archive {
18 const handle = file_handles.items[handle_index];
19 var pos: usize = 0;
20 {
21 var magic_buffer: [elf.ARMAG.len]u8 = undefined;
22 const n = try handle.preadAll(&magic_buffer, pos);
23 if (n != magic_buffer.len) return error.BadMagic;
24 if (!mem.eql(u8, &magic_buffer, elf.ARMAG)) return error.BadMagic;
25 pos += magic_buffer.len;
26 }
27
1428 const size = (try handle.stat()).size;
1529
16 var pos: usize = elf.ARMAG.len;
17 while (true) {
18 if (pos >= size) break;
19 if (!mem.isAligned(pos, 2)) pos += 1;
30 var objects: std.ArrayListUnmanaged(Object) = .empty;
31 defer objects.deinit(gpa);
2032
21 var hdr_buffer: [@sizeOf(elf.ar_hdr)]u8 = undefined;
33 var strtab: std.ArrayListUnmanaged(u8) = .empty;
34 defer strtab.deinit(gpa);
35
36 while (pos < size) {
37 pos = mem.alignForward(usize, pos, 2);
38
39 var hdr: elf.ar_hdr = undefined;
2240 {
23 const amt = try handle.preadAll(&hdr_buffer, pos);
24 if (amt != @sizeOf(elf.ar_hdr)) return error.InputOutput;
41 const n = try handle.preadAll(mem.asBytes(&hdr), pos);
42 if (n != @sizeOf(elf.ar_hdr)) return error.UnexpectedEndOfFile;
2543 }
26 const hdr = @as(*align(1) const elf.ar_hdr, @ptrCast(&hdr_buffer)).*;
2744 pos += @sizeOf(elf.ar_hdr);
2845
2946 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {
......@@ -37,8 +54,8 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.Hand
3754
3855 if (hdr.isSymtab() or hdr.isSymtab64()) continue;
3956 if (hdr.isStrtab()) {
40 try self.strtab.resize(gpa, obj_size);
41 const amt = try handle.preadAll(self.strtab.items, pos);
57 try strtab.resize(gpa, obj_size);
58 const amt = try handle.preadAll(strtab.items, pos);
4259 if (amt != obj_size) return error.InputOutput;
4360 continue;
4461 }
......@@ -47,7 +64,7 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.Hand
4764 const name = if (hdr.name()) |name|
4865 name
4966 else if (try hdr.nameOffset()) |off|
50 self.getString(off)
67 stringTableLookup(strtab.items, off)
5168 else
5269 unreachable;
5370
......@@ -70,14 +87,18 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.Hand
7087 @as(Path, object.path), @as(Path, path),
7188 });
7289
73 try self.objects.append(gpa, object);
90 try objects.append(gpa, object);
7491 }
92
93 return .{
94 .objects = try objects.toOwnedSlice(gpa),
95 .strtab = try strtab.toOwnedSlice(gpa),
96 };
7597}
7698
77fn getString(self: Archive, off: u32) []const u8 {
78 assert(off < self.strtab.items.len);
79 const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
80 return name[0 .. name.len - 1];
99pub fn stringTableLookup(strtab: []const u8, off: u32) [:'\n']const u8 {
100 const slice = strtab[off..];
101 return slice[0..mem.indexOfScalar(u8, slice, '\n').? :'\n'];
81102}
82103
83104pub fn setArHdr(opts: struct {
......@@ -290,8 +311,9 @@ const fs = std.fs;
290311const log = std.log.scoped(.link);
291312const mem = std.mem;
292313const Path = std.Build.Cache.Path;
314const Allocator = std.mem.Allocator;
293315
294const Allocator = mem.Allocator;
316const Diags = @import("../../link.zig").Diags;
295317const Archive = @This();
296318const Elf = @import("../Elf.zig");
297319const File = @import("file.zig").File;
src/link/Elf/Atom.zig+27-24
......@@ -102,9 +102,13 @@ pub fn relocsShndx(self: Atom) ?u32 {
102102 return self.relocs_section_index;
103103}
104104
105pub fn priority(self: Atom, elf_file: *Elf) u64 {
106 const index = self.file(elf_file).?.index();
107 return (@as(u64, @intCast(index)) << 32) | @as(u64, @intCast(self.input_section_index));
105pub fn priority(atom: Atom, elf_file: *Elf) u64 {
106 const index = atom.file(elf_file).?.index();
107 return priorityLookup(index, atom.input_section_index);
108}
109
110pub fn priorityLookup(file_index: File.Index, input_section_index: u32) u64 {
111 return (@as(u64, @intCast(file_index)) << 32) | @as(u64, @intCast(input_section_index));
108112}
109113
110114/// Returns how much room there is to grow in virtual address space.
......@@ -255,19 +259,13 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El
255259 }
256260}
257261
258pub fn fdes(self: Atom, elf_file: *Elf) []Fde {
259 const extras = self.extra(elf_file);
260 return switch (self.file(elf_file).?) {
261 .shared_object => unreachable,
262 .linker_defined, .zig_object => &[0]Fde{},
263 .object => |x| x.fdes.items[extras.fde_start..][0..extras.fde_count],
264 };
262pub fn fdes(atom: Atom, object: *Object) []Fde {
263 const extras = object.atomExtra(atom.extra_index);
264 return object.fdes.items[extras.fde_start..][0..extras.fde_count];
265265}
266266
267pub fn markFdesDead(self: Atom, elf_file: *Elf) void {
268 for (self.fdes(elf_file)) |*fde| {
269 fde.alive = false;
270 }
267pub fn markFdesDead(self: Atom, object: *Object) void {
268 for (self.fdes(object)) |*fde| fde.alive = false;
271269}
272270
273271pub fn addReloc(self: Atom, alloc: Allocator, reloc: elf.Elf64_Rela, zo: *ZigObject) !void {
......@@ -946,16 +944,21 @@ fn format2(
946944 atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size,
947945 atom.prev_atom_ref, atom.next_atom_ref,
948946 });
949 if (atom.fdes(elf_file).len > 0) {
950 try writer.writeAll(" : fdes{ ");
951 const extras = atom.extra(elf_file);
952 for (atom.fdes(elf_file), extras.fde_start..) |fde, i| {
953 try writer.print("{d}", .{i});
954 if (!fde.alive) try writer.writeAll("([*])");
955 if (i - extras.fde_start < extras.fde_count - 1) try writer.writeAll(", ");
956 }
957 try writer.writeAll(" }");
958 }
947 if (atom.file(elf_file)) |atom_file| switch (atom_file) {
948 .object => |object| {
949 if (atom.fdes(object).len > 0) {
950 try writer.writeAll(" : fdes{ ");
951 const extras = atom.extra(elf_file);
952 for (atom.fdes(object), extras.fde_start..) |fde, i| {
953 try writer.print("{d}", .{i});
954 if (!fde.alive) try writer.writeAll("([*])");
955 if (i - extras.fde_start < extras.fde_count - 1) try writer.writeAll(", ");
956 }
957 try writer.writeAll(" }");
958 }
959 },
960 else => {},
961 };
959962 if (!atom.alive) {
960963 try writer.writeAll(" : [*]");
961964 }
src/link/Elf/LdScript.zig deleted-439
......@@ -1,439 +0,0 @@
1path: Path,
2cpu_arch: ?std.Target.Cpu.Arch = null,
3args: std.ArrayListUnmanaged(Arg) = .empty,
4
5pub const Arg = struct {
6 needed: bool = false,
7 path: []const u8,
8};
9
10pub fn deinit(scr: *LdScript, allocator: Allocator) void {
11 scr.args.deinit(allocator);
12}
13
14pub const Error = error{
15 LinkFailure,
16 UnexpectedToken,
17 UnknownCpuArch,
18 OutOfMemory,
19};
20
21pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void {
22 const comp = elf_file.base.comp;
23 const gpa = comp.gpa;
24 const diags = &comp.link_diags;
25
26 var tokenizer = Tokenizer{ .source = data };
27 var tokens = std.ArrayList(Token).init(gpa);
28 defer tokens.deinit();
29 var line_col = std.ArrayList(LineColumn).init(gpa);
30 defer line_col.deinit();
31
32 var line: usize = 0;
33 var prev_line_last_col: usize = 0;
34
35 while (true) {
36 const tok = tokenizer.next();
37 try tokens.append(tok);
38 const column = tok.start - prev_line_last_col;
39 try line_col.append(.{ .line = line, .column = column });
40 switch (tok.id) {
41 .invalid => {
42 return diags.failParse(scr.path, "invalid token in LD script: '{s}' ({d}:{d})", .{
43 std.fmt.fmtSliceEscapeLower(tok.get(data)), line, column,
44 });
45 },
46 .new_line => {
47 line += 1;
48 prev_line_last_col = tok.end;
49 },
50 .eof => break,
51 else => {},
52 }
53 }
54
55 var it = TokenIterator{ .tokens = tokens.items };
56 var parser = Parser{ .source = data, .it = &it };
57 var args = std.ArrayList(Arg).init(gpa);
58 scr.doParse(.{
59 .parser = &parser,
60 .args = &args,
61 }) catch |err| switch (err) {
62 error.UnexpectedToken => {
63 const last_token_id = parser.it.pos - 1;
64 const last_token = parser.it.get(last_token_id);
65 const lcol = line_col.items[last_token_id];
66 return diags.failParse(scr.path, "unexpected token in LD script: {s}: '{s}' ({d}:{d})", .{
67 @tagName(last_token.id),
68 last_token.get(data),
69 lcol.line,
70 lcol.column,
71 });
72 },
73 else => |e| return e,
74 };
75 scr.args = args.moveToUnmanaged();
76}
77
78fn doParse(scr: *LdScript, ctx: struct {
79 parser: *Parser,
80 args: *std.ArrayList(Arg),
81}) !void {
82 while (true) {
83 ctx.parser.skipAny(&.{ .comment, .new_line });
84
85 if (ctx.parser.maybe(.command)) |cmd_id| {
86 const cmd = ctx.parser.getCommand(cmd_id);
87 switch (cmd) {
88 .output_format => scr.cpu_arch = try ctx.parser.outputFormat(),
89 // TODO we should verify that group only contains libraries
90 .input, .group => try ctx.parser.group(ctx.args),
91 else => return error.UnexpectedToken,
92 }
93 } else break;
94 }
95
96 if (ctx.parser.it.next()) |tok| switch (tok.id) {
97 .eof => {},
98 else => return error.UnexpectedToken,
99 };
100}
101
102const LineColumn = struct {
103 line: usize,
104 column: usize,
105};
106
107const Command = enum {
108 output_format,
109 input,
110 group,
111 as_needed,
112
113 fn fromString(s: []const u8) ?Command {
114 inline for (@typeInfo(Command).@"enum".fields) |field| {
115 const upper_name = n: {
116 comptime var buf: [field.name.len]u8 = undefined;
117 inline for (field.name, 0..) |c, i| {
118 buf[i] = comptime std.ascii.toUpper(c);
119 }
120 break :n buf;
121 };
122 if (std.mem.eql(u8, &upper_name, s)) return @field(Command, field.name);
123 }
124 return null;
125 }
126};
127
128const Parser = struct {
129 source: []const u8,
130 it: *TokenIterator,
131
132 fn outputFormat(p: *Parser) !std.Target.Cpu.Arch {
133 const value = value: {
134 if (p.skip(&.{.lparen})) {
135 const value_id = try p.require(.literal);
136 const value = p.it.get(value_id);
137 _ = try p.require(.rparen);
138 break :value value.get(p.source);
139 } else if (p.skip(&.{ .new_line, .lbrace })) {
140 const value_id = try p.require(.literal);
141 const value = p.it.get(value_id);
142 _ = p.skip(&.{.new_line});
143 _ = try p.require(.rbrace);
144 break :value value.get(p.source);
145 } else return error.UnexpectedToken;
146 };
147 if (std.mem.eql(u8, value, "elf64-x86-64")) return .x86_64;
148 if (std.mem.eql(u8, value, "elf64-littleaarch64")) return .aarch64;
149 return error.UnknownCpuArch;
150 }
151
152 fn group(p: *Parser, args: *std.ArrayList(Arg)) !void {
153 if (!p.skip(&.{.lparen})) return error.UnexpectedToken;
154
155 while (true) {
156 if (p.maybe(.literal)) |tok_id| {
157 const tok = p.it.get(tok_id);
158 const path = tok.get(p.source);
159 try args.append(.{ .path = path, .needed = true });
160 } else if (p.maybe(.command)) |cmd_id| {
161 const cmd = p.getCommand(cmd_id);
162 switch (cmd) {
163 .as_needed => try p.asNeeded(args),
164 else => return error.UnexpectedToken,
165 }
166 } else break;
167 }
168
169 _ = try p.require(.rparen);
170 }
171
172 fn asNeeded(p: *Parser, args: *std.ArrayList(Arg)) !void {
173 if (!p.skip(&.{.lparen})) return error.UnexpectedToken;
174
175 while (p.maybe(.literal)) |tok_id| {
176 const tok = p.it.get(tok_id);
177 const path = tok.get(p.source);
178 try args.append(.{ .path = path, .needed = false });
179 }
180
181 _ = try p.require(.rparen);
182 }
183
184 fn skip(p: *Parser, comptime ids: []const Token.Id) bool {
185 const pos = p.it.pos;
186 inline for (ids) |id| {
187 const tok = p.it.next() orelse return false;
188 if (tok.id != id) {
189 p.it.seekTo(pos);
190 return false;
191 }
192 }
193 return true;
194 }
195
196 fn skipAny(p: *Parser, comptime ids: []const Token.Id) void {
197 outer: while (p.it.next()) |tok| {
198 inline for (ids) |id| {
199 if (id == tok.id) continue :outer;
200 }
201 break p.it.seekBy(-1);
202 }
203 }
204
205 fn maybe(p: *Parser, comptime id: Token.Id) ?Token.Index {
206 const pos = p.it.pos;
207 const tok = p.it.next() orelse return null;
208 if (tok.id == id) return pos;
209 p.it.seekBy(-1);
210 return null;
211 }
212
213 fn require(p: *Parser, comptime id: Token.Id) !Token.Index {
214 return p.maybe(id) orelse return error.UnexpectedToken;
215 }
216
217 fn getCommand(p: *Parser, index: Token.Index) Command {
218 const tok = p.it.get(index);
219 assert(tok.id == .command);
220 return Command.fromString(tok.get(p.source)).?;
221 }
222};
223
224const Token = struct {
225 id: Id,
226 start: usize,
227 end: usize,
228
229 const Id = enum {
230 // zig fmt: off
231 eof,
232 invalid,
233
234 new_line,
235 lparen, // (
236 rparen, // )
237 lbrace, // {
238 rbrace, // }
239
240 comment, // /* */
241
242 command, // literal with special meaning, see Command
243 literal,
244 // zig fmt: on
245 };
246
247 const Index = usize;
248
249 fn get(tok: Token, source: []const u8) []const u8 {
250 return source[tok.start..tok.end];
251 }
252};
253
254const Tokenizer = struct {
255 source: []const u8,
256 index: usize = 0,
257
258 fn matchesPattern(comptime pattern: []const u8, slice: []const u8) bool {
259 comptime var count: usize = 0;
260 inline while (count < pattern.len) : (count += 1) {
261 if (count >= slice.len) return false;
262 const c = slice[count];
263 if (pattern[count] != c) return false;
264 }
265 return true;
266 }
267
268 fn matches(tok: Tokenizer, comptime pattern: []const u8) bool {
269 return matchesPattern(pattern, tok.source[tok.index..]);
270 }
271
272 fn isCommand(tok: Tokenizer, start: usize, end: usize) bool {
273 return if (Command.fromString(tok.source[start..end]) == null) false else true;
274 }
275
276 fn next(tok: *Tokenizer) Token {
277 var result = Token{
278 .id = .eof,
279 .start = tok.index,
280 .end = undefined,
281 };
282
283 var state: enum {
284 start,
285 comment,
286 literal,
287 } = .start;
288
289 while (tok.index < tok.source.len) : (tok.index += 1) {
290 const c = tok.source[tok.index];
291 switch (state) {
292 .start => switch (c) {
293 ' ', '\t' => result.start += 1,
294
295 '\n' => {
296 result.id = .new_line;
297 tok.index += 1;
298 break;
299 },
300
301 '\r' => {
302 if (tok.matches("\r\n")) {
303 result.id = .new_line;
304 tok.index += "\r\n".len;
305 } else {
306 result.id = .invalid;
307 tok.index += 1;
308 }
309 break;
310 },
311
312 '/' => if (tok.matches("/*")) {
313 state = .comment;
314 tok.index += "/*".len;
315 } else {
316 state = .literal;
317 },
318
319 '(' => {
320 result.id = .lparen;
321 tok.index += 1;
322 break;
323 },
324
325 ')' => {
326 result.id = .rparen;
327 tok.index += 1;
328 break;
329 },
330
331 '{' => {
332 result.id = .lbrace;
333 tok.index += 1;
334 break;
335 },
336
337 '}' => {
338 result.id = .rbrace;
339 tok.index += 1;
340 break;
341 },
342
343 else => state = .literal,
344 },
345
346 .comment => switch (c) {
347 '*' => if (tok.matches("*/")) {
348 result.id = .comment;
349 tok.index += "*/".len;
350 break;
351 },
352 else => {},
353 },
354
355 .literal => switch (c) {
356 ' ', '(', '\n' => {
357 if (tok.isCommand(result.start, tok.index)) {
358 result.id = .command;
359 } else {
360 result.id = .literal;
361 }
362 break;
363 },
364
365 ')' => {
366 result.id = .literal;
367 break;
368 },
369
370 '\r' => {
371 if (tok.matches("\r\n")) {
372 if (tok.isCommand(result.start, tok.index)) {
373 result.id = .command;
374 } else {
375 result.id = .literal;
376 }
377 } else {
378 result.id = .invalid;
379 tok.index += 1;
380 }
381 break;
382 },
383
384 else => {},
385 },
386 }
387 }
388
389 result.end = tok.index;
390 return result;
391 }
392};
393
394const TokenIterator = struct {
395 tokens: []const Token,
396 pos: Token.Index = 0,
397
398 fn next(it: *TokenIterator) ?Token {
399 const token = it.peek() orelse return null;
400 it.pos += 1;
401 return token;
402 }
403
404 fn peek(it: TokenIterator) ?Token {
405 if (it.pos >= it.tokens.len) return null;
406 return it.tokens[it.pos];
407 }
408
409 fn reset(it: *TokenIterator) void {
410 it.pos = 0;
411 }
412
413 fn seekTo(it: *TokenIterator, pos: Token.Index) void {
414 it.pos = pos;
415 }
416
417 fn seekBy(it: *TokenIterator, offset: isize) void {
418 const new_pos = @as(isize, @bitCast(it.pos)) + offset;
419 if (new_pos < 0) {
420 it.pos = 0;
421 } else {
422 it.pos = @as(usize, @intCast(new_pos));
423 }
424 }
425
426 fn get(it: *TokenIterator, pos: Token.Index) Token {
427 assert(pos < it.tokens.len);
428 return it.tokens[pos];
429 }
430};
431
432const LdScript = @This();
433
434const std = @import("std");
435const assert = std.debug.assert;
436const Path = std.Build.Cache.Path;
437
438const Allocator = std.mem.Allocator;
439const Elf = @import("../Elf.zig");
src/link/Elf/Object.zig+190-131
......@@ -37,72 +37,84 @@ num_dynrelocs: u32 = 0,
3737output_symtab_ctx: Elf.SymtabCtx = .{},
3838output_ar_state: Archive.ArState = .{},
3939
40pub fn deinit(self: *Object, allocator: Allocator) void {
41 if (self.archive) |*ar| allocator.free(ar.path.sub_path);
42 allocator.free(self.path.sub_path);
43 self.shdrs.deinit(allocator);
44 self.symtab.deinit(allocator);
45 self.strtab.deinit(allocator);
46 self.symbols.deinit(allocator);
47 self.symbols_extra.deinit(allocator);
48 self.symbols_resolver.deinit(allocator);
49 self.atoms.deinit(allocator);
50 self.atoms_indexes.deinit(allocator);
51 self.atoms_extra.deinit(allocator);
52 self.comdat_groups.deinit(allocator);
53 self.comdat_group_data.deinit(allocator);
54 self.relocs.deinit(allocator);
55 self.fdes.deinit(allocator);
56 self.cies.deinit(allocator);
57 self.eh_frame_data.deinit(allocator);
40pub fn deinit(self: *Object, gpa: Allocator) void {
41 if (self.archive) |*ar| gpa.free(ar.path.sub_path);
42 gpa.free(self.path.sub_path);
43 self.shdrs.deinit(gpa);
44 self.symtab.deinit(gpa);
45 self.strtab.deinit(gpa);
46 self.symbols.deinit(gpa);
47 self.symbols_extra.deinit(gpa);
48 self.symbols_resolver.deinit(gpa);
49 self.atoms.deinit(gpa);
50 self.atoms_indexes.deinit(gpa);
51 self.atoms_extra.deinit(gpa);
52 self.comdat_groups.deinit(gpa);
53 self.comdat_group_data.deinit(gpa);
54 self.relocs.deinit(gpa);
55 self.fdes.deinit(gpa);
56 self.cies.deinit(gpa);
57 self.eh_frame_data.deinit(gpa);
5858 for (self.input_merge_sections.items) |*isec| {
59 isec.deinit(allocator);
59 isec.deinit(gpa);
6060 }
61 self.input_merge_sections.deinit(allocator);
62 self.input_merge_sections_indexes.deinit(allocator);
61 self.input_merge_sections.deinit(gpa);
62 self.input_merge_sections_indexes.deinit(gpa);
6363}
6464
65pub fn parse(self: *Object, elf_file: *Elf) !void {
66 const gpa = elf_file.base.comp.gpa;
67 const cpu_arch = elf_file.getTarget().cpu.arch;
68 const handle = elf_file.fileHandle(self.file_handle);
69
70 try self.parseCommon(gpa, handle, elf_file);
71
65pub fn parse(
66 self: *Object,
67 gpa: Allocator,
68 diags: *Diags,
69 /// For error reporting purposes only.
70 path: Path,
71 handle: fs.File,
72 target: std.Target,
73 debug_fmt_strip: bool,
74 default_sym_version: elf.Versym,
75) !void {
7276 // Append null input merge section
7377 try self.input_merge_sections.append(gpa, .{});
7478 // Allocate atom index 0 to null atom
7579 try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) });
7680
77 try self.initAtoms(gpa, handle, elf_file);
78 try self.initSymbols(gpa, elf_file);
81 try self.initAtoms(gpa, diags, path, handle, debug_fmt_strip, target);
82 try self.initSymbols(gpa, default_sym_version);
7983
8084 for (self.shdrs.items, 0..) |shdr, i| {
8185 const atom_ptr = self.atom(self.atoms_indexes.items[i]) orelse continue;
8286 if (!atom_ptr.alive) continue;
83 if ((cpu_arch == .x86_64 and shdr.sh_type == elf.SHT_X86_64_UNWIND) or
84 mem.eql(u8, atom_ptr.name(elf_file), ".eh_frame"))
87 if ((target.cpu.arch == .x86_64 and shdr.sh_type == elf.SHT_X86_64_UNWIND) or
88 mem.eql(u8, self.getString(atom_ptr.name_offset), ".eh_frame"))
8589 {
86 try self.parseEhFrame(gpa, handle, @as(u32, @intCast(i)), elf_file);
90 try self.parseEhFrame(gpa, handle, @intCast(i), target);
8791 }
8892 }
8993}
9094
91fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void {
95pub fn parseCommon(
96 self: *Object,
97 gpa: Allocator,
98 diags: *Diags,
99 path: Path,
100 handle: fs.File,
101 target: std.Target,
102 first_eflags: *?elf.Word,
103) !void {
92104 const offset = if (self.archive) |ar| ar.offset else 0;
93105 const file_size = (try handle.stat()).size;
94106
95 const header_buffer = try Elf.preadAllAlloc(allocator, handle, offset, @sizeOf(elf.Elf64_Ehdr));
96 defer allocator.free(header_buffer);
107 const header_buffer = try Elf.preadAllAlloc(gpa, handle, offset, @sizeOf(elf.Elf64_Ehdr));
108 defer gpa.free(header_buffer);
97109 self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*;
98110
99 const em = elf_file.base.comp.root_mod.resolved_target.result.toElfMachine();
111 const em = target.toElfMachine();
100112 if (em != self.header.?.e_machine) {
101 return elf_file.failFile(self.index, "invalid ELF machine type: {s}", .{
113 return diags.failParse(path, "invalid ELF machine type: {s}", .{
102114 @tagName(self.header.?.e_machine),
103115 });
104116 }
105 try elf_file.validateEFlags(self.index, self.header.?.e_flags);
117 try validateEFlags(diags, path, target, self.header.?.e_flags, first_eflags);
106118
107119 if (self.header.?.e_shnum == 0) return;
108120
......@@ -110,30 +122,30 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil
110122 const shnum = math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow;
111123 const shsize = shnum * @sizeOf(elf.Elf64_Shdr);
112124 if (file_size < offset + shoff or file_size < offset + shoff + shsize) {
113 return elf_file.failFile(self.index, "corrupt header: section header table extends past the end of file", .{});
125 return diags.failParse(path, "corrupt header: section header table extends past the end of file", .{});
114126 }
115127
116 const shdrs_buffer = try Elf.preadAllAlloc(allocator, handle, offset + shoff, shsize);
117 defer allocator.free(shdrs_buffer);
128 const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, offset + shoff, shsize);
129 defer gpa.free(shdrs_buffer);
118130 const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum];
119 try self.shdrs.appendUnalignedSlice(allocator, shdrs);
131 try self.shdrs.appendUnalignedSlice(gpa, shdrs);
120132
121133 for (self.shdrs.items) |shdr| {
122134 if (shdr.sh_type != elf.SHT_NOBITS) {
123135 if (file_size < offset + shdr.sh_offset or file_size < offset + shdr.sh_offset + shdr.sh_size) {
124 return elf_file.failFile(self.index, "corrupt section: extends past the end of file", .{});
136 return diags.failParse(path, "corrupt section: extends past the end of file", .{});
125137 }
126138 }
127139 }
128140
129 const shstrtab = try self.preadShdrContentsAlloc(allocator, handle, self.header.?.e_shstrndx);
130 defer allocator.free(shstrtab);
141 const shstrtab = try self.preadShdrContentsAlloc(gpa, handle, self.header.?.e_shstrndx);
142 defer gpa.free(shstrtab);
131143 for (self.shdrs.items) |shdr| {
132144 if (shdr.sh_name >= shstrtab.len) {
133 return elf_file.failFile(self.index, "corrupt section name offset", .{});
145 return diags.failParse(path, "corrupt section name offset", .{});
134146 }
135147 }
136 try self.strtab.appendSlice(allocator, shstrtab);
148 try self.strtab.appendSlice(gpa, shstrtab);
137149
138150 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
139151 elf.SHT_SYMTAB => break @as(u32, @intCast(i)),
......@@ -144,19 +156,19 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil
144156 const shdr = self.shdrs.items[index];
145157 self.first_global = shdr.sh_info;
146158
147 const raw_symtab = try self.preadShdrContentsAlloc(allocator, handle, index);
148 defer allocator.free(raw_symtab);
159 const raw_symtab = try self.preadShdrContentsAlloc(gpa, handle, index);
160 defer gpa.free(raw_symtab);
149161 const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch {
150 return elf_file.failFile(self.index, "symbol table not evenly divisible", .{});
162 return diags.failParse(path, "symbol table not evenly divisible", .{});
151163 };
152164 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
153165
154166 const strtab_bias = @as(u32, @intCast(self.strtab.items.len));
155 const strtab = try self.preadShdrContentsAlloc(allocator, handle, shdr.sh_link);
156 defer allocator.free(strtab);
157 try self.strtab.appendSlice(allocator, strtab);
167 const strtab = try self.preadShdrContentsAlloc(gpa, handle, shdr.sh_link);
168 defer gpa.free(strtab);
169 try self.strtab.appendSlice(gpa, strtab);
158170
159 try self.symtab.ensureUnusedCapacity(allocator, symtab.len);
171 try self.symtab.ensureUnusedCapacity(gpa, symtab.len);
160172 for (symtab) |sym| {
161173 const out_sym = self.symtab.addOneAssumeCapacity();
162174 out_sym.* = sym;
......@@ -168,15 +180,56 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil
168180 }
169181}
170182
171fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void {
172 const comp = elf_file.base.comp;
173 const debug_fmt_strip = comp.config.debug_format == .strip;
174 const target = comp.root_mod.resolved_target.result;
183fn validateEFlags(
184 diags: *Diags,
185 path: Path,
186 target: std.Target,
187 e_flags: elf.Word,
188 first_eflags: *?elf.Word,
189) error{LinkFailure}!void {
190 if (first_eflags.*) |*self_eflags| {
191 switch (target.cpu.arch) {
192 .riscv64 => {
193 if (e_flags != self_eflags.*) {
194 const riscv_eflags: riscv.RiscvEflags = @bitCast(e_flags);
195 const self_riscv_eflags: *riscv.RiscvEflags = @ptrCast(self_eflags);
196
197 self_riscv_eflags.rvc = self_riscv_eflags.rvc or riscv_eflags.rvc;
198 self_riscv_eflags.tso = self_riscv_eflags.tso or riscv_eflags.tso;
199
200 var any_errors: bool = false;
201 if (self_riscv_eflags.fabi != riscv_eflags.fabi) {
202 any_errors = true;
203 diags.addParseError(path, "cannot link object files with different float-point ABIs", .{});
204 }
205 if (self_riscv_eflags.rve != riscv_eflags.rve) {
206 any_errors = true;
207 diags.addParseError(path, "cannot link object files with different RVEs", .{});
208 }
209 if (any_errors) return error.LinkFailure;
210 }
211 },
212 else => {},
213 }
214 } else {
215 first_eflags.* = e_flags;
216 }
217}
218
219fn initAtoms(
220 self: *Object,
221 gpa: Allocator,
222 diags: *Diags,
223 path: Path,
224 handle: fs.File,
225 debug_fmt_strip: bool,
226 target: std.Target,
227) !void {
175228 const shdrs = self.shdrs.items;
176 try self.atoms.ensureTotalCapacityPrecise(allocator, shdrs.len);
177 try self.atoms_extra.ensureTotalCapacityPrecise(allocator, shdrs.len * @sizeOf(Atom.Extra));
178 try self.atoms_indexes.ensureTotalCapacityPrecise(allocator, shdrs.len);
179 try self.atoms_indexes.resize(allocator, shdrs.len);
229 try self.atoms.ensureTotalCapacityPrecise(gpa, shdrs.len);
230 try self.atoms_extra.ensureTotalCapacityPrecise(gpa, shdrs.len * @sizeOf(Atom.Extra));
231 try self.atoms_indexes.ensureTotalCapacityPrecise(gpa, shdrs.len);
232 try self.atoms_indexes.resize(gpa, shdrs.len);
180233 @memset(self.atoms_indexes.items, 0);
181234
182235 for (shdrs, 0..) |shdr, i| {
......@@ -201,24 +254,24 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
201254 };
202255
203256 const shndx: u32 = @intCast(i);
204 const group_raw_data = try self.preadShdrContentsAlloc(allocator, handle, shndx);
205 defer allocator.free(group_raw_data);
257 const group_raw_data = try self.preadShdrContentsAlloc(gpa, handle, shndx);
258 defer gpa.free(group_raw_data);
206259 const group_nmembers = math.divExact(usize, group_raw_data.len, @sizeOf(u32)) catch {
207 return elf_file.failFile(self.index, "corrupt section group: not evenly divisible ", .{});
260 return diags.failParse(path, "corrupt section group: not evenly divisible ", .{});
208261 };
209262 if (group_nmembers == 0) {
210 return elf_file.failFile(self.index, "corrupt section group: empty section", .{});
263 return diags.failParse(path, "corrupt section group: empty section", .{});
211264 }
212265 const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];
213266
214267 if (group_members[0] != elf.GRP_COMDAT) {
215 return elf_file.failFile(self.index, "corrupt section group: unknown SHT_GROUP format", .{});
268 return diags.failParse(path, "corrupt section group: unknown SHT_GROUP format", .{});
216269 }
217270
218271 const group_start: u32 = @intCast(self.comdat_group_data.items.len);
219 try self.comdat_group_data.appendUnalignedSlice(allocator, group_members[1..]);
272 try self.comdat_group_data.appendUnalignedSlice(gpa, group_members[1..]);
220273
221 const comdat_group_index = try self.addComdatGroup(allocator);
274 const comdat_group_index = try self.addComdatGroup(gpa);
222275 const comdat_group = self.comdatGroup(comdat_group_index);
223276 comdat_group.* = .{
224277 .signature_off = group_signature,
......@@ -242,8 +295,8 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
242295 const shndx: u32 = @intCast(i);
243296 if (self.skipShdr(shndx, debug_fmt_strip)) continue;
244297 const size, const alignment = if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) blk: {
245 const data = try self.preadShdrContentsAlloc(allocator, handle, shndx);
246 defer allocator.free(data);
298 const data = try self.preadShdrContentsAlloc(gpa, handle, shndx);
299 defer gpa.free(data);
247300 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
248301 break :blk .{ chdr.ch_size, Alignment.fromNonzeroByteUnits(chdr.ch_addralign) };
249302 } else .{ shdr.sh_size, Alignment.fromNonzeroByteUnits(shdr.sh_addralign) };
......@@ -263,13 +316,13 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
263316 elf.SHT_REL, elf.SHT_RELA => {
264317 const atom_index = self.atoms_indexes.items[shdr.sh_info];
265318 if (self.atom(atom_index)) |atom_ptr| {
266 const relocs = try self.preadRelocsAlloc(allocator, handle, @intCast(i));
267 defer allocator.free(relocs);
319 const relocs = try self.preadRelocsAlloc(gpa, handle, @intCast(i));
320 defer gpa.free(relocs);
268321 atom_ptr.relocs_section_index = @intCast(i);
269322 const rel_index: u32 = @intCast(self.relocs.items.len);
270323 const rel_count: u32 = @intCast(relocs.len);
271324 self.setAtomFields(atom_ptr, .{ .rel_index = rel_index, .rel_count = rel_count });
272 try self.relocs.appendUnalignedSlice(allocator, relocs);
325 try self.relocs.appendUnalignedSlice(gpa, relocs);
273326 if (target.cpu.arch == .riscv64) {
274327 sortRelocs(self.relocs.items[rel_index..][0..rel_count]);
275328 }
......@@ -293,14 +346,18 @@ fn skipShdr(self: *Object, index: u32, debug_fmt_strip: bool) bool {
293346 return ignore;
294347}
295348
296fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void {
349fn initSymbols(
350 self: *Object,
351 gpa: Allocator,
352 default_sym_version: elf.Versym,
353) !void {
297354 const first_global = self.first_global orelse self.symtab.items.len;
298355 const nglobals = self.symtab.items.len - first_global;
299356
300 try self.symbols.ensureTotalCapacityPrecise(allocator, self.symtab.items.len);
301 try self.symbols_extra.ensureTotalCapacityPrecise(allocator, self.symtab.items.len * @sizeOf(Symbol.Extra));
302 try self.symbols_resolver.ensureTotalCapacityPrecise(allocator, nglobals);
303 self.symbols_resolver.resize(allocator, nglobals) catch unreachable;
357 try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.items.len);
358 try self.symbols_extra.ensureTotalCapacityPrecise(gpa, self.symtab.items.len * @sizeOf(Symbol.Extra));
359 try self.symbols_resolver.ensureTotalCapacityPrecise(gpa, nglobals);
360 self.symbols_resolver.resize(gpa, nglobals) catch unreachable;
304361 @memset(self.symbols_resolver.items, 0);
305362
306363 for (self.symtab.items, 0..) |sym, i| {
......@@ -310,7 +367,7 @@ fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void {
310367 sym_ptr.name_offset = sym.st_name;
311368 sym_ptr.esym_index = @intCast(i);
312369 sym_ptr.extra_index = self.addSymbolExtraAssumeCapacity(.{});
313 sym_ptr.version_index = if (i >= first_global) elf_file.default_sym_version else .LOCAL;
370 sym_ptr.version_index = if (i >= first_global) default_sym_version else .LOCAL;
314371 sym_ptr.flags.weak = sym.st_bind() == elf.STB_WEAK;
315372 if (sym.st_shndx != elf.SHN_ABS and sym.st_shndx != elf.SHN_COMMON) {
316373 sym_ptr.ref = .{ .index = self.atoms_indexes.items[sym.st_shndx], .file = self.index };
......@@ -318,24 +375,30 @@ fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void {
318375 }
319376}
320377
321fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx: u32, elf_file: *Elf) !void {
378fn parseEhFrame(
379 self: *Object,
380 gpa: Allocator,
381 handle: fs.File,
382 shndx: u32,
383 target: std.Target,
384) !void {
322385 const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
323386 elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u32, @intCast(i)),
324387 else => {},
325388 } else null;
326389
327 const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx);
328 defer allocator.free(raw);
329 const data_start = @as(u32, @intCast(self.eh_frame_data.items.len));
330 try self.eh_frame_data.appendSlice(allocator, raw);
390 const raw = try self.preadShdrContentsAlloc(gpa, handle, shndx);
391 defer gpa.free(raw);
392 const data_start: u32 = @intCast(self.eh_frame_data.items.len);
393 try self.eh_frame_data.appendSlice(gpa, raw);
331394 const relocs = if (relocs_shndx) |index|
332 try self.preadRelocsAlloc(allocator, handle, index)
395 try self.preadRelocsAlloc(gpa, handle, index)
333396 else
334397 &[0]elf.Elf64_Rela{};
335 defer allocator.free(relocs);
336 const rel_start = @as(u32, @intCast(self.relocs.items.len));
337 try self.relocs.appendUnalignedSlice(allocator, relocs);
338 if (elf_file.getTarget().cpu.arch == .riscv64) {
398 defer gpa.free(relocs);
399 const rel_start: u32 = @intCast(self.relocs.items.len);
400 try self.relocs.appendUnalignedSlice(gpa, relocs);
401 if (target.cpu.arch == .riscv64) {
339402 sortRelocs(self.relocs.items[rel_start..][0..relocs.len]);
340403 }
341404 const fdes_start = self.fdes.items.len;
......@@ -345,11 +408,11 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
345408 while (try it.next()) |rec| {
346409 const rel_range = filterRelocs(self.relocs.items[rel_start..][0..relocs.len], rec.offset, rec.size + 4);
347410 switch (rec.tag) {
348 .cie => try self.cies.append(allocator, .{
411 .cie => try self.cies.append(gpa, .{
349412 .offset = data_start + rec.offset,
350413 .size = rec.size,
351414 .rel_index = rel_start + @as(u32, @intCast(rel_range.start)),
352 .rel_num = @as(u32, @intCast(rel_range.len)),
415 .rel_num = @intCast(rel_range.len),
353416 .input_section_index = shndx,
354417 .file_index = self.index,
355418 }),
......@@ -361,12 +424,12 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
361424 // this can happen for object files built with -r flag by the linker.
362425 continue;
363426 }
364 try self.fdes.append(allocator, .{
427 try self.fdes.append(gpa, .{
365428 .offset = data_start + rec.offset,
366429 .size = rec.size,
367430 .cie_index = undefined,
368431 .rel_index = rel_start + @as(u32, @intCast(rel_range.start)),
369 .rel_num = @as(u32, @intCast(rel_range.len)),
432 .rel_num = @intCast(rel_range.len),
370433 .input_section_index = shndx,
371434 .file_index = self.index,
372435 });
......@@ -376,7 +439,7 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
376439
377440 // Tie each FDE to its CIE
378441 for (self.fdes.items[fdes_start..]) |*fde| {
379 const cie_ptr = fde.offset + 4 - fde.ciePointer(elf_file);
442 const cie_ptr = fde.offset + 4 - fde.ciePointer(self);
380443 const cie_index = for (self.cies.items[cies_start..], cies_start..) |cie, cie_index| {
381444 if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index));
382445 } else {
......@@ -392,26 +455,26 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
392455
393456 // Tie each FDE record to its matching atom
394457 const SortFdes = struct {
395 pub fn lessThan(ctx: *Elf, lhs: Fde, rhs: Fde) bool {
458 pub fn lessThan(ctx: *Object, lhs: Fde, rhs: Fde) bool {
396459 const lhs_atom = lhs.atom(ctx);
397460 const rhs_atom = rhs.atom(ctx);
398 return lhs_atom.priority(ctx) < rhs_atom.priority(ctx);
461 return Atom.priorityLookup(ctx.index, lhs_atom.input_section_index) < Atom.priorityLookup(ctx.index, rhs_atom.input_section_index);
399462 }
400463 };
401 mem.sort(Fde, self.fdes.items[fdes_start..], elf_file, SortFdes.lessThan);
464 mem.sort(Fde, self.fdes.items[fdes_start..], self, SortFdes.lessThan);
402465
403466 // Create a back-link from atom to FDEs
404 var i: u32 = @as(u32, @intCast(fdes_start));
467 var i: u32 = @intCast(fdes_start);
405468 while (i < self.fdes.items.len) {
406469 const fde = self.fdes.items[i];
407 const atom_ptr = fde.atom(elf_file);
470 const atom_ptr = fde.atom(self);
408471 const start = i;
409472 i += 1;
410473 while (i < self.fdes.items.len) : (i += 1) {
411474 const next_fde = self.fdes.items[i];
412 if (atom_ptr.atom_index != next_fde.atom(elf_file).atom_index) break;
475 if (atom_ptr.atom_index != next_fde.atom(self).atom_index) break;
413476 }
414 atom_ptr.addExtra(.{ .fde_start = start, .fde_count = i - start }, elf_file);
477 self.setAtomFields(atom_ptr, .{ .fde_start = start, .fde_count = i - start });
415478 }
416479}
417480
......@@ -904,7 +967,7 @@ pub fn markComdatGroupsDead(self: *Object, elf_file: *Elf) void {
904967 const atom_index = self.atoms_indexes.items[shndx];
905968 if (self.atom(atom_index)) |atom_ptr| {
906969 atom_ptr.alive = false;
907 atom_ptr.markFdesDead(elf_file);
970 atom_ptr.markFdesDead(self);
908971 }
909972 }
910973 }
......@@ -970,12 +1033,6 @@ pub fn addAtomsToRelaSections(self: *Object, elf_file: *Elf) !void {
9701033 }
9711034}
9721035
973pub fn parseAr(self: *Object, elf_file: *Elf) !void {
974 const gpa = elf_file.base.comp.gpa;
975 const handle = elf_file.fileHandle(self.file_handle);
976 try self.parseCommon(gpa, handle, elf_file);
977}
978
9791036pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {
9801037 const comp = elf_file.base.comp;
9811038 const gpa = comp.gpa;
......@@ -1000,7 +1057,7 @@ pub fn updateArSize(self: *Object, elf_file: *Elf) !void {
10001057pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void {
10011058 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
10021059 const offset: u64 = if (self.archive) |ar| ar.offset else 0;
1003 const name = std.fs.path.basename(self.path.sub_path);
1060 const name = fs.path.basename(self.path.sub_path);
10041061 const hdr = Archive.setArHdr(.{
10051062 .name = if (name.len <= Archive.max_member_name_len)
10061063 .{ .name = name }
......@@ -1136,8 +1193,8 @@ pub fn resolveSymbol(self: Object, index: Symbol.Index, elf_file: *Elf) Elf.Ref
11361193 return elf_file.resolver.get(resolv).?;
11371194}
11381195
1139fn addSymbol(self: *Object, allocator: Allocator) !Symbol.Index {
1140 try self.symbols.ensureUnusedCapacity(allocator, 1);
1196fn addSymbol(self: *Object, gpa: Allocator) !Symbol.Index {
1197 try self.symbols.ensureUnusedCapacity(gpa, 1);
11411198 return self.addSymbolAssumeCapacity();
11421199}
11431200
......@@ -1147,9 +1204,9 @@ fn addSymbolAssumeCapacity(self: *Object) Symbol.Index {
11471204 return index;
11481205}
11491206
1150pub fn addSymbolExtra(self: *Object, allocator: Allocator, extra: Symbol.Extra) !u32 {
1207pub fn addSymbolExtra(self: *Object, gpa: Allocator, extra: Symbol.Extra) !u32 {
11511208 const fields = @typeInfo(Symbol.Extra).@"struct".fields;
1152 try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
1209 try self.symbols_extra.ensureUnusedCapacity(gpa, fields.len);
11531210 return self.addSymbolExtraAssumeCapacity(extra);
11541211}
11551212
......@@ -1198,27 +1255,27 @@ pub fn getString(self: Object, off: u32) [:0]const u8 {
11981255 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
11991256}
12001257
1201fn addString(self: *Object, allocator: Allocator, str: []const u8) !u32 {
1258fn addString(self: *Object, gpa: Allocator, str: []const u8) !u32 {
12021259 const off: u32 = @intCast(self.strtab.items.len);
1203 try self.strtab.ensureUnusedCapacity(allocator, str.len + 1);
1260 try self.strtab.ensureUnusedCapacity(gpa, str.len + 1);
12041261 self.strtab.appendSliceAssumeCapacity(str);
12051262 self.strtab.appendAssumeCapacity(0);
12061263 return off;
12071264}
12081265
12091266/// Caller owns the memory.
1210fn preadShdrContentsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, index: u32) ![]u8 {
1267fn preadShdrContentsAlloc(self: Object, gpa: Allocator, handle: fs.File, index: u32) ![]u8 {
12111268 assert(index < self.shdrs.items.len);
12121269 const offset = if (self.archive) |ar| ar.offset else 0;
12131270 const shdr = self.shdrs.items[index];
12141271 const sh_offset = math.cast(u64, shdr.sh_offset) orelse return error.Overflow;
12151272 const sh_size = math.cast(u64, shdr.sh_size) orelse return error.Overflow;
1216 return Elf.preadAllAlloc(allocator, handle, offset + sh_offset, sh_size);
1273 return Elf.preadAllAlloc(gpa, handle, offset + sh_offset, sh_size);
12171274}
12181275
12191276/// Caller owns the memory.
1220fn preadRelocsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, shndx: u32) ![]align(1) const elf.Elf64_Rela {
1221 const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx);
1277fn preadRelocsAlloc(self: Object, gpa: Allocator, handle: fs.File, shndx: u32) ![]align(1) const elf.Elf64_Rela {
1278 const raw = try self.preadShdrContentsAlloc(gpa, handle, shndx);
12221279 const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela));
12231280 return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
12241281}
......@@ -1230,9 +1287,9 @@ const AddAtomArgs = struct {
12301287 alignment: Alignment,
12311288};
12321289
1233fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index {
1234 try self.atoms.ensureUnusedCapacity(allocator, 1);
1235 try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
1290fn addAtom(self: *Object, gpa: Allocator, args: AddAtomArgs) !Atom.Index {
1291 try self.atoms.ensureUnusedCapacity(gpa, 1);
1292 try self.atoms_extra.ensureUnusedCapacity(gpa, @sizeOf(Atom.Extra));
12361293 return self.addAtomAssumeCapacity(args);
12371294}
12381295
......@@ -1257,9 +1314,9 @@ pub fn atom(self: *Object, atom_index: Atom.Index) ?*Atom {
12571314 return &self.atoms.items[atom_index];
12581315}
12591316
1260pub fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 {
1317pub fn addAtomExtra(self: *Object, gpa: Allocator, extra: Atom.Extra) !u32 {
12611318 const fields = @typeInfo(Atom.Extra).@"struct".fields;
1262 try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
1319 try self.atoms_extra.ensureUnusedCapacity(gpa, fields.len);
12631320 return self.addAtomExtraAssumeCapacity(extra);
12641321}
12651322
......@@ -1308,9 +1365,9 @@ fn setAtomFields(o: *Object, atom_ptr: *Atom, opts: Atom.Extra.AsOptionals) void
13081365 o.setAtomExtra(atom_ptr.extra_index, extras);
13091366}
13101367
1311fn addInputMergeSection(self: *Object, allocator: Allocator) !Merge.InputSection.Index {
1368fn addInputMergeSection(self: *Object, gpa: Allocator) !Merge.InputSection.Index {
13121369 const index: Merge.InputSection.Index = @intCast(self.input_merge_sections.items.len);
1313 const msec = try self.input_merge_sections.addOne(allocator);
1370 const msec = try self.input_merge_sections.addOne(gpa);
13141371 msec.* = .{};
13151372 return index;
13161373}
......@@ -1320,9 +1377,9 @@ fn inputMergeSection(self: *Object, index: Merge.InputSection.Index) ?*Merge.Inp
13201377 return &self.input_merge_sections.items[index];
13211378}
13221379
1323fn addComdatGroup(self: *Object, allocator: Allocator) !Elf.ComdatGroup.Index {
1380fn addComdatGroup(self: *Object, gpa: Allocator) !Elf.ComdatGroup.Index {
13241381 const index = @as(Elf.ComdatGroup.Index, @intCast(self.comdat_groups.items.len));
1325 _ = try self.comdat_groups.addOne(allocator);
1382 _ = try self.comdat_groups.addOne(gpa);
13261383 return index;
13271384}
13281385
......@@ -1516,8 +1573,9 @@ const log = std.log.scoped(.link);
15161573const math = std.math;
15171574const mem = std.mem;
15181575const Path = std.Build.Cache.Path;
1519const Allocator = mem.Allocator;
1576const Allocator = std.mem.Allocator;
15201577
1578const Diags = @import("../../link.zig").Diags;
15211579const Archive = @import("Archive.zig");
15221580const Atom = @import("Atom.zig");
15231581const AtomList = @import("AtomList.zig");
......@@ -1528,3 +1586,4 @@ const File = @import("file.zig").File;
15281586const Merge = @import("Merge.zig");
15291587const Symbol = @import("Symbol.zig");
15301588const Alignment = Atom.Alignment;
1589const riscv = @import("../riscv.zig");
src/link/Elf/ZigObject.zig+12-18
......@@ -928,7 +928,7 @@ pub fn getNavVAddr(
928928 nav.name.toSlice(ip),
929929 @"extern".lib_name.toSlice(ip),
930930 ),
931 else => try self.getOrCreateMetadataForNav(elf_file, nav_index),
931 else => try self.getOrCreateMetadataForNav(zcu, nav_index),
932932 };
933933 const this_sym = self.symbol(this_sym_index);
934934 const vaddr = this_sym.address(.{}, elf_file);
......@@ -1102,21 +1102,15 @@ pub fn freeNav(self: *ZigObject, elf_file: *Elf, nav_index: InternPool.Nav.Index
11021102 }
11031103}
11041104
1105pub fn getOrCreateMetadataForNav(
1106 self: *ZigObject,
1107 elf_file: *Elf,
1108 nav_index: InternPool.Nav.Index,
1109) !Symbol.Index {
1110 const gpa = elf_file.base.comp.gpa;
1105pub fn getOrCreateMetadataForNav(self: *ZigObject, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
1106 const gpa = zcu.gpa;
11111107 const gop = try self.navs.getOrPut(gpa, nav_index);
11121108 if (!gop.found_existing) {
1113 const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded;
11141109 const symbol_index = try self.newSymbolWithAtom(gpa, 0);
1115 const zcu = elf_file.base.comp.zcu.?;
11161110 const nav_val = Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val);
11171111 const sym = self.symbol(symbol_index);
11181112 if (nav_val.getVariable(zcu)) |variable| {
1119 if (variable.is_threadlocal and any_non_single_threaded) {
1113 if (variable.is_threadlocal and zcu.comp.config.any_non_single_threaded) {
11201114 sym.flags.is_tls = true;
11211115 }
11221116 }
......@@ -1425,8 +1419,8 @@ pub fn updateFunc(
14251419
14261420 log.debug("updateFunc {}({d})", .{ ip.getNav(func.owner_nav).fqn.fmt(ip), func.owner_nav });
14271421
1428 const sym_index = try self.getOrCreateMetadataForNav(elf_file, func.owner_nav);
1429 self.symbol(sym_index).atom(elf_file).?.freeRelocs(self);
1422 const sym_index = try self.getOrCreateMetadataForNav(zcu, func.owner_nav);
1423 self.atom(self.symbol(sym_index).ref.index).?.freeRelocs(self);
14301424
14311425 var code_buffer = std.ArrayList(u8).init(gpa);
14321426 defer code_buffer.deinit();
......@@ -1460,12 +1454,12 @@ pub fn updateFunc(
14601454 ip.getNav(func.owner_nav).fqn.fmt(ip),
14611455 });
14621456 const old_rva, const old_alignment = blk: {
1463 const atom_ptr = self.symbol(sym_index).atom(elf_file).?;
1457 const atom_ptr = self.atom(self.symbol(sym_index).ref.index).?;
14641458 break :blk .{ atom_ptr.value, atom_ptr.alignment };
14651459 };
14661460 try self.updateNavCode(elf_file, pt, func.owner_nav, sym_index, shndx, code, elf.STT_FUNC);
14671461 const new_rva, const new_alignment = blk: {
1468 const atom_ptr = self.symbol(sym_index).atom(elf_file).?;
1462 const atom_ptr = self.atom(self.symbol(sym_index).ref.index).?;
14691463 break :blk .{ atom_ptr.value, atom_ptr.alignment };
14701464 };
14711465
......@@ -1477,7 +1471,7 @@ pub fn updateFunc(
14771471 .{
14781472 .index = sym_index,
14791473 .addr = @intCast(sym.address(.{}, elf_file)),
1480 .size = sym.atom(elf_file).?.size,
1474 .size = self.atom(sym.ref.index).?.size,
14811475 },
14821476 wip_nav,
14831477 );
......@@ -1500,7 +1494,7 @@ pub fn updateFunc(
15001494 });
15011495 defer gpa.free(name);
15021496 const osec = if (self.text_index) |sect_sym_index|
1503 self.symbol(sect_sym_index).atom(elf_file).?.output_section_index
1497 self.atom(self.symbol(sect_sym_index).ref.index).?.output_section_index
15041498 else osec: {
15051499 const osec = try elf_file.addSection(.{
15061500 .name = try elf_file.insertShString(".text"),
......@@ -1565,7 +1559,7 @@ pub fn updateNav(
15651559 };
15661560
15671561 if (nav_init != .none and Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {
1568 const sym_index = try self.getOrCreateMetadataForNav(elf_file, nav_index);
1562 const sym_index = try self.getOrCreateMetadataForNav(zcu, nav_index);
15691563 self.symbol(sym_index).atom(elf_file).?.freeRelocs(self);
15701564
15711565 var code_buffer = std.ArrayList(u8).init(zcu.gpa);
......@@ -1789,7 +1783,7 @@ pub fn updateExports(
17891783 const gpa = elf_file.base.comp.gpa;
17901784 const metadata = switch (exported) {
17911785 .nav => |nav| blk: {
1792 _ = try self.getOrCreateMetadataForNav(elf_file, nav);
1786 _ = try self.getOrCreateMetadataForNav(zcu, nav);
17931787 break :blk self.navs.getPtr(nav).?;
17941788 },
17951789 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
src/link/Elf/eh_frame.zig+17-20
......@@ -19,18 +19,16 @@ pub const Fde = struct {
1919 return base + fde.out_offset;
2020 }
2121
22 pub fn data(fde: Fde, elf_file: *Elf) []u8 {
23 const object = elf_file.file(fde.file_index).?.object;
22 pub fn data(fde: Fde, object: *Object) []u8 {
2423 return object.eh_frame_data.items[fde.offset..][0..fde.calcSize()];
2524 }
2625
27 pub fn cie(fde: Fde, elf_file: *Elf) Cie {
28 const object = elf_file.file(fde.file_index).?.object;
26 pub fn cie(fde: Fde, object: *Object) Cie {
2927 return object.cies.items[fde.cie_index];
3028 }
3129
32 pub fn ciePointer(fde: Fde, elf_file: *Elf) u32 {
33 const fde_data = fde.data(elf_file);
30 pub fn ciePointer(fde: Fde, object: *Object) u32 {
31 const fde_data = fde.data(object);
3432 return std.mem.readInt(u32, fde_data[4..8], .little);
3533 }
3634
......@@ -38,16 +36,14 @@ pub const Fde = struct {
3836 return fde.size + 4;
3937 }
4038
41 pub fn atom(fde: Fde, elf_file: *Elf) *Atom {
42 const object = elf_file.file(fde.file_index).?.object;
43 const rel = fde.relocs(elf_file)[0];
39 pub fn atom(fde: Fde, object: *Object) *Atom {
40 const rel = fde.relocs(object)[0];
4441 const sym = object.symtab.items[rel.r_sym()];
4542 const atom_index = object.atoms_indexes.items[sym.st_shndx];
4643 return object.atom(atom_index).?;
4744 }
4845
49 pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
50 const object = elf_file.file(fde.file_index).?.object;
46 pub fn relocs(fde: Fde, object: *Object) []const elf.Elf64_Rela {
5147 return object.relocs.items[fde.rel_index..][0..fde.rel_num];
5248 }
5349
......@@ -87,7 +83,8 @@ pub const Fde = struct {
8783 const fde = ctx.fde;
8884 const elf_file = ctx.elf_file;
8985 const base_addr = fde.address(elf_file);
90 const atom_name = fde.atom(elf_file).name(elf_file);
86 const object = elf_file.file(fde.file_index).?.object;
87 const atom_name = fde.atom(object).name(elf_file);
9188 try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{
9289 base_addr + fde.out_offset,
9390 fde.calcSize(),
......@@ -306,7 +303,7 @@ pub fn calcEhFrameRelocs(elf_file: *Elf) usize {
306303 }
307304 for (object.fdes.items) |fde| {
308305 if (!fde.alive) continue;
309 count += fde.relocs(elf_file).len;
306 count += fde.relocs(object).len;
310307 }
311308 }
312309 return count;
......@@ -369,16 +366,16 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
369366 for (object.fdes.items) |fde| {
370367 if (!fde.alive) continue;
371368
372 const contents = fde.data(elf_file);
369 const contents = fde.data(object);
373370
374371 std.mem.writeInt(
375372 i32,
376373 contents[4..8],
377 @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(elf_file).out_offset))),
374 @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(object).out_offset))),
378375 .little,
379376 );
380377
381 for (fde.relocs(elf_file)) |rel| {
378 for (fde.relocs(object)) |rel| {
382379 const ref = object.resolveSymbol(rel.r_sym(), elf_file);
383380 const sym = elf_file.symbol(ref).?;
384381 resolveReloc(fde, sym, rel, elf_file, contents) catch |err| switch (err) {
......@@ -412,12 +409,12 @@ pub fn writeEhFrameRelocatable(elf_file: *Elf, writer: anytype) !void {
412409 for (object.fdes.items) |fde| {
413410 if (!fde.alive) continue;
414411
415 const contents = fde.data(elf_file);
412 const contents = fde.data(object);
416413
417414 std.mem.writeInt(
418415 i32,
419416 contents[4..8],
420 @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(elf_file).out_offset))),
417 @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(object).out_offset))),
421418 .little,
422419 );
423420
......@@ -490,7 +487,7 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
490487
491488 for (object.fdes.items) |fde| {
492489 if (!fde.alive) continue;
493 for (fde.relocs(elf_file)) |rel| {
490 for (fde.relocs(object)) |rel| {
494491 const ref = object.resolveSymbol(rel.r_sym(), elf_file);
495492 const sym = elf_file.symbol(ref).?;
496493 const r_offset = fde.address(elf_file) + rel.r_offset - fde.offset;
......@@ -548,7 +545,7 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
548545 for (object.fdes.items) |fde| {
549546 if (!fde.alive) continue;
550547
551 const relocs = fde.relocs(elf_file);
548 const relocs = fde.relocs(object);
552549 assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips...
553550 const rel = relocs[0];
554551 const ref = object.resolveSymbol(rel.r_sym(), elf_file);
src/link/Elf/file.zig+2-2
......@@ -279,8 +279,8 @@ pub const File = union(enum) {
279279 pub const Index = u32;
280280
281281 pub const Entry = union(enum) {
282 null: void,
283 zig_object: ZigObject,
282 null,
283 zig_object,
284284 linker_defined: LinkerDefined,
285285 object: Object,
286286 shared_object: SharedObject,
src/link/Elf/gc.zig+28-21
......@@ -103,15 +103,20 @@ fn markLive(atom: *Atom, elf_file: *Elf) void {
103103 assert(atom.visited);
104104 const file = atom.file(elf_file).?;
105105
106 for (atom.fdes(elf_file)) |fde| {
107 for (fde.relocs(elf_file)[1..]) |rel| {
108 const ref = file.resolveSymbol(rel.r_sym(), elf_file);
109 const target_sym = elf_file.symbol(ref) orelse continue;
110 const target_atom = target_sym.atom(elf_file) orelse continue;
111 target_atom.alive = true;
112 gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index });
113 if (markAtom(target_atom)) markLive(target_atom, elf_file);
114 }
106 switch (file) {
107 .object => |object| {
108 for (atom.fdes(object)) |fde| {
109 for (fde.relocs(object)[1..]) |rel| {
110 const ref = file.resolveSymbol(rel.r_sym(), elf_file);
111 const target_sym = elf_file.symbol(ref) orelse continue;
112 const target_atom = target_sym.atom(elf_file) orelse continue;
113 target_atom.alive = true;
114 gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index });
115 if (markAtom(target_atom)) markLive(target_atom, elf_file);
116 }
117 }
118 },
119 else => {},
115120 }
116121
117122 for (atom.relocs(elf_file)) |rel| {
......@@ -135,23 +140,25 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void {
135140 }
136141}
137142
138fn prune(elf_file: *Elf) void {
139 const pruneInFile = struct {
140 fn pruneInFile(file: File, ef: *Elf) void {
141 for (file.atoms()) |atom_index| {
142 const atom = file.atom(atom_index) orelse continue;
143 if (atom.alive and !atom.visited) {
144 atom.alive = false;
145 atom.markFdesDead(ef);
146 }
143fn pruneInFile(file: File) void {
144 for (file.atoms()) |atom_index| {
145 const atom = file.atom(atom_index) orelse continue;
146 if (atom.alive and !atom.visited) {
147 atom.alive = false;
148 switch (file) {
149 .object => |object| atom.markFdesDead(object),
150 else => {},
147151 }
148152 }
149 }.pruneInFile;
153 }
154}
155
156fn prune(elf_file: *Elf) void {
150157 if (elf_file.zigObjectPtr()) |zo| {
151 pruneInFile(zo.asFile(), elf_file);
158 pruneInFile(zo.asFile());
152159 }
153160 for (elf_file.objects.items) |index| {
154 pruneInFile(elf_file.file(index).?, elf_file);
161 pruneInFile(elf_file.file(index).?);
155162 }
156163}
157164
src/link/Elf/relocatable.zig+4-94
......@@ -1,27 +1,7 @@
1pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void {
1pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void {
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 }
12
13 for (comp.c_object_table.keys()) |key| {
14 parseObjectStaticLibReportingFailure(elf_file, key.status.success.object_path);
15 }
16
17 if (module_obj_path) |path| {
18 parseObjectStaticLibReportingFailure(elf_file, path);
19 }
20
21 if (comp.include_compiler_rt) {
22 parseObjectStaticLibReportingFailure(elf_file, comp.compiler_rt_obj.?.full_object_path);
23 }
24
255 if (diags.hasErrors()) return error.FlushFailure;
266
277 // First, we flush relocatable object file generated with our backends.
......@@ -150,22 +130,9 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path
150130 if (diags.hasErrors()) return error.FlushFailure;
151131}
152132
153pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void {
133pub fn flushObject(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void {
154134 const diags = &comp.link_diags;
155135
156 for (comp.objects) |obj| {
157 elf_file.parseInputReportingFailure(obj.path, false, obj.must_link);
158 }
159
160 // This is a set of object files emitted by clang in a single `build-exe` invocation.
161 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
162 // in this set.
163 for (comp.c_object_table.keys()) |key| {
164 elf_file.parseObjectReportingFailure(key.status.success.object_path);
165 }
166
167 if (module_obj_path) |path| elf_file.parseObjectReportingFailure(path);
168
169136 if (diags.hasErrors()) return error.FlushFailure;
170137
171138 // Now, we are ready to resolve the symbols across all input files.
......@@ -215,64 +182,6 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) l
215182 if (diags.hasErrors()) return error.FlushFailure;
216183}
217184
218fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: Path) void {
219 const diags = &elf_file.base.comp.link_diags;
220 parseObjectStaticLib(elf_file, path) catch |err| switch (err) {
221 error.LinkFailure => return,
222 else => |e| diags.addParseError(path, "parsing object failed: {s}", .{@errorName(e)}),
223 };
224}
225
226fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: Path) void {
227 const diags = &elf_file.base.comp.link_diags;
228 parseArchiveStaticLib(elf_file, path) catch |err| switch (err) {
229 error.LinkFailure => return,
230 else => |e| diags.addParseError(path, "parsing static library failed: {s}", .{@errorName(e)}),
231 };
232}
233
234fn parseObjectStaticLib(elf_file: *Elf, path: Path) Elf.ParseError!void {
235 const gpa = elf_file.base.comp.gpa;
236 const handle = try path.root_dir.handle.openFile(path.sub_path, .{});
237 const fh = try elf_file.addFileHandle(handle);
238
239 const index: File.Index = @intCast(try elf_file.files.addOne(gpa));
240 elf_file.files.set(index, .{ .object = .{
241 .path = .{
242 .root_dir = path.root_dir,
243 .sub_path = try gpa.dupe(u8, path.sub_path),
244 },
245 .file_handle = fh,
246 .index = index,
247 } });
248 try elf_file.objects.append(gpa, index);
249
250 const object = elf_file.file(index).?.object;
251 try object.parseAr(elf_file);
252}
253
254fn parseArchiveStaticLib(elf_file: *Elf, path: Path) Elf.ParseError!void {
255 const gpa = elf_file.base.comp.gpa;
256 const handle = try path.root_dir.handle.openFile(path.sub_path, .{});
257 const fh = try elf_file.addFileHandle(handle);
258
259 var archive = Archive{};
260 defer archive.deinit(gpa);
261 try archive.parse(elf_file, path, fh);
262
263 const objects = try archive.objects.toOwnedSlice(gpa);
264 defer gpa.free(objects);
265
266 for (objects) |extracted| {
267 const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa)));
268 elf_file.files.set(index, .{ .object = extracted });
269 const object = &elf_file.files.items(.data)[index].object;
270 object.index = index;
271 try object.parseAr(elf_file);
272 try elf_file.objects.append(gpa, index);
273 }
274}
275
276185fn claimUnresolved(elf_file: *Elf) void {
277186 if (elf_file.zigObjectPtr()) |zig_object| {
278187 zig_object.claimUnresolvedRelocatable(elf_file);
......@@ -473,11 +382,12 @@ fn writeSyntheticSections(elf_file: *Elf) !void {
473382 const SortRelocs = struct {
474383 pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
475384 _ = ctx;
385 assert(lhs.r_offset != rhs.r_offset);
476386 return lhs.r_offset < rhs.r_offset;
477387 }
478388 };
479389
480 mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
390 mem.sortUnstable(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
481391
482392 log.debug("writing {s} from 0x{x} to 0x{x}", .{
483393 elf_file.getShString(shdr.sh_name),
src/link/LdScript.zig created+449
......@@ -0,0 +1,449 @@
1path: Path,
2cpu_arch: ?std.Target.Cpu.Arch,
3args: []const Arg,
4
5pub const Arg = struct {
6 needed: bool = false,
7 path: []const u8,
8};
9
10pub fn deinit(ls: *LdScript, gpa: Allocator) void {
11 gpa.free(ls.args);
12 ls.* = undefined;
13}
14
15pub const Error = error{
16 LinkFailure,
17 UnknownCpuArch,
18 OutOfMemory,
19};
20
21pub fn parse(
22 gpa: Allocator,
23 diags: *Diags,
24 /// For error reporting.
25 path: Path,
26 data: []const u8,
27) Error!LdScript {
28 var tokenizer = Tokenizer{ .source = data };
29 var tokens: std.ArrayListUnmanaged(Token) = .empty;
30 defer tokens.deinit(gpa);
31 var line_col: std.ArrayListUnmanaged(LineColumn) = .empty;
32 defer line_col.deinit(gpa);
33
34 var line: usize = 0;
35 var prev_line_last_col: usize = 0;
36
37 while (true) {
38 const tok = tokenizer.next();
39 try tokens.append(gpa, tok);
40 const column = tok.start - prev_line_last_col;
41 try line_col.append(gpa, .{ .line = line, .column = column });
42 switch (tok.id) {
43 .invalid => {
44 return diags.failParse(path, "invalid token in LD script: '{s}' ({d}:{d})", .{
45 std.fmt.fmtSliceEscapeLower(tok.get(data)), line, column,
46 });
47 },
48 .new_line => {
49 line += 1;
50 prev_line_last_col = tok.end;
51 },
52 .eof => break,
53 else => {},
54 }
55 }
56
57 var it: TokenIterator = .{ .tokens = tokens.items };
58 var parser: Parser = .{
59 .gpa = gpa,
60 .source = data,
61 .it = &it,
62 .args = .empty,
63 .cpu_arch = null,
64 };
65 defer parser.args.deinit(gpa);
66
67 parser.start() catch |err| switch (err) {
68 error.UnexpectedToken => {
69 const last_token_id = parser.it.pos - 1;
70 const last_token = parser.it.get(last_token_id);
71 const lcol = line_col.items[last_token_id];
72 return diags.failParse(path, "unexpected token in LD script: {s}: '{s}' ({d}:{d})", .{
73 @tagName(last_token.id),
74 last_token.get(data),
75 lcol.line,
76 lcol.column,
77 });
78 },
79 else => |e| return e,
80 };
81 return .{
82 .path = path,
83 .cpu_arch = parser.cpu_arch,
84 .args = try parser.args.toOwnedSlice(gpa),
85 };
86}
87
88const LineColumn = struct {
89 line: usize,
90 column: usize,
91};
92
93const Command = enum {
94 output_format,
95 input,
96 group,
97 as_needed,
98
99 fn fromString(s: []const u8) ?Command {
100 inline for (@typeInfo(Command).@"enum".fields) |field| {
101 const upper_name = n: {
102 comptime var buf: [field.name.len]u8 = undefined;
103 inline for (field.name, 0..) |c, i| {
104 buf[i] = comptime std.ascii.toUpper(c);
105 }
106 break :n buf;
107 };
108 if (std.mem.eql(u8, &upper_name, s)) return @field(Command, field.name);
109 }
110 return null;
111 }
112};
113
114const Parser = struct {
115 gpa: Allocator,
116 source: []const u8,
117 it: *TokenIterator,
118
119 cpu_arch: ?std.Target.Cpu.Arch,
120 args: std.ArrayListUnmanaged(Arg),
121
122 fn start(parser: *Parser) !void {
123 while (true) {
124 parser.skipAny(&.{ .comment, .new_line });
125
126 if (parser.maybe(.command)) |cmd_id| {
127 const cmd = parser.getCommand(cmd_id);
128 switch (cmd) {
129 .output_format => parser.cpu_arch = try parser.outputFormat(),
130 // TODO we should verify that group only contains libraries
131 .input, .group => try parser.group(),
132 else => return error.UnexpectedToken,
133 }
134 } else break;
135 }
136
137 if (parser.it.next()) |tok| switch (tok.id) {
138 .eof => {},
139 else => return error.UnexpectedToken,
140 };
141 }
142
143 fn outputFormat(p: *Parser) !std.Target.Cpu.Arch {
144 const value = value: {
145 if (p.skip(&.{.lparen})) {
146 const value_id = try p.require(.literal);
147 const value = p.it.get(value_id);
148 _ = try p.require(.rparen);
149 break :value value.get(p.source);
150 } else if (p.skip(&.{ .new_line, .lbrace })) {
151 const value_id = try p.require(.literal);
152 const value = p.it.get(value_id);
153 _ = p.skip(&.{.new_line});
154 _ = try p.require(.rbrace);
155 break :value value.get(p.source);
156 } else return error.UnexpectedToken;
157 };
158 if (std.mem.eql(u8, value, "elf64-x86-64")) return .x86_64;
159 if (std.mem.eql(u8, value, "elf64-littleaarch64")) return .aarch64;
160 return error.UnknownCpuArch;
161 }
162
163 fn group(p: *Parser) !void {
164 const gpa = p.gpa;
165 if (!p.skip(&.{.lparen})) return error.UnexpectedToken;
166
167 while (true) {
168 if (p.maybe(.literal)) |tok_id| {
169 const tok = p.it.get(tok_id);
170 const path = tok.get(p.source);
171 try p.args.append(gpa, .{ .path = path, .needed = true });
172 } else if (p.maybe(.command)) |cmd_id| {
173 const cmd = p.getCommand(cmd_id);
174 switch (cmd) {
175 .as_needed => try p.asNeeded(),
176 else => return error.UnexpectedToken,
177 }
178 } else break;
179 }
180
181 _ = try p.require(.rparen);
182 }
183
184 fn asNeeded(p: *Parser) !void {
185 const gpa = p.gpa;
186 if (!p.skip(&.{.lparen})) return error.UnexpectedToken;
187
188 while (p.maybe(.literal)) |tok_id| {
189 const tok = p.it.get(tok_id);
190 const path = tok.get(p.source);
191 try p.args.append(gpa, .{ .path = path, .needed = false });
192 }
193
194 _ = try p.require(.rparen);
195 }
196
197 fn skip(p: *Parser, comptime ids: []const Token.Id) bool {
198 const pos = p.it.pos;
199 inline for (ids) |id| {
200 const tok = p.it.next() orelse return false;
201 if (tok.id != id) {
202 p.it.seekTo(pos);
203 return false;
204 }
205 }
206 return true;
207 }
208
209 fn skipAny(p: *Parser, comptime ids: []const Token.Id) void {
210 outer: while (p.it.next()) |tok| {
211 inline for (ids) |id| {
212 if (id == tok.id) continue :outer;
213 }
214 break p.it.seekBy(-1);
215 }
216 }
217
218 fn maybe(p: *Parser, comptime id: Token.Id) ?Token.Index {
219 const pos = p.it.pos;
220 const tok = p.it.next() orelse return null;
221 if (tok.id == id) return pos;
222 p.it.seekBy(-1);
223 return null;
224 }
225
226 fn require(p: *Parser, comptime id: Token.Id) !Token.Index {
227 return p.maybe(id) orelse return error.UnexpectedToken;
228 }
229
230 fn getCommand(p: *Parser, index: Token.Index) Command {
231 const tok = p.it.get(index);
232 assert(tok.id == .command);
233 return Command.fromString(tok.get(p.source)).?;
234 }
235};
236
237const Token = struct {
238 id: Id,
239 start: usize,
240 end: usize,
241
242 const Id = enum {
243 eof,
244 invalid,
245
246 new_line,
247 lparen, // (
248 rparen, // )
249 lbrace, // {
250 rbrace, // }
251
252 comment, // /* */
253
254 command, // literal with special meaning, see Command
255 literal,
256 };
257
258 const Index = usize;
259
260 fn get(tok: Token, source: []const u8) []const u8 {
261 return source[tok.start..tok.end];
262 }
263};
264
265const Tokenizer = struct {
266 source: []const u8,
267 index: usize = 0,
268
269 fn matchesPattern(comptime pattern: []const u8, slice: []const u8) bool {
270 comptime var count: usize = 0;
271 inline while (count < pattern.len) : (count += 1) {
272 if (count >= slice.len) return false;
273 const c = slice[count];
274 if (pattern[count] != c) return false;
275 }
276 return true;
277 }
278
279 fn matches(tok: Tokenizer, comptime pattern: []const u8) bool {
280 return matchesPattern(pattern, tok.source[tok.index..]);
281 }
282
283 fn isCommand(tok: Tokenizer, start: usize, end: usize) bool {
284 return if (Command.fromString(tok.source[start..end]) == null) false else true;
285 }
286
287 fn next(tok: *Tokenizer) Token {
288 var result = Token{
289 .id = .eof,
290 .start = tok.index,
291 .end = undefined,
292 };
293
294 var state: enum {
295 start,
296 comment,
297 literal,
298 } = .start;
299
300 while (tok.index < tok.source.len) : (tok.index += 1) {
301 const c = tok.source[tok.index];
302 switch (state) {
303 .start => switch (c) {
304 ' ', '\t' => result.start += 1,
305
306 '\n' => {
307 result.id = .new_line;
308 tok.index += 1;
309 break;
310 },
311
312 '\r' => {
313 if (tok.matches("\r\n")) {
314 result.id = .new_line;
315 tok.index += "\r\n".len;
316 } else {
317 result.id = .invalid;
318 tok.index += 1;
319 }
320 break;
321 },
322
323 '/' => if (tok.matches("/*")) {
324 state = .comment;
325 tok.index += "/*".len;
326 } else {
327 state = .literal;
328 },
329
330 '(' => {
331 result.id = .lparen;
332 tok.index += 1;
333 break;
334 },
335
336 ')' => {
337 result.id = .rparen;
338 tok.index += 1;
339 break;
340 },
341
342 '{' => {
343 result.id = .lbrace;
344 tok.index += 1;
345 break;
346 },
347
348 '}' => {
349 result.id = .rbrace;
350 tok.index += 1;
351 break;
352 },
353
354 else => state = .literal,
355 },
356
357 .comment => switch (c) {
358 '*' => if (tok.matches("*/")) {
359 result.id = .comment;
360 tok.index += "*/".len;
361 break;
362 },
363 else => {},
364 },
365
366 .literal => switch (c) {
367 ' ', '(', '\n' => {
368 if (tok.isCommand(result.start, tok.index)) {
369 result.id = .command;
370 } else {
371 result.id = .literal;
372 }
373 break;
374 },
375
376 ')' => {
377 result.id = .literal;
378 break;
379 },
380
381 '\r' => {
382 if (tok.matches("\r\n")) {
383 if (tok.isCommand(result.start, tok.index)) {
384 result.id = .command;
385 } else {
386 result.id = .literal;
387 }
388 } else {
389 result.id = .invalid;
390 tok.index += 1;
391 }
392 break;
393 },
394
395 else => {},
396 },
397 }
398 }
399
400 result.end = tok.index;
401 return result;
402 }
403};
404
405const TokenIterator = struct {
406 tokens: []const Token,
407 pos: Token.Index = 0,
408
409 fn next(it: *TokenIterator) ?Token {
410 const token = it.peek() orelse return null;
411 it.pos += 1;
412 return token;
413 }
414
415 fn peek(it: TokenIterator) ?Token {
416 if (it.pos >= it.tokens.len) return null;
417 return it.tokens[it.pos];
418 }
419
420 fn reset(it: *TokenIterator) void {
421 it.pos = 0;
422 }
423
424 fn seekTo(it: *TokenIterator, pos: Token.Index) void {
425 it.pos = pos;
426 }
427
428 fn seekBy(it: *TokenIterator, offset: isize) void {
429 const new_pos = @as(isize, @bitCast(it.pos)) + offset;
430 if (new_pos < 0) {
431 it.pos = 0;
432 } else {
433 it.pos = @as(usize, @intCast(new_pos));
434 }
435 }
436
437 fn get(it: *TokenIterator, pos: Token.Index) Token {
438 assert(pos < it.tokens.len);
439 return it.tokens[pos];
440 }
441};
442
443const LdScript = @This();
444const Diags = @import("../link.zig").Diags;
445
446const std = @import("std");
447const assert = std.debug.assert;
448const Path = std.Build.Cache.Path;
449const Allocator = std.mem.Allocator;
src/link/MachO.zig+122-71
......@@ -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,40 @@ 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
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 };
448
439449 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)});
450 switch (Compilation.classifyFileExt(lib.path.sub_path)) {
451 .shared_library => {
452 const dso_input = try link.openDsoInput(diags, lib.path, lib.needed, lib.weak, lib.reexport);
453 self.classifyInputFile(dso_input) catch |err|
454 diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)});
455 },
456 .static_library => {
457 const archive_input = try link.openArchiveInput(diags, lib.path, lib.must_link, lib.hidden);
458 self.classifyInputFile(archive_input) catch |err|
459 diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)});
460 },
461 else => unreachable,
462 }
442463 }
443464
444465 // 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)});
466 if (comp.compiler_rt_lib) |crt_file| {
467 const path = crt_file.full_object_path;
468 self.classifyInputFile(try link.openArchiveInput(diags, path, false, false)) catch |err|
469 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
470 } else if (comp.compiler_rt_obj) |crt_file| {
471 const path = crt_file.full_object_path;
472 self.classifyInputFile(try link.openObjectInput(diags, path)) catch |err|
473 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
453474 }
454475
455476 try self.parseInputFiles();
......@@ -596,9 +617,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
596617 }
597618
598619 if (self.base.isRelocatable()) {
599 for (comp.objects) |obj| {
600 try argv.append(try obj.path.toString(arena));
601 }
620 for (comp.link_inputs) |link_input| switch (link_input) {
621 .object, .archive => |obj| try argv.append(try obj.path.toString(arena)),
622 .res => |res| try argv.append(try res.path.toString(arena)),
623 .dso => |dso| try argv.append(try dso.path.toString(arena)),
624 .dso_exact => |dso_exact| try argv.appendSlice(&.{ "-l", dso_exact.name }),
625 };
602626
603627 for (comp.c_object_table.keys()) |key| {
604628 try argv.append(try key.status.success.object_path.toString(arena));
......@@ -678,13 +702,15 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
678702 try argv.append("dynamic_lookup");
679703 }
680704
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 }
705 for (comp.link_inputs) |link_input| switch (link_input) {
706 .dso => continue, // handled below
707 .res => unreachable, // windows only
708 .object, .archive => |obj| {
709 if (obj.must_link) try argv.append("-force_load"); // TODO: verify this
710 try argv.append(try obj.path.toString(arena));
711 },
712 .dso_exact => |dso_exact| try argv.appendSlice(&.{ "-l", dso_exact.name }),
713 };
688714
689715 for (comp.c_object_table.keys()) |key| {
690716 try argv.append(try key.status.success.object_path.toString(arena));
......@@ -703,21 +729,25 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
703729 try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));
704730 }
705731
706 for (self.lib_dirs) |lib_dir| {
707 const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir});
732 for (self.lib_directories) |lib_directory| {
733 // TODO delete this, directories must instead be resolved by the frontend
734 const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_directory.path orelse "."});
708735 try argv.append(arg);
709736 }
710737
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 }
738 for (comp.link_inputs) |link_input| switch (link_input) {
739 .object, .archive, .dso_exact => continue, // handled above
740 .res => unreachable, // windows only
741 .dso => |dso| {
742 if (dso.needed) {
743 try argv.appendSlice(&.{ "-needed-l", try dso.path.toString(arena) });
744 } else if (dso.weak) {
745 try argv.appendSlice(&.{ "-weak-l", try dso.path.toString(arena) });
746 } else {
747 try argv.appendSlice(&.{ "-l", try dso.path.toString(arena) });
748 }
749 },
750 };
721751
722752 for (self.framework_dirs) |f_dir| {
723753 try argv.append("-F");
......@@ -751,6 +781,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
751781 Compilation.dump_argv(argv.items);
752782}
753783
784/// TODO delete this, libsystem must be resolved when setting up the compilation pipeline
754785pub fn resolveLibSystem(
755786 self: *MachO,
756787 arena: Allocator,
......@@ -774,8 +805,8 @@ pub fn resolveLibSystem(
774805 },
775806 };
776807
777 for (self.lib_dirs) |dir| {
778 if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success;
808 for (self.lib_directories) |directory| {
809 if (try accessLibPath(arena, &test_path, &checked_paths, directory.path orelse ".", "System")) break :success;
779810 }
780811
781812 diags.addMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{});
......@@ -789,13 +820,14 @@ pub fn resolveLibSystem(
789820 });
790821}
791822
792pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bool) !void {
823pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
793824 const tracy = trace(@src());
794825 defer tracy.end();
795826
827 const path, const file = input.pathAndFile().?;
828 // TODO don't classify now, it's too late. The input file has already been classified
796829 log.debug("classifying input file {}", .{path});
797830
798 const file = try path.root_dir.handle.openFile(path.sub_path, .{});
799831 const fh = try self.addFileHandle(file);
800832 var buffer: [Archive.SARMAG]u8 = undefined;
801833
......@@ -806,17 +838,17 @@ pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bo
806838 if (h.magic != macho.MH_MAGIC_64) break :blk;
807839 switch (h.filetype) {
808840 macho.MH_OBJECT => try self.addObject(path, fh, offset),
809 macho.MH_DYLIB => _ = try self.addDylib(lib, true, fh, offset),
841 macho.MH_DYLIB => _ = try self.addDylib(.fromLinkInput(input), true, fh, offset),
810842 else => return error.UnknownFileType,
811843 }
812844 return;
813845 }
814846 if (readArMagic(file, offset, &buffer) catch null) |ar_magic| blk: {
815847 if (!mem.eql(u8, ar_magic, Archive.ARMAG)) break :blk;
816 try self.addArchive(lib, must_link, fh, fat_arch);
848 try self.addArchive(input.archive, fh, fat_arch);
817849 return;
818850 }
819 _ = try self.addTbd(lib, true, fh);
851 _ = try self.addTbd(.fromLinkInput(input), true, fh);
820852}
821853
822854fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch {
......@@ -903,7 +935,7 @@ fn parseInputFileWorker(self: *MachO, file: File) void {
903935 };
904936}
905937
906fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.HandleIndex, fat_arch: ?fat.Arch) !void {
938fn addArchive(self: *MachO, lib: link.Input.Object, handle: File.HandleIndex, fat_arch: ?fat.Arch) !void {
907939 const tracy = trace(@src());
908940 defer tracy.end();
909941
......@@ -918,7 +950,7 @@ fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.Handle
918950 self.files.set(index, .{ .object = unpacked });
919951 const object = &self.files.items(.data)[index].object;
920952 object.index = index;
921 object.alive = must_link or lib.needed; // TODO: or self.options.all_load;
953 object.alive = lib.must_link; // TODO: or self.options.all_load;
922954 object.hidden = lib.hidden;
923955 try self.objects.append(gpa, index);
924956 }
......@@ -993,6 +1025,7 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
9931025 return false;
9941026}
9951027
1028/// TODO delete this, libraries must be instead resolved when instantiating the compilation pipeline
9961029fn accessLibPath(
9971030 arena: Allocator,
9981031 test_path: *std.ArrayList(u8),
......@@ -1051,9 +1084,11 @@ fn parseDependentDylibs(self: *MachO) !void {
10511084 if (self.dylibs.items.len == 0) return;
10521085
10531086 const gpa = self.base.comp.gpa;
1054 const lib_dirs = self.lib_dirs;
10551087 const framework_dirs = self.framework_dirs;
10561088
1089 // TODO delete this, directories must instead be resolved by the frontend
1090 const lib_directories = self.lib_directories;
1091
10571092 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
10581093 defer arena_alloc.deinit();
10591094 const arena = arena_alloc.allocator();
......@@ -1094,9 +1129,9 @@ fn parseDependentDylibs(self: *MachO) !void {
10941129
10951130 // Library
10961131 const lib_name = eatPrefix(stem, "lib") orelse stem;
1097 for (lib_dirs) |dir| {
1132 for (lib_directories) |lib_directory| {
10981133 test_path.clearRetainingCapacity();
1099 if (try accessLibPath(arena, &test_path, &checked_paths, dir, lib_name)) break :full_path test_path.items;
1134 if (try accessLibPath(arena, &test_path, &checked_paths, lib_directory.path orelse ".", lib_name)) break :full_path test_path.items;
11001135 }
11011136 }
11021137
......@@ -4366,6 +4401,24 @@ const SystemLib = struct {
43664401 hidden: bool = false,
43674402 reexport: bool = false,
43684403 must_link: bool = false,
4404
4405 fn fromLinkInput(link_input: link.Input) SystemLib {
4406 return switch (link_input) {
4407 .dso_exact => unreachable,
4408 .res => unreachable,
4409 .object, .archive => |obj| .{
4410 .path = obj.path,
4411 .must_link = obj.must_link,
4412 .hidden = obj.hidden,
4413 },
4414 .dso => |dso| .{
4415 .path = dso.path,
4416 .needed = dso.needed,
4417 .weak = dso.weak,
4418 .reexport = dso.reexport,
4419 },
4420 };
4421 }
43694422};
43704423
43714424pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout;
......@@ -5303,17 +5356,16 @@ const Air = @import("../Air.zig");
53035356const Alignment = Atom.Alignment;
53045357const Allocator = mem.Allocator;
53055358const Archive = @import("MachO/Archive.zig");
5306pub const Atom = @import("MachO/Atom.zig");
53075359const AtomicBool = std.atomic.Value(bool);
53085360const Bind = bind.Bind;
53095361const Cache = std.Build.Cache;
5310const Path = Cache.Path;
53115362const CodeSignature = @import("MachO/CodeSignature.zig");
53125363const Compilation = @import("../Compilation.zig");
53135364const DataInCode = synthetic.DataInCode;
5314pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
5365const Directory = Cache.Directory;
53155366const Dylib = @import("MachO/Dylib.zig");
53165367const ExportTrie = @import("MachO/dyld_info/Trie.zig");
5368const Path = Cache.Path;
53175369const File = @import("MachO/file.zig").File;
53185370const GotSection = synthetic.GotSection;
53195371const Hash = std.hash.Wyhash;
......@@ -5329,7 +5381,6 @@ const Md5 = std.crypto.hash.Md5;
53295381const Zcu = @import("../Zcu.zig");
53305382const InternPool = @import("../InternPool.zig");
53315383const Rebase = @import("MachO/dyld_info/Rebase.zig");
5332pub const Relocation = @import("MachO/Relocation.zig");
53335384const StringTable = @import("StringTable.zig");
53345385const StubsSection = synthetic.StubsSection;
53355386const 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+33-34
......@@ -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;
......@@ -3643,11 +3645,8 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
36433645 try argv.append(p);
36443646 }
36453647
3646 if (comp.config.output_mode != .Obj and
3647 !comp.skip_linker_dependencies and
3648 !comp.config.link_libc)
3649 {
3650 try argv.append(try comp.libc_static_lib.?.full_object_path.toString(arena));
3648 if (comp.libc_static_lib) |crt_file| {
3649 try argv.append(try crt_file.full_object_path.toString(arena));
36513650 }
36523651
36533652 if (compiler_rt_path) |p| {
src/main.zig+322-469
......@@ -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 = .{
......@@ -555,6 +556,8 @@ const usage_build_generic =
555556 \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library
556557 \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries
557558 \\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries
559 \\ -fallow-so-scripts Allows .so files to be GNU ld scripts
560 \\ -fno-allow-so-scripts (default) .so files must be ELF files
558561 \\ --build-id[=style] At a minor link-time expense, coordinates stripped binaries
559562 \\ fast, uuid, sha1, md5 with debug symbols via a '.note.gnu.build-id' section
560563 \\ 0x[hexstring] Maximum 32 bytes
......@@ -766,27 +769,6 @@ const ArgsIterator = struct {
766769 }
767770};
768771
769/// In contrast to `link.SystemLib`, this stores arguments that may need to be
770/// resolved into static libraries so that we can pass only dynamic libraries
771/// as system libs to `Compilation`.
772const SystemLib = struct {
773 needed: bool,
774 weak: bool,
775
776 preferred_mode: std.builtin.LinkMode,
777 search_strategy: SearchStrategy,
778
779 const SearchStrategy = enum { paths_first, mode_first, no_fallback };
780
781 fn fallbackMode(this: SystemLib) std.builtin.LinkMode {
782 assert(this.search_strategy != .no_fallback);
783 return switch (this.preferred_mode) {
784 .dynamic => .static,
785 .static => .dynamic,
786 };
787 }
788};
789
790772/// Similar to `link.Framework` except it doesn't store yet unresolved
791773/// path to the framework.
792774const Framework = struct {
......@@ -867,6 +849,7 @@ fn buildOutputType(
867849 var linker_gc_sections: ?bool = null;
868850 var linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null;
869851 var linker_allow_shlib_undefined: ?bool = null;
852 var allow_so_scripts: bool = false;
870853 var linker_bind_global_refs_locally: ?bool = null;
871854 var linker_import_symbols: bool = false;
872855 var linker_import_table: bool = false;
......@@ -919,7 +902,7 @@ fn buildOutputType(
919902 var hash_style: link.File.Elf.HashStyle = .both;
920903 var entitlements: ?[]const u8 = null;
921904 var pagezero_size: ?u64 = null;
922 var lib_search_strategy: SystemLib.SearchStrategy = .paths_first;
905 var lib_search_strategy: link.UnresolvedInput.SearchStrategy = .paths_first;
923906 var lib_preferred_mode: std.builtin.LinkMode = .dynamic;
924907 var headerpad_size: ?u32 = null;
925908 var headerpad_max_install_names: bool = false;
......@@ -983,8 +966,10 @@ fn buildOutputType(
983966 // Populated in the call to `createModule` for the root module.
984967 .resolved_options = undefined,
985968
986 .system_libs = .{},
987 .resolved_system_libs = .{},
969 .cli_link_inputs = .empty,
970 .windows_libs = .empty,
971 .link_inputs = .empty,
972
988973 .wasi_emulated_libs = .{},
989974
990975 .c_source_files = .{},
......@@ -992,7 +977,7 @@ fn buildOutputType(
992977
993978 .llvm_m_args = .{},
994979 .sysroot = null,
995 .lib_dirs = .{}, // populated by createModule()
980 .lib_directories = .{}, // populated by createModule()
996981 .lib_dir_args = .{}, // populated from CLI arg parsing
997982 .libc_installation = null,
998983 .want_native_include_dirs = false,
......@@ -1001,7 +986,6 @@ fn buildOutputType(
1001986 .rpath_list = .{},
1002987 .each_lib_rpath = null,
1003988 .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena),
1004 .link_objects = .{},
1005989 .native_system_include_paths = &.{},
1006990 };
1007991
......@@ -1237,30 +1221,42 @@ fn buildOutputType(
12371221 // We don't know whether this library is part of libc
12381222 // or libc++ until we resolve the target, so we append
12391223 // to the list for now.
1240 try create_module.system_libs.put(arena, args_iter.nextOrFatal(), .{
1241 .needed = false,
1242 .weak = false,
1243 .preferred_mode = lib_preferred_mode,
1244 .search_strategy = lib_search_strategy,
1245 });
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 } });
12461234 } else if (mem.eql(u8, arg, "--needed-library") or
12471235 mem.eql(u8, arg, "-needed-l") or
12481236 mem.eql(u8, arg, "-needed_library"))
12491237 {
12501238 const next_arg = args_iter.nextOrFatal();
1251 try create_module.system_libs.put(arena, next_arg, .{
1252 .needed = true,
1253 .weak = false,
1254 .preferred_mode = lib_preferred_mode,
1255 .search_strategy = lib_search_strategy,
1256 });
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 } });
12571249 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
1258 try create_module.system_libs.put(arena, args_iter.nextOrFatal(), .{
1259 .needed = false,
1260 .weak = true,
1261 .preferred_mode = lib_preferred_mode,
1262 .search_strategy = lib_search_strategy,
1263 });
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 } });
12641260 } else if (mem.eql(u8, arg, "-D")) {
12651261 try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });
12661262 } else if (mem.eql(u8, arg, "-I")) {
......@@ -1573,6 +1569,10 @@ fn buildOutputType(
15731569 linker_allow_shlib_undefined = true;
15741570 } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) {
15751571 linker_allow_shlib_undefined = false;
1572 } else if (mem.eql(u8, arg, "-fallow-so-scripts")) {
1573 allow_so_scripts = true;
1574 } else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {
1575 allow_so_scripts = false;
15761576 } else if (mem.eql(u8, arg, "-z")) {
15771577 const z_arg = args_iter.nextOrFatal();
15781578 if (mem.eql(u8, z_arg, "nodelete")) {
......@@ -1680,26 +1680,38 @@ fn buildOutputType(
16801680 // We don't know whether this library is part of libc
16811681 // or libc++ until we resolve the target, so we append
16821682 // to the list for now.
1683 try create_module.system_libs.put(arena, arg["-l".len..], .{
1684 .needed = false,
1685 .weak = false,
1686 .preferred_mode = lib_preferred_mode,
1687 .search_strategy = lib_search_strategy,
1688 });
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 } });
16891693 } else if (mem.startsWith(u8, arg, "-needed-l")) {
1690 try create_module.system_libs.put(arena, arg["-needed-l".len..], .{
1691 .needed = true,
1692 .weak = false,
1693 .preferred_mode = lib_preferred_mode,
1694 .search_strategy = lib_search_strategy,
1695 });
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 } });
16961704 } else if (mem.startsWith(u8, arg, "-weak-l")) {
1697 try create_module.system_libs.put(arena, arg["-weak-l".len..], .{
1698 .needed = false,
1699 .weak = true,
1700 .preferred_mode = lib_preferred_mode,
1701 .search_strategy = lib_search_strategy,
1702 });
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 } });
17031715 } else if (mem.startsWith(u8, arg, "-D")) {
17041716 try cc_argv.append(arena, arg);
17051717 } else if (mem.startsWith(u8, arg, "-I")) {
......@@ -1724,15 +1736,28 @@ fn buildOutputType(
17241736 fatal("unrecognized parameter: '{s}'", .{arg});
17251737 }
17261738 } else switch (file_ext orelse Compilation.classifyFileExt(arg)) {
1727 .shared_library => {
1728 try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) });
1729 create_module.opts.any_dyn_libs = true;
1730 },
1731 .object, .static_library => {
1732 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.
17331751 },
17341752 .res => {
1735 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 } });
17361761 contains_res_file = true;
17371762 },
17381763 .manifest => {
......@@ -1785,6 +1810,7 @@ fn buildOutputType(
17851810 // some functionality that depend on it, such as C++ exceptions and
17861811 // DWARF-based stack traces.
17871812 link_eh_frame_hdr = true;
1813 allow_so_scripts = true;
17881814
17891815 const COutMode = enum {
17901816 link,
......@@ -1844,24 +1870,32 @@ fn buildOutputType(
18441870 .ext = file_ext, // duped while parsing the args.
18451871 });
18461872 },
1847 .shared_library => {
1848 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 = .{
18491875 .path = Path.initCwd(it.only_arg),
1850 .must_link = must_link,
1851 });
1852 create_module.opts.any_dyn_libs = true;
1853 },
1854 .unknown, .object, .static_library => {
1855 try create_module.link_objects.append(arena, .{
1856 .path = Path.initCwd(it.only_arg),
1857 .must_link = must_link,
1858 });
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.
18591887 },
18601888 .res => {
1861 try create_module.link_objects.append(arena, .{
1889 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
18621890 .path = Path.initCwd(it.only_arg),
1863 .must_link = must_link,
1864 });
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 } });
18651899 contains_res_file = true;
18661900 },
18671901 .manifest => {
......@@ -1893,19 +1927,21 @@ fn buildOutputType(
18931927 // -l :path/to/filename is used when callers need
18941928 // more control over what's in the resulting
18951929 // binary: no extra rpaths and DSO filename exactly
1896 // as provided. Hello, Go.
1897 try create_module.link_objects.append(arena, .{
1898 .path = Path.initCwd(it.only_arg),
1899 .must_link = must_link,
1900 .loption = true,
1901 });
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 } });
19021934 } else {
1903 try create_module.system_libs.put(arena, it.only_arg, .{
1904 .needed = needed,
1905 .weak = false,
1906 .preferred_mode = lib_preferred_mode,
1907 .search_strategy = lib_search_strategy,
1908 });
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 } });
19091945 }
19101946 },
19111947 .ignore => {},
......@@ -2174,12 +2210,16 @@ fn buildOutputType(
21742210 },
21752211 .force_load_objc => force_load_objc = true,
21762212 .mingw_unicode_entry_point => mingw_unicode_entry_point = true,
2177 .weak_library => try create_module.system_libs.put(arena, it.only_arg, .{
2178 .needed = false,
2179 .weak = true,
2180 .preferred_mode = lib_preferred_mode,
2181 .search_strategy = lib_search_strategy,
2182 }),
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 } }),
21832223 .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),
21842224 .headerpad_max_install_names => headerpad_max_install_names = true,
21852225 .compress_debug_sections => {
......@@ -2482,26 +2522,38 @@ fn buildOutputType(
24822522 } else if (mem.eql(u8, arg, "-needed_framework")) {
24832523 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true });
24842524 } else if (mem.eql(u8, arg, "-needed_library")) {
2485 try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{
2486 .weak = false,
2487 .needed = true,
2488 .preferred_mode = lib_preferred_mode,
2489 .search_strategy = lib_search_strategy,
2490 });
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 } });
24912535 } else if (mem.startsWith(u8, arg, "-weak-l")) {
2492 try create_module.system_libs.put(arena, arg["-weak-l".len..], .{
2493 .weak = true,
2494 .needed = false,
2495 .preferred_mode = lib_preferred_mode,
2496 .search_strategy = lib_search_strategy,
2497 });
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 } });
24982546 } else if (mem.eql(u8, arg, "-weak_library")) {
2499 try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{
2500 .weak = true,
2501 .needed = false,
2502 .preferred_mode = lib_preferred_mode,
2503 .search_strategy = lib_search_strategy,
2504 });
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 } });
25052557 } else if (mem.eql(u8, arg, "-compatibility_version")) {
25062558 const compat_version = linker_args_it.nextOrFatal();
25072559 compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {
......@@ -2532,10 +2584,14 @@ fn buildOutputType(
25322584 } else if (mem.eql(u8, arg, "-install_name")) {
25332585 install_name = linker_args_it.nextOrFatal();
25342586 } else if (mem.eql(u8, arg, "-force_load")) {
2535 try create_module.link_objects.append(arena, .{
2587 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
25362588 .path = Path.initCwd(linker_args_it.nextOrFatal()),
2537 .must_link = true,
2538 });
2589 .query = .{
2590 .must_link = true,
2591 .preferred_mode = .static,
2592 .search_strategy = .no_fallback,
2593 },
2594 } });
25392595 } else if (mem.eql(u8, arg, "-hash-style") or
25402596 mem.eql(u8, arg, "--hash-style"))
25412597 {
......@@ -2665,7 +2721,7 @@ fn buildOutputType(
26652721 },
26662722 }
26672723 if (create_module.c_source_files.items.len == 0 and
2668 create_module.link_objects.items.len == 0 and
2724 !anyObjectLinkInputs(create_module.cli_link_inputs.items) and
26692725 root_src_file == null)
26702726 {
26712727 // For example `zig cc` and no args should print the "no input files" message.
......@@ -2707,8 +2763,11 @@ fn buildOutputType(
27072763 if (create_module.c_source_files.items.len >= 1)
27082764 break :b create_module.c_source_files.items[0].src_path;
27092765
2710 if (create_module.link_objects.items.len >= 1)
2711 break :b create_module.link_objects.items[0].path.sub_path;
2766 for (create_module.cli_link_inputs.items) |unresolved_link_input| switch (unresolved_link_input) {
2767 // Intentionally includes dynamic libraries provided by file path.
2768 .path_query => |pq| break :b pq.path.sub_path,
2769 else => continue,
2770 };
27122771
27132772 if (emit_bin == .yes)
27142773 break :b emit_bin.yes;
......@@ -2794,7 +2853,7 @@ fn buildOutputType(
27942853 fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
27952854 };
27962855
2797 var zig_lib_directory: Compilation.Directory = d: {
2856 var zig_lib_directory: Directory = d: {
27982857 if (override_lib_dir) |unresolved_lib_dir| {
27992858 const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
28002859 break :d .{
......@@ -2815,7 +2874,7 @@ fn buildOutputType(
28152874 };
28162875 defer zig_lib_directory.handle.close();
28172876
2818 var global_cache_directory: Compilation.Directory = l: {
2877 var global_cache_directory: Directory = l: {
28192878 if (override_global_cache_dir) |p| {
28202879 break :l .{
28212880 .handle = try fs.cwd().makeOpenPath(p, .{}),
......@@ -2845,7 +2904,7 @@ fn buildOutputType(
28452904
28462905 var builtin_modules: std.StringHashMapUnmanaged(*Package.Module) = .empty;
28472906 // `builtin_modules` allocated into `arena`, so no deinit
2848 const main_mod = try createModule(gpa, arena, &create_module, 0, null, zig_lib_directory, &builtin_modules);
2907 const main_mod = try createModule(gpa, arena, &create_module, 0, null, zig_lib_directory, &builtin_modules, color);
28492908 for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {
28502909 if (cli_mod.resolved == null)
28512910 fatal("module '{s}' declared but not used", .{key});
......@@ -2939,7 +2998,6 @@ fn buildOutputType(
29392998 }
29402999 }
29413000
2942 // We now repeat part of the process for frameworks.
29433001 var resolved_frameworks = std.ArrayList(Compilation.Framework).init(arena);
29443002
29453003 if (create_module.frameworks.keys().len > 0) {
......@@ -2996,7 +3054,7 @@ fn buildOutputType(
29963054 const total_obj_count = create_module.c_source_files.items.len +
29973055 @intFromBool(root_src_file != null) +
29983056 create_module.rc_source_files.items.len +
2999 create_module.link_objects.items.len;
3057 link.countObjectInputs(create_module.link_inputs.items);
30003058 if (total_obj_count > 1) {
30013059 fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)});
30023060 }
......@@ -3212,7 +3270,7 @@ fn buildOutputType(
32123270 var cleanup_local_cache_dir: ?fs.Dir = null;
32133271 defer if (cleanup_local_cache_dir) |*dir| dir.close();
32143272
3215 var local_cache_directory: Compilation.Directory = l: {
3273 var local_cache_directory: Directory = l: {
32163274 if (override_local_cache_dir) |local_cache_dir_path| {
32173275 const dir = try fs.cwd().makeOpenPath(local_cache_dir_path, .{});
32183276 cleanup_local_cache_dir = dir;
......@@ -3349,7 +3407,7 @@ fn buildOutputType(
33493407 .emit_llvm_bc = emit_llvm_bc_resolved.data,
33503408 .emit_docs = emit_docs_resolved.data,
33513409 .emit_implib = emit_implib_resolved.data,
3352 .lib_dirs = create_module.lib_dirs.items,
3410 .lib_directories = create_module.lib_directories.items,
33533411 .rpath_list = create_module.rpath_list.items,
33543412 .symbol_wrap_set = symbol_wrap_set,
33553413 .c_source_files = create_module.c_source_files.items,
......@@ -3357,11 +3415,10 @@ fn buildOutputType(
33573415 .manifest_file = manifest_file,
33583416 .rc_includes = rc_includes,
33593417 .mingw_unicode_entry_point = mingw_unicode_entry_point,
3360 .link_objects = create_module.link_objects.items,
3418 .link_inputs = create_module.link_inputs.items,
33613419 .framework_dirs = create_module.framework_dirs.items,
33623420 .frameworks = resolved_frameworks.items,
3363 .system_lib_names = create_module.resolved_system_libs.items(.name),
3364 .system_lib_infos = create_module.resolved_system_libs.items(.lib),
3421 .windows_lib_names = create_module.windows_libs.keys(),
33653422 .wasi_emulated_libs = create_module.wasi_emulated_libs.items,
33663423 .want_compiler_rt = want_compiler_rt,
33673424 .hash_style = hash_style,
......@@ -3630,12 +3687,14 @@ const CreateModule = struct {
36303687 /// This one is used while collecting CLI options. The set of libs is used
36313688 /// directly after computing the target and used to compute link_libc,
36323689 /// link_libcpp, and then the libraries are filtered into
3633 /// `external_system_libs` and `resolved_system_libs`.
3634 system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
3635 resolved_system_libs: std.MultiArrayList(struct {
3636 name: []const u8,
3637 lib: Compilation.SystemLib,
3638 }),
3690 /// `unresolved_linker_inputs` and `windows_libs`.
3691 cli_link_inputs: std.ArrayListUnmanaged(link.UnresolvedInput),
3692 windows_libs: std.StringArrayHashMapUnmanaged(void),
3693 /// The local variable `unresolved_link_inputs` is fed into library
3694 /// resolution, mutating the input array, and producing this data as
3695 /// output. Allocated with gpa.
3696 link_inputs: std.ArrayListUnmanaged(link.Input),
3697
36393698 wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile),
36403699
36413700 c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile),
......@@ -3646,7 +3705,7 @@ const CreateModule = struct {
36463705 /// CPU features.
36473706 llvm_m_args: std.ArrayListUnmanaged([]const u8),
36483707 sysroot: ?[]const u8,
3649 lib_dirs: std.ArrayListUnmanaged([]const u8),
3708 lib_directories: std.ArrayListUnmanaged(Directory),
36503709 lib_dir_args: std.ArrayListUnmanaged([]const u8),
36513710 libc_installation: ?LibCInstallation,
36523711 want_native_include_dirs: bool,
......@@ -3656,7 +3715,6 @@ const CreateModule = struct {
36563715 rpath_list: std.ArrayListUnmanaged([]const u8),
36573716 each_lib_rpath: ?bool,
36583717 libc_paths_file: ?[]const u8,
3659 link_objects: std.ArrayListUnmanaged(Compilation.LinkObject),
36603718};
36613719
36623720fn createModule(
......@@ -3667,6 +3725,7 @@ fn createModule(
36673725 parent: ?*Package.Module,
36683726 zig_lib_directory: Cache.Directory,
36693727 builtin_modules: *std.StringHashMapUnmanaged(*Package.Module),
3728 color: std.zig.Color,
36703729) Allocator.Error!*Package.Module {
36713730 const cli_mod = &create_module.modules.values()[index];
36723731 if (cli_mod.resolved) |m| return m;
......@@ -3760,85 +3819,83 @@ fn createModule(
37603819 // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.
37613820 // We need to know whether the set of system libraries contains anything besides these
37623821 // to decide whether to trigger native path detection logic.
3763 var external_system_libs: std.MultiArrayList(struct {
3764 name: []const u8,
3765 info: SystemLib,
3766 }) = .{};
3767 for (create_module.system_libs.keys(), create_module.system_libs.values()) |lib_name, info| {
3768 if (std.zig.target.isLibCLibName(target, lib_name)) {
3769 create_module.opts.link_libc = true;
3770 continue;
3771 }
3772 if (std.zig.target.isLibCxxLibName(target, lib_name)) {
3773 create_module.opts.link_libcpp = true;
3774 continue;
3775 }
3776 switch (target_util.classifyCompilerRtLibName(target, lib_name)) {
3777 .none => {},
3778 .only_libunwind, .both => {
3779 create_module.opts.link_libunwind = true;
3822 // Preserves linker input order.
3823 var unresolved_link_inputs: std.ArrayListUnmanaged(link.UnresolvedInput) = .empty;
3824 try unresolved_link_inputs.ensureUnusedCapacity(arena, create_module.cli_link_inputs.items.len);
3825 var any_name_queries_remaining = false;
3826 for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) {
3827 .name_query => |nq| {
3828 const lib_name = nq.name;
3829 if (std.zig.target.isLibCLibName(target, lib_name)) {
3830 create_module.opts.link_libc = true;
37803831 continue;
3781 },
3782 .only_compiler_rt => {
3783 warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});
3832 }
3833 if (std.zig.target.isLibCxxLibName(target, lib_name)) {
3834 create_module.opts.link_libcpp = true;
37843835 continue;
3785 },
3786 }
3836 }
3837 switch (target_util.classifyCompilerRtLibName(target, lib_name)) {
3838 .none => {},
3839 .only_libunwind, .both => {
3840 create_module.opts.link_libunwind = true;
3841 continue;
3842 },
3843 .only_compiler_rt => {
3844 warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});
3845 continue;
3846 },
3847 }
37873848
3788 if (target.isMinGW()) {
3789 const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| {
3790 fatal("failed to check zig installation for DLL import libs: {s}", .{
3791 @errorName(err),
3792 });
3793 };
3794 if (exists) {
3795 try create_module.resolved_system_libs.append(arena, .{
3796 .name = lib_name,
3797 .lib = .{
3798 .needed = true,
3799 .weak = false,
3800 .path = null,
3801 },
3802 });
3803 continue;
3849 if (target.isMinGW()) {
3850 const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| {
3851 fatal("failed to check zig installation for DLL import libs: {s}", .{
3852 @errorName(err),
3853 });
3854 };
3855 if (exists) {
3856 try create_module.windows_libs.put(arena, lib_name, {});
3857 continue;
3858 }
38043859 }
3805 }
38063860
3807 if (fs.path.isAbsolute(lib_name)) {
3808 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
3809 }
3861 if (fs.path.isAbsolute(lib_name)) {
3862 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
3863 }
38103864
3811 if (target.os.tag == .wasi) {
3812 if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| {
3813 try create_module.wasi_emulated_libs.append(arena, crt_file);
3814 continue;
3865 if (target.os.tag == .wasi) {
3866 if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| {
3867 try create_module.wasi_emulated_libs.append(arena, crt_file);
3868 continue;
3869 }
38153870 }
3816 }
3871 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
3872 any_name_queries_remaining = true;
3873 },
3874 else => {
3875 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
3876 },
3877 }; // After this point, unresolved_link_inputs is used instead of cli_link_inputs.
38173878
3818 try external_system_libs.append(arena, .{
3819 .name = lib_name,
3820 .info = info,
3821 });
3822 }
3823 // After this point, external_system_libs is used instead of system_libs.
3824 if (external_system_libs.len != 0)
3825 create_module.want_native_include_dirs = true;
3879 if (any_name_queries_remaining) create_module.want_native_include_dirs = true;
38263880
38273881 // Resolve the library path arguments with respect to sysroot.
3882 try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len);
38283883 if (create_module.sysroot) |root| {
3829 try create_module.lib_dirs.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len * 2);
3830 for (create_module.lib_dir_args.items) |dir| {
3831 if (fs.path.isAbsolute(dir)) {
3832 const stripped_dir = dir[fs.path.diskDesignator(dir).len..];
3884 for (create_module.lib_dir_args.items) |lib_dir_arg| {
3885 if (fs.path.isAbsolute(lib_dir_arg)) {
3886 const stripped_dir = lib_dir_arg[fs.path.diskDesignator(lib_dir_arg).len..];
38333887 const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });
3834 create_module.lib_dirs.appendAssumeCapacity(full_path);
3888 addLibDirectoryWarn(&create_module.lib_directories, full_path);
3889 } else {
3890 addLibDirectoryWarn(&create_module.lib_directories, lib_dir_arg);
38353891 }
3836 create_module.lib_dirs.appendAssumeCapacity(dir);
38373892 }
38383893 } else {
3839 create_module.lib_dirs = create_module.lib_dir_args;
3894 for (create_module.lib_dir_args.items) |lib_dir_arg| {
3895 addLibDirectoryWarn(&create_module.lib_directories, lib_dir_arg);
3896 }
38403897 }
3841 create_module.lib_dir_args = undefined; // From here we use lib_dirs instead.
3898 create_module.lib_dir_args = undefined; // From here we use lib_directories instead.
38423899
38433900 if (resolved_target.is_native_os and target.isDarwin()) {
38443901 // If we want to link against frameworks, we need system headers.
......@@ -3847,7 +3904,10 @@ fn createModule(
38473904 }
38483905
38493906 if (create_module.each_lib_rpath orelse resolved_target.is_native_os) {
3850 try create_module.rpath_list.appendSlice(arena, create_module.lib_dirs.items);
3907 try create_module.rpath_list.ensureUnusedCapacity(arena, create_module.lib_directories.items.len);
3908 for (create_module.lib_directories.items) |lib_directory| {
3909 create_module.rpath_list.appendAssumeCapacity(lib_directory.path.?);
3910 }
38513911 }
38523912
38533913 // Trigger native system library path detection if necessary.
......@@ -3865,8 +3925,10 @@ fn createModule(
38653925 create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena);
38663926
38673927 try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items);
3868 try create_module.lib_dirs.appendSlice(arena, paths.lib_dirs.items);
38693928 try create_module.rpath_list.appendSlice(arena, paths.rpaths.items);
3929
3930 try create_module.lib_directories.ensureUnusedCapacity(arena, paths.lib_dirs.items.len);
3931 for (paths.lib_dirs.items) |path| addLibDirectoryWarn(&create_module.lib_directories, path);
38703932 }
38713933
38723934 if (create_module.libc_paths_file) |paths_file| {
......@@ -3878,7 +3940,7 @@ fn createModule(
38783940 }
38793941
38803942 if (builtin.target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and
3881 external_system_libs.len != 0)
3943 any_name_queries_remaining)
38823944 {
38833945 if (create_module.libc_installation == null) {
38843946 create_module.libc_installation = LibCInstallation.findNative(.{
......@@ -3889,181 +3951,31 @@ fn createModule(
38893951 fatal("unable to find native libc installation: {s}", .{@errorName(err)});
38903952 };
38913953
3892 try create_module.lib_dirs.appendSlice(arena, &.{
3893 create_module.libc_installation.?.msvc_lib_dir.?,
3894 create_module.libc_installation.?.kernel32_lib_dir.?,
3895 });
3896 }
3897 }
3898
3899 // If any libs in this list are statically provided, we omit them from the
3900 // resolved list and populate the link_objects array instead.
3901 {
3902 var test_path = std.ArrayList(u8).init(gpa);
3903 defer test_path.deinit();
3904
3905 var checked_paths = std.ArrayList(u8).init(gpa);
3906 defer checked_paths.deinit();
3907
3908 var failed_libs = std.ArrayList(struct {
3909 name: []const u8,
3910 strategy: SystemLib.SearchStrategy,
3911 checked_paths: []const u8,
3912 preferred_mode: std.builtin.LinkMode,
3913 }).init(arena);
3914
3915 syslib: for (external_system_libs.items(.name), external_system_libs.items(.info)) |lib_name, info| {
3916 // Checked in the first pass above while looking for libc libraries.
3917 assert(!fs.path.isAbsolute(lib_name));
3918
3919 checked_paths.clearRetainingCapacity();
3920
3921 switch (info.search_strategy) {
3922 .mode_first, .no_fallback => {
3923 // check for preferred mode
3924 for (create_module.lib_dirs.items) |lib_dir_path| {
3925 if (try accessLibPath(
3926 &test_path,
3927 &checked_paths,
3928 lib_dir_path,
3929 lib_name,
3930 target,
3931 info.preferred_mode,
3932 )) {
3933 const path = Path.initCwd(try arena.dupe(u8, test_path.items));
3934 switch (info.preferred_mode) {
3935 .static => try create_module.link_objects.append(arena, .{ .path = path }),
3936 .dynamic => try create_module.resolved_system_libs.append(arena, .{
3937 .name = lib_name,
3938 .lib = .{
3939 .needed = info.needed,
3940 .weak = info.weak,
3941 .path = path,
3942 },
3943 }),
3944 }
3945 continue :syslib;
3946 }
3947 }
3948 // check for fallback mode
3949 if (info.search_strategy == .no_fallback) {
3950 try failed_libs.append(.{
3951 .name = lib_name,
3952 .strategy = info.search_strategy,
3953 .checked_paths = try arena.dupe(u8, checked_paths.items),
3954 .preferred_mode = info.preferred_mode,
3955 });
3956 continue :syslib;
3957 }
3958 for (create_module.lib_dirs.items) |lib_dir_path| {
3959 if (try accessLibPath(
3960 &test_path,
3961 &checked_paths,
3962 lib_dir_path,
3963 lib_name,
3964 target,
3965 info.fallbackMode(),
3966 )) {
3967 const path = Path.initCwd(try arena.dupe(u8, test_path.items));
3968 switch (info.fallbackMode()) {
3969 .static => try create_module.link_objects.append(arena, .{ .path = path }),
3970 .dynamic => try create_module.resolved_system_libs.append(arena, .{
3971 .name = lib_name,
3972 .lib = .{
3973 .needed = info.needed,
3974 .weak = info.weak,
3975 .path = path,
3976 },
3977 }),
3978 }
3979 continue :syslib;
3980 }
3981 }
3982 try failed_libs.append(.{
3983 .name = lib_name,
3984 .strategy = info.search_strategy,
3985 .checked_paths = try arena.dupe(u8, checked_paths.items),
3986 .preferred_mode = info.preferred_mode,
3987 });
3988 continue :syslib;
3989 },
3990 .paths_first => {
3991 for (create_module.lib_dirs.items) |lib_dir_path| {
3992 // check for preferred mode
3993 if (try accessLibPath(
3994 &test_path,
3995 &checked_paths,
3996 lib_dir_path,
3997 lib_name,
3998 target,
3999 info.preferred_mode,
4000 )) {
4001 const path = Path.initCwd(try arena.dupe(u8, test_path.items));
4002 switch (info.preferred_mode) {
4003 .static => try create_module.link_objects.append(arena, .{ .path = path }),
4004 .dynamic => try create_module.resolved_system_libs.append(arena, .{
4005 .name = lib_name,
4006 .lib = .{
4007 .needed = info.needed,
4008 .weak = info.weak,
4009 .path = path,
4010 },
4011 }),
4012 }
4013 continue :syslib;
4014 }
4015
4016 // check for fallback mode
4017 if (try accessLibPath(
4018 &test_path,
4019 &checked_paths,
4020 lib_dir_path,
4021 lib_name,
4022 target,
4023 info.fallbackMode(),
4024 )) {
4025 const path = Path.initCwd(try arena.dupe(u8, test_path.items));
4026 switch (info.fallbackMode()) {
4027 .static => try create_module.link_objects.append(arena, .{ .path = path }),
4028 .dynamic => try create_module.resolved_system_libs.append(arena, .{
4029 .name = lib_name,
4030 .lib = .{
4031 .needed = info.needed,
4032 .weak = info.weak,
4033 .path = path,
4034 },
4035 }),
4036 }
4037 continue :syslib;
4038 }
4039 }
4040 try failed_libs.append(.{
4041 .name = lib_name,
4042 .strategy = info.search_strategy,
4043 .checked_paths = try arena.dupe(u8, checked_paths.items),
4044 .preferred_mode = info.preferred_mode,
4045 });
4046 continue :syslib;
4047 },
4048 }
4049 @compileError("unreachable");
4050 }
4051
4052 if (failed_libs.items.len > 0) {
4053 for (failed_libs.items) |f| {
4054 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
4055 std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{
4056 @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths,
4057 });
4058 }
4059 process.exit(1);
3954 try create_module.lib_directories.ensureUnusedCapacity(arena, 2);
3955 addLibDirectoryWarn(&create_module.lib_directories, create_module.libc_installation.?.msvc_lib_dir.?);
3956 addLibDirectoryWarn(&create_module.lib_directories, create_module.libc_installation.?.kernel32_lib_dir.?);
40603957 }
40613958 }
4062 // After this point, create_module.resolved_system_libs is used instead of
4063 // create_module.external_system_libs.
40643959
4065 if (create_module.resolved_system_libs.len != 0)
4066 create_module.opts.any_dyn_libs = true;
3960 // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`.
3961 link.resolveInputs(
3962 gpa,
3963 arena,
3964 target,
3965 &unresolved_link_inputs,
3966 &create_module.link_inputs,
3967 create_module.lib_directories.items,
3968 color,
3969 ) catch |err| fatal("failed to resolve link inputs: {s}", .{@errorName(err)});
3970
3971 if (create_module.windows_libs.count() != 0) create_module.opts.any_dyn_libs = true;
3972 if (!create_module.opts.any_dyn_libs) for (create_module.link_inputs.items) |item| switch (item) {
3973 .dso, .dso_exact => {
3974 create_module.opts.any_dyn_libs = true;
3975 break;
3976 },
3977 else => {},
3978 };
40673979
40683980 create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) {
40693981 error.WasiExecModelRequiresWasi => fatal("only WASI OS targets support execution model", .{}),
......@@ -4131,7 +4043,7 @@ fn createModule(
41314043 for (cli_mod.deps) |dep| {
41324044 const dep_index = create_module.modules.getIndex(dep.value) orelse
41334045 fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key });
4134 const dep_mod = try createModule(gpa, arena, create_module, dep_index, mod, zig_lib_directory, builtin_modules);
4046 const dep_mod = try createModule(gpa, arena, create_module, dep_index, mod, zig_lib_directory, builtin_modules, color);
41354047 try mod.deps.put(arena, dep.key, dep_mod);
41364048 }
41374049
......@@ -4996,7 +4908,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
49964908
49974909 process.raiseFileDescriptorLimit();
49984910
4999 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
4911 var zig_lib_directory: Directory = if (override_lib_dir) |lib_dir| .{
50004912 .path = lib_dir,
50014913 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
50024914 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });
......@@ -5015,7 +4927,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
50154927 });
50164928 child_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path;
50174929
5018 var global_cache_directory: Compilation.Directory = l: {
4930 var global_cache_directory: Directory = l: {
50194931 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
50204932 break :l .{
50214933 .handle = try fs.cwd().makeOpenPath(p, .{}),
......@@ -5026,7 +4938,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
50264938
50274939 child_argv.items[argv_index_global_cache_dir] = global_cache_directory.path orelse cwd_path;
50284940
5029 var local_cache_directory: Compilation.Directory = l: {
4941 var local_cache_directory: Directory = l: {
50304942 if (override_local_cache_dir) |local_cache_dir_path| {
50314943 break :l .{
50324944 .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}),
......@@ -5460,7 +5372,7 @@ fn jitCmd(
54605372 const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
54615373 const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
54625374
5463 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
5375 var zig_lib_directory: Directory = if (override_lib_dir) |lib_dir| .{
54645376 .path = lib_dir,
54655377 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
54665378 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });
......@@ -5470,7 +5382,7 @@ fn jitCmd(
54705382 };
54715383 defer zig_lib_directory.handle.close();
54725384
5473 var global_cache_directory: Compilation.Directory = l: {
5385 var global_cache_directory: Directory = l: {
54745386 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
54755387 break :l .{
54765388 .handle = try fs.cwd().makeOpenPath(p, .{}),
......@@ -6857,86 +6769,6 @@ const ClangSearchSanitizer = struct {
68576769 };
68586770};
68596771
6860fn accessLibPath(
6861 test_path: *std.ArrayList(u8),
6862 checked_paths: *std.ArrayList(u8),
6863 lib_dir_path: []const u8,
6864 lib_name: []const u8,
6865 target: std.Target,
6866 link_mode: std.builtin.LinkMode,
6867) !bool {
6868 const sep = fs.path.sep_str;
6869
6870 if (target.isDarwin() and link_mode == .dynamic) tbd: {
6871 // Prefer .tbd over .dylib.
6872 test_path.clearRetainingCapacity();
6873 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.tbd", .{ lib_dir_path, lib_name });
6874 try checked_paths.writer().print("\n {s}", .{test_path.items});
6875 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6876 error.FileNotFound => break :tbd,
6877 else => |e| fatal("unable to search for tbd library '{s}': {s}", .{
6878 test_path.items, @errorName(e),
6879 }),
6880 };
6881 return true;
6882 }
6883
6884 main_check: {
6885 test_path.clearRetainingCapacity();
6886 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
6887 lib_dir_path,
6888 target.libPrefix(),
6889 lib_name,
6890 switch (link_mode) {
6891 .static => target.staticLibSuffix(),
6892 .dynamic => target.dynamicLibSuffix(),
6893 },
6894 });
6895 try checked_paths.writer().print("\n {s}", .{test_path.items});
6896 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6897 error.FileNotFound => break :main_check,
6898 else => |e| fatal("unable to search for {s} library '{s}': {s}", .{
6899 @tagName(link_mode), test_path.items, @errorName(e),
6900 }),
6901 };
6902 return true;
6903 }
6904
6905 // In the case of Darwin, the main check will be .dylib, so here we
6906 // additionally check for .so files.
6907 if (target.isDarwin() and link_mode == .dynamic) so: {
6908 test_path.clearRetainingCapacity();
6909 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, lib_name });
6910 try checked_paths.writer().print("\n {s}", .{test_path.items});
6911 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6912 error.FileNotFound => break :so,
6913 else => |e| fatal("unable to search for so library '{s}': {s}", .{
6914 test_path.items, @errorName(e),
6915 }),
6916 };
6917 return true;
6918 }
6919
6920 // In the case of MinGW, the main check will be .lib but we also need to
6921 // look for `libfoo.a`.
6922 if (target.isMinGW() and link_mode == .static) mingw: {
6923 test_path.clearRetainingCapacity();
6924 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.a", .{
6925 lib_dir_path, lib_name,
6926 });
6927 try checked_paths.writer().print("\n {s}", .{test_path.items});
6928 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6929 error.FileNotFound => break :mingw,
6930 else => |e| fatal("unable to search for static library '{s}': {s}", .{
6931 test_path.items, @errorName(e),
6932 }),
6933 };
6934 return true;
6935 }
6936
6937 return false;
6938}
6939
69406772fn accessFrameworkPath(
69416773 test_path: *std.ArrayList(u8),
69426774 checked_paths: *std.ArrayList(u8),
......@@ -7057,7 +6889,7 @@ fn cmdFetch(
70576889 });
70586890 defer root_prog_node.end();
70596891
7060 var global_cache_directory: Compilation.Directory = l: {
6892 var global_cache_directory: Directory = l: {
70616893 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
70626894 break :l .{
70636895 .handle = try fs.cwd().makeOpenPath(p, .{}),
......@@ -7634,3 +7466,24 @@ fn handleModArg(
76347466 c_source_files_owner_index.* = create_module.c_source_files.items.len;
76357467 rc_source_files_owner_index.* = create_module.rc_source_files.items.len;
76367468}
7469
7470fn anyObjectLinkInputs(link_inputs: []const link.UnresolvedInput) bool {
7471 for (link_inputs) |link_input| switch (link_input) {
7472 .path_query => |pq| switch (Compilation.classifyFileExt(pq.path.sub_path)) {
7473 .object, .static_library, .res => return true,
7474 else => continue,
7475 },
7476 else => continue,
7477 };
7478 return false;
7479}
7480
7481fn addLibDirectoryWarn(lib_directories: *std.ArrayListUnmanaged(Directory), path: []const u8) void {
7482 lib_directories.appendAssumeCapacity(.{
7483 .handle = fs.cwd().openDir(path, .{}) catch |err| {
7484 warn("unable to open library directory '{s}': {s}", .{ path, @errorName(err) });
7485 return;
7486 },
7487 .path = path,
7488 });
7489}
src/musl.zig+25-13
......@@ -19,7 +19,7 @@ pub const CrtFile = enum {
1919 libc_so,
2020};
2121
22pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void {
22pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Progress.Node) !void {
2323 if (!build_options.have_llvm) {
2424 return error.ZigCompilerNotBuiltWithLLVMExtensions;
2525 }
......@@ -28,7 +28,7 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
2828 defer arena_allocator.deinit();
2929 const arena = arena_allocator.allocator();
3030
31 switch (crt_file) {
31 switch (in_crt_file) {
3232 .crti_o => {
3333 var args = std.ArrayList([]const u8).init(arena);
3434 try addCcArgs(comp, arena, &args, false);
......@@ -195,8 +195,9 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
195195 .libc_so => {
196196 const optimize_mode = comp.compilerRtOptMode();
197197 const strip = comp.compilerRtStrip();
198 const output_mode: std.builtin.OutputMode = .Lib;
198199 const config = try Compilation.Config.resolve(.{
199 .output_mode = .Lib,
200 .output_mode = output_mode,
200201 .link_mode = .dynamic,
201202 .resolved_target = comp.root_mod.resolved_target,
202203 .is_test = false,
......@@ -276,28 +277,39 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
276277
277278 try comp.updateSubCompilation(sub_compilation, .@"musl libc.so", prog_node);
278279
279 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);
280
281280 const basename = try comp.gpa.dupe(u8, "libc.so");
282281 errdefer comp.gpa.free(basename);
283282
284 comp.crt_files.putAssumeCapacityNoClobber(basename, try sub_compilation.toCrtFile());
283 const crt_file = try sub_compilation.toCrtFile();
284 comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
285 {
286 comp.mutex.lock();
287 defer comp.mutex.unlock();
288 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);
289 comp.crt_files.putAssumeCapacityNoClobber(basename, crt_file);
290 }
285291 },
286292 }
287293}
288294
289// Return true if musl has arch-specific crti/crtn sources.
290// See lib/libc/musl/crt/ARCH/crt?.s .
295/// Return true if musl has arch-specific crti/crtn sources.
296/// See lib/libc/musl/crt/ARCH/crt?.s .
291297pub fn needsCrtiCrtn(target: std.Target) bool {
292 // zig fmt: off
293298 return switch (target.cpu.arch) {
294 .riscv32,
295 .riscv64,
296 .wasm32, .wasm64 => false,
299 .riscv32, .riscv64, .wasm32, .wasm64 => false,
297300 .loongarch64 => false,
298301 else => true,
299302 };
300 // zig fmt: on
303}
304
305pub fn needsCrt0(output_mode: std.builtin.OutputMode, link_mode: std.builtin.LinkMode, pie: bool) ?CrtFile {
306 return switch (output_mode) {
307 .Obj, .Lib => null,
308 .Exe => switch (link_mode) {
309 .dynamic => if (pie) .scrt1_o else .crt1_o,
310 .static => if (pie) .rcrt1_o else .crt1_o,
311 },
312 };
301313}
302314
303315fn isMuslArchName(name: []const u8) bool {
src/target.zig+12-32
......@@ -1,4 +1,6 @@
11const std = @import("std");
2const assert = std.debug.assert;
3
24const Type = @import("Type.zig");
35const AddressSpace = std.builtin.AddressSpace;
46const Alignment = @import("InternPool.zig").Alignment;
......@@ -284,40 +286,18 @@ pub fn hasRedZone(target: std.Target) bool {
284286pub fn libcFullLinkFlags(target: std.Target) []const []const u8 {
285287 // The linking order of these is significant and should match the order other
286288 // c compilers such as gcc or clang use.
287 return switch (target.os.tag) {
288 .netbsd, .openbsd => &[_][]const u8{
289 "-lm",
290 "-lpthread",
291 "-lc",
292 "-lutil",
293 },
294 .solaris, .illumos => &[_][]const u8{
295 "-lm",
296 "-lsocket",
297 "-lnsl",
298 // Solaris releases after 10 merged the threading libraries into libc.
299 "-lc",
300 },
301 .haiku => &[_][]const u8{
302 "-lm",
303 "-lroot",
304 "-lpthread",
305 "-lc",
306 "-lnetwork",
307 },
308 else => if (target.isAndroid() or target.abi.isOpenHarmony()) &[_][]const u8{
309 "-lm",
310 "-lc",
311 "-ldl",
312 } else &[_][]const u8{
313 "-lm",
314 "-lpthread",
315 "-lc",
316 "-ldl",
317 "-lrt",
318 "-lutil",
289 const result: []const []const u8 = switch (target.os.tag) {
290 .netbsd, .openbsd => &.{ "-lm", "-lpthread", "-lc", "-lutil" },
291 // Solaris releases after 10 merged the threading libraries into libc.
292 .solaris, .illumos => &.{ "-lm", "-lsocket", "-lnsl", "-lc" },
293 .haiku => &.{ "-lm", "-lroot", "-lpthread", "-lc", "-lnetwork" },
294 .linux => switch (target.abi) {
295 .android, .androideabi, .ohos, .ohoseabi => &.{ "-lm", "-lc", "-ldl" },
296 else => &.{ "-lm", "-lpthread", "-lc", "-ldl", "-lrt", "-lutil" },
319297 },
298 else => &.{},
320299 };
300 return result;
321301}
322302
323303pub fn clangMightShellOutForAssembly(target: std.Target) bool {
test/link/elf.zig+115-92
......@@ -51,6 +51,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
5151 elf_step.dependOn(testEmitRelocatable(b, .{ .target = musl_target }));
5252 elf_step.dependOn(testRelocatableArchive(b, .{ .target = musl_target }));
5353 elf_step.dependOn(testRelocatableEhFrame(b, .{ .target = musl_target }));
54 elf_step.dependOn(testRelocatableEhFrameComdatHeavy(b, .{ .target = musl_target }));
5455 elf_step.dependOn(testRelocatableNoEhFrame(b, .{ .target = musl_target }));
5556
5657 // Exercise linker in ar mode
......@@ -2145,6 +2146,7 @@ fn testLdScript(b: *Build, opts: Options) *Step {
21452146 exe.addLibraryPath(dso.getEmittedBinDirectory());
21462147 exe.addRPath(dso.getEmittedBinDirectory());
21472148 exe.linkLibC();
2149 exe.allow_so_scripts = true;
21482150
21492151 const run = addRunArtifact(exe);
21502152 run.expectExitCode(0);
......@@ -2164,14 +2166,13 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
21642166 exe.linkSystemLibrary2("a", .{});
21652167 exe.addLibraryPath(scripts.getDirectory());
21662168 exe.linkLibC();
2169 exe.allow_so_scripts = true;
21672170
2168 expectLinkErrors(
2169 exe,
2170 test_step,
2171 .{
2172 .contains = "error: missing library dependency: GNU ld script '/?/liba.so' requires 'libfoo.so', but file not found",
2173 },
2174 );
2171 // TODO: A future enhancement could make this error message also mention
2172 // the file that references the missing library.
2173 expectLinkErrors(exe, test_step, .{
2174 .stderr_contains = "error: unable to find dynamic system library 'foo' using strategy 'no_fallback'. searched paths:",
2175 });
21752176
21762177 return test_step;
21772178}
......@@ -2203,6 +2204,7 @@ fn testLdScriptAllowUndefinedVersion(b: *Build, opts: Options) *Step {
22032204 });
22042205 exe.linkLibrary(so);
22052206 exe.linkLibC();
2207 exe.allow_so_scripts = true;
22062208
22072209 const run = addRunArtifact(exe);
22082210 run.expectStdErrEqual("3\n");
......@@ -2225,6 +2227,7 @@ fn testLdScriptDisallowUndefinedVersion(b: *Build, opts: Options) *Step {
22252227 const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }");
22262228 so.setLinkerScript(ld);
22272229 so.linker_allow_undefined_version = false;
2230 so.allow_so_scripts = true;
22282231
22292232 expectLinkErrors(
22302233 so,
......@@ -2721,86 +2724,110 @@ fn testRelocatableArchive(b: *Build, opts: Options) *Step {
27212724fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
27222725 const test_step = addTestStep(b, "relocatable-eh-frame", opts);
27232726
2724 {
2725 const obj = addObject(b, opts, .{
2726 .name = "obj1",
2727 .cpp_source_bytes =
2728 \\#include <stdexcept>
2729 \\int try_me() {
2730 \\ throw std::runtime_error("Oh no!");
2731 \\}
2732 ,
2733 });
2734 addCppSourceBytes(obj,
2735 \\extern int try_me();
2736 \\int try_again() {
2737 \\ return try_me();
2738 \\}
2739 , &.{});
2740 obj.linkLibCpp();
2727 const obj1 = addObject(b, opts, .{
2728 .name = "obj1",
2729 .cpp_source_bytes =
2730 \\#include <stdexcept>
2731 \\int try_me() {
2732 \\ throw std::runtime_error("Oh no!");
2733 \\}
2734 ,
2735 });
2736 obj1.linkLibCpp();
2737 const obj2 = addObject(b, opts, .{
2738 .name = "obj2",
2739 .cpp_source_bytes =
2740 \\extern int try_me();
2741 \\int try_again() {
2742 \\ return try_me();
2743 \\}
2744 ,
2745 });
2746 obj2.linkLibCpp();
27412747
2742 const exe = addExecutable(b, opts, .{ .name = "test1" });
2743 addCppSourceBytes(exe,
2744 \\#include <iostream>
2745 \\#include <stdexcept>
2746 \\extern int try_again();
2747 \\int main() {
2748 \\ try {
2749 \\ try_again();
2750 \\ } catch (const std::exception &e) {
2751 \\ std::cout << "exception=" << e.what();
2752 \\ }
2753 \\ return 0;
2754 \\}
2755 , &.{});
2756 exe.addObject(obj);
2757 exe.linkLibCpp();
2748 const obj = addObject(b, opts, .{ .name = "obj" });
2749 obj.addObject(obj1);
2750 obj.addObject(obj2);
2751 obj.linkLibCpp();
27582752
2759 const run = addRunArtifact(exe);
2760 run.expectStdOutEqual("exception=Oh no!");
2761 test_step.dependOn(&run.step);
2762 }
2753 const exe = addExecutable(b, opts, .{ .name = "test1" });
2754 addCppSourceBytes(exe,
2755 \\#include <iostream>
2756 \\#include <stdexcept>
2757 \\extern int try_again();
2758 \\int main() {
2759 \\ try {
2760 \\ try_again();
2761 \\ } catch (const std::exception &e) {
2762 \\ std::cout << "exception=" << e.what();
2763 \\ }
2764 \\ return 0;
2765 \\}
2766 , &.{});
2767 exe.addObject(obj);
2768 exe.linkLibCpp();
27632769
2764 {
2765 // Let's make the object file COMDAT group heavy!
2766 const obj = addObject(b, opts, .{
2767 .name = "obj2",
2768 .cpp_source_bytes =
2769 \\#include <stdexcept>
2770 \\int try_me() {
2771 \\ throw std::runtime_error("Oh no!");
2772 \\}
2773 ,
2774 });
2775 addCppSourceBytes(obj,
2776 \\extern int try_me();
2777 \\int try_again() {
2778 \\ return try_me();
2779 \\}
2780 , &.{});
2781 addCppSourceBytes(obj,
2782 \\#include <iostream>
2783 \\#include <stdexcept>
2784 \\extern int try_again();
2785 \\int main() {
2786 \\ try {
2787 \\ try_again();
2788 \\ } catch (const std::exception &e) {
2789 \\ std::cout << "exception=" << e.what();
2790 \\ }
2791 \\ return 0;
2792 \\}
2793 , &.{});
2794 obj.linkLibCpp();
2770 const run = addRunArtifact(exe);
2771 run.expectStdOutEqual("exception=Oh no!");
2772 test_step.dependOn(&run.step);
27952773
2796 const exe = addExecutable(b, opts, .{ .name = "test2" });
2797 exe.addObject(obj);
2798 exe.linkLibCpp();
2774 return test_step;
2775}
27992776
2800 const run = addRunArtifact(exe);
2801 run.expectStdOutEqual("exception=Oh no!");
2802 test_step.dependOn(&run.step);
2803 }
2777fn testRelocatableEhFrameComdatHeavy(b: *Build, opts: Options) *Step {
2778 const test_step = addTestStep(b, "relocatable-eh-frame-comdat-heavy", opts);
2779
2780 const obj1 = addObject(b, opts, .{
2781 .name = "obj1",
2782 .cpp_source_bytes =
2783 \\#include <stdexcept>
2784 \\int try_me() {
2785 \\ throw std::runtime_error("Oh no!");
2786 \\}
2787 ,
2788 });
2789 obj1.linkLibCpp();
2790 const obj2 = addObject(b, opts, .{
2791 .name = "obj2",
2792 .cpp_source_bytes =
2793 \\extern int try_me();
2794 \\int try_again() {
2795 \\ return try_me();
2796 \\}
2797 ,
2798 });
2799 obj2.linkLibCpp();
2800 const obj3 = addObject(b, opts, .{
2801 .name = "obj3",
2802 .cpp_source_bytes =
2803 \\#include <iostream>
2804 \\#include <stdexcept>
2805 \\extern int try_again();
2806 \\int main() {
2807 \\ try {
2808 \\ try_again();
2809 \\ } catch (const std::exception &e) {
2810 \\ std::cout << "exception=" << e.what();
2811 \\ }
2812 \\ return 0;
2813 \\}
2814 ,
2815 });
2816 obj3.linkLibCpp();
2817
2818 const obj = addObject(b, opts, .{ .name = "obj" });
2819 obj.addObject(obj1);
2820 obj.addObject(obj2);
2821 obj.addObject(obj3);
2822 obj.linkLibCpp();
2823
2824 const exe = addExecutable(b, opts, .{ .name = "test2" });
2825 exe.addObject(obj);
2826 exe.linkLibCpp();
2827
2828 const run = addRunArtifact(exe);
2829 run.expectStdOutEqual("exception=Oh no!");
2830 test_step.dependOn(&run.step);
28042831
28052832 return test_step;
28062833}
......@@ -3730,11 +3757,15 @@ fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {
37303757 \\#include <pthread.h>
37313758 \\#include <dlfcn.h>
37323759 \\#include <assert.h>
3760 \\#include <stdio.h>
37333761 \\void *(*verify)(void *);
37343762 \\
37353763 \\int main() {
37363764 \\ void *handle = dlopen("liba.so", RTLD_NOW);
3737 \\ assert(handle);
3765 \\ if (!handle) {
3766 \\ fprintf(stderr, "dlopen failed: %s\n", dlerror());
3767 \\ return 1;
3768 \\ }
37383769 \\ *(void**)(&verify) = dlsym(handle, "verify");
37393770 \\ assert(verify);
37403771 \\
......@@ -3907,16 +3938,8 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
39073938 exe.linkLibrary(dylib);
39083939 exe.linkLibC();
39093940
3910 // TODO: improve the test harness to be able to selectively match lines in error output
3911 // while avoiding jankiness
3912 // expectLinkErrors(exe, test_step, .{ .exact = &.{
3913 // "error: invalid token in LD script: '\\x00\\x00\\x00\\x0c\\x00\\x00\\x00/usr/lib/dyld\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0d' (0:989)",
3914 // "note: while parsing /?/liba.dylib",
3915 // "error: unexpected error: parsing input file failed with error InvalidLdScript",
3916 // "note: while parsing /?/liba.dylib",
3917 // } });
39183941 expectLinkErrors(exe, test_step, .{
3919 .starts_with = "error: invalid token in LD script: '\\x00\\x00\\x00\\x0c\\x00\\x00\\x00/usr/lib/dyld\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0d' (",
3942 .contains = "error: failed to parse shared library: BadMagic",
39203943 });
39213944
39223945 return test_step;