bool
DWARFCallFrameInfo::FDEToUnwindPlan (dw_offset_t dwarf_offset, Address startaddr, UnwindPlan& unwind_plan)
{
    lldb::offset_t offset = dwarf_offset;
    lldb::offset_t current_entry = offset;

    if (m_section_sp.get() == nullptr || m_section_sp->IsEncrypted())
        return false;

    if (m_cfi_data_initialized == false)
        GetCFIData();

    uint32_t length = m_cfi_data.GetU32 (&offset);
    dw_offset_t cie_offset;
    bool is_64bit = (length == UINT32_MAX);
    if (is_64bit) {
        length = m_cfi_data.GetU64 (&offset);
        cie_offset = m_cfi_data.GetU64 (&offset);
    } else {
        cie_offset = m_cfi_data.GetU32 (&offset);
    }

    assert (cie_offset != 0 && cie_offset != UINT32_MAX);

    // Translate the CIE_id from the eh_frame format, which
    // is relative to the FDE offset, into a __eh_frame section
    // offset
    if (m_is_eh_frame)
    {
        unwind_plan.SetSourceName ("eh_frame CFI");
        cie_offset = current_entry + (is_64bit ? 12 : 4) - cie_offset;
        unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
    }
    else
    {
        unwind_plan.SetSourceName ("DWARF CFI");
        // In theory the debug_frame info should be valid at all call sites
        // ("asynchronous unwind info" as it is sometimes called) but in practice
        // gcc et al all emit call frame info for the prologue and call sites, but
        // not for the epilogue or all the other locations during the function reliably.
        unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
    }
    unwind_plan.SetSourcedFromCompiler (eLazyBoolYes);

    const CIE *cie = GetCIE (cie_offset);
    assert (cie != nullptr);

    const dw_offset_t end_offset = current_entry + length + (is_64bit ? 12 : 4);

    const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress();
    const lldb::addr_t text_addr = LLDB_INVALID_ADDRESS;
    const lldb::addr_t data_addr = LLDB_INVALID_ADDRESS;
    lldb::addr_t range_base = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding, pc_rel_addr, text_addr, data_addr);
    lldb::addr_t range_len = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding & DW_EH_PE_MASK_ENCODING, pc_rel_addr, text_addr, data_addr);
    AddressRange range (range_base, m_objfile.GetAddressByteSize(), m_objfile.GetSectionList());
    range.SetByteSize (range_len);

    addr_t lsda_data_file_address = LLDB_INVALID_ADDRESS;

    if (cie->augmentation[0] == 'z')
    {
        uint32_t aug_data_len = (uint32_t)m_cfi_data.GetULEB128(&offset);
        if (aug_data_len != 0 && cie->lsda_addr_encoding != DW_EH_PE_omit)
        {
            offset_t saved_offset = offset;
            lsda_data_file_address = m_cfi_data.GetGNUEHPointer(&offset, cie->lsda_addr_encoding, pc_rel_addr, text_addr, data_addr);
            if (offset - saved_offset != aug_data_len)
            {
                // There is more in the augmentation region than we know how to process;
                // don't read anything.
                lsda_data_file_address = LLDB_INVALID_ADDRESS;
            }
            offset = saved_offset;
        }
        offset += aug_data_len;
    }
    Address lsda_data;
    Address personality_function_ptr;

    if (lsda_data_file_address != LLDB_INVALID_ADDRESS && cie->personality_loc != LLDB_INVALID_ADDRESS)
    {
        m_objfile.GetModule()->ResolveFileAddress (lsda_data_file_address, lsda_data);
        m_objfile.GetModule()->ResolveFileAddress (cie->personality_loc, personality_function_ptr);
    }

    if (lsda_data.IsValid() && personality_function_ptr.IsValid())
    {
        unwind_plan.SetLSDAAddress (lsda_data);
        unwind_plan.SetPersonalityFunctionPtr (personality_function_ptr);
    }

    uint32_t code_align = cie->code_align;
    int32_t data_align = cie->data_align;

    unwind_plan.SetPlanValidAddressRange (range);
    UnwindPlan::Row *cie_initial_row = new UnwindPlan::Row;
    *cie_initial_row = cie->initial_row;
    UnwindPlan::RowSP row(cie_initial_row);

    unwind_plan.SetRegisterKind (m_reg_kind);
    unwind_plan.SetReturnAddressRegister (cie->return_addr_reg_num);

    std::vector<UnwindPlan::RowSP> stack;

    UnwindPlan::Row::RegisterLocation reg_location;
    while (m_cfi_data.ValidOffset(offset) && offset < end_offset)
    {
        uint8_t inst = m_cfi_data.GetU8(&offset);
        uint8_t primary_opcode  = inst & 0xC0;
        uint8_t extended_opcode = inst & 0x3F;

        if (!HandleCommonDwarfOpcode(primary_opcode, extended_opcode, data_align, offset, *row))
        {
            if (primary_opcode)
            {
                switch (primary_opcode)
                {
                    case DW_CFA_advance_loc :   // (Row Creation Instruction)
                    {   // 0x40 - high 2 bits are 0x1, lower 6 bits are delta
                        // takes a single argument that represents a constant delta. The
                        // required action is to create a new table row with a location
                        // value that is computed by taking the current entry's location
                        // value and adding (delta * code_align). All other
                        // values in the new row are initially identical to the current row.
                        unwind_plan.AppendRow(row);
                        UnwindPlan::Row *newrow = new UnwindPlan::Row;
                        *newrow = *row.get();
                        row.reset (newrow);
                        row->SlideOffset(extended_opcode * code_align);
                        break;
                    }

                    case DW_CFA_restore     :
                    {   // 0xC0 - high 2 bits are 0x3, lower 6 bits are register
                        // takes a single argument that represents a register number. The
                        // required action is to change the rule for the indicated register
                        // to the rule assigned it by the initial_instructions in the CIE.
                        uint32_t reg_num = extended_opcode;
                        // We only keep enough register locations around to
                        // unwind what is in our thread, and these are organized
                        // by the register index in that state, so we need to convert our
                        // GCC register number from the EH frame info, to a register index

                        if (unwind_plan.IsValidRowIndex(0) && unwind_plan.GetRowAtIndex(0)->GetRegisterInfo(reg_num, reg_location))
                            row->SetRegisterInfo (reg_num, reg_location);
                        break;
                    }
                }
            }
            else
            {
                switch (extended_opcode)
                {
                    case DW_CFA_set_loc             : // 0x1 (Row Creation Instruction)
                    {
                        // DW_CFA_set_loc takes a single argument that represents an address.
                        // The required action is to create a new table row using the
                        // specified address as the location. All other values in the new row
                        // are initially identical to the current row. The new location value
                        // should always be greater than the current one.
                        unwind_plan.AppendRow(row);
                        UnwindPlan::Row *newrow = new UnwindPlan::Row;
                        *newrow = *row.get();
                        row.reset (newrow);
                        row->SetOffset(m_cfi_data.GetPointer(&offset) - startaddr.GetFileAddress());
                        break;
                    }

                    case DW_CFA_advance_loc1        : // 0x2 (Row Creation Instruction)
                    {
                        // takes a single uword argument that represents a constant delta.
                        // This instruction is identical to DW_CFA_advance_loc except for the
                        // encoding and size of the delta argument.
                        unwind_plan.AppendRow(row);
                        UnwindPlan::Row *newrow = new UnwindPlan::Row;
                        *newrow = *row.get();
                        row.reset (newrow);
                        row->SlideOffset (m_cfi_data.GetU8(&offset) * code_align);
                        break;
                    }

                    case DW_CFA_advance_loc2        : // 0x3 (Row Creation Instruction)
                    {
                        // takes a single uword argument that represents a constant delta.
                        // This instruction is identical to DW_CFA_advance_loc except for the
                        // encoding and size of the delta argument.
                        unwind_plan.AppendRow(row);
                        UnwindPlan::Row *newrow = new UnwindPlan::Row;
                        *newrow = *row.get();
                        row.reset (newrow);
                        row->SlideOffset (m_cfi_data.GetU16(&offset) * code_align);
                        break;
                    }

                    case DW_CFA_advance_loc4        : // 0x4 (Row Creation Instruction)
                    {
                        // takes a single uword argument that represents a constant delta.
                        // This instruction is identical to DW_CFA_advance_loc except for the
                        // encoding and size of the delta argument.
                        unwind_plan.AppendRow(row);
                        UnwindPlan::Row *newrow = new UnwindPlan::Row;
                        *newrow = *row.get();
                        row.reset (newrow);
                        row->SlideOffset (m_cfi_data.GetU32(&offset) * code_align);
                        break;
                    }

                    case DW_CFA_restore_extended    : // 0x6
                    {
                        // takes a single unsigned LEB128 argument that represents a register
                        // number. This instruction is identical to DW_CFA_restore except for
                        // the encoding and size of the register argument.
                        uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        if (unwind_plan.IsValidRowIndex(0) && unwind_plan.GetRowAtIndex(0)->GetRegisterInfo(reg_num, reg_location))
                            row->SetRegisterInfo (reg_num, reg_location);
                        break;
                    }

                    case DW_CFA_remember_state      : // 0xA
                    {
                        // These instructions define a stack of information. Encountering the
                        // DW_CFA_remember_state instruction means to save the rules for every
                        // register on the current row on the stack. Encountering the
                        // DW_CFA_restore_state instruction means to pop the set of rules off
                        // the stack and place them in the current row. (This operation is
                        // useful for compilers that move epilogue code into the body of a
                        // function.)
                        stack.push_back (row);
                        UnwindPlan::Row *newrow = new UnwindPlan::Row;
                        *newrow = *row.get();
                        row.reset (newrow);
                        break;
                    }

                    case DW_CFA_restore_state       : // 0xB
                    {
                        // These instructions define a stack of information. Encountering the
                        // DW_CFA_remember_state instruction means to save the rules for every
                        // register on the current row on the stack. Encountering the
                        // DW_CFA_restore_state instruction means to pop the set of rules off
                        // the stack and place them in the current row. (This operation is
                        // useful for compilers that move epilogue code into the body of a
                        // function.)
                        lldb::addr_t offset = row->GetOffset ();
                        row = stack.back ();
                        stack.pop_back ();
                        row->SetOffset (offset);
                        break;
                    }

                    case DW_CFA_val_offset          :   // 0x14
                    case DW_CFA_val_offset_sf       :   // 0x15
                    default:
                        break;
                }
            }
        }
    }
    unwind_plan.AppendRow(row);

    return true;
}
예제 #2
0
bool
CompactUnwindInfo::CreateUnwindPlan_x86_64 (Target &target, FunctionInfo &function_info, UnwindPlan &unwind_plan, Address pc_or_function_start)
{
    unwind_plan.SetSourceName ("compact unwind info");
    unwind_plan.SetSourcedFromCompiler (eLazyBoolYes);
    unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
    unwind_plan.SetRegisterKind (eRegisterKindEHFrame);

    unwind_plan.SetLSDAAddress (function_info.lsda_address);
    unwind_plan.SetPersonalityFunctionPtr (function_info.personality_ptr_address);

    UnwindPlan::RowSP row (new UnwindPlan::Row);

    const int wordsize = 8;
    int mode = function_info.encoding & UNWIND_X86_64_MODE_MASK;
    switch (mode)
    {
        case UNWIND_X86_64_MODE_RBP_FRAME:
        {
            row->GetCFAValue().SetIsRegisterPlusOffset (
                    translate_to_eh_frame_regnum_x86_64 (UNWIND_X86_64_REG_RBP),
                    2 * wordsize);
            row->SetOffset (0);
            row->SetRegisterLocationToAtCFAPlusOffset (x86_64_eh_regnum::rbp, wordsize * -2, true);
            row->SetRegisterLocationToAtCFAPlusOffset (x86_64_eh_regnum::rip, wordsize * -1, true);
            row->SetRegisterLocationToIsCFAPlusOffset (x86_64_eh_regnum::rsp, 0, true);
            
            uint32_t saved_registers_offset = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_RBP_FRAME_OFFSET);

            uint32_t saved_registers_locations = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);

            saved_registers_offset += 2;

            for (int i = 0; i < 5; i++)
            {
                uint32_t regnum = saved_registers_locations & 0x7;
                switch (regnum)
                {
                    case UNWIND_X86_64_REG_NONE:
                        break;
                    case UNWIND_X86_64_REG_RBX:
                    case UNWIND_X86_64_REG_R12:
                    case UNWIND_X86_64_REG_R13:
                    case UNWIND_X86_64_REG_R14:
                    case UNWIND_X86_64_REG_R15:
                        row->SetRegisterLocationToAtCFAPlusOffset (translate_to_eh_frame_regnum_x86_64 (regnum), wordsize * -saved_registers_offset, true);
                        break;
                }
                saved_registers_offset--;
                saved_registers_locations >>= 3;
            }
            unwind_plan.AppendRow (row);
            return true;
        }
        break;

        case UNWIND_X86_64_MODE_STACK_IND:
        {
            // The clang in Xcode 6 is emitting incorrect compact unwind encodings for this
            // style of unwind.  It was fixed in llvm r217020.  
            // The clang in Xcode 7 has this fixed.
            return false;
        }
        break;

        case UNWIND_X86_64_MODE_STACK_IMMD:
        {
            uint32_t stack_size = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
            uint32_t register_count = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
            uint32_t permutation = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);

            if (mode == UNWIND_X86_64_MODE_STACK_IND && function_info.valid_range_offset_start != 0)
            {
                uint32_t stack_adjust = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);

                // offset into the function instructions; 0 == beginning of first instruction
                uint32_t offset_to_subl_insn = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);

                SectionList *sl = m_objfile.GetSectionList ();
                if (sl)
                {
                    ProcessSP process_sp = target.GetProcessSP();
                    if (process_sp)
                    {
                        Address subl_payload_addr (function_info.valid_range_offset_start, sl);
                        subl_payload_addr.Slide (offset_to_subl_insn);
                        Error error;
                        uint64_t large_stack_size = process_sp->ReadUnsignedIntegerFromMemory (subl_payload_addr.GetLoadAddress (&target),
                                4, 0, error);
                        if (large_stack_size != 0 && error.Success ())
                        {
                            // Got the large stack frame size correctly - use it
                            stack_size = large_stack_size + (stack_adjust * wordsize);
                        }
                        else
                        {
                            return false;
                        }
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }

            int32_t offset = mode == UNWIND_X86_64_MODE_STACK_IND ? stack_size : stack_size * wordsize;
            row->GetCFAValue().SetIsRegisterPlusOffset (x86_64_eh_regnum::rsp, offset);

            row->SetOffset (0);
            row->SetRegisterLocationToAtCFAPlusOffset (x86_64_eh_regnum::rip, wordsize * -1, true);
            row->SetRegisterLocationToIsCFAPlusOffset (x86_64_eh_regnum::rsp, 0, true);

            if (register_count > 0)
            {

                // We need to include (up to) 6 registers in 10 bits.
                // That would be 18 bits if we just used 3 bits per reg to indicate
                // the order they're saved on the stack. 
                //
                // This is done with Lehmer code permutation, e.g. see
                // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms
                int permunreg[6] = {0, 0, 0, 0, 0, 0};

                // This decodes the variable-base number in the 10 bits
                // and gives us the Lehmer code sequence which can then
                // be decoded.

                switch (register_count) 
                {
                    case 6:
                        permunreg[0] = permutation/120;    // 120 == 5!
                        permutation -= (permunreg[0]*120);
                        permunreg[1] = permutation/24;     // 24 == 4!
                        permutation -= (permunreg[1]*24);
                        permunreg[2] = permutation/6;      // 6 == 3!
                        permutation -= (permunreg[2]*6);
                        permunreg[3] = permutation/2;      // 2 == 2!
                        permutation -= (permunreg[3]*2);
                        permunreg[4] = permutation;        // 1 == 1!
                        permunreg[5] = 0;
                        break;
                    case 5:
                        permunreg[0] = permutation/120;
                        permutation -= (permunreg[0]*120);
                        permunreg[1] = permutation/24;
                        permutation -= (permunreg[1]*24);
                        permunreg[2] = permutation/6;
                        permutation -= (permunreg[2]*6);
                        permunreg[3] = permutation/2;
                        permutation -= (permunreg[3]*2);
                        permunreg[4] = permutation;
                        break;
                    case 4:
                        permunreg[0] = permutation/60;
                        permutation -= (permunreg[0]*60);
                        permunreg[1] = permutation/12;
                        permutation -= (permunreg[1]*12);
                        permunreg[2] = permutation/3;
                        permutation -= (permunreg[2]*3);
                        permunreg[3] = permutation;
                        break;
                    case 3:
                        permunreg[0] = permutation/20;
                        permutation -= (permunreg[0]*20);
                        permunreg[1] = permutation/4;
                        permutation -= (permunreg[1]*4);
                        permunreg[2] = permutation;
                        break;
                    case 2:
                        permunreg[0] = permutation/5;
                        permutation -= (permunreg[0]*5);
                        permunreg[1] = permutation;
                        break;
                    case 1:
                        permunreg[0] = permutation;
                        break;
                }
                
                // Decode the Lehmer code for this permutation of
                // the registers v. http://en.wikipedia.org/wiki/Lehmer_code

                int registers[6] = { UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE };
                bool used[7] = { false, false, false, false, false, false, false };
                for (uint32_t i = 0; i < register_count; i++)
                {
                    int renum = 0;
                    for (int j = 1; j < 7; j++)
                    {
                        if (used[j] == false)
                        {
                            if (renum == permunreg[i])
                            {
                                registers[i] = j;
                                used[j] = true;
                                break;
                            }
                            renum++;
                        }
                    }
                }

                uint32_t saved_registers_offset = 1;
                saved_registers_offset++;

                for (int i = (sizeof (registers) / sizeof (int)) - 1; i >= 0; i--)
                {
                    switch (registers[i])
                    {
                        case UNWIND_X86_64_REG_NONE:
                            break;
                        case UNWIND_X86_64_REG_RBX:
                        case UNWIND_X86_64_REG_R12:
                        case UNWIND_X86_64_REG_R13:
                        case UNWIND_X86_64_REG_R14:
                        case UNWIND_X86_64_REG_R15:
                        case UNWIND_X86_64_REG_RBP:
                            row->SetRegisterLocationToAtCFAPlusOffset (translate_to_eh_frame_regnum_x86_64 (registers[i]), wordsize * -saved_registers_offset, true);
                            saved_registers_offset++;
                        break;
                    }
                }
            }
            unwind_plan.AppendRow (row);
            return true;
        }
        break;

        case UNWIND_X86_64_MODE_DWARF:
        {
            return false;
        }
        break;

        case 0:
        {
            return false;
        }
        break;
    }
    return false;
}
bool
DWARFCallFrameInfo::FDEToUnwindPlan (dw_offset_t dwarf_offset, Address startaddr, UnwindPlan& unwind_plan)
{
    lldb::offset_t offset = dwarf_offset;
    lldb::offset_t current_entry = offset;

    if (m_section_sp.get() == nullptr || m_section_sp->IsEncrypted())
        return false;

    if (m_cfi_data_initialized == false)
        GetCFIData();

    uint32_t length = m_cfi_data.GetU32 (&offset);
    dw_offset_t cie_offset;
    bool is_64bit = (length == UINT32_MAX);
    if (is_64bit) {
        length = m_cfi_data.GetU64 (&offset);
        cie_offset = m_cfi_data.GetU64 (&offset);
    } else {
        cie_offset = m_cfi_data.GetU32 (&offset);
    }

    assert (cie_offset != 0 && cie_offset != UINT32_MAX);

    // Translate the CIE_id from the eh_frame format, which
    // is relative to the FDE offset, into a __eh_frame section
    // offset
    if (m_is_eh_frame)
    {
        unwind_plan.SetSourceName ("eh_frame CFI");
        cie_offset = current_entry + (is_64bit ? 12 : 4) - cie_offset;
        unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
    }
    else
    {
        unwind_plan.SetSourceName ("DWARF CFI");
        // In theory the debug_frame info should be valid at all call sites
        // ("asynchronous unwind info" as it is sometimes called) but in practice
        // gcc et al all emit call frame info for the prologue and call sites, but
        // not for the epilogue or all the other locations during the function reliably.
        unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
    }
    unwind_plan.SetSourcedFromCompiler (eLazyBoolYes);

    const CIE *cie = GetCIE (cie_offset);
    assert (cie != nullptr);

    const dw_offset_t end_offset = current_entry + length + (is_64bit ? 12 : 4);

    const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress();
    const lldb::addr_t text_addr = LLDB_INVALID_ADDRESS;
    const lldb::addr_t data_addr = LLDB_INVALID_ADDRESS;
    lldb::addr_t range_base = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding, pc_rel_addr, text_addr, data_addr);
    lldb::addr_t range_len = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding & DW_EH_PE_MASK_ENCODING, pc_rel_addr, text_addr, data_addr);
    AddressRange range (range_base, m_objfile.GetAddressByteSize(), m_objfile.GetSectionList());
    range.SetByteSize (range_len);

    addr_t lsda_data_file_address = LLDB_INVALID_ADDRESS;

    if (cie->augmentation[0] == 'z')
    {
        uint32_t aug_data_len = (uint32_t)m_cfi_data.GetULEB128(&offset);
        if (aug_data_len != 0 && cie->lsda_addr_encoding != DW_EH_PE_omit)
        {
            offset_t saved_offset = offset;
            lsda_data_file_address = m_cfi_data.GetGNUEHPointer(&offset, cie->lsda_addr_encoding, pc_rel_addr, text_addr, data_addr);
            if (offset - saved_offset != aug_data_len)
            {
                // There is more in the augmentation region than we know how to process;
                // don't read anything.
                lsda_data_file_address = LLDB_INVALID_ADDRESS;
            }
            offset = saved_offset;
        }
        offset += aug_data_len;
    }
    Address lsda_data;
    Address personality_function_ptr;

    if (lsda_data_file_address != LLDB_INVALID_ADDRESS && cie->personality_loc != LLDB_INVALID_ADDRESS)
    {
        m_objfile.GetModule()->ResolveFileAddress (lsda_data_file_address, lsda_data);
        m_objfile.GetModule()->ResolveFileAddress (cie->personality_loc, personality_function_ptr);
    }

    if (lsda_data.IsValid() && personality_function_ptr.IsValid())
    {
        unwind_plan.SetLSDAAddress (lsda_data);
        unwind_plan.SetPersonalityFunctionPtr (personality_function_ptr);
    }

    uint32_t reg_num = 0;
    int32_t op_offset = 0;
    uint32_t code_align = cie->code_align;
    int32_t data_align = cie->data_align;

    unwind_plan.SetPlanValidAddressRange (range);
    UnwindPlan::Row *cie_initial_row = new UnwindPlan::Row;
    *cie_initial_row = cie->initial_row;
    UnwindPlan::RowSP row(cie_initial_row);

    unwind_plan.SetRegisterKind (m_reg_kind);
    unwind_plan.SetReturnAddressRegister (cie->return_addr_reg_num);

    std::vector<UnwindPlan::RowSP> stack;

    UnwindPlan::Row::RegisterLocation reg_location;
    while (m_cfi_data.ValidOffset(offset) && offset < end_offset)
    {
        uint8_t inst = m_cfi_data.GetU8(&offset);
        uint8_t primary_opcode  = inst & 0xC0;
        uint8_t extended_opcode = inst & 0x3F;

        if (primary_opcode)
        {
            switch (primary_opcode)
            {
                case DW_CFA_advance_loc :   // (Row Creation Instruction)
                    {   // 0x40 - high 2 bits are 0x1, lower 6 bits are delta
                        // takes a single argument that represents a constant delta. The
                        // required action is to create a new table row with a location
                        // value that is computed by taking the current entry's location
                        // value and adding (delta * code_align). All other
                        // values in the new row are initially identical to the current row.
                        unwind_plan.AppendRow(row);
                        UnwindPlan::Row *newrow = new UnwindPlan::Row;
                        *newrow = *row.get();
                        row.reset (newrow);
                        row->SlideOffset(extended_opcode * code_align);
                    }
                    break;

                case DW_CFA_offset      :
                    {   // 0x80 - high 2 bits are 0x2, lower 6 bits are register
                        // takes two arguments: an unsigned LEB128 constant representing a
                        // factored offset and a register number. The required action is to
                        // change the rule for the register indicated by the register number
                        // to be an offset(N) rule with a value of
                        // (N = factored offset * data_align).
                        reg_num = extended_opcode;
                        op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * data_align;
                        reg_location.SetAtCFAPlusOffset(op_offset);
                        row->SetRegisterInfo (reg_num, reg_location);
                    }
                    break;

                case DW_CFA_restore     :
                    {   // 0xC0 - high 2 bits are 0x3, lower 6 bits are register
                        // takes a single argument that represents a register number. The
                        // required action is to change the rule for the indicated register
                        // to the rule assigned it by the initial_instructions in the CIE.
                        reg_num = extended_opcode;
                        // We only keep enough register locations around to
                        // unwind what is in our thread, and these are organized
                        // by the register index in that state, so we need to convert our
                        // GCC register number from the EH frame info, to a register index

                        if (unwind_plan.IsValidRowIndex(0) && unwind_plan.GetRowAtIndex(0)->GetRegisterInfo(reg_num, reg_location))
                            row->SetRegisterInfo (reg_num, reg_location);
                    }
                    break;
            }
        }
        else
        {
            switch (extended_opcode)
            {
                case DW_CFA_nop                 : // 0x0
                    break;

                case DW_CFA_set_loc             : // 0x1 (Row Creation Instruction)
                    {
                        // DW_CFA_set_loc takes a single argument that represents an address.
                        // The required action is to create a new table row using the
                        // specified address as the location. All other values in the new row
                        // are initially identical to the current row. The new location value
                        // should always be greater than the current one.
                        unwind_plan.AppendRow(row);
                        UnwindPlan::Row *newrow = new UnwindPlan::Row;
                        *newrow = *row.get();
                        row.reset (newrow);
                        row->SetOffset(m_cfi_data.GetPointer(&offset) - startaddr.GetFileAddress());
                    }
                    break;

                case DW_CFA_advance_loc1        : // 0x2 (Row Creation Instruction)
                    {
                        // takes a single uword argument that represents a constant delta.
                        // This instruction is identical to DW_CFA_advance_loc except for the
                        // encoding and size of the delta argument.
                        unwind_plan.AppendRow(row);
                        UnwindPlan::Row *newrow = new UnwindPlan::Row;
                        *newrow = *row.get();
                        row.reset (newrow);
                        row->SlideOffset (m_cfi_data.GetU8(&offset) * code_align);
                    }
                    break;

                case DW_CFA_advance_loc2        : // 0x3 (Row Creation Instruction)
                    {
                        // takes a single uword argument that represents a constant delta.
                        // This instruction is identical to DW_CFA_advance_loc except for the
                        // encoding and size of the delta argument.
                        unwind_plan.AppendRow(row);
                        UnwindPlan::Row *newrow = new UnwindPlan::Row;
                        *newrow = *row.get();
                        row.reset (newrow);
                        row->SlideOffset (m_cfi_data.GetU16(&offset) * code_align);
                    }
                    break;

                case DW_CFA_advance_loc4        : // 0x4 (Row Creation Instruction)
                    {
                        // takes a single uword argument that represents a constant delta.
                        // This instruction is identical to DW_CFA_advance_loc except for the
                        // encoding and size of the delta argument.
                        unwind_plan.AppendRow(row);
                        UnwindPlan::Row *newrow = new UnwindPlan::Row;
                        *newrow = *row.get();
                        row.reset (newrow);
                        row->SlideOffset (m_cfi_data.GetU32(&offset) * code_align);
                    }
                    break;

                case DW_CFA_offset_extended     : // 0x5
                    {
                        // takes two unsigned LEB128 arguments representing a register number
                        // and a factored offset. This instruction is identical to DW_CFA_offset
                        // except for the encoding and size of the register argument.
                        reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * data_align;
                        reg_location.SetAtCFAPlusOffset(op_offset);
                        row->SetRegisterInfo (reg_num, reg_location);
                    }
                    break;

                case DW_CFA_restore_extended    : // 0x6
                    {
                        // takes a single unsigned LEB128 argument that represents a register
                        // number. This instruction is identical to DW_CFA_restore except for
                        // the encoding and size of the register argument.
                        reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        if (unwind_plan.IsValidRowIndex(0) && unwind_plan.GetRowAtIndex(0)->GetRegisterInfo(reg_num, reg_location))
                            row->SetRegisterInfo (reg_num, reg_location);
                    }
                    break;

                case DW_CFA_undefined           : // 0x7
                    {
                        // takes a single unsigned LEB128 argument that represents a register
                        // number. The required action is to set the rule for the specified
                        // register to undefined.
                        reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        reg_location.SetUndefined();
                        row->SetRegisterInfo (reg_num, reg_location);
                    }
                    break;

                case DW_CFA_same_value          : // 0x8
                    {
                        // takes a single unsigned LEB128 argument that represents a register
                        // number. The required action is to set the rule for the specified
                        // register to same value.
                        reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        reg_location.SetSame();
                        row->SetRegisterInfo (reg_num, reg_location);
                    }
                    break;

                case DW_CFA_register            : // 0x9
                    {
                        // takes two unsigned LEB128 arguments representing register numbers.
                        // The required action is to set the rule for the first register to be
                        // the second register.

                        reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        uint32_t other_reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        reg_location.SetInRegister(other_reg_num);
                        row->SetRegisterInfo (reg_num, reg_location);
                    }
                    break;

                case DW_CFA_remember_state      : // 0xA
                    {
                        // These instructions define a stack of information. Encountering the
                        // DW_CFA_remember_state instruction means to save the rules for every
                        // register on the current row on the stack. Encountering the
                        // DW_CFA_restore_state instruction means to pop the set of rules off
                        // the stack and place them in the current row. (This operation is
                        // useful for compilers that move epilogue code into the body of a
                        // function.)
                        stack.push_back (row);
                        UnwindPlan::Row *newrow = new UnwindPlan::Row;
                        *newrow = *row.get();
                        row.reset (newrow);
                    }
                    break;

                case DW_CFA_restore_state       : // 0xB
                    // These instructions define a stack of information. Encountering the
                    // DW_CFA_remember_state instruction means to save the rules for every
                    // register on the current row on the stack. Encountering the
                    // DW_CFA_restore_state instruction means to pop the set of rules off
                    // the stack and place them in the current row. (This operation is
                    // useful for compilers that move epilogue code into the body of a
                    // function.)
                    {
                        row = stack.back ();
                        stack.pop_back ();
                    }
                    break;

                case DW_CFA_def_cfa             : // 0xC    (CFA Definition Instruction)
                    {
                        // Takes two unsigned LEB128 operands representing a register
                        // number and a (non-factored) offset. The required action
                        // is to define the current CFA rule to use the provided
                        // register and offset.
                        reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        op_offset = (int32_t)m_cfi_data.GetULEB128(&offset);
                        row->SetCFARegister (reg_num);
                        row->SetCFAOffset (op_offset);
                    }
                    break;

                case DW_CFA_def_cfa_register    : // 0xD    (CFA Definition Instruction)
                    {
                        // takes a single unsigned LEB128 argument representing a register
                        // number. The required action is to define the current CFA rule to
                        // use the provided register (but to keep the old offset).
                        reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        row->SetCFARegister (reg_num);
                    }
                    break;

                case DW_CFA_def_cfa_offset      : // 0xE    (CFA Definition Instruction)
                    {
                        // Takes a single unsigned LEB128 operand representing a
                        // (non-factored) offset. The required action is to define
                        // the current CFA rule to use the provided offset (but
                        // to keep the old register).
                        op_offset = (int32_t)m_cfi_data.GetULEB128(&offset);
                        row->SetCFAOffset (op_offset);
                    }
                    break;

                case DW_CFA_def_cfa_expression  : // 0xF    (CFA Definition Instruction)
                    {
                        size_t block_len = (size_t)m_cfi_data.GetULEB128(&offset);
                        offset += (uint32_t)block_len;
                    }
                    break;

                case DW_CFA_expression          : // 0x10
                    {
                        // Takes two operands: an unsigned LEB128 value representing
                        // a register number, and a DW_FORM_block value representing a DWARF
                        // expression. The required action is to change the rule for the
                        // register indicated by the register number to be an expression(E)
                        // rule where E is the DWARF expression. That is, the DWARF
                        // expression computes the address. The value of the CFA is
                        // pushed on the DWARF evaluation stack prior to execution of
                        // the DWARF expression.
                        reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        uint32_t block_len = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        const uint8_t *block_data = (uint8_t *)m_cfi_data.GetData(&offset, block_len);

                        reg_location.SetAtDWARFExpression(block_data, block_len);
                        row->SetRegisterInfo (reg_num, reg_location);
                    }
                    break;

                case DW_CFA_offset_extended_sf  : // 0x11
                    {
                        // takes two operands: an unsigned LEB128 value representing a
                        // register number and a signed LEB128 factored offset. This
                        // instruction is identical to DW_CFA_offset_extended except
                        //that the second operand is signed and factored.
                        reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align;
                        reg_location.SetAtCFAPlusOffset(op_offset);
                        row->SetRegisterInfo (reg_num, reg_location);
                    }
                    break;

                case DW_CFA_def_cfa_sf          : // 0x12   (CFA Definition Instruction)
                    {
                        // Takes two operands: an unsigned LEB128 value representing
                        // a register number and a signed LEB128 factored offset.
                        // This instruction is identical to DW_CFA_def_cfa except
                        // that the second operand is signed and factored.
                        reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align;
                        row->SetCFARegister (reg_num);
                        row->SetCFAOffset (op_offset);
                    }
                    break;

                case DW_CFA_def_cfa_offset_sf   : // 0x13   (CFA Definition Instruction)
                    {
                        // takes a signed LEB128 operand representing a factored
                        // offset. This instruction is identical to  DW_CFA_def_cfa_offset
                        // except that the operand is signed and factored.
                        op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align;
                        row->SetCFAOffset (op_offset);
                    }
                    break;

                case DW_CFA_val_expression      :   // 0x16
                    {
                        // takes two operands: an unsigned LEB128 value representing a register
                        // number, and a DW_FORM_block value representing a DWARF expression.
                        // The required action is to change the rule for the register indicated
                        // by the register number to be a val_expression(E) rule where E is the
                        // DWARF expression. That is, the DWARF expression computes the value of
                        // the given register. The value of the CFA is pushed on the DWARF
                        // evaluation stack prior to execution of the DWARF expression.
                        reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        uint32_t block_len = (uint32_t)m_cfi_data.GetULEB128(&offset);
                        const uint8_t* block_data = (uint8_t*)m_cfi_data.GetData(&offset, block_len);
//#if defined(__i386__) || defined(__x86_64__)
//                      // The EH frame info for EIP and RIP contains code that looks for traps to
//                      // be a specific type and increments the PC.
//                      // For i386:
//                      // DW_CFA_val_expression where:
//                      // eip = DW_OP_breg6(+28), DW_OP_deref, DW_OP_dup, DW_OP_plus_uconst(0x34),
//                      //       DW_OP_deref, DW_OP_swap, DW_OP_plus_uconst(0), DW_OP_deref,
//                      //       DW_OP_dup, DW_OP_lit3, DW_OP_ne, DW_OP_swap, DW_OP_lit4, DW_OP_ne,
//                      //       DW_OP_and, DW_OP_plus
//                      // This basically does a:
//                      // eip = ucontenxt.mcontext32->gpr.eip;
//                      // if (ucontenxt.mcontext32->exc.trapno != 3 && ucontenxt.mcontext32->exc.trapno != 4)
//                      //   eip++;
//                      //
//                      // For x86_64:
//                      // DW_CFA_val_expression where:
//                      // rip =  DW_OP_breg3(+48), DW_OP_deref, DW_OP_dup, DW_OP_plus_uconst(0x90), DW_OP_deref,
//                      //          DW_OP_swap, DW_OP_plus_uconst(0), DW_OP_deref_size(4), DW_OP_dup, DW_OP_lit3,
//                      //          DW_OP_ne, DW_OP_swap, DW_OP_lit4, DW_OP_ne, DW_OP_and, DW_OP_plus
//                      // This basically does a:
//                      // rip = ucontenxt.mcontext64->gpr.rip;
//                      // if (ucontenxt.mcontext64->exc.trapno != 3 && ucontenxt.mcontext64->exc.trapno != 4)
//                      //   rip++;
//                      // The trap comparisons and increments are not needed as it hoses up the unwound PC which
//                      // is expected to point at least past the instruction that causes the fault/trap. So we
//                      // take it out by trimming the expression right at the first "DW_OP_swap" opcodes
//                      if (block_data != NULL && thread->GetPCRegNum(Thread::GCC) == reg_num)
//                      {
//                          if (thread->Is64Bit())
//                          {
//                              if (block_len > 9 && block_data[8] == DW_OP_swap && block_data[9] == DW_OP_plus_uconst)
//                                  block_len = 8;
//                          }
//                          else
//                          {
//                              if (block_len > 8 && block_data[7] == DW_OP_swap && block_data[8] == DW_OP_plus_uconst)
//                                  block_len = 7;
//                          }
//                      }
//#endif
                        reg_location.SetIsDWARFExpression(block_data, block_len);
                        row->SetRegisterInfo (reg_num, reg_location);
                    }
                    break;

                case DW_CFA_val_offset          :   // 0x14
                case DW_CFA_val_offset_sf       :   // 0x15
                default:
                    break;
            }
        }
    }
    unwind_plan.AppendRow(row);

    return true;
}