static BOOL symbol_get_debug_start(const struct dbg_type* func, ULONG64* start) { DWORD count, tag; char buffer[sizeof(TI_FINDCHILDREN_PARAMS) + 256 * sizeof(DWORD)]; TI_FINDCHILDREN_PARAMS* fcp = (TI_FINDCHILDREN_PARAMS*)buffer; int i; struct dbg_type child; if (!func->id) return FALSE; /* native dbghelp not always fills the info field */ if (!types_get_info(func, TI_GET_CHILDRENCOUNT, &count)) return FALSE; fcp->Start = 0; while (count) { fcp->Count = min(count, 256); if (types_get_info(func, TI_FINDCHILDREN, fcp)) { for (i = 0; i < min(fcp->Count, count); i++) { child.module = func->module; child.id = fcp->ChildId[i]; types_get_info(&child, TI_GET_SYMTAG, &tag); if (tag != SymTagFuncDebugStart) continue; return types_get_info(&child, TI_GET_ADDRESS, start); } count -= min(count, 256); fcp->Start += 256; fcp->Start += 256; } } return FALSE; }
/****************************************************************** * types_get_real_type * * Get rid of any potential typedef in the lvalue's type to get * to the 'real' type (the one we can work upon). */ BOOL types_get_real_type(struct dbg_type* type, DWORD* tag) { if (type->id == dbg_itype_none) return FALSE; do { if (!types_get_info(type, TI_GET_SYMTAG, tag)) return FALSE; if (*tag != SymTagTypedef) return TRUE; } while (types_get_info(type, TI_GET_TYPE, &type->id)); return FALSE; }
/****************************************************************** * types_deref * */ BOOL types_deref(const struct dbg_lvalue* lvalue, struct dbg_lvalue* result) { struct dbg_type type = lvalue->type; DWORD tag; memset(result, 0, sizeof(*result)); result->type.id = dbg_itype_none; result->type.module = 0; /* * Make sure that this really makes sense. */ if (!types_get_real_type(&type, &tag) || tag != SymTagPointerType || !memory_read_value(lvalue, sizeof(result->addr.Offset), &result->addr.Offset) || !types_get_info(&type, TI_GET_TYPE, &result->type.id)) return FALSE; result->type.module = type.module; result->cookie = DLV_TARGET; /* FIXME: this is currently buggy. * there is no way to tell were the deref:ed value is... * for example: * x is a pointer to struct s, x being on the stack * => lvalue is in debuggee, result is in debugger * x is a pointer to struct s, x being optimized into a reg * => lvalue is debugger, result is debuggee * x is a pointer to internal variable x * => lvalue is debugger, result is debuggee * so we force debuggee address space, because dereferencing pointers to * internal variables is very unlikely. A correct fix would be * rather large. */ result->addr.Mode = AddrModeFlat; return TRUE; }
BOOL types_store_value(struct dbg_lvalue* lvalue_to, const struct dbg_lvalue* lvalue_from) { LONGLONG val; DWORD64 size; BOOL is_signed; if (!types_get_info(&lvalue_to->type, TI_GET_LENGTH, &size)) return FALSE; if (0 == lvalue_to->type.module) { lvalue_to->cookie = DLV_TARGET; #if defined(__x86_64__) lvalue_to->addr.Offset = *(DWORD64*)(lvalue_to->addr.Offset); #else lvalue_to->addr.Offset = *(DWORD64*)(DWORD)(lvalue_to->addr.Offset); #endif } if (sizeof(val) < size) { dbg_printf("Unsufficient size\n"); return FALSE; } /* FIXME: should support floats as well */ val = types_extract_as_longlong(lvalue_from, NULL, &is_signed); return be_cpu->store_integer(lvalue_to, size, is_signed, val); }
/****************************************************************** * types_get_udt_element_lvalue * * Implement a structure derefencement */ static BOOL types_get_udt_element_lvalue(struct dbg_lvalue* lvalue, const struct dbg_type* type, long int* tmpbuf) { DWORD offset, bitoffset; DWORD bt; DWORD64 length; unsigned mask; types_get_info(type, TI_GET_TYPE, &lvalue->type.id); lvalue->type.module = type->module; if (!types_get_info(type, TI_GET_OFFSET, &offset)) return FALSE; lvalue->addr.Offset += offset; if (types_get_info(type, TI_GET_BITPOSITION, &bitoffset)) { types_get_info(type, TI_GET_LENGTH, &length); /* FIXME: this test isn't sufficient, depending on start of bitfield * (ie a 32 bit field can spread across 5 bytes) */ if (length > 8 * sizeof(*tmpbuf)) return FALSE; lvalue->addr.Offset += bitoffset >> 3; /* * Bitfield operation. We have to extract the field and store * it in a temporary buffer so that we get it all right. */ if (!memory_read_value(lvalue, sizeof(*tmpbuf), tmpbuf)) return FALSE; mask = 0xffffffff << (DWORD)length; *tmpbuf >>= bitoffset & 7; *tmpbuf &= ~mask; lvalue->cookie = DLV_HOST; lvalue->addr.Offset = (ULONG_PTR)tmpbuf; /* * OK, now we have the correct part of the number. * Check to see whether the basic type is signed or not, and if so, * we need to sign extend the number. */ if (types_get_info(&lvalue->type, TI_GET_BASETYPE, &bt) && bt == btInt && (*tmpbuf & (1 << ((DWORD)length - 1)))) { *tmpbuf |= mask; } }
BOOL types_store_value(struct dbg_lvalue* lvalue_to, const struct dbg_lvalue* lvalue_from) { LONGLONG val; DWORD64 size; BOOL is_signed; if (!types_get_info(&lvalue_to->type, TI_GET_LENGTH, &size)) return FALSE; if (sizeof(val) < size) { dbg_printf("Unsufficient size\n"); return FALSE; } /* FIXME: should support floats as well */ val = types_extract_as_longlong(lvalue_from, NULL, &is_signed); return be_cpu->store_integer(lvalue_to, size, is_signed, val); }
/*********************************************************************** * break_add_watch * * Add a watchpoint. */ static void break_add_watch(const struct dbg_lvalue* lvalue, BOOL is_write) { int num; DWORD64 l = 4; if (lvalue->cookie == DLV_HOST) { dbg_printf("Cannot set a watch point on register or register-based variable\n"); return; } num = init_xpoint((is_write) ? be_xpoint_watch_write : be_xpoint_watch_read, &lvalue->addr); if (num == -1) return; if (lvalue->type.id != dbg_itype_none) { if (types_get_info(&lvalue->type, TI_GET_LENGTH, &l)) { switch (l) { case 4: case 2: case 1: break; default: dbg_printf("Unsupported length (%s) for watch-points, defaulting to 4\n", wine_dbgstr_longlong(l)); break; } } else dbg_printf("Cannot get watch size, defaulting to 4\n"); } dbg_curr_process->bp[num].w.len = (DWORD)l - 1; if (!get_watched_value(num, &dbg_curr_process->bp[num].w.oldval)) { dbg_printf("Bad address. Watchpoint not set\n"); dbg_curr_process->bp[num].refcount = 0; return; } dbg_printf("Watchpoint %d at ", num); print_address(&dbg_curr_process->bp[num].addr, TRUE); dbg_printf("\n"); }
/*********************************************************************** * memory_write_value * * Store a value in memory. */ BOOL memory_write_value(const struct dbg_lvalue* lvalue, DWORD size, void* value) { BOOL ret = TRUE; DWORD64 os; os = ~(DWORD64)size; types_get_info(&lvalue->type, TI_GET_LENGTH, &os); assert(size == os); /* FIXME: only works on little endian systems */ if (lvalue->cookie == DLV_TARGET) { void* linear = memory_to_linear_addr(&lvalue->addr); if (!(ret = dbg_write_memory(linear, value, size))) memory_report_invalid_addr(linear); } else { memcpy((void*)(DWORD_PTR)lvalue->addr.Offset, value, size); } return ret; }
/****************************************************************** * types_extract_as_longlong * * Given a lvalue, try to get an integral (or pointer/address) value * out of it */ LONGLONG types_extract_as_longlong(const struct dbg_lvalue* lvalue, unsigned* psize, BOOL *issigned) { LONGLONG rtn; DWORD tag, bt; DWORD64 size; struct dbg_type type = lvalue->type; BOOL s = FALSE; if (!types_get_real_type(&type, &tag)) RaiseException(DEBUG_STATUS_NOT_AN_INTEGER, 0, 0, NULL); if (type.id == dbg_itype_segptr) { return (LONG_PTR)memory_to_linear_addr(&lvalue->addr); } if (psize) *psize = 0; if (issigned) *issigned = FALSE; switch (tag) { case SymTagBaseType: if (!types_get_info(&type, TI_GET_LENGTH, &size) || !types_get_info(&type, TI_GET_BASETYPE, &bt)) { WINE_ERR("Couldn't get information\n"); RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); } if (size > sizeof(rtn)) { WINE_ERR("Size too large (%s)\n", wine_dbgstr_longlong(size)); RaiseException(DEBUG_STATUS_NOT_AN_INTEGER, 0, 0, NULL); } switch (bt) { case btChar: case btInt: if (!be_cpu->fetch_integer(lvalue, (unsigned)size, s = TRUE, &rtn)) RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); break; case btUInt: if (!be_cpu->fetch_integer(lvalue, (unsigned)size, s = FALSE, &rtn)) RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); break; case btFloat: RaiseException(DEBUG_STATUS_NOT_AN_INTEGER, 0, 0, NULL); } if (psize) *psize = (unsigned)size; if (issigned) *issigned = s; break; case SymTagPointerType: if (!be_cpu->fetch_integer(lvalue, sizeof(void*), s = FALSE, &rtn)) RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); break; case SymTagArrayType: case SymTagUDT: if (!be_cpu->fetch_integer(lvalue, sizeof(unsigned), s = FALSE, &rtn)) RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); break; case SymTagEnum: /* FIXME: we don't handle enum size */ if (!be_cpu->fetch_integer(lvalue, sizeof(unsigned), s = FALSE, &rtn)) RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); break; case SymTagFunctionType: rtn = (ULONG_PTR)memory_to_linear_addr(&lvalue->addr); break; default: WINE_FIXME("Unsupported tag %u\n", tag); RaiseException(DEBUG_STATUS_NOT_AN_INTEGER, 0, 0, NULL); break; } return rtn; }
static void print_typed_basic(const struct dbg_lvalue* lvalue) { LONGLONG val_int; void* val_ptr; long double val_real; DWORD64 size64; DWORD tag, size, count, bt; struct dbg_type type = lvalue->type; struct dbg_type sub_type; if (!types_get_real_type(&type, &tag)) return; switch (tag) { case SymTagBaseType: if (!types_get_info(&type, TI_GET_LENGTH, &size64) || !types_get_info(&type, TI_GET_BASETYPE, &bt)) { WINE_ERR("Couldn't get information\n"); RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); } size = (DWORD)size64; switch (bt) { case btInt: if (!be_cpu->fetch_integer(lvalue, size, TRUE, &val_int)) return; dbg_printf("%lld", val_int); break; case btUInt: if (!be_cpu->fetch_integer(lvalue, size, FALSE, &val_int)) return; dbg_printf("%llu", val_int); break; case btFloat: if (!be_cpu->fetch_float(lvalue, size, &val_real)) return; dbg_printf("%Lf", val_real); break; case btChar: if (!be_cpu->fetch_integer(lvalue, size, TRUE, &val_int)) return; /* FIXME: should do the same for a Unicode character (size == 2) */ if (size == 1 && (val_int < 0x20 || val_int > 0x80)) dbg_printf("%d", (int)val_int); else dbg_printf("'%c'", (char)val_int); break; default: WINE_FIXME("Unsupported basetype %lu\n", bt); break; } break; case SymTagPointerType: if (!memory_read_value(lvalue, sizeof(void*), &val_ptr)) return; sub_type.module = lvalue->type.module; if (!types_get_info(&type, TI_GET_TYPE, &sub_type.id) || sub_type.id == dbg_itype_none) { dbg_printf("Internal symbol error: unable to access memory location %p", val_ptr); break; } if (!types_get_real_type(&sub_type, &tag)) return; if (types_get_info(&sub_type, TI_GET_SYMTAG, &tag) && tag == SymTagBaseType && types_get_info(&sub_type, TI_GET_BASETYPE, &bt) && bt == btChar && types_get_info(&sub_type, TI_GET_LENGTH, &size64)) { char buffer[1024]; if (!val_ptr) dbg_printf("0x0"); else if (memory_get_string(dbg_curr_process, val_ptr, lvalue->cookie == DLV_TARGET, size64 == 2, buffer, sizeof(buffer))) dbg_printf("\"%s\"", buffer); else dbg_printf("*** invalid address %p ***", val_ptr); } else dbg_printf("%p", val_ptr); break; case SymTagArrayType: case SymTagUDT: assert(lvalue->cookie == DLV_TARGET); if (!memory_read_value(lvalue, sizeof(val_ptr), &val_ptr)) return; dbg_printf("%p", val_ptr); break; case SymTagEnum: { BOOL ok = FALSE; assert(lvalue->cookie == DLV_TARGET); /* FIXME: it depends on underlying type for enums * (not supported yet in dbghelp) * Assuming 4 as for an int */ if (!be_cpu->fetch_integer(lvalue, 4, TRUE, &val_int)) return; if (types_get_info(&type, TI_GET_CHILDRENCOUNT, &count)) { char buffer[sizeof(TI_FINDCHILDREN_PARAMS) + 256 * sizeof(DWORD)]; TI_FINDCHILDREN_PARAMS* fcp = (TI_FINDCHILDREN_PARAMS*)buffer; WCHAR* ptr; char tmp[256]; VARIANT variant; int i; fcp->Start = 0; while (count) { fcp->Count = min(count, 256); if (types_get_info(&type, TI_FINDCHILDREN, fcp)) { sub_type.module = type.module; for (i = 0; i < min(fcp->Count, count); i++) { sub_type.id = fcp->ChildId[i]; if (!types_get_info(&sub_type, TI_GET_VALUE, &variant)) continue; switch (variant.n1.n2.vt) { case VT_I4: ok = (val_int == variant.n1.n2.n3.lVal); break; default: WINE_FIXME("Unsupported variant type (%u)\n", variant.n1.n2.vt); } if (ok) { ptr = NULL; types_get_info(&sub_type, TI_GET_SYMNAME, &ptr); if (!ptr) continue; WideCharToMultiByte(CP_ACP, 0, ptr, -1, tmp, sizeof(tmp), NULL, NULL); HeapFree(GetProcessHeap(), 0, ptr); dbg_printf("%s", tmp); count = 0; /* so that we'll get away from outter loop */ break; } } } } count -= min(count, 256); fcp->Start += 256; } if (!ok) dbg_printf("%lld", val_int); } break; default: WINE_FIXME("Unsupported tag %lu\n", tag); break; } }
static BOOL fill_sym_lvalue(const SYMBOL_INFO* sym, ULONG base, struct dbg_lvalue* lvalue, char* buffer, size_t sz) { if (buffer) buffer[0] = '\0'; if (sym->Flags & SYMFLAG_REGISTER) { DWORD* pval; if (!memory_get_register(sym->Register, &pval, buffer, sz)) return FALSE; lvalue->cookie = DLV_HOST; lvalue->addr.Offset = (DWORD_PTR)pval; } else if (sym->Flags & SYMFLAG_REGREL) { DWORD* pval; if (!memory_get_register(sym->Register, &pval, buffer, sz)) return FALSE; lvalue->cookie = DLV_TARGET; lvalue->addr.Offset = (ULONG)((ULONG64)*pval + sym->Address); } else if (sym->Flags & SYMFLAG_VALUEPRESENT) { struct dbg_type type; VARIANT v; DWORD* pdw; type.module = sym->ModBase; type.id = sym->info; /* FIXME: this won't work for pointers, as we always for the * dereference to be in debuggee address space while here * it's in debugger address space */ if (!types_get_info(&type, TI_GET_VALUE, &v) || (v.n1.n2.vt & VT_BYREF)) { snprintf(buffer, sz, "Couldn't dereference pointer for const value"); return FALSE; } pdw = (DWORD*)lexeme_alloc_size(sizeof(*pdw)); lvalue->cookie = DLV_HOST; lvalue->addr.Offset = (ULONG)(DWORD_PTR)pdw; *pdw = sym->Value; } else if (sym->Flags & SYMFLAG_LOCAL) { lvalue->cookie = DLV_TARGET; lvalue->addr.Offset = base + sym->Address; } else { lvalue->cookie = DLV_TARGET; lvalue->addr.Offset = sym->Address; } lvalue->addr.Mode = AddrModeFlat; lvalue->type.module = sym->ModBase; lvalue->type.id = sym->TypeIndex; return TRUE; }
static BOOL fill_sym_lvalue(const SYMBOL_INFO* sym, ULONG_PTR base, struct dbg_lvalue* lvalue, char* buffer, size_t sz) { if (buffer) buffer[0] = '\0'; if (sym->Flags & SYMFLAG_REGISTER) { DWORD_PTR* pval; if (!memory_get_register(sym->Register, &pval, buffer, sz)) return FALSE; lvalue->cookie = DLV_HOST; lvalue->addr.Offset = (DWORD_PTR)pval; } else if (sym->Flags & SYMFLAG_REGREL) { DWORD_PTR* pval; size_t l; *buffer++ = '['; sz--; if (!memory_get_register(sym->Register, &pval, buffer, sz)) return FALSE; l = strlen(buffer); sz -= l; buffer += l; lvalue->cookie = DLV_TARGET; lvalue->addr.Offset = (ULONG64)*pval + sym->Address; if ((LONG_PTR)sym->Address >= 0) snprintf(buffer, sz, "+%ld]", (ULONG_PTR)sym->Address); else snprintf(buffer, sz, "-%ld]", -(LONG_PTR)sym->Address); } else if (sym->Flags & SYMFLAG_VALUEPRESENT) { struct dbg_type type; VARIANT v; type.module = sym->ModBase; type.id = sym->info; if (!types_get_info(&type, TI_GET_VALUE, &v)) { if (buffer) snprintf(buffer, sz, "Couldn't get full value information for %s", sym->Name); return FALSE; } else if (v.n1.n2.vt & VT_BYREF) { /* FIXME: this won't work for pointers or arrays, as we don't always * know, if the value to be dereferenced lies in debuggee or * debugger address space. */ if (sym->Tag == SymTagPointerType || sym->Tag == SymTagArrayType) { if (buffer) snprintf(buffer, sz, "Couldn't dereference pointer for const value for %s", sym->Name); return FALSE; } /* this is likely Wine's dbghelp which passes const values by reference * (object is managed by dbghelp, hence in debugger address space) */ lvalue->cookie = DLV_HOST; lvalue->addr.Offset = (DWORD_PTR)sym->Value; } else { DWORD* pdw = (DWORD*)lexeme_alloc_size(sizeof(*pdw)); lvalue->cookie = DLV_HOST; lvalue->addr.Offset = (DWORD_PTR)pdw; *pdw = sym->Value; } } else if (sym->Flags & SYMFLAG_LOCAL) { lvalue->cookie = DLV_TARGET; lvalue->addr.Offset = base + sym->Address; } else if (sym->Flags & SYMFLAG_TLSREL) { PROCESS_BASIC_INFORMATION pbi; THREAD_BASIC_INFORMATION tbi; DWORD_PTR addr; PEB peb; PEB_LDR_DATA ldr_data; PLIST_ENTRY head, current; LDR_MODULE ldr_module; unsigned tlsindex = -1; if (NtQueryInformationProcess(dbg_curr_process->handle, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) || NtQueryInformationThread(dbg_curr_thread->handle, ThreadBasicInformation, &tbi, sizeof(tbi), NULL)) { tls_error: if (buffer) snprintf(buffer, sz, "Cannot read TLS address\n"); return FALSE; } addr = (DWORD_PTR)&(((TEB*)tbi.TebBaseAddress)->ThreadLocalStoragePointer); if (!dbg_read_memory((void*)addr, &addr, sizeof(addr)) || !dbg_read_memory(pbi.PebBaseAddress, &peb, sizeof(peb)) || !dbg_read_memory(peb.LdrData, &ldr_data, sizeof(ldr_data))) goto tls_error; current = ldr_data.InLoadOrderModuleList.Flink; head = &((PEB_LDR_DATA*)peb.LdrData)->InLoadOrderModuleList; do { if (!dbg_read_memory(CONTAINING_RECORD(current, LDR_MODULE, InLoadOrderModuleList), &ldr_module, sizeof(ldr_module))) goto tls_error; if ((DWORD_PTR)ldr_module.BaseAddress == sym->ModBase) { tlsindex = ldr_module.TlsIndex; break; } current = ldr_module.InLoadOrderModuleList.Flink; } while (current != head); addr += tlsindex * sizeof(DWORD_PTR); if (!dbg_read_memory((void*)addr, &addr, sizeof(addr))) goto tls_error; lvalue->cookie = DLV_TARGET; lvalue->addr.Offset = addr + sym->Address; } else { lvalue->cookie = DLV_TARGET; lvalue->addr.Offset = sym->Address; } lvalue->addr.Mode = AddrModeFlat; lvalue->type.module = sym->ModBase; lvalue->type.id = sym->TypeIndex; return TRUE; }
/****************************************************************** * types_extract_as_integer * * Given a lvalue, try to get an integral (or pointer/address) value * out of it */ long int types_extract_as_integer(const struct dbg_lvalue* lvalue) { long int rtn; LONGLONG val; DWORD tag, bt; DWORD64 size; struct dbg_type type = lvalue->type; if (!types_get_real_type(&type, &tag)) RaiseException(DEBUG_STATUS_NOT_AN_INTEGER, 0, 0, NULL); if (type.id == dbg_itype_segptr) { return (long int)memory_to_linear_addr(&lvalue->addr); } switch (tag) { case SymTagBaseType: if (!types_get_info(&type, TI_GET_LENGTH, &size) || !types_get_info(&type, TI_GET_BASETYPE, &bt)) { WINE_ERR("Couldn't get information\n"); RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); } if (size > sizeof(rtn)) { WINE_ERR("Size too large (%s)\n", wine_dbgstr_longlong(size)); RaiseException(DEBUG_STATUS_NOT_AN_INTEGER, 0, 0, NULL); } switch (bt) { case btChar: case btInt: if (!be_cpu->fetch_integer(lvalue, (unsigned)size, TRUE, &val)) RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); rtn = (long)val; break; case btUInt: if (!be_cpu->fetch_integer(lvalue, (unsigned)size, FALSE, &val)) RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); rtn = (DWORD)(DWORD64)val; break; case btFloat: RaiseException(DEBUG_STATUS_NOT_AN_INTEGER, 0, 0, NULL); } break; case SymTagPointerType: if (!memory_read_value(lvalue, sizeof(void*), &rtn)) RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); break; case SymTagArrayType: case SymTagUDT: assert(lvalue->cookie == DLV_TARGET); if (!memory_read_value(lvalue, sizeof(rtn), &rtn)) RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); break; case SymTagEnum: assert(lvalue->cookie == DLV_TARGET); if (!memory_read_value(lvalue, sizeof(rtn), &rtn)) RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); break; case SymTagFunctionType: rtn = (unsigned)memory_to_linear_addr(&lvalue->addr); break; default: WINE_FIXME("Unsupported tag %lu\n", tag); RaiseException(DEBUG_STATUS_NOT_AN_INTEGER, 0, 0, NULL); break; } return rtn; }