1/*
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
93        ALOGV("#%d: frame=%p pc=%08x sp=%08x\n", index, frame, frame->absolute_pc, frame->stack_top);
94
95        for (addr = state->pc; maxcheck-- > 0 && !found_start; addr -= 4) {
96            uint32_t op;
97            if (!try_get_word(memory, addr, &op))
98                break;
99
100            // ALOGV("@0x%08x: 0x%08x\n", addr, op);
101            switch (op & 0xffff0000) {
102            case 0x27bd0000: // addiu sp, imm
103                {
104                    // looking for stack being decremented
105                    int32_t immediate = ((((int)op) << 16) >> 16);
106                    if (immediate < 0) {
107                        stack_size = -immediate;
108                        found_start = true;
109                        ALOGV("@0x%08x: found stack adjustment=%d\n", addr, stack_size);
110                    }
111                }
112                break;
113            case 0xafbf0000: // sw ra, imm(sp)
114                ra_offset = ((((int)op) << 16) >> 16);
115                ALOGV("@0x%08x: found ra offset=%d\n", addr, ra_offset);
116                break;
117            case 0x3c1c0000: // lui gp
118                    ALOGV("@0x%08x: found function boundary\n", addr);
119                found_start = true;
120                break;
121            default:
122                break;
123            }
124        }
125
126        if (ra_offset) {
127            uint32_t next_ra;
128            if (!try_get_word(memory, state->sp + ra_offset, &next_ra))
129                break;
130            state->ra = next_ra;
131            ALOGV("New ra: 0x%08x\n", state->ra);
132        }
133
134        if (stack_size) {
135            if (frame)
136                frame->stack_size = stack_size;
137            state->sp += stack_size;
138            ALOGV("New sp: 0x%08x\n", state->sp);
139        }
140
141        if (state->pc == state->ra && stack_size == 0)
142            break;
143
144        if (state->ra == 0)
145            break;
146
147        state->pc = state->ra;
148    }
149
150    ALOGV("returning %d frames\n", returned_frames);
151
152    return returned_frames;
153}
154
155ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
156        const map_info_t* map_info_list,
157        backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
158    const ucontext_t* uc = (const ucontext_t*)sigcontext;
159
160    unwind_state_t state;
161    state.sp = uc->sp;
162    state.pc = uc->pc;
163    state.ra = uc->ra;
164
165    ALOGV("unwind_backtrace_signal_arch: ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
166          ignore_depth, max_depth, state.pc, state.sp, state.ra);
167
168    memory_t memory;
169    init_memory(&memory, map_info_list);
170    return unwind_backtrace_common(&memory, map_info_list,
171            &state, backtrace, ignore_depth, max_depth);
172}
173
174ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
175        backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
176
177    user_regs_struct regs;
178    if (ptrace(PTRACE_GETREGS, tid, 0, &regs)) {
179        return -1;
180    }
181
182    unwind_state_t state;
183    state.sp = regs.regs[29];
184    state.ra = regs.regs[31];
185    state.pc = regs.epc;
186
187    ALOGV("unwind_backtrace_ptrace_arch: ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
188          ignore_depth, max_depth, state.pc, state.sp, state.ra);
189
190    memory_t memory;
191    init_memory_ptrace(&memory, tid);
192    return unwind_backtrace_common(&memory, context->map_info_list,
193            &state, backtrace, ignore_depth, max_depth);
194}
195