2425 lines
71 KiB
C++
2425 lines
71 KiB
C++
|
|
#include "tilbuild.hpp"
|
|
#include "misc.cpp"
|
|
|
|
//#define PDEB
|
|
//#define PDEBSYM
|
|
#ifdef PDEB
|
|
#define ddeb(x) _ddeb x
|
|
AS_PRINTF(1, 2) inline void _ddeb(const char *format, ...)
|
|
{
|
|
va_list va;
|
|
va_start(va, format);
|
|
vmsg(format, va);
|
|
va_end(va);
|
|
}
|
|
#else
|
|
#define ddeb(x) (void)0
|
|
#endif
|
|
#ifndef TESTABLE_BUILD
|
|
#undef PDEB
|
|
#undef PDEBSYM
|
|
#endif
|
|
|
|
static const char fake_vtable_type[] = "$vt";
|
|
|
|
//----------------------------------------------------------------------------
|
|
void til_builder_t::remove_anonymous_namespaces(qstring &buf)
|
|
{
|
|
char *p = buf.begin();
|
|
while ( true )
|
|
{ // 1234567890
|
|
p = strstr(p, "`anonymous");
|
|
if ( p == NULL )
|
|
break;
|
|
const char *q = p + 10;
|
|
if ( *q != '-' && *q != ' ' )
|
|
break;
|
|
if ( strncmp(q+1, "namespace'::", 12) != 0 )
|
|
break; // 123456789012
|
|
size_t idx = p - buf.begin();
|
|
buf.remove(idx, 10+1+12);
|
|
p = buf.begin() + idx;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
static inline bool ident_char(char c)
|
|
{
|
|
return c == '_' || qisalnum(c);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool til_builder_t::get_symbol_name(pdb_sym_t &sym, qstring &buf)
|
|
{
|
|
bool is_unnamed = false;
|
|
sym.get_name(&buf);
|
|
if ( buf.empty() )
|
|
{
|
|
is_unnamed = true;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
remove_anonymous_namespaces(buf);
|
|
|
|
// <unnamed-tag> => <unnamed_tag>
|
|
// <unnamed-type-xxx> => <unnamed_type_xxx>
|
|
char *p = buf.begin();
|
|
while ( true )
|
|
{
|
|
// 012345678
|
|
p = strstr(p, "<unnamed");
|
|
if ( p == NULL )
|
|
break;
|
|
if ( p == buf.begin() )
|
|
is_unnamed = true;
|
|
p += 8;
|
|
while ( *p != '\0' )
|
|
{
|
|
if ( *p == '>' )
|
|
{
|
|
p++;
|
|
break;
|
|
}
|
|
else if ( *p == '-' )
|
|
{
|
|
*p = '_';
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
if ( !is_unnamed )
|
|
{
|
|
const char *marker = strstr(buf.begin(), "__unnamed");
|
|
if ( marker != NULL
|
|
// Is prev char not a valid identifier char?
|
|
&& (marker == buf.begin() || !ident_char(marker[-1]))
|
|
// Is next char not a valid identifier char?
|
|
&& !ident_char(marker[9]) )
|
|
{
|
|
is_unnamed = true;
|
|
}
|
|
}
|
|
}
|
|
return is_unnamed;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool til_builder_t::get_symbol_type(tpinfo_t *out, pdb_sym_t &sym, int *p_id)
|
|
{
|
|
#ifdef PDEBSYM
|
|
static int zz=0; ++zz;
|
|
int zzz = zz;
|
|
qstring sym_name;
|
|
sym.get_name(&sym_name);
|
|
DWORD sym_id = 0;
|
|
sym.get_symIndexId(&sym_id);
|
|
msg("PDEB: %d: get_symbol_type sym_id=%d '%s'\n", zzz, sym_id, sym_name.c_str());
|
|
#endif
|
|
pdb_sym_t *pType = pdb_access->create_sym();
|
|
pdb_sym_janitor_t janitor_pType(pType);
|
|
if ( p_id != NULL )
|
|
*p_id = -1;
|
|
if ( sym.get_type(pType) != S_OK )
|
|
return false;
|
|
bool ok = retrieve_type(out, *pType, NULL, p_id);
|
|
#ifdef PDEBSYM
|
|
DWORD typsym_id = 0;
|
|
pType->get_symIndexId(&typsym_id);
|
|
msg("PDEB: %d: get_symbol_type typsym_id=%d tif='%s' ok=%d\n", zzz, typsym_id, out->type.dstr(), ok);
|
|
#endif
|
|
return ok;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
size_t til_builder_t::get_symbol_type_length(pdb_sym_t &sym) const
|
|
{
|
|
DWORD64 size = 0;
|
|
DWORD tag = 0;
|
|
|
|
sym.get_symTag(&tag);
|
|
if ( tag == SymTagData )
|
|
{
|
|
pdb_sym_t *pType = pdb_access->create_sym();
|
|
pdb_sym_janitor_t janitor_pType(pType);
|
|
if ( sym.get_type(pType) == S_OK )
|
|
pType->get_length(&size);
|
|
}
|
|
else
|
|
{
|
|
sym.get_length(&size);
|
|
}
|
|
return size_t(size);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
cvt_code_t til_builder_t::convert_basetype(
|
|
tpinfo_t *out,
|
|
DWORD baseType,
|
|
int size) const
|
|
{
|
|
type_t bt = BTF_TYPEDEF;
|
|
const char *name = NULL;
|
|
switch ( baseType )
|
|
{
|
|
case btNoType:
|
|
out->is_notype = true;
|
|
// Fallthrough.
|
|
default:
|
|
case 0x12c304: // "impdir_entry" (guessed)
|
|
case btBCD:
|
|
case btBit:
|
|
return cvt_failed;
|
|
case btVoid:
|
|
bt = BTF_VOID;
|
|
break;
|
|
case btChar:
|
|
bt = BT_INT8|BTMT_CHAR;
|
|
break;
|
|
case btBool:
|
|
bt = BT_BOOL;
|
|
if ( size != inf_get_cc_size_b() )
|
|
{
|
|
switch ( size )
|
|
{
|
|
case 1:
|
|
bt |= BTMT_BOOL1;
|
|
break;
|
|
case 2:
|
|
if ( inf_is_64bit() )
|
|
goto MAKE_INT; // 64bit apps do not have BOOL2
|
|
bt |= BTMT_BOOL2;
|
|
break;
|
|
case 4:
|
|
bt |= BTMT_BOOL4;
|
|
break;
|
|
case 8:
|
|
if ( !inf_is_64bit() )
|
|
goto MAKE_INT; // 32bit apps do not have BOOL8
|
|
bt |= BTMT_BOOL8;
|
|
break;
|
|
default:
|
|
// can't make this bool size; make an int
|
|
goto MAKE_INT;
|
|
}
|
|
}
|
|
break;
|
|
MAKE_INT:
|
|
case btInt:
|
|
case btLong:
|
|
bt = get_scalar_bt(size);
|
|
if ( bt == BT_UNK )
|
|
return cvt_failed;
|
|
break;
|
|
case btUInt:
|
|
case btULong:
|
|
if ( size == 1 )
|
|
{
|
|
bt = BTF_UCHAR; // get_scalar_bt returns 'char', or'ing it with BTMT_USIGNED
|
|
// does not help
|
|
}
|
|
else
|
|
{
|
|
bt = get_scalar_bt(size);
|
|
if ( bt == BT_UNK )
|
|
return cvt_failed;
|
|
bt |= BTMT_USIGNED;
|
|
}
|
|
break;
|
|
case btFloat:
|
|
if ( size == pv.ph.sizeof_ldbl() )
|
|
{
|
|
bt = BTMT_LNGDBL;
|
|
}
|
|
else
|
|
{
|
|
switch ( size )
|
|
{
|
|
case 4: bt = BTMT_FLOAT; break;
|
|
default:
|
|
case 8: bt = BTMT_DOUBLE; break;
|
|
case 10: bt = BTMT_SPECFLT; break;
|
|
}
|
|
}
|
|
bt |= BT_FLOAT;
|
|
break;
|
|
case btWChar: name = "wchar_t"; break;
|
|
case btBSTR: name = "BSTR"; break;
|
|
case btHresult: name = "HRESULT"; break;
|
|
case btCurrency: name = "CURRENCY"; break;
|
|
case btVariant: name = "VARIANT"; break;
|
|
case btComplex: name = "complex"; break;
|
|
case btDate: name = "DATE"; break;
|
|
}
|
|
if ( name != NULL )
|
|
{
|
|
out->type.create_typedef(ti, name);
|
|
return cvt_typedef;
|
|
}
|
|
else
|
|
{
|
|
out->type = tinfo_t(bt);
|
|
return cvt_ok;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool til_builder_t::retrieve_arguments(
|
|
pdb_sym_t &_sym,
|
|
func_type_data_t &fi,
|
|
pdb_sym_t *funcSym)
|
|
{
|
|
struct type_name_collector_t : public pdb_access_t::children_visitor_t
|
|
{
|
|
func_type_data_t &fi;
|
|
til_builder_t *tb;
|
|
til_t *ti;
|
|
HRESULT visit_child(pdb_sym_t &sym) override
|
|
{
|
|
// check that it's a parameter
|
|
DWORD dwDataKind;
|
|
if ( sym.get_dataKind(&dwDataKind) == S_OK
|
|
&& dwDataKind != DataIsParam
|
|
&& dwDataKind != DataIsObjectPtr )
|
|
{
|
|
return S_OK;
|
|
}
|
|
tpinfo_t tpi;
|
|
bool cvt_succeeded = tb->retrieve_type(&tpi, sym, parent, NULL);
|
|
if ( cvt_succeeded || tpi.is_notype )
|
|
{
|
|
funcarg_t &arg = fi.push_back();
|
|
arg.type = tpi.type;
|
|
sym.get_name(&arg.name);
|
|
}
|
|
return S_OK;
|
|
}
|
|
type_name_collector_t(til_t *_ti, til_builder_t *_tb, func_type_data_t &_fi)
|
|
: fi(_fi), tb(_tb), ti(_ti) {}
|
|
};
|
|
fi.clear();
|
|
type_name_collector_t pp(ti, this, fi);
|
|
HRESULT hr = pdb_access->iterate_children(_sym, SymTagNull, pp);
|
|
if ( hr == S_OK && funcSym != NULL )
|
|
{
|
|
// get parameter names from the function symbol
|
|
func_type_data_t args;
|
|
args.flags = 0;
|
|
enum_function_args(*funcSym, args);
|
|
// QASSERT(497, args.empty() || args.size() == fi.size() );
|
|
bool custom_cc = false;
|
|
for ( int i = 0; i < fi.size(); i++ )
|
|
{
|
|
if ( i < args.size() )
|
|
{
|
|
if ( fi[i].name.empty() )
|
|
fi[i].name = args[i].name;
|
|
argloc_t &cur_argloc = args[i].argloc;
|
|
fi[i].argloc = cur_argloc;
|
|
if ( !custom_cc && cur_argloc.is_reg1() )
|
|
{
|
|
if ( is_intel386(pdb_access->get_machine_type()) )
|
|
{
|
|
if ( fi.cc == CM_CC_FASTCALL
|
|
&& cur_argloc.regoff() == 0
|
|
&& (cur_argloc.reg1() == R_cx && i == 0
|
|
|| cur_argloc.reg1() == R_dx && i == 1) )
|
|
{
|
|
// ignore ecx and edx for fastcall
|
|
}
|
|
else if ( fi.cc == CM_CC_THISCALL
|
|
&& cur_argloc.regoff() == 0
|
|
&& cur_argloc.reg1() == R_cx && i == 0 )
|
|
{
|
|
// ignore ecx for thiscall
|
|
}
|
|
else
|
|
{
|
|
custom_cc = true;
|
|
}
|
|
}
|
|
}
|
|
//ask_for_feedback("pdb: register arguments are not supported for machine type %d", machine_type);
|
|
}
|
|
}
|
|
if ( custom_cc )
|
|
{
|
|
// we have some register params; need to convert function to custom cc
|
|
fi.cc = (is_purging_cc(fi.cc) || fi.cc == CM_CC_THISCALL || fi.cc == CM_CC_FASTCALL)
|
|
? CM_CC_SPECIALP
|
|
: CM_CC_SPECIAL;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
cm_t til_builder_t::convert_cc(DWORD cc0) const
|
|
{
|
|
switch ( cc0 )
|
|
{
|
|
case CV_CALL_GENERIC :
|
|
case CV_CALL_NEAR_C :
|
|
case CV_CALL_FAR_C :
|
|
return inf_is_64bit() ? CM_CC_FASTCALL : CM_CC_CDECL;
|
|
case CV_CALL_NEAR_PASCAL:
|
|
case CV_CALL_FAR_PASCAL : return CM_CC_PASCAL;
|
|
case CV_CALL_NEAR_FAST :
|
|
case CV_CALL_FAR_FAST : return CM_CC_FASTCALL;
|
|
// case CV_CALL_SKIPPED :
|
|
case CV_CALL_NEAR_STD :
|
|
case CV_CALL_FAR_STD :
|
|
case CV_CALL_ARMCALL : return CM_CC_STDCALL;
|
|
case CV_CALL_THISCALL : return CM_CC_THISCALL;
|
|
// case CV_CALL_NEAR_SYS :
|
|
// case CV_CALL_FAR_SYS :
|
|
// case CV_CALL_MIPSCALL :
|
|
// case CV_CALL_ALPHACALL :
|
|
// case CV_CALL_PPCCALL :
|
|
// case CV_CALL_SHCALL :
|
|
// case CV_CALL_ARMCALL :
|
|
// case CV_CALL_AM33CALL :
|
|
// case CV_CALL_TRICALL :
|
|
// case CV_CALL_SH5CALL :
|
|
// case CV_CALL_M32RCALL :
|
|
}
|
|
return CM_CC_UNKNOWN;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool til_builder_t::get_variant_string_value(qstring *out, pdb_sym_t &sym) const
|
|
{
|
|
bool ok = false;
|
|
VARIANT value;
|
|
VariantInit(&value);
|
|
if ( sym.get_value(&value) == S_OK )
|
|
{
|
|
if ( value.vt == VT_BSTR )
|
|
{
|
|
utf16_utf8(out, (wchar16_t*) value.bstrVal);
|
|
ok = true;
|
|
}
|
|
else if ( value.vt == VT_LPSTR )
|
|
{
|
|
qstring str((const char *)value.byref);
|
|
out->swap(str);
|
|
ok = true;
|
|
}
|
|
}
|
|
VariantClear(&value);
|
|
return ok;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
uint32 til_builder_t::get_variant_long_value(pdb_sym_t &sym) const
|
|
{
|
|
uint32 v = 0;
|
|
VARIANT value;
|
|
VariantInit(&value);
|
|
if ( sym.get_value(&value) == S_OK )
|
|
{
|
|
switch ( value.vt )
|
|
{
|
|
case VT_I1: v = value.cVal; break;
|
|
case VT_I2: v = value.iVal; break;
|
|
case VT_I4: v = value.lVal; break;
|
|
case VT_I8: v = value.llVal; break;
|
|
case VT_INT: v = value.intVal; break;
|
|
case VT_UI1: v = value.bVal; break;
|
|
case VT_UI2: v = value.uiVal; break;
|
|
case VT_UI4: v = value.ulVal; break;
|
|
case VT_UI8: v = value.ullVal; break;
|
|
case VT_UINT: v = value.uintVal; break;
|
|
default:
|
|
ask_for_feedback("pdb: unsupported VARIANT type %d", value.vt);
|
|
break;
|
|
}
|
|
}
|
|
VariantClear(&value);
|
|
return v;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// funcSym is Function, typeSym is FunctionType
|
|
bool til_builder_t::is_member_func(tinfo_t *class_type, pdb_sym_t &typeSym, pdb_sym_t *funcSym)
|
|
{
|
|
// make sure we retrieve class type first
|
|
pdb_sym_t *pParent = pdb_access->create_sym();
|
|
pdb_sym_janitor_t janitor_pParent(pParent);
|
|
if ( typeSym.get_classParent(pParent) != S_OK || pParent->empty() )
|
|
return false;
|
|
|
|
tpinfo_t tpi;
|
|
if ( !retrieve_type(&tpi, *pParent, NULL, NULL) )
|
|
return false; // failed to retrieve the parent's type
|
|
|
|
class_type->swap(tpi.type);
|
|
|
|
// then check if it's static
|
|
if ( funcSym != NULL
|
|
&& pdb_access->get_dia_version() >= 800 )
|
|
{
|
|
BOOL bIsStatic = false;
|
|
HRESULT hr = funcSym->get_isStatic(&bIsStatic);
|
|
if ( hr == S_OK )
|
|
return !bIsStatic;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool til_builder_t::is_intel386(DWORD machine_type) const
|
|
{
|
|
return machine_type == CV_CFL_80386
|
|
|| machine_type == CV_CFL_80486
|
|
|| machine_type == CV_CFL_PENTIUM
|
|
|| machine_type == CV_CFL_PENTIUMII
|
|
|| machine_type == CV_CFL_PENTIUMIII;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool til_builder_t::is_arm(DWORD machine_type) const
|
|
{
|
|
return machine_type == CV_CFL_ARM3
|
|
|| machine_type == CV_CFL_ARM4
|
|
|| machine_type == CV_CFL_ARM4T
|
|
|| machine_type == CV_CFL_ARM5
|
|
|| machine_type == CV_CFL_ARM5T
|
|
|| machine_type == CV_CFL_ARM6
|
|
|| machine_type == CV_CFL_ARM7
|
|
|| machine_type == CV_CFL_ARMNT
|
|
|| machine_type == CV_CFL_ARM_XMAC
|
|
|| machine_type == CV_CFL_ARM_WMMX
|
|
|| machine_type == CV_CFL_THUMB;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool til_builder_t::is_frame_reg(int reg) const
|
|
{
|
|
if ( is_intel386(pdb_access->get_machine_type()) )
|
|
return reg == CV_REG_EBP;
|
|
else if ( is_arm(pdb_access->get_machine_type()) )
|
|
return reg == CV_ARM_R11 || reg == CV_ARM_SP;
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int til_builder_t::get_symbol_funcarg_info(
|
|
funcarg_t *out,
|
|
pdb_sym_t &sym,
|
|
DWORD /*dwDataKind*/,
|
|
DWORD locType,
|
|
int stack_off)
|
|
{
|
|
sym.get_name(&out->name);
|
|
tpinfo_t tpi;
|
|
get_symbol_type(&tpi, sym, NULL);
|
|
out->type = tpi.type;
|
|
if ( locType == LocIsEnregistered )
|
|
{
|
|
DWORD dwReg;
|
|
if ( sym.get_registerId(&dwReg) == S_OK )
|
|
{
|
|
if ( enregistered_bug && dwReg > 0 )
|
|
dwReg--;
|
|
qstring regname;
|
|
print_pdb_register(®name, pdb_access->get_machine_type(), dwReg);
|
|
out->argloc._set_reg1(str2reg(regname.c_str()));
|
|
}
|
|
}
|
|
else if ( locType == LocIsRegRel )
|
|
{
|
|
DWORD dwReg;
|
|
LONG lOffset;
|
|
if ( sym.get_registerId(&dwReg) == S_OK
|
|
&& sym.get_offset(&lOffset) == S_OK
|
|
&& is_frame_reg(dwReg) )
|
|
{
|
|
uint32 align;
|
|
out->argloc._set_stkoff(stack_off);
|
|
size_t argsz = out->type.get_size(&align);
|
|
if ( align > argsz )
|
|
argsz = align;
|
|
stack_off += argsz;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ask_for_feedback("pdb: unsupported location type %d", locType);
|
|
}
|
|
return stack_off;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void til_builder_t::enum_function_args(pdb_sym_t &_sym, func_type_data_t &args)
|
|
{
|
|
// enumerate all function parameters and gather their names
|
|
struct param_enumerator_t : public pdb_access_t::children_visitor_t
|
|
{
|
|
func_type_data_t &args;
|
|
til_builder_t *tb;
|
|
int stack_off;
|
|
virtual HRESULT visit_child(pdb_sym_t &sym) override
|
|
{
|
|
DWORD tag = 0;
|
|
HRESULT hr = sym.get_symTag(&tag);
|
|
if ( FAILED(hr) )
|
|
return hr;
|
|
|
|
switch ( tag )
|
|
{
|
|
case SymTagBlock: // nested blocks
|
|
return tb->pdb_access->iterate_children(sym, SymTagNull, *this);
|
|
case SymTagFuncDebugStart:
|
|
case SymTagFuncDebugEnd:
|
|
return S_OK; // ignore these for the moment
|
|
}
|
|
|
|
DWORD dwDataKind, locType;
|
|
if ( sym.get_dataKind(&dwDataKind) == S_OK
|
|
&& dwDataKind == DataIsParam
|
|
&& sym.get_locationType(&locType) == S_OK )
|
|
{
|
|
funcarg_t &fa = args.push_back();
|
|
stack_off = tb->get_symbol_funcarg_info(&fa, sym, dwDataKind, locType, stack_off);
|
|
}
|
|
return S_OK; // continue enumeration
|
|
}
|
|
param_enumerator_t(func_type_data_t &_args, til_builder_t *_tb)
|
|
: args(_args), tb(_tb), stack_off(0) {}
|
|
};
|
|
param_enumerator_t pen(args, this);
|
|
pdb_access->iterate_children(_sym, SymTagData, pen);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// corrupted PDB may produce the strange results
|
|
cvt_code_t til_builder_t::verify_struct(pdb_udt_type_data_t &udt) const
|
|
{
|
|
for ( auto &udm : udt )
|
|
{
|
|
if ( !udm.is_bitfield() && (udm.offset % 8) != 0 )
|
|
return cvt_failed;
|
|
}
|
|
return cvt_ok;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// verify unions that would be created out of [p1, p2) members.
|
|
// The [p1, p2) members are spoiled by the function.
|
|
// Create substructures if necessary. Returns the result in out (can be the same
|
|
// vector as [p1, p2)
|
|
cvt_code_t til_builder_t::verify_union(
|
|
pdb_udt_type_data_t *out,
|
|
pdb_udt_type_data_t::iterator p1,
|
|
pdb_udt_type_data_t::const_iterator p2) const
|
|
{
|
|
if ( p1 == p2 )
|
|
return cvt_ok;
|
|
|
|
QASSERT(498, p2 > p1);
|
|
uint64 off = p1->offset;
|
|
typedef qvector<pdb_udt_type_data_t> stems_t;
|
|
stems_t stems; // each stem is a member of the future union
|
|
for ( pdb_udt_type_data_t::iterator q=p1; q != p2; ++q )
|
|
{
|
|
pdb_udt_type_data_t *best = NULL;
|
|
q->offset -= off;
|
|
if ( q->offset != 0 )
|
|
{ // find best suited stem: the one with end() closest to our offset
|
|
uint64 bestend = 0;
|
|
for ( stems_t::iterator s=stems.begin(); s != stems.end(); ++s )
|
|
{
|
|
pdb_udt_type_data_t &sm = *s;
|
|
pdb_udt_member_t &lastmem = sm.back();
|
|
uint64 smend = lastmem.end();
|
|
if ( (lastmem.is_bitfield() == q->is_bitfield() || q->bit_offset == 0)
|
|
&& smend <= q->begin()
|
|
&& (best == NULL || bestend < smend) )
|
|
{
|
|
best = &sm;
|
|
bestend = smend;
|
|
}
|
|
}
|
|
}
|
|
if ( best == NULL )
|
|
best = &stems.push_back();
|
|
uint64 qend;
|
|
if ( q->is_bitfield() )
|
|
{
|
|
bitfield_type_data_t bi;
|
|
q->type.get_bitfield_details(&bi);
|
|
size_t size = bi.nbytes * 8;
|
|
QASSERT(30385, size == 8 || size == 16 || size == 32 || size == 64);
|
|
qend = q->offset - q->bit_offset + size;
|
|
}
|
|
else
|
|
{
|
|
qend = q->offset + q->size + 7;
|
|
}
|
|
qend /= 8;
|
|
if ( best->total_size < qend )
|
|
best->total_size = qend;
|
|
qswap(best->push_back(), *q);
|
|
}
|
|
|
|
// all non-trivial stems must be converted to structures
|
|
for ( stems_t::iterator s=stems.begin(); s != stems.end(); ++s )
|
|
{
|
|
if ( s->size() == 1 && s->begin()->offset == 0 && !s->begin()->is_bitfield() )
|
|
continue;
|
|
#ifdef PDEB
|
|
msg("CREATE STEM\n");
|
|
for ( pdb_udt_type_data_t::iterator p=s->begin(); p != s->end(); ++p )
|
|
msg(" %" FMT_64 "x %s %s\n", p->offset, p->type.dstr(), p->name.c_str());
|
|
#endif
|
|
if ( verify_struct(*s) != cvt_ok )
|
|
return cvt_failed;
|
|
tinfo_t tif;
|
|
int total_size = s->total_size;
|
|
cvt_code_t code = create_udt_ref(&tif, s, UdtStruct);
|
|
if ( code != cvt_ok )
|
|
return code;
|
|
s->resize(1);
|
|
pdb_udt_member_t &sm = s->front();
|
|
sm.offset = 0;
|
|
sm.size = uint64(total_size) * 8;
|
|
sm.name.sprnt("__s%u", uint(s-stems.begin()));
|
|
sm.type = tif;
|
|
}
|
|
|
|
// collect the results
|
|
out->resize(stems.size());
|
|
for ( int i=0; i < stems.size(); i++ )
|
|
{
|
|
QASSERT(499, stems[i].size() == 1);
|
|
qswap(out->at(i), *stems[i].begin());
|
|
}
|
|
return cvt_ok;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// create a union out of [p1, p2) members. they are spoiled by the function.
|
|
// returns type of the new union and its fields
|
|
// this function also creates substructures if necessary
|
|
cvt_code_t til_builder_t::create_union(
|
|
tinfo_t *out,
|
|
size_t *p_total_size,
|
|
pdb_udt_type_data_t::iterator p1,
|
|
pdb_udt_type_data_t::const_iterator p2) const
|
|
{
|
|
#ifdef PDEB
|
|
msg("CREATE UNION\n");
|
|
for ( pdb_udt_type_data_t::iterator p=p1; p != p2; ++p )
|
|
msg(" %" FMT_64 "x %s %s\n", p->offset, p->type.dstr(), p->name.c_str());
|
|
#endif
|
|
pdb_udt_type_data_t unimems;
|
|
cvt_code_t code = verify_union(&unimems, p1, p2);
|
|
if ( code != cvt_ok )
|
|
return code;
|
|
// calculate the total size
|
|
for ( int i=0; i < unimems.size(); i++ )
|
|
{
|
|
pdb_udt_member_t &udm = unimems[i];
|
|
size_t nbytes = (udm.end() + 7) / 8;
|
|
if ( nbytes > unimems.total_size )
|
|
unimems.total_size = nbytes;
|
|
}
|
|
if ( p_total_size != NULL )
|
|
*p_total_size = unimems.total_size;
|
|
return create_udt_ref(out, &unimems, UdtUnion);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline void ida_vft_name(qstring *vftn, const char *ns, uint32_t offset=0)
|
|
{
|
|
qstring new_vft_name(ns);
|
|
if ( offset != 0 )
|
|
new_vft_name.cat_sprnt("_%04X", offset);
|
|
new_vft_name.append(VTBL_SUFFIX);
|
|
vftn->swap(new_vft_name);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
#define MS_VTBL_SUFFIX "Vtbl"
|
|
inline void ms_vft_name(qstring *vftn, const char *ns)
|
|
{
|
|
qstring new_vft_name(ns);
|
|
new_vft_name.append(MS_VTBL_SUFFIX);
|
|
vftn->swap(new_vft_name);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline bool is_ms_vft_name(const qstring &udt_name)
|
|
{
|
|
size_t len = udt_name.length();
|
|
return len > sizeof(MS_VTBL_SUFFIX)
|
|
&& streq(udt_name.begin() + (len - sizeof(MS_VTBL_SUFFIX) + 1), MS_VTBL_SUFFIX);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline void ida_vft_name_from_ms(qstring *ivftnm, const qstring &msvftnm)
|
|
{
|
|
qstring tmp(msvftnm);
|
|
if ( is_ms_vft_name(msvftnm) )
|
|
tmp.remove(tmp.length()-4, 4);
|
|
ida_vft_name(ivftnm, tmp.c_str());
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool til_builder_t::get_vft_name(qstring *vftn, uint32 *ord, const char *ns, uint32_t offset)
|
|
{
|
|
bool vft_creating = false;
|
|
qstring new_vft_name;
|
|
// check for MS vftable
|
|
ms_vft_name(&new_vft_name, ns);
|
|
uint32 id = get_type_ordinal(ti, new_vft_name.c_str());
|
|
if ( id == 0 )
|
|
{
|
|
// maybe creating?
|
|
vft_creating = creating.find(new_vft_name.c_str()) != creating.end();
|
|
if ( !vft_creating )
|
|
{
|
|
ida_vft_name(&new_vft_name, ns, offset);
|
|
if ( ord != nullptr )
|
|
id = get_type_ordinal(ti, new_vft_name.c_str());
|
|
}
|
|
}
|
|
vftn->swap(new_vft_name);
|
|
if ( ord != nullptr )
|
|
*ord = id;
|
|
return vft_creating;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void pdb_udt_type_data_t::convert_to_tinfo_udt(udt_type_data_t *out)
|
|
{
|
|
out->total_size = total_size;
|
|
out->taudt_bits = taudt_bits;
|
|
out->is_union = is_union;
|
|
out->reserve(size());
|
|
for ( size_t i = 0; i < size(); i++ )
|
|
{
|
|
udt_member_t &udm = at(i);
|
|
out->push_back().swap(udm);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// insert si into the destination type
|
|
inline void merge_vft_udm(int *j, udt_type_data_t *dst_udt, const udt_member_t &si, bool replace)
|
|
{
|
|
bool insert_src = true;
|
|
for ( ; *j < dst_udt->size(); (*j)++ )
|
|
{
|
|
udt_member_t &dj = (*dst_udt)[*j];
|
|
if ( dj.offset + dj.size <= si.offset )
|
|
continue;
|
|
if ( dj.offset >= si.offset + si.size )
|
|
break; // should insert before dj
|
|
// Looks like an overlap,
|
|
// fields may differ in type and name only.
|
|
// Ignore "__vecDelDtor",
|
|
// this often happens when virtual class::~class
|
|
// is later redefined as __vecDelDtor()
|
|
if ( replace && si.name != "__vecDelDtor"
|
|
|| dj.name == "__vecDelDtor" )
|
|
{
|
|
dj.type = si.type;
|
|
dj.name = si.name;
|
|
}
|
|
insert_src = false;
|
|
break;
|
|
}
|
|
if ( insert_src )
|
|
dst_udt->insert(dst_udt->begin()+*j, si);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// merge two vftables into one
|
|
// dst_udt gets all fields of srctype in addition to its own fields.
|
|
// dst_udt preserves or overrides the coinciding field.
|
|
static void merge_vftables(udt_type_data_t *dst_udt, const tinfo_t &srcvft, bool replace)
|
|
{
|
|
udt_type_data_t src_udt;
|
|
if ( !srcvft.get_udt_details(&src_udt) )
|
|
{
|
|
deb(IDA_DEBUG_DBGINFO, "PDB: failed to merge type '%s' to vftable\n", srcvft.dstr());
|
|
#if defined(TESTABLE_BUILD) && !defined(__FUZZER__)
|
|
INTERR(30585);
|
|
#else
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
int j(0);
|
|
for ( const auto &si : src_udt )
|
|
merge_vft_udm(&j, dst_udt, si, replace);
|
|
dst_udt->total_size = (dst_udt->back().end() + 7) / 8;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static void add_vftable_member(
|
|
udt_type_data_t *dst_udt,
|
|
const tinfo_t &member,
|
|
const char *name,
|
|
DWORD vfptr_offset)
|
|
{
|
|
tinfo_t ptr_member;
|
|
ptr_member.create_ptr(member); // the field is a pointer to function
|
|
asize_t size = ptr_member.get_size();
|
|
|
|
udt_member_t udm;
|
|
udm.offset = uint64(vfptr_offset) * 8;
|
|
udm.size = uint64(size) * 8;
|
|
udm.type = ptr_member;
|
|
udm.effalign = size;
|
|
udm.name = name;
|
|
|
|
int j(0);
|
|
merge_vft_udm(&j, dst_udt, udm, true);
|
|
dst_udt->total_size = (dst_udt->back().end() + 7) / 8;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline bool get_vfptr_offset(DWORD *vfptr_offset, pdb_sym_t &sym)
|
|
{
|
|
BOOL is_virtual;
|
|
return sym.get_virtual(&is_virtual) == S_OK
|
|
&& is_virtual
|
|
&& sym.get_virtualBaseOffset(vfptr_offset) == S_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// enumerate virtual functions of class sym and create a vtable structure
|
|
// with function pointers
|
|
cvt_code_t til_builder_t::make_vtable_struct(tinfo_t *out, pdb_sym_t &_sym)
|
|
{
|
|
struct virtual_func_visitor_t : public pdb_access_t::children_visitor_t
|
|
{
|
|
til_builder_t *tb;
|
|
vft_info_t *vftinfo; // vftable info
|
|
virtual HRESULT visit_child(pdb_sym_t &sym) override
|
|
{
|
|
qstring name;
|
|
sym.get_name(&name);
|
|
|
|
// is introducing virtual?
|
|
DWORD vfptr_offset = -1;
|
|
bool is_intro_virtual = get_vfptr_offset(&vfptr_offset, sym);
|
|
|
|
tpinfo_t tpi;
|
|
if ( is_intro_virtual && tb->retrieve_type(&tpi, sym, parent, NULL) )
|
|
{
|
|
ddeb(("PDEB: make_vtable_struct add '%s' vptr offset %u\n", tpi.type.dstr(), vfptr_offset));
|
|
add_vftable_member(&vftinfo->udt, tpi.type, name.c_str(), vfptr_offset);
|
|
}
|
|
return S_OK;
|
|
}
|
|
virtual_func_visitor_t(til_builder_t *_tb, vft_info_t *_vftinfo)
|
|
: tb(_tb),
|
|
vftinfo(_vftinfo)
|
|
{}
|
|
};
|
|
|
|
qstring udt_name;
|
|
_sym.get_name(&udt_name);
|
|
// FIXME: should we remove classprefix (name + "::") ?
|
|
#ifdef PDEB
|
|
static int zzlevel = 0;
|
|
msg("PDEB: %d{ make_vtable_struct '%s'\n", ++zzlevel, udt_name.c_str());
|
|
#endif
|
|
vft_info_t vftinfo;
|
|
virtual_func_visitor_t pp(this, &vftinfo);
|
|
pdb_access->iterate_children(_sym, SymTagFunction, pp);
|
|
|
|
bool ok = false;
|
|
if ( !vftinfo.udt.empty() )
|
|
{
|
|
out->create_udt(vftinfo.udt, BTF_STRUCT);
|
|
ddeb(("PDEB: %d make_vtable_struct collected vftable '%s'\n", zzlevel, out->dstr()));
|
|
ok = out->calc_udt_aligns();
|
|
}
|
|
#ifdef PDEB
|
|
if ( !ok )
|
|
msg("PDEB: make_vtable_struct failed to create vftable\n");
|
|
msg("PDEB: %d} make_vtable_struct '%s'\n", zzlevel--, udt_name.c_str());
|
|
#endif
|
|
return ok ? cvt_ok : cvt_failed;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline bool is_fake_vftable(tinfo_t vft_tif)
|
|
{
|
|
qstring tname;
|
|
return vft_tif.get_final_type_name(&tname) && tname == fake_vtable_type;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
cvt_code_t til_builder_t::convert_udt(
|
|
tinfo_t *out,
|
|
pdb_sym_t &_sym,
|
|
DWORD64 size)
|
|
{
|
|
DWORD udtKind;
|
|
if ( _sym.get_udtKind(&udtKind) != S_OK )
|
|
return cvt_failed;
|
|
|
|
// retrieve member names, types, offsets
|
|
struct type_name_collector_t : public pdb_access_t::children_visitor_t
|
|
{
|
|
til_builder_t *tb;
|
|
pdb_udt_type_data_t &udt;
|
|
const char *vftname; // vftable name
|
|
vft_info_t *vftinfo; // vftable info, maybe nullptr if we don't want
|
|
// to collect vftable
|
|
bool mark_lpVtbl; // mark "lpVtbl" as pointer to vftable
|
|
bool has_virtbases;
|
|
HRESULT visit_child(pdb_sym_t &sym) override
|
|
{
|
|
qstring name;
|
|
sym.get_name(&name);
|
|
|
|
// is introducing virtual?
|
|
DWORD vfptr_offset = -1;
|
|
bool is_intro_virtual = get_vfptr_offset(&vfptr_offset, sym);
|
|
|
|
LONG offset = 0;
|
|
if ( !is_intro_virtual && sym.get_offset(&offset) != S_OK )
|
|
return S_OK;
|
|
// assert: intro virtual or data member
|
|
|
|
tpinfo_t tpi;
|
|
if ( !tb->retrieve_type(&tpi, sym, parent, NULL) )
|
|
return S_OK;
|
|
|
|
if ( is_intro_virtual )
|
|
{
|
|
if ( vftinfo != nullptr )
|
|
{
|
|
ddeb(("PDEB: convert_udt vtable %s add '%s' of '%s' vptr offset %u\n", vftname, name.c_str(), tpi.type.dstr(), vfptr_offset));
|
|
add_vftable_member(&vftinfo->udt, tpi.type, name.c_str(), vfptr_offset);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
ddeb(("PDEB: convert_udt adding member '%s' of type '%s'\n", name.c_str(), tpi.type.dstr()));
|
|
asize_t memsize = tb->get_symbol_type_length(sym);
|
|
|
|
pdb_udt_member_t &udm = udt.push_back();
|
|
|
|
DWORD tag = SymTagNull;
|
|
sym.get_symTag(&tag);
|
|
if ( tag == SymTagBaseClass )
|
|
{
|
|
udm.set_baseclass();
|
|
// determine if the base is virtual
|
|
BOOL is_virtbase = false;
|
|
sym.get_isVirtualBaseClass(&is_virtbase);
|
|
if ( is_virtbase )
|
|
{
|
|
udm.set_virtbase();
|
|
has_virtbases = true;
|
|
}
|
|
|
|
// we are interested only in baseclass at offset 0
|
|
if ( offset == 0 && vftinfo != nullptr )
|
|
{
|
|
// get baseclass vftable
|
|
qstring bcvft_name;
|
|
uint32 bcvft_ord;
|
|
tb->get_vft_name(&bcvft_name, &bcvft_ord, name.c_str());
|
|
if ( bcvft_ord != 0 )
|
|
{
|
|
tinfo_t bcvft_tif;
|
|
bcvft_tif.get_numbered_type(tb->ti, bcvft_ord);
|
|
if ( !is_fake_vftable(bcvft_tif) )
|
|
{
|
|
ddeb(("PDEB: convert_udt vtable %s add baseclass %s vtable '%s'\n", vftname, bcvft_name.c_str(), bcvft_tif.dstr()));
|
|
merge_vftables(&vftinfo->udt, bcvft_tif, false);
|
|
}
|
|
else
|
|
{
|
|
ddeb(("PDEB: convert_udt vtable %s baseclass %s vtable is fake\n", vftname, bcvft_name.c_str()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ddeb(("PDEB: convert_udt vtable %s baseclass %s vtable not found\n", vftname, bcvft_name.c_str()));
|
|
vftinfo->base0.swap(bcvft_name);
|
|
}
|
|
}
|
|
name.clear(); // no name for baseclass member
|
|
}
|
|
else if ( tag == SymTagVTable )
|
|
{
|
|
ddeb(("PDEB: convert_udt our vtable '%s'\n", tpi.type.dstr()));
|
|
if ( is_fake_vftable(tpi.type) )
|
|
{
|
|
tpi.type = tinfo_t::get_stock(STI_PVOID);
|
|
}
|
|
else
|
|
{
|
|
if ( vftinfo != nullptr )
|
|
merge_vftables(&vftinfo->udt, tpi.type, true);
|
|
// type is a structure, while the field is a pointer to it
|
|
tpi.type.create_ptr(tpi.type);
|
|
}
|
|
name = VTBL_MEMNAME; // we need only this name
|
|
memsize = tpi.type.get_size();
|
|
udm.set_vftable();
|
|
}
|
|
// mark MS vftable pointer
|
|
if ( mark_lpVtbl && tpi.type.is_ptr() && name == "lpVtbl" )
|
|
{ // no need to rename it
|
|
udm.set_vftable();
|
|
}
|
|
mark_lpVtbl = false; // pointer to vftable maybe the first field only
|
|
|
|
DWORD64 ulLen = DWORD64(memsize) * 8;
|
|
DWORD dwBitPos = 0;
|
|
DWORD dwLocType = LocIsNull;
|
|
sym.get_locationType(&dwLocType); // may fail, just ignore
|
|
if ( dwLocType == LocIsBitField )
|
|
{
|
|
sym.get_bitPosition(&dwBitPos);
|
|
sym.get_length(&ulLen);
|
|
if ( dwBitPos + ulLen > DWORD64(memsize) * 8 )
|
|
return E_FAIL;
|
|
bool is_unsigned = tpi.type.is_unsigned();
|
|
udm.type.create_bitfield(memsize, ulLen, is_unsigned);
|
|
}
|
|
else
|
|
{
|
|
udm.type = tpi.type;
|
|
}
|
|
udm.size = ulLen;
|
|
udm.offset = uint64(offset) * 8 + dwBitPos;
|
|
udm.bit_offset = dwBitPos;
|
|
udm.name.swap(name);
|
|
ddeb(("PDEB: convert_udt adding member size %" FMT_64 "u offset %" FMT_64 "u bit_offset %u\n", udm.size, udm.offset, udm.bit_offset));
|
|
return S_OK;
|
|
}
|
|
type_name_collector_t(
|
|
til_builder_t *_tb,
|
|
pdb_udt_type_data_t &m,
|
|
const char *_vftname,
|
|
vft_info_t *_vftinfo)
|
|
: tb(_tb),
|
|
udt(m),
|
|
vftname(_vftname),
|
|
vftinfo(_vftinfo),
|
|
mark_lpVtbl(true),
|
|
has_virtbases(false)
|
|
{}
|
|
};
|
|
|
|
qstring udt_name;
|
|
_sym.get_name(&udt_name);
|
|
|
|
bool is_vtbl_udt = is_ms_vft_name(udt_name);
|
|
qstring udt_vft_name;
|
|
uint32 udt_vft_ord(0);
|
|
bool vft_creating = false;
|
|
if ( !is_vtbl_udt )
|
|
vft_creating = get_vft_name(&udt_vft_name, &udt_vft_ord, udt_name.c_str());
|
|
bool collect_vft = !vft_creating && !is_vtbl_udt && udt_vft_ord == 0;
|
|
|
|
#ifdef PDEB
|
|
static int zzlevel = 0;
|
|
msg("PDEB: %d{ convert_udt '%s' assuming vftable '%s'\n", ++zzlevel, udt_name.c_str(), udt_vft_name.c_str());
|
|
#endif
|
|
pdb_udt_type_data_t udt;
|
|
if ( is_vtbl_udt )
|
|
udt.taudt_bits |= TAUDT_VFTABLE;
|
|
vft_info_t vtinfo;
|
|
type_name_collector_t pp(
|
|
this,
|
|
udt,
|
|
udt_vft_name.c_str(),
|
|
collect_vft ? &vtinfo : nullptr);
|
|
pdb_access->iterate_children(_sym, SymTagNull, pp);
|
|
|
|
bool is_cppobj = false;
|
|
if ( collect_vft && !vtinfo.udt.empty() )
|
|
{
|
|
if ( vtinfo.base0.empty() )
|
|
{
|
|
tinfo_t vft_tif;
|
|
if ( vft_tif.create_udt(vtinfo.udt, BTF_STRUCT)
|
|
&& vft_tif.calc_udt_aligns() )
|
|
{
|
|
ddeb(("PDEB: convert_udt %d collected vftable '%s' '%s'\n", zzlevel, udt_vft_name.c_str(), vft_tif.dstr()));
|
|
uint32 id = get_type_ordinal(ti, udt_vft_name.c_str());
|
|
if ( id == 0 )
|
|
vft_tif.set_named_type(ti, udt_vft_name.c_str(), NTF_NOBASE);
|
|
else
|
|
ddeb(("PDEB: convert_udt '%s' exists\n", udt_vft_name.c_str()));
|
|
is_cppobj = true; // there is a vftable, so it is a C++ object
|
|
}
|
|
else
|
|
{ // ignore failure, continue w/o vftable
|
|
ddeb(("PDEB: convert_udt failed to create vftable\n"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vftmap.emplace(std::make_pair(udt_vft_name.c_str(), vtinfo));
|
|
}
|
|
}
|
|
#ifdef PDEB
|
|
msg("PDEB: %d} convert_udt '%s'\n", zzlevel--, udt_name.c_str());
|
|
#endif
|
|
|
|
// if we will use MS Vtbl then create IDA synonym
|
|
if ( is_vtbl_udt )
|
|
{
|
|
tinfo_t tif;
|
|
tif.create_typedef(ti, udt_name.c_str());
|
|
qstring ivftnm;
|
|
ida_vft_name_from_ms(&ivftnm, udt_name.c_str());
|
|
tif.set_named_type(ti, ivftnm.c_str(), NTF_NOBASE);
|
|
}
|
|
|
|
// if we inherit from c++ object, we are too a c++ object
|
|
if ( size > 0 )
|
|
{
|
|
if ( udt.empty() )
|
|
is_cppobj = true;
|
|
if ( udt.size() == 1
|
|
&& udt[0].is_baseclass()
|
|
&& udt[0].type.is_empty_udt() )
|
|
{
|
|
is_cppobj = true;
|
|
}
|
|
}
|
|
if ( is_cppobj )
|
|
{
|
|
udt.taudt_bits |= TAUDT_CPPOBJ;
|
|
}
|
|
else if ( udt.empty() )
|
|
{ // create forward ref
|
|
qstring name;
|
|
get_symbol_name(_sym, name);
|
|
type_t bt = udtKind == UdtUnion ? BTF_UNION : BTF_STRUCT;
|
|
out->create_typedef(ti, name.c_str(), bt);
|
|
return cvt_typedef;
|
|
}
|
|
udt.total_size = size;
|
|
std::stable_sort(udt.begin(), udt.end());
|
|
BOOL cppobj;
|
|
if ( _sym.get_constructor(&cppobj) == S_OK && cppobj > 0 )
|
|
udt.taudt_bits |= TAUDT_CPPOBJ;
|
|
return create_udt(out, &udt, udtKind, udt_name.c_str());
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline void get_empty_vft_tif(tinfo_t *vtif)
|
|
{
|
|
udt_type_data_t empty_udt;
|
|
empty_udt.taudt_bits |= TAUDT_VFTABLE;
|
|
vtif->create_udt(empty_udt, BTF_STRUCT);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// fill the empty start slots of vftable,
|
|
// the holes will be filled latter, see SUDT_GAPS
|
|
static void fill_vft_empty_splots(udt_type_data_t *udt)
|
|
{
|
|
if ( udt->empty() )
|
|
return;
|
|
|
|
uint64 offset = udt->begin()->offset;
|
|
if ( offset == 0 )
|
|
return;
|
|
uint32 nbytes = offset / 8;
|
|
|
|
udt_member_t gap;
|
|
gap.type.create_array(tinfo_t(BTF_BYTE), nbytes);
|
|
gap.offset = 0;
|
|
gap.size = offset;
|
|
gap.effalign = 1;
|
|
gap.name = "gap0";
|
|
udt->insert(udt->begin(), gap);
|
|
// assert: no need to fix udt->total_size
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void til_builder_t::create_vftables()
|
|
{
|
|
while ( !vftmap.empty() )
|
|
{
|
|
bool changed = false;
|
|
for ( auto p=vftmap.begin(); p != vftmap.end(); )
|
|
{
|
|
auto &name = p->first;
|
|
auto &info = p->second;
|
|
ddeb(("PDB: create_vftables checking %s base0 %s\n", name.c_str(), info.base0.c_str()));
|
|
const char *b0name = info.base0.c_str();
|
|
uint32 id = get_type_ordinal(ti, b0name);
|
|
if ( id != 0 )
|
|
{ // merge the known base class vftable
|
|
ddeb(("PDB: merge %s to %s\n", b0name, name.c_str()));
|
|
tinfo_t btif;
|
|
btif.get_numbered_type(ti, id);
|
|
merge_vftables(&info.udt, btif, false);
|
|
info.base0.clear();
|
|
changed = true;
|
|
}
|
|
else
|
|
{
|
|
auto r = vftmap.find(b0name);
|
|
if ( r == vftmap.end() || r->second.empty() )
|
|
{ // ordinary class
|
|
ddeb(("PDB: ignore %s for %s\n", b0name, name.c_str()));
|
|
info.base0.clear();
|
|
changed = true;
|
|
}
|
|
else
|
|
{
|
|
ddeb(("PDB: skip %s for %s\n", b0name, name.c_str()));
|
|
}
|
|
}
|
|
if ( info.base0.empty() )
|
|
{ // all base classes references are resolved, create vftable
|
|
ddeb(("PDB: create_vftables creating %s\n", name.c_str()));
|
|
tinfo_t vtif;
|
|
if ( !vtif.create_udt(info.udt, BTF_STRUCT)
|
|
|| !vtif.calc_udt_aligns() )
|
|
{ // something wrong with vftable udt,
|
|
// create an empty vftable
|
|
ddeb(("PDEB: create_vftables failed to create vftable %s\n", name.c_str()));
|
|
get_empty_vft_tif(&vtif);
|
|
}
|
|
ddeb(("PDB: create_vftables created %s '%s'\n", name.c_str(), vtif.dstr()));
|
|
vtif.set_named_type(ti, name.c_str(), NTF_NOBASE);
|
|
p = vftmap.erase(p);
|
|
changed = true;
|
|
}
|
|
else
|
|
{
|
|
++p;
|
|
}
|
|
}
|
|
if ( !changed )
|
|
break;
|
|
}
|
|
|
|
if ( !vftmap.empty() )
|
|
{ // Something wrong or not:
|
|
// base class w/o virtual functions
|
|
// cyclic references,
|
|
// missed types
|
|
// Create the vftables ASIS
|
|
for ( auto p : vftmap )
|
|
{
|
|
auto &name = p.first;
|
|
auto &info = p.second;
|
|
ddeb(("PDEB: create_vftables create vftable %s ASIS, base0 %s\n", name.c_str(), info.base0.c_str()));
|
|
fill_vft_empty_splots(&info.udt);
|
|
tinfo_t vtif;
|
|
vtif.create_udt(info.udt, BTF_STRUCT);
|
|
vtif.calc_udt_aligns();
|
|
ddeb(("PDB: create_vftables created %s '%s'\n", name.c_str(), vtif.dstr()));
|
|
vtif.set_named_type(ti, name.c_str(), NTF_NOBASE);
|
|
}
|
|
vftmap.clear();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static bool set_array_type(pdb_udt_member_t *udm, int nbytes)
|
|
{
|
|
bool ok = udm->type.create_array(tinfo_t(BT_UNK_BYTE), nbytes);
|
|
if ( ok )
|
|
udm->size = nbytes * 8;
|
|
return ok;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// the real UDT should have non-zero size,
|
|
// detect a forward reference to a UDT without a real definition
|
|
inline bool is_fwdref_baseclass(pdb_udt_member_t &udm)
|
|
{
|
|
return udm.is_baseclass() && udm.size == 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
cvt_code_t til_builder_t::create_udt(tinfo_t *out, pdb_udt_type_data_t *udt, int udtKind, const char *udt_name) const
|
|
{
|
|
cvt_code_t code;
|
|
if ( udtKind == UdtUnion )
|
|
{
|
|
udt->is_union = true;
|
|
code = verify_union(udt, udt->begin(), udt->end());
|
|
}
|
|
else
|
|
{
|
|
// find overlapping members and convert into subunions (anonymous union would be great)
|
|
udt->is_union = false;
|
|
code = handle_overlapping_members(udt);
|
|
}
|
|
if ( code != cvt_ok )
|
|
return code;
|
|
|
|
// validate the type sizes, for the following reasons:
|
|
// - pdb information may be misleading (see pc_pdb_redefined_type.pe)
|
|
// - the same type name can be used for different types
|
|
// - invalid arrays happen (pc_pdb_wow.pe)
|
|
for ( int i=0; i < udt->size(); i++ )
|
|
{
|
|
pdb_udt_member_t &udm = udt->at(i);
|
|
if ( udm.is_bitfield() )
|
|
continue;
|
|
int gts_code = GTS_NESTED | (udm.is_baseclass() ? GTS_BASECLASS : 0);
|
|
size_t nbytes = udm.type.get_size(NULL, gts_code);
|
|
if ( nbytes == BADSIZE && !is_fwdref_baseclass(udm) )
|
|
continue; // cannot verify, the type is not ready yet
|
|
if ( uint64(nbytes)*8 != udm.size )
|
|
{
|
|
if ( nbytes != 0 )
|
|
{
|
|
if ( !set_array_type(&udm, udm.size/8) )
|
|
return cvt_failed;
|
|
}
|
|
else if ( udm.is_baseclass() || udm.type.is_array() )
|
|
{ // nbytes==0
|
|
udm.size = 0; // correct the base class size
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( udt->total_size == 0 && !udt->empty() )
|
|
{ // msdia did not provide the udt size. use the end of the last element
|
|
pdb_udt_member_t &udm = udt->back();
|
|
udt->total_size = (udm.end() + 7) / 8;
|
|
}
|
|
|
|
// the kernel cannot handle virtual base classes yet, so we remove them
|
|
// also check for overlapping members and members that go past the udt end
|
|
uint64 last = 0;
|
|
uint64 total_bits = uint64(udt->total_size) * 8;
|
|
for ( int i=0; i < udt->size(); i++ )
|
|
{
|
|
pdb_udt_member_t &udm = udt->at(i);
|
|
if ( udm.offset < last || udm.end() > total_bits )
|
|
{
|
|
if ( udm.end() > total_bits )
|
|
udm.size = total_bits - udm.offset;
|
|
int nbytes = (udm.end() + 7 - last) / 8;
|
|
if ( nbytes > 0 )
|
|
{ // replace with byte array
|
|
if ( !set_array_type(&udm, nbytes) )
|
|
return cvt_failed;
|
|
if ( udm.name.empty() )
|
|
udm.name.sprnt("_bytes_%" FMT_64 "x", last/8);
|
|
udm.offset = last;
|
|
udm.clr_baseclass();
|
|
udm.clr_virtbase();
|
|
}
|
|
else
|
|
{ // we do not need this member
|
|
udt->erase(udt->begin()+i);
|
|
--i;
|
|
continue;
|
|
}
|
|
}
|
|
if ( udtKind != UdtUnion )
|
|
last = udm.end();
|
|
}
|
|
|
|
type_t bt = udt->is_union ? BTF_UNION : BTF_STRUCT;
|
|
udt_type_data_t tinfo_udt;
|
|
udt->convert_to_tinfo_udt(&tinfo_udt);
|
|
out->create_udt(tinfo_udt, bt);
|
|
if ( !out->calc_udt_aligns(SUDT_GAPS|SUDT_UNEX) )
|
|
{
|
|
#if defined(TESTABLE_BUILD) && !defined(__FUZZER__)
|
|
QASSERT(30380, !inf_test_mode() && out->get_size() == BADSIZE);
|
|
#endif
|
|
deb(IDA_DEBUG_DBGINFO, "PDB: Failed to calculate struct '%s' member alignments\n", udt_name != nullptr ? udt_name : "");
|
|
ask_for_feedback("Failed to calculate struct member alignments");
|
|
}
|
|
return cvt_ok;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// is the return type complex?
|
|
// if so, a pointer to return value will be passed as a hidden parameter
|
|
bool til_builder_t::is_complex_return(pdb_sym_t &sym) const
|
|
{
|
|
pdb_sym_t *pType = pdb_access->create_sym();
|
|
pdb_sym_janitor_t janitor_pType(pType);
|
|
bool complex = false;
|
|
if ( sym.get_type(pType) == S_OK )
|
|
{
|
|
DWORD tag = 0;
|
|
complex = pType->get_symTag(&tag) == S_OK && tag == SymTagUDT;
|
|
if ( complex )
|
|
{
|
|
ULONGLONG size;
|
|
complex = pType->get_length(&size) == S_OK && size > 8;
|
|
}
|
|
if ( !complex && tag == SymTagUDT )
|
|
{
|
|
// we've got a small UDT which possibly fits into a register (or two)
|
|
// but it has to be a POD for that, i.e. should have no constructor or assignment operators
|
|
BOOL b;
|
|
if ( (pType->get_constructor (&b) == S_OK) && b
|
|
|| (pType->get_hasAssignmentOperator(&b) == S_OK) && b
|
|
|| (pType->get_hasCastOperator (&b) == S_OK) && b )
|
|
complex = true;
|
|
}
|
|
}
|
|
return complex;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool til_builder_t::is_unnamed_tag_typedef(const tinfo_t &tif) const
|
|
{
|
|
uint32 id = tif.get_ordinal();
|
|
if ( id == 0 )
|
|
return false;
|
|
|
|
return unnamed_types.find(id) != unnamed_types.end();
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
// borland does not like this structure to be defined inside a function.
|
|
// this is the only reason why it is in the file scope.
|
|
struct this_seeker_t : public pdb_access_t::children_visitor_t
|
|
{
|
|
funcarg_t thisarg;
|
|
til_builder_t *tb;
|
|
bool found;
|
|
virtual HRESULT visit_child(pdb_sym_t &sym) override
|
|
{
|
|
DWORD dwDataKind, locType;
|
|
if ( sym.get_dataKind(&dwDataKind) == S_OK
|
|
&& dwDataKind == DataIsObjectPtr
|
|
&& sym.get_locationType(&locType) == S_OK )
|
|
{
|
|
tb->get_symbol_funcarg_info(&thisarg, sym, dwDataKind, locType, 0);
|
|
found = true;
|
|
return S_FALSE; // Stop enum.
|
|
}
|
|
return S_OK;
|
|
}
|
|
this_seeker_t(til_builder_t *_tb) : thisarg(), tb(_tb), found(false) {}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
inline type_t get_sym_modifiers(pdb_sym_t &sym)
|
|
{
|
|
type_t type_mod = 0;
|
|
BOOL sym_mod;
|
|
if ( sym.get_constType(&sym_mod) == S_OK && sym_mod )
|
|
type_mod |= BTM_CONST;
|
|
if ( sym.get_volatileType(&sym_mod) == S_OK && sym_mod )
|
|
type_mod |= BTM_VOLATILE;
|
|
return type_mod;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
cvt_code_t til_builder_t::really_convert_type(
|
|
tpinfo_t *out,
|
|
pdb_sym_t &sym,
|
|
pdb_sym_t *parent,
|
|
DWORD tag)
|
|
{
|
|
// retrieve type modifiers
|
|
type_t mods = get_sym_modifiers(sym);
|
|
|
|
DWORD64 size = 0;
|
|
sym.get_length(&size);
|
|
DWORD bt, count;
|
|
cvt_code_t code = cvt_ok;
|
|
switch ( tag )
|
|
{
|
|
default:
|
|
case SymTagNull:
|
|
deb(IDA_DEBUG_DBGINFO, "PDB: unsupported tag %s\n", symtag_to_string(tag));
|
|
code = cvt_failed;
|
|
break;
|
|
|
|
case SymTagBaseType:
|
|
if ( sym.get_baseType(&bt) != S_OK )
|
|
code = cvt_failed;
|
|
else
|
|
code = convert_basetype(out, bt, int(size));
|
|
break;
|
|
|
|
case SymTagPointerType:
|
|
{
|
|
tpinfo_t obj;
|
|
if ( !get_symbol_type(&obj, sym, NULL) )
|
|
{
|
|
code = cvt_failed;
|
|
break;
|
|
}
|
|
tinfo_t tif;
|
|
tif.create_ptr(obj.type);
|
|
int s2 = tif.get_size();
|
|
if ( size != s2 )
|
|
{
|
|
if ( size == 4 || size == 8 )
|
|
{ // use __ptr32 or __ptr64
|
|
ptr_type_data_t pi;
|
|
pi.obj_type = obj.type;
|
|
pi.taptr_bits = size == 4 ? TAPTR_PTR32 : TAPTR_PTR64;
|
|
tif.create_ptr(pi);
|
|
}
|
|
else
|
|
{ // revert to int
|
|
type_t inttype = get_scalar_bt(size);
|
|
if ( inttype == BT_UNK )
|
|
{
|
|
code = cvt_failed;
|
|
break;
|
|
}
|
|
tif = tinfo_t(inttype);
|
|
}
|
|
}
|
|
out->type.swap(tif);
|
|
}
|
|
break;
|
|
|
|
case SymTagArrayType:
|
|
{
|
|
tpinfo_t el;
|
|
if ( !get_symbol_type(&el, sym, NULL) )
|
|
{
|
|
FAILED_ARRAY:
|
|
code = cvt_failed;
|
|
break;
|
|
}
|
|
if ( sym.get_count(&count) != S_OK )
|
|
goto FAILED_ARRAY;
|
|
mods |= el.type.get_modifiers(); // propagate element type to array
|
|
if ( !out->type.create_array(el.type, count) )
|
|
goto FAILED_ARRAY;
|
|
}
|
|
break;
|
|
|
|
case SymTagFunctionType:
|
|
{
|
|
tpinfo_t itp2;
|
|
if ( !get_symbol_type(&itp2, sym, NULL) ) // return type
|
|
{
|
|
code = cvt_failed;
|
|
break;
|
|
}
|
|
func_type_data_t fi;
|
|
fi.rettype = itp2.type;
|
|
if ( fi.rettype.is_array() )
|
|
{
|
|
code = cvt_failed; // arrays cannot be returned
|
|
break;
|
|
}
|
|
DWORD cc0;
|
|
fi.cc = CM_CC_UNKNOWN;
|
|
if ( sym.get_callingConvention(&cc0) == S_OK )
|
|
fi.cc = convert_cc(cc0);
|
|
|
|
if ( get_cc(fi.cc) != CM_CC_VOIDARG )
|
|
{
|
|
retrieve_arguments(sym, fi, parent);
|
|
// if arg has unknown/invalid argument => convert to ellipsis
|
|
for ( func_type_data_t::iterator i = fi.begin(); i != fi.end(); i++ )
|
|
{
|
|
if ( i->type.empty() )
|
|
{
|
|
// If the CC is cdecl, empty arguments represent an ellipsis.
|
|
// Otherwise, it's likely to be a C-type function
|
|
// with unknown number of arguments, such as 'foo()'
|
|
// (as opposed to 'foo(void)'), and which might not have a cdecl
|
|
// calling convention. E.g., pc_win32_appcall.pe's 'FARPROC':
|
|
// "int (FAR WINAPI * FARPROC) ()", which is a stdcall.
|
|
cm_t cc = get_cc(fi.cc);
|
|
if ( cc == CM_CC_CDECL || inf_is_64bit() && cc == CM_CC_FASTCALL )
|
|
fi.cc = CM_CC_ELLIPSIS;
|
|
// remove the ellipsis and any trailing arguments
|
|
fi.erase(i, fi.end());
|
|
break;
|
|
}
|
|
}
|
|
// is there an implicit "result" pointer passed?
|
|
if ( is_complex_return(sym) )
|
|
{
|
|
// complex return type: what's returned is actually a pointer
|
|
fi.rettype.create_ptr(fi.rettype);
|
|
funcarg_t retarg;
|
|
retarg.type = fi.rettype;
|
|
retarg.name = "result";
|
|
fi.insert(fi.begin(), retarg);
|
|
}
|
|
// is there an implicit "this" passed?
|
|
// N.B.: 'this' is passed before the implicit result, if both are present
|
|
tinfo_t class_type;
|
|
if ( is_member_func(&class_type, sym, parent) )
|
|
{
|
|
class_type.create_ptr(class_type);
|
|
funcarg_t thisarg;
|
|
thisarg.type = class_type;
|
|
thisarg.name = "this";
|
|
// due to MSDIA error sometimes it is failed to answer correctly
|
|
// for the get_isStatic() request (S_FALSE).
|
|
// So we need to check does 'this' pointer present in the function parameters.
|
|
bool add_this = true;
|
|
if ( parent != NULL )
|
|
{
|
|
this_seeker_t ts(this);
|
|
pdb_access->iterate_children(*parent, SymTagData, ts);
|
|
thisarg.argloc = ts.thisarg.argloc;
|
|
if ( thisarg.argloc.is_stkoff() )
|
|
{ // shift the remaining stkargs
|
|
int delta = thisarg.type.get_size();
|
|
for ( int i=0; i < fi.size(); i++ )
|
|
{
|
|
funcarg_t &fa = fi[i];
|
|
if ( fa.argloc.is_stkoff() )
|
|
fa.argloc.set_stkoff(fa.argloc.stkoff()+delta);
|
|
}
|
|
}
|
|
add_this = ts.found;
|
|
}
|
|
if ( add_this )
|
|
fi.insert(fi.begin(), thisarg);
|
|
}
|
|
if ( is_user_cc(fi.cc) )
|
|
{
|
|
// specify argloc for the return value
|
|
size_t retsize = fi.rettype.get_size();
|
|
if ( retsize <= 1 )
|
|
fi.retloc._set_reg1(R_al);
|
|
else if ( retsize <= 4 )
|
|
fi.retloc._set_reg1(R_ax);
|
|
else
|
|
fi.retloc._set_reg2(R_ax, R_dx);
|
|
|
|
// __usercall must have all its arguments location
|
|
// specified.
|
|
// It happens that some PDB information,
|
|
// generated at compile-time, does _not_ hold info
|
|
// about all the parameters. For example,
|
|
// a function declared as:
|
|
// void BlockOpVPSDec(char *p, uint32 dwLength, char btXorKey, char /*foo*/)
|
|
// will end up having only its first three arguments
|
|
// properly defined in the PDB (because the fourth is
|
|
// not used, its location is not defined.)
|
|
// Still, in order for 'build_func_type2()' to work,
|
|
// it requires all valid argloc_t instances. Thus,
|
|
// we remove invalid ones completely.
|
|
for ( int i = fi.size() - 1; i >= 0; --i )
|
|
if ( fi[i].argloc.is_badloc() )
|
|
fi.erase(fi.begin() + i);
|
|
}
|
|
out->type.create_func(fi);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SymTagUDT:
|
|
case SymTagBaseClass:
|
|
code = convert_udt(&out->type, sym, size);
|
|
break;
|
|
case SymTagEnum:
|
|
{
|
|
struct name_value_collector_t : public pdb_access_t::children_visitor_t
|
|
{
|
|
const til_builder_t *tb;
|
|
enum_type_data_t ei;
|
|
const type_t *idatype;
|
|
HRESULT visit_child(pdb_sym_t &child) override
|
|
{
|
|
enum_member_t &em = ei.push_back();
|
|
child.get_name(&em.name);
|
|
em.value = tb->get_variant_long_value(child);
|
|
if ( em.name.empty()
|
|
|| get_named_type(tb->ti, em.name.c_str(), NTF_SYMM, &idatype) == 1 )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
name_value_collector_t(const til_builder_t *_tb)
|
|
: tb(_tb), idatype(NULL) {}
|
|
};
|
|
name_value_collector_t nvc(this);
|
|
if ( size != 0 && size <= 64 )
|
|
{
|
|
int bte_size = log2ceil(size) + 1;
|
|
nvc.ei.bte |= bte_size & BTE_SIZE_MASK;
|
|
}
|
|
HRESULT hr = pdb_access->iterate_children(sym, SymTagNull, nvc);
|
|
if ( FAILED(hr) )
|
|
{ // symbol already exists or
|
|
// corrupted name or
|
|
// iterate_children failed to read any child
|
|
if ( nvc.ei.empty() || nvc.ei.back().name.empty() )
|
|
{
|
|
code = cvt_failed;
|
|
break;
|
|
}
|
|
// just reuse the existing enum
|
|
if ( !out->type.deserialize(ti, &nvc.idatype) ) // this is not quite correct
|
|
INTERR(30407);
|
|
qstring n1;
|
|
if ( out->type.get_type_name(&n1) )
|
|
{
|
|
qstring nm;
|
|
get_symbol_name(sym, nm);
|
|
if ( nm == n1 )
|
|
code = cvt_typedef; // avoid circular dependencies
|
|
}
|
|
}
|
|
else
|
|
{
|
|
out->type.create_enum(nvc.ei);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SymTagTypedef:
|
|
case SymTagFunctionArgType:
|
|
case SymTagFunction:
|
|
case SymTagData:
|
|
if ( !get_symbol_type(out, sym, NULL) )
|
|
code = cvt_failed;
|
|
else if ( out->type.is_decl_typedef() )
|
|
code = cvt_typedef; // signal that this is a typedef
|
|
break;
|
|
|
|
case SymTagVTable:
|
|
if ( parent == NULL || make_vtable_struct(&out->type, *parent) != cvt_ok )
|
|
out->type.create_typedef(ti, fake_vtable_type);
|
|
break;
|
|
}
|
|
if ( code != cvt_failed && mods != 0 )
|
|
out->type.set_modifiers(mods);
|
|
// todo: check that the type has the expected size
|
|
return code;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
cvt_code_t til_builder_t::convert_type(
|
|
tpinfo_t *out,
|
|
pdb_sym_t &sym,
|
|
pdb_sym_t *parent,
|
|
DWORD type,
|
|
DWORD tag)
|
|
{
|
|
if ( level == 500 )
|
|
{
|
|
deb(IDA_DEBUG_DBGINFO, "PDB: the maximum recursion level was reached\n");
|
|
return cvt_failed;
|
|
}
|
|
ddeb(("PDEB: convert_type tag %d sym_id %d\n", tag, type));
|
|
level++;
|
|
typemap_t::iterator p = typemap.find(type);
|
|
if ( p == typemap.end() )
|
|
{
|
|
tpinfo_t tpi;
|
|
tpi.cvt_code = really_convert_type(&tpi, sym, parent, tag);
|
|
p = typemap.insert(std::make_pair(type, tpi)).first;
|
|
}
|
|
tpinfo_t &tpi = p->second;
|
|
*out = tpi;
|
|
level--;
|
|
return tpi.cvt_code;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool til_builder_t::begin_creation(DWORD tag, const qstring &name, uint32 *p_id)
|
|
{
|
|
if ( tag != SymTagFunction )
|
|
{
|
|
uint32 id = *p_id;
|
|
creating_t::iterator c = creating.find(name);
|
|
if ( c != creating.end() ) // recursive call
|
|
{
|
|
if ( !c->second ) // allocated?
|
|
{
|
|
if ( id == 0 )
|
|
id = alloc_type_ordinal(ti); // have to create the type id immediately
|
|
c->second = id;
|
|
QASSERT(490, id != 0);
|
|
// msg("%d %s: prematurely mapped to %d\n", type, name.c_str(), c->second);
|
|
}
|
|
*p_id = c->second;
|
|
return false;
|
|
}
|
|
creating.insert(std::make_pair(name, id)); // add to the 'creating' list
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
uint32 til_builder_t::end_creation(const qstring &name)
|
|
{
|
|
uint32 id = 0;
|
|
creating_t::iterator c = creating.find(name);
|
|
if ( c != creating.end() )
|
|
{
|
|
id = c->second;
|
|
creating.erase(c);
|
|
}
|
|
if ( id == 0 )
|
|
{
|
|
id = alloc_type_ordinal(ti); // have to create the type id immediately
|
|
QASSERT(491, id != 0);
|
|
// msg("%d %s: mapped to %d\n", type, name.c_str(), id);
|
|
}
|
|
return id;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
cvt_code_t til_builder_t::handle_overlapping_members(pdb_udt_type_data_t *udt) const
|
|
{
|
|
qstack<qstring> union_names;
|
|
pdb_udt_type_data_t::iterator end = udt->end();
|
|
pdb_udt_type_data_t::iterator first = end; // !=end => collecting union members
|
|
pdb_udt_type_data_t::iterator last = end; // member with highest ending offset so far
|
|
for ( pdb_udt_type_data_t::iterator p=udt->begin(); ; ++p )
|
|
{
|
|
if ( p != udt->end() )
|
|
{
|
|
if ( is_unnamed_tag_typedef(p->type) )
|
|
handle_unnamed_overlapping_member(udt, &union_names, &p->name);
|
|
if ( last == end )
|
|
{
|
|
last = p;
|
|
continue;
|
|
}
|
|
if ( last->end() > p->begin() )
|
|
{ // found an overlap. however, we ignore base classes, in order
|
|
// not to convert them into unions
|
|
if ( first == end && !last->is_baseclass() )
|
|
first = last;
|
|
goto NEXT;
|
|
}
|
|
}
|
|
if ( first != end )
|
|
{
|
|
int fidx = first - udt->begin();
|
|
uval_t off = first->offset;
|
|
// if we have a bitfield, include the adjacent bitfields in the new type
|
|
int bf_typesize = 0;
|
|
for ( pdb_udt_type_data_t::iterator q=first; q != p; ++q )
|
|
{
|
|
if ( q->is_bitfield() )
|
|
{
|
|
bf_typesize = q->type.get_size();
|
|
break;
|
|
}
|
|
}
|
|
if ( bf_typesize != 0 )
|
|
{
|
|
while ( fidx > 0
|
|
&& (first-1)->is_bitfield()
|
|
&& (first-1)->type.get_size() == bf_typesize )
|
|
{
|
|
--fidx;
|
|
--first;
|
|
off = first->offset;
|
|
}
|
|
while ( p != end
|
|
&& p->is_bitfield()
|
|
&& p->type.get_size() == bf_typesize )
|
|
{
|
|
++p;
|
|
}
|
|
}
|
|
// range [first, p) is overlapping, create a new type for it
|
|
tinfo_t unitif;
|
|
size_t union_size;
|
|
cvt_code_t code = create_union(&unitif, &union_size, first, p);
|
|
if ( code != cvt_ok )
|
|
return code;
|
|
udt->erase(first+1, p);
|
|
end = udt->end();
|
|
first = end;
|
|
last = end;
|
|
p = udt->begin() + fidx;
|
|
p->offset = off & ~7;
|
|
p->size = uint64(union_size) * 8;
|
|
if ( union_names.empty() )
|
|
p->name.sprnt("___u%d", fidx);
|
|
else
|
|
p->name = union_names.pop();
|
|
p->type = unitif;
|
|
}
|
|
if ( p == end )
|
|
break;
|
|
NEXT:
|
|
if ( last->end() < p->end() )
|
|
last = p;
|
|
}
|
|
return cvt_ok;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
void til_builder_t::handle_function_type(pdb_sym_t &fun_sym, ea_t ea)
|
|
{
|
|
struct local_data_creator_t : public pdb_access_t::children_visitor_t
|
|
{
|
|
virtual HRESULT visit_child(pdb_sym_t &sym) override
|
|
{
|
|
DWORD tag = 0;
|
|
HRESULT hr = sym.get_symTag(&tag);
|
|
if ( FAILED(hr) )
|
|
return hr;
|
|
|
|
switch ( tag )
|
|
{
|
|
case SymTagBlock: // nested blocks
|
|
return tb->pdb_access->iterate_children(sym, SymTagNull, *this);
|
|
case SymTagFuncDebugStart:
|
|
case SymTagFuncDebugEnd:
|
|
return S_OK; // ignore these for the moment
|
|
}
|
|
|
|
DWORD loc_type;
|
|
if ( sym.get_locationType(&loc_type) != S_OK )
|
|
return S_OK; // optimized away?
|
|
|
|
return tb->handle_function_child(fun_sym, ea, sym, tag, loc_type);
|
|
}
|
|
local_data_creator_t(til_builder_t *_tb, pdb_sym_t &_fun_sym, ea_t _ea) :
|
|
tb(_tb), fun_sym(_fun_sym), ea(_ea) {}
|
|
til_builder_t *tb;
|
|
pdb_sym_t &fun_sym;
|
|
ea_t ea;
|
|
};
|
|
local_data_creator_t ldc(this, fun_sym, ea);
|
|
pdb_access->iterate_children(fun_sym, SymTagNull, ldc);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
void til_builder_t::type_created(
|
|
ea_t /*ea*/,
|
|
int /*id*/,
|
|
const char * /*name*/,
|
|
const tinfo_t & /*ptr*/) const
|
|
{
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
HRESULT til_builder_t::handle_function_child(
|
|
pdb_sym_t & /*fun_sym*/,
|
|
ea_t ea,
|
|
pdb_sym_t &child_sym,
|
|
DWORD child_tag,
|
|
DWORD child_loc_type)
|
|
{
|
|
switch ( child_loc_type )
|
|
{
|
|
case LocIsConstant:
|
|
break; // we ignore function level constants
|
|
|
|
case LocIsStatic:
|
|
case LocIsTLS: // not tested
|
|
handle_symbol(child_sym);
|
|
break;
|
|
|
|
case LocIsEnregistered:
|
|
case LocIsRegRel:
|
|
break;
|
|
|
|
default:
|
|
ask_for_feedback("pdb: unsupported location type %d, tag %d at %a", child_loc_type, child_tag, ea);
|
|
break;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
cvt_code_t til_builder_t::create_udt_ref(tinfo_t *out, pdb_udt_type_data_t *udt, int udt_kind) const
|
|
{
|
|
tinfo_t tif;
|
|
cvt_code_t code = create_udt(&tif, udt, udt_kind, nullptr);
|
|
if ( code != cvt_ok )
|
|
return code;
|
|
|
|
qtype type, fields;
|
|
tif.serialize(&type, &fields);
|
|
|
|
qstring name;
|
|
build_anon_type_name(&name, type.begin(), fields.begin());
|
|
uint32 id = get_type_ordinal(ti, name.c_str());
|
|
if ( id == 0 )
|
|
{
|
|
id = alloc_type_ordinal(ti);
|
|
if ( set_numbered_type(ti, id, NTF_NOBASE|NTF_FIXNAME, name.c_str(), type.begin(), fields.begin()) != TERR_OK )
|
|
return cvt_failed;
|
|
type_created(BADADDR, id, NULL, tif);
|
|
}
|
|
|
|
out->create_typedef(ti, id);
|
|
return cvt_ok;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool til_builder_t::retrieve_type(
|
|
tpinfo_t *out,
|
|
pdb_sym_t &sym,
|
|
pdb_sym_t *parent,
|
|
int *p_id)
|
|
{
|
|
if ( p_id != NULL )
|
|
*p_id = -1;
|
|
|
|
// id -> unknown typedef?
|
|
DWORD sym_id = 0;
|
|
sym.get_symIndexId(&sym_id);
|
|
tpdefs_t::iterator q = tpdefs.find(sym_id);
|
|
if ( q != tpdefs.end() )
|
|
{
|
|
out->type = q->second;
|
|
return true;
|
|
}
|
|
|
|
DWORD tag = 0;
|
|
HRESULT hr = sym.get_symTag(&tag);
|
|
if ( FAILED(hr) )
|
|
return false;
|
|
|
|
qstring ns;
|
|
bool is_unnamed = get_symbol_name(sym, ns);
|
|
//msg("ID: %d -> %s\n", sym_id, ns.begin());
|
|
uint32 id(0);
|
|
bool id_set = false;
|
|
if ( tag == SymTagVTable && ns.empty() )
|
|
{
|
|
if ( parent != NULL )
|
|
get_symbol_name(*parent, ns);
|
|
LONG offset = 0;
|
|
sym.get_offset(&offset);
|
|
get_vft_name(&ns, &id, ns.c_str(), offset);
|
|
|
|
is_unnamed = false;
|
|
id_set = true;
|
|
}
|
|
|
|
// udt fields and simple types are converted without allocating
|
|
// an ordinal number
|
|
if ( tag == SymTagData || ns.empty() )
|
|
return convert_type(out, sym, parent, sym_id, tag) != cvt_failed;
|
|
|
|
// give a unique name to unnamed types so they can be told apart
|
|
// this is a temporary name, it will be replaced by $hex..
|
|
if ( is_unnamed )
|
|
ns.sprnt("unnamed-%d", unnamed_idx++);
|
|
else
|
|
validate_name(&ns, VNT_TYPE);
|
|
|
|
// some types can be defined multiple times. check if the name is already defined
|
|
bool defined_correctly = false;
|
|
bool defined_wrongly = false;
|
|
type_t tif_mod = 0;
|
|
if ( !id_set )
|
|
id = get_type_ordinal(ti, ns.c_str());
|
|
if ( id != 0 )
|
|
{
|
|
tinfo_t tif;
|
|
tif.create_typedef(ti, id);
|
|
tif_mod = get_sym_modifiers(sym);
|
|
if ( tif.get_realtype() == BT_UNK )
|
|
defined_wrongly = true;
|
|
else
|
|
defined_correctly = true;
|
|
}
|
|
if ( !defined_correctly )
|
|
{
|
|
if ( begin_creation(tag, ns, &id) )
|
|
{
|
|
// now convert the type information, recursive types won't bomb
|
|
tpinfo_t tpi2;
|
|
cvt_code_t cc = convert_type(&tpi2, sym, parent, sym_id, tag);
|
|
if ( cc != cvt_ok ) // failed or typedef
|
|
{
|
|
creating.erase(ns);
|
|
if ( cc == cvt_failed )
|
|
return false;
|
|
// cvt_typedef
|
|
{
|
|
tinfo_t tif;
|
|
tif.create_typedef(ti, ns.c_str());
|
|
tif.set_modifiers(tpi2.type.get_modifiers());
|
|
tpdefs[sym_id] = tif; // reference to unknown typedef
|
|
}
|
|
RETT2:
|
|
out->type = tpi2.type;
|
|
return true;
|
|
}
|
|
|
|
qtype type, fields;
|
|
if ( !tpi2.type.serialize(&type, &fields) )
|
|
INTERR(30408);
|
|
|
|
// Function types are saved as symbols
|
|
if ( tag == SymTagFunction )
|
|
{
|
|
// the following may fail because of c++ overloaded functions
|
|
// do not check the error code - we cannot help it
|
|
tpi2.type.set_symbol_type(ti, ns.c_str(), NTF_SYMM);
|
|
type_created(BADADDR, 0, ns.c_str(), tpi2.type);
|
|
goto RETT2;
|
|
}
|
|
|
|
bool reuse_anon_type = false;
|
|
if ( is_unnamed ) // this type will be referenced, so create a name for it
|
|
{
|
|
build_anon_type_name(&ns, type.begin(), fields.begin());
|
|
id = get_type_ordinal(ti, ns.c_str());
|
|
if ( id != 0 ) // this type already exists, just reuse it
|
|
{
|
|
creating.erase(ns);
|
|
reuse_anon_type = true;
|
|
}
|
|
tif_mod = get_sym_modifiers(sym);
|
|
}
|
|
if ( !reuse_anon_type )
|
|
{
|
|
id = end_creation(ns);
|
|
int ntf_flags = NTF_NOBASE|NTF_FIXNAME;
|
|
if ( defined_wrongly )
|
|
ntf_flags |= NTF_REPLACE;
|
|
if ( set_numbered_type(ti, id, ntf_flags,
|
|
ns.empty() ? NULL : ns.c_str(),
|
|
type.begin(),
|
|
fields.begin()) != TERR_OK )
|
|
{
|
|
return 0;
|
|
}
|
|
tif_mod = tpi2.type.get_modifiers();
|
|
}
|
|
if ( is_unnamed )
|
|
unnamed_types.insert(id);
|
|
// msg("%d: %s\n name: %s\n", id, tpi2.dstr(), ns.c_str());
|
|
type_created(BADADDR, id, NULL, tpi2.type);
|
|
}
|
|
else
|
|
{ // in case of recursive call we need to preserve modifiers
|
|
tif_mod = get_sym_modifiers(sym);
|
|
}
|
|
}
|
|
if ( p_id != NULL )
|
|
*p_id = id;
|
|
out->type.create_typedef(ti, id);
|
|
if ( tif_mod != 0 )
|
|
out->type.set_modifiers(tif_mod);
|
|
return true;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool til_builder_t::handle_symbol_at_ea(pdb_sym_t &/*sym*/, DWORD /*tag*/, ea_t /*ea*/, qstring & /*name*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
HRESULT til_builder_t::handle_symbol(pdb_sym_t &sym)
|
|
{
|
|
DWORD id;
|
|
HRESULT hr = sym.get_symIndexId(&id);
|
|
if ( FAILED(hr) )
|
|
return hr;
|
|
|
|
if ( handled.find(id) != handled.end() )
|
|
return S_OK;
|
|
handled.insert(id);
|
|
|
|
DWORD tag = 0;
|
|
hr = sym.get_symTag(&tag);
|
|
if ( FAILED(hr) )
|
|
return hr;
|
|
|
|
switch ( tag )
|
|
{
|
|
case SymTagNull:
|
|
case SymTagExe:
|
|
case SymTagCompiland:
|
|
case SymTagCompilandEnv:
|
|
case SymTagCustom:
|
|
case SymTagCustomType:
|
|
case SymTagManagedType:
|
|
case SymTagUDT:
|
|
case SymTagEnum:
|
|
case SymTagFunctionType:
|
|
case SymTagPointerType:
|
|
case SymTagArrayType:
|
|
case SymTagBaseType:
|
|
case SymTagTypedef:
|
|
case SymTagBaseClass:
|
|
case SymTagFunctionArgType:
|
|
case SymTagUsingNamespace:
|
|
case SymTagVTableShape:
|
|
case SymTagDimension:
|
|
return S_OK;
|
|
case SymTagCompilandDetails:
|
|
{
|
|
DWORD backEndVer;
|
|
if ( is_intel386(pdb_access->get_machine_type()) && sym.get_backEndMajor(&backEndVer) == S_OK )
|
|
enregistered_bug = backEndVer <= 13;
|
|
}
|
|
return S_OK;
|
|
// new tags for msdia140
|
|
case SymTagCoffGroup:
|
|
return S_OK;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
DWORD off = 0;
|
|
hr = sym.get_relativeVirtualAddress(&off);
|
|
if ( hr == S_OK )
|
|
{
|
|
ea_t ea = get_load_address() + off;
|
|
qstring name;
|
|
sym.get_name(&name);
|
|
handle_symbol_at_ea(sym, tag, ea, name);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
// Each time we encounter a toplevel type/func/whatever, we want to make
|
|
// sure the UI has had a chance to refresh itself.
|
|
struct toplevel_children_visitor_t : public pdb_access_t::children_visitor_t
|
|
{
|
|
virtual HRESULT visit_child(pdb_sym_t &sym) override
|
|
{
|
|
user_cancelled();
|
|
return do_visit_child(sym);
|
|
}
|
|
|
|
virtual HRESULT do_visit_child(pdb_sym_t &sym) = 0;
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
struct symbol_handler_t : public toplevel_children_visitor_t
|
|
{
|
|
virtual HRESULT do_visit_child(pdb_sym_t &sym) override
|
|
{
|
|
return tb->handle_symbol(sym);
|
|
}
|
|
symbol_handler_t(til_builder_t *_tb) : tb(_tb) {}
|
|
til_builder_t *tb;
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
HRESULT til_builder_t::handle_symbols(pdb_sym_t &global_sym)
|
|
{
|
|
symbol_handler_t cp(this);
|
|
HRESULT hr;
|
|
while ( true )
|
|
{
|
|
hr = pdb_access->iterate_subtags(global_sym, SymTagNull, cp);
|
|
if ( FAILED(hr) )
|
|
break;
|
|
if ( !iterate_symbols_once_more(global_sym) )
|
|
break;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
HRESULT til_builder_t::handle_publics(pdb_sym_t &global_sym)
|
|
{
|
|
symbol_handler_t cp(this);
|
|
return pdb_access->iterate_children(global_sym, SymTagPublicSymbol, cp);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
HRESULT til_builder_t::handle_globals(pdb_sym_t &global_sym)
|
|
{
|
|
symbol_handler_t cp(this);
|
|
return pdb_access->iterate_children(global_sym, SymTagData, cp);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
HRESULT til_builder_t::handle_types(pdb_sym_t &global_sym)
|
|
{
|
|
struct type_importer_t : public toplevel_children_visitor_t
|
|
{
|
|
til_builder_t *tb;
|
|
int counter;
|
|
virtual HRESULT do_visit_child(pdb_sym_t &sym) override
|
|
{
|
|
tpinfo_t tpi;
|
|
if ( tb->retrieve_type(&tpi, sym, parent, NULL) )
|
|
counter++;
|
|
return S_OK;
|
|
}
|
|
type_importer_t(til_builder_t *_tb) : tb(_tb), counter(0) {}
|
|
};
|
|
type_importer_t timp(this);
|
|
HRESULT hr = pdb_access->iterate_children(global_sym, SymTagEnum, timp);
|
|
if ( hr == S_OK )
|
|
hr = pdb_access->iterate_children(global_sym, SymTagUDT, timp);
|
|
if ( hr == S_OK )
|
|
hr = pdb_access->iterate_children(global_sym, SymTagTypedef, timp);
|
|
msg("PDB: loaded %d type%s\n", timp.counter, timp.counter != 1 ? "s" : "");
|
|
return hr;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
HRESULT til_builder_t::before_iterating(pdb_sym_t &)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
HRESULT til_builder_t::after_iterating(pdb_sym_t &)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
HRESULT til_builder_t::build(pdb_sym_t &global_sym)
|
|
{
|
|
HRESULT hr = before_iterating(global_sym);
|
|
if ( hr == S_OK )
|
|
hr = handle_types(global_sym);
|
|
if ( (pdb_access->pdbargs.flags & PDBFLG_ONLY_TYPES) == 0 )
|
|
{
|
|
if ( hr == S_OK )
|
|
hr = handle_symbols(global_sym);
|
|
if ( hr == S_OK )
|
|
hr = handle_globals(global_sym);
|
|
// handle_globals() will set the type and undecorated name for globals,
|
|
// and handle_publics() will set the decorated name for public symbols.
|
|
// We want both the type (from handle_globals()) and the decorated symbol
|
|
// name (from handle_publics()), since that gives the user more information
|
|
// about the variable and enables FLIRT to match rulefuncs based on the
|
|
// symbol name.
|
|
// For example, @__security_check_cookie@4 is used as a rulefunc by FLIRT,
|
|
// and that won't match with the undecorated name __security_check_cookie.
|
|
// Therefore, handle_publics() must be called *after* handle_globals().
|
|
if ( hr == S_OK )
|
|
hr = handle_publics(global_sym);
|
|
}
|
|
if ( hr == S_OK )
|
|
{
|
|
create_vftables();
|
|
hr = after_iterating(global_sym);
|
|
}
|
|
return hr;
|
|
}
|