Files
clang-p2996/lldb/unittests/UnwindAssembly/x86/Testx86AssemblyInspectionEngine.cpp
Raphael Isemann 808142876c [lldb][NFC] Fix all formatting errors in .cpp file headers
Summary:
A *.cpp file header in LLDB (and in LLDB) should like this:
```
//===-- TestUtilities.cpp -------------------------------------------------===//
```
However in LLDB most of our source files have arbitrary changes to this format and
these changes are spreading through LLDB as folks usually just use the existing
source files as templates for their new files (most notably the unnecessary
editor language indicator `-*- C++ -*-` is spreading and in every review
someone is pointing out that this is wrong, resulting in people pointing out that this
is done in the same way in other files).

This patch removes most of these inconsistencies including the editor language indicators,
all the different missing/additional '-' characters, files that center the file name, missing
trailing `===//` (mostly caused by clang-format breaking the line).

Reviewers: aprantl, espindola, jfb, shafik, JDevlieghere

Reviewed By: JDevlieghere

Subscribers: dexonsmith, wuzish, emaste, sdardis, nemanjai, kbarton, MaskRay, atanasyan, arphaman, jfb, abidh, jsji, JDevlieghere, usaxena95, lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D73258
2020-01-24 08:52:55 +01:00

2938 lines
104 KiB
C++

