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(Modi: 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(Index: Off); |
126 | if (!ExpectedValue) { |
127 | consumeError(Err: 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(Array&: Descriptors, Size: 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(Dest&: 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(Array&: ModuleIndices, NumItems: FileInfoHeader->NumModules)) |
201 | return EC; |
202 | if (auto EC = FISR.readArray(Array&: ModFileCountArray, NumItems: 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(Array&: FileNameOffsets, NumItems: NumSourceFiles)) |
218 | return EC; |
219 | |
220 | if (auto EC = FISR.readStreamRef(Ref&: NamesBuffer)) |
221 | return EC; |
222 | |
223 | auto DescriptorIter = Descriptors.begin(); |
224 | uint32_t NextFileIndex = 0; |
225 | ModuleInitialFileIndex.resize(new_size: FileInfoHeader->NumModules); |
226 | ModuleDescriptorOffsets.resize(new_size: 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 | x: DbiModuleSourceFilesIterator(*this, Modi, 0), |
268 | y: 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>(Args: 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(Dest&: Name)) |
280 | return std::move(EC); |
281 | return Name; |
282 | } |
283 | |