REhints/HexRaysCodeXplorer

View on GitHub
src/HexRaysCodeXplorer/MSVCObjectFormatParser.cpp

Summary

Maintainability
Test Coverage
/*    Copyright (c) 2013-2020
REhints <info@rehints.com>
All rights reserved.

==============================================================================

This file is part of HexRaysCodeXplorer

HexRaysCodeXplorer is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see
<http://www.gnu.org/licenses/>.

==============================================================================
*/

#include "MSVCObjectFormatParser.h"
#include "ObjectExplorer.h"
#include "Utility.h"


#if !defined (__LINUX__) && !defined (__MAC__)
#include <tchar.h>
#else
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif


//---------------------------------------------------------------------------
// MSVC VTBL parsing
//
// Based on some impressions and code from ClassInformer plugin
// http://sourceforge.net/projects/classinformer/
//---------------------------------------------------------------------------

// Attempt to get information of and fix vftable at address
// Return TRUE along with info if valid vftable parsed at address
bool vftable::getTableInfo(ea_t ea, vtinfo &info)
{
    ZeroMemory(&info, sizeof(vtinfo));

    // Start of a vft should have an xref and a name (auto, or user, etc).
    // Ideal flags 32bit: FF_DWRD, FF_0OFF, FF_REF, FF_NAME, FF_DATA, FF_IVL
    //dumpFlags(ea);
    flags_t flags = get_flags(ea);
    if (has_xref(flags) && has_any_name(flags) && (isEa(flags) || is_unknown(flags)))
    {
        // Get raw (auto-generated mangled, or user named) vft name
        //if (!get_name(BADADDR, ea, info.name, SIZESTR(info.name)))
        //    logmsg(DEBUG, EAFORMAT" ** vftable::getTableInfo(): failed to get raw name!\n", ea);

        // Determine the vft's method count
        ea_t start = info.start = ea;
        while (TRUE)
        {
            // Should be an ea_t offset to a function here (could be unknown if dirty IDB)
            // Ideal flags for 32bit: FF_DWRD, FF_0OFF, FF_REF, FF_NAME, FF_DATA, FF_IVL
            //dumpFlags(ea);
            flags_t indexFlags = get_flags(ea);
            if (!(isEa(indexFlags) || is_unknown(indexFlags)))
            {
                break;
            }

            // Look at what this (assumed vftable index) points too
            ea_t memberPtr = getEa(ea);
            if (!(memberPtr && (memberPtr != BADADDR)))
            {
                // vft's often have a zero ea_t (NULL pointer?) following, fix it
                if (memberPtr == 0)
                    fixEa(ea);

                break;
            }

            // Should see code for a good vft method here, but it could be dirty
            flags_t flags = get_flags(memberPtr);
            if (!(is_code(flags) || is_unknown(flags)))
            {
                break;
            }

            if (ea != start)
            {
                // If we see a ref after first index it's probably the beginning of the next vft or something else
                if (has_xref(indexFlags))
                {
                    break;
                }

                // If we see a COL here it must be the start of another vftable
                if (RTTI::_RTTICompleteObjectLocator::isValid(memberPtr))
                {
                    break;
                }
            }

            // As needed fix ea_t pointer, and, or, missing code and function def here
            fixEa(ea);
            fixFunction(memberPtr);

            ea += sizeof(ea_t);
        };

        // Reached the presumed end of it
        if ((info.methodCount = ((ea - start) / sizeof(ea_t))) > 0)
        {
            info.end = ea;
            return true;
        }
    }

    return false;
}


//---------------------------------------------------------------------------
// MSVC RTTI parsing
//
// Based on some impressions and code from ClassInformer plugin
// http://sourceforge.net/projects/classinformer/
//---------------------------------------------------------------------------

// Skip type_info tag for class/struct mangled name strings
#define SKIP_TD_TAG(_str) ((_str) + _countof(".?Ax") - 1)

// Class name list container
struct bcdInfo
{
    qstring m_name;
    UINT m_attribute;
    RTTI::PMD m_pmd;
};
typedef qvector<bcdInfo> bcdList;

namespace RTTI
{
    static bool getBCDInfo(ea_t col, bcdList &nameList, OUT UINT &numBaseClasses);
};


typedef std::map<ea_t, qstring> stringMap;
static stringMap stringCache;
static eaSet tdSet;
static eaSet chdSet;
static eaSet bcdSet;

