authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-08 17:57:14-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-08 18:02:59-07:00
log31d70cb1e11cf480f4a45afdff20e4adf0d6068b
tree384541411c9b2be3192232cb90b1d43d98351c1f
parent2c41c453b634b3d1f378f6f1b335314f0a4be2de

link.Elf: avoid needless file system reads in flush()

flush() must not do anything more than necessary. Determining the type of input files must be done only once, before flush. Fortunately, we don't even need any file system accesses to do this since that information is statically known in most cases, and in the rest of the cases can be determined by file extension alone. This commit also updates the nearby code to conform to the convention for error handling where there is exactly one error code to represent the fact that error messages have already been emitted. This had the side effect of improving the error message for a linker script parse error. "positionals" is not a linker concept; it is a command line interface concept. Zig's linker implementation should not mention "positionals". This commit deletes that array list in favor of directly making function calls, eliminating that heap allocation during flush().

9 files changed, 169 insertions(+), 233 deletions(-)

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
......@@ -533,7 +533,10 @@ pub const File = struct {
533533 FailedToEmit,
534534 FileSystem,
535535 FilesOpenedWithWrongFlags,
536 /// Indicates an error will be present in `Compilation.link_errors`.
536537 FlushFailure,
538 /// Indicates an error will be present in `Compilation.link_errors`.
539 LinkFailure,
537540 FunctionSignatureMismatch,
538541 GlobalTypeMismatch,
539542 HotSwapUnavailableOnHostOperatingSystem,
src/link/Elf.zig+84-98
......@@ -791,8 +791,8 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
791791 const target = self.getTarget();
792792 const link_mode = comp.config.link_mode;
793793 const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
794 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
795794 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});
796796 if (fs.path.dirname(full_out_path)) |dirname| {
797797 break :blk try fs.path.join(arena, &.{ dirname, path });
798798 } else {
......@@ -808,61 +808,37 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
808808 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
809809
810810 const csu = try CsuObjects.init(arena, comp);
811 const compiler_rt_path: ?[]const u8 = blk: {
812 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
813 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
814 break :blk null;
815 };
816811
817 // Here we will parse input positional and library files (if referenced).
818 // This will roughly match in any linker backend we support.
819 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
812 // Here we will parse object and library files (if referenced).
820813
821814 // csu prelude
822 if (csu.crt0) |v| try positionals.append(.{ .path = v });
823 if (csu.crti) |v| try positionals.append(.{ .path = v });
824 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);
825818
826 try positionals.ensureUnusedCapacity(comp.objects.len);
827 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 }
828826
829827 // This is a set of object files emitted by clang in a single `build-exe` invocation.
830828 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
831829 // in this set.
832830 for (comp.c_object_table.keys()) |key| {
833 try positionals.append(.{ .path = key.status.success.object_path });
831 try parseObjectReportingFailure(self, key.status.success.object_path);
834832 }
835833
836 if (module_obj_path) |path| try positionals.append(.{ .path = path });
837
838 if (comp.config.any_sanitize_thread) {
839 try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path });
840 }
834 if (module_obj_path) |path| try parseObjectReportingFailure(self, path);
841835
842 if (comp.config.any_fuzz) {
843 try positionals.append(.{ .path = comp.fuzzer_lib.?.full_object_path });
844 }
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.?);
845838
846839 // libc
847840 if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
848 if (comp.libc_static_lib) |lib| {
849 try positionals.append(.{ .path = lib.full_object_path });
850 }
851 }
852
853 for (positionals.items) |obj| {
854 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
855 error.MalformedObject,
856 error.MalformedArchive,
857 error.MismatchedEflags,
858 error.InvalidMachineType,
859 => continue, // already reported
860 else => |e| try self.reportParseError(
861 obj.path,
862 "unexpected error: parsing input file failed with error {s}",
863 .{@errorName(e)},
864 ),
865 };
841 if (comp.libc_static_lib) |lib| try parseCrtFileReportingFailure(self, lib);
866842 }
867843
868844 var system_libs = std.ArrayList(SystemLib).init(arena);
......@@ -945,42 +921,23 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
945921 }
946922
947923 for (system_libs.items) |lib| {
948 self.parseLibrary(lib, false) catch |err| switch (err) {
949 error.MalformedObject, error.MalformedArchive, error.InvalidMachineType => continue, // already reported
950 else => |e| try self.reportParseError(
951 lib.path,
952 "unexpected error: parsing library failed with error {s}",
953 .{@errorName(e)},
954 ),
955 };
924 try self.parseLibraryReportingFailure(lib, false);
956925 }
957926
958927 // Finally, as the last input objects we add compiler_rt and CSU postlude (if any).
959 positionals.clearRetainingCapacity();
960928
961929 // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
962930 // to be after the shared libraries, so they are picked up from the shared
963931 // libraries, not libcompiler_rt.
964 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 }
965937
966938 // csu postlude
967 if (csu.crtend) |v| try positionals.append(.{ .path = v });
968 if (csu.crtn) |v| try positionals.append(.{ .path = v });
969
970 for (positionals.items) |obj| {
971 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
972 error.MalformedObject,
973 error.MalformedArchive,
974 error.MismatchedEflags,
975 error.InvalidMachineType,
976 => continue, // already reported
977 else => |e| try self.reportParseError(
978 obj.path,
979 "unexpected error: parsing input file failed with error {s}",
980 .{@errorName(e)},
981 ),
982 };
983 }
939 if (csu.crtend) |path| try parseObjectReportingFailure(self, path);
940 if (csu.crtn) |path| try parseObjectReportingFailure(self, path);
984941
985942 if (self.base.hasErrors()) return error.FlushFailure;
986943
......@@ -1022,7 +979,9 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
1022979 self.markEhFrameAtomsDead();
1023980 try self.resolveMergeSections();
1024981
1025 try self.convertCommonSymbols();
982 for (self.objects.items) |index| {
983 try self.file(index).?.object.convertCommonSymbols(self);
984 }
1026985 self.markImportsExports();
1027986
1028987 if (self.base.gc_sections) {
......@@ -1402,10 +1361,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
14021361}
14031362
14041363pub const ParseError = error{
1405 MalformedObject,
1406 MalformedArchive,
1407 InvalidMachineType,
1408 MismatchedEflags,
1364 /// Indicates the error is already reported on `Compilation.link_errors`.
1365 LinkFailure,
1366
14091367 OutOfMemory,
14101368 Overflow,
14111369 InputOutput,
......@@ -1416,16 +1374,30 @@ pub const ParseError = error{
14161374 UnknownFileType,
14171375} || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;
14181376
1419pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
1420 const tracy = trace(@src());
1421 defer tracy.end();
1422 if (try Object.isObject(path)) {
1423 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);
14241380 } else {
1425 try self.parseLibrary(.{ .path = path }, must_link);
1381 try parseLibraryReportingFailure(self, .{ .path = crt_file.full_object_path }, false);
14261382 }
14271383}
14281384
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
14291401fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool) ParseError!void {
14301402 const tracy = trace(@src());
14311403 defer tracy.end();
......@@ -1575,8 +1547,8 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
15751547 .needed = scr_obj.needed,
15761548 .path = full_path,
15771549 }, false) catch |err| switch (err) {
1578 error.MalformedObject, error.MalformedArchive, error.InvalidMachineType => continue, // already reported
1579 else => |e| try self.reportParseError(
1550 error.LinkFailure => continue, // already reported
1551 else => |e| try self.addParseError(
15801552 full_path,
15811553 "unexpected error: parsing library failed with error {s}",
15821554 .{@errorName(e)},
......@@ -1601,24 +1573,24 @@ pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Elf64_Wor
16011573 self_riscv_eflags.rvc = self_riscv_eflags.rvc or riscv_eflags.rvc;
16021574 self_riscv_eflags.tso = self_riscv_eflags.tso or riscv_eflags.tso;
16031575
1604 var is_error: bool = false;
1576 var any_errors: bool = false;
16051577 if (self_riscv_eflags.fabi != riscv_eflags.fabi) {
1606 is_error = true;
1607 _ = try self.reportParseError2(
1578 any_errors = true;
1579 try self.addFileError(
16081580 file_index,
16091581 "cannot link object files with different float-point ABIs",
16101582 .{},
16111583 );
16121584 }
16131585 if (self_riscv_eflags.rve != riscv_eflags.rve) {
1614 is_error = true;
1615 _ = try self.reportParseError2(
1586 any_errors = true;
1587 try self.addFileError(
16161588 file_index,
16171589 "cannot link object files with different RVEs",
16181590 .{},
16191591 );
16201592 }
1621 if (is_error) return error.MismatchedEflags;
1593 if (any_errors) return error.LinkFailure;
16221594 }
16231595 },
16241596 else => {},
......@@ -1740,12 +1712,6 @@ pub fn markEhFrameAtomsDead(self: *Elf) void {
17401712 }
17411713}
17421714
1743fn convertCommonSymbols(self: *Elf) !void {
1744 for (self.objects.items) |index| {
1745 try self.file(index).?.object.convertCommonSymbols(self);
1746 }
1747}
1748
17491715fn markImportsExports(self: *Elf) void {
17501716 if (self.zigObjectPtr()) |zo| {
17511717 zo.markImportsExports(self);
......@@ -2838,7 +2804,7 @@ pub fn resolveMergeSections(self: *Elf) !void {
28382804 const file_ptr = self.file(index).?;
28392805 if (!file_ptr.isAlive()) continue;
28402806 file_ptr.object.initInputMergeSections(self) catch |err| switch (err) {
2841 error.MalformedObject => has_errors = true,
2807 error.LinkFailure => has_errors = true,
28422808 else => |e| return e,
28432809 };
28442810 }
......@@ -2855,12 +2821,12 @@ pub fn resolveMergeSections(self: *Elf) !void {
28552821 const file_ptr = self.file(index).?;
28562822 if (!file_ptr.isAlive()) continue;
28572823 file_ptr.object.resolveMergeSubsections(self) catch |err| switch (err) {
2858 error.MalformedObject => has_errors = true,
2824 error.LinkFailure => has_errors = true,
28592825 else => |e| return e,
28602826 };
28612827 }
28622828
2863 if (has_errors) return error.FlushFailure;
2829 if (has_errors) return error.LinkFailure;
28642830}
28652831
28662832pub fn finalizeMergeSections(self: *Elf) !void {
......@@ -5192,7 +5158,7 @@ fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
51925158 });
51935159}
51945160
5195pub fn reportParseError(
5161pub fn addParseError(
51965162 self: *Elf,
51975163 path: []const u8,
51985164 comptime format: []const u8,
......@@ -5203,7 +5169,7 @@ pub fn reportParseError(
52035169 try err.addNote("while parsing {s}", .{path});
52045170}
52055171
5206pub fn reportParseError2(
5172pub fn addFileError(
52075173 self: *Elf,
52085174 file_index: File.Index,
52095175 comptime format: []const u8,
......@@ -5214,6 +5180,26 @@ pub fn reportParseError2(
52145180 try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()});
52155181}
52165182
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
52175203const FormatShdrCtx = struct {
52185204 elf_file: *Elf,
52195205 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 {
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 .contains = "error: invalid token in LD script: '\\x00\\x00\\x00\\x0c\\x00\\x00\\x00/usr/lib/dyld\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0d' (0:1069)",
39203920 });
39213921
39223922 return test_step;