llvm-project/llvm/lib/DebugInfo/PDB/Native/PublicsStreamBuilder.cpp

221 lines
7.3 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/PublicsStreamBuilder.h"
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/MSF/MSFCommon.h"
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
#include "llvm/DebugInfo/PDB/Native/Hash.h"
#include "llvm/Support/BinaryItemStream.h"
#include "llvm/Support/BinaryStreamWriter.h"
#include <algorithm>
#include <vector>
using namespace llvm;
using namespace llvm::msf;
using namespace llvm::pdb;
using namespace llvm::codeview;
PublicsStreamBuilder::PublicsStreamBuilder(msf::MSFBuilder &Msf)
: Table(new GSIHashTableBuilder), Msf(Msf) {}
PublicsStreamBuilder::~PublicsStreamBuilder() {}
uint32_t PublicsStreamBuilder::calculateSerializedLength() const {
uint32_t Size = 0;
Size += sizeof(PublicsStreamHeader);
Size += sizeof(GSIHashHeader);
Size += Table->HashRecords.size() * sizeof(PSHashRecord);
Size += Table->HashBitmap.size() * sizeof(uint32_t);
Size += Table->HashBuckets.size() * sizeof(uint32_t);
Size += Publics.size() * sizeof(uint32_t); // AddrMap
// FIXME: Add thunk map and section offsets for incremental linking.
return Size;
}
Error PublicsStreamBuilder::finalizeMsfLayout() {
Table->addSymbols(Publics);
Expected<uint32_t> Idx = Msf.addStream(calculateSerializedLength());
if (!Idx)
return Idx.takeError();
StreamIdx = *Idx;
uint32_t PublicRecordBytes = 0;
for (auto &Pub : Publics)
PublicRecordBytes += Pub.length();
Expected<uint32_t> RecordIdx = Msf.addStream(PublicRecordBytes);
if (!RecordIdx)
return RecordIdx.takeError();
RecordStreamIdx = *RecordIdx;
return Error::success();
}
void PublicsStreamBuilder::addPublicSymbol(const PublicSym32 &Pub) {
Publics.push_back(SymbolSerializer::writeOneSymbol(
const_cast<PublicSym32 &>(Pub), Msf.getAllocator(),
CodeViewContainer::Pdb));
}
// FIXME: Put this back in the header.
struct PubSymLayout {
ulittle16_t reclen;
ulittle16_t reckind;
ulittle32_t flags;
ulittle32_t off;
ulittle16_t seg;
char name[1];
};
bool comparePubSymByAddrAndName(const CVSymbol *LS, const CVSymbol *RS) {
assert(LS->length() > sizeof(PubSymLayout) &&
RS->length() > sizeof(PubSymLayout));
auto *L = reinterpret_cast<const PubSymLayout *>(LS->data().data());
auto *R = reinterpret_cast<const PubSymLayout *>(RS->data().data());
if (L->seg < R->seg)
return true;
if (L->seg > R->seg)
return false;
if (L->off < R->off)
return true;
if (L->off > R->off)
return false;
return strcmp(L->name, R->name) < 0;
}
static StringRef getSymbolName(const CVSymbol &Sym) {
assert(Sym.kind() == S_PUB32 && "handle other kinds");
ArrayRef<uint8_t> NameBytes =
Sym.data().drop_front(offsetof(PubSymLayout, name));
return StringRef(reinterpret_cast<const char *>(NameBytes.data()),
NameBytes.size())
.trim('\0');
}
/// Compute the address map. The address map is an array of symbol offsets
/// sorted so that it can be binary searched by address.
static std::vector<ulittle32_t> computeAddrMap(ArrayRef<CVSymbol> Publics) {
// Make a vector of pointers to the symbols so we can sort it by address.
// Also gather the symbol offsets while we're at it.
std::vector<const CVSymbol *> PublicsByAddr;
std::vector<uint32_t> SymOffsets;
PublicsByAddr.reserve(Publics.size());
uint32_t SymOffset = 0;
for (const CVSymbol &Sym : Publics) {
PublicsByAddr.push_back(&Sym);
SymOffsets.push_back(SymOffset);
SymOffset += Sym.length();
}
std::stable_sort(PublicsByAddr.begin(), PublicsByAddr.end(),
comparePubSymByAddrAndName);
// Fill in the symbol offsets in the appropriate order.
std::vector<ulittle32_t> AddrMap;
AddrMap.reserve(Publics.size());
for (const CVSymbol *Sym : PublicsByAddr) {
ptrdiff_t Idx = std::distance(Publics.data(), Sym);
assert(Idx >= 0 && size_t(Idx) < Publics.size());
AddrMap.push_back(ulittle32_t(SymOffsets[Idx]));
}
return AddrMap;
}
Error PublicsStreamBuilder::commit(BinaryStreamWriter &PublicsWriter,
BinaryStreamWriter &RecWriter) {
assert(Table->HashRecords.size() == Publics.size());
PublicsStreamHeader PSH;
GSIHashHeader GSH;
PSH.AddrMap = Publics.size() * 4;
// FIXME: Fill these in. They are for incremental linking.
PSH.NumThunks = 0;
PSH.SizeOfThunk = 0;
PSH.ISectThunkTable = 0;
PSH.OffThunkTable = 0;
PSH.NumSections = 0;
GSH.VerSignature = GSIHashHeader::HdrSignature;
GSH.VerHdr = GSIHashHeader::HdrVersion;
GSH.HrSize = Table->HashRecords.size() * sizeof(PSHashRecord);
GSH.NumBuckets = Table->HashBitmap.size() * 4 + Table->HashBuckets.size() * 4;
PSH.SymHash = sizeof(GSH) + GSH.HrSize + GSH.NumBuckets;
if (auto EC = PublicsWriter.writeObject(PSH))
return EC;
if (auto EC = PublicsWriter.writeObject(GSH))
return EC;
if (auto EC = PublicsWriter.writeArray(makeArrayRef(Table->HashRecords)))
return EC;
if (auto EC = PublicsWriter.writeArray(makeArrayRef(Table->HashBitmap)))
return EC;
if (auto EC = PublicsWriter.writeArray(makeArrayRef(Table->HashBuckets)))
return EC;
std::vector<ulittle32_t> AddrMap = computeAddrMap(Publics);
if (auto EC = PublicsWriter.writeArray(makeArrayRef(AddrMap)))
return EC;
BinaryItemStream<CVSymbol> Records(support::endianness::little);
Records.setItems(Publics);
BinaryStreamRef RecordsRef(Records);
if (auto EC = RecWriter.writeStreamRef(RecordsRef))
return EC;
return Error::success();
}
void GSIHashTableBuilder::addSymbols(ArrayRef<CVSymbol> Symbols) {
std::array<std::vector<PSHashRecord>, IPHR_HASH + 1> TmpBuckets;
uint32_t SymOffset = 0;
for (const CVSymbol &Sym : Symbols) {
PSHashRecord HR;
// Add one when writing symbol offsets to disk. See GSI1::fixSymRecs.
HR.Off = SymOffset + 1;
HR.CRef = 1; // Always use a refcount of 1.
// Hash the name to figure out which bucket this goes into.
StringRef Name = getSymbolName(Sym);
size_t BucketIdx = hashStringV1(Name) % IPHR_HASH;
TmpBuckets[BucketIdx].push_back(HR); // FIXME: Does order matter?
SymOffset += Sym.length();
}
// Compute the three tables: the hash records in bucket and chain order, the
// bucket presence bitmap, and the bucket chain start offsets.
HashRecords.reserve(Symbols.size());
for (size_t BucketIdx = 0; BucketIdx < IPHR_HASH + 1; ++BucketIdx) {
auto &Bucket = TmpBuckets[BucketIdx];
if (Bucket.empty())
continue;
HashBitmap[BucketIdx / 32] |= 1U << (BucketIdx % 32);
// Calculate what the offset of the first hash record in the chain would be
// if it were inflated to contain 32-bit pointers. On a 32-bit system, each
// record would be 12 bytes. See HROffsetCalc in gsi.h.
const int SizeOfHROffsetCalc = 12;
ulittle32_t ChainStartOff =
ulittle32_t(HashRecords.size() * SizeOfHROffsetCalc);
HashBuckets.push_back(ChainStartOff);
for (const auto &HR : Bucket)
HashRecords.push_back(HR);
}
}