1) Don't write a /src/headerblock stream. This appears to be written conditionally by MSVC, but it's not clear what the condition is. For now, just remove it since we dont' know what it is anyway and the particular pdb we've checked in for the test doesn't have one. 2) Write a valid timestamp for the PDB file signature. This leads to non-reproducible builds, but it matches the default behavior of link, so it should be out default as well. If we need reproducibility, we should add a separate command line option for it that is off by default. 3) Write an empty FPO stream. MSVC seems to always write an FPO stream. This change makes the stream directory match up, although we still need to make the contents of the FPO stream match. llvm-svn: 307436
197 lines
5.7 KiB
C++
197 lines
5.7 KiB
C++
//===- PDBFileBuilder.cpp - PDB File 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/PDBFileBuilder.h"
|
|
|
|
#include "llvm/ADT/BitVector.h"
|
|
|
|
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
|
|
#include "llvm/DebugInfo/PDB/GenericError.h"
|
|
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
|
|
#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
|
|
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
|
|
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
|
|
#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"
|
|
#include "llvm/DebugInfo/PDB/Native/RawError.h"
|
|
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
|
#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
|
|
#include "llvm/Support/BinaryStream.h"
|
|
#include "llvm/Support/BinaryStreamWriter.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::codeview;
|
|
using namespace llvm::msf;
|
|
using namespace llvm::pdb;
|
|
using namespace llvm::support;
|
|
|
|
PDBFileBuilder::PDBFileBuilder(BumpPtrAllocator &Allocator)
|
|
: Allocator(Allocator) {}
|
|
|
|
Error PDBFileBuilder::initialize(uint32_t BlockSize) {
|
|
auto ExpectedMsf = MSFBuilder::create(Allocator, BlockSize);
|
|
if (!ExpectedMsf)
|
|
return ExpectedMsf.takeError();
|
|
Msf = llvm::make_unique<MSFBuilder>(std::move(*ExpectedMsf));
|
|
return Error::success();
|
|
}
|
|
|
|
MSFBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; }
|
|
|
|
InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() {
|
|
if (!Info)
|
|
Info = llvm::make_unique<InfoStreamBuilder>(*Msf, NamedStreams);
|
|
return *Info;
|
|
}
|
|
|
|
DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() {
|
|
if (!Dbi)
|
|
Dbi = llvm::make_unique<DbiStreamBuilder>(*Msf);
|
|
return *Dbi;
|
|
}
|
|
|
|
TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() {
|
|
if (!Tpi)
|
|
Tpi = llvm::make_unique<TpiStreamBuilder>(*Msf, StreamTPI);
|
|
return *Tpi;
|
|
}
|
|
|
|
TpiStreamBuilder &PDBFileBuilder::getIpiBuilder() {
|
|
if (!Ipi)
|
|
Ipi = llvm::make_unique<TpiStreamBuilder>(*Msf, StreamIPI);
|
|
return *Ipi;
|
|
}
|
|
|
|
PDBStringTableBuilder &PDBFileBuilder::getStringTableBuilder() {
|
|
return Strings;
|
|
}
|
|
|
|
Error PDBFileBuilder::addNamedStream(StringRef Name, uint32_t Size) {
|
|
auto ExpectedStream = Msf->addStream(Size);
|
|
if (!ExpectedStream)
|
|
return ExpectedStream.takeError();
|
|
NamedStreams.set(Name, *ExpectedStream);
|
|
return Error::success();
|
|
}
|
|
|
|
Expected<msf::MSFLayout> PDBFileBuilder::finalizeMsfLayout() {
|
|
|
|
if (Ipi && Ipi->getRecordCount() > 0) {
|
|
// In theory newer PDBs always have an ID stream, but by saying that we're
|
|
// only going to *really* have an ID stream if there is at least one ID
|
|
// record, we leave open the opportunity to test older PDBs such as those
|
|
// that don't have an ID stream.
|
|
auto &Info = getInfoBuilder();
|
|
Info.addFeature(PdbRaw_FeatureSig::VC140);
|
|
}
|
|
|
|
uint32_t StringsLen = Strings.calculateSerializedSize();
|
|
|
|
if (auto EC = addNamedStream("/names", StringsLen))
|
|
return std::move(EC);
|
|
if (auto EC = addNamedStream("/LinkInfo", 0))
|
|
return std::move(EC);
|
|
|
|
if (Info) {
|
|
if (auto EC = Info->finalizeMsfLayout())
|
|
return std::move(EC);
|
|
}
|
|
if (Dbi) {
|
|
if (auto EC = Dbi->finalizeMsfLayout())
|
|
return std::move(EC);
|
|
}
|
|
if (Tpi) {
|
|
if (auto EC = Tpi->finalizeMsfLayout())
|
|
return std::move(EC);
|
|
}
|
|
if (Ipi) {
|
|
if (auto EC = Ipi->finalizeMsfLayout())
|
|
return std::move(EC);
|
|
}
|
|
|
|
return Msf->build();
|
|
}
|
|
|
|
Expected<uint32_t> PDBFileBuilder::getNamedStreamIndex(StringRef Name) const {
|
|
uint32_t SN = 0;
|
|
if (!NamedStreams.get(Name, SN))
|
|
return llvm::make_error<pdb::RawError>(raw_error_code::no_stream);
|
|
return SN;
|
|
}
|
|
|
|
Error PDBFileBuilder::commit(StringRef Filename) {
|
|
assert(!Filename.empty());
|
|
auto ExpectedLayout = finalizeMsfLayout();
|
|
if (!ExpectedLayout)
|
|
return ExpectedLayout.takeError();
|
|
auto &Layout = *ExpectedLayout;
|
|
|
|
uint64_t Filesize = Layout.SB->BlockSize * Layout.SB->NumBlocks;
|
|
auto OutFileOrError = FileOutputBuffer::create(Filename, Filesize);
|
|
if (OutFileOrError.getError())
|
|
return llvm::make_error<pdb::GenericError>(generic_error_code::invalid_path,
|
|
Filename);
|
|
FileBufferByteStream Buffer(std::move(*OutFileOrError),
|
|
llvm::support::little);
|
|
BinaryStreamWriter Writer(Buffer);
|
|
|
|
if (auto EC = Writer.writeObject(*Layout.SB))
|
|
return EC;
|
|
uint32_t BlockMapOffset =
|
|
msf::blockToOffset(Layout.SB->BlockMapAddr, Layout.SB->BlockSize);
|
|
Writer.setOffset(BlockMapOffset);
|
|
if (auto EC = Writer.writeArray(Layout.DirectoryBlocks))
|
|
return EC;
|
|
|
|
auto DirStream = WritableMappedBlockStream::createDirectoryStream(
|
|
Layout, Buffer, Allocator);
|
|
BinaryStreamWriter DW(*DirStream);
|
|
if (auto EC = DW.writeInteger<uint32_t>(Layout.StreamSizes.size()))
|
|
return EC;
|
|
|
|
if (auto EC = DW.writeArray(Layout.StreamSizes))
|
|
return EC;
|
|
|
|
for (const auto &Blocks : Layout.StreamMap) {
|
|
if (auto EC = DW.writeArray(Blocks))
|
|
return EC;
|
|
}
|
|
|
|
auto ExpectedSN = getNamedStreamIndex("/names");
|
|
if (!ExpectedSN)
|
|
return ExpectedSN.takeError();
|
|
|
|
auto NS = WritableMappedBlockStream::createIndexedStream(
|
|
Layout, Buffer, *ExpectedSN, Allocator);
|
|
BinaryStreamWriter NSWriter(*NS);
|
|
if (auto EC = Strings.commit(NSWriter))
|
|
return EC;
|
|
|
|
if (Info) {
|
|
if (auto EC = Info->commit(Layout, Buffer))
|
|
return EC;
|
|
}
|
|
|
|
if (Dbi) {
|
|
if (auto EC = Dbi->commit(Layout, Buffer))
|
|
return EC;
|
|
}
|
|
|
|
if (Tpi) {
|
|
if (auto EC = Tpi->commit(Layout, Buffer))
|
|
return EC;
|
|
}
|
|
|
|
if (Ipi) {
|
|
if (auto EC = Ipi->commit(Layout, Buffer))
|
|
return EC;
|
|
}
|
|
|
|
return Buffer.commit();
|
|
}
|