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