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