| //===-- DWARFLocationExpression.cpp ---------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "DWARFLocationExpression.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Core/Section.h" | |
| #include "lldb/Expression/DWARFExpression.h" | |
| #include "lldb/Utility/ArchSpec.h" | |
| #include "lldb/Utility/DataBufferHeap.h" | |
| #include "lldb/Utility/StreamBuffer.h" | |
| #include "llvm/BinaryFormat/Dwarf.h" | |
| #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" | |
| #include "llvm/DebugInfo/CodeView/TypeIndex.h" | |
| #include "llvm/DebugInfo/PDB/Native/TpiStream.h" | |
| #include "llvm/Support/Endian.h" | |
| #include "PdbUtil.h" | |
| #include "CodeViewRegisterMapping.h" | |
| #include "PdbFPOProgramToDWARFExpression.h" | |
| #include <optional> | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| using namespace lldb_private::npdb; | |
| using namespace llvm::codeview; | |
| using namespace llvm::pdb; | |
| uint32_t GetGenericRegisterNumber(llvm::codeview::RegisterId register_id) { | |
| if (register_id == llvm::codeview::RegisterId::VFRAME) | |
| return LLDB_REGNUM_GENERIC_FP; | |
| return LLDB_INVALID_REGNUM; | |
| } | |
| static uint32_t GetRegisterNumber(llvm::Triple::ArchType arch_type, | |
| llvm::codeview::RegisterId register_id, | |
| RegisterKind ®ister_kind) { | |
| register_kind = eRegisterKindLLDB; | |
| uint32_t reg_num = GetLLDBRegisterNumber(arch_type, register_id); | |
| if (reg_num != LLDB_INVALID_REGNUM) | |
| return reg_num; | |
| register_kind = eRegisterKindGeneric; | |
| return GetGenericRegisterNumber(register_id); | |
| } | |
| static bool IsSimpleTypeSignedInteger(SimpleTypeKind kind) { | |
| switch (kind) { | |
| case SimpleTypeKind::Int128: | |
| case SimpleTypeKind::Int64: | |
| case SimpleTypeKind::Int64Quad: | |
| case SimpleTypeKind::Int32: | |
| case SimpleTypeKind::Int32Long: | |
| case SimpleTypeKind::Int16: | |
| case SimpleTypeKind::Int16Short: | |
| case SimpleTypeKind::Float128: | |
| case SimpleTypeKind::Float80: | |
| case SimpleTypeKind::Float64: | |
| case SimpleTypeKind::Float32: | |
| case SimpleTypeKind::Float16: | |
| case SimpleTypeKind::NarrowCharacter: | |
| case SimpleTypeKind::SignedCharacter: | |
| case SimpleTypeKind::SByte: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| static llvm::Expected<std::pair<size_t, bool>> | |
| GetIntegralTypeInfo(TypeIndex ti, TpiStream &tpi) { | |
| if (ti.isSimple()) { | |
| SimpleTypeKind stk = ti.getSimpleKind(); | |
| return std::make_pair(GetTypeSizeForSimpleKind(stk), | |
| IsSimpleTypeSignedInteger(stk)); | |
| } | |
| CVType cvt = tpi.getType(ti); | |
| switch (cvt.kind()) { | |
| case LF_MODIFIER: { | |
| ModifierRecord mfr; | |
| if (auto err = TypeDeserializer::deserializeAs<ModifierRecord>(cvt, mfr)) | |
| return std::move(err); | |
| return GetIntegralTypeInfo(mfr.ModifiedType, tpi); | |
| } | |
| case LF_POINTER: { | |
| PointerRecord pr; | |
| if (auto err = TypeDeserializer::deserializeAs<PointerRecord>(cvt, pr)) | |
| return std::move(err); | |
| return std::make_pair(pr.getSize(), false); | |
| } | |
| case LF_ENUM: { | |
| EnumRecord er; | |
| if (auto err = TypeDeserializer::deserializeAs<EnumRecord>(cvt, er)) | |
| return std::move(err); | |
| return GetIntegralTypeInfo(er.UnderlyingType, tpi); | |
| } | |
| default: | |
| return llvm::make_error<llvm::StringError>("Type is not integral", | |
| llvm::inconvertibleErrorCode()); | |
| } | |
| } | |
| template <typename StreamWriter> | |
| static DWARFExpression MakeLocationExpressionInternal(lldb::ModuleSP module, | |
| StreamWriter &&writer) { | |
| const ArchSpec &architecture = module->GetArchitecture(); | |
| ByteOrder byte_order = architecture.GetByteOrder(); | |
| uint32_t address_size = architecture.GetAddressByteSize(); | |
| if (byte_order == eByteOrderInvalid || address_size == 0) | |
| return DWARFExpression(); | |
| RegisterKind register_kind = eRegisterKindDWARF; | |
| StreamBuffer<32> stream(Stream::eBinary, byte_order); | |
| if (!writer(stream, register_kind)) | |
| return DWARFExpression(); | |
| DataBufferSP buffer = | |
| std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize()); | |
| DataExtractor extractor(buffer, byte_order, address_size); | |
| DWARFExpression result(extractor); | |
| result.SetRegisterKind(register_kind); | |
| return result; | |
| } | |
| static bool MakeRegisterBasedLocationExpressionInternal( | |
| Stream &stream, llvm::codeview::RegisterId reg, RegisterKind ®ister_kind, | |
| std::optional<int32_t> relative_offset, lldb::ModuleSP module) { | |
| uint32_t reg_num = GetRegisterNumber(module->GetArchitecture().GetMachine(), | |
| reg, register_kind); | |
| if (reg_num == LLDB_INVALID_REGNUM) | |
| return false; | |
| if (reg_num > 31) { | |
| llvm::dwarf::LocationAtom base = | |
| relative_offset ? llvm::dwarf::DW_OP_bregx : llvm::dwarf::DW_OP_regx; | |
| stream.PutHex8(base); | |
| stream.PutULEB128(reg_num); | |
| } else { | |
| llvm::dwarf::LocationAtom base = | |
| relative_offset ? llvm::dwarf::DW_OP_breg0 : llvm::dwarf::DW_OP_reg0; | |
| stream.PutHex8(base + reg_num); | |
| } | |
| if (relative_offset) | |
| stream.PutSLEB128(*relative_offset); | |
| return true; | |
| } | |
| /// *(reg + indir_offset) + offset | |
| static bool MakeRegisterBasedIndirectLocationExpressionInternal( | |
| Stream &stream, llvm::codeview::RegisterId reg, RegisterKind ®ister_kind, | |
| int32_t indir_offset, int32_t offset, lldb::ModuleSP module) { | |
| if (!MakeRegisterBasedLocationExpressionInternal(stream, reg, register_kind, | |
| indir_offset, module)) | |
| return false; | |
| stream.PutHex8(llvm::dwarf::DW_OP_deref); | |
| stream.PutHex8(llvm::dwarf::DW_OP_consts); | |
| stream.PutSLEB128(offset); | |
| stream.PutHex8(llvm::dwarf::DW_OP_plus); | |
| return true; | |
| } | |
| static DWARFExpression MakeRegisterBasedLocationExpressionInternal( | |
| llvm::codeview::RegisterId reg, std::optional<int32_t> relative_offset, | |
| lldb::ModuleSP module) { | |
| return MakeLocationExpressionInternal( | |
| module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { | |
| return MakeRegisterBasedLocationExpressionInternal( | |
| stream, reg, register_kind, relative_offset, module); | |
| }); | |
| } | |
| DWARFExpression lldb_private::npdb::MakeEnregisteredLocationExpression( | |
| llvm::codeview::RegisterId reg, lldb::ModuleSP module) { | |
| return MakeRegisterBasedLocationExpressionInternal(reg, std::nullopt, module); | |
| } | |
| DWARFExpression lldb_private::npdb::MakeRegRelLocationExpression( | |
| llvm::codeview::RegisterId reg, int32_t offset, lldb::ModuleSP module) { | |
| return MakeRegisterBasedLocationExpressionInternal(reg, offset, module); | |
| } | |
| DWARFExpression lldb_private::npdb::MakeRegRelIndirLocationExpression( | |
| llvm::codeview::RegisterId reg, int32_t offset, int32_t offset_in_udt, | |
| lldb::ModuleSP module) { | |
| return MakeLocationExpressionInternal( | |
| module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { | |
| return MakeRegisterBasedIndirectLocationExpressionInternal( | |
| stream, reg, register_kind, offset, offset_in_udt, module); | |
| }); | |
| } | |
| static bool EmitVFrameEvaluationDWARFExpression( | |
| llvm::StringRef program, llvm::Triple::ArchType arch_type, Stream &stream) { | |
| // VFrame value always stored in $TO pseudo-register | |
| return TranslateFPOProgramToDWARFExpression(program, "$T0", arch_type, | |
| stream); | |
| } | |
| DWARFExpression lldb_private::npdb::MakeVFrameRelLocationExpression( | |
| llvm::StringRef fpo_program, int32_t offset, lldb::ModuleSP module) { | |
| return MakeLocationExpressionInternal( | |
| module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { | |
| const ArchSpec &architecture = module->GetArchitecture(); | |
| if (!EmitVFrameEvaluationDWARFExpression(fpo_program, architecture.GetMachine(), | |
| stream)) | |
| return false; | |
| stream.PutHex8(llvm::dwarf::DW_OP_consts); | |
| stream.PutSLEB128(offset); | |
| stream.PutHex8(llvm::dwarf::DW_OP_plus); | |
| register_kind = eRegisterKindLLDB; | |
| return true; | |
| }); | |
| } | |
| DWARFExpression lldb_private::npdb::MakeVFrameRelIndirLocationExpression( | |
| llvm::StringRef fpo_program, int32_t offset, int32_t offset_in_udt, | |
| lldb::ModuleSP module) { | |
| return MakeLocationExpressionInternal( | |
| module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { | |
| const ArchSpec &architecture = module->GetArchitecture(); | |
| if (!EmitVFrameEvaluationDWARFExpression( | |
| fpo_program, architecture.GetMachine(), stream)) | |
| return false; | |
| stream.PutHex8(llvm::dwarf::DW_OP_consts); | |
| stream.PutSLEB128(offset); | |
| stream.PutHex8(llvm::dwarf::DW_OP_plus); | |
| stream.PutHex8(llvm::dwarf::DW_OP_deref); | |
| stream.PutHex8(llvm::dwarf::DW_OP_consts); | |
| stream.PutSLEB128(offset_in_udt); | |
| stream.PutHex8(llvm::dwarf::DW_OP_plus); | |
| register_kind = eRegisterKindLLDB; | |
| return true; | |
| }); | |
| } | |
| DWARFExpression lldb_private::npdb::MakeGlobalLocationExpression( | |
| uint16_t section, uint32_t offset, ModuleSP module) { | |
| assert(section > 0); | |
| assert(module); | |
| return MakeLocationExpressionInternal( | |
| module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { | |
| stream.PutHex8(llvm::dwarf::DW_OP_addr); | |
| SectionList *section_list = module->GetSectionList(); | |
| assert(section_list); | |
| auto section_ptr = section_list->FindSectionByID(section); | |
| if (!section_ptr) | |
| return false; | |
| const ArchSpec &arch = module->GetArchitecture(); | |
| stream.PutMaxHex64(section_ptr->GetFileAddress() + offset, | |
| arch.GetAddressByteSize(), arch.GetByteOrder()); | |
| return true; | |
| }); | |
| } | |
| llvm::Expected<DWARFExpression> | |
| lldb_private::npdb::MakeConstantLocationExpression(TypeIndex underlying_ti, | |
| TpiStream &tpi, | |
| const llvm::APSInt &constant, | |
| ModuleSP module) { | |
| const ArchSpec &architecture = module->GetArchitecture(); | |
| uint32_t address_size = architecture.GetAddressByteSize(); | |
| auto type_info = GetIntegralTypeInfo(underlying_ti, tpi); | |
| if (!type_info) | |
| return type_info.takeError(); | |
| auto [size, is_signed] = *type_info; | |
| union { | |
| llvm::support::little64_t I; | |
| llvm::support::ulittle64_t U; | |
| } Value; | |
| std::shared_ptr<DataBufferHeap> buffer = std::make_shared<DataBufferHeap>(); | |
| buffer->SetByteSize(size); | |
| llvm::ArrayRef<uint8_t> bytes; | |
| if (is_signed) { | |
| Value.I = constant.getSExtValue(); | |
| } else { | |
| Value.U = constant.getZExtValue(); | |
| } | |
| bytes = llvm::ArrayRef(reinterpret_cast<const uint8_t *>(&Value), 8) | |
| .take_front(size); | |
| buffer->CopyData(bytes.data(), size); | |
| DataExtractor extractor(buffer, lldb::eByteOrderLittle, address_size); | |
| DWARFExpression result(extractor); | |
| return result; | |
| } | |
| DWARFExpression | |
| lldb_private::npdb::MakeEnregisteredLocationExpressionForComposite( | |
| const std::map<uint64_t, MemberValLocation> &offset_to_location, | |
| std::map<uint64_t, size_t> &offset_to_size, size_t total_size, | |
| lldb::ModuleSP module) { | |
| return MakeLocationExpressionInternal( | |
| module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { | |
| size_t cur_offset = 0; | |
| bool is_simple_type = offset_to_size.empty(); | |
| // Iterate through offset_to_location because offset_to_size might be | |
| // empty if the variable is a simple type. | |
| for (const auto &offset_loc : offset_to_location) { | |
| if (cur_offset < offset_loc.first) { | |
| stream.PutHex8(llvm::dwarf::DW_OP_piece); | |
| stream.PutULEB128(offset_loc.first - cur_offset); | |
| cur_offset = offset_loc.first; | |
| } | |
| MemberValLocation loc = offset_loc.second; | |
| std::optional<int32_t> offset = | |
| loc.is_at_reg ? std::nullopt | |
| : std::optional<int32_t>(loc.reg_offset); | |
| if (!MakeRegisterBasedLocationExpressionInternal( | |
| stream, (RegisterId)loc.reg_id, register_kind, offset, | |
| module)) | |
| return false; | |
| if (!is_simple_type) { | |
| stream.PutHex8(llvm::dwarf::DW_OP_piece); | |
| stream.PutULEB128(offset_to_size[offset_loc.first]); | |
| cur_offset = offset_loc.first + offset_to_size[offset_loc.first]; | |
| } | |
| } | |
| // For simple type, it specifies the byte size of the value described by | |
| // the previous dwarf expr. For udt, it's the remaining byte size at end | |
| // of a struct. | |
| if (total_size > cur_offset) { | |
| stream.PutHex8(llvm::dwarf::DW_OP_piece); | |
| stream.PutULEB128(total_size - cur_offset); | |
| } | |
| return true; | |
| }); | |
| } |