| author | |
| committer | |
| log | c563ba6b15b65ecdc1cb538c9437e11dfb330453 |
| tree | 99dd968efc3daea52a1d3628b7d8cedba53e84b7 |
| parent | 33d07f4b6efe461ee3fbfa32cb18f60aac8c2827 |
| parent | 4bdc2d38717b5655acd862a5762e069419b158c7 |
| signature |
move linker input file parsing to the frontend40 files changed, 3260 insertions(+), 2665 deletions(-)
CMakeLists.txt+2-1| ... | ... | @@ -522,6 +522,7 @@ set(ZIG_STAGE2_SOURCES |
| 522 | 522 | src/Sema.zig |
| 523 | 523 | src/Sema/bitcast.zig |
| 524 | 524 | src/Sema/comptime_ptr_access.zig |
| 525 | src/ThreadSafeQueue.zig | |
| 525 | 526 | src/Type.zig |
| 526 | 527 | src/Value.zig |
| 527 | 528 | src/Zcu.zig |
| ... | ... | @@ -601,7 +602,6 @@ set(ZIG_STAGE2_SOURCES |
| 601 | 602 | src/link/Elf/Archive.zig |
| 602 | 603 | src/link/Elf/Atom.zig |
| 603 | 604 | src/link/Elf/AtomList.zig |
| 604 | src/link/Elf/LdScript.zig | |
| 605 | 605 | src/link/Elf/LinkerDefined.zig |
| 606 | 606 | src/link/Elf/Merge.zig |
| 607 | 607 | src/link/Elf/Object.zig |
| ... | ... | @@ -615,6 +615,7 @@ set(ZIG_STAGE2_SOURCES |
| 615 | 615 | src/link/Elf/relocatable.zig |
| 616 | 616 | src/link/Elf/relocation.zig |
| 617 | 617 | src/link/Elf/synthetic_sections.zig |
| 618 | src/link/LdScript.zig | |
| 618 | 619 | src/link/MachO.zig |
| 619 | 620 | src/link/MachO/Archive.zig |
| 620 | 621 | src/link/MachO/Atom.zig |
lib/c.zig-4| ... | ... | @@ -38,8 +38,6 @@ comptime { |
| 38 | 38 | @export(&strncpy, .{ .name = "strncpy", .linkage = .strong }); |
| 39 | 39 | @export(&strcat, .{ .name = "strcat", .linkage = .strong }); |
| 40 | 40 | @export(&strncat, .{ .name = "strncat", .linkage = .strong }); |
| 41 | } else if (is_msvc) { | |
| 42 | @export(&_fltused, .{ .name = "_fltused", .linkage = .strong }); | |
| 43 | 41 | } |
| 44 | 42 | } |
| 45 | 43 | |
| ... | ... | @@ -62,8 +60,6 @@ fn wasm_start() callconv(.C) void { |
| 62 | 60 | _ = main(0, undefined); |
| 63 | 61 | } |
| 64 | 62 | |
| 65 | var _fltused: c_int = 1; | |
| 66 | ||
| 67 | 63 | fn strcpy(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 { |
| 68 | 64 | var i: usize = 0; |
| 69 | 65 | while (src[i] != 0) : (i += 1) { |
lib/compiler/build_runner.zig+6| ... | ... | @@ -280,6 +280,10 @@ pub fn main() !void { |
| 280 | 280 | builder.enable_darling = true; |
| 281 | 281 | } else if (mem.eql(u8, arg, "-fno-darling")) { |
| 282 | 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 | 287 | } else if (mem.eql(u8, arg, "-freference-trace")) { |
| 284 | 288 | builder.reference_trace = 256; |
| 285 | 289 | } else if (mem.startsWith(u8, arg, "-freference-trace=")) { |
| ... | ... | @@ -1341,6 +1345,8 @@ fn usage(b: *std.Build, out_stream: anytype) !void { |
| 1341 | 1345 | \\Advanced Options: |
| 1342 | 1346 | \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error |
| 1343 | 1347 | \\ -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 | 1350 | \\ --build-file [file] Override path to build.zig |
| 1345 | 1351 | \\ --cache-dir [path] Override path to local Zig cache directory |
| 1346 | 1352 | \\ --global-cache-dir [path] Override path to global Zig cache directory |
lib/compiler_rt.zig+8-1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | const common = @import("compiler_rt/common.zig"); | |
| 2 | 3 | |
| 3 | pub const panic = @import("compiler_rt/common.zig").panic; | |
| 4 | pub const panic = common.panic; | |
| 4 | 5 | |
| 5 | 6 | comptime { |
| 6 | 7 | // Integer routines |
| ... | ... | @@ -236,4 +237,10 @@ comptime { |
| 236 | 237 | _ = @import("compiler_rt/bcmp.zig"); |
| 237 | 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 | ||
| 246 | var _fltused: c_int = 1; |
lib/std/Build.zig+1| ... | ... | @@ -123,6 +123,7 @@ pub const Graph = struct { |
| 123 | 123 | incremental: ?bool = null, |
| 124 | 124 | random_seed: u32 = 0, |
| 125 | 125 | dependency_cache: InitializedDepMap = .empty, |
| 126 | allow_so_scripts: ?bool = null, | |
| 126 | 127 | }; |
| 127 | 128 | |
| 128 | 129 | const 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 | 142 | pub const File = struct { |
| 143 | 143 | prefixed_path: PrefixedPath, |
| 144 | 144 | max_file_size: ?usize, |
| 145 | /// Populated if the user calls `addOpenedFile`. | |
| 146 | /// The handle is not owned here. | |
| 147 | handle: ?fs.File, | |
| 145 | 148 | stat: Stat, |
| 146 | 149 | bin_digest: BinDigest, |
| 147 | 150 | contents: ?[]const u8, |
| ... | ... | @@ -173,6 +176,11 @@ pub const File = struct { |
| 173 | 176 | const new = new_max_size orelse return; |
| 174 | 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 | }; |
| 177 | 185 | |
| 178 | 186 | pub const HashHelper = struct { |
| ... | ... | @@ -363,15 +371,20 @@ pub const Manifest = struct { |
| 363 | 371 | /// var file_contents = cache_hash.files.keys()[file_index].contents.?; |
| 364 | 372 | /// ``` |
| 365 | 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 | 379 | const gpa = m.cache.gpa; |
| 367 | 380 | try m.files.ensureUnusedCapacity(gpa, 1); |
| 368 | 381 | const resolved_path = try fs.path.resolve(gpa, &.{ |
| 369 | file_path.root_dir.path orelse ".", | |
| 370 | file_path.subPathOrDot(), | |
| 382 | path.root_dir.path orelse ".", | |
| 383 | path.subPathOrDot(), | |
| 371 | 384 | }); |
| 372 | 385 | errdefer gpa.free(resolved_path); |
| 373 | 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 | } |
| 376 | 389 | |
| 377 | 390 | /// Deprecated; use `addFilePath`. |
| ... | ... | @@ -383,13 +396,14 @@ pub const Manifest = struct { |
| 383 | 396 | const prefixed_path = try self.cache.findPrefix(file_path); |
| 384 | 397 | errdefer gpa.free(prefixed_path.sub_path); |
| 385 | 398 | |
| 386 | return addFileInner(self, prefixed_path, max_file_size); | |
| 399 | return addFileInner(self, prefixed_path, null, max_file_size); | |
| 387 | 400 | } |
| 388 | 401 | |
| 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 | 403 | const gop = self.files.getOrPutAssumeCapacityAdapted(prefixed_path, FilesAdapter{}); |
| 391 | 404 | if (gop.found_existing) { |
| 392 | 405 | gop.key_ptr.updateMaxSize(max_file_size); |
| 406 | gop.key_ptr.updateHandle(handle); | |
| 393 | 407 | return gop.index; |
| 394 | 408 | } |
| 395 | 409 | gop.key_ptr.* = .{ |
| ... | ... | @@ -398,6 +412,7 @@ pub const Manifest = struct { |
| 398 | 412 | .max_file_size = max_file_size, |
| 399 | 413 | .stat = undefined, |
| 400 | 414 | .bin_digest = undefined, |
| 415 | .handle = handle, | |
| 401 | 416 | }; |
| 402 | 417 | |
| 403 | 418 | self.hash.add(prefixed_path.prefix); |
| ... | ... | @@ -565,6 +580,7 @@ pub const Manifest = struct { |
| 565 | 580 | }, |
| 566 | 581 | .contents = null, |
| 567 | 582 | .max_file_size = null, |
| 583 | .handle = null, | |
| 568 | 584 | .stat = .{ |
| 569 | 585 | .size = stat_size, |
| 570 | 586 | .inode = stat_inode, |
| ... | ... | @@ -708,12 +724,19 @@ pub const Manifest = struct { |
| 708 | 724 | } |
| 709 | 725 | |
| 710 | 726 | fn populateFileHash(self: *Manifest, ch_file: *File) !void { |
| 711 | const pp = ch_file.prefixed_path; | |
| 712 | const dir = self.cache.prefixes()[pp.prefix].handle; | |
| 713 | const file = try dir.openFile(pp.sub_path, .{}); | |
| 714 | defer file.close(); | |
| 727 | if (ch_file.handle) |handle| { | |
| 728 | return populateFileHashHandle(self, ch_file, handle); | |
| 729 | } else { | |
| 730 | const pp = ch_file.prefixed_path; | |
| 731 | const dir = self.cache.prefixes()[pp.prefix].handle; | |
| 732 | const handle = try dir.openFile(pp.sub_path, .{}); | |
| 733 | defer handle.close(); | |
| 734 | return populateFileHashHandle(self, ch_file, handle); | |
| 735 | } | |
| 736 | } | |
| 715 | 737 | |
| 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 | 740 | ch_file.stat = .{ |
| 718 | 741 | .size = actual_stat.size, |
| 719 | 742 | .mtime = actual_stat.mtime, |
| ... | ... | @@ -739,8 +762,7 @@ pub const Manifest = struct { |
| 739 | 762 | var hasher = hasher_init; |
| 740 | 763 | var off: usize = 0; |
| 741 | 764 | while (true) { |
| 742 | // give me everything you've got, captain | |
| 743 | const bytes_read = try file.read(contents[off..]); | |
| 765 | const bytes_read = try handle.pread(contents[off..], off); | |
| 744 | 766 | if (bytes_read == 0) break; |
| 745 | 767 | hasher.update(contents[off..][0..bytes_read]); |
| 746 | 768 | off += bytes_read; |
| ... | ... | @@ -749,7 +771,7 @@ pub const Manifest = struct { |
| 749 | 771 | |
| 750 | 772 | ch_file.contents = contents; |
| 751 | 773 | } else { |
| 752 | try hashFile(file, &ch_file.bin_digest); | |
| 774 | try hashFile(handle, &ch_file.bin_digest); | |
| 753 | 775 | } |
| 754 | 776 | |
| 755 | 777 | self.hash.hasher.update(&ch_file.bin_digest); |
| ... | ... | @@ -813,6 +835,7 @@ pub const Manifest = struct { |
| 813 | 835 | gop.key_ptr.* = .{ |
| 814 | 836 | .prefixed_path = prefixed_path, |
| 815 | 837 | .max_file_size = null, |
| 838 | .handle = null, | |
| 816 | 839 | .stat = undefined, |
| 817 | 840 | .bin_digest = undefined, |
| 818 | 841 | .contents = null, |
| ... | ... | @@ -851,6 +874,7 @@ pub const Manifest = struct { |
| 851 | 874 | new_file.* = .{ |
| 852 | 875 | .prefixed_path = prefixed_path, |
| 853 | 876 | .max_file_size = null, |
| 877 | .handle = null, | |
| 854 | 878 | .stat = stat, |
| 855 | 879 | .bin_digest = undefined, |
| 856 | 880 | .contents = null, |
| ... | ... | @@ -1067,6 +1091,7 @@ pub const Manifest = struct { |
| 1067 | 1091 | gop.key_ptr.* = .{ |
| 1068 | 1092 | .prefixed_path = prefixed_path, |
| 1069 | 1093 | .max_file_size = file.max_file_size, |
| 1094 | .handle = file.handle, | |
| 1070 | 1095 | .stat = file.stat, |
| 1071 | 1096 | .bin_digest = file.bin_digest, |
| 1072 | 1097 | .contents = null, |
| ... | ... | @@ -1103,14 +1128,14 @@ pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void |
| 1103 | 1128 | |
| 1104 | 1129 | fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void { |
| 1105 | 1130 | var buf: [1024]u8 = undefined; |
| 1106 | ||
| 1107 | 1131 | var hasher = hasher_init; |
| 1132 | var off: u64 = 0; | |
| 1108 | 1133 | while (true) { |
| 1109 | const bytes_read = try file.read(&buf); | |
| 1134 | const bytes_read = try file.pread(&buf, off); | |
| 1110 | 1135 | if (bytes_read == 0) break; |
| 1111 | 1136 | hasher.update(buf[0..bytes_read]); |
| 1137 | off += bytes_read; | |
| 1112 | 1138 | } |
| 1113 | ||
| 1114 | 1139 | hasher.final(bin_digest); |
| 1115 | 1140 | } |
| 1116 | 1141 |
lib/std/Build/Step/Compile.zig+42-8| ... | ... | @@ -186,6 +186,15 @@ want_lto: ?bool = null, |
| 186 | 186 | use_llvm: ?bool, |
| 187 | 187 | use_lld: ?bool, |
| 188 | 188 | |
| 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. | |
| 196 | allow_so_scripts: ?bool = null, | |
| 197 | ||
| 189 | 198 | /// This is an advanced setting that can change the intent of this Compile step. |
| 190 | 199 | /// If this value is non-null, it means that this Compile step exists to |
| 191 | 200 | /// check for compile errors and return *success* if they match, and failure |
| ... | ... | @@ -236,6 +245,7 @@ pub const ExpectedCompileErrors = union(enum) { |
| 236 | 245 | contains: []const u8, |
| 237 | 246 | exact: []const []const u8, |
| 238 | 247 | starts_with: []const u8, |
| 248 | stderr_contains: []const u8, | |
| 239 | 249 | }; |
| 240 | 250 | |
| 241 | 251 | pub const Entry = union(enum) { |
| ... | ... | @@ -1035,6 +1045,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { |
| 1035 | 1045 | if (b.reference_trace) |some| { |
| 1036 | 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); | |
| 1038 | 1049 | |
| 1039 | 1050 | try addFlag(&zig_args, "llvm", compile.use_llvm); |
| 1040 | 1051 | try addFlag(&zig_args, "lld", compile.use_lld); |
| ... | ... | @@ -1945,24 +1956,24 @@ fn checkCompileErrors(compile: *Compile) !void { |
| 1945 | 1956 | |
| 1946 | 1957 | const arena = compile.step.owner.allocator; |
| 1947 | 1958 | |
| 1948 | var actual_stderr_list = std.ArrayList(u8).init(arena); | |
| 1959 | var actual_errors_list = std.ArrayList(u8).init(arena); | |
| 1949 | 1960 | try actual_eb.renderToWriter(.{ |
| 1950 | 1961 | .ttyconf = .no_color, |
| 1951 | 1962 | .include_reference_trace = false, |
| 1952 | 1963 | .include_source_line = false, |
| 1953 | }, actual_stderr_list.writer()); | |
| 1954 | const actual_stderr = try actual_stderr_list.toOwnedSlice(); | |
| 1964 | }, actual_errors_list.writer()); | |
| 1965 | const actual_errors = try actual_errors_list.toOwnedSlice(); | |
| 1955 | 1966 | |
| 1956 | 1967 | // Render the expected lines into a string that we can compare verbatim. |
| 1957 | 1968 | var expected_generated = std.ArrayList(u8).init(arena); |
| 1958 | 1969 | const expect_errors = compile.expect_errors.?; |
| 1959 | 1970 | |
| 1960 | var actual_line_it = mem.splitScalar(u8, actual_stderr, '\n'); | |
| 1971 | var actual_line_it = mem.splitScalar(u8, actual_errors, '\n'); | |
| 1961 | 1972 | |
| 1962 | 1973 | // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile |
| 1963 | 1974 | switch (expect_errors) { |
| 1964 | 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 | 1977 | return compile.step.fail( |
| 1967 | 1978 | \\ |
| 1968 | 1979 | \\========= should start with: ============ |
| ... | ... | @@ -1970,7 +1981,7 @@ fn checkCompileErrors(compile: *Compile) !void { |
| 1970 | 1981 | \\========= but not found: ================ |
| 1971 | 1982 | \\{s} |
| 1972 | 1983 | \\========================================= |
| 1973 | , .{ expect_starts_with, actual_stderr }); | |
| 1984 | , .{ expect_starts_with, actual_errors }); | |
| 1974 | 1985 | }, |
| 1975 | 1986 | .contains => |expect_line| { |
| 1976 | 1987 | while (actual_line_it.next()) |actual_line| { |
| ... | ... | @@ -1978,6 +1989,29 @@ fn checkCompileErrors(compile: *Compile) !void { |
| 1978 | 1989 | return; |
| 1979 | 1990 | } |
| 1980 | 1991 | |
| 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 | 2015 | return compile.step.fail( |
| 1982 | 2016 | \\ |
| 1983 | 2017 | \\========= should contain: =============== |
| ... | ... | @@ -2003,7 +2037,7 @@ fn checkCompileErrors(compile: *Compile) !void { |
| 2003 | 2037 | try expected_generated.append('\n'); |
| 2004 | 2038 | } |
| 2005 | 2039 | |
| 2006 | if (mem.eql(u8, expected_generated.items, actual_stderr)) return; | |
| 2040 | if (mem.eql(u8, expected_generated.items, actual_errors)) return; | |
| 2007 | 2041 | |
| 2008 | 2042 | return compile.step.fail( |
| 2009 | 2043 | \\ |
| ... | ... | @@ -2012,7 +2046,7 @@ fn checkCompileErrors(compile: *Compile) !void { |
| 2012 | 2046 | \\========= but found: ==================== |
| 2013 | 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 | 14 | assert((state / one_pending) < (std.math.maxInt(usize) / one_pending)); |
| 15 | 15 | } |
| 16 | 16 | |
| 17 | pub 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 | ||
| 17 | 22 | pub fn finish(self: *WaitGroup) void { |
| 18 | 23 | const state = self.state.fetchSub(one_pending, .acq_rel); |
| 19 | 24 | assert((state / one_pending) > 0); |
src/Compilation.zig+297-388| ... | ... | @@ -10,6 +10,7 @@ const Target = std.Target; |
| 10 | 10 | const ThreadPool = std.Thread.Pool; |
| 11 | 11 | const WaitGroup = std.Thread.WaitGroup; |
| 12 | 12 | const ErrorBundle = std.zig.ErrorBundle; |
| 13 | const Path = Cache.Path; | |
| 13 | 14 | |
| 14 | 15 | const Value = @import("Value.zig"); |
| 15 | 16 | const Type = @import("Type.zig"); |
| ... | ... | @@ -39,15 +40,17 @@ const Air = @import("Air.zig"); |
| 39 | 40 | const Builtin = @import("Builtin.zig"); |
| 40 | 41 | const LlvmObject = @import("codegen/llvm.zig").Object; |
| 41 | 42 | const dev = @import("dev.zig"); |
| 42 | pub const Directory = Cache.Directory; | |
| 43 | const Path = Cache.Path; | |
| 43 | const ThreadSafeQueue = @import("ThreadSafeQueue.zig").ThreadSafeQueue; | |
| 44 | 44 | |
| 45 | pub const Directory = Cache.Directory; | |
| 45 | 46 | pub const Config = @import("Compilation/Config.zig"); |
| 46 | 47 | |
| 47 | 48 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| 48 | 49 | gpa: Allocator, |
| 49 | 50 | /// Arena-allocated memory, mostly used during initialization. However, it can |
| 50 | 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. | |
| 51 | 54 | arena: Allocator, |
| 52 | 55 | /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. |
| 53 | 56 | zcu: ?*Zcu, |
| ... | ... | @@ -76,12 +79,13 @@ implib_emit: ?Path, |
| 76 | 79 | docs_emit: ?Path, |
| 77 | 80 | root_name: [:0]const u8, |
| 78 | 81 | include_compiler_rt: bool, |
| 79 | objects: []Compilation.LinkObject, | |
| 82 | /// Resolved into known paths, any GNU ld scripts already resolved. | |
| 83 | link_inputs: []const link.Input, | |
| 80 | 84 | /// Needed only for passing -F args to clang. |
| 81 | 85 | framework_dirs: []const []const u8, |
| 82 | /// These are *always* dynamically linked. Static libraries will be | |
| 83 | /// provided as positional arguments. | |
| 84 | system_libs: std.StringArrayHashMapUnmanaged(SystemLib), | |
| 86 | /// These are only for DLLs dependencies fulfilled by the `.def` files shipped | |
| 87 | /// with Zig. Static libraries are provided as `link.Input` values. | |
| 88 | windows_libs: std.StringArrayHashMapUnmanaged(void), | |
| 85 | 89 | version: ?std.SemanticVersion, |
| 86 | 90 | libc_installation: ?*const LibCInstallation, |
| 87 | 91 | skip_linker_dependencies: bool, |
| ... | ... | @@ -107,6 +111,9 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa |
| 107 | 111 | } = .{}, |
| 108 | 112 | |
| 109 | 113 | link_diags: link.Diags, |
| 114 | link_task_queue: ThreadSafeQueue(link.Task) = .empty, | |
| 115 | /// Ensure only 1 simultaneous call to `flushTaskQueue`. | |
| 116 | link_task_queue_safety: std.debug.SafetyLock = .{}, | |
| 110 | 117 | |
| 111 | 118 | work_queues: [ |
| 112 | 119 | len: { |
| ... | ... | @@ -118,14 +125,6 @@ work_queues: [ |
| 118 | 125 | } |
| 119 | 126 | ]std.fifo.LinearFifo(Job, .Dynamic), |
| 120 | 127 | |
| 121 | codegen_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 | 128 | /// These jobs are to invoke the Clang compiler to create an object file, which |
| 130 | 129 | /// gets linked with the Compilation. |
| 131 | 130 | c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic), |
| ... | ... | @@ -262,6 +261,9 @@ emit_asm: ?EmitLoc, |
| 262 | 261 | emit_llvm_ir: ?EmitLoc, |
| 263 | 262 | emit_llvm_bc: ?EmitLoc, |
| 264 | 263 | |
| 264 | link_task_wait_group: WaitGroup = .{}, | |
| 265 | work_queue_progress_node: std.Progress.Node = .none, | |
| 266 | ||
| 265 | 267 | llvm_opt_bisect_limit: c_int, |
| 266 | 268 | |
| 267 | 269 | file_system_inputs: ?*std.ArrayListUnmanaged(u8), |
| ... | ... | @@ -339,16 +341,14 @@ pub const RcIncludes = enum { |
| 339 | 341 | }; |
| 340 | 342 | |
| 341 | 343 | const 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 | 346 | codegen_nav: InternPool.Nav.Index, |
| 344 | /// Write the machine code for a function to the output file. | |
| 345 | codegen_func: struct { | |
| 346 | /// This will either be a non-generic `func_decl` or a `func_instance`. | |
| 347 | func: InternPool.Index, | |
| 348 | /// This `Air` is owned by the `Job` and allocated with `gpa`. | |
| 349 | /// It must be deinited when the job is processed. | |
| 350 | air: Air, | |
| 351 | }, | |
| 347 | /// Corresponds to the task in `link.Task`. | |
| 348 | /// Only needed for backends that haven't yet been updated to not race against Sema. | |
| 349 | codegen_func: link.Task.CodegenFunc, | |
| 350 | /// Corresponds to the task in `link.Task`. | |
| 351 | /// Only needed for backends that haven't yet been updated to not race against Sema. | |
| 352 | 352 | codegen_type: InternPool.Index, |
| 353 | 353 | /// The `Cau` must be semantically analyzed (and possibly export itself). |
| 354 | 354 | /// This may be its first time being analyzed, or it may be outdated. |
| ... | ... | @@ -357,9 +357,6 @@ const Job = union(enum) { |
| 357 | 357 | /// After analysis, a `codegen_func` job will be queued. |
| 358 | 358 | /// These must be separate jobs to ensure any needed type resolution occurs *before* codegen. |
| 359 | 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 | 360 | /// The main source file for the module needs to be analyzed. |
| 364 | 361 | analyze_mod: *Package.Module, |
| 365 | 362 | /// Fully resolve the given `struct` or `union` type. |
| ... | ... | @@ -373,6 +370,7 @@ const Job = union(enum) { |
| 373 | 370 | musl_crt_file: musl.CrtFile, |
| 374 | 371 | /// one of the mingw-w64 static objects |
| 375 | 372 | mingw_crt_file: mingw.CrtFile, |
| 373 | ||
| 376 | 374 | /// libunwind.a, usually needed when linking libc |
| 377 | 375 | libunwind: void, |
| 378 | 376 | libcxx: void, |
| ... | ... | @@ -384,7 +382,7 @@ const Job = union(enum) { |
| 384 | 382 | /// one of WASI libc static objects |
| 385 | 383 | wasi_libc_crt_file: wasi_libc.CrtFile, |
| 386 | 384 | |
| 387 | /// The value is the index into `system_libs`. | |
| 385 | /// The value is the index into `windows_libs`. | |
| 388 | 386 | windows_import_lib: usize, |
| 389 | 387 | |
| 390 | 388 | const Tag = @typeInfo(Job).@"union".tag_type.?; |
| ... | ... | @@ -402,17 +400,6 @@ const Job = union(enum) { |
| 402 | 400 | } |
| 403 | 401 | }; |
| 404 | 402 | |
| 405 | const 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 | ||
| 416 | 403 | pub const CObject = struct { |
| 417 | 404 | /// Relative to cwd. Owned by arena. |
| 418 | 405 | src: CSourceFile, |
| ... | ... | @@ -999,24 +986,6 @@ const CacheUse = union(CacheMode) { |
| 999 | 986 | } |
| 1000 | 987 | }; |
| 1001 | 988 | |
| 1002 | pub 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 | ||
| 1020 | 989 | pub const CreateOptions = struct { |
| 1021 | 990 | zig_lib_directory: Directory, |
| 1022 | 991 | local_cache_directory: Directory, |
| ... | ... | @@ -1061,18 +1030,20 @@ pub const CreateOptions = struct { |
| 1061 | 1030 | /// this flag would be set to disable this machinery to avoid false positives. |
| 1062 | 1031 | disable_lld_caching: bool = false, |
| 1063 | 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 | 1037 | rpath_list: []const []const u8 = &[0][]const u8{}, |
| 1066 | 1038 | symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty, |
| 1067 | 1039 | c_source_files: []const CSourceFile = &.{}, |
| 1068 | 1040 | rc_source_files: []const RcSourceFile = &.{}, |
| 1069 | 1041 | manifest_file: ?[]const u8 = null, |
| 1070 | 1042 | rc_includes: RcIncludes = .any, |
| 1071 | link_objects: []LinkObject = &[0]LinkObject{}, | |
| 1043 | link_inputs: []const link.Input = &.{}, | |
| 1072 | 1044 | framework_dirs: []const []const u8 = &[0][]const u8{}, |
| 1073 | 1045 | frameworks: []const Framework = &.{}, |
| 1074 | system_lib_names: []const []const u8 = &.{}, | |
| 1075 | system_lib_infos: []const SystemLib = &.{}, | |
| 1046 | windows_lib_names: []const []const u8 = &.{}, | |
| 1076 | 1047 | /// These correspond to the WASI libc emulated subcomponents including: |
| 1077 | 1048 | /// * process clocks |
| 1078 | 1049 | /// * getpid |
| ... | ... | @@ -1455,12 +1426,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1455 | 1426 | }; |
| 1456 | 1427 | errdefer if (opt_zcu) |zcu| zcu.deinit(); |
| 1457 | 1428 | |
| 1458 | var system_libs = try std.StringArrayHashMapUnmanaged(SystemLib).init( | |
| 1459 | gpa, | |
| 1460 | options.system_lib_names, | |
| 1461 | options.system_lib_infos, | |
| 1462 | ); | |
| 1463 | errdefer system_libs.deinit(gpa); | |
| 1429 | var windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{}); | |
| 1430 | errdefer windows_libs.deinit(gpa); | |
| 1464 | 1431 | |
| 1465 | 1432 | comp.* = .{ |
| 1466 | 1433 | .gpa = gpa, |
| ... | ... | @@ -1479,13 +1446,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1479 | 1446 | .emit_llvm_ir = options.emit_llvm_ir, |
| 1480 | 1447 | .emit_llvm_bc = options.emit_llvm_bc, |
| 1481 | 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 | 1449 | .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa), |
| 1490 | 1450 | .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa) else .{}, |
| 1491 | 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 | 1482 | .libcxx_abi_version = options.libcxx_abi_version, |
| 1523 | 1483 | .root_name = root_name, |
| 1524 | 1484 | .sysroot = sysroot, |
| 1525 | .system_libs = system_libs, | |
| 1485 | .windows_libs = windows_libs, | |
| 1526 | 1486 | .version = options.version, |
| 1527 | 1487 | .libc_installation = libc_dirs.libc_installation, |
| 1528 | 1488 | .include_compiler_rt = include_compiler_rt, |
| 1529 | .objects = options.link_objects, | |
| 1489 | .link_inputs = options.link_inputs, | |
| 1530 | 1490 | .framework_dirs = options.framework_dirs, |
| 1531 | 1491 | .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit, |
| 1532 | 1492 | .skip_linker_dependencies = options.skip_linker_dependencies, |
| ... | ... | @@ -1564,7 +1524,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1564 | 1524 | .z_max_page_size = options.linker_z_max_page_size, |
| 1565 | 1525 | .darwin_sdk_layout = libc_dirs.darwin_sdk_layout, |
| 1566 | 1526 | .frameworks = options.frameworks, |
| 1567 | .lib_dirs = options.lib_dirs, | |
| 1527 | .lib_directories = options.lib_directories, | |
| 1568 | 1528 | .framework_dirs = options.framework_dirs, |
| 1569 | 1529 | .rpath_list = options.rpath_list, |
| 1570 | 1530 | .symbol_wrap_set = options.symbol_wrap_set, |
| ... | ... | @@ -1776,157 +1736,175 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1776 | 1736 | .incremental => comp.bin_file != null, |
| 1777 | 1737 | }; |
| 1778 | 1738 | |
| 1779 | if (have_bin_emit and !comp.skip_linker_dependencies and target.ofmt != .c) { | |
| 1780 | if (target.isDarwin()) { | |
| 1781 | switch (target.abi) { | |
| 1782 | .none, | |
| 1783 | .simulator, | |
| 1784 | .macabi, | |
| 1785 | => {}, | |
| 1786 | else => return error.LibCUnavailable, | |
| 1787 | } | |
| 1788 | } | |
| 1789 | // If we need to build glibc for the target, add work items for it. | |
| 1790 | // We go through the work queue so that building can be done in parallel. | |
| 1791 | if (comp.wantBuildGLibCFromSource()) { | |
| 1792 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; | |
| 1793 | ||
| 1794 | if (glibc.needsCrtiCrtn(target)) { | |
| 1795 | try comp.queueJobs(&[_]Job{ | |
| 1796 | .{ .glibc_crt_file = .crti_o }, | |
| 1797 | .{ .glibc_crt_file = .crtn_o }, | |
| 1798 | }); | |
| 1799 | } | |
| 1800 | try comp.queueJobs(&[_]Job{ | |
| 1801 | .{ .glibc_crt_file = .scrt1_o }, | |
| 1802 | .{ .glibc_crt_file = .libc_nonshared_a }, | |
| 1803 | .{ .glibc_shared_objects = {} }, | |
| 1804 | }); | |
| 1805 | } | |
| 1806 | if (comp.wantBuildMuslFromSource()) { | |
| 1807 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; | |
| 1808 | ||
| 1809 | if (musl.needsCrtiCrtn(target)) { | |
| 1810 | try comp.queueJobs(&[_]Job{ | |
| 1811 | .{ .musl_crt_file = .crti_o }, | |
| 1812 | .{ .musl_crt_file = .crtn_o }, | |
| 1813 | }); | |
| 1814 | } | |
| 1815 | try comp.queueJobs(&[_]Job{ | |
| 1816 | .{ .musl_crt_file = .crt1_o }, | |
| 1817 | .{ .musl_crt_file = .scrt1_o }, | |
| 1818 | .{ .musl_crt_file = .rcrt1_o }, | |
| 1819 | switch (comp.config.link_mode) { | |
| 1820 | .static => .{ .musl_crt_file = .libc_a }, | |
| 1821 | .dynamic => .{ .musl_crt_file = .libc_so }, | |
| 1822 | }, | |
| 1823 | }); | |
| 1824 | } | |
| 1739 | if (have_bin_emit and target.ofmt != .c) { | |
| 1740 | if (!comp.skip_linker_dependencies) { | |
| 1741 | // If we need to build libc for the target, add work items for it. | |
| 1742 | // We go through the work queue so that building can be done in parallel. | |
| 1743 | // If linking against host libc installation, instead queue up jobs | |
| 1744 | // for loading those files in the linker. | |
| 1745 | if (comp.config.link_libc and is_exe_or_dyn_lib) { | |
| 1746 | // If the "is darwin" check is moved below the libc_installation check below, | |
| 1747 | // error.LibCInstallationMissingCrtDir is returned from lci.resolveCrtPaths(). | |
| 1748 | if (target.isDarwin()) { | |
| 1749 | switch (target.abi) { | |
| 1750 | .none, .simulator, .macabi => {}, | |
| 1751 | else => return error.LibCUnavailable, | |
| 1752 | } | |
| 1753 | // TODO delete logic from MachO flush() and queue up tasks here instead. | |
| 1754 | } else if (comp.libc_installation) |lci| { | |
| 1755 | const basenames = LibCInstallation.CrtBasenames.get(.{ | |
| 1756 | .target = target, | |
| 1757 | .link_libc = comp.config.link_libc, | |
| 1758 | .output_mode = comp.config.output_mode, | |
| 1759 | .link_mode = comp.config.link_mode, | |
| 1760 | .pie = comp.config.pie, | |
| 1761 | }); | |
| 1762 | const paths = try lci.resolveCrtPaths(arena, basenames, target); | |
| 1825 | 1763 | |
| 1826 | if (comp.wantBuildWasiLibcFromSource()) { | |
| 1827 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; | |
| 1764 | const fields = @typeInfo(@TypeOf(paths)).@"struct".fields; | |
| 1765 | try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, fields.len + 1); | |
| 1766 | inline for (fields) |field| { | |
| 1767 | if (@field(paths, field.name)) |path| { | |
| 1768 | comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path }); | |
| 1769 | } | |
| 1770 | } | |
| 1771 | // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`. | |
| 1772 | comp.link_task_queue.shared.appendAssumeCapacity(.load_host_libc); | |
| 1773 | } else if (target.isMusl() and !target.isWasm()) { | |
| 1774 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; | |
| 1775 | ||
| 1776 | if (musl.needsCrtiCrtn(target)) { | |
| 1777 | try comp.queueJobs(&[_]Job{ | |
| 1778 | .{ .musl_crt_file = .crti_o }, | |
| 1779 | .{ .musl_crt_file = .crtn_o }, | |
| 1780 | }); | |
| 1781 | } | |
| 1782 | if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| { | |
| 1783 | try comp.queueJobs(&.{.{ .musl_crt_file = f }}); | |
| 1784 | } | |
| 1785 | try comp.queueJobs(&.{.{ .musl_crt_file = switch (comp.config.link_mode) { | |
| 1786 | .static => .libc_a, | |
| 1787 | .dynamic => .libc_so, | |
| 1788 | } }}); | |
| 1789 | } else if (target.isGnuLibC()) { | |
| 1790 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; | |
| 1791 | ||
| 1792 | if (glibc.needsCrtiCrtn(target)) { | |
| 1793 | try comp.queueJobs(&[_]Job{ | |
| 1794 | .{ .glibc_crt_file = .crti_o }, | |
| 1795 | .{ .glibc_crt_file = .crtn_o }, | |
| 1796 | }); | |
| 1797 | } | |
| 1798 | if (!is_dyn_lib) { | |
| 1799 | try comp.queueJob(.{ .glibc_crt_file = .scrt1_o }); | |
| 1800 | } | |
| 1801 | try comp.queueJobs(&[_]Job{ | |
| 1802 | .{ .glibc_shared_objects = {} }, | |
| 1803 | .{ .glibc_crt_file = .libc_nonshared_a }, | |
| 1804 | }); | |
| 1805 | } else if (target.isWasm() and target.os.tag == .wasi) { | |
| 1806 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; | |
| 1828 | 1807 | |
| 1829 | for (comp.wasi_emulated_libs) |crt_file| { | |
| 1830 | try comp.queueJob(.{ | |
| 1831 | .wasi_libc_crt_file = crt_file, | |
| 1832 | }); | |
| 1833 | } | |
| 1834 | try comp.queueJobs(&[_]Job{ | |
| 1835 | .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) }, | |
| 1836 | .{ .wasi_libc_crt_file = .libc_a }, | |
| 1837 | }); | |
| 1838 | } | |
| 1808 | for (comp.wasi_emulated_libs) |crt_file| { | |
| 1809 | try comp.queueJob(.{ | |
| 1810 | .wasi_libc_crt_file = crt_file, | |
| 1811 | }); | |
| 1812 | } | |
| 1813 | try comp.queueJobs(&[_]Job{ | |
| 1814 | .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) }, | |
| 1815 | .{ .wasi_libc_crt_file = .libc_a }, | |
| 1816 | }); | |
| 1817 | } else if (target.isMinGW()) { | |
| 1818 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; | |
| 1839 | 1819 | |
| 1840 | if (comp.wantBuildMinGWFromSource()) { | |
| 1841 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; | |
| 1820 | const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o }; | |
| 1821 | try comp.queueJobs(&.{ | |
| 1822 | .{ .mingw_crt_file = .mingw32_lib }, | |
| 1823 | crt_job, | |
| 1824 | }); | |
| 1842 | 1825 | |
| 1843 | const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o }; | |
| 1844 | try comp.queueJobs(&.{ | |
| 1845 | .{ .mingw_crt_file = .mingw32_lib }, | |
| 1846 | crt_job, | |
| 1847 | }); | |
| 1826 | // When linking mingw-w64 there are some import libs we always need. | |
| 1827 | try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); | |
| 1828 | for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {}); | |
| 1829 | } else if (target.isDarwin()) { | |
| 1830 | switch (target.abi) { | |
| 1831 | .none, .simulator, .macabi => {}, | |
| 1832 | else => return error.LibCUnavailable, | |
| 1833 | } | |
| 1834 | } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) { | |
| 1835 | try comp.queueJob(.{ .zig_libc = {} }); | |
| 1836 | } else { | |
| 1837 | return error.LibCUnavailable; | |
| 1838 | } | |
| 1839 | } | |
| 1848 | 1840 | |
| 1849 | // When linking mingw-w64 there are some import libs we always need. | |
| 1850 | for (mingw.always_link_libs) |name| { | |
| 1851 | try comp.system_libs.put(comp.gpa, name, .{ | |
| 1852 | .needed = false, | |
| 1853 | .weak = false, | |
| 1854 | .path = null, | |
| 1855 | }); | |
| 1841 | // Generate Windows import libs. | |
| 1842 | if (target.os.tag == .windows) { | |
| 1843 | const count = comp.windows_libs.count(); | |
| 1844 | for (0..count) |i| { | |
| 1845 | try comp.queueJob(.{ .windows_import_lib = i }); | |
| 1846 | } | |
| 1856 | 1847 | } |
| 1857 | } | |
| 1858 | // Generate Windows import libs. | |
| 1859 | if (target.os.tag == .windows) { | |
| 1860 | const count = comp.system_libs.count(); | |
| 1861 | for (0..count) |i| { | |
| 1862 | try comp.queueJob(.{ .windows_import_lib = i }); | |
| 1848 | if (comp.wantBuildLibUnwindFromSource()) { | |
| 1849 | try comp.queueJob(.{ .libunwind = {} }); | |
| 1850 | } | |
| 1851 | if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) { | |
| 1852 | try comp.queueJob(.libcxx); | |
| 1853 | try comp.queueJob(.libcxxabi); | |
| 1854 | } | |
| 1855 | if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) { | |
| 1856 | try comp.queueJob(.libtsan); | |
| 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 | } | |
| 1875 | 1858 | |
| 1876 | if (target.isMinGW() and comp.config.any_non_single_threaded) { | |
| 1877 | // LLD might drop some symbols as unused during LTO and GCing, therefore, | |
| 1878 | // we force mark them for resolution here. | |
| 1859 | if (target.isMinGW() and comp.config.any_non_single_threaded) { | |
| 1860 | // LLD might drop some symbols as unused during LTO and GCing, therefore, | |
| 1861 | // we force mark them for resolution here. | |
| 1879 | 1862 | |
| 1880 | const tls_index_sym = switch (target.cpu.arch) { | |
| 1881 | .x86 => "__tls_index", | |
| 1882 | else => "_tls_index", | |
| 1883 | }; | |
| 1863 | const tls_index_sym = switch (target.cpu.arch) { | |
| 1864 | .x86 => "__tls_index", | |
| 1865 | else => "_tls_index", | |
| 1866 | }; | |
| 1884 | 1867 | |
| 1885 | try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {}); | |
| 1886 | } | |
| 1868 | try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {}); | |
| 1869 | } | |
| 1887 | 1870 | |
| 1888 | if (comp.include_compiler_rt and capable_of_building_compiler_rt) { | |
| 1889 | if (is_exe_or_dyn_lib) { | |
| 1890 | log.debug("queuing a job to build compiler_rt_lib", .{}); | |
| 1891 | comp.job_queued_compiler_rt_lib = true; | |
| 1892 | } else if (output_mode != .Obj) { | |
| 1893 | log.debug("queuing a job to build compiler_rt_obj", .{}); | |
| 1894 | // In this case we are making a static library, so we ask | |
| 1895 | // for a compiler-rt object to put in it. | |
| 1896 | comp.job_queued_compiler_rt_obj = true; | |
| 1871 | if (comp.include_compiler_rt and capable_of_building_compiler_rt) { | |
| 1872 | if (is_exe_or_dyn_lib) { | |
| 1873 | log.debug("queuing a job to build compiler_rt_lib", .{}); | |
| 1874 | comp.job_queued_compiler_rt_lib = true; | |
| 1875 | } else if (output_mode != .Obj) { | |
| 1876 | log.debug("queuing a job to build compiler_rt_obj", .{}); | |
| 1877 | // In this case we are making a static library, so we ask | |
| 1878 | // for a compiler-rt object to put in it. | |
| 1879 | comp.job_queued_compiler_rt_obj = true; | |
| 1880 | } | |
| 1897 | 1881 | } |
| 1898 | } | |
| 1899 | 1882 | |
| 1900 | if (comp.config.any_fuzz and capable_of_building_compiler_rt) { | |
| 1901 | if (is_exe_or_dyn_lib) { | |
| 1883 | if (is_exe_or_dyn_lib and comp.config.any_fuzz and capable_of_building_compiler_rt) { | |
| 1902 | 1884 | log.debug("queuing a job to build libfuzzer", .{}); |
| 1903 | 1885 | comp.job_queued_fuzzer_lib = true; |
| 1904 | 1886 | } |
| 1905 | 1887 | } |
| 1906 | 1888 | |
| 1907 | if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and | |
| 1908 | !comp.config.link_libc and capable_of_building_zig_libc) | |
| 1909 | { | |
| 1910 | try comp.queueJob(.{ .zig_libc = {} }); | |
| 1911 | } | |
| 1889 | try comp.link_task_queue.shared.append(gpa, .load_explicitly_provided); | |
| 1912 | 1890 | } |
| 1913 | 1891 | |
| 1914 | 1892 | return comp; |
| 1915 | 1893 | } |
| 1916 | 1894 | |
| 1917 | 1895 | pub fn destroy(comp: *Compilation) void { |
| 1896 | const gpa = comp.gpa; | |
| 1897 | ||
| 1918 | 1898 | if (comp.bin_file) |lf| lf.destroy(); |
| 1919 | 1899 | if (comp.zcu) |zcu| zcu.deinit(); |
| 1920 | 1900 | comp.cache_use.deinit(); |
| 1921 | 1901 | for (comp.work_queues) |work_queue| work_queue.deinit(); |
| 1922 | if (!InternPool.single_threaded) comp.codegen_work.queue.deinit(); | |
| 1923 | 1902 | comp.c_object_work_queue.deinit(); |
| 1924 | 1903 | comp.win32_resource_work_queue.deinit(); |
| 1925 | 1904 | comp.astgen_work_queue.deinit(); |
| 1926 | 1905 | comp.embed_file_work_queue.deinit(); |
| 1927 | 1906 | |
| 1928 | const gpa = comp.gpa; | |
| 1929 | comp.system_libs.deinit(gpa); | |
| 1907 | comp.windows_libs.deinit(gpa); | |
| 1930 | 1908 | |
| 1931 | 1909 | { |
| 1932 | 1910 | var it = comp.crt_files.iterator(); |
| ... | ... | @@ -2559,12 +2537,7 @@ fn addNonIncrementalStuffToCacheManifest( |
| 2559 | 2537 | cache_helpers.addModule(&man.hash, comp.root_mod); |
| 2560 | 2538 | } |
| 2561 | 2539 | |
| 2562 | for (comp.objects) |obj| { | |
| 2563 | _ = try man.addFilePath(obj.path, null); | |
| 2564 | man.hash.add(obj.must_link); | |
| 2565 | man.hash.add(obj.needed); | |
| 2566 | man.hash.add(obj.loption); | |
| 2567 | } | |
| 2540 | try link.hashInputs(man, comp.link_inputs); | |
| 2568 | 2541 | |
| 2569 | 2542 | for (comp.c_object_table.keys()) |key| { |
| 2570 | 2543 | _ = try man.addFile(key.src.src_path, null); |
| ... | ... | @@ -2601,7 +2574,7 @@ fn addNonIncrementalStuffToCacheManifest( |
| 2601 | 2574 | man.hash.add(comp.rc_includes); |
| 2602 | 2575 | man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); |
| 2603 | 2576 | man.hash.addListOfBytes(comp.framework_dirs); |
| 2604 | try link.hashAddSystemLibs(man, comp.system_libs); | |
| 2577 | man.hash.addListOfBytes(comp.windows_libs.keys()); | |
| 2605 | 2578 | |
| 2606 | 2579 | cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm); |
| 2607 | 2580 | cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir); |
| ... | ... | @@ -2620,12 +2593,16 @@ fn addNonIncrementalStuffToCacheManifest( |
| 2620 | 2593 | man.hash.addOptional(opts.image_base); |
| 2621 | 2594 | man.hash.addOptional(opts.gc_sections); |
| 2622 | 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 | 2602 | man.hash.addListOfBytes(opts.rpath_list); |
| 2625 | 2603 | man.hash.addListOfBytes(opts.symbol_wrap_set.keys()); |
| 2626 | 2604 | if (comp.config.link_libc) { |
| 2627 | 2605 | man.hash.add(comp.libc_installation != null); |
| 2628 | const target = comp.root_mod.resolved_target.result; | |
| 2629 | 2606 | if (comp.libc_installation) |libc_installation| { |
| 2630 | 2607 | man.hash.addOptionalBytes(libc_installation.crt_dir); |
| 2631 | 2608 | if (target.abi == .msvc or target.abi == .itanium) { |
| ... | ... | @@ -3219,18 +3196,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3219 | 3196 | })); |
| 3220 | 3197 | } |
| 3221 | 3198 | |
| 3222 | for (comp.link_diags.msgs.items) |link_err| { | |
| 3223 | try bundle.addRootErrorMessage(.{ | |
| 3224 | .msg = try bundle.addString(link_err.msg), | |
| 3225 | .notes_len = @intCast(link_err.notes.len), | |
| 3226 | }); | |
| 3227 | const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len)); | |
| 3228 | for (link_err.notes, 0..) |note, i| { | |
| 3229 | bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{ | |
| 3230 | .msg = try bundle.addString(note.msg), | |
| 3231 | })); | |
| 3232 | } | |
| 3233 | } | |
| 3199 | try comp.link_diags.addMessagesToBundle(&bundle); | |
| 3234 | 3200 | |
| 3235 | 3201 | if (comp.zcu) |zcu| { |
| 3236 | 3202 | if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) { |
| ... | ... | @@ -3482,6 +3448,9 @@ pub fn performAllTheWork( |
| 3482 | 3448 | comp: *Compilation, |
| 3483 | 3449 | main_progress_node: std.Progress.Node, |
| 3484 | 3450 | ) JobError!void { |
| 3451 | comp.work_queue_progress_node = main_progress_node; | |
| 3452 | defer comp.work_queue_progress_node = .none; | |
| 3453 | ||
| 3485 | 3454 | defer if (comp.zcu) |zcu| { |
| 3486 | 3455 | zcu.sema_prog_node.end(); |
| 3487 | 3456 | zcu.sema_prog_node = std.Progress.Node.none; |
| ... | ... | @@ -3491,7 +3460,6 @@ pub fn performAllTheWork( |
| 3491 | 3460 | zcu.generation += 1; |
| 3492 | 3461 | }; |
| 3493 | 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 | } |
| 3496 | 3464 | |
| 3497 | 3465 | fn performAllTheWorkInner( |
| ... | ... | @@ -3506,12 +3474,34 @@ fn performAllTheWorkInner( |
| 3506 | 3474 | var work_queue_wait_group: WaitGroup = .{}; |
| 3507 | 3475 | defer work_queue_wait_group.wait(); |
| 3508 | 3476 | |
| 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 | 3484 | if (comp.docs_emit != null) { |
| 3510 | 3485 | dev.check(.docs_emit); |
| 3511 | 3486 | comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp}); |
| 3512 | 3487 | work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node }); |
| 3513 | 3488 | } |
| 3514 | 3489 | |
| 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 | 3506 | const astgen_frame = tracy.namedFrame("astgen"); |
| 3517 | 3507 | defer astgen_frame.end(); |
| ... | ... | @@ -3574,22 +3564,18 @@ fn performAllTheWorkInner( |
| 3574 | 3564 | } |
| 3575 | 3565 | |
| 3576 | 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 | 3568 | comp, c_object, main_progress_node, |
| 3579 | 3569 | }); |
| 3580 | 3570 | } |
| 3581 | 3571 | |
| 3582 | 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 | 3574 | comp, win32_resource, main_progress_node, |
| 3585 | 3575 | }); |
| 3586 | 3576 | } |
| 3587 | 3577 | } |
| 3588 | 3578 | |
| 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 | 3579 | if (comp.zcu) |zcu| { |
| 3594 | 3580 | const pt: Zcu.PerThread = .{ .zcu = zcu, .tid = .main }; |
| 3595 | 3581 | if (comp.incremental) { |
| ... | ... | @@ -3604,18 +3590,12 @@ fn performAllTheWorkInner( |
| 3604 | 3590 | zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0); |
| 3605 | 3591 | } |
| 3606 | 3592 | |
| 3607 | if (!InternPool.single_threaded) { | |
| 3608 | comp.codegen_work.done = false; // may be `true` from a prior update | |
| 3609 | comp.thread_pool.spawnWgId(&work_queue_wait_group, codegenThread, .{comp}); | |
| 3593 | if (!comp.separateCodegenThreadOk()) { | |
| 3594 | // Waits until all input files have been parsed. | |
| 3595 | comp.link_task_wait_group.wait(); | |
| 3596 | comp.link_task_wait_group.reset(); | |
| 3597 | std.log.scoped(.link).debug("finished waiting for link_task_wait_group", .{}); | |
| 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 | }; | |
| 3619 | 3599 | |
| 3620 | 3600 | work: while (true) { |
| 3621 | 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 | 3639 | } |
| 3660 | 3640 | } |
| 3661 | 3641 | assert(nav.status == .resolved); |
| 3662 | try comp.queueCodegenJob(tid, .{ .nav = nav_index }); | |
| 3642 | comp.dispatchCodegenTask(tid, .{ .codegen_nav = nav_index }); | |
| 3663 | 3643 | }, |
| 3664 | 3644 | .codegen_func => |func| { |
| 3665 | // This call takes ownership of `func.air`. | |
| 3666 | try comp.queueCodegenJob(tid, .{ .func = .{ | |
| 3667 | .func = func.func, | |
| 3668 | .air = func.air, | |
| 3669 | } }); | |
| 3645 | comp.dispatchCodegenTask(tid, .{ .codegen_func = func }); | |
| 3646 | }, | |
| 3647 | .codegen_type => |ty| { | |
| 3648 | comp.dispatchCodegenTask(tid, .{ .codegen_type = ty }); | |
| 3670 | 3649 | }, |
| 3671 | .codegen_type => |ty| try comp.queueCodegenJob(tid, .{ .type = ty }), | |
| 3672 | 3650 | .analyze_func => |func| { |
| 3673 | 3651 | const named_frame = tracy.namedFrame("analyze_func"); |
| 3674 | 3652 | defer named_frame.end(); |
| ... | ... | @@ -3715,31 +3693,6 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre |
| 3715 | 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 | 3696 | .analyze_mod => |mod| { |
| 3744 | 3697 | const named_frame = tracy.namedFrame("analyze_mod"); |
| 3745 | 3698 | defer named_frame.end(); |
| ... | ... | @@ -3804,7 +3757,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre |
| 3804 | 3757 | const named_frame = tracy.namedFrame("windows_import_lib"); |
| 3805 | 3758 | defer named_frame.end(); |
| 3806 | 3759 | |
| 3807 | const link_lib = comp.system_libs.keys()[index]; | |
| 3760 | const link_lib = comp.windows_libs.keys()[index]; | |
| 3808 | 3761 | mingw.buildImportLib(comp, link_lib) catch |err| { |
| 3809 | 3762 | // TODO Surface more error details. |
| 3810 | 3763 | comp.lockAndSetMiscFailure( |
| ... | ... | @@ -3906,66 +3859,20 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre |
| 3906 | 3859 | } |
| 3907 | 3860 | } |
| 3908 | 3861 | |
| 3909 | fn queueCodegenJob(comp: *Compilation, tid: usize, codegen_job: CodegenJob) !void { | |
| 3910 | if (InternPool.single_threaded or | |
| 3911 | !comp.zcu.?.backendSupportsFeature(.separate_thread)) | |
| 3912 | return processOneCodegenJob(tid, comp, codegen_job); | |
| 3913 | ||
| 3914 | { | |
| 3915 | comp.codegen_work.mutex.lock(); | |
| 3916 | defer comp.codegen_work.mutex.unlock(); | |
| 3917 | try comp.codegen_work.queue.writeItem(codegen_job); | |
| 3862 | /// The reason for the double-queue here is that the first queue ensures any | |
| 3863 | /// resolve_type_fully tasks are complete before this dispatch function is called. | |
| 3864 | fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) void { | |
| 3865 | if (comp.separateCodegenThreadOk()) { | |
| 3866 | comp.queueLinkTasks(&.{link_task}); | |
| 3867 | } else { | |
| 3868 | link.doTask(comp, tid, link_task); | |
| 3918 | 3869 | } |
| 3919 | comp.codegen_work.cond.signal(); | |
| 3920 | 3870 | } |
| 3921 | 3871 | |
| 3922 | fn codegenThread(tid: usize, comp: *Compilation) void { | |
| 3923 | comp.codegen_work.mutex.lock(); | |
| 3924 | defer comp.codegen_work.mutex.unlock(); | |
| 3925 | ||
| 3926 | while (true) { | |
| 3927 | if (comp.codegen_work.queue.readItem()) |codegen_job| { | |
| 3928 | comp.codegen_work.mutex.unlock(); | |
| 3929 | defer comp.codegen_work.mutex.lock(); | |
| 3930 | ||
| 3931 | processOneCodegenJob(tid, comp, codegen_job) catch |job_error| { | |
| 3932 | comp.codegen_work.job_error = job_error; | |
| 3933 | break; | |
| 3934 | }; | |
| 3935 | continue; | |
| 3936 | } | |
| 3937 | ||
| 3938 | if (comp.codegen_work.done) break; | |
| 3939 | ||
| 3940 | comp.codegen_work.cond.wait(&comp.codegen_work.mutex); | |
| 3941 | } | |
| 3942 | } | |
| 3943 | ||
| 3944 | fn 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 | } | |
| 3872 | fn separateCodegenThreadOk(comp: *const Compilation) bool { | |
| 3873 | if (InternPool.single_threaded) return false; | |
| 3874 | const zcu = comp.zcu orelse return true; | |
| 3875 | return zcu.backendSupportsFeature(.separate_thread); | |
| 3969 | 3876 | } |
| 3970 | 3877 | |
| 3971 | 3878 | fn workerDocsCopy(comp: *Compilation) void { |
| ... | ... | @@ -4717,7 +4624,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr |
| 4717 | 4624 | // file and building an object we need to link them together, but with just one it should go |
| 4718 | 4625 | // directly to the output file. |
| 4719 | 4626 | 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 | 4628 | const o_basename_noext = if (direct_o) |
| 4722 | 4629 | comp.root_name |
| 4723 | 4630 | else |
| ... | ... | @@ -4956,7 +4863,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr |
| 4956 | 4863 | // the contents were the same, we hit the cache but the manifest is dirty and we need to update |
| 4957 | 4864 | // it to prevent doing a full file content comparison the next time around. |
| 4958 | 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 | } |
| 4962 | 4871 | |
| ... | ... | @@ -4971,6 +4880,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr |
| 4971 | 4880 | .lock = man.toOwnedLock(), |
| 4972 | 4881 | }, |
| 4973 | 4882 | }; |
| 4883 | ||
| 4884 | comp.queueLinkTasks(&.{.{ .load_object = c_object.status.success.object_path }}); | |
| 4974 | 4885 | } |
| 4975 | 4886 | |
| 4976 | 4887 | fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void { |
| ... | ... | @@ -6075,8 +5986,8 @@ test "classifyFileExt" { |
| 6075 | 5986 | try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig")); |
| 6076 | 5987 | } |
| 6077 | 5988 | |
| 6078 | pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Path { | |
| 6079 | return (try crtFilePath(comp, basename)) orelse { | |
| 5989 | fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Path { | |
| 5990 | return (try crtFilePath(&comp.crt_files, basename)) orelse { | |
| 6080 | 5991 | const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable; |
| 6081 | 5992 | const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir; |
| 6082 | 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 | 6000 | return path.toString(arena); |
| 6090 | 6001 | } |
| 6091 | 6002 | |
| 6092 | pub fn crtFilePath(comp: *Compilation, basename: []const u8) Allocator.Error!?Path { | |
| 6093 | const crt_file = comp.crt_files.get(basename) orelse return null; | |
| 6003 | fn crtFilePath(crt_files: *std.StringHashMapUnmanaged(CrtFile), basename: []const u8) Allocator.Error!?Path { | |
| 6004 | const crt_file = crt_files.get(basename) orelse return null; | |
| 6094 | 6005 | return crt_file.full_object_path; |
| 6095 | 6006 | } |
| 6096 | 6007 | |
| 6097 | fn 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 | ||
| 6108 | fn wantBuildGLibCFromSource(comp: Compilation) bool { | |
| 6109 | return comp.wantBuildLibCFromSource() and comp.getTarget().isGnuLibC(); | |
| 6110 | } | |
| 6111 | ||
| 6112 | fn wantBuildMuslFromSource(comp: Compilation) bool { | |
| 6113 | return comp.wantBuildLibCFromSource() and comp.getTarget().isMusl() and | |
| 6114 | !comp.getTarget().isWasm(); | |
| 6115 | } | |
| 6116 | ||
| 6117 | fn wantBuildWasiLibcFromSource(comp: Compilation) bool { | |
| 6118 | return comp.wantBuildLibCFromSource() and comp.getTarget().isWasm() and | |
| 6119 | comp.getTarget().os.tag == .wasi; | |
| 6120 | } | |
| 6121 | ||
| 6122 | fn wantBuildMinGWFromSource(comp: Compilation) bool { | |
| 6123 | return comp.wantBuildLibCFromSource() and comp.getTarget().isMinGW(); | |
| 6124 | } | |
| 6125 | ||
| 6126 | 6008 | fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { |
| 6127 | 6009 | const is_exe_or_dyn_lib = switch (comp.config.output_mode) { |
| 6128 | 6010 | .Obj => false, |
| ... | ... | @@ -6133,7 +6015,7 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { |
| 6133 | 6015 | return is_exe_or_dyn_lib and comp.config.link_libunwind and ofmt != .c; |
| 6134 | 6016 | } |
| 6135 | 6017 | |
| 6136 | fn setAllocFailure(comp: *Compilation) void { | |
| 6018 | pub fn setAllocFailure(comp: *Compilation) void { | |
| 6137 | 6019 | @branchHint(.cold); |
| 6138 | 6020 | log.debug("memory allocation failure", .{}); |
| 6139 | 6021 | comp.alloc_failure_occurred = true; |
| ... | ... | @@ -6370,9 +6252,11 @@ fn buildOutputFromZig( |
| 6370 | 6252 | |
| 6371 | 6253 | try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); |
| 6372 | 6254 | |
| 6373 | // Under incremental compilation, `out` may already be populated from a prior update. | |
| 6374 | assert(out.* == null or comp.incremental); | |
| 6375 | out.* = try sub_compilation.toCrtFile(); | |
| 6255 | const crt_file = try sub_compilation.toCrtFile(); | |
| 6256 | assert(out.* == null); | |
| 6257 | out.* = crt_file; | |
| 6258 | ||
| 6259 | comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); | |
| 6376 | 6260 | } |
| 6377 | 6261 | |
| 6378 | 6262 | pub fn build_crt_file( |
| ... | ... | @@ -6479,8 +6363,33 @@ pub fn build_crt_file( |
| 6479 | 6363 | |
| 6480 | 6364 | try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); |
| 6481 | 6365 | |
| 6482 | try comp.crt_files.ensureUnusedCapacity(gpa, 1); | |
| 6483 | comp.crt_files.putAssumeCapacityNoClobber(basename, try sub_compilation.toCrtFile()); | |
| 6366 | const crt_file = try sub_compilation.toCrtFile(); | |
| 6367 | comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); | |
| 6368 | ||
| 6369 | { | |
| 6370 | comp.mutex.lock(); | |
| 6371 | defer comp.mutex.unlock(); | |
| 6372 | try comp.crt_files.ensureUnusedCapacity(gpa, 1); | |
| 6373 | comp.crt_files.putAssumeCapacityNoClobber(basename, crt_file); | |
| 6374 | } | |
| 6375 | } | |
| 6376 | ||
| 6377 | pub 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. | |
| 6387 | pub 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 | } |
| 6485 | 6394 | |
| 6486 | 6395 | pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile { |
| ... | ... | @@ -6498,21 +6407,31 @@ pub fn getCrtPaths( |
| 6498 | 6407 | arena: Allocator, |
| 6499 | 6408 | ) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths { |
| 6500 | 6409 | const target = comp.root_mod.resolved_target.result; |
| 6410 | return getCrtPathsInner(arena, target, comp.config, comp.libc_installation, &comp.crt_files); | |
| 6411 | } | |
| 6412 | ||
| 6413 | fn 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 | 6420 | const basenames = LibCInstallation.CrtBasenames.get(.{ |
| 6502 | 6421 | .target = target, |
| 6503 | .link_libc = comp.config.link_libc, | |
| 6504 | .output_mode = comp.config.output_mode, | |
| 6505 | .link_mode = comp.config.link_mode, | |
| 6506 | .pie = comp.config.pie, | |
| 6422 | .link_libc = config.link_libc, | |
| 6423 | .output_mode = config.output_mode, | |
| 6424 | .link_mode = config.link_mode, | |
| 6425 | .pie = config.pie, | |
| 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); | |
| 6509 | 6428 | |
| 6510 | 6429 | return .{ |
| 6511 | .crt0 = if (basenames.crt0) |basename| try comp.crtFilePath(basename) else null, | |
| 6512 | .crti = if (basenames.crti) |basename| try comp.crtFilePath(basename) else null, | |
| 6513 | .crtbegin = if (basenames.crtbegin) |basename| try comp.crtFilePath(basename) else null, | |
| 6514 | .crtend = if (basenames.crtend) |basename| try comp.crtFilePath(basename) else null, | |
| 6515 | .crtn = if (basenames.crtn) |basename| try comp.crtFilePath(basename) else null, | |
| 6430 | .crt0 = if (basenames.crt0) |basename| try crtFilePath(crt_files, basename) else null, | |
| 6431 | .crti = if (basenames.crti) |basename| try crtFilePath(crt_files, basename) else null, | |
| 6432 | .crtbegin = if (basenames.crtbegin) |basename| try crtFilePath(crt_files, basename) else null, | |
| 6433 | .crtend = if (basenames.crtend) |basename| try crtFilePath(crt_files, basename) else null, | |
| 6434 | .crtn = if (basenames.crtn) |basename| try crtFilePath(crt_files, basename) else null, | |
| 6516 | 6435 | }; |
| 6517 | 6436 | } |
| 6518 | 6437 | |
| ... | ... | @@ -6522,24 +6441,14 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { |
| 6522 | 6441 | // then when we create a sub-Compilation for zig libc, it also tries to |
| 6523 | 6442 | // build kernel32.lib. |
| 6524 | 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; | |
| 6525 | 6446 | |
| 6526 | 6447 | // This happens when an `extern "foo"` function is referenced. |
| 6527 | 6448 | // If we haven't seen this library yet and we're targeting Windows, we need |
| 6528 | 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); | |
| 6530 | if (!gop.found_existing) { | |
| 6531 | gop.value_ptr.* = .{ | |
| 6532 | .needed = true, | |
| 6533 | .weak = false, | |
| 6534 | .path = null, | |
| 6535 | }; | |
| 6536 | const target = comp.root_mod.resolved_target.result; | |
| 6537 | if (target.os.tag == .windows and target.ofmt != .c) { | |
| 6538 | try comp.queueJob(.{ | |
| 6539 | .windows_import_lib = comp.system_libs.count() - 1, | |
| 6540 | }); | |
| 6541 | } | |
| 6542 | } | |
| 6450 | const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name); | |
| 6451 | if (!gop.found_existing) try comp.queueJob(.{ .windows_import_lib = comp.windows_libs.count() - 1 }); | |
| 6543 | 6452 | } |
| 6544 | 6453 | |
| 6545 | 6454 | /// This decides the optimization mode for all zig-provided libraries, including |
src/Sema.zig+8-1| ... | ... | @@ -2899,6 +2899,7 @@ fn zirStructDecl( |
| 2899 | 2899 | codegen_type: { |
| 2900 | 2900 | if (zcu.comp.config.use_llvm) break :codegen_type; |
| 2901 | 2901 | if (block.ownerModule().strip) break :codegen_type; |
| 2902 | // This job depends on any resolve_type_fully jobs queued up before it. | |
| 2902 | 2903 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 2903 | 2904 | } |
| 2904 | 2905 | try sema.declareDependency(.{ .interned = wip_ty.index }); |
| ... | ... | @@ -3149,6 +3150,7 @@ fn zirEnumDecl( |
| 3149 | 3150 | codegen_type: { |
| 3150 | 3151 | if (zcu.comp.config.use_llvm) break :codegen_type; |
| 3151 | 3152 | if (block.ownerModule().strip) break :codegen_type; |
| 3153 | // This job depends on any resolve_type_fully jobs queued up before it. | |
| 3152 | 3154 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 3153 | 3155 | } |
| 3154 | 3156 | return Air.internedToRef(wip_ty.index); |
| ... | ... | @@ -3272,6 +3274,7 @@ fn zirUnionDecl( |
| 3272 | 3274 | codegen_type: { |
| 3273 | 3275 | if (zcu.comp.config.use_llvm) break :codegen_type; |
| 3274 | 3276 | if (block.ownerModule().strip) break :codegen_type; |
| 3277 | // This job depends on any resolve_type_fully jobs queued up before it. | |
| 3275 | 3278 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 3276 | 3279 | } |
| 3277 | 3280 | try sema.declareDependency(.{ .interned = wip_ty.index }); |
| ... | ... | @@ -3357,6 +3360,7 @@ fn zirOpaqueDecl( |
| 3357 | 3360 | codegen_type: { |
| 3358 | 3361 | if (zcu.comp.config.use_llvm) break :codegen_type; |
| 3359 | 3362 | if (block.ownerModule().strip) break :codegen_type; |
| 3363 | // This job depends on any resolve_type_fully jobs queued up before it. | |
| 3360 | 3364 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 3361 | 3365 | } |
| 3362 | 3366 | try sema.addTypeReferenceEntry(src, wip_ty.index); |
| ... | ... | @@ -9595,7 +9599,7 @@ fn resolveGenericBody( |
| 9595 | 9599 | } |
| 9596 | 9600 | |
| 9597 | 9601 | /// Given a library name, examines if the library name should end up in |
| 9598 | /// `link.File.Options.system_libs` table (for example, libc is always | |
| 9602 | /// `link.File.Options.windows_libs` table (for example, libc is always | |
| 9599 | 9603 | /// specified via dedicated flag `link_libc` instead), |
| 9600 | 9604 | /// and puts it there if it doesn't exist. |
| 9601 | 9605 | /// It also dupes the library name which can then be saved as part of the |
| ... | ... | @@ -22456,6 +22460,7 @@ fn reifyEnum( |
| 22456 | 22460 | codegen_type: { |
| 22457 | 22461 | if (zcu.comp.config.use_llvm) break :codegen_type; |
| 22458 | 22462 | if (block.ownerModule().strip) break :codegen_type; |
| 22463 | // This job depends on any resolve_type_fully jobs queued up before it. | |
| 22459 | 22464 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 22460 | 22465 | } |
| 22461 | 22466 | return Air.internedToRef(wip_ty.index); |
| ... | ... | @@ -22713,6 +22718,7 @@ fn reifyUnion( |
| 22713 | 22718 | codegen_type: { |
| 22714 | 22719 | if (zcu.comp.config.use_llvm) break :codegen_type; |
| 22715 | 22720 | if (block.ownerModule().strip) break :codegen_type; |
| 22721 | // This job depends on any resolve_type_fully jobs queued up before it. | |
| 22716 | 22722 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 22717 | 22723 | } |
| 22718 | 22724 | try sema.declareDependency(.{ .interned = wip_ty.index }); |
| ... | ... | @@ -22997,6 +23003,7 @@ fn reifyStruct( |
| 22997 | 23003 | codegen_type: { |
| 22998 | 23004 | if (zcu.comp.config.use_llvm) break :codegen_type; |
| 22999 | 23005 | if (block.ownerModule().strip) break :codegen_type; |
| 23006 | // This job depends on any resolve_type_fully jobs queued up before it. | |
| 23000 | 23007 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 23001 | 23008 | } |
| 23002 | 23009 | try sema.declareDependency(.{ .interned = wip_ty.index }); |
src/ThreadSafeQueue.zig created+72| ... | ... | @@ -0,0 +1,72 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const Allocator = std.mem.Allocator; | |
| 4 | ||
| 5 | pub 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 | 845 | return .{ .ies_outdated = ies_outdated }; |
| 846 | 846 | } |
| 847 | 847 | |
| 848 | // This job depends on any resolve_type_fully jobs queued up before it. | |
| 848 | 849 | try comp.queueJob(.{ .codegen_func = .{ |
| 849 | 850 | .func = func_index, |
| 850 | 851 | .air = air, |
| ... | ... | @@ -1016,6 +1017,7 @@ fn createFileRootStruct( |
| 1016 | 1017 | codegen_type: { |
| 1017 | 1018 | if (zcu.comp.config.use_llvm) break :codegen_type; |
| 1018 | 1019 | if (file.mod.strip) break :codegen_type; |
| 1020 | // This job depends on any resolve_type_fully jobs queued up before it. | |
| 1019 | 1021 | try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 1020 | 1022 | } |
| 1021 | 1023 | zcu.setFileRootType(file_index, wip_ty.index); |
| ... | ... | @@ -1362,6 +1364,7 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult { |
| 1362 | 1364 | if (file.mod.strip) break :queue_codegen; |
| 1363 | 1365 | } |
| 1364 | 1366 | |
| 1367 | // This job depends on any resolve_type_fully jobs queued up before it. | |
| 1365 | 1368 | try zcu.comp.queueJob(.{ .codegen_nav = nav_index }); |
| 1366 | 1369 | } |
| 1367 | 1370 | |
| ... | ... | @@ -2593,7 +2596,7 @@ pub fn populateTestFunctions( |
| 2593 | 2596 | } |
| 2594 | 2597 | } |
| 2595 | 2598 | |
| 2596 | pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { | |
| 2599 | pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{OutOfMemory}!void { | |
| 2597 | 2600 | const zcu = pt.zcu; |
| 2598 | 2601 | const comp = zcu.comp; |
| 2599 | 2602 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -3163,6 +3166,7 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator. |
| 3163 | 3166 | pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!InternPool.Index { |
| 3164 | 3167 | const result = try pt.zcu.intern_pool.getExtern(pt.zcu.gpa, pt.tid, key); |
| 3165 | 3168 | if (result.new_nav.unwrap()) |nav| { |
| 3169 | // This job depends on any resolve_type_fully jobs queued up before it. | |
| 3166 | 3170 | try pt.zcu.comp.queueJob(.{ .codegen_nav = nav }); |
| 3167 | 3171 | } |
| 3168 | 3172 | return result.index; |
src/arch/riscv64/CodeGen.zig+2-2| ... | ... | @@ -133,7 +133,7 @@ const Owner = union(enum) { |
| 133 | 133 | switch (owner) { |
| 134 | 134 | .nav_index => |nav_index| { |
| 135 | 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 | 138 | .lazy_sym => |lazy_sym| { |
| 139 | 139 | const elf_file = func.bin_file.cast(.elf).?; |
| ... | ... | @@ -5002,7 +5002,7 @@ fn genCall( |
| 5002 | 5002 | .func => |func_val| { |
| 5003 | 5003 | if (func.bin_file.cast(.elf)) |elf_file| { |
| 5004 | 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); | |
| 5006 | 5006 | |
| 5007 | 5007 | if (func.mod.pic) { |
| 5008 | 5008 | return func.fail("TODO: genCall pic", .{}); |
src/arch/x86_64/CodeGen.zig+2-2| ... | ... | @@ -126,7 +126,7 @@ const Owner = union(enum) { |
| 126 | 126 | const pt = ctx.pt; |
| 127 | 127 | switch (owner) { |
| 128 | 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 | 130 | } else if (ctx.bin_file.cast(.macho)) |macho_file| { |
| 131 | 131 | return macho_file.getZigObject().?.getOrCreateMetadataForNav(macho_file, nav_index); |
| 132 | 132 | } else if (ctx.bin_file.cast(.coff)) |coff_file| { |
| ... | ... | @@ -12605,7 +12605,7 @@ fn genCall(self: *Self, info: union(enum) { |
| 12605 | 12605 | .func => |func| { |
| 12606 | 12606 | if (self.bin_file.cast(.elf)) |elf_file| { |
| 12607 | 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 | 12609 | try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = sym_index })); |
| 12610 | 12610 | } else if (self.bin_file.cast(.coff)) |coff_file| { |
| 12611 | 12611 | const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav); |
src/codegen.zig+1-1| ... | ... | @@ -866,7 +866,7 @@ fn genNavRef( |
| 866 | 866 | zo.symbol(sym_index).flags.is_extern_ptr = true; |
| 867 | 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 | 870 | if (!single_threaded and is_threadlocal) { |
| 871 | 871 | return .{ .mcv = .{ .load_tlv = sym_index } }; |
| 872 | 872 | } |
src/glibc.zig+53-18| ... | ... | @@ -6,12 +6,14 @@ const fs = std.fs; |
| 6 | 6 | const path = fs.path; |
| 7 | 7 | const assert = std.debug.assert; |
| 8 | 8 | const Version = std.SemanticVersion; |
| 9 | const Path = std.Build.Cache.Path; | |
| 9 | 10 | |
| 10 | 11 | const Compilation = @import("Compilation.zig"); |
| 11 | 12 | const build_options = @import("build_options"); |
| 12 | 13 | const trace = @import("tracy.zig").trace; |
| 13 | 14 | const Cache = std.Build.Cache; |
| 14 | 15 | const Module = @import("Package/Module.zig"); |
| 16 | const link = @import("link.zig"); | |
| 15 | 17 | |
| 16 | 18 | pub const Lib = struct { |
| 17 | 19 | name: []const u8, |
| ... | ... | @@ -717,11 +719,11 @@ fn lib_path(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const |
| 717 | 719 | |
| 718 | 720 | pub const BuiltSharedObjects = struct { |
| 719 | 721 | lock: Cache.Lock, |
| 720 | dir_path: []u8, | |
| 722 | dir_path: Path, | |
| 721 | 723 | |
| 722 | 724 | pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator) void { |
| 723 | 725 | self.lock.release(); |
| 724 | gpa.free(self.dir_path); | |
| 726 | gpa.free(self.dir_path.sub_path); | |
| 725 | 727 | self.* = undefined; |
| 726 | 728 | } |
| 727 | 729 | }; |
| ... | ... | @@ -742,7 +744,9 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi |
| 742 | 744 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 743 | 745 | } |
| 744 | 746 | |
| 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 | 750 | defer arena_allocator.deinit(); |
| 747 | 751 | const arena = arena_allocator.allocator(); |
| 748 | 752 | |
| ... | ... | @@ -751,7 +755,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi |
| 751 | 755 | |
| 752 | 756 | // Use the global cache directory. |
| 753 | 757 | var cache: Cache = .{ |
| 754 | .gpa = comp.gpa, | |
| 758 | .gpa = gpa, | |
| 755 | 759 | .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}), |
| 756 | 760 | }; |
| 757 | 761 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); |
| ... | ... | @@ -772,12 +776,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi |
| 772 | 776 | if (try man.hit()) { |
| 773 | 777 | const digest = man.final(); |
| 774 | 778 | |
| 775 | assert(comp.glibc_so_files == null); | |
| 776 | comp.glibc_so_files = BuiltSharedObjects{ | |
| 779 | return queueSharedObjects(comp, .{ | |
| 777 | 780 | .lock = man.toOwnedLock(), |
| 778 | .dir_path = try comp.global_cache_directory.join(comp.gpa, &.{ "o", &digest }), | |
| 779 | }; | |
| 780 | return; | |
| 781 | .dir_path = .{ | |
| 782 | .root_dir = comp.global_cache_directory, | |
| 783 | .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest), | |
| 784 | }, | |
| 785 | }); | |
| 781 | 786 | } |
| 782 | 787 | |
| 783 | 788 | const digest = man.final(); |
| ... | ... | @@ -790,8 +795,8 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi |
| 790 | 795 | defer o_directory.handle.close(); |
| 791 | 796 | |
| 792 | 797 | const abilists_contents = man.files.keys()[abilists_index].contents.?; |
| 793 | const metadata = try loadMetaData(comp.gpa, abilists_contents); | |
| 794 | defer metadata.destroy(comp.gpa); | |
| 798 | const metadata = try loadMetaData(gpa, abilists_contents); | |
| 799 | defer metadata.destroy(gpa); | |
| 795 | 800 | |
| 796 | 801 | const target_targ_index = for (metadata.all_targets, 0..) |targ, i| { |
| 797 | 802 | if (targ.arch == target.cpu.arch and |
| ... | ... | @@ -835,7 +840,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi |
| 835 | 840 | map_contents.deinit(); // The most recent allocation of an arena can be freed :) |
| 836 | 841 | } |
| 837 | 842 | |
| 838 | var stubs_asm = std.ArrayList(u8).init(comp.gpa); | |
| 843 | var stubs_asm = std.ArrayList(u8).init(gpa); | |
| 839 | 844 | defer stubs_asm.deinit(); |
| 840 | 845 | |
| 841 | 846 | for (libs, 0..) |lib, lib_i| { |
| ... | ... | @@ -1195,7 +1200,6 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi |
| 1195 | 1200 | var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc. |
| 1196 | 1201 | const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; |
| 1197 | 1202 | try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items }); |
| 1198 | ||
| 1199 | 1203 | try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib, prog_node); |
| 1200 | 1204 | } |
| 1201 | 1205 | |
| ... | ... | @@ -1203,14 +1207,45 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi |
| 1203 | 1207 | log.warn("failed to write cache manifest for glibc stubs: {s}", .{@errorName(err)}); |
| 1204 | 1208 | }; |
| 1205 | 1209 | |
| 1206 | assert(comp.glibc_so_files == null); | |
| 1207 | comp.glibc_so_files = BuiltSharedObjects{ | |
| 1210 | return queueSharedObjects(comp, .{ | |
| 1208 | 1211 | .lock = man.toOwnedLock(), |
| 1209 | .dir_path = try comp.global_cache_directory.join(comp.gpa, &.{ "o", &digest }), | |
| 1210 | }; | |
| 1212 | .dir_path = .{ | |
| 1213 | .root_dir = comp.global_cache_directory, | |
| 1214 | .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest), | |
| 1215 | }, | |
| 1216 | }); | |
| 1211 | 1217 | } |
| 1212 | 1218 | |
| 1213 | // zig fmt: on | |
| 1219 | fn 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 | } | |
| 1214 | 1249 | |
| 1215 | 1250 | fn buildSharedLib( |
| 1216 | 1251 | comp: *Compilation, |
src/libcxx.zig+6-2| ... | ... | @@ -355,7 +355,9 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: std.Progress.Node) BuildError! |
| 355 | 355 | }; |
| 356 | 356 | |
| 357 | 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 | } |
| 360 | 362 | |
| 361 | 363 | pub 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 | 586 | }; |
| 585 | 587 | |
| 586 | 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 | } |
| 589 | 593 | |
| 590 | 594 | pub 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 | 342 | }, |
| 343 | 343 | }; |
| 344 | 344 | |
| 345 | const crt_file = try sub_compilation.toCrtFile(); | |
| 346 | comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); | |
| 345 | 347 | assert(comp.tsan_lib == null); |
| 346 | comp.tsan_lib = try sub_compilation.toCrtFile(); | |
| 348 | comp.tsan_lib = crt_file; | |
| 347 | 349 | } |
| 348 | 350 | |
| 349 | 351 | const 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 | 199 | }, |
| 200 | 200 | }; |
| 201 | 201 | |
| 202 | const crt_file = try sub_compilation.toCrtFile(); | |
| 203 | comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); | |
| 202 | 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 | } |
| 205 | 207 | |
| 206 | 208 | const unwind_src_list = [_][]const u8{ |
src/link.zig+1000-44| ... | ... | @@ -12,6 +12,7 @@ const Air = @import("Air.zig"); |
| 12 | 12 | const Allocator = std.mem.Allocator; |
| 13 | 13 | const Cache = std.Build.Cache; |
| 14 | 14 | const Path = std.Build.Cache.Path; |
| 15 | const Directory = std.Build.Cache.Directory; | |
| 15 | 16 | const Compilation = @import("Compilation.zig"); |
| 16 | 17 | const LibCInstallation = std.zig.LibCInstallation; |
| 17 | 18 | const Liveness = @import("Liveness.zig"); |
| ... | ... | @@ -23,19 +24,10 @@ const LlvmObject = @import("codegen/llvm.zig").Object; |
| 23 | 24 | const lldMain = @import("main.zig").lldMain; |
| 24 | 25 | const Package = @import("Package.zig"); |
| 25 | 26 | const dev = @import("dev.zig"); |
| 27 | const ThreadSafeQueue = @import("ThreadSafeQueue.zig").ThreadSafeQueue; | |
| 28 | const target_util = @import("target.zig"); | |
| 26 | 29 | |
| 27 | /// When adding a new field, remember to update `hashAddSystemLibs`. | |
| 28 | /// These are *always* dynamically linked. Static libraries will be | |
| 29 | /// provided as positional arguments. | |
| 30 | pub 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 | }; | |
| 30 | pub const LdScript = @import("link/LdScript.zig"); | |
| 39 | 31 | |
| 40 | 32 | pub const Diags = struct { |
| 41 | 33 | /// Stored here so that function definitions can distinguish between |
| ... | ... | @@ -336,20 +328,22 @@ pub const Diags = struct { |
| 336 | 328 | log.debug("memory allocation failure", .{}); |
| 337 | 329 | diags.flags.alloc_failure_occurred = true; |
| 338 | 330 | } |
| 339 | }; | |
| 340 | 331 | |
| 341 | pub fn hashAddSystemLibs( | |
| 342 | man: *Cache.Manifest, | |
| 343 | hm: std.StringArrayHashMapUnmanaged(SystemLib), | |
| 344 | ) !void { | |
| 345 | const keys = hm.keys(); | |
| 346 | man.hash.addListOfBytes(keys); | |
| 347 | for (hm.values()) |value| { | |
| 348 | man.hash.add(value.needed); | |
| 349 | man.hash.add(value.weak); | |
| 350 | if (value.path) |p| _ = try man.addFilePath(p, null); | |
| 332 | pub fn addMessagesToBundle(diags: *const Diags, bundle: *std.zig.ErrorBundle.Wip) Allocator.Error!void { | |
| 333 | for (diags.msgs.items) |link_err| { | |
| 334 | try bundle.addRootErrorMessage(.{ | |
| 335 | .msg = try bundle.addString(link_err.msg), | |
| 336 | .notes_len = @intCast(link_err.notes.len), | |
| 337 | }); | |
| 338 | const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len)); | |
| 339 | for (link_err.notes, 0..) |note, i| { | |
| 340 | bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{ | |
| 341 | .msg = try bundle.addString(note.msg), | |
| 342 | })); | |
| 343 | } | |
| 344 | } | |
| 351 | 345 | } |
| 352 | } | |
| 346 | }; | |
| 353 | 347 | |
| 354 | 348 | pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version; |
| 355 | 349 | |
| ... | ... | @@ -438,7 +432,7 @@ pub const File = struct { |
| 438 | 432 | compatibility_version: ?std.SemanticVersion, |
| 439 | 433 | |
| 440 | 434 | // TODO: remove this. libraries are resolved by the frontend. |
| 441 | lib_dirs: []const []const u8, | |
| 435 | lib_directories: []const Directory, | |
| 442 | 436 | framework_dirs: []const []const u8, |
| 443 | 437 | rpath_list: []const []const u8, |
| 444 | 438 | |
| ... | ... | @@ -1003,6 +997,102 @@ pub const File = struct { |
| 1003 | 997 | } |
| 1004 | 998 | } |
| 1005 | 999 | |
| 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 | 1096 | pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { |
| 1007 | 1097 | dev.check(.lld_linker); |
| 1008 | 1098 | |
| ... | ... | @@ -1010,7 +1100,6 @@ pub const File = struct { |
| 1010 | 1100 | defer tracy.end(); |
| 1011 | 1101 | |
| 1012 | 1102 | const comp = base.comp; |
| 1013 | const gpa = comp.gpa; | |
| 1014 | 1103 | |
| 1015 | 1104 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. |
| 1016 | 1105 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); |
| ... | ... | @@ -1042,7 +1131,7 @@ pub const File = struct { |
| 1042 | 1131 | var man: Cache.Manifest = undefined; |
| 1043 | 1132 | defer if (!base.disable_lld_caching) man.deinit(); |
| 1044 | 1133 | |
| 1045 | const objects = comp.objects; | |
| 1134 | const link_inputs = comp.link_inputs; | |
| 1046 | 1135 | |
| 1047 | 1136 | var digest: [Cache.hex_digest_len]u8 = undefined; |
| 1048 | 1137 | |
| ... | ... | @@ -1052,11 +1141,8 @@ pub const File = struct { |
| 1052 | 1141 | // We are about to obtain this lock, so here we give other processes a chance first. |
| 1053 | 1142 | base.releaseLock(); |
| 1054 | 1143 | |
| 1055 | for (objects) |obj| { | |
| 1056 | _ = try man.addFilePath(obj.path, null); | |
| 1057 | man.hash.add(obj.must_link); | |
| 1058 | man.hash.add(obj.loption); | |
| 1059 | } | |
| 1144 | try hashInputs(&man, link_inputs); | |
| 1145 | ||
| 1060 | 1146 | for (comp.c_object_table.keys()) |key| { |
| 1061 | 1147 | _ = try man.addFilePath(key.status.success.object_path, null); |
| 1062 | 1148 | } |
| ... | ... | @@ -1092,26 +1178,24 @@ pub const File = struct { |
| 1092 | 1178 | }; |
| 1093 | 1179 | } |
| 1094 | 1180 | |
| 1095 | const win32_resource_table_len = comp.win32_resource_table.count(); | |
| 1096 | const num_object_files = objects.len + comp.c_object_table.count() + win32_resource_table_len + 2; | |
| 1097 | var object_files = try std.ArrayList([*:0]const u8).initCapacity(gpa, num_object_files); | |
| 1098 | defer object_files.deinit(); | |
| 1181 | var object_files: std.ArrayListUnmanaged([*:0]const u8) = .empty; | |
| 1099 | 1182 | |
| 1100 | for (objects) |obj| { | |
| 1101 | object_files.appendAssumeCapacity(try obj.path.toStringZ(arena)); | |
| 1183 | try object_files.ensureUnusedCapacity(arena, link_inputs.len); | |
| 1184 | for (link_inputs) |input| { | |
| 1185 | object_files.appendAssumeCapacity(try input.path().?.toStringZ(arena)); | |
| 1102 | 1186 | } |
| 1187 | ||
| 1188 | try object_files.ensureUnusedCapacity(arena, comp.c_object_table.count() + | |
| 1189 | comp.win32_resource_table.count() + 2); | |
| 1190 | ||
| 1103 | 1191 | for (comp.c_object_table.keys()) |key| { |
| 1104 | 1192 | object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena)); |
| 1105 | 1193 | } |
| 1106 | 1194 | for (comp.win32_resource_table.keys()) |key| { |
| 1107 | 1195 | object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path)); |
| 1108 | 1196 | } |
| 1109 | if (zcu_obj_path) |p| { | |
| 1110 | object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); | |
| 1111 | } | |
| 1112 | if (compiler_rt_path) |p| { | |
| 1113 | object_files.appendAssumeCapacity(try p.toStringZ(arena)); | |
| 1114 | } | |
| 1197 | if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); | |
| 1198 | if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena)); | |
| 1115 | 1199 | |
| 1116 | 1200 | if (comp.verbose_link) { |
| 1117 | 1201 | std.debug.print("ar rcs {s}", .{full_out_path_z}); |
| ... | ... | @@ -1277,6 +1361,195 @@ pub const File = struct { |
| 1277 | 1361 | pub const Dwarf = @import("link/Dwarf.zig"); |
| 1278 | 1362 | }; |
| 1279 | 1363 | |
| 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. | |
| 1367 | pub 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 | ||
| 1377 | pub 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 | ||
| 1411 | pub 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 | ||
| 1280 | 1553 | pub fn spawnLld( |
| 1281 | 1554 | comp: *Compilation, |
| 1282 | 1555 | arena: Allocator, |
| ... | ... | @@ -1387,3 +1660,686 @@ pub fn spawnLld( |
| 1387 | 1660 | |
| 1388 | 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. | |
| 1666 | pub 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 | ||
| 1718 | pub 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 | ||
| 1780 | pub 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 | ||
| 1805 | pub 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 | ||
| 1996 | const ResolveLibInputResult = enum { ok, no_match }; | |
| 1997 | const fatal = std.process.fatal; | |
| 1998 | ||
| 1999 | fn 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 | ||
| 2092 | fn 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 | ||
| 2117 | fn 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 | ||
| 2162 | fn 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 | ||
| 2266 | pub 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 | ||
| 2277 | pub 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 | ||
| 2289 | pub 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 | ||
| 2295 | pub 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 | ||
| 2301 | pub 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 | ||
| 2307 | fn 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. | |
| 2324 | pub 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. | |
| 2329 | pub 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. | |
| 2339 | pub 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 | 16 | /// default or populated together. They should not be separate fields. |
| 17 | 17 | major_subsystem_version: u16, |
| 18 | 18 | minor_subsystem_version: u16, |
| 19 | lib_dirs: []const []const u8, | |
| 19 | lib_directories: []const Directory, | |
| 20 | 20 | entry: link.File.OpenOptions.Entry, |
| 21 | 21 | entry_addr: ?u32, |
| 22 | 22 | module_definition_file: ?[]const u8, |
| ... | ... | @@ -297,7 +297,7 @@ pub fn createEmpty( |
| 297 | 297 | .dynamicbase = options.dynamicbase, |
| 298 | 298 | .major_subsystem_version = options.major_subsystem_version orelse 6, |
| 299 | 299 | .minor_subsystem_version = options.minor_subsystem_version orelse 0, |
| 300 | .lib_dirs = options.lib_dirs, | |
| 300 | .lib_directories = options.lib_directories, | |
| 301 | 301 | .entry_addr = math.cast(u32, options.entry_addr orelse 0) orelse |
| 302 | 302 | return error.EntryAddressTooBig, |
| 303 | 303 | .module_definition_file = options.module_definition_file, |
| ... | ... | @@ -2727,6 +2727,7 @@ const mem = std.mem; |
| 2727 | 2727 | |
| 2728 | 2728 | const Allocator = std.mem.Allocator; |
| 2729 | 2729 | const Path = std.Build.Cache.Path; |
| 2730 | const Directory = std.Build.Cache.Directory; | |
| 2730 | 2731 | |
| 2731 | 2732 | const codegen = @import("../codegen.zig"); |
| 2732 | 2733 | const link = @import("../link.zig"); |
src/link/Coff/lld.zig+32-27| ... | ... | @@ -8,6 +8,7 @@ const log = std.log.scoped(.link); |
| 8 | 8 | const mem = std.mem; |
| 9 | 9 | const Cache = std.Build.Cache; |
| 10 | 10 | const Path = std.Build.Cache.Path; |
| 11 | const Directory = std.Build.Cache.Directory; | |
| 11 | 12 | |
| 12 | 13 | const mingw = @import("../../mingw.zig"); |
| 13 | 14 | const link = @import("../../link.zig"); |
| ... | ... | @@ -74,10 +75,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no |
| 74 | 75 | |
| 75 | 76 | comptime assert(Compilation.link_hash_implementation_version == 14); |
| 76 | 77 | |
| 77 | for (comp.objects) |obj| { | |
| 78 | _ = try man.addFilePath(obj.path, null); | |
| 79 | man.hash.add(obj.must_link); | |
| 80 | } | |
| 78 | try link.hashInputs(&man, comp.link_inputs); | |
| 81 | 79 | for (comp.c_object_table.keys()) |key| { |
| 82 | 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 | 86 | man.hash.addOptionalBytes(entry_name); |
| 89 | 87 | man.hash.add(self.base.stack_size); |
| 90 | 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 | 93 | man.hash.add(comp.skip_linker_dependencies); |
| 93 | 94 | if (comp.config.link_libc) { |
| 94 | 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 | 101 | } |
| 101 | 102 | } |
| 102 | 103 | } |
| 103 | try link.hashAddSystemLibs(&man, comp.system_libs); | |
| 104 | man.hash.addListOfBytes(comp.windows_libs.keys()); | |
| 104 | 105 | man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); |
| 105 | 106 | man.hash.addOptional(self.subsystem); |
| 106 | 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 | 149 | // here. TODO: think carefully about how we can avoid this redundant operation when doing |
| 149 | 150 | // build-obj. See also the corresponding TODO in linkAsArchive. |
| 150 | 151 | const the_object_path = blk: { |
| 151 | if (comp.objects.len != 0) | |
| 152 | break :blk comp.objects[0].path; | |
| 152 | if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path; | |
| 153 | 153 | |
| 154 | 154 | if (comp.c_object_table.count() != 0) |
| 155 | 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 | 266 | } |
| 267 | 267 | } |
| 268 | 268 | |
| 269 | for (self.lib_dirs) |lib_dir| { | |
| 270 | try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir})); | |
| 269 | for (self.lib_directories) |lib_directory| { | |
| 270 | try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_directory.path orelse "."})); | |
| 271 | 271 | } |
| 272 | 272 | |
| 273 | try argv.ensureUnusedCapacity(comp.objects.len); | |
| 274 | for (comp.objects) |obj| { | |
| 275 | if (obj.must_link) { | |
| 276 | argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)})); | |
| 277 | } else { | |
| 278 | argv.appendAssumeCapacity(try obj.path.toString(arena)); | |
| 279 | } | |
| 280 | } | |
| 273 | try argv.ensureUnusedCapacity(comp.link_inputs.len); | |
| 274 | for (comp.link_inputs) |link_input| switch (link_input) { | |
| 275 | .dso_exact => unreachable, // not applicable to PE/COFF | |
| 276 | inline .dso, .res => |x| { | |
| 277 | argv.appendAssumeCapacity(try x.path.toString(arena)); | |
| 278 | }, | |
| 279 | .object, .archive => |obj| { | |
| 280 | if (obj.must_link) { | |
| 281 | argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)})); | |
| 282 | } else { | |
| 283 | argv.appendAssumeCapacity(try obj.path.toString(arena)); | |
| 284 | } | |
| 285 | }, | |
| 286 | }; | |
| 281 | 287 | |
| 282 | 288 | for (comp.c_object_table.keys()) |key| { |
| 283 | 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 | 490 | if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena)); |
| 485 | 491 | } |
| 486 | 492 | |
| 487 | try argv.ensureUnusedCapacity(comp.system_libs.count()); | |
| 488 | for (comp.system_libs.keys()) |key| { | |
| 493 | try argv.ensureUnusedCapacity(comp.windows_libs.count()); | |
| 494 | for (comp.windows_libs.keys()) |key| { | |
| 489 | 495 | const lib_basename = try allocPrint(arena, "{s}.lib", .{key}); |
| 490 | 496 | if (comp.crt_files.get(lib_basename)) |crt_file| { |
| 491 | 497 | argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena)); |
| 492 | 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 | 501 | argv.appendAssumeCapacity(full_path); |
| 496 | 502 | continue; |
| 497 | 503 | } |
| 498 | 504 | if (target.abi.isGnu()) { |
| 499 | 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 | 507 | argv.appendAssumeCapacity(full_path); |
| 502 | 508 | continue; |
| 503 | 509 | } |
| ... | ... | @@ -530,14 +536,13 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no |
| 530 | 536 | } |
| 531 | 537 | } |
| 532 | 538 | |
| 533 | fn findLib(arena: Allocator, name: []const u8, lib_dirs: []const []const u8) !?[]const u8 { | |
| 534 | for (lib_dirs) |lib_dir| { | |
| 535 | const full_path = try fs.path.join(arena, &.{ lib_dir, name }); | |
| 536 | fs.cwd().access(full_path, .{}) catch |err| switch (err) { | |
| 539 | fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Directory) !?[]const u8 { | |
| 540 | for (lib_directories) |lib_directory| { | |
| 541 | lib_directory.handle.access(name, .{}) catch |err| switch (err) { | |
| 537 | 542 | error.FileNotFound => continue, |
| 538 | 543 | else => |e| return e, |
| 539 | 544 | }; |
| 540 | return full_path; | |
| 545 | return try lib_directory.join(arena, &.{name}); | |
| 541 | 546 | } |
| 542 | 547 | return null; |
| 543 | 548 | } |
src/link/Elf.zig+233-631| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | pub const Atom = @import("Elf/Atom.zig"); |
| 2 | 2 | |
| 3 | 3 | base: link.File, |
| 4 | zig_object: ?*ZigObject, | |
| 4 | 5 | rpath_table: std.StringArrayHashMapUnmanaged(void), |
| 5 | 6 | image_base: u64, |
| 6 | 7 | emit_relocs: bool, |
| ... | ... | @@ -15,7 +16,6 @@ z_relro: bool, |
| 15 | 16 | z_common_page_size: ?u64, |
| 16 | 17 | /// TODO make this non optional and resolve the default in open() |
| 17 | 18 | z_max_page_size: ?u64, |
| 18 | lib_dirs: []const []const u8, | |
| 19 | 19 | hash_style: HashStyle, |
| 20 | 20 | compress_debug_sections: CompressDebugSections, |
| 21 | 21 | symbol_wrap_set: std.StringArrayHashMapUnmanaged(void), |
| ... | ... | @@ -36,8 +36,7 @@ ptr_width: PtrWidth, |
| 36 | 36 | llvm_object: ?LlvmObject.Ptr = null, |
| 37 | 37 | |
| 38 | 38 | /// A list of all input files. |
| 39 | /// Index of each input file also encodes the priority or precedence of one input file | |
| 40 | /// over another. | |
| 39 | /// First index is a special "null file". Order is otherwise not observed. | |
| 41 | 40 | files: std.MultiArrayList(File.Entry) = .{}, |
| 42 | 41 | /// Long-lived list of all file descriptors. |
| 43 | 42 | /// 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 | 115 | |
| 117 | 116 | first_eflags: ?elf.Word = null, |
| 118 | 117 | |
| 118 | /// `--verbose-link` output. | |
| 119 | /// Initialized on creation, appended to as inputs are added, printed during `flush`. | |
| 120 | dump_argv_list: std.ArrayListUnmanaged([]const u8), | |
| 121 | ||
| 119 | 122 | const SectionIndexes = struct { |
| 120 | 123 | copy_rel: ?u32 = null, |
| 121 | 124 | dynamic: ?u32 = null, |
| ... | ... | @@ -297,6 +300,7 @@ pub fn createEmpty( |
| 297 | 300 | .disable_lld_caching = options.disable_lld_caching, |
| 298 | 301 | .build_id = options.build_id, |
| 299 | 302 | }, |
| 303 | .zig_object = null, | |
| 300 | 304 | .rpath_table = rpath_table, |
| 301 | 305 | .ptr_width = ptr_width, |
| 302 | 306 | .page_size = page_size, |
| ... | ... | @@ -328,7 +332,6 @@ pub fn createEmpty( |
| 328 | 332 | .z_relro = options.z_relro, |
| 329 | 333 | .z_common_page_size = options.z_common_page_size, |
| 330 | 334 | .z_max_page_size = options.z_max_page_size, |
| 331 | .lib_dirs = options.lib_dirs, | |
| 332 | 335 | .hash_style = options.hash_style, |
| 333 | 336 | .compress_debug_sections = options.compress_debug_sections, |
| 334 | 337 | .symbol_wrap_set = options.symbol_wrap_set, |
| ... | ... | @@ -341,6 +344,7 @@ pub fn createEmpty( |
| 341 | 344 | .enable_new_dtags = options.enable_new_dtags, |
| 342 | 345 | .print_icf_sections = options.print_icf_sections, |
| 343 | 346 | .print_map = options.print_map, |
| 347 | .dump_argv_list = .empty, | |
| 344 | 348 | }; |
| 345 | 349 | if (use_llvm and comp.config.have_zcu) { |
| 346 | 350 | self.llvm_object = try LlvmObject.create(arena, comp); |
| ... | ... | @@ -352,6 +356,9 @@ pub fn createEmpty( |
| 352 | 356 | return self; |
| 353 | 357 | } |
| 354 | 358 | |
| 359 | // --verbose-link | |
| 360 | if (comp.verbose_link) try dumpArgvInit(self, arena); | |
| 361 | ||
| 355 | 362 | const is_obj = output_mode == .Obj; |
| 356 | 363 | const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static); |
| 357 | 364 | |
| ... | ... | @@ -418,14 +425,17 @@ pub fn createEmpty( |
| 418 | 425 | if (opt_zcu) |zcu| { |
| 419 | 426 | if (!use_llvm) { |
| 420 | 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 | 433 | .index = index, |
| 423 | 434 | .basename = try std.fmt.allocPrint(arena, "{s}.o", .{ |
| 424 | 435 | fs.path.stem(zcu.main_mod.root_src_path), |
| 425 | 436 | }), |
| 426 | } }); | |
| 427 | self.zig_object_index = index; | |
| 428 | try self.zigObjectPtr().?.init(self, .{ | |
| 437 | }; | |
| 438 | try zig_object.init(self, .{ | |
| 429 | 439 | .symbol_count_hint = options.symbol_count_hint, |
| 430 | 440 | .program_code_size_hint = options.program_code_size_hint, |
| 431 | 441 | }); |
| ... | ... | @@ -457,12 +467,14 @@ pub fn deinit(self: *Elf) void { |
| 457 | 467 | self.file_handles.deinit(gpa); |
| 458 | 468 | |
| 459 | 469 | for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) { |
| 460 | .null => {}, | |
| 461 | .zig_object => data.zig_object.deinit(gpa), | |
| 470 | .null, .zig_object => {}, | |
| 462 | 471 | .linker_defined => data.linker_defined.deinit(gpa), |
| 463 | 472 | .object => data.object.deinit(gpa), |
| 464 | 473 | .shared_object => data.shared_object.deinit(gpa), |
| 465 | 474 | }; |
| 475 | if (self.zig_object) |zig_object| { | |
| 476 | zig_object.deinit(gpa); | |
| 477 | } | |
| 466 | 478 | self.files.deinit(gpa); |
| 467 | 479 | self.objects.deinit(gpa); |
| 468 | 480 | self.shared_objects.deinit(gpa); |
| ... | ... | @@ -501,6 +513,7 @@ pub fn deinit(self: *Elf) void { |
| 501 | 513 | self.rela_dyn.deinit(gpa); |
| 502 | 514 | self.rela_plt.deinit(gpa); |
| 503 | 515 | self.comdat_group_sections.deinit(gpa); |
| 516 | self.dump_argv_list.deinit(gpa); | |
| 504 | 517 | } |
| 505 | 518 | |
| 506 | 519 | pub 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 | 765 | return res; |
| 753 | 766 | } |
| 754 | 767 | |
| 768 | pub 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 | ||
| 755 | 799 | pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { |
| 756 | 800 | const use_lld = build_options.have_llvm and self.base.comp.config.use_lld; |
| 757 | 801 | if (use_lld) { |
| ... | ... | @@ -774,11 +818,11 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 774 | 818 | if (use_lld) return; |
| 775 | 819 | } |
| 776 | 820 | |
| 821 | if (comp.verbose_link) Compilation.dump_argv(self.dump_argv_list.items); | |
| 822 | ||
| 777 | 823 | const sub_prog_node = prog_node.start("ELF Flush", 0); |
| 778 | 824 | defer sub_prog_node.end(); |
| 779 | 825 | |
| 780 | const target = self.getTarget(); | |
| 781 | const link_mode = comp.config.link_mode; | |
| 782 | 826 | const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. |
| 783 | 827 | const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{ |
| 784 | 828 | .root_dir = directory, |
| ... | ... | @@ -788,126 +832,19 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 788 | 832 | path, |
| 789 | 833 | } else null; |
| 790 | 834 | |
| 791 | // --verbose-link | |
| 792 | if (comp.verbose_link) try self.dumpArgv(comp); | |
| 793 | ||
| 794 | 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); | |
| 799 | 836 | |
| 800 | // csu prelude | |
| 801 | if (csu.crt0) |path| parseObjectReportingFailure(self, path); | |
| 802 | if (csu.crti) |path| parseObjectReportingFailure(self, path); | |
| 803 | if (csu.crtbegin) |path| parseObjectReportingFailure(self, path); | |
| 804 | ||
| 805 | for (comp.objects) |obj| { | |
| 806 | parseInputReportingFailure(self, obj.path, obj.needed, obj.must_link); | |
| 807 | } | |
| 808 | ||
| 809 | // This is a set of object files emitted by clang in a single `build-exe` invocation. | |
| 810 | // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up | |
| 811 | // in this set. | |
| 812 | for (comp.c_object_table.keys()) |key| { | |
| 813 | parseObjectReportingFailure(self, key.status.success.object_path); | |
| 814 | } | |
| 815 | ||
| 816 | if (module_obj_path) |path| parseObjectReportingFailure(self, path); | |
| 817 | ||
| 818 | if (comp.config.any_sanitize_thread) parseCrtFileReportingFailure(self, comp.tsan_lib.?); | |
| 819 | if (comp.config.any_fuzz) parseCrtFileReportingFailure(self, comp.fuzzer_lib.?); | |
| 820 | ||
| 821 | // libc | |
| 822 | if (!comp.skip_linker_dependencies and !comp.config.link_libc) { | |
| 823 | if (comp.libc_static_lib) |lib| parseCrtFileReportingFailure(self, lib); | |
| 824 | } | |
| 837 | if (module_obj_path) |path| openParseObjectReportingFailure(self, path); | |
| 825 | 838 | |
| 826 | for (comp.system_libs.values()) |lib_info| { | |
| 827 | parseInputReportingFailure(self, lib_info.path.?, lib_info.needed, false); | |
| 828 | } | |
| 829 | ||
| 830 | // libc++ dep | |
| 831 | if (comp.config.link_libcpp) { | |
| 832 | parseInputReportingFailure(self, comp.libcxxabi_static_lib.?.full_object_path, false, false); | |
| 833 | parseInputReportingFailure(self, comp.libcxx_static_lib.?.full_object_path, false, false); | |
| 834 | } | |
| 835 | ||
| 836 | // libunwind dep | |
| 837 | if (comp.config.link_libunwind) { | |
| 838 | parseInputReportingFailure(self, comp.libunwind_static_lib.?.full_object_path, false, false); | |
| 839 | } | |
| 840 | ||
| 841 | // libc dep | |
| 842 | diags.flags.missing_libc = false; | |
| 843 | if (comp.config.link_libc) { | |
| 844 | if (comp.libc_installation) |lc| { | |
| 845 | const flags = target_util.libcFullLinkFlags(target); | |
| 846 | ||
| 847 | var test_path = std.ArrayList(u8).init(arena); | |
| 848 | var checked_paths = std.ArrayList([]const u8).init(arena); | |
| 849 | ||
| 850 | for (flags) |flag| { | |
| 851 | checked_paths.clearRetainingCapacity(); | |
| 852 | const lib_name = flag["-l".len..]; | |
| 853 | ||
| 854 | success: { | |
| 855 | if (!self.base.isStatic()) { | |
| 856 | if (try self.accessLibPath(arena, &test_path, &checked_paths, lc.crt_dir.?, lib_name, .dynamic)) | |
| 857 | break :success; | |
| 858 | } | |
| 859 | if (try self.accessLibPath(arena, &test_path, &checked_paths, lc.crt_dir.?, lib_name, .static)) | |
| 860 | break :success; | |
| 861 | ||
| 862 | diags.addMissingLibraryError( | |
| 863 | checked_paths.items, | |
| 864 | "missing system library: '{s}' was not found", | |
| 865 | .{lib_name}, | |
| 866 | ); | |
| 867 | continue; | |
| 868 | } | |
| 869 | ||
| 870 | const resolved_path = Path.initCwd(try arena.dupe(u8, test_path.items)); | |
| 871 | parseInputReportingFailure(self, resolved_path, false, false); | |
| 872 | } | |
| 873 | } else if (target.isGnuLibC()) { | |
| 874 | for (glibc.libs) |lib| { | |
| 875 | if (lib.removed_in) |rem_in| { | |
| 876 | if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue; | |
| 877 | } | |
| 878 | ||
| 879 | const lib_path = Path.initCwd(try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ | |
| 880 | comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, | |
| 881 | })); | |
| 882 | parseInputReportingFailure(self, lib_path, false, false); | |
| 883 | } | |
| 884 | parseInputReportingFailure(self, try comp.get_libc_crt_file(arena, "libc_nonshared.a"), false, false); | |
| 885 | } else if (target.isMusl()) { | |
| 886 | const path = try comp.get_libc_crt_file(arena, switch (link_mode) { | |
| 887 | .static => "libc.a", | |
| 888 | .dynamic => "libc.so", | |
| 889 | }); | |
| 890 | parseInputReportingFailure(self, path, false, false); | |
| 891 | } else { | |
| 892 | diags.flags.missing_libc = true; | |
| 893 | } | |
| 894 | } | |
| 895 | ||
| 896 | // Finally, as the last input objects we add compiler_rt and CSU postlude (if any). | |
| 897 | ||
| 898 | // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs | |
| 899 | // to be after the shared libraries, so they are picked up from the shared | |
| 900 | // libraries, not libcompiler_rt. | |
| 901 | if (comp.compiler_rt_lib) |crt_file| { | |
| 902 | parseInputReportingFailure(self, crt_file.full_object_path, false, false); | |
| 903 | } else if (comp.compiler_rt_obj) |crt_file| { | |
| 904 | parseObjectReportingFailure(self, crt_file.full_object_path); | |
| 839 | switch (comp.config.output_mode) { | |
| 840 | .Obj => return relocatable.flushObject(self, comp), | |
| 841 | .Lib => switch (comp.config.link_mode) { | |
| 842 | .dynamic => {}, | |
| 843 | .static => return relocatable.flushStaticLib(self, comp), | |
| 844 | }, | |
| 845 | .Exe => {}, | |
| 905 | 846 | } |
| 906 | 847 | |
| 907 | // csu postlude | |
| 908 | if (csu.crtend) |path| parseObjectReportingFailure(self, path); | |
| 909 | if (csu.crtn) |path| parseObjectReportingFailure(self, path); | |
| 910 | ||
| 911 | 848 | if (diags.hasErrors()) return error.FlushFailure; |
| 912 | 849 | |
| 913 | 850 | // 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 | 995 | if (diags.hasErrors()) return error.FlushFailure; |
| 1059 | 996 | } |
| 1060 | 997 | |
| 1061 | /// --verbose-link output | |
| 1062 | fn dumpArgv(self: *Elf, comp: *Compilation) !void { | |
| 1063 | const gpa = self.base.comp.gpa; | |
| 1064 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); | |
| 1065 | defer arena_allocator.deinit(); | |
| 1066 | const arena = arena_allocator.allocator(); | |
| 1067 | ||
| 998 | fn dumpArgvInit(self: *Elf, arena: Allocator) !void { | |
| 999 | const comp = self.base.comp; | |
| 1000 | const gpa = comp.gpa; | |
| 1068 | 1001 | const target = self.getTarget(); |
| 1069 | const link_mode = self.base.comp.config.link_mode; | |
| 1070 | const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. | |
| 1071 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); | |
| 1072 | const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { | |
| 1073 | if (fs.path.dirname(full_out_path)) |dirname| { | |
| 1074 | break :blk try fs.path.join(arena, &.{ dirname, path }); | |
| 1075 | } else { | |
| 1076 | break :blk path; | |
| 1077 | } | |
| 1078 | } else null; | |
| 1079 | ||
| 1080 | const csu = try comp.getCrtPaths(arena); | |
| 1081 | const compiler_rt_path: ?[]const u8 = blk: { | |
| 1082 | if (comp.compiler_rt_lib) |x| break :blk try x.full_object_path.toString(arena); | |
| 1083 | if (comp.compiler_rt_obj) |x| break :blk try x.full_object_path.toString(arena); | |
| 1084 | break :blk null; | |
| 1085 | }; | |
| 1002 | const full_out_path = try self.base.emit.root_dir.join(arena, &[_][]const u8{self.base.emit.sub_path}); | |
| 1086 | 1003 | |
| 1087 | var argv = std.ArrayList([]const u8).init(arena); | |
| 1004 | const argv = &self.dump_argv_list; | |
| 1088 | 1005 | |
| 1089 | try argv.append("zig"); | |
| 1006 | try argv.append(gpa, "zig"); | |
| 1090 | 1007 | |
| 1091 | 1008 | if (self.base.isStaticLib()) { |
| 1092 | try argv.append("ar"); | |
| 1009 | try argv.append(gpa, "ar"); | |
| 1093 | 1010 | } else { |
| 1094 | try argv.append("ld"); | |
| 1011 | try argv.append(gpa, "ld"); | |
| 1095 | 1012 | } |
| 1096 | 1013 | |
| 1097 | 1014 | if (self.base.isObject()) { |
| 1098 | try argv.append("-r"); | |
| 1015 | try argv.append(gpa, "-r"); | |
| 1099 | 1016 | } |
| 1100 | 1017 | |
| 1101 | try argv.append("-o"); | |
| 1102 | try argv.append(full_out_path); | |
| 1018 | try argv.append(gpa, "-o"); | |
| 1019 | try argv.append(gpa, full_out_path); | |
| 1103 | 1020 | |
| 1104 | if (self.base.isRelocatable()) { | |
| 1105 | for (comp.objects) |obj| { | |
| 1106 | try argv.append(try obj.path.toString(arena)); | |
| 1107 | } | |
| 1108 | ||
| 1109 | for (comp.c_object_table.keys()) |key| { | |
| 1110 | try argv.append(try key.status.success.object_path.toString(arena)); | |
| 1111 | } | |
| 1112 | ||
| 1113 | if (module_obj_path) |p| { | |
| 1114 | try argv.append(p); | |
| 1115 | } | |
| 1116 | } else { | |
| 1021 | if (!self.base.isRelocatable()) { | |
| 1117 | 1022 | if (!self.base.isStatic()) { |
| 1118 | 1023 | if (target.dynamic_linker.get()) |path| { |
| 1119 | try argv.append("-dynamic-linker"); | |
| 1120 | try argv.append(path); | |
| 1024 | try argv.appendSlice(gpa, &.{ "-dynamic-linker", try arena.dupe(u8, path) }); | |
| 1121 | 1025 | } |
| 1122 | 1026 | } |
| 1123 | 1027 | |
| 1124 | 1028 | if (self.base.isDynLib()) { |
| 1125 | 1029 | if (self.soname) |name| { |
| 1126 | try argv.append("-soname"); | |
| 1127 | try argv.append(name); | |
| 1030 | try argv.append(gpa, "-soname"); | |
| 1031 | try argv.append(gpa, name); | |
| 1128 | 1032 | } |
| 1129 | 1033 | } |
| 1130 | 1034 | |
| 1131 | 1035 | if (self.entry_name) |name| { |
| 1132 | try argv.appendSlice(&.{ "--entry", name }); | |
| 1036 | try argv.appendSlice(gpa, &.{ "--entry", name }); | |
| 1133 | 1037 | } |
| 1134 | 1038 | |
| 1135 | 1039 | for (self.rpath_table.keys()) |rpath| { |
| 1136 | try argv.appendSlice(&.{ "-rpath", rpath }); | |
| 1040 | try argv.appendSlice(gpa, &.{ "-rpath", rpath }); | |
| 1137 | 1041 | } |
| 1138 | 1042 | |
| 1139 | try argv.appendSlice(&.{ | |
| 1043 | try argv.appendSlice(gpa, &.{ | |
| 1140 | 1044 | "-z", |
| 1141 | 1045 | try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}), |
| 1142 | 1046 | }); |
| 1143 | 1047 | |
| 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})); | |
| 1145 | 1049 | |
| 1146 | 1050 | if (self.base.gc_sections) { |
| 1147 | try argv.append("--gc-sections"); | |
| 1051 | try argv.append(gpa, "--gc-sections"); | |
| 1148 | 1052 | } |
| 1149 | 1053 | |
| 1150 | 1054 | if (self.base.print_gc_sections) { |
| 1151 | try argv.append("--print-gc-sections"); | |
| 1055 | try argv.append(gpa, "--print-gc-sections"); | |
| 1152 | 1056 | } |
| 1153 | 1057 | |
| 1154 | 1058 | if (comp.link_eh_frame_hdr) { |
| 1155 | try argv.append("--eh-frame-hdr"); | |
| 1059 | try argv.append(gpa, "--eh-frame-hdr"); | |
| 1156 | 1060 | } |
| 1157 | 1061 | |
| 1158 | 1062 | if (comp.config.rdynamic) { |
| 1159 | try argv.append("--export-dynamic"); | |
| 1063 | try argv.append(gpa, "--export-dynamic"); | |
| 1160 | 1064 | } |
| 1161 | 1065 | |
| 1162 | 1066 | if (self.z_notext) { |
| 1163 | try argv.append("-z"); | |
| 1164 | try argv.append("notext"); | |
| 1067 | try argv.append(gpa, "-z"); | |
| 1068 | try argv.append(gpa, "notext"); | |
| 1165 | 1069 | } |
| 1166 | 1070 | |
| 1167 | 1071 | if (self.z_nocopyreloc) { |
| 1168 | try argv.append("-z"); | |
| 1169 | try argv.append("nocopyreloc"); | |
| 1072 | try argv.append(gpa, "-z"); | |
| 1073 | try argv.append(gpa, "nocopyreloc"); | |
| 1170 | 1074 | } |
| 1171 | 1075 | |
| 1172 | 1076 | if (self.z_now) { |
| 1173 | try argv.append("-z"); | |
| 1174 | try argv.append("now"); | |
| 1077 | try argv.append(gpa, "-z"); | |
| 1078 | try argv.append(gpa, "now"); | |
| 1175 | 1079 | } |
| 1176 | 1080 | |
| 1177 | 1081 | if (self.base.isStatic()) { |
| 1178 | try argv.append("-static"); | |
| 1082 | try argv.append(gpa, "-static"); | |
| 1179 | 1083 | } else if (self.isEffectivelyDynLib()) { |
| 1180 | try argv.append("-shared"); | |
| 1084 | try argv.append(gpa, "-shared"); | |
| 1181 | 1085 | } |
| 1182 | 1086 | |
| 1183 | 1087 | if (comp.config.pie and self.base.isExe()) { |
| 1184 | try argv.append("-pie"); | |
| 1088 | try argv.append(gpa, "-pie"); | |
| 1185 | 1089 | } |
| 1186 | 1090 | |
| 1187 | 1091 | if (comp.config.debug_format == .strip) { |
| 1188 | try argv.append("-s"); | |
| 1189 | } | |
| 1190 | ||
| 1191 | // csu prelude | |
| 1192 | if (csu.crt0) |path| try argv.append(try path.toString(arena)); | |
| 1193 | if (csu.crti) |path| try argv.append(try path.toString(arena)); | |
| 1194 | if (csu.crtbegin) |path| try argv.append(try path.toString(arena)); | |
| 1195 | ||
| 1196 | for (self.lib_dirs) |lib_dir| { | |
| 1197 | try argv.append("-L"); | |
| 1198 | try argv.append(lib_dir); | |
| 1199 | } | |
| 1200 | ||
| 1201 | if (comp.config.link_libc) { | |
| 1202 | if (self.base.comp.libc_installation) |libc_installation| { | |
| 1203 | try argv.append("-L"); | |
| 1204 | try argv.append(libc_installation.crt_dir.?); | |
| 1205 | } | |
| 1206 | } | |
| 1207 | ||
| 1208 | var whole_archive = false; | |
| 1209 | for (comp.objects) |obj| { | |
| 1210 | if (obj.must_link and !whole_archive) { | |
| 1211 | try argv.append("-whole-archive"); | |
| 1212 | whole_archive = true; | |
| 1213 | } else if (!obj.must_link and whole_archive) { | |
| 1214 | try argv.append("-no-whole-archive"); | |
| 1215 | whole_archive = false; | |
| 1216 | } | |
| 1217 | ||
| 1218 | if (obj.loption) { | |
| 1219 | try argv.append("-l"); | |
| 1220 | } | |
| 1221 | try argv.append(try obj.path.toString(arena)); | |
| 1222 | } | |
| 1223 | if (whole_archive) { | |
| 1224 | try argv.append("-no-whole-archive"); | |
| 1225 | whole_archive = false; | |
| 1226 | } | |
| 1227 | ||
| 1228 | for (comp.c_object_table.keys()) |key| { | |
| 1229 | try argv.append(try key.status.success.object_path.toString(arena)); | |
| 1230 | } | |
| 1231 | ||
| 1232 | if (module_obj_path) |p| { | |
| 1233 | try argv.append(p); | |
| 1234 | } | |
| 1235 | ||
| 1236 | if (comp.config.any_sanitize_thread) { | |
| 1237 | try argv.append(try comp.tsan_lib.?.full_object_path.toString(arena)); | |
| 1092 | try argv.append(gpa, "-s"); | |
| 1238 | 1093 | } |
| 1239 | 1094 | |
| 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 | 1095 | if (comp.config.link_libc) { |
| 1292 | if (self.base.comp.libc_installation != null) { | |
| 1293 | const needs_grouping = link_mode == .static; | |
| 1294 | if (needs_grouping) try argv.append("--start-group"); | |
| 1295 | try argv.appendSlice(target_util.libcFullLinkFlags(target)); | |
| 1296 | if (needs_grouping) try argv.append("--end-group"); | |
| 1297 | } else if (target.isGnuLibC()) { | |
| 1298 | for (glibc.libs) |lib| { | |
| 1299 | if (lib.removed_in) |rem_in| { | |
| 1300 | if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue; | |
| 1301 | } | |
| 1302 | ||
| 1303 | const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ | |
| 1304 | comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, | |
| 1305 | }); | |
| 1306 | try argv.append(lib_path); | |
| 1307 | } | |
| 1308 | try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a")); | |
| 1309 | } else if (target.isMusl()) { | |
| 1310 | try argv.append(try comp.crtFileAsString(arena, switch (link_mode) { | |
| 1311 | .static => "libc.a", | |
| 1312 | .dynamic => "libc.so", | |
| 1313 | })); | |
| 1096 | if (self.base.comp.libc_installation) |lci| { | |
| 1097 | try argv.append(gpa, "-L"); | |
| 1098 | try argv.append(gpa, lci.crt_dir.?); | |
| 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 | } |
| 1329 | 1103 | |
| 1330 | pub const ParseError = error{ | |
| 1331 | /// Indicates the error is already reported on `Compilation.link_diags`. | |
| 1332 | LinkFailure, | |
| 1333 | ||
| 1334 | OutOfMemory, | |
| 1335 | Overflow, | |
| 1336 | InputOutput, | |
| 1337 | EndOfStream, | |
| 1338 | FileSystem, | |
| 1339 | NotSupported, | |
| 1340 | InvalidCharacter, | |
| 1341 | UnknownFileType, | |
| 1342 | } || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError; | |
| 1343 | ||
| 1344 | fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CrtFile) void { | |
| 1345 | parseInputReportingFailure(self, crt_file.full_object_path, false, false); | |
| 1346 | } | |
| 1347 | ||
| 1348 | pub fn parseInputReportingFailure(self: *Elf, path: Path, needed: bool, must_link: bool) void { | |
| 1349 | const gpa = self.base.comp.gpa; | |
| 1104 | pub fn openParseObjectReportingFailure(self: *Elf, path: Path) void { | |
| 1350 | 1105 | const diags = &self.base.comp.link_diags; |
| 1351 | const target = self.getTarget(); | |
| 1352 | ||
| 1353 | switch (Compilation.classifyFileExt(path.sub_path)) { | |
| 1354 | .object => parseObjectReportingFailure(self, path), | |
| 1355 | .shared_library => parseSharedObject(gpa, diags, .{ | |
| 1356 | .path = path, | |
| 1357 | .needed = needed, | |
| 1358 | }, &self.shared_objects, &self.files, target) catch |err| switch (err) { | |
| 1359 | error.LinkFailure => return, // already reported | |
| 1360 | error.BadMagic, error.UnexpectedEndOfFile => { | |
| 1361 | // It could be a linker script. | |
| 1362 | self.parseLdScript(.{ .path = path, .needed = needed }) catch |err2| switch (err2) { | |
| 1363 | error.LinkFailure => return, // already reported | |
| 1364 | else => |e| diags.addParseError(path, "failed to parse linker script: {s}", .{@errorName(e)}), | |
| 1365 | }; | |
| 1366 | }, | |
| 1367 | else => |e| diags.addParseError(path, "failed to parse shared object: {s}", .{@errorName(e)}), | |
| 1368 | }, | |
| 1369 | .static_library => parseArchive(self, path, must_link) catch |err| switch (err) { | |
| 1370 | error.LinkFailure => return, // already reported | |
| 1371 | else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}), | |
| 1372 | }, | |
| 1373 | .unknown => self.parseLdScript(.{ .path = path, .needed = needed }) catch |err| switch (err) { | |
| 1374 | error.LinkFailure => return, // already reported | |
| 1375 | else => |e| diags.addParseError(path, "failed to parse linker script: {s}", .{@errorName(e)}), | |
| 1376 | }, | |
| 1377 | else => diags.addParseError(path, "unrecognized file type", .{}), | |
| 1378 | } | |
| 1106 | const obj = link.openObject(path, false, false) catch |err| { | |
| 1107 | switch (diags.failParse(path, "failed to open object {}: {s}", .{ path, @errorName(err) })) { | |
| 1108 | error.LinkFailure => return, | |
| 1109 | } | |
| 1110 | }; | |
| 1111 | self.parseObjectReportingFailure(obj); | |
| 1379 | 1112 | } |
| 1380 | 1113 | |
| 1381 | pub fn parseObjectReportingFailure(self: *Elf, path: Path) void { | |
| 1114 | fn parseObjectReportingFailure(self: *Elf, obj: link.Input.Object) void { | |
| 1382 | 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 | 1117 | 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 | } |
| 1388 | 1121 | |
| 1389 | fn parseObject(self: *Elf, path: Path) ParseError!void { | |
| 1122 | fn parseObject(self: *Elf, obj: link.Input.Object) !void { | |
| 1390 | 1123 | const tracy = trace(@src()); |
| 1391 | 1124 | defer tracy.end(); |
| 1392 | 1125 | |
| 1393 | 1126 | const gpa = self.base.comp.gpa; |
| 1394 | const handle = try path.root_dir.handle.openFile(path.sub_path, .{}); | |
| 1395 | const fh = try self.addFileHandle(handle); | |
| 1127 | const diags = &self.base.comp.link_diags; | |
| 1128 | const first_eflags = &self.first_eflags; | |
| 1129 | const target = self.base.comp.root_mod.resolved_target.result; | |
| 1130 | const debug_fmt_strip = self.base.comp.config.debug_format == .strip; | |
| 1131 | const default_sym_version = self.default_sym_version; | |
| 1132 | const file_handles = &self.file_handles; | |
| 1133 | ||
| 1134 | const handle = obj.file; | |
| 1135 | const fh = try addFileHandle(gpa, file_handles, handle); | |
| 1396 | 1136 | |
| 1397 | 1137 | const index: File.Index = @intCast(try self.files.addOne(gpa)); |
| 1398 | 1138 | self.files.set(index, .{ .object = .{ |
| 1399 | 1139 | .path = .{ |
| 1400 | .root_dir = path.root_dir, | |
| 1401 | .sub_path = try gpa.dupe(u8, path.sub_path), | |
| 1140 | .root_dir = obj.path.root_dir, | |
| 1141 | .sub_path = try gpa.dupe(u8, obj.path.sub_path), | |
| 1402 | 1142 | }, |
| 1403 | 1143 | .file_handle = fh, |
| 1404 | 1144 | .index = index, |
| ... | ... | @@ -1406,39 +1146,51 @@ fn parseObject(self: *Elf, path: Path) ParseError!void { |
| 1406 | 1146 | try self.objects.append(gpa, index); |
| 1407 | 1147 | |
| 1408 | 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 | } |
| 1411 | 1154 | |
| 1412 | fn parseArchive(self: *Elf, path: Path, must_link: bool) ParseError!void { | |
| 1155 | fn 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 | 1168 | const tracy = trace(@src()); |
| 1414 | 1169 | defer tracy.end(); |
| 1415 | 1170 | |
| 1416 | const gpa = self.base.comp.gpa; | |
| 1417 | const handle = try path.root_dir.handle.openFile(path.sub_path, .{}); | |
| 1418 | const fh = try self.addFileHandle(handle); | |
| 1419 | ||
| 1420 | var archive: Archive = .{}; | |
| 1171 | const fh = try addFileHandle(gpa, file_handles, obj.file); | |
| 1172 | var archive = try Archive.parse(gpa, diags, file_handles, obj.path, fh); | |
| 1421 | 1173 | defer archive.deinit(gpa); |
| 1422 | try archive.parse(self, path, fh); | |
| 1423 | 1174 | |
| 1424 | const objects = try archive.objects.toOwnedSlice(gpa); | |
| 1425 | defer gpa.free(objects); | |
| 1175 | const init_alive = if (is_static_lib) true else obj.must_link; | |
| 1426 | 1176 | |
| 1427 | for (objects) |extracted| { | |
| 1428 | const index: File.Index = @intCast(try self.files.addOne(gpa)); | |
| 1429 | self.files.set(index, .{ .object = extracted }); | |
| 1430 | const object = &self.files.items(.data)[index].object; | |
| 1177 | for (archive.objects) |extracted| { | |
| 1178 | const index: File.Index = @intCast(try files.addOne(gpa)); | |
| 1179 | files.set(index, .{ .object = extracted }); | |
| 1180 | const object = &files.items(.data)[index].object; | |
| 1431 | 1181 | object.index = index; |
| 1432 | object.alive = must_link; | |
| 1433 | try object.parse(self); | |
| 1434 | try self.objects.append(gpa, index); | |
| 1182 | object.alive = init_alive; | |
| 1183 | try object.parseCommon(gpa, diags, obj.path, obj.file, target, first_eflags); | |
| 1184 | if (!is_static_lib) | |
| 1185 | try object.parse(gpa, diags, obj.path, obj.file, target, debug_fmt_strip, default_sym_version); | |
| 1186 | try objects.append(gpa, index); | |
| 1435 | 1187 | } |
| 1436 | 1188 | } |
| 1437 | 1189 | |
| 1438 | fn parseSharedObject( | |
| 1190 | fn parseDso( | |
| 1439 | 1191 | gpa: Allocator, |
| 1440 | 1192 | diags: *Diags, |
| 1441 | lib: SystemLib, | |
| 1193 | dso: link.Input.Dso, | |
| 1442 | 1194 | shared_objects: *std.StringArrayHashMapUnmanaged(File.Index), |
| 1443 | 1195 | files: *std.MultiArrayList(File.Entry), |
| 1444 | 1196 | target: std.Target, |
| ... | ... | @@ -1446,20 +1198,16 @@ fn parseSharedObject( |
| 1446 | 1198 | const tracy = trace(@src()); |
| 1447 | 1199 | defer tracy.end(); |
| 1448 | 1200 | |
| 1449 | const handle = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{}); | |
| 1450 | defer handle.close(); | |
| 1201 | const handle = dso.file; | |
| 1451 | 1202 | |
| 1452 | 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 | 1205 | defer header.deinit(gpa); |
| 1455 | 1206 | |
| 1456 | const soname = header.soname() orelse lib.path.basename(); | |
| 1207 | const soname = header.soname() orelse dso.path.basename(); | |
| 1457 | 1208 | |
| 1458 | 1209 | const gop = try shared_objects.getOrPut(gpa, soname); |
| 1459 | if (gop.found_existing) { | |
| 1460 | header.deinit(gpa); | |
| 1461 | return; | |
| 1462 | } | |
| 1210 | if (gop.found_existing) return; | |
| 1463 | 1211 | errdefer _ = shared_objects.pop(); |
| 1464 | 1212 | |
| 1465 | 1213 | const index: File.Index = @intCast(try files.addOne(gpa)); |
| ... | ... | @@ -1471,8 +1219,8 @@ fn parseSharedObject( |
| 1471 | 1219 | errdefer parsed.deinit(gpa); |
| 1472 | 1220 | |
| 1473 | 1221 | const duped_path: Path = .{ |
| 1474 | .root_dir = lib.path.root_dir, | |
| 1475 | .sub_path = try gpa.dupe(u8, lib.path.sub_path), | |
| 1222 | .root_dir = dso.path.root_dir, | |
| 1223 | .sub_path = try gpa.dupe(u8, dso.path.sub_path), | |
| 1476 | 1224 | }; |
| 1477 | 1225 | errdefer gpa.free(duped_path.sub_path); |
| 1478 | 1226 | |
| ... | ... | @@ -1481,8 +1229,8 @@ fn parseSharedObject( |
| 1481 | 1229 | .parsed = parsed, |
| 1482 | 1230 | .path = duped_path, |
| 1483 | 1231 | .index = index, |
| 1484 | .needed = lib.needed, | |
| 1485 | .alive = lib.needed, | |
| 1232 | .needed = dso.needed, | |
| 1233 | .alive = dso.needed, | |
| 1486 | 1234 | .aliases = null, |
| 1487 | 1235 | .symbols = .empty, |
| 1488 | 1236 | .symbols_extra = .empty, |
| ... | ... | @@ -1490,7 +1238,7 @@ fn parseSharedObject( |
| 1490 | 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; | |
| 1494 | 1242 | |
| 1495 | 1243 | // TODO: save this work for later |
| 1496 | 1244 | const nsyms = parsed.symbols.len; |
| ... | ... | @@ -1511,148 +1259,6 @@ fn parseSharedObject( |
| 1511 | 1259 | } |
| 1512 | 1260 | } |
| 1513 | 1261 | |
| 1514 | fn 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 | ||
| 1583 | pub 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 | ||
| 1623 | fn 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 | 1262 | /// When resolving symbols, we approach the problem similarly to `mold`. |
| 1657 | 1263 | /// 1. Resolve symbols across all objects (including those preemptively extracted archives). |
| 1658 | 1264 | /// 2. Resolve symbols across all shared objects. |
| ... | ... | @@ -1842,7 +1448,7 @@ pub fn initOutputSection(self: *Elf, args: struct { |
| 1842 | 1448 | ".dtors", ".gnu.warning", |
| 1843 | 1449 | }; |
| 1844 | 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 | 1452 | break :blk prefix; |
| 1847 | 1453 | } |
| 1848 | 1454 | } |
| ... | ... | @@ -1854,9 +1460,9 @@ pub fn initOutputSection(self: *Elf, args: struct { |
| 1854 | 1460 | switch (args.type) { |
| 1855 | 1461 | elf.SHT_NULL => unreachable, |
| 1856 | 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 | 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 | 1466 | break :tt elf.SHT_FINI_ARRAY; |
| 1861 | 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 | 1557 | try man.addOptionalFile(self.version_script); |
| 1952 | 1558 | man.hash.add(self.allow_undefined_version); |
| 1953 | 1559 | man.hash.addOptional(self.enable_new_dtags); |
| 1954 | for (comp.objects) |obj| { | |
| 1955 | _ = try man.addFilePath(obj.path, null); | |
| 1956 | man.hash.add(obj.must_link); | |
| 1957 | man.hash.add(obj.loption); | |
| 1958 | } | |
| 1560 | try link.hashInputs(&man, comp.link_inputs); | |
| 1959 | 1561 | for (comp.c_object_table.keys()) |key| { |
| 1960 | 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 | 1575 | man.hash.add(comp.link_eh_frame_hdr); |
| 1974 | 1576 | man.hash.add(self.emit_relocs); |
| 1975 | 1577 | man.hash.add(comp.config.rdynamic); |
| 1976 | man.hash.addListOfBytes(self.lib_dirs); | |
| 1977 | 1578 | man.hash.addListOfBytes(self.rpath_table.keys()); |
| 1978 | 1579 | if (output_mode == .Exe) { |
| 1979 | 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 | 1604 | } |
| 2004 | 1605 | man.hash.addOptionalBytes(self.soname); |
| 2005 | 1606 | man.hash.addOptional(comp.version); |
| 2006 | try link.hashAddSystemLibs(&man, comp.system_libs); | |
| 2007 | 1607 | man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); |
| 2008 | 1608 | man.hash.add(self.base.allow_shlib_undefined); |
| 2009 | 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 | 1650 | // here. TODO: think carefully about how we can avoid this redundant operation when doing |
| 2051 | 1651 | // build-obj. See also the corresponding TODO in linkAsArchive. |
| 2052 | 1652 | const the_object_path = blk: { |
| 2053 | if (comp.objects.len != 0) | |
| 2054 | break :blk comp.objects[0].path; | |
| 1653 | if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path; | |
| 2055 | 1654 | |
| 2056 | 1655 | if (comp.c_object_table.count() != 0) |
| 2057 | 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 | 1866 | try argv.appendSlice(&.{ "-wrap", symbol_name }); |
| 2268 | 1867 | } |
| 2269 | 1868 | |
| 2270 | for (self.lib_dirs) |lib_dir| { | |
| 2271 | try argv.append("-L"); | |
| 2272 | try argv.append(lib_dir); | |
| 2273 | } | |
| 2274 | ||
| 2275 | 1869 | if (comp.config.link_libc) { |
| 2276 | 1870 | if (comp.libc_installation) |libc_installation| { |
| 2277 | 1871 | try argv.append("-L"); |
| ... | ... | @@ -2311,21 +1905,26 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s |
| 2311 | 1905 | |
| 2312 | 1906 | // Positional arguments to the linker such as object files. |
| 2313 | 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 | } | |
| 2322 | 1908 | |
| 2323 | if (obj.loption) { | |
| 2324 | assert(obj.path.sub_path[0] == ':'); | |
| 2325 | try argv.append("-l"); | |
| 2326 | } | |
| 2327 | try argv.append(try obj.path.toString(arena)); | |
| 2328 | } | |
| 1909 | for (self.base.comp.link_inputs) |link_input| switch (link_input) { | |
| 1910 | .res => unreachable, // Windows-only | |
| 1911 | .dso => continue, | |
| 1912 | .object, .archive => |obj| { | |
| 1913 | if (obj.must_link and !whole_archive) { | |
| 1914 | try argv.append("-whole-archive"); | |
| 1915 | whole_archive = true; | |
| 1916 | } else if (!obj.must_link and whole_archive) { | |
| 1917 | try argv.append("-no-whole-archive"); | |
| 1918 | whole_archive = false; | |
| 1919 | } | |
| 1920 | try argv.append(try obj.path.toString(arena)); | |
| 1921 | }, | |
| 1922 | .dso_exact => |dso_exact| { | |
| 1923 | assert(dso_exact.name[0] == ':'); | |
| 1924 | try argv.appendSlice(&.{ "-l", dso_exact.name }); | |
| 1925 | }, | |
| 1926 | }; | |
| 1927 | ||
| 2329 | 1928 | if (whole_archive) { |
| 2330 | 1929 | try argv.append("-no-whole-archive"); |
| 2331 | 1930 | whole_archive = false; |
| ... | ... | @@ -2361,35 +1960,35 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s |
| 2361 | 1960 | |
| 2362 | 1961 | // Shared libraries. |
| 2363 | 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 | 1963 | // Worst-case, we need an --as-needed argument for every lib, as well |
| 2368 | 1964 | // as one before and one after. |
| 2369 | try argv.ensureUnusedCapacity(system_libs.len * 2 + 2); | |
| 2370 | argv.appendAssumeCapacity("--as-needed"); | |
| 1965 | try argv.append("--as-needed"); | |
| 2371 | 1966 | var as_needed = true; |
| 2372 | 1967 | |
| 2373 | for (system_libs_values) |lib_info| { | |
| 2374 | const lib_as_needed = !lib_info.needed; | |
| 2375 | switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) { | |
| 2376 | 0b00, 0b11 => {}, | |
| 2377 | 0b01 => { | |
| 2378 | argv.appendAssumeCapacity("--no-as-needed"); | |
| 2379 | as_needed = false; | |
| 2380 | }, | |
| 2381 | 0b10 => { | |
| 2382 | argv.appendAssumeCapacity("--as-needed"); | |
| 2383 | as_needed = true; | |
| 2384 | }, | |
| 2385 | } | |
| 1968 | for (self.base.comp.link_inputs) |link_input| switch (link_input) { | |
| 1969 | .res => unreachable, // Windows-only | |
| 1970 | .object, .archive, .dso_exact => continue, | |
| 1971 | .dso => |dso| { | |
| 1972 | const lib_as_needed = !dso.needed; | |
| 1973 | switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) { | |
| 1974 | 0b00, 0b11 => {}, | |
| 1975 | 0b01 => { | |
| 1976 | argv.appendAssumeCapacity("--no-as-needed"); | |
| 1977 | as_needed = false; | |
| 1978 | }, | |
| 1979 | 0b10 => { | |
| 1980 | argv.appendAssumeCapacity("--as-needed"); | |
| 1981 | as_needed = true; | |
| 1982 | }, | |
| 1983 | } | |
| 2386 | 1984 | |
| 2387 | // By this time, we depend on these libs being dynamically linked | |
| 2388 | // libraries and not static libraries (the check for that needs to be earlier), | |
| 2389 | // but they could be full paths to .so files, in which case we | |
| 2390 | // want to avoid prepending "-l". | |
| 2391 | argv.appendAssumeCapacity(try lib_info.path.?.toString(arena)); | |
| 2392 | } | |
| 1985 | // By this time, we depend on these libs being dynamically linked | |
| 1986 | // libraries and not static libraries (the check for that needs to be earlier), | |
| 1987 | // but they could be full paths to .so files, in which case we | |
| 1988 | // want to avoid prepending "-l". | |
| 1989 | argv.appendAssumeCapacity(try dso.path.toString(arena)); | |
| 1990 | }, | |
| 1991 | }; | |
| 2393 | 1992 | |
| 2394 | 1993 | if (!as_needed) { |
| 2395 | 1994 | argv.appendAssumeCapacity("--as-needed"); |
| ... | ... | @@ -2421,7 +2020,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s |
| 2421 | 2020 | if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue; |
| 2422 | 2021 | } |
| 2423 | 2022 | |
| 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 | 2024 | comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, |
| 2426 | 2025 | }); |
| 2427 | 2026 | try argv.append(lib_path); |
| ... | ... | @@ -3469,8 +3068,14 @@ pub fn sortShdrs( |
| 3469 | 3068 | }; |
| 3470 | 3069 | |
| 3471 | 3070 | pub fn lessThan(ctx: Context, lhs: @This(), rhs: @This()) bool { |
| 3472 | return shdrRank(ctx.shdrs[lhs.shndx], ctx.shstrtab) < | |
| 3473 | shdrRank(ctx.shdrs[rhs.shndx], ctx.shstrtab); | |
| 3071 | const lhs_rank = shdrRank(ctx.shdrs[lhs.shndx], ctx.shstrtab); | |
| 3072 | const rhs_rank = shdrRank(ctx.shdrs[rhs.shndx], ctx.shstrtab); | |
| 3073 | if (lhs_rank == rhs_rank) { | |
| 3074 | const lhs_name = shString(ctx.shstrtab, ctx.shdrs[lhs.shndx].sh_name); | |
| 3075 | const rhs_name = shString(ctx.shstrtab, ctx.shdrs[rhs.shndx].sh_name); | |
| 3076 | return std.mem.lessThan(u8, lhs_name, rhs_name); | |
| 3077 | } | |
| 3078 | return lhs_rank < rhs_rank; | |
| 3474 | 3079 | } |
| 3475 | 3080 | }; |
| 3476 | 3081 | |
| ... | ... | @@ -3486,7 +3091,7 @@ pub fn sortShdrs( |
| 3486 | 3091 | .shdrs = shdrs, |
| 3487 | 3092 | .shstrtab = shstrtab, |
| 3488 | 3093 | }; |
| 3489 | mem.sort(Entry, entries, sort_context, Entry.lessThan); | |
| 3094 | mem.sortUnstable(Entry, entries, sort_context, Entry.lessThan); | |
| 3490 | 3095 | |
| 3491 | 3096 | const backlinks = try gpa.alloc(u32, entries.len); |
| 3492 | 3097 | defer gpa.free(backlinks); |
| ... | ... | @@ -3515,7 +3120,7 @@ pub fn sortShdrs( |
| 3515 | 3120 | for (slice.items(.shdr), slice.items(.atom_list_2)) |*shdr, *atom_list| { |
| 3516 | 3121 | atom_list.output_section_index = backlinks[atom_list.output_section_index]; |
| 3517 | 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 | 3125 | if (shdr.sh_type == elf.SHT_RELA) { |
| 3521 | 3126 | // 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 | 4350 | } |
| 4746 | 4351 | |
| 4747 | 4352 | pub 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 | } |
| 4750 | 4355 | |
| 4751 | fn fileLookup(files: std.MultiArrayList(File.Entry), index: File.Index) ?File { | |
| 4356 | fn fileLookup(files: std.MultiArrayList(File.Entry), index: File.Index, zig_object: ?*ZigObject) ?File { | |
| 4752 | 4357 | const tag = files.items(.tags)[index]; |
| 4753 | 4358 | return switch (tag) { |
| 4754 | 4359 | .null => null, |
| 4755 | 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 | 4362 | .object => .{ .object = &files.items(.data)[index].object }, |
| 4758 | 4363 | .shared_object => .{ .shared_object = &files.items(.data)[index].shared_object }, |
| 4759 | 4364 | }; |
| 4760 | 4365 | } |
| 4761 | 4366 | |
| 4762 | pub fn addFileHandle(self: *Elf, handle: fs.File) !File.HandleIndex { | |
| 4763 | const gpa = self.base.comp.gpa; | |
| 4764 | const index: File.HandleIndex = @intCast(self.file_handles.items.len); | |
| 4765 | const fh = try self.file_handles.addOne(gpa); | |
| 4766 | fh.* = handle; | |
| 4767 | return index; | |
| 4367 | pub fn addFileHandle( | |
| 4368 | gpa: Allocator, | |
| 4369 | file_handles: *std.ArrayListUnmanaged(File.Handle), | |
| 4370 | handle: fs.File, | |
| 4371 | ) Allocator.Error!File.HandleIndex { | |
| 4372 | try file_handles.append(gpa, handle); | |
| 4373 | return @intCast(file_handles.items.len - 1); | |
| 4768 | 4374 | } |
| 4769 | 4375 | |
| 4770 | 4376 | pub fn fileHandle(self: Elf, index: File.HandleIndex) File.Handle { |
| 4771 | assert(index < self.file_handles.items.len); | |
| 4772 | 4377 | return self.file_handles.items[index]; |
| 4773 | 4378 | } |
| 4774 | 4379 | |
| ... | ... | @@ -4791,8 +4396,7 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 |
| 4791 | 4396 | } |
| 4792 | 4397 | |
| 4793 | 4398 | pub fn zigObjectPtr(self: *Elf) ?*ZigObject { |
| 4794 | const index = self.zig_object_index orelse return null; | |
| 4795 | return self.file(index).?.zig_object; | |
| 4399 | return self.zig_object; | |
| 4796 | 4400 | } |
| 4797 | 4401 | |
| 4798 | 4402 | pub fn linkerDefinedPtr(self: *Elf) ?*LinkerDefined { |
| ... | ... | @@ -4870,7 +4474,7 @@ fn shString( |
| 4870 | 4474 | off: u32, |
| 4871 | 4475 | ) [:0]const u8 { |
| 4872 | 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 | } |
| 4875 | 4479 | |
| 4876 | 4480 | pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 { |
| ... | ... | @@ -5628,7 +5232,6 @@ const GnuHashSection = synthetic_sections.GnuHashSection; |
| 5628 | 5232 | const GotSection = synthetic_sections.GotSection; |
| 5629 | 5233 | const GotPltSection = synthetic_sections.GotPltSection; |
| 5630 | 5234 | const HashSection = synthetic_sections.HashSection; |
| 5631 | const LdScript = @import("Elf/LdScript.zig"); | |
| 5632 | 5235 | const LinkerDefined = @import("Elf/LinkerDefined.zig"); |
| 5633 | 5236 | const Liveness = @import("../Liveness.zig"); |
| 5634 | 5237 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| ... | ... | @@ -5644,4 +5247,3 @@ const Thunk = @import("Elf/Thunk.zig"); |
| 5644 | 5247 | const Value = @import("../Value.zig"); |
| 5645 | 5248 | const VerneedSection = synthetic_sections.VerneedSection; |
| 5646 | 5249 | const ZigObject = @import("Elf/ZigObject.zig"); |
| 5647 | const riscv = @import("riscv.zig"); |
src/link/Elf/Archive.zig+50-28| ... | ... | @@ -1,29 +1,46 @@ |
| 1 | objects: std.ArrayListUnmanaged(Object) = .empty, | |
| 2 | strtab: std.ArrayListUnmanaged(u8) = .empty, | |
| 3 | ||
| 4 | pub fn deinit(self: *Archive, allocator: Allocator) void { | |
| 5 | self.objects.deinit(allocator); | |
| 6 | self.strtab.deinit(allocator); | |
| 1 | objects: []const Object, | |
| 2 | /// '\n'-delimited | |
| 3 | strtab: []const u8, | |
| 4 | ||
| 5 | pub fn deinit(a: *Archive, gpa: Allocator) void { | |
| 6 | gpa.free(a.objects); | |
| 7 | gpa.free(a.strtab); | |
| 8 | a.* = undefined; | |
| 7 | 9 | } |
| 8 | 10 | |
| 9 | pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.HandleIndex) !void { | |
| 10 | const comp = elf_file.base.comp; | |
| 11 | const gpa = comp.gpa; | |
| 12 | const diags = &comp.link_diags; | |
| 13 | const handle = elf_file.fileHandle(handle_index); | |
| 11 | pub fn parse( | |
| 12 | gpa: Allocator, | |
| 13 | diags: *Diags, | |
| 14 | file_handles: *const std.ArrayListUnmanaged(File.Handle), | |
| 15 | path: Path, | |
| 16 | handle_index: File.HandleIndex, | |
| 17 | ) !Archive { | |
| 18 | const handle = file_handles.items[handle_index]; | |
| 19 | var pos: usize = 0; | |
| 20 | { | |
| 21 | var magic_buffer: [elf.ARMAG.len]u8 = undefined; | |
| 22 | const n = try handle.preadAll(&magic_buffer, pos); | |
| 23 | if (n != magic_buffer.len) return error.BadMagic; | |
| 24 | if (!mem.eql(u8, &magic_buffer, elf.ARMAG)) return error.BadMagic; | |
| 25 | pos += magic_buffer.len; | |
| 26 | } | |
| 27 | ||
| 14 | 28 | const size = (try handle.stat()).size; |
| 15 | 29 | |
| 16 | var pos: usize = elf.ARMAG.len; | |
| 17 | while (true) { | |
| 18 | if (pos >= size) break; | |
| 19 | if (!mem.isAligned(pos, 2)) pos += 1; | |
| 30 | var objects: std.ArrayListUnmanaged(Object) = .empty; | |
| 31 | defer objects.deinit(gpa); | |
| 20 | 32 | |
| 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); | |
| 24 | if (amt != @sizeOf(elf.ar_hdr)) return error.InputOutput; | |
| 41 | const n = try handle.preadAll(mem.asBytes(&hdr), pos); | |
| 42 | if (n != @sizeOf(elf.ar_hdr)) return error.UnexpectedEndOfFile; | |
| 25 | 43 | } |
| 26 | const hdr = @as(*align(1) const elf.ar_hdr, @ptrCast(&hdr_buffer)).*; | |
| 27 | 44 | pos += @sizeOf(elf.ar_hdr); |
| 28 | 45 | |
| 29 | 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 | 54 | |
| 38 | 55 | if (hdr.isSymtab() or hdr.isSymtab64()) continue; |
| 39 | 56 | if (hdr.isStrtab()) { |
| 40 | try self.strtab.resize(gpa, obj_size); | |
| 41 | const amt = try handle.preadAll(self.strtab.items, pos); | |
| 57 | try strtab.resize(gpa, obj_size); | |
| 58 | const amt = try handle.preadAll(strtab.items, pos); | |
| 42 | 59 | if (amt != obj_size) return error.InputOutput; |
| 43 | 60 | continue; |
| 44 | 61 | } |
| ... | ... | @@ -47,7 +64,7 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.Hand |
| 47 | 64 | const name = if (hdr.name()) |name| |
| 48 | 65 | name |
| 49 | 66 | else if (try hdr.nameOffset()) |off| |
| 50 | self.getString(off) | |
| 67 | stringTableLookup(strtab.items, off) | |
| 51 | 68 | else |
| 52 | 69 | unreachable; |
| 53 | 70 | |
| ... | ... | @@ -70,14 +87,18 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.Hand |
| 70 | 87 | @as(Path, object.path), @as(Path, path), |
| 71 | 88 | }); |
| 72 | 89 | |
| 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 | } |
| 76 | 98 | |
| 77 | fn getString(self: Archive, off: u32) []const u8 { | |
| 78 | assert(off < self.strtab.items.len); | |
| 79 | const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.items.ptr + off)), 0); | |
| 80 | return name[0 .. name.len - 1]; | |
| 99 | pub fn stringTableLookup(strtab: []const u8, off: u32) [:'\n']const u8 { | |
| 100 | const slice = strtab[off..]; | |
| 101 | return slice[0..mem.indexOfScalar(u8, slice, '\n').? :'\n']; | |
| 81 | 102 | } |
| 82 | 103 | |
| 83 | 104 | pub fn setArHdr(opts: struct { |
| ... | ... | @@ -290,8 +311,9 @@ const fs = std.fs; |
| 290 | 311 | const log = std.log.scoped(.link); |
| 291 | 312 | const mem = std.mem; |
| 292 | 313 | const Path = std.Build.Cache.Path; |
| 314 | const Allocator = std.mem.Allocator; | |
| 293 | 315 | |
| 294 | const Allocator = mem.Allocator; | |
| 316 | const Diags = @import("../../link.zig").Diags; | |
| 295 | 317 | const Archive = @This(); |
| 296 | 318 | const Elf = @import("../Elf.zig"); |
| 297 | 319 | const File = @import("file.zig").File; |
src/link/Elf/Atom.zig+27-24| ... | ... | @@ -102,9 +102,13 @@ pub fn relocsShndx(self: Atom) ?u32 { |
| 102 | 102 | return self.relocs_section_index; |
| 103 | 103 | } |
| 104 | 104 | |
| 105 | pub fn priority(self: Atom, elf_file: *Elf) u64 { | |
| 106 | const index = self.file(elf_file).?.index(); | |
| 107 | return (@as(u64, @intCast(index)) << 32) | @as(u64, @intCast(self.input_section_index)); | |
| 105 | pub fn priority(atom: Atom, elf_file: *Elf) u64 { | |
| 106 | const index = atom.file(elf_file).?.index(); | |
| 107 | return priorityLookup(index, atom.input_section_index); | |
| 108 | } | |
| 109 | ||
| 110 | pub 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 | } |
| 109 | 113 | |
| 110 | 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 | 259 | } |
| 256 | 260 | } |
| 257 | 261 | |
| 258 | pub fn fdes(self: Atom, elf_file: *Elf) []Fde { | |
| 259 | const extras = self.extra(elf_file); | |
| 260 | return switch (self.file(elf_file).?) { | |
| 261 | .shared_object => unreachable, | |
| 262 | .linker_defined, .zig_object => &[0]Fde{}, | |
| 263 | .object => |x| x.fdes.items[extras.fde_start..][0..extras.fde_count], | |
| 264 | }; | |
| 262 | pub fn fdes(atom: Atom, object: *Object) []Fde { | |
| 263 | const extras = object.atomExtra(atom.extra_index); | |
| 264 | return object.fdes.items[extras.fde_start..][0..extras.fde_count]; | |
| 265 | 265 | } |
| 266 | 266 | |
| 267 | pub fn markFdesDead(self: Atom, elf_file: *Elf) void { | |
| 268 | for (self.fdes(elf_file)) |*fde| { | |
| 269 | fde.alive = false; | |
| 270 | } | |
| 267 | pub fn markFdesDead(self: Atom, object: *Object) void { | |
| 268 | for (self.fdes(object)) |*fde| fde.alive = false; | |
| 271 | 269 | } |
| 272 | 270 | |
| 273 | 271 | pub fn addReloc(self: Atom, alloc: Allocator, reloc: elf.Elf64_Rela, zo: *ZigObject) !void { |
| ... | ... | @@ -946,16 +944,21 @@ fn format2( |
| 946 | 944 | atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size, |
| 947 | 945 | atom.prev_atom_ref, atom.next_atom_ref, |
| 948 | 946 | }); |
| 949 | if (atom.fdes(elf_file).len > 0) { | |
| 950 | try writer.writeAll(" : fdes{ "); | |
| 951 | const extras = atom.extra(elf_file); | |
| 952 | for (atom.fdes(elf_file), extras.fde_start..) |fde, i| { | |
| 953 | try writer.print("{d}", .{i}); | |
| 954 | if (!fde.alive) try writer.writeAll("([*])"); | |
| 955 | if (i - extras.fde_start < extras.fde_count - 1) try writer.writeAll(", "); | |
| 956 | } | |
| 957 | try writer.writeAll(" }"); | |
| 958 | } | |
| 947 | if (atom.file(elf_file)) |atom_file| switch (atom_file) { | |
| 948 | .object => |object| { | |
| 949 | if (atom.fdes(object).len > 0) { | |
| 950 | try writer.writeAll(" : fdes{ "); | |
| 951 | const extras = atom.extra(elf_file); | |
| 952 | for (atom.fdes(object), extras.fde_start..) |fde, i| { | |
| 953 | try writer.print("{d}", .{i}); | |
| 954 | if (!fde.alive) try writer.writeAll("([*])"); | |
| 955 | if (i - extras.fde_start < extras.fde_count - 1) try writer.writeAll(", "); | |
| 956 | } | |
| 957 | try writer.writeAll(" }"); | |
| 958 | } | |
| 959 | }, | |
| 960 | else => {}, | |
| 961 | }; | |
| 959 | 962 | if (!atom.alive) { |
| 960 | 963 | try writer.writeAll(" : [*]"); |
| 961 | 964 | } |
src/link/Elf/LdScript.zig deleted-439| ... | ... | @@ -1,439 +0,0 @@ |
| 1 | path: Path, | |
| 2 | cpu_arch: ?std.Target.Cpu.Arch = null, | |
| 3 | args: std.ArrayListUnmanaged(Arg) = .empty, | |
| 4 | ||
| 5 | pub const Arg = struct { | |
| 6 | needed: bool = false, | |
| 7 | path: []const u8, | |
| 8 | }; | |
| 9 | ||
| 10 | pub fn deinit(scr: *LdScript, allocator: Allocator) void { | |
| 11 | scr.args.deinit(allocator); | |
| 12 | } | |
| 13 | ||
| 14 | pub const Error = error{ | |
| 15 | LinkFailure, | |
| 16 | UnexpectedToken, | |
| 17 | UnknownCpuArch, | |
| 18 | OutOfMemory, | |
| 19 | }; | |
| 20 | ||
| 21 | pub 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 | ||
| 78 | fn 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 | ||
| 102 | const LineColumn = struct { | |
| 103 | line: usize, | |
| 104 | column: usize, | |
| 105 | }; | |
| 106 | ||
| 107 | const 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 | ||
| 128 | const 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 | ||
| 224 | const 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 | ||
| 254 | const 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 | ||
| 394 | const 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 | ||
| 432 | const LdScript = @This(); | |
| 433 | ||
| 434 | const std = @import("std"); | |
| 435 | const assert = std.debug.assert; | |
| 436 | const Path = std.Build.Cache.Path; | |
| 437 | ||
| 438 | const Allocator = std.mem.Allocator; | |
| 439 | const Elf = @import("../Elf.zig"); |
src/link/Elf/Object.zig+190-131| ... | ... | @@ -37,72 +37,84 @@ num_dynrelocs: u32 = 0, |
| 37 | 37 | output_symtab_ctx: Elf.SymtabCtx = .{}, |
| 38 | 38 | output_ar_state: Archive.ArState = .{}, |
| 39 | 39 | |
| 40 | pub fn deinit(self: *Object, allocator: Allocator) void { | |
| 41 | if (self.archive) |*ar| allocator.free(ar.path.sub_path); | |
| 42 | allocator.free(self.path.sub_path); | |
| 43 | self.shdrs.deinit(allocator); | |
| 44 | self.symtab.deinit(allocator); | |
| 45 | self.strtab.deinit(allocator); | |
| 46 | self.symbols.deinit(allocator); | |
| 47 | self.symbols_extra.deinit(allocator); | |
| 48 | self.symbols_resolver.deinit(allocator); | |
| 49 | self.atoms.deinit(allocator); | |
| 50 | self.atoms_indexes.deinit(allocator); | |
| 51 | self.atoms_extra.deinit(allocator); | |
| 52 | self.comdat_groups.deinit(allocator); | |
| 53 | self.comdat_group_data.deinit(allocator); | |
| 54 | self.relocs.deinit(allocator); | |
| 55 | self.fdes.deinit(allocator); | |
| 56 | self.cies.deinit(allocator); | |
| 57 | self.eh_frame_data.deinit(allocator); | |
| 40 | pub fn deinit(self: *Object, gpa: Allocator) void { | |
| 41 | if (self.archive) |*ar| gpa.free(ar.path.sub_path); | |
| 42 | gpa.free(self.path.sub_path); | |
| 43 | self.shdrs.deinit(gpa); | |
| 44 | self.symtab.deinit(gpa); | |
| 45 | self.strtab.deinit(gpa); | |
| 46 | self.symbols.deinit(gpa); | |
| 47 | self.symbols_extra.deinit(gpa); | |
| 48 | self.symbols_resolver.deinit(gpa); | |
| 49 | self.atoms.deinit(gpa); | |
| 50 | self.atoms_indexes.deinit(gpa); | |
| 51 | self.atoms_extra.deinit(gpa); | |
| 52 | self.comdat_groups.deinit(gpa); | |
| 53 | self.comdat_group_data.deinit(gpa); | |
| 54 | self.relocs.deinit(gpa); | |
| 55 | self.fdes.deinit(gpa); | |
| 56 | self.cies.deinit(gpa); | |
| 57 | self.eh_frame_data.deinit(gpa); | |
| 58 | 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); | |
| 62 | self.input_merge_sections_indexes.deinit(allocator); | |
| 61 | self.input_merge_sections.deinit(gpa); | |
| 62 | self.input_merge_sections_indexes.deinit(gpa); | |
| 63 | 63 | } |
| 64 | 64 | |
| 65 | pub fn parse(self: *Object, elf_file: *Elf) !void { | |
| 66 | const gpa = elf_file.base.comp.gpa; | |
| 67 | const cpu_arch = elf_file.getTarget().cpu.arch; | |
| 68 | const handle = elf_file.fileHandle(self.file_handle); | |
| 69 | ||
| 70 | try self.parseCommon(gpa, handle, elf_file); | |
| 71 | ||
| 65 | pub fn parse( | |
| 66 | self: *Object, | |
| 67 | gpa: Allocator, | |
| 68 | diags: *Diags, | |
| 69 | /// For error reporting purposes only. | |
| 70 | path: Path, | |
| 71 | handle: fs.File, | |
| 72 | target: std.Target, | |
| 73 | debug_fmt_strip: bool, | |
| 74 | default_sym_version: elf.Versym, | |
| 75 | ) !void { | |
| 72 | 76 | // Append null input merge section |
| 73 | 77 | try self.input_merge_sections.append(gpa, .{}); |
| 74 | 78 | // Allocate atom index 0 to null atom |
| 75 | 79 | try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) }); |
| 76 | 80 | |
| 77 | try self.initAtoms(gpa, handle, elf_file); | |
| 78 | try self.initSymbols(gpa, elf_file); | |
| 81 | try self.initAtoms(gpa, diags, path, handle, debug_fmt_strip, target); | |
| 82 | try self.initSymbols(gpa, default_sym_version); | |
| 79 | 83 | |
| 80 | 84 | for (self.shdrs.items, 0..) |shdr, i| { |
| 81 | 85 | const atom_ptr = self.atom(self.atoms_indexes.items[i]) orelse continue; |
| 82 | 86 | if (!atom_ptr.alive) continue; |
| 83 | if ((cpu_arch == .x86_64 and shdr.sh_type == elf.SHT_X86_64_UNWIND) or | |
| 84 | mem.eql(u8, atom_ptr.name(elf_file), ".eh_frame")) | |
| 87 | if ((target.cpu.arch == .x86_64 and shdr.sh_type == elf.SHT_X86_64_UNWIND) or | |
| 88 | mem.eql(u8, self.getString(atom_ptr.name_offset), ".eh_frame")) | |
| 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 | } |
| 90 | 94 | |
| 91 | fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void { | |
| 95 | pub 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 | 104 | const offset = if (self.archive) |ar| ar.offset else 0; |
| 93 | 105 | const file_size = (try handle.stat()).size; |
| 94 | 106 | |
| 95 | const header_buffer = try Elf.preadAllAlloc(allocator, handle, offset, @sizeOf(elf.Elf64_Ehdr)); | |
| 96 | defer allocator.free(header_buffer); | |
| 107 | const header_buffer = try Elf.preadAllAlloc(gpa, handle, offset, @sizeOf(elf.Elf64_Ehdr)); | |
| 108 | defer gpa.free(header_buffer); | |
| 97 | 109 | self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*; |
| 98 | 110 | |
| 99 | const em = elf_file.base.comp.root_mod.resolved_target.result.toElfMachine(); | |
| 111 | const em = target.toElfMachine(); | |
| 100 | 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 | 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); | |
| 106 | 118 | |
| 107 | 119 | if (self.header.?.e_shnum == 0) return; |
| 108 | 120 | |
| ... | ... | @@ -110,30 +122,30 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil |
| 110 | 122 | const shnum = math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow; |
| 111 | 123 | const shsize = shnum * @sizeOf(elf.Elf64_Shdr); |
| 112 | 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 | } |
| 115 | 127 | |
| 116 | const shdrs_buffer = try Elf.preadAllAlloc(allocator, handle, offset + shoff, shsize); | |
| 117 | defer allocator.free(shdrs_buffer); | |
| 128 | const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, offset + shoff, shsize); | |
| 129 | defer gpa.free(shdrs_buffer); | |
| 118 | 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); | |
| 120 | 132 | |
| 121 | 133 | for (self.shdrs.items) |shdr| { |
| 122 | 134 | if (shdr.sh_type != elf.SHT_NOBITS) { |
| 123 | 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 | } |
| 128 | 140 | |
| 129 | const shstrtab = try self.preadShdrContentsAlloc(allocator, handle, self.header.?.e_shstrndx); | |
| 130 | defer allocator.free(shstrtab); | |
| 141 | const shstrtab = try self.preadShdrContentsAlloc(gpa, handle, self.header.?.e_shstrndx); | |
| 142 | defer gpa.free(shstrtab); | |
| 131 | 143 | for (self.shdrs.items) |shdr| { |
| 132 | 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); | |
| 137 | 149 | |
| 138 | 150 | const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { |
| 139 | 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 | 156 | const shdr = self.shdrs.items[index]; |
| 145 | 157 | self.first_global = shdr.sh_info; |
| 146 | 158 | |
| 147 | const raw_symtab = try self.preadShdrContentsAlloc(allocator, handle, index); | |
| 148 | defer allocator.free(raw_symtab); | |
| 159 | const raw_symtab = try self.preadShdrContentsAlloc(gpa, handle, index); | |
| 160 | defer gpa.free(raw_symtab); | |
| 149 | 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 | 164 | const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms]; |
| 153 | 165 | |
| 154 | 166 | const strtab_bias = @as(u32, @intCast(self.strtab.items.len)); |
| 155 | const strtab = try self.preadShdrContentsAlloc(allocator, handle, shdr.sh_link); | |
| 156 | defer allocator.free(strtab); | |
| 157 | try self.strtab.appendSlice(allocator, strtab); | |
| 167 | const strtab = try self.preadShdrContentsAlloc(gpa, handle, shdr.sh_link); | |
| 168 | defer gpa.free(strtab); | |
| 169 | try self.strtab.appendSlice(gpa, strtab); | |
| 158 | 170 | |
| 159 | try self.symtab.ensureUnusedCapacity(allocator, symtab.len); | |
| 171 | try self.symtab.ensureUnusedCapacity(gpa, symtab.len); | |
| 160 | 172 | for (symtab) |sym| { |
| 161 | 173 | const out_sym = self.symtab.addOneAssumeCapacity(); |
| 162 | 174 | out_sym.* = sym; |
| ... | ... | @@ -168,15 +180,56 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil |
| 168 | 180 | } |
| 169 | 181 | } |
| 170 | 182 | |
| 171 | fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void { | |
| 172 | const comp = elf_file.base.comp; | |
| 173 | const debug_fmt_strip = comp.config.debug_format == .strip; | |
| 174 | const target = comp.root_mod.resolved_target.result; | |
| 183 | fn validateEFlags( | |
| 184 | diags: *Diags, | |
| 185 | path: Path, | |
| 186 | target: std.Target, | |
| 187 | e_flags: elf.Word, | |
| 188 | first_eflags: *?elf.Word, | |
| 189 | ) error{LinkFailure}!void { | |
| 190 | if (first_eflags.*) |*self_eflags| { | |
| 191 | switch (target.cpu.arch) { | |
| 192 | .riscv64 => { | |
| 193 | if (e_flags != self_eflags.*) { | |
| 194 | const riscv_eflags: riscv.RiscvEflags = @bitCast(e_flags); | |
| 195 | const self_riscv_eflags: *riscv.RiscvEflags = @ptrCast(self_eflags); | |
| 196 | ||
| 197 | self_riscv_eflags.rvc = self_riscv_eflags.rvc or riscv_eflags.rvc; | |
| 198 | self_riscv_eflags.tso = self_riscv_eflags.tso or riscv_eflags.tso; | |
| 199 | ||
| 200 | var any_errors: bool = false; | |
| 201 | if (self_riscv_eflags.fabi != riscv_eflags.fabi) { | |
| 202 | any_errors = true; | |
| 203 | diags.addParseError(path, "cannot link object files with different float-point ABIs", .{}); | |
| 204 | } | |
| 205 | if (self_riscv_eflags.rve != riscv_eflags.rve) { | |
| 206 | any_errors = true; | |
| 207 | diags.addParseError(path, "cannot link object files with different RVEs", .{}); | |
| 208 | } | |
| 209 | if (any_errors) return error.LinkFailure; | |
| 210 | } | |
| 211 | }, | |
| 212 | else => {}, | |
| 213 | } | |
| 214 | } else { | |
| 215 | first_eflags.* = e_flags; | |
| 216 | } | |
| 217 | } | |
| 218 | ||
| 219 | fn 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 | 228 | const shdrs = self.shdrs.items; |
| 176 | try self.atoms.ensureTotalCapacityPrecise(allocator, shdrs.len); | |
| 177 | try self.atoms_extra.ensureTotalCapacityPrecise(allocator, shdrs.len * @sizeOf(Atom.Extra)); | |
| 178 | try self.atoms_indexes.ensureTotalCapacityPrecise(allocator, shdrs.len); | |
| 179 | try self.atoms_indexes.resize(allocator, shdrs.len); | |
| 229 | try self.atoms.ensureTotalCapacityPrecise(gpa, shdrs.len); | |
| 230 | try self.atoms_extra.ensureTotalCapacityPrecise(gpa, shdrs.len * @sizeOf(Atom.Extra)); | |
| 231 | try self.atoms_indexes.ensureTotalCapacityPrecise(gpa, shdrs.len); | |
| 232 | try self.atoms_indexes.resize(gpa, shdrs.len); | |
| 180 | 233 | @memset(self.atoms_indexes.items, 0); |
| 181 | 234 | |
| 182 | 235 | for (shdrs, 0..) |shdr, i| { |
| ... | ... | @@ -201,24 +254,24 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: |
| 201 | 254 | }; |
| 202 | 255 | |
| 203 | 256 | const shndx: u32 = @intCast(i); |
| 204 | const group_raw_data = try self.preadShdrContentsAlloc(allocator, handle, shndx); | |
| 205 | defer allocator.free(group_raw_data); | |
| 257 | const group_raw_data = try self.preadShdrContentsAlloc(gpa, handle, shndx); | |
| 258 | defer gpa.free(group_raw_data); | |
| 206 | 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 | 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 | 265 | const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers]; |
| 213 | 266 | |
| 214 | 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 | } |
| 217 | 270 | |
| 218 | 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..]); | |
| 220 | 273 | |
| 221 | const comdat_group_index = try self.addComdatGroup(allocator); | |
| 274 | const comdat_group_index = try self.addComdatGroup(gpa); | |
| 222 | 275 | const comdat_group = self.comdatGroup(comdat_group_index); |
| 223 | 276 | comdat_group.* = .{ |
| 224 | 277 | .signature_off = group_signature, |
| ... | ... | @@ -242,8 +295,8 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: |
| 242 | 295 | const shndx: u32 = @intCast(i); |
| 243 | 296 | if (self.skipShdr(shndx, debug_fmt_strip)) continue; |
| 244 | 297 | const size, const alignment = if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) blk: { |
| 245 | const data = try self.preadShdrContentsAlloc(allocator, handle, shndx); | |
| 246 | defer allocator.free(data); | |
| 298 | const data = try self.preadShdrContentsAlloc(gpa, handle, shndx); | |
| 299 | defer gpa.free(data); | |
| 247 | 300 | const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; |
| 248 | 301 | break :blk .{ chdr.ch_size, Alignment.fromNonzeroByteUnits(chdr.ch_addralign) }; |
| 249 | 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 | 316 | elf.SHT_REL, elf.SHT_RELA => { |
| 264 | 317 | const atom_index = self.atoms_indexes.items[shdr.sh_info]; |
| 265 | 318 | if (self.atom(atom_index)) |atom_ptr| { |
| 266 | const relocs = try self.preadRelocsAlloc(allocator, handle, @intCast(i)); | |
| 267 | defer allocator.free(relocs); | |
| 319 | const relocs = try self.preadRelocsAlloc(gpa, handle, @intCast(i)); | |
| 320 | defer gpa.free(relocs); | |
| 268 | 321 | atom_ptr.relocs_section_index = @intCast(i); |
| 269 | 322 | const rel_index: u32 = @intCast(self.relocs.items.len); |
| 270 | 323 | const rel_count: u32 = @intCast(relocs.len); |
| 271 | 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 | 326 | if (target.cpu.arch == .riscv64) { |
| 274 | 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 | 346 | return ignore; |
| 294 | 347 | } |
| 295 | 348 | |
| 296 | fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void { | |
| 349 | fn initSymbols( | |
| 350 | self: *Object, | |
| 351 | gpa: Allocator, | |
| 352 | default_sym_version: elf.Versym, | |
| 353 | ) !void { | |
| 297 | 354 | const first_global = self.first_global orelse self.symtab.items.len; |
| 298 | 355 | const nglobals = self.symtab.items.len - first_global; |
| 299 | 356 | |
| 300 | try self.symbols.ensureTotalCapacityPrecise(allocator, self.symtab.items.len); | |
| 301 | try self.symbols_extra.ensureTotalCapacityPrecise(allocator, self.symtab.items.len * @sizeOf(Symbol.Extra)); | |
| 302 | try self.symbols_resolver.ensureTotalCapacityPrecise(allocator, nglobals); | |
| 303 | self.symbols_resolver.resize(allocator, nglobals) catch unreachable; | |
| 357 | try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.items.len); | |
| 358 | try self.symbols_extra.ensureTotalCapacityPrecise(gpa, self.symtab.items.len * @sizeOf(Symbol.Extra)); | |
| 359 | try self.symbols_resolver.ensureTotalCapacityPrecise(gpa, nglobals); | |
| 360 | self.symbols_resolver.resize(gpa, nglobals) catch unreachable; | |
| 304 | 361 | @memset(self.symbols_resolver.items, 0); |
| 305 | 362 | |
| 306 | 363 | for (self.symtab.items, 0..) |sym, i| { |
| ... | ... | @@ -310,7 +367,7 @@ fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void { |
| 310 | 367 | sym_ptr.name_offset = sym.st_name; |
| 311 | 368 | sym_ptr.esym_index = @intCast(i); |
| 312 | 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 | 371 | sym_ptr.flags.weak = sym.st_bind() == elf.STB_WEAK; |
| 315 | 372 | if (sym.st_shndx != elf.SHN_ABS and sym.st_shndx != elf.SHN_COMMON) { |
| 316 | 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 | 375 | } |
| 319 | 376 | } |
| 320 | 377 | |
| 321 | fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx: u32, elf_file: *Elf) !void { | |
| 378 | fn parseEhFrame( | |
| 379 | self: *Object, | |
| 380 | gpa: Allocator, | |
| 381 | handle: fs.File, | |
| 382 | shndx: u32, | |
| 383 | target: std.Target, | |
| 384 | ) !void { | |
| 322 | 385 | const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { |
| 323 | 386 | elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u32, @intCast(i)), |
| 324 | 387 | else => {}, |
| 325 | 388 | } else null; |
| 326 | 389 | |
| 327 | const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx); | |
| 328 | defer allocator.free(raw); | |
| 329 | const data_start = @as(u32, @intCast(self.eh_frame_data.items.len)); | |
| 330 | try self.eh_frame_data.appendSlice(allocator, raw); | |
| 390 | const raw = try self.preadShdrContentsAlloc(gpa, handle, shndx); | |
| 391 | defer gpa.free(raw); | |
| 392 | const data_start: u32 = @intCast(self.eh_frame_data.items.len); | |
| 393 | try self.eh_frame_data.appendSlice(gpa, raw); | |
| 331 | 394 | const relocs = if (relocs_shndx) |index| |
| 332 | try self.preadRelocsAlloc(allocator, handle, index) | |
| 395 | try self.preadRelocsAlloc(gpa, handle, index) | |
| 333 | 396 | else |
| 334 | 397 | &[0]elf.Elf64_Rela{}; |
| 335 | defer allocator.free(relocs); | |
| 336 | const rel_start = @as(u32, @intCast(self.relocs.items.len)); | |
| 337 | try self.relocs.appendUnalignedSlice(allocator, relocs); | |
| 338 | if (elf_file.getTarget().cpu.arch == .riscv64) { | |
| 398 | defer gpa.free(relocs); | |
| 399 | const rel_start: u32 = @intCast(self.relocs.items.len); | |
| 400 | try self.relocs.appendUnalignedSlice(gpa, relocs); | |
| 401 | if (target.cpu.arch == .riscv64) { | |
| 339 | 402 | sortRelocs(self.relocs.items[rel_start..][0..relocs.len]); |
| 340 | 403 | } |
| 341 | 404 | const fdes_start = self.fdes.items.len; |
| ... | ... | @@ -345,11 +408,11 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx: |
| 345 | 408 | while (try it.next()) |rec| { |
| 346 | 409 | const rel_range = filterRelocs(self.relocs.items[rel_start..][0..relocs.len], rec.offset, rec.size + 4); |
| 347 | 410 | switch (rec.tag) { |
| 348 | .cie => try self.cies.append(allocator, .{ | |
| 411 | .cie => try self.cies.append(gpa, .{ | |
| 349 | 412 | .offset = data_start + rec.offset, |
| 350 | 413 | .size = rec.size, |
| 351 | 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 | 416 | .input_section_index = shndx, |
| 354 | 417 | .file_index = self.index, |
| 355 | 418 | }), |
| ... | ... | @@ -361,12 +424,12 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx: |
| 361 | 424 | // this can happen for object files built with -r flag by the linker. |
| 362 | 425 | continue; |
| 363 | 426 | } |
| 364 | try self.fdes.append(allocator, .{ | |
| 427 | try self.fdes.append(gpa, .{ | |
| 365 | 428 | .offset = data_start + rec.offset, |
| 366 | 429 | .size = rec.size, |
| 367 | 430 | .cie_index = undefined, |
| 368 | 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 | 433 | .input_section_index = shndx, |
| 371 | 434 | .file_index = self.index, |
| 372 | 435 | }); |
| ... | ... | @@ -376,7 +439,7 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx: |
| 376 | 439 | |
| 377 | 440 | // Tie each FDE to its CIE |
| 378 | 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 | 443 | const cie_index = for (self.cies.items[cies_start..], cies_start..) |cie, cie_index| { |
| 381 | 444 | if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index)); |
| 382 | 445 | } else { |
| ... | ... | @@ -392,26 +455,26 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx: |
| 392 | 455 | |
| 393 | 456 | // Tie each FDE record to its matching atom |
| 394 | 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 | 459 | const lhs_atom = lhs.atom(ctx); |
| 397 | 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); | |
| 402 | 465 | |
| 403 | 466 | // 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 | 468 | while (i < self.fdes.items.len) { |
| 406 | 469 | const fde = self.fdes.items[i]; |
| 407 | const atom_ptr = fde.atom(elf_file); | |
| 470 | const atom_ptr = fde.atom(self); | |
| 408 | 471 | const start = i; |
| 409 | 472 | i += 1; |
| 410 | 473 | while (i < self.fdes.items.len) : (i += 1) { |
| 411 | 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 | } |
| 417 | 480 | |
| ... | ... | @@ -904,7 +967,7 @@ pub fn markComdatGroupsDead(self: *Object, elf_file: *Elf) void { |
| 904 | 967 | const atom_index = self.atoms_indexes.items[shndx]; |
| 905 | 968 | if (self.atom(atom_index)) |atom_ptr| { |
| 906 | 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 | 1033 | } |
| 971 | 1034 | } |
| 972 | 1035 | |
| 973 | pub 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 | ||
| 979 | 1036 | pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { |
| 980 | 1037 | const comp = elf_file.base.comp; |
| 981 | 1038 | const gpa = comp.gpa; |
| ... | ... | @@ -1000,7 +1057,7 @@ pub fn updateArSize(self: *Object, elf_file: *Elf) !void { |
| 1000 | 1057 | pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void { |
| 1001 | 1058 | const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; |
| 1002 | 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 | 1061 | const hdr = Archive.setArHdr(.{ |
| 1005 | 1062 | .name = if (name.len <= Archive.max_member_name_len) |
| 1006 | 1063 | .{ .name = name } |
| ... | ... | @@ -1136,8 +1193,8 @@ pub fn resolveSymbol(self: Object, index: Symbol.Index, elf_file: *Elf) Elf.Ref |
| 1136 | 1193 | return elf_file.resolver.get(resolv).?; |
| 1137 | 1194 | } |
| 1138 | 1195 | |
| 1139 | fn addSymbol(self: *Object, allocator: Allocator) !Symbol.Index { | |
| 1140 | try self.symbols.ensureUnusedCapacity(allocator, 1); | |
| 1196 | fn addSymbol(self: *Object, gpa: Allocator) !Symbol.Index { | |
| 1197 | try self.symbols.ensureUnusedCapacity(gpa, 1); | |
| 1141 | 1198 | return self.addSymbolAssumeCapacity(); |
| 1142 | 1199 | } |
| 1143 | 1200 | |
| ... | ... | @@ -1147,9 +1204,9 @@ fn addSymbolAssumeCapacity(self: *Object) Symbol.Index { |
| 1147 | 1204 | return index; |
| 1148 | 1205 | } |
| 1149 | 1206 | |
| 1150 | pub fn addSymbolExtra(self: *Object, allocator: Allocator, extra: Symbol.Extra) !u32 { | |
| 1207 | pub fn addSymbolExtra(self: *Object, gpa: Allocator, extra: Symbol.Extra) !u32 { | |
| 1151 | 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 | 1210 | return self.addSymbolExtraAssumeCapacity(extra); |
| 1154 | 1211 | } |
| 1155 | 1212 | |
| ... | ... | @@ -1198,27 +1255,27 @@ pub fn getString(self: Object, off: u32) [:0]const u8 { |
| 1198 | 1255 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); |
| 1199 | 1256 | } |
| 1200 | 1257 | |
| 1201 | fn addString(self: *Object, allocator: Allocator, str: []const u8) !u32 { | |
| 1258 | fn addString(self: *Object, gpa: Allocator, str: []const u8) !u32 { | |
| 1202 | 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 | 1261 | self.strtab.appendSliceAssumeCapacity(str); |
| 1205 | 1262 | self.strtab.appendAssumeCapacity(0); |
| 1206 | 1263 | return off; |
| 1207 | 1264 | } |
| 1208 | 1265 | |
| 1209 | 1266 | /// Caller owns the memory. |
| 1210 | fn preadShdrContentsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, index: u32) ![]u8 { | |
| 1267 | fn preadShdrContentsAlloc(self: Object, gpa: Allocator, handle: fs.File, index: u32) ![]u8 { | |
| 1211 | 1268 | assert(index < self.shdrs.items.len); |
| 1212 | 1269 | const offset = if (self.archive) |ar| ar.offset else 0; |
| 1213 | 1270 | const shdr = self.shdrs.items[index]; |
| 1214 | 1271 | const sh_offset = math.cast(u64, shdr.sh_offset) orelse return error.Overflow; |
| 1215 | 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 | } |
| 1218 | 1275 | |
| 1219 | 1276 | /// Caller owns the memory. |
| 1220 | fn preadRelocsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, shndx: u32) ![]align(1) const elf.Elf64_Rela { | |
| 1221 | const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx); | |
| 1277 | fn preadRelocsAlloc(self: Object, gpa: Allocator, handle: fs.File, shndx: u32) ![]align(1) const elf.Elf64_Rela { | |
| 1278 | const raw = try self.preadShdrContentsAlloc(gpa, handle, shndx); | |
| 1222 | 1279 | const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela)); |
| 1223 | 1280 | return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num]; |
| 1224 | 1281 | } |
| ... | ... | @@ -1230,9 +1287,9 @@ const AddAtomArgs = struct { |
| 1230 | 1287 | alignment: Alignment, |
| 1231 | 1288 | }; |
| 1232 | 1289 | |
| 1233 | fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index { | |
| 1234 | try self.atoms.ensureUnusedCapacity(allocator, 1); | |
| 1235 | try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra)); | |
| 1290 | fn addAtom(self: *Object, gpa: Allocator, args: AddAtomArgs) !Atom.Index { | |
| 1291 | try self.atoms.ensureUnusedCapacity(gpa, 1); | |
| 1292 | try self.atoms_extra.ensureUnusedCapacity(gpa, @sizeOf(Atom.Extra)); | |
| 1236 | 1293 | return self.addAtomAssumeCapacity(args); |
| 1237 | 1294 | } |
| 1238 | 1295 | |
| ... | ... | @@ -1257,9 +1314,9 @@ pub fn atom(self: *Object, atom_index: Atom.Index) ?*Atom { |
| 1257 | 1314 | return &self.atoms.items[atom_index]; |
| 1258 | 1315 | } |
| 1259 | 1316 | |
| 1260 | pub fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 { | |
| 1317 | pub fn addAtomExtra(self: *Object, gpa: Allocator, extra: Atom.Extra) !u32 { | |
| 1261 | 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 | 1320 | return self.addAtomExtraAssumeCapacity(extra); |
| 1264 | 1321 | } |
| 1265 | 1322 | |
| ... | ... | @@ -1308,9 +1365,9 @@ fn setAtomFields(o: *Object, atom_ptr: *Atom, opts: Atom.Extra.AsOptionals) void |
| 1308 | 1365 | o.setAtomExtra(atom_ptr.extra_index, extras); |
| 1309 | 1366 | } |
| 1310 | 1367 | |
| 1311 | fn addInputMergeSection(self: *Object, allocator: Allocator) !Merge.InputSection.Index { | |
| 1368 | fn addInputMergeSection(self: *Object, gpa: Allocator) !Merge.InputSection.Index { | |
| 1312 | 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 | 1371 | msec.* = .{}; |
| 1315 | 1372 | return index; |
| 1316 | 1373 | } |
| ... | ... | @@ -1320,9 +1377,9 @@ fn inputMergeSection(self: *Object, index: Merge.InputSection.Index) ?*Merge.Inp |
| 1320 | 1377 | return &self.input_merge_sections.items[index]; |
| 1321 | 1378 | } |
| 1322 | 1379 | |
| 1323 | fn addComdatGroup(self: *Object, allocator: Allocator) !Elf.ComdatGroup.Index { | |
| 1380 | fn addComdatGroup(self: *Object, gpa: Allocator) !Elf.ComdatGroup.Index { | |
| 1324 | 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 | 1383 | return index; |
| 1327 | 1384 | } |
| 1328 | 1385 | |
| ... | ... | @@ -1516,8 +1573,9 @@ const log = std.log.scoped(.link); |
| 1516 | 1573 | const math = std.math; |
| 1517 | 1574 | const mem = std.mem; |
| 1518 | 1575 | const Path = std.Build.Cache.Path; |
| 1519 | const Allocator = mem.Allocator; | |
| 1576 | const Allocator = std.mem.Allocator; | |
| 1520 | 1577 | |
| 1578 | const Diags = @import("../../link.zig").Diags; | |
| 1521 | 1579 | const Archive = @import("Archive.zig"); |
| 1522 | 1580 | const Atom = @import("Atom.zig"); |
| 1523 | 1581 | const AtomList = @import("AtomList.zig"); |
| ... | ... | @@ -1528,3 +1586,4 @@ const File = @import("file.zig").File; |
| 1528 | 1586 | const Merge = @import("Merge.zig"); |
| 1529 | 1587 | const Symbol = @import("Symbol.zig"); |
| 1530 | 1588 | const Alignment = Atom.Alignment; |
| 1589 | const riscv = @import("../riscv.zig"); |
src/link/Elf/ZigObject.zig+12-18| ... | ... | @@ -928,7 +928,7 @@ pub fn getNavVAddr( |
| 928 | 928 | nav.name.toSlice(ip), |
| 929 | 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 | 933 | const this_sym = self.symbol(this_sym_index); |
| 934 | 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 | 1102 | } |
| 1103 | 1103 | } |
| 1104 | 1104 | |
| 1105 | pub fn getOrCreateMetadataForNav( | |
| 1106 | self: *ZigObject, | |
| 1107 | elf_file: *Elf, | |
| 1108 | nav_index: InternPool.Nav.Index, | |
| 1109 | ) !Symbol.Index { | |
| 1110 | const gpa = elf_file.base.comp.gpa; | |
| 1105 | pub fn getOrCreateMetadataForNav(self: *ZigObject, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index { | |
| 1106 | const gpa = zcu.gpa; | |
| 1111 | 1107 | const gop = try self.navs.getOrPut(gpa, nav_index); |
| 1112 | 1108 | if (!gop.found_existing) { |
| 1113 | const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded; | |
| 1114 | 1109 | const symbol_index = try self.newSymbolWithAtom(gpa, 0); |
| 1115 | const zcu = elf_file.base.comp.zcu.?; | |
| 1116 | 1110 | const nav_val = Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val); |
| 1117 | 1111 | const sym = self.symbol(symbol_index); |
| 1118 | 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 | 1114 | sym.flags.is_tls = true; |
| 1121 | 1115 | } |
| 1122 | 1116 | } |
| ... | ... | @@ -1425,8 +1419,8 @@ pub fn updateFunc( |
| 1425 | 1419 | |
| 1426 | 1420 | log.debug("updateFunc {}({d})", .{ ip.getNav(func.owner_nav).fqn.fmt(ip), func.owner_nav }); |
| 1427 | 1421 | |
| 1428 | const sym_index = try self.getOrCreateMetadataForNav(elf_file, func.owner_nav); | |
| 1429 | self.symbol(sym_index).atom(elf_file).?.freeRelocs(self); | |
| 1422 | const sym_index = try self.getOrCreateMetadataForNav(zcu, func.owner_nav); | |
| 1423 | self.atom(self.symbol(sym_index).ref.index).?.freeRelocs(self); | |
| 1430 | 1424 | |
| 1431 | 1425 | var code_buffer = std.ArrayList(u8).init(gpa); |
| 1432 | 1426 | defer code_buffer.deinit(); |
| ... | ... | @@ -1460,12 +1454,12 @@ pub fn updateFunc( |
| 1460 | 1454 | ip.getNav(func.owner_nav).fqn.fmt(ip), |
| 1461 | 1455 | }); |
| 1462 | 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 | 1458 | break :blk .{ atom_ptr.value, atom_ptr.alignment }; |
| 1465 | 1459 | }; |
| 1466 | 1460 | try self.updateNavCode(elf_file, pt, func.owner_nav, sym_index, shndx, code, elf.STT_FUNC); |
| 1467 | 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 | 1463 | break :blk .{ atom_ptr.value, atom_ptr.alignment }; |
| 1470 | 1464 | }; |
| 1471 | 1465 | |
| ... | ... | @@ -1477,7 +1471,7 @@ pub fn updateFunc( |
| 1477 | 1471 | .{ |
| 1478 | 1472 | .index = sym_index, |
| 1479 | 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 | 1476 | wip_nav, |
| 1483 | 1477 | ); |
| ... | ... | @@ -1500,7 +1494,7 @@ pub fn updateFunc( |
| 1500 | 1494 | }); |
| 1501 | 1495 | defer gpa.free(name); |
| 1502 | 1496 | const osec = if (self.text_index) |sect_sym_index| |
| 1503 | self.symbol(sect_sym_index).atom(elf_file).?.output_section_index | |
| 1497 | self.atom(self.symbol(sect_sym_index).ref.index).?.output_section_index | |
| 1504 | 1498 | else osec: { |
| 1505 | 1499 | const osec = try elf_file.addSection(.{ |
| 1506 | 1500 | .name = try elf_file.insertShString(".text"), |
| ... | ... | @@ -1565,7 +1559,7 @@ pub fn updateNav( |
| 1565 | 1559 | }; |
| 1566 | 1560 | |
| 1567 | 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 | 1563 | self.symbol(sym_index).atom(elf_file).?.freeRelocs(self); |
| 1570 | 1564 | |
| 1571 | 1565 | var code_buffer = std.ArrayList(u8).init(zcu.gpa); |
| ... | ... | @@ -1789,7 +1783,7 @@ pub fn updateExports( |
| 1789 | 1783 | const gpa = elf_file.base.comp.gpa; |
| 1790 | 1784 | const metadata = switch (exported) { |
| 1791 | 1785 | .nav => |nav| blk: { |
| 1792 | _ = try self.getOrCreateMetadataForNav(elf_file, nav); | |
| 1786 | _ = try self.getOrCreateMetadataForNav(zcu, nav); | |
| 1793 | 1787 | break :blk self.navs.getPtr(nav).?; |
| 1794 | 1788 | }, |
| 1795 | 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 | 19 | return base + fde.out_offset; |
| 20 | 20 | } |
| 21 | 21 | |
| 22 | pub fn data(fde: Fde, elf_file: *Elf) []u8 { | |
| 23 | const object = elf_file.file(fde.file_index).?.object; | |
| 22 | pub fn data(fde: Fde, object: *Object) []u8 { | |
| 24 | 23 | return object.eh_frame_data.items[fde.offset..][0..fde.calcSize()]; |
| 25 | 24 | } |
| 26 | 25 | |
| 27 | pub fn cie(fde: Fde, elf_file: *Elf) Cie { | |
| 28 | const object = elf_file.file(fde.file_index).?.object; | |
| 26 | pub fn cie(fde: Fde, object: *Object) Cie { | |
| 29 | 27 | return object.cies.items[fde.cie_index]; |
| 30 | 28 | } |
| 31 | 29 | |
| 32 | pub fn ciePointer(fde: Fde, elf_file: *Elf) u32 { | |
| 33 | const fde_data = fde.data(elf_file); | |
| 30 | pub fn ciePointer(fde: Fde, object: *Object) u32 { | |
| 31 | const fde_data = fde.data(object); | |
| 34 | 32 | return std.mem.readInt(u32, fde_data[4..8], .little); |
| 35 | 33 | } |
| 36 | 34 | |
| ... | ... | @@ -38,16 +36,14 @@ pub const Fde = struct { |
| 38 | 36 | return fde.size + 4; |
| 39 | 37 | } |
| 40 | 38 | |
| 41 | pub fn atom(fde: Fde, elf_file: *Elf) *Atom { | |
| 42 | const object = elf_file.file(fde.file_index).?.object; | |
| 43 | const rel = fde.relocs(elf_file)[0]; | |
| 39 | pub fn atom(fde: Fde, object: *Object) *Atom { | |
| 40 | const rel = fde.relocs(object)[0]; | |
| 44 | 41 | const sym = object.symtab.items[rel.r_sym()]; |
| 45 | 42 | const atom_index = object.atoms_indexes.items[sym.st_shndx]; |
| 46 | 43 | return object.atom(atom_index).?; |
| 47 | 44 | } |
| 48 | 45 | |
| 49 | pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela { | |
| 50 | const object = elf_file.file(fde.file_index).?.object; | |
| 46 | pub fn relocs(fde: Fde, object: *Object) []const elf.Elf64_Rela { | |
| 51 | 47 | return object.relocs.items[fde.rel_index..][0..fde.rel_num]; |
| 52 | 48 | } |
| 53 | 49 | |
| ... | ... | @@ -87,7 +83,8 @@ pub const Fde = struct { |
| 87 | 83 | const fde = ctx.fde; |
| 88 | 84 | const elf_file = ctx.elf_file; |
| 89 | 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 | 88 | try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{ |
| 92 | 89 | base_addr + fde.out_offset, |
| 93 | 90 | fde.calcSize(), |
| ... | ... | @@ -306,7 +303,7 @@ pub fn calcEhFrameRelocs(elf_file: *Elf) usize { |
| 306 | 303 | } |
| 307 | 304 | for (object.fdes.items) |fde| { |
| 308 | 305 | if (!fde.alive) continue; |
| 309 | count += fde.relocs(elf_file).len; | |
| 306 | count += fde.relocs(object).len; | |
| 310 | 307 | } |
| 311 | 308 | } |
| 312 | 309 | return count; |
| ... | ... | @@ -369,16 +366,16 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { |
| 369 | 366 | for (object.fdes.items) |fde| { |
| 370 | 367 | if (!fde.alive) continue; |
| 371 | 368 | |
| 372 | const contents = fde.data(elf_file); | |
| 369 | const contents = fde.data(object); | |
| 373 | 370 | |
| 374 | 371 | std.mem.writeInt( |
| 375 | 372 | i32, |
| 376 | 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 | 375 | .little, |
| 379 | 376 | ); |
| 380 | 377 | |
| 381 | for (fde.relocs(elf_file)) |rel| { | |
| 378 | for (fde.relocs(object)) |rel| { | |
| 382 | 379 | const ref = object.resolveSymbol(rel.r_sym(), elf_file); |
| 383 | 380 | const sym = elf_file.symbol(ref).?; |
| 384 | 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 | 409 | for (object.fdes.items) |fde| { |
| 413 | 410 | if (!fde.alive) continue; |
| 414 | 411 | |
| 415 | const contents = fde.data(elf_file); | |
| 412 | const contents = fde.data(object); | |
| 416 | 413 | |
| 417 | 414 | std.mem.writeInt( |
| 418 | 415 | i32, |
| 419 | 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 | 418 | .little, |
| 422 | 419 | ); |
| 423 | 420 | |
| ... | ... | @@ -490,7 +487,7 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void { |
| 490 | 487 | |
| 491 | 488 | for (object.fdes.items) |fde| { |
| 492 | 489 | if (!fde.alive) continue; |
| 493 | for (fde.relocs(elf_file)) |rel| { | |
| 490 | for (fde.relocs(object)) |rel| { | |
| 494 | 491 | const ref = object.resolveSymbol(rel.r_sym(), elf_file); |
| 495 | 492 | const sym = elf_file.symbol(ref).?; |
| 496 | 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 | 545 | for (object.fdes.items) |fde| { |
| 549 | 546 | if (!fde.alive) continue; |
| 550 | 547 | |
| 551 | const relocs = fde.relocs(elf_file); | |
| 548 | const relocs = fde.relocs(object); | |
| 552 | 549 | assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips... |
| 553 | 550 | const rel = relocs[0]; |
| 554 | 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 | 279 | pub const Index = u32; |
| 280 | 280 | |
| 281 | 281 | pub const Entry = union(enum) { |
| 282 | null: void, | |
| 283 | zig_object: ZigObject, | |
| 282 | null, | |
| 283 | zig_object, | |
| 284 | 284 | linker_defined: LinkerDefined, |
| 285 | 285 | object: Object, |
| 286 | 286 | shared_object: SharedObject, |
src/link/Elf/gc.zig+28-21| ... | ... | @@ -103,15 +103,20 @@ fn markLive(atom: *Atom, elf_file: *Elf) void { |
| 103 | 103 | assert(atom.visited); |
| 104 | 104 | const file = atom.file(elf_file).?; |
| 105 | 105 | |
| 106 | for (atom.fdes(elf_file)) |fde| { | |
| 107 | for (fde.relocs(elf_file)[1..]) |rel| { | |
| 108 | const ref = file.resolveSymbol(rel.r_sym(), elf_file); | |
| 109 | const target_sym = elf_file.symbol(ref) orelse continue; | |
| 110 | const target_atom = target_sym.atom(elf_file) orelse continue; | |
| 111 | target_atom.alive = true; | |
| 112 | gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); | |
| 113 | if (markAtom(target_atom)) markLive(target_atom, elf_file); | |
| 114 | } | |
| 106 | switch (file) { | |
| 107 | .object => |object| { | |
| 108 | for (atom.fdes(object)) |fde| { | |
| 109 | for (fde.relocs(object)[1..]) |rel| { | |
| 110 | const ref = file.resolveSymbol(rel.r_sym(), elf_file); | |
| 111 | const target_sym = elf_file.symbol(ref) orelse continue; | |
| 112 | const target_atom = target_sym.atom(elf_file) orelse continue; | |
| 113 | target_atom.alive = true; | |
| 114 | gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); | |
| 115 | if (markAtom(target_atom)) markLive(target_atom, elf_file); | |
| 116 | } | |
| 117 | } | |
| 118 | }, | |
| 119 | else => {}, | |
| 115 | 120 | } |
| 116 | 121 | |
| 117 | 122 | for (atom.relocs(elf_file)) |rel| { |
| ... | ... | @@ -135,23 +140,25 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void { |
| 135 | 140 | } |
| 136 | 141 | } |
| 137 | 142 | |
| 138 | fn prune(elf_file: *Elf) void { | |
| 139 | const pruneInFile = struct { | |
| 140 | fn pruneInFile(file: File, ef: *Elf) void { | |
| 141 | for (file.atoms()) |atom_index| { | |
| 142 | const atom = file.atom(atom_index) orelse continue; | |
| 143 | if (atom.alive and !atom.visited) { | |
| 144 | atom.alive = false; | |
| 145 | atom.markFdesDead(ef); | |
| 146 | } | |
| 143 | fn pruneInFile(file: File) void { | |
| 144 | for (file.atoms()) |atom_index| { | |
| 145 | const atom = file.atom(atom_index) orelse continue; | |
| 146 | if (atom.alive and !atom.visited) { | |
| 147 | atom.alive = false; | |
| 148 | switch (file) { | |
| 149 | .object => |object| atom.markFdesDead(object), | |
| 150 | else => {}, | |
| 147 | 151 | } |
| 148 | 152 | } |
| 149 | }.pruneInFile; | |
| 153 | } | |
| 154 | } | |
| 155 | ||
| 156 | fn prune(elf_file: *Elf) void { | |
| 150 | 157 | if (elf_file.zigObjectPtr()) |zo| { |
| 151 | pruneInFile(zo.asFile(), elf_file); | |
| 158 | pruneInFile(zo.asFile()); | |
| 152 | 159 | } |
| 153 | 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 | } |
| 157 | 164 |
src/link/Elf/relocatable.zig+4-94| ... | ... | @@ -1,27 +1,7 @@ |
| 1 | pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void { | |
| 1 | pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void { | |
| 2 | 2 | const gpa = comp.gpa; |
| 3 | 3 | const diags = &comp.link_diags; |
| 4 | 4 | |
| 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 | 5 | if (diags.hasErrors()) return error.FlushFailure; |
| 26 | 6 | |
| 27 | 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 | 130 | if (diags.hasErrors()) return error.FlushFailure; |
| 151 | 131 | } |
| 152 | 132 | |
| 153 | pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void { | |
| 133 | pub fn flushObject(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void { | |
| 154 | 134 | const diags = &comp.link_diags; |
| 155 | 135 | |
| 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 | 136 | if (diags.hasErrors()) return error.FlushFailure; |
| 170 | 137 | |
| 171 | 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 | 182 | if (diags.hasErrors()) return error.FlushFailure; |
| 216 | 183 | } |
| 217 | 184 | |
| 218 | fn 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 | ||
| 226 | fn 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 | ||
| 234 | fn 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 | ||
| 254 | fn 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 | ||
| 276 | 185 | fn claimUnresolved(elf_file: *Elf) void { |
| 277 | 186 | if (elf_file.zigObjectPtr()) |zig_object| { |
| 278 | 187 | zig_object.claimUnresolvedRelocatable(elf_file); |
| ... | ... | @@ -473,11 +382,12 @@ fn writeSyntheticSections(elf_file: *Elf) !void { |
| 473 | 382 | const SortRelocs = struct { |
| 474 | 383 | pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool { |
| 475 | 384 | _ = ctx; |
| 385 | assert(lhs.r_offset != rhs.r_offset); | |
| 476 | 386 | return lhs.r_offset < rhs.r_offset; |
| 477 | 387 | } |
| 478 | 388 | }; |
| 479 | 389 | |
| 480 | mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan); | |
| 390 | mem.sortUnstable(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan); | |
| 481 | 391 | |
| 482 | 392 | log.debug("writing {s} from 0x{x} to 0x{x}", .{ |
| 483 | 393 | elf_file.getShString(shdr.sh_name), |
src/link/LdScript.zig created+449| ... | ... | @@ -0,0 +1,449 @@ |
| 1 | path: Path, | |
| 2 | cpu_arch: ?std.Target.Cpu.Arch, | |
| 3 | args: []const Arg, | |
| 4 | ||
| 5 | pub const Arg = struct { | |
| 6 | needed: bool = false, | |
| 7 | path: []const u8, | |
| 8 | }; | |
| 9 | ||
| 10 | pub fn deinit(ls: *LdScript, gpa: Allocator) void { | |
| 11 | gpa.free(ls.args); | |
| 12 | ls.* = undefined; | |
| 13 | } | |
| 14 | ||
| 15 | pub const Error = error{ | |
| 16 | LinkFailure, | |
| 17 | UnknownCpuArch, | |
| 18 | OutOfMemory, | |
| 19 | }; | |
| 20 | ||
| 21 | pub 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 | ||
| 88 | const LineColumn = struct { | |
| 89 | line: usize, | |
| 90 | column: usize, | |
| 91 | }; | |
| 92 | ||
| 93 | const 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 | ||
| 114 | const 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 | ||
| 237 | const 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 | ||
| 265 | const 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 | ||
| 405 | const 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 | ||
| 443 | const LdScript = @This(); | |
| 444 | const Diags = @import("../link.zig").Diags; | |
| 445 | ||
| 446 | const std = @import("std"); | |
| 447 | const assert = std.debug.assert; | |
| 448 | const Path = std.Build.Cache.Path; | |
| 449 | const Allocator = std.mem.Allocator; |
src/link/MachO.zig+122-71| ... | ... | @@ -1,3 +1,7 @@ |
| 1 | pub const Atom = @import("MachO/Atom.zig"); | |
| 2 | pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); | |
| 3 | pub const Relocation = @import("MachO/Relocation.zig"); | |
| 4 | ||
| 1 | 5 | base: link.File, |
| 2 | 6 | |
| 3 | 7 | rpath_list: []const []const u8, |
| ... | ... | @@ -114,8 +118,8 @@ headerpad_max_install_names: bool, |
| 114 | 118 | dead_strip_dylibs: bool, |
| 115 | 119 | /// Treatment of undefined symbols |
| 116 | 120 | undefined_treatment: UndefinedTreatment, |
| 117 | /// Resolved list of library search directories | |
| 118 | lib_dirs: []const []const u8, | |
| 121 | /// TODO: delete this, libraries need to be resolved by the frontend instead | |
| 122 | lib_directories: []const Directory, | |
| 119 | 123 | /// Resolved list of framework search directories |
| 120 | 124 | framework_dirs: []const []const u8, |
| 121 | 125 | /// List of input frameworks |
| ... | ... | @@ -213,7 +217,8 @@ pub fn createEmpty( |
| 213 | 217 | .platform = Platform.fromTarget(target), |
| 214 | 218 | .sdk_version = if (options.darwin_sdk_layout) |layout| inferSdkVersion(comp, layout) else null, |
| 215 | 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 | 222 | .framework_dirs = options.framework_dirs, |
| 218 | 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 | 376 | if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); |
| 372 | 377 | if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); |
| 373 | 378 | |
| 374 | var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); | |
| 379 | var positionals = std.ArrayList(link.Input).init(gpa); | |
| 375 | 380 | defer positionals.deinit(); |
| 376 | 381 | |
| 377 | try positionals.ensureUnusedCapacity(comp.objects.len); | |
| 378 | positionals.appendSliceAssumeCapacity(comp.objects); | |
| 382 | try positionals.ensureUnusedCapacity(comp.link_inputs.len); | |
| 383 | ||
| 384 | for (comp.link_inputs) |link_input| switch (link_input) { | |
| 385 | .dso => continue, // handled below | |
| 386 | .object, .archive => positionals.appendAssumeCapacity(link_input), | |
| 387 | .dso_exact => @panic("TODO"), | |
| 388 | .res => unreachable, | |
| 389 | }; | |
| 379 | 390 | |
| 380 | 391 | // This is a set of object files emitted by clang in a single `build-exe` invocation. |
| 381 | 392 | // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up |
| 382 | 393 | // in this set. |
| 383 | 394 | try positionals.ensureUnusedCapacity(comp.c_object_table.keys().len); |
| 384 | 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 | } |
| 387 | 398 | |
| 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)); | |
| 389 | 400 | |
| 390 | 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 | } |
| 393 | 404 | |
| 394 | 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 | } |
| 397 | 408 | |
| 398 | for (positionals.items) |obj| { | |
| 399 | self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| | |
| 400 | diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)}); | |
| 409 | for (positionals.items) |link_input| { | |
| 410 | self.classifyInputFile(link_input) catch |err| | |
| 411 | diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)}); | |
| 401 | 412 | } |
| 402 | 413 | |
| 403 | 414 | var system_libs = std.ArrayList(SystemLib).init(gpa); |
| 404 | 415 | defer system_libs.deinit(); |
| 405 | 416 | |
| 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 | 417 | // frameworks |
| 417 | 418 | try system_libs.ensureUnusedCapacity(self.frameworks.len); |
| 418 | 419 | for (self.frameworks) |info| { |
| ... | ... | @@ -436,20 +437,40 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 436 | 437 | else => |e| return e, // TODO: convert into an error |
| 437 | 438 | }; |
| 438 | 439 | |
| 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 | 449 | for (system_libs.items) |lib| { |
| 440 | self.classifyInputFile(lib.path, lib, false) catch |err| | |
| 441 | diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)}); | |
| 450 | switch (Compilation.classifyFileExt(lib.path.sub_path)) { | |
| 451 | .shared_library => { | |
| 452 | const dso_input = try link.openDsoInput(diags, lib.path, lib.needed, lib.weak, lib.reexport); | |
| 453 | self.classifyInputFile(dso_input) catch |err| | |
| 454 | diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)}); | |
| 455 | }, | |
| 456 | .static_library => { | |
| 457 | const archive_input = try link.openArchiveInput(diags, lib.path, lib.must_link, lib.hidden); | |
| 458 | self.classifyInputFile(archive_input) catch |err| | |
| 459 | diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)}); | |
| 460 | }, | |
| 461 | else => unreachable, | |
| 462 | } | |
| 442 | 463 | } |
| 443 | 464 | |
| 444 | 465 | // Finally, link against compiler_rt. |
| 445 | const compiler_rt_path: ?Path = blk: { | |
| 446 | if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; | |
| 447 | if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; | |
| 448 | break :blk null; | |
| 449 | }; | |
| 450 | if (compiler_rt_path) |path| { | |
| 451 | self.classifyInputFile(path, .{ .path = path }, false) catch |err| | |
| 452 | diags.addParseError(path, "failed to parse input file: {s}", .{@errorName(err)}); | |
| 466 | if (comp.compiler_rt_lib) |crt_file| { | |
| 467 | const path = crt_file.full_object_path; | |
| 468 | self.classifyInputFile(try link.openArchiveInput(diags, path, false, false)) catch |err| | |
| 469 | diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); | |
| 470 | } else if (comp.compiler_rt_obj) |crt_file| { | |
| 471 | const path = crt_file.full_object_path; | |
| 472 | self.classifyInputFile(try link.openObjectInput(diags, path)) catch |err| | |
| 473 | diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); | |
| 453 | 474 | } |
| 454 | 475 | |
| 455 | 476 | try self.parseInputFiles(); |
| ... | ... | @@ -596,9 +617,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 596 | 617 | } |
| 597 | 618 | |
| 598 | 619 | if (self.base.isRelocatable()) { |
| 599 | for (comp.objects) |obj| { | |
| 600 | try argv.append(try obj.path.toString(arena)); | |
| 601 | } | |
| 620 | for (comp.link_inputs) |link_input| switch (link_input) { | |
| 621 | .object, .archive => |obj| try argv.append(try obj.path.toString(arena)), | |
| 622 | .res => |res| try argv.append(try res.path.toString(arena)), | |
| 623 | .dso => |dso| try argv.append(try dso.path.toString(arena)), | |
| 624 | .dso_exact => |dso_exact| try argv.appendSlice(&.{ "-l", dso_exact.name }), | |
| 625 | }; | |
| 602 | 626 | |
| 603 | 627 | for (comp.c_object_table.keys()) |key| { |
| 604 | 628 | try argv.append(try key.status.success.object_path.toString(arena)); |
| ... | ... | @@ -678,13 +702,15 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 678 | 702 | try argv.append("dynamic_lookup"); |
| 679 | 703 | } |
| 680 | 704 | |
| 681 | for (comp.objects) |obj| { | |
| 682 | // TODO: verify this | |
| 683 | if (obj.must_link) { | |
| 684 | try argv.append("-force_load"); | |
| 685 | } | |
| 686 | try argv.append(try obj.path.toString(arena)); | |
| 687 | } | |
| 705 | for (comp.link_inputs) |link_input| switch (link_input) { | |
| 706 | .dso => continue, // handled below | |
| 707 | .res => unreachable, // windows only | |
| 708 | .object, .archive => |obj| { | |
| 709 | if (obj.must_link) try argv.append("-force_load"); // TODO: verify this | |
| 710 | try argv.append(try obj.path.toString(arena)); | |
| 711 | }, | |
| 712 | .dso_exact => |dso_exact| try argv.appendSlice(&.{ "-l", dso_exact.name }), | |
| 713 | }; | |
| 688 | 714 | |
| 689 | 715 | for (comp.c_object_table.keys()) |key| { |
| 690 | 716 | try argv.append(try key.status.success.object_path.toString(arena)); |
| ... | ... | @@ -703,21 +729,25 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 703 | 729 | try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena)); |
| 704 | 730 | } |
| 705 | 731 | |
| 706 | for (self.lib_dirs) |lib_dir| { | |
| 707 | const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}); | |
| 732 | for (self.lib_directories) |lib_directory| { | |
| 733 | // TODO delete this, directories must instead be resolved by the frontend | |
| 734 | const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_directory.path orelse "."}); | |
| 708 | 735 | try argv.append(arg); |
| 709 | 736 | } |
| 710 | 737 | |
| 711 | for (comp.system_libs.keys()) |l_name| { | |
| 712 | const info = comp.system_libs.get(l_name).?; | |
| 713 | const arg = if (info.needed) | |
| 714 | try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name}) | |
| 715 | else if (info.weak) | |
| 716 | try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name}) | |
| 717 | else | |
| 718 | try std.fmt.allocPrint(arena, "-l{s}", .{l_name}); | |
| 719 | try argv.append(arg); | |
| 720 | } | |
| 738 | for (comp.link_inputs) |link_input| switch (link_input) { | |
| 739 | .object, .archive, .dso_exact => continue, // handled above | |
| 740 | .res => unreachable, // windows only | |
| 741 | .dso => |dso| { | |
| 742 | if (dso.needed) { | |
| 743 | try argv.appendSlice(&.{ "-needed-l", try dso.path.toString(arena) }); | |
| 744 | } else if (dso.weak) { | |
| 745 | try argv.appendSlice(&.{ "-weak-l", try dso.path.toString(arena) }); | |
| 746 | } else { | |
| 747 | try argv.appendSlice(&.{ "-l", try dso.path.toString(arena) }); | |
| 748 | } | |
| 749 | }, | |
| 750 | }; | |
| 721 | 751 | |
| 722 | 752 | for (self.framework_dirs) |f_dir| { |
| 723 | 753 | try argv.append("-F"); |
| ... | ... | @@ -751,6 +781,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 751 | 781 | Compilation.dump_argv(argv.items); |
| 752 | 782 | } |
| 753 | 783 | |
| 784 | /// TODO delete this, libsystem must be resolved when setting up the compilation pipeline | |
| 754 | 785 | pub fn resolveLibSystem( |
| 755 | 786 | self: *MachO, |
| 756 | 787 | arena: Allocator, |
| ... | ... | @@ -774,8 +805,8 @@ pub fn resolveLibSystem( |
| 774 | 805 | }, |
| 775 | 806 | }; |
| 776 | 807 | |
| 777 | for (self.lib_dirs) |dir| { | |
| 778 | if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success; | |
| 808 | for (self.lib_directories) |directory| { | |
| 809 | if (try accessLibPath(arena, &test_path, &checked_paths, directory.path orelse ".", "System")) break :success; | |
| 779 | 810 | } |
| 780 | 811 | |
| 781 | 812 | diags.addMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{}); |
| ... | ... | @@ -789,13 +820,14 @@ pub fn resolveLibSystem( |
| 789 | 820 | }); |
| 790 | 821 | } |
| 791 | 822 | |
| 792 | pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bool) !void { | |
| 823 | pub fn classifyInputFile(self: *MachO, input: link.Input) !void { | |
| 793 | 824 | const tracy = trace(@src()); |
| 794 | 825 | defer tracy.end(); |
| 795 | 826 | |
| 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 | 829 | log.debug("classifying input file {}", .{path}); |
| 797 | 830 | |
| 798 | const file = try path.root_dir.handle.openFile(path.sub_path, .{}); | |
| 799 | 831 | const fh = try self.addFileHandle(file); |
| 800 | 832 | var buffer: [Archive.SARMAG]u8 = undefined; |
| 801 | 833 | |
| ... | ... | @@ -806,17 +838,17 @@ pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bo |
| 806 | 838 | if (h.magic != macho.MH_MAGIC_64) break :blk; |
| 807 | 839 | switch (h.filetype) { |
| 808 | 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 | 842 | else => return error.UnknownFileType, |
| 811 | 843 | } |
| 812 | 844 | return; |
| 813 | 845 | } |
| 814 | 846 | if (readArMagic(file, offset, &buffer) catch null) |ar_magic| blk: { |
| 815 | 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 | 849 | return; |
| 818 | 850 | } |
| 819 | _ = try self.addTbd(lib, true, fh); | |
| 851 | _ = try self.addTbd(.fromLinkInput(input), true, fh); | |
| 820 | 852 | } |
| 821 | 853 | |
| 822 | 854 | fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch { |
| ... | ... | @@ -903,7 +935,7 @@ fn parseInputFileWorker(self: *MachO, file: File) void { |
| 903 | 935 | }; |
| 904 | 936 | } |
| 905 | 937 | |
| 906 | fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.HandleIndex, fat_arch: ?fat.Arch) !void { | |
| 938 | fn addArchive(self: *MachO, lib: link.Input.Object, handle: File.HandleIndex, fat_arch: ?fat.Arch) !void { | |
| 907 | 939 | const tracy = trace(@src()); |
| 908 | 940 | defer tracy.end(); |
| 909 | 941 | |
| ... | ... | @@ -918,7 +950,7 @@ fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.Handle |
| 918 | 950 | self.files.set(index, .{ .object = unpacked }); |
| 919 | 951 | const object = &self.files.items(.data)[index].object; |
| 920 | 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 | 954 | object.hidden = lib.hidden; |
| 923 | 955 | try self.objects.append(gpa, index); |
| 924 | 956 | } |
| ... | ... | @@ -993,6 +1025,7 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool { |
| 993 | 1025 | return false; |
| 994 | 1026 | } |
| 995 | 1027 | |
| 1028 | /// TODO delete this, libraries must be instead resolved when instantiating the compilation pipeline | |
| 996 | 1029 | fn accessLibPath( |
| 997 | 1030 | arena: Allocator, |
| 998 | 1031 | test_path: *std.ArrayList(u8), |
| ... | ... | @@ -1051,9 +1084,11 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1051 | 1084 | if (self.dylibs.items.len == 0) return; |
| 1052 | 1085 | |
| 1053 | 1086 | const gpa = self.base.comp.gpa; |
| 1054 | const lib_dirs = self.lib_dirs; | |
| 1055 | 1087 | const framework_dirs = self.framework_dirs; |
| 1056 | 1088 | |
| 1089 | // TODO delete this, directories must instead be resolved by the frontend | |
| 1090 | const lib_directories = self.lib_directories; | |
| 1091 | ||
| 1057 | 1092 | var arena_alloc = std.heap.ArenaAllocator.init(gpa); |
| 1058 | 1093 | defer arena_alloc.deinit(); |
| 1059 | 1094 | const arena = arena_alloc.allocator(); |
| ... | ... | @@ -1094,9 +1129,9 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1094 | 1129 | |
| 1095 | 1130 | // Library |
| 1096 | 1131 | const lib_name = eatPrefix(stem, "lib") orelse stem; |
| 1097 | for (lib_dirs) |dir| { | |
| 1132 | for (lib_directories) |lib_directory| { | |
| 1098 | 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 | } |
| 1102 | 1137 | |
| ... | ... | @@ -4366,6 +4401,24 @@ const SystemLib = struct { |
| 4366 | 4401 | hidden: bool = false, |
| 4367 | 4402 | reexport: bool = false, |
| 4368 | 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 | }; |
| 4370 | 4423 | |
| 4371 | 4424 | pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout; |
| ... | ... | @@ -5303,17 +5356,16 @@ const Air = @import("../Air.zig"); |
| 5303 | 5356 | const Alignment = Atom.Alignment; |
| 5304 | 5357 | const Allocator = mem.Allocator; |
| 5305 | 5358 | const Archive = @import("MachO/Archive.zig"); |
| 5306 | pub const Atom = @import("MachO/Atom.zig"); | |
| 5307 | 5359 | const AtomicBool = std.atomic.Value(bool); |
| 5308 | 5360 | const Bind = bind.Bind; |
| 5309 | 5361 | const Cache = std.Build.Cache; |
| 5310 | const Path = Cache.Path; | |
| 5311 | 5362 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 5312 | 5363 | const Compilation = @import("../Compilation.zig"); |
| 5313 | 5364 | const DataInCode = synthetic.DataInCode; |
| 5314 | pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); | |
| 5365 | const Directory = Cache.Directory; | |
| 5315 | 5366 | const Dylib = @import("MachO/Dylib.zig"); |
| 5316 | 5367 | const ExportTrie = @import("MachO/dyld_info/Trie.zig"); |
| 5368 | const Path = Cache.Path; | |
| 5317 | 5369 | const File = @import("MachO/file.zig").File; |
| 5318 | 5370 | const GotSection = synthetic.GotSection; |
| 5319 | 5371 | const Hash = std.hash.Wyhash; |
| ... | ... | @@ -5329,7 +5381,6 @@ const Md5 = std.crypto.hash.Md5; |
| 5329 | 5381 | const Zcu = @import("../Zcu.zig"); |
| 5330 | 5382 | const InternPool = @import("../InternPool.zig"); |
| 5331 | 5383 | const Rebase = @import("MachO/dyld_info/Rebase.zig"); |
| 5332 | pub const Relocation = @import("MachO/Relocation.zig"); | |
| 5333 | 5384 | const StringTable = @import("StringTable.zig"); |
| 5334 | 5385 | const StubsSection = synthetic.StubsSection; |
| 5335 | 5386 | const 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 | 3 | const diags = &macho_file.base.comp.link_diags; |
| 4 | 4 | |
| 5 | 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 | 7 | defer positionals.deinit(); |
| 8 | try positionals.ensureUnusedCapacity(comp.objects.len); | |
| 9 | positionals.appendSliceAssumeCapacity(comp.objects); | |
| 8 | try positionals.ensureUnusedCapacity(comp.link_inputs.len); | |
| 9 | positionals.appendSliceAssumeCapacity(comp.link_inputs); | |
| 10 | 10 | |
| 11 | 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 | } |
| 14 | 14 | |
| 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)); | |
| 16 | 16 | |
| 17 | 17 | if (macho_file.getZigObject() == null and positionals.items.len == 1) { |
| 18 | 18 | // 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 | 20 | // the *only* input file over. |
| 21 | 21 | // TODO: in the future, when we implement `dsymutil` alternative directly in the Zig |
| 22 | 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 | 24 | const in_file = try path.root_dir.handle.openFile(path.sub_path, .{}); |
| 25 | 25 | const stat = try in_file.stat(); |
| 26 | 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 | 28 | return; |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | for (positionals.items) |obj| { | |
| 32 | macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| | |
| 33 | diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)}); | |
| 31 | for (positionals.items) |link_input| { | |
| 32 | macho_file.classifyInputFile(link_input) catch |err| | |
| 33 | diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)}); | |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | 36 | if (diags.hasErrors()) return error.FlushFailure; |
| ... | ... | @@ -72,25 +72,25 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 72 | 72 | const gpa = comp.gpa; |
| 73 | 73 | const diags = &macho_file.base.comp.link_diags; |
| 74 | 74 | |
| 75 | var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); | |
| 75 | var positionals = std.ArrayList(link.Input).init(gpa); | |
| 76 | 76 | defer positionals.deinit(); |
| 77 | 77 | |
| 78 | try positionals.ensureUnusedCapacity(comp.objects.len); | |
| 79 | positionals.appendSliceAssumeCapacity(comp.objects); | |
| 78 | try positionals.ensureUnusedCapacity(comp.link_inputs.len); | |
| 79 | positionals.appendSliceAssumeCapacity(comp.link_inputs); | |
| 80 | 80 | |
| 81 | 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 | } |
| 84 | 84 | |
| 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)); | |
| 86 | 86 | |
| 87 | 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 | } |
| 90 | 90 | |
| 91 | for (positionals.items) |obj| { | |
| 92 | macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| | |
| 93 | diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)}); | |
| 91 | for (positionals.items) |link_input| { | |
| 92 | macho_file.classifyInputFile(link_input) catch |err| | |
| 93 | diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)}); | |
| 94 | 94 | } |
| 95 | 95 | |
| 96 | 96 | if (diags.hasErrors()) return error.FlushFailure; |
| ... | ... | @@ -745,20 +745,15 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void { |
| 745 | 745 | try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0); |
| 746 | 746 | } |
| 747 | 747 | |
| 748 | const std = @import("std"); | |
| 749 | const Path = std.Build.Cache.Path; | |
| 750 | const WaitGroup = std.Thread.WaitGroup; | |
| 748 | 751 | const assert = std.debug.assert; |
| 749 | const build_options = @import("build_options"); | |
| 750 | const eh_frame = @import("eh_frame.zig"); | |
| 751 | const fat = @import("fat.zig"); | |
| 752 | const link = @import("../../link.zig"); | |
| 753 | const load_commands = @import("load_commands.zig"); | |
| 754 | 752 | const log = std.log.scoped(.link); |
| 755 | 753 | const macho = std.macho; |
| 756 | 754 | const math = std.math; |
| 757 | 755 | const mem = std.mem; |
| 758 | 756 | const state_log = std.log.scoped(.link_state); |
| 759 | const std = @import("std"); | |
| 760 | const trace = @import("../../tracy.zig").trace; | |
| 761 | const Path = std.Build.Cache.Path; | |
| 762 | 757 | |
| 763 | 758 | const Archive = @import("Archive.zig"); |
| 764 | 759 | const Atom = @import("Atom.zig"); |
| ... | ... | @@ -767,3 +762,9 @@ const File = @import("file.zig").File; |
| 767 | 762 | const MachO = @import("../MachO.zig"); |
| 768 | 763 | const Object = @import("Object.zig"); |
| 769 | 764 | const Symbol = @import("Symbol.zig"); |
| 765 | const build_options = @import("build_options"); | |
| 766 | const eh_frame = @import("eh_frame.zig"); | |
| 767 | const fat = @import("fat.zig"); | |
| 768 | const link = @import("../../link.zig"); | |
| 769 | const load_commands = @import("load_commands.zig"); | |
| 770 | const 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 | 637 | return loc; |
| 638 | 638 | } |
| 639 | 639 | |
| 640 | fn 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 | 640 | /// Parses the object file from given path. Returns true when the given file was an object |
| 649 | 641 | /// file and parsed successfully. Returns false when file is not an object file. |
| 650 | 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 | 2514 | // Positional arguments to the linker such as object files and static archives. |
| 2523 | 2515 | // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list. |
| 2524 | 2516 | var positionals = std.ArrayList([]const u8).init(arena); |
| 2525 | try positionals.ensureUnusedCapacity(comp.objects.len); | |
| 2517 | try positionals.ensureUnusedCapacity(comp.link_inputs.len); | |
| 2526 | 2518 | |
| 2527 | 2519 | const target = comp.root_mod.resolved_target.result; |
| 2528 | 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 | 2558 | try positionals.append(path); |
| 2567 | 2559 | } |
| 2568 | 2560 | |
| 2569 | for (comp.objects) |object| { | |
| 2570 | try positionals.append(try object.path.toString(arena)); | |
| 2571 | } | |
| 2561 | for (comp.link_inputs) |link_input| switch (link_input) { | |
| 2562 | .object, .archive => |obj| try positionals.append(try obj.path.toString(arena)), | |
| 2563 | .dso => |dso| try positionals.append(try dso.path.toString(arena)), | |
| 2564 | .dso_exact => unreachable, // forbidden by frontend | |
| 2565 | .res => unreachable, // windows only | |
| 2566 | }; | |
| 2572 | 2567 | |
| 2573 | 2568 | for (comp.c_object_table.keys()) |c_object| { |
| 2574 | 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 | 2572 | if (comp.compiler_rt_lib) |lib| try positionals.append(try lib.full_object_path.toString(arena)); |
| 2578 | 2573 | if (comp.compiler_rt_obj) |obj| try positionals.append(try obj.full_object_path.toString(arena)); |
| 2579 | 2574 | |
| 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 | } | |
| 2581 | 2580 | |
| 2582 | 2581 | if (wasm.zig_object_index != .null) { |
| 2583 | 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 | 3400 | |
| 3402 | 3401 | comptime assert(Compilation.link_hash_implementation_version == 14); |
| 3403 | 3402 | |
| 3404 | for (comp.objects) |obj| { | |
| 3405 | _ = try man.addFilePath(obj.path, null); | |
| 3406 | man.hash.add(obj.must_link); | |
| 3407 | } | |
| 3403 | try link.hashInputs(&man, comp.link_inputs); | |
| 3408 | 3404 | for (comp.c_object_table.keys()) |key| { |
| 3409 | 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 | 3454 | // here. TODO: think carefully about how we can avoid this redundant operation when doing |
| 3459 | 3455 | // build-obj. See also the corresponding TODO in linkAsArchive. |
| 3460 | 3456 | const the_object_path = blk: { |
| 3461 | if (comp.objects.len != 0) | |
| 3462 | break :blk comp.objects[0].path; | |
| 3457 | if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path; | |
| 3463 | 3458 | |
| 3464 | 3459 | if (comp.c_object_table.count() != 0) |
| 3465 | 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 | 3616 | |
| 3622 | 3617 | // Positional arguments to the linker such as object files. |
| 3623 | 3618 | var whole_archive = false; |
| 3624 | for (comp.objects) |obj| { | |
| 3625 | if (obj.must_link and !whole_archive) { | |
| 3626 | try argv.append("-whole-archive"); | |
| 3627 | whole_archive = true; | |
| 3628 | } else if (!obj.must_link and whole_archive) { | |
| 3629 | try argv.append("-no-whole-archive"); | |
| 3630 | whole_archive = false; | |
| 3631 | } | |
| 3632 | try argv.append(try obj.path.toString(arena)); | |
| 3633 | } | |
| 3619 | for (comp.link_inputs) |link_input| switch (link_input) { | |
| 3620 | .object, .archive => |obj| { | |
| 3621 | if (obj.must_link and !whole_archive) { | |
| 3622 | try argv.append("-whole-archive"); | |
| 3623 | whole_archive = true; | |
| 3624 | } else if (!obj.must_link and whole_archive) { | |
| 3625 | try argv.append("-no-whole-archive"); | |
| 3626 | whole_archive = false; | |
| 3627 | } | |
| 3628 | try argv.append(try obj.path.toString(arena)); | |
| 3629 | }, | |
| 3630 | .dso => |dso| { | |
| 3631 | try argv.append(try dso.path.toString(arena)); | |
| 3632 | }, | |
| 3633 | .dso_exact => unreachable, | |
| 3634 | .res => unreachable, | |
| 3635 | }; | |
| 3634 | 3636 | if (whole_archive) { |
| 3635 | 3637 | try argv.append("-no-whole-archive"); |
| 3636 | 3638 | whole_archive = false; |
| ... | ... | @@ -3643,11 +3645,8 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 3643 | 3645 | try argv.append(p); |
| 3644 | 3646 | } |
| 3645 | 3647 | |
| 3646 | if (comp.config.output_mode != .Obj and | |
| 3647 | !comp.skip_linker_dependencies and | |
| 3648 | !comp.config.link_libc) | |
| 3649 | { | |
| 3650 | try argv.append(try comp.libc_static_lib.?.full_object_path.toString(arena)); | |
| 3648 | if (comp.libc_static_lib) |crt_file| { | |
| 3649 | try argv.append(try crt_file.full_object_path.toString(arena)); | |
| 3651 | 3650 | } |
| 3652 | 3651 | |
| 3653 | 3652 | if (compiler_rt_path) |p| { |
src/main.zig+322-469| ... | ... | @@ -15,6 +15,7 @@ const cleanExit = std.process.cleanExit; |
| 15 | 15 | const native_os = builtin.os.tag; |
| 16 | 16 | const Cache = std.Build.Cache; |
| 17 | 17 | const Path = std.Build.Cache.Path; |
| 18 | const Directory = std.Build.Cache.Directory; | |
| 18 | 19 | const EnvVar = std.zig.EnvVar; |
| 19 | 20 | const LibCInstallation = std.zig.LibCInstallation; |
| 20 | 21 | const AstGen = std.zig.AstGen; |
| ... | ... | @@ -55,7 +56,7 @@ pub fn wasi_cwd() std.os.wasi.fd_t { |
| 55 | 56 | return cwd_fd; |
| 56 | 57 | } |
| 57 | 58 | |
| 58 | fn getWasiPreopen(name: []const u8) Compilation.Directory { | |
| 59 | fn getWasiPreopen(name: []const u8) Directory { | |
| 59 | 60 | return .{ |
| 60 | 61 | .path = name, |
| 61 | 62 | .handle = .{ |
| ... | ... | @@ -555,6 +556,8 @@ const usage_build_generic = |
| 555 | 556 | \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library |
| 556 | 557 | \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries |
| 557 | 558 | \\ -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 | 561 | \\ --build-id[=style] At a minor link-time expense, coordinates stripped binaries |
| 559 | 562 | \\ fast, uuid, sha1, md5 with debug symbols via a '.note.gnu.build-id' section |
| 560 | 563 | \\ 0x[hexstring] Maximum 32 bytes |
| ... | ... | @@ -766,27 +769,6 @@ const ArgsIterator = struct { |
| 766 | 769 | } |
| 767 | 770 | }; |
| 768 | 771 | |
| 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`. | |
| 772 | const 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 | 772 | /// Similar to `link.Framework` except it doesn't store yet unresolved |
| 791 | 773 | /// path to the framework. |
| 792 | 774 | const Framework = struct { |
| ... | ... | @@ -867,6 +849,7 @@ fn buildOutputType( |
| 867 | 849 | var linker_gc_sections: ?bool = null; |
| 868 | 850 | var linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null; |
| 869 | 851 | var linker_allow_shlib_undefined: ?bool = null; |
| 852 | var allow_so_scripts: bool = false; | |
| 870 | 853 | var linker_bind_global_refs_locally: ?bool = null; |
| 871 | 854 | var linker_import_symbols: bool = false; |
| 872 | 855 | var linker_import_table: bool = false; |
| ... | ... | @@ -919,7 +902,7 @@ fn buildOutputType( |
| 919 | 902 | var hash_style: link.File.Elf.HashStyle = .both; |
| 920 | 903 | var entitlements: ?[]const u8 = null; |
| 921 | 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 | 906 | var lib_preferred_mode: std.builtin.LinkMode = .dynamic; |
| 924 | 907 | var headerpad_size: ?u32 = null; |
| 925 | 908 | var headerpad_max_install_names: bool = false; |
| ... | ... | @@ -983,8 +966,10 @@ fn buildOutputType( |
| 983 | 966 | // Populated in the call to `createModule` for the root module. |
| 984 | 967 | .resolved_options = undefined, |
| 985 | 968 | |
| 986 | .system_libs = .{}, | |
| 987 | .resolved_system_libs = .{}, | |
| 969 | .cli_link_inputs = .empty, | |
| 970 | .windows_libs = .empty, | |
| 971 | .link_inputs = .empty, | |
| 972 | ||
| 988 | 973 | .wasi_emulated_libs = .{}, |
| 989 | 974 | |
| 990 | 975 | .c_source_files = .{}, |
| ... | ... | @@ -992,7 +977,7 @@ fn buildOutputType( |
| 992 | 977 | |
| 993 | 978 | .llvm_m_args = .{}, |
| 994 | 979 | .sysroot = null, |
| 995 | .lib_dirs = .{}, // populated by createModule() | |
| 980 | .lib_directories = .{}, // populated by createModule() | |
| 996 | 981 | .lib_dir_args = .{}, // populated from CLI arg parsing |
| 997 | 982 | .libc_installation = null, |
| 998 | 983 | .want_native_include_dirs = false, |
| ... | ... | @@ -1001,7 +986,6 @@ fn buildOutputType( |
| 1001 | 986 | .rpath_list = .{}, |
| 1002 | 987 | .each_lib_rpath = null, |
| 1003 | 988 | .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena), |
| 1004 | .link_objects = .{}, | |
| 1005 | 989 | .native_system_include_paths = &.{}, |
| 1006 | 990 | }; |
| 1007 | 991 | |
| ... | ... | @@ -1237,30 +1221,42 @@ fn buildOutputType( |
| 1237 | 1221 | // We don't know whether this library is part of libc |
| 1238 | 1222 | // or libc++ until we resolve the target, so we append |
| 1239 | 1223 | // to the list for now. |
| 1240 | try create_module.system_libs.put(arena, args_iter.nextOrFatal(), .{ | |
| 1241 | .needed = false, | |
| 1242 | .weak = false, | |
| 1243 | .preferred_mode = lib_preferred_mode, | |
| 1244 | .search_strategy = lib_search_strategy, | |
| 1245 | }); | |
| 1224 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ | |
| 1225 | .name = args_iter.nextOrFatal(), | |
| 1226 | .query = .{ | |
| 1227 | .needed = false, | |
| 1228 | .weak = false, | |
| 1229 | .preferred_mode = lib_preferred_mode, | |
| 1230 | .search_strategy = lib_search_strategy, | |
| 1231 | .allow_so_scripts = allow_so_scripts, | |
| 1232 | }, | |
| 1233 | } }); | |
| 1246 | 1234 | } else if (mem.eql(u8, arg, "--needed-library") or |
| 1247 | 1235 | mem.eql(u8, arg, "-needed-l") or |
| 1248 | 1236 | mem.eql(u8, arg, "-needed_library")) |
| 1249 | 1237 | { |
| 1250 | 1238 | const next_arg = args_iter.nextOrFatal(); |
| 1251 | try create_module.system_libs.put(arena, next_arg, .{ | |
| 1252 | .needed = true, | |
| 1253 | .weak = false, | |
| 1254 | .preferred_mode = lib_preferred_mode, | |
| 1255 | .search_strategy = lib_search_strategy, | |
| 1256 | }); | |
| 1239 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ | |
| 1240 | .name = next_arg, | |
| 1241 | .query = .{ | |
| 1242 | .needed = true, | |
| 1243 | .weak = false, | |
| 1244 | .preferred_mode = lib_preferred_mode, | |
| 1245 | .search_strategy = lib_search_strategy, | |
| 1246 | .allow_so_scripts = allow_so_scripts, | |
| 1247 | }, | |
| 1248 | } }); | |
| 1257 | 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(), .{ | |
| 1259 | .needed = false, | |
| 1260 | .weak = true, | |
| 1261 | .preferred_mode = lib_preferred_mode, | |
| 1262 | .search_strategy = lib_search_strategy, | |
| 1263 | }); | |
| 1250 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ | |
| 1251 | .name = args_iter.nextOrFatal(), | |
| 1252 | .query = .{ | |
| 1253 | .needed = false, | |
| 1254 | .weak = true, | |
| 1255 | .preferred_mode = lib_preferred_mode, | |
| 1256 | .search_strategy = lib_search_strategy, | |
| 1257 | .allow_so_scripts = allow_so_scripts, | |
| 1258 | }, | |
| 1259 | } }); | |
| 1264 | 1260 | } else if (mem.eql(u8, arg, "-D")) { |
| 1265 | 1261 | try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() }); |
| 1266 | 1262 | } else if (mem.eql(u8, arg, "-I")) { |
| ... | ... | @@ -1573,6 +1569,10 @@ fn buildOutputType( |
| 1573 | 1569 | linker_allow_shlib_undefined = true; |
| 1574 | 1570 | } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) { |
| 1575 | 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 | 1576 | } else if (mem.eql(u8, arg, "-z")) { |
| 1577 | 1577 | const z_arg = args_iter.nextOrFatal(); |
| 1578 | 1578 | if (mem.eql(u8, z_arg, "nodelete")) { |
| ... | ... | @@ -1680,26 +1680,38 @@ fn buildOutputType( |
| 1680 | 1680 | // We don't know whether this library is part of libc |
| 1681 | 1681 | // or libc++ until we resolve the target, so we append |
| 1682 | 1682 | // to the list for now. |
| 1683 | try create_module.system_libs.put(arena, arg["-l".len..], .{ | |
| 1684 | .needed = false, | |
| 1685 | .weak = false, | |
| 1686 | .preferred_mode = lib_preferred_mode, | |
| 1687 | .search_strategy = lib_search_strategy, | |
| 1688 | }); | |
| 1683 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ | |
| 1684 | .name = arg["-l".len..], | |
| 1685 | .query = .{ | |
| 1686 | .needed = false, | |
| 1687 | .weak = false, | |
| 1688 | .preferred_mode = lib_preferred_mode, | |
| 1689 | .search_strategy = lib_search_strategy, | |
| 1690 | .allow_so_scripts = allow_so_scripts, | |
| 1691 | }, | |
| 1692 | } }); | |
| 1689 | 1693 | } else if (mem.startsWith(u8, arg, "-needed-l")) { |
| 1690 | try create_module.system_libs.put(arena, arg["-needed-l".len..], .{ | |
| 1691 | .needed = true, | |
| 1692 | .weak = false, | |
| 1693 | .preferred_mode = lib_preferred_mode, | |
| 1694 | .search_strategy = lib_search_strategy, | |
| 1695 | }); | |
| 1694 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ | |
| 1695 | .name = arg["-needed-l".len..], | |
| 1696 | .query = .{ | |
| 1697 | .needed = true, | |
| 1698 | .weak = false, | |
| 1699 | .preferred_mode = lib_preferred_mode, | |
| 1700 | .search_strategy = lib_search_strategy, | |
| 1701 | .allow_so_scripts = allow_so_scripts, | |
| 1702 | }, | |
| 1703 | } }); | |
| 1696 | 1704 | } else if (mem.startsWith(u8, arg, "-weak-l")) { |
| 1697 | try create_module.system_libs.put(arena, arg["-weak-l".len..], .{ | |
| 1698 | .needed = false, | |
| 1699 | .weak = true, | |
| 1700 | .preferred_mode = lib_preferred_mode, | |
| 1701 | .search_strategy = lib_search_strategy, | |
| 1702 | }); | |
| 1705 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ | |
| 1706 | .name = arg["-weak-l".len..], | |
| 1707 | .query = .{ | |
| 1708 | .needed = false, | |
| 1709 | .weak = true, | |
| 1710 | .preferred_mode = lib_preferred_mode, | |
| 1711 | .search_strategy = lib_search_strategy, | |
| 1712 | .allow_so_scripts = allow_so_scripts, | |
| 1713 | }, | |
| 1714 | } }); | |
| 1703 | 1715 | } else if (mem.startsWith(u8, arg, "-D")) { |
| 1704 | 1716 | try cc_argv.append(arena, arg); |
| 1705 | 1717 | } else if (mem.startsWith(u8, arg, "-I")) { |
| ... | ... | @@ -1724,15 +1736,28 @@ fn buildOutputType( |
| 1724 | 1736 | fatal("unrecognized parameter: '{s}'", .{arg}); |
| 1725 | 1737 | } |
| 1726 | 1738 | } else switch (file_ext orelse Compilation.classifyFileExt(arg)) { |
| 1727 | .shared_library => { | |
| 1728 | try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) }); | |
| 1729 | create_module.opts.any_dyn_libs = true; | |
| 1730 | }, | |
| 1731 | .object, .static_library => { | |
| 1732 | try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) }); | |
| 1739 | .shared_library, .object, .static_library => { | |
| 1740 | try create_module.cli_link_inputs.append(arena, .{ .path_query = .{ | |
| 1741 | .path = Path.initCwd(arg), | |
| 1742 | .query = .{ | |
| 1743 | .preferred_mode = lib_preferred_mode, | |
| 1744 | .search_strategy = lib_search_strategy, | |
| 1745 | .allow_so_scripts = allow_so_scripts, | |
| 1746 | }, | |
| 1747 | } }); | |
| 1748 | // We do not set `any_dyn_libs` yet because a .so file | |
| 1749 | // may actually resolve to a GNU ld script which ends | |
| 1750 | // up being a static library. | |
| 1733 | 1751 | }, |
| 1734 | 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 | 1761 | contains_res_file = true; |
| 1737 | 1762 | }, |
| 1738 | 1763 | .manifest => { |
| ... | ... | @@ -1785,6 +1810,7 @@ fn buildOutputType( |
| 1785 | 1810 | // some functionality that depend on it, such as C++ exceptions and |
| 1786 | 1811 | // DWARF-based stack traces. |
| 1787 | 1812 | link_eh_frame_hdr = true; |
| 1813 | allow_so_scripts = true; | |
| 1788 | 1814 | |
| 1789 | 1815 | const COutMode = enum { |
| 1790 | 1816 | link, |
| ... | ... | @@ -1844,24 +1870,32 @@ fn buildOutputType( |
| 1844 | 1870 | .ext = file_ext, // duped while parsing the args. |
| 1845 | 1871 | }); |
| 1846 | 1872 | }, |
| 1847 | .shared_library => { | |
| 1848 | try create_module.link_objects.append(arena, .{ | |
| 1873 | .unknown, .object, .static_library, .shared_library => { | |
| 1874 | try create_module.cli_link_inputs.append(arena, .{ .path_query = .{ | |
| 1849 | 1875 | .path = Path.initCwd(it.only_arg), |
| 1850 | .must_link = must_link, | |
| 1851 | }); | |
| 1852 | create_module.opts.any_dyn_libs = true; | |
| 1853 | }, | |
| 1854 | .unknown, .object, .static_library => { | |
| 1855 | try create_module.link_objects.append(arena, .{ | |
| 1856 | .path = Path.initCwd(it.only_arg), | |
| 1857 | .must_link = must_link, | |
| 1858 | }); | |
| 1876 | .query = .{ | |
| 1877 | .must_link = must_link, | |
| 1878 | .needed = needed, | |
| 1879 | .preferred_mode = lib_preferred_mode, | |
| 1880 | .search_strategy = lib_search_strategy, | |
| 1881 | .allow_so_scripts = allow_so_scripts, | |
| 1882 | }, | |
| 1883 | } }); | |
| 1884 | // We do not set `any_dyn_libs` yet because a .so file | |
| 1885 | // may actually resolve to a GNU ld script which ends | |
| 1886 | // up being a static library. | |
| 1859 | 1887 | }, |
| 1860 | 1888 | .res => { |
| 1861 | try create_module.link_objects.append(arena, .{ | |
| 1889 | try create_module.cli_link_inputs.append(arena, .{ .path_query = .{ | |
| 1862 | 1890 | .path = Path.initCwd(it.only_arg), |
| 1863 | .must_link = must_link, | |
| 1864 | }); | |
| 1891 | .query = .{ | |
| 1892 | .must_link = must_link, | |
| 1893 | .needed = needed, | |
| 1894 | .preferred_mode = lib_preferred_mode, | |
| 1895 | .search_strategy = lib_search_strategy, | |
| 1896 | .allow_so_scripts = allow_so_scripts, | |
| 1897 | }, | |
| 1898 | } }); | |
| 1865 | 1899 | contains_res_file = true; |
| 1866 | 1900 | }, |
| 1867 | 1901 | .manifest => { |
| ... | ... | @@ -1893,19 +1927,21 @@ fn buildOutputType( |
| 1893 | 1927 | // -l :path/to/filename is used when callers need |
| 1894 | 1928 | // more control over what's in the resulting |
| 1895 | 1929 | // binary: no extra rpaths and DSO filename exactly |
| 1896 | // as provided. Hello, Go. | |
| 1897 | try create_module.link_objects.append(arena, .{ | |
| 1898 | .path = Path.initCwd(it.only_arg), | |
| 1899 | .must_link = must_link, | |
| 1900 | .loption = true, | |
| 1901 | }); | |
| 1930 | // as provided. CGo compilation depends on this. | |
| 1931 | try create_module.cli_link_inputs.append(arena, .{ .dso_exact = .{ | |
| 1932 | .name = it.only_arg, | |
| 1933 | } }); | |
| 1902 | 1934 | } else { |
| 1903 | try create_module.system_libs.put(arena, it.only_arg, .{ | |
| 1904 | .needed = needed, | |
| 1905 | .weak = false, | |
| 1906 | .preferred_mode = lib_preferred_mode, | |
| 1907 | .search_strategy = lib_search_strategy, | |
| 1908 | }); | |
| 1935 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ | |
| 1936 | .name = it.only_arg, | |
| 1937 | .query = .{ | |
| 1938 | .needed = needed, | |
| 1939 | .weak = false, | |
| 1940 | .preferred_mode = lib_preferred_mode, | |
| 1941 | .search_strategy = lib_search_strategy, | |
| 1942 | .allow_so_scripts = allow_so_scripts, | |
| 1943 | }, | |
| 1944 | } }); | |
| 1909 | 1945 | } |
| 1910 | 1946 | }, |
| 1911 | 1947 | .ignore => {}, |
| ... | ... | @@ -2174,12 +2210,16 @@ fn buildOutputType( |
| 2174 | 2210 | }, |
| 2175 | 2211 | .force_load_objc => force_load_objc = true, |
| 2176 | 2212 | .mingw_unicode_entry_point => mingw_unicode_entry_point = true, |
| 2177 | .weak_library => try create_module.system_libs.put(arena, it.only_arg, .{ | |
| 2178 | .needed = false, | |
| 2179 | .weak = true, | |
| 2180 | .preferred_mode = lib_preferred_mode, | |
| 2181 | .search_strategy = lib_search_strategy, | |
| 2182 | }), | |
| 2213 | .weak_library => try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ | |
| 2214 | .name = it.only_arg, | |
| 2215 | .query = .{ | |
| 2216 | .needed = false, | |
| 2217 | .weak = true, | |
| 2218 | .preferred_mode = lib_preferred_mode, | |
| 2219 | .search_strategy = lib_search_strategy, | |
| 2220 | .allow_so_scripts = allow_so_scripts, | |
| 2221 | }, | |
| 2222 | } }), | |
| 2183 | 2223 | .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }), |
| 2184 | 2224 | .headerpad_max_install_names => headerpad_max_install_names = true, |
| 2185 | 2225 | .compress_debug_sections => { |
| ... | ... | @@ -2482,26 +2522,38 @@ fn buildOutputType( |
| 2482 | 2522 | } else if (mem.eql(u8, arg, "-needed_framework")) { |
| 2483 | 2523 | try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true }); |
| 2484 | 2524 | } else if (mem.eql(u8, arg, "-needed_library")) { |
| 2485 | try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{ | |
| 2486 | .weak = false, | |
| 2487 | .needed = true, | |
| 2488 | .preferred_mode = lib_preferred_mode, | |
| 2489 | .search_strategy = lib_search_strategy, | |
| 2490 | }); | |
| 2525 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ | |
| 2526 | .name = linker_args_it.nextOrFatal(), | |
| 2527 | .query = .{ | |
| 2528 | .weak = false, | |
| 2529 | .needed = true, | |
| 2530 | .preferred_mode = lib_preferred_mode, | |
| 2531 | .search_strategy = lib_search_strategy, | |
| 2532 | .allow_so_scripts = allow_so_scripts, | |
| 2533 | }, | |
| 2534 | } }); | |
| 2491 | 2535 | } else if (mem.startsWith(u8, arg, "-weak-l")) { |
| 2492 | try create_module.system_libs.put(arena, arg["-weak-l".len..], .{ | |
| 2493 | .weak = true, | |
| 2494 | .needed = false, | |
| 2495 | .preferred_mode = lib_preferred_mode, | |
| 2496 | .search_strategy = lib_search_strategy, | |
| 2497 | }); | |
| 2536 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ | |
| 2537 | .name = arg["-weak-l".len..], | |
| 2538 | .query = .{ | |
| 2539 | .weak = true, | |
| 2540 | .needed = false, | |
| 2541 | .preferred_mode = lib_preferred_mode, | |
| 2542 | .search_strategy = lib_search_strategy, | |
| 2543 | .allow_so_scripts = allow_so_scripts, | |
| 2544 | }, | |
| 2545 | } }); | |
| 2498 | 2546 | } else if (mem.eql(u8, arg, "-weak_library")) { |
| 2499 | try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{ | |
| 2500 | .weak = true, | |
| 2501 | .needed = false, | |
| 2502 | .preferred_mode = lib_preferred_mode, | |
| 2503 | .search_strategy = lib_search_strategy, | |
| 2504 | }); | |
| 2547 | try create_module.cli_link_inputs.append(arena, .{ .name_query = .{ | |
| 2548 | .name = linker_args_it.nextOrFatal(), | |
| 2549 | .query = .{ | |
| 2550 | .weak = true, | |
| 2551 | .needed = false, | |
| 2552 | .preferred_mode = lib_preferred_mode, | |
| 2553 | .search_strategy = lib_search_strategy, | |
| 2554 | .allow_so_scripts = allow_so_scripts, | |
| 2555 | }, | |
| 2556 | } }); | |
| 2505 | 2557 | } else if (mem.eql(u8, arg, "-compatibility_version")) { |
| 2506 | 2558 | const compat_version = linker_args_it.nextOrFatal(); |
| 2507 | 2559 | compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| { |
| ... | ... | @@ -2532,10 +2584,14 @@ fn buildOutputType( |
| 2532 | 2584 | } else if (mem.eql(u8, arg, "-install_name")) { |
| 2533 | 2585 | install_name = linker_args_it.nextOrFatal(); |
| 2534 | 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 | 2588 | .path = Path.initCwd(linker_args_it.nextOrFatal()), |
| 2537 | .must_link = true, | |
| 2538 | }); | |
| 2589 | .query = .{ | |
| 2590 | .must_link = true, | |
| 2591 | .preferred_mode = .static, | |
| 2592 | .search_strategy = .no_fallback, | |
| 2593 | }, | |
| 2594 | } }); | |
| 2539 | 2595 | } else if (mem.eql(u8, arg, "-hash-style") or |
| 2540 | 2596 | mem.eql(u8, arg, "--hash-style")) |
| 2541 | 2597 | { |
| ... | ... | @@ -2665,7 +2721,7 @@ fn buildOutputType( |
| 2665 | 2721 | }, |
| 2666 | 2722 | } |
| 2667 | 2723 | if (create_module.c_source_files.items.len == 0 and |
| 2668 | create_module.link_objects.items.len == 0 and | |
| 2724 | !anyObjectLinkInputs(create_module.cli_link_inputs.items) and | |
| 2669 | 2725 | root_src_file == null) |
| 2670 | 2726 | { |
| 2671 | 2727 | // For example `zig cc` and no args should print the "no input files" message. |
| ... | ... | @@ -2707,8 +2763,11 @@ fn buildOutputType( |
| 2707 | 2763 | if (create_module.c_source_files.items.len >= 1) |
| 2708 | 2764 | break :b create_module.c_source_files.items[0].src_path; |
| 2709 | 2765 | |
| 2710 | if (create_module.link_objects.items.len >= 1) | |
| 2711 | break :b create_module.link_objects.items[0].path.sub_path; | |
| 2766 | for (create_module.cli_link_inputs.items) |unresolved_link_input| switch (unresolved_link_input) { | |
| 2767 | // Intentionally includes dynamic libraries provided by file path. | |
| 2768 | .path_query => |pq| break :b pq.path.sub_path, | |
| 2769 | else => continue, | |
| 2770 | }; | |
| 2712 | 2771 | |
| 2713 | 2772 | if (emit_bin == .yes) |
| 2714 | 2773 | break :b emit_bin.yes; |
| ... | ... | @@ -2794,7 +2853,7 @@ fn buildOutputType( |
| 2794 | 2853 | fatal("unable to find zig self exe path: {s}", .{@errorName(err)}); |
| 2795 | 2854 | }; |
| 2796 | 2855 | |
| 2797 | var zig_lib_directory: Compilation.Directory = d: { | |
| 2856 | var zig_lib_directory: Directory = d: { | |
| 2798 | 2857 | if (override_lib_dir) |unresolved_lib_dir| { |
| 2799 | 2858 | const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir); |
| 2800 | 2859 | break :d .{ |
| ... | ... | @@ -2815,7 +2874,7 @@ fn buildOutputType( |
| 2815 | 2874 | }; |
| 2816 | 2875 | defer zig_lib_directory.handle.close(); |
| 2817 | 2876 | |
| 2818 | var global_cache_directory: Compilation.Directory = l: { | |
| 2877 | var global_cache_directory: Directory = l: { | |
| 2819 | 2878 | if (override_global_cache_dir) |p| { |
| 2820 | 2879 | break :l .{ |
| 2821 | 2880 | .handle = try fs.cwd().makeOpenPath(p, .{}), |
| ... | ... | @@ -2845,7 +2904,7 @@ fn buildOutputType( |
| 2845 | 2904 | |
| 2846 | 2905 | var builtin_modules: std.StringHashMapUnmanaged(*Package.Module) = .empty; |
| 2847 | 2906 | // `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 | 2908 | for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| { |
| 2850 | 2909 | if (cli_mod.resolved == null) |
| 2851 | 2910 | fatal("module '{s}' declared but not used", .{key}); |
| ... | ... | @@ -2939,7 +2998,6 @@ fn buildOutputType( |
| 2939 | 2998 | } |
| 2940 | 2999 | } |
| 2941 | 3000 | |
| 2942 | // We now repeat part of the process for frameworks. | |
| 2943 | 3001 | var resolved_frameworks = std.ArrayList(Compilation.Framework).init(arena); |
| 2944 | 3002 | |
| 2945 | 3003 | if (create_module.frameworks.keys().len > 0) { |
| ... | ... | @@ -2996,7 +3054,7 @@ fn buildOutputType( |
| 2996 | 3054 | const total_obj_count = create_module.c_source_files.items.len + |
| 2997 | 3055 | @intFromBool(root_src_file != null) + |
| 2998 | 3056 | create_module.rc_source_files.items.len + |
| 2999 | create_module.link_objects.items.len; | |
| 3057 | link.countObjectInputs(create_module.link_inputs.items); | |
| 3000 | 3058 | if (total_obj_count > 1) { |
| 3001 | 3059 | fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)}); |
| 3002 | 3060 | } |
| ... | ... | @@ -3212,7 +3270,7 @@ fn buildOutputType( |
| 3212 | 3270 | var cleanup_local_cache_dir: ?fs.Dir = null; |
| 3213 | 3271 | defer if (cleanup_local_cache_dir) |*dir| dir.close(); |
| 3214 | 3272 | |
| 3215 | var local_cache_directory: Compilation.Directory = l: { | |
| 3273 | var local_cache_directory: Directory = l: { | |
| 3216 | 3274 | if (override_local_cache_dir) |local_cache_dir_path| { |
| 3217 | 3275 | const dir = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}); |
| 3218 | 3276 | cleanup_local_cache_dir = dir; |
| ... | ... | @@ -3349,7 +3407,7 @@ fn buildOutputType( |
| 3349 | 3407 | .emit_llvm_bc = emit_llvm_bc_resolved.data, |
| 3350 | 3408 | .emit_docs = emit_docs_resolved.data, |
| 3351 | 3409 | .emit_implib = emit_implib_resolved.data, |
| 3352 | .lib_dirs = create_module.lib_dirs.items, | |
| 3410 | .lib_directories = create_module.lib_directories.items, | |
| 3353 | 3411 | .rpath_list = create_module.rpath_list.items, |
| 3354 | 3412 | .symbol_wrap_set = symbol_wrap_set, |
| 3355 | 3413 | .c_source_files = create_module.c_source_files.items, |
| ... | ... | @@ -3357,11 +3415,10 @@ fn buildOutputType( |
| 3357 | 3415 | .manifest_file = manifest_file, |
| 3358 | 3416 | .rc_includes = rc_includes, |
| 3359 | 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 | 3419 | .framework_dirs = create_module.framework_dirs.items, |
| 3362 | 3420 | .frameworks = resolved_frameworks.items, |
| 3363 | .system_lib_names = create_module.resolved_system_libs.items(.name), | |
| 3364 | .system_lib_infos = create_module.resolved_system_libs.items(.lib), | |
| 3421 | .windows_lib_names = create_module.windows_libs.keys(), | |
| 3365 | 3422 | .wasi_emulated_libs = create_module.wasi_emulated_libs.items, |
| 3366 | 3423 | .want_compiler_rt = want_compiler_rt, |
| 3367 | 3424 | .hash_style = hash_style, |
| ... | ... | @@ -3630,12 +3687,14 @@ const CreateModule = struct { |
| 3630 | 3687 | /// This one is used while collecting CLI options. The set of libs is used |
| 3631 | 3688 | /// directly after computing the target and used to compute link_libc, |
| 3632 | 3689 | /// link_libcpp, and then the libraries are filtered into |
| 3633 | /// `external_system_libs` and `resolved_system_libs`. | |
| 3634 | system_libs: std.StringArrayHashMapUnmanaged(SystemLib), | |
| 3635 | resolved_system_libs: std.MultiArrayList(struct { | |
| 3636 | name: []const u8, | |
| 3637 | lib: Compilation.SystemLib, | |
| 3638 | }), | |
| 3690 | /// `unresolved_linker_inputs` and `windows_libs`. | |
| 3691 | cli_link_inputs: std.ArrayListUnmanaged(link.UnresolvedInput), | |
| 3692 | windows_libs: std.StringArrayHashMapUnmanaged(void), | |
| 3693 | /// The local variable `unresolved_link_inputs` is fed into library | |
| 3694 | /// resolution, mutating the input array, and producing this data as | |
| 3695 | /// output. Allocated with gpa. | |
| 3696 | link_inputs: std.ArrayListUnmanaged(link.Input), | |
| 3697 | ||
| 3639 | 3698 | wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile), |
| 3640 | 3699 | |
| 3641 | 3700 | c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile), |
| ... | ... | @@ -3646,7 +3705,7 @@ const CreateModule = struct { |
| 3646 | 3705 | /// CPU features. |
| 3647 | 3706 | llvm_m_args: std.ArrayListUnmanaged([]const u8), |
| 3648 | 3707 | sysroot: ?[]const u8, |
| 3649 | lib_dirs: std.ArrayListUnmanaged([]const u8), | |
| 3708 | lib_directories: std.ArrayListUnmanaged(Directory), | |
| 3650 | 3709 | lib_dir_args: std.ArrayListUnmanaged([]const u8), |
| 3651 | 3710 | libc_installation: ?LibCInstallation, |
| 3652 | 3711 | want_native_include_dirs: bool, |
| ... | ... | @@ -3656,7 +3715,6 @@ const CreateModule = struct { |
| 3656 | 3715 | rpath_list: std.ArrayListUnmanaged([]const u8), |
| 3657 | 3716 | each_lib_rpath: ?bool, |
| 3658 | 3717 | libc_paths_file: ?[]const u8, |
| 3659 | link_objects: std.ArrayListUnmanaged(Compilation.LinkObject), | |
| 3660 | 3718 | }; |
| 3661 | 3719 | |
| 3662 | 3720 | fn createModule( |
| ... | ... | @@ -3667,6 +3725,7 @@ fn createModule( |
| 3667 | 3725 | parent: ?*Package.Module, |
| 3668 | 3726 | zig_lib_directory: Cache.Directory, |
| 3669 | 3727 | builtin_modules: *std.StringHashMapUnmanaged(*Package.Module), |
| 3728 | color: std.zig.Color, | |
| 3670 | 3729 | ) Allocator.Error!*Package.Module { |
| 3671 | 3730 | const cli_mod = &create_module.modules.values()[index]; |
| 3672 | 3731 | if (cli_mod.resolved) |m| return m; |
| ... | ... | @@ -3760,85 +3819,83 @@ fn createModule( |
| 3760 | 3819 | // First, remove libc, libc++, and compiler_rt libraries from the system libraries list. |
| 3761 | 3820 | // We need to know whether the set of system libraries contains anything besides these |
| 3762 | 3821 | // to decide whether to trigger native path detection logic. |
| 3763 | var external_system_libs: std.MultiArrayList(struct { | |
| 3764 | name: []const u8, | |
| 3765 | info: SystemLib, | |
| 3766 | }) = .{}; | |
| 3767 | for (create_module.system_libs.keys(), create_module.system_libs.values()) |lib_name, info| { | |
| 3768 | if (std.zig.target.isLibCLibName(target, lib_name)) { | |
| 3769 | create_module.opts.link_libc = true; | |
| 3770 | continue; | |
| 3771 | } | |
| 3772 | if (std.zig.target.isLibCxxLibName(target, lib_name)) { | |
| 3773 | create_module.opts.link_libcpp = true; | |
| 3774 | continue; | |
| 3775 | } | |
| 3776 | switch (target_util.classifyCompilerRtLibName(target, lib_name)) { | |
| 3777 | .none => {}, | |
| 3778 | .only_libunwind, .both => { | |
| 3779 | create_module.opts.link_libunwind = true; | |
| 3822 | // Preserves linker input order. | |
| 3823 | var unresolved_link_inputs: std.ArrayListUnmanaged(link.UnresolvedInput) = .empty; | |
| 3824 | try unresolved_link_inputs.ensureUnusedCapacity(arena, create_module.cli_link_inputs.items.len); | |
| 3825 | var any_name_queries_remaining = false; | |
| 3826 | for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) { | |
| 3827 | .name_query => |nq| { | |
| 3828 | const lib_name = nq.name; | |
| 3829 | if (std.zig.target.isLibCLibName(target, lib_name)) { | |
| 3830 | create_module.opts.link_libc = true; | |
| 3780 | 3831 | continue; |
| 3781 | }, | |
| 3782 | .only_compiler_rt => { | |
| 3783 | warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name}); | |
| 3832 | } | |
| 3833 | if (std.zig.target.isLibCxxLibName(target, lib_name)) { | |
| 3834 | create_module.opts.link_libcpp = true; | |
| 3784 | 3835 | continue; |
| 3785 | }, | |
| 3786 | } | |
| 3836 | } | |
| 3837 | switch (target_util.classifyCompilerRtLibName(target, lib_name)) { | |
| 3838 | .none => {}, | |
| 3839 | .only_libunwind, .both => { | |
| 3840 | create_module.opts.link_libunwind = true; | |
| 3841 | continue; | |
| 3842 | }, | |
| 3843 | .only_compiler_rt => { | |
| 3844 | warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name}); | |
| 3845 | continue; | |
| 3846 | }, | |
| 3847 | } | |
| 3787 | 3848 | |
| 3788 | if (target.isMinGW()) { | |
| 3789 | const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| { | |
| 3790 | fatal("failed to check zig installation for DLL import libs: {s}", .{ | |
| 3791 | @errorName(err), | |
| 3792 | }); | |
| 3793 | }; | |
| 3794 | if (exists) { | |
| 3795 | try create_module.resolved_system_libs.append(arena, .{ | |
| 3796 | .name = lib_name, | |
| 3797 | .lib = .{ | |
| 3798 | .needed = true, | |
| 3799 | .weak = false, | |
| 3800 | .path = null, | |
| 3801 | }, | |
| 3802 | }); | |
| 3803 | continue; | |
| 3849 | if (target.isMinGW()) { | |
| 3850 | const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| { | |
| 3851 | fatal("failed to check zig installation for DLL import libs: {s}", .{ | |
| 3852 | @errorName(err), | |
| 3853 | }); | |
| 3854 | }; | |
| 3855 | if (exists) { | |
| 3856 | try create_module.windows_libs.put(arena, lib_name, {}); | |
| 3857 | continue; | |
| 3858 | } | |
| 3804 | 3859 | } |
| 3805 | } | |
| 3806 | 3860 | |
| 3807 | if (fs.path.isAbsolute(lib_name)) { | |
| 3808 | fatal("cannot use absolute path as a system library: {s}", .{lib_name}); | |
| 3809 | } | |
| 3861 | if (fs.path.isAbsolute(lib_name)) { | |
| 3862 | fatal("cannot use absolute path as a system library: {s}", .{lib_name}); | |
| 3863 | } | |
| 3810 | 3864 | |
| 3811 | if (target.os.tag == .wasi) { | |
| 3812 | if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| { | |
| 3813 | try create_module.wasi_emulated_libs.append(arena, crt_file); | |
| 3814 | continue; | |
| 3865 | if (target.os.tag == .wasi) { | |
| 3866 | if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| { | |
| 3867 | try create_module.wasi_emulated_libs.append(arena, crt_file); | |
| 3868 | continue; | |
| 3869 | } | |
| 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. | |
| 3817 | 3878 | |
| 3818 | try external_system_libs.append(arena, .{ | |
| 3819 | .name = lib_name, | |
| 3820 | .info = info, | |
| 3821 | }); | |
| 3822 | } | |
| 3823 | // After this point, external_system_libs is used instead of system_libs. | |
| 3824 | if (external_system_libs.len != 0) | |
| 3825 | create_module.want_native_include_dirs = true; | |
| 3879 | if (any_name_queries_remaining) create_module.want_native_include_dirs = true; | |
| 3826 | 3880 | |
| 3827 | 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 | 3883 | if (create_module.sysroot) |root| { |
| 3829 | try create_module.lib_dirs.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len * 2); | |
| 3830 | for (create_module.lib_dir_args.items) |dir| { | |
| 3831 | if (fs.path.isAbsolute(dir)) { | |
| 3832 | const stripped_dir = dir[fs.path.diskDesignator(dir).len..]; | |
| 3884 | for (create_module.lib_dir_args.items) |lib_dir_arg| { | |
| 3885 | if (fs.path.isAbsolute(lib_dir_arg)) { | |
| 3886 | const stripped_dir = lib_dir_arg[fs.path.diskDesignator(lib_dir_arg).len..]; | |
| 3833 | 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 | 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. | |
| 3842 | 3899 | |
| 3843 | 3900 | if (resolved_target.is_native_os and target.isDarwin()) { |
| 3844 | 3901 | // If we want to link against frameworks, we need system headers. |
| ... | ... | @@ -3847,7 +3904,10 @@ fn createModule( |
| 3847 | 3904 | } |
| 3848 | 3905 | |
| 3849 | 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 | } |
| 3852 | 3912 | |
| 3853 | 3913 | // Trigger native system library path detection if necessary. |
| ... | ... | @@ -3865,8 +3925,10 @@ fn createModule( |
| 3865 | 3925 | create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena); |
| 3866 | 3926 | |
| 3867 | 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 | 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 | } |
| 3871 | 3933 | |
| 3872 | 3934 | if (create_module.libc_paths_file) |paths_file| { |
| ... | ... | @@ -3878,7 +3940,7 @@ fn createModule( |
| 3878 | 3940 | } |
| 3879 | 3941 | |
| 3880 | 3942 | 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 | 3945 | if (create_module.libc_installation == null) { |
| 3884 | 3946 | create_module.libc_installation = LibCInstallation.findNative(.{ |
| ... | ... | @@ -3889,181 +3951,31 @@ fn createModule( |
| 3889 | 3951 | fatal("unable to find native libc installation: {s}", .{@errorName(err)}); |
| 3890 | 3952 | }; |
| 3891 | 3953 | |
| 3892 | try create_module.lib_dirs.appendSlice(arena, &.{ | |
| 3893 | create_module.libc_installation.?.msvc_lib_dir.?, | |
| 3894 | create_module.libc_installation.?.kernel32_lib_dir.?, | |
| 3895 | }); | |
| 3896 | } | |
| 3897 | } | |
| 3898 | ||
| 3899 | // If any libs in this list are statically provided, we omit them from the | |
| 3900 | // resolved list and populate the link_objects array instead. | |
| 3901 | { | |
| 3902 | var test_path = std.ArrayList(u8).init(gpa); | |
| 3903 | defer test_path.deinit(); | |
| 3904 | ||
| 3905 | var checked_paths = std.ArrayList(u8).init(gpa); | |
| 3906 | defer checked_paths.deinit(); | |
| 3907 | ||
| 3908 | var failed_libs = std.ArrayList(struct { | |
| 3909 | name: []const u8, | |
| 3910 | strategy: SystemLib.SearchStrategy, | |
| 3911 | checked_paths: []const u8, | |
| 3912 | preferred_mode: std.builtin.LinkMode, | |
| 3913 | }).init(arena); | |
| 3914 | ||
| 3915 | syslib: for (external_system_libs.items(.name), external_system_libs.items(.info)) |lib_name, info| { | |
| 3916 | // Checked in the first pass above while looking for libc libraries. | |
| 3917 | assert(!fs.path.isAbsolute(lib_name)); | |
| 3918 | ||
| 3919 | checked_paths.clearRetainingCapacity(); | |
| 3920 | ||
| 3921 | switch (info.search_strategy) { | |
| 3922 | .mode_first, .no_fallback => { | |
| 3923 | // check for preferred mode | |
| 3924 | for (create_module.lib_dirs.items) |lib_dir_path| { | |
| 3925 | if (try accessLibPath( | |
| 3926 | &test_path, | |
| 3927 | &checked_paths, | |
| 3928 | lib_dir_path, | |
| 3929 | lib_name, | |
| 3930 | target, | |
| 3931 | info.preferred_mode, | |
| 3932 | )) { | |
| 3933 | const path = Path.initCwd(try arena.dupe(u8, test_path.items)); | |
| 3934 | switch (info.preferred_mode) { | |
| 3935 | .static => try create_module.link_objects.append(arena, .{ .path = path }), | |
| 3936 | .dynamic => try create_module.resolved_system_libs.append(arena, .{ | |
| 3937 | .name = lib_name, | |
| 3938 | .lib = .{ | |
| 3939 | .needed = info.needed, | |
| 3940 | .weak = info.weak, | |
| 3941 | .path = path, | |
| 3942 | }, | |
| 3943 | }), | |
| 3944 | } | |
| 3945 | continue :syslib; | |
| 3946 | } | |
| 3947 | } | |
| 3948 | // check for fallback mode | |
| 3949 | if (info.search_strategy == .no_fallback) { | |
| 3950 | try failed_libs.append(.{ | |
| 3951 | .name = lib_name, | |
| 3952 | .strategy = info.search_strategy, | |
| 3953 | .checked_paths = try arena.dupe(u8, checked_paths.items), | |
| 3954 | .preferred_mode = info.preferred_mode, | |
| 3955 | }); | |
| 3956 | continue :syslib; | |
| 3957 | } | |
| 3958 | for (create_module.lib_dirs.items) |lib_dir_path| { | |
| 3959 | if (try accessLibPath( | |
| 3960 | &test_path, | |
| 3961 | &checked_paths, | |
| 3962 | lib_dir_path, | |
| 3963 | lib_name, | |
| 3964 | target, | |
| 3965 | info.fallbackMode(), | |
| 3966 | )) { | |
| 3967 | const path = Path.initCwd(try arena.dupe(u8, test_path.items)); | |
| 3968 | switch (info.fallbackMode()) { | |
| 3969 | .static => try create_module.link_objects.append(arena, .{ .path = path }), | |
| 3970 | .dynamic => try create_module.resolved_system_libs.append(arena, .{ | |
| 3971 | .name = lib_name, | |
| 3972 | .lib = .{ | |
| 3973 | .needed = info.needed, | |
| 3974 | .weak = info.weak, | |
| 3975 | .path = path, | |
| 3976 | }, | |
| 3977 | }), | |
| 3978 | } | |
| 3979 | continue :syslib; | |
| 3980 | } | |
| 3981 | } | |
| 3982 | try failed_libs.append(.{ | |
| 3983 | .name = lib_name, | |
| 3984 | .strategy = info.search_strategy, | |
| 3985 | .checked_paths = try arena.dupe(u8, checked_paths.items), | |
| 3986 | .preferred_mode = info.preferred_mode, | |
| 3987 | }); | |
| 3988 | continue :syslib; | |
| 3989 | }, | |
| 3990 | .paths_first => { | |
| 3991 | for (create_module.lib_dirs.items) |lib_dir_path| { | |
| 3992 | // check for preferred mode | |
| 3993 | if (try accessLibPath( | |
| 3994 | &test_path, | |
| 3995 | &checked_paths, | |
| 3996 | lib_dir_path, | |
| 3997 | lib_name, | |
| 3998 | target, | |
| 3999 | info.preferred_mode, | |
| 4000 | )) { | |
| 4001 | const path = Path.initCwd(try arena.dupe(u8, test_path.items)); | |
| 4002 | switch (info.preferred_mode) { | |
| 4003 | .static => try create_module.link_objects.append(arena, .{ .path = path }), | |
| 4004 | .dynamic => try create_module.resolved_system_libs.append(arena, .{ | |
| 4005 | .name = lib_name, | |
| 4006 | .lib = .{ | |
| 4007 | .needed = info.needed, | |
| 4008 | .weak = info.weak, | |
| 4009 | .path = path, | |
| 4010 | }, | |
| 4011 | }), | |
| 4012 | } | |
| 4013 | continue :syslib; | |
| 4014 | } | |
| 4015 | ||
| 4016 | // check for fallback mode | |
| 4017 | if (try accessLibPath( | |
| 4018 | &test_path, | |
| 4019 | &checked_paths, | |
| 4020 | lib_dir_path, | |
| 4021 | lib_name, | |
| 4022 | target, | |
| 4023 | info.fallbackMode(), | |
| 4024 | )) { | |
| 4025 | const path = Path.initCwd(try arena.dupe(u8, test_path.items)); | |
| 4026 | switch (info.fallbackMode()) { | |
| 4027 | .static => try create_module.link_objects.append(arena, .{ .path = path }), | |
| 4028 | .dynamic => try create_module.resolved_system_libs.append(arena, .{ | |
| 4029 | .name = lib_name, | |
| 4030 | .lib = .{ | |
| 4031 | .needed = info.needed, | |
| 4032 | .weak = info.weak, | |
| 4033 | .path = path, | |
| 4034 | }, | |
| 4035 | }), | |
| 4036 | } | |
| 4037 | continue :syslib; | |
| 4038 | } | |
| 4039 | } | |
| 4040 | try failed_libs.append(.{ | |
| 4041 | .name = lib_name, | |
| 4042 | .strategy = info.search_strategy, | |
| 4043 | .checked_paths = try arena.dupe(u8, checked_paths.items), | |
| 4044 | .preferred_mode = info.preferred_mode, | |
| 4045 | }); | |
| 4046 | continue :syslib; | |
| 4047 | }, | |
| 4048 | } | |
| 4049 | @compileError("unreachable"); | |
| 4050 | } | |
| 4051 | ||
| 4052 | if (failed_libs.items.len > 0) { | |
| 4053 | for (failed_libs.items) |f| { | |
| 4054 | const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths; | |
| 4055 | std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{ | |
| 4056 | @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths, | |
| 4057 | }); | |
| 4058 | } | |
| 4059 | process.exit(1); | |
| 3954 | try create_module.lib_directories.ensureUnusedCapacity(arena, 2); | |
| 3955 | addLibDirectoryWarn(&create_module.lib_directories, create_module.libc_installation.?.msvc_lib_dir.?); | |
| 3956 | addLibDirectoryWarn(&create_module.lib_directories, create_module.libc_installation.?.kernel32_lib_dir.?); | |
| 4060 | 3957 | } |
| 4061 | 3958 | } |
| 4062 | // After this point, create_module.resolved_system_libs is used instead of | |
| 4063 | // create_module.external_system_libs. | |
| 4064 | 3959 | |
| 4065 | if (create_module.resolved_system_libs.len != 0) | |
| 4066 | create_module.opts.any_dyn_libs = true; | |
| 3960 | // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`. | |
| 3961 | link.resolveInputs( | |
| 3962 | gpa, | |
| 3963 | arena, | |
| 3964 | target, | |
| 3965 | &unresolved_link_inputs, | |
| 3966 | &create_module.link_inputs, | |
| 3967 | create_module.lib_directories.items, | |
| 3968 | color, | |
| 3969 | ) catch |err| fatal("failed to resolve link inputs: {s}", .{@errorName(err)}); | |
| 3970 | ||
| 3971 | if (create_module.windows_libs.count() != 0) create_module.opts.any_dyn_libs = true; | |
| 3972 | if (!create_module.opts.any_dyn_libs) for (create_module.link_inputs.items) |item| switch (item) { | |
| 3973 | .dso, .dso_exact => { | |
| 3974 | create_module.opts.any_dyn_libs = true; | |
| 3975 | break; | |
| 3976 | }, | |
| 3977 | else => {}, | |
| 3978 | }; | |
| 4067 | 3979 | |
| 4068 | 3980 | create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) { |
| 4069 | 3981 | error.WasiExecModelRequiresWasi => fatal("only WASI OS targets support execution model", .{}), |
| ... | ... | @@ -4131,7 +4043,7 @@ fn createModule( |
| 4131 | 4043 | for (cli_mod.deps) |dep| { |
| 4132 | 4044 | const dep_index = create_module.modules.getIndex(dep.value) orelse |
| 4133 | 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 | 4047 | try mod.deps.put(arena, dep.key, dep_mod); |
| 4136 | 4048 | } |
| 4137 | 4049 | |
| ... | ... | @@ -4996,7 +4908,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 4996 | 4908 | |
| 4997 | 4909 | process.raiseFileDescriptorLimit(); |
| 4998 | 4910 | |
| 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 | 4912 | .path = lib_dir, |
| 5001 | 4913 | .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| { |
| 5002 | 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 | 4927 | }); |
| 5016 | 4928 | child_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path; |
| 5017 | 4929 | |
| 5018 | var global_cache_directory: Compilation.Directory = l: { | |
| 4930 | var global_cache_directory: Directory = l: { | |
| 5019 | 4931 | const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); |
| 5020 | 4932 | break :l .{ |
| 5021 | 4933 | .handle = try fs.cwd().makeOpenPath(p, .{}), |
| ... | ... | @@ -5026,7 +4938,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5026 | 4938 | |
| 5027 | 4939 | child_argv.items[argv_index_global_cache_dir] = global_cache_directory.path orelse cwd_path; |
| 5028 | 4940 | |
| 5029 | var local_cache_directory: Compilation.Directory = l: { | |
| 4941 | var local_cache_directory: Directory = l: { | |
| 5030 | 4942 | if (override_local_cache_dir) |local_cache_dir_path| { |
| 5031 | 4943 | break :l .{ |
| 5032 | 4944 | .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}), |
| ... | ... | @@ -5460,7 +5372,7 @@ fn jitCmd( |
| 5460 | 5372 | const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); |
| 5461 | 5373 | const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); |
| 5462 | 5374 | |
| 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 | 5376 | .path = lib_dir, |
| 5465 | 5377 | .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| { |
| 5466 | 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 | 5382 | }; |
| 5471 | 5383 | defer zig_lib_directory.handle.close(); |
| 5472 | 5384 | |
| 5473 | var global_cache_directory: Compilation.Directory = l: { | |
| 5385 | var global_cache_directory: Directory = l: { | |
| 5474 | 5386 | const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); |
| 5475 | 5387 | break :l .{ |
| 5476 | 5388 | .handle = try fs.cwd().makeOpenPath(p, .{}), |
| ... | ... | @@ -6857,86 +6769,6 @@ const ClangSearchSanitizer = struct { |
| 6857 | 6769 | }; |
| 6858 | 6770 | }; |
| 6859 | 6771 | |
| 6860 | fn 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 | ||
| 6940 | 6772 | fn accessFrameworkPath( |
| 6941 | 6773 | test_path: *std.ArrayList(u8), |
| 6942 | 6774 | checked_paths: *std.ArrayList(u8), |
| ... | ... | @@ -7057,7 +6889,7 @@ fn cmdFetch( |
| 7057 | 6889 | }); |
| 7058 | 6890 | defer root_prog_node.end(); |
| 7059 | 6891 | |
| 7060 | var global_cache_directory: Compilation.Directory = l: { | |
| 6892 | var global_cache_directory: Directory = l: { | |
| 7061 | 6893 | const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); |
| 7062 | 6894 | break :l .{ |
| 7063 | 6895 | .handle = try fs.cwd().makeOpenPath(p, .{}), |
| ... | ... | @@ -7634,3 +7466,24 @@ fn handleModArg( |
| 7634 | 7466 | c_source_files_owner_index.* = create_module.c_source_files.items.len; |
| 7635 | 7467 | rc_source_files_owner_index.* = create_module.rc_source_files.items.len; |
| 7636 | 7468 | } |
| 7469 | ||
| 7470 | fn 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 | ||
| 7481 | fn 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 | 19 | libc_so, |
| 20 | 20 | }; |
| 21 | 21 | |
| 22 | pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void { | |
| 22 | pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Progress.Node) !void { | |
| 23 | 23 | if (!build_options.have_llvm) { |
| 24 | 24 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 25 | 25 | } |
| ... | ... | @@ -28,7 +28,7 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre |
| 28 | 28 | defer arena_allocator.deinit(); |
| 29 | 29 | const arena = arena_allocator.allocator(); |
| 30 | 30 | |
| 31 | switch (crt_file) { | |
| 31 | switch (in_crt_file) { | |
| 32 | 32 | .crti_o => { |
| 33 | 33 | var args = std.ArrayList([]const u8).init(arena); |
| 34 | 34 | try addCcArgs(comp, arena, &args, false); |
| ... | ... | @@ -195,8 +195,9 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre |
| 195 | 195 | .libc_so => { |
| 196 | 196 | const optimize_mode = comp.compilerRtOptMode(); |
| 197 | 197 | const strip = comp.compilerRtStrip(); |
| 198 | const output_mode: std.builtin.OutputMode = .Lib; | |
| 198 | 199 | const config = try Compilation.Config.resolve(.{ |
| 199 | .output_mode = .Lib, | |
| 200 | .output_mode = output_mode, | |
| 200 | 201 | .link_mode = .dynamic, |
| 201 | 202 | .resolved_target = comp.root_mod.resolved_target, |
| 202 | 203 | .is_test = false, |
| ... | ... | @@ -276,28 +277,39 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre |
| 276 | 277 | |
| 277 | 278 | try comp.updateSubCompilation(sub_compilation, .@"musl libc.so", prog_node); |
| 278 | 279 | |
| 279 | try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); | |
| 280 | ||
| 281 | 280 | const basename = try comp.gpa.dupe(u8, "libc.so"); |
| 282 | 281 | errdefer comp.gpa.free(basename); |
| 283 | 282 | |
| 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 | } |
| 288 | 294 | |
| 289 | // Return true if musl has arch-specific crti/crtn sources. | |
| 290 | // See lib/libc/musl/crt/ARCH/crt?.s . | |
| 295 | /// Return true if musl has arch-specific crti/crtn sources. | |
| 296 | /// See lib/libc/musl/crt/ARCH/crt?.s . | |
| 291 | 297 | pub fn needsCrtiCrtn(target: std.Target) bool { |
| 292 | // zig fmt: off | |
| 293 | 298 | return switch (target.cpu.arch) { |
| 294 | .riscv32, | |
| 295 | .riscv64, | |
| 296 | .wasm32, .wasm64 => false, | |
| 299 | .riscv32, .riscv64, .wasm32, .wasm64 => false, | |
| 297 | 300 | .loongarch64 => false, |
| 298 | 301 | else => true, |
| 299 | 302 | }; |
| 300 | // zig fmt: on | |
| 303 | } | |
| 304 | ||
| 305 | pub 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 | } |
| 302 | 314 | |
| 303 | 315 | fn isMuslArchName(name: []const u8) bool { |
src/target.zig+12-32| ... | ... | @@ -1,4 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; | |
| 3 | ||
| 2 | 4 | const Type = @import("Type.zig"); |
| 3 | 5 | const AddressSpace = std.builtin.AddressSpace; |
| 4 | 6 | const Alignment = @import("InternPool.zig").Alignment; |
| ... | ... | @@ -284,40 +286,18 @@ pub fn hasRedZone(target: std.Target) bool { |
| 284 | 286 | pub fn libcFullLinkFlags(target: std.Target) []const []const u8 { |
| 285 | 287 | // The linking order of these is significant and should match the order other |
| 286 | 288 | // c compilers such as gcc or clang use. |
| 287 | return switch (target.os.tag) { | |
| 288 | .netbsd, .openbsd => &[_][]const u8{ | |
| 289 | "-lm", | |
| 290 | "-lpthread", | |
| 291 | "-lc", | |
| 292 | "-lutil", | |
| 293 | }, | |
| 294 | .solaris, .illumos => &[_][]const u8{ | |
| 295 | "-lm", | |
| 296 | "-lsocket", | |
| 297 | "-lnsl", | |
| 298 | // Solaris releases after 10 merged the threading libraries into libc. | |
| 299 | "-lc", | |
| 300 | }, | |
| 301 | .haiku => &[_][]const u8{ | |
| 302 | "-lm", | |
| 303 | "-lroot", | |
| 304 | "-lpthread", | |
| 305 | "-lc", | |
| 306 | "-lnetwork", | |
| 307 | }, | |
| 308 | else => if (target.isAndroid() or target.abi.isOpenHarmony()) &[_][]const u8{ | |
| 309 | "-lm", | |
| 310 | "-lc", | |
| 311 | "-ldl", | |
| 312 | } else &[_][]const u8{ | |
| 313 | "-lm", | |
| 314 | "-lpthread", | |
| 315 | "-lc", | |
| 316 | "-ldl", | |
| 317 | "-lrt", | |
| 318 | "-lutil", | |
| 289 | const result: []const []const u8 = switch (target.os.tag) { | |
| 290 | .netbsd, .openbsd => &.{ "-lm", "-lpthread", "-lc", "-lutil" }, | |
| 291 | // Solaris releases after 10 merged the threading libraries into libc. | |
| 292 | .solaris, .illumos => &.{ "-lm", "-lsocket", "-lnsl", "-lc" }, | |
| 293 | .haiku => &.{ "-lm", "-lroot", "-lpthread", "-lc", "-lnetwork" }, | |
| 294 | .linux => switch (target.abi) { | |
| 295 | .android, .androideabi, .ohos, .ohoseabi => &.{ "-lm", "-lc", "-ldl" }, | |
| 296 | else => &.{ "-lm", "-lpthread", "-lc", "-ldl", "-lrt", "-lutil" }, | |
| 319 | 297 | }, |
| 298 | else => &.{}, | |
| 320 | 299 | }; |
| 300 | return result; | |
| 321 | 301 | } |
| 322 | 302 | |
| 323 | 303 | pub 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 | 51 | elf_step.dependOn(testEmitRelocatable(b, .{ .target = musl_target })); |
| 52 | 52 | elf_step.dependOn(testRelocatableArchive(b, .{ .target = musl_target })); |
| 53 | 53 | elf_step.dependOn(testRelocatableEhFrame(b, .{ .target = musl_target })); |
| 54 | elf_step.dependOn(testRelocatableEhFrameComdatHeavy(b, .{ .target = musl_target })); | |
| 54 | 55 | elf_step.dependOn(testRelocatableNoEhFrame(b, .{ .target = musl_target })); |
| 55 | 56 | |
| 56 | 57 | // Exercise linker in ar mode |
| ... | ... | @@ -2145,6 +2146,7 @@ fn testLdScript(b: *Build, opts: Options) *Step { |
| 2145 | 2146 | exe.addLibraryPath(dso.getEmittedBinDirectory()); |
| 2146 | 2147 | exe.addRPath(dso.getEmittedBinDirectory()); |
| 2147 | 2148 | exe.linkLibC(); |
| 2149 | exe.allow_so_scripts = true; | |
| 2148 | 2150 | |
| 2149 | 2151 | const run = addRunArtifact(exe); |
| 2150 | 2152 | run.expectExitCode(0); |
| ... | ... | @@ -2164,14 +2166,13 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step { |
| 2164 | 2166 | exe.linkSystemLibrary2("a", .{}); |
| 2165 | 2167 | exe.addLibraryPath(scripts.getDirectory()); |
| 2166 | 2168 | exe.linkLibC(); |
| 2169 | exe.allow_so_scripts = true; | |
| 2167 | 2170 | |
| 2168 | expectLinkErrors( | |
| 2169 | exe, | |
| 2170 | test_step, | |
| 2171 | .{ | |
| 2172 | .contains = "error: missing library dependency: GNU ld script '/?/liba.so' requires 'libfoo.so', but file not found", | |
| 2173 | }, | |
| 2174 | ); | |
| 2171 | // TODO: A future enhancement could make this error message also mention | |
| 2172 | // the file that references the missing library. | |
| 2173 | expectLinkErrors(exe, test_step, .{ | |
| 2174 | .stderr_contains = "error: unable to find dynamic system library 'foo' using strategy 'no_fallback'. searched paths:", | |
| 2175 | }); | |
| 2175 | 2176 | |
| 2176 | 2177 | return test_step; |
| 2177 | 2178 | } |
| ... | ... | @@ -2203,6 +2204,7 @@ fn testLdScriptAllowUndefinedVersion(b: *Build, opts: Options) *Step { |
| 2203 | 2204 | }); |
| 2204 | 2205 | exe.linkLibrary(so); |
| 2205 | 2206 | exe.linkLibC(); |
| 2207 | exe.allow_so_scripts = true; | |
| 2206 | 2208 | |
| 2207 | 2209 | const run = addRunArtifact(exe); |
| 2208 | 2210 | run.expectStdErrEqual("3\n"); |
| ... | ... | @@ -2225,6 +2227,7 @@ fn testLdScriptDisallowUndefinedVersion(b: *Build, opts: Options) *Step { |
| 2225 | 2227 | const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }"); |
| 2226 | 2228 | so.setLinkerScript(ld); |
| 2227 | 2229 | so.linker_allow_undefined_version = false; |
| 2230 | so.allow_so_scripts = true; | |
| 2228 | 2231 | |
| 2229 | 2232 | expectLinkErrors( |
| 2230 | 2233 | so, |
| ... | ... | @@ -2721,86 +2724,110 @@ fn testRelocatableArchive(b: *Build, opts: Options) *Step { |
| 2721 | 2724 | fn testRelocatableEhFrame(b: *Build, opts: Options) *Step { |
| 2722 | 2725 | const test_step = addTestStep(b, "relocatable-eh-frame", opts); |
| 2723 | 2726 | |
| 2724 | { | |
| 2725 | const obj = addObject(b, opts, .{ | |
| 2726 | .name = "obj1", | |
| 2727 | .cpp_source_bytes = | |
| 2728 | \\#include <stdexcept> | |
| 2729 | \\int try_me() { | |
| 2730 | \\ throw std::runtime_error("Oh no!"); | |
| 2731 | \\} | |
| 2732 | , | |
| 2733 | }); | |
| 2734 | addCppSourceBytes(obj, | |
| 2735 | \\extern int try_me(); | |
| 2736 | \\int try_again() { | |
| 2737 | \\ return try_me(); | |
| 2738 | \\} | |
| 2739 | , &.{}); | |
| 2740 | obj.linkLibCpp(); | |
| 2727 | const obj1 = addObject(b, opts, .{ | |
| 2728 | .name = "obj1", | |
| 2729 | .cpp_source_bytes = | |
| 2730 | \\#include <stdexcept> | |
| 2731 | \\int try_me() { | |
| 2732 | \\ throw std::runtime_error("Oh no!"); | |
| 2733 | \\} | |
| 2734 | , | |
| 2735 | }); | |
| 2736 | obj1.linkLibCpp(); | |
| 2737 | const obj2 = addObject(b, opts, .{ | |
| 2738 | .name = "obj2", | |
| 2739 | .cpp_source_bytes = | |
| 2740 | \\extern int try_me(); | |
| 2741 | \\int try_again() { | |
| 2742 | \\ return try_me(); | |
| 2743 | \\} | |
| 2744 | , | |
| 2745 | }); | |
| 2746 | obj2.linkLibCpp(); | |
| 2741 | 2747 | |
| 2742 | const exe = addExecutable(b, opts, .{ .name = "test1" }); | |
| 2743 | addCppSourceBytes(exe, | |
| 2744 | \\#include <iostream> | |
| 2745 | \\#include <stdexcept> | |
| 2746 | \\extern int try_again(); | |
| 2747 | \\int main() { | |
| 2748 | \\ try { | |
| 2749 | \\ try_again(); | |
| 2750 | \\ } catch (const std::exception &e) { | |
| 2751 | \\ std::cout << "exception=" << e.what(); | |
| 2752 | \\ } | |
| 2753 | \\ return 0; | |
| 2754 | \\} | |
| 2755 | , &.{}); | |
| 2756 | exe.addObject(obj); | |
| 2757 | exe.linkLibCpp(); | |
| 2748 | const obj = addObject(b, opts, .{ .name = "obj" }); | |
| 2749 | obj.addObject(obj1); | |
| 2750 | obj.addObject(obj2); | |
| 2751 | obj.linkLibCpp(); | |
| 2758 | 2752 | |
| 2759 | const run = addRunArtifact(exe); | |
| 2760 | run.expectStdOutEqual("exception=Oh no!"); | |
| 2761 | test_step.dependOn(&run.step); | |
| 2762 | } | |
| 2753 | const exe = addExecutable(b, opts, .{ .name = "test1" }); | |
| 2754 | addCppSourceBytes(exe, | |
| 2755 | \\#include <iostream> | |
| 2756 | \\#include <stdexcept> | |
| 2757 | \\extern int try_again(); | |
| 2758 | \\int main() { | |
| 2759 | \\ try { | |
| 2760 | \\ try_again(); | |
| 2761 | \\ } catch (const std::exception &e) { | |
| 2762 | \\ std::cout << "exception=" << e.what(); | |
| 2763 | \\ } | |
| 2764 | \\ return 0; | |
| 2765 | \\} | |
| 2766 | , &.{}); | |
| 2767 | exe.addObject(obj); | |
| 2768 | exe.linkLibCpp(); | |
| 2763 | 2769 | |
| 2764 | { | |
| 2765 | // Let's make the object file COMDAT group heavy! | |
| 2766 | const obj = addObject(b, opts, .{ | |
| 2767 | .name = "obj2", | |
| 2768 | .cpp_source_bytes = | |
| 2769 | \\#include <stdexcept> | |
| 2770 | \\int try_me() { | |
| 2771 | \\ throw std::runtime_error("Oh no!"); | |
| 2772 | \\} | |
| 2773 | , | |
| 2774 | }); | |
| 2775 | addCppSourceBytes(obj, | |
| 2776 | \\extern int try_me(); | |
| 2777 | \\int try_again() { | |
| 2778 | \\ return try_me(); | |
| 2779 | \\} | |
| 2780 | , &.{}); | |
| 2781 | addCppSourceBytes(obj, | |
| 2782 | \\#include <iostream> | |
| 2783 | \\#include <stdexcept> | |
| 2784 | \\extern int try_again(); | |
| 2785 | \\int main() { | |
| 2786 | \\ try { | |
| 2787 | \\ try_again(); | |
| 2788 | \\ } catch (const std::exception &e) { | |
| 2789 | \\ std::cout << "exception=" << e.what(); | |
| 2790 | \\ } | |
| 2791 | \\ return 0; | |
| 2792 | \\} | |
| 2793 | , &.{}); | |
| 2794 | obj.linkLibCpp(); | |
| 2770 | const run = addRunArtifact(exe); | |
| 2771 | run.expectStdOutEqual("exception=Oh no!"); | |
| 2772 | test_step.dependOn(&run.step); | |
| 2795 | 2773 | |
| 2796 | const exe = addExecutable(b, opts, .{ .name = "test2" }); | |
| 2797 | exe.addObject(obj); | |
| 2798 | exe.linkLibCpp(); | |
| 2774 | return test_step; | |
| 2775 | } | |
| 2799 | 2776 | |
| 2800 | const run = addRunArtifact(exe); | |
| 2801 | run.expectStdOutEqual("exception=Oh no!"); | |
| 2802 | test_step.dependOn(&run.step); | |
| 2803 | } | |
| 2777 | fn testRelocatableEhFrameComdatHeavy(b: *Build, opts: Options) *Step { | |
| 2778 | const test_step = addTestStep(b, "relocatable-eh-frame-comdat-heavy", opts); | |
| 2779 | ||
| 2780 | const obj1 = addObject(b, opts, .{ | |
| 2781 | .name = "obj1", | |
| 2782 | .cpp_source_bytes = | |
| 2783 | \\#include <stdexcept> | |
| 2784 | \\int try_me() { | |
| 2785 | \\ throw std::runtime_error("Oh no!"); | |
| 2786 | \\} | |
| 2787 | , | |
| 2788 | }); | |
| 2789 | obj1.linkLibCpp(); | |
| 2790 | const obj2 = addObject(b, opts, .{ | |
| 2791 | .name = "obj2", | |
| 2792 | .cpp_source_bytes = | |
| 2793 | \\extern int try_me(); | |
| 2794 | \\int try_again() { | |
| 2795 | \\ return try_me(); | |
| 2796 | \\} | |
| 2797 | , | |
| 2798 | }); | |
| 2799 | obj2.linkLibCpp(); | |
| 2800 | const obj3 = addObject(b, opts, .{ | |
| 2801 | .name = "obj3", | |
| 2802 | .cpp_source_bytes = | |
| 2803 | \\#include <iostream> | |
| 2804 | \\#include <stdexcept> | |
| 2805 | \\extern int try_again(); | |
| 2806 | \\int main() { | |
| 2807 | \\ try { | |
| 2808 | \\ try_again(); | |
| 2809 | \\ } catch (const std::exception &e) { | |
| 2810 | \\ std::cout << "exception=" << e.what(); | |
| 2811 | \\ } | |
| 2812 | \\ return 0; | |
| 2813 | \\} | |
| 2814 | , | |
| 2815 | }); | |
| 2816 | obj3.linkLibCpp(); | |
| 2817 | ||
| 2818 | const obj = addObject(b, opts, .{ .name = "obj" }); | |
| 2819 | obj.addObject(obj1); | |
| 2820 | obj.addObject(obj2); | |
| 2821 | obj.addObject(obj3); | |
| 2822 | obj.linkLibCpp(); | |
| 2823 | ||
| 2824 | const exe = addExecutable(b, opts, .{ .name = "test2" }); | |
| 2825 | exe.addObject(obj); | |
| 2826 | exe.linkLibCpp(); | |
| 2827 | ||
| 2828 | const run = addRunArtifact(exe); | |
| 2829 | run.expectStdOutEqual("exception=Oh no!"); | |
| 2830 | test_step.dependOn(&run.step); | |
| 2804 | 2831 | |
| 2805 | 2832 | return test_step; |
| 2806 | 2833 | } |
| ... | ... | @@ -3730,11 +3757,15 @@ fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step { |
| 3730 | 3757 | \\#include <pthread.h> |
| 3731 | 3758 | \\#include <dlfcn.h> |
| 3732 | 3759 | \\#include <assert.h> |
| 3760 | \\#include <stdio.h> | |
| 3733 | 3761 | \\void *(*verify)(void *); |
| 3734 | 3762 | \\ |
| 3735 | 3763 | \\int main() { |
| 3736 | 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 | 3769 | \\ *(void**)(&verify) = dlsym(handle, "verify"); |
| 3739 | 3770 | \\ assert(verify); |
| 3740 | 3771 | \\ |
| ... | ... | @@ -3907,16 +3938,8 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step { |
| 3907 | 3938 | exe.linkLibrary(dylib); |
| 3908 | 3939 | exe.linkLibC(); |
| 3909 | 3940 | |
| 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 | 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 | }); |
| 3921 | 3944 | |
| 3922 | 3945 | return test_step; |