280 lines
8.3 KiB
C++
280 lines
8.3 KiB
C++
|
//===- DbiModuleList.cpp - PDB module information list --------------------===//
|
||
|
//
|
||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "llvm/DebugInfo/PDB/Native/DbiModuleList.h"
|
||
|
#include "llvm/ADT/StringRef.h"
|
||
|
#include "llvm/ADT/iterator_range.h"
|
||
|
#include "llvm/DebugInfo/PDB/Native/RawError.h"
|
||
|
#include "llvm/Support/BinaryStreamReader.h"
|
||
|
#include "llvm/Support/Error.h"
|
||
|
#include <algorithm>
|
||
|
#include <cassert>
|
||
|
#include <cstddef>
|
||
|
#include <cstdint>
|
||
|
|
||
|
using namespace llvm;
|
||
|
using namespace llvm::pdb;
|
||
|
|
||
|
DbiModuleSourceFilesIterator::DbiModuleSourceFilesIterator(
|
||
|
const DbiModuleList &Modules, uint32_t Modi, uint16_t Filei)
|
||
|
: Modules(&Modules), Modi(Modi), Filei(Filei) {
|
||
|
setValue();
|
||
|
}
|
||
|
|
||
|
bool DbiModuleSourceFilesIterator::
|
||
|
operator==(const DbiModuleSourceFilesIterator &R) const {
|
||
|
// incompatible iterators are never equal
|
||
|
if (!isCompatible(R))
|
||
|
return false;
|
||
|
|
||
|
// If they're compatible, and they're both ends, then they're equal.
|
||
|
if (isEnd() && R.isEnd())
|
||
|
return true;
|
||
|
|
||
|
// If one is an end and the other is not, they're not equal.
|
||
|
if (isEnd() != R.isEnd())
|
||
|
return false;
|
||
|
|
||
|
// Now we know:
|
||
|
// - They're compatible
|
||
|
// - They're not *both* end iterators
|
||
|
// - Their endness is the same.
|
||
|
// Thus, they're compatible iterators pointing to a valid file on the same
|
||
|
// module. All we need to check are the file indices.
|
||
|
assert(Modules == R.Modules);
|
||
|
assert(Modi == R.Modi);
|
||
|
assert(!isEnd());
|
||
|
assert(!R.isEnd());
|
||
|
|
||
|
return (Filei == R.Filei);
|
||
|
}
|
||
|
|
||
|
bool DbiModuleSourceFilesIterator::
|
||
|
operator<(const DbiModuleSourceFilesIterator &R) const {
|
||
|
assert(isCompatible(R));
|
||
|
|
||
|
// It's not sufficient to compare the file indices, because default
|
||
|
// constructed iterators could be equal to iterators with valid indices. To
|
||
|
// account for this, early-out if they're equal.
|
||
|
if (*this == R)
|
||
|
return false;
|
||
|
|
||
|
return Filei < R.Filei;
|
||
|
}
|
||
|
|
||
|
std::ptrdiff_t DbiModuleSourceFilesIterator::
|
||
|
operator-(const DbiModuleSourceFilesIterator &R) const {
|
||
|
assert(isCompatible(R));
|
||
|
assert(!(*this < R));
|
||
|
|
||
|
// If they're both end iterators, the distance is 0.
|
||
|
if (isEnd() && R.isEnd())
|
||
|
return 0;
|
||
|
|
||
|
assert(!R.isEnd());
|
||
|
|
||
|
// At this point, R cannot be end, but *this can, which means that *this
|
||
|
// might be a universal end iterator with none of its fields set. So in that
|
||
|
// case have to rely on R as the authority to figure out how many files there
|
||
|
// are to compute the distance.
|
||
|
uint32_t Thisi = Filei;
|
||
|
if (isEnd()) {
|
||
|
uint32_t RealModi = R.Modi;
|
||
|
Thisi = R.Modules->getSourceFileCount(RealModi);
|
||
|
}
|
||
|
|
||
|
assert(Thisi >= R.Filei);
|
||
|
return Thisi - R.Filei;
|
||
|
}
|
||
|
|
||
|
DbiModuleSourceFilesIterator &DbiModuleSourceFilesIterator::
|
||
|
operator+=(std::ptrdiff_t N) {
|
||
|
assert(!isEnd());
|
||
|
|
||
|
Filei += N;
|
||
|
assert(Filei <= Modules->getSourceFileCount(Modi));
|
||
|
setValue();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
DbiModuleSourceFilesIterator &DbiModuleSourceFilesIterator::
|
||
|
operator-=(std::ptrdiff_t N) {
|
||
|
// Note that we can subtract from an end iterator, but not a universal end
|
||
|
// iterator.
|
||
|
assert(!isUniversalEnd());
|
||
|
|
||
|
assert(N <= Filei);
|
||
|
|
||
|
Filei -= N;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
void DbiModuleSourceFilesIterator::setValue() {
|
||
|
if (isEnd()) {
|
||
|
ThisValue = "";
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
uint32_t Off = Modules->ModuleInitialFileIndex[Modi] + Filei;
|
||
|
auto ExpectedValue = Modules->getFileName(Off);
|
||
|
if (!ExpectedValue) {
|
||
|
consumeError(ExpectedValue.takeError());
|
||
|
Filei = Modules->getSourceFileCount(Modi);
|
||
|
} else
|
||
|
ThisValue = *ExpectedValue;
|
||
|
}
|
||
|
|
||
|
bool DbiModuleSourceFilesIterator::isEnd() const {
|
||
|
if (isUniversalEnd())
|
||
|
return true;
|
||
|
|
||
|
assert(Modules);
|
||
|
assert(Modi <= Modules->getModuleCount());
|
||
|
assert(Filei <= Modules->getSourceFileCount(Modi));
|
||
|
|
||
|
if (Modi == Modules->getModuleCount())
|
||
|
return true;
|
||
|
if (Filei == Modules->getSourceFileCount(Modi))
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool DbiModuleSourceFilesIterator::isUniversalEnd() const { return !Modules; }
|
||
|
|
||
|
bool DbiModuleSourceFilesIterator::isCompatible(
|
||
|
const DbiModuleSourceFilesIterator &R) const {
|
||
|
// Universal iterators are compatible with any other iterator.
|
||
|
if (isUniversalEnd() || R.isUniversalEnd())
|
||
|
return true;
|
||
|
|
||
|
// At this point, neither iterator is a universal end iterator, although one
|
||
|
// or both might be non-universal end iterators. Regardless, the module index
|
||
|
// is valid, so they are compatible if and only if they refer to the same
|
||
|
// module.
|
||
|
return Modi == R.Modi;
|
||
|
}
|
||
|
|
||
|
Error DbiModuleList::initialize(BinaryStreamRef ModInfo,
|
||
|
BinaryStreamRef FileInfo) {
|
||
|
if (auto EC = initializeModInfo(ModInfo))
|
||
|
return EC;
|
||
|
if (auto EC = initializeFileInfo(FileInfo))
|
||
|
return EC;
|
||
|
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
Error DbiModuleList::initializeModInfo(BinaryStreamRef ModInfo) {
|
||
|
ModInfoSubstream = ModInfo;
|
||
|
|
||
|
if (ModInfo.getLength() == 0)
|
||
|
return Error::success();
|
||
|
|
||
|
BinaryStreamReader Reader(ModInfo);
|
||
|
|
||
|
if (auto EC = Reader.readArray(Descriptors, ModInfo.getLength()))
|
||
|
return EC;
|
||
|
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
Error DbiModuleList::initializeFileInfo(BinaryStreamRef FileInfo) {
|
||
|
FileInfoSubstream = FileInfo;
|
||
|
|
||
|
if (FileInfo.getLength() == 0)
|
||
|
return Error::success();
|
||
|
|
||
|
BinaryStreamReader FISR(FileInfo);
|
||
|
if (auto EC = FISR.readObject(FileInfoHeader))
|
||
|
return EC;
|
||
|
|
||
|
// First is an array of `NumModules` module indices. This does not seem to be
|
||
|
// used for anything meaningful, so we ignore it.
|
||
|
FixedStreamArray<support::ulittle16_t> ModuleIndices;
|
||
|
if (auto EC = FISR.readArray(ModuleIndices, FileInfoHeader->NumModules))
|
||
|
return EC;
|
||
|
if (auto EC = FISR.readArray(ModFileCountArray, FileInfoHeader->NumModules))
|
||
|
return EC;
|
||
|
|
||
|
// Compute the real number of source files. We can't trust the value in
|
||
|
// `FileInfoHeader->NumSourceFiles` because it is a unit16, and the sum of all
|
||
|
// source file counts might be larger than a unit16. So we compute the real
|
||
|
// count by summing up the individual counts.
|
||
|
uint32_t NumSourceFiles = 0;
|
||
|
for (auto Count : ModFileCountArray)
|
||
|
NumSourceFiles += Count;
|
||
|
|
||
|
// In the reference implementation, this array is where the pointer documented
|
||
|
// at the definition of ModuleInfoHeader::FileNameOffs points to. Note that
|
||
|
// although the field in ModuleInfoHeader is ignored this array is not, as it
|
||
|
// is the authority on where each filename begins in the names buffer.
|
||
|
if (auto EC = FISR.readArray(FileNameOffsets, NumSourceFiles))
|
||
|
return EC;
|
||
|
|
||
|
if (auto EC = FISR.readStreamRef(NamesBuffer))
|
||
|
return EC;
|
||
|
|
||
|
auto DescriptorIter = Descriptors.begin();
|
||
|
uint32_t NextFileIndex = 0;
|
||
|
ModuleInitialFileIndex.resize(FileInfoHeader->NumModules);
|
||
|
ModuleDescriptorOffsets.resize(FileInfoHeader->NumModules);
|
||
|
for (size_t I = 0; I < FileInfoHeader->NumModules; ++I) {
|
||
|
assert(DescriptorIter != Descriptors.end());
|
||
|
ModuleInitialFileIndex[I] = NextFileIndex;
|
||
|
ModuleDescriptorOffsets[I] = DescriptorIter.offset();
|
||
|
|
||
|
NextFileIndex += ModFileCountArray[I];
|
||
|
++DescriptorIter;
|
||
|
}
|
||
|
|
||
|
assert(DescriptorIter == Descriptors.end());
|
||
|
assert(NextFileIndex == NumSourceFiles);
|
||
|
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
uint32_t DbiModuleList::getModuleCount() const {
|
||
|
return FileInfoHeader->NumModules;
|
||
|
}
|
||
|
|
||
|
uint32_t DbiModuleList::getSourceFileCount() const {
|
||
|
return FileNameOffsets.size();
|
||
|
}
|
||
|
|
||
|
uint16_t DbiModuleList::getSourceFileCount(uint32_t Modi) const {
|
||
|
return ModFileCountArray[Modi];
|
||
|
}
|
||
|
|
||
|
DbiModuleDescriptor DbiModuleList::getModuleDescriptor(uint32_t Modi) const {
|
||
|
assert(Modi < getModuleCount());
|
||
|
uint32_t Offset = ModuleDescriptorOffsets[Modi];
|
||
|
auto Iter = Descriptors.at(Offset);
|
||
|
assert(Iter != Descriptors.end());
|
||
|
return *Iter;
|
||
|
}
|
||
|
|
||
|
iterator_range<DbiModuleSourceFilesIterator>
|
||
|
DbiModuleList::source_files(uint32_t Modi) const {
|
||
|
return make_range<DbiModuleSourceFilesIterator>(
|
||
|
DbiModuleSourceFilesIterator(*this, Modi, 0),
|
||
|
DbiModuleSourceFilesIterator());
|
||
|
}
|
||
|
|
||
|
Expected<StringRef> DbiModuleList::getFileName(uint32_t Index) const {
|
||
|
BinaryStreamReader Names(NamesBuffer);
|
||
|
if (Index >= getSourceFileCount())
|
||
|
return make_error<RawError>(raw_error_code::index_out_of_bounds);
|
||
|
|
||
|
uint32_t FileOffset = FileNameOffsets[Index];
|
||
|
Names.setOffset(FileOffset);
|
||
|
StringRef Name;
|
||
|
if (auto EC = Names.readCString(Name))
|
||
|
return std::move(EC);
|
||
|
return Name;
|
||
|
}
|