Skip to content

Commit 188b074

Browse files
committed
Support dereferencing a DWARF scalar stack value
Swift async functions receive function arguments inside a heap-allocated data structure, similar to how ObjC block captures or C++ coroutine arguments are implement. In DWARF they are described relative to an entry value that produces a pointer into that heap object. At typical location looks like DW_OP_entry_value [ DW_OP_reg14 ] DW_OP_deref DW_OP_plus_uconst 32 DW_OP_deref This allows the unwinder (which has special ABI knowledge to restore the contents of r14) to push the base address onto the stack thus allowing the deref/offset operations to continue. The result of the entry value is a scalar, because DW_OP_reg14 is a register location — as it should be since we want to restore the pointer value contained in r14 at the beginning of the function and not the historical memory contents it was pointing to. The entry value should restore the address, which is still valid, not the contents at function entry. To make this work, we need to allow LLDB to dereference Scalar stack results like load addresses, which is what this patch does. Unfortunately it is difficult to test this in isolation, since the DWARFExpression unit test doesn't have a process. Differential Revision: https://reviews.llvm.org/D96549
1 parent 057efa9 commit 188b074

File tree

3 files changed

+78
-9
lines changed

3 files changed

+78
-9
lines changed

lldb/source/Expression/DWARFExpression.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,7 @@ bool DWARFExpression::Evaluate(
10671067
stack.back().SetValueType(Value::ValueType::LoadAddress);
10681068
// Fall through to load address code below...
10691069
} LLVM_FALLTHROUGH;
1070+
case Value::ValueType::Scalar:
10701071
case Value::ValueType::LoadAddress:
10711072
if (exe_ctx) {
10721073
if (process) {
@@ -1099,7 +1100,6 @@ bool DWARFExpression::Evaluate(
10991100
}
11001101
break;
11011102

1102-
case Value::ValueType::Scalar:
11031103
case Value::ValueType::Invalid:
11041104
if (error_ptr)
11051105
error_ptr->SetErrorString("Invalid value type for DW_OP_deref.\n");
@@ -1170,6 +1170,7 @@ bool DWARFExpression::Evaluate(
11701170
stack.back().GetScalar() = ptr;
11711171
stack.back().ClearContext();
11721172
} break;
1173+
case Value::ValueType::Scalar:
11731174
case Value::ValueType::LoadAddress:
11741175
if (exe_ctx) {
11751176
if (process) {
@@ -1221,7 +1222,6 @@ bool DWARFExpression::Evaluate(
12211222
}
12221223
break;
12231224

1224-
case Value::ValueType::Scalar:
12251225
case Value::ValueType::FileAddress:
12261226
case Value::ValueType::Invalid:
12271227
if (error_ptr)

lldb/unittests/Expression/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ add_lldb_unittest(ExpressionTests
77

88
LINK_LIBS
99
lldbCore
10+
lldbPluginObjectFileELF
11+
lldbPluginPlatformLinux
1012
lldbPluginExpressionParserClang
1113
lldbPluginTypeSystemClang
1214
lldbUtility

lldb/unittests/Expression/DWARFExpressionTest.cpp

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "lldb/Expression/DWARFExpression.h"
10+
#include "Plugins/Platform/Linux/PlatformLinux.h"
1011
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
1112
#include "TestingSupport/Symbol/YAMLModuleTester.h"
1213
#include "lldb/Core/Value.h"
14+
#include "lldb/Core/Debugger.h"
1315
#include "lldb/Core/dwarf.h"
16+
#include "lldb/Host/HostInfo.h"
1417
#include "lldb/Symbol/ObjectFile.h"
18+
#include "lldb/Utility/Reproducer.h"
1519
#include "lldb/Utility/StreamString.h"
1620
#include "llvm/ADT/StringExtras.h"
1721
#include "llvm/Testing/Support/Error.h"
@@ -21,16 +25,17 @@ using namespace lldb_private;
2125

2226
static llvm::Expected<Scalar> Evaluate(llvm::ArrayRef<uint8_t> expr,
2327
lldb::ModuleSP module_sp = {},
24-
DWARFUnit *unit = nullptr) {
28+
DWARFUnit *unit = nullptr,
29+
ExecutionContext *exe_ctx = nullptr) {
2530
DataExtractor extractor(expr.data(), expr.size(), lldb::eByteOrderLittle,
2631
/*addr_size*/ 4);
2732
Value result;
2833
Status status;
29-
if (!DWARFExpression::Evaluate(
30-
/*exe_ctx*/ nullptr, /*reg_ctx*/ nullptr, module_sp, extractor, unit,
31-
lldb::eRegisterKindLLDB,
32-
/*initial_value_ptr*/ nullptr,
33-
/*object_address_ptr*/ nullptr, result, &status))
34+
if (!DWARFExpression::Evaluate(exe_ctx, /*reg_ctx*/ nullptr, module_sp,
35+
extractor, unit, lldb::eRegisterKindLLDB,
36+
/*initial_value_ptr*/ nullptr,
37+
/*object_address_ptr*/ nullptr, result,
38+
&status))
3439
return status.ToError();
3540

3641
switch (result.GetValueType()) {
@@ -66,6 +71,23 @@ static Scalar GetScalar(unsigned bits, uint64_t value, bool sign) {
6671
return scalar;
6772
}
6873

74+
/// This is needed for the tests that use a mock process.
75+
class DWARFExpressionMockProcessTest : public ::testing::Test {
76+
public:
77+
void SetUp() override {
78+
llvm::cantFail(repro::Reproducer::Initialize(repro::ReproducerMode::Off, {}));
79+
FileSystem::Initialize();
80+
HostInfo::Initialize();
81+
platform_linux::PlatformLinux::Initialize();
82+
}
83+
void TearDown() override {
84+
platform_linux::PlatformLinux::Terminate();
85+
HostInfo::Terminate();
86+
FileSystem::Terminate();
87+
repro::Reproducer::Terminate();
88+
}
89+
};
90+
6991
TEST(DWARFExpression, DW_OP_pick) {
7092
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 0}),
7193
llvm::HasValue(0));
@@ -277,6 +299,51 @@ TEST(DWARFExpression, DW_OP_unknown) {
277299
"Unhandled opcode DW_OP_unknown_ff in DWARFExpression"));
278300
}
279301

280-
TEST(DWARFExpression, DW_OP_deref) {
302+
TEST_F(DWARFExpressionMockProcessTest, DW_OP_deref) {
281303
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit0, DW_OP_deref}), llvm::Failed());
304+
305+
struct MockProcess : Process {
306+
using Process::Process;
307+
ConstString GetPluginName() override { return ConstString("mock process"); }
308+
uint32_t GetPluginVersion() override { return 0; }
309+
bool CanDebug(lldb::TargetSP target,
310+
bool plugin_specified_by_name) override {
311+
return false;
312+
};
313+
Status DoDestroy() override { return {}; }
314+
void RefreshStateAfterStop() override {}
315+
bool DoUpdateThreadList(ThreadList &old_thread_list,
316+
ThreadList &new_thread_list) override {
317+
return false;
318+
};
319+
size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
320+
Status &error) override {
321+
for (size_t i = 0; i < size; ++i)
322+
((char *)buf)[i] = (vm_addr + i) & 0xff;
323+
error.Clear();
324+
return size;
325+
}
326+
};
327+
328+
// Set up a mock process.
329+
ArchSpec arch("i386-pc-linux");
330+
Platform::SetHostPlatform(
331+
platform_linux::PlatformLinux::CreateInstance(true, &arch));
332+
lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
333+
ASSERT_TRUE(debugger_sp);
334+
lldb::TargetSP target_sp;
335+
lldb::PlatformSP platform_sp;
336+
debugger_sp->GetTargetList().CreateTarget(
337+
*debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);
338+
ASSERT_TRUE(target_sp);
339+
ASSERT_TRUE(target_sp->GetArchitecture().IsValid());
340+
ASSERT_TRUE(platform_sp);
341+
lldb::ListenerSP listener_sp(Listener::MakeListener("dummy"));
342+
lldb::ProcessSP process_sp =
343+
std::make_shared<MockProcess>(target_sp, listener_sp);
344+
ASSERT_TRUE(process_sp);
345+
346+
ExecutionContext exe_ctx(process_sp);
347+
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4, DW_OP_deref}, {}, {}, &exe_ctx),
348+
llvm::HasValue(GetScalar(32, 0x07060504, false)));
282349
}

0 commit comments

Comments
 (0)