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;...@@ -19,6 +19,7 @@ const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
19const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;19const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
2020
21const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;21const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
22const IMAGE_DEBUG_TYPE_CODEVIEW = 2;
22const DEBUG_DIRECTORY = 6;23const DEBUG_DIRECTORY = 6;
2324
24pub const CoffError = error{25pub const CoffError = error{
...@@ -28,6 +29,7 @@ pub const CoffError = error{...@@ -28,6 +29,7 @@ pub const CoffError = error{
28 MissingCoffSection,29 MissingCoffSection,
29};30};
3031
32// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
31pub const Coff = struct {33pub const Coff = struct {
32 in_file: File,34 in_file: File,
33 allocator: *mem.Allocator,35 allocator: *mem.Allocator,
...@@ -120,6 +122,7 @@ pub const Coff = struct {...@@ -120,6 +122,7 @@ pub const Coff = struct {
120122
121 pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {123 pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {
122 try self.loadSections();124 try self.loadSections();
125
123 const header = blk: {126 const header = blk: {
124 if (self.getSection(".buildid")) |section| {127 if (self.getSection(".buildid")) |section| {
125 break :blk section.header;128 break :blk section.header;
...@@ -130,14 +133,32 @@ pub const Coff = struct {...@@ -130,14 +133,32 @@ pub const Coff = struct {
130 }133 }
131 };134 };
132135
133 // The linker puts a chunk that contains the .pdb path right after the
134 // debug_directory.
135 const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];136 const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
136 const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;137 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
139 var file_stream = self.in_file.inStream();139 var file_stream = self.in_file.inStream();
140 const in = &file_stream.stream;140 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
142 var cv_signature: [4]u8 = undefined; // CodeView signature163 var cv_signature: [4]u8 = undefined; // CodeView signature
143 try in.readNoEof(cv_signature[0..]);164 try in.readNoEof(cv_signature[0..]);
...@@ -149,7 +170,7 @@ pub const Coff = struct {...@@ -149,7 +170,7 @@ pub const Coff = struct {
149170
150 // Finally read the null-terminated string.171 // Finally read the null-terminated string.
151 var byte = try in.readByte();172 var byte = try in.readByte();
152 var i: usize = 0;173 i = 0;
153 while (byte != 0 and i < buffer.len) : (i += 1) {174 while (byte != 0 and i < buffer.len) : (i += 1) {
154 buffer[i] = byte;175 buffer[i] = byte;
155 byte = try in.readByte();176 byte = try in.readByte();
...@@ -178,7 +199,7 @@ pub const Coff = struct {...@@ -178,7 +199,7 @@ pub const Coff = struct {
178 try self.sections.append(Section{199 try self.sections.append(Section{
179 .header = SectionHeader{200 .header = SectionHeader{
180 .name = name,201 .name = name,
181 .misc = SectionHeader.Misc{ .physical_address = try in.readIntLittle(u32) },202 .misc = SectionHeader.Misc{ .virtual_size = try in.readIntLittle(u32) },
182 .virtual_address = try in.readIntLittle(u32),203 .virtual_address = try in.readIntLittle(u32),
183 .size_of_raw_data = try in.readIntLittle(u32),204 .size_of_raw_data = try in.readIntLittle(u32),
184 .pointer_to_raw_data = try in.readIntLittle(u32),205 .pointer_to_raw_data = try in.readIntLittle(u32),
...@@ -222,6 +243,17 @@ const OptionalHeader = struct {...@@ -222,6 +243,17 @@ const OptionalHeader = struct {
222 data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,243 data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,
223};244};
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
225pub const Section = struct {257pub const Section = struct {
226 header: SectionHeader,258 header: SectionHeader,
227};259};
std/debug.zig+10-3
...@@ -375,7 +375,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres...@@ -375,7 +375,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres
375 const obj_basename = fs.path.basename(mod.obj_file_name);375 const obj_basename = fs.path.basename(mod.obj_file_name);
376376
377 var symbol_i: usize = 0;377 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) {
379 const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]);379 const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]);
380 if (prefix.RecordLen < 2)380 if (prefix.RecordLen < 2)
381 return error.InvalidDebugInfo;381 return error.InvalidDebugInfo;
...@@ -858,8 +858,10 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {...@@ -858,8 +858,10 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
858 const age = try pdb_stream.stream.readIntLittle(u32);858 const age = try pdb_stream.stream.readIntLittle(u32);
859 var guid: [16]u8 = undefined;859 var guid: [16]u8 = undefined;
860 try pdb_stream.stream.readNoEof(guid[0..]);860 try pdb_stream.stream.readNoEof(guid[0..]);
861 if (version != 20000404) // VC70, only value observed by LLVM team
862 return error.UnknownPDBVersion;
861 if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age)863 if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age)
862 return error.InvalidDebugInfo;864 return error.PDBMismatch;
863 // We validated the executable and pdb match.865 // We validated the executable and pdb match.
864866
865 const string_table_index = str_tab_index: {867 const string_table_index = str_tab_index: {
...@@ -903,13 +905,18 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {...@@ -903,13 +905,18 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
903 return error.MissingDebugInfo;905 return error.MissingDebugInfo;
904 };906 };
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;
907 di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo;909 di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo;
908910
909 const dbi = di.pdb.dbi;911 const dbi = di.pdb.dbi;
910912
911 // Dbi Header913 // Dbi Header
912 const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader);914 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
913 const mod_info_size = dbi_stream_header.ModInfoSize;920 const mod_info_size = dbi_stream_header.ModInfoSize;
914 const section_contrib_size = dbi_stream_header.SectionContributionSize;921 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 {...@@ -2053,6 +2053,22 @@ pub fn accessC(path: [*]const u8, mode: u32) AccessError!void {
2053 }2053 }
2054}2054}
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
2056pub const PipeError = error{2072pub const PipeError = error{
2057 SystemFdQuotaExceeded,2073 SystemFdQuotaExceeded,
2058 ProcessFdQuotaExceeded,2074 ProcessFdQuotaExceeded,
std/pdb.zig+73-44
...@@ -499,45 +499,78 @@ const Msf = struct {...@@ -499,45 +499,78 @@ const Msf = struct {
499499
500 const superblock = try in.readStruct(SuperBlock);500 const superblock = try in.readStruct(SuperBlock);
501501
502 // Sanity checks
502 if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic))503 if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic))
503 return error.InvalidDebugInfo;504 return error.InvalidDebugInfo;
504505 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;
505 switch (superblock.BlockSize) {509 switch (superblock.BlockSize) {
506 // llvm only supports 4096 but we can handle any of these values510 // llvm only supports 4096 but we can handle any of these values
507 512, 1024, 2048, 4096 => {},511 512, 1024, 2048, 4096 => {},
508 else => return error.InvalidDebugInfo,512 else => return error.InvalidDebugInfo,
509 }513 }
510514
511 if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos())515 const dir_block_count = blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize);
512 return error.InvalidDebugInfo;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(
515 superblock.BlockSize,525 superblock.BlockSize,
516 blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize),
517 superblock.BlockSize * superblock.BlockMapAddr,
518 file,526 file,
519 allocator,527 dir_blocks,
520 );528 );
521529
530 const begin = self.directory.pos;
522 const stream_count = try self.directory.stream.readIntLittle(u32);531 const stream_count = try self.directory.stream.readIntLittle(u32);
523
524 const stream_sizes = try allocator.alloc(u32, stream_count);532 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| {
526 const size = try self.directory.stream.readIntLittle(u32);540 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);
528 }542 }
529543
530 self.streams = try allocator.alloc(MsfStream, stream_count);544 self.streams = try allocator.alloc(MsfStream, stream_count);
531 for (self.streams) |*stream, i| {545 for (self.streams) |*stream, i| {
532 stream.* = try MsfStream.init(546 const size = stream_sizes[i];
533 superblock.BlockSize,547 if (size == 0) {
534 stream_sizes[i],548 stream.* = MsfStream{
535 // MsfStream.init expects the file to be at the part where it reads [N]u32549 .blocks = [_]u32{},
536 try file.getPos(),550 };
537 file,551 } else {
538 allocator,552 var blocks = try allocator.alloc(u32, size);
539 );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 }
540 }569 }
570
571 const end = self.directory.pos;
572 if (end - begin != superblock.NumDirectoryBytes)
573 return error.InvalidStreamDirectory;
541 }574 }
542};575};
543576
...@@ -574,7 +607,6 @@ const SuperBlock = packed struct {...@@ -574,7 +607,6 @@ const SuperBlock = packed struct {
574 NumDirectoryBytes: u32,607 NumDirectoryBytes: u32,
575608
576 Unknown: u32,609 Unknown: u32,
577
578 /// The index of a block within the MSF file. At this block is an array of610 /// The index of a block within the MSF file. At this block is an array of
579 /// ulittle32_t’s listing the blocks that the stream directory resides on.611 /// ulittle32_t’s listing the blocks that the stream directory resides on.
580 /// For large MSF files, the stream directory (which describes the block612 /// For large MSF files, the stream directory (which describes the block
...@@ -584,45 +616,41 @@ const SuperBlock = packed struct {...@@ -584,45 +616,41 @@ const SuperBlock = packed struct {
584 /// and the stream directory itself can be stitched together accordingly.616 /// and the stream directory itself can be stitched together accordingly.
585 /// The number of ulittle32_t’s in this array is given by617 /// The number of ulittle32_t’s in this array is given by
586 /// ceil(NumDirectoryBytes / BlockSize).618 /// 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.
587 BlockMapAddr: u32,625 BlockMapAddr: u32,
588};626};
589627
590const MsfStream = struct {628const MsfStream = struct {
591 in_file: File,629 in_file: File = undefined,
592 pos: u64,630 pos: u64 = undefined,
593 blocks: []u32,631 blocks: []u32 = undefined,
594 block_size: u32,632 block_size: u32 = undefined,
595633
596 /// Implementation of InStream trait for Pdb.MsfStream634 /// Implementation of InStream trait for Pdb.MsfStream
597 stream: Stream,635 stream: Stream = undefined,
598636
599 pub const Error = @typeOf(read).ReturnType.ErrorSet;637 pub const Error = @typeOf(read).ReturnType.ErrorSet;
600 pub const Stream = io.InStream(Error);638 pub const Stream = io.InStream(Error);
601639
602 fn init(block_size: u32, block_count: u32, pos: u64, file: File, allocator: *mem.Allocator) !MsfStream {640 fn init(block_size: u32, file: File, blocks: []u32) MsfStream {
603 var stream = MsfStream{641 const stream = MsfStream{
604 .in_file = file,642 .in_file = file,
605 .pos = 0,643 .pos = 0,
606 .blocks = try allocator.alloc(u32, block_count),644 .blocks = blocks,
607 .block_size = block_size,645 .block_size = block_size,
608 .stream = Stream{ .readFn = readFn },646 .stream = Stream{ .readFn = readFn },
609 };647 };
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
620 return stream;649 return stream;
621 }650 }
622651
623 fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 {652 fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 {
624 var list = ArrayList(u8).init(allocator);653 var list = ArrayList(u8).init(allocator);
625 defer list.deinit();
626 while (true) {654 while (true) {
627 const byte = try self.stream.readByte();655 const byte = try self.stream.readByte();
628 if (byte == 0) {656 if (byte == 0) {
...@@ -642,11 +670,12 @@ const MsfStream = struct {...@@ -642,11 +670,12 @@ const MsfStream = struct {
642 const in = &file_stream.stream;670 const in = &file_stream.stream;
643671
644 var size: usize = 0;672 var size: usize = 0;
645 for (buffer) |*byte| {673 var rem_buffer = buffer;
646 byte.* = try in.readByte();674 while (size < buffer.len) {
647675 const size_to_read = math.min(self.block_size - offset, rem_buffer.len);
648 offset += 1;676 size += try in.read(rem_buffer[0..size_to_read]);
649 size += 1;677 rem_buffer = buffer[size..];
678 offset += size_to_read;
650679
651 // If we're at the end of a block, go to the next one.680 // If we're at the end of a block, go to the next one.
652 if (offset == self.block_size) {681 if (offset == self.block_size) {
...@@ -657,8 +686,8 @@ const MsfStream = struct {...@@ -657,8 +686,8 @@ const MsfStream = struct {
657 }686 }
658 }687 }
659688
660 self.pos += size;689 self.pos += buffer.len;
661 return size;690 return buffer.len;
662 }691 }
663692
664 fn seekBy(self: *MsfStream, len: i64) !void {693 fn seekBy(self: *MsfStream, len: i64) !void {