File: | tools/llvm-pdbutil/InputFile.cpp |
Warning: | line 81, column 13 The left operand of '!=' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===- InputFile.cpp ------------------------------------------ *- C++ --*-===// | |||
2 | // | |||
3 | // The LLVM Compiler Infrastructure | |||
4 | // | |||
5 | // This file is distributed under the University of Illinois Open Source | |||
6 | // License. See LICENSE.TXT for details. | |||
7 | // | |||
8 | //===----------------------------------------------------------------------===// | |||
9 | ||||
10 | #include "InputFile.h" | |||
11 | ||||
12 | #include "FormatUtil.h" | |||
13 | #include "LinePrinter.h" | |||
14 | ||||
15 | #include "llvm/BinaryFormat/Magic.h" | |||
16 | #include "llvm/DebugInfo/CodeView/CodeView.h" | |||
17 | #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" | |||
18 | #include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" | |||
19 | #include "llvm/DebugInfo/PDB/Native/DbiStream.h" | |||
20 | #include "llvm/DebugInfo/PDB/Native/NativeSession.h" | |||
21 | #include "llvm/DebugInfo/PDB/Native/PDBFile.h" | |||
22 | #include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" | |||
23 | #include "llvm/DebugInfo/PDB/Native/RawError.h" | |||
24 | #include "llvm/DebugInfo/PDB/Native/TpiStream.h" | |||
25 | #include "llvm/DebugInfo/PDB/PDB.h" | |||
26 | #include "llvm/Object/COFF.h" | |||
27 | #include "llvm/Support/FileSystem.h" | |||
28 | #include "llvm/Support/FormatVariadic.h" | |||
29 | ||||
30 | using namespace llvm; | |||
31 | using namespace llvm::codeview; | |||
32 | using namespace llvm::object; | |||
33 | using namespace llvm::pdb; | |||
34 | ||||
35 | InputFile::InputFile() {} | |||
36 | InputFile::~InputFile() {} | |||
37 | ||||
38 | static Expected<ModuleDebugStreamRef> | |||
39 | getModuleDebugStream(PDBFile &File, StringRef &ModuleName, uint32_t Index) { | |||
40 | ExitOnError Err("Unexpected error: "); | |||
41 | ||||
42 | auto &Dbi = Err(File.getPDBDbiStream()); | |||
43 | const auto &Modules = Dbi.modules(); | |||
44 | auto Modi = Modules.getModuleDescriptor(Index); | |||
45 | ||||
46 | ModuleName = Modi.getModuleName(); | |||
47 | ||||
48 | uint16_t ModiStream = Modi.getModuleStreamIndex(); | |||
49 | if (ModiStream == kInvalidStreamIndex) | |||
50 | return make_error<RawError>(raw_error_code::no_stream, | |||
51 | "Module stream not present"); | |||
52 | ||||
53 | auto ModStreamData = File.createIndexedStream(ModiStream); | |||
54 | ||||
55 | ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); | |||
56 | if (auto EC = ModS.reload()) | |||
57 | return make_error<RawError>(raw_error_code::corrupt_file, | |||
58 | "Invalid module stream"); | |||
59 | ||||
60 | return std::move(ModS); | |||
61 | } | |||
62 | ||||
63 | static inline bool isCodeViewDebugSubsection(object::SectionRef Section, | |||
64 | StringRef Name, | |||
65 | BinaryStreamReader &Reader) { | |||
66 | StringRef SectionName, Contents; | |||
67 | if (Section.getName(SectionName)) | |||
68 | return false; | |||
69 | ||||
70 | if (SectionName != Name) | |||
71 | return false; | |||
72 | ||||
73 | if (Section.getContents(Contents)) | |||
74 | return false; | |||
75 | ||||
76 | Reader = BinaryStreamReader(Contents, support::little); | |||
77 | uint32_t Magic; | |||
78 | if (Reader.bytesRemaining() < sizeof(uint32_t)) | |||
79 | return false; | |||
80 | cantFail(Reader.readInteger(Magic)); | |||
81 | if (Magic != COFF::DEBUG_SECTION_MAGIC) | |||
| ||||
82 | return false; | |||
83 | return true; | |||
84 | } | |||
85 | ||||
86 | static inline bool isDebugSSection(object::SectionRef Section, | |||
87 | DebugSubsectionArray &Subsections) { | |||
88 | BinaryStreamReader Reader; | |||
89 | if (!isCodeViewDebugSubsection(Section, ".debug$S", Reader)) | |||
90 | return false; | |||
91 | ||||
92 | cantFail(Reader.readArray(Subsections, Reader.bytesRemaining())); | |||
93 | return true; | |||
94 | } | |||
95 | ||||
96 | static bool isDebugTSection(SectionRef Section, CVTypeArray &Types) { | |||
97 | BinaryStreamReader Reader; | |||
98 | if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader) && | |||
99 | !isCodeViewDebugSubsection(Section, ".debug$P", Reader)) | |||
100 | return false; | |||
101 | cantFail(Reader.readArray(Types, Reader.bytesRemaining())); | |||
102 | return true; | |||
103 | } | |||
104 | ||||
105 | static std::string formatChecksumKind(FileChecksumKind Kind) { | |||
106 | switch (Kind) { | |||
107 | RETURN_CASE(FileChecksumKind, None, "None")case FileChecksumKind::None: return "None";; | |||
108 | RETURN_CASE(FileChecksumKind, MD5, "MD5")case FileChecksumKind::MD5: return "MD5";; | |||
109 | RETURN_CASE(FileChecksumKind, SHA1, "SHA-1")case FileChecksumKind::SHA1: return "SHA-1";; | |||
110 | RETURN_CASE(FileChecksumKind, SHA256, "SHA-256")case FileChecksumKind::SHA256: return "SHA-256";; | |||
111 | } | |||
112 | return formatUnknownEnum(Kind); | |||
113 | } | |||
114 | ||||
115 | static const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) { | |||
116 | return cantFail(File.getStringTable()).getStringTable(); | |||
117 | } | |||
118 | ||||
119 | template <typename... Args> | |||
120 | static void formatInternal(LinePrinter &Printer, bool Append, Args &&... args) { | |||
121 | if (Append) | |||
122 | Printer.format(std::forward<Args>(args)...); | |||
123 | else | |||
124 | Printer.formatLine(std::forward<Args>(args)...); | |||
125 | } | |||
126 | ||||
127 | SymbolGroup::SymbolGroup(InputFile *File, uint32_t GroupIndex) : File(File) { | |||
128 | if (!File) | |||
129 | return; | |||
130 | ||||
131 | if (File->isPdb()) | |||
132 | initializeForPdb(GroupIndex); | |||
133 | else { | |||
134 | Name = ".debug$S"; | |||
135 | uint32_t I = 0; | |||
136 | for (const auto &S : File->obj().sections()) { | |||
137 | DebugSubsectionArray SS; | |||
138 | if (!isDebugSSection(S, SS)) | |||
139 | continue; | |||
140 | ||||
141 | if (!SC.hasChecksums() || !SC.hasStrings()) | |||
142 | SC.initialize(SS); | |||
143 | ||||
144 | if (I == GroupIndex) | |||
145 | Subsections = SS; | |||
146 | ||||
147 | if (SC.hasChecksums() && SC.hasStrings()) | |||
148 | break; | |||
149 | } | |||
150 | rebuildChecksumMap(); | |||
151 | } | |||
152 | } | |||
153 | ||||
154 | StringRef SymbolGroup::name() const { return Name; } | |||
155 | ||||
156 | void SymbolGroup::updateDebugS(const codeview::DebugSubsectionArray &SS) { | |||
157 | Subsections = SS; | |||
158 | } | |||
159 | ||||
160 | void SymbolGroup::updatePdbModi(uint32_t Modi) { initializeForPdb(Modi); } | |||
161 | ||||
162 | void SymbolGroup::initializeForPdb(uint32_t Modi) { | |||
163 | assert(File && File->isPdb())(static_cast <bool> (File && File->isPdb()) ? void (0) : __assert_fail ("File && File->isPdb()" , "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 163, __extension__ __PRETTY_FUNCTION__)); | |||
164 | ||||
165 | // PDB always uses the same string table, but each module has its own | |||
166 | // checksums. So we only set the strings if they're not already set. | |||
167 | if (!SC.hasStrings()) | |||
168 | SC.setStrings(extractStringTable(File->pdb())); | |||
169 | ||||
170 | SC.resetChecksums(); | |||
171 | auto MDS = getModuleDebugStream(File->pdb(), Name, Modi); | |||
172 | if (!MDS) { | |||
173 | consumeError(MDS.takeError()); | |||
174 | return; | |||
175 | } | |||
176 | ||||
177 | DebugStream = std::make_shared<ModuleDebugStreamRef>(std::move(*MDS)); | |||
178 | Subsections = DebugStream->getSubsectionsArray(); | |||
179 | SC.initialize(Subsections); | |||
180 | rebuildChecksumMap(); | |||
181 | } | |||
182 | ||||
183 | void SymbolGroup::rebuildChecksumMap() { | |||
184 | if (!SC.hasChecksums()) | |||
185 | return; | |||
186 | ||||
187 | for (const auto &Entry : SC.checksums()) { | |||
188 | auto S = SC.strings().getString(Entry.FileNameOffset); | |||
189 | if (!S) | |||
190 | continue; | |||
191 | ChecksumsByFile[*S] = Entry; | |||
192 | } | |||
193 | } | |||
194 | ||||
195 | const ModuleDebugStreamRef &SymbolGroup::getPdbModuleStream() const { | |||
196 | assert(File && File->isPdb() && DebugStream)(static_cast <bool> (File && File->isPdb() && DebugStream) ? void (0) : __assert_fail ("File && File->isPdb() && DebugStream" , "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 196, __extension__ __PRETTY_FUNCTION__)); | |||
197 | return *DebugStream; | |||
198 | } | |||
199 | ||||
200 | Expected<StringRef> SymbolGroup::getNameFromStringTable(uint32_t Offset) const { | |||
201 | return SC.strings().getString(Offset); | |||
202 | } | |||
203 | ||||
204 | void SymbolGroup::formatFromFileName(LinePrinter &Printer, StringRef File, | |||
205 | bool Append) const { | |||
206 | auto FC = ChecksumsByFile.find(File); | |||
207 | if (FC == ChecksumsByFile.end()) { | |||
208 | formatInternal(Printer, Append, "- (no checksum) {0}", File); | |||
209 | return; | |||
210 | } | |||
211 | ||||
212 | formatInternal(Printer, Append, "- ({0}: {1}) {2}", | |||
213 | formatChecksumKind(FC->getValue().Kind), | |||
214 | toHex(FC->getValue().Checksum), File); | |||
215 | } | |||
216 | ||||
217 | void SymbolGroup::formatFromChecksumsOffset(LinePrinter &Printer, | |||
218 | uint32_t Offset, | |||
219 | bool Append) const { | |||
220 | if (!SC.hasChecksums()) { | |||
221 | formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); | |||
222 | return; | |||
223 | } | |||
224 | ||||
225 | auto Iter = SC.checksums().getArray().at(Offset); | |||
226 | if (Iter == SC.checksums().getArray().end()) { | |||
227 | formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); | |||
228 | return; | |||
229 | } | |||
230 | ||||
231 | uint32_t FO = Iter->FileNameOffset; | |||
232 | auto ExpectedFile = getNameFromStringTable(FO); | |||
233 | if (!ExpectedFile) { | |||
234 | formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); | |||
235 | consumeError(ExpectedFile.takeError()); | |||
236 | return; | |||
237 | } | |||
238 | if (Iter->Kind == FileChecksumKind::None) { | |||
239 | formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile); | |||
240 | } else { | |||
241 | formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile, | |||
242 | formatChecksumKind(Iter->Kind), toHex(Iter->Checksum)); | |||
243 | } | |||
244 | } | |||
245 | ||||
246 | Expected<InputFile> InputFile::open(StringRef Path, bool AllowUnknownFile) { | |||
247 | InputFile IF; | |||
248 | if (!llvm::sys::fs::exists(Path)) | |||
249 | return make_error<StringError>(formatv("File {0} not found", Path), | |||
250 | inconvertibleErrorCode()); | |||
251 | ||||
252 | file_magic Magic; | |||
253 | if (auto EC = identify_magic(Path, Magic)) | |||
254 | return make_error<StringError>( | |||
255 | formatv("Unable to identify file type for file {0}", Path), EC); | |||
256 | ||||
257 | if (Magic == file_magic::coff_object) { | |||
258 | Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Path); | |||
259 | if (!BinaryOrErr) | |||
260 | return BinaryOrErr.takeError(); | |||
261 | ||||
262 | IF.CoffObject = std::move(*BinaryOrErr); | |||
263 | IF.PdbOrObj = llvm::cast<COFFObjectFile>(IF.CoffObject.getBinary()); | |||
264 | return std::move(IF); | |||
265 | } | |||
266 | ||||
267 | if (Magic == file_magic::pdb) { | |||
268 | std::unique_ptr<IPDBSession> Session; | |||
269 | if (auto Err = loadDataForPDB(PDB_ReaderType::Native, Path, Session)) | |||
270 | return std::move(Err); | |||
271 | ||||
272 | IF.PdbSession.reset(static_cast<NativeSession *>(Session.release())); | |||
273 | IF.PdbOrObj = &IF.PdbSession->getPDBFile(); | |||
274 | ||||
275 | return std::move(IF); | |||
276 | } | |||
277 | ||||
278 | if (!AllowUnknownFile) | |||
279 | return make_error<StringError>( | |||
280 | formatv("File {0} is not a supported file type", Path), | |||
281 | inconvertibleErrorCode()); | |||
282 | ||||
283 | auto Result = MemoryBuffer::getFile(Path, -1LL, false); | |||
284 | if (!Result) | |||
285 | return make_error<StringError>( | |||
286 | formatv("File {0} could not be opened", Path), Result.getError()); | |||
287 | ||||
288 | IF.UnknownFile = std::move(*Result); | |||
289 | IF.PdbOrObj = IF.UnknownFile.get(); | |||
290 | return std::move(IF); | |||
291 | } | |||
292 | ||||
293 | PDBFile &InputFile::pdb() { | |||
294 | assert(isPdb())(static_cast <bool> (isPdb()) ? void (0) : __assert_fail ("isPdb()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 294, __extension__ __PRETTY_FUNCTION__)); | |||
295 | return *PdbOrObj.get<PDBFile *>(); | |||
296 | } | |||
297 | ||||
298 | const PDBFile &InputFile::pdb() const { | |||
299 | assert(isPdb())(static_cast <bool> (isPdb()) ? void (0) : __assert_fail ("isPdb()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 299, __extension__ __PRETTY_FUNCTION__)); | |||
300 | return *PdbOrObj.get<PDBFile *>(); | |||
301 | } | |||
302 | ||||
303 | object::COFFObjectFile &InputFile::obj() { | |||
304 | assert(isObj())(static_cast <bool> (isObj()) ? void (0) : __assert_fail ("isObj()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 304, __extension__ __PRETTY_FUNCTION__)); | |||
305 | return *PdbOrObj.get<object::COFFObjectFile *>(); | |||
306 | } | |||
307 | ||||
308 | const object::COFFObjectFile &InputFile::obj() const { | |||
309 | assert(isObj())(static_cast <bool> (isObj()) ? void (0) : __assert_fail ("isObj()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 309, __extension__ __PRETTY_FUNCTION__)); | |||
310 | return *PdbOrObj.get<object::COFFObjectFile *>(); | |||
311 | } | |||
312 | ||||
313 | MemoryBuffer &InputFile::unknown() { | |||
314 | assert(isUnknown())(static_cast <bool> (isUnknown()) ? void (0) : __assert_fail ("isUnknown()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 314, __extension__ __PRETTY_FUNCTION__)); | |||
315 | return *PdbOrObj.get<MemoryBuffer *>(); | |||
316 | } | |||
317 | ||||
318 | const MemoryBuffer &InputFile::unknown() const { | |||
319 | assert(isUnknown())(static_cast <bool> (isUnknown()) ? void (0) : __assert_fail ("isUnknown()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 319, __extension__ __PRETTY_FUNCTION__)); | |||
320 | return *PdbOrObj.get<MemoryBuffer *>(); | |||
321 | } | |||
322 | ||||
323 | StringRef InputFile::getFilePath() const { | |||
324 | if (isPdb()) | |||
325 | return pdb().getFilePath(); | |||
326 | if (isObj()) | |||
327 | return obj().getFileName(); | |||
328 | assert(isUnknown())(static_cast <bool> (isUnknown()) ? void (0) : __assert_fail ("isUnknown()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 328, __extension__ __PRETTY_FUNCTION__)); | |||
329 | return unknown().getBufferIdentifier(); | |||
330 | } | |||
331 | ||||
332 | bool InputFile::hasTypes() const { | |||
333 | if (isPdb()) | |||
| ||||
334 | return pdb().hasPDBTpiStream(); | |||
335 | ||||
336 | for (const auto &Section : obj().sections()) { | |||
337 | CVTypeArray Types; | |||
338 | if (isDebugTSection(Section, Types)) | |||
339 | return true; | |||
340 | } | |||
341 | return false; | |||
342 | } | |||
343 | ||||
344 | bool InputFile::hasIds() const { | |||
345 | if (isObj()) | |||
346 | return false; | |||
347 | return pdb().hasPDBIpiStream(); | |||
348 | } | |||
349 | ||||
350 | bool InputFile::isPdb() const { return PdbOrObj.is<PDBFile *>(); } | |||
351 | ||||
352 | bool InputFile::isObj() const { | |||
353 | return PdbOrObj.is<object::COFFObjectFile *>(); | |||
354 | } | |||
355 | ||||
356 | bool InputFile::isUnknown() const { return PdbOrObj.is<MemoryBuffer *>(); } | |||
357 | ||||
358 | codeview::LazyRandomTypeCollection & | |||
359 | InputFile::getOrCreateTypeCollection(TypeCollectionKind Kind) { | |||
360 | if (Types && Kind == kTypes) | |||
361 | return *Types; | |||
362 | if (Ids && Kind == kIds) | |||
363 | return *Ids; | |||
364 | ||||
365 | if (Kind == kIds) { | |||
366 | assert(isPdb() && pdb().hasPDBIpiStream())(static_cast <bool> (isPdb() && pdb().hasPDBIpiStream ()) ? void (0) : __assert_fail ("isPdb() && pdb().hasPDBIpiStream()" , "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 366, __extension__ __PRETTY_FUNCTION__)); | |||
367 | } | |||
368 | ||||
369 | // If the collection was already initialized, we should have just returned it | |||
370 | // in step 1. | |||
371 | if (isPdb()) { | |||
372 | TypeCollectionPtr &Collection = (Kind == kIds) ? Ids : Types; | |||
373 | auto &Stream = cantFail((Kind == kIds) ? pdb().getPDBIpiStream() | |||
374 | : pdb().getPDBTpiStream()); | |||
375 | ||||
376 | auto &Array = Stream.typeArray(); | |||
377 | uint32_t Count = Stream.getNumTypeRecords(); | |||
378 | auto Offsets = Stream.getTypeIndexOffsets(); | |||
379 | Collection = | |||
380 | llvm::make_unique<LazyRandomTypeCollection>(Array, Count, Offsets); | |||
381 | return *Collection; | |||
382 | } | |||
383 | ||||
384 | assert(isObj())(static_cast <bool> (isObj()) ? void (0) : __assert_fail ("isObj()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 384, __extension__ __PRETTY_FUNCTION__)); | |||
385 | assert(Kind == kTypes)(static_cast <bool> (Kind == kTypes) ? void (0) : __assert_fail ("Kind == kTypes", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 385, __extension__ __PRETTY_FUNCTION__)); | |||
386 | assert(!Types)(static_cast <bool> (!Types) ? void (0) : __assert_fail ("!Types", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 386, __extension__ __PRETTY_FUNCTION__)); | |||
387 | ||||
388 | for (const auto &Section : obj().sections()) { | |||
389 | CVTypeArray Records; | |||
390 | if (!isDebugTSection(Section, Records)) | |||
391 | continue; | |||
392 | ||||
393 | Types = llvm::make_unique<LazyRandomTypeCollection>(Records, 100); | |||
394 | return *Types; | |||
395 | } | |||
396 | ||||
397 | Types = llvm::make_unique<LazyRandomTypeCollection>(100); | |||
398 | return *Types; | |||
399 | } | |||
400 | ||||
401 | codeview::LazyRandomTypeCollection &InputFile::types() { | |||
402 | return getOrCreateTypeCollection(kTypes); | |||
403 | } | |||
404 | ||||
405 | codeview::LazyRandomTypeCollection &InputFile::ids() { | |||
406 | // Object files have only one type stream that contains both types and ids. | |||
407 | // Similarly, some PDBs don't contain an IPI stream, and for those both types | |||
408 | // and IDs are in the same stream. | |||
409 | if (isObj() || !pdb().hasPDBIpiStream()) | |||
410 | return types(); | |||
411 | ||||
412 | return getOrCreateTypeCollection(kIds); | |||
413 | } | |||
414 | ||||
415 | iterator_range<SymbolGroupIterator> InputFile::symbol_groups() { | |||
416 | return make_range<SymbolGroupIterator>(symbol_groups_begin(), | |||
417 | symbol_groups_end()); | |||
418 | } | |||
419 | ||||
420 | SymbolGroupIterator InputFile::symbol_groups_begin() { | |||
421 | return SymbolGroupIterator(*this); | |||
422 | } | |||
423 | ||||
424 | SymbolGroupIterator InputFile::symbol_groups_end() { | |||
425 | return SymbolGroupIterator(); | |||
426 | } | |||
427 | ||||
428 | SymbolGroupIterator::SymbolGroupIterator() : Value(nullptr) {} | |||
429 | ||||
430 | SymbolGroupIterator::SymbolGroupIterator(InputFile &File) : Value(&File) { | |||
431 | if (File.isObj()) { | |||
432 | SectionIter = File.obj().section_begin(); | |||
433 | scanToNextDebugS(); | |||
434 | } | |||
435 | } | |||
436 | ||||
437 | bool SymbolGroupIterator::operator==(const SymbolGroupIterator &R) const { | |||
438 | bool E = isEnd(); | |||
439 | bool RE = R.isEnd(); | |||
440 | if (E || RE) | |||
441 | return E == RE; | |||
442 | ||||
443 | if (Value.File != R.Value.File) | |||
444 | return false; | |||
445 | return Index == R.Index; | |||
446 | } | |||
447 | ||||
448 | const SymbolGroup &SymbolGroupIterator::operator*() const { | |||
449 | assert(!isEnd())(static_cast <bool> (!isEnd()) ? void (0) : __assert_fail ("!isEnd()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 449, __extension__ __PRETTY_FUNCTION__)); | |||
450 | return Value; | |||
451 | } | |||
452 | SymbolGroup &SymbolGroupIterator::operator*() { | |||
453 | assert(!isEnd())(static_cast <bool> (!isEnd()) ? void (0) : __assert_fail ("!isEnd()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 453, __extension__ __PRETTY_FUNCTION__)); | |||
454 | return Value; | |||
455 | } | |||
456 | ||||
457 | SymbolGroupIterator &SymbolGroupIterator::operator++() { | |||
458 | assert(Value.File && !isEnd())(static_cast <bool> (Value.File && !isEnd()) ? void (0) : __assert_fail ("Value.File && !isEnd()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 458, __extension__ __PRETTY_FUNCTION__)); | |||
459 | ++Index; | |||
460 | if (isEnd()) | |||
461 | return *this; | |||
462 | ||||
463 | if (Value.File->isPdb()) { | |||
464 | Value.updatePdbModi(Index); | |||
465 | return *this; | |||
466 | } | |||
467 | ||||
468 | scanToNextDebugS(); | |||
469 | return *this; | |||
470 | } | |||
471 | ||||
472 | void SymbolGroupIterator::scanToNextDebugS() { | |||
473 | assert(SectionIter.hasValue())(static_cast <bool> (SectionIter.hasValue()) ? void (0) : __assert_fail ("SectionIter.hasValue()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 473, __extension__ __PRETTY_FUNCTION__)); | |||
474 | auto End = Value.File->obj().section_end(); | |||
475 | auto &Iter = *SectionIter; | |||
476 | assert(!isEnd())(static_cast <bool> (!isEnd()) ? void (0) : __assert_fail ("!isEnd()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 476, __extension__ __PRETTY_FUNCTION__)); | |||
477 | ||||
478 | while (++Iter != End) { | |||
479 | DebugSubsectionArray SS; | |||
480 | SectionRef SR = *Iter; | |||
481 | if (!isDebugSSection(SR, SS)) | |||
482 | continue; | |||
483 | ||||
484 | Value.updateDebugS(SS); | |||
485 | return; | |||
486 | } | |||
487 | } | |||
488 | ||||
489 | bool SymbolGroupIterator::isEnd() const { | |||
490 | if (!Value.File) | |||
491 | return true; | |||
492 | if (Value.File->isPdb()) { | |||
493 | auto &Dbi = cantFail(Value.File->pdb().getPDBDbiStream()); | |||
494 | uint32_t Count = Dbi.modules().getModuleCount(); | |||
495 | assert(Index <= Count)(static_cast <bool> (Index <= Count) ? void (0) : __assert_fail ("Index <= Count", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 495, __extension__ __PRETTY_FUNCTION__)); | |||
496 | return Index == Count; | |||
497 | } | |||
498 | ||||
499 | assert(SectionIter.hasValue())(static_cast <bool> (SectionIter.hasValue()) ? void (0) : __assert_fail ("SectionIter.hasValue()", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-pdbutil/InputFile.cpp" , 499, __extension__ __PRETTY_FUNCTION__)); | |||
500 | return *SectionIter == Value.File->obj().section_end(); | |||
501 | } |
1 | //===- BinaryStreamReader.h - Reads objects from a binary stream *- C++ -*-===// |
2 | // |
3 | // The LLVM Compiler Infrastructure |
4 | // |
5 | // This file is distributed under the University of Illinois Open Source |
6 | // License. See LICENSE.TXT for details. |
7 | // |
8 | //===----------------------------------------------------------------------===// |
9 | |
10 | #ifndef LLVM_SUPPORT_BINARYSTREAMREADER_H |
11 | #define LLVM_SUPPORT_BINARYSTREAMREADER_H |
12 | |
13 | #include "llvm/ADT/ArrayRef.h" |
14 | #include "llvm/ADT/STLExtras.h" |
15 | #include "llvm/Support/BinaryStreamArray.h" |
16 | #include "llvm/Support/BinaryStreamRef.h" |
17 | #include "llvm/Support/ConvertUTF.h" |
18 | #include "llvm/Support/Endian.h" |
19 | #include "llvm/Support/Error.h" |
20 | #include "llvm/Support/type_traits.h" |
21 | |
22 | #include <string> |
23 | #include <type_traits> |
24 | |
25 | namespace llvm { |
26 | |
27 | /// \brief Provides read only access to a subclass of `BinaryStream`. Provides |
28 | /// bounds checking and helpers for writing certain common data types such as |
29 | /// null-terminated strings, integers in various flavors of endianness, etc. |
30 | /// Can be subclassed to provide reading of custom datatypes, although no |
31 | /// are overridable. |
32 | class BinaryStreamReader { |
33 | public: |
34 | BinaryStreamReader() = default; |
35 | explicit BinaryStreamReader(BinaryStreamRef Ref); |
36 | explicit BinaryStreamReader(BinaryStream &Stream); |
37 | explicit BinaryStreamReader(ArrayRef<uint8_t> Data, |
38 | llvm::support::endianness Endian); |
39 | explicit BinaryStreamReader(StringRef Data, llvm::support::endianness Endian); |
40 | |
41 | BinaryStreamReader(const BinaryStreamReader &Other) |
42 | : Stream(Other.Stream), Offset(Other.Offset) {} |
43 | |
44 | BinaryStreamReader &operator=(const BinaryStreamReader &Other) { |
45 | Stream = Other.Stream; |
46 | Offset = Other.Offset; |
47 | return *this; |
48 | } |
49 | |
50 | virtual ~BinaryStreamReader() {} |
51 | |
52 | /// Read as much as possible from the underlying string at the current offset |
53 | /// without invoking a copy, and set \p Buffer to the resulting data slice. |
54 | /// Updates the stream's offset to point after the newly read data. |
55 | /// |
56 | /// \returns a success error code if the data was successfully read, otherwise |
57 | /// returns an appropriate error code. |
58 | Error readLongestContiguousChunk(ArrayRef<uint8_t> &Buffer); |
59 | |
60 | /// Read \p Size bytes from the underlying stream at the current offset and |
61 | /// and set \p Buffer to the resulting data slice. Whether a copy occurs |
62 | /// depends on the implementation of the underlying stream. Updates the |
63 | /// stream's offset to point after the newly read data. |
64 | /// |
65 | /// \returns a success error code if the data was successfully read, otherwise |
66 | /// returns an appropriate error code. |
67 | Error readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size); |
68 | |
69 | /// Read an integer of the specified endianness into \p Dest and update the |
70 | /// stream's offset. The data is always copied from the stream's underlying |
71 | /// buffer into \p Dest. Updates the stream's offset to point after the newly |
72 | /// read data. |
73 | /// |
74 | /// \returns a success error code if the data was successfully read, otherwise |
75 | /// returns an appropriate error code. |
76 | template <typename T> Error readInteger(T &Dest) { |
77 | static_assert(std::is_integral<T>::value, |
78 | "Cannot call readInteger with non-integral value!"); |
79 | |
80 | ArrayRef<uint8_t> Bytes; |
81 | if (auto EC = readBytes(Bytes, sizeof(T))) |
82 | return EC; |
83 | |
84 | Dest = llvm::support::endian::read<T, llvm::support::unaligned>( |
85 | Bytes.data(), Stream.getEndian()); |
86 | return Error::success(); |
87 | } |
88 | |
89 | /// Similar to readInteger. |
90 | template <typename T> Error readEnum(T &Dest) { |
91 | static_assert(std::is_enum<T>::value, |
92 | "Cannot call readEnum with non-enum value!"); |
93 | typename std::underlying_type<T>::type N; |
94 | if (auto EC = readInteger(N)) |
95 | return EC; |
96 | Dest = static_cast<T>(N); |
97 | return Error::success(); |
98 | } |
99 | |
100 | /// Read a null terminated string from \p Dest. Whether a copy occurs depends |
101 | /// on the implementation of the underlying stream. Updates the stream's |
102 | /// offset to point after the newly read data. |
103 | /// |
104 | /// \returns a success error code if the data was successfully read, otherwise |
105 | /// returns an appropriate error code. |
106 | Error readCString(StringRef &Dest); |
107 | |
108 | /// Similar to readCString, however read a null-terminated UTF16 string |
109 | /// instead. |
110 | /// |
111 | /// \returns a success error code if the data was successfully read, otherwise |
112 | /// returns an appropriate error code. |
113 | Error readWideString(ArrayRef<UTF16> &Dest); |
114 | |
115 | /// Read a \p Length byte string into \p Dest. Whether a copy occurs depends |
116 | /// on the implementation of the underlying stream. Updates the stream's |
117 | /// offset to point after the newly read data. |
118 | /// |
119 | /// \returns a success error code if the data was successfully read, otherwise |
120 | /// returns an appropriate error code. |
121 | Error readFixedString(StringRef &Dest, uint32_t Length); |
122 | |
123 | /// Read the entire remainder of the underlying stream into \p Ref. This is |
124 | /// equivalent to calling getUnderlyingStream().slice(Offset). Updates the |
125 | /// stream's offset to point to the end of the stream. Never causes a copy. |
126 | /// |
127 | /// \returns a success error code if the data was successfully read, otherwise |
128 | /// returns an appropriate error code. |
129 | Error readStreamRef(BinaryStreamRef &Ref); |
130 | |
131 | /// Read \p Length bytes from the underlying stream into \p Ref. This is |
132 | /// equivalent to calling getUnderlyingStream().slice(Offset, Length). |
133 | /// Updates the stream's offset to point after the newly read object. Never |
134 | /// causes a copy. |
135 | /// |
136 | /// \returns a success error code if the data was successfully read, otherwise |
137 | /// returns an appropriate error code. |
138 | Error readStreamRef(BinaryStreamRef &Ref, uint32_t Length); |
139 | |
140 | /// Read \p Length bytes from the underlying stream into \p Stream. This is |
141 | /// equivalent to calling getUnderlyingStream().slice(Offset, Length). |
142 | /// Updates the stream's offset to point after the newly read object. Never |
143 | /// causes a copy. |
144 | /// |
145 | /// \returns a success error code if the data was successfully read, otherwise |
146 | /// returns an appropriate error code. |
147 | Error readSubstream(BinarySubstreamRef &Stream, uint32_t Size); |
148 | |
149 | /// Get a pointer to an object of type T from the underlying stream, as if by |
150 | /// memcpy, and store the result into \p Dest. It is up to the caller to |
151 | /// ensure that objects of type T can be safely treated in this manner. |
152 | /// Updates the stream's offset to point after the newly read object. Whether |
153 | /// a copy occurs depends upon the implementation of the underlying |
154 | /// stream. |
155 | /// |
156 | /// \returns a success error code if the data was successfully read, otherwise |
157 | /// returns an appropriate error code. |
158 | template <typename T> Error readObject(const T *&Dest) { |
159 | ArrayRef<uint8_t> Buffer; |
160 | if (auto EC = readBytes(Buffer, sizeof(T))) |
161 | return EC; |
162 | Dest = reinterpret_cast<const T *>(Buffer.data()); |
163 | return Error::success(); |
164 | } |
165 | |
166 | /// Get a reference to a \p NumElements element array of objects of type T |
167 | /// from the underlying stream as if by memcpy, and store the resulting array |
168 | /// slice into \p array. It is up to the caller to ensure that objects of |
169 | /// type T can be safely treated in this manner. Updates the stream's offset |
170 | /// to point after the newly read object. Whether a copy occurs depends upon |
171 | /// the implementation of the underlying stream. |
172 | /// |
173 | /// \returns a success error code if the data was successfully read, otherwise |
174 | /// returns an appropriate error code. |
175 | template <typename T> |
176 | Error readArray(ArrayRef<T> &Array, uint32_t NumElements) { |
177 | ArrayRef<uint8_t> Bytes; |
178 | if (NumElements == 0) { |
179 | Array = ArrayRef<T>(); |
180 | return Error::success(); |
181 | } |
182 | |
183 | if (NumElements > UINT32_MAX(4294967295U) / sizeof(T)) |
184 | return make_error<BinaryStreamError>( |
185 | stream_error_code::invalid_array_size); |
186 | |
187 | if (auto EC = readBytes(Bytes, NumElements * sizeof(T))) |
188 | return EC; |
189 | |
190 | assert(alignmentAdjustment(Bytes.data(), alignof(T)) == 0 &&(static_cast <bool> (alignmentAdjustment(Bytes.data(), alignof (T)) == 0 && "Reading at invalid alignment!") ? void ( 0) : __assert_fail ("alignmentAdjustment(Bytes.data(), alignof(T)) == 0 && \"Reading at invalid alignment!\"" , "/build/llvm-toolchain-snapshot-7~svn329677/include/llvm/Support/BinaryStreamReader.h" , 191, __extension__ __PRETTY_FUNCTION__)) |
191 | "Reading at invalid alignment!")(static_cast <bool> (alignmentAdjustment(Bytes.data(), alignof (T)) == 0 && "Reading at invalid alignment!") ? void ( 0) : __assert_fail ("alignmentAdjustment(Bytes.data(), alignof(T)) == 0 && \"Reading at invalid alignment!\"" , "/build/llvm-toolchain-snapshot-7~svn329677/include/llvm/Support/BinaryStreamReader.h" , 191, __extension__ __PRETTY_FUNCTION__)); |
192 | |
193 | Array = ArrayRef<T>(reinterpret_cast<const T *>(Bytes.data()), NumElements); |
194 | return Error::success(); |
195 | } |
196 | |
197 | /// Read a VarStreamArray of size \p Size bytes and store the result into |
198 | /// \p Array. Updates the stream's offset to point after the newly read |
199 | /// array. Never causes a copy (although iterating the elements of the |
200 | /// VarStreamArray may, depending upon the implementation of the underlying |
201 | /// stream). |
202 | /// |
203 | /// \returns a success error code if the data was successfully read, otherwise |
204 | /// returns an appropriate error code. |
205 | template <typename T, typename U> |
206 | Error readArray(VarStreamArray<T, U> &Array, uint32_t Size) { |
207 | BinaryStreamRef S; |
208 | if (auto EC = readStreamRef(S, Size)) |
209 | return EC; |
210 | Array.setUnderlyingStream(S); |
211 | return Error::success(); |
212 | } |
213 | |
214 | /// Read a FixedStreamArray of \p NumItems elements and store the result into |
215 | /// \p Array. Updates the stream's offset to point after the newly read |
216 | /// array. Never causes a copy (although iterating the elements of the |
217 | /// FixedStreamArray may, depending upon the implementation of the underlying |
218 | /// stream). |
219 | /// |
220 | /// \returns a success error code if the data was successfully read, otherwise |
221 | /// returns an appropriate error code. |
222 | template <typename T> |
223 | Error readArray(FixedStreamArray<T> &Array, uint32_t NumItems) { |
224 | if (NumItems == 0) { |
225 | Array = FixedStreamArray<T>(); |
226 | return Error::success(); |
227 | } |
228 | |
229 | if (NumItems > UINT32_MAX(4294967295U) / sizeof(T)) |
230 | return make_error<BinaryStreamError>( |
231 | stream_error_code::invalid_array_size); |
232 | |
233 | BinaryStreamRef View; |
234 | if (auto EC = readStreamRef(View, NumItems * sizeof(T))) |
235 | return EC; |
236 | |
237 | Array = FixedStreamArray<T>(View); |
238 | return Error::success(); |
239 | } |
240 | |
241 | bool empty() const { return bytesRemaining() == 0; } |
242 | void setOffset(uint32_t Off) { Offset = Off; } |
243 | uint32_t getOffset() const { return Offset; } |
244 | uint32_t getLength() const { return Stream.getLength(); } |
245 | uint32_t bytesRemaining() const { return getLength() - getOffset(); } |
246 | |
247 | /// Advance the stream's offset by \p Amount bytes. |
248 | /// |
249 | /// \returns a success error code if at least \p Amount bytes remain in the |
250 | /// stream, otherwise returns an appropriate error code. |
251 | Error skip(uint32_t Amount); |
252 | |
253 | /// Examine the next byte of the underlying stream without advancing the |
254 | /// stream's offset. If the stream is empty the behavior is undefined. |
255 | /// |
256 | /// \returns the next byte in the stream. |
257 | uint8_t peek() const; |
258 | |
259 | Error padToAlignment(uint32_t Align); |
260 | |
261 | std::pair<BinaryStreamReader, BinaryStreamReader> |
262 | split(uint32_t Offset) const; |
263 | |
264 | private: |
265 | BinaryStreamRef Stream; |
266 | uint32_t Offset = 0; |
267 | }; |
268 | } // namespace llvm |
269 | |
270 | #endif // LLVM_SUPPORT_BINARYSTREAMREADER_H |