summaryrefslogtreecommitdiffstats
path: root/libcorkscrew/arch-mips/backtrace-mips.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcorkscrew/arch-mips/backtrace-mips.c')
-rw-r--r--libcorkscrew/arch-mips/backtrace-mips.c901
1 files changed, 0 insertions, 901 deletions
diff --git a/libcorkscrew/arch-mips/backtrace-mips.c b/libcorkscrew/arch-mips/backtrace-mips.c
deleted file mode 100644
index 832fb86..0000000
--- a/libcorkscrew/arch-mips/backtrace-mips.c
+++ /dev/null
@@ -1,901 +0,0 @@
-/*
- * 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 "../ptrace-arch.h"
-#include <corkscrew/ptrace.h>
-#include "dwarf.h"
-
-#include <stdlib.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <limits.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/ptrace.h>
-#include <cutils/log.h>
-
-#include <sys/ucontext.h>
-
-/* For PTRACE_GETREGS */
-typedef struct {
- 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;
-
-enum {
- REG_ZERO = 0, REG_AT, REG_V0, REG_V1,
- REG_A0, REG_A1, REG_A2, REG_A3,
- REG_T0, REG_T1, REG_T2, REG_T3,
- REG_T4, REG_T5, REG_T6, REG_T7,
- REG_S0, REG_S1, REG_S2, REG_S3,
- REG_S4, REG_S5, REG_S6, REG_S7,
- REG_T8, REG_T9, REG_K0, REG_K1,
- REG_GP, REG_SP, REG_S8, REG_RA,
-};
-
-
-/* Unwind state. */
-typedef struct {
- uint32_t reg[DWARF_REGISTERS];
-} unwind_state_t;
-
-uintptr_t rewind_pc_arch(const memory_t* memory __attribute__((unused)), uintptr_t pc) {
- if (pc == 0)
- return pc;
- if ((pc & 1) == 0)
- return pc-8; /* jal/bal/jalr + branch delay slot */
- return pc;
-}
-
-/* Read byte through 4 byte cache. Usually we read byte by byte and updating cursor. */
-static bool try_get_byte(const memory_t* memory, uintptr_t ptr, uint8_t* out_value, uint32_t* cursor) {
- static uintptr_t lastptr;
- static uint32_t buf;
-
- ptr += *cursor;
-
- if (ptr < lastptr || lastptr + 3 < ptr) {
- lastptr = (ptr >> 2) << 2;
- if (!try_get_word(memory, lastptr, &buf)) {
- return false;
- }
- }
- *out_value = (uint8_t)((buf >> ((ptr & 3) * 8)) & 0xff);
- ++*cursor;
- return true;
-}
-
-/* Getting X bytes. 4 is maximum for now. */
-static bool try_get_xbytes(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t bytes, uint32_t* cursor) {
- uint32_t data = 0;
- if (bytes > 4) {
- ALOGE("can't read more than 4 bytes, trying to read %d", bytes);
- return false;
- }
- for (int i = 0; i < bytes; i++) {
- uint8_t buf;
- if (!try_get_byte(memory, ptr, &buf, cursor)) {
- return false;
- }
- data |= (uint32_t)buf << (i * 8);
- }
- *out_value = data;
- return true;
-}
-
-/* Reads signed/unsigned LEB128 encoded data. From 1 to 4 bytes. */
-static bool try_get_leb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor, bool sign_extend) {
- uint8_t buf = 0;
- uint32_t val = 0;
- uint8_t c = 0;
- do {
- if (!try_get_byte(memory, ptr, &buf, cursor)) {
- return false;
- }
- val |= ((uint32_t)buf & 0x7f) << (c * 7);
- c++;
- } while (buf & 0x80 && (c * 7) <= 32);
- if (c * 7 > 32) {
- ALOGE("%s: data exceeds expected 4 bytes maximum", __FUNCTION__);
- return false;
- }
- if (sign_extend) {
- if (buf & 0x40) {
- val |= ((uint32_t)-1 << (c * 7));
- }
- }
- *out_value = val;
- return true;
-}
-
-/* Reads signed LEB128 encoded data. From 1 to 4 bytes. */
-static bool try_get_sleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) {
- return try_get_leb128(memory, ptr, out_value, cursor, true);
-}
-
-/* Reads unsigned LEB128 encoded data. From 1 to 4 bytes. */
-static bool try_get_uleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) {
- return try_get_leb128(memory, ptr, out_value, cursor, false);
-}
-
-/* Getting data encoded by dwarf encodings. */
-static bool read_dwarf(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t encoding, uint32_t* cursor) {
- uint32_t data = 0;
- bool issigned = true;
- uintptr_t addr = ptr + *cursor;
- /* Lower 4 bits is data type/size */
- /* TODO: add more encodings if it becomes necessary */
- switch (encoding & 0xf) {
- case DW_EH_PE_absptr:
- if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) {
- return false;
- }
- *out_value = data;
- return true;
- case DW_EH_PE_udata4:
- issigned = false;
- case DW_EH_PE_sdata4:
- if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) {
- return false;
- }
- break;
- default:
- ALOGE("unrecognized dwarf lower part encoding: 0x%x", encoding);
- return false;
- }
- /* Higher 4 bits is modifier */
- /* TODO: add more encodings if it becomes necessary */
- switch (encoding & 0xf0) {
- case 0:
- *out_value = data;
- break;
- case DW_EH_PE_pcrel:
- if (issigned) {
- *out_value = addr + (int32_t)data;
- } else {
- *out_value = addr + data;
- }
- break;
- /* Assuming ptr is correct base to calculate datarel */
- case DW_EH_PE_datarel:
- if (issigned) {
- *out_value = ptr + (int32_t)data;
- } else {
- *out_value = ptr + data;
- }
- break;
- default:
- ALOGE("unrecognized dwarf higher part encoding: 0x%x", encoding);
- return false;
- }
- return true;
-}
-
-/* Having PC find corresponding FDE by reading .eh_frame_hdr section data. */
-static uintptr_t find_fde(const memory_t* memory,
- const map_info_t* map_info_list, uintptr_t pc) {
- if (!pc) {
- ALOGV("find_fde: pc is zero, no eh_frame");
- return 0;
- }
- const map_info_t* mi = find_map_info(map_info_list, pc);
- if (!mi) {
- ALOGV("find_fde: no map info for pc:0x%x", pc);
- return 0;
- }
- const map_info_data_t* midata = mi->data;
- if (!midata) {
- ALOGV("find_fde: no eh_frame_hdr for map: start=0x%x, end=0x%x", mi->start, mi->end);
- return 0;
- }
-
- eh_frame_hdr_info_t eh_hdr_info;
- memset(&eh_hdr_info, 0, sizeof(eh_frame_hdr_info_t));
-
- /* Getting the first word of eh_frame_hdr:
- 1st byte is version;
- 2nd byte is encoding of pointer to eh_frames;
- 3rd byte is encoding of count of FDEs in lookup table;
- 4th byte is encoding of lookup table entries.
- */
- uintptr_t eh_frame_hdr = midata->eh_frame_hdr;
- uint32_t c = 0;
- if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.version, &c)) return 0;
- if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr_enc, &c)) return 0;
- if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_count_enc, &c)) return 0;
- if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_table_enc, &c)) return 0;
-
- /* TODO: 3rd byte can be DW_EH_PE_omit, that means no lookup table available and we should
- try to parse eh_frame instead. Not sure how often it may occur, skipping now.
- */
- if (eh_hdr_info.version != 1) {
- ALOGV("find_fde: eh_frame_hdr version %d is not supported", eh_hdr_info.version);
- return 0;
- }
- /* Getting the data:
- 2nd word is eh_frame pointer (normally not used, because lookup table has all we need);
- 3rd word is count of FDEs in the lookup table;
- starting from 4 word there is FDE lookup table (pairs of PC and FDE pointer) sorted by PC;
- */
- if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr, eh_hdr_info.eh_frame_ptr_enc, &c)) return 0;
- if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.fde_count, eh_hdr_info.fde_count_enc, &c)) return 0;
- ALOGV("find_fde: found %d FDEs", eh_hdr_info.fde_count);
-
- int32_t low = 0;
- int32_t high = eh_hdr_info.fde_count;
- uintptr_t start = 0;
- uintptr_t fde = 0;
- /* eh_frame_hdr + c points to lookup table at this point. */
- while (low <= high) {
- uint32_t mid = (high + low)/2;
- uint32_t entry = c + mid * 8;
- if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &entry)) return 0;
- if (pc <= start) {
- high = mid - 1;
- } else {
- low = mid + 1;
- }
- }
- /* Value found is at high. */
- if (high < 0) {
- ALOGV("find_fde: pc %x is out of FDE bounds: %x", pc, start);
- return 0;
- }
- c += high * 8;
- if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &c)) return 0;
- if (!read_dwarf(memory, eh_frame_hdr, &fde, eh_hdr_info.fde_table_enc, &c)) return 0;
- ALOGV("pc 0x%x, ENTRY %d: start=0x%x, fde=0x%x", pc, high, start, fde);
- return fde;
-}
-
-/* Execute single dwarf instruction and update dwarf state accordingly. */
-static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie_info,
- dwarf_state_t* dstate, uint32_t* cursor,
- dwarf_state_t* stack, uint8_t* stack_ptr) {
- uint8_t inst;
- uint8_t op = 0;
-
- if (!try_get_byte(memory, ptr, &inst, cursor)) {
- return false;
- }
- ALOGV("DW_CFA inst: 0x%x", inst);
-
- /* For some instructions upper 2 bits is opcode and lower 6 bits is operand. See dwarf-2.0 7.23. */
- if (inst & 0xc0) {
- op = inst & 0x3f;
- inst &= 0xc0;
- }
-
- switch ((dwarf_CFA)inst) {
- uint32_t reg = 0;
- uint32_t offset = 0;
- case DW_CFA_advance_loc:
- dstate->loc += op * cie_info->code_align;
- ALOGV("DW_CFA_advance_loc: %d to 0x%x", op, dstate->loc);
- break;
- case DW_CFA_offset:
- if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
- dstate->regs[op].rule = 'o';
- dstate->regs[op].value = offset * cie_info->data_align;
- ALOGV("DW_CFA_offset: r%d = o(%d)", op, dstate->regs[op].value);
- break;
- case DW_CFA_restore:
- dstate->regs[op].rule = stack->regs[op].rule;
- dstate->regs[op].value = stack->regs[op].value;
- ALOGV("DW_CFA_restore: r%d = %c(%d)", op, dstate->regs[op].rule, dstate->regs[op].value);
- break;
- case DW_CFA_nop:
- break;
- case DW_CFA_set_loc: // probably we don't have it on mips.
- if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false;
- if (offset < dstate->loc) {
- ALOGE("DW_CFA_set_loc: attempt to move location backward");
- return false;
- }
- dstate->loc = offset * cie_info->code_align;
- ALOGV("DW_CFA_set_loc: %d to 0x%x", offset * cie_info->code_align, dstate->loc);
- break;
- case DW_CFA_advance_loc1:
- if (!try_get_byte(memory, ptr, (uint8_t*)&offset, cursor)) return false;
- dstate->loc += (uint8_t)offset * cie_info->code_align;
- ALOGV("DW_CFA_advance_loc1: %d to 0x%x", (uint8_t)offset * cie_info->code_align, dstate->loc);
- break;
- case DW_CFA_advance_loc2:
- if (!try_get_xbytes(memory, ptr, &offset, 2, cursor)) return false;
- dstate->loc += (uint16_t)offset * cie_info->code_align;
- ALOGV("DW_CFA_advance_loc2: %d to 0x%x", (uint16_t)offset * cie_info->code_align, dstate->loc);
- break;
- case DW_CFA_advance_loc4:
- if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false;
- dstate->loc += offset * cie_info->code_align;
- ALOGV("DW_CFA_advance_loc4: %d to 0x%x", offset * cie_info->code_align, dstate->loc);
- break;
- case DW_CFA_offset_extended: // probably we don't have it on mips.
- if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
- if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
- if (reg >= DWARF_REGISTERS) {
- ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
- return false;
- }
- dstate->regs[reg].rule = 'o';
- dstate->regs[reg].value = offset * cie_info->data_align;
- ALOGV("DW_CFA_offset_extended: r%d = o(%d)", reg, dstate->regs[reg].value);
- break;
- case DW_CFA_restore_extended: // probably we don't have it on mips.
- if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
- if (reg >= DWARF_REGISTERS) {
- ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
- return false;
- }
- dstate->regs[reg].rule = stack->regs[reg].rule;
- dstate->regs[reg].value = stack->regs[reg].value;
- ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value);
- break;
- case DW_CFA_undefined: // probably we don't have it on mips.
- if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
- if (reg >= DWARF_REGISTERS) {
- ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
- return false;
- }
- dstate->regs[reg].rule = 'u';
- dstate->regs[reg].value = 0;
- ALOGV("DW_CFA_undefined: r%d", reg);
- break;
- case DW_CFA_same_value: // probably we don't have it on mips.
- if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
- if (reg >= DWARF_REGISTERS) {
- ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
- return false;
- }
- dstate->regs[reg].rule = 's';
- dstate->regs[reg].value = 0;
- ALOGV("DW_CFA_same_value: r%d", reg);
- break;
- case DW_CFA_register: // probably we don't have it on mips.
- if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
- /* that's new register actually, not offset */
- if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
- if (reg >= DWARF_REGISTERS || offset >= DWARF_REGISTERS) {
- ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS);
- return false;
- }
- dstate->regs[reg].rule = 'r';
- dstate->regs[reg].value = offset;
- ALOGV("DW_CFA_register: r%d = r(%d)", reg, dstate->regs[reg].value);
- break;
- case DW_CFA_remember_state:
- if (*stack_ptr == DWARF_STATES_STACK) {
- ALOGE("DW_CFA_remember_state: states stack overflow %d", *stack_ptr);
- return false;
- }
- stack[(*stack_ptr)++] = *dstate;
- ALOGV("DW_CFA_remember_state: stacktop moves to %d", *stack_ptr);
- break;
- case DW_CFA_restore_state:
- /* We have CIE state saved at 0 position. It's not supposed to be taken
- by DW_CFA_restore_state. */
- if (*stack_ptr == 1) {
- ALOGE("DW_CFA_restore_state: states stack is empty");
- return false;
- }
- /* Don't touch location on restore. */
- uintptr_t saveloc = dstate->loc;
- *dstate = stack[--*stack_ptr];
- dstate->loc = saveloc;
- ALOGV("DW_CFA_restore_state: stacktop moves to %d", *stack_ptr);
- break;
- case DW_CFA_def_cfa:
- if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
- if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
- dstate->cfa_reg = reg;
- dstate->cfa_off = offset;
- ALOGV("DW_CFA_def_cfa: %x(r%d)", offset, reg);
- break;
- case DW_CFA_def_cfa_register:
- if (!try_get_uleb128(memory, ptr, &reg, cursor)) {
- return false;
- }
- dstate->cfa_reg = reg;
- ALOGV("DW_CFA_def_cfa_register: r%d", reg);
- break;
- case DW_CFA_def_cfa_offset:
- if (!try_get_uleb128(memory, ptr, &offset, cursor)) {
- return false;
- }
- dstate->cfa_off = offset;
- ALOGV("DW_CFA_def_cfa_offset: %x", offset);
- break;
- default:
- ALOGE("unrecognized DW_CFA_* instruction: 0x%x", inst);
- return false;
- }
- return true;
-}
-
-/* Restoring particular register value based on dwarf state. */
-static bool get_old_register_value(const memory_t* memory, uint32_t cfa,
- dwarf_state_t* dstate, uint8_t reg,
- unwind_state_t* state, unwind_state_t* newstate) {
- uint32_t addr;
- switch (dstate->regs[reg].rule) {
- case 0:
- /* We don't have dstate updated for this register, so assuming value kept the same.
- Normally we should look into state and return current value as the old one
- but we don't have all registers in state to handle this properly */
- ALOGV("get_old_register_value: value of r%d is the same", reg);
- // for SP if it's not updated by dwarf rule we assume it's equal to CFA
- // for PC if it's not updated by dwarf rule we assume it's equal to RA
- if (reg == DWARF_SP) {
- ALOGV("get_old_register_value: adjusting sp to CFA: 0x%x", cfa);
- newstate->reg[reg] = cfa;
- } else if (reg == DWARF_PC) {
- ALOGV("get_old_register_value: adjusting PC to RA: 0x%x", newstate->reg[DWARF_RA]);
- newstate->reg[reg] = newstate->reg[DWARF_RA];
- } else {
- newstate->reg[reg] = state->reg[reg];
- }
- break;
- case 'o':
- addr = cfa + (int32_t)dstate->regs[reg].value;
- if (!try_get_word(memory, addr, &newstate->reg[reg])) {
- ALOGE("get_old_register_value: can't read from 0x%x", addr);
- return false;
- }
- ALOGV("get_old_register_value: r%d at 0x%x is 0x%x", reg, addr, newstate->reg[reg]);
- break;
- case 'r':
- /* We don't have all registers in state so don't even try to look at 'r' */
- ALOGE("get_old_register_value: register lookup not implemented yet");
- break;
- default:
- ALOGE("get_old_register_value: unexpected rule:%c value:%d for register %d",
- dstate->regs[reg].rule, (int32_t)dstate->regs[reg].value, reg);
- return false;
- }
- return true;
-}
-
-/* Updaing state based on dwarf state. */
-static bool update_state(const memory_t* memory, unwind_state_t* state,
- dwarf_state_t* dstate) {
- unwind_state_t newstate;
- /* We can restore more registers here if we need them. Meanwile doing minimal work here. */
- /* Getting CFA. */
- uintptr_t cfa = 0;
- if (dstate->cfa_reg == DWARF_SP) {
- cfa = state->reg[DWARF_SP] + dstate->cfa_off;
- } else if (dstate->cfa_reg == DWARF_FP) {
- cfa = state->reg[DWARF_FP] + dstate->cfa_off;
- } else {
- ALOGE("update_state: unexpected CFA register: %d", dstate->cfa_reg);
- return false;
- }
- ALOGV("update_state: new CFA: 0x%x", cfa);
-
- /* Update registers. Order is important to allow RA to propagate to PC */
- /* Getting FP. */
- if (!get_old_register_value(memory, cfa, dstate, DWARF_FP, state, &newstate)) return false;
- /* Getting SP. */
- if (!get_old_register_value(memory, cfa, dstate, DWARF_SP, state, &newstate)) return false;
- /* Getting RA. */
- if (!get_old_register_value(memory, cfa, dstate, DWARF_RA, state, &newstate)) return false;
- /* Getting PC. */
- if (!get_old_register_value(memory, cfa, dstate, DWARF_PC, state, &newstate)) return false;
-
- ALOGV("update_state: PC: 0x%x; restore PC: 0x%x", state->reg[DWARF_PC], newstate.reg[DWARF_PC]);
- ALOGV("update_state: RA: 0x%x; restore RA: 0x%x", state->reg[DWARF_RA], newstate.reg[DWARF_RA]);
- ALOGV("update_state: FP: 0x%x; restore FP: 0x%x", state->reg[DWARF_FP], newstate.reg[DWARF_FP]);
- ALOGV("update_state: SP: 0x%x; restore SP: 0x%x", state->reg[DWARF_SP], newstate.reg[DWARF_SP]);
-
- if (newstate.reg[DWARF_PC] == 0)
- return false;
-
- /* End backtrace if registers do not change */
- if ((state->reg[DWARF_PC] == newstate.reg[DWARF_PC]) &&
- (state->reg[DWARF_RA] == newstate.reg[DWARF_RA]) &&
- (state->reg[DWARF_FP] == newstate.reg[DWARF_FP]) &&
- (state->reg[DWARF_SP] == newstate.reg[DWARF_SP]))
- return false;
-
- *state = newstate;
- return true;
-}
-
-/* Execute CIE and FDE instructions for FDE found with find_fde. */
-static bool execute_fde(const memory_t* memory,
- uintptr_t fde,
- unwind_state_t* state) {
- uint32_t fde_length = 0;
- uint32_t cie_length = 0;
- uintptr_t cie = 0;
- uintptr_t cie_offset = 0;
- cie_info_t cie_i;
- cie_info_t* cie_info = &cie_i;
- fde_info_t fde_i;
- fde_info_t* fde_info = &fde_i;
- dwarf_state_t dwarf_state;
- dwarf_state_t* dstate = &dwarf_state;
- dwarf_state_t stack[DWARF_STATES_STACK];
- uint8_t stack_ptr = 0;
-
- memset(dstate, 0, sizeof(dwarf_state_t));
- memset(cie_info, 0, sizeof(cie_info_t));
- memset(fde_info, 0, sizeof(fde_info_t));
-
- /* Read common CIE or FDE area:
- 1st word is length;
- 2nd word is ID: 0 for CIE, CIE pointer for FDE.
- */
- if (!try_get_word(memory, fde, &fde_length)) {
- return false;
- }
- if ((int32_t)fde_length == -1) {
- ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet");
- return false;
- }
- if (!try_get_word(memory, fde + 4, &cie_offset)) {
- return false;
- }
- if (cie_offset == 0) {
- /* This is CIE. We shouldn't be here normally. */
- cie = fde;
- cie_length = fde_length;
- } else {
- /* Find CIE. */
- /* Positive cie_offset goes backward from current field. */
- cie = fde + 4 - cie_offset;
- if (!try_get_word(memory, cie, &cie_length)) {
- return false;
- }
- if ((int32_t)cie_length == -1) {
- ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet");
- return false;
- }
- if (!try_get_word(memory, cie + 4, &cie_offset)) {
- return false;
- }
- if (cie_offset != 0) {
- ALOGV("execute_fde: can't find CIE");
- return false;
- }
- }
- ALOGV("execute_fde: FDE length: %d", fde_length);
- ALOGV("execute_fde: CIE pointer: %x", cie);
- ALOGV("execute_fde: CIE length: %d", cie_length);
-
- /* Read CIE:
- Augmentation independent:
- 1st byte is version;
- next x bytes is /0 terminated augmentation string;
- next x bytes is unsigned LEB128 encoded code alignment factor;
- next x bytes is signed LEB128 encoded data alignment factor;
- next 1 (CIE version 1) or x (CIE version 3 unsigned LEB128) bytes is return register column;
- Augmentation dependent:
- if 'z' next x bytes is unsigned LEB128 encoded augmentation data size;
- if 'L' next 1 byte is LSDA encoding;
- if 'R' next 1 byte is FDE encoding;
- if 'S' CIE represents signal handler stack frame;
- if 'P' next 1 byte is personality encoding folowed by personality function pointer;
- Next x bytes is CIE program.
- */
-
- uint32_t c = 8;
- if (!try_get_byte(memory, cie, &cie_info->version, &c)) {
- return false;
- }
- ALOGV("execute_fde: CIE version: %d", cie_info->version);
- uint8_t ch;
- do {
- if (!try_get_byte(memory, cie, &ch, &c)) {
- return false;
- }
- switch (ch) {
- case '\0': break;
- case 'z': cie_info->aug_z = 1; break;
- case 'L': cie_info->aug_L = 1; break;
- case 'R': cie_info->aug_R = 1; break;
- case 'S': cie_info->aug_S = 1; break;
- case 'P': cie_info->aug_P = 1; break;
- default:
- ALOGV("execute_fde: Unrecognized CIE augmentation char: '%c'", ch);
- return false;
- break;
- }
- } while (ch);
- if (!try_get_uleb128(memory, cie, &cie_info->code_align, &c)) {
- return false;
- }
- if (!try_get_sleb128(memory, cie, &cie_info->data_align, &c)) {
- return false;
- }
- if (cie_info->version >= 3) {
- if (!try_get_uleb128(memory, cie, &cie_info->reg, &c)) {
- return false;
- }
- } else {
- if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->reg, &c)) {
- return false;
- }
- }
- ALOGV("execute_fde: CIE code alignment factor: %d", cie_info->code_align);
- ALOGV("execute_fde: CIE data alignment factor: %d", cie_info->data_align);
- if (cie_info->aug_z) {
- if (!try_get_uleb128(memory, cie, &cie_info->aug_z, &c)) {
- return false;
- }
- }
- if (cie_info->aug_L) {
- if (!try_get_byte(memory, cie, &cie_info->aug_L, &c)) {
- return false;
- }
- } else {
- /* Default encoding. */
- cie_info->aug_L = DW_EH_PE_absptr;
- }
- if (cie_info->aug_R) {
- if (!try_get_byte(memory, cie, &cie_info->aug_R, &c)) {
- return false;
- }
- } else {
- /* Default encoding. */
- cie_info->aug_R = DW_EH_PE_absptr;
- }
- if (cie_info->aug_P) {
- /* Get encoding of personality routine pointer. We don't use it now. */
- if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->aug_P, &c)) {
- return false;
- }
- /* Get routine pointer. */
- if (!read_dwarf(memory, cie, &cie_info->aug_P, (uint8_t)cie_info->aug_P, &c)) {
- return false;
- }
- }
- /* CIE program. */
- /* Length field itself (4 bytes) is not included into length. */
- stack[0] = *dstate;
- stack_ptr = 1;
- while (c < cie_length + 4) {
- if (!execute_dwarf(memory, cie, cie_info, dstate, &c, stack, &stack_ptr)) {
- return false;
- }
- }
-
- /* We went directly to CIE. Normally it shouldn't occur. */
- if (cie == fde) return true;
-
- /* Go back to FDE. */
- c = 8;
- /* Read FDE:
- Augmentation independent:
- next x bytes (encoded as specified in CIE) is FDE starting address;
- next x bytes (encoded as specified in CIE) is FDE number of instructions covered;
- Augmentation dependent:
- if 'z' next x bytes is unsigned LEB128 encoded augmentation data size;
- if 'L' next x bytes is LSDA pointer (encoded as specified in CIE);
- Next x bytes is FDE program.
- */
- if (!read_dwarf(memory, fde, &fde_info->start, (uint8_t)cie_info->aug_R, &c)) {
- return false;
- }
- dstate->loc = fde_info->start;
- ALOGV("execute_fde: FDE start: %x", dstate->loc);
- if (!read_dwarf(memory, fde, &fde_info->length, 0, &c)) {
- return false;
- }
- ALOGV("execute_fde: FDE length: %x", fde_info->length);
- if (cie_info->aug_z) {
- if (!try_get_uleb128(memory, fde, &fde_info->aug_z, &c)) {
- return false;
- }
- }
- if (cie_info->aug_L && cie_info->aug_L != DW_EH_PE_omit) {
- if (!read_dwarf(memory, fde, &fde_info->aug_L, cie_info->aug_L, &c)) {
- return false;
- }
- }
- /* FDE program. */
- /* Length field itself (4 bytes) is not included into length. */
- /* Save CIE state as 0 element of stack. Used by DW_CFA_restore. */
- stack[0] = *dstate;
- stack_ptr = 1;
- while (c < fde_length + 4 && state->reg[DWARF_PC] >= dstate->loc) {
- if (!execute_dwarf(memory, fde, cie_info, dstate, &c, stack, &stack_ptr)) {
- return false;
- }
- ALOGV("PC: %x, LOC: %x", state->reg[DWARF_PC], dstate->loc);
- }
-
- return update_state(memory, state, dstate);
-}
-
-static bool heuristic_state_update(const memory_t* memory, unwind_state_t* state)
-{
- bool found_start = false;
- int maxcheck = 1024;
- int32_t stack_size = 0;
- int32_t ra_offset = 0;
- dwarf_state_t dwarf_state;
- dwarf_state_t* dstate = &dwarf_state;
-
- static struct {
- uint32_t insn;
- uint32_t mask;
- } frame0sig[] = {
- {0x3c1c0000, 0xffff0000}, /* lui gp,xxxx */
- {0x279c0000, 0xffff0000}, /* addiu gp,gp,xxxx */
- {0x039fe021, 0xffffffff}, /* addu gp,gp,ra */
- };
- const int nframe0sig = sizeof(frame0sig)/sizeof(frame0sig[0]);
- int f0 = nframe0sig;
- memset(dstate, 0, sizeof(dwarf_state_t));
-
- /* Search code backwards looking for function prologue */
- for (uint32_t pc = state->reg[DWARF_PC]-4; maxcheck-- > 0 && !found_start; pc -= 4) {
- uint32_t op;
- int32_t immediate;
-
- if (!try_get_word(memory, pc, &op))
- return false;
-
- // ALOGV("@0x%08x: 0x%08x\n", pc, op);
-
- // Check for frame 0 signature
- if ((op & frame0sig[f0].mask) == frame0sig[f0].insn) {
- if (f0 == 0)
- return false;
- f0--;
- }
- else {
- f0 = nframe0sig;
- }
-
- switch (op & 0xffff0000) {
- case 0x27bd0000: // addiu sp, imm
- // looking for stack being decremented
- immediate = (((int32_t)op) << 16) >> 16;
- if (immediate < 0) {
- stack_size = -immediate;
- ALOGV("@0x%08x: found stack adjustment=%d\n", pc, stack_size);
- }
- break;
- case 0x039f0000: // e021
-
- case 0xafbf0000: // sw ra, imm(sp)
- ra_offset = (((int32_t)op) << 16) >> 16;
- ALOGV("@0x%08x: found ra offset=%d\n", pc, ra_offset);
- break;
- case 0x3c1c0000: // lui gp
- ALOGV("@0x%08x: found function boundary", pc);
- found_start = true;
- break;
- default:
- break;
- }
- }
-
- dstate->cfa_reg = DWARF_SP;
- dstate->cfa_off = stack_size;
-
- if (ra_offset) {
- dstate->regs[DWARF_RA].rule = 'o';
- dstate->regs[DWARF_RA].value = -stack_size + ra_offset;
- }
-
- return update_state(memory, state, dstate);
-}
-
-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;
-
- ALOGV("Unwinding tid: %d", memory->tid);
- ALOGV("PC: %x", state->reg[DWARF_PC]);
- ALOGV("RA: %x", state->reg[DWARF_RA]);
- ALOGV("FP: %x", state->reg[DWARF_FP]);
- ALOGV("SP: %x", state->reg[DWARF_SP]);
-
- for (size_t index = 0; returned_frames < max_depth; index++) {
- uintptr_t fde = find_fde(memory, map_info_list, state->reg[DWARF_PC]);
- backtrace_frame_t* frame = add_backtrace_entry(
- index ? rewind_pc_arch(memory, state->reg[DWARF_PC]) : state->reg[DWARF_PC],
- backtrace, ignore_depth, max_depth,
- &ignored_frames, &returned_frames);
- uint32_t stack_top = state->reg[DWARF_SP];
-
- if (fde) {
- /* Use FDE to update state */
- if (!execute_fde(memory, fde, state))
- break;
- }
- else {
- /* FDE is not found, update state heuristically */
- if (!heuristic_state_update(memory, state))
- break;
- }
-
- if (frame) {
- frame->stack_top = stack_top;
- if (stack_top < state->reg[DWARF_SP]) {
- frame->stack_size = state->reg[DWARF_SP] - stack_top;
- }
- }
- ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_SP], frame->stack_size);
- }
- return returned_frames;
-}
-
-ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), 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.reg[DWARF_PC] = uc->uc_mcontext.pc;
- state.reg[DWARF_RA] = uc->uc_mcontext.gregs[REG_RA];
- state.reg[DWARF_FP] = uc->uc_mcontext.gregs[REG_S8];
- state.reg[DWARF_SP] = uc->uc_mcontext.gregs[REG_SP];
-
- 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.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_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, &regs)) {
- return -1;
- }
-
- unwind_state_t state;
- state.reg[DWARF_PC] = regs.epc;
- state.reg[DWARF_RA] = regs.regs[REG_RA];
- state.reg[DWARF_FP] = regs.regs[REG_S8];
- state.reg[DWARF_SP] = regs.regs[REG_SP];
-
- 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.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]);
-
- memory_t memory;
- init_memory_ptrace(&memory, tid);
- return unwind_backtrace_common(&memory, context->map_info_list,
- &state, backtrace, ignore_depth, max_depth);
-}