diff options
author | Christopher Ferris <cferris@google.com> | 2017-07-11 21:23:13 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2017-07-11 21:23:13 +0000 |
commit | 8604a2774cc215b45197e17846ad5a8579072928 (patch) | |
tree | ca92e630f2e703dc37c625a9b5133f60ada8fe93 | |
parent | fadcf7eb39f7f8e754caf485159203bb021b7568 (diff) | |
parent | 63988ba67649420bd88ca360e167204388340171 (diff) | |
download | unwinding-8604a2774cc215b45197e17846ad5a8579072928.tar.gz |
Merge "Add full support for initing registers."
am: 63988ba676
Change-Id: Ib9bb8b413ddc0ad411506a5343817f2af5177b1e
-rw-r--r-- | libunwindstack/Android.bp | 10 | ||||
-rw-r--r-- | libunwindstack/AsmGetRegsX86.S | 62 | ||||
-rw-r--r-- | libunwindstack/AsmGetRegsX86_64.S | 62 | ||||
-rw-r--r-- | libunwindstack/Machine.h | 66 | ||||
-rw-r--r-- | libunwindstack/Regs.cpp | 149 | ||||
-rw-r--r-- | libunwindstack/Regs.h | 13 | ||||
-rw-r--r-- | libunwindstack/RegsGetLocal.h | 100 | ||||
-rw-r--r-- | libunwindstack/Ucontext.h | 180 | ||||
-rw-r--r-- | libunwindstack/tests/MapInfoCreateMemoryTest.cpp | 1 | ||||
-rw-r--r-- | libunwindstack/tests/MemoryRemoteTest.cpp | 3 | ||||
-rw-r--r-- | libunwindstack/tests/RegsFake.h | 1 | ||||
-rw-r--r-- | libunwindstack/tests/RegsTest.cpp | 43 | ||||
-rw-r--r-- | libunwindstack/tests/UnwindTest.cpp | 233 |
13 files changed, 878 insertions, 45 deletions
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 03a9a54..27221af 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -64,6 +64,15 @@ cc_defaults { "Symbols.cpp", ], + arch: { + x86: { + srcs: ["AsmGetRegsX86.S"], + }, + x86_64: { + srcs: ["AsmGetRegsX86_64.S"], + }, + }, + shared_libs: [ "libbase", "liblog", @@ -112,6 +121,7 @@ cc_defaults { "tests/MemoryTest.cpp", "tests/RegsTest.cpp", "tests/SymbolsTest.cpp", + "tests/UnwindTest.cpp", ], cflags: [ diff --git a/libunwindstack/AsmGetRegsX86.S b/libunwindstack/AsmGetRegsX86.S new file mode 100644 index 0000000..14927a3 --- /dev/null +++ b/libunwindstack/AsmGetRegsX86.S @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + .text + .global AsmGetRegs + .balign 16 + .type AsmGetRegs, @function +AsmGetRegs: + .cfi_startproc + mov 4(%esp), %eax + movl $0, (%eax) + movl %ecx, 4(%eax) + movl %edx, 8(%eax) + movl %ebx, 12(%eax) + + /* ESP */ + leal 4(%esp), %ecx + movl %ecx, 16(%eax) + + movl %ebp, 20(%eax) + movl %esi, 24(%eax) + movl %edi, 28(%eax) + + /* EIP */ + movl (%esp), %ecx + movl %ecx, 32(%eax) + + movl %cs, 36(%eax) + movl %ss, 40(%eax) + movl %ds, 44(%eax) + movl %es, 48(%eax) + movl %fs, 52(%eax) + movl %gs, 56(%eax) + ret + + .cfi_endproc + .size AsmGetRegs, .-AsmGetRegs diff --git a/libunwindstack/AsmGetRegsX86_64.S b/libunwindstack/AsmGetRegsX86_64.S new file mode 100644 index 0000000..4cd3b6f --- /dev/null +++ b/libunwindstack/AsmGetRegsX86_64.S @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + .text + .global AsmGetRegs + .balign 16 + .type AsmGetRegs, @function +AsmGetRegs: + .cfi_startproc + movq %rax, (%rdi) + movq %rdx, 8(%rdi) + movq %rcx, 16(%rdi) + movq %rbx, 24(%rdi) + movq %rsi, 32(%rdi) + movq %rdi, 40(%rdi) + movq %rbp, 48(%rdi) + + /* RSP */ + lea 8(%rsp), %rax + movq %rax, 56(%rdi) + + movq %r8, 64(%rdi) + movq %r9, 72(%rdi) + movq %r10, 80(%rdi) + movq %r11, 88(%rdi) + movq %r12, 96(%rdi) + movq %r13, 104(%rdi) + movq %r14, 112(%rdi) + movq %r15, 120(%rdi) + + /* RIP */ + movq (%rsp), %rax + movq %rax, 128(%rdi) + ret + + .cfi_endproc + .size AsmGetRegs, .-AsmGetRegs diff --git a/libunwindstack/Machine.h b/libunwindstack/Machine.h index 323ce80..bcc3788 100644 --- a/libunwindstack/Machine.h +++ b/libunwindstack/Machine.h @@ -83,47 +83,51 @@ enum Arm64Reg : uint16_t { ARM64_REG_LR = ARM64_REG_R30, }; +// Matches the numbers for the registers as generated by compilers. +// If this is changed, then unwinding will fail. enum X86Reg : uint16_t { X86_REG_EAX = 0, - X86_REG_ECX, - X86_REG_EDX, - X86_REG_EBX, - X86_REG_ESP, - X86_REG_EBP, - X86_REG_ESI, - X86_REG_EDI, - X86_REG_EIP, - X86_REG_EFL, - X86_REG_CS, - X86_REG_SS, - X86_REG_DS, - X86_REG_ES, - X86_REG_FS, - X86_REG_GS, + X86_REG_ECX = 1, + X86_REG_EDX = 2, + X86_REG_EBX = 3, + X86_REG_ESP = 4, + X86_REG_EBP = 5, + X86_REG_ESI = 6, + X86_REG_EDI = 7, + X86_REG_EIP = 8, + X86_REG_EFL = 9, + X86_REG_CS = 10, + X86_REG_SS = 11, + X86_REG_DS = 12, + X86_REG_ES = 13, + X86_REG_FS = 14, + X86_REG_GS = 15, X86_REG_LAST, X86_REG_SP = X86_REG_ESP, X86_REG_PC = X86_REG_EIP, }; +// Matches the numbers for the registers as generated by compilers. +// If this is changed, then unwinding will fail. enum X86_64Reg : uint16_t { X86_64_REG_RAX = 0, - X86_64_REG_RDX, - X86_64_REG_RCX, - X86_64_REG_RBX, - X86_64_REG_RSI, - X86_64_REG_RDI, - X86_64_REG_RBP, - X86_64_REG_RSP, - X86_64_REG_R8, - X86_64_REG_R9, - X86_64_REG_R10, - X86_64_REG_R11, - X86_64_REG_R12, - X86_64_REG_R13, - X86_64_REG_R14, - X86_64_REG_R15, - X86_64_REG_RIP, + X86_64_REG_RDX = 1, + X86_64_REG_RCX = 2, + X86_64_REG_RBX = 3, + X86_64_REG_RSI = 4, + X86_64_REG_RDI = 5, + X86_64_REG_RBP = 6, + X86_64_REG_RSP = 7, + X86_64_REG_R8 = 8, + X86_64_REG_R9 = 9, + X86_64_REG_R10 = 10, + X86_64_REG_R11 = 11, + X86_64_REG_R12 = 12, + X86_64_REG_R13 = 13, + X86_64_REG_R14 = 14, + X86_64_REG_R15 = 15, + X86_64_REG_RIP = 16, X86_64_REG_LAST, X86_64_REG_SP = X86_64_REG_RSP, diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp index da22b07..92b6e22 100644 --- a/libunwindstack/Regs.cpp +++ b/libunwindstack/Regs.cpp @@ -27,6 +27,7 @@ #include "Machine.h" #include "MapInfo.h" #include "Regs.h" +#include "Ucontext.h" #include "User.h" template <typename AddressType> @@ -88,6 +89,11 @@ uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) { return rel_pc - 4; } +void RegsArm::SetFromRaw() { + set_pc(regs_[ARM_REG_PC]); + set_sp(regs_[ARM_REG_SP]); +} + RegsArm64::RegsArm64() : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {} @@ -102,6 +108,11 @@ uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) { return rel_pc - 4; } +void RegsArm64::SetFromRaw() { + set_pc(regs_[ARM64_REG_PC]); + set_sp(regs_[ARM64_REG_SP]); +} + RegsX86::RegsX86() : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {} @@ -116,6 +127,11 @@ uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) { return rel_pc - 1; } +void RegsX86::SetFromRaw() { + set_pc(regs_[X86_REG_PC]); + set_sp(regs_[X86_REG_SP]); +} + RegsX86_64::RegsX86_64() : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {} @@ -131,15 +147,17 @@ uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) { return rel_pc - 1; } +void RegsX86_64::SetFromRaw() { + set_pc(regs_[X86_64_REG_PC]); + set_sp(regs_[X86_64_REG_SP]); +} + static Regs* ReadArm(void* remote_data) { arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data); RegsArm* regs = new RegsArm(); memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t)); - - regs->set_pc(user->regs[ARM_REG_PC]); - regs->set_sp(user->regs[ARM_REG_SP]); - + regs->SetFromRaw(); return regs; } @@ -148,9 +166,10 @@ static Regs* ReadArm64(void* remote_data) { RegsArm64* regs = new RegsArm64(); memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t)); - regs->set_pc(user->pc); - regs->set_sp(user->sp); - + uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData()); + reg_data[ARM64_REG_PC] = user->pc; + reg_data[ARM64_REG_SP] = user->sp; + regs->SetFromRaw(); return regs; } @@ -168,9 +187,7 @@ static Regs* ReadX86(void* remote_data) { (*regs)[X86_REG_ESP] = user->esp; (*regs)[X86_REG_EIP] = user->eip; - regs->set_pc(user->eip); - regs->set_sp(user->esp); - + regs->SetFromRaw(); return regs; } @@ -196,9 +213,7 @@ static Regs* ReadX86_64(void* remote_data) { (*regs)[X86_64_REG_RSP] = user->rsp; (*regs)[X86_64_REG_RIP] = user->rip; - regs->set_pc(user->rip); - regs->set_sp(user->rsp); - + regs->SetFromRaw(); return regs; } @@ -231,3 +246,111 @@ Regs* Regs::RemoteGet(pid_t pid, uint32_t* machine_type) { } return nullptr; } + +static Regs* CreateFromArmUcontext(void* ucontext) { + arm_ucontext_t* arm_ucontext = reinterpret_cast<arm_ucontext_t*>(ucontext); + + RegsArm* regs = new RegsArm(); + memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t)); + regs->SetFromRaw(); + return regs; +} + +static Regs* CreateFromArm64Ucontext(void* ucontext) { + arm64_ucontext_t* arm64_ucontext = reinterpret_cast<arm64_ucontext_t*>(ucontext); + + RegsArm64* regs = new RegsArm64(); + memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t)); + regs->SetFromRaw(); + return regs; +} + +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(); + return regs; +} + +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(); + return regs; +} + +Regs* Regs::CreateFromUcontext(uint32_t machine_type, void* ucontext) { + switch (machine_type) { + case EM_386: + return CreateFromX86Ucontext(ucontext); + case EM_X86_64: + return CreateFromX86_64Ucontext(ucontext); + case EM_ARM: + return CreateFromArmUcontext(ucontext); + case EM_AARCH64: + return CreateFromArm64Ucontext(ucontext); + } + return nullptr; +} + +uint32_t Regs::GetMachineType() { +#if defined(__arm__) + return EM_ARM; +#elif defined(__aarch64__) + return EM_AARCH64; +#elif defined(__i386__) + return EM_386; +#elif defined(__x86_64__) + return EM_X86_64; +#else + abort(); +#endif +} + +Regs* Regs::CreateFromLocal() { + Regs* regs; +#if defined(__arm__) + regs = new RegsArm(); +#elif defined(__aarch64__) + regs = new RegsArm64(); +#elif defined(__i386__) + regs = new RegsX86(); +#elif defined(__x86_64__) + regs = new RegsX86_64(); +#else + abort(); +#endif + return regs; +} diff --git a/libunwindstack/Regs.h b/libunwindstack/Regs.h index 8f5a721..a7567d8 100644 --- a/libunwindstack/Regs.h +++ b/libunwindstack/Regs.h @@ -54,10 +54,15 @@ class Regs { virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0; + virtual void SetFromRaw() = 0; + uint16_t sp_reg() { return sp_reg_; } uint16_t total_regs() { return total_regs_; } + static uint32_t GetMachineType(); static Regs* RemoteGet(pid_t pid, uint32_t* machine_type); + static Regs* CreateFromUcontext(uint32_t machine_type, void* ucontext); + static Regs* CreateFromLocal(); protected: uint16_t total_regs_; @@ -98,6 +103,8 @@ class RegsArm : public RegsImpl<uint32_t> { virtual ~RegsArm() = default; uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; + + void SetFromRaw() override; }; class RegsArm64 : public RegsImpl<uint64_t> { @@ -106,6 +113,8 @@ class RegsArm64 : public RegsImpl<uint64_t> { virtual ~RegsArm64() = default; uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; + + void SetFromRaw() override; }; class RegsX86 : public RegsImpl<uint32_t> { @@ -114,6 +123,8 @@ class RegsX86 : public RegsImpl<uint32_t> { virtual ~RegsX86() = default; uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; + + void SetFromRaw() override; }; class RegsX86_64 : public RegsImpl<uint64_t> { @@ -122,6 +133,8 @@ class RegsX86_64 : public RegsImpl<uint64_t> { virtual ~RegsX86_64() = default; uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; + + void SetFromRaw() override; }; #endif // _LIBUNWINDSTACK_REGS_H diff --git a/libunwindstack/RegsGetLocal.h b/libunwindstack/RegsGetLocal.h new file mode 100644 index 0000000..8e6d04c --- /dev/null +++ b/libunwindstack/RegsGetLocal.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBUNWINDSTACK_REGS_GET_LOCAL_H +#define _LIBUNWINDSTACK_REGS_GET_LOCAL_H + +#if defined(__arm__) + +inline void RegsGetLocal(Regs* regs) { + void* reg_data = regs->RawData(); + asm volatile( + ".align 2\n" + "bx pc\n" + "nop\n" + ".code 32\n" + "stmia %[base], {r0-r12}\n" + "add %[base], #52\n" + "mov r1, r13\n" + "mov r2, r14\n" + "mov r3, r15\n" + "stmia %[base], {r1-r3}\n" + "orr %[base], pc, #1\n" + "bx %[base]\n" + : [base] "+r"(reg_data) + : + : "memory"); + + regs->SetFromRaw(); +} + +#elif defined(__aarch64__) + +inline void RegsGetLocal(Regs* regs) { + void* reg_data = regs->RawData(); + asm volatile( + "1:\n" + "stp x0, x1, [%[base], #0]\n" + "stp x2, x3, [%[base], #16]\n" + "stp x4, x5, [%[base], #32]\n" + "stp x6, x7, [%[base], #48]\n" + "stp x8, x9, [%[base], #64]\n" + "stp x10, x11, [%[base], #80]\n" + "stp x12, x13, [%[base], #96]\n" + "stp x14, x15, [%[base], #112]\n" + "stp x16, x17, [%[base], #128]\n" + "stp x18, x19, [%[base], #144]\n" + "stp x20, x21, [%[base], #160]\n" + "stp x22, x23, [%[base], #176]\n" + "stp x24, x25, [%[base], #192]\n" + "stp x26, x27, [%[base], #208]\n" + "stp x28, x29, [%[base], #224]\n" + "str x30, [%[base], #240]\n" + "mov x12, sp\n" + "adr x13, 1b\n" + "stp x12, x13, [%[base], #248]\n" + : [base] "+r"(reg_data) + : + : "x12", "x13", "memory"); + + regs->SetFromRaw(); +} + +#elif defined(__i386__) || defined(__x86_64__) + +extern "C" void AsmGetRegs(void* regs); + +inline void RegsGetLocal(Regs* regs) { + AsmGetRegs(regs->RawData()); + + regs->SetFromRaw(); +} + +#endif + +#endif // _LIBUNWINDSTACK_REGS_GET_LOCAL_H diff --git a/libunwindstack/Ucontext.h b/libunwindstack/Ucontext.h new file mode 100644 index 0000000..95ba682 --- /dev/null +++ b/libunwindstack/Ucontext.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBUNWINDSTACK_UCONTEXT_H +#define _LIBUNWINDSTACK_UCONTEXT_H + +#include <stdint.h> + +//------------------------------------------------------------------- +// ARM ucontext structures +//------------------------------------------------------------------- +struct arm_stack_t { + uint32_t ss_sp; // void __user* + int32_t ss_flags; // int + uint32_t ss_size; // size_t +}; + +struct arm_mcontext_t { + uint32_t trap_no; // unsigned long + uint32_t error_code; // unsigned long + uint32_t oldmask; // unsigned long + uint32_t regs[ARM_REG_LAST]; // unsigned long + uint32_t cpsr; // unsigned long + uint32_t fault_address; // unsigned long +}; + +struct arm_ucontext_t { + uint32_t uc_flags; // unsigned long + uint32_t uc_link; // struct ucontext* + arm_stack_t uc_stack; + arm_mcontext_t uc_mcontext; + // Nothing else is used, so don't define it. +}; +//------------------------------------------------------------------- + +//------------------------------------------------------------------- +// ARM64 ucontext structures +//------------------------------------------------------------------- +struct arm64_stack_t { + uint64_t ss_sp; // void __user* + int32_t ss_flags; // int + uint64_t ss_size; // size_t +}; + +struct arm64_sigset_t { + uint64_t sig; // unsigned long +}; + +struct arm64_mcontext_t { + uint64_t fault_address; // __u64 + uint64_t regs[ARM64_REG_LAST]; // __u64 + uint64_t pstate; // __u64 + // Nothing else is used, so don't define it. +}; + +struct arm64_ucontext_t { + uint64_t uc_flags; // unsigned long + uint64_t uc_link; // struct ucontext* + arm64_stack_t uc_stack; + arm64_sigset_t uc_sigmask; + // The kernel adds extra padding after uc_sigmask to match glibc sigset_t on ARM64. + char __padding[128 - sizeof(arm64_sigset_t)]; + // The full structure requires 16 byte alignment, but our partial structure + // doesn't, so force the alignment. + arm64_mcontext_t uc_mcontext __attribute__((aligned(16))); +}; +//------------------------------------------------------------------- + +//------------------------------------------------------------------- +// X86 ucontext structures +//------------------------------------------------------------------- +struct x86_stack_t { + uint32_t ss_sp; // void __user* + int32_t ss_flags; // int + uint32_t ss_size; // size_t +}; + +struct x86_mcontext_t { + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint32_t trapno; + uint32_t err; + uint32_t eip; + uint32_t cs; + uint32_t efl; + uint32_t uesp; + uint32_t ss; + // Only care about the registers, skip everything else. +}; + +struct x86_ucontext_t { + uint32_t uc_flags; // unsigned long + uint32_t uc_link; // struct ucontext* + x86_stack_t uc_stack; + x86_mcontext_t uc_mcontext; + // Nothing else is used, so don't define it. +}; +//------------------------------------------------------------------- + +//------------------------------------------------------------------- +// X86_64 ucontext structures +//------------------------------------------------------------------- +struct x86_64_stack_t { + uint64_t ss_sp; // void __user* + int32_t ss_flags; // int + uint64_t ss_size; // size_t +}; + +struct x86_64_mcontext_t { + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rbx; + uint64_t rdx; + uint64_t rax; + uint64_t rcx; + uint64_t rsp; + uint64_t rip; + uint64_t efl; + uint64_t csgsfs; + uint64_t err; + uint64_t trapno; + uint64_t oldmask; + uint64_t cr2; + // Only care about the registers, skip everything else. +}; + +typedef struct x86_64_ucontext { + 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; +//------------------------------------------------------------------- + +#endif // _LIBUNWINDSTACK_UCONTEXT_H diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp index d7433e1..b82c841 100644 --- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp +++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp @@ -201,4 +201,5 @@ TEST_F(MapInfoCreateMemoryTest, remote_memory) { ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); kill(pid, SIGKILL); + ASSERT_EQ(pid, wait(nullptr)); } diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp index e48edf7..7210786 100644 --- a/libunwindstack/tests/MemoryRemoteTest.cpp +++ b/libunwindstack/tests/MemoryRemoteTest.cpp @@ -91,6 +91,7 @@ TEST_F(MemoryRemoteTest, read) { ASSERT_TRUE(Detach(pid)); kill(pid, SIGKILL); + ASSERT_EQ(pid, wait(nullptr)); } TEST_F(MemoryRemoteTest, read_fail) { @@ -131,6 +132,7 @@ TEST_F(MemoryRemoteTest, read_fail) { ASSERT_TRUE(Detach(pid)); kill(pid, SIGKILL); + ASSERT_EQ(pid, wait(nullptr)); } TEST_F(MemoryRemoteTest, read_overflow) { @@ -160,4 +162,5 @@ TEST_F(MemoryRemoteTest, read_illegal) { ASSERT_TRUE(Detach(pid)); kill(pid, SIGKILL); + ASSERT_EQ(pid, wait(nullptr)); } diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h index ff030c8..c57954c 100644 --- a/libunwindstack/tests/RegsFake.h +++ b/libunwindstack/tests/RegsFake.h @@ -30,6 +30,7 @@ class RegsFake : public RegsImpl<TypeParam> { uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; } uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; } + void SetFromRaw() override {} bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; } }; diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp index 3056373..dde42de 100644 --- a/libunwindstack/tests/RegsTest.cpp +++ b/libunwindstack/tests/RegsTest.cpp @@ -56,7 +56,8 @@ class RegsTestImpl : public RegsImpl<TypeParam> { : RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {} virtual ~RegsTestImpl() = default; - uint64_t GetAdjustedPc(uint64_t, Elf*) { return 0; } + uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; } + void SetFromRaw() override {} }; class RegsTest : public ::testing::Test { @@ -264,3 +265,43 @@ TEST_F(RegsTest, elf_invalid) { ASSERT_EQ(0x800U, regs_x86_64.GetRelPc(&invalid_elf, &map_info)); ASSERT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, &invalid_elf)); } + +TEST_F(RegsTest, arm_set_from_raw) { + RegsArm arm; + uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData()); + regs[13] = 0x100; + regs[15] = 0x200; + arm.SetFromRaw(); + EXPECT_EQ(0x100U, arm.sp()); + EXPECT_EQ(0x200U, arm.pc()); +} + +TEST_F(RegsTest, arm64_set_from_raw) { + RegsArm64 arm64; + uint64_t* regs = reinterpret_cast<uint64_t*>(arm64.RawData()); + regs[31] = 0xb100000000ULL; + regs[32] = 0xc200000000ULL; + arm64.SetFromRaw(); + EXPECT_EQ(0xb100000000U, arm64.sp()); + EXPECT_EQ(0xc200000000U, arm64.pc()); +} + +TEST_F(RegsTest, x86_set_from_raw) { + RegsX86 x86; + uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData()); + regs[4] = 0x23450000; + regs[8] = 0xabcd0000; + x86.SetFromRaw(); + EXPECT_EQ(0x23450000U, x86.sp()); + EXPECT_EQ(0xabcd0000U, x86.pc()); +} + +TEST_F(RegsTest, x86_64_set_from_raw) { + RegsX86_64 x86_64; + uint64_t* regs = reinterpret_cast<uint64_t*>(x86_64.RawData()); + regs[7] = 0x1200000000ULL; + regs[16] = 0x4900000000ULL; + x86_64.SetFromRaw(); + EXPECT_EQ(0x1200000000U, x86_64.sp()); + EXPECT_EQ(0x4900000000U, x86_64.pc()); +} diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp new file mode 100644 index 0000000..7497b65 --- /dev/null +++ b/libunwindstack/tests/UnwindTest.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <string.h> + +#include <signal.h> +#include <stdint.h> +#include <sys/ptrace.h> +#include <sys/syscall.h> +#include <unistd.h> + +#include <gtest/gtest.h> + +#include <atomic> +#include <memory> +#include <sstream> +#include <string> +#include <thread> + +#include "Elf.h" +#include "MapInfo.h" +#include "Maps.h" +#include "Memory.h" +#include "Regs.h" +#include "RegsGetLocal.h" + +static std::atomic_bool g_ready(false); +static volatile bool g_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) { + g_ucontext = reinterpret_cast<uintptr_t>(sigcontext); + while (!g_finish.load()) { + } +} + +static std::string ErrorMsg(const char** function_names, size_t index, + std::stringstream& unwind_stream) { + return std::string( + "Unwind completed without finding all frames\n" + " Looking for function: ") + + 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", + }; + size_t function_name_index = 0; + + std::stringstream unwind_stream; + unwind_stream << std::hex; + for (size_t frame_num = 0; frame_num < 64; frame_num++) { + ASSERT_NE(0U, regs->pc()) << ErrorMsg(function_names, function_name_index, unwind_stream); + MapInfo* map_info = maps->Find(regs->pc()); + ASSERT_TRUE(map_info != nullptr) << ErrorMsg(function_names, function_name_index, unwind_stream); + + Elf* elf = map_info->GetElf(pid, true); + uint64_t rel_pc = regs->GetRelPc(elf, map_info); + uint64_t adjusted_rel_pc = rel_pc; + if (frame_num != 0) { + adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf); + } + unwind_stream << " PC: 0x" << regs->pc() << " Rel: 0x" << adjusted_rel_pc; + unwind_stream << " Map: "; + if (!map_info->name.empty()) { + unwind_stream << map_info->name; + } else { + unwind_stream << " anonymous"; + } + unwind_stream << "<" << map_info->start << "-" << map_info->end << ">"; + + std::string name; + 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*)) { + return; + } + } + unwind_stream << " " << name; + } + unwind_stream << "\n"; + ASSERT_TRUE(elf->Step(rel_pc + map_info->elf_offset, regs, memory)) + << ErrorMsg(function_names, function_name_index, unwind_stream); + } + ASSERT_TRUE(false) << ErrorMsg(function_names, function_name_index, unwind_stream); +} + +// This test assumes that this code is compiled with optimizations turned +// off. If this doesn't happen, then all of the calls will be optimized +// away. +extern "C" void InnerFunction(bool local) { + if (local) { + LocalMaps maps; + ASSERT_TRUE(maps.Parse()); + std::unique_ptr<Regs> regs(Regs::CreateFromLocal()); + RegsGetLocal(regs.get()); + MemoryLocal memory; + + VerifyUnwind(getpid(), &memory, &maps, regs.get()); + } else { + g_ready_for_remote = true; + g_ready = true; + while (!g_finish.load()) { + } + } +} + +extern "C" void MiddleFunction(bool local) { + InnerFunction(local); +} + +extern "C" void OuterFunction(bool local) { + MiddleFunction(local); +} + +TEST(UnwindTest, local) { + OuterFunction(true); +} + +TEST(UnwindTest, remote) { + pid_t pid; + if ((pid = fork()) == 0) { + OuterFunction(false); + exit(0); + } + 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."; + + 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()); + + ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0)); + + kill(pid, SIGKILL); + ASSERT_EQ(pid, wait(nullptr)); +} + +TEST(UnwindTest, from_context) { + std::atomic_int tid(0); + std::thread thread([&]() { + tid = syscall(__NR_gettid); + OuterFunction(false); + }); + + struct sigaction act, oldact; + memset(&act, 0, sizeof(act)); + act.sa_sigaction = Signal; + act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; + ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact)); + // Wait for the tid to get set. + for (size_t i = 0; i < 100; i++) { + if (tid.load() != 0) { + break; + } + usleep(1000); + } + ASSERT_NE(0, tid.load()); + // Portable tgkill method. + ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Failed because " + << strerror(errno); + + // Wait for context data. + void* ucontext; + for (size_t i = 0; i < 200; i++) { + ucontext = reinterpret_cast<void*>(g_ucontext.load()); + if (ucontext != nullptr) { + break; + } + usleep(1000); + } + ASSERT_TRUE(ucontext != nullptr) << "Timed out waiting for thread to respond to signal."; + + LocalMaps maps; + ASSERT_TRUE(maps.Parse()); + std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::GetMachineType(), ucontext)); + MemoryLocal memory; + + VerifyUnwind(tid.load(), &memory, &maps, regs.get()); + + ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr)); + + g_finish = true; + thread.join(); +} |