221 lines
7.3 KiB
C++
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);
|
|
}
|
|
}
|