From 2ec9a11646c792a046b4601e0b99f8e182416a6c Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Sat, 21 Jul 2018 20:30:11 +0200 Subject: Very much WIP base implementation for #721. Currently does: - read COFF executable file - locate and load corresponding .pdb file - expose .pdb content as streams (PDB format) --- std/pdb.zig | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 std/pdb.zig (limited to 'std/pdb.zig') diff --git a/std/pdb.zig b/std/pdb.zig new file mode 100644 index 0000000000..8c5a82880e --- /dev/null +++ b/std/pdb.zig @@ -0,0 +1,265 @@ +const builtin = @import("builtin"); +const std = @import("index.zig"); +const io = std.io; +const math = std.math; +const mem = std.mem; +const os = std.os; +const warn = std.debug.warn; + +const ArrayList = std.ArrayList; + +pub const PdbError = error { + InvalidPdbMagic, + CorruptedFile, +}; + +pub const StreamType = enum(u16) { + Pdb = 1, + Tpi = 2, + Dbi = 3, + Ipi = 4, +}; + +pub const Pdb = struct { + in_file: os.File, + allocator: *mem.Allocator, + + msf: Msf, + + pub fn openFile(self: *Pdb, allocator: *mem.Allocator, file_name: []u8) !void { + self.in_file = try os.File.openRead(allocator, file_name[0..]); + self.allocator = allocator; + + try self.msf.openFile(allocator, &self.in_file); + } + + pub fn getStream(self: *Pdb, stream: StreamType) ?*MsfStream { + const id = u16(stream); + if (id < self.msf.streams.len) + return &self.msf.streams.items[id]; + return null; + } + + pub fn getSourceLine(self: *Pdb, address: usize) !void { + const dbi = self.getStream(StreamType.Dbi) orelse return error.CorruptedFile; + + // Dbi Header + try dbi.seekForward(@sizeOf(u32) * 3 + @sizeOf(u16) * 6); + warn("dbi stream at {} (file offset)\n", dbi.getFilePos()); + const module_info_size = try dbi.stream.readIntLe(u32); + const section_contribution_size = try dbi.stream.readIntLe(u32); + const section_map_size = try dbi.stream.readIntLe(u32); + const source_info_size = try dbi.stream.readIntLe(u32); + warn("module_info_size: {}\n", module_info_size); + warn("section_contribution_size: {}\n", section_contribution_size); + warn("section_map_size: {}\n", section_map_size); + warn("source_info_size: {}\n", source_info_size); + try dbi.seekForward(@sizeOf(u32) * 5 + @sizeOf(u16) * 2); + warn("after header dbi stream at {} (file offset)\n", dbi.getFilePos()); + + // Module Info Substream + try dbi.seekForward(@sizeOf(u32) + @sizeOf(u16) + @sizeOf(u8) * 2); + const offset = try dbi.stream.readIntLe(u32); + const size = try dbi.stream.readIntLe(u32); + try dbi.seekForward(@sizeOf(u32)); + const module_index = try dbi.stream.readIntLe(u16); + warn("module {} of size {} at {}\n", module_index, size, offset); + + // TODO: locate corresponding source line information + } +}; + +// see https://llvm.org/docs/PDB/MsfFile.html +const Msf = struct { + superblock: SuperBlock, + directory: MsfStream, + streams: ArrayList(MsfStream), + + fn openFile(self: *Msf, allocator: *mem.Allocator, file: *os.File) !void { + var file_stream = io.FileInStream.init(file); + const in = &file_stream.stream; + + var magic: SuperBlock.FileMagicBuffer = undefined; + try in.readNoEof(magic[0..]); + warn("magic: '{}'\n", magic); + + if (!mem.eql(u8, magic, SuperBlock.FileMagic)) + return error.InvalidPdbMagic; + + self.superblock = SuperBlock { + .block_size = try in.readIntLe(u32), + .free_block_map_block = try in.readIntLe(u32), + .num_blocks = try in.readIntLe(u32), + .num_directory_bytes = try in.readIntLe(u32), + .unknown = try in.readIntLe(u32), + .block_map_addr = try in.readIntLe(u32), + }; + + switch (self.superblock.block_size) { + 512, 1024, 2048, 4096 => {}, // llvm only uses 4096 + else => return error.InvalidPdbMagic + } + + if (self.superblock.fileSize() != try file.getEndPos()) + return error.CorruptedFile; // Should always stand. + + self.directory = try MsfStream.init( + self.superblock.block_size, + self.superblock.blocksOccupiedByDirectoryStream(), + self.superblock.blockMapAddr(), + file, + allocator + ); + + const stream_count = try self.directory.stream.readIntLe(u32); + warn("stream count {}\n", stream_count); + + var stream_sizes = ArrayList(u32).init(allocator); + try stream_sizes.resize(stream_count); + for (stream_sizes.toSlice()) |*s| { + const size = try self.directory.stream.readIntLe(u32); + s.* = blockCountFromSize(size, self.superblock.block_size); + warn("stream {}B {} blocks\n", size, s.*); + } + + self.streams = ArrayList(MsfStream).init(allocator); + try self.streams.resize(stream_count); + for (self.streams.toSlice()) |*ss, i| { + ss.* = try MsfStream.init( + self.superblock.block_size, + stream_sizes.items[i], + try file.getPos(), // We're reading the jagged array of block indices when creating streams so the file is always at the right position. + file, + allocator + ); + } + } +}; + +fn blockCountFromSize(size: u32, block_size: u32) u32 { + return (size + block_size - 1) / block_size; +} + +const SuperBlock = struct { + const FileMagic = "Microsoft C/C++ MSF 7.00\r\n" ++ []u8 { 0x1A, 'D', 'S', 0, 0, 0}; + const FileMagicBuffer = @typeOf(FileMagic); + + block_size: u32, + free_block_map_block: u32, + num_blocks: u32, + num_directory_bytes: u32, + unknown: u32, + block_map_addr: u32, + + fn fileSize(self: *const SuperBlock) usize { + return self.num_blocks * self.block_size; + } + + fn blockMapAddr(self: *const SuperBlock) usize { + return self.block_size * self.block_map_addr; + } + + fn blocksOccupiedByDirectoryStream(self: *const SuperBlock) u32 { + return blockCountFromSize(self.num_directory_bytes, self.block_size); + } +}; + +const MsfStream = struct { + in_file: *os.File, + pos: usize, + blocks: ArrayList(u32), + block_size: u32, + + fn init(block_size: u32, block_count: u32, pos: usize, file: *os.File, allocator: *mem.Allocator) !MsfStream { + var stream = MsfStream { + .in_file = file, + .pos = 0, + .blocks = ArrayList(u32).init(allocator), + .block_size = block_size, + .stream = Stream { + .readFn = readFn, + }, + }; + + try stream.blocks.resize(block_count); + + var file_stream = io.FileInStream.init(file); + const in = &file_stream.stream; + try file.seekTo(pos); + + warn("stream with blocks"); + var i: u32 = 0; + while (i < block_count) : (i += 1) { + stream.blocks.items[i] = try in.readIntLe(u32); + warn(" {}", stream.blocks.items[i]); + } + warn("\n"); + + return stream; + } + + fn read(self: *MsfStream, buffer: []u8) !usize { + var block_id = self.pos / self.block_size; + var block = self.blocks.items[block_id]; + var offset = self.pos % self.block_size; + + try self.in_file.seekTo(block * self.block_size + offset); + var file_stream = io.FileInStream.init(self.in_file); + const in = &file_stream.stream; + + var size: usize = 0; + for (buffer) |*byte| { + byte.* = try in.readByte(); + + offset += 1; + size += 1; + + // If we're at the end of a block, go to the next one. + if (offset == self.block_size) + { + offset = 0; + block_id += 1; + block = self.blocks.items[block_id]; + try self.in_file.seekTo(block * self.block_size); + } + } + + self.pos += size; + return size; + } + + fn seekForward(self: *MsfStream, len: usize) !void { + self.pos += len; + if (self.pos >= self.blocks.len * self.block_size) + return error.EOF; + } + + fn seekTo(self: *MsfStream, len: usize) !void { + self.pos = len; + if (self.pos >= self.blocks.len * self.block_size) + return error.EOF; + } + + fn getSize(self: *const MsfStream) usize { + return self.blocks.len * self.block_size; + } + + fn getFilePos(self: *const MsfStream) usize { + const block_id = self.pos / self.block_size; + const block = self.blocks.items[block_id]; + const offset = self.pos % self.block_size; + + return block * self.block_size + offset; + } + + /// Implementation of InStream trait for Pdb.MsfStream + pub const Error = @typeOf(read).ReturnType.ErrorSet; + pub const Stream = io.InStream(Error); + + stream: Stream, + + fn readFn(in_stream: *Stream, buffer: []u8) Error!usize { + const self = @fieldParentPtr(MsfStream, "stream", in_stream); + return self.read(buffer); + } +}; \ No newline at end of file -- cgit v1.2.3 From 686663239af6afd8dea814a9fe6a8885f06d6cb3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 29 Aug 2018 19:00:24 -0400 Subject: printing info from the ModuleInfo substream of DebugInfo --- std/coff.zig | 8 ++-- std/debug/index.zig | 10 ++-- std/io.zig | 8 ++-- std/os/file.zig | 19 ++++---- std/pdb.zig | 132 ++++++++++++++++++++++++++++++++++++++-------------- 5 files changed, 120 insertions(+), 57 deletions(-) (limited to 'std/pdb.zig') diff --git a/std/coff.zig b/std/coff.zig index 475b4fcbc1..cce001d618 100644 --- a/std/coff.zig +++ b/std/coff.zig @@ -41,7 +41,7 @@ pub const Coff = struct { pub fn loadHeader(self: *Coff) !void { const pe_pointer_offset = 0x3C; - var file_stream = io.FileInStream.init(&self.in_file); + var file_stream = io.FileInStream.init(self.in_file); const in = &file_stream.stream; var magic: [2]u8 = undefined; @@ -126,7 +126,7 @@ pub const Coff = struct { std.debug.warn("file offset {x}\n", file_offset); try self.in_file.seekTo(file_offset + debug_dir.size); - var file_stream = io.FileInStream.init(&self.in_file); + var file_stream = io.FileInStream.init(self.in_file); const in = &file_stream.stream; var cv_signature: [4]u8 = undefined; // CodeView signature @@ -158,7 +158,7 @@ pub const Coff = struct { self.sections = ArrayList(Section).init(self.allocator); - var file_stream = io.FileInStream.init(&self.in_file); + var file_stream = io.FileInStream.init(self.in_file); const in = &file_stream.stream; var name: [8]u8 = undefined; @@ -235,4 +235,4 @@ const SectionHeader = struct { number_of_relocations: u16, number_of_line_numbers: u16, characteristics: u32, -}; \ No newline at end of file +}; diff --git a/std/debug/index.zig b/std/debug/index.zig index 1bf38a9fbe..36c5a6bdc9 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -40,7 +40,7 @@ pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) { return st; } else { stderr_file = try io.getStdErr(); - stderr_file_out_stream = io.FileOutStream.init(&stderr_file); + stderr_file_out_stream = io.FileOutStream.init(stderr_file); const st = &stderr_file_out_stream.stream; stderr_stream = st; return st; @@ -73,7 +73,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; return; }; - writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, wantTtyColor(), start_addr) catch |err| { + writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| { stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; return; }; @@ -194,9 +194,9 @@ pub inline fn getReturnAddress(frame_count: usize) usize { return @intToPtr(*const usize, fp + @sizeOf(usize)).*; } -pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { +pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { switch (builtin.os) { - builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, allocator, debug_info, tty_color, start_addr), + builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr), else => {}, } const AddressState = union(enum) { @@ -231,7 +231,7 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_ } } -pub fn writeCurrentStackTraceWindows(out_stream: var, allocator: *mem.Allocator, debug_info: *DebugInfo, +pub fn writeCurrentStackTraceWindows(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { var addr_buf: [1024]usize = undefined; diff --git a/std/io.zig b/std/io.zig index 369f6eede3..2de52493b1 100644 --- a/std/io.zig +++ b/std/io.zig @@ -34,13 +34,13 @@ pub fn getStdIn() GetStdIoErrs!File { /// Implementation of InStream trait for File pub const FileInStream = struct { - file: *File, + file: File, stream: Stream, pub const Error = @typeOf(File.read).ReturnType.ErrorSet; pub const Stream = InStream(Error); - pub fn init(file: *File) FileInStream { + pub fn init(file: File) FileInStream { return FileInStream{ .file = file, .stream = Stream{ .readFn = readFn }, @@ -55,13 +55,13 @@ pub const FileInStream = struct { /// Implementation of OutStream trait for File pub const FileOutStream = struct { - file: *File, + file: File, stream: Stream, pub const Error = File.WriteError; pub const Stream = OutStream(Error); - pub fn init(file: *File) FileOutStream { + pub fn init(file: File) FileOutStream { return FileOutStream{ .file = file, .stream = Stream{ .writeFn = writeFn }, diff --git a/std/os/file.zig b/std/os/file.zig index e90275ec7f..020a5dca54 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -205,17 +205,16 @@ pub const File = struct { /// Upon success, the stream is in an uninitialized state. To continue using it, /// you must use the open() function. - pub fn close(self: *File) void { + pub fn close(self: File) void { os.close(self.handle); - self.handle = undefined; } /// Calls `os.isTty` on `self.handle`. - pub fn isTty(self: *File) bool { + pub fn isTty(self: File) bool { return os.isTty(self.handle); } - pub fn seekForward(self: *File, amount: isize) !void { + pub fn seekForward(self: File, amount: isize) !void { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const result = posix.lseek(self.handle, amount, posix.SEEK_CUR); @@ -246,7 +245,7 @@ pub const File = struct { } } - pub fn seekTo(self: *File, pos: usize) !void { + pub fn seekTo(self: File, pos: usize) !void { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const ipos = try math.cast(isize, pos); @@ -280,7 +279,7 @@ pub const File = struct { } } - pub fn getPos(self: *File) !usize { + pub fn getPos(self: File) !usize { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const result = posix.lseek(self.handle, 0, posix.SEEK_CUR); @@ -316,7 +315,7 @@ pub const File = struct { } } - pub fn getEndPos(self: *File) !usize { + pub fn getEndPos(self: File) !usize { if (is_posix) { const stat = try os.posixFStat(self.handle); return @intCast(usize, stat.size); @@ -341,7 +340,7 @@ pub const File = struct { Unexpected, }; - pub fn mode(self: *File) ModeError!Mode { + pub fn mode(self: File) ModeError!Mode { if (is_posix) { var stat: posix.Stat = undefined; const err = posix.getErrno(posix.fstat(self.handle, &stat)); @@ -375,7 +374,7 @@ pub const File = struct { Unexpected, }; - pub fn read(self: *File, buffer: []u8) ReadError!usize { + pub fn read(self: File, buffer: []u8) ReadError!usize { if (is_posix) { var index: usize = 0; while (index < buffer.len) { @@ -423,7 +422,7 @@ pub const File = struct { pub const WriteError = os.WindowsWriteError || os.PosixWriteError; - pub fn write(self: *File, bytes: []const u8) WriteError!void { + pub fn write(self: File, bytes: []const u8) WriteError!void { if (is_posix) { try os.posixWrite(self.handle, bytes); } else if (is_windows) { diff --git a/std/pdb.zig b/std/pdb.zig index 08c1d25f65..a83011df6a 100644 --- a/std/pdb.zig +++ b/std/pdb.zig @@ -8,9 +8,58 @@ const warn = std.debug.warn; const ArrayList = std.ArrayList; -pub const PdbError = error { - InvalidPdbMagic, - CorruptedFile, +// https://llvm.org/docs/PDB/DbiStream.html#stream-header +const DbiStreamHeader = packed struct { + VersionSignature: i32, + VersionHeader: u32, + Age: u32, + GlobalStreamIndex: u16, + BuildNumber: u16, + PublicStreamIndex: u16, + PdbDllVersion: u16, + SymRecordStream: u16, + PdbDllRbld: u16, + ModInfoSize: u32, + SectionContributionSize: i32, + SectionMapSize: i32, + SourceInfoSize: i32, + TypeServerSize: i32, + MFCTypeServerIndex: u32, + OptionalDbgHeaderSize: i32, + ECSubstreamSize: i32, + Flags: u16, + Machine: u16, + Padding: u32, +}; + +const SectionContribEntry = packed struct { + Section: u16, + Padding1: [2]u8, + Offset: i32, + Size: i32, + Characteristics: u32, + ModuleIndex: u16, + Padding2: [2]u8, + DataCrc: u32, + RelocCrc: u32, +}; + +const ModInfo = packed struct { + Unused1: u32, + SectionContr: SectionContribEntry, + Flags: u16, + ModuleSymStream: u16, + SymByteSize: u32, + C11ByteSize: u32, + C13ByteSize: u32, + SourceFileCount: u16, + Padding: [2]u8, + Unused2: u32, + SourceFileNameIndex: u32, + PdbFilePathNameIndex: u32, + // These fields are variable length + //ModuleName: char[], + //ObjFileName: char[], }; pub const StreamType = enum(u16) { @@ -30,7 +79,7 @@ pub const Pdb = struct { self.in_file = try os.File.openRead(file_name[0..]); self.allocator = allocator; - try self.msf.openFile(allocator, &self.in_file); + try self.msf.openFile(allocator, self.in_file); } pub fn getStream(self: *Pdb, stream: StreamType) ?*MsfStream { @@ -41,29 +90,32 @@ pub const Pdb = struct { } pub fn getSourceLine(self: *Pdb, address: usize) !void { - const dbi = self.getStream(StreamType.Dbi) orelse return error.CorruptedFile; + const dbi = self.getStream(StreamType.Dbi) orelse return error.InvalidDebugInfo; // Dbi Header - try dbi.seekForward(@sizeOf(u32) * 3 + @sizeOf(u16) * 6); - warn("dbi stream at {} (file offset)\n", dbi.getFilePos()); - const module_info_size = try dbi.stream.readIntLe(u32); - const section_contribution_size = try dbi.stream.readIntLe(u32); - const section_map_size = try dbi.stream.readIntLe(u32); - const source_info_size = try dbi.stream.readIntLe(u32); - warn("module_info_size: {}\n", module_info_size); - warn("section_contribution_size: {}\n", section_contribution_size); - warn("section_map_size: {}\n", section_map_size); - warn("source_info_size: {}\n", source_info_size); - try dbi.seekForward(@sizeOf(u32) * 5 + @sizeOf(u16) * 2); + var header: DbiStreamHeader = undefined; + try dbi.stream.readStruct(DbiStreamHeader, &header); + std.debug.warn("{}\n", header); warn("after header dbi stream at {} (file offset)\n", dbi.getFilePos()); // Module Info Substream - try dbi.seekForward(@sizeOf(u32) + @sizeOf(u16) + @sizeOf(u8) * 2); - const offset = try dbi.stream.readIntLe(u32); - const size = try dbi.stream.readIntLe(u32); - try dbi.seekForward(@sizeOf(u32)); - const module_index = try dbi.stream.readIntLe(u16); - warn("module {} of size {} at {}\n", module_index, size, offset); + var mod_info_offset: usize = 0; + while (mod_info_offset < header.ModInfoSize) { + var mod_info: ModInfo = undefined; + try dbi.stream.readStruct(ModInfo, &mod_info); + std.debug.warn("{}\n", mod_info); + mod_info_offset += @sizeOf(ModInfo); + + const module_name = try dbi.readNullTermString(self.allocator); + std.debug.warn("module_name {}\n", module_name); + mod_info_offset += module_name.len + 1; + + const obj_file_name = try dbi.readNullTermString(self.allocator); + std.debug.warn("obj_file_name {}\n", obj_file_name); + mod_info_offset += obj_file_name.len + 1; + } + std.debug.warn("end modules\n"); + // TODO: locate corresponding source line information } @@ -75,7 +127,7 @@ const Msf = struct { directory: MsfStream, streams: ArrayList(MsfStream), - fn openFile(self: *Msf, allocator: *mem.Allocator, file: *os.File) !void { + fn openFile(self: *Msf, allocator: *mem.Allocator, file: os.File) !void { var file_stream = io.FileInStream.init(file); const in = &file_stream.stream; @@ -84,7 +136,7 @@ const Msf = struct { warn("magic: '{}'\n", magic); if (!mem.eql(u8, magic, SuperBlock.FileMagic)) - return error.InvalidPdbMagic; + return error.InvalidDebugInfo; self.superblock = SuperBlock { .block_size = try in.readIntLe(u32), @@ -97,11 +149,11 @@ const Msf = struct { switch (self.superblock.block_size) { 512, 1024, 2048, 4096 => {}, // llvm only uses 4096 - else => return error.InvalidPdbMagic + else => return error.InvalidDebugInfo } if (self.superblock.fileSize() != try file.getEndPos()) - return error.CorruptedFile; // Should always stand. + return error.InvalidDebugInfo; // Should always stand. self.directory = try MsfStream.init( self.superblock.block_size, @@ -165,12 +217,18 @@ const SuperBlock = struct { }; const MsfStream = struct { - in_file: *os.File, + in_file: os.File, pos: usize, blocks: ArrayList(u32), block_size: u32, - fn init(block_size: u32, block_count: u32, pos: usize, file: *os.File, allocator: *mem.Allocator) !MsfStream { + /// Implementation of InStream trait for Pdb.MsfStream + stream: Stream, + + pub const Error = @typeOf(read).ReturnType.ErrorSet; + pub const Stream = io.InStream(Error); + + fn init(block_size: u32, block_count: u32, pos: usize, file: os.File, allocator: *mem.Allocator) !MsfStream { var stream = MsfStream { .in_file = file, .pos = 0, @@ -198,6 +256,18 @@ const MsfStream = struct { return stream; } + fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 { + var list = ArrayList(u8).init(allocator); + defer list.deinit(); + while (true) { + const byte = try self.stream.readByte(); + if (byte == 0) { + return list.toSlice(); + } + try list.append(byte); + } + } + fn read(self: *MsfStream, buffer: []u8) !usize { var block_id = self.pos / self.block_size; var block = self.blocks.items[block_id]; @@ -252,12 +322,6 @@ const MsfStream = struct { return block * self.block_size + offset; } - /// Implementation of InStream trait for Pdb.MsfStream - pub const Error = @typeOf(read).ReturnType.ErrorSet; - pub const Stream = io.InStream(Error); - - stream: Stream, - fn readFn(in_stream: *Stream, buffer: []u8) Error!usize { const self = @fieldParentPtr(MsfStream, "stream", in_stream); return self.read(buffer); -- cgit v1.2.3 From 96117e20cc7c076cbf9d8243602c6e4205fdd0d4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Aug 2018 03:44:34 -0400 Subject: reading the module information substream --- std/pdb.zig | 169 ++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 96 insertions(+), 73 deletions(-) (limited to 'std/pdb.zig') diff --git a/std/pdb.zig b/std/pdb.zig index a83011df6a..6f086b59e0 100644 --- a/std/pdb.zig +++ b/std/pdb.zig @@ -76,7 +76,7 @@ pub const Pdb = struct { msf: Msf, pub fn openFile(self: *Pdb, allocator: *mem.Allocator, file_name: []u8) !void { - self.in_file = try os.File.openRead(file_name[0..]); + self.in_file = try os.File.openRead(file_name); self.allocator = allocator; try self.msf.openFile(allocator, self.in_file); @@ -85,7 +85,7 @@ pub const Pdb = struct { pub fn getStream(self: *Pdb, stream: StreamType) ?*MsfStream { const id = @enumToInt(stream); if (id < self.msf.streams.len) - return &self.msf.streams.items[id]; + return &self.msf.streams[id]; return null; } @@ -101,17 +101,31 @@ pub const Pdb = struct { // Module Info Substream var mod_info_offset: usize = 0; while (mod_info_offset < header.ModInfoSize) { + const march_forward_bytes = dbi.getFilePos() % 4; + if (march_forward_bytes != 0) { + try dbi.seekForward(march_forward_bytes); + mod_info_offset += march_forward_bytes; + } var mod_info: ModInfo = undefined; try dbi.stream.readStruct(ModInfo, &mod_info); std.debug.warn("{}\n", mod_info); mod_info_offset += @sizeOf(ModInfo); const module_name = try dbi.readNullTermString(self.allocator); - std.debug.warn("module_name {}\n", module_name); + std.debug.warn("module_name '{}'\n", module_name); mod_info_offset += module_name.len + 1; + //if (mem.eql(u8, module_name, "piler_rt.obj")) { + // std.debug.warn("detected bad thing\n"); + // try dbi.seekTo(dbi.pos - + // "c:\\msys64\\home\\andy\\zig\\build-llvm6-msvc-release\\.\\zig-cache\\compiler_rt.obj\x00".len - + // @sizeOf(ModInfo)); + // mod_info_offset -= module_name.len + 1; + // continue; + //} + const obj_file_name = try dbi.readNullTermString(self.allocator); - std.debug.warn("obj_file_name {}\n", obj_file_name); + std.debug.warn("obj_file_name '{}'\n", obj_file_name); mod_info_offset += obj_file_name.len + 1; } std.debug.warn("end modules\n"); @@ -123,66 +137,55 @@ pub const Pdb = struct { // see https://llvm.org/docs/PDB/MsfFile.html const Msf = struct { - superblock: SuperBlock, directory: MsfStream, - streams: ArrayList(MsfStream), + streams: []MsfStream, fn openFile(self: *Msf, allocator: *mem.Allocator, file: os.File) !void { var file_stream = io.FileInStream.init(file); const in = &file_stream.stream; - var magic: SuperBlock.FileMagicBuffer = undefined; - try in.readNoEof(magic[0..]); - warn("magic: '{}'\n", magic); - - if (!mem.eql(u8, magic, SuperBlock.FileMagic)) - return error.InvalidDebugInfo; + var superblock: SuperBlock = undefined; + try in.readStruct(SuperBlock, &superblock); - self.superblock = SuperBlock { - .block_size = try in.readIntLe(u32), - .free_block_map_block = try in.readIntLe(u32), - .num_blocks = try in.readIntLe(u32), - .num_directory_bytes = try in.readIntLe(u32), - .unknown = try in.readIntLe(u32), - .block_map_addr = try in.readIntLe(u32), - }; + if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic)) + return error.InvalidDebugInfo; - switch (self.superblock.block_size) { - 512, 1024, 2048, 4096 => {}, // llvm only uses 4096 + switch (superblock.BlockSize) { + // llvm only supports 4096 but we can handle any of these values + 512, 1024, 2048, 4096 => {}, else => return error.InvalidDebugInfo } - if (self.superblock.fileSize() != try file.getEndPos()) - return error.InvalidDebugInfo; // Should always stand. + if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos()) + return error.InvalidDebugInfo; self.directory = try MsfStream.init( - self.superblock.block_size, - self.superblock.blocksOccupiedByDirectoryStream(), - self.superblock.blockMapAddr(), + superblock.BlockSize, + blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize), + superblock.BlockSize * superblock.BlockMapAddr, file, - allocator + allocator, ); const stream_count = try self.directory.stream.readIntLe(u32); warn("stream count {}\n", stream_count); - var stream_sizes = ArrayList(u32).init(allocator); - try stream_sizes.resize(stream_count); - for (stream_sizes.toSlice()) |*s| { + const stream_sizes = try allocator.alloc(u32, stream_count); + for (stream_sizes) |*s| { const size = try self.directory.stream.readIntLe(u32); - s.* = blockCountFromSize(size, self.superblock.block_size); + s.* = blockCountFromSize(size, superblock.BlockSize); warn("stream {}B {} blocks\n", size, s.*); } - self.streams = ArrayList(MsfStream).init(allocator); - try self.streams.resize(stream_count); - for (self.streams.toSlice()) |*ss, i| { - ss.* = try MsfStream.init( - self.superblock.block_size, - stream_sizes.items[i], - try file.getPos(), // We're reading the jagged array of block indices when creating streams so the file is always at the right position. + self.streams = try allocator.alloc(MsfStream, stream_count); + for (self.streams) |*stream, i| { + stream.* = try MsfStream.init( + superblock.BlockSize, + stream_sizes[i], + // MsfStream.init expects the file to be at the part where it reads [N]u32 + try file.getPos(), file, - allocator + allocator, ); } } @@ -192,34 +195,53 @@ fn blockCountFromSize(size: u32, block_size: u32) u32 { return (size + block_size - 1) / block_size; } -const SuperBlock = struct { - const FileMagic = "Microsoft C/C++ MSF 7.00\r\n" ++ []u8 { 0x1A, 'D', 'S', 0, 0, 0}; - const FileMagicBuffer = @typeOf(FileMagic); - - block_size: u32, - free_block_map_block: u32, - num_blocks: u32, - num_directory_bytes: u32, - unknown: u32, - block_map_addr: u32, - - fn fileSize(self: *const SuperBlock) usize { - return self.num_blocks * self.block_size; - } - - fn blockMapAddr(self: *const SuperBlock) usize { - return self.block_size * self.block_map_addr; - } +// https://llvm.org/docs/PDB/MsfFile.html#the-superblock +const SuperBlock = packed struct { + /// The LLVM docs list a space between C / C++ but empirically this is not the case. + const file_magic = "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\x00\x00\x00"; + + FileMagic: [file_magic.len]u8, + + /// The block size of the internal file system. Valid values are 512, 1024, + /// 2048, and 4096 bytes. Certain aspects of the MSF file layout vary depending + /// on the block sizes. For the purposes of LLVM, we handle only block sizes of + /// 4KiB, and all further discussion assumes a block size of 4KiB. + BlockSize: u32, + + /// The index of a block within the file, at which begins a bitfield representing + /// the set of all blocks within the file which are “free” (i.e. the data within + /// that block is not used). See The Free Block Map for more information. Important: + /// FreeBlockMapBlock can only be 1 or 2! + FreeBlockMapBlock: u32, + + /// The total number of blocks in the file. NumBlocks * BlockSize should equal the + /// size of the file on disk. + NumBlocks: u32, + + /// The size of the stream directory, in bytes. The stream directory contains + /// information about each stream’s size and the set of blocks that it occupies. + /// It will be described in more detail later. + NumDirectoryBytes: u32, + + Unknown: u32, + + /// The index of a block within the MSF file. At this block is an array of + /// ulittle32_t’s listing the blocks that the stream directory resides on. + /// For large MSF files, the stream directory (which describes the block + /// layout of each stream) may not fit entirely on a single block. As a + /// result, this extra layer of indirection is introduced, whereby this + /// block contains the list of blocks that the stream directory occupies, + /// and the stream directory itself can be stitched together accordingly. + /// The number of ulittle32_t’s in this array is given by + /// ceil(NumDirectoryBytes / BlockSize). + BlockMapAddr: u32, - fn blocksOccupiedByDirectoryStream(self: *const SuperBlock) u32 { - return blockCountFromSize(self.num_directory_bytes, self.block_size); - } }; const MsfStream = struct { in_file: os.File, pos: usize, - blocks: ArrayList(u32), + blocks: []u32, block_size: u32, /// Implementation of InStream trait for Pdb.MsfStream @@ -232,15 +254,13 @@ const MsfStream = struct { var stream = MsfStream { .in_file = file, .pos = 0, - .blocks = ArrayList(u32).init(allocator), + .blocks = try allocator.alloc(u32, block_count), .block_size = block_size, .stream = Stream { .readFn = readFn, }, }; - try stream.blocks.resize(block_count); - var file_stream = io.FileInStream.init(file); const in = &file_stream.stream; try file.seekTo(pos); @@ -248,8 +268,8 @@ const MsfStream = struct { warn("stream with blocks"); var i: u32 = 0; while (i < block_count) : (i += 1) { - stream.blocks.items[i] = try in.readIntLe(u32); - warn(" {}", stream.blocks.items[i]); + stream.blocks[i] = try in.readIntLe(u32); + warn(" {}", stream.blocks[i]); } warn("\n"); @@ -270,9 +290,13 @@ const MsfStream = struct { fn read(self: *MsfStream, buffer: []u8) !usize { var block_id = self.pos / self.block_size; - var block = self.blocks.items[block_id]; + var block = self.blocks[block_id]; var offset = self.pos % self.block_size; + //std.debug.warn("seek {} read {}B: block_id={} block={} offset={}\n", + // block * self.block_size + offset, + // buffer.len, block_id, block, offset); + try self.in_file.seekTo(block * self.block_size + offset); var file_stream = io.FileInStream.init(self.in_file); const in = &file_stream.stream; @@ -285,11 +309,10 @@ const MsfStream = struct { size += 1; // If we're at the end of a block, go to the next one. - if (offset == self.block_size) - { + if (offset == self.block_size) { offset = 0; block_id += 1; - block = self.blocks.items[block_id]; + block = self.blocks[block_id]; try self.in_file.seekTo(block * self.block_size); } } @@ -314,9 +337,9 @@ const MsfStream = struct { return self.blocks.len * self.block_size; } - fn getFilePos(self: *const MsfStream) usize { + fn getFilePos(self: MsfStream) usize { const block_id = self.pos / self.block_size; - const block = self.blocks.items[block_id]; + const block = self.blocks[block_id]; const offset = self.pos % self.block_size; return block * self.block_size + offset; -- cgit v1.2.3 From 44f908d2e63d761e183e266248e777fe74184f44 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Aug 2018 15:33:50 -0400 Subject: figuring out which module an address belongs in --- std/debug/index.zig | 35 +++-- std/pdb.zig | 425 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 419 insertions(+), 41 deletions(-) (limited to 'std/pdb.zig') diff --git a/std/debug/index.zig b/std/debug/index.zig index 36c5a6bdc9..5bdfb52186 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -408,26 +408,36 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { } fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { - var coff_file: coff.Coff = undefined; - coff_file.in_file = try os.openSelfExe(); - coff_file.allocator = allocator; - defer coff_file.in_file.close(); + const self_file = try os.openSelfExe(); + defer self_file.close(); + + const coff_obj = try allocator.createOne(coff.Coff); + coff_obj.* = coff.Coff{ + .in_file = self_file, + .allocator = allocator, + .coff_header = undefined, + .pe_header = undefined, + .sections = undefined, + .guid = undefined, + .age = undefined, + }; + + var di = DebugInfo{ + .coff = coff_obj, + .pdb = undefined, + }; - try coff_file.loadHeader(); + try di.coff.loadHeader(); var path_buf: [windows.MAX_PATH]u8 = undefined; - const len = try coff_file.getPdbPath(path_buf[0..]); + const len = try di.coff.getPdbPath(path_buf[0..]); const raw_path = path_buf[0..len]; std.debug.warn("pdb raw path {}\n", raw_path); const path = try os.path.resolve(allocator, raw_path); std.debug.warn("pdb resolved path {}\n", path); - var di = DebugInfo{ - .pdb = undefined, - }; - - try di.pdb.openFile(allocator, path); + try di.pdb.openFile(di.coff, path); var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; std.debug.warn("pdb real filepos {}\n", pdb_stream.getFilePos()); @@ -436,7 +446,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { const age = try pdb_stream.stream.readIntLe(u32); var guid: [16]u8 = undefined; try pdb_stream.stream.readNoEof(guid[0..]); - if (!mem.eql(u8, coff_file.guid, guid) or coff_file.age != age) + if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age) return error.InvalidDebugInfo; std.debug.warn("v {} s {} a {}\n", version, signature, age); // We validated the executable and pdb match. @@ -644,6 +654,7 @@ pub const DebugInfo = switch (builtin.os) { }, builtin.Os.windows => struct { pdb: pdb.Pdb, + coff: *coff.Coff, }, builtin.Os.linux => struct { self_exe_file: os.File, diff --git a/std/pdb.zig b/std/pdb.zig index 6f086b59e0..14c584a9f7 100644 --- a/std/pdb.zig +++ b/std/pdb.zig @@ -5,6 +5,7 @@ const math = std.math; const mem = std.mem; const os = std.os; const warn = std.debug.warn; +const Coff = std.coff.Coff; const ArrayList = std.ArrayList; @@ -20,8 +21,8 @@ const DbiStreamHeader = packed struct { SymRecordStream: u16, PdbDllRbld: u16, ModInfoSize: u32, - SectionContributionSize: i32, - SectionMapSize: i32, + SectionContributionSize: u32, + SectionMapSize: u32, SourceInfoSize: i32, TypeServerSize: i32, MFCTypeServerIndex: u32, @@ -35,8 +36,8 @@ const DbiStreamHeader = packed struct { const SectionContribEntry = packed struct { Section: u16, Padding1: [2]u8, - Offset: i32, - Size: i32, + Offset: u32, + Size: u32, Characteristics: u32, ModuleIndex: u16, Padding2: [2]u8, @@ -62,6 +63,22 @@ const ModInfo = packed struct { //ObjFileName: char[], }; +const SectionMapHeader = packed struct { + Count: u16, /// Number of segment descriptors + LogCount: u16, /// Number of logical segment descriptors +}; + +const SectionMapEntry = packed struct { + Flags: u16 , /// See the SectionMapEntryFlags enum below. + Ovl: u16 , /// Logical overlay number + Group: u16 , /// Group index into descriptor array. + Frame: u16 , + SectionName: u16 , /// Byte index of segment / group name in string table, or 0xFFFF. + ClassName: u16 , /// Byte index of class in string table, or 0xFFFF. + Offset: u32 , /// Byte offset of the logical segment within physical segment. If group is set in flags, this is the offset of the group. + SectionLength: u32 , /// Byte count of the segment or group. +}; + pub const StreamType = enum(u16) { Pdb = 1, Tpi = 2, @@ -69,24 +86,306 @@ pub const StreamType = enum(u16) { Ipi = 4, }; +const Module = struct { + mod_info: ModInfo, + module_name: []u8, + obj_file_name: []u8, +}; + +/// Distinguishes individual records in the Symbols subsection of a .debug$S +/// section. Equivalent to SYM_ENUM_e in cvinfo.h. +pub const SymbolRecordKind = enum(u16) { + InlineesSym = 4456, + ScopeEndSym = 6, + InlineSiteEnd = 4430, + ProcEnd = 4431, + Thunk32Sym = 4354, + TrampolineSym = 4396, + SectionSym = 4406, + CoffGroupSym = 4407, + ExportSym = 4408, + ProcSym = 4367, + GlobalProcSym = 4368, + ProcIdSym = 4422, + GlobalProcIdSym = 4423, + DPCProcSym = 4437, + DPCProcIdSym = 4438, + RegisterSym = 4358, + PublicSym32 = 4366, + ProcRefSym = 4389, + LocalProcRef = 4391, + EnvBlockSym = 4413, + InlineSiteSym = 4429, + LocalSym = 4414, + DefRangeSym = 4415, + DefRangeSubfieldSym = 4416, + DefRangeRegisterSym = 4417, + DefRangeFramePointerRelSym = 4418, + DefRangeSubfieldRegisterSym = 4419, + DefRangeFramePointerRelFullScopeSym = 4420, + DefRangeRegisterRelSym = 4421, + BlockSym = 4355, + LabelSym = 4357, + ObjNameSym = 4353, + Compile2Sym = 4374, + Compile3Sym = 4412, + FrameProcSym = 4114, + CallSiteInfoSym = 4409, + FileStaticSym = 4435, + HeapAllocationSiteSym = 4446, + FrameCookieSym = 4410, + CallerSym = 4442, + CalleeSym = 4443, + UDTSym = 4360, + CobolUDT = 4361, + BuildInfoSym = 4428, + BPRelativeSym = 4363, + RegRelativeSym = 4369, + ConstantSym = 4359, + ManagedConstant = 4397, + DataSym = 4364, + GlobalData = 4365, + ManagedLocalData = 4380, + ManagedGlobalData = 4381, + ThreadLocalDataSym = 4370, + GlobalTLS = 4371, +}; + +/// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful +/// for reference purposes and when dealing with unknown record types. +pub const SymbolKind = enum(u16) { + S_COMPILE = 1, + S_REGISTER_16t = 2, + S_CONSTANT_16t = 3, + S_UDT_16t = 4, + S_SSEARCH = 5, + S_SKIP = 7, + S_CVRESERVE = 8, + S_OBJNAME_ST = 9, + S_ENDARG = 10, + S_COBOLUDT_16t = 11, + S_MANYREG_16t = 12, + S_RETURN = 13, + S_ENTRYTHIS = 14, + S_BPREL16 = 256, + S_LDATA16 = 257, + S_GDATA16 = 258, + S_PUB16 = 259, + S_LPROC16 = 260, + S_GPROC16 = 261, + S_THUNK16 = 262, + S_BLOCK16 = 263, + S_WITH16 = 264, + S_LABEL16 = 265, + S_CEXMODEL16 = 266, + S_VFTABLE16 = 267, + S_REGREL16 = 268, + S_BPREL32_16t = 512, + S_LDATA32_16t = 513, + S_GDATA32_16t = 514, + S_PUB32_16t = 515, + S_LPROC32_16t = 516, + S_GPROC32_16t = 517, + S_THUNK32_ST = 518, + S_BLOCK32_ST = 519, + S_WITH32_ST = 520, + S_LABEL32_ST = 521, + S_CEXMODEL32 = 522, + S_VFTABLE32_16t = 523, + S_REGREL32_16t = 524, + S_LTHREAD32_16t = 525, + S_GTHREAD32_16t = 526, + S_SLINK32 = 527, + S_LPROCMIPS_16t = 768, + S_GPROCMIPS_16t = 769, + S_PROCREF_ST = 1024, + S_DATAREF_ST = 1025, + S_ALIGN = 1026, + S_LPROCREF_ST = 1027, + S_OEM = 1028, + S_TI16_MAX = 4096, + S_REGISTER_ST = 4097, + S_CONSTANT_ST = 4098, + S_UDT_ST = 4099, + S_COBOLUDT_ST = 4100, + S_MANYREG_ST = 4101, + S_BPREL32_ST = 4102, + S_LDATA32_ST = 4103, + S_GDATA32_ST = 4104, + S_PUB32_ST = 4105, + S_LPROC32_ST = 4106, + S_GPROC32_ST = 4107, + S_VFTABLE32 = 4108, + S_REGREL32_ST = 4109, + S_LTHREAD32_ST = 4110, + S_GTHREAD32_ST = 4111, + S_LPROCMIPS_ST = 4112, + S_GPROCMIPS_ST = 4113, + S_COMPILE2_ST = 4115, + S_MANYREG2_ST = 4116, + S_LPROCIA64_ST = 4117, + S_GPROCIA64_ST = 4118, + S_LOCALSLOT_ST = 4119, + S_PARAMSLOT_ST = 4120, + S_ANNOTATION = 4121, + S_GMANPROC_ST = 4122, + S_LMANPROC_ST = 4123, + S_RESERVED1 = 4124, + S_RESERVED2 = 4125, + S_RESERVED3 = 4126, + S_RESERVED4 = 4127, + S_LMANDATA_ST = 4128, + S_GMANDATA_ST = 4129, + S_MANFRAMEREL_ST = 4130, + S_MANREGISTER_ST = 4131, + S_MANSLOT_ST = 4132, + S_MANMANYREG_ST = 4133, + S_MANREGREL_ST = 4134, + S_MANMANYREG2_ST = 4135, + S_MANTYPREF = 4136, + S_UNAMESPACE_ST = 4137, + S_ST_MAX = 4352, + S_WITH32 = 4356, + S_MANYREG = 4362, + S_LPROCMIPS = 4372, + S_GPROCMIPS = 4373, + S_MANYREG2 = 4375, + S_LPROCIA64 = 4376, + S_GPROCIA64 = 4377, + S_LOCALSLOT = 4378, + S_PARAMSLOT = 4379, + S_MANFRAMEREL = 4382, + S_MANREGISTER = 4383, + S_MANSLOT = 4384, + S_MANMANYREG = 4385, + S_MANREGREL = 4386, + S_MANMANYREG2 = 4387, + S_UNAMESPACE = 4388, + S_DATAREF = 4390, + S_ANNOTATIONREF = 4392, + S_TOKENREF = 4393, + S_GMANPROC = 4394, + S_LMANPROC = 4395, + S_ATTR_FRAMEREL = 4398, + S_ATTR_REGISTER = 4399, + S_ATTR_REGREL = 4400, + S_ATTR_MANYREG = 4401, + S_SEPCODE = 4402, + S_LOCAL_2005 = 4403, + S_DEFRANGE_2005 = 4404, + S_DEFRANGE2_2005 = 4405, + S_DISCARDED = 4411, + S_LPROCMIPS_ID = 4424, + S_GPROCMIPS_ID = 4425, + S_LPROCIA64_ID = 4426, + S_GPROCIA64_ID = 4427, + S_DEFRANGE_HLSL = 4432, + S_GDATA_HLSL = 4433, + S_LDATA_HLSL = 4434, + S_LOCAL_DPC_GROUPSHARED = 4436, + S_DEFRANGE_DPC_PTR_TAG = 4439, + S_DPC_SYM_TAG_MAP = 4440, + S_ARMSWITCHTABLE = 4441, + S_POGODATA = 4444, + S_INLINESITE2 = 4445, + S_MOD_TYPEREF = 4447, + S_REF_MINIPDB = 4448, + S_PDBMAP = 4449, + S_GDATA_HLSL32 = 4450, + S_LDATA_HLSL32 = 4451, + S_GDATA_HLSL32_EX = 4452, + S_LDATA_HLSL32_EX = 4453, + S_FASTLINK = 4455, + S_INLINEES = 4456, + S_END = 6, + S_INLINESITE_END = 4430, + S_PROC_ID_END = 4431, + S_THUNK32 = 4354, + S_TRAMPOLINE = 4396, + S_SECTION = 4406, + S_COFFGROUP = 4407, + S_EXPORT = 4408, + S_LPROC32 = 4367, + S_GPROC32 = 4368, + S_LPROC32_ID = 4422, + S_GPROC32_ID = 4423, + S_LPROC32_DPC = 4437, + S_LPROC32_DPC_ID = 4438, + S_REGISTER = 4358, + S_PUB32 = 4366, + S_PROCREF = 4389, + S_LPROCREF = 4391, + S_ENVBLOCK = 4413, + S_INLINESITE = 4429, + S_LOCAL = 4414, + S_DEFRANGE = 4415, + S_DEFRANGE_SUBFIELD = 4416, + S_DEFRANGE_REGISTER = 4417, + S_DEFRANGE_FRAMEPOINTER_REL = 4418, + S_DEFRANGE_SUBFIELD_REGISTER = 4419, + S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE = 4420, + S_DEFRANGE_REGISTER_REL = 4421, + S_BLOCK32 = 4355, + S_LABEL32 = 4357, + S_OBJNAME = 4353, + S_COMPILE2 = 4374, + S_COMPILE3 = 4412, + S_FRAMEPROC = 4114, + S_CALLSITEINFO = 4409, + S_FILESTATIC = 4435, + S_HEAPALLOCSITE = 4446, + S_FRAMECOOKIE = 4410, + S_CALLEES = 4442, + S_CALLERS = 4443, + S_UDT = 4360, + S_COBOLUDT = 4361, + S_BUILDINFO = 4428, + S_BPREL32 = 4363, + S_REGREL32 = 4369, + S_CONSTANT = 4359, + S_MANCONSTANT = 4397, + S_LDATA32 = 4364, + S_GDATA32 = 4365, + S_LMANDATA = 4380, + S_GMANDATA = 4381, + S_LTHREAD32 = 4370, + S_GTHREAD32 = 4371, +}; + +const SectionContrSubstreamVersion = enum(u32) { + Ver60 = 0xeffe0000 + 19970605, + V2 = 0xeffe0000 + 20140516 +}; + +const RecordPrefix = packed struct { + RecordLen: u16, /// Record length, starting from &RecordKind. + RecordKind: u16, /// Record kind enum (SymRecordKind or TypeRecordKind) +}; + pub const Pdb = struct { in_file: os.File, allocator: *mem.Allocator, + coff: *Coff, msf: Msf, - pub fn openFile(self: *Pdb, allocator: *mem.Allocator, file_name: []u8) !void { + pub fn openFile(self: *Pdb, coff: *Coff, file_name: []u8) !void { self.in_file = try os.File.openRead(file_name); - self.allocator = allocator; + self.allocator = coff.allocator; + self.coff = coff; - try self.msf.openFile(allocator, self.in_file); + try self.msf.openFile(self.allocator, self.in_file); + } + + pub fn getStreamById(self: *Pdb, id: u32) ?*MsfStream { + if (id >= self.msf.streams.len) + return null; + return &self.msf.streams[id]; } pub fn getStream(self: *Pdb, stream: StreamType) ?*MsfStream { const id = @enumToInt(stream); - if (id < self.msf.streams.len) - return &self.msf.streams[id]; - return null; + return self.getStreamById(id); } pub fn getSourceLine(self: *Pdb, address: usize) !void { @@ -98,38 +397,106 @@ pub const Pdb = struct { std.debug.warn("{}\n", header); warn("after header dbi stream at {} (file offset)\n", dbi.getFilePos()); + var modules = ArrayList(Module).init(self.allocator); + // Module Info Substream var mod_info_offset: usize = 0; - while (mod_info_offset < header.ModInfoSize) { - const march_forward_bytes = dbi.getFilePos() % 4; - if (march_forward_bytes != 0) { - try dbi.seekForward(march_forward_bytes); - mod_info_offset += march_forward_bytes; - } + while (mod_info_offset != header.ModInfoSize) { var mod_info: ModInfo = undefined; try dbi.stream.readStruct(ModInfo, &mod_info); std.debug.warn("{}\n", mod_info); - mod_info_offset += @sizeOf(ModInfo); + var this_record_len: usize = @sizeOf(ModInfo); const module_name = try dbi.readNullTermString(self.allocator); std.debug.warn("module_name '{}'\n", module_name); - mod_info_offset += module_name.len + 1; - - //if (mem.eql(u8, module_name, "piler_rt.obj")) { - // std.debug.warn("detected bad thing\n"); - // try dbi.seekTo(dbi.pos - - // "c:\\msys64\\home\\andy\\zig\\build-llvm6-msvc-release\\.\\zig-cache\\compiler_rt.obj\x00".len - - // @sizeOf(ModInfo)); - // mod_info_offset -= module_name.len + 1; - // continue; - //} + this_record_len += module_name.len + 1; const obj_file_name = try dbi.readNullTermString(self.allocator); std.debug.warn("obj_file_name '{}'\n", obj_file_name); - mod_info_offset += obj_file_name.len + 1; + this_record_len += obj_file_name.len + 1; + + const march_forward_bytes = this_record_len % 4; + if (march_forward_bytes != 0) { + try dbi.seekForward(march_forward_bytes); + this_record_len += march_forward_bytes; + } + + try modules.append(Module{ + .mod_info = mod_info, + .module_name = module_name, + .obj_file_name = obj_file_name, + }); + + mod_info_offset += this_record_len; + if (mod_info_offset > header.ModInfoSize) + return error.InvalidDebugInfo; } - std.debug.warn("end modules\n"); + // Section Contribution Substream + var sect_contribs = ArrayList(SectionContribEntry).init(self.allocator); + std.debug.warn("looking at Section Contributinos now\n"); + var sect_cont_offset: usize = 0; + if (header.SectionContributionSize != 0) { + const ver = @intToEnum(SectionContrSubstreamVersion, try dbi.stream.readIntLe(u32)); + if (ver != SectionContrSubstreamVersion.Ver60) + return error.InvalidDebugInfo; + sect_cont_offset += @sizeOf(u32); + } + while (sect_cont_offset != header.SectionContributionSize) { + const entry = try sect_contribs.addOne(); + try dbi.stream.readStruct(SectionContribEntry, entry); + std.debug.warn("{}\n", entry); + sect_cont_offset += @sizeOf(SectionContribEntry); + + if (sect_cont_offset > header.SectionContributionSize) + return error.InvalidDebugInfo; + } + //std.debug.warn("looking at section map now\n"); + //if (header.SectionMapSize == 0) + // return error.MissingDebugInfo; + + //var sect_map_hdr: SectionMapHeader = undefined; + //try dbi.stream.readStruct(SectionMapHeader, §_map_hdr); + + //const sect_entries = try self.allocator.alloc(SectionMapEntry, sect_map_hdr.Count); + //const as_bytes = @sliceToBytes(sect_entries); + //if (as_bytes.len + @sizeOf(SectionMapHeader) != header.SectionMapSize) + // return error.InvalidDebugInfo; + //try dbi.stream.readNoEof(as_bytes); + + //for (sect_entries) |sect_entry| { + // std.debug.warn("{}\n", sect_entry); + //} + + const mod_index = for (sect_contribs.toSlice()) |sect_contrib| { + const coff_section = self.coff.sections.toSlice()[sect_contrib.Section]; + std.debug.warn("looking in coff name: {}\n", mem.toSliceConst(u8, &coff_section.header.name)); + + const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; + const vaddr_end = vaddr_start + sect_contrib.Size; + if (address >= vaddr_start and address < vaddr_end) { + std.debug.warn("found sect contrib: {}\n", sect_contrib); + break sect_contrib.ModuleIndex; + } + } else return error.MissingDebugInfo; + + const mod = &modules.toSlice()[mod_index]; + const modi = self.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.InvalidDebugInfo; + + const signature = try modi.stream.readIntLe(u32); + if (signature != 4) + return error.InvalidDebugInfo; + + const symbols = try self.allocator.alloc(u8, mod.mod_info.SymByteSize - 4); + std.debug.warn("read {} bytes of symbol info\n", symbols.len); + try modi.stream.readNoEof(symbols); + + if (mod.mod_info.C11ByteSize != 0) + return error.InvalidDebugInfo; + + if (mod.mod_info.C13ByteSize != 0) { + std.debug.warn("read C13 line info\n"); + } // TODO: locate corresponding source line information } -- cgit v1.2.3 From 72185e7dd36d9b87c57a3ed3719b9824ca11edf9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Aug 2018 16:57:55 -0400 Subject: finding the function that an address is in --- std/coff.zig | 2 +- std/pdb.zig | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 9 deletions(-) (limited to 'std/pdb.zig') diff --git a/std/coff.zig b/std/coff.zig index cce001d618..2921109bd6 100644 --- a/std/coff.zig +++ b/std/coff.zig @@ -215,7 +215,7 @@ const OptionalHeader = struct { data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory, }; -const Section = struct { +pub const Section = struct { header: SectionHeader, }; diff --git a/std/pdb.zig b/std/pdb.zig index 14c584a9f7..e67ca7cf0c 100644 --- a/std/pdb.zig +++ b/std/pdb.zig @@ -5,7 +5,7 @@ const math = std.math; const mem = std.mem; const os = std.os; const warn = std.debug.warn; -const Coff = std.coff.Coff; +const coff = std.coff; const ArrayList = std.ArrayList; @@ -153,7 +153,7 @@ pub const SymbolRecordKind = enum(u16) { /// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful /// for reference purposes and when dealing with unknown record types. -pub const SymbolKind = enum(u16) { +pub const SymbolKind = packed enum(u16) { S_COMPILE = 1, S_REGISTER_16t = 2, S_CONSTANT_16t = 3, @@ -352,6 +352,33 @@ pub const SymbolKind = enum(u16) { S_GTHREAD32 = 4371, }; +const TypeIndex = u32; + +const ProcSym = packed struct { + Parent: u32 , + End: u32 , + Next: u32 , + CodeSize: u32 , + DbgStart: u32 , + DbgEnd: u32 , + FunctionType: TypeIndex , + CodeOffset: u32, + Segment: u16, + Flags: ProcSymFlags, + Name: u8, +}; + +const ProcSymFlags = packed struct { + HasFP: bool, + HasIRET: bool, + HasFRET: bool, + IsNoReturn: bool, + IsUnreachable: bool, + HasCustomCallingConv: bool, + IsNoInline: bool, + HasOptimizedDebugInfo: bool, +}; + const SectionContrSubstreamVersion = enum(u32) { Ver60 = 0xeffe0000 + 19970605, V2 = 0xeffe0000 + 20140516 @@ -359,20 +386,20 @@ const SectionContrSubstreamVersion = enum(u32) { const RecordPrefix = packed struct { RecordLen: u16, /// Record length, starting from &RecordKind. - RecordKind: u16, /// Record kind enum (SymRecordKind or TypeRecordKind) + RecordKind: SymbolKind, /// Record kind enum (SymRecordKind or TypeRecordKind) }; pub const Pdb = struct { in_file: os.File, allocator: *mem.Allocator, - coff: *Coff, + coff: *coff.Coff, msf: Msf, - pub fn openFile(self: *Pdb, coff: *Coff, file_name: []u8) !void { + pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void { self.in_file = try os.File.openRead(file_name); - self.allocator = coff.allocator; - self.coff = coff; + self.allocator = coff_ptr.allocator; + self.coff = coff_ptr; try self.msf.openFile(self.allocator, self.in_file); } @@ -468,8 +495,9 @@ pub const Pdb = struct { // std.debug.warn("{}\n", sect_entry); //} + var coff_section: *coff.Section = undefined; const mod_index = for (sect_contribs.toSlice()) |sect_contrib| { - const coff_section = self.coff.sections.toSlice()[sect_contrib.Section]; + coff_section = &self.coff.sections.toSlice()[sect_contrib.Section]; std.debug.warn("looking in coff name: {}\n", mem.toSliceConst(u8, &coff_section.header.name)); const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; @@ -490,6 +518,26 @@ pub const Pdb = struct { const symbols = try self.allocator.alloc(u8, mod.mod_info.SymByteSize - 4); std.debug.warn("read {} bytes of symbol info\n", symbols.len); try modi.stream.readNoEof(symbols); + var symbol_i: usize = 0; + const proc_sym = while (symbol_i != symbols.len) { + const prefix = @ptrCast(*RecordPrefix, &symbols[symbol_i]); + if (prefix.RecordLen < 2) + return error.InvalidDebugInfo; + if (prefix.RecordKind == SymbolKind.S_LPROC32) { + const proc_sym = @ptrCast(*ProcSym, &symbols[symbol_i + @sizeOf(RecordPrefix)]); + const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; + const vaddr_end = vaddr_start + proc_sym.CodeSize; + std.debug.warn(" {}\n", proc_sym); + if (address >= vaddr_start and address < vaddr_end) { + break proc_sym; + } + } + symbol_i += prefix.RecordLen + @sizeOf(u16); + if (symbol_i > symbols.len) + return error.InvalidDebugInfo; + } else return error.MissingDebugInfo; + + std.debug.warn("found in {s}: {}\n", ([*]u8)((*[1]u8)(&proc_sym.Name)), proc_sym); if (mod.mod_info.C11ByteSize != 0) return error.InvalidDebugInfo; -- cgit v1.2.3 From 99170aa13db236b47aa8fda4a3d94b49e6b7f93c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 31 Aug 2018 01:01:37 -0400 Subject: finding source file, line, and column info --- std/io.zig | 2 +- std/pdb.zig | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 118 insertions(+), 6 deletions(-) (limited to 'std/pdb.zig') diff --git a/std/io.zig b/std/io.zig index 2de52493b1..a1a77271e1 100644 --- a/std/io.zig +++ b/std/io.zig @@ -210,7 +210,7 @@ pub fn InStream(comptime ReadError: type) type { pub fn readStruct(self: *Self, comptime T: type, ptr: *T) !void { // Only extern and packed structs have defined in-memory layout. - assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto); + comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto); return self.readNoEof(@sliceToBytes((*[1]T)(ptr)[0..])); } }; diff --git a/std/pdb.zig b/std/pdb.zig index e67ca7cf0c..c1fb863b67 100644 --- a/std/pdb.zig +++ b/std/pdb.zig @@ -365,7 +365,8 @@ const ProcSym = packed struct { CodeOffset: u32, Segment: u16, Flags: ProcSymFlags, - Name: u8, + // following is a null terminated string + // Name: [*]u8, }; const ProcSymFlags = packed struct { @@ -389,6 +390,51 @@ const RecordPrefix = packed struct { RecordKind: SymbolKind, /// Record kind enum (SymRecordKind or TypeRecordKind) }; +const LineFragmentHeader = packed struct { + RelocOffset: u32, /// Code offset of line contribution. + RelocSegment: u16, /// Code segment of line contribution. + Flags: LineFlags, + CodeSize: u32, /// Code size of this line contribution. +}; + +const LineFlags = packed struct { + LF_HaveColumns: bool, /// CV_LINES_HAVE_COLUMNS + unused: u15, +}; + +/// The following two variable length arrays appear immediately after the +/// header. The structure definitions follow. +/// LineNumberEntry Lines[NumLines]; +/// ColumnNumberEntry Columns[NumLines]; +const LineBlockFragmentHeader = packed struct { + /// Offset of FileChecksum entry in File + /// checksums buffer. The checksum entry then + /// contains another offset into the string + /// table of the actual name. + NameIndex: u32, + NumLines: u32, + BlockSize: u32, /// code size of block, in bytes +}; + + +const LineNumberEntry = packed struct { + Offset: u32, /// Offset to start of code bytes for line number + Flags: u32, + + /// TODO runtime crash when I make the actual type of Flags this + const Flags = packed struct { + Start: u24, + End: u7, + IsStatement: bool, + }; +}; + +const ColumnNumberEntry = packed struct { + StartColumn: u16, + EndColumn: u16, +}; + + pub const Pdb = struct { in_file: os.File, allocator: *mem.Allocator, @@ -537,16 +583,82 @@ pub const Pdb = struct { return error.InvalidDebugInfo; } else return error.MissingDebugInfo; - std.debug.warn("found in {s}: {}\n", ([*]u8)((*[1]u8)(&proc_sym.Name)), proc_sym); + std.debug.warn("found in {s}: {}\n", @ptrCast([*]u8, proc_sym) + @sizeOf(ProcSym), proc_sym); if (mod.mod_info.C11ByteSize != 0) return error.InvalidDebugInfo; - if (mod.mod_info.C13ByteSize != 0) { - std.debug.warn("read C13 line info\n"); + if (mod.mod_info.C13ByteSize == 0) { + return error.MissingDebugInfo; + } + + const line_info = try self.allocator.alloc(u8, mod.mod_info.C13ByteSize); + std.debug.warn("read C13 line info {} bytes\n", line_info.len); + try modi.stream.readNoEof(line_info); + + var line_index: usize = 0; + while (line_index != line_info.len) { + std.debug.warn("unknown bytes: {x2} {x2} {x2} {x2} {x2} {x2} {x2} {x2}\n", + line_info[line_index+0], + line_info[line_index+1], + line_info[line_index+2], + line_info[line_index+3], + line_info[line_index+4], + line_info[line_index+5], + line_info[line_index+6], + line_info[line_index+7], + ); + line_index += 8; + + const line_hdr = @ptrCast(*LineFragmentHeader, &line_info[line_index]); + if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; + std.debug.warn("{}\n", line_hdr); + line_index += @sizeOf(LineFragmentHeader); + + const block_hdr = @ptrCast(*LineBlockFragmentHeader, &line_info[line_index]); + std.debug.warn("{}\n", block_hdr); + line_index += @sizeOf(LineBlockFragmentHeader); + + const has_column = line_hdr.Flags.LF_HaveColumns; + std.debug.warn("has column: {}\n", has_column); + + const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; + const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; + if (address >= frag_vaddr_start and address < frag_vaddr_end) { + std.debug.warn("found line listing\n"); + var line_i: usize = 0; + const start_line_index = line_index; + while (line_i < block_hdr.NumLines) : (line_i += 1) { + const line_num_entry = @ptrCast(*LineNumberEntry, &line_info[line_index]); + line_index += @sizeOf(LineNumberEntry); + const flags = @ptrCast(*LineNumberEntry.Flags, &line_num_entry.Flags); + std.debug.warn("{} {}\n", line_num_entry, flags); + const vaddr_start = frag_vaddr_start + line_num_entry.Offset; + const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End; + std.debug.warn("test {x} <= {x} < {x}\n", vaddr_start, address, vaddr_end); + if (address >= vaddr_start and address < vaddr_end) { + std.debug.warn("{} line {}\n", block_hdr.NameIndex, flags.Start); + if (has_column) { + line_index = start_line_index + @sizeOf(LineNumberEntry) * block_hdr.NumLines; + line_index += @sizeOf(ColumnNumberEntry) * line_i; + const col_num_entry = @ptrCast(*ColumnNumberEntry, &line_info[line_index]); + std.debug.warn("col {}\n", col_num_entry.StartColumn); + } + return; + } + } + return error.MissingDebugInfo; + } else { + line_index += @sizeOf(LineNumberEntry) * block_hdr.NumLines; + if (has_column) + line_index += @sizeOf(ColumnNumberEntry) * block_hdr.NumLines; + } + + if (line_index > mod.mod_info.C13ByteSize) + return error.InvalidDebugInfo; } - // TODO: locate corresponding source line information + std.debug.warn("end line info\n"); } }; -- cgit v1.2.3 From b36b93fb3ef3ce96ecc457d0e379740b39d37f80 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 31 Aug 2018 15:02:41 -0400 Subject: awareness of debug subsections --- std/debug/index.zig | 28 ++++++ std/pdb.zig | 247 +++++++++++++++++++++++++--------------------------- 2 files changed, 147 insertions(+), 128 deletions(-) (limited to 'std/pdb.zig') diff --git a/std/debug/index.zig b/std/debug/index.zig index 5bdfb52186..3b32eafb01 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -451,6 +451,34 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { std.debug.warn("v {} s {} a {}\n", version, signature, age); // We validated the executable and pdb match. + const name_bytes_len = try pdb_stream.stream.readIntLe(u32); + const name_bytes = try allocator.alloc(u8, name_bytes_len); + try pdb_stream.stream.readNoEof(name_bytes); + + //const HashTableHeader = packed struct { + // Size: u32, + // Capacity: u32, + + // fn maxLoad(cap: u32) u32 { + // return cap * 2 / 3 + 1; + // } + //}; + //var hash_tbl_hdr: HashTableHeader = undefined; + //try pdb_stream.stream.readStruct(Header, &hash_tbl_hdr); + //if (hash_tbl_hdr.Capacity == 0) + // return error.InvalidDebugInfo; + + //if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) + // return error.InvalidDebugInfo; + + //std.debug.warn("{}\n", hash_tbl_hdr); + + //var more_buf: [100]u8 = undefined; + //const more_len = try pdb_stream.stream.read(more_buf[0..]); + //for (more_buf[0..more_len]) |x| { + // std.debug.warn("{x2} {c}\n", x, x); + //} + return di; } diff --git a/std/pdb.zig b/std/pdb.zig index c1fb863b67..1ee9b23383 100644 --- a/std/pdb.zig +++ b/std/pdb.zig @@ -92,65 +92,6 @@ const Module = struct { obj_file_name: []u8, }; -/// Distinguishes individual records in the Symbols subsection of a .debug$S -/// section. Equivalent to SYM_ENUM_e in cvinfo.h. -pub const SymbolRecordKind = enum(u16) { - InlineesSym = 4456, - ScopeEndSym = 6, - InlineSiteEnd = 4430, - ProcEnd = 4431, - Thunk32Sym = 4354, - TrampolineSym = 4396, - SectionSym = 4406, - CoffGroupSym = 4407, - ExportSym = 4408, - ProcSym = 4367, - GlobalProcSym = 4368, - ProcIdSym = 4422, - GlobalProcIdSym = 4423, - DPCProcSym = 4437, - DPCProcIdSym = 4438, - RegisterSym = 4358, - PublicSym32 = 4366, - ProcRefSym = 4389, - LocalProcRef = 4391, - EnvBlockSym = 4413, - InlineSiteSym = 4429, - LocalSym = 4414, - DefRangeSym = 4415, - DefRangeSubfieldSym = 4416, - DefRangeRegisterSym = 4417, - DefRangeFramePointerRelSym = 4418, - DefRangeSubfieldRegisterSym = 4419, - DefRangeFramePointerRelFullScopeSym = 4420, - DefRangeRegisterRelSym = 4421, - BlockSym = 4355, - LabelSym = 4357, - ObjNameSym = 4353, - Compile2Sym = 4374, - Compile3Sym = 4412, - FrameProcSym = 4114, - CallSiteInfoSym = 4409, - FileStaticSym = 4435, - HeapAllocationSiteSym = 4446, - FrameCookieSym = 4410, - CallerSym = 4442, - CalleeSym = 4443, - UDTSym = 4360, - CobolUDT = 4361, - BuildInfoSym = 4428, - BPRelativeSym = 4363, - RegRelativeSym = 4369, - ConstantSym = 4359, - ManagedConstant = 4397, - DataSym = 4364, - GlobalData = 4365, - ManagedLocalData = 4380, - ManagedGlobalData = 4381, - ThreadLocalDataSym = 4370, - GlobalTLS = 4371, -}; - /// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful /// for reference purposes and when dealing with unknown record types. pub const SymbolKind = packed enum(u16) { @@ -434,6 +375,32 @@ const ColumnNumberEntry = packed struct { EndColumn: u16, }; +/// Checksum bytes follow. +const FileChecksumEntryHeader = packed struct { + FileNameOffset: u32, /// Byte offset of filename in global string table. + ChecksumSize: u8, /// Number of bytes of checksum. + ChecksumKind: u8, /// FileChecksumKind +}; + +const DebugSubsectionKind = packed enum(u32) { + None = 0, + Symbols = 0xf1, + Lines = 0xf2, + StringTable = 0xf3, + FileChecksums = 0xf4, + FrameData = 0xf5, + InlineeLines = 0xf6, + CrossScopeImports = 0xf7, + CrossScopeExports = 0xf8, + + // These appear to relate to .Net assembly info. + ILLines = 0xf9, + FuncMDTokenMap = 0xfa, + TypeMDTokenMap = 0xfb, + MergedAssemblyInput = 0xfc, + + CoffSymbolRVA = 0xfd, +}; pub const Pdb = struct { in_file: os.File, @@ -569,14 +536,17 @@ pub const Pdb = struct { const prefix = @ptrCast(*RecordPrefix, &symbols[symbol_i]); if (prefix.RecordLen < 2) return error.InvalidDebugInfo; - if (prefix.RecordKind == SymbolKind.S_LPROC32) { - const proc_sym = @ptrCast(*ProcSym, &symbols[symbol_i + @sizeOf(RecordPrefix)]); - const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; - const vaddr_end = vaddr_start + proc_sym.CodeSize; - std.debug.warn(" {}\n", proc_sym); - if (address >= vaddr_start and address < vaddr_end) { - break proc_sym; - } + switch (prefix.RecordKind) { + SymbolKind.S_LPROC32 => { + const proc_sym = @ptrCast(*ProcSym, &symbols[symbol_i + @sizeOf(RecordPrefix)]); + const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; + const vaddr_end = vaddr_start + proc_sym.CodeSize; + std.debug.warn(" {}\n", proc_sym); + if (address >= vaddr_start and address < vaddr_end) { + break proc_sym; + } + }, + else => {}, } symbol_i += prefix.RecordLen + @sizeOf(u16); if (symbol_i > symbols.len) @@ -592,73 +562,94 @@ pub const Pdb = struct { return error.MissingDebugInfo; } - const line_info = try self.allocator.alloc(u8, mod.mod_info.C13ByteSize); - std.debug.warn("read C13 line info {} bytes\n", line_info.len); - try modi.stream.readNoEof(line_info); - - var line_index: usize = 0; - while (line_index != line_info.len) { - std.debug.warn("unknown bytes: {x2} {x2} {x2} {x2} {x2} {x2} {x2} {x2}\n", - line_info[line_index+0], - line_info[line_index+1], - line_info[line_index+2], - line_info[line_index+3], - line_info[line_index+4], - line_info[line_index+5], - line_info[line_index+6], - line_info[line_index+7], - ); - line_index += 8; - - const line_hdr = @ptrCast(*LineFragmentHeader, &line_info[line_index]); - if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; - std.debug.warn("{}\n", line_hdr); - line_index += @sizeOf(LineFragmentHeader); - - const block_hdr = @ptrCast(*LineBlockFragmentHeader, &line_info[line_index]); - std.debug.warn("{}\n", block_hdr); - line_index += @sizeOf(LineBlockFragmentHeader); - - const has_column = line_hdr.Flags.LF_HaveColumns; - std.debug.warn("has column: {}\n", has_column); - - const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; - const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; - if (address >= frag_vaddr_start and address < frag_vaddr_end) { - std.debug.warn("found line listing\n"); - var line_i: usize = 0; - const start_line_index = line_index; - while (line_i < block_hdr.NumLines) : (line_i += 1) { - const line_num_entry = @ptrCast(*LineNumberEntry, &line_info[line_index]); - line_index += @sizeOf(LineNumberEntry); - const flags = @ptrCast(*LineNumberEntry.Flags, &line_num_entry.Flags); - std.debug.warn("{} {}\n", line_num_entry, flags); - const vaddr_start = frag_vaddr_start + line_num_entry.Offset; - const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End; - std.debug.warn("test {x} <= {x} < {x}\n", vaddr_start, address, vaddr_end); - if (address >= vaddr_start and address < vaddr_end) { - std.debug.warn("{} line {}\n", block_hdr.NameIndex, flags.Start); - if (has_column) { - line_index = start_line_index + @sizeOf(LineNumberEntry) * block_hdr.NumLines; - line_index += @sizeOf(ColumnNumberEntry) * line_i; - const col_num_entry = @ptrCast(*ColumnNumberEntry, &line_info[line_index]); - std.debug.warn("col {}\n", col_num_entry.StartColumn); + const subsect_info = try self.allocator.alloc(u8, mod.mod_info.C13ByteSize); + std.debug.warn("read C13 line info {} bytes\n", subsect_info.len); + const line_info_file_pos = modi.getFilePos(); + try modi.stream.readNoEof(subsect_info); + + const DebugSubsectionHeader = packed struct { + Kind: DebugSubsectionKind, /// codeview::DebugSubsectionKind enum + Length: u32, /// number of bytes occupied by this record. + }; + var sect_offset: usize = 0; + var skip_len: usize = undefined; + var have_line_info: bool = false; + subsections: while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { + const subsect_hdr = @ptrCast(*DebugSubsectionHeader, &subsect_info[sect_offset]); + skip_len = subsect_hdr.Length; + sect_offset += @sizeOf(DebugSubsectionHeader); + + switch (subsect_hdr.Kind) { + DebugSubsectionKind.Lines => { + if (have_line_info) + continue :subsections; + + var line_index: usize = sect_offset; + + const line_hdr = @ptrCast(*LineFragmentHeader, &subsect_info[line_index]); + if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; + std.debug.warn("{}\n", line_hdr); + line_index += @sizeOf(LineFragmentHeader); + + const block_hdr = @ptrCast(*LineBlockFragmentHeader, &subsect_info[line_index]); + std.debug.warn("{}\n", block_hdr); + line_index += @sizeOf(LineBlockFragmentHeader); + + const has_column = line_hdr.Flags.LF_HaveColumns; + std.debug.warn("has column: {}\n", has_column); + + const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; + const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; + if (address >= frag_vaddr_start and address < frag_vaddr_end) { + std.debug.warn("found line listing\n"); + var line_i: usize = 0; + const start_line_index = line_index; + while (line_i < block_hdr.NumLines) : (line_i += 1) { + const line_num_entry = @ptrCast(*LineNumberEntry, &subsect_info[line_index]); + line_index += @sizeOf(LineNumberEntry); + const flags = @ptrCast(*LineNumberEntry.Flags, &line_num_entry.Flags); + std.debug.warn("{} {}\n", line_num_entry, flags); + const vaddr_start = frag_vaddr_start + line_num_entry.Offset; + const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End; + std.debug.warn("test {x} <= {x} < {x}\n", vaddr_start, address, vaddr_end); + if (address >= vaddr_start and address < vaddr_end) { + std.debug.warn("{} line {}\n", block_hdr.NameIndex, flags.Start); + if (has_column) { + line_index = start_line_index + @sizeOf(LineNumberEntry) * block_hdr.NumLines; + line_index += @sizeOf(ColumnNumberEntry) * line_i; + const col_num_entry = @ptrCast(*ColumnNumberEntry, &subsect_info[line_index]); + std.debug.warn("col {}\n", col_num_entry.StartColumn); + } + have_line_info = true; + continue :subsections; + } } - return; + return error.MissingDebugInfo; } - } - return error.MissingDebugInfo; - } else { - line_index += @sizeOf(LineNumberEntry) * block_hdr.NumLines; - if (has_column) - line_index += @sizeOf(ColumnNumberEntry) * block_hdr.NumLines; + + }, + DebugSubsectionKind.FileChecksums => { + var chksum_index: usize = sect_offset; + + while (chksum_index != subsect_info.len) { + const chksum_hdr = @ptrCast(*FileChecksumEntryHeader, &subsect_info[chksum_index]); + std.debug.warn("{}\n", chksum_hdr); + const len = @sizeOf(FileChecksumEntryHeader) + chksum_hdr.ChecksumSize; + chksum_index += len + (len % 4); + if (chksum_index > subsect_info.len) + return error.InvalidDebugInfo; + } + + }, + else => { + std.debug.warn("ignore subsection {}\n", @tagName(subsect_hdr.Kind)); + }, } - if (line_index > mod.mod_info.C13ByteSize) + if (sect_offset > subsect_info.len) return error.InvalidDebugInfo; } - - std.debug.warn("end line info\n"); + std.debug.warn("end subsections\n"); } }; -- cgit v1.2.3 From 6ddbd345aa085291f540e6d1b436edad32fe9a69 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 31 Aug 2018 19:50:03 -0400 Subject: figuring out where /names stream is --- std/debug/index.zig | 78 +++++++++++++++++++++++++++++++++++++---------------- std/pdb.zig | 1 + 2 files changed, 56 insertions(+), 23 deletions(-) (limited to 'std/pdb.zig') diff --git a/std/debug/index.zig b/std/debug/index.zig index 3b32eafb01..2883465931 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -455,33 +455,65 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { const name_bytes = try allocator.alloc(u8, name_bytes_len); try pdb_stream.stream.readNoEof(name_bytes); - //const HashTableHeader = packed struct { - // Size: u32, - // Capacity: u32, - - // fn maxLoad(cap: u32) u32 { - // return cap * 2 / 3 + 1; - // } - //}; - //var hash_tbl_hdr: HashTableHeader = undefined; - //try pdb_stream.stream.readStruct(Header, &hash_tbl_hdr); - //if (hash_tbl_hdr.Capacity == 0) - // return error.InvalidDebugInfo; - - //if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) - // return error.InvalidDebugInfo; - - //std.debug.warn("{}\n", hash_tbl_hdr); - - //var more_buf: [100]u8 = undefined; - //const more_len = try pdb_stream.stream.read(more_buf[0..]); - //for (more_buf[0..more_len]) |x| { - // std.debug.warn("{x2} {c}\n", x, x); - //} + const HashTableHeader = packed struct { + Size: u32, + Capacity: u32, + + fn maxLoad(cap: u32) u32 { + return cap * 2 / 3 + 1; + } + }; + var hash_tbl_hdr: HashTableHeader = undefined; + try pdb_stream.stream.readStruct(HashTableHeader, &hash_tbl_hdr); + if (hash_tbl_hdr.Capacity == 0) + return error.InvalidDebugInfo; + + if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) + return error.InvalidDebugInfo; + + std.debug.warn("{}\n", hash_tbl_hdr); + + const present = try readSparseBitVector(&pdb_stream.stream, allocator); + if (present.len != hash_tbl_hdr.Size) + return error.InvalidDebugInfo; + const deleted = try readSparseBitVector(&pdb_stream.stream, allocator); + + const Bucket = struct { + first: u32, + second: u32, + }; + const bucket_list = try allocator.alloc(Bucket, present.len); + const string_table_index = for (present) |_| { + const name_offset = try pdb_stream.stream.readIntLe(u32); + const name_index = try pdb_stream.stream.readIntLe(u32); + const name = mem.toSlice(u8, name_bytes.ptr + name_offset); + if (mem.eql(u8, name, "/names")) { + break name_index; + } + } else return error.MissingDebugInfo; + + di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo; return di; } +fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { + const num_words = try stream.readIntLe(u32); + var word_i: usize = 0; + var list = ArrayList(usize).init(allocator); + while (word_i != num_words) : (word_i += 1) { + const word = try stream.readIntLe(u32); + var bit_i: u5 = 0; + while (true) : (bit_i += 1) { + if (word & (u32(1) << bit_i) != 0) { + try list.append(word_i * 32 + bit_i); + } + if (bit_i == @maxValue(u5)) break; + } + } + return list.toOwnedSlice(); +} + fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo { var di = DebugInfo{ .self_exe_file = undefined, diff --git a/std/pdb.zig b/std/pdb.zig index 1ee9b23383..3aefc4f724 100644 --- a/std/pdb.zig +++ b/std/pdb.zig @@ -406,6 +406,7 @@ pub const Pdb = struct { in_file: os.File, allocator: *mem.Allocator, coff: *coff.Coff, + string_table: *MsfStream, msf: Msf, -- cgit v1.2.3 From 98dc943c0784b93ed28099bb75044c536174a144 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 2 Sep 2018 15:58:08 -0400 Subject: rework code to avoid duplicate operations --- std/coff.zig | 8 - std/debug/index.zig | 427 +++++++++++++++++++++++++++++++++++++++----- std/os/windows/index.zig | 13 ++ std/os/windows/kernel32.zig | 18 ++ std/pdb.zig | 287 ++++------------------------- 5 files changed, 448 insertions(+), 305 deletions(-) (limited to 'std/pdb.zig') diff --git a/std/coff.zig b/std/coff.zig index 2921109bd6..379fd1af42 100644 --- a/std/coff.zig +++ b/std/coff.zig @@ -83,7 +83,6 @@ pub const Coff = struct { fn loadOptionalHeader(self: *Coff, file_stream: *io.FileInStream) !void { const in = &file_stream.stream; self.pe_header.magic = try in.readIntLe(u16); - std.debug.warn("reading pe optional\n"); // For now we're only interested in finding the reference to the .pdb, // so we'll skip most of this header, which size is different in 32 // 64 bits by the way. @@ -97,11 +96,9 @@ pub const Coff = struct { else return error.InvalidPEMagic; - std.debug.warn("skipping {}\n", skip_size); try self.in_file.seekForward(skip_size); const number_of_rva_and_sizes = try in.readIntLe(u32); - //std.debug.warn("indicating {} data dirs\n", number_of_rva_and_sizes); if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES) return error.InvalidPEHeader; @@ -110,9 +107,7 @@ pub const Coff = struct { .virtual_address = try in.readIntLe(u32), .size = try in.readIntLe(u32), }; - //std.debug.warn("data_dir @ {x}, size {}\n", data_dir.virtual_address, data_dir.size); } - std.debug.warn("loaded data directories\n"); } pub fn getPdbPath(self: *Coff, buffer: []u8) !usize { @@ -123,7 +118,6 @@ pub const Coff = struct { // debug_directory. const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY]; const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data; - std.debug.warn("file offset {x}\n", file_offset); try self.in_file.seekTo(file_offset + debug_dir.size); var file_stream = io.FileInStream.init(self.in_file); @@ -134,7 +128,6 @@ pub const Coff = struct { // 'RSDS' indicates PDB70 format, used by lld. if (!mem.eql(u8, cv_signature, "RSDS")) return error.InvalidPEMagic; - std.debug.warn("cv_signature {}\n", cv_signature); try in.readNoEof(self.guid[0..]); self.age = try in.readIntLe(u32); @@ -181,7 +174,6 @@ pub const Coff = struct { }, }); } - std.debug.warn("loaded {} sections\n", self.coff_header.number_of_sections); } pub fn getSection(self: *Coff, comptime name: []const u8) ?*Section { diff --git a/std/debug/index.zig b/std/debug/index.zig index 2883465931..de4d7745c6 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -20,6 +20,17 @@ pub const runtime_safety = switch (builtin.mode) { builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false, }; +const Module = struct { + mod_info: pdb.ModInfo, + module_name: []u8, + obj_file_name: []u8, + + populated: bool, + symbols: []u8, + subsect_info: []u8, + checksums: []u32, +}; + /// Tries to write to stderr, unbuffered, and ignores any error returned. /// Does not append a newline. var stderr_file: os.File = undefined; @@ -258,12 +269,277 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: us } } -fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { +fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void { + const allocator = getDebugInfoAllocator(); const base_address = os.getBaseAddress(); - const relative_address = address - base_address; - std.debug.warn("{x} - {x} => {x}\n", address, base_address, relative_address); - try di.pdb.getSourceLine(relative_address); - return error.UnsupportedDebugInfo; + const relative_address = relocated_address - base_address; + + var coff_section: *coff.Section = undefined; + const mod_index = for (di.sect_contribs) |sect_contrib| { + coff_section = &di.coff.sections.toSlice()[sect_contrib.Section]; + + const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; + const vaddr_end = vaddr_start + sect_contrib.Size; + if (relative_address >= vaddr_start and relative_address < vaddr_end) { + break sect_contrib.ModuleIndex; + } + } else { + // we have no information to add to the address + if (tty_color) { + try out_stream.print("???:?:?: "); + setTtyColor(TtyColor.Dim); + try out_stream.print("0x{x} in ??? (???)", relocated_address); + setTtyColor(TtyColor.Reset); + try out_stream.print("\n\n\n"); + } else { + try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", relocated_address); + } + return; + }; + + const mod = &di.modules[mod_index]; + try populateModule(di, mod); + const obj_basename = os.path.basename(mod.obj_file_name); + + var symbol_i: usize = 0; + const symbol_name = while (symbol_i != mod.symbols.len) { + const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); + if (prefix.RecordLen < 2) + return error.InvalidDebugInfo; + switch (prefix.RecordKind) { + pdb.SymbolKind.S_LPROC32 => { + const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); + const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; + const vaddr_end = vaddr_start + proc_sym.CodeSize; + if (relative_address >= vaddr_start and relative_address < vaddr_end) { + break mem.toSliceConst(u8, @ptrCast([*]u8, proc_sym) + @sizeOf(pdb.ProcSym)); + } + }, + else => {}, + } + symbol_i += prefix.RecordLen + @sizeOf(u16); + if (symbol_i > mod.symbols.len) + return error.InvalidDebugInfo; + } else "???"; + + const subsect_info = mod.subsect_info; + + var sect_offset: usize = 0; + var skip_len: usize = undefined; + const opt_line_info = subsections: while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { + const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]); + skip_len = subsect_hdr.Length; + sect_offset += @sizeOf(pdb.DebugSubsectionHeader); + + switch (subsect_hdr.Kind) { + pdb.DebugSubsectionKind.Lines => { + var line_index: usize = sect_offset; + + const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); + if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; + line_index += @sizeOf(pdb.LineFragmentHeader); + + const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); + line_index += @sizeOf(pdb.LineBlockFragmentHeader); + + const has_column = line_hdr.Flags.LF_HaveColumns; + + const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; + const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; + if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) { + var line_i: usize = 0; + const start_line_index = line_index; + while (line_i < block_hdr.NumLines) : (line_i += 1) { + const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); + line_index += @sizeOf(pdb.LineNumberEntry); + const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); + const vaddr_start = frag_vaddr_start + line_num_entry.Offset; + const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End; + if (relative_address >= vaddr_start and relative_address < vaddr_end) { + const chksum_index = block_hdr.NameIndex; + std.debug.warn("looking up checksum {}\n", chksum_index); + const strtab_offset = mod.checksums[chksum_index]; + try di.pdb.string_table.seekTo(@sizeOf(pdb.PDBStringTableHeader) + strtab_offset); + const source_file_name = try di.pdb.string_table.readNullTermString(allocator); + const line = flags.Start; + const column = if (has_column) blk: { + line_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; + line_index += @sizeOf(pdb.ColumnNumberEntry) * line_i; + const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[line_index]); + break :blk col_num_entry.StartColumn; + } else 0; + break :subsections LineInfo{ + .allocator = allocator, + .file_name = source_file_name, + .line = line, + .column = column, + }; + } + } + break :subsections null; + } + }, + else => {}, + } + + if (sect_offset > subsect_info.len) + return error.InvalidDebugInfo; + } else null; + + if (tty_color) { + if (opt_line_info) |li| { + try out_stream.print("{}:{}:{}: ", li.file_name, li.line, li.column); + } else { + try out_stream.print("???:?:?: "); + } + setTtyColor(TtyColor.Dim); + try out_stream.print("0x{x} in {} ({})", relocated_address, symbol_name, obj_basename); + setTtyColor(TtyColor.Reset); + + if (opt_line_info) |line_info| { + try out_stream.print("\n"); + if (printLineFromFile(out_stream, line_info)) { + if (line_info.column == 0) { + try out_stream.write("\n"); + } else { + { + var col_i: usize = 1; + while (col_i < line_info.column) : (col_i += 1) { + try out_stream.writeByte(' '); + } + } + setTtyColor(TtyColor.Green); + try out_stream.write("^"); + setTtyColor(TtyColor.Reset); + try out_stream.write("\n"); + } + } else |err| switch (err) { + error.EndOfFile => {}, + else => return err, + } + } else { + try out_stream.print("\n\n\n"); + } + } else { + if (opt_line_info) |li| { + try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", li.file_name, li.line, li.column, relocated_address, symbol_name, obj_basename); + } else { + try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", relocated_address, symbol_name, obj_basename); + } + } +} + +const TtyColor = enum{ + Red, + Green, + Cyan, + White, + Dim, + Bold, + Reset, +}; + +/// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt +fn setTtyColor(tty_color: TtyColor) void { + const S = struct { + var attrs: windows.WORD = undefined; + var init_attrs = false; + }; + if (!S.init_attrs) { + S.init_attrs = true; + var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; + // TODO handle error + _ = windows.GetConsoleScreenBufferInfo(stderr_file.handle, &info); + S.attrs = info.wAttributes; + } + + // TODO handle errors + switch (tty_color) { + TtyColor.Red => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED|windows.FOREGROUND_INTENSITY); + }, + TtyColor.Green => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN|windows.FOREGROUND_INTENSITY); + }, + TtyColor.Cyan => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, + windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY); + }, + TtyColor.White, TtyColor.Bold => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, + windows.FOREGROUND_RED|windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY); + }, + TtyColor.Dim => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, + windows.FOREGROUND_RED|windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE); + }, + TtyColor.Reset => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs); + }, + } +} + +fn populateModule(di: *DebugInfo, mod: *Module) !void { + if (mod.populated) + return; + const allocator = getDebugInfoAllocator(); + + if (mod.mod_info.C11ByteSize != 0) + return error.InvalidDebugInfo; + + if (mod.mod_info.C13ByteSize == 0) + return error.MissingDebugInfo; + + const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo; + + const signature = try modi.stream.readIntLe(u32); + if (signature != 4) + return error.InvalidDebugInfo; + + mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4); + try modi.stream.readNoEof(mod.symbols); + + mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize); + try modi.stream.readNoEof(mod.subsect_info); + + var checksum_list = ArrayList(u32).init(allocator); + var sect_offset: usize = 0; + var skip_len: usize = undefined; + while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) { + const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &mod.subsect_info[sect_offset]); + skip_len = subsect_hdr.Length; + sect_offset += @sizeOf(pdb.DebugSubsectionHeader); + + switch (subsect_hdr.Kind) { + pdb.DebugSubsectionKind.FileChecksums => { + var chksum_index: usize = sect_offset; + + while (chksum_index != mod.subsect_info.len) { + const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[chksum_index]); + std.debug.warn("{} {}\n", checksum_list.len, chksum_hdr); + try checksum_list.append(chksum_hdr.FileNameOffset); + const len = @sizeOf(pdb.FileChecksumEntryHeader) + chksum_hdr.ChecksumSize; + chksum_index += len + (len % 4); + if (chksum_index > mod.subsect_info.len) + return error.InvalidDebugInfo; + } + + }, + else => {}, + } + + if (sect_offset > mod.subsect_info.len) + return error.InvalidDebugInfo; + } + mod.checksums = checksum_list.toOwnedSlice(); + + for (mod.checksums) |strtab_offset| { + try di.pdb.string_table.seekTo(@sizeOf(pdb.PDBStringTableHeader) + strtab_offset); + const source_file_name = try di.pdb.string_table.readNullTermString(allocator); + std.debug.warn("{}={}\n", strtab_offset, source_file_name); + } + + mod.populated = true; } fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol { @@ -425,6 +701,8 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { var di = DebugInfo{ .coff = coff_obj, .pdb = undefined, + .sect_contribs = undefined, + .modules = undefined, }; try di.coff.loadHeader(); @@ -432,15 +710,12 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { var path_buf: [windows.MAX_PATH]u8 = undefined; const len = try di.coff.getPdbPath(path_buf[0..]); const raw_path = path_buf[0..len]; - std.debug.warn("pdb raw path {}\n", raw_path); const path = try os.path.resolve(allocator, raw_path); - std.debug.warn("pdb resolved path {}\n", path); try di.pdb.openFile(di.coff, path); var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; - std.debug.warn("pdb real filepos {}\n", pdb_stream.getFilePos()); const version = try pdb_stream.stream.readIntLe(u32); const signature = try pdb_stream.stream.readIntLe(u32); const age = try pdb_stream.stream.readIntLe(u32); @@ -448,51 +723,119 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { try pdb_stream.stream.readNoEof(guid[0..]); if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age) return error.InvalidDebugInfo; - std.debug.warn("v {} s {} a {}\n", version, signature, age); // We validated the executable and pdb match. - const name_bytes_len = try pdb_stream.stream.readIntLe(u32); - const name_bytes = try allocator.alloc(u8, name_bytes_len); - try pdb_stream.stream.readNoEof(name_bytes); + const string_table_index = str_tab_index: { + const name_bytes_len = try pdb_stream.stream.readIntLe(u32); + const name_bytes = try allocator.alloc(u8, name_bytes_len); + try pdb_stream.stream.readNoEof(name_bytes); - const HashTableHeader = packed struct { - Size: u32, - Capacity: u32, + const HashTableHeader = packed struct { + Size: u32, + Capacity: u32, - fn maxLoad(cap: u32) u32 { - return cap * 2 / 3 + 1; + fn maxLoad(cap: u32) u32 { + return cap * 2 / 3 + 1; + } + }; + var hash_tbl_hdr: HashTableHeader = undefined; + try pdb_stream.stream.readStruct(HashTableHeader, &hash_tbl_hdr); + 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(&pdb_stream.stream, allocator); + if (present.len != hash_tbl_hdr.Size) + return error.InvalidDebugInfo; + const deleted = try readSparseBitVector(&pdb_stream.stream, allocator); + + const Bucket = struct { + first: u32, + second: u32, + }; + const bucket_list = try allocator.alloc(Bucket, present.len); + for (present) |_| { + const name_offset = try pdb_stream.stream.readIntLe(u32); + const name_index = try pdb_stream.stream.readIntLe(u32); + const name = mem.toSlice(u8, name_bytes.ptr + name_offset); + if (mem.eql(u8, name, "/names")) { + break :str_tab_index name_index; + } } + return error.MissingDebugInfo; }; - var hash_tbl_hdr: HashTableHeader = undefined; - try pdb_stream.stream.readStruct(HashTableHeader, &hash_tbl_hdr); - if (hash_tbl_hdr.Capacity == 0) - return error.InvalidDebugInfo; - if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) - return error.InvalidDebugInfo; + di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo; + di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; - std.debug.warn("{}\n", hash_tbl_hdr); + const dbi = di.pdb.dbi; - const present = try readSparseBitVector(&pdb_stream.stream, allocator); - if (present.len != hash_tbl_hdr.Size) - return error.InvalidDebugInfo; - const deleted = try readSparseBitVector(&pdb_stream.stream, allocator); + // Dbi Header + var dbi_stream_header: pdb.DbiStreamHeader = undefined; + try dbi.stream.readStruct(pdb.DbiStreamHeader, &dbi_stream_header); + const mod_info_size = dbi_stream_header.ModInfoSize; + const section_contrib_size = dbi_stream_header.SectionContributionSize; - const Bucket = struct { - first: u32, - second: u32, - }; - const bucket_list = try allocator.alloc(Bucket, present.len); - const string_table_index = for (present) |_| { - const name_offset = try pdb_stream.stream.readIntLe(u32); - const name_index = try pdb_stream.stream.readIntLe(u32); - const name = mem.toSlice(u8, name_bytes.ptr + name_offset); - if (mem.eql(u8, name, "/names")) { - break name_index; + var modules = ArrayList(Module).init(allocator); + + // Module Info Substream + var mod_info_offset: usize = 0; + while (mod_info_offset != mod_info_size) { + var mod_info: pdb.ModInfo = undefined; + try dbi.stream.readStruct(pdb.ModInfo, &mod_info); + var this_record_len: usize = @sizeOf(pdb.ModInfo); + + const module_name = try dbi.readNullTermString(allocator); + this_record_len += module_name.len + 1; + + const obj_file_name = try dbi.readNullTermString(allocator); + this_record_len += obj_file_name.len + 1; + + const march_forward_bytes = this_record_len % 4; + if (march_forward_bytes != 0) { + try dbi.seekForward(march_forward_bytes); + this_record_len += march_forward_bytes; } - } else return error.MissingDebugInfo; - di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo; + try modules.append(Module{ + .mod_info = mod_info, + .module_name = module_name, + .obj_file_name = obj_file_name, + + .populated = false, + .symbols = undefined, + .subsect_info = undefined, + .checksums = undefined, + }); + + mod_info_offset += this_record_len; + if (mod_info_offset > mod_info_size) + return error.InvalidDebugInfo; + } + + di.modules = modules.toOwnedSlice(); + + // Section Contribution Substream + var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); + var sect_cont_offset: usize = 0; + if (section_contrib_size != 0) { + const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLe(u32)); + if (ver != pdb.SectionContrSubstreamVersion.Ver60) + return error.InvalidDebugInfo; + sect_cont_offset += @sizeOf(u32); + } + while (sect_cont_offset != section_contrib_size) { + const entry = try sect_contribs.addOne(); + try dbi.stream.readStruct(pdb.SectionContribEntry, entry); + sect_cont_offset += @sizeOf(pdb.SectionContribEntry); + + if (sect_cont_offset > section_contrib_size) + return error.InvalidDebugInfo; + } + + di.sect_contribs = sect_contribs.toOwnedSlice(); return di; } @@ -715,6 +1058,8 @@ pub const DebugInfo = switch (builtin.os) { builtin.Os.windows => struct { pdb: pdb.Pdb, coff: *coff.Coff, + sect_contribs: []pdb.SectionContribEntry, + modules: []Module, }, builtin.Os.linux => struct { self_exe_file: os.File, diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index ca6299dc5e..9286b7d090 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -15,6 +15,7 @@ test "import" { pub const ERROR = @import("error.zig"); +pub const SHORT = c_short; pub const BOOL = c_int; pub const BOOLEAN = BYTE; pub const BYTE = u8; @@ -364,3 +365,15 @@ pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000; pub const FILE_FLAG_SESSION_AWARE = 0x00800000; pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; pub const FILE_FLAG_WRITE_THROUGH = 0x80000000; + +pub const SMALL_RECT = extern struct { + Left: SHORT, + Top: SHORT, + Right: SHORT, + Bottom: SHORT, +}; + +pub const COORD = extern struct { + X: SHORT, + Y: SHORT, +}; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 65f10a5a2a..ffa4422760 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -72,6 +72,8 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; +pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL; + pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD; pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD; @@ -179,6 +181,8 @@ pub extern "kernel32" stdcallcc fn ReadFile( pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL; +pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL; + pub extern "kernel32" stdcallcc fn SetFilePointerEx( in_fFile: HANDLE, in_liDistanceToMove: LARGE_INTEGER, @@ -234,3 +238,17 @@ pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16; pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2; pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1; pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4; + + +pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct { + dwSize: COORD, + dwCursorPosition: COORD, + wAttributes: WORD, + srWindow: SMALL_RECT, + dwMaximumWindowSize: COORD, +}; + +pub const FOREGROUND_BLUE = 1; +pub const FOREGROUND_GREEN = 2; +pub const FOREGROUND_RED = 4; +pub const FOREGROUND_INTENSITY = 8; diff --git a/std/pdb.zig b/std/pdb.zig index 3aefc4f724..907eddff04 100644 --- a/std/pdb.zig +++ b/std/pdb.zig @@ -10,7 +10,7 @@ const coff = std.coff; const ArrayList = std.ArrayList; // https://llvm.org/docs/PDB/DbiStream.html#stream-header -const DbiStreamHeader = packed struct { +pub const DbiStreamHeader = packed struct { VersionSignature: i32, VersionHeader: u32, Age: u32, @@ -33,7 +33,7 @@ const DbiStreamHeader = packed struct { Padding: u32, }; -const SectionContribEntry = packed struct { +pub const SectionContribEntry = packed struct { Section: u16, Padding1: [2]u8, Offset: u32, @@ -45,7 +45,7 @@ const SectionContribEntry = packed struct { RelocCrc: u32, }; -const ModInfo = packed struct { +pub const ModInfo = packed struct { Unused1: u32, SectionContr: SectionContribEntry, Flags: u16, @@ -63,12 +63,12 @@ const ModInfo = packed struct { //ObjFileName: char[], }; -const SectionMapHeader = packed struct { +pub const SectionMapHeader = packed struct { Count: u16, /// Number of segment descriptors LogCount: u16, /// Number of logical segment descriptors }; -const SectionMapEntry = packed struct { +pub const SectionMapEntry = packed struct { Flags: u16 , /// See the SectionMapEntryFlags enum below. Ovl: u16 , /// Logical overlay number Group: u16 , /// Group index into descriptor array. @@ -86,12 +86,6 @@ pub const StreamType = enum(u16) { Ipi = 4, }; -const Module = struct { - mod_info: ModInfo, - module_name: []u8, - obj_file_name: []u8, -}; - /// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful /// for reference purposes and when dealing with unknown record types. pub const SymbolKind = packed enum(u16) { @@ -293,9 +287,9 @@ pub const SymbolKind = packed enum(u16) { S_GTHREAD32 = 4371, }; -const TypeIndex = u32; +pub const TypeIndex = u32; -const ProcSym = packed struct { +pub const ProcSym = packed struct { Parent: u32 , End: u32 , Next: u32 , @@ -310,7 +304,7 @@ const ProcSym = packed struct { // Name: [*]u8, }; -const ProcSymFlags = packed struct { +pub const ProcSymFlags = packed struct { HasFP: bool, HasIRET: bool, HasFRET: bool, @@ -321,24 +315,24 @@ const ProcSymFlags = packed struct { HasOptimizedDebugInfo: bool, }; -const SectionContrSubstreamVersion = enum(u32) { +pub const SectionContrSubstreamVersion = enum(u32) { Ver60 = 0xeffe0000 + 19970605, V2 = 0xeffe0000 + 20140516 }; -const RecordPrefix = packed struct { +pub const RecordPrefix = packed struct { RecordLen: u16, /// Record length, starting from &RecordKind. RecordKind: SymbolKind, /// Record kind enum (SymRecordKind or TypeRecordKind) }; -const LineFragmentHeader = packed struct { +pub const LineFragmentHeader = packed struct { RelocOffset: u32, /// Code offset of line contribution. RelocSegment: u16, /// Code segment of line contribution. Flags: LineFlags, CodeSize: u32, /// Code size of this line contribution. }; -const LineFlags = packed struct { +pub const LineFlags = packed struct { LF_HaveColumns: bool, /// CV_LINES_HAVE_COLUMNS unused: u15, }; @@ -347,7 +341,7 @@ const LineFlags = packed struct { /// header. The structure definitions follow. /// LineNumberEntry Lines[NumLines]; /// ColumnNumberEntry Columns[NumLines]; -const LineBlockFragmentHeader = packed struct { +pub const LineBlockFragmentHeader = packed struct { /// Offset of FileChecksum entry in File /// checksums buffer. The checksum entry then /// contains another offset into the string @@ -358,7 +352,7 @@ const LineBlockFragmentHeader = packed struct { }; -const LineNumberEntry = packed struct { +pub const LineNumberEntry = packed struct { Offset: u32, /// Offset to start of code bytes for line number Flags: u32, @@ -370,19 +364,19 @@ const LineNumberEntry = packed struct { }; }; -const ColumnNumberEntry = packed struct { +pub const ColumnNumberEntry = packed struct { StartColumn: u16, EndColumn: u16, }; /// Checksum bytes follow. -const FileChecksumEntryHeader = packed struct { +pub const FileChecksumEntryHeader = packed struct { FileNameOffset: u32, /// Byte offset of filename in global string table. ChecksumSize: u8, /// Number of bytes of checksum. ChecksumKind: u8, /// FileChecksumKind }; -const DebugSubsectionKind = packed enum(u32) { +pub const DebugSubsectionKind = packed enum(u32) { None = 0, Symbols = 0xf1, Lines = 0xf2, @@ -402,11 +396,25 @@ const DebugSubsectionKind = packed enum(u32) { CoffSymbolRVA = 0xfd, }; + +pub const DebugSubsectionHeader = packed struct { + Kind: DebugSubsectionKind, /// codeview::DebugSubsectionKind enum + Length: u32, /// number of bytes occupied by this record. +}; + + +pub const PDBStringTableHeader = packed struct { + Signature: u32, /// PDBStringTableSignature + HashVersion: u32, /// 1 or 2 + ByteSize: u32, /// Number of bytes of names buffer. +}; + pub const Pdb = struct { in_file: os.File, allocator: *mem.Allocator, coff: *coff.Coff, string_table: *MsfStream, + dbi: *MsfStream, msf: Msf, @@ -428,230 +436,6 @@ pub const Pdb = struct { const id = @enumToInt(stream); return self.getStreamById(id); } - - pub fn getSourceLine(self: *Pdb, address: usize) !void { - const dbi = self.getStream(StreamType.Dbi) orelse return error.InvalidDebugInfo; - - // Dbi Header - var header: DbiStreamHeader = undefined; - try dbi.stream.readStruct(DbiStreamHeader, &header); - std.debug.warn("{}\n", header); - warn("after header dbi stream at {} (file offset)\n", dbi.getFilePos()); - - var modules = ArrayList(Module).init(self.allocator); - - // Module Info Substream - var mod_info_offset: usize = 0; - while (mod_info_offset != header.ModInfoSize) { - var mod_info: ModInfo = undefined; - try dbi.stream.readStruct(ModInfo, &mod_info); - std.debug.warn("{}\n", mod_info); - var this_record_len: usize = @sizeOf(ModInfo); - - const module_name = try dbi.readNullTermString(self.allocator); - std.debug.warn("module_name '{}'\n", module_name); - this_record_len += module_name.len + 1; - - const obj_file_name = try dbi.readNullTermString(self.allocator); - std.debug.warn("obj_file_name '{}'\n", obj_file_name); - this_record_len += obj_file_name.len + 1; - - const march_forward_bytes = this_record_len % 4; - if (march_forward_bytes != 0) { - try dbi.seekForward(march_forward_bytes); - this_record_len += march_forward_bytes; - } - - try modules.append(Module{ - .mod_info = mod_info, - .module_name = module_name, - .obj_file_name = obj_file_name, - }); - - mod_info_offset += this_record_len; - if (mod_info_offset > header.ModInfoSize) - return error.InvalidDebugInfo; - } - - // Section Contribution Substream - var sect_contribs = ArrayList(SectionContribEntry).init(self.allocator); - std.debug.warn("looking at Section Contributinos now\n"); - var sect_cont_offset: usize = 0; - if (header.SectionContributionSize != 0) { - const ver = @intToEnum(SectionContrSubstreamVersion, try dbi.stream.readIntLe(u32)); - if (ver != SectionContrSubstreamVersion.Ver60) - return error.InvalidDebugInfo; - sect_cont_offset += @sizeOf(u32); - } - while (sect_cont_offset != header.SectionContributionSize) { - const entry = try sect_contribs.addOne(); - try dbi.stream.readStruct(SectionContribEntry, entry); - std.debug.warn("{}\n", entry); - sect_cont_offset += @sizeOf(SectionContribEntry); - - if (sect_cont_offset > header.SectionContributionSize) - return error.InvalidDebugInfo; - } - //std.debug.warn("looking at section map now\n"); - //if (header.SectionMapSize == 0) - // return error.MissingDebugInfo; - - //var sect_map_hdr: SectionMapHeader = undefined; - //try dbi.stream.readStruct(SectionMapHeader, §_map_hdr); - - //const sect_entries = try self.allocator.alloc(SectionMapEntry, sect_map_hdr.Count); - //const as_bytes = @sliceToBytes(sect_entries); - //if (as_bytes.len + @sizeOf(SectionMapHeader) != header.SectionMapSize) - // return error.InvalidDebugInfo; - //try dbi.stream.readNoEof(as_bytes); - - //for (sect_entries) |sect_entry| { - // std.debug.warn("{}\n", sect_entry); - //} - - var coff_section: *coff.Section = undefined; - const mod_index = for (sect_contribs.toSlice()) |sect_contrib| { - coff_section = &self.coff.sections.toSlice()[sect_contrib.Section]; - std.debug.warn("looking in coff name: {}\n", mem.toSliceConst(u8, &coff_section.header.name)); - - const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; - const vaddr_end = vaddr_start + sect_contrib.Size; - if (address >= vaddr_start and address < vaddr_end) { - std.debug.warn("found sect contrib: {}\n", sect_contrib); - break sect_contrib.ModuleIndex; - } - } else return error.MissingDebugInfo; - - const mod = &modules.toSlice()[mod_index]; - const modi = self.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.InvalidDebugInfo; - - const signature = try modi.stream.readIntLe(u32); - if (signature != 4) - return error.InvalidDebugInfo; - - const symbols = try self.allocator.alloc(u8, mod.mod_info.SymByteSize - 4); - std.debug.warn("read {} bytes of symbol info\n", symbols.len); - try modi.stream.readNoEof(symbols); - var symbol_i: usize = 0; - const proc_sym = while (symbol_i != symbols.len) { - const prefix = @ptrCast(*RecordPrefix, &symbols[symbol_i]); - if (prefix.RecordLen < 2) - return error.InvalidDebugInfo; - switch (prefix.RecordKind) { - SymbolKind.S_LPROC32 => { - const proc_sym = @ptrCast(*ProcSym, &symbols[symbol_i + @sizeOf(RecordPrefix)]); - const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; - const vaddr_end = vaddr_start + proc_sym.CodeSize; - std.debug.warn(" {}\n", proc_sym); - if (address >= vaddr_start and address < vaddr_end) { - break proc_sym; - } - }, - else => {}, - } - symbol_i += prefix.RecordLen + @sizeOf(u16); - if (symbol_i > symbols.len) - return error.InvalidDebugInfo; - } else return error.MissingDebugInfo; - - std.debug.warn("found in {s}: {}\n", @ptrCast([*]u8, proc_sym) + @sizeOf(ProcSym), proc_sym); - - if (mod.mod_info.C11ByteSize != 0) - return error.InvalidDebugInfo; - - if (mod.mod_info.C13ByteSize == 0) { - return error.MissingDebugInfo; - } - - const subsect_info = try self.allocator.alloc(u8, mod.mod_info.C13ByteSize); - std.debug.warn("read C13 line info {} bytes\n", subsect_info.len); - const line_info_file_pos = modi.getFilePos(); - try modi.stream.readNoEof(subsect_info); - - const DebugSubsectionHeader = packed struct { - Kind: DebugSubsectionKind, /// codeview::DebugSubsectionKind enum - Length: u32, /// number of bytes occupied by this record. - }; - var sect_offset: usize = 0; - var skip_len: usize = undefined; - var have_line_info: bool = false; - subsections: while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { - const subsect_hdr = @ptrCast(*DebugSubsectionHeader, &subsect_info[sect_offset]); - skip_len = subsect_hdr.Length; - sect_offset += @sizeOf(DebugSubsectionHeader); - - switch (subsect_hdr.Kind) { - DebugSubsectionKind.Lines => { - if (have_line_info) - continue :subsections; - - var line_index: usize = sect_offset; - - const line_hdr = @ptrCast(*LineFragmentHeader, &subsect_info[line_index]); - if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; - std.debug.warn("{}\n", line_hdr); - line_index += @sizeOf(LineFragmentHeader); - - const block_hdr = @ptrCast(*LineBlockFragmentHeader, &subsect_info[line_index]); - std.debug.warn("{}\n", block_hdr); - line_index += @sizeOf(LineBlockFragmentHeader); - - const has_column = line_hdr.Flags.LF_HaveColumns; - std.debug.warn("has column: {}\n", has_column); - - const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; - const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; - if (address >= frag_vaddr_start and address < frag_vaddr_end) { - std.debug.warn("found line listing\n"); - var line_i: usize = 0; - const start_line_index = line_index; - while (line_i < block_hdr.NumLines) : (line_i += 1) { - const line_num_entry = @ptrCast(*LineNumberEntry, &subsect_info[line_index]); - line_index += @sizeOf(LineNumberEntry); - const flags = @ptrCast(*LineNumberEntry.Flags, &line_num_entry.Flags); - std.debug.warn("{} {}\n", line_num_entry, flags); - const vaddr_start = frag_vaddr_start + line_num_entry.Offset; - const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End; - std.debug.warn("test {x} <= {x} < {x}\n", vaddr_start, address, vaddr_end); - if (address >= vaddr_start and address < vaddr_end) { - std.debug.warn("{} line {}\n", block_hdr.NameIndex, flags.Start); - if (has_column) { - line_index = start_line_index + @sizeOf(LineNumberEntry) * block_hdr.NumLines; - line_index += @sizeOf(ColumnNumberEntry) * line_i; - const col_num_entry = @ptrCast(*ColumnNumberEntry, &subsect_info[line_index]); - std.debug.warn("col {}\n", col_num_entry.StartColumn); - } - have_line_info = true; - continue :subsections; - } - } - return error.MissingDebugInfo; - } - - }, - DebugSubsectionKind.FileChecksums => { - var chksum_index: usize = sect_offset; - - while (chksum_index != subsect_info.len) { - const chksum_hdr = @ptrCast(*FileChecksumEntryHeader, &subsect_info[chksum_index]); - std.debug.warn("{}\n", chksum_hdr); - const len = @sizeOf(FileChecksumEntryHeader) + chksum_hdr.ChecksumSize; - chksum_index += len + (len % 4); - if (chksum_index > subsect_info.len) - return error.InvalidDebugInfo; - } - - }, - else => { - std.debug.warn("ignore subsection {}\n", @tagName(subsect_hdr.Kind)); - }, - } - - if (sect_offset > subsect_info.len) - return error.InvalidDebugInfo; - } - std.debug.warn("end subsections\n"); - } }; // see https://llvm.org/docs/PDB/MsfFile.html @@ -687,13 +471,11 @@ const Msf = struct { ); const stream_count = try self.directory.stream.readIntLe(u32); - warn("stream count {}\n", stream_count); const stream_sizes = try allocator.alloc(u32, stream_count); for (stream_sizes) |*s| { const size = try self.directory.stream.readIntLe(u32); s.* = blockCountFromSize(size, superblock.BlockSize); - warn("stream {}B {} blocks\n", size, s.*); } self.streams = try allocator.alloc(MsfStream, stream_count); @@ -784,13 +566,10 @@ const MsfStream = struct { const in = &file_stream.stream; try file.seekTo(pos); - warn("stream with blocks"); var i: u32 = 0; while (i < block_count) : (i += 1) { stream.blocks[i] = try in.readIntLe(u32); - warn(" {}", stream.blocks[i]); } - warn("\n"); return stream; } @@ -812,10 +591,6 @@ const MsfStream = struct { var block = self.blocks[block_id]; var offset = self.pos % self.block_size; - //std.debug.warn("seek {} read {}B: block_id={} block={} offset={}\n", - // block * self.block_size + offset, - // buffer.len, block_id, block, offset); - try self.in_file.seekTo(block * self.block_size + offset); var file_stream = io.FileInStream.init(self.in_file); const in = &file_stream.stream; -- cgit v1.2.3