blob: 2c8152053ef4c85fa31beeb663162d52c7b1c949 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* Symbols service - Windows version
*/
#include <config.h>
#if SERVICE_Symbols && !ENABLE_SymbolsProxy && defined(WIN32) && !ENABLE_ELF
#include <errno.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <framework/errors.h>
#include <framework/events.h>
#include <framework/myalloc.h>
#include <framework/trace.h>
#include <services/symbols.h>
#include <services/stacktrace.h>
#include <services/memorymap.h>
#include <system/Windows/windbgcache.h>
#include <system/Windows/context-win32.h>
#if ENABLE_RCBP_TEST
# include <main/test.h>
#endif
#ifndef MAX_SYM_NAME
# define MAX_SYM_NAME 2000
#endif
typedef struct TypeInfo {
char * name;
unsigned char size;
unsigned char sign;
unsigned char real;
} TypeInfo;
static const TypeInfo basic_type_info[] = {
{ "void", 0, 0, 0 },
{ "char", sizeof(char), 1, 0 },
{ "unsigned char", sizeof(char), 0, 0 },
{ "signed char", sizeof(char), 1, 0 },
{ "short", sizeof(short), 1, 0 },
{ "unsigned short", sizeof(short), 0, 0 },
{ "signed short", sizeof(short), 1, 0 },
{ "short int", sizeof(short), 1, 0 },
{ "unsigned short int", sizeof(short), 0, 0 },
{ "signed short int", sizeof(short), 1, 0 },
{ "int", sizeof(int), 1, 0 },
{ "unsigned", sizeof(int), 0, 0 },
{ "unsigned int", sizeof(int), 0, 0 },
{ "signed int", sizeof(int), 1, 0 },
{ "long", sizeof(long), 1, 0 },
{ "unsigned long", sizeof(long), 0, 0 },
{ "signed long", sizeof(long), 1, 0 },
{ "long int", sizeof(long), 1, 0 },
{ "unsigned long int", sizeof(long), 0, 0 },
{ "signed long int", sizeof(long), 1, 0 },
{ "long long", sizeof(int64_t), 1, 0 },
{ "unsigned long long", sizeof(int64_t), 0, 0 },
{ "signed long long", sizeof(int64_t), 1, 0 },
{ "long long int", sizeof(int64_t), 1, 0 },
{ "unsigned long long int", sizeof(int64_t), 0, 0 },
{ "signed long long int", sizeof(int64_t), 1, 0 },
{ "float", sizeof(float), 1, 1 },
{ "double", sizeof(double), 1, 1 },
{ "long double", sizeof(long double), 1, 1 },
{ NULL }
};
#define BST_UNSIGNED 11
struct Symbol {
unsigned magic;
Context * ctx;
unsigned frame;
int sym_class;
ULONG64 module;
ULONG index; /* The symbol index in debug info section */
const TypeInfo * info; /* If not NULL, the symbol is basic type */
const Symbol * base; /* If not NULL, the symbol is array or pointer with this base type */
size_t length;
ContextAddress address;
};
#include <services/symbols_alloc.h>
typedef struct SymbolCacheEntry {
HANDLE process;
ULONG64 pc;
char name[MAX_SYM_NAME];
ErrorReport * error;
int sym_class;
int frame_relative;
ULONG64 module;
ULONG index;
} SymbolCacheEntry;
#define SYMBOL_CACHE_SIZE (4 * MEM_USAGE_FACTOR - 1)
static SymbolCacheEntry symbol_cache[SYMBOL_CACHE_SIZE];
static char * tmp_buf = NULL;
static int tmp_buf_size = 0;
static int get_stack_frame(Context * ctx, int frame, ContextAddress ip, IMAGEHLP_STACK_FRAME * stack_frame) {
memset(stack_frame, 0, sizeof(IMAGEHLP_STACK_FRAME));
if (frame == STACK_NO_FRAME) {
stack_frame->InstructionOffset = ip;
}
else if (ctx->parent != NULL) {
uint64_t v = 0;
StackFrame * frame_info;
if (get_frame_info(ctx, frame, &frame_info) < 0) return -1;
if (read_reg_value(frame_info, get_PC_definition(ctx), &v) < 0) return -1;
stack_frame->InstructionOffset = v;
}
return 0;
}
static int get_sym_info(const Symbol * sym, DWORD index, SYMBOL_INFO ** res) {
static ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
SYMBOL_INFO * info = (SYMBOL_INFO *)buffer;
HANDLE process = get_context_handle(sym->ctx->parent == NULL ? sym->ctx : sym->ctx->parent);
info->SizeOfStruct = sizeof(SYMBOL_INFO);
info->MaxNameLen = MAX_SYM_NAME;
if (!SymFromIndex(process, sym->module, index, info)) {
set_win32_errno(GetLastError());
return -1;
}
*res = info;
return 0;
}
static int get_type_info(const Symbol * sym, IMAGEHLP_SYMBOL_TYPE_INFO info_tag, void * info) {
HANDLE process = get_context_handle(sym->ctx->parent == NULL ? sym->ctx : sym->ctx->parent);
if (!SymGetTypeInfo(process, sym->module, sym->index, info_tag, info)) {
set_win32_errno(GetLastError());
return -1;
}
return 0;
}
static void tag2symclass(Symbol * sym, int tag) {
DWORD dword;
sym->sym_class = SYM_CLASS_UNKNOWN;
switch (tag) {
case SymTagFunction:
sym->sym_class = SYM_CLASS_FUNCTION;
break;
case SymTagData:
if (get_type_info(sym, TI_GET_DATAKIND, &dword) == 0) {
if (dword == DataIsConstant) {
sym->sym_class = SYM_CLASS_VALUE;
break;
}
}
sym->sym_class = SYM_CLASS_REFERENCE;
break;
case SymTagUDT:
case SymTagEnum:
case SymTagFunctionType:
case SymTagPointerType:
case SymTagArrayType:
case SymTagBaseType:
case SymTagTypedef:
case SymTagBaseClass:
case SymTagFunctionArgType:
case SymTagCustomType:
case SymTagManagedType:
sym->sym_class = SYM_CLASS_TYPE;
break;
}
}
static int is_frame_relative(SYMBOL_INFO * info) {
return (info->Flags & SYMFLAG_FRAMEREL) || (info->Flags & SYMFLAG_REGREL);
}
static int is_register(SYMBOL_INFO * info) {
return info->Flags & SYMFLAG_REGISTER;
}
static void syminfo2symbol(Context * ctx, int frame, SYMBOL_INFO * info, Symbol * sym) {
sym->module = info->ModBase;
sym->index = info->Index;
if (is_frame_relative(info)) {
assert(frame >= 0);
assert(ctx != ctx->mem);
sym->frame = frame - STACK_NO_FRAME;
}
else {
assert(sym->frame == 0);
ctx = ctx->mem;
}
sym->ctx = ctx;
tag2symclass(sym, info->Tag);
}
static int get_type_tag(Symbol * type, DWORD * tag) {
DWORD dword;
for (;;) {
if (get_type_info(type, TI_GET_SYMTAG, &dword) < 0) return -1;
if (dword != SymTagTypedef && dword != SymTagFunction && dword != SymTagData) break;
if (get_type_info(type, TI_GET_TYPE, &dword) < 0) return -1;
type->index = dword;
}
type->sym_class = SYM_CLASS_TYPE;
*tag = dword;
return 0;
}
const char * symbol2id(const Symbol * sym) {
static char buf[256];
assert(sym->magic == SYMBOL_MAGIC);
if (sym->base) {
char base[sizeof(buf)];
assert(sym->ctx == sym->base->ctx);
assert(sym->sym_class == SYM_CLASS_TYPE);
strcpy(base, symbol2id(sym->base));
snprintf(buf, sizeof(buf), "@P%"PRIX64".%s", (uint64_t)sym->length, base);
}
else {
int i = sym->info ? sym->info - basic_type_info + 1 : 0;
snprintf(buf, sizeof(buf), "@S%"PRIX64".%lX.%X.%X.%s",
(uint64_t)sym->module, sym->index, sym->frame, i, sym->ctx->id);
}
return buf;
}
static uint64_t read_hex(const char ** s) {
uint64_t res = 0;
const char * p = *s;
for (;;) {
if (*p >= '0' && *p <= '9') res = (res << 4) | (*p - '0');
else if (*p >= 'A' && *p <= 'F') res = (res << 4) | (*p - 'A' + 10);
else break;
p++;
}
*s = p;
return res;
}
int id2symbol(const char * id, Symbol ** res) {
Symbol * sym = NULL;
Context * ctx = NULL;
ULONG64 module = 0;
ULONG index = 0;
unsigned frame = 0;
const Symbol * base = NULL;
const TypeInfo * info = NULL;
size_t length = 0;
const char * p;
if (id != NULL && id[0] == '@' && id[1] == 'P') {
p = id + 2;
length = (size_t)read_hex(&p);
if (*p == '.') p++;
if (id2symbol(p, (Symbol **)&base)) return -1;
ctx = base->ctx;
}
else if (id != NULL && id[0] == '@' && id[1] == 'S') {
unsigned idx = 0;
p = id + 2;
module = (ULONG64)read_hex(&p);
if (*p == '.') p++;
index = (ULONG)read_hex(&p);
if (*p == '.') p++;
frame = (unsigned)read_hex(&p);
if (*p == '.') p++;
idx = (unsigned)read_hex(&p);
if (idx) info = basic_type_info + (idx - 1);
if (*p == '.') p++;
ctx = id2ctx(p);
}
else {
errno = ERR_INV_CONTEXT;
return -1;
}
if (ctx == NULL) {
errno = ERR_INV_CONTEXT;
return -1;
}
sym = alloc_symbol();
sym->ctx = ctx;
sym->module = module;
sym->index = index;
sym->frame = frame;
sym->base = base;
sym->info = info;
sym->length = length;
if (sym->base || sym->info) {
sym->sym_class = SYM_CLASS_TYPE;
}
else {
DWORD dword = 0;
if (get_type_info(sym, TI_GET_SYMTAG, &dword) < 0) return -1;
tag2symclass(sym, dword);
}
*res = sym;
return 0;
}
int get_symbol_class(const Symbol * sym, int * sym_class) {
assert(sym->magic == SYMBOL_MAGIC);
*sym_class = sym->sym_class;
return 0;
}
int get_symbol_type_class(const Symbol * sym, int * type_class) {
int res = TYPE_CLASS_UNKNOWN;
Symbol type = *sym;
DWORD tag = 0;
DWORD base = 0;
assert(sym->magic == SYMBOL_MAGIC);
if (sym->base) {
*type_class = sym->length == 0 ? TYPE_CLASS_POINTER : TYPE_CLASS_ARRAY;
return 0;
}
if (sym->info) {
if (sym->info->real) {
*type_class = TYPE_CLASS_REAL;
}
else if (sym->info->sign) {
*type_class = TYPE_CLASS_INTEGER;
}
else {
*type_class = TYPE_CLASS_CARDINAL;
}
return 0;
}
if (get_type_tag(&type, &tag)) return -1;
switch (tag) {
case SymTagFunction:
case SymTagPublicSymbol:
res = TYPE_CLASS_FUNCTION;
break;
case SymTagEnum:
res = TYPE_CLASS_ENUMERATION;
break;
case SymTagFunctionType:
res = TYPE_CLASS_FUNCTION;
break;
case SymTagPointerType:
res = TYPE_CLASS_POINTER;
break;
case SymTagArrayType:
res = TYPE_CLASS_ARRAY;
break;
case SymTagUDT:
res = TYPE_CLASS_COMPOSITE;
break;
case SymTagBaseType:
if (get_type_info(&type, TI_GET_BASETYPE, &base) < 0) return -1;
switch (base) {
case btNoType:
break;
case btVoid:
case btChar:
case btWChar:
case btInt:
case btBool:
case btLong:
case btBit:
res = TYPE_CLASS_INTEGER;
break;
case btUInt:
case btULong:
res = TYPE_CLASS_CARDINAL;
break;
case btFloat:
res = TYPE_CLASS_REAL;
break;
case btBCD:
case btCurrency:
case btDate:
case btVariant:
case btComplex:
case btBSTR:
case btHresult:
break;
}
break;
}
*type_class = res;
return 0;
}
int get_symbol_update_policy(const Symbol * sym, char ** id, int * policy) {
assert(sym->magic == SYMBOL_MAGIC);
*id = sym->ctx->id;
*policy = context_has_state(sym->ctx) ? UPDATE_ON_EXE_STATE_CHANGES : UPDATE_ON_MEMORY_MAP_CHANGES;
return 0;
}
int get_symbol_name(const Symbol * sym, char ** name) {
WCHAR * ptr = NULL;
assert(sym->magic == SYMBOL_MAGIC);
if (sym->base) {
*name = NULL;
return 0;
}
if (sym->info) {
*name = sym->info->name;
return 0;
}
*name = NULL;
if (get_type_info(sym, TI_GET_SYMNAME, &ptr) < 0) ptr = NULL;
if (ptr != NULL && wcscmp(ptr, L"<unnamed-tag>") == 0) ptr = NULL;
if (ptr != NULL) {
int len = 0;
int err = 0;
if (tmp_buf == NULL) {
tmp_buf_size = 256;
tmp_buf = (char *)loc_alloc(tmp_buf_size);
}
for (;;) {
len = WideCharToMultiByte(CP_UTF8, 0, ptr, -1, tmp_buf, tmp_buf_size - 1, NULL, NULL);
if (len != 0) break;
err = GetLastError();
if (err != ERROR_INSUFFICIENT_BUFFER) {
set_win32_errno(err);
return -1;
}
tmp_buf_size *= 2;
tmp_buf = (char *)loc_realloc(tmp_buf, tmp_buf_size);
}
HeapFree(GetProcessHeap(), 0, ptr);
tmp_buf[len] = 0;
*name = tmp_buf;
}
else {
DWORD tag = 0;
Symbol type = *sym;
if (get_type_tag(&type, &tag)) return -1;
if (tag == SymTagBaseType) {
ContextAddress size = 0;
int type_class = 0;
unsigned char sign = 0;
unsigned char real = 0;
const TypeInfo * p = basic_type_info;
if (get_symbol_size(&type, &size)) return -1;
if (get_symbol_type_class(&type, &type_class)) return -1;
if (type_class == TYPE_CLASS_INTEGER) sign = 1;
else if (type_class == TYPE_CLASS_REAL) real = sign = 1;
while (p->name != NULL) {
if (p->size == size && p->sign == sign && p->real == real) {
*name = p->name;
break;
}
p++;
}
}
}
return 0;
}
int get_symbol_size(const Symbol * sym, ContextAddress * size) {
uint64_t res = 0;
Symbol type = *sym;
DWORD tag = 0;
assert(sym->magic == SYMBOL_MAGIC);
if (sym->base) {
if (sym->length > 0) {
if (get_symbol_size(sym->base, size)) return -1;
*size *= sym->length;
}
else {
*size = sizeof(void *);
}
return 0;
}
if (sym->info) {
*size = sym->info->size;
return 0;
}
if (sym->sym_class == SYM_CLASS_REFERENCE || sym->sym_class == SYM_CLASS_FUNCTION) {
SYMBOL_INFO * info = NULL;
if (get_sym_info(sym, sym->index, &info) < 0) return -1;
res = info->Size;
}
else {
if (get_type_tag(&type, &tag)) return -1;
if (get_type_info(&type, TI_GET_LENGTH, &res) < 0) return -1;
}
*size = (ContextAddress)res;
return 0;
}
int get_symbol_type(const Symbol * sym, Symbol ** type) {
DWORD tag = 0;
Symbol * res = alloc_symbol();
assert(sym->magic == SYMBOL_MAGIC);
*res = *sym;
if (!res->base && !res->info) {
if (get_type_tag(res, &tag)) return -1;
}
assert(res->sym_class == SYM_CLASS_TYPE);
*type = res;
return 0;
}
int get_symbol_base_type(const Symbol * sym, Symbol ** type) {
DWORD tag = 0;
DWORD index = 0;
Symbol * res = NULL;
assert(sym->magic == SYMBOL_MAGIC);
if (sym->base) {
*type = (Symbol *)sym->base;
return 0;
}
if (sym->info) {
errno = ERR_INV_CONTEXT;
return -1;
}
res = alloc_symbol();
*res = *sym;
if (get_type_tag(res, &tag)) return -1;
if (get_type_info(res, TI_GET_TYPE, &index) < 0) return -1;
res->index = index;
*type = res;
return 0;
}
int get_symbol_index_type(const Symbol * sym, Symbol ** type) {
DWORD tag = 0;
DWORD index = 0;
Symbol * res = alloc_symbol();
assert(sym->magic == SYMBOL_MAGIC);
if (sym->base) {
res->ctx = sym->ctx;
res->sym_class = SYM_CLASS_TYPE;
res->info = basic_type_info + BST_UNSIGNED;
assert(res->info->size == sizeof(int));
assert(res->info->sign == 0);
assert(res->info->real == 0);
*type = res;
return 0;
}
if (sym->info) {
errno = ERR_INV_CONTEXT;
return -1;
}
*res = *sym;
if (get_type_tag(res, &tag)) return -1;
if (get_type_info(res, TI_GET_ARRAYINDEXTYPEID, &index) < 0) return -1;
res->index = index;
*type = res;
return 0;
}
int get_symbol_length(const Symbol * sym, ContextAddress * length) {
DWORD res = 0;
Symbol type = *sym;
DWORD tag = 0;
assert(sym->magic == SYMBOL_MAGIC);
if (sym->base) {
*length = sym->length == 0 ? 1 : sym->length;
return 0;
}
if (sym->info) {
errno = ERR_INV_CONTEXT;
return -1;
}
if (get_type_tag(&type, &tag)) return -1;
if (get_type_info(&type, TI_GET_COUNT, &res) < 0) return -1;
*length = res;
return 0;
}
int get_symbol_lower_bound(const Symbol * sym, int64_t * value) {
Symbol type = *sym;
DWORD tag = 0;
assert(sym->magic == SYMBOL_MAGIC);
if (sym->base) {
*value = 0;
return 0;
}
if (sym->info) {
errno = ERR_INV_CONTEXT;
return -1;
}
if (get_type_tag(&type, &tag)) return -1;
switch (tag) {
case SymTagArrayType:
/* TODO: Windows array symbol lower bound value */
*value = 0;
return 0;
}
errno = ERR_INV_CONTEXT;
return -1;
}
int get_symbol_children(const Symbol * sym, Symbol *** children, int * count) {
static const DWORD FINDCHILDREN_BUF_SIZE = 64;
static TI_FINDCHILDREN_PARAMS * params = NULL;
static Symbol ** buf = NULL;
static unsigned buf_len = 0;
DWORD cnt = 0;
Symbol type = *sym;
DWORD tag = 0;
assert(sym->magic == SYMBOL_MAGIC);
if (sym->base || sym->info) {
*children = NULL;
*count = 0;
return 0;
}
if (get_type_tag(&type, &tag)) return -1;
if (get_type_info(&type, TI_GET_CHILDRENCOUNT, &cnt) < 0) return -1;
if (params == NULL) params = (TI_FINDCHILDREN_PARAMS *)loc_alloc(
sizeof(TI_FINDCHILDREN_PARAMS) + (FINDCHILDREN_BUF_SIZE - 1) * sizeof(ULONG));
if (buf_len < cnt) {
buf = (Symbol **)loc_realloc(buf, sizeof(Symbol *) * cnt);
buf_len = cnt;
}
params->Start = 0;
while (params->Start < cnt) {
DWORD i = cnt - (DWORD)params->Start;
params->Count = i > FINDCHILDREN_BUF_SIZE ? FINDCHILDREN_BUF_SIZE : i;
if (get_type_info(&type, TI_FINDCHILDREN, params) < 0) return -1;
for (i = 0; params->Start < cnt; i++) {
DWORD dword = 0;
Symbol * x = alloc_symbol();
*x = *sym;
x->index = params->ChildId[i];
if (get_type_info(x, TI_GET_SYMTAG, &dword) < 0) return -1;
tag2symclass(x, dword);
buf[params->Start++] = x;
}
}
*children = buf;
*count = cnt;
return 0;
}
int get_symbol_offset(const Symbol * sym, ContextAddress * offset) {
DWORD dword = 0;
assert(sym->magic == SYMBOL_MAGIC);
if (sym->base || sym->info) {
errno = ERR_INV_CONTEXT;
return -1;
}
if (get_type_info(sym, TI_GET_OFFSET, &dword) < 0) return -1;
*offset = dword;
return 0;
}
int get_symbol_value(const Symbol * sym, void ** value, size_t * size, int * big_endian) {
static VARIANT data;
VARTYPE vt;
void * data_addr = &data.bVal;
size_t data_size = 0;
assert(sym->magic == SYMBOL_MAGIC);
if (sym->base || sym->info) {
errno = ERR_INV_CONTEXT;
return -1;
}
assert(data_addr == &data.lVal);
if (get_type_info(sym, TI_GET_VALUE, &data) < 0) return -1;
vt = data.vt;
if (vt & VT_BYREF) {
data_addr = *(void **)data_addr;
vt &= ~VT_BYREF;
}
switch (vt) {
/* VOID */ case 0: break;
/* CHAR */ case VT_I1: data_size = 1; break;
/* SHORT */ case VT_I2: data_size = 2; break;
/* LONG */ case VT_I4: data_size = 4; break;
/* LONGLONG */ case VT_I8: data_size = 8; break;
/* INT */ case VT_INT: data_size = sizeof(int); break;
/* BYTE */ case VT_UI1: data_size = 1; break;
/* USHORT */ case VT_UI2: data_size = 2; break;
/* ULONG */ case VT_UI4: data_size = 4; break;
/* ULONGLONG */ case VT_UI8: data_size = 8; break;
/* UINT */ case VT_UINT: data_size = sizeof(unsigned int); break;
/* FLOAT */ case VT_R4: data_size = 4; break;
/* DOUBLE */ case VT_R8: data_size = 8; break;
/* VARIANT_BOOL */ case VT_BOOL: data_size = sizeof(BOOL); break;
/* SCODE */ case VT_ERROR: data_size = sizeof(ERROR); break;
/* CY */ case VT_CY: data_size = sizeof(CY); break;
/* DATE */ case VT_DATE: data_size = sizeof(DATE); break;
/* BSTR */ case VT_BSTR: data_size = sizeof(BSTR); break;
/* IUnknown * */ case VT_UNKNOWN: data_size = sizeof(IUnknown *); break;
/* IDispatch * */ case VT_DISPATCH: data_size = sizeof(IDispatch *); break;
/* SAFEARRAY * */ case VT_ARRAY: data_size = sizeof(SAFEARRAY *); break;
/* VARIANT */ case VT_VARIANT: data_size = sizeof(VARIANT); break;
/* DECIMAL */ case VT_DECIMAL: data_size = sizeof(DECIMAL); break;
}
*size = data_size;
*value = data_addr;
*big_endian = 0;
return 0;
}
int get_symbol_address(const Symbol * sym, ContextAddress * addr) {
SYMBOL_INFO * info = NULL;
assert(sym->magic == SYMBOL_MAGIC);
if (sym->address != 0) {
*addr = sym->address;
return 0;
}
if (sym->base || sym->info) {
errno = ERR_INV_CONTEXT;
return -1;
}
if (get_sym_info(sym, sym->index, &info) < 0) return -1;
*addr = (ContextAddress)info->Address;
if (is_frame_relative(info)) {
StackFrame * frame_info;
int frame = sym->frame + STACK_NO_FRAME;
if (get_frame_info(sym->ctx, frame, &frame_info) < 0) return -1;
*addr += frame_info->fp - sizeof(ContextAddress) * 2;
}
return 0;
}
int get_symbol_register(const Symbol * sym, Context ** ctx, int * frame, RegisterDefinition ** reg) {
SYMBOL_INFO * info = NULL;
assert(sym->magic == SYMBOL_MAGIC);
if (sym->address != 0 || sym->base || sym->info) {
errno = ERR_INV_CONTEXT;
return -1;
}
if (get_sym_info(sym, sym->index, &info) < 0) return -1;
if (!is_register(info)) {
errno = ERR_INV_CONTEXT;
return -1;
}
/* TODO: map info.Register to regditer definition */
errno = ERR_UNSUPPORTED;
return -1;
}
int get_array_symbol(const Symbol * sym, ContextAddress length, Symbol ** ptr) {
assert(sym->magic == SYMBOL_MAGIC);
*ptr = alloc_symbol();
(*ptr)->ctx = sym->ctx;
(*ptr)->sym_class = SYM_CLASS_TYPE;
(*ptr)->base = (Symbol *)sym;
(*ptr)->length = length;
return 0;
}
static unsigned symbol_hash(HANDLE process, ULONG64 pc, PCSTR name) {
int i;
unsigned h = (unsigned)(uintptr_t)process;
h += h >> 8;
h += (unsigned)(uintptr_t)pc;
for (i = 0; name[i]; i++) h += name[i];
h = h + h / SYMBOL_CACHE_SIZE;
return h % SYMBOL_CACHE_SIZE;
}
static int find_cache_symbol(Context * ctx, int frame, HANDLE process, ULONG64 pc, PCSTR name, Symbol * sym) {
SymbolCacheEntry * entry = symbol_cache + symbol_hash(process, pc, name);
assert(process != NULL);
if (entry->process != process) return 0;
if (entry->pc != pc) return 0;
if (strcmp(entry->name, name)) return 0;
if (entry->error == NULL) {
if (entry->frame_relative) {
assert(frame >= 0);
sym->frame = frame - STACK_NO_FRAME;
}
else {
ctx = ctx->mem;
}
sym->ctx = ctx;
sym->sym_class = entry->sym_class;
sym->module = entry->module;
sym->index = entry->index;
}
set_error_report_errno(entry->error);
return 1;
}
static void add_cache_symbol(HANDLE process, ULONG64 pc, PCSTR name, Symbol * sym, int error) {
SymbolCacheEntry * entry = symbol_cache + symbol_hash(process, pc, name);
assert(process != NULL);
entry->process = process;
entry->pc = pc;
strcpy(entry->name, name);
release_error_report(entry->error);
entry->error = get_error_report(error);
if (!error) {
entry->frame_relative = sym->frame > 0;
entry->sym_class = sym->sym_class;
entry->module = sym->module;
entry->index = sym->index;
}
}
static int set_pe_context(Context * ctx, int frame, ContextAddress ip, HANDLE process, IMAGEHLP_STACK_FRAME * stack_frame) {
if (get_stack_frame(ctx, frame, ip, stack_frame) < 0) return -1;
if (!SymSetContext(process, stack_frame, NULL)) {
DWORD err = GetLastError();
if (err == ERROR_SUCCESS) {
/* Don't know why Windows do that */
}
else if (err == ERROR_MOD_NOT_FOUND && frame != STACK_NO_FRAME) {
/* No local symbols data, search global scope */
if (get_stack_frame(ctx, STACK_NO_FRAME, 0, stack_frame) < 0) return -1;
if (!SymSetContext(process, stack_frame, NULL)) {
err = GetLastError();
if (err != ERROR_SUCCESS) {
set_win32_errno(err);
return -1;
}
}
}
else if (err == ERROR_NOT_SUPPORTED) {
/* Compiled without debug info */
errno = ERR_SYM_NOT_FOUND;
return -1;
}
else {
set_win32_errno(err);
return -1;
}
}
return 0;
}
static int find_pe_symbol_by_name(Context * ctx, int frame, ContextAddress ip, char * name, Symbol * sym) {
HANDLE process = get_context_handle(ctx->parent == NULL ? ctx : ctx->parent);
ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
SYMBOL_INFO * info = (SYMBOL_INFO *)buffer;
IMAGEHLP_STACK_FRAME stack_frame;
DWORD err;
if (set_pe_context(ctx, frame, ip, process, &stack_frame) < 0) return -1;
memset(info, 0, sizeof(SYMBOL_INFO));
info->SizeOfStruct = sizeof(SYMBOL_INFO);
info->MaxNameLen = MAX_SYM_NAME;
if (find_cache_symbol(ctx, frame, process, stack_frame.InstructionOffset, name, sym)) return errno ? -1 : 0;
/* TODO: SymFromName() searches only main executable, need to search DLLs too */
if (SymFromName(process, name, info) && info->Tag != SymTagPublicSymbol) {
syminfo2symbol(ctx, frame, info, sym);
add_cache_symbol(process, stack_frame.InstructionOffset, name, sym, 0);
return 0;
}
if (stack_frame.InstructionOffset != 0) {
DWORD64 module = SymGetModuleBase64(process, stack_frame.InstructionOffset);
if (module != 0 && SymGetTypeFromName(process, module, name, info)) {
syminfo2symbol(ctx, frame, info, sym);
add_cache_symbol(process, stack_frame.InstructionOffset, name, sym, 0);
return 0;
}
}
set_win32_errno(err = GetLastError());
if (err == 0 || err == ERROR_MOD_NOT_FOUND) {
add_cache_symbol(process, stack_frame.InstructionOffset, name, NULL, ERR_SYM_NOT_FOUND);
errno = ERR_SYM_NOT_FOUND;
}
return -1;
}
static int find_pe_symbol_by_addr(Context * ctx, int frame, ContextAddress addr, Symbol * sym) {
HANDLE process = get_context_handle(ctx->parent == NULL ? ctx : ctx->parent);
ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
SYMBOL_INFO * info = (SYMBOL_INFO *)buffer;
IMAGEHLP_STACK_FRAME stack_frame;
DWORD err;
if (set_pe_context(ctx, frame, 0, process, &stack_frame) < 0) return -1;
memset(info, 0, sizeof(SYMBOL_INFO));
info->SizeOfStruct = sizeof(SYMBOL_INFO);
info->MaxNameLen = MAX_SYM_NAME;
if (SymFromAddr(process, addr, NULL, info)) {
syminfo2symbol(ctx, frame, info, sym);
return 0;
}
set_win32_errno(err = GetLastError());
if (err == 0 || err == ERROR_MOD_NOT_FOUND) {
errno = ERR_SYM_NOT_FOUND;
}
return -1;
}
static int find_basic_type_symbol(Context * ctx, char * name, Symbol * sym) {
const TypeInfo * p = basic_type_info;
while (p->name != NULL) {
if (strcmp(p->name, name) == 0) {
sym->ctx = ctx->mem;
sym->sym_class = SYM_CLASS_TYPE;
sym->info = p;
return 0;
}
p++;
}
errno = ERR_SYM_NOT_FOUND;
return -1;
}
int find_symbol_by_name(Context * ctx, int frame, ContextAddress ip, char * name, Symbol ** sym) {
int found = 0;
*sym = alloc_symbol();
(*sym)->ctx = ctx;
if (frame == STACK_TOP_FRAME && (frame = get_top_frame(ctx)) < 0) return -1;
if (find_pe_symbol_by_name(ctx, frame, ip, name, *sym) >= 0) found = 1;
else if (get_error_code(errno) != ERR_SYM_NOT_FOUND) return -1;
#if ENABLE_RCBP_TEST
if (!found) {
int sym_class = 0;
void * address = NULL;
if (find_test_symbol(ctx, name, &address, &sym_class) >= 0) found = 1;
else if (get_error_code(errno) != ERR_SYM_NOT_FOUND) return -1;
if (found) {
(*sym)->ctx = ctx->mem;
(*sym)->sym_class = sym_class;
(*sym)->address = (ContextAddress)address;
}
}
#endif
if (!found) {
if (find_basic_type_symbol(ctx, name, *sym) >= 0) found = 1;
else if (get_error_code(errno) != ERR_SYM_NOT_FOUND) return -1;
}
if (!found) {
errno = ERR_SYM_NOT_FOUND;
return -1;
}
assert(frame >= 0 || (*sym)->ctx == ctx->mem);
assert((*sym)->ctx == ((*sym)->frame ? ctx : ctx->mem));
assert((*sym)->frame == ((*sym)->ctx == (*sym)->ctx->mem ? 0u : frame - STACK_NO_FRAME));
return 0;
}
int find_symbol_in_scope(Context * ctx, int frame, ContextAddress ip, Symbol * scope, char * name, Symbol ** sym) {
errno = ERR_SYM_NOT_FOUND;
return -1;
}
int find_symbol_by_addr(Context * ctx, int frame, ContextAddress addr, Symbol ** sym) {
*sym = alloc_symbol();
(*sym)->ctx = ctx;
if (frame == STACK_TOP_FRAME && (frame = get_top_frame(ctx)) < 0) return -1;
if (find_pe_symbol_by_addr(ctx, frame, addr, *sym) < 0) return -1;
assert(frame >= 0 || (*sym)->ctx == ctx->mem);
assert((*sym)->ctx == ((*sym)->frame ? ctx : ctx->mem));
assert((*sym)->frame == ((*sym)->ctx == (*sym)->ctx->mem ? 0u : frame - STACK_NO_FRAME));
return 0;
}
typedef struct EnumerateSymbolsContext {
Context * ctx;
int frame;
EnumerateSymbolsCallBack * call_back;
void * args;
} EnumerateSymbolsContext;
static BOOL CALLBACK enumerate_symbols_proc(SYMBOL_INFO * info, ULONG symbol_size, VOID * user_context) {
EnumerateSymbolsContext * enum_context = (EnumerateSymbolsContext *)user_context;
Symbol * sym = alloc_symbol();
syminfo2symbol(enum_context->ctx, enum_context->frame, info, sym);
enum_context->call_back(enum_context->args, sym);
return TRUE;
}
int enumerate_symbols(Context * ctx, int frame, EnumerateSymbolsCallBack * call_back, void * args) {
ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
SYMBOL_INFO * symbol = (SYMBOL_INFO *)buffer;
IMAGEHLP_STACK_FRAME stack_frame;
EnumerateSymbolsContext enum_context;
HANDLE process = get_context_handle(ctx->parent == NULL ? ctx : ctx->parent);
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
if (frame == STACK_TOP_FRAME) frame = get_top_frame(ctx);
if (frame == STACK_TOP_FRAME) return -1;
if (get_stack_frame(ctx, frame, 0, &stack_frame) < 0) return -1;
if (!SymSetContext(process, &stack_frame, NULL)) {
DWORD err = GetLastError();
if (err == ERROR_SUCCESS) {
/* Don't know why Windows does that */
}
else {
set_win32_errno(err);
return -1;
}
}
enum_context.ctx = ctx;
enum_context.frame = frame;
enum_context.call_back = call_back;
enum_context.args = args;
if (!SymEnumSymbols(process, 0, NULL, enumerate_symbols_proc, &enum_context)) {
set_win32_errno(GetLastError());
return -1;
}
return 0;
}
ContextAddress is_plt_section(Context * ctx, ContextAddress addr) {
return 0;
}
int get_stack_tracing_info(Context * ctx, ContextAddress addr, StackTracingInfo ** info) {
*info = NULL;
return 0;
}
int get_next_stack_frame(StackFrame * frame, StackFrame * down) {
return 0;
}
static void event_context_exited(Context * ctx, void * client_data) {
unsigned i;
HANDLE handle = get_context_handle(ctx);
if (ctx->parent != NULL) return;
assert(handle != NULL);
for (i = 0; i < SYMBOL_CACHE_SIZE; i++) {
if (symbol_cache[i].process == handle) {
release_error_report(symbol_cache[i].error);
memset(symbol_cache + i, 0, sizeof(SymbolCacheEntry));
}
}
}
static void event_module_loaded(Context * ctx, void * client_data) {
unsigned i;
HANDLE handle = get_context_handle(ctx);
assert(ctx->mem == ctx);
assert(handle != NULL);
for (i = 0; i < SYMBOL_CACHE_SIZE; i++) {
if (symbol_cache[i].process == handle && symbol_cache[i].error) symbol_cache[i].process = NULL;
}
}
static void event_module_unloaded(Context * ctx, void * client_data) {
unsigned i;
HANDLE handle = get_context_handle(ctx);
assert(ctx->mem == ctx);
assert(handle != NULL);
for (i = 0; i < SYMBOL_CACHE_SIZE; i++) {
if (symbol_cache[i].process == handle) symbol_cache[i].process = NULL;
}
}
void ini_symbols_lib(void) {
static ContextEventListener ctx_listener = {
NULL,
event_context_exited,
};
static MemoryMapEventListener map_listener = {
event_module_loaded,
NULL,
event_module_unloaded
};
add_context_event_listener(&ctx_listener, NULL);
add_memory_map_event_listener(&map_listener, NULL);
SymSetOptions(SymGetOptions() | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
}
#endif /* SERVICE_Symbols && defined(_MSC_VER) */