Skip to content

Commit 7dc84e2

Browse files
authored
[lldb] Support reading DW_OP_piece from file address (#94026)
We received a bug report where someone was trying to print a global variable without a process. This would succeed in a debug build but fail in a on optimized build. We traced the issue back to the location being described by a DW_OP_addr + DW_OP_piece. The issue is that the DWARF expression evaluator only support reading pieces from a load address. There's no reason it cannot do the same for a file address, and indeed, that solves the problem. I unsuccessfully tried to craft a test case to illustrate the original example, using a global struct and trying to trick the compiler into breaking it apart with SROA. Instead I wrote a unit test that uses a mock target to read memory from. rdar://127435923
1 parent dfd1a2f commit 7dc84e2

File tree

3 files changed

+97
-29
lines changed

3 files changed

+97
-29
lines changed

lldb/include/lldb/Target/Target.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,9 +1077,11 @@ class Target : public std::enable_shared_from_this<Target>,
10771077
// section, then read from the file cache
10781078
// 2 - if there is a process, then read from memory
10791079
// 3 - if there is no process, then read from the file cache
1080-
size_t ReadMemory(const Address &addr, void *dst, size_t dst_len,
1081-
Status &error, bool force_live_memory = false,
1082-
lldb::addr_t *load_addr_ptr = nullptr);
1080+
//
1081+
// The method is virtual for mocking in the unit tests.
1082+
virtual size_t ReadMemory(const Address &addr, void *dst, size_t dst_len,
1083+
Status &error, bool force_live_memory = false,
1084+
lldb::addr_t *load_addr_ptr = nullptr);
10831085

10841086
size_t ReadCStringFromMemory(const Address &addr, std::string &out_str,
10851087
Status &error, bool force_live_memory = false);
@@ -1615,7 +1617,7 @@ class Target : public std::enable_shared_from_this<Target>,
16151617

16161618
TargetStats &GetStatistics() { return m_stats; }
16171619

1618-
private:
1620+
protected:
16191621
/// Construct with optional file and arch.
16201622
///
16211623
/// This member is private. Clients must use

lldb/source/Expression/DWARFExpression.cpp

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2123,23 +2123,29 @@ bool DWARFExpression::Evaluate(
21232123

21242124
const Value::ValueType curr_piece_source_value_type =
21252125
curr_piece_source_value.GetValueType();
2126+
Scalar &scalar = curr_piece_source_value.GetScalar();
2127+
const lldb::addr_t addr = scalar.ULongLong(LLDB_INVALID_ADDRESS);
21262128
switch (curr_piece_source_value_type) {
21272129
case Value::ValueType::Invalid:
21282130
return false;
21292131
case Value::ValueType::LoadAddress:
2130-
if (process) {
2132+
case Value::ValueType::FileAddress: {
2133+
if (target) {
21312134
if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) {
2132-
lldb::addr_t load_addr =
2133-
curr_piece_source_value.GetScalar().ULongLong(
2134-
LLDB_INVALID_ADDRESS);
2135-
if (process->ReadMemory(
2136-
load_addr, curr_piece.GetBuffer().GetBytes(),
2137-
piece_byte_size, error) != piece_byte_size) {
2138-
if (error_ptr)
2135+
if (target->ReadMemory(addr, curr_piece.GetBuffer().GetBytes(),
2136+
piece_byte_size, error,
2137+
/*force_live_memory=*/false) !=
2138+
piece_byte_size) {
2139+
if (error_ptr) {
2140+
const char *addr_type = (curr_piece_source_value_type ==
2141+
Value::ValueType::LoadAddress)
2142+
? "load"
2143+
: "file";
21392144
error_ptr->SetErrorStringWithFormat(
21402145
"failed to read memory DW_OP_piece(%" PRIu64
2141-
") from 0x%" PRIx64,
2142-
piece_byte_size, load_addr);
2146+
") from %s address 0x%" PRIx64,
2147+
piece_byte_size, addr_type, addr);
2148+
}
21432149
return false;
21442150
}
21452151
} else {
@@ -2151,28 +2157,19 @@ bool DWARFExpression::Evaluate(
21512157
return false;
21522158
}
21532159
}
2154-
break;
2155-
2156-
case Value::ValueType::FileAddress:
2157-
case Value::ValueType::HostAddress:
2158-
if (error_ptr) {
2159-
lldb::addr_t addr = curr_piece_source_value.GetScalar().ULongLong(
2160-
LLDB_INVALID_ADDRESS);
2160+
} break;
2161+
case Value::ValueType::HostAddress: {
2162+
if (error_ptr)
21612163
error_ptr->SetErrorStringWithFormat(
21622164
"failed to read memory DW_OP_piece(%" PRIu64
2163-
") from %s address 0x%" PRIx64,
2164-
piece_byte_size, curr_piece_source_value.GetValueType() ==
2165-
Value::ValueType::FileAddress
2166-
? "file"
2167-
: "host",
2168-
addr);
2169-
}
2165+
") from host address 0x%" PRIx64,
2166+
piece_byte_size, addr);
21702167
return false;
2168+
} break;
21712169

21722170
case Value::ValueType::Scalar: {
21732171
uint32_t bit_size = piece_byte_size * 8;
21742172
uint32_t bit_offset = 0;
2175-
Scalar &scalar = curr_piece_source_value.GetScalar();
21762173
if (!scalar.ExtractBitfield(
21772174
bit_size, bit_offset)) {
21782175
if (error_ptr)

lldb/unittests/Expression/DWARFExpressionTest.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,34 @@ class DWARFExpressionMockProcessTest : public ::testing::Test {
9595
}
9696
};
9797

98+
// NB: This class doesn't use the override keyword to avoid
99+
// -Winconsistent-missing-override warnings from the compiler. The
100+
// inconsistency comes from the overriding definitions in the MOCK_*** macros.
101+
class MockTarget : public Target {
102+
public:
103+
MockTarget(Debugger &debugger, const ArchSpec &target_arch,
104+
const lldb::PlatformSP &platform_sp)
105+
: Target(debugger, target_arch, platform_sp, true) {}
106+
107+
MOCK_METHOD2(ReadMemory,
108+
llvm::Expected<std::vector<uint8_t>>(lldb::addr_t addr,
109+
size_t size));
110+
111+
size_t ReadMemory(const Address &addr, void *dst, size_t dst_len,
112+
Status &error, bool force_live_memory = false,
113+
lldb::addr_t *load_addr_ptr = nullptr) /*override*/ {
114+
auto expected_memory = this->ReadMemory(addr.GetOffset(), dst_len);
115+
if (!expected_memory) {
116+
llvm::consumeError(expected_memory.takeError());
117+
return 0;
118+
}
119+
const size_t bytes_read = expected_memory->size();
120+
assert(bytes_read <= dst_len);
121+
std::memcpy(dst, expected_memory->data(), bytes_read);
122+
return bytes_read;
123+
}
124+
};
125+
98126
TEST(DWARFExpression, DW_OP_pick) {
99127
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 0}),
100128
llvm::HasValue(0));
@@ -768,3 +796,44 @@ TEST(DWARFExpression, ExtensionsDWO) {
768796

769797
testExpressionVendorExtensions(dwo_module_sp, *dwo_dwarf_unit);
770798
}
799+
800+
TEST_F(DWARFExpressionMockProcessTest, DW_OP_piece_file_addr) {
801+
using ::testing::ByMove;
802+
using ::testing::ElementsAre;
803+
using ::testing::Return;
804+
805+
// Set up a mock process.
806+
ArchSpec arch("i386-pc-linux");
807+
Platform::SetHostPlatform(
808+
platform_linux::PlatformLinux::CreateInstance(true, &arch));
809+
lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
810+
ASSERT_TRUE(debugger_sp);
811+
lldb::PlatformSP platform_sp;
812+
auto target_sp =
813+
std::make_shared<MockTarget>(*debugger_sp, arch, platform_sp);
814+
ASSERT_TRUE(target_sp);
815+
ASSERT_TRUE(target_sp->GetArchitecture().IsValid());
816+
817+
EXPECT_CALL(*target_sp, ReadMemory(0x40, 1))
818+
.WillOnce(Return(ByMove(std::vector<uint8_t>{0x11})));
819+
EXPECT_CALL(*target_sp, ReadMemory(0x50, 1))
820+
.WillOnce(Return(ByMove(std::vector<uint8_t>{0x22})));
821+
822+
ExecutionContext exe_ctx(static_cast<lldb::TargetSP>(target_sp), false);
823+
824+
uint8_t expr[] = {DW_OP_addr, 0x40, 0x0, 0x0, 0x0, DW_OP_piece, 1,
825+
DW_OP_addr, 0x50, 0x0, 0x0, 0x0, DW_OP_piece, 1};
826+
DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
827+
/*addr_size*/ 4);
828+
Value result;
829+
Status status;
830+
ASSERT_TRUE(DWARFExpression::Evaluate(
831+
&exe_ctx, /*reg_ctx*/ nullptr, /*module_sp*/ {}, extractor,
832+
/*unit*/ nullptr, lldb::eRegisterKindLLDB,
833+
/*initial_value_ptr*/ nullptr,
834+
/*object_address_ptr*/ nullptr, result, &status))
835+
<< status.ToError();
836+
837+
ASSERT_EQ(result.GetValueType(), Value::ValueType::HostAddress);
838+
ASSERT_THAT(result.GetBuffer().GetData(), ElementsAre(0x11, 0x22));
839+
}

0 commit comments

Comments
 (0)