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 <cassert>
17#include <cstddef>
18#include <cstdint>
19
20using namespace llvm;
21using namespace llvm::pdb;
22
23DbiModuleSourceFilesIterator::DbiModuleSourceFilesIterator(
24 const DbiModuleList &Modules, uint32_t Modi, uint16_t Filei)
25 : Modules(&Modules), Modi(Modi), Filei(Filei) {
26 setValue();
27}
28
29bool DbiModuleSourceFilesIterator::
30operator==(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
57bool DbiModuleSourceFilesIterator::
58operator<(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
70std::ptrdiff_t DbiModuleSourceFilesIterator::
71operator-(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(Modi: RealModi);
89 }
90
91 assert(Thisi >= R.Filei);
92 return Thisi - R.Filei;
93}
94
95DbiModuleSourceFilesIterator &DbiModuleSourceFilesIterator::
96operator+=(std::ptrdiff_t N) {
97 assert(!isEnd());
98
99 Filei += N;
100 assert(Filei <= Modules->getSourceFileCount(Modi));
101 setValue();
102 return *this;
103}
104
105DbiModuleSourceFilesIterator &DbiModuleSourceFilesIterator::
106operator-=(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
117void DbiModuleSourceFilesIterator::setValue() {
118 if (isEnd()) {
119 ThisValue = "";
120 return;
121 }
122
123 uint32_t Off = Modules->ModuleInitialFileIndex[Modi] + Filei;
124 auto ExpectedValue = Modules->getFileName(Index: Off);
125 if (!ExpectedValue) {
126 consumeError(Err: ExpectedValue.takeError());
127 Filei = Modules->getSourceFileCount(Modi);
128 } else
129 ThisValue = *ExpectedValue;
130}
131
132bool 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
147bool DbiModuleSourceFilesIterator::isUniversalEnd() const { return !Modules; }
148
149bool 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
162Error 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
172Error 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(Array&: Descriptors, Size: ModInfo.getLength()))
181 return EC;
182
183 return Error::success();
184}
185
186Error 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(Dest&: 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(Array&: ModuleIndices, NumItems: FileInfoHeader->NumModules))
200 return EC;
201 if (auto EC = FISR.readArray(Array&: ModFileCountArray, NumItems: 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(Array&: FileNameOffsets, NumItems: NumSourceFiles))
217 return EC;
218
219 if (auto EC = FISR.readStreamRef(Ref&: NamesBuffer))
220 return EC;
221
222 auto DescriptorIter = Descriptors.begin();
223 uint32_t NextFileIndex = 0;
224 ModuleInitialFileIndex.resize(new_size: FileInfoHeader->NumModules);
225 ModuleDescriptorOffsets.resize(new_size: 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
241uint32_t DbiModuleList::getModuleCount() const {
242 // Workaround to avoid the crash until upstream issue is fixed:
243 // https://github.com/llvm/llvm-project/issues/55214
244 return FileInfoHeader ? FileInfoHeader->NumModules : 0;
245}
246
247uint32_t DbiModuleList::getSourceFileCount() const {
248 return FileNameOffsets.size();
249}
250
251uint16_t DbiModuleList::getSourceFileCount(uint32_t Modi) const {
252 return ModFileCountArray[Modi];
253}
254
255DbiModuleDescriptor DbiModuleList::getModuleDescriptor(uint32_t Modi) const {
256 assert(Modi < getModuleCount());
257 uint32_t Offset = ModuleDescriptorOffsets[Modi];
258 auto Iter = Descriptors.at(Offset);
259 assert(Iter != Descriptors.end());
260 return *Iter;
261}
262
263iterator_range<DbiModuleSourceFilesIterator>
264DbiModuleList::source_files(uint32_t Modi) const {
265 return make_range<DbiModuleSourceFilesIterator>(
266 x: DbiModuleSourceFilesIterator(*this, Modi, 0),
267 y: DbiModuleSourceFilesIterator());
268}
269
270Expected<StringRef> DbiModuleList::getFileName(uint32_t Index) const {
271 BinaryStreamReader Names(NamesBuffer);
272 if (Index >= getSourceFileCount())
273 return make_error<RawError>(Args: raw_error_code::index_out_of_bounds);
274
275 uint32_t FileOffset = FileNameOffsets[Index];
276 Names.setOffset(FileOffset);
277 StringRef Name;
278 if (auto EC = Names.readCString(Dest&: Name))
279 return std::move(EC);
280 return Name;
281}
282