You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			1242 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1242 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //===-- x86AssemblyInspectionEngine.cpp -------------------------*- C++ -*-===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "x86AssemblyInspectionEngine.h"
 | |
| 
 | |
| #include "llvm-c/Disassembler.h"
 | |
| 
 | |
| #include "lldb/Core/Address.h"
 | |
| #include "lldb/Symbol/UnwindPlan.h"
 | |
| #include "lldb/Target/RegisterContext.h"
 | |
| #include "lldb/Target/UnwindAssembly.h"
 | |
| 
 | |
| using namespace lldb_private;
 | |
| using namespace lldb;
 | |
| 
 | |
| x86AssemblyInspectionEngine::x86AssemblyInspectionEngine(const ArchSpec &arch)
 | |
|     : m_cur_insn(nullptr), m_machine_ip_regnum(LLDB_INVALID_REGNUM),
 | |
|       m_machine_sp_regnum(LLDB_INVALID_REGNUM),
 | |
|       m_machine_fp_regnum(LLDB_INVALID_REGNUM),
 | |
|       m_lldb_ip_regnum(LLDB_INVALID_REGNUM),
 | |
|       m_lldb_sp_regnum(LLDB_INVALID_REGNUM),
 | |
|       m_lldb_fp_regnum(LLDB_INVALID_REGNUM),
 | |
| 
 | |
|       m_reg_map(), m_arch(arch), m_cpu(k_cpu_unspecified), m_wordsize(-1),
 | |
|       m_register_map_initialized(false), m_disasm_context() {
 | |
|   m_disasm_context =
 | |
|       ::LLVMCreateDisasm(arch.GetTriple().getTriple().c_str(), nullptr,
 | |
|                          /*TagType=*/1, nullptr, nullptr);
 | |
| }
 | |
| 
 | |
| x86AssemblyInspectionEngine::~x86AssemblyInspectionEngine() {
 | |
|   ::LLVMDisasmDispose(m_disasm_context);
 | |
| }
 | |
| 
 | |
| void x86AssemblyInspectionEngine::Initialize(RegisterContextSP ®_ctx) {
 | |
|   m_cpu = k_cpu_unspecified;
 | |
|   m_wordsize = -1;
 | |
|   m_register_map_initialized = false;
 | |
| 
 | |
|   const llvm::Triple::ArchType cpu = m_arch.GetMachine();
 | |
|   if (cpu == llvm::Triple::x86)
 | |
|     m_cpu = k_i386;
 | |
|   else if (cpu == llvm::Triple::x86_64)
 | |
|     m_cpu = k_x86_64;
 | |
| 
 | |
|   if (m_cpu == k_cpu_unspecified)
 | |
|     return;
 | |
| 
 | |
|   if (reg_ctx.get() == nullptr)
 | |
|     return;
 | |
| 
 | |
|   if (m_cpu == k_i386) {
 | |
|     m_machine_ip_regnum = k_machine_eip;
 | |
|     m_machine_sp_regnum = k_machine_esp;
 | |
|     m_machine_fp_regnum = k_machine_ebp;
 | |
|     m_wordsize = 4;
 | |
| 
 | |
|     struct lldb_reg_info reginfo;
 | |
|     reginfo.name = "eax";
 | |
|     m_reg_map[k_machine_eax] = reginfo;
 | |
|     reginfo.name = "edx";
 | |
|     m_reg_map[k_machine_edx] = reginfo;
 | |
|     reginfo.name = "esp";
 | |
|     m_reg_map[k_machine_esp] = reginfo;
 | |
|     reginfo.name = "esi";
 | |
|     m_reg_map[k_machine_esi] = reginfo;
 | |
|     reginfo.name = "eip";
 | |
|     m_reg_map[k_machine_eip] = reginfo;
 | |
|     reginfo.name = "ecx";
 | |
|     m_reg_map[k_machine_ecx] = reginfo;
 | |
|     reginfo.name = "ebx";
 | |
|     m_reg_map[k_machine_ebx] = reginfo;
 | |
|     reginfo.name = "ebp";
 | |
|     m_reg_map[k_machine_ebp] = reginfo;
 | |
|     reginfo.name = "edi";
 | |
|     m_reg_map[k_machine_edi] = reginfo;
 | |
|   } else {
 | |
|     m_machine_ip_regnum = k_machine_rip;
 | |
|     m_machine_sp_regnum = k_machine_rsp;
 | |
|     m_machine_fp_regnum = k_machine_rbp;
 | |
|     m_wordsize = 8;
 | |
| 
 | |
|     struct lldb_reg_info reginfo;
 | |
|     reginfo.name = "rax";
 | |
|     m_reg_map[k_machine_rax] = reginfo;
 | |
|     reginfo.name = "rdx";
 | |
|     m_reg_map[k_machine_rdx] = reginfo;
 | |
|     reginfo.name = "rsp";
 | |
|     m_reg_map[k_machine_rsp] = reginfo;
 | |
|     reginfo.name = "rsi";
 | |
|     m_reg_map[k_machine_rsi] = reginfo;
 | |
|     reginfo.name = "r8";
 | |
|     m_reg_map[k_machine_r8] = reginfo;
 | |
|     reginfo.name = "r10";
 | |
|     m_reg_map[k_machine_r10] = reginfo;
 | |
|     reginfo.name = "r12";
 | |
|     m_reg_map[k_machine_r12] = reginfo;
 | |
|     reginfo.name = "r14";
 | |
|     m_reg_map[k_machine_r14] = reginfo;
 | |
|     reginfo.name = "rip";
 | |
|     m_reg_map[k_machine_rip] = reginfo;
 | |
|     reginfo.name = "rcx";
 | |
|     m_reg_map[k_machine_rcx] = reginfo;
 | |
|     reginfo.name = "rbx";
 | |
|     m_reg_map[k_machine_rbx] = reginfo;
 | |
|     reginfo.name = "rbp";
 | |
|     m_reg_map[k_machine_rbp] = reginfo;
 | |
|     reginfo.name = "rdi";
 | |
|     m_reg_map[k_machine_rdi] = reginfo;
 | |
|     reginfo.name = "r9";
 | |
|     m_reg_map[k_machine_r9] = reginfo;
 | |
|     reginfo.name = "r11";
 | |
|     m_reg_map[k_machine_r11] = reginfo;
 | |
|     reginfo.name = "r13";
 | |
|     m_reg_map[k_machine_r13] = reginfo;
 | |
|     reginfo.name = "r15";
 | |
|     m_reg_map[k_machine_r15] = reginfo;
 | |
|   }
 | |
| 
 | |
|   for (MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.begin();
 | |
|        it != m_reg_map.end(); ++it) {
 | |
|     const RegisterInfo *ri = reg_ctx->GetRegisterInfoByName(it->second.name);
 | |
|     if (ri)
 | |
|       it->second.lldb_regnum = ri->kinds[eRegisterKindLLDB];
 | |
|   }
 | |
| 
 | |
|   uint32_t lldb_regno;
 | |
|   if (machine_regno_to_lldb_regno(m_machine_sp_regnum, lldb_regno))
 | |
|     m_lldb_sp_regnum = lldb_regno;
 | |
|   if (machine_regno_to_lldb_regno(m_machine_fp_regnum, lldb_regno))
 | |
|     m_lldb_fp_regnum = lldb_regno;
 | |
|   if (machine_regno_to_lldb_regno(m_machine_ip_regnum, lldb_regno))
 | |
|     m_lldb_ip_regnum = lldb_regno;
 | |
| 
 | |
|   m_register_map_initialized = true;
 | |
| }
 | |
