brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.0 KiB · ad3d09a Raw
456 lines · cpp
1//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"10 11#include "llvm/ADT/ArrayRef.h"12#include "llvm/BinaryFormat/COFF.h"13#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"14#include "llvm/DebugInfo/MSF/MSFBuilder.h"15#include "llvm/DebugInfo/MSF/MappedBlockStream.h"16#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"17#include "llvm/DebugInfo/PDB/Native/RawError.h"18#include "llvm/Object/COFF.h"19#include "llvm/Support/BinaryStreamWriter.h"20#include "llvm/Support/Parallel.h"21#include "llvm/Support/TimeProfiler.h"22 23using namespace llvm;24using namespace llvm::codeview;25using namespace llvm::msf;26using namespace llvm::pdb;27 28DbiStreamBuilder::DbiStreamBuilder(msf::MSFBuilder &Msf)29    : Msf(Msf), Allocator(Msf.getAllocator()), Age(1), BuildNumber(0),30      PdbDllVersion(0), PdbDllRbld(0), Flags(0), MachineType(PDB_Machine::x86),31      Header(nullptr) {}32 33DbiStreamBuilder::~DbiStreamBuilder() = default;34 35void DbiStreamBuilder::setVersionHeader(PdbRaw_DbiVer V) { VerHeader = V; }36 37void DbiStreamBuilder::setAge(uint32_t A) { Age = A; }38 39void DbiStreamBuilder::setBuildNumber(uint16_t B) { BuildNumber = B; }40 41void DbiStreamBuilder::setBuildNumber(uint8_t Major, uint8_t Minor) {42  BuildNumber = (uint16_t(Major) << DbiBuildNo::BuildMajorShift) &43                DbiBuildNo::BuildMajorMask;44  BuildNumber |= (uint16_t(Minor) << DbiBuildNo::BuildMinorShift) &45                 DbiBuildNo::BuildMinorMask;46  BuildNumber |= DbiBuildNo::NewVersionFormatMask;47}48 49void DbiStreamBuilder::setPdbDllVersion(uint16_t V) { PdbDllVersion = V; }50 51void DbiStreamBuilder::setPdbDllRbld(uint16_t R) { PdbDllRbld = R; }52 53void DbiStreamBuilder::setFlags(uint16_t F) { Flags = F; }54 55void DbiStreamBuilder::setMachineType(PDB_Machine M) { MachineType = M; }56 57void DbiStreamBuilder::setMachineType(COFF::MachineTypes M) {58  // These enums are mirrors of each other, so we can just cast the value.59  MachineType = static_cast<pdb::PDB_Machine>(static_cast<unsigned>(M));60}61 62void DbiStreamBuilder::setGlobalsStreamIndex(uint32_t Index) {63  GlobalsStreamIndex = Index;64}65 66void DbiStreamBuilder::setSymbolRecordStreamIndex(uint32_t Index) {67  SymRecordStreamIndex = Index;68}69 70void DbiStreamBuilder::setPublicsStreamIndex(uint32_t Index) {71  PublicsStreamIndex = Index;72}73 74void DbiStreamBuilder::addNewFpoData(const codeview::FrameData &FD) {75  if (!NewFpoData)76    NewFpoData.emplace(false);77 78  NewFpoData->addFrameData(FD);79}80 81void DbiStreamBuilder::addOldFpoData(const object::FpoData &FD) {82  OldFpoData.push_back(FD);83}84 85Error DbiStreamBuilder::addDbgStream(pdb::DbgHeaderType Type,86                                     ArrayRef<uint8_t> Data) {87  assert(Type != DbgHeaderType::NewFPO &&88         "NewFPO data should be written via addFrameData()!");89 90  DbgStreams[(int)Type] = DebugStream{};91  DbgStreams[(int)Type]->Size = Data.size();92  DbgStreams[(int)Type]->WriteFn = [Data](BinaryStreamWriter &Writer) {93    return Writer.writeArray(Data);94  };95  return Error::success();96}97 98uint32_t DbiStreamBuilder::addECName(StringRef Name) {99  return ECNamesBuilder.insert(Name);100}101 102uint32_t DbiStreamBuilder::calculateSerializedLength() const {103  // For now we only support serializing the header.104  return sizeof(DbiStreamHeader) + calculateFileInfoSubstreamSize() +105         calculateModiSubstreamSize() + calculateSectionContribsStreamSize() +106         calculateSectionMapStreamSize() + calculateDbgStreamsSize() +107         ECNamesBuilder.calculateSerializedSize();108}109 110Expected<DbiModuleDescriptorBuilder &>111DbiStreamBuilder::addModuleInfo(StringRef ModuleName) {112  uint32_t Index = ModiList.size();113  ModiList.push_back(114      std::make_unique<DbiModuleDescriptorBuilder>(ModuleName, Index, Msf));115  return *ModiList.back();116}117 118Error DbiStreamBuilder::addModuleSourceFile(DbiModuleDescriptorBuilder &Module,119                                            StringRef File) {120  uint32_t Index = SourceFileNames.size();121  SourceFileNames.insert(std::make_pair(File, Index));122  Module.addSourceFile(File);123  return Error::success();124}125 126Expected<uint32_t> DbiStreamBuilder::getSourceFileNameIndex(StringRef File) {127  auto NameIter = SourceFileNames.find(File);128  if (NameIter == SourceFileNames.end())129    return make_error<RawError>(raw_error_code::no_entry,130                                "The specified source file was not found");131  return NameIter->getValue();132}133 134uint32_t DbiStreamBuilder::calculateModiSubstreamSize() const {135  uint32_t Size = 0;136  for (const auto &M : ModiList)137    Size += M->calculateSerializedLength();138  return Size;139}140 141uint32_t DbiStreamBuilder::calculateSectionContribsStreamSize() const {142  if (SectionContribs.empty())143    return 0;144  return sizeof(enum PdbRaw_DbiSecContribVer) +145         sizeof(SectionContribs[0]) * SectionContribs.size();146}147 148uint32_t DbiStreamBuilder::calculateSectionMapStreamSize() const {149  if (SectionMap.empty())150    return 0;151  return sizeof(SecMapHeader) + sizeof(SecMapEntry) * SectionMap.size();152}153 154uint32_t DbiStreamBuilder::calculateNamesOffset() const {155  uint32_t Offset = 0;156  Offset += sizeof(ulittle16_t);                         // NumModules157  Offset += sizeof(ulittle16_t);                         // NumSourceFiles158  Offset += ModiList.size() * sizeof(ulittle16_t);       // ModIndices159  Offset += ModiList.size() * sizeof(ulittle16_t);       // ModFileCounts160  uint32_t NumFileInfos = 0;161  for (const auto &M : ModiList)162    NumFileInfos += M->source_files().size();163  Offset += NumFileInfos * sizeof(ulittle32_t); // FileNameOffsets164  return Offset;165}166 167uint32_t DbiStreamBuilder::calculateFileInfoSubstreamSize() const {168  uint32_t Size = calculateNamesOffset();169  Size += calculateNamesBufferSize();170  return alignTo(Size, sizeof(uint32_t));171}172 173uint32_t DbiStreamBuilder::calculateNamesBufferSize() const {174  uint32_t Size = 0;175  for (const auto &F : SourceFileNames) {176    Size += F.getKeyLength() + 1; // Names[I];177  }178  return Size;179}180 181uint32_t DbiStreamBuilder::calculateDbgStreamsSize() const {182  return DbgStreams.size() * sizeof(uint16_t);183}184 185Error DbiStreamBuilder::generateFileInfoSubstream() {186  uint32_t Size = calculateFileInfoSubstreamSize();187  auto Data = Allocator.Allocate<uint8_t>(Size);188  uint32_t NamesOffset = calculateNamesOffset();189 190  FileInfoBuffer = MutableBinaryByteStream(MutableArrayRef<uint8_t>(Data, Size),191                                           llvm::endianness::little);192 193  WritableBinaryStreamRef MetadataBuffer =194      WritableBinaryStreamRef(FileInfoBuffer).keep_front(NamesOffset);195  BinaryStreamWriter MetadataWriter(MetadataBuffer);196 197  uint16_t ModiCount = std::min<uint32_t>(UINT16_MAX, ModiList.size());198  uint16_t FileCount = std::min<uint32_t>(UINT16_MAX, SourceFileNames.size());199  if (auto EC = MetadataWriter.writeInteger(ModiCount)) // NumModules200    return EC;201  if (auto EC = MetadataWriter.writeInteger(FileCount)) // NumSourceFiles202    return EC;203  for (uint16_t I = 0; I < ModiCount; ++I) {204    if (auto EC = MetadataWriter.writeInteger(I)) // Mod Indices205      return EC;206  }207  for (const auto &MI : ModiList) {208    FileCount = static_cast<uint16_t>(MI->source_files().size());209    if (auto EC = MetadataWriter.writeInteger(FileCount)) // Mod File Counts210      return EC;211  }212 213  // Before writing the FileNameOffsets array, write the NamesBuffer array.214  // A side effect of this is that this will actually compute the various215  // file name offsets, so we can then go back and write the FileNameOffsets216  // array to the other substream.217  NamesBuffer = WritableBinaryStreamRef(FileInfoBuffer).drop_front(NamesOffset);218  BinaryStreamWriter NameBufferWriter(NamesBuffer);219  for (auto &Name : SourceFileNames) {220    Name.second = NameBufferWriter.getOffset();221    if (auto EC = NameBufferWriter.writeCString(Name.getKey()))222      return EC;223  }224 225  for (const auto &MI : ModiList) {226    for (StringRef Name : MI->source_files()) {227      auto Result = SourceFileNames.find(Name);228      if (Result == SourceFileNames.end())229        return make_error<RawError>(raw_error_code::no_entry,230                                    "The source file was not found.");231      if (auto EC = MetadataWriter.writeInteger(Result->second))232        return EC;233    }234  }235 236  if (auto EC = NameBufferWriter.padToAlignment(sizeof(uint32_t)))237    return EC;238 239  if (NameBufferWriter.bytesRemaining() > 0)240    return make_error<RawError>(raw_error_code::invalid_format,241                                "The names buffer contained unexpected data.");242 243  if (MetadataWriter.bytesRemaining() > sizeof(uint32_t))244    return make_error<RawError>(245        raw_error_code::invalid_format,246        "The metadata buffer contained unexpected data.");247 248  return Error::success();249}250 251Error DbiStreamBuilder::finalize() {252  if (Header)253    return Error::success();254 255  for (auto &MI : ModiList)256    MI->finalize();257 258  if (auto EC = generateFileInfoSubstream())259    return EC;260 261  DbiStreamHeader *H = Allocator.Allocate<DbiStreamHeader>();262  ::memset(H, 0, sizeof(DbiStreamHeader));263  H->VersionHeader = *VerHeader;264  H->VersionSignature = -1;265  H->Age = Age;266  H->BuildNumber = BuildNumber;267  H->Flags = Flags;268  H->PdbDllRbld = PdbDllRbld;269  H->PdbDllVersion = PdbDllVersion;270  H->MachineType = static_cast<uint16_t>(MachineType);271 272  H->ECSubstreamSize = ECNamesBuilder.calculateSerializedSize();273  H->FileInfoSize = FileInfoBuffer.getLength();274  H->ModiSubstreamSize = calculateModiSubstreamSize();275  H->OptionalDbgHdrSize = DbgStreams.size() * sizeof(uint16_t);276  H->SecContrSubstreamSize = calculateSectionContribsStreamSize();277  H->SectionMapSize = calculateSectionMapStreamSize();278  H->TypeServerSize = 0;279  H->SymRecordStreamIndex = SymRecordStreamIndex;280  H->PublicSymbolStreamIndex = PublicsStreamIndex;281  H->MFCTypeServerIndex = 0; // Not sure what this is, but link.exe writes 0.282  H->GlobalSymbolStreamIndex = GlobalsStreamIndex;283 284  Header = H;285  return Error::success();286}287 288Error DbiStreamBuilder::finalizeMsfLayout() {289  if (NewFpoData) {290    DbgStreams[(int)DbgHeaderType::NewFPO] = DebugStream{};291    DbgStreams[(int)DbgHeaderType::NewFPO]->Size =292        NewFpoData->calculateSerializedSize();293    DbgStreams[(int)DbgHeaderType::NewFPO]->WriteFn =294        [this](BinaryStreamWriter &Writer) {295          return NewFpoData->commit(Writer);296        };297  }298 299  if (!OldFpoData.empty()) {300    DbgStreams[(int)DbgHeaderType::FPO] = DebugStream{};301    DbgStreams[(int)DbgHeaderType::FPO]->Size =302        sizeof(object::FpoData) * OldFpoData.size();303    DbgStreams[(int)DbgHeaderType::FPO]->WriteFn =304        [this](BinaryStreamWriter &Writer) {305          return Writer.writeArray(ArrayRef(OldFpoData));306        };307  }308 309  for (auto &S : DbgStreams) {310    if (!S)311      continue;312    auto ExpectedIndex = Msf.addStream(S->Size);313    if (!ExpectedIndex)314      return ExpectedIndex.takeError();315    S->StreamNumber = *ExpectedIndex;316  }317 318  for (auto &MI : ModiList) {319    if (auto EC = MI->finalizeMsfLayout())320      return EC;321  }322 323  uint32_t Length = calculateSerializedLength();324  if (auto EC = Msf.setStreamSize(StreamDBI, Length))325    return EC;326  return Error::success();327}328 329static uint16_t toSecMapFlags(uint32_t Flags) {330  uint16_t Ret = 0;331  if (Flags & COFF::IMAGE_SCN_MEM_READ)332    Ret |= static_cast<uint16_t>(OMFSegDescFlags::Read);333  if (Flags & COFF::IMAGE_SCN_MEM_WRITE)334    Ret |= static_cast<uint16_t>(OMFSegDescFlags::Write);335  if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE)336    Ret |= static_cast<uint16_t>(OMFSegDescFlags::Execute);337  if (!(Flags & COFF::IMAGE_SCN_MEM_16BIT))338    Ret |= static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit);339 340  // This seems always 1.341  Ret |= static_cast<uint16_t>(OMFSegDescFlags::IsSelector);342 343  return Ret;344}345 346// Populate the Section Map from COFF section headers.347//348// A Section Map seem to be a copy of a COFF section list in other format.349// I don't know why a PDB file contains both a COFF section header and350// a Section Map, but it seems it must be present in a PDB.351void DbiStreamBuilder::createSectionMap(352    ArrayRef<llvm::object::coff_section> SecHdrs) {353  int Idx = 0;354 355  auto Add = [&]() -> SecMapEntry & {356    SectionMap.emplace_back();357    auto &Entry = SectionMap.back();358    memset(&Entry, 0, sizeof(Entry));359 360    Entry.Frame = Idx + 1;361 362    // We don't know the meaning of these fields yet.363    Entry.SecName = UINT16_MAX;364    Entry.ClassName = UINT16_MAX;365 366    return Entry;367  };368 369  for (auto &Hdr : SecHdrs) {370    auto &Entry = Add();371    Entry.Flags = toSecMapFlags(Hdr.Characteristics);372    Entry.SecByteLength = Hdr.VirtualSize;373    ++Idx;374  }375 376  // The last entry is for absolute symbols.377  auto &Entry = Add();378  Entry.Flags = static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit) |379                static_cast<uint16_t>(OMFSegDescFlags::IsAbsoluteAddress);380  Entry.SecByteLength = UINT32_MAX;381}382 383Error DbiStreamBuilder::commit(const msf::MSFLayout &Layout,384                               WritableBinaryStreamRef MsfBuffer) {385  llvm::TimeTraceScope timeScope("Commit DBI stream");386  if (auto EC = finalize())387    return EC;388 389  auto DbiS = WritableMappedBlockStream::createIndexedStream(390      Layout, MsfBuffer, StreamDBI, Allocator);391 392  BinaryStreamWriter Writer(*DbiS);393  if (auto EC = Writer.writeObject(*Header))394    return EC;395 396  for (auto &M : ModiList) {397    if (auto EC = M->commit(Writer))398      return EC;399  }400 401  // Commit symbol streams. This is a lot of data, so do it in parallel.402  if (auto EC = parallelForEachError(403          ModiList, [&](std::unique_ptr<DbiModuleDescriptorBuilder> &M) {404            return M->commitSymbolStream(Layout, MsfBuffer);405          }))406    return EC;407 408  if (!SectionContribs.empty()) {409    if (auto EC = Writer.writeEnum(DbiSecContribVer60))410      return EC;411    if (auto EC = Writer.writeArray(ArrayRef(SectionContribs)))412      return EC;413  }414 415  if (!SectionMap.empty()) {416    ulittle16_t Size = static_cast<ulittle16_t>(SectionMap.size());417    SecMapHeader SMHeader = {Size, Size};418    if (auto EC = Writer.writeObject(SMHeader))419      return EC;420    if (auto EC = Writer.writeArray(ArrayRef(SectionMap)))421      return EC;422  }423 424  if (auto EC = Writer.writeStreamRef(FileInfoBuffer))425    return EC;426 427  if (auto EC = ECNamesBuilder.commit(Writer))428    return EC;429 430  for (auto &Stream : DbgStreams) {431    uint16_t StreamNumber = kInvalidStreamIndex;432    if (Stream)433      StreamNumber = Stream->StreamNumber;434    if (auto EC = Writer.writeInteger(StreamNumber))435      return EC;436  }437 438  for (auto &Stream : DbgStreams) {439    if (!Stream)440      continue;441    assert(Stream->StreamNumber != kInvalidStreamIndex);442 443    auto WritableStream = WritableMappedBlockStream::createIndexedStream(444        Layout, MsfBuffer, Stream->StreamNumber, Allocator);445    BinaryStreamWriter DbgStreamWriter(*WritableStream);446 447    if (auto EC = Stream->WriteFn(DbgStreamWriter))448      return EC;449  }450 451  if (Writer.bytesRemaining() > 0)452    return make_error<RawError>(raw_error_code::invalid_format,453                                "Unexpected bytes found in DBI Stream");454  return Error::success();455}456