blob: 9f5dab6c2e8404e11181e9d79e6228acb3c3da90 [file] [log] [blame]
//===-- PdbIndex.cpp --------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "PdbIndex.h"
#include "PdbUtil.h"
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Error.h"
#include "lldb/Utility/LLDBAssert.h"
#include "lldb/lldb-defines.h"
using namespace lldb_private;
using namespace lldb_private::npdb;
using namespace llvm::codeview;
using namespace llvm::pdb;
PdbIndex::PdbIndex() : m_cus(*this), m_va_to_modi(m_allocator) {}
#define ASSIGN_PTR_OR_RETURN(result_ptr, expr) \
{ \
auto expected_result = expr; \
if (!expected_result) \
return expected_result.takeError(); \
result_ptr = &expected_result.get(); \
}
llvm::Expected<std::unique_ptr<PdbIndex>>
PdbIndex::create(std::unique_ptr<llvm::pdb::PDBFile> file) {
lldbassert(file);
std::unique_ptr<PdbIndex> result(new PdbIndex());
ASSIGN_PTR_OR_RETURN(result->m_dbi, file->getPDBDbiStream());
ASSIGN_PTR_OR_RETURN(result->m_tpi, file->getPDBTpiStream());
ASSIGN_PTR_OR_RETURN(result->m_ipi, file->getPDBIpiStream());
ASSIGN_PTR_OR_RETURN(result->m_info, file->getPDBInfoStream());
ASSIGN_PTR_OR_RETURN(result->m_publics, file->getPDBPublicsStream());
ASSIGN_PTR_OR_RETURN(result->m_globals, file->getPDBGlobalsStream());
ASSIGN_PTR_OR_RETURN(result->m_symrecords, file->getPDBSymbolStream());
result->m_tpi->buildHashMap();
result->m_file = std::move(file);
return std::move(result);
}
lldb::addr_t PdbIndex::MakeVirtualAddress(uint16_t segment,
uint32_t offset) const {
// Segment indices are 1-based.
lldbassert(segment > 0);
uint32_t max_section = dbi().getSectionHeaders().size();
lldbassert(segment <= max_section + 1);
// If this is an absolute symbol, it's indicated by the magic section index
// |max_section+1|. In this case, the offset is meaningless, so just return.
if (segment == max_section + 1)
return LLDB_INVALID_ADDRESS;
const llvm::object::coff_section &cs = dbi().getSectionHeaders()[segment - 1];
return m_load_address + static_cast<lldb::addr_t>(cs.VirtualAddress) +
static_cast<lldb::addr_t>(offset);
}
lldb::addr_t PdbIndex::MakeVirtualAddress(const SegmentOffset &so) const {
return MakeVirtualAddress(so.segment, so.offset);
}
llvm::Optional<uint16_t>
PdbIndex::GetModuleIndexForAddr(uint16_t segment, uint32_t offset) const {
return GetModuleIndexForVa(MakeVirtualAddress(segment, offset));
}
llvm::Optional<uint16_t> PdbIndex::GetModuleIndexForVa(lldb::addr_t va) const {
auto iter = m_va_to_modi.find(va);
if (iter == m_va_to_modi.end())
return llvm::None;
return iter.value();
}
void PdbIndex::ParseSectionContribs() {
class Visitor : public ISectionContribVisitor {
PdbIndex &m_ctx;
llvm::IntervalMap<uint64_t, uint16_t> &m_imap;
public:
Visitor(PdbIndex &ctx, llvm::IntervalMap<uint64_t, uint16_t> &imap)
: m_ctx(ctx), m_imap(imap) {}
void visit(const SectionContrib &C) override {
if (C.Size == 0)
return;
uint64_t va = m_ctx.MakeVirtualAddress(C.ISect, C.Off);
uint64_t end = va + C.Size;
// IntervalMap's start and end represent a closed range, not a half-open
// range, so we have to subtract 1.
m_imap.insert(va, end - 1, C.Imod);
}
void visit(const SectionContrib2 &C) override { visit(C.Base); }
};
Visitor v(*this, m_va_to_modi);
dbi().visitSectionContributions(v);
}
void PdbIndex::BuildAddrToSymbolMap(CompilandIndexItem &cci) {
lldbassert(cci.m_symbols_by_va.empty() &&
"Addr to symbol map is already built!");
uint16_t modi = cci.m_id.modi;
const CVSymbolArray &syms = cci.m_debug_stream.getSymbolArray();
for (auto iter = syms.begin(); iter != syms.end(); ++iter) {
if (!SymbolHasAddress(*iter))
continue;
SegmentOffset so = GetSegmentAndOffset(*iter);
lldb::addr_t va = MakeVirtualAddress(so);
PdbCompilandSymId cu_sym_id(modi, iter.offset());
// If the debug info is incorrect, we could have multiple symbols with the
// same address. So use try_emplace instead of insert, and the first one
// will win.
cci.m_symbols_by_va.insert(std::make_pair(va, PdbSymUid(cu_sym_id)));
}
}
std::vector<SymbolAndUid> PdbIndex::FindSymbolsByVa(lldb::addr_t va) {
std::vector<SymbolAndUid> result;
llvm::Optional<uint16_t> modi = GetModuleIndexForVa(va);
if (!modi)
return result;
CompilandIndexItem &cci = compilands().GetOrCreateCompiland(*modi);
if (cci.m_symbols_by_va.empty())
BuildAddrToSymbolMap(cci);
// The map is sorted by starting address of the symbol. So for example
// we could (in theory) have this situation
//
// [------------------]
// [----------]
// [-----------]
// [-------------]
// [----]
// [-----]
// ^ Address we're searching for
// In order to find this, we use the upper_bound of the key value which would
// be the first symbol whose starting address is higher than the element we're
// searching for.
auto ub = cci.m_symbols_by_va.upper_bound(va);
for (auto iter = cci.m_symbols_by_va.begin(); iter != ub; ++iter) {
PdbCompilandSymId cu_sym_id = iter->second.asCompilandSym();
CVSymbol sym = ReadSymbolRecord(cu_sym_id);
SegmentOffsetLength sol;
if (SymbolIsCode(sym))
sol = GetSegmentOffsetAndLength(sym);
else
sol.so = GetSegmentAndOffset(sym);
lldb::addr_t start = MakeVirtualAddress(sol.so);
lldb::addr_t end = start + sol.length;
if (va >= start && va < end)
result.push_back({std::move(sym), iter->second});
}
return result;
}
CVSymbol PdbIndex::ReadSymbolRecord(PdbCompilandSymId cu_sym) const {
// We need to subtract 4 here to adjust for the codeview debug magic
// at the beginning of the debug info stream.
const CompilandIndexItem *cci = compilands().GetCompiland(cu_sym.modi);
auto iter = cci->m_debug_stream.getSymbolArray().at(cu_sym.offset);
lldbassert(iter != cci->m_debug_stream.getSymbolArray().end());
return *iter;
}
CVSymbol PdbIndex::ReadSymbolRecord(PdbGlobalSymId global) const {
return symrecords().readRecord(global.offset);
}