mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-05-14 02:56:07 +00:00

to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. llvm-svn: 351636
182 lines
6.6 KiB
C++
182 lines
6.6 KiB
C++
//===- GlobalsStream.cpp - PDB Index of Symbols by Name ---------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// The on-disk structores used in this file are based on the reference
|
|
// implementation which is available at
|
|
// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h
|
|
//
|
|
// When you are reading the reference source code, you'd find the
|
|
// information below useful.
|
|
//
|
|
// - ppdb1->m_fMinimalDbgInfo seems to be always true.
|
|
// - SMALLBUCKETS macro is defined.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/RecordName.h"
|
|
#include "llvm/DebugInfo/PDB/Native/Hash.h"
|
|
#include "llvm/DebugInfo/PDB/Native/RawError.h"
|
|
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
|
|
#include "llvm/Support/BinaryStreamReader.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include <algorithm>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::msf;
|
|
using namespace llvm::pdb;
|
|
|
|
GlobalsStream::GlobalsStream(std::unique_ptr<MappedBlockStream> Stream)
|
|
: Stream(std::move(Stream)) {}
|
|
|
|
GlobalsStream::~GlobalsStream() = default;
|
|
|
|
Error GlobalsStream::reload() {
|
|
BinaryStreamReader Reader(*Stream);
|
|
if (auto E = GlobalsTable.read(Reader))
|
|
return E;
|
|
return Error::success();
|
|
}
|
|
|
|
std::vector<std::pair<uint32_t, codeview::CVSymbol>>
|
|
GlobalsStream::findRecordsByName(StringRef Name,
|
|
const SymbolStream &Symbols) const {
|
|
std::vector<std::pair<uint32_t, codeview::CVSymbol>> Result;
|
|
|
|
// Hash the name to figure out which bucket this goes into.
|
|
size_t ExpandedBucketIndex = hashStringV1(Name) % IPHR_HASH;
|
|
int32_t CompressedBucketIndex = GlobalsTable.BucketMap[ExpandedBucketIndex];
|
|
if (CompressedBucketIndex == -1)
|
|
return Result;
|
|
|
|
uint32_t LastBucketIndex = GlobalsTable.HashBuckets.size() - 1;
|
|
uint32_t StartRecordIndex =
|
|
GlobalsTable.HashBuckets[CompressedBucketIndex] / 12;
|
|
uint32_t EndRecordIndex = 0;
|
|
if (LLVM_LIKELY(uint32_t(CompressedBucketIndex) < LastBucketIndex)) {
|
|
EndRecordIndex = GlobalsTable.HashBuckets[CompressedBucketIndex + 1];
|
|
} else {
|
|
// If this is the last bucket, it consists of all hash records until the end
|
|
// of the HashRecords array.
|
|
EndRecordIndex = GlobalsTable.HashRecords.size() * 12;
|
|
}
|
|
|
|
EndRecordIndex /= 12;
|
|
|
|
assert(EndRecordIndex <= GlobalsTable.HashRecords.size());
|
|
while (StartRecordIndex < EndRecordIndex) {
|
|
PSHashRecord PSH = GlobalsTable.HashRecords[StartRecordIndex];
|
|
uint32_t Off = PSH.Off - 1;
|
|
codeview::CVSymbol Record = Symbols.readRecord(Off);
|
|
if (codeview::getSymbolName(Record) == Name)
|
|
Result.push_back(std::make_pair(Off, std::move(Record)));
|
|
++StartRecordIndex;
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
static Error checkHashHdrVersion(const GSIHashHeader *HashHdr) {
|
|
if (HashHdr->VerHdr != GSIHashHeader::HdrVersion)
|
|
return make_error<RawError>(
|
|
raw_error_code::feature_unsupported,
|
|
"Encountered unsupported globals stream version.");
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
static Error readGSIHashHeader(const GSIHashHeader *&HashHdr,
|
|
BinaryStreamReader &Reader) {
|
|
if (Reader.readObject(HashHdr))
|
|
return make_error<RawError>(raw_error_code::corrupt_file,
|
|
"Stream does not contain a GSIHashHeader.");
|
|
|
|
if (HashHdr->VerSignature != GSIHashHeader::HdrSignature)
|
|
return make_error<RawError>(
|
|
raw_error_code::feature_unsupported,
|
|
"GSIHashHeader signature (0xffffffff) not found.");
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
static Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords,
|
|
const GSIHashHeader *HashHdr,
|
|
BinaryStreamReader &Reader) {
|
|
if (auto EC = checkHashHdrVersion(HashHdr))
|
|
return EC;
|
|
|
|
// HashHdr->HrSize specifies the number of bytes of PSHashRecords we have.
|
|
// Verify that we can read them all.
|
|
if (HashHdr->HrSize % sizeof(PSHashRecord))
|
|
return make_error<RawError>(raw_error_code::corrupt_file,
|
|
"Invalid HR array size.");
|
|
uint32_t NumHashRecords = HashHdr->HrSize / sizeof(PSHashRecord);
|
|
if (auto EC = Reader.readArray(HashRecords, NumHashRecords))
|
|
return joinErrors(std::move(EC),
|
|
make_error<RawError>(raw_error_code::corrupt_file,
|
|
"Error reading hash records."));
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
static Error
|
|
readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets,
|
|
FixedStreamArray<support::ulittle32_t> &HashBitmap,
|
|
const GSIHashHeader *HashHdr,
|
|
MutableArrayRef<int32_t> BucketMap,
|
|
BinaryStreamReader &Reader) {
|
|
if (auto EC = checkHashHdrVersion(HashHdr))
|
|
return EC;
|
|
|
|
// Before the actual hash buckets, there is a bitmap of length determined by
|
|
// IPHR_HASH.
|
|
size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32);
|
|
uint32_t NumBitmapEntries = BitmapSizeInBits / 32;
|
|
if (auto EC = Reader.readArray(HashBitmap, NumBitmapEntries))
|
|
return joinErrors(std::move(EC),
|
|
make_error<RawError>(raw_error_code::corrupt_file,
|
|
"Could not read a bitmap."));
|
|
uint32_t NumBuckets1 = 0;
|
|
uint32_t CompressedBucketIdx = 0;
|
|
for (uint32_t I = 0; I <= IPHR_HASH; ++I) {
|
|
uint8_t WordIdx = I / 32;
|
|
uint8_t BitIdx = I % 32;
|
|
bool IsSet = HashBitmap[WordIdx] & (1U << BitIdx);
|
|
if (IsSet) {
|
|
++NumBuckets1;
|
|
BucketMap[I] = CompressedBucketIdx++;
|
|
} else {
|
|
BucketMap[I] = -1;
|
|
}
|
|
}
|
|
|
|
uint32_t NumBuckets = 0;
|
|
for (uint32_t B : HashBitmap)
|
|
NumBuckets += countPopulation(B);
|
|
|
|
// Hash buckets follow.
|
|
if (auto EC = Reader.readArray(HashBuckets, NumBuckets))
|
|
return joinErrors(std::move(EC),
|
|
make_error<RawError>(raw_error_code::corrupt_file,
|
|
"Hash buckets corrupted."));
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
Error GSIHashTable::read(BinaryStreamReader &Reader) {
|
|
if (auto EC = readGSIHashHeader(HashHdr, Reader))
|
|
return EC;
|
|
if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader))
|
|
return EC;
|
|
if (HashHdr->HrSize > 0)
|
|
if (auto EC = readGSIHashBuckets(HashBuckets, HashBitmap, HashHdr,
|
|
BucketMap, Reader))
|
|
return EC;
|
|
return Error::success();
|
|
}
|