//===-- 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 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> 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(cvt, mfr)) return std::move(err); return GetIntegralTypeInfo(mfr.ModifiedType, tpi); } case LF_POINTER: { PointerRecord pr; if (auto err = TypeDeserializer::deserializeAs(cvt, pr)) return std::move(err); return std::make_pair(pr.getSize(), false); } case LF_ENUM: { EnumRecord er; if (auto err = TypeDeserializer::deserializeAs(cvt, er)) return std::move(err); return GetIntegralTypeInfo(er.UnderlyingType, tpi); } default: return llvm::make_error("Type is not integral", llvm::inconvertibleErrorCode()); } } template 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(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 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 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 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 buffer = std::make_shared(); buffer->SetByteSize(size); llvm::ArrayRef bytes; if (is_signed) { Value.I = constant.getSExtValue(); } else { Value.U = constant.getZExtValue(); } bytes = llvm::ArrayRef(reinterpret_cast(&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 &offset_to_location, std::map &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 offset = loc.is_at_reg ? std::nullopt : std::optional(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; }); }