void RTTI::freeWorkingData()
{
    stringCache.clear();
    tdSet.clear();
    chdSet.clear();
    bcdSet.clear();
}


// Return a short label indicating the CHD inheritance type by attributes
// TODO: Consider CHD_AMBIGUOUS?
static LPCSTR attributeLabel(UINT attributes)
{
    if ((attributes & 3) == RTTI::CHD_MULTINH)
        return((char *) "[MI]");
    else
        if ((attributes & 3) == RTTI::CHD_VIRTINH)
            return((char *) "[VI]");
        else
            if ((attributes & 3) == (RTTI::CHD_MULTINH | RTTI::CHD_VIRTINH))
                return((char *) "[MI VI]");
            else
                return((char *) "");
}

// Read a string from IDB at address
static size_t readIdaString(ea_t ea, qstring& rv)
{
    // Return cached name if it exists
    auto it = stringCache.find(ea);
    if (it != stringCache.end())
    {
        rv = it->second;
        return rv.length();
    }

    // Read string at ea if it exists
    auto len = get_max_strlit_length(ea, STRTYPE_C, ALOPT_IGNHEADS);
    if (!len)
        return 0;

    rv.reserve(len + 4);
    if (get_strlit_contents(&rv, ea, len, STRTYPE_C) <= 0)
        return 0;

    // Cache it
    stringCache[ea] = rv;
    return rv.length();
}


// --------------------------- Type descriptor ---------------------------

// Get type name into a buffer
// type_info assumed to be valid
bool RTTI::type_info::getName(ea_t typeInfo, qstring& outName)
{
    return readIdaString(typeInfo + offsetof(type_info, _M_d_name), outName) > 0;
}

// A valid type_info/TypeDescriptor at pointer?
bool RTTI::type_info::isValid(ea_t typeInfo)
{
    // TRUE if we've already seen it
    if (tdSet.find(typeInfo) != tdSet.end())
        return true;

    if (is_loaded(typeInfo))
    {
        // Verify what should be a vftable
        ea_t ea = getEa(typeInfo + offsetof(type_info, vfptr));
        if (is_loaded(ea))
        {
            // _M_data should be NULL statically
            ea_t _M_data = BADADDR;
            if (getVerifyEa((typeInfo + offsetof(type_info, _M_data)), _M_data))
            {
                if (_M_data == 0)
                    return(isTypeName(typeInfo + offsetof(type_info, _M_d_name)));
            }
        }
    }

    return false;
}

// Returns TRUE if known typename at address
bool RTTI::type_info::isTypeName(ea_t name)
{
    //    char demangledStr[MAXSTR];
    // Should start with a period
    if (get_byte(name) == '.')
    {
        // Read the rest of the possible name string
        qstring buffer;

        if (readIdaString(name, buffer) && buffer.length() > 3)
        {
            // Should be valid if it properly demangles
            //            if (demangle_name(demangledStr, (MAXSTR), buffer, (MT_MSCOMP | MNG_NODEFINIT)) >= 0)
            //if (LPSTR s = __unDName(NULL, buffer + 1 /*skip the '.'*/, 0, malloc, free, (UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY)))
            if ((buffer[0] == '.') && (buffer[1] == '?') && (buffer[2] == 'A') && (buffer[3] == 'V'))
            {
                //                free(s);
                return true;
            }
        }
    }
    return false;
}

// --------------------------- Complete Object Locator ---------------------------

// Return TRUE if address is a valid RTTI structure
bool RTTI::_RTTICompleteObjectLocator::isValid(ea_t col)
{
    if (!is_loaded(col))
        return false;

    // Check signature
    UINT signature = -1;
    if (!getVerify32_t(col + ea_t(offsetof(_RTTICompleteObjectLocator, signature)), signature) || signature != 0)
        return false;

    // Check valid type_info
    ea_t typeInfo = getEa(col + ea_t(offsetof(_RTTICompleteObjectLocator, typeDescriptor)));
    if (!RTTI::type_info::isValid(typeInfo))
        return false;

    ea_t classDescriptor = getEa(col + ea_t(offsetof(_RTTICompleteObjectLocator, classDescriptor)));

    return RTTI::_RTTIClassHierarchyDescriptor::isValid(classDescriptor, 0);
}

