Created
February 24, 2015 20:29
-
-
Save Keno/ef471f766d8dddf074e7 to your computer and use it in GitHub Desktop.
UnwindAssembly-mips.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//===-- UnwindAssembly-mips.cpp ----------------------------------*- C++ -*-===// | |
// | |
// The LLVM Compiler Infrastructure | |
// | |
// This file is distributed under the University of Illinois Open Source | |
// License. See LICENSE.TXT for details. | |
// | |
//===----------------------------------------------------------------------===// | |
#include "UnwindAssembly-mips.h" | |
#define GET_INSTRINFO_ENUM | |
#include "MipsGenInstrInfo.inc" | |
#include "llvm-c/Disassembler.h" | |
#include "llvm/Support/TargetSelect.h" | |
#include "llvm/Support/TargetRegistry.h" | |
#include "llvm/MC/MCAsmInfo.h" | |
#include "llvm/MC/MCInst.h" | |
#include "llvm/MC/MCDisassembler.h" | |
#include "llvm/MC/MCRegisterInfo.h" | |
#include "llvm/MC/MCContext.h" | |
#include "lldb/Core/Address.h" | |
#include "lldb/Core/Error.h" | |
#include "lldb/Core/ArchSpec.h" | |
#include "lldb/Core/PluginManager.h" | |
#include "lldb/Symbol/UnwindPlan.h" | |
#include "lldb/Target/ExecutionContext.h" | |
#include "lldb/Target/Process.h" | |
#include "lldb/Target/RegisterContext.h" | |
#include "lldb/Target/Thread.h" | |
#include "lldb/Target/Target.h" | |
#include "lldb/Target/UnwindAssembly.h" | |
using namespace lldb; | |
using namespace lldb_private; | |
enum CPU | |
{ | |
k_mips32, | |
k_mips64 | |
}; | |
//----------------------------------------------------------------------------------------------- | |
// AssemblyParse_mips local-file class definition & implementation functions | |
//----------------------------------------------------------------------------------------------- | |
class AssemblyParse_mips | |
{ | |
public: | |
AssemblyParse_mips (const ExecutionContext &exe_ctx, int cpu, ArchSpec &arch, AddressRange func); | |
~AssemblyParse_mips (); | |
bool get_non_call_site_unwind_plan (UnwindPlan &unwind_plan); | |
bool augment_unwind_plan_from_call_site (AddressRange& func, UnwindPlan &unwind_plan); | |
bool get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_plan); | |
bool find_first_non_prologue_insn (Address &address); | |
private: | |
bool nonvolatile_reg_p (uint32_t machine_regno); | |
bool add_imm_sp_pattern_p (const llvm::MCInst &Inst, int& amount); | |
bool store_reg_pattern_p (const llvm::MCInst &Inst, uint32_t& regno, int& offset); | |
bool mov_sp_to_fp_pattern_p (const llvm::MCInst &Inst); | |
bool mov_fp_to_sp_pattern_p (const llvm::MCInst &Inst); | |
const ExecutionContext m_exe_ctx; | |
AddressRange m_func_bounds; | |
Address m_cur_insn; | |
uint32_t m_machine_ra_regnum; | |
uint32_t m_machine_pc_regnum; | |
uint32_t m_machine_sp_regnum; | |
uint32_t m_machine_fp_regnum; | |
int m_cpu; | |
ArchSpec m_arch; | |
std::unique_ptr<llvm::MCDisassembler> m_disasm; | |
std::unique_ptr<llvm::MCRegisterInfo> m_MRI; | |
std::unique_ptr<llvm::MCAsmInfo> m_MAI; | |
std::unique_ptr<llvm::MCSubtargetInfo> m_MSI; | |
std::unique_ptr<llvm::MCContext> m_Ctx; | |
DISALLOW_COPY_AND_ASSIGN (AssemblyParse_mips); | |
}; | |
static inline uint32_t generic_reg_to_machine(RegisterContext *reg_ctx, uint32_t kind) | |
{ | |
uint32_t reg_idx = reg_ctx->ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,kind); | |
if (reg_idx == LLDB_INVALID_REGNUM) | |
return LLDB_INVALID_REGNUM; | |
return reg_ctx->GetRegisterInfoAtIndex(reg_idx)->kinds[eRegisterKindGCC]; | |
} | |
AssemblyParse_mips::AssemblyParse_mips (const ExecutionContext &exe_ctx, int cpu, ArchSpec &arch, AddressRange func) : | |
m_exe_ctx (exe_ctx), | |
m_func_bounds(func), | |
m_cur_insn (), | |
m_machine_ra_regnum (LLDB_INVALID_REGNUM), | |
m_machine_sp_regnum (LLDB_INVALID_REGNUM), | |
m_machine_fp_regnum (LLDB_INVALID_REGNUM), | |
m_cpu(cpu), | |
m_arch(arch) | |
{ | |
// we only look at prologue - it will be complete earlier than 512 bytes into func | |
if (m_func_bounds.GetByteSize() == 0) | |
m_func_bounds.SetByteSize(512); | |
Thread *thread = m_exe_ctx.GetThreadPtr(); | |
if (thread) | |
{ | |
RegisterContext *reg_ctx = thread->GetRegisterContext().get(); | |
if (reg_ctx) | |
{ | |
m_machine_pc_regnum = generic_reg_to_machine(reg_ctx,LLDB_REGNUM_GENERIC_PC); | |
m_machine_ra_regnum = generic_reg_to_machine(reg_ctx,LLDB_REGNUM_GENERIC_RA); | |
m_machine_sp_regnum = generic_reg_to_machine(reg_ctx,LLDB_REGNUM_GENERIC_SP); | |
m_machine_fp_regnum = generic_reg_to_machine(reg_ctx,LLDB_REGNUM_GENERIC_FP); | |
} | |
} | |
// Create the disassembler | |
std::string Error; | |
llvm::Triple TheTriple = m_arch.GetTriple(); | |
const llvm::Target *TheTarget = llvm::TargetRegistry::lookupTarget(TheTriple.getTriple(), Error); | |
assert(TheTarget); | |
m_MRI.reset(TheTarget->createMCRegInfo(TheTriple.getTriple())); | |
assert(m_MRI.get()); | |
m_MAI.reset(TheTarget->createMCAsmInfo(*m_MRI, TheTriple.getTriple())); | |
m_MSI.reset(TheTarget->createMCSubtargetInfo(TheTriple.getTriple(), "", "")); | |
assert(m_MAI.get() && m_MSI.get()); | |
m_Ctx.reset(new llvm::MCContext(m_MAI.get(), m_MRI.get(), nullptr)); | |
assert(m_Ctx.get()); | |
// Set up disassembler | |
m_disasm.reset(TheTarget->createMCDisassembler(*m_MSI, *m_Ctx)); | |
assert(m_disasm.get()); | |
} | |
AssemblyParse_mips::~AssemblyParse_mips () | |
{ | |
} | |
// This function expects an mips native register number (i.e. the bits stripped out of the | |
// actual instruction), not an lldb register number. | |
bool | |
AssemblyParse_mips::nonvolatile_reg_p (uint32_t machine_regno) | |
{ | |
Process *proc = m_exe_ctx.GetProcessPtr(); | |
Thread *thread = m_exe_ctx.GetThreadPtr(); | |
if (proc && thread) { | |
const ABISP abi = proc->GetABI(); | |
RegisterContextSP reg_ctx = thread->GetRegisterContext(); | |
if (abi && reg_ctx) { | |
uint32_t reg_idx = reg_ctx->ConvertRegisterKindToRegisterNumber(eRegisterKindGCC,machine_regno); | |
if (reg_idx != LLDB_INVALID_REGNUM) | |
return !abi->RegisterIsVolatile(reg_ctx->GetRegisterInfoAtIndex(reg_idx)); | |
} | |
} | |
// Fall back path in case any of the above do not exit, note at least sp fp ra as callee-saved | |
if (machine_regno == m_machine_sp_regnum || | |
machine_regno == m_machine_fp_regnum || | |
machine_regno == m_machine_ra_regnum || | |
machine_regno == m_machine_pc_regnum) | |
return true; | |
return false; | |
} | |
// daddiu sp,sp,-X | |
bool AssemblyParse_mips::add_imm_sp_pattern_p (const llvm::MCInst &Inst, int& amount) | |
{ | |
uint32_t opc = Inst.getOpcode(); | |
if (opc == llvm::Mips::DADDiu || opc == llvm::Mips::ADDiu) | |
{ | |
if (m_machine_sp_regnum == m_MRI->getEncodingValue(Inst.getOperand(0).getReg())) | |
{ | |
if (m_machine_sp_regnum == m_MRI->getEncodingValue(Inst.getOperand(1).getReg())) | |
{ | |
/* Get the immediate operand */ | |
amount = Inst.getOperand(2).getImm(); | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
/* sd ra/fp,xx(sp) */ | |
bool AssemblyParse_mips::store_reg_pattern_p (const llvm::MCInst &Inst, uint32_t& regnum, int& offset) | |
{ | |
uint32_t opc = Inst.getOpcode(); | |
uint32_t regno; | |
if (opc == llvm::Mips::SD || opc == llvm::Mips::SW) | |
{ | |
if(m_machine_sp_regnum == m_MRI->getEncodingValue(Inst.getOperand(1).getReg())) | |
{ | |
regno = m_MRI->getEncodingValue(Inst.getOperand(0).getReg()); | |
if (nonvolatile_reg_p(regno)) | |
{ | |
offset = Inst.getOperand(2).getImm(); | |
regnum = regno; | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
/* move s8, sp is actually ADDU s8, sp, $0 */ | |
bool AssemblyParse_mips::mov_sp_to_fp_pattern_p (const llvm::MCInst &Inst) | |
{ | |
uint32_t opc = Inst.getOpcode(); | |
if ((opc == llvm::Mips::ADDu || opc == llvm::Mips::DADDu) && | |
m_machine_fp_regnum == m_MRI->getEncodingValue(Inst.getOperand(0).getReg()) && | |
m_machine_sp_regnum == m_MRI->getEncodingValue(Inst.getOperand(1).getReg()) && | |
0 == Inst.getOperand(2).getImm()) | |
return true; | |
return false; | |
} | |
/* move s8, sp is actually ADDU s8, sp, $0 */ | |
bool AssemblyParse_mips::mov_fp_to_sp_pattern_p (const llvm::MCInst &Inst) | |
{ | |
uint32_t opc = Inst.getOpcode(); | |
if ((opc == llvm::Mips::ADDu || opc == llvm::Mips::DADDu) && | |
m_machine_sp_regnum == m_MRI->getEncodingValue(Inst.getOperand(0).getReg()) && | |
m_machine_fp_regnum == m_MRI->getEncodingValue(Inst.getOperand(1).getReg()) && | |
0 == Inst.getOperand(2).getImm()) | |
return true; | |
return false; | |
} | |
bool | |
AssemblyParse_mips::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan) | |
{ | |
UnwindPlan::RowSP row(new UnwindPlan::Row); | |
m_cur_insn = m_func_bounds.GetBaseAddress (); | |
int current_func_text_offset = 0; | |
int bytes_from_initial_sp = 0; | |
UnwindPlan::Row::RegisterLocation initial_regloc; | |
Error error; | |
if (!m_cur_insn.IsValid()) | |
{ | |
return false; | |
} | |
unwind_plan.SetPlanValidAddressRange (m_func_bounds); | |
unwind_plan.SetRegisterKind (eRegisterKindGCC); | |
// At the start of the function, CFA is previous SP register value | |
row->SetOffset (current_func_text_offset); | |
row->SetCFARegister (m_machine_sp_regnum); | |
row->SetCFAOffset (0); | |
// stack pointer hasn't changed yet | |
initial_regloc.SetIsCFAPlusOffset (0); | |
row->SetRegisterInfo (m_machine_sp_regnum, initial_regloc); | |
unwind_plan.AppendRow (row); | |
// Allocate a new Row, populate it with the existing Row contents. | |
UnwindPlan::Row *newrow = new UnwindPlan::Row; | |
*newrow = *row.get(); | |
row.reset(newrow); | |
const bool prefer_file_cache = true; | |
Target *target = m_exe_ctx.GetTargetPtr(); | |
if (!target) | |
return false; | |
uint64_t Size; | |
llvm::MCDisassembler::DecodeStatus S; | |
int stack_offset; | |
uint32_t machine_regno; // register numbers extracted directly out of instructions | |
while (m_func_bounds.ContainsFileAddress (m_cur_insn)) | |
{ | |
llvm::MCInst Inst; | |
if (!m_cur_insn.IsValid()) | |
return false; | |
// Read the instruction from memory | |
const uint32_t max_op_byte_size = m_arch.GetMaximumOpcodeByteSize(); | |
llvm::SmallVector <uint8_t, 32> opcode_data; | |
opcode_data.resize (max_op_byte_size); | |
if (target->ReadMemory (m_cur_insn, prefer_file_cache, opcode_data.data(), | |
max_op_byte_size, error) == static_cast<size_t>(-1)) | |
{ | |
return false; | |
} | |
// Disassemble the instruction | |
const addr_t PC = m_cur_insn.GetFileAddress(); | |
S = m_disasm->getInstruction(Inst, Size, opcode_data, PC, llvm::nulls(), llvm::nulls()); | |
if (S != llvm::MCDisassembler::Success) | |
return false; | |
if (!m_cur_insn.IsValid()) | |
return false; | |
/* check addiu sp,sp,XX */ | |
if (add_imm_sp_pattern_p (Inst,stack_offset)) | |
{ | |
bytes_from_initial_sp += (-stack_offset); // The bytes allocated on stack | |
if (bytes_from_initial_sp == 0) | |
{ | |
//SP is restored | |
//Create a fresh, empty Row and RegisterLocation - don't mention any other registers | |
UnwindPlan::RowSP last_row(new UnwindPlan::Row); | |
UnwindPlan::Row::RegisterLocation last_regloc; | |
last_row->SetOffset (current_func_text_offset + Size); | |
last_row->SetCFARegister (m_machine_sp_regnum); | |
last_row->SetCFAOffset (0); | |
last_regloc.SetIsCFAPlusOffset (0); | |
last_row->SetRegisterInfo (m_machine_sp_regnum, last_regloc); | |
unwind_plan.AppendRow (last_row); | |
break; | |
} | |
else if (row->GetCFARegister() == m_machine_sp_regnum) | |
{ | |
row->SetOffset (current_func_text_offset + Size); // Set offset of row in the function | |
UnwindPlan::Row::RegisterLocation regloc; | |
regloc.SetIsCFAPlusOffset (bytes_from_initial_sp); | |
row->SetRegisterInfo (m_machine_sp_regnum, regloc); | |
unwind_plan.AppendRow (row); | |
goto loopnext; | |
} | |
} | |
/* Check sw <ra/fp>,xx(sp) */ | |
if (store_reg_pattern_p (Inst, machine_regno, stack_offset)) | |
{ | |
row->SetOffset (current_func_text_offset + Size); | |
UnwindPlan::Row::RegisterLocation regloc; | |
regloc.SetAtCFAPlusOffset (stack_offset); | |
row->SetRegisterInfo (machine_regno, regloc); | |
unwind_plan.AppendRow (row); | |
goto loopnext; | |
} | |
loopnext: | |
m_cur_insn.SetOffset (m_cur_insn.GetOffset() + Size); | |
current_func_text_offset += Size; | |
// Allocate a new Row, populate it with the existing Row contents. | |
newrow = new UnwindPlan::Row; | |
*newrow = *row.get(); | |
row.reset(newrow); | |
} | |
unwind_plan.SetSourceName ("assembly insn profiling"); | |
unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); | |
unwind_plan.SetReturnAddressRegister(m_machine_ra_regnum); | |
unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes); | |
return true; | |
} | |
bool | |
AssemblyParse_mips::augment_unwind_plan_from_call_site (AddressRange& func, UnwindPlan &unwind_plan) | |
{ | |
return false; | |
} | |
bool | |
AssemblyParse_mips::get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_plan) | |
{ | |
return false; | |
} | |
bool | |
AssemblyParse_mips::find_first_non_prologue_insn (Address &address) | |
{ | |
m_cur_insn = m_func_bounds.GetBaseAddress (); | |
if (!m_cur_insn.IsValid()) | |
{ | |
return false; | |
} | |
const bool prefer_file_cache = true; | |
Target *target = m_exe_ctx.GetTargetPtr(); | |
while (m_func_bounds.ContainsFileAddress (m_cur_insn)) | |
{ | |
int offset; | |
uint32_t regno; | |
uint64_t Size; | |
llvm::MCInst Inst; | |
llvm::MCDisassembler::DecodeStatus S; | |
Error error; | |
if (!m_cur_insn.IsValid()) | |
return false; | |
// Read the instruction from memory | |
const uint32_t max_op_byte_size = m_arch.GetMaximumOpcodeByteSize(); | |
llvm::SmallVector <uint8_t, 32> opcode_data; | |
opcode_data.resize (max_op_byte_size); | |
if (target->ReadMemory (m_cur_insn, prefer_file_cache, opcode_data.data(), | |
max_op_byte_size, error) == static_cast<size_t>(-1)) | |
{ | |
return false; | |
} | |
// Disassemble the instruction | |
const addr_t PC = m_cur_insn.GetFileAddress(); | |
S = m_disasm->getInstruction(Inst, Size, opcode_data, PC, llvm::nulls(), llvm::nulls()); | |
if (S != llvm::MCDisassembler::Success) | |
return false; | |
if (add_imm_sp_pattern_p (Inst,offset) | |
|| store_reg_pattern_p (Inst,regno, offset) | |
|| mov_sp_to_fp_pattern_p (Inst)) | |
{ | |
m_cur_insn.SetOffset (m_cur_insn.GetOffset() + Size); | |
continue; | |
} | |
// Unknown non-prologue instruction - stop scanning | |
break; | |
} | |
address = m_cur_insn; | |
return true; | |
} | |
//----------------------------------------------------------------------------------------------- | |
// UnwindAssemblyParser_mips method definitions | |
//----------------------------------------------------------------------------------------------- | |
UnwindAssembly_mips::UnwindAssembly_mips (const ArchSpec &arch, int cpu) : | |
lldb_private::UnwindAssembly(arch), | |
m_cpu(cpu), | |
m_arch(arch) | |
{ | |
} | |
UnwindAssembly_mips::~UnwindAssembly_mips () | |
{ | |
} | |
bool | |
UnwindAssembly_mips::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& func, Thread& thread, UnwindPlan& unwind_plan) | |
{ | |
ExecutionContext exe_ctx (thread.shared_from_this()); | |
AssemblyParse_mips asm_parse(exe_ctx, m_cpu, m_arch, func); | |
return asm_parse.get_non_call_site_unwind_plan (unwind_plan); | |
} | |
bool | |
UnwindAssembly_mips::AugmentUnwindPlanFromCallSite (AddressRange& func, Thread& thread, UnwindPlan& unwind_plan) | |
{ | |
ExecutionContext exe_ctx (thread.shared_from_this()); | |
AssemblyParse_mips asm_parse(exe_ctx, m_cpu, m_arch, func); | |
return asm_parse.augment_unwind_plan_from_call_site (func, unwind_plan); | |
} | |
bool | |
UnwindAssembly_mips::GetFastUnwindPlan (AddressRange& func, Thread& thread, UnwindPlan &unwind_plan) | |
{ | |
ExecutionContext exe_ctx (thread.shared_from_this()); | |
AssemblyParse_mips asm_parse(exe_ctx, m_cpu, m_arch, func); | |
return asm_parse.get_fast_unwind_plan (func, unwind_plan); | |
} | |
bool | |
UnwindAssembly_mips::FirstNonPrologueInsn (AddressRange& func, const ExecutionContext &exe_ctx, Address& first_non_prologue_insn) | |
{ | |
AssemblyParse_mips asm_parse(exe_ctx, m_cpu, m_arch, func); | |
return asm_parse.find_first_non_prologue_insn (first_non_prologue_insn); | |
} | |
UnwindAssembly * | |
UnwindAssembly_mips::CreateInstance (const ArchSpec &arch) | |
{ | |
const llvm::Triple::ArchType cpu = arch.GetMachine (); | |
if (cpu == llvm::Triple::mips64) | |
return new UnwindAssembly_mips (arch, k_mips64); | |
else if (cpu == llvm::Triple::mips) | |
return new UnwindAssembly_mips (arch, k_mips32); | |
return NULL; | |
} | |
//------------------------------------------------------------------ | |
// PluginInterface protocol in UnwindAssemblyParser_mips | |
//------------------------------------------------------------------ | |
ConstString | |
UnwindAssembly_mips::GetPluginName() | |
{ | |
return GetPluginNameStatic(); | |
} | |
uint32_t | |
UnwindAssembly_mips::GetPluginVersion() | |
{ | |
return 1; | |
} | |
void | |
UnwindAssembly_mips::Initialize() | |
{ | |
PluginManager::RegisterPlugin (GetPluginNameStatic(), | |
GetPluginDescriptionStatic(), | |
CreateInstance); | |
} | |
void | |
UnwindAssembly_mips::Terminate() | |
{ | |
PluginManager::UnregisterPlugin (CreateInstance); | |
} | |
lldb_private::ConstString | |
UnwindAssembly_mips::GetPluginNameStatic() | |
{ | |
static ConstString g_name("mips"); | |
return g_name; | |
} | |
const char * | |
UnwindAssembly_mips::GetPluginDescriptionStatic() | |
{ | |
return "mips assembly language profiler plugin."; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment