diff --git a/lib/std/Io/Reader.zig b/lib/std/Io/Reader.zig index 5be8413c74..4cad73e8ce 100644 --- a/lib/std/Io/Reader.zig +++ b/lib/std/Io/Reader.zig @@ -143,8 +143,8 @@ pub const failing: Reader = .{ /// This is generally safe to `@constCast` because it has an empty buffer, so /// there is not really a way to accidentally attempt mutation of these fields. -const ending_state: Reader = .fixed(&.{}); -pub const ending: *Reader = @constCast(&ending_state); +pub const ending_instance: Reader = .fixed(&.{}); +pub const ending: *Reader = @constCast(&ending_instance); pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited { return .init(r, limit, buffer); diff --git a/lib/std/debug/Pdb.zig b/lib/std/debug/Pdb.zig index 808be42ccb..008aad6ab6 100644 --- a/lib/std/debug/Pdb.zig +++ b/lib/std/debug/Pdb.zig @@ -6,7 +6,7 @@ const assert = std.debug.assert; const Pdb = @This(); -in_file: File, +file_reader: *File.Reader, msf: Msf, allocator: Allocator, string_table: ?*MsfStream, @@ -36,31 +36,28 @@ pub const Module = struct { } }; -pub fn init(allocator: Allocator, path: []const u8) !Pdb { - const file = try std.fs.cwd().openFile(path, .{}); - errdefer file.close(); - +pub fn init(gpa: Allocator, file_reader: *File.Reader) !Pdb { return .{ - .in_file = file, - .allocator = allocator, + .file_reader = file_reader, + .allocator = gpa, .string_table = null, .dbi = null, - .msf = try Msf.init(allocator, file), - .modules = &[_]Module{}, - .sect_contribs = &[_]pdb.SectionContribEntry{}, + .msf = try Msf.init(gpa, file_reader), + .modules = &.{}, + .sect_contribs = &.{}, .guid = undefined, .age = undefined, }; } pub fn deinit(self: *Pdb) void { - self.in_file.close(); - self.msf.deinit(self.allocator); + const gpa = self.allocator; + self.msf.deinit(gpa); for (self.modules) |*module| { - module.deinit(self.allocator); + module.deinit(gpa); } - self.allocator.free(self.modules); - self.allocator.free(self.sect_contribs); + gpa.free(self.modules); + gpa.free(self.sect_contribs); } pub fn parseDbiStream(self: *Pdb) !void { @@ -68,11 +65,7 @@ pub fn parseDbiStream(self: *Pdb) !void { return error.InvalidDebugInfo; const gpa = self.allocator; - - const deprecated_reader = stream.reader(); - var adapted_buffer: [1024]u8 = undefined; - var adapted_reader = deprecated_reader.adaptToNewApi(&adapted_buffer); - const reader = &adapted_reader.new_interface; + const reader = &stream.interface; const header = try reader.takeStruct(std.pdb.DbiStreamHeader, .little); if (header.version_header != 19990903) // V70, only value observed by LLVM team @@ -113,7 +106,7 @@ pub fn parseDbiStream(self: *Pdb) !void { this_record_len += march_forward_bytes; } - try modules.append(Module{ + try modules.append(.{ .mod_info = mod_info, .module_name = try module_name.toOwnedSlice(), .obj_file_name = try obj_file_name.toOwnedSlice(), @@ -156,29 +149,28 @@ pub fn parseDbiStream(self: *Pdb) !void { } pub fn parseInfoStream(self: *Pdb) !void { - var stream = self.getStream(pdb.StreamType.pdb) orelse - return error.InvalidDebugInfo; - const reader = stream.reader(); + var stream = self.getStream(pdb.StreamType.pdb) orelse return error.InvalidDebugInfo; + const reader = &stream.interface; // Parse the InfoStreamHeader. - const version = try reader.readInt(u32, .little); - const signature = try reader.readInt(u32, .little); + const version = try reader.takeInt(u32, .little); + const signature = try reader.takeInt(u32, .little); _ = signature; - const age = try reader.readInt(u32, .little); - const guid = try reader.readBytesNoEof(16); + const age = try reader.takeInt(u32, .little); + const guid = try reader.takeArray(16); if (version != 20000404) // VC70, only value observed by LLVM team return error.UnknownPDBVersion; - self.guid = guid; + self.guid = guid.*; self.age = age; + const gpa = self.allocator; + // Find the string table. const string_table_index = str_tab_index: { - const name_bytes_len = try reader.readInt(u32, .little); - const name_bytes = try self.allocator.alloc(u8, name_bytes_len); - defer self.allocator.free(name_bytes); - try reader.readNoEof(name_bytes); + const name_bytes_len = try reader.takeInt(u32, .little); + const name_bytes = try reader.readAlloc(gpa, name_bytes_len); const HashTableHeader = extern struct { size: u32, @@ -188,23 +180,23 @@ pub fn parseInfoStream(self: *Pdb) !void { return cap * 2 / 3 + 1; } }; - const hash_tbl_hdr = try reader.readStruct(HashTableHeader); + const hash_tbl_hdr = try reader.takeStruct(HashTableHeader, .little); if (hash_tbl_hdr.capacity == 0) return error.InvalidDebugInfo; if (hash_tbl_hdr.size > HashTableHeader.maxLoad(hash_tbl_hdr.capacity)) return error.InvalidDebugInfo; - const present = try readSparseBitVector(&reader, self.allocator); - defer self.allocator.free(present); + const present = try readSparseBitVector(reader, gpa); + defer gpa.free(present); if (present.len != hash_tbl_hdr.size) return error.InvalidDebugInfo; - const deleted = try readSparseBitVector(&reader, self.allocator); - defer self.allocator.free(deleted); + const deleted = try readSparseBitVector(reader, gpa); + defer gpa.free(deleted); for (present) |_| { - const name_offset = try reader.readInt(u32, .little); - const name_index = try reader.readInt(u32, .little); + const name_offset = try reader.takeInt(u32, .little); + const name_index = try reader.takeInt(u32, .little); if (name_offset > name_bytes.len) return error.InvalidDebugInfo; const name = std.mem.sliceTo(name_bytes[name_offset..], 0); @@ -302,14 +294,12 @@ pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.S const strtab_offset = @sizeOf(pdb.StringTableHeader) + chksum_hdr.file_name_offset; try self.string_table.?.seekTo(strtab_offset); const source_file_name = s: { - const deprecated_reader = self.string_table.?.reader(); - var adapted_buffer: [1024]u8 = undefined; - var adapted_reader = deprecated_reader.adaptToNewApi(&adapted_buffer); + const string_reader = &self.string_table.?.interface; var source_file_name: std.Io.Writer.Allocating = .init(gpa); defer source_file_name.deinit(); - _ = try adapted_reader.new_interface.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024)); - assert(adapted_reader.new_interface.buffered()[0] == 0); // TODO change streamDelimiterLimit API - adapted_reader.new_interface.toss(1); + _ = try string_reader.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024)); + assert(string_reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API + string_reader.toss(1); break :s try source_file_name.toOwnedSlice(); }; errdefer gpa.free(source_file_name); @@ -366,19 +356,16 @@ pub fn getModule(self: *Pdb, index: usize) !?*Module { const stream = self.getStreamById(mod.mod_info.module_sym_stream) orelse return error.MissingDebugInfo; - const reader = stream.reader(); + const reader = &stream.interface; - const signature = try reader.readInt(u32, .little); + const signature = try reader.takeInt(u32, .little); if (signature != 4) return error.InvalidDebugInfo; - mod.symbols = try self.allocator.alloc(u8, mod.mod_info.sym_byte_size - 4); - errdefer self.allocator.free(mod.symbols); - try reader.readNoEof(mod.symbols); + const gpa = self.allocator; - mod.subsect_info = try self.allocator.alloc(u8, mod.mod_info.c13_byte_size); - errdefer self.allocator.free(mod.subsect_info); - try reader.readNoEof(mod.subsect_info); + mod.symbols = try reader.readAlloc(gpa, mod.mod_info.sym_byte_size - 4); + mod.subsect_info = try reader.readAlloc(gpa, mod.mod_info.c13_byte_size); var sect_offset: usize = 0; var skip_len: usize = undefined; @@ -404,8 +391,7 @@ pub fn getModule(self: *Pdb, index: usize) !?*Module { } pub fn getStreamById(self: *Pdb, id: u32) ?*MsfStream { - if (id >= self.msf.streams.len) - return null; + if (id >= self.msf.streams.len) return null; return &self.msf.streams[id]; } @@ -419,17 +405,14 @@ const Msf = struct { directory: MsfStream, streams: []MsfStream, - fn init(allocator: Allocator, file: File) !Msf { - const in = file.deprecatedReader(); + fn init(gpa: Allocator, file_reader: *File.Reader) !Msf { + const superblock = try file_reader.interface.takeStruct(pdb.SuperBlock, .little); - const superblock = try in.readStruct(pdb.SuperBlock); - - // Sanity checks if (!std.mem.eql(u8, &superblock.file_magic, pdb.SuperBlock.expect_magic)) return error.InvalidDebugInfo; if (superblock.free_block_map_block != 1 and superblock.free_block_map_block != 2) return error.InvalidDebugInfo; - const file_len = try file.getEndPos(); + const file_len = try file_reader.getSize(); if (superblock.num_blocks * superblock.block_size != file_len) return error.InvalidDebugInfo; switch (superblock.block_size) { @@ -442,163 +425,182 @@ const Msf = struct { if (dir_block_count > superblock.block_size / @sizeOf(u32)) return error.UnhandledBigDirectoryStream; // cf. BlockMapAddr comment. - try file.seekTo(superblock.block_size * superblock.block_map_addr); - const dir_blocks = try allocator.alloc(u32, dir_block_count); + try file_reader.seekTo(superblock.block_size * superblock.block_map_addr); + const dir_blocks = try gpa.alloc(u32, dir_block_count); for (dir_blocks) |*b| { - b.* = try in.readInt(u32, .little); + b.* = try file_reader.interface.takeInt(u32, .little); } - var directory = MsfStream.init( - superblock.block_size, - file, - dir_blocks, - ); + var directory_buffer: [64]u8 = undefined; + var directory = MsfStream.init(superblock.block_size, file_reader, dir_blocks, &directory_buffer); - const begin = directory.pos; - const stream_count = try directory.reader().readInt(u32, .little); - const stream_sizes = try allocator.alloc(u32, stream_count); - defer allocator.free(stream_sizes); + const begin = directory.logicalPos(); + const stream_count = try directory.interface.takeInt(u32, .little); + const stream_sizes = try gpa.alloc(u32, stream_count); + defer gpa.free(stream_sizes); // Microsoft's implementation uses @as(u32, -1) for inexistent streams. // These streams are not used, but still participate in the file // and must be taken into account when resolving stream indices. - const Nil = 0xFFFFFFFF; + const nil_size = 0xFFFFFFFF; for (stream_sizes) |*s| { - const size = try directory.reader().readInt(u32, .little); - s.* = if (size == Nil) 0 else blockCountFromSize(size, superblock.block_size); + const size = try directory.interface.takeInt(u32, .little); + s.* = if (size == nil_size) 0 else blockCountFromSize(size, superblock.block_size); } - const streams = try allocator.alloc(MsfStream, stream_count); + const streams = try gpa.alloc(MsfStream, stream_count); + errdefer gpa.free(streams); + for (streams, 0..) |*stream, i| { const size = stream_sizes[i]; if (size == 0) { - stream.* = MsfStream{ - .blocks = &[_]u32{}, - }; + stream.* = .empty; } else { - var blocks = try allocator.alloc(u32, size); - var j: u32 = 0; - while (j < size) : (j += 1) { - const block_id = try directory.reader().readInt(u32, .little); + const blocks = try gpa.alloc(u32, size); + errdefer gpa.free(blocks); + for (blocks) |*block| { + const block_id = try directory.interface.takeInt(u32, .little); const n = (block_id % superblock.block_size); // 0 is for pdb.SuperBlock, 1 and 2 for FPMs. if (block_id == 0 or n == 1 or n == 2 or block_id * superblock.block_size > file_len) return error.InvalidBlockIndex; - blocks[j] = block_id; + block.* = block_id; } - - stream.* = MsfStream.init( - superblock.block_size, - file, - blocks, - ); + const buffer = try gpa.alloc(u8, 64); + errdefer gpa.free(buffer); + stream.* = .init(superblock.block_size, file_reader, blocks, buffer); } } - const end = directory.pos; + const end = directory.logicalPos(); if (end - begin != superblock.num_directory_bytes) return error.InvalidStreamDirectory; - return Msf{ + return .{ .directory = directory, .streams = streams, }; } - fn deinit(self: *Msf, allocator: Allocator) void { - allocator.free(self.directory.blocks); + fn deinit(self: *Msf, gpa: Allocator) void { + gpa.free(self.directory.blocks); for (self.streams) |*stream| { - allocator.free(stream.blocks); + gpa.free(stream.interface.buffer); + gpa.free(stream.blocks); } - allocator.free(self.streams); + gpa.free(self.streams); } }; const MsfStream = struct { - in_file: File = undefined, - pos: u64 = undefined, - blocks: []u32 = undefined, - block_size: u32 = undefined, + file_reader: *File.Reader, + next_read_pos: u64, + blocks: []u32, + block_size: u32, + interface: std.Io.Reader, + err: ?Error, - pub const Error = @typeInfo(@typeInfo(@TypeOf(read)).@"fn".return_type.?).error_union.error_set; + const Error = File.Reader.SeekError; - fn init(block_size: u32, file: File, blocks: []u32) MsfStream { - const stream = MsfStream{ - .in_file = file, - .pos = 0, + const empty: MsfStream = .{ + .file_reader = undefined, + .next_read_pos = 0, + .blocks = &.{}, + .block_size = undefined, + .interface = .ending_instance, + .err = null, + }; + + fn init(block_size: u32, file_reader: *File.Reader, blocks: []u32, buffer: []u8) MsfStream { + return .{ + .file_reader = file_reader, + .next_read_pos = 0, .blocks = blocks, .block_size = block_size, + .interface = .{ + .vtable = &.{ .stream = stream }, + .buffer = buffer, + .seek = 0, + .end = 0, + }, + .err = null, }; - - return stream; } - fn read(self: *MsfStream, buffer: []u8) !usize { - var block_id = @as(usize, @intCast(self.pos / self.block_size)); - if (block_id >= self.blocks.len) return 0; // End of Stream - var block = self.blocks[block_id]; - var offset = self.pos % self.block_size; + fn stream(r: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize { + const ms: *MsfStream = @alignCast(@fieldParentPtr("interface", r)); - try self.in_file.seekTo(block * self.block_size + offset); - const in = self.in_file.deprecatedReader(); + var block_id: usize = @intCast(ms.next_read_pos / ms.block_size); + if (block_id >= ms.blocks.len) return error.EndOfStream; + var block = ms.blocks[block_id]; + var offset = ms.next_read_pos % ms.block_size; - var size: usize = 0; - var rem_buffer = buffer; - while (size < buffer.len) { - const size_to_read = @min(self.block_size - offset, rem_buffer.len); - size += try in.read(rem_buffer[0..size_to_read]); - rem_buffer = buffer[size..]; - offset += size_to_read; + ms.file_reader.seekTo(block * ms.block_size + offset) catch |err| { + ms.err = err; + return error.ReadFailed; + }; + + var remaining = @intFromEnum(limit); + while (remaining != 0) { + const stream_len: usize = @min(remaining, ms.block_size - offset); + const n = try ms.file_reader.interface.stream(w, .limited(stream_len)); + remaining -= n; + offset += n; // If we're at the end of a block, go to the next one. - if (offset == self.block_size) { + if (offset == ms.block_size) { offset = 0; block_id += 1; - if (block_id >= self.blocks.len) break; // End of Stream - block = self.blocks[block_id]; - try self.in_file.seekTo(block * self.block_size); + if (block_id >= ms.blocks.len) break; // End of Stream + block = ms.blocks[block_id]; + ms.file_reader.seekTo(block * ms.block_size) catch |err| { + ms.err = err; + return error.ReadFailed; + }; } } - self.pos += buffer.len; - return buffer.len; + const total = @intFromEnum(limit) - remaining; + ms.next_read_pos += total; + return total; } - pub fn seekBy(self: *MsfStream, len: i64) !void { - self.pos = @as(u64, @intCast(@as(i64, @intCast(self.pos)) + len)); - if (self.pos >= self.blocks.len * self.block_size) - return error.EOF; + pub fn logicalPos(ms: *const MsfStream) u64 { + return ms.next_read_pos - ms.interface.bufferedLen(); } - pub fn seekTo(self: *MsfStream, len: u64) !void { - self.pos = len; - if (self.pos >= self.blocks.len * self.block_size) - return error.EOF; + pub fn seekBy(ms: *MsfStream, len: i64) !void { + ms.next_read_pos = @as(u64, @intCast(@as(i64, @intCast(ms.logicalPos())) + len)); + if (ms.next_read_pos >= ms.blocks.len * ms.block_size) return error.EOF; + ms.interface.tossBuffered(); } - fn getSize(self: *const MsfStream) u64 { - return self.blocks.len * self.block_size; + pub fn seekTo(ms: *MsfStream, len: u64) !void { + ms.next_read_pos = len; + if (ms.next_read_pos >= ms.blocks.len * ms.block_size) return error.EOF; + ms.interface.tossBuffered(); } - fn getFilePos(self: MsfStream) u64 { - const block_id = self.pos / self.block_size; - const block = self.blocks[block_id]; - const offset = self.pos % self.block_size; - - return block * self.block_size + offset; + fn getSize(ms: *const MsfStream) u64 { + return ms.blocks.len * ms.block_size; } - pub fn reader(self: *MsfStream) std.io.GenericReader(*MsfStream, Error, read) { - return .{ .context = self }; + fn getFilePos(ms: *const MsfStream) u64 { + const pos = ms.logicalPos(); + const block_id = pos / ms.block_size; + const block = ms.blocks[block_id]; + const offset = pos % ms.block_size; + + return block * ms.block_size + offset; } }; -fn readSparseBitVector(stream: anytype, allocator: Allocator) ![]u32 { - const num_words = try stream.readInt(u32, .little); +fn readSparseBitVector(reader: *std.Io.Reader, allocator: Allocator) ![]u32 { + const num_words = try reader.takeInt(u32, .little); var list = std.array_list.Managed(u32).init(allocator); errdefer list.deinit(); var word_i: u32 = 0; while (word_i != num_words) : (word_i += 1) { - const word = try stream.readInt(u32, .little); + const word = try reader.takeInt(u32, .little); var bit_i: u5 = 0; while (true) : (bit_i += 1) { if (word & (@as(u32, 1) << bit_i) != 0) { diff --git a/lib/std/debug/SelfInfo.zig b/lib/std/debug/SelfInfo.zig index 693643c598..0a7a0f9a80 100644 --- a/lib/std/debug/SelfInfo.zig +++ b/lib/std/debug/SelfInfo.zig @@ -713,22 +713,26 @@ pub const Module = switch (native_os) { }, .uefi, .windows => struct { base_address: usize, - pdb: ?Pdb = null, - dwarf: ?Dwarf = null, + pdb: ?Pdb, + dwarf: ?Dwarf, coff_image_base: u64, /// Only used if pdb is non-null coff_section_headers: []coff.SectionHeader, - pub fn deinit(self: *@This(), allocator: Allocator) void { + pub fn deinit(self: *@This(), gpa: Allocator) void { if (self.dwarf) |*dwarf| { - dwarf.deinit(allocator); + dwarf.deinit(gpa); } if (self.pdb) |*p| { + gpa.free(p.file_reader.interface.buffer); + gpa.destroy(p.file_reader); p.deinit(); - allocator.free(self.coff_section_headers); + gpa.free(self.coff_section_headers); } + + self.* = undefined; } fn getSymbolFromPdb(self: *@This(), relocated_address: usize) !?std.debug.Symbol { @@ -970,23 +974,25 @@ fn readMachODebugInfo(allocator: Allocator, macho_file: File) !Module { }; } -fn readCoffDebugInfo(allocator: Allocator, coff_obj: *coff.Coff) !Module { +fn readCoffDebugInfo(gpa: Allocator, coff_obj: *coff.Coff) !Module { nosuspend { var di: Module = .{ .base_address = undefined, .coff_image_base = coff_obj.getImageBase(), .coff_section_headers = undefined, + .pdb = null, + .dwarf = null, }; if (coff_obj.getSectionByName(".debug_info")) |_| { // This coff file has embedded DWARF debug info var sections: Dwarf.SectionArray = Dwarf.null_section_array; - errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data); + errdefer for (sections) |section| if (section) |s| if (s.owned) gpa.free(s.data); inline for (@typeInfo(Dwarf.Section.Id).@"enum".fields, 0..) |section, i| { sections[i] = if (coff_obj.getSectionByName("." ++ section.name)) |section_header| blk: { break :blk .{ - .data = try coff_obj.getSectionDataAlloc(section_header, allocator), + .data = try coff_obj.getSectionDataAlloc(section_header, gpa), .virtual_address = section_header.virtual_address, .owned = true, }; @@ -999,7 +1005,7 @@ fn readCoffDebugInfo(allocator: Allocator, coff_obj: *coff.Coff) !Module { .is_macho = false, }; - try Dwarf.open(&dwarf, allocator); + try Dwarf.open(&dwarf, gpa); di.dwarf = dwarf; } @@ -1008,20 +1014,31 @@ fn readCoffDebugInfo(allocator: Allocator, coff_obj: *coff.Coff) !Module { if (fs.path.isAbsolute(raw_path)) { break :blk raw_path; } else { - const self_dir = try fs.selfExeDirPathAlloc(allocator); - defer allocator.free(self_dir); - break :blk try fs.path.join(allocator, &.{ self_dir, raw_path }); + const self_dir = try fs.selfExeDirPathAlloc(gpa); + defer gpa.free(self_dir); + break :blk try fs.path.join(gpa, &.{ self_dir, raw_path }); } }; - defer if (path.ptr != raw_path.ptr) allocator.free(path); + defer if (path.ptr != raw_path.ptr) gpa.free(path); - di.pdb = Pdb.init(allocator, path) catch |err| switch (err) { + const pdb_file = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound, error.IsDir => { if (di.dwarf == null) return error.MissingDebugInfo; return di; }, - else => return err, + else => |e| return e, }; + errdefer pdb_file.close(); + + const pdb_file_reader_buffer = try gpa.alloc(u8, 4096); + errdefer gpa.free(pdb_file_reader_buffer); + + const pdb_file_reader = try gpa.create(File.Reader); + errdefer gpa.destroy(pdb_file_reader); + + pdb_file_reader.* = pdb_file.reader(pdb_file_reader_buffer); + + di.pdb = try Pdb.init(gpa, pdb_file_reader); try di.pdb.?.parseInfoStream(); try di.pdb.?.parseDbiStream(); @@ -1029,8 +1046,8 @@ fn readCoffDebugInfo(allocator: Allocator, coff_obj: *coff.Coff) !Module { return error.InvalidDebugInfo; // Only used by the pdb path - di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator); - errdefer allocator.free(di.coff_section_headers); + di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(gpa); + errdefer gpa.free(di.coff_section_headers); return di; }