// Same as above but from an already validated type_info perspective

bool RTTI::_RTTICompleteObjectLocator::isValid2(ea_t col)
{
    // 'signature' should be zero
    UINT signature = -1;
    if (!getVerify32_t((col + ea_t(offsetof(_RTTICompleteObjectLocator, signature))), signature) || signature != 0)
        return false;

    // Verify CHD
    ea_t classDescriptor = getEa(col + offsetof(_RTTICompleteObjectLocator, classDescriptor));
    if (classDescriptor && (classDescriptor != BADADDR))
        return RTTI::_RTTIClassHierarchyDescriptor::isValid(classDescriptor, 0);

    return false;
}


// --------------------------- Base Class Descriptor ---------------------------

// Return TRUE if address is a valid BCD
bool RTTI::_RTTIBaseClassDescriptor::isValid(ea_t bcd, ea_t colBase64)
{
    // TRUE if we've already seen it
    if (bcdSet.find(bcd) != bcdSet.end())
        return true;

    if (!is_loaded(bcd))
        return false;

    // Check attributes flags first
    UINT attributes = -1;
    if (getVerify32_t(bcd + ea_t(offsetof(_RTTIBaseClassDescriptor, attributes)), attributes))
    {
        // Valid flags are the lower byte only
        if ((attributes & 0xFFFFFF00) == 0)
        {
            // Check for valid type_info
#ifndef __EA64__
            return RTTI::type_info::isValid(getEa(bcd + ea_t(offsetof(_RTTIBaseClassDescriptor, typeDescriptor))));
#else
            UINT tdOffset = get_32bit(bcd + ea_t(offsetof(_RTTIBaseClassDescriptor, typeDescriptor)));
            ea_t typeInfo = colBase64 + (UINT64)tdOffset;
            return RTTI::type_info::isValid(typeInfo);
#endif
        }
    }

    return false;
}

// --------------------------- Class Hierarchy Descriptor ---------------------------

// Return true if address is a valid CHD structure
bool RTTI::_RTTIClassHierarchyDescriptor::isValid(ea_t chd, ea_t colBase64)
{
    // TRUE if we've already seen it
    if (chdSet.find(chd) != chdSet.end())
        return true;

    if (!is_loaded(chd))
        return false;

    // signature should be zero statically
    UINT signature = -1;
    if (!getVerify32_t((chd + offsetof(_RTTIClassHierarchyDescriptor, signature)), signature) || signature != 0)
        return false;

    // Check attributes flags
    UINT attributes = -1;
    if (!getVerify32_t(chd + offsetof(_RTTIClassHierarchyDescriptor, attributes), attributes))
        return false;

    // Valid flags are the lower nibble only
    if (attributes & 0xFFFFFFF0)
        return false;

    // Should have at least one base class
    UINT numBaseClasses = 0;
    if (!getVerify32_t((chd + offsetof(_RTTIClassHierarchyDescriptor, numBaseClasses)), numBaseClasses))
        return false;

    if (!numBaseClasses)
        return false;

    // Check the first BCD entry
#ifndef __EA64__
    ea_t baseClassArray = getEa(chd + offsetof(_RTTIClassHierarchyDescriptor, baseClassArray));
#else
    UINT baseClassArrayOffset = get_32bit(chd + offsetof(_RTTIClassHierarchyDescriptor, baseClassArray));
    ea_t baseClassArray = colBase64 + (UINT64)baseClassArrayOffset;
#endif

    if (!is_loaded(baseClassArray))
        return false;

#ifndef __EA64__
    ea_t baseClassDescriptor = getEa(baseClassArray);
    return RTTI::_RTTIBaseClassDescriptor::isValid(baseClassDescriptor, 0);
#else
    ea_t baseClassDescriptor = colBase64 + (UINT64)get_32bit(baseClassArray);
    return RTTI::_RTTIBaseClassDescriptor::isValid(baseClassDescriptor, colBase64);
#endif

    return false;
}


