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