1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
/*
* Copyright (C) 2012 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.
*/
/*
* Backtracing functions for mips
*/
#define LOG_TAG "Corkscrew"
//#define LOG_NDEBUG 0
#include "../backtrace-arch.h"
#include "../backtrace-helper.h"
#include <corkscrew/ptrace.h>
#include <stdlib.h>
#include <signal.h>
#include <stdbool.h>
#include <limits.h>
#include <errno.h>
#include <sys/ptrace.h>
#include <sys/exec_elf.h>
#include <cutils/log.h>
/* For PTRACE_GETREGS */
typedef struct {
/* FIXME: check this definition */
uint64_t regs[32];
uint64_t lo;
uint64_t hi;
uint64_t epc;
uint64_t badvaddr;
uint64_t status;
uint64_t cause;
} user_regs_struct;
/* Machine context at the time a signal was raised. */
typedef struct ucontext {
/* FIXME: use correct definition */
uint32_t sp;
uint32_t ra;
uint32_t pc;
} ucontext_t;
/* Unwind state. */
typedef struct {
uint32_t sp;
uint32_t ra;
uint32_t pc;
} unwind_state_t;
uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) {
if (pc == 0)
return pc;
if ((pc & 1) == 0)
return pc-8; /* jal/bal/jalr + branch delay slot */
return pc;
}
static ssize_t unwind_backtrace_common(const memory_t* memory,
const map_info_t* map_info_list,
unwind_state_t* state, backtrace_frame_t* backtrace,
size_t ignore_depth, size_t max_depth) {
size_t ignored_frames = 0;
size_t returned_frames = 0;
for (size_t index = 0; returned_frames < max_depth; index++) {
uintptr_t pc = index ? rewind_pc_arch(memory, state->pc) : state->pc;
backtrace_frame_t* frame;
uintptr_t addr;
int maxcheck = 1024;
int stack_size = 0, ra_offset = 0;
bool found_start = false;
frame = add_backtrace_entry(pc, backtrace, ignore_depth,
max_depth, &ignored_frames, &returned_frames);
if (frame)
frame->stack_top = state->sp;
ALOGV("#%d: frame=%p pc=%08x sp=%08x\n", index, frame, frame->absolute_pc, frame->stack_top);
for (addr = state->pc; maxcheck-- > 0 && !found_start; addr -= 4) {
uint32_t op;
if (!try_get_word(memory, addr, &op))
break;
// ALOGV("@0x%08x: 0x%08x\n", addr, op);
switch (op & 0xffff0000) {
case 0x27bd0000: // addiu sp, imm
{
// looking for stack being decremented
int32_t immediate = ((((int)op) << 16) >> 16);
if (immediate < 0) {
stack_size = -immediate;
found_start = true;
ALOGV("@0x%08x: found stack adjustment=%d\n", addr, stack_size);
}
}
break;
case 0xafbf0000: // sw ra, imm(sp)
ra_offset = ((((int)op) << 16) >> 16);
ALOGV("@0x%08x: found ra offset=%d\n", addr, ra_offset);
break;
case 0x3c1c0000: // lui gp
ALOGV("@0x%08x: found function boundary\n", addr);
found_start = true;
break;
default:
break;
}
}
if (ra_offset) {
uint32_t next_ra;
if (!try_get_word(memory, state->sp + ra_offset, &next_ra))
break;
state->ra = next_ra;
ALOGV("New ra: 0x%08x\n", state->ra);
}
if (stack_size) {
if (frame)
frame->stack_size = stack_size;
state->sp += stack_size;
ALOGV("New sp: 0x%08x\n", state->sp);
}
if (state->pc == state->ra && stack_size == 0)
break;
if (state->ra == 0)
break;
state->pc = state->ra;
}
ALOGV("returning %d frames\n", returned_frames);
return returned_frames;
}
ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
const map_info_t* map_info_list,
backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
const ucontext_t* uc = (const ucontext_t*)sigcontext;
unwind_state_t state;
state.sp = uc->sp;
state.pc = uc->pc;
state.ra = uc->ra;
ALOGV("unwind_backtrace_signal_arch: ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
ignore_depth, max_depth, state.pc, state.sp, state.ra);
memory_t memory;
init_memory(&memory, map_info_list);
return unwind_backtrace_common(&memory, map_info_list,
&state, backtrace, ignore_depth, max_depth);
}
ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
user_regs_struct regs;
if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) {
return -1;
}
unwind_state_t state;
state.sp = regs.regs[29];
state.ra = regs.regs[31];
state.pc = regs.epc;
ALOGV("unwind_backtrace_ptrace_arch: ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
ignore_depth, max_depth, state.pc, state.sp, state.ra);
memory_t memory;
init_memory_ptrace(&memory, tid);
return unwind_backtrace_common(&memory, context->map_info_list,
&state, backtrace, ignore_depth, max_depth);
}
|