1 //===-- SBInstruction.cpp ---------------------------------------*- C++ -*-===//
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 "lldb/API/SBInstruction.h"
10 #include "SBReproducerPrivate.h"
11 
12 #include "lldb/API/SBAddress.h"
13 #include "lldb/API/SBFrame.h"
14 
15 #include "lldb/API/SBInstruction.h"
16 #include "lldb/API/SBStream.h"
17 #include "lldb/API/SBTarget.h"
18 #include "lldb/Core/Disassembler.h"
19 #include "lldb/Core/EmulateInstruction.h"
20 #include "lldb/Core/Module.h"
21 #include "lldb/Core/StreamFile.h"
22 #include "lldb/Host/HostInfo.h"
23 #include "lldb/Target/ExecutionContext.h"
24 #include "lldb/Target/StackFrame.h"
25 #include "lldb/Target/Target.h"
26 #include "lldb/Utility/ArchSpec.h"
27 #include "lldb/Utility/DataBufferHeap.h"
28 #include "lldb/Utility/DataExtractor.h"
29 
30 #include <memory>
31 
32 //----------------------------------------------------------------------
33 // We recently fixed a leak in one of the Instruction subclasses where the
34 // instruction will only hold a weak reference to the disassembler to avoid a
35 // cycle that was keeping both objects alive (leak) and we need the
36 // InstructionImpl class to make sure our public API behaves as users would
37 // expect. Calls in our public API allow clients to do things like:
38 //
39 // 1  lldb::SBInstruction inst;
40 // 2  inst = target.ReadInstructions(pc, 1).GetInstructionAtIndex(0)
41 // 3  if (inst.DoesBranch())
42 // 4  ...
43 //
44 // There was a temporary lldb::DisassemblerSP object created in the
45 // SBInstructionList that was returned by lldb.target.ReadInstructions() that
46 // will go away after line 2 but the "inst" object should be able to still
47 // answer questions about itself. So we make sure that any SBInstruction
48 // objects that are given out have a strong reference to the disassembler and
49 // the instruction so that the object can live and successfully respond to all
50 // queries.
51 //----------------------------------------------------------------------
52 class InstructionImpl {
53 public:
54   InstructionImpl(const lldb::DisassemblerSP &disasm_sp,
55                   const lldb::InstructionSP &inst_sp)
56       : m_disasm_sp(disasm_sp), m_inst_sp(inst_sp) {}
57 
58   lldb::InstructionSP GetSP() const { return m_inst_sp; }
59 
60   bool IsValid() const { return (bool)m_inst_sp; }
61 
62 protected:
63   lldb::DisassemblerSP m_disasm_sp; // Can be empty/invalid
64   lldb::InstructionSP m_inst_sp;
65 };
66 
67 using namespace lldb;
68 using namespace lldb_private;
69 
70 SBInstruction::SBInstruction() : m_opaque_sp() {
71   LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBInstruction);
72 }
73 
74 SBInstruction::SBInstruction(const lldb::DisassemblerSP &disasm_sp,
75                              const lldb::InstructionSP &inst_sp)
76     : m_opaque_sp(new InstructionImpl(disasm_sp, inst_sp)) {}
77 
78 SBInstruction::SBInstruction(const SBInstruction &rhs)
79     : m_opaque_sp(rhs.m_opaque_sp) {
80   LLDB_RECORD_CONSTRUCTOR(SBInstruction, (const lldb::SBInstruction &), rhs);
81 }
82 
83 const SBInstruction &SBInstruction::operator=(const SBInstruction &rhs) {
84   LLDB_RECORD_METHOD(const lldb::SBInstruction &,
85                      SBInstruction, operator=,(const lldb::SBInstruction &),
86                      rhs);
87 
88   if (this != &rhs)
89     m_opaque_sp = rhs.m_opaque_sp;
90   return *this;
91 }
92 
93 SBInstruction::~SBInstruction() {}
94 
95 bool SBInstruction::IsValid() {
96   LLDB_RECORD_METHOD_NO_ARGS(bool, SBInstruction, IsValid);
97 
98   return m_opaque_sp && m_opaque_sp->IsValid();
99 }
100 
101 SBAddress SBInstruction::GetAddress() {
102   LLDB_RECORD_METHOD_NO_ARGS(lldb::SBAddress, SBInstruction, GetAddress);
103 
104   SBAddress sb_addr;
105   lldb::InstructionSP inst_sp(GetOpaque());
106   if (inst_sp && inst_sp->GetAddress().IsValid())
107     sb_addr.SetAddress(&inst_sp->GetAddress());
108   return LLDB_RECORD_RESULT(sb_addr);
109 }
110 
111 const char *SBInstruction::GetMnemonic(SBTarget target) {
112   LLDB_RECORD_METHOD(const char *, SBInstruction, GetMnemonic, (lldb::SBTarget),
113                      target);
114 
115   lldb::InstructionSP inst_sp(GetOpaque());
116   if (inst_sp) {
117     ExecutionContext exe_ctx;
118     TargetSP target_sp(target.GetSP());
119     std::unique_lock<std::recursive_mutex> lock;
120     if (target_sp) {
121       lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
122 
123       target_sp->CalculateExecutionContext(exe_ctx);
124       exe_ctx.SetProcessSP(target_sp->GetProcessSP());
125     }
126     return inst_sp->GetMnemonic(&exe_ctx);
127   }
128   return NULL;
129 }
130 
131 const char *SBInstruction::GetOperands(SBTarget target) {
132   LLDB_RECORD_METHOD(const char *, SBInstruction, GetOperands, (lldb::SBTarget),
133                      target);
134 
135   lldb::InstructionSP inst_sp(GetOpaque());
136   if (inst_sp) {
137     ExecutionContext exe_ctx;
138     TargetSP target_sp(target.GetSP());
139     std::unique_lock<std::recursive_mutex> lock;
140     if (target_sp) {
141       lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
142 
143       target_sp->CalculateExecutionContext(exe_ctx);
144       exe_ctx.SetProcessSP(target_sp->GetProcessSP());
145     }
146     return inst_sp->GetOperands(&exe_ctx);
147   }
148   return NULL;
149 }
150 
151 const char *SBInstruction::GetComment(SBTarget target) {
152   LLDB_RECORD_METHOD(const char *, SBInstruction, GetComment, (lldb::SBTarget),
153                      target);
154 
155   lldb::InstructionSP inst_sp(GetOpaque());
156   if (inst_sp) {
157     ExecutionContext exe_ctx;
158     TargetSP target_sp(target.GetSP());
159     std::unique_lock<std::recursive_mutex> lock;
160     if (target_sp) {
161       lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
162 
163       target_sp->CalculateExecutionContext(exe_ctx);
164       exe_ctx.SetProcessSP(target_sp->GetProcessSP());
165     }
166     return inst_sp->GetComment(&exe_ctx);
167   }
168   return NULL;
169 }
170 
171 size_t SBInstruction::GetByteSize() {
172   LLDB_RECORD_METHOD_NO_ARGS(size_t, SBInstruction, GetByteSize);
173 
174   lldb::InstructionSP inst_sp(GetOpaque());
175   if (inst_sp)
176     return inst_sp->GetOpcode().GetByteSize();
177   return 0;
178 }
179 
180 SBData SBInstruction::GetData(SBTarget target) {
181   LLDB_RECORD_METHOD(lldb::SBData, SBInstruction, GetData, (lldb::SBTarget),
182                      target);
183 
184   lldb::SBData sb_data;
185   lldb::InstructionSP inst_sp(GetOpaque());
186   if (inst_sp) {
187     DataExtractorSP data_extractor_sp(new DataExtractor());
188     if (inst_sp->GetData(*data_extractor_sp)) {
189       sb_data.SetOpaque(data_extractor_sp);
190     }
191   }
192   return LLDB_RECORD_RESULT(sb_data);
193 }
194 
195 bool SBInstruction::DoesBranch() {
196   LLDB_RECORD_METHOD_NO_ARGS(bool, SBInstruction, DoesBranch);
197 
198   lldb::InstructionSP inst_sp(GetOpaque());
199   if (inst_sp)
200     return inst_sp->DoesBranch();
201   return false;
202 }
203 
204 bool SBInstruction::HasDelaySlot() {
205   LLDB_RECORD_METHOD_NO_ARGS(bool, SBInstruction, HasDelaySlot);
206 
207   lldb::InstructionSP inst_sp(GetOpaque());
208   if (inst_sp)
209     return inst_sp->HasDelaySlot();
210   return false;
211 }
212 
213 bool SBInstruction::CanSetBreakpoint() {
214   LLDB_RECORD_METHOD_NO_ARGS(bool, SBInstruction, CanSetBreakpoint);
215 
216   lldb::InstructionSP inst_sp(GetOpaque());
217   if (inst_sp)
218     return inst_sp->CanSetBreakpoint();
219   return false;
220 }
221 
222 lldb::InstructionSP SBInstruction::GetOpaque() {
223   if (m_opaque_sp)
224     return m_opaque_sp->GetSP();
225   else
226     return lldb::InstructionSP();
227 }
228 
229 void SBInstruction::SetOpaque(const lldb::DisassemblerSP &disasm_sp,
230                               const lldb::InstructionSP &inst_sp) {
231   m_opaque_sp = std::make_shared<InstructionImpl>(disasm_sp, inst_sp);
232 }
233 
234 bool SBInstruction::GetDescription(lldb::SBStream &s) {
235   LLDB_RECORD_METHOD(bool, SBInstruction, GetDescription, (lldb::SBStream &),
236                      s);
237 
238   lldb::InstructionSP inst_sp(GetOpaque());
239   if (inst_sp) {
240     SymbolContext sc;
241     const Address &addr = inst_sp->GetAddress();
242     ModuleSP module_sp(addr.GetModule());
243     if (module_sp)
244       module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything,
245                                                 sc);
246     // Use the "ref()" instead of the "get()" accessor in case the SBStream
247     // didn't have a stream already created, one will get created...
248     FormatEntity::Entry format;
249     FormatEntity::Parse("${addr}: ", format);
250     inst_sp->Dump(&s.ref(), 0, true, false, NULL, &sc, NULL, &format, 0);
251     return true;
252   }
253   return false;
254 }
255 
256 void SBInstruction::Print(FILE *out) {
257   LLDB_RECORD_METHOD(void, SBInstruction, Print, (FILE *), out);
258 
259   if (out == NULL)
260     return;
261 
262   lldb::InstructionSP inst_sp(GetOpaque());
263   if (inst_sp) {
264     SymbolContext sc;
265     const Address &addr = inst_sp->GetAddress();
266     ModuleSP module_sp(addr.GetModule());
267     if (module_sp)
268       module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything,
269                                                 sc);
270     StreamFile out_stream(out, false);
271     FormatEntity::Entry format;
272     FormatEntity::Parse("${addr}: ", format);
273     inst_sp->Dump(&out_stream, 0, true, false, NULL, &sc, NULL, &format, 0);
274   }
275 }
276 
277 bool SBInstruction::EmulateWithFrame(lldb::SBFrame &frame,
278                                      uint32_t evaluate_options) {
279   LLDB_RECORD_METHOD(bool, SBInstruction, EmulateWithFrame,
280                      (lldb::SBFrame &, uint32_t), frame, evaluate_options);
281 
282   lldb::InstructionSP inst_sp(GetOpaque());
283   if (inst_sp) {
284     lldb::StackFrameSP frame_sp(frame.GetFrameSP());
285 
286     if (frame_sp) {
287       lldb_private::ExecutionContext exe_ctx;
288       frame_sp->CalculateExecutionContext(exe_ctx);
289       lldb_private::Target *target = exe_ctx.GetTargetPtr();
290       lldb_private::ArchSpec arch = target->GetArchitecture();
291 
292       return inst_sp->Emulate(
293           arch, evaluate_options, (void *)frame_sp.get(),
294           &lldb_private::EmulateInstruction::ReadMemoryFrame,
295           &lldb_private::EmulateInstruction::WriteMemoryFrame,
296           &lldb_private::EmulateInstruction::ReadRegisterFrame,
297           &lldb_private::EmulateInstruction::WriteRegisterFrame);
298     }
299   }
300   return false;
301 }
302 
303 bool SBInstruction::DumpEmulation(const char *triple) {
304   LLDB_RECORD_METHOD(bool, SBInstruction, DumpEmulation, (const char *),
305                      triple);
306 
307   lldb::InstructionSP inst_sp(GetOpaque());
308   if (inst_sp && triple) {
309     return inst_sp->DumpEmulation(HostInfo::GetAugmentedArchSpec(triple));
310   }
311   return false;
312 }
313 
314 bool SBInstruction::TestEmulation(lldb::SBStream &output_stream,
315                                   const char *test_file) {
316   LLDB_RECORD_METHOD(bool, SBInstruction, TestEmulation,
317                      (lldb::SBStream &, const char *), output_stream,
318                      test_file);
319 
320   if (!m_opaque_sp)
321     SetOpaque(lldb::DisassemblerSP(),
322               lldb::InstructionSP(new PseudoInstruction()));
323 
324   lldb::InstructionSP inst_sp(GetOpaque());
325   if (inst_sp)
326     return inst_sp->TestEmulation(output_stream.get(), test_file);
327   return false;
328 }
329