authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-22 18:40:11+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-07-22 18:40:11+02:00
logf34b4780b7bd52d14df253d0762d9c73db8eb226
tree87b5fc511384829f0f504ef8c649cf9ae5bf1556
parentc41ac8f19ec96ed854d9ac31a2015006ba3e4657
parent34d2778239b7eea854385354fd956358ae7cf5a0
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #24521 from ziglang/fs-streaming

std.fs.File: delete writeFileAll and friends

19 files changed, 722 insertions(+), 1945 deletions(-)

lib/compiler/build_runner.zig+1-1
...@@ -708,7 +708,7 @@ fn runStepNames(...@@ -708,7 +708,7 @@ fn runStepNames(
708708
709 const total_count = success_count + failure_count + pending_count + skipped_count;709 const total_count = success_count + failure_count + pending_count + skipped_count;
710 ttyconf.setColor(w, .cyan) catch {};710 ttyconf.setColor(w, .cyan) catch {};
711 w.writeAll("Build Summary:") catch {};711 w.writeAll("\nBuild Summary:") catch {};
712 ttyconf.setColor(w, .reset) catch {};712 ttyconf.setColor(w, .reset) catch {};
713 w.print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {};713 w.print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
714 if (skipped_count > 0) w.print("; {d} skipped", .{skipped_count}) catch {};714 if (skipped_count > 0) w.print("; {d} skipped", .{skipped_count}) catch {};
lib/compiler/objcopy.zig+62-907
...@@ -13,6 +13,9 @@ const Server = std.zig.Server;...@@ -13,6 +13,9 @@ const Server = std.zig.Server;
13var stdin_buffer: [1024]u8 = undefined;13var stdin_buffer: [1024]u8 = undefined;
14var stdout_buffer: [1024]u8 = undefined;14var stdout_buffer: [1024]u8 = undefined;
1515
16var input_buffer: [1024]u8 = undefined;
17var output_buffer: [1024]u8 = undefined;
18
16pub fn main() !void {19pub fn main() !void {
17 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);20 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
18 defer arena_instance.deinit();21 defer arena_instance.deinit();
...@@ -145,13 +148,16 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -145,13 +148,16 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
145 const input = opt_input orelse fatal("expected input parameter", .{});148 const input = opt_input orelse fatal("expected input parameter", .{});
146 const output = opt_output orelse fatal("expected output parameter", .{});149 const output = opt_output orelse fatal("expected output parameter", .{});
147150
148 var in_file = fs.cwd().openFile(input, .{}) catch |err|151 const input_file = fs.cwd().openFile(input, .{}) catch |err| fatal("failed to open {s}: {t}", .{ input, err });
149 fatal("unable to open '{s}': {s}", .{ input, @errorName(err) });152 defer input_file.close();
150 defer in_file.close();153
154 const stat = input_file.stat() catch |err| fatal("failed to stat {s}: {t}", .{ input, err });
151155
152 const elf_hdr = std.elf.Header.read(in_file) catch |err| switch (err) {156 var in: File.Reader = .initSize(input_file, &input_buffer, stat.size);
153 error.InvalidElfMagic => fatal("not an ELF file: '{s}'", .{input}),157
154 else => fatal("unable to read '{s}': {s}", .{ input, @errorName(err) }),158 const elf_hdr = std.elf.Header.read(&in.interface) catch |err| switch (err) {
159 error.ReadFailed => fatal("unable to read {s}: {t}", .{ input, in.err.? }),
160 else => |e| fatal("invalid elf file: {t}", .{e}),
155 };161 };
156162
157 const in_ofmt = .elf;163 const in_ofmt = .elf;
...@@ -168,16 +174,12 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -168,16 +174,12 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
168 }174 }
169 };175 };
170176
171 const mode = mode: {177 const mode = if (out_fmt != .elf or only_keep_debug) fs.File.default_mode else stat.mode;
172 if (out_fmt != .elf or only_keep_debug)178
173 break :mode fs.File.default_mode;179 var output_file = try fs.cwd().createFile(output, .{ .mode = mode });
174 if (in_file.stat()) |stat|180 defer output_file.close();
175 break :mode stat.mode181
176 else |_|182 var out = output_file.writer(&output_buffer);
177 break :mode fs.File.default_mode;
178 };
179 var out_file = try fs.cwd().createFile(output, .{ .mode = mode });
180 defer out_file.close();
181183
182 switch (out_fmt) {184 switch (out_fmt) {
183 .hex, .raw => {185 .hex, .raw => {
...@@ -192,7 +194,7 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -192,7 +194,7 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
192 if (set_section_flags != null)194 if (set_section_flags != null)
193 fatal("zig objcopy: ELF to RAW or HEX copying does not support --set_section_flags", .{});195 fatal("zig objcopy: ELF to RAW or HEX copying does not support --set_section_flags", .{});
194196
195 try emitElf(arena, in_file, out_file, elf_hdr, .{197 try emitElf(arena, &in, &out, elf_hdr, .{
196 .ofmt = out_fmt,198 .ofmt = out_fmt,
197 .only_section = only_section,199 .only_section = only_section,
198 .pad_to = pad_to,200 .pad_to = pad_to,
...@@ -208,22 +210,13 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -208,22 +210,13 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
208 if (pad_to) |_|210 if (pad_to) |_|
209 fatal("zig objcopy: ELF to ELF copying does not support --pad-to", .{});211 fatal("zig objcopy: ELF to ELF copying does not support --pad-to", .{});
210212
211 try stripElf(arena, in_file, out_file, elf_hdr, .{213 fatal("unimplemented", .{});
212 .strip_debug = strip_debug,
213 .strip_all = strip_all,
214 .only_keep_debug = only_keep_debug,
215 .add_debuglink = opt_add_debuglink,
216 .extract_to = opt_extract,
217 .compress_debug = compress_debug_sections,
218 .add_section = add_section,
219 .set_section_alignment = set_section_alignment,
220 .set_section_flags = set_section_flags,
221 });
222 return std.process.cleanExit();
223 },214 },
224 else => fatal("unsupported output object format: {s}", .{@tagName(out_fmt)}),215 else => fatal("unsupported output object format: {s}", .{@tagName(out_fmt)}),
225 }216 }
226217
218 try out.end();
219
227 if (listen) {220 if (listen) {
228 var stdin_reader = fs.File.stdin().reader(&stdin_buffer);221 var stdin_reader = fs.File.stdin().reader(&stdin_buffer);
229 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);222 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
...@@ -304,12 +297,12 @@ const SetSectionFlags = struct {...@@ -304,12 +297,12 @@ const SetSectionFlags = struct {
304297
305fn emitElf(298fn emitElf(
306 arena: Allocator,299 arena: Allocator,
307 in_file: File,300 in: *File.Reader,
308 out_file: File,301 out: *File.Writer,
309 elf_hdr: elf.Header,302 elf_hdr: elf.Header,
310 options: EmitRawElfOptions,303 options: EmitRawElfOptions,
311) !void {304) !void {
312 var binary_elf_output = try BinaryElfOutput.parse(arena, in_file, elf_hdr);305 var binary_elf_output = try BinaryElfOutput.parse(arena, in, elf_hdr);
313 defer binary_elf_output.deinit();306 defer binary_elf_output.deinit();
314307
315 if (options.ofmt == .elf) {308 if (options.ofmt == .elf) {
...@@ -328,8 +321,8 @@ fn emitElf(...@@ -328,8 +321,8 @@ fn emitElf(
328 continue;321 continue;
329 }322 }
330323
331 try writeBinaryElfSection(in_file, out_file, section);324 try writeBinaryElfSection(in, out, section);
332 try padFile(out_file, options.pad_to);325 try padFile(out, options.pad_to);
333 return;326 return;
334 }327 }
335 },328 },
...@@ -342,10 +335,10 @@ fn emitElf(...@@ -342,10 +335,10 @@ fn emitElf(
342 switch (options.ofmt) {335 switch (options.ofmt) {
343 .raw => {336 .raw => {
344 for (binary_elf_output.sections.items) |section| {337 for (binary_elf_output.sections.items) |section| {
345 try out_file.seekTo(section.binaryOffset);338 try out.seekTo(section.binaryOffset);
346 try writeBinaryElfSection(in_file, out_file, section);339 try writeBinaryElfSection(in, out, section);
347 }340 }
348 try padFile(out_file, options.pad_to);341 try padFile(out, options.pad_to);
349 },342 },
350 .hex => {343 .hex => {
351 if (binary_elf_output.segments.items.len == 0) return;344 if (binary_elf_output.segments.items.len == 0) return;
...@@ -353,15 +346,15 @@ fn emitElf(...@@ -353,15 +346,15 @@ fn emitElf(
353 return error.InvalidHexfileAddressRange;346 return error.InvalidHexfileAddressRange;
354 }347 }
355348
356 var hex_writer = HexWriter{ .out_file = out_file };349 var hex_writer = HexWriter{ .out = out };
357 for (binary_elf_output.segments.items) |segment| {350 for (binary_elf_output.segments.items) |segment| {
358 try hex_writer.writeSegment(segment, in_file);351 try hex_writer.writeSegment(segment, in);
359 }352 }
360 if (options.pad_to) |_| {353 if (options.pad_to) |_| {
361 // Padding to a size in hex files isn't applicable354 // Padding to a size in hex files isn't applicable
362 return error.InvalidArgument;355 return error.InvalidArgument;
363 }356 }
364 try hex_writer.writeEOF();357 try hex_writer.writeEof();
365 },358 },
366 else => unreachable,359 else => unreachable,
367 }360 }
...@@ -399,7 +392,7 @@ const BinaryElfOutput = struct {...@@ -399,7 +392,7 @@ const BinaryElfOutput = struct {
399 self.segments.deinit(self.allocator);392 self.segments.deinit(self.allocator);
400 }393 }
401394
402 pub fn parse(allocator: Allocator, elf_file: File, elf_hdr: elf.Header) !Self {395 pub fn parse(allocator: Allocator, in: *File.Reader, elf_hdr: elf.Header) !Self {
403 var self: Self = .{396 var self: Self = .{
404 .segments = .{},397 .segments = .{},
405 .sections = .{},398 .sections = .{},
...@@ -412,7 +405,7 @@ const BinaryElfOutput = struct {...@@ -412,7 +405,7 @@ const BinaryElfOutput = struct {
412 self.shstrtab = blk: {405 self.shstrtab = blk: {
413 if (elf_hdr.shstrndx >= elf_hdr.shnum) break :blk null;406 if (elf_hdr.shstrndx >= elf_hdr.shnum) break :blk null;
414407
415 var section_headers = elf_hdr.section_header_iterator(&elf_file);408 var section_headers = elf_hdr.iterateSectionHeaders(in);
416409
417 var section_counter: usize = 0;410 var section_counter: usize = 0;
418 while (section_counter < elf_hdr.shstrndx) : (section_counter += 1) {411 while (section_counter < elf_hdr.shstrndx) : (section_counter += 1) {
...@@ -421,18 +414,13 @@ const BinaryElfOutput = struct {...@@ -421,18 +414,13 @@ const BinaryElfOutput = struct {
421414
422 const shstrtab_shdr = (try section_headers.next()).?;415 const shstrtab_shdr = (try section_headers.next()).?;
423416
424 const buffer = try allocator.alloc(u8, @intCast(shstrtab_shdr.sh_size));417 try in.seekTo(shstrtab_shdr.sh_offset);
425 errdefer allocator.free(buffer);418 break :blk try in.interface.readAlloc(allocator, shstrtab_shdr.sh_size);
426
427 const num_read = try elf_file.preadAll(buffer, shstrtab_shdr.sh_offset);
428 if (num_read != buffer.len) return error.EndOfStream;
429
430 break :blk buffer;
431 };419 };
432420
433 errdefer if (self.shstrtab) |shstrtab| allocator.free(shstrtab);421 errdefer if (self.shstrtab) |shstrtab| allocator.free(shstrtab);
434422
435 var section_headers = elf_hdr.section_header_iterator(&elf_file);423 var section_headers = elf_hdr.iterateSectionHeaders(in);
436 while (try section_headers.next()) |section| {424 while (try section_headers.next()) |section| {
437 if (sectionValidForOutput(section)) {425 if (sectionValidForOutput(section)) {
438 const newSection = try allocator.create(BinaryElfSection);426 const newSection = try allocator.create(BinaryElfSection);
...@@ -451,7 +439,7 @@ const BinaryElfOutput = struct {...@@ -451,7 +439,7 @@ const BinaryElfOutput = struct {
451 }439 }
452 }440 }
453441
454 var program_headers = elf_hdr.program_header_iterator(&elf_file);442 var program_headers = elf_hdr.iterateProgramHeaders(in);
455 while (try program_headers.next()) |phdr| {443 while (try program_headers.next()) |phdr| {
456 if (phdr.p_type == elf.PT_LOAD) {444 if (phdr.p_type == elf.PT_LOAD) {
457 const newSegment = try allocator.create(BinaryElfSegment);445 const newSegment = try allocator.create(BinaryElfSegment);
...@@ -539,19 +527,17 @@ const BinaryElfOutput = struct {...@@ -539,19 +527,17 @@ const BinaryElfOutput = struct {
539 }527 }
540};528};
541529
542fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void {530fn writeBinaryElfSection(in: *File.Reader, out: *File.Writer, section: *BinaryElfSection) !void {
543 try out_file.writeFileAll(elf_file, .{531 try in.seekTo(section.elfOffset);
544 .in_offset = section.elfOffset,532 _ = try out.interface.sendFileAll(in, .limited(section.fileSize));
545 .in_len = section.fileSize,
546 });
547}533}
548534
549const HexWriter = struct {535const HexWriter = struct {
550 prev_addr: ?u32 = null,536 prev_addr: ?u32 = null,
551 out_file: File,537 out: *File.Writer,
552538
553 /// Max data bytes per line of output539 /// Max data bytes per line of output
554 const MAX_PAYLOAD_LEN: u8 = 16;540 const max_payload_len: u8 = 16;
555541
556 fn addressParts(address: u16) [2]u8 {542 fn addressParts(address: u16) [2]u8 {
557 const msb: u8 = @truncate(address >> 8);543 const msb: u8 = @truncate(address >> 8);
...@@ -627,13 +613,13 @@ const HexWriter = struct {...@@ -627,13 +613,13 @@ const HexWriter = struct {
627 return (sum ^ 0xFF) +% 1;613 return (sum ^ 0xFF) +% 1;
628 }614 }
629615
630 fn write(self: Record, file: File) File.WriteError!void {616 fn write(self: Record, out: *File.Writer) !void {
631 const linesep = "\r\n";617 const linesep = "\r\n";
632 // colon, (length, address, type, payload, checksum) as hex, CRLF618 // colon, (length, address, type, payload, checksum) as hex, CRLF
633 const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1) * 2 + linesep.len;619 const BUFSIZE = 1 + (1 + 2 + 1 + max_payload_len + 1) * 2 + linesep.len;
634 var outbuf: [BUFSIZE]u8 = undefined;620 var outbuf: [BUFSIZE]u8 = undefined;
635 const payload_bytes = self.getPayloadBytes();621 const payload_bytes = self.getPayloadBytes();
636 assert(payload_bytes.len <= MAX_PAYLOAD_LEN);622 assert(payload_bytes.len <= max_payload_len);
637623
638 const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3X}{4X:0>2}" ++ linesep, .{624 const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3X}{4X:0>2}" ++ linesep, .{
639 @as(u8, @intCast(payload_bytes.len)),625 @as(u8, @intCast(payload_bytes.len)),
...@@ -642,38 +628,37 @@ const HexWriter = struct {...@@ -642,38 +628,37 @@ const HexWriter = struct {
642 payload_bytes,628 payload_bytes,
643 self.checksum(),629 self.checksum(),
644 });630 });
645 try file.writeAll(line);631 try out.interface.writeAll(line);
646 }632 }
647 };633 };
648634
649 pub fn writeSegment(self: *HexWriter, segment: *const BinaryElfSegment, elf_file: File) !void {635 pub fn writeSegment(self: *HexWriter, segment: *const BinaryElfSegment, in: *File.Reader) !void {
650 var buf: [MAX_PAYLOAD_LEN]u8 = undefined;636 var buf: [max_payload_len]u8 = undefined;
651 var bytes_read: usize = 0;637 var bytes_read: usize = 0;
652 while (bytes_read < segment.fileSize) {638 while (bytes_read < segment.fileSize) {
653 const row_address: u32 = @intCast(segment.physicalAddress + bytes_read);639 const row_address: u32 = @intCast(segment.physicalAddress + bytes_read);
654640
655 const remaining = segment.fileSize - bytes_read;641 const remaining = segment.fileSize - bytes_read;
656 const to_read: usize = @intCast(@min(remaining, MAX_PAYLOAD_LEN));642 const dest = buf[0..@min(remaining, max_payload_len)];
657 const did_read = try elf_file.preadAll(buf[0..to_read], segment.elfOffset + bytes_read);643 try in.seekTo(segment.elfOffset + bytes_read);
658 if (did_read < to_read) return error.UnexpectedEOF;644 try in.interface.readSliceAll(dest);
645 try self.writeDataRow(row_address, dest);
659646
660 try self.writeDataRow(row_address, buf[0..did_read]);647 bytes_read += dest.len;
661
662 bytes_read += did_read;
663 }648 }
664 }649 }
665650
666 fn writeDataRow(self: *HexWriter, address: u32, data: []const u8) File.WriteError!void {651 fn writeDataRow(self: *HexWriter, address: u32, data: []const u8) !void {
667 const record = Record.Data(address, data);652 const record = Record.Data(address, data);
668 if (address > 0xFFFF and (self.prev_addr == null or record.address != self.prev_addr.?)) {653 if (address > 0xFFFF and (self.prev_addr == null or record.address != self.prev_addr.?)) {
669 try Record.Address(address).write(self.out_file);654 try Record.Address(address).write(self.out);
670 }655 }
671 try record.write(self.out_file);656 try record.write(self.out);
672 self.prev_addr = @intCast(record.address + data.len);657 self.prev_addr = @intCast(record.address + data.len);
673 }658 }
674659
675 fn writeEOF(self: HexWriter) File.WriteError!void {660 fn writeEof(self: HexWriter) !void {
676 try Record.EOF().write(self.out_file);661 try Record.EOF().write(self.out);
677 }662 }
678};663};
679664
...@@ -686,9 +671,9 @@ fn containsValidAddressRange(segments: []*BinaryElfSegment) bool {...@@ -686,9 +671,9 @@ fn containsValidAddressRange(segments: []*BinaryElfSegment) bool {
686 return true;671 return true;
687}672}
688673
689fn padFile(f: File, opt_size: ?u64) !void {674fn padFile(out: *File.Writer, opt_size: ?u64) !void {
690 const size = opt_size orelse return;675 const size = opt_size orelse return;
691 try f.setEndPos(size);676 try out.file.setEndPos(size);
692}677}
693678
694test "HexWriter.Record.Address has correct payload and checksum" {679test "HexWriter.Record.Address has correct payload and checksum" {
...@@ -732,836 +717,6 @@ test "containsValidAddressRange" {...@@ -732,836 +717,6 @@ test "containsValidAddressRange" {
732 try std.testing.expect(containsValidAddressRange(&buf));717 try std.testing.expect(containsValidAddressRange(&buf));
733}718}
734719
735// -------------
736// ELF to ELF stripping
737
738const StripElfOptions = struct {
739 extract_to: ?[]const u8 = null,
740 add_debuglink: ?[]const u8 = null,
741 strip_all: bool = false,
742 strip_debug: bool = false,
743 only_keep_debug: bool = false,
744 compress_debug: bool = false,
745 add_section: ?AddSection,
746 set_section_alignment: ?SetSectionAlignment,
747 set_section_flags: ?SetSectionFlags,
748};
749
750fn stripElf(
751 allocator: Allocator,
752 in_file: File,
753 out_file: File,
754 elf_hdr: elf.Header,
755 options: StripElfOptions,
756) !void {
757 const Filter = ElfFileHelper.Filter;
758 const DebugLink = ElfFileHelper.DebugLink;
759
760 const filter: Filter = filter: {
761 if (options.only_keep_debug) break :filter .debug;
762 if (options.strip_all) break :filter .program;
763 if (options.strip_debug) break :filter .program_and_symbols;
764 break :filter .all;
765 };
766
767 const filter_complement: ?Filter = blk: {
768 if (options.extract_to) |_| {
769 break :blk switch (filter) {
770 .program => .debug_and_symbols,
771 .debug => .program_and_symbols,
772 .program_and_symbols => .debug,
773 .debug_and_symbols => .program,
774 .all => fatal("zig objcopy: nothing to extract", .{}),
775 };
776 } else {
777 break :blk null;
778 }
779 };
780 const debuglink_path = path: {
781 if (options.add_debuglink) |path| break :path path;
782 if (options.extract_to) |path| break :path path;
783 break :path null;
784 };
785
786 switch (elf_hdr.is_64) {
787 inline else => |is_64| {
788 var elf_file = try ElfFile(is_64).parse(allocator, in_file, elf_hdr);
789 defer elf_file.deinit();
790
791 if (options.add_section) |user_section| {
792 for (elf_file.sections) |section| {
793 if (std.mem.eql(u8, section.name, user_section.section_name)) {
794 fatal("zig objcopy: unable to add section '{s}'. Section already exists in input", .{user_section.section_name});
795 }
796 }
797 }
798
799 if (filter_complement) |flt| {
800 // write the .dbg file and close it, so it can be read back to compute the debuglink checksum.
801 const path = options.extract_to.?;
802 const dbg_file = std.fs.cwd().createFile(path, .{}) catch |err| {
803 fatal("zig objcopy: unable to create '{s}': {s}", .{ path, @errorName(err) });
804 };
805 defer dbg_file.close();
806
807 try elf_file.emit(allocator, dbg_file, in_file, .{ .section_filter = flt, .compress_debug = options.compress_debug });
808 }
809
810 const debuglink: ?DebugLink = if (debuglink_path) |path| ElfFileHelper.createDebugLink(path) else null;
811 try elf_file.emit(allocator, out_file, in_file, .{
812 .section_filter = filter,
813 .debuglink = debuglink,
814 .compress_debug = options.compress_debug,
815 .add_section = options.add_section,
816 .set_section_alignment = options.set_section_alignment,
817 .set_section_flags = options.set_section_flags,
818 });
819 },
820 }
821}
822
823// note: this is "a minimal effort implementation"
824// It doesn't support all possibile elf files: some sections type may need fixups, the program header may need fix up, ...
825// It was written for a specific use case (strip debug info to a sperate file, for linux 64-bits executables built with `zig` or `zig c++` )
826// It moves and reoders the sections as little as possible to avoid having to do fixups.
827// TODO: support non-native endianess
828
829fn ElfFile(comptime is_64: bool) type {
830 const Elf_Ehdr = if (is_64) elf.Elf64_Ehdr else elf.Elf32_Ehdr;
831 const Elf_Phdr = if (is_64) elf.Elf64_Phdr else elf.Elf32_Phdr;
832 const Elf_Shdr = if (is_64) elf.Elf64_Shdr else elf.Elf32_Shdr;
833 const Elf_Chdr = if (is_64) elf.Elf64_Chdr else elf.Elf32_Chdr;
834 const Elf_Sym = if (is_64) elf.Elf64_Sym else elf.Elf32_Sym;
835 const Elf_OffSize = if (is_64) elf.Elf64_Off else elf.Elf32_Off;
836
837 return struct {
838 raw_elf_header: Elf_Ehdr,
839 program_segments: []const Elf_Phdr,
840 sections: []const Section,
841 arena: std.heap.ArenaAllocator,
842
843 const SectionCategory = ElfFileHelper.SectionCategory;
844 const section_memory_align: std.mem.Alignment = .of(Elf_Sym); // most restrictive of what we may load in memory
845 const Section = struct {
846 section: Elf_Shdr,
847 name: []const u8 = "",
848 segment: ?*const Elf_Phdr = null, // if the section is used by a program segment (there can be more than one)
849 payload: ?[]align(section_memory_align.toByteUnits()) const u8 = null, // if we need the data in memory
850 category: SectionCategory = .none, // should the section be kept in the exe or stripped to the debug database, or both.
851 };
852
853 const Self = @This();
854
855 pub fn parse(gpa: Allocator, in_file: File, header: elf.Header) !Self {
856 var arena = std.heap.ArenaAllocator.init(gpa);
857 errdefer arena.deinit();
858 const allocator = arena.allocator();
859
860 var raw_header: Elf_Ehdr = undefined;
861 {
862 const bytes_read = try in_file.preadAll(std.mem.asBytes(&raw_header), 0);
863 if (bytes_read < @sizeOf(Elf_Ehdr))
864 return error.TRUNCATED_ELF;
865 }
866
867 // program header: list of segments
868 const program_segments = blk: {
869 if (@sizeOf(Elf_Phdr) != header.phentsize)
870 fatal("zig objcopy: unsupported ELF file, unexpected phentsize ({d})", .{header.phentsize});
871
872 const program_header = try allocator.alloc(Elf_Phdr, header.phnum);
873 const bytes_read = try in_file.preadAll(std.mem.sliceAsBytes(program_header), header.phoff);
874 if (bytes_read < @sizeOf(Elf_Phdr) * header.phnum)
875 return error.TRUNCATED_ELF;
876 break :blk program_header;
877 };
878
879 // section header
880 const sections = blk: {
881 if (@sizeOf(Elf_Shdr) != header.shentsize)
882 fatal("zig objcopy: unsupported ELF file, unexpected shentsize ({d})", .{header.shentsize});
883
884 const section_header = try allocator.alloc(Section, header.shnum);
885
886 const raw_section_header = try allocator.alloc(Elf_Shdr, header.shnum);
887 defer allocator.free(raw_section_header);
888 const bytes_read = try in_file.preadAll(std.mem.sliceAsBytes(raw_section_header), header.shoff);
889 if (bytes_read < @sizeOf(Elf_Phdr) * header.shnum)
890 return error.TRUNCATED_ELF;
891
892 for (section_header, raw_section_header) |*section, hdr| {
893 section.* = .{ .section = hdr };
894 }
895 break :blk section_header;
896 };
897
898 // load data to memory for some sections:
899 // string tables for access
900 // sections than need modifications when other sections move.
901 for (sections, 0..) |*section, idx| {
902 const need_data = switch (section.section.sh_type) {
903 elf.DT_VERSYM => true,
904 elf.SHT_SYMTAB, elf.SHT_DYNSYM => true,
905 else => false,
906 };
907 const need_strings = (idx == header.shstrndx);
908
909 if (need_data or need_strings) {
910 const buffer = try allocator.alignedAlloc(u8, section_memory_align, @intCast(section.section.sh_size));
911 const bytes_read = try in_file.preadAll(buffer, section.section.sh_offset);
912 if (bytes_read != section.section.sh_size) return error.TRUNCATED_ELF;
913 section.payload = buffer;
914 }
915 }
916
917 // fill-in sections info:
918 // resolve the name
919 // find if a program segment uses the section
920 // categorize sections usage (used by program segments, debug datadase, common metadata, symbol table)
921 for (sections) |*section| {
922 section.segment = for (program_segments) |*seg| {
923 if (sectionWithinSegment(section.section, seg.*)) break seg;
924 } else null;
925
926 if (section.section.sh_name != 0 and header.shstrndx != elf.SHN_UNDEF)
927 section.name = std.mem.span(@as([*:0]const u8, @ptrCast(&sections[header.shstrndx].payload.?[section.section.sh_name])));
928
929 const category_from_program: SectionCategory = if (section.segment != null) .exe else .debug;
930 section.category = switch (section.section.sh_type) {
931 elf.SHT_NOTE => .common,
932 elf.SHT_SYMTAB => .symbols, // "strip all" vs "strip only debug"
933 elf.SHT_DYNSYM => .exe,
934 elf.SHT_PROGBITS => cat: {
935 if (std.mem.eql(u8, section.name, ".comment")) break :cat .exe;
936 if (std.mem.eql(u8, section.name, ".gnu_debuglink")) break :cat .none;
937 break :cat category_from_program;
938 },
939 elf.SHT_LOPROC...elf.SHT_HIPROC => .common, // don't strip unknown sections
940 elf.SHT_LOUSER...elf.SHT_HIUSER => .common, // don't strip unknown sections
941 else => category_from_program,
942 };
943 }
944
945 sections[0].category = .common; // mandatory null section
946 if (header.shstrndx != elf.SHN_UNDEF)
947 sections[header.shstrndx].category = .common; // string table for the headers
948
949 // recursively propagate section categories to their linked sections, so that they are kept together
950 var dirty: u1 = 1;
951 while (dirty != 0) {
952 dirty = 0;
953
954 for (sections) |*section| {
955 if (section.section.sh_link != elf.SHN_UNDEF)
956 dirty |= ElfFileHelper.propagateCategory(&sections[section.section.sh_link].category, section.category);
957 if ((section.section.sh_flags & elf.SHF_INFO_LINK) != 0 and section.section.sh_info != elf.SHN_UNDEF)
958 dirty |= ElfFileHelper.propagateCategory(&sections[section.section.sh_info].category, section.category);
959 }
960 }
961
962 return Self{
963 .arena = arena,
964 .raw_elf_header = raw_header,
965 .program_segments = program_segments,
966 .sections = sections,
967 };
968 }
969
970 pub fn deinit(self: *Self) void {
971 self.arena.deinit();
972 }
973
974 const Filter = ElfFileHelper.Filter;
975 const DebugLink = ElfFileHelper.DebugLink;
976 const EmitElfOptions = struct {
977 section_filter: Filter = .all,
978 debuglink: ?DebugLink = null,
979 compress_debug: bool = false,
980 add_section: ?AddSection = null,
981 set_section_alignment: ?SetSectionAlignment = null,
982 set_section_flags: ?SetSectionFlags = null,
983 };
984 fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void {
985 var arena = std.heap.ArenaAllocator.init(gpa);
986 defer arena.deinit();
987 const allocator = arena.allocator();
988
989 // when emitting the stripped exe:
990 // - unused sections are removed
991 // when emitting the debug file:
992 // - all sections are kept, but some are emptied and their types is changed to SHT_NOBITS
993 // the program header is kept unchanged. (`strip` does update it, but `eu-strip` does not, and it still works)
994
995 const Update = struct {
996 action: ElfFileHelper.Action,
997
998 // remap the indexs after omitting the filtered sections
999 remap_idx: u16,
1000
1001 // optionally overrides the payload from the source file
1002 payload: ?[]align(section_memory_align.toByteUnits()) const u8 = null,
1003 section: ?Elf_Shdr = null,
1004 };
1005 const sections_update = try allocator.alloc(Update, self.sections.len);
1006 const new_shnum = blk: {
1007 var next_idx: u16 = 0;
1008 for (self.sections, sections_update) |section, *update| {
1009 const action = ElfFileHelper.selectAction(section.category, options.section_filter);
1010 const remap_idx = idx: {
1011 if (action == .strip) break :idx elf.SHN_UNDEF;
1012 next_idx += 1;
1013 break :idx next_idx - 1;
1014 };
1015 update.* = Update{ .action = action, .remap_idx = remap_idx };
1016 }
1017
1018 if (options.debuglink != null)
1019 next_idx += 1;
1020
1021 if (options.add_section != null) {
1022 next_idx += 1;
1023 }
1024
1025 break :blk next_idx;
1026 };
1027
1028 // add a ".gnu_debuglink" to the string table if needed
1029 const debuglink_name: u32 = blk: {
1030 if (options.debuglink == null) break :blk elf.SHN_UNDEF;
1031 if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF)
1032 fatal("zig objcopy: no strtab, cannot add the debuglink section", .{}); // TODO add the section if needed?
1033
1034 const strtab = &self.sections[self.raw_elf_header.e_shstrndx];
1035 const update = &sections_update[self.raw_elf_header.e_shstrndx];
1036
1037 const name: []const u8 = ".gnu_debuglink";
1038 const new_offset: u32 = @intCast(strtab.payload.?.len);
1039 const buf = try allocator.alignedAlloc(u8, section_memory_align, new_offset + name.len + 1);
1040 @memcpy(buf[0..new_offset], strtab.payload.?);
1041 @memcpy(buf[new_offset..][0..name.len], name);
1042 buf[new_offset + name.len] = 0;
1043
1044 assert(update.action == .keep);
1045 update.payload = buf;
1046
1047 break :blk new_offset;
1048 };
1049
1050 // add user section to the string table if needed
1051 const user_section_name: u32 = blk: {
1052 if (options.add_section == null) break :blk elf.SHN_UNDEF;
1053 if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF)
1054 fatal("zig objcopy: no strtab, cannot add the user section", .{}); // TODO add the section if needed?
1055
1056 const strtab = &self.sections[self.raw_elf_header.e_shstrndx];
1057 const update = &sections_update[self.raw_elf_header.e_shstrndx];
1058
1059 const name = options.add_section.?.section_name;
1060 const new_offset: u32 = @intCast(strtab.payload.?.len);
1061 const buf = try allocator.alignedAlloc(u8, section_memory_align, new_offset + name.len + 1);
1062 @memcpy(buf[0..new_offset], strtab.payload.?);
1063 @memcpy(buf[new_offset..][0..name.len], name);
1064 buf[new_offset + name.len] = 0;
1065
1066 assert(update.action == .keep);
1067 update.payload = buf;
1068
1069 break :blk new_offset;
1070 };
1071
1072 // maybe compress .debug sections
1073 if (options.compress_debug) {
1074 for (self.sections[1..], sections_update[1..]) |section, *update| {
1075 if (update.action != .keep) continue;
1076 if (!std.mem.startsWith(u8, section.name, ".debug_")) continue;
1077 if ((section.section.sh_flags & elf.SHF_COMPRESSED) != 0) continue; // already compressed
1078
1079 const chdr = Elf_Chdr{
1080 .ch_type = elf.COMPRESS.ZLIB,
1081 .ch_size = section.section.sh_size,
1082 .ch_addralign = section.section.sh_addralign,
1083 };
1084
1085 const compressed_payload = try ElfFileHelper.tryCompressSection(allocator, in_file, section.section.sh_offset, section.section.sh_size, std.mem.asBytes(&chdr));
1086 if (compressed_payload) |payload| {
1087 update.payload = payload;
1088 update.section = section.section;
1089 update.section.?.sh_addralign = @alignOf(Elf_Chdr);
1090 update.section.?.sh_size = @intCast(payload.len);
1091 update.section.?.sh_flags |= elf.SHF_COMPRESSED;
1092 }
1093 }
1094 }
1095
1096 var cmdbuf = std.ArrayList(ElfFileHelper.WriteCmd).init(allocator);
1097 defer cmdbuf.deinit();
1098 try cmdbuf.ensureUnusedCapacity(3 + new_shnum);
1099 var eof_offset: Elf_OffSize = 0; // track the end of the data written so far.
1100
1101 // build the updated headers
1102 // nb: updated_elf_header will be updated before the actual write
1103 var updated_elf_header = self.raw_elf_header;
1104 if (updated_elf_header.e_shstrndx != elf.SHN_UNDEF)
1105 updated_elf_header.e_shstrndx = sections_update[updated_elf_header.e_shstrndx].remap_idx;
1106 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = std.mem.asBytes(&updated_elf_header), .out_offset = 0 } });
1107 eof_offset = @sizeOf(Elf_Ehdr);
1108
1109 // program header as-is.
1110 // nb: for only-debug files, removing it appears to work, but is invalid by ELF specifcation.
1111 {
1112 assert(updated_elf_header.e_phoff == @sizeOf(Elf_Ehdr));
1113 const data = std.mem.sliceAsBytes(self.program_segments);
1114 assert(data.len == @as(usize, updated_elf_header.e_phentsize) * updated_elf_header.e_phnum);
1115 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = data, .out_offset = updated_elf_header.e_phoff } });
1116 eof_offset = updated_elf_header.e_phoff + @as(Elf_OffSize, @intCast(data.len));
1117 }
1118
1119 // update sections and queue payload writes
1120 const updated_section_header = blk: {
1121 const dest_sections = try allocator.alloc(Elf_Shdr, new_shnum);
1122
1123 {
1124 // the ELF format doesn't specify the order for all sections.
1125 // this code only supports when they are in increasing file order.
1126 var offset: u64 = eof_offset;
1127 for (self.sections[1..]) |section| {
1128 if (section.section.sh_type == elf.SHT_NOBITS)
1129 continue;
1130 if (section.section.sh_offset < offset) {
1131 fatal("zig objcopy: unsupported ELF file", .{});
1132 }
1133 offset = section.section.sh_offset;
1134 }
1135 }
1136
1137 dest_sections[0] = self.sections[0].section;
1138
1139 var dest_section_idx: u32 = 1;
1140 for (self.sections[1..], sections_update[1..]) |section, update| {
1141 if (update.action == .strip) continue;
1142 assert(update.remap_idx == dest_section_idx);
1143
1144 const src = if (update.section) |*s| s else &section.section;
1145 const dest = &dest_sections[dest_section_idx];
1146 const payload = if (update.payload) |data| data else section.payload;
1147 dest_section_idx += 1;
1148
1149 dest.* = src.*;
1150
1151 if (src.sh_link != elf.SHN_UNDEF)
1152 dest.sh_link = sections_update[src.sh_link].remap_idx;
1153 if ((src.sh_flags & elf.SHF_INFO_LINK) != 0 and src.sh_info != elf.SHN_UNDEF)
1154 dest.sh_info = sections_update[src.sh_info].remap_idx;
1155
1156 if (payload) |data|
1157 dest.sh_size = @intCast(data.len);
1158
1159 const addralign = if (src.sh_addralign == 0 or dest.sh_type == elf.SHT_NOBITS) 1 else src.sh_addralign;
1160 dest.sh_offset = std.mem.alignForward(Elf_OffSize, eof_offset, addralign);
1161 if (src.sh_offset != dest.sh_offset and section.segment != null and update.action != .empty and dest.sh_type != elf.SHT_NOTE and dest.sh_type != elf.SHT_NOBITS) {
1162 if (src.sh_offset > dest.sh_offset) {
1163 dest.sh_offset = src.sh_offset; // add padding to avoid modifing the program segments
1164 } else {
1165 fatal("zig objcopy: cannot adjust program segments", .{});
1166 }
1167 }
1168 assert(dest.sh_addr % addralign == dest.sh_offset % addralign);
1169
1170 if (update.action == .empty)
1171 dest.sh_type = elf.SHT_NOBITS;
1172
1173 if (dest.sh_type != elf.SHT_NOBITS) {
1174 if (payload) |src_data| {
1175 // update sections payload and write
1176 const dest_data = switch (src.sh_type) {
1177 elf.DT_VERSYM => dst_data: {
1178 const data = try allocator.alignedAlloc(u8, section_memory_align, src_data.len);
1179 @memcpy(data, src_data);
1180
1181 const defs = @as([*]elf.Verdef, @ptrCast(data))[0 .. @as(usize, @intCast(src.sh_size)) / @sizeOf(elf.Verdef)];
1182 for (defs) |*def| switch (def.ndx) {
1183 .LOCAL, .GLOBAL => {},
1184 else => def.ndx = @enumFromInt(sections_update[src.sh_info].remap_idx),
1185 };
1186
1187 break :dst_data data;
1188 },
1189 elf.SHT_SYMTAB, elf.SHT_DYNSYM => dst_data: {
1190 const data = try allocator.alignedAlloc(u8, section_memory_align, src_data.len);
1191 @memcpy(data, src_data);
1192
1193 const syms = @as([*]Elf_Sym, @ptrCast(data))[0 .. @as(usize, @intCast(src.sh_size)) / @sizeOf(Elf_Sym)];
1194 for (syms) |*sym| {
1195 if (sym.st_shndx != elf.SHN_UNDEF and sym.st_shndx < elf.SHN_LORESERVE)
1196 sym.st_shndx = sections_update[sym.st_shndx].remap_idx;
1197 }
1198
1199 break :dst_data data;
1200 },
1201 else => src_data,
1202 };
1203
1204 assert(dest_data.len == dest.sh_size);
1205 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = dest_data, .out_offset = dest.sh_offset } });
1206 eof_offset = dest.sh_offset + dest.sh_size;
1207 } else {
1208 // direct contents copy
1209 cmdbuf.appendAssumeCapacity(.{ .copy_range = .{ .in_offset = src.sh_offset, .len = dest.sh_size, .out_offset = dest.sh_offset } });
1210 eof_offset = dest.sh_offset + dest.sh_size;
1211 }
1212 } else {
1213 // account for alignment padding even in empty sections to keep logical section order
1214 eof_offset = dest.sh_offset;
1215 }
1216 }
1217
1218 // add a ".gnu_debuglink" section
1219 if (options.debuglink) |link| {
1220 const payload = payload: {
1221 const crc_offset = std.mem.alignForward(usize, link.name.len + 1, 4);
1222 const buf = try allocator.alignedAlloc(u8, .@"4", crc_offset + 4);
1223 @memcpy(buf[0..link.name.len], link.name);
1224 @memset(buf[link.name.len..crc_offset], 0);
1225 @memcpy(buf[crc_offset..], std.mem.asBytes(&link.crc32));
1226 break :payload buf;
1227 };
1228
1229 dest_sections[dest_section_idx] = Elf_Shdr{
1230 .sh_name = debuglink_name,
1231 .sh_type = elf.SHT_PROGBITS,
1232 .sh_flags = 0,
1233 .sh_addr = 0,
1234 .sh_offset = eof_offset,
1235 .sh_size = @intCast(payload.len),
1236 .sh_link = elf.SHN_UNDEF,
1237 .sh_info = elf.SHN_UNDEF,
1238 .sh_addralign = 4,
1239 .sh_entsize = 0,
1240 };
1241 dest_section_idx += 1;
1242
1243 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = payload, .out_offset = eof_offset } });
1244 eof_offset += @as(Elf_OffSize, @intCast(payload.len));
1245 }
1246
1247 // --add-section
1248 if (options.add_section) |add_section| {
1249 var section_file = fs.cwd().openFile(add_section.file_path, .{}) catch |err|
1250 fatal("unable to open '{s}': {s}", .{ add_section.file_path, @errorName(err) });
1251 defer section_file.close();
1252
1253 const payload = try section_file.readToEndAlloc(arena.allocator(), std.math.maxInt(usize));
1254
1255 dest_sections[dest_section_idx] = Elf_Shdr{
1256 .sh_name = user_section_name,
1257 .sh_type = elf.SHT_PROGBITS,
1258 .sh_flags = 0,
1259 .sh_addr = 0,
1260 .sh_offset = eof_offset,
1261 .sh_size = @intCast(payload.len),
1262 .sh_link = elf.SHN_UNDEF,
1263 .sh_info = elf.SHN_UNDEF,
1264 .sh_addralign = 4,
1265 .sh_entsize = 0,
1266 };
1267 dest_section_idx += 1;
1268
1269 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = payload, .out_offset = eof_offset } });
1270 eof_offset += @as(Elf_OffSize, @intCast(payload.len));
1271 }
1272
1273 assert(dest_section_idx == new_shnum);
1274 break :blk dest_sections;
1275 };
1276
1277 // --set-section-alignment: overwrite alignment
1278 if (options.set_section_alignment) |set_align| {
1279 if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF)
1280 fatal("zig objcopy: no strtab, cannot add the user section", .{}); // TODO add the section if needed?
1281
1282 const strtab = &sections_update[self.raw_elf_header.e_shstrndx];
1283 for (updated_section_header) |*section| {
1284 const section_name = std.mem.span(@as([*:0]const u8, @ptrCast(&strtab.payload.?[section.sh_name])));
1285 if (std.mem.eql(u8, section_name, set_align.section_name)) {
1286 section.sh_addralign = set_align.alignment;
1287 break;
1288 }
1289 } else std.log.warn("Skipping --set-section-alignment. Section '{s}' not found", .{set_align.section_name});
1290 }
1291
1292 // --set-section-flags: overwrite flags
1293 if (options.set_section_flags) |set_flags| {
1294 if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF)
1295 fatal("zig objcopy: no strtab, cannot add the user section", .{}); // TODO add the section if needed?
1296
1297 const strtab = &sections_update[self.raw_elf_header.e_shstrndx];
1298 for (updated_section_header) |*section| {
1299 const section_name = std.mem.span(@as([*:0]const u8, @ptrCast(&strtab.payload.?[section.sh_name])));
1300 if (std.mem.eql(u8, section_name, set_flags.section_name)) {
1301 section.sh_flags = std.elf.SHF_WRITE; // default is writable cleared by "readonly"
1302 const f = set_flags.flags;
1303
1304 // Supporting a subset of GNU and LLVM objcopy for ELF only
1305 // GNU:
1306 // alloc: add SHF_ALLOC
1307 // contents: if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing
1308 // load: if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing (same as contents)
1309 // noload: not ELF relevant
1310 // readonly: clear default SHF_WRITE flag
1311 // code: add SHF_EXECINSTR
1312 // data: not ELF relevant
1313 // rom: ignored
1314 // exclude: add SHF_EXCLUDE
1315 // share: not ELF relevant
1316 // debug: not ELF relevant
1317 // large: add SHF_X86_64_LARGE. Fatal error if target is not x86_64
1318 if (f.alloc) section.sh_flags |= std.elf.SHF_ALLOC;
1319 if (f.contents or f.load) {
1320 if (section.sh_type == std.elf.SHT_NOBITS) section.sh_type = std.elf.SHT_PROGBITS;
1321 }
1322 if (f.readonly) section.sh_flags &= ~@as(@TypeOf(section.sh_type), std.elf.SHF_WRITE);
1323 if (f.code) section.sh_flags |= std.elf.SHF_EXECINSTR;
1324 if (f.exclude) section.sh_flags |= std.elf.SHF_EXCLUDE;
1325 if (f.large) {
1326 if (updated_elf_header.e_machine != std.elf.EM.X86_64)
1327 fatal("zig objcopy: 'large' section flag is only supported on x86_64 targets", .{});
1328 section.sh_flags |= std.elf.SHF_X86_64_LARGE;
1329 }
1330
1331 // LLVM:
1332 // merge: add SHF_MERGE
1333 // strings: add SHF_STRINGS
1334 if (f.merge) section.sh_flags |= std.elf.SHF_MERGE;
1335 if (f.strings) section.sh_flags |= std.elf.SHF_STRINGS;
1336 break;
1337 }
1338 } else std.log.warn("Skipping --set-section-flags. Section '{s}' not found", .{set_flags.section_name});
1339 }
1340
1341 // write the section header at the tail
1342 {
1343 const offset = std.mem.alignForward(Elf_OffSize, eof_offset, @alignOf(Elf_Shdr));
1344
1345 const data = std.mem.sliceAsBytes(updated_section_header);
1346 assert(data.len == @as(usize, updated_elf_header.e_shentsize) * new_shnum);
1347 updated_elf_header.e_shoff = offset;
1348 updated_elf_header.e_shnum = new_shnum;
1349
1350 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = data, .out_offset = updated_elf_header.e_shoff } });
1351 }
1352
1353 try ElfFileHelper.write(allocator, out_file, in_file, cmdbuf.items);
1354 }
1355
1356 fn sectionWithinSegment(section: Elf_Shdr, segment: Elf_Phdr) bool {
1357 const file_size = if (section.sh_type == elf.SHT_NOBITS) 0 else section.sh_size;
1358 return segment.p_offset <= section.sh_offset and (segment.p_offset + segment.p_filesz) >= (section.sh_offset + file_size);
1359 }
1360 };
1361}
1362
1363const ElfFileHelper = struct {
1364 const DebugLink = struct { name: []const u8, crc32: u32 };
1365 const Filter = enum { all, program, debug, program_and_symbols, debug_and_symbols };
1366
1367 const SectionCategory = enum { common, exe, debug, symbols, none };
1368 fn propagateCategory(cur: *SectionCategory, new: SectionCategory) u1 {
1369 const cat: SectionCategory = switch (cur.*) {
1370 .none => new,
1371 .common => .common,
1372 .debug => switch (new) {
1373 .none, .debug => .debug,
1374 else => new,
1375 },
1376 .exe => switch (new) {
1377 .common => .common,
1378 .none, .debug, .exe => .exe,
1379 .symbols => .exe,
1380 },
1381 .symbols => switch (new) {
1382 .none, .common, .debug, .exe => unreachable,
1383 .symbols => .symbols,
1384 },
1385 };
1386
1387 if (cur.* != cat) {
1388 cur.* = cat;
1389 return 1;
1390 } else {
1391 return 0;
1392 }
1393 }
1394
1395 const Action = enum { keep, strip, empty };
1396 fn selectAction(category: SectionCategory, filter: Filter) Action {
1397 if (category == .none) return .strip;
1398 return switch (filter) {
1399 .all => switch (category) {
1400 .none => .strip,
1401 else => .keep,
1402 },
1403 .program => switch (category) {
1404 .common, .exe => .keep,
1405 else => .strip,
1406 },
1407 .program_and_symbols => switch (category) {
1408 .common, .exe, .symbols => .keep,
1409 else => .strip,
1410 },
1411 .debug => switch (category) {
1412 .exe, .symbols => .empty,
1413 .none => .strip,
1414 else => .keep,
1415 },
1416 .debug_and_symbols => switch (category) {
1417 .exe => .empty,
1418 .none => .strip,
1419 else => .keep,
1420 },
1421 };
1422 }
1423
1424 const WriteCmd = union(enum) {
1425 copy_range: struct { in_offset: u64, len: u64, out_offset: u64 },
1426 write_data: struct { data: []const u8, out_offset: u64 },
1427 };
1428 fn write(allocator: Allocator, out_file: File, in_file: File, cmds: []const WriteCmd) !void {
1429 // consolidate holes between writes:
1430 // by coping original padding data from in_file (by fusing contiguous ranges)
1431 // by writing zeroes otherwise
1432 const zeroes = [1]u8{0} ** 4096;
1433 var consolidated = std.ArrayList(WriteCmd).init(allocator);
1434 defer consolidated.deinit();
1435 try consolidated.ensureUnusedCapacity(cmds.len * 2);
1436 var offset: u64 = 0;
1437 var fused_cmd: ?WriteCmd = null;
1438 for (cmds) |cmd| {
1439 switch (cmd) {
1440 .write_data => |data| {
1441 assert(data.out_offset >= offset);
1442 if (fused_cmd) |prev| {
1443 consolidated.appendAssumeCapacity(prev);
1444 fused_cmd = null;
1445 }
1446 if (data.out_offset > offset) {
1447 consolidated.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@intCast(data.out_offset - offset)], .out_offset = offset } });
1448 }
1449 consolidated.appendAssumeCapacity(cmd);
1450 offset = data.out_offset + data.data.len;
1451 },
1452 .copy_range => |range| {
1453 assert(range.out_offset >= offset);
1454 if (fused_cmd) |prev| {
1455 if (range.in_offset >= prev.copy_range.in_offset + prev.copy_range.len and (range.out_offset - prev.copy_range.out_offset == range.in_offset - prev.copy_range.in_offset)) {
1456 fused_cmd = .{ .copy_range = .{
1457 .in_offset = prev.copy_range.in_offset,
1458 .out_offset = prev.copy_range.out_offset,
1459 .len = (range.out_offset + range.len) - prev.copy_range.out_offset,
1460 } };
1461 } else {
1462 consolidated.appendAssumeCapacity(prev);
1463 if (range.out_offset > offset) {
1464 consolidated.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@intCast(range.out_offset - offset)], .out_offset = offset } });
1465 }
1466 fused_cmd = cmd;
1467 }
1468 } else {
1469 fused_cmd = cmd;
1470 }
1471 offset = range.out_offset + range.len;
1472 },
1473 }
1474 }
1475 if (fused_cmd) |cmd| {
1476 consolidated.appendAssumeCapacity(cmd);
1477 }
1478
1479 // write the output file
1480 for (consolidated.items) |cmd| {
1481 switch (cmd) {
1482 .write_data => |data| {
1483 var iovec = [_]std.posix.iovec_const{.{ .base = data.data.ptr, .len = data.data.len }};
1484 try out_file.pwritevAll(&iovec, data.out_offset);
1485 },
1486 .copy_range => |range| {
1487 const copied_bytes = try in_file.copyRangeAll(range.in_offset, out_file, range.out_offset, range.len);
1488 if (copied_bytes < range.len) return error.TRUNCATED_ELF;
1489 },
1490 }
1491 }
1492 }
1493
1494 fn tryCompressSection(allocator: Allocator, in_file: File, offset: u64, size: u64, prefix: []const u8) !?[]align(8) const u8 {
1495 if (size < prefix.len) return null;
1496
1497 try in_file.seekTo(offset);
1498 var section_reader = std.io.limitedReader(in_file.deprecatedReader(), size);
1499
1500 // allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed.
1501 const compressed_data = try allocator.alignedAlloc(u8, .@"8", @intCast(size));
1502 var compressed_stream = std.io.fixedBufferStream(compressed_data);
1503
1504 try compressed_stream.writer().writeAll(prefix);
1505
1506 {
1507 var compressor = try std.compress.zlib.compressor(compressed_stream.writer(), .{});
1508
1509 var buf: [8000]u8 = undefined;
1510 while (true) {
1511 const bytes_read = try section_reader.read(&buf);
1512 if (bytes_read == 0) break;
1513 const bytes_written = compressor.write(buf[0..bytes_read]) catch |err| switch (err) {
1514 error.NoSpaceLeft => {
1515 allocator.free(compressed_data);
1516 return null;
1517 },
1518 else => return err,
1519 };
1520 std.debug.assert(bytes_written == bytes_read);
1521 }
1522 compressor.finish() catch |err| switch (err) {
1523 error.NoSpaceLeft => {
1524 allocator.free(compressed_data);
1525 return null;
1526 },
1527 else => return err,
1528 };
1529 }
1530
1531 const compressed_len: usize = @intCast(compressed_stream.getPos() catch unreachable);
1532 const data = allocator.realloc(compressed_data, compressed_len) catch compressed_data;
1533 return data[0..compressed_len];
1534 }
1535
1536 fn createDebugLink(path: []const u8) DebugLink {
1537 const file = std.fs.cwd().openFile(path, .{}) catch |err| {
1538 fatal("zig objcopy: could not open `{s}`: {s}\n", .{ path, @errorName(err) });
1539 };
1540 defer file.close();
1541
1542 const crc = ElfFileHelper.computeFileCrc(file) catch |err| {
1543 fatal("zig objcopy: could not read `{s}`: {s}\n", .{ path, @errorName(err) });
1544 };
1545 return .{
1546 .name = std.fs.path.basename(path),
1547 .crc32 = crc,
1548 };
1549 }
1550
1551 fn computeFileCrc(file: File) !u32 {
1552 var buf: [8000]u8 = undefined;
1553
1554 try file.seekTo(0);
1555 var hasher = std.hash.Crc32.init();
1556 while (true) {
1557 const bytes_read = try file.read(&buf);
1558 if (bytes_read == 0) break;
1559 hasher.update(buf[0..bytes_read]);
1560 }
1561 return hasher.final();
1562 }
1563};
1564
1565const SectionFlags = packed struct {720const SectionFlags = packed struct {
1566 alloc: bool = false,721 alloc: bool = false,
1567 contents: bool = false,722 contents: bool = false,
lib/std/Build/Step/Run.zig+14-5
...@@ -169,7 +169,7 @@ pub const Output = struct {...@@ -169,7 +169,7 @@ pub const Output = struct {
169pub fn create(owner: *std.Build, name: []const u8) *Run {169pub fn create(owner: *std.Build, name: []const u8) *Run {
170 const run = owner.allocator.create(Run) catch @panic("OOM");170 const run = owner.allocator.create(Run) catch @panic("OOM");
171 run.* = .{171 run.* = .{
172 .step = Step.init(.{172 .step = .init(.{
173 .id = base_id,173 .id = base_id,
174 .name = name,174 .name = name,
175 .owner = owner,175 .owner = owner,
...@@ -1769,13 +1769,22 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult {...@@ -1769,13 +1769,22 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult {
1769 child.stdin = null;1769 child.stdin = null;
1770 },1770 },
1771 .lazy_path => |lazy_path| {1771 .lazy_path => |lazy_path| {
1772 const path = lazy_path.getPath2(b, &run.step);1772 const path = lazy_path.getPath3(b, &run.step);
1773 const file = b.build_root.handle.openFile(path, .{}) catch |err| {1773 const file = path.root_dir.handle.openFile(path.subPathOrDot(), .{}) catch |err| {
1774 return run.step.fail("unable to open stdin file: {s}", .{@errorName(err)});1774 return run.step.fail("unable to open stdin file: {s}", .{@errorName(err)});
1775 };1775 };
1776 defer file.close();1776 defer file.close();
1777 child.stdin.?.writeFileAll(file, .{}) catch |err| {1777 // TODO https://github.com/ziglang/zig/issues/23955
1778 return run.step.fail("unable to write file to stdin: {s}", .{@errorName(err)});1778 var buffer: [1024]u8 = undefined;
1779 var file_reader = file.reader(&buffer);
1780 var stdin_writer = child.stdin.?.writer(&.{});
1781 _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) {
1782 error.ReadFailed => return run.step.fail("failed to read from {f}: {t}", .{
1783 path, file_reader.err.?,
1784 }),
1785 error.WriteFailed => return run.step.fail("failed to write to stdin: {t}", .{
1786 stdin_writer.err.?,
1787 }),
1779 };1788 };
1780 child.stdin.?.close();1789 child.stdin.?.close();
1781 child.stdin = null;1790 child.stdin = null;
lib/std/c.zig+2-2
...@@ -10497,9 +10497,9 @@ pub const sysconf = switch (native_os) {...@@ -10497,9 +10497,9 @@ pub const sysconf = switch (native_os) {
1049710497
10498pub const sf_hdtr = switch (native_os) {10498pub const sf_hdtr = switch (native_os) {
10499 .freebsd, .macos, .ios, .tvos, .watchos, .visionos => extern struct {10499 .freebsd, .macos, .ios, .tvos, .watchos, .visionos => extern struct {
10500 headers: [*]const iovec_const,10500 headers: ?[*]const iovec_const,
10501 hdr_cnt: c_int,10501 hdr_cnt: c_int,
10502 trailers: [*]const iovec_const,10502 trailers: ?[*]const iovec_const,
10503 trl_cnt: c_int,10503 trl_cnt: c_int,
10504 },10504 },
10505 else => void,10505 else => void,
lib/std/elf.zig+102-171
...@@ -482,6 +482,7 @@ pub const Header = struct {...@@ -482,6 +482,7 @@ pub const Header = struct {
482 is_64: bool,482 is_64: bool,
483 endian: std.builtin.Endian,483 endian: std.builtin.Endian,
484 os_abi: OSABI,484 os_abi: OSABI,
485 /// The meaning of this value depends on `os_abi`.
485 abi_version: u8,486 abi_version: u8,
486 type: ET,487 type: ET,
487 machine: EM,488 machine: EM,
...@@ -494,205 +495,135 @@ pub const Header = struct {...@@ -494,205 +495,135 @@ pub const Header = struct {
494 shnum: u16,495 shnum: u16,
495 shstrndx: u16,496 shstrndx: u16,
496497
497 pub fn program_header_iterator(self: Header, parse_source: anytype) ProgramHeaderIterator(@TypeOf(parse_source)) {498 pub fn iterateProgramHeaders(h: Header, file_reader: *std.fs.File.Reader) ProgramHeaderIterator {
498 return ProgramHeaderIterator(@TypeOf(parse_source)){499 return .{
499 .elf_header = self,500 .elf_header = h,
500 .parse_source = parse_source,501 .file_reader = file_reader,
501 };502 };
502 }503 }
503504
504 pub fn section_header_iterator(self: Header, parse_source: anytype) SectionHeaderIterator(@TypeOf(parse_source)) {505 pub fn iterateSectionHeaders(h: Header, file_reader: *std.fs.File.Reader) SectionHeaderIterator {
505 return SectionHeaderIterator(@TypeOf(parse_source)){506 return .{
506 .elf_header = self,507 .elf_header = h,
507 .parse_source = parse_source,508 .file_reader = file_reader,
508 };509 };
509 }510 }
510511
511 pub fn read(parse_source: anytype) !Header {512 pub const ReadError = std.Io.Reader.Error || error{
512 var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined;513 InvalidElfMagic,
513 try parse_source.seekableStream().seekTo(0);514 InvalidElfVersion,
514 try parse_source.deprecatedReader().readNoEof(&hdr_buf);515 InvalidElfClass,
515 return Header.parse(&hdr_buf);516 InvalidElfEndian,
516 }517 };
517518
518 pub fn parse(hdr_buf: *align(@alignOf(Elf64_Ehdr)) const [@sizeOf(Elf64_Ehdr)]u8) !Header {519 pub fn read(r: *std.Io.Reader) ReadError!Header {
519 const hdr32 = @as(*const Elf32_Ehdr, @ptrCast(hdr_buf));520 const buf = try r.peek(@sizeOf(Elf64_Ehdr));
520 const hdr64 = @as(*const Elf64_Ehdr, @ptrCast(hdr_buf));
521 if (!mem.eql(u8, hdr32.e_ident[0..4], MAGIC)) return error.InvalidElfMagic;
522 if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion;
523521
524 const is_64 = switch (hdr32.e_ident[EI_CLASS]) {522 if (!mem.eql(u8, buf[0..4], MAGIC)) return error.InvalidElfMagic;
525 ELFCLASS32 => false,523 if (buf[EI_VERSION] != 1) return error.InvalidElfVersion;
526 ELFCLASS64 => true,
527 else => return error.InvalidElfClass,
528 };
529524
530 const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) {525 const endian: std.builtin.Endian = switch (buf[EI_DATA]) {
531 ELFDATA2LSB => .little,526 ELFDATA2LSB => .little,
532 ELFDATA2MSB => .big,527 ELFDATA2MSB => .big,
533 else => return error.InvalidElfEndian,528 else => return error.InvalidElfEndian,
534 };529 };
535 const need_bswap = endian != native_endian;
536530
531 return switch (buf[EI_CLASS]) {
532 ELFCLASS32 => .init(try r.takeStruct(Elf32_Ehdr, endian), endian),
533 ELFCLASS64 => .init(try r.takeStruct(Elf64_Ehdr, endian), endian),
534 else => return error.InvalidElfClass,
535 };
536 }
537
538 pub fn init(hdr: anytype, endian: std.builtin.Endian) Header {
537 // Converting integers to exhaustive enums using `@enumFromInt` could cause a panic.539 // Converting integers to exhaustive enums using `@enumFromInt` could cause a panic.
538 comptime assert(!@typeInfo(OSABI).@"enum".is_exhaustive);540 comptime assert(!@typeInfo(OSABI).@"enum".is_exhaustive);
539 const os_abi: OSABI = @enumFromInt(hdr32.e_ident[EI_OSABI]);541 return .{
542 .is_64 = switch (@TypeOf(hdr)) {
543 Elf32_Ehdr => false,
544 Elf64_Ehdr => true,
545 else => @compileError("bad type"),
546 },
547 .endian = endian,
548 .os_abi = @enumFromInt(hdr.e_ident[EI_OSABI]),
549 .abi_version = hdr.e_ident[EI_ABIVERSION],
550 .type = hdr.e_type,
551 .machine = hdr.e_machine,
552 .entry = hdr.e_entry,
553 .phoff = hdr.e_phoff,
554 .shoff = hdr.e_shoff,
555 .phentsize = hdr.e_phentsize,
556 .phnum = hdr.e_phnum,
557 .shentsize = hdr.e_shentsize,
558 .shnum = hdr.e_shnum,
559 .shstrndx = hdr.e_shstrndx,
560 };
561 }
562};
540563
541 // The meaning of this value depends on `os_abi` so just make it available as `u8`.564pub const ProgramHeaderIterator = struct {
542 const abi_version = hdr32.e_ident[EI_ABIVERSION];565 elf_header: Header,
566 file_reader: *std.fs.File.Reader,
567 index: usize = 0,
543568
544 const @"type" = if (need_bswap) blk: {569 pub fn next(it: *ProgramHeaderIterator) !?Elf64_Phdr {
545 comptime assert(!@typeInfo(ET).@"enum".is_exhaustive);570 if (it.index >= it.elf_header.phnum) return null;
546 const value = @intFromEnum(hdr32.e_type);571 defer it.index += 1;
547 break :blk @as(ET, @enumFromInt(@byteSwap(value)));
548 } else hdr32.e_type;
549572
550 const machine = if (need_bswap) blk: {573 if (it.elf_header.is_64) {
551 comptime assert(!@typeInfo(EM).@"enum".is_exhaustive);574 const offset = it.elf_header.phoff + @sizeOf(Elf64_Phdr) * it.index;
552 const value = @intFromEnum(hdr32.e_machine);575 try it.file_reader.seekTo(offset);
553 break :blk @as(EM, @enumFromInt(@byteSwap(value)));576 const phdr = try it.file_reader.interface.takeStruct(Elf64_Phdr, it.elf_header.endian);
554 } else hdr32.e_machine;577 return phdr;
578 }
555579
556 return @as(Header, .{580 const offset = it.elf_header.phoff + @sizeOf(Elf32_Phdr) * it.index;
557 .is_64 = is_64,581 try it.file_reader.seekTo(offset);
558 .endian = endian,582 const phdr = try it.file_reader.interface.takeStruct(Elf32_Phdr, it.elf_header.endian);
559 .os_abi = os_abi,583 return .{
560 .abi_version = abi_version,584 .p_type = phdr.p_type,
561 .type = @"type",585 .p_offset = phdr.p_offset,
562 .machine = machine,586 .p_vaddr = phdr.p_vaddr,
563 .entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry),587 .p_paddr = phdr.p_paddr,
564 .phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff),588 .p_filesz = phdr.p_filesz,
565 .shoff = int(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff),589 .p_memsz = phdr.p_memsz,
566 .phentsize = int(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize),590 .p_flags = phdr.p_flags,
567 .phnum = int(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum),591 .p_align = phdr.p_align,
568 .shentsize = int(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize),592 };
569 .shnum = int(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum),
570 .shstrndx = int(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx),
571 });
572 }593 }
573};594};
574595
575pub fn ProgramHeaderIterator(comptime ParseSource: anytype) type {596pub const SectionHeaderIterator = struct {
576 return struct {597 elf_header: Header,
577 elf_header: Header,598 file_reader: *std.fs.File.Reader,
578 parse_source: ParseSource,599 index: usize = 0,
579 index: usize = 0,
580
581 pub fn next(self: *@This()) !?Elf64_Phdr {
582 if (self.index >= self.elf_header.phnum) return null;
583 defer self.index += 1;
584
585 if (self.elf_header.is_64) {
586 var phdr: Elf64_Phdr = undefined;
587 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
588 try self.parse_source.seekableStream().seekTo(offset);
589 try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&phdr));
590
591 // ELF endianness matches native endianness.
592 if (self.elf_header.endian == native_endian) return phdr;
593
594 // Convert fields to native endianness.
595 mem.byteSwapAllFields(Elf64_Phdr, &phdr);
596 return phdr;
597 }
598
599 var phdr: Elf32_Phdr = undefined;
600 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
601 try self.parse_source.seekableStream().seekTo(offset);
602 try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&phdr));
603
604 // ELF endianness does NOT match native endianness.
605 if (self.elf_header.endian != native_endian) {
606 // Convert fields to native endianness.
607 mem.byteSwapAllFields(Elf32_Phdr, &phdr);
608 }
609
610 // Convert 32-bit header to 64-bit.
611 return Elf64_Phdr{
612 .p_type = phdr.p_type,
613 .p_offset = phdr.p_offset,
614 .p_vaddr = phdr.p_vaddr,
615 .p_paddr = phdr.p_paddr,
616 .p_filesz = phdr.p_filesz,
617 .p_memsz = phdr.p_memsz,
618 .p_flags = phdr.p_flags,
619 .p_align = phdr.p_align,
620 };
621 }
622 };
623}
624600
625pub fn SectionHeaderIterator(comptime ParseSource: anytype) type {601 pub fn next(it: *SectionHeaderIterator) !?Elf64_Shdr {
626 return struct {602 if (it.index >= it.elf_header.shnum) return null;
627 elf_header: Header,603 defer it.index += 1;
628 parse_source: ParseSource,
629 index: usize = 0,
630
631 pub fn next(self: *@This()) !?Elf64_Shdr {
632 if (self.index >= self.elf_header.shnum) return null;
633 defer self.index += 1;
634
635 if (self.elf_header.is_64) {
636 var shdr: Elf64_Shdr = undefined;
637 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
638 try self.parse_source.seekableStream().seekTo(offset);
639 try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&shdr));
640
641 // ELF endianness matches native endianness.
642 if (self.elf_header.endian == native_endian) return shdr;
643
644 // Convert fields to native endianness.
645 mem.byteSwapAllFields(Elf64_Shdr, &shdr);
646 return shdr;
647 }
648
649 var shdr: Elf32_Shdr = undefined;
650 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
651 try self.parse_source.seekableStream().seekTo(offset);
652 try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&shdr));
653
654 // ELF endianness does NOT match native endianness.
655 if (self.elf_header.endian != native_endian) {
656 // Convert fields to native endianness.
657 mem.byteSwapAllFields(Elf32_Shdr, &shdr);
658 }
659
660 // Convert 32-bit header to 64-bit.
661 return Elf64_Shdr{
662 .sh_name = shdr.sh_name,
663 .sh_type = shdr.sh_type,
664 .sh_flags = shdr.sh_flags,
665 .sh_addr = shdr.sh_addr,
666 .sh_offset = shdr.sh_offset,
667 .sh_size = shdr.sh_size,
668 .sh_link = shdr.sh_link,
669 .sh_info = shdr.sh_info,
670 .sh_addralign = shdr.sh_addralign,
671 .sh_entsize = shdr.sh_entsize,
672 };
673 }
674 };
675}
676604
677fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {605 if (it.elf_header.is_64) {
678 if (is_64) {606 try it.file_reader.seekTo(it.elf_header.shoff + @sizeOf(Elf64_Shdr) * it.index);
679 if (need_bswap) {607 const shdr = try it.file_reader.interface.takeStruct(Elf64_Shdr, it.elf_header.endian);
680 return @byteSwap(int_64);608 return shdr;
681 } else {
682 return int_64;
683 }609 }
684 } else {
685 return int32(need_bswap, int_32, @TypeOf(int_64));
686 }
687}
688610
689fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 {611 try it.file_reader.seekTo(it.elf_header.shoff + @sizeOf(Elf32_Shdr) * it.index);
690 if (need_bswap) {612 const shdr = try it.file_reader.interface.takeStruct(Elf32_Shdr, it.elf_header.endian);
691 return @byteSwap(int_32);613 return .{
692 } else {614 .sh_name = shdr.sh_name,
693 return int_32;615 .sh_type = shdr.sh_type,
616 .sh_flags = shdr.sh_flags,
617 .sh_addr = shdr.sh_addr,
618 .sh_offset = shdr.sh_offset,
619 .sh_size = shdr.sh_size,
620 .sh_link = shdr.sh_link,
621 .sh_info = shdr.sh_info,
622 .sh_addralign = shdr.sh_addralign,
623 .sh_entsize = shdr.sh_entsize,
624 };
694 }625 }
695}626};
696627
697pub const ELFCLASSNONE = 0;628pub const ELFCLASSNONE = 0;
698pub const ELFCLASS32 = 1;629pub const ELFCLASS32 = 1;
lib/std/fs/AtomicFile.zig+52-46
...@@ -1,6 +1,13 @@...@@ -1,6 +1,13 @@
1file: File,1const AtomicFile = @This();
2// TODO either replace this with rand_buf or use []u16 on Windows2const std = @import("../std.zig");
3tmp_path_buf: [tmp_path_len:0]u8,3const File = std.fs.File;
4const Dir = std.fs.Dir;
5const fs = std.fs;
6const assert = std.debug.assert;
7const posix = std.posix;
8
9file_writer: File.Writer,
10random_integer: u64,
4dest_basename: []const u8,11dest_basename: []const u8,
5file_open: bool,12file_open: bool,
6file_exists: bool,13file_exists: bool,
...@@ -9,35 +16,24 @@ dir: Dir,...@@ -9,35 +16,24 @@ dir: Dir,
916
10pub const InitError = File.OpenError;17pub const InitError = File.OpenError;
1118
12pub const random_bytes_len = 12;
13const tmp_path_len = fs.base64_encoder.calcSize(random_bytes_len);
14
15/// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.19/// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.
16pub fn init(20pub fn init(
17 dest_basename: []const u8,21 dest_basename: []const u8,
18 mode: File.Mode,22 mode: File.Mode,
19 dir: Dir,23 dir: Dir,
20 close_dir_on_deinit: bool,24 close_dir_on_deinit: bool,
25 write_buffer: []u8,
21) InitError!AtomicFile {26) InitError!AtomicFile {
22 var rand_buf: [random_bytes_len]u8 = undefined;
23 var tmp_path_buf: [tmp_path_len:0]u8 = undefined;
24
25 while (true) {27 while (true) {
26 std.crypto.random.bytes(rand_buf[0..]);28 const random_integer = std.crypto.random.int(u64);
27 const tmp_path = fs.base64_encoder.encode(&tmp_path_buf, &rand_buf);29 const tmp_sub_path = std.fmt.hex(random_integer);
28 tmp_path_buf[tmp_path.len] = 0;30 const file = dir.createFile(&tmp_sub_path, .{ .mode = mode, .exclusive = true }) catch |err| switch (err) {
29
30 const file = dir.createFile(
31 tmp_path,
32 .{ .mode = mode, .exclusive = true },
33 ) catch |err| switch (err) {
34 error.PathAlreadyExists => continue,31 error.PathAlreadyExists => continue,
35 else => |e| return e,32 else => |e| return e,
36 };33 };
3734 return .{
38 return AtomicFile{35 .file_writer = file.writer(write_buffer),
39 .file = file,36 .random_integer = random_integer,
40 .tmp_path_buf = tmp_path_buf,
41 .dest_basename = dest_basename,37 .dest_basename = dest_basename,
42 .file_open = true,38 .file_open = true,
43 .file_exists = true,39 .file_exists = true,
...@@ -48,41 +44,51 @@ pub fn init(...@@ -48,41 +44,51 @@ pub fn init(
48}44}
4945
50/// Always call deinit, even after a successful finish().46/// Always call deinit, even after a successful finish().
51pub fn deinit(self: *AtomicFile) void {47pub fn deinit(af: *AtomicFile) void {
52 if (self.file_open) {48 if (af.file_open) {
53 self.file.close();49 af.file_writer.file.close();
54 self.file_open = false;50 af.file_open = false;
55 }51 }
56 if (self.file_exists) {52 if (af.file_exists) {
57 self.dir.deleteFile(&self.tmp_path_buf) catch {};53 const tmp_sub_path = std.fmt.hex(af.random_integer);
58 self.file_exists = false;54 af.dir.deleteFile(&tmp_sub_path) catch {};
55 af.file_exists = false;
59 }56 }
60 if (self.close_dir_on_deinit) {57 if (af.close_dir_on_deinit) {
61 self.dir.close();58 af.dir.close();
62 }59 }
63 self.* = undefined;60 af.* = undefined;
64}61}
6562
66pub const FinishError = posix.RenameError;63pub const FlushError = File.WriteError;
64
65pub fn flush(af: *AtomicFile) FlushError!void {
66 af.file_writer.interface.flush() catch |err| switch (err) {
67 error.WriteFailed => return af.file_writer.err.?,
68 };
69}
70
71pub const RenameIntoPlaceError = posix.RenameError;
6772
68/// On Windows, this function introduces a period of time where some file73/// On Windows, this function introduces a period of time where some file
69/// system operations on the destination file will result in74/// system operations on the destination file will result in
70/// `error.AccessDenied`, including rename operations (such as the one used in75/// `error.AccessDenied`, including rename operations (such as the one used in
71/// this function).76/// this function).
72pub fn finish(self: *AtomicFile) FinishError!void {77pub fn renameIntoPlace(af: *AtomicFile) RenameIntoPlaceError!void {
73 assert(self.file_exists);78 assert(af.file_exists);
74 if (self.file_open) {79 if (af.file_open) {
75 self.file.close();80 af.file_writer.file.close();
76 self.file_open = false;81 af.file_open = false;
77 }82 }
78 try posix.renameat(self.dir.fd, self.tmp_path_buf[0..], self.dir.fd, self.dest_basename);83 const tmp_sub_path = std.fmt.hex(af.random_integer);
79 self.file_exists = false;84 try posix.renameat(af.dir.fd, &tmp_sub_path, af.dir.fd, af.dest_basename);
85 af.file_exists = false;
80}86}
8187
82const AtomicFile = @This();88pub const FinishError = FlushError || RenameIntoPlaceError;
83const std = @import("../std.zig");89
84const File = std.fs.File;90/// Combination of `flush` followed by `renameIntoPlace`.
85const Dir = std.fs.Dir;91pub fn finish(af: *AtomicFile) FinishError!void {
86const fs = std.fs;92 try af.flush();
87const assert = std.debug.assert;93 try af.renameIntoPlace();
88const posix = std.posix;94}
lib/std/fs/Dir.zig+71-109
...@@ -1,3 +1,20 @@...@@ -1,3 +1,20 @@
1const Dir = @This();
2const builtin = @import("builtin");
3const std = @import("../std.zig");
4const File = std.fs.File;
5const AtomicFile = std.fs.AtomicFile;
6const base64_encoder = fs.base64_encoder;
7const posix = std.posix;
8const mem = std.mem;
9const path = fs.path;
10const fs = std.fs;
11const Allocator = std.mem.Allocator;
12const assert = std.debug.assert;
13const linux = std.os.linux;
14const windows = std.os.windows;
15const native_os = builtin.os.tag;
16const have_flock = @TypeOf(posix.system.flock) != void;
17
1fd: Handle,18fd: Handle,
219
3pub const Handle = posix.fd_t;20pub const Handle = posix.fd_t;
...@@ -1862,9 +1879,10 @@ pub fn symLinkW(...@@ -1862,9 +1879,10 @@ pub fn symLinkW(
18621879
1863/// Same as `symLink`, except tries to create the symbolic link until it1880/// Same as `symLink`, except tries to create the symbolic link until it
1864/// succeeds or encounters an error other than `error.PathAlreadyExists`.1881/// succeeds or encounters an error other than `error.PathAlreadyExists`.
1865/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).1882///
1866/// On WASI, both paths should be encoded as valid UTF-8.1883/// * On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1867/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.1884/// * On WASI, both paths should be encoded as valid UTF-8.
1885/// * On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
1868pub fn atomicSymLink(1886pub fn atomicSymLink(
1869 dir: Dir,1887 dir: Dir,
1870 target_path: []const u8,1888 target_path: []const u8,
...@@ -1880,9 +1898,8 @@ pub fn atomicSymLink(...@@ -1880,9 +1898,8 @@ pub fn atomicSymLink(
18801898
1881 const dirname = path.dirname(sym_link_path) orelse ".";1899 const dirname = path.dirname(sym_link_path) orelse ".";
18821900
1883 var rand_buf: [AtomicFile.random_bytes_len]u8 = undefined;1901 const rand_len = @sizeOf(u64) * 2;
18841902 const temp_path_len = dirname.len + 1 + rand_len;
1885 const temp_path_len = dirname.len + 1 + base64_encoder.calcSize(rand_buf.len);
1886 var temp_path_buf: [fs.max_path_bytes]u8 = undefined;1903 var temp_path_buf: [fs.max_path_bytes]u8 = undefined;
18871904
1888 if (temp_path_len > temp_path_buf.len) return error.NameTooLong;1905 if (temp_path_len > temp_path_buf.len) return error.NameTooLong;
...@@ -1892,8 +1909,8 @@ pub fn atomicSymLink(...@@ -1892,8 +1909,8 @@ pub fn atomicSymLink(
1892 const temp_path = temp_path_buf[0..temp_path_len];1909 const temp_path = temp_path_buf[0..temp_path_len];
18931910
1894 while (true) {1911 while (true) {
1895 crypto.random.bytes(rand_buf[0..]);1912 const random_integer = std.crypto.random.int(u64);
1896 _ = base64_encoder.encode(temp_path[dirname.len + 1 ..], rand_buf[0..]);1913 temp_path[dirname.len + 1 ..][0..rand_len].* = std.fmt.hex(random_integer);
18971914
1898 if (dir.symLink(target_path, temp_path, flags)) {1915 if (dir.symLink(target_path, temp_path, flags)) {
1899 return dir.rename(temp_path, sym_link_path);1916 return dir.rename(temp_path, sym_link_path);
...@@ -2552,25 +2569,42 @@ pub fn updateFile(...@@ -2552,25 +2569,42 @@ pub fn updateFile(
2552 try dest_dir.makePath(dirname);2569 try dest_dir.makePath(dirname);
2553 }2570 }
25542571
2555 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode });2572 var buffer: [1000]u8 = undefined; // Used only when direct fd-to-fd is not available.
2573 var atomic_file = try dest_dir.atomicFile(dest_path, .{
2574 .mode = actual_mode,
2575 .write_buffer = &buffer,
2576 });
2556 defer atomic_file.deinit();2577 defer atomic_file.deinit();
25572578
2558 try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size });2579 var src_reader: File.Reader = .initSize(src_file, &.{}, src_stat.size);
2559 try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);2580 const dest_writer = &atomic_file.file_writer.interface;
2581
2582 _ = dest_writer.sendFileAll(&src_reader, .unlimited) catch |err| switch (err) {
2583 error.ReadFailed => return src_reader.err.?,
2584 error.WriteFailed => return atomic_file.file_writer.err.?,
2585 };
2586 try atomic_file.file_writer.file.updateTimes(src_stat.atime, src_stat.mtime);
2560 try atomic_file.finish();2587 try atomic_file.finish();
2561 return PrevStatus.stale;2588 return .stale;
2562}2589}
25632590
2564pub const CopyFileError = File.OpenError || File.StatError ||2591pub const CopyFileError = File.OpenError || File.StatError ||
2565 AtomicFile.InitError || CopyFileRawError || AtomicFile.FinishError;2592 AtomicFile.InitError || AtomicFile.FinishError ||
2593 File.ReadError || File.WriteError;
25662594
2567/// Guaranteed to be atomic.2595/// Atomically creates a new file at `dest_path` within `dest_dir` with the
2568/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,2596/// same contents as `source_path` within `source_dir`, overwriting any already
2569/// there is a possibility of power loss or application termination leaving temporary files present2597/// existing file.
2570/// in the same directory as dest_path.2598///
2571/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).2599/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and
2572/// On WASI, both paths should be encoded as valid UTF-8.2600/// readily available, there is a possibility of power loss or application
2573/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.2601/// termination leaving temporary files present in the same directory as
2602/// dest_path.
2603///
2604/// On Windows, both paths should be encoded as
2605/// [WTF-8](https://simonsapin.github.io/wtf-8/). On WASI, both paths should be
2606/// encoded as valid UTF-8. On other platforms, both paths are an opaque
2607/// sequence of bytes with no particular encoding.
2574pub fn copyFile(2608pub fn copyFile(
2575 source_dir: Dir,2609 source_dir: Dir,
2576 source_path: []const u8,2610 source_path: []const u8,
...@@ -2578,79 +2612,34 @@ pub fn copyFile(...@@ -2578,79 +2612,34 @@ pub fn copyFile(
2578 dest_path: []const u8,2612 dest_path: []const u8,
2579 options: CopyFileOptions,2613 options: CopyFileOptions,
2580) CopyFileError!void {2614) CopyFileError!void {
2581 var in_file = try source_dir.openFile(source_path, .{});2615 var file_reader: File.Reader = .init(try source_dir.openFile(source_path, .{}), &.{});
2582 defer in_file.close();2616 defer file_reader.file.close();
25832617
2584 var size: ?u64 = null;
2585 const mode = options.override_mode orelse blk: {2618 const mode = options.override_mode orelse blk: {
2586 const st = try in_file.stat();2619 const st = try file_reader.file.stat();
2587 size = st.size;2620 file_reader.size = st.size;
2588 break :blk st.mode;2621 break :blk st.mode;
2589 };2622 };
25902623
2591 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });2624 var buffer: [1024]u8 = undefined; // Used only when direct fd-to-fd is not available.
2625 var atomic_file = try dest_dir.atomicFile(dest_path, .{
2626 .mode = mode,
2627 .write_buffer = &buffer,
2628 });
2592 defer atomic_file.deinit();2629 defer atomic_file.deinit();
25932630
2594 try copy_file(in_file.handle, atomic_file.file.handle, size);2631 _ = atomic_file.file_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) {
2595 try atomic_file.finish();2632 error.ReadFailed => return file_reader.err.?,
2596}2633 error.WriteFailed => return atomic_file.file_writer.err.?,
25972634 };
2598const CopyFileRawError = error{SystemResources} || posix.CopyFileRangeError || posix.SendFileError;
2599
2600// Transfer all the data between two file descriptors in the most efficient way.
2601// The copy starts at offset 0, the initial offsets are preserved.
2602// No metadata is transferred over.
2603fn copy_file(fd_in: posix.fd_t, fd_out: posix.fd_t, maybe_size: ?u64) CopyFileRawError!void {
2604 if (builtin.target.os.tag.isDarwin()) {
2605 const rc = posix.system.fcopyfile(fd_in, fd_out, null, .{ .DATA = true });
2606 switch (posix.errno(rc)) {
2607 .SUCCESS => return,
2608 .INVAL => unreachable,
2609 .NOMEM => return error.SystemResources,
2610 // The source file is not a directory, symbolic link, or regular file.
2611 // Try with the fallback path before giving up.
2612 .OPNOTSUPP => {},
2613 else => |err| return posix.unexpectedErrno(err),
2614 }
2615 }
2616
2617 if (native_os == .linux) {
2618 // Try copy_file_range first as that works at the FS level and is the
2619 // most efficient method (if available).
2620 var offset: u64 = 0;
2621 cfr_loop: while (true) {
2622 // The kernel checks the u64 value `offset+count` for overflow, use
2623 // a 32 bit value so that the syscall won't return EINVAL except for
2624 // impossibly large files (> 2^64-1 - 2^32-1).
2625 const amt = try posix.copy_file_range(fd_in, offset, fd_out, offset, std.math.maxInt(u32), 0);
2626 // Terminate as soon as we have copied size bytes or no bytes
2627 if (maybe_size) |s| {
2628 if (s == amt) break :cfr_loop;
2629 }
2630 if (amt == 0) break :cfr_loop;
2631 offset += amt;
2632 }
2633 return;
2634 }
26352635
2636 // Sendfile is a zero-copy mechanism iff the OS supports it, otherwise the2636 try atomic_file.finish();
2637 // fallback code will copy the contents chunk by chunk.
2638 const empty_iovec = [0]posix.iovec_const{};
2639 var offset: u64 = 0;
2640 sendfile_loop: while (true) {
2641 const amt = try posix.sendfile(fd_out, fd_in, offset, 0, &empty_iovec, &empty_iovec, 0);
2642 // Terminate as soon as we have copied size bytes or no bytes
2643 if (maybe_size) |s| {
2644 if (s == amt) break :sendfile_loop;
2645 }
2646 if (amt == 0) break :sendfile_loop;
2647 offset += amt;
2648 }
2649}2637}
26502638
2651pub const AtomicFileOptions = struct {2639pub const AtomicFileOptions = struct {
2652 mode: File.Mode = File.default_mode,2640 mode: File.Mode = File.default_mode,
2653 make_path: bool = false,2641 make_path: bool = false,
2642 write_buffer: []u8,
2654};2643};
26552644
2656/// Directly access the `.file` field, and then call `AtomicFile.finish` to2645/// Directly access the `.file` field, and then call `AtomicFile.finish` to
...@@ -2668,9 +2657,9 @@ pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions)...@@ -2668,9 +2657,9 @@ pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions)
2668 else2657 else
2669 try self.openDir(dirname, .{});2658 try self.openDir(dirname, .{});
26702659
2671 return AtomicFile.init(fs.path.basename(dest_path), options.mode, dir, true);2660 return .init(fs.path.basename(dest_path), options.mode, dir, true, options.write_buffer);
2672 } else {2661 } else {
2673 return AtomicFile.init(dest_path, options.mode, self, false);2662 return .init(dest_path, options.mode, self, false, options.write_buffer);
2674 }2663 }
2675}2664}
26762665
...@@ -2768,30 +2757,3 @@ pub fn setPermissions(self: Dir, permissions: Permissions) SetPermissionsError!v...@@ -2768,30 +2757,3 @@ pub fn setPermissions(self: Dir, permissions: Permissions) SetPermissionsError!v
2768 const file: File = .{ .handle = self.fd };2757 const file: File = .{ .handle = self.fd };
2769 try file.setPermissions(permissions);2758 try file.setPermissions(permissions);
2770}2759}
2771
2772const Metadata = File.Metadata;
2773pub const MetadataError = File.MetadataError;
2774
2775/// Returns a `Metadata` struct, representing the permissions on the directory
2776pub fn metadata(self: Dir) MetadataError!Metadata {
2777 const file: File = .{ .handle = self.fd };
2778 return try file.metadata();
2779}
2780
2781const Dir = @This();
2782const builtin = @import("builtin");
2783const std = @import("../std.zig");
2784const File = std.fs.File;
2785const AtomicFile = std.fs.AtomicFile;
2786const base64_encoder = fs.base64_encoder;
2787const crypto = std.crypto;
2788const posix = std.posix;
2789const mem = std.mem;
2790const path = fs.path;
2791const fs = std.fs;
2792const Allocator = std.mem.Allocator;
2793const assert = std.debug.assert;
2794const linux = std.os.linux;
2795const windows = std.os.windows;
2796const native_os = builtin.os.tag;
2797const have_flock = @TypeOf(posix.system.flock) != void;
lib/std/fs/File.zig+171-127
...@@ -1089,113 +1089,6 @@ pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: u...@@ -1089,113 +1089,6 @@ pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: u
1089 return total_bytes_copied;1089 return total_bytes_copied;
1090}1090}
10911091
1092/// Deprecated in favor of `Writer`.
1093pub const WriteFileOptions = struct {
1094 in_offset: u64 = 0,
1095 in_len: ?u64 = null,
1096 headers_and_trailers: []posix.iovec_const = &[0]posix.iovec_const{},
1097 header_count: usize = 0,
1098};
1099
1100/// Deprecated in favor of `Writer`.
1101pub const WriteFileError = ReadError || error{EndOfStream} || WriteError;
1102
1103/// Deprecated in favor of `Writer`.
1104pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void {
1105 return self.writeFileAllSendfile(in_file, args) catch |err| switch (err) {
1106 error.Unseekable,
1107 error.FastOpenAlreadyInProgress,
1108 error.MessageTooBig,
1109 error.FileDescriptorNotASocket,
1110 error.NetworkUnreachable,
1111 error.NetworkSubsystemFailed,
1112 error.ConnectionRefused,
1113 => return self.writeFileAllUnseekable(in_file, args),
1114 else => |e| return e,
1115 };
1116}
1117
1118/// Deprecated in favor of `Writer`.
1119pub fn writeFileAllUnseekable(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void {
1120 const headers = args.headers_and_trailers[0..args.header_count];
1121 const trailers = args.headers_and_trailers[args.header_count..];
1122 try self.writevAll(headers);
1123 try in_file.deprecatedReader().skipBytes(args.in_offset, .{ .buf_size = 4096 });
1124 var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init();
1125 if (args.in_len) |len| {
1126 var stream = std.io.limitedReader(in_file.deprecatedReader(), len);
1127 try fifo.pump(stream.reader(), self.deprecatedWriter());
1128 } else {
1129 try fifo.pump(in_file.deprecatedReader(), self.deprecatedWriter());
1130 }
1131 try self.writevAll(trailers);
1132}
1133
1134/// Deprecated in favor of `Writer`.
1135fn writeFileAllSendfile(self: File, in_file: File, args: WriteFileOptions) posix.SendFileError!void {
1136 const count = blk: {
1137 if (args.in_len) |l| {
1138 if (l == 0) {
1139 return self.writevAll(args.headers_and_trailers);
1140 } else {
1141 break :blk l;
1142 }
1143 } else {
1144 break :blk 0;
1145 }
1146 };
1147 const headers = args.headers_and_trailers[0..args.header_count];
1148 const trailers = args.headers_and_trailers[args.header_count..];
1149 const zero_iovec = &[0]posix.iovec_const{};
1150 // When reading the whole file, we cannot put the trailers in the sendfile() syscall,
1151 // because we have no way to determine whether a partial write is past the end of the file or not.
1152 const trls = if (count == 0) zero_iovec else trailers;
1153 const offset = args.in_offset;
1154 const out_fd = self.handle;
1155 const in_fd = in_file.handle;
1156 const flags = 0;
1157 var amt: usize = 0;
1158 hdrs: {
1159 var i: usize = 0;
1160 while (i < headers.len) {
1161 amt = try posix.sendfile(out_fd, in_fd, offset, count, headers[i..], trls, flags);
1162 while (amt >= headers[i].len) {
1163 amt -= headers[i].len;
1164 i += 1;
1165 if (i >= headers.len) break :hdrs;
1166 }
1167 headers[i].base += amt;
1168 headers[i].len -= amt;
1169 }
1170 }
1171 if (count == 0) {
1172 var off: u64 = amt;
1173 while (true) {
1174 amt = try posix.sendfile(out_fd, in_fd, offset + off, 0, zero_iovec, zero_iovec, flags);
1175 if (amt == 0) break;
1176 off += amt;
1177 }
1178 } else {
1179 var off: u64 = amt;
1180 while (off < count) {
1181 amt = try posix.sendfile(out_fd, in_fd, offset + off, count - off, zero_iovec, trailers, flags);
1182 off += amt;
1183 }
1184 amt = @as(usize, @intCast(off - count));
1185 }
1186 var i: usize = 0;
1187 while (i < trailers.len) {
1188 while (amt >= trailers[i].len) {
1189 amt -= trailers[i].len;
1190 i += 1;
1191 if (i >= trailers.len) return;
1192 }
1193 trailers[i].base += amt;
1194 trailers[i].len -= amt;
1195 amt = try posix.writev(self.handle, trailers[i..]);
1196 }
1197}
1198
1199/// Deprecated in favor of `Reader`.1092/// Deprecated in favor of `Reader`.
1200pub const DeprecatedReader = io.GenericReader(File, ReadError, read);1093pub const DeprecatedReader = io.GenericReader(File, ReadError, read);
12011094
...@@ -1242,7 +1135,7 @@ pub const Reader = struct {...@@ -1242,7 +1135,7 @@ pub const Reader = struct {
1242 err: ?ReadError = null,1135 err: ?ReadError = null,
1243 mode: Reader.Mode = .positional,1136 mode: Reader.Mode = .positional,
1244 /// Tracks the true seek position in the file. To obtain the logical1137 /// Tracks the true seek position in the file. To obtain the logical
1245 /// position, subtract the buffer size from this value.1138 /// position, use `logicalPos`.
1246 pos: u64 = 0,1139 pos: u64 = 0,
1247 size: ?u64 = null,1140 size: ?u64 = null,
1248 size_err: ?GetEndPosError = null,1141 size_err: ?GetEndPosError = null,
...@@ -1335,14 +1228,12 @@ pub const Reader = struct {...@@ -1335,14 +1228,12 @@ pub const Reader = struct {
1335 pub fn seekBy(r: *Reader, offset: i64) Reader.SeekError!void {1228 pub fn seekBy(r: *Reader, offset: i64) Reader.SeekError!void {
1336 switch (r.mode) {1229 switch (r.mode) {
1337 .positional, .positional_reading => {1230 .positional, .positional_reading => {
1338 // TODO: make += operator allow any integer types1231 setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset));
1339 r.pos = @intCast(@as(i64, @intCast(r.pos)) + offset);
1340 },1232 },
1341 .streaming, .streaming_reading => {1233 .streaming, .streaming_reading => {
1342 const seek_err = r.seek_err orelse e: {1234 const seek_err = r.seek_err orelse e: {
1343 if (posix.lseek_CUR(r.file.handle, offset)) |_| {1235 if (posix.lseek_CUR(r.file.handle, offset)) |_| {
1344 // TODO: make += operator allow any integer types1236 setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset));
1345 r.pos = @intCast(@as(i64, @intCast(r.pos)) + offset);
1346 return;1237 return;
1347 } else |err| {1238 } else |err| {
1348 r.seek_err = err;1239 r.seek_err = err;
...@@ -1358,6 +1249,8 @@ pub const Reader = struct {...@@ -1358,6 +1249,8 @@ pub const Reader = struct {
1358 r.pos += n;1249 r.pos += n;
1359 remaining -= n;1250 remaining -= n;
1360 }1251 }
1252 r.interface.seek = 0;
1253 r.interface.end = 0;
1361 },1254 },
1362 .failure => return r.seek_err.?,1255 .failure => return r.seek_err.?,
1363 }1256 }
...@@ -1366,7 +1259,7 @@ pub const Reader = struct {...@@ -1366,7 +1259,7 @@ pub const Reader = struct {
1366 pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void {1259 pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void {
1367 switch (r.mode) {1260 switch (r.mode) {
1368 .positional, .positional_reading => {1261 .positional, .positional_reading => {
1369 r.pos = offset;1262 setPosAdjustingBuffer(r, offset);
1370 },1263 },
1371 .streaming, .streaming_reading => {1264 .streaming, .streaming_reading => {
1372 if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - r.pos));1265 if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - r.pos));
...@@ -1375,12 +1268,28 @@ pub const Reader = struct {...@@ -1375,12 +1268,28 @@ pub const Reader = struct {
1375 r.seek_err = err;1268 r.seek_err = err;
1376 return err;1269 return err;
1377 };1270 };
1378 r.pos = offset;1271 setPosAdjustingBuffer(r, offset);
1379 },1272 },
1380 .failure => return r.seek_err.?,1273 .failure => return r.seek_err.?,
1381 }1274 }
1382 }1275 }
13831276
1277 pub fn logicalPos(r: *const Reader) u64 {
1278 return r.pos - r.interface.bufferedLen();
1279 }
1280
1281 fn setPosAdjustingBuffer(r: *Reader, offset: u64) void {
1282 const logical_pos = logicalPos(r);
1283 if (offset < logical_pos or offset >= r.pos) {
1284 r.interface.seek = 0;
1285 r.interface.end = 0;
1286 r.pos = offset;
1287 } else {
1288 const logical_delta: usize = @intCast(offset - logical_pos);
1289 r.interface.seek += logical_delta;
1290 }
1291 }
1292
1384 /// Number of slices to store on the stack, when trying to send as many byte1293 /// Number of slices to store on the stack, when trying to send as many byte
1385 /// vectors through the underlying read calls as possible.1294 /// vectors through the underlying read calls as possible.
1386 const max_buffers_len = 16;1295 const max_buffers_len = 16;
...@@ -1526,7 +1435,7 @@ pub const Reader = struct {...@@ -1526,7 +1435,7 @@ pub const Reader = struct {
1526 }1435 }
1527 return 0;1436 return 0;
1528 };1437 };
1529 const n = @min(size - pos, std.math.maxInt(i64), @intFromEnum(limit));1438 const n = @min(size - pos, maxInt(i64), @intFromEnum(limit));
1530 file.seekBy(n) catch |err| {1439 file.seekBy(n) catch |err| {
1531 r.seek_err = err;1440 r.seek_err = err;
1532 return 0;1441 return 0;
...@@ -1715,7 +1624,6 @@ pub const Writer = struct {...@@ -1715,7 +1624,6 @@ pub const Writer = struct {
1715 const pattern = data[data.len - 1];1624 const pattern = data[data.len - 1];
1716 if (pattern.len == 0 or splat == 0) return 0;1625 if (pattern.len == 0 or splat == 0) return 0;
1717 const n = windows.WriteFile(handle, pattern, null) catch |err| {1626 const n = windows.WriteFile(handle, pattern, null) catch |err| {
1718 std.debug.print("windows write file failed3: {t}\n", .{err});
1719 w.err = err;1627 w.err = err;
1720 return error.WriteFailed;1628 return error.WriteFailed;
1721 };1629 };
...@@ -1817,18 +1725,141 @@ pub const Writer = struct {...@@ -1817,18 +1725,141 @@ pub const Writer = struct {
1817 file_reader: *Reader,1725 file_reader: *Reader,
1818 limit: std.io.Limit,1726 limit: std.io.Limit,
1819 ) std.io.Writer.FileError!usize {1727 ) std.io.Writer.FileError!usize {
1728 const reader_buffered = file_reader.interface.buffered();
1729 if (reader_buffered.len >= @intFromEnum(limit))
1730 return sendFileBuffered(io_w, file_reader, reader_buffered);
1731 const writer_buffered = io_w.buffered();
1732 const file_limit = @intFromEnum(limit) - reader_buffered.len;
1820 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));1733 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
1821 const out_fd = w.file.handle;1734 const out_fd = w.file.handle;
1822 const in_fd = file_reader.file.handle;1735 const in_fd = file_reader.file.handle;
1823 // TODO try using copy_file_range on FreeBSD1736
1824 // TODO try using sendfile on macOS1737 if (file_reader.size) |size| {
1825 // TODO try using sendfile on FreeBSD1738 if (size - file_reader.pos == 0) {
1739 if (reader_buffered.len != 0) {
1740 return sendFileBuffered(io_w, file_reader, reader_buffered);
1741 } else {
1742 return error.EndOfStream;
1743 }
1744 }
1745 }
1746
1747 if (native_os == .freebsd and w.mode == .streaming) sf: {
1748 // Try using sendfile on FreeBSD.
1749 if (w.sendfile_err != null) break :sf;
1750 const offset = std.math.cast(std.c.off_t, file_reader.pos) orelse break :sf;
1751 var hdtr_data: std.c.sf_hdtr = undefined;
1752 var headers: [2]posix.iovec_const = undefined;
1753 var headers_i: u8 = 0;
1754 if (writer_buffered.len != 0) {
1755 headers[headers_i] = .{ .base = writer_buffered.ptr, .len = writer_buffered.len };
1756 headers_i += 1;
1757 }
1758 if (reader_buffered.len != 0) {
1759 headers[headers_i] = .{ .base = reader_buffered.ptr, .len = reader_buffered.len };
1760 headers_i += 1;
1761 }
1762 const hdtr: ?*std.c.sf_hdtr = if (headers_i == 0) null else b: {
1763 hdtr_data = .{
1764 .headers = &headers,
1765 .hdr_cnt = headers_i,
1766 .trailers = null,
1767 .trl_cnt = 0,
1768 };
1769 break :b &hdtr_data;
1770 };
1771 var sbytes: std.c.off_t = undefined;
1772 const nbytes: usize = @min(file_limit, maxInt(usize));
1773 const flags = 0;
1774 switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, nbytes, hdtr, &sbytes, flags))) {
1775 .SUCCESS, .INTR => {},
1776 .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => w.sendfile_err = error.UnsupportedOperation,
1777 .BADF => if (builtin.mode == .Debug) @panic("race condition") else {
1778 w.sendfile_err = error.Unexpected;
1779 },
1780 .FAULT => if (builtin.mode == .Debug) @panic("segmentation fault") else {
1781 w.sendfile_err = error.Unexpected;
1782 },
1783 .NOTCONN => w.sendfile_err = error.BrokenPipe,
1784 .AGAIN, .BUSY => if (sbytes == 0) {
1785 w.sendfile_err = error.WouldBlock;
1786 },
1787 .IO => w.sendfile_err = error.InputOutput,
1788 .PIPE => w.sendfile_err = error.BrokenPipe,
1789 .NOBUFS => w.sendfile_err = error.SystemResources,
1790 else => |err| w.sendfile_err = posix.unexpectedErrno(err),
1791 }
1792 if (sbytes == 0) {
1793 file_reader.size = file_reader.pos;
1794 return error.EndOfStream;
1795 }
1796 const consumed = io_w.consume(@intCast(sbytes));
1797 file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed;
1798 return consumed;
1799 }
1800
1801 if (native_os.isDarwin() and w.mode == .streaming) sf: {
1802 // Try using sendfile on macOS.
1803 if (w.sendfile_err != null) break :sf;
1804 const offset = std.math.cast(std.c.off_t, file_reader.pos) orelse break :sf;
1805 var hdtr_data: std.c.sf_hdtr = undefined;
1806 var headers: [2]posix.iovec_const = undefined;
1807 var headers_i: u8 = 0;
1808 if (writer_buffered.len != 0) {
1809 headers[headers_i] = .{ .base = writer_buffered.ptr, .len = writer_buffered.len };
1810 headers_i += 1;
1811 }
1812 if (reader_buffered.len != 0) {
1813 headers[headers_i] = .{ .base = reader_buffered.ptr, .len = reader_buffered.len };
1814 headers_i += 1;
1815 }
1816 const hdtr: ?*std.c.sf_hdtr = if (headers_i == 0) null else b: {
1817 hdtr_data = .{
1818 .headers = &headers,
1819 .hdr_cnt = headers_i,
1820 .trailers = null,
1821 .trl_cnt = 0,
1822 };
1823 break :b &hdtr_data;
1824 };
1825 const max_count = maxInt(i32); // Avoid EINVAL.
1826 var len: std.c.off_t = @min(file_limit, max_count);
1827 const flags = 0;
1828 switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, &len, hdtr, flags))) {
1829 .SUCCESS, .INTR => {},
1830 .OPNOTSUPP, .NOTSOCK, .NOSYS => w.sendfile_err = error.UnsupportedOperation,
1831 .BADF => if (builtin.mode == .Debug) @panic("race condition") else {
1832 w.sendfile_err = error.Unexpected;
1833 },
1834 .FAULT => if (builtin.mode == .Debug) @panic("segmentation fault") else {
1835 w.sendfile_err = error.Unexpected;
1836 },
1837 .INVAL => if (builtin.mode == .Debug) @panic("invalid API usage") else {
1838 w.sendfile_err = error.Unexpected;
1839 },
1840 .NOTCONN => w.sendfile_err = error.BrokenPipe,
1841 .AGAIN => if (len == 0) {
1842 w.sendfile_err = error.WouldBlock;
1843 },
1844 .IO => w.sendfile_err = error.InputOutput,
1845 .PIPE => w.sendfile_err = error.BrokenPipe,
1846 else => |err| w.sendfile_err = posix.unexpectedErrno(err),
1847 }
1848 if (len == 0) {
1849 file_reader.size = file_reader.pos;
1850 return error.EndOfStream;
1851 }
1852 const consumed = io_w.consume(@bitCast(len));
1853 file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed;
1854 return consumed;
1855 }
1856
1826 if (native_os == .linux and w.mode == .streaming) sf: {1857 if (native_os == .linux and w.mode == .streaming) sf: {
1827 // Try using sendfile on Linux.1858 // Try using sendfile on Linux.
1828 if (w.sendfile_err != null) break :sf;1859 if (w.sendfile_err != null) break :sf;
1829 // Linux sendfile does not support headers.1860 // Linux sendfile does not support headers.
1830 const buffered = limit.slice(file_reader.interface.buffer);1861 if (writer_buffered.len != 0 or reader_buffered.len != 0)
1831 if (io_w.end != 0 or buffered.len != 0) return drain(io_w, &.{buffered}, 1);1862 return sendFileBuffered(io_w, file_reader, reader_buffered);
1832 const max_count = 0x7ffff000; // Avoid EINVAL.1863 const max_count = 0x7ffff000; // Avoid EINVAL.
1833 var off: std.os.linux.off_t = undefined;1864 var off: std.os.linux.off_t = undefined;
1834 const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {1865 const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {
...@@ -1875,6 +1906,7 @@ pub const Writer = struct {...@@ -1875,6 +1906,7 @@ pub const Writer = struct {
1875 w.pos += n;1906 w.pos += n;
1876 return n;1907 return n;
1877 }1908 }
1909
1878 const copy_file_range = switch (native_os) {1910 const copy_file_range = switch (native_os) {
1879 .freebsd => std.os.freebsd.copy_file_range,1911 .freebsd => std.os.freebsd.copy_file_range,
1880 .linux => if (std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) std.os.linux.wrapped.copy_file_range else {},1912 .linux => if (std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) std.os.linux.wrapped.copy_file_range else {},
...@@ -1882,8 +1914,8 @@ pub const Writer = struct {...@@ -1882,8 +1914,8 @@ pub const Writer = struct {
1882 };1914 };
1883 if (@TypeOf(copy_file_range) != void) cfr: {1915 if (@TypeOf(copy_file_range) != void) cfr: {
1884 if (w.copy_file_range_err != null) break :cfr;1916 if (w.copy_file_range_err != null) break :cfr;
1885 const buffered = limit.slice(file_reader.interface.buffer);1917 if (writer_buffered.len != 0 or reader_buffered.len != 0)
1886 if (io_w.end != 0 or buffered.len != 0) return drain(io_w, &.{buffered}, 1);1918 return sendFileBuffered(io_w, file_reader, reader_buffered);
1887 var off_in: i64 = undefined;1919 var off_in: i64 = undefined;
1888 var off_out: i64 = undefined;1920 var off_out: i64 = undefined;
1889 const off_in_ptr: ?*i64 = switch (file_reader.mode) {1921 const off_in_ptr: ?*i64 = switch (file_reader.mode) {
...@@ -1922,6 +1954,9 @@ pub const Writer = struct {...@@ -1922,6 +1954,9 @@ pub const Writer = struct {
1922 if (file_reader.pos != 0) break :fcf;1954 if (file_reader.pos != 0) break :fcf;
1923 if (w.pos != 0) break :fcf;1955 if (w.pos != 0) break :fcf;
1924 if (limit != .unlimited) break :fcf;1956 if (limit != .unlimited) break :fcf;
1957 const size = file_reader.getSize() catch break :fcf;
1958 if (writer_buffered.len != 0 or reader_buffered.len != 0)
1959 return sendFileBuffered(io_w, file_reader, reader_buffered);
1925 const rc = std.c.fcopyfile(in_fd, out_fd, null, .{ .DATA = true });1960 const rc = std.c.fcopyfile(in_fd, out_fd, null, .{ .DATA = true });
1926 switch (posix.errno(rc)) {1961 switch (posix.errno(rc)) {
1927 .SUCCESS => {},1962 .SUCCESS => {},
...@@ -1942,15 +1977,24 @@ pub const Writer = struct {...@@ -1942,15 +1977,24 @@ pub const Writer = struct {
1942 return 0;1977 return 0;
1943 },1978 },
1944 }1979 }
1945 const n = if (file_reader.size) |size| size else @panic("TODO figure out how much copied");1980 file_reader.pos = size;
1946 file_reader.pos = n;1981 w.pos = size;
1947 w.pos = n;1982 return size;
1948 return n;
1949 }1983 }
19501984
1951 return error.Unimplemented;1985 return error.Unimplemented;
1952 }1986 }
19531987
1988 fn sendFileBuffered(
1989 io_w: *std.io.Writer,
1990 file_reader: *Reader,
1991 reader_buffered: []const u8,
1992 ) std.io.Writer.FileError!usize {
1993 const n = try drain(io_w, &.{reader_buffered}, 1);
1994 file_reader.seekTo(file_reader.pos + n) catch return error.ReadFailed;
1995 return n;
1996 }
1997
1954 pub fn seekTo(w: *Writer, offset: u64) SeekError!void {1998 pub fn seekTo(w: *Writer, offset: u64) SeekError!void {
1955 switch (w.mode) {1999 switch (w.mode) {
1956 .positional, .positional_reading => {2000 .positional, .positional_reading => {
lib/std/fs/test.zig+57-29
...@@ -1499,32 +1499,18 @@ test "sendfile" {...@@ -1499,32 +1499,18 @@ test "sendfile" {
1499 const header2 = "second header\n";1499 const header2 = "second header\n";
1500 const trailer1 = "trailer1\n";1500 const trailer1 = "trailer1\n";
1501 const trailer2 = "second trailer\n";1501 const trailer2 = "second trailer\n";
1502 var hdtr = [_]posix.iovec_const{1502 var headers: [2][]const u8 = .{ header1, header2 };
1503 .{1503 var trailers: [2][]const u8 = .{ trailer1, trailer2 };
1504 .base = header1,
1505 .len = header1.len,
1506 },
1507 .{
1508 .base = header2,
1509 .len = header2.len,
1510 },
1511 .{
1512 .base = trailer1,
1513 .len = trailer1.len,
1514 },
1515 .{
1516 .base = trailer2,
1517 .len = trailer2.len,
1518 },
1519 };
15201504
1521 var written_buf: [100]u8 = undefined;1505 var written_buf: [100]u8 = undefined;
1522 try dest_file.writeFileAll(src_file, .{1506 var file_reader = src_file.reader(&.{});
1523 .in_offset = 1,1507 var fallback_buffer: [50]u8 = undefined;
1524 .in_len = 10,1508 var file_writer = dest_file.writer(&fallback_buffer);
1525 .headers_and_trailers = &hdtr,1509 try file_writer.interface.writeVecAll(&headers);
1526 .header_count = 2,1510 try file_reader.seekTo(1);
1527 });1511 try testing.expectEqual(10, try file_writer.interface.sendFileAll(&file_reader, .limited(10)));
1512 try file_writer.interface.writeVecAll(&trailers);
1513 try file_writer.interface.flush();
1528 const amt = try dest_file.preadAll(&written_buf, 0);1514 const amt = try dest_file.preadAll(&written_buf, 0);
1529 try testing.expectEqualStrings("header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n", written_buf[0..amt]);1515 try testing.expectEqualStrings("header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n", written_buf[0..amt]);
1530}1516}
...@@ -1595,9 +1581,10 @@ test "AtomicFile" {...@@ -1595,9 +1581,10 @@ test "AtomicFile" {
1595 ;1581 ;
15961582
1597 {1583 {
1598 var af = try ctx.dir.atomicFile(test_out_file, .{});1584 var buffer: [100]u8 = undefined;
1585 var af = try ctx.dir.atomicFile(test_out_file, .{ .write_buffer = &buffer });
1599 defer af.deinit();1586 defer af.deinit();
1600 try af.file.writeAll(test_content);1587 try af.file_writer.interface.writeAll(test_content);
1601 try af.finish();1588 try af.finish();
1602 }1589 }
1603 const content = try ctx.dir.readFileAlloc(allocator, test_out_file, 9999);1590 const content = try ctx.dir.readFileAlloc(allocator, test_out_file, 9999);
...@@ -2073,7 +2060,7 @@ test "invalid UTF-8/WTF-8 paths" {...@@ -2073,7 +2060,7 @@ test "invalid UTF-8/WTF-8 paths" {
2073}2060}
20742061
2075test "read file non vectored" {2062test "read file non vectored" {
2076 var tmp_dir = std.testing.tmpDir(.{});2063 var tmp_dir = testing.tmpDir(.{});
2077 defer tmp_dir.cleanup();2064 defer tmp_dir.cleanup();
20782065
2079 const contents = "hello, world!\n";2066 const contents = "hello, world!\n";
...@@ -2098,6 +2085,47 @@ test "read file non vectored" {...@@ -2098,6 +2085,47 @@ test "read file non vectored" {
2098 else => |e| return e,2085 else => |e| return e,
2099 };2086 };
2100 }2087 }
2101 try std.testing.expectEqualStrings(contents, w.buffered());2088 try testing.expectEqualStrings(contents, w.buffered());
2102 try std.testing.expectEqual(contents.len, i);2089 try testing.expectEqual(contents.len, i);
2090}
2091
2092test "seek keeping partial buffer" {
2093 var tmp_dir = testing.tmpDir(.{});
2094 defer tmp_dir.cleanup();
2095
2096 const contents = "0123456789";
2097
2098 const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true });
2099 defer file.close();
2100 {
2101 var file_writer: std.fs.File.Writer = .init(file, &.{});
2102 try file_writer.interface.writeAll(contents);
2103 try file_writer.interface.flush();
2104 }
2105
2106 var read_buffer: [3]u8 = undefined;
2107 var file_reader: std.fs.File.Reader = .init(file, &read_buffer);
2108
2109 try testing.expectEqual(0, file_reader.logicalPos());
2110
2111 var buf: [4]u8 = undefined;
2112 try file_reader.interface.readSliceAll(&buf);
2113
2114 if (file_reader.interface.bufferedLen() != 3) {
2115 // Pass the test if the OS doesn't give us vectored reads.
2116 return;
2117 }
2118
2119 try testing.expectEqual(4, file_reader.logicalPos());
2120 try testing.expectEqual(7, file_reader.pos);
2121 try file_reader.seekTo(6);
2122 try testing.expectEqual(6, file_reader.logicalPos());
2123 try testing.expectEqual(7, file_reader.pos);
2124
2125 try testing.expectEqualStrings("0123", &buf);
2126
2127 const n = try file_reader.interface.readSliceShort(&buf);
2128 try testing.expectEqual(4, n);
2129
2130 try testing.expectEqualStrings("6789", &buf);
2103}2131}
lib/std/json.zig-1
...@@ -69,7 +69,6 @@ pub const ArrayHashMap = @import("json/hashmap.zig").ArrayHashMap;...@@ -69,7 +69,6 @@ pub const ArrayHashMap = @import("json/hashmap.zig").ArrayHashMap;
69pub const Scanner = @import("json/Scanner.zig");69pub const Scanner = @import("json/Scanner.zig");
70pub const validate = Scanner.validate;70pub const validate = Scanner.validate;
71pub const Error = Scanner.Error;71pub const Error = Scanner.Error;
72pub const reader = Scanner.reader;
73pub const default_buffer_size = Scanner.default_buffer_size;72pub const default_buffer_size = Scanner.default_buffer_size;
74pub const Token = Scanner.Token;73pub const Token = Scanner.Token;
75pub const TokenType = Scanner.TokenType;74pub const TokenType = Scanner.TokenType;
lib/std/posix.zig-289
...@@ -6326,295 +6326,6 @@ pub fn send(...@@ -6326,295 +6326,6 @@ pub fn send(
6326 };6326 };
6327}6327}
63286328
6329pub const SendFileError = PReadError || WriteError || SendError;
6330
6331/// Transfer data between file descriptors, with optional headers and trailers.
6332///
6333/// Returns the number of bytes written, which can be zero.
6334///
6335/// The `sendfile` call copies `in_len` bytes from one file descriptor to another. When possible,
6336/// this is done within the operating system kernel, which can provide better performance
6337/// characteristics than transferring data from kernel to user space and back, such as with
6338/// `read` and `write` calls. When `in_len` is `0`, it means to copy until the end of the input file has been
6339/// reached. Note, however, that partial writes are still possible in this case.
6340///
6341/// `in_fd` must be a file descriptor opened for reading, and `out_fd` must be a file descriptor
6342/// opened for writing. They may be any kind of file descriptor; however, if `in_fd` is not a regular
6343/// file system file, it may cause this function to fall back to calling `read` and `write`, in which case
6344/// atomicity guarantees no longer apply.
6345///
6346/// Copying begins reading at `in_offset`. The input file descriptor seek position is ignored and not updated.
6347/// If the output file descriptor has a seek position, it is updated as bytes are written. When
6348/// `in_offset` is past the end of the input file, it successfully reads 0 bytes.
6349///
6350/// `flags` has different meanings per operating system; refer to the respective man pages.
6351///
6352/// These systems support atomically sending everything, including headers and trailers:
6353/// * macOS
6354/// * FreeBSD
6355///
6356/// These systems support in-kernel data copying, but headers and trailers are not sent atomically:
6357/// * Linux
6358///
6359/// Other systems fall back to calling `read` / `write`.
6360///
6361/// Linux has a limit on how many bytes may be transferred in one `sendfile` call, which is `0x7ffff000`
6362/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
6363/// well as stuffing the errno codes into the last `4096` values. This is noted on the `sendfile` man page.
6364/// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
6365/// The corresponding POSIX limit on this is `maxInt(isize)`.
6366pub fn sendfile(
6367 out_fd: fd_t,
6368 in_fd: fd_t,
6369 in_offset: u64,
6370 in_len: u64,
6371 headers: []const iovec_const,
6372 trailers: []const iovec_const,
6373 flags: u32,
6374) SendFileError!usize {
6375 var header_done = false;
6376 var total_written: usize = 0;
6377
6378 // Prevents EOVERFLOW.
6379 const size_t = std.meta.Int(.unsigned, @typeInfo(usize).int.bits - 1);
6380 const max_count = switch (native_os) {
6381 .linux => 0x7ffff000,
6382 .macos, .ios, .watchos, .tvos, .visionos => maxInt(i32),
6383 else => maxInt(size_t),
6384 };
6385
6386 switch (native_os) {
6387 .linux => sf: {
6388 if (headers.len != 0) {
6389 const amt = try writev(out_fd, headers);
6390 total_written += amt;
6391 if (amt < count_iovec_bytes(headers)) return total_written;
6392 header_done = true;
6393 }
6394
6395 // Here we match BSD behavior, making a zero count value send as many bytes as possible.
6396 const adjusted_count = if (in_len == 0) max_count else @min(in_len, max_count);
6397
6398 const sendfile_sym = if (lfs64_abi) system.sendfile64 else system.sendfile;
6399 while (true) {
6400 var offset: off_t = @bitCast(in_offset);
6401 const rc = sendfile_sym(out_fd, in_fd, &offset, adjusted_count);
6402 switch (errno(rc)) {
6403 .SUCCESS => {
6404 const amt: usize = @bitCast(rc);
6405 total_written += amt;
6406 if (in_len == 0 and amt == 0) {
6407 // We have detected EOF from `in_fd`.
6408 break;
6409 } else if (amt < in_len) {
6410 return total_written;
6411 } else {
6412 break;
6413 }
6414 },
6415
6416 .BADF => unreachable, // Always a race condition.
6417 .FAULT => unreachable, // Segmentation fault.
6418 .OVERFLOW => unreachable, // We avoid passing too large of a `count`.
6419 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
6420
6421 .INVAL => {
6422 // EINVAL could be any of the following situations:
6423 // * Descriptor is not valid or locked
6424 // * an mmap(2)-like operation is not available for in_fd
6425 // * count is negative
6426 // * out_fd has the APPEND flag set
6427 // Because of the "mmap(2)-like operation" possibility, we fall back to doing read/write
6428 // manually.
6429 break :sf;
6430 },
6431 .AGAIN => return error.WouldBlock,
6432 .IO => return error.InputOutput,
6433 .PIPE => return error.BrokenPipe,
6434 .NOMEM => return error.SystemResources,
6435 .NXIO => return error.Unseekable,
6436 .SPIPE => return error.Unseekable,
6437 else => |err| {
6438 unexpectedErrno(err) catch {};
6439 break :sf;
6440 },
6441 }
6442 }
6443
6444 if (trailers.len != 0) {
6445 total_written += try writev(out_fd, trailers);
6446 }
6447
6448 return total_written;
6449 },
6450 .freebsd => sf: {
6451 var hdtr_data: std.c.sf_hdtr = undefined;
6452 var hdtr: ?*std.c.sf_hdtr = null;
6453 if (headers.len != 0 or trailers.len != 0) {
6454 // Here we carefully avoid `@intCast` by returning partial writes when
6455 // too many io vectors are provided.
6456 const hdr_cnt = cast(u31, headers.len) orelse maxInt(u31);
6457 if (headers.len > hdr_cnt) return writev(out_fd, headers);
6458
6459 const trl_cnt = cast(u31, trailers.len) orelse maxInt(u31);
6460
6461 hdtr_data = std.c.sf_hdtr{
6462 .headers = headers.ptr,
6463 .hdr_cnt = hdr_cnt,
6464 .trailers = trailers.ptr,
6465 .trl_cnt = trl_cnt,
6466 };
6467 hdtr = &hdtr_data;
6468 }
6469
6470 while (true) {
6471 var sbytes: off_t = undefined;
6472 const err = errno(system.sendfile(in_fd, out_fd, @bitCast(in_offset), @min(in_len, max_count), hdtr, &sbytes, flags));
6473 const amt: usize = @bitCast(sbytes);
6474 switch (err) {
6475 .SUCCESS => return amt,
6476
6477 .BADF => unreachable, // Always a race condition.
6478 .FAULT => unreachable, // Segmentation fault.
6479 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
6480
6481 .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {
6482 // EINVAL could be any of the following situations:
6483 // * The fd argument is not a regular file.
6484 // * The s argument is not a SOCK.STREAM type socket.
6485 // * The offset argument is negative.
6486 // Because of some of these possibilities, we fall back to doing read/write
6487 // manually, the same as ENOSYS.
6488 break :sf;
6489 },
6490
6491 .INTR => if (amt != 0) return amt else continue,
6492
6493 .AGAIN => if (amt != 0) {
6494 return amt;
6495 } else {
6496 return error.WouldBlock;
6497 },
6498
6499 .BUSY => if (amt != 0) {
6500 return amt;
6501 } else {
6502 return error.WouldBlock;
6503 },
6504
6505 .IO => return error.InputOutput,
6506 .NOBUFS => return error.SystemResources,
6507 .PIPE => return error.BrokenPipe,
6508
6509 else => {
6510 unexpectedErrno(err) catch {};
6511 if (amt != 0) {
6512 return amt;
6513 } else {
6514 break :sf;
6515 }
6516 },
6517 }
6518 }
6519 },
6520 .macos, .ios, .tvos, .watchos, .visionos => sf: {
6521 var hdtr_data: std.c.sf_hdtr = undefined;
6522 var hdtr: ?*std.c.sf_hdtr = null;
6523 if (headers.len != 0 or trailers.len != 0) {
6524 // Here we carefully avoid `@intCast` by returning partial writes when
6525 // too many io vectors are provided.
6526 const hdr_cnt = cast(u31, headers.len) orelse maxInt(u31);
6527 if (headers.len > hdr_cnt) return writev(out_fd, headers);
6528
6529 const trl_cnt = cast(u31, trailers.len) orelse maxInt(u31);
6530
6531 hdtr_data = std.c.sf_hdtr{
6532 .headers = headers.ptr,
6533 .hdr_cnt = hdr_cnt,
6534 .trailers = trailers.ptr,
6535 .trl_cnt = trl_cnt,
6536 };
6537 hdtr = &hdtr_data;
6538 }
6539
6540 while (true) {
6541 var sbytes: off_t = @min(in_len, max_count);
6542 const err = errno(system.sendfile(in_fd, out_fd, @bitCast(in_offset), &sbytes, hdtr, flags));
6543 const amt: usize = @bitCast(sbytes);
6544 switch (err) {
6545 .SUCCESS => return amt,
6546
6547 .BADF => unreachable, // Always a race condition.
6548 .FAULT => unreachable, // Segmentation fault.
6549 .INVAL => unreachable,
6550 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
6551
6552 .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf,
6553
6554 .INTR => if (amt != 0) return amt else continue,
6555
6556 .AGAIN => if (amt != 0) {
6557 return amt;
6558 } else {
6559 return error.WouldBlock;
6560 },
6561
6562 .IO => return error.InputOutput,
6563 .PIPE => return error.BrokenPipe,
6564
6565 else => {
6566 unexpectedErrno(err) catch {};
6567 if (amt != 0) {
6568 return amt;
6569 } else {
6570 break :sf;
6571 }
6572 },
6573 }
6574 }
6575 },
6576 else => {}, // fall back to read/write
6577 }
6578
6579 if (headers.len != 0 and !header_done) {
6580 const amt = try writev(out_fd, headers);
6581 total_written += amt;
6582 if (amt < count_iovec_bytes(headers)) return total_written;
6583 }
6584
6585 rw: {
6586 var buf: [8 * 4096]u8 = undefined;
6587 // Here we match BSD behavior, making a zero count value send as many bytes as possible.
6588 const adjusted_count = if (in_len == 0) buf.len else @min(buf.len, in_len);
6589 const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset);
6590 if (amt_read == 0) {
6591 if (in_len == 0) {
6592 // We have detected EOF from `in_fd`.
6593 break :rw;
6594 } else {
6595 return total_written;
6596 }
6597 }
6598 const amt_written = try write(out_fd, buf[0..amt_read]);
6599 total_written += amt_written;
6600 if (amt_written < in_len or in_len == 0) return total_written;
6601 }
6602
6603 if (trailers.len != 0) {
6604 total_written += try writev(out_fd, trailers);
6605 }
6606
6607 return total_written;
6608}
6609
6610fn count_iovec_bytes(iovs: []const iovec_const) usize {
6611 var count: usize = 0;
6612 for (iovs) |iov| {
6613 count += iov.len;
6614 }
6615 return count;
6616}
6617
6618pub const CopyFileRangeError = error{6329pub const CopyFileRangeError = error{
6619 FileTooBig,6330 FileTooBig,
6620 InputOutput,6331 InputOutput,
src/Builtin.zig+2-2
...@@ -342,9 +342,9 @@ pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void {...@@ -342,9 +342,9 @@ pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void {
342 }342 }
343343
344 // `make_path` matters because the dir hasn't actually been created yet.344 // `make_path` matters because the dir hasn't actually been created yet.
345 var af = try root_dir.atomicFile(sub_path, .{ .make_path = true });345 var af = try root_dir.atomicFile(sub_path, .{ .make_path = true, .write_buffer = &.{} });
346 defer af.deinit();346 defer af.deinit();
347 try af.file.writeAll(file.source.?);347 try af.file_writer.interface.writeAll(file.source.?);
348 af.finish() catch |err| switch (err) {348 af.finish() catch |err| switch (err) {
349 error.AccessDenied => switch (builtin.os.tag) {349 error.AccessDenied => switch (builtin.os.tag) {
350 .windows => {350 .windows => {
src/Compilation.zig+117-117
...@@ -3382,7 +3382,7 @@ pub fn saveState(comp: *Compilation) !void {...@@ -3382,7 +3382,7 @@ pub fn saveState(comp: *Compilation) !void {
33823382
3383 const gpa = comp.gpa;3383 const gpa = comp.gpa;
33843384
3385 var bufs = std.ArrayList(std.posix.iovec_const).init(gpa);3385 var bufs = std.ArrayList([]const u8).init(gpa);
3386 defer bufs.deinit();3386 defer bufs.deinit();
33873387
3388 var pt_headers = std.ArrayList(Header.PerThread).init(gpa);3388 var pt_headers = std.ArrayList(Header.PerThread).init(gpa);
...@@ -3421,50 +3421,50 @@ pub fn saveState(comp: *Compilation) !void {...@@ -3421,50 +3421,50 @@ pub fn saveState(comp: *Compilation) !void {
34213421
3422 try bufs.ensureTotalCapacityPrecise(14 + 8 * pt_headers.items.len);3422 try bufs.ensureTotalCapacityPrecise(14 + 8 * pt_headers.items.len);
3423 addBuf(&bufs, mem.asBytes(&header));3423 addBuf(&bufs, mem.asBytes(&header));
3424 addBuf(&bufs, mem.sliceAsBytes(pt_headers.items));3424 addBuf(&bufs, @ptrCast(pt_headers.items));
34253425
3426 addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.keys()));3426 addBuf(&bufs, @ptrCast(ip.src_hash_deps.keys()));
3427 addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values()));3427 addBuf(&bufs, @ptrCast(ip.src_hash_deps.values()));
3428 addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys()));3428 addBuf(&bufs, @ptrCast(ip.nav_val_deps.keys()));
3429 addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.values()));3429 addBuf(&bufs, @ptrCast(ip.nav_val_deps.values()));
3430 addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.keys()));3430 addBuf(&bufs, @ptrCast(ip.nav_ty_deps.keys()));
3431 addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.values()));3431 addBuf(&bufs, @ptrCast(ip.nav_ty_deps.values()));
3432 addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.keys()));3432 addBuf(&bufs, @ptrCast(ip.interned_deps.keys()));
3433 addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.values()));3433 addBuf(&bufs, @ptrCast(ip.interned_deps.values()));
3434 addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.keys()));3434 addBuf(&bufs, @ptrCast(ip.zon_file_deps.keys()));
3435 addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.values()));3435 addBuf(&bufs, @ptrCast(ip.zon_file_deps.values()));
3436 addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.keys()));3436 addBuf(&bufs, @ptrCast(ip.embed_file_deps.keys()));
3437 addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.values()));3437 addBuf(&bufs, @ptrCast(ip.embed_file_deps.values()));
3438 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys()));3438 addBuf(&bufs, @ptrCast(ip.namespace_deps.keys()));
3439 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values()));3439 addBuf(&bufs, @ptrCast(ip.namespace_deps.values()));
3440 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys()));3440 addBuf(&bufs, @ptrCast(ip.namespace_name_deps.keys()));
3441 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.values()));3441 addBuf(&bufs, @ptrCast(ip.namespace_name_deps.values()));
34423442
3443 addBuf(&bufs, mem.sliceAsBytes(ip.first_dependency.keys()));3443 addBuf(&bufs, @ptrCast(ip.first_dependency.keys()));
3444 addBuf(&bufs, mem.sliceAsBytes(ip.first_dependency.values()));3444 addBuf(&bufs, @ptrCast(ip.first_dependency.values()));
3445 addBuf(&bufs, mem.sliceAsBytes(ip.dep_entries.items));3445 addBuf(&bufs, @ptrCast(ip.dep_entries.items));
3446 addBuf(&bufs, mem.sliceAsBytes(ip.free_dep_entries.items));3446 addBuf(&bufs, @ptrCast(ip.free_dep_entries.items));
34473447
3448 for (ip.locals, pt_headers.items) |*local, pt_header| {3448 for (ip.locals, pt_headers.items) |*local, pt_header| {
3449 if (pt_header.intern_pool.limbs_len > 0) {3449 if (pt_header.intern_pool.limbs_len > 0) {
3450 addBuf(&bufs, mem.sliceAsBytes(local.shared.limbs.view().items(.@"0")[0..pt_header.intern_pool.limbs_len]));3450 addBuf(&bufs, @ptrCast(local.shared.limbs.view().items(.@"0")[0..pt_header.intern_pool.limbs_len]));
3451 }3451 }
3452 if (pt_header.intern_pool.extra_len > 0) {3452 if (pt_header.intern_pool.extra_len > 0) {
3453 addBuf(&bufs, mem.sliceAsBytes(local.shared.extra.view().items(.@"0")[0..pt_header.intern_pool.extra_len]));3453 addBuf(&bufs, @ptrCast(local.shared.extra.view().items(.@"0")[0..pt_header.intern_pool.extra_len]));
3454 }3454 }
3455 if (pt_header.intern_pool.items_len > 0) {3455 if (pt_header.intern_pool.items_len > 0) {
3456 addBuf(&bufs, mem.sliceAsBytes(local.shared.items.view().items(.data)[0..pt_header.intern_pool.items_len]));3456 addBuf(&bufs, @ptrCast(local.shared.items.view().items(.data)[0..pt_header.intern_pool.items_len]));
3457 addBuf(&bufs, mem.sliceAsBytes(local.shared.items.view().items(.tag)[0..pt_header.intern_pool.items_len]));3457 addBuf(&bufs, @ptrCast(local.shared.items.view().items(.tag)[0..pt_header.intern_pool.items_len]));
3458 }3458 }
3459 if (pt_header.intern_pool.string_bytes_len > 0) {3459 if (pt_header.intern_pool.string_bytes_len > 0) {
3460 addBuf(&bufs, local.shared.strings.view().items(.@"0")[0..pt_header.intern_pool.string_bytes_len]);3460 addBuf(&bufs, local.shared.strings.view().items(.@"0")[0..pt_header.intern_pool.string_bytes_len]);
3461 }3461 }
3462 if (pt_header.intern_pool.tracked_insts_len > 0) {3462 if (pt_header.intern_pool.tracked_insts_len > 0) {
3463 addBuf(&bufs, mem.sliceAsBytes(local.shared.tracked_insts.view().items(.@"0")[0..pt_header.intern_pool.tracked_insts_len]));3463 addBuf(&bufs, @ptrCast(local.shared.tracked_insts.view().items(.@"0")[0..pt_header.intern_pool.tracked_insts_len]));
3464 }3464 }
3465 if (pt_header.intern_pool.files_len > 0) {3465 if (pt_header.intern_pool.files_len > 0) {
3466 addBuf(&bufs, mem.sliceAsBytes(local.shared.files.view().items(.bin_digest)[0..pt_header.intern_pool.files_len]));3466 addBuf(&bufs, @ptrCast(local.shared.files.view().items(.bin_digest)[0..pt_header.intern_pool.files_len]));
3467 addBuf(&bufs, mem.sliceAsBytes(local.shared.files.view().items(.root_type)[0..pt_header.intern_pool.files_len]));3467 addBuf(&bufs, @ptrCast(local.shared.files.view().items(.root_type)[0..pt_header.intern_pool.files_len]));
3468 }3468 }
3469 }3469 }
34703470
...@@ -3482,95 +3482,95 @@ pub fn saveState(comp: *Compilation) !void {...@@ -3482,95 +3482,95 @@ pub fn saveState(comp: *Compilation) !void {
3482 try bufs.ensureUnusedCapacity(85);3482 try bufs.ensureUnusedCapacity(85);
3483 addBuf(&bufs, wasm.string_bytes.items);3483 addBuf(&bufs, wasm.string_bytes.items);
3484 // TODO make it well-defined memory layout3484 // TODO make it well-defined memory layout
3485 //addBuf(&bufs, mem.sliceAsBytes(wasm.objects.items));3485 //addBuf(&bufs, @ptrCast(wasm.objects.items));
3486 addBuf(&bufs, mem.sliceAsBytes(wasm.func_types.keys()));3486 addBuf(&bufs, @ptrCast(wasm.func_types.keys()));
3487 addBuf(&bufs, mem.sliceAsBytes(wasm.object_function_imports.keys()));3487 addBuf(&bufs, @ptrCast(wasm.object_function_imports.keys()));
3488 addBuf(&bufs, mem.sliceAsBytes(wasm.object_function_imports.values()));3488 addBuf(&bufs, @ptrCast(wasm.object_function_imports.values()));
3489 addBuf(&bufs, mem.sliceAsBytes(wasm.object_functions.items));3489 addBuf(&bufs, @ptrCast(wasm.object_functions.items));
3490 addBuf(&bufs, mem.sliceAsBytes(wasm.object_global_imports.keys()));3490 addBuf(&bufs, @ptrCast(wasm.object_global_imports.keys()));
3491 addBuf(&bufs, mem.sliceAsBytes(wasm.object_global_imports.values()));3491 addBuf(&bufs, @ptrCast(wasm.object_global_imports.values()));
3492 addBuf(&bufs, mem.sliceAsBytes(wasm.object_globals.items));3492 addBuf(&bufs, @ptrCast(wasm.object_globals.items));
3493 addBuf(&bufs, mem.sliceAsBytes(wasm.object_table_imports.keys()));3493 addBuf(&bufs, @ptrCast(wasm.object_table_imports.keys()));
3494 addBuf(&bufs, mem.sliceAsBytes(wasm.object_table_imports.values()));3494 addBuf(&bufs, @ptrCast(wasm.object_table_imports.values()));
3495 addBuf(&bufs, mem.sliceAsBytes(wasm.object_tables.items));3495 addBuf(&bufs, @ptrCast(wasm.object_tables.items));
3496 addBuf(&bufs, mem.sliceAsBytes(wasm.object_memory_imports.keys()));3496 addBuf(&bufs, @ptrCast(wasm.object_memory_imports.keys()));
3497 addBuf(&bufs, mem.sliceAsBytes(wasm.object_memory_imports.values()));3497 addBuf(&bufs, @ptrCast(wasm.object_memory_imports.values()));
3498 addBuf(&bufs, mem.sliceAsBytes(wasm.object_memories.items));3498 addBuf(&bufs, @ptrCast(wasm.object_memories.items));
3499 addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.tag)));3499 addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.tag)));
3500 addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.offset)));3500 addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.offset)));
3501 // TODO handle the union safety field3501 // TODO handle the union safety field
3502 //addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.pointee)));3502 //addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.pointee)));
3503 addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.addend)));3503 addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.addend)));
3504 addBuf(&bufs, mem.sliceAsBytes(wasm.object_init_funcs.items));3504 addBuf(&bufs, @ptrCast(wasm.object_init_funcs.items));
3505 addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_segments.items));3505 addBuf(&bufs, @ptrCast(wasm.object_data_segments.items));
3506 addBuf(&bufs, mem.sliceAsBytes(wasm.object_datas.items));3506 addBuf(&bufs, @ptrCast(wasm.object_datas.items));
3507 addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_imports.keys()));3507 addBuf(&bufs, @ptrCast(wasm.object_data_imports.keys()));
3508 addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_imports.values()));3508 addBuf(&bufs, @ptrCast(wasm.object_data_imports.values()));
3509 addBuf(&bufs, mem.sliceAsBytes(wasm.object_custom_segments.keys()));3509 addBuf(&bufs, @ptrCast(wasm.object_custom_segments.keys()));
3510 addBuf(&bufs, mem.sliceAsBytes(wasm.object_custom_segments.values()));3510 addBuf(&bufs, @ptrCast(wasm.object_custom_segments.values()));
3511 // TODO make it well-defined memory layout3511 // TODO make it well-defined memory layout
3512 // addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdats.items));3512 // addBuf(&bufs, @ptrCast(wasm.object_comdats.items));
3513 addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations_table.keys()));3513 addBuf(&bufs, @ptrCast(wasm.object_relocations_table.keys()));
3514 addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations_table.values()));3514 addBuf(&bufs, @ptrCast(wasm.object_relocations_table.values()));
3515 addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdat_symbols.items(.kind)));3515 addBuf(&bufs, @ptrCast(wasm.object_comdat_symbols.items(.kind)));
3516 addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdat_symbols.items(.index)));3516 addBuf(&bufs, @ptrCast(wasm.object_comdat_symbols.items(.index)));
3517 addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.tag)));3517 addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.tag)));
3518 addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.offset)));3518 addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.offset)));
3519 // TODO handle the union safety field3519 // TODO handle the union safety field
3520 //addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.pointee)));3520 //addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.pointee)));
3521 addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.addend)));3521 addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.addend)));
3522 addBuf(&bufs, mem.sliceAsBytes(wasm.uav_fixups.items));3522 addBuf(&bufs, @ptrCast(wasm.uav_fixups.items));
3523 addBuf(&bufs, mem.sliceAsBytes(wasm.nav_fixups.items));3523 addBuf(&bufs, @ptrCast(wasm.nav_fixups.items));
3524 addBuf(&bufs, mem.sliceAsBytes(wasm.func_table_fixups.items));3524 addBuf(&bufs, @ptrCast(wasm.func_table_fixups.items));
3525 if (is_obj) {3525 if (is_obj) {
3526 addBuf(&bufs, mem.sliceAsBytes(wasm.navs_obj.keys()));3526 addBuf(&bufs, @ptrCast(wasm.navs_obj.keys()));
3527 addBuf(&bufs, mem.sliceAsBytes(wasm.navs_obj.values()));3527 addBuf(&bufs, @ptrCast(wasm.navs_obj.values()));
3528 addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_obj.keys()));3528 addBuf(&bufs, @ptrCast(wasm.uavs_obj.keys()));
3529 addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_obj.values()));3529 addBuf(&bufs, @ptrCast(wasm.uavs_obj.values()));
3530 } else {3530 } else {
3531 addBuf(&bufs, mem.sliceAsBytes(wasm.navs_exe.keys()));3531 addBuf(&bufs, @ptrCast(wasm.navs_exe.keys()));
3532 addBuf(&bufs, mem.sliceAsBytes(wasm.navs_exe.values()));3532 addBuf(&bufs, @ptrCast(wasm.navs_exe.values()));
3533 addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_exe.keys()));3533 addBuf(&bufs, @ptrCast(wasm.uavs_exe.keys()));
3534 addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_exe.values()));3534 addBuf(&bufs, @ptrCast(wasm.uavs_exe.values()));
3535 }3535 }
3536 addBuf(&bufs, mem.sliceAsBytes(wasm.overaligned_uavs.keys()));3536 addBuf(&bufs, @ptrCast(wasm.overaligned_uavs.keys()));
3537 addBuf(&bufs, mem.sliceAsBytes(wasm.overaligned_uavs.values()));3537 addBuf(&bufs, @ptrCast(wasm.overaligned_uavs.values()));
3538 addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_funcs.keys()));3538 addBuf(&bufs, @ptrCast(wasm.zcu_funcs.keys()));
3539 // TODO handle the union safety field3539 // TODO handle the union safety field
3540 // addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_funcs.values()));3540 // addBuf(&bufs, @ptrCast(wasm.zcu_funcs.values()));
3541 addBuf(&bufs, mem.sliceAsBytes(wasm.nav_exports.keys()));3541 addBuf(&bufs, @ptrCast(wasm.nav_exports.keys()));
3542 addBuf(&bufs, mem.sliceAsBytes(wasm.nav_exports.values()));3542 addBuf(&bufs, @ptrCast(wasm.nav_exports.values()));
3543 addBuf(&bufs, mem.sliceAsBytes(wasm.uav_exports.keys()));3543 addBuf(&bufs, @ptrCast(wasm.uav_exports.keys()));
3544 addBuf(&bufs, mem.sliceAsBytes(wasm.uav_exports.values()));3544 addBuf(&bufs, @ptrCast(wasm.uav_exports.values()));
3545 addBuf(&bufs, mem.sliceAsBytes(wasm.imports.keys()));3545 addBuf(&bufs, @ptrCast(wasm.imports.keys()));
3546 addBuf(&bufs, mem.sliceAsBytes(wasm.missing_exports.keys()));3546 addBuf(&bufs, @ptrCast(wasm.missing_exports.keys()));
3547 addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.keys()));3547 addBuf(&bufs, @ptrCast(wasm.function_exports.keys()));
3548 addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.values()));3548 addBuf(&bufs, @ptrCast(wasm.function_exports.values()));
3549 addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.keys()));3549 addBuf(&bufs, @ptrCast(wasm.hidden_function_exports.keys()));
3550 addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.values()));3550 addBuf(&bufs, @ptrCast(wasm.hidden_function_exports.values()));
3551 addBuf(&bufs, mem.sliceAsBytes(wasm.global_exports.items));3551 addBuf(&bufs, @ptrCast(wasm.global_exports.items));
3552 addBuf(&bufs, mem.sliceAsBytes(wasm.functions.keys()));3552 addBuf(&bufs, @ptrCast(wasm.functions.keys()));
3553 addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.keys()));3553 addBuf(&bufs, @ptrCast(wasm.function_imports.keys()));
3554 addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.values()));3554 addBuf(&bufs, @ptrCast(wasm.function_imports.values()));
3555 addBuf(&bufs, mem.sliceAsBytes(wasm.data_imports.keys()));3555 addBuf(&bufs, @ptrCast(wasm.data_imports.keys()));
3556 addBuf(&bufs, mem.sliceAsBytes(wasm.data_imports.values()));3556 addBuf(&bufs, @ptrCast(wasm.data_imports.values()));
3557 addBuf(&bufs, mem.sliceAsBytes(wasm.data_segments.keys()));3557 addBuf(&bufs, @ptrCast(wasm.data_segments.keys()));
3558 addBuf(&bufs, mem.sliceAsBytes(wasm.globals.keys()));3558 addBuf(&bufs, @ptrCast(wasm.globals.keys()));
3559 addBuf(&bufs, mem.sliceAsBytes(wasm.global_imports.keys()));3559 addBuf(&bufs, @ptrCast(wasm.global_imports.keys()));
3560 addBuf(&bufs, mem.sliceAsBytes(wasm.global_imports.values()));3560 addBuf(&bufs, @ptrCast(wasm.global_imports.values()));
3561 addBuf(&bufs, mem.sliceAsBytes(wasm.tables.keys()));3561 addBuf(&bufs, @ptrCast(wasm.tables.keys()));
3562 addBuf(&bufs, mem.sliceAsBytes(wasm.table_imports.keys()));3562 addBuf(&bufs, @ptrCast(wasm.table_imports.keys()));
3563 addBuf(&bufs, mem.sliceAsBytes(wasm.table_imports.values()));3563 addBuf(&bufs, @ptrCast(wasm.table_imports.values()));
3564 addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_indirect_function_set.keys()));3564 addBuf(&bufs, @ptrCast(wasm.zcu_indirect_function_set.keys()));
3565 addBuf(&bufs, mem.sliceAsBytes(wasm.object_indirect_function_import_set.keys()));3565 addBuf(&bufs, @ptrCast(wasm.object_indirect_function_import_set.keys()));
3566 addBuf(&bufs, mem.sliceAsBytes(wasm.object_indirect_function_set.keys()));3566 addBuf(&bufs, @ptrCast(wasm.object_indirect_function_set.keys()));
3567 addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.tag)));3567 addBuf(&bufs, @ptrCast(wasm.mir_instructions.items(.tag)));
3568 // TODO handle the union safety field3568 // TODO handle the union safety field
3569 //addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.data)));3569 //addBuf(&bufs, @ptrCast(wasm.mir_instructions.items(.data)));
3570 addBuf(&bufs, mem.sliceAsBytes(wasm.mir_extra.items));3570 addBuf(&bufs, @ptrCast(wasm.mir_extra.items));
3571 addBuf(&bufs, mem.sliceAsBytes(wasm.mir_locals.items));3571 addBuf(&bufs, @ptrCast(wasm.mir_locals.items));
3572 addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_bytes.items));3572 addBuf(&bufs, @ptrCast(wasm.tag_name_bytes.items));
3573 addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_offs.items));3573 addBuf(&bufs, @ptrCast(wasm.tag_name_offs.items));
35743574
3575 // TODO add as header fields3575 // TODO add as header fields
3576 // entry_resolution: FunctionImport.Resolution3576 // entry_resolution: FunctionImport.Resolution
...@@ -3596,16 +3596,16 @@ pub fn saveState(comp: *Compilation) !void {...@@ -3596,16 +3596,16 @@ pub fn saveState(comp: *Compilation) !void {
35963596
3597 // Using an atomic file prevents a crash or power failure from corrupting3597 // Using an atomic file prevents a crash or power failure from corrupting
3598 // the previous incremental compilation state.3598 // the previous incremental compilation state.
3599 var af = try lf.emit.root_dir.handle.atomicFile(basename, .{});3599 var write_buffer: [1024]u8 = undefined;
3600 var af = try lf.emit.root_dir.handle.atomicFile(basename, .{ .write_buffer = &write_buffer });
3600 defer af.deinit();3601 defer af.deinit();
3601 try af.file.pwritevAll(bufs.items, 0);3602 try af.file_writer.interface.writeVecAll(bufs.items);
3602 try af.finish();3603 try af.finish();
3603}3604}
36043605
3605fn addBuf(list: *std.ArrayList(std.posix.iovec_const), buf: []const u8) void {3606fn addBuf(list: *std.ArrayList([]const u8), buf: []const u8) void {
3606 // Even when len=0, the undefined pointer might cause EFAULT.
3607 if (buf.len == 0) return;3607 if (buf.len == 0) return;
3608 list.appendAssumeCapacity(.{ .base = buf.ptr, .len = buf.len });3608 list.appendAssumeCapacity(buf);
3609}3609}
36103610
3611/// This function is temporally single-threaded.3611/// This function is temporally single-threaded.
src/fmt.zig+2-2
...@@ -348,10 +348,10 @@ fn fmtPathFile(...@@ -348,10 +348,10 @@ fn fmtPathFile(
348 try fmt.stdout_writer.interface.print("{s}\n", .{file_path});348 try fmt.stdout_writer.interface.print("{s}\n", .{file_path});
349 fmt.any_error = true;349 fmt.any_error = true;
350 } else {350 } else {
351 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });351 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode, .write_buffer = &.{} });
352 defer af.deinit();352 defer af.deinit();
353353
354 try af.file.writeAll(fmt.out_buffer.getWritten());354 try af.file_writer.interface.writeAll(fmt.out_buffer.getWritten());
355 try af.finish();355 try af.finish();
356 try fmt.stdout_writer.interface.print("{s}\n", .{file_path});356 try fmt.stdout_writer.interface.print("{s}\n", .{file_path});
357 }357 }
src/link/MachO.zig-1
...@@ -612,7 +612,6 @@ pub fn flush(...@@ -612,7 +612,6 @@ pub fn flush(
612 };612 };
613 const emit = self.base.emit;613 const emit = self.base.emit;
614 invalidateKernelCache(emit.root_dir.handle, emit.sub_path) catch |err| switch (err) {614 invalidateKernelCache(emit.root_dir.handle, emit.sub_path) catch |err| switch (err) {
615 error.OutOfMemory => return error.OutOfMemory,
616 else => |e| return diags.fail("failed to invalidate kernel cache: {s}", .{@errorName(e)}),615 else => |e| return diags.fail("failed to invalidate kernel cache: {s}", .{@errorName(e)}),
617 };616 };
618 }617 }
src/main.zig+3-1
...@@ -4624,7 +4624,9 @@ fn cmdTranslateC(...@@ -4624,7 +4624,9 @@ fn cmdTranslateC(
4624 fatal("unable to open cached translated zig file '{s}{s}{s}': {s}", .{ path, fs.path.sep_str, out_zig_path, @errorName(err) });4624 fatal("unable to open cached translated zig file '{s}{s}{s}': {s}", .{ path, fs.path.sep_str, out_zig_path, @errorName(err) });
4625 };4625 };
4626 defer zig_file.close();4626 defer zig_file.close();
4627 try fs.File.stdout().writeFileAll(zig_file, .{});4627 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
4628 var file_reader = zig_file.reader(&.{});
4629 _ = try stdout_writer.interface.sendFileAll(&file_reader, .unlimited);
4628 return cleanExit();4630 return cleanExit();
4629 }4631 }
4630}4632}
test/incremental/fix_many_errors deleted-71
...@@ -1,71 +0,0 @@
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe
4#update=initial version
5#file=main.zig
6pub fn main() !void {}
7comptime { @compileError("c0"); }
8comptime { @compileError("c1"); }
9comptime { @compileError("c2"); }
10comptime { @compileError("c3"); }
11comptime { @compileError("c4"); }
12comptime { @compileError("c5"); }
13comptime { @compileError("c6"); }
14comptime { @compileError("c7"); }
15comptime { @compileError("c8"); }
16comptime { @compileError("c9"); }
17export fn f0() void { @compileError("f0"); }
18export fn f1() void { @compileError("f1"); }
19export fn f2() void { @compileError("f2"); }
20export fn f3() void { @compileError("f3"); }
21export fn f4() void { @compileError("f4"); }
22export fn f5() void { @compileError("f5"); }
23export fn f6() void { @compileError("f6"); }
24export fn f7() void { @compileError("f7"); }
25export fn f8() void { @compileError("f8"); }
26export fn f9() void { @compileError("f9"); }
27#expect_error=main.zig:2:12: error: c0
28#expect_error=main.zig:3:12: error: c1
29#expect_error=main.zig:4:12: error: c2
30#expect_error=main.zig:5:12: error: c3
31#expect_error=main.zig:6:12: error: c4
32#expect_error=main.zig:7:12: error: c5
33#expect_error=main.zig:8:12: error: c6
34#expect_error=main.zig:9:12: error: c7
35#expect_error=main.zig:10:12: error: c8
36#expect_error=main.zig:11:12: error: c9
37#expect_error=main.zig:12:23: error: f0
38#expect_error=main.zig:13:23: error: f1
39#expect_error=main.zig:14:23: error: f2
40#expect_error=main.zig:15:23: error: f3
41#expect_error=main.zig:16:23: error: f4
42#expect_error=main.zig:17:23: error: f5
43#expect_error=main.zig:18:23: error: f6
44#expect_error=main.zig:19:23: error: f7
45#expect_error=main.zig:20:23: error: f8
46#expect_error=main.zig:21:23: error: f9
47#update=fix all the errors
48#file=main.zig
49pub fn main() !void {}
50comptime {}
51comptime {}
52comptime {}
53comptime {}
54comptime {}
55comptime {}
56comptime {}
57comptime {}
58comptime {}
59comptime {}
60export fn f0() void {}
61export fn f1() void {}
62export fn f2() void {}
63export fn f3() void {}
64export fn f4() void {}
65export fn f5() void {}
66export fn f6() void {}
67export fn f7() void {}
68export fn f8() void {}
69export fn f9() void {}
70const std = @import("std");
71#expect_stdout=""
test/standalone/stack_iterator/build.zig+64-63
...@@ -65,69 +65,70 @@ pub fn build(b: *std.Build) void {...@@ -65,69 +65,70 @@ pub fn build(b: *std.Build) void {
65 test_step.dependOn(&run_cmd.step);65 test_step.dependOn(&run_cmd.step);
66 }66 }
6767
68 // Unwinding through a C shared library without a frame pointer (libc)68 // https://github.com/ziglang/zig/issues/24522
69 //69 //// Unwinding through a C shared library without a frame pointer (libc)
70 // getcontext version: libc70 ////
71 //71 //// getcontext version: libc
72 // Unwind info type:72 ////
73 // - ELF: DWARF .eh_frame + .debug_frame73 //// Unwind info type:
74 // - MachO: __unwind_info encodings:74 //// - ELF: DWARF .eh_frame + .debug_frame
75 // - x86_64: STACK_IMMD, STACK_IND75 //// - MachO: __unwind_info encodings:
76 // - aarch64: FRAMELESS, DWARF76 //// - x86_64: STACK_IMMD, STACK_IND
77 {77 //// - aarch64: FRAMELESS, DWARF
78 const c_shared_lib = b.addLibrary(.{78 //{
79 .linkage = .dynamic,79 // const c_shared_lib = b.addLibrary(.{
80 .name = "c_shared_lib",80 // .linkage = .dynamic,
81 .root_module = b.createModule(.{81 // .name = "c_shared_lib",
82 .root_source_file = null,82 // .root_module = b.createModule(.{
83 .target = target,83 // .root_source_file = null,
84 .optimize = optimize,84 // .target = target,
85 .link_libc = true,85 // .optimize = optimize,
86 .strip = false,86 // .link_libc = true,
87 }),87 // .strip = false,
88 });88 // }),
8989 // });
90 if (target.result.os.tag == .windows)90
91 c_shared_lib.root_module.addCMacro("LIB_API", "__declspec(dllexport)");91 // if (target.result.os.tag == .windows)
9292 // c_shared_lib.root_module.addCMacro("LIB_API", "__declspec(dllexport)");
93 c_shared_lib.root_module.addCSourceFile(.{93
94 .file = b.path("shared_lib.c"),94 // c_shared_lib.root_module.addCSourceFile(.{
95 .flags = &.{"-fomit-frame-pointer"},95 // .file = b.path("shared_lib.c"),
96 });96 // .flags = &.{"-fomit-frame-pointer"},
9797 // });
98 const exe = b.addExecutable(.{98
99 .name = "shared_lib_unwind",99 // const exe = b.addExecutable(.{
100 .root_module = b.createModule(.{100 // .name = "shared_lib_unwind",
101 .root_source_file = b.path("shared_lib_unwind.zig"),101 // .root_module = b.createModule(.{
102 .target = target,102 // .root_source_file = b.path("shared_lib_unwind.zig"),
103 .optimize = optimize,103 // .target = target,
104 .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null,104 // .optimize = optimize,
105 .omit_frame_pointer = true,105 // .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null,
106 }),106 // .omit_frame_pointer = true,
107 // zig objcopy doesn't support incremental binaries107 // }),
108 .use_llvm = true,108 // // zig objcopy doesn't support incremental binaries
109 });109 // .use_llvm = true,
110110 // });
111 exe.linkLibrary(c_shared_lib);111
112112 // exe.linkLibrary(c_shared_lib);
113 const run_cmd = b.addRunArtifact(exe);113
114 test_step.dependOn(&run_cmd.step);114 // const run_cmd = b.addRunArtifact(exe);
115115 // test_step.dependOn(&run_cmd.step);
116 // Separate debug info ELF file116
117 if (target.result.ofmt == .elf) {117 // // Separate debug info ELF file
118 const filename = b.fmt("{s}_stripped", .{exe.out_filename});118 // if (target.result.ofmt == .elf) {
119 const stripped_exe = b.addObjCopy(exe.getEmittedBin(), .{119 // const filename = b.fmt("{s}_stripped", .{exe.out_filename});
120 .basename = filename, // set the name for the debuglink120 // const stripped_exe = b.addObjCopy(exe.getEmittedBin(), .{
121 .compress_debug = true,121 // .basename = filename, // set the name for the debuglink
122 .strip = .debug,122 // .compress_debug = true,
123 .extract_to_separate_file = true,123 // .strip = .debug,
124 });124 // .extract_to_separate_file = true,
125125 // });
126 const run_stripped = std.Build.Step.Run.create(b, b.fmt("run {s}", .{filename}));126
127 run_stripped.addFileArg(stripped_exe.getOutput());127 // const run_stripped = std.Build.Step.Run.create(b, b.fmt("run {s}", .{filename}));
128 test_step.dependOn(&run_stripped.step);128 // run_stripped.addFileArg(stripped_exe.getOutput());
129 }129 // test_step.dependOn(&run_stripped.step);
130 }130 // }
131 //}
131132
132 // Unwinding without libc/posix133 // Unwinding without libc/posix
133 //134 //
tools/gen_stubs.zig+2-1
...@@ -310,7 +310,8 @@ pub fn main() !void {...@@ -310,7 +310,8 @@ pub fn main() !void {
310 build_all_path, libc_so_path, @errorName(err),310 build_all_path, libc_so_path, @errorName(err),
311 });311 });
312 };312 };
313 const header = try elf.Header.parse(elf_bytes[0..@sizeOf(elf.Elf64_Ehdr)]);313 var stream: std.Io.Reader = .fixed(elf_bytes);
314 const header = try elf.Header.read(&stream);
314315
315 const parse: Parse = .{316 const parse: Parse = .{
316 .arena = arena,317 .arena = arena,