wine/dlls/dbghelp/type.c
Eric Pouech c5e0a696d9 - fixed some bugs in StackWalk (claimed for but forgotten in last
patch)
- removed location field in symt_data, and reworked the actual
  location of information based on the 'kind' field
- shorten debug channel name
- added support for bitfield in struct:s
- cleaned up source line information support
- now storing constants values as VARIANT
2004-04-30 04:15:41 +00:00

749 lines
25 KiB
C

/*
* File types.c - management of types (hierarchical tree)
*
* Copyright (C) 1997, Eric Youngdale.
* 2004, Eric Pouech.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Note: This really doesn't do much at the moment, but it forms the framework
* upon which full support for datatype handling will eventually be built.
*/
#include "config.h"
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winnls.h"
#include "wine/debug.h"
#include "dbghelp_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
WINE_DECLARE_DEBUG_CHANNEL(dbghelp_symt);
static const char* symt_get_tag_str(DWORD tag)
{
switch (tag)
{
case SymTagNull: return "SymTagNull";
case SymTagExe: return "SymTagExe";
case SymTagCompiland: return "SymTagCompiland";
case SymTagCompilandDetails: return "SymTagCompilandDetails";
case SymTagCompilandEnv: return "SymTagCompilandEnv";
case SymTagFunction: return "SymTagFunction";
case SymTagBlock: return "SymTagBlock";
case SymTagData: return "SymTagData";
case SymTagAnnotation: return "SymTagAnnotation";
case SymTagLabel: return "SymTagLabel";
case SymTagPublicSymbol: return "SymTagPublicSymbol";
case SymTagUDT: return "SymTagUDT";
case SymTagEnum: return "SymTagEnum";
case SymTagFunctionType: return "SymTagFunctionType";
case SymTagPointerType: return "SymTagPointerType";
case SymTagArrayType: return "SymTagArrayType";
case SymTagBaseType: return "SymTagBaseType";
case SymTagTypedef: return "SymTagTypedef,";
case SymTagBaseClass: return "SymTagBaseClass";
case SymTagFriend: return "SymTagFriend";
case SymTagFunctionArgType: return "SymTagFunctionArgType,";
case SymTagFuncDebugStart: return "SymTagFuncDebugStart,";
case SymTagFuncDebugEnd: return "SymTagFuncDebugEnd";
case SymTagUsingNamespace: return "SymTagUsingNamespace,";
case SymTagVTableShape: return "SymTagVTableShape";
case SymTagVTable: return "SymTagVTable";
case SymTagCustom: return "SymTagCustom";
case SymTagThunk: return "SymTagThunk";
case SymTagCustomType: return "SymTagCustomType";
case SymTagManagedType: return "SymTagManagedType";
case SymTagDimension: return "SymTagDimension";
default: return "---";
}
}
const char* symt_get_name(const struct symt* sym)
{
switch (sym->tag)
{
/* lexical tree */
case SymTagData: return ((struct symt_data*)sym)->hash_elt.name;
case SymTagFunction: return ((struct symt_function*)sym)->hash_elt.name;
case SymTagPublicSymbol: return ((struct symt_public*)sym)->hash_elt.name;
case SymTagBaseType: return ((struct symt_basic*)sym)->hash_elt.name;
case SymTagLabel: return ((struct symt_function_point*)sym)->name;
/* hierarchy tree */
case SymTagEnum: return ((struct symt_enum*)sym)->name;
case SymTagTypedef: return ((struct symt_typedef*)sym)->hash_elt.name;
case SymTagUDT: return ((struct symt_udt*)sym)->hash_elt.name;
default:
FIXME("Unsupported sym-tag %s\n", symt_get_tag_str(sym->tag));
/* fall through */
case SymTagArrayType:
case SymTagPointerType:
case SymTagFunctionType:
return NULL;
}
}
static struct symt* symt_find_type_by_name(struct module* module,
enum SymTagEnum sym_tag,
const char* typename)
{
void* ptr;
struct symt_ht* type;
struct hash_table_iter hti;
assert(typename);
assert(module);
hash_table_iter_init(&module->ht_types, &hti, typename);
while ((ptr = hash_table_iter_up(&hti)))
{
type = GET_ENTRY(ptr, struct symt_ht, hash_elt);
if ((sym_tag == SymTagNull || type->symt.tag == sym_tag) &&
type->hash_elt.name && !strcmp(type->hash_elt.name, typename))
return &type->symt;
}
SetLastError(ERROR_INVALID_NAME); /* FIXME ?? */
return NULL;
}
struct symt_basic* symt_new_basic(struct module* module, enum BasicType bt,
const char* typename, unsigned size)
{
struct symt_basic* sym;
if (typename)
{
sym = (struct symt_basic*)symt_find_type_by_name(module, SymTagBaseType,
typename);
if (sym && sym->bt == bt && sym->size == size)
return sym;
}
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagBaseType;
if (typename)
{
sym->hash_elt.name = pool_strdup(&module->pool, typename);
hash_table_add(&module->ht_types, &sym->hash_elt);
} else sym->hash_elt.name = NULL;
sym->bt = bt;
sym->size = size;
}
return sym;
}
struct symt_udt* symt_new_udt(struct module* module, const char* typename,
unsigned size, enum UdtKind kind)
{
struct symt_udt* sym;
TRACE_(dbghelp_symt)("Adding udt %s:%s\n", module->module.ModuleName, typename);
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagUDT;
sym->kind = kind;
sym->size = size;
if (typename)
{
sym->hash_elt.name = pool_strdup(&module->pool, typename);
hash_table_add(&module->ht_types, &sym->hash_elt);
} else sym->hash_elt.name = NULL;
vector_init(&sym->vchildren, sizeof(struct symt*), 8);
}
return sym;
}
BOOL symt_set_udt_size(struct module* module, struct symt_udt* udt, unsigned size)
{
assert(udt->symt.tag == SymTagUDT);
if (vector_length(&udt->vchildren) != 0)
{
if (udt->size != size)
FIXME_(dbghelp_symt)("Changing size for %s from %u to %u\n",
udt->hash_elt.name, udt->size, size);
return TRUE;
}
udt->size = size;
return TRUE;
}
/******************************************************************
* symt_add_udt_element
*
* add an element to a udt (struct, class, union)
* the size & offset parameters are expressed in bits (not bytes) so that
* we can mix in the single call bytes aligned elements (regular fields) and
* the others (bit fields)
*/
BOOL symt_add_udt_element(struct module* module, struct symt_udt* udt_type,
const char* name, struct symt* elt_type,
unsigned offset, unsigned size)
{
struct symt_data* m;
struct symt** p;
assert(udt_type->symt.tag == SymTagUDT);
TRACE_(dbghelp_symt)("Adding %s to UDT %s\n", name, udt_type->hash_elt.name);
p = NULL;
while ((p = vector_iter_up(&udt_type->vchildren, p)))
{
m = (struct symt_data*)*p;
assert(m);
assert(m->symt.tag == SymTagData);
if (m->hash_elt.name[0] == name[0] && strcmp(m->hash_elt.name, name) == 0)
return TRUE;
}
if ((m = pool_alloc(&module->pool, sizeof(*m))) == NULL) return FALSE;
memset(m, 0, sizeof(*m));
m->symt.tag = SymTagData;
m->hash_elt.name = pool_strdup(&module->pool, name);
m->hash_elt.next = NULL;
m->kind = DataIsMember;
m->container = &udt_type->symt;
m->type = elt_type;
m->u.s.offset = offset;
m->u.s.length = ((offset & 7) || (size & 7)) ? size : 0;
m->u.s.reg_id = 0;
p = vector_add(&udt_type->vchildren, &module->pool);
*p = &m->symt;
return TRUE;
}
struct symt_enum* symt_new_enum(struct module* module, const char* typename)
{
struct symt_enum* sym;
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagEnum;
sym->name = pool_strdup(&module->pool, typename);
vector_init(&sym->vchildren, sizeof(struct symt*), 8);
}
return sym;
}
BOOL symt_add_enum_element(struct module* module, struct symt_enum* enum_type,
const char* name, int value)
{
struct symt_data* e;
struct symt** p;
assert(enum_type->symt.tag == SymTagEnum);
e = pool_alloc(&module->pool, sizeof(*e));
if (e == NULL) return FALSE;
e->symt.tag = SymTagData;
e->hash_elt.name = pool_strdup(&module->pool, name);
e->hash_elt.next = NULL;
e->kind = DataIsConstant;
e->container = &enum_type->symt;
/* CV defines the underlying type for the enumeration */
e->type = &symt_new_basic(module, btInt, "int", 4)->symt;
e->u.value.n1.n2.vt = VT_I4;
e->u.value.n1.n2.n3.lVal = value;
p = vector_add(&enum_type->vchildren, &module->pool);
if (!p) return FALSE; /* FIXME we leak e */
*p = &e->symt;
return TRUE;
}
struct symt_array* symt_new_array(struct module* module, int min, int max,
struct symt* base)
{
struct symt_array* sym;
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagArrayType;
sym->start = min;
sym->end = max;
sym->basetype = base;
}
return sym;
}
struct symt_function_signature* symt_new_function_signature(struct module* module,
struct symt* ret_type)
{
struct symt_function_signature* sym;
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagFunctionType;
sym->rettype = ret_type;
vector_init(&sym->vchildren, sizeof(struct symt*), 4);
}
return sym;
}
BOOL symt_add_function_signature_parameter(struct module* module,
struct symt_function_signature* sig_type,
struct symt* param)
{
struct symt** p;
assert(sig_type->symt.tag == SymTagFunctionType);
p = vector_add(&sig_type->vchildren, &module->pool);
if (!p) return FALSE; /* FIXME we leak e */
*p = param;
return TRUE;
}
struct symt_pointer* symt_new_pointer(struct module* module, struct symt* ref_type)
{
struct symt_pointer* sym;
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagPointerType;
sym->pointsto = ref_type;
}
return sym;
}
struct symt_typedef* symt_new_typedef(struct module* module, struct symt* ref,
const char* name)
{
struct symt_typedef* sym;
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagTypedef;
sym->type = ref;
sym->hash_elt.name = pool_strdup(&module->pool, name);
hash_table_add(&module->ht_types, &sym->hash_elt);
}
return sym;
}
/******************************************************************
* SymEnumTypes (DBGHELP.@)
*
*/
BOOL WINAPI SymEnumTypes(HANDLE hProcess, unsigned long BaseOfDll,
PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
void* UserContext)
{
struct process* pcs;
struct module* module;
struct symt_ht* type;
void* ptr;
char buffer[sizeof(SYMBOL_INFO) + 256];
SYMBOL_INFO* sym_info = (SYMBOL_INFO*)buffer;
struct hash_table_iter hti;
const char* tmp;
TRACE("(%p %08lx %p %p)\n",
hProcess, BaseOfDll, EnumSymbolsCallback, UserContext);
pcs = process_find_by_handle(hProcess);
if (!pcs) return FALSE;
module = module_find_by_addr(pcs, BaseOfDll, DMT_UNKNOWN);
if (!(module = module_get_debug(pcs, module))) return FALSE;
sym_info->SizeOfStruct = sizeof(SYMBOL_INFO);
sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
hash_table_iter_init(&module->ht_types, &hti, NULL);
while ((ptr = hash_table_iter_up(&hti)))
{
type = GET_ENTRY(ptr, struct symt_ht, hash_elt);
sym_info->TypeIndex = (DWORD)type;
sym_info->info = 0; /* FIXME */
symt_get_info(&type->symt, TI_GET_LENGTH, &sym_info->Size);
sym_info->ModBase = module->module.BaseOfImage;
sym_info->Flags = 0; /* FIXME */
sym_info->Value = 0; /* FIXME */
sym_info->Address = 0; /* FIXME */
sym_info->Register = 0; /* FIXME */
sym_info->Scope = 0; /* FIXME */
sym_info->Tag = type->symt.tag;
tmp = symt_get_name(&type->symt);
sym_info->NameLen = strlen(tmp) + 1;
strncpy(sym_info->Name, tmp, min(sym_info->NameLen, sym_info->MaxNameLen));
sym_info->Name[sym_info->MaxNameLen - 1] = '\0';
if (!EnumSymbolsCallback(sym_info, sym_info->Size, UserContext)) break;
}
return TRUE;
}
/******************************************************************
* symt_get_info
*
* Retrieves inforamtion about a symt (either symbol or type)
*/
BOOL symt_get_info(const struct symt* type, IMAGEHLP_SYMBOL_TYPE_INFO req,
void* pInfo)
{
unsigned len;
if (!type) return FALSE;
/* helper to typecast pInfo to its expected type (_t) */
#define X(_t) (*((_t*)pInfo))
switch (req)
{
case TI_FINDCHILDREN:
{
const struct vector* v;
struct symt** pt;
unsigned i;
TI_FINDCHILDREN_PARAMS* tifp = pInfo;
switch (type->tag)
{
case SymTagUDT: v = &((struct symt_udt*)type)->vchildren; break;
case SymTagEnum: v = &((struct symt_enum*)type)->vchildren; break;
case SymTagFunctionType: v = &((struct symt_function_signature*)type)->vchildren; break;
case SymTagFunction: v = &((struct symt_function*)type)->vchildren; break;
default:
FIXME("Unsupported sym-tag %s for find-children\n",
symt_get_tag_str(type->tag));
return FALSE;
}
for (i = 0; i < tifp->Count; i++)
{
if (!(pt = vector_at(v, tifp->Start + i))) return FALSE;
tifp->ChildId[i] = (DWORD)*pt;
}
}
break;
case TI_GET_ADDRESS:
switch (type->tag)
{
case SymTagData:
switch (((struct symt_data*)type)->kind)
{
case DataIsGlobal:
case DataIsFileStatic:
X(DWORD) = ((struct symt_data*)type)->u.address;
break;
default: return FALSE;
}
break;
case SymTagFunction:
X(DWORD) = ((struct symt_function*)type)->addr;
break;
case SymTagPublicSymbol:
X(DWORD) = ((struct symt_public*)type)->address;
break;
case SymTagFuncDebugStart:
case SymTagFuncDebugEnd:
case SymTagLabel:
X(DWORD) = ((struct symt_function_point*)type)->parent->addr +
((struct symt_function_point*)type)->offset;
break;
default:
FIXME("Unsupported sym-tag %s for get-address\n",
symt_get_tag_str(type->tag));
return FALSE;
}
break;
case TI_GET_BASETYPE:
switch (type->tag)
{
case SymTagBaseType:
X(DWORD) = ((struct symt_basic*)type)->bt;
break;
case SymTagEnum:
X(DWORD) = btInt;
break;
default:
return FALSE;
}
break;
case TI_GET_BITPOSITION:
if (type->tag != SymTagData ||
((struct symt_data*)type)->kind != DataIsMember ||
((struct symt_data*)type)->u.s.length == 0)
return FALSE;
X(DWORD) = ((struct symt_data*)type)->u.s.offset & 7;
break;
case TI_GET_CHILDRENCOUNT:
switch (type->tag)
{
case SymTagUDT:
X(DWORD) = vector_length(&((struct symt_udt*)type)->vchildren);
break;
case SymTagEnum:
X(DWORD) = vector_length(&((struct symt_enum*)type)->vchildren);
break;
case SymTagFunctionType:
X(DWORD) = vector_length(&((struct symt_function_signature*)type)->vchildren);
break;
case SymTagFunction:
X(DWORD) = vector_length(&((struct symt_function*)type)->vchildren);
break;
case SymTagPointerType: /* MS does it that way */
case SymTagArrayType: /* MS does it that way */
X(DWORD) = 0;
break;
default:
FIXME("Unsupported sym-tag %s for get-children-count\n",
symt_get_tag_str(type->tag));
/* fall through */
case SymTagData:
case SymTagPublicSymbol:
case SymTagBaseType:
return FALSE;
}
break;
case TI_GET_COUNT:
/* it seems that FunctionType also react to GET_COUNT (same value as
* GET_CHILDREN_COUNT ?, except for C++ methods, where it seems to
* also include 'this' (GET_CHILDREN_COUNT+1)
*/
if (type->tag != SymTagArrayType) return FALSE;
X(DWORD) = ((struct symt_array*)type)->end -
((struct symt_array*)type)->start;
break;
case TI_GET_DATAKIND:
if (type->tag != SymTagData) return FALSE;
X(DWORD) = ((struct symt_data*)type)->kind;
break;
case TI_GET_LENGTH:
switch (type->tag)
{
case SymTagBaseType:
X(DWORD) = ((struct symt_basic*)type)->size;
break;
case SymTagFunction:
X(DWORD) = ((struct symt_function*)type)->size;
break;
case SymTagPointerType:
X(DWORD) = sizeof(void*);
break;
case SymTagUDT:
X(DWORD) = ((struct symt_udt*)type)->size;
break;
case SymTagEnum:
X(DWORD) = sizeof(int); /* FIXME: should be size of base-type of enum !!! */
break;
case SymTagData:
if (((struct symt_data*)type)->kind != DataIsMember ||
!((struct symt_data*)type)->u.s.length)
return FALSE;
X(DWORD) = ((struct symt_data*)type)->u.s.length;
break;
case SymTagArrayType:
if (!symt_get_info(((struct symt_array*)type)->basetype,
TI_GET_LENGTH, pInfo))
return FALSE;
X(DWORD) *= ((struct symt_array*)type)->end -
((struct symt_array*)type)->start;
break;
case SymTagPublicSymbol:
X(DWORD) = ((struct symt_public*)type)->size;
break;
case SymTagTypedef:
return symt_get_info(((struct symt_typedef*)type)->type, TI_GET_LENGTH, pInfo);
break;
default:
FIXME("Unsupported sym-tag %s for get-length\n",
symt_get_tag_str(type->tag));
return 0;
}
break;
case TI_GET_LEXICALPARENT:
switch (type->tag)
{
case SymTagBlock:
X(DWORD) = (DWORD)((struct symt_block*)type)->container;
break;
case SymTagData:
X(DWORD) = (DWORD)((struct symt_data*)type)->container;
break;
default:
FIXME("Unsupported sym-tag %s for get-lexical-parent\n",
symt_get_tag_str(type->tag));
return FALSE;
}
break;
case TI_GET_NESTED:
switch (type->tag)
{
case SymTagUDT:
case SymTagEnum:
X(DWORD) = 0;
break;
default:
return FALSE;
}
break;
case TI_GET_OFFSET:
switch (type->tag)
{
case SymTagData:
switch (((struct symt_data*)type)->kind)
{
case DataIsParam:
case DataIsLocal:
case DataIsMember:
X(ULONG) = ((struct symt_data*)type)->u.s.offset >> 3;
break;
default:
FIXME("Unknown kind (%u) for get-offset\n",
((struct symt_data*)type)->kind);
return FALSE;
}
break;
default:
FIXME("Unsupported sym-tag %s for get-offset\n",
symt_get_tag_str(type->tag));
return FALSE;
}
break;
case TI_GET_SYMNAME:
{
const char* name = symt_get_name(type);
if (!name) return FALSE;
len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
X(WCHAR*) = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
if (!X(WCHAR*)) return FALSE;
MultiByteToWideChar(CP_ACP, 0, name, -1, X(WCHAR*), len);
}
break;
case TI_GET_SYMTAG:
X(DWORD) = type->tag;
break;
case TI_GET_TYPE:
case TI_GET_TYPEID:
switch (type->tag)
{
/* hierarchical => hierarchical */
case SymTagArrayType:
X(DWORD) = (DWORD)((struct symt_array*)type)->basetype;
break;
case SymTagPointerType:
X(DWORD) = (DWORD)((struct symt_pointer*)type)->pointsto;
break;
case SymTagFunctionType:
X(DWORD) = (DWORD)((struct symt_function_signature*)type)->rettype;
break;
case SymTagTypedef:
X(DWORD) = (DWORD)((struct symt_typedef*)type)->type;
break;
/* lexical => hierarchical */
case SymTagData:
X(DWORD) = (DWORD)((struct symt_data*)type)->type;
break;
case SymTagFunction:
X(DWORD) = (DWORD)((struct symt_function*)type)->type;
break;
/* FIXME: should also work for enums and FunctionArgType */
default:
FIXME("Unsupported sym-tag %s for get-type\n",
symt_get_tag_str(type->tag));
return FALSE;
}
break;
case TI_GET_UDTKIND:
if (type->tag != SymTagUDT) return FALSE;
X(DWORD) = ((struct symt_udt*)type)->kind;
break;
case TI_GET_VALUE:
if (type->tag != SymTagData || ((struct symt_data*)type)->kind != DataIsConstant)
return FALSE;
X(VARIANT) = ((struct symt_data*)type)->u.value;
break;
#undef X
case TI_GET_ADDRESSOFFSET:
case TI_GET_ARRAYINDEXTYPEID:
case TI_GET_CALLING_CONVENTION:
case TI_GET_CLASSPARENTID:
case TI_GET_SYMINDEX:
case TI_GET_THISADJUST:
case TI_GET_VIRTUALBASECLASS:
case TI_GET_VIRTUALBASEPOINTEROFFSET:
case TI_GET_VIRTUALTABLESHAPEID:
case TI_IS_EQUIV_TO:
FIXME("Unsupported GetInfo request (%u)\n", req);
return FALSE;
}
return TRUE;
}
/******************************************************************
* SymGetTypeInfo (DBGHELP.@)
*
*/
BOOL WINAPI SymGetTypeInfo(HANDLE hProcess, unsigned long ModBase,
ULONG TypeId, IMAGEHLP_SYMBOL_TYPE_INFO GetType,
PVOID pInfo)
{
struct process* pcs = process_find_by_handle(hProcess);
if (!pcs) return FALSE;
#if 0
struct module* module;
module = module_find_by_addr(pcs, ModBase, DMT_UNKNOWN);
if (!(module = module_get_debug(pcs, module))) return FALSE;
#endif
return symt_get_info((struct symt*)TypeId, GetType, pInfo);
}
/******************************************************************
* SymGetTypeFromName (DBGHELP.@)
*
*/
BOOL WINAPI SymGetTypeFromName(HANDLE hProcess, unsigned long BaseOfDll,
LPSTR Name, PSYMBOL_INFO Symbol)
{
struct process* pcs = process_find_by_handle(hProcess);
struct module* module;
struct symt* type;
if (!pcs) return FALSE;
module = module_find_by_addr(pcs, BaseOfDll, DMT_UNKNOWN);
if (!module) return FALSE;
type = symt_find_type_by_name(module, SymTagNull, Name);
if (!type) return FALSE;
Symbol->TypeIndex = (DWORD)type;
return TRUE;
}