// Get list of base class descriptor info
static bool RTTI::getBCDInfo(ea_t col, bcdList &list, OUT UINT &numBaseClasses)
{
    numBaseClasses = 0;

#ifndef __EA64__
    ea_t chd = getEa(col + offsetof(_RTTICompleteObjectLocator, classDescriptor));
#else
    UINT cdOffset = get_32bit(col + offsetof(RTTI::_RTTICompleteObjectLocator, classDescriptor));
    UINT objectLocator = get_32bit(col + offsetof(RTTI::_RTTICompleteObjectLocator, objectBase));
    ea_t colBase = (col - (UINT64)objectLocator);
    ea_t chd = (colBase + (UINT64)cdOffset);
#endif

    if (!chd)
        return false;

    if (!(numBaseClasses = get_32bit(chd + offsetof(_RTTIClassHierarchyDescriptor, numBaseClasses))))
        return true;

    list.resize(numBaseClasses);

    // Get pointer
#ifndef __EA64__
    ea_t baseClassArray = getEa(chd + offsetof(_RTTIClassHierarchyDescriptor, baseClassArray));
#else
    UINT bcaOffset = get_32bit(chd + offsetof(_RTTIClassHierarchyDescriptor, baseClassArray));
    ea_t baseClassArray = (colBase + (UINT64)bcaOffset);
#endif

    if (!::is_mapped(baseClassArray))
        return false;

    for (UINT i = 0; i < numBaseClasses; ++i, baseClassArray += sizeof(UINT)) // sizeof(ea_t)
    {
#ifndef __EA64__
        // Get next BCD
        ea_t bcd = getEa(baseClassArray);
        if (!is_mapped(bcd))
            continue;

        // Get type name
        ea_t typeInfo = getEa(bcd + offsetof(_RTTIBaseClassDescriptor, typeDescriptor));
#else
        UINT bcdOffset = get_32bit(baseClassArray);
        ea_t bcd = (colBase + (UINT64)bcdOffset);

        UINT tdOffset = get_32bit(bcd + offsetof(_RTTIBaseClassDescriptor, typeDescriptor));
        ea_t typeInfo = (colBase + (UINT64)tdOffset);
#endif
        if (!is_mapped(typeInfo))
            continue;

        bcdInfo& bi = list[i];
        type_info::getName(typeInfo, bi.m_name);

        // Add info to list
        UINT mdisp = get_32bit(bcd + (offsetof(_RTTIBaseClassDescriptor, pmd) + offsetof(PMD, mdisp)));
        UINT pdisp = get_32bit(bcd + (offsetof(_RTTIBaseClassDescriptor, pmd) + offsetof(PMD, pdisp)));
        UINT vdisp = get_32bit(bcd + (offsetof(_RTTIBaseClassDescriptor, pmd) + offsetof(PMD, vdisp)));
        // As signed int
        bi.m_pmd.mdisp = *((PINT)&mdisp);
        bi.m_pmd.pdisp = *((PINT)&pdisp);
        bi.m_pmd.vdisp = *((PINT)&vdisp);
        bi.m_attribute = get_32bit(bcd + offsetof(_RTTIBaseClassDescriptor, attributes));
    }
    return true;
}


