1 | //===- DbiStream.cpp - PDB Dbi Stream (Stream 3) Access -------------------===// |
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/DbiStream.h" |
10 | #include "llvm/ADT/StringRef.h" |
11 | #include "llvm/DebugInfo/MSF/MappedBlockStream.h" |
12 | #include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" |
13 | #include "llvm/DebugInfo/PDB/Native/PDBFile.h" |
14 | #include "llvm/DebugInfo/PDB/Native/RawConstants.h" |
15 | #include "llvm/DebugInfo/PDB/Native/RawError.h" |
16 | #include "llvm/DebugInfo/PDB/Native/RawTypes.h" |
17 | #include "llvm/DebugInfo/PDB/PDBTypes.h" |
18 | #include "llvm/Object/COFF.h" |
19 | #include "llvm/Support/BinaryStreamArray.h" |
20 | #include "llvm/Support/BinaryStreamReader.h" |
21 | #include "llvm/Support/Error.h" |
22 | #include <cstddef> |
23 | #include <cstdint> |
24 | |
25 | using namespace llvm; |
26 | using namespace llvm::codeview; |
27 | using namespace llvm::msf; |
28 | using namespace llvm::pdb; |
29 | using namespace llvm::support; |
30 | |
31 | template <typename ContribType> |
32 | static Error loadSectionContribs(FixedStreamArray<ContribType> &Output, |
33 | BinaryStreamReader &Reader) { |
34 | if (Reader.bytesRemaining() % sizeof(ContribType) != 0) |
35 | return make_error<RawError>( |
36 | Args: raw_error_code::corrupt_file, |
37 | Args: "Invalid number of bytes of section contributions" ); |
38 | |
39 | uint32_t Count = Reader.bytesRemaining() / sizeof(ContribType); |
40 | if (auto EC = Reader.readArray(Output, Count)) |
41 | return EC; |
42 | return Error::success(); |
43 | } |
44 | |
45 | DbiStream::DbiStream(std::unique_ptr<BinaryStream> Stream) |
46 | : Stream(std::move(Stream)), Header(nullptr) {} |
47 | |
48 | DbiStream::~DbiStream() = default; |
49 | |
50 | Error DbiStream::reload(PDBFile *Pdb) { |
51 | BinaryStreamReader Reader(*Stream); |
52 | |
53 | if (Stream->getLength() < sizeof(DbiStreamHeader)) |
54 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
55 | Args: "DBI Stream does not contain a header." ); |
56 | if (auto EC = Reader.readObject(Dest&: Header)) |
57 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
58 | Args: "DBI Stream does not contain a header." ); |
59 | |
60 | if (Header->VersionSignature != -1) |
61 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
62 | Args: "Invalid DBI version signature." ); |
63 | |
64 | // Require at least version 7, which should be present in all PDBs |
65 | // produced in the last decade and allows us to avoid having to |
66 | // special case all kinds of complicated arcane formats. |
67 | if (Header->VersionHeader < PdbDbiV70) |
68 | return make_error<RawError>(Args: raw_error_code::feature_unsupported, |
69 | Args: "Unsupported DBI version." ); |
70 | |
71 | if (Stream->getLength() != |
72 | sizeof(DbiStreamHeader) + Header->ModiSubstreamSize + |
73 | Header->SecContrSubstreamSize + Header->SectionMapSize + |
74 | Header->FileInfoSize + Header->TypeServerSize + |
75 | Header->OptionalDbgHdrSize + Header->ECSubstreamSize) |
76 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
77 | Args: "DBI Length does not equal sum of substreams." ); |
78 | |
79 | // Only certain substreams are guaranteed to be aligned. Validate |
80 | // them here. |
81 | if (Header->ModiSubstreamSize % sizeof(uint32_t) != 0) |
82 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
83 | Args: "DBI MODI substream not aligned." ); |
84 | if (Header->SecContrSubstreamSize % sizeof(uint32_t) != 0) |
85 | return make_error<RawError>( |
86 | Args: raw_error_code::corrupt_file, |
87 | Args: "DBI section contribution substream not aligned." ); |
88 | if (Header->SectionMapSize % sizeof(uint32_t) != 0) |
89 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
90 | Args: "DBI section map substream not aligned." ); |
91 | if (Header->FileInfoSize % sizeof(uint32_t) != 0) |
92 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
93 | Args: "DBI file info substream not aligned." ); |
94 | if (Header->TypeServerSize % sizeof(uint32_t) != 0) |
95 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
96 | Args: "DBI type server substream not aligned." ); |
97 | |
98 | if (auto EC = Reader.readSubstream(Ref&: ModiSubstream, Length: Header->ModiSubstreamSize)) |
99 | return EC; |
100 | |
101 | if (auto EC = Reader.readSubstream(Ref&: SecContrSubstream, |
102 | Length: Header->SecContrSubstreamSize)) |
103 | return EC; |
104 | if (auto EC = Reader.readSubstream(Ref&: SecMapSubstream, Length: Header->SectionMapSize)) |
105 | return EC; |
106 | if (auto EC = Reader.readSubstream(Ref&: FileInfoSubstream, Length: Header->FileInfoSize)) |
107 | return EC; |
108 | if (auto EC = |
109 | Reader.readSubstream(Ref&: TypeServerMapSubstream, Length: Header->TypeServerSize)) |
110 | return EC; |
111 | if (auto EC = Reader.readSubstream(Ref&: ECSubstream, Length: Header->ECSubstreamSize)) |
112 | return EC; |
113 | if (auto EC = Reader.readArray( |
114 | Array&: DbgStreams, NumItems: Header->OptionalDbgHdrSize / sizeof(ulittle16_t))) |
115 | return EC; |
116 | |
117 | if (auto EC = Modules.initialize(ModInfo: ModiSubstream.StreamData, |
118 | FileInfo: FileInfoSubstream.StreamData)) |
119 | return EC; |
120 | |
121 | if (auto EC = initializeSectionContributionData()) |
122 | return EC; |
123 | if (auto EC = initializeSectionHeadersData(Pdb)) |
124 | return EC; |
125 | if (auto EC = initializeSectionMapData()) |
126 | return EC; |
127 | if (auto EC = initializeOldFpoRecords(Pdb)) |
128 | return EC; |
129 | if (auto EC = initializeNewFpoRecords(Pdb)) |
130 | return EC; |
131 | |
132 | if (Reader.bytesRemaining() > 0) |
133 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
134 | Args: "Found unexpected bytes in DBI Stream." ); |
135 | |
136 | if (!ECSubstream.empty()) { |
137 | BinaryStreamReader ECReader(ECSubstream.StreamData); |
138 | if (auto EC = ECNames.reload(Reader&: ECReader)) |
139 | return EC; |
140 | } |
141 | |
142 | return Error::success(); |
143 | } |
144 | |
145 | PdbRaw_DbiVer DbiStream::getDbiVersion() const { |
146 | uint32_t Value = Header->VersionHeader; |
147 | return static_cast<PdbRaw_DbiVer>(Value); |
148 | } |
149 | |
150 | uint32_t DbiStream::getAge() const { return Header->Age; } |
151 | |
152 | uint16_t DbiStream::getPublicSymbolStreamIndex() const { |
153 | return Header->PublicSymbolStreamIndex; |
154 | } |
155 | |
156 | uint16_t DbiStream::getGlobalSymbolStreamIndex() const { |
157 | return Header->GlobalSymbolStreamIndex; |
158 | } |
159 | |
160 | uint16_t DbiStream::getFlags() const { return Header->Flags; } |
161 | |
162 | bool DbiStream::isIncrementallyLinked() const { |
163 | return (Header->Flags & DbiFlags::FlagIncrementalMask) != 0; |
164 | } |
165 | |
166 | bool DbiStream::hasCTypes() const { |
167 | return (Header->Flags & DbiFlags::FlagHasCTypesMask) != 0; |
168 | } |
169 | |
170 | bool DbiStream::isStripped() const { |
171 | return (Header->Flags & DbiFlags::FlagStrippedMask) != 0; |
172 | } |
173 | |
174 | uint16_t DbiStream::getBuildNumber() const { return Header->BuildNumber; } |
175 | |
176 | uint16_t DbiStream::getBuildMajorVersion() const { |
177 | return (Header->BuildNumber & DbiBuildNo::BuildMajorMask) >> |
178 | DbiBuildNo::BuildMajorShift; |
179 | } |
180 | |
181 | uint16_t DbiStream::getBuildMinorVersion() const { |
182 | return (Header->BuildNumber & DbiBuildNo::BuildMinorMask) >> |
183 | DbiBuildNo::BuildMinorShift; |
184 | } |
185 | |
186 | uint16_t DbiStream::getPdbDllRbld() const { return Header->PdbDllRbld; } |
187 | |
188 | uint32_t DbiStream::getPdbDllVersion() const { return Header->PdbDllVersion; } |
189 | |
190 | uint32_t DbiStream::getSymRecordStreamIndex() const { |
191 | return Header->SymRecordStreamIndex; |
192 | } |
193 | |
194 | PDB_Machine DbiStream::getMachineType() const { |
195 | uint16_t Machine = Header->MachineType; |
196 | return static_cast<PDB_Machine>(Machine); |
197 | } |
198 | |
199 | FixedStreamArray<object::coff_section> DbiStream::() const { |
200 | return SectionHeaders; |
201 | } |
202 | |
203 | bool DbiStream::hasOldFpoRecords() const { return OldFpoStream != nullptr; } |
204 | |
205 | FixedStreamArray<object::FpoData> DbiStream::getOldFpoRecords() const { |
206 | return OldFpoRecords; |
207 | } |
208 | |
209 | bool DbiStream::hasNewFpoRecords() const { return NewFpoStream != nullptr; } |
210 | |
211 | const DebugFrameDataSubsectionRef &DbiStream::getNewFpoRecords() const { |
212 | return NewFpoRecords; |
213 | } |
214 | |
215 | const DbiModuleList &DbiStream::modules() const { return Modules; } |
216 | |
217 | FixedStreamArray<SecMapEntry> DbiStream::getSectionMap() const { |
218 | return SectionMap; |
219 | } |
220 | |
221 | void DbiStream::visitSectionContributions( |
222 | ISectionContribVisitor &Visitor) const { |
223 | if (!SectionContribs.empty()) { |
224 | assert(SectionContribVersion == DbiSecContribVer60); |
225 | for (auto &SC : SectionContribs) |
226 | Visitor.visit(C: SC); |
227 | } else if (!SectionContribs2.empty()) { |
228 | assert(SectionContribVersion == DbiSecContribV2); |
229 | for (auto &SC : SectionContribs2) |
230 | Visitor.visit(C: SC); |
231 | } |
232 | } |
233 | |
234 | Expected<StringRef> DbiStream::getECName(uint32_t NI) const { |
235 | return ECNames.getStringForID(ID: NI); |
236 | } |
237 | |
238 | Error DbiStream::initializeSectionContributionData() { |
239 | if (SecContrSubstream.empty()) |
240 | return Error::success(); |
241 | |
242 | BinaryStreamReader SCReader(SecContrSubstream.StreamData); |
243 | if (auto EC = SCReader.readEnum(Dest&: SectionContribVersion)) |
244 | return EC; |
245 | |
246 | if (SectionContribVersion == DbiSecContribVer60) |
247 | return loadSectionContribs<SectionContrib>(Output&: SectionContribs, Reader&: SCReader); |
248 | if (SectionContribVersion == DbiSecContribV2) |
249 | return loadSectionContribs<SectionContrib2>(Output&: SectionContribs2, Reader&: SCReader); |
250 | |
251 | return make_error<RawError>(Args: raw_error_code::feature_unsupported, |
252 | Args: "Unsupported DBI Section Contribution version" ); |
253 | } |
254 | |
255 | // Initializes this->SectionHeaders. |
256 | Error DbiStream::(PDBFile *Pdb) { |
257 | Expected<std::unique_ptr<msf::MappedBlockStream>> ExpectedStream = |
258 | createIndexedStreamForHeaderType(Pdb, Type: DbgHeaderType::SectionHdr); |
259 | if (auto EC = ExpectedStream.takeError()) |
260 | return EC; |
261 | |
262 | auto &SHS = *ExpectedStream; |
263 | if (!SHS) |
264 | return Error::success(); |
265 | |
266 | size_t StreamLen = SHS->getLength(); |
267 | if (StreamLen % sizeof(object::coff_section)) |
268 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
269 | Args: "Corrupted section header stream." ); |
270 | |
271 | size_t NumSections = StreamLen / sizeof(object::coff_section); |
272 | BinaryStreamReader Reader(*SHS); |
273 | if (auto EC = Reader.readArray(Array&: SectionHeaders, NumItems: NumSections)) |
274 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
275 | Args: "Could not read a bitmap." ); |
276 | |
277 | SectionHeaderStream = std::move(SHS); |
278 | return Error::success(); |
279 | } |
280 | |
281 | // Initializes this->Fpos. |
282 | Error DbiStream::initializeOldFpoRecords(PDBFile *Pdb) { |
283 | Expected<std::unique_ptr<msf::MappedBlockStream>> ExpectedStream = |
284 | createIndexedStreamForHeaderType(Pdb, Type: DbgHeaderType::FPO); |
285 | if (auto EC = ExpectedStream.takeError()) |
286 | return EC; |
287 | |
288 | auto &FS = *ExpectedStream; |
289 | if (!FS) |
290 | return Error::success(); |
291 | |
292 | size_t StreamLen = FS->getLength(); |
293 | if (StreamLen % sizeof(object::FpoData)) |
294 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
295 | Args: "Corrupted Old FPO stream." ); |
296 | |
297 | size_t NumRecords = StreamLen / sizeof(object::FpoData); |
298 | BinaryStreamReader Reader(*FS); |
299 | if (auto EC = Reader.readArray(Array&: OldFpoRecords, NumItems: NumRecords)) |
300 | return make_error<RawError>(Args: raw_error_code::corrupt_file, |
301 | Args: "Corrupted Old FPO stream." ); |
302 | OldFpoStream = std::move(FS); |
303 | return Error::success(); |
304 | } |
305 | |
306 | Error DbiStream::initializeNewFpoRecords(PDBFile *Pdb) { |
307 | Expected<std::unique_ptr<msf::MappedBlockStream>> ExpectedStream = |
308 | createIndexedStreamForHeaderType(Pdb, Type: DbgHeaderType::NewFPO); |
309 | if (auto EC = ExpectedStream.takeError()) |
310 | return EC; |
311 | |
312 | auto &FS = *ExpectedStream; |
313 | if (!FS) |
314 | return Error::success(); |
315 | |
316 | if (auto EC = NewFpoRecords.initialize(Stream: *FS)) |
317 | return EC; |
318 | |
319 | NewFpoStream = std::move(FS); |
320 | return Error::success(); |
321 | } |
322 | |
323 | Expected<std::unique_ptr<msf::MappedBlockStream>> |
324 | DbiStream::(PDBFile *Pdb, |
325 | DbgHeaderType Type) const { |
326 | if (!Pdb) |
327 | return nullptr; |
328 | |
329 | if (DbgStreams.empty()) |
330 | return nullptr; |
331 | |
332 | uint32_t StreamNum = getDebugStreamIndex(Type); |
333 | |
334 | // This means there is no such stream. |
335 | if (StreamNum == kInvalidStreamIndex) |
336 | return nullptr; |
337 | |
338 | return Pdb->safelyCreateIndexedStream(StreamIndex: StreamNum); |
339 | } |
340 | |
341 | BinarySubstreamRef DbiStream::getSectionContributionData() const { |
342 | return SecContrSubstream; |
343 | } |
344 | |
345 | BinarySubstreamRef DbiStream::getSecMapSubstreamData() const { |
346 | return SecMapSubstream; |
347 | } |
348 | |
349 | BinarySubstreamRef DbiStream::getModiSubstreamData() const { |
350 | return ModiSubstream; |
351 | } |
352 | |
353 | BinarySubstreamRef DbiStream::getFileInfoSubstreamData() const { |
354 | return FileInfoSubstream; |
355 | } |
356 | |
357 | BinarySubstreamRef DbiStream::getTypeServerMapSubstreamData() const { |
358 | return TypeServerMapSubstream; |
359 | } |
360 | |
361 | BinarySubstreamRef DbiStream::getECSubstreamData() const { return ECSubstream; } |
362 | |
363 | Error DbiStream::initializeSectionMapData() { |
364 | if (SecMapSubstream.empty()) |
365 | return Error::success(); |
366 | |
367 | BinaryStreamReader SMReader(SecMapSubstream.StreamData); |
368 | const SecMapHeader *; |
369 | if (auto EC = SMReader.readObject(Dest&: Header)) |
370 | return EC; |
371 | if (auto EC = SMReader.readArray(Array&: SectionMap, NumItems: Header->SecCount)) |
372 | return EC; |
373 | return Error::success(); |
374 | } |
375 | |
376 | uint32_t DbiStream::(DbgHeaderType Type) const { |
377 | uint16_t T = static_cast<uint16_t>(Type); |
378 | if (T >= DbgStreams.size()) |
379 | return kInvalidStreamIndex; |
380 | return DbgStreams[T]; |
381 | } |
382 | |