authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:37-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:27:17-04:00
log1b74bc22e969e708d5ac6cb34a7b9cd069b71e80
tree028e0838d2d2f6a5673f922f791170f32dbdae2e
parent537d7b74274c31ea4240870724ffd41e0e5de8dd

Rebase fixups

- Fixup error sets / use the new linker error conventions - Improve snapshot diff output

7 files changed, 262 insertions(+), 111 deletions(-)

lib/compiler/Maker/Step/Run.zig+55-3
...@@ -2126,10 +2126,16 @@ fn runCommand(...@@ -2126,10 +2126,16 @@ fn runCommand(
2126 defer gpa.free(snapshot_contents);2126 defer gpa.free(snapshot_contents);
21272127
2128 const result = switch (snapshot.result) {2128 const result = switch (snapshot.result) {
2129 .stdout => generic_result.stdout.?,
2130 .stderr => generic_result.stderr.?,2129 .stderr => generic_result.stderr.?,
2130 .stdout => generic_result.stdout.?,
2131 };2131 };
2132 if (!mem.eql(u8, snapshot_contents, result)) {2132 if (std.mem.findDiff(u8, snapshot_contents, result)) |diff_index| {
2133 var diff_line_number: usize = 1;
2134
2135 for (snapshot_contents[0..diff_index]) |value| {
2136 if (value == '\n') diff_line_number += 1;
2137 }
2138
2133 return step.fail(maker,2139 return step.fail(maker,
2134 \\2140 \\
2135 \\========= snapshot file: =========2141 \\========= snapshot file: =========
...@@ -2138,7 +2144,21 @@ fn runCommand(...@@ -2138,7 +2144,21 @@ fn runCommand(
2138 \\{s}2144 \\{s}
2139 \\========= {t} output was: ========2145 \\========= {t} output was: ========
2140 \\{s}2146 \\{s}
2141 , .{ snapshot.path, snapshot_contents, snapshot.result, result });2147 \\==================================
2148 \\first difference on line {d}:
2149 \\expected:
2150 \\{f}
2151 \\found:
2152 \\{f}
2153 , .{
2154 snapshot.path,
2155 snapshot_contents,
2156 snapshot.result,
2157 result,
2158 diff_line_number,
2159 fmtSnapshotIndicatorLine(snapshot_contents, diff_index),
2160 fmtSnapshotIndicatorLine(result, diff_index),
2161 });
2142 }2162 }
2143 }2163 }
2144 },2164 },
...@@ -2154,6 +2174,38 @@ fn runCommand(...@@ -2154,6 +2174,38 @@ fn runCommand(
2154 }2174 }
2155}2175}
21562176
2177const FmtIndicatorLine = struct {
2178 buf: []const u8,
2179 index: usize,
2180};
2181
2182fn fmtSnapshotIndicatorLine(buf: []const u8, index: usize) std.fmt.Alt(
2183 FmtIndicatorLine,
2184 snapshotIndicatorLine,
2185) {
2186 return .{ .data = .{ .buf = buf, .index = index } };
2187}
2188
2189fn snapshotIndicatorLine(line: FmtIndicatorLine, w: *std.Io.Writer) std.Io.Writer.Error!void {
2190 const line_begin_index = if (std.mem.lastIndexOfScalar(u8, line.buf[0..line.index], '\n')) |line_begin|
2191 line_begin + 1
2192 else
2193 0;
2194 const line_end_index = if (std.mem.findScalar(u8, line.buf[line.index..], '\n')) |line_end|
2195 (line.index + line_end)
2196 else
2197 line.buf.len;
2198
2199 try w.writeAll(line.buf[line_begin_index..line_end_index]);
2200 try w.writeByte('\n');
2201 try w.splatByteAll(' ', line_end_index - line_begin_index);
2202 try w.writeByte('\n');
2203 if (line.index >= line.buf.len)
2204 try w.writeAll("^ (end of file)")
2205 else
2206 try w.print("^ ('\\x{x:0>2}')\n", .{line.buf[line.index]});
2207}
2208
2157const EvalGenericResult = struct {2209const EvalGenericResult = struct {
2158 term: process.Child.Term,2210 term: process.Child.Term,
2159 stdout: ?[]const u8,2211 stdout: ?[]const u8,
lib/compiler/objdump.zig+17-16
...@@ -1653,10 +1653,10 @@ const coff = struct {...@@ -1653,10 +1653,10 @@ const coff = struct {
16531653
1654 fn dumpFlags(w: *Io.Writer, comptime fmt: []const u8, comptime T: type, flags: *const T, cols: u32) !void {1654 fn dumpFlags(w: *Io.Writer, comptime fmt: []const u8, comptime T: type, flags: *const T, cols: u32) !void {
1655 const s = @typeInfo(T).@"struct";1655 const s = @typeInfo(T).@"struct";
1656 inline for (s.fields) |flag_field| {1656 inline for (s.field_names, s.field_types) |field_name, field_type| {
1657 if (flag_field.type == bool and @field(flags, flag_field.name)) {1657 if (field_type == bool and @field(flags, field_name)) {
1658 try w.splatByteAll(' ', cols);1658 try w.splatByteAll(' ', cols);
1659 try w.print(fmt, .{flag_field.name});1659 try w.print(fmt, .{field_name});
1660 }1660 }
1661 }1661 }
1662 }1662 }
...@@ -1693,25 +1693,26 @@ const coff = struct {...@@ -1693,25 +1693,26 @@ const coff = struct {
1693 header: *const T,1693 header: *const T,
1694 Custom: type,1694 Custom: type,
1695 ) !void {1695 ) !void {
1696 inline for (@typeInfo(T).@"struct".fields) |field| {1696 const s = @typeInfo(T).@"struct";
1697 const val = &@field(header, field.name);1697 inline for (s.field_names, s.field_types) |field_name, field_type| {
1698 if (@hasDecl(Custom, field.name)) {1698 const val = &@field(header, field_name);
1699 try @field(Custom, field.name)(d, header);1699 if (@hasDecl(Custom, field_name)) {
1700 try @field(Custom, field_name)(d, header);
1700 } else {1701 } else {
1701 switch (@typeInfo(field.type)) {1702 switch (@typeInfo(field_type)) {
1702 .int => try d.w.print("{f} {s}\n", .{ fmtIntField(d, val.*, .{1703 .int => try d.w.print("{f} {s}\n", .{ fmtIntField(d, val.*, .{
1703 .kind = comptime fieldKind(field.name),1704 .kind = comptime fieldKind(field_name),
1704 .width = .{ .explicit = 16 },1705 .width = .{ .explicit = 16 },
1705 }), field.name }),1706 }), field_name }),
1706 .@"enum" => try d.w.print("{x: >16} {s} ({t})\n", .{ val.*, field.name, val.* }),1707 .@"enum" => try d.w.print("{x: >16} {s} ({t})\n", .{ val.*, field_name, val.* }),
1707 .@"struct" => |s| {1708 .@"struct" => |s_field| {
1708 switch (s.layout) {1709 switch (s_field.layout) {
1709 .auto,1710 .auto,
1710 .@"extern",1711 .@"extern",
1711 => try dumpHeader(d, field.type, val, Custom),1712 => try dumpHeader(d, field_type, val, Custom),
1712 .@"packed" => {1713 .@"packed" => {
1713 try d.w.print("{x: >16} {s}\n", .{ @as(s.backing_integer.?, @bitCast(val.*)), field.name });1714 try d.w.print("{x: >16} {s}\n", .{ @as(s_field.backing_integer.?, @bitCast(val.*)), field_name });
1714 try dumpFlags(d.w, "| {s}\n", field.type, val, 15);1715 try dumpFlags(d.w, "| {s}\n", field_type, val, 15);
1715 },1716 },
1716 }1717 }
1717 },1718 },
src/codegen/x86_64/Emit.zig+2-1
...@@ -155,6 +155,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -155,6 +155,7 @@ pub fn emitMir(emit: *Emit) Error!void {
155 @enumFromInt(try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null))155 @enumFromInt(try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null))
156 else if (emit.bin_file.cast(.elf2)) |elf| try elf.externSymbol(.{156 else if (emit.bin_file.cast(.elf2)) |elf| try elf.externSymbol(.{
157 .name = extern_func.toSlice(&emit.lower.mir).?,157 .name = extern_func.toSlice(&emit.lower.mir).?,
158 .lib_name = null,
158 .type = .FUNC,159 .type = .FUNC,
159 }) else if (emit.bin_file.cast(.macho)) |macho_file|160 }) else if (emit.bin_file.cast(.macho)) |macho_file|
160 @enumFromInt(try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null))161 @enumFromInt(try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null))
...@@ -254,7 +255,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -254,7 +255,7 @@ pub fn emitMir(emit: *Emit) Error!void {
254 else => unreachable,255 else => unreachable,
255 }256 }
256 } else if (emit.bin_file.cast(.coff2)) |_| {257 } else if (emit.bin_file.cast(.coff2)) |_| {
257 if (reloc.target.is_dll_import) switch (lowered_inst.encoding.mnemonic) {258 if (target.is_dll_import) switch (lowered_inst.encoding.mnemonic) {
258 .lea => try emit.encodeInst(try .new(.none, .mov, &.{259 .lea => try emit.encodeInst(try .new(.none, .mov, &.{
259 lowered_inst.ops[0],260 lowered_inst.ops[0],
260 .{ .mem = .initRip(.ptr, 0) },261 .{ .mem = .initRip(.ptr, 0) },
src/link/Coff.zig+167-77
...@@ -94,6 +94,13 @@ pub const imp_prefix = "__imp_";...@@ -94,6 +94,13 @@ pub const imp_prefix = "__imp_";
9494
95const header_name_max_len = @typeInfo(@FieldType(std.coff.SectionHeader, "name")).array.len;95const header_name_max_len = @typeInfo(@FieldType(std.coff.SectionHeader, "name")).array.len;
9696
97const Error = link.Error || error{MappedFileIo};
98const LoadInputError = Error ||
99 Io.File.SeekError ||
100 Io.File.Reader.SizeError ||
101 Io.Reader.Error ||
102 MappedFile.Error;
103
97/// This is the start of a Portable Executable (PE) file.104/// This is the start of a Portable Executable (PE) file.
98/// It starts with a MS-DOS header followed by a MS-DOS stub program.105/// It starts with a MS-DOS header followed by a MS-DOS stub program.
99/// This data does not change so we include it as follows in all binaries.106/// This data does not change so we include it as follows in all binaries.
...@@ -484,7 +491,7 @@ pub const Member = struct {...@@ -484,7 +491,7 @@ pub const Member = struct {
484 longnames,491 longnames,
485 _,492 _,
486493
487 const known_count = @typeInfo(Index).@"enum".fields.len;494 const known_count = @typeInfo(Index).@"enum".field_names.len;
488495
489 pub fn get(member_index: Member.Index, coff: *Coff) *Member {496 pub fn get(member_index: Member.Index, coff: *Coff) *Member {
490 return &coff.members.items[@intFromEnum(member_index)];497 return &coff.members.items[@intFromEnum(member_index)];
...@@ -2855,7 +2862,7 @@ pub fn getNavVAddr(...@@ -2855,7 +2862,7 @@ pub fn getNavVAddr(
2855 pt: Zcu.PerThread,2862 pt: Zcu.PerThread,
2856 nav: InternPool.Nav.Index,2863 nav: InternPool.Nav.Index,
2857 reloc_info: link.File.RelocInfo,2864 reloc_info: link.File.RelocInfo,
2858) !u64 {2865) link.Error!u64 {
2859 return coff.getVAddr(reloc_info, try coff.navSymbol(pt.zcu, nav));2866 return coff.getVAddr(reloc_info, try coff.navSymbol(pt.zcu, nav));
2860}2867}
28612868
...@@ -2863,11 +2870,11 @@ pub fn getUavVAddr(...@@ -2863,11 +2870,11 @@ pub fn getUavVAddr(
2863 coff: *Coff,2870 coff: *Coff,
2864 uav: InternPool.Index,2871 uav: InternPool.Index,
2865 reloc_info: link.File.RelocInfo,2872 reloc_info: link.File.RelocInfo,
2866) !u64 {2873) link.Error!u64 {
2867 return coff.getVAddr(reloc_info, try coff.uavSymbol(uav));2874 return coff.getVAddr(reloc_info, try coff.uavSymbol(uav));
2868}2875}
28692876
2870pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) !u64 {2877pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) link.Error!u64 {
2871 try coff.addReloc(2878 try coff.addReloc(
2872 @enumFromInt(@intFromEnum(reloc_info.parent.atom_index)),2879 @enumFromInt(@intFromEnum(reloc_info.parent.atom_index)),
2873 reloc_info.offset,2880 reloc_info.offset,
...@@ -3524,19 +3531,13 @@ fn objectSectionMapIndex(...@@ -3524,19 +3531,13 @@ fn objectSectionMapIndex(
3524 const parent_alignment = parent_ni.alignment(&coff.mf);3531 const parent_alignment = parent_ni.alignment(&coff.mf);
3525 if (alignment.compare(.gt, parent_alignment)) {3532 if (alignment.compare(.gt, parent_alignment)) {
3526 log.debug("realignParent({s}, {d}) {d}->{d}", .{ name.toSlice(coff), parent_ni, parent_alignment, alignment });3533 log.debug("realignParent({s}, {d}) {d}->{d}", .{ name.toSlice(coff), parent_ni, parent_alignment, alignment });
3527 parent_ni.realign(&coff.mf, gpa, alignment, true) catch |err| switch (err) {3534 try parent_ni.realign(&coff.mf, gpa, alignment, true);
3528 error.Unimplemented => unreachable,
3529 else => |e| return e,
3530 };
3531 }3535 }
35323536
3533 const old_alignment = sym.ni.alignment(&coff.mf);3537 const old_alignment = sym.ni.alignment(&coff.mf);
3534 if (alignment.compare(.gt, old_alignment)) {3538 if (alignment.compare(.gt, old_alignment)) {
3535 log.debug("realignObject({s}) {d}->{d}", .{ name.toSlice(coff), old_alignment, alignment });3539 log.debug("realignObject({s}) {d}->{d}", .{ name.toSlice(coff), old_alignment, alignment });
3536 sym.ni.realign(&coff.mf, gpa, alignment, true) catch |err| switch (err) {3540 try sym.ni.realign(&coff.mf, gpa, alignment, true);
3537 error.Unimplemented => unreachable,
3538 else => |e| return e,
3539 };
3540 }3541 }
35413542
3542 try coff.verifyParentSectionAttributes(3543 try coff.verifyParentSectionAttributes(
...@@ -3561,7 +3562,6 @@ fn verifyParentSectionAttributes(...@@ -3561,7 +3562,6 @@ fn verifyParentSectionAttributes(
3561) !void {3562) !void {
3562 if (parent_attrs == child_attrs) return;3563 if (parent_attrs == child_attrs) return;
35633564
3564 const fields = std.meta.fields(ObjectSectionAttributes);
3565 const BackingT = @typeInfo(ObjectSectionAttributes).@"struct".backing_integer.?;3565 const BackingT = @typeInfo(ObjectSectionAttributes).@"struct".backing_integer.?;
3566 const num_notes = @popCount(@as(BackingT, @bitCast(parent_attrs)) ^ @as(BackingT, @bitCast(child_attrs)));3566 const num_notes = @popCount(@as(BackingT, @bitCast(parent_attrs)) ^ @as(BackingT, @bitCast(child_attrs)));
3567 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);3567 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);
...@@ -3571,30 +3571,67 @@ fn verifyParentSectionAttributes(...@@ -3571,30 +3571,67 @@ fn verifyParentSectionAttributes(
3571 parent_name.toSlice(coff),3571 parent_name.toSlice(coff),
3572 });3572 });
35733573
3574 inline for (fields) |field| {3574 inline for (comptime std.meta.fieldNames(ObjectSectionAttributes)) |field| {
3575 if (@field(child_attrs, field.name) != @field(parent_attrs, field.name)) {3575 if (@field(child_attrs, field) != @field(parent_attrs, field)) {
3576 err.addNote("flags.{s} was {d} in {s}, but {d} in {s}", .{3576 err.addNote("flags.{s} was {d} in {s}, but {d} in {s}", .{
3577 field.name,3577 field,
3578 @intFromBool(@field(child_attrs, field.name)),3578 @intFromBool(@field(child_attrs, field)),
3579 child_name.toSlice(coff),3579 child_name.toSlice(coff),
3580 @intFromBool(@field(parent_attrs, field.name)),3580 @intFromBool(@field(parent_attrs, field)),
3581 parent_name.toSlice(coff),3581 parent_name.toSlice(coff),
3582 });3582 });
3583 }3583 }
3584 }3584 }
35853585
3586 return error.LinkFailure;3586 return error.AlreadyReported;
3587}3587}
35883588
3589const RelocAddend = union(enum) {
3590 known: i64,
3591 /// Relocs tables in input objects don't include the addend.
3592 /// The value needs to be recovered from the reloc location.
3593 pending: void,
3594};
3595
3589pub fn addReloc(3596pub fn addReloc(
3590 coff: *Coff,3597 coff: *Coff,
3591 loc_si: Symbol.Index,3598 loc_si: Symbol.Index,
3592 offset: u64,3599 offset: u64,
3593 target_si: Symbol.Index,3600 target_si: Symbol.Index,
3594 addend: union(enum) {3601 addend: RelocAddend,
3595 known: i64,3602 @"type": Reloc.Type,
3596 pending: void,3603) link.Error!void {
3597 },3604 const diags = &coff.base.comp.link_diags;
3605 try coff.ensureUnusedRelocCapacity(loc_si, 1);
3606 coff.addRelocAssumeCapacity(loc_si, offset, target_si, addend, @"type") catch |err| switch (err) {
3607 error.MappedFileIo => return diags.fail(
3608 "failed to write output file: {t}",
3609 .{coff.mf.io_err.?},
3610 ),
3611 else => |e| return e,
3612 };
3613}
3614
3615fn ensureUnusedRelocCapacity(coff: *Coff, loc_si: Symbol.Index, len: usize) !void {
3616 const gpa = coff.base.comp.gpa;
3617 try coff.relocs.ensureUnusedCapacity(gpa, len);
3618 if (isImage(coff)) return;
3619 switch (loc_si.get(coff).section_number) {
3620 .UNDEFINED, .ABSOLUTE, .DEBUG => {},
3621 else => |loc_sn| {
3622 const section = loc_sn.section(coff);
3623 if (section.relocation_table_ni == .none)
3624 try coff.nodes.ensureUnusedCapacity(gpa, 1);
3625 },
3626 }
3627}
3628
3629fn addRelocAssumeCapacity(
3630 coff: *Coff,
3631 loc_si: Symbol.Index,
3632 offset: u64,
3633 target_si: Symbol.Index,
3634 addend: RelocAddend,
3598 @"type": Reloc.Type,3635 @"type": Reloc.Type,
3599) !void {3636) !void {
3600 const gpa = coff.base.comp.gpa;3637 const gpa = coff.base.comp.gpa;
...@@ -3612,8 +3649,6 @@ pub fn addReloc(...@@ -3612,8 +3649,6 @@ pub fn addReloc(
3612 ri,3649 ri,
3613 });3650 });
36143651
3615 try coff.relocs.ensureUnusedCapacity(gpa, 1);
3616
3617 const sri: Section.RelocationIndex = if (isImage(coff))3652 const sri: Section.RelocationIndex = if (isImage(coff))
3618 .none3653 .none
3619 else switch (loc_si.get(coff).section_number) {3654 else switch (loc_si.get(coff).section_number) {
...@@ -3638,13 +3673,16 @@ pub fn addReloc(...@@ -3638,13 +3673,16 @@ pub fn addReloc(
3638 const new_num_relocations = old_num_relocations + 1;3673 const new_num_relocations = old_num_relocations + 1;
3639 const new_size = new_num_relocations * std.coff.Relocation.sizeOf();3674 const new_size = new_num_relocations * std.coff.Relocation.sizeOf();
3640 if (section.relocation_table_ni == .none) {3675 if (section.relocation_table_ni == .none) {
3641 try coff.nodes.ensureUnusedCapacity(gpa, 1);3676 section.relocation_table_ni = try coff.mf.addLastChildNode(
3642 section.relocation_table_ni = try coff.mf.addLastChildNode(gpa, coff.sectionParent(), .{3677 gpa,
3643 .size = new_size,3678 coff.sectionParent(),
3644 .alignment = .@"2",3679 .{
3645 .moved = true,3680 .size = new_size,
3646 .resized = true,3681 .alignment = .@"2",
3647 });3682 .moved = true,
3683 .resized = true,
3684 },
3685 );
3648 coff.nodes.appendAssumeCapacity(.{ .relocation_table = loc_sn });3686 coff.nodes.appendAssumeCapacity(.{ .relocation_table = loc_sn });
3649 } else {3687 } else {
3650 try section.relocation_table_ni.resize(&coff.mf, gpa, new_size);3688 try section.relocation_table_ni.resize(&coff.mf, gpa, new_size);
...@@ -3687,8 +3725,60 @@ pub fn addReloc(...@@ -3687,8 +3725,60 @@ pub fn addReloc(
3687 target.target_relocs = ri;3725 target.target_relocs = ri;
3688}3726}
36893727
3690pub fn loadInput(coff: *Coff, input: link.Input) (Io.File.Reader.SizeError ||3728// pub fn loadInput(coff: *Coff, input: link.Input) link.Error!void {
3691 Io.File.Reader.Error || MappedFile.Error || error{ WriteFailed, EndOfStream, BadMagic, LinkFailure })!void {3729// const diags = &coff.base.comp.link_diags;
3730// return coff.loadInputInner(input) catch |err| switch (err) {
3731// else => |e| return e,
3732// error.MappedFileIo => return diags.fail(
3733// "failed to write output file: {t}",
3734// .{coff.mf.io_err.?},
3735// ),
3736// };
3737// }
3738
3739fn failLoadInput(
3740 coff: *Coff,
3741 err: LoadInputError,
3742 fr: *Io.File.Reader,
3743 path: std.Build.Cache.Path,
3744) link.Error {
3745 const diags = &coff.base.comp.link_diags;
3746 switch (err) {
3747 else => |e| return e,
3748 error.MappedFileIo => return diags.fail(
3749 "failed to write output file: {t}",
3750 .{coff.mf.io_err.?},
3751 ),
3752 error.EndOfStream => return diags.failParse(
3753 path,
3754 "unexpected eof",
3755 .{},
3756 ),
3757 error.AccessDenied,
3758 error.Unexpected,
3759 error.Unseekable,
3760 => |e| return diags.fail(
3761 "failed to read \"{f}\": {t}",
3762 .{ path.fmtEscapeString(), e },
3763 ),
3764 error.PermissionDenied,
3765 error.SystemResources,
3766 error.Streaming,
3767 => |e| return diags.fail(
3768 "failed to stat \"{f}\": {t}",
3769 .{ path.fmtEscapeString(), e },
3770 ),
3771 error.ReadFailed => switch (fr.err.?) {
3772 error.Canceled => |e| return e,
3773 else => |e| return diags.fail(
3774 "failed to read \"{f}\": {t}",
3775 .{ path.fmtEscapeString(), e },
3776 ),
3777 },
3778 }
3779}
3780
3781pub fn loadInput(coff: *Coff, input: link.Input) link.Error!void {
3692 const comp = coff.base.comp;3782 const comp = coff.base.comp;
3693 const io = comp.io;3783 const io = comp.io;
36943784
...@@ -3703,32 +3793,24 @@ pub fn loadInput(coff: *Coff, input: link.Input) (Io.File.Reader.SizeError ||...@@ -3703,32 +3793,24 @@ pub fn loadInput(coff: *Coff, input: link.Input) (Io.File.Reader.SizeError ||
3703 var fr = object.file.reader(io, &buf);3793 var fr = object.file.reader(io, &buf);
3704 coff.loadObject(object.path, null, &fr, .{3794 coff.loadObject(object.path, null, &fr, .{
3705 .offset = fr.logicalPos(),3795 .offset = fr.logicalPos(),
3706 .size = try fr.getSize(),3796 .size = fr.getSize() catch |err|
3707 }) catch |err| switch (err) {3797 return coff.failLoadInput(err, &fr, object.path),
3708 error.ReadFailed => return fr.err.?,3798 }) catch |err| return coff.failLoadInput(err, &fr, object.path);
3709 else => |e| return e,
3710 };
3711 },3799 },
3712 .archive => |archive| {3800 .archive => |archive| {
3713 var fr = archive.file.reader(io, &buf);3801 var fr = archive.file.reader(io, &buf);
3714 coff.loadArchive(archive.path, &fr) catch |err| switch (err) {3802 coff.loadArchive(archive.path, &fr) catch |err|
3715 error.ReadFailed => return fr.err.?,3803 return coff.failLoadInput(err, &fr, archive.path);
3716 else => |e| return e,
3717 };
3718 },3804 },
3719 .res => |res| {3805 .res => |res| {
3720 var fr = res.file.reader(io, &buf);3806 var fr = res.file.reader(io, &buf);
3721 coff.loadRes(res.path, &fr) catch |err| switch (err) {3807 coff.loadRes(res.path, &fr) catch |err|
3722 error.ReadFailed => return fr.err.?,3808 return coff.failLoadInput(err, &fr, res.path);
3723 else => |e| return e,
3724 };
3725 },3809 },
3726 .dso => |dso| {3810 .dso => |dso| {
3727 var fr = dso.file.reader(io, &buf);3811 var fr = dso.file.reader(io, &buf);
3728 coff.loadDll(dso.path, &fr) catch |err| switch (err) {3812 coff.loadDll(dso.path, &fr) catch |err|
3729 error.ReadFailed => return fr.err.?,3813 return coff.failLoadInput(err, &fr, dso.path);
3730 else => |e| return e,
3731 };
3732 },3814 },
3733 .dso_exact => unreachable,3815 .dso_exact => unreachable,
3734 }3816 }
...@@ -3771,7 +3853,7 @@ fn loadObject(...@@ -3771,7 +3853,7 @@ fn loadObject(
3771 member_name: ?[]const u8,3853 member_name: ?[]const u8,
3772 fr: *Io.File.Reader,3854 fr: *Io.File.Reader,
3773 fl: MappedFile.Node.FileLocation,3855 fl: MappedFile.Node.FileLocation,
3774) !void {3856) LoadInputError!void {
3775 const comp = coff.base.comp;3857 const comp = coff.base.comp;
3776 const gpa = comp.gpa;3858 const gpa = comp.gpa;
3777 const diags = &comp.link_diags;3859 const diags = &comp.link_diags;
...@@ -3955,14 +4037,18 @@ fn loadObject(...@@ -3955,14 +4037,18 @@ fn loadObject(
3955 try member.initHeader(coff, path_str, header.time_date_stamp);4037 try member.initHeader(coff, path_str, header.time_date_stamp);
39564038
3957 {4039 {
3958 // TODO: This should be deferred to an idle task4040 // TODO: This should be deferred to an idle task (but resize it here!)
3959 var nw: MappedFile.Node.Writer = undefined;4041 var nw: MappedFile.Node.Writer = undefined;
3960 member.content_ni.writer(&coff.mf, gpa, &nw);4042 member.content_ni.writer(&coff.mf, gpa, &nw);
3961 defer nw.deinit();4043 defer nw.deinit();
39624044
3963 try fr.seekTo(fl.offset);4045 try fr.seekTo(fl.offset);
3964 if (try nw.interface.sendFileAll(fr, .limited64(fl.size)) != fl.size)4046 const written = nw.interface.sendFileAll(fr, .limited64(fl.size)) catch |err| switch (err) {
3965 return error.EndOfStream;4047 error.WriteFailed => return nw.err.?,
4048 else => |e| return e,
4049 };
4050
4051 if (written != fl.size) return error.EndOfStream;
3966 }4052 }
39674053
3968 break :mi mi;4054 break :mi mi;
...@@ -4816,7 +4902,7 @@ fn failMultipleDefinitions(...@@ -4816,7 +4902,7 @@ fn failMultipleDefinitions(
4816 size: struct { a: u64, b: u64 },4902 size: struct { a: u64, b: u64 },
4817 crc: struct { a: u32, b: u32 },4903 crc: struct { a: u32, b: u32 },
4818 },4904 },
4819) error{ LinkFailure, OutOfMemory } {4905) error{ AlreadyReported, OutOfMemory } {
4820 const num_notes: usize = 2 + @as(usize, @intFromBool(comdat_reason != .none));4906 const num_notes: usize = 2 + @as(usize, @intFromBool(comdat_reason != .none));
4821 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);4907 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);
4822 try err.addMsg("multiple definitions of '{s}'", .{name.toSlice(coff)});4908 try err.addMsg("multiple definitions of '{s}'", .{name.toSlice(coff)});
...@@ -4849,7 +4935,7 @@ fn failMultipleDefinitions(...@@ -4849,7 +4935,7 @@ fn failMultipleDefinitions(
4849 ),4935 ),
4850 }4936 }
48514937
4852 return error.LinkFailure;4938 return error.AlreadyReported;
4853}4939}
48544940
4855const ArchiveMemberHeader = struct {4941const ArchiveMemberHeader = struct {
...@@ -4889,7 +4975,7 @@ fn parseArchiveMemberHeaderInner(...@@ -4889,7 +4975,7 @@ fn parseArchiveMemberHeaderInner(
4889 };4975 };
4890}4976}
48914977
4892fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {4978fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) LoadInputError!void {
4893 const comp = coff.base.comp;4979 const comp = coff.base.comp;
4894 const gpa = comp.gpa;4980 const gpa = comp.gpa;
4895 const diags = &comp.link_diags;4981 const diags = &comp.link_diags;
...@@ -5164,7 +5250,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo...@@ -5164,7 +5250,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
5164 }5250 }
5165}5251}
51665252
5167fn loadRes(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {5253fn loadRes(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) LoadInputError!void {
5168 const comp = coff.base.comp;5254 const comp = coff.base.comp;
5169 const gpa = comp.gpa;5255 const gpa = comp.gpa;
5170 const diags = &comp.link_diags;5256 const diags = &comp.link_diags;
...@@ -5177,7 +5263,7 @@ fn loadRes(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {...@@ -5177,7 +5263,7 @@ fn loadRes(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
5177 _ = r;5263 _ = r;
5178}5264}
51795265
5180fn loadDll(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {5266fn loadDll(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) LoadInputError!void {
5181 const comp = coff.base.comp;5267 const comp = coff.base.comp;
5182 const gpa = comp.gpa;5268 const gpa = comp.gpa;
5183 const diags = &comp.link_diags;5269 const diags = &comp.link_diags;
...@@ -5241,7 +5327,6 @@ pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {...@@ -5241,7 +5327,6 @@ pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {
5241 errdefer archive.file.close(comp.io);5327 errdefer archive.file.close(comp.io);
52425328
5243 coff.loadInput(.{ .archive = archive }) catch |err| switch (err) {5329 coff.loadInput(.{ .archive = archive }) catch |err| switch (err) {
5244 error.LinkFailure => return,
5245 else => |e| return comp.link_diags.failParse(5330 else => |e| return comp.link_diags.failParse(
5246 lib.ioi.path(coff),5331 lib.ioi.path(coff),
5247 "error loading /DEFAULTLIB library '{s}': {t}",5332 "error loading /DEFAULTLIB library '{s}': {t}",
...@@ -5265,10 +5350,14 @@ pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {...@@ -5265,10 +5350,14 @@ pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {
5265 coff.exports_complete = true;5350 coff.exports_complete = true;
5266}5351}
52675352
5268pub fn updateNav(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {5353pub fn updateNav(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {
5269 coff.updateNavInner(pt, nav_index) catch |err| switch (err) {5354 coff.updateNavInner(pt, nav_index) catch |err| switch (err) {
5355 error.MappedFileIo => return coff.base.cgFail(
5356 nav_index,
5357 "linker failed to update variable: {t}",
5358 .{coff.mf.io_err.?},
5359 ),
5270 else => |e| return e,5360 else => |e| return e,
5271 error.MappedFileIo => return coff.base.cgFail(nav_index, "linker failed to update variable: {t}", .{coff.mf.io_err.?}),
5272 };5361 };
5273}5362}
5274fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {5363fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
...@@ -5351,7 +5440,7 @@ pub fn lowerUav(...@@ -5351,7 +5440,7 @@ pub fn lowerUav(
5351 pt: Zcu.PerThread,5440 pt: Zcu.PerThread,
5352 uav_val: InternPool.Index,5441 uav_val: InternPool.Index,
5353 uav_align: InternPool.Alignment,5442 uav_align: InternPool.Alignment,
5354) !link.File.SymbolId {5443) link.Error!link.File.SymbolId {
5355 const zcu = pt.zcu;5444 const zcu = pt.zcu;
5356 const gpa = zcu.gpa;5445 const gpa = zcu.gpa;
53575446
...@@ -5380,7 +5469,7 @@ pub fn updateFunc(...@@ -5380,7 +5469,7 @@ pub fn updateFunc(
5380 pt: Zcu.PerThread,5469 pt: Zcu.PerThread,
5381 func_index: InternPool.Index,5470 func_index: InternPool.Index,
5382 mir: *const codegen.AnyMir,5471 mir: *const codegen.AnyMir,
5383) !void {5472) link.Error!void {
5384 coff.updateFuncInner(pt, func_index, mir) catch |err| switch (err) {5473 coff.updateFuncInner(pt, func_index, mir) catch |err| switch (err) {
5385 else => |e| return e,5474 else => |e| return e,
5386 error.MappedFileIo => return coff.base.cgFail(5475 error.MappedFileIo => return coff.base.cgFail(
...@@ -5692,7 +5781,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {...@@ -5692,7 +5781,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
5692 }5781 }
5693 }5782 }
56945783
5695 return error.LinkFailure;5784 return error.AlreadyReported;
5696}5785}
56975786
5698pub fn flush(5787pub fn flush(
...@@ -5700,7 +5789,7 @@ pub fn flush(...@@ -5700,7 +5789,7 @@ pub fn flush(
5700 arena: std.mem.Allocator,5789 arena: std.mem.Allocator,
5701 tid: Zcu.PerThread.Id,5790 tid: Zcu.PerThread.Id,
5702 prog_node: std.Progress.Node,5791 prog_node: std.Progress.Node,
5703) !void {5792) link.Error!void {
5704 _ = arena;5793 _ = arena;
5705 _ = prog_node;5794 _ = prog_node;
5706 const comp = coff.base.comp;5795 const comp = coff.base.comp;
...@@ -5723,13 +5812,10 @@ pub fn flush(...@@ -5723,13 +5812,10 @@ pub fn flush(
5723 comp.gpa,5812 comp.gpa,
5724 number_of_symbols * std.coff.Symbol.sizeOf(),5813 number_of_symbols * std.coff.Symbol.sizeOf(),
5725 true,5814 true,
5726 ) catch |err| switch (err) {5815 ) catch |err| return comp.link_diags.fail(
5727 error.OutOfMemory => return error.OutOfMemory,5816 "linker failed to compact symbol table: {t}",
5728 else => |e| return comp.link_diags.fail(5817 .{err},
5729 "linker failed to compact symbol table: {t}",5818 );
5730 .{e},
5731 ),
5732 };
5733 }5819 }
5734 while (try coff.idle(tid)) {}5820 while (try coff.idle(tid)) {}
57355821
...@@ -7220,10 +7306,14 @@ pub fn updateExports(...@@ -7220,10 +7306,14 @@ pub fn updateExports(
7220 pt: Zcu.PerThread,7306 pt: Zcu.PerThread,
7221 exported: Zcu.Exported,7307 exported: Zcu.Exported,
7222 export_indices: []const Zcu.Export.Index,7308 export_indices: []const Zcu.Export.Index,
7223) !void {7309) link.Error!void {
7310 const diags = &coff.base.comp.link_diags;
7224 return coff.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {7311 return coff.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {
7225 error.OutOfMemory => error.OutOfMemory,7312 error.MappedFileIo => return diags.fail(
7226 else => |e| coff.base.comp.link_diags.fail("updateExports failed {t}", .{e}) catch error.AnalysisFail,7313 "failed to write output file: {t}",
7314 .{coff.mf.io_err.?},
7315 ),
7316 else => |e| return e,
7227 };7317 };
7228}7318}
7229fn updateExportsInner(7319fn updateExportsInner(
src/link/Elf2.zig+4-4
...@@ -3787,7 +3787,7 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -3787,7 +3787,7 @@ fn mapInputSection(elf: *Elf, opts: struct {
3787 const new_alignment: std.mem.Alignment = .fromByteUnits(3787 const new_alignment: std.mem.Alignment = .fromByteUnits(
3788 std.math.ceilPowerOfTwoAssert(usize, @intCast(opts.addralign)),3788 std.math.ceilPowerOfTwoAssert(usize, @intCast(opts.addralign)),
3789 );3789 );
3790 try existing_shndx.get(elf).ni.realign(&elf.mf, gpa, new_alignment);3790 try existing_shndx.get(elf).ni.realign(&elf.mf, gpa, new_alignment, true);
3791 }3791 }
3792 // ...and update the shdr as needed.3792 // ...and update the shdr as needed.
3793 switch (elf.shdrPtr(existing_shndx)) {3793 switch (elf.shdrPtr(existing_shndx)) {
...@@ -3950,7 +3950,7 @@ fn uavMapIndex(...@@ -3950,7 +3950,7 @@ fn uavMapIndex(
3950 } else {3950 } else {
3951 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;3951 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;
3952 if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {3952 if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {
3953 try node.realign(&elf.mf, gpa, resolved_align.toStdMem());3953 try node.realign(&elf.mf, gpa, resolved_align.toStdMem(), true);
3954 }3954 }
3955 }3955 }
3956 return umi;3956 return umi;
...@@ -4679,7 +4679,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars...@@ -4679,7 +4679,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
4679 // We have a copy relocation for this global, but the amount of space we4679 // We have a copy relocation for this global, but the amount of space we
4680 // reserved for it could be too small or underaligned!4680 // reserved for it could be too small or underaligned!
4681 try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size);4681 try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size);
4682 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment);4682 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment, true);
4683 const global_ptr = elf.globalByName(name).?;4683 const global_ptr = elf.globalByName(name).?;
4684 switch (elf.symPtr(global_ptr.symtab_index)) {4684 switch (elf.symPtr(global_ptr.symtab_index)) {
4685 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),4685 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),
...@@ -6762,7 +6762,7 @@ pub fn printNode(...@@ -6762,7 +6762,7 @@ pub fn printNode(
6762 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),6762 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),
6763 });6763 });
6764 },6764 },
6765 .copied_global => |name| try w.print("(copy:{s})", .{name}),6765 .copied_global => |name| try w.print("(copy:{s})", .{name.slice(elf)}),
6766 .nav => |nmi| {6766 .nav => |nmi| {
6767 const zcu = elf.base.comp.zcu.?;6767 const zcu = elf.base.comp.zcu.?;
6768 const ip = &zcu.intern_pool;6768 const ip = &zcu.intern_pool;
src/link/MappedFile.zig+16-9
...@@ -385,13 +385,14 @@ pub const Node = extern struct {...@@ -385,13 +385,14 @@ pub const Node = extern struct {
385 /// If the new size can't contain all the children, returns error.ShrinkImpossible.385 /// If the new size can't contain all the children, returns error.ShrinkImpossible.
386 /// If `shift_next` is set, then the following node is shifted backwards into386 /// If `shift_next` is set, then the following node is shifted backwards into
387 /// the free space as much as alignment allows.387 /// the free space as much as alignment allows.
388 /// Asserts that `size` is >= the end of the last child node.
388 pub fn shrink(389 pub fn shrink(
389 ni: Node.Index,390 ni: Node.Index,
390 mf: *MappedFile,391 mf: *MappedFile,
391 gpa: std.mem.Allocator,392 gpa: std.mem.Allocator,
392 size: u64,393 size: u64,
393 shift_next: bool,394 shift_next: bool,
394 ) !void {395 ) Error!void {
395 try mf.shrinkNode(gpa, ni, size, shift_next);396 try mf.shrinkNode(gpa, ni, size, shift_next);
396 var writers_it = mf.writers.first;397 var writers_it = mf.writers.first;
397 while (writers_it) |writer_node| : (writers_it = writer_node.next) {398 while (writers_it) |writer_node| : (writers_it = writer_node.next) {
...@@ -572,10 +573,7 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {...@@ -572,10 +573,7 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
572 else => |next_ni| {573 else => |next_ni| {
573 const next_offset, _ = next_ni.location(mf).resolve(mf);574 const next_offset, _ = next_ni.location(mf).resolve(mf);
574 if (new_end > next_offset)575 if (new_end > next_offset)
575 mf.realignNode(gpa, next_ni, opts.add_node.alignment, false, false) catch |err| switch (err) {576 try next_ni.realign(mf, gpa, opts.add_node.alignment, false);
576 error.Unimplemented => unreachable,
577 else => |e| return e,
578 };
579 },577 },
580 }578 }
581 }579 }
...@@ -724,13 +722,13 @@ fn shrinkNode(...@@ -724,13 +722,13 @@ fn shrinkNode(
724 const old_offset, _ = node.location().resolve(mf);722 const old_offset, _ = node.location().resolve(mf);
725723
726 // This would require unmapping first724 // This would require unmapping first
727 if (ni == Node.Index.root) return error.Unimplemented;725 assert(ni != Node.Index.root);
728 defer if (std.debug.runtime_safety) mf.verify();726 defer if (std.debug.runtime_safety) mf.verify();
729727
730 if (node.last != .none) {728 if (node.last != .none) {
731 const last = node.last.get(mf);729 const last = node.last.get(mf);
732 const last_offset, const last_size = last.location().resolve(mf);730 const last_offset, const last_size = last.location().resolve(mf);
733 if (last_offset + last_size > size) return error.ShrinkImpossible;731 assert(last_offset + last_size > size);
734 }732 }
735733
736 try mf.large.ensureUnusedCapacity(gpa, 4);734 try mf.large.ensureUnusedCapacity(gpa, 4);
...@@ -757,7 +755,12 @@ fn shrinkNode(...@@ -757,7 +755,12 @@ fn shrinkNode(
757 node.next.setLocationAssumeCapacity(mf, new_next_offset, next_size);755 node.next.setLocationAssumeCapacity(mf, new_next_offset, next_size);
758}756}
759757
760fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) (Allocator.Error || Io.Cancelable || IoError)!void {758fn resizeNode(
759 mf: *MappedFile,
760 gpa: std.mem.Allocator,
761 ni: Node.Index,
762 requested_size: u64,
763) (Allocator.Error || Io.Cancelable || IoError)!void {
761 mf.nodes_lock.assertUnlocked();764 mf.nodes_lock.assertUnlocked();
762 const io = mf.io;765 const io = mf.io;
763 const node = ni.get(mf);766 const node = ni.get(mf);
...@@ -1271,7 +1274,11 @@ fn ensureTotalCapacityPreciseInner(mf: *MappedFile, new_capacity: usize) (Alloca...@@ -1271,7 +1274,11 @@ fn ensureTotalCapacityPreciseInner(mf: *MappedFile, new_capacity: usize) (Alloca
1271 else => |e| return e,1274 else => |e| return e,
1272 }1275 }
12731276
1274 try mf.memory_map.write(io);1277 mf.memory_map.write(io) catch |err| switch (err) {
1278 error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking
1279 error.NotOpenForWriting => return error.Unexpected, // we definitely opened the file for writing
1280 else => |e| return e,
1281 };
1275 unmap(mf);1282 unmap(mf);
1276 }1283 }
12771284
test/src/Link.zig+1-1
...@@ -151,7 +151,7 @@ pub const Case = struct {...@@ -151,7 +151,7 @@ pub const Case = struct {
151 const snapshot_update_path = run_step.captureStdOut(.{});151 const snapshot_update_path = run_step.captureStdOut(.{});
152 update_step.addCopyFileToSource(snapshot_update_path, snapshot_sub_path);152 update_step.addCopyFileToSource(snapshot_update_path, snapshot_sub_path);
153 } else {153 } else {
154 run_step.addCheck(.{ .snapshot = .{ .file = ctx.b.path(snapshot_sub_path) } });154 run_step.addCheck(.{ .expect_stdout_snapshot = ctx.b.path(snapshot_sub_path) });
155 }155 }
156156
157 ctx.step.dependOn(&run_step.step);157 ctx.step.dependOn(&run_step.step);