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