blob: 1abdd833a422850978dd007926b06b95b14205fa [file] [log] [blame]
Chris Dearman231e3c82012-08-10 17:06:20 -07001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Backtracing functions for mips
19 */
20
21#define LOG_TAG "Corkscrew"
22//#define LOG_NDEBUG 0
23
24#include "../backtrace-arch.h"
25#include "../backtrace-helper.h"
26#include <corkscrew/ptrace.h>
27
28#include <stdlib.h>
29#include <signal.h>
30#include <stdbool.h>
31#include <limits.h>
32#include <errno.h>
33#include <sys/ptrace.h>
34#include <sys/exec_elf.h>
35#include <cutils/log.h>
36
37/* For PTRACE_GETREGS */
38typedef struct {
39 /* FIXME: check this definition */
40 uint64_t regs[32];
41 uint64_t lo;
42 uint64_t hi;
43 uint64_t epc;
44 uint64_t badvaddr;
45 uint64_t status;
46 uint64_t cause;
47} user_regs_struct;
48
49/* Machine context at the time a signal was raised. */
50typedef struct ucontext {
51 /* FIXME: use correct definition */
52 uint32_t sp;
53 uint32_t ra;
54 uint32_t pc;
55} ucontext_t;
56
57/* Unwind state. */
58typedef struct {
59 uint32_t sp;
60 uint32_t ra;
61 uint32_t pc;
62} unwind_state_t;
63
64uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) {
65 if (pc == 0)
66 return pc;
67 if ((pc & 1) == 0)
68 return pc-8; /* jal/bal/jalr + branch delay slot */
69 return pc;
70}
71
72static ssize_t unwind_backtrace_common(const memory_t* memory,
73 const map_info_t* map_info_list,
74 unwind_state_t* state, backtrace_frame_t* backtrace,
75 size_t ignore_depth, size_t max_depth) {
76 size_t ignored_frames = 0;
77 size_t returned_frames = 0;
78
79 for (size_t index = 0; returned_frames < max_depth; index++) {
80 uintptr_t pc = index ? rewind_pc_arch(memory, state->pc) : state->pc;
81 backtrace_frame_t* frame;
82 uintptr_t addr;
83 int maxcheck = 1024;
84 int stack_size = 0, ra_offset = 0;
85 bool found_start = false;
86
87 frame = add_backtrace_entry(pc, backtrace, ignore_depth,
88 max_depth, &ignored_frames, &returned_frames);
89
90 if (frame)
91 frame->stack_top = state->sp;
92
Duane Sandaf3dd882012-09-24 09:24:45 -070093 ALOGV("#%d: frame=%p pc=%08x sp=%08x\n",
94 index, frame, frame->absolute_pc, frame->stack_top);
Chris Dearman231e3c82012-08-10 17:06:20 -070095
96 for (addr = state->pc; maxcheck-- > 0 && !found_start; addr -= 4) {
97 uint32_t op;
98 if (!try_get_word(memory, addr, &op))
99 break;
100
101 // ALOGV("@0x%08x: 0x%08x\n", addr, op);
102 switch (op & 0xffff0000) {
103 case 0x27bd0000: // addiu sp, imm
104 {
105 // looking for stack being decremented
106 int32_t immediate = ((((int)op) << 16) >> 16);
107 if (immediate < 0) {
108 stack_size = -immediate;
109 found_start = true;
110 ALOGV("@0x%08x: found stack adjustment=%d\n", addr, stack_size);
111 }
112 }
113 break;
114 case 0xafbf0000: // sw ra, imm(sp)
115 ra_offset = ((((int)op) << 16) >> 16);
116 ALOGV("@0x%08x: found ra offset=%d\n", addr, ra_offset);
117 break;
118 case 0x3c1c0000: // lui gp
Duane Sandaf3dd882012-09-24 09:24:45 -0700119 ALOGV("@0x%08x: found function boundary\n", addr);
Chris Dearman231e3c82012-08-10 17:06:20 -0700120 found_start = true;
121 break;
122 default:
123 break;
124 }
125 }
126
127 if (ra_offset) {
128 uint32_t next_ra;
129 if (!try_get_word(memory, state->sp + ra_offset, &next_ra))
130 break;
131 state->ra = next_ra;
132 ALOGV("New ra: 0x%08x\n", state->ra);
133 }
134
135 if (stack_size) {
136 if (frame)
137 frame->stack_size = stack_size;
138 state->sp += stack_size;
139 ALOGV("New sp: 0x%08x\n", state->sp);
140 }
141
142 if (state->pc == state->ra && stack_size == 0)
143 break;
144
145 if (state->ra == 0)
146 break;
147
148 state->pc = state->ra;
149 }
150
151 ALOGV("returning %d frames\n", returned_frames);
152
153 return returned_frames;
154}
155
156ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
157 const map_info_t* map_info_list,
158 backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
159 const ucontext_t* uc = (const ucontext_t*)sigcontext;
160
161 unwind_state_t state;
162 state.sp = uc->sp;
163 state.pc = uc->pc;
164 state.ra = uc->ra;
165
Duane Sandaf3dd882012-09-24 09:24:45 -0700166 ALOGV("unwind_backtrace_signal_arch: "
167 "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
Chris Dearman231e3c82012-08-10 17:06:20 -0700168 ignore_depth, max_depth, state.pc, state.sp, state.ra);
169
170 memory_t memory;
171 init_memory(&memory, map_info_list);
172 return unwind_backtrace_common(&memory, map_info_list,
173 &state, backtrace, ignore_depth, max_depth);
174}
175
176ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
177 backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
178
179 user_regs_struct regs;
180 if (ptrace(PTRACE_GETREGS, tid, 0, &regs)) {
181 return -1;
182 }
183
184 unwind_state_t state;
185 state.sp = regs.regs[29];
186 state.ra = regs.regs[31];
187 state.pc = regs.epc;
188
Duane Sandaf3dd882012-09-24 09:24:45 -0700189 ALOGV("unwind_backtrace_ptrace_arch: "
190 "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
Chris Dearman231e3c82012-08-10 17:06:20 -0700191 ignore_depth, max_depth, state.pc, state.sp, state.ra);
192
193 memory_t memory;
194 init_memory_ptrace(&memory, tid);
195 return unwind_backtrace_common(&memory, context->map_info_list,
196 &state, backtrace, ignore_depth, max_depth);
197}