1 //===-- PdbIndex.cpp ------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
12 #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
13 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
14 #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
15 #include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h"
16 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
17 #include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
18 #include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
19 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
20 #include "llvm/Object/COFF.h"
21 #include "llvm/Support/Error.h"
23 #include "lldb/Utility/LLDBAssert.h"
24 #include "lldb/lldb-defines.h"
27 using namespace lldb_private
;
28 using namespace lldb_private::npdb
;
29 using namespace llvm::codeview
;
30 using namespace llvm::pdb
;
32 PdbIndex::PdbIndex() : m_cus(*this), m_va_to_modi(m_allocator
) {}
34 #define ASSIGN_PTR_OR_RETURN(result_ptr, expr) \
36 auto expected_result = expr; \
37 if (!expected_result) \
38 return expected_result.takeError(); \
39 result_ptr = &expected_result.get(); \
42 llvm::Expected
<std::unique_ptr
<PdbIndex
>>
43 PdbIndex::create(llvm::pdb::PDBFile
*file
) {
46 std::unique_ptr
<PdbIndex
> result(new PdbIndex());
47 ASSIGN_PTR_OR_RETURN(result
->m_dbi
, file
->getPDBDbiStream());
48 ASSIGN_PTR_OR_RETURN(result
->m_tpi
, file
->getPDBTpiStream());
49 ASSIGN_PTR_OR_RETURN(result
->m_ipi
, file
->getPDBIpiStream());
50 ASSIGN_PTR_OR_RETURN(result
->m_info
, file
->getPDBInfoStream());
51 ASSIGN_PTR_OR_RETURN(result
->m_publics
, file
->getPDBPublicsStream());
52 ASSIGN_PTR_OR_RETURN(result
->m_globals
, file
->getPDBGlobalsStream());
53 ASSIGN_PTR_OR_RETURN(result
->m_symrecords
, file
->getPDBSymbolStream());
55 result
->m_tpi
->buildHashMap();
57 result
->m_file
= file
;
59 return std::move(result
);
62 lldb::addr_t
PdbIndex::MakeVirtualAddress(uint16_t segment
,
63 uint32_t offset
) const {
64 uint32_t max_section
= dbi().getSectionHeaders().size();
65 // Segment indices are 1-based.
66 // If this is an absolute symbol, it's indicated by the magic section index
67 // |max_section+1|. In this case, the offset is meaningless, so just return.
68 if (segment
== 0 || segment
> max_section
)
69 return LLDB_INVALID_ADDRESS
;
71 const llvm::object::coff_section
&cs
= dbi().getSectionHeaders()[segment
- 1];
72 return m_load_address
+ static_cast<lldb::addr_t
>(cs
.VirtualAddress
) +
73 static_cast<lldb::addr_t
>(offset
);
76 std::optional
<uint16_t> PdbIndex::GetModuleIndexForAddr(uint16_t segment
,
77 uint32_t offset
) const {
78 return GetModuleIndexForVa(MakeVirtualAddress(segment
, offset
));
81 std::optional
<uint16_t> PdbIndex::GetModuleIndexForVa(lldb::addr_t va
) const {
82 auto iter
= m_va_to_modi
.find(va
);
83 if (iter
== m_va_to_modi
.end())
89 void PdbIndex::ParseSectionContribs() {
90 class Visitor
: public ISectionContribVisitor
{
92 llvm::IntervalMap
<uint64_t, uint16_t> &m_imap
;
95 Visitor(PdbIndex
&ctx
, llvm::IntervalMap
<uint64_t, uint16_t> &imap
)
96 : m_ctx(ctx
), m_imap(imap
) {}
98 void visit(const SectionContrib
&C
) override
{
102 uint64_t va
= m_ctx
.MakeVirtualAddress(C
.ISect
, C
.Off
);
103 if (va
== LLDB_INVALID_ADDRESS
)
105 uint64_t end
= va
+ C
.Size
;
106 // IntervalMap's start and end represent a closed range, not a half-open
107 // range, so we have to subtract 1.
108 m_imap
.insert(va
, end
- 1, C
.Imod
);
110 void visit(const SectionContrib2
&C
) override
{ visit(C
.Base
); }
112 Visitor
v(*this, m_va_to_modi
);
113 dbi().visitSectionContributions(v
);
116 void PdbIndex::BuildAddrToSymbolMap(CompilandIndexItem
&cci
) {
117 lldbassert(cci
.m_symbols_by_va
.empty() &&
118 "Addr to symbol map is already built!");
119 uint16_t modi
= cci
.m_id
.modi
;
120 const CVSymbolArray
&syms
= cci
.m_debug_stream
.getSymbolArray();
121 for (auto iter
= syms
.begin(); iter
!= syms
.end(); ++iter
) {
122 if (!SymbolHasAddress(*iter
))
125 SegmentOffset so
= GetSegmentAndOffset(*iter
);
126 lldb::addr_t va
= MakeVirtualAddress(so
.segment
, so
.offset
);
127 if (va
== LLDB_INVALID_ADDRESS
)
130 PdbCompilandSymId
cu_sym_id(modi
, iter
.offset());
132 // It's rare, but we could have multiple symbols with the same address
133 // because of identical comdat folding. Right now, the first one will win.
134 cci
.m_symbols_by_va
.insert(std::make_pair(va
, PdbSymUid(cu_sym_id
)));
138 std::vector
<SymbolAndUid
> PdbIndex::FindSymbolsByVa(lldb::addr_t va
) {
139 std::vector
<SymbolAndUid
> result
;
141 std::optional
<uint16_t> modi
= GetModuleIndexForVa(va
);
145 CompilandIndexItem
&cci
= compilands().GetOrCreateCompiland(*modi
);
146 if (cci
.m_symbols_by_va
.empty())
147 BuildAddrToSymbolMap(cci
);
149 // The map is sorted by starting address of the symbol. So for example
150 // we could (in theory) have this situation
152 // [------------------]
158 // ^ Address we're searching for
159 // In order to find this, we use the upper_bound of the key value which would
160 // be the first symbol whose starting address is higher than the element we're
163 auto ub
= cci
.m_symbols_by_va
.upper_bound(va
);
165 for (auto iter
= cci
.m_symbols_by_va
.begin(); iter
!= ub
; ++iter
) {
166 PdbCompilandSymId cu_sym_id
= iter
->second
.asCompilandSym();
167 CVSymbol sym
= ReadSymbolRecord(cu_sym_id
);
169 SegmentOffsetLength sol
;
170 if (SymbolIsCode(sym
))
171 sol
= GetSegmentOffsetAndLength(sym
);
173 sol
.so
= GetSegmentAndOffset(sym
);
175 lldb::addr_t start
= MakeVirtualAddress(sol
.so
.segment
, sol
.so
.offset
);
176 if (start
== LLDB_INVALID_ADDRESS
)
179 lldb::addr_t end
= start
+ sol
.length
;
180 if (va
>= start
&& va
< end
)
181 result
.push_back({std::move(sym
), iter
->second
});
187 CVSymbol
PdbIndex::ReadSymbolRecord(PdbCompilandSymId cu_sym
) const {
188 const CompilandIndexItem
*cci
= compilands().GetCompiland(cu_sym
.modi
);
189 auto iter
= cci
->m_debug_stream
.getSymbolArray().at(cu_sym
.offset
);
190 lldbassert(iter
!= cci
->m_debug_stream
.getSymbolArray().end());
194 CVSymbol
PdbIndex::ReadSymbolRecord(PdbGlobalSymId global
) const {
195 return symrecords().readRecord(global
.offset
);