diff options
Diffstat (limited to 'libcorkscrew/arch-mips')
-rw-r--r-- | libcorkscrew/arch-mips/backtrace-mips.c | 901 | ||||
-rw-r--r-- | libcorkscrew/arch-mips/dwarf.h | 187 | ||||
-rw-r--r-- | libcorkscrew/arch-mips/ptrace-mips.c | 77 |
3 files changed, 0 insertions, 1165 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); -} diff --git a/libcorkscrew/arch-mips/dwarf.h b/libcorkscrew/arch-mips/dwarf.h deleted file mode 100644 index 8504ea0..0000000 --- a/libcorkscrew/arch-mips/dwarf.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -/* - * Dwarf2 data encoding flags. - */ - -#define DW_EH_PE_absptr 0x00 -#define DW_EH_PE_omit 0xff -#define DW_EH_PE_uleb128 0x01 -#define DW_EH_PE_udata2 0x02 -#define DW_EH_PE_udata4 0x03 -#define DW_EH_PE_udata8 0x04 -#define DW_EH_PE_sleb128 0x09 -#define DW_EH_PE_sdata2 0x0A -#define DW_EH_PE_sdata4 0x0B -#define DW_EH_PE_sdata8 0x0C -#define DW_EH_PE_signed 0x08 -#define DW_EH_PE_pcrel 0x10 -#define DW_EH_PE_textrel 0x20 -#define DW_EH_PE_datarel 0x30 -#define DW_EH_PE_funcrel 0x40 -#define DW_EH_PE_aligned 0x50 -#define DW_EH_PE_indirect 0x80 - -/* - * Dwarf2 call frame instructions. - */ - -typedef enum { - DW_CFA_advance_loc = 0x40, - DW_CFA_offset = 0x80, - DW_CFA_restore = 0xc0, - DW_CFA_nop = 0x00, - DW_CFA_set_loc = 0x01, - DW_CFA_advance_loc1 = 0x02, - DW_CFA_advance_loc2 = 0x03, - DW_CFA_advance_loc4 = 0x04, - DW_CFA_offset_extended = 0x05, - DW_CFA_restore_extended = 0x06, - DW_CFA_undefined = 0x07, - DW_CFA_same_value = 0x08, - DW_CFA_register = 0x09, - DW_CFA_remember_state = 0x0a, - DW_CFA_restore_state = 0x0b, - DW_CFA_def_cfa = 0x0c, - DW_CFA_def_cfa_register = 0x0d, - DW_CFA_def_cfa_offset = 0x0e -} dwarf_CFA; - -/* - * eh_frame_hdr information. -*/ - -typedef struct { - uint8_t version; - uint8_t eh_frame_ptr_enc; - uint8_t fde_count_enc; - uint8_t fde_table_enc; - uintptr_t eh_frame_ptr; - uint32_t fde_count; -} eh_frame_hdr_info_t; - -/* - * CIE information. -*/ - -typedef struct { - uint8_t version; - uint32_t code_align; - uint32_t data_align; - uint32_t reg; - uint32_t aug_z; - uint8_t aug_L; - uint8_t aug_R; - uint8_t aug_S; - uint32_t aug_P; -} cie_info_t; - -/* - * FDE information. -*/ - -typedef struct { - uint32_t start; - uint32_t length; // number of instructions covered by FDE - uint32_t aug_z; - uint32_t aug_L; -} fde_info_t; - -/* - * Dwarf state. -*/ - -/* Stack of states: required for DW_CFA_remember_state/DW_CFA_restore_state - 30 should be enough */ -#define DWARF_STATES_STACK 30 - -typedef struct { - char rule; // rule: o - offset(value); r - register(value) - uint32_t value; // value -} reg_rule_t; - -/* Dwarf preserved number of registers for mips */ -typedef enum - { - UNW_MIPS_R0, - UNW_MIPS_R1, - UNW_MIPS_R2, - UNW_MIPS_R3, - UNW_MIPS_R4, - UNW_MIPS_R5, - UNW_MIPS_R6, - UNW_MIPS_R7, - UNW_MIPS_R8, - UNW_MIPS_R9, - UNW_MIPS_R10, - UNW_MIPS_R11, - UNW_MIPS_R12, - UNW_MIPS_R13, - UNW_MIPS_R14, - UNW_MIPS_R15, - UNW_MIPS_R16, - UNW_MIPS_R17, - UNW_MIPS_R18, - UNW_MIPS_R19, - UNW_MIPS_R20, - UNW_MIPS_R21, - UNW_MIPS_R22, - UNW_MIPS_R23, - UNW_MIPS_R24, - UNW_MIPS_R25, - UNW_MIPS_R26, - UNW_MIPS_R27, - UNW_MIPS_R28, - UNW_MIPS_R29, - UNW_MIPS_R30, - UNW_MIPS_R31, - - UNW_MIPS_PC = 34, - - /* FIXME: Other registers! */ - - /* For MIPS, the CFA is the value of SP (r29) at the call site in the - previous frame. */ - UNW_MIPS_CFA, - - UNW_TDEP_LASTREG, - - UNW_TDEP_LAST_REG = UNW_MIPS_R31, - - UNW_TDEP_IP = UNW_MIPS_R31, - UNW_TDEP_SP = UNW_MIPS_R29, - UNW_TDEP_EH = UNW_MIPS_R0 /* FIXME. */ - - } -mips_regnum_t; - -#define DWARF_REGISTERS UNW_TDEP_LASTREG - -typedef struct { - uintptr_t loc; // location (ip) - uint8_t cfa_reg; // index of register where CFA location stored - intptr_t cfa_off; // offset - reg_rule_t regs[DWARF_REGISTERS]; // dwarf preserved registers for mips -} dwarf_state_t; - -/* DWARF registers we are caring about. */ - - -#define DWARF_SP UNW_MIPS_R29 -#define DWARF_RA UNW_MIPS_R31 -#define DWARF_PC UNW_MIPS_PC -#define DWARF_FP UNW_MIPS_CFA /* FIXME is this correct? */ diff --git a/libcorkscrew/arch-mips/ptrace-mips.c b/libcorkscrew/arch-mips/ptrace-mips.c deleted file mode 100644 index ba3b60a..0000000 --- a/libcorkscrew/arch-mips/ptrace-mips.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../ptrace-arch.h" - -#include <stddef.h> -#include <elf.h> -#include <cutils/log.h> - -static void load_eh_frame_hdr(pid_t pid, map_info_t* mi, uintptr_t *eh_frame_hdr) { - uint32_t elf_phoff; - uint32_t elf_phentsize_ehsize; - uint32_t elf_shentsize_phnum; - - - try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff); - ALOGV("reading 0x%08x elf_phoff:%x", mi->start + offsetof(Elf32_Ehdr, e_phoff), elf_phoff); - try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), &elf_phentsize_ehsize); - ALOGV("reading 0x%08x elf_phentsize_ehsize:%x", mi->start + offsetof(Elf32_Ehdr, e_ehsize), elf_phentsize_ehsize); - try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), &elf_shentsize_phnum); - ALOGV("reading 0x%08x elf_shentsize_phnum:%x", mi->start + offsetof(Elf32_Ehdr, e_phnum), elf_shentsize_phnum); - - - - if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), - &elf_phentsize_ehsize) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), - &elf_shentsize_phnum)) { - uint32_t elf_phentsize = elf_phentsize_ehsize >> 16; - uint32_t elf_phnum = elf_shentsize_phnum & 0xffff; - for (uint32_t i = 0; i < elf_phnum; i++) { - uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize; - uint32_t elf_phdr_type; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) { - break; - } - if (elf_phdr_type == PT_GNU_EH_FRAME) { - uint32_t elf_phdr_offset; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset), - &elf_phdr_offset)) { - break; - } - *eh_frame_hdr = mi->start + elf_phdr_offset; - ALOGV("Parsed .eh_frame_hdr info for %s: start=0x%08x", mi->name, *eh_frame_hdr); - return; - } - } - } - *eh_frame_hdr = 0; -} - -void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) { - ALOGV("load_ptrace_map_info_data_arch"); - load_eh_frame_hdr(pid, mi, &data->eh_frame_hdr); -} - -void free_ptrace_map_info_data_arch(map_info_t* mi __attribute__((unused)), - map_info_data_t* data __attribute__((unused))) { - ALOGV("free_ptrace_map_info_data_arch"); -} |