Skip to content

Commit 088bf55

Browse files
committed
[lldb] Handle deref of register and implicit locations
This commit modifies how we handle the deref operation for register and implicit locations on the stack. For a typical memory location a deref operation will read the value from memory. For register and implicit locations the deref operation will read the value from the register or its implicit location. In lldb we eagerly read register and implicit values and push them on the stack so the deref operation for these becomes a "no-op" that leaves the value on the stack and updates the tracked location kind. The motivation for this change is to handle `DW_OP_deref*` operations on location descriptions as described by the heterogenious debugging [extensions](https://rocm.docs.amd.com/projects/llvm-project/en/latest/LLVM/llvm/html/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html#a-2-5-4-4-4-register-location-description-operations). Specifically, for register locations it states > These operations obtain a register location. To fetch the contents of > a register, it is necessary to use DW_OP_regval_type, use one of the > DW_OP_breg* register-based addressing operations, or use DW_OP_deref* on > a register location description. My understanding is that this is the intended behavior from dwarf5 as well and is not a change in behavior.
1 parent 89206de commit 088bf55

File tree

2 files changed

+146
-5
lines changed

2 files changed

+146
-5
lines changed

lldb/source/Expression/DWARFExpression.cpp

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -861,13 +861,30 @@ ResolveLoadAddress(ExecutionContext *exe_ctx, lldb::ModuleSP &module_sp,
861861
return load_addr;
862862
}
863863

864-
static llvm::Error Evaluate_DW_OP_deref(DWARFExpression::Stack &stack,
865-
ExecutionContext *exe_ctx,
866-
lldb::ModuleSP module_sp,
867-
Process *process) {
864+
static llvm::Error Evaluate_DW_OP_deref(
865+
DWARFExpression::Stack &stack, ExecutionContext *exe_ctx,
866+
lldb::ModuleSP module_sp, Process *process,
867+
LocationDescriptionKind &dwarf4_location_description_kind) {
868868
if (stack.empty())
869869
return llvm::createStringError("expression stack empty for DW_OP_deref");
870870

871+
// Handle deref of a register or implicit location.
872+
// When the current value is a register or implicit location description then
873+
// a deref operation should read the value from that location. We eagerly
874+
// read the register and implicit values and so its value is already on top of
875+
// the stack. We just need to reset the value context and description to their
876+
// defaults.
877+
if (dwarf4_location_description_kind == Register ||
878+
dwarf4_location_description_kind == Implicit) {
879+
// Reset context to default values.
880+
dwarf4_location_description_kind = Memory;
881+
stack.back().ClearContext();
882+
883+
// The value is already on top of the stack so there is nothing
884+
// more to do here.
885+
return llvm::Error::success();
886+
}
887+
871888
const Value::ValueType value_type = stack.back().GetValueType();
872889
switch (value_type) {
873890
case Value::ValueType::HostAddress: {
@@ -1080,7 +1097,8 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
10801097
// target machine.
10811098
case DW_OP_deref: {
10821099
if (llvm::Error err =
1083-
Evaluate_DW_OP_deref(stack, exe_ctx, module_sp, process))
1100+
Evaluate_DW_OP_deref(stack, exe_ctx, module_sp, process,
1101+
dwarf4_location_description_kind))
10841102
return err;
10851103
} break;
10861104

@@ -1106,6 +1124,25 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
11061124
return llvm::createStringError(
11071125
"Invalid address size for DW_OP_deref_size: %d\n", size);
11081126
}
1127+
1128+
// Deref a register or implicit location and truncate the value to `size`
1129+
// bytes. See the corresponding comment in DW_OP_deref for more details on
1130+
// why we deref these locations this way.
1131+
if (dwarf4_location_description_kind == Register ||
1132+
dwarf4_location_description_kind == Implicit) {
1133+
// Reset context to default values.
1134+
dwarf4_location_description_kind = Memory;
1135+
stack.back().ClearContext();
1136+
1137+
// Truncate the value on top of the stack to *size* bytes then
1138+
// extend to the size of an address (e.g. generic type).
1139+
Scalar scalar = stack.back().GetScalar();
1140+
scalar.TruncOrExtendTo(size * 8, /*sign=*/false);
1141+
scalar.TruncOrExtendTo(opcodes.GetAddressByteSize() * 8,
1142+
/*sign=*/false);
1143+
stack.back().GetScalar() = scalar;
1144+
break;
1145+
}
11091146
Value::ValueType value_type = stack.back().GetValueType();
11101147
switch (value_type) {
11111148
case Value::ValueType::HostAddress: {

lldb/unittests/Expression/DWARFExpressionTest.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,3 +1216,107 @@ TEST_F(DWARFExpressionMockProcessTestWithAArch, DW_op_deref_no_ptr_fixing) {
12161216
llvm::Expected<Value> result_deref = evaluate_expr(expr_deref);
12171217
EXPECT_THAT_EXPECTED(result_deref, ExpectLoadAddress(expected_value));
12181218
}
1219+
1220+
TEST_F(DWARFExpressionMockProcessTest, deref_register) {
1221+
TestContext test_ctx;
1222+
constexpr uint32_t reg_r0 = 0x504;
1223+
MockMemory::Map memory = {
1224+
{{0x004, 4}, {0x1, 0x2, 0x3, 0x4}},
1225+
{{0x504, 4}, {0xa, 0xb, 0xc, 0xd}},
1226+
{{0x505, 4}, {0x5, 0x6, 0x7, 0x8}},
1227+
};
1228+
ASSERT_TRUE(CreateTestContext(&test_ctx, "i386-pc-linux",
1229+
RegisterValue(reg_r0), memory, memory));
1230+
1231+
ExecutionContext exe_ctx(test_ctx.process_sp);
1232+
MockDwarfDelegate delegate = MockDwarfDelegate::Dwarf5();
1233+
auto Eval = [&](llvm::ArrayRef<uint8_t> expr_data) {
1234+
ExecutionContext exe_ctx(test_ctx.process_sp);
1235+
return Evaluate(expr_data, {}, &delegate, &exe_ctx,
1236+
test_ctx.reg_ctx_sp.get());
1237+
};
1238+
1239+
// Reads from the register r0.
1240+
// Sets the context to RegisterInfo so we know this is a register location.
1241+
EXPECT_THAT_EXPECTED(Eval({DW_OP_reg0}),
1242+
ExpectScalar(reg_r0, Value::ContextType::RegisterInfo));
1243+
1244+
// Reads from the location(register r0).
1245+
// Clears the context so we know this is a value not a location.
1246+
EXPECT_THAT_EXPECTED(Eval({DW_OP_reg0, DW_OP_deref}),
1247+
ExpectLoadAddress(reg_r0, Value::ContextType::Invalid));
1248+
1249+
// Reads from the location(register r0) and adds the value to the host buffer.
1250+
// The evaluator should implicitly convert it to a memory location when
1251+
// added to a composite value and should add the contents of memory[r0]
1252+
// to the host buffer.
1253+
EXPECT_THAT_EXPECTED(Eval({DW_OP_reg0, DW_OP_deref, DW_OP_piece, 4}),
1254+
ExpectHostAddress({0xa, 0xb, 0xc, 0xd}));
1255+
1256+
// Reads from the location(register r0) and truncates the value to one byte.
1257+
// Clears the context so we know this is a value not a location.
1258+
EXPECT_THAT_EXPECTED(
1259+
Eval({DW_OP_reg0, DW_OP_deref_size, 1}),
1260+
ExpectLoadAddress(reg_r0 & 0xff, Value::ContextType::Invalid));
1261+
1262+
// Reads from the location(register r0) and truncates to one byte then adds
1263+
// the value to the host buffer. The evaluator should implicitly convert it to
1264+
// a memory location when added to a composite value and should add the
1265+
// contents of memory[r0 & 0xff] to the host buffer.
1266+
EXPECT_THAT_EXPECTED(Eval({DW_OP_reg0, DW_OP_deref_size, 1, DW_OP_piece, 4}),
1267+
ExpectHostAddress({0x1, 0x2, 0x3, 0x4}));
1268+
1269+
// Reads from the register r0 + 1.
1270+
EXPECT_THAT_EXPECTED(
1271+
Eval({DW_OP_breg0, 1}),
1272+
ExpectLoadAddress(reg_r0 + 1, Value::ContextType::Invalid));
1273+
1274+
// Reads from address r0 + 1, which contains the bytes [5,6,7,8].
1275+
EXPECT_THAT_EXPECTED(
1276+
Eval({DW_OP_breg0, 1, DW_OP_deref}),
1277+
ExpectLoadAddress(0x08070605, Value::ContextType::Invalid));
1278+
}
1279+
1280+
TEST_F(DWARFExpressionMockProcessTest, deref_implicit_value) {
1281+
TestContext test_ctx;
1282+
MockMemory::Map memory = {
1283+
{{0x4, 1}, {0x1}},
1284+
{{0x4, 4}, {0x1, 0x2, 0x3, 0x4}},
1285+
};
1286+
ASSERT_TRUE(CreateTestContext(&test_ctx, "i386-pc-linux", {}, memory));
1287+
1288+
ExecutionContext exe_ctx(test_ctx.process_sp);
1289+
MockDwarfDelegate delegate = MockDwarfDelegate::Dwarf5();
1290+
auto Eval = [&](llvm::ArrayRef<uint8_t> expr_data) {
1291+
ExecutionContext exe_ctx(test_ctx.process_sp);
1292+
return Evaluate(expr_data, {}, &delegate, &exe_ctx,
1293+
test_ctx.reg_ctx_sp.get());
1294+
};
1295+
1296+
// Creates an implicit location with a value of 4.
1297+
EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4, DW_OP_stack_value}),
1298+
ExpectScalar(0x4));
1299+
1300+
// Creates an implicit location with a value of 4. The deref reads the value
1301+
// out of the location and implicitly converts it to a load address.
1302+
EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4, DW_OP_stack_value, DW_OP_deref}),
1303+
ExpectLoadAddress(0x4));
1304+
1305+
// Creates an implicit location with a value of 0x504 (uleb128(0x504) =
1306+
// 0xa84). The deref reads the low byte out of the location and implicitly
1307+
// converts it to a load address.
1308+
EXPECT_THAT_EXPECTED(
1309+
Eval({DW_OP_constu, 0x84, 0xa, DW_OP_stack_value, DW_OP_deref_size, 1}),
1310+
ExpectLoadAddress(0x4));
1311+
1312+
// The tests below are similar to the ones above, but there is no implicit
1313+
// location created by a stack_value operation. They are provided here as a
1314+
// reference to contrast with the above tests.
1315+
EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4}), ExpectLoadAddress(0x4));
1316+
1317+
EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4, DW_OP_deref}),
1318+
ExpectLoadAddress(0x04030201));
1319+
1320+
EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4, DW_OP_deref_size, 1}),
1321+
ExpectLoadAddress(0x01));
1322+
}

0 commit comments

Comments
 (0)