diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -904,6 +904,52 @@ object_address_ptr, result, error_ptr); } +namespace { +/// The location description kinds described by the DWARF v5 +/// specification. Composite locations are handled out-of-band and +/// thus aren't part of the enum. +enum LocationDescriptionKind { + Empty, + Memory, + Register, + Implicit + /* Composite*/ +}; +/// Adjust value's ValueType according to the kind of location description. +void UpdateValueTypeFromLocationDescription(Log *log, const DWARFUnit *dwarf_cu, + LocationDescriptionKind kind, + Value *value = nullptr) { + // Note that this function is conflating DWARF expressions with + // DWARF location descriptions. Perhaps it would be better to define + // a wrapper for DWARFExpresssion::Eval() that deals with DWARF + // location descriptions (which consist of one or more DWARF + // expressions). But doing this would mean we'd also need factor the + // handling of DW_OP_(bit_)piece out of this function. + if (dwarf_cu && dwarf_cu->GetVersion() >= 4) { + const char *log_msg = "DWARF location description kind: %s"; + switch (kind) { + case Empty: + LLDB_LOGF(log, log_msg, "Empty"); + break; + case Memory: + LLDB_LOGF(log, log_msg, "Memory"); + if (value->GetValueType() == Value::ValueType::Scalar) + value->SetValueType(Value::ValueType::LoadAddress); + break; + case Register: + LLDB_LOGF(log, log_msg, "Register"); + value->SetValueType(Value::ValueType::Scalar); + break; + case Implicit: + LLDB_LOGF(log, log_msg, "Implicit"); + if (value->GetValueType() == Value::ValueType::LoadAddress) + value->SetValueType(Value::ValueType::Scalar); + break; + } + } +} +} // namespace + bool DWARFExpression::Evaluate( ExecutionContext *exe_ctx, RegisterContext *reg_ctx, lldb::ModuleSP module_sp, const DataExtractor &opcodes, @@ -952,6 +998,11 @@ !is_signed)); }; + // The default kind is a memory location. This is updated by any + // operation that changes this, such as DW_OP_stack_value, and reset + // by composition operations like DW_OP_piece. + LocationDescriptionKind dwarf4_location_description_kind = Memory; + while (opcodes.ValidOffset(offset)) { const lldb::offset_t op_offset = offset; const uint8_t op = opcodes.GetU8(&offset); @@ -1950,6 +2001,7 @@ case DW_OP_reg29: case DW_OP_reg30: case DW_OP_reg31: { + dwarf4_location_description_kind = Register; reg_num = op - DW_OP_reg0; if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) @@ -1962,6 +2014,7 @@ // ULEB128 literal operand that encodes the register. // DESCRIPTION: Push the value in register on the top of the stack. case DW_OP_regx: { + dwarf4_location_description_kind = Register; reg_num = opcodes.GetULEB128(&offset); if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) stack.push_back(tmp); @@ -2085,12 +2138,18 @@ // provides a way of describing how large a part of a variable a particular // DWARF expression refers to. case DW_OP_piece: { + LocationDescriptionKind piece_locdesc = dwarf4_location_description_kind; + // Reset for the next piece. + dwarf4_location_description_kind = Memory; + const uint64_t piece_byte_size = opcodes.GetULEB128(&offset); if (piece_byte_size > 0) { Value curr_piece; if (stack.empty()) { + UpdateValueTypeFromLocationDescription( + log, dwarf_cu, LocationDescriptionKind::Empty); // In a multi-piece expression, this means that the current piece is // not available. Fill with zeros for now by resizing the data and // appending it @@ -2106,6 +2165,8 @@ // Extract the current piece into "curr_piece" Value curr_piece_source_value(stack.back()); stack.pop_back(); + UpdateValueTypeFromLocationDescription(log, dwarf_cu, piece_locdesc, + &curr_piece_source_value); const Value::ValueType curr_piece_source_value_type = curr_piece_source_value.GetValueType(); @@ -2216,11 +2277,19 @@ case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3); if (stack.size() < 1) { + UpdateValueTypeFromLocationDescription(log, dwarf_cu, + LocationDescriptionKind::Empty); + // Reset for the next piece. + dwarf4_location_description_kind = Memory; if (error_ptr) error_ptr->SetErrorString( "Expression stack needs at least 1 item for DW_OP_bit_piece."); return false; } else { + UpdateValueTypeFromLocationDescription( + log, dwarf_cu, dwarf4_location_description_kind, &stack.back()); + // Reset for the next piece. + dwarf4_location_description_kind = Memory; const uint64_t piece_bit_size = opcodes.GetULEB128(&offset); const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset); switch (stack.back().GetValueType()) { @@ -2261,6 +2330,8 @@ // DESCRIPTION: Value is immediately stored in block in the debug info with // the memory representation of the target. case DW_OP_implicit_value: { + dwarf4_location_description_kind = Implicit; + const uint32_t len = opcodes.GetULEB128(&offset); const void *data = opcodes.GetData(&offset, len); @@ -2276,6 +2347,12 @@ break; } + case DW_OP_implicit_pointer: { + dwarf4_location_description_kind = Implicit; + LLDB_ERRORF(error_ptr, "Could not evaluate %s.", DW_OP_value_to_name(op)); + return false; + } + // OPCODE: DW_OP_push_object_address // OPERANDS: none // DESCRIPTION: Pushes the address of the object currently being @@ -2347,6 +2424,7 @@ // rather is a constant value. The value from the top of the stack is the // value to be used. This is the actual object value and not the location. case DW_OP_stack_value: + dwarf4_location_description_kind = Implicit; if (stack.empty()) { if (error_ptr) error_ptr->SetErrorString( @@ -2567,25 +2645,28 @@ // or DW_OP_bit_piece opcodes if (pieces.GetBuffer().GetByteSize()) { result = pieces; - } else { - if (error_ptr) - error_ptr->SetErrorString("Stack empty after evaluation."); - return false; + return true; } - } else { - if (log && log->GetVerbose()) { - size_t count = stack.size(); - LLDB_LOGF(log, "Stack after operation has %" PRIu64 " values:", - (uint64_t)count); - for (size_t i = 0; i < count; ++i) { - StreamString new_value; - new_value.Printf("[%" PRIu64 "]", (uint64_t)i); - stack[i].Dump(&new_value); - LLDB_LOGF(log, " %s", new_value.GetData()); - } + if (error_ptr) + error_ptr->SetErrorString("Stack empty after evaluation."); + return false; + } + + UpdateValueTypeFromLocationDescription( + log, dwarf_cu, dwarf4_location_description_kind, &stack.back()); + + if (log && log->GetVerbose()) { + size_t count = stack.size(); + LLDB_LOGF(log, + "Stack after operation has %" PRIu64 " values:", (uint64_t)count); + for (size_t i = 0; i < count; ++i) { + StreamString new_value; + new_value.Printf("[%" PRIu64 "]", (uint64_t)i); + stack[i].Dump(&new_value); + LLDB_LOGF(log, " %s", new_value.GetData()); } - result = stack.back(); } + result = stack.back(); return true; // Return true on success } diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp --- a/lldb/unittests/Expression/DWARFExpressionTest.cpp +++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp @@ -213,42 +213,45 @@ // // Leave as is. - EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4u, 0x11, 0x22, 0x33, 0x44, // - DW_OP_convert, offs_uint32_t}), - llvm::HasValue(GetScalar(64, 0x44332211, not_signed))); + 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}), - llvm::HasValue(GetScalar(64, 0x44332211, not_signed))); + 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_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}), - llvm::HasValue(GetScalar(32, 0xffeeddcc, not_signed))); + 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_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}), - llvm::HasValue(GetScalar(8, 'A', not_signed))); + 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}), - llvm::HasValue(GetScalar(8, 'A', is_signed))); + 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. @@ -354,4 +357,13 @@ // 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))); }