diff options
Diffstat (limited to 'libcorkscrew/arch-mips/backtrace-mips.c')
-rw-r--r-- | libcorkscrew/arch-mips/backtrace-mips.c | 901 |
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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®s)) { - 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); -} |