llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp
Zachary Turner bbf9606175 [pdb] Write the module info and symbol record streams.
Previously we did not have support for writing detailed
module information for each module, as well as the symbol
records.  This patch adds support for this, and in doing
so enables the ability to construct minimal PDBs from
just a few lines of YAML.  A test is added to illustrate
this functionality.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@297900 91177308-0d34-0410-b5e6-96231b3b80d8
2017-03-15 22:18:53 +00:00

394 lines
13 KiB
C++

//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
#include "llvm/DebugInfo/PDB/Native/ModInfoBuilder.h"
#include "llvm/DebugInfo/PDB/Native/RawError.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/BinaryStreamWriter.h"
#include "llvm/Support/COFF.h"
using namespace llvm;
using namespace llvm::codeview;
using namespace llvm::msf;
using namespace llvm::pdb;
DbiStreamBuilder::DbiStreamBuilder(msf::MSFBuilder &Msf)
: Msf(Msf), Allocator(Msf.getAllocator()), Age(1), BuildNumber(0),
PdbDllVersion(0), PdbDllRbld(0), Flags(0), MachineType(PDB_Machine::x86),
Header(nullptr), DbgStreams((int)DbgHeaderType::Max) {}
DbiStreamBuilder::~DbiStreamBuilder() {}
void DbiStreamBuilder::setVersionHeader(PdbRaw_DbiVer V) { VerHeader = V; }
void DbiStreamBuilder::setAge(uint32_t A) { Age = A; }
void DbiStreamBuilder::setBuildNumber(uint16_t B) { BuildNumber = B; }
void DbiStreamBuilder::setPdbDllVersion(uint16_t V) { PdbDllVersion = V; }
void DbiStreamBuilder::setPdbDllRbld(uint16_t R) { PdbDllRbld = R; }
void DbiStreamBuilder::setFlags(uint16_t F) { Flags = F; }
void DbiStreamBuilder::setMachineType(PDB_Machine M) { MachineType = M; }
void DbiStreamBuilder::setSectionContribs(ArrayRef<SectionContrib> Arr) {
SectionContribs = Arr;
}
void DbiStreamBuilder::setSectionMap(ArrayRef<SecMapEntry> SecMap) {
SectionMap = SecMap;
}
Error DbiStreamBuilder::addDbgStream(pdb::DbgHeaderType Type,
ArrayRef<uint8_t> Data) {
if (DbgStreams[(int)Type].StreamNumber)
return make_error<RawError>(raw_error_code::duplicate_entry,
"The specified stream type already exists");
auto ExpectedIndex = Msf.addStream(Data.size());
if (!ExpectedIndex)
return ExpectedIndex.takeError();
uint32_t Index = std::move(*ExpectedIndex);
DbgStreams[(int)Type].Data = Data;
DbgStreams[(int)Type].StreamNumber = Index;
return Error::success();
}
uint32_t DbiStreamBuilder::calculateSerializedLength() const {
// For now we only support serializing the header.
return sizeof(DbiStreamHeader) + calculateFileInfoSubstreamSize() +
calculateModiSubstreamSize() + calculateSectionContribsStreamSize() +
calculateSectionMapStreamSize() + calculateDbgStreamsSize();
}
Expected<ModInfoBuilder &>
DbiStreamBuilder::addModuleInfo(StringRef ModuleName) {
uint32_t Index = ModiList.size();
auto MIB = llvm::make_unique<ModInfoBuilder>(ModuleName, Index, Msf);
auto M = MIB.get();
auto Result = ModiMap.insert(std::make_pair(ModuleName, std::move(MIB)));
if (!Result.second)
return make_error<RawError>(raw_error_code::duplicate_entry,
"The specified module already exists");
ModiList.push_back(M);
return *M;
}
Error DbiStreamBuilder::addModuleSourceFile(StringRef Module, StringRef File) {
auto ModIter = ModiMap.find(Module);
if (ModIter == ModiMap.end())
return make_error<RawError>(raw_error_code::no_entry,
"The specified module was not found");
uint32_t Index = SourceFileNames.size();
SourceFileNames.insert(std::make_pair(File, Index));
auto &ModEntry = *ModIter;
ModEntry.second->addSourceFile(File);
return Error::success();
}
uint32_t DbiStreamBuilder::calculateModiSubstreamSize() const {
uint32_t Size = 0;
for (const auto &M : ModiList)
Size += M->calculateSerializedLength();
return Size;
}
uint32_t DbiStreamBuilder::calculateSectionContribsStreamSize() const {
if (SectionContribs.empty())
return 0;
return sizeof(enum PdbRaw_DbiSecContribVer) +
sizeof(SectionContribs[0]) * SectionContribs.size();
}
uint32_t DbiStreamBuilder::calculateSectionMapStreamSize() const {
if (SectionMap.empty())
return 0;
return sizeof(SecMapHeader) + sizeof(SecMapEntry) * SectionMap.size();
}
uint32_t DbiStreamBuilder::calculateFileInfoSubstreamSize() const {
uint32_t Size = 0;
Size += sizeof(ulittle16_t); // NumModules
Size += sizeof(ulittle16_t); // NumSourceFiles
Size += ModiList.size() * sizeof(ulittle16_t); // ModIndices
Size += ModiList.size() * sizeof(ulittle16_t); // ModFileCounts
uint32_t NumFileInfos = 0;
for (const auto &M : ModiList)
NumFileInfos += M->source_files().size();
Size += NumFileInfos * sizeof(ulittle32_t); // FileNameOffsets
Size += calculateNamesBufferSize();
return alignTo(Size, sizeof(uint32_t));
}
uint32_t DbiStreamBuilder::calculateNamesBufferSize() const {
uint32_t Size = 0;
for (const auto &F : SourceFileNames) {
Size += F.getKeyLength() + 1; // Names[I];
}
return Size;
}
uint32_t DbiStreamBuilder::calculateDbgStreamsSize() const {
return DbgStreams.size() * sizeof(uint16_t);
}
Error DbiStreamBuilder::generateFileInfoSubstream() {
uint32_t Size = calculateFileInfoSubstreamSize();
uint32_t NameSize = calculateNamesBufferSize();
auto Data = Allocator.Allocate<uint8_t>(Size);
uint32_t NamesOffset = Size - NameSize;
FileInfoBuffer = MutableBinaryByteStream(MutableArrayRef<uint8_t>(Data, Size),
llvm::support::little);
WritableBinaryStreamRef MetadataBuffer =
WritableBinaryStreamRef(FileInfoBuffer).keep_front(NamesOffset);
BinaryStreamWriter MetadataWriter(MetadataBuffer);
uint16_t ModiCount = std::min<uint32_t>(UINT16_MAX, ModiList.size());
uint16_t FileCount = std::min<uint32_t>(UINT16_MAX, SourceFileNames.size());
if (auto EC = MetadataWriter.writeInteger(ModiCount)) // NumModules
return EC;
if (auto EC = MetadataWriter.writeInteger(FileCount)) // NumSourceFiles
return EC;
for (uint16_t I = 0; I < ModiCount; ++I) {
if (auto EC = MetadataWriter.writeInteger(I)) // Mod Indices
return EC;
}
for (const auto &MI : ModiList) {
FileCount = static_cast<uint16_t>(MI->source_files().size());
if (auto EC = MetadataWriter.writeInteger(FileCount)) // Mod File Counts
return EC;
}
// Before writing the FileNameOffsets array, write the NamesBuffer array.
// A side effect of this is that this will actually compute the various
// file name offsets, so we can then go back and write the FileNameOffsets
// array to the other substream.
NamesBuffer = WritableBinaryStreamRef(FileInfoBuffer).drop_front(NamesOffset);
BinaryStreamWriter NameBufferWriter(NamesBuffer);
for (auto &Name : SourceFileNames) {
Name.second = NameBufferWriter.getOffset();
if (auto EC = NameBufferWriter.writeCString(Name.getKey()))
return EC;
}
for (const auto &MI : ModiList) {
for (StringRef Name : MI->source_files()) {
auto Result = SourceFileNames.find(Name);
if (Result == SourceFileNames.end())
return make_error<RawError>(raw_error_code::no_entry,
"The source file was not found.");
if (auto EC = MetadataWriter.writeInteger(Result->second))
return EC;
}
}
if (NameBufferWriter.bytesRemaining() > 0)
return make_error<RawError>(raw_error_code::invalid_format,
"The names buffer contained unexpected data.");
if (MetadataWriter.bytesRemaining() > sizeof(uint32_t))
return make_error<RawError>(
raw_error_code::invalid_format,
"The metadata buffer contained unexpected data.");
return Error::success();
}
Error DbiStreamBuilder::finalize() {
if (Header)
return Error::success();
for (auto &MI : ModiList)
MI->finalize();
if (auto EC = generateFileInfoSubstream())
return EC;
DbiStreamHeader *H = Allocator.Allocate<DbiStreamHeader>();
H->VersionHeader = *VerHeader;
H->VersionSignature = -1;
H->Age = Age;
H->BuildNumber = BuildNumber;
H->Flags = Flags;
H->PdbDllRbld = PdbDllRbld;
H->PdbDllVersion = PdbDllVersion;
H->MachineType = static_cast<uint16_t>(MachineType);
H->ECSubstreamSize = 0;
H->FileInfoSize = FileInfoBuffer.getLength();
H->ModiSubstreamSize = calculateModiSubstreamSize();
H->OptionalDbgHdrSize = DbgStreams.size() * sizeof(uint16_t);
H->SecContrSubstreamSize = calculateSectionContribsStreamSize();
H->SectionMapSize = calculateSectionMapStreamSize();
H->TypeServerSize = 0;
H->SymRecordStreamIndex = kInvalidStreamIndex;
H->PublicSymbolStreamIndex = kInvalidStreamIndex;
H->MFCTypeServerIndex = kInvalidStreamIndex;
H->GlobalSymbolStreamIndex = kInvalidStreamIndex;
Header = H;
return Error::success();
}
Error DbiStreamBuilder::finalizeMsfLayout() {
for (auto &MI : ModiList) {
if (auto EC = MI->finalizeMsfLayout())
return EC;
}
uint32_t Length = calculateSerializedLength();
if (auto EC = Msf.setStreamSize(StreamDBI, Length))
return EC;
return Error::success();
}
static uint16_t toSecMapFlags(uint32_t Flags) {
uint16_t Ret = 0;
if (Flags & COFF::IMAGE_SCN_MEM_READ)
Ret |= static_cast<uint16_t>(OMFSegDescFlags::Read);
if (Flags & COFF::IMAGE_SCN_MEM_WRITE)
Ret |= static_cast<uint16_t>(OMFSegDescFlags::Write);
if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE)
Ret |= static_cast<uint16_t>(OMFSegDescFlags::Execute);
if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE)
Ret |= static_cast<uint16_t>(OMFSegDescFlags::Execute);
if (!(Flags & COFF::IMAGE_SCN_MEM_16BIT))
Ret |= static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit);
// This seems always 1.
Ret |= static_cast<uint16_t>(OMFSegDescFlags::IsSelector);
return Ret;
}
// A utility function to create Section Contributions
// for a given input sections.
std::vector<SectionContrib> DbiStreamBuilder::createSectionContribs(
ArrayRef<object::coff_section> SecHdrs) {
std::vector<SectionContrib> Ret;
// Create a SectionContrib for each input section.
for (auto &Sec : SecHdrs) {
Ret.emplace_back();
auto &Entry = Ret.back();
memset(&Entry, 0, sizeof(Entry));
Entry.Off = Sec.PointerToRawData;
Entry.Size = Sec.SizeOfRawData;
Entry.Characteristics = Sec.Characteristics;
}
return Ret;
}
// A utility function to create a Section Map for a given list of COFF sections.
//
// A Section Map seem to be a copy of a COFF section list in other format.
// I don't know why a PDB file contains both a COFF section header and
// a Section Map, but it seems it must be present in a PDB.
std::vector<SecMapEntry> DbiStreamBuilder::createSectionMap(
ArrayRef<llvm::object::coff_section> SecHdrs) {
std::vector<SecMapEntry> Ret;
int Idx = 0;
auto Add = [&]() -> SecMapEntry & {
Ret.emplace_back();
auto &Entry = Ret.back();
memset(&Entry, 0, sizeof(Entry));
Entry.Frame = Idx + 1;
// We don't know the meaning of these fields yet.
Entry.SecName = UINT16_MAX;
Entry.ClassName = UINT16_MAX;
return Entry;
};
for (auto &Hdr : SecHdrs) {
auto &Entry = Add();
Entry.Flags = toSecMapFlags(Hdr.Characteristics);
Entry.SecByteLength = Hdr.VirtualSize;
++Idx;
}
// The last entry is for absolute symbols.
auto &Entry = Add();
Entry.Flags = static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit) |
static_cast<uint16_t>(OMFSegDescFlags::IsAbsoluteAddress);
Entry.SecByteLength = UINT32_MAX;
return Ret;
}
Error DbiStreamBuilder::commit(const msf::MSFLayout &Layout,
WritableBinaryStreamRef MsfBuffer) {
if (auto EC = finalize())
return EC;
auto DbiS = WritableMappedBlockStream::createIndexedStream(Layout, MsfBuffer,
StreamDBI);
BinaryStreamWriter Writer(*DbiS);
if (auto EC = Writer.writeObject(*Header))
return EC;
for (auto &M : ModiList) {
if (auto EC = M->commit(Writer, Layout, MsfBuffer))
return EC;
}
if (!SectionContribs.empty()) {
if (auto EC = Writer.writeEnum(DbiSecContribVer60))
return EC;
if (auto EC = Writer.writeArray(SectionContribs))
return EC;
}
if (!SectionMap.empty()) {
ulittle16_t Size = static_cast<ulittle16_t>(SectionMap.size());
SecMapHeader SMHeader = {Size, Size};
if (auto EC = Writer.writeObject(SMHeader))
return EC;
if (auto EC = Writer.writeArray(SectionMap))
return EC;
}
if (auto EC = Writer.writeStreamRef(FileInfoBuffer))
return EC;
for (auto &Stream : DbgStreams)
if (auto EC = Writer.writeInteger(Stream.StreamNumber))
return EC;
for (auto &Stream : DbgStreams) {
if (Stream.StreamNumber == kInvalidStreamIndex)
continue;
auto WritableStream = WritableMappedBlockStream::createIndexedStream(
Layout, MsfBuffer, Stream.StreamNumber);
BinaryStreamWriter DbgStreamWriter(*WritableStream);
if (auto EC = DbgStreamWriter.writeArray(Stream.Data))
return EC;
}
if (Writer.bytesRemaining() > 0)
return make_error<RawError>(raw_error_code::invalid_format,
"Unexpected bytes found in DBI Stream");
return Error::success();
}