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