LLDB  mainline
UnwindAssembly-x86.cpp
Go to the documentation of this file.
1 //===-- UnwindAssembly-x86.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 "UnwindAssembly-x86.h"
11 
12 #include "llvm-c/Disassembler.h"
13 #include "llvm/ADT/STLExtras.h"
14 #include "llvm/Support/TargetSelect.h"
15 
16 #include "lldb/Core/Address.h"
18 #include "lldb/Symbol/UnwindPlan.h"
19 #include "lldb/Target/ABI.h"
21 #include "lldb/Target/Process.h"
24 #include "lldb/Target/Target.h"
25 #include "lldb/Target/Thread.h"
27 #include "lldb/Utility/ArchSpec.h"
28 #include "lldb/Utility/Status.h"
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 
34 
35 // UnwindAssemblyParser_x86 method definitions
36 
39  m_assembly_inspection_engine(new x86AssemblyInspectionEngine(arch)) {}
40 
42  delete m_assembly_inspection_engine;
43 }
44 
46  AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {
47  if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0)
48  return false;
49  if (m_assembly_inspection_engine == nullptr)
50  return false;
51  ProcessSP process_sp(thread.GetProcess());
52  if (process_sp.get() == nullptr)
53  return false;
54  const bool prefer_file_cache = true;
55  std::vector<uint8_t> function_text(func.GetByteSize());
56  Status error;
57  if (process_sp->GetTarget().ReadMemory(
58  func.GetBaseAddress(), prefer_file_cache, function_text.data(),
59  func.GetByteSize(), error) == func.GetByteSize()) {
60  RegisterContextSP reg_ctx(thread.GetRegisterContext());
61  m_assembly_inspection_engine->Initialize(reg_ctx);
62  return m_assembly_inspection_engine->GetNonCallSiteUnwindPlanFromAssembly(
63  function_text.data(), func.GetByteSize(), func, unwind_plan);
64  }
65  return false;
66 }
67 
69  AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {
70  bool do_augment_unwindplan = true;
71 
72  UnwindPlan::RowSP first_row = unwind_plan.GetRowForFunctionOffset(0);
73  UnwindPlan::RowSP last_row = unwind_plan.GetRowForFunctionOffset(-1);
74 
75  int wordsize = 8;
76  ProcessSP process_sp(thread.GetProcess());
77  if (process_sp.get() == nullptr)
78  return false;
79 
80  wordsize = process_sp->GetTarget().GetArchitecture().GetAddressByteSize();
81 
82  RegisterNumber sp_regnum(thread, eRegisterKindGeneric,
84  RegisterNumber pc_regnum(thread, eRegisterKindGeneric,
86 
87  // Does this UnwindPlan describe the prologue? I want to see that the CFA is
88  // set in terms of the stack pointer plus an offset, and I want to see that
89  // rip is retrieved at the CFA-wordsize. If there is no description of the
90  // prologue, don't try to augment this eh_frame unwinder code, fall back to
91  // assembly parsing instead.
92 
93  if (first_row->GetCFAValue().GetValueType() !=
94  UnwindPlan::Row::FAValue::isRegisterPlusOffset ||
95  RegisterNumber(thread, unwind_plan.GetRegisterKind(),
96  first_row->GetCFAValue().GetRegisterNumber()) !=
97  sp_regnum ||
98  first_row->GetCFAValue().GetOffset() != wordsize) {
99  return false;
100  }
101  UnwindPlan::Row::RegisterLocation first_row_pc_loc;
102  if (!first_row->GetRegisterInfo(
103  pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()),
104  first_row_pc_loc) ||
105  !first_row_pc_loc.IsAtCFAPlusOffset() ||
106  first_row_pc_loc.GetOffset() != -wordsize) {
107  return false;
108  }
109 
110  // It looks like the prologue is described. Is the epilogue described? If it
111  // is, no need to do any augmentation.
112 
113  if (first_row != last_row &&
114  first_row->GetOffset() != last_row->GetOffset()) {
115  // The first & last row have the same CFA register and the same CFA offset
116  // value and the CFA register is esp/rsp (the stack pointer).
117 
118  // We're checking that both of them have an unwind rule like "CFA=esp+4" or
119  // CFA+rsp+8".
120 
121  if (first_row->GetCFAValue().GetValueType() ==
122  last_row->GetCFAValue().GetValueType() &&
123  first_row->GetCFAValue().GetRegisterNumber() ==
124  last_row->GetCFAValue().GetRegisterNumber() &&
125  first_row->GetCFAValue().GetOffset() ==
126  last_row->GetCFAValue().GetOffset()) {
127  // Get the register locations for eip/rip from the first & last rows. Are
128  // they both CFA plus an offset? Is it the same offset?
129 
130  UnwindPlan::Row::RegisterLocation last_row_pc_loc;
131  if (last_row->GetRegisterInfo(
132  pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()),
133  last_row_pc_loc)) {
134  if (last_row_pc_loc.IsAtCFAPlusOffset() &&
135  first_row_pc_loc.GetOffset() == last_row_pc_loc.GetOffset()) {
136 
137  // One last sanity check: Is the unwind rule for getting the caller
138  // pc value "deref the CFA-4" or "deref the CFA-8"?
139 
140  // If so, we have an UnwindPlan that already describes the epilogue
141  // and we don't need to modify it at all.
142 
143  if (first_row_pc_loc.GetOffset() == -wordsize) {
144  return true;
145  }
146  }
147  }
148  }
149  }
150 
151  if (do_augment_unwindplan) {
152  if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0)
153  return false;
154  if (m_assembly_inspection_engine == nullptr)
155  return false;
156  const bool prefer_file_cache = true;
157  std::vector<uint8_t> function_text(func.GetByteSize());
158  Status error;
159  if (process_sp->GetTarget().ReadMemory(
160  func.GetBaseAddress(), prefer_file_cache, function_text.data(),
161  func.GetByteSize(), error) == func.GetByteSize()) {
162  RegisterContextSP reg_ctx(thread.GetRegisterContext());
163  m_assembly_inspection_engine->Initialize(reg_ctx);
164  return m_assembly_inspection_engine->AugmentUnwindPlanFromCallSite(
165  function_text.data(), func.GetByteSize(), func, unwind_plan, reg_ctx);
166  }
167  }
168 
169  return false;
170 }
171 
173  UnwindPlan &unwind_plan) {
174  // if prologue is
175  // 55 pushl %ebp
176  // 89 e5 movl %esp, %ebp
177  // or
178  // 55 pushq %rbp
179  // 48 89 e5 movq %rsp, %rbp
180 
181  // We should pull in the ABI architecture default unwind plan and return that
182 
183  llvm::SmallVector<uint8_t, 4> opcode_data;
184 
185  ProcessSP process_sp = thread.GetProcess();
186  if (process_sp) {
187  Target &target(process_sp->GetTarget());
188  const bool prefer_file_cache = true;
189  Status error;
190  if (target.ReadMemory(func.GetBaseAddress(), prefer_file_cache,
191  opcode_data.data(), 4, error) == 4) {
192  uint8_t i386_push_mov[] = {0x55, 0x89, 0xe5};
193  uint8_t x86_64_push_mov[] = {0x55, 0x48, 0x89, 0xe5};
194 
195  if (memcmp(opcode_data.data(), i386_push_mov, sizeof(i386_push_mov)) ==
196  0 ||
197  memcmp(opcode_data.data(), x86_64_push_mov,
198  sizeof(x86_64_push_mov)) == 0) {
199  ABISP abi_sp = process_sp->GetABI();
200  if (abi_sp) {
201  return abi_sp->CreateDefaultUnwindPlan(unwind_plan);
202  }
203  }
204  }
205  }
206  return false;
207 }
208 
210  AddressRange &func, const ExecutionContext &exe_ctx,
211  Address &first_non_prologue_insn) {
212 
213  if (!func.GetBaseAddress().IsValid())
214  return false;
215 
216  Target *target = exe_ctx.GetTargetPtr();
217  if (target == nullptr)
218  return false;
219 
220  if (m_assembly_inspection_engine == nullptr)
221  return false;
222 
223  const bool prefer_file_cache = true;
224  std::vector<uint8_t> function_text(func.GetByteSize());
225  Status error;
226  if (target->ReadMemory(func.GetBaseAddress(), prefer_file_cache,
227  function_text.data(), func.GetByteSize(),
228  error) == func.GetByteSize()) {
229  size_t offset;
230  if (m_assembly_inspection_engine->FindFirstNonPrologueInstruction(
231  function_text.data(), func.GetByteSize(), offset)) {
232  first_non_prologue_insn = func.GetBaseAddress();
233  first_non_prologue_insn.Slide(offset);
234  }
235  return true;
236  }
237  return false;
238 }
239 
241  const llvm::Triple::ArchType cpu = arch.GetMachine();
242  if (cpu == llvm::Triple::x86 || cpu == llvm::Triple::x86_64)
243  return new UnwindAssembly_x86(arch);
244  return nullptr;
245 }
246 
247 // PluginInterface protocol in UnwindAssemblyParser_x86
248 
250  return GetPluginNameStatic();
251 }
252 
254 
256  PluginManager::RegisterPlugin(GetPluginNameStatic(),
257  GetPluginDescriptionStatic(), CreateInstance);
258 }
259 
261  PluginManager::UnregisterPlugin(CreateInstance);
262 }
263 
265  static ConstString g_name("x86");
266  return g_name;
267 }
268 
270  return "i386 and x86_64 assembly language profiler plugin.";
271 }
#define LLDB_REGNUM_GENERIC_PC
Definition: lldb-defines.h:63
lldb::RegisterKind GetRegisterKind() const
Definition: UnwindPlan.h:418
A class that represents a running process on the host machine.
lldb::addr_t GetByteSize() const
Get accessor for the byte size of this range.
Definition: AddressRange.h:222
insn ptr reg, stack ptr reg, etc not specific to any particular target
bool AugmentUnwindPlanFromCallSite(lldb_private::AddressRange &func, lldb_private::Thread &thread, lldb_private::UnwindPlan &unwind_plan) override
static lldb_private::ConstString GetPluginNameStatic()
lldb_private::ConstString GetPluginName() override
An architecture specification class.
Definition: ArchSpec.h:33
uint32_t GetAsKind(lldb::RegisterKind kind)
"lldb/Target/ExecutionContext.h" A class that contains an execution context.
#define LLDB_REGNUM_GENERIC_SP
Definition: lldb-defines.h:64
bool GetNonCallSiteUnwindPlanFromAssembly(lldb_private::AddressRange &func, lldb_private::Thread &thread, lldb_private::UnwindPlan &unwind_plan) override
uint32_t GetPluginVersion() override
bool Slide(int64_t offset)
Definition: Address.h:436
Target * GetTargetPtr() const
Returns a pointer to the target object.
static llvm::raw_ostream & error(Stream &strm)
std::shared_ptr< Row > RowSP
Definition: UnwindPlan.h:377
virtual lldb::RegisterContextSP GetRegisterContext()=0
A section + offset based address class.
Definition: Address.h:59
bool IsValid() const
Check if the object state is valid.
Definition: Address.h:332
static const char * GetPluginDescriptionStatic()
lldb::ProcessSP GetProcess() const
Definition: Thread.h:153
bool FirstNonPrologueInsn(lldb_private::AddressRange &func, const lldb_private::ExecutionContext &exe_ctx, lldb_private::Address &first_non_prologue_insn) override
size_t ReadMemory(const Address &addr, bool prefer_file_cache, void *dst, size_t dst_len, Status &error, lldb::addr_t *load_addr_ptr=nullptr)
Definition: Target.cpp:1716
A uniqued constant string class.
Definition: ConstString.h:40
bool GetFastUnwindPlan(lldb_private::AddressRange &func, lldb_private::Thread &thread, lldb_private::UnwindPlan &unwind_plan) override
Definition: SBAddress.h:15
A class to represent register numbers, and able to convert between different register numbering schem...
Address & GetBaseAddress()
Get accessor for the base address of the range.
Definition: AddressRange.h:210
static lldb_private::UnwindAssembly * CreateInstance(const lldb_private::ArchSpec &arch)
UnwindPlan::RowSP GetRowForFunctionOffset(int offset) const
Definition: UnwindPlan.cpp:380
A section + offset based address range class.
Definition: AddressRange.h:25
llvm::Triple::ArchType GetMachine() const
Returns a machine family for the current architecture.
Definition: ArchSpec.cpp:714
An error handling class.
Definition: Status.h:44
LLDB_PLUGIN_DEFINE_ADV(ObjectContainerUniversalMachO, ObjectContainerMachOArchive) void ObjectContainerUniversalMachO