// Process RTTI vftable info
bool RTTI::processVftable(ea_t vft, ea_t col, vftable::vtinfo &vi)
{
    // Get vftable info
    if (!vftable::getTableInfo(vft, vi))
        return false;

    bool sucess = false;
    qstring plainName;

    ea_t typeInfo = getEa(col + offsetof(_RTTICompleteObjectLocator, typeDescriptor));
    ea_t chd = get_32bit(col + offsetof(_RTTICompleteObjectLocator, classDescriptor));

    qstring colName;
    type_info::getName(typeInfo, colName);

    qstring demangledColName;
    getPlainTypeName(colName, demangledColName);
    UINT chdAttributes = get_32bit(chd + offsetof(_RTTIClassHierarchyDescriptor, attributes));
    UINT offset = get_32bit(col + offsetof(_RTTICompleteObjectLocator, offset));

    // Parse BCD info
    bcdList list;
    UINT numBaseClasses = 0;
    if (!getBCDInfo(col, list, numBaseClasses))
        return false;

    bool isTopLevel = false;
    qstring cmt;

    // ======= Simple or no inheritance
    if (offset == 0 && (chdAttributes & (CHD_MULTINH | CHD_VIRTINH)) == 0) {
        // Build object hierarchy string
        int placed = 0;
        if (numBaseClasses > 1) {
            // Parent
            getPlainTypeName(list[0].m_name, plainName);
            cmt.sprnt("%s%s: ", list[0].m_name.length() < 4 || list[0].m_name[3] == 'V' ? "" : "struct ", plainName.c_str());
            placed++;
            isTopLevel = list[0].m_name == colName;

            // Child object hierarchy
            for (UINT i = 1; i < numBaseClasses; i++)
            {
                // Append name
                getPlainTypeName(list[i].m_name, plainName);
                cmt.cat_sprnt("%s%s, ", list[i].m_name.length() < 4 || list[i].m_name[3] == 'V' ? "" : "struct ", plainName.c_str());
                placed++;
            }

            // Nix the ending ',' for the last one
            if (placed > 1)
                cmt.remove(cmt.length() - 2, 2);
        }
        else {
            // Plain, no inheritance object(s)
            cmt.sprnt("%s%s", colName.length() < 4 || colName[3] == 'V' ? "" : "struct ", demangledColName.c_str());
            isTopLevel = true;
        }

        vi.type_info = cmt;
        return true;
    }

    // ======= Multiple inheritance, and, or, virtual inheritance hierarchies
    bcdInfo *bi = nullptr;
    int index = 0;

    // Must be the top level object for the type
    if (offset == 0)
    {
        //_ASSERT(strcmp(colName, list[0].m_name) == 0);
        bi = &list[0];
        isTopLevel = true;
    }
    else
    {
        // Get our object BCD level by matching COL offset to displacement
        for (UINT i = 0; i < numBaseClasses; i++)
        {
            if (list[i].m_pmd.mdisp == offset)
            {
                bi = &list[i];
                index = i;
                break;
            }
        }

        // If not found in list, use the first base object instead
        if (!bi)
        {
            //logmsg(DEBUG, "** "EAFORMAT" MI COL class offset: %X(%d) not in BCD.\n", vft, offset, offset);
            for (UINT i = 0; i < numBaseClasses; i++)
            {
                if (list[i].m_pmd.pdisp != -1)
                {
                    bi = &list[i];
                    index = i;
                    break;
                }
            }
        }
    }

    if (bi)
    {
        // Top object level layout
        int placed = 0;
        if (isTopLevel)
        {
            // Build hierarchy string starting with parent
            getPlainTypeName(list[0].m_name, plainName);
            cmt.sprnt("%s%s: ", list[0].m_name.length() < 4 || list[0].m_name[3] == 'V' ? "" : "struct ", plainName.c_str());
            placed++;

            // Concatenate forward child hierarchy
            for (UINT i = 1; i < numBaseClasses; i++)
            {
                getPlainTypeName(list[i].m_name, plainName);
                cmt.cat_sprnt("%s%s, ", list[i].m_name.length() < 4 || list[i].m_name[3] == 'V' ? "" : "struct ", plainName.c_str());
                placed++;
            }
            if (placed > 1)
                cmt.remove(cmt.length() - 2, 2);
        }
        else
        {
            // Combine COL and CHD name
//            char combinedName[MAXSTR] = {};
//            _snprintf(combinedName, _countof(combinedName) - 1, "%s6B%s@", SKIP_TD_TAG(colName), SKIP_TD_TAG(bi->m_name));

            // Build hierarchy string starting with parent
            getPlainTypeName(bi->m_name, plainName);
            cmt.sprnt("%s%s: ", bi->m_name.length() < 4 || bi->m_name[3] == 'V' ? "" : "struct ", plainName.c_str());
            placed++;

            // Concatenate forward child hierarchy
            if (++index < (int)numBaseClasses)
            {
                for (; index < (int)numBaseClasses; index++)
                {
                    getPlainTypeName(list[index].m_name, plainName);
                    cmt.cat_sprnt("%s%s, ", list[index].m_name.length() < 4 || list[index].m_name[3] == 'V' ? "" : "struct ", plainName.c_str());
                    placed++;
                }
                if (placed > 1)
                    cmt.remove(cmt.length() - 2, 2);
            }
        }
        // if (placed > 1)
        //     cmt += ';';
        // cmt.cat_sprnt(" %s", attributeLabel(chdAttributes));        vi.type_info = cmt;
        return true;
    }

    return false;
}


//---------------------------------------------------------------------------
// MSVC parsing core
//---------------------------------------------------------------------------

#if defined (__LINUX__) || defined (__MAC__)
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif

namespace {

static eaList colList;
static std::map<ea_t, vftable::vtinfo> rtti_vftables;

} // anonymous

