LLDB mainline
SymbolFileWasm.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "SymbolFileWasm.h"
14#include "lldb/Core/Address.h"
16#include "lldb/Core/Mangled.h"
17#include "lldb/Core/Module.h"
19#include "lldb/Symbol/Symbol.h"
20#include "lldb/Symbol/Symtab.h"
23#include "llvm/ADT/DenseMap.h"
24#include "llvm/ADT/DenseSet.h"
25#include <optional>
26#include <utility>
27#include <vector>
28
29using namespace lldb;
30using namespace lldb_private;
31using namespace lldb_private::plugin::dwarf;
32using namespace llvm::dwarf;
33
35 SectionList *dwo_section_list)
36 : SymbolFileDWARF(objfile_sp, dwo_section_list) {}
37
39
40/// Index the code symbols that carry no mangled name by their demangled name.
41/// The value is a symbol table index rather than a Symbol pointer because
42/// pointers do not survive symbols being appended to the table.
43static llvm::DenseMap<ConstString, uint32_t>
45 llvm::DenseMap<ConstString, uint32_t> index;
46 for (size_t i = 0, n = symtab.GetNumSymbols(); i < n; ++i) {
47 Symbol *symbol = symtab.SymbolAtIndex(i);
48 if (!symbol || symbol->GetType() != eSymbolTypeCode)
49 continue;
50 Mangled &mangled = symbol->GetMangled();
51 if (mangled.GetMangledName())
52 continue;
53 if (ConstString demangled = mangled.GetDemangledName())
54 index.try_emplace(demangled, static_cast<uint32_t>(i));
55 }
56 return index;
57}
58
61
62 // The Wasm "name" section names functions but not data, so data symbols are
63 // absent from the symbol table. Recover them from the DWARF. A subprogram's
64 // mangled name goes onto its existing code symbol. A global variable at a
65 // static DW_OP_addr gets a synthesized data symbol so that its address
66 // resolves back to the variable's name. This is how the Itanium C++ runtime
67 // recovers a dynamic type from a vtable ("vtable for X"), and it also lets a
68 // plain C global resolve back to its name.
69 ModuleSP module_sp = GetObjectFile()->GetModule();
70 SectionList *section_list = module_sp ? module_sp->GetSectionList() : nullptr;
71
72 // Built on demand to recover a mangled name from a declaration-only DIE.
73 std::optional<llvm::DenseMap<ConstString, uint32_t>> demangled_index;
74
75 // The object file already has data symbols for exported variables. A
76 // variable that DWARF describes too must not get a second symbol.
77 llvm::DenseSet<std::pair<ConstString, addr_t>> existing_data_symbols;
78 for (size_t i = 0, n = symtab.GetNumSymbols(); i < n; ++i) {
79 Symbol *symbol = symtab.SymbolAtIndex(i);
80 if (!symbol || symbol->GetType() != eSymbolTypeData ||
81 !symbol->ValueIsAddress())
82 continue;
83 existing_data_symbols.insert(
85 symbol->GetAddressRef().GetFileAddress()});
86 }
87
88 std::vector<Symbol> data_symbols;
89 DWARFDebugInfo &debug_info = DebugInfo();
90 const size_t num_units = debug_info.GetNumUnits();
91 for (size_t i = 0; i < num_units; ++i) {
92 DWARFUnit *unit = debug_info.GetUnitAtIndex(i);
93 if (!unit)
94 continue;
95
96 for (const DWARFDebugInfoEntry &entry : unit->dies()) {
97 const dw_tag_t tag = entry.Tag();
98 if (tag != DW_TAG_subprogram && tag != DW_TAG_variable)
99 continue;
100
101 DWARFDIE die(unit, &entry);
102
103 if (tag == DW_TAG_subprogram) {
104 // Only a mangled (linkage) name is worth putting onto a code symbol.
105 // The source name is already carried by the Wasm name section.
106 const char *mangled =
107 die.GetMangledName(/*substitute_name_allowed=*/false);
108 if (!mangled)
109 continue;
110
111 // A defining DIE names its symbol by address. A function defined in a
112 // translation unit compiled without debug info has only a declaration
113 // DIE with no address, so match its symbol by the demangled name that
114 // the Wasm name section carries.
115 const addr_t file_addr =
117 Symbol *symbol = nullptr;
118 if (file_addr != LLDB_INVALID_ADDRESS) {
119 symbol = symtab.FindSymbolAtFileAddress(file_addr);
120 } else if (ConstString demangled =
121 Mangled(ConstString(mangled)).GetDemangledName()) {
122 if (!demangled_index)
123 demangled_index = BuildDemangledCodeSymbolIndex(symtab);
124 auto it = demangled_index->find(demangled);
125 if (it != demangled_index->end())
126 symbol = symtab.SymbolAtIndex(it->second);
127 }
128 if (symbol && !symbol->GetMangled().GetMangledName())
129 symbol->GetMangled().SetMangledName(ConstString(mangled));
130 continue;
131 }
132
133 // A global variable has no symbol at all. Use its linkage name, or the
134 // source name when there is none (plain C globals have only a
135 // DW_AT_name).
136 const char *name = die.GetMangledName(/*substitute_name_allowed=*/true);
137 if (!name)
138 continue;
139
140 // A global's location is a plain DW_OP_addr.
141 if (!section_list)
142 continue;
143 DWARFAttributes attributes = die.GetAttributes();
144 DWARFFormValue location;
145 bool has_location = false;
146 for (size_t attr_idx = 0; attr_idx < attributes.Size(); ++attr_idx) {
147 if (attributes.AttributeAtIndex(attr_idx) == DW_AT_location) {
148 has_location = attributes.ExtractFormValueAtIndex(attr_idx, location);
149 break;
150 }
151 }
152 if (!has_location || !DWARFFormValue::IsBlockForm(location.Form()))
153 continue;
154
155 const DWARFDataExtractor &data = die.GetData();
156 DWARFExpression expr(
157 DataExtractor(data, location.BlockData() - data.GetDataStart(),
158 location.Unsigned()));
159 llvm::Expected<addr_t> file_addr = expr.GetLocation_DW_OP_addr(unit);
160 if (!file_addr) {
161 llvm::consumeError(file_addr.takeError());
162 continue;
163 }
164
165 Address addr(*file_addr, section_list);
166 if (!addr.IsSectionOffset())
167 continue;
168 if (existing_data_symbols.contains({ConstString(name), *file_addr}))
169 continue;
170
171 // Leave the size unset for Symtab to compute from the gap to the next
172 // symbol.
173 data_symbols.push_back(Symbol(
174 /*symID=*/0, Mangled(ConstString(name)), eSymbolTypeData,
175 /*external=*/true, /*is_debug=*/false, /*is_trampoline=*/false,
176 /*is_artificial=*/false, AddressRange(addr, 0),
177 /*size_is_valid=*/false, /*contains_linker_annotations=*/false,
178 /*flags=*/0));
179 }
180 }
181
182 // Added after the loop: FindSymbolAtFileAddress above sizes the symbols
183 // without a size from the gap to the next symbol, and Symtab keeps those
184 // sizes.
185 for (const Symbol &symbol : data_symbols)
186 symtab.AddSymbol(symbol);
187}
188
191 const lldb::offset_t data_offset,
192 const uint8_t op) const {
193 if (op != llvm::dwarf::DW_OP_WASM_location)
194 return LLDB_INVALID_OFFSET;
195
196 lldb::offset_t offset = data_offset;
197 const uint8_t wasm_op = data.GetU8(&offset);
198 switch (wasm_op) {
199 case 0: // LOCAL
200 case 1: // GLOBAL_FIXED
201 case 2: // OPERAND_STACK
202 data.GetULEB128(&offset);
203 break;
204 case 3: // GLOBAL_RELOC
205 data.GetU32(&offset);
206 break;
207 default:
208 return LLDB_INVALID_OFFSET;
209 }
210
211 return offset - data_offset;
212}
213
215 const llvm::DataExtractor &opcodes,
216 lldb::offset_t &offset,
217 RegisterContext *reg_ctx,
218 lldb::RegisterKind reg_kind,
219 std::vector<Value> &stack) const {
220 if (op != llvm::dwarf::DW_OP_WASM_location)
221 return false;
222
223 uint32_t index = 0;
224 uint8_t tag = eWasmTagNotAWasmLocation;
225
226 /// |DWARF Location Index | WebAssembly Construct |
227 /// |---------------------|-----------------------|
228 /// |0 | Local |
229 /// |1 or 3 | Global |
230 /// |2 | Operand Stack |
231 const uint8_t wasm_op = opcodes.getU8(&offset);
232 switch (wasm_op) {
233 case 0: // LOCAL
234 index = opcodes.getULEB128(&offset);
235 tag = eWasmTagLocal;
236 break;
237 case 1: // GLOBAL_FIXED
238 index = opcodes.getULEB128(&offset);
239 tag = eWasmTagGlobal;
240 break;
241 case 2: // OPERAND_STACK
242 index = opcodes.getULEB128(&offset);
244 break;
245 case 3: // GLOBAL_RELOC
246 index = opcodes.getU32(&offset);
247 tag = eWasmTagGlobal;
248 break;
249 default:
250 return false;
251 }
252
253 const uint32_t reg_num = GetWasmRegister(tag, index);
254
255 Value tmp;
257 reg_ctx, reg_kind, reg_num, tmp);
258 if (error) {
259 LLDB_LOG_ERROR(GetLog(DWARFLog::DebugInfo), std::move(error), "{0}");
260 return false;
261 }
262
263 stack.push_back(tmp);
264 return true;
265}
static llvm::raw_ostream & error(Stream &strm)
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:405
static llvm::DenseMap< ConstString, uint32_t > BuildDemangledCodeSymbolIndex(Symtab &symtab)
Index the code symbols that carry no mangled name by their demangled name.
A section + offset based address range class.
A section + offset based address class.
Definition Address.h:62
lldb::addr_t GetFileAddress() const
Get the file address.
Definition Address.cpp:283
bool IsSectionOffset() const
Check if an address is section offset.
Definition Address.h:342
A uniqued constant string class.
Definition ConstString.h:40
"lldb/Expression/DWARFExpression.h" Encapsulates a DWARF location expression and interprets it.
llvm::Expected< lldb::addr_t > GetLocation_DW_OP_addr(const Delegate *dwarf_cu) const
Return the address specified by the first DW_OP_{addr, addrx, GNU_addr_index} in the operation stream...
static llvm::Error ReadRegisterValueAsScalar(RegisterContext *reg_ctx, lldb::RegisterKind reg_kind, uint32_t reg_num, Value &value)
An data extractor class.
uint64_t GetULEB128(lldb::offset_t *offset_ptr) const
Extract a unsigned LEB128 value from *offset_ptr.
uint32_t GetU32(lldb::offset_t *offset_ptr) const
Extract a uint32_t value from *offset_ptr.
const uint8_t * GetDataStart() const
Get the data start pointer.
uint8_t GetU8(lldb::offset_t *offset_ptr) const
Extract a uint8_t value from *offset_ptr.
A class that handles mangled names.
Definition Mangled.h:34
ConstString GetMangledName() const
Mangled name get accessor.
Definition Mangled.h:170
ConstString GetDemangledName() const
Demangled name get accessor.
Definition Mangled.cpp:171
void SetMangledName(ConstString name)
Definition Mangled.h:164
ConstString GetName(NamePreference preference=ePreferDemangled) const
Best name get accessor.
Definition Mangled.cpp:257
lldb::ModuleSP GetModule() const
Get const accessor for the module pointer.
ObjectFile * GetObjectFile() override
Definition SymbolFile.h:595
virtual void AddSymbols(Symtab &symtab)
Definition SymbolFile.h:387
bool ValueIsAddress() const
Definition Symbol.cpp:191
Mangled & GetMangled()
Definition Symbol.h:162
Address & GetAddressRef()
Definition Symbol.h:78
lldb::SymbolType GetType() const
Definition Symbol.h:197
Symbol * SymbolAtIndex(size_t idx)
Definition Symtab.cpp:226
Symbol * FindSymbolAtFileAddress(lldb::addr_t file_addr)
Definition Symtab.cpp:997
uint32_t AddSymbol(const Symbol &symbol)
Definition Symtab.cpp:62
size_t GetNumSymbols() const
Definition Symtab.cpp:75
dw_attr_t AttributeAtIndex(uint32_t i) const
bool ExtractFormValueAtIndex(uint32_t i, DWARFFormValue &form_value) const
DWARFAttributes GetAttributes(Recurse recurse=Recurse::yes) const
const DWARFDataExtractor & GetData() const
uint64_t GetAttributeValueAsAddress(const dw_attr_t attr, uint64_t fail_value) const
const char * GetMangledName(bool substitute_name_allowed=true) const
Definition DWARFDIE.cpp:212
DWARFDebugInfoEntry objects assume that they are living in one big vector and do pointer arithmetic o...
static bool IsBlockForm(const dw_form_t form)
SymbolFileDWARF(lldb::ObjectFileSP objfile_sp, SectionList *dwo_section_list)
SymbolFileWasm(lldb::ObjectFileSP objfile_sp, SectionList *dwo_section_list)
void AddSymbols(Symtab &symtab) override
The Wasm "name" section stores only demangled names, so symbols parsed from it have no mangled name t...
lldb::offset_t GetVendorDWARFOpcodeSize(const DataExtractor &data, const lldb::offset_t data_offset, const uint8_t op) const override
bool ParseVendorDWARFOpcode(uint8_t op, const llvm::DataExtractor &opcodes, lldb::offset_t &offset, RegisterContext *reg_ctx, lldb::RegisterKind reg_kind, std::vector< Value > &stack) const override
llvm::dwarf::Tag dw_tag_t
Definition dwarf.h:19
#define LLDB_INVALID_ADDRESS
#define LLDB_INVALID_OFFSET
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:338
uint32_t GetWasmRegister(uint8_t tag, uint32_t index)
uint64_t offset_t
Definition lldb-types.h:86
std::shared_ptr< lldb_private::ObjectFile > ObjectFileSP
uint64_t addr_t
Definition lldb-types.h:80
std::shared_ptr< lldb_private::Module > ModuleSP
RegisterKind
Register numbering types.