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 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 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); }