static void freeWorkingData() {
    RTTI::freeWorkingData();
    colList.clear();
    rtti_vftables.clear();
}


// Return TRUE if address as a anterior comment
inline BOOL hasAnteriorComment(ea_t ea)
{
    return(get_first_free_extra_cmtidx(ea, E_PREV) != E_PREV);
}

// Delete any anterior comment(s) at address if there is some
inline void killAnteriorComments(ea_t ea)
{
    delete_extra_cmts(ea, E_PREV);
}

// Force a memory location to be DWORD size
void fixDword(ea_t ea)
{
    if (!is_dword(get_flags(ea)))
    {
        setUnknown(ea, sizeof(DWORD));
        create_dword(ea, sizeof(DWORD));
    }
}

// Force memory location to be ea_t size
void fixEa(ea_t ea)
{
#ifndef __EA64__
    if (!is_dword(get_flags(ea)))
#else
    if (!is_qword(get_flags(ea)))
#endif
    {
        setUnknown(ea, sizeof(ea_t));
#ifndef __EA64__
        create_dword(ea, sizeof(ea_t));
#else
        create_qword(ea, sizeof(ea_t));
#endif
    }
}

// Make address a function
void fixFunction(ea_t ea)
{
    flags_t flags = get_flags(ea);
    if (!is_code(flags))
    {
        create_insn(ea);
        add_func(ea, BADADDR);
    }
    else
        if (!is_func(flags))
            add_func(ea, BADADDR);
}

// Get IDA EA bit value with verification
bool getVerifyEa(ea_t ea, ea_t &rValue)
{
    // Location valid?
    if (is_loaded(ea))
    {
        // Get ea_t value
        rValue = getEa(ea);
        return true;
    }

    return false;
}


// Undecorate to minimal class name
// typeid(T).name()
// http://en.wikipedia.org/wiki/Name_mangling
// http://en.wikipedia.org/wiki/Visual_C%2B%2B_name_mangling
// http://www.agner.org/optimize/calling_conventions.pdf

bool getPlainTypeName(const qstring& mangled, qstring& outStr)
{
    outStr.clear();

    // Use CRT function for type names
    if (!mangled.empty() && mangled[0] == '.')
    {
        /*
        __unDName(outStr, mangled + 1, MAXSTR, malloc, free, (UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY | UNDNAME_NO_ECSU));
        if ((outStr[0] == 0) || (strcmp((mangled + 1), outStr) == 0))
        {
        logmsg(ERROR, "** getPlainClassName:__unDName() failed to unmangle! input: \"%s\"\n", mangled);
        return(FALSE);
        }
        */
        outStr = mangled;
    }
    else
        // IDA demangler for everything else
    {
        int result = demangle_name(&outStr, mangled.c_str(), (MT_MSCOMP | MNG_NODEFINIT));
        if (result < 0)
            return false;

        // No inhibit flags will drop this

        if (auto p = outStr.find("::`vftable'"))
            outStr.resize(p);
    }

    return true;
}

// Scan segment for COLs
void idaapi scanSeg4Cols(segment_t *seg)
{
    unsigned int found = 0;
    if (seg->size() < sizeof(RTTI::_RTTICompleteObjectLocator))
        return;

    ea_t startEA = ((seg->start_ea + sizeof(UINT)) & ~((ea_t)(sizeof(UINT) - 1)));
    ea_t endEA = (seg->end_ea - sizeof(RTTI::_RTTICompleteObjectLocator));

    for (ea_t ptr = startEA; ptr < endEA;)
    {
        // TypeDescriptor address here?
        ea_t ea = getEa(ptr);
        if (ea >= 0x10000)
        {
            if (RTTI::type_info::isValid(ea))
            {
                // yes, a COL here?
                ea_t col = (ptr - offsetof(RTTI::_RTTICompleteObjectLocator, typeDescriptor));
                if (RTTI::_RTTICompleteObjectLocator::isValid2(col))
                {
                    // yes
                    colList.push_front(col);
                    ptr += sizeof(RTTI::_RTTICompleteObjectLocator);
                    continue;
                }
            }
        }

        ptr += sizeof(unsigned int);
    }
}
//
// Locate COL by descriptor list
void idaapi findCols()
{
    // Usually in ".rdata" seg, try it first
    std::set<segment_t *> segSet;
    if (segment_t *seg = get_segm_by_name(".rdata"))
    {
        segSet.insert(seg);
        scanSeg4Cols(seg);
    }

    // And ones named ".data"
    int segCount = get_segm_qty();
    for (int i = 0; i < segCount; ++i)
    {
        segment_t *seg = getnseg(i);
        if (!seg)
            continue;

        if (seg->type != SEG_DATA)
            continue;

        if (segSet.find(seg) == segSet.end())
        {
            qstring name;
            if (get_segm_name(&name, seg) <= 0)
                continue;

            if (name == ".data" || name == "_data")
            {
                segSet.insert(seg);
                scanSeg4Cols(seg);
            }
        }
    }

    // If still none found, try any remaining data type segments
    if (colList.empty())
    {
        for (int i = 0; i < segCount; i++)
        {
            segment_t *seg = getnseg(i);
            if (!seg || seg->type != SEG_DATA)
                continue;
            if (segSet.find(seg) == segSet.end())
            {
                segSet.insert(seg);
                scanSeg4Cols(seg);
            }
        }
    }

    return;
}

