blob: b568e104874a44840bb3bd736fb43c40b0cc8b90 [file]
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "SymbolFileWasm.h"
#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
#include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
#include "Utility/WasmVirtualRegisters.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/AddressRange.h"
#include "lldb/Core/Module.h"
#include "lldb/Expression/DWARFExpression.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/Symtab.h"
#include "lldb/Utility/LLDBLog.h"
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::plugin::dwarf;
using namespace llvm::dwarf;
SymbolFileWasm::SymbolFileWasm(ObjectFileSP objfile_sp,
SectionList *dwo_section_list)
: SymbolFileDWARF(objfile_sp, dwo_section_list) {}
SymbolFileWasm::~SymbolFileWasm() = default;
void SymbolFileWasm::AddSymbols(Symtab &symtab) {
SymbolFileDWARF::AddSymbols(symtab);
// The Wasm "name" section names functions but not data, so data symbols such
// as C++ vtables are absent from the symbol table. Recover them from the
// DWARF. A subprogram's mangled name goes onto its existing code symbol. A
// global variable at a static DW_OP_addr, such as a vtable, gets a
// synthesized data symbol so its address resolves back to "vtable for X",
// which is how the Itanium C++ runtime recovers a dynamic type.
ModuleSP module_sp = GetObjectFile()->GetModule();
SectionList *section_list = module_sp ? module_sp->GetSectionList() : nullptr;
DWARFDebugInfo &debug_info = DebugInfo();
const size_t num_units = debug_info.GetNumUnits();
for (size_t i = 0; i < num_units; ++i) {
DWARFUnit *unit = debug_info.GetUnitAtIndex(i);
if (!unit)
continue;
for (const DWARFDebugInfoEntry &entry : unit->dies()) {
const dw_tag_t tag = entry.Tag();
if (tag != DW_TAG_subprogram && tag != DW_TAG_variable)
continue;
DWARFDIE die(unit, &entry);
const char *mangled =
die.GetMangledName(/*substitute_name_allowed=*/false);
if (!mangled)
continue;
if (tag == DW_TAG_subprogram) {
const addr_t file_addr =
die.GetAttributeValueAsAddress(DW_AT_low_pc, LLDB_INVALID_ADDRESS);
if (file_addr == LLDB_INVALID_ADDRESS)
continue;
Symbol *symbol = symtab.FindSymbolAtFileAddress(file_addr);
if (symbol && !symbol->GetMangled().GetMangledName())
symbol->GetMangled().SetMangledName(ConstString(mangled));
continue;
}
// A vtable's location is a plain DW_OP_addr.
if (!section_list)
continue;
DWARFAttributes attributes = die.GetAttributes();
DWARFFormValue location;
bool has_location = false;
for (size_t attr_idx = 0; attr_idx < attributes.Size(); ++attr_idx) {
if (attributes.AttributeAtIndex(attr_idx) == DW_AT_location) {
has_location = attributes.ExtractFormValueAtIndex(attr_idx, location);
break;
}
}
if (!has_location || !DWARFFormValue::IsBlockForm(location.Form()))
continue;
const DWARFDataExtractor &data = die.GetData();
DWARFExpression expr(
DataExtractor(data, location.BlockData() - data.GetDataStart(),
location.Unsigned()));
llvm::Expected<addr_t> file_addr = expr.GetLocation_DW_OP_addr(unit);
if (!file_addr) {
llvm::consumeError(file_addr.takeError());
continue;
}
Address addr(*file_addr, section_list);
if (!addr.IsSectionOffset())
continue;
// Leave the size unset for Symtab to compute from the gap to the next
// symbol. Don't look the address up in the symtab here. That would build
// the address index before the remaining vtables are added, mis-sizing
// them.
symtab.AddSymbol(Symbol(
/*symID=*/0, Mangled(ConstString(mangled)), eSymbolTypeData,
/*external=*/true, /*is_debug=*/false, /*is_trampoline=*/false,
/*is_artificial=*/false, AddressRange(addr, 0),
/*size_is_valid=*/false, /*contains_linker_annotations=*/false,
/*flags=*/0));
}
}
}
lldb::offset_t
SymbolFileWasm::GetVendorDWARFOpcodeSize(const DataExtractor &data,
const lldb::offset_t data_offset,
const uint8_t op) const {
if (op != llvm::dwarf::DW_OP_WASM_location)
return LLDB_INVALID_OFFSET;
lldb::offset_t offset = data_offset;
const uint8_t wasm_op = data.GetU8(&offset);
switch (wasm_op) {
case 0: // LOCAL
case 1: // GLOBAL_FIXED
case 2: // OPERAND_STACK
data.GetULEB128(&offset);
break;
case 3: // GLOBAL_RELOC
data.GetU32(&offset);
break;
default:
return LLDB_INVALID_OFFSET;
}
return offset - data_offset;
}
bool SymbolFileWasm::ParseVendorDWARFOpcode(uint8_t op,
const llvm::DataExtractor &opcodes,
lldb::offset_t &offset,
RegisterContext *reg_ctx,
lldb::RegisterKind reg_kind,
std::vector<Value> &stack) const {
if (op != llvm::dwarf::DW_OP_WASM_location)
return false;
uint32_t index = 0;
uint8_t tag = eWasmTagNotAWasmLocation;
/// |DWARF Location Index | WebAssembly Construct |
/// |---------------------|-----------------------|
/// |0 | Local |
/// |1 or 3 | Global |
/// |2 | Operand Stack |
const uint8_t wasm_op = opcodes.getU8(&offset);
switch (wasm_op) {
case 0: // LOCAL
index = opcodes.getULEB128(&offset);
tag = eWasmTagLocal;
break;
case 1: // GLOBAL_FIXED
index = opcodes.getULEB128(&offset);
tag = eWasmTagGlobal;
break;
case 2: // OPERAND_STACK
index = opcodes.getULEB128(&offset);
tag = eWasmTagOperandStack;
break;
case 3: // GLOBAL_RELOC
index = opcodes.getU32(&offset);
tag = eWasmTagGlobal;
break;
default:
return false;
}
const uint32_t reg_num = GetWasmRegister(tag, index);
Value tmp;
llvm::Error error = DWARFExpression::ReadRegisterValueAsScalar(
reg_ctx, reg_kind, reg_num, tmp);
if (error) {
LLDB_LOG_ERROR(GetLog(DWARFLog::DebugInfo), std::move(error), "{0}");
return false;
}
stack.push_back(tmp);
return true;
}