| 
 | |
| void x86AssemblyInspectionEngine::Initialize(
 | |
|     std::vector<lldb_reg_info> ®_info) {
 | |
|   m_cpu = k_cpu_unspecified;
 | |
|   m_wordsize = -1;
 | |
|   m_register_map_initialized = false;
 | |
| 
 | |
|   const llvm::Triple::ArchType cpu = m_arch.GetMachine();
 | |
|   if (cpu == llvm::Triple::x86)
 | |
|     m_cpu = k_i386;
 | |
|   else if (cpu == llvm::Triple::x86_64)
 | |
|     m_cpu = k_x86_64;
 | |
| 
 | |
|   if (m_cpu == k_cpu_unspecified)
 | |
|     return;
 | |
| 
 | |
|   if (m_cpu == k_i386) {
 | |
|     m_machine_ip_regnum = k_machine_eip;
 | |
|     m_machine_sp_regnum = k_machine_esp;
 | |
|     m_machine_fp_regnum = k_machine_ebp;
 | |
|     m_wordsize = 4;
 | |
| 
 | |
|     struct lldb_reg_info reginfo;
 | |
|     reginfo.name = "eax";
 | |
|     m_reg_map[k_machine_eax] = reginfo;
 | |
|     reginfo.name = "edx";
 | |
|     m_reg_map[k_machine_edx] = reginfo;
 | |
|     reginfo.name = "esp";
 | |
|     m_reg_map[k_machine_esp] = reginfo;
 | |
|     reginfo.name = "esi";
 | |
|     m_reg_map[k_machine_esi] = reginfo;
 | |
|     reginfo.name = "eip";
 | |
|     m_reg_map[k_machine_eip] = reginfo;
 | |
|     reginfo.name = "ecx";
 | |
|     m_reg_map[k_machine_ecx] = reginfo;
 | |
|     reginfo.name = "ebx";
 | |
|     m_reg_map[k_machine_ebx] = reginfo;
 | |
|     reginfo.name = "ebp";
 | |
|     m_reg_map[k_machine_ebp] = reginfo;
 | |
|     reginfo.name = "edi";
 | |
|     m_reg_map[k_machine_edi] = reginfo;
 | |
|   } else {
 | |
|     m_machine_ip_regnum = k_machine_rip;
 | |
|     m_machine_sp_regnum = k_machine_rsp;
 | |
|     m_machine_fp_regnum = k_machine_rbp;
 | |
|     m_wordsize = 8;
 | |
| 
 | |
|     struct lldb_reg_info reginfo;
 | |
|     reginfo.name = "rax";
 | |
|     m_reg_map[k_machine_rax] = reginfo;
 | |
|     reginfo.name = "rdx";
 | |
|     m_reg_map[k_machine_rdx] = reginfo;
 | |
|     reginfo.name = "rsp";
 | |
|     m_reg_map[k_machine_rsp] = reginfo;
 | |
|     reginfo.name = "rsi";
 | |
|     m_reg_map[k_machine_rsi] = reginfo;
 | |
|     reginfo.name = "r8";
 | |
|     m_reg_map[k_machine_r8] = reginfo;
 | |
|     reginfo.name = "r10";
 | |
|     m_reg_map[k_machine_r10] = reginfo;
 | |
|     reginfo.name = "r12";
 | |
|     m_reg_map[k_machine_r12] = reginfo;
 | |
|     reginfo.name = "r14";
 | |
|     m_reg_map[k_machine_r14] = reginfo;
 | |
|     reginfo.name = "rip";
 | |
|     m_reg_map[k_machine_rip] = reginfo;
 | |
|     reginfo.name = "rcx";
 | |
|     m_reg_map[k_machine_rcx] = reginfo;
 | |
|     reginfo.name = "rbx";
 | |
|     m_reg_map[k_machine_rbx] = reginfo;
 | |
|     reginfo.name = "rbp";
 | |
|     m_reg_map[k_machine_rbp] = reginfo;
 | |
|     reginfo.name = "rdi";
 | |
|     m_reg_map[k_machine_rdi] = reginfo;
 | |
|     reginfo.name = "r9";
 | |
|     m_reg_map[k_machine_r9] = reginfo;
 | |
|     reginfo.name = "r11";
 | |
|     m_reg_map[k_machine_r11] = reginfo;
 | |
|     reginfo.name = "r13";
 | |
|     m_reg_map[k_machine_r13] = reginfo;
 | |
|     reginfo.name = "r15";
 | |
|     m_reg_map[k_machine_r15] = reginfo;
 | |
|   }
 | |
| 
 | |
|   for (MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.begin();
 | |
|        it != m_reg_map.end(); ++it) {
 | |
|     for (size_t i = 0; i < reg_info.size(); ++i) {
 | |
|       if (::strcmp(reg_info[i].name, it->second.name) == 0) {
 | |
|         it->second.lldb_regnum = reg_info[i].lldb_regnum;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   uint32_t lldb_regno;
 | |
|   if (machine_regno_to_lldb_regno(m_machine_sp_regnum, lldb_regno))
 | |
|     m_lldb_sp_regnum = lldb_regno;
 | |
|   if (machine_regno_to_lldb_regno(m_machine_fp_regnum, lldb_regno))
 | |
|     m_lldb_fp_regnum = lldb_regno;
 | |
|   if (machine_regno_to_lldb_regno(m_machine_ip_regnum, lldb_regno))
 | |
|     m_lldb_ip_regnum = lldb_regno;
 | |
| 
 | |
|   m_register_map_initialized = true;
 | |
| }
 | |
| 
 | |
| // This function expects an x86 native register number (i.e. the bits stripped
 | |
| // out of the
 | |
| // actual instruction), not an lldb register number.
 | |
| //
 | |
| // FIXME: This is ABI dependent, it shouldn't be hardcoded here.
 | |
| 
 | |
| bool x86AssemblyInspectionEngine::nonvolatile_reg_p(int machine_regno) {
 | |
|   if (m_cpu == k_i386) {
 | |
|     switch (machine_regno) {
 | |
|     case k_machine_ebx:
 | |
|     case k_machine_ebp: // not actually a nonvolatile but often treated as such
 | |
|                         // by convention
 | |
|     case k_machine_esi:
 | |
|     case k_machine_edi:
 | |
|     case k_machine_esp:
 | |
|       return true;
 | |
|     default:
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   if (m_cpu == k_x86_64) {
 | |
|     switch (machine_regno) {
 | |
|     case k_machine_rbx:
 | |
|     case k_machine_rsp:
 | |
|     case k_machine_rbp: // not actually a nonvolatile but often treated as such
 | |
|                         // by convention
 | |
|     case k_machine_r12:
 | |
|     case k_machine_r13:
 | |
|     case k_machine_r14:
 | |
|     case k_machine_r15:
 | |
|       return true;
 | |
|     default:
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // Macro to detect if this is a REX mode prefix byte.
 | |
| #define REX_W_PREFIX_P(opcode) (((opcode) & (~0x5)) == 0x48)
 | |
| 
 | |
| // The high bit which should be added to the source register number (the "R"
 | |
| // bit)
 | |
| #define REX_W_SRCREG(opcode) (((opcode)&0x4) >> 2)
 | |
| 
 | |
| // The high bit which should be added to the destination register number (the
 | |
| // "B" bit)
 | |
| #define REX_W_DSTREG(opcode) ((opcode)&0x1)
 | |
| 
 | |
| // pushq %rbp [0x55]
 | |
| bool x86AssemblyInspectionEngine::push_rbp_pattern_p() {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   if (*p == 0x55)
 | |
|     return true;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // pushq $0 ; the first instruction in start() [0x6a 0x00]
 | |
| bool x86AssemblyInspectionEngine::push_0_pattern_p() {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   if (*p == 0x6a && *(p + 1) == 0x0)
 | |
|     return true;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // pushq $0
 | |
| // pushl $0
 | |
| bool x86AssemblyInspectionEngine::push_imm_pattern_p() {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   if (*p == 0x68 || *p == 0x6a)
 | |
|     return true;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // pushl imm8(%esp)
 | |
| //
 | |
| // e.g. 0xff 0x74 0x24 0x20 - 'pushl 0x20(%esp)'
 | |
| // (same byte pattern for 'pushq 0x20(%rsp)' in an x86_64 program)
 | |
| //
 | |
| // 0xff (with opcode bits '6' in next byte, PUSH r/m32)
 | |
| // 0x74 (ModR/M byte with three bits used to specify the opcode)
 | |
| //      mod == b01, opcode == b110, R/M == b100
 | |
| //      "+disp8"
 | |
| // 0x24 (SIB byte - scaled index = 0, r32 == esp)
 | |
| // 0x20 imm8 value
 | |
| 
 | |
| bool x86AssemblyInspectionEngine::push_extended_pattern_p() {
 | |
|   if (*m_cur_insn == 0xff) {
 | |
|     // Get the 3 opcode bits from the ModR/M byte
 | |
|     uint8_t opcode = (*(m_cur_insn + 1) >> 3) & 7;
 | |
|     if (opcode == 6) {
 | |
|       // I'm only looking for 0xff /6 here - I
 | |
|       // don't really care what value is being pushed,
 | |
|       // just that we're pushing a 32/64 bit value on
 | |
|       // to the stack is enough.
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // instructions only valid in 32-bit mode:
 | |
| // 0x0e - push cs
 | |
| // 0x16 - push ss
 | |
| // 0x1e - push ds
 | |
| // 0x06 - push es
 | |
| bool x86AssemblyInspectionEngine::push_misc_reg_p() {
 | |
|   uint8_t p = *m_cur_insn;
 | |
|   if (m_wordsize == 4) {
 | |
|     if (p == 0x0e || p == 0x16 || p == 0x1e || p == 0x06)
 | |
|       return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // pushq %rbx
 | |
| // pushl %ebx
 | |
| bool x86AssemblyInspectionEngine::push_reg_p(int ®no) {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   int regno_prefix_bit = 0;
 | |
|   // If we have a rex prefix byte, check to see if a B bit is set
 | |
|   if (m_wordsize == 8 && *p == 0x41) {
 | |
|     regno_prefix_bit = 1 << 3;
 | |
|     p++;
 | |
|   }
 | |
|   if (*p >= 0x50 && *p <= 0x57) {
 | |
|     regno = (*p - 0x50) | regno_prefix_bit;
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // movq %rsp, %rbp [0x48 0x8b 0xec] or [0x48 0x89 0xe5]
 | |
| // movl %esp, %ebp [0x8b 0xec] or [0x89 0xe5]
 | |
| bool x86AssemblyInspectionEngine::mov_rsp_rbp_pattern_p() {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   if (m_wordsize == 8 && *p == 0x48)
 | |
|     p++;
 | |
|   if (*(p) == 0x8b && *(p + 1) == 0xec)
 | |
|     return true;
 | |
|   if (*(p) == 0x89 && *(p + 1) == 0xe5)
 | |
|     return true;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // subq $0x20, %rsp
 | |
| bool x86AssemblyInspectionEngine::sub_rsp_pattern_p(int &amount) {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   if (m_wordsize == 8 && *p == 0x48)
 | |
|     p++;
 | |
|   // 8-bit immediate operand
 | |
|   if (*p == 0x83 && *(p + 1) == 0xec) {
 | |
|     amount = (int8_t) * (p + 2);
 | |
|     return true;
 | |
|   }
 | |
|   // 32-bit immediate operand
 | |
|   if (*p == 0x81 && *(p + 1) == 0xec) {
 | |
|     amount = (int32_t)extract_4(p + 2);
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // addq $0x20, %rsp
 | |
| bool x86AssemblyInspectionEngine::add_rsp_pattern_p(int &amount) {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   if (m_wordsize == 8 && *p == 0x48)
 | |
|     p++;
 | |
|   // 8-bit immediate operand
 | |
|   if (*p == 0x83 && *(p + 1) == 0xc4) {
 | |
|     amount = (int8_t) * (p + 2);
 | |
|     return true;
 | |
|   }
 | |
|   // 32-bit immediate operand
 | |
|   if (*p == 0x81 && *(p + 1) == 0xc4) {
 | |
|     amount = (int32_t)extract_4(p + 2);
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // lea esp, [esp - 0x28]
 | |
| // lea esp, [esp + 0x28]
 | |
| bool x86AssemblyInspectionEngine::lea_rsp_pattern_p(int &amount) {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   if (m_wordsize == 8 && *p == 0x48)
 | |
|     p++;
 | |
| 
 | |
|   // Check opcode
 | |
|   if (*p != 0x8d)
 | |
|     return false;
 | |
| 
 | |
|   // 8 bit displacement
 | |
|   if (*(p + 1) == 0x64 && (*(p + 2) & 0x3f) == 0x24) {
 | |
|     amount = (int8_t) * (p + 3);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // 32 bit displacement
 | |
|   if (*(p + 1) == 0xa4 && (*(p + 2) & 0x3f) == 0x24) {
 | |
|     amount = (int32_t)extract_4(p + 3);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // lea -0x28(%ebp), %esp
 | |
| // (32-bit and 64-bit variants, 8-bit and 32-bit displacement)
 | |
| bool x86AssemblyInspectionEngine::lea_rbp_rsp_pattern_p(int &amount) {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   if (m_wordsize == 8 && *p == 0x48)
 | |
|     p++;
 | |
| 
 | |
|   // Check opcode
 | |
|   if (*p != 0x8d)
 | |
|     return false;
 | |
|   ++p;
 | |
| 
 | |
|   // 8 bit displacement
 | |
|   if (*p == 0x65) {
 | |
|     amount = (int8_t)p[1];
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // 32 bit displacement
 | |
|   if (*p == 0xa5) {
 | |
|     amount = (int32_t)extract_4(p + 1);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // popq %rbx
 | |
| // popl %ebx
 | |
| bool x86AssemblyInspectionEngine::pop_reg_p(int ®no) {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   int regno_prefix_bit = 0;
 | |
|   // If we have a rex prefix byte, check to see if a B bit is set
 | |
|   if (m_wordsize == 8 && *p == 0x41) {
 | |
|     regno_prefix_bit = 1 << 3;
 | |
|     p++;
 | |
|   }
 | |
|   if (*p >= 0x58 && *p <= 0x5f) {
 | |
|     regno = (*p - 0x58) | regno_prefix_bit;
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // popq %rbp [0x5d]
 | |
| // popl %ebp [0x5d]
 | |
| bool x86AssemblyInspectionEngine::pop_rbp_pattern_p() {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   return (*p == 0x5d);
 | |
| }
 | |
| 
 | |
| // instructions valid only in 32-bit mode:
 | |
| // 0x1f - pop ds
 | |
| // 0x07 - pop es
 | |
| // 0x17 - pop ss
 | |
| bool x86AssemblyInspectionEngine::pop_misc_reg_p() {
 | |
|   uint8_t p = *m_cur_insn;
 | |
|   if (m_wordsize == 4) {
 | |
|     if (p == 0x1f || p == 0x07 || p == 0x17)
 | |
|       return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // leave [0xc9]
 | |
| bool x86AssemblyInspectionEngine::leave_pattern_p() {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   return (*p == 0xc9);
 | |
| }
 | |
| 
 | |
| // call $0 [0xe8 0x0 0x0 0x0 0x0]
 | |
| bool x86AssemblyInspectionEngine::call_next_insn_pattern_p() {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   return (*p == 0xe8) && (*(p + 1) == 0x0) && (*(p + 2) == 0x0) &&
 | |
|          (*(p + 3) == 0x0) && (*(p + 4) == 0x0);
 | |
| }
 | |
| 
 | |
| // Look for an instruction sequence storing a nonvolatile register
 | |
| // on to the stack frame.
 | |
| 
 | |
| //  movq %rax, -0x10(%rbp) [0x48 0x89 0x45 0xf0]
 | |
| //  movl %eax, -0xc(%ebp)  [0x89 0x45 0xf4]
 | |
| 
 | |
| // The offset value returned in rbp_offset will be positive --
 | |
| // but it must be subtraced from the frame base register to get
 | |
| // the actual location.  The positive value returned for the offset
 | |
| // is a convention used elsewhere for CFA offsets et al.
 | |
| 
 | |
| bool x86AssemblyInspectionEngine::mov_reg_to_local_stack_frame_p(
 | |
|     int ®no, int &rbp_offset) {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   int src_reg_prefix_bit = 0;
 | |
|   int target_reg_prefix_bit = 0;
 | |
| 
 | |
|   if (m_wordsize == 8 && REX_W_PREFIX_P(*p)) {
 | |
|     src_reg_prefix_bit = REX_W_SRCREG(*p) << 3;
 | |
|     target_reg_prefix_bit = REX_W_DSTREG(*p) << 3;
 | |
|     if (target_reg_prefix_bit == 1) {
 | |
|       // rbp/ebp don't need a prefix bit - we know this isn't the
 | |
|       // reg we care about.
 | |
|       return false;
 | |
|     }
 | |
|     p++;
 | |
|   }
 | |
| 
 | |
|   if (*p == 0x89) {
 | |
|     /* Mask off the 3-5 bits which indicate the destination register
 | |
|        if this is a ModR/M byte.  */
 | |
|     int opcode_destreg_masked_out = *(p + 1) & (~0x38);
 | |
| 
 | |
|     /* Is this a ModR/M byte with Mod bits 01 and R/M bits 101
 | |
|        and three bits between them, e.g. 01nnn101
 | |
|        We're looking for a destination of ebp-disp8 or ebp-disp32.   */
 | |
|     int immsize;
 | |
|     if (opcode_destreg_masked_out == 0x45)
 | |
|       immsize = 2;
 | |
|     else if (opcode_destreg_masked_out == 0x85)
 | |
|       immsize = 4;
 | |
|     else
 | |
|       return false;
 | |
| 
 | |
|     int offset = 0;
 | |
|     if (immsize == 2)
 | |
|       offset = (int8_t) * (p + 2);
 | |
|     if (immsize == 4)
 | |
|       offset = (uint32_t)extract_4(p + 2);
 | |
|     if (offset > 0)
 | |
|       return false;
 | |
| 
 | |
|     regno = ((*(p + 1) >> 3) & 0x7) | src_reg_prefix_bit;
 | |
|     rbp_offset = offset > 0 ? offset : -offset;
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // ret [0xc9] or [0xc2 imm8] or [0xca imm8]
 | |
| bool x86AssemblyInspectionEngine::ret_pattern_p() {
 | |
|   uint8_t *p = m_cur_insn;
 | |
|   if (*p == 0xc9 || *p == 0xc2 || *p == 0xca || *p == 0xc3)
 | |
|     return true;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| uint32_t x86AssemblyInspectionEngine::extract_4(uint8_t *b) {
 | |
|   uint32_t v = 0;
 | |
|   for (int i = 3; i >= 0; i--)
 | |
|     v = (v << 8) | b[i];
 | |
|   return v;
 | |
| }
 | |
| 
 | |
| bool x86AssemblyInspectionEngine::instruction_length(uint8_t *insn_p,
 | |
|                                                      int &length, 
 | |
|                                                      uint32_t buffer_remaining_bytes) {
 | |
| 
 | |
|   uint32_t max_op_byte_size = std::min(buffer_remaining_bytes, m_arch.GetMaximumOpcodeByteSize());
 | |
|   llvm::SmallVector<uint8_t, 32> opcode_data;
 | |
|   opcode_data.resize(max_op_byte_size);
 | |
| 
 | |
|   char out_string[512];
 | |
|   const size_t inst_size =
 | |
|       ::LLVMDisasmInstruction(m_disasm_context, insn_p, max_op_byte_size, 0,
 | |
|                               out_string, sizeof(out_string));
 | |
| 
 | |
|   length = inst_size;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool x86AssemblyInspectionEngine::machine_regno_to_lldb_regno(
 | |
|     int machine_regno, uint32_t &lldb_regno) {
 | |
|   MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.find(machine_regno);
 | |
|   if (it != m_reg_map.end()) {
 | |
|     lldb_regno = it->second.lldb_regnum;
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool x86AssemblyInspectionEngine::GetNonCallSiteUnwindPlanFromAssembly(
 | |
|     uint8_t *data, size_t size, AddressRange &func_range,
 | |
|     UnwindPlan &unwind_plan) {
 | |
|   unwind_plan.Clear();
 | |
| 
 | |
|   if (data == nullptr || size == 0)
 | |
|     return false;
 | |
| 
 | |
|   if (m_register_map_initialized == false)
 | |
|     return false;
 | |
| 
 | |
|   addr_t current_func_text_offset = 0;
 | |
|   int current_sp_bytes_offset_from_cfa = 0;
 | |
|   UnwindPlan::Row::RegisterLocation initial_regloc;
 | |
|   UnwindPlan::RowSP row(new UnwindPlan::Row);
 | |
| 
 | |
|   unwind_plan.SetPlanValidAddressRange(func_range);
 | |
|   unwind_plan.SetRegisterKind(eRegisterKindLLDB);
 | |
| 
 | |
|   // At the start of the function, find the CFA by adding wordsize to the SP
 | |
|   // register
 | |
|   row->SetOffset(current_func_text_offset);
 | |
|   row->GetCFAValue().SetIsRegisterPlusOffset(m_lldb_sp_regnum, m_wordsize);
 | |
| 
 | |
|   // caller's stack pointer value before the call insn is the CFA address
 | |
|   initial_regloc.SetIsCFAPlusOffset(0);
 | |
|   row->SetRegisterInfo(m_lldb_sp_regnum, initial_regloc);
 | |
| 
 | |
|   // saved instruction pointer can be found at CFA - wordsize.
 | |
|   current_sp_bytes_offset_from_cfa = m_wordsize;
 | |
|   initial_regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_cfa);
 | |
|   row->SetRegisterInfo(m_lldb_ip_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);
 | |
| 
 | |
|   // Track which registers have been saved so far in the prologue.
 | |
|   // If we see another push of that register, it's not part of the prologue.
 | |
|   // The register numbers used here are the machine register #'s
 | |
|   // (i386_register_numbers, x86_64_register_numbers).
 | |
|   std::vector<bool> saved_registers(32, false);
 | |
| 
 | |
|   // Once the prologue has completed we'll save a copy of the unwind
 | |
|   // instructions
 | |
|   // If there is an epilogue in the middle of the function, after that epilogue
 | |
|   // we'll reinstate
 | |
|   // the unwind setup -- we assume that some code path jumps over the
 | |
|   // mid-function epilogue
 | |
| 
 | |
|   UnwindPlan::RowSP prologue_completed_row; // copy of prologue row of CFI
 | |
|   int prologue_completed_sp_bytes_offset_from_cfa; // The sp value before the
 | |
|                                                    // epilogue started executed
 | |
|   std::vector<bool> prologue_completed_saved_registers;
 | |
| 
 | |
|   while (current_func_text_offset < size) {
 | |
|     int stack_offset, insn_len;
 | |
|     int machine_regno;   // register numbers masked directly out of instructions
 | |
|     uint32_t lldb_regno; // register numbers in lldb's eRegisterKindLLDB
 | |
|                          // numbering scheme
 | |
| 
 | |
|     bool in_epilogue = false; // we're in the middle of an epilogue sequence
 | |
|     bool row_updated = false; // The UnwindPlan::Row 'row' has been updated
 | |
| 
 | |
|     m_cur_insn = data + current_func_text_offset;
 | |
|     if (!instruction_length(m_cur_insn, insn_len, size - current_func_text_offset)
 | |
|         || insn_len == 0 
 | |
|         || insn_len > kMaxInstructionByteSize) {
 | |
|       // An unrecognized/junk instruction
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if (push_rbp_pattern_p()) {
 | |
|       current_sp_bytes_offset_from_cfa += m_wordsize;
 | |
|       row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
 | |
|       UnwindPlan::Row::RegisterLocation regloc;
 | |
|       regloc.SetAtCFAPlusOffset(-row->GetCFAValue().GetOffset());
 | |
|       row->SetRegisterInfo(m_lldb_fp_regnum, regloc);
 | |
|       saved_registers[m_machine_fp_regnum] = true;
 | |
|       row_updated = true;
 | |
|     }
 | |
| 
 | |
|     else if (mov_rsp_rbp_pattern_p()) {
 | |
|       row->GetCFAValue().SetIsRegisterPlusOffset(
 | |
|           m_lldb_fp_regnum, row->GetCFAValue().GetOffset());
 | |
|       row_updated = true;
 | |
|     }
 | |
| 
 | |
|     // This is the start() function (or a pthread equivalent), it starts with a
 | |
|     // pushl $0x0 which puts the
 | |
|     // saved pc value of 0 on the stack.  In this case we want to pretend we
 | |
|     // didn't see a stack movement at all --
 | |
|     // normally the saved pc value is already on the stack by the time the
 | |
|     // function starts executing.
 | |
|     else if (push_0_pattern_p()) {
 | |
|     }
 | |
| 
 | |
|     else if (push_reg_p(machine_regno)) {
 | |
|       current_sp_bytes_offset_from_cfa += m_wordsize;
 | |
|       // the PUSH instruction has moved the stack pointer - if the CFA is set in
 | |
|       // terms of the stack pointer,
 | |
|       // we need to add a new row of instructions.
 | |
|       if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
 | |
|         row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
 | |
|         row_updated = true;
 | |
|       }
 | |
|       // record where non-volatile (callee-saved, spilled) registers are saved
 | |
|       // on the stack
 | |
|       if (nonvolatile_reg_p(machine_regno) &&
 | |
|           machine_regno_to_lldb_regno(machine_regno, lldb_regno) &&
 | |
|           saved_registers[machine_regno] == false) {
 | |
|         UnwindPlan::Row::RegisterLocation regloc;
 | |
|         regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_cfa);
 | |
|         row->SetRegisterInfo(lldb_regno, regloc);
 | |
|         saved_registers[machine_regno] = true;
 | |
|         row_updated = true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     else if (pop_reg_p(machine_regno)) {
 | |
|       current_sp_bytes_offset_from_cfa -= m_wordsize;
 | |
| 
 | |
|       if (nonvolatile_reg_p(machine_regno) &&
 | |
|           machine_regno_to_lldb_regno(machine_regno, lldb_regno) &&
 | |
|           saved_registers[machine_regno] == true) {
 | |
|         saved_registers[machine_regno] = false;
 | |
|         row->RemoveRegisterInfo(lldb_regno);
 | |
| 
 | |
|         if (machine_regno == (int)m_machine_fp_regnum) {
 | |
|           row->GetCFAValue().SetIsRegisterPlusOffset(
 | |
|               m_lldb_sp_regnum, row->GetCFAValue().GetOffset());
 | |
|         }
 | |
| 
 | |
|         in_epilogue = true;
 | |
|         row_updated = true;
 | |
|       }
 | |
| 
 | |
|       // the POP instruction has moved the stack pointer - if the CFA is set in
 | |
|       // terms of the stack pointer,
 | |
|       // we need to add a new row of instructions.
 | |
|       if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
 | |
|         row->GetCFAValue().SetIsRegisterPlusOffset(
 | |
|             m_lldb_sp_regnum, current_sp_bytes_offset_from_cfa);
 | |
|         row_updated = true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     else if (pop_misc_reg_p()) {
 | |
|       current_sp_bytes_offset_from_cfa -= m_wordsize;
 | |
|       if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
 | |
|         row->GetCFAValue().SetIsRegisterPlusOffset(
 | |
|             m_lldb_sp_regnum, current_sp_bytes_offset_from_cfa);
 | |
|         row_updated = true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // The LEAVE instruction moves the value from rbp into rsp and pops
 | |
|     // a value off the stack into rbp (restoring the caller's rbp value).
 | |
|     // It is the opposite of ENTER, or 'push rbp, mov rsp rbp'.
 | |
|     else if (leave_pattern_p()) {
 | |
|       // We're going to copy the value in rbp into rsp, so re-set the sp offset
 | |
|       // based on the CFAValue.  Also, adjust it to recognize that we're popping
 | |
|       // the saved rbp value off the stack.
 | |
|       current_sp_bytes_offset_from_cfa = row->GetCFAValue().GetOffset();
 | |
|       current_sp_bytes_offset_from_cfa -= m_wordsize;
 | |
|       row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
 | |
| 
 | |
|       // rbp is restored to the caller's value
 | |
|       saved_registers[m_machine_fp_regnum] = false;
 | |
|       row->RemoveRegisterInfo(m_lldb_fp_regnum);
 | |
| 
 | |
|       // cfa is now in terms of rsp again.
 | |
|       row->GetCFAValue().SetIsRegisterPlusOffset(
 | |
|           m_lldb_sp_regnum, row->GetCFAValue().GetOffset());
 | |
|       row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
 | |
| 
 | |
|       in_epilogue = true;
 | |
|       row_updated = true;
 | |
|     }
 | |
| 
 | |
|     else if (mov_reg_to_local_stack_frame_p(machine_regno, stack_offset) &&
 | |
|              nonvolatile_reg_p(machine_regno) &&
 | |
|              machine_regno_to_lldb_regno(machine_regno, lldb_regno) &&
 | |
|              saved_registers[machine_regno] == false) {
 | |
|       saved_registers[machine_regno] = true;
 | |
| 
 | |
|       UnwindPlan::Row::RegisterLocation regloc;
 | |
| 
 | |
|       // stack_offset for 'movq %r15, -80(%rbp)' will be 80.
 | |
|       // In the Row, we want to express this as the offset from the CFA.  If the
 | |
|       // frame base
 | |
|       // is rbp (like the above instruction), the CFA offset for rbp is probably
 | |
|       // 16.  So we
 | |
|       // want to say that the value is stored at the CFA address - 96.
 | |
|       regloc.SetAtCFAPlusOffset(
 | |
|           -(stack_offset + row->GetCFAValue().GetOffset()));
 | |
| 
 | |
|       row->SetRegisterInfo(lldb_regno, regloc);
 | |
| 
 | |
|       row_updated = true;
 | |
|     }
 | |
| 
 | |
|     else if (sub_rsp_pattern_p(stack_offset)) {
 | |
|       current_sp_bytes_offset_from_cfa += stack_offset;
 | |
|       if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
 | |
|         row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
 | |
|         row_updated = true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     else if (add_rsp_pattern_p(stack_offset)) {
 | |
|       current_sp_bytes_offset_from_cfa -= stack_offset;
 | |
|       if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
 | |
|         row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
 | |
|         row_updated = true;
 | |
|       }
 | |
|       in_epilogue = true;
 | |
|     }
 | |
| 
 | |
|     else if (push_extended_pattern_p() || push_imm_pattern_p() ||
 | |
|              push_misc_reg_p()) {
 | |
|       current_sp_bytes_offset_from_cfa += m_wordsize;
 | |
|       if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
 | |
|         row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
 | |
|         row_updated = true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     else if (lea_rsp_pattern_p(stack_offset)) {
 | |
|       current_sp_bytes_offset_from_cfa -= stack_offset;
 | |
|       if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
 | |
|         row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
 | |
|         row_updated = true;
 | |
|       }
 | |
|       if (stack_offset > 0)
 | |
|         in_epilogue = true;
 | |
|     }
 | |
| 
 | |
|     else if (lea_rbp_rsp_pattern_p(stack_offset) &&
 | |
|              row->GetCFAValue().GetRegisterNumber() == m_lldb_fp_regnum) {
 | |
|       current_sp_bytes_offset_from_cfa =
 | |
|           row->GetCFAValue().GetOffset() - stack_offset;
 | |
|     }
 | |
| 
 | |
|     else if (ret_pattern_p() && prologue_completed_row.get()) {
 | |
|       // Reinstate the saved prologue setup for any instructions
 | |
|       // that come after the ret instruction
 | |
| 
 | |
|       UnwindPlan::Row *newrow = new UnwindPlan::Row;
 | |
|       *newrow = *prologue_completed_row.get();
 | |
|       row.reset(newrow);
 | |
|       current_sp_bytes_offset_from_cfa =
 | |
|           prologue_completed_sp_bytes_offset_from_cfa;
 | |
| 
 | |
|       saved_registers.clear();
 | |
|       saved_registers.resize(prologue_completed_saved_registers.size(), false);
 | |
|       for (size_t i = 0; i < prologue_completed_saved_registers.size(); ++i) {
 | |
|         saved_registers[i] = prologue_completed_saved_registers[i];
 | |
|       }
 | |
| 
 | |
|       in_epilogue = true;
 | |
|       row_updated = true;
 | |
|     }
 | |
| 
 | |
|     // call next instruction
 | |
|     //     call 0
 | |
|     //  => pop  %ebx
 | |
|     // This is used in i386 programs to get the PIC base address for finding
 | |
|     // global data
 | |
|     else if (call_next_insn_pattern_p()) {
 | |
|       current_sp_bytes_offset_from_cfa += m_wordsize;
 | |
|       if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum) {
 | |
|         row->GetCFAValue().SetOffset(current_sp_bytes_offset_from_cfa);
 | |
|         row_updated = true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (row_updated) {
 | |
|       if (current_func_text_offset + insn_len < size) {
 | |
|         row->SetOffset(current_func_text_offset + insn_len);
 | |
|         unwind_plan.AppendRow(row);
 | |
|         // Allocate a new Row, populate it with the existing Row contents.
 | |
|         newrow = new UnwindPlan::Row;
 | |
|         *newrow = *row.get();
 | |
|         row.reset(newrow);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (in_epilogue == false && row_updated) {
 | |
|       // If we're not in an epilogue sequence, save the updated Row
 | |
|       UnwindPlan::Row *newrow = new UnwindPlan::Row;
 | |
|       *newrow = *row.get();
 | |
|       prologue_completed_row.reset(newrow);
 | |
| 
 | |
|       prologue_completed_saved_registers.clear();
 | |
|       prologue_completed_saved_registers.resize(saved_registers.size(), false);
 | |
|       for (size_t i = 0; i < saved_registers.size(); ++i) {
 | |
|         prologue_completed_saved_registers[i] = saved_registers[i];
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // We may change the sp value without adding a new Row necessarily -- keep
 | |
|     // track of it either way.
 | |
|     if (in_epilogue == false) {
 | |
|       prologue_completed_sp_bytes_offset_from_cfa =
 | |
|           current_sp_bytes_offset_from_cfa;
 | |
|     }
 | |
| 
 | |
|     m_cur_insn = m_cur_insn + insn_len;
 | |
|     current_func_text_offset += insn_len;
 | |
|   }
 | |
| 
 | |
|   unwind_plan.SetSourceName("assembly insn profiling");
 | |
|   unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
 | |
|   unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite(
 | |
|     uint8_t *data, size_t size, AddressRange &func_range,
 | |
|     UnwindPlan &unwind_plan, RegisterContextSP ®_ctx) {
 | |
|   Address addr_start = func_range.GetBaseAddress();
 | |
|   if (!addr_start.IsValid())
 | |
|     return false;
 | |
| 
 | |
|   // We either need a live RegisterContext, or we need the UnwindPlan to already
 | |
|   // be in the lldb register numbering scheme.
 | |
|   if (reg_ctx.get() == nullptr &&
 | |
|       unwind_plan.GetRegisterKind() != eRegisterKindLLDB)
 | |
|     return false;
 | |
| 
 | |
|   // Is original unwind_plan valid?
 | |
|   // unwind_plan should have at least one row which is ABI-default (CFA register
 | |
|   // is sp),
 | |
|   // and another row in mid-function.
 | |
|   if (unwind_plan.GetRowCount() < 2)
 | |
|     return false;
 | |
| 
 | |
|   UnwindPlan::RowSP first_row = unwind_plan.GetRowAtIndex(0);
 | |
|   if (first_row->GetOffset() != 0)
 | |
|     return false;
 | |
|   uint32_t cfa_reg = first_row->GetCFAValue().GetRegisterNumber();
 | |
|   if (unwind_plan.GetRegisterKind() != eRegisterKindLLDB) {
 | |
|     cfa_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
 | |
|         unwind_plan.GetRegisterKind(),
 | |
|         first_row->GetCFAValue().GetRegisterNumber());
 | |
|   }
 | |
|   if (cfa_reg != m_lldb_sp_regnum ||
 | |
|       first_row->GetCFAValue().GetOffset() != m_wordsize)
 | |
|     return false;
 | |
| 
 | |
|   UnwindPlan::RowSP original_last_row = unwind_plan.GetRowForFunctionOffset(-1);
 | |
| 
 | |
|   size_t offset = 0;
 | |
|   int row_id = 1;
 | |
|   bool unwind_plan_updated = false;
 | |
|   UnwindPlan::RowSP row(new UnwindPlan::Row(*first_row));
 | |
|   m_cur_insn = data + offset;
 | |
| 
 | |
|   // After a mid-function epilogue we will need to re-insert the original unwind
 | |
|   // rules
 | |
|   // so unwinds work for the remainder of the function.  These aren't common
 | |
|   // with clang/gcc
 | |
|   // on x86 but it is possible.
 | |
|   bool reinstate_unwind_state = false;
 | |
| 
 | |
|   while (offset < size) {
 | |
|     m_cur_insn = data + offset;
 | |
|     int insn_len;
 | |
|     if (!instruction_length(m_cur_insn, insn_len, size - offset)
 | |
|         || insn_len == 0 
 | |
|         || insn_len > kMaxInstructionByteSize) {
 | |
|       // An unrecognized/junk instruction.
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     // Advance offsets.
 | |
|     offset += insn_len;
 | |
|     m_cur_insn = data + offset;
 | |
| 
 | |
|     // offset is pointing beyond the bounds of the
 | |
|     // function; stop looping.
 | |
|     if (offset >= size) 
 | |
|       continue;
 | |
| 
 | |
|     if (reinstate_unwind_state) {
 | |
|       UnwindPlan::RowSP new_row(new UnwindPlan::Row());
 | |
|       *new_row = *original_last_row;
 | |
|       new_row->SetOffset(offset);
 | |
|       unwind_plan.AppendRow(new_row);
 | |
|       row.reset(new UnwindPlan::Row());
 | |
|       *row = *new_row;
 | |
|       reinstate_unwind_state = false;
 | |
|       unwind_plan_updated = true;
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     // If we already have one row for this instruction, we can continue.
 | |
|     while (row_id < unwind_plan.GetRowCount() &&
 | |
|            unwind_plan.GetRowAtIndex(row_id)->GetOffset() <= offset) {
 | |
|       row_id++;
 | |
|     }
 | |
|     UnwindPlan::RowSP original_row = unwind_plan.GetRowAtIndex(row_id - 1);
 | |
|     if (original_row->GetOffset() == offset) {
 | |
|       *row = *original_row;
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if (row_id == 0) {
 | |
|       // If we are here, compiler didn't generate CFI for prologue.
 | |
|       // This won't happen to GCC or clang.
 | |
|       // In this case, bail out directly.
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     // Inspect the instruction to check if we need a new row for it.
 | |
|     cfa_reg = row->GetCFAValue().GetRegisterNumber();
 | |
|     if (unwind_plan.GetRegisterKind() != eRegisterKindLLDB) {
 | |
|       cfa_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
 | |
|           unwind_plan.GetRegisterKind(),
 | |
|           row->GetCFAValue().GetRegisterNumber());
 | |
|     }
 | |
|     if (cfa_reg == m_lldb_sp_regnum) {
 | |
|       // CFA register is sp.
 | |
| 
 | |
|       // call next instruction
 | |
|       //     call 0
 | |
|       //  => pop  %ebx
 | |
|       if (call_next_insn_pattern_p()) {
 | |
|         row->SetOffset(offset);
 | |
|         row->GetCFAValue().IncOffset(m_wordsize);
 | |
| 
 | |
|         UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
 | |
|         unwind_plan.InsertRow(new_row);
 | |
|         unwind_plan_updated = true;
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       // push/pop register
 | |
|       int regno;
 | |
|       if (push_reg_p(regno)) {
 | |
|         row->SetOffset(offset);
 | |
|         row->GetCFAValue().IncOffset(m_wordsize);
 | |
| 
 | |
|         UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
 | |
|         unwind_plan.InsertRow(new_row);
 | |
|         unwind_plan_updated = true;
 | |
|         continue;
 | |
|       }
 | |
|       if (pop_reg_p(regno)) {
 | |
|         // Technically, this might be a nonvolatile register recover in
 | |
|         // epilogue.
 | |
|         // We should reset RegisterInfo for the register.
 | |
|         // But in practice, previous rule for the register is still valid...
 | |
|         // So we ignore this case.
 | |
| 
 | |
|         row->SetOffset(offset);
 | |
|         row->GetCFAValue().IncOffset(-m_wordsize);
 | |
| 
 | |
|         UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
 | |
|         unwind_plan.InsertRow(new_row);
 | |
|         unwind_plan_updated = true;
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       if (pop_misc_reg_p()) {
 | |
|         row->SetOffset(offset);
 | |
|         row->GetCFAValue().IncOffset(-m_wordsize);
 | |
| 
 | |
|         UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
 | |
|         unwind_plan.InsertRow(new_row);
 | |
|         unwind_plan_updated = true;
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       // push imm
 | |
|       if (push_imm_pattern_p()) {
 | |
|         row->SetOffset(offset);
 | |
|         row->GetCFAValue().IncOffset(m_wordsize);
 | |
|         UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
 | |
|         unwind_plan.InsertRow(new_row);
 | |
|         unwind_plan_updated = true;
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       // push extended
 | |
|       if (push_extended_pattern_p() || push_misc_reg_p()) {
 | |
|         row->SetOffset(offset);
 | |
|         row->GetCFAValue().IncOffset(m_wordsize);
 | |
|         UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
 | |
|         unwind_plan.InsertRow(new_row);
 | |
|         unwind_plan_updated = true;
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       // add/sub %rsp/%esp
 | |
|       int amount;
 | |
|       if (add_rsp_pattern_p(amount)) {
 | |
|         row->SetOffset(offset);
 | |
|         row->GetCFAValue().IncOffset(-amount);
 | |
| 
 | |
|         UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
 | |
|         unwind_plan.InsertRow(new_row);
 | |
|         unwind_plan_updated = true;
 | |
|         continue;
 | |
|       }
 | |
|       if (sub_rsp_pattern_p(amount)) {
 | |
|         row->SetOffset(offset);
 | |
|         row->GetCFAValue().IncOffset(amount);
 | |
| 
 | |
|         UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
 | |
|         unwind_plan.InsertRow(new_row);
 | |
|         unwind_plan_updated = true;
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       // lea %rsp, [%rsp + $offset]
 | |
|       if (lea_rsp_pattern_p(amount)) {
 | |
|         row->SetOffset(offset);
 | |
|         row->GetCFAValue().IncOffset(-amount);
 | |
| 
 | |
|         UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
 | |
|         unwind_plan.InsertRow(new_row);
 | |
|         unwind_plan_updated = true;
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       if (ret_pattern_p()) {
 | |
|         reinstate_unwind_state = true;
 | |
|         continue;
 | |
|       }
 | |
|     } else if (cfa_reg == m_lldb_fp_regnum) {
 | |
|       // CFA register is fp.
 | |
| 
 | |
|       // The only case we care about is epilogue:
 | |
|       //     [0x5d] pop %rbp/%ebp
 | |
|       //  => [0xc3] ret
 | |
|       if (pop_rbp_pattern_p() || leave_pattern_p()) {
 | |
|         offset += 1;
 | |
|         row->SetOffset(offset);
 | |
|         row->GetCFAValue().SetIsRegisterPlusOffset(
 | |
|             first_row->GetCFAValue().GetRegisterNumber(), m_wordsize);
 | |
| 
 | |
|         UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
 | |
|         unwind_plan.InsertRow(new_row);
 | |
|         unwind_plan_updated = true;
 | |
|         reinstate_unwind_state = true;
 | |
|         continue;
 | |
|       }
 | |
|     } else {
 | |
|       // CFA register is not sp or fp.
 | |
| 
 | |
|       // This must be hand-written assembly.
 | |
|       // Just trust eh_frame and assume we have finished.
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   unwind_plan.SetPlanValidAddressRange(func_range);
 | |
|   if (unwind_plan_updated) {
 | |
|     std::string unwind_plan_source(unwind_plan.GetSourceName().AsCString());
 | |
|     unwind_plan_source += " plus augmentation from assembly parsing";
 | |
|     unwind_plan.SetSourceName(unwind_plan_source.c_str());
 | |
|     unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
 | |
|     unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool x86AssemblyInspectionEngine::FindFirstNonPrologueInstruction(
 | |
|     uint8_t *data, size_t size, size_t &offset) {
 | |
|   offset = 0;
 | |
| 
 | |
|   if (m_register_map_initialized == false)
 | |
|     return false;
 | |
| 
 | |
|   while (offset < size) {
 | |
|     int regno;
 | |
|     int insn_len;
 | |
|     int scratch;
 | |
| 
 | |
|     m_cur_insn = data + offset;
 | |
|     if (!instruction_length(m_cur_insn, insn_len, size - offset) 
 | |
|         || insn_len > kMaxInstructionByteSize 
 | |
|         || insn_len == 0) {
 | |
|       // An error parsing the instruction, i.e. probably data/garbage - stop
 | |
|       // scanning
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if (push_rbp_pattern_p() || mov_rsp_rbp_pattern_p() ||
 | |
|         sub_rsp_pattern_p(scratch) || push_reg_p(regno) ||
 | |
|         mov_reg_to_local_stack_frame_p(regno, scratch) ||
 | |
|         (lea_rsp_pattern_p(scratch) && offset == 0)) {
 | |
|       offset += insn_len;
 | |
|       continue;
 | |
|     }
 | |
|     //
 | |
|     // Unknown non-prologue instruction - stop scanning
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 |