// Locate vftables
void idaapi scanSeg4Vftables(segment_t *seg, eaRefMap &colMap)
{
    //UINT found = 0;
    if (seg->size() <= sizeof(ea_t))
        return;

    ea_t startEA = ((seg->start_ea + sizeof(ea_t)) & ~((ea_t)(sizeof(ea_t) - 1)));
    ea_t endEA = (seg->end_ea - sizeof(ea_t));

    if (startEA >= endEA)
        return;

    eaRefMap::iterator colEnd = colMap.end();

    for (ea_t ptr = startEA; ptr < endEA; ptr += sizeof(UINT))
    {
        // COL here?
        ea_t ea = getEa(ptr);
        eaRefMap::iterator it = colMap.find(ea);
        if (it == colEnd)
            continue;

        // yes, look for vftable one ea_t below
        ea_t vfptr = ptr + sizeof(ea_t);
        ea_t method = getEa(vfptr);
        // Points to code?
        if (segment_t *s = getseg(method))
        {
            // yes,
            if (s->type == SEG_CODE)
            {
                vftable::vtinfo vi;
                if (RTTI::processVftable(vfptr, it->first, vi)) {
                    rtti_vftables[vfptr] = vi;
                }

                it->second++;
                //found++;
            }
        }
    }
}
//
void idaapi findVftables()
{
    // COLs in a hash map for speed, plus match counts
    eaRefMap colMap;
    for (ea_t ea : colList)
        colMap[ea] = 0;

    // Usually in ".rdata", try first.
    std::set<segment_t *> segSet;
    if (segment_t *seg = get_segm_by_name(".rdata"))
    {
        segSet.insert(seg);
        scanSeg4Vftables(seg, colMap);
    }

    // And ones named ".data"
    int segCount = get_segm_qty();
    for (int i = 0; i < segCount; i++)
    {
        segment_t *seg = getnseg(i);
        if (!seg || seg->type != SEG_DATA)
            continue;

        if (segSet.find(seg) == segSet.end())
        {
            qstring name;
            if (get_segm_name(&name, seg) > 0 && name == ".data")
            {
                segSet.insert(seg);
                scanSeg4Vftables(seg, colMap);
            }
        }
    }

    // If still none found, try any remaining data type segments
    if (colList.empty())
    {
        for (int i = 0; i < segCount; i++)
        {
            segment_t *seg = getnseg(i);
            if (!seg || seg->type == SEG_DATA)
                continue;

            if (segSet.find(seg) == segSet.end())
            {
                segSet.insert(seg);
                scanSeg4Vftables(seg, colMap);
            }
        }
    }

    // Rebuild 'colList' with any that were not located
    if (!colList.empty())
    {
        colList.clear();
        for (eaRefMap::const_iterator it = colMap.begin(), end = colMap.end(); it != end; ++it)
        {
            if (it->second == 0)
                colList.push_front(it->first);
        }
    }
}

static void buildReconstructableTypes() {

}


MSVCObjectFormatParser::~MSVCObjectFormatParser()
{
}

void MSVCObjectFormatParser::get_rtti_info()
{
    freeWorkingData();

    findCols();

    findVftables();

    buildReconstructableTypes();
}

void MSVCObjectFormatParser::clear_info()
{
    freeWorkingData();
}