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