[lldb] Allow plugins to extend DWARF expression parsing for vendor extensions

Parsing DWARF expressions currently does not support DW_OPs that are vendor
extensions. With this change expression parsing calls into SymbolFileDWARF for
unknown opcodes, which is the semantically "closest" plugin that we have right
now. Plugins can then extend SymbolFileDWARF to add support for vendor
extensions.

Reviewed By: labath

Differential Revision: https://reviews.llvm.org/D137247
This commit is contained in:
Philip Pfaffe 2022-11-22 14:29:19 +00:00
parent 4d39552abe
commit c08d3b08f6
8 changed files with 311 additions and 16 deletions

View File

@ -75,14 +75,15 @@ public:
lldb::addr_t GetLocation_DW_OP_addr(const DWARFUnit *dwarf_cu,
uint32_t op_addr_idx, bool &error) const;
bool Update_DW_OP_addr(lldb::addr_t file_addr);
bool Update_DW_OP_addr(const DWARFUnit *dwarf_cu, lldb::addr_t file_addr);
void UpdateValue(uint64_t const_value, lldb::offset_t const_value_byte_size,
uint8_t addr_byte_size);
bool ContainsThreadLocalStorage() const;
bool ContainsThreadLocalStorage(const DWARFUnit *dwarf_cu) const;
bool LinkThreadLocalStorage(
const DWARFUnit *dwarf_cu,
std::function<lldb::addr_t(lldb::addr_t file_addr)> const
&link_address_callback);

View File

