The rationale here is that ArchSpec is used throughout the codebase, including in places which should not depend on the rest of the code in the Core module. This commit touches many files, but most of it is just renaming of #include lines. In a couple of cases, I removed the #include ArchSpec line altogether, as the file was not using it. In one or two places, this necessitated adding other #includes like lldb-private-defines.h. llvm-svn: 318048
2443 lines
84 KiB
C++
2443 lines
84 KiB
C++
//===-- 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/Symbol/UnwindPlan.h"
|
|
#include "lldb/Utility/ArchSpec.h"
|
|
#include "lldb/Utility/StreamString.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:
|
|
};
|
|
|
|
void Testx86AssemblyInspectionEngine::SetUpTestCase() {
|
|
llvm::InitializeAllTargets();
|
|
llvm::InitializeAllAsmPrinters();
|
|
llvm::InitializeAllTargetMCs();
|
|
llvm::InitializeAllDisassemblers();
|
|
}
|
|
|
|
// 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", "rbx", "rcx", "rdx", "rsp", "rbp",
|
|
"rsi", "rdi", "r8", "r9", "r10", "r11",
|
|
"r12", "r13", "r14", "r15", "rip"};
|
|
|
|
enum x86_64_regs {
|
|
k_rax = 0,
|
|
k_rbx = 1,
|
|
k_rcx = 2,
|
|
k_rdx = 3,
|
|
k_rsp = 4,
|
|
k_rbp = 5,
|
|
k_rsi = 6,
|
|
k_rdi = 7,
|
|
k_r8 = 8,
|
|
k_r9 = 9,
|
|
k_r10 = 10,
|
|
k_r11 = 11,
|
|
k_r12 = 12,
|
|
k_r13 = 13,
|
|
k_r14 = 14,
|
|
k_r15 = 15,
|
|
k_rip = 16
|
|
};
|
|
|
|
// 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");
|
|
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");
|
|
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;
|
|
}
|
|
|
|
namespace lldb_private {
|
|
static std::ostream &operator<<(std::ostream &OS,
|
|
const UnwindPlan::Row::CFAValue &CFA) {
|
|
StreamString S;
|
|
CFA.Dump(S, nullptr, nullptr);
|
|
return OS << S.GetData();
|
|
}
|
|
} // namespace lldb_private
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestSimple64bitFrameFunction) {
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
|
|
|
|
// 'int main() { }' compiled for x86_64-apple-macosx with clang
|
|
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_EQ(0ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
|
|
// 1: CFA=rsp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(1);
|
|
EXPECT_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
|
|
// 4: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(4);
|
|
EXPECT_EQ(4ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
|
|
// 7: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(7);
|
|
EXPECT_EQ(7ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestSimple32bitFrameFunction) {
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector();
|
|
|
|
// 'int main() { }' compiled for i386-apple-macosx with clang
|
|
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_EQ(0ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset());
|
|
|
|
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_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-4, regloc.GetOffset());
|
|
|
|
// 3: CFA=ebp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(3);
|
|
EXPECT_EQ(3ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_ebp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-4, regloc.GetOffset());
|
|
|
|
// 6: CFA=esp +4 => esp=CFA+0 eip=[CFA-4]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(6);
|
|
EXPECT_EQ(6ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-4, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, Test64bitFramelessBigStackFrame) {
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
|
|
|
|
// this source file:
|
|
//
|
|
// #include <stdio.h>
|
|
// int main (int argc, char **argv)
|
|
// {
|
|
//
|
|
// const int arrsize = 60;
|
|
// int buf[arrsize * arrsize];
|
|
// int accum = argc;
|
|
// for (int i = 0; i < arrsize; i++)
|
|
// for (int j = 0; j < arrsize; j++)
|
|
// {
|
|
// if (i > 0 && j > 0)
|
|
// {
|
|
// int n = buf[(i-1) * (j-1)] * 2;
|
|
// int m = buf[(i-1) * (j-1)] / 2;
|
|
// int j = buf[(i-1) * (j-1)] + 2;
|
|
// int k = buf[(i-1) * (j-1)] - 2;
|
|
// printf ("%d ", n + m + j + k);
|
|
// buf[(i-1) * (j-1)] += n - m + j - k;
|
|
// }
|
|
// buf[i*j] = accum++;
|
|
// }
|
|
//
|
|
// return buf[(arrsize * arrsize) - 2] + printf ("%d\n", buf[(arrsize *
|
|
// arrsize) - 3]);
|
|
// }
|
|
//
|
|
// compiled 'clang -fomit-frame-pointer -Os' for x86_64-apple-macosx
|
|
|
|
uint8_t data[] = {
|
|
0x55, // offset 0 -- pushq %rbp
|
|
0x41, 0x57, // offset 1 -- pushq %r15
|
|
0x41, 0x56, // offset 3 -- pushq %r14
|
|
0x41, 0x55, // offset 5 -- pushq %r13
|
|
0x41, 0x54, // offset 7 -- pushq %r12
|
|
0x53, // offset 9 -- pushq %rbx
|
|
0x48, 0x81, 0xec, 0x68, 0x38, 0x00,
|
|
0x00, // offset 10 -- subq $0x3868, %rsp
|
|
|
|
// ....
|
|
|
|
0x48, 0x81, 0xc4, 0x68, 0x38, 0x00,
|
|
0x00, // offset 17 -- addq $0x3868, %rsp
|
|
0x5b, // offset 24 -- popq %rbx
|
|
0x41, 0x5c, // offset 25 -- popq %r12
|
|
0x41, 0x5d, // offset 27 -- popq %r13
|
|
0x41, 0x5e, // offset 29 -- popq %r14
|
|
0x41, 0x5f, // offset 31 -- popq %r15
|
|
0x5d, // offset 33 -- popq %rbp
|
|
0xc3, // offset 34 -- retq
|
|
0xe8, 0x12, 0x34, 0x56, 0x78 // offset 35 -- callq whatever
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
// Unwind rules should look like
|
|
// 0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
|
|
// 1: CFA=rsp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
|
|
// 3: CFA=rsp+24 => rbp=[CFA-16] rsp=CFA+0 r15=[CFA-24] rip=[CFA-8]
|
|
// 5: CFA=rsp+32 => rbp=[CFA-16] rsp=CFA+0 r14=[CFA-32] r15=[CFA-24]
|
|
// rip=[CFA-8
|
|
// 7: CFA=rsp+40 => rbp=[CFA-16] rsp=CFA+0 r13=[CFA-40] r14=[CFA-32]
|
|
// r15=[CFA-24] rip=[CFA-8]
|
|
// 9: CFA=rsp+48 => rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48] r13=[CFA-40]
|
|
// r14=[CFA-32] r15=[CFA-24] rip=[CFA-8]
|
|
// 10: CFA=rsp+56 => rbx=[CFA-56] rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48]
|
|
// r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] rip=[CFA-8]
|
|
// 17: CFA=rsp+14496 => rbx=[CFA-56] rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48]
|
|
// r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] rip=[CFA-8]
|
|
|
|
// 24: CFA=rsp+56 => rbx=[CFA-56] rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48]
|
|
// r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] rip=[CFA-8]
|
|
// 25: CFA=rsp+48 => rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48] r13=[CFA-40]
|
|
// r14=[CFA-32] r15=[CFA-24] rip=[CFA-8]
|
|
// 27: CFA=rsp+40 => rbp=[CFA-16] rsp=CFA+0 r13=[CFA-40] r14=[CFA-32]
|
|
// r15=[CFA-24] rip=[CFA-8]
|
|
// 29: CFA=rsp+32 => rbp=[CFA-16] rsp=CFA+0 r14=[CFA-32] r15=[CFA-24]
|
|
// rip=[CFA-8]
|
|
// 31: CFA=rsp+24 => rbp=[CFA-16] rsp=CFA+0 r15=[CFA-24] rip=[CFA-8]
|
|
// 33: CFA=rsp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
|
|
// 34: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
|
|
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
|
|
// grab the Row for when the prologue has finished executing:
|
|
// 17: CFA=rsp+14496 => rbx=[CFA-56] rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48]
|
|
// r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] rip=[CFA-8]
|
|
|
|
UnwindPlan::RowSP row_sp = unwind_plan.GetRowForFunctionOffset(17);
|
|
|
|
EXPECT_EQ(17ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(14496, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbp, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-16, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r15, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-24, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r14, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-32, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r13, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-40, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r12, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-48, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbx, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-56, regloc.GetOffset());
|
|
|
|
// grab the Row for when the epilogue has finished executing:
|
|
// 34: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(34);
|
|
|
|
EXPECT_EQ(34ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
|
|
// these could be set to IsSame and be valid -- meaning that the
|
|
// register value is the same as the caller's -- but I'd rather
|
|
// they not be mentioned at all.
|
|
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rax, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbx, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rcx, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rdx, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbp, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rsi, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rdi, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r8, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r9, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r10, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r11, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r12, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r13, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r14, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r15, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, Test32bitFramelessBigStackFrame) {
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector();
|
|
|
|
// this source file:
|
|
//
|
|
// #include <stdio.h>
|
|
// int main (int argc, char **argv)
|
|
// {
|
|
//
|
|
// const int arrsize = 60;
|
|
// int buf[arrsize * arrsize];
|
|
// int accum = argc;
|
|
// for (int i = 0; i < arrsize; i++)
|
|
// for (int j = 0; j < arrsize; j++)
|
|
// {
|
|
// if (i > 0 && j > 0)
|
|
// {
|
|
// int n = buf[(i-1) * (j-1)] * 2;
|
|
// int m = buf[(i-1) * (j-1)] / 2;
|
|
// int j = buf[(i-1) * (j-1)] + 2;
|
|
// int k = buf[(i-1) * (j-1)] - 2;
|
|
// printf ("%d ", n + m + j + k);
|
|
// buf[(i-1) * (j-1)] += n - m + j - k;
|
|
// }
|
|
// buf[i*j] = accum++;
|
|
// }
|
|
//
|
|
// return buf[(arrsize * arrsize) - 2] + printf ("%d\n", buf[(arrsize *
|
|
// arrsize) - 3]);
|
|
// }
|
|
//
|
|
// compiled 'clang -arch i386 -fomit-frame-pointer -Os' for i386-apple-macosx
|
|
|
|
// simplified assembly version of the above function, which is used as the
|
|
// input
|
|
// data:
|
|
//
|
|
// .section __TEXT,__text,regular,pure_instructions
|
|
// .macosx_version_min 10, 12
|
|
// .globl _main
|
|
// .align 4, 0x90
|
|
// _main: ## @main
|
|
// ## BB#0:
|
|
// pushl %ebp
|
|
// pushl %ebx
|
|
// pushl %edi
|
|
// pushl %esi
|
|
// L0$pb:
|
|
// subl $0x386c, %esp
|
|
// calll L1
|
|
// L1:
|
|
// popl %ecx
|
|
// movl %ecx, 0x8(%esp)
|
|
// subl $0x8, %esp
|
|
// pushl %eax
|
|
// pushl 0x20(%esp)
|
|
// calll _puts
|
|
// addl $0x10, %esp
|
|
// incl %ebx
|
|
// addl $0x386c, %esp
|
|
// popl %esi
|
|
// popl %edi
|
|
// popl %ebx
|
|
// popl %ebp
|
|
// retl
|
|
//
|
|
// .section __TEXT,__cstring,cstring_literals
|
|
// L_.str: ## @.str
|
|
// .asciz "HI"
|
|
//
|
|
//
|
|
// .subsections_via_symbols
|
|
|
|
uint8_t data[] = {
|
|
0x55,
|
|
// offset 0 -- pushl %ebp
|
|
|
|
0x53,
|
|
// offset 1 -- pushl %ebx
|
|
|
|
0x57,
|
|
// offset 2 -- pushl %edi
|
|
|
|
0x56,
|
|
// offset 3 -- pushl %esi
|
|
|
|
0x81, 0xec, 0x6c, 0x38, 0x00, 0x00,
|
|
// offset 4 -- subl $0x386c, %esp
|
|
|
|
0xe8, 0x00, 0x00, 0x00, 0x00,
|
|
// offset 10 -- calll 0
|
|
// call the next instruction, to put the pc on the stack
|
|
|
|
0x59,
|
|
// offset 15 -- popl %ecx
|
|
// pop the saved pc address into ecx
|
|
|
|
0x89, 0x4c, 0x24, 0x08,
|
|
// offset 16 -- movl %ecx, 0x8(%esp)
|
|
|
|
// ....
|
|
|
|
0x83, 0xec, 0x08,
|
|
// offset 20 -- subl $0x8, %esp
|
|
|
|
0x50,
|
|
// offset 23 -- pushl %eax
|
|
|
|
0xff, 0x74, 0x24, 0x20,
|
|
// offset 24 -- pushl 0x20(%esp)
|
|
|
|
0xe8, 0x8c, 0x00, 0x00, 0x00,
|
|
// offset 28 -- calll puts
|
|
|
|
0x83, 0xc4, 0x10,
|
|
// offset 33 -- addl $0x10, %esp
|
|
// get esp back to the value it was before the
|
|
// alignment & argument saves for the puts call
|
|
|
|
0x43,
|
|
// offset 36 -- incl %ebx
|
|
|
|
// ....
|
|
|
|
0x81, 0xc4, 0x6c, 0x38, 0x00, 0x00,
|
|
// offset 37 -- addl $0x386c, %esp
|
|
|
|
0x5e,
|
|
// offset 43 -- popl %esi
|
|
|
|
0x5f,
|
|
// offset 44 -- popl %edi
|
|
|
|
0x5b,
|
|
// offset 45 -- popl %ebx
|
|
|
|
0x5d,
|
|
// offset 46 -- popl %ebp
|
|
|
|
0xc3,
|
|
// offset 47 -- retl
|
|
|
|
0xe8, 0x12, 0x34, 0x56, 0x78,
|
|
// offset 48 -- calll __stack_chk_fail
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
// Unwind rules should look like
|
|
//
|
|
// 0: CFA=esp +4 => esp=CFA+0 eip=[CFA-4]
|
|
// 1: CFA=esp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4]
|
|
// 2: CFA=esp+12 => ebx=[CFA-12] ebp=[CFA-8] esp=CFA+0 eip=[CFA-4]
|
|
// 3: CFA=esp+16 => ebx=[CFA-12] edi=[CFA-16] ebp=[CFA-8] esp=CFA+0
|
|
// eip=[CFA-4]
|
|
// 4: CFA=esp+20 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
// 10: CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
// 15: CFA=esp+14468 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
// 16: CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
//
|
|
// ....
|
|
//
|
|
// 23: CFA=esp+14472 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
// 24: CFA=esp+14476 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
// 28: CFA=esp+14480 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
// 36: CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
//
|
|
// .....
|
|
//
|
|
// 37: CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
// 43: CFA=esp+20 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
// 44: CFA=esp+16 => ebx=[CFA-12] edi=[CFA-16] ebp=[CFA-8] esp=CFA+0
|
|
// eip=[CFA-4]
|
|
// 45: CFA=esp+12 => ebx=[CFA-12] ebp=[CFA-8] esp=CFA+0 eip=[CFA-4]
|
|
// 46: CFA=esp +8 => ebp=[CFA-8] esp=CFA+0 eip=[CFA-4]
|
|
// 47: CFA=esp +4 => esp=CFA+0 eip=[CFA-4]
|
|
// 48: CFA=esp+14480 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
|
|
// Check that we get the CFA correct for the pic base setup sequence
|
|
|
|
// CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(10);
|
|
EXPECT_EQ(10ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(14464, row_sp->GetCFAValue().GetOffset());
|
|
|
|
// 15: CFA=esp+14468 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(15);
|
|
EXPECT_EQ(15ull, row_sp->GetOffset());
|
|
EXPECT_EQ(14468, row_sp->GetCFAValue().GetOffset());
|
|
|
|
// 16: CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(16);
|
|
EXPECT_EQ(16ull, row_sp->GetOffset());
|
|
EXPECT_EQ(14464, row_sp->GetCFAValue().GetOffset());
|
|
|
|
// Check that the row for offset 16 has the registers saved that we expect
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-4, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_ebp, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_ebx, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-12, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_edi, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-16, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_esi, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-20, regloc.GetOffset());
|
|
|
|
//
|
|
// Check the pushing & popping around the call printf instruction
|
|
|
|
// 23: CFA=esp+14472 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(23);
|
|
EXPECT_EQ(23ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(14472, row_sp->GetCFAValue().GetOffset());
|
|
|
|
// 24: CFA=esp+14476 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(24);
|
|
EXPECT_EQ(24ull, row_sp->GetOffset());
|
|
EXPECT_EQ(14476, row_sp->GetCFAValue().GetOffset());
|
|
|
|
// 28: CFA=esp+14480 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(28);
|
|
EXPECT_EQ(28ull, row_sp->GetOffset());
|
|
EXPECT_EQ(14480, row_sp->GetCFAValue().GetOffset());
|
|
|
|
// 36: CFA=esp+14464 => ebx=[CFA-12] edi=[CFA-16] esi=[CFA-20] ebp=[CFA-8]
|
|
// esp=CFA+0 eip=[CFA-4]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(36);
|
|
EXPECT_EQ(36ull, row_sp->GetOffset());
|
|
EXPECT_EQ(14464, row_sp->GetCFAValue().GetOffset());
|
|
|
|
// Check that the epilogue gets us back to the original unwind state
|
|
|
|
// 47: CFA=esp +4 => esp=CFA+0 eip=[CFA-4]
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(47);
|
|
EXPECT_EQ(47ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_eip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-4, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_esp, regloc));
|
|
EXPECT_TRUE(regloc.IsCFAPlusOffset());
|
|
EXPECT_EQ(0, regloc.GetOffset());
|
|
|
|
// Check that no unexpected registers were saved
|
|
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_eax, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebx, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_ecx, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_edx, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_esi, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_edi, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebp, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, Test64bitFramelessSmallStackFrame) {
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
|
|
|
|
// this source file:
|
|
// #include <stdio.h>
|
|
// int main () {
|
|
// puts ("HI");
|
|
// }
|
|
//
|
|
// compiled 'clang -fomit-frame-pointer' for x86_64-apple-macosx
|
|
|
|
uint8_t data[] = {
|
|
0x50,
|
|
// offset 0 -- pushq %rax
|
|
|
|
0x48, 0x8d, 0x3d, 0x32, 0x00, 0x00, 0x00,
|
|
// offset 1 -- leaq 0x32(%rip), %rdi ; "HI"
|
|
|
|
0xe8, 0x0b, 0x00, 0x00, 0x00,
|
|
// offset 8 -- callq 0x100000f58 ; puts
|
|
|
|
0x31, 0xc9,
|
|
// offset 13 -- xorl %ecx, %ecx
|
|
|
|
0x89, 0x44, 0x24, 0x04,
|
|
// offset 15 -- movl %eax, 0x4(%rsp)
|
|
|
|
0x89, 0xc8,
|
|
// offset 19 -- movl %ecx, %eax
|
|
|
|
0x59,
|
|
// offset 21 -- popq %rcx
|
|
|
|
0xc3
|
|
// offset 22 -- retq
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
// Unwind rules should look like
|
|
// 0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
|
|
// 1: CFA=rsp+16 => rsp=CFA+0 rip=[CFA-8]
|
|
// 22: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
|
|
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
|
|
// grab the Row for when the prologue has finished executing:
|
|
// 1: CFA=rsp+16 => rsp=CFA+0 rip=[CFA-8]
|
|
|
|
UnwindPlan::RowSP row_sp = unwind_plan.GetRowForFunctionOffset(13);
|
|
|
|
EXPECT_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
|
|
// none of these were spilled
|
|
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rax, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbx, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rcx, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rdx, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbp, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rsi, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rdi, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r8, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r9, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r10, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r11, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r12, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r13, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r14, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r15, regloc));
|
|
|
|
// grab the Row for when the epilogue has finished executing:
|
|
// 22: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(22);
|
|
|
|
EXPECT_EQ(22ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, Test32bitFramelessSmallStackFrame) {
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector();
|
|
|
|
// this source file:
|
|
// #include <stdio.h>
|
|
// int main () {
|
|
// puts ("HI");
|
|
// }
|
|
//
|
|
// compiled 'clang -arch i386 -fomit-frame-pointer' for i386-apple-macosx
|
|
|
|
uint8_t data[] = {
|
|
0x83, 0xec, 0x0c,
|
|
// offset 0 -- subl $0xc, %esp
|
|
|
|
0xe8, 0x00, 0x00, 0x00, 0x00,
|
|
// offset 3 -- calll 0 {call the next instruction, to put the pc on
|
|
// the stack}
|
|
|
|
0x58,
|
|
// offset 8 -- popl %eax {pop the saved pc value off stack, into eax}
|
|
|
|
0x8d, 0x80, 0x3a, 0x00, 0x00, 0x00,
|
|
// offset 9 -- leal 0x3a(%eax),%eax
|
|
|
|
0x89, 0x04, 0x24,
|
|
// offset 15 -- movl %eax, (%esp)
|
|
|
|
0xe8, 0x0d, 0x00, 0x00, 0x00,
|
|
// offset 18 -- calll 0x1f94 (puts)
|
|
|
|
0x31, 0xc9,
|
|
// offset 23 -- xorl %ecx, %ecx
|
|
|
|
0x89, 0x44, 0x24, 0x08,
|
|
// offset 25 -- movl %eax, 0x8(%esp)
|
|
|
|
0x89, 0xc8,
|
|
// offset 29 -- movl %ecx, %eax
|
|
|
|
0x83, 0xc4, 0x0c,
|
|
// offset 31 -- addl $0xc, %esp
|
|
|
|
0xc3
|
|
// offset 34 -- retl
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
// Unwind rules should look like
|
|
// row[0]: 0: CFA=esp +4 => esp=CFA+0 eip=[CFA-4]
|
|
// row[1]: 3: CFA=esp+16 => esp=CFA+0 eip=[CFA-4]
|
|
// row[2]: 8: CFA=esp+20 => esp=CFA+0 eip=[CFA-4]
|
|
// row[3]: 9: CFA=esp+16 => esp=CFA+0 eip=[CFA-4]
|
|
// row[4]: 34: CFA=esp +4 => esp=CFA+0 eip=[CFA-4]
|
|
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
|
|
// Check unwind state before we set up the picbase register
|
|
// 3: CFA=esp+16 => esp=CFA+0 eip=[CFA-4]
|
|
|
|
UnwindPlan::RowSP row_sp = unwind_plan.GetRowForFunctionOffset(3);
|
|
|
|
EXPECT_EQ(3ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
// Check unwind state after we call the next instruction
|
|
// 8: CFA=esp+20 => esp=CFA+0 eip=[CFA-4]
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(8);
|
|
EXPECT_EQ(8ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(20, row_sp->GetCFAValue().GetOffset());
|
|
|
|
// Check unwind state after we pop the pic base value off the stack
|
|
// row[3]: 9: CFA=esp+16 => esp=CFA+0 eip=[CFA-4]
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(9);
|
|
EXPECT_EQ(9ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
// Check that no unexpected registers were saved
|
|
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_eax, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebx, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_ecx, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_edx, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_esi, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_edi, regloc));
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebp, regloc));
|
|
|
|
// verify that we get back to the original unwind state before the ret
|
|
// 34: CFA=esp +4 => esp=CFA+0 eip=[CFA-4]
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(34);
|
|
EXPECT_EQ(34ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushRBP) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
|
|
uint8_t data[] = {
|
|
0x55, // pushq %rbp
|
|
0x90 // nop
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(1);
|
|
|
|
EXPECT_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbp, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-16, regloc.GetOffset());
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(1);
|
|
|
|
EXPECT_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbp, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushImm) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
|
|
uint8_t data[] = {
|
|
0x68, 0xff, 0xff, 0x01, 0x69, // pushq $0x6901ffff
|
|
0x6a, 0x7d, // pushl $0x7d
|
|
0x90 // nop
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(5);
|
|
EXPECT_EQ(5ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(7);
|
|
EXPECT_EQ(7ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(24, row_sp->GetCFAValue().GetOffset());
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(5);
|
|
EXPECT_EQ(5ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(7);
|
|
EXPECT_EQ(7ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(12, row_sp->GetCFAValue().GetOffset());
|
|
}
|
|
|
|
// We treat 'pushq $0' / 'pushl $0' specially - this shows up
|
|
// in the first function called in a new thread and it needs to
|
|
// put a 0 as the saved pc. We pretend it didn't change the CFA.
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPush0) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
|
|
uint8_t data[] = {
|
|
0x6a, 0x00, // pushq $0
|
|
0x90 // nop
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
|
|
// We're verifying that no row was created for the 'pushq $0'
|
|
EXPECT_EQ(0ull, row_sp->GetOffset());
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
|
|
// We're verifying that no row was created for the 'pushq $0'
|
|
EXPECT_EQ(0ull, row_sp->GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushExtended) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
|
|
uint8_t data[] = {
|
|
0xff, 0x74, 0x24, 0x20, // pushl 0x20(%esp)
|
|
0xff, 0xb6, 0xce, 0x01, 0xf0, 0x00, // pushl 0xf001ce(%esi)
|
|
0xff, 0x30, // pushl (%eax)
|
|
0x90 // nop
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(4);
|
|
|
|
EXPECT_EQ(4ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(4);
|
|
EXPECT_EQ(4ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(10);
|
|
EXPECT_EQ(10ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(12, row_sp->GetCFAValue().GetOffset());
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(12);
|
|
EXPECT_EQ(12ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushR15) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
|
|
uint8_t data[] = {
|
|
0x41, 0x57, // pushq %r15
|
|
0x90 // nop
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r15, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-16, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushR14) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
|
|
uint8_t data[] = {
|
|
0x41, 0x56, // pushq %r14
|
|
0x90 // nop
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r14, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-16, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushR13) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
|
|
uint8_t data[] = {
|
|
0x41, 0x55, // pushq %r13
|
|
0x90 // nop
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r13, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-16, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushR12) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
|
|
uint8_t data[] = {
|
|
0x41, 0x54, // pushq %r13
|
|
0x90 // nop
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r12, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-16, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushRBX) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
|
|
uint8_t data[] = {
|
|
0x53, // pushq %rbx
|
|
0x90 // nop
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(1);
|
|
|
|
EXPECT_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbx, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-16, regloc.GetOffset());
|
|
}
|
|
|
|
// The ABI is hardcoded in x86AssemblyInspectionEngine such that
|
|
// eax, ecx, edx are all considered volatile and push/pops of them are
|
|
// not tracked (except to keep track of stack pointer movement)
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushEAX) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x50, // pushl %eax
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(1);
|
|
EXPECT_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_eax, regloc));
|
|
}
|
|
|
|
// The ABI is hardcoded in x86AssemblyInspectionEngine such that
|
|
// eax, ecx, edx are all considered volatile and push/pops of them are
|
|
// not tracked (except to keep track of stack pointer movement)
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushECX) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x51, // pushl %ecx
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(1);
|
|
EXPECT_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_ecx, regloc));
|
|
}
|
|
|
|
// The ABI is hardcoded in x86AssemblyInspectionEngine such that
|
|
// eax, ecx, edx are all considered volatile and push/pops of them are
|
|
// not tracked (except to keep track of stack pointer movement)
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushEDX) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x52, // pushl %edx
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(1);
|
|
EXPECT_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_edx, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushEBX) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x53, // pushl %ebx
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(1);
|
|
EXPECT_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_ebx, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushEBP) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x55, // pushl %ebp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(1);
|
|
EXPECT_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_ebp, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushESI) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x56, // pushl %esi
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(1);
|
|
EXPECT_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_esi, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPushEDI) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x57, // pushl %edi
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(1);
|
|
EXPECT_EQ(1ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_edi, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestMovRSPtoRBP) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
|
|
uint8_t data64_1[] = {
|
|
0x48, 0x8b, 0xec, // movq %rsp, %rbp
|
|
0x90 // nop
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data64_1));
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data64_1, sizeof(data64_1), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(3);
|
|
|
|
EXPECT_EQ(3ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
uint8_t data64_2[] = {
|
|
0x48, 0x89, 0xe5, // movq %rsp, %rbp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data64_2));
|
|
unwind_plan.Clear();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data64_2, sizeof(data64_2), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(3);
|
|
EXPECT_EQ(3ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
uint8_t data32_1[] = {
|
|
0x8b, 0xec, // movl %rsp, %rbp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data32_1));
|
|
unwind_plan.Clear();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data32_1, sizeof(data32_1), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_ebp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
uint8_t data32_2[] = {
|
|
0x89, 0xe5, // movl %rsp, %rbp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data32_2));
|
|
unwind_plan.Clear();
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data32_2, sizeof(data32_2), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_ebp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestSubRSP) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
|
|
uint8_t data1[] = {
|
|
0x48, 0x81, 0xec, 0x00, 0x01, 0x00, 0x00, // subq $0x100, $rsp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data1));
|
|
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data1, sizeof(data1), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(7);
|
|
EXPECT_EQ(7ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(264, row_sp->GetCFAValue().GetOffset());
|
|
|
|
uint8_t data2[] = {
|
|
0x48, 0x83, 0xec, 0x10, // subq $0x10, %rsp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data2));
|
|
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data2, sizeof(data2), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(4);
|
|
EXPECT_EQ(4ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(24, row_sp->GetCFAValue().GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestSubESP) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data1[] = {
|
|
0x81, 0xec, 0x00, 0x01, 0x00, 0x00, // subl $0x100, %esp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data1));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data1, sizeof(data1), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(6);
|
|
EXPECT_EQ(6ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(260, row_sp->GetCFAValue().GetOffset());
|
|
|
|
uint8_t data2[] = {
|
|
0x83, 0xec, 0x10, // subq $0x10, %esp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data2));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data2, sizeof(data2), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(3);
|
|
EXPECT_EQ(3ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(20, row_sp->GetCFAValue().GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestAddRSP) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
|
|
uint8_t data1[] = {
|
|
0x48, 0x81, 0xc4, 0x00, 0x01, 0x00, 0x00, // addq $0x100, %rsp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data1));
|
|
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data1, sizeof(data1), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(7);
|
|
EXPECT_EQ(7ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8 - 256, row_sp->GetCFAValue().GetOffset());
|
|
|
|
uint8_t data2[] = {
|
|
0x48, 0x83, 0xc4, 0x10, // addq $0x10, %rsp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data2));
|
|
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data2, sizeof(data2), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(4);
|
|
EXPECT_EQ(4ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8 - 16, row_sp->GetCFAValue().GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestAddESP) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data1[] = {
|
|
0x81, 0xc4, 0x00, 0x01, 0x00, 0x00, // addl $0x100, %esp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data1));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data1, sizeof(data1), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(6);
|
|
EXPECT_EQ(6ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(4 - 256, row_sp->GetCFAValue().GetOffset());
|
|
|
|
uint8_t data2[] = {
|
|
0x83, 0xc4, 0x10, // addq $0x10, %esp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data2));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data2, sizeof(data2), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(3);
|
|
EXPECT_EQ(3ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(4 - 16, row_sp->GetCFAValue().GetOffset());
|
|
}
|
|
|
|
// FIXME add test for lea_rsp_pattern_p
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPopRBX) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x53, // pushq %rbx
|
|
0x5b, // popq %rbx
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbx, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPopRBP) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x55, // pushq %rbp
|
|
0x5d, // popq %rbp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbp, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPopR12) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x41, 0x54, // pushq %r12
|
|
0x41, 0x5c, // popq %r12
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(4);
|
|
EXPECT_EQ(4ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r12, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPopR13) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x41, 0x55, // pushq %r13
|
|
0x41, 0x5d, // popq %r13
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(4);
|
|
EXPECT_EQ(4ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r13, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPopR14) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x41, 0x56, // pushq %r14
|
|
0x41, 0x5e, // popq %r14
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(4);
|
|
EXPECT_EQ(4ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r14, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPopR15) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x41, 0x57, // pushq %r15
|
|
0x41, 0x5f, // popq %r15
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(4);
|
|
EXPECT_EQ(4ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_r15, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPopEBX) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x53, // pushl %ebx
|
|
0x5b, // popl %ebx
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebx, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPopEBP) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x55, // pushl %ebp
|
|
0x5d, // popl %ebp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebp, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPopESI) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x56, // pushl %esi
|
|
0x5e, // popl %esi
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_esi, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestPopEDI) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x57, // pushl %edi
|
|
0x5f, // popl %edi
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_edi, regloc));
|
|
}
|
|
|
|
// We don't track these registers, but make sure the CFA address is updated
|
|
// if we're defining the CFA in term of esp.
|
|
TEST_F(Testx86AssemblyInspectionEngine, Testi386IgnoredRegisters) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x0e, // push cs
|
|
0x16, // push ss
|
|
0x1e, // push ds
|
|
0x06, // push es
|
|
|
|
0x07, // pop es
|
|
0x1f, // pop ds
|
|
0x17, // pop ss
|
|
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(4);
|
|
EXPECT_EQ(4ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(20, row_sp->GetCFAValue().GetOffset());
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(7);
|
|
EXPECT_EQ(7ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestLEAVE) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x55, // push %rbp/ebp
|
|
0xc9, // leave
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbp, regloc));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebp, regloc));
|
|
}
|
|
|
|
// In i386, which lacks pc-relative addressing, a common code sequence
|
|
// is to call the next instruction (i.e. call imm32, value of 0) which
|
|
// pushes the addr of the next insn on the stack, and then pop that value
|
|
// into a register (the "pic base" register).
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestCALLNextInsn) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0xe8, 0x00, 0x00, 0x00, 0x00, // call 0
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(5);
|
|
EXPECT_EQ(5ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebp, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestSpillRegToStackViaMOVx86_64) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x55, // pushq %rbp
|
|
0x48, 0x89, 0xe5, // movq %rsp, %rbp
|
|
0x4c, 0x89, 0x75, 0xc0, // movq %r14, -0x40(%rbp)
|
|
0x4c, 0x89, 0xbd, 0x28, 0xfa, 0xff, 0xff, // movq %r15, -0x5d8(%rbp)
|
|
0x48, 0x89, 0x5d, 0xb8, // movq %rbx, -0x48(%rbp)
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(19);
|
|
EXPECT_EQ(19ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp);
|
|
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r14, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-80, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r15, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-1512, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbx, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-88, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestSpillRegToStackViaMOVi386) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x55, // pushl %ebp
|
|
0x89, 0xe5, // movl %esp, %ebp
|
|
0x89, 0x9d, 0xb0, 0xfe, 0xff, 0xff, // movl %ebx, -0x150(%ebp)
|
|
0x89, 0x75, 0xe0, // movl %esi, -0x20(%ebp)
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(12);
|
|
EXPECT_EQ(12ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_ebx, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-344, regloc.GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_esi, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-40, regloc.GetOffset());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestSimplex86_64Augmented) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x55, // pushq %rbp
|
|
0x48, 0x89, 0xe5, // movq %rsp, %rbp
|
|
|
|
// x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite
|
|
// has a bug where it can't augment a function that is just
|
|
// prologue+epilogue - it needs at least one other instruction
|
|
// in between.
|
|
0x90, // nop
|
|
|
|
0x5d, // popq %rbp
|
|
0xc3 // retq
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
unwind_plan.SetSourceName("unit testing hand-created unwind plan");
|
|
unwind_plan.SetPlanValidAddressRange(sample_range);
|
|
unwind_plan.SetRegisterKind(eRegisterKindLLDB);
|
|
|
|
row_sp.reset(new UnwindPlan::Row);
|
|
|
|
// Describe offset 0
|
|
row_sp->SetOffset(0);
|
|
row_sp->GetCFAValue().SetIsRegisterPlusOffset(k_rsp, 8);
|
|
|
|
regloc.SetAtCFAPlusOffset(-8);
|
|
row_sp->SetRegisterInfo(k_rip, regloc);
|
|
|
|
unwind_plan.AppendRow(row_sp);
|
|
|
|
// Allocate a new Row, populate it with the existing Row contents.
|
|
UnwindPlan::Row *new_row = new UnwindPlan::Row;
|
|
*new_row = *row_sp.get();
|
|
row_sp.reset(new_row);
|
|
|
|
// Describe offset 1
|
|
row_sp->SetOffset(1);
|
|
row_sp->GetCFAValue().SetIsRegisterPlusOffset(k_rsp, 16);
|
|
regloc.SetAtCFAPlusOffset(-16);
|
|
row_sp->SetRegisterInfo(k_rbp, regloc);
|
|
unwind_plan.AppendRow(row_sp);
|
|
|
|
// Allocate a new Row, populate it with the existing Row contents.
|
|
new_row = new UnwindPlan::Row;
|
|
*new_row = *row_sp.get();
|
|
row_sp.reset(new_row);
|
|
|
|
// Describe offset 4
|
|
row_sp->SetOffset(4);
|
|
row_sp->GetCFAValue().SetIsRegisterPlusOffset(k_rbp, 16);
|
|
unwind_plan.AppendRow(row_sp);
|
|
|
|
RegisterContextSP reg_ctx_sp;
|
|
EXPECT_TRUE(engine64->AugmentUnwindPlanFromCallSite(
|
|
data, sizeof(data), sample_range, unwind_plan, reg_ctx_sp));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(6);
|
|
EXPECT_EQ(6ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
// x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite
|
|
// doesn't track register restores (pop'ing a reg value back from
|
|
// the stack) - it was just written to make stepping work correctly.
|
|
// Technically we should be able to do the following test, but it
|
|
// won't work today - the unwind plan will still say that the caller's
|
|
// rbp is on the stack.
|
|
// EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbp, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestSimplei386ugmented) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x55, // pushl %ebp
|
|
0x89, 0xe5, // movl %esp, %ebp
|
|
|
|
// x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite
|
|
// has a bug where it can't augment a function that is just
|
|
// prologue+epilogue - it needs at least one other instruction
|
|
// in between.
|
|
0x90, // nop
|
|
|
|
0x5d, // popl %ebp
|
|
0xc3 // retl
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
unwind_plan.SetSourceName("unit testing hand-created unwind plan");
|
|
unwind_plan.SetPlanValidAddressRange(sample_range);
|
|
unwind_plan.SetRegisterKind(eRegisterKindLLDB);
|
|
|
|
row_sp.reset(new UnwindPlan::Row);
|
|
|
|
// Describe offset 0
|
|
row_sp->SetOffset(0);
|
|
row_sp->GetCFAValue().SetIsRegisterPlusOffset(k_esp, 4);
|
|
|
|
regloc.SetAtCFAPlusOffset(-4);
|
|
row_sp->SetRegisterInfo(k_eip, regloc);
|
|
|
|
unwind_plan.AppendRow(row_sp);
|
|
|
|
// Allocate a new Row, populate it with the existing Row contents.
|
|
UnwindPlan::Row *new_row = new UnwindPlan::Row;
|
|
*new_row = *row_sp.get();
|
|
row_sp.reset(new_row);
|
|
|
|
// Describe offset 1
|
|
row_sp->SetOffset(1);
|
|
row_sp->GetCFAValue().SetIsRegisterPlusOffset(k_esp, 8);
|
|
regloc.SetAtCFAPlusOffset(-8);
|
|
row_sp->SetRegisterInfo(k_ebp, regloc);
|
|
unwind_plan.AppendRow(row_sp);
|
|
|
|
// Allocate a new Row, populate it with the existing Row contents.
|
|
new_row = new UnwindPlan::Row;
|
|
*new_row = *row_sp.get();
|
|
row_sp.reset(new_row);
|
|
|
|
// Describe offset 3
|
|
row_sp->SetOffset(3);
|
|
row_sp->GetCFAValue().SetIsRegisterPlusOffset(k_ebp, 8);
|
|
unwind_plan.AppendRow(row_sp);
|
|
|
|
RegisterContextSP reg_ctx_sp;
|
|
EXPECT_TRUE(engine32->AugmentUnwindPlanFromCallSite(
|
|
data, sizeof(data), sample_range, unwind_plan, reg_ctx_sp));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(5);
|
|
EXPECT_EQ(5ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_EQ(4, row_sp->GetCFAValue().GetOffset());
|
|
|
|
// x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite
|
|
// doesn't track register restores (pop'ing a reg value back from
|
|
// the stack) - it was just written to make stepping work correctly.
|
|
// Technically we should be able to do the following test, but it
|
|
// won't work today - the unwind plan will still say that the caller's
|
|
// ebp is on the stack.
|
|
// EXPECT_FALSE(row_sp->GetRegisterInfo(k_ebp, regloc));
|
|
}
|
|
|
|
// Check that the i386 disassembler disassembles past an opcode that
|
|
// is only valid in 32-bit mode (non-long mode), and the x86_64 disassembler
|
|
// stops
|
|
// disassembling at that point (long-mode).
|
|
TEST_F(Testx86AssemblyInspectionEngine, Test32BitOnlyInstruction) {
|
|
UnwindPlan::Row::RegisterLocation regloc;
|
|
UnwindPlan::RowSP row_sp;
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x43, // incl $ebx --- an invalid opcode in 64-bit mode
|
|
0x55, // pushl %ebp
|
|
0x90 // nop
|
|
};
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
EXPECT_EQ(2ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_TRUE(row_sp->GetRegisterInfo(k_ebp, regloc));
|
|
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
|
|
EXPECT_EQ(-8, regloc.GetOffset());
|
|
|
|
unwind_plan.Clear();
|
|
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
row_sp = unwind_plan.GetRowForFunctionOffset(2);
|
|
EXPECT_EQ(0ull, row_sp->GetOffset());
|
|
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
|
|
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
|
|
EXPECT_EQ(8, row_sp->GetCFAValue().GetOffset());
|
|
|
|
EXPECT_FALSE(row_sp->GetRegisterInfo(k_rbp, regloc));
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestStackRealign8BitDisp_i386) {
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x55, // pushl %ebp
|
|
0x89, 0xe5, // movl %esp, %ebp
|
|
0x53, // pushl %ebx
|
|
0x83, 0xe4, 0xf0, // andl $-16, %esp
|
|
0x83, 0xec, 0x10, // subl $16, %esp
|
|
0x8d, 0x65, 0xfc, // leal -4(%ebp), %esp
|
|
0x5b, // popl %ebx
|
|
0x5d, // popl %ebp
|
|
0xc3, // retl
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
UnwindPlan plan(eRegisterKindLLDB);
|
|
ASSERT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(data, sizeof(data),
|
|
sample_range, plan));
|
|
|
|
UnwindPlan::Row::CFAValue esp_plus_4, esp_plus_8, ebp_plus_8;
|
|
esp_plus_4.SetIsRegisterPlusOffset(k_esp, 4);
|
|
esp_plus_8.SetIsRegisterPlusOffset(k_esp, 8);
|
|
ebp_plus_8.SetIsRegisterPlusOffset(k_ebp, 8);
|
|
|
|
EXPECT_EQ(esp_plus_4, plan.GetRowForFunctionOffset(0)->GetCFAValue());
|
|
EXPECT_EQ(esp_plus_8, plan.GetRowForFunctionOffset(1)->GetCFAValue());
|
|
for (size_t i = 3; i < sizeof(data) - 2; ++i)
|
|
EXPECT_EQ(ebp_plus_8, plan.GetRowForFunctionOffset(i)->GetCFAValue())
|
|
<< "i: " << i;
|
|
EXPECT_EQ(esp_plus_4,
|
|
plan.GetRowForFunctionOffset(sizeof(data) - 1)->GetCFAValue());
|
|
}
|
|
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestStackRealign32BitDisp_x86_64) {
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x55, // pushq %rbp
|
|
0x48, 0x89, 0xe5, // movq %rsp, %rbp
|
|
0x53, // pushl %rbx
|
|
0x48, 0x83, 0xe4, 0xf0, // andq $-16, %rsp
|
|
0x48, 0x81, 0xec, 0x00, 0x01, 0x00, 0x00, // subq $256, %rsp
|
|
0x48, 0x8d, 0x65, 0xf8, // leaq -8(%rbp), %rsp
|
|
0x5b, // popq %rbx
|
|
0x5d, // popq %rbp
|
|
0xc3, // retq
|
|
};
|
|
|
|
AddressRange sample_range(0x1000, sizeof(data));
|
|
UnwindPlan plan(eRegisterKindLLDB);
|
|
ASSERT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(data, sizeof(data),
|
|
sample_range, plan));
|
|
|
|
UnwindPlan::Row::CFAValue rsp_plus_8, rsp_plus_16, rbp_plus_16;
|
|
rsp_plus_8.SetIsRegisterPlusOffset(k_rsp, 8);
|
|
rsp_plus_16.SetIsRegisterPlusOffset(k_rsp, 16);
|
|
rbp_plus_16.SetIsRegisterPlusOffset(k_rbp, 16);
|
|
|
|
EXPECT_EQ(rsp_plus_8, plan.GetRowForFunctionOffset(0)->GetCFAValue());
|
|
EXPECT_EQ(rsp_plus_16, plan.GetRowForFunctionOffset(1)->GetCFAValue());
|
|
for (size_t i = 4; i < sizeof(data) - 2; ++i)
|
|
EXPECT_EQ(rbp_plus_16, plan.GetRowForFunctionOffset(i)->GetCFAValue())
|
|
<< "i: " << i;
|
|
EXPECT_EQ(rsp_plus_8,
|
|
plan.GetRowForFunctionOffset(sizeof(data) - 1)->GetCFAValue());
|
|
}
|
|
|
|
// Give the disassembler random bytes to test that it doesn't exceed
|
|
// the bounds of the array when run under clang's address sanitizer.
|
|
TEST_F(Testx86AssemblyInspectionEngine, TestDisassemblyJunkBytes) {
|
|
AddressRange sample_range;
|
|
UnwindPlan unwind_plan(eRegisterKindLLDB);
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
|
|
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
|
|
|
|
uint8_t data[] = {
|
|
0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
|
|
|
|
sample_range = AddressRange(0x1000, sizeof(data));
|
|
|
|
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
unwind_plan.Clear();
|
|
|
|
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
|
|
data, sizeof(data), sample_range, unwind_plan));
|
|
|
|
}
|
|
|