Index: llvm/include/llvm/DebugInfo/CodeView/ModuleDebugFileChecksumFragment.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/ModuleDebugFileChecksumFragment.h +++ llvm/include/llvm/DebugInfo/CodeView/ModuleDebugFileChecksumFragment.h @@ -11,7 +11,9 @@ #define LLVM_DEBUGINFO_CODEVIEW_MODULEDEBUGFILECHECKSUMFRAGMENT_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/DebugInfo/CodeView/ModuleDebugFragment.h" +#include "llvm/Support/Allocator.h" #include "llvm/Support/BinaryStreamArray.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Endian.h" @@ -61,6 +63,28 @@ private: FileChecksumArray Checksums; }; + +class ModuleDebugFileChecksumFragment final : public ModuleDebugFragment { +public: + ModuleDebugFileChecksumFragment(); + + static bool classof(const ModuleDebugFragment *S) { + return S->kind() == ModuleDebugFragmentKind::FileChecksums; + } + + void addChecksum(uint32_t StringTableOffset, FileChecksumKind Kind, + ArrayRef Bytes); + + uint32_t calculateSerializedLength() override; + Error commit(BinaryStreamWriter &Writer) override; + uint32_t mapChecksumOffset(uint32_t StringTableOffset) const; + +private: + DenseMap OffsetMap; + uint32_t SerializedSize = 0; + llvm::BumpPtrAllocator Storage; + std::vector Checksums; +}; } } Index: llvm/include/llvm/DebugInfo/CodeView/ModuleDebugFragment.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/ModuleDebugFragment.h +++ llvm/include/llvm/DebugInfo/CodeView/ModuleDebugFragment.h @@ -11,6 +11,7 @@ #define LLVM_DEBUGINFO_CODEVIEW_MODULEDEBUGFRAGMENT_H #include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Casting.h" namespace llvm { @@ -27,6 +28,20 @@ ModuleDebugFragmentKind Kind; }; +class ModuleDebugFragment { +public: + explicit ModuleDebugFragment(ModuleDebugFragmentKind Kind) : Kind(Kind) {} + virtual ~ModuleDebugFragment(); + + ModuleDebugFragmentKind kind() const { return Kind; } + + virtual Error commit(BinaryStreamWriter &Writer) = 0; + virtual uint32_t calculateSerializedLength() = 0; + +protected: + ModuleDebugFragmentKind Kind; +}; + } // namespace codeview } // namespace llvm Index: llvm/include/llvm/DebugInfo/CodeView/ModuleDebugFragmentRecord.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/ModuleDebugFragmentRecord.h +++ llvm/include/llvm/DebugInfo/CodeView/ModuleDebugFragmentRecord.h @@ -13,12 +13,15 @@ #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/Support/BinaryStreamArray.h" #include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" namespace llvm { namespace codeview { +class ModuleDebugFragment; + // Corresponds to the `CV_DebugSSubsectionHeader_t` structure. struct ModuleDebugFragmentHeader { support::ulittle32_t Kind; // codeview::ModuleDebugFragmentKind enum @@ -32,6 +35,7 @@ static Error initialize(BinaryStreamRef Stream, ModuleDebugFragmentRecord &Info); + uint32_t getRecordLength() const; ModuleDebugFragmentKind kind() const; BinaryStreamRef getRecordData() const; @@ -41,6 +45,18 @@ BinaryStreamRef Data; }; +class ModuleDebugFragmentRecordBuilder { +public: + ModuleDebugFragmentRecordBuilder(ModuleDebugFragmentKind Kind, + ModuleDebugFragment &Frag); + uint32_t calculateSerializedLength(); + Error commit(BinaryStreamWriter &Writer); + +private: + ModuleDebugFragmentKind Kind; + ModuleDebugFragment &Frag; +}; + typedef VarStreamArray ModuleDebugFragmentArray; } // namespace codeview Index: llvm/include/llvm/DebugInfo/CodeView/ModuleDebugLineFragment.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/ModuleDebugLineFragment.h +++ llvm/include/llvm/DebugInfo/CodeView/ModuleDebugLineFragment.h @@ -10,6 +10,7 @@ #ifndef LLVM_DEBUGINFO_CODEVIEW_MODULEDEBUGLINEFRAGMENT_H #define LLVM_DEBUGINFO_CODEVIEW_MODULEDEBUGLINEFRAGMENT_H +#include "llvm/DebugInfo/CodeView/Line.h" #include "llvm/DebugInfo/CodeView/ModuleDebugFragment.h" #include "llvm/Support/BinaryStreamArray.h" #include "llvm/Support/BinaryStreamReader.h" @@ -28,7 +29,10 @@ // Corresponds to the `CV_DebugSLinesFileBlockHeader_t` structure. struct LineBlockFragmentHeader { - support::ulittle32_t NameIndex; // Index in DBI name buffer of filename. + support::ulittle32_t NameIndex; // Offset of FileChecksum entry in File + // checksums buffer. The checksum entry then + // contains another offset into the string + // table of the actual name. support::ulittle32_t NumLines; // Number of lines support::ulittle32_t BlockSize; // Code size of block, in bytes. // The following two variable length arrays appear immediately after the @@ -88,6 +92,45 @@ const LineFragmentHeader *Header = nullptr; LineInfoArray LinesAndColumns; }; + +class ModuleDebugLineFragment final : public ModuleDebugFragment { + struct Block { + Block(uint32_t ChecksumBufferOffset) + : ChecksumBufferOffset(ChecksumBufferOffset) {} + + uint32_t ChecksumBufferOffset; + std::vector Lines; + std::vector Columns; + }; + +public: + ModuleDebugLineFragment(); + + static bool classof(const ModuleDebugFragment *S) { + return S->kind() == ModuleDebugFragmentKind::Lines; + } + + void createBlock(uint32_t ChecksumBufferOffset); + void addLineInfo(uint32_t Offset, const LineInfo &Line); + void addLineAndColumnInfo(uint32_t Offset, const LineInfo &Line, + uint32_t ColStart, uint32_t ColEnd); + + uint32_t calculateSerializedLength() override; + Error commit(BinaryStreamWriter &Writer) override; + + void setRelocationAddress(uint16_t Segment, uint16_t Offset); + void setCodeSize(uint32_t Size); + void setFlags(LineFlags Flags); + + bool hasColumnInfo() const; + +private: + uint16_t RelocOffset = 0; + uint16_t RelocSegment = 0; + uint32_t CodeSize = 0; + LineFlags Flags = LF_None; + std::vector Blocks; +}; } } Index: llvm/include/llvm/DebugInfo/CodeView/TypeDatabase.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/TypeDatabase.h +++ llvm/include/llvm/DebugInfo/CodeView/TypeDatabase.h @@ -35,6 +35,7 @@ StringRef getTypeName(TypeIndex Index) const; const CVType &getTypeRecord(TypeIndex Index) const; + CVType &getTypeRecord(TypeIndex Index); bool containsTypeIndex(TypeIndex Index) const; Index: llvm/include/llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h =================================================================== --- llvm/include/llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h +++ llvm/include/llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h @@ -11,6 +11,8 @@ #define LLVM_DEBUGINFO_PDB_RAW_DBIMODULEDESCRIPTORBUILDER_H #include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/CodeView/ModuleDebugFileChecksumFragment.h" +#include "llvm/DebugInfo/CodeView/ModuleDebugLineFragment.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" #include "llvm/Support/Error.h" @@ -21,6 +23,10 @@ namespace llvm { class BinaryStreamWriter; +namespace codeview { +class ModuleDebugFragmentRecordBuilder; +} + namespace msf { class MSFBuilder; struct MSFLayout; @@ -33,6 +39,7 @@ public: DbiModuleDescriptorBuilder(StringRef ModuleName, uint32_t ModIndex, msf::MSFBuilder &Msf); + ~DbiModuleDescriptorBuilder(); DbiModuleDescriptorBuilder(const DbiModuleDescriptorBuilder &) = delete; DbiModuleDescriptorBuilder & @@ -41,6 +48,11 @@ void setObjFileName(StringRef Name); void addSymbol(codeview::CVSymbol Symbol); + void + addC13LineFragment(std::unique_ptr Lines); + void setC13FileChecksums( + std::unique_ptr Checksums); + uint16_t getStreamIndex() const; StringRef getModuleName() const { return ModuleName; } StringRef getObjFileName() const { return ObjFileName; } @@ -58,6 +70,8 @@ WritableBinaryStreamRef MsfBuffer); private: + uint32_t calculateC13DebugInfoSize() const; + void addSourceFile(StringRef Path); msf::MSFBuilder &MSF; @@ -66,6 +80,12 @@ std::string ObjFileName; std::vector SourceFiles; std::vector Symbols; + std::vector> LineInfo; + std::unique_ptr ChecksumInfo; + + std::vector> + C13Builders; + ModuleInfoHeader Layout; }; Index: llvm/include/llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h =================================================================== --- llvm/include/llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h +++ llvm/include/llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h @@ -59,6 +59,7 @@ Expected addModuleInfo(StringRef ModuleName); Error addModuleSourceFile(StringRef Module, StringRef File); + Expected getSourceFileNameIndex(StringRef FileName); Error finalizeMsfLayout(); Index: llvm/include/llvm/DebugInfo/PDB/Native/StringTableBuilder.h =================================================================== --- llvm/include/llvm/DebugInfo/PDB/Native/StringTableBuilder.h +++ llvm/include/llvm/DebugInfo/PDB/Native/StringTableBuilder.h @@ -29,6 +29,7 @@ // If string S does not exist in the string table, insert it. // Returns the ID for S. uint32_t insert(StringRef S); + uint32_t getStringIndex(StringRef S); uint32_t finalize(); Error commit(BinaryStreamWriter &Writer) const; Index: llvm/lib/DebugInfo/CodeView/ModuleDebugFileChecksumFragment.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/ModuleDebugFileChecksumFragment.cpp +++ llvm/lib/DebugInfo/CodeView/ModuleDebugFileChecksumFragment.cpp @@ -48,3 +48,50 @@ return Error::success(); } + +ModuleDebugFileChecksumFragment::ModuleDebugFileChecksumFragment() + : ModuleDebugFragment(ModuleDebugFragmentKind::FileChecksums) {} + +void ModuleDebugFileChecksumFragment::addChecksum(uint32_t StringTableOffset, + FileChecksumKind Kind, + ArrayRef Bytes) { + FileChecksumEntry Entry; + if (!Bytes.empty()) { + uint8_t *Copy = Storage.Allocate(Bytes.size()); + ::memcpy(Copy, Bytes.data(), Bytes.size()); + Entry.Checksum = makeArrayRef(Copy, Bytes.size()); + } + Entry.FileNameOffset = StringTableOffset; + Entry.Kind = Kind; + Checksums.push_back(Entry); + + // This maps the offset of this string in the string table to the offset + // of this checksum entry in the checksum buffer. + OffsetMap[StringTableOffset] = SerializedSize; + SerializedSize += sizeof(FileChecksumEntryHeader) + Bytes.size(); +} + +uint32_t ModuleDebugFileChecksumFragment::calculateSerializedLength() { + return SerializedSize; +} + +Error ModuleDebugFileChecksumFragment::commit(BinaryStreamWriter &Writer) { + for (const auto &FC : Checksums) { + FileChecksumEntryHeader Header; + Header.ChecksumKind = uint8_t(FC.Kind); + Header.ChecksumSize = FC.Checksum.size(); + Header.FileNameOffset = FC.FileNameOffset; + if (auto EC = Writer.writeObject(Header)) + return EC; + if (auto EC = Writer.writeArray(makeArrayRef(FC.Checksum))) + return EC; + } + return Error::success(); +} + +uint32_t ModuleDebugFileChecksumFragment::mapChecksumOffset( + uint32_t StringTableOffset) const { + auto Iter = OffsetMap.find(StringTableOffset); + assert(Iter != OffsetMap.end()); + return Iter->second; +} Index: llvm/lib/DebugInfo/CodeView/ModuleDebugFragment.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/ModuleDebugFragment.cpp +++ llvm/lib/DebugInfo/CodeView/ModuleDebugFragment.cpp @@ -12,3 +12,5 @@ using namespace llvm::codeview; ModuleDebugFragmentRef::~ModuleDebugFragmentRef() {} + +ModuleDebugFragment::~ModuleDebugFragment() {} Index: llvm/lib/DebugInfo/CodeView/ModuleDebugFragmentRecord.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/ModuleDebugFragmentRecord.cpp +++ llvm/lib/DebugInfo/CodeView/ModuleDebugFragmentRecord.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/CodeView/ModuleDebugFragmentRecord.h" +#include "llvm/DebugInfo/CodeView/ModuleDebugFragment.h" #include "llvm/Support/BinaryStreamReader.h" @@ -30,6 +31,14 @@ ModuleDebugFragmentKind Kind = static_cast(uint32_t(Header->Kind)); + switch (Kind) { + case ModuleDebugFragmentKind::FileChecksums: + case ModuleDebugFragmentKind::Lines: + case ModuleDebugFragmentKind::InlineeLines: + break; + default: + __debugbreak(); + } if (auto EC = Reader.readStreamRef(Info.Data, Header->Length)) return EC; Info.Kind = Kind; @@ -37,7 +46,9 @@ } uint32_t ModuleDebugFragmentRecord::getRecordLength() const { - return sizeof(ModuleDebugFragmentHeader) + Data.getLength(); + uint32_t Result = sizeof(ModuleDebugFragmentHeader) + Data.getLength(); + assert(Result % 4 == 0); + return Result; } ModuleDebugFragmentKind ModuleDebugFragmentRecord::kind() const { return Kind; } @@ -45,3 +56,29 @@ BinaryStreamRef ModuleDebugFragmentRecord::getRecordData() const { return Data; } + +ModuleDebugFragmentRecordBuilder::ModuleDebugFragmentRecordBuilder( + ModuleDebugFragmentKind Kind, ModuleDebugFragment &Frag) + : Kind(Kind), Frag(Frag) {} + +uint32_t ModuleDebugFragmentRecordBuilder::calculateSerializedLength() { + uint32_t Size = sizeof(ModuleDebugFragmentHeader) + + alignTo(Frag.calculateSerializedLength(), 4); + return Size; +} + +Error ModuleDebugFragmentRecordBuilder::commit(BinaryStreamWriter &Writer) { + ModuleDebugFragmentHeader Header; + Header.Kind = uint32_t(Kind); + Header.Length = + calculateSerializedLength() - sizeof(ModuleDebugFragmentHeader); + + if (auto EC = Writer.writeObject(Header)) + return EC; + if (auto EC = Frag.commit(Writer)) + return EC; + if (auto EC = Writer.padToAlignment(4)) + return EC; + + return Error::success(); +} Index: llvm/lib/DebugInfo/CodeView/ModuleDebugLineFragment.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/ModuleDebugLineFragment.cpp +++ llvm/lib/DebugInfo/CodeView/ModuleDebugLineFragment.cpp @@ -64,3 +64,92 @@ bool ModuleDebugLineFragmentRef::hasColumnInfo() const { return !!(Header->Flags & LF_HaveColumns); } + +ModuleDebugLineFragment::ModuleDebugLineFragment() + : ModuleDebugFragment(ModuleDebugFragmentKind::Lines) {} + +void ModuleDebugLineFragment::createBlock(uint32_t ChecksumBufferOffset) { + Blocks.emplace_back(ChecksumBufferOffset); +} + +void ModuleDebugLineFragment::addLineInfo(uint32_t Offset, + const LineInfo &Line) { + Block &B = Blocks.back(); + LineNumberEntry LNE; + LNE.Flags = Line.getRawData(); + LNE.Offset = Offset; + B.Lines.push_back(LNE); +} + +void ModuleDebugLineFragment::addLineAndColumnInfo(uint32_t Offset, + const LineInfo &Line, + uint32_t ColStart, + uint32_t ColEnd) { + Block &B = Blocks.back(); + assert(B.Lines.size() == B.Columns.size()); + + addLineInfo(Offset, Line); + ColumnNumberEntry CNE; + CNE.StartColumn = ColStart; + CNE.EndColumn = ColEnd; + B.Columns.push_back(CNE); +} + +Error ModuleDebugLineFragment::commit(BinaryStreamWriter &Writer) { + LineFragmentHeader Header; + Header.CodeSize = CodeSize; + Header.Flags = hasColumnInfo() ? LF_HaveColumns : 0; + Header.RelocOffset = RelocOffset; + Header.RelocSegment = RelocSegment; + + if (auto EC = Writer.writeObject(Header)) + return EC; + + for (const auto &B : Blocks) { + LineBlockFragmentHeader BlockHeader; + assert(B.Lines.size() == B.Columns.size() || B.Columns.empty()); + + BlockHeader.NumLines = B.Lines.size(); + BlockHeader.BlockSize = sizeof(LineBlockFragmentHeader); + BlockHeader.BlockSize += BlockHeader.NumLines * sizeof(LineNumberEntry); + if (hasColumnInfo()) + BlockHeader.BlockSize += BlockHeader.NumLines * sizeof(ColumnNumberEntry); + BlockHeader.NameIndex = B.ChecksumBufferOffset; + if (auto EC = Writer.writeObject(BlockHeader)) + return EC; + + if (auto EC = Writer.writeArray(makeArrayRef(B.Lines))) + return EC; + + if (hasColumnInfo()) { + if (auto EC = Writer.writeArray(makeArrayRef(B.Columns))) + return EC; + } + } + return Error::success(); +} + +uint32_t ModuleDebugLineFragment::calculateSerializedLength() { + uint32_t Size = sizeof(LineFragmentHeader); + for (const auto &B : Blocks) { + Size += sizeof(LineBlockFragmentHeader); + Size += B.Lines.size() * sizeof(LineNumberEntry); + if (hasColumnInfo()) + Size += B.Columns.size() * sizeof(ColumnNumberEntry); + } + return Size; +} + +void ModuleDebugLineFragment::setRelocationAddress(uint16_t Segment, + uint16_t Offset) { + RelocOffset = Offset; + RelocSegment = Segment; +} + +void ModuleDebugLineFragment::setCodeSize(uint32_t Size) { CodeSize = Size; } + +void ModuleDebugLineFragment::setFlags(LineFlags Flags) { this->Flags = Flags; } + +bool ModuleDebugLineFragment::hasColumnInfo() const { + return Flags & LF_HaveColumns; +} Index: llvm/lib/DebugInfo/CodeView/TypeDatabase.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/TypeDatabase.cpp +++ llvm/lib/DebugInfo/CodeView/TypeDatabase.cpp @@ -110,6 +110,10 @@ return TypeRecords[Index.getIndex() - TypeIndex::FirstNonSimpleIndex]; } +CVType &TypeDatabase::getTypeRecord(TypeIndex Index) { + return TypeRecords[Index.getIndex() - TypeIndex::FirstNonSimpleIndex]; +} + bool TypeDatabase::containsTypeIndex(TypeIndex Index) const { uint32_t I = Index.getIndex() - TypeIndex::FirstNonSimpleIndex; return I < CVUDTNames.size(); Index: llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp +++ llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp @@ -10,6 +10,7 @@ #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/ModuleDebugFragmentRecord.h" #include "llvm/DebugInfo/MSF/MSFBuilder.h" #include "llvm/DebugInfo/MSF/MSFCommon.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" @@ -35,11 +36,12 @@ }; } -static uint32_t calculateDiSymbolStreamSize(uint32_t SymbolByteSize) { +static uint32_t calculateDiSymbolStreamSize(uint32_t SymbolByteSize, + uint32_t C13Size) { uint32_t Size = sizeof(uint32_t); // Signature Size += SymbolByteSize; // Symbol Data - Size += 0; // TODO: Layout.LineBytes - Size += 0; // TODO: Layout.C13Bytes + Size += 0; // TODO: Layout.C11Bytes + Size += C13Size; // TODO: Layout.C13Bytes Size += sizeof(uint32_t); // GlobalRefs substream size (always 0) Size += 0; // GlobalRefs substream bytes return Size; @@ -52,6 +54,8 @@ Layout.Mod = ModIndex; } +DbiModuleDescriptorBuilder::~DbiModuleDescriptorBuilder() {} + uint16_t DbiModuleDescriptorBuilder::getStreamIndex() const { return Layout.ModDiStream; } @@ -69,6 +73,15 @@ SourceFiles.push_back(Path); } +uint32_t DbiModuleDescriptorBuilder::calculateC13DebugInfoSize() const { + uint32_t Result = 0; + for (const auto &Builder : C13Builders) { + assert(Builder && "Empty C13 Fragment Builder!"); + Result += Builder->calculateSerializedLength(); + } + return Result; +} + uint32_t DbiModuleDescriptorBuilder::calculateSerializedLength() const { uint32_t L = sizeof(Layout); uint32_t M = ModuleName.size() + 1; @@ -80,7 +93,7 @@ Layout.FileNameOffs = 0; // TODO: Fix this Layout.Flags = 0; // TODO: Fix this Layout.C11Bytes = 0; - Layout.C13Bytes = 0; + Layout.C13Bytes = calculateC13DebugInfoSize(); (void)Layout.Mod; // Set in constructor (void)Layout.ModDiStream; // Set in finalizeMsfLayout Layout.NumFiles = SourceFiles.size(); @@ -94,7 +107,9 @@ Error DbiModuleDescriptorBuilder::finalizeMsfLayout() { this->Layout.ModDiStream = kInvalidStreamIndex; - auto ExpectedSN = MSF.addStream(calculateDiSymbolStreamSize(SymbolByteSize)); + uint32_t C13Size = calculateC13DebugInfoSize(); + auto ExpectedSN = + MSF.addStream(calculateDiSymbolStreamSize(SymbolByteSize, C13Size)); if (!ExpectedSN) return ExpectedSN.takeError(); Layout.ModDiStream = *ExpectedSN; @@ -130,7 +145,13 @@ if (auto EC = SymbolWriter.writeStreamRef(RecordsRef)) return EC; // TODO: Write C11 Line data - // TODO: Write C13 Line data + + for (const auto &Builder : C13Builders) { + assert(Builder && "Empty C13 Fragment Builder!"); + if (auto EC = Builder->commit(SymbolWriter)) + return EC; + } + // TODO: Figure out what GlobalRefs substream actually is and populate it. if (auto EC = SymbolWriter.writeInteger(0)) return EC; @@ -139,3 +160,29 @@ } return Error::success(); } + +void DbiModuleDescriptorBuilder::addC13LineFragment( + std::unique_ptr Lines) { + ModuleDebugLineFragment &Frag = *Lines; + + // File Checksums have to come first, so push an empty entry on if this + // is the first. + if (C13Builders.empty()) + C13Builders.push_back(nullptr); + + this->LineInfo.push_back(std::move(Lines)); + C13Builders.push_back( + llvm::make_unique(Frag.kind(), Frag)); +} + +void DbiModuleDescriptorBuilder::setC13FileChecksums( + std::unique_ptr Checksums) { + assert(!ChecksumInfo && "Can't have more than one checksum info!"); + + if (C13Builders.empty()) + C13Builders.push_back(nullptr); + + ChecksumInfo = std::move(Checksums); + C13Builders[0] = llvm::make_unique( + ChecksumInfo->kind(), *ChecksumInfo); +} Index: llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp +++ llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp @@ -101,6 +101,14 @@ return Error::success(); } +Expected DbiStreamBuilder::getSourceFileNameIndex(StringRef File) { + auto NameIter = SourceFileNames.find(File); + if (NameIter == SourceFileNames.end()) + return make_error(raw_error_code::no_entry, + "The specified source file was not found"); + return NameIter->getValue(); +} + uint32_t DbiStreamBuilder::calculateModiSubstreamSize() const { uint32_t Size = 0; for (const auto &M : ModiList) Index: llvm/lib/DebugInfo/PDB/Native/StringTableBuilder.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/StringTableBuilder.cpp +++ llvm/lib/DebugInfo/PDB/Native/StringTableBuilder.cpp @@ -29,6 +29,12 @@ return P.first->second; } +uint32_t StringTableBuilder::getStringIndex(StringRef S) { + auto Iter = Strings.find(S); + assert(Iter != Strings.end()); + return Iter->second; +} + static uint32_t computeBucketCount(uint32_t NumStrings) { // The /names stream is basically an on-disk open-addressing hash table. // Hash collisions are resolved by linear probing. We cannot make Index: llvm/test/DebugInfo/PDB/Inputs/simple-line-info.yaml =================================================================== --- /dev/null +++ llvm/test/DebugInfo/PDB/Inputs/simple-line-info.yaml @@ -0,0 +1,38 @@ +--- +StringTable: + - 'junk_a' + - 'd:\src\llvm\test\debuginfo\pdb\inputs\empty.cpp' + - 'junk_b' +DbiStream: + Modules: + - Module: 'd:\src\llvm\test\DebugInfo\PDB\Inputs\empty.obj' + ObjFile: 'd:\src\llvm\test\DebugInfo\PDB\Inputs\empty.obj' + SourceFiles: + - 'd:\src\llvm\test\debuginfo\pdb\inputs\empty.cpp' + LineInfo: + Checksums: + - FileName: 'd:\src\llvm\test\debuginfo\pdb\inputs\empty.cpp' + Kind: MD5 + Checksum: A0A5BD0D3ECD93FC29D19DE826FBF4BC + Lines: + - CodeSize: 10 + Flags: [ ] + RelocOffset: 16 + RelocSegment: 1 + Blocks: + - FileName: 'd:\src\llvm\test\debuginfo\pdb\inputs\empty.cpp' + Lines: + - Offset: 0 + LineStart: 5 + IsStatement: true + EndDelta: 0 + - Offset: 3 + LineStart: 6 + IsStatement: true + EndDelta: 0 + - Offset: 8 + LineStart: 7 + IsStatement: true + EndDelta: 0 + Columns: +... Index: llvm/test/DebugInfo/PDB/pdbdump-headers.test =================================================================== --- llvm/test/DebugInfo/PDB/pdbdump-headers.test +++ llvm/test/DebugInfo/PDB/pdbdump-headers.test @@ -495,11 +495,11 @@ ; EMPTY-NEXT: } ; EMPTY-NEXT: } ; EMPTY-NEXT: Lines { -; EMPTY-NEXT: LineFragment { +; EMPTY-NEXT: Block { ; EMPTY-NEXT: RelocSegment: 1 ; EMPTY-NEXT: RelocOffset: 16 ; EMPTY-NEXT: CodeSize: 10 -; EMPTY-NEXT: HasColumns: 0 +; EMPTY-NEXT: HasColumns: No ; EMPTY-NEXT: Lines { ; EMPTY-NEXT: FileName: d:\src\llvm\test\debuginfo\pdb\inputs\empty.cpp ; EMPTY-NEXT: Line { Index: llvm/test/DebugInfo/PDB/pdbdump-yaml-lineinfo-write.test =================================================================== --- /dev/null +++ llvm/test/DebugInfo/PDB/pdbdump-yaml-lineinfo-write.test @@ -0,0 +1,52 @@ +; This testcase verifies that we can produce a PDB with line +; information. It does this by describing some line information +; manually in YAML, creating a PDB out of it, then dumping then +; line information from the resulting PDB. + +; RUN: llvm-pdbdump yaml2pdb -pdb=%t.pdb %p/Inputs/simple-line-info.yaml +; RUN: llvm-pdbdump raw -line-info %t.pdb | FileCheck -check-prefix=LINES %s + +LINES: Modules [ +LINES-NEXT: { +LINES-NEXT: Name: d:\src\llvm\test\DebugInfo\PDB\Inputs\empty.obj +LINES: LineInfo [ +LINES-NEXT: FileChecksums { +LINES-NEXT: Checksum { +LINES-NEXT: FileName: d:\src\llvm\test\debuginfo\pdb\inputs\empty.cpp +LINES-NEXT: Kind: MD5 (0x1) +LINES-NEXT: Checksum ( +LINES-NEXT: 0000: A0A5BD0D 3ECD93FC 29D19DE8 26FBF4BC |....>...)...&...| +LINES-NEXT: ) +LINES-NEXT: } +LINES-NEXT: } +LINES-NEXT: Lines { +LINES-NEXT: Block { +LINES-NEXT: RelocSegment: 1 +LINES-NEXT: RelocOffset: 16 +LINES-NEXT: CodeSize: 10 +LINES-NEXT: HasColumns: No +LINES-NEXT: Lines { +LINES-NEXT: FileName: d:\src\llvm\test\debuginfo\pdb\inputs\empty.cpp +LINES-NEXT: Line { +LINES-NEXT: Offset: 0 +LINES-NEXT: LineNumberStart: 5 +LINES-NEXT: EndDelta: 0 +LINES-NEXT: IsStatement: Yes +LINES-NEXT: } +LINES-NEXT: Line { +LINES-NEXT: Offset: 3 +LINES-NEXT: LineNumberStart: 6 +LINES-NEXT: EndDelta: 0 +LINES-NEXT: IsStatement: Yes +LINES-NEXT: } +LINES-NEXT: Line { +LINES-NEXT: Offset: 8 +LINES-NEXT: LineNumberStart: 7 +LINES-NEXT: EndDelta: 0 +LINES-NEXT: IsStatement: Yes +LINES-NEXT: } +LINES-NEXT: } +LINES-NEXT: } +LINES-NEXT: } +LINES-NEXT: ] +LINES-NEXT: } Index: llvm/tools/llvm-pdbdump/LLVMOutputStyle.cpp =================================================================== --- llvm/tools/llvm-pdbdump/LLVMOutputStyle.cpp +++ llvm/tools/llvm-pdbdump/LLVMOutputStyle.cpp @@ -89,11 +89,11 @@ DictScope DD(P, "Lines"); for (const auto &Fragment : Lines) { - DictScope DDD(P, "LineFragment"); + DictScope DDD(P, "Block"); P.printNumber("RelocSegment", Fragment.header()->RelocSegment); P.printNumber("RelocOffset", Fragment.header()->RelocOffset); P.printNumber("CodeSize", Fragment.header()->CodeSize); - P.printNumber("HasColumns", Fragment.hasColumnInfo()); + P.printBoolean("HasColumns", Fragment.hasColumnInfo()); for (const auto &L : Fragment) { DictScope DDDD(P, "Lines"); @@ -557,6 +557,7 @@ bool IsSilentDatabaseBuild = !DumpRecordBytes && !DumpRecords && !DumpTpiHash; if (IsSilentDatabaseBuild) { + outs().flush(); errs() << "Building Type Information For " << Label << "\n"; } Index: llvm/tools/llvm-pdbdump/YAMLOutputStyle.cpp =================================================================== --- llvm/tools/llvm-pdbdump/YAMLOutputStyle.cpp +++ llvm/tools/llvm-pdbdump/YAMLOutputStyle.cpp @@ -286,8 +286,7 @@ continue; auto ModStreamData = msf::MappedBlockStream::createIndexedStream( - File.getMsfLayout(), File.getMsfBuffer(), - MI.Info.getModuleStreamIndex()); + File.getMsfLayout(), File.getMsfBuffer(), ModiStream); pdb::ModuleDebugStreamRef ModS(MI.Info, std::move(ModStreamData)); if (auto EC = ModS.reload()) Index: llvm/tools/llvm-pdbdump/llvm-pdbdump.cpp =================================================================== --- llvm/tools/llvm-pdbdump/llvm-pdbdump.cpp +++ llvm/tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -28,6 +28,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Config/config.h" #include "llvm/DebugInfo/MSF/MSFBuilder.h" @@ -491,6 +492,67 @@ for (auto Symbol : ModiStream.Symbols) ModiBuilder.addSymbol(Symbol.Record); } + if (MI.FileLineInfo.hasValue()) { + const auto &FLI = *MI.FileLineInfo; + + // File Checksums must be emitted before line information, because line + // info records use offsets into the checksum buffer to reference a file's + // source file name. + auto Checksums = llvm::make_unique(); + auto &ChecksumRef = *Checksums; + if (!FLI.FileChecksums.empty()) { + auto &Strings = Builder.getStringTableBuilder(); + for (auto &FC : FLI.FileChecksums) { + uint32_t STOffset = Strings.getStringIndex(FC.FileName); + Checksums->addChecksum(STOffset, FC.Kind, FC.ChecksumBytes.Bytes); + } + } + ModiBuilder.setC13FileChecksums(std::move(Checksums)); + + for (const auto &Fragment : FLI.LineFragments) { + auto Lines = llvm::make_unique(); + Lines->setCodeSize(Fragment.CodeSize); + Lines->setRelocationAddress(Fragment.RelocSegment, + Fragment.RelocOffset); + Lines->setFlags(Fragment.Flags); + for (const auto &LC : Fragment.Blocks) { + // FIXME: StringTable / StringTableBuilder should really be in + // DebugInfoCodeView. This would allow us to construct the + // ModuleDebugLineFragment with a reference to the string table, + // and we could just pass strings around rather than having to + // remember how to calculate the right offset. + auto &Strings = Builder.getStringTableBuilder(); + // The offset in the line info record is the offset of the checksum + // entry for the corresponding file. That entry then contains an + // offset into the global string table of the file name. So to + // compute the proper offset to write into the line info record, we + // must first get its offset in the global string table, then ask the + // checksum builder to find the offset in its serialized buffer that + // it mapped that filename string table offset to. + uint32_t StringOffset = Strings.getStringIndex(LC.FileName); + uint32_t ChecksumOffset = ChecksumRef.mapChecksumOffset(StringOffset); + + Lines->createBlock(ChecksumOffset); + if (Lines->hasColumnInfo()) { + for (const auto &Item : zip(LC.Lines, LC.Columns)) { + auto &L = std::get<0>(Item); + auto &C = std::get<1>(Item); + uint32_t LE = L.LineStart + L.EndDelta; + Lines->addLineAndColumnInfo( + L.Offset, LineInfo(L.LineStart, LE, L.IsStatement), + C.StartColumn, C.EndColumn); + } + } else { + for (const auto &L : LC.Lines) { + uint32_t LE = L.LineStart + L.EndDelta; + Lines->addLineInfo(L.Offset, + LineInfo(L.LineStart, LE, L.IsStatement)); + } + } + } + ModiBuilder.addC13LineFragment(std::move(Lines)); + } + } } auto &TpiBuilder = Builder.getTpiBuilder();