diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 4f9d6ebf27bf0..3820e1e002ff9 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -861,13 +861,30 @@ ResolveLoadAddress(ExecutionContext *exe_ctx, lldb::ModuleSP &module_sp, return load_addr; } -static llvm::Error Evaluate_DW_OP_deref(DWARFExpression::Stack &stack, - ExecutionContext *exe_ctx, - lldb::ModuleSP module_sp, - Process *process) { +static llvm::Error Evaluate_DW_OP_deref( + DWARFExpression::Stack &stack, ExecutionContext *exe_ctx, + lldb::ModuleSP module_sp, Process *process, + LocationDescriptionKind &dwarf4_location_description_kind) { if (stack.empty()) return llvm::createStringError("expression stack empty for DW_OP_deref"); + // Handle deref of a register or implicit location. + // When the current value is a register or implicit location description then + // a deref operation should read the value from that location. We eagerly + // read the register and implicit values and so its value is already on top of + // the stack. We just need to reset the value context and description to their + // defaults. + if (dwarf4_location_description_kind == Register || + dwarf4_location_description_kind == Implicit) { + // Reset context to default values. + dwarf4_location_description_kind = Memory; + stack.back().ClearContext(); + + // The value is already on top of the stack so there is nothing + // more to do here. + return llvm::Error::success(); + } + const Value::ValueType value_type = stack.back().GetValueType(); switch (value_type) { case Value::ValueType::HostAddress: { @@ -1080,7 +1097,8 @@ llvm::Expected DWARFExpression::Evaluate( // target machine. case DW_OP_deref: { if (llvm::Error err = - Evaluate_DW_OP_deref(stack, exe_ctx, module_sp, process)) + Evaluate_DW_OP_deref(stack, exe_ctx, module_sp, process, + dwarf4_location_description_kind)) return err; } break; @@ -1106,6 +1124,25 @@ llvm::Expected DWARFExpression::Evaluate( return llvm::createStringError( "Invalid address size for DW_OP_deref_size: %d\n", size); } + + // Deref a register or implicit location and truncate the value to `size` + // bytes. See the corresponding comment in DW_OP_deref for more details on + // why we deref these locations this way. + if (dwarf4_location_description_kind == Register || + dwarf4_location_description_kind == Implicit) { + // Reset context to default values. + dwarf4_location_description_kind = Memory; + stack.back().ClearContext(); + + // Truncate the value on top of the stack to *size* bytes then + // extend to the size of an address (e.g. generic type). + Scalar scalar = stack.back().GetScalar(); + scalar.TruncOrExtendTo(size * 8, /*sign=*/false); + scalar.TruncOrExtendTo(opcodes.GetAddressByteSize() * 8, + /*sign=*/false); + stack.back().GetScalar() = scalar; + break; + } Value::ValueType value_type = stack.back().GetValueType(); switch (value_type) { case Value::ValueType::HostAddress: { diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp index e0c2193d27c36..112159e261835 100644 --- a/lldb/unittests/Expression/DWARFExpressionTest.cpp +++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp @@ -1216,3 +1216,107 @@ TEST_F(DWARFExpressionMockProcessTestWithAArch, DW_op_deref_no_ptr_fixing) { llvm::Expected result_deref = evaluate_expr(expr_deref); EXPECT_THAT_EXPECTED(result_deref, ExpectLoadAddress(expected_value)); } + +TEST_F(DWARFExpressionMockProcessTest, deref_register) { + TestContext test_ctx; + constexpr uint32_t reg_r0 = 0x504; + MockMemory::Map memory = { + {{0x004, 4}, {0x1, 0x2, 0x3, 0x4}}, + {{0x504, 4}, {0xa, 0xb, 0xc, 0xd}}, + {{0x505, 4}, {0x5, 0x6, 0x7, 0x8}}, + }; + ASSERT_TRUE(CreateTestContext(&test_ctx, "i386-pc-linux", + RegisterValue(reg_r0), memory, memory)); + + ExecutionContext exe_ctx(test_ctx.process_sp); + MockDwarfDelegate delegate = MockDwarfDelegate::Dwarf5(); + auto Eval = [&](llvm::ArrayRef expr_data) { + ExecutionContext exe_ctx(test_ctx.process_sp); + return Evaluate(expr_data, {}, &delegate, &exe_ctx, + test_ctx.reg_ctx_sp.get()); + }; + + // Reads from the register r0. + // Sets the context to RegisterInfo so we know this is a register location. + EXPECT_THAT_EXPECTED(Eval({DW_OP_reg0}), + ExpectScalar(reg_r0, Value::ContextType::RegisterInfo)); + + // Reads from the location(register r0). + // Clears the context so we know this is a value not a location. + EXPECT_THAT_EXPECTED(Eval({DW_OP_reg0, DW_OP_deref}), + ExpectLoadAddress(reg_r0, Value::ContextType::Invalid)); + + // Reads from the location(register r0) and adds the value to the host buffer. + // The evaluator should implicitly convert it to a memory location when + // added to a composite value and should add the contents of memory[r0] + // to the host buffer. + EXPECT_THAT_EXPECTED(Eval({DW_OP_reg0, DW_OP_deref, DW_OP_piece, 4}), + ExpectHostAddress({0xa, 0xb, 0xc, 0xd})); + + // Reads from the location(register r0) and truncates the value to one byte. + // Clears the context so we know this is a value not a location. + EXPECT_THAT_EXPECTED( + Eval({DW_OP_reg0, DW_OP_deref_size, 1}), + ExpectLoadAddress(reg_r0 & 0xff, Value::ContextType::Invalid)); + + // Reads from the location(register r0) and truncates to one byte then adds + // the value to the host buffer. The evaluator should implicitly convert it to + // a memory location when added to a composite value and should add the + // contents of memory[r0 & 0xff] to the host buffer. + EXPECT_THAT_EXPECTED(Eval({DW_OP_reg0, DW_OP_deref_size, 1, DW_OP_piece, 4}), + ExpectHostAddress({0x1, 0x2, 0x3, 0x4})); + + // Reads from the register r0 + 1. + EXPECT_THAT_EXPECTED( + Eval({DW_OP_breg0, 1}), + ExpectLoadAddress(reg_r0 + 1, Value::ContextType::Invalid)); + + // Reads from address r0 + 1, which contains the bytes [5,6,7,8]. + EXPECT_THAT_EXPECTED( + Eval({DW_OP_breg0, 1, DW_OP_deref}), + ExpectLoadAddress(0x08070605, Value::ContextType::Invalid)); +} + +TEST_F(DWARFExpressionMockProcessTest, deref_implicit_value) { + TestContext test_ctx; + MockMemory::Map memory = { + {{0x4, 1}, {0x1}}, + {{0x4, 4}, {0x1, 0x2, 0x3, 0x4}}, + }; + ASSERT_TRUE(CreateTestContext(&test_ctx, "i386-pc-linux", {}, memory)); + + ExecutionContext exe_ctx(test_ctx.process_sp); + MockDwarfDelegate delegate = MockDwarfDelegate::Dwarf5(); + auto Eval = [&](llvm::ArrayRef expr_data) { + ExecutionContext exe_ctx(test_ctx.process_sp); + return Evaluate(expr_data, {}, &delegate, &exe_ctx, + test_ctx.reg_ctx_sp.get()); + }; + + // Creates an implicit location with a value of 4. + EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4, DW_OP_stack_value}), + ExpectScalar(0x4)); + + // Creates an implicit location with a value of 4. The deref reads the value + // out of the location and implicitly converts it to a load address. + EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4, DW_OP_stack_value, DW_OP_deref}), + ExpectLoadAddress(0x4)); + + // Creates an implicit location with a value of 0x504 (uleb128(0x504) = + // 0xa84). The deref reads the low byte out of the location and implicitly + // converts it to a load address. + EXPECT_THAT_EXPECTED( + Eval({DW_OP_constu, 0x84, 0xa, DW_OP_stack_value, DW_OP_deref_size, 1}), + ExpectLoadAddress(0x4)); + + // The tests below are similar to the ones above, but there is no implicit + // location created by a stack_value operation. They are provided here as a + // reference to contrast with the above tests. + EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4}), ExpectLoadAddress(0x4)); + + EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4, DW_OP_deref}), + ExpectLoadAddress(0x04030201)); + + EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4, DW_OP_deref_size, 1}), + ExpectLoadAddress(0x01)); +}