@ -131,7 +131,7 @@ static bool ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
/// are made on the state of \p data after this call.
static offset_t GetOpcodeDataSize(const DataExtractor &data,
const lldb::offset_t data_offset,
const uint8_t op) {
const uint8_t op, const DWARFUnit *dwarf_cu) {
lldb::offset_t offset = data_offset;
switch (op) {
case DW_OP_addr:
@ -333,9 +333,12 @@ static offset_t GetOpcodeDataSize(const DataExtractor &data,
}
default:
break;
if (!dwarf_cu) {
return LLDB_INVALID_OFFSET;
}
return dwarf_cu->GetSymbolFileDWARF().GetVendorDWARFOpcodeSize(
data, data_offset, op);
}
return LLDB_INVALID_OFFSET;
}
lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(const DWARFUnit *dwarf_cu,
@ -364,7 +367,8 @@ lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(const DWARFUnit *dwarf_cu,
}
++curr_op_addr_idx;
} else {
const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
const offset_t op_arg_size =
GetOpcodeDataSize(m_data, offset, op, dwarf_cu);
if (op_arg_size == LLDB_INVALID_OFFSET) {
error = true;
break;
@ -375,7 +379,8 @@ lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(const DWARFUnit *dwarf_cu,
return LLDB_INVALID_ADDRESS;
}
bool DWARFExpression::Update_DW_OP_addr(lldb::addr_t file_addr) {
bool DWARFExpression::Update_DW_OP_addr(const DWARFUnit *dwarf_cu,
lldb::addr_t file_addr) {
lldb::offset_t offset = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
@ -402,7 +407,8 @@ bool DWARFExpression::Update_DW_OP_addr(lldb::addr_t file_addr) {
m_data.SetData(encoder.GetDataBuffer());
return true;
} else {
const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
const offset_t op_arg_size =
GetOpcodeDataSize(m_data, offset, op, dwarf_cu);
if (op_arg_size == LLDB_INVALID_OFFSET)
break;
offset += op_arg_size;
@ -411,14 +417,16 @@ bool DWARFExpression::Update_DW_OP_addr(lldb::addr_t file_addr) {
return false;
}
bool DWARFExpression::ContainsThreadLocalStorage() const {
bool DWARFExpression::ContainsThreadLocalStorage(
const DWARFUnit *dwarf_cu) const {
lldb::offset_t offset = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
if (op == DW_OP_form_tls_address || op == DW_OP_GNU_push_tls_address)
return true;
const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
const offset_t op_arg_size =
GetOpcodeDataSize(m_data, offset, op, dwarf_cu);
if (op_arg_size == LLDB_INVALID_OFFSET)
return false;
offset += op_arg_size;
@ -426,6 +434,7 @@ bool DWARFExpression::ContainsThreadLocalStorage() const {
return false;
}
bool DWARFExpression::LinkThreadLocalStorage(
const DWARFUnit *dwarf_cu,
std::function<lldb::addr_t(lldb::addr_t file_addr)> const
&link_address_callback) {
const uint32_t addr_byte_size = m_data.GetAddressByteSize();
@ -496,7 +505,8 @@ bool DWARFExpression::LinkThreadLocalStorage(
}
if (!decoded_data) {
const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
const offset_t op_arg_size =
GetOpcodeDataSize(m_data, offset, op, dwarf_cu);
if (op_arg_size == LLDB_INVALID_OFFSET)
return false;
else
@ -2556,6 +2566,12 @@ bool DWARFExpression::Evaluate(
}
default:
if (dwarf_cu) {
if (dwarf_cu->GetSymbolFileDWARF().ParseVendorDWARFOpcode(
op, opcodes, offset, stack)) {
break;
}
}
if (error_ptr)
error_ptr->SetErrorStringWithFormatv(
"Unhandled opcode {0} in DWARFExpression", LocationAtom(op));

View File

@ -90,7 +90,7 @@ bool DWARFExpressionList::ContainsThreadLocalStorage() const {
return false;
const DWARFExpression &expr = m_exprs.GetEntryRef(0).data;
return expr.ContainsThreadLocalStorage();
return expr.ContainsThreadLocalStorage(m_dwarf_cu);
}
bool DWARFExpressionList::LinkThreadLocalStorage(
@ -107,7 +107,7 @@ bool DWARFExpressionList::LinkThreadLocalStorage(
// If we linked the TLS address correctly, update the module so that when the
// expression is evaluated it can resolve the file address to a load address
// and read the TLS data
if (expr.LinkThreadLocalStorage(link_address_callback))
if (expr.LinkThreadLocalStorage(m_dwarf_cu, link_address_callback))
m_module_wp = new_module_sp;
return true;
}

View File

@ -3492,7 +3492,8 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
if (exe_file_addr != LLDB_INVALID_ADDRESS) {
DWARFExpression *location =
location_list.GetMutableExpressionAtAddress();
if (location->Update_DW_OP_addr(exe_file_addr)) {
if (location->Update_DW_OP_addr(die.GetCU(),
exe_file_addr)) {
linked_oso_file_addr = true;
symbol_context_scope = exe_symbol;
}
@ -3512,7 +3513,7 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
// Update the file address for this variable
DWARFExpression *location =
location_list.GetMutableExpressionAtAddress();
location->Update_DW_OP_addr(exe_file_addr);
location->Update_DW_OP_addr(die.GetCU(), exe_file_addr);
} else {
// Variable didn't make it into the final executable
return nullptr;

View File

@ -330,6 +330,20 @@ public:
return m_parse_time;
}
virtual lldb::offset_t
GetVendorDWARFOpcodeSize(const lldb_private::DataExtractor &data,
const lldb::offset_t data_offset,
const uint8_t op) const {
return LLDB_INVALID_OFFSET;
}
virtual bool
ParseVendorDWARFOpcode(uint8_t op, const lldb_private::DataExtractor &opcodes,
lldb::offset_t &offset,
std::vector<lldb_private::Value> &stack) const {
return false;
}
lldb_private::ConstString ConstructFunctionDemangledName(const DWARFDIE &die);
protected:

View File

@ -77,6 +77,18 @@ DWARFCompileUnit *SymbolFileDWARFDwo::FindSingleCompileUnit() {
return cu;
}
lldb::offset_t SymbolFileDWARFDwo::GetVendorDWARFOpcodeSize(
const lldb_private::DataExtractor &data, const lldb::offset_t data_offset,
const uint8_t op) const {
return GetBaseSymbolFile().GetVendorDWARFOpcodeSize(data, data_offset, op);
}
bool SymbolFileDWARFDwo::ParseVendorDWARFOpcode(
uint8_t op, const lldb_private::DataExtractor &opcodes,
lldb::offset_t &offset, std::vector<lldb_private::Value> &stack) const {
return GetBaseSymbolFile().ParseVendorDWARFOpcode(op, opcodes, offset, stack);
}
SymbolFileDWARF::DIEToTypePtr &SymbolFileDWARFDwo::GetDIEToType() {
return GetBaseSymbolFile().GetDIEToType();
}

View File

@ -42,6 +42,16 @@ public:
llvm::Optional<uint32_t> GetDwoNum() override { return GetID() >> 32; }
lldb::offset_t
GetVendorDWARFOpcodeSize(const lldb_private::DataExtractor &data,
const lldb::offset_t data_offset,
const uint8_t op) const override;
bool ParseVendorDWARFOpcode(
uint8_t op, const lldb_private::DataExtractor &opcodes,
lldb::offset_t &offset,
std::vector<lldb_private::Value> &stack) const override;
protected:
DIEToTypePtr &GetDIEToType() override;
@ -60,7 +70,7 @@ protected:
const DWARFDIE &die, lldb_private::ConstString type_name,
bool must_be_implementation) override;
SymbolFileDWARF &GetBaseSymbolFile() { return m_base_symbol_file; }
SymbolFileDWARF &GetBaseSymbolFile() const { return m_base_symbol_file; }
/// If this file contains exactly one compile unit, this function will return
/// it. Otherwise it returns nullptr.

View File

@ -9,9 +9,11 @@
#include "lldb/Expression/DWARFExpression.h"
#include "Plugins/Platform/Linux/PlatformLinux.h"
#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "TestingSupport/Symbol/YAMLModuleTester.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Value.h"
#include "lldb/Core/dwarf.h"
#include "lldb/Host/HostInfo.h"
@ -514,3 +516,242 @@ DWARF:
ASSERT_EQ(result.GetValueType(), Value::ValueType::LoadAddress);
ASSERT_EQ(result.GetScalar().UInt(), 0x5678u);
}
class CustomSymbolFileDWARF : public SymbolFileDWARF {
static char ID;
public:
using SymbolFileDWARF::SymbolFileDWARF;
bool isA(const void *ClassID) const override {
return ClassID == &ID || SymbolFile::isA(ClassID);
}
static bool classof(const SymbolFile *obj) { return obj->isA(&ID); }
static llvm::StringRef GetPluginNameStatic() { return "custom_dwarf"; }
static llvm::StringRef GetPluginDescriptionStatic() {
return "Symbol file reader with expression extensions.";
}
static void Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(),
GetPluginDescriptionStatic(), CreateInstance,
SymbolFileDWARF::DebuggerInitialize);
}
static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }
static lldb_private::SymbolFile *
CreateInstance(lldb::ObjectFileSP objfile_sp) {
return new CustomSymbolFileDWARF(std::move(objfile_sp),
/*dwo_section_list*/ nullptr);
}
lldb::offset_t
GetVendorDWARFOpcodeSize(const lldb_private::DataExtractor &data,
const lldb::offset_t data_offset,
const uint8_t op) const final {
auto offset = data_offset;
if (op != DW_OP_WASM_location) {
return LLDB_INVALID_OFFSET;
}
// DW_OP_WASM_location WASM_GLOBAL:0x03 index:u32
// Called with "arguments" 0x03 and 0x04
// Location type:
if (data.GetU8(&offset) != /* global */ 0x03) {
return LLDB_INVALID_OFFSET;
}
// Index
if (data.GetU32(&offset) != 0x04) {
return LLDB_INVALID_OFFSET;
}
// Report the skipped distance:
return offset - data_offset;
}
bool
ParseVendorDWARFOpcode(uint8_t op, const lldb_private::DataExtractor &opcodes,
lldb::offset_t &offset,
std::vector<lldb_private::Value> &stack) const final {
if (op != DW_OP_WASM_location) {
return false;
}
// DW_OP_WASM_location WASM_GLOBAL:0x03 index:u32
// Called with "arguments" 0x03 and 0x04
// Location type:
if (opcodes.GetU8(&offset) != /* global */ 0x03) {
return false;
}
// Index:
if (opcodes.GetU32(&offset) != 0x04) {
return false;
}
// Return some value:
stack.push_back({GetScalar(32, 42, false)});
return true;
}
};
char CustomSymbolFileDWARF::ID;
static auto testExpressionVendorExtensions(lldb::ModuleSP module_sp,
DWARFUnit &dwarf_unit) {
// Test that expression extensions can be evaluated, for example
// DW_OP_WASM_location which is not currently handled by DWARFExpression:
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_WASM_location, 0x03, // WASM_GLOBAL:0x03
0x04, 0x00, 0x00, // index:u32
0x00, DW_OP_stack_value},
module_sp, &dwarf_unit),
llvm::HasValue(GetScalar(32, 42, false)));
// Test that searches for opcodes work in the presence of extensions:
uint8_t expr[] = {DW_OP_WASM_location, 0x03, 0x04, 0x00, 0x00, 0x00,
DW_OP_form_tls_address};
DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
/*addr_size*/ 4);
DWARFExpression dwarf_expr(extractor);
ASSERT_TRUE(dwarf_expr.ContainsThreadLocalStorage(&dwarf_unit));
}
TEST(DWARFExpression, Extensions) {
const char *yamldata = R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_386
DWARF:
debug_abbrev:
- Table:
- Code: 0x00000001
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_no
debug_info:
- Version: 4
AddrSize: 4
Entries:
- AbbrCode: 0x1
- AbbrCode: 0x0
)";
SubsystemRAII<FileSystem, HostInfo, TypeSystemClang, ObjectFileELF,
CustomSymbolFileDWARF>
subsystems;
llvm::Expected<TestFile> file = TestFile::fromYaml(yamldata);
EXPECT_THAT_EXPECTED(file, llvm::Succeeded());
auto module_sp = std::make_shared<Module>(file->moduleSpec());
auto &symfile =
*llvm::cast<CustomSymbolFileDWARF>(module_sp->GetSymbolFile());
auto *dwarf_unit = symfile.DebugInfo().GetUnitAtIndex(0);
testExpressionVendorExtensions(module_sp, *dwarf_unit);
}
TEST(DWARFExpression, ExtensionsDWO) {
const char *skeleton_yamldata = R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_386
DWARF:
debug_abbrev:
- Table:
- Code: 0x00000001
Tag: DW_TAG_skeleton_unit
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_dwo_name
Form: DW_FORM_string
- Attribute: DW_AT_dwo_id
Form: DW_FORM_data4
debug_info:
- Version: 4
AddrSize: 4
Entries:
- AbbrCode: 0x1
Values:
- CStr: "dwo_unit"
- Value: 0x01020304
- AbbrCode: 0x0
)";
// .dwo sections aren't currently supported by dwarfyaml. The dwo_yamldata
// contents where generated by roundtripping the following yaml through
// yaml2obj | obj2yaml and renaming the sections. This works because the
// structure of the .dwo and non-.dwo sections is identical.
//
// --- !ELF
// FileHeader:
// Class: ELFCLASS64
// Data: ELFDATA2LSB
// Type: ET_EXEC
// Machine: EM_386
// DWARF:
// debug_abbrev: #.dwo
// - Table:
// - Code: 0x00000001
// Tag: DW_TAG_compile_unit
// Children: DW_CHILDREN_no
// Attributes:
// - Attribute: DW_AT_dwo_id
// Form: DW_FORM_data4
// debug_info: #.dwo
// - Version: 4
// AddrSize: 4
// Entries:
// - AbbrCode: 0x1
// Values:
// - Value: 0x01020304
// - AbbrCode: 0x0
const char *dwo_yamldata = R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_386
Sections:
- Name: .debug_abbrev.dwo
Type: SHT_PROGBITS
AddressAlign: 0x1
Content: '0111007506000000'
- Name: .debug_info.dwo
Type: SHT_PROGBITS
AddressAlign: 0x1
Content: 0D00000004000000000004010403020100
)";
SubsystemRAII<FileSystem, HostInfo, ObjectFileELF, CustomSymbolFileDWARF>
subsystems;
llvm::Expected<TestFile> skeleton_file =
TestFile::fromYaml(skeleton_yamldata);
EXPECT_THAT_EXPECTED(skeleton_file, llvm::Succeeded());
llvm::Expected<TestFile> dwo_file = TestFile::fromYaml(dwo_yamldata);
EXPECT_THAT_EXPECTED(dwo_file, llvm::Succeeded());
auto skeleton_module_sp =
std::make_shared<Module>(skeleton_file->moduleSpec());
auto &skeleton_symfile =
*llvm::cast<CustomSymbolFileDWARF>(skeleton_module_sp->GetSymbolFile());
auto dwo_module_sp = std::make_shared<Module>(dwo_file->moduleSpec());
SymbolFileDWARFDwo dwo_symfile(
skeleton_symfile, dwo_module_sp->GetObjectFile()->shared_from_this(),
0x01020304);
auto *dwo_dwarf_unit = dwo_symfile.DebugInfo().GetUnitAtIndex(0);
testExpressionVendorExtensions(dwo_module_sp, *dwo_dwarf_unit);
}