//===- NamedStreamMap.cpp - PDB Named Stream Map --------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" #include "llvm/DebugInfo/PDB/Native/HashTable.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/BinaryStreamRef.h" #include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include #include #include #include using namespace llvm; using namespace llvm::pdb; // FIXME: This shouldn't be necessary, but if we insert the strings in any // other order, cvdump cannot read the generated name map. This suggests that // we may be using the wrong hash function. A closer inspection of the cvdump // source code may reveal something, but for now this at least makes us work, // even if only by accident. static constexpr const char *OrderedStreamNames[] = {"/LinkInfo", "/names", "/src/headerblock"}; NamedStreamMap::NamedStreamMap() = default; Error NamedStreamMap::load(BinaryStreamReader &Stream) { Mapping.clear(); FinalizedHashTable.clear(); FinalizedInfo.reset(); uint32_t StringBufferSize; if (auto EC = Stream.readInteger(StringBufferSize)) return joinErrors(std::move(EC), make_error(raw_error_code::corrupt_file, "Expected string buffer size")); BinaryStreamRef StringsBuffer; if (auto EC = Stream.readStreamRef(StringsBuffer, StringBufferSize)) return EC; HashTable OffsetIndexMap; if (auto EC = OffsetIndexMap.load(Stream)) return EC; uint32_t NameOffset; uint32_t NameIndex; for (const auto &Entry : OffsetIndexMap) { std::tie(NameOffset, NameIndex) = Entry; // Compute the offset of the start of the string relative to the stream. BinaryStreamReader NameReader(StringsBuffer); NameReader.setOffset(NameOffset); // Pump out our c-string from the stream. StringRef Str; if (auto EC = NameReader.readCString(Str)) return joinErrors(std::move(EC), make_error(raw_error_code::corrupt_file, "Expected name map name")); // Add this to a string-map from name to stream number. Mapping.insert({Str, NameIndex}); } return Error::success(); } Error NamedStreamMap::commit(BinaryStreamWriter &Writer) const { assert(FinalizedInfo.hasValue()); // The first field is the number of bytes of string data. if (auto EC = Writer.writeInteger(FinalizedInfo->StringDataBytes)) return EC; for (const auto &Name : OrderedStreamNames) { auto Item = Mapping.find(Name); if (Item == Mapping.end()) continue; if (auto EC = Writer.writeCString(Item->getKey())) return EC; } // And finally the Offset Index map. if (auto EC = FinalizedHashTable.commit(Writer)) return EC; return Error::success(); } uint32_t NamedStreamMap::finalize() { if (FinalizedInfo.hasValue()) return FinalizedInfo->SerializedLength; // Build the finalized hash table. FinalizedHashTable.clear(); FinalizedInfo.emplace(); for (const auto &Name : OrderedStreamNames) { auto Item = Mapping.find(Name); if (Item == Mapping.end()) continue; FinalizedHashTable.set(FinalizedInfo->StringDataBytes, Item->getValue()); FinalizedInfo->StringDataBytes += Item->getKeyLength() + 1; } // Number of bytes of string data. FinalizedInfo->SerializedLength += sizeof(support::ulittle32_t); // Followed by that many actual bytes of string data. FinalizedInfo->SerializedLength += FinalizedInfo->StringDataBytes; // Followed by the mapping from Offset to Index. FinalizedInfo->SerializedLength += FinalizedHashTable.calculateSerializedLength(); return FinalizedInfo->SerializedLength; } iterator_range> NamedStreamMap::entries() const { return make_range>(Mapping.begin(), Mapping.end()); } uint32_t NamedStreamMap::size() const { return Mapping.size(); } bool NamedStreamMap::get(StringRef Stream, uint32_t &StreamNo) const { auto Iter = Mapping.find(Stream); if (Iter == Mapping.end()) return false; StreamNo = Iter->second; return true; } void NamedStreamMap::set(StringRef Stream, uint32_t StreamNo) { FinalizedInfo.reset(); Mapping[Stream] = StreamNo; } void NamedStreamMap::remove(StringRef Stream) { FinalizedInfo.reset(); Mapping.erase(Stream); }