| /******************************************************************************* |
| * Copyright (c) 2007, 2014 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. |
| * You may elect to redistribute this code under either of these licenses. |
| * |
| * 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 <tcf/config.h> |
| |
| #if ENABLE_ELF && ENABLE_DebugContext |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <tcf/framework/exceptions.h> |
| #include <tcf/framework/myalloc.h> |
| #include <tcf/framework/trace.h> |
| #include <tcf/services/dwarf.h> |
| #include <tcf/services/dwarfio.h> |
| #include <tcf/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 |
| |
| struct FrameInfoRange { |
| U4_T mSection; |
| ContextAddress mAddr; |
| ContextAddress mSize; |
| U8_T mOffset; |
| }; |
| |
| typedef struct RegisterRules { |
| int rule; |
| I4_T offset; |
| U8_T expression; |
| } RegisterRules; |
| |
| typedef struct StackFrameRegisters { |
| int cfa_rule; |
| I4_T cfa_offset; |
| U4_T cfa_register; |
| U8_T cfa_expression; |
| RegisterRules * regs; |
| int regs_cnt; |
| int regs_max; |
| } StackFrameRegisters; |
| |
| typedef struct StackFrameRules { |
| Context * ctx; |
| ELF_Section * section; |
| ELF_Section * text_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; |
| ELF_Section * loc_section; |
| U8_T location; |
| int return_address_register; |
| } 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; |
| |
| StackFrameRegisterLocation * dwarf_stack_trace_fp = NULL; |
| |
| int dwarf_stack_trace_regs_cnt = 0; |
| StackFrameRegisterLocation ** dwarf_stack_trace_regs = NULL; |
| |
| static int trace_regs_max = 0; |
| static unsigned trace_cmds_max = 0; |
| static unsigned trace_cmds_cnt = 0; |
| static LocationExpressionCommand * trace_cmds = NULL; |
| |
| static RegisterRules * get_reg(StackFrameRegisters * regs, int reg) { |
| int min_reg_cnt = 0; |
| while (regs->regs_cnt <= reg || regs->regs_cnt < min_reg_cnt) { |
| int n = regs->regs_cnt++; |
| if (n >= 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); |
| } |
| memset(regs->regs + n, 0, sizeof(RegisterRules)); |
| /* Architecture specific implied rules */ |
| switch (rules.reg_id_scope.machine) { |
| case EM_386: |
| min_reg_cnt = 8; |
| 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: |
| min_reg_cnt = 16; |
| 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: |
| case EM_PPC64: |
| min_reg_cnt = 64; |
| 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; /* LR */ |
| } |
| break; |
| case EM_ARM: |
| min_reg_cnt = 14; |
| if (n >= 4 && n <= 11) { /* Local variables */ |
| regs->regs[n].rule = RULE_SAME_VALUE; |
| } |
| else if (n == 13) { /* Stack pointer */ |
| regs->regs[n].rule = RULE_VAL_OFFSET; |
| } |
| else if (n == rules.return_address_register) { |
| regs->regs[n].rule = RULE_REGISTER; |
| regs->regs[n].offset = 14; /* LR */ |
| } |
| break; |
| case EM_V850: |
| min_reg_cnt = 32; |
| if (n == 0) { /* Always same - reads as zero */ |
| regs->regs[n].rule = RULE_SAME_VALUE; |
| } |
| else if (n >= 6 && n <= 29) { /* Must be saved across function calls. Callee-save */ |
| regs->regs[n].rule = RULE_SAME_VALUE; |
| } |
| else if (n == 3) { /* Stack pointer */ |
| regs->regs[n].rule = RULE_VAL_OFFSET; |
| } |
| else if (n == rules.return_address_register) { |
| regs->regs[n].rule = RULE_REGISTER; |
| regs->regs[n].offset = 31; /* Link */ |
| } |
| break; |
| case EM_MICROBLAZE: |
| min_reg_cnt = 32; |
| if (n == 0) { /* Always same - reads as zero */ |
| regs->regs[n].rule = RULE_SAME_VALUE; |
| } |
| else if (n >= 19 && n <= 31) { /* Must be saved across function calls. Callee-save */ |
| regs->regs[n].rule = RULE_SAME_VALUE; |
| } |
| else if (n == 2) { /* Read-only small data area anchor */ |
| regs->regs[n].rule = RULE_SAME_VALUE; |
| } |
| else if (n == 13) { /* Read-write small data area anchor */ |
| regs->regs[n].rule = RULE_SAME_VALUE; |
| } |
| else if (n == 1) { /* Stack pointer */ |
| regs->regs[n].rule = RULE_VAL_OFFSET; |
| } |
| else if (n == rules.return_address_register) { |
| regs->regs[n].rule = RULE_REGISTER; |
| regs->regs[n].offset = 15; /* R15 is used for func return address */ |
| } |
| break; |
| case EM_AARCH64: |
| min_reg_cnt = 32; |
| if (n >= 19 && n <= 28) { /* Callee-saved registers */ |
| regs->regs[n].rule = RULE_SAME_VALUE; |
| } |
| else if (n == 31) { /* Stack pointer */ |
| regs->regs[n].rule = RULE_VAL_OFFSET; |
| } |
| else if (n == rules.return_address_register) { |
| regs->regs[n].rule = RULE_REGISTER; |
| regs->regs[n].offset = 30; /* LR */ |
| } |
| break; |
| } |
| } |
| return regs->regs + reg; |
| } |
| |
| static void clear_frame_registers(StackFrameRegisters * rgs) { |
| rgs->cfa_rule = 0; |
| rgs->cfa_offset = 0; |
| rgs->cfa_register = 0; |
| rgs->cfa_expression = 0; |
| rgs->regs_cnt = 0; |
| } |
| |
| static void copy_register_rules(StackFrameRegisters * dst, StackFrameRegisters * src) { |
| int n; |
| clear_frame_registers(dst); |
| dst->cfa_rule = src->cfa_rule; |
| dst->cfa_offset = src->cfa_offset; |
| dst->cfa_register = src->cfa_register; |
| dst->cfa_expression = src->cfa_expression; |
| 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 func_addr) { |
| U8_T v = 0; |
| U8_T pos; |
| unsigned idx; |
| ELF_File * file; |
| |
| if (encoding == EH_PE_omit) return 0; |
| pos = dio_GetPos(); |
| /* Decode the base or adjust the offset */ |
| switch ((encoding >> 4) & 0x7) { |
| case 0: |
| case EH_PB_funcrel: |
| v = func_addr; |
| break; |
| case EH_PB_pcrel: |
| if (sec != NULL) { |
| v = pos + rules.section->addr; |
| } |
| break; |
| case EH_PB_datarel: |
| if (sec != NULL) { |
| v = rules.section->addr; |
| } |
| break; |
| case EH_PB_textrel: |
| if (sec != NULL && rules.text_section != NULL) { |
| v = rules.text_section->addr; |
| } |
| break; |
| case EH_PB_aligned: |
| if ((pos % rules.address_size) != 0) dio_SetPos(pos + (rules.address_size - (pos % rules.address_size))); |
| break; |
| default: |
| str_exception(ERR_INV_DWARF, "Unknown encoding of .eh_frame section pointers"); |
| break; |
| } |
| /* Decode the value */ |
| 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_ReadAddressX(sec, 2); |
| break; |
| case EH_PE_udata4: |
| v += dio_ReadAddressX(sec, 4); |
| break; |
| case EH_PE_udata8: |
| v += dio_ReadAddressX(sec, 8); |
| break; |
| case EH_PE_sleb128: |
| v += dio_ReadS8LEB128(); |
| break; |
| case EH_PE_sdata2: |
| v += (I2_T)dio_ReadAddressX(sec, 2); |
| break; |
| case EH_PE_sdata4: |
| v += (I4_T)dio_ReadAddressX(sec, 4); |
| break; |
| case EH_PE_sdata8: |
| v += (I8_T)dio_ReadAddressX(sec, 8); |
| break; |
| default: |
| str_exception(ERR_INV_DWARF, "Unknown encoding of .eh_frame section pointers"); |
| break; |
| } |
| if (encoding & EH_PE_indirect) { |
| size_t size = rules.address_size; |
| U8_T res = 0; |
| file = rules.section->file; |
| 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(U8_T func_addr) { |
| RegisterRules * reg; |
| U4_T n; |
| U1_T op = dio_ReadU1(); |
| switch (op) { |
| case CFA_nop: |
| break; |
| case CFA_set_loc: |
| rules.location = read_frame_data_pointer(rules.addr_encoding, &rules.loc_section, func_addr); |
| break; |
| case CFA_advance_loc1: |
| rules.location += dio_ReadU1() * rules.code_alignment; |
| break; |
| case CFA_advance_loc2: |
| rules.location += dio_ReadU2() * rules.code_alignment; |
| break; |
| case CFA_advance_loc4: |
| rules.location += dio_ReadU4() * rules.code_alignment; |
| break; |
| case CFA_offset_extended: |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_OFFSET; |
| reg->offset = dio_ReadULEB128() * rules.data_alignment; |
| break; |
| case CFA_restore_extended: |
| n = dio_ReadULEB128(); |
| reg = get_reg(&frame_regs, n); |
| *reg = *get_reg(&cie_regs, n); |
| break; |
| case CFA_undefined: |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| memset(reg, 0, sizeof(*reg)); |
| break; |
| case CFA_same_value: |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_SAME_VALUE; |
| break; |
| case CFA_register: |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_REGISTER; |
| reg->offset = dio_ReadULEB128(); |
| break; |
| case CFA_remember_state: |
| copy_register_rules(get_regs_stack_item(regs_stack_pos++), &frame_regs); |
| break; |
| case 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 CFA_def_cfa: |
| frame_regs.cfa_rule = RULE_OFFSET; |
| frame_regs.cfa_register = dio_ReadULEB128(); |
| frame_regs.cfa_offset = dio_ReadULEB128(); |
| break; |
| case CFA_def_cfa_register: |
| frame_regs.cfa_rule = RULE_OFFSET; |
| frame_regs.cfa_register = dio_ReadULEB128(); |
| break; |
| case CFA_def_cfa_offset: |
| frame_regs.cfa_rule = RULE_OFFSET; |
| frame_regs.cfa_offset = dio_ReadULEB128(); |
| break; |
| case CFA_def_cfa_expression: |
| frame_regs.cfa_rule = RULE_EXPRESSION; |
| frame_regs.cfa_offset = dio_ReadULEB128(); |
| frame_regs.cfa_expression = dio_GetPos(); |
| dio_Skip(frame_regs.cfa_offset); |
| break; |
| case 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 CFA_offset_extended_sf: |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_OFFSET; |
| reg->offset = dio_ReadSLEB128() * rules.data_alignment; |
| break; |
| case CFA_def_cfa_sf: |
| frame_regs.cfa_rule = RULE_OFFSET; |
| frame_regs.cfa_register = dio_ReadULEB128(); |
| frame_regs.cfa_offset = dio_ReadSLEB128() * rules.data_alignment; |
| break; |
| case CFA_def_cfa_offset_sf: |
| frame_regs.cfa_rule = RULE_OFFSET; |
| frame_regs.cfa_offset = dio_ReadSLEB128() * rules.data_alignment; |
| break; |
| case CFA_val_offset: |
| reg = get_reg(&frame_regs, dio_ReadULEB128()); |
| reg->rule = RULE_VAL_OFFSET; |
| reg->offset = dio_ReadULEB128() * rules.data_alignment; |
| break; |
| case 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 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 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 CFA_GNU_negative_offset_ext: |
| /* 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 LocationExpressionCommand * add_command(int op) { |
| LocationExpressionCommand * cmd = NULL; |
| if (trace_cmds_cnt >= trace_cmds_max) { |
| trace_cmds_max += 16; |
| trace_cmds = (LocationExpressionCommand *)loc_realloc(trace_cmds, trace_cmds_max * sizeof(LocationExpressionCommand)); |
| } |
| cmd = trace_cmds + trace_cmds_cnt++; |
| memset(cmd, 0, sizeof(*cmd)); |
| cmd->cmd = op; |
| return cmd; |
| } |
| |
| static void add_command_sequence(StackFrameRegisterLocation ** ptr, RegisterDefinition * reg) { |
| StackFrameRegisterLocation * seq = *ptr; |
| if (seq == NULL || seq->cmds_max < trace_cmds_cnt) { |
| *ptr = seq = (StackFrameRegisterLocation *)loc_realloc(seq, sizeof(StackFrameRegisterLocation) + (trace_cmds_cnt - 1) * sizeof(LocationExpressionCommand)); |
| seq->cmds_max = trace_cmds_cnt; |
| } |
| seq->reg = reg; |
| seq->cmds_cnt = trace_cmds_cnt; |
| memcpy(seq->cmds, trace_cmds, trace_cmds_cnt * sizeof(LocationExpressionCommand)); |
| } |
| |
| 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 (errno) str_exception(errno, "Cannot get object run-time address"); |
| add_command(SFT_CMD_NUMBER)->args.num = rt_addr; |
| } |
| break; |
| case OP_deref: |
| { |
| LocationExpressionCommand * cmd = add_command(SFT_CMD_RD_MEM); |
| cmd->args.mem.size = rules.address_size; |
| cmd->args.mem.big_endian = rules.reg_id_scope.big_endian; |
| } |
| break; |
| case OP_deref_size: |
| { |
| LocationExpressionCommand * cmd = add_command(SFT_CMD_RD_MEM); |
| cmd->args.mem.size = dio_ReadU1(); |
| cmd->args.mem.big_endian = rules.reg_id_scope.big_endian; |
| } |
| break; |
| case OP_const1u: |
| add_command(SFT_CMD_NUMBER)->args.num = dio_ReadU1(); |
| break; |
| case OP_const1s: |
| add_command(SFT_CMD_NUMBER)->args.num = (I1_T)dio_ReadU1(); |
| break; |
| case OP_const2u: |
| add_command(SFT_CMD_NUMBER)->args.num = dio_ReadU2(); |
| break; |
| case OP_const2s: |
| add_command(SFT_CMD_NUMBER)->args.num = (I2_T)dio_ReadU2(); |
| break; |
| case OP_const4u: |
| add_command(SFT_CMD_NUMBER)->args.num = dio_ReadU4(); |
| break; |
| case OP_const4s: |
| add_command(SFT_CMD_NUMBER)->args.num = (I4_T)dio_ReadU4(); |
| break; |
| case OP_const8u: |
| add_command(SFT_CMD_NUMBER)->args.num = dio_ReadU8(); |
| break; |
| case OP_const8s: |
| add_command(SFT_CMD_NUMBER)->args.num = (I8_T)dio_ReadU8(); |
| break; |
| case OP_constu: |
| add_command(SFT_CMD_NUMBER)->args.num = dio_ReadU8LEB128(); |
| break; |
| case OP_consts: |
| add_command(SFT_CMD_NUMBER)->args.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)->args.num = dio_ReadU8LEB128(); |
| add_command(SFT_CMD_ADD); |
| break; |
| case OP_ge: |
| add_command(SFT_CMD_GE); |
| break; |
| case OP_gt: |
| add_command(SFT_CMD_GT); |
| break; |
| case OP_le: |
| add_command(SFT_CMD_LE); |
| break; |
| case OP_lt: |
| add_command(SFT_CMD_LT); |
| break; |
| case OP_shl: |
| add_command(SFT_CMD_SHL); |
| 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)->args.num = op - OP_lit0; |
| break; |
| case OP_reg0: |
| case OP_reg1: |
| case OP_reg2: |
| case OP_reg3: |
| case OP_reg4: |
| case OP_reg5: |
| case OP_reg6: |
| case OP_reg7: |
| case OP_reg8: |
| case OP_reg9: |
| case OP_reg10: |
| case OP_reg11: |
| case OP_reg12: |
| case OP_reg13: |
| case OP_reg14: |
| case OP_reg15: |
| case OP_reg16: |
| case OP_reg17: |
| case OP_reg18: |
| case OP_reg19: |
| case OP_reg20: |
| case OP_reg21: |
| case OP_reg22: |
| case OP_reg23: |
| case OP_reg24: |
| case OP_reg25: |
| case OP_reg26: |
| case OP_reg27: |
| case OP_reg28: |
| case OP_reg29: |
| case OP_reg30: |
| case OP_reg31: |
| { |
| RegisterDefinition * def = get_reg_by_id(rules.ctx, op - OP_reg0, &rules.reg_id_scope); |
| if (def == NULL) str_exception(errno, "Cannot read DWARF frame info"); |
| add_command(SFT_CMD_RD_REG)->args.reg = def; |
| } |
| break; |
| case OP_regx: |
| { |
| unsigned n = (unsigned)dio_ReadULEB128(); |
| RegisterDefinition * def = get_reg_by_id(rules.ctx, n, &rules.reg_id_scope); |
| if (def == NULL) str_exception(errno, "Cannot read DWARF frame info"); |
| add_command(SFT_CMD_RD_REG)->args.reg = def; |
| } |
| 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_RD_REG)->args.reg = def; |
| if (offs != 0) { |
| add_command(SFT_CMD_NUMBER)->args.num = offs; |
| add_command(SFT_CMD_ADD); |
| } |
| } |
| break; |
| case OP_bregx: |
| { |
| unsigned n = (unsigned)dio_ReadULEB128(); |
| I8_T offs = dio_ReadS8LEB128(); |
| RegisterDefinition * def = get_reg_by_id(rules.ctx, n, &rules.reg_id_scope); |
| if (def == NULL) str_exception(errno, "Cannot read DWARF frame info"); |
| add_command(SFT_CMD_RD_REG)->args.reg = def; |
| if (offs != 0) { |
| add_command(SFT_CMD_NUMBER)->args.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)->args.num = reg->offset; |
| add_command(SFT_CMD_ADD); |
| } |
| if (reg->rule == RULE_OFFSET) { |
| LocationExpressionCommand * cmd = add_command(SFT_CMD_RD_MEM); |
| cmd->args.mem.size = dst_reg_def->size; |
| if (cmd->args.mem.size > rules.address_size) cmd->args.mem.size = rules.address_size; |
| cmd->args.mem.big_endian = rules.reg_id_scope.big_endian; |
| } |
| break; |
| case RULE_SAME_VALUE: |
| if (src_reg_def == NULL) return; |
| add_command(SFT_CMD_RD_REG)->args.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_RD_REG)->args.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) { |
| LocationExpressionCommand * cmd = add_command(SFT_CMD_RD_MEM); |
| cmd->args.mem.size = dst_reg_def->size; |
| if (cmd->args.mem.size > rules.address_size) cmd->args.mem.size = rules.address_size; |
| cmd->args.mem.big_endian = rules.reg_id_scope.big_endian; |
| } |
| break; |
| default: |
| str_exception(ERR_INV_DWARF, "Invalid .debug_frame"); |
| break; |
| } |
| if (rules.reg_id_scope.machine == EM_MICROBLAZE && |
| dst_reg_def != NULL && dst_reg_def->dwarf_id == 32 && |
| rules.return_address_register == 15) { |
| add_command(SFT_CMD_NUMBER)->args.num = 8; |
| add_command(SFT_CMD_ADD); |
| } |
| if (dwarf_stack_trace_regs_cnt >= trace_regs_max) { |
| int i; |
| trace_regs_max += 16; |
| dwarf_stack_trace_regs = (StackFrameRegisterLocation **)loc_realloc(dwarf_stack_trace_regs, trace_regs_max * sizeof(StackFrameRegisterLocation *)); |
| 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++) { |
| 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 (frame_regs.cfa_rule) { |
| case RULE_OFFSET: |
| reg_def = get_reg_by_id(rules.ctx, frame_regs.cfa_register, &rules.reg_id_scope); |
| if (reg_def != NULL) { |
| add_command(SFT_CMD_RD_REG)->args.reg = reg_def; |
| if (frame_regs.cfa_offset != 0) { |
| add_command(SFT_CMD_NUMBER)->args.num = frame_regs.cfa_offset; |
| add_command(SFT_CMD_ADD); |
| } |
| } |
| break; |
| case RULE_EXPRESSION: |
| add_dwarf_expression_commands(frame_regs.cfa_expression, frame_regs.cfa_offset); |
| break; |
| default: |
| str_exception(ERR_INV_DWARF, "Invalid .debug_frame"); |
| break; |
| } |
| add_command_sequence(&dwarf_stack_trace_fp, NULL); |
| } |
| |
| static void generate_plt_section_commands(Context * ctx, ELF_File * file, U8_T offs) { |
| RegisterRules * reg = NULL; |
| |
| memset(&rules, 0, sizeof(StackFrameRules)); |
| rules.ctx = ctx; |
| 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.elf64 = file->elf64; |
| rules.reg_id_scope.id_type = REGNUM_DWARF; |
| rules.address_size = file->elf64 ? 8 : 4; |
| |
| clear_frame_registers(&cie_regs); |
| clear_frame_registers(&frame_regs); |
| switch (rules.reg_id_scope.machine) { |
| case EM_386: |
| frame_regs.cfa_rule = RULE_OFFSET; |
| frame_regs.cfa_register = 4; /* esp */ |
| if (offs == 0) { |
| frame_regs.cfa_offset = 8; |
| } |
| else if (offs < 16) { |
| frame_regs.cfa_offset = 12; |
| } |
| else if ((offs - 16) % 16 < 11) { |
| frame_regs.cfa_offset = 4; |
| } |
| else { |
| frame_regs.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(); |
| break; |
| case EM_X86_64: |
| frame_regs.cfa_rule = RULE_OFFSET; |
| frame_regs.cfa_register = 7; /* rsp */ |
| if (offs == 0) { |
| frame_regs.cfa_offset = 16; |
| } |
| else if (offs < 16) { |
| frame_regs.cfa_offset = 24; |
| } |
| else if ((offs - 16) % 16 < 11) { |
| frame_regs.cfa_offset = 8; |
| } |
| else { |
| frame_regs.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(); |
| break; |
| case EM_PPC: |
| case EM_PPC64: |
| rules.return_address_register = 108; /* LR */ |
| frame_regs.cfa_rule = RULE_OFFSET; |
| frame_regs.cfa_register = 1; /* R1 */ |
| generate_commands(); |
| break; |
| case EM_ARM: |
| rules.return_address_register = 14; /* LR */ |
| frame_regs.cfa_rule = RULE_OFFSET; |
| frame_regs.cfa_register = 13; /* SP */ |
| generate_commands(); |
| break; |
| case EM_MICROBLAZE: |
| rules.return_address_register = 15; /* R15 */ |
| frame_regs.cfa_rule = RULE_OFFSET; |
| frame_regs.cfa_register = 1; /* R1 */ |
| generate_commands(); |
| break; |
| case EM_AARCH64: |
| rules.return_address_register = 30; /* LR */ |
| frame_regs.cfa_rule = RULE_OFFSET; |
| frame_regs.cfa_register = 31; /* SP */ |
| generate_commands(); |
| break; |
| } |
| } |
| |
| 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) { |
| str_fmt_exception(ERR_INV_DWARF, |
| "Invalid CIE pointer 0x%" PRIX64 |
| " in FDE at 0x%" PRIX64, pos, fde_pos); |
| } |
| dio_SetPos(pos); |
| 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_fmt_exception(ERR_INV_DWARF, |
| "Unsupported version of Call Frame Information: %d", rules.version); |
| } |
| 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': |
| { |
| Trap trap; |
| U8_T addr_pos; |
| rules.prh_encoding = dio_ReadU1(); |
| addr_pos = dio_GetPos(); |
| if (set_trap(&trap)) { |
| read_frame_data_pointer(rules.prh_encoding, NULL, 0); |
| clear_trap(&trap); |
| } |
| else { |
| dio_SetPos(addr_pos + rules.address_size); |
| } |
| } |
| break; |
| case 'R': |
| rules.addr_encoding = dio_ReadU1(); |
| break; |
| } |
| } |
| dio_SetPos(aug_pos + aug_length); |
| } |
| clear_frame_registers(&cie_regs); |
| clear_frame_registers(&frame_regs); |
| regs_stack_pos = 0; |
| while (dio_GetPos() < cie_end) { |
| exec_stack_frame_instruction(0); |
| } |
| copy_register_rules(&cie_regs, &frame_regs); |
| dio_SetPos(saved_pos); |
| } |
| |
| static void read_frame_fde(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, rules.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; |
| 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, &rules.loc_section, 0); |
| Range = read_frame_data_pointer(rules.addr_encoding, NULL, 0); |
| 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(Addr); |
| assert(location0 <= IP); |
| if (rules.location > IP) break; |
| location0 = rules.location; |
| } |
| dwarf_stack_trace_addr = location0; |
| dwarf_stack_trace_size = rules.location - location0; |
| if (rules.reg_id_scope.machine == EM_ARM && IP + 4 == Addr + Range) { |
| /* GCC generates invalid frame info for ARM function epilogue */ |
| /* Ignore frame info, fall-back to stack crawl logic */ |
| dio_ExitSection(); |
| return; |
| } |
| 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->mSection < ry->mSection) return -1; |
| if (rx->mSection > ry->mSection) return +1; |
| if (rx->mAddr < ry->mAddr) return -1; |
| if (rx->mAddr > ry->mAddr) return +1; |
| return 0; |
| } |
| |
| static void create_search_index(DWARFCache * cache, FrameInfoIndex * index) { |
| ELF_Section * section = index->mSection; |
| 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) { |
| U4_T alignment = section->alignment; |
| if (alignment > 1 && fde_pos % alignment != 0) { |
| /* Workaround for sections with invalid alignment */ |
| dio_SetPos(fde_pos + alignment - fde_pos % alignment); |
| continue; |
| } |
| else { |
| str_fmt_exception(ERR_INV_DWARF, |
| "Invalid length 0x%" PRIX64 |
| " in FDE at 0x%" PRIX64, fde_length, fde_pos); |
| } |
| } |
| 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 (index->mFrameInfoRangesCnt >= index->mFrameInfoRangesMax) { |
| index->mFrameInfoRangesMax += 512; |
| if (index->mFrameInfoRanges == NULL) index->mFrameInfoRangesMax += (unsigned)(section->size / 32); |
| index->mFrameInfoRanges = (FrameInfoRange *)loc_realloc(index->mFrameInfoRanges, |
| index->mFrameInfoRangesMax * sizeof(FrameInfoRange)); |
| } |
| range = index->mFrameInfoRanges + index->mFrameInfoRangesCnt++; |
| memset(range, 0, sizeof(FrameInfoRange)); |
| range->mAddr = (ContextAddress)read_frame_data_pointer(rules.addr_encoding, &sec, 0); |
| range->mSize = (ContextAddress)read_frame_data_pointer(rules.addr_encoding, NULL, 0); |
| if (sec != NULL) { |
| range->mSection = sec->index; |
| index->mRelocatable = 1; |
| } |
| range->mOffset = fde_pos; |
| } |
| dio_SetPos(fde_end); |
| } |
| dio_ExitSection(); |
| qsort(index->mFrameInfoRanges, index->mFrameInfoRangesCnt, sizeof(FrameInfoRange), cmp_frame_info_ranges); |
| } |
| |
| static void read_frame_info_section(Context * ctx, ELF_Section * text_section, |
| U8_T IP, DWARFCache * cache, FrameInfoIndex * index) { |
| unsigned l, h; |
| ELF_Section * section = index->mSection; |
| ELF_File * file = section->file; |
| U4_T sec_idx = 0; |
| |
| if (text_section != NULL && text_section->file != file) { |
| unsigned i; |
| assert(get_dwarf_file(text_section->file) == file); |
| for (i = 1; i < file->section_cnt; i++) { |
| ELF_Section * sec = cache->mFile->sections + i; |
| if (sec->name == NULL) continue; |
| if (strcmp(sec->name, text_section->name) == 0) { |
| text_section = sec; |
| break; |
| } |
| } |
| } |
| |
| memset(&rules, 0, sizeof(StackFrameRules)); |
| rules.ctx = ctx; |
| rules.section = section; |
| rules.text_section = text_section; |
| rules.eh_frame = strcmp(section->name, ".eh_frame") == 0; |
| 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.elf64 = file->elf64; |
| rules.reg_id_scope.id_type = rules.eh_frame ? REGNUM_EH_FRAME : REGNUM_DWARF; |
| rules.cie_pos = ~(U8_T)0; |
| |
| if (index->mFrameInfoRanges == NULL) create_search_index(cache, index); |
| l = 0; |
| h = index->mFrameInfoRangesCnt; |
| if (index->mRelocatable && text_section != NULL) sec_idx = text_section->index; |
| while (l < h) { |
| unsigned k = (l + h) / 2; |
| FrameInfoRange * range = index->mFrameInfoRanges + k; |
| if (sec_idx < range->mSection) { |
| h = k; |
| } |
| else if (sec_idx > range->mSection) { |
| l = k + 1; |
| } |
| else if (range->mAddr > IP) { |
| h = k; |
| } |
| else if (range->mAddr + range->mSize <= IP) { |
| l = k + 1; |
| } |
| else { |
| read_frame_fde(IP, range->mOffset); |
| return; |
| } |
| } |
| } |
| |
| void get_dwarf_stack_frame_info(Context * ctx, ELF_File * file, ELF_Section * text_section, U8_T addr) { |
| DWARFCache * cache = NULL; |
| FrameInfoIndex * index = NULL; |
| ELF_File * dwarf_file = NULL; |
| |
| dwarf_stack_trace_regs_cnt = 0; |
| if (dwarf_stack_trace_fp == NULL) { |
| dwarf_stack_trace_fp = (StackFrameRegisterLocation *)loc_alloc_zero(sizeof(StackFrameRegisterLocation)); |
| dwarf_stack_trace_fp->cmds_max = 1; |
| } |
| dwarf_stack_trace_fp->cmds_cnt = 0; |
| dwarf_stack_trace_addr = 0; |
| dwarf_stack_trace_size = 0; |
| |
| dwarf_file = get_dwarf_file(file); |
| if (dwarf_file != file) { |
| cache = get_dwarf_cache(dwarf_file); |
| index = cache->mFrameInfo; |
| while (index != NULL) { |
| read_frame_info_section(ctx, text_section, addr, cache, index); |
| if (dwarf_stack_trace_fp->cmds_cnt > 0) return; |
| index = index->mNext; |
| } |
| } |
| |
| cache = get_dwarf_cache(file); |
| index = cache->mFrameInfo; |
| while (index != NULL) { |
| read_frame_info_section(ctx, text_section, addr, cache, index); |
| if (dwarf_stack_trace_fp->cmds_cnt > 0) return; |
| index = index->mNext; |
| } |
| |
| if (text_section != NULL && text_section->name != NULL && strcmp(text_section->name, ".plt") == 0) { |
| assert(addr >= text_section->addr); |
| assert(addr < text_section->addr + text_section->size); |
| generate_plt_section_commands(ctx, file, addr - text_section->addr); |
| if (dwarf_stack_trace_fp->cmds_cnt > 0) { |
| dwarf_stack_trace_addr = addr; |
| dwarf_stack_trace_size = 1; |
| } |
| } |
| } |
| |
| #endif /* ENABLE_ELF && ENABLE_DebugContext */ |