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 | |