| ... | @@ -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 | }; |
| 319 | | 334 | |
| 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 | } |
| 393 | | 408 | |
| | 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 | } |
| 401 | | 418 | 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 | } |
| 423 | | 422 | |
| | 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 | }; |
| 435 | | 430 | |
| ... | @@ -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 | }; |
| 548 | | 547 | |
| 549 | pub fn findInlineeName(self: *const Pdb, inlinee: u32) ?[]const u8 { | 548 | pub 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; |
| 555 | | 560 | |
| 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); |