//===-- Testx86AssemblyInspectionEngine.cpp -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "gtest/gtest.h"
#include <memory>
#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::FAValue &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, TestPushRBPWithREX) {
UnwindPlan::Row::RegisterLocation regloc;
UnwindPlan::RowSP row_sp;
uint8_t data[] = {
0x40, 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(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_rbp, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-16, 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, TestPopRBPWithREX) {
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[] = {
0x40, 0x55, // pushq %rbp
0x40, 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(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_rbp, 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, TestSpArithx86_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
0x48, 0x81, 0xec, 0x88, 0, 0, 0, // subq $0x88, %rsp
0x90, // nop
0x48, 0x81, 0xc4, 0x88, 0, 0, 0, // addq $0x88, %rsp
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 = std::make_shared<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_rsp, 16);
unwind_plan.AppendRow(row_sp);
RegisterContextSP reg_ctx_sp;
EXPECT_TRUE(engine64->AugmentUnwindPlanFromCallSite(
data, sizeof(data), sample_range, unwind_plan, reg_ctx_sp));
// Before we touch the stack pointer, we should still refer to the
// row from after the prologue.
row_sp = unwind_plan.GetRowForFunctionOffset(5);
EXPECT_EQ(4ull, row_sp->GetOffset());
// Check the first stack pointer update.
row_sp = unwind_plan.GetRowForFunctionOffset(12);
EXPECT_EQ(12ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
EXPECT_EQ(152, row_sp->GetCFAValue().GetOffset());
// After the nop, we should still refer to the same row.
row_sp = unwind_plan.GetRowForFunctionOffset(13);
EXPECT_EQ(12ull, row_sp->GetOffset());
// Check that the second stack pointer update is reflected in the
// unwind plan.
row_sp = unwind_plan.GetRowForFunctionOffset(20);
EXPECT_EQ(20ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
EXPECT_EQ(16, row_sp->GetCFAValue().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 = std::make_shared<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 = std::make_shared<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::FAValue 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::FAValue 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());
}
TEST_F(Testx86AssemblyInspectionEngine, TestStackRealignMSVC_i386) {
std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector();
uint8_t data[] = {
0x53, // offset 00 -- pushl %ebx
0x8b, 0xdc, // offset 01 -- movl %esp, %ebx
0x83, 0xec, 0x08, // offset 03 -- subl $8, %esp
0x81, 0xe4, 0x00, 0xff, 0xff, 0xff, // offset 06 -- andl $-256, %esp
0x83, 0xc4, 0x04, // offset 12 -- addl $4, %esp
0x55, // offset 15 -- pushl %ebp
0x8b, 0xec, // offset 16 -- movl %esp, %ebp
0x81, 0xec, 0x00, 0x02, 0x00, 0x00, // offset 18 -- subl $512, %esp
0x89, 0x7d, 0xfc, // offset 24 -- movl %edi, -4(%ebp)
0x8b, 0xe5, // offset 27 -- movl %ebp, %esp
0x5d, // offset 29 -- popl %ebp
0x8b, 0xe3, // offset 30 -- movl %ebx, %esp
0x5b, // offset 32 -- popl %ebx
0xc3 // offset 33 -- retl
};
AddressRange sample_range(0x1000, sizeof(data));
UnwindPlan plan(eRegisterKindLLDB);
ASSERT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(data, sizeof(data),
sample_range, plan));
UnwindPlan::Row::FAValue esp_minus_4, esp_plus_0, esp_plus_4, esp_plus_8,
ebx_plus_8, ebp_plus_0;
esp_minus_4.SetIsRegisterPlusOffset(k_esp, -4);
esp_plus_0.SetIsRegisterPlusOffset(k_esp, 0);
esp_plus_4.SetIsRegisterPlusOffset(k_esp, 4);
esp_plus_8.SetIsRegisterPlusOffset(k_esp, 8);
ebx_plus_8.SetIsRegisterPlusOffset(k_ebx, 8);
ebp_plus_0.SetIsRegisterPlusOffset(k_ebp, 0);
// Test CFA
EXPECT_EQ(esp_plus_4, plan.GetRowForFunctionOffset(0)->GetCFAValue());
EXPECT_EQ(esp_plus_8, plan.GetRowForFunctionOffset(1)->GetCFAValue());
for (size_t i = 3; i < 33; ++i)
EXPECT_EQ(ebx_plus_8, plan.GetRowForFunctionOffset(i)->GetCFAValue())
<< "i: " << i;
EXPECT_EQ(esp_plus_4, plan.GetRowForFunctionOffset(33)->GetCFAValue());
// Test AFA
EXPECT_EQ(esp_plus_0, plan.GetRowForFunctionOffset(12)->GetAFAValue());
EXPECT_EQ(esp_minus_4, plan.GetRowForFunctionOffset(15)->GetAFAValue());
EXPECT_EQ(esp_plus_0, plan.GetRowForFunctionOffset(16)->GetAFAValue());
for (size_t i = 18; i < 30; ++i)
EXPECT_EQ(ebp_plus_0, plan.GetRowForFunctionOffset(i)->GetAFAValue())
<< "i: " << i;
EXPECT_EQ(esp_minus_4, plan.GetRowForFunctionOffset(30)->GetAFAValue());
// Test saved register
UnwindPlan::Row::RegisterLocation reg_loc;
EXPECT_TRUE(
plan.GetRowForFunctionOffset(27)->GetRegisterInfo(k_edi, reg_loc));
EXPECT_TRUE(reg_loc.IsAtAFAPlusOffset());
EXPECT_EQ(-4, reg_loc.GetOffset());
}
// 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));
}
TEST_F(Testx86AssemblyInspectionEngine, TestReturnDetect) {
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
// Single fragment with all four return forms.
// We want to verify that the unwind state is reset after each ret.
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
0x31, 0xc0, // offset 8 -- xorl %eax, %eax
0x5d, // offset 10 -- popq %rbp
0xcb, // offset 11 -- retf
0x31, 0xc0, // offset 12 -- xorl %eax, %eax
0x5d, // offset 14 -- popq %rbp
0xc2, 0x22, 0x11, // offset 15 -- retq 0x1122
0x31, 0xc0, // offset 18 -- xorl %eax, %eax
0x5d, // offset 20 -- popq %rbp
0xca, 0x44, 0x33, // offset 21 -- retf 0x3344
0x31, 0xc0, // offset 24 -- xorl %eax, %eax
};
AddressRange sample_range(0x1000, sizeof(data));
UnwindPlan unwind_plan(eRegisterKindLLDB);
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
data, sizeof(data), sample_range, unwind_plan));
// Expect following 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]
// 8: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
// 11: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
// 12: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
// 15: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
// 18: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
// 21: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
// 24: CFA=rbp+16 => rbp=[CFA-16] 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());
// 8: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(8);
EXPECT_EQ(8ull, 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());
// 11: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(11);
EXPECT_EQ(11ull, 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());
// 12: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(12);
EXPECT_EQ(12ull, 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());
// 15: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(15);
EXPECT_EQ(15ull, 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());
// 18: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(18);
EXPECT_EQ(18ull, 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());
// 21: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(21);
EXPECT_EQ(21ull, 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());
// 24: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(24);
EXPECT_EQ(24ull, 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());
}
// Test mid-function epilogues - the unwind state post-prologue
// should be re-instated.
TEST_F(Testx86AssemblyInspectionEngine, TestDisassemblyMidFunctionEpilogues) {
AddressRange sample_range;
UnwindPlan unwind_plan(eRegisterKindLLDB);
std::unique_ptr<x86AssemblyInspectionEngine> engine32 = Geti386Inspector();
std::unique_ptr<x86AssemblyInspectionEngine> engine64 = Getx86_64Inspector();
uint8_t data[] = {
0x55, // <+0>: pushq %rbp
0x48, 0x89, 0xe5, // <+1>: movq %rsp, %rbp
0x48, 0x83, 0xec, 0x70, // <+4>: subq $0x70, %rsp
0x90, // <+8>: nop // prologue set up
0x74, 0x7, // <+9>: je 7 <+18>
0x48, 0x83, 0xc4, 0x70, // <+11>: addq $0x70, %rsp
0x5d, // <+15>: popq %rbp
0xff, 0xe0, // <+16>: jmpq *%rax // epilogue completed
0x90, // <+18>: nop // prologue setup back
0x74, 0x7, // <+19>: je 6 <+27>
0x48, 0x83, 0xc4, 0x70, // <+21>: addq $0x70, %rsp
0x5d, // <+25>: popq %rbp
0xc3, // <+26>: retq // epilogue completed
0x90, // <+27>: nop // prologue setup back
0x48, 0x83, 0xc4, 0x70, // <+28>: addq $0x70, %rsp
0x5d, // <+32>: popq %rbp
0xc3, // <+33>: retq // epilogue completed
};
sample_range = AddressRange(0x1000, sizeof(data));
int wordsize = 4;
EXPECT_TRUE(engine32->GetNonCallSiteUnwindPlanFromAssembly(
data, sizeof(data), sample_range, unwind_plan));
// Check that we've unwound the stack after the first mid-function epilogue
// row: CFA=esp +4 => esp=CFA+0 eip=[CFA-4]
UnwindPlan::RowSP row_sp = unwind_plan.GetRowForFunctionOffset(16);
EXPECT_EQ(16ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(wordsize, row_sp->GetCFAValue().GetOffset());
// Check that we've reinstated the stack frame setup
// unwind instructions after a jmpq *%eax
// row: CFA=ebp +8 => esp=CFA+0 eip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(18);
EXPECT_EQ(18ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_ebp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(wordsize * 2, row_sp->GetCFAValue().GetOffset());
// Check that we've reinstated the stack frame setup
// unwind instructions after a mid-function retq
// row: CFA=ebp +8 => esp=CFA+0 eip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(27);
EXPECT_EQ(27ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_ebp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(wordsize * 2, row_sp->GetCFAValue().GetOffset());
// After last instruction in the function, verify that
// the stack frame has been unwound
// row: CFA=esp +4 => esp=CFA+0 eip=[CFA-4]
row_sp = unwind_plan.GetRowForFunctionOffset(33);
EXPECT_EQ(33ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_esp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(wordsize, row_sp->GetCFAValue().GetOffset());
unwind_plan.Clear();
wordsize = 8;
EXPECT_TRUE(engine64->GetNonCallSiteUnwindPlanFromAssembly(
data, sizeof(data), sample_range, unwind_plan));
// Check that we've unwound the stack after the first mid-function epilogue
// row: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(16);
EXPECT_EQ(16ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(wordsize, row_sp->GetCFAValue().GetOffset());
// Check that we've reinstated the stack frame setup
// unwind instructions after a jmpq *%eax
// row: CFA=rbp+16 => rsp=CFA+0 rip=[CFA-16]
row_sp = unwind_plan.GetRowForFunctionOffset(18);
EXPECT_EQ(18ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(wordsize * 2, row_sp->GetCFAValue().GetOffset());
// Check that we've reinstated the stack frame setup
// unwind instructions after a mid-function retq
// row: CFA=rbp+16 => rsp=CFA+0 rip=[CFA-16]
row_sp = unwind_plan.GetRowForFunctionOffset(27);
EXPECT_EQ(27ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rbp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(wordsize * 2, row_sp->GetCFAValue().GetOffset());
// After last instruction in the function, verify that
// the stack frame has been unwound
// row: CFA=rsp +8 => esp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(33);
EXPECT_EQ(33ull, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(wordsize, row_sp->GetCFAValue().GetOffset());
}