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
This commit is contained in:
Christopher Ferris 2017-07-07 16:35:48 -07:00
parent 896297b2ef
commit 2a25c4aab5
13 changed files with 878 additions and 45 deletions

View file

@ -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: [

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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;
}

View file

@ -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

View file

@ -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

180
libunwindstack/Ucontext.h Normal file
View file

@ -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

View file

@ -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));
}

View file

@ -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));
}

View file

@ -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; }
};

View file

@ -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());
}

View file

@ -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();
}