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 bool updated_symbols = false;
90 DWARFDebugInfo &debug_info = DebugInfo();
91 const size_t num_units = debug_info.GetNumUnits();
92 for (size_t i = 0; i < num_units; ++i) {
93 DWARFUnit *unit = debug_info.GetUnitAtIndex(i);
94 if (!unit)
95 continue;
96
97 for (const DWARFDebugInfoEntry &entry : unit->dies()) {
98 const dw_tag_t tag = entry.Tag();
99 if (tag != DW_TAG_subprogram && tag != DW_TAG_variable)
100 continue;
101
102 DWARFDIE die(unit, &entry);
103
104 if (tag == DW_TAG_subprogram) {
105 // Only a mangled (linkage) name is worth putting onto a code symbol.
106 // The source name is already carried by the Wasm name section.
107 const char *mangled =
108 die.GetMangledName(/*substitute_name_allowed=*/false);
109 if (!mangled)
110 continue;
111
112 // A defining DIE names its symbol by address. A function defined in a
113 // translation unit compiled without debug info has only a declaration
114 // DIE with no address, so match its symbol by the demangled name that
115 // the Wasm name section carries.
116 const addr_t file_addr =
118 Symbol *symbol = nullptr;
119 if (file_addr != LLDB_INVALID_ADDRESS) {
120 symbol = symtab.FindSymbolAtFileAddress(file_addr);
121 } else if (ConstString demangled =
122 Mangled(ConstString(mangled)).GetDemangledName()) {
123 if (!demangled_index)
124 demangled_index = BuildDemangledCodeSymbolIndex(symtab);
125 auto it = demangled_index->find(demangled);
126 if (it != demangled_index->end())
127 symbol = symtab.SymbolAtIndex(it->second);
128 }
129 if (symbol && !symbol->GetMangled().GetMangledName()) {
130 symbol->GetMangled().SetMangledName(ConstString(mangled));
131 updated_symbols = true;
132 }
133 continue;
134 }
135
136 // A global variable has no symbol at all. Use its linkage name, or the
137 // source name when there is none (plain C globals have only a
138 // DW_AT_name).
139 const char *name = die.GetMangledName(/*substitute_name_allowed=*/true);
140 if (!name)
141 continue;
142
143 // A global's location is a plain DW_OP_addr.
144 if (!section_list)
145 continue;
146 DWARFAttributes attributes = die.GetAttributes();
147 DWARFFormValue location;
148 bool has_location = false;
149 for (size_t attr_idx = 0; attr_idx < attributes.Size(); ++attr_idx) {
150 if (attributes.AttributeAtIndex(attr_idx) == DW_AT_location) {
151 has_location = attributes.ExtractFormValueAtIndex(attr_idx, location);
152 break;
153 }
154 }
155 if (!has_location || !DWARFFormValue::IsBlockForm(location.Form()))
156 continue;
157
158 const DWARFDataExtractor &data = die.GetData();
159 DWARFExpression expr(
160 DataExtractor(data, location.BlockData() - data.GetDataStart(),
161 location.Unsigned()));
162 llvm::Expected<addr_t> file_addr = expr.GetLocation_DW_OP_addr(unit);
163 if (!file_addr) {
164 llvm::consumeError(file_addr.takeError());
165 continue;
166 }
167
168 Address addr(*file_addr, section_list);
169 if (!addr.IsSectionOffset())
170 continue;
171 if (existing_data_symbols.contains({ConstString(name), *file_addr}))
172 continue;
173
174 // Leave the size unset for Symtab to compute from the gap to the next
175 // symbol.
176 data_symbols.push_back(Symbol(
177 /*symID=*/0, Mangled(ConstString(name)), eSymbolTypeData,
178 /*external=*/true, /*is_debug=*/false, /*is_trampoline=*/false,
179 /*is_artificial=*/false, AddressRange(addr, 0),
180 /*size_is_valid=*/false, /*contains_linker_annotations=*/false,
181 /*flags=*/0));
182 }
183 }
184
185 // Added after the loop: FindSymbolAtFileAddress above sizes the symbols
186 // without a size from the gap to the next symbol, and Symtab keeps those
187 // sizes.
188 for (const Symbol &symbol : data_symbols)
189 symtab.AddSymbol(symbol);
190
191 if (updated_symbols)
193}
194
197 const lldb::offset_t data_offset,
198 const uint8_t op) const {
199 if (op != llvm::dwarf::DW_OP_WASM_location)
200 return LLDB_INVALID_OFFSET;
201
202 lldb::offset_t offset = data_offset;
203 const uint8_t wasm_op = data.GetU8(&offset);
204 switch (wasm_op) {
205 case 0: // LOCAL
206 case 1: // GLOBAL_FIXED
207 case 2: // OPERAND_STACK
208 data.GetULEB128(&offset);
209 break;
210 case 3: // GLOBAL_RELOC
211 data.GetU32(&offset);
212 break;
213 default:
214 return LLDB_INVALID_OFFSET;
215 }
216
217 return offset - data_offset;
218}
219
221 const llvm::DataExtractor &opcodes,
222 lldb::offset_t &offset,
223 RegisterContext *reg_ctx,
224 lldb::RegisterKind reg_kind,
225 std::vector<Value> &stack) const {
226 if (op != llvm::dwarf::DW_OP_WASM_location)
227 return false;
228
229 uint32_t index = 0;
230 uint8_t tag = eWasmTagNotAWasmLocation;
231
232 /// |DWARF Location Index | WebAssembly Construct |
233 /// |---------------------|-----------------------|
234 /// |0 | Local |
235 /// |1 or 3 | Global |
236 /// |2 | Operand Stack |
237 const uint8_t wasm_op = opcodes.getU8(&offset);
238 switch (wasm_op) {
239 case 0: // LOCAL
240 index = opcodes.getULEB128(&offset);
241 tag = eWasmTagLocal;
242 break;
243 case 1: // GLOBAL_FIXED
244 index = opcodes.getULEB128(&offset);
245 tag = eWasmTagGlobal;
246 break;
247 case 2: // OPERAND_STACK
248 index = opcodes.getULEB128(&offset);
250 break;
251 case 3: // GLOBAL_RELOC
252 index = opcodes.getU32(&offset);
253 tag = eWasmTagGlobal;
254 break;
255 default:
256 return false;
257 }
258
259 const uint32_t reg_num = GetWasmRegister(tag, index);
260
261 Value tmp;
263 reg_ctx, reg_kind, reg_num, tmp);
264 if (error) {
265 LLDB_LOG_ERROR(GetLog(DWARFLog::DebugInfo), std::move(error), "{0}");
266 return false;
267 }
268
269 stack.push_back(tmp);
270 return true;
271}
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:36
ConstString GetMangledName() const
Mangled name get accessor.
Definition Mangled.h:169
ConstString GetDemangledName() const
Demangled name get accessor.
Definition Mangled.cpp:238
void SetMangledName(ConstString name)
Definition Mangled.h:163
ConstString GetName(NamePreference preference=ePreferDemangled) const
Best name get accessor.
Definition Mangled.cpp:297
lldb::ModuleSP GetModule() const
Get const accessor for the module pointer.
ObjectFile * GetObjectFile() override
Definition SymbolFile.h:601
virtual void AddSymbols(Symtab &symtab)
Add symbols from this SymbolFile to the ObjectFile's symbol table.
Definition SymbolFile.h:393
bool ValueIsAddress() const
Definition Symbol.cpp:192
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:999
uint32_t AddSymbol(const Symbol &symbol)
Definition Symtab.cpp:62
size_t GetNumSymbols() const
Definition Symtab.cpp:75
void SetSymbolFileAddedSymbols()
Definition Symtab.h:243
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.