| 1 | //===-- DWARFLocationExpression.cpp ---------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "DWARFLocationExpression.h" |
| 10 | |
| 11 | #include "lldb/Core/Module.h" |
| 12 | #include "lldb/Core/Section.h" |
| 13 | #include "lldb/Expression/DWARFExpression.h" |
| 14 | #include "lldb/Utility/ArchSpec.h" |
| 15 | #include "lldb/Utility/DataBufferHeap.h" |
| 16 | #include "lldb/Utility/StreamBuffer.h" |
| 17 | |
| 18 | #include "llvm/BinaryFormat/Dwarf.h" |
| 19 | #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" |
| 20 | #include "llvm/DebugInfo/CodeView/TypeIndex.h" |
| 21 | #include "llvm/DebugInfo/PDB/Native/TpiStream.h" |
| 22 | #include "llvm/Support/Endian.h" |
| 23 | |
| 24 | #include "PdbUtil.h" |
| 25 | #include "CodeViewRegisterMapping.h" |
| 26 | #include "PdbFPOProgramToDWARFExpression.h" |
| 27 | #include <optional> |
| 28 | |
| 29 | using namespace lldb; |
| 30 | using namespace lldb_private; |
| 31 | using namespace lldb_private::npdb; |
| 32 | using namespace llvm::codeview; |
| 33 | using namespace llvm::pdb; |
| 34 | |
| 35 | uint32_t GetGenericRegisterNumber(llvm::codeview::RegisterId register_id) { |
| 36 | if (register_id == llvm::codeview::RegisterId::VFRAME) |
| 37 | return LLDB_REGNUM_GENERIC_FP; |
| 38 | |
| 39 | return LLDB_INVALID_REGNUM; |
| 40 | } |
| 41 | |
| 42 | static uint32_t GetRegisterNumber(llvm::Triple::ArchType arch_type, |
| 43 | llvm::codeview::RegisterId register_id, |
| 44 | RegisterKind ®ister_kind) { |
| 45 | register_kind = eRegisterKindLLDB; |
| 46 | uint32_t reg_num = GetLLDBRegisterNumber(arch_type, register_id); |
| 47 | if (reg_num != LLDB_INVALID_REGNUM) |
| 48 | return reg_num; |
| 49 | |
| 50 | register_kind = eRegisterKindGeneric; |
| 51 | return GetGenericRegisterNumber(register_id); |
| 52 | } |
| 53 | |
| 54 | static bool IsSimpleTypeSignedInteger(SimpleTypeKind kind) { |
| 55 | switch (kind) { |
| 56 | case SimpleTypeKind::Int128: |
| 57 | case SimpleTypeKind::Int64: |
| 58 | case SimpleTypeKind::Int64Quad: |
| 59 | case SimpleTypeKind::Int32: |
| 60 | case SimpleTypeKind::Int32Long: |
| 61 | case SimpleTypeKind::Int16: |
| 62 | case SimpleTypeKind::Int16Short: |
| 63 | case SimpleTypeKind::Float128: |
| 64 | case SimpleTypeKind::Float80: |
| 65 | case SimpleTypeKind::Float64: |
| 66 | case SimpleTypeKind::Float32: |
| 67 | case SimpleTypeKind::Float16: |
| 68 | case SimpleTypeKind::NarrowCharacter: |
| 69 | case SimpleTypeKind::SignedCharacter: |
| 70 | case SimpleTypeKind::SByte: |
| 71 | return true; |
| 72 | default: |
| 73 | return false; |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | static std::pair<size_t, bool> GetIntegralTypeInfo(TypeIndex ti, |
| 78 | TpiStream &tpi) { |
| 79 | if (ti.isSimple()) { |
| 80 | SimpleTypeKind stk = ti.getSimpleKind(); |
| 81 | return {GetTypeSizeForSimpleKind(kind: stk), IsSimpleTypeSignedInteger(kind: stk)}; |
| 82 | } |
| 83 | |
| 84 | CVType cvt = tpi.getType(Index: ti); |
| 85 | switch (cvt.kind()) { |
| 86 | case LF_MODIFIER: { |
| 87 | ModifierRecord mfr; |
| 88 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<ModifierRecord>(CVT&: cvt, Record&: mfr)); |
| 89 | return GetIntegralTypeInfo(ti: mfr.ModifiedType, tpi); |
| 90 | } |
| 91 | case LF_POINTER: { |
| 92 | PointerRecord pr; |
| 93 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<PointerRecord>(CVT&: cvt, Record&: pr)); |
| 94 | return GetIntegralTypeInfo(ti: pr.ReferentType, tpi); |
| 95 | } |
| 96 | case LF_ENUM: { |
| 97 | EnumRecord er; |
| 98 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<EnumRecord>(CVT&: cvt, Record&: er)); |
| 99 | return GetIntegralTypeInfo(ti: er.UnderlyingType, tpi); |
| 100 | } |
| 101 | default: |
| 102 | assert(false && "Type is not integral!" ); |
| 103 | return {0, false}; |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | template <typename StreamWriter> |
| 108 | static DWARFExpression MakeLocationExpressionInternal(lldb::ModuleSP module, |
| 109 | StreamWriter &&writer) { |
| 110 | const ArchSpec &architecture = module->GetArchitecture(); |
| 111 | ByteOrder byte_order = architecture.GetByteOrder(); |
| 112 | uint32_t address_size = architecture.GetAddressByteSize(); |
| 113 | uint32_t byte_size = architecture.GetDataByteSize(); |
| 114 | if (byte_order == eByteOrderInvalid || address_size == 0) |
| 115 | return DWARFExpression(); |
| 116 | |
| 117 | RegisterKind register_kind = eRegisterKindDWARF; |
| 118 | StreamBuffer<32> stream(Stream::eBinary, address_size, byte_order); |
| 119 | |
| 120 | if (!writer(stream, register_kind)) |
| 121 | return DWARFExpression(); |
| 122 | |
| 123 | DataBufferSP buffer = |
| 124 | std::make_shared<DataBufferHeap>(args: stream.GetData(), args: stream.GetSize()); |
| 125 | DataExtractor (buffer, byte_order, address_size, byte_size); |
| 126 | DWARFExpression result(extractor); |
| 127 | result.SetRegisterKind(register_kind); |
| 128 | |
| 129 | return result; |
| 130 | } |
| 131 | |
| 132 | static bool MakeRegisterBasedLocationExpressionInternal( |
| 133 | Stream &stream, llvm::codeview::RegisterId reg, RegisterKind ®ister_kind, |
| 134 | std::optional<int32_t> relative_offset, lldb::ModuleSP module) { |
| 135 | uint32_t reg_num = GetRegisterNumber(arch_type: module->GetArchitecture().GetMachine(), |
| 136 | register_id: reg, register_kind); |
| 137 | if (reg_num == LLDB_INVALID_REGNUM) |
| 138 | return false; |
| 139 | |
| 140 | if (reg_num > 31) { |
| 141 | llvm::dwarf::LocationAtom base = |
| 142 | relative_offset ? llvm::dwarf::DW_OP_bregx : llvm::dwarf::DW_OP_regx; |
| 143 | stream.PutHex8(uvalue: base); |
| 144 | stream.PutULEB128(uval: reg_num); |
| 145 | } else { |
| 146 | llvm::dwarf::LocationAtom base = |
| 147 | relative_offset ? llvm::dwarf::DW_OP_breg0 : llvm::dwarf::DW_OP_reg0; |
| 148 | stream.PutHex8(uvalue: base + reg_num); |
| 149 | } |
| 150 | |
| 151 | if (relative_offset) |
| 152 | stream.PutSLEB128(uval: *relative_offset); |
| 153 | |
| 154 | return true; |
| 155 | } |
| 156 | |
| 157 | static DWARFExpression MakeRegisterBasedLocationExpressionInternal( |
| 158 | llvm::codeview::RegisterId reg, std::optional<int32_t> relative_offset, |
| 159 | lldb::ModuleSP module) { |
| 160 | return MakeLocationExpressionInternal( |
| 161 | module, writer: [&](Stream &stream, RegisterKind ®ister_kind) -> bool { |
| 162 | return MakeRegisterBasedLocationExpressionInternal( |
| 163 | stream, reg, register_kind, relative_offset, module); |
| 164 | }); |
| 165 | } |
| 166 | |
| 167 | DWARFExpression lldb_private::npdb::MakeEnregisteredLocationExpression( |
| 168 | llvm::codeview::RegisterId reg, lldb::ModuleSP module) { |
| 169 | return MakeRegisterBasedLocationExpressionInternal(reg, relative_offset: std::nullopt, module); |
| 170 | } |
| 171 | |
| 172 | DWARFExpression lldb_private::npdb::MakeRegRelLocationExpression( |
| 173 | llvm::codeview::RegisterId reg, int32_t offset, lldb::ModuleSP module) { |
| 174 | return MakeRegisterBasedLocationExpressionInternal(reg, relative_offset: offset, module); |
| 175 | } |
| 176 | |
| 177 | static bool EmitVFrameEvaluationDWARFExpression( |
| 178 | llvm::StringRef program, llvm::Triple::ArchType arch_type, Stream &stream) { |
| 179 | // VFrame value always stored in $TO pseudo-register |
| 180 | return TranslateFPOProgramToDWARFExpression(program, register_name: "$T0" , arch_type, |
| 181 | stream); |
| 182 | } |
| 183 | |
| 184 | DWARFExpression lldb_private::npdb::MakeVFrameRelLocationExpression( |
| 185 | llvm::StringRef fpo_program, int32_t offset, lldb::ModuleSP module) { |
| 186 | return MakeLocationExpressionInternal( |
| 187 | module, writer: [&](Stream &stream, RegisterKind ®ister_kind) -> bool { |
| 188 | const ArchSpec &architecture = module->GetArchitecture(); |
| 189 | |
| 190 | if (!EmitVFrameEvaluationDWARFExpression(program: fpo_program, arch_type: architecture.GetMachine(), |
| 191 | stream)) |
| 192 | return false; |
| 193 | |
| 194 | stream.PutHex8(uvalue: llvm::dwarf::DW_OP_consts); |
| 195 | stream.PutSLEB128(uval: offset); |
| 196 | stream.PutHex8(uvalue: llvm::dwarf::DW_OP_plus); |
| 197 | |
| 198 | register_kind = eRegisterKindLLDB; |
| 199 | |
| 200 | return true; |
| 201 | }); |
| 202 | } |
| 203 | |
| 204 | DWARFExpression lldb_private::npdb::MakeGlobalLocationExpression( |
| 205 | uint16_t section, uint32_t offset, ModuleSP module) { |
| 206 | assert(section > 0); |
| 207 | assert(module); |
| 208 | |
| 209 | return MakeLocationExpressionInternal( |
| 210 | module, writer: [&](Stream &stream, RegisterKind ®ister_kind) -> bool { |
| 211 | stream.PutHex8(uvalue: llvm::dwarf::DW_OP_addr); |
| 212 | |
| 213 | SectionList *section_list = module->GetSectionList(); |
| 214 | assert(section_list); |
| 215 | |
| 216 | auto section_ptr = section_list->FindSectionByID(sect_id: section); |
| 217 | if (!section_ptr) |
| 218 | return false; |
| 219 | |
| 220 | stream.PutMaxHex64(uvalue: section_ptr->GetFileAddress() + offset, |
| 221 | byte_size: stream.GetAddressByteSize(), byte_order: stream.GetByteOrder()); |
| 222 | |
| 223 | return true; |
| 224 | }); |
| 225 | } |
| 226 | |
| 227 | DWARFExpression lldb_private::npdb::MakeConstantLocationExpression( |
| 228 | TypeIndex underlying_ti, TpiStream &tpi, const llvm::APSInt &constant, |
| 229 | ModuleSP module) { |
| 230 | const ArchSpec &architecture = module->GetArchitecture(); |
| 231 | uint32_t address_size = architecture.GetAddressByteSize(); |
| 232 | |
| 233 | size_t size = 0; |
| 234 | bool is_signed = false; |
| 235 | std::tie(args&: size, args&: is_signed) = GetIntegralTypeInfo(ti: underlying_ti, tpi); |
| 236 | |
| 237 | union { |
| 238 | llvm::support::little64_t I; |
| 239 | llvm::support::ulittle64_t U; |
| 240 | } Value; |
| 241 | |
| 242 | std::shared_ptr<DataBufferHeap> buffer = std::make_shared<DataBufferHeap>(); |
| 243 | buffer->SetByteSize(size); |
| 244 | |
| 245 | llvm::ArrayRef<uint8_t> bytes; |
| 246 | if (is_signed) { |
| 247 | Value.I = constant.getSExtValue(); |
| 248 | } else { |
| 249 | Value.U = constant.getZExtValue(); |
| 250 | } |
| 251 | |
| 252 | bytes = llvm::ArrayRef(reinterpret_cast<const uint8_t *>(&Value), 8) |
| 253 | .take_front(N: size); |
| 254 | buffer->CopyData(src: bytes.data(), src_len: size); |
| 255 | DataExtractor (buffer, lldb::eByteOrderLittle, address_size); |
| 256 | DWARFExpression result(extractor); |
| 257 | return result; |
| 258 | } |
| 259 | |
| 260 | DWARFExpression |
| 261 | lldb_private::npdb::MakeEnregisteredLocationExpressionForComposite( |
| 262 | const std::map<uint64_t, MemberValLocation> &offset_to_location, |
| 263 | std::map<uint64_t, size_t> &offset_to_size, size_t total_size, |
| 264 | lldb::ModuleSP module) { |
| 265 | return MakeLocationExpressionInternal( |
| 266 | module, writer: [&](Stream &stream, RegisterKind ®ister_kind) -> bool { |
| 267 | size_t cur_offset = 0; |
| 268 | bool is_simple_type = offset_to_size.empty(); |
| 269 | // Iterate through offset_to_location because offset_to_size might be |
| 270 | // empty if the variable is a simple type. |
| 271 | for (const auto &offset_loc : offset_to_location) { |
| 272 | if (cur_offset < offset_loc.first) { |
| 273 | stream.PutHex8(uvalue: llvm::dwarf::DW_OP_piece); |
| 274 | stream.PutULEB128(uval: offset_loc.first - cur_offset); |
| 275 | cur_offset = offset_loc.first; |
| 276 | } |
| 277 | MemberValLocation loc = offset_loc.second; |
| 278 | std::optional<int32_t> offset = |
| 279 | loc.is_at_reg ? std::nullopt |
| 280 | : std::optional<int32_t>(loc.reg_offset); |
| 281 | if (!MakeRegisterBasedLocationExpressionInternal( |
| 282 | stream, reg: (RegisterId)loc.reg_id, register_kind, relative_offset: offset, |
| 283 | module)) |
| 284 | return false; |
| 285 | if (!is_simple_type) { |
| 286 | stream.PutHex8(uvalue: llvm::dwarf::DW_OP_piece); |
| 287 | stream.PutULEB128(uval: offset_to_size[offset_loc.first]); |
| 288 | cur_offset = offset_loc.first + offset_to_size[offset_loc.first]; |
| 289 | } |
| 290 | } |
| 291 | // For simple type, it specifies the byte size of the value described by |
| 292 | // the previous dwarf expr. For udt, it's the remaining byte size at end |
| 293 | // of a struct. |
| 294 | if (total_size > cur_offset) { |
| 295 | stream.PutHex8(uvalue: llvm::dwarf::DW_OP_piece); |
| 296 | stream.PutULEB128(uval: total_size - cur_offset); |
| 297 | } |
| 298 | return true; |
| 299 | }); |
| 300 | } |
| 301 | |