Add full support for initing registers.
- Fixes a few bugs in untested functionality.
- Add tests for the way the register handling code is used.
- Fix a few tests that were not reaping child processes.
Bug: 23762183
Test: Ran unit tests on host (32 bit and 64 bit).
Test: Ran unit tests on angler (32 bit and 64 bit).
Change-Id: I573d6617b4f1561f6e8494d7213c52086d112d97
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 @@
"Symbols.cpp",
],
+ arch: {
+ x86: {
+ srcs: ["AsmGetRegsX86.S"],
+ },
+ x86_64: {
+ srcs: ["AsmGetRegsX86_64.S"],
+ },
+ },
+
shared_libs: [
"libbase",
"liblog",
@@ -112,6 +121,7 @@
"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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
(*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 @@
(*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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
ASSERT_TRUE(Detach(pid));
kill(pid, SIGKILL);
+ ASSERT_EQ(pid, wait(nullptr));
}
TEST_F(MemoryRemoteTest, read_fail) {
@@ -131,6 +132,7 @@
ASSERT_TRUE(Detach(pid));
kill(pid, SIGKILL);
+ ASSERT_EQ(pid, wait(nullptr));
}
TEST_F(MemoryRemoteTest, read_overflow) {
@@ -160,4 +162,5 @@
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 @@
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 @@
: 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 @@
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();
+}