authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-07-29 17:50:11-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-07-29 17:50:11-04:00
logceec2393cfbe20b0993bd7cccb43e930438281fe
treee0818d84bca8c67e23c81c721a241401f9b06afa
parentbc982e65cf77aa6551029620ceaec8f3ae49202f
parentc087525edae2db147c89c83513a0778f87bee30d
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #2965 from Sahnvour/zig-stack-traces

Stage1 stack traces on windows

4 files changed, 136 insertions(+), 52 deletions(-)

std/coff.zig+37-5
......@@ -19,6 +19,7 @@ const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
1919const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
2020
2121const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
22const IMAGE_DEBUG_TYPE_CODEVIEW = 2;
2223const DEBUG_DIRECTORY = 6;
2324
2425pub const CoffError = error{
......@@ -28,6 +29,7 @@ pub const CoffError = error{
2829 MissingCoffSection,
2930};
3031
32// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
3133pub const Coff = struct {
3234 in_file: File,
3335 allocator: *mem.Allocator,
......@@ -120,6 +122,7 @@ pub const Coff = struct {
120122
121123 pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {
122124 try self.loadSections();
125
123126 const header = blk: {
124127 if (self.getSection(".buildid")) |section| {
125128 break :blk section.header;
......@@ -130,14 +133,32 @@ pub const Coff = struct {
130133 }
131134 };
132135
133 // The linker puts a chunk that contains the .pdb path right after the
134 // debug_directory.
135136 const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
136137 const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
137 try self.in_file.seekTo(file_offset + debug_dir.size);
138138
139139 var file_stream = self.in_file.inStream();
140140 const in = &file_stream.stream;
141 try self.in_file.seekTo(file_offset);
142
143 // Find the correct DebugDirectoryEntry, and where its data is stored.
144 // It can be in any section.
145 const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry);
146 var i: u32 = 0;
147 blk: while (i < debug_dir_entry_count) : (i += 1) {
148 const debug_dir_entry = try in.readStruct(DebugDirectoryEntry);
149 if (debug_dir_entry.type == IMAGE_DEBUG_TYPE_CODEVIEW) {
150 for (self.sections.toSlice()) |*section| {
151 const section_start = section.header.virtual_address;
152 const section_size = section.header.misc.virtual_size;
153 const rva = debug_dir_entry.address_of_raw_data;
154 const offset = rva - section_start;
155 if (section_start <= rva and offset < section_size and debug_dir_entry.size_of_data <= section_size - offset) {
156 try self.in_file.seekTo(section.header.pointer_to_raw_data + offset);
157 break :blk;
158 }
159 }
160 }
161 }
141162
142163 var cv_signature: [4]u8 = undefined; // CodeView signature
143164 try in.readNoEof(cv_signature[0..]);
......@@ -149,7 +170,7 @@ pub const Coff = struct {
149170
150171 // Finally read the null-terminated string.
151172 var byte = try in.readByte();
152 var i: usize = 0;
173 i = 0;
153174 while (byte != 0 and i < buffer.len) : (i += 1) {
154175 buffer[i] = byte;
155176 byte = try in.readByte();
......@@ -178,7 +199,7 @@ pub const Coff = struct {
178199 try self.sections.append(Section{
179200 .header = SectionHeader{
180201 .name = name,
181 .misc = SectionHeader.Misc{ .physical_address = try in.readIntLittle(u32) },
202 .misc = SectionHeader.Misc{ .virtual_size = try in.readIntLittle(u32) },
182203 .virtual_address = try in.readIntLittle(u32),
183204 .size_of_raw_data = try in.readIntLittle(u32),
184205 .pointer_to_raw_data = try in.readIntLittle(u32),
......@@ -222,6 +243,17 @@ const OptionalHeader = struct {
222243 data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,
223244};
224245
246const DebugDirectoryEntry = packed struct {
247 characteristiccs: u32,
248 time_date_stamp: u32,
249 major_version: u16,
250 minor_version: u16,
251 @"type": u32,
252 size_of_data: u32,
253 address_of_raw_data: u32,
254 pointer_to_raw_data: u32,
255};
256
225257pub const Section = struct {
226258 header: SectionHeader,
227259};
std/debug.zig+10-3
......@@ -375,7 +375,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres
375375 const obj_basename = fs.path.basename(mod.obj_file_name);
376376
377377 var symbol_i: usize = 0;
378 const symbol_name = while (symbol_i != mod.symbols.len) {
378 const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) {
379379 const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]);
380380 if (prefix.RecordLen < 2)
381381 return error.InvalidDebugInfo;
......@@ -858,8 +858,10 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
858858 const age = try pdb_stream.stream.readIntLittle(u32);
859859 var guid: [16]u8 = undefined;
860860 try pdb_stream.stream.readNoEof(guid[0..]);
861 if (version != 20000404) // VC70, only value observed by LLVM team
862 return error.UnknownPDBVersion;
861863 if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age)
862 return error.InvalidDebugInfo;
864 return error.PDBMismatch;
863865 // We validated the executable and pdb match.
864866
865867 const string_table_index = str_tab_index: {
......@@ -903,13 +905,18 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
903905 return error.MissingDebugInfo;
904906 };
905907
906 di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo;
908 di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.MissingDebugInfo;
907909 di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo;
908910
909911 const dbi = di.pdb.dbi;
910912
911913 // Dbi Header
912914 const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader);
915 if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team
916 return error.UnknownPDBVersion;
917 if (dbi_stream_header.Age != age)
918 return error.UnmatchingPDB;
919
913920 const mod_info_size = dbi_stream_header.ModInfoSize;
914921 const section_contrib_size = dbi_stream_header.SectionContributionSize;
915922
std/os.zig+16
......@@ -2053,6 +2053,22 @@ pub fn accessC(path: [*]const u8, mode: u32) AccessError!void {
20532053 }
20542054}
20552055
2056/// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
2057/// Otherwise use `access` or `accessC`.
2058/// TODO currently this ignores `mode`.
2059pub fn accessW(path: [*]const u16, mode: u32) windows.GetFileAttributesError!void {
2060 const ret = try windows.GetFileAttributesW(path);
2061 if (ret != windows.INVALID_FILE_ATTRIBUTES) {
2062 return;
2063 }
2064 switch (windows.kernel32.GetLastError()) {
2065 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
2066 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
2067 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
2068 else => |err| return windows.unexpectedError(err),
2069 }
2070}
2071
20562072pub const PipeError = error{
20572073 SystemFdQuotaExceeded,
20582074 ProcessFdQuotaExceeded,
std/pdb.zig+73-44
......@@ -499,45 +499,78 @@ const Msf = struct {
499499
500500 const superblock = try in.readStruct(SuperBlock);
501501
502 // Sanity checks
502503 if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic))
503504 return error.InvalidDebugInfo;
504
505 if (superblock.FreeBlockMapBlock != 1 and superblock.FreeBlockMapBlock != 2)
506 return error.InvalidDebugInfo;
507 if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos())
508 return error.InvalidDebugInfo;
505509 switch (superblock.BlockSize) {
506510 // llvm only supports 4096 but we can handle any of these values
507511 512, 1024, 2048, 4096 => {},
508512 else => return error.InvalidDebugInfo,
509513 }
510514
511 if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos())
512 return error.InvalidDebugInfo;
515 const dir_block_count = blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize);
516 if (dir_block_count > superblock.BlockSize / @sizeOf(u32))
517 return error.UnhandledBigDirectoryStream; // cf. BlockMapAddr comment.
513518
514 self.directory = try MsfStream.init(
519 try file.seekTo(superblock.BlockSize * superblock.BlockMapAddr);
520 var dir_blocks = try allocator.alloc(u32, dir_block_count);
521 for (dir_blocks) |*b| {
522 b.* = try in.readIntLittle(u32);
523 }
524 self.directory = MsfStream.init(
515525 superblock.BlockSize,
516 blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize),
517 superblock.BlockSize * superblock.BlockMapAddr,
518526 file,
519 allocator,
527 dir_blocks,
520528 );
521529
530 const begin = self.directory.pos;
522531 const stream_count = try self.directory.stream.readIntLittle(u32);
523
524532 const stream_sizes = try allocator.alloc(u32, stream_count);
525 for (stream_sizes) |*s| {
533 defer allocator.free(stream_sizes);
534
535 // Microsoft's implementation uses u32(-1) for inexistant streams.
536 // These streams are not used, but still participate in the file
537 // and must be taken into account when resolving stream indices.
538 const Nil = 0xFFFFFFFF;
539 for (stream_sizes) |*s, i| {
526540 const size = try self.directory.stream.readIntLittle(u32);
527 s.* = blockCountFromSize(size, superblock.BlockSize);
541 s.* = if (size == Nil) 0 else blockCountFromSize(size, superblock.BlockSize);
528542 }
529543
530544 self.streams = try allocator.alloc(MsfStream, stream_count);
531545 for (self.streams) |*stream, i| {
532 stream.* = try MsfStream.init(
533 superblock.BlockSize,
534 stream_sizes[i],
535 // MsfStream.init expects the file to be at the part where it reads [N]u32
536 try file.getPos(),
537 file,
538 allocator,
539 );
546 const size = stream_sizes[i];
547 if (size == 0) {
548 stream.* = MsfStream{
549 .blocks = [_]u32{},
550 };
551 } else {
552 var blocks = try allocator.alloc(u32, size);
553 var j: u32 = 0;
554 while (j < size) : (j += 1) {
555 const block_id = try self.directory.stream.readIntLittle(u32);
556 const n = (block_id % superblock.BlockSize);
557 // 0 is for SuperBlock, 1 and 2 for FPMs.
558 if (block_id == 0 or n == 1 or n == 2 or block_id * superblock.BlockSize > try file.getEndPos())
559 return error.InvalidBlockIndex;
560 blocks[j] = block_id;
561 }
562
563 stream.* = MsfStream.init(
564 superblock.BlockSize,
565 file,
566 blocks,
567 );
568 }
540569 }
570
571 const end = self.directory.pos;
572 if (end - begin != superblock.NumDirectoryBytes)
573 return error.InvalidStreamDirectory;
541574 }
542575};
543576
......@@ -574,7 +607,6 @@ const SuperBlock = packed struct {
574607 NumDirectoryBytes: u32,
575608
576609 Unknown: u32,
577
578610 /// The index of a block within the MSF file. At this block is an array of
579611 /// ulittle32_t’s listing the blocks that the stream directory resides on.
580612 /// For large MSF files, the stream directory (which describes the block
......@@ -584,45 +616,41 @@ const SuperBlock = packed struct {
584616 /// and the stream directory itself can be stitched together accordingly.
585617 /// The number of ulittle32_t’s in this array is given by
586618 /// ceil(NumDirectoryBytes / BlockSize).
619 // Note: microsoft-pdb code actually suggests this is a variable-length
620 // array. If the indices of blocks occupied by the Stream Directory didn't
621 // fit in one page, there would be other u32 following it.
622 // This would mean the Stream Directory is bigger than BlockSize / sizeof(u32)
623 // blocks. We're not even close to this with a 1GB pdb file, and LLVM didn't
624 // implement it so we're kind of safe making this assumption for now.
587625 BlockMapAddr: u32,
588626};
589627
590628const MsfStream = struct {
591 in_file: File,
592 pos: u64,
593 blocks: []u32,
594 block_size: u32,
629 in_file: File = undefined,
630 pos: u64 = undefined,
631 blocks: []u32 = undefined,
632 block_size: u32 = undefined,
595633
596634 /// Implementation of InStream trait for Pdb.MsfStream
597 stream: Stream,
635 stream: Stream = undefined,
598636
599637 pub const Error = @typeOf(read).ReturnType.ErrorSet;
600638 pub const Stream = io.InStream(Error);
601639
602 fn init(block_size: u32, block_count: u32, pos: u64, file: File, allocator: *mem.Allocator) !MsfStream {
603 var stream = MsfStream{
640 fn init(block_size: u32, file: File, blocks: []u32) MsfStream {
641 const stream = MsfStream{
604642 .in_file = file,
605643 .pos = 0,
606 .blocks = try allocator.alloc(u32, block_count),
644 .blocks = blocks,
607645 .block_size = block_size,
608646 .stream = Stream{ .readFn = readFn },
609647 };
610648
611 var file_stream = file.inStream();
612 const in = &file_stream.stream;
613 try file.seekTo(pos);
614
615 var i: u32 = 0;
616 while (i < block_count) : (i += 1) {
617 stream.blocks[i] = try in.readIntLittle(u32);
618 }
619
620649 return stream;
621650 }
622651
623652 fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 {
624653 var list = ArrayList(u8).init(allocator);
625 defer list.deinit();
626654 while (true) {
627655 const byte = try self.stream.readByte();
628656 if (byte == 0) {
......@@ -642,11 +670,12 @@ const MsfStream = struct {
642670 const in = &file_stream.stream;
643671
644672 var size: usize = 0;
645 for (buffer) |*byte| {
646 byte.* = try in.readByte();
647
648 offset += 1;
649 size += 1;
673 var rem_buffer = buffer;
674 while (size < buffer.len) {
675 const size_to_read = math.min(self.block_size - offset, rem_buffer.len);
676 size += try in.read(rem_buffer[0..size_to_read]);
677 rem_buffer = buffer[size..];
678 offset += size_to_read;
650679
651680 // If we're at the end of a block, go to the next one.
652681 if (offset == self.block_size) {
......@@ -657,8 +686,8 @@ const MsfStream = struct {
657686 }
658687 }
659688
660 self.pos += size;
661 return size;
689 self.pos += buffer.len;
690 return buffer.len;
662691 }
663692
664693 fn seekBy(self: *MsfStream, len: i64) !void {