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 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 68 bool UnwindAssembly_x86::AugmentUnwindPlanFromCallSite( 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, 83 LLDB_REGNUM_GENERIC_SP); 84 RegisterNumber pc_regnum(thread, eRegisterKindGeneric, 85 LLDB_REGNUM_GENERIC_PC); 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 172 bool UnwindAssembly_x86::GetFastUnwindPlan(AddressRange &func, Thread &thread, 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 209 bool UnwindAssembly_x86::FirstNonPrologueInsn( 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 240 UnwindAssembly *UnwindAssembly_x86::CreateInstance(const ArchSpec &arch) { 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 249 ConstString UnwindAssembly_x86::GetPluginName() { 250 return GetPluginNameStatic(); 251 } 252 253 uint32_t UnwindAssembly_x86::GetPluginVersion() { return 1; } 254 255 void UnwindAssembly_x86::Initialize() { 256 PluginManager::RegisterPlugin(GetPluginNameStatic(), 257 GetPluginDescriptionStatic(), CreateInstance); 258 } 259 260 void UnwindAssembly_x86::Terminate() { 261 PluginManager::UnregisterPlugin(CreateInstance); 262 } 263 264 lldb_private::ConstString UnwindAssembly_x86::GetPluginNameStatic() { 265 static ConstString g_name("x86"); 266 return g_name; 267 } 268 269 const char *UnwindAssembly_x86::GetPluginDescriptionStatic() { 270 return "i386 and x86_64 assembly language profiler plugin."; 271 } 272