authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-02 15:58:08-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-02 15:58:08-04:00
log98dc943c0784b93ed28099bb75044c536174a144
tree5f1ba858f8de71a48519057656fbc63d8c136ab5
parent6ddbd345aa085291f540e6d1b436edad32fe9a69

rework code to avoid duplicate operations


5 files changed, 448 insertions(+), 305 deletions(-)

std/coff.zig-8
......@@ -83,7 +83,6 @@ pub const Coff = struct {
8383 fn loadOptionalHeader(self: *Coff, file_stream: *io.FileInStream) !void {
8484 const in = &file_stream.stream;
8585 self.pe_header.magic = try in.readIntLe(u16);
86 std.debug.warn("reading pe optional\n");
8786 // For now we're only interested in finding the reference to the .pdb,
8887 // so we'll skip most of this header, which size is different in 32
8988 // 64 bits by the way.
......@@ -97,11 +96,9 @@ pub const Coff = struct {
9796 else
9897 return error.InvalidPEMagic;
9998
100 std.debug.warn("skipping {}\n", skip_size);
10199 try self.in_file.seekForward(skip_size);
102100
103101 const number_of_rva_and_sizes = try in.readIntLe(u32);
104 //std.debug.warn("indicating {} data dirs\n", number_of_rva_and_sizes);
105102 if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
106103 return error.InvalidPEHeader;
107104
......@@ -110,9 +107,7 @@ pub const Coff = struct {
110107 .virtual_address = try in.readIntLe(u32),
111108 .size = try in.readIntLe(u32),
112109 };
113 //std.debug.warn("data_dir @ {x}, size {}\n", data_dir.virtual_address, data_dir.size);
114110 }
115 std.debug.warn("loaded data directories\n");
116111 }
117112
118113 pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {
......@@ -123,7 +118,6 @@ pub const Coff = struct {
123118 // debug_directory.
124119 const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
125120 const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
126 std.debug.warn("file offset {x}\n", file_offset);
127121 try self.in_file.seekTo(file_offset + debug_dir.size);
128122
129123 var file_stream = io.FileInStream.init(self.in_file);
......@@ -134,7 +128,6 @@ pub const Coff = struct {
134128 // 'RSDS' indicates PDB70 format, used by lld.
135129 if (!mem.eql(u8, cv_signature, "RSDS"))
136130 return error.InvalidPEMagic;
137 std.debug.warn("cv_signature {}\n", cv_signature);
138131 try in.readNoEof(self.guid[0..]);
139132 self.age = try in.readIntLe(u32);
140133
......@@ -181,7 +174,6 @@ pub const Coff = struct {
181174 },
182175 });
183176 }
184 std.debug.warn("loaded {} sections\n", self.coff_header.number_of_sections);
185177 }
186178
187179 pub fn getSection(self: *Coff, comptime name: []const u8) ?*Section {
std/debug/index.zig+386-41
......@@ -20,6 +20,17 @@ pub const runtime_safety = switch (builtin.mode) {
2020 builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false,
2121};
2222
23const Module = struct {
24 mod_info: pdb.ModInfo,
25 module_name: []u8,
26 obj_file_name: []u8,
27
28 populated: bool,
29 symbols: []u8,
30 subsect_info: []u8,
31 checksums: []u32,
32};
33
2334/// Tries to write to stderr, unbuffered, and ignores any error returned.
2435/// Does not append a newline.
2536var stderr_file: os.File = undefined;
......@@ -258,12 +269,277 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: us
258269 }
259270}
260271
261fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
272fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void {
273 const allocator = getDebugInfoAllocator();
262274 const base_address = os.getBaseAddress();
263 const relative_address = address - base_address;
264 std.debug.warn("{x} - {x} => {x}\n", address, base_address, relative_address);
265 try di.pdb.getSourceLine(relative_address);
266 return error.UnsupportedDebugInfo;
275 const relative_address = relocated_address - base_address;
276
277 var coff_section: *coff.Section = undefined;
278 const mod_index = for (di.sect_contribs) |sect_contrib| {
279 coff_section = &di.coff.sections.toSlice()[sect_contrib.Section];
280
281 const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
282 const vaddr_end = vaddr_start + sect_contrib.Size;
283 if (relative_address >= vaddr_start and relative_address < vaddr_end) {
284 break sect_contrib.ModuleIndex;
285 }
286 } else {
287 // we have no information to add to the address
288 if (tty_color) {
289 try out_stream.print("???:?:?: ");
290 setTtyColor(TtyColor.Dim);
291 try out_stream.print("0x{x} in ??? (???)", relocated_address);
292 setTtyColor(TtyColor.Reset);
293 try out_stream.print("\n\n\n");
294 } else {
295 try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", relocated_address);
296 }
297 return;
298 };
299
300 const mod = &di.modules[mod_index];
301 try populateModule(di, mod);
302 const obj_basename = os.path.basename(mod.obj_file_name);
303
304 var symbol_i: usize = 0;
305 const symbol_name = while (symbol_i != mod.symbols.len) {
306 const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]);
307 if (prefix.RecordLen < 2)
308 return error.InvalidDebugInfo;
309 switch (prefix.RecordKind) {
310 pdb.SymbolKind.S_LPROC32 => {
311 const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]);
312 const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset;
313 const vaddr_end = vaddr_start + proc_sym.CodeSize;
314 if (relative_address >= vaddr_start and relative_address < vaddr_end) {
315 break mem.toSliceConst(u8, @ptrCast([*]u8, proc_sym) + @sizeOf(pdb.ProcSym));
316 }
317 },
318 else => {},
319 }
320 symbol_i += prefix.RecordLen + @sizeOf(u16);
321 if (symbol_i > mod.symbols.len)
322 return error.InvalidDebugInfo;
323 } else "???";
324
325 const subsect_info = mod.subsect_info;
326
327 var sect_offset: usize = 0;
328 var skip_len: usize = undefined;
329 const opt_line_info = subsections: while (sect_offset != subsect_info.len) : (sect_offset += skip_len) {
330 const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]);
331 skip_len = subsect_hdr.Length;
332 sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
333
334 switch (subsect_hdr.Kind) {
335 pdb.DebugSubsectionKind.Lines => {
336 var line_index: usize = sect_offset;
337
338 const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]);
339 if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo;
340 line_index += @sizeOf(pdb.LineFragmentHeader);
341
342 const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]);
343 line_index += @sizeOf(pdb.LineBlockFragmentHeader);
344
345 const has_column = line_hdr.Flags.LF_HaveColumns;
346
347 const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset;
348 const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize;
349 if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) {
350 var line_i: usize = 0;
351 const start_line_index = line_index;
352 while (line_i < block_hdr.NumLines) : (line_i += 1) {
353 const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]);
354 line_index += @sizeOf(pdb.LineNumberEntry);
355 const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags);
356 const vaddr_start = frag_vaddr_start + line_num_entry.Offset;
357 const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End;
358 if (relative_address >= vaddr_start and relative_address < vaddr_end) {
359 const chksum_index = block_hdr.NameIndex;
360 std.debug.warn("looking up checksum {}\n", chksum_index);
361 const strtab_offset = mod.checksums[chksum_index];
362 try di.pdb.string_table.seekTo(@sizeOf(pdb.PDBStringTableHeader) + strtab_offset);
363 const source_file_name = try di.pdb.string_table.readNullTermString(allocator);
364 const line = flags.Start;
365 const column = if (has_column) blk: {
366 line_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines;
367 line_index += @sizeOf(pdb.ColumnNumberEntry) * line_i;
368 const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[line_index]);
369 break :blk col_num_entry.StartColumn;
370 } else 0;
371 break :subsections LineInfo{
372 .allocator = allocator,
373 .file_name = source_file_name,
374 .line = line,
375 .column = column,
376 };
377 }
378 }
379 break :subsections null;
380 }
381 },
382 else => {},
383 }
384
385 if (sect_offset > subsect_info.len)
386 return error.InvalidDebugInfo;
387 } else null;
388
389 if (tty_color) {
390 if (opt_line_info) |li| {
391 try out_stream.print("{}:{}:{}: ", li.file_name, li.line, li.column);
392 } else {
393 try out_stream.print("???:?:?: ");
394 }
395 setTtyColor(TtyColor.Dim);
396 try out_stream.print("0x{x} in {} ({})", relocated_address, symbol_name, obj_basename);
397 setTtyColor(TtyColor.Reset);
398
399 if (opt_line_info) |line_info| {
400 try out_stream.print("\n");
401 if (printLineFromFile(out_stream, line_info)) {
402 if (line_info.column == 0) {
403 try out_stream.write("\n");
404 } else {
405 {
406 var col_i: usize = 1;
407 while (col_i < line_info.column) : (col_i += 1) {
408 try out_stream.writeByte(' ');
409 }
410 }
411 setTtyColor(TtyColor.Green);
412 try out_stream.write("^");
413 setTtyColor(TtyColor.Reset);
414 try out_stream.write("\n");
415 }
416 } else |err| switch (err) {
417 error.EndOfFile => {},
418 else => return err,
419 }
420 } else {
421 try out_stream.print("\n\n\n");
422 }
423 } else {
424 if (opt_line_info) |li| {
425 try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", li.file_name, li.line, li.column, relocated_address, symbol_name, obj_basename);
426 } else {
427 try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", relocated_address, symbol_name, obj_basename);
428 }
429 }
430}
431
432const TtyColor = enum{
433 Red,
434 Green,
435 Cyan,
436 White,
437 Dim,
438 Bold,
439 Reset,
440};
441
442/// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt
443fn setTtyColor(tty_color: TtyColor) void {
444 const S = struct {
445 var attrs: windows.WORD = undefined;
446 var init_attrs = false;
447 };
448 if (!S.init_attrs) {
449 S.init_attrs = true;
450 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
451 // TODO handle error
452 _ = windows.GetConsoleScreenBufferInfo(stderr_file.handle, &info);
453 S.attrs = info.wAttributes;
454 }
455
456 // TODO handle errors
457 switch (tty_color) {
458 TtyColor.Red => {
459 _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED|windows.FOREGROUND_INTENSITY);
460 },
461 TtyColor.Green => {
462 _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN|windows.FOREGROUND_INTENSITY);
463 },
464 TtyColor.Cyan => {
465 _ = windows.SetConsoleTextAttribute(stderr_file.handle,
466 windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY);
467 },
468 TtyColor.White, TtyColor.Bold => {
469 _ = windows.SetConsoleTextAttribute(stderr_file.handle,
470 windows.FOREGROUND_RED|windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY);
471 },
472 TtyColor.Dim => {
473 _ = windows.SetConsoleTextAttribute(stderr_file.handle,
474 windows.FOREGROUND_RED|windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE);
475 },
476 TtyColor.Reset => {
477 _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs);
478 },
479 }
480}
481
482fn populateModule(di: *DebugInfo, mod: *Module) !void {
483 if (mod.populated)
484 return;
485 const allocator = getDebugInfoAllocator();
486
487 if (mod.mod_info.C11ByteSize != 0)
488 return error.InvalidDebugInfo;
489
490 if (mod.mod_info.C13ByteSize == 0)
491 return error.MissingDebugInfo;
492
493 const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo;
494
495 const signature = try modi.stream.readIntLe(u32);
496 if (signature != 4)
497 return error.InvalidDebugInfo;
498
499 mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4);
500 try modi.stream.readNoEof(mod.symbols);
501
502 mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize);
503 try modi.stream.readNoEof(mod.subsect_info);
504
505 var checksum_list = ArrayList(u32).init(allocator);
506 var sect_offset: usize = 0;
507 var skip_len: usize = undefined;
508 while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) {
509 const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &mod.subsect_info[sect_offset]);
510 skip_len = subsect_hdr.Length;
511 sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
512
513 switch (subsect_hdr.Kind) {
514 pdb.DebugSubsectionKind.FileChecksums => {
515 var chksum_index: usize = sect_offset;
516
517 while (chksum_index != mod.subsect_info.len) {
518 const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[chksum_index]);
519 std.debug.warn("{} {}\n", checksum_list.len, chksum_hdr);
520 try checksum_list.append(chksum_hdr.FileNameOffset);
521 const len = @sizeOf(pdb.FileChecksumEntryHeader) + chksum_hdr.ChecksumSize;
522 chksum_index += len + (len % 4);
523 if (chksum_index > mod.subsect_info.len)
524 return error.InvalidDebugInfo;
525 }
526
527 },
528 else => {},
529 }
530
531 if (sect_offset > mod.subsect_info.len)
532 return error.InvalidDebugInfo;
533 }
534 mod.checksums = checksum_list.toOwnedSlice();
535
536 for (mod.checksums) |strtab_offset| {
537 try di.pdb.string_table.seekTo(@sizeOf(pdb.PDBStringTableHeader) + strtab_offset);
538 const source_file_name = try di.pdb.string_table.readNullTermString(allocator);
539 std.debug.warn("{}={}\n", strtab_offset, source_file_name);
540 }
541
542 mod.populated = true;
267543}
268544
269545fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
......@@ -425,6 +701,8 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
425701 var di = DebugInfo{
426702 .coff = coff_obj,
427703 .pdb = undefined,
704 .sect_contribs = undefined,
705 .modules = undefined,
428706 };
429707
430708 try di.coff.loadHeader();
......@@ -432,15 +710,12 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
432710 var path_buf: [windows.MAX_PATH]u8 = undefined;
433711 const len = try di.coff.getPdbPath(path_buf[0..]);
434712 const raw_path = path_buf[0..len];
435 std.debug.warn("pdb raw path {}\n", raw_path);
436713
437714 const path = try os.path.resolve(allocator, raw_path);
438 std.debug.warn("pdb resolved path {}\n", path);
439715
440716 try di.pdb.openFile(di.coff, path);
441717
442718 var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo;
443 std.debug.warn("pdb real filepos {}\n", pdb_stream.getFilePos());
444719 const version = try pdb_stream.stream.readIntLe(u32);
445720 const signature = try pdb_stream.stream.readIntLe(u32);
446721 const age = try pdb_stream.stream.readIntLe(u32);
......@@ -448,51 +723,119 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
448723 try pdb_stream.stream.readNoEof(guid[0..]);
449724 if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age)
450725 return error.InvalidDebugInfo;
451 std.debug.warn("v {} s {} a {}\n", version, signature, age);
452726 // We validated the executable and pdb match.
453727
454 const name_bytes_len = try pdb_stream.stream.readIntLe(u32);
455 const name_bytes = try allocator.alloc(u8, name_bytes_len);
456 try pdb_stream.stream.readNoEof(name_bytes);
728 const string_table_index = str_tab_index: {
729 const name_bytes_len = try pdb_stream.stream.readIntLe(u32);
730 const name_bytes = try allocator.alloc(u8, name_bytes_len);
731 try pdb_stream.stream.readNoEof(name_bytes);
457732
458 const HashTableHeader = packed struct {
459 Size: u32,
460 Capacity: u32,
733 const HashTableHeader = packed struct {
734 Size: u32,
735 Capacity: u32,
461736
462 fn maxLoad(cap: u32) u32 {
463 return cap * 2 / 3 + 1;
737 fn maxLoad(cap: u32) u32 {
738 return cap * 2 / 3 + 1;
739 }
740 };
741 var hash_tbl_hdr: HashTableHeader = undefined;
742 try pdb_stream.stream.readStruct(HashTableHeader, &hash_tbl_hdr);
743 if (hash_tbl_hdr.Capacity == 0)
744 return error.InvalidDebugInfo;
745
746 if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))
747 return error.InvalidDebugInfo;
748
749 const present = try readSparseBitVector(&pdb_stream.stream, allocator);
750 if (present.len != hash_tbl_hdr.Size)
751 return error.InvalidDebugInfo;
752 const deleted = try readSparseBitVector(&pdb_stream.stream, allocator);
753
754 const Bucket = struct {
755 first: u32,
756 second: u32,
757 };
758 const bucket_list = try allocator.alloc(Bucket, present.len);
759 for (present) |_| {
760 const name_offset = try pdb_stream.stream.readIntLe(u32);
761 const name_index = try pdb_stream.stream.readIntLe(u32);
762 const name = mem.toSlice(u8, name_bytes.ptr + name_offset);
763 if (mem.eql(u8, name, "/names")) {
764 break :str_tab_index name_index;
765 }
464766 }
767 return error.MissingDebugInfo;
465768 };
466 var hash_tbl_hdr: HashTableHeader = undefined;
467 try pdb_stream.stream.readStruct(HashTableHeader, &hash_tbl_hdr);
468 if (hash_tbl_hdr.Capacity == 0)
469 return error.InvalidDebugInfo;
470769
471 if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))
472 return error.InvalidDebugInfo;
770 di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo;
771 di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo;
473772
474 std.debug.warn("{}\n", hash_tbl_hdr);
773 const dbi = di.pdb.dbi;
475774
476 const present = try readSparseBitVector(&pdb_stream.stream, allocator);
477 if (present.len != hash_tbl_hdr.Size)
478 return error.InvalidDebugInfo;
479 const deleted = try readSparseBitVector(&pdb_stream.stream, allocator);
775 // Dbi Header
776 var dbi_stream_header: pdb.DbiStreamHeader = undefined;
777 try dbi.stream.readStruct(pdb.DbiStreamHeader, &dbi_stream_header);
778 const mod_info_size = dbi_stream_header.ModInfoSize;
779 const section_contrib_size = dbi_stream_header.SectionContributionSize;
480780
481 const Bucket = struct {
482 first: u32,
483 second: u32,
484 };
485 const bucket_list = try allocator.alloc(Bucket, present.len);
486 const string_table_index = for (present) |_| {
487 const name_offset = try pdb_stream.stream.readIntLe(u32);
488 const name_index = try pdb_stream.stream.readIntLe(u32);
489 const name = mem.toSlice(u8, name_bytes.ptr + name_offset);
490 if (mem.eql(u8, name, "/names")) {
491 break name_index;
781 var modules = ArrayList(Module).init(allocator);
782
783 // Module Info Substream
784 var mod_info_offset: usize = 0;
785 while (mod_info_offset != mod_info_size) {
786 var mod_info: pdb.ModInfo = undefined;
787 try dbi.stream.readStruct(pdb.ModInfo, &mod_info);
788 var this_record_len: usize = @sizeOf(pdb.ModInfo);
789
790 const module_name = try dbi.readNullTermString(allocator);
791 this_record_len += module_name.len + 1;
792
793 const obj_file_name = try dbi.readNullTermString(allocator);
794 this_record_len += obj_file_name.len + 1;
795
796 const march_forward_bytes = this_record_len % 4;
797 if (march_forward_bytes != 0) {
798 try dbi.seekForward(march_forward_bytes);
799 this_record_len += march_forward_bytes;
492800 }
493 } else return error.MissingDebugInfo;
494801
495 di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo;
802 try modules.append(Module{
803 .mod_info = mod_info,
804 .module_name = module_name,
805 .obj_file_name = obj_file_name,
806
807 .populated = false,
808 .symbols = undefined,
809 .subsect_info = undefined,
810 .checksums = undefined,
811 });
812
813 mod_info_offset += this_record_len;
814 if (mod_info_offset > mod_info_size)
815 return error.InvalidDebugInfo;
816 }
817
818 di.modules = modules.toOwnedSlice();
819
820 // Section Contribution Substream
821 var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator);
822 var sect_cont_offset: usize = 0;
823 if (section_contrib_size != 0) {
824 const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLe(u32));
825 if (ver != pdb.SectionContrSubstreamVersion.Ver60)
826 return error.InvalidDebugInfo;
827 sect_cont_offset += @sizeOf(u32);
828 }
829 while (sect_cont_offset != section_contrib_size) {
830 const entry = try sect_contribs.addOne();
831 try dbi.stream.readStruct(pdb.SectionContribEntry, entry);
832 sect_cont_offset += @sizeOf(pdb.SectionContribEntry);
833
834 if (sect_cont_offset > section_contrib_size)
835 return error.InvalidDebugInfo;
836 }
837
838 di.sect_contribs = sect_contribs.toOwnedSlice();
496839
497840 return di;
498841}
......@@ -715,6 +1058,8 @@ pub const DebugInfo = switch (builtin.os) {
7151058 builtin.Os.windows => struct {
7161059 pdb: pdb.Pdb,
7171060 coff: *coff.Coff,
1061 sect_contribs: []pdb.SectionContribEntry,
1062 modules: []Module,
7181063 },
7191064 builtin.Os.linux => struct {
7201065 self_exe_file: os.File,
std/os/windows/index.zig+13
......@@ -15,6 +15,7 @@ test "import" {
1515
1616pub const ERROR = @import("error.zig");
1717
18pub const SHORT = c_short;
1819pub const BOOL = c_int;
1920pub const BOOLEAN = BYTE;
2021pub const BYTE = u8;
......@@ -364,3 +365,15 @@ pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
364365pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
365366pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
366367pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
368
369pub const SMALL_RECT = extern struct {
370 Left: SHORT,
371 Top: SHORT,
372 Right: SHORT,
373 Bottom: SHORT,
374};
375
376pub const COORD = extern struct {
377 X: SHORT,
378 Y: SHORT,
379};
std/os/windows/kernel32.zig+18
......@@ -72,6 +72,8 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
7272
7373pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
7474
75pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL;
76
7577pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD;
7678pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;
7779
......@@ -179,6 +181,8 @@ pub extern "kernel32" stdcallcc fn ReadFile(
179181
180182pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
181183
184pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL;
185
182186pub extern "kernel32" stdcallcc fn SetFilePointerEx(
183187 in_fFile: HANDLE,
184188 in_liDistanceToMove: LARGE_INTEGER,
......@@ -234,3 +238,17 @@ pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16;
234238pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2;
235239pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1;
236240pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4;
241
242
243pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct {
244 dwSize: COORD,
245 dwCursorPosition: COORD,
246 wAttributes: WORD,
247 srWindow: SMALL_RECT,
248 dwMaximumWindowSize: COORD,
249};
250
251pub const FOREGROUND_BLUE = 1;
252pub const FOREGROUND_GREEN = 2;
253pub const FOREGROUND_RED = 4;
254pub const FOREGROUND_INTENSITY = 8;
std/pdb.zig+31-256
......@@ -10,7 +10,7 @@ const coff = std.coff;
1010const ArrayList = std.ArrayList;
1111
1212// https://llvm.org/docs/PDB/DbiStream.html#stream-header
13const DbiStreamHeader = packed struct {
13pub const DbiStreamHeader = packed struct {
1414 VersionSignature: i32,
1515 VersionHeader: u32,
1616 Age: u32,
......@@ -33,7 +33,7 @@ const DbiStreamHeader = packed struct {
3333 Padding: u32,
3434};
3535
36const SectionContribEntry = packed struct {
36pub const SectionContribEntry = packed struct {
3737 Section: u16,
3838 Padding1: [2]u8,
3939 Offset: u32,
......@@ -45,7 +45,7 @@ const SectionContribEntry = packed struct {
4545 RelocCrc: u32,
4646};
4747
48const ModInfo = packed struct {
48pub const ModInfo = packed struct {
4949 Unused1: u32,
5050 SectionContr: SectionContribEntry,
5151 Flags: u16,
......@@ -63,12 +63,12 @@ const ModInfo = packed struct {
6363 //ObjFileName: char[],
6464};
6565
66const SectionMapHeader = packed struct {
66pub const SectionMapHeader = packed struct {
6767 Count: u16, /// Number of segment descriptors
6868 LogCount: u16, /// Number of logical segment descriptors
6969};
7070
71const SectionMapEntry = packed struct {
71pub const SectionMapEntry = packed struct {
7272 Flags: u16 , /// See the SectionMapEntryFlags enum below.
7373 Ovl: u16 , /// Logical overlay number
7474 Group: u16 , /// Group index into descriptor array.
......@@ -86,12 +86,6 @@ pub const StreamType = enum(u16) {
8686 Ipi = 4,
8787};
8888
89const Module = struct {
90 mod_info: ModInfo,
91 module_name: []u8,
92 obj_file_name: []u8,
93};
94
9589/// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful
9690/// for reference purposes and when dealing with unknown record types.
9791pub const SymbolKind = packed enum(u16) {
......@@ -293,9 +287,9 @@ pub const SymbolKind = packed enum(u16) {
293287 S_GTHREAD32 = 4371,
294288};
295289
296const TypeIndex = u32;
290pub const TypeIndex = u32;
297291
298const ProcSym = packed struct {
292pub const ProcSym = packed struct {
299293 Parent: u32 ,
300294 End: u32 ,
301295 Next: u32 ,
......@@ -310,7 +304,7 @@ const ProcSym = packed struct {
310304 // Name: [*]u8,
311305};
312306
313const ProcSymFlags = packed struct {
307pub const ProcSymFlags = packed struct {
314308 HasFP: bool,
315309 HasIRET: bool,
316310 HasFRET: bool,
......@@ -321,24 +315,24 @@ const ProcSymFlags = packed struct {
321315 HasOptimizedDebugInfo: bool,
322316};
323317
324const SectionContrSubstreamVersion = enum(u32) {
318pub const SectionContrSubstreamVersion = enum(u32) {
325319 Ver60 = 0xeffe0000 + 19970605,
326320 V2 = 0xeffe0000 + 20140516
327321};
328322
329const RecordPrefix = packed struct {
323pub const RecordPrefix = packed struct {
330324 RecordLen: u16, /// Record length, starting from &RecordKind.
331325 RecordKind: SymbolKind, /// Record kind enum (SymRecordKind or TypeRecordKind)
332326};
333327
334const LineFragmentHeader = packed struct {
328pub const LineFragmentHeader = packed struct {
335329 RelocOffset: u32, /// Code offset of line contribution.
336330 RelocSegment: u16, /// Code segment of line contribution.
337331 Flags: LineFlags,
338332 CodeSize: u32, /// Code size of this line contribution.
339333};
340334
341const LineFlags = packed struct {
335pub const LineFlags = packed struct {
342336 LF_HaveColumns: bool, /// CV_LINES_HAVE_COLUMNS
343337 unused: u15,
344338};
......@@ -347,7 +341,7 @@ const LineFlags = packed struct {
347341/// header. The structure definitions follow.
348342/// LineNumberEntry Lines[NumLines];
349343/// ColumnNumberEntry Columns[NumLines];
350const LineBlockFragmentHeader = packed struct {
344pub const LineBlockFragmentHeader = packed struct {
351345 /// Offset of FileChecksum entry in File
352346 /// checksums buffer. The checksum entry then
353347 /// contains another offset into the string
......@@ -358,7 +352,7 @@ const LineBlockFragmentHeader = packed struct {
358352};
359353
360354
361const LineNumberEntry = packed struct {
355pub const LineNumberEntry = packed struct {
362356 Offset: u32, /// Offset to start of code bytes for line number
363357 Flags: u32,
364358
......@@ -370,19 +364,19 @@ const LineNumberEntry = packed struct {
370364 };
371365};
372366
373const ColumnNumberEntry = packed struct {
367pub const ColumnNumberEntry = packed struct {
374368 StartColumn: u16,
375369 EndColumn: u16,
376370};
377371
378372/// Checksum bytes follow.
379const FileChecksumEntryHeader = packed struct {
373pub const FileChecksumEntryHeader = packed struct {
380374 FileNameOffset: u32, /// Byte offset of filename in global string table.
381375 ChecksumSize: u8, /// Number of bytes of checksum.
382376 ChecksumKind: u8, /// FileChecksumKind
383377};
384378
385const DebugSubsectionKind = packed enum(u32) {
379pub const DebugSubsectionKind = packed enum(u32) {
386380 None = 0,
387381 Symbols = 0xf1,
388382 Lines = 0xf2,
......@@ -402,11 +396,25 @@ const DebugSubsectionKind = packed enum(u32) {
402396 CoffSymbolRVA = 0xfd,
403397};
404398
399
400pub const DebugSubsectionHeader = packed struct {
401 Kind: DebugSubsectionKind, /// codeview::DebugSubsectionKind enum
402 Length: u32, /// number of bytes occupied by this record.
403};
404
405
406pub const PDBStringTableHeader = packed struct {
407 Signature: u32, /// PDBStringTableSignature
408 HashVersion: u32, /// 1 or 2
409 ByteSize: u32, /// Number of bytes of names buffer.
410};
411
405412pub const Pdb = struct {
406413 in_file: os.File,
407414 allocator: *mem.Allocator,
408415 coff: *coff.Coff,
409416 string_table: *MsfStream,
417 dbi: *MsfStream,
410418
411419 msf: Msf,
412420
......@@ -428,230 +436,6 @@ pub const Pdb = struct {
428436 const id = @enumToInt(stream);
429437 return self.getStreamById(id);
430438 }
431
432 pub fn getSourceLine(self: *Pdb, address: usize) !void {
433 const dbi = self.getStream(StreamType.Dbi) orelse return error.InvalidDebugInfo;
434
435 // Dbi Header
436 var header: DbiStreamHeader = undefined;
437 try dbi.stream.readStruct(DbiStreamHeader, &header);
438 std.debug.warn("{}\n", header);
439 warn("after header dbi stream at {} (file offset)\n", dbi.getFilePos());
440
441 var modules = ArrayList(Module).init(self.allocator);
442
443 // Module Info Substream
444 var mod_info_offset: usize = 0;
445 while (mod_info_offset != header.ModInfoSize) {
446 var mod_info: ModInfo = undefined;
447 try dbi.stream.readStruct(ModInfo, &mod_info);
448 std.debug.warn("{}\n", mod_info);
449 var this_record_len: usize = @sizeOf(ModInfo);
450
451 const module_name = try dbi.readNullTermString(self.allocator);
452 std.debug.warn("module_name '{}'\n", module_name);
453 this_record_len += module_name.len + 1;
454
455 const obj_file_name = try dbi.readNullTermString(self.allocator);
456 std.debug.warn("obj_file_name '{}'\n", obj_file_name);
457 this_record_len += obj_file_name.len + 1;
458
459 const march_forward_bytes = this_record_len % 4;
460 if (march_forward_bytes != 0) {
461 try dbi.seekForward(march_forward_bytes);
462 this_record_len += march_forward_bytes;
463 }
464
465 try modules.append(Module{
466 .mod_info = mod_info,
467 .module_name = module_name,
468 .obj_file_name = obj_file_name,
469 });
470
471 mod_info_offset += this_record_len;
472 if (mod_info_offset > header.ModInfoSize)
473 return error.InvalidDebugInfo;
474 }
475
476 // Section Contribution Substream
477 var sect_contribs = ArrayList(SectionContribEntry).init(self.allocator);
478 std.debug.warn("looking at Section Contributinos now\n");
479 var sect_cont_offset: usize = 0;
480 if (header.SectionContributionSize != 0) {
481 const ver = @intToEnum(SectionContrSubstreamVersion, try dbi.stream.readIntLe(u32));
482 if (ver != SectionContrSubstreamVersion.Ver60)
483 return error.InvalidDebugInfo;
484 sect_cont_offset += @sizeOf(u32);
485 }
486 while (sect_cont_offset != header.SectionContributionSize) {
487 const entry = try sect_contribs.addOne();
488 try dbi.stream.readStruct(SectionContribEntry, entry);
489 std.debug.warn("{}\n", entry);
490 sect_cont_offset += @sizeOf(SectionContribEntry);
491
492 if (sect_cont_offset > header.SectionContributionSize)
493 return error.InvalidDebugInfo;
494 }
495 //std.debug.warn("looking at section map now\n");
496 //if (header.SectionMapSize == 0)
497 // return error.MissingDebugInfo;
498
499 //var sect_map_hdr: SectionMapHeader = undefined;
500 //try dbi.stream.readStruct(SectionMapHeader, &sect_map_hdr);
501
502 //const sect_entries = try self.allocator.alloc(SectionMapEntry, sect_map_hdr.Count);
503 //const as_bytes = @sliceToBytes(sect_entries);
504 //if (as_bytes.len + @sizeOf(SectionMapHeader) != header.SectionMapSize)
505 // return error.InvalidDebugInfo;
506 //try dbi.stream.readNoEof(as_bytes);
507
508 //for (sect_entries) |sect_entry| {
509 // std.debug.warn("{}\n", sect_entry);
510 //}
511
512 var coff_section: *coff.Section = undefined;
513 const mod_index = for (sect_contribs.toSlice()) |sect_contrib| {
514 coff_section = &self.coff.sections.toSlice()[sect_contrib.Section];
515 std.debug.warn("looking in coff name: {}\n", mem.toSliceConst(u8, &coff_section.header.name));
516
517 const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
518 const vaddr_end = vaddr_start + sect_contrib.Size;
519 if (address >= vaddr_start and address < vaddr_end) {
520 std.debug.warn("found sect contrib: {}\n", sect_contrib);
521 break sect_contrib.ModuleIndex;
522 }
523 } else return error.MissingDebugInfo;
524
525 const mod = &modules.toSlice()[mod_index];
526 const modi = self.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.InvalidDebugInfo;
527
528 const signature = try modi.stream.readIntLe(u32);
529 if (signature != 4)
530 return error.InvalidDebugInfo;
531
532 const symbols = try self.allocator.alloc(u8, mod.mod_info.SymByteSize - 4);
533 std.debug.warn("read {} bytes of symbol info\n", symbols.len);
534 try modi.stream.readNoEof(symbols);
535 var symbol_i: usize = 0;
536 const proc_sym = while (symbol_i != symbols.len) {
537 const prefix = @ptrCast(*RecordPrefix, &symbols[symbol_i]);
538 if (prefix.RecordLen < 2)
539 return error.InvalidDebugInfo;
540 switch (prefix.RecordKind) {
541 SymbolKind.S_LPROC32 => {
542 const proc_sym = @ptrCast(*ProcSym, &symbols[symbol_i + @sizeOf(RecordPrefix)]);
543 const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset;
544 const vaddr_end = vaddr_start + proc_sym.CodeSize;
545 std.debug.warn(" {}\n", proc_sym);
546 if (address >= vaddr_start and address < vaddr_end) {
547 break proc_sym;
548 }
549 },
550 else => {},
551 }
552 symbol_i += prefix.RecordLen + @sizeOf(u16);
553 if (symbol_i > symbols.len)
554 return error.InvalidDebugInfo;
555 } else return error.MissingDebugInfo;
556
557 std.debug.warn("found in {s}: {}\n", @ptrCast([*]u8, proc_sym) + @sizeOf(ProcSym), proc_sym);
558
559 if (mod.mod_info.C11ByteSize != 0)
560 return error.InvalidDebugInfo;
561
562 if (mod.mod_info.C13ByteSize == 0) {
563 return error.MissingDebugInfo;
564 }
565
566 const subsect_info = try self.allocator.alloc(u8, mod.mod_info.C13ByteSize);
567 std.debug.warn("read C13 line info {} bytes\n", subsect_info.len);
568 const line_info_file_pos = modi.getFilePos();
569 try modi.stream.readNoEof(subsect_info);
570
571 const DebugSubsectionHeader = packed struct {
572 Kind: DebugSubsectionKind, /// codeview::DebugSubsectionKind enum
573 Length: u32, /// number of bytes occupied by this record.
574 };
575 var sect_offset: usize = 0;
576 var skip_len: usize = undefined;
577 var have_line_info: bool = false;
578 subsections: while (sect_offset != subsect_info.len) : (sect_offset += skip_len) {
579 const subsect_hdr = @ptrCast(*DebugSubsectionHeader, &subsect_info[sect_offset]);
580 skip_len = subsect_hdr.Length;
581 sect_offset += @sizeOf(DebugSubsectionHeader);
582
583 switch (subsect_hdr.Kind) {
584 DebugSubsectionKind.Lines => {
585 if (have_line_info)
586 continue :subsections;
587
588 var line_index: usize = sect_offset;
589
590 const line_hdr = @ptrCast(*LineFragmentHeader, &subsect_info[line_index]);
591 if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo;
592 std.debug.warn("{}\n", line_hdr);
593 line_index += @sizeOf(LineFragmentHeader);
594
595 const block_hdr = @ptrCast(*LineBlockFragmentHeader, &subsect_info[line_index]);
596 std.debug.warn("{}\n", block_hdr);
597 line_index += @sizeOf(LineBlockFragmentHeader);
598
599 const has_column = line_hdr.Flags.LF_HaveColumns;
600 std.debug.warn("has column: {}\n", has_column);
601
602 const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset;
603 const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize;
604 if (address >= frag_vaddr_start and address < frag_vaddr_end) {
605 std.debug.warn("found line listing\n");
606 var line_i: usize = 0;
607 const start_line_index = line_index;
608 while (line_i < block_hdr.NumLines) : (line_i += 1) {
609 const line_num_entry = @ptrCast(*LineNumberEntry, &subsect_info[line_index]);
610 line_index += @sizeOf(LineNumberEntry);
611 const flags = @ptrCast(*LineNumberEntry.Flags, &line_num_entry.Flags);
612 std.debug.warn("{} {}\n", line_num_entry, flags);
613 const vaddr_start = frag_vaddr_start + line_num_entry.Offset;
614 const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End;
615 std.debug.warn("test {x} <= {x} < {x}\n", vaddr_start, address, vaddr_end);
616 if (address >= vaddr_start and address < vaddr_end) {
617 std.debug.warn("{} line {}\n", block_hdr.NameIndex, flags.Start);
618 if (has_column) {
619 line_index = start_line_index + @sizeOf(LineNumberEntry) * block_hdr.NumLines;
620 line_index += @sizeOf(ColumnNumberEntry) * line_i;
621 const col_num_entry = @ptrCast(*ColumnNumberEntry, &subsect_info[line_index]);
622 std.debug.warn("col {}\n", col_num_entry.StartColumn);
623 }
624 have_line_info = true;
625 continue :subsections;
626 }
627 }
628 return error.MissingDebugInfo;
629 }
630
631 },
632 DebugSubsectionKind.FileChecksums => {
633 var chksum_index: usize = sect_offset;
634
635 while (chksum_index != subsect_info.len) {
636 const chksum_hdr = @ptrCast(*FileChecksumEntryHeader, &subsect_info[chksum_index]);
637 std.debug.warn("{}\n", chksum_hdr);
638 const len = @sizeOf(FileChecksumEntryHeader) + chksum_hdr.ChecksumSize;
639 chksum_index += len + (len % 4);
640 if (chksum_index > subsect_info.len)
641 return error.InvalidDebugInfo;
642 }
643
644 },
645 else => {
646 std.debug.warn("ignore subsection {}\n", @tagName(subsect_hdr.Kind));
647 },
648 }
649
650 if (sect_offset > subsect_info.len)
651 return error.InvalidDebugInfo;
652 }
653 std.debug.warn("end subsections\n");
654 }
655439};
656440
657441// see https://llvm.org/docs/PDB/MsfFile.html
......@@ -687,13 +471,11 @@ const Msf = struct {
687471 );
688472
689473 const stream_count = try self.directory.stream.readIntLe(u32);
690 warn("stream count {}\n", stream_count);
691474
692475 const stream_sizes = try allocator.alloc(u32, stream_count);
693476 for (stream_sizes) |*s| {
694477 const size = try self.directory.stream.readIntLe(u32);
695478 s.* = blockCountFromSize(size, superblock.BlockSize);
696 warn("stream {}B {} blocks\n", size, s.*);
697479 }
698480
699481 self.streams = try allocator.alloc(MsfStream, stream_count);
......@@ -784,13 +566,10 @@ const MsfStream = struct {
784566 const in = &file_stream.stream;
785567 try file.seekTo(pos);
786568
787 warn("stream with blocks");
788569 var i: u32 = 0;
789570 while (i < block_count) : (i += 1) {
790571 stream.blocks[i] = try in.readIntLe(u32);
791 warn(" {}", stream.blocks[i]);
792572 }
793 warn("\n");
794573
795574 return stream;
796575 }
......@@ -812,10 +591,6 @@ const MsfStream = struct {
812591 var block = self.blocks[block_id];
813592 var offset = self.pos % self.block_size;
814593
815 //std.debug.warn("seek {} read {}B: block_id={} block={} offset={}\n",
816 // block * self.block_size + offset,
817 // buffer.len, block_id, block, offset);
818
819594 try self.in_file.seekTo(block * self.block_size + offset);
820595 var file_stream = io.FileInStream.init(self.in_file);
821596 const in = &file_stream.stream;