void load_line_numbers(DWARFCache * Cache, CompUnit * Unit) { Trap trap; if (Unit->mFiles != NULL && Unit->mDirs != NULL) return; if (elf_load(Cache->mDebugLine)) exception(errno); dio_EnterDataSection(&Unit->mDesc, Cache->mDebugLine->data, Unit->mLineInfoOffs, Cache->mDebugLine->size); if (set_trap(&trap)) { U8_T header_pos = 0; U1_T opcode_base = 0; U1_T opcode_size[256]; U8_T header_size = 0; U1_T min_instruction_length = 0; U1_T is_stmt_default = 0; I1_T line_base = 0; U1_T line_range = 0; U8_T unit_size = 0; int dwarf64 = 0; LineNumbersState state; /* Read header */ unit_size = dio_ReadU4(); if (unit_size == 0xffffffffu) { unit_size = dio_ReadU8(); unit_size += 12; dwarf64 = 1; } else { unit_size += 4; } dio_ReadU2(); /* line info version */ header_size = dwarf64 ? dio_ReadU8() : (U8_T)dio_ReadU4(); header_pos = dio_GetPos(); min_instruction_length = dio_ReadU1(); is_stmt_default = dio_ReadU1() != 0; line_base = (I1_T)dio_ReadU1(); line_range = dio_ReadU1(); opcode_base = dio_ReadU1(); memset(opcode_size, 0, sizeof(opcode_size)); dio_Read(opcode_size + 1, opcode_base - 1); /* Read directory names */ for (;;) { char * Name = dio_ReadString(); if (Name == NULL) break; add_dir(Unit, Name); } /* Read source sFileLocks info */ for (;;) { U4_T dir = 0; FileInfo File; memset(&File, 0, sizeof(File)); File.mName = dio_ReadString(); if (File.mName == NULL) break; dir = dio_ReadULEB128(); if (dir > 0 && dir <= Unit->mDirsCnt) File.mDir = Unit->mDirs[dir - 1]; File.mModTime = dio_ReadULEB128(); File.mSize = dio_ReadULEB128(); add_file(Unit, &File); } /* Run the program */ if (header_pos + header_size != dio_GetPos()) str_exception(ERR_INV_DWARF, "Invalid line info header"); memset(&state, 0, sizeof(state)); state.mFile = 1; state.mLine = 1; if (is_stmt_default) state.mFlags |= LINE_IsStmt; while (dio_GetPos() < Unit->mLineInfoOffs + unit_size) { U1_T opcode = dio_ReadU1(); if (opcode >= opcode_base) { state.mLine += (unsigned)((int)((opcode - opcode_base) % line_range) + line_base); state.mAddress += (opcode - opcode_base) / line_range * min_instruction_length; add_state(Unit, &state); state.mFlags &= ~(LINE_BasicBlock | LINE_PrologueEnd | LINE_EpilogueBegin); } else if (opcode == 0) { U4_T op_size = dio_ReadULEB128(); U8_T op_pos = dio_GetPos(); switch (dio_ReadU1()) { case DW_LNE_define_file: { U4_T dir = 0; FileInfo File; memset(&File, 0, sizeof(File)); File.mName = dio_ReadString(); dir = dio_ReadULEB128(); if (dir > 0 && dir <= Unit->mDirsCnt) File.mDir = Unit->mDirs[dir - 1]; File.mModTime = dio_ReadULEB128(); File.mSize = dio_ReadULEB128(); add_file(Unit, &File); break; } case DW_LNE_end_sequence: state.mFlags |= LINE_EndSequence; add_state(Unit, &state); memset(&state, 0, sizeof(state)); state.mFile = 1; state.mLine = 1; if (is_stmt_default) state.mFlags |= LINE_IsStmt; else state.mFlags &= ~LINE_IsStmt; break; case DW_LNE_set_address: state.mAddress = (ContextAddress)dio_ReadAddress(); break; default: dio_Skip(op_size - 1); break; } if (dio_GetPos() != op_pos + op_size) str_exception(ERR_INV_DWARF, "Invalid line info op size"); } else { switch (opcode) { case DW_LNS_copy: add_state(Unit, &state); state.mFlags &= ~(LINE_BasicBlock | LINE_PrologueEnd | LINE_EpilogueBegin); break; case DW_LNS_advance_pc: state.mAddress += (ContextAddress)(dio_ReadU8LEB128() * min_instruction_length); break; case DW_LNS_advance_line: state.mLine += dio_ReadSLEB128(); break; case DW_LNS_set_file: state.mFile = dio_ReadULEB128(); break; case DW_LNS_set_column: state.mColumn = dio_ReadULEB128(); break; case DW_LNS_negate_stmt: state.mFlags ^= LINE_IsStmt; break; case DW_LNS_set_basic_block: state.mFlags |= LINE_BasicBlock; break; case DW_LNS_const_add_pc: state.mAddress += (255 - opcode_base) / line_range * min_instruction_length; break; case DW_LNS_fixed_advance_pc: state.mAddress += dio_ReadU2(); break; case DW_LNS_set_prologue_end: state.mFlags |= LINE_PrologueEnd; break; case DW_LNS_set_epilogue_begin: state.mFlags |= LINE_EpilogueBegin; break; case DW_LNS_set_isa: state.mISA = (U1_T)dio_ReadULEB128(); break; default: str_exception(ERR_INV_DWARF, "Invalid line info op code"); break; } } } dio_ExitSection(); clear_trap(&trap); } else { dio_ExitSection(); free_unit_cache(Unit); str_exception(trap.error, trap.msg); } }
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(); if (dwarf_stack_trace_regs_cnt == 0) { /* GHS generates dummy frame info with all registers marked undefined */ /* Ignore frame info, fall-back to stack crawl logic */ dwarf_stack_trace_fp->cmds_cnt = 0; dwarf_stack_trace_addr = 0; dwarf_stack_trace_size = 0; dio_ExitSection(); return; } } } dio_ExitSection(); }
static void exec_stack_frame_instruction(U8_T func_addr) { RegisterRules * reg; U1_T op = dio_ReadU1(); U4_T n; 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_CFA_GNU_window_save: /* SPARC-specific code */ for (n = 8; n < 16; n++) { reg = get_reg(&frame_regs, n); reg->rule = RULE_REGISTER; reg->offset = n + 16; } for (n = 16; n < 32; n++) { reg = get_reg(&frame_regs, n); reg->rule = RULE_OFFSET; reg->offset = (n - 16) * (rules.reg_id_scope.machine == EM_SPARCV9 ? 8 : 4); } 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 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); }