| author | |
| committer | |
| log | 31d70cb1e11cf480f4a45afdff20e4adf0d6068b |
| tree | 384541411c9b2be3192232cb90b1d43d98351c1f |
| parent | 2c41c453b634b3d1f378f6f1b335314f0a4be2de |
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 { | ... | @@ -280,6 +280,13 @@ pub const CRTFile = struct { |
| 280 | lock: Cache.Lock, | 280 | lock: Cache.Lock, |
| 281 | full_object_path: []const u8, | 281 | full_object_path: []const u8, |
| 282 | 282 | ||
| 283 | pub fn isObject(cf: CRTFile) bool { | ||
| 284 | return switch (classifyFileExt(cf.full_object_path)) { | ||
| 285 | .object => true, | ||
| 286 | else => false, | ||
| 287 | }; | ||
| 288 | } | ||
| 289 | |||
| 283 | pub fn deinit(self: *CRTFile, gpa: Allocator) void { | 290 | pub fn deinit(self: *CRTFile, gpa: Allocator) void { |
| 284 | self.lock.release(); | 291 | self.lock.release(); |
| 285 | gpa.free(self.full_object_path); | 292 | gpa.free(self.full_object_path); |
| ... | @@ -1018,6 +1025,13 @@ pub const LinkObject = struct { | ... | @@ -1018,6 +1025,13 @@ pub const LinkObject = struct { |
| 1018 | // | 1025 | // |
| 1019 | // Consistent with `withLOption` variable name in lld ELF driver. | 1026 | // Consistent with `withLOption` variable name in lld ELF driver. |
| 1020 | loption: bool = false, | 1027 | 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 | } | ||
| 1021 | }; | 1035 | }; |
| 1022 | 1036 | ||
| 1023 | pub const CreateOptions = struct { | 1037 | pub const CreateOptions = struct { |
| ... | @@ -2433,7 +2447,7 @@ fn flush( | ... | @@ -2433,7 +2447,7 @@ fn flush( |
| 2433 | if (comp.bin_file) |lf| { | 2447 | if (comp.bin_file) |lf| { |
| 2434 | // This is needed before reading the error flags. | 2448 | // This is needed before reading the error flags. |
| 2435 | lf.flush(arena, tid, prog_node) catch |err| switch (err) { | 2449 | 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 |
| 2437 | error.LLDReportedFailure => {}, // error reported via lockAndParseLldStderr | 2451 | error.LLDReportedFailure => {}, // error reported via lockAndParseLldStderr |
| 2438 | else => |e| return e, | 2452 | else => |e| return e, |
| 2439 | }; | 2453 | }; |
src/link.zig+3| ... | @@ -533,7 +533,10 @@ pub const File = struct { | ... | @@ -533,7 +533,10 @@ pub const File = struct { |
| 533 | FailedToEmit, | 533 | FailedToEmit, |
| 534 | FileSystem, | 534 | FileSystem, |
| 535 | FilesOpenedWithWrongFlags, | 535 | FilesOpenedWithWrongFlags, |
| 536 | /// Indicates an error will be present in `Compilation.link_errors`. | ||
| 536 | FlushFailure, | 537 | FlushFailure, |
| 538 | /// Indicates an error will be present in `Compilation.link_errors`. | ||
| 539 | LinkFailure, | ||
| 537 | FunctionSignatureMismatch, | 540 | FunctionSignatureMismatch, |
| 538 | GlobalTypeMismatch, | 541 | GlobalTypeMismatch, |
| 539 | HotSwapUnavailableOnHostOperatingSystem, | 542 | HotSwapUnavailableOnHostOperatingSystem, |
src/link/Elf.zig+84-98| ... | @@ -791,8 +791,8 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod | ... | @@ -791,8 +791,8 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 791 | const target = self.getTarget(); | 791 | const target = self.getTarget(); |
| 792 | const link_mode = comp.config.link_mode; | 792 | const link_mode = comp.config.link_mode; |
| 793 | const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. | 793 | 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}); | ||
| 795 | const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { | 794 | 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}); | ||
| 796 | if (fs.path.dirname(full_out_path)) |dirname| { | 796 | if (fs.path.dirname(full_out_path)) |dirname| { |
| 797 | break :blk try fs.path.join(arena, &.{ dirname, path }); | 797 | break :blk try fs.path.join(arena, &.{ dirname, path }); |
| 798 | } else { | 798 | } else { |
| ... | @@ -808,61 +808,37 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod | ... | @@ -808,61 +808,37 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 808 | if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); | 808 | if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); |
| 809 | 809 | ||
| 810 | const csu = try CsuObjects.init(arena, comp); | 810 | 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 | }; | ||
| 816 | 811 | ||
| 817 | // Here we will parse input positional and library files (if referenced). | 812 | // Here we will parse object 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); | ||
| 820 | 813 | ||
| 821 | // csu prelude | 814 | // csu prelude |
| 822 | if (csu.crt0) |v| try positionals.append(.{ .path = v }); | 815 | if (csu.crt0) |path| try parseObjectReportingFailure(self, path); |
| 823 | if (csu.crti) |v| try positionals.append(.{ .path = v }); | 816 | if (csu.crti) |path| try parseObjectReportingFailure(self, path); |
| 824 | if (csu.crtbegin) |v| try positionals.append(.{ .path = v }); | 817 | if (csu.crtbegin) |path| try parseObjectReportingFailure(self, path); |
| 825 | 818 | ||
| 826 | try positionals.ensureUnusedCapacity(comp.objects.len); | 819 | for (comp.objects) |obj| { |
| 827 | positionals.appendSliceAssumeCapacity(comp.objects); | 820 | if (obj.isObject()) { |
| 821 | try parseObjectReportingFailure(self, obj.path); | ||
| 822 | } else { | ||
| 823 | try parseLibraryReportingFailure(self, .{ .path = obj.path }, obj.must_link); | ||
| 824 | } | ||
| 825 | } | ||
| 828 | 826 | ||
| 829 | // This is a set of object files emitted by clang in a single `build-exe` invocation. | 827 | // This is a set of object files emitted by clang in a single `build-exe` invocation. |
| 830 | // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up | 828 | // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up |
| 831 | // in this set. | 829 | // in this set. |
| 832 | for (comp.c_object_table.keys()) |key| { | 830 | 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); |
| 834 | } | 832 | } |
| 835 | 833 | ||
| 836 | if (module_obj_path) |path| try positionals.append(.{ .path = path }); | 834 | if (module_obj_path) |path| try parseObjectReportingFailure(self, path); |
| 837 | |||
| 838 | if (comp.config.any_sanitize_thread) { | ||
| 839 | try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path }); | ||
| 840 | } | ||
| 841 | 835 | ||
| 842 | if (comp.config.any_fuzz) { | 836 | if (comp.config.any_sanitize_thread) try parseCrtFileReportingFailure(self, comp.tsan_lib.?); |
| 843 | try positionals.append(.{ .path = comp.fuzzer_lib.?.full_object_path }); | 837 | if (comp.config.any_fuzz) try parseCrtFileReportingFailure(self, comp.fuzzer_lib.?); |
| 844 | } | ||
| 845 | 838 | ||
| 846 | // libc | 839 | // libc |
| 847 | if (!comp.skip_linker_dependencies and !comp.config.link_libc) { | 840 | if (!comp.skip_linker_dependencies and !comp.config.link_libc) { |
| 848 | if (comp.libc_static_lib) |lib| { | 841 | if (comp.libc_static_lib) |lib| try parseCrtFileReportingFailure(self, 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 | }; | ||
| 866 | } | 842 | } |
| 867 | 843 | ||
| 868 | var system_libs = std.ArrayList(SystemLib).init(arena); | 844 | 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 | ... | @@ -945,42 +921,23 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 945 | } | 921 | } |
| 946 | 922 | ||
| 947 | for (system_libs.items) |lib| { | 923 | for (system_libs.items) |lib| { |
| 948 | self.parseLibrary(lib, false) catch |err| switch (err) { | 924 | try self.parseLibraryReportingFailure(lib, false); |
| 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 | }; | ||
| 956 | } | 925 | } |
| 957 | 926 | ||
| 958 | // Finally, as the last input objects we add compiler_rt and CSU postlude (if any). | 927 | // Finally, as the last input objects we add compiler_rt and CSU postlude (if any). |
| 959 | positionals.clearRetainingCapacity(); | ||
| 960 | 928 | ||
| 961 | // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs | 929 | // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs |
| 962 | // to be after the shared libraries, so they are picked up from the shared | 930 | // to be after the shared libraries, so they are picked up from the shared |
| 963 | // libraries, not libcompiler_rt. | 931 | // 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 | } | ||
| 965 | 937 | ||
| 966 | // csu postlude | 938 | // csu postlude |
| 967 | if (csu.crtend) |v| try positionals.append(.{ .path = v }); | 939 | if (csu.crtend) |path| try parseObjectReportingFailure(self, path); |
| 968 | if (csu.crtn) |v| try positionals.append(.{ .path = v }); | 940 | if (csu.crtn) |path| try parseObjectReportingFailure(self, path); |
| 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 | } | ||
| 984 | 941 | ||
| 985 | if (self.base.hasErrors()) return error.FlushFailure; | 942 | if (self.base.hasErrors()) return error.FlushFailure; |
| 986 | 943 | ||
| ... | @@ -1022,7 +979,9 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod | ... | @@ -1022,7 +979,9 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 1022 | self.markEhFrameAtomsDead(); | 979 | self.markEhFrameAtomsDead(); |
| 1023 | try self.resolveMergeSections(); | 980 | try self.resolveMergeSections(); |
| 1024 | 981 | ||
| 1025 | try self.convertCommonSymbols(); | 982 | for (self.objects.items) |index| { |
| 983 | try self.file(index).?.object.convertCommonSymbols(self); | ||
| 984 | } | ||
| 1026 | self.markImportsExports(); | 985 | self.markImportsExports(); |
| 1027 | 986 | ||
| 1028 | if (self.base.gc_sections) { | 987 | if (self.base.gc_sections) { |
| ... | @@ -1402,10 +1361,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { | ... | @@ -1402,10 +1361,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { |
| 1402 | } | 1361 | } |
| 1403 | 1362 | ||
| 1404 | pub const ParseError = error{ | 1363 | pub const ParseError = error{ |
| 1405 | MalformedObject, | 1364 | /// Indicates the error is already reported on `Compilation.link_errors`. |
| 1406 | MalformedArchive, | 1365 | LinkFailure, |
| 1407 | InvalidMachineType, | 1366 | |
| 1408 | MismatchedEflags, | ||
| 1409 | OutOfMemory, | 1367 | OutOfMemory, |
| 1410 | Overflow, | 1368 | Overflow, |
| 1411 | InputOutput, | 1369 | InputOutput, |
| ... | @@ -1416,16 +1374,30 @@ pub const ParseError = error{ | ... | @@ -1416,16 +1374,30 @@ pub const ParseError = error{ |
| 1416 | UnknownFileType, | 1374 | UnknownFileType, |
| 1417 | } || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError; | 1375 | } || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError; |
| 1418 | 1376 | ||
| 1419 | pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void { | 1377 | fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CRTFile) error{OutOfMemory}!void { |
| 1420 | const tracy = trace(@src()); | 1378 | if (crt_file.isObject()) { |
| 1421 | defer tracy.end(); | 1379 | try parseObjectReportingFailure(self, crt_file.full_object_path); |
| 1422 | if (try Object.isObject(path)) { | ||
| 1423 | try self.parseObject(path); | ||
| 1424 | } else { | 1380 | } else { |
| 1425 | try self.parseLibrary(.{ .path = path }, must_link); | 1381 | try parseLibraryReportingFailure(self, .{ .path = crt_file.full_object_path }, false); |
| 1426 | } | 1382 | } |
| 1427 | } | 1383 | } |
| 1428 | 1384 | ||
| 1385 | pub 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 | |||
| 1393 | pub 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 | |||
| 1429 | fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool) ParseError!void { | 1401 | fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool) ParseError!void { |
| 1430 | const tracy = trace(@src()); | 1402 | const tracy = trace(@src()); |
| 1431 | defer tracy.end(); | 1403 | defer tracy.end(); |
| ... | @@ -1575,8 +1547,8 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { | ... | @@ -1575,8 +1547,8 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { |
| 1575 | .needed = scr_obj.needed, | 1547 | .needed = scr_obj.needed, |
| 1576 | .path = full_path, | 1548 | .path = full_path, |
| 1577 | }, false) catch |err| switch (err) { | 1549 | }, false) catch |err| switch (err) { |
| 1578 | error.MalformedObject, error.MalformedArchive, error.InvalidMachineType => continue, // already reported | 1550 | error.LinkFailure => continue, // already reported |
| 1579 | else => |e| try self.reportParseError( | 1551 | else => |e| try self.addParseError( |
| 1580 | full_path, | 1552 | full_path, |
| 1581 | "unexpected error: parsing library failed with error {s}", | 1553 | "unexpected error: parsing library failed with error {s}", |
| 1582 | .{@errorName(e)}, | 1554 | .{@errorName(e)}, |
| ... | @@ -1601,24 +1573,24 @@ pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Elf64_Wor | ... | @@ -1601,24 +1573,24 @@ pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Elf64_Wor |
| 1601 | self_riscv_eflags.rvc = self_riscv_eflags.rvc or riscv_eflags.rvc; | 1573 | self_riscv_eflags.rvc = self_riscv_eflags.rvc or riscv_eflags.rvc; |
| 1602 | self_riscv_eflags.tso = self_riscv_eflags.tso or riscv_eflags.tso; | 1574 | self_riscv_eflags.tso = self_riscv_eflags.tso or riscv_eflags.tso; |
| 1603 | 1575 | ||
| 1604 | var is_error: bool = false; | 1576 | var any_errors: bool = false; |
| 1605 | if (self_riscv_eflags.fabi != riscv_eflags.fabi) { | 1577 | if (self_riscv_eflags.fabi != riscv_eflags.fabi) { |
| 1606 | is_error = true; | 1578 | any_errors = true; |
| 1607 | _ = try self.reportParseError2( | 1579 | try self.addFileError( |
| 1608 | file_index, | 1580 | file_index, |
| 1609 | "cannot link object files with different float-point ABIs", | 1581 | "cannot link object files with different float-point ABIs", |
| 1610 | .{}, | 1582 | .{}, |
| 1611 | ); | 1583 | ); |
| 1612 | } | 1584 | } |
| 1613 | if (self_riscv_eflags.rve != riscv_eflags.rve) { | 1585 | if (self_riscv_eflags.rve != riscv_eflags.rve) { |
| 1614 | is_error = true; | 1586 | any_errors = true; |
| 1615 | _ = try self.reportParseError2( | 1587 | try self.addFileError( |
| 1616 | file_index, | 1588 | file_index, |
| 1617 | "cannot link object files with different RVEs", | 1589 | "cannot link object files with different RVEs", |
| 1618 | .{}, | 1590 | .{}, |
| 1619 | ); | 1591 | ); |
| 1620 | } | 1592 | } |
| 1621 | if (is_error) return error.MismatchedEflags; | 1593 | if (any_errors) return error.LinkFailure; |
| 1622 | } | 1594 | } |
| 1623 | }, | 1595 | }, |
| 1624 | else => {}, | 1596 | else => {}, |
| ... | @@ -1740,12 +1712,6 @@ pub fn markEhFrameAtomsDead(self: *Elf) void { | ... | @@ -1740,12 +1712,6 @@ pub fn markEhFrameAtomsDead(self: *Elf) void { |
| 1740 | } | 1712 | } |
| 1741 | } | 1713 | } |
| 1742 | 1714 | ||
| 1743 | fn convertCommonSymbols(self: *Elf) !void { | ||
| 1744 | for (self.objects.items) |index| { | ||
| 1745 | try self.file(index).?.object.convertCommonSymbols(self); | ||
| 1746 | } | ||
| 1747 | } | ||
| 1748 | |||
| 1749 | fn markImportsExports(self: *Elf) void { | 1715 | fn markImportsExports(self: *Elf) void { |
| 1750 | if (self.zigObjectPtr()) |zo| { | 1716 | if (self.zigObjectPtr()) |zo| { |
| 1751 | zo.markImportsExports(self); | 1717 | zo.markImportsExports(self); |
| ... | @@ -2838,7 +2804,7 @@ pub fn resolveMergeSections(self: *Elf) !void { | ... | @@ -2838,7 +2804,7 @@ pub fn resolveMergeSections(self: *Elf) !void { |
| 2838 | const file_ptr = self.file(index).?; | 2804 | const file_ptr = self.file(index).?; |
| 2839 | if (!file_ptr.isAlive()) continue; | 2805 | if (!file_ptr.isAlive()) continue; |
| 2840 | file_ptr.object.initInputMergeSections(self) catch |err| switch (err) { | 2806 | file_ptr.object.initInputMergeSections(self) catch |err| switch (err) { |
| 2841 | error.MalformedObject => has_errors = true, | 2807 | error.LinkFailure => has_errors = true, |
| 2842 | else => |e| return e, | 2808 | else => |e| return e, |
| 2843 | }; | 2809 | }; |
| 2844 | } | 2810 | } |
| ... | @@ -2855,12 +2821,12 @@ pub fn resolveMergeSections(self: *Elf) !void { | ... | @@ -2855,12 +2821,12 @@ pub fn resolveMergeSections(self: *Elf) !void { |
| 2855 | const file_ptr = self.file(index).?; | 2821 | const file_ptr = self.file(index).?; |
| 2856 | if (!file_ptr.isAlive()) continue; | 2822 | if (!file_ptr.isAlive()) continue; |
| 2857 | file_ptr.object.resolveMergeSubsections(self) catch |err| switch (err) { | 2823 | file_ptr.object.resolveMergeSubsections(self) catch |err| switch (err) { |
| 2858 | error.MalformedObject => has_errors = true, | 2824 | error.LinkFailure => has_errors = true, |
| 2859 | else => |e| return e, | 2825 | else => |e| return e, |
| 2860 | }; | 2826 | }; |
| 2861 | } | 2827 | } |
| 2862 | 2828 | ||
| 2863 | if (has_errors) return error.FlushFailure; | 2829 | if (has_errors) return error.LinkFailure; |
| 2864 | } | 2830 | } |
| 2865 | 2831 | ||
| 2866 | pub fn finalizeMergeSections(self: *Elf) !void { | 2832 | pub fn finalizeMergeSections(self: *Elf) !void { |
| ... | @@ -5192,7 +5158,7 @@ fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void { | ... | @@ -5192,7 +5158,7 @@ fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void { |
| 5192 | }); | 5158 | }); |
| 5193 | } | 5159 | } |
| 5194 | 5160 | ||
| 5195 | pub fn reportParseError( | 5161 | pub fn addParseError( |
| 5196 | self: *Elf, | 5162 | self: *Elf, |
| 5197 | path: []const u8, | 5163 | path: []const u8, |
| 5198 | comptime format: []const u8, | 5164 | comptime format: []const u8, |
| ... | @@ -5203,7 +5169,7 @@ pub fn reportParseError( | ... | @@ -5203,7 +5169,7 @@ pub fn reportParseError( |
| 5203 | try err.addNote("while parsing {s}", .{path}); | 5169 | try err.addNote("while parsing {s}", .{path}); |
| 5204 | } | 5170 | } |
| 5205 | 5171 | ||
| 5206 | pub fn reportParseError2( | 5172 | pub fn addFileError( |
| 5207 | self: *Elf, | 5173 | self: *Elf, |
| 5208 | file_index: File.Index, | 5174 | file_index: File.Index, |
| 5209 | comptime format: []const u8, | 5175 | comptime format: []const u8, |
| ... | @@ -5214,6 +5180,26 @@ pub fn reportParseError2( | ... | @@ -5214,6 +5180,26 @@ pub fn reportParseError2( |
| 5214 | try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()}); | 5180 | try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()}); |
| 5215 | } | 5181 | } |
| 5216 | 5182 | ||
| 5183 | pub 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 | |||
| 5193 | pub 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 | |||
| 5217 | const FormatShdrCtx = struct { | 5203 | const FormatShdrCtx = struct { |
| 5218 | elf_file: *Elf, | 5204 | elf_file: *Elf, |
| 5219 | shdr: elf.Elf64_Shdr, | 5205 | 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 | ... | @@ -35,10 +35,9 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: Fil |
| 35 | pos += @sizeOf(elf.ar_hdr); | 35 | pos += @sizeOf(elf.ar_hdr); |
| 36 | 36 | ||
| 37 | if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) { | 37 | 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}", .{ |
| 39 | std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag), | 39 | std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag), |
| 40 | }); | 40 | }); |
| 41 | return error.MalformedArchive; | ||
| 42 | } | 41 | } |
| 43 | 42 | ||
| 44 | const obj_size = try hdr.size(); | 43 | 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 { | ... | @@ -7,7 +7,7 @@ pub fn deinit(scr: *LdScript, allocator: Allocator) void { |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | pub const Error = error{ | 9 | pub const Error = error{ |
| 10 | InvalidLdScript, | 10 | LinkFailure, |
| 11 | UnexpectedToken, | 11 | UnexpectedToken, |
| 12 | UnknownCpuArch, | 12 | UnknownCpuArch, |
| 13 | OutOfMemory, | 13 | OutOfMemory, |
| ... | @@ -32,12 +32,9 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { | ... | @@ -32,12 +32,9 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { |
| 32 | try line_col.append(.{ .line = line, .column = column }); | 32 | try line_col.append(.{ .line = line, .column = column }); |
| 33 | switch (tok.id) { | 33 | switch (tok.id) { |
| 34 | .invalid => { | 34 | .invalid => { |
| 35 | try elf_file.reportParseError(scr.path, "invalid token in LD script: '{s}' ({d}:{d})", .{ | 35 | return elf_file.failParse(scr.path, "invalid token in LD script: '{s}' ({d}:{d})", .{ |
| 36 | std.fmt.fmtSliceEscapeLower(tok.get(data)), | 36 | std.fmt.fmtSliceEscapeLower(tok.get(data)), line, column, |
| 37 | line, | ||
| 38 | column, | ||
| 39 | }); | 37 | }); |
| 40 | return error.InvalidLdScript; | ||
| 41 | }, | 38 | }, |
| 42 | .new_line => { | 39 | .new_line => { |
| 43 | line += 1; | 40 | line += 1; |
| ... | @@ -59,13 +56,12 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { | ... | @@ -59,13 +56,12 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { |
| 59 | const last_token_id = parser.it.pos - 1; | 56 | const last_token_id = parser.it.pos - 1; |
| 60 | const last_token = parser.it.get(last_token_id); | 57 | const last_token = parser.it.get(last_token_id); |
| 61 | const lcol = line_col.items[last_token_id]; | 58 | 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})", .{ |
| 63 | @tagName(last_token.id), | 60 | @tagName(last_token.id), |
| 64 | last_token.get(data), | 61 | last_token.get(data), |
| 65 | lcol.line, | 62 | lcol.line, |
| 66 | lcol.column, | 63 | lcol.column, |
| 67 | }); | 64 | }); |
| 68 | return error.InvalidLdScript; | ||
| 69 | }, | 65 | }, |
| 70 | else => |e| return e, | 66 | else => |e| return e, |
| 71 | }; | 67 | }; |
src/link/Elf/Object.zig+20-53| ... | @@ -34,18 +34,6 @@ num_dynrelocs: u32 = 0, | ... | @@ -34,18 +34,6 @@ num_dynrelocs: u32 = 0, |
| 34 | output_symtab_ctx: Elf.SymtabCtx = .{}, | 34 | output_symtab_ctx: Elf.SymtabCtx = .{}, |
| 35 | output_ar_state: Archive.ArState = .{}, | 35 | output_ar_state: Archive.ArState = .{}, |
| 36 | 36 | ||
| 37 | pub 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 | |||
| 49 | pub fn deinit(self: *Object, allocator: Allocator) void { | 37 | pub fn deinit(self: *Object, allocator: Allocator) void { |
| 50 | if (self.archive) |*ar| allocator.free(ar.path); | 38 | if (self.archive) |*ar| allocator.free(ar.path); |
| 51 | allocator.free(self.path); | 39 | allocator.free(self.path); |
| ... | @@ -107,12 +95,9 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil | ... | @@ -107,12 +95,9 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil |
| 107 | 95 | ||
| 108 | const em = elf_file.base.comp.root_mod.resolved_target.result.toElfMachine(); | 96 | const em = elf_file.base.comp.root_mod.resolved_target.result.toElfMachine(); |
| 109 | if (em != self.header.?.e_machine) { | 97 | if (em != self.header.?.e_machine) { |
| 110 | try elf_file.reportParseError2( | 98 | return elf_file.failFile(self.index, "invalid ELF machine type: {s}", .{ |
| 111 | self.index, | 99 | @tagName(self.header.?.e_machine), |
| 112 | "invalid ELF machine type: {s}", | 100 | }); |
| 113 | .{@tagName(self.header.?.e_machine)}, | ||
| 114 | ); | ||
| 115 | return error.InvalidMachineType; | ||
| 116 | } | 101 | } |
| 117 | try elf_file.validateEFlags(self.index, self.header.?.e_flags); | 102 | try elf_file.validateEFlags(self.index, self.header.?.e_flags); |
| 118 | 103 | ||
| ... | @@ -122,12 +107,7 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil | ... | @@ -122,12 +107,7 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil |
| 122 | const shnum = math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow; | 107 | const shnum = math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow; |
| 123 | const shsize = shnum * @sizeOf(elf.Elf64_Shdr); | 108 | const shsize = shnum * @sizeOf(elf.Elf64_Shdr); |
| 124 | if (file_size < offset + shoff or file_size < offset + shoff + shsize) { | 109 | if (file_size < offset + shoff or file_size < offset + shoff + shsize) { |
| 125 | try elf_file.reportParseError2( | 110 | return elf_file.failFile(self.index, "corrupt header: section header table extends past the end of file", .{}); |
| 126 | self.index, | ||
| 127 | "corrupt header: section header table extends past the end of file", | ||
| 128 | .{}, | ||
| 129 | ); | ||
| 130 | return error.MalformedObject; | ||
| 131 | } | 111 | } |
| 132 | 112 | ||
| 133 | const shdrs_buffer = try Elf.preadAllAlloc(allocator, handle, offset + shoff, shsize); | 113 | 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 | ... | @@ -138,8 +118,7 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil |
| 138 | for (self.shdrs.items) |shdr| { | 118 | for (self.shdrs.items) |shdr| { |
| 139 | if (shdr.sh_type != elf.SHT_NOBITS) { | 119 | if (shdr.sh_type != elf.SHT_NOBITS) { |
| 140 | if (file_size < offset + shdr.sh_offset or file_size < offset + shdr.sh_offset + shdr.sh_size) { | 120 | 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", .{}); | 121 | return elf_file.failFile(self.index, "corrupt section: extends past the end of file", .{}); |
| 142 | return error.MalformedObject; | ||
| 143 | } | 122 | } |
| 144 | } | 123 | } |
| 145 | } | 124 | } |
| ... | @@ -148,8 +127,7 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil | ... | @@ -148,8 +127,7 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil |
| 148 | defer allocator.free(shstrtab); | 127 | defer allocator.free(shstrtab); |
| 149 | for (self.shdrs.items) |shdr| { | 128 | for (self.shdrs.items) |shdr| { |
| 150 | if (shdr.sh_name >= shstrtab.len) { | 129 | if (shdr.sh_name >= shstrtab.len) { |
| 151 | try elf_file.reportParseError2(self.index, "corrupt section name offset", .{}); | 130 | return elf_file.failFile(self.index, "corrupt section name offset", .{}); |
| 152 | return error.MalformedObject; | ||
| 153 | } | 131 | } |
| 154 | } | 132 | } |
| 155 | try self.strtab.appendSlice(allocator, shstrtab); | 133 | try self.strtab.appendSlice(allocator, shstrtab); |
| ... | @@ -166,8 +144,7 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil | ... | @@ -166,8 +144,7 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil |
| 166 | const raw_symtab = try self.preadShdrContentsAlloc(allocator, handle, index); | 144 | const raw_symtab = try self.preadShdrContentsAlloc(allocator, handle, index); |
| 167 | defer allocator.free(raw_symtab); | 145 | defer allocator.free(raw_symtab); |
| 168 | const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch { | 146 | 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", .{}); | 147 | return elf_file.failFile(self.index, "symbol table not evenly divisible", .{}); |
| 170 | return error.MalformedObject; | ||
| 171 | }; | 148 | }; |
| 172 | const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms]; | 149 | const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms]; |
| 173 | 150 | ||
| ... | @@ -221,30 +198,15 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: | ... | @@ -221,30 +198,15 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: |
| 221 | const group_raw_data = try self.preadShdrContentsAlloc(allocator, handle, shndx); | 198 | const group_raw_data = try self.preadShdrContentsAlloc(allocator, handle, shndx); |
| 222 | defer allocator.free(group_raw_data); | 199 | defer allocator.free(group_raw_data); |
| 223 | const group_nmembers = math.divExact(usize, group_raw_data.len, @sizeOf(u32)) catch { | 200 | const group_nmembers = math.divExact(usize, group_raw_data.len, @sizeOf(u32)) catch { |
| 224 | try elf_file.reportParseError2( | 201 | return elf_file.failFile(self.index, "corrupt section group: not evenly divisible ", .{}); |
| 225 | self.index, | ||
| 226 | "corrupt section group: not evenly divisible ", | ||
| 227 | .{}, | ||
| 228 | ); | ||
| 229 | return error.MalformedObject; | ||
| 230 | }; | 202 | }; |
| 231 | if (group_nmembers == 0) { | 203 | if (group_nmembers == 0) { |
| 232 | try elf_file.reportParseError2( | 204 | return elf_file.failFile(self.index, "corrupt section group: empty section", .{}); |
| 233 | self.index, | ||
| 234 | "corrupt section group: empty section", | ||
| 235 | .{}, | ||
| 236 | ); | ||
| 237 | return error.MalformedObject; | ||
| 238 | } | 205 | } |
| 239 | const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers]; | 206 | const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers]; |
| 240 | 207 | ||
| 241 | if (group_members[0] != elf.GRP_COMDAT) { | 208 | if (group_members[0] != elf.GRP_COMDAT) { |
| 242 | try elf_file.reportParseError2( | 209 | return elf_file.failFile(self.index, "corrupt section group: unknown SHT_GROUP format", .{}); |
| 243 | self.index, | ||
| 244 | "corrupt section group: unknown SHT_GROUP format", | ||
| 245 | .{}, | ||
| 246 | ); | ||
| 247 | return error.MalformedObject; | ||
| 248 | } | 210 | } |
| 249 | 211 | ||
| 250 | const group_start = @as(u32, @intCast(self.comdat_group_data.items.len)); | 212 | 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 { | ... | @@ -722,7 +684,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { |
| 722 | var err = try elf_file.base.addErrorWithNotes(1); | 684 | var err = try elf_file.base.addErrorWithNotes(1); |
| 723 | try err.addMsg("string not null terminated", .{}); | 685 | try err.addMsg("string not null terminated", .{}); |
| 724 | try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); | 686 | try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); |
| 725 | return error.MalformedObject; | 687 | return error.LinkFailure; |
| 726 | } | 688 | } |
| 727 | end += sh_entsize; | 689 | end += sh_entsize; |
| 728 | const string = data[start..end]; | 690 | const string = data[start..end]; |
| ... | @@ -737,7 +699,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { | ... | @@ -737,7 +699,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { |
| 737 | var err = try elf_file.base.addErrorWithNotes(1); | 699 | var err = try elf_file.base.addErrorWithNotes(1); |
| 738 | try err.addMsg("size not a multiple of sh_entsize", .{}); | 700 | try err.addMsg("size not a multiple of sh_entsize", .{}); |
| 739 | try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); | 701 | try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); |
| 740 | return error.MalformedObject; | 702 | return error.LinkFailure; |
| 741 | } | 703 | } |
| 742 | 704 | ||
| 743 | var pos: u32 = 0; | 705 | var pos: u32 = 0; |
| ... | @@ -765,7 +727,12 @@ pub fn initOutputMergeSections(self: *Object, elf_file: *Elf) !void { | ... | @@ -765,7 +727,12 @@ pub fn initOutputMergeSections(self: *Object, elf_file: *Elf) !void { |
| 765 | } | 727 | } |
| 766 | } | 728 | } |
| 767 | 729 | ||
| 768 | pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { | 730 | pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{ |
| 731 | LinkFailure, | ||
| 732 | OutOfMemory, | ||
| 733 | /// TODO report the error and remove this | ||
| 734 | Overflow, | ||
| 735 | }!void { | ||
| 769 | const gpa = elf_file.base.comp.gpa; | 736 | const gpa = elf_file.base.comp.gpa; |
| 770 | 737 | ||
| 771 | for (self.input_merge_sections_indexes.items) |index| { | 738 | for (self.input_merge_sections_indexes.items) |index| { |
| ... | @@ -809,7 +776,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { | ... | @@ -809,7 +776,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { |
| 809 | try err.addMsg("invalid symbol value: {x}", .{esym.st_value}); | 776 | try err.addMsg("invalid symbol value: {x}", .{esym.st_value}); |
| 810 | try err.addNote("for symbol {s}", .{sym.name(elf_file)}); | 777 | try err.addNote("for symbol {s}", .{sym.name(elf_file)}); |
| 811 | try err.addNote("in {}", .{self.fmtPath()}); | 778 | try err.addNote("in {}", .{self.fmtPath()}); |
| 812 | return error.MalformedObject; | 779 | return error.LinkFailure; |
| 813 | }; | 780 | }; |
| 814 | 781 | ||
| 815 | sym.ref = .{ .index = res.msub_index, .file = imsec.merge_section_index }; | 782 | sym.ref = .{ .index = res.msub_index, .file = imsec.merge_section_index }; |
| ... | @@ -834,7 +801,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { | ... | @@ -834,7 +801,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { |
| 834 | var err = try elf_file.base.addErrorWithNotes(1); | 801 | var err = try elf_file.base.addErrorWithNotes(1); |
| 835 | try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset}); | 802 | try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset}); |
| 836 | try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); | 803 | try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); |
| 837 | return error.MalformedObject; | 804 | return error.LinkFailure; |
| 838 | }; | 805 | }; |
| 839 | 806 | ||
| 840 | const sym_index = try self.addSymbol(gpa); | 807 | 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 { | ... | @@ -58,24 +58,16 @@ pub fn parse(self: *SharedObject, elf_file: *Elf, handle: std.fs.File) !void { |
| 58 | 58 | ||
| 59 | const em = elf_file.base.comp.root_mod.resolved_target.result.toElfMachine(); | 59 | const em = elf_file.base.comp.root_mod.resolved_target.result.toElfMachine(); |
| 60 | if (em != self.header.?.e_machine) { | 60 | if (em != self.header.?.e_machine) { |
| 61 | try elf_file.reportParseError2( | 61 | return elf_file.failFile(self.index, "invalid ELF machine type: {s}", .{ |
| 62 | self.index, | 62 | @tagName(self.header.?.e_machine), |
| 63 | "invalid ELF machine type: {s}", | 63 | }); |
| 64 | .{@tagName(self.header.?.e_machine)}, | ||
| 65 | ); | ||
| 66 | return error.InvalidMachineType; | ||
| 67 | } | 64 | } |
| 68 | 65 | ||
| 69 | const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow; | 66 | const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow; |
| 70 | const shnum = std.math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow; | 67 | const shnum = std.math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow; |
| 71 | const shsize = shnum * @sizeOf(elf.Elf64_Shdr); | 68 | const shsize = shnum * @sizeOf(elf.Elf64_Shdr); |
| 72 | if (file_size < shoff or file_size < shoff + shsize) { | 69 | if (file_size < shoff or file_size < shoff + shsize) { |
| 73 | try elf_file.reportParseError2( | 70 | return elf_file.failFile(self.index, "corrupted header: section header table extends past the end of file", .{}); |
| 74 | self.index, | ||
| 75 | "corrupted header: section header table extends past the end of file", | ||
| 76 | .{}, | ||
| 77 | ); | ||
| 78 | return error.MalformedObject; | ||
| 79 | } | 71 | } |
| 80 | 72 | ||
| 81 | const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, shoff, shsize); | 73 | 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 { | ... | @@ -90,8 +82,7 @@ pub fn parse(self: *SharedObject, elf_file: *Elf, handle: std.fs.File) !void { |
| 90 | for (self.shdrs.items, 0..) |shdr, i| { | 82 | for (self.shdrs.items, 0..) |shdr, i| { |
| 91 | if (shdr.sh_type != elf.SHT_NOBITS) { | 83 | if (shdr.sh_type != elf.SHT_NOBITS) { |
| 92 | if (file_size < shdr.sh_offset or file_size < shdr.sh_offset + shdr.sh_size) { | 84 | 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", .{}); | 85 | return elf_file.failFile(self.index, "corrupted section header", .{}); |
| 94 | return error.MalformedObject; | ||
| 95 | } | 86 | } |
| 96 | } | 87 | } |
| 97 | switch (shdr.sh_type) { | 88 | switch (shdr.sh_type) { |
src/link/Elf/relocatable.zig+36-56| ... | @@ -1,36 +1,24 @@ | ... | @@ -1,36 +1,24 @@ |
| 1 | pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { | 1 | pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { |
| 2 | const gpa = comp.gpa; | 2 | const gpa = comp.gpa; |
| 3 | 3 | ||
| 4 | var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); | 4 | for (comp.objects) |obj| { |
| 5 | defer positionals.deinit(); | 5 | switch (Compilation.classifyFileExt(obj.path)) { |
| 6 | 6 | .object => try parseObjectStaticLibReportingFailure(elf_file, obj.path), | |
| 7 | try positionals.ensureUnusedCapacity(comp.objects.len); | 7 | .static_library => try parseArchiveStaticLibReportingFailure(elf_file, obj.path), |
| 8 | positionals.appendSliceAssumeCapacity(comp.objects); | 8 | else => try elf_file.addParseError(obj.path, "unrecognized file extension", .{}), |
| 9 | } | ||
| 10 | } | ||
| 9 | 11 | ||
| 10 | for (comp.c_object_table.keys()) |key| { | 12 | 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); |
| 12 | } | 14 | } |
| 13 | 15 | ||
| 14 | if (module_obj_path) |path| try positionals.append(.{ .path = path }); | 16 | if (module_obj_path) |path| { |
| 17 | try parseObjectStaticLibReportingFailure(elf_file, path); | ||
| 18 | } | ||
| 15 | 19 | ||
| 16 | if (comp.include_compiler_rt) { | 20 | if (comp.include_compiler_rt) { |
| 17 | try positionals.append(.{ .path = comp.compiler_rt_obj.?.full_object_path }); | 21 | try parseObjectStaticLibReportingFailure(elf_file, 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 | }; | ||
| 34 | } | 22 | } |
| 35 | 23 | ||
| 36 | if (elf_file.base.hasErrors()) return error.FlushFailure; | 24 | if (elf_file.base.hasErrors()) return error.FlushFailure; |
| ... | @@ -153,37 +141,23 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co | ... | @@ -153,37 +141,23 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co |
| 153 | } | 141 | } |
| 154 | 142 | ||
| 155 | pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { | 143 | pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { |
| 156 | const gpa = elf_file.base.comp.gpa; | 144 | for (comp.objects) |obj| { |
| 157 | 145 | if (obj.isObject()) { | |
| 158 | var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); | 146 | try elf_file.parseObjectReportingFailure(obj.path); |
| 159 | defer positionals.deinit(); | 147 | } else { |
| 160 | try positionals.ensureUnusedCapacity(comp.objects.len); | 148 | try elf_file.parseLibraryReportingFailure(.{ .path = obj.path }, obj.must_link); |
| 161 | positionals.appendSliceAssumeCapacity(comp.objects); | 149 | } |
| 150 | } | ||
| 162 | 151 | ||
| 163 | // This is a set of object files emitted by clang in a single `build-exe` invocation. | 152 | // This is a set of object files emitted by clang in a single `build-exe` invocation. |
| 164 | // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up | 153 | // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up |
| 165 | // in this set. | 154 | // in this set. |
| 166 | for (comp.c_object_table.keys()) |key| { | 155 | for (comp.c_object_table.keys()) |key| { |
| 167 | try positionals.append(.{ .path = key.status.success.object_path }); | 156 | try elf_file.parseObjectReportingFailure(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 | }; | ||
| 185 | } | 157 | } |
| 186 | 158 | ||
| 159 | if (module_obj_path) |path| try elf_file.parseObjectReportingFailure(path); | ||
| 160 | |||
| 187 | if (elf_file.base.hasErrors()) return error.FlushFailure; | 161 | if (elf_file.base.hasErrors()) return error.FlushFailure; |
| 188 | 162 | ||
| 189 | // Now, we are ready to resolve the symbols across all input files. | 163 | // 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 | ... | @@ -224,14 +198,20 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const |
| 224 | if (elf_file.base.hasErrors()) return error.FlushFailure; | 198 | if (elf_file.base.hasErrors()) return error.FlushFailure; |
| 225 | } | 199 | } |
| 226 | 200 | ||
| 227 | fn parsePositionalStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void { | 201 | fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error{OutOfMemory}!void { |
| 228 | if (try Object.isObject(path)) { | 202 | parseObjectStaticLib(elf_file, path) catch |err| switch (err) { |
| 229 | try parseObjectStaticLib(elf_file, path); | 203 | error.LinkFailure => return, |
| 230 | } else if (try Archive.isArchive(path)) { | 204 | error.OutOfMemory => return error.OutOfMemory, |
| 231 | try parseArchiveStaticLib(elf_file, path); | 205 | else => |e| try elf_file.addParseError(path, "parsing object failed: {s}", .{@errorName(e)}), |
| 232 | } else return error.UnknownFileType; | 206 | }; |
| 233 | // TODO: should we check for LD script? | 207 | } |
| 234 | // Actually, should we even unpack an archive? | 208 | |
| 209 | fn 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 | }; | ||
| 235 | } | 215 | } |
| 236 | 216 | ||
| 237 | fn parseObjectStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void { | 217 | fn 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 { | ... | @@ -3916,7 +3916,7 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step { |
| 3916 | // "note: while parsing /?/liba.dylib", | 3916 | // "note: while parsing /?/liba.dylib", |
| 3917 | // } }); | 3917 | // } }); |
| 3918 | expectLinkErrors(exe, test_step, .{ | 3918 | 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)", |
| 3920 | }); | 3920 | }); |
| 3921 | 3921 | ||
| 3922 | return test_step; | 3922 | return test_step; |