Path: blob/main/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
39648 views
//===-- UnwindAssembly-x86.cpp --------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "UnwindAssembly-x86.h"9#include "x86AssemblyInspectionEngine.h"1011#include "llvm-c/Disassembler.h"12#include "llvm/ADT/STLExtras.h"13#include "llvm/Support/TargetSelect.h"1415#include "lldb/Core/Address.h"16#include "lldb/Core/PluginManager.h"17#include "lldb/Symbol/UnwindPlan.h"18#include "lldb/Target/ABI.h"19#include "lldb/Target/ExecutionContext.h"20#include "lldb/Target/Process.h"21#include "lldb/Target/RegisterContext.h"22#include "lldb/Target/RegisterNumber.h"23#include "lldb/Target/Target.h"24#include "lldb/Target/Thread.h"25#include "lldb/Target/UnwindAssembly.h"26#include "lldb/Utility/ArchSpec.h"27#include "lldb/Utility/Status.h"2829using namespace lldb;30using namespace lldb_private;3132LLDB_PLUGIN_DEFINE_ADV(UnwindAssembly_x86, UnwindAssemblyX86)3334// UnwindAssemblyParser_x86 method definitions3536UnwindAssembly_x86::UnwindAssembly_x86(const ArchSpec &arch)37: lldb_private::UnwindAssembly(arch),38m_assembly_inspection_engine(new x86AssemblyInspectionEngine(arch)) {}3940UnwindAssembly_x86::~UnwindAssembly_x86() {41delete m_assembly_inspection_engine;42}4344bool UnwindAssembly_x86::GetNonCallSiteUnwindPlanFromAssembly(45AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {46if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0)47return false;48if (m_assembly_inspection_engine == nullptr)49return false;50ProcessSP process_sp(thread.GetProcess());51if (process_sp.get() == nullptr)52return false;53std::vector<uint8_t> function_text(func.GetByteSize());54Status error;55if (process_sp->GetTarget().ReadMemory(56func.GetBaseAddress(), function_text.data(), func.GetByteSize(),57error) == func.GetByteSize()) {58RegisterContextSP reg_ctx(thread.GetRegisterContext());59m_assembly_inspection_engine->Initialize(reg_ctx);60return m_assembly_inspection_engine->GetNonCallSiteUnwindPlanFromAssembly(61function_text.data(), func.GetByteSize(), func, unwind_plan);62}63return false;64}6566bool UnwindAssembly_x86::AugmentUnwindPlanFromCallSite(67AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {68bool do_augment_unwindplan = true;6970UnwindPlan::RowSP first_row = unwind_plan.GetRowForFunctionOffset(0);71UnwindPlan::RowSP last_row = unwind_plan.GetRowForFunctionOffset(-1);7273int wordsize = 8;74ProcessSP process_sp(thread.GetProcess());75if (process_sp.get() == nullptr)76return false;7778wordsize = process_sp->GetTarget().GetArchitecture().GetAddressByteSize();7980RegisterNumber sp_regnum(thread, eRegisterKindGeneric,81LLDB_REGNUM_GENERIC_SP);82RegisterNumber pc_regnum(thread, eRegisterKindGeneric,83LLDB_REGNUM_GENERIC_PC);8485// Does this UnwindPlan describe the prologue? I want to see that the CFA is86// set in terms of the stack pointer plus an offset, and I want to see that87// rip is retrieved at the CFA-wordsize. If there is no description of the88// prologue, don't try to augment this eh_frame unwinder code, fall back to89// assembly parsing instead.9091if (first_row->GetCFAValue().GetValueType() !=92UnwindPlan::Row::FAValue::isRegisterPlusOffset ||93RegisterNumber(thread, unwind_plan.GetRegisterKind(),94first_row->GetCFAValue().GetRegisterNumber()) !=95sp_regnum ||96first_row->GetCFAValue().GetOffset() != wordsize) {97return false;98}99UnwindPlan::Row::RegisterLocation first_row_pc_loc;100if (!first_row->GetRegisterInfo(101pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()),102first_row_pc_loc) ||103!first_row_pc_loc.IsAtCFAPlusOffset() ||104first_row_pc_loc.GetOffset() != -wordsize) {105return false;106}107108// It looks like the prologue is described. Is the epilogue described? If it109// is, no need to do any augmentation.110111if (first_row != last_row &&112first_row->GetOffset() != last_row->GetOffset()) {113// The first & last row have the same CFA register and the same CFA offset114// value and the CFA register is esp/rsp (the stack pointer).115116// We're checking that both of them have an unwind rule like "CFA=esp+4" or117// CFA+rsp+8".118119if (first_row->GetCFAValue().GetValueType() ==120last_row->GetCFAValue().GetValueType() &&121first_row->GetCFAValue().GetRegisterNumber() ==122last_row->GetCFAValue().GetRegisterNumber() &&123first_row->GetCFAValue().GetOffset() ==124last_row->GetCFAValue().GetOffset()) {125// Get the register locations for eip/rip from the first & last rows. Are126// they both CFA plus an offset? Is it the same offset?127128UnwindPlan::Row::RegisterLocation last_row_pc_loc;129if (last_row->GetRegisterInfo(130pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()),131last_row_pc_loc)) {132if (last_row_pc_loc.IsAtCFAPlusOffset() &&133first_row_pc_loc.GetOffset() == last_row_pc_loc.GetOffset()) {134135// One last sanity check: Is the unwind rule for getting the caller136// pc value "deref the CFA-4" or "deref the CFA-8"?137138// If so, we have an UnwindPlan that already describes the epilogue139// and we don't need to modify it at all.140141if (first_row_pc_loc.GetOffset() == -wordsize) {142return true;143}144}145}146}147}148149if (do_augment_unwindplan) {150if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0)151return false;152if (m_assembly_inspection_engine == nullptr)153return false;154std::vector<uint8_t> function_text(func.GetByteSize());155Status error;156if (process_sp->GetTarget().ReadMemory(157func.GetBaseAddress(), function_text.data(), func.GetByteSize(),158error) == func.GetByteSize()) {159RegisterContextSP reg_ctx(thread.GetRegisterContext());160m_assembly_inspection_engine->Initialize(reg_ctx);161return m_assembly_inspection_engine->AugmentUnwindPlanFromCallSite(162function_text.data(), func.GetByteSize(), func, unwind_plan, reg_ctx);163}164}165166return false;167}168169bool UnwindAssembly_x86::GetFastUnwindPlan(AddressRange &func, Thread &thread,170UnwindPlan &unwind_plan) {171// if prologue is172// 55 pushl %ebp173// 89 e5 movl %esp, %ebp174// or175// 55 pushq %rbp176// 48 89 e5 movq %rsp, %rbp177178// We should pull in the ABI architecture default unwind plan and return that179180llvm::SmallVector<uint8_t, 4> opcode_data;181182ProcessSP process_sp = thread.GetProcess();183if (process_sp) {184Target &target(process_sp->GetTarget());185Status error;186if (target.ReadMemory(func.GetBaseAddress(), opcode_data.data(), 4,187error) == 4) {188uint8_t i386_push_mov[] = {0x55, 0x89, 0xe5};189uint8_t x86_64_push_mov[] = {0x55, 0x48, 0x89, 0xe5};190191if (memcmp(opcode_data.data(), i386_push_mov, sizeof(i386_push_mov)) ==1920 ||193memcmp(opcode_data.data(), x86_64_push_mov,194sizeof(x86_64_push_mov)) == 0) {195ABISP abi_sp = process_sp->GetABI();196if (abi_sp) {197return abi_sp->CreateDefaultUnwindPlan(unwind_plan);198}199}200}201}202return false;203}204205bool UnwindAssembly_x86::FirstNonPrologueInsn(206AddressRange &func, const ExecutionContext &exe_ctx,207Address &first_non_prologue_insn) {208209if (!func.GetBaseAddress().IsValid())210return false;211212Target *target = exe_ctx.GetTargetPtr();213if (target == nullptr)214return false;215216if (m_assembly_inspection_engine == nullptr)217return false;218219std::vector<uint8_t> function_text(func.GetByteSize());220Status error;221if (target->ReadMemory(func.GetBaseAddress(), function_text.data(),222func.GetByteSize(), error) == func.GetByteSize()) {223size_t offset;224if (m_assembly_inspection_engine->FindFirstNonPrologueInstruction(225function_text.data(), func.GetByteSize(), offset)) {226first_non_prologue_insn = func.GetBaseAddress();227first_non_prologue_insn.Slide(offset);228}229return true;230}231return false;232}233234UnwindAssembly *UnwindAssembly_x86::CreateInstance(const ArchSpec &arch) {235const llvm::Triple::ArchType cpu = arch.GetMachine();236if (cpu == llvm::Triple::x86 || cpu == llvm::Triple::x86_64)237return new UnwindAssembly_x86(arch);238return nullptr;239}240241void UnwindAssembly_x86::Initialize() {242PluginManager::RegisterPlugin(GetPluginNameStatic(),243GetPluginDescriptionStatic(), CreateInstance);244}245246void UnwindAssembly_x86::Terminate() {247PluginManager::UnregisterPlugin(CreateInstance);248}249250llvm::StringRef UnwindAssembly_x86::GetPluginDescriptionStatic() {251return "i386 and x86_64 assembly language profiler plugin.";252}253254255