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);
    }
}
Example #2
0
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();
}
Example #3
0
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;
        }
    }
}
Example #4
0
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);
}