diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp index a800c3124..e962a7319 100644 --- a/libunwindstack/Elf.cpp +++ b/libunwindstack/Elf.cpp @@ -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)); } diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp index aa59e314a..33319b1ed 100644 --- a/libunwindstack/Regs.cpp +++ b/libunwindstack/Regs.cpp @@ -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(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(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 diff --git a/libunwindstack/Ucontext.h b/libunwindstack/Ucontext.h index 410af2604..22f6a8988 100644 --- a/libunwindstack/Ucontext.h +++ b/libunwindstack/Ucontext.h @@ -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 diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h index ab98f3223..7623b7da6 100644 --- a/libunwindstack/include/unwindstack/Regs.h +++ b/libunwindstack/include/unwindstack/Regs.h @@ -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 { uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; void SetFromRaw() override; + + bool StepIfSignalHandler(Memory* memory) override; }; class RegsArm64 : public RegsImpl { @@ -114,6 +120,8 @@ class RegsArm64 : public RegsImpl { uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; void SetFromRaw() override; + + bool StepIfSignalHandler(Memory* memory) override; }; class RegsX86 : public RegsImpl { @@ -124,6 +132,10 @@ class RegsX86 : public RegsImpl { 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 { @@ -134,6 +146,10 @@ class RegsX86_64 : public RegsImpl { 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 diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp index 72ceb85ee..ed1be3b33 100644 --- a/libunwindstack/tests/ElfTest.cpp +++ b/libunwindstack/tests/ElfTest.cpp @@ -26,6 +26,7 @@ #include #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_); diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h index e796c9b24..f998da7a2 100644 --- a/libunwindstack/tests/RegsFake.h +++ b/libunwindstack/tests/RegsFake.h @@ -33,6 +33,7 @@ class RegsFake : public RegsImpl { 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; } }; diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp index 36136897a..9622166e2 100644 --- a/libunwindstack/tests/RegsTest.cpp +++ b/libunwindstack/tests/RegsTest.cpp @@ -23,6 +23,8 @@ #include #include +#include "Machine.h" + #include "MemoryFake.h" namespace unwindstack { @@ -60,6 +62,7 @@ class RegsTestImpl : public RegsImpl { 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 - 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 -void RegsTest::regs_return_address_register() { +void RegsTest::RegsReturnAddressRegister() { RegsTestImpl 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(); + RegsReturnAddressRegister(); } TEST_F(RegsTest, regs64_return_address_register) { - regs_return_address_register(); + RegsReturnAddressRegister(); } 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 diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp index 72065c95e..3c69e2a49 100644 --- a/libunwindstack/tests/UnwindTest.cpp +++ b/libunwindstack/tests/UnwindTest.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -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 kFunctionOrder{"InnerFunction", "MiddleFunction", "OuterFunction"}; + +static std::vector kFunctionSignalOrder{"SignalInnerFunction", "SignalMiddleFunction", + "SignalOuterFunction", "InnerFunction", + "MiddleFunction", "OuterFunction"}; + +static void SignalHandler(int, siginfo_t*, void* sigcontext) { g_ucontext = reinterpret_cast(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& 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& 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(&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(&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::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::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(&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(&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::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