| /******************************************************************************* |
| * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * and Eclipse Distribution License v1.0 which accompany this distribution. |
| * The Eclipse Public License is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * Contributors: |
| * Wind River Systems - initial API and implementation |
| *******************************************************************************/ |
| |
| /* |
| * This module implements handling of .debug_frame and .eh_frame sections. |
| * |
| * Functions in this module use exceptions to report errors, see exceptions.h |
| */ |
| |
| #include <config.h> |
| |
| #if ENABLE_ELF |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <framework/exceptions.h> |
| #include <framework/myalloc.h> |
| #include <framework/trace.h> |
| #include <services/dwarf.h> |
| #include <services/dwarfio.h> |
| #include <services/dwarfframe.h> |
| |
| #define EH_PE_omit 0xff |
| |
| #define EH_PE_absptr 0x00 |
| #define EH_PE_uleb128 0x01 |
| #define EH_PE_udata2 0x02 |
| #define EH_PE_udata4 0x03 |
| #define EH_PE_udata8 0x04 |
| #define EH_PE_sleb128 0x09 |
| #define EH_PE_sdata2 0x0a |
| #define EH_PE_sdata4 0x0b |
| #define EH_PE_sdata8 0x0c |
| |
| #define EH_PB_pcrel 0x01 |
| #define EH_PB_textrel 0x02 |
| #define EH_PB_datarel 0x03 |
| #define EH_PB_funcrel 0x04 |
| #define EH_PB_aligned 0x05 |
| |
| #define EH_PE_indirect 0x80 |
| |
| #define RULE_OFFSET 1 |
| #define RULE_SAME_VALUE 2 |
| #define RULE_REGISTER 3 |
| #define RULE_EXPRESSION 4 |
| #define RULE_VAL_OFFSET 5 |
| #define RULE_VAL_EXPRESSION 6 |
| |
| typedef struct RegisterRules { |
| int rule; |
| I4_T offset; |
| U8_T expression; |
| } RegisterRules; |
| |
| typedef struct StackFrameRegisters { |
| RegisterRules * regs; |
| int regs_cnt; |
| int regs_max; |
| } StackFrameRegisters; |
| |
| typedef struct StackFrameRules { |
| Context * ctx; |
| ELF_Section * section; |
| RegisterIdScope reg_id_scope; |
| int eh_frame; |
| U1_T version; |
| U1_T address_size; |
| U1_T segment_size; |
| U4_T code_alignment; |
| I4_T data_alignment; |
| U8_T cie_pos; |
| char * cie_aug; |
| U8_T cie_eh_data; |
| ELF_Section * cie_eh_data_section; |
| U4_T fde_aug_length; |
| U1_T * fde_aug_data; |
| U1_T lsda_encoding; |
| U1_T prh_encoding; |
| U1_T addr_encoding; |
| U8_T location; |
| int return_address_register; |
| int cfa_rule; |
| I4_T cfa_offset; |
| U4_T cfa_register; |
| U8_T cfa_expression; |
| } StackFrameRules; |
| |
| static StackFrameRegisters frame_regs; |
| static StackFrameRegisters cie_regs; |
| static StackFrameRegisters * regs_stack = NULL; |
| static int regs_stack_max = 0; |
| static int regs_stack_pos = 0; |
| |
| static StackFrameRules rules; |
| |
| U8_T dwarf_stack_trace_addr = 0; |
| U8_T dwarf_stack_trace_size = 0; |
| |
| StackTracingCommandSequence * dwarf_stack_trace_fp = NULL; |
| |
| int dwarf_stack_trace_regs_cnt = 0; |
| StackTracingCommandSequence ** dwarf_stack_trace_regs = NULL; |
| |
| static int trace_regs_max = 0; |
| static int trace_cmds_max = 0; |
| static int trace_cmds_cnt = 0; |
| static StackTracingCommand * trace_cmds = NULL; |
| |
| static RegisterRules * get_reg(StackFrameRegisters * regs, int reg) { |
| while (reg >= regs->regs_max) { |
| regs->regs_max = regs->regs_max == 0 ? 32 : regs->regs_max * 2; |
| regs->regs = (RegisterRules *)loc_realloc(regs->regs, sizeof(RegisterRules) * regs->regs_max); |
| } |
| while (regs->regs_cnt <= reg) { |
| int n = regs->regs_cnt++; |
| memset(regs->regs + n, 0, sizeof(RegisterRules)); |
| /* Architecture specific implied rules */ |
| switch (rules.reg_id_scope.machine) { |
| case EM_386: |
| switch (n) { |
| case 4: /* SP */ |
| regs->regs[n].rule = RULE_VAL_OFFSET; |
| break; |
| case 3: /* BX */ |
| case 5: /* BP */ |
| case 6: /* SI */ |
| case 7: /* DI */ |
| regs->regs[n].rule = RULE_SAME_VALUE; |
| break; |
| } |
| break; |
| case EM_X86_64: |
| switch (n) { |
| case 3: /* BX */ |
| case 6: /* BP */ |
| case 12: /* R12 */ |
| case 13: /* R13 */ |
| case 14: /* R14 */ |
| case 15: /* R15 */ |
| regs->regs[n].rule = RULE_SAME_VALUE; |
| break; |
| case 7: /* SP */ |
| regs->regs[n].rule = RULE_VAL_OFFSET; |
| break; |
| } |
| break; |
| case EM_PPC: |
| if (n == 1) { |
| regs->regs[n].rule = RULE_VAL_OFFSET; |
| } |
| else if ((n >= 14 && n <= 31) || (n >= 46 && n <= 63)) { |
| regs->regs[n].rule = RULE_SAME_VALUE; |
| } |
| else if (n == rules.return_address_register) { |
| regs->regs[n].rule = RULE_REGISTER; |
| regs->regs[n].offset = 108; |
| } |
| break; |
| } |
| } |
| return regs->regs + reg; |
| } |
| |
| static void copy_register_rules(StackFrameRegisters * dst, StackFrameRegisters * src) { |
| int n; |
| dst->regs_cnt = 0; |
| for (n = 0; n < src->regs_cnt; n++) { |
| *get_reg(dst, n) = *get_reg(src, n); |
| } |
| } |
| |
| static StackFrameRegisters * get_regs_stack_item(int n) { |
| while (n >= regs_stack_max) { |
| int max = regs_stack_max; |
| regs_stack_max = regs_stack_max == 0 ? 8 : regs_stack_max * 2; |
| regs_stack = (StackFrameRegisters *)loc_realloc(regs_stack, sizeof(StackFrameRegisters) * regs_stack_max); |
| memset(regs_stack + max, 0, sizeof(StackFrameRegisters) * (regs_stack_max - max)); |
| } |
| return regs_stack + n; |
| } |
| |
| static U8_T read_frame_data_pointer(U1_T encoding, ELF_Section ** sec) { |
| U8_T v = 0; |
| if (encoding != EH_PE_omit) { |
| U8_T pos = dio_GetPos(); |
| switch (encoding & 0xf) { |
| case EH_PE_absptr: |
| v = dio_ReadAddress(sec); |
| break; |
| case EH_PE_uleb128: |
| v = dio_ReadU8LEB128(); |
| break; |
| case EH_PE_udata2: |
| v = dio_ReadU2(); |
| break; |
| case EH_PE_udata4: |
| v = dio_ReadU4(); |
| break; |
| case EH_PE_udata8: |
| v = dio_ReadU8(); |
| break; |
| case EH_PE_sleb128: |
| v = dio_ReadS8LEB128(); |
| break; |
| case EH_PE_sdata2: |
| v = (I2_T)dio_ReadU2(); |
| break; |
| case EH_PE_sdata4: |
| v = (I4_T)dio_ReadU4(); |
| break; |
| case EH_PE_sdata8: |
| v = (I8_T)dio_ReadU8(); |
| break; |
| default: |
| str_exception(ERR_INV_DWARF, "Unknown encoding of .eh_frame section pointers"); |
| break; |
| } |
| if (v != 0 && sec != NULL) { |
| switch ((encoding >> 4) & 0x7) { |
| case 0: |
| break; |
| case EH_PB_pcrel: |
| *sec = rules.section; |
| v += rules.section->addr + pos; |
| break; |
| case EH_PB_datarel: |
| *sec = rules.section; |
| v += rules.section->addr; |
| break; |
| case EH_PB_textrel: |
| case EH_PB_funcrel: |
| case EH_PB_aligned: |
| default: |
| str_exception(ERR_INV_DWARF, "Unknown encoding of .eh_frame section pointers"); |
| break; |
| } |
| if (encoding & EH_PE_indirect) { |
| unsigned idx; |
| ELF_File * file = rules.section->file; |
| size_t size = rules.address_size; |
| U8_T res = 0; |
| for (idx = 1; idx < file->section_cnt; idx++) { |
| ELF_Section * sec = file->sections + idx; |
| if ((sec->flags & SHF_ALLOC) == 0) continue; |
| if (sec->addr <= v && sec->addr + sec->size >= v + size) { |
| U1_T * p; |
| size_t i; |
| if (sec->data == NULL && elf_load(sec) < 0) exception(errno); |
| p = (U1_T *)sec->data + (uintptr_t)(v - sec->addr); |
| for (i = 0; i < size; i++) { |
| res = (res << 8) | p[file->big_endian ? i : size - i - 1]; |
| } |
| break; |
| } |
| } |
| v = res; |
| } |
| } |
| } |
| return v; |
| } |
| |
| static void exec_stack_frame_instruction(void) { |
| RegisterRules * reg; |
| U4_T n; |
| U1_T op = dio_ReadU1(); |
| switch (op) { |
| case 0x00: /* DW_CFA_nop */ |
| break; |
| case 0x01: /* DW_CFA_set_loc */ |
| rules.location = read_frame_data_pointer(rules.addr_encoding, 0); |
| break; |
| case 0x02: /* DW_CFA_advance_loc1 */ |
| rules.location += dio_ReadU1() * rules.code_alignment; |
| break; |
| case 0x03: /* DW_CFA_advance_loc2 */ |
| rules.location += dio_ReadU2() * rules.code_alignment; |
| break; |
| case 0x04: /* DW_CFA_advance_loc4 */ |
| rules.location += dio_ReadU4() * rules.code_alignment; |
| break; |
| case 0x05: /* DW_CFA_offset_extended */ |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_OFFSET; |
| reg->offset = dio_ReadULEB128() * rules.data_alignment; |
| break; |
| case 0x06: /* DW_CFA_restore_extended */ |
| n = dio_ReadULEB128(); |
| reg = get_reg(&frame_regs, n); |
| *reg = *get_reg(&cie_regs, n); |
| break; |
| case 0x07: /* DW_CFA_undefined */ |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| memset(reg, 0, sizeof(*reg)); |
| break; |
| case 0x08: /* DW_CFA_same_value */ |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_SAME_VALUE; |
| break; |
| case 0x09: /* DW_CFA_register */ |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_REGISTER; |
| reg->offset = dio_ReadULEB128(); |
| break; |
| case 0x0a: /* DW_CFA_remember_state */ |
| copy_register_rules(get_regs_stack_item(regs_stack_pos++), &frame_regs); |
| break; |
| case 0x0b: /* DW_CFA_restore_state */ |
| if (regs_stack_pos <= 0) { |
| str_exception(ERR_INV_DWARF, "Invalid DW_CFA_restore_state instruction"); |
| } |
| copy_register_rules(&frame_regs, get_regs_stack_item(--regs_stack_pos)); |
| break; |
| case 0x0c: /* DW_CFA_def_cfa */ |
| rules.cfa_rule = RULE_OFFSET; |
| rules.cfa_register = dio_ReadULEB128(); |
| rules.cfa_offset = dio_ReadULEB128(); |
| break; |
| case 0x0d: /* DW_CFA_def_cfa_register */ |
| rules.cfa_rule = RULE_OFFSET; |
| rules.cfa_register = dio_ReadULEB128(); |
| break; |
| case 0x0e: /* DW_CFA_def_cfa_offset */ |
| rules.cfa_rule = RULE_OFFSET; |
| rules.cfa_offset = dio_ReadULEB128(); |
| break; |
| case 0x0f: /* DW_CFA_def_cfa_expression */ |
| rules.cfa_rule = RULE_EXPRESSION; |
| rules.cfa_offset = dio_ReadULEB128(); |
| rules.cfa_expression = dio_GetPos(); |
| dio_Skip(rules.cfa_offset); |
| break; |
| case 0x10: /* DW_CFA_expression */ |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_EXPRESSION; |
| reg->offset = dio_ReadULEB128(); |
| reg->expression = dio_GetPos(); |
| dio_Skip(reg->offset); |
| break; |
| case 0x11: /* DW_CFA_offset_extended_sf */ |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_OFFSET; |
| reg->offset = dio_ReadSLEB128() * rules.data_alignment; |
| break; |
| case 0x12: /* DW_CFA_def_cfa_sf */ |
| rules.cfa_rule = RULE_OFFSET; |
| rules.cfa_register = dio_ReadULEB128(); |
| rules.cfa_offset = dio_ReadSLEB128() * rules.data_alignment; |
| break; |
| case 0x13: /* DW_CFA_def_cfa_offset_sf */ |
| rules.cfa_rule = RULE_OFFSET; |
| rules.cfa_offset = dio_ReadSLEB128() * rules.data_alignment; |
| break; |
| case 0x14: /* DW_CFA_val_offset */ |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_VAL_OFFSET; |
| reg->offset = dio_ReadULEB128() * rules.data_alignment; |
| break; |
| case 0x15: /* DW_CFA_val_offset_sf */ |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_VAL_OFFSET; |
| reg->offset = dio_ReadSLEB128() * rules.data_alignment; |
| break; |
| case 0x16: /* DW_CFA_val_expression */ |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_VAL_EXPRESSION; |
| reg->offset = dio_ReadULEB128(); |
| reg->expression = dio_GetPos(); |
| dio_Skip(reg->offset); |
| break; |
| case 0x2e: /* DW_CFA_GNU_args_size */ |
| /* This instruction specifies the total size of the arguments |
| * which have been pushed onto the stack. Not used by the debugger. */ |
| dio_ReadULEB128(); |
| break; |
| case 0x2f: /* DW_CFA_GNU_negative_offset_extended */ |
| /* This instruction is identical to DW_CFA_offset_extended_sf |
| * except that the operand is subtracted to produce the offset. */ |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_OFFSET; |
| reg->offset = -dio_ReadSLEB128() * rules.data_alignment; |
| break; |
| default: |
| switch (op >> 6) { |
| case 0: |
| str_exception(ERR_INV_DWARF, "Unsupported instruction in Call Frame Information"); |
| break; |
| case 1: /* DW_CFA_advance_loc */ |
| rules.location += (op & 0x3f) * rules.code_alignment; |
| break; |
| case 2: /* DW_CFA_offset */ |
| reg = get_reg(&frame_regs, op & 0x3f); |
| reg->rule = RULE_OFFSET; |
| reg->offset = dio_ReadULEB128() * rules.data_alignment; |
| break; |
| case 3: /* DW_CFA_restore */ |
| n = op & 0x3f; |
| reg = get_reg(&frame_regs, n); |
| *reg = *get_reg(&cie_regs, n); |
| break; |
| } |
| } |
| } |
| |
| static StackTracingCommand * add_command(int op) { |
| StackTracingCommand * cmd = NULL; |
| if (trace_cmds_cnt >= trace_cmds_max) { |
| trace_cmds_max += 16; |
| trace_cmds = (StackTracingCommand *)loc_realloc(trace_cmds, trace_cmds_max * sizeof(StackTracingCommand)); |
| } |
| cmd = trace_cmds + trace_cmds_cnt++; |
| memset(cmd, 0, sizeof(*cmd)); |
| cmd->cmd = op; |
| return cmd; |
| } |
| |
| static void add_command_sequence(StackTracingCommandSequence ** ptr, RegisterDefinition * reg) { |
| StackTracingCommandSequence * seq = *ptr; |
| if (seq == NULL || seq->cmds_max < trace_cmds_cnt) { |
| *ptr = seq = (StackTracingCommandSequence *)loc_realloc(seq, sizeof(StackTracingCommandSequence) + (trace_cmds_cnt - 1) * sizeof(StackTracingCommand)); |
| seq->cmds_max = trace_cmds_cnt; |
| } |
| seq->reg = reg; |
| seq->cmds_cnt = trace_cmds_cnt; |
| memcpy(seq->cmds, trace_cmds, trace_cmds_cnt * sizeof(StackTracingCommand)); |
| } |
| |
| static void add_dwarf_expression_commands(U8_T cmds_offs, U4_T cmds_size) { |
| dio_EnterSection(NULL, rules.section, cmds_offs); |
| while (dio_GetPos() < cmds_offs + cmds_size) { |
| U1_T op = dio_ReadU1(); |
| |
| switch (op) { |
| case OP_addr: |
| { |
| ELF_Section * section = NULL; |
| U8_T lt_addr = dio_ReadAddress(§ion); |
| ContextAddress rt_addr = elf_map_to_run_time_address( |
| rules.ctx, rules.section->file, section, (ContextAddress)lt_addr); |
| if (rt_addr == 0) str_exception(ERR_INV_DWARF, "object has no RT address"); |
| add_command(SFT_CMD_NUMBER)->num = rt_addr; |
| } |
| break; |
| case OP_deref: |
| { |
| StackTracingCommand * cmd = add_command(SFT_CMD_DEREF); |
| cmd->size = rules.address_size; |
| cmd->big_endian = rules.section->file->big_endian; |
| } |
| break; |
| case OP_deref_size: |
| { |
| StackTracingCommand * cmd = add_command(SFT_CMD_DEREF); |
| cmd->size = dio_ReadU1(); |
| cmd->big_endian = rules.section->file->big_endian; |
| } |
| break; |
| case OP_const1u: |
| add_command(SFT_CMD_NUMBER)->num = dio_ReadU1(); |
| break; |
| case OP_const1s: |
| add_command(SFT_CMD_NUMBER)->num = (I1_T)dio_ReadU1(); |
| break; |
| case OP_const2u: |
| add_command(SFT_CMD_NUMBER)->num = dio_ReadU2(); |
| break; |
| case OP_const2s: |
| add_command(SFT_CMD_NUMBER)->num = (I2_T)dio_ReadU2(); |
| break; |
| case OP_const4u: |
| add_command(SFT_CMD_NUMBER)->num = dio_ReadU4(); |
| break; |
| case OP_const4s: |
| add_command(SFT_CMD_NUMBER)->num = (I4_T)dio_ReadU4(); |
| break; |
| case OP_const8u: |
| add_command(SFT_CMD_NUMBER)->num = dio_ReadU8(); |
| break; |
| case OP_const8s: |
| add_command(SFT_CMD_NUMBER)->num = (I8_T)dio_ReadU8(); |
| break; |
| case OP_constu: |
| add_command(SFT_CMD_NUMBER)->num = dio_ReadU8LEB128(); |
| break; |
| case OP_consts: |
| add_command(SFT_CMD_NUMBER)->num = dio_ReadS8LEB128(); |
| break; |
| case OP_and: |
| add_command(SFT_CMD_AND); |
| break; |
| case OP_minus: |
| add_command(SFT_CMD_SUB); |
| break; |
| case OP_or: |
| add_command(SFT_CMD_OR); |
| break; |
| case OP_plus: |
| add_command(SFT_CMD_ADD); |
| break; |
| case OP_plus_uconst: |
| add_command(SFT_CMD_NUMBER)->num = dio_ReadU8LEB128(); |
| add_command(SFT_CMD_ADD); |
| break; |
| case OP_lit0: |
| case OP_lit1: |
| case OP_lit2: |
| case OP_lit3: |
| case OP_lit4: |
| case OP_lit5: |
| case OP_lit6: |
| case OP_lit7: |
| case OP_lit8: |
| case OP_lit9: |
| case OP_lit10: |
| case OP_lit11: |
| case OP_lit12: |
| case OP_lit13: |
| case OP_lit14: |
| case OP_lit15: |
| case OP_lit16: |
| case OP_lit17: |
| case OP_lit18: |
| case OP_lit19: |
| case OP_lit20: |
| case OP_lit21: |
| case OP_lit22: |
| case OP_lit23: |
| case OP_lit24: |
| case OP_lit25: |
| case OP_lit26: |
| case OP_lit27: |
| case OP_lit28: |
| case OP_lit29: |
| case OP_lit30: |
| case OP_lit31: |
| add_command(SFT_CMD_NUMBER)->num = op - OP_lit0; |
| break; |
| case OP_breg0: |
| case OP_breg1: |
| case OP_breg2: |
| case OP_breg3: |
| case OP_breg4: |
| case OP_breg5: |
| case OP_breg6: |
| case OP_breg7: |
| case OP_breg8: |
| case OP_breg9: |
| case OP_breg10: |
| case OP_breg11: |
| case OP_breg12: |
| case OP_breg13: |
| case OP_breg14: |
| case OP_breg15: |
| case OP_breg16: |
| case OP_breg17: |
| case OP_breg18: |
| case OP_breg19: |
| case OP_breg20: |
| case OP_breg21: |
| case OP_breg22: |
| case OP_breg23: |
| case OP_breg24: |
| case OP_breg25: |
| case OP_breg26: |
| case OP_breg27: |
| case OP_breg28: |
| case OP_breg29: |
| case OP_breg30: |
| case OP_breg31: |
| { |
| I8_T offs = dio_ReadS8LEB128(); |
| RegisterDefinition * def = get_reg_by_id(rules.ctx, op - OP_breg0, &rules.reg_id_scope); |
| if (def == NULL) str_exception(errno, "Cannot read DWARF frame info"); |
| add_command(SFT_CMD_REGISTER)->reg = def; |
| if (offs != 0) { |
| add_command(SFT_CMD_NUMBER)->num = offs; |
| add_command(SFT_CMD_ADD); |
| } |
| } |
| break; |
| case OP_nop: |
| break; |
| default: |
| trace(LOG_ALWAYS, "Unsupported DWARF expression op 0x%02x", op); |
| str_exception(ERR_UNSUPPORTED, "Unsupported DWARF expression op"); |
| } |
| } |
| } |
| |
| static void generate_register_commands(RegisterRules * reg, RegisterDefinition * dst_reg_def, RegisterDefinition * src_reg_def) { |
| if (dst_reg_def == NULL) return; |
| trace_cmds_cnt = 0; |
| switch (reg->rule) { |
| case RULE_VAL_OFFSET: |
| case RULE_OFFSET: |
| add_command(SFT_CMD_FP); |
| if (reg->offset != 0) { |
| add_command(SFT_CMD_NUMBER)->num = reg->offset; |
| add_command(SFT_CMD_ADD); |
| } |
| if (reg->rule == RULE_OFFSET) { |
| StackTracingCommand * cmd = add_command(SFT_CMD_DEREF); |
| cmd->size = dst_reg_def->size; |
| if (cmd->size > rules.address_size) cmd->size = rules.address_size; |
| cmd->big_endian = rules.section->file->big_endian; |
| } |
| break; |
| case RULE_SAME_VALUE: |
| if (src_reg_def == NULL) return; |
| add_command(SFT_CMD_REGISTER)->reg = src_reg_def; |
| break; |
| case RULE_REGISTER: |
| { |
| RegisterDefinition * src_sef = get_reg_by_id(rules.ctx, reg->offset, &rules.reg_id_scope); |
| if (src_sef != NULL) add_command(SFT_CMD_REGISTER)->reg = src_sef; |
| } |
| break; |
| case RULE_EXPRESSION: |
| case RULE_VAL_EXPRESSION: |
| add_command(SFT_CMD_FP); |
| add_dwarf_expression_commands(reg->expression, reg->offset); |
| if (reg->rule == RULE_EXPRESSION) { |
| StackTracingCommand * cmd = add_command(SFT_CMD_DEREF); |
| cmd->size = dst_reg_def->size; |
| if (cmd->size > rules.address_size) cmd->size = rules.address_size; |
| cmd->big_endian = rules.section->file->big_endian; |
| } |
| break; |
| default: |
| str_exception(ERR_INV_DWARF, "Invalid .debug_frame"); |
| break; |
| } |
| if (dwarf_stack_trace_regs_cnt >= trace_regs_max) { |
| int i; |
| trace_regs_max += 16; |
| dwarf_stack_trace_regs = (StackTracingCommandSequence **)loc_realloc(dwarf_stack_trace_regs, trace_regs_max * sizeof(StackTracingCommandSequence *)); |
| for (i = dwarf_stack_trace_regs_cnt; i < trace_regs_max; i++) dwarf_stack_trace_regs[i] = NULL; |
| } |
| if (trace_cmds_cnt == 0) return; |
| add_command_sequence(dwarf_stack_trace_regs + dwarf_stack_trace_regs_cnt++, dst_reg_def); |
| } |
| |
| static void generate_commands(void) { |
| int i; |
| RegisterRules * reg; |
| RegisterDefinition * reg_def; |
| |
| reg = get_reg(&frame_regs, rules.return_address_register); |
| if (reg->rule != 0) { |
| reg_def = get_reg_by_id(rules.ctx, rules.return_address_register, &rules.reg_id_scope); |
| generate_register_commands(reg, get_PC_definition(rules.ctx), reg_def); |
| } |
| for (i = 0; i < frame_regs.regs_cnt; i++) { |
| if (i == rules.return_address_register) continue; |
| reg = get_reg(&frame_regs, i); |
| if (reg->rule == 0) continue; |
| reg_def = get_reg_by_id(rules.ctx, i, &rules.reg_id_scope); |
| generate_register_commands(reg, reg_def, reg_def); |
| } |
| |
| trace_cmds_cnt = 0; |
| switch (rules.cfa_rule) { |
| case RULE_OFFSET: |
| reg_def = get_reg_by_id(rules.ctx, rules.cfa_register, &rules.reg_id_scope); |
| if (reg_def != NULL) { |
| add_command(SFT_CMD_REGISTER)->reg = reg_def; |
| if (rules.cfa_offset != 0) { |
| add_command(SFT_CMD_NUMBER)->num = rules.cfa_offset; |
| add_command(SFT_CMD_ADD); |
| } |
| } |
| break; |
| case RULE_EXPRESSION: |
| add_dwarf_expression_commands(rules.cfa_expression, rules.cfa_offset); |
| break; |
| default: |
| str_exception(ERR_INV_DWARF, "Invalid .debug_frame"); |
| break; |
| } |
| add_command_sequence(&dwarf_stack_trace_fp, NULL); |
| } |
| |
| static int generate_plt_section_commands(U8_T offs) { |
| RegisterRules * reg = NULL; |
| |
| cie_regs.regs_cnt = 0; |
| frame_regs.regs_cnt = 0; |
| switch (rules.reg_id_scope.machine) { |
| case EM_386: |
| rules.cfa_rule = RULE_OFFSET; |
| rules.cfa_register = 4; /* esp */ |
| if (offs == 0) { |
| rules.cfa_offset = 8; |
| } |
| else if (offs < 16) { |
| rules.cfa_offset = 12; |
| } |
| else if ((offs - 16) % 16 < 11) { |
| rules.cfa_offset = 4; |
| } |
| else { |
| rules.cfa_offset = 8; |
| } |
| rules.return_address_register = 8; /* eip */ |
| reg = get_reg(&frame_regs, rules.return_address_register); |
| reg->rule = RULE_OFFSET; |
| reg->offset = -4; |
| generate_commands(); |
| return 1; |
| case EM_X86_64: |
| rules.cfa_rule = RULE_OFFSET; |
| rules.cfa_register = 7; /* rsp */ |
| if (offs == 0) { |
| rules.cfa_offset = 16; |
| } |
| else if (offs < 16) { |
| rules.cfa_offset = 24; |
| } |
| else if ((offs - 16) % 16 < 11) { |
| rules.cfa_offset = 8; |
| } |
| else { |
| rules.cfa_offset = 16; |
| } |
| rules.return_address_register = 16; /* rip */ |
| reg = get_reg(&frame_regs, rules.return_address_register); |
| reg->rule = RULE_OFFSET; |
| reg->offset = -8; |
| generate_commands(); |
| return 1; |
| case EM_PPC: |
| rules.return_address_register = 108; /* LR */ |
| rules.cfa_rule = RULE_OFFSET; |
| rules.cfa_register = 1; /* R1 */ |
| rules.cfa_offset = 0; |
| generate_commands(); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static void read_frame_cie(U8_T fde_pos, U8_T pos) { |
| int cie_dwarf64 = 0; |
| U8_T saved_pos = dio_GetPos(); |
| U8_T cie_length = 0; |
| U8_T cie_end = 0; |
| |
| rules.cie_pos = pos; |
| if (pos >= rules.section->size) { |
| char msg[256]; |
| snprintf(msg, sizeof(msg), |
| "Invalid CIE pointer 0x%" PRIX64 |
| " in FDE at 0x%" PRIX64, pos, fde_pos); |
| str_exception(ERR_INV_DWARF, msg); |
| } |
| dio_Skip(pos - dio_GetPos()); |
| cie_length = dio_ReadU4(); |
| if (cie_length == ~(U4_T)0) { |
| cie_length = dio_ReadU8(); |
| cie_dwarf64 = 1; |
| } |
| cie_end = dio_GetPos() + cie_length; |
| dio_Skip(cie_dwarf64 ? 8 : 4); |
| rules.version = dio_ReadU1(); |
| if (rules.version != 1 && rules.version != 3 && rules.version != 4) { |
| str_exception(ERR_INV_DWARF, "Unsupported version of Call Frame Information"); |
| } |
| rules.cie_aug = dio_ReadString(); |
| if (rules.cie_aug != NULL && strcmp(rules.cie_aug, "eh") == 0) { |
| rules.cie_eh_data = dio_ReadAddress(&rules.cie_eh_data_section); |
| } |
| if (rules.version >= 4) { |
| rules.address_size = dio_ReadU1(); |
| rules.segment_size = dio_ReadU1(); |
| } |
| else { |
| rules.address_size = rules.section->file->elf64 ? 8 : 4; |
| rules.segment_size = 0; |
| } |
| if (rules.segment_size != 0) { |
| str_exception(ERR_INV_DWARF, "Unsupported Call Frame Information: segment size != 0"); |
| } |
| rules.code_alignment = dio_ReadULEB128(); |
| rules.data_alignment = dio_ReadSLEB128(); |
| rules.return_address_register = dio_ReadULEB128(); |
| rules.lsda_encoding = 0; |
| rules.prh_encoding = 0; |
| rules.addr_encoding = 0; |
| if (rules.cie_aug != NULL && rules.cie_aug[0] == 'z') { |
| U4_T aug_length = dio_ReadULEB128(); |
| U8_T aug_pos = dio_GetPos(); |
| char * p = rules.cie_aug + 1; |
| while (*p) { |
| switch (*p++) { |
| case 'L': |
| rules.lsda_encoding = dio_ReadU1(); |
| break; |
| case 'P': |
| rules.prh_encoding = dio_ReadU1(); |
| read_frame_data_pointer(rules.prh_encoding, 0); |
| break; |
| case 'R': |
| rules.addr_encoding = dio_ReadU1(); |
| break; |
| } |
| } |
| dio_Skip(aug_pos + aug_length - dio_GetPos()); |
| } |
| cie_regs.regs_cnt = 0; |
| frame_regs.regs_cnt = 0; |
| regs_stack_pos = 0; |
| while (dio_GetPos() < cie_end) { |
| exec_stack_frame_instruction(); |
| } |
| copy_register_rules(&cie_regs, &frame_regs); |
| dio_Skip(saved_pos - dio_GetPos()); |
| } |
| |
| static void read_frame_fde(ELF_Section * section, U8_T IP, U8_T fde_pos) { |
| int fde_dwarf64 = 0; |
| U8_T fde_length = 0; |
| U8_T fde_end = 0; |
| U8_T ref_pos = 0; |
| U8_T cie_ref = 0; |
| int fde_flag = 0; |
| |
| dio_EnterSection(NULL, section, fde_pos); |
| fde_length = dio_ReadU4(); |
| assert(fde_length > 0); |
| if (fde_length == ~(U4_T)0) { |
| fde_length = dio_ReadU8(); |
| fde_dwarf64 = 1; |
| } |
| ref_pos = dio_GetPos(); |
| fde_end = ref_pos + fde_length; |
| cie_ref = fde_dwarf64 ? dio_ReadU8() : dio_ReadU4(); |
| if (rules.eh_frame) fde_flag = cie_ref != 0; |
| else if (fde_dwarf64) fde_flag = cie_ref != ~(U8_T)0; |
| else fde_flag = cie_ref != ~(U4_T)0; |
| assert(fde_flag); |
| if (fde_flag) { |
| U8_T Addr, Range; |
| ELF_Section * sec = NULL; |
| if (rules.eh_frame) cie_ref = ref_pos - cie_ref; |
| if (cie_ref != rules.cie_pos) read_frame_cie(fde_pos, cie_ref); |
| Addr = read_frame_data_pointer(rules.addr_encoding, &sec); |
| Range = read_frame_data_pointer(rules.addr_encoding, NULL); |
| assert(Addr <= IP && Addr + Range > IP); |
| if (Addr <= IP && Addr + Range > IP) { |
| U8_T location0 = Addr; |
| if (rules.cie_aug != NULL && rules.cie_aug[0] == 'z') { |
| rules.fde_aug_length = dio_ReadULEB128(); |
| rules.fde_aug_data = dio_GetDataPtr(); |
| dio_Skip(rules.fde_aug_length); |
| } |
| copy_register_rules(&frame_regs, &cie_regs); |
| rules.location = Addr; |
| regs_stack_pos = 0; |
| for (;;) { |
| if (dio_GetPos() >= fde_end) { |
| rules.location = Addr + Range; |
| break; |
| } |
| exec_stack_frame_instruction(); |
| assert(location0 <= IP); |
| if (rules.location > IP) break; |
| location0 = rules.location; |
| } |
| dwarf_stack_trace_addr = location0; |
| dwarf_stack_trace_size = rules.location - location0; |
| generate_commands(); |
| } |
| } |
| dio_ExitSection(); |
| } |
| |
| static int cmp_frame_info_ranges(const void * x, const void * y) { |
| FrameInfoRange * rx = (FrameInfoRange *)x; |
| FrameInfoRange * ry = (FrameInfoRange *)y; |
| if (rx->mAddr < ry->mAddr) return -1; |
| if (rx->mAddr > ry->mAddr) return +1; |
| return 0; |
| } |
| |
| static void create_search_index(DWARFCache * cache, ELF_Section * section) { |
| dio_EnterSection(NULL, section, 0); |
| while (dio_GetPos() < section->size) { |
| int fde_dwarf64 = 0; |
| U8_T fde_length = 0; |
| U8_T fde_pos = 0; |
| U8_T fde_end = 0; |
| U8_T ref_pos = 0; |
| U8_T cie_ref = 0; |
| int fde_flag = 0; |
| |
| fde_pos = dio_GetPos(); |
| fde_length = dio_ReadU4(); |
| if (fde_length == 0) continue; |
| if (fde_length == ~(U4_T)0) { |
| fde_length = dio_ReadU8(); |
| fde_dwarf64 = 1; |
| } |
| ref_pos = dio_GetPos(); |
| fde_end = ref_pos + fde_length; |
| if (fde_end > rules.section->size) { |
| char msg[256]; |
| snprintf(msg, sizeof(msg), |
| "Invalid length 0x%" PRIX64 |
| " in FDE at 0x%" PRIX64, fde_length, fde_pos); |
| str_exception(ERR_INV_DWARF, msg); |
| } |
| cie_ref = fde_dwarf64 ? dio_ReadU8() : dio_ReadU4(); |
| if (rules.eh_frame) fde_flag = cie_ref != 0; |
| else if (fde_dwarf64) fde_flag = cie_ref != ~(U8_T)0; |
| else fde_flag = cie_ref != ~(U4_T)0; |
| if (fde_flag) { |
| ELF_Section * sec = NULL; |
| FrameInfoRange * range = NULL; |
| if (rules.eh_frame) cie_ref = ref_pos - cie_ref; |
| if (cie_ref != rules.cie_pos) read_frame_cie(fde_pos, cie_ref); |
| if (cache->mFrameInfoRangesCnt >= cache->mFrameInfoRangesMax) { |
| cache->mFrameInfoRangesMax += 512; |
| if (cache->mFrameInfoRanges == NULL) cache->mFrameInfoRangesMax += (unsigned)(section->size / 32); |
| cache->mFrameInfoRanges = (FrameInfoRange *)loc_realloc(cache->mFrameInfoRanges, |
| cache->mFrameInfoRangesMax * sizeof(FrameInfoRange)); |
| } |
| range = cache->mFrameInfoRanges + cache->mFrameInfoRangesCnt++; |
| range->mAddr = (ContextAddress)read_frame_data_pointer(rules.addr_encoding, &sec); |
| range->mSize = (ContextAddress)read_frame_data_pointer(rules.addr_encoding, NULL); |
| range->mOffset = fde_pos; |
| } |
| dio_Skip(fde_end - dio_GetPos()); |
| } |
| dio_ExitSection(); |
| qsort(cache->mFrameInfoRanges, cache->mFrameInfoRangesCnt, sizeof(FrameInfoRange), cmp_frame_info_ranges); |
| } |
| |
| void get_dwarf_stack_frame_info(Context * ctx, ELF_File * file, ELF_Section * sec, U8_T IP) { |
| DWARFCache * cache = get_dwarf_cache(file); |
| ELF_Section * section = cache->mDebugFrame; |
| unsigned l, h; |
| |
| dwarf_stack_trace_regs_cnt = 0; |
| if (dwarf_stack_trace_fp == NULL) { |
| dwarf_stack_trace_fp = (StackTracingCommandSequence *)loc_alloc_zero(sizeof(StackTracingCommandSequence)); |
| dwarf_stack_trace_fp->cmds_max = 1; |
| } |
| dwarf_stack_trace_fp->cmds_cnt = 0; |
| dwarf_stack_trace_addr = 0; |
| dwarf_stack_trace_size = 0; |
| |
| if (section == NULL) section = cache->mEHFrame; |
| if (section == NULL) return; |
| |
| memset(&rules, 0, sizeof(StackFrameRules)); |
| rules.ctx = ctx; |
| rules.section = section; |
| rules.eh_frame = section == cache->mEHFrame; |
| rules.reg_id_scope.big_endian = file->big_endian; |
| rules.reg_id_scope.machine = file->machine; |
| rules.reg_id_scope.os_abi = file->os_abi; |
| rules.reg_id_scope.id_type = rules.eh_frame ? REGNUM_EH_FRAME : REGNUM_DWARF; |
| rules.cie_pos = ~(U8_T)0; |
| |
| if (cache->mFrameInfoRanges == NULL) create_search_index(cache, section); |
| l = 0; |
| h = cache->mFrameInfoRangesCnt; |
| while (l < h) { |
| unsigned k = (l + h) / 2; |
| FrameInfoRange * range = cache->mFrameInfoRanges + k; |
| assert(cache->mFrameInfoRanges[l].mAddr <= cache->mFrameInfoRanges[h - 1].mAddr); |
| if (range->mAddr > IP) { |
| h = k; |
| } |
| else if (range->mAddr + range->mSize <= IP) { |
| l = k + 1; |
| } |
| else { |
| read_frame_fde(section, IP, range->mOffset); |
| return; |
| } |
| } |
| if (sec != NULL && sec->name != NULL && strcmp(sec->name, ".plt") == 0) { |
| assert(IP >= sec->addr); |
| assert(IP < sec->addr + sec->size); |
| if (generate_plt_section_commands(IP - sec->addr)) return; |
| } |
| } |
| |
| #endif /* ENABLE_ELF */ |