authorgravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-11 01:01:22-07:00
committergravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-12 04:01:30-07:00
log334f40576e48e7204455f264c883f9ea86588e1a
treeca7043b94d43e493c16ed91e16593806bcdb0da0
parent4efbb27aa2b841357c03d6ab158418ccad022415

Cleans up some PDB parsing logic


1 files changed, 34 insertions(+), 29 deletions(-)

lib/std/debug/Pdb.zig+34-29
...@@ -315,6 +315,21 @@ pub const BinaryAnnotation = union(enum) {...@@ -315,6 +315,21 @@ pub const BinaryAnnotation = union(enum) {
315 file_id: ?u32,315 file_id: ?u32,
316 code_offset: u32,316 code_offset: u32,
317 code_length: ?u32,317 code_length: ?u32,
318
319 /// Resolves a partial range to a range with a definite length, or returns null if this
320 /// is not possible.
321 fn resolve(self: PartialRange, next_code_offset: ?u32) ?Range {
322 return .{
323 .line_offset = self.line_offset,
324 .file_id = self.file_id,
325 .code_offset = self.code_offset,
326 .code_length = b: {
327 if (self.code_length) |l| break :b l;
328 const end = next_code_offset orelse return null;
329 break :b end - self.code_offset;
330 },
331 };
332 }
318 };333 };
319334
320 pub fn init(annotations: Iterator) RangeIterator {335 pub fn init(annotations: Iterator) RangeIterator {
...@@ -391,6 +406,8 @@ pub const BinaryAnnotation = union(enum) {...@@ -391,6 +406,8 @@ pub const BinaryAnnotation = union(enum) {
391 },406 },
392 }407 }
393408
409 // If we have a new code offset, return the previous range if it exists, resolving
410 // its length if necessary.
394 switch (annotation) {411 switch (annotation) {
395 .change_code_offset,412 .change_code_offset,
396 .change_code_offset_and_line_offset,413 .change_code_offset_and_line_offset,
...@@ -398,38 +415,16 @@ pub const BinaryAnnotation = union(enum) {...@@ -398,38 +415,16 @@ pub const BinaryAnnotation = union(enum) {
398 => {},415 => {},
399 else => continue,416 else => continue,
400 }417 }
401418 defer self.prev = self.curr;
402 if (self.prev) |*prev| {
403 if (prev.code_length == null) {
404 prev.code_length = self.curr.code_offset - prev.code_offset;
405 }
406 }
407
408 defer self.prev = .{
409 .code_offset = self.curr.code_offset,
410 .code_length = self.curr.code_length,
411 .line_offset = self.curr.line_offset,
412 .file_id = self.curr.file_id,
413 };
414 const prev = self.prev orelse continue;419 const prev = self.prev orelse continue;
415 const prev_code_length = prev.code_length orelse continue;420 return prev.resolve(self.curr.code_offset);
416 return .{
417 .code_offset = prev.code_offset,
418 .code_length = prev_code_length,
419 .line_offset = prev.line_offset,
420 .file_id = prev.file_id,
421 };
422 }421 }
423422
423 // If we've processed all the binary operations but still have a previous range leftover
424 // with a known length, return it.
424 const prev = self.prev orelse return null;425 const prev = self.prev orelse return null;
425 defer self.prev = null;426 defer self.prev = null;
426 const prev_code_length = prev.code_length orelse return null;427 return prev.resolve(null);
427 return .{
428 .code_offset = prev.code_offset,
429 .code_length = prev_code_length,
430 .line_offset = prev.line_offset,
431 .file_id = prev.file_id,
432 };
433 }428 }
434 };429 };
435430
...@@ -452,6 +447,10 @@ pub const BinaryAnnotation = union(enum) {...@@ -452,6 +447,10 @@ pub const BinaryAnnotation = union(enum) {
452 try takePackedU32(reader),447 try takePackedU32(reader),
453 ) orelse return error.ReadFailed;448 ) orelse return error.ReadFailed;
454 switch (op) {449 switch (op) {
450 // Microsoft's docs say that invalid is used as padding, though it is left ambiguous
451 // whether padding is allowed internally or only after all instructions are complete.
452 // Empircally, the latter appears to be the case, at lest with the output from LLVM that
453 // I've tested.
455 .invalid => return error.EndOfStream,454 .invalid => return error.EndOfStream,
456 .code_offset => return .{455 .code_offset => return .{
457 .code_offset = try expect(takePackedU32(reader)),456 .code_offset = try expect(takePackedU32(reader)),
...@@ -547,13 +546,19 @@ pub const BinaryAnnotation = union(enum) {...@@ -547,13 +546,19 @@ pub const BinaryAnnotation = union(enum) {
547};546};
548547
549pub fn findInlineeName(self: *const Pdb, inlinee: u32) ?[]const u8 {548pub fn findInlineeName(self: *const Pdb, inlinee: u32) ?[]const u8 {
549 // According to LLVM, the high bit *can* be used to indicate that a type index comes from the
550 // ipi stream in which case that bit needs to be cleared. LLVM doesn't generate data in this
551 // manner, but we may as well handle it since it just involves a single bitwise and.
552 // https://llvm.org/docs/PDB/TpiStream.html#type-indices
553 const type_index = inlinee & 0x7FFFFFFF;
554
550 var reader: Io.Reader = .fixed(self.ipi orelse return null);555 var reader: Io.Reader = .fixed(self.ipi orelse return null);
551 const header = reader.takeStructPointer(pdb.IpiStreamHeader) catch return null;556 const header = reader.takeStructPointer(pdb.IpiStreamHeader) catch return null;
552 for (header.type_index_begin..header.type_index_end) |type_index| {557 for (header.type_index_begin..header.type_index_end) |curr_type_index| {
553 const prefix = reader.takeStructPointer(pdb.LfRecordPrefix) catch return null;558 const prefix = reader.takeStructPointer(pdb.LfRecordPrefix) catch return null;
554 reader.discardAll(prefix.len - @sizeOf(@FieldType(pdb.LfRecordPrefix, "len"))) catch return null;559 reader.discardAll(prefix.len - @sizeOf(@FieldType(pdb.LfRecordPrefix, "len"))) catch return null;
555560
556 if (type_index == inlinee) {561 if (curr_type_index == type_index) {
557 switch (prefix.kind) {562 switch (prefix.kind) {
558 .func_id => {563 .func_id => {
559 const func: *align(1) pdb.LfFuncId = @ptrCast(prefix);564 const func: *align(1) pdb.LfFuncId = @ptrCast(prefix);