Refactor the x86 UnwindAssembly class into a separate class called

x86AssemblyInspectionEngine and the current UnwindAssembly_x86 to
allow for the core engine to be exercised by unit tests.

The UnwindAssembly_x86 class will have access to Targets, Processes,
Threads, RegisterContexts -- it will be working in the full lldb
environment.

x86AssemblyInspectionEngine is layered away from all of that, it is
given some register definitions and a bag of bytes to profile.

I wrote an initial unittest for a do-nothing simple x86_64/i386
function to start with.  I'll be adding more.

The x86 assembly unwinder was added to lldb early in its bringup;
I made some modernization changes as I was refactoring the code
to make it more consistent with how we write lldb today.

I also added RegisterContextMinidump_x86_64.cpp to the xcode project
file so I can run the unittests from that.

The testsuite passes with this change, but there was quite a bit of
code change by the refactoring and it's possible there are some 
issues.  I'll be testing this more in the coming days, but it looks
like it is behaving correctly as far as I can tell with automated
testing.

<rdar://problem/28509178> 

llvm-svn: 282565
This commit is contained in:
Jason Molenda 2016-09-28 02:52:19 +00:00
parent 4499145a5f
commit 1c9858b298
10 changed files with 1695 additions and 1201 deletions

View File

@ -928,6 +928,8 @@
AF33B4BE1C1FA441001B28D9 /* NetBSDSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF33B4BC1C1FA441001B28D9 /* NetBSDSignals.cpp */; };
AF33B4BF1C1FA441001B28D9 /* NetBSDSignals.h in Headers */ = {isa = PBXBuildFile; fileRef = AF33B4BD1C1FA441001B28D9 /* NetBSDSignals.h */; };
AF37E10A17C861F20061E18E /* ProcessRunLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF37E10917C861F20061E18E /* ProcessRunLock.cpp */; };
AF415AE71D949E4400FCE0D4 /* x86AssemblyInspectionEngine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF415AE51D949E4400FCE0D4 /* x86AssemblyInspectionEngine.cpp */; };
AF415AE81D949E4400FCE0D4 /* x86AssemblyInspectionEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = AF415AE61D949E4400FCE0D4 /* x86AssemblyInspectionEngine.h */; };
AF45FDE518A1F3AC0007051C /* AppleGetThreadItemInfoHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF45FDE318A1F3AC0007051C /* AppleGetThreadItemInfoHandler.cpp */; };
AF6335E21C87B21E00F7D554 /* SymbolFilePDB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF6335E01C87B21E00F7D554 /* SymbolFilePDB.cpp */; };
AF6335E31C87B21E00F7D554 /* SymbolFilePDB.h in Headers */ = {isa = PBXBuildFile; fileRef = AF6335E11C87B21E00F7D554 /* SymbolFilePDB.h */; };
@ -953,9 +955,12 @@
AFC234091AF85CE100CDE8B6 /* CommandObjectLanguage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFC234061AF85CE000CDE8B6 /* CommandObjectLanguage.cpp */; };
AFCB2BBD1BF577F40018B553 /* PythonExceptionState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFCB2BBB1BF577F40018B553 /* PythonExceptionState.cpp */; };
AFCB2BBE1BF577F40018B553 /* PythonExceptionState.h in Headers */ = {isa = PBXBuildFile; fileRef = AFCB2BBC1BF577F40018B553 /* PythonExceptionState.h */; };
AFD65C811D9B5B2E00D93120 /* RegisterContextMinidump_x86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFD65C7F1D9B5B2E00D93120 /* RegisterContextMinidump_x86_64.cpp */; };
AFD65C821D9B5B2E00D93120 /* RegisterContextMinidump_x86_64.h in Headers */ = {isa = PBXBuildFile; fileRef = AFD65C801D9B5B2E00D93120 /* RegisterContextMinidump_x86_64.h */; };
AFDCDBCB19DD0F42005EA55E /* SBExecutionContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 940B02F419DC96CB00AD0F52 /* SBExecutionContext.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFDFDFD119E34D3400EAE509 /* ConnectionFileDescriptorPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFDFDFD019E34D3400EAE509 /* ConnectionFileDescriptorPosix.cpp */; };
AFEC3362194A8ABA00FF05C6 /* StructuredData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3361194A8ABA00FF05C6 /* StructuredData.cpp */; };
AFEC5FD81D94F9380076A480 /* Testx86AssemblyInspectionEngine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC5FD51D94F9380076A480 /* Testx86AssemblyInspectionEngine.cpp */; };
AFF87C87150FF669000E1742 /* com.apple.debugserver.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = AFF87C86150FF669000E1742 /* com.apple.debugserver.plist */; };
AFF87C8F150FF688000E1742 /* com.apple.debugserver.applist.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = AFF87C8E150FF688000E1742 /* com.apple.debugserver.applist.plist */; };
B207C4931429607D00F36E4E /* CommandObjectWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B207C4921429607D00F36E4E /* CommandObjectWatchpoint.cpp */; };
@ -2946,6 +2951,8 @@
AF3F54BF1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_powerpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextPOSIXProcessMonitor_powerpc.h; sourceTree = "<group>"; };
AF3F54C01B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_x86.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextPOSIXProcessMonitor_x86.cpp; sourceTree = "<group>"; };
AF3F54C11B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_x86.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextPOSIXProcessMonitor_x86.h; sourceTree = "<group>"; };
AF415AE51D949E4400FCE0D4 /* x86AssemblyInspectionEngine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = x86AssemblyInspectionEngine.cpp; sourceTree = "<group>"; };
AF415AE61D949E4400FCE0D4 /* x86AssemblyInspectionEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x86AssemblyInspectionEngine.h; sourceTree = "<group>"; };
AF45E1FC1BF57C8D000563EB /* PythonTestSuite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PythonTestSuite.cpp; sourceTree = "<group>"; };
AF45E1FD1BF57C8D000563EB /* PythonTestSuite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PythonTestSuite.h; sourceTree = "<group>"; };
AF45FDE318A1F3AC0007051C /* AppleGetThreadItemInfoHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleGetThreadItemInfoHandler.cpp; sourceTree = "<group>"; };
@ -2992,8 +2999,11 @@
AFC234071AF85CE000CDE8B6 /* CommandObjectLanguage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectLanguage.h; path = source/Commands/CommandObjectLanguage.h; sourceTree = "<group>"; };
AFCB2BBB1BF577F40018B553 /* PythonExceptionState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PythonExceptionState.cpp; path = ScriptInterpreter/Python/PythonExceptionState.cpp; sourceTree = "<group>"; };
AFCB2BBC1BF577F40018B553 /* PythonExceptionState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PythonExceptionState.h; path = ScriptInterpreter/Python/PythonExceptionState.h; sourceTree = "<group>"; };
AFD65C7F1D9B5B2E00D93120 /* RegisterContextMinidump_x86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextMinidump_x86_64.cpp; sourceTree = "<group>"; };
AFD65C801D9B5B2E00D93120 /* RegisterContextMinidump_x86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextMinidump_x86_64.h; sourceTree = "<group>"; };
AFDFDFD019E34D3400EAE509 /* ConnectionFileDescriptorPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConnectionFileDescriptorPosix.cpp; sourceTree = "<group>"; };
AFEC3361194A8ABA00FF05C6 /* StructuredData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StructuredData.cpp; path = source/Core/StructuredData.cpp; sourceTree = "<group>"; };
AFEC5FD51D94F9380076A480 /* Testx86AssemblyInspectionEngine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Testx86AssemblyInspectionEngine.cpp; path = UnwindAssembly/x86/Testx86AssemblyInspectionEngine.cpp; sourceTree = "<group>"; };
AFF87C86150FF669000E1742 /* com.apple.debugserver.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.debugserver.plist; path = tools/debugserver/source/com.apple.debugserver.plist; sourceTree = "<group>"; };
AFF87C8A150FF677000E1742 /* com.apple.debugserver.applist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.debugserver.applist.plist; path = tools/debugserver/source/com.apple.debugserver.applist.plist; sourceTree = "<group>"; };
AFF87C8C150FF680000E1742 /* com.apple.debugserver.applist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.debugserver.applist.plist; path = tools/debugserver/source/com.apple.debugserver.applist.plist; sourceTree = "<group>"; };
@ -3227,6 +3237,7 @@
2321F93F1BDD33D800BA9A93 /* ScriptInterpreter */,
23CB15091D66CF2B00EDDDE1 /* Symbol */,
23CB150A1D66CF3200EDDDE1 /* SymbolFile */,
AFEC5FD31D94F9130076A480 /* UnwindAssembly */,
2321F9421BDD343A00BA9A93 /* Utility */,
);
path = unittests;
@ -3527,6 +3538,8 @@
23E2E5351D9048E7006F38BB /* minidump */ = {
isa = PBXGroup;
children = (
AFD65C7F1D9B5B2E00D93120 /* RegisterContextMinidump_x86_64.cpp */,
AFD65C801D9B5B2E00D93120 /* RegisterContextMinidump_x86_64.h */,
23E2E5361D9048FB006F38BB /* CMakeLists.txt */,
23E2E5371D9048FB006F38BB /* MinidumpParser.cpp */,
23E2E5381D9048FB006F38BB /* MinidumpParser.h */,
@ -4405,6 +4418,8 @@
2692BA17136611CD00F9E14D /* x86 */ = {
isa = PBXGroup;
children = (
AF415AE51D949E4400FCE0D4 /* x86AssemblyInspectionEngine.cpp */,
AF415AE61D949E4400FCE0D4 /* x86AssemblyInspectionEngine.h */,
263E949D13661AE400E7D1CE /* UnwindAssembly-x86.cpp */,
263E949E13661AE400E7D1CE /* UnwindAssembly-x86.h */,
);
@ -6283,6 +6298,22 @@
name = "SysV-ppc64";
sourceTree = "<group>";
};
AFEC5FD31D94F9130076A480 /* UnwindAssembly */ = {
isa = PBXGroup;
children = (
AFEC5FD51D94F9380076A480 /* Testx86AssemblyInspectionEngine.cpp */,
AFEC5FD41D94F9270076A480 /* x86 */,
);
name = UnwindAssembly;
sourceTree = "<group>";
};
AFEC5FD41D94F9270076A480 /* x86 */ = {
isa = PBXGroup;
children = (
);
name = x86;
sourceTree = "<group>";
};
E769331B1A94D10E00C73337 /* lldb-server */ = {
isa = PBXGroup;
children = (
@ -6400,7 +6431,9 @@
4984BA181B979C08008658D4 /* ExpressionVariable.h in Headers */,
26C7C4841BFFEA7E009BD01F /* WindowsMiniDump.h in Headers */,
30B38A001CAAA6D7009524E3 /* ClangUtil.h in Headers */,
AFD65C821D9B5B2E00D93120 /* RegisterContextMinidump_x86_64.h in Headers */,
238F2BA11D2C835A001FF92A /* StructuredDataPlugin.h in Headers */,
AF415AE81D949E4400FCE0D4 /* x86AssemblyInspectionEngine.h in Headers */,
AF8AD62F1BEC28A400150209 /* PlatformAppleTVSimulator.h in Headers */,
238F2BA91D2C85FA001FF92A /* StructuredDataDarwinLog.h in Headers */,
AF8AD63A1BEC28C400150209 /* PlatformRemoteAppleWatch.h in Headers */,
@ -6890,6 +6923,7 @@
23CB153E1D66DA9300EDDDE1 /* PythonDataObjectsTests.cpp in Sources */,
23CB153F1D66DA9300EDDDE1 /* SymbolsTest.cpp in Sources */,
23E2E52B1D9037E6006F38BB /* ModuleCacheTest.cpp in Sources */,
AFEC5FD81D94F9380076A480 /* Testx86AssemblyInspectionEngine.cpp in Sources */,
23CB15401D66DA9300EDDDE1 /* TestClangASTContext.cpp in Sources */,
23CB15411D66DA9300EDDDE1 /* StringExtractorTest.cpp in Sources */,
23CB15421D66DA9300EDDDE1 /* TaskPoolTest.cpp in Sources */,
@ -6989,6 +7023,7 @@
26D1804216CEDF0700EDFB5B /* TimeSpecTimeout.cpp in Sources */,
2689FFDA13353D9D00698AC0 /* lldb.cpp in Sources */,
4C0083401B9F9BA900D5CF24 /* UtilityFunction.cpp in Sources */,
AF415AE71D949E4400FCE0D4 /* x86AssemblyInspectionEngine.cpp in Sources */,
26474CCD18D0CB5B0073DEBA /* RegisterContextPOSIX_x86.cpp in Sources */,
AEB0E4591BD6E9F800B24093 /* LLVMUserExpression.cpp in Sources */,
2689FFEF13353DB600698AC0 /* Breakpoint.cpp in Sources */,
@ -7547,6 +7582,7 @@
23D0658F1D4A7BEE0008EDE6 /* RenderScriptExpressionOpts.cpp in Sources */,
945215DF17F639EE00521C0B /* ValueObjectPrinter.cpp in Sources */,
26EFB61B1BFE8D3E00544801 /* PlatformNetBSD.cpp in Sources */,
AFD65C811D9B5B2E00D93120 /* RegisterContextMinidump_x86_64.cpp in Sources */,
260CC64815D0440D002BF2E0 /* OptionValueArgs.cpp in Sources */,
260CC64915D0440D002BF2E0 /* OptionValueArray.cpp in Sources */,
260CC64A15D0440D002BF2E0 /* OptionValueBoolean.cpp in Sources */,

View File

@ -1,3 +1,4 @@
add_lldb_library(lldbPluginUnwindAssemblyX86
UnwindAssembly-x86.cpp
x86AssemblyInspectionEngine.cpp
)

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@
// C Includes
// C++ Includes
// Other libraries and framework includes
#include "llvm-c/Disassembler.h"
#include "x86AssemblyInspectionEngine.h"
// Project includes
#include "lldb/Target/UnwindAssembly.h"
@ -62,10 +62,11 @@ public:
uint32_t GetPluginVersion() override;
private:
UnwindAssembly_x86(const lldb_private::ArchSpec &arch, int cpu);
UnwindAssembly_x86(const lldb_private::ArchSpec &arch);
int m_cpu;
lldb_private::ArchSpec m_arch;
lldb_private::x86AssemblyInspectionEngine *m_assembly_inspection_engine;
};
#endif // liblldb_UnwindAssembly_x86_h_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,178 @@
//===-- x86AssemblyInspectionEngine.h ---------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_x86AssemblyInspectionEngine_h_
#define liblldb_x86AssemblyInspectionEngine_h_
#include "llvm-c/Disassembler.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include "lldb/lldb-private.h"
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/ConstString.h"
#include <map>
#include <vector>
namespace lldb_private {
// x86AssemblyInspectionEngine - a class which will take a buffer of bytes
// of i386/x86_64 instructions and create an UnwindPlan based on those
// assembly instructions.
class x86AssemblyInspectionEngine {
public:
/// default ctor
x86AssemblyInspectionEngine(const lldb_private::ArchSpec &arch);
/// default dtor
~x86AssemblyInspectionEngine();
/// One of the two initialize methods that can be called on this object;
/// they must be called before any of the assembly inspection methods
/// are called. This one should be used if the caller has access to a
/// valid RegisterContext.
void Initialize(lldb::RegisterContextSP &reg_ctx);
/// One of the two initialize methods that can be called on this object;
/// they must be called before any of the assembly inspection methods
/// are called. This one takes a vector of register name and lldb
/// register numbers.
struct lldb_reg_info {
const char *name;
uint32_t lldb_regnum;
lldb_reg_info() : name(nullptr), lldb_regnum(LLDB_INVALID_REGNUM) {}
};
void Initialize(std::vector<lldb_reg_info> &reg_info);
/// Create an UnwindPlan for a "non-call site" stack frame situation.
/// This is usually when this function/method is currently executing, and may
/// be at
/// a location where exception-handling style unwind information (eh_frame,
/// compact unwind info, arm unwind info)
/// are not valid.
/// \p data is a pointer to the instructions for the function
/// \p size is the size of the instruction buffer above
/// \p func_range is the start Address and size of the function, to be
/// included in the UnwindPlan
/// \p unwind_plan is the unwind plan that this method creates
/// \returns true if it was able to create an UnwindPlan; false if not.
bool
GetNonCallSiteUnwindPlanFromAssembly(uint8_t *data, size_t size,
lldb_private::AddressRange &func_range,
lldb_private::UnwindPlan &unwind_plan);
/// Take an existing UnwindPlan, probably from eh_frame which may be missing
/// description
/// of the epilogue instructions, and add the epilogue description to it based
/// on the
/// instructions in the function.
///
/// The \p unwind_plan 's register numbers must be converted into the lldb
/// register numbering
/// scheme OR a RegisterContext must be provided in \p reg_ctx. If the \p
/// unwind_plan
/// register numbers are already in lldb register numbering, \p reg_ctx may be
/// null.
/// \returns true if the \p unwind_plan was updated, false if it was not.
bool AugmentUnwindPlanFromCallSite(uint8_t *data, size_t size,
lldb_private::AddressRange &func_range,
lldb_private::UnwindPlan &unwind_plan,
lldb::RegisterContextSP &reg_ctx);
bool FindFirstNonPrologueInstruction(uint8_t *data, size_t size,
size_t &offset);
private:
bool nonvolatile_reg_p(int machine_regno);
bool push_rbp_pattern_p();
bool push_0_pattern_p();
bool push_imm_pattern_p();
bool mov_rsp_rbp_pattern_p();
bool sub_rsp_pattern_p(int &amount);
bool add_rsp_pattern_p(int &amount);
bool lea_rsp_pattern_p(int &amount);
bool push_reg_p(int &regno);
bool pop_reg_p(int &regno);
bool pop_rbp_pattern_p();
bool leave_pattern_p();
bool call_next_insn_pattern_p();
bool mov_reg_to_local_stack_frame_p(int &regno, int &rbp_offset);
bool ret_pattern_p();
uint32_t extract_4(uint8_t *b);
bool instruction_length(uint8_t *insn, int &length);
bool machine_regno_to_lldb_regno(int machine_regno, uint32_t &lldb_regno);
enum CPU { k_i386, k_x86_64, k_cpu_unspecified };
enum i386_register_numbers {
k_machine_eax = 0,
k_machine_ecx = 1,
k_machine_edx = 2,
k_machine_ebx = 3,
k_machine_esp = 4,
k_machine_ebp = 5,
k_machine_esi = 6,
k_machine_edi = 7,
k_machine_eip = 8
};
enum x86_64_register_numbers {
k_machine_rax = 0,
k_machine_rcx = 1,
k_machine_rdx = 2,
k_machine_rbx = 3,
k_machine_rsp = 4,
k_machine_rbp = 5,
k_machine_rsi = 6,
k_machine_rdi = 7,
k_machine_r8 = 8,
k_machine_r9 = 9,
k_machine_r10 = 10,
k_machine_r11 = 11,
k_machine_r12 = 12,
k_machine_r13 = 13,
k_machine_r14 = 14,
k_machine_r15 = 15,
k_machine_rip = 16
};
enum { kMaxInstructionByteSize = 32 };
uint8_t *m_cur_insn;
uint32_t m_machine_ip_regnum;
uint32_t m_machine_sp_regnum;
uint32_t m_machine_fp_regnum;
uint32_t m_lldb_ip_regnum;
uint32_t m_lldb_sp_regnum;
uint32_t m_lldb_fp_regnum;
typedef std::map<uint32_t, lldb_reg_info> MachineRegnumToNameAndLLDBRegnum;
MachineRegnumToNameAndLLDBRegnum m_reg_map;
lldb_private::ArchSpec m_arch;
CPU m_cpu;
int m_wordsize;
bool m_register_map_initialized;
::LLVMDisasmContextRef m_disasm_context;
DISALLOW_COPY_AND_ASSIGN(x86AssemblyInspectionEngine);
};
} // namespace lldb_private
#endif // liblldb_x86AssemblyInspectionEngine_h_

View File

@ -50,4 +50,5 @@ add_subdirectory(Process)
add_subdirectory(ScriptInterpreter)
add_subdirectory(Symbol)
add_subdirectory(SymbolFile)
add_subdirectory(UnwindAssembly)
add_subdirectory(Utility)

View File

@ -0,0 +1 @@
add_subdirectory(x86)

View File

@ -0,0 +1,3 @@
add_lldb_unittest(UnwindAssemblyx86Tests
Testx86AssemblyInspectionEngine.cpp
)

View File

@ -0,0 +1,281 @@
//===-- Testx86AssemblyInspectionEngine.cpp ---------------------------*- C++
//-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "gtest/gtest.h"
#include <vector>
#include "Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/AddressRange.h"
#include "lldb/Core/ArchSpec.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "llvm/Support/TargetSelect.h"
using namespace lldb;
using namespace lldb_private;
class Testx86AssemblyInspectionEngine : public testing::Test {
public:
// static void SetUpTestCase() { }
// static void TearDownTestCase() { }
// virtual void SetUp() override { }
// virtual void TearDown() override { }
protected:
};
// only defining the register names / numbers that the unwinder is actually
// using today
// names should match the constants below. These will be the eRegisterKindLLDB
// register numbers.
const char *x86_64_reg_names[] = {"rax", "rcx", "rdx", "rsp", "rbp", "rsi",
"rdi", "r8", "r9", "r10", "r11", "r12",
"r13", "r14", "r15", "rip"};
enum x86_64_regs {
k_rax = 0,
k_rcx = 1,
k_rdx = 2,
k_rsp = 3,
k_rbp = 4,
k_rsi = 5,
k_rdi = 6,
k_r8 = 7,
k_r9 = 8,
k_r10 = 9,
k_r11 = 10,
k_r12 = 11,
k_r13 = 12,
k_r14 = 13,
k_r15 = 14,
k_rip = 15
};
// names should match the constants below. These will be the eRegisterKindLLDB
// register numbers.
const char *i386_reg_names[] = {"eax", "ecx", "edx", "ebx", "esp",
"ebp", "esi", "edi", "eip"};
enum i386_regs {
k_eax = 0,
k_ecx = 1,
k_edx = 2,
k_ebx = 3,
k_esp = 4,
k_ebp = 5,
k_esi = 6,
k_edi = 7,
k_eip = 8
};
std::unique_ptr<x86AssemblyInspectionEngine> Getx86_64Inspector() {
ArchSpec arch("x86_64-apple-macosx", nullptr);
llvm::InitializeAllTargets();
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllDisassemblers();
std::unique_ptr<x86AssemblyInspectionEngine> engine(
new x86AssemblyInspectionEngine(arch));
std::vector<x86AssemblyInspectionEngine::lldb_reg_info> lldb_regnums;
int i = 0;
for (const auto &name : x86_64_reg_names) {
x86AssemblyInspectionEngine::lldb_reg_info ri;
ri.name = name;
ri.lldb_regnum = i++;
lldb_regnums.push_back(ri);
}
engine->Initialize(lldb_regnums);
return engine;
}
std::unique_ptr<x86AssemblyInspectionEngine> Geti386Inspector() {
ArchSpec arch("i386-apple-macosx", nullptr);
llvm::InitializeAllTargets();
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllDisassemblers();
std::unique_ptr<x86AssemblyInspectionEngine> engine(
new x86AssemblyInspectionEngine(arch));
std::vector<x86AssemblyInspectionEngine::lldb_reg_info> lldb_regnums;
int i = 0;
for (const auto &name : i386_reg_names) {
x86AssemblyInspectionEngine::lldb_reg_info ri;
ri.name = name;
ri.lldb_regnum = i++;
lldb_regnums.push_back(ri);
}
engine->Initialize(lldb_regnums);
return engine;
}
TEST_F(Testx86AssemblyInspectionEngine, TestSimple64bitFrameFunction) {
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
uint8_t data[] = {
0x55, // offset 0 -- pushq %rbp
0x48, 0x89, 0xe5, // offset 1 -- movq %rsp, %rbp
0x31, 0xc0, // offset 4 -- xorl %eax, %eax
0x5d, // offset 6 -- popq %rbp
0xc3 // offset 7 -- retq
};
AddressRange sample_range(0x1000, sizeof(data));
UnwindPlan unwind_plan(eRegisterKindLLDB);
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
data, sizeof(data), sample_range, unwind_plan));
// Expect four unwind rows:
// 0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
// 1: CFA=rsp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
// 4: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
// 7: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
EXPECT_TRUE(unwind_plan.GetInitialCFARegister() == k_rsp);
EXPECT_TRUE(unwind_plan.GetUnwindPlanValidAtAllInstructions() ==
eLazyBoolYes);
EXPECT_TRUE(unwind_plan.GetSourcedFromCompiler() == eLazyBoolNo);
UnwindPlan::Row::RegisterLocation regloc;
// 0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
UnwindPlan::RowSP row_sp = unwind_plan.GetRowForFunctionOffset(0);
EXPECT_TRUE(row_sp->GetOffset() == 0);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_TRUE(row_sp->GetCFAValue().GetOffset() == 8);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -8);
// 1: CFA=rsp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(1);
EXPECT_TRUE(row_sp->GetOffset() == 1);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_TRUE(row_sp->GetCFAValue().GetOffset() == 16);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -8);
// 4: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(4);
EXPECT_TRUE(row_sp->GetOffset() == 4);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_TRUE(row_sp->GetCFAValue().GetOffset() == 16);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -8);
// 7: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(7);
EXPECT_TRUE(row_sp->GetOffset() == 7);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_TRUE(row_sp->GetCFAValue().GetOffset() == 8);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -8);
}
TEST_F(Testx86AssemblyInspectionEngine, TestSimple32bitFrameFunction) {
std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector();
uint8_t data[] = {
0x55, // offset 0 -- pushl %ebp
0x89, 0xe5, // offset 1 -- movl %esp, %ebp
0x31, 0xc0, // offset 3 -- xorl %eax, %eax
0x5d, // offset 5 -- popl %ebp
0xc3 // offset 6 -- retl
};
AddressRange sample_range(0x1000, sizeof(data));
UnwindPlan unwind_plan(eRegisterKindLLDB);
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
data, sizeof(data), sample_range, unwind_plan));
// Expect four unwind rows:
// 0: CFA=esp +4 => esp=CFA+0 eip=[CFA-4]
// 1: CFA=esp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4]
// 3: CFA=ebp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4]
// 6: CFA=esp +4 => esp=CFA+0 eip=[CFA-4]
EXPECT_TRUE(unwind_plan.GetInitialCFARegister() == k_esp);
EXPECT_TRUE(unwind_plan.GetUnwindPlanValidAtAllInstructions() ==
eLazyBoolYes);
EXPECT_TRUE(unwind_plan.GetSourcedFromCompiler() == eLazyBoolNo);
UnwindPlan::Row::RegisterLocation regloc;
// offset 0 -- pushl %ebp
UnwindPlan::RowSP row_sp = unwind_plan.GetRowForFunctionOffset(0);
EXPECT_TRUE(row_sp->GetOffset() == 0);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_TRUE(row_sp->GetCFAValue().GetOffset() == 4);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -4);
// 1: CFA=esp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4]
row_sp = unwind_plan.GetRowForFunctionOffset(1);
EXPECT_TRUE(row_sp->GetOffset() == 1);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_TRUE(row_sp->GetCFAValue().GetOffset() == 8);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -4);
// 3: CFA=ebp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4]
row_sp = unwind_plan.GetRowForFunctionOffset(3);
EXPECT_TRUE(row_sp->GetOffset() == 3);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_ebp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_TRUE(row_sp->GetCFAValue().GetOffset() == 8);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -4);
// 6: CFA=esp +4 => esp=CFA+0 eip=[CFA-4]
row_sp = unwind_plan.GetRowForFunctionOffset(6);
EXPECT_TRUE(row_sp->GetOffset() == 6);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_TRUE(row_sp->GetCFAValue().GetOffset() == 4);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -4);
}