authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-09 01:43:57-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-10-09 01:43:57-07:00
logce5a5c361b5b098c3b7d68f88136a9c91e7bec19
tree713dbd96a58ada0527b2668246df813f40a09cf0
parente1e151df0d948be7464b448c61033d4c1d80d86b
parent22661f3d67251688a1fabf9e5fe65210ce284b9f
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21633 from ziglang/reduce-flush-logic

link.Elf: reduce flush logic

18 files changed, 201 insertions(+), 268 deletions(-)

lib/std/Build/Step/Compile.zig+12
......@@ -235,6 +235,7 @@ sanitize_coverage_trace_pc_guard: ?bool = null,
235235pub const ExpectedCompileErrors = union(enum) {
236236 contains: []const u8,
237237 exact: []const []const u8,
238 starts_with: []const u8,
238239};
239240
240241pub const Entry = union(enum) {
......@@ -1958,6 +1959,17 @@ fn checkCompileErrors(compile: *Compile) !void {
19581959
19591960 // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile
19601961 switch (expect_errors) {
1962 .starts_with => |expect_starts_with| {
1963 if (std.mem.startsWith(u8, actual_stderr, expect_starts_with)) return;
1964 return compile.step.fail(
1965 \\
1966 \\========= should start with: ============
1967 \\{s}
1968 \\========= but not found: ================
1969 \\{s}
1970 \\=========================================
1971 , .{ expect_starts_with, actual_stderr });
1972 },
19611973 .contains => |expect_line| {
19621974 while (actual_line_it.next()) |actual_line| {
19631975 if (!matchCompileError(actual_line, expect_line)) continue;
src/Compilation.zig+15-1
......@@ -280,6 +280,13 @@ pub const CRTFile = struct {
280280 lock: Cache.Lock,
281281 full_object_path: []const u8,
282282
283 pub fn isObject(cf: CRTFile) bool {
284 return switch (classifyFileExt(cf.full_object_path)) {
285 .object => true,
286 else => false,
287 };
288 }
289
283290 pub fn deinit(self: *CRTFile, gpa: Allocator) void {
284291 self.lock.release();
285292 gpa.free(self.full_object_path);
......@@ -1018,6 +1025,13 @@ pub const LinkObject = struct {
10181025 //
10191026 // Consistent with `withLOption` variable name in lld ELF driver.
10201027 loption: bool = false,
1028
1029 pub fn isObject(lo: LinkObject) bool {
1030 return switch (classifyFileExt(lo.path)) {
1031 .object => true,
1032 else => false,
1033 };
1034 }
10211035};
10221036
10231037pub const CreateOptions = struct {
......@@ -2433,7 +2447,7 @@ fn flush(
24332447 if (comp.bin_file) |lf| {
24342448 // This is needed before reading the error flags.
24352449 lf.flush(arena, tid, prog_node) catch |err| switch (err) {
2436 error.FlushFailure => {}, // error reported through link_error_flags
2450 error.FlushFailure, error.LinkFailure => {}, // error reported through link_error_flags
24372451 error.LLDReportedFailure => {}, // error reported via lockAndParseLldStderr
24382452 else => |e| return e,
24392453 };
src/link.zig+3-1
......@@ -67,7 +67,6 @@ pub const File = struct {
6767 gc_sections: bool,
6868 print_gc_sections: bool,
6969 build_id: std.zig.BuildId,
70 rpath_list: []const []const u8,
7170 allow_shlib_undefined: bool,
7271 stack_size: u64,
7372
......@@ -534,7 +533,10 @@ pub const File = struct {
534533 FailedToEmit,
535534 FileSystem,
536535 FilesOpenedWithWrongFlags,
536 /// Indicates an error will be present in `Compilation.link_errors`.
537537 FlushFailure,
538 /// Indicates an error will be present in `Compilation.link_errors`.
539 LinkFailure,
538540 FunctionSignatureMismatch,
539541 GlobalTypeMismatch,
540542 HotSwapUnavailableOnHostOperatingSystem,
src/link/C.zig-1
......@@ -148,7 +148,6 @@ pub fn createEmpty(
148148 .file = file,
149149 .disable_lld_caching = options.disable_lld_caching,
150150 .build_id = options.build_id,
151 .rpath_list = options.rpath_list,
152151 },
153152 };
154153
src/link/Coff.zig-1
......@@ -263,7 +263,6 @@ pub fn createEmpty(
263263 .file = null,
264264 .disable_lld_caching = options.disable_lld_caching,
265265 .build_id = options.build_id,
266 .rpath_list = options.rpath_list,
267266 },
268267 .ptr_width = ptr_width,
269268 .page_size = page_size,
src/link/Elf.zig+97-120
......@@ -1,4 +1,5 @@
11base: link.File,
2rpath_table: std.StringArrayHashMapUnmanaged(void),
23image_base: u64,
34emit_relocs: bool,
45z_nodelete: bool,
......@@ -239,6 +240,11 @@ pub fn createEmpty(
239240 else
240241 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
241242
243 var rpath_table: std.StringArrayHashMapUnmanaged(void) = .empty;
244 try rpath_table.entries.resize(arena, options.rpath_list.len);
245 @memcpy(rpath_table.entries.items(.key), options.rpath_list);
246 try rpath_table.reIndex(arena);
247
242248 const self = try arena.create(Elf);
243249 self.* = .{
244250 .base = .{
......@@ -253,8 +259,8 @@ pub fn createEmpty(
253259 .file = null,
254260 .disable_lld_caching = options.disable_lld_caching,
255261 .build_id = options.build_id,
256 .rpath_list = options.rpath_list,
257262 },
263 .rpath_table = rpath_table,
258264 .ptr_width = ptr_width,
259265 .page_size = page_size,
260266 .default_sym_version = default_sym_version,
......@@ -785,8 +791,8 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
785791 const target = self.getTarget();
786792 const link_mode = comp.config.link_mode;
787793 const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
788 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
789794 const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: {
795 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
790796 if (fs.path.dirname(full_out_path)) |dirname| {
791797 break :blk try fs.path.join(arena, &.{ dirname, path });
792798 } else {
......@@ -802,69 +808,37 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
802808 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
803809
804810 const csu = try CsuObjects.init(arena, comp);
805 const compiler_rt_path: ?[]const u8 = blk: {
806 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
807 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
808 break :blk null;
809 };
810811
811 // Here we will parse input positional and library files (if referenced).
812 // This will roughly match in any linker backend we support.
813 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
812 // Here we will parse object and library files (if referenced).
814813
815814 // csu prelude
816 if (csu.crt0) |v| try positionals.append(.{ .path = v });
817 if (csu.crti) |v| try positionals.append(.{ .path = v });
818 if (csu.crtbegin) |v| try positionals.append(.{ .path = v });
815 if (csu.crt0) |path| try parseObjectReportingFailure(self, path);
816 if (csu.crti) |path| try parseObjectReportingFailure(self, path);
817 if (csu.crtbegin) |path| try parseObjectReportingFailure(self, path);
819818
820 try positionals.ensureUnusedCapacity(comp.objects.len);
821 positionals.appendSliceAssumeCapacity(comp.objects);
819 for (comp.objects) |obj| {
820 if (obj.isObject()) {
821 try parseObjectReportingFailure(self, obj.path);
822 } else {
823 try parseLibraryReportingFailure(self, .{ .path = obj.path }, obj.must_link);
824 }
825 }
822826
823827 // This is a set of object files emitted by clang in a single `build-exe` invocation.
824828 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
825829 // in this set.
826830 for (comp.c_object_table.keys()) |key| {
827 try positionals.append(.{ .path = key.status.success.object_path });
828 }
829
830 if (module_obj_path) |path| try positionals.append(.{ .path = path });
831
832 // rpaths
833 var rpath_table = std.StringArrayHashMap(void).init(gpa);
834 defer rpath_table.deinit();
835
836 for (self.base.rpath_list) |rpath| {
837 _ = try rpath_table.put(rpath, {});
831 try parseObjectReportingFailure(self, key.status.success.object_path);
838832 }
839833
840 if (comp.config.any_sanitize_thread) {
841 try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path });
842 }
834 if (module_obj_path) |path| try parseObjectReportingFailure(self, path);
843835
844 if (comp.config.any_fuzz) {
845 try positionals.append(.{ .path = comp.fuzzer_lib.?.full_object_path });
846 }
836 if (comp.config.any_sanitize_thread) try parseCrtFileReportingFailure(self, comp.tsan_lib.?);
837 if (comp.config.any_fuzz) try parseCrtFileReportingFailure(self, comp.fuzzer_lib.?);
847838
848839 // libc
849840 if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
850 if (comp.libc_static_lib) |lib| {
851 try positionals.append(.{ .path = lib.full_object_path });
852 }
853 }
854
855 for (positionals.items) |obj| {
856 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
857 error.MalformedObject,
858 error.MalformedArchive,
859 error.MismatchedEflags,
860 error.InvalidMachineType,
861 => continue, // already reported
862 else => |e| try self.reportParseError(
863 obj.path,
864 "unexpected error: parsing input file failed with error {s}",
865 .{@errorName(e)},
866 ),
867 };
841 if (comp.libc_static_lib) |lib| try parseCrtFileReportingFailure(self, lib);
868842 }
869843
870844 var system_libs = std.ArrayList(SystemLib).init(arena);
......@@ -947,42 +921,23 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
947921 }
948922
949923 for (system_libs.items) |lib| {
950 self.parseLibrary(lib, false) catch |err| switch (err) {
951 error.MalformedObject, error.MalformedArchive, error.InvalidMachineType => continue, // already reported
952 else => |e| try self.reportParseError(
953 lib.path,
954 "unexpected error: parsing library failed with error {s}",
955 .{@errorName(e)},
956 ),
957 };
924 try self.parseLibraryReportingFailure(lib, false);
958925 }
959926
960927 // Finally, as the last input objects we add compiler_rt and CSU postlude (if any).
961 positionals.clearRetainingCapacity();
962928
963929 // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
964930 // to be after the shared libraries, so they are picked up from the shared
965931 // libraries, not libcompiler_rt.
966 if (compiler_rt_path) |path| try positionals.append(.{ .path = path });
932 if (comp.compiler_rt_lib) |crt_file| {
933 try parseLibraryReportingFailure(self, .{ .path = crt_file.full_object_path }, false);
934 } else if (comp.compiler_rt_obj) |crt_file| {
935 try parseObjectReportingFailure(self, crt_file.full_object_path);
936 }
967937
968938 // csu postlude
969 if (csu.crtend) |v| try positionals.append(.{ .path = v });
970 if (csu.crtn) |v| try positionals.append(.{ .path = v });
971
972 for (positionals.items) |obj| {
973 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
974 error.MalformedObject,
975 error.MalformedArchive,
976 error.MismatchedEflags,
977 error.InvalidMachineType,
978 => continue, // already reported
979 else => |e| try self.reportParseError(
980 obj.path,
981 "unexpected error: parsing input file failed with error {s}",
982 .{@errorName(e)},
983 ),
984 };
985 }
939 if (csu.crtend) |path| try parseObjectReportingFailure(self, path);
940 if (csu.crtn) |path| try parseObjectReportingFailure(self, path);
986941
987942 if (self.base.hasErrors()) return error.FlushFailure;
988943
......@@ -1024,7 +979,9 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
1024979 self.markEhFrameAtomsDead();
1025980 try self.resolveMergeSections();
1026981
1027 try self.convertCommonSymbols();
982 for (self.objects.items) |index| {
983 try self.file(index).?.object.convertCommonSymbols(self);
984 }
1028985 self.markImportsExports();
1029986
1030987 if (self.base.gc_sections) {
......@@ -1056,7 +1013,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
10561013 try self.initSpecialPhdrs();
10571014 try self.sortShdrs();
10581015
1059 try self.setDynamicSection(rpath_table.keys());
1016 try self.setDynamicSection(self.rpath_table.keys());
10601017 self.sortDynamicSymtab();
10611018 try self.setHashSections();
10621019 try self.setVersionSymtab();
......@@ -1207,9 +1164,8 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
12071164 try argv.appendSlice(&.{ "--entry", name });
12081165 }
12091166
1210 for (self.base.rpath_list) |rpath| {
1211 try argv.append("-rpath");
1212 try argv.append(rpath);
1167 for (self.rpath_table.keys()) |rpath| {
1168 try argv.appendSlice(&.{ "-rpath", rpath });
12131169 }
12141170
12151171 try argv.appendSlice(&.{
......@@ -1405,10 +1361,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
14051361}
14061362
14071363pub const ParseError = error{
1408 MalformedObject,
1409 MalformedArchive,
1410 InvalidMachineType,
1411 MismatchedEflags,
1364 /// Indicates the error is already reported on `Compilation.link_errors`.
1365 LinkFailure,
1366
14121367 OutOfMemory,
14131368 Overflow,
14141369 InputOutput,
......@@ -1419,16 +1374,30 @@ pub const ParseError = error{
14191374 UnknownFileType,
14201375} || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;
14211376
1422pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
1423 const tracy = trace(@src());
1424 defer tracy.end();
1425 if (try Object.isObject(path)) {
1426 try self.parseObject(path);
1377fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CRTFile) error{OutOfMemory}!void {
1378 if (crt_file.isObject()) {
1379 try parseObjectReportingFailure(self, crt_file.full_object_path);
14271380 } else {
1428 try self.parseLibrary(.{ .path = path }, must_link);
1381 try parseLibraryReportingFailure(self, .{ .path = crt_file.full_object_path }, false);
14291382 }
14301383}
14311384
1385pub fn parseObjectReportingFailure(self: *Elf, path: []const u8) error{OutOfMemory}!void {
1386 self.parseObject(path) catch |err| switch (err) {
1387 error.LinkFailure => return, // already reported
1388 error.OutOfMemory => return error.OutOfMemory,
1389 else => |e| try self.addParseError(path, "unable to parse object: {s}", .{@errorName(e)}),
1390 };
1391}
1392
1393pub fn parseLibraryReportingFailure(self: *Elf, lib: SystemLib, must_link: bool) error{OutOfMemory}!void {
1394 self.parseLibrary(lib, must_link) catch |err| switch (err) {
1395 error.LinkFailure => return, // already reported
1396 error.OutOfMemory => return error.OutOfMemory,
1397 else => |e| try self.addParseError(lib.path, "unable to parse library: {s}", .{@errorName(e)}),
1398 };
1399}
1400
14321401fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool) ParseError!void {
14331402 const tracy = trace(@src());
14341403 defer tracy.end();
......@@ -1578,8 +1547,8 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
15781547 .needed = scr_obj.needed,
15791548 .path = full_path,
15801549 }, false) catch |err| switch (err) {
1581 error.MalformedObject, error.MalformedArchive, error.InvalidMachineType => continue, // already reported
1582 else => |e| try self.reportParseError(
1550 error.LinkFailure => continue, // already reported
1551 else => |e| try self.addParseError(
15831552 full_path,
15841553 "unexpected error: parsing library failed with error {s}",
15851554 .{@errorName(e)},
......@@ -1604,24 +1573,24 @@ pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Elf64_Wor
16041573 self_riscv_eflags.rvc = self_riscv_eflags.rvc or riscv_eflags.rvc;
16051574 self_riscv_eflags.tso = self_riscv_eflags.tso or riscv_eflags.tso;
16061575
1607 var is_error: bool = false;
1576 var any_errors: bool = false;
16081577 if (self_riscv_eflags.fabi != riscv_eflags.fabi) {
1609 is_error = true;
1610 _ = try self.reportParseError2(
1578 any_errors = true;
1579 try self.addFileError(
16111580 file_index,
16121581 "cannot link object files with different float-point ABIs",
16131582 .{},
16141583 );
16151584 }
16161585 if (self_riscv_eflags.rve != riscv_eflags.rve) {
1617 is_error = true;
1618 _ = try self.reportParseError2(
1586 any_errors = true;
1587 try self.addFileError(
16191588 file_index,
16201589 "cannot link object files with different RVEs",
16211590 .{},
16221591 );
16231592 }
1624 if (is_error) return error.MismatchedEflags;
1593 if (any_errors) return error.LinkFailure;
16251594 }
16261595 },
16271596 else => {},
......@@ -1743,12 +1712,6 @@ pub fn markEhFrameAtomsDead(self: *Elf) void {
17431712 }
17441713}
17451714
1746fn convertCommonSymbols(self: *Elf) !void {
1747 for (self.objects.items) |index| {
1748 try self.file(index).?.object.convertCommonSymbols(self);
1749 }
1750}
1751
17521715fn markImportsExports(self: *Elf) void {
17531716 if (self.zigObjectPtr()) |zo| {
17541717 zo.markImportsExports(self);
......@@ -1978,7 +1941,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
19781941 man.hash.add(self.emit_relocs);
19791942 man.hash.add(comp.config.rdynamic);
19801943 man.hash.addListOfBytes(self.lib_dirs);
1981 man.hash.addListOfBytes(self.base.rpath_list);
1944 man.hash.addListOfBytes(self.rpath_table.keys());
19821945 if (output_mode == .Exe) {
19831946 man.hash.add(self.base.stack_size);
19841947 man.hash.add(self.base.build_id);
......@@ -2263,14 +2226,8 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
22632226 if (csu.crti) |v| try argv.append(v);
22642227 if (csu.crtbegin) |v| try argv.append(v);
22652228
2266 // rpaths
2267 var rpath_table = std.StringHashMap(void).init(gpa);
2268 defer rpath_table.deinit();
2269 for (self.base.rpath_list) |rpath| {
2270 if ((try rpath_table.fetchPut(rpath, {})) == null) {
2271 try argv.append("-rpath");
2272 try argv.append(rpath);
2273 }
2229 for (self.rpath_table.keys()) |rpath| {
2230 try argv.appendSlice(&.{ "-rpath", rpath });
22742231 }
22752232
22762233 for (self.symbol_wrap_set.keys()) |symbol_name| {
......@@ -2847,7 +2804,7 @@ pub fn resolveMergeSections(self: *Elf) !void {
28472804 const file_ptr = self.file(index).?;
28482805 if (!file_ptr.isAlive()) continue;
28492806 file_ptr.object.initInputMergeSections(self) catch |err| switch (err) {
2850 error.MalformedObject => has_errors = true,
2807 error.LinkFailure => has_errors = true,
28512808 else => |e| return e,
28522809 };
28532810 }
......@@ -2864,12 +2821,12 @@ pub fn resolveMergeSections(self: *Elf) !void {
28642821 const file_ptr = self.file(index).?;
28652822 if (!file_ptr.isAlive()) continue;
28662823 file_ptr.object.resolveMergeSubsections(self) catch |err| switch (err) {
2867 error.MalformedObject => has_errors = true,
2824 error.LinkFailure => has_errors = true,
28682825 else => |e| return e,
28692826 };
28702827 }
28712828
2872 if (has_errors) return error.FlushFailure;
2829 if (has_errors) return error.LinkFailure;
28732830}
28742831
28752832pub fn finalizeMergeSections(self: *Elf) !void {
......@@ -5201,7 +5158,7 @@ fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
52015158 });
52025159}
52035160
5204pub fn reportParseError(
5161pub fn addParseError(
52055162 self: *Elf,
52065163 path: []const u8,
52075164 comptime format: []const u8,
......@@ -5212,7 +5169,7 @@ pub fn reportParseError(
52125169 try err.addNote("while parsing {s}", .{path});
52135170}
52145171
5215pub fn reportParseError2(
5172pub fn addFileError(
52165173 self: *Elf,
52175174 file_index: File.Index,
52185175 comptime format: []const u8,
......@@ -5223,6 +5180,26 @@ pub fn reportParseError2(
52235180 try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()});
52245181}
52255182
5183pub fn failFile(
5184 self: *Elf,
5185 file_index: File.Index,
5186 comptime format: []const u8,
5187 args: anytype,
5188) error{ OutOfMemory, LinkFailure } {
5189 try addFileError(self, file_index, format, args);
5190 return error.LinkFailure;
5191}
5192
5193pub fn failParse(
5194 self: *Elf,
5195 path: []const u8,
5196 comptime format: []const u8,
5197 args: anytype,
5198) error{ OutOfMemory, LinkFailure } {
5199 try addParseError(self, path, format, args);
5200 return error.LinkFailure;
5201}
5202
52265203const FormatShdrCtx = struct {
52275204 elf_file: *Elf,
52285205 shdr: elf.Elf64_Shdr,
src/link/Elf/Archive.zig+1-2
......@@ -35,10 +35,9 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: Fil
3535 pos += @sizeOf(elf.ar_hdr);
3636
3737 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {
38 try elf_file.reportParseError(path, "invalid archive header delimiter: {s}", .{
38 return elf_file.failParse(path, "invalid archive header delimiter: {s}", .{
3939 std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),
4040 });
41 return error.MalformedArchive;
4241 }
4342
4443 const obj_size = try hdr.size();
src/link/Elf/LdScript.zig+4-8
......@@ -7,7 +7,7 @@ pub fn deinit(scr: *LdScript, allocator: Allocator) void {
77}
88
99pub const Error = error{
10 InvalidLdScript,
10 LinkFailure,
1111 UnexpectedToken,
1212 UnknownCpuArch,
1313 OutOfMemory,
......@@ -32,12 +32,9 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void {
3232 try line_col.append(.{ .line = line, .column = column });
3333 switch (tok.id) {
3434 .invalid => {
35 try elf_file.reportParseError(scr.path, "invalid token in LD script: '{s}' ({d}:{d})", .{
36 std.fmt.fmtSliceEscapeLower(tok.get(data)),
37 line,
38 column,
35 return elf_file.failParse(scr.path, "invalid token in LD script: '{s}' ({d}:{d})", .{
36 std.fmt.fmtSliceEscapeLower(tok.get(data)), line, column,
3937 });
40 return error.InvalidLdScript;
4138 },
4239 .new_line => {
4340 line += 1;
......@@ -59,13 +56,12 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void {
5956 const last_token_id = parser.it.pos - 1;
6057 const last_token = parser.it.get(last_token_id);
6158 const lcol = line_col.items[last_token_id];
62 try elf_file.reportParseError(scr.path, "unexpected token in LD script: {s}: '{s}' ({d}:{d})", .{
59 return elf_file.failParse(scr.path, "unexpected token in LD script: {s}: '{s}' ({d}:{d})", .{
6360 @tagName(last_token.id),
6461 last_token.get(data),
6562 lcol.line,
6663 lcol.column,
6764 });
68 return error.InvalidLdScript;
6965 },
7066 else => |e| return e,
7167 };
src/link/Elf/Object.zig+20-53
......@@ -34,18 +34,6 @@ num_dynrelocs: u32 = 0,
3434output_symtab_ctx: Elf.SymtabCtx = .{},
3535output_ar_state: Archive.ArState = .{},
3636
37pub fn isObject(path: []const u8) !bool {
38 const file = try std.fs.cwd().openFile(path, .{});
39 defer file.close();
40 const reader = file.reader();
41 const header = reader.readStruct(elf.Elf64_Ehdr) catch return false;
42 if (!mem.eql(u8, header.e_ident[0..4], "\x7fELF")) return false;
43 if (header.e_ident[elf.EI_VERSION] != 1) return false;
44 if (header.e_type != elf.ET.REL) return false;
45 if (header.e_version != 1) return false;
46 return true;
47}
48
4937pub fn deinit(self: *Object, allocator: Allocator) void {
5038 if (self.archive) |*ar| allocator.free(ar.path);
5139 allocator.free(self.path);
......@@ -107,12 +95,9 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil
10795
10896 const em = elf_file.base.comp.root_mod.resolved_target.result.toElfMachine();
10997 if (em != self.header.?.e_machine) {
110 try elf_file.reportParseError2(
111 self.index,
112 "invalid ELF machine type: {s}",
113 .{@tagName(self.header.?.e_machine)},
114 );
115 return error.InvalidMachineType;
98 return elf_file.failFile(self.index, "invalid ELF machine type: {s}", .{
99 @tagName(self.header.?.e_machine),
100 });
116101 }
117102 try elf_file.validateEFlags(self.index, self.header.?.e_flags);
118103
......@@ -122,12 +107,7 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil
122107 const shnum = math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow;
123108 const shsize = shnum * @sizeOf(elf.Elf64_Shdr);
124109 if (file_size < offset + shoff or file_size < offset + shoff + shsize) {
125 try elf_file.reportParseError2(
126 self.index,
127 "corrupt header: section header table extends past the end of file",
128 .{},
129 );
130 return error.MalformedObject;
110 return elf_file.failFile(self.index, "corrupt header: section header table extends past the end of file", .{});
131111 }
132112
133113 const shdrs_buffer = try Elf.preadAllAlloc(allocator, handle, offset + shoff, shsize);
......@@ -138,8 +118,7 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil
138118 for (self.shdrs.items) |shdr| {
139119 if (shdr.sh_type != elf.SHT_NOBITS) {
140120 if (file_size < offset + shdr.sh_offset or file_size < offset + shdr.sh_offset + shdr.sh_size) {
141 try elf_file.reportParseError2(self.index, "corrupt section: extends past the end of file", .{});
142 return error.MalformedObject;
121 return elf_file.failFile(self.index, "corrupt section: extends past the end of file", .{});
143122 }
144123 }
145124 }
......@@ -148,8 +127,7 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil
148127 defer allocator.free(shstrtab);
149128 for (self.shdrs.items) |shdr| {
150129 if (shdr.sh_name >= shstrtab.len) {
151 try elf_file.reportParseError2(self.index, "corrupt section name offset", .{});
152 return error.MalformedObject;
130 return elf_file.failFile(self.index, "corrupt section name offset", .{});
153131 }
154132 }
155133 try self.strtab.appendSlice(allocator, shstrtab);
......@@ -166,8 +144,7 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil
166144 const raw_symtab = try self.preadShdrContentsAlloc(allocator, handle, index);
167145 defer allocator.free(raw_symtab);
168146 const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch {
169 try elf_file.reportParseError2(self.index, "symbol table not evenly divisible", .{});
170 return error.MalformedObject;
147 return elf_file.failFile(self.index, "symbol table not evenly divisible", .{});
171148 };
172149 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
173150
......@@ -221,30 +198,15 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
221198 const group_raw_data = try self.preadShdrContentsAlloc(allocator, handle, shndx);
222199 defer allocator.free(group_raw_data);
223200 const group_nmembers = math.divExact(usize, group_raw_data.len, @sizeOf(u32)) catch {
224 try elf_file.reportParseError2(
225 self.index,
226 "corrupt section group: not evenly divisible ",
227 .{},
228 );
229 return error.MalformedObject;
201 return elf_file.failFile(self.index, "corrupt section group: not evenly divisible ", .{});
230202 };
231203 if (group_nmembers == 0) {
232 try elf_file.reportParseError2(
233 self.index,
234 "corrupt section group: empty section",
235 .{},
236 );
237 return error.MalformedObject;
204 return elf_file.failFile(self.index, "corrupt section group: empty section", .{});
238205 }
239206 const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];
240207
241208 if (group_members[0] != elf.GRP_COMDAT) {
242 try elf_file.reportParseError2(
243 self.index,
244 "corrupt section group: unknown SHT_GROUP format",
245 .{},
246 );
247 return error.MalformedObject;
209 return elf_file.failFile(self.index, "corrupt section group: unknown SHT_GROUP format", .{});
248210 }
249211
250212 const group_start = @as(u32, @intCast(self.comdat_group_data.items.len));
......@@ -722,7 +684,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void {
722684 var err = try elf_file.base.addErrorWithNotes(1);
723685 try err.addMsg("string not null terminated", .{});
724686 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
725 return error.MalformedObject;
687 return error.LinkFailure;
726688 }
727689 end += sh_entsize;
728690 const string = data[start..end];
......@@ -737,7 +699,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void {
737699 var err = try elf_file.base.addErrorWithNotes(1);
738700 try err.addMsg("size not a multiple of sh_entsize", .{});
739701 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
740 return error.MalformedObject;
702 return error.LinkFailure;
741703 }
742704
743705 var pos: u32 = 0;
......@@ -765,7 +727,12 @@ pub fn initOutputMergeSections(self: *Object, elf_file: *Elf) !void {
765727 }
766728}
767729
768pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
730pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{
731 LinkFailure,
732 OutOfMemory,
733 /// TODO report the error and remove this
734 Overflow,
735}!void {
769736 const gpa = elf_file.base.comp.gpa;
770737
771738 for (self.input_merge_sections_indexes.items) |index| {
......@@ -809,7 +776,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
809776 try err.addMsg("invalid symbol value: {x}", .{esym.st_value});
810777 try err.addNote("for symbol {s}", .{sym.name(elf_file)});
811778 try err.addNote("in {}", .{self.fmtPath()});
812 return error.MalformedObject;
779 return error.LinkFailure;
813780 };
814781
815782 sym.ref = .{ .index = res.msub_index, .file = imsec.merge_section_index };
......@@ -834,7 +801,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
834801 var err = try elf_file.base.addErrorWithNotes(1);
835802 try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset});
836803 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
837 return error.MalformedObject;
804 return error.LinkFailure;
838805 };
839806
840807 const sym_index = try self.addSymbol(gpa);
src/link/Elf/SharedObject.zig+5-14
......@@ -58,24 +58,16 @@ pub fn parse(self: *SharedObject, elf_file: *Elf, handle: std.fs.File) !void {
5858
5959 const em = elf_file.base.comp.root_mod.resolved_target.result.toElfMachine();
6060 if (em != self.header.?.e_machine) {
61 try elf_file.reportParseError2(
62 self.index,
63 "invalid ELF machine type: {s}",
64 .{@tagName(self.header.?.e_machine)},
65 );
66 return error.InvalidMachineType;
61 return elf_file.failFile(self.index, "invalid ELF machine type: {s}", .{
62 @tagName(self.header.?.e_machine),
63 });
6764 }
6865
6966 const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
7067 const shnum = std.math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow;
7168 const shsize = shnum * @sizeOf(elf.Elf64_Shdr);
7269 if (file_size < shoff or file_size < shoff + shsize) {
73 try elf_file.reportParseError2(
74 self.index,
75 "corrupted header: section header table extends past the end of file",
76 .{},
77 );
78 return error.MalformedObject;
70 return elf_file.failFile(self.index, "corrupted header: section header table extends past the end of file", .{});
7971 }
8072
8173 const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, shoff, shsize);
......@@ -90,8 +82,7 @@ pub fn parse(self: *SharedObject, elf_file: *Elf, handle: std.fs.File) !void {
9082 for (self.shdrs.items, 0..) |shdr, i| {
9183 if (shdr.sh_type != elf.SHT_NOBITS) {
9284 if (file_size < shdr.sh_offset or file_size < shdr.sh_offset + shdr.sh_size) {
93 try elf_file.reportParseError2(self.index, "corrupted section header", .{});
94 return error.MalformedObject;
85 return elf_file.failFile(self.index, "corrupted section header", .{});
9586 }
9687 }
9788 switch (shdr.sh_type) {
src/link/Elf/relocatable.zig+36-56
......@@ -1,36 +1,24 @@
11pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
22 const gpa = comp.gpa;
33
4 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
5 defer positionals.deinit();
6
7 try positionals.ensureUnusedCapacity(comp.objects.len);
8 positionals.appendSliceAssumeCapacity(comp.objects);
4 for (comp.objects) |obj| {
5 switch (Compilation.classifyFileExt(obj.path)) {
6 .object => try parseObjectStaticLibReportingFailure(elf_file, obj.path),
7 .static_library => try parseArchiveStaticLibReportingFailure(elf_file, obj.path),
8 else => try elf_file.addParseError(obj.path, "unrecognized file extension", .{}),
9 }
10 }
911
1012 for (comp.c_object_table.keys()) |key| {
11 try positionals.append(.{ .path = key.status.success.object_path });
13 try parseObjectStaticLibReportingFailure(elf_file, key.status.success.object_path);
1214 }
1315
14 if (module_obj_path) |path| try positionals.append(.{ .path = path });
16 if (module_obj_path) |path| {
17 try parseObjectStaticLibReportingFailure(elf_file, path);
18 }
1519
1620 if (comp.include_compiler_rt) {
17 try positionals.append(.{ .path = comp.compiler_rt_obj.?.full_object_path });
18 }
19
20 for (positionals.items) |obj| {
21 parsePositionalStaticLib(elf_file, obj.path) catch |err| switch (err) {
22 error.MalformedObject,
23 error.MalformedArchive,
24 error.InvalidMachineType,
25 error.MismatchedEflags,
26 => continue, // already reported
27 error.UnknownFileType => try elf_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
28 else => |e| try elf_file.reportParseError(
29 obj.path,
30 "unexpected error: parsing input file failed with error {s}",
31 .{@errorName(e)},
32 ),
33 };
21 try parseObjectStaticLibReportingFailure(elf_file, comp.compiler_rt_obj.?.full_object_path);
3422 }
3523
3624 if (elf_file.base.hasErrors()) return error.FlushFailure;
......@@ -153,37 +141,23 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
153141}
154142
155143pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
156 const gpa = elf_file.base.comp.gpa;
157
158 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
159 defer positionals.deinit();
160 try positionals.ensureUnusedCapacity(comp.objects.len);
161 positionals.appendSliceAssumeCapacity(comp.objects);
144 for (comp.objects) |obj| {
145 if (obj.isObject()) {
146 try elf_file.parseObjectReportingFailure(obj.path);
147 } else {
148 try elf_file.parseLibraryReportingFailure(.{ .path = obj.path }, obj.must_link);
149 }
150 }
162151
163152 // This is a set of object files emitted by clang in a single `build-exe` invocation.
164153 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
165154 // in this set.
166155 for (comp.c_object_table.keys()) |key| {
167 try positionals.append(.{ .path = key.status.success.object_path });
168 }
169
170 if (module_obj_path) |path| try positionals.append(.{ .path = path });
171
172 for (positionals.items) |obj| {
173 elf_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
174 error.MalformedObject,
175 error.MalformedArchive,
176 error.InvalidMachineType,
177 error.MismatchedEflags,
178 => continue, // already reported
179 else => |e| try elf_file.reportParseError(
180 obj.path,
181 "unexpected error: parsing input file failed with error {s}",
182 .{@errorName(e)},
183 ),
184 };
156 try elf_file.parseObjectReportingFailure(key.status.success.object_path);
185157 }
186158
159 if (module_obj_path) |path| try elf_file.parseObjectReportingFailure(path);
160
187161 if (elf_file.base.hasErrors()) return error.FlushFailure;
188162
189163 // Now, we are ready to resolve the symbols across all input files.
......@@ -224,14 +198,20 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
224198 if (elf_file.base.hasErrors()) return error.FlushFailure;
225199}
226200
227fn parsePositionalStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
228 if (try Object.isObject(path)) {
229 try parseObjectStaticLib(elf_file, path);
230 } else if (try Archive.isArchive(path)) {
231 try parseArchiveStaticLib(elf_file, path);
232 } else return error.UnknownFileType;
233 // TODO: should we check for LD script?
234 // Actually, should we even unpack an archive?
201fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error{OutOfMemory}!void {
202 parseObjectStaticLib(elf_file, path) catch |err| switch (err) {
203 error.LinkFailure => return,
204 error.OutOfMemory => return error.OutOfMemory,
205 else => |e| try elf_file.addParseError(path, "parsing object failed: {s}", .{@errorName(e)}),
206 };
207}
208
209fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error{OutOfMemory}!void {
210 parseArchiveStaticLib(elf_file, path) catch |err| switch (err) {
211 error.LinkFailure => return,
212 error.OutOfMemory => return error.OutOfMemory,
213 else => |e| try elf_file.addParseError(path, "parsing static library failed: {s}", .{@errorName(e)}),
214 };
235215}
236216
237217fn parseObjectStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
src/link/MachO.zig+6-5
......@@ -1,5 +1,7 @@
11base: link.File,
22
3rpath_list: []const []const u8,
4
35/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path.
46llvm_object: ?LlvmObject.Ptr = null,
57
......@@ -192,8 +194,8 @@ pub fn createEmpty(
192194 .file = null,
193195 .disable_lld_caching = options.disable_lld_caching,
194196 .build_id = options.build_id,
195 .rpath_list = options.rpath_list,
196197 },
198 .rpath_list = options.rpath_list,
197199 .pagezero_size = options.pagezero_size,
198200 .headerpad_size = options.headerpad_size,
199201 .headerpad_max_install_names = options.headerpad_max_install_names,
......@@ -662,9 +664,8 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
662664 try argv.append(syslibroot);
663665 }
664666
665 for (self.base.rpath_list) |rpath| {
666 try argv.append("-rpath");
667 try argv.append(rpath);
667 for (self.rpath_list) |rpath| {
668 try argv.appendSlice(&.{ "-rpath", rpath });
668669 }
669670
670671 if (self.pagezero_size) |size| {
......@@ -2842,7 +2843,7 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
28422843 ncmds += 1;
28432844 }
28442845
2845 for (self.base.rpath_list) |rpath| {
2846 for (self.rpath_list) |rpath| {
28462847 try load_commands.writeRpathLC(rpath, writer);
28472848 ncmds += 1;
28482849 }
src/link/MachO/load_commands.zig+1-1
......@@ -63,7 +63,7 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) !u32
6363 }
6464 // LC_RPATH
6565 {
66 for (macho_file.base.rpath_list) |rpath| {
66 for (macho_file.rpath_list) |rpath| {
6767 sizeofcmds += calcInstallNameLen(
6868 @sizeOf(macho.rpath_command),
6969 rpath,
src/link/NvPtx.zig-1
......@@ -60,7 +60,6 @@ pub fn createEmpty(
6060 .file = null,
6161 .disable_lld_caching = options.disable_lld_caching,
6262 .build_id = options.build_id,
63 .rpath_list = options.rpath_list,
6463 },
6564 .llvm_object = llvm_object,
6665 };
src/link/Plan9.zig-1
......@@ -304,7 +304,6 @@ pub fn createEmpty(
304304 .file = null,
305305 .disable_lld_caching = options.disable_lld_caching,
306306 .build_id = options.build_id,
307 .rpath_list = options.rpath_list,
308307 },
309308 .sixtyfour_bit = sixtyfour_bit,
310309 .bases = undefined,
src/link/SpirV.zig-1
......@@ -74,7 +74,6 @@ pub fn createEmpty(
7474 .file = null,
7575 .disable_lld_caching = options.disable_lld_caching,
7676 .build_id = options.build_id,
77 .rpath_list = options.rpath_list,
7877 },
7978 .object = codegen.Object.init(gpa),
8079 };
src/link/Wasm.zig-1
......@@ -398,7 +398,6 @@ pub fn createEmpty(
398398 .file = null,
399399 .disable_lld_caching = options.disable_lld_caching,
400400 .build_id = options.build_id,
401 .rpath_list = options.rpath_list,
402401 },
403402 .name = undefined,
404403 .import_table = options.import_table,
test/link/elf.zig+1-1
......@@ -3916,7 +3916,7 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
39163916 // "note: while parsing /?/liba.dylib",
39173917 // } });
39183918 expectLinkErrors(exe, test_step, .{
3919 .contains = "error: unexpected error: parsing input file failed with error InvalidLdScript",
3919 .starts_with = "error: invalid token in LD script: '\\x00\\x00\\x00\\x0c\\x00\\x00\\x00/usr/lib/dyld\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0d' (",
39203920 });
39213921
39223922 return test_step;