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...@@ -522,6 +522,7 @@ set(ZIG_STAGE2_SOURCES
522 src/Sema.zig522 src/Sema.zig
523 src/Sema/bitcast.zig523 src/Sema/bitcast.zig
524 src/Sema/comptime_ptr_access.zig524 src/Sema/comptime_ptr_access.zig
525 src/ThreadSafeQueue.zig
525 src/Type.zig526 src/Type.zig
526 src/Value.zig527 src/Value.zig
527 src/Zcu.zig528 src/Zcu.zig
...@@ -601,7 +602,6 @@ set(ZIG_STAGE2_SOURCES...@@ -601,7 +602,6 @@ set(ZIG_STAGE2_SOURCES
601 src/link/Elf/Archive.zig602 src/link/Elf/Archive.zig
602 src/link/Elf/Atom.zig603 src/link/Elf/Atom.zig
603 src/link/Elf/AtomList.zig604 src/link/Elf/AtomList.zig
604 src/link/Elf/LdScript.zig
605 src/link/Elf/LinkerDefined.zig605 src/link/Elf/LinkerDefined.zig
606 src/link/Elf/Merge.zig606 src/link/Elf/Merge.zig
607 src/link/Elf/Object.zig607 src/link/Elf/Object.zig
...@@ -615,6 +615,7 @@ set(ZIG_STAGE2_SOURCES...@@ -615,6 +615,7 @@ set(ZIG_STAGE2_SOURCES
615 src/link/Elf/relocatable.zig615 src/link/Elf/relocatable.zig
616 src/link/Elf/relocation.zig616 src/link/Elf/relocation.zig
617 src/link/Elf/synthetic_sections.zig617 src/link/Elf/synthetic_sections.zig
618 src/link/LdScript.zig
618 src/link/MachO.zig619 src/link/MachO.zig
619 src/link/MachO/Archive.zig620 src/link/MachO/Archive.zig
620 src/link/MachO/Atom.zig621 src/link/MachO/Atom.zig
lib/c.zig-4
...@@ -38,8 +38,6 @@ comptime {...@@ -38,8 +38,6 @@ comptime {
38 @export(&strncpy, .{ .name = "strncpy", .linkage = .strong });38 @export(&strncpy, .{ .name = "strncpy", .linkage = .strong });
39 @export(&strcat, .{ .name = "strcat", .linkage = .strong });39 @export(&strcat, .{ .name = "strcat", .linkage = .strong });
40 @export(&strncat, .{ .name = "strncat", .linkage = .strong });40 @export(&strncat, .{ .name = "strncat", .linkage = .strong });
41 } else if (is_msvc) {
42 @export(&_fltused, .{ .name = "_fltused", .linkage = .strong });
43 }41 }
44}42}
4543
...@@ -62,8 +60,6 @@ fn wasm_start() callconv(.C) void {...@@ -62,8 +60,6 @@ fn wasm_start() callconv(.C) void {
62 _ = main(0, undefined);60 _ = main(0, undefined);
63}61}
6462
65var _fltused: c_int = 1;
66
67fn strcpy(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 {63fn strcpy(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 {
68 var i: usize = 0;64 var i: usize = 0;
69 while (src[i] != 0) : (i += 1) {65 while (src[i] != 0) : (i += 1) {
lib/compiler/build_runner.zig+6
...@@ -280,6 +280,10 @@ pub fn main() !void {...@@ -280,6 +280,10 @@ pub fn main() !void {
280 builder.enable_darling = true;280 builder.enable_darling = true;
281 } else if (mem.eql(u8, arg, "-fno-darling")) {281 } else if (mem.eql(u8, arg, "-fno-darling")) {
282 builder.enable_darling = false;282 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;
283 } else if (mem.eql(u8, arg, "-freference-trace")) {287 } else if (mem.eql(u8, arg, "-freference-trace")) {
284 builder.reference_trace = 256;288 builder.reference_trace = 256;
285 } else if (mem.startsWith(u8, arg, "-freference-trace=")) {289 } else if (mem.startsWith(u8, arg, "-freference-trace=")) {
...@@ -1341,6 +1345,8 @@ fn usage(b: *std.Build, out_stream: anytype) !void {...@@ -1341,6 +1345,8 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
1341 \\Advanced Options:1345 \\Advanced Options:
1342 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error1346 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
1343 \\ -fno-reference-trace Disable reference trace1347 \\ -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
1344 \\ --build-file [file] Override path to build.zig1350 \\ --build-file [file] Override path to build.zig
1345 \\ --cache-dir [path] Override path to local Zig cache directory1351 \\ --cache-dir [path] Override path to local Zig cache directory
1346 \\ --global-cache-dir [path] Override path to global Zig cache directory1352 \\ --global-cache-dir [path] Override path to global Zig cache directory
lib/compiler_rt.zig+8-1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const builtin = @import("builtin");1const 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
5comptime {6comptime {
6 // Integer routines7 // Integer routines
...@@ -236,4 +237,10 @@ comptime {...@@ -236,4 +237,10 @@ comptime {
236 _ = @import("compiler_rt/bcmp.zig");237 _ = @import("compiler_rt/bcmp.zig");
237 _ = @import("compiler_rt/ssp.zig");238 _ = @import("compiler_rt/ssp.zig");
238 }239 }
240
241 if (!builtin.link_libc and builtin.abi == .msvc) {
242 @export(&_fltused, .{ .name = "_fltused", .linkage = common.linkage, .visibility = common.visibility });
243 }
239}244}
245
246var _fltused: c_int = 1;
lib/std/Build.zig+1
...@@ -123,6 +123,7 @@ pub const Graph = struct {...@@ -123,6 +123,7 @@ pub const Graph = struct {
123 incremental: ?bool = null,123 incremental: ?bool = null,
124 random_seed: u32 = 0,124 random_seed: u32 = 0,
125 dependency_cache: InitializedDepMap = .empty,125 dependency_cache: InitializedDepMap = .empty,
126 allow_so_scripts: ?bool = null,
126};127};
127128
128const AvailableDeps = []const struct { []const u8, []const u8 };129const 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{...@@ -142,6 +142,9 @@ pub const hasher_init: Hasher = Hasher.init(&[_]u8{
142pub const File = struct {142pub const File = struct {
143 prefixed_path: PrefixedPath,143 prefixed_path: PrefixedPath,
144 max_file_size: ?usize,144 max_file_size: ?usize,
145 /// Populated if the user calls `addOpenedFile`.
146 /// The handle is not owned here.
147 handle: ?fs.File,
145 stat: Stat,148 stat: Stat,
146 bin_digest: BinDigest,149 bin_digest: BinDigest,
147 contents: ?[]const u8,150 contents: ?[]const u8,
...@@ -173,6 +176,11 @@ pub const File = struct {...@@ -173,6 +176,11 @@ pub const File = struct {
173 const new = new_max_size orelse return;176 const new = new_max_size orelse return;
174 file.max_file_size = if (file.max_file_size) |old| @max(old, new) else new;177 file.max_file_size = if (file.max_file_size) |old| @max(old, new) else new;
175 }178 }
179
180 pub fn updateHandle(file: *File, new_handle: ?fs.File) void {
181 const handle = new_handle orelse return;
182 file.handle = handle;
183 }
176};184};
177185
178pub const HashHelper = struct {186pub const HashHelper = struct {
...@@ -363,15 +371,20 @@ pub const Manifest = struct {...@@ -363,15 +371,20 @@ pub const Manifest = struct {
363 /// var file_contents = cache_hash.files.keys()[file_index].contents.?;371 /// var file_contents = cache_hash.files.keys()[file_index].contents.?;
364 /// ```372 /// ```
365 pub fn addFilePath(m: *Manifest, file_path: Path, max_file_size: ?usize) !usize {373 pub fn addFilePath(m: *Manifest, file_path: Path, max_file_size: ?usize) !usize {
374 return addOpenedFile(m, file_path, null, max_file_size);
375 }
376
377 /// Same as `addFilePath` except the file has already been opened.
378 pub fn addOpenedFile(m: *Manifest, path: Path, handle: ?fs.File, max_file_size: ?usize) !usize {
366 const gpa = m.cache.gpa;379 const gpa = m.cache.gpa;
367 try m.files.ensureUnusedCapacity(gpa, 1);380 try m.files.ensureUnusedCapacity(gpa, 1);
368 const resolved_path = try fs.path.resolve(gpa, &.{381 const resolved_path = try fs.path.resolve(gpa, &.{
369 file_path.root_dir.path orelse ".",382 path.root_dir.path orelse ".",
370 file_path.subPathOrDot(),383 path.subPathOrDot(),
371 });384 });
372 errdefer gpa.free(resolved_path);385 errdefer gpa.free(resolved_path);
373 const prefixed_path = try m.cache.findPrefixResolved(resolved_path);386 const prefixed_path = try m.cache.findPrefixResolved(resolved_path);
374 return addFileInner(m, prefixed_path, max_file_size);387 return addFileInner(m, prefixed_path, handle, max_file_size);
375 }388 }
376389
377 /// Deprecated; use `addFilePath`.390 /// Deprecated; use `addFilePath`.
...@@ -383,13 +396,14 @@ pub const Manifest = struct {...@@ -383,13 +396,14 @@ pub const Manifest = struct {
383 const prefixed_path = try self.cache.findPrefix(file_path);396 const prefixed_path = try self.cache.findPrefix(file_path);
384 errdefer gpa.free(prefixed_path.sub_path);397 errdefer gpa.free(prefixed_path.sub_path);
385398
386 return addFileInner(self, prefixed_path, max_file_size);399 return addFileInner(self, prefixed_path, null, max_file_size);
387 }400 }
388401
389 fn addFileInner(self: *Manifest, prefixed_path: PrefixedPath, max_file_size: ?usize) !usize {402 fn addFileInner(self: *Manifest, prefixed_path: PrefixedPath, handle: ?fs.File, max_file_size: ?usize) usize {
390 const gop = self.files.getOrPutAssumeCapacityAdapted(prefixed_path, FilesAdapter{});403 const gop = self.files.getOrPutAssumeCapacityAdapted(prefixed_path, FilesAdapter{});
391 if (gop.found_existing) {404 if (gop.found_existing) {
392 gop.key_ptr.updateMaxSize(max_file_size);405 gop.key_ptr.updateMaxSize(max_file_size);
406 gop.key_ptr.updateHandle(handle);
393 return gop.index;407 return gop.index;
394 }408 }
395 gop.key_ptr.* = .{409 gop.key_ptr.* = .{
...@@ -398,6 +412,7 @@ pub const Manifest = struct {...@@ -398,6 +412,7 @@ pub const Manifest = struct {
398 .max_file_size = max_file_size,412 .max_file_size = max_file_size,
399 .stat = undefined,413 .stat = undefined,
400 .bin_digest = undefined,414 .bin_digest = undefined,
415 .handle = handle,
401 };416 };
402417
403 self.hash.add(prefixed_path.prefix);418 self.hash.add(prefixed_path.prefix);
...@@ -565,6 +580,7 @@ pub const Manifest = struct {...@@ -565,6 +580,7 @@ pub const Manifest = struct {
565 },580 },
566 .contents = null,581 .contents = null,
567 .max_file_size = null,582 .max_file_size = null,
583 .handle = null,
568 .stat = .{584 .stat = .{
569 .size = stat_size,585 .size = stat_size,
570 .inode = stat_inode,586 .inode = stat_inode,
...@@ -708,12 +724,19 @@ pub const Manifest = struct {...@@ -708,12 +724,19 @@ pub const Manifest = struct {
708 }724 }
709725
710 fn populateFileHash(self: *Manifest, ch_file: *File) !void {726 fn populateFileHash(self: *Manifest, ch_file: *File) !void {
711 const pp = ch_file.prefixed_path;727 if (ch_file.handle) |handle| {
712 const dir = self.cache.prefixes()[pp.prefix].handle;728 return populateFileHashHandle(self, ch_file, handle);
713 const file = try dir.openFile(pp.sub_path, .{});729 } else {
714 defer file.close();730 const pp = ch_file.prefixed_path;
731 const dir = self.cache.prefixes()[pp.prefix].handle;
732 const handle = try dir.openFile(pp.sub_path, .{});
733 defer handle.close();
734 return populateFileHashHandle(self, ch_file, handle);
735 }
736 }
715737
716 const actual_stat = try file.stat();738 fn populateFileHashHandle(self: *Manifest, ch_file: *File, handle: fs.File) !void {
739 const actual_stat = try handle.stat();
717 ch_file.stat = .{740 ch_file.stat = .{
718 .size = actual_stat.size,741 .size = actual_stat.size,
719 .mtime = actual_stat.mtime,742 .mtime = actual_stat.mtime,
...@@ -739,8 +762,7 @@ pub const Manifest = struct {...@@ -739,8 +762,7 @@ pub const Manifest = struct {
739 var hasher = hasher_init;762 var hasher = hasher_init;
740 var off: usize = 0;763 var off: usize = 0;
741 while (true) {764 while (true) {
742 // give me everything you've got, captain765 const bytes_read = try handle.pread(contents[off..], off);
743 const bytes_read = try file.read(contents[off..]);
744 if (bytes_read == 0) break;766 if (bytes_read == 0) break;
745 hasher.update(contents[off..][0..bytes_read]);767 hasher.update(contents[off..][0..bytes_read]);
746 off += bytes_read;768 off += bytes_read;
...@@ -749,7 +771,7 @@ pub const Manifest = struct {...@@ -749,7 +771,7 @@ pub const Manifest = struct {
749771
750 ch_file.contents = contents;772 ch_file.contents = contents;
751 } else {773 } else {
752 try hashFile(file, &ch_file.bin_digest);774 try hashFile(handle, &ch_file.bin_digest);
753 }775 }
754776
755 self.hash.hasher.update(&ch_file.bin_digest);777 self.hash.hasher.update(&ch_file.bin_digest);
...@@ -813,6 +835,7 @@ pub const Manifest = struct {...@@ -813,6 +835,7 @@ pub const Manifest = struct {
813 gop.key_ptr.* = .{835 gop.key_ptr.* = .{
814 .prefixed_path = prefixed_path,836 .prefixed_path = prefixed_path,
815 .max_file_size = null,837 .max_file_size = null,
838 .handle = null,
816 .stat = undefined,839 .stat = undefined,
817 .bin_digest = undefined,840 .bin_digest = undefined,
818 .contents = null,841 .contents = null,
...@@ -851,6 +874,7 @@ pub const Manifest = struct {...@@ -851,6 +874,7 @@ pub const Manifest = struct {
851 new_file.* = .{874 new_file.* = .{
852 .prefixed_path = prefixed_path,875 .prefixed_path = prefixed_path,
853 .max_file_size = null,876 .max_file_size = null,
877 .handle = null,
854 .stat = stat,878 .stat = stat,
855 .bin_digest = undefined,879 .bin_digest = undefined,
856 .contents = null,880 .contents = null,
...@@ -1067,6 +1091,7 @@ pub const Manifest = struct {...@@ -1067,6 +1091,7 @@ pub const Manifest = struct {
1067 gop.key_ptr.* = .{1091 gop.key_ptr.* = .{
1068 .prefixed_path = prefixed_path,1092 .prefixed_path = prefixed_path,
1069 .max_file_size = file.max_file_size,1093 .max_file_size = file.max_file_size,
1094 .handle = file.handle,
1070 .stat = file.stat,1095 .stat = file.stat,
1071 .bin_digest = file.bin_digest,1096 .bin_digest = file.bin_digest,
1072 .contents = null,1097 .contents = null,
...@@ -1103,14 +1128,14 @@ pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void...@@ -1103,14 +1128,14 @@ pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void
11031128
1104fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {1129fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {
1105 var buf: [1024]u8 = undefined;1130 var buf: [1024]u8 = undefined;
1106
1107 var hasher = hasher_init;1131 var hasher = hasher_init;
1132 var off: u64 = 0;
1108 while (true) {1133 while (true) {
1109 const bytes_read = try file.read(&buf);1134 const bytes_read = try file.pread(&buf, off);
1110 if (bytes_read == 0) break;1135 if (bytes_read == 0) break;
1111 hasher.update(buf[0..bytes_read]);1136 hasher.update(buf[0..bytes_read]);
1137 off += bytes_read;
1112 }1138 }
1113
1114 hasher.final(bin_digest);1139 hasher.final(bin_digest);
1115}1140}
11161141
lib/std/Build/Step/Compile.zig+42-8
...@@ -186,6 +186,15 @@ want_lto: ?bool = null,...@@ -186,6 +186,15 @@ want_lto: ?bool = null,
186use_llvm: ?bool,186use_llvm: ?bool,
187use_lld: ?bool,187use_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
189/// This is an advanced setting that can change the intent of this Compile step.198/// This is an advanced setting that can change the intent of this Compile step.
190/// If this value is non-null, it means that this Compile step exists to199/// If this value is non-null, it means that this Compile step exists to
191/// check for compile errors and return *success* if they match, and failure200/// check for compile errors and return *success* if they match, and failure
...@@ -236,6 +245,7 @@ pub const ExpectedCompileErrors = union(enum) {...@@ -236,6 +245,7 @@ pub const ExpectedCompileErrors = union(enum) {
236 contains: []const u8,245 contains: []const u8,
237 exact: []const []const u8,246 exact: []const []const u8,
238 starts_with: []const u8,247 starts_with: []const u8,
248 stderr_contains: []const u8,
239};249};
240250
241pub const Entry = union(enum) {251pub const Entry = union(enum) {
...@@ -1035,6 +1045,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -1035,6 +1045,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
1035 if (b.reference_trace) |some| {1045 if (b.reference_trace) |some| {
1036 try zig_args.append(try std.fmt.allocPrint(arena, "-freference-trace={d}", .{some}));1046 try zig_args.append(try std.fmt.allocPrint(arena, "-freference-trace={d}", .{some}));
1037 }1047 }
1048 try addFlag(&zig_args, "allow-so-scripts", compile.allow_so_scripts orelse b.graph.allow_so_scripts);
10381049
1039 try addFlag(&zig_args, "llvm", compile.use_llvm);1050 try addFlag(&zig_args, "llvm", compile.use_llvm);
1040 try addFlag(&zig_args, "lld", compile.use_lld);1051 try addFlag(&zig_args, "lld", compile.use_lld);
...@@ -1945,24 +1956,24 @@ fn checkCompileErrors(compile: *Compile) !void {...@@ -1945,24 +1956,24 @@ fn checkCompileErrors(compile: *Compile) !void {
19451956
1946 const arena = compile.step.owner.allocator;1957 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);
1949 try actual_eb.renderToWriter(.{1960 try actual_eb.renderToWriter(.{
1950 .ttyconf = .no_color,1961 .ttyconf = .no_color,
1951 .include_reference_trace = false,1962 .include_reference_trace = false,
1952 .include_source_line = false,1963 .include_source_line = false,
1953 }, actual_stderr_list.writer());1964 }, actual_errors_list.writer());
1954 const actual_stderr = try actual_stderr_list.toOwnedSlice();1965 const actual_errors = try actual_errors_list.toOwnedSlice();
19551966
1956 // Render the expected lines into a string that we can compare verbatim.1967 // Render the expected lines into a string that we can compare verbatim.
1957 var expected_generated = std.ArrayList(u8).init(arena);1968 var expected_generated = std.ArrayList(u8).init(arena);
1958 const expect_errors = compile.expect_errors.?;1969 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
1962 // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile1973 // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile
1963 switch (expect_errors) {1974 switch (expect_errors) {
1964 .starts_with => |expect_starts_with| {1975 .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;
1966 return compile.step.fail(1977 return compile.step.fail(
1967 \\1978 \\
1968 \\========= should start with: ============1979 \\========= should start with: ============
...@@ -1970,7 +1981,7 @@ fn checkCompileErrors(compile: *Compile) !void {...@@ -1970,7 +1981,7 @@ fn checkCompileErrors(compile: *Compile) !void {
1970 \\========= but not found: ================1981 \\========= but not found: ================
1971 \\{s}1982 \\{s}
1972 \\=========================================1983 \\=========================================
1973 , .{ expect_starts_with, actual_stderr });1984 , .{ expect_starts_with, actual_errors });
1974 },1985 },
1975 .contains => |expect_line| {1986 .contains => |expect_line| {
1976 while (actual_line_it.next()) |actual_line| {1987 while (actual_line_it.next()) |actual_line| {
...@@ -1978,6 +1989,29 @@ fn checkCompileErrors(compile: *Compile) !void {...@@ -1978,6 +1989,29 @@ fn checkCompileErrors(compile: *Compile) !void {
1978 return;1989 return;
1979 }1990 }
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
1981 return compile.step.fail(2015 return compile.step.fail(
1982 \\2016 \\
1983 \\========= should contain: ===============2017 \\========= should contain: ===============
...@@ -2003,7 +2037,7 @@ fn checkCompileErrors(compile: *Compile) !void {...@@ -2003,7 +2037,7 @@ fn checkCompileErrors(compile: *Compile) !void {
2003 try expected_generated.append('\n');2037 try expected_generated.append('\n');
2004 }2038 }
20052039
2006 if (mem.eql(u8, expected_generated.items, actual_stderr)) return;2040 if (mem.eql(u8, expected_generated.items, actual_errors)) return;
20072041
2008 return compile.step.fail(2042 return compile.step.fail(
2009 \\2043 \\
...@@ -2012,7 +2046,7 @@ fn checkCompileErrors(compile: *Compile) !void {...@@ -2012,7 +2046,7 @@ fn checkCompileErrors(compile: *Compile) !void {
2012 \\========= but found: ====================2046 \\========= but found: ====================
2013 \\{s}2047 \\{s}
2014 \\=========================================2048 \\=========================================
2015 , .{ expected_generated.items, actual_stderr });2049 , .{ expected_generated.items, actual_errors });
2016 },2050 },
2017 }2051 }
2018}2052}
lib/std/Thread/WaitGroup.zig+5
...@@ -14,6 +14,11 @@ pub fn start(self: *WaitGroup) void {...@@ -14,6 +14,11 @@ pub fn start(self: *WaitGroup) void {
14 assert((state / one_pending) < (std.math.maxInt(usize) / one_pending));14 assert((state / one_pending) < (std.math.maxInt(usize) / one_pending));
15}15}
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
17pub fn finish(self: *WaitGroup) void {22pub fn finish(self: *WaitGroup) void {
18 const state = self.state.fetchSub(one_pending, .acq_rel);23 const state = self.state.fetchSub(one_pending, .acq_rel);
19 assert((state / one_pending) > 0);24 assert((state / one_pending) > 0);
src/Compilation.zig+297-388
...@@ -10,6 +10,7 @@ const Target = std.Target;...@@ -10,6 +10,7 @@ const Target = std.Target;
10const ThreadPool = std.Thread.Pool;10const ThreadPool = std.Thread.Pool;
11const WaitGroup = std.Thread.WaitGroup;11const WaitGroup = std.Thread.WaitGroup;
12const ErrorBundle = std.zig.ErrorBundle;12const ErrorBundle = std.zig.ErrorBundle;
13const Path = Cache.Path;
1314
14const Value = @import("Value.zig");15const Value = @import("Value.zig");
15const Type = @import("Type.zig");16const Type = @import("Type.zig");
...@@ -39,15 +40,17 @@ const Air = @import("Air.zig");...@@ -39,15 +40,17 @@ const Air = @import("Air.zig");
39const Builtin = @import("Builtin.zig");40const Builtin = @import("Builtin.zig");
40const LlvmObject = @import("codegen/llvm.zig").Object;41const LlvmObject = @import("codegen/llvm.zig").Object;
41const dev = @import("dev.zig");42const dev = @import("dev.zig");
42pub const Directory = Cache.Directory;43const ThreadSafeQueue = @import("ThreadSafeQueue.zig").ThreadSafeQueue;
43const Path = Cache.Path;
4444
45pub const Directory = Cache.Directory;
45pub const Config = @import("Compilation/Config.zig");46pub const Config = @import("Compilation/Config.zig");
4647
47/// General-purpose allocator. Used for both temporary and long-term storage.48/// General-purpose allocator. Used for both temporary and long-term storage.
48gpa: Allocator,49gpa: Allocator,
49/// Arena-allocated memory, mostly used during initialization. However, it can50/// Arena-allocated memory, mostly used during initialization. However, it can
50/// be used for other things requiring the same lifetime as the `Compilation`.51/// 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.
51arena: Allocator,54arena: Allocator,
52/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.55/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
53zcu: ?*Zcu,56zcu: ?*Zcu,
...@@ -76,12 +79,13 @@ implib_emit: ?Path,...@@ -76,12 +79,13 @@ implib_emit: ?Path,
76docs_emit: ?Path,79docs_emit: ?Path,
77root_name: [:0]const u8,80root_name: [:0]const u8,
78include_compiler_rt: bool,81include_compiler_rt: bool,
79objects: []Compilation.LinkObject,82/// Resolved into known paths, any GNU ld scripts already resolved.
83link_inputs: []const link.Input,
80/// Needed only for passing -F args to clang.84/// Needed only for passing -F args to clang.
81framework_dirs: []const []const u8,85framework_dirs: []const []const u8,
82/// These are *always* dynamically linked. Static libraries will be86/// These are only for DLLs dependencies fulfilled by the `.def` files shipped
83/// provided as positional arguments.87/// with Zig. Static libraries are provided as `link.Input` values.
84system_libs: std.StringArrayHashMapUnmanaged(SystemLib),88windows_libs: std.StringArrayHashMapUnmanaged(void),
85version: ?std.SemanticVersion,89version: ?std.SemanticVersion,
86libc_installation: ?*const LibCInstallation,90libc_installation: ?*const LibCInstallation,
87skip_linker_dependencies: bool,91skip_linker_dependencies: bool,
...@@ -107,6 +111,9 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa...@@ -107,6 +111,9 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa
107} = .{},111} = .{},
108112
109link_diags: link.Diags,113link_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
111work_queues: [118work_queues: [
112 len: {119 len: {
...@@ -118,14 +125,6 @@ work_queues: [...@@ -118,14 +125,6 @@ work_queues: [
118 }125 }
119]std.fifo.LinearFifo(Job, .Dynamic),126]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
129/// These jobs are to invoke the Clang compiler to create an object file, which128/// These jobs are to invoke the Clang compiler to create an object file, which
130/// gets linked with the Compilation.129/// gets linked with the Compilation.
131c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic),130c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic),
...@@ -262,6 +261,9 @@ emit_asm: ?EmitLoc,...@@ -262,6 +261,9 @@ emit_asm: ?EmitLoc,
262emit_llvm_ir: ?EmitLoc,261emit_llvm_ir: ?EmitLoc,
263emit_llvm_bc: ?EmitLoc,262emit_llvm_bc: ?EmitLoc,
264263
264link_task_wait_group: WaitGroup = .{},
265work_queue_progress_node: std.Progress.Node = .none,
266
265llvm_opt_bisect_limit: c_int,267llvm_opt_bisect_limit: c_int,
266268
267file_system_inputs: ?*std.ArrayListUnmanaged(u8),269file_system_inputs: ?*std.ArrayListUnmanaged(u8),
...@@ -339,16 +341,14 @@ pub const RcIncludes = enum {...@@ -339,16 +341,14 @@ pub const RcIncludes = enum {
339};341};
340342
341const Job = union(enum) {343const 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.
343 codegen_nav: InternPool.Nav.Index,346 codegen_nav: InternPool.Nav.Index,
344 /// Write the machine code for a function to the output file.347 /// Corresponds to the task in `link.Task`.
345 codegen_func: struct {348 /// Only needed for backends that haven't yet been updated to not race against Sema.
346 /// This will either be a non-generic `func_decl` or a `func_instance`.349 codegen_func: link.Task.CodegenFunc,
347 func: InternPool.Index,350 /// Corresponds to the task in `link.Task`.
348 /// This `Air` is owned by the `Job` and allocated with `gpa`.351 /// Only needed for backends that haven't yet been updated to not race against Sema.
349 /// It must be deinited when the job is processed.
350 air: Air,
351 },
352 codegen_type: InternPool.Index,352 codegen_type: InternPool.Index,
353 /// The `Cau` must be semantically analyzed (and possibly export itself).353 /// The `Cau` must be semantically analyzed (and possibly export itself).
354 /// This may be its first time being analyzed, or it may be outdated.354 /// This may be its first time being analyzed, or it may be outdated.
...@@ -357,9 +357,6 @@ const Job = union(enum) {...@@ -357,9 +357,6 @@ const Job = union(enum) {
357 /// After analysis, a `codegen_func` job will be queued.357 /// After analysis, a `codegen_func` job will be queued.
358 /// These must be separate jobs to ensure any needed type resolution occurs *before* codegen.358 /// These must be separate jobs to ensure any needed type resolution occurs *before* codegen.
359 analyze_func: InternPool.Index,359 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
363 /// The main source file for the module needs to be analyzed.360 /// The main source file for the module needs to be analyzed.
364 analyze_mod: *Package.Module,361 analyze_mod: *Package.Module,
365 /// Fully resolve the given `struct` or `union` type.362 /// Fully resolve the given `struct` or `union` type.
...@@ -373,6 +370,7 @@ const Job = union(enum) {...@@ -373,6 +370,7 @@ const Job = union(enum) {
373 musl_crt_file: musl.CrtFile,370 musl_crt_file: musl.CrtFile,
374 /// one of the mingw-w64 static objects371 /// one of the mingw-w64 static objects
375 mingw_crt_file: mingw.CrtFile,372 mingw_crt_file: mingw.CrtFile,
373
376 /// libunwind.a, usually needed when linking libc374 /// libunwind.a, usually needed when linking libc
377 libunwind: void,375 libunwind: void,
378 libcxx: void,376 libcxx: void,
...@@ -384,7 +382,7 @@ const Job = union(enum) {...@@ -384,7 +382,7 @@ const Job = union(enum) {
384 /// one of WASI libc static objects382 /// one of WASI libc static objects
385 wasi_libc_crt_file: wasi_libc.CrtFile,383 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`.
388 windows_import_lib: usize,386 windows_import_lib: usize,
389387
390 const Tag = @typeInfo(Job).@"union".tag_type.?;388 const Tag = @typeInfo(Job).@"union".tag_type.?;
...@@ -402,17 +400,6 @@ const Job = union(enum) {...@@ -402,17 +400,6 @@ const Job = union(enum) {
402 }400 }
403};401};
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
416pub const CObject = struct {403pub const CObject = struct {
417 /// Relative to cwd. Owned by arena.404 /// Relative to cwd. Owned by arena.
418 src: CSourceFile,405 src: CSourceFile,
...@@ -999,24 +986,6 @@ const CacheUse = union(CacheMode) {...@@ -999,24 +986,6 @@ const CacheUse = union(CacheMode) {
999 }986 }
1000};987};
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
1020pub const CreateOptions = struct {989pub const CreateOptions = struct {
1021 zig_lib_directory: Directory,990 zig_lib_directory: Directory,
1022 local_cache_directory: Directory,991 local_cache_directory: Directory,
...@@ -1061,18 +1030,20 @@ pub const CreateOptions = struct {...@@ -1061,18 +1030,20 @@ pub const CreateOptions = struct {
1061 /// this flag would be set to disable this machinery to avoid false positives.1030 /// this flag would be set to disable this machinery to avoid false positives.
1062 disable_lld_caching: bool = false,1031 disable_lld_caching: bool = false,
1063 cache_mode: CacheMode = .incremental,1032 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 = &.{},
1065 rpath_list: []const []const u8 = &[0][]const u8{},1037 rpath_list: []const []const u8 = &[0][]const u8{},
1066 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty,1038 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty,
1067 c_source_files: []const CSourceFile = &.{},1039 c_source_files: []const CSourceFile = &.{},
1068 rc_source_files: []const RcSourceFile = &.{},1040 rc_source_files: []const RcSourceFile = &.{},
1069 manifest_file: ?[]const u8 = null,1041 manifest_file: ?[]const u8 = null,
1070 rc_includes: RcIncludes = .any,1042 rc_includes: RcIncludes = .any,
1071 link_objects: []LinkObject = &[0]LinkObject{},1043 link_inputs: []const link.Input = &.{},
1072 framework_dirs: []const []const u8 = &[0][]const u8{},1044 framework_dirs: []const []const u8 = &[0][]const u8{},
1073 frameworks: []const Framework = &.{},1045 frameworks: []const Framework = &.{},
1074 system_lib_names: []const []const u8 = &.{},1046 windows_lib_names: []const []const u8 = &.{},
1075 system_lib_infos: []const SystemLib = &.{},
1076 /// These correspond to the WASI libc emulated subcomponents including:1047 /// These correspond to the WASI libc emulated subcomponents including:
1077 /// * process clocks1048 /// * process clocks
1078 /// * getpid1049 /// * getpid
...@@ -1455,12 +1426,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1455,12 +1426,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1455 };1426 };
1456 errdefer if (opt_zcu) |zcu| zcu.deinit();1427 errdefer if (opt_zcu) |zcu| zcu.deinit();
14571428
1458 var system_libs = try std.StringArrayHashMapUnmanaged(SystemLib).init(1429 var windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{});
1459 gpa,1430 errdefer windows_libs.deinit(gpa);
1460 options.system_lib_names,
1461 options.system_lib_infos,
1462 );
1463 errdefer system_libs.deinit(gpa);
14641431
1465 comp.* = .{1432 comp.* = .{
1466 .gpa = gpa,1433 .gpa = gpa,
...@@ -1479,13 +1446,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1479,13 +1446,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1479 .emit_llvm_ir = options.emit_llvm_ir,1446 .emit_llvm_ir = options.emit_llvm_ir,
1480 .emit_llvm_bc = options.emit_llvm_bc,1447 .emit_llvm_bc = options.emit_llvm_bc,
1481 .work_queues = .{std.fifo.LinearFifo(Job, .Dynamic).init(gpa)} ** @typeInfo(std.meta.FieldType(Compilation, .work_queues)).array.len,1448 .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 },
1489 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),1449 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),
1490 .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa) else .{},1450 .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa) else .{},
1491 .astgen_work_queue = std.fifo.LinearFifo(Zcu.File.Index, .Dynamic).init(gpa),1451 .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...@@ -1522,11 +1482,11 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1522 .libcxx_abi_version = options.libcxx_abi_version,1482 .libcxx_abi_version = options.libcxx_abi_version,
1523 .root_name = root_name,1483 .root_name = root_name,
1524 .sysroot = sysroot,1484 .sysroot = sysroot,
1525 .system_libs = system_libs,1485 .windows_libs = windows_libs,
1526 .version = options.version,1486 .version = options.version,
1527 .libc_installation = libc_dirs.libc_installation,1487 .libc_installation = libc_dirs.libc_installation,
1528 .include_compiler_rt = include_compiler_rt,1488 .include_compiler_rt = include_compiler_rt,
1529 .objects = options.link_objects,1489 .link_inputs = options.link_inputs,
1530 .framework_dirs = options.framework_dirs,1490 .framework_dirs = options.framework_dirs,
1531 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,1491 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
1532 .skip_linker_dependencies = options.skip_linker_dependencies,1492 .skip_linker_dependencies = options.skip_linker_dependencies,
...@@ -1564,7 +1524,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1564,7 +1524,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1564 .z_max_page_size = options.linker_z_max_page_size,1524 .z_max_page_size = options.linker_z_max_page_size,
1565 .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,1525 .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,
1566 .frameworks = options.frameworks,1526 .frameworks = options.frameworks,
1567 .lib_dirs = options.lib_dirs,1527 .lib_directories = options.lib_directories,
1568 .framework_dirs = options.framework_dirs,1528 .framework_dirs = options.framework_dirs,
1569 .rpath_list = options.rpath_list,1529 .rpath_list = options.rpath_list,
1570 .symbol_wrap_set = options.symbol_wrap_set,1530 .symbol_wrap_set = options.symbol_wrap_set,
...@@ -1776,157 +1736,175 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1776,157 +1736,175 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1776 .incremental => comp.bin_file != null,1736 .incremental => comp.bin_file != null,
1777 };1737 };
17781738
1779 if (have_bin_emit and !comp.skip_linker_dependencies and target.ofmt != .c) {1739 if (have_bin_emit and target.ofmt != .c) {
1780 if (target.isDarwin()) {1740 if (!comp.skip_linker_dependencies) {
1781 switch (target.abi) {1741 // If we need to build libc for the target, add work items for it.
1782 .none,1742 // We go through the work queue so that building can be done in parallel.
1783 .simulator,1743 // If linking against host libc installation, instead queue up jobs
1784 .macabi,1744 // for loading those files in the linker.
1785 => {},1745 if (comp.config.link_libc and is_exe_or_dyn_lib) {
1786 else => return error.LibCUnavailable,1746 // If the "is darwin" check is moved below the libc_installation check below,
1787 }1747 // error.LibCInstallationMissingCrtDir is returned from lci.resolveCrtPaths().
1788 }1748 if (target.isDarwin()) {
1789 // If we need to build glibc for the target, add work items for it.1749 switch (target.abi) {
1790 // We go through the work queue so that building can be done in parallel.1750 .none, .simulator, .macabi => {},
1791 if (comp.wantBuildGLibCFromSource()) {1751 else => return error.LibCUnavailable,
1792 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;1752 }
17931753 // TODO delete logic from MachO flush() and queue up tasks here instead.
1794 if (glibc.needsCrtiCrtn(target)) {1754 } else if (comp.libc_installation) |lci| {
1795 try comp.queueJobs(&[_]Job{1755 const basenames = LibCInstallation.CrtBasenames.get(.{
1796 .{ .glibc_crt_file = .crti_o },1756 .target = target,
1797 .{ .glibc_crt_file = .crtn_o },1757 .link_libc = comp.config.link_libc,
1798 });1758 .output_mode = comp.config.output_mode,
1799 }1759 .link_mode = comp.config.link_mode,
1800 try comp.queueJobs(&[_]Job{1760 .pie = comp.config.pie,
1801 .{ .glibc_crt_file = .scrt1_o },1761 });
1802 .{ .glibc_crt_file = .libc_nonshared_a },1762 const paths = try lci.resolveCrtPaths(arena, basenames, target);
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 }
18251763
1826 if (comp.wantBuildWasiLibcFromSource()) {1764 const fields = @typeInfo(@TypeOf(paths)).@"struct".fields;
1827 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;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| {1808 for (comp.wasi_emulated_libs) |crt_file| {
1830 try comp.queueJob(.{1809 try comp.queueJob(.{
1831 .wasi_libc_crt_file = crt_file,1810 .wasi_libc_crt_file = crt_file,
1832 });1811 });
1833 }1812 }
1834 try comp.queueJobs(&[_]Job{1813 try comp.queueJobs(&[_]Job{
1835 .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) },1814 .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) },
1836 .{ .wasi_libc_crt_file = .libc_a },1815 .{ .wasi_libc_crt_file = .libc_a },
1837 });1816 });
1838 }1817 } else if (target.isMinGW()) {
1818 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
18391819
1840 if (comp.wantBuildMinGWFromSource()) {1820 const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o };
1841 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;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 };1826 // When linking mingw-w64 there are some import libs we always need.
1844 try comp.queueJobs(&.{1827 try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);
1845 .{ .mingw_crt_file = .mingw32_lib },1828 for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {});
1846 crt_job,1829 } else if (target.isDarwin()) {
1847 });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.1841 // Generate Windows import libs.
1850 for (mingw.always_link_libs) |name| {1842 if (target.os.tag == .windows) {
1851 try comp.system_libs.put(comp.gpa, name, .{1843 const count = comp.windows_libs.count();
1852 .needed = false,1844 for (0..count) |i| {
1853 .weak = false,1845 try comp.queueJob(.{ .windows_import_lib = i });
1854 .path = null,1846 }
1855 });
1856 }1847 }
1857 }1848 if (comp.wantBuildLibUnwindFromSource()) {
1858 // Generate Windows import libs.1849 try comp.queueJob(.{ .libunwind = {} });
1859 if (target.os.tag == .windows) {1850 }
1860 const count = comp.system_libs.count();1851 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
1861 for (0..count) |i| {1852 try comp.queueJob(.libcxx);
1862 try comp.queueJob(.{ .windows_import_lib = i });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);
1863 }1857 }
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) {1859 if (target.isMinGW() and comp.config.any_non_single_threaded) {
1877 // LLD might drop some symbols as unused during LTO and GCing, therefore,1860 // LLD might drop some symbols as unused during LTO and GCing, therefore,
1878 // we force mark them for resolution here.1861 // we force mark them for resolution here.
18791862
1880 const tls_index_sym = switch (target.cpu.arch) {1863 const tls_index_sym = switch (target.cpu.arch) {
1881 .x86 => "__tls_index",1864 .x86 => "__tls_index",
1882 else => "_tls_index",1865 else => "_tls_index",
1883 };1866 };
18841867
1885 try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});1868 try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});
1886 }1869 }
18871870
1888 if (comp.include_compiler_rt and capable_of_building_compiler_rt) {1871 if (comp.include_compiler_rt and capable_of_building_compiler_rt) {
1889 if (is_exe_or_dyn_lib) {1872 if (is_exe_or_dyn_lib) {
1890 log.debug("queuing a job to build compiler_rt_lib", .{});1873 log.debug("queuing a job to build compiler_rt_lib", .{});
1891 comp.job_queued_compiler_rt_lib = true;1874 comp.job_queued_compiler_rt_lib = true;
1892 } else if (output_mode != .Obj) {1875 } else if (output_mode != .Obj) {
1893 log.debug("queuing a job to build compiler_rt_obj", .{});1876 log.debug("queuing a job to build compiler_rt_obj", .{});
1894 // In this case we are making a static library, so we ask1877 // In this case we are making a static library, so we ask
1895 // for a compiler-rt object to put in it.1878 // for a compiler-rt object to put in it.
1896 comp.job_queued_compiler_rt_obj = true;1879 comp.job_queued_compiler_rt_obj = true;
1880 }
1897 }1881 }
1898 }
18991882
1900 if (comp.config.any_fuzz and capable_of_building_compiler_rt) {1883 if (is_exe_or_dyn_lib and comp.config.any_fuzz and capable_of_building_compiler_rt) {
1901 if (is_exe_or_dyn_lib) {
1902 log.debug("queuing a job to build libfuzzer", .{});1884 log.debug("queuing a job to build libfuzzer", .{});
1903 comp.job_queued_fuzzer_lib = true;1885 comp.job_queued_fuzzer_lib = true;
1904 }1886 }
1905 }1887 }
19061888
1907 if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and1889 try comp.link_task_queue.shared.append(gpa, .load_explicitly_provided);
1908 !comp.config.link_libc and capable_of_building_zig_libc)
1909 {
1910 try comp.queueJob(.{ .zig_libc = {} });
1911 }
1912 }1890 }
19131891
1914 return comp;1892 return comp;
1915}1893}
19161894
1917pub fn destroy(comp: *Compilation) void {1895pub fn destroy(comp: *Compilation) void {
1896 const gpa = comp.gpa;
1897
1918 if (comp.bin_file) |lf| lf.destroy();1898 if (comp.bin_file) |lf| lf.destroy();
1919 if (comp.zcu) |zcu| zcu.deinit();1899 if (comp.zcu) |zcu| zcu.deinit();
1920 comp.cache_use.deinit();1900 comp.cache_use.deinit();
1921 for (comp.work_queues) |work_queue| work_queue.deinit();1901 for (comp.work_queues) |work_queue| work_queue.deinit();
1922 if (!InternPool.single_threaded) comp.codegen_work.queue.deinit();
1923 comp.c_object_work_queue.deinit();1902 comp.c_object_work_queue.deinit();
1924 comp.win32_resource_work_queue.deinit();1903 comp.win32_resource_work_queue.deinit();
1925 comp.astgen_work_queue.deinit();1904 comp.astgen_work_queue.deinit();
1926 comp.embed_file_work_queue.deinit();1905 comp.embed_file_work_queue.deinit();
19271906
1928 const gpa = comp.gpa;1907 comp.windows_libs.deinit(gpa);
1929 comp.system_libs.deinit(gpa);
19301908
1931 {1909 {
1932 var it = comp.crt_files.iterator();1910 var it = comp.crt_files.iterator();
...@@ -2559,12 +2537,7 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -2559,12 +2537,7 @@ fn addNonIncrementalStuffToCacheManifest(
2559 cache_helpers.addModule(&man.hash, comp.root_mod);2537 cache_helpers.addModule(&man.hash, comp.root_mod);
2560 }2538 }
25612539
2562 for (comp.objects) |obj| {2540 try link.hashInputs(man, comp.link_inputs);
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 }
25682541
2569 for (comp.c_object_table.keys()) |key| {2542 for (comp.c_object_table.keys()) |key| {
2570 _ = try man.addFile(key.src.src_path, null);2543 _ = try man.addFile(key.src.src_path, null);
...@@ -2601,7 +2574,7 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -2601,7 +2574,7 @@ fn addNonIncrementalStuffToCacheManifest(
2601 man.hash.add(comp.rc_includes);2574 man.hash.add(comp.rc_includes);
2602 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());2575 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
2603 man.hash.addListOfBytes(comp.framework_dirs);2576 man.hash.addListOfBytes(comp.framework_dirs);
2604 try link.hashAddSystemLibs(man, comp.system_libs);2577 man.hash.addListOfBytes(comp.windows_libs.keys());
26052578
2606 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);2579 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
2607 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);2580 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
...@@ -2620,12 +2593,16 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -2620,12 +2593,16 @@ fn addNonIncrementalStuffToCacheManifest(
2620 man.hash.addOptional(opts.image_base);2593 man.hash.addOptional(opts.image_base);
2621 man.hash.addOptional(opts.gc_sections);2594 man.hash.addOptional(opts.gc_sections);
2622 man.hash.add(opts.emit_relocs);2595 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 }
2624 man.hash.addListOfBytes(opts.rpath_list);2602 man.hash.addListOfBytes(opts.rpath_list);
2625 man.hash.addListOfBytes(opts.symbol_wrap_set.keys());2603 man.hash.addListOfBytes(opts.symbol_wrap_set.keys());
2626 if (comp.config.link_libc) {2604 if (comp.config.link_libc) {
2627 man.hash.add(comp.libc_installation != null);2605 man.hash.add(comp.libc_installation != null);
2628 const target = comp.root_mod.resolved_target.result;
2629 if (comp.libc_installation) |libc_installation| {2606 if (comp.libc_installation) |libc_installation| {
2630 man.hash.addOptionalBytes(libc_installation.crt_dir);2607 man.hash.addOptionalBytes(libc_installation.crt_dir);
2631 if (target.abi == .msvc or target.abi == .itanium) {2608 if (target.abi == .msvc or target.abi == .itanium) {
...@@ -3219,18 +3196,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3219,18 +3196,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3219 }));3196 }));
3220 }3197 }
32213198
3222 for (comp.link_diags.msgs.items) |link_err| {3199 try comp.link_diags.addMessagesToBundle(&bundle);
3223 try bundle.addRootErrorMessage(.{
3224 .msg = try bundle.addString(link_err.msg),
3225 .notes_len = @intCast(link_err.notes.len),
3226 });
3227 const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
3228 for (link_err.notes, 0..) |note, i| {
3229 bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
3230 .msg = try bundle.addString(note.msg),
3231 }));
3232 }
3233 }
32343200
3235 if (comp.zcu) |zcu| {3201 if (comp.zcu) |zcu| {
3236 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {3202 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
...@@ -3482,6 +3448,9 @@ pub fn performAllTheWork(...@@ -3482,6 +3448,9 @@ pub fn performAllTheWork(
3482 comp: *Compilation,3448 comp: *Compilation,
3483 main_progress_node: std.Progress.Node,3449 main_progress_node: std.Progress.Node,
3484) JobError!void {3450) JobError!void {
3451 comp.work_queue_progress_node = main_progress_node;
3452 defer comp.work_queue_progress_node = .none;
3453
3485 defer if (comp.zcu) |zcu| {3454 defer if (comp.zcu) |zcu| {
3486 zcu.sema_prog_node.end();3455 zcu.sema_prog_node.end();
3487 zcu.sema_prog_node = std.Progress.Node.none;3456 zcu.sema_prog_node = std.Progress.Node.none;
...@@ -3491,7 +3460,6 @@ pub fn performAllTheWork(...@@ -3491,7 +3460,6 @@ pub fn performAllTheWork(
3491 zcu.generation += 1;3460 zcu.generation += 1;
3492 };3461 };
3493 try comp.performAllTheWorkInner(main_progress_node);3462 try comp.performAllTheWorkInner(main_progress_node);
3494 if (!InternPool.single_threaded) if (comp.codegen_work.job_error) |job_error| return job_error;
3495}3463}
34963464
3497fn performAllTheWorkInner(3465fn performAllTheWorkInner(
...@@ -3506,12 +3474,34 @@ fn performAllTheWorkInner(...@@ -3506,12 +3474,34 @@ fn performAllTheWorkInner(
3506 var work_queue_wait_group: WaitGroup = .{};3474 var work_queue_wait_group: WaitGroup = .{};
3507 defer work_queue_wait_group.wait();3475 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
3509 if (comp.docs_emit != null) {3484 if (comp.docs_emit != null) {
3510 dev.check(.docs_emit);3485 dev.check(.docs_emit);
3511 comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp});3486 comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp});
3512 work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node });3487 work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node });
3513 }3488 }
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
3515 {3505 {
3516 const astgen_frame = tracy.namedFrame("astgen");3506 const astgen_frame = tracy.namedFrame("astgen");
3517 defer astgen_frame.end();3507 defer astgen_frame.end();
...@@ -3574,22 +3564,18 @@ fn performAllTheWorkInner(...@@ -3574,22 +3564,18 @@ fn performAllTheWorkInner(
3574 }3564 }
35753565
3576 while (comp.c_object_work_queue.readItem()) |c_object| {3566 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, .{
3578 comp, c_object, main_progress_node,3568 comp, c_object, main_progress_node,
3579 });3569 });
3580 }3570 }
35813571
3582 while (comp.win32_resource_work_queue.readItem()) |win32_resource| {3572 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, .{
3584 comp, win32_resource, main_progress_node,3574 comp, win32_resource, main_progress_node,
3585 });3575 });
3586 }3576 }
3587 }3577 }
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
3593 if (comp.zcu) |zcu| {3579 if (comp.zcu) |zcu| {
3594 const pt: Zcu.PerThread = .{ .zcu = zcu, .tid = .main };3580 const pt: Zcu.PerThread = .{ .zcu = zcu, .tid = .main };
3595 if (comp.incremental) {3581 if (comp.incremental) {
...@@ -3604,18 +3590,12 @@ fn performAllTheWorkInner(...@@ -3604,18 +3590,12 @@ fn performAllTheWorkInner(
3604 zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);3590 zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);
3605 }3591 }
36063592
3607 if (!InternPool.single_threaded) {3593 if (!comp.separateCodegenThreadOk()) {
3608 comp.codegen_work.done = false; // may be `true` from a prior update3594 // Waits until all input files have been parsed.
3609 comp.thread_pool.spawnWgId(&work_queue_wait_group, codegenThread, .{comp});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", .{});
3610 }3598 }
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
3620 work: while (true) {3600 work: while (true) {
3621 for (&comp.work_queues) |*work_queue| if (work_queue.readItem()) |job| {3601 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...@@ -3659,16 +3639,14 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
3659 }3639 }
3660 }3640 }
3661 assert(nav.status == .resolved);3641 assert(nav.status == .resolved);
3662 try comp.queueCodegenJob(tid, .{ .nav = nav_index });3642 comp.dispatchCodegenTask(tid, .{ .codegen_nav = nav_index });
3663 },3643 },
3664 .codegen_func => |func| {3644 .codegen_func => |func| {
3665 // This call takes ownership of `func.air`.3645 comp.dispatchCodegenTask(tid, .{ .codegen_func = func });
3666 try comp.queueCodegenJob(tid, .{ .func = .{3646 },
3667 .func = func.func,3647 .codegen_type => |ty| {
3668 .air = func.air,3648 comp.dispatchCodegenTask(tid, .{ .codegen_type = ty });
3669 } });
3670 },3649 },
3671 .codegen_type => |ty| try comp.queueCodegenJob(tid, .{ .type = ty }),
3672 .analyze_func => |func| {3650 .analyze_func => |func| {
3673 const named_frame = tracy.namedFrame("analyze_func");3651 const named_frame = tracy.namedFrame("analyze_func");
3674 defer named_frame.end();3652 defer named_frame.end();
...@@ -3715,31 +3693,6 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre...@@ -3715,31 +3693,6 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
3715 error.AnalysisFail => return,3693 error.AnalysisFail => return,
3716 };3694 };
3717 },3695 },
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 },
3743 .analyze_mod => |mod| {3696 .analyze_mod => |mod| {
3744 const named_frame = tracy.namedFrame("analyze_mod");3697 const named_frame = tracy.namedFrame("analyze_mod");
3745 defer named_frame.end();3698 defer named_frame.end();
...@@ -3804,7 +3757,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre...@@ -3804,7 +3757,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
3804 const named_frame = tracy.namedFrame("windows_import_lib");3757 const named_frame = tracy.namedFrame("windows_import_lib");
3805 defer named_frame.end();3758 defer named_frame.end();
38063759
3807 const link_lib = comp.system_libs.keys()[index];3760 const link_lib = comp.windows_libs.keys()[index];
3808 mingw.buildImportLib(comp, link_lib) catch |err| {3761 mingw.buildImportLib(comp, link_lib) catch |err| {
3809 // TODO Surface more error details.3762 // TODO Surface more error details.
3810 comp.lockAndSetMiscFailure(3763 comp.lockAndSetMiscFailure(
...@@ -3906,66 +3859,20 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre...@@ -3906,66 +3859,20 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
3906 }3859 }
3907}3860}
39083861
3909fn queueCodegenJob(comp: *Compilation, tid: usize, codegen_job: CodegenJob) !void {3862/// The reason for the double-queue here is that the first queue ensures any
3910 if (InternPool.single_threaded or3863/// resolve_type_fully tasks are complete before this dispatch function is called.
3911 !comp.zcu.?.backendSupportsFeature(.separate_thread))3864fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) void {
3912 return processOneCodegenJob(tid, comp, codegen_job);3865 if (comp.separateCodegenThreadOk()) {
39133866 comp.queueLinkTasks(&.{link_task});
3914 {3867 } else {
3915 comp.codegen_work.mutex.lock();3868 link.doTask(comp, tid, link_task);
3916 defer comp.codegen_work.mutex.unlock();
3917 try comp.codegen_work.queue.writeItem(codegen_job);
3918 }3869 }
3919 comp.codegen_work.cond.signal();
3920}3870}
39213871
3922fn codegenThread(tid: usize, comp: *Compilation) void {3872fn separateCodegenThreadOk(comp: *const Compilation) bool {
3923 comp.codegen_work.mutex.lock();3873 if (InternPool.single_threaded) return false;
3924 defer comp.codegen_work.mutex.unlock();3874 const zcu = comp.zcu orelse return true;
39253875 return zcu.backendSupportsFeature(.separate_thread);
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 }
3969}3876}
39703877
3971fn workerDocsCopy(comp: *Compilation) void {3878fn workerDocsCopy(comp: *Compilation) void {
...@@ -4717,7 +4624,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr...@@ -4717,7 +4624,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
4717 // file and building an object we need to link them together, but with just one it should go4624 // file and building an object we need to link them together, but with just one it should go
4718 // directly to the output file.4625 // directly to the output file.
4719 const direct_o = comp.c_source_files.len == 1 and comp.zcu == null and4626 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);
4721 const o_basename_noext = if (direct_o)4628 const o_basename_noext = if (direct_o)
4722 comp.root_name4629 comp.root_name
4723 else4630 else
...@@ -4956,7 +4863,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr...@@ -4956,7 +4863,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
4956 // the contents were the same, we hit the cache but the manifest is dirty and we need to update4863 // the contents were the same, we hit the cache but the manifest is dirty and we need to update
4957 // it to prevent doing a full file content comparison the next time around.4864 // it to prevent doing a full file content comparison the next time around.
4958 man.writeManifest() catch |err| {4865 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 });
4960 };4869 };
4961 }4870 }
49624871
...@@ -4971,6 +4880,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr...@@ -4971,6 +4880,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
4971 .lock = man.toOwnedLock(),4880 .lock = man.toOwnedLock(),
4972 },4881 },
4973 };4882 };
4883
4884 comp.queueLinkTasks(&.{.{ .load_object = c_object.status.success.object_path }});
4974}4885}
49754886
4976fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void {4887fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void {
...@@ -6075,8 +5986,8 @@ test "classifyFileExt" {...@@ -6075,8 +5986,8 @@ test "classifyFileExt" {
6075 try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));5986 try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));
6076}5987}
60775988
6078pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Path {5989fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Path {
6079 return (try crtFilePath(comp, basename)) orelse {5990 return (try crtFilePath(&comp.crt_files, basename)) orelse {
6080 const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable;5991 const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable;
6081 const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir;5992 const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir;
6082 const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });5993 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...@@ -6089,40 +6000,11 @@ pub fn crtFileAsString(comp: *Compilation, arena: Allocator, basename: []const u
6089 return path.toString(arena);6000 return path.toString(arena);
6090}6001}
60916002
6092pub fn crtFilePath(comp: *Compilation, basename: []const u8) Allocator.Error!?Path {6003fn crtFilePath(crt_files: *std.StringHashMapUnmanaged(CrtFile), basename: []const u8) Allocator.Error!?Path {
6093 const crt_file = comp.crt_files.get(basename) orelse return null;6004 const crt_file = crt_files.get(basename) orelse return null;
6094 return crt_file.full_object_path;6005 return crt_file.full_object_path;
6095}6006}
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
6126fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {6008fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
6127 const is_exe_or_dyn_lib = switch (comp.config.output_mode) {6009 const is_exe_or_dyn_lib = switch (comp.config.output_mode) {
6128 .Obj => false,6010 .Obj => false,
...@@ -6133,7 +6015,7 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {...@@ -6133,7 +6015,7 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
6133 return is_exe_or_dyn_lib and comp.config.link_libunwind and ofmt != .c;6015 return is_exe_or_dyn_lib and comp.config.link_libunwind and ofmt != .c;
6134}6016}
61356017
6136fn setAllocFailure(comp: *Compilation) void {6018pub fn setAllocFailure(comp: *Compilation) void {
6137 @branchHint(.cold);6019 @branchHint(.cold);
6138 log.debug("memory allocation failure", .{});6020 log.debug("memory allocation failure", .{});
6139 comp.alloc_failure_occurred = true;6021 comp.alloc_failure_occurred = true;
...@@ -6370,9 +6252,11 @@ fn buildOutputFromZig(...@@ -6370,9 +6252,11 @@ fn buildOutputFromZig(
63706252
6371 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);6253 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
63726254
6373 // Under incremental compilation, `out` may already be populated from a prior update.6255 const crt_file = try sub_compilation.toCrtFile();
6374 assert(out.* == null or comp.incremental);6256 assert(out.* == null);
6375 out.* = try sub_compilation.toCrtFile();6257 out.* = crt_file;
6258
6259 comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
6376}6260}
63776261
6378pub fn build_crt_file(6262pub fn build_crt_file(
...@@ -6479,8 +6363,33 @@ pub fn build_crt_file(...@@ -6479,8 +6363,33 @@ pub fn build_crt_file(
64796363
6480 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);6364 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
64816365
6482 try comp.crt_files.ensureUnusedCapacity(gpa, 1);6366 const crt_file = try sub_compilation.toCrtFile();
6483 comp.crt_files.putAssumeCapacityNoClobber(basename, 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 }
6484}6393}
64856394
6486pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile {6395pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile {
...@@ -6498,21 +6407,31 @@ pub fn getCrtPaths(...@@ -6498,21 +6407,31 @@ pub fn getCrtPaths(
6498 arena: Allocator,6407 arena: Allocator,
6499) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {6408) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {
6500 const target = comp.root_mod.resolved_target.result;6409 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 {
6501 const basenames = LibCInstallation.CrtBasenames.get(.{6420 const basenames = LibCInstallation.CrtBasenames.get(.{
6502 .target = target,6421 .target = target,
6503 .link_libc = comp.config.link_libc,6422 .link_libc = config.link_libc,
6504 .output_mode = comp.config.output_mode,6423 .output_mode = config.output_mode,
6505 .link_mode = comp.config.link_mode,6424 .link_mode = config.link_mode,
6506 .pie = comp.config.pie,6425 .pie = config.pie,
6507 });6426 });
6508 if (comp.libc_installation) |lci| return lci.resolveCrtPaths(arena, basenames, target);6427 if (libc_installation) |lci| return lci.resolveCrtPaths(arena, basenames, target);
65096428
6510 return .{6429 return .{
6511 .crt0 = if (basenames.crt0) |basename| try comp.crtFilePath(basename) else null,6430 .crt0 = if (basenames.crt0) |basename| try crtFilePath(crt_files, basename) else null,
6512 .crti = if (basenames.crti) |basename| try comp.crtFilePath(basename) else null,6431 .crti = if (basenames.crti) |basename| try crtFilePath(crt_files, basename) else null,
6513 .crtbegin = if (basenames.crtbegin) |basename| try comp.crtFilePath(basename) else null,6432 .crtbegin = if (basenames.crtbegin) |basename| try crtFilePath(crt_files, basename) else null,
6514 .crtend = if (basenames.crtend) |basename| try comp.crtFilePath(basename) else null,6433 .crtend = if (basenames.crtend) |basename| try crtFilePath(crt_files, basename) else null,
6515 .crtn = if (basenames.crtn) |basename| try comp.crtFilePath(basename) else null,6434 .crtn = if (basenames.crtn) |basename| try crtFilePath(crt_files, basename) else null,
6516 };6435 };
6517}6436}
65186437
...@@ -6522,24 +6441,14 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {...@@ -6522,24 +6441,14 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
6522 // then when we create a sub-Compilation for zig libc, it also tries to6441 // then when we create a sub-Compilation for zig libc, it also tries to
6523 // build kernel32.lib.6442 // build kernel32.lib.
6524 if (comp.skip_linker_dependencies) return;6443 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
6526 // This happens when an `extern "foo"` function is referenced.6447 // This happens when an `extern "foo"` function is referenced.
6527 // If we haven't seen this library yet and we're targeting Windows, we need6448 // If we haven't seen this library yet and we're targeting Windows, we need
6528 // to queue up a work item to produce the DLL import library for this.6449 // 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);6450 const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name);
6530 if (!gop.found_existing) {6451 if (!gop.found_existing) try comp.queueJob(.{ .windows_import_lib = comp.windows_libs.count() - 1 });
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 }
6543}6452}
65446453
6545/// This decides the optimization mode for all zig-provided libraries, including6454/// This decides the optimization mode for all zig-provided libraries, including
src/Sema.zig+8-1
...@@ -2899,6 +2899,7 @@ fn zirStructDecl(...@@ -2899,6 +2899,7 @@ fn zirStructDecl(
2899 codegen_type: {2899 codegen_type: {
2900 if (zcu.comp.config.use_llvm) break :codegen_type;2900 if (zcu.comp.config.use_llvm) break :codegen_type;
2901 if (block.ownerModule().strip) break :codegen_type;2901 if (block.ownerModule().strip) break :codegen_type;
2902 // This job depends on any resolve_type_fully jobs queued up before it.
2902 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });2903 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
2903 }2904 }
2904 try sema.declareDependency(.{ .interned = wip_ty.index });2905 try sema.declareDependency(.{ .interned = wip_ty.index });
...@@ -3149,6 +3150,7 @@ fn zirEnumDecl(...@@ -3149,6 +3150,7 @@ fn zirEnumDecl(
3149 codegen_type: {3150 codegen_type: {
3150 if (zcu.comp.config.use_llvm) break :codegen_type;3151 if (zcu.comp.config.use_llvm) break :codegen_type;
3151 if (block.ownerModule().strip) break :codegen_type;3152 if (block.ownerModule().strip) break :codegen_type;
3153 // This job depends on any resolve_type_fully jobs queued up before it.
3152 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });3154 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
3153 }3155 }
3154 return Air.internedToRef(wip_ty.index);3156 return Air.internedToRef(wip_ty.index);
...@@ -3272,6 +3274,7 @@ fn zirUnionDecl(...@@ -3272,6 +3274,7 @@ fn zirUnionDecl(
3272 codegen_type: {3274 codegen_type: {
3273 if (zcu.comp.config.use_llvm) break :codegen_type;3275 if (zcu.comp.config.use_llvm) break :codegen_type;
3274 if (block.ownerModule().strip) break :codegen_type;3276 if (block.ownerModule().strip) break :codegen_type;
3277 // This job depends on any resolve_type_fully jobs queued up before it.
3275 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });3278 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
3276 }3279 }
3277 try sema.declareDependency(.{ .interned = wip_ty.index });3280 try sema.declareDependency(.{ .interned = wip_ty.index });
...@@ -3357,6 +3360,7 @@ fn zirOpaqueDecl(...@@ -3357,6 +3360,7 @@ fn zirOpaqueDecl(
3357 codegen_type: {3360 codegen_type: {
3358 if (zcu.comp.config.use_llvm) break :codegen_type;3361 if (zcu.comp.config.use_llvm) break :codegen_type;
3359 if (block.ownerModule().strip) break :codegen_type;3362 if (block.ownerModule().strip) break :codegen_type;
3363 // This job depends on any resolve_type_fully jobs queued up before it.
3360 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });3364 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
3361 }3365 }
3362 try sema.addTypeReferenceEntry(src, wip_ty.index);3366 try sema.addTypeReferenceEntry(src, wip_ty.index);
...@@ -9595,7 +9599,7 @@ fn resolveGenericBody(...@@ -9595,7 +9599,7 @@ fn resolveGenericBody(
9595}9599}
95969600
9597/// Given a library name, examines if the library name should end up in9601/// Given a library name, examines if the library name should end up in
9598/// `link.File.Options.system_libs` table (for example, libc is always9602/// `link.File.Options.windows_libs` table (for example, libc is always
9599/// specified via dedicated flag `link_libc` instead),9603/// specified via dedicated flag `link_libc` instead),
9600/// and puts it there if it doesn't exist.9604/// and puts it there if it doesn't exist.
9601/// It also dupes the library name which can then be saved as part of the9605/// It also dupes the library name which can then be saved as part of the
...@@ -22456,6 +22460,7 @@ fn reifyEnum(...@@ -22456,6 +22460,7 @@ fn reifyEnum(
22456 codegen_type: {22460 codegen_type: {
22457 if (zcu.comp.config.use_llvm) break :codegen_type;22461 if (zcu.comp.config.use_llvm) break :codegen_type;
22458 if (block.ownerModule().strip) break :codegen_type;22462 if (block.ownerModule().strip) break :codegen_type;
22463 // This job depends on any resolve_type_fully jobs queued up before it.
22459 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });22464 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
22460 }22465 }
22461 return Air.internedToRef(wip_ty.index);22466 return Air.internedToRef(wip_ty.index);
...@@ -22713,6 +22718,7 @@ fn reifyUnion(...@@ -22713,6 +22718,7 @@ fn reifyUnion(
22713 codegen_type: {22718 codegen_type: {
22714 if (zcu.comp.config.use_llvm) break :codegen_type;22719 if (zcu.comp.config.use_llvm) break :codegen_type;
22715 if (block.ownerModule().strip) break :codegen_type;22720 if (block.ownerModule().strip) break :codegen_type;
22721 // This job depends on any resolve_type_fully jobs queued up before it.
22716 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });22722 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
22717 }22723 }
22718 try sema.declareDependency(.{ .interned = wip_ty.index });22724 try sema.declareDependency(.{ .interned = wip_ty.index });
...@@ -22997,6 +23003,7 @@ fn reifyStruct(...@@ -22997,6 +23003,7 @@ fn reifyStruct(
22997 codegen_type: {23003 codegen_type: {
22998 if (zcu.comp.config.use_llvm) break :codegen_type;23004 if (zcu.comp.config.use_llvm) break :codegen_type;
22999 if (block.ownerModule().strip) break :codegen_type;23005 if (block.ownerModule().strip) break :codegen_type;
23006 // This job depends on any resolve_type_fully jobs queued up before it.
23000 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });23007 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
23001 }23008 }
23002 try sema.declareDependency(.{ .interned = wip_ty.index });23009 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(...@@ -845,6 +845,7 @@ fn ensureFuncBodyAnalyzedInner(
845 return .{ .ies_outdated = ies_outdated };845 return .{ .ies_outdated = ies_outdated };
846 }846 }
847847
848 // This job depends on any resolve_type_fully jobs queued up before it.
848 try comp.queueJob(.{ .codegen_func = .{849 try comp.queueJob(.{ .codegen_func = .{
849 .func = func_index,850 .func = func_index,
850 .air = air,851 .air = air,
...@@ -1016,6 +1017,7 @@ fn createFileRootStruct(...@@ -1016,6 +1017,7 @@ fn createFileRootStruct(
1016 codegen_type: {1017 codegen_type: {
1017 if (zcu.comp.config.use_llvm) break :codegen_type;1018 if (zcu.comp.config.use_llvm) break :codegen_type;
1018 if (file.mod.strip) break :codegen_type;1019 if (file.mod.strip) break :codegen_type;
1020 // This job depends on any resolve_type_fully jobs queued up before it.
1019 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });1021 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
1020 }1022 }
1021 zcu.setFileRootType(file_index, wip_ty.index);1023 zcu.setFileRootType(file_index, wip_ty.index);
...@@ -1362,6 +1364,7 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {...@@ -1362,6 +1364,7 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
1362 if (file.mod.strip) break :queue_codegen;1364 if (file.mod.strip) break :queue_codegen;
1363 }1365 }
13641366
1367 // This job depends on any resolve_type_fully jobs queued up before it.
1365 try zcu.comp.queueJob(.{ .codegen_nav = nav_index });1368 try zcu.comp.queueJob(.{ .codegen_nav = nav_index });
1366 }1369 }
13671370
...@@ -2593,7 +2596,7 @@ pub fn populateTestFunctions(...@@ -2593,7 +2596,7 @@ pub fn populateTestFunctions(
2593 }2596 }
2594}2597}
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 {
2597 const zcu = pt.zcu;2600 const zcu = pt.zcu;
2598 const comp = zcu.comp;2601 const comp = zcu.comp;
2599 const ip = &zcu.intern_pool;2602 const ip = &zcu.intern_pool;
...@@ -3163,6 +3166,7 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator....@@ -3163,6 +3166,7 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator.
3163pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!InternPool.Index {3166pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!InternPool.Index {
3164 const result = try pt.zcu.intern_pool.getExtern(pt.zcu.gpa, pt.tid, key);3167 const result = try pt.zcu.intern_pool.getExtern(pt.zcu.gpa, pt.tid, key);
3165 if (result.new_nav.unwrap()) |nav| {3168 if (result.new_nav.unwrap()) |nav| {
3169 // This job depends on any resolve_type_fully jobs queued up before it.
3166 try pt.zcu.comp.queueJob(.{ .codegen_nav = nav });3170 try pt.zcu.comp.queueJob(.{ .codegen_nav = nav });
3167 }3171 }
3168 return result.index;3172 return result.index;
src/arch/riscv64/CodeGen.zig+2-2
...@@ -133,7 +133,7 @@ const Owner = union(enum) {...@@ -133,7 +133,7 @@ const Owner = union(enum) {
133 switch (owner) {133 switch (owner) {
134 .nav_index => |nav_index| {134 .nav_index => |nav_index| {
135 const elf_file = func.bin_file.cast(.elf).?;135 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);
137 },137 },
138 .lazy_sym => |lazy_sym| {138 .lazy_sym => |lazy_sym| {
139 const elf_file = func.bin_file.cast(.elf).?;139 const elf_file = func.bin_file.cast(.elf).?;
...@@ -5002,7 +5002,7 @@ fn genCall(...@@ -5002,7 +5002,7 @@ fn genCall(
5002 .func => |func_val| {5002 .func => |func_val| {
5003 if (func.bin_file.cast(.elf)) |elf_file| {5003 if (func.bin_file.cast(.elf)) |elf_file| {
5004 const zo = elf_file.zigObjectPtr().?;5004 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
5007 if (func.mod.pic) {5007 if (func.mod.pic) {
5008 return func.fail("TODO: genCall pic", .{});5008 return func.fail("TODO: genCall pic", .{});
src/arch/x86_64/CodeGen.zig+2-2
...@@ -126,7 +126,7 @@ const Owner = union(enum) {...@@ -126,7 +126,7 @@ const Owner = union(enum) {
126 const pt = ctx.pt;126 const pt = ctx.pt;
127 switch (owner) {127 switch (owner) {
128 .nav_index => |nav_index| if (ctx.bin_file.cast(.elf)) |elf_file| {128 .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);
130 } else if (ctx.bin_file.cast(.macho)) |macho_file| {130 } else if (ctx.bin_file.cast(.macho)) |macho_file| {
131 return macho_file.getZigObject().?.getOrCreateMetadataForNav(macho_file, nav_index);131 return macho_file.getZigObject().?.getOrCreateMetadataForNav(macho_file, nav_index);
132 } else if (ctx.bin_file.cast(.coff)) |coff_file| {132 } else if (ctx.bin_file.cast(.coff)) |coff_file| {
...@@ -12605,7 +12605,7 @@ fn genCall(self: *Self, info: union(enum) {...@@ -12605,7 +12605,7 @@ fn genCall(self: *Self, info: union(enum) {
12605 .func => |func| {12605 .func => |func| {
12606 if (self.bin_file.cast(.elf)) |elf_file| {12606 if (self.bin_file.cast(.elf)) |elf_file| {
12607 const zo = elf_file.zigObjectPtr().?;12607 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);
12609 try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = sym_index }));12609 try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = sym_index }));
12610 } else if (self.bin_file.cast(.coff)) |coff_file| {12610 } else if (self.bin_file.cast(.coff)) |coff_file| {
12611 const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav);12611 const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav);
src/codegen.zig+1-1
...@@ -866,7 +866,7 @@ fn genNavRef(...@@ -866,7 +866,7 @@ fn genNavRef(
866 zo.symbol(sym_index).flags.is_extern_ptr = true;866 zo.symbol(sym_index).flags.is_extern_ptr = true;
867 return .{ .mcv = .{ .lea_symbol = sym_index } };867 return .{ .mcv = .{ .lea_symbol = sym_index } };
868 }868 }
869 const sym_index = try zo.getOrCreateMetadataForNav(elf_file, nav_index);869 const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index);
870 if (!single_threaded and is_threadlocal) {870 if (!single_threaded and is_threadlocal) {
871 return .{ .mcv = .{ .load_tlv = sym_index } };871 return .{ .mcv = .{ .load_tlv = sym_index } };
872 }872 }
src/glibc.zig+53-18
...@@ -6,12 +6,14 @@ const fs = std.fs;...@@ -6,12 +6,14 @@ const fs = std.fs;
6const path = fs.path;6const path = fs.path;
7const assert = std.debug.assert;7const assert = std.debug.assert;
8const Version = std.SemanticVersion;8const Version = std.SemanticVersion;
9const Path = std.Build.Cache.Path;
910
10const Compilation = @import("Compilation.zig");11const Compilation = @import("Compilation.zig");
11const build_options = @import("build_options");12const build_options = @import("build_options");
12const trace = @import("tracy.zig").trace;13const trace = @import("tracy.zig").trace;
13const Cache = std.Build.Cache;14const Cache = std.Build.Cache;
14const Module = @import("Package/Module.zig");15const Module = @import("Package/Module.zig");
16const link = @import("link.zig");
1517
16pub const Lib = struct {18pub const Lib = struct {
17 name: []const u8,19 name: []const u8,
...@@ -717,11 +719,11 @@ fn lib_path(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const...@@ -717,11 +719,11 @@ fn lib_path(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const
717719
718pub const BuiltSharedObjects = struct {720pub const BuiltSharedObjects = struct {
719 lock: Cache.Lock,721 lock: Cache.Lock,
720 dir_path: []u8,722 dir_path: Path,
721723
722 pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator) void {724 pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator) void {
723 self.lock.release();725 self.lock.release();
724 gpa.free(self.dir_path);726 gpa.free(self.dir_path.sub_path);
725 self.* = undefined;727 self.* = undefined;
726 }728 }
727};729};
...@@ -742,7 +744,9 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi...@@ -742,7 +744,9 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
742 return error.ZigCompilerNotBuiltWithLLVMExtensions;744 return error.ZigCompilerNotBuiltWithLLVMExtensions;
743 }745 }
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);
746 defer arena_allocator.deinit();750 defer arena_allocator.deinit();
747 const arena = arena_allocator.allocator();751 const arena = arena_allocator.allocator();
748752
...@@ -751,7 +755,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi...@@ -751,7 +755,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
751755
752 // Use the global cache directory.756 // Use the global cache directory.
753 var cache: Cache = .{757 var cache: Cache = .{
754 .gpa = comp.gpa,758 .gpa = gpa,
755 .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}),759 .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}),
756 };760 };
757 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });761 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
...@@ -772,12 +776,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi...@@ -772,12 +776,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
772 if (try man.hit()) {776 if (try man.hit()) {
773 const digest = man.final();777 const digest = man.final();
774778
775 assert(comp.glibc_so_files == null);779 return queueSharedObjects(comp, .{
776 comp.glibc_so_files = BuiltSharedObjects{
777 .lock = man.toOwnedLock(),780 .lock = man.toOwnedLock(),
778 .dir_path = try comp.global_cache_directory.join(comp.gpa, &.{ "o", &digest }),781 .dir_path = .{
779 };782 .root_dir = comp.global_cache_directory,
780 return;783 .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest),
784 },
785 });
781 }786 }
782787
783 const digest = man.final();788 const digest = man.final();
...@@ -790,8 +795,8 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi...@@ -790,8 +795,8 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
790 defer o_directory.handle.close();795 defer o_directory.handle.close();
791796
792 const abilists_contents = man.files.keys()[abilists_index].contents.?;797 const abilists_contents = man.files.keys()[abilists_index].contents.?;
793 const metadata = try loadMetaData(comp.gpa, abilists_contents);798 const metadata = try loadMetaData(gpa, abilists_contents);
794 defer metadata.destroy(comp.gpa);799 defer metadata.destroy(gpa);
795800
796 const target_targ_index = for (metadata.all_targets, 0..) |targ, i| {801 const target_targ_index = for (metadata.all_targets, 0..) |targ, i| {
797 if (targ.arch == target.cpu.arch and802 if (targ.arch == target.cpu.arch and
...@@ -835,7 +840,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi...@@ -835,7 +840,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
835 map_contents.deinit(); // The most recent allocation of an arena can be freed :)840 map_contents.deinit(); // The most recent allocation of an arena can be freed :)
836 }841 }
837842
838 var stubs_asm = std.ArrayList(u8).init(comp.gpa);843 var stubs_asm = std.ArrayList(u8).init(gpa);
839 defer stubs_asm.deinit();844 defer stubs_asm.deinit();
840845
841 for (libs, 0..) |lib, lib_i| {846 for (libs, 0..) |lib, lib_i| {
...@@ -1195,7 +1200,6 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi...@@ -1195,7 +1200,6 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
1195 var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.1200 var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
1196 const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;1201 const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
1197 try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items });1202 try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items });
1198
1199 try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib, prog_node);1203 try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib, prog_node);
1200 }1204 }
12011205
...@@ -1203,14 +1207,45 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi...@@ -1203,14 +1207,45 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
1203 log.warn("failed to write cache manifest for glibc stubs: {s}", .{@errorName(err)});1207 log.warn("failed to write cache manifest for glibc stubs: {s}", .{@errorName(err)});
1204 };1208 };
12051209
1206 assert(comp.glibc_so_files == null);1210 return queueSharedObjects(comp, .{
1207 comp.glibc_so_files = BuiltSharedObjects{
1208 .lock = man.toOwnedLock(),1211 .lock = man.toOwnedLock(),
1209 .dir_path = try comp.global_cache_directory.join(comp.gpa, &.{ "o", &digest }),1212 .dir_path = .{
1210 };1213 .root_dir = comp.global_cache_directory,
1214 .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest),
1215 },
1216 });
1211}1217}
12121218
1213// zig fmt: on1219fn 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
1215fn buildSharedLib(1250fn buildSharedLib(
1216 comp: *Compilation,1251 comp: *Compilation,
src/libcxx.zig+6-2
...@@ -355,7 +355,9 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: std.Progress.Node) BuildError!...@@ -355,7 +355,9 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: std.Progress.Node) BuildError!
355 };355 };
356356
357 assert(comp.libcxx_static_lib == null);357 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);
359}361}
360362
361pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {363pub 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...@@ -584,7 +586,9 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
584 };586 };
585587
586 assert(comp.libcxxabi_static_lib == null);588 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);
588}592}
589593
590pub fn hardeningModeFlag(optimize_mode: std.builtin.OptimizeMode) []const u8 {594pub 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...@@ -342,8 +342,10 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
342 },342 },
343 };343 };
344344
345 const crt_file = try sub_compilation.toCrtFile();
346 comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
345 assert(comp.tsan_lib == null);347 assert(comp.tsan_lib == null);
346 comp.tsan_lib = try sub_compilation.toCrtFile();348 comp.tsan_lib = crt_file;
347}349}
348350
349const tsan_sources = [_][]const u8{351const tsan_sources = [_][]const u8{
src/libunwind.zig+3-1
...@@ -199,8 +199,10 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr...@@ -199,8 +199,10 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
199 },199 },
200 };200 };
201201
202 const crt_file = try sub_compilation.toCrtFile();
203 comp.queueLinkTaskMode(crt_file.full_object_path, output_mode);
202 assert(comp.libunwind_static_lib == null);204 assert(comp.libunwind_static_lib == null);
203 comp.libunwind_static_lib = try sub_compilation.toCrtFile();205 comp.libunwind_static_lib = crt_file;
204}206}
205207
206const unwind_src_list = [_][]const u8{208const unwind_src_list = [_][]const u8{
src/link.zig+1000-44
...@@ -12,6 +12,7 @@ const Air = @import("Air.zig");...@@ -12,6 +12,7 @@ const Air = @import("Air.zig");
12const Allocator = std.mem.Allocator;12const Allocator = std.mem.Allocator;
13const Cache = std.Build.Cache;13const Cache = std.Build.Cache;
14const Path = std.Build.Cache.Path;14const Path = std.Build.Cache.Path;
15const Directory = std.Build.Cache.Directory;
15const Compilation = @import("Compilation.zig");16const Compilation = @import("Compilation.zig");
16const LibCInstallation = std.zig.LibCInstallation;17const LibCInstallation = std.zig.LibCInstallation;
17const Liveness = @import("Liveness.zig");18const Liveness = @import("Liveness.zig");
...@@ -23,19 +24,10 @@ const LlvmObject = @import("codegen/llvm.zig").Object;...@@ -23,19 +24,10 @@ const LlvmObject = @import("codegen/llvm.zig").Object;
23const lldMain = @import("main.zig").lldMain;24const lldMain = @import("main.zig").lldMain;
24const Package = @import("Package.zig");25const Package = @import("Package.zig");
25const dev = @import("dev.zig");26const 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`.30pub const LdScript = @import("link/LdScript.zig");
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};
3931
40pub const Diags = struct {32pub const Diags = struct {
41 /// Stored here so that function definitions can distinguish between33 /// Stored here so that function definitions can distinguish between
...@@ -336,20 +328,22 @@ pub const Diags = struct {...@@ -336,20 +328,22 @@ pub const Diags = struct {
336 log.debug("memory allocation failure", .{});328 log.debug("memory allocation failure", .{});
337 diags.flags.alloc_failure_occurred = true;329 diags.flags.alloc_failure_occurred = true;
338 }330 }
339};
340331
341pub fn hashAddSystemLibs(332 pub fn addMessagesToBundle(diags: *const Diags, bundle: *std.zig.ErrorBundle.Wip) Allocator.Error!void {
342 man: *Cache.Manifest,333 for (diags.msgs.items) |link_err| {
343 hm: std.StringArrayHashMapUnmanaged(SystemLib),334 try bundle.addRootErrorMessage(.{
344) !void {335 .msg = try bundle.addString(link_err.msg),
345 const keys = hm.keys();336 .notes_len = @intCast(link_err.notes.len),
346 man.hash.addListOfBytes(keys);337 });
347 for (hm.values()) |value| {338 const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
348 man.hash.add(value.needed);339 for (link_err.notes, 0..) |note, i| {
349 man.hash.add(value.weak);340 bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
350 if (value.path) |p| _ = try man.addFilePath(p, null);341 .msg = try bundle.addString(note.msg),
342 }));
343 }
344 }
351 }345 }
352}346};
353347
354pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version;348pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version;
355349
...@@ -438,7 +432,7 @@ pub const File = struct {...@@ -438,7 +432,7 @@ pub const File = struct {
438 compatibility_version: ?std.SemanticVersion,432 compatibility_version: ?std.SemanticVersion,
439433
440 // TODO: remove this. libraries are resolved by the frontend.434 // TODO: remove this. libraries are resolved by the frontend.
441 lib_dirs: []const []const u8,435 lib_directories: []const Directory,
442 framework_dirs: []const []const u8,436 framework_dirs: []const []const u8,
443 rpath_list: []const []const u8,437 rpath_list: []const []const u8,
444438
...@@ -1003,6 +997,102 @@ pub const File = struct {...@@ -1003,6 +997,102 @@ pub const File = struct {
1003 }997 }
1004 }998 }
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
1006 pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void {1096 pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void {
1007 dev.check(.lld_linker);1097 dev.check(.lld_linker);
10081098
...@@ -1010,7 +1100,6 @@ pub const File = struct {...@@ -1010,7 +1100,6 @@ pub const File = struct {
1010 defer tracy.end();1100 defer tracy.end();
10111101
1012 const comp = base.comp;1102 const comp = base.comp;
1013 const gpa = comp.gpa;
10141103
1015 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.1104 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
1016 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});1105 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
...@@ -1042,7 +1131,7 @@ pub const File = struct {...@@ -1042,7 +1131,7 @@ pub const File = struct {
1042 var man: Cache.Manifest = undefined;1131 var man: Cache.Manifest = undefined;
1043 defer if (!base.disable_lld_caching) man.deinit();1132 defer if (!base.disable_lld_caching) man.deinit();
10441133
1045 const objects = comp.objects;1134 const link_inputs = comp.link_inputs;
10461135
1047 var digest: [Cache.hex_digest_len]u8 = undefined;1136 var digest: [Cache.hex_digest_len]u8 = undefined;
10481137
...@@ -1052,11 +1141,8 @@ pub const File = struct {...@@ -1052,11 +1141,8 @@ pub const File = struct {
1052 // We are about to obtain this lock, so here we give other processes a chance first.1141 // We are about to obtain this lock, so here we give other processes a chance first.
1053 base.releaseLock();1142 base.releaseLock();
10541143
1055 for (objects) |obj| {1144 try hashInputs(&man, link_inputs);
1056 _ = try man.addFilePath(obj.path, null);1145
1057 man.hash.add(obj.must_link);
1058 man.hash.add(obj.loption);
1059 }
1060 for (comp.c_object_table.keys()) |key| {1146 for (comp.c_object_table.keys()) |key| {
1061 _ = try man.addFilePath(key.status.success.object_path, null);1147 _ = try man.addFilePath(key.status.success.object_path, null);
1062 }1148 }
...@@ -1092,26 +1178,24 @@ pub const File = struct {...@@ -1092,26 +1178,24 @@ pub const File = struct {
1092 };1178 };
1093 }1179 }
10941180
1095 const win32_resource_table_len = comp.win32_resource_table.count();1181 var object_files: std.ArrayListUnmanaged([*:0]const u8) = .empty;
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();
10991182
1100 for (objects) |obj| {1183 try object_files.ensureUnusedCapacity(arena, link_inputs.len);
1101 object_files.appendAssumeCapacity(try obj.path.toStringZ(arena));1184 for (link_inputs) |input| {
1185 object_files.appendAssumeCapacity(try input.path().?.toStringZ(arena));
1102 }1186 }
1187
1188 try object_files.ensureUnusedCapacity(arena, comp.c_object_table.count() +
1189 comp.win32_resource_table.count() + 2);
1190
1103 for (comp.c_object_table.keys()) |key| {1191 for (comp.c_object_table.keys()) |key| {
1104 object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena));1192 object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena));
1105 }1193 }
1106 for (comp.win32_resource_table.keys()) |key| {1194 for (comp.win32_resource_table.keys()) |key| {
1107 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));1195 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));
1108 }1196 }
1109 if (zcu_obj_path) |p| {1197 if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
1110 object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));1198 if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
1111 }
1112 if (compiler_rt_path) |p| {
1113 object_files.appendAssumeCapacity(try p.toStringZ(arena));
1114 }
11151199
1116 if (comp.verbose_link) {1200 if (comp.verbose_link) {
1117 std.debug.print("ar rcs {s}", .{full_out_path_z});1201 std.debug.print("ar rcs {s}", .{full_out_path_z});
...@@ -1277,6 +1361,195 @@ pub const File = struct {...@@ -1277,6 +1361,195 @@ pub const File = struct {
1277 pub const Dwarf = @import("link/Dwarf.zig");1361 pub const Dwarf = @import("link/Dwarf.zig");
1278};1362};
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
1280pub fn spawnLld(1553pub fn spawnLld(
1281 comp: *Compilation,1554 comp: *Compilation,
1282 arena: Allocator,1555 arena: Allocator,
...@@ -1387,3 +1660,686 @@ pub fn spawnLld(...@@ -1387,3 +1660,686 @@ pub fn spawnLld(
13871660
1388 if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});1661 if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1389}1662}
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,...@@ -16,7 +16,7 @@ dynamicbase: bool,
16/// default or populated together. They should not be separate fields.16/// default or populated together. They should not be separate fields.
17major_subsystem_version: u16,17major_subsystem_version: u16,
18minor_subsystem_version: u16,18minor_subsystem_version: u16,
19lib_dirs: []const []const u8,19lib_directories: []const Directory,
20entry: link.File.OpenOptions.Entry,20entry: link.File.OpenOptions.Entry,
21entry_addr: ?u32,21entry_addr: ?u32,
22module_definition_file: ?[]const u8,22module_definition_file: ?[]const u8,
...@@ -297,7 +297,7 @@ pub fn createEmpty(...@@ -297,7 +297,7 @@ pub fn createEmpty(
297 .dynamicbase = options.dynamicbase,297 .dynamicbase = options.dynamicbase,
298 .major_subsystem_version = options.major_subsystem_version orelse 6,298 .major_subsystem_version = options.major_subsystem_version orelse 6,
299 .minor_subsystem_version = options.minor_subsystem_version orelse 0,299 .minor_subsystem_version = options.minor_subsystem_version orelse 0,
300 .lib_dirs = options.lib_dirs,300 .lib_directories = options.lib_directories,
301 .entry_addr = math.cast(u32, options.entry_addr orelse 0) orelse301 .entry_addr = math.cast(u32, options.entry_addr orelse 0) orelse
302 return error.EntryAddressTooBig,302 return error.EntryAddressTooBig,
303 .module_definition_file = options.module_definition_file,303 .module_definition_file = options.module_definition_file,
...@@ -2727,6 +2727,7 @@ const mem = std.mem;...@@ -2727,6 +2727,7 @@ const mem = std.mem;
27272727
2728const Allocator = std.mem.Allocator;2728const Allocator = std.mem.Allocator;
2729const Path = std.Build.Cache.Path;2729const Path = std.Build.Cache.Path;
2730const Directory = std.Build.Cache.Directory;
27302731
2731const codegen = @import("../codegen.zig");2732const codegen = @import("../codegen.zig");
2732const link = @import("../link.zig");2733const link = @import("../link.zig");
src/link/Coff/lld.zig+32-27
...@@ -8,6 +8,7 @@ const log = std.log.scoped(.link);...@@ -8,6 +8,7 @@ const log = std.log.scoped(.link);
8const mem = std.mem;8const mem = std.mem;
9const Cache = std.Build.Cache;9const Cache = std.Build.Cache;
10const Path = std.Build.Cache.Path;10const Path = std.Build.Cache.Path;
11const Directory = std.Build.Cache.Directory;
1112
12const mingw = @import("../../mingw.zig");13const mingw = @import("../../mingw.zig");
13const link = @import("../../link.zig");14const link = @import("../../link.zig");
...@@ -74,10 +75,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -74,10 +75,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
7475
75 comptime assert(Compilation.link_hash_implementation_version == 14);76 comptime assert(Compilation.link_hash_implementation_version == 14);
7677
77 for (comp.objects) |obj| {78 try link.hashInputs(&man, comp.link_inputs);
78 _ = try man.addFilePath(obj.path, null);
79 man.hash.add(obj.must_link);
80 }
81 for (comp.c_object_table.keys()) |key| {79 for (comp.c_object_table.keys()) |key| {
82 _ = try man.addFilePath(key.status.success.object_path, null);80 _ = try man.addFilePath(key.status.success.object_path, null);
83 }81 }
...@@ -88,7 +86,10 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -88,7 +86,10 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
88 man.hash.addOptionalBytes(entry_name);86 man.hash.addOptionalBytes(entry_name);
89 man.hash.add(self.base.stack_size);87 man.hash.add(self.base.stack_size);
90 man.hash.add(self.image_base);88 man.hash.add(self.image_base);
91 man.hash.addListOfBytes(self.lib_dirs);89 {
90 // TODO remove this, libraries must instead be resolved by the frontend.
91 for (self.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path);
92 }
92 man.hash.add(comp.skip_linker_dependencies);93 man.hash.add(comp.skip_linker_dependencies);
93 if (comp.config.link_libc) {94 if (comp.config.link_libc) {
94 man.hash.add(comp.libc_installation != null);95 man.hash.add(comp.libc_installation != null);
...@@ -100,7 +101,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -100,7 +101,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
100 }101 }
101 }102 }
102 }103 }
103 try link.hashAddSystemLibs(&man, comp.system_libs);104 man.hash.addListOfBytes(comp.windows_libs.keys());
104 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());105 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
105 man.hash.addOptional(self.subsystem);106 man.hash.addOptional(self.subsystem);
106 man.hash.add(comp.config.is_test);107 man.hash.add(comp.config.is_test);
...@@ -148,8 +149,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -148,8 +149,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
148 // here. TODO: think carefully about how we can avoid this redundant operation when doing149 // here. TODO: think carefully about how we can avoid this redundant operation when doing
149 // build-obj. See also the corresponding TODO in linkAsArchive.150 // build-obj. See also the corresponding TODO in linkAsArchive.
150 const the_object_path = blk: {151 const the_object_path = blk: {
151 if (comp.objects.len != 0)152 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
152 break :blk comp.objects[0].path;
153153
154 if (comp.c_object_table.count() != 0)154 if (comp.c_object_table.count() != 0)
155 break :blk comp.c_object_table.keys()[0].status.success.object_path;155 break :blk comp.c_object_table.keys()[0].status.success.object_path;
...@@ -266,18 +266,24 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -266,18 +266,24 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
266 }266 }
267 }267 }
268268
269 for (self.lib_dirs) |lib_dir| {269 for (self.lib_directories) |lib_directory| {
270 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir}));270 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_directory.path orelse "."}));
271 }271 }
272272
273 try argv.ensureUnusedCapacity(comp.objects.len);273 try argv.ensureUnusedCapacity(comp.link_inputs.len);
274 for (comp.objects) |obj| {274 for (comp.link_inputs) |link_input| switch (link_input) {
275 if (obj.must_link) {275 .dso_exact => unreachable, // not applicable to PE/COFF
276 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)}));276 inline .dso, .res => |x| {
277 } else {277 argv.appendAssumeCapacity(try x.path.toString(arena));
278 argv.appendAssumeCapacity(try obj.path.toString(arena));278 },
279 }279 .object, .archive => |obj| {
280 }280 if (obj.must_link) {
281 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)}));
282 } else {
283 argv.appendAssumeCapacity(try obj.path.toString(arena));
284 }
285 },
286 };
281287
282 for (comp.c_object_table.keys()) |key| {288 for (comp.c_object_table.keys()) |key| {
283 try argv.append(try key.status.success.object_path.toString(arena));289 try argv.append(try key.status.success.object_path.toString(arena));
...@@ -484,20 +490,20 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -484,20 +490,20 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
484 if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));490 if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));
485 }491 }
486492
487 try argv.ensureUnusedCapacity(comp.system_libs.count());493 try argv.ensureUnusedCapacity(comp.windows_libs.count());
488 for (comp.system_libs.keys()) |key| {494 for (comp.windows_libs.keys()) |key| {
489 const lib_basename = try allocPrint(arena, "{s}.lib", .{key});495 const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
490 if (comp.crt_files.get(lib_basename)) |crt_file| {496 if (comp.crt_files.get(lib_basename)) |crt_file| {
491 argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena));497 argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena));
492 continue;498 continue;
493 }499 }
494 if (try findLib(arena, lib_basename, self.lib_dirs)) |full_path| {500 if (try findLib(arena, lib_basename, self.lib_directories)) |full_path| {
495 argv.appendAssumeCapacity(full_path);501 argv.appendAssumeCapacity(full_path);
496 continue;502 continue;
497 }503 }
498 if (target.abi.isGnu()) {504 if (target.abi.isGnu()) {
499 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});505 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});
500 if (try findLib(arena, fallback_name, self.lib_dirs)) |full_path| {506 if (try findLib(arena, fallback_name, self.lib_directories)) |full_path| {
501 argv.appendAssumeCapacity(full_path);507 argv.appendAssumeCapacity(full_path);
502 continue;508 continue;
503 }509 }
...@@ -530,14 +536,13 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -530,14 +536,13 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
530 }536 }
531}537}
532538
533fn findLib(arena: Allocator, name: []const u8, lib_dirs: []const []const u8) !?[]const u8 {539fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Directory) !?[]const u8 {
534 for (lib_dirs) |lib_dir| {540 for (lib_directories) |lib_directory| {
535 const full_path = try fs.path.join(arena, &.{ lib_dir, name });541 lib_directory.handle.access(name, .{}) catch |err| switch (err) {
536 fs.cwd().access(full_path, .{}) catch |err| switch (err) {
537 error.FileNotFound => continue,542 error.FileNotFound => continue,
538 else => |e| return e,543 else => |e| return e,
539 };544 };
540 return full_path;545 return try lib_directory.join(arena, &.{name});
541 }546 }
542 return null;547 return null;
543}548}
src/link/Elf.zig+233-631
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1pub const Atom = @import("Elf/Atom.zig");1pub const Atom = @import("Elf/Atom.zig");
22
3base: link.File,3base: link.File,
4zig_object: ?*ZigObject,
4rpath_table: std.StringArrayHashMapUnmanaged(void),5rpath_table: std.StringArrayHashMapUnmanaged(void),
5image_base: u64,6image_base: u64,
6emit_relocs: bool,7emit_relocs: bool,
...@@ -15,7 +16,6 @@ z_relro: bool,...@@ -15,7 +16,6 @@ z_relro: bool,
15z_common_page_size: ?u64,16z_common_page_size: ?u64,
16/// TODO make this non optional and resolve the default in open()17/// TODO make this non optional and resolve the default in open()
17z_max_page_size: ?u64,18z_max_page_size: ?u64,
18lib_dirs: []const []const u8,
19hash_style: HashStyle,19hash_style: HashStyle,
20compress_debug_sections: CompressDebugSections,20compress_debug_sections: CompressDebugSections,
21symbol_wrap_set: std.StringArrayHashMapUnmanaged(void),21symbol_wrap_set: std.StringArrayHashMapUnmanaged(void),
...@@ -36,8 +36,7 @@ ptr_width: PtrWidth,...@@ -36,8 +36,7 @@ ptr_width: PtrWidth,
36llvm_object: ?LlvmObject.Ptr = null,36llvm_object: ?LlvmObject.Ptr = null,
3737
38/// A list of all input files.38/// A list of all input files.
39/// Index of each input file also encodes the priority or precedence of one input file39/// First index is a special "null file". Order is otherwise not observed.
40/// over another.
41files: std.MultiArrayList(File.Entry) = .{},40files: std.MultiArrayList(File.Entry) = .{},
42/// Long-lived list of all file descriptors.41/// Long-lived list of all file descriptors.
43/// We store them globally rather than per actual File so that we can re-use42/// 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,...@@ -116,6 +115,10 @@ comment_merge_section_index: ?Merge.Section.Index = null,
116115
117first_eflags: ?elf.Word = null,116first_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
119const SectionIndexes = struct {122const SectionIndexes = struct {
120 copy_rel: ?u32 = null,123 copy_rel: ?u32 = null,
121 dynamic: ?u32 = null,124 dynamic: ?u32 = null,
...@@ -297,6 +300,7 @@ pub fn createEmpty(...@@ -297,6 +300,7 @@ pub fn createEmpty(
297 .disable_lld_caching = options.disable_lld_caching,300 .disable_lld_caching = options.disable_lld_caching,
298 .build_id = options.build_id,301 .build_id = options.build_id,
299 },302 },
303 .zig_object = null,
300 .rpath_table = rpath_table,304 .rpath_table = rpath_table,
301 .ptr_width = ptr_width,305 .ptr_width = ptr_width,
302 .page_size = page_size,306 .page_size = page_size,
...@@ -328,7 +332,6 @@ pub fn createEmpty(...@@ -328,7 +332,6 @@ pub fn createEmpty(
328 .z_relro = options.z_relro,332 .z_relro = options.z_relro,
329 .z_common_page_size = options.z_common_page_size,333 .z_common_page_size = options.z_common_page_size,
330 .z_max_page_size = options.z_max_page_size,334 .z_max_page_size = options.z_max_page_size,
331 .lib_dirs = options.lib_dirs,
332 .hash_style = options.hash_style,335 .hash_style = options.hash_style,
333 .compress_debug_sections = options.compress_debug_sections,336 .compress_debug_sections = options.compress_debug_sections,
334 .symbol_wrap_set = options.symbol_wrap_set,337 .symbol_wrap_set = options.symbol_wrap_set,
...@@ -341,6 +344,7 @@ pub fn createEmpty(...@@ -341,6 +344,7 @@ pub fn createEmpty(
341 .enable_new_dtags = options.enable_new_dtags,344 .enable_new_dtags = options.enable_new_dtags,
342 .print_icf_sections = options.print_icf_sections,345 .print_icf_sections = options.print_icf_sections,
343 .print_map = options.print_map,346 .print_map = options.print_map,
347 .dump_argv_list = .empty,
344 };348 };
345 if (use_llvm and comp.config.have_zcu) {349 if (use_llvm and comp.config.have_zcu) {
346 self.llvm_object = try LlvmObject.create(arena, comp);350 self.llvm_object = try LlvmObject.create(arena, comp);
...@@ -352,6 +356,9 @@ pub fn createEmpty(...@@ -352,6 +356,9 @@ pub fn createEmpty(
352 return self;356 return self;
353 }357 }
354358
359 // --verbose-link
360 if (comp.verbose_link) try dumpArgvInit(self, arena);
361
355 const is_obj = output_mode == .Obj;362 const is_obj = output_mode == .Obj;
356 const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static);363 const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static);
357364
...@@ -418,14 +425,17 @@ pub fn createEmpty(...@@ -418,14 +425,17 @@ pub fn createEmpty(
418 if (opt_zcu) |zcu| {425 if (opt_zcu) |zcu| {
419 if (!use_llvm) {426 if (!use_llvm) {
420 const index: File.Index = @intCast(try self.files.addOne(gpa));427 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.* = .{
422 .index = index,433 .index = index,
423 .basename = try std.fmt.allocPrint(arena, "{s}.o", .{434 .basename = try std.fmt.allocPrint(arena, "{s}.o", .{
424 fs.path.stem(zcu.main_mod.root_src_path),435 fs.path.stem(zcu.main_mod.root_src_path),
425 }),436 }),
426 } });437 };
427 self.zig_object_index = index;438 try zig_object.init(self, .{
428 try self.zigObjectPtr().?.init(self, .{
429 .symbol_count_hint = options.symbol_count_hint,439 .symbol_count_hint = options.symbol_count_hint,
430 .program_code_size_hint = options.program_code_size_hint,440 .program_code_size_hint = options.program_code_size_hint,
431 });441 });
...@@ -457,12 +467,14 @@ pub fn deinit(self: *Elf) void {...@@ -457,12 +467,14 @@ pub fn deinit(self: *Elf) void {
457 self.file_handles.deinit(gpa);467 self.file_handles.deinit(gpa);
458468
459 for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {469 for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {
460 .null => {},470 .null, .zig_object => {},
461 .zig_object => data.zig_object.deinit(gpa),
462 .linker_defined => data.linker_defined.deinit(gpa),471 .linker_defined => data.linker_defined.deinit(gpa),
463 .object => data.object.deinit(gpa),472 .object => data.object.deinit(gpa),
464 .shared_object => data.shared_object.deinit(gpa),473 .shared_object => data.shared_object.deinit(gpa),
465 };474 };
475 if (self.zig_object) |zig_object| {
476 zig_object.deinit(gpa);
477 }
466 self.files.deinit(gpa);478 self.files.deinit(gpa);
467 self.objects.deinit(gpa);479 self.objects.deinit(gpa);
468 self.shared_objects.deinit(gpa);480 self.shared_objects.deinit(gpa);
...@@ -501,6 +513,7 @@ pub fn deinit(self: *Elf) void {...@@ -501,6 +513,7 @@ pub fn deinit(self: *Elf) void {
501 self.rela_dyn.deinit(gpa);513 self.rela_dyn.deinit(gpa);
502 self.rela_plt.deinit(gpa);514 self.rela_plt.deinit(gpa);
503 self.comdat_group_sections.deinit(gpa);515 self.comdat_group_sections.deinit(gpa);
516 self.dump_argv_list.deinit(gpa);
504}517}
505518
506pub fn getNavVAddr(self: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 {519pub 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 {...@@ -752,6 +765,37 @@ pub fn allocateChunk(self: *Elf, args: struct {
752 return res;765 return res;
753}766}
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
755pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {799pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
756 const use_lld = build_options.have_llvm and self.base.comp.config.use_lld;800 const use_lld = build_options.have_llvm and self.base.comp.config.use_lld;
757 if (use_lld) {801 if (use_lld) {
...@@ -774,11 +818,11 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -774,11 +818,11 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
774 if (use_lld) return;818 if (use_lld) return;
775 }819 }
776820
821 if (comp.verbose_link) Compilation.dump_argv(self.dump_argv_list.items);
822
777 const sub_prog_node = prog_node.start("ELF Flush", 0);823 const sub_prog_node = prog_node.start("ELF Flush", 0);
778 defer sub_prog_node.end();824 defer sub_prog_node.end();
779825
780 const target = self.getTarget();
781 const link_mode = comp.config.link_mode;
782 const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.826 const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
783 const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{827 const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{
784 .root_dir = directory,828 .root_dir = directory,
...@@ -788,126 +832,19 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -788,126 +832,19 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
788 path,832 path,
789 } else null;833 } else null;
790834
791 // --verbose-link
792 if (comp.verbose_link) try self.dumpArgv(comp);
793
794 if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid);835 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 prelude837 if (module_obj_path) |path| openParseObjectReportingFailure(self, path);
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 }
825838
826 for (comp.system_libs.values()) |lib_info| {839 switch (comp.config.output_mode) {
827 parseInputReportingFailure(self, lib_info.path.?, lib_info.needed, false);840 .Obj => return relocatable.flushObject(self, comp),
828 }841 .Lib => switch (comp.config.link_mode) {
829842 .dynamic => {},
830 // libc++ dep843 .static => return relocatable.flushStaticLib(self, comp),
831 if (comp.config.link_libcpp) {844 },
832 parseInputReportingFailure(self, comp.libcxxabi_static_lib.?.full_object_path, false, false);845 .Exe => {},
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);
905 }846 }
906847
907 // csu postlude
908 if (csu.crtend) |path| parseObjectReportingFailure(self, path);
909 if (csu.crtn) |path| parseObjectReportingFailure(self, path);
910
911 if (diags.hasErrors()) return error.FlushFailure;848 if (diags.hasErrors()) return error.FlushFailure;
912849
913 // If we haven't already, create a linker-generated input file comprising of850 // 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...@@ -1058,347 +995,150 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
1058 if (diags.hasErrors()) return error.FlushFailure;995 if (diags.hasErrors()) return error.FlushFailure;
1059}996}
1060997
1061/// --verbose-link output998fn dumpArgvInit(self: *Elf, arena: Allocator) !void {
1062fn dumpArgv(self: *Elf, comp: *Compilation) !void {999 const comp = self.base.comp;
1063 const gpa = self.base.comp.gpa;1000 const gpa = comp.gpa;
1064 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
1065 defer arena_allocator.deinit();
1066 const arena = arena_allocator.allocator();
1067
1068 const target = self.getTarget();1001 const target = self.getTarget();
1069 const link_mode = self.base.comp.config.link_mode;1002 const full_out_path = try self.base.emit.root_dir.join(arena, &[_][]const u8{self.base.emit.sub_path});
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 };
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
1091 if (self.base.isStaticLib()) {1008 if (self.base.isStaticLib()) {
1092 try argv.append("ar");1009 try argv.append(gpa, "ar");
1093 } else {1010 } else {
1094 try argv.append("ld");1011 try argv.append(gpa, "ld");
1095 }1012 }
10961013
1097 if (self.base.isObject()) {1014 if (self.base.isObject()) {
1098 try argv.append("-r");1015 try argv.append(gpa, "-r");
1099 }1016 }
11001017
1101 try argv.append("-o");1018 try argv.append(gpa, "-o");
1102 try argv.append(full_out_path);1019 try argv.append(gpa, full_out_path);
11031020
1104 if (self.base.isRelocatable()) {1021 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 {
1117 if (!self.base.isStatic()) {1022 if (!self.base.isStatic()) {
1118 if (target.dynamic_linker.get()) |path| {1023 if (target.dynamic_linker.get()) |path| {
1119 try argv.append("-dynamic-linker");1024 try argv.appendSlice(gpa, &.{ "-dynamic-linker", try arena.dupe(u8, path) });
1120 try argv.append(path);
1121 }1025 }
1122 }1026 }
11231027
1124 if (self.base.isDynLib()) {1028 if (self.base.isDynLib()) {
1125 if (self.soname) |name| {1029 if (self.soname) |name| {
1126 try argv.append("-soname");1030 try argv.append(gpa, "-soname");
1127 try argv.append(name);1031 try argv.append(gpa, name);
1128 }1032 }
1129 }1033 }
11301034
1131 if (self.entry_name) |name| {1035 if (self.entry_name) |name| {
1132 try argv.appendSlice(&.{ "--entry", name });1036 try argv.appendSlice(gpa, &.{ "--entry", name });
1133 }1037 }
11341038
1135 for (self.rpath_table.keys()) |rpath| {1039 for (self.rpath_table.keys()) |rpath| {
1136 try argv.appendSlice(&.{ "-rpath", rpath });1040 try argv.appendSlice(gpa, &.{ "-rpath", rpath });
1137 }1041 }
11381042
1139 try argv.appendSlice(&.{1043 try argv.appendSlice(gpa, &.{
1140 "-z",1044 "-z",
1141 try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}),1045 try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}),
1142 });1046 });
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
1146 if (self.base.gc_sections) {1050 if (self.base.gc_sections) {
1147 try argv.append("--gc-sections");1051 try argv.append(gpa, "--gc-sections");
1148 }1052 }
11491053
1150 if (self.base.print_gc_sections) {1054 if (self.base.print_gc_sections) {
1151 try argv.append("--print-gc-sections");1055 try argv.append(gpa, "--print-gc-sections");
1152 }1056 }
11531057
1154 if (comp.link_eh_frame_hdr) {1058 if (comp.link_eh_frame_hdr) {
1155 try argv.append("--eh-frame-hdr");1059 try argv.append(gpa, "--eh-frame-hdr");
1156 }1060 }
11571061
1158 if (comp.config.rdynamic) {1062 if (comp.config.rdynamic) {
1159 try argv.append("--export-dynamic");1063 try argv.append(gpa, "--export-dynamic");
1160 }1064 }
11611065
1162 if (self.z_notext) {1066 if (self.z_notext) {
1163 try argv.append("-z");1067 try argv.append(gpa, "-z");
1164 try argv.append("notext");1068 try argv.append(gpa, "notext");
1165 }1069 }
11661070
1167 if (self.z_nocopyreloc) {1071 if (self.z_nocopyreloc) {
1168 try argv.append("-z");1072 try argv.append(gpa, "-z");
1169 try argv.append("nocopyreloc");1073 try argv.append(gpa, "nocopyreloc");
1170 }1074 }
11711075
1172 if (self.z_now) {1076 if (self.z_now) {
1173 try argv.append("-z");1077 try argv.append(gpa, "-z");
1174 try argv.append("now");1078 try argv.append(gpa, "now");
1175 }1079 }
11761080
1177 if (self.base.isStatic()) {1081 if (self.base.isStatic()) {
1178 try argv.append("-static");1082 try argv.append(gpa, "-static");
1179 } else if (self.isEffectivelyDynLib()) {1083 } else if (self.isEffectivelyDynLib()) {
1180 try argv.append("-shared");1084 try argv.append(gpa, "-shared");
1181 }1085 }
11821086
1183 if (comp.config.pie and self.base.isExe()) {1087 if (comp.config.pie and self.base.isExe()) {
1184 try argv.append("-pie");1088 try argv.append(gpa, "-pie");
1185 }1089 }
11861090
1187 if (comp.config.debug_format == .strip) {1091 if (comp.config.debug_format == .strip) {
1188 try argv.append("-s");1092 try argv.append(gpa, "-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));
1238 }1093 }
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
1291 if (comp.config.link_libc) {1095 if (comp.config.link_libc) {
1292 if (self.base.comp.libc_installation != null) {1096 if (self.base.comp.libc_installation) |lci| {
1293 const needs_grouping = link_mode == .static;1097 try argv.append(gpa, "-L");
1294 if (needs_grouping) try argv.append("--start-group");1098 try argv.append(gpa, lci.crt_dir.?);
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 }));
1314 }1099 }
1315 }1100 }
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));
1325 }1101 }
1326
1327 Compilation.dump_argv(argv.items);
1328}1102}
13291103
1330pub const ParseError = error{1104pub fn openParseObjectReportingFailure(self: *Elf, path: Path) void {
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;
1350 const diags = &self.base.comp.link_diags;1105 const diags = &self.base.comp.link_diags;
1351 const target = self.getTarget();1106 const obj = link.openObject(path, false, false) catch |err| {
13521107 switch (diags.failParse(path, "failed to open object {}: {s}", .{ path, @errorName(err) })) {
1353 switch (Compilation.classifyFileExt(path.sub_path)) {1108 error.LinkFailure => return,
1354 .object => parseObjectReportingFailure(self, path),1109 }
1355 .shared_library => parseSharedObject(gpa, diags, .{1110 };
1356 .path = path,1111 self.parseObjectReportingFailure(obj);
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 }
1379}1112}
13801113
1381pub fn parseObjectReportingFailure(self: *Elf, path: Path) void {1114fn parseObjectReportingFailure(self: *Elf, obj: link.Input.Object) void {
1382 const diags = &self.base.comp.link_diags;1115 const diags = &self.base.comp.link_diags;
1383 self.parseObject(path) catch |err| switch (err) {1116 self.parseObject(obj) catch |err| switch (err) {
1384 error.LinkFailure => return, // already reported1117 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)}),
1386 };1119 };
1387}1120}
13881121
1389fn parseObject(self: *Elf, path: Path) ParseError!void {1122fn parseObject(self: *Elf, obj: link.Input.Object) !void {
1390 const tracy = trace(@src());1123 const tracy = trace(@src());
1391 defer tracy.end();1124 defer tracy.end();
13921125
1393 const gpa = self.base.comp.gpa;1126 const gpa = self.base.comp.gpa;
1394 const handle = try path.root_dir.handle.openFile(path.sub_path, .{});1127 const diags = &self.base.comp.link_diags;
1395 const fh = try self.addFileHandle(handle);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
1397 const index: File.Index = @intCast(try self.files.addOne(gpa));1137 const index: File.Index = @intCast(try self.files.addOne(gpa));
1398 self.files.set(index, .{ .object = .{1138 self.files.set(index, .{ .object = .{
1399 .path = .{1139 .path = .{
1400 .root_dir = path.root_dir,1140 .root_dir = obj.path.root_dir,
1401 .sub_path = try gpa.dupe(u8, path.sub_path),1141 .sub_path = try gpa.dupe(u8, obj.path.sub_path),
1402 },1142 },
1403 .file_handle = fh,1143 .file_handle = fh,
1404 .index = index,1144 .index = index,
...@@ -1406,39 +1146,51 @@ fn parseObject(self: *Elf, path: Path) ParseError!void {...@@ -1406,39 +1146,51 @@ fn parseObject(self: *Elf, path: Path) ParseError!void {
1406 try self.objects.append(gpa, index);1146 try self.objects.append(gpa, index);
14071147
1408 const object = self.file(index).?.object;1148 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 }
1410}1153}
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 {
1413 const tracy = trace(@src());1168 const tracy = trace(@src());
1414 defer tracy.end();1169 defer tracy.end();
14151170
1416 const gpa = self.base.comp.gpa;1171 const fh = try addFileHandle(gpa, file_handles, obj.file);
1417 const handle = try path.root_dir.handle.openFile(path.sub_path, .{});1172 var archive = try Archive.parse(gpa, diags, file_handles, obj.path, fh);
1418 const fh = try self.addFileHandle(handle);
1419
1420 var archive: Archive = .{};
1421 defer archive.deinit(gpa);1173 defer archive.deinit(gpa);
1422 try archive.parse(self, path, fh);
14231174
1424 const objects = try archive.objects.toOwnedSlice(gpa);1175 const init_alive = if (is_static_lib) true else obj.must_link;
1425 defer gpa.free(objects);
14261176
1427 for (objects) |extracted| {1177 for (archive.objects) |extracted| {
1428 const index: File.Index = @intCast(try self.files.addOne(gpa));1178 const index: File.Index = @intCast(try files.addOne(gpa));
1429 self.files.set(index, .{ .object = extracted });1179 files.set(index, .{ .object = extracted });
1430 const object = &self.files.items(.data)[index].object;1180 const object = &files.items(.data)[index].object;
1431 object.index = index;1181 object.index = index;
1432 object.alive = must_link;1182 object.alive = init_alive;
1433 try object.parse(self);1183 try object.parseCommon(gpa, diags, obj.path, obj.file, target, first_eflags);
1434 try self.objects.append(gpa, index);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);
1435 }1187 }
1436}1188}
14371189
1438fn parseSharedObject(1190fn parseDso(
1439 gpa: Allocator,1191 gpa: Allocator,
1440 diags: *Diags,1192 diags: *Diags,
1441 lib: SystemLib,1193 dso: link.Input.Dso,
1442 shared_objects: *std.StringArrayHashMapUnmanaged(File.Index),1194 shared_objects: *std.StringArrayHashMapUnmanaged(File.Index),
1443 files: *std.MultiArrayList(File.Entry),1195 files: *std.MultiArrayList(File.Entry),
1444 target: std.Target,1196 target: std.Target,
...@@ -1446,20 +1198,16 @@ fn parseSharedObject(...@@ -1446,20 +1198,16 @@ fn parseSharedObject(
1446 const tracy = trace(@src());1198 const tracy = trace(@src());
1447 defer tracy.end();1199 defer tracy.end();
14481200
1449 const handle = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{});1201 const handle = dso.file;
1450 defer handle.close();
14511202
1452 const stat = Stat.fromFs(try handle.stat());1203 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);
1454 defer header.deinit(gpa);1205 defer header.deinit(gpa);
14551206
1456 const soname = header.soname() orelse lib.path.basename();1207 const soname = header.soname() orelse dso.path.basename();
14571208
1458 const gop = try shared_objects.getOrPut(gpa, soname);1209 const gop = try shared_objects.getOrPut(gpa, soname);
1459 if (gop.found_existing) {1210 if (gop.found_existing) return;
1460 header.deinit(gpa);
1461 return;
1462 }
1463 errdefer _ = shared_objects.pop();1211 errdefer _ = shared_objects.pop();
14641212
1465 const index: File.Index = @intCast(try files.addOne(gpa));1213 const index: File.Index = @intCast(try files.addOne(gpa));
...@@ -1471,8 +1219,8 @@ fn parseSharedObject(...@@ -1471,8 +1219,8 @@ fn parseSharedObject(
1471 errdefer parsed.deinit(gpa);1219 errdefer parsed.deinit(gpa);
14721220
1473 const duped_path: Path = .{1221 const duped_path: Path = .{
1474 .root_dir = lib.path.root_dir,1222 .root_dir = dso.path.root_dir,
1475 .sub_path = try gpa.dupe(u8, lib.path.sub_path),1223 .sub_path = try gpa.dupe(u8, dso.path.sub_path),
1476 };1224 };
1477 errdefer gpa.free(duped_path.sub_path);1225 errdefer gpa.free(duped_path.sub_path);
14781226
...@@ -1481,8 +1229,8 @@ fn parseSharedObject(...@@ -1481,8 +1229,8 @@ fn parseSharedObject(
1481 .parsed = parsed,1229 .parsed = parsed,
1482 .path = duped_path,1230 .path = duped_path,
1483 .index = index,1231 .index = index,
1484 .needed = lib.needed,1232 .needed = dso.needed,
1485 .alive = lib.needed,1233 .alive = dso.needed,
1486 .aliases = null,1234 .aliases = null,
1487 .symbols = .empty,1235 .symbols = .empty,
1488 .symbols_extra = .empty,1236 .symbols_extra = .empty,
...@@ -1490,7 +1238,7 @@ fn parseSharedObject(...@@ -1490,7 +1238,7 @@ fn parseSharedObject(
1490 .output_symtab_ctx = .{},1238 .output_symtab_ctx = .{},
1491 },1239 },
1492 });1240 });
1493 const so = fileLookup(files.*, index).?.shared_object;1241 const so = fileLookup(files.*, index, null).?.shared_object;
14941242
1495 // TODO: save this work for later1243 // TODO: save this work for later
1496 const nsyms = parsed.symbols.len;1244 const nsyms = parsed.symbols.len;
...@@ -1511,148 +1259,6 @@ fn parseSharedObject(...@@ -1511,148 +1259,6 @@ fn parseSharedObject(
1511 }1259 }
1512}1260}
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
1656/// When resolving symbols, we approach the problem similarly to `mold`.1262/// When resolving symbols, we approach the problem similarly to `mold`.
1657/// 1. Resolve symbols across all objects (including those preemptively extracted archives).1263/// 1. Resolve symbols across all objects (including those preemptively extracted archives).
1658/// 2. Resolve symbols across all shared objects.1264/// 2. Resolve symbols across all shared objects.
...@@ -1842,7 +1448,7 @@ pub fn initOutputSection(self: *Elf, args: struct {...@@ -1842,7 +1448,7 @@ pub fn initOutputSection(self: *Elf, args: struct {
1842 ".dtors", ".gnu.warning",1448 ".dtors", ".gnu.warning",
1843 };1449 };
1844 inline for (name_prefixes) |prefix| {1450 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 ++ ".")) {
1846 break :blk prefix;1452 break :blk prefix;
1847 }1453 }
1848 }1454 }
...@@ -1854,9 +1460,9 @@ pub fn initOutputSection(self: *Elf, args: struct {...@@ -1854,9 +1460,9 @@ pub fn initOutputSection(self: *Elf, args: struct {
1854 switch (args.type) {1460 switch (args.type) {
1855 elf.SHT_NULL => unreachable,1461 elf.SHT_NULL => unreachable,
1856 elf.SHT_PROGBITS => {1462 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."))
1858 break :tt elf.SHT_INIT_ARRAY;1464 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."))
1860 break :tt elf.SHT_FINI_ARRAY;1466 break :tt elf.SHT_FINI_ARRAY;
1861 break :tt args.type;1467 break :tt args.type;
1862 },1468 },
...@@ -1951,11 +1557,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -1951,11 +1557,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
1951 try man.addOptionalFile(self.version_script);1557 try man.addOptionalFile(self.version_script);
1952 man.hash.add(self.allow_undefined_version);1558 man.hash.add(self.allow_undefined_version);
1953 man.hash.addOptional(self.enable_new_dtags);1559 man.hash.addOptional(self.enable_new_dtags);
1954 for (comp.objects) |obj| {1560 try link.hashInputs(&man, comp.link_inputs);
1955 _ = try man.addFilePath(obj.path, null);
1956 man.hash.add(obj.must_link);
1957 man.hash.add(obj.loption);
1958 }
1959 for (comp.c_object_table.keys()) |key| {1561 for (comp.c_object_table.keys()) |key| {
1960 _ = try man.addFilePath(key.status.success.object_path, null);1562 _ = try man.addFilePath(key.status.success.object_path, null);
1961 }1563 }
...@@ -1973,7 +1575,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -1973,7 +1575,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
1973 man.hash.add(comp.link_eh_frame_hdr);1575 man.hash.add(comp.link_eh_frame_hdr);
1974 man.hash.add(self.emit_relocs);1576 man.hash.add(self.emit_relocs);
1975 man.hash.add(comp.config.rdynamic);1577 man.hash.add(comp.config.rdynamic);
1976 man.hash.addListOfBytes(self.lib_dirs);
1977 man.hash.addListOfBytes(self.rpath_table.keys());1578 man.hash.addListOfBytes(self.rpath_table.keys());
1978 if (output_mode == .Exe) {1579 if (output_mode == .Exe) {
1979 man.hash.add(self.base.stack_size);1580 man.hash.add(self.base.stack_size);
...@@ -2003,7 +1604,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -2003,7 +1604,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
2003 }1604 }
2004 man.hash.addOptionalBytes(self.soname);1605 man.hash.addOptionalBytes(self.soname);
2005 man.hash.addOptional(comp.version);1606 man.hash.addOptional(comp.version);
2006 try link.hashAddSystemLibs(&man, comp.system_libs);
2007 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());1607 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
2008 man.hash.add(self.base.allow_shlib_undefined);1608 man.hash.add(self.base.allow_shlib_undefined);
2009 man.hash.add(self.bind_global_refs_locally);1609 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...@@ -2050,8 +1650,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
2050 // here. TODO: think carefully about how we can avoid this redundant operation when doing1650 // here. TODO: think carefully about how we can avoid this redundant operation when doing
2051 // build-obj. See also the corresponding TODO in linkAsArchive.1651 // build-obj. See also the corresponding TODO in linkAsArchive.
2052 const the_object_path = blk: {1652 const the_object_path = blk: {
2053 if (comp.objects.len != 0)1653 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
2054 break :blk comp.objects[0].path;
20551654
2056 if (comp.c_object_table.count() != 0)1655 if (comp.c_object_table.count() != 0)
2057 break :blk comp.c_object_table.keys()[0].status.success.object_path;1656 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...@@ -2267,11 +1866,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
2267 try argv.appendSlice(&.{ "-wrap", symbol_name });1866 try argv.appendSlice(&.{ "-wrap", symbol_name });
2268 }1867 }
22691868
2270 for (self.lib_dirs) |lib_dir| {
2271 try argv.append("-L");
2272 try argv.append(lib_dir);
2273 }
2274
2275 if (comp.config.link_libc) {1869 if (comp.config.link_libc) {
2276 if (comp.libc_installation) |libc_installation| {1870 if (comp.libc_installation) |libc_installation| {
2277 try argv.append("-L");1871 try argv.append("-L");
...@@ -2311,21 +1905,26 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -2311,21 +1905,26 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
23111905
2312 // Positional arguments to the linker such as object files.1906 // Positional arguments to the linker such as object files.
2313 var whole_archive = false;1907 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) {1909 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
2324 assert(obj.path.sub_path[0] == ':');1910 .res => unreachable, // Windows-only
2325 try argv.append("-l");1911 .dso => continue,
2326 }1912 .object, .archive => |obj| {
2327 try argv.append(try obj.path.toString(arena));1913 if (obj.must_link and !whole_archive) {
2328 }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
2329 if (whole_archive) {1928 if (whole_archive) {
2330 try argv.append("-no-whole-archive");1929 try argv.append("-no-whole-archive");
2331 whole_archive = false;1930 whole_archive = false;
...@@ -2361,35 +1960,35 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -2361,35 +1960,35 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
23611960
2362 // Shared libraries.1961 // Shared libraries.
2363 if (is_exe_or_dyn_lib) {1962 if (is_exe_or_dyn_lib) {
2364 const system_libs = comp.system_libs.keys();
2365 const system_libs_values = comp.system_libs.values();
2366
2367 // Worst-case, we need an --as-needed argument for every lib, as well1963 // Worst-case, we need an --as-needed argument for every lib, as well
2368 // as one before and one after.1964 // as one before and one after.
2369 try argv.ensureUnusedCapacity(system_libs.len * 2 + 2);1965 try argv.append("--as-needed");
2370 argv.appendAssumeCapacity("--as-needed");
2371 var as_needed = true;1966 var as_needed = true;
23721967
2373 for (system_libs_values) |lib_info| {1968 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
2374 const lib_as_needed = !lib_info.needed;1969 .res => unreachable, // Windows-only
2375 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {1970 .object, .archive, .dso_exact => continue,
2376 0b00, 0b11 => {},1971 .dso => |dso| {
2377 0b01 => {1972 const lib_as_needed = !dso.needed;
2378 argv.appendAssumeCapacity("--no-as-needed");1973 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
2379 as_needed = false;1974 0b00, 0b11 => {},
2380 },1975 0b01 => {
2381 0b10 => {1976 argv.appendAssumeCapacity("--no-as-needed");
2382 argv.appendAssumeCapacity("--as-needed");1977 as_needed = false;
2383 as_needed = true;1978 },
2384 },1979 0b10 => {
2385 }1980 argv.appendAssumeCapacity("--as-needed");
1981 as_needed = true;
1982 },
1983 }
23861984
2387 // By this time, we depend on these libs being dynamically linked1985 // 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),1986 // 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 we1987 // but they could be full paths to .so files, in which case we
2390 // want to avoid prepending "-l".1988 // want to avoid prepending "-l".
2391 argv.appendAssumeCapacity(try lib_info.path.?.toString(arena));1989 argv.appendAssumeCapacity(try dso.path.toString(arena));
2392 }1990 },
1991 };
23931992
2394 if (!as_needed) {1993 if (!as_needed) {
2395 argv.appendAssumeCapacity("--as-needed");1994 argv.appendAssumeCapacity("--as-needed");
...@@ -2421,7 +2020,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -2421,7 +2020,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
2421 if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue;2020 if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue;
2422 }2021 }
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}", .{
2425 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,2024 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
2426 });2025 });
2427 try argv.append(lib_path);2026 try argv.append(lib_path);
...@@ -3469,8 +3068,14 @@ pub fn sortShdrs(...@@ -3469,8 +3068,14 @@ pub fn sortShdrs(
3469 };3068 };
34703069
3471 pub fn lessThan(ctx: Context, lhs: @This(), rhs: @This()) bool {3070 pub fn lessThan(ctx: Context, lhs: @This(), rhs: @This()) bool {
3472 return shdrRank(ctx.shdrs[lhs.shndx], ctx.shstrtab) <3071 const lhs_rank = shdrRank(ctx.shdrs[lhs.shndx], ctx.shstrtab);
3473 shdrRank(ctx.shdrs[rhs.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;
3474 }3079 }
3475 };3080 };
34763081
...@@ -3486,7 +3091,7 @@ pub fn sortShdrs(...@@ -3486,7 +3091,7 @@ pub fn sortShdrs(
3486 .shdrs = shdrs,3091 .shdrs = shdrs,
3487 .shstrtab = shstrtab,3092 .shstrtab = shstrtab,
3488 };3093 };
3489 mem.sort(Entry, entries, sort_context, Entry.lessThan);3094 mem.sortUnstable(Entry, entries, sort_context, Entry.lessThan);
34903095
3491 const backlinks = try gpa.alloc(u32, entries.len);3096 const backlinks = try gpa.alloc(u32, entries.len);
3492 defer gpa.free(backlinks);3097 defer gpa.free(backlinks);
...@@ -3515,7 +3120,7 @@ pub fn sortShdrs(...@@ -3515,7 +3120,7 @@ pub fn sortShdrs(
3515 for (slice.items(.shdr), slice.items(.atom_list_2)) |*shdr, *atom_list| {3120 for (slice.items(.shdr), slice.items(.atom_list_2)) |*shdr, *atom_list| {
3516 atom_list.output_section_index = backlinks[atom_list.output_section_index];3121 atom_list.output_section_index = backlinks[atom_list.output_section_index];
3517 for (atom_list.atoms.keys()) |ref| {3122 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;
3519 }3124 }
3520 if (shdr.sh_type == elf.SHT_RELA) {3125 if (shdr.sh_type == elf.SHT_RELA) {
3521 // FIXME:JK we should spin up .symtab potentially earlier, or set all non-dynamic RELA sections3126 // 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 {...@@ -4745,30 +4350,30 @@ pub fn thunk(self: *Elf, index: Thunk.Index) *Thunk {
4745}4350}
47464351
4747pub fn file(self: *Elf, index: File.Index) ?File {4352pub fn file(self: *Elf, index: File.Index) ?File {
4748 return fileLookup(self.files, index);4353 return fileLookup(self.files, index, self.zig_object);
4749}4354}
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 {
4752 const tag = files.items(.tags)[index];4357 const tag = files.items(.tags)[index];
4753 return switch (tag) {4358 return switch (tag) {
4754 .null => null,4359 .null => null,
4755 .linker_defined => .{ .linker_defined = &files.items(.data)[index].linker_defined },4360 .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.? },
4757 .object => .{ .object = &files.items(.data)[index].object },4362 .object => .{ .object = &files.items(.data)[index].object },
4758 .shared_object => .{ .shared_object = &files.items(.data)[index].shared_object },4363 .shared_object => .{ .shared_object = &files.items(.data)[index].shared_object },
4759 };4364 };
4760}4365}
47614366
4762pub fn addFileHandle(self: *Elf, handle: fs.File) !File.HandleIndex {4367pub fn addFileHandle(
4763 const gpa = self.base.comp.gpa;4368 gpa: Allocator,
4764 const index: File.HandleIndex = @intCast(self.file_handles.items.len);4369 file_handles: *std.ArrayListUnmanaged(File.Handle),
4765 const fh = try self.file_handles.addOne(gpa);4370 handle: fs.File,
4766 fh.* = handle;4371) Allocator.Error!File.HandleIndex {
4767 return index;4372 try file_handles.append(gpa, handle);
4373 return @intCast(file_handles.items.len - 1);
4768}4374}
47694375
4770pub fn fileHandle(self: Elf, index: File.HandleIndex) File.Handle {4376pub fn fileHandle(self: Elf, index: File.HandleIndex) File.Handle {
4771 assert(index < self.file_handles.items.len);
4772 return self.file_handles.items[index];4377 return self.file_handles.items[index];
4773}4378}
47744379
...@@ -4791,8 +4396,7 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32...@@ -4791,8 +4396,7 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32
4791}4396}
47924397
4793pub fn zigObjectPtr(self: *Elf) ?*ZigObject {4398pub fn zigObjectPtr(self: *Elf) ?*ZigObject {
4794 const index = self.zig_object_index orelse return null;4399 return self.zig_object;
4795 return self.file(index).?.zig_object;
4796}4400}
47974401
4798pub fn linkerDefinedPtr(self: *Elf) ?*LinkerDefined {4402pub fn linkerDefinedPtr(self: *Elf) ?*LinkerDefined {
...@@ -4870,7 +4474,7 @@ fn shString(...@@ -4870,7 +4474,7 @@ fn shString(
4870 off: u32,4474 off: u32,
4871) [:0]const u8 {4475) [:0]const u8 {
4872 const slice = shstrtab[off..];4476 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];
4874}4478}
48754479
4876pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 {4480pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 {
...@@ -5628,7 +5232,6 @@ const GnuHashSection = synthetic_sections.GnuHashSection;...@@ -5628,7 +5232,6 @@ const GnuHashSection = synthetic_sections.GnuHashSection;
5628const GotSection = synthetic_sections.GotSection;5232const GotSection = synthetic_sections.GotSection;
5629const GotPltSection = synthetic_sections.GotPltSection;5233const GotPltSection = synthetic_sections.GotPltSection;
5630const HashSection = synthetic_sections.HashSection;5234const HashSection = synthetic_sections.HashSection;
5631const LdScript = @import("Elf/LdScript.zig");
5632const LinkerDefined = @import("Elf/LinkerDefined.zig");5235const LinkerDefined = @import("Elf/LinkerDefined.zig");
5633const Liveness = @import("../Liveness.zig");5236const Liveness = @import("../Liveness.zig");
5634const LlvmObject = @import("../codegen/llvm.zig").Object;5237const LlvmObject = @import("../codegen/llvm.zig").Object;
...@@ -5644,4 +5247,3 @@ const Thunk = @import("Elf/Thunk.zig");...@@ -5644,4 +5247,3 @@ const Thunk = @import("Elf/Thunk.zig");
5644const Value = @import("../Value.zig");5247const Value = @import("../Value.zig");
5645const VerneedSection = synthetic_sections.VerneedSection;5248const VerneedSection = synthetic_sections.VerneedSection;
5646const ZigObject = @import("Elf/ZigObject.zig");5249const ZigObject = @import("Elf/ZigObject.zig");
5647const riscv = @import("riscv.zig");
src/link/Elf/Archive.zig+50-28
...@@ -1,29 +1,46 @@...@@ -1,29 +1,46 @@
1objects: std.ArrayListUnmanaged(Object) = .empty,1objects: []const Object,
2strtab: std.ArrayListUnmanaged(u8) = .empty,2/// '\n'-delimited
33strtab: []const u8,
4pub fn deinit(self: *Archive, allocator: Allocator) void {4
5 self.objects.deinit(allocator);5pub fn deinit(a: *Archive, gpa: Allocator) void {
6 self.strtab.deinit(allocator);6 gpa.free(a.objects);
7 gpa.free(a.strtab);
8 a.* = undefined;
7}9}
810
9pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.HandleIndex) !void {11pub fn parse(
10 const comp = elf_file.base.comp;12 gpa: Allocator,
11 const gpa = comp.gpa;13 diags: *Diags,
12 const diags = &comp.link_diags;14 file_handles: *const std.ArrayListUnmanaged(File.Handle),
13 const handle = elf_file.fileHandle(handle_index);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
14 const size = (try handle.stat()).size;28 const size = (try handle.stat()).size;
1529
16 var pos: usize = elf.ARMAG.len;30 var objects: std.ArrayListUnmanaged(Object) = .empty;
17 while (true) {31 defer objects.deinit(gpa);
18 if (pos >= size) break;
19 if (!mem.isAligned(pos, 2)) pos += 1;
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;
22 {40 {
23 const amt = try handle.preadAll(&hdr_buffer, pos);41 const n = try handle.preadAll(mem.asBytes(&hdr), pos);
24 if (amt != @sizeOf(elf.ar_hdr)) return error.InputOutput;42 if (n != @sizeOf(elf.ar_hdr)) return error.UnexpectedEndOfFile;
25 }43 }
26 const hdr = @as(*align(1) const elf.ar_hdr, @ptrCast(&hdr_buffer)).*;
27 pos += @sizeOf(elf.ar_hdr);44 pos += @sizeOf(elf.ar_hdr);
2845
29 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {46 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...@@ -37,8 +54,8 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.Hand
3754
38 if (hdr.isSymtab() or hdr.isSymtab64()) continue;55 if (hdr.isSymtab() or hdr.isSymtab64()) continue;
39 if (hdr.isStrtab()) {56 if (hdr.isStrtab()) {
40 try self.strtab.resize(gpa, obj_size);57 try strtab.resize(gpa, obj_size);
41 const amt = try handle.preadAll(self.strtab.items, pos);58 const amt = try handle.preadAll(strtab.items, pos);
42 if (amt != obj_size) return error.InputOutput;59 if (amt != obj_size) return error.InputOutput;
43 continue;60 continue;
44 }61 }
...@@ -47,7 +64,7 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.Hand...@@ -47,7 +64,7 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.Hand
47 const name = if (hdr.name()) |name|64 const name = if (hdr.name()) |name|
48 name65 name
49 else if (try hdr.nameOffset()) |off|66 else if (try hdr.nameOffset()) |off|
50 self.getString(off)67 stringTableLookup(strtab.items, off)
51 else68 else
52 unreachable;69 unreachable;
5370
...@@ -70,14 +87,18 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.Hand...@@ -70,14 +87,18 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.Hand
70 @as(Path, object.path), @as(Path, path),87 @as(Path, object.path), @as(Path, path),
71 });88 });
7289
73 try self.objects.append(gpa, object);90 try objects.append(gpa, object);
74 }91 }
92
93 return .{
94 .objects = try objects.toOwnedSlice(gpa),
95 .strtab = try strtab.toOwnedSlice(gpa),
96 };
75}97}
7698
77fn getString(self: Archive, off: u32) []const u8 {99pub fn stringTableLookup(strtab: []const u8, off: u32) [:'\n']const u8 {
78 assert(off < self.strtab.items.len);100 const slice = strtab[off..];
79 const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.items.ptr + off)), 0);101 return slice[0..mem.indexOfScalar(u8, slice, '\n').? :'\n'];
80 return name[0 .. name.len - 1];
81}102}
82103
83pub fn setArHdr(opts: struct {104pub fn setArHdr(opts: struct {
...@@ -290,8 +311,9 @@ const fs = std.fs;...@@ -290,8 +311,9 @@ const fs = std.fs;
290const log = std.log.scoped(.link);311const log = std.log.scoped(.link);
291const mem = std.mem;312const mem = std.mem;
292const Path = std.Build.Cache.Path;313const Path = std.Build.Cache.Path;
314const Allocator = std.mem.Allocator;
293315
294const Allocator = mem.Allocator;316const Diags = @import("../../link.zig").Diags;
295const Archive = @This();317const Archive = @This();
296const Elf = @import("../Elf.zig");318const Elf = @import("../Elf.zig");
297const File = @import("file.zig").File;319const File = @import("file.zig").File;
src/link/Elf/Atom.zig+27-24
...@@ -102,9 +102,13 @@ pub fn relocsShndx(self: Atom) ?u32 {...@@ -102,9 +102,13 @@ pub fn relocsShndx(self: Atom) ?u32 {
102 return self.relocs_section_index;102 return self.relocs_section_index;
103}103}
104104
105pub fn priority(self: Atom, elf_file: *Elf) u64 {105pub fn priority(atom: Atom, elf_file: *Elf) u64 {
106 const index = self.file(elf_file).?.index();106 const index = atom.file(elf_file).?.index();
107 return (@as(u64, @intCast(index)) << 32) | @as(u64, @intCast(self.input_section_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));
108}112}
109113
110/// Returns how much room there is to grow in virtual address space.114/// 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...@@ -255,19 +259,13 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El
255 }259 }
256}260}
257261
258pub fn fdes(self: Atom, elf_file: *Elf) []Fde {262pub fn fdes(atom: Atom, object: *Object) []Fde {
259 const extras = self.extra(elf_file);263 const extras = object.atomExtra(atom.extra_index);
260 return switch (self.file(elf_file).?) {264 return object.fdes.items[extras.fde_start..][0..extras.fde_count];
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 };
265}265}
266266
267pub fn markFdesDead(self: Atom, elf_file: *Elf) void {267pub fn markFdesDead(self: Atom, object: *Object) void {
268 for (self.fdes(elf_file)) |*fde| {268 for (self.fdes(object)) |*fde| fde.alive = false;
269 fde.alive = false;
270 }
271}269}
272270
273pub fn addReloc(self: Atom, alloc: Allocator, reloc: elf.Elf64_Rela, zo: *ZigObject) !void {271pub fn addReloc(self: Atom, alloc: Allocator, reloc: elf.Elf64_Rela, zo: *ZigObject) !void {
...@@ -946,16 +944,21 @@ fn format2(...@@ -946,16 +944,21 @@ fn format2(
946 atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size,944 atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size,
947 atom.prev_atom_ref, atom.next_atom_ref,945 atom.prev_atom_ref, atom.next_atom_ref,
948 });946 });
949 if (atom.fdes(elf_file).len > 0) {947 if (atom.file(elf_file)) |atom_file| switch (atom_file) {
950 try writer.writeAll(" : fdes{ ");948 .object => |object| {
951 const extras = atom.extra(elf_file);949 if (atom.fdes(object).len > 0) {
952 for (atom.fdes(elf_file), extras.fde_start..) |fde, i| {950 try writer.writeAll(" : fdes{ ");
953 try writer.print("{d}", .{i});951 const extras = atom.extra(elf_file);
954 if (!fde.alive) try writer.writeAll("([*])");952 for (atom.fdes(object), extras.fde_start..) |fde, i| {
955 if (i - extras.fde_start < extras.fde_count - 1) try writer.writeAll(", ");953 try writer.print("{d}", .{i});
956 }954 if (!fde.alive) try writer.writeAll("([*])");
957 try writer.writeAll(" }");955 if (i - extras.fde_start < extras.fde_count - 1) try writer.writeAll(", ");
958 }956 }
957 try writer.writeAll(" }");
958 }
959 },
960 else => {},
961 };
959 if (!atom.alive) {962 if (!atom.alive) {
960 try writer.writeAll(" : [*]");963 try writer.writeAll(" : [*]");
961 }964 }
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,...@@ -37,72 +37,84 @@ num_dynrelocs: u32 = 0,
37output_symtab_ctx: Elf.SymtabCtx = .{},37output_symtab_ctx: Elf.SymtabCtx = .{},
38output_ar_state: Archive.ArState = .{},38output_ar_state: Archive.ArState = .{},
3939
40pub fn deinit(self: *Object, allocator: Allocator) void {40pub fn deinit(self: *Object, gpa: Allocator) void {
41 if (self.archive) |*ar| allocator.free(ar.path.sub_path);41 if (self.archive) |*ar| gpa.free(ar.path.sub_path);
42 allocator.free(self.path.sub_path);42 gpa.free(self.path.sub_path);
43 self.shdrs.deinit(allocator);43 self.shdrs.deinit(gpa);
44 self.symtab.deinit(allocator);44 self.symtab.deinit(gpa);
45 self.strtab.deinit(allocator);45 self.strtab.deinit(gpa);
46 self.symbols.deinit(allocator);46 self.symbols.deinit(gpa);
47 self.symbols_extra.deinit(allocator);47 self.symbols_extra.deinit(gpa);
48 self.symbols_resolver.deinit(allocator);48 self.symbols_resolver.deinit(gpa);
49 self.atoms.deinit(allocator);49 self.atoms.deinit(gpa);
50 self.atoms_indexes.deinit(allocator);50 self.atoms_indexes.deinit(gpa);
51 self.atoms_extra.deinit(allocator);51 self.atoms_extra.deinit(gpa);
52 self.comdat_groups.deinit(allocator);52 self.comdat_groups.deinit(gpa);
53 self.comdat_group_data.deinit(allocator);53 self.comdat_group_data.deinit(gpa);
54 self.relocs.deinit(allocator);54 self.relocs.deinit(gpa);
55 self.fdes.deinit(allocator);55 self.fdes.deinit(gpa);
56 self.cies.deinit(allocator);56 self.cies.deinit(gpa);
57 self.eh_frame_data.deinit(allocator);57 self.eh_frame_data.deinit(gpa);
58 for (self.input_merge_sections.items) |*isec| {58 for (self.input_merge_sections.items) |*isec| {
59 isec.deinit(allocator);59 isec.deinit(gpa);
60 }60 }
61 self.input_merge_sections.deinit(allocator);61 self.input_merge_sections.deinit(gpa);
62 self.input_merge_sections_indexes.deinit(allocator);62 self.input_merge_sections_indexes.deinit(gpa);
63}63}
6464
65pub fn parse(self: *Object, elf_file: *Elf) !void {65pub fn parse(
66 const gpa = elf_file.base.comp.gpa;66 self: *Object,
67 const cpu_arch = elf_file.getTarget().cpu.arch;67 gpa: Allocator,
68 const handle = elf_file.fileHandle(self.file_handle);68 diags: *Diags,
6969 /// For error reporting purposes only.
70 try self.parseCommon(gpa, handle, elf_file);70 path: Path,
7171 handle: fs.File,
72 target: std.Target,
73 debug_fmt_strip: bool,
74 default_sym_version: elf.Versym,
75) !void {
72 // Append null input merge section76 // Append null input merge section
73 try self.input_merge_sections.append(gpa, .{});77 try self.input_merge_sections.append(gpa, .{});
74 // Allocate atom index 0 to null atom78 // Allocate atom index 0 to null atom
75 try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) });79 try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) });
7680
77 try self.initAtoms(gpa, handle, elf_file);81 try self.initAtoms(gpa, diags, path, handle, debug_fmt_strip, target);
78 try self.initSymbols(gpa, elf_file);82 try self.initSymbols(gpa, default_sym_version);
7983
80 for (self.shdrs.items, 0..) |shdr, i| {84 for (self.shdrs.items, 0..) |shdr, i| {
81 const atom_ptr = self.atom(self.atoms_indexes.items[i]) orelse continue;85 const atom_ptr = self.atom(self.atoms_indexes.items[i]) orelse continue;
82 if (!atom_ptr.alive) continue;86 if (!atom_ptr.alive) continue;
83 if ((cpu_arch == .x86_64 and shdr.sh_type == elf.SHT_X86_64_UNWIND) or87 if ((target.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"))88 mem.eql(u8, self.getString(atom_ptr.name_offset), ".eh_frame"))
85 {89 {
86 try self.parseEhFrame(gpa, handle, @as(u32, @intCast(i)), elf_file);90 try self.parseEhFrame(gpa, handle, @intCast(i), target);
87 }91 }
88 }92 }
89}93}
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 {
92 const offset = if (self.archive) |ar| ar.offset else 0;104 const offset = if (self.archive) |ar| ar.offset else 0;
93 const file_size = (try handle.stat()).size;105 const file_size = (try handle.stat()).size;
94106
95 const header_buffer = try Elf.preadAllAlloc(allocator, handle, offset, @sizeOf(elf.Elf64_Ehdr));107 const header_buffer = try Elf.preadAllAlloc(gpa, handle, offset, @sizeOf(elf.Elf64_Ehdr));
96 defer allocator.free(header_buffer);108 defer gpa.free(header_buffer);
97 self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*;109 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();
100 if (em != self.header.?.e_machine) {112 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}", .{
102 @tagName(self.header.?.e_machine),114 @tagName(self.header.?.e_machine),
103 });115 });
104 }116 }
105 try elf_file.validateEFlags(self.index, self.header.?.e_flags);117 try validateEFlags(diags, path, target, self.header.?.e_flags, first_eflags);
106118
107 if (self.header.?.e_shnum == 0) return;119 if (self.header.?.e_shnum == 0) return;
108120
...@@ -110,30 +122,30 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil...@@ -110,30 +122,30 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil
110 const shnum = math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow;122 const shnum = math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow;
111 const shsize = shnum * @sizeOf(elf.Elf64_Shdr);123 const shsize = shnum * @sizeOf(elf.Elf64_Shdr);
112 if (file_size < offset + shoff or file_size < offset + shoff + shsize) {124 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", .{});
114 }126 }
115127
116 const shdrs_buffer = try Elf.preadAllAlloc(allocator, handle, offset + shoff, shsize);128 const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, offset + shoff, shsize);
117 defer allocator.free(shdrs_buffer);129 defer gpa.free(shdrs_buffer);
118 const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum];130 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
121 for (self.shdrs.items) |shdr| {133 for (self.shdrs.items) |shdr| {
122 if (shdr.sh_type != elf.SHT_NOBITS) {134 if (shdr.sh_type != elf.SHT_NOBITS) {
123 if (file_size < offset + shdr.sh_offset or file_size < offset + shdr.sh_offset + shdr.sh_size) {135 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", .{});
125 }137 }
126 }138 }
127 }139 }
128140
129 const shstrtab = try self.preadShdrContentsAlloc(allocator, handle, self.header.?.e_shstrndx);141 const shstrtab = try self.preadShdrContentsAlloc(gpa, handle, self.header.?.e_shstrndx);
130 defer allocator.free(shstrtab);142 defer gpa.free(shstrtab);
131 for (self.shdrs.items) |shdr| {143 for (self.shdrs.items) |shdr| {
132 if (shdr.sh_name >= shstrtab.len) {144 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", .{});
134 }146 }
135 }147 }
136 try self.strtab.appendSlice(allocator, shstrtab);148 try self.strtab.appendSlice(gpa, shstrtab);
137149
138 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {150 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
139 elf.SHT_SYMTAB => break @as(u32, @intCast(i)),151 elf.SHT_SYMTAB => break @as(u32, @intCast(i)),
...@@ -144,19 +156,19 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil...@@ -144,19 +156,19 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil
144 const shdr = self.shdrs.items[index];156 const shdr = self.shdrs.items[index];
145 self.first_global = shdr.sh_info;157 self.first_global = shdr.sh_info;
146158
147 const raw_symtab = try self.preadShdrContentsAlloc(allocator, handle, index);159 const raw_symtab = try self.preadShdrContentsAlloc(gpa, handle, index);
148 defer allocator.free(raw_symtab);160 defer gpa.free(raw_symtab);
149 const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch {161 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", .{});
151 };163 };
152 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];164 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
153165
154 const strtab_bias = @as(u32, @intCast(self.strtab.items.len));166 const strtab_bias = @as(u32, @intCast(self.strtab.items.len));
155 const strtab = try self.preadShdrContentsAlloc(allocator, handle, shdr.sh_link);167 const strtab = try self.preadShdrContentsAlloc(gpa, handle, shdr.sh_link);
156 defer allocator.free(strtab);168 defer gpa.free(strtab);
157 try self.strtab.appendSlice(allocator, 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);
160 for (symtab) |sym| {172 for (symtab) |sym| {
161 const out_sym = self.symtab.addOneAssumeCapacity();173 const out_sym = self.symtab.addOneAssumeCapacity();
162 out_sym.* = sym;174 out_sym.* = sym;
...@@ -168,15 +180,56 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil...@@ -168,15 +180,56 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil
168 }180 }
169}181}
170182
171fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void {183fn validateEFlags(
172 const comp = elf_file.base.comp;184 diags: *Diags,
173 const debug_fmt_strip = comp.config.debug_format == .strip;185 path: Path,
174 const target = comp.root_mod.resolved_target.result;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 {
175 const shdrs = self.shdrs.items;228 const shdrs = self.shdrs.items;
176 try self.atoms.ensureTotalCapacityPrecise(allocator, shdrs.len);229 try self.atoms.ensureTotalCapacityPrecise(gpa, shdrs.len);
177 try self.atoms_extra.ensureTotalCapacityPrecise(allocator, shdrs.len * @sizeOf(Atom.Extra));230 try self.atoms_extra.ensureTotalCapacityPrecise(gpa, shdrs.len * @sizeOf(Atom.Extra));
178 try self.atoms_indexes.ensureTotalCapacityPrecise(allocator, shdrs.len);231 try self.atoms_indexes.ensureTotalCapacityPrecise(gpa, shdrs.len);
179 try self.atoms_indexes.resize(allocator, shdrs.len);232 try self.atoms_indexes.resize(gpa, shdrs.len);
180 @memset(self.atoms_indexes.items, 0);233 @memset(self.atoms_indexes.items, 0);
181234
182 for (shdrs, 0..) |shdr, i| {235 for (shdrs, 0..) |shdr, i| {
...@@ -201,24 +254,24 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:...@@ -201,24 +254,24 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
201 };254 };
202255
203 const shndx: u32 = @intCast(i);256 const shndx: u32 = @intCast(i);
204 const group_raw_data = try self.preadShdrContentsAlloc(allocator, handle, shndx);257 const group_raw_data = try self.preadShdrContentsAlloc(gpa, handle, shndx);
205 defer allocator.free(group_raw_data);258 defer gpa.free(group_raw_data);
206 const group_nmembers = math.divExact(usize, group_raw_data.len, @sizeOf(u32)) catch {259 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 ", .{});
208 };261 };
209 if (group_nmembers == 0) {262 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", .{});
211 }264 }
212 const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];265 const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];
213266
214 if (group_members[0] != elf.GRP_COMDAT) {267 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", .{});
216 }269 }
217270
218 const group_start: u32 = @intCast(self.comdat_group_data.items.len);271 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);
222 const comdat_group = self.comdatGroup(comdat_group_index);275 const comdat_group = self.comdatGroup(comdat_group_index);
223 comdat_group.* = .{276 comdat_group.* = .{
224 .signature_off = group_signature,277 .signature_off = group_signature,
...@@ -242,8 +295,8 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:...@@ -242,8 +295,8 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
242 const shndx: u32 = @intCast(i);295 const shndx: u32 = @intCast(i);
243 if (self.skipShdr(shndx, debug_fmt_strip)) continue;296 if (self.skipShdr(shndx, debug_fmt_strip)) continue;
244 const size, const alignment = if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) blk: {297 const size, const alignment = if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) blk: {
245 const data = try self.preadShdrContentsAlloc(allocator, handle, shndx);298 const data = try self.preadShdrContentsAlloc(gpa, handle, shndx);
246 defer allocator.free(data);299 defer gpa.free(data);
247 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;300 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
248 break :blk .{ chdr.ch_size, Alignment.fromNonzeroByteUnits(chdr.ch_addralign) };301 break :blk .{ chdr.ch_size, Alignment.fromNonzeroByteUnits(chdr.ch_addralign) };
249 } else .{ shdr.sh_size, Alignment.fromNonzeroByteUnits(shdr.sh_addralign) };302 } 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:...@@ -263,13 +316,13 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
263 elf.SHT_REL, elf.SHT_RELA => {316 elf.SHT_REL, elf.SHT_RELA => {
264 const atom_index = self.atoms_indexes.items[shdr.sh_info];317 const atom_index = self.atoms_indexes.items[shdr.sh_info];
265 if (self.atom(atom_index)) |atom_ptr| {318 if (self.atom(atom_index)) |atom_ptr| {
266 const relocs = try self.preadRelocsAlloc(allocator, handle, @intCast(i));319 const relocs = try self.preadRelocsAlloc(gpa, handle, @intCast(i));
267 defer allocator.free(relocs);320 defer gpa.free(relocs);
268 atom_ptr.relocs_section_index = @intCast(i);321 atom_ptr.relocs_section_index = @intCast(i);
269 const rel_index: u32 = @intCast(self.relocs.items.len);322 const rel_index: u32 = @intCast(self.relocs.items.len);
270 const rel_count: u32 = @intCast(relocs.len);323 const rel_count: u32 = @intCast(relocs.len);
271 self.setAtomFields(atom_ptr, .{ .rel_index = rel_index, .rel_count = rel_count });324 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);
273 if (target.cpu.arch == .riscv64) {326 if (target.cpu.arch == .riscv64) {
274 sortRelocs(self.relocs.items[rel_index..][0..rel_count]);327 sortRelocs(self.relocs.items[rel_index..][0..rel_count]);
275 }328 }
...@@ -293,14 +346,18 @@ fn skipShdr(self: *Object, index: u32, debug_fmt_strip: bool) bool {...@@ -293,14 +346,18 @@ fn skipShdr(self: *Object, index: u32, debug_fmt_strip: bool) bool {
293 return ignore;346 return ignore;
294}347}
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 {
297 const first_global = self.first_global orelse self.symtab.items.len;354 const first_global = self.first_global orelse self.symtab.items.len;
298 const nglobals = self.symtab.items.len - first_global;355 const nglobals = self.symtab.items.len - first_global;
299356
300 try self.symbols.ensureTotalCapacityPrecise(allocator, self.symtab.items.len);357 try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.items.len);
301 try self.symbols_extra.ensureTotalCapacityPrecise(allocator, self.symtab.items.len * @sizeOf(Symbol.Extra));358 try self.symbols_extra.ensureTotalCapacityPrecise(gpa, self.symtab.items.len * @sizeOf(Symbol.Extra));
302 try self.symbols_resolver.ensureTotalCapacityPrecise(allocator, nglobals);359 try self.symbols_resolver.ensureTotalCapacityPrecise(gpa, nglobals);
303 self.symbols_resolver.resize(allocator, nglobals) catch unreachable;360 self.symbols_resolver.resize(gpa, nglobals) catch unreachable;
304 @memset(self.symbols_resolver.items, 0);361 @memset(self.symbols_resolver.items, 0);
305362
306 for (self.symtab.items, 0..) |sym, i| {363 for (self.symtab.items, 0..) |sym, i| {
...@@ -310,7 +367,7 @@ fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void {...@@ -310,7 +367,7 @@ fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void {
310 sym_ptr.name_offset = sym.st_name;367 sym_ptr.name_offset = sym.st_name;
311 sym_ptr.esym_index = @intCast(i);368 sym_ptr.esym_index = @intCast(i);
312 sym_ptr.extra_index = self.addSymbolExtraAssumeCapacity(.{});369 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;
314 sym_ptr.flags.weak = sym.st_bind() == elf.STB_WEAK;371 sym_ptr.flags.weak = sym.st_bind() == elf.STB_WEAK;
315 if (sym.st_shndx != elf.SHN_ABS and sym.st_shndx != elf.SHN_COMMON) {372 if (sym.st_shndx != elf.SHN_ABS and sym.st_shndx != elf.SHN_COMMON) {
316 sym_ptr.ref = .{ .index = self.atoms_indexes.items[sym.st_shndx], .file = self.index };373 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 {...@@ -318,24 +375,30 @@ fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void {
318 }375 }
319}376}
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 {
322 const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {385 const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
323 elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u32, @intCast(i)),386 elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u32, @intCast(i)),
324 else => {},387 else => {},
325 } else null;388 } else null;
326389
327 const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx);390 const raw = try self.preadShdrContentsAlloc(gpa, handle, shndx);
328 defer allocator.free(raw);391 defer gpa.free(raw);
329 const data_start = @as(u32, @intCast(self.eh_frame_data.items.len));392 const data_start: u32 = @intCast(self.eh_frame_data.items.len);
330 try self.eh_frame_data.appendSlice(allocator, raw);393 try self.eh_frame_data.appendSlice(gpa, raw);
331 const relocs = if (relocs_shndx) |index|394 const relocs = if (relocs_shndx) |index|
332 try self.preadRelocsAlloc(allocator, handle, index)395 try self.preadRelocsAlloc(gpa, handle, index)
333 else396 else
334 &[0]elf.Elf64_Rela{};397 &[0]elf.Elf64_Rela{};
335 defer allocator.free(relocs);398 defer gpa.free(relocs);
336 const rel_start = @as(u32, @intCast(self.relocs.items.len));399 const rel_start: u32 = @intCast(self.relocs.items.len);
337 try self.relocs.appendUnalignedSlice(allocator, relocs);400 try self.relocs.appendUnalignedSlice(gpa, relocs);
338 if (elf_file.getTarget().cpu.arch == .riscv64) {401 if (target.cpu.arch == .riscv64) {
339 sortRelocs(self.relocs.items[rel_start..][0..relocs.len]);402 sortRelocs(self.relocs.items[rel_start..][0..relocs.len]);
340 }403 }
341 const fdes_start = self.fdes.items.len;404 const fdes_start = self.fdes.items.len;
...@@ -345,11 +408,11 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:...@@ -345,11 +408,11 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
345 while (try it.next()) |rec| {408 while (try it.next()) |rec| {
346 const rel_range = filterRelocs(self.relocs.items[rel_start..][0..relocs.len], rec.offset, rec.size + 4);409 const rel_range = filterRelocs(self.relocs.items[rel_start..][0..relocs.len], rec.offset, rec.size + 4);
347 switch (rec.tag) {410 switch (rec.tag) {
348 .cie => try self.cies.append(allocator, .{411 .cie => try self.cies.append(gpa, .{
349 .offset = data_start + rec.offset,412 .offset = data_start + rec.offset,
350 .size = rec.size,413 .size = rec.size,
351 .rel_index = rel_start + @as(u32, @intCast(rel_range.start)),414 .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),
353 .input_section_index = shndx,416 .input_section_index = shndx,
354 .file_index = self.index,417 .file_index = self.index,
355 }),418 }),
...@@ -361,12 +424,12 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:...@@ -361,12 +424,12 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
361 // this can happen for object files built with -r flag by the linker.424 // this can happen for object files built with -r flag by the linker.
362 continue;425 continue;
363 }426 }
364 try self.fdes.append(allocator, .{427 try self.fdes.append(gpa, .{
365 .offset = data_start + rec.offset,428 .offset = data_start + rec.offset,
366 .size = rec.size,429 .size = rec.size,
367 .cie_index = undefined,430 .cie_index = undefined,
368 .rel_index = rel_start + @as(u32, @intCast(rel_range.start)),431 .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),
370 .input_section_index = shndx,433 .input_section_index = shndx,
371 .file_index = self.index,434 .file_index = self.index,
372 });435 });
...@@ -376,7 +439,7 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:...@@ -376,7 +439,7 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
376439
377 // Tie each FDE to its CIE440 // Tie each FDE to its CIE
378 for (self.fdes.items[fdes_start..]) |*fde| {441 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);
380 const cie_index = for (self.cies.items[cies_start..], cies_start..) |cie, cie_index| {443 const cie_index = for (self.cies.items[cies_start..], cies_start..) |cie, cie_index| {
381 if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index));444 if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index));
382 } else {445 } else {
...@@ -392,26 +455,26 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:...@@ -392,26 +455,26 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
392455
393 // Tie each FDE record to its matching atom456 // Tie each FDE record to its matching atom
394 const SortFdes = struct {457 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 {
396 const lhs_atom = lhs.atom(ctx);459 const lhs_atom = lhs.atom(ctx);
397 const rhs_atom = rhs.atom(ctx);460 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);
399 }462 }
400 };463 };
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
403 // Create a back-link from atom to FDEs466 // Create a back-link from atom to FDEs
404 var i: u32 = @as(u32, @intCast(fdes_start));467 var i: u32 = @intCast(fdes_start);
405 while (i < self.fdes.items.len) {468 while (i < self.fdes.items.len) {
406 const fde = self.fdes.items[i];469 const fde = self.fdes.items[i];
407 const atom_ptr = fde.atom(elf_file);470 const atom_ptr = fde.atom(self);
408 const start = i;471 const start = i;
409 i += 1;472 i += 1;
410 while (i < self.fdes.items.len) : (i += 1) {473 while (i < self.fdes.items.len) : (i += 1) {
411 const next_fde = self.fdes.items[i];474 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;
413 }476 }
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 });
415 }478 }
416}479}
417480
...@@ -904,7 +967,7 @@ pub fn markComdatGroupsDead(self: *Object, elf_file: *Elf) void {...@@ -904,7 +967,7 @@ pub fn markComdatGroupsDead(self: *Object, elf_file: *Elf) void {
904 const atom_index = self.atoms_indexes.items[shndx];967 const atom_index = self.atoms_indexes.items[shndx];
905 if (self.atom(atom_index)) |atom_ptr| {968 if (self.atom(atom_index)) |atom_ptr| {
906 atom_ptr.alive = false;969 atom_ptr.alive = false;
907 atom_ptr.markFdesDead(elf_file);970 atom_ptr.markFdesDead(self);
908 }971 }
909 }972 }
910 }973 }
...@@ -970,12 +1033,6 @@ pub fn addAtomsToRelaSections(self: *Object, elf_file: *Elf) !void {...@@ -970,12 +1033,6 @@ pub fn addAtomsToRelaSections(self: *Object, elf_file: *Elf) !void {
970 }1033 }
971}1034}
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
979pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {1036pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {
980 const comp = elf_file.base.comp;1037 const comp = elf_file.base.comp;
981 const gpa = comp.gpa;1038 const gpa = comp.gpa;
...@@ -1000,7 +1057,7 @@ pub fn updateArSize(self: *Object, elf_file: *Elf) !void {...@@ -1000,7 +1057,7 @@ pub fn updateArSize(self: *Object, elf_file: *Elf) !void {
1000pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void {1057pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void {
1001 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;1058 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
1002 const offset: u64 = if (self.archive) |ar| ar.offset else 0;1059 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);
1004 const hdr = Archive.setArHdr(.{1061 const hdr = Archive.setArHdr(.{
1005 .name = if (name.len <= Archive.max_member_name_len)1062 .name = if (name.len <= Archive.max_member_name_len)
1006 .{ .name = name }1063 .{ .name = name }
...@@ -1136,8 +1193,8 @@ pub fn resolveSymbol(self: Object, index: Symbol.Index, elf_file: *Elf) Elf.Ref...@@ -1136,8 +1193,8 @@ pub fn resolveSymbol(self: Object, index: Symbol.Index, elf_file: *Elf) Elf.Ref
1136 return elf_file.resolver.get(resolv).?;1193 return elf_file.resolver.get(resolv).?;
1137}1194}
11381195
1139fn addSymbol(self: *Object, allocator: Allocator) !Symbol.Index {1196fn addSymbol(self: *Object, gpa: Allocator) !Symbol.Index {
1140 try self.symbols.ensureUnusedCapacity(allocator, 1);1197 try self.symbols.ensureUnusedCapacity(gpa, 1);
1141 return self.addSymbolAssumeCapacity();1198 return self.addSymbolAssumeCapacity();
1142}1199}
11431200
...@@ -1147,9 +1204,9 @@ fn addSymbolAssumeCapacity(self: *Object) Symbol.Index {...@@ -1147,9 +1204,9 @@ fn addSymbolAssumeCapacity(self: *Object) Symbol.Index {
1147 return index;1204 return index;
1148}1205}
11491206
1150pub fn addSymbolExtra(self: *Object, allocator: Allocator, extra: Symbol.Extra) !u32 {1207pub fn addSymbolExtra(self: *Object, gpa: Allocator, extra: Symbol.Extra) !u32 {
1151 const fields = @typeInfo(Symbol.Extra).@"struct".fields;1208 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);
1153 return self.addSymbolExtraAssumeCapacity(extra);1210 return self.addSymbolExtraAssumeCapacity(extra);
1154}1211}
11551212
...@@ -1198,27 +1255,27 @@ pub fn getString(self: Object, off: u32) [:0]const u8 {...@@ -1198,27 +1255,27 @@ pub fn getString(self: Object, off: u32) [:0]const u8 {
1198 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);1255 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
1199}1256}
12001257
1201fn addString(self: *Object, allocator: Allocator, str: []const u8) !u32 {1258fn addString(self: *Object, gpa: Allocator, str: []const u8) !u32 {
1202 const off: u32 = @intCast(self.strtab.items.len);1259 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);
1204 self.strtab.appendSliceAssumeCapacity(str);1261 self.strtab.appendSliceAssumeCapacity(str);
1205 self.strtab.appendAssumeCapacity(0);1262 self.strtab.appendAssumeCapacity(0);
1206 return off;1263 return off;
1207}1264}
12081265
1209/// Caller owns the memory.1266/// 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 {
1211 assert(index < self.shdrs.items.len);1268 assert(index < self.shdrs.items.len);
1212 const offset = if (self.archive) |ar| ar.offset else 0;1269 const offset = if (self.archive) |ar| ar.offset else 0;
1213 const shdr = self.shdrs.items[index];1270 const shdr = self.shdrs.items[index];
1214 const sh_offset = math.cast(u64, shdr.sh_offset) orelse return error.Overflow;1271 const sh_offset = math.cast(u64, shdr.sh_offset) orelse return error.Overflow;
1215 const sh_size = math.cast(u64, shdr.sh_size) orelse return error.Overflow;1272 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);
1217}1274}
12181275
1219/// Caller owns the memory.1276/// Caller owns the memory.
1220fn preadRelocsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, shndx: u32) ![]align(1) const elf.Elf64_Rela {1277fn preadRelocsAlloc(self: Object, gpa: Allocator, handle: fs.File, shndx: u32) ![]align(1) const elf.Elf64_Rela {
1221 const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx);1278 const raw = try self.preadShdrContentsAlloc(gpa, handle, shndx);
1222 const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela));1279 const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela));
1223 return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];1280 return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
1224}1281}
...@@ -1230,9 +1287,9 @@ const AddAtomArgs = struct {...@@ -1230,9 +1287,9 @@ const AddAtomArgs = struct {
1230 alignment: Alignment,1287 alignment: Alignment,
1231};1288};
12321289
1233fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index {1290fn addAtom(self: *Object, gpa: Allocator, args: AddAtomArgs) !Atom.Index {
1234 try self.atoms.ensureUnusedCapacity(allocator, 1);1291 try self.atoms.ensureUnusedCapacity(gpa, 1);
1235 try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));1292 try self.atoms_extra.ensureUnusedCapacity(gpa, @sizeOf(Atom.Extra));
1236 return self.addAtomAssumeCapacity(args);1293 return self.addAtomAssumeCapacity(args);
1237}1294}
12381295
...@@ -1257,9 +1314,9 @@ pub fn atom(self: *Object, atom_index: Atom.Index) ?*Atom {...@@ -1257,9 +1314,9 @@ pub fn atom(self: *Object, atom_index: Atom.Index) ?*Atom {
1257 return &self.atoms.items[atom_index];1314 return &self.atoms.items[atom_index];
1258}1315}
12591316
1260pub fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 {1317pub fn addAtomExtra(self: *Object, gpa: Allocator, extra: Atom.Extra) !u32 {
1261 const fields = @typeInfo(Atom.Extra).@"struct".fields;1318 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);
1263 return self.addAtomExtraAssumeCapacity(extra);1320 return self.addAtomExtraAssumeCapacity(extra);
1264}1321}
12651322
...@@ -1308,9 +1365,9 @@ fn setAtomFields(o: *Object, atom_ptr: *Atom, opts: Atom.Extra.AsOptionals) void...@@ -1308,9 +1365,9 @@ fn setAtomFields(o: *Object, atom_ptr: *Atom, opts: Atom.Extra.AsOptionals) void
1308 o.setAtomExtra(atom_ptr.extra_index, extras);1365 o.setAtomExtra(atom_ptr.extra_index, extras);
1309}1366}
13101367
1311fn addInputMergeSection(self: *Object, allocator: Allocator) !Merge.InputSection.Index {1368fn addInputMergeSection(self: *Object, gpa: Allocator) !Merge.InputSection.Index {
1312 const index: Merge.InputSection.Index = @intCast(self.input_merge_sections.items.len);1369 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);
1314 msec.* = .{};1371 msec.* = .{};
1315 return index;1372 return index;
1316}1373}
...@@ -1320,9 +1377,9 @@ fn inputMergeSection(self: *Object, index: Merge.InputSection.Index) ?*Merge.Inp...@@ -1320,9 +1377,9 @@ fn inputMergeSection(self: *Object, index: Merge.InputSection.Index) ?*Merge.Inp
1320 return &self.input_merge_sections.items[index];1377 return &self.input_merge_sections.items[index];
1321}1378}
13221379
1323fn addComdatGroup(self: *Object, allocator: Allocator) !Elf.ComdatGroup.Index {1380fn addComdatGroup(self: *Object, gpa: Allocator) !Elf.ComdatGroup.Index {
1324 const index = @as(Elf.ComdatGroup.Index, @intCast(self.comdat_groups.items.len));1381 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);
1326 return index;1383 return index;
1327}1384}
13281385
...@@ -1516,8 +1573,9 @@ const log = std.log.scoped(.link);...@@ -1516,8 +1573,9 @@ const log = std.log.scoped(.link);
1516const math = std.math;1573const math = std.math;
1517const mem = std.mem;1574const mem = std.mem;
1518const Path = std.Build.Cache.Path;1575const Path = std.Build.Cache.Path;
1519const Allocator = mem.Allocator;1576const Allocator = std.mem.Allocator;
15201577
1578const Diags = @import("../../link.zig").Diags;
1521const Archive = @import("Archive.zig");1579const Archive = @import("Archive.zig");
1522const Atom = @import("Atom.zig");1580const Atom = @import("Atom.zig");
1523const AtomList = @import("AtomList.zig");1581const AtomList = @import("AtomList.zig");
...@@ -1528,3 +1586,4 @@ const File = @import("file.zig").File;...@@ -1528,3 +1586,4 @@ const File = @import("file.zig").File;
1528const Merge = @import("Merge.zig");1586const Merge = @import("Merge.zig");
1529const Symbol = @import("Symbol.zig");1587const Symbol = @import("Symbol.zig");
1530const Alignment = Atom.Alignment;1588const Alignment = Atom.Alignment;
1589const riscv = @import("../riscv.zig");
src/link/Elf/ZigObject.zig+12-18
...@@ -928,7 +928,7 @@ pub fn getNavVAddr(...@@ -928,7 +928,7 @@ pub fn getNavVAddr(
928 nav.name.toSlice(ip),928 nav.name.toSlice(ip),
929 @"extern".lib_name.toSlice(ip),929 @"extern".lib_name.toSlice(ip),
930 ),930 ),
931 else => try self.getOrCreateMetadataForNav(elf_file, nav_index),931 else => try self.getOrCreateMetadataForNav(zcu, nav_index),
932 };932 };
933 const this_sym = self.symbol(this_sym_index);933 const this_sym = self.symbol(this_sym_index);
934 const vaddr = this_sym.address(.{}, elf_file);934 const vaddr = this_sym.address(.{}, elf_file);
...@@ -1102,21 +1102,15 @@ pub fn freeNav(self: *ZigObject, elf_file: *Elf, nav_index: InternPool.Nav.Index...@@ -1102,21 +1102,15 @@ pub fn freeNav(self: *ZigObject, elf_file: *Elf, nav_index: InternPool.Nav.Index
1102 }1102 }
1103}1103}
11041104
1105pub fn getOrCreateMetadataForNav(1105pub fn getOrCreateMetadataForNav(self: *ZigObject, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
1106 self: *ZigObject,1106 const gpa = zcu.gpa;
1107 elf_file: *Elf,
1108 nav_index: InternPool.Nav.Index,
1109) !Symbol.Index {
1110 const gpa = elf_file.base.comp.gpa;
1111 const gop = try self.navs.getOrPut(gpa, nav_index);1107 const gop = try self.navs.getOrPut(gpa, nav_index);
1112 if (!gop.found_existing) {1108 if (!gop.found_existing) {
1113 const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded;
1114 const symbol_index = try self.newSymbolWithAtom(gpa, 0);1109 const symbol_index = try self.newSymbolWithAtom(gpa, 0);
1115 const zcu = elf_file.base.comp.zcu.?;
1116 const nav_val = Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val);1110 const nav_val = Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val);
1117 const sym = self.symbol(symbol_index);1111 const sym = self.symbol(symbol_index);
1118 if (nav_val.getVariable(zcu)) |variable| {1112 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) {
1120 sym.flags.is_tls = true;1114 sym.flags.is_tls = true;
1121 }1115 }
1122 }1116 }
...@@ -1425,8 +1419,8 @@ pub fn updateFunc(...@@ -1425,8 +1419,8 @@ pub fn updateFunc(
14251419
1426 log.debug("updateFunc {}({d})", .{ ip.getNav(func.owner_nav).fqn.fmt(ip), func.owner_nav });1420 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);1422 const sym_index = try self.getOrCreateMetadataForNav(zcu, func.owner_nav);
1429 self.symbol(sym_index).atom(elf_file).?.freeRelocs(self);1423 self.atom(self.symbol(sym_index).ref.index).?.freeRelocs(self);
14301424
1431 var code_buffer = std.ArrayList(u8).init(gpa);1425 var code_buffer = std.ArrayList(u8).init(gpa);
1432 defer code_buffer.deinit();1426 defer code_buffer.deinit();
...@@ -1460,12 +1454,12 @@ pub fn updateFunc(...@@ -1460,12 +1454,12 @@ pub fn updateFunc(
1460 ip.getNav(func.owner_nav).fqn.fmt(ip),1454 ip.getNav(func.owner_nav).fqn.fmt(ip),
1461 });1455 });
1462 const old_rva, const old_alignment = blk: {1456 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).?;
1464 break :blk .{ atom_ptr.value, atom_ptr.alignment };1458 break :blk .{ atom_ptr.value, atom_ptr.alignment };
1465 };1459 };
1466 try self.updateNavCode(elf_file, pt, func.owner_nav, sym_index, shndx, code, elf.STT_FUNC);1460 try self.updateNavCode(elf_file, pt, func.owner_nav, sym_index, shndx, code, elf.STT_FUNC);
1467 const new_rva, const new_alignment = blk: {1461 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).?;
1469 break :blk .{ atom_ptr.value, atom_ptr.alignment };1463 break :blk .{ atom_ptr.value, atom_ptr.alignment };
1470 };1464 };
14711465
...@@ -1477,7 +1471,7 @@ pub fn updateFunc(...@@ -1477,7 +1471,7 @@ pub fn updateFunc(
1477 .{1471 .{
1478 .index = sym_index,1472 .index = sym_index,
1479 .addr = @intCast(sym.address(.{}, elf_file)),1473 .addr = @intCast(sym.address(.{}, elf_file)),
1480 .size = sym.atom(elf_file).?.size,1474 .size = self.atom(sym.ref.index).?.size,
1481 },1475 },
1482 wip_nav,1476 wip_nav,
1483 );1477 );
...@@ -1500,7 +1494,7 @@ pub fn updateFunc(...@@ -1500,7 +1494,7 @@ pub fn updateFunc(
1500 });1494 });
1501 defer gpa.free(name);1495 defer gpa.free(name);
1502 const osec = if (self.text_index) |sect_sym_index|1496 const osec = if (self.text_index) |sect_sym_index|
1503 self.symbol(sect_sym_index).atom(elf_file).?.output_section_index1497 self.atom(self.symbol(sect_sym_index).ref.index).?.output_section_index
1504 else osec: {1498 else osec: {
1505 const osec = try elf_file.addSection(.{1499 const osec = try elf_file.addSection(.{
1506 .name = try elf_file.insertShString(".text"),1500 .name = try elf_file.insertShString(".text"),
...@@ -1565,7 +1559,7 @@ pub fn updateNav(...@@ -1565,7 +1559,7 @@ pub fn updateNav(
1565 };1559 };
15661560
1567 if (nav_init != .none and Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {1561 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);
1569 self.symbol(sym_index).atom(elf_file).?.freeRelocs(self);1563 self.symbol(sym_index).atom(elf_file).?.freeRelocs(self);
15701564
1571 var code_buffer = std.ArrayList(u8).init(zcu.gpa);1565 var code_buffer = std.ArrayList(u8).init(zcu.gpa);
...@@ -1789,7 +1783,7 @@ pub fn updateExports(...@@ -1789,7 +1783,7 @@ pub fn updateExports(
1789 const gpa = elf_file.base.comp.gpa;1783 const gpa = elf_file.base.comp.gpa;
1790 const metadata = switch (exported) {1784 const metadata = switch (exported) {
1791 .nav => |nav| blk: {1785 .nav => |nav| blk: {
1792 _ = try self.getOrCreateMetadataForNav(elf_file, nav);1786 _ = try self.getOrCreateMetadataForNav(zcu, nav);
1793 break :blk self.navs.getPtr(nav).?;1787 break :blk self.navs.getPtr(nav).?;
1794 },1788 },
1795 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {1789 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
src/link/Elf/eh_frame.zig+17-20
...@@ -19,18 +19,16 @@ pub const Fde = struct {...@@ -19,18 +19,16 @@ pub const Fde = struct {
19 return base + fde.out_offset;19 return base + fde.out_offset;
20 }20 }
2121
22 pub fn data(fde: Fde, elf_file: *Elf) []u8 {22 pub fn data(fde: Fde, object: *Object) []u8 {
23 const object = elf_file.file(fde.file_index).?.object;
24 return object.eh_frame_data.items[fde.offset..][0..fde.calcSize()];23 return object.eh_frame_data.items[fde.offset..][0..fde.calcSize()];
25 }24 }
2625
27 pub fn cie(fde: Fde, elf_file: *Elf) Cie {26 pub fn cie(fde: Fde, object: *Object) Cie {
28 const object = elf_file.file(fde.file_index).?.object;
29 return object.cies.items[fde.cie_index];27 return object.cies.items[fde.cie_index];
30 }28 }
3129
32 pub fn ciePointer(fde: Fde, elf_file: *Elf) u32 {30 pub fn ciePointer(fde: Fde, object: *Object) u32 {
33 const fde_data = fde.data(elf_file);31 const fde_data = fde.data(object);
34 return std.mem.readInt(u32, fde_data[4..8], .little);32 return std.mem.readInt(u32, fde_data[4..8], .little);
35 }33 }
3634
...@@ -38,16 +36,14 @@ pub const Fde = struct {...@@ -38,16 +36,14 @@ pub const Fde = struct {
38 return fde.size + 4;36 return fde.size + 4;
39 }37 }
4038
41 pub fn atom(fde: Fde, elf_file: *Elf) *Atom {39 pub fn atom(fde: Fde, object: *Object) *Atom {
42 const object = elf_file.file(fde.file_index).?.object;40 const rel = fde.relocs(object)[0];
43 const rel = fde.relocs(elf_file)[0];
44 const sym = object.symtab.items[rel.r_sym()];41 const sym = object.symtab.items[rel.r_sym()];
45 const atom_index = object.atoms_indexes.items[sym.st_shndx];42 const atom_index = object.atoms_indexes.items[sym.st_shndx];
46 return object.atom(atom_index).?;43 return object.atom(atom_index).?;
47 }44 }
4845
49 pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela {46 pub fn relocs(fde: Fde, object: *Object) []const elf.Elf64_Rela {
50 const object = elf_file.file(fde.file_index).?.object;
51 return object.relocs.items[fde.rel_index..][0..fde.rel_num];47 return object.relocs.items[fde.rel_index..][0..fde.rel_num];
52 }48 }
5349
...@@ -87,7 +83,8 @@ pub const Fde = struct {...@@ -87,7 +83,8 @@ pub const Fde = struct {
87 const fde = ctx.fde;83 const fde = ctx.fde;
88 const elf_file = ctx.elf_file;84 const elf_file = ctx.elf_file;
89 const base_addr = fde.address(elf_file);85 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);
91 try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{88 try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{
92 base_addr + fde.out_offset,89 base_addr + fde.out_offset,
93 fde.calcSize(),90 fde.calcSize(),
...@@ -306,7 +303,7 @@ pub fn calcEhFrameRelocs(elf_file: *Elf) usize {...@@ -306,7 +303,7 @@ pub fn calcEhFrameRelocs(elf_file: *Elf) usize {
306 }303 }
307 for (object.fdes.items) |fde| {304 for (object.fdes.items) |fde| {
308 if (!fde.alive) continue;305 if (!fde.alive) continue;
309 count += fde.relocs(elf_file).len;306 count += fde.relocs(object).len;
310 }307 }
311 }308 }
312 return count;309 return count;
...@@ -369,16 +366,16 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {...@@ -369,16 +366,16 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
369 for (object.fdes.items) |fde| {366 for (object.fdes.items) |fde| {
370 if (!fde.alive) continue;367 if (!fde.alive) continue;
371368
372 const contents = fde.data(elf_file);369 const contents = fde.data(object);
373370
374 std.mem.writeInt(371 std.mem.writeInt(
375 i32,372 i32,
376 contents[4..8],373 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))),
378 .little,375 .little,
379 );376 );
380377
381 for (fde.relocs(elf_file)) |rel| {378 for (fde.relocs(object)) |rel| {
382 const ref = object.resolveSymbol(rel.r_sym(), elf_file);379 const ref = object.resolveSymbol(rel.r_sym(), elf_file);
383 const sym = elf_file.symbol(ref).?;380 const sym = elf_file.symbol(ref).?;
384 resolveReloc(fde, sym, rel, elf_file, contents) catch |err| switch (err) {381 resolveReloc(fde, sym, rel, elf_file, contents) catch |err| switch (err) {
...@@ -412,12 +409,12 @@ pub fn writeEhFrameRelocatable(elf_file: *Elf, writer: anytype) !void {...@@ -412,12 +409,12 @@ pub fn writeEhFrameRelocatable(elf_file: *Elf, writer: anytype) !void {
412 for (object.fdes.items) |fde| {409 for (object.fdes.items) |fde| {
413 if (!fde.alive) continue;410 if (!fde.alive) continue;
414411
415 const contents = fde.data(elf_file);412 const contents = fde.data(object);
416413
417 std.mem.writeInt(414 std.mem.writeInt(
418 i32,415 i32,
419 contents[4..8],416 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))),
421 .little,418 .little,
422 );419 );
423420
...@@ -490,7 +487,7 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {...@@ -490,7 +487,7 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
490487
491 for (object.fdes.items) |fde| {488 for (object.fdes.items) |fde| {
492 if (!fde.alive) continue;489 if (!fde.alive) continue;
493 for (fde.relocs(elf_file)) |rel| {490 for (fde.relocs(object)) |rel| {
494 const ref = object.resolveSymbol(rel.r_sym(), elf_file);491 const ref = object.resolveSymbol(rel.r_sym(), elf_file);
495 const sym = elf_file.symbol(ref).?;492 const sym = elf_file.symbol(ref).?;
496 const r_offset = fde.address(elf_file) + rel.r_offset - fde.offset;493 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 {...@@ -548,7 +545,7 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
548 for (object.fdes.items) |fde| {545 for (object.fdes.items) |fde| {
549 if (!fde.alive) continue;546 if (!fde.alive) continue;
550547
551 const relocs = fde.relocs(elf_file);548 const relocs = fde.relocs(object);
552 assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips...549 assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips...
553 const rel = relocs[0];550 const rel = relocs[0];
554 const ref = object.resolveSymbol(rel.r_sym(), elf_file);551 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) {...@@ -279,8 +279,8 @@ pub const File = union(enum) {
279 pub const Index = u32;279 pub const Index = u32;
280280
281 pub const Entry = union(enum) {281 pub const Entry = union(enum) {
282 null: void,282 null,
283 zig_object: ZigObject,283 zig_object,
284 linker_defined: LinkerDefined,284 linker_defined: LinkerDefined,
285 object: Object,285 object: Object,
286 shared_object: SharedObject,286 shared_object: SharedObject,
src/link/Elf/gc.zig+28-21
...@@ -103,15 +103,20 @@ fn markLive(atom: *Atom, elf_file: *Elf) void {...@@ -103,15 +103,20 @@ fn markLive(atom: *Atom, elf_file: *Elf) void {
103 assert(atom.visited);103 assert(atom.visited);
104 const file = atom.file(elf_file).?;104 const file = atom.file(elf_file).?;
105105
106 for (atom.fdes(elf_file)) |fde| {106 switch (file) {
107 for (fde.relocs(elf_file)[1..]) |rel| {107 .object => |object| {
108 const ref = file.resolveSymbol(rel.r_sym(), elf_file);108 for (atom.fdes(object)) |fde| {
109 const target_sym = elf_file.symbol(ref) orelse continue;109 for (fde.relocs(object)[1..]) |rel| {
110 const target_atom = target_sym.atom(elf_file) orelse continue;110 const ref = file.resolveSymbol(rel.r_sym(), elf_file);
111 target_atom.alive = true;111 const target_sym = elf_file.symbol(ref) orelse continue;
112 gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index });112 const target_atom = target_sym.atom(elf_file) orelse continue;
113 if (markAtom(target_atom)) markLive(target_atom, elf_file);113 target_atom.alive = true;
114 }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 => {},
115 }120 }
116121
117 for (atom.relocs(elf_file)) |rel| {122 for (atom.relocs(elf_file)) |rel| {
...@@ -135,23 +140,25 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void {...@@ -135,23 +140,25 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void {
135 }140 }
136}141}
137142
138fn prune(elf_file: *Elf) void {143fn pruneInFile(file: File) void {
139 const pruneInFile = struct {144 for (file.atoms()) |atom_index| {
140 fn pruneInFile(file: File, ef: *Elf) void {145 const atom = file.atom(atom_index) orelse continue;
141 for (file.atoms()) |atom_index| {146 if (atom.alive and !atom.visited) {
142 const atom = file.atom(atom_index) orelse continue;147 atom.alive = false;
143 if (atom.alive and !atom.visited) {148 switch (file) {
144 atom.alive = false;149 .object => |object| atom.markFdesDead(object),
145 atom.markFdesDead(ef);150 else => {},
146 }
147 }151 }
148 }152 }
149 }.pruneInFile;153 }
154}
155
156fn prune(elf_file: *Elf) void {
150 if (elf_file.zigObjectPtr()) |zo| {157 if (elf_file.zigObjectPtr()) |zo| {
151 pruneInFile(zo.asFile(), elf_file);158 pruneInFile(zo.asFile());
152 }159 }
153 for (elf_file.objects.items) |index| {160 for (elf_file.objects.items) |index| {
154 pruneInFile(elf_file.file(index).?, elf_file);161 pruneInFile(elf_file.file(index).?);
155 }162 }
156}163}
157164
src/link/Elf/relocatable.zig+4-94
...@@ -1,27 +1,7 @@...@@ -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 {
2 const gpa = comp.gpa;2 const gpa = comp.gpa;
3 const diags = &comp.link_diags;3 const diags = &comp.link_diags;
44
5 for (comp.objects) |obj| {
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
25 if (diags.hasErrors()) return error.FlushFailure;5 if (diags.hasErrors()) return error.FlushFailure;
266
27 // First, we flush relocatable object file generated with our backends.7 // 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...@@ -150,22 +130,9 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path
150 if (diags.hasErrors()) return error.FlushFailure;130 if (diags.hasErrors()) return error.FlushFailure;
151}131}
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 {
154 const diags = &comp.link_diags;134 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
169 if (diags.hasErrors()) return error.FlushFailure;136 if (diags.hasErrors()) return error.FlushFailure;
170137
171 // Now, we are ready to resolve the symbols across all input files.138 // 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...@@ -215,64 +182,6 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) l
215 if (diags.hasErrors()) return error.FlushFailure;182 if (diags.hasErrors()) return error.FlushFailure;
216}183}
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
276fn claimUnresolved(elf_file: *Elf) void {185fn claimUnresolved(elf_file: *Elf) void {
277 if (elf_file.zigObjectPtr()) |zig_object| {186 if (elf_file.zigObjectPtr()) |zig_object| {
278 zig_object.claimUnresolvedRelocatable(elf_file);187 zig_object.claimUnresolvedRelocatable(elf_file);
...@@ -473,11 +382,12 @@ fn writeSyntheticSections(elf_file: *Elf) !void {...@@ -473,11 +382,12 @@ fn writeSyntheticSections(elf_file: *Elf) !void {
473 const SortRelocs = struct {382 const SortRelocs = struct {
474 pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {383 pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
475 _ = ctx;384 _ = ctx;
385 assert(lhs.r_offset != rhs.r_offset);
476 return lhs.r_offset < rhs.r_offset;386 return lhs.r_offset < rhs.r_offset;
477 }387 }
478 };388 };
479389
480 mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);390 mem.sortUnstable(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
481391
482 log.debug("writing {s} from 0x{x} to 0x{x}", .{392 log.debug("writing {s} from 0x{x} to 0x{x}", .{
483 elf_file.getShString(shdr.sh_name),393 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 @@...@@ -1,3 +1,7 @@
1pub const Atom = @import("MachO/Atom.zig");
2pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
3pub const Relocation = @import("MachO/Relocation.zig");
4
1base: link.File,5base: link.File,
26
3rpath_list: []const []const u8,7rpath_list: []const []const u8,
...@@ -114,8 +118,8 @@ headerpad_max_install_names: bool,...@@ -114,8 +118,8 @@ headerpad_max_install_names: bool,
114dead_strip_dylibs: bool,118dead_strip_dylibs: bool,
115/// Treatment of undefined symbols119/// Treatment of undefined symbols
116undefined_treatment: UndefinedTreatment,120undefined_treatment: UndefinedTreatment,
117/// Resolved list of library search directories121/// TODO: delete this, libraries need to be resolved by the frontend instead
118lib_dirs: []const []const u8,122lib_directories: []const Directory,
119/// Resolved list of framework search directories123/// Resolved list of framework search directories
120framework_dirs: []const []const u8,124framework_dirs: []const []const u8,
121/// List of input frameworks125/// List of input frameworks
...@@ -213,7 +217,8 @@ pub fn createEmpty(...@@ -213,7 +217,8 @@ pub fn createEmpty(
213 .platform = Platform.fromTarget(target),217 .platform = Platform.fromTarget(target),
214 .sdk_version = if (options.darwin_sdk_layout) |layout| inferSdkVersion(comp, layout) else null,218 .sdk_version = if (options.darwin_sdk_layout) |layout| inferSdkVersion(comp, layout) else null,
215 .undefined_treatment = if (allow_shlib_undefined) .dynamic_lookup else .@"error",219 .undefined_treatment = if (allow_shlib_undefined) .dynamic_lookup else .@"error",
216 .lib_dirs = options.lib_dirs,220 // TODO delete this, directories must instead be resolved by the frontend
221 .lib_directories = options.lib_directories,
217 .framework_dirs = options.framework_dirs,222 .framework_dirs = options.framework_dirs,
218 .force_load_objc = options.force_load_objc,223 .force_load_objc = options.force_load_objc,
219 };224 };
...@@ -371,48 +376,44 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -371,48 +376,44 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
371 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);376 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
372 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);377 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
373378
374 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);379 var positionals = std.ArrayList(link.Input).init(gpa);
375 defer positionals.deinit();380 defer positionals.deinit();
376381
377 try positionals.ensureUnusedCapacity(comp.objects.len);382 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
378 positionals.appendSliceAssumeCapacity(comp.objects);383
384 for (comp.link_inputs) |link_input| switch (link_input) {
385 .dso => continue, // handled below
386 .object, .archive => positionals.appendAssumeCapacity(link_input),
387 .dso_exact => @panic("TODO"),
388 .res => unreachable,
389 };
379390
380 // This is a set of object files emitted by clang in a single `build-exe` invocation.391 // This is a set of object files emitted by clang in a single `build-exe` invocation.
381 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up392 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
382 // in this set.393 // in this set.
383 try positionals.ensureUnusedCapacity(comp.c_object_table.keys().len);394 try positionals.ensureUnusedCapacity(comp.c_object_table.keys().len);
384 for (comp.c_object_table.keys()) |key| {395 for (comp.c_object_table.keys()) |key| {
385 positionals.appendAssumeCapacity(.{ .path = key.status.success.object_path });396 positionals.appendAssumeCapacity(try link.openObjectInput(diags, key.status.success.object_path));
386 }397 }
387398
388 if (module_obj_path) |path| try positionals.append(.{ .path = path });399 if (module_obj_path) |path| try positionals.append(try link.openObjectInput(diags, path));
389400
390 if (comp.config.any_sanitize_thread) {401 if (comp.config.any_sanitize_thread) {
391 try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path });402 try positionals.append(try link.openObjectInput(diags, comp.tsan_lib.?.full_object_path));
392 }403 }
393404
394 if (comp.config.any_fuzz) {405 if (comp.config.any_fuzz) {
395 try positionals.append(.{ .path = comp.fuzzer_lib.?.full_object_path });406 try positionals.append(try link.openObjectInput(diags, comp.fuzzer_lib.?.full_object_path));
396 }407 }
397408
398 for (positionals.items) |obj| {409 for (positionals.items) |link_input| {
399 self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err|410 self.classifyInputFile(link_input) catch |err|
400 diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)});411 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
401 }412 }
402413
403 var system_libs = std.ArrayList(SystemLib).init(gpa);414 var system_libs = std.ArrayList(SystemLib).init(gpa);
404 defer system_libs.deinit();415 defer system_libs.deinit();
405416
406 // libs
407 try system_libs.ensureUnusedCapacity(comp.system_libs.values().len);
408 for (comp.system_libs.values()) |info| {
409 system_libs.appendAssumeCapacity(.{
410 .needed = info.needed,
411 .weak = info.weak,
412 .path = info.path.?,
413 });
414 }
415
416 // frameworks417 // frameworks
417 try system_libs.ensureUnusedCapacity(self.frameworks.len);418 try system_libs.ensureUnusedCapacity(self.frameworks.len);
418 for (self.frameworks) |info| {419 for (self.frameworks) |info| {
...@@ -436,20 +437,40 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -436,20 +437,40 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
436 else => |e| return e, // TODO: convert into an error437 else => |e| return e, // TODO: convert into an error
437 };438 };
438439
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
439 for (system_libs.items) |lib| {449 for (system_libs.items) |lib| {
440 self.classifyInputFile(lib.path, lib, false) catch |err|450 switch (Compilation.classifyFileExt(lib.path.sub_path)) {
441 diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)});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 }
442 }463 }
443464
444 // Finally, link against compiler_rt.465 // Finally, link against compiler_rt.
445 const compiler_rt_path: ?Path = blk: {466 if (comp.compiler_rt_lib) |crt_file| {
446 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;467 const path = crt_file.full_object_path;
447 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;468 self.classifyInputFile(try link.openArchiveInput(diags, path, false, false)) catch |err|
448 break :blk null;469 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
449 };470 } else if (comp.compiler_rt_obj) |crt_file| {
450 if (compiler_rt_path) |path| {471 const path = crt_file.full_object_path;
451 self.classifyInputFile(path, .{ .path = path }, false) catch |err|472 self.classifyInputFile(try link.openObjectInput(diags, path)) catch |err|
452 diags.addParseError(path, "failed to parse input file: {s}", .{@errorName(err)});473 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
453 }474 }
454475
455 try self.parseInputFiles();476 try self.parseInputFiles();
...@@ -596,9 +617,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -596,9 +617,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
596 }617 }
597618
598 if (self.base.isRelocatable()) {619 if (self.base.isRelocatable()) {
599 for (comp.objects) |obj| {620 for (comp.link_inputs) |link_input| switch (link_input) {
600 try argv.append(try obj.path.toString(arena));621 .object, .archive => |obj| try argv.append(try obj.path.toString(arena)),
601 }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
603 for (comp.c_object_table.keys()) |key| {627 for (comp.c_object_table.keys()) |key| {
604 try argv.append(try key.status.success.object_path.toString(arena));628 try argv.append(try key.status.success.object_path.toString(arena));
...@@ -678,13 +702,15 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -678,13 +702,15 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
678 try argv.append("dynamic_lookup");702 try argv.append("dynamic_lookup");
679 }703 }
680704
681 for (comp.objects) |obj| {705 for (comp.link_inputs) |link_input| switch (link_input) {
682 // TODO: verify this706 .dso => continue, // handled below
683 if (obj.must_link) {707 .res => unreachable, // windows only
684 try argv.append("-force_load");708 .object, .archive => |obj| {
685 }709 if (obj.must_link) try argv.append("-force_load"); // TODO: verify this
686 try argv.append(try obj.path.toString(arena));710 try argv.append(try obj.path.toString(arena));
687 }711 },
712 .dso_exact => |dso_exact| try argv.appendSlice(&.{ "-l", dso_exact.name }),
713 };
688714
689 for (comp.c_object_table.keys()) |key| {715 for (comp.c_object_table.keys()) |key| {
690 try argv.append(try key.status.success.object_path.toString(arena));716 try argv.append(try key.status.success.object_path.toString(arena));
...@@ -703,21 +729,25 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -703,21 +729,25 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
703 try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));729 try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));
704 }730 }
705731
706 for (self.lib_dirs) |lib_dir| {732 for (self.lib_directories) |lib_directory| {
707 const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir});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 "."});
708 try argv.append(arg);735 try argv.append(arg);
709 }736 }
710737
711 for (comp.system_libs.keys()) |l_name| {738 for (comp.link_inputs) |link_input| switch (link_input) {
712 const info = comp.system_libs.get(l_name).?;739 .object, .archive, .dso_exact => continue, // handled above
713 const arg = if (info.needed)740 .res => unreachable, // windows only
714 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})741 .dso => |dso| {
715 else if (info.weak)742 if (dso.needed) {
716 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})743 try argv.appendSlice(&.{ "-needed-l", try dso.path.toString(arena) });
717 else744 } else if (dso.weak) {
718 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});745 try argv.appendSlice(&.{ "-weak-l", try dso.path.toString(arena) });
719 try argv.append(arg);746 } else {
720 }747 try argv.appendSlice(&.{ "-l", try dso.path.toString(arena) });
748 }
749 },
750 };
721751
722 for (self.framework_dirs) |f_dir| {752 for (self.framework_dirs) |f_dir| {
723 try argv.append("-F");753 try argv.append("-F");
...@@ -751,6 +781,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -751,6 +781,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
751 Compilation.dump_argv(argv.items);781 Compilation.dump_argv(argv.items);
752}782}
753783
784/// TODO delete this, libsystem must be resolved when setting up the compilation pipeline
754pub fn resolveLibSystem(785pub fn resolveLibSystem(
755 self: *MachO,786 self: *MachO,
756 arena: Allocator,787 arena: Allocator,
...@@ -774,8 +805,8 @@ pub fn resolveLibSystem(...@@ -774,8 +805,8 @@ pub fn resolveLibSystem(
774 },805 },
775 };806 };
776807
777 for (self.lib_dirs) |dir| {808 for (self.lib_directories) |directory| {
778 if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success;809 if (try accessLibPath(arena, &test_path, &checked_paths, directory.path orelse ".", "System")) break :success;
779 }810 }
780811
781 diags.addMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{});812 diags.addMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{});
...@@ -789,13 +820,14 @@ pub fn resolveLibSystem(...@@ -789,13 +820,14 @@ pub fn resolveLibSystem(
789 });820 });
790}821}
791822
792pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bool) !void {823pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
793 const tracy = trace(@src());824 const tracy = trace(@src());
794 defer tracy.end();825 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
796 log.debug("classifying input file {}", .{path});829 log.debug("classifying input file {}", .{path});
797830
798 const file = try path.root_dir.handle.openFile(path.sub_path, .{});
799 const fh = try self.addFileHandle(file);831 const fh = try self.addFileHandle(file);
800 var buffer: [Archive.SARMAG]u8 = undefined;832 var buffer: [Archive.SARMAG]u8 = undefined;
801833
...@@ -806,17 +838,17 @@ pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bo...@@ -806,17 +838,17 @@ pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bo
806 if (h.magic != macho.MH_MAGIC_64) break :blk;838 if (h.magic != macho.MH_MAGIC_64) break :blk;
807 switch (h.filetype) {839 switch (h.filetype) {
808 macho.MH_OBJECT => try self.addObject(path, fh, offset),840 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),
810 else => return error.UnknownFileType,842 else => return error.UnknownFileType,
811 }843 }
812 return;844 return;
813 }845 }
814 if (readArMagic(file, offset, &buffer) catch null) |ar_magic| blk: {846 if (readArMagic(file, offset, &buffer) catch null) |ar_magic| blk: {
815 if (!mem.eql(u8, ar_magic, Archive.ARMAG)) break :blk;847 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);
817 return;849 return;
818 }850 }
819 _ = try self.addTbd(lib, true, fh);851 _ = try self.addTbd(.fromLinkInput(input), true, fh);
820}852}
821853
822fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch {854fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch {
...@@ -903,7 +935,7 @@ fn parseInputFileWorker(self: *MachO, file: File) void {...@@ -903,7 +935,7 @@ fn parseInputFileWorker(self: *MachO, file: File) void {
903 };935 };
904}936}
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 {
907 const tracy = trace(@src());939 const tracy = trace(@src());
908 defer tracy.end();940 defer tracy.end();
909941
...@@ -918,7 +950,7 @@ fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.Handle...@@ -918,7 +950,7 @@ fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.Handle
918 self.files.set(index, .{ .object = unpacked });950 self.files.set(index, .{ .object = unpacked });
919 const object = &self.files.items(.data)[index].object;951 const object = &self.files.items(.data)[index].object;
920 object.index = index;952 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;
922 object.hidden = lib.hidden;954 object.hidden = lib.hidden;
923 try self.objects.append(gpa, index);955 try self.objects.append(gpa, index);
924 }956 }
...@@ -993,6 +1025,7 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {...@@ -993,6 +1025,7 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
993 return false;1025 return false;
994}1026}
9951027
1028/// TODO delete this, libraries must be instead resolved when instantiating the compilation pipeline
996fn accessLibPath(1029fn accessLibPath(
997 arena: Allocator,1030 arena: Allocator,
998 test_path: *std.ArrayList(u8),1031 test_path: *std.ArrayList(u8),
...@@ -1051,9 +1084,11 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1051,9 +1084,11 @@ fn parseDependentDylibs(self: *MachO) !void {
1051 if (self.dylibs.items.len == 0) return;1084 if (self.dylibs.items.len == 0) return;
10521085
1053 const gpa = self.base.comp.gpa;1086 const gpa = self.base.comp.gpa;
1054 const lib_dirs = self.lib_dirs;
1055 const framework_dirs = self.framework_dirs;1087 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
1057 var arena_alloc = std.heap.ArenaAllocator.init(gpa);1092 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
1058 defer arena_alloc.deinit();1093 defer arena_alloc.deinit();
1059 const arena = arena_alloc.allocator();1094 const arena = arena_alloc.allocator();
...@@ -1094,9 +1129,9 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1094,9 +1129,9 @@ fn parseDependentDylibs(self: *MachO) !void {
10941129
1095 // Library1130 // Library
1096 const lib_name = eatPrefix(stem, "lib") orelse stem;1131 const lib_name = eatPrefix(stem, "lib") orelse stem;
1097 for (lib_dirs) |dir| {1132 for (lib_directories) |lib_directory| {
1098 test_path.clearRetainingCapacity();1133 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;
1100 }1135 }
1101 }1136 }
11021137
...@@ -4366,6 +4401,24 @@ const SystemLib = struct {...@@ -4366,6 +4401,24 @@ const SystemLib = struct {
4366 hidden: bool = false,4401 hidden: bool = false,
4367 reexport: bool = false,4402 reexport: bool = false,
4368 must_link: bool = false,4403 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 }
4369};4422};
43704423
4371pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout;4424pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout;
...@@ -5303,17 +5356,16 @@ const Air = @import("../Air.zig");...@@ -5303,17 +5356,16 @@ const Air = @import("../Air.zig");
5303const Alignment = Atom.Alignment;5356const Alignment = Atom.Alignment;
5304const Allocator = mem.Allocator;5357const Allocator = mem.Allocator;
5305const Archive = @import("MachO/Archive.zig");5358const Archive = @import("MachO/Archive.zig");
5306pub const Atom = @import("MachO/Atom.zig");
5307const AtomicBool = std.atomic.Value(bool);5359const AtomicBool = std.atomic.Value(bool);
5308const Bind = bind.Bind;5360const Bind = bind.Bind;
5309const Cache = std.Build.Cache;5361const Cache = std.Build.Cache;
5310const Path = Cache.Path;
5311const CodeSignature = @import("MachO/CodeSignature.zig");5362const CodeSignature = @import("MachO/CodeSignature.zig");
5312const Compilation = @import("../Compilation.zig");5363const Compilation = @import("../Compilation.zig");
5313const DataInCode = synthetic.DataInCode;5364const DataInCode = synthetic.DataInCode;
5314pub const DebugSymbols = @import("MachO/DebugSymbols.zig");5365const Directory = Cache.Directory;
5315const Dylib = @import("MachO/Dylib.zig");5366const Dylib = @import("MachO/Dylib.zig");
5316const ExportTrie = @import("MachO/dyld_info/Trie.zig");5367const ExportTrie = @import("MachO/dyld_info/Trie.zig");
5368const Path = Cache.Path;
5317const File = @import("MachO/file.zig").File;5369const File = @import("MachO/file.zig").File;
5318const GotSection = synthetic.GotSection;5370const GotSection = synthetic.GotSection;
5319const Hash = std.hash.Wyhash;5371const Hash = std.hash.Wyhash;
...@@ -5329,7 +5381,6 @@ const Md5 = std.crypto.hash.Md5;...@@ -5329,7 +5381,6 @@ const Md5 = std.crypto.hash.Md5;
5329const Zcu = @import("../Zcu.zig");5381const Zcu = @import("../Zcu.zig");
5330const InternPool = @import("../InternPool.zig");5382const InternPool = @import("../InternPool.zig");
5331const Rebase = @import("MachO/dyld_info/Rebase.zig");5383const Rebase = @import("MachO/dyld_info/Rebase.zig");
5332pub const Relocation = @import("MachO/Relocation.zig");
5333const StringTable = @import("StringTable.zig");5384const StringTable = @import("StringTable.zig");
5334const StubsSection = synthetic.StubsSection;5385const StubsSection = synthetic.StubsSection;
5335const StubsHelperSection = synthetic.StubsHelperSection;5386const StubsHelperSection = synthetic.StubsHelperSection;
src/link/MachO/relocatable.zig+27-26
...@@ -3,16 +3,16 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -3,16 +3,16 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
3 const diags = &macho_file.base.comp.link_diags;3 const diags = &macho_file.base.comp.link_diags;
44
5 // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.5 // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.
6 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);6 var positionals = std.ArrayList(link.Input).init(gpa);
7 defer positionals.deinit();7 defer positionals.deinit();
8 try positionals.ensureUnusedCapacity(comp.objects.len);8 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
9 positionals.appendSliceAssumeCapacity(comp.objects);9 positionals.appendSliceAssumeCapacity(comp.link_inputs);
1010
11 for (comp.c_object_table.keys()) |key| {11 for (comp.c_object_table.keys()) |key| {
12 try positionals.append(.{ .path = key.status.success.object_path });12 try positionals.append(try link.openObjectInput(diags, key.status.success.object_path));
13 }13 }
1414
15 if (module_obj_path) |path| try positionals.append(.{ .path = path });15 if (module_obj_path) |path| try positionals.append(try link.openObjectInput(diags, path));
1616
17 if (macho_file.getZigObject() == null and positionals.items.len == 1) {17 if (macho_file.getZigObject() == null and positionals.items.len == 1) {
18 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all18 // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all
...@@ -20,7 +20,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -20,7 +20,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
20 // the *only* input file over.20 // the *only* input file over.
21 // TODO: in the future, when we implement `dsymutil` alternative directly in the Zig21 // TODO: in the future, when we implement `dsymutil` alternative directly in the Zig
22 // compiler, investigate if we can get rid of this `if` prong here.22 // compiler, investigate if we can get rid of this `if` prong here.
23 const path = positionals.items[0].path;23 const path = positionals.items[0].path().?;
24 const in_file = try path.root_dir.handle.openFile(path.sub_path, .{});24 const in_file = try path.root_dir.handle.openFile(path.sub_path, .{});
25 const stat = try in_file.stat();25 const stat = try in_file.stat();
26 const amt = try in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size);26 const amt = try in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size);
...@@ -28,9 +28,9 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -28,9 +28,9 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
28 return;28 return;
29 }29 }
3030
31 for (positionals.items) |obj| {31 for (positionals.items) |link_input| {
32 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err|32 macho_file.classifyInputFile(link_input) catch |err|
33 diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)});33 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
34 }34 }
3535
36 if (diags.hasErrors()) return error.FlushFailure;36 if (diags.hasErrors()) return error.FlushFailure;
...@@ -72,25 +72,25 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -72,25 +72,25 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
72 const gpa = comp.gpa;72 const gpa = comp.gpa;
73 const diags = &macho_file.base.comp.link_diags;73 const diags = &macho_file.base.comp.link_diags;
7474
75 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);75 var positionals = std.ArrayList(link.Input).init(gpa);
76 defer positionals.deinit();76 defer positionals.deinit();
7777
78 try positionals.ensureUnusedCapacity(comp.objects.len);78 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
79 positionals.appendSliceAssumeCapacity(comp.objects);79 positionals.appendSliceAssumeCapacity(comp.link_inputs);
8080
81 for (comp.c_object_table.keys()) |key| {81 for (comp.c_object_table.keys()) |key| {
82 try positionals.append(.{ .path = key.status.success.object_path });82 try positionals.append(try link.openObjectInput(diags, key.status.success.object_path));
83 }83 }
8484
85 if (module_obj_path) |path| try positionals.append(.{ .path = path });85 if (module_obj_path) |path| try positionals.append(try link.openObjectInput(diags, path));
8686
87 if (comp.include_compiler_rt) {87 if (comp.include_compiler_rt) {
88 try positionals.append(.{ .path = comp.compiler_rt_obj.?.full_object_path });88 try positionals.append(try link.openObjectInput(diags, comp.compiler_rt_obj.?.full_object_path));
89 }89 }
9090
91 for (positionals.items) |obj| {91 for (positionals.items) |link_input| {
92 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err|92 macho_file.classifyInputFile(link_input) catch |err|
93 diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)});93 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
94 }94 }
9595
96 if (diags.hasErrors()) return error.FlushFailure;96 if (diags.hasErrors()) return error.FlushFailure;
...@@ -745,20 +745,15 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {...@@ -745,20 +745,15 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {
745 try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0);745 try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0);
746}746}
747747
748const std = @import("std");
749const Path = std.Build.Cache.Path;
750const WaitGroup = std.Thread.WaitGroup;
748const assert = std.debug.assert;751const assert = std.debug.assert;
749const build_options = @import("build_options");
750const eh_frame = @import("eh_frame.zig");
751const fat = @import("fat.zig");
752const link = @import("../../link.zig");
753const load_commands = @import("load_commands.zig");
754const log = std.log.scoped(.link);752const log = std.log.scoped(.link);
755const macho = std.macho;753const macho = std.macho;
756const math = std.math;754const math = std.math;
757const mem = std.mem;755const mem = std.mem;
758const state_log = std.log.scoped(.link_state);756const state_log = std.log.scoped(.link_state);
759const std = @import("std");
760const trace = @import("../../tracy.zig").trace;
761const Path = std.Build.Cache.Path;
762757
763const Archive = @import("Archive.zig");758const Archive = @import("Archive.zig");
764const Atom = @import("Atom.zig");759const Atom = @import("Atom.zig");
...@@ -767,3 +762,9 @@ const File = @import("file.zig").File;...@@ -767,3 +762,9 @@ const File = @import("file.zig").File;
767const MachO = @import("../MachO.zig");762const MachO = @import("../MachO.zig");
768const Object = @import("Object.zig");763const Object = @import("Object.zig");
769const Symbol = @import("Symbol.zig");764const Symbol = @import("Symbol.zig");
765const build_options = @import("build_options");
766const eh_frame = @import("eh_frame.zig");
767const fat = @import("fat.zig");
768const link = @import("../../link.zig");
769const load_commands = @import("load_commands.zig");
770const trace = @import("../../tracy.zig").trace;
src/link/Wasm.zig+33-34
...@@ -637,14 +637,6 @@ fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !...@@ -637,14 +637,6 @@ fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !
637 return loc;637 return loc;
638}638}
639639
640fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void {
641 for (files) |path| {
642 if (try wasm.parseObjectFile(path)) continue;
643 if (try wasm.parseArchive(path, false)) continue; // load archives lazily
644 log.warn("Unexpected file format at path: '{s}'", .{path});
645 }
646}
647
648/// Parses the object file from given path. Returns true when the given file was an object640/// Parses the object file from given path. Returns true when the given file was an object
649/// file and parsed successfully. Returns false when file is not an object file.641/// file and parsed successfully. Returns false when file is not an object file.
650/// May return an error instead when parsing failed.642/// May return an error instead when parsing failed.
...@@ -2522,7 +2514,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2522,7 +2514,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2522 // Positional arguments to the linker such as object files and static archives.2514 // Positional arguments to the linker such as object files and static archives.
2523 // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.2515 // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.
2524 var positionals = std.ArrayList([]const u8).init(arena);2516 var positionals = std.ArrayList([]const u8).init(arena);
2525 try positionals.ensureUnusedCapacity(comp.objects.len);2517 try positionals.ensureUnusedCapacity(comp.link_inputs.len);
25262518
2527 const target = comp.root_mod.resolved_target.result;2519 const target = comp.root_mod.resolved_target.result;
2528 const output_mode = comp.config.output_mode;2520 const output_mode = comp.config.output_mode;
...@@ -2566,9 +2558,12 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2566,9 +2558,12 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2566 try positionals.append(path);2558 try positionals.append(path);
2567 }2559 }
25682560
2569 for (comp.objects) |object| {2561 for (comp.link_inputs) |link_input| switch (link_input) {
2570 try positionals.append(try object.path.toString(arena));2562 .object, .archive => |obj| try positionals.append(try obj.path.toString(arena)),
2571 }2563 .dso => |dso| try positionals.append(try dso.path.toString(arena)),
2564 .dso_exact => unreachable, // forbidden by frontend
2565 .res => unreachable, // windows only
2566 };
25722567
2573 for (comp.c_object_table.keys()) |c_object| {2568 for (comp.c_object_table.keys()) |c_object| {
2574 try positionals.append(try c_object.status.success.object_path.toString(arena));2569 try positionals.append(try c_object.status.success.object_path.toString(arena));
...@@ -2577,7 +2572,11 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2577,7 +2572,11 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2577 if (comp.compiler_rt_lib) |lib| try positionals.append(try lib.full_object_path.toString(arena));2572 if (comp.compiler_rt_lib) |lib| try positionals.append(try lib.full_object_path.toString(arena));
2578 if (comp.compiler_rt_obj) |obj| try positionals.append(try obj.full_object_path.toString(arena));2573 if (comp.compiler_rt_obj) |obj| try positionals.append(try obj.full_object_path.toString(arena));
25792574
2580 try wasm.parseInputFiles(positionals.items);2575 for (positionals.items) |path| {
2576 if (try wasm.parseObjectFile(path)) continue;
2577 if (try wasm.parseArchive(path, false)) continue; // load archives lazily
2578 log.warn("Unexpected file format at path: '{s}'", .{path});
2579 }
25812580
2582 if (wasm.zig_object_index != .null) {2581 if (wasm.zig_object_index != .null) {
2583 try wasm.resolveSymbolsInObject(wasm.zig_object_index);2582 try wasm.resolveSymbolsInObject(wasm.zig_object_index);
...@@ -3401,10 +3400,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3401,10 +3400,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
34013400
3402 comptime assert(Compilation.link_hash_implementation_version == 14);3401 comptime assert(Compilation.link_hash_implementation_version == 14);
34033402
3404 for (comp.objects) |obj| {3403 try link.hashInputs(&man, comp.link_inputs);
3405 _ = try man.addFilePath(obj.path, null);
3406 man.hash.add(obj.must_link);
3407 }
3408 for (comp.c_object_table.keys()) |key| {3404 for (comp.c_object_table.keys()) |key| {
3409 _ = try man.addFilePath(key.status.success.object_path, null);3405 _ = try man.addFilePath(key.status.success.object_path, null);
3410 }3406 }
...@@ -3458,8 +3454,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3458,8 +3454,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
3458 // here. TODO: think carefully about how we can avoid this redundant operation when doing3454 // here. TODO: think carefully about how we can avoid this redundant operation when doing
3459 // build-obj. See also the corresponding TODO in linkAsArchive.3455 // build-obj. See also the corresponding TODO in linkAsArchive.
3460 const the_object_path = blk: {3456 const the_object_path = blk: {
3461 if (comp.objects.len != 0)3457 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
3462 break :blk comp.objects[0].path;
34633458
3464 if (comp.c_object_table.count() != 0)3459 if (comp.c_object_table.count() != 0)
3465 break :blk comp.c_object_table.keys()[0].status.success.object_path;3460 break :blk comp.c_object_table.keys()[0].status.success.object_path;
...@@ -3621,16 +3616,23 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3621,16 +3616,23 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
36213616
3622 // Positional arguments to the linker such as object files.3617 // Positional arguments to the linker such as object files.
3623 var whole_archive = false;3618 var whole_archive = false;
3624 for (comp.objects) |obj| {3619 for (comp.link_inputs) |link_input| switch (link_input) {
3625 if (obj.must_link and !whole_archive) {3620 .object, .archive => |obj| {
3626 try argv.append("-whole-archive");3621 if (obj.must_link and !whole_archive) {
3627 whole_archive = true;3622 try argv.append("-whole-archive");
3628 } else if (!obj.must_link and whole_archive) {3623 whole_archive = true;
3629 try argv.append("-no-whole-archive");3624 } else if (!obj.must_link and whole_archive) {
3630 whole_archive = false;3625 try argv.append("-no-whole-archive");
3631 }3626 whole_archive = false;
3632 try argv.append(try obj.path.toString(arena));3627 }
3633 }3628 try argv.append(try obj.path.toString(arena));
3629 },
3630 .dso => |dso| {
3631 try argv.append(try dso.path.toString(arena));
3632 },
3633 .dso_exact => unreachable,
3634 .res => unreachable,
3635 };
3634 if (whole_archive) {3636 if (whole_archive) {
3635 try argv.append("-no-whole-archive");3637 try argv.append("-no-whole-archive");
3636 whole_archive = false;3638 whole_archive = false;
...@@ -3643,11 +3645,8 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3643,11 +3645,8 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
3643 try argv.append(p);3645 try argv.append(p);
3644 }3646 }
36453647
3646 if (comp.config.output_mode != .Obj and3648 if (comp.libc_static_lib) |crt_file| {
3647 !comp.skip_linker_dependencies and3649 try argv.append(try crt_file.full_object_path.toString(arena));
3648 !comp.config.link_libc)
3649 {
3650 try argv.append(try comp.libc_static_lib.?.full_object_path.toString(arena));
3651 }3650 }
36523651
3653 if (compiler_rt_path) |p| {3652 if (compiler_rt_path) |p| {
src/main.zig+322-469
...@@ -15,6 +15,7 @@ const cleanExit = std.process.cleanExit;...@@ -15,6 +15,7 @@ const cleanExit = std.process.cleanExit;
15const native_os = builtin.os.tag;15const native_os = builtin.os.tag;
16const Cache = std.Build.Cache;16const Cache = std.Build.Cache;
17const Path = std.Build.Cache.Path;17const Path = std.Build.Cache.Path;
18const Directory = std.Build.Cache.Directory;
18const EnvVar = std.zig.EnvVar;19const EnvVar = std.zig.EnvVar;
19const LibCInstallation = std.zig.LibCInstallation;20const LibCInstallation = std.zig.LibCInstallation;
20const AstGen = std.zig.AstGen;21const AstGen = std.zig.AstGen;
...@@ -55,7 +56,7 @@ pub fn wasi_cwd() std.os.wasi.fd_t {...@@ -55,7 +56,7 @@ pub fn wasi_cwd() std.os.wasi.fd_t {
55 return cwd_fd;56 return cwd_fd;
56}57}
5758
58fn getWasiPreopen(name: []const u8) Compilation.Directory {59fn getWasiPreopen(name: []const u8) Directory {
59 return .{60 return .{
60 .path = name,61 .path = name,
61 .handle = .{62 .handle = .{
...@@ -555,6 +556,8 @@ const usage_build_generic =...@@ -555,6 +556,8 @@ const usage_build_generic =
555 \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library556 \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library
556 \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries557 \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries
557 \\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries558 \\ -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
558 \\ --build-id[=style] At a minor link-time expense, coordinates stripped binaries561 \\ --build-id[=style] At a minor link-time expense, coordinates stripped binaries
559 \\ fast, uuid, sha1, md5 with debug symbols via a '.note.gnu.build-id' section562 \\ fast, uuid, sha1, md5 with debug symbols via a '.note.gnu.build-id' section
560 \\ 0x[hexstring] Maximum 32 bytes563 \\ 0x[hexstring] Maximum 32 bytes
...@@ -766,27 +769,6 @@ const ArgsIterator = struct {...@@ -766,27 +769,6 @@ const ArgsIterator = struct {
766 }769 }
767};770};
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
790/// Similar to `link.Framework` except it doesn't store yet unresolved772/// Similar to `link.Framework` except it doesn't store yet unresolved
791/// path to the framework.773/// path to the framework.
792const Framework = struct {774const Framework = struct {
...@@ -867,6 +849,7 @@ fn buildOutputType(...@@ -867,6 +849,7 @@ fn buildOutputType(
867 var linker_gc_sections: ?bool = null;849 var linker_gc_sections: ?bool = null;
868 var linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null;850 var linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null;
869 var linker_allow_shlib_undefined: ?bool = null;851 var linker_allow_shlib_undefined: ?bool = null;
852 var allow_so_scripts: bool = false;
870 var linker_bind_global_refs_locally: ?bool = null;853 var linker_bind_global_refs_locally: ?bool = null;
871 var linker_import_symbols: bool = false;854 var linker_import_symbols: bool = false;
872 var linker_import_table: bool = false;855 var linker_import_table: bool = false;
...@@ -919,7 +902,7 @@ fn buildOutputType(...@@ -919,7 +902,7 @@ fn buildOutputType(
919 var hash_style: link.File.Elf.HashStyle = .both;902 var hash_style: link.File.Elf.HashStyle = .both;
920 var entitlements: ?[]const u8 = null;903 var entitlements: ?[]const u8 = null;
921 var pagezero_size: ?u64 = null;904 var pagezero_size: ?u64 = null;
922 var lib_search_strategy: SystemLib.SearchStrategy = .paths_first;905 var lib_search_strategy: link.UnresolvedInput.SearchStrategy = .paths_first;
923 var lib_preferred_mode: std.builtin.LinkMode = .dynamic;906 var lib_preferred_mode: std.builtin.LinkMode = .dynamic;
924 var headerpad_size: ?u32 = null;907 var headerpad_size: ?u32 = null;
925 var headerpad_max_install_names: bool = false;908 var headerpad_max_install_names: bool = false;
...@@ -983,8 +966,10 @@ fn buildOutputType(...@@ -983,8 +966,10 @@ fn buildOutputType(
983 // Populated in the call to `createModule` for the root module.966 // Populated in the call to `createModule` for the root module.
984 .resolved_options = undefined,967 .resolved_options = undefined,
985968
986 .system_libs = .{},969 .cli_link_inputs = .empty,
987 .resolved_system_libs = .{},970 .windows_libs = .empty,
971 .link_inputs = .empty,
972
988 .wasi_emulated_libs = .{},973 .wasi_emulated_libs = .{},
989974
990 .c_source_files = .{},975 .c_source_files = .{},
...@@ -992,7 +977,7 @@ fn buildOutputType(...@@ -992,7 +977,7 @@ fn buildOutputType(
992977
993 .llvm_m_args = .{},978 .llvm_m_args = .{},
994 .sysroot = null,979 .sysroot = null,
995 .lib_dirs = .{}, // populated by createModule()980 .lib_directories = .{}, // populated by createModule()
996 .lib_dir_args = .{}, // populated from CLI arg parsing981 .lib_dir_args = .{}, // populated from CLI arg parsing
997 .libc_installation = null,982 .libc_installation = null,
998 .want_native_include_dirs = false,983 .want_native_include_dirs = false,
...@@ -1001,7 +986,6 @@ fn buildOutputType(...@@ -1001,7 +986,6 @@ fn buildOutputType(
1001 .rpath_list = .{},986 .rpath_list = .{},
1002 .each_lib_rpath = null,987 .each_lib_rpath = null,
1003 .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena),988 .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena),
1004 .link_objects = .{},
1005 .native_system_include_paths = &.{},989 .native_system_include_paths = &.{},
1006 };990 };
1007991
...@@ -1237,30 +1221,42 @@ fn buildOutputType(...@@ -1237,30 +1221,42 @@ fn buildOutputType(
1237 // We don't know whether this library is part of libc1221 // We don't know whether this library is part of libc
1238 // or libc++ until we resolve the target, so we append1222 // or libc++ until we resolve the target, so we append
1239 // to the list for now.1223 // to the list for now.
1240 try create_module.system_libs.put(arena, args_iter.nextOrFatal(), .{1224 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1241 .needed = false,1225 .name = args_iter.nextOrFatal(),
1242 .weak = false,1226 .query = .{
1243 .preferred_mode = lib_preferred_mode,1227 .needed = false,
1244 .search_strategy = lib_search_strategy,1228 .weak = false,
1245 });1229 .preferred_mode = lib_preferred_mode,
1230 .search_strategy = lib_search_strategy,
1231 .allow_so_scripts = allow_so_scripts,
1232 },
1233 } });
1246 } else if (mem.eql(u8, arg, "--needed-library") or1234 } else if (mem.eql(u8, arg, "--needed-library") or
1247 mem.eql(u8, arg, "-needed-l") or1235 mem.eql(u8, arg, "-needed-l") or
1248 mem.eql(u8, arg, "-needed_library"))1236 mem.eql(u8, arg, "-needed_library"))
1249 {1237 {
1250 const next_arg = args_iter.nextOrFatal();1238 const next_arg = args_iter.nextOrFatal();
1251 try create_module.system_libs.put(arena, next_arg, .{1239 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1252 .needed = true,1240 .name = next_arg,
1253 .weak = false,1241 .query = .{
1254 .preferred_mode = lib_preferred_mode,1242 .needed = true,
1255 .search_strategy = lib_search_strategy,1243 .weak = false,
1256 });1244 .preferred_mode = lib_preferred_mode,
1245 .search_strategy = lib_search_strategy,
1246 .allow_so_scripts = allow_so_scripts,
1247 },
1248 } });
1257 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {1249 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
1258 try create_module.system_libs.put(arena, args_iter.nextOrFatal(), .{1250 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1259 .needed = false,1251 .name = args_iter.nextOrFatal(),
1260 .weak = true,1252 .query = .{
1261 .preferred_mode = lib_preferred_mode,1253 .needed = false,
1262 .search_strategy = lib_search_strategy,1254 .weak = true,
1263 });1255 .preferred_mode = lib_preferred_mode,
1256 .search_strategy = lib_search_strategy,
1257 .allow_so_scripts = allow_so_scripts,
1258 },
1259 } });
1264 } else if (mem.eql(u8, arg, "-D")) {1260 } else if (mem.eql(u8, arg, "-D")) {
1265 try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });1261 try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });
1266 } else if (mem.eql(u8, arg, "-I")) {1262 } else if (mem.eql(u8, arg, "-I")) {
...@@ -1573,6 +1569,10 @@ fn buildOutputType(...@@ -1573,6 +1569,10 @@ fn buildOutputType(
1573 linker_allow_shlib_undefined = true;1569 linker_allow_shlib_undefined = true;
1574 } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) {1570 } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) {
1575 linker_allow_shlib_undefined = false;1571 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;
1576 } else if (mem.eql(u8, arg, "-z")) {1576 } else if (mem.eql(u8, arg, "-z")) {
1577 const z_arg = args_iter.nextOrFatal();1577 const z_arg = args_iter.nextOrFatal();
1578 if (mem.eql(u8, z_arg, "nodelete")) {1578 if (mem.eql(u8, z_arg, "nodelete")) {
...@@ -1680,26 +1680,38 @@ fn buildOutputType(...@@ -1680,26 +1680,38 @@ fn buildOutputType(
1680 // We don't know whether this library is part of libc1680 // We don't know whether this library is part of libc
1681 // or libc++ until we resolve the target, so we append1681 // or libc++ until we resolve the target, so we append
1682 // to the list for now.1682 // to the list for now.
1683 try create_module.system_libs.put(arena, arg["-l".len..], .{1683 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1684 .needed = false,1684 .name = arg["-l".len..],
1685 .weak = false,1685 .query = .{
1686 .preferred_mode = lib_preferred_mode,1686 .needed = false,
1687 .search_strategy = lib_search_strategy,1687 .weak = false,
1688 });1688 .preferred_mode = lib_preferred_mode,
1689 .search_strategy = lib_search_strategy,
1690 .allow_so_scripts = allow_so_scripts,
1691 },
1692 } });
1689 } else if (mem.startsWith(u8, arg, "-needed-l")) {1693 } else if (mem.startsWith(u8, arg, "-needed-l")) {
1690 try create_module.system_libs.put(arena, arg["-needed-l".len..], .{1694 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1691 .needed = true,1695 .name = arg["-needed-l".len..],
1692 .weak = false,1696 .query = .{
1693 .preferred_mode = lib_preferred_mode,1697 .needed = true,
1694 .search_strategy = lib_search_strategy,1698 .weak = false,
1695 });1699 .preferred_mode = lib_preferred_mode,
1700 .search_strategy = lib_search_strategy,
1701 .allow_so_scripts = allow_so_scripts,
1702 },
1703 } });
1696 } else if (mem.startsWith(u8, arg, "-weak-l")) {1704 } else if (mem.startsWith(u8, arg, "-weak-l")) {
1697 try create_module.system_libs.put(arena, arg["-weak-l".len..], .{1705 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1698 .needed = false,1706 .name = arg["-weak-l".len..],
1699 .weak = true,1707 .query = .{
1700 .preferred_mode = lib_preferred_mode,1708 .needed = false,
1701 .search_strategy = lib_search_strategy,1709 .weak = true,
1702 });1710 .preferred_mode = lib_preferred_mode,
1711 .search_strategy = lib_search_strategy,
1712 .allow_so_scripts = allow_so_scripts,
1713 },
1714 } });
1703 } else if (mem.startsWith(u8, arg, "-D")) {1715 } else if (mem.startsWith(u8, arg, "-D")) {
1704 try cc_argv.append(arena, arg);1716 try cc_argv.append(arena, arg);
1705 } else if (mem.startsWith(u8, arg, "-I")) {1717 } else if (mem.startsWith(u8, arg, "-I")) {
...@@ -1724,15 +1736,28 @@ fn buildOutputType(...@@ -1724,15 +1736,28 @@ fn buildOutputType(
1724 fatal("unrecognized parameter: '{s}'", .{arg});1736 fatal("unrecognized parameter: '{s}'", .{arg});
1725 }1737 }
1726 } else switch (file_ext orelse Compilation.classifyFileExt(arg)) {1738 } else switch (file_ext orelse Compilation.classifyFileExt(arg)) {
1727 .shared_library => {1739 .shared_library, .object, .static_library => {
1728 try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) });1740 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1729 create_module.opts.any_dyn_libs = true;1741 .path = Path.initCwd(arg),
1730 },1742 .query = .{
1731 .object, .static_library => {1743 .preferred_mode = lib_preferred_mode,
1732 try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) });1744 .search_strategy = lib_search_strategy,
1745 .allow_so_scripts = allow_so_scripts,
1746 },
1747 } });
1748 // We do not set `any_dyn_libs` yet because a .so file
1749 // may actually resolve to a GNU ld script which ends
1750 // up being a static library.
1733 },1751 },
1734 .res => {1752 .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 } });
1736 contains_res_file = true;1761 contains_res_file = true;
1737 },1762 },
1738 .manifest => {1763 .manifest => {
...@@ -1785,6 +1810,7 @@ fn buildOutputType(...@@ -1785,6 +1810,7 @@ fn buildOutputType(
1785 // some functionality that depend on it, such as C++ exceptions and1810 // some functionality that depend on it, such as C++ exceptions and
1786 // DWARF-based stack traces.1811 // DWARF-based stack traces.
1787 link_eh_frame_hdr = true;1812 link_eh_frame_hdr = true;
1813 allow_so_scripts = true;
17881814
1789 const COutMode = enum {1815 const COutMode = enum {
1790 link,1816 link,
...@@ -1844,24 +1870,32 @@ fn buildOutputType(...@@ -1844,24 +1870,32 @@ fn buildOutputType(
1844 .ext = file_ext, // duped while parsing the args.1870 .ext = file_ext, // duped while parsing the args.
1845 });1871 });
1846 },1872 },
1847 .shared_library => {1873 .unknown, .object, .static_library, .shared_library => {
1848 try create_module.link_objects.append(arena, .{1874 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1849 .path = Path.initCwd(it.only_arg),1875 .path = Path.initCwd(it.only_arg),
1850 .must_link = must_link,1876 .query = .{
1851 });1877 .must_link = must_link,
1852 create_module.opts.any_dyn_libs = true;1878 .needed = needed,
1853 },1879 .preferred_mode = lib_preferred_mode,
1854 .unknown, .object, .static_library => {1880 .search_strategy = lib_search_strategy,
1855 try create_module.link_objects.append(arena, .{1881 .allow_so_scripts = allow_so_scripts,
1856 .path = Path.initCwd(it.only_arg),1882 },
1857 .must_link = must_link,1883 } });
1858 });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.
1859 },1887 },
1860 .res => {1888 .res => {
1861 try create_module.link_objects.append(arena, .{1889 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1862 .path = Path.initCwd(it.only_arg),1890 .path = Path.initCwd(it.only_arg),
1863 .must_link = must_link,1891 .query = .{
1864 });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 } });
1865 contains_res_file = true;1899 contains_res_file = true;
1866 },1900 },
1867 .manifest => {1901 .manifest => {
...@@ -1893,19 +1927,21 @@ fn buildOutputType(...@@ -1893,19 +1927,21 @@ fn buildOutputType(
1893 // -l :path/to/filename is used when callers need1927 // -l :path/to/filename is used when callers need
1894 // more control over what's in the resulting1928 // more control over what's in the resulting
1895 // binary: no extra rpaths and DSO filename exactly1929 // binary: no extra rpaths and DSO filename exactly
1896 // as provided. Hello, Go.1930 // as provided. CGo compilation depends on this.
1897 try create_module.link_objects.append(arena, .{1931 try create_module.cli_link_inputs.append(arena, .{ .dso_exact = .{
1898 .path = Path.initCwd(it.only_arg),1932 .name = it.only_arg,
1899 .must_link = must_link,1933 } });
1900 .loption = true,
1901 });
1902 } else {1934 } else {
1903 try create_module.system_libs.put(arena, it.only_arg, .{1935 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1904 .needed = needed,1936 .name = it.only_arg,
1905 .weak = false,1937 .query = .{
1906 .preferred_mode = lib_preferred_mode,1938 .needed = needed,
1907 .search_strategy = lib_search_strategy,1939 .weak = false,
1908 });1940 .preferred_mode = lib_preferred_mode,
1941 .search_strategy = lib_search_strategy,
1942 .allow_so_scripts = allow_so_scripts,
1943 },
1944 } });
1909 }1945 }
1910 },1946 },
1911 .ignore => {},1947 .ignore => {},
...@@ -2174,12 +2210,16 @@ fn buildOutputType(...@@ -2174,12 +2210,16 @@ fn buildOutputType(
2174 },2210 },
2175 .force_load_objc => force_load_objc = true,2211 .force_load_objc => force_load_objc = true,
2176 .mingw_unicode_entry_point => mingw_unicode_entry_point = true,2212 .mingw_unicode_entry_point => mingw_unicode_entry_point = true,
2177 .weak_library => try create_module.system_libs.put(arena, it.only_arg, .{2213 .weak_library => try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2178 .needed = false,2214 .name = it.only_arg,
2179 .weak = true,2215 .query = .{
2180 .preferred_mode = lib_preferred_mode,2216 .needed = false,
2181 .search_strategy = lib_search_strategy,2217 .weak = true,
2182 }),2218 .preferred_mode = lib_preferred_mode,
2219 .search_strategy = lib_search_strategy,
2220 .allow_so_scripts = allow_so_scripts,
2221 },
2222 } }),
2183 .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),2223 .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),
2184 .headerpad_max_install_names => headerpad_max_install_names = true,2224 .headerpad_max_install_names => headerpad_max_install_names = true,
2185 .compress_debug_sections => {2225 .compress_debug_sections => {
...@@ -2482,26 +2522,38 @@ fn buildOutputType(...@@ -2482,26 +2522,38 @@ fn buildOutputType(
2482 } else if (mem.eql(u8, arg, "-needed_framework")) {2522 } else if (mem.eql(u8, arg, "-needed_framework")) {
2483 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true });2523 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true });
2484 } else if (mem.eql(u8, arg, "-needed_library")) {2524 } else if (mem.eql(u8, arg, "-needed_library")) {
2485 try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{2525 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2486 .weak = false,2526 .name = linker_args_it.nextOrFatal(),
2487 .needed = true,2527 .query = .{
2488 .preferred_mode = lib_preferred_mode,2528 .weak = false,
2489 .search_strategy = lib_search_strategy,2529 .needed = true,
2490 });2530 .preferred_mode = lib_preferred_mode,
2531 .search_strategy = lib_search_strategy,
2532 .allow_so_scripts = allow_so_scripts,
2533 },
2534 } });
2491 } else if (mem.startsWith(u8, arg, "-weak-l")) {2535 } else if (mem.startsWith(u8, arg, "-weak-l")) {
2492 try create_module.system_libs.put(arena, arg["-weak-l".len..], .{2536 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2493 .weak = true,2537 .name = arg["-weak-l".len..],
2494 .needed = false,2538 .query = .{
2495 .preferred_mode = lib_preferred_mode,2539 .weak = true,
2496 .search_strategy = lib_search_strategy,2540 .needed = false,
2497 });2541 .preferred_mode = lib_preferred_mode,
2542 .search_strategy = lib_search_strategy,
2543 .allow_so_scripts = allow_so_scripts,
2544 },
2545 } });
2498 } else if (mem.eql(u8, arg, "-weak_library")) {2546 } else if (mem.eql(u8, arg, "-weak_library")) {
2499 try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{2547 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2500 .weak = true,2548 .name = linker_args_it.nextOrFatal(),
2501 .needed = false,2549 .query = .{
2502 .preferred_mode = lib_preferred_mode,2550 .weak = true,
2503 .search_strategy = lib_search_strategy,2551 .needed = false,
2504 });2552 .preferred_mode = lib_preferred_mode,
2553 .search_strategy = lib_search_strategy,
2554 .allow_so_scripts = allow_so_scripts,
2555 },
2556 } });
2505 } else if (mem.eql(u8, arg, "-compatibility_version")) {2557 } else if (mem.eql(u8, arg, "-compatibility_version")) {
2506 const compat_version = linker_args_it.nextOrFatal();2558 const compat_version = linker_args_it.nextOrFatal();
2507 compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {2559 compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {
...@@ -2532,10 +2584,14 @@ fn buildOutputType(...@@ -2532,10 +2584,14 @@ fn buildOutputType(
2532 } else if (mem.eql(u8, arg, "-install_name")) {2584 } else if (mem.eql(u8, arg, "-install_name")) {
2533 install_name = linker_args_it.nextOrFatal();2585 install_name = linker_args_it.nextOrFatal();
2534 } else if (mem.eql(u8, arg, "-force_load")) {2586 } 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 = .{
2536 .path = Path.initCwd(linker_args_it.nextOrFatal()),2588 .path = Path.initCwd(linker_args_it.nextOrFatal()),
2537 .must_link = true,2589 .query = .{
2538 });2590 .must_link = true,
2591 .preferred_mode = .static,
2592 .search_strategy = .no_fallback,
2593 },
2594 } });
2539 } else if (mem.eql(u8, arg, "-hash-style") or2595 } else if (mem.eql(u8, arg, "-hash-style") or
2540 mem.eql(u8, arg, "--hash-style"))2596 mem.eql(u8, arg, "--hash-style"))
2541 {2597 {
...@@ -2665,7 +2721,7 @@ fn buildOutputType(...@@ -2665,7 +2721,7 @@ fn buildOutputType(
2665 },2721 },
2666 }2722 }
2667 if (create_module.c_source_files.items.len == 0 and2723 if (create_module.c_source_files.items.len == 0 and
2668 create_module.link_objects.items.len == 0 and2724 !anyObjectLinkInputs(create_module.cli_link_inputs.items) and
2669 root_src_file == null)2725 root_src_file == null)
2670 {2726 {
2671 // For example `zig cc` and no args should print the "no input files" message.2727 // For example `zig cc` and no args should print the "no input files" message.
...@@ -2707,8 +2763,11 @@ fn buildOutputType(...@@ -2707,8 +2763,11 @@ fn buildOutputType(
2707 if (create_module.c_source_files.items.len >= 1)2763 if (create_module.c_source_files.items.len >= 1)
2708 break :b create_module.c_source_files.items[0].src_path;2764 break :b create_module.c_source_files.items[0].src_path;
27092765
2710 if (create_module.link_objects.items.len >= 1)2766 for (create_module.cli_link_inputs.items) |unresolved_link_input| switch (unresolved_link_input) {
2711 break :b create_module.link_objects.items[0].path.sub_path;2767 // Intentionally includes dynamic libraries provided by file path.
2768 .path_query => |pq| break :b pq.path.sub_path,
2769 else => continue,
2770 };
27122771
2713 if (emit_bin == .yes)2772 if (emit_bin == .yes)
2714 break :b emit_bin.yes;2773 break :b emit_bin.yes;
...@@ -2794,7 +2853,7 @@ fn buildOutputType(...@@ -2794,7 +2853,7 @@ fn buildOutputType(
2794 fatal("unable to find zig self exe path: {s}", .{@errorName(err)});2853 fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
2795 };2854 };
27962855
2797 var zig_lib_directory: Compilation.Directory = d: {2856 var zig_lib_directory: Directory = d: {
2798 if (override_lib_dir) |unresolved_lib_dir| {2857 if (override_lib_dir) |unresolved_lib_dir| {
2799 const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);2858 const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
2800 break :d .{2859 break :d .{
...@@ -2815,7 +2874,7 @@ fn buildOutputType(...@@ -2815,7 +2874,7 @@ fn buildOutputType(
2815 };2874 };
2816 defer zig_lib_directory.handle.close();2875 defer zig_lib_directory.handle.close();
28172876
2818 var global_cache_directory: Compilation.Directory = l: {2877 var global_cache_directory: Directory = l: {
2819 if (override_global_cache_dir) |p| {2878 if (override_global_cache_dir) |p| {
2820 break :l .{2879 break :l .{
2821 .handle = try fs.cwd().makeOpenPath(p, .{}),2880 .handle = try fs.cwd().makeOpenPath(p, .{}),
...@@ -2845,7 +2904,7 @@ fn buildOutputType(...@@ -2845,7 +2904,7 @@ fn buildOutputType(
28452904
2846 var builtin_modules: std.StringHashMapUnmanaged(*Package.Module) = .empty;2905 var builtin_modules: std.StringHashMapUnmanaged(*Package.Module) = .empty;
2847 // `builtin_modules` allocated into `arena`, so no deinit2906 // `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);
2849 for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {2908 for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {
2850 if (cli_mod.resolved == null)2909 if (cli_mod.resolved == null)
2851 fatal("module '{s}' declared but not used", .{key});2910 fatal("module '{s}' declared but not used", .{key});
...@@ -2939,7 +2998,6 @@ fn buildOutputType(...@@ -2939,7 +2998,6 @@ fn buildOutputType(
2939 }2998 }
2940 }2999 }
29413000
2942 // We now repeat part of the process for frameworks.
2943 var resolved_frameworks = std.ArrayList(Compilation.Framework).init(arena);3001 var resolved_frameworks = std.ArrayList(Compilation.Framework).init(arena);
29443002
2945 if (create_module.frameworks.keys().len > 0) {3003 if (create_module.frameworks.keys().len > 0) {
...@@ -2996,7 +3054,7 @@ fn buildOutputType(...@@ -2996,7 +3054,7 @@ fn buildOutputType(
2996 const total_obj_count = create_module.c_source_files.items.len +3054 const total_obj_count = create_module.c_source_files.items.len +
2997 @intFromBool(root_src_file != null) +3055 @intFromBool(root_src_file != null) +
2998 create_module.rc_source_files.items.len +3056 create_module.rc_source_files.items.len +
2999 create_module.link_objects.items.len;3057 link.countObjectInputs(create_module.link_inputs.items);
3000 if (total_obj_count > 1) {3058 if (total_obj_count > 1) {
3001 fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)});3059 fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)});
3002 }3060 }
...@@ -3212,7 +3270,7 @@ fn buildOutputType(...@@ -3212,7 +3270,7 @@ fn buildOutputType(
3212 var cleanup_local_cache_dir: ?fs.Dir = null;3270 var cleanup_local_cache_dir: ?fs.Dir = null;
3213 defer if (cleanup_local_cache_dir) |*dir| dir.close();3271 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: {
3216 if (override_local_cache_dir) |local_cache_dir_path| {3274 if (override_local_cache_dir) |local_cache_dir_path| {
3217 const dir = try fs.cwd().makeOpenPath(local_cache_dir_path, .{});3275 const dir = try fs.cwd().makeOpenPath(local_cache_dir_path, .{});
3218 cleanup_local_cache_dir = dir;3276 cleanup_local_cache_dir = dir;
...@@ -3349,7 +3407,7 @@ fn buildOutputType(...@@ -3349,7 +3407,7 @@ fn buildOutputType(
3349 .emit_llvm_bc = emit_llvm_bc_resolved.data,3407 .emit_llvm_bc = emit_llvm_bc_resolved.data,
3350 .emit_docs = emit_docs_resolved.data,3408 .emit_docs = emit_docs_resolved.data,
3351 .emit_implib = emit_implib_resolved.data,3409 .emit_implib = emit_implib_resolved.data,
3352 .lib_dirs = create_module.lib_dirs.items,3410 .lib_directories = create_module.lib_directories.items,
3353 .rpath_list = create_module.rpath_list.items,3411 .rpath_list = create_module.rpath_list.items,
3354 .symbol_wrap_set = symbol_wrap_set,3412 .symbol_wrap_set = symbol_wrap_set,
3355 .c_source_files = create_module.c_source_files.items,3413 .c_source_files = create_module.c_source_files.items,
...@@ -3357,11 +3415,10 @@ fn buildOutputType(...@@ -3357,11 +3415,10 @@ fn buildOutputType(
3357 .manifest_file = manifest_file,3415 .manifest_file = manifest_file,
3358 .rc_includes = rc_includes,3416 .rc_includes = rc_includes,
3359 .mingw_unicode_entry_point = mingw_unicode_entry_point,3417 .mingw_unicode_entry_point = mingw_unicode_entry_point,
3360 .link_objects = create_module.link_objects.items,3418 .link_inputs = create_module.link_inputs.items,
3361 .framework_dirs = create_module.framework_dirs.items,3419 .framework_dirs = create_module.framework_dirs.items,
3362 .frameworks = resolved_frameworks.items,3420 .frameworks = resolved_frameworks.items,
3363 .system_lib_names = create_module.resolved_system_libs.items(.name),3421 .windows_lib_names = create_module.windows_libs.keys(),
3364 .system_lib_infos = create_module.resolved_system_libs.items(.lib),
3365 .wasi_emulated_libs = create_module.wasi_emulated_libs.items,3422 .wasi_emulated_libs = create_module.wasi_emulated_libs.items,
3366 .want_compiler_rt = want_compiler_rt,3423 .want_compiler_rt = want_compiler_rt,
3367 .hash_style = hash_style,3424 .hash_style = hash_style,
...@@ -3630,12 +3687,14 @@ const CreateModule = struct {...@@ -3630,12 +3687,14 @@ const CreateModule = struct {
3630 /// This one is used while collecting CLI options. The set of libs is used3687 /// This one is used while collecting CLI options. The set of libs is used
3631 /// directly after computing the target and used to compute link_libc,3688 /// directly after computing the target and used to compute link_libc,
3632 /// link_libcpp, and then the libraries are filtered into3689 /// link_libcpp, and then the libraries are filtered into
3633 /// `external_system_libs` and `resolved_system_libs`.3690 /// `unresolved_linker_inputs` and `windows_libs`.
3634 system_libs: std.StringArrayHashMapUnmanaged(SystemLib),3691 cli_link_inputs: std.ArrayListUnmanaged(link.UnresolvedInput),
3635 resolved_system_libs: std.MultiArrayList(struct {3692 windows_libs: std.StringArrayHashMapUnmanaged(void),
3636 name: []const u8,3693 /// The local variable `unresolved_link_inputs` is fed into library
3637 lib: Compilation.SystemLib,3694 /// resolution, mutating the input array, and producing this data as
3638 }),3695 /// output. Allocated with gpa.
3696 link_inputs: std.ArrayListUnmanaged(link.Input),
3697
3639 wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile),3698 wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile),
36403699
3641 c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile),3700 c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile),
...@@ -3646,7 +3705,7 @@ const CreateModule = struct {...@@ -3646,7 +3705,7 @@ const CreateModule = struct {
3646 /// CPU features.3705 /// CPU features.
3647 llvm_m_args: std.ArrayListUnmanaged([]const u8),3706 llvm_m_args: std.ArrayListUnmanaged([]const u8),
3648 sysroot: ?[]const u8,3707 sysroot: ?[]const u8,
3649 lib_dirs: std.ArrayListUnmanaged([]const u8),3708 lib_directories: std.ArrayListUnmanaged(Directory),
3650 lib_dir_args: std.ArrayListUnmanaged([]const u8),3709 lib_dir_args: std.ArrayListUnmanaged([]const u8),
3651 libc_installation: ?LibCInstallation,3710 libc_installation: ?LibCInstallation,
3652 want_native_include_dirs: bool,3711 want_native_include_dirs: bool,
...@@ -3656,7 +3715,6 @@ const CreateModule = struct {...@@ -3656,7 +3715,6 @@ const CreateModule = struct {
3656 rpath_list: std.ArrayListUnmanaged([]const u8),3715 rpath_list: std.ArrayListUnmanaged([]const u8),
3657 each_lib_rpath: ?bool,3716 each_lib_rpath: ?bool,
3658 libc_paths_file: ?[]const u8,3717 libc_paths_file: ?[]const u8,
3659 link_objects: std.ArrayListUnmanaged(Compilation.LinkObject),
3660};3718};
36613719
3662fn createModule(3720fn createModule(
...@@ -3667,6 +3725,7 @@ fn createModule(...@@ -3667,6 +3725,7 @@ fn createModule(
3667 parent: ?*Package.Module,3725 parent: ?*Package.Module,
3668 zig_lib_directory: Cache.Directory,3726 zig_lib_directory: Cache.Directory,
3669 builtin_modules: *std.StringHashMapUnmanaged(*Package.Module),3727 builtin_modules: *std.StringHashMapUnmanaged(*Package.Module),
3728 color: std.zig.Color,
3670) Allocator.Error!*Package.Module {3729) Allocator.Error!*Package.Module {
3671 const cli_mod = &create_module.modules.values()[index];3730 const cli_mod = &create_module.modules.values()[index];
3672 if (cli_mod.resolved) |m| return m;3731 if (cli_mod.resolved) |m| return m;
...@@ -3760,85 +3819,83 @@ fn createModule(...@@ -3760,85 +3819,83 @@ fn createModule(
3760 // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.3819 // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.
3761 // We need to know whether the set of system libraries contains anything besides these3820 // We need to know whether the set of system libraries contains anything besides these
3762 // to decide whether to trigger native path detection logic.3821 // to decide whether to trigger native path detection logic.
3763 var external_system_libs: std.MultiArrayList(struct {3822 // Preserves linker input order.
3764 name: []const u8,3823 var unresolved_link_inputs: std.ArrayListUnmanaged(link.UnresolvedInput) = .empty;
3765 info: SystemLib,3824 try unresolved_link_inputs.ensureUnusedCapacity(arena, create_module.cli_link_inputs.items.len);
3766 }) = .{};3825 var any_name_queries_remaining = false;
3767 for (create_module.system_libs.keys(), create_module.system_libs.values()) |lib_name, info| {3826 for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) {
3768 if (std.zig.target.isLibCLibName(target, lib_name)) {3827 .name_query => |nq| {
3769 create_module.opts.link_libc = true;3828 const lib_name = nq.name;
3770 continue;3829 if (std.zig.target.isLibCLibName(target, lib_name)) {
3771 }3830 create_module.opts.link_libc = true;
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;
3780 continue;3831 continue;
3781 },3832 }
3782 .only_compiler_rt => {3833 if (std.zig.target.isLibCxxLibName(target, lib_name)) {
3783 warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});3834 create_module.opts.link_libcpp = true;
3784 continue;3835 continue;
3785 },3836 }
3786 }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()) {3849 if (target.isMinGW()) {
3789 const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| {3850 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}", .{3851 fatal("failed to check zig installation for DLL import libs: {s}", .{
3791 @errorName(err),3852 @errorName(err),
3792 });3853 });
3793 };3854 };
3794 if (exists) {3855 if (exists) {
3795 try create_module.resolved_system_libs.append(arena, .{3856 try create_module.windows_libs.put(arena, lib_name, {});
3796 .name = lib_name,3857 continue;
3797 .lib = .{3858 }
3798 .needed = true,
3799 .weak = false,
3800 .path = null,
3801 },
3802 });
3803 continue;
3804 }3859 }
3805 }
38063860
3807 if (fs.path.isAbsolute(lib_name)) {3861 if (fs.path.isAbsolute(lib_name)) {
3808 fatal("cannot use absolute path as a system library: {s}", .{lib_name});3862 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
3809 }3863 }
38103864
3811 if (target.os.tag == .wasi) {3865 if (target.os.tag == .wasi) {
3812 if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| {3866 if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| {
3813 try create_module.wasi_emulated_libs.append(arena, crt_file);3867 try create_module.wasi_emulated_libs.append(arena, crt_file);
3814 continue;3868 continue;
3869 }
3815 }3870 }
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, .{3879 if (any_name_queries_remaining) create_module.want_native_include_dirs = true;
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;
38263880
3827 // Resolve the library path arguments with respect to sysroot.3881 // Resolve the library path arguments with respect to sysroot.
3882 try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len);
3828 if (create_module.sysroot) |root| {3883 if (create_module.sysroot) |root| {
3829 try create_module.lib_dirs.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len * 2);3884 for (create_module.lib_dir_args.items) |lib_dir_arg| {
3830 for (create_module.lib_dir_args.items) |dir| {3885 if (fs.path.isAbsolute(lib_dir_arg)) {
3831 if (fs.path.isAbsolute(dir)) {3886 const stripped_dir = lib_dir_arg[fs.path.diskDesignator(lib_dir_arg).len..];
3832 const stripped_dir = dir[fs.path.diskDesignator(dir).len..];
3833 const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });3887 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);
3835 }3891 }
3836 create_module.lib_dirs.appendAssumeCapacity(dir);
3837 }3892 }
3838 } else {3893 } 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 }
3840 }3897 }
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
3843 if (resolved_target.is_native_os and target.isDarwin()) {3900 if (resolved_target.is_native_os and target.isDarwin()) {
3844 // If we want to link against frameworks, we need system headers.3901 // If we want to link against frameworks, we need system headers.
...@@ -3847,7 +3904,10 @@ fn createModule(...@@ -3847,7 +3904,10 @@ fn createModule(
3847 }3904 }
38483905
3849 if (create_module.each_lib_rpath orelse resolved_target.is_native_os) {3906 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 }
3851 }3911 }
38523912
3853 // Trigger native system library path detection if necessary.3913 // Trigger native system library path detection if necessary.
...@@ -3865,8 +3925,10 @@ fn createModule(...@@ -3865,8 +3925,10 @@ fn createModule(
3865 create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena);3925 create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena);
38663926
3867 try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items);3927 try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items);
3868 try create_module.lib_dirs.appendSlice(arena, paths.lib_dirs.items);
3869 try create_module.rpath_list.appendSlice(arena, paths.rpaths.items);3928 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);
3870 }3932 }
38713933
3872 if (create_module.libc_paths_file) |paths_file| {3934 if (create_module.libc_paths_file) |paths_file| {
...@@ -3878,7 +3940,7 @@ fn createModule(...@@ -3878,7 +3940,7 @@ fn createModule(
3878 }3940 }
38793941
3880 if (builtin.target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and3942 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)
3882 {3944 {
3883 if (create_module.libc_installation == null) {3945 if (create_module.libc_installation == null) {
3884 create_module.libc_installation = LibCInstallation.findNative(.{3946 create_module.libc_installation = LibCInstallation.findNative(.{
...@@ -3889,181 +3951,31 @@ fn createModule(...@@ -3889,181 +3951,31 @@ fn createModule(
3889 fatal("unable to find native libc installation: {s}", .{@errorName(err)});3951 fatal("unable to find native libc installation: {s}", .{@errorName(err)});
3890 };3952 };
38913953
3892 try create_module.lib_dirs.appendSlice(arena, &.{3954 try create_module.lib_directories.ensureUnusedCapacity(arena, 2);
3893 create_module.libc_installation.?.msvc_lib_dir.?,3955 addLibDirectoryWarn(&create_module.lib_directories, create_module.libc_installation.?.msvc_lib_dir.?);
3894 create_module.libc_installation.?.kernel32_lib_dir.?,3956 addLibDirectoryWarn(&create_module.lib_directories, 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);
4060 }3957 }
4061 }3958 }
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)3960 // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`.
4066 create_module.opts.any_dyn_libs = true;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
4068 create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) {3980 create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) {
4069 error.WasiExecModelRequiresWasi => fatal("only WASI OS targets support execution model", .{}),3981 error.WasiExecModelRequiresWasi => fatal("only WASI OS targets support execution model", .{}),
...@@ -4131,7 +4043,7 @@ fn createModule(...@@ -4131,7 +4043,7 @@ fn createModule(
4131 for (cli_mod.deps) |dep| {4043 for (cli_mod.deps) |dep| {
4132 const dep_index = create_module.modules.getIndex(dep.value) orelse4044 const dep_index = create_module.modules.getIndex(dep.value) orelse
4133 fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key });4045 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);
4135 try mod.deps.put(arena, dep.key, dep_mod);4047 try mod.deps.put(arena, dep.key, dep_mod);
4136 }4048 }
41374049
...@@ -4996,7 +4908,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -4996,7 +4908,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
49964908
4997 process.raiseFileDescriptorLimit();4909 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| .{
5000 .path = lib_dir,4912 .path = lib_dir,
5001 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {4913 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
5002 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });4914 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 {...@@ -5015,7 +4927,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5015 });4927 });
5016 child_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path;4928 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: {
5019 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);4931 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
5020 break :l .{4932 break :l .{
5021 .handle = try fs.cwd().makeOpenPath(p, .{}),4933 .handle = try fs.cwd().makeOpenPath(p, .{}),
...@@ -5026,7 +4938,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5026,7 +4938,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
50264938
5027 child_argv.items[argv_index_global_cache_dir] = global_cache_directory.path orelse cwd_path;4939 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: {
5030 if (override_local_cache_dir) |local_cache_dir_path| {4942 if (override_local_cache_dir) |local_cache_dir_path| {
5031 break :l .{4943 break :l .{
5032 .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}),4944 .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}),
...@@ -5460,7 +5372,7 @@ fn jitCmd(...@@ -5460,7 +5372,7 @@ fn jitCmd(
5460 const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);5372 const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
5461 const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);5373 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| .{
5464 .path = lib_dir,5376 .path = lib_dir,
5465 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {5377 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
5466 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });5378 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });
...@@ -5470,7 +5382,7 @@ fn jitCmd(...@@ -5470,7 +5382,7 @@ fn jitCmd(
5470 };5382 };
5471 defer zig_lib_directory.handle.close();5383 defer zig_lib_directory.handle.close();
54725384
5473 var global_cache_directory: Compilation.Directory = l: {5385 var global_cache_directory: Directory = l: {
5474 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);5386 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
5475 break :l .{5387 break :l .{
5476 .handle = try fs.cwd().makeOpenPath(p, .{}),5388 .handle = try fs.cwd().makeOpenPath(p, .{}),
...@@ -6857,86 +6769,6 @@ const ClangSearchSanitizer = struct {...@@ -6857,86 +6769,6 @@ const ClangSearchSanitizer = struct {
6857 };6769 };
6858};6770};
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
6940fn accessFrameworkPath(6772fn accessFrameworkPath(
6941 test_path: *std.ArrayList(u8),6773 test_path: *std.ArrayList(u8),
6942 checked_paths: *std.ArrayList(u8),6774 checked_paths: *std.ArrayList(u8),
...@@ -7057,7 +6889,7 @@ fn cmdFetch(...@@ -7057,7 +6889,7 @@ fn cmdFetch(
7057 });6889 });
7058 defer root_prog_node.end();6890 defer root_prog_node.end();
70596891
7060 var global_cache_directory: Compilation.Directory = l: {6892 var global_cache_directory: Directory = l: {
7061 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);6893 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
7062 break :l .{6894 break :l .{
7063 .handle = try fs.cwd().makeOpenPath(p, .{}),6895 .handle = try fs.cwd().makeOpenPath(p, .{}),
...@@ -7634,3 +7466,24 @@ fn handleModArg(...@@ -7634,3 +7466,24 @@ fn handleModArg(
7634 c_source_files_owner_index.* = create_module.c_source_files.items.len;7466 c_source_files_owner_index.* = create_module.c_source_files.items.len;
7635 rc_source_files_owner_index.* = create_module.rc_source_files.items.len;7467 rc_source_files_owner_index.* = create_module.rc_source_files.items.len;
7636}7468}
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 {...@@ -19,7 +19,7 @@ pub const CrtFile = enum {
19 libc_so,19 libc_so,
20};20};
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 {
23 if (!build_options.have_llvm) {23 if (!build_options.have_llvm) {
24 return error.ZigCompilerNotBuiltWithLLVMExtensions;24 return error.ZigCompilerNotBuiltWithLLVMExtensions;
25 }25 }
...@@ -28,7 +28,7 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre...@@ -28,7 +28,7 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
28 defer arena_allocator.deinit();28 defer arena_allocator.deinit();
29 const arena = arena_allocator.allocator();29 const arena = arena_allocator.allocator();
3030
31 switch (crt_file) {31 switch (in_crt_file) {
32 .crti_o => {32 .crti_o => {
33 var args = std.ArrayList([]const u8).init(arena);33 var args = std.ArrayList([]const u8).init(arena);
34 try addCcArgs(comp, arena, &args, false);34 try addCcArgs(comp, arena, &args, false);
...@@ -195,8 +195,9 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre...@@ -195,8 +195,9 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
195 .libc_so => {195 .libc_so => {
196 const optimize_mode = comp.compilerRtOptMode();196 const optimize_mode = comp.compilerRtOptMode();
197 const strip = comp.compilerRtStrip();197 const strip = comp.compilerRtStrip();
198 const output_mode: std.builtin.OutputMode = .Lib;
198 const config = try Compilation.Config.resolve(.{199 const config = try Compilation.Config.resolve(.{
199 .output_mode = .Lib,200 .output_mode = output_mode,
200 .link_mode = .dynamic,201 .link_mode = .dynamic,
201 .resolved_target = comp.root_mod.resolved_target,202 .resolved_target = comp.root_mod.resolved_target,
202 .is_test = false,203 .is_test = false,
...@@ -276,28 +277,39 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre...@@ -276,28 +277,39 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
276277
277 try comp.updateSubCompilation(sub_compilation, .@"musl libc.so", prog_node);278 try comp.updateSubCompilation(sub_compilation, .@"musl libc.so", prog_node);
278279
279 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);
280
281 const basename = try comp.gpa.dupe(u8, "libc.so");280 const basename = try comp.gpa.dupe(u8, "libc.so");
282 errdefer comp.gpa.free(basename);281 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 }
285 },291 },
286 }292 }
287}293}
288294
289// Return true if musl has arch-specific crti/crtn sources.295/// Return true if musl has arch-specific crti/crtn sources.
290// See lib/libc/musl/crt/ARCH/crt?.s .296/// See lib/libc/musl/crt/ARCH/crt?.s .
291pub fn needsCrtiCrtn(target: std.Target) bool {297pub fn needsCrtiCrtn(target: std.Target) bool {
292 // zig fmt: off
293 return switch (target.cpu.arch) {298 return switch (target.cpu.arch) {
294 .riscv32,299 .riscv32, .riscv64, .wasm32, .wasm64 => false,
295 .riscv64,
296 .wasm32, .wasm64 => false,
297 .loongarch64 => false,300 .loongarch64 => false,
298 else => true,301 else => true,
299 };302 };
300 // zig fmt: on303}
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 };
301}313}
302314
303fn isMuslArchName(name: []const u8) bool {315fn isMuslArchName(name: []const u8) bool {
src/target.zig+12-32
...@@ -1,4 +1,6 @@...@@ -1,4 +1,6 @@
1const std = @import("std");1const std = @import("std");
2const assert = std.debug.assert;
3
2const Type = @import("Type.zig");4const Type = @import("Type.zig");
3const AddressSpace = std.builtin.AddressSpace;5const AddressSpace = std.builtin.AddressSpace;
4const Alignment = @import("InternPool.zig").Alignment;6const Alignment = @import("InternPool.zig").Alignment;
...@@ -284,40 +286,18 @@ pub fn hasRedZone(target: std.Target) bool {...@@ -284,40 +286,18 @@ pub fn hasRedZone(target: std.Target) bool {
284pub fn libcFullLinkFlags(target: std.Target) []const []const u8 {286pub fn libcFullLinkFlags(target: std.Target) []const []const u8 {
285 // The linking order of these is significant and should match the order other287 // The linking order of these is significant and should match the order other
286 // c compilers such as gcc or clang use.288 // c compilers such as gcc or clang use.
287 return switch (target.os.tag) {289 const result: []const []const u8 = switch (target.os.tag) {
288 .netbsd, .openbsd => &[_][]const u8{290 .netbsd, .openbsd => &.{ "-lm", "-lpthread", "-lc", "-lutil" },
289 "-lm",291 // Solaris releases after 10 merged the threading libraries into libc.
290 "-lpthread",292 .solaris, .illumos => &.{ "-lm", "-lsocket", "-lnsl", "-lc" },
291 "-lc",293 .haiku => &.{ "-lm", "-lroot", "-lpthread", "-lc", "-lnetwork" },
292 "-lutil",294 .linux => switch (target.abi) {
293 },295 .android, .androideabi, .ohos, .ohoseabi => &.{ "-lm", "-lc", "-ldl" },
294 .solaris, .illumos => &[_][]const u8{296 else => &.{ "-lm", "-lpthread", "-lc", "-ldl", "-lrt", "-lutil" },
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",
319 },297 },
298 else => &.{},
320 };299 };
300 return result;
321}301}
322302
323pub fn clangMightShellOutForAssembly(target: std.Target) bool {303pub 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 {...@@ -51,6 +51,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
51 elf_step.dependOn(testEmitRelocatable(b, .{ .target = musl_target }));51 elf_step.dependOn(testEmitRelocatable(b, .{ .target = musl_target }));
52 elf_step.dependOn(testRelocatableArchive(b, .{ .target = musl_target }));52 elf_step.dependOn(testRelocatableArchive(b, .{ .target = musl_target }));
53 elf_step.dependOn(testRelocatableEhFrame(b, .{ .target = musl_target }));53 elf_step.dependOn(testRelocatableEhFrame(b, .{ .target = musl_target }));
54 elf_step.dependOn(testRelocatableEhFrameComdatHeavy(b, .{ .target = musl_target }));
54 elf_step.dependOn(testRelocatableNoEhFrame(b, .{ .target = musl_target }));55 elf_step.dependOn(testRelocatableNoEhFrame(b, .{ .target = musl_target }));
5556
56 // Exercise linker in ar mode57 // Exercise linker in ar mode
...@@ -2145,6 +2146,7 @@ fn testLdScript(b: *Build, opts: Options) *Step {...@@ -2145,6 +2146,7 @@ fn testLdScript(b: *Build, opts: Options) *Step {
2145 exe.addLibraryPath(dso.getEmittedBinDirectory());2146 exe.addLibraryPath(dso.getEmittedBinDirectory());
2146 exe.addRPath(dso.getEmittedBinDirectory());2147 exe.addRPath(dso.getEmittedBinDirectory());
2147 exe.linkLibC();2148 exe.linkLibC();
2149 exe.allow_so_scripts = true;
21482150
2149 const run = addRunArtifact(exe);2151 const run = addRunArtifact(exe);
2150 run.expectExitCode(0);2152 run.expectExitCode(0);
...@@ -2164,14 +2166,13 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {...@@ -2164,14 +2166,13 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
2164 exe.linkSystemLibrary2("a", .{});2166 exe.linkSystemLibrary2("a", .{});
2165 exe.addLibraryPath(scripts.getDirectory());2167 exe.addLibraryPath(scripts.getDirectory());
2166 exe.linkLibC();2168 exe.linkLibC();
2169 exe.allow_so_scripts = true;
21672170
2168 expectLinkErrors(2171 // TODO: A future enhancement could make this error message also mention
2169 exe,2172 // the file that references the missing library.
2170 test_step,2173 expectLinkErrors(exe, test_step, .{
2171 .{2174 .stderr_contains = "error: unable to find dynamic system library 'foo' using strategy 'no_fallback'. searched paths:",
2172 .contains = "error: missing library dependency: GNU ld script '/?/liba.so' requires 'libfoo.so', but file not found",2175 });
2173 },
2174 );
21752176
2176 return test_step;2177 return test_step;
2177}2178}
...@@ -2203,6 +2204,7 @@ fn testLdScriptAllowUndefinedVersion(b: *Build, opts: Options) *Step {...@@ -2203,6 +2204,7 @@ fn testLdScriptAllowUndefinedVersion(b: *Build, opts: Options) *Step {
2203 });2204 });
2204 exe.linkLibrary(so);2205 exe.linkLibrary(so);
2205 exe.linkLibC();2206 exe.linkLibC();
2207 exe.allow_so_scripts = true;
22062208
2207 const run = addRunArtifact(exe);2209 const run = addRunArtifact(exe);
2208 run.expectStdErrEqual("3\n");2210 run.expectStdErrEqual("3\n");
...@@ -2225,6 +2227,7 @@ fn testLdScriptDisallowUndefinedVersion(b: *Build, opts: Options) *Step {...@@ -2225,6 +2227,7 @@ fn testLdScriptDisallowUndefinedVersion(b: *Build, opts: Options) *Step {
2225 const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }");2227 const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }");
2226 so.setLinkerScript(ld);2228 so.setLinkerScript(ld);
2227 so.linker_allow_undefined_version = false;2229 so.linker_allow_undefined_version = false;
2230 so.allow_so_scripts = true;
22282231
2229 expectLinkErrors(2232 expectLinkErrors(
2230 so,2233 so,
...@@ -2721,86 +2724,110 @@ fn testRelocatableArchive(b: *Build, opts: Options) *Step {...@@ -2721,86 +2724,110 @@ fn testRelocatableArchive(b: *Build, opts: Options) *Step {
2721fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {2724fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
2722 const test_step = addTestStep(b, "relocatable-eh-frame", opts);2725 const test_step = addTestStep(b, "relocatable-eh-frame", opts);
27232726
2724 {2727 const obj1 = addObject(b, opts, .{
2725 const obj = addObject(b, opts, .{2728 .name = "obj1",
2726 .name = "obj1",2729 .cpp_source_bytes =
2727 .cpp_source_bytes =2730 \\#include <stdexcept>
2728 \\#include <stdexcept>2731 \\int try_me() {
2729 \\int try_me() {2732 \\ throw std::runtime_error("Oh no!");
2730 \\ throw std::runtime_error("Oh no!");2733 \\}
2731 \\}2734 ,
2732 ,2735 });
2733 });2736 obj1.linkLibCpp();
2734 addCppSourceBytes(obj,2737 const obj2 = addObject(b, opts, .{
2735 \\extern int try_me();2738 .name = "obj2",
2736 \\int try_again() {2739 .cpp_source_bytes =
2737 \\ return try_me();2740 \\extern int try_me();
2738 \\}2741 \\int try_again() {
2739 , &.{});2742 \\ return try_me();
2740 obj.linkLibCpp();2743 \\}
2744 ,
2745 });
2746 obj2.linkLibCpp();
27412747
2742 const exe = addExecutable(b, opts, .{ .name = "test1" });2748 const obj = addObject(b, opts, .{ .name = "obj" });
2743 addCppSourceBytes(exe,2749 obj.addObject(obj1);
2744 \\#include <iostream>2750 obj.addObject(obj2);
2745 \\#include <stdexcept>2751 obj.linkLibCpp();
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();
27582752
2759 const run = addRunArtifact(exe);2753 const exe = addExecutable(b, opts, .{ .name = "test1" });
2760 run.expectStdOutEqual("exception=Oh no!");2754 addCppSourceBytes(exe,
2761 test_step.dependOn(&run.step);2755 \\#include <iostream>
2762 }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 {2770 const run = addRunArtifact(exe);
2765 // Let's make the object file COMDAT group heavy!2771 run.expectStdOutEqual("exception=Oh no!");
2766 const obj = addObject(b, opts, .{2772 test_step.dependOn(&run.step);
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();
27952773
2796 const exe = addExecutable(b, opts, .{ .name = "test2" });2774 return test_step;
2797 exe.addObject(obj);2775}
2798 exe.linkLibCpp();
27992776
2800 const run = addRunArtifact(exe);2777fn testRelocatableEhFrameComdatHeavy(b: *Build, opts: Options) *Step {
2801 run.expectStdOutEqual("exception=Oh no!");2778 const test_step = addTestStep(b, "relocatable-eh-frame-comdat-heavy", opts);
2802 test_step.dependOn(&run.step);2779
2803 }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
2805 return test_step;2832 return test_step;
2806}2833}
...@@ -3730,11 +3757,15 @@ fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {...@@ -3730,11 +3757,15 @@ fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {
3730 \\#include <pthread.h>3757 \\#include <pthread.h>
3731 \\#include <dlfcn.h>3758 \\#include <dlfcn.h>
3732 \\#include <assert.h>3759 \\#include <assert.h>
3760 \\#include <stdio.h>
3733 \\void *(*verify)(void *);3761 \\void *(*verify)(void *);
3734 \\3762 \\
3735 \\int main() {3763 \\int main() {
3736 \\ void *handle = dlopen("liba.so", RTLD_NOW);3764 \\ 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 \\ }
3738 \\ *(void**)(&verify) = dlsym(handle, "verify");3769 \\ *(void**)(&verify) = dlsym(handle, "verify");
3739 \\ assert(verify);3770 \\ assert(verify);
3740 \\3771 \\
...@@ -3907,16 +3938,8 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {...@@ -3907,16 +3938,8 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
3907 exe.linkLibrary(dylib);3938 exe.linkLibrary(dylib);
3908 exe.linkLibC();3939 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 // } });
3918 expectLinkErrors(exe, test_step, .{3941 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",
3920 });3943 });
39213944
3922 return test_step;3945 return test_step;