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
758 lines
26 KiB
C++
758 lines
26 KiB
C++
//===-- DWARFExpressionTest.cpp -------------------------------------------===//
|
|
//
|
|
// 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 "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"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Utility/StreamString.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Testing/Support/Error.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace lldb_private;
|
|
using namespace lldb_private::dwarf;
|
|
|
|
static llvm::Expected<Scalar> Evaluate(llvm::ArrayRef<uint8_t> expr,
|
|
lldb::ModuleSP module_sp = {},
|
|
DWARFUnit *unit = nullptr,
|
|
ExecutionContext *exe_ctx = nullptr) {
|
|
DataExtractor extractor(expr.data(), expr.size(), lldb::eByteOrderLittle,
|
|
/*addr_size*/ 4);
|
|
Value result;
|
|
Status status;
|
|
if (!DWARFExpression::Evaluate(exe_ctx, /*reg_ctx*/ nullptr, module_sp,
|
|
extractor, unit, lldb::eRegisterKindLLDB,
|
|
/*initial_value_ptr*/ nullptr,
|
|
/*object_address_ptr*/ nullptr, result,
|
|
&status))
|
|
return status.ToError();
|
|
|
|
switch (result.GetValueType()) {
|
|
case Value::ValueType::Scalar:
|
|
return result.GetScalar();
|
|
case Value::ValueType::LoadAddress:
|
|
return LLDB_INVALID_ADDRESS;
|
|
case Value::ValueType::HostAddress: {
|
|
// Convert small buffers to scalars to simplify the tests.
|
|
DataBufferHeap &buf = result.GetBuffer();
|
|
if (buf.GetByteSize() <= 8) {
|
|
uint64_t val = 0;
|
|
memcpy(&val, buf.GetBytes(), buf.GetByteSize());
|
|
return Scalar(llvm::APInt(buf.GetByteSize()*8, val, false));
|
|
}
|
|
}
|
|
[[fallthrough]];
|
|
default:
|
|
return status.ToError();
|
|
}
|
|
}
|
|
|
|
class DWARFExpressionTester : public YAMLModuleTester {
|
|
public:
|
|
DWARFExpressionTester(llvm::StringRef yaml_data, size_t cu_index) :
|
|
YAMLModuleTester(yaml_data, cu_index) {}
|
|
|
|
using YAMLModuleTester::YAMLModuleTester;
|
|
llvm::Expected<Scalar> Eval(llvm::ArrayRef<uint8_t> expr) {
|
|
return ::Evaluate(expr, m_module_sp, m_dwarf_unit);
|
|
}
|
|
};
|
|
|
|
/// Unfortunately Scalar's operator==() is really picky.
|
|
static Scalar GetScalar(unsigned bits, uint64_t value, bool sign) {
|
|
Scalar scalar(value);
|
|
scalar.TruncOrExtendTo(bits, sign);
|
|
return scalar;
|
|
}
|
|
|
|
/// This is needed for the tests that use a mock process.
|
|
class DWARFExpressionMockProcessTest : public ::testing::Test {
|
|
public:
|
|
void SetUp() override {
|
|
FileSystem::Initialize();
|
|
HostInfo::Initialize();
|
|
platform_linux::PlatformLinux::Initialize();
|
|
}
|
|
void TearDown() override {
|
|
platform_linux::PlatformLinux::Terminate();
|
|
HostInfo::Terminate();
|
|
FileSystem::Terminate();
|
|
}
|
|
};
|
|
|
|
TEST(DWARFExpression, DW_OP_pick) {
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 0}),
|
|
llvm::HasValue(0));
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 1}),
|
|
llvm::HasValue(1));
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 2}),
|
|
llvm::Failed());
|
|
}
|
|
|
|
TEST(DWARFExpression, DW_OP_const) {
|
|
// Extend to address size.
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x88}), llvm::HasValue(0x88));
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1s, 0x88}),
|
|
llvm::HasValue(0xffffff88));
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2u, 0x47, 0x88}),
|
|
llvm::HasValue(0x8847));
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2s, 0x47, 0x88}),
|
|
llvm::HasValue(0xffff8847));
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const4u, 0x44, 0x42, 0x47, 0x88}),
|
|
llvm::HasValue(0x88474244));
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const4s, 0x44, 0x42, 0x47, 0x88}),
|
|
llvm::HasValue(0x88474244));
|
|
|
|
// Truncate to address size.
|
|
EXPECT_THAT_EXPECTED(
|
|
Evaluate({DW_OP_const8u, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}),
|
|
llvm::HasValue(0x33221100));
|
|
EXPECT_THAT_EXPECTED(
|
|
Evaluate({DW_OP_const8s, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}),
|
|
llvm::HasValue(0x33221100));
|
|
|
|
// Don't truncate to address size for compatibility with clang (pr48087).
|
|
EXPECT_THAT_EXPECTED(
|
|
Evaluate({DW_OP_constu, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}),
|
|
llvm::HasValue(0x01010101010101));
|
|
EXPECT_THAT_EXPECTED(
|
|
Evaluate({DW_OP_consts, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}),
|
|
llvm::HasValue(0xffff010101010101));
|
|
}
|
|
|
|
TEST(DWARFExpression, DW_OP_skip) {
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x42, DW_OP_skip, 0x02, 0x00,
|
|
DW_OP_const1u, 0xff}),
|
|
llvm::HasValue(0x42));
|
|
}
|
|
|
|
TEST(DWARFExpression, DW_OP_bra) {
|
|
EXPECT_THAT_EXPECTED(
|
|
// clang-format off
|
|
Evaluate({
|
|
DW_OP_const1u, 0x42, // push 0x42
|
|
DW_OP_const1u, 0x1, // push 0x1
|
|
DW_OP_bra, 0x02, 0x00, // if 0x1 > 0, then skip 0x0002 opcodes
|
|
DW_OP_const1u, 0xff, // push 0xff
|
|
}),
|
|
// clang-format on
|
|
llvm::HasValue(0x42));
|
|
}
|
|
|
|
TEST(DWARFExpression, DW_OP_convert) {
|
|
/// Auxiliary debug info.
|
|
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_yes
|
|
Attributes:
|
|
- Attribute: DW_AT_language
|
|
Form: DW_FORM_data2
|
|
- Code: 0x00000002
|
|
Tag: DW_TAG_base_type
|
|
Children: DW_CHILDREN_no
|
|
Attributes:
|
|
- Attribute: DW_AT_encoding
|
|
Form: DW_FORM_data1
|
|
- Attribute: DW_AT_byte_size
|
|
Form: DW_FORM_data1
|
|
debug_info:
|
|
- Version: 4
|
|
AddrSize: 8
|
|
AbbrevTableID: 0
|
|
AbbrOffset: 0x0
|
|
Entries:
|
|
- AbbrCode: 0x00000001
|
|
Values:
|
|
- Value: 0x000000000000000C
|
|
- AbbrCode: 0x00000000
|
|
- Version: 4
|
|
AddrSize: 8
|
|
AbbrevTableID: 0
|
|
AbbrOffset: 0x0
|
|
Entries:
|
|
- AbbrCode: 0x00000001
|
|
Values:
|
|
- Value: 0x000000000000000C
|
|
# 0x0000000e:
|
|
- AbbrCode: 0x00000002
|
|
Values:
|
|
- Value: 0x0000000000000007 # DW_ATE_unsigned
|
|
- Value: 0x0000000000000004
|
|
# 0x00000011:
|
|
- AbbrCode: 0x00000002
|
|
Values:
|
|
- Value: 0x0000000000000007 # DW_ATE_unsigned
|
|
- Value: 0x0000000000000008
|
|
# 0x00000014:
|
|
- AbbrCode: 0x00000002
|
|
Values:
|
|
- Value: 0x0000000000000005 # DW_ATE_signed
|
|
- Value: 0x0000000000000008
|
|
# 0x00000017:
|
|
- AbbrCode: 0x00000002
|
|
Values:
|
|
- Value: 0x0000000000000008 # DW_ATE_unsigned_char
|
|
- Value: 0x0000000000000001
|
|
# 0x0000001a:
|
|
- AbbrCode: 0x00000002
|
|
Values:
|
|
- Value: 0x0000000000000006 # DW_ATE_signed_char
|
|
- Value: 0x0000000000000001
|
|
# 0x0000001d:
|
|
- AbbrCode: 0x00000002
|
|
Values:
|
|
- Value: 0x000000000000000b # DW_ATE_numeric_string
|
|
- Value: 0x0000000000000001
|
|
- AbbrCode: 0x00000000
|
|
|
|
)";
|
|
// Compile unit relative offsets to each DW_TAG_base_type
|
|
uint8_t offs_uint32_t = 0x0000000e;
|
|
uint8_t offs_uint64_t = 0x00000011;
|
|
uint8_t offs_sint64_t = 0x00000014;
|
|
uint8_t offs_uchar = 0x00000017;
|
|
uint8_t offs_schar = 0x0000001a;
|
|
|
|
DWARFExpressionTester t(yamldata, /*cu_index=*/1);
|
|
ASSERT_TRUE((bool)t.GetDwarfUnit());
|
|
|
|
// Constant is given as little-endian.
|
|
bool is_signed = true;
|
|
bool not_signed = false;
|
|
|
|
//
|
|
// Positive tests.
|
|
//
|
|
|
|
// Leave as is.
|
|
EXPECT_THAT_EXPECTED(
|
|
t.Eval({DW_OP_const4u, 0x11, 0x22, 0x33, 0x44, //
|
|
DW_OP_convert, offs_uint32_t, DW_OP_stack_value}),
|
|
llvm::HasValue(GetScalar(64, 0x44332211, not_signed)));
|
|
|
|
// Zero-extend to 64 bits.
|
|
EXPECT_THAT_EXPECTED(
|
|
t.Eval({DW_OP_const4u, 0x11, 0x22, 0x33, 0x44, //
|
|
DW_OP_convert, offs_uint64_t, DW_OP_stack_value}),
|
|
llvm::HasValue(GetScalar(64, 0x44332211, not_signed)));
|
|
|
|
// Sign-extend to 64 bits.
|
|
EXPECT_THAT_EXPECTED(
|
|
t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, //
|
|
DW_OP_convert, offs_sint64_t, DW_OP_stack_value}),
|
|
llvm::HasValue(GetScalar(64, 0xffffffffffeeddcc, is_signed)));
|
|
|
|
// Sign-extend, then truncate.
|
|
EXPECT_THAT_EXPECTED(
|
|
t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, //
|
|
DW_OP_convert, offs_sint64_t, //
|
|
DW_OP_convert, offs_uint32_t, DW_OP_stack_value}),
|
|
llvm::HasValue(GetScalar(32, 0xffeeddcc, not_signed)));
|
|
|
|
// Truncate to default unspecified (pointer-sized) type.
|
|
EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, //
|
|
DW_OP_convert, offs_sint64_t, //
|
|
DW_OP_convert, 0x00, DW_OP_stack_value}),
|
|
llvm::HasValue(GetScalar(32, 0xffeeddcc, not_signed)));
|
|
|
|
// Truncate to 8 bits.
|
|
EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 'A', 'B', 'C', 'D', DW_OP_convert,
|
|
offs_uchar, DW_OP_stack_value}),
|
|
llvm::HasValue(GetScalar(8, 'A', not_signed)));
|
|
|
|
// Also truncate to 8 bits.
|
|
EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 'A', 'B', 'C', 'D', DW_OP_convert,
|
|
offs_schar, DW_OP_stack_value}),
|
|
llvm::HasValue(GetScalar(8, 'A', is_signed)));
|
|
|
|
//
|
|
// Errors.
|
|
//
|
|
|
|
// No Module.
|
|
EXPECT_THAT_ERROR(Evaluate({DW_OP_const1s, 'X', DW_OP_convert, 0x00}, nullptr,
|
|
t.GetDwarfUnit())
|
|
.takeError(),
|
|
llvm::Failed());
|
|
|
|
// No DIE.
|
|
EXPECT_THAT_ERROR(
|
|
t.Eval({DW_OP_const1s, 'X', DW_OP_convert, 0x01}).takeError(),
|
|
llvm::Failed());
|
|
|
|
// Unsupported.
|
|
EXPECT_THAT_ERROR(
|
|
t.Eval({DW_OP_const1s, 'X', DW_OP_convert, 0x1d}).takeError(),
|
|
llvm::Failed());
|
|
}
|
|
|
|
TEST(DWARFExpression, DW_OP_stack_value) {
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_stack_value}), llvm::Failed());
|
|
}
|
|
|
|
TEST(DWARFExpression, DW_OP_piece) {
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2u, 0x11, 0x22, DW_OP_piece, 2,
|
|
DW_OP_const2u, 0x33, 0x44, DW_OP_piece, 2}),
|
|
llvm::HasValue(GetScalar(32, 0x44332211, true)));
|
|
EXPECT_THAT_EXPECTED(
|
|
Evaluate({DW_OP_piece, 1, DW_OP_const1u, 0xff, DW_OP_piece, 1}),
|
|
// Note that the "00" should really be "undef", but we can't
|
|
// represent that yet.
|
|
llvm::HasValue(GetScalar(16, 0xff00, true)));
|
|
}
|
|
|
|
TEST(DWARFExpression, DW_OP_implicit_value) {
|
|
unsigned char bytes = 4;
|
|
|
|
EXPECT_THAT_EXPECTED(
|
|
Evaluate({DW_OP_implicit_value, bytes, 0x11, 0x22, 0x33, 0x44}),
|
|
llvm::HasValue(GetScalar(8 * bytes, 0x44332211, true)));
|
|
}
|
|
|
|
TEST(DWARFExpression, DW_OP_unknown) {
|
|
EXPECT_THAT_EXPECTED(
|
|
Evaluate({0xff}),
|
|
llvm::FailedWithMessage(
|
|
"Unhandled opcode DW_OP_unknown_ff in DWARFExpression"));
|
|
}
|
|
|
|
TEST_F(DWARFExpressionMockProcessTest, DW_OP_deref) {
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit0, DW_OP_deref}), llvm::Failed());
|
|
|
|
struct MockProcess : Process {
|
|
MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
|
|
: Process(target_sp, listener_sp) {}
|
|
|
|
llvm::StringRef GetPluginName() override { return "mock process"; }
|
|
bool CanDebug(lldb::TargetSP target,
|
|
bool plugin_specified_by_name) override {
|
|
return false;
|
|
};
|
|
Status DoDestroy() override { return {}; }
|
|
void RefreshStateAfterStop() override {}
|
|
bool DoUpdateThreadList(ThreadList &old_thread_list,
|
|
ThreadList &new_thread_list) override {
|
|
return false;
|
|
};
|
|
size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
|
|
Status &error) override {
|
|
for (size_t i = 0; i < size; ++i)
|
|
((char *)buf)[i] = (vm_addr + i) & 0xff;
|
|
error.Clear();
|
|
return size;
|
|
}
|
|
};
|
|
|
|
// Set up a mock process.
|
|
ArchSpec arch("i386-pc-linux");
|
|
Platform::SetHostPlatform(
|
|
platform_linux::PlatformLinux::CreateInstance(true, &arch));
|
|
lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
|
|
ASSERT_TRUE(debugger_sp);
|
|
lldb::TargetSP target_sp;
|
|
lldb::PlatformSP platform_sp;
|
|
debugger_sp->GetTargetList().CreateTarget(
|
|
*debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);
|
|
ASSERT_TRUE(target_sp);
|
|
ASSERT_TRUE(target_sp->GetArchitecture().IsValid());
|
|
ASSERT_TRUE(platform_sp);
|
|
lldb::ListenerSP listener_sp(Listener::MakeListener("dummy"));
|
|
lldb::ProcessSP process_sp =
|
|
std::make_shared<MockProcess>(target_sp, listener_sp);
|
|
ASSERT_TRUE(process_sp);
|
|
|
|
ExecutionContext exe_ctx(process_sp);
|
|
// Implicit location: *0x4.
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4, DW_OP_deref, DW_OP_stack_value},
|
|
{}, {}, &exe_ctx),
|
|
llvm::HasValue(GetScalar(32, 0x07060504, false)));
|
|
// Memory location: *(*0x4).
|
|
// Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4, DW_OP_deref}, {}, {}, &exe_ctx),
|
|
llvm::HasValue(Scalar(LLDB_INVALID_ADDRESS)));
|
|
// Memory location: *0x4.
|
|
// Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
|
|
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4}, {}, {}, &exe_ctx),
|
|
llvm::HasValue(Scalar(4)));
|
|
// Implicit location: *0x4.
|
|
// Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
|
|
EXPECT_THAT_EXPECTED(
|
|
Evaluate({DW_OP_lit4, DW_OP_deref, DW_OP_stack_value}, {}, {}, &exe_ctx),
|
|
llvm::HasValue(GetScalar(32, 0x07060504, false)));
|
|
}
|
|
|
|
TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr) {
|
|
// Set up a wasm target
|
|
ArchSpec arch("wasm32-unknown-unknown-wasm");
|
|
lldb::PlatformSP host_platform_sp =
|
|
platform_linux::PlatformLinux::CreateInstance(true, &arch);
|
|
ASSERT_TRUE(host_platform_sp);
|
|
Platform::SetHostPlatform(host_platform_sp);
|
|
lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
|
|
ASSERT_TRUE(debugger_sp);
|
|
lldb::TargetSP target_sp;
|
|
lldb::PlatformSP platform_sp;
|
|
debugger_sp->GetTargetList().CreateTarget(*debugger_sp, "", arch,
|
|
lldb_private::eLoadDependentsNo,
|
|
platform_sp, target_sp);
|
|
|
|
ExecutionContext exe_ctx(target_sp, false);
|
|
// DW_OP_addr takes a single operand of address size width:
|
|
uint8_t expr[] = {DW_OP_addr, 0x40, 0x0, 0x0, 0x0};
|
|
DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
|
|
/*addr_size*/ 4);
|
|
Value result;
|
|
Status status;
|
|
ASSERT_TRUE(DWARFExpression::Evaluate(
|
|
&exe_ctx, /*reg_ctx*/ nullptr, /*module_sp*/ {}, extractor,
|
|
/*unit*/ nullptr, lldb::eRegisterKindLLDB,
|
|
/*initial_value_ptr*/ nullptr,
|
|
/*object_address_ptr*/ nullptr, result, &status))
|
|
<< status.ToError();
|
|
|
|
ASSERT_EQ(result.GetValueType(), Value::ValueType::LoadAddress);
|
|
}
|
|
|
|
TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr_index) {
|
|
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
|
|
Attributes:
|
|
- Attribute: DW_AT_addr_base
|
|
Form: DW_FORM_sec_offset
|
|
|
|
debug_info:
|
|
- Version: 5
|
|
AddrSize: 4
|
|
UnitType: DW_UT_compile
|
|
Entries:
|
|
- AbbrCode: 0x00000001
|
|
Values:
|
|
- Value: 0x8 # Offset of the first Address past the header
|
|
- AbbrCode: 0x0
|
|
|
|
debug_addr:
|
|
- Version: 5
|
|
AddressSize: 4
|
|
Entries:
|
|
- Address: 0x1234
|
|
- Address: 0x5678
|
|
)";
|
|
|
|
// Can't use DWARFExpressionTester from above because subsystems overlap with
|
|
// the fixture.
|
|
SubsystemRAII<ObjectFileELF, SymbolFileDWARF> subsystems;
|
|
llvm::Expected<TestFile> file = TestFile::fromYaml(yamldata);
|
|
EXPECT_THAT_EXPECTED(file, llvm::Succeeded());
|
|
auto module_sp = std::make_shared<Module>(file->moduleSpec());
|
|
auto *dwarf_cu = llvm::cast<SymbolFileDWARF>(module_sp->GetSymbolFile())
|
|
->DebugInfo()
|
|
.GetUnitAtIndex(0);
|
|
ASSERT_TRUE(dwarf_cu);
|
|
dwarf_cu->ExtractDIEsIfNeeded();
|
|
|
|
// Set up a wasm target
|
|
ArchSpec arch("wasm32-unknown-unknown-wasm");
|
|
lldb::PlatformSP host_platform_sp =
|
|
platform_linux::PlatformLinux::CreateInstance(true, &arch);
|
|
ASSERT_TRUE(host_platform_sp);
|
|
Platform::SetHostPlatform(host_platform_sp);
|
|
lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
|
|
ASSERT_TRUE(debugger_sp);
|
|
lldb::TargetSP target_sp;
|
|
lldb::PlatformSP platform_sp;
|
|
debugger_sp->GetTargetList().CreateTarget(*debugger_sp, "", arch,
|
|
lldb_private::eLoadDependentsNo,
|
|
platform_sp, target_sp);
|
|
|
|
ExecutionContext exe_ctx(target_sp, false);
|
|
// DW_OP_addrx takes a single leb128 operand, the index in the addr table:
|
|
uint8_t expr[] = {DW_OP_addrx, 0x01};
|
|
DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
|
|
/*addr_size*/ 4);
|
|
Value result;
|
|
Status status;
|
|
ASSERT_TRUE(DWARFExpression::Evaluate(
|
|
&exe_ctx, /*reg_ctx*/ nullptr, /*module_sp*/ {}, extractor, dwarf_cu,
|
|
lldb::eRegisterKindLLDB,
|
|
/*initial_value_ptr*/ nullptr,
|
|
/*object_address_ptr*/ nullptr, result, &status))
|
|
<< status.ToError();
|
|
|
|
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);
|
|
}
|