Merge "Add signal handling to the register object." am: 33913ebfb5
am: b945cc6de0
am: 2f80aa506f
Change-Id: I0df6230ffb131aa0537802e00192ce9154963d02
This commit is contained in:
commit
9f7c071743
8 changed files with 544 additions and 81 deletions
|
@ -96,7 +96,8 @@ bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offse
|
|||
}
|
||||
|
||||
bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
|
||||
return valid_ && (interface_->Step(rel_pc, regs, process_memory) ||
|
||||
return valid_ && (regs->StepIfSignalHandler(process_memory) ||
|
||||
interface_->Step(rel_pc, regs, process_memory) ||
|
||||
(gnu_debugdata_interface_ &&
|
||||
gnu_debugdata_interface_->Step(rel_pc, regs, process_memory)));
|
||||
}
|
||||
|
@ -147,21 +148,22 @@ ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
|
|||
machine_type_ = e_machine;
|
||||
if (e_machine == EM_ARM) {
|
||||
interface.reset(new ElfInterfaceArm(memory));
|
||||
} else {
|
||||
} else if (e_machine == EM_386) {
|
||||
interface.reset(new ElfInterface32(memory));
|
||||
} else {
|
||||
ALOGI("32 bit elf that is neither arm nor x86: e_machine = %d\n", e_machine);
|
||||
return nullptr;
|
||||
}
|
||||
} else if (class_type_ == ELFCLASS64) {
|
||||
Elf64_Half e_machine;
|
||||
if (!memory->Read(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (e_machine != EM_AARCH64 && e_machine != EM_X86_64) {
|
||||
// Unsupported.
|
||||
ALOGI("64 bit elf that is neither aarch64 nor x86_64: e_machine = %d\n", e_machine);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
machine_type_ = e_machine;
|
||||
interface.reset(new ElfInterface64(memory));
|
||||
}
|
||||
|
|
|
@ -258,49 +258,51 @@ static Regs* CreateFromArm64Ucontext(void* ucontext) {
|
|||
return regs;
|
||||
}
|
||||
|
||||
void RegsX86::SetFromUcontext(x86_ucontext_t* ucontext) {
|
||||
// Put the registers in the expected order.
|
||||
regs_[X86_REG_EDI] = ucontext->uc_mcontext.edi;
|
||||
regs_[X86_REG_ESI] = ucontext->uc_mcontext.esi;
|
||||
regs_[X86_REG_EBP] = ucontext->uc_mcontext.ebp;
|
||||
regs_[X86_REG_ESP] = ucontext->uc_mcontext.esp;
|
||||
regs_[X86_REG_EBX] = ucontext->uc_mcontext.ebx;
|
||||
regs_[X86_REG_EDX] = ucontext->uc_mcontext.edx;
|
||||
regs_[X86_REG_ECX] = ucontext->uc_mcontext.ecx;
|
||||
regs_[X86_REG_EAX] = ucontext->uc_mcontext.eax;
|
||||
regs_[X86_REG_EIP] = ucontext->uc_mcontext.eip;
|
||||
SetFromRaw();
|
||||
}
|
||||
|
||||
static Regs* CreateFromX86Ucontext(void* ucontext) {
|
||||
x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext);
|
||||
|
||||
RegsX86* regs = new RegsX86();
|
||||
// Put the registers in the expected order.
|
||||
(*regs)[X86_REG_GS] = x86_ucontext->uc_mcontext.gs;
|
||||
(*regs)[X86_REG_FS] = x86_ucontext->uc_mcontext.fs;
|
||||
(*regs)[X86_REG_ES] = x86_ucontext->uc_mcontext.es;
|
||||
(*regs)[X86_REG_DS] = x86_ucontext->uc_mcontext.ds;
|
||||
(*regs)[X86_REG_EDI] = x86_ucontext->uc_mcontext.edi;
|
||||
(*regs)[X86_REG_ESI] = x86_ucontext->uc_mcontext.esi;
|
||||
(*regs)[X86_REG_EBP] = x86_ucontext->uc_mcontext.ebp;
|
||||
(*regs)[X86_REG_ESP] = x86_ucontext->uc_mcontext.esp;
|
||||
(*regs)[X86_REG_EBX] = x86_ucontext->uc_mcontext.ebx;
|
||||
(*regs)[X86_REG_EDX] = x86_ucontext->uc_mcontext.edx;
|
||||
(*regs)[X86_REG_ECX] = x86_ucontext->uc_mcontext.ecx;
|
||||
(*regs)[X86_REG_EAX] = x86_ucontext->uc_mcontext.eax;
|
||||
(*regs)[X86_REG_EIP] = x86_ucontext->uc_mcontext.eip;
|
||||
regs->SetFromRaw();
|
||||
regs->SetFromUcontext(x86_ucontext);
|
||||
return regs;
|
||||
}
|
||||
|
||||
void RegsX86_64::SetFromUcontext(x86_64_ucontext_t* ucontext) {
|
||||
// R8-R15
|
||||
memcpy(®s_[X86_64_REG_R8], &ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
|
||||
|
||||
// Rest of the registers.
|
||||
regs_[X86_64_REG_RDI] = ucontext->uc_mcontext.rdi;
|
||||
regs_[X86_64_REG_RSI] = ucontext->uc_mcontext.rsi;
|
||||
regs_[X86_64_REG_RBP] = ucontext->uc_mcontext.rbp;
|
||||
regs_[X86_64_REG_RBX] = ucontext->uc_mcontext.rbx;
|
||||
regs_[X86_64_REG_RDX] = ucontext->uc_mcontext.rdx;
|
||||
regs_[X86_64_REG_RAX] = ucontext->uc_mcontext.rax;
|
||||
regs_[X86_64_REG_RCX] = ucontext->uc_mcontext.rcx;
|
||||
regs_[X86_64_REG_RSP] = ucontext->uc_mcontext.rsp;
|
||||
regs_[X86_64_REG_RIP] = ucontext->uc_mcontext.rip;
|
||||
|
||||
SetFromRaw();
|
||||
}
|
||||
|
||||
static Regs* CreateFromX86_64Ucontext(void* ucontext) {
|
||||
x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext);
|
||||
|
||||
RegsX86_64* regs = new RegsX86_64();
|
||||
// Put the registers in the expected order.
|
||||
|
||||
// R8-R15
|
||||
memcpy(&(*regs)[X86_64_REG_R8], &x86_64_ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
|
||||
|
||||
// Rest of the registers.
|
||||
(*regs)[X86_64_REG_RDI] = x86_64_ucontext->uc_mcontext.rdi;
|
||||
(*regs)[X86_64_REG_RSI] = x86_64_ucontext->uc_mcontext.rsi;
|
||||
(*regs)[X86_64_REG_RBP] = x86_64_ucontext->uc_mcontext.rbp;
|
||||
(*regs)[X86_64_REG_RBX] = x86_64_ucontext->uc_mcontext.rbx;
|
||||
(*regs)[X86_64_REG_RDX] = x86_64_ucontext->uc_mcontext.rdx;
|
||||
(*regs)[X86_64_REG_RAX] = x86_64_ucontext->uc_mcontext.rax;
|
||||
(*regs)[X86_64_REG_RCX] = x86_64_ucontext->uc_mcontext.rcx;
|
||||
(*regs)[X86_64_REG_RSP] = x86_64_ucontext->uc_mcontext.rsp;
|
||||
(*regs)[X86_64_REG_RIP] = x86_64_ucontext->uc_mcontext.rip;
|
||||
|
||||
regs->SetFromRaw();
|
||||
regs->SetFromUcontext(x86_64_ucontext);
|
||||
return regs;
|
||||
}
|
||||
|
||||
|
@ -348,4 +350,178 @@ Regs* Regs::CreateFromLocal() {
|
|||
return regs;
|
||||
}
|
||||
|
||||
bool RegsArm::StepIfSignalHandler(Memory* memory) {
|
||||
uint32_t data;
|
||||
if (!memory->Read(pc(), &data, sizeof(data))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t offset = 0;
|
||||
if (data == 0xe3a07077 || data == 0xef900077 || data == 0xdf002777) {
|
||||
// non-RT sigreturn call.
|
||||
// __restore:
|
||||
//
|
||||
// Form 1 (arm):
|
||||
// 0x77 0x70 mov r7, #0x77
|
||||
// 0xa0 0xe3 svc 0x00000000
|
||||
//
|
||||
// Form 2 (arm):
|
||||
// 0x77 0x00 0x90 0xef svc 0x00900077
|
||||
//
|
||||
// Form 3 (thumb):
|
||||
// 0x77 0x27 movs r7, #77
|
||||
// 0x00 0xdf svc 0
|
||||
if (!memory->Read(sp(), &data, sizeof(data))) {
|
||||
return false;
|
||||
}
|
||||
if (data == 0x5ac3c35a) {
|
||||
// SP + uc_mcontext offset + r0 offset.
|
||||
offset = sp() + 0x14 + 0xc;
|
||||
} else {
|
||||
// SP + r0 offset
|
||||
offset = sp() + 0xc;
|
||||
}
|
||||
} else if (data == 0xe3a070ad || data == 0xef9000ad || data == 0xdf0027ad) {
|
||||
// RT sigreturn call.
|
||||
// __restore_rt:
|
||||
//
|
||||
// Form 1 (arm):
|
||||
// 0xad 0x70 mov r7, #0xad
|
||||
// 0xa0 0xe3 svc 0x00000000
|
||||
//
|
||||
// Form 2 (arm):
|
||||
// 0xad 0x00 0x90 0xef svc 0x009000ad
|
||||
//
|
||||
// Form 3 (thumb):
|
||||
// 0xad 0x27 movs r7, #ad
|
||||
// 0x00 0xdf svc 0
|
||||
if (!memory->Read(sp(), &data, sizeof(data))) {
|
||||
return false;
|
||||
}
|
||||
if (data == sp() + 8) {
|
||||
// SP + 8 + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
|
||||
offset = sp() + 8 + 0x80 + 0x14 + 0xc;
|
||||
} else {
|
||||
// SP + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
|
||||
offset = sp() + 0x80 + 0x14 + 0xc;
|
||||
}
|
||||
}
|
||||
if (offset == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!memory->Read(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) {
|
||||
return false;
|
||||
}
|
||||
SetFromRaw();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RegsArm64::StepIfSignalHandler(Memory* memory) {
|
||||
uint64_t data;
|
||||
if (!memory->Read(pc(), &data, sizeof(data))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look for the kernel sigreturn function.
|
||||
// __kernel_rt_sigreturn:
|
||||
// 0xd2801168 mov x8, #0x8b
|
||||
// 0xd4000001 svc #0x0
|
||||
if (data != 0xd4000001d2801168ULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset.
|
||||
if (!memory->Read(sp() + 0x80 + 0xb0 + 0x08, regs_.data(), sizeof(uint64_t) * ARM64_REG_LAST)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SetFromRaw();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RegsX86::StepIfSignalHandler(Memory* memory) {
|
||||
uint64_t data;
|
||||
if (!memory->Read(pc(), &data, sizeof(data))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data == 0x80cd00000077b858ULL) {
|
||||
// Without SA_SIGINFO set, the return sequence is:
|
||||
//
|
||||
// __restore:
|
||||
// 0x58 pop %eax
|
||||
// 0xb8 0x77 0x00 0x00 0x00 movl 0x77,%eax
|
||||
// 0xcd 0x80 int 0x80
|
||||
//
|
||||
// SP points at arguments:
|
||||
// int signum
|
||||
// struct sigcontext (same format as mcontext)
|
||||
struct x86_mcontext_t context;
|
||||
if (!memory->Read(sp() + 4, &context, sizeof(context))) {
|
||||
return false;
|
||||
}
|
||||
regs_[X86_REG_EBP] = context.ebp;
|
||||
regs_[X86_REG_ESP] = context.esp;
|
||||
regs_[X86_REG_EBX] = context.ebx;
|
||||
regs_[X86_REG_EDX] = context.edx;
|
||||
regs_[X86_REG_ECX] = context.ecx;
|
||||
regs_[X86_REG_EAX] = context.eax;
|
||||
regs_[X86_REG_EIP] = context.eip;
|
||||
SetFromRaw();
|
||||
return true;
|
||||
} else if ((data & 0x00ffffffffffffffULL) == 0x0080cd000000adb8ULL) {
|
||||
// With SA_SIGINFO set, the return sequence is:
|
||||
//
|
||||
// __restore_rt:
|
||||
// 0xb8 0xad 0x00 0x00 0x00 movl 0xad,%eax
|
||||
// 0xcd 0x80 int 0x80
|
||||
//
|
||||
// SP points at arguments:
|
||||
// int signum
|
||||
// siginfo*
|
||||
// ucontext*
|
||||
|
||||
// Get the location of the sigcontext data.
|
||||
uint32_t ptr;
|
||||
if (!memory->Read(sp() + 8, &ptr, sizeof(ptr))) {
|
||||
return false;
|
||||
}
|
||||
// Only read the portion of the data structure we care about.
|
||||
x86_ucontext_t x86_ucontext;
|
||||
if (!memory->Read(ptr + 0x14, &x86_ucontext.uc_mcontext, sizeof(x86_mcontext_t))) {
|
||||
return false;
|
||||
}
|
||||
SetFromUcontext(&x86_ucontext);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RegsX86_64::StepIfSignalHandler(Memory* memory) {
|
||||
uint64_t data;
|
||||
if (!memory->Read(pc(), &data, sizeof(data)) || data != 0x0f0000000fc0c748) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t data2;
|
||||
if (!memory->Read(pc() + 8, &data2, sizeof(data2)) || data2 != 0x0f05) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// __restore_rt:
|
||||
// 0x48 0xc7 0xc0 0x0f 0x00 0x00 0x00 mov $0xf,%rax
|
||||
// 0x0f 0x05 syscall
|
||||
// 0x0f nopl 0x0($rax)
|
||||
|
||||
// Read the mcontext data from the stack.
|
||||
// sp points to the ucontext data structure, read only the mcontext part.
|
||||
x86_64_ucontext_t x86_64_ucontext;
|
||||
if (!memory->Read(sp() + 0x28, &x86_64_ucontext.uc_mcontext, sizeof(x86_64_mcontext_t))) {
|
||||
return false;
|
||||
}
|
||||
SetFromUcontext(&x86_64_ucontext);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -170,13 +170,13 @@ struct x86_64_mcontext_t {
|
|||
// Only care about the registers, skip everything else.
|
||||
};
|
||||
|
||||
typedef struct x86_64_ucontext {
|
||||
struct x86_64_ucontext_t {
|
||||
uint64_t uc_flags; // unsigned long
|
||||
uint64_t uc_link; // struct ucontext*
|
||||
x86_64_stack_t uc_stack;
|
||||
x86_64_mcontext_t uc_mcontext;
|
||||
// Nothing else is used, so don't define it.
|
||||
} x86_64_ucontext_t;
|
||||
};
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace unwindstack {
|
|||
class Elf;
|
||||
struct MapInfo;
|
||||
class Memory;
|
||||
struct x86_ucontext_t;
|
||||
struct x86_64_ucontext_t;
|
||||
|
||||
class Regs {
|
||||
public:
|
||||
|
@ -55,6 +57,8 @@ class Regs {
|
|||
|
||||
virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
|
||||
|
||||
virtual bool StepIfSignalHandler(Memory*) = 0;
|
||||
|
||||
virtual void SetFromRaw() = 0;
|
||||
|
||||
uint16_t sp_reg() { return sp_reg_; }
|
||||
|
@ -104,6 +108,8 @@ class RegsArm : public RegsImpl<uint32_t> {
|
|||
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
|
||||
|
||||
void SetFromRaw() override;
|
||||
|
||||
bool StepIfSignalHandler(Memory* memory) override;
|
||||
};
|
||||
|
||||
class RegsArm64 : public RegsImpl<uint64_t> {
|
||||
|
@ -114,6 +120,8 @@ class RegsArm64 : public RegsImpl<uint64_t> {
|
|||
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
|
||||
|
||||
void SetFromRaw() override;
|
||||
|
||||
bool StepIfSignalHandler(Memory* memory) override;
|
||||
};
|
||||
|
||||
class RegsX86 : public RegsImpl<uint32_t> {
|
||||
|
@ -124,6 +132,10 @@ class RegsX86 : public RegsImpl<uint32_t> {
|
|||
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
|
||||
|
||||
void SetFromRaw() override;
|
||||
|
||||
bool StepIfSignalHandler(Memory* memory) override;
|
||||
|
||||
void SetFromUcontext(x86_ucontext_t* ucontext);
|
||||
};
|
||||
|
||||
class RegsX86_64 : public RegsImpl<uint64_t> {
|
||||
|
@ -134,6 +146,10 @@ class RegsX86_64 : public RegsImpl<uint64_t> {
|
|||
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
|
||||
|
||||
void SetFromRaw() override;
|
||||
|
||||
bool StepIfSignalHandler(Memory* memory) override;
|
||||
|
||||
void SetFromUcontext(x86_64_ucontext_t* ucontext);
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <unwindstack/MapInfo.h>
|
||||
|
||||
#include "ElfTestUtils.h"
|
||||
#include "LogFake.h"
|
||||
#include "MemoryFake.h"
|
||||
|
||||
#if !defined(PT_ARM_EXIDX)
|
||||
|
@ -131,6 +132,32 @@ TEST_F(ElfTest, elf_invalid) {
|
|||
ASSERT_FALSE(elf.Step(0, nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, elf32_invalid_machine) {
|
||||
Elf elf(memory_);
|
||||
|
||||
InitElf32(EM_PPC);
|
||||
|
||||
ResetLogs();
|
||||
ASSERT_FALSE(elf.Init());
|
||||
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86: e_machine = 20\n\n",
|
||||
GetFakeLogPrint());
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, elf64_invalid_machine) {
|
||||
Elf elf(memory_);
|
||||
|
||||
InitElf64(EM_PPC64);
|
||||
|
||||
ResetLogs();
|
||||
ASSERT_FALSE(elf.Init());
|
||||
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64: e_machine = 21\n\n",
|
||||
GetFakeLogPrint());
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, elf_arm) {
|
||||
Elf elf(memory_);
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ class RegsFake : public RegsImpl<TypeParam> {
|
|||
|
||||
uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
|
||||
void SetFromRaw() override {}
|
||||
bool StepIfSignalHandler(Memory*) override { return false; }
|
||||
bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
|
||||
};
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
|
||||
#include "Machine.h"
|
||||
|
||||
#include "MemoryFake.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
@ -60,6 +62,7 @@ class RegsTestImpl : public RegsImpl<TypeParam> {
|
|||
|
||||
uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
|
||||
void SetFromRaw() override {}
|
||||
bool StepIfSignalHandler(Memory*) override { return false; }
|
||||
};
|
||||
|
||||
class RegsTest : public ::testing::Test {
|
||||
|
@ -72,7 +75,10 @@ class RegsTest : public ::testing::Test {
|
|||
}
|
||||
|
||||
template <typename AddressType>
|
||||
void regs_return_address_register();
|
||||
void RegsReturnAddressRegister();
|
||||
|
||||
void ArmStepIfSignalHandlerNonRt(uint32_t pc_data);
|
||||
void ArmStepIfSignalHandlerRt(uint32_t pc_data);
|
||||
|
||||
ElfInterfaceFake* elf_interface_;
|
||||
MemoryFake* memory_;
|
||||
|
@ -126,7 +132,7 @@ TEST_F(RegsTest, regs64) {
|
|||
}
|
||||
|
||||
template <typename AddressType>
|
||||
void RegsTest::regs_return_address_register() {
|
||||
void RegsTest::RegsReturnAddressRegister() {
|
||||
RegsTestImpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
|
||||
|
||||
regs[5] = 0x12345;
|
||||
|
@ -136,11 +142,11 @@ void RegsTest::regs_return_address_register() {
|
|||
}
|
||||
|
||||
TEST_F(RegsTest, regs32_return_address_register) {
|
||||
regs_return_address_register<uint32_t>();
|
||||
RegsReturnAddressRegister<uint32_t>();
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, regs64_return_address_register) {
|
||||
regs_return_address_register<uint64_t>();
|
||||
RegsReturnAddressRegister<uint64_t>();
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, regs32_return_address_sp_offset) {
|
||||
|
@ -285,4 +291,160 @@ TEST_F(RegsTest, x86_64_set_from_raw) {
|
|||
EXPECT_EQ(0x4900000000U, x86_64.pc());
|
||||
}
|
||||
|
||||
void RegsTest::ArmStepIfSignalHandlerNonRt(uint32_t pc_data) {
|
||||
uint64_t addr = 0x1000;
|
||||
RegsArm regs;
|
||||
regs[ARM_REG_PC] = 0x5000;
|
||||
regs[ARM_REG_SP] = addr;
|
||||
regs.SetFromRaw();
|
||||
|
||||
memory_->SetData32(0x5000, pc_data);
|
||||
|
||||
for (uint64_t index = 0; index <= 30; index++) {
|
||||
memory_->SetData32(addr + index * 4, index * 0x10);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(regs.StepIfSignalHandler(memory_));
|
||||
EXPECT_EQ(0x100U, regs[ARM_REG_SP]);
|
||||
EXPECT_EQ(0x120U, regs[ARM_REG_PC]);
|
||||
EXPECT_EQ(0x100U, regs.sp());
|
||||
EXPECT_EQ(0x120U, regs.pc());
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, arm_step_if_signal_handler_non_rt) {
|
||||
// Form 1
|
||||
ArmStepIfSignalHandlerNonRt(0xe3a07077);
|
||||
|
||||
// Form 2
|
||||
ArmStepIfSignalHandlerNonRt(0xef900077);
|
||||
|
||||
// Form 3
|
||||
ArmStepIfSignalHandlerNonRt(0xdf002777);
|
||||
}
|
||||
|
||||
void RegsTest::ArmStepIfSignalHandlerRt(uint32_t pc_data) {
|
||||
uint64_t addr = 0x1000;
|
||||
RegsArm regs;
|
||||
regs[ARM_REG_PC] = 0x5000;
|
||||
regs[ARM_REG_SP] = addr;
|
||||
regs.SetFromRaw();
|
||||
|
||||
memory_->SetData32(0x5000, pc_data);
|
||||
|
||||
for (uint64_t index = 0; index <= 100; index++) {
|
||||
memory_->SetData32(addr + index * 4, index * 0x10);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(regs.StepIfSignalHandler(memory_));
|
||||
EXPECT_EQ(0x350U, regs[ARM_REG_SP]);
|
||||
EXPECT_EQ(0x370U, regs[ARM_REG_PC]);
|
||||
EXPECT_EQ(0x350U, regs.sp());
|
||||
EXPECT_EQ(0x370U, regs.pc());
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, arm_step_if_signal_handler_rt) {
|
||||
// Form 1
|
||||
ArmStepIfSignalHandlerRt(0xe3a070ad);
|
||||
|
||||
// Form 2
|
||||
ArmStepIfSignalHandlerRt(0xef9000ad);
|
||||
|
||||
// Form 3
|
||||
ArmStepIfSignalHandlerRt(0xdf0027ad);
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, arm64_step_if_signal_handler) {
|
||||
uint64_t addr = 0x1000;
|
||||
RegsArm64 regs;
|
||||
regs[ARM64_REG_PC] = 0x8000;
|
||||
regs[ARM64_REG_SP] = addr;
|
||||
regs.SetFromRaw();
|
||||
|
||||
memory_->SetData64(0x8000, 0xd4000001d2801168ULL);
|
||||
|
||||
for (uint64_t index = 0; index <= 100; index++) {
|
||||
memory_->SetData64(addr + index * 8, index * 0x10);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(regs.StepIfSignalHandler(memory_));
|
||||
EXPECT_EQ(0x460U, regs[ARM64_REG_SP]);
|
||||
EXPECT_EQ(0x470U, regs[ARM64_REG_PC]);
|
||||
EXPECT_EQ(0x460U, regs.sp());
|
||||
EXPECT_EQ(0x470U, regs.pc());
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, x86_step_if_signal_handler_no_siginfo) {
|
||||
uint64_t addr = 0xa00;
|
||||
RegsX86 regs;
|
||||
regs[X86_REG_EIP] = 0x4100;
|
||||
regs[X86_REG_ESP] = addr;
|
||||
regs.SetFromRaw();
|
||||
|
||||
memory_->SetData64(0x4100, 0x80cd00000077b858ULL);
|
||||
for (uint64_t index = 0; index <= 25; index++) {
|
||||
memory_->SetData32(addr + index * 4, index * 0x10);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(regs.StepIfSignalHandler(memory_));
|
||||
EXPECT_EQ(0x70U, regs[X86_REG_EBP]);
|
||||
EXPECT_EQ(0x80U, regs[X86_REG_ESP]);
|
||||
EXPECT_EQ(0x90U, regs[X86_REG_EBX]);
|
||||
EXPECT_EQ(0xa0U, regs[X86_REG_EDX]);
|
||||
EXPECT_EQ(0xb0U, regs[X86_REG_ECX]);
|
||||
EXPECT_EQ(0xc0U, regs[X86_REG_EAX]);
|
||||
EXPECT_EQ(0xf0U, regs[X86_REG_EIP]);
|
||||
EXPECT_EQ(0x80U, regs.sp());
|
||||
EXPECT_EQ(0xf0U, regs.pc());
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, x86_step_if_signal_handler_siginfo) {
|
||||
uint64_t addr = 0xa00;
|
||||
RegsX86 regs;
|
||||
regs[X86_REG_EIP] = 0x4100;
|
||||
regs[X86_REG_ESP] = addr;
|
||||
regs.SetFromRaw();
|
||||
|
||||
memory_->SetData64(0x4100, 0x0080cd000000adb8ULL);
|
||||
addr += 8;
|
||||
// Pointer to ucontext data.
|
||||
memory_->SetData32(addr, 0x8100);
|
||||
|
||||
addr = 0x8100;
|
||||
for (uint64_t index = 0; index <= 30; index++) {
|
||||
memory_->SetData32(addr + index * 4, index * 0x10);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(regs.StepIfSignalHandler(memory_));
|
||||
EXPECT_EQ(0xb0U, regs[X86_REG_EBP]);
|
||||
EXPECT_EQ(0xc0U, regs[X86_REG_ESP]);
|
||||
EXPECT_EQ(0xd0U, regs[X86_REG_EBX]);
|
||||
EXPECT_EQ(0xe0U, regs[X86_REG_EDX]);
|
||||
EXPECT_EQ(0xf0U, regs[X86_REG_ECX]);
|
||||
EXPECT_EQ(0x100U, regs[X86_REG_EAX]);
|
||||
EXPECT_EQ(0x130U, regs[X86_REG_EIP]);
|
||||
EXPECT_EQ(0xc0U, regs.sp());
|
||||
EXPECT_EQ(0x130U, regs.pc());
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, x86_64_step_if_signal_handler) {
|
||||
uint64_t addr = 0x500;
|
||||
RegsX86_64 regs;
|
||||
regs[X86_64_REG_RIP] = 0x7000;
|
||||
regs[X86_64_REG_RSP] = addr;
|
||||
regs.SetFromRaw();
|
||||
|
||||
memory_->SetData64(0x7000, 0x0f0000000fc0c748);
|
||||
memory_->SetData16(0x7008, 0x0f05);
|
||||
|
||||
for (uint64_t index = 0; index <= 30; index++) {
|
||||
memory_->SetData64(addr + index * 8, index * 0x10);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(regs.StepIfSignalHandler(memory_));
|
||||
EXPECT_EQ(0x140U, regs[X86_64_REG_RSP]);
|
||||
EXPECT_EQ(0x150U, regs[X86_64_REG_RIP]);
|
||||
EXPECT_EQ(0x140U, regs.sp());
|
||||
EXPECT_EQ(0x150U, regs.pc());
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
|
@ -42,16 +43,41 @@ namespace unwindstack {
|
|||
|
||||
static std::atomic_bool g_ready(false);
|
||||
static volatile bool g_ready_for_remote = false;
|
||||
static volatile bool g_signal_ready_for_remote = false;
|
||||
static std::atomic_bool g_finish(false);
|
||||
static std::atomic_uintptr_t g_ucontext;
|
||||
|
||||
static void Signal(int, siginfo_t*, void* sigcontext) {
|
||||
static std::vector<const char*> kFunctionOrder{"InnerFunction", "MiddleFunction", "OuterFunction"};
|
||||
|
||||
static std::vector<const char*> kFunctionSignalOrder{"SignalInnerFunction", "SignalMiddleFunction",
|
||||
"SignalOuterFunction", "InnerFunction",
|
||||
"MiddleFunction", "OuterFunction"};
|
||||
|
||||
static void SignalHandler(int, siginfo_t*, void* sigcontext) {
|
||||
g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
|
||||
while (!g_finish.load()) {
|
||||
}
|
||||
}
|
||||
|
||||
static std::string ErrorMsg(const char** function_names, size_t index,
|
||||
extern "C" void SignalInnerFunction() {
|
||||
g_signal_ready_for_remote = true;
|
||||
while (!g_finish.load()) {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void SignalMiddleFunction() {
|
||||
SignalInnerFunction();
|
||||
}
|
||||
|
||||
extern "C" void SignalOuterFunction() {
|
||||
SignalMiddleFunction();
|
||||
}
|
||||
|
||||
static void SignalCallerHandler(int, siginfo_t*, void*) {
|
||||
SignalOuterFunction();
|
||||
}
|
||||
|
||||
static std::string ErrorMsg(const std::vector<const char*>& function_names, size_t index,
|
||||
std::stringstream& unwind_stream) {
|
||||
return std::string(
|
||||
"Unwind completed without finding all frames\n"
|
||||
|
@ -59,10 +85,8 @@ static std::string ErrorMsg(const char** function_names, size_t index,
|
|||
function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str();
|
||||
}
|
||||
|
||||
static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs) {
|
||||
const char* function_names[] = {
|
||||
"InnerFunction", "MiddleFunction", "OuterFunction",
|
||||
};
|
||||
static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs,
|
||||
std::vector<const char*>& function_names) {
|
||||
size_t function_name_index = 0;
|
||||
|
||||
std::stringstream unwind_stream;
|
||||
|
@ -91,8 +115,7 @@ static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs) {
|
|||
uint64_t func_offset;
|
||||
if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
|
||||
if (name == function_names[function_name_index]) {
|
||||
function_name_index++;
|
||||
if (function_name_index == sizeof(function_names) / sizeof(const char*)) {
|
||||
if (++function_name_index == function_names.size()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +139,7 @@ extern "C" void InnerFunction(bool local) {
|
|||
RegsGetLocal(regs.get());
|
||||
MemoryLocal memory;
|
||||
|
||||
VerifyUnwind(getpid(), &memory, &maps, regs.get());
|
||||
VerifyUnwind(getpid(), &memory, &maps, regs.get(), kFunctionOrder);
|
||||
} else {
|
||||
g_ready_for_remote = true;
|
||||
g_ready = true;
|
||||
|
@ -137,6 +160,37 @@ TEST(UnwindTest, local) {
|
|||
OuterFunction(true);
|
||||
}
|
||||
|
||||
void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
|
||||
*completed = false;
|
||||
// Need to sleep before attempting first ptrace. Without this, on the
|
||||
// host it becomes impossible to attach and ptrace set errno to EPERM.
|
||||
usleep(1000);
|
||||
for (size_t i = 0; i < 100; i++) {
|
||||
ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0));
|
||||
for (size_t j = 0; j < 100; j++) {
|
||||
siginfo_t si;
|
||||
if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
|
||||
MemoryRemote memory(pid);
|
||||
// Read the remote value to see if we are ready.
|
||||
bool value;
|
||||
if (memory.Read(addr, &value, sizeof(value)) && value) {
|
||||
*completed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
if (leave_attached && *completed) {
|
||||
break;
|
||||
}
|
||||
ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
|
||||
if (*completed) {
|
||||
break;
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(UnwindTest, remote) {
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
|
@ -145,31 +199,9 @@ TEST(UnwindTest, remote) {
|
|||
}
|
||||
ASSERT_NE(-1, pid);
|
||||
|
||||
bool ready = false;
|
||||
uint64_t addr = reinterpret_cast<uint64_t>(&g_ready_for_remote);
|
||||
for (size_t i = 0; i < 100; i++) {
|
||||
ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0));
|
||||
for (size_t j = 0; j < 100; j++) {
|
||||
siginfo_t si;
|
||||
if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
|
||||
// Check to see if process is ready to be unwound.
|
||||
MemoryRemote memory(pid);
|
||||
// Read the remote value to see if we are ready.
|
||||
bool value;
|
||||
if (memory.Read(addr, &value, sizeof(value)) && value) {
|
||||
ready = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
if (ready) {
|
||||
break;
|
||||
}
|
||||
ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
|
||||
usleep(1000);
|
||||
}
|
||||
ASSERT_TRUE(read) << "Timed out waiting for remote process to be ready.";
|
||||
bool completed;
|
||||
WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
|
||||
ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
|
||||
|
||||
RemoteMaps maps(pid);
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
|
@ -178,7 +210,7 @@ TEST(UnwindTest, remote) {
|
|||
std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type));
|
||||
ASSERT_TRUE(regs.get() != nullptr);
|
||||
|
||||
VerifyUnwind(pid, &memory, &maps, regs.get());
|
||||
VerifyUnwind(pid, &memory, &maps, regs.get(), kFunctionOrder);
|
||||
|
||||
ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
|
||||
|
||||
|
@ -195,7 +227,7 @@ TEST(UnwindTest, from_context) {
|
|||
|
||||
struct sigaction act, oldact;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_sigaction = Signal;
|
||||
act.sa_sigaction = SignalHandler;
|
||||
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
|
||||
ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
|
||||
// Wait for the tid to get set.
|
||||
|
@ -207,8 +239,7 @@ TEST(UnwindTest, from_context) {
|
|||
}
|
||||
ASSERT_NE(0, tid.load());
|
||||
// Portable tgkill method.
|
||||
ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Failed because "
|
||||
<< strerror(errno);
|
||||
ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
|
||||
|
||||
// Wait for context data.
|
||||
void* ucontext;
|
||||
|
@ -226,7 +257,7 @@ TEST(UnwindTest, from_context) {
|
|||
std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::GetMachineType(), ucontext));
|
||||
MemoryLocal memory;
|
||||
|
||||
VerifyUnwind(tid.load(), &memory, &maps, regs.get());
|
||||
VerifyUnwind(tid.load(), &memory, &maps, regs.get(), kFunctionOrder);
|
||||
|
||||
ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
|
||||
|
||||
|
@ -234,4 +265,52 @@ TEST(UnwindTest, from_context) {
|
|||
thread.join();
|
||||
}
|
||||
|
||||
static void RemoteThroughSignal(unsigned int sa_flags) {
|
||||
g_ready = false;
|
||||
g_signal_ready_for_remote = false;
|
||||
g_finish = false;
|
||||
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
struct sigaction act, oldact;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_sigaction = SignalCallerHandler;
|
||||
act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags;
|
||||
ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
|
||||
|
||||
OuterFunction(false);
|
||||
exit(0);
|
||||
}
|
||||
ASSERT_NE(-1, pid);
|
||||
|
||||
bool completed;
|
||||
WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
|
||||
ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
|
||||
ASSERT_EQ(0, kill(pid, SIGUSR1));
|
||||
WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote), true, &completed);
|
||||
ASSERT_TRUE(completed) << "Timed out waiting for remote process to be in signal handler.";
|
||||
|
||||
RemoteMaps maps(pid);
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
MemoryRemote memory(pid);
|
||||
uint32_t machine_type;
|
||||
std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type));
|
||||
ASSERT_TRUE(regs.get() != nullptr);
|
||||
|
||||
VerifyUnwind(pid, &memory, &maps, regs.get(), kFunctionSignalOrder);
|
||||
|
||||
ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
ASSERT_EQ(pid, wait(nullptr));
|
||||
}
|
||||
|
||||
TEST(UnwindTest, remote_through_signal) {
|
||||
RemoteThroughSignal(0);
|
||||
}
|
||||
|
||||
TEST(UnwindTest, remote_through_signal_sa_siginfo) {
|
||||
RemoteThroughSignal(SA_SIGINFO);
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
Loading…
Reference in a new issue