10b57cec5SDimitry Andric //===- DbiModuleList.cpp - PDB module information list --------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/DbiModuleList.h"
100b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
110b57cec5SDimitry Andric #include "llvm/ADT/iterator_range.h"
120b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/RawError.h"
1381ad6265SDimitry Andric #include "llvm/DebugInfo/PDB/Native/RawTypes.h"
140b57cec5SDimitry Andric #include "llvm/Support/BinaryStreamReader.h"
150b57cec5SDimitry Andric #include "llvm/Support/Error.h"
160b57cec5SDimitry Andric #include <algorithm>
170b57cec5SDimitry Andric #include <cassert>
180b57cec5SDimitry Andric #include <cstddef>
190b57cec5SDimitry Andric #include <cstdint>
200b57cec5SDimitry Andric
210b57cec5SDimitry Andric using namespace llvm;
220b57cec5SDimitry Andric using namespace llvm::pdb;
230b57cec5SDimitry Andric
DbiModuleSourceFilesIterator(const DbiModuleList & Modules,uint32_t Modi,uint16_t Filei)240b57cec5SDimitry Andric DbiModuleSourceFilesIterator::DbiModuleSourceFilesIterator(
250b57cec5SDimitry Andric const DbiModuleList &Modules, uint32_t Modi, uint16_t Filei)
260b57cec5SDimitry Andric : Modules(&Modules), Modi(Modi), Filei(Filei) {
270b57cec5SDimitry Andric setValue();
280b57cec5SDimitry Andric }
290b57cec5SDimitry Andric
300b57cec5SDimitry Andric bool DbiModuleSourceFilesIterator::
operator ==(const DbiModuleSourceFilesIterator & R) const310b57cec5SDimitry Andric operator==(const DbiModuleSourceFilesIterator &R) const {
320b57cec5SDimitry Andric // incompatible iterators are never equal
330b57cec5SDimitry Andric if (!isCompatible(R))
340b57cec5SDimitry Andric return false;
350b57cec5SDimitry Andric
360b57cec5SDimitry Andric // If they're compatible, and they're both ends, then they're equal.
370b57cec5SDimitry Andric if (isEnd() && R.isEnd())
380b57cec5SDimitry Andric return true;
390b57cec5SDimitry Andric
400b57cec5SDimitry Andric // If one is an end and the other is not, they're not equal.
410b57cec5SDimitry Andric if (isEnd() != R.isEnd())
420b57cec5SDimitry Andric return false;
430b57cec5SDimitry Andric
440b57cec5SDimitry Andric // Now we know:
450b57cec5SDimitry Andric // - They're compatible
460b57cec5SDimitry Andric // - They're not *both* end iterators
470b57cec5SDimitry Andric // - Their endness is the same.
480b57cec5SDimitry Andric // Thus, they're compatible iterators pointing to a valid file on the same
490b57cec5SDimitry Andric // module. All we need to check are the file indices.
500b57cec5SDimitry Andric assert(Modules == R.Modules);
510b57cec5SDimitry Andric assert(Modi == R.Modi);
520b57cec5SDimitry Andric assert(!isEnd());
530b57cec5SDimitry Andric assert(!R.isEnd());
540b57cec5SDimitry Andric
550b57cec5SDimitry Andric return (Filei == R.Filei);
560b57cec5SDimitry Andric }
570b57cec5SDimitry Andric
580b57cec5SDimitry Andric bool DbiModuleSourceFilesIterator::
operator <(const DbiModuleSourceFilesIterator & R) const590b57cec5SDimitry Andric operator<(const DbiModuleSourceFilesIterator &R) const {
600b57cec5SDimitry Andric assert(isCompatible(R));
610b57cec5SDimitry Andric
620b57cec5SDimitry Andric // It's not sufficient to compare the file indices, because default
630b57cec5SDimitry Andric // constructed iterators could be equal to iterators with valid indices. To
640b57cec5SDimitry Andric // account for this, early-out if they're equal.
650b57cec5SDimitry Andric if (*this == R)
660b57cec5SDimitry Andric return false;
670b57cec5SDimitry Andric
680b57cec5SDimitry Andric return Filei < R.Filei;
690b57cec5SDimitry Andric }
700b57cec5SDimitry Andric
710b57cec5SDimitry Andric std::ptrdiff_t DbiModuleSourceFilesIterator::
operator -(const DbiModuleSourceFilesIterator & R) const720b57cec5SDimitry Andric operator-(const DbiModuleSourceFilesIterator &R) const {
730b57cec5SDimitry Andric assert(isCompatible(R));
740b57cec5SDimitry Andric assert(!(*this < R));
750b57cec5SDimitry Andric
760b57cec5SDimitry Andric // If they're both end iterators, the distance is 0.
770b57cec5SDimitry Andric if (isEnd() && R.isEnd())
780b57cec5SDimitry Andric return 0;
790b57cec5SDimitry Andric
800b57cec5SDimitry Andric assert(!R.isEnd());
810b57cec5SDimitry Andric
820b57cec5SDimitry Andric // At this point, R cannot be end, but *this can, which means that *this
830b57cec5SDimitry Andric // might be a universal end iterator with none of its fields set. So in that
840b57cec5SDimitry Andric // case have to rely on R as the authority to figure out how many files there
850b57cec5SDimitry Andric // are to compute the distance.
860b57cec5SDimitry Andric uint32_t Thisi = Filei;
870b57cec5SDimitry Andric if (isEnd()) {
880b57cec5SDimitry Andric uint32_t RealModi = R.Modi;
890b57cec5SDimitry Andric Thisi = R.Modules->getSourceFileCount(RealModi);
900b57cec5SDimitry Andric }
910b57cec5SDimitry Andric
920b57cec5SDimitry Andric assert(Thisi >= R.Filei);
930b57cec5SDimitry Andric return Thisi - R.Filei;
940b57cec5SDimitry Andric }
950b57cec5SDimitry Andric
960b57cec5SDimitry Andric DbiModuleSourceFilesIterator &DbiModuleSourceFilesIterator::
operator +=(std::ptrdiff_t N)970b57cec5SDimitry Andric operator+=(std::ptrdiff_t N) {
980b57cec5SDimitry Andric assert(!isEnd());
990b57cec5SDimitry Andric
1000b57cec5SDimitry Andric Filei += N;
1010b57cec5SDimitry Andric assert(Filei <= Modules->getSourceFileCount(Modi));
1020b57cec5SDimitry Andric setValue();
1030b57cec5SDimitry Andric return *this;
1040b57cec5SDimitry Andric }
1050b57cec5SDimitry Andric
1060b57cec5SDimitry Andric DbiModuleSourceFilesIterator &DbiModuleSourceFilesIterator::
operator -=(std::ptrdiff_t N)1070b57cec5SDimitry Andric operator-=(std::ptrdiff_t N) {
1080b57cec5SDimitry Andric // Note that we can subtract from an end iterator, but not a universal end
1090b57cec5SDimitry Andric // iterator.
1100b57cec5SDimitry Andric assert(!isUniversalEnd());
1110b57cec5SDimitry Andric
1120b57cec5SDimitry Andric assert(N <= Filei);
1130b57cec5SDimitry Andric
1140b57cec5SDimitry Andric Filei -= N;
1150b57cec5SDimitry Andric return *this;
1160b57cec5SDimitry Andric }
1170b57cec5SDimitry Andric
setValue()1180b57cec5SDimitry Andric void DbiModuleSourceFilesIterator::setValue() {
1190b57cec5SDimitry Andric if (isEnd()) {
1200b57cec5SDimitry Andric ThisValue = "";
1210b57cec5SDimitry Andric return;
1220b57cec5SDimitry Andric }
1230b57cec5SDimitry Andric
1240b57cec5SDimitry Andric uint32_t Off = Modules->ModuleInitialFileIndex[Modi] + Filei;
1250b57cec5SDimitry Andric auto ExpectedValue = Modules->getFileName(Off);
1260b57cec5SDimitry Andric if (!ExpectedValue) {
1270b57cec5SDimitry Andric consumeError(ExpectedValue.takeError());
1280b57cec5SDimitry Andric Filei = Modules->getSourceFileCount(Modi);
1290b57cec5SDimitry Andric } else
1300b57cec5SDimitry Andric ThisValue = *ExpectedValue;
1310b57cec5SDimitry Andric }
1320b57cec5SDimitry Andric
isEnd() const1330b57cec5SDimitry Andric bool DbiModuleSourceFilesIterator::isEnd() const {
1340b57cec5SDimitry Andric if (isUniversalEnd())
1350b57cec5SDimitry Andric return true;
1360b57cec5SDimitry Andric
1370b57cec5SDimitry Andric assert(Modules);
1380b57cec5SDimitry Andric assert(Modi <= Modules->getModuleCount());
1390b57cec5SDimitry Andric assert(Filei <= Modules->getSourceFileCount(Modi));
1400b57cec5SDimitry Andric
1410b57cec5SDimitry Andric if (Modi == Modules->getModuleCount())
1420b57cec5SDimitry Andric return true;
1430b57cec5SDimitry Andric if (Filei == Modules->getSourceFileCount(Modi))
1440b57cec5SDimitry Andric return true;
1450b57cec5SDimitry Andric return false;
1460b57cec5SDimitry Andric }
1470b57cec5SDimitry Andric
isUniversalEnd() const1480b57cec5SDimitry Andric bool DbiModuleSourceFilesIterator::isUniversalEnd() const { return !Modules; }
1490b57cec5SDimitry Andric
isCompatible(const DbiModuleSourceFilesIterator & R) const1500b57cec5SDimitry Andric bool DbiModuleSourceFilesIterator::isCompatible(
1510b57cec5SDimitry Andric const DbiModuleSourceFilesIterator &R) const {
1520b57cec5SDimitry Andric // Universal iterators are compatible with any other iterator.
1530b57cec5SDimitry Andric if (isUniversalEnd() || R.isUniversalEnd())
1540b57cec5SDimitry Andric return true;
1550b57cec5SDimitry Andric
1560b57cec5SDimitry Andric // At this point, neither iterator is a universal end iterator, although one
1570b57cec5SDimitry Andric // or both might be non-universal end iterators. Regardless, the module index
1580b57cec5SDimitry Andric // is valid, so they are compatible if and only if they refer to the same
1590b57cec5SDimitry Andric // module.
1600b57cec5SDimitry Andric return Modi == R.Modi;
1610b57cec5SDimitry Andric }
1620b57cec5SDimitry Andric
initialize(BinaryStreamRef ModInfo,BinaryStreamRef FileInfo)1630b57cec5SDimitry Andric Error DbiModuleList::initialize(BinaryStreamRef ModInfo,
1640b57cec5SDimitry Andric BinaryStreamRef FileInfo) {
1650b57cec5SDimitry Andric if (auto EC = initializeModInfo(ModInfo))
1660b57cec5SDimitry Andric return EC;
1670b57cec5SDimitry Andric if (auto EC = initializeFileInfo(FileInfo))
1680b57cec5SDimitry Andric return EC;
1690b57cec5SDimitry Andric
1700b57cec5SDimitry Andric return Error::success();
1710b57cec5SDimitry Andric }
1720b57cec5SDimitry Andric
initializeModInfo(BinaryStreamRef ModInfo)1730b57cec5SDimitry Andric Error DbiModuleList::initializeModInfo(BinaryStreamRef ModInfo) {
1740b57cec5SDimitry Andric ModInfoSubstream = ModInfo;
1750b57cec5SDimitry Andric
1760b57cec5SDimitry Andric if (ModInfo.getLength() == 0)
1770b57cec5SDimitry Andric return Error::success();
1780b57cec5SDimitry Andric
1790b57cec5SDimitry Andric BinaryStreamReader Reader(ModInfo);
1800b57cec5SDimitry Andric
1810b57cec5SDimitry Andric if (auto EC = Reader.readArray(Descriptors, ModInfo.getLength()))
1820b57cec5SDimitry Andric return EC;
1830b57cec5SDimitry Andric
1840b57cec5SDimitry Andric return Error::success();
1850b57cec5SDimitry Andric }
1860b57cec5SDimitry Andric
initializeFileInfo(BinaryStreamRef FileInfo)1870b57cec5SDimitry Andric Error DbiModuleList::initializeFileInfo(BinaryStreamRef FileInfo) {
1880b57cec5SDimitry Andric FileInfoSubstream = FileInfo;
1890b57cec5SDimitry Andric
1900b57cec5SDimitry Andric if (FileInfo.getLength() == 0)
1910b57cec5SDimitry Andric return Error::success();
1920b57cec5SDimitry Andric
1930b57cec5SDimitry Andric BinaryStreamReader FISR(FileInfo);
1940b57cec5SDimitry Andric if (auto EC = FISR.readObject(FileInfoHeader))
1950b57cec5SDimitry Andric return EC;
1960b57cec5SDimitry Andric
1970b57cec5SDimitry Andric // First is an array of `NumModules` module indices. This does not seem to be
1980b57cec5SDimitry Andric // used for anything meaningful, so we ignore it.
1990b57cec5SDimitry Andric FixedStreamArray<support::ulittle16_t> ModuleIndices;
2000b57cec5SDimitry Andric if (auto EC = FISR.readArray(ModuleIndices, FileInfoHeader->NumModules))
2010b57cec5SDimitry Andric return EC;
2020b57cec5SDimitry Andric if (auto EC = FISR.readArray(ModFileCountArray, FileInfoHeader->NumModules))
2030b57cec5SDimitry Andric return EC;
2040b57cec5SDimitry Andric
2050b57cec5SDimitry Andric // Compute the real number of source files. We can't trust the value in
2060b57cec5SDimitry Andric // `FileInfoHeader->NumSourceFiles` because it is a unit16, and the sum of all
2070b57cec5SDimitry Andric // source file counts might be larger than a unit16. So we compute the real
2080b57cec5SDimitry Andric // count by summing up the individual counts.
2090b57cec5SDimitry Andric uint32_t NumSourceFiles = 0;
2100b57cec5SDimitry Andric for (auto Count : ModFileCountArray)
2110b57cec5SDimitry Andric NumSourceFiles += Count;
2120b57cec5SDimitry Andric
2130b57cec5SDimitry Andric // In the reference implementation, this array is where the pointer documented
2140b57cec5SDimitry Andric // at the definition of ModuleInfoHeader::FileNameOffs points to. Note that
2150b57cec5SDimitry Andric // although the field in ModuleInfoHeader is ignored this array is not, as it
2160b57cec5SDimitry Andric // is the authority on where each filename begins in the names buffer.
2170b57cec5SDimitry Andric if (auto EC = FISR.readArray(FileNameOffsets, NumSourceFiles))
2180b57cec5SDimitry Andric return EC;
2190b57cec5SDimitry Andric
2200b57cec5SDimitry Andric if (auto EC = FISR.readStreamRef(NamesBuffer))
2210b57cec5SDimitry Andric return EC;
2220b57cec5SDimitry Andric
2230b57cec5SDimitry Andric auto DescriptorIter = Descriptors.begin();
2240b57cec5SDimitry Andric uint32_t NextFileIndex = 0;
2250b57cec5SDimitry Andric ModuleInitialFileIndex.resize(FileInfoHeader->NumModules);
2260b57cec5SDimitry Andric ModuleDescriptorOffsets.resize(FileInfoHeader->NumModules);
2270b57cec5SDimitry Andric for (size_t I = 0; I < FileInfoHeader->NumModules; ++I) {
2280b57cec5SDimitry Andric assert(DescriptorIter != Descriptors.end());
2290b57cec5SDimitry Andric ModuleInitialFileIndex[I] = NextFileIndex;
2300b57cec5SDimitry Andric ModuleDescriptorOffsets[I] = DescriptorIter.offset();
2310b57cec5SDimitry Andric
2320b57cec5SDimitry Andric NextFileIndex += ModFileCountArray[I];
2330b57cec5SDimitry Andric ++DescriptorIter;
2340b57cec5SDimitry Andric }
2350b57cec5SDimitry Andric
2360b57cec5SDimitry Andric assert(DescriptorIter == Descriptors.end());
2370b57cec5SDimitry Andric assert(NextFileIndex == NumSourceFiles);
2380b57cec5SDimitry Andric
2390b57cec5SDimitry Andric return Error::success();
2400b57cec5SDimitry Andric }
2410b57cec5SDimitry Andric
getModuleCount() const2420b57cec5SDimitry Andric uint32_t DbiModuleList::getModuleCount() const {
243*06c3fb27SDimitry Andric // Workaround to avoid the crash until upstream issue is fixed:
244*06c3fb27SDimitry Andric // https://github.com/llvm/llvm-project/issues/55214
245*06c3fb27SDimitry Andric return FileInfoHeader ? FileInfoHeader->NumModules : 0;
2460b57cec5SDimitry Andric }
2470b57cec5SDimitry Andric
getSourceFileCount() const2480b57cec5SDimitry Andric uint32_t DbiModuleList::getSourceFileCount() const {
2490b57cec5SDimitry Andric return FileNameOffsets.size();
2500b57cec5SDimitry Andric }
2510b57cec5SDimitry Andric
getSourceFileCount(uint32_t Modi) const2520b57cec5SDimitry Andric uint16_t DbiModuleList::getSourceFileCount(uint32_t Modi) const {
2530b57cec5SDimitry Andric return ModFileCountArray[Modi];
2540b57cec5SDimitry Andric }
2550b57cec5SDimitry Andric
getModuleDescriptor(uint32_t Modi) const2560b57cec5SDimitry Andric DbiModuleDescriptor DbiModuleList::getModuleDescriptor(uint32_t Modi) const {
2570b57cec5SDimitry Andric assert(Modi < getModuleCount());
2580b57cec5SDimitry Andric uint32_t Offset = ModuleDescriptorOffsets[Modi];
2590b57cec5SDimitry Andric auto Iter = Descriptors.at(Offset);
2600b57cec5SDimitry Andric assert(Iter != Descriptors.end());
2610b57cec5SDimitry Andric return *Iter;
2620b57cec5SDimitry Andric }
2630b57cec5SDimitry Andric
2640b57cec5SDimitry Andric iterator_range<DbiModuleSourceFilesIterator>
source_files(uint32_t Modi) const2650b57cec5SDimitry Andric DbiModuleList::source_files(uint32_t Modi) const {
2660b57cec5SDimitry Andric return make_range<DbiModuleSourceFilesIterator>(
2670b57cec5SDimitry Andric DbiModuleSourceFilesIterator(*this, Modi, 0),
2680b57cec5SDimitry Andric DbiModuleSourceFilesIterator());
2690b57cec5SDimitry Andric }
2700b57cec5SDimitry Andric
getFileName(uint32_t Index) const2710b57cec5SDimitry Andric Expected<StringRef> DbiModuleList::getFileName(uint32_t Index) const {
2720b57cec5SDimitry Andric BinaryStreamReader Names(NamesBuffer);
2730b57cec5SDimitry Andric if (Index >= getSourceFileCount())
2740b57cec5SDimitry Andric return make_error<RawError>(raw_error_code::index_out_of_bounds);
2750b57cec5SDimitry Andric
2760b57cec5SDimitry Andric uint32_t FileOffset = FileNameOffsets[Index];
2770b57cec5SDimitry Andric Names.setOffset(FileOffset);
2780b57cec5SDimitry Andric StringRef Name;
2790b57cec5SDimitry Andric if (auto EC = Names.readCString(Name))
2800b57cec5SDimitry Andric return std::move(EC);
2810b57cec5SDimitry Andric return Name;
2820b57cec5SDimitry Andric }
283