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" 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 LLDB_PLUGIN_DEFINE_ADV(UnwindAssembly_x86, UnwindAssemblyX86) 34 35 // UnwindAssemblyParser_x86 method definitions 36 37 UnwindAssembly_x86::UnwindAssembly_x86(const ArchSpec &arch) 38 : lldb_private::UnwindAssembly(arch), 39 m_assembly_inspection_engine(new x86AssemblyInspectionEngine(arch)) {} 40 41 UnwindAssembly_x86::~UnwindAssembly_x86() { 42 delete m_assembly_inspection_engine; 43 } 44 45 bool UnwindAssembly_x86::GetNonCallSiteUnwindPlanFromAssembly( 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 std::vector<uint8_t> function_text(func.GetByteSize()); 55 Status error; 56 if (process_sp->GetTarget().ReadMemory( 57 func.GetBaseAddress(), function_text.data(), func.GetByteSize(), 58 error) == func.GetByteSize()) { 59 RegisterContextSP reg_ctx(thread.GetRegisterContext()); 60 m_assembly_inspection_engine->Initialize(reg_ctx); 61 return m_assembly_inspection_engine->GetNonCallSiteUnwindPlanFromAssembly( 62 function_text.data(), func.GetByteSize(), func, unwind_plan); 63 } 64 return false; 65 } 66 67 bool UnwindAssembly_x86::AugmentUnwindPlanFromCallSite( 68 AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) { 69 bool do_augment_unwindplan = true; 70 71 UnwindPlan::RowSP first_row = unwind_plan.GetRowForFunctionOffset(0); 72 UnwindPlan::RowSP last_row = unwind_plan.GetRowForFunctionOffset(-1); 73 74 int wordsize = 8; 75 ProcessSP process_sp(thread.GetProcess()); 76 if (process_sp.get() == nullptr) 77 return false; 78 79 wordsize = process_sp->GetTarget().GetArchitecture().GetAddressByteSize(); 80 81 RegisterNumber sp_regnum(thread, eRegisterKindGeneric, 82 LLDB_REGNUM_GENERIC_SP); 83 RegisterNumber pc_regnum(thread, eRegisterKindGeneric, 84 LLDB_REGNUM_GENERIC_PC); 85 86 // Does this UnwindPlan describe the prologue? I want to see that the CFA is 87 // set in terms of the stack pointer plus an offset, and I want to see that 88 // rip is retrieved at the CFA-wordsize. If there is no description of the 89 // prologue, don't try to augment this eh_frame unwinder code, fall back to 90 // assembly parsing instead. 91 92 if (first_row->GetCFAValue().GetValueType() != 93 UnwindPlan::Row::FAValue::isRegisterPlusOffset || 94 RegisterNumber(thread, unwind_plan.GetRegisterKind(), 95 first_row->GetCFAValue().GetRegisterNumber()) != 96 sp_regnum || 97 first_row->GetCFAValue().GetOffset() != wordsize) { 98 return false; 99 } 100 UnwindPlan::Row::RegisterLocation first_row_pc_loc; 101 if (!first_row->GetRegisterInfo( 102 pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()), 103 first_row_pc_loc) || 104 !first_row_pc_loc.IsAtCFAPlusOffset() || 105 first_row_pc_loc.GetOffset() != -wordsize) { 106 return false; 107 } 108 109 // It looks like the prologue is described. Is the epilogue described? If it 110 // is, no need to do any augmentation. 111 112 if (first_row != last_row && 113 first_row->GetOffset() != last_row->GetOffset()) { 114 // The first & last row have the same CFA register and the same CFA offset 115 // value and the CFA register is esp/rsp (the stack pointer). 116 117 // We're checking that both of them have an unwind rule like "CFA=esp+4" or 118 // CFA+rsp+8". 119 120 if (first_row->GetCFAValue().GetValueType() == 121 last_row->GetCFAValue().GetValueType() && 122 first_row->GetCFAValue().GetRegisterNumber() == 123 last_row->GetCFAValue().GetRegisterNumber() && 124 first_row->GetCFAValue().GetOffset() == 125 last_row->GetCFAValue().GetOffset()) { 126 // Get the register locations for eip/rip from the first & last rows. Are 127 // they both CFA plus an offset? Is it the same offset? 128 129 UnwindPlan::Row::RegisterLocation last_row_pc_loc; 130 if (last_row->GetRegisterInfo( 131 pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()), 132 last_row_pc_loc)) { 133 if (last_row_pc_loc.IsAtCFAPlusOffset() && 134 first_row_pc_loc.GetOffset() == last_row_pc_loc.GetOffset()) { 135 136 // One last sanity check: Is the unwind rule for getting the caller 137 // pc value "deref the CFA-4" or "deref the CFA-8"? 138 139 // If so, we have an UnwindPlan that already describes the epilogue 140 // and we don't need to modify it at all. 141 142 if (first_row_pc_loc.GetOffset() == -wordsize) { 143 return true; 144 } 145 } 146 } 147 } 148 } 149 150 if (do_augment_unwindplan) { 151 if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0) 152 return false; 153 if (m_assembly_inspection_engine == nullptr) 154 return false; 155 std::vector<uint8_t> function_text(func.GetByteSize()); 156 Status error; 157 if (process_sp->GetTarget().ReadMemory( 158 func.GetBaseAddress(), function_text.data(), func.GetByteSize(), 159 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 Status error; 187 if (target.ReadMemory(func.GetBaseAddress(), opcode_data.data(), 4, 188 error) == 4) { 189 uint8_t i386_push_mov[] = {0x55, 0x89, 0xe5}; 190 uint8_t x86_64_push_mov[] = {0x55, 0x48, 0x89, 0xe5}; 191 192 if (memcmp(opcode_data.data(), i386_push_mov, sizeof(i386_push_mov)) == 193 0 || 194 memcmp(opcode_data.data(), x86_64_push_mov, 195 sizeof(x86_64_push_mov)) == 0) { 196 ABISP abi_sp = process_sp->GetABI(); 197 if (abi_sp) { 198 return abi_sp->CreateDefaultUnwindPlan(unwind_plan); 199 } 200 } 201 } 202 } 203 return false; 204 } 205 206 bool UnwindAssembly_x86::FirstNonPrologueInsn( 207 AddressRange &func, const ExecutionContext &exe_ctx, 208 Address &first_non_prologue_insn) { 209 210 if (!func.GetBaseAddress().IsValid()) 211 return false; 212 213 Target *target = exe_ctx.GetTargetPtr(); 214 if (target == nullptr) 215 return false; 216 217 if (m_assembly_inspection_engine == nullptr) 218 return false; 219 220 std::vector<uint8_t> function_text(func.GetByteSize()); 221 Status error; 222 if (target->ReadMemory(func.GetBaseAddress(), function_text.data(), 223 func.GetByteSize(), error) == func.GetByteSize()) { 224 size_t offset; 225 if (m_assembly_inspection_engine->FindFirstNonPrologueInstruction( 226 function_text.data(), func.GetByteSize(), offset)) { 227 first_non_prologue_insn = func.GetBaseAddress(); 228 first_non_prologue_insn.Slide(offset); 229 } 230 return true; 231 } 232 return false; 233 } 234 235 UnwindAssembly *UnwindAssembly_x86::CreateInstance(const ArchSpec &arch) { 236 const llvm::Triple::ArchType cpu = arch.GetMachine(); 237 if (cpu == llvm::Triple::x86 || cpu == llvm::Triple::x86_64) 238 return new UnwindAssembly_x86(arch); 239 return nullptr; 240 } 241 242 void UnwindAssembly_x86::Initialize() { 243 PluginManager::RegisterPlugin(GetPluginNameStatic(), 244 GetPluginDescriptionStatic(), CreateInstance); 245 } 246 247 void UnwindAssembly_x86::Terminate() { 248 PluginManager::UnregisterPlugin(CreateInstance); 249 } 250 251 llvm::StringRef UnwindAssembly_x86::GetPluginDescriptionStatic() { 252 return "i386 and x86_64 assembly language profiler plugin."; 253 } 254