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
29using namespace lldb;
30using namespace lldb_private;
31using namespace lldb_private::npdb;
32using namespace llvm::codeview;
33using namespace llvm::pdb;
34
35uint32_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
42static uint32_t GetRegisterNumber(llvm::Triple::ArchType arch_type,
43                                  llvm::codeview::RegisterId register_id,
44                                  RegisterKind &register_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
54static 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
77static std::pair<size_t, bool> GetIntegralTypeInfo(TypeIndex ti,
78                                                   TpiStream &tpi) {
79  if (ti.isSimple()) {
80    SimpleTypeKind stk = ti.getSimpleKind();
81    return {GetTypeSizeForSimpleKind(stk), IsSimpleTypeSignedInteger(stk)};
82  }
83
84  CVType cvt = tpi.getType(ti);
85  switch (cvt.kind()) {
86  case LF_MODIFIER: {
87    ModifierRecord mfr;
88    llvm::cantFail(TypeDeserializer::deserializeAs<ModifierRecord>(cvt, mfr));
89    return GetIntegralTypeInfo(mfr.ModifiedType, tpi);
90  }
91  case LF_POINTER: {
92    PointerRecord pr;
93    llvm::cantFail(TypeDeserializer::deserializeAs<PointerRecord>(cvt, pr));
94    return GetIntegralTypeInfo(pr.ReferentType, tpi);
95  }
96  case LF_ENUM: {
97    EnumRecord er;
98    llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
99    return GetIntegralTypeInfo(er.UnderlyingType, tpi);
100  }
101  default:
102    assert(false && "Type is not integral!");
103    return {0, false};
104  }
105}
106
107template <typename StreamWriter>
108static 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>(stream.GetData(), stream.GetSize());
125  DataExtractor extractor(buffer, byte_order, address_size, byte_size);
126  DWARFExpression result(extractor);
127  result.SetRegisterKind(register_kind);
128
129  return result;
130}
131
132static bool MakeRegisterBasedLocationExpressionInternal(
133    Stream &stream, llvm::codeview::RegisterId reg, RegisterKind &register_kind,
134    std::optional<int32_t> relative_offset, lldb::ModuleSP module) {
135  uint32_t reg_num = GetRegisterNumber(module->GetArchitecture().GetMachine(),
136                                       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(base);
144    stream.PutULEB128(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(base + reg_num);
149  }
150
151  if (relative_offset)
152    stream.PutSLEB128(*relative_offset);
153
154  return true;
155}
156
157static DWARFExpression MakeRegisterBasedLocationExpressionInternal(
158    llvm::codeview::RegisterId reg, std::optional<int32_t> relative_offset,
159    lldb::ModuleSP module) {
160  return MakeLocationExpressionInternal(
161      module, [&](Stream &stream, RegisterKind &register_kind) -> bool {
162        return MakeRegisterBasedLocationExpressionInternal(
163            stream, reg, register_kind, relative_offset, module);
164      });
165}
166
167DWARFExpression lldb_private::npdb::MakeEnregisteredLocationExpression(
168    llvm::codeview::RegisterId reg, lldb::ModuleSP module) {
169  return MakeRegisterBasedLocationExpressionInternal(reg, std::nullopt, module);
170}
171
172DWARFExpression lldb_private::npdb::MakeRegRelLocationExpression(
173    llvm::codeview::RegisterId reg, int32_t offset, lldb::ModuleSP module) {
174  return MakeRegisterBasedLocationExpressionInternal(reg, offset, module);
175}
176
177static 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, "$T0", arch_type,
181                                              stream);
182}
183
184DWARFExpression lldb_private::npdb::MakeVFrameRelLocationExpression(
185    llvm::StringRef fpo_program, int32_t offset, lldb::ModuleSP module) {
186  return MakeLocationExpressionInternal(
187      module, [&](Stream &stream, RegisterKind &register_kind) -> bool {
188        const ArchSpec &architecture = module->GetArchitecture();
189
190        if (!EmitVFrameEvaluationDWARFExpression(fpo_program, architecture.GetMachine(),
191                                                 stream))
192          return false;
193
194        stream.PutHex8(llvm::dwarf::DW_OP_consts);
195        stream.PutSLEB128(offset);
196        stream.PutHex8(llvm::dwarf::DW_OP_plus);
197
198        register_kind = eRegisterKindLLDB;
199
200        return true;
201      });
202}
203
204DWARFExpression 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, [&](Stream &stream, RegisterKind &register_kind) -> bool {
211        stream.PutHex8(llvm::dwarf::DW_OP_addr);
212
213        SectionList *section_list = module->GetSectionList();
214        assert(section_list);
215
216        auto section_ptr = section_list->FindSectionByID(section);
217        if (!section_ptr)
218          return false;
219
220        stream.PutMaxHex64(section_ptr->GetFileAddress() + offset,
221                           stream.GetAddressByteSize(), stream.GetByteOrder());
222
223        return true;
224      });
225}
226
227DWARFExpression 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(size, is_signed) = GetIntegralTypeInfo(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(size);
254  buffer->CopyData(bytes.data(), size);
255  DataExtractor extractor(buffer, lldb::eByteOrderLittle, address_size);
256  DWARFExpression result(extractor);
257  return result;
258}
259
260DWARFExpression
261lldb_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, [&](Stream &stream, RegisterKind &register_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(llvm::dwarf::DW_OP_piece);
274            stream.PutULEB128(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, (RegisterId)loc.reg_id, register_kind, offset,
283                  module))
284            return false;
285          if (!is_simple_type) {
286            stream.PutHex8(llvm::dwarf::DW_OP_piece);
287            stream.PutULEB128(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(llvm::dwarf::DW_OP_piece);
296          stream.PutULEB128(total_size - cur_offset);
297        }
298        return true;
299      });
300}
301