1 //===- DbiModuleList.cpp - PDB module information list --------------------===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/DebugInfo/PDB/Native/DbiModuleList.h"
10 #include "llvm/ADT/StringRef.h"
11 #include "llvm/ADT/iterator_range.h"
12 #include "llvm/DebugInfo/PDB/Native/RawError.h"
13 #include "llvm/DebugInfo/PDB/Native/RawTypes.h"
14 #include "llvm/Support/BinaryStreamReader.h"
15 #include "llvm/Support/Error.h"
16 #include <algorithm>
17 #include <cassert>
18 #include <cstddef>
19 #include <cstdint>
20 
21 using namespace llvm;
22 using namespace llvm::pdb;
23 
24 DbiModuleSourceFilesIterator::DbiModuleSourceFilesIterator(
25     const DbiModuleList &Modules, uint32_t Modi, uint16_t Filei)
26     : Modules(&Modules), Modi(Modi), Filei(Filei) {
27   setValue();
28 }
29 
30 bool DbiModuleSourceFilesIterator::
31 operator==(const DbiModuleSourceFilesIterator &R) const {
32   // incompatible iterators are never equal
33   if (!isCompatible(R))
34     return false;
35 
36   // If they're compatible, and they're both ends, then they're equal.
37   if (isEnd() && R.isEnd())
38     return true;
39 
40   // If one is an end and the other is not, they're not equal.
41   if (isEnd() != R.isEnd())
42     return false;
43 
44   // Now we know:
45   // - They're compatible
46   // - They're not *both* end iterators
47   // - Their endness is the same.
48   // Thus, they're compatible iterators pointing to a valid file on the same
49   // module.  All we need to check are the file indices.
50   assert(Modules == R.Modules);
51   assert(Modi == R.Modi);
52   assert(!isEnd());
53   assert(!R.isEnd());
54 
55   return (Filei == R.Filei);
56 }
57 
58 bool DbiModuleSourceFilesIterator::
59 operator<(const DbiModuleSourceFilesIterator &R) const {
60   assert(isCompatible(R));
61 
62   // It's not sufficient to compare the file indices, because default
63   // constructed iterators could be equal to iterators with valid indices.  To
64   // account for this, early-out if they're equal.
65   if (*this == R)
66     return false;
67 
68   return Filei < R.Filei;
69 }
70 
71 std::ptrdiff_t DbiModuleSourceFilesIterator::
72 operator-(const DbiModuleSourceFilesIterator &R) const {
73   assert(isCompatible(R));
74   assert(!(*this < R));
75 
76   // If they're both end iterators, the distance is 0.
77   if (isEnd() && R.isEnd())
78     return 0;
79 
80   assert(!R.isEnd());
81 
82   // At this point, R cannot be end, but *this can, which means that *this
83   // might be a universal end iterator with none of its fields set.  So in that
84   // case have to rely on R as the authority to figure out how many files there
85   // are to compute the distance.
86   uint32_t Thisi = Filei;
87   if (isEnd()) {
88     uint32_t RealModi = R.Modi;
89     Thisi = R.Modules->getSourceFileCount(RealModi);
90   }
91 
92   assert(Thisi >= R.Filei);
93   return Thisi - R.Filei;
94 }
95 
96 DbiModuleSourceFilesIterator &DbiModuleSourceFilesIterator::
97 operator+=(std::ptrdiff_t N) {
98   assert(!isEnd());
99 
100   Filei += N;
101   assert(Filei <= Modules->getSourceFileCount(Modi));
102   setValue();
103   return *this;
104 }
105 
106 DbiModuleSourceFilesIterator &DbiModuleSourceFilesIterator::
107 operator-=(std::ptrdiff_t N) {
108   // Note that we can subtract from an end iterator, but not a universal end
109   // iterator.
110   assert(!isUniversalEnd());
111 
112   assert(N <= Filei);
113 
114   Filei -= N;
115   return *this;
116 }
117 
118 void DbiModuleSourceFilesIterator::setValue() {
119   if (isEnd()) {
120     ThisValue = "";
121     return;
122   }
123 
124   uint32_t Off = Modules->ModuleInitialFileIndex[Modi] + Filei;
125   auto ExpectedValue = Modules->getFileName(Off);
126   if (!ExpectedValue) {
127     consumeError(ExpectedValue.takeError());
128     Filei = Modules->getSourceFileCount(Modi);
129   } else
130     ThisValue = *ExpectedValue;
131 }
132 
133 bool DbiModuleSourceFilesIterator::isEnd() const {
134   if (isUniversalEnd())
135     return true;
136 
137   assert(Modules);
138   assert(Modi <= Modules->getModuleCount());
139   assert(Filei <= Modules->getSourceFileCount(Modi));
140 
141   if (Modi == Modules->getModuleCount())
142     return true;
143   if (Filei == Modules->getSourceFileCount(Modi))
144     return true;
145   return false;
146 }
147 
148 bool DbiModuleSourceFilesIterator::isUniversalEnd() const { return !Modules; }
149 
150 bool DbiModuleSourceFilesIterator::isCompatible(
151     const DbiModuleSourceFilesIterator &R) const {
152   // Universal iterators are compatible with any other iterator.
153   if (isUniversalEnd() || R.isUniversalEnd())
154     return true;
155 
156   // At this point, neither iterator is a universal end iterator, although one
157   // or both might be non-universal end iterators.  Regardless, the module index
158   // is valid, so they are compatible if and only if they refer to the same
159   // module.
160   return Modi == R.Modi;
161 }
162 
163 Error DbiModuleList::initialize(BinaryStreamRef ModInfo,
164                                 BinaryStreamRef FileInfo) {
165   if (auto EC = initializeModInfo(ModInfo))
166     return EC;
167   if (auto EC = initializeFileInfo(FileInfo))
168     return EC;
169 
170   return Error::success();
171 }
172 
173 Error DbiModuleList::initializeModInfo(BinaryStreamRef ModInfo) {
174   ModInfoSubstream = ModInfo;
175 
176   if (ModInfo.getLength() == 0)
177     return Error::success();
178 
179   BinaryStreamReader Reader(ModInfo);
180 
181   if (auto EC = Reader.readArray(Descriptors, ModInfo.getLength()))
182     return EC;
183 
184   return Error::success();
185 }
186 
187 Error DbiModuleList::initializeFileInfo(BinaryStreamRef FileInfo) {
188   FileInfoSubstream = FileInfo;
189 
190   if (FileInfo.getLength() == 0)
191     return Error::success();
192 
193   BinaryStreamReader FISR(FileInfo);
194   if (auto EC = FISR.readObject(FileInfoHeader))
195     return EC;
196 
197   // First is an array of `NumModules` module indices.  This does not seem to be
198   // used for anything meaningful, so we ignore it.
199   FixedStreamArray<support::ulittle16_t> ModuleIndices;
200   if (auto EC = FISR.readArray(ModuleIndices, FileInfoHeader->NumModules))
201     return EC;
202   if (auto EC = FISR.readArray(ModFileCountArray, FileInfoHeader->NumModules))
203     return EC;
204 
205   // Compute the real number of source files.  We can't trust the value in
206   // `FileInfoHeader->NumSourceFiles` because it is a unit16, and the sum of all
207   // source file counts might be larger than a unit16.  So we compute the real
208   // count by summing up the individual counts.
209   uint32_t NumSourceFiles = 0;
210   for (auto Count : ModFileCountArray)
211     NumSourceFiles += Count;
212 
213   // In the reference implementation, this array is where the pointer documented
214   // at the definition of ModuleInfoHeader::FileNameOffs points to.  Note that
215   // although the field in ModuleInfoHeader is ignored this array is not, as it
216   // is the authority on where each filename begins in the names buffer.
217   if (auto EC = FISR.readArray(FileNameOffsets, NumSourceFiles))
218     return EC;
219 
220   if (auto EC = FISR.readStreamRef(NamesBuffer))
221     return EC;
222 
223   auto DescriptorIter = Descriptors.begin();
224   uint32_t NextFileIndex = 0;
225   ModuleInitialFileIndex.resize(FileInfoHeader->NumModules);
226   ModuleDescriptorOffsets.resize(FileInfoHeader->NumModules);
227   for (size_t I = 0; I < FileInfoHeader->NumModules; ++I) {
228     assert(DescriptorIter != Descriptors.end());
229     ModuleInitialFileIndex[I] = NextFileIndex;
230     ModuleDescriptorOffsets[I] = DescriptorIter.offset();
231 
232     NextFileIndex += ModFileCountArray[I];
233     ++DescriptorIter;
234   }
235 
236   assert(DescriptorIter == Descriptors.end());
237   assert(NextFileIndex == NumSourceFiles);
238 
239   return Error::success();
240 }
241 
242 uint32_t DbiModuleList::getModuleCount() const {
243   // Workaround to avoid the crash until upstream issue is fixed:
244   // https://github.com/llvm/llvm-project/issues/55214
245   return FileInfoHeader ? FileInfoHeader->NumModules : 0;
246 }
247 
248 uint32_t DbiModuleList::getSourceFileCount() const {
249   return FileNameOffsets.size();
250 }
251 
252 uint16_t DbiModuleList::getSourceFileCount(uint32_t Modi) const {
253   return ModFileCountArray[Modi];
254 }
255 
256 DbiModuleDescriptor DbiModuleList::getModuleDescriptor(uint32_t Modi) const {
257   assert(Modi < getModuleCount());
258   uint32_t Offset = ModuleDescriptorOffsets[Modi];
259   auto Iter = Descriptors.at(Offset);
260   assert(Iter != Descriptors.end());
261   return *Iter;
262 }
263 
264 iterator_range<DbiModuleSourceFilesIterator>
265 DbiModuleList::source_files(uint32_t Modi) const {
266   return make_range<DbiModuleSourceFilesIterator>(
267       DbiModuleSourceFilesIterator(*this, Modi, 0),
268       DbiModuleSourceFilesIterator());
269 }
270 
271 Expected<StringRef> DbiModuleList::getFileName(uint32_t Index) const {
272   BinaryStreamReader Names(NamesBuffer);
273   if (Index >= getSourceFileCount())
274     return make_error<RawError>(raw_error_code::index_out_of_bounds);
275 
276   uint32_t FileOffset = FileNameOffsets[Index];
277   Names.setOffset(FileOffset);
278   StringRef Name;
279   if (auto EC = Names.readCString(Name))
280     return std::move(EC);
281   return Name;
282 }
283