- all symbol information storage is now module relative, so we can
unload a module (and it's debugging information), and a process without pain - portabiblity to another CPU should be easier now (CPU dependent backend) - speed up memory allocation - stabs related fixes: + now correctly handling symbol's size + blocks {} in functions are now correctly recognized and stored (also applies to local variables scoping) + better basic types management (less wild guesses in the code) + full support of inline functions (source stepping now shows the code in .h files for example) - removal of external debugger (attaching with gdb is just fine to debug winedbg) - fixed a couple of issues for symbol address handling (address lookup, incorrect type binding) - winedbg now has a man page
This commit is contained in:
parent
9f33a4b109
commit
deca2502d6
File diff suppressed because it is too large
Load Diff
|
@ -220,14 +220,22 @@ HANDLE32 WINAPI YourFunc(LPCSTR s)
|
|||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="dbg-control">
|
||||
<title>Controlling the debugging output</title>
|
||||
<sect1 id="dbg-control">
|
||||
<title>Controlling the debugging output</title>
|
||||
|
||||
<para>
|
||||
It is possible to turn on and off debugging output from
|
||||
within the debugger using the set command. Please see the
|
||||
WineDbg Command Reference section for how to do this.
|
||||
</para>
|
||||
<para>
|
||||
It is possible to turn on and off debugging output from
|
||||
within the debugger using the set command. Please see the
|
||||
WineDbg Command Reference section
|
||||
(<xref linkend="winedbg-dbg-chan">) for how to do this.
|
||||
</para>
|
||||
<para>
|
||||
You can do the same using the task manager
|
||||
(<command>taskmgr</command>) and selecting your application in
|
||||
the application list. Right clicking on the application, and
|
||||
selecting the debug option in the popup menu, will let you
|
||||
select the modifications you want on the debug channels.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Another way to conditionally log debug output (e.g. in case of
|
||||
|
|
|
@ -4,26 +4,21 @@ SRCDIR = @srcdir@
|
|||
VPATH = @srcdir@
|
||||
MODULE = winedbg.exe
|
||||
APPMODE = -mconsole
|
||||
IMPORTS = advapi32 kernel32 ntdll
|
||||
IMPORTS = dbghelp advapi32 kernel32 ntdll
|
||||
DELAYIMPORTS = user32
|
||||
|
||||
C_SRCS = \
|
||||
be_i386.c \
|
||||
be_ppc.c \
|
||||
break.c \
|
||||
db_disasm.c \
|
||||
display.c \
|
||||
elf.c \
|
||||
expr.c \
|
||||
ext_debugger.c \
|
||||
gdbproxy.c \
|
||||
hash.c \
|
||||
info.c \
|
||||
memory.c \
|
||||
module.c \
|
||||
msc.c \
|
||||
pe.c \
|
||||
registers.c \
|
||||
source.c \
|
||||
stabs.c \
|
||||
symbol.c \
|
||||
stack.c \
|
||||
types.c \
|
||||
winedbg.c
|
||||
|
@ -45,4 +40,11 @@ y.tab.o: y.tab.h
|
|||
|
||||
@LEX_OUTPUT_ROOT@.o: y.tab.h
|
||||
|
||||
install::
|
||||
$(MKINSTALLDIRS) $(mandir)/man$(prog_manext)
|
||||
$(INSTALL_DATA) $(SRCDIR)/winedbg.man $(mandir)/man$(prog_manext)/winedbg.$(prog_manext)
|
||||
|
||||
uninstall::
|
||||
$(RM) $(mandir)/man$(prog_manext)/winedbg.$(prog_manext)
|
||||
|
||||
### Dependencies:
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Debugger CPU backend definitions
|
||||
*
|
||||
* Copyright 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
|
||||
*/
|
||||
|
||||
enum be_cpu_addr {be_cpu_addr_pc, be_cpu_addr_stack, be_cpu_addr_frame};
|
||||
enum be_xpoint_type {be_xpoint_break, be_xpoint_watch_exec, be_xpoint_watch_read,
|
||||
be_xpoint_watch_write};
|
||||
struct backend_cpu
|
||||
{
|
||||
/* ------------------------------------------------------------------------------
|
||||
* address manipulation
|
||||
* ------------------------------------------------------------------------------ */
|
||||
/* Linearizes an address. Only CPUs with segmented address model need this.
|
||||
* Otherwise, implementation is straigthforward (be_cpu_linearize will do)
|
||||
*/
|
||||
void* (*linearize)(HANDLE hThread, const ADDRESS*);
|
||||
/* Fills in an ADDRESS structure from a segment & an offset. CPUs without
|
||||
* segment address model should use 0 as seg. Required method to fill
|
||||
* in an ADDRESS (except an linear one).
|
||||
* Non segmented CPU shall use be_cpu_build_addr
|
||||
*/
|
||||
unsigned (*build_addr)(HANDLE hThread, const CONTEXT* ctx,
|
||||
ADDRESS* addr, unsigned seg,
|
||||
unsigned long offset);
|
||||
/* Retrieves in addr an address related to the context (program counter, stack
|
||||
* pointer, frame pointer)
|
||||
*/
|
||||
unsigned (*get_addr)(HANDLE hThread, const CONTEXT* ctx,
|
||||
enum be_cpu_addr, ADDRESS* addr);
|
||||
/* -------------------------------------------------------------------------------
|
||||
* context manipulation
|
||||
* ------------------------------------------------------------------------------- */
|
||||
/* Enables/disables CPU single step mode (depending on enable) */
|
||||
void (*single_step)(CONTEXT* ctx, unsigned enable);
|
||||
/* Dumps out the content of the context */
|
||||
void (*print_context)(HANDLE hThread, const CONTEXT* ctx);
|
||||
/* Prints information about segments. Non segmented CPU should leave this
|
||||
* function empty
|
||||
*/
|
||||
void (*print_segment_info)(HANDLE hThread, const CONTEXT* ctx);
|
||||
/* Do the initialization so that the debugger has internal variables linked
|
||||
* to the context's registers
|
||||
*/
|
||||
const struct dbg_internal_var*
|
||||
(*init_registers)(CONTEXT* ctx);
|
||||
/* -------------------------------------------------------------------------------
|
||||
* code inspection
|
||||
* -------------------------------------------------------------------------------*/
|
||||
/* Check whether the instruction at addr is an insn to step over
|
||||
* (like function call, interruption...)
|
||||
*/
|
||||
unsigned (*is_step_over_insn)(const void* addr);
|
||||
/* Check whether instruction at 'addr' is the return from a function call */
|
||||
unsigned (*is_function_return)(const void* addr);
|
||||
/* Check whether instruction at 'addr' is the CPU break instruction. On i386,
|
||||
* it's INT3 (0xCC)
|
||||
*/
|
||||
unsigned (*is_break_insn)(const void*);
|
||||
/* Check whether instruciton at 'addr' is a function call */
|
||||
unsigned (*is_function_call)(const void* insn, ADDRESS* callee);
|
||||
/* Ask for dissasembling one instruction. If display is true, assembly code
|
||||
* will be printed. In all cases, 'addr' is advanced at next instruction
|
||||
*/
|
||||
void (*disasm_one_insn)(ADDRESS* addr, int display);
|
||||
/* -------------------------------------------------------------------------------
|
||||
* break points / watchpoints handling
|
||||
* -------------------------------------------------------------------------------*/
|
||||
/* Inserts an Xpoint in the CPU context and/or debuggee address space */
|
||||
unsigned (*insert_Xpoint)(HANDLE hProcess, CONTEXT* ctx,
|
||||
enum be_xpoint_type type, void* addr,
|
||||
unsigned long* val, unsigned size);
|
||||
/* Removes an Xpoint in the CPU context and/or debuggee address space */
|
||||
unsigned (*remove_Xpoint)(HANDLE hProcess, CONTEXT* ctx,
|
||||
enum be_xpoint_type type, void* addr,
|
||||
unsigned long val, unsigned size);
|
||||
/* Checks whether a given watchpoint has been triggered */
|
||||
unsigned (*is_watchpoint_set)(const CONTEXT* ctx, unsigned idx);
|
||||
/* Clears the watchpoint indicator */
|
||||
void (*clear_watchpoint)(CONTEXT* ctx, unsigned idx);
|
||||
/* After a break instruction is executed, in the corresponding exception handler,
|
||||
* some CPUs report the address of the insn after the break insn, some others
|
||||
* report the address of the break insn itself.
|
||||
* This function lets adjust the context PC to reflect this behavior.
|
||||
*/
|
||||
int (*adjust_pc_for_break)(CONTEXT* ctx, BOOL way);
|
||||
/* -------------------------------------------------------------------------------
|
||||
* basic type read/write
|
||||
* -------------------------------------------------------------------------------*/
|
||||
/* Reads an integer from memory and stores it inside a long long int */
|
||||
int (*fetch_integer)(const struct dbg_lvalue* lvalue, unsigned size, unsigned is_signed, long long int*);
|
||||
/* Reads a real from memory and stores it inside a long double */
|
||||
int (*fetch_float)(const struct dbg_lvalue* lvalue, unsigned size, long double*);
|
||||
};
|
||||
|
||||
extern struct backend_cpu* be_cpu;
|
||||
|
||||
/* some handy functions for non segmented CPUs */
|
||||
void* be_cpu_linearize(HANDLE hThread, const ADDRESS*);
|
||||
unsigned be_cpu_build_addr(HANDLE hThread, const CONTEXT* ctx, ADDRESS* addr,
|
||||
unsigned seg, unsigned long offset);
|
|
@ -0,0 +1,549 @@
|
|||
/*
|
||||
* Debugger i386 specific functions
|
||||
*
|
||||
* Copyright 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
|
||||
*/
|
||||
|
||||
#include "debugger.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
/* debugger/db_disasm.c */
|
||||
extern void be_i386_disasm_one_insn(ADDRESS* addr, int display);
|
||||
|
||||
#define STEP_FLAG 0x00000100 /* single step flag */
|
||||
#define V86_FLAG 0x00020000
|
||||
|
||||
#define IS_VM86_MODE(ctx) (ctx->EFlags & V86_FLAG)
|
||||
|
||||
static ADDRESS_MODE get_selector_type(HANDLE hThread, const CONTEXT* ctx, WORD sel)
|
||||
{
|
||||
LDT_ENTRY le;
|
||||
|
||||
if (IS_VM86_MODE(ctx)) return AddrModeReal;
|
||||
/* null or system selector */
|
||||
if (!(sel & 4) || ((sel >> 3) < 17)) return AddrModeFlat;
|
||||
if (GetThreadSelectorEntry(hThread, sel, &le))
|
||||
return le.HighWord.Bits.Default_Big ? AddrMode1632 : AddrMode1616;
|
||||
/* selector doesn't exist */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void* be_i386_linearize(HANDLE hThread, const ADDRESS* addr)
|
||||
{
|
||||
LDT_ENTRY le;
|
||||
|
||||
switch (addr->Mode)
|
||||
{
|
||||
case AddrModeReal:
|
||||
return (void*)((DWORD)(LOWORD(addr->Segment) << 4) + addr->Offset);
|
||||
case AddrMode1632:
|
||||
if (!(addr->Segment & 4) || ((addr->Segment >> 3) < 17))
|
||||
return (void*)addr->Offset;
|
||||
/* fall through */
|
||||
case AddrMode1616:
|
||||
if (!GetThreadSelectorEntry(hThread, addr->Segment, &le)) return NULL;
|
||||
return (void*)((le.HighWord.Bits.BaseHi << 24) +
|
||||
(le.HighWord.Bits.BaseMid << 16) + le.BaseLow + addr->Offset);
|
||||
break;
|
||||
case AddrModeFlat:
|
||||
return (void*)addr->Offset;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned be_i386_build_addr(HANDLE hThread, const CONTEXT* ctx, ADDRESS* addr,
|
||||
unsigned seg, unsigned long offset)
|
||||
{
|
||||
addr->Mode = AddrModeFlat;
|
||||
addr->Segment = seg;
|
||||
addr->Offset = offset;
|
||||
if (seg)
|
||||
{
|
||||
addr->Mode = get_selector_type(hThread, ctx, seg);
|
||||
switch (addr->Mode)
|
||||
{
|
||||
case AddrModeReal:
|
||||
case AddrMode1616:
|
||||
addr->Offset &= 0xffff;
|
||||
break;
|
||||
case AddrModeFlat:
|
||||
case AddrMode1632:
|
||||
break;
|
||||
default:
|
||||
addr->Mode = -1;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static unsigned be_i386_get_addr(HANDLE hThread, const CONTEXT* ctx,
|
||||
enum be_cpu_addr bca, ADDRESS* addr)
|
||||
{
|
||||
switch (bca)
|
||||
{
|
||||
case be_cpu_addr_pc:
|
||||
return be_i386_build_addr(hThread, ctx, addr, ctx->SegCs, ctx->Eip);
|
||||
case be_cpu_addr_stack:
|
||||
return be_i386_build_addr(hThread, ctx, addr, ctx->SegSs, ctx->Esp);
|
||||
case be_cpu_addr_frame:
|
||||
return be_i386_build_addr(hThread, ctx, addr, ctx->SegSs, ctx->Ebp);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void be_i386_single_step(CONTEXT* ctx, unsigned enable)
|
||||
{
|
||||
if (enable) ctx->EFlags |= STEP_FLAG;
|
||||
else ctx->EFlags &= ~STEP_FLAG;
|
||||
}
|
||||
|
||||
static void be_i386_print_context(HANDLE hThread, const CONTEXT* ctx)
|
||||
{
|
||||
char buf[33];
|
||||
char* pt;
|
||||
|
||||
dbg_printf("Register dump:\n");
|
||||
|
||||
/* First get the segment registers out of the way */
|
||||
dbg_printf(" CS:%04x SS:%04x DS:%04x ES:%04x FS:%04x GS:%04x",
|
||||
(WORD)ctx->SegCs, (WORD)ctx->SegSs,
|
||||
(WORD)ctx->SegDs, (WORD)ctx->SegEs,
|
||||
(WORD)ctx->SegFs, (WORD)ctx->SegGs);
|
||||
|
||||
strcpy(buf, " - 00 - - - ");
|
||||
pt = buf + strlen(buf) - 1;
|
||||
if (ctx->EFlags & 0x00000001) *pt-- = 'C'; /* Carry Flag */
|
||||
if (ctx->EFlags & 0x00000002) *pt-- = '1';
|
||||
if (ctx->EFlags & 0x00000004) *pt-- = 'P'; /* Parity Flag */
|
||||
if (ctx->EFlags & 0x00000008) *pt-- = '-';
|
||||
if (ctx->EFlags & 0x00000010) *pt-- = 'A'; /* Auxiliary Carry Flag */
|
||||
if (ctx->EFlags & 0x00000020) *pt-- = '-';
|
||||
if (ctx->EFlags & 0x00000040) *pt-- = 'Z'; /* Zero Flag */
|
||||
if (ctx->EFlags & 0x00000080) *pt-- = 'S'; /* Sign Flag */
|
||||
if (ctx->EFlags & 0x00000100) *pt-- = 'T'; /* Trap/Trace Flag */
|
||||
if (ctx->EFlags & 0x00000200) *pt-- = 'I'; /* Interupt Enable Flag */
|
||||
if (ctx->EFlags & 0x00000400) *pt-- = 'D'; /* Direction Indicator */
|
||||
if (ctx->EFlags & 0x00000800) *pt-- = 'O'; /* Overflow flags */
|
||||
if (ctx->EFlags & 0x00001000) *pt-- = '1'; /* I/O Privilege Level */
|
||||
if (ctx->EFlags & 0x00002000) *pt-- = '1'; /* I/O Privilege Level */
|
||||
if (ctx->EFlags & 0x00004000) *pt-- = 'N'; /* Nested Task Flag */
|
||||
if (ctx->EFlags & 0x00008000) *pt-- = '-';
|
||||
if (ctx->EFlags & 0x00010000) *pt-- = 'R'; /* Resume Flag */
|
||||
if (ctx->EFlags & 0x00020000) *pt-- = 'V'; /* Vritual Mode Flag */
|
||||
if (ctx->EFlags & 0x00040000) *pt-- = 'a'; /* Alignment Check Flag */
|
||||
|
||||
switch (get_selector_type(hThread, ctx, ctx->SegCs))
|
||||
{
|
||||
case AddrMode1616:
|
||||
case AddrModeReal:
|
||||
dbg_printf("\n IP:%04x SP:%04x BP:%04x FLAGS:%04x(%s)\n",
|
||||
LOWORD(ctx->Eip), LOWORD(ctx->Esp),
|
||||
LOWORD(ctx->Ebp), LOWORD(ctx->EFlags), buf);
|
||||
dbg_printf(" AX:%04x BX:%04x CX:%04x DX:%04x SI:%04x DI:%04x\n",
|
||||
LOWORD(ctx->Eax), LOWORD(ctx->Ebx),
|
||||
LOWORD(ctx->Ecx), LOWORD(ctx->Edx),
|
||||
LOWORD(ctx->Esi), LOWORD(ctx->Edi));
|
||||
break;
|
||||
case AddrModeFlat:
|
||||
case AddrMode1632:
|
||||
dbg_printf("\n EIP:%08lx ESP:%08lx EBP:%08lx EFLAGS:%08lx(%s)\n",
|
||||
ctx->Eip, ctx->Esp, ctx->Ebp, ctx->EFlags, buf);
|
||||
dbg_printf(" EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n",
|
||||
ctx->Eax, ctx->Ebx, ctx->Ecx, ctx->Edx);
|
||||
dbg_printf(" ESI:%08lx EDI:%08lx\n",
|
||||
ctx->Esi, ctx->Edi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void be_i386_print_segment_info(HANDLE hThread, const CONTEXT* ctx)
|
||||
{
|
||||
if (get_selector_type(hThread, ctx, ctx->SegCs) == AddrMode1616)
|
||||
{
|
||||
info_win32_segments(ctx->SegDs >> 3, 1);
|
||||
if (ctx->SegEs != ctx->SegDs) info_win32_segments(ctx->SegEs >> 3, 1);
|
||||
}
|
||||
info_win32_segments(ctx->SegFs >> 3, 1);
|
||||
}
|
||||
|
||||
static struct dbg_internal_var be_i386_ctx[] =
|
||||
{
|
||||
{CV_REG_AL, "AL", (DWORD*)FIELD_OFFSET(CONTEXT, Eax), dbg_itype_unsigned_char_int},
|
||||
{CV_REG_CL, "CL", (DWORD*)FIELD_OFFSET(CONTEXT, Ecx), dbg_itype_unsigned_char_int},
|
||||
{CV_REG_DL, "DL", (DWORD*)FIELD_OFFSET(CONTEXT, Edx), dbg_itype_unsigned_char_int},
|
||||
{CV_REG_BL, "BL", (DWORD*)FIELD_OFFSET(CONTEXT, Ebx), dbg_itype_unsigned_char_int},
|
||||
{CV_REG_AH, "AH", (DWORD*)(FIELD_OFFSET(CONTEXT, Eax)+1), dbg_itype_unsigned_char_int},
|
||||
{CV_REG_CH, "CH", (DWORD*)(FIELD_OFFSET(CONTEXT, Ecx)+1), dbg_itype_unsigned_char_int},
|
||||
{CV_REG_DH, "DH", (DWORD*)(FIELD_OFFSET(CONTEXT, Edx)+1), dbg_itype_unsigned_char_int},
|
||||
{CV_REG_BH, "BH", (DWORD*)(FIELD_OFFSET(CONTEXT, Ebx)+1), dbg_itype_unsigned_char_int},
|
||||
{CV_REG_AX, "AX", (DWORD*)FIELD_OFFSET(CONTEXT, Eax), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_CX, "CX", (DWORD*)FIELD_OFFSET(CONTEXT, Ecx), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_DX, "DX", (DWORD*)FIELD_OFFSET(CONTEXT, Edx), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_BX, "BX", (DWORD*)FIELD_OFFSET(CONTEXT, Ebx), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_SP, "SP", (DWORD*)FIELD_OFFSET(CONTEXT, Esp), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_BP, "BP", (DWORD*)FIELD_OFFSET(CONTEXT, Ebp), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_SI, "SI", (DWORD*)FIELD_OFFSET(CONTEXT, Esi), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_DI, "DI", (DWORD*)FIELD_OFFSET(CONTEXT, Edi), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_EAX, "EAX", (DWORD*)FIELD_OFFSET(CONTEXT, Eax), dbg_itype_unsigned_int},
|
||||
{CV_REG_ECX, "ECX", (DWORD*)FIELD_OFFSET(CONTEXT, Ecx), dbg_itype_unsigned_int},
|
||||
{CV_REG_EDX, "EDX", (DWORD*)FIELD_OFFSET(CONTEXT, Edx), dbg_itype_unsigned_int},
|
||||
{CV_REG_EBX, "EBX", (DWORD*)FIELD_OFFSET(CONTEXT, Ebx), dbg_itype_unsigned_int},
|
||||
{CV_REG_ESP, "ESP", (DWORD*)FIELD_OFFSET(CONTEXT, Esp), dbg_itype_unsigned_int},
|
||||
{CV_REG_EBP, "EBP", (DWORD*)FIELD_OFFSET(CONTEXT, Ebp), dbg_itype_unsigned_int},
|
||||
{CV_REG_ESI, "ESI", (DWORD*)FIELD_OFFSET(CONTEXT, Esi), dbg_itype_unsigned_int},
|
||||
{CV_REG_EDI, "EDI", (DWORD*)FIELD_OFFSET(CONTEXT, Edi), dbg_itype_unsigned_int},
|
||||
{CV_REG_ES, "ES", (DWORD*)FIELD_OFFSET(CONTEXT, SegEs), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_CS, "CS", (DWORD*)FIELD_OFFSET(CONTEXT, SegCs), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_SS, "SS", (DWORD*)FIELD_OFFSET(CONTEXT, SegSs), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_DS, "DS", (DWORD*)FIELD_OFFSET(CONTEXT, SegDs), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_FS, "FS", (DWORD*)FIELD_OFFSET(CONTEXT, SegFs), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_GS, "GS", (DWORD*)FIELD_OFFSET(CONTEXT, SegGs), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_IP, "IP", (DWORD*)FIELD_OFFSET(CONTEXT, Eip), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_FLAGS, "FLAGS", (DWORD*)FIELD_OFFSET(CONTEXT, EFlags), dbg_itype_unsigned_short_int},
|
||||
{CV_REG_EIP, "EIP", (DWORD*)FIELD_OFFSET(CONTEXT, Eip), dbg_itype_unsigned_int},
|
||||
{CV_REG_EFLAGS, "EFLAGS", (DWORD*)FIELD_OFFSET(CONTEXT, EFlags), dbg_itype_unsigned_int},
|
||||
{0, NULL, 0, dbg_itype_none}
|
||||
};
|
||||
|
||||
static const struct dbg_internal_var* be_i386_init_registers(CONTEXT* ctx)
|
||||
{
|
||||
struct dbg_internal_var* div;
|
||||
|
||||
for (div = be_i386_ctx; div->name; div++)
|
||||
div->pval = (DWORD*)((char*)ctx + (DWORD)div->pval);
|
||||
return be_i386_ctx;
|
||||
}
|
||||
|
||||
static unsigned be_i386_is_step_over_insn(const void* insn)
|
||||
{
|
||||
BYTE ch;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
|
||||
|
||||
switch (ch)
|
||||
{
|
||||
/* Skip all prefixes */
|
||||
case 0x2e: /* cs: */
|
||||
case 0x36: /* ss: */
|
||||
case 0x3e: /* ds: */
|
||||
case 0x26: /* es: */
|
||||
case 0x64: /* fs: */
|
||||
case 0x65: /* gs: */
|
||||
case 0x66: /* opcode size prefix */
|
||||
case 0x67: /* addr size prefix */
|
||||
case 0xf0: /* lock */
|
||||
case 0xf2: /* repne */
|
||||
case 0xf3: /* repe */
|
||||
insn = (const char*)insn + 1;
|
||||
continue;
|
||||
|
||||
/* Handle call instructions */
|
||||
case 0xcd: /* int <intno> */
|
||||
case 0xe8: /* call <offset> */
|
||||
case 0x9a: /* lcall <seg>:<off> */
|
||||
return TRUE;
|
||||
|
||||
case 0xff: /* call <regmodrm> */
|
||||
if (!dbg_read_memory((const char*)insn + 1, &ch, sizeof(ch)))
|
||||
return FALSE;
|
||||
return (((ch & 0x38) == 0x10) || ((ch & 0x38) == 0x18));
|
||||
|
||||
/* Handle string instructions */
|
||||
case 0x6c: /* insb */
|
||||
case 0x6d: /* insw */
|
||||
case 0x6e: /* outsb */
|
||||
case 0x6f: /* outsw */
|
||||
case 0xa4: /* movsb */
|
||||
case 0xa5: /* movsw */
|
||||
case 0xa6: /* cmpsb */
|
||||
case 0xa7: /* cmpsw */
|
||||
case 0xaa: /* stosb */
|
||||
case 0xab: /* stosw */
|
||||
case 0xac: /* lodsb */
|
||||
case 0xad: /* lodsw */
|
||||
case 0xae: /* scasb */
|
||||
case 0xaf: /* scasw */
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned be_i386_is_function_return(const void* insn)
|
||||
{
|
||||
BYTE ch;
|
||||
|
||||
if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
|
||||
return (ch == 0xC2) || (ch == 0xC3);
|
||||
}
|
||||
|
||||
static unsigned be_i386_is_break_insn(const void* insn)
|
||||
{
|
||||
BYTE c;
|
||||
|
||||
if (!dbg_read_memory(insn, &c, 1)) return FALSE;
|
||||
return c == 0xCC;
|
||||
}
|
||||
|
||||
static unsigned be_i386_is_func_call(const void* insn, ADDRESS* callee)
|
||||
{
|
||||
BYTE ch;
|
||||
int delta;
|
||||
|
||||
dbg_read_memory(insn, &ch, sizeof(ch));
|
||||
if (ch == 0xe8)
|
||||
{
|
||||
dbg_read_memory((const char*)insn + 1, &delta, sizeof(delta));
|
||||
|
||||
callee->Mode = AddrModeFlat;
|
||||
callee->Offset = (DWORD)insn;
|
||||
be_i386_disasm_one_insn(callee, FALSE);
|
||||
callee->Offset += delta;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#define DR7_CONTROL_SHIFT 16
|
||||
#define DR7_CONTROL_SIZE 4
|
||||
|
||||
#define DR7_RW_EXECUTE (0x0)
|
||||
#define DR7_RW_WRITE (0x1)
|
||||
#define DR7_RW_READ (0x3)
|
||||
|
||||
#define DR7_LEN_1 (0x0)
|
||||
#define DR7_LEN_2 (0x4)
|
||||
#define DR7_LEN_4 (0xC)
|
||||
|
||||
#define DR7_LOCAL_ENABLE_SHIFT 0
|
||||
#define DR7_GLOBAL_ENABLE_SHIFT 1
|
||||
#define DR7_ENABLE_SIZE 2
|
||||
|
||||
#define DR7_LOCAL_ENABLE_MASK (0x55)
|
||||
#define DR7_GLOBAL_ENABLE_MASK (0xAA)
|
||||
|
||||
#define DR7_CONTROL_RESERVED (0xFC00)
|
||||
#define DR7_LOCAL_SLOWDOWN (0x100)
|
||||
#define DR7_GLOBAL_SLOWDOWN (0x200)
|
||||
|
||||
#define DR7_ENABLE_MASK(dr) (1<<(DR7_LOCAL_ENABLE_SHIFT+DR7_ENABLE_SIZE*(dr)))
|
||||
#define IS_DR7_SET(ctrl,dr) ((ctrl)&DR7_ENABLE_MASK(dr))
|
||||
|
||||
static inline int be_i386_get_unused_DR(CONTEXT* ctx, unsigned long** r)
|
||||
{
|
||||
if (!IS_DR7_SET(ctx->Dr7, 0))
|
||||
{
|
||||
*r = &ctx->Dr0;
|
||||
return 0;
|
||||
}
|
||||
if (!IS_DR7_SET(ctx->Dr7, 1))
|
||||
{
|
||||
*r = &ctx->Dr1;
|
||||
return 1;
|
||||
}
|
||||
if (!IS_DR7_SET(ctx->Dr7, 2))
|
||||
{
|
||||
*r = &ctx->Dr2;
|
||||
return 2;
|
||||
}
|
||||
if (!IS_DR7_SET(ctx->Dr7, 3))
|
||||
{
|
||||
*r = &ctx->Dr3;
|
||||
return 3;
|
||||
}
|
||||
dbg_printf("All hardware registers have been used\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static unsigned be_i386_insert_Xpoint(HANDLE hProcess, CONTEXT* ctx,
|
||||
enum be_xpoint_type type, void* addr,
|
||||
unsigned long* val, unsigned size)
|
||||
{
|
||||
unsigned char ch;
|
||||
unsigned long sz;
|
||||
unsigned long* pr;
|
||||
int reg;
|
||||
unsigned long bits;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case be_xpoint_break:
|
||||
if (size != 0) return 0;
|
||||
if (!ReadProcessMemory(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0;
|
||||
*val = ch;
|
||||
ch = 0xcc;
|
||||
if (!WriteProcessMemory(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0;
|
||||
break;
|
||||
case be_xpoint_watch_exec:
|
||||
bits = DR7_RW_EXECUTE;
|
||||
goto hw_bp;
|
||||
case be_xpoint_watch_read:
|
||||
bits = DR7_RW_READ;
|
||||
goto hw_bp;
|
||||
case be_xpoint_watch_write:
|
||||
bits = DR7_RW_WRITE;
|
||||
hw_bp:
|
||||
if ((reg = be_i386_get_unused_DR(ctx, &pr)) == -1) return 0;
|
||||
*pr = (unsigned long)addr;
|
||||
if (type != be_xpoint_watch_exec) switch (size)
|
||||
{
|
||||
case 4: bits |= DR7_LEN_4; break;
|
||||
case 2: bits |= DR7_LEN_2; break;
|
||||
case 1: bits |= DR7_LEN_1; break;
|
||||
default: return 0;
|
||||
}
|
||||
*val = reg;
|
||||
/* clear old values */
|
||||
ctx->Dr7 &= ~(0x0F << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg));
|
||||
/* set the correct ones */
|
||||
ctx->Dr7 |= bits << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg);
|
||||
ctx->Dr7 |= DR7_ENABLE_MASK(reg) | DR7_LOCAL_SLOWDOWN;
|
||||
break;
|
||||
default:
|
||||
dbg_printf("Unknown bp type %c\n", type);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned be_i386_remove_Xpoint(HANDLE hProcess, CONTEXT* ctx,
|
||||
enum be_xpoint_type type, void* addr,
|
||||
unsigned long val, unsigned size)
|
||||
{
|
||||
unsigned long sz;
|
||||
unsigned char ch;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case be_xpoint_break:
|
||||
if (size != 0) return 0;
|
||||
if (!ReadProcessMemory(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0;
|
||||
if (ch != (unsigned char)0xCC)
|
||||
WINE_FIXME("Cannot get back %02x instead of 0xCC at %08lx\n",
|
||||
ch, (unsigned long)addr);
|
||||
ch = (unsigned char)val;
|
||||
if (!WriteProcessMemory(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0;
|
||||
break;
|
||||
case be_xpoint_watch_exec:
|
||||
case be_xpoint_watch_read:
|
||||
case be_xpoint_watch_write:
|
||||
/* simply disable the entry */
|
||||
ctx->Dr7 &= ~DR7_ENABLE_MASK(val);
|
||||
break;
|
||||
default:
|
||||
dbg_printf("Unknown bp type %c\n", type);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned be_i386_is_watchpoint_set(const CONTEXT* ctx, unsigned idx)
|
||||
{
|
||||
return ctx->Dr6 & (1 << idx);
|
||||
}
|
||||
|
||||
static void be_i386_clear_watchpoint(CONTEXT* ctx, unsigned idx)
|
||||
{
|
||||
ctx->Dr6 &= ~(1 << idx);
|
||||
}
|
||||
|
||||
static int be_i386_adjust_pc_for_break(CONTEXT* ctx, BOOL way)
|
||||
{
|
||||
if (way)
|
||||
{
|
||||
ctx->Eip--;
|
||||
return -1;
|
||||
}
|
||||
ctx->Eip++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int be_i386_fetch_integer(const struct dbg_lvalue* lvalue, unsigned size,
|
||||
unsigned ext_sign, long long int* ret)
|
||||
{
|
||||
if (size != 1 && size != 2 && size != 4 && size != 8) return FALSE;
|
||||
|
||||
memset(ret, 0, sizeof(*ret)); /* clear unread bytes */
|
||||
/* FIXME: this assumes that debuggee and debugger use the same
|
||||
* integral representation
|
||||
*/
|
||||
if (!memory_read_value(lvalue, size, ret)) return FALSE;
|
||||
|
||||
/* propagate sign information */
|
||||
if (ext_sign && size < 8 && (*ret >> (size * 8 - 1)) != 0)
|
||||
{
|
||||
long long unsigned int neg = -1;
|
||||
*ret |= neg << (size * 8);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int be_i386_fetch_float(const struct dbg_lvalue* lvalue, unsigned size,
|
||||
long double* ret)
|
||||
{
|
||||
char tmp[12];
|
||||
|
||||
/* FIXME: this assumes that debuggee and debugger use the same
|
||||
* representation for reals
|
||||
*/
|
||||
if (!memory_read_value(lvalue, size, tmp)) return FALSE;
|
||||
|
||||
/* float & double types have to be promoted to a long double */
|
||||
switch (size)
|
||||
{
|
||||
case sizeof(float): *ret = *(float*)tmp; break;
|
||||
case sizeof(double): *ret = *(double*)tmp; break;
|
||||
case sizeof(long double): *ret = *(long double*)tmp; break;
|
||||
default: return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
struct backend_cpu be_i386 =
|
||||
{
|
||||
be_i386_linearize,
|
||||
be_i386_build_addr,
|
||||
be_i386_get_addr,
|
||||
be_i386_single_step,
|
||||
be_i386_print_context,
|
||||
be_i386_print_segment_info,
|
||||
be_i386_init_registers,
|
||||
be_i386_is_step_over_insn,
|
||||
be_i386_is_function_return,
|
||||
be_i386_is_break_insn,
|
||||
be_i386_is_func_call,
|
||||
be_i386_disasm_one_insn,
|
||||
be_i386_insert_Xpoint,
|
||||
be_i386_remove_Xpoint,
|
||||
be_i386_is_watchpoint_set,
|
||||
be_i386_clear_watchpoint,
|
||||
be_i386_adjust_pc_for_break,
|
||||
be_i386_fetch_integer,
|
||||
be_i386_fetch_float,
|
||||
};
|
||||
#endif
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Debugger Power PC specific functions
|
||||
*
|
||||
* Copyright 2000-2003 Marcus Meissner
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "debugger.h"
|
||||
|
||||
#if defined(__powerpc__)
|
||||
|
||||
static unsigned be_ppc_get_addr(HANDLE hThread, const CONTEXT* ctx,
|
||||
enum be_cpu_addr bca, ADDRESS* addr)
|
||||
{
|
||||
switch (bca)
|
||||
{
|
||||
case be_cpu_addr_pc:
|
||||
return be_cpu_build_addr(hThread, ctx, addr, 0, ctx->Iar);
|
||||
default:
|
||||
case be_cpu_addr_stack:
|
||||
case be_cpu_addr_frame:
|
||||
dbg_printf("not done\n");
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void be_ppc_single_step(CONTEXT* ctx, unsigned enable)
|
||||
{
|
||||
#ifndef MSR_SE
|
||||
# define MSR_SE (1<<10)
|
||||
#endif
|
||||
if (enable) ctx->Msr |= MSR_SE;
|
||||
else ctx->Msr &= MSR_SE;
|
||||
}
|
||||
|
||||
static void be_ppc_print_context(HANDLE hThread, const CONTEXT* ctx)
|
||||
{
|
||||
dbg_printf("Context printing for PPC not done yet\n");
|
||||
}
|
||||
|
||||
static void be_ppc_print_segment_info(HANDLE hThread, const CONTEXT* ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static struct dbg_internal_var be_ppc_ctx[] =
|
||||
{
|
||||
{0, NULL, 0, dbg_itype_none}
|
||||
};
|
||||
|
||||
static const struct dbg_internal_var* be_ppc_init_registers(CONTEXT* ctx)
|
||||
{
|
||||
dbg_printf("not done\n");
|
||||
return be_ppc_ctx;
|
||||
}
|
||||
|
||||
static unsigned be_ppc_is_step_over_insn(void* insn)
|
||||
{
|
||||
dbg_printf("not done\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static unsigned be_ppc_is_function_return(void* insn)
|
||||
{
|
||||
dbg_printf("not done\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static unsigned be_ppc_is_break_insn(void* insn)
|
||||
{
|
||||
dbg_printf("not done\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static unsigned be_ppc_is_func_call(void* insn, void** insn_callee)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void be_ppc_disasm_one_insn(ADDRESS* addr, int display)
|
||||
{
|
||||
dbg_printf("Disasm NIY\n");
|
||||
}
|
||||
|
||||
static unsigned be_ppc_insert_Xpoint(HANDLE hProcess, CONTEXT* ctx,
|
||||
enum be_xpoint_type type, void* addr,
|
||||
unsigned long* val, unsigned size)
|
||||
{
|
||||
unsigned long xbp;
|
||||
unsigned long sz;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case be_xpoint_break:
|
||||
if (!size) return 0;
|
||||
if (!ReadProcessMemory(hProcess, addr, val, 4, &sz) || sz != 4) return 0;
|
||||
xbp = 0x7d821008; /* 7d 82 10 08 ... in big endian */
|
||||
if (!WriteProcessMemory(hProcess, addr, &xbp, 4, &sz) || sz != 4) return 0;
|
||||
break;
|
||||
default:
|
||||
dbg_printf("Unknown/unsupported bp type %c\n", type);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned be_ppc_remove_Xpoint(HANDLE hProcess, CONTEXT* ctx,
|
||||
enum be_xpoint_type type, void* addr,
|
||||
unsigned long val, unsigned size)
|
||||
{
|
||||
unsigned long sz;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case be_xpoint_break:
|
||||
if (!size) return 0;
|
||||
if (!WriteProcessMemory(hProcess, addr, &val, 4, &sz) || sz != 4) return 0;
|
||||
break;
|
||||
default:
|
||||
dbg_printf("Unknown/unsupported bp type %c\n", type);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned be_ppc_is_watchpoint_set(const CONTEXT* ctx, unsigned idx)
|
||||
{
|
||||
dbg_printf("not done\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void be_ppc_clear_watchpoint(CONTEXT* ctx, unsigned idx)
|
||||
{
|
||||
dbg_printf("not done\n");
|
||||
}
|
||||
|
||||
static int be_ppc_adjust_pc_for_break(CONTEXT* ctx, BOOL way)
|
||||
{
|
||||
dbg_printf("not done\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int be_ppc_fetch_integer(const struct dbg_lvalue* lvalue, unsigned size,
|
||||
unsigned ext_sign, long long int* ret)
|
||||
{
|
||||
dbg_printf("not done\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int be_ppc_fetch_float(const struct dbg_lvalue* lvalue, unsigned size,
|
||||
long double* ret)
|
||||
{
|
||||
dbg_printf("not done\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
struct backend_cpu be_ppc =
|
||||
{
|
||||
be_cpu_linearize,
|
||||
be_cpu_build_addr,
|
||||
be_ppc_get_addr,
|
||||
be_ppc_single_step,
|
||||
be_ppc_print_context,
|
||||
be_ppc_print_segment_info,
|
||||
be_ppc_init_registers,
|
||||
be_ppc_is_step_over_insn,
|
||||
be_ppc_is_function_return,
|
||||
be_ppc_is_break_insn,
|
||||
be_ppc_is_func_call,
|
||||
be_ppc_disasm_one_insn,
|
||||
be_ppc_insert_Xpoint,
|
||||
be_ppc_remove_Xpoint,
|
||||
be_ppc_is_watchpoint_set,
|
||||
be_ppc_clear_watchpoint,
|
||||
be_ppc_adjust_pc_for_break,
|
||||
be_ppc_fetch_integer,
|
||||
be_ppc_fetch_float,
|
||||
};
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -220,8 +220,8 @@ static const char * const db_Grp12[] = {
|
|||
};
|
||||
|
||||
static const struct inst db_inst_0f0x[] = {
|
||||
/*00*/ { "", TRUE, NONE, op1(Ew), (char *)db_Grp6 },
|
||||
/*01*/ { "", TRUE, NONE, op1(Ew), (char *)db_Grp7 },
|
||||
/*00*/ { "", TRUE, NONE, op1(Ew), (const char *)db_Grp6 },
|
||||
/*01*/ { "", TRUE, NONE, op1(Ew), (const char *)db_Grp7 },
|
||||
/*02*/ { "lar", TRUE, LONG, op2(E,R), 0 },
|
||||
/*03*/ { "lsl", TRUE, LONG, op2(E,R), 0 },
|
||||
/*04*/ { "", FALSE, NONE, 0, 0 },
|
||||
|
@ -341,9 +341,9 @@ static const struct inst db_inst_0f6x[] = {
|
|||
|
||||
static const struct inst db_inst_0f7x[] = {
|
||||
/*70*/ { "pshufw", TRUE, NONE, op2(MX, EMX), 0 },
|
||||
/*71*/ { "(grp10)", TRUE, BYTE, op2(EMX, I), (char*)db_Grp10 },
|
||||
/*72*/ { "(grp11)", TRUE, BYTE, op2(EMX, I), (char*)db_Grp11 },
|
||||
/*73*/ { "(grp12)", TRUE, BYTE, op2(EMX, I), (char*)db_Grp12 },
|
||||
/*71*/ { "(grp10)", TRUE, BYTE, op2(EMX, I), (const char*)db_Grp10 },
|
||||
/*72*/ { "(grp11)", TRUE, BYTE, op2(EMX, I), (const char*)db_Grp11 },
|
||||
/*73*/ { "(grp12)", TRUE, BYTE, op2(EMX, I), (const char*)db_Grp12 },
|
||||
/*74*/ { "pcmpeqb", TRUE, NONE, op2(E, MX), 0 },
|
||||
/*75*/ { "pcmpeqw", TRUE, NONE, op2(E, MX), 0 },
|
||||
/*76*/ { "pcmpeqd", TRUE, NONE, op2(E, MX), 0 },
|
||||
|
@ -431,7 +431,7 @@ static const struct inst db_inst_0fbx[] = {
|
|||
|
||||
/*b8*/ { "", FALSE, NONE, 0, 0 },
|
||||
/*b9*/ { "", FALSE, NONE, 0, 0 },
|
||||
/*ba*/ { "", TRUE, LONG, op2(Ib, E), (char *)db_Grp8 },
|
||||
/*ba*/ { "", TRUE, LONG, op2(Ib, E), (const char *)db_Grp8 },
|
||||
/*bb*/ { "btc", TRUE, LONG, op2(R, E), 0 },
|
||||
/*bc*/ { "bsf", TRUE, LONG, op2(E, R), 0 },
|
||||
/*bd*/ { "bsr", TRUE, LONG, op2(E, R), 0 },
|
||||
|
@ -584,12 +584,12 @@ static const struct finst db_Esc8[] = {
|
|||
static const struct finst db_Esc9[] = {
|
||||
/*0*/ { "fld", SNGL, op1(STI), 0 },
|
||||
/*1*/ { "", NONE, op1(STI), "fxch" },
|
||||
/*2*/ { "fst", SNGL, op1(X), (char *)db_Esc92 },
|
||||
/*3*/ { "fstp", SNGL, op1(X), (char *)db_Esc93 },
|
||||
/*4*/ { "fldenv", NONE, op1(X), (char *)db_Esc94 },
|
||||
/*5*/ { "fldcw", NONE, op1(X), (char *)db_Esc95 },
|
||||
/*6*/ { "fnstenv",NONE, op1(X), (char *)db_Esc96 },
|
||||
/*7*/ { "fnstcw", NONE, op1(X), (char *)db_Esc97 },
|
||||
/*2*/ { "fst", SNGL, op1(X), (const char *)db_Esc92 },
|
||||
/*3*/ { "fstp", SNGL, op1(X), (const char *)db_Esc93 },
|
||||
/*4*/ { "fldenv", NONE, op1(X), (const char *)db_Esc94 },
|
||||
/*5*/ { "fldcw", NONE, op1(X), (const char *)db_Esc95 },
|
||||
/*6*/ { "fnstenv",NONE, op1(X), (const char *)db_Esc96 },
|
||||
/*7*/ { "fnstcw", NONE, op1(X), (const char *)db_Esc97 },
|
||||
};
|
||||
|
||||
static const struct finst db_Esca[] = {
|
||||
|
@ -597,7 +597,7 @@ static const struct finst db_Esca[] = {
|
|||
/*1*/ { "fimul", WORD, 0, 0 },
|
||||
/*2*/ { "ficom", WORD, 0, 0 },
|
||||
/*3*/ { "ficomp", WORD, 0, 0 },
|
||||
/*4*/ { "fisub", WORD, op1(X), (char *)db_Esca4 },
|
||||
/*4*/ { "fisub", WORD, op1(X), (const char *)db_Esca4 },
|
||||
/*5*/ { "fisubr", WORD, 0, 0 },
|
||||
/*6*/ { "fidiv", WORD, 0, 0 },
|
||||
/*7*/ { "fidivr", WORD, 0, 0 }
|
||||
|
@ -608,7 +608,7 @@ static const struct finst db_Escb[] = {
|
|||
/*1*/ { "", NONE, 0, 0 },
|
||||
/*2*/ { "fist", WORD, 0, 0 },
|
||||
/*3*/ { "fistp", WORD, 0, 0 },
|
||||
/*4*/ { "", WORD, op1(X), (char *)db_Escb4 },
|
||||
/*4*/ { "", WORD, op1(X), (const char *)db_Escb4 },
|
||||
/*5*/ { "fld", EXTR, 0, 0 },
|
||||
/*6*/ { "", WORD, 0, 0 },
|
||||
/*7*/ { "fstp", EXTR, 0, 0 },
|
||||
|
@ -640,7 +640,7 @@ static const struct finst db_Esce[] = {
|
|||
/*0*/ { "fiadd", LONG, op2(ST,STI), "faddp" },
|
||||
/*1*/ { "fimul", LONG, op2(ST,STI), "fmulp" },
|
||||
/*2*/ { "ficom", LONG, 0, 0 },
|
||||
/*3*/ { "ficomp", LONG, op1(X), (char *)db_Esce3 },
|
||||
/*3*/ { "ficomp", LONG, op1(X), (const char *)db_Esce3 },
|
||||
/*4*/ { "fisub", LONG, op2(ST,STI), "fsubrp" },
|
||||
/*5*/ { "fisubr", LONG, op2(ST,STI), "fsubp" },
|
||||
/*6*/ { "fidiv", LONG, op2(ST,STI), "fdivrp" },
|
||||
|
@ -652,7 +652,7 @@ static const struct finst db_Escf[] = {
|
|||
/*1*/ { "", LONG, 0, 0 },
|
||||
/*2*/ { "fist", LONG, 0, 0 },
|
||||
/*3*/ { "fistp", LONG, 0, 0 },
|
||||
/*4*/ { "fbld", NONE, op1(XA), (char *)db_Escf4 },
|
||||
/*4*/ { "fbld", NONE, op1(XA), (const char *)db_Escf4 },
|
||||
/*5*/ { "fld", QUAD, 0, 0 },
|
||||
/*6*/ { "fbstp", NONE, 0, 0 },
|
||||
/*7*/ { "fstp", QUAD, 0, 0 },
|
||||
|
@ -864,10 +864,10 @@ static const struct inst db_inst_table[256] = {
|
|||
/*7e*/ { "jle", FALSE, NONE, op1(Db), 0 },
|
||||
/*7f*/ { "jnle", FALSE, NONE, op1(Db), 0 },
|
||||
|
||||
/*80*/ { "", TRUE, BYTE, op2(I, E), (char *)db_Grp1 },
|
||||
/*81*/ { "", TRUE, LONG, op2(I, E), (char *)db_Grp1 },
|
||||
/*82*/ { "", TRUE, BYTE, op2(Is,E), (char *)db_Grp1 },
|
||||
/*83*/ { "", TRUE, LONG, op2(Ibs,E), (char *)db_Grp1 },
|
||||
/*80*/ { "", TRUE, BYTE, op2(I, E), (const char *)db_Grp1 },
|
||||
/*81*/ { "", TRUE, LONG, op2(I, E), (const char *)db_Grp1 },
|
||||
/*82*/ { "", TRUE, BYTE, op2(Is,E), (const char *)db_Grp1 },
|
||||
/*83*/ { "", TRUE, LONG, op2(Ibs,E), (const char *)db_Grp1 },
|
||||
/*84*/ { "test", TRUE, BYTE, op2(R, E), 0 },
|
||||
/*85*/ { "test", TRUE, LONG, op2(R, E), 0 },
|
||||
/*86*/ { "xchg", TRUE, BYTE, op2(R, E), 0 },
|
||||
|
@ -936,8 +936,8 @@ static const struct inst db_inst_table[256] = {
|
|||
/*be*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
|
||||
/*bf*/ { "mov", FALSE, LONG, op2(I, Ri), 0 },
|
||||
|
||||
/*c0*/ { "", TRUE, BYTE, op2(Ib, E), (char *)db_Grp2 },
|
||||
/*c1*/ { "", TRUE, LONG, op2(Ib, E), (char *)db_Grp2 },
|
||||
/*c0*/ { "", TRUE, BYTE, op2(Ib, E), (const char *)db_Grp2 },
|
||||
/*c1*/ { "", TRUE, LONG, op2(Ib, E), (const char *)db_Grp2 },
|
||||
/*c2*/ { "ret", FALSE, NONE, op1(Iw), 0 },
|
||||
/*c3*/ { "ret", FALSE, NONE, 0, 0 },
|
||||
/*c4*/ { "les", TRUE, LONG, op2(E, R), 0 },
|
||||
|
@ -954,23 +954,23 @@ static const struct inst db_inst_table[256] = {
|
|||
/*ce*/ { "into", FALSE, NONE, 0, 0 },
|
||||
/*cf*/ { "iret", FALSE, NONE, 0, 0 },
|
||||
|
||||
/*d0*/ { "", TRUE, BYTE, op2(o1, E), (char *)db_Grp2 },
|
||||
/*d1*/ { "", TRUE, LONG, op2(o1, E), (char *)db_Grp2 },
|
||||
/*d2*/ { "", TRUE, BYTE, op2(CL, E), (char *)db_Grp2 },
|
||||
/*d3*/ { "", TRUE, LONG, op2(CL, E), (char *)db_Grp2 },
|
||||
/*d0*/ { "", TRUE, BYTE, op2(o1, E), (const char *)db_Grp2 },
|
||||
/*d1*/ { "", TRUE, LONG, op2(o1, E), (const char *)db_Grp2 },
|
||||
/*d2*/ { "", TRUE, BYTE, op2(CL, E), (const char *)db_Grp2 },
|
||||
/*d3*/ { "", TRUE, LONG, op2(CL, E), (const char *)db_Grp2 },
|
||||
/*d4*/ { "aam", TRUE, NONE, 0, 0 },
|
||||
/*d5*/ { "aad", TRUE, NONE, 0, 0 },
|
||||
/*d6*/ { "", FALSE, NONE, 0, 0 },
|
||||
/*d7*/ { "xlat", FALSE, BYTE, op1(BX), 0 },
|
||||
|
||||
/*d8*/ { "", TRUE, NONE, 0, (char *)db_Esc8 },
|
||||
/*d9*/ { "", TRUE, NONE, 0, (char *)db_Esc9 },
|
||||
/*da*/ { "", TRUE, NONE, 0, (char *)db_Esca },
|
||||
/*db*/ { "", TRUE, NONE, 0, (char *)db_Escb },
|
||||
/*dc*/ { "", TRUE, NONE, 0, (char *)db_Escc },
|
||||
/*dd*/ { "", TRUE, NONE, 0, (char *)db_Escd },
|
||||
/*de*/ { "", TRUE, NONE, 0, (char *)db_Esce },
|
||||
/*df*/ { "", TRUE, NONE, 0, (char *)db_Escf },
|
||||
/*d8*/ { "", TRUE, NONE, 0, (const char *)db_Esc8 },
|
||||
/*d9*/ { "", TRUE, NONE, 0, (const char *)db_Esc9 },
|
||||
/*da*/ { "", TRUE, NONE, 0, (const char *)db_Esca },
|
||||
/*db*/ { "", TRUE, NONE, 0, (const char *)db_Escb },
|
||||
/*dc*/ { "", TRUE, NONE, 0, (const char *)db_Escc },
|
||||
/*dd*/ { "", TRUE, NONE, 0, (const char *)db_Escd },
|
||||
/*de*/ { "", TRUE, NONE, 0, (const char *)db_Esce },
|
||||
/*df*/ { "", TRUE, NONE, 0, (const char *)db_Escf },
|
||||
|
||||
/*e0*/ { "loopne",FALSE, NONE, op1(Db), 0 },
|
||||
/*e1*/ { "loope", FALSE, NONE, op1(Db), 0 },
|
||||
|
@ -996,8 +996,8 @@ static const struct inst db_inst_table[256] = {
|
|||
/*f3*/ { "", FALSE, NONE, 0, 0 },
|
||||
/*f4*/ { "hlt", FALSE, NONE, 0, 0 },
|
||||
/*f5*/ { "cmc", FALSE, NONE, 0, 0 },
|
||||
/*f6*/ { "", TRUE, BYTE, 0, (char *)db_Grp3 },
|
||||
/*f7*/ { "", TRUE, LONG, 0, (char *)db_Grp3 },
|
||||
/*f6*/ { "", TRUE, BYTE, 0, (const char *)db_Grp3 },
|
||||
/*f7*/ { "", TRUE, LONG, 0, (const char *)db_Grp3 },
|
||||
|
||||
/*f8*/ { "clc", FALSE, NONE, 0, 0 },
|
||||
/*f9*/ { "stc", FALSE, NONE, 0, 0 },
|
||||
|
@ -1005,8 +1005,8 @@ static const struct inst db_inst_table[256] = {
|
|||
/*fb*/ { "sti", FALSE, NONE, 0, 0 },
|
||||
/*fc*/ { "cld", FALSE, NONE, 0, 0 },
|
||||
/*fd*/ { "std", FALSE, NONE, 0, 0 },
|
||||
/*fe*/ { "", TRUE, NONE, 0, (char *)db_Grp4 },
|
||||
/*ff*/ { "", TRUE, NONE, 0, (char *)db_Grp5 },
|
||||
/*fe*/ { "", TRUE, NONE, 0, (const char *)db_Grp4 },
|
||||
/*ff*/ { "", TRUE, NONE, 0, (const char *)db_Grp5 },
|
||||
};
|
||||
|
||||
static const struct inst db_bad_inst =
|
||||
|
@ -1063,46 +1063,49 @@ static const int db_lengths[] = {
|
|||
10, /* EXTR */
|
||||
};
|
||||
|
||||
static unsigned int db_get_task_value( const DBG_ADDR *addr,
|
||||
static unsigned int db_get_task_value( const ADDRESS* addr,
|
||||
int size, int is_signed )
|
||||
{
|
||||
unsigned int result = 0;
|
||||
char buffer[4];
|
||||
|
||||
if (size != 1 && size != 2 && size != 4) {
|
||||
DEBUG_Printf("Illegal size specified\n");
|
||||
} else {
|
||||
DEBUG_READ_MEM((void*)DEBUG_ToLinear( addr ), buffer, size);
|
||||
if (size != 1 && size != 2 && size != 4)
|
||||
{
|
||||
dbg_printf("Illegal size specified\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_read_memory(memory_to_linear_addr(addr), buffer, size);
|
||||
|
||||
switch(size)
|
||||
{
|
||||
case 4:
|
||||
if (is_signed) result = (unsigned int) *(int *)buffer;
|
||||
else result = *(unsigned int *)buffer;
|
||||
break;
|
||||
case 2:
|
||||
if (is_signed) result = (unsigned int) *(short int *)buffer;
|
||||
else result = *(unsigned short int *)buffer;
|
||||
break;
|
||||
case 1:
|
||||
if (is_signed) result = (unsigned int) *(char *)buffer;
|
||||
else result = *(unsigned char *)buffer;
|
||||
break;
|
||||
}
|
||||
switch (size)
|
||||
{
|
||||
case 4:
|
||||
if (is_signed) result = (unsigned int) *(int *)buffer;
|
||||
else result = *(unsigned int *)buffer;
|
||||
break;
|
||||
case 2:
|
||||
if (is_signed) result = (unsigned int) *(short int *)buffer;
|
||||
else result = *(unsigned short int *)buffer;
|
||||
break;
|
||||
case 1:
|
||||
if (is_signed) result = (unsigned int) *(char *)buffer;
|
||||
else result = *(unsigned char *)buffer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#define get_value_inc(result, addr, size, is_signed) \
|
||||
result = db_get_task_value((addr), (size), (is_signed)); \
|
||||
if (!db_disasm_16) (addr)->off += (size); \
|
||||
else (addr)->off = ((addr)->off + (size)) & 0xffff;
|
||||
if (!db_disasm_16) (addr)->Offset += (size); \
|
||||
else (addr)->Offset = ((addr)->Offset + (size)) & 0xffff;
|
||||
|
||||
/*
|
||||
* Read address at location and return updated location.
|
||||
*/
|
||||
void db_read_address( DBG_ADDR *addr, int short_addr, int regmodrm,
|
||||
struct i_addr *addrp )
|
||||
static void db_read_address( ADDRESS* addr, int short_addr, int regmodrm,
|
||||
struct i_addr *addrp )
|
||||
{
|
||||
int mod, rm, sib, index, disp;
|
||||
|
||||
|
@ -1183,48 +1186,47 @@ void db_read_address( DBG_ADDR *addr, int short_addr, int regmodrm,
|
|||
|
||||
static void db_task_printsym(unsigned int addr, int size)
|
||||
{
|
||||
DBG_ADDR address;
|
||||
ADDRESS a;
|
||||
a.Mode = AddrModeFlat;
|
||||
a.Offset = addr;
|
||||
|
||||
address.seg = 0;
|
||||
address.off = addr;
|
||||
|
||||
DEBUG_PrintAddress( &address, db_disasm_16 ? MODE_16 : MODE_32, TRUE );
|
||||
print_address(&a, TRUE);
|
||||
}
|
||||
|
||||
void db_print_address(const char *seg, int size, struct i_addr *addrp, int byref)
|
||||
static void db_print_address(const char *seg, int size, struct i_addr *addrp, int byref)
|
||||
{
|
||||
if (addrp->is_reg) {
|
||||
DEBUG_Printf("%s", db_reg[size][addrp->disp]);
|
||||
dbg_printf("%s", db_reg[size][addrp->disp]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (seg) {
|
||||
DEBUG_Printf("%s:", seg);
|
||||
dbg_printf("%s:", seg);
|
||||
}
|
||||
|
||||
if (addrp->base != 0 || addrp->index != 0) {
|
||||
DEBUG_Printf("0x%x(", addrp->disp);
|
||||
dbg_printf("0x%x(", addrp->disp);
|
||||
if (addrp->base)
|
||||
DEBUG_Printf("%s", addrp->base);
|
||||
dbg_printf("%s", addrp->base);
|
||||
if (addrp->index)
|
||||
DEBUG_Printf(",%s,%d", addrp->index, 1<<addrp->ss);
|
||||
DEBUG_Printf(")");
|
||||
dbg_printf(",%s,%d", addrp->index, 1<<addrp->ss);
|
||||
dbg_printf(")");
|
||||
}
|
||||
else {
|
||||
|
||||
/* try to get destination of indirect call
|
||||
does not work for segmented adresses */
|
||||
if (!seg && byref) {
|
||||
void* a1;
|
||||
void* a2;
|
||||
|
||||
DEBUG_Printf("0x%x -> ", addrp->disp);
|
||||
if (!DEBUG_READ_MEM((void*)addrp->disp, &a1, sizeof(a1))) {
|
||||
DEBUG_Printf("(invalid source)");
|
||||
} else if (!DEBUG_READ_MEM(a1, &a2, sizeof(a2))) {
|
||||
DEBUG_Printf("(invalid destination)");
|
||||
void* a1;
|
||||
void* a2;
|
||||
|
||||
dbg_printf("0x%x -> ", addrp->disp);
|
||||
if (!dbg_read_memory((void*)addrp->disp, &a1, sizeof(a1))) {
|
||||
dbg_printf("(invalid source)");
|
||||
} else if (!dbg_read_memory(a1, &a2, sizeof(a2))) {
|
||||
dbg_printf("(invalid destination)");
|
||||
} else {
|
||||
db_task_printsym((unsigned long)a1, 0);
|
||||
db_task_printsym((unsigned long)a1, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1236,8 +1238,8 @@ void db_print_address(const char *seg, int size, struct i_addr *addrp, int byref
|
|||
* Disassemble floating-point ("escape") instruction
|
||||
* and return updated location.
|
||||
*/
|
||||
void db_disasm_esc( DBG_ADDR *addr, int inst, int short_addr,
|
||||
int size, const char *seg )
|
||||
static void db_disasm_esc( ADDRESS* addr, int inst, int short_addr,
|
||||
int size, const char *seg )
|
||||
{
|
||||
int regmodrm;
|
||||
const struct finst *fp;
|
||||
|
@ -1259,7 +1261,7 @@ void db_disasm_esc( DBG_ADDR *addr, int inst, int short_addr,
|
|||
* Normal address modes.
|
||||
*/
|
||||
db_read_address( addr, short_addr, regmodrm, &address);
|
||||
DEBUG_Printf(fp->f_name);
|
||||
dbg_printf(fp->f_name);
|
||||
switch(fp->f_size) {
|
||||
case SNGL: p = "s"; break;
|
||||
case DBLR: p = "l"; break;
|
||||
|
@ -1269,7 +1271,7 @@ void db_disasm_esc( DBG_ADDR *addr, int inst, int short_addr,
|
|||
case QUAD: p = "q"; break;
|
||||
default: p = ""; break;
|
||||
}
|
||||
DEBUG_Printf("%s\t", p);
|
||||
dbg_printf("%s\t", p);
|
||||
db_print_address(seg, BYTE, &address, 0);
|
||||
}
|
||||
else {
|
||||
|
@ -1279,24 +1281,24 @@ void db_disasm_esc( DBG_ADDR *addr, int inst, int short_addr,
|
|||
switch (fp->f_rrmode) {
|
||||
case op2(ST,STI):
|
||||
name = (fp->f_rrname) ? fp->f_rrname : fp->f_name;
|
||||
DEBUG_Printf("%s\t%%st,%%st(%d)",name,f_rm(regmodrm));
|
||||
dbg_printf("%s\t%%st,%%st(%d)",name,f_rm(regmodrm));
|
||||
break;
|
||||
case op2(STI,ST):
|
||||
name = (fp->f_rrname) ? fp->f_rrname : fp->f_name;
|
||||
DEBUG_Printf("%s\t%%st(%d),%%st",name, f_rm(regmodrm));
|
||||
dbg_printf("%s\t%%st(%d),%%st",name, f_rm(regmodrm));
|
||||
break;
|
||||
case op1(STI):
|
||||
name = (fp->f_rrname) ? fp->f_rrname : fp->f_name;
|
||||
DEBUG_Printf("%s\t%%st(%d)",name, f_rm(regmodrm));
|
||||
dbg_printf("%s\t%%st(%d)",name, f_rm(regmodrm));
|
||||
break;
|
||||
case op1(X):
|
||||
DEBUG_Printf("%s", ((char **)fp->f_rrname)[f_rm(regmodrm)]);
|
||||
dbg_printf("%s", ((char * const*)fp->f_rrname)[f_rm(regmodrm)]);
|
||||
break;
|
||||
case op1(XA):
|
||||
DEBUG_Printf("%s\t%%ax", ((char **)fp->f_rrname)[f_rm(regmodrm)]);
|
||||
dbg_printf("%s\t%%ax", ((char * const*)fp->f_rrname)[f_rm(regmodrm)]);
|
||||
break;
|
||||
default:
|
||||
DEBUG_Printf("<bad instruction>");
|
||||
dbg_printf("<bad instruction>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1304,12 +1306,12 @@ void db_disasm_esc( DBG_ADDR *addr, int inst, int short_addr,
|
|||
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_Disasm
|
||||
* disasm_one_insn
|
||||
*
|
||||
* Disassemble instruction at 'addr'. addr is changed to point to the
|
||||
* start of the next instruction.
|
||||
*/
|
||||
void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
||||
void be_i386_disasm_one_insn(ADDRESS *addr, int display)
|
||||
{
|
||||
int inst;
|
||||
int size;
|
||||
|
@ -1331,12 +1333,11 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
* Set this so we get can supress the printout if we need to.
|
||||
*/
|
||||
db_display = display;
|
||||
switch (DEBUG_GetSelectorType(addr->seg))
|
||||
switch (addr->Mode)
|
||||
{
|
||||
case MODE_VM86:
|
||||
case MODE_16: db_disasm_16 = 1; break;
|
||||
case MODE_32: db_disasm_16 = 0; break;
|
||||
default: DEBUG_Printf("Bad selector %lx\n", addr->seg); return;
|
||||
case AddrModeReal:
|
||||
case AddrMode1616: db_disasm_16 = 1; break;
|
||||
default: db_disasm_16 = 0; break;
|
||||
}
|
||||
|
||||
get_value_inc( inst, addr, 1, FALSE );
|
||||
|
@ -1386,15 +1387,15 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
break;
|
||||
case 0xf0:
|
||||
if( db_display )
|
||||
DEBUG_Printf("lock ");
|
||||
dbg_printf("lock ");
|
||||
break;
|
||||
case 0xf2:
|
||||
if( db_display )
|
||||
DEBUG_Printf("repne ");
|
||||
dbg_printf("repne ");
|
||||
break;
|
||||
case 0xf3:
|
||||
if( db_display )
|
||||
DEBUG_Printf("repe "); /* XXX repe VS rep */
|
||||
dbg_printf("repe "); /* XXX repe VS rep */
|
||||
break;
|
||||
default:
|
||||
prefix = FALSE;
|
||||
|
@ -1433,25 +1434,25 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
i_size = ip->i_size;
|
||||
i_mode = ip->i_mode;
|
||||
|
||||
if (ip->i_extra == (char *)db_Grp1 ||
|
||||
ip->i_extra == (char *)db_Grp2 ||
|
||||
ip->i_extra == (char *)db_Grp6 ||
|
||||
ip->i_extra == (char *)db_Grp7 ||
|
||||
ip->i_extra == (char *)db_Grp8 ||
|
||||
ip->i_extra == (char *)db_Grp10 ||
|
||||
ip->i_extra == (char *)db_Grp11 ||
|
||||
ip->i_extra == (char *)db_Grp12) {
|
||||
i_name = ((char **)ip->i_extra)[f_reg(regmodrm)];
|
||||
if (ip->i_extra == (const char *)db_Grp1 ||
|
||||
ip->i_extra == (const char *)db_Grp2 ||
|
||||
ip->i_extra == (const char *)db_Grp6 ||
|
||||
ip->i_extra == (const char *)db_Grp7 ||
|
||||
ip->i_extra == (const char *)db_Grp8 ||
|
||||
ip->i_extra == (const char *)db_Grp10 ||
|
||||
ip->i_extra == (const char *)db_Grp11 ||
|
||||
ip->i_extra == (const char *)db_Grp12) {
|
||||
i_name = ((const char * const*)ip->i_extra)[f_reg(regmodrm)];
|
||||
}
|
||||
else if (ip->i_extra == (char *)db_Grp3) {
|
||||
ip = (struct inst *)ip->i_extra;
|
||||
else if (ip->i_extra == (const char *)db_Grp3) {
|
||||
ip = (const struct inst *)ip->i_extra;
|
||||
ip = &ip[f_reg(regmodrm)];
|
||||
i_name = ip->i_name;
|
||||
i_mode = ip->i_mode;
|
||||
}
|
||||
else if (ip->i_extra == (char *)db_Grp4 ||
|
||||
ip->i_extra == (char *)db_Grp5) {
|
||||
ip = (struct inst *)ip->i_extra;
|
||||
else if (ip->i_extra == (const char *)db_Grp4 ||
|
||||
ip->i_extra == (const char *)db_Grp5) {
|
||||
ip = (const struct inst *)ip->i_extra;
|
||||
ip = &ip[f_reg(regmodrm)];
|
||||
i_name = ip->i_name;
|
||||
i_mode = ip->i_mode;
|
||||
|
@ -1462,28 +1463,28 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
if( db_display )
|
||||
{
|
||||
if (size == WORD)
|
||||
DEBUG_Printf(i_name);
|
||||
dbg_printf(i_name);
|
||||
else
|
||||
DEBUG_Printf(ip->i_extra);
|
||||
dbg_printf(ip->i_extra);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf(i_name);
|
||||
dbg_printf(i_name);
|
||||
}
|
||||
if (i_size != NONE) {
|
||||
if (i_size == BYTE) {
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("b");
|
||||
dbg_printf("b");
|
||||
}
|
||||
size = BYTE;
|
||||
}
|
||||
else if (i_size == WORD) {
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("w");
|
||||
dbg_printf("w");
|
||||
}
|
||||
size = WORD;
|
||||
}
|
||||
|
@ -1491,28 +1492,28 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
{
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("w");
|
||||
dbg_printf("w");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("l");
|
||||
dbg_printf("l");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("\t");
|
||||
dbg_printf("\t");
|
||||
}
|
||||
for (first = TRUE;
|
||||
i_mode != 0;
|
||||
i_mode >>= 8, first = FALSE)
|
||||
{
|
||||
if (!first && db_display)
|
||||
DEBUG_Printf(",");
|
||||
dbg_printf(",");
|
||||
|
||||
switch (i_mode & 0xFF) {
|
||||
|
||||
|
@ -1526,7 +1527,7 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
case Eind:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("*");
|
||||
dbg_printf("*");
|
||||
db_print_address(seg, size, &address, 1);
|
||||
}
|
||||
break;
|
||||
|
@ -1548,31 +1549,31 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
case R:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%s", db_reg[size][f_reg(regmodrm)]);
|
||||
dbg_printf("%s", db_reg[size][f_reg(regmodrm)]);
|
||||
}
|
||||
break;
|
||||
case MX:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%%mm%d", f_reg(regmodrm));
|
||||
dbg_printf("%%mm%d", f_reg(regmodrm));
|
||||
}
|
||||
break;
|
||||
case EMX:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%%mm%d", f_rm(regmodrm));
|
||||
dbg_printf("%%mm%d", f_rm(regmodrm));
|
||||
}
|
||||
break;
|
||||
case XMM:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%%xmm%d", f_reg(regmodrm));
|
||||
dbg_printf("%%xmm%d", f_reg(regmodrm));
|
||||
}
|
||||
break;
|
||||
case EXMM:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%%xmm%d", f_rm(regmodrm));
|
||||
dbg_printf("%%xmm%d", f_rm(regmodrm));
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1580,35 +1581,35 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
case Rw:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%s", db_reg[WORD][f_reg(regmodrm)]);
|
||||
dbg_printf("%s", db_reg[WORD][f_reg(regmodrm)]);
|
||||
}
|
||||
break;
|
||||
|
||||
case Ri:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%s", db_reg[size][f_rm(inst)]);
|
||||
dbg_printf("%s", db_reg[size][f_rm(inst)]);
|
||||
}
|
||||
break;
|
||||
|
||||
case S:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%s", db_seg_reg[f_reg(regmodrm)]);
|
||||
dbg_printf("%s", db_seg_reg[f_reg(regmodrm)]);
|
||||
}
|
||||
break;
|
||||
|
||||
case Si:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%s", db_seg_reg[f_reg(inst)]);
|
||||
dbg_printf("%s", db_seg_reg[f_reg(inst)]);
|
||||
}
|
||||
break;
|
||||
|
||||
case A:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%s", db_reg[size][0]); /* acc */
|
||||
dbg_printf("%s", db_reg[size][0]); /* acc */
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1616,22 +1617,22 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
if( db_display )
|
||||
{
|
||||
if (seg)
|
||||
DEBUG_Printf("%s:", seg);
|
||||
DEBUG_Printf("(%s)", short_addr ? "%bx" : "%ebx");
|
||||
dbg_printf("%s:", seg);
|
||||
dbg_printf("(%s)", short_addr ? "%bx" : "%ebx");
|
||||
}
|
||||
break;
|
||||
|
||||
case CL:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%%cl");
|
||||
dbg_printf("%%cl");
|
||||
}
|
||||
break;
|
||||
|
||||
case DX:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%%dx");
|
||||
dbg_printf("%%dx");
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1639,36 +1640,36 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
if( db_display )
|
||||
{
|
||||
if (seg)
|
||||
DEBUG_Printf("%s:", seg);
|
||||
DEBUG_Printf("(%s)", short_addr ? "%si" : "%esi");
|
||||
dbg_printf("%s:", seg);
|
||||
dbg_printf("(%s)", short_addr ? "%si" : "%esi");
|
||||
}
|
||||
break;
|
||||
|
||||
case DI:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%%es:(%s)", short_addr ? "%di" : "%edi");
|
||||
dbg_printf("%%es:(%s)", short_addr ? "%di" : "%edi");
|
||||
}
|
||||
break;
|
||||
|
||||
case CR:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%%cr%d", f_reg(regmodrm));
|
||||
dbg_printf("%%cr%d", f_reg(regmodrm));
|
||||
}
|
||||
break;
|
||||
|
||||
case DR:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%%dr%d", f_reg(regmodrm));
|
||||
dbg_printf("%%dr%d", f_reg(regmodrm));
|
||||
}
|
||||
break;
|
||||
|
||||
case TR:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("%%tr%d", f_reg(regmodrm));
|
||||
dbg_printf("%%tr%d", f_reg(regmodrm));
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1677,7 +1678,7 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
get_value_inc(imm, addr, len, FALSE);/* unsigned */
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("$0x%x", imm);
|
||||
dbg_printf("$0x%x", imm);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1686,7 +1687,7 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
get_value_inc(imm, addr, len, TRUE); /* signed */
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("$%d", imm);
|
||||
dbg_printf("$%d", imm);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1694,7 +1695,7 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
get_value_inc(imm, addr, 1, FALSE); /* unsigned */
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("$0x%x", imm);
|
||||
dbg_printf("$0x%x", imm);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1702,7 +1703,7 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
get_value_inc(imm, addr, 1, TRUE); /* signed */
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("$%d", imm);
|
||||
dbg_printf("$%d", imm);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1710,7 +1711,7 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
get_value_inc(imm, addr, 2, FALSE); /* unsigned */
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("$0x%x", imm);
|
||||
dbg_printf("$0x%x", imm);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1718,7 +1719,7 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
get_value_inc(imm, addr, 4, FALSE);
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("$0x%x", imm);
|
||||
dbg_printf("$0x%x", imm);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1735,7 +1736,7 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
}
|
||||
|
||||
if (seg)
|
||||
DEBUG_Printf("%s:0x%x",seg, displ);
|
||||
dbg_printf("%s:0x%x",seg, displ);
|
||||
else
|
||||
db_task_printsym(displ, short_addr ? WORD : LONG);
|
||||
break;
|
||||
|
@ -1749,23 +1750,23 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
|
||||
if (size == WORD) {
|
||||
/* offset only affects low 16 bits */
|
||||
displ = (addr->off & 0xffff0000)
|
||||
| ((addr->off + displ) & 0xffff);
|
||||
displ = (addr->Offset & 0xffff0000)
|
||||
| ((addr->Offset + displ) & 0xffff);
|
||||
}
|
||||
else displ += addr->off;
|
||||
else displ += addr->Offset;
|
||||
db_task_printsym(displ, size);
|
||||
break;
|
||||
|
||||
case Dl:
|
||||
if (size == WORD) {
|
||||
get_value_inc(displ, addr, 2, TRUE);
|
||||
/* offset only affects low 16 bits */
|
||||
displ = (addr->off & 0xffff0000)
|
||||
| ((addr->off + displ) & 0xffff);
|
||||
/* Offsetset only affects low 16 bits */
|
||||
displ = (addr->Offset & 0xffff0000)
|
||||
| ((addr->Offset + displ) & 0xffff);
|
||||
}
|
||||
else {
|
||||
get_value_inc(displ, addr, 4, TRUE);
|
||||
displ += addr->off;
|
||||
displ += addr->Offset;
|
||||
}
|
||||
if( !db_display )
|
||||
{
|
||||
|
@ -1777,27 +1778,29 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
case o1:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("$1");
|
||||
dbg_printf("$1");
|
||||
}
|
||||
break;
|
||||
|
||||
case o3:
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_Printf("$3");
|
||||
dbg_printf("$3");
|
||||
}
|
||||
break;
|
||||
|
||||
case OS:
|
||||
{
|
||||
DBG_ADDR address;
|
||||
get_value_inc( address.off, addr, /* offset */
|
||||
ADDRESS address;
|
||||
get_value_inc( address.Offset, addr, /* offset */
|
||||
short_addr ? 2 : 4, FALSE );
|
||||
get_value_inc( address.seg, addr, /* segment */
|
||||
get_value_inc( address.Segment, addr, /* segment */
|
||||
2, FALSE );
|
||||
be_cpu->build_addr(dbg_curr_thread->handle, &dbg_context,
|
||||
&address, address.Segment, address.Offset);
|
||||
if( db_display )
|
||||
{
|
||||
DEBUG_PrintAddress( &address, short_addr ? MODE_16 : MODE_32, TRUE );
|
||||
print_address( &address, TRUE );
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1805,11 +1808,4 @@ void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else /* __i386__ */
|
||||
|
||||
void DEBUG_Disasm( DBG_ADDR *addr, int display )
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* __i386__ */
|
||||
|
|
|
@ -24,41 +24,36 @@
|
|||
#include "config.h"
|
||||
#include "wine/port.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "wine/exception.h"
|
||||
#include "debugger.h"
|
||||
#include "expr.h"
|
||||
#include "excpt.h"
|
||||
|
||||
static void mode_command(int);
|
||||
int yylex(void);
|
||||
int yyerror(const char *);
|
||||
int yyerror(const char*);
|
||||
|
||||
%}
|
||||
|
||||
%union
|
||||
{
|
||||
DBG_VALUE value;
|
||||
char * string;
|
||||
int integer;
|
||||
struct list_id listing;
|
||||
struct expr * expression;
|
||||
struct datatype* type;
|
||||
struct dbg_lvalue lvalue;
|
||||
char* string;
|
||||
int integer;
|
||||
IMAGEHLP_LINE listing;
|
||||
struct expr* expression;
|
||||
struct type_expr_t type;
|
||||
}
|
||||
|
||||
%token tCONT tPASS tSTEP tLIST tNEXT tQUIT tHELP tBACKTRACE tINFO tWALK tUP tDOWN
|
||||
%token tCONT tPASS tSTEP tLIST tNEXT tQUIT tHELP tBACKTRACE tINFO tUP tDOWN
|
||||
%token tENABLE tDISABLE tBREAK tWATCH tDELETE tSET tMODE tPRINT tEXAM tABORT tVM86
|
||||
%token tCLASS tMAPS tMODULE tSTACK tSEGMENTS tSYMBOL tREGS tWND tQUEUE tLOCAL tEXCEPTION
|
||||
%token tCLASS tMAPS tSTACK tSEGMENTS tSYMBOL tREGS tWND tQUEUE tLOCAL tEXCEPTION
|
||||
%token tPROCESS tTHREAD tMODREF tEOL tEOF
|
||||
%token tFRAME tSHARE tCOND tDISPLAY tUNDISPLAY tDISASSEMBLE
|
||||
%token tSTEPI tNEXTI tFINISH tSHOW tDIR tWHATIS tSOURCE
|
||||
%token <string> tPATH
|
||||
%token <string> tIDENTIFIER tSTRING tDEBUGSTR tINTVAR
|
||||
%token <string> tPATH tIDENTIFIER tSTRING tDEBUGSTR tINTVAR
|
||||
%token <integer> tNUM tFORMAT
|
||||
%token tSYMBOLFILE tRUN tATTACH tDETACH tNOPROCESS tMAINTENANCE tTYPE
|
||||
|
||||
|
@ -84,225 +79,239 @@ int yyerror(const char *);
|
|||
%left '.' '[' OP_DRF
|
||||
%nonassoc ':'
|
||||
|
||||
%type <expression> expr lval lvalue
|
||||
%type <type> type_expr
|
||||
%type <value> expr_addr lval_addr
|
||||
%type <integer> expr_value
|
||||
%type <expression> expr lvalue
|
||||
%type <lvalue> expr_lvalue lvalue_addr
|
||||
%type <integer> expr_rvalue
|
||||
%type <string> pathname identifier
|
||||
|
||||
%type <listing> list_arg
|
||||
%type <type> type_expr
|
||||
|
||||
%%
|
||||
|
||||
input: line
|
||||
input:
|
||||
line
|
||||
| input line
|
||||
;
|
||||
|
||||
line: command { DEBUG_FreeExprMem(); }
|
||||
line:
|
||||
command tEOL { expr_free_all(); }
|
||||
| tEOL
|
||||
| tEOF { return 1; }
|
||||
| error tEOL { yyerrok; DEBUG_FreeExprMem(); }
|
||||
| error tEOL { yyerrok; expr_free_all(); }
|
||||
;
|
||||
|
||||
command:
|
||||
tQUIT tEOL { /*DEBUG_Quit();*/ return 1; }
|
||||
| tHELP tEOL { DEBUG_Help(); }
|
||||
| tHELP tINFO tEOL { DEBUG_HelpInfo(); }
|
||||
| tPASS tEOL { DEBUG_WaitNextException(DBG_EXCEPTION_NOT_HANDLED, 0, 0); }
|
||||
| tCONT tEOL { DEBUG_WaitNextException(DBG_CONTINUE, 1, EXEC_CONT); }
|
||||
| tCONT tNUM tEOL { DEBUG_WaitNextException(DBG_CONTINUE, $2, EXEC_CONT); }
|
||||
| tSTEP tEOL { DEBUG_WaitNextException(DBG_CONTINUE, 1, EXEC_STEP_INSTR); }
|
||||
| tSTEP tNUM tEOL { DEBUG_WaitNextException(DBG_CONTINUE, $2, EXEC_STEP_INSTR); }
|
||||
| tNEXT tEOL { DEBUG_WaitNextException(DBG_CONTINUE, 1, EXEC_STEP_OVER); }
|
||||
| tNEXT tNUM tEOL { DEBUG_WaitNextException(DBG_CONTINUE, $2, EXEC_STEP_OVER); }
|
||||
| tSTEPI tEOL { DEBUG_WaitNextException(DBG_CONTINUE, 1, EXEC_STEPI_INSTR); }
|
||||
| tSTEPI tNUM tEOL { DEBUG_WaitNextException(DBG_CONTINUE, $2, EXEC_STEPI_INSTR); }
|
||||
| tNEXTI tEOL { DEBUG_WaitNextException(DBG_CONTINUE, 1, EXEC_STEPI_OVER); }
|
||||
| tNEXTI tNUM tEOL { DEBUG_WaitNextException(DBG_CONTINUE, $2, EXEC_STEPI_OVER); }
|
||||
| tFINISH tEOL { DEBUG_WaitNextException(DBG_CONTINUE, 0, EXEC_FINISH); }
|
||||
| tABORT tEOL { abort(); }
|
||||
| tMODE tNUM tEOL { mode_command($2); }
|
||||
| tMODE tVM86 tEOL { DEBUG_CurrThread->dbg_mode = MODE_VM86; }
|
||||
| tBACKTRACE tEOL { DEBUG_BackTrace(DEBUG_CurrTid, TRUE); }
|
||||
| tBACKTRACE tNUM tEOL { DEBUG_BackTrace($2, TRUE); }
|
||||
| tUP tEOL { DEBUG_SetFrame( curr_frame + 1 ); }
|
||||
| tUP tNUM tEOL { DEBUG_SetFrame( curr_frame + $2 ); }
|
||||
| tDOWN tEOL { DEBUG_SetFrame( curr_frame - 1 ); }
|
||||
| tDOWN tNUM tEOL { DEBUG_SetFrame( curr_frame - $2 ); }
|
||||
| tFRAME tNUM tEOL { DEBUG_SetFrame( $2 ); }
|
||||
| tSHOW tDIR tEOL { DEBUG_ShowDir(); }
|
||||
| tDIR pathname tEOL { DEBUG_AddPath( $2 ); }
|
||||
| tDIR tEOL { DEBUG_NukePath(); }
|
||||
| tCOND tNUM tEOL { DEBUG_AddBPCondition($2, NULL); }
|
||||
| tCOND tNUM expr tEOL { DEBUG_AddBPCondition($2, $3); }
|
||||
| tSOURCE pathname tEOL { DEBUG_Parser($2); }
|
||||
| tSYMBOLFILE pathname tEOL { DEBUG_ReadSymbolTable($2, 0); }
|
||||
| tSYMBOLFILE pathname expr_value tEOL { DEBUG_ReadSymbolTable($2, $3); }
|
||||
| tWHATIS expr_addr tEOL { DEBUG_PrintType(&$2); }
|
||||
| tATTACH tNUM tEOL { DEBUG_Attach($2, FALSE, TRUE); }
|
||||
| tDETACH tEOL { return DEBUG_Detach(); /* FIXME: we shouldn't return, but since we cannot simply clean the symbol table, exit debugger for now */ }
|
||||
tQUIT { return 1; }
|
||||
| tHELP { print_help(); }
|
||||
| tHELP tINFO { info_help(); }
|
||||
| tPASS { dbg_wait_next_exception(DBG_EXCEPTION_NOT_HANDLED, 0, 0); }
|
||||
| tCONT { dbg_wait_next_exception(DBG_CONTINUE, 1, dbg_exec_cont); }
|
||||
| tCONT tNUM { dbg_wait_next_exception(DBG_CONTINUE, $2, dbg_exec_cont); }
|
||||
| tSTEP { dbg_wait_next_exception(DBG_CONTINUE, 1, dbg_exec_step_into_line); }
|
||||
| tSTEP tNUM { dbg_wait_next_exception(DBG_CONTINUE, $2, dbg_exec_step_into_line); }
|
||||
| tNEXT { dbg_wait_next_exception(DBG_CONTINUE, 1, dbg_exec_step_over_line); }
|
||||
| tNEXT tNUM { dbg_wait_next_exception(DBG_CONTINUE, $2, dbg_exec_step_over_line); }
|
||||
| tSTEPI { dbg_wait_next_exception(DBG_CONTINUE, 1, dbg_exec_step_into_insn); }
|
||||
| tSTEPI tNUM { dbg_wait_next_exception(DBG_CONTINUE, $2, dbg_exec_step_into_insn); }
|
||||
| tNEXTI { dbg_wait_next_exception(DBG_CONTINUE, 1, dbg_exec_step_over_insn); }
|
||||
| tNEXTI tNUM { dbg_wait_next_exception(DBG_CONTINUE, $2, dbg_exec_step_over_insn); }
|
||||
| tFINISH { dbg_wait_next_exception(DBG_CONTINUE, 0, dbg_exec_finish); }
|
||||
| tABORT { abort(); }
|
||||
| tBACKTRACE { stack_backtrace(dbg_curr_tid, TRUE); }
|
||||
| tBACKTRACE tNUM { stack_backtrace($2, TRUE); }
|
||||
| tUP { stack_set_frame(dbg_curr_frame + 1); }
|
||||
| tUP tNUM { stack_set_frame(dbg_curr_frame + $2); }
|
||||
| tDOWN { stack_set_frame(dbg_curr_frame - 1); }
|
||||
| tDOWN tNUM { stack_set_frame(dbg_curr_frame - $2); }
|
||||
| tFRAME tNUM { stack_set_frame($2); }
|
||||
| tSHOW tDIR { source_show_path(); }
|
||||
| tDIR pathname { source_add_path($2); }
|
||||
| tDIR { source_nuke_path(); }
|
||||
| tCOND tNUM { break_add_condition($2, NULL); }
|
||||
| tCOND tNUM expr { break_add_condition($2, $3); }
|
||||
| tSOURCE pathname { parser($2); }
|
||||
| tSYMBOLFILE pathname { symbol_read_symtable($2, 0); }
|
||||
| tSYMBOLFILE pathname expr_rvalue { symbol_read_symtable($2, $3); }
|
||||
| tWHATIS expr_lvalue { types_print_type((DWORD)memory_to_linear_addr(&$2.addr), $2.typeid, FALSE); dbg_printf("\n"); }
|
||||
| tATTACH tNUM { dbg_attach_debuggee($2, FALSE, TRUE); }
|
||||
| tDETACH { dbg_detach_debuggee(); }
|
||||
| run_command
|
||||
| list_command
|
||||
| disassemble_command
|
||||
| set_command
|
||||
| x_command
|
||||
| print_command
|
||||
| break_commands
|
||||
| display_commands
|
||||
| print_command
|
||||
| break_command
|
||||
| watch_command
|
||||
| display_command
|
||||
| info_command
|
||||
| walk_command
|
||||
| run_command
|
||||
| maintenance_command
|
||||
| noprocess_state
|
||||
;
|
||||
|
||||
display_commands:
|
||||
| tDISPLAY tEOL { DEBUG_InfoDisplay(); }
|
||||
| tDISPLAY expr tEOL { DEBUG_AddDisplay($2, 1, 0, FALSE); }
|
||||
| tDISPLAY tFORMAT expr tEOL{ DEBUG_AddDisplay($3, $2 >> 8, $2 & 0xff, FALSE); }
|
||||
| tLOCAL tDISPLAY expr tEOL { DEBUG_AddDisplay($3, 1, 0, TRUE); }
|
||||
| tLOCAL tDISPLAY tFORMAT expr tEOL { DEBUG_AddDisplay($4, $3 >> 8, $3 & 0xff, TRUE); }
|
||||
| tENABLE tDISPLAY tNUM tEOL{ DEBUG_EnableDisplay( $3, TRUE ); }
|
||||
| tDISABLE tDISPLAY tNUM tEOL { DEBUG_EnableDisplay( $3, FALSE ); }
|
||||
| tDELETE tDISPLAY tNUM tEOL{ DEBUG_DelDisplay( $3 ); }
|
||||
| tDELETE tDISPLAY tEOL { DEBUG_DelDisplay( -1 ); }
|
||||
| tUNDISPLAY tNUM tEOL { DEBUG_DelDisplay( $2 ); }
|
||||
| tUNDISPLAY tEOL { DEBUG_DelDisplay( -1 ); }
|
||||
;
|
||||
|
||||
set_command:
|
||||
tSET lval_addr '=' expr_value tEOL { DEBUG_WriteMemory(&$2, $4); }
|
||||
| tSET '+' tIDENTIFIER tEOL { DEBUG_DbgChannel(TRUE, NULL, $3); }
|
||||
| tSET '-' tIDENTIFIER tEOL { DEBUG_DbgChannel(FALSE, NULL, $3); }
|
||||
| tSET tIDENTIFIER '+' tIDENTIFIER tEOL { DEBUG_DbgChannel(TRUE, $2, $4); }
|
||||
| tSET tIDENTIFIER '-' tIDENTIFIER tEOL { DEBUG_DbgChannel(FALSE, $2, $4); }
|
||||
;
|
||||
|
||||
pathname:
|
||||
tIDENTIFIER { $$ = $1; }
|
||||
identifier { $$ = $1; }
|
||||
| tPATH { $$ = $1; }
|
||||
;
|
||||
|
||||
disassemble_command:
|
||||
tDISASSEMBLE tEOL { DEBUG_Disassemble( NULL, NULL, 10 ); }
|
||||
| tDISASSEMBLE expr_addr tEOL { DEBUG_Disassemble( & $2, NULL, 10 ); }
|
||||
| tDISASSEMBLE expr_addr ',' expr_addr tEOL { DEBUG_Disassemble( & $2, & $4, 0 ); }
|
||||
;
|
||||
|
||||
list_command:
|
||||
tLIST tEOL { DEBUG_List( NULL, NULL, 10 ); }
|
||||
| tLIST '-' tEOL { DEBUG_List( NULL, NULL, -10 ); }
|
||||
| tLIST list_arg tEOL { DEBUG_List( & $2, NULL, 10 ); }
|
||||
| tLIST ',' list_arg tEOL { DEBUG_List( NULL, & $3, -10 ); }
|
||||
| tLIST list_arg ',' list_arg tEOL { DEBUG_List( & $2, & $4, 0 ); }
|
||||
identifier:
|
||||
tIDENTIFIER { $$ = $1; }
|
||||
| tIDENTIFIER '!' tIDENTIFIER { char* ptr = HeapAlloc(GetProcessHeap(), 0, strlen($1) + 1 + strlen($3) + 1);
|
||||
sprintf(ptr, "%s!%s", $1, $3); $$ = lexeme_alloc(ptr);
|
||||
HeapFree(GetProcessHeap(), 0, ptr); }
|
||||
| identifier ':' ':' tIDENTIFIER { char* ptr = HeapAlloc(GetProcessHeap(), 0, strlen($1) + 2 + strlen($4) + 1);
|
||||
sprintf(ptr, "%s::%s", $1, $4); $$ = lexeme_alloc(ptr);
|
||||
HeapFree(GetProcessHeap(), 0, ptr); }
|
||||
;
|
||||
|
||||
list_arg:
|
||||
tNUM { $$.sourcefile = NULL; $$.line = $1; }
|
||||
| pathname ':' tNUM { $$.sourcefile = $1; $$.line = $3; }
|
||||
| tIDENTIFIER { DEBUG_GetFuncInfo( & $$, NULL, $1); }
|
||||
| pathname ':' tIDENTIFIER { DEBUG_GetFuncInfo( & $$, $1, $3); }
|
||||
| '*' expr_addr { DEBUG_FindNearestSymbol( & $2.addr, FALSE, NULL, 0, & $$ ); }
|
||||
;
|
||||
|
||||
x_command:
|
||||
tEXAM expr_addr tEOL { DEBUG_ExamineMemory( &$2, 1, 'x'); }
|
||||
| tEXAM tFORMAT expr_addr tEOL { DEBUG_ExamineMemory( &$3, $2>>8, $2&0xff ); }
|
||||
;
|
||||
|
||||
print_command:
|
||||
tPRINT expr_addr tEOL { DEBUG_Print( &$2, 1, 0, 0 ); }
|
||||
| tPRINT tFORMAT expr_addr tEOL { DEBUG_Print( &$3, $2 >> 8, $2 & 0xff, 0 ); }
|
||||
;
|
||||
|
||||
break_commands:
|
||||
tBREAK '*' expr_addr tEOL{ DEBUG_AddBreakpointFromValue( &$3 ); }
|
||||
| tBREAK identifier tEOL { DEBUG_AddBreakpointFromId($2, -1); }
|
||||
| tBREAK identifier ':' tNUM tEOL { DEBUG_AddBreakpointFromId($2, $4); }
|
||||
| tBREAK tNUM tEOL { DEBUG_AddBreakpointFromLineno($2); }
|
||||
| tBREAK tEOL { DEBUG_AddBreakpointFromLineno(-1); }
|
||||
| tENABLE tNUM tEOL { DEBUG_EnableBreakpoint( $2, TRUE ); }
|
||||
| tENABLE tBREAK tNUM tEOL { DEBUG_EnableBreakpoint( $3, TRUE ); }
|
||||
| tDISABLE tNUM tEOL { DEBUG_EnableBreakpoint( $2, FALSE ); }
|
||||
| tDISABLE tBREAK tNUM tEOL { DEBUG_EnableBreakpoint( $3, FALSE ); }
|
||||
| tDELETE tNUM tEOL { DEBUG_DelBreakpoint( $2 ); }
|
||||
| tDELETE tBREAK tNUM tEOL { DEBUG_DelBreakpoint( $3 ); }
|
||||
;
|
||||
|
||||
watch_command:
|
||||
tWATCH '*' expr_addr tEOL { DEBUG_AddWatchpoint( &$3, 1 ); }
|
||||
| tWATCH identifier tEOL { DEBUG_AddWatchpointFromId($2); }
|
||||
;
|
||||
|
||||
info_command:
|
||||
tINFO tBREAK tEOL { DEBUG_InfoBreakpoints(); }
|
||||
| tINFO tCLASS tSTRING tEOL { DEBUG_InfoClass( $3 ); }
|
||||
| tINFO tSHARE tEOL { DEBUG_InfoShare(); }
|
||||
| tINFO tMODULE expr_value tEOL { DEBUG_DumpModule( $3 ); }
|
||||
| tINFO tREGS tEOL { DEBUG_InfoRegisters(&DEBUG_context); }
|
||||
| tINFO tSEGMENTS expr_value tEOL { DEBUG_InfoSegments( $3, 1 ); }
|
||||
| tINFO tSEGMENTS tEOL { DEBUG_InfoSegments( 0, -1 ); }
|
||||
| tINFO tSTACK tEOL { DEBUG_InfoStack(); }
|
||||
| tINFO tSYMBOL tSTRING tEOL{ DEBUG_InfoSymbols($3); }
|
||||
| tINFO tWND expr_value tEOL{ DEBUG_InfoWindow( (HWND)$3 ); }
|
||||
| tINFO tLOCAL tEOL { DEBUG_InfoLocals(); }
|
||||
| tINFO tDISPLAY tEOL { DEBUG_InfoDisplay(); }
|
||||
;
|
||||
|
||||
walk_command:
|
||||
tWALK tCLASS tEOL { DEBUG_WalkClasses(); }
|
||||
| tWALK tMODULE tEOL { DEBUG_WalkModules(); }
|
||||
| tWALK tWND tEOL { DEBUG_WalkWindows( 0, 0 ); }
|
||||
| tWALK tWND expr_value tEOL{ DEBUG_WalkWindows( (HWND)$3, 0 ); }
|
||||
| tWALK tMAPS tEOL { DEBUG_InfoVirtual(0); }
|
||||
| tWALK tMAPS expr_value tEOL { DEBUG_InfoVirtual($3); }
|
||||
| tWALK tPROCESS tEOL { DEBUG_WalkProcess(); }
|
||||
| tWALK tTHREAD tEOL { DEBUG_WalkThreads(); }
|
||||
| tWALK tEXCEPTION tEOL { DEBUG_WalkExceptions(DEBUG_CurrTid); }
|
||||
| tWALK tEXCEPTION expr_value tEOL{ DEBUG_WalkExceptions($3); }
|
||||
tNUM { $$.FileName = NULL; $$.LineNumber = $1; }
|
||||
| pathname ':' tNUM { $$.FileName = $1; $$.LineNumber = $3; }
|
||||
| identifier { symbol_get_line(NULL, $1, &$$); }
|
||||
| pathname ':' identifier { symbol_get_line($3, $1, &$$); }
|
||||
| '*' expr_lvalue { $$.SizeOfStruct = sizeof($$);
|
||||
SymGetLineFromAddr(dbg_curr_process->handle, (unsigned long)memory_to_linear_addr(& $2.addr), NULL, & $$); }
|
||||
;
|
||||
|
||||
run_command:
|
||||
tRUN tEOL { DEBUG_Run(NULL); }
|
||||
| tRUN tSTRING tEOL { DEBUG_Run($2); }
|
||||
tRUN { dbg_run_debuggee(NULL); }
|
||||
| tRUN tSTRING { dbg_run_debuggee($2); }
|
||||
;
|
||||
|
||||
list_command:
|
||||
tLIST { source_list(NULL, NULL, 10); }
|
||||
| tLIST '-' { source_list(NULL, NULL, -10); }
|
||||
| tLIST list_arg { source_list(& $2, NULL, 10); }
|
||||
| tLIST ',' list_arg { source_list(NULL, & $3, -10); }
|
||||
| tLIST list_arg ',' list_arg { source_list(& $2, & $4, 0); }
|
||||
;
|
||||
|
||||
disassemble_command:
|
||||
tDISASSEMBLE { memory_disassemble(NULL, NULL, 10); }
|
||||
| tDISASSEMBLE expr_lvalue { memory_disassemble(&$2, NULL, 10); }
|
||||
| tDISASSEMBLE expr_lvalue ',' expr_lvalue { memory_disassemble(&$2, &$4, 0); }
|
||||
;
|
||||
|
||||
set_command:
|
||||
tSET lvalue_addr '=' expr_rvalue { memory_write_value(&$2, sizeof(int), &$4); }
|
||||
| tSET '+' tIDENTIFIER { info_wine_dbg_channel(TRUE, NULL, $3); }
|
||||
| tSET '-' tIDENTIFIER { info_wine_dbg_channel(FALSE, NULL, $3); }
|
||||
| tSET tIDENTIFIER '+' tIDENTIFIER { info_wine_dbg_channel(TRUE, $2, $4); }
|
||||
| tSET tIDENTIFIER '-' tIDENTIFIER { info_wine_dbg_channel(FALSE, $2, $4); }
|
||||
;
|
||||
|
||||
x_command:
|
||||
tEXAM expr_lvalue { memory_examine(&$2, 1, 'x'); }
|
||||
| tEXAM tFORMAT expr_lvalue { memory_examine(&$3, $2 >> 8, $2 & 0xff); }
|
||||
;
|
||||
|
||||
print_command:
|
||||
tPRINT expr_lvalue { print_value(&$2, 0, 0); }
|
||||
| tPRINT tFORMAT expr_lvalue { if (($2 >> 8) == 1) print_value(&$3, $2 & 0xff, 0); else dbg_printf("Count is meaningless in print command\n"); }
|
||||
;
|
||||
|
||||
break_command:
|
||||
tBREAK '*' expr_lvalue { break_add_break_from_lvalue(&$3); }
|
||||
| tBREAK identifier { break_add_break_from_id($2, -1); }
|
||||
| tBREAK identifier ':' tNUM { break_add_break_from_id($2, $4); }
|
||||
| tBREAK tNUM { break_add_break_from_lineno($2); }
|
||||
| tBREAK { break_add_break_from_lineno(-1); }
|
||||
| tENABLE tNUM { break_enable_xpoint($2, TRUE); }
|
||||
| tENABLE tBREAK tNUM { break_enable_xpoint($3, TRUE); }
|
||||
| tDISABLE tNUM { break_enable_xpoint($2, FALSE); }
|
||||
| tDISABLE tBREAK tNUM { break_enable_xpoint($3, FALSE); }
|
||||
| tDELETE tNUM { break_delete_xpoint($2); }
|
||||
| tDELETE tBREAK tNUM { break_delete_xpoint($3); }
|
||||
;
|
||||
|
||||
watch_command:
|
||||
tWATCH '*' expr_lvalue { break_add_watch(&$3, 1); }
|
||||
| tWATCH identifier { break_add_watch_from_id($2); }
|
||||
;
|
||||
|
||||
display_command:
|
||||
tDISPLAY { display_info(); }
|
||||
| tDISPLAY expr { display_add($2, 1, 0, FALSE); }
|
||||
| tDISPLAY tFORMAT expr { display_add($3, $2 >> 8, $2 & 0xff, FALSE); }
|
||||
| tLOCAL tDISPLAY expr { display_add($3, 1, 0, TRUE); }
|
||||
| tLOCAL tDISPLAY tFORMAT expr { display_add($4, $3 >> 8, $3 & 0xff, TRUE); }
|
||||
| tENABLE tDISPLAY tNUM { display_enable($3, TRUE); }
|
||||
| tDISABLE tDISPLAY tNUM { display_enable($3, FALSE); }
|
||||
| tDELETE tDISPLAY tNUM { display_delete($3); }
|
||||
| tDELETE tDISPLAY { display_delete(-1); }
|
||||
| tUNDISPLAY tNUM { display_delete($2); }
|
||||
| tUNDISPLAY { display_delete(-1); }
|
||||
;
|
||||
|
||||
info_command:
|
||||
tINFO tBREAK { break_info(); }
|
||||
| tINFO tSHARE { info_win32_module(0); }
|
||||
| tINFO tSHARE expr_rvalue { info_win32_module($3); }
|
||||
| tINFO tREGS { be_cpu->print_context(dbg_curr_thread->handle, &dbg_context); }
|
||||
| tINFO tSEGMENTS expr_rvalue { info_win32_segments($3, 1); }
|
||||
| tINFO tSEGMENTS { info_win32_segments(0, -1); }
|
||||
| tINFO tSTACK { stack_info(); }
|
||||
| tINFO tSYMBOL tSTRING { symbol_info($3); }
|
||||
| tINFO tLOCAL { symbol_info_locals(); }
|
||||
| tINFO tDISPLAY { display_info(); }
|
||||
| tINFO tCLASS { info_win32_class(NULL, NULL); }
|
||||
| tINFO tCLASS tSTRING { info_win32_class(NULL, $3); }
|
||||
| tINFO tWND { info_win32_window(NULL, FALSE); }
|
||||
| tINFO tWND expr_rvalue { info_win32_window((HWND)$3, FALSE); }
|
||||
| tINFO '*' tWND { info_win32_window(NULL, TRUE); }
|
||||
| tINFO '*' tWND expr_rvalue { info_win32_window((HWND)$4, TRUE); }
|
||||
| tINFO tPROCESS { info_win32_processes(); }
|
||||
| tINFO tTHREAD { info_win32_threads(); }
|
||||
| tINFO tEXCEPTION { info_win32_exceptions(dbg_curr_tid); }
|
||||
| tINFO tEXCEPTION expr_rvalue { info_win32_exceptions($3); }
|
||||
| tINFO tMAPS { info_win32_virtual(dbg_curr_pid); }
|
||||
| tINFO tMAPS expr_rvalue { info_win32_virtual($3); }
|
||||
;
|
||||
|
||||
maintenance_command:
|
||||
tMAINTENANCE tTYPE { DEBUG_DumpTypes(); }
|
||||
tMAINTENANCE tTYPE { print_types(); }
|
||||
;
|
||||
|
||||
noprocess_state:
|
||||
tNOPROCESS tEOL {} /* <CR> shall not barf anything */
|
||||
| tNOPROCESS tSTRING tEOL { DEBUG_Printf("No process loaded, cannot execute '%s'\n", $2); }
|
||||
tNOPROCESS {} /* <CR> shall not barf anything */
|
||||
| tNOPROCESS tSTRING { dbg_printf("No process loaded, cannot execute '%s'\n", $2); }
|
||||
;
|
||||
|
||||
type_expr:
|
||||
type_expr '*' { $$ = $1 ? DEBUG_FindOrMakePointerType($1) : NULL; }
|
||||
| tINT { $$ = DEBUG_GetBasicType(DT_BASIC_INT); }
|
||||
| tCHAR { $$ = DEBUG_GetBasicType(DT_BASIC_CHAR); }
|
||||
| tLONG tINT { $$ = DEBUG_GetBasicType(DT_BASIC_LONGINT); }
|
||||
| tUNSIGNED tINT { $$ = DEBUG_GetBasicType(DT_BASIC_UINT); }
|
||||
| tLONG tUNSIGNED tINT { $$ = DEBUG_GetBasicType(DT_BASIC_ULONGINT); }
|
||||
| tLONG tLONG tINT { $$ = DEBUG_GetBasicType(DT_BASIC_LONGLONGINT); }
|
||||
| tLONG tLONG tUNSIGNED tINT{ $$ = DEBUG_GetBasicType(DT_BASIC_ULONGLONGINT); }
|
||||
| tSHORT tINT { $$ = DEBUG_GetBasicType(DT_BASIC_SHORTINT); }
|
||||
| tSHORT tUNSIGNED tINT { $$ = DEBUG_GetBasicType(DT_BASIC_USHORTINT); }
|
||||
| tSIGNED tCHAR { $$ = DEBUG_GetBasicType(DT_BASIC_SCHAR); }
|
||||
| tUNSIGNED tCHAR { $$ = DEBUG_GetBasicType(DT_BASIC_UCHAR); }
|
||||
| tFLOAT { $$ = DEBUG_GetBasicType(DT_BASIC_FLOAT); }
|
||||
| tDOUBLE { $$ = DEBUG_GetBasicType(DT_BASIC_DOUBLE); }
|
||||
| tLONG tDOUBLE { $$ = DEBUG_GetBasicType(DT_BASIC_LONGDOUBLE); }
|
||||
| tSTRUCT tIDENTIFIER { $$ = DEBUG_TypeCast(DT_STRUCT, $2); }
|
||||
| tUNION tIDENTIFIER { $$ = DEBUG_TypeCast(DT_STRUCT, $2); }
|
||||
| tENUM tIDENTIFIER { $$ = DEBUG_TypeCast(DT_ENUM, $2); }
|
||||
tCHAR { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_char; }
|
||||
| tINT { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_signed_int; }
|
||||
| tLONG tINT { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_signed_long_int; }
|
||||
| tLONG { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_signed_long_int; }
|
||||
| tUNSIGNED tINT { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_unsigned_int; }
|
||||
| tUNSIGNED { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_unsigned_int; }
|
||||
| tLONG tUNSIGNED tINT { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_unsigned_long_int; }
|
||||
| tLONG tUNSIGNED { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_unsigned_long_int; }
|
||||
| tSHORT tINT { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_signed_short_int; }
|
||||
| tSHORT { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_signed_short_int; }
|
||||
| tSHORT tUNSIGNED tINT { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_unsigned_short_int; }
|
||||
| tSHORT tUNSIGNED { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_unsigned_short_int; }
|
||||
| tSIGNED tCHAR { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_signed_char_int; }
|
||||
| tUNSIGNED tCHAR { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_unsigned_char_int; }
|
||||
| tLONG tLONG tUNSIGNED tINT{ $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_unsigned_longlong_int; }
|
||||
| tLONG tLONG tUNSIGNED { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_unsigned_longlong_int; }
|
||||
| tLONG tLONG tINT { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_signed_longlong_int; }
|
||||
| tLONG tLONG { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_signed_longlong_int; }
|
||||
| tFLOAT { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_short_real; }
|
||||
| tDOUBLE { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_real; }
|
||||
| tLONG tDOUBLE { $$.type = type_expr_type_id; $$.deref_count = 0; $$.u.typeid = dbg_itype_long_real; }
|
||||
| type_expr '*' { $$ = $1; $$.deref_count++; }
|
||||
| tCLASS identifier { $$.type = type_expr_udt_class; $$.deref_count = 0; $$.u.name = lexeme_alloc($2); }
|
||||
| tSTRUCT identifier { $$.type = type_expr_udt_struct; $$.deref_count = 0; $$.u.name = lexeme_alloc($2); }
|
||||
| tUNION identifier { $$.type = type_expr_udt_union; $$.deref_count = 0; $$.u.name = lexeme_alloc($2); }
|
||||
| tENUM identifier { $$.type = type_expr_enumeration; $$.deref_count = 0; $$.u.name = lexeme_alloc($2); }
|
||||
;
|
||||
|
||||
expr_addr: expr { $$ = DEBUG_EvalExpr($1); }
|
||||
expr_lvalue:
|
||||
expr { $$ = expr_eval($1); }
|
||||
;
|
||||
|
||||
expr_value: expr { DBG_VALUE value = DEBUG_EvalExpr($1);
|
||||
/* expr_value is typed as an integer */
|
||||
$$ = DEBUG_ReadMemory(&value); }
|
||||
expr_rvalue:
|
||||
expr_lvalue { $$ = types_extract_as_integer(&$1); }
|
||||
;
|
||||
|
||||
/*
|
||||
|
@ -312,149 +321,209 @@ expr_value: expr { DBG_VALUE value = DEBUG_EvalExpr($1);
|
|||
* use in 'display' commands, and in conditional watchpoints.
|
||||
*/
|
||||
expr:
|
||||
tNUM { $$ = DEBUG_ConstExpr($1); }
|
||||
| tSTRING { $$ = DEBUG_StringExpr($1); }
|
||||
| tINTVAR { $$ = DEBUG_IntVarExpr($1); }
|
||||
| tIDENTIFIER { $$ = DEBUG_SymbolExpr($1); }
|
||||
| expr OP_DRF tIDENTIFIER { $$ = DEBUG_StructPExpr($1, $3); }
|
||||
| expr '.' tIDENTIFIER { $$ = DEBUG_StructExpr($1, $3); }
|
||||
| tIDENTIFIER '(' ')' { $$ = DEBUG_CallExpr($1, 0); }
|
||||
| tIDENTIFIER '(' expr ')' { $$ = DEBUG_CallExpr($1, 1, $3); }
|
||||
| tIDENTIFIER '(' expr ',' expr ')' { $$ = DEBUG_CallExpr($1, 2, $3, $5); }
|
||||
| tIDENTIFIER '(' expr ',' expr ',' expr ')' { $$ = DEBUG_CallExpr($1, 3, $3, $5, $7); }
|
||||
| tIDENTIFIER '(' expr ',' expr ',' expr ',' expr ')' { $$ = DEBUG_CallExpr($1, 4, $3, $5, $7, $9); }
|
||||
| tIDENTIFIER '(' expr ',' expr ',' expr ',' expr ',' expr ')' { $$ = DEBUG_CallExpr($1, 5, $3, $5, $7, $9, $11); }
|
||||
| expr '[' expr ']' { $$ = DEBUG_BinopExpr(EXP_OP_ARR, $1, $3); }
|
||||
| expr ':' expr { $$ = DEBUG_BinopExpr(EXP_OP_SEG, $1, $3); }
|
||||
| expr OP_LOR expr { $$ = DEBUG_BinopExpr(EXP_OP_LOR, $1, $3); }
|
||||
| expr OP_LAND expr { $$ = DEBUG_BinopExpr(EXP_OP_LAND, $1, $3); }
|
||||
| expr '|' expr { $$ = DEBUG_BinopExpr(EXP_OP_OR, $1, $3); }
|
||||
| expr '&' expr { $$ = DEBUG_BinopExpr(EXP_OP_AND, $1, $3); }
|
||||
| expr '^' expr { $$ = DEBUG_BinopExpr(EXP_OP_XOR, $1, $3); }
|
||||
| expr OP_EQ expr { $$ = DEBUG_BinopExpr(EXP_OP_EQ, $1, $3); }
|
||||
| expr '>' expr { $$ = DEBUG_BinopExpr(EXP_OP_GT, $1, $3); }
|
||||
| expr '<' expr { $$ = DEBUG_BinopExpr(EXP_OP_LT, $1, $3); }
|
||||
| expr OP_GE expr { $$ = DEBUG_BinopExpr(EXP_OP_GE, $1, $3); }
|
||||
| expr OP_LE expr { $$ = DEBUG_BinopExpr(EXP_OP_LE, $1, $3); }
|
||||
| expr OP_NE expr { $$ = DEBUG_BinopExpr(EXP_OP_NE, $1, $3); }
|
||||
| expr OP_SHL expr { $$ = DEBUG_BinopExpr(EXP_OP_SHL, $1, $3); }
|
||||
| expr OP_SHR expr { $$ = DEBUG_BinopExpr(EXP_OP_SHR, $1, $3); }
|
||||
| expr '+' expr { $$ = DEBUG_BinopExpr(EXP_OP_ADD, $1, $3); }
|
||||
| expr '-' expr { $$ = DEBUG_BinopExpr(EXP_OP_SUB, $1, $3); }
|
||||
| expr '*' expr { $$ = DEBUG_BinopExpr(EXP_OP_MUL, $1, $3); }
|
||||
| expr '/' expr { $$ = DEBUG_BinopExpr(EXP_OP_DIV, $1, $3); }
|
||||
| expr '%' expr { $$ = DEBUG_BinopExpr(EXP_OP_REM, $1, $3); }
|
||||
| '-' expr %prec OP_SIGN { $$ = DEBUG_UnopExpr(EXP_OP_NEG, $2); }
|
||||
tNUM { $$ = expr_alloc_sconstant($1); }
|
||||
| tSTRING { $$ = expr_alloc_string($1); }
|
||||
| tINTVAR { $$ = expr_alloc_internal_var($1); }
|
||||
| identifier { $$ = expr_alloc_symbol($1); }
|
||||
| expr OP_DRF tIDENTIFIER { $$ = expr_alloc_pstruct($1, $3); }
|
||||
| expr '.' tIDENTIFIER { $$ = expr_alloc_struct($1, $3); }
|
||||
| identifier '(' ')' { $$ = expr_alloc_func_call($1, 0); }
|
||||
| identifier '(' expr ')' { $$ = expr_alloc_func_call($1, 1, $3); }
|
||||
| identifier '(' expr ',' expr ')' { $$ = expr_alloc_func_call($1, 2, $3, $5); }
|
||||
| identifier '(' expr ',' expr ',' expr ')' { $$ = expr_alloc_func_call($1, 3, $3, $5, $7); }
|
||||
| identifier '(' expr ',' expr ',' expr ',' expr ')' { $$ = expr_alloc_func_call($1, 4, $3, $5, $7, $9); }
|
||||
| identifier '(' expr ',' expr ',' expr ',' expr ',' expr ')' { $$ = expr_alloc_func_call($1, 5, $3, $5, $7, $9, $11); }
|
||||
| expr '[' expr ']' { $$ = expr_alloc_binary_op(EXP_OP_ARR, $1, $3); }
|
||||
| expr ':' expr { $$ = expr_alloc_binary_op(EXP_OP_SEG, $1, $3); }
|
||||
| expr OP_LOR expr { $$ = expr_alloc_binary_op(EXP_OP_LOR, $1, $3); }
|
||||
| expr OP_LAND expr { $$ = expr_alloc_binary_op(EXP_OP_LAND, $1, $3); }
|
||||
| expr '|' expr { $$ = expr_alloc_binary_op(EXP_OP_OR, $1, $3); }
|
||||
| expr '&' expr { $$ = expr_alloc_binary_op(EXP_OP_AND, $1, $3); }
|
||||
| expr '^' expr { $$ = expr_alloc_binary_op(EXP_OP_XOR, $1, $3); }
|
||||
| expr OP_EQ expr { $$ = expr_alloc_binary_op(EXP_OP_EQ, $1, $3); }
|
||||
| expr '>' expr { $$ = expr_alloc_binary_op(EXP_OP_GT, $1, $3); }
|
||||
| expr '<' expr { $$ = expr_alloc_binary_op(EXP_OP_LT, $1, $3); }
|
||||
| expr OP_GE expr { $$ = expr_alloc_binary_op(EXP_OP_GE, $1, $3); }
|
||||
| expr OP_LE expr { $$ = expr_alloc_binary_op(EXP_OP_LE, $1, $3); }
|
||||
| expr OP_NE expr { $$ = expr_alloc_binary_op(EXP_OP_NE, $1, $3); }
|
||||
| expr OP_SHL expr { $$ = expr_alloc_binary_op(EXP_OP_SHL, $1, $3); }
|
||||
| expr OP_SHR expr { $$ = expr_alloc_binary_op(EXP_OP_SHR, $1, $3); }
|
||||
| expr '+' expr { $$ = expr_alloc_binary_op(EXP_OP_ADD, $1, $3); }
|
||||
| expr '-' expr { $$ = expr_alloc_binary_op(EXP_OP_SUB, $1, $3); }
|
||||
| expr '*' expr { $$ = expr_alloc_binary_op(EXP_OP_MUL, $1, $3); }
|
||||
| expr '/' expr { $$ = expr_alloc_binary_op(EXP_OP_DIV, $1, $3); }
|
||||
| expr '%' expr { $$ = expr_alloc_binary_op(EXP_OP_REM, $1, $3); }
|
||||
| '-' expr %prec OP_SIGN { $$ = expr_alloc_unary_op(EXP_OP_NEG, $2); }
|
||||
| '+' expr %prec OP_SIGN { $$ = $2; }
|
||||
| '!' expr { $$ = DEBUG_UnopExpr(EXP_OP_NOT, $2); }
|
||||
| '~' expr { $$ = DEBUG_UnopExpr(EXP_OP_LNOT, $2); }
|
||||
| '!' expr { $$ = expr_alloc_unary_op(EXP_OP_NOT, $2); }
|
||||
| '~' expr { $$ = expr_alloc_unary_op(EXP_OP_LNOT, $2); }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| '*' expr %prec OP_DEREF { $$ = DEBUG_UnopExpr(EXP_OP_DEREF, $2); }
|
||||
| '&' expr %prec OP_DEREF { $$ = DEBUG_UnopExpr(EXP_OP_ADDR, $2); }
|
||||
| '(' type_expr ')' expr %prec OP_DEREF { $$ = DEBUG_TypeCastExpr($2, $4); }
|
||||
| '*' expr %prec OP_DEREF { $$ = expr_alloc_unary_op(EXP_OP_DEREF, $2); }
|
||||
| '&' expr %prec OP_DEREF { $$ = expr_alloc_unary_op(EXP_OP_ADDR, $2); }
|
||||
| '(' type_expr ')' expr %prec OP_DEREF { $$ = expr_alloc_typecast(&$2, $4); }
|
||||
;
|
||||
|
||||
/*
|
||||
* The lvalue rule builds an expression tree. This is a limited form
|
||||
* of expression that is suitable to be used as an lvalue.
|
||||
*/
|
||||
lval_addr: lval { $$ = DEBUG_EvalExpr($1); }
|
||||
lvalue_addr:
|
||||
lvalue { $$ = expr_eval($1); }
|
||||
;
|
||||
|
||||
lval: lvalue { $$ = $1; }
|
||||
| '*' expr { $$ = DEBUG_UnopExpr(EXP_OP_FORCE_DEREF, $2); }
|
||||
;
|
||||
|
||||
lvalue: tNUM { $$ = DEBUG_ConstExpr($1); }
|
||||
| tINTVAR { $$ = DEBUG_IntVarExpr($1); }
|
||||
| tIDENTIFIER { $$ = DEBUG_SymbolExpr($1); }
|
||||
| lvalue OP_DRF tIDENTIFIER { $$ = DEBUG_StructPExpr($1, $3); }
|
||||
| lvalue '.' tIDENTIFIER { $$ = DEBUG_StructExpr($1, $3); }
|
||||
| lvalue '[' expr ']' { $$ = DEBUG_BinopExpr(EXP_OP_ARR, $1, $3); }
|
||||
;
|
||||
|
||||
identifier: tIDENTIFIER { $$ = $1; }
|
||||
| identifier '.' tIDENTIFIER { char* ptr = DBG_alloc(strlen($1) + 1 + strlen($3)+ 1);
|
||||
sprintf(ptr, "%s.%s", $1, $3); $$ = DEBUG_MakeSymbol(ptr);
|
||||
DBG_free(ptr); }
|
||||
| identifier ':' ':' tIDENTIFIER { char* ptr = DBG_alloc(strlen($1) + 2 + strlen($4) + 1);
|
||||
sprintf(ptr, "%s::%s", $1, $4); $$ = DEBUG_MakeSymbol(ptr);
|
||||
DBG_free(ptr); }
|
||||
lvalue: tNUM { $$ = expr_alloc_sconstant($1); }
|
||||
| tINTVAR { $$ = expr_alloc_internal_var($1); }
|
||||
| identifier { $$ = expr_alloc_symbol($1); }
|
||||
| lvalue OP_DRF tIDENTIFIER { $$ = expr_alloc_pstruct($1, $3); }
|
||||
| lvalue '.' tIDENTIFIER { $$ = expr_alloc_struct($1, $3); }
|
||||
| lvalue '[' expr ']' { $$ = expr_alloc_binary_op(EXP_OP_ARR, $1, $3); }
|
||||
| '*' expr { $$ = expr_alloc_unary_op(EXP_OP_FORCE_DEREF, $2); }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
static void mode_command(int newmode)
|
||||
{
|
||||
switch(newmode)
|
||||
{
|
||||
case 16: DEBUG_CurrThread->dbg_mode = MODE_16; break;
|
||||
case 32: DEBUG_CurrThread->dbg_mode = MODE_32; break;
|
||||
default: DEBUG_Printf("Invalid mode (use 16, 32 or vm86)\n");
|
||||
}
|
||||
}
|
||||
|
||||
void DEBUG_Exit(DWORD ec)
|
||||
{
|
||||
ExitProcess(ec);
|
||||
}
|
||||
|
||||
static WINE_EXCEPTION_FILTER(wine_dbg_cmd)
|
||||
{
|
||||
if (DBG_IVAR(ExtDbgOnInternalException))
|
||||
DEBUG_ExternalDebugger();
|
||||
DEBUG_Printf("\nwine_dbg_cmd: ");
|
||||
switch (GetExceptionCode()) {
|
||||
switch (GetExceptionCode())
|
||||
{
|
||||
case DEBUG_STATUS_INTERNAL_ERROR:
|
||||
DEBUG_Printf("WineDbg internal error\n");
|
||||
if (DBG_IVAR(ExtDbgOnInternalException))
|
||||
DEBUG_ExternalDebugger();
|
||||
dbg_printf("\nWineDbg internal error\n");
|
||||
break;
|
||||
case DEBUG_STATUS_NO_SYMBOL:
|
||||
DEBUG_Printf("Undefined symbol\n");
|
||||
dbg_printf("\nUndefined symbol\n");
|
||||
break;
|
||||
case DEBUG_STATUS_DIV_BY_ZERO:
|
||||
DEBUG_Printf("Division by zero\n");
|
||||
dbg_printf("\nDivision by zero\n");
|
||||
break;
|
||||
case DEBUG_STATUS_BAD_TYPE:
|
||||
DEBUG_Printf("No type or type mismatch\n");
|
||||
dbg_printf("\nNo type or type mismatch\n");
|
||||
break;
|
||||
case DEBUG_STATUS_NO_FIELD:
|
||||
DEBUG_Printf("No such field in structure or union\n");
|
||||
dbg_printf("\nNo such field in structure or union\n");
|
||||
break;
|
||||
case DEBUG_STATUS_CANT_DEREF:
|
||||
dbg_printf("\nDereference failed (not a pointer, or out of array bounds)\n");
|
||||
break;
|
||||
case DEBUG_STATUS_ABORT:
|
||||
break;
|
||||
case DEBUG_STATUS_NOT_AN_INTEGER:
|
||||
dbg_printf("\nNeeding an integral value\n");
|
||||
break;
|
||||
case CONTROL_C_EXIT:
|
||||
/* this is generally sent by a ctrl-c when we run winedbg outside of wineconsole */
|
||||
DEBUG_Printf("Ctrl-C\n");
|
||||
/* stop the debuggee, and continue debugger execution, we will be reintered by the
|
||||
dbg_printf("\nCtrl-C\n");
|
||||
/* stop the debuggee, and continue debugger execution, we will be reentered by the
|
||||
* debug events generated by stopping
|
||||
*/
|
||||
DEBUG_InterruptDebuggee();
|
||||
dbg_interrupt_debuggee();
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
default:
|
||||
DEBUG_Printf("Exception %lx\n", GetExceptionCode());
|
||||
DEBUG_ExternalDebugger();
|
||||
dbg_printf("\nException %lx\n", GetExceptionCode());
|
||||
break;
|
||||
}
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
static void set_default_channels(void)
|
||||
#ifndef whitespace
|
||||
#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
|
||||
#endif
|
||||
|
||||
/* Strip whitespace from the start and end of STRING. */
|
||||
static void stripwhite(char *string)
|
||||
{
|
||||
DEBUG_hParserOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DEBUG_hParserInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
int i, last;
|
||||
|
||||
for (i = 0; whitespace(string[i]); i++);
|
||||
if (i) strcpy(string, string + i);
|
||||
|
||||
last = i = strlen(string) - 1;
|
||||
if (string[last] == '\n') i--;
|
||||
|
||||
while (i > 0 && whitespace(string[i])) i--;
|
||||
if (string[last] == '\n')
|
||||
string[++i] = '\n';
|
||||
string[++i] = '\0';
|
||||
}
|
||||
|
||||
static HANDLE dbg_parser_input;
|
||||
static HANDLE dbg_parser_output;
|
||||
|
||||
int input_fetch_entire_line(const char* pfx, char** line, size_t* alloc, BOOL check_nl)
|
||||
{
|
||||
char buf_line[256];
|
||||
DWORD nread;
|
||||
size_t len;
|
||||
|
||||
/* as of today, console handles can be file handles... so better use file APIs rather than
|
||||
* console's
|
||||
*/
|
||||
WriteFile(dbg_parser_output, pfx, strlen(pfx), NULL, NULL);
|
||||
|
||||
len = 0;
|
||||
do
|
||||
{
|
||||
if (!ReadFile(dbg_parser_input, buf_line, sizeof(buf_line) - 1, &nread, NULL) || nread == 0)
|
||||
break;
|
||||
buf_line[nread] = '\0';
|
||||
|
||||
if (check_nl && len == 0 && nread == 1 && buf_line[0] == '\n')
|
||||
return 0;
|
||||
|
||||
/* store stuff at the end of last_line */
|
||||
if (len + nread + 1 > *alloc)
|
||||
{
|
||||
while (len + nread + 1 > *alloc) *alloc *= 2;
|
||||
*line = dbg_heap_realloc(*line, *alloc);
|
||||
}
|
||||
strcpy(*line + len, buf_line);
|
||||
len += nread;
|
||||
} while (nread == 0 || buf_line[nread - 1] != '\n');
|
||||
|
||||
if (!len)
|
||||
{
|
||||
*line = HeapReAlloc(GetProcessHeap(), 0, *line, *alloc = 1);
|
||||
**line = '\0';
|
||||
}
|
||||
|
||||
/* Remove leading and trailing whitespace from the line */
|
||||
stripwhite(*line);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int input_read_line(const char* pfx, char* buf, int size)
|
||||
{
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
/* first alloc of our current buffer */
|
||||
line = HeapAlloc(GetProcessHeap(), 0, len = 2);
|
||||
assert(line);
|
||||
line[0] = '\n';
|
||||
line[1] = '\0';
|
||||
|
||||
input_fetch_entire_line(pfx, &line, &len, FALSE);
|
||||
len = strlen(line);
|
||||
/* remove trailing \n */
|
||||
if (len > 0 && line[len - 1] == '\n') len--;
|
||||
len = min(size - 1, len);
|
||||
memcpy(buf, line, len);
|
||||
buf[len] = '\0';
|
||||
HeapFree(GetProcessHeap(), 0, line);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_Parser
|
||||
* parser
|
||||
*
|
||||
* Debugger command line parser
|
||||
*/
|
||||
void DEBUG_Parser(LPCSTR filename)
|
||||
void parser(const char* filename)
|
||||
{
|
||||
BOOL ret_ok;
|
||||
HANDLE in_copy = dbg_parser_input;
|
||||
HANDLE out_copy = dbg_parser_output;
|
||||
|
||||
#ifdef YYDEBUG
|
||||
yydebug = 0;
|
||||
#endif
|
||||
|
@ -463,16 +532,16 @@ void DEBUG_Parser(LPCSTR filename)
|
|||
|
||||
if (filename)
|
||||
{
|
||||
DEBUG_hParserOutput = 0;
|
||||
DEBUG_hParserInput = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0L, 0);
|
||||
if (DEBUG_hParserInput == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
set_default_channels();
|
||||
return;
|
||||
}
|
||||
HANDLE h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0L, 0);
|
||||
if (h == INVALID_HANDLE_VALUE) return;
|
||||
dbg_parser_output = 0;
|
||||
dbg_parser_input = h;
|
||||
}
|
||||
else
|
||||
set_default_channels();
|
||||
{
|
||||
dbg_parser_output = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
dbg_parser_input = GetStdHandle(STD_INPUT_HANDLE);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
|
@ -486,16 +555,16 @@ void DEBUG_Parser(LPCSTR filename)
|
|||
ret_ok = FALSE;
|
||||
}
|
||||
__ENDTRY;
|
||||
DEBUG_FlushSymbols();
|
||||
lexeme_flush();
|
||||
} while (!ret_ok);
|
||||
|
||||
if (filename)
|
||||
CloseHandle(DEBUG_hParserInput);
|
||||
set_default_channels();
|
||||
if (filename) CloseHandle(dbg_parser_input);
|
||||
dbg_parser_input = in_copy;
|
||||
dbg_parser_output = out_copy;
|
||||
}
|
||||
|
||||
int yyerror(const char* s)
|
||||
{
|
||||
DEBUG_Printf("%s\n", s);
|
||||
return 0;
|
||||
dbg_printf("%s\n", s);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -24,22 +24,46 @@
|
|||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "windef.h"
|
||||
#include "winbase.h"
|
||||
#include "wincon.h"
|
||||
#include "debugger.h"
|
||||
#include "y.tab.h"
|
||||
|
||||
#undef YY_INPUT
|
||||
|
||||
HANDLE DEBUG_hParserInput;
|
||||
HANDLE DEBUG_hParserOutput;
|
||||
static int read_input(const char* pfx, char* buf, int size)
|
||||
{
|
||||
size_t len;
|
||||
static char* last_line = NULL;
|
||||
static size_t last_line_size = 0;
|
||||
static size_t last_line_idx = 0;
|
||||
|
||||
static int DEBUG_FetchFromLine(const char* pfx, char* buf, int size);
|
||||
/* first alloc of our current buffer */
|
||||
if (!last_line)
|
||||
{
|
||||
last_line = HeapAlloc(GetProcessHeap(), 0, last_line_size = 2);
|
||||
assert(last_line);
|
||||
last_line[0] = '\n';
|
||||
last_line[1] = '\0';
|
||||
}
|
||||
|
||||
/* try first to fetch the remaining of an existing line */
|
||||
if (last_line_idx == 0)
|
||||
{
|
||||
/* no remaining chars to be read from last line, grab a brand new line up to '\n' */
|
||||
lexeme_flush();
|
||||
input_fetch_entire_line(pfx, &last_line, &last_line_size, TRUE);
|
||||
}
|
||||
|
||||
len = min(strlen(last_line + last_line_idx), size - 1);
|
||||
memcpy(buf, last_line + last_line_idx, len);
|
||||
buf[len] = '\0';
|
||||
if ((last_line_idx += len) >= strlen(last_line))
|
||||
last_line_idx = 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
#define YY_INPUT(buf,result,max_size) \
|
||||
if ( (result = DEBUG_FetchFromLine("Wine-dbg>", buf, max_size)) < 0 ) \
|
||||
YY_FATAL_ERROR( "FetchFromLine() in flex scanner failed" );
|
||||
if ((result = read_input("Wine-dbg>", buf, max_size)) < 0) \
|
||||
YY_FATAL_ERROR("read_input in flex scanner failed");
|
||||
|
||||
#define YY_NO_UNPUT
|
||||
|
||||
|
@ -59,7 +83,6 @@ STRING \"[^\n"]+\"
|
|||
%s HELP_CMD
|
||||
%s BD_CMD
|
||||
%s LOCAL_CMD
|
||||
%s WALK_CMD
|
||||
%s SHOW_CMD
|
||||
%s MODE_CMD
|
||||
%s MAINT_CMD
|
||||
|
@ -69,7 +92,7 @@ STRING \"[^\n"]+\"
|
|||
%x NOPROCESS
|
||||
%%
|
||||
/* set to special state when no process is loaded. */
|
||||
if (!DEBUG_CurrProcess && YYSTATE == INITIAL) {BEGIN(NOPROCESS);}
|
||||
if (!dbg_curr_process && YYSTATE == INITIAL) {BEGIN(NOPROCESS);}
|
||||
|
||||
<<EOF>> { return tEOF; }
|
||||
<*>\n { BEGIN(INITIAL); syntax_error = 0; return tEOL; }
|
||||
|
@ -92,17 +115,17 @@ STRING \"[^\n"]+\"
|
|||
{DIGIT}+ { sscanf(yytext, "%d", &yylval.integer); return tNUM; }
|
||||
|
||||
<FORMAT_EXPECTED>"/"{DIGIT}+{FORMAT} { char* last;
|
||||
yylval.integer = strtol( yytext+1, &last, 0 ) << 8;
|
||||
yylval.integer = strtol(yytext+1, &last, 0) << 8;
|
||||
yylval.integer |= *last;
|
||||
return tFORMAT; }
|
||||
|
||||
<FORMAT_EXPECTED>"/"{FORMAT} { yylval.integer = (1 << 8) | yytext[1]; return tFORMAT; }
|
||||
|
||||
{STRING} { yylval.string = DEBUG_MakeSymbol(yytext); return tSTRING; }
|
||||
{STRING} { yylval.string = lexeme_alloc(yytext); return tSTRING; }
|
||||
<ASTRING_EXPECTED>[^\n]+ { char* p = yytext; while (*p == ' ' || *p == '\t') p++;
|
||||
yylval.string = DEBUG_MakeSymbol(p); return tSTRING; }
|
||||
yylval.string = lexeme_alloc(p); return tSTRING; }
|
||||
|
||||
<INITIAL>info|inf|in { BEGIN(INFO_CMD); return tINFO; }
|
||||
<INITIAL,NOPROCESS>info|inf|in { BEGIN(INFO_CMD); return tINFO; }
|
||||
<INITIAL>up { BEGIN(NOCMD); return tUP; }
|
||||
<INITIAL>down|dow|do { BEGIN(NOCMD); return tDOWN; }
|
||||
<INITIAL>frame|fram|fra|fr { BEGIN(NOCMD); return tFRAME; }
|
||||
|
@ -117,9 +140,8 @@ STRING \"[^\n"]+\"
|
|||
<INITIAL>delete|delet|dele|del { BEGIN(BD_CMD); return tDELETE; }
|
||||
<INITIAL,NOPROCESS>quit|qui|qu|q { BEGIN(NOCMD); return tQUIT; }
|
||||
<INITIAL>set|se { BEGIN(NOCMD); return tSET; }
|
||||
<INITIAL,NOPROCESS>walk|w { BEGIN(WALK_CMD); return tWALK; }
|
||||
<INITIAL>x { BEGIN(FORMAT_EXPECTED); return tEXAM; }
|
||||
<INITIAL,NOPROCESS>help|hel|he|"?" { BEGIN(HELP_CMD); return tHELP; }
|
||||
<INITIAL,NOPROCESS>help|hel|he|"?" { BEGIN(HELP_CMD); return tHELP; }
|
||||
|
||||
<INITIAL>backtrace|backtrac|backtra|backt|back|bac|ba|bt { BEGIN(NOCMD); return tBACKTRACE; }
|
||||
<INITIAL>where|wher|whe { BEGIN(NOCMD); return tBACKTRACE; }
|
||||
|
@ -150,17 +172,16 @@ STRING \"[^\n"]+\"
|
|||
<NOPROCESS>attach|attac|atta|att { BEGIN(NOCMD); return tATTACH; }
|
||||
<INFO_CMD>share|shar|sha { return tSHARE; }
|
||||
<INFO_CMD>locals|local|loca|loc { return tLOCAL; }
|
||||
<INFO_CMD,WALK_CMD>class|clas|cla { return tCLASS; }
|
||||
<INFO_CMD,WALK_CMD>module|modul|modu|mod { return tMODULE; }
|
||||
<INFO_CMD,WALK_CMD>process|proces|proce|proc { return tPROCESS; }
|
||||
<INFO_CMD,WALK_CMD>threads|thread|threa|thre|thr|th { return tTHREAD; }
|
||||
<WALK_CMD>exception|except|exc|ex { return tEXCEPTION; }
|
||||
<INFO_CMD>class|clas|cla { return tCLASS; }
|
||||
<INFO_CMD>process|proces|proce|proc { return tPROCESS; }
|
||||
<INFO_CMD>threads|thread|threa|thre|thr|th { return tTHREAD; }
|
||||
<INFO_CMD>exception|except|exc|ex { return tEXCEPTION; }
|
||||
<INFO_CMD>registers|regs|reg|re { return tREGS; }
|
||||
<INFO_CMD>segments|segment|segm|seg|se { return tSEGMENTS; }
|
||||
<INFO_CMD>stack|stac|sta|st { return tSTACK; }
|
||||
<INFO_CMD>symbol|symbo|symb|sym { BEGIN(ASTRING_EXPECTED); return tSYMBOL; }
|
||||
<WALK_CMD>maps|map { return tMAPS; }
|
||||
<INFO_CMD,WALK_CMD>window|windo|wind|win|wnd { return tWND; }
|
||||
<INFO_CMD>maps|map { return tMAPS; }
|
||||
<INFO_CMD>window|windo|wind|win|wnd { return tWND; }
|
||||
<HELP_CMD>info|inf|in { return tINFO; }
|
||||
<MODE_CMD>vm86 { return tVM86; }
|
||||
<MAINT_CMD>type { return tTYPE; }
|
||||
|
@ -180,167 +201,39 @@ struct { return tSTRUCT; }
|
|||
union { return tUNION; }
|
||||
enum { return tENUM; }
|
||||
|
||||
{IDENTIFIER} { yylval.string = DEBUG_MakeSymbol(yytext); return tIDENTIFIER; }
|
||||
"$"{IDENTIFIER} { yylval.string = DEBUG_MakeSymbol(yytext+1); return tINTVAR; }
|
||||
{IDENTIFIER} { yylval.string = lexeme_alloc(yytext); return tIDENTIFIER; }
|
||||
"$"{IDENTIFIER} { yylval.string = lexeme_alloc(yytext+1); return tINTVAR; }
|
||||
|
||||
<PATH_EXPECTED>{PATHNAME} { yylval.string = DEBUG_MakeSymbol(yytext); return tPATH; }
|
||||
<PATH_EXPECTED>{PATHNAME} { yylval.string = lexeme_alloc(yytext); return tPATH; }
|
||||
|
||||
<*>[ \t]+ /* Eat up whitespace */
|
||||
|
||||
<NOPROCESS>. { BEGIN(ASTRING_EXPECTED); yyless(0); return tNOPROCESS;}
|
||||
<*>. { if (syntax_error == 0) {
|
||||
syntax_error++;
|
||||
DEBUG_Printf("Syntax Error (%s)\n", yytext); }
|
||||
}
|
||||
<*>. { if (syntax_error == 0) { syntax_error++; dbg_printf("Syntax Error (%s)\n", yytext); } }
|
||||
%%
|
||||
|
||||
#ifndef yywrap
|
||||
int yywrap(void) { return 1; }
|
||||
#endif
|
||||
|
||||
#ifndef whitespace
|
||||
#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
|
||||
#endif
|
||||
static char** local_lexemes /* = NULL */;
|
||||
static int next_lexeme /* = 0 */;
|
||||
static int alloc_lexeme /* = 0 */;
|
||||
|
||||
|
||||
/* Strip whitespace from the start and end of STRING. */
|
||||
static void stripwhite(char *string)
|
||||
char* lexeme_alloc(const char* lexeme)
|
||||
{
|
||||
int i, last;
|
||||
|
||||
for (i = 0; whitespace(string[i]); i++);
|
||||
if (i) strcpy(string, string + i);
|
||||
|
||||
last = i = strlen(string) - 1;
|
||||
if (string[last] == '\n') i--;
|
||||
|
||||
while (i > 0 && whitespace(string[i])) i--;
|
||||
if (string[last] == '\n')
|
||||
string[++i] = '\n';
|
||||
string[++i] = '\0';
|
||||
}
|
||||
|
||||
static int DEBUG_FetchEntireLine(const char* pfx, char** line, size_t* alloc, BOOL check_nl)
|
||||
{
|
||||
char buf_line[256];
|
||||
DWORD nread;
|
||||
size_t len;
|
||||
|
||||
/* as of today, console handles can be file handles... so better use file APIs rather than
|
||||
* consoles
|
||||
*/
|
||||
WriteFile(DEBUG_hParserOutput, pfx, strlen(pfx), NULL, NULL);
|
||||
|
||||
len = 0;
|
||||
do
|
||||
assert(0 <= next_lexeme && next_lexeme < alloc_lexeme + 1);
|
||||
if (next_lexeme >= alloc_lexeme)
|
||||
{
|
||||
if (!ReadFile(DEBUG_hParserInput, buf_line, sizeof(buf_line) - 1, &nread, NULL) || nread == 0)
|
||||
break;
|
||||
buf_line[nread] = '\0';
|
||||
|
||||
if (check_nl && len == 0 && nread == 1 && buf_line[0] == '\n')
|
||||
return 0;
|
||||
|
||||
/* store stuff at the end of last_line */
|
||||
if (len + nread + 1 > *alloc)
|
||||
{
|
||||
if (*line)
|
||||
*line = HeapReAlloc(GetProcessHeap(), 0, *line, *alloc += nread + 1);
|
||||
else
|
||||
*line = HeapAlloc(GetProcessHeap(), 0, *alloc += nread + 1);
|
||||
}
|
||||
strcpy(*line + len, buf_line);
|
||||
len += nread;
|
||||
} while (nread == 0 || buf_line[nread - 1] != '\n');
|
||||
|
||||
if (!len)
|
||||
{
|
||||
*line = HeapReAlloc(GetProcessHeap(), 0, *line, *alloc = 1);
|
||||
**line = '\0';
|
||||
alloc_lexeme += 32;
|
||||
local_lexemes = dbg_heap_realloc(local_lexemes, alloc_lexeme * sizeof(local_lexemes[0]));
|
||||
assert(local_lexemes);
|
||||
}
|
||||
|
||||
/* Remove leading and trailing whitespace from the line */
|
||||
stripwhite(*line);
|
||||
return 1;
|
||||
return local_lexemes[next_lexeme++] = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(lexeme) + 1), lexeme);
|
||||
}
|
||||
|
||||
static int DEBUG_FetchFromLine(const char* pfx, char* buf, int size)
|
||||
void lexeme_flush(void)
|
||||
{
|
||||
size_t len;
|
||||
static char* last_line = NULL;
|
||||
static size_t last_line_size = 0;
|
||||
static size_t last_line_idx = 0;
|
||||
|
||||
/* first alloc of our current buffer */
|
||||
if (!last_line)
|
||||
{
|
||||
last_line = HeapAlloc(GetProcessHeap(), 0, last_line_size = 2);
|
||||
assert(last_line);
|
||||
last_line[0] = '\n';
|
||||
last_line[1] = '\0';
|
||||
}
|
||||
|
||||
/* try first to fetch the remaining of an existing line */
|
||||
if (last_line_idx == 0)
|
||||
{
|
||||
/* no remaining chars to be read from last line, grab a brand new line up to '\n' */
|
||||
DEBUG_FlushSymbols();
|
||||
|
||||
DEBUG_FetchEntireLine(pfx, &last_line, &last_line_size, TRUE);
|
||||
}
|
||||
|
||||
len = min(strlen(last_line + last_line_idx), size - 1);
|
||||
memcpy(buf, last_line + last_line_idx, len);
|
||||
buf[len] = '\0';
|
||||
if ((last_line_idx += len) >= strlen(last_line))
|
||||
last_line_idx = 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
int DEBUG_ReadLine(const char* pfx, char* buf, int size)
|
||||
{
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
/* first alloc of our current buffer */
|
||||
line = HeapAlloc(GetProcessHeap(), 0, len = 2);
|
||||
assert(line);
|
||||
line[0] = '\n';
|
||||
line[1] = '\0';
|
||||
|
||||
DEBUG_FetchEntireLine(pfx, &line, &len, FALSE);
|
||||
len = strlen(line);
|
||||
/* remove trailing \n */
|
||||
if (len > 0 && line[len - 1] == '\n') len--;
|
||||
len = min(size - 1, len);
|
||||
memcpy(buf, line, len);
|
||||
buf[len] = '\0';
|
||||
HeapFree(GetProcessHeap(), 0, line);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char** local_symbols /* = NULL */;
|
||||
static int next_symbol /* = 0 */;
|
||||
static int alloc_symbol /* = 0 */;
|
||||
|
||||
char* DEBUG_MakeSymbol(const char* symbol)
|
||||
{
|
||||
assert(0 <= next_symbol && next_symbol < alloc_symbol + 1);
|
||||
if (next_symbol >= alloc_symbol)
|
||||
{
|
||||
if (!local_symbols)
|
||||
local_symbols = HeapAlloc(GetProcessHeap(), 0,
|
||||
(alloc_symbol += 32) * sizeof(local_symbols[0]));
|
||||
else
|
||||
local_symbols = HeapReAlloc(GetProcessHeap(), 0, local_symbols,
|
||||
(alloc_symbol += 32) * sizeof(local_symbols[0]));
|
||||
assert(local_symbols);
|
||||
}
|
||||
return local_symbols[next_symbol++] = DBG_strdup(symbol);
|
||||
}
|
||||
|
||||
void DEBUG_FlushSymbols(void)
|
||||
{
|
||||
while(--next_symbol >= 0) DBG_free(local_symbols[next_symbol]);
|
||||
next_symbol = 0;
|
||||
while (--next_lexeme >= 0) HeapFree(GetProcessHeap(), 0, local_lexemes[next_lexeme]);
|
||||
next_lexeme = 0;
|
||||
}
|
||||
|
|
|
@ -24,162 +24,107 @@
|
|||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define NONAMELESSUNION
|
||||
#define NONAMELESSSTRUCT
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include "windef.h"
|
||||
#include "winbase.h"
|
||||
#include "ntstatus.h"
|
||||
#include "winver.h"
|
||||
#include "dbghelp.h"
|
||||
#include "cvconst.h"
|
||||
#include "oaidl.h"
|
||||
|
||||
#ifdef __i386__
|
||||
#define STEP_FLAG 0x00000100 /* single step flag */
|
||||
#define V86_FLAG 0x00020000
|
||||
#endif
|
||||
|
||||
#define SYM_FUNC 0x0
|
||||
#define SYM_DATA 0x1
|
||||
#define SYM_WIN32 0x2
|
||||
#define SYM_WINE 0x4
|
||||
#define SYM_INVALID 0x8
|
||||
#define SYM_TRAMPOLINE 0x10
|
||||
#define SYM_STEP_THROUGH 0x20
|
||||
|
||||
enum debug_type {DT_BASIC, DT_POINTER, DT_ARRAY, DT_STRUCT, DT_ENUM,
|
||||
DT_FUNC, DT_BITFIELD};
|
||||
|
||||
enum debug_type_basic {DT_BASIC_INT = 1, DT_BASIC_CHAR, DT_BASIC_LONGINT, DT_BASIC_UINT,
|
||||
DT_BASIC_ULONGINT, DT_BASIC_LONGLONGINT, DT_BASIC_ULONGLONGINT,
|
||||
DT_BASIC_SHORTINT, DT_BASIC_USHORTINT, DT_BASIC_SCHAR, DT_BASIC_UCHAR,
|
||||
DT_BASIC_FLOAT, DT_BASIC_LONGDOUBLE, DT_BASIC_DOUBLE,
|
||||
DT_BASIC_CMPLX_INT, DT_BASIC_CMPLX_FLOAT, DT_BASIC_CMPLX_DOUBLE,
|
||||
DT_BASIC_CMPLX_LONGDOUBLE, DT_BASIC_VOID,
|
||||
/* modifier on size isn't possible on current types definitions
|
||||
* so we need to add more types... */
|
||||
DT_BASIC_BOOL1, DT_BASIC_BOOL2, DT_BASIC_BOOL4,
|
||||
/* this is not really a basic type... */
|
||||
DT_BASIC_STRING,
|
||||
/* this is for historical reasons... should take care of it RSN */
|
||||
DT_BASIC_CONST_INT,
|
||||
/* not so basic, but handy */
|
||||
DT_BASIC_CONTEXT,
|
||||
/* to be kept as last... sentinel entry... do not use */
|
||||
DT_BASIC_LAST};
|
||||
/* the debugger uses these exceptions for it's internal use */
|
||||
#define DEBUG_STATUS_OFFSET 0x80003000
|
||||
#define DEBUG_STATUS_INTERNAL_ERROR (DEBUG_STATUS_OFFSET+0) /* something went wrong */
|
||||
#define DEBUG_STATUS_NO_SYMBOL (DEBUG_STATUS_OFFSET+1) /* no symbol found in lookup */
|
||||
#define DEBUG_STATUS_DIV_BY_ZERO (DEBUG_STATUS_OFFSET+2)
|
||||
#define DEBUG_STATUS_BAD_TYPE (DEBUG_STATUS_OFFSET+3) /* no type found, when type was expected */
|
||||
#define DEBUG_STATUS_NO_FIELD (DEBUG_STATUS_OFFSET+4) /* when dereferencing a struct, the field was not found */
|
||||
#define DEBUG_STATUS_ABORT (DEBUG_STATUS_OFFSET+5) /* user aborted on going action */
|
||||
#define DEBUG_STATUS_CANT_DEREF (DEBUG_STATUS_OFFSET+6) /* either not deref:able, or index out of bounds */
|
||||
#define DEBUG_STATUS_NOT_AN_INTEGER (DEBUG_STATUS_OFFSET+7) /* requiring an integral value */
|
||||
|
||||
/*
|
||||
* Return values for DEBUG_CheckLinenoStatus. Used to determine
|
||||
* Return values for symbol_get_function_line_status. Used to determine
|
||||
* what to do when the 'step' command is given.
|
||||
*/
|
||||
#define FUNC_HAS_NO_LINES (0)
|
||||
#define NOT_ON_LINENUMBER (1)
|
||||
#define AT_LINENUMBER (2)
|
||||
#define FUNC_IS_TRAMPOLINE (3)
|
||||
|
||||
typedef struct
|
||||
enum dbg_line_status
|
||||
{
|
||||
DWORD seg; /* 0xffffffff means current default segment (cs or ds) */
|
||||
DWORD off;
|
||||
} DBG_ADDR;
|
||||
dbg_no_line_info,
|
||||
dbg_not_on_a_line_number,
|
||||
dbg_on_a_line_number,
|
||||
dbg_in_a_thunk,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
enum dbg_internal_types
|
||||
{
|
||||
struct datatype* type;
|
||||
int cookie; /* DV_??? */
|
||||
/* DV_TARGET references an address in debugger's address space, whereas DV_HOST
|
||||
* references the debuggee address space
|
||||
dbg_itype_first = 0xffffff00,
|
||||
dbg_itype_unsigned_int,
|
||||
dbg_itype_signed_int,
|
||||
dbg_itype_signed_char_int,
|
||||
dbg_itype_unsigned_char_int,
|
||||
dbg_itype_unsigned_short_int,
|
||||
dbg_itype_signed_short_int,
|
||||
dbg_itype_unsigned_long_int,
|
||||
dbg_itype_signed_long_int,
|
||||
dbg_itype_unsigned_longlong_int,
|
||||
dbg_itype_signed_longlong_int,
|
||||
dbg_itype_char,
|
||||
dbg_itype_wchar,
|
||||
dbg_itype_short_real, /* aka float */
|
||||
dbg_itype_real, /* aka double */
|
||||
dbg_itype_long_real, /* aka long double */
|
||||
dbg_itype_astring,
|
||||
dbg_itype_ustring,
|
||||
dbg_itype_none = 0xffffffff
|
||||
};
|
||||
|
||||
struct dbg_lvalue /* structure to hold left-values... */
|
||||
{
|
||||
int cookie; /* DLV_??? */
|
||||
/* DLV_TARGET references an address in debuggee's address space, whereas DLV_HOST
|
||||
* references the winedbg's address space
|
||||
*/
|
||||
# define DV_TARGET 0xF00D
|
||||
# define DV_HOST 0x50DA
|
||||
# define DV_INVALID 0x0000
|
||||
# define DLV_TARGET 0xF00D
|
||||
# define DLV_HOST 0x50DA
|
||||
ADDRESS addr;
|
||||
unsigned long typeid;
|
||||
};
|
||||
|
||||
DBG_ADDR addr;
|
||||
} DBG_VALUE;
|
||||
|
||||
struct list_id
|
||||
enum dbg_exec_mode
|
||||
{
|
||||
char * sourcefile;
|
||||
int line;
|
||||
};
|
||||
|
||||
struct wine_lines {
|
||||
unsigned long line_number;
|
||||
DBG_ADDR pc_offset;
|
||||
};
|
||||
|
||||
struct symbol_info
|
||||
{
|
||||
struct name_hash * sym;
|
||||
struct list_id list;
|
||||
};
|
||||
|
||||
typedef struct wine_lines WineLineNo;
|
||||
|
||||
/*
|
||||
* This structure holds information about stack variables, function
|
||||
* parameters, and register variables, which are all local to this
|
||||
* function.
|
||||
*/
|
||||
struct wine_locals {
|
||||
unsigned int regno:8; /* For register symbols */
|
||||
signed int offset:24; /* offset from esp/ebp to symbol */
|
||||
unsigned int pc_start; /* For RBRAC/LBRAC */
|
||||
unsigned int pc_end; /* For RBRAC/LBRAC */
|
||||
char * name; /* Name of symbol */
|
||||
struct datatype * type; /* Datatype of symbol */
|
||||
};
|
||||
|
||||
typedef struct wine_locals WineLocals;
|
||||
|
||||
enum exec_mode
|
||||
{
|
||||
EXEC_CONT, /* Continuous execution */
|
||||
EXEC_STEP_OVER, /* Stepping over a call to next source line */
|
||||
EXEC_STEP_INSTR, /* Step to next source line, stepping in if needed */
|
||||
EXEC_STEPI_OVER, /* Stepping over a call */
|
||||
EXEC_STEPI_INSTR, /* Single-stepping an instruction */
|
||||
EXEC_FINISH, /* Step until we exit current frame */
|
||||
EXEC_STEP_OVER_TRAMPOLINE, /* Step over trampoline. Requires that
|
||||
* we dig the real return value off the stack
|
||||
* and set breakpoint there - not at the
|
||||
* instr just after the call.
|
||||
dbg_exec_cont, /* Continue execution */
|
||||
dbg_exec_step_over_line, /* Stepping over a call to next source line */
|
||||
dbg_exec_step_into_line, /* Step to next source line, stepping in if needed */
|
||||
dbg_exec_step_over_insn, /* Stepping over a call */
|
||||
dbg_exec_step_into_insn, /* Single-stepping an instruction */
|
||||
dbg_exec_finish, /* Single-step until we exit current frame */
|
||||
#if 0
|
||||
EXEC_STEP_OVER_TRAMPOLINE, /* Step over trampoline. Requires that we dig the real
|
||||
* return value off the stack and set breakpoint there -
|
||||
* not at the instr just after the call.
|
||||
*/
|
||||
#endif
|
||||
};
|
||||
|
||||
enum dbg_mode
|
||||
struct dbg_breakpoint
|
||||
{
|
||||
MODE_INVALID, MODE_16, MODE_32, MODE_VM86
|
||||
ADDRESS addr;
|
||||
unsigned long enabled : 1,
|
||||
xpoint_type : 2,
|
||||
refcount : 13,
|
||||
skipcount : 16;
|
||||
DWORD info;
|
||||
struct /* only used for watchpoints */
|
||||
{
|
||||
BYTE len : 2;
|
||||
DWORD oldval;
|
||||
} w;
|
||||
struct expr* condition;
|
||||
};
|
||||
|
||||
enum exit_mode /* of exception handling */
|
||||
{
|
||||
EXIT_CONTINUE, /* continue execution */
|
||||
EXIT_PASS, /* pass exception back to app (1st chance) */
|
||||
EXIT_DETACH, /* detach debugger */
|
||||
EXIT_QUIT, /* exit debugger and kill debuggee */
|
||||
};
|
||||
|
||||
#define DBG_BREAK 0
|
||||
#define DBG_WATCH 1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DBG_ADDR addr;
|
||||
WORD enabled : 1,
|
||||
type : 1,
|
||||
is32 : 1,
|
||||
refcount : 13;
|
||||
WORD skipcount;
|
||||
union {
|
||||
struct {
|
||||
BYTE opcode;
|
||||
BOOL (*func)(void);
|
||||
} b;
|
||||
struct {
|
||||
BYTE rw : 1,
|
||||
len : 2;
|
||||
BYTE reg;
|
||||
DWORD oldval;
|
||||
} w;
|
||||
} u;
|
||||
struct expr * condition;
|
||||
} DBG_BREAKPOINT;
|
||||
|
||||
/* Wine extension; Windows doesn't have a name for this code. This is an
|
||||
undocumented exception understood by MS VC debugger, allowing the program
|
||||
to name a particular thread. Search google.com or deja.com for "0x406d1388"
|
||||
|
@ -195,403 +140,279 @@ typedef struct tagTHREADNAME_INFO
|
|||
DWORD dwFlags; /* Reserved for future use. Must be zero. */
|
||||
} THREADNAME_INFO;
|
||||
|
||||
typedef struct tagDBG_THREAD {
|
||||
struct tagDBG_PROCESS* process;
|
||||
struct dbg_thread
|
||||
{
|
||||
struct dbg_process* process;
|
||||
HANDLE handle;
|
||||
DWORD tid;
|
||||
LPVOID start;
|
||||
LPVOID teb;
|
||||
void* teb;
|
||||
int wait_for_first_exception;
|
||||
enum exec_mode exec_mode; /* mode the thread is run (step/run...) */
|
||||
enum dbg_exec_mode exec_mode; /* mode the thread is run (step/run...) */
|
||||
int exec_count; /* count of mode operations */
|
||||
enum dbg_mode dbg_mode; /* mode (VM86, 32bit, 16bit) */
|
||||
DBG_BREAKPOINT stepOverBP;
|
||||
ADDRESS_MODE addr_mode; /* mode */
|
||||
struct dbg_breakpoint step_over_bp;
|
||||
char name[9];
|
||||
struct tagDBG_THREAD* next;
|
||||
struct tagDBG_THREAD* prev;
|
||||
} DBG_THREAD;
|
||||
struct dbg_thread* next;
|
||||
struct dbg_thread* prev;
|
||||
};
|
||||
|
||||
typedef struct tagDBG_DELAYED_BP {
|
||||
struct dbg_delayed_bp
|
||||
{
|
||||
BOOL is_symbol;
|
||||
union {
|
||||
struct {
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
int lineno;
|
||||
char* name;
|
||||
} symbol;
|
||||
DBG_VALUE value;
|
||||
ADDRESS addr;
|
||||
} u;
|
||||
} DBG_DELAYED_BP;
|
||||
};
|
||||
|
||||
typedef struct tagDBG_PROCESS {
|
||||
#define MAX_BREAKPOINTS 100
|
||||
struct dbg_process
|
||||
{
|
||||
HANDLE handle;
|
||||
DWORD pid;
|
||||
const char* imageName;
|
||||
DBG_THREAD* threads;
|
||||
int num_threads;
|
||||
struct dbg_thread* threads;
|
||||
unsigned continue_on_first_exception;
|
||||
struct tagDBG_MODULE** modules;
|
||||
int num_modules;
|
||||
unsigned long dbg_hdr_addr;
|
||||
DBG_DELAYED_BP* delayed_bp;
|
||||
struct dbg_breakpoint bp[MAX_BREAKPOINTS];
|
||||
unsigned next_bp;
|
||||
struct dbg_delayed_bp* delayed_bp;
|
||||
int num_delayed_bp;
|
||||
/*
|
||||
* This is an index we use to keep track of the debug information
|
||||
* when we have multiple sources. We use the same database to also
|
||||
* allow us to do an 'info shared' type of deal, and we use the index
|
||||
* to eliminate duplicates.
|
||||
*/
|
||||
int next_index;
|
||||
struct tagDBG_PROCESS* next;
|
||||
struct tagDBG_PROCESS* prev;
|
||||
} DBG_PROCESS;
|
||||
|
||||
extern DBG_PROCESS* DEBUG_CurrProcess;
|
||||
extern DBG_THREAD* DEBUG_CurrThread;
|
||||
extern DWORD DEBUG_CurrTid;
|
||||
extern DWORD DEBUG_CurrPid;
|
||||
extern CONTEXT DEBUG_context;
|
||||
extern BOOL DEBUG_InteractiveP;
|
||||
extern enum exit_mode DEBUG_ExitMode;
|
||||
extern HANDLE DEBUG_hParserInput;
|
||||
extern HANDLE DEBUG_hParserOutput;
|
||||
|
||||
#define DEBUG_READ_MEM(addr, buf, len) \
|
||||
(ReadProcessMemory(DEBUG_CurrProcess->handle, (addr), (buf), (len), NULL))
|
||||
|
||||
#define DEBUG_WRITE_MEM(addr, buf, len) \
|
||||
(WriteProcessMemory(DEBUG_CurrProcess->handle, (addr), (buf), (len), NULL))
|
||||
|
||||
#define DEBUG_READ_MEM_VERBOSE(addr, buf, len) \
|
||||
(DEBUG_READ_MEM((addr), (buf), (len)) || (DEBUG_InvalLinAddr( addr ),0))
|
||||
|
||||
#define DEBUG_WRITE_MEM_VERBOSE(addr, buf, len) \
|
||||
(DEBUG_WRITE_MEM((addr), (buf), (len)) || (DEBUG_InvalLinAddr( addr ),0))
|
||||
|
||||
enum DbgInfoLoad {DIL_DEFERRED, DIL_LOADED, DIL_NOINFO, DIL_NOT_SUPPORTED, DIL_ERROR};
|
||||
enum DbgModuleType {DMT_UNKNOWN, DMT_ELF, DMT_NE, DMT_PE};
|
||||
|
||||
typedef struct tagDBG_MODULE {
|
||||
void* load_addr;
|
||||
unsigned long size;
|
||||
const char* module_name;
|
||||
enum DbgInfoLoad dil;
|
||||
enum DbgModuleType type;
|
||||
unsigned short main : 1;
|
||||
short int dbg_index;
|
||||
HMODULE handle;
|
||||
struct tagMSC_DBG_INFO* msc_dbg_info;
|
||||
struct tagELF_DBG_INFO* elf_dbg_info;
|
||||
} DBG_MODULE;
|
||||
|
||||
typedef struct {
|
||||
DWORD val;
|
||||
const char* name;
|
||||
LPDWORD pval;
|
||||
struct datatype* type;
|
||||
} DBG_INTVAR;
|
||||
|
||||
#define OFFSET_OF(__c,__f) ((int)(((char*)&(((__c*)0)->__f))-((char*)0)))
|
||||
|
||||
enum get_sym_val {gsv_found, gsv_unknown, gsv_aborted};
|
||||
|
||||
/* from winelib.so */
|
||||
extern void DEBUG_ExternalDebugger(void);
|
||||
|
||||
/* debugger/break.c */
|
||||
extern void DEBUG_SetBreakpoints( BOOL set );
|
||||
extern BOOL DEBUG_AddBreakpoint( const DBG_VALUE *addr, BOOL (*func)(void), BOOL verbose );
|
||||
extern BOOL DEBUG_AddBreakpointFromValue( const DBG_VALUE *addr );
|
||||
extern void DEBUG_AddBreakpointFromId( const char *name, int lineno );
|
||||
extern void DEBUG_AddBreakpointFromLineno( int lineno );
|
||||
extern void DEBUG_AddWatchpoint( const DBG_VALUE *addr, int is_write );
|
||||
extern void DEBUG_AddWatchpointFromId( const char *name );
|
||||
extern void DEBUG_CheckDelayedBP( void );
|
||||
extern void DEBUG_DelBreakpoint( int num );
|
||||
extern void DEBUG_EnableBreakpoint( int num, BOOL enable );
|
||||
extern void DEBUG_InfoBreakpoints(void);
|
||||
extern BOOL DEBUG_HandleTrap(void);
|
||||
extern BOOL DEBUG_ShouldContinue( DBG_ADDR* addr, DWORD code, int* count );
|
||||
extern void DEBUG_SuspendExecution( void );
|
||||
extern void DEBUG_RestartExecution( int count );
|
||||
extern BOOL DEBUG_IsFctReturn(void);
|
||||
extern int DEBUG_AddBPCondition(int bpnum, struct expr * exp);
|
||||
|
||||
/* debugger/db_disasm.c */
|
||||
extern void DEBUG_Disasm( DBG_ADDR *addr, int display );
|
||||
|
||||
/* debugger/dbg.y */
|
||||
extern void DEBUG_Parser(LPCSTR);
|
||||
extern void DEBUG_Exit(DWORD);
|
||||
|
||||
/* debugger/debug.l */
|
||||
extern void DEBUG_FlushSymbols(void);
|
||||
extern char*DEBUG_MakeSymbol(const char*);
|
||||
extern int DEBUG_ReadLine(const char* pfx, char* buffer, int size);
|
||||
|
||||
/* debugger/display.c */
|
||||
extern int DEBUG_DoDisplay(void);
|
||||
extern int DEBUG_AddDisplay(struct expr * exp, int count, char format, int local_frame);
|
||||
extern int DEBUG_DelDisplay(int displaynum);
|
||||
extern int DEBUG_InfoDisplay(void);
|
||||
extern int DEBUG_EnableDisplay(int displaynum, int enable);
|
||||
|
||||
/* debugger/elf.c */
|
||||
#define ELF_INFO_PATH 0x0001
|
||||
#define ELF_INFO_DEBUG_HEADER 0x0002
|
||||
#define ELF_INFO_SEGMENTS 0x0004
|
||||
#define ELF_INFO_MODULE 0x0008
|
||||
|
||||
struct elf_info
|
||||
{
|
||||
unsigned flags;
|
||||
char* elf_path; /* path to unix elf path, if ELF_INFO_PATH is set */
|
||||
size_t elf_path_len;
|
||||
void* load_addr; /* 32 bit linear addr, where ELF module is loaded */
|
||||
unsigned long size; /* size of elf module (guessed) */
|
||||
unsigned long dbg_hdr_addr; /* address of debug header (if ELF_INFO_DEBUG_HEADER is set) */
|
||||
unsigned long segments[3]; /* addresses of .text, .data and .bss segments (not filled yet) */
|
||||
struct dbg_process* next;
|
||||
struct dbg_process* prev;
|
||||
};
|
||||
|
||||
extern enum DbgInfoLoad DEBUG_ReadWineLoaderDbgInfo(HANDLE hProcess, struct elf_info* elf_info);
|
||||
extern BOOL DEBUG_SetElfSoLoadBreakpoint(const struct elf_info* elf_info);
|
||||
extern struct dbg_process* dbg_curr_process;
|
||||
extern DWORD dbg_curr_pid;
|
||||
extern struct dbg_thread* dbg_curr_thread;
|
||||
extern DWORD dbg_curr_tid;
|
||||
extern CONTEXT dbg_context;
|
||||
extern BOOL dbg_interactiveP;
|
||||
extern int dbg_curr_frame;
|
||||
|
||||
/* debugger/expr.c */
|
||||
extern void DEBUG_FreeExprMem(void);
|
||||
struct expr * DEBUG_IntVarExpr(const char* name);
|
||||
struct expr * DEBUG_SymbolExpr(const char * name);
|
||||
struct expr * DEBUG_ConstExpr(int val);
|
||||
struct expr * DEBUG_StringExpr(const char * str);
|
||||
struct expr * DEBUG_SegAddr(struct expr *, struct expr *);
|
||||
struct expr * DEBUG_USConstExpr(unsigned int val);
|
||||
struct expr * DEBUG_BinopExpr(int oper, struct expr *, struct expr *);
|
||||
struct expr * DEBUG_UnopExpr(int oper, struct expr *);
|
||||
struct expr * DEBUG_StructPExpr(struct expr *, const char * element);
|
||||
struct expr * DEBUG_StructExpr(struct expr *, const char * element);
|
||||
struct expr * DEBUG_ArrayExpr(struct expr *, struct expr * index);
|
||||
struct expr * DEBUG_CallExpr(const char *, int nargs, ...);
|
||||
struct expr * DEBUG_TypeCastExpr(struct datatype *, struct expr *);
|
||||
extern DBG_VALUE DEBUG_EvalExpr(struct expr *);
|
||||
extern int DEBUG_DelDisplay(int displaynum);
|
||||
extern struct expr * DEBUG_CloneExpr(const struct expr * exp);
|
||||
extern int DEBUG_FreeExpr(struct expr * exp);
|
||||
extern int DEBUG_DisplayExpr(const struct expr * exp);
|
||||
struct dbg_internal_var
|
||||
{
|
||||
DWORD val;
|
||||
const char* name;
|
||||
LPDWORD pval;
|
||||
unsigned long typeid;
|
||||
};
|
||||
|
||||
/* debugger/hash.c */
|
||||
extern struct name_hash * DEBUG_AddSymbol( const char *name,
|
||||
const DBG_VALUE *addr,
|
||||
const char *sourcefile,
|
||||
int flags);
|
||||
extern enum get_sym_val DEBUG_GetSymbolValue( const char * name, const int lineno, DBG_VALUE *addr, int );
|
||||
extern const char * DEBUG_FindNearestSymbol( const DBG_ADDR *addr, int flag,
|
||||
struct name_hash ** rtn,
|
||||
unsigned int ebp,
|
||||
struct list_id * source);
|
||||
extern void DEBUG_ReadSymbolTable( const char * filename, unsigned long offset );
|
||||
extern void DEBUG_AddLineNumber( struct name_hash * func, int line_num,
|
||||
unsigned long offset );
|
||||
extern struct wine_locals *
|
||||
DEBUG_AddLocal( struct name_hash * func, int regno,
|
||||
int offset,
|
||||
int pc_start,
|
||||
int pc_end,
|
||||
char * name);
|
||||
extern int DEBUG_CheckLinenoStatus(const DBG_ADDR *addr);
|
||||
extern void DEBUG_GetFuncInfo(struct list_id * ret, const char * file,
|
||||
const char * func);
|
||||
extern int DEBUG_SetSymbolSize(struct name_hash * sym, unsigned int len);
|
||||
extern int DEBUG_SetSymbolBPOff(struct name_hash * sym, unsigned int len);
|
||||
extern int DEBUG_GetSymbolAddr(struct name_hash * sym, DBG_ADDR * addr);
|
||||
extern int DEBUG_cmp_sym(const void * p1, const void * p2);
|
||||
extern BOOL DEBUG_GetLineNumberAddr( const struct name_hash *, const int lineno,
|
||||
DBG_ADDR *addr, int bp_flag );
|
||||
enum sym_get_lval {sglv_found, sglv_unknown, sglv_aborted};
|
||||
|
||||
extern int DEBUG_SetLocalSymbolType(struct wine_locals * sym,
|
||||
struct datatype * type);
|
||||
extern BOOL DEBUG_Normalize(struct name_hash * nh );
|
||||
void DEBUG_InfoSymbols(const char* str);
|
||||
const char *DEBUG_GetSymbolName(const struct name_hash *);
|
||||
enum type_expr_e
|
||||
{
|
||||
type_expr_type_id,
|
||||
type_expr_udt_class,
|
||||
type_expr_udt_struct,
|
||||
type_expr_udt_union,
|
||||
type_expr_enumeration
|
||||
};
|
||||
|
||||
/* debugger/info.c */
|
||||
extern void DEBUG_PrintBasic( const DBG_VALUE* value, int count, char format );
|
||||
extern struct symbol_info DEBUG_PrintAddress( const DBG_ADDR *addr,
|
||||
enum dbg_mode mode, int flag );
|
||||
extern void DEBUG_Help(void);
|
||||
extern void DEBUG_HelpInfo(void);
|
||||
extern struct symbol_info DEBUG_PrintAddressAndArgs( const DBG_ADDR *addr,
|
||||
enum dbg_mode mode,
|
||||
unsigned int ebp,
|
||||
int flag );
|
||||
extern void DEBUG_InfoClass(const char* clsName);
|
||||
extern void DEBUG_WalkClasses(void);
|
||||
extern void DEBUG_DumpModule(DWORD mod);
|
||||
extern void DEBUG_WalkModules(void);
|
||||
extern void DEBUG_WalkProcess(void);
|
||||
extern void DEBUG_WalkThreads(void);
|
||||
extern void DEBUG_WalkExceptions(DWORD tid);
|
||||
extern void DEBUG_InfoSegments(DWORD s, int v);
|
||||
extern void DEBUG_InfoVirtual(DWORD pid);
|
||||
extern void DEBUG_InfoWindow(HWND hWnd);
|
||||
extern void DEBUG_WalkWindows(HWND hWnd, int indent);
|
||||
extern void DEBUG_DbgChannel(BOOL add, const char* chnl, const char* name);
|
||||
struct type_expr_t
|
||||
{
|
||||
enum type_expr_e type;
|
||||
unsigned deref_count;
|
||||
union
|
||||
{
|
||||
unsigned long typeid;
|
||||
const char* name;
|
||||
} u;
|
||||
};
|
||||
|
||||
/* debugger/memory.c */
|
||||
extern int DEBUG_ReadMemory( const DBG_VALUE* value );
|
||||
extern void DEBUG_WriteMemory( const DBG_VALUE* val, int value );
|
||||
extern void DEBUG_ExamineMemory( const DBG_VALUE *addr, int count, char format);
|
||||
extern void DEBUG_InvalAddr( const DBG_ADDR* addr );
|
||||
extern void DEBUG_InvalLinAddr( void* addr );
|
||||
extern DWORD DEBUG_ToLinear( const DBG_ADDR *address );
|
||||
extern void DEBUG_GetCurrentAddress( DBG_ADDR * );
|
||||
extern BOOL DEBUG_GrabAddress( DBG_VALUE* value, BOOL fromCode );
|
||||
extern enum dbg_mode DEBUG_GetSelectorType( WORD sel );
|
||||
#ifdef __i386__
|
||||
extern void DEBUG_FixAddress( DBG_ADDR *address, DWORD def );
|
||||
extern int DEBUG_IsSelectorSystem( WORD sel );
|
||||
#endif
|
||||
extern int DEBUG_PrintStringA(const DBG_ADDR* address, int len);
|
||||
extern int DEBUG_PrintStringW(const DBG_ADDR* address, int len);
|
||||
/* break.c */
|
||||
extern void break_set_xpoints(BOOL set);
|
||||
extern BOOL break_add_break(const ADDRESS* addr, BOOL verbose);
|
||||
extern BOOL break_add_break_from_lvalue(const struct dbg_lvalue* value);
|
||||
extern void break_add_break_from_id(const char* name, int lineno);
|
||||
extern void break_add_break_from_lineno(int lineno);
|
||||
extern void break_add_watch(const struct dbg_lvalue* lvalue, int is_write);
|
||||
extern void break_add_watch_from_id(const char* name);
|
||||
extern void break_check_delayed_bp(void);
|
||||
extern void break_delete_xpoint(int num);
|
||||
extern void break_delete_xpoints_from_module(unsigned long base);
|
||||
extern void break_enable_xpoint(int num, BOOL enable);
|
||||
extern void break_info(void);
|
||||
extern BOOL break_should_continue(ADDRESS* addr, DWORD code, int* count);
|
||||
extern void break_suspend_execution(void);
|
||||
extern void break_restart_execution(int count);
|
||||
extern int break_add_condition(int bpnum, struct expr* exp);
|
||||
|
||||
/* debugger/module.c */
|
||||
extern DBG_MODULE* DEBUG_AddModule(const char* name, enum DbgModuleType type,
|
||||
void* mod_addr, unsigned long size, HMODULE hmodule);
|
||||
extern int DEBUG_LoadEntryPoints( const char * prefix );
|
||||
extern DBG_MODULE* DEBUG_FindModuleByName(const char* name, enum DbgModuleType type);
|
||||
extern DBG_MODULE* DEBUG_FindModuleByHandle(HANDLE handle, enum DbgModuleType type);
|
||||
extern DBG_MODULE* DEBUG_FindModuleByAddr(void* addr, enum DbgModuleType type);
|
||||
extern DBG_MODULE* DEBUG_GetProcessMainModule(DBG_PROCESS* process);
|
||||
extern void DEBUG_ReportDIL(enum DbgInfoLoad dil, const char* pfx,
|
||||
const char* filename, void *load_addr);
|
||||
extern void DEBUG_InfoShare(void);
|
||||
/* dbg.y */
|
||||
extern void parser(const char*);
|
||||
extern int input_read_line(const char* pfx, char* buffer, int size);
|
||||
extern int input_fetch_entire_line(const char* pfx, char** line, size_t* alloc, BOOL check_nl);
|
||||
|
||||
/* debugger/msc.c */
|
||||
extern void DEBUG_InitCVDataTypes(void);
|
||||
extern enum DbgInfoLoad DEBUG_ProcessDebugDirectory( DBG_MODULE *module, const BYTE* file_map,
|
||||
PIMAGE_DEBUG_DIRECTORY dbg, int nDbg );
|
||||
/* debug.l */
|
||||
extern void lexeme_flush(void);
|
||||
extern char* lexeme_alloc(const char*);
|
||||
|
||||
/* debugger/pe.c */
|
||||
extern void* DEBUG_MapDebugInfoFile(const char* name, DWORD offset, DWORD size,
|
||||
HANDLE* hFile, HANDLE* hMap);
|
||||
extern void DEBUG_UnmapDebugInfoFile(HANDLE hFile, HANDLE hMap, const void* addr);
|
||||
extern void DEBUG_LoadPEModule( const char* name, HANDLE hFile, void *base );
|
||||
extern enum DbgInfoLoad DEBUG_RegisterMSCDebugInfo(DBG_MODULE* module, HANDLE hFile,
|
||||
void* nth, unsigned long nth_ofs);
|
||||
extern enum DbgInfoLoad DEBUG_RegisterStabsDebugInfo(DBG_MODULE* module,
|
||||
HANDLE hFile, void* nth,
|
||||
unsigned long nth_ofs);
|
||||
extern enum DbgInfoLoad DEBUG_RegisterPEDebugInfo(DBG_MODULE* wmod, HANDLE hFile,
|
||||
void* _nth, unsigned long nth_ofs);
|
||||
/* display.c */
|
||||
extern int display_print(void);
|
||||
extern int display_add(struct expr* exp, int count, char format, int local_frame);
|
||||
extern int display_delete(int displaynum);
|
||||
extern int display_info(void);
|
||||
extern int display_enable(int displaynum, int enable);
|
||||
|
||||
/* debugger/registers.c */
|
||||
extern void DEBUG_InfoRegisters(const CONTEXT* ctx);
|
||||
extern BOOL DEBUG_ValidateRegisters(void);
|
||||
/* expr.c */
|
||||
extern void expr_free_all(void);
|
||||
extern struct expr* expr_alloc_internal_var(const char* name);
|
||||
extern struct expr* expr_alloc_symbol(const char* name);
|
||||
extern struct expr* expr_alloc_sconstant(int val);
|
||||
extern struct expr* expr_alloc_uconstant(unsigned val);
|
||||
extern struct expr* expr_alloc_string(const char* str);
|
||||
extern struct expr* expr_alloc_binary_op(int oper, struct expr*, struct expr*);
|
||||
extern struct expr* expr_alloc_unary_op(int oper, struct expr*);
|
||||
extern struct expr* expr_alloc_pstruct(struct expr*, const char* element);
|
||||
extern struct expr* expr_alloc_struct(struct expr*, const char* element);
|
||||
extern struct expr* expr_alloc_func_call(const char*, int nargs, ...);
|
||||
extern struct expr* expr_alloc_typecast(struct type_expr_t*, struct expr*);
|
||||
extern struct dbg_lvalue expr_eval(struct expr*);
|
||||
extern struct expr* expr_clone(const struct expr* exp);
|
||||
extern int expr_free(struct expr* exp);
|
||||
extern int expr_print(const struct expr* exp);
|
||||
|
||||
/* debugger/source.c */
|
||||
extern void DEBUG_ShowDir(void);
|
||||
extern void DEBUG_AddPath(const char * path);
|
||||
extern void DEBUG_List(struct list_id * line1, struct list_id * line2,
|
||||
int delta);
|
||||
extern void DEBUG_NukePath(void);
|
||||
extern void DEBUG_Disassemble(const DBG_VALUE *, const DBG_VALUE*, int offset);
|
||||
extern BOOL DEBUG_DisassembleInstruction(DBG_ADDR *addr);
|
||||
/* info.c */
|
||||
extern void print_help(void);
|
||||
extern void info_help(void);
|
||||
extern void info_win32_module(DWORD mod);
|
||||
extern void info_win32_class(HWND hWnd, const char* clsName);
|
||||
extern void info_win32_window(HWND hWnd, BOOL detailed);
|
||||
extern void info_win32_processes(void);
|
||||
extern void info_win32_threads(void);
|
||||
extern void info_win32_exceptions(DWORD tid);
|
||||
extern void info_win32_virtual(DWORD pid);
|
||||
extern void info_win32_segments(DWORD start, int length);
|
||||
extern void info_wine_dbg_channel(BOOL add, const char* chnl, const char* name);
|
||||
|
||||
/* debugger/stack.c */
|
||||
extern void DEBUG_InfoStack(void);
|
||||
extern void DEBUG_BackTrace(DWORD threadID, BOOL noisy);
|
||||
extern int DEBUG_InfoLocals(void);
|
||||
extern int DEBUG_SetFrame(int newframe);
|
||||
extern int DEBUG_GetCurrentFrame(struct name_hash ** name,
|
||||
unsigned int * eip,
|
||||
unsigned int * ebp);
|
||||
/* memory.c */
|
||||
extern BOOL memory_read_value(const struct dbg_lvalue* val, DWORD size, void* result);
|
||||
extern BOOL memory_write_value(const struct dbg_lvalue* val, DWORD size, void* value);
|
||||
extern void memory_examine(const struct dbg_lvalue* addr, int count, char format);
|
||||
extern void memory_report_invalid_addr(const void* addr);
|
||||
extern void* memory_to_linear_addr(const ADDRESS* address);
|
||||
extern BOOL memory_get_current_pc(ADDRESS* address);
|
||||
extern BOOL memory_get_current_stack(ADDRESS* address);
|
||||
extern BOOL memory_get_current_frame(ADDRESS* address);
|
||||
extern BOOL memory_get_string(HANDLE hp, void* addr, unsigned cookie, BOOL unicode, char* buffer, int size);
|
||||
extern BOOL memory_get_string_indirect(HANDLE hp, void* addr, BOOL unicode, char* buffer, int size);
|
||||
extern void memory_disassemble(const struct dbg_lvalue*, const struct dbg_lvalue*, int offset);
|
||||
extern BOOL memory_disasm_one_insn(ADDRESS* addr);
|
||||
extern void print_bare_address(const ADDRESS* addr);
|
||||
extern void print_address(const ADDRESS* addr, BOOLEAN with_line);
|
||||
extern void print_addr_and_args(const ADDRESS* pc, const ADDRESS* frame);
|
||||
extern void print_basic(const struct dbg_lvalue* value, int count, char format);
|
||||
|
||||
/* debugger/stabs.c */
|
||||
extern enum DbgInfoLoad DEBUG_ParseStabs(const char* addr, void *load_offset,
|
||||
unsigned int staboff, int stablen,
|
||||
unsigned int strtaboff, int strtablen);
|
||||
/* source.c */
|
||||
extern void source_list(IMAGEHLP_LINE* src1, IMAGEHLP_LINE* src2, int delta);
|
||||
extern void source_list_from_addr(const ADDRESS* addr, int nlines);
|
||||
extern void source_show_path(void);
|
||||
extern void source_add_path(const char* path);
|
||||
extern void source_nuke_path(void);
|
||||
|
||||
/* debugger/types.c */
|
||||
extern int DEBUG_nchar;
|
||||
extern void DEBUG_InitTypes(void);
|
||||
extern struct datatype * DEBUG_NewDataType(enum debug_type xtype,
|
||||
const char * typename);
|
||||
extern unsigned int DEBUG_TypeDerefPointer(const DBG_VALUE *value,
|
||||
struct datatype ** newtype);
|
||||
extern int DEBUG_AddStructElement(struct datatype * dt,
|
||||
char * name, struct datatype * type,
|
||||
int offset, int size);
|
||||
extern int DEBUG_SetStructSize(struct datatype * dt, int size);
|
||||
extern int DEBUG_SetPointerType(struct datatype * dt, struct datatype * dt2);
|
||||
extern int DEBUG_SetArrayParams(struct datatype * dt, int min, int max,
|
||||
struct datatype * dt2);
|
||||
extern void DEBUG_Print( const DBG_VALUE *addr, int count, char format, int level );
|
||||
extern unsigned int DEBUG_FindStructElement(DBG_VALUE * addr,
|
||||
const char * ele_name, int * tmpbuf);
|
||||
extern struct datatype * DEBUG_GetPointerType(struct datatype * dt);
|
||||
extern int DEBUG_GetObjectSize(struct datatype * dt);
|
||||
extern unsigned int DEBUG_ArrayIndex(const DBG_VALUE * addr, DBG_VALUE * result,
|
||||
int index);
|
||||
extern struct datatype * DEBUG_FindOrMakePointerType(struct datatype * reftype);
|
||||
extern long long int DEBUG_GetExprValue(const DBG_VALUE * addr, const char ** format);
|
||||
extern int DEBUG_SetBitfieldParams(struct datatype * dt, int offset,
|
||||
int nbits, struct datatype * dt2);
|
||||
extern int DEBUG_CopyFieldlist(struct datatype * dt, struct datatype * dt2);
|
||||
extern const char* DEBUG_GetName(struct datatype * dt);
|
||||
extern enum debug_type DEBUG_GetType(struct datatype * dt);
|
||||
extern struct datatype * DEBUG_TypeCast(enum debug_type, const char *);
|
||||
extern int DEBUG_PrintTypeCast(const struct datatype *);
|
||||
extern int DEBUG_PrintType(const DBG_VALUE* addr);
|
||||
extern struct datatype * DEBUG_GetBasicType(enum debug_type_basic);
|
||||
extern int DEBUG_DumpTypes(void);
|
||||
/* stack.c */
|
||||
extern void stack_info(void);
|
||||
extern void stack_backtrace(DWORD threadID, BOOL noisy);
|
||||
extern int stack_set_frame(int newframe);
|
||||
extern int stack_get_frame(SYMBOL_INFO* sym, IMAGEHLP_STACK_FRAME* ihsf);
|
||||
|
||||
/* debugger/winedbg.c */
|
||||
extern void DEBUG_OutputA(const char* buffer, int len);
|
||||
extern void DEBUG_OutputW(const WCHAR* buffer, int len);
|
||||
/* symbol.c */
|
||||
extern enum sym_get_lval symbol_get_lvalue(const char* name, const int lineno, struct dbg_lvalue* addr, BOOL bp_disp);
|
||||
extern void symbol_read_symtable(const char* filename, unsigned long offset);
|
||||
extern enum dbg_line_status symbol_get_function_line_status(const ADDRESS* addr);
|
||||
extern BOOL symbol_get_line(const char* filename, const char* func, IMAGEHLP_LINE* ret);
|
||||
extern void symbol_info(const char* str);
|
||||
extern int symbol_info_locals(void);
|
||||
|
||||
/* types.c */
|
||||
extern void print_value(const struct dbg_lvalue* addr, char format, int level);
|
||||
extern int types_print_type(DWORD linear, DWORD typeid, BOOL details);
|
||||
extern int print_types(void);
|
||||
extern long int types_extract_as_integer(const struct dbg_lvalue*);
|
||||
extern BOOL types_deref(const struct dbg_lvalue* value, struct dbg_lvalue* result);
|
||||
extern BOOL types_udt_find_element(struct dbg_lvalue* value, const char* name, long int* tmpbuf);
|
||||
extern BOOL types_array_index(const struct dbg_lvalue* value, int index, struct dbg_lvalue* result);
|
||||
extern BOOL types_get_info(unsigned long, unsigned long,
|
||||
IMAGEHLP_SYMBOL_TYPE_INFO, void*);
|
||||
extern unsigned long types_find_pointer(unsigned long linear, unsigned long typeid);
|
||||
extern unsigned long types_find_type(unsigned long linear, const char* name, enum SymTagEnum tag);
|
||||
|
||||
/* winedbg.c */
|
||||
extern void dbg_outputA(const char* buffer, int len);
|
||||
extern void dbg_outputW(const WCHAR* buffer, int len);
|
||||
#ifdef __GNUC__
|
||||
extern int DEBUG_Printf(const char* format, ...) __attribute__((format (printf,1,2)));
|
||||
extern int dbg_printf(const char* format, ...) __attribute__((format (printf,1,2)));
|
||||
#else
|
||||
extern int DEBUG_Printf(const char* format, ...);
|
||||
extern int dbg_printf(const char* format, ...);
|
||||
#endif
|
||||
extern DBG_INTVAR* DEBUG_GetIntVar(const char*);
|
||||
extern BOOL DEBUG_Attach(DWORD pid, BOOL cofe, BOOL wfe);
|
||||
extern BOOL DEBUG_Detach(void);
|
||||
extern void DEBUG_Run(const char* args);
|
||||
extern DBG_PROCESS* DEBUG_AddProcess(DWORD pid, HANDLE h, const char* imageName);
|
||||
extern DBG_PROCESS* DEBUG_GetProcess(DWORD pid);
|
||||
extern void DEBUG_DelProcess(DBG_PROCESS* p);
|
||||
extern DBG_THREAD* DEBUG_AddThread(DBG_PROCESS* p, DWORD tid, HANDLE h, LPVOID start, LPVOID teb);
|
||||
extern DBG_THREAD* DEBUG_GetThread(DBG_PROCESS* p, DWORD tid);
|
||||
extern void DEBUG_DelThread(DBG_THREAD* t);
|
||||
extern BOOL DEBUG_ProcessGetString(char* buffer, int size, HANDLE hp, LPSTR addr);
|
||||
extern BOOL DEBUG_ProcessGetStringIndirect(char* buffer, int size, HANDLE hp, LPVOID addr, BOOL unicode);
|
||||
extern void DEBUG_WaitNextException(DWORD cont, int count, int mode);
|
||||
extern BOOL DEBUG_InterruptDebuggee(void);
|
||||
extern int curr_frame;
|
||||
extern const struct dbg_internal_var* dbg_get_internal_var(const char*);
|
||||
extern BOOL dbg_attach_debuggee(DWORD pid, BOOL cofe, BOOL wfe);
|
||||
extern BOOL dbg_detach_debuggee(void);
|
||||
extern BOOL dbg_interrupt_debuggee(void);
|
||||
extern void dbg_run_debuggee(const char* args);
|
||||
extern struct dbg_process* dbg_add_process(DWORD pid, HANDLE h, const char* imageName);
|
||||
extern struct dbg_process* dbg_get_process(DWORD pid);
|
||||
extern void dbg_del_process(struct dbg_process* p);
|
||||
struct dbg_thread* dbg_add_thread(struct dbg_process* p, DWORD tid, HANDLE h, void* teb);
|
||||
extern struct dbg_thread* dbg_get_thread(struct dbg_process* p, DWORD tid);
|
||||
extern void dbg_del_thread(struct dbg_thread* t);
|
||||
extern void dbg_wait_next_exception(DWORD cont, int count, int mode);
|
||||
extern BOOL dbg_get_debuggee_info(HANDLE hProcess, IMAGEHLP_MODULE* imh_mod);
|
||||
|
||||
/* gdbproxy.c */
|
||||
extern BOOL DEBUG_GdbRemote(unsigned int);
|
||||
extern BOOL gdb_remote(unsigned int);
|
||||
|
||||
extern void *DBG_alloc(size_t size);
|
||||
extern void *DBG_realloc(void *ptr, size_t size);
|
||||
extern void DBG_free(void *ptr);
|
||||
extern char *DBG_strdup(const char *str);
|
||||
static inline BOOL dbg_read_memory(const void* addr, void* buffer, size_t len)
|
||||
{
|
||||
DWORD rlen;
|
||||
return ReadProcessMemory(dbg_curr_process->handle, addr, buffer, len, &rlen) && len == rlen;
|
||||
}
|
||||
|
||||
#define DEBUG_STATUS_OFFSET 0x80003000
|
||||
#define DEBUG_STATUS_INTERNAL_ERROR (DEBUG_STATUS_OFFSET+0)
|
||||
#define DEBUG_STATUS_NO_SYMBOL (DEBUG_STATUS_OFFSET+1)
|
||||
#define DEBUG_STATUS_DIV_BY_ZERO (DEBUG_STATUS_OFFSET+2)
|
||||
#define DEBUG_STATUS_BAD_TYPE (DEBUG_STATUS_OFFSET+3)
|
||||
#define DEBUG_STATUS_NO_FIELD (DEBUG_STATUS_OFFSET+4)
|
||||
#define DEBUG_STATUS_ABORT (DEBUG_STATUS_OFFSET+5)
|
||||
static inline BOOL dbg_write_memory(void* addr, const void* buffer, size_t len)
|
||||
{
|
||||
DWORD wlen;
|
||||
return WriteProcessMemory(dbg_curr_process->handle, addr, buffer, len, &wlen) && len == wlen;
|
||||
}
|
||||
|
||||
extern DBG_INTVAR DEBUG_IntVars[];
|
||||
static inline BOOL dbg_read_memory_verbose(const void* addr, void* buffer, size_t len)
|
||||
{
|
||||
if (dbg_read_memory(addr, buffer, len)) return TRUE;
|
||||
memory_report_invalid_addr(addr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#define DBG_IVARNAME(_var) DEBUG_IV_##_var
|
||||
#define DBG_IVARSTRUCT(_var) DEBUG_IntVars[DBG_IVARNAME(_var)]
|
||||
static inline BOOL dbg_write_memory_verbose(void* addr, const void* buffer, size_t len)
|
||||
{
|
||||
if (dbg_write_memory(addr, buffer, len)) return TRUE;
|
||||
memory_report_invalid_addr(addr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static inline void* dbg_heap_realloc(void* buffer, size_t size)
|
||||
{
|
||||
return (buffer) ? HeapReAlloc(GetProcessHeap(), 0, buffer, size) :
|
||||
HeapAlloc(GetProcessHeap(), 0, size);
|
||||
}
|
||||
|
||||
extern struct dbg_internal_var dbg_internal_vars[];
|
||||
extern const struct dbg_internal_var* dbg_context_vars;
|
||||
|
||||
#define DBG_IVARNAME(_var) dbg_internal_var_##_var
|
||||
#define DBG_IVARSTRUCT(_var) dbg_internal_vars[DBG_IVARNAME(_var)]
|
||||
#define DBG_IVAR(_var) (*(DBG_IVARSTRUCT(_var).pval))
|
||||
#define INTERNAL_VAR(_var,_val,_ref,_typ) DBG_IVARNAME(_var),
|
||||
enum debug_int_var {
|
||||
#define INTERNAL_VAR(_var,_val,_ref,itype) DBG_IVARNAME(_var),
|
||||
enum debug_int_var
|
||||
{
|
||||
#include "intvar.h"
|
||||
DBG_IV_LAST
|
||||
};
|
||||
#undef INTERNAL_VAR
|
||||
|
||||
/* include CPU dependent bits */
|
||||
#include "be_cpu.h"
|
||||
|
||||
#endif /* __WINE_DEBUGGER_H */
|
||||
|
|
|
@ -21,178 +21,229 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "debugger.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
|
||||
|
||||
#define DISPTAB_DELTA 8 /* needs to be power of 2, search for MARK to see why :) */
|
||||
/* needs to be power of 2, search for MARK to see why :) */
|
||||
#define DISPTAB_DELTA 8
|
||||
|
||||
struct display {
|
||||
struct expr *exp;
|
||||
int count;
|
||||
char format;
|
||||
char enabled;
|
||||
struct name_hash *function_name;
|
||||
struct display
|
||||
{
|
||||
struct expr* exp;
|
||||
int count;
|
||||
char format;
|
||||
char enabled;
|
||||
char func_buffer[sizeof(SYMBOL_INFO) + 256];
|
||||
SYMBOL_INFO* func;
|
||||
};
|
||||
|
||||
static struct display *displaypoints = NULL;
|
||||
static unsigned int maxdisplays = 0, ndisplays = 0;
|
||||
|
||||
static struct name_hash *DEBUG_GetCurrentFrameFunctionName(void)
|
||||
static inline BOOL cmp_symbol(const SYMBOL_INFO* si1, const SYMBOL_INFO* si2)
|
||||
{
|
||||
struct name_hash *name;
|
||||
unsigned int eip, ebp;
|
||||
|
||||
if (DEBUG_GetCurrentFrame(&name, &eip, &ebp))
|
||||
return name;
|
||||
return NULL;
|
||||
if (si1->NameLen != si2->NameLen) return FALSE;
|
||||
return !memcmp(si1, si2, sizeof(SYMBOL_INFO) + si1->NameLen);
|
||||
}
|
||||
|
||||
int DEBUG_AddDisplay(struct expr *exp, int count, char format, int in_frame)
|
||||
int display_add(struct expr *exp, int count, char format, int in_frame)
|
||||
{
|
||||
int i;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ndisplays; i++)
|
||||
if (displaypoints[i].exp == NULL)
|
||||
break;
|
||||
for (i = 0; i < ndisplays; i++)
|
||||
if (displaypoints[i].exp == NULL)
|
||||
break;
|
||||
|
||||
if (i == maxdisplays) /* no space left - expand */
|
||||
displaypoints = DBG_realloc(displaypoints,
|
||||
(maxdisplays += DISPTAB_DELTA) * sizeof(*displaypoints));
|
||||
if (i == maxdisplays)
|
||||
{
|
||||
/* no space left - expand */
|
||||
maxdisplays += DISPTAB_DELTA;
|
||||
displaypoints = dbg_heap_realloc(displaypoints,
|
||||
maxdisplays * sizeof(*displaypoints));
|
||||
}
|
||||
|
||||
if (i == ndisplays)
|
||||
++ndisplays;
|
||||
if (i == ndisplays) ndisplays++;
|
||||
|
||||
displaypoints[i].exp = DEBUG_CloneExpr(exp);
|
||||
displaypoints[i].count = count;
|
||||
displaypoints[i].format = format;
|
||||
displaypoints[i].enabled = TRUE;
|
||||
displaypoints[i].function_name =
|
||||
(in_frame ? DEBUG_GetCurrentFrameFunctionName() : NULL);
|
||||
displaypoints[i].exp = expr_clone(exp);
|
||||
displaypoints[i].count = count;
|
||||
displaypoints[i].format = format;
|
||||
displaypoints[i].enabled = TRUE;
|
||||
if (in_frame)
|
||||
{
|
||||
displaypoints[i].func = (SYMBOL_INFO*)displaypoints[i].func_buffer;
|
||||
displaypoints[i].func->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
displaypoints[i].func->MaxNameLen = sizeof(displaypoints[i].func_buffer) -
|
||||
sizeof(*displaypoints[i].func);
|
||||
if (!stack_get_frame(displaypoints[i].func, NULL))
|
||||
{
|
||||
expr_free(displaypoints[i].exp);
|
||||
displaypoints[i].exp = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else displaypoints[i].func = NULL;
|
||||
|
||||
return TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int DEBUG_InfoDisplay(void)
|
||||
int display_info(void)
|
||||
{
|
||||
int i;
|
||||
int i;
|
||||
char buffer[sizeof(SYMBOL_INFO) + 256];
|
||||
SYMBOL_INFO* func;
|
||||
const char* info;
|
||||
|
||||
for (i = 0; i < ndisplays; i++) {
|
||||
if (displaypoints[i].exp == NULL)
|
||||
continue;
|
||||
func = (SYMBOL_INFO*)buffer;
|
||||
func->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
func->MaxNameLen = sizeof(buffer) - sizeof(*func);
|
||||
if (!stack_get_frame(func, NULL)) return FALSE;
|
||||
|
||||
if (displaypoints[i].function_name)
|
||||
DEBUG_Printf("%d in %s%s : ", i + 1,
|
||||
DEBUG_GetSymbolName(displaypoints[i].function_name),
|
||||
(displaypoints[i].enabled ?
|
||||
(displaypoints[i].function_name != DEBUG_GetCurrentFrameFunctionName() ?
|
||||
" (out of scope)" : "")
|
||||
: " (disabled)")
|
||||
);
|
||||
else
|
||||
DEBUG_Printf("%d%s : ", i + 1,
|
||||
(displaypoints[i].enabled ? "" : " (disabled)"));
|
||||
DEBUG_DisplayExpr(displaypoints[i].exp);
|
||||
DEBUG_Printf("\n");
|
||||
}
|
||||
for (i = 0; i < ndisplays; i++)
|
||||
{
|
||||
if (displaypoints[i].exp == NULL) continue;
|
||||
|
||||
return TRUE;
|
||||
if (displaypoints[i].enabled)
|
||||
{
|
||||
if (displaypoints[i].func && !cmp_symbol(displaypoints[i].func, func))
|
||||
info = " (out of scope)";
|
||||
else
|
||||
info = "";
|
||||
}
|
||||
else
|
||||
info = " (disabled)";
|
||||
dbg_printf("%d in %s%s: ",
|
||||
i + 1, func ? displaypoints[i].func->Name : "", info);
|
||||
expr_print(displaypoints[i].exp);
|
||||
dbg_printf("\n");
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void DEBUG_PrintOneDisplay(int i)
|
||||
static void print_one_display(int i)
|
||||
{
|
||||
DBG_VALUE value;
|
||||
struct dbg_lvalue lvalue;
|
||||
|
||||
if (displaypoints[i].enabled) {
|
||||
value = DEBUG_EvalExpr(displaypoints[i].exp);
|
||||
if (value.type == NULL) {
|
||||
DEBUG_Printf("Unable to evaluate expression ");
|
||||
DEBUG_DisplayExpr(displaypoints[i].exp);
|
||||
DEBUG_Printf("\nDisabling display %d ...\n", i + 1);
|
||||
displaypoints[i].enabled = FALSE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (displaypoints[i].enabled)
|
||||
{
|
||||
lvalue = expr_eval(displaypoints[i].exp);
|
||||
if (lvalue.typeid == dbg_itype_none)
|
||||
{
|
||||
dbg_printf("Unable to evaluate expression ");
|
||||
expr_print(displaypoints[i].exp);
|
||||
dbg_printf("\nDisabling display %d ...\n", i + 1);
|
||||
displaypoints[i].enabled = FALSE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_Printf("%d : ", i + 1);
|
||||
DEBUG_DisplayExpr(displaypoints[i].exp);
|
||||
DEBUG_Printf(" = ");
|
||||
if (!displaypoints[i].enabled)
|
||||
DEBUG_Printf("(disabled)\n");
|
||||
else
|
||||
dbg_printf("%d: ", i + 1);
|
||||
expr_print(displaypoints[i].exp);
|
||||
dbg_printf(" = ");
|
||||
if (!displaypoints[i].enabled)
|
||||
dbg_printf("(disabled)\n");
|
||||
else
|
||||
if (displaypoints[i].format == 'i')
|
||||
DEBUG_ExamineMemory(&value, displaypoints[i].count, displaypoints[i].format);
|
||||
memory_examine(&lvalue, displaypoints[i].count, displaypoints[i].format);
|
||||
else
|
||||
DEBUG_Print(&value, displaypoints[i].count, displaypoints[i].format, 0);
|
||||
print_value(&lvalue, displaypoints[i].format, 0);
|
||||
}
|
||||
|
||||
int DEBUG_DoDisplay(void)
|
||||
int display_print(void)
|
||||
{
|
||||
int i;
|
||||
struct name_hash *cur_function_name = DEBUG_GetCurrentFrameFunctionName();
|
||||
int i;
|
||||
char buffer[sizeof(SYMBOL_INFO) + 256];
|
||||
SYMBOL_INFO* func;
|
||||
|
||||
for (i = 0; i < ndisplays; i++) {
|
||||
if (displaypoints[i].exp == NULL || !displaypoints[i].enabled)
|
||||
continue;
|
||||
if (displaypoints[i].function_name
|
||||
&& displaypoints[i].function_name != cur_function_name)
|
||||
continue;
|
||||
DEBUG_PrintOneDisplay(i);
|
||||
}
|
||||
func = (SYMBOL_INFO*)buffer;
|
||||
func->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
func->MaxNameLen = sizeof(buffer) - sizeof(*func);
|
||||
if (!stack_get_frame(func, NULL)) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
for (i = 0; i < ndisplays; i++)
|
||||
{
|
||||
if (displaypoints[i].exp == NULL || !displaypoints[i].enabled)
|
||||
continue;
|
||||
if (displaypoints[i].func && !cmp_symbol(displaypoints[i].func, func))
|
||||
continue;
|
||||
print_one_display(i);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int DEBUG_DelDisplay(int displaynum)
|
||||
int display_delete(int displaynum)
|
||||
{
|
||||
int i;
|
||||
int i;
|
||||
|
||||
if (displaynum > ndisplays || displaynum == 0 || displaynum < -1
|
||||
|| displaypoints[displaynum - 1].exp == NULL) {
|
||||
DEBUG_Printf("Invalid display number\n");
|
||||
return TRUE;
|
||||
}
|
||||
if (displaynum > ndisplays || displaynum == 0 || displaynum < -1 ||
|
||||
displaypoints[displaynum - 1].exp == NULL)
|
||||
{
|
||||
dbg_printf("Invalid display number\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (displaynum == -1) {
|
||||
for (i = 0; i < ndisplays; i++) {
|
||||
if (displaypoints[i].exp != NULL) {
|
||||
DEBUG_FreeExpr(displaypoints[i].exp);
|
||||
displaypoints[i].exp = NULL;
|
||||
}
|
||||
}
|
||||
displaypoints = DBG_realloc(displaypoints,
|
||||
(maxdisplays = DISPTAB_DELTA) * sizeof(*displaypoints));
|
||||
ndisplays = 0;
|
||||
} else if (displaypoints[--displaynum].exp != NULL) {
|
||||
DEBUG_FreeExpr(displaypoints[displaynum].exp);
|
||||
displaypoints[displaynum].exp = NULL;
|
||||
while (displaynum == ndisplays - 1
|
||||
&& displaypoints[displaynum].exp == NULL)
|
||||
--ndisplays, --displaynum;
|
||||
if (maxdisplays - ndisplays >= 2 * DISPTAB_DELTA) {
|
||||
maxdisplays = (ndisplays + DISPTAB_DELTA - 1) & ~(DISPTAB_DELTA - 1); /* MARK */
|
||||
displaypoints = DBG_realloc(displaypoints,
|
||||
maxdisplays * sizeof(*displaypoints));
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
if (displaynum == -1)
|
||||
{
|
||||
for (i = 0; i < ndisplays; i++)
|
||||
{
|
||||
if (displaypoints[i].exp != NULL)
|
||||
{
|
||||
expr_free(displaypoints[i].exp);
|
||||
displaypoints[i].exp = NULL;
|
||||
}
|
||||
}
|
||||
maxdisplays = DISPTAB_DELTA;
|
||||
displaypoints = dbg_heap_realloc(displaypoints,
|
||||
(maxdisplays = DISPTAB_DELTA) * sizeof(*displaypoints));
|
||||
ndisplays = 0;
|
||||
}
|
||||
else if (displaypoints[--displaynum].exp != NULL)
|
||||
{
|
||||
expr_free(displaypoints[displaynum].exp);
|
||||
displaypoints[displaynum].exp = NULL;
|
||||
while (displaynum == ndisplays - 1 && displaypoints[displaynum].exp == NULL)
|
||||
{
|
||||
--ndisplays;
|
||||
--displaynum;
|
||||
}
|
||||
if (maxdisplays - ndisplays >= 2 * DISPTAB_DELTA)
|
||||
{
|
||||
/* MARK */
|
||||
maxdisplays = (ndisplays + DISPTAB_DELTA - 1) & ~(DISPTAB_DELTA - 1);
|
||||
displaypoints = dbg_heap_realloc(displaypoints,
|
||||
maxdisplays * sizeof(*displaypoints));
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int DEBUG_EnableDisplay(int displaynum, int enable)
|
||||
int display_enable(int displaynum, int enable)
|
||||
{
|
||||
--displaynum;
|
||||
if (displaynum >= ndisplays || displaynum < 0 || displaypoints[displaynum].exp == NULL) {
|
||||
DEBUG_Printf("Invalid display number\n");
|
||||
return TRUE;
|
||||
}
|
||||
char buffer[sizeof(SYMBOL_INFO) + 256];
|
||||
SYMBOL_INFO* func;
|
||||
|
||||
displaypoints[displaynum].enabled = enable;
|
||||
if (!displaypoints[displaynum].function_name
|
||||
|| displaypoints[displaynum].function_name == DEBUG_GetCurrentFrameFunctionName())
|
||||
DEBUG_PrintOneDisplay(displaynum);
|
||||
func = (SYMBOL_INFO*)buffer;
|
||||
func->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
func->MaxNameLen = sizeof(buffer) - sizeof(*func);
|
||||
if (!stack_get_frame(func, NULL)) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
--displaynum;
|
||||
if (displaynum >= ndisplays || displaynum < 0 ||
|
||||
displaypoints[displaynum].exp == NULL)
|
||||
{
|
||||
dbg_printf("Invalid display number\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
displaypoints[displaynum].enabled = enable;
|
||||
if (!displaypoints[displaynum].func ||
|
||||
cmp_symbol(displaypoints[displaynum].func, func))
|
||||
{
|
||||
print_one_display(displaynum);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -1,610 +0,0 @@
|
|||
/*
|
||||
* File elf.c - processing of ELF files
|
||||
*
|
||||
* Copyright (C) 1996, Eric Youngdale.
|
||||
* 1999-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
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX MAX_PATH
|
||||
#endif
|
||||
|
||||
#include "debugger.h"
|
||||
|
||||
#if defined(__svr4__) || defined(__sun)
|
||||
#define __ELF__
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ELF_H
|
||||
# include <elf.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_ELF32_H
|
||||
# include <sys/elf32.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_EXEC_ELF_H
|
||||
# include <sys/exec_elf.h>
|
||||
#endif
|
||||
#if !defined(DT_NUM)
|
||||
# if defined(DT_COUNT)
|
||||
# define DT_NUM DT_COUNT
|
||||
# else
|
||||
/* this seems to be a satisfactory value on Solaris, which doesn't support this AFAICT */
|
||||
# define DT_NUM 24
|
||||
# endif
|
||||
#endif
|
||||
#ifdef HAVE_LINK_H
|
||||
# include <link.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_LINK_H
|
||||
# include <sys/link.h>
|
||||
#endif
|
||||
|
||||
#include "wine/debug.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
|
||||
|
||||
typedef struct tagELF_DBG_INFO
|
||||
{
|
||||
void *elf_addr;
|
||||
} ELF_DBG_INFO;
|
||||
|
||||
#ifdef __ELF__
|
||||
|
||||
/*
|
||||
* Walk through the entire symbol table and add any symbols we find there.
|
||||
* This can be used in cases where we have stripped ELF shared libraries,
|
||||
* or it can be used in cases where we have data symbols for which the address
|
||||
* isn't encoded in the stabs.
|
||||
*
|
||||
* This is all really quite easy, since we don't have to worry about line
|
||||
* numbers or local data variables.
|
||||
*/
|
||||
static int DEBUG_ProcessElfSymtab(DBG_MODULE* module, const char* addr,
|
||||
void *load_addr, const Elf32_Shdr* symtab,
|
||||
const Elf32_Shdr* strtab)
|
||||
{
|
||||
const char* curfile = NULL;
|
||||
struct name_hash* curr_sym = NULL;
|
||||
int flags;
|
||||
int i;
|
||||
DBG_VALUE new_value;
|
||||
int nsym;
|
||||
const char* strp;
|
||||
const char* symname;
|
||||
const Elf32_Sym* symp;
|
||||
|
||||
symp = (Elf32_Sym *)(addr + symtab->sh_offset);
|
||||
nsym = symtab->sh_size / sizeof(*symp);
|
||||
strp = (char *)(addr + strtab->sh_offset);
|
||||
|
||||
for (i = 0; i < nsym; i++, symp++)
|
||||
{
|
||||
/*
|
||||
* Ignore certain types of entries which really aren't of that much
|
||||
* interest.
|
||||
*/
|
||||
if (ELF32_ST_TYPE(symp->st_info) == STT_SECTION ||
|
||||
symp->st_shndx == SHN_UNDEF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
symname = strp + symp->st_name;
|
||||
|
||||
/*
|
||||
* Save the name of the current file, so we have a way of tracking
|
||||
* static functions/data.
|
||||
*/
|
||||
if (ELF32_ST_TYPE(symp->st_info) == STT_FILE)
|
||||
{
|
||||
curfile = symname;
|
||||
continue;
|
||||
}
|
||||
|
||||
new_value.type = NULL;
|
||||
new_value.addr.seg = 0;
|
||||
new_value.addr.off = (unsigned long)load_addr + symp->st_value;
|
||||
new_value.cookie = DV_TARGET;
|
||||
flags = SYM_WINE | ((ELF32_ST_TYPE(symp->st_info) == STT_FUNC)
|
||||
? SYM_FUNC : SYM_DATA);
|
||||
if (ELF32_ST_BIND(symp->st_info) == STB_GLOBAL)
|
||||
curr_sym = DEBUG_AddSymbol(symname, &new_value, NULL, flags);
|
||||
else
|
||||
curr_sym = DEBUG_AddSymbol(symname, &new_value, curfile, flags);
|
||||
|
||||
/*
|
||||
* Record the size of the symbol. This can come in handy in
|
||||
* some cases. Not really used yet, however.
|
||||
*/
|
||||
if (symp->st_size != 0)
|
||||
DEBUG_SetSymbolSize(curr_sym, symp->st_size);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads the symbolic information from ELF module stored in 'filename'
|
||||
* the module has been loaded at 'load_offset' address, so symbols' address
|
||||
* relocation is performed
|
||||
* returns
|
||||
* -1 if the file cannot be found/opened
|
||||
* 0 if the file doesn't contain symbolic info (or this info cannot be
|
||||
* read or parsed)
|
||||
* 1 on success
|
||||
*/
|
||||
enum DbgInfoLoad DEBUG_LoadElfStabs(DBG_MODULE* module)
|
||||
{
|
||||
enum DbgInfoLoad dil = DIL_ERROR;
|
||||
char* addr = (char*)0xffffffff;
|
||||
int fd = -1;
|
||||
struct stat statbuf;
|
||||
const Elf32_Ehdr* ehptr;
|
||||
const Elf32_Shdr* spnt;
|
||||
const char* shstrtab;
|
||||
int i;
|
||||
int stabsect, stabstrsect, debugsect;
|
||||
|
||||
if (module->type != DMT_ELF || !module->elf_dbg_info)
|
||||
{
|
||||
WINE_ERR("Bad elf module '%s'\n", module->module_name);
|
||||
return DIL_ERROR;
|
||||
}
|
||||
|
||||
/* check that the file exists, and that the module hasn't been loaded yet */
|
||||
if (stat(module->module_name, &statbuf) == -1) goto leave;
|
||||
if (S_ISDIR(statbuf.st_mode)) goto leave;
|
||||
|
||||
/*
|
||||
* Now open the file, so that we can mmap() it.
|
||||
*/
|
||||
if ((fd = open(module->module_name, O_RDONLY)) == -1) goto leave;
|
||||
|
||||
dil = DIL_NOINFO;
|
||||
/*
|
||||
* Now mmap() the file.
|
||||
*/
|
||||
addr = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (addr == (char*)0xffffffff) goto leave;
|
||||
|
||||
/*
|
||||
* Next, we need to find a few of the internal ELF headers within
|
||||
* this thing. We need the main executable header, and the section
|
||||
* table.
|
||||
*/
|
||||
ehptr = (Elf32_Ehdr*)addr;
|
||||
spnt = (Elf32_Shdr*)(addr + ehptr->e_shoff);
|
||||
shstrtab = (addr + spnt[ehptr->e_shstrndx].sh_offset);
|
||||
|
||||
stabsect = stabstrsect = debugsect = -1;
|
||||
|
||||
for (i = 0; i < ehptr->e_shnum; i++)
|
||||
{
|
||||
if (strcmp(shstrtab + spnt[i].sh_name, ".stab") == 0)
|
||||
stabsect = i;
|
||||
if (strcmp(shstrtab + spnt[i].sh_name, ".stabstr") == 0)
|
||||
stabstrsect = i;
|
||||
if (strcmp(shstrtab + spnt[i].sh_name, ".debug_info") == 0)
|
||||
debugsect = i;
|
||||
}
|
||||
|
||||
if (stabsect != -1 && stabstrsect != -1)
|
||||
{
|
||||
/*
|
||||
* OK, now just parse all of the stabs.
|
||||
*/
|
||||
if (DEBUG_ParseStabs(addr,
|
||||
module->elf_dbg_info->elf_addr,
|
||||
spnt[stabsect].sh_offset,
|
||||
spnt[stabsect].sh_size,
|
||||
spnt[stabstrsect].sh_offset,
|
||||
spnt[stabstrsect].sh_size))
|
||||
{
|
||||
dil = DIL_LOADED;
|
||||
}
|
||||
else
|
||||
{
|
||||
dil = DIL_ERROR;
|
||||
WINE_WARN("Couldn't read correctly read stabs\n");
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
else if (debugsect != -1)
|
||||
{
|
||||
/* Dwarf 2 debug information */
|
||||
dil = DIL_NOT_SUPPORTED;
|
||||
}
|
||||
/* now load dynamic symbol info */
|
||||
for (i = 0; i < ehptr->e_shnum; i++)
|
||||
{
|
||||
if ((strcmp(shstrtab + spnt[i].sh_name, ".symtab") == 0) &&
|
||||
(spnt[i].sh_type == SHT_SYMTAB))
|
||||
DEBUG_ProcessElfSymtab(module, addr, module->elf_dbg_info->elf_addr,
|
||||
spnt + i, spnt + spnt[i].sh_link);
|
||||
|
||||
if ((strcmp(shstrtab + spnt[i].sh_name, ".dynsym") == 0) &&
|
||||
(spnt[i].sh_type == SHT_DYNSYM))
|
||||
DEBUG_ProcessElfSymtab(module, addr, module->elf_dbg_info->elf_addr,
|
||||
spnt + i, spnt + spnt[i].sh_link);
|
||||
}
|
||||
|
||||
leave:
|
||||
if (addr != (char*)0xffffffff) munmap(addr, statbuf.st_size);
|
||||
if (fd != -1) close(fd);
|
||||
|
||||
return dil;
|
||||
}
|
||||
|
||||
static unsigned is_dt_flag_valid(unsigned d_tag)
|
||||
{
|
||||
#ifndef DT_PROCNUM
|
||||
#define DT_PROCNUM 0
|
||||
#endif
|
||||
#ifndef DT_EXTRANUM
|
||||
#define DT_EXTRANUM 0
|
||||
#endif
|
||||
return (d_tag >= 0 && d_tag < DT_NUM + DT_PROCNUM + DT_EXTRANUM)
|
||||
#if defined(DT_LOOS) && defined(DT_HIOS)
|
||||
|| (d_tag >= DT_LOOS && d_tag < DT_HIOS)
|
||||
#endif
|
||||
#if defined(DT_LOPROC) && defined(DT_HIPROC)
|
||||
|| (d_tag >= DT_LOPROC && d_tag < DT_HIPROC)
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads the information for ELF module stored in 'filename'
|
||||
* the module has been loaded at 'load_offset' address
|
||||
* returns
|
||||
* -1 if the file cannot be found/opened
|
||||
* 0 if the file doesn't contain symbolic info (or this info cannot be
|
||||
* read or parsed)
|
||||
* 1 on success
|
||||
*/
|
||||
static enum DbgInfoLoad DEBUG_ProcessElfFile(HANDLE hProcess,
|
||||
const char* filename,
|
||||
void *load_offset,
|
||||
struct elf_info* elf_info)
|
||||
{
|
||||
static const unsigned char elf_signature[4] = { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3 };
|
||||
enum DbgInfoLoad dil = DIL_ERROR;
|
||||
const char* addr = (char*)0xffffffff;
|
||||
int fd = -1;
|
||||
struct stat statbuf;
|
||||
const Elf32_Ehdr* ehptr;
|
||||
const Elf32_Shdr* spnt;
|
||||
const Elf32_Phdr* ppnt;
|
||||
const char* shstrtab;
|
||||
int i;
|
||||
DWORD delta;
|
||||
|
||||
WINE_TRACE("Processing elf file '%s' at %p\n", filename, load_offset);
|
||||
|
||||
/* check that the file exists, and that the module hasn't been loaded yet */
|
||||
if (stat(filename, &statbuf) == -1) goto leave;
|
||||
|
||||
/*
|
||||
* Now open the file, so that we can mmap() it.
|
||||
*/
|
||||
if ((fd = open(filename, O_RDONLY)) == -1) goto leave;
|
||||
|
||||
/*
|
||||
* Now mmap() the file.
|
||||
*/
|
||||
addr = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (addr == (char*)-1) goto leave;
|
||||
|
||||
dil = DIL_NOINFO;
|
||||
|
||||
/*
|
||||
* Next, we need to find a few of the internal ELF headers within
|
||||
* this thing. We need the main executable header, and the section
|
||||
* table.
|
||||
*/
|
||||
ehptr = (Elf32_Ehdr*)addr;
|
||||
if (memcmp(ehptr->e_ident, elf_signature, sizeof(elf_signature))) goto leave;
|
||||
|
||||
spnt = (Elf32_Shdr*)(addr + ehptr->e_shoff);
|
||||
shstrtab = (addr + spnt[ehptr->e_shstrndx].sh_offset);
|
||||
|
||||
/* if non relocatable ELF, then remove fixed address from computation
|
||||
* otherwise, all addresses are zero based
|
||||
*/
|
||||
delta = (load_offset == 0) ? ehptr->e_entry : 0;
|
||||
|
||||
/* grab size of module once loaded in memory */
|
||||
ppnt = (Elf32_Phdr*)(addr + ehptr->e_phoff);
|
||||
elf_info->size = 0;
|
||||
for (i = 0; i < ehptr->e_phnum; i++)
|
||||
{
|
||||
if (ppnt[i].p_type != PT_LOAD) continue;
|
||||
elf_info->size += (ppnt[i].p_align <= 1) ? ppnt[i].p_memsz :
|
||||
(ppnt[i].p_memsz + ppnt[i].p_align - 1) & ~(ppnt[i].p_align - 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < ehptr->e_shnum; i++)
|
||||
{
|
||||
if (strcmp(shstrtab + spnt[i].sh_name, ".dynamic") == 0 &&
|
||||
spnt[i].sh_type == SHT_DYNAMIC)
|
||||
{
|
||||
if (elf_info->flags & ELF_INFO_DEBUG_HEADER)
|
||||
{
|
||||
Elf32_Dyn dyn;
|
||||
char* ptr = (char*)spnt[i].sh_addr;
|
||||
unsigned long len;
|
||||
|
||||
do
|
||||
{
|
||||
if (!ReadProcessMemory(hProcess, ptr, &dyn, sizeof(dyn), &len) ||
|
||||
len != sizeof(dyn) || !is_dt_flag_valid(dyn.d_tag))
|
||||
dyn.d_tag = DT_NULL;
|
||||
ptr += sizeof(dyn);
|
||||
} while (dyn.d_tag != DT_DEBUG && dyn.d_tag != DT_NULL);
|
||||
if (dyn.d_tag == DT_NULL)
|
||||
{
|
||||
dil = DIL_ERROR;
|
||||
goto leave;
|
||||
}
|
||||
elf_info->dbg_hdr_addr = dyn.d_un.d_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elf_info->segments[0] = elf_info->segments[1] = elf_info->segments[2] = 0;
|
||||
if (elf_info->flags & ELF_INFO_PATH)
|
||||
{
|
||||
strncpy(elf_info->elf_path, filename, elf_info->elf_path_len);
|
||||
elf_info->elf_path[elf_info->elf_path_len - 1] = '\0';
|
||||
}
|
||||
|
||||
elf_info->load_addr = (load_offset == 0) ? (void *)ehptr->e_entry : load_offset;
|
||||
|
||||
if (elf_info->flags & ELF_INFO_MODULE)
|
||||
{
|
||||
DBG_MODULE* module;
|
||||
|
||||
module = DEBUG_AddModule(filename, DMT_ELF, elf_info->load_addr, elf_info->size, 0);
|
||||
if (module)
|
||||
{
|
||||
if ((module->elf_dbg_info = DBG_alloc(sizeof(ELF_DBG_INFO))) == NULL)
|
||||
{
|
||||
WINE_ERR("OOM\n");
|
||||
exit(0);
|
||||
}
|
||||
module->elf_dbg_info->elf_addr = load_offset;
|
||||
module->dil = dil = DEBUG_LoadElfStabs(module);
|
||||
}
|
||||
else dil = DIL_ERROR;
|
||||
}
|
||||
|
||||
leave:
|
||||
if (addr != (char*)0xffffffff) munmap((void*)addr, statbuf.st_size);
|
||||
if (fd != -1) close(fd);
|
||||
|
||||
return dil;
|
||||
}
|
||||
|
||||
static enum DbgInfoLoad DEBUG_ProcessElfFileFromPath(HANDLE hProcess,
|
||||
const char * filename,
|
||||
void *load_offset,
|
||||
const char* path,
|
||||
struct elf_info* elf_info)
|
||||
{
|
||||
enum DbgInfoLoad dil = DIL_ERROR;
|
||||
char *s, *t, *fn;
|
||||
char* paths = NULL;
|
||||
|
||||
if (!path) return dil;
|
||||
|
||||
for (s = paths = DBG_strdup(path); s && *s; s = (t) ? (t+1) : NULL)
|
||||
{
|
||||
t = strchr(s, ':');
|
||||
if (t) *t = '\0';
|
||||
fn = (char*)DBG_alloc(strlen(filename) + 1 + strlen(s) + 1);
|
||||
if (!fn) break;
|
||||
strcpy(fn, s );
|
||||
strcat(fn, "/");
|
||||
strcat(fn, filename);
|
||||
dil = DEBUG_ProcessElfFile(hProcess, fn, load_offset, elf_info);
|
||||
DBG_free(fn);
|
||||
if (dil != DIL_ERROR) break;
|
||||
s = (t) ? (t+1) : NULL;
|
||||
}
|
||||
|
||||
DBG_free(paths);
|
||||
return dil;
|
||||
}
|
||||
|
||||
static enum DbgInfoLoad DEBUG_ProcessElfObject(HANDLE hProcess,
|
||||
const char* filename,
|
||||
void *load_offset,
|
||||
struct elf_info* elf_info)
|
||||
{
|
||||
enum DbgInfoLoad dil = DIL_ERROR;
|
||||
|
||||
if (filename == NULL || *filename == '\0') return DIL_ERROR;
|
||||
if (DEBUG_FindModuleByName(filename, DMT_ELF))
|
||||
{
|
||||
assert(!(elf_info->flags & ELF_INFO_PATH));
|
||||
return DIL_LOADED;
|
||||
}
|
||||
|
||||
if (strstr(filename, "libstdc++")) return DIL_ERROR; /* We know we can't do it */
|
||||
dil = DEBUG_ProcessElfFile(hProcess, filename, load_offset, elf_info);
|
||||
/* if relative pathname, try some absolute base dirs */
|
||||
if (dil == DIL_ERROR && !strchr(filename, '/'))
|
||||
{
|
||||
dil = DEBUG_ProcessElfFileFromPath(hProcess, filename, load_offset,
|
||||
getenv("PATH"), elf_info);
|
||||
if (dil == DIL_ERROR)
|
||||
dil = DEBUG_ProcessElfFileFromPath(hProcess, filename, load_offset,
|
||||
getenv("LD_LIBRARY_PATH"), elf_info);
|
||||
if (dil == DIL_ERROR)
|
||||
dil = DEBUG_ProcessElfFileFromPath(hProcess, filename, load_offset,
|
||||
getenv("WINEDLLPATH"), elf_info);
|
||||
}
|
||||
|
||||
DEBUG_ReportDIL(dil, "ELF", filename, load_offset);
|
||||
|
||||
return dil;
|
||||
}
|
||||
|
||||
static BOOL DEBUG_WalkList(const struct r_debug* dbg_hdr)
|
||||
{
|
||||
void* lm_addr;
|
||||
struct link_map lm;
|
||||
char bufstr[256];
|
||||
struct elf_info elf_info;
|
||||
|
||||
elf_info.flags = ELF_INFO_MODULE;
|
||||
/*
|
||||
* Now walk the linked list. In all known ELF implementations,
|
||||
* the dynamic loader maintains this linked list for us. In some
|
||||
* cases the first entry doesn't appear with a name, in other cases it
|
||||
* does.
|
||||
*/
|
||||
for (lm_addr = (void *)dbg_hdr->r_map; lm_addr; lm_addr = (void *)lm.l_next)
|
||||
{
|
||||
if (!DEBUG_READ_MEM_VERBOSE(lm_addr, &lm, sizeof(lm)))
|
||||
return FALSE;
|
||||
|
||||
if (lm.l_prev != NULL && /* skip first entry, normally debuggee itself */
|
||||
lm.l_name != NULL &&
|
||||
DEBUG_READ_MEM_VERBOSE((void*)lm.l_name, bufstr, sizeof(bufstr)))
|
||||
{
|
||||
bufstr[sizeof(bufstr) - 1] = '\0';
|
||||
DEBUG_ProcessElfObject(DEBUG_CurrProcess->handle, bufstr,
|
||||
(void *)lm.l_addr, &elf_info);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL DEBUG_RescanElf(void)
|
||||
{
|
||||
struct r_debug dbg_hdr;
|
||||
|
||||
if (DEBUG_CurrProcess &&
|
||||
DEBUG_READ_MEM_VERBOSE((void*)DEBUG_CurrProcess->dbg_hdr_addr, &dbg_hdr, sizeof(dbg_hdr)))
|
||||
{
|
||||
switch (dbg_hdr.r_state)
|
||||
{
|
||||
case RT_CONSISTENT:
|
||||
DEBUG_WalkList(&dbg_hdr);
|
||||
DEBUG_CheckDelayedBP();
|
||||
break;
|
||||
case RT_ADD:
|
||||
break;
|
||||
case RT_DELETE:
|
||||
/* FIXME: this is not currently handled */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* DEBUG_ReadWineLoaderDbgInfo
|
||||
*
|
||||
* Try to find a decent wine executable which could have loader the debuggee
|
||||
*/
|
||||
enum DbgInfoLoad DEBUG_ReadWineLoaderDbgInfo(HANDLE hProcess, struct elf_info* elf_info)
|
||||
{
|
||||
const char* ptr;
|
||||
enum DbgInfoLoad dil;
|
||||
|
||||
/* All binaries are loaded with WINELOADER (if run from tree) or by the
|
||||
* main executable (either wine-kthread or wine-pthread)
|
||||
* Note: the heuristic used to know whether we need to load wine-pthread or
|
||||
* wine-kthread is not 100% safe
|
||||
*/
|
||||
elf_info->flags |= ELF_INFO_DEBUG_HEADER;
|
||||
if ((ptr = getenv("WINELOADER")))
|
||||
dil = DEBUG_ProcessElfObject(hProcess, ptr, 0, elf_info);
|
||||
else
|
||||
{
|
||||
if ((dil = DEBUG_ProcessElfObject(hProcess, "wine-kthread", 0, elf_info)) == DIL_ERROR)
|
||||
dil = DEBUG_ProcessElfObject(hProcess, "wine-pthread", 0, elf_info);
|
||||
}
|
||||
return dil;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* DEBUG_SetElfSoLoadBreakpoint
|
||||
*
|
||||
* Sets a breakpoint to handle .so loading events, so we can add debug info
|
||||
* on the fly
|
||||
*/
|
||||
BOOL DEBUG_SetElfSoLoadBreakpoint(const struct elf_info* elf_info)
|
||||
{
|
||||
struct r_debug dbg_hdr;
|
||||
|
||||
/*
|
||||
* OK, now dig into the actual tables themselves.
|
||||
*/
|
||||
if (!DEBUG_READ_MEM_VERBOSE((void*)elf_info->dbg_hdr_addr, &dbg_hdr, sizeof(dbg_hdr)))
|
||||
return FALSE;
|
||||
|
||||
assert(!DEBUG_CurrProcess->dbg_hdr_addr);
|
||||
DEBUG_CurrProcess->dbg_hdr_addr = elf_info->dbg_hdr_addr;
|
||||
|
||||
if (dbg_hdr.r_brk)
|
||||
{
|
||||
DBG_VALUE value;
|
||||
|
||||
WINE_TRACE("Setting up a breakpoint on r_brk(%lx)\n",
|
||||
(unsigned long)dbg_hdr.r_brk);
|
||||
|
||||
DEBUG_SetBreakpoints(FALSE);
|
||||
value.type = NULL;
|
||||
value.cookie = DV_TARGET;
|
||||
value.addr.seg = 0;
|
||||
value.addr.off = (DWORD)dbg_hdr.r_brk;
|
||||
DEBUG_AddBreakpoint(&value, DEBUG_RescanElf, TRUE);
|
||||
DEBUG_SetBreakpoints(TRUE);
|
||||
}
|
||||
|
||||
return DEBUG_WalkList(&dbg_hdr);
|
||||
}
|
||||
|
||||
#else /* !__ELF__ */
|
||||
|
||||
enum DbgInfoLoad DEBUG_ReadWineLoaderDbgInfo(HANDLE hProcess, struct elf_info* elf_info)
|
||||
{
|
||||
return DIL_ERROR;
|
||||
}
|
||||
|
||||
BOOL DEBUG_SetElfSoLoadBreakpoint(const struct elf_info* elf_info)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#endif /* __ELF__ */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
* Convenience functions to handle use of external debugger.
|
||||
*
|
||||
* Copyright 1999 Kevin Holbrook
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "wine/port.h"
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DBG_BUFF_SIZE 12
|
||||
|
||||
#define DBG_EXTERNAL_DEFAULT "gdb"
|
||||
#define DBG_LOCATION_DEFAULT "/usr/local/bin/wine"
|
||||
#define DBG_SLEEPTIME_DEFAULT 120
|
||||
|
||||
|
||||
|
||||
/* DEBUG_ExternalDebugger
|
||||
*
|
||||
* This function invokes an external debugger on the current
|
||||
* wine process. The form of the command executed is:
|
||||
* <debugger image> <wine image> <attach process id>
|
||||
*
|
||||
* The debugger command is normally invoked by a newly created xterm.
|
||||
*
|
||||
* The current calling process is temporarily put to sleep
|
||||
* so that the invoked debugger has time to come up and attach.
|
||||
*
|
||||
* The following environment variables may be used:
|
||||
*
|
||||
* Name Use Default
|
||||
* -------------------------------------------------------------------------------------
|
||||
* WINE_DBG_EXTERNAL debugger command to invoke ("gdb")
|
||||
* WINE_DBG_LOCATION fully qualified location of wine image ("/usr/local/bin/wine")
|
||||
* WINE_DBG_NO_XTERM if set do not invoke xterm with command (not set)
|
||||
* WINE_DBG_SLEEPTIME number of seconds to make process sleep (120)
|
||||
*
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* #include "wine/debug.h"
|
||||
*
|
||||
* DEBUG_ExternalDebugger();
|
||||
*
|
||||
*
|
||||
* Environment Example:
|
||||
*
|
||||
* export WINE_DBG_EXTERNAL="ddd"
|
||||
* export WINE_DBG_NO_XTERM=1
|
||||
* export WINE_DBG_SLEEPTIME=60
|
||||
*
|
||||
*/
|
||||
|
||||
void DEBUG_ExternalDebugger(void)
|
||||
{
|
||||
pid_t attach_pid;
|
||||
pid_t child_pid;
|
||||
int dbg_sleep_secs = DBG_SLEEPTIME_DEFAULT;
|
||||
char *dbg_sleeptime;
|
||||
|
||||
|
||||
dbg_sleeptime = getenv("WINE_DBG_SLEEPTIME");
|
||||
|
||||
/* convert sleep time string to integer seconds */
|
||||
if (dbg_sleeptime)
|
||||
{
|
||||
dbg_sleep_secs = atoi(dbg_sleeptime);
|
||||
|
||||
/* check for conversion error */
|
||||
if (dbg_sleep_secs == 0)
|
||||
dbg_sleep_secs = DBG_SLEEPTIME_DEFAULT;
|
||||
}
|
||||
|
||||
/* get the current process id */
|
||||
attach_pid = getpid();
|
||||
|
||||
/* create new process */
|
||||
child_pid = fork();
|
||||
|
||||
/* check if we are the child process */
|
||||
if (child_pid == 0)
|
||||
{
|
||||
int status;
|
||||
const char *dbg_external;
|
||||
const char *dbg_wine_location;
|
||||
const char *dbg_no_xterm;
|
||||
char pid_string[DBG_BUFF_SIZE];
|
||||
|
||||
|
||||
/* check settings in environment for debugger to use */
|
||||
dbg_external = getenv("WINE_DBG_EXTERNAL");
|
||||
dbg_wine_location = getenv("WINE_DBG_LOCATION");
|
||||
dbg_no_xterm = getenv("WINE_DBG_NO_XTERM");
|
||||
|
||||
/* if not set in environment, use default */
|
||||
if (!dbg_external)
|
||||
dbg_external = "gdb";
|
||||
|
||||
/* if not set in environment, use default */
|
||||
if (!dbg_wine_location)
|
||||
if (!(dbg_wine_location = getenv("WINELOADER")))
|
||||
dbg_wine_location = "miscemu/wine";
|
||||
|
||||
/* check for empty string in WINE_DBG_NO_XTERM */
|
||||
if (dbg_no_xterm && (strlen(dbg_no_xterm) < 1))
|
||||
dbg_no_xterm = NULL;
|
||||
|
||||
/* clear the buffer */
|
||||
memset(pid_string, 0, DBG_BUFF_SIZE);
|
||||
|
||||
/* make pid into string */
|
||||
snprintf(pid_string, sizeof(pid_string), "%ld", (long) attach_pid);
|
||||
|
||||
/* now exec the debugger to get it's own clean memory space */
|
||||
if (dbg_no_xterm)
|
||||
status = execlp(dbg_external, dbg_external, dbg_wine_location, pid_string, NULL);
|
||||
else
|
||||
status = execlp("xterm", "xterm", "-e", dbg_external, dbg_wine_location, pid_string, NULL);
|
||||
|
||||
if (status == -1)
|
||||
{
|
||||
if (dbg_no_xterm)
|
||||
fprintf(stderr, "DEBUG_ExternalDebugger failed to execute \"%s %s %s\" (%s)\n",
|
||||
dbg_external, dbg_wine_location, pid_string, strerror(errno));
|
||||
else
|
||||
fprintf(stderr, "DEBUG_ExternalDebugger failed to execute \"xterm -e %s %s %s\" (%s)\n",
|
||||
dbg_external, dbg_wine_location, pid_string, strerror(errno));
|
||||
}
|
||||
|
||||
}
|
||||
else if (child_pid != -1)
|
||||
{
|
||||
/* make the parent/caller sleep so the child/debugger can catch it */
|
||||
sleep(dbg_sleep_secs);
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "DEBUG_ExternalDebugger failed.\n");
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -20,52 +20,16 @@
|
|||
*/
|
||||
|
||||
/* break handling */
|
||||
INTERNAL_VAR(BreakAllThreadsStartup, FALSE, NULL, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(BreakOnCritSectTimeOut, FALSE, NULL, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(BreakOnAttach, FALSE, NULL, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(BreakOnFirstChance, TRUE, NULL, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(BreakOnDllLoad, FALSE, NULL, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(CanDeferOnBPByAddr, FALSE, NULL, DT_BASIC_CONST_INT)
|
||||
|
||||
/* debugging debugger */
|
||||
INTERNAL_VAR(ExtDbgOnInvalidAddress, FALSE, NULL, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(ExtDbgOnInternalException, FALSE, NULL, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(BreakAllThreadsStartup, FALSE, NULL, dbg_itype_unsigned_int)
|
||||
INTERNAL_VAR(BreakOnCritSectTimeOut, FALSE, NULL, dbg_itype_unsigned_int)
|
||||
INTERNAL_VAR(BreakOnAttach, FALSE, NULL, dbg_itype_unsigned_int)
|
||||
INTERNAL_VAR(BreakOnFirstChance, TRUE, NULL, dbg_itype_unsigned_int)
|
||||
INTERNAL_VAR(BreakOnDllLoad, FALSE, NULL, dbg_itype_unsigned_int)
|
||||
INTERNAL_VAR(CanDeferOnBPByAddr, FALSE, NULL, dbg_itype_unsigned_int)
|
||||
|
||||
/* current process/thread */
|
||||
INTERNAL_VAR(ThreadId, FALSE, &DEBUG_CurrTid, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(ProcessId, FALSE, &DEBUG_CurrPid, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(ThreadId, FALSE, &dbg_curr_tid, dbg_itype_unsigned_int)
|
||||
INTERNAL_VAR(ProcessId, FALSE, &dbg_curr_pid, dbg_itype_unsigned_int)
|
||||
|
||||
/* context manipulation */
|
||||
#ifdef __i386__
|
||||
/* FIXME: 16 bit registers use imply that CPU is little endian, which is
|
||||
* the case when running natively i386 code
|
||||
*/
|
||||
INTERNAL_VAR(eip, 0, &DEBUG_context.Eip, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(ip, 0, &DEBUG_context.Eip, DT_BASIC_USHORTINT)
|
||||
INTERNAL_VAR(pc, 0, &DEBUG_context.Eip, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(flags, 0, &DEBUG_context.EFlags, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(esp, 0, &DEBUG_context.Esp, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(sp, 0, &DEBUG_context.Esp, DT_BASIC_USHORTINT)
|
||||
INTERNAL_VAR(eax, 0, &DEBUG_context.Eax, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(ax, 0, &DEBUG_context.Eax, DT_BASIC_USHORTINT)
|
||||
INTERNAL_VAR(ebx, 0, &DEBUG_context.Ebx, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(bx, 0, &DEBUG_context.Ebx, DT_BASIC_USHORTINT)
|
||||
INTERNAL_VAR(ecx, 0, &DEBUG_context.Ecx, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(cx, 0, &DEBUG_context.Ecx, DT_BASIC_USHORTINT)
|
||||
INTERNAL_VAR(edx, 0, &DEBUG_context.Edx, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(dx, 0, &DEBUG_context.Edx, DT_BASIC_USHORTINT)
|
||||
INTERNAL_VAR(esi, 0, &DEBUG_context.Esi, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(si, 0, &DEBUG_context.Esi, DT_BASIC_USHORTINT)
|
||||
INTERNAL_VAR(edi, 0, &DEBUG_context.Edi, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(di, 0, &DEBUG_context.Edi, DT_BASIC_USHORTINT)
|
||||
INTERNAL_VAR(ebp, 0, &DEBUG_context.Ebp, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(bp, 0, &DEBUG_context.Ebp, DT_BASIC_USHORTINT)
|
||||
INTERNAL_VAR(es, 0, &DEBUG_context.SegEs, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(ds, 0, &DEBUG_context.SegDs, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(cs, 0, &DEBUG_context.SegCs, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(ss, 0, &DEBUG_context.SegSs, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(fs, 0, &DEBUG_context.SegFs, DT_BASIC_CONST_INT)
|
||||
INTERNAL_VAR(gs, 0, &DEBUG_context.SegGs, DT_BASIC_CONST_INT)
|
||||
|
||||
INTERNAL_VAR(regs, 0, (DWORD*)&DEBUG_context, DT_BASIC_CONTEXT)
|
||||
#endif
|
||||
/* symbol manipulation */
|
||||
INTERNAL_VAR(AlwaysShowThunks, FALSE, NULL, dbg_itype_unsigned_int)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Copyright 1993 Eric Youngdale
|
||||
* Copyright 1995 Alexandre Julliard
|
||||
* Copyright 2000 Eric Pouech
|
||||
* Copyright 2000-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
|
||||
|
@ -25,371 +25,581 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "debugger.h"
|
||||
#include "winbase.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
#ifdef __i386__
|
||||
#define IS_VM86_MODE() (DEBUG_context.EFlags & V86_FLAG)
|
||||
#endif
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
|
||||
|
||||
static void DEBUG_Die(const char* msg)
|
||||
void* be_cpu_linearize(HANDLE hThread, const ADDRESS* addr)
|
||||
{
|
||||
DEBUG_Printf(msg);
|
||||
exit(1);
|
||||
assert(addr->Mode == AddrModeFlat);
|
||||
return (void*)addr->Offset;
|
||||
}
|
||||
|
||||
void* DBG_alloc(size_t size)
|
||||
unsigned be_cpu_build_addr(HANDLE hThread, const CONTEXT* ctx, ADDRESS* addr,
|
||||
unsigned seg, unsigned long offset)
|
||||
{
|
||||
void *res = malloc(size ? size : 1);
|
||||
if (res == NULL)
|
||||
DEBUG_Die("Memory exhausted.\n");
|
||||
memset(res, 0, size);
|
||||
return res;
|
||||
addr->Mode = AddrModeFlat;
|
||||
addr->Segment = 0; /* don't need segment */
|
||||
addr->Offset = offset;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void* DBG_realloc(void *ptr, size_t size)
|
||||
void* memory_to_linear_addr(const ADDRESS* addr)
|
||||
{
|
||||
void* res = realloc(ptr, size);
|
||||
if ((res == NULL) && size)
|
||||
DEBUG_Die("Memory exhausted.\n");
|
||||
return res;
|
||||
return be_cpu->linearize(dbg_curr_thread->handle, addr);
|
||||
}
|
||||
|
||||
char* DBG_strdup(const char *str)
|
||||
BOOL memory_get_current_pc(ADDRESS* addr)
|
||||
{
|
||||
char *res = strdup(str);
|
||||
if (!res)
|
||||
DEBUG_Die("Memory exhausted.\n");
|
||||
return res;
|
||||
assert(be_cpu->get_addr);
|
||||
return be_cpu->get_addr(dbg_curr_thread->handle, &dbg_context,
|
||||
be_cpu_addr_pc, addr);
|
||||
}
|
||||
|
||||
void DBG_free(void *ptr)
|
||||
BOOL memory_get_current_stack(ADDRESS* addr)
|
||||
{
|
||||
free(ptr);
|
||||
assert(be_cpu->get_addr);
|
||||
return be_cpu->get_addr(dbg_curr_thread->handle, &dbg_context,
|
||||
be_cpu_addr_stack, addr);
|
||||
}
|
||||
|
||||
enum dbg_mode DEBUG_GetSelectorType( WORD sel )
|
||||
BOOL memory_get_current_frame(ADDRESS* addr)
|
||||
{
|
||||
#ifdef __i386__
|
||||
LDT_ENTRY le;
|
||||
|
||||
if (IS_VM86_MODE()) return MODE_VM86;
|
||||
if (sel == 0) return MODE_32;
|
||||
if (GetThreadSelectorEntry( DEBUG_CurrThread->handle, sel, &le))
|
||||
return le.HighWord.Bits.Default_Big ? MODE_32 : MODE_16;
|
||||
/* selector doesn't exist */
|
||||
return MODE_INVALID;
|
||||
#else
|
||||
return MODE_32;
|
||||
#endif
|
||||
}
|
||||
#ifdef __i386__
|
||||
void DEBUG_FixAddress( DBG_ADDR *addr, DWORD def)
|
||||
{
|
||||
if (addr->seg == 0xffffffff) addr->seg = def;
|
||||
if (DEBUG_IsSelectorSystem(addr->seg)) addr->seg = 0;
|
||||
assert(be_cpu->get_addr);
|
||||
return be_cpu->get_addr(dbg_curr_thread->handle, &dbg_context,
|
||||
be_cpu_addr_frame, addr);
|
||||
}
|
||||
|
||||
/* Determine if sel is a system selector (i.e. not managed by Wine) */
|
||||
BOOL DEBUG_IsSelectorSystem(WORD sel)
|
||||
void memory_report_invalid_addr(const void* addr)
|
||||
{
|
||||
if (IS_VM86_MODE()) return FALSE; /* no system selectors in vm86 mode */
|
||||
return !(sel & 4) || ((sel >> 3) < 17);
|
||||
}
|
||||
#endif /* __i386__ */
|
||||
ADDRESS address;
|
||||
|
||||
DWORD DEBUG_ToLinear( const DBG_ADDR *addr )
|
||||
{
|
||||
#ifdef __i386__
|
||||
LDT_ENTRY le;
|
||||
|
||||
if (IS_VM86_MODE()) return (DWORD)(LOWORD(addr->seg) << 4) + addr->off;
|
||||
|
||||
if (DEBUG_IsSelectorSystem(addr->seg))
|
||||
return addr->off;
|
||||
|
||||
if (GetThreadSelectorEntry( DEBUG_CurrThread->handle, addr->seg, &le)) {
|
||||
return (le.HighWord.Bits.BaseHi << 24) + (le.HighWord.Bits.BaseMid << 16) + le.BaseLow + addr->off;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return addr->off;
|
||||
#endif
|
||||
}
|
||||
|
||||
void DEBUG_GetCurrentAddress( DBG_ADDR *addr )
|
||||
{
|
||||
#ifdef __i386__
|
||||
addr->seg = DEBUG_context.SegCs;
|
||||
|
||||
if (DEBUG_IsSelectorSystem(addr->seg))
|
||||
addr->seg = 0;
|
||||
addr->off = DEBUG_context.Eip;
|
||||
#elif defined(__sparc__)
|
||||
addr->seg = 0;
|
||||
addr->off = DEBUG_context.pc;
|
||||
#elif defined(__powerpc__)
|
||||
addr->seg = 0;
|
||||
addr->off = DEBUG_context.Iar;
|
||||
#else
|
||||
# error You must define GET_IP for this CPU
|
||||
#endif
|
||||
}
|
||||
|
||||
void DEBUG_InvalAddr( const DBG_ADDR* addr )
|
||||
{
|
||||
DEBUG_Printf("*** Invalid address ");
|
||||
DEBUG_PrintAddress(addr, DEBUG_CurrThread ? DEBUG_CurrThread->dbg_mode : MODE_32, FALSE);
|
||||
DEBUG_Printf("\n");
|
||||
if (DBG_IVAR(ExtDbgOnInvalidAddress)) DEBUG_ExternalDebugger();
|
||||
}
|
||||
|
||||
void DEBUG_InvalLinAddr( void* addr )
|
||||
{
|
||||
DBG_ADDR address;
|
||||
|
||||
address.seg = 0;
|
||||
address.off = (unsigned long)addr;
|
||||
DEBUG_InvalAddr( &address );
|
||||
address.Mode = AddrModeFlat;
|
||||
address.Segment = 0;
|
||||
address.Offset = (unsigned long)addr;
|
||||
dbg_printf("*** Invalid address ");
|
||||
print_address(&address, FALSE);
|
||||
dbg_printf("\n");
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_ReadMemory
|
||||
* memory_read_value
|
||||
*
|
||||
* Read a memory value.
|
||||
*/
|
||||
/* FIXME: this function is now getting closer and closer to
|
||||
* DEBUG_ExprGetValue. They should be merged...
|
||||
*/
|
||||
int DEBUG_ReadMemory( const DBG_VALUE* val )
|
||||
BOOL memory_read_value(const struct dbg_lvalue* lvalue, DWORD size, void* result)
|
||||
{
|
||||
int value = 0; /* to clear any unused byte */
|
||||
int os = DEBUG_GetObjectSize(val->type);
|
||||
|
||||
assert(sizeof(value) >= os);
|
||||
|
||||
/* FIXME: only works on little endian systems */
|
||||
|
||||
if (val->cookie == DV_TARGET) {
|
||||
DBG_ADDR addr = val->addr;
|
||||
void* lin;
|
||||
|
||||
#ifdef __i386__
|
||||
DEBUG_FixAddress( &addr, DEBUG_context.SegDs );
|
||||
#endif
|
||||
lin = (void*)DEBUG_ToLinear( &addr );
|
||||
|
||||
DEBUG_READ_MEM_VERBOSE(lin, &value, os);
|
||||
} else {
|
||||
if (val->addr.off)
|
||||
memcpy(&value, (void*)val->addr.off, os);
|
||||
if (lvalue->cookie == DLV_TARGET)
|
||||
{
|
||||
if (!dbg_read_memory_verbose(memory_to_linear_addr(&lvalue->addr), result, size))
|
||||
return FALSE;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_WriteMemory
|
||||
*
|
||||
* Store a value in memory.
|
||||
*/
|
||||
void DEBUG_WriteMemory( const DBG_VALUE* val, int value )
|
||||
{
|
||||
int os = DEBUG_GetObjectSize(val->type);
|
||||
|
||||
assert(sizeof(value) >= os);
|
||||
|
||||
/* FIXME: only works on little endian systems */
|
||||
|
||||
if (val->cookie == DV_TARGET) {
|
||||
DBG_ADDR addr = val->addr;
|
||||
void* lin;
|
||||
|
||||
#ifdef __i386__
|
||||
DEBUG_FixAddress( &addr, DEBUG_context.SegDs );
|
||||
#endif
|
||||
lin = (void*)DEBUG_ToLinear( &addr );
|
||||
DEBUG_WRITE_MEM_VERBOSE(lin, &value, os);
|
||||
} else {
|
||||
memcpy((void*)val->addr.off, &value, os);
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_GrabAddress
|
||||
*
|
||||
* Get the address from a value
|
||||
*/
|
||||
BOOL DEBUG_GrabAddress( DBG_VALUE* value, BOOL fromCode )
|
||||
{
|
||||
assert(value->cookie == DV_TARGET || value->cookie == DV_HOST);
|
||||
|
||||
#ifdef __i386__
|
||||
DEBUG_FixAddress( &value->addr,
|
||||
(fromCode) ? DEBUG_context.SegCs : DEBUG_context.SegDs);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dereference pointer to get actual memory address we need to be
|
||||
* reading. We will use the same segment as what we have already,
|
||||
* and hope that this is a sensible thing to do.
|
||||
*/
|
||||
if (value->type != NULL) {
|
||||
if (value->type == DEBUG_GetBasicType(DT_BASIC_CONST_INT)) {
|
||||
/*
|
||||
* We know that we have the actual offset stored somewhere
|
||||
* else in 32-bit space. Grab it, and we
|
||||
* should be all set.
|
||||
*/
|
||||
unsigned int seg2 = value->addr.seg;
|
||||
value->addr.seg = 0;
|
||||
value->addr.off = DEBUG_GetExprValue(value, NULL);
|
||||
value->addr.seg = seg2;
|
||||
} else {
|
||||
struct datatype * testtype;
|
||||
|
||||
if (DEBUG_TypeDerefPointer(value, &testtype) == 0)
|
||||
return FALSE;
|
||||
if (testtype != NULL || value->type == DEBUG_GetBasicType(DT_BASIC_CONST_INT))
|
||||
value->addr.off = DEBUG_GetExprValue(value, NULL);
|
||||
}
|
||||
} else if (!value->addr.seg && !value->addr.off) {
|
||||
DEBUG_Printf("Invalid expression\n");
|
||||
return FALSE;
|
||||
else
|
||||
{
|
||||
assert(lvalue->addr.Mode == AddrModeFlat);
|
||||
if (!lvalue->addr.Offset) return FALSE;
|
||||
memcpy(result, (void*)lvalue->addr.Offset, size);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_ExamineMemory
|
||||
* memory_write_value
|
||||
*
|
||||
* Store a value in memory.
|
||||
*/
|
||||
BOOL memory_write_value(const struct dbg_lvalue* lvalue, DWORD size, void* value)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
DWORD os;
|
||||
DWORD linear = (DWORD)memory_to_linear_addr(&lvalue->addr);
|
||||
|
||||
os = ~size;
|
||||
types_get_info(linear, lvalue->typeid, TI_GET_LENGTH, &os);
|
||||
assert(size == os);
|
||||
|
||||
/* FIXME: only works on little endian systems */
|
||||
if (lvalue->cookie == DLV_TARGET)
|
||||
{
|
||||
ret = dbg_write_memory_verbose((void*)linear, value, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(lvalue->addr.Mode == AddrModeFlat);
|
||||
memcpy((void*)lvalue->addr.Offset, value, size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* memory_examine
|
||||
*
|
||||
* Implementation of the 'x' command.
|
||||
*/
|
||||
void DEBUG_ExamineMemory( const DBG_VALUE *_value, int count, char format )
|
||||
void memory_examine(const struct dbg_lvalue* lvalue, int count, char format)
|
||||
{
|
||||
DBG_VALUE value = *_value;
|
||||
int i;
|
||||
unsigned char * pnt;
|
||||
|
||||
if (!DEBUG_GrabAddress(&value, (format == 'i'))) return;
|
||||
int i;
|
||||
ADDRESS x;
|
||||
char buffer[256];
|
||||
|
||||
x.Mode = AddrModeFlat;
|
||||
x.Offset = types_extract_as_integer(lvalue);
|
||||
if (format != 'i' && count > 1)
|
||||
{
|
||||
DEBUG_PrintAddress( &value.addr, DEBUG_CurrThread->dbg_mode, FALSE );
|
||||
DEBUG_Printf(": ");
|
||||
print_address(&x, FALSE);
|
||||
dbg_printf(": ");
|
||||
}
|
||||
|
||||
pnt = (void*)DEBUG_ToLinear( &value.addr );
|
||||
|
||||
switch(format)
|
||||
switch (format)
|
||||
{
|
||||
case 'u':
|
||||
if (count == 1) count = 256;
|
||||
DEBUG_nchar += DEBUG_PrintStringW(&value.addr, count);
|
||||
DEBUG_Printf("\n");
|
||||
return;
|
||||
case 's':
|
||||
if (count == 1) count = 256;
|
||||
DEBUG_nchar += DEBUG_PrintStringA(&value.addr, count);
|
||||
DEBUG_Printf("\n");
|
||||
return;
|
||||
case 'i':
|
||||
while (count-- && DEBUG_DisassembleInstruction( &value.addr ));
|
||||
return;
|
||||
case 'g':
|
||||
while (count--)
|
||||
{
|
||||
GUID guid;
|
||||
if (!DEBUG_READ_MEM_VERBOSE(pnt, &guid, sizeof(guid))) break;
|
||||
DEBUG_Printf("{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
|
||||
guid.Data1, guid.Data2, guid.Data3,
|
||||
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
|
||||
guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7] );
|
||||
pnt += sizeof(guid);
|
||||
value.addr.off += sizeof(guid);
|
||||
if (count)
|
||||
{
|
||||
DEBUG_PrintAddress( &value.addr, DEBUG_CurrThread->dbg_mode, FALSE );
|
||||
DEBUG_Printf(": ");
|
||||
}
|
||||
}
|
||||
return;
|
||||
case 'u':
|
||||
if (count == 1) count = 256;
|
||||
memory_get_string(dbg_curr_thread->handle, (void*)x.Offset, lvalue->cookie,
|
||||
TRUE, buffer, min(count, sizeof(buffer)));
|
||||
dbg_printf("%s\n", buffer);
|
||||
return;
|
||||
case 's':
|
||||
if (count == 1) count = 256;
|
||||
memory_get_string(dbg_curr_thread->handle, (void*)x.Offset, lvalue->cookie,
|
||||
FALSE, buffer, min(count, sizeof(buffer)));
|
||||
dbg_printf("%s\n", buffer);
|
||||
return;
|
||||
case 'i':
|
||||
while (count-- && memory_disasm_one_insn(&x));
|
||||
return;
|
||||
case 'g':
|
||||
while (count--)
|
||||
{
|
||||
GUID guid;
|
||||
if (!dbg_read_memory_verbose((void*)x.Offset, &guid, sizeof(guid))) break;
|
||||
dbg_printf("{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
|
||||
guid.Data1, guid.Data2, guid.Data3,
|
||||
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
|
||||
guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
|
||||
x.Offset += sizeof(guid);
|
||||
if (count)
|
||||
{
|
||||
print_address(&x, FALSE);
|
||||
dbg_printf(": ");
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
#define DO_DUMP2(_t,_l,_f,_vv) { \
|
||||
_t _v; \
|
||||
for(i=0; i<count; i++) { \
|
||||
if (!DEBUG_READ_MEM_VERBOSE(pnt, &_v, sizeof(_t))) break; \
|
||||
DEBUG_Printf(_f,(_vv)); \
|
||||
pnt += sizeof(_t); value.addr.off += sizeof(_t); \
|
||||
if ((i % (_l)) == (_l)-1) { \
|
||||
DEBUG_Printf("\n"); \
|
||||
DEBUG_PrintAddress( &value.addr, DEBUG_CurrThread->dbg_mode, FALSE );\
|
||||
DEBUG_Printf(": ");\
|
||||
} \
|
||||
} \
|
||||
DEBUG_Printf("\n"); \
|
||||
} \
|
||||
#define DO_DUMP2(_t,_l,_f,_vv) { \
|
||||
_t _v; \
|
||||
for (i = 0; i < count; i++) { \
|
||||
if (!dbg_read_memory_verbose((void*)x.Offset, &_v, \
|
||||
sizeof(_t))) break; \
|
||||
dbg_printf(_f, (_vv)); \
|
||||
x.Offset += sizeof(_t); \
|
||||
if ((i % (_l)) == (_l) - 1) { \
|
||||
dbg_printf("\n"); \
|
||||
print_address(&x, FALSE); \
|
||||
dbg_printf(": "); \
|
||||
} \
|
||||
} \
|
||||
dbg_printf("\n"); \
|
||||
} \
|
||||
return
|
||||
#define DO_DUMP(_t,_l,_f) DO_DUMP2(_t,_l,_f,_v)
|
||||
|
||||
case 'x': DO_DUMP(int, 4, " %8.8x");
|
||||
case 'd': DO_DUMP(unsigned int, 4, " %10d");
|
||||
case 'w': DO_DUMP(unsigned short, 8, " %04x");
|
||||
case 'c': DO_DUMP2(char, 32, " %c", (_v < 0x20) ? ' ' : _v);
|
||||
case 'b': DO_DUMP2(char, 16, " %02x", (_v) & 0xff);
|
||||
}
|
||||
case 'x': DO_DUMP(int, 4, " %8.8x");
|
||||
case 'd': DO_DUMP(unsigned int, 4, " %10d");
|
||||
case 'w': DO_DUMP(unsigned short, 8, " %04x");
|
||||
case 'c': DO_DUMP2(char, 32, " %c", (_v < 0x20) ? ' ' : _v);
|
||||
case 'b': DO_DUMP2(char, 16, " %02x", (_v) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
#define CHARBUFSIZE 16
|
||||
BOOL memory_get_string(HANDLE hp, void* addr, unsigned cookie, BOOL unicode,
|
||||
char* buffer, int size)
|
||||
{
|
||||
DWORD sz;
|
||||
WCHAR* buffW;
|
||||
|
||||
/******************************************************************
|
||||
* DEBUG_PrintStringA
|
||||
buffer[0] = 0;
|
||||
if (!addr) return FALSE;
|
||||
switch (cookie)
|
||||
{
|
||||
case DLV_TARGET:
|
||||
if (!unicode) return ReadProcessMemory(hp, addr, buffer, size, &sz);
|
||||
|
||||
buffW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
|
||||
ReadProcessMemory(hp, addr, buffW, size * sizeof(WCHAR), &sz);
|
||||
WideCharToMultiByte(CP_ACP, 0, buffW, sz / sizeof(WCHAR), buffer, size,
|
||||
NULL, NULL);
|
||||
HeapFree(GetProcessHeap(), 0, buffW);
|
||||
return TRUE;
|
||||
case DLV_HOST:
|
||||
strncpy(buffer, addr, size);
|
||||
buffer[size - 1] = 0;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL memory_get_string_indirect(HANDLE hp, void* addr, BOOL unicode, char* buffer, int size)
|
||||
{
|
||||
void* ad;
|
||||
DWORD sz;
|
||||
|
||||
buffer[0] = 0;
|
||||
if (addr &&
|
||||
ReadProcessMemory(hp, addr, &ad, sizeof(ad), &sz) && sz == sizeof(ad) && ad)
|
||||
{
|
||||
return memory_get_string(hp, ad, DLV_TARGET, unicode, buffer, size);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void print_typed_basic(const struct dbg_lvalue* lvalue)
|
||||
{
|
||||
long long int val_int;
|
||||
void* val_ptr;
|
||||
long double val_real;
|
||||
DWORD tag, size, count, bt, rtype;
|
||||
DWORD linear = (DWORD)memory_to_linear_addr(&lvalue->addr);
|
||||
|
||||
assert(lvalue->cookie == DLV_TARGET || lvalue->cookie == DLV_HOST);
|
||||
|
||||
if (lvalue->typeid == dbg_itype_none ||
|
||||
!types_get_info(linear, lvalue->typeid, TI_GET_SYMTAG, &tag))
|
||||
return;
|
||||
|
||||
switch (tag)
|
||||
{
|
||||
case SymTagBaseType:
|
||||
if (!types_get_info(linear, lvalue->typeid, TI_GET_LENGTH, &size) ||
|
||||
!types_get_info(linear, lvalue->typeid, TI_GET_BASETYPE, &bt))
|
||||
{
|
||||
WINE_ERR("Couldn't get information\n");
|
||||
RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!types_get_info(linear, lvalue->typeid, TI_GET_TYPE, &rtype) ||
|
||||
rtype == dbg_itype_none)
|
||||
{
|
||||
dbg_printf("Internal symbol error: unable to access memory location %p", val_ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
if (types_get_info(linear, rtype, TI_GET_SYMTAG, &tag) && tag == SymTagBaseType &&
|
||||
types_get_info(linear, rtype, TI_GET_BASETYPE, &bt) && bt == btChar &&
|
||||
types_get_info(linear, rtype, TI_GET_LENGTH, &size))
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
memory_get_string(dbg_curr_thread->handle, (void*)val_ptr, lvalue->cookie,
|
||||
size == 2, buffer, sizeof(buffer));
|
||||
dbg_printf("\"%s\"", buffer);
|
||||
}
|
||||
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(linear, lvalue->typeid, 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(linear, lvalue->typeid, TI_FINDCHILDREN, fcp))
|
||||
{
|
||||
for (i = 0; i < min(fcp->Count, count); i++)
|
||||
{
|
||||
if (!types_get_info(linear, fcp->ChildId[i], 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(linear, fcp->ChildId[i], 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;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* print_basic
|
||||
*
|
||||
* Prints on channel chnl, the string starting at address in target
|
||||
* address space. The string stops when either len chars (if <> -1)
|
||||
* have been printed, or the '\0' char is printed
|
||||
* Implementation of the 'print' command.
|
||||
*/
|
||||
int DEBUG_PrintStringA(const DBG_ADDR* address, int len)
|
||||
void print_basic(const struct dbg_lvalue* lvalue, int count, char format)
|
||||
{
|
||||
char* lin = (void*)DEBUG_ToLinear(address);
|
||||
char ch[CHARBUFSIZE+1];
|
||||
int written = 0;
|
||||
long int res;
|
||||
|
||||
if (len == -1) len = 32767; /* should be big enough */
|
||||
|
||||
while (written < len)
|
||||
assert(lvalue->cookie == DLV_TARGET || lvalue->cookie == DLV_HOST);
|
||||
if (lvalue->typeid == dbg_itype_none)
|
||||
{
|
||||
int to_write = min(CHARBUFSIZE, len - written );
|
||||
if (!DEBUG_READ_MEM_VERBOSE(lin, ch, to_write)) break;
|
||||
ch[to_write] = '\0'; /* protect from displaying junk */
|
||||
to_write = lstrlenA(ch);
|
||||
DEBUG_OutputA(ch, to_write);
|
||||
lin += to_write;
|
||||
written += to_write;
|
||||
if (to_write < CHARBUFSIZE) break;
|
||||
dbg_printf("Unable to evaluate expression\n");
|
||||
return;
|
||||
}
|
||||
|
||||
res = types_extract_as_integer(lvalue);
|
||||
|
||||
/* FIXME: this implies i386 byte ordering */
|
||||
switch (format)
|
||||
{
|
||||
case 'x':
|
||||
if (lvalue->addr.Mode != AddrModeFlat)
|
||||
dbg_printf("0x%04lx", res);
|
||||
else
|
||||
dbg_printf("0x%08lx", res);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
dbg_printf("%ld\n", res);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
dbg_printf("%d = '%c'", (char)(res & 0xff), (char)(res & 0xff));
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
{
|
||||
WCHAR wch = (WCHAR)(res & 0xFFFF);
|
||||
dbg_printf("%d = '", wch);
|
||||
dbg_outputW(&wch, 1);
|
||||
dbg_printf("'");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
case 's':
|
||||
case 'w':
|
||||
case 'b':
|
||||
dbg_printf("Format specifier '%c' is meaningless in 'print' command\n", format);
|
||||
case 0:
|
||||
print_typed_basic(lvalue);
|
||||
break;
|
||||
}
|
||||
return written; /* number of actually written chars */
|
||||
}
|
||||
|
||||
int DEBUG_PrintStringW(const DBG_ADDR* address, int len)
|
||||
void print_bare_address(const ADDRESS* addr)
|
||||
{
|
||||
WCHAR* lin = (void*)DEBUG_ToLinear(address);
|
||||
WCHAR ch[CHARBUFSIZE+1];
|
||||
int written = 0;
|
||||
|
||||
if (len == -1) len = 32767; /* should be big enough */
|
||||
|
||||
while (written < len)
|
||||
switch (addr->Mode)
|
||||
{
|
||||
int to_write = min(CHARBUFSIZE, len - written );
|
||||
if (!DEBUG_READ_MEM_VERBOSE(lin, ch, to_write * sizeof(WCHAR))) break;
|
||||
ch[to_write] = 0; /* protect from displaying junk */
|
||||
to_write = lstrlenW(ch);
|
||||
DEBUG_OutputW(ch, to_write);
|
||||
lin += to_write;
|
||||
written += to_write;
|
||||
if (to_write < CHARBUFSIZE) break;
|
||||
case AddrModeFlat:
|
||||
dbg_printf("0x%08lx", addr->Offset);
|
||||
break;
|
||||
case AddrModeReal:
|
||||
case AddrMode1616:
|
||||
dbg_printf("0x%04x:0x%04lx", addr->Segment, addr->Offset);
|
||||
break;
|
||||
case AddrMode1632:
|
||||
dbg_printf("0x%04x:0x%08lx", addr->Segment, addr->Offset);
|
||||
break;
|
||||
}
|
||||
return written; /* number of actually written chars */
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* print_address
|
||||
*
|
||||
* Print an 16- or 32-bit address, with the nearest symbol if any.
|
||||
*/
|
||||
void print_address(const ADDRESS* addr, BOOLEAN with_line)
|
||||
{
|
||||
char buffer[sizeof(SYMBOL_INFO) + 256];
|
||||
SYMBOL_INFO* si = (SYMBOL_INFO*)buffer;
|
||||
void* lin = memory_to_linear_addr(addr);
|
||||
|
||||
print_bare_address(addr);
|
||||
|
||||
si->SizeOfStruct = sizeof(*si);
|
||||
si->MaxNameLen = 256;
|
||||
if (!SymFromAddr(dbg_curr_process->handle, (unsigned long)lin, NULL, si)) return;
|
||||
dbg_printf(" %s", si->Name);
|
||||
if (with_line)
|
||||
{
|
||||
IMAGEHLP_LINE il;
|
||||
IMAGEHLP_MODULE im;
|
||||
|
||||
il.SizeOfStruct = sizeof(il);
|
||||
if (SymGetLineFromAddr(dbg_curr_process->handle, (unsigned long)lin, NULL, &il))
|
||||
dbg_printf(" [%s:%lu]", il.FileName, il.LineNumber);
|
||||
im.SizeOfStruct = sizeof(im);
|
||||
if (SymGetModuleInfo(dbg_curr_process->handle, (unsigned long)lin, &im))
|
||||
dbg_printf(" in %s", im.ModuleName);
|
||||
}
|
||||
}
|
||||
|
||||
struct foo
|
||||
{
|
||||
char* tmp;
|
||||
DWORD frame;
|
||||
};
|
||||
|
||||
static BOOL WINAPI sym_enum_cb(SYMBOL_INFO* sym_info, ULONG size, void* user)
|
||||
{
|
||||
struct foo* foo = (struct foo*)user;
|
||||
DWORD addr;
|
||||
unsigned val;
|
||||
long offset;
|
||||
|
||||
if ((sym_info->Flags & (SYMFLAG_PARAMETER|SYMFLAG_FRAMEREL)) == (SYMFLAG_PARAMETER|SYMFLAG_FRAMEREL))
|
||||
{
|
||||
if (foo->tmp[0]) strcat(foo->tmp, ", ");
|
||||
addr = foo->frame;
|
||||
types_get_info(sym_info->ModBase, sym_info->TypeIndex, TI_GET_OFFSET, &offset);
|
||||
addr += offset;
|
||||
dbg_read_memory_verbose((char*)addr, &val, sizeof(val));
|
||||
sprintf(foo->tmp + strlen(foo->tmp), "%s=0x%x", sym_info->Name, val);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void print_addr_and_args(const ADDRESS* pc, const ADDRESS* frame)
|
||||
{
|
||||
char buffer[sizeof(SYMBOL_INFO) + 256];
|
||||
SYMBOL_INFO* si = (SYMBOL_INFO*)buffer;
|
||||
IMAGEHLP_STACK_FRAME isf;
|
||||
IMAGEHLP_LINE il;
|
||||
IMAGEHLP_MODULE im;
|
||||
struct foo foo;
|
||||
char tmp[1024];
|
||||
DWORD disp;
|
||||
|
||||
if (pc->Mode != AddrModeFlat)
|
||||
dbg_printf("0x%04x:0x%04lx", pc->Segment, pc->Offset);
|
||||
else
|
||||
dbg_printf("0x%08lx", pc->Offset);
|
||||
|
||||
isf.InstructionOffset = (unsigned long)memory_to_linear_addr(pc);
|
||||
isf.FrameOffset = (unsigned long)memory_to_linear_addr(frame);
|
||||
|
||||
si->SizeOfStruct = sizeof(*si);
|
||||
si->MaxNameLen = 256;
|
||||
if (!SymFromAddr(dbg_curr_process->handle, isf.InstructionOffset, &disp, si))
|
||||
return;
|
||||
|
||||
dbg_printf(" %s", si->Name);
|
||||
if (disp) dbg_printf("+0x%lx", disp);
|
||||
|
||||
SymSetContext(dbg_curr_process->handle, &isf, NULL);
|
||||
foo.tmp = tmp;
|
||||
foo.frame = isf.FrameOffset;
|
||||
tmp[0] = '\0';
|
||||
SymEnumSymbols(dbg_curr_process->handle, 0, NULL, sym_enum_cb, &foo);
|
||||
if (tmp[0]) dbg_printf("(%s)", tmp);
|
||||
|
||||
il.SizeOfStruct = sizeof(il);
|
||||
if (SymGetLineFromAddr(dbg_curr_process->handle, isf.InstructionOffset,
|
||||
NULL, &il))
|
||||
dbg_printf(" [%s:%lu]", il.FileName, il.LineNumber);
|
||||
im.SizeOfStruct = sizeof(im);
|
||||
if (SymGetModuleInfo(dbg_curr_process->handle, isf.InstructionOffset, &im))
|
||||
dbg_printf(" in %s", im.ModuleName);
|
||||
}
|
||||
|
||||
BOOL memory_disasm_one_insn(ADDRESS* addr)
|
||||
{
|
||||
char ch;
|
||||
|
||||
print_address(addr, TRUE);
|
||||
dbg_printf(": ");
|
||||
if (!dbg_read_memory(memory_to_linear_addr(addr), &ch, sizeof(ch)))
|
||||
{
|
||||
dbg_printf("-- no code accessible --\n");
|
||||
return FALSE;
|
||||
}
|
||||
be_cpu->disasm_one_insn(addr, TRUE);
|
||||
dbg_printf("\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void memory_disassemble(const struct dbg_lvalue* xstart,
|
||||
const struct dbg_lvalue* xend, int offset)
|
||||
{
|
||||
static ADDRESS last = {0,0,0};
|
||||
|
||||
if (!xstart && !xend)
|
||||
{
|
||||
if (!last.Segment && !last.Offset) memory_get_current_pc(&last);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (xstart)
|
||||
{
|
||||
last.Mode = AddrModeFlat;
|
||||
last.Offset = types_extract_as_integer(xstart);
|
||||
}
|
||||
if (xend) offset = types_extract_as_integer(xend) - last.Offset + 1;
|
||||
}
|
||||
while (offset-- > 0 && memory_disasm_one_insn(&last));
|
||||
}
|
||||
|
|
|
@ -1,479 +0,0 @@
|
|||
/*
|
||||
* File module.c - module handling for the wine debugger
|
||||
*
|
||||
* Copyright (C) 1993, Eric Youngdale.
|
||||
* 2000, 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
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "debugger.h"
|
||||
#include "wingdi.h"
|
||||
#include "winuser.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
|
||||
|
||||
/***********************************************************************
|
||||
* Creates and links a new module to the current process
|
||||
*
|
||||
*/
|
||||
DBG_MODULE* DEBUG_AddModule(const char* name, enum DbgModuleType type,
|
||||
void* mod_addr, unsigned long size, HMODULE hmodule)
|
||||
{
|
||||
DBG_MODULE* wmod;
|
||||
|
||||
if (!(wmod = (DBG_MODULE*)DBG_alloc(sizeof(*wmod))))
|
||||
return NULL;
|
||||
|
||||
memset(wmod, 0, sizeof(*wmod));
|
||||
|
||||
wmod->dil = DIL_DEFERRED;
|
||||
wmod->main = (DEBUG_CurrProcess->num_modules == 0);
|
||||
wmod->type = type;
|
||||
wmod->load_addr = mod_addr;
|
||||
wmod->size = size;
|
||||
wmod->handle = hmodule;
|
||||
wmod->dbg_index = DEBUG_CurrProcess->next_index;
|
||||
wmod->module_name = DBG_strdup(name);
|
||||
DEBUG_CurrProcess->next_index++;
|
||||
|
||||
DEBUG_CurrProcess->modules = DBG_realloc(DEBUG_CurrProcess->modules,
|
||||
++DEBUG_CurrProcess->num_modules * sizeof(DBG_MODULE*));
|
||||
DEBUG_CurrProcess->modules[DEBUG_CurrProcess->num_modules - 1] = wmod;
|
||||
|
||||
return wmod;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_FindModuleByName
|
||||
*
|
||||
*/
|
||||
DBG_MODULE* DEBUG_FindModuleByName(const char* name, enum DbgModuleType type)
|
||||
{
|
||||
int i;
|
||||
DBG_MODULE** amod = DEBUG_CurrProcess->modules;
|
||||
|
||||
for (i = 0; i < DEBUG_CurrProcess->num_modules; i++) {
|
||||
if ((type == DMT_UNKNOWN || type == amod[i]->type) &&
|
||||
!strcasecmp(name, amod[i]->module_name))
|
||||
return amod[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_FindModuleByAddr
|
||||
*
|
||||
* either the addr where module is loaded, or any address inside the
|
||||
* module
|
||||
*/
|
||||
DBG_MODULE* DEBUG_FindModuleByAddr(void* addr, enum DbgModuleType type)
|
||||
{
|
||||
int i;
|
||||
DBG_MODULE** amod = DEBUG_CurrProcess->modules;
|
||||
DBG_MODULE* res = NULL;
|
||||
|
||||
for (i = 0; i < DEBUG_CurrProcess->num_modules; i++) {
|
||||
if ((type == DMT_UNKNOWN || type == amod[i]->type) &&
|
||||
(char *)addr >= (char *)amod[i]->load_addr &&
|
||||
(char *)addr < (char *)amod[i]->load_addr + amod[i]->size) {
|
||||
/* amod[i] contains it... check against res now */
|
||||
if (!res || res->load_addr < amod[i]->load_addr)
|
||||
res = amod[i];
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_FindModuleByHandle
|
||||
*/
|
||||
DBG_MODULE* DEBUG_FindModuleByHandle(HANDLE handle, enum DbgModuleType type)
|
||||
{
|
||||
int i;
|
||||
DBG_MODULE** amod = DEBUG_CurrProcess->modules;
|
||||
|
||||
for (i = 0; i < DEBUG_CurrProcess->num_modules; i++) {
|
||||
if ((type == DMT_UNKNOWN || type == amod[i]->type) &&
|
||||
handle == amod[i]->handle)
|
||||
return amod[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_GetProcessMainModule
|
||||
*/
|
||||
DBG_MODULE* DEBUG_GetProcessMainModule(DBG_PROCESS* process)
|
||||
{
|
||||
if (!process || !process->num_modules) return NULL;
|
||||
|
||||
/* main module is the first to be loaded on a given process, so it's the first
|
||||
* in the array */
|
||||
assert(process->modules[0]->main);
|
||||
return process->modules[0];
|
||||
}
|
||||
|
||||
#if 0
|
||||
/***********************************************************************
|
||||
* DEBUG_RegisterNEModule
|
||||
*
|
||||
*/
|
||||
static DBG_MODULE* DEBUG_RegisterNEModule(HMODULE hModule, void* load_addr,
|
||||
unsigned long size, const char *module_name)
|
||||
{
|
||||
DBG_MODULE* wmod = DEBUG_AddModule(module_name, DMT_NE, load_addr, size, hModule);
|
||||
|
||||
if (!wmod) return NULL;
|
||||
|
||||
DEBUG_CurrProcess->next_index++;
|
||||
return wmod;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_GetEP16
|
||||
*
|
||||
* Helper function fo DEBUG_LoadModuleEPs16:
|
||||
* finds the address of a given entry point from a given module
|
||||
*/
|
||||
static BOOL DEBUG_GetEP16(char* moduleAddr, const NE_MODULE* module,
|
||||
WORD ordinal, DBG_ADDR* addr)
|
||||
{
|
||||
void* idx;
|
||||
ET_ENTRY entry;
|
||||
ET_BUNDLE bundle;
|
||||
SEGTABLEENTRY ste;
|
||||
|
||||
bundle.next = module->entry_table;
|
||||
do {
|
||||
if (!bundle.next)
|
||||
return FALSE;
|
||||
idx = moduleAddr + bundle.next;
|
||||
if (!DEBUG_READ_MEM_VERBOSE(idx, &bundle, sizeof(bundle)))
|
||||
return FALSE;
|
||||
} while ((ordinal < bundle.first + 1) || (ordinal > bundle.last));
|
||||
|
||||
if (!DEBUG_READ_MEM_VERBOSE((char*)idx + sizeof(ET_BUNDLE) +
|
||||
(ordinal - bundle.first - 1) * sizeof(ET_ENTRY),
|
||||
&entry, sizeof(ET_ENTRY)))
|
||||
return FALSE;
|
||||
|
||||
addr->seg = entry.segnum;
|
||||
addr->off = entry.offs;
|
||||
|
||||
if (addr->seg == 0xfe) addr->seg = 0xffff; /* constant entry */
|
||||
else {
|
||||
if (!DEBUG_READ_MEM_VERBOSE(moduleAddr + module->seg_table +
|
||||
sizeof(ste) * (addr->seg - 1),
|
||||
&ste, sizeof(ste)))
|
||||
return FALSE;
|
||||
addr->seg = GlobalHandleToSel16(ste.hSeg);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_LoadModule16
|
||||
*
|
||||
* Load the entry points of a Win16 module into the hash table.
|
||||
*/
|
||||
static void DEBUG_LoadModule16(HMODULE hModule, NE_MODULE* module, char* moduleAddr, const char* name)
|
||||
{
|
||||
DBG_VALUE value;
|
||||
BYTE buf[1 + 256 + 2];
|
||||
char epname[512];
|
||||
char* cpnt;
|
||||
DBG_MODULE* wmod;
|
||||
|
||||
wmod = DEBUG_RegisterNEModule(hModule, moduleAddr, name);
|
||||
|
||||
value.type = NULL;
|
||||
value.cookie = DV_TARGET;
|
||||
value.addr.seg = 0;
|
||||
value.addr.off = 0;
|
||||
|
||||
cpnt = moduleAddr + module->name_table;
|
||||
|
||||
/* First search the resident names */
|
||||
|
||||
/* skip module name */
|
||||
if (!DEBUG_READ_MEM_VERBOSE(cpnt, buf, sizeof(buf)) || !buf[0])
|
||||
return;
|
||||
cpnt += 1 + buf[0] + sizeof(WORD);
|
||||
|
||||
while (DEBUG_READ_MEM_VERBOSE(cpnt, buf, sizeof(buf)) && buf[0]) {
|
||||
snprintf(epname, sizeof(epname), "%s.%.*s", name, buf[0], &buf[1]);
|
||||
if (DEBUG_GetEP16(moduleAddr, module, *(WORD*)&buf[1 + buf[0]], &value.addr)) {
|
||||
DEBUG_AddSymbol(epname, &value, NULL, SYM_WIN32 | SYM_FUNC);
|
||||
}
|
||||
cpnt += buf[0] + 1 + sizeof(WORD);
|
||||
}
|
||||
|
||||
/* Now search the non-resident names table */
|
||||
if (!module->nrname_handle) return; /* No non-resident table */
|
||||
cpnt = (char *)GlobalLock16(module->nrname_handle);
|
||||
while (DEBUG_READ_MEM_VERBOSE(cpnt, buf, sizeof(buf)) && buf[0]) {
|
||||
snprintf(epname, sizeof(epname), "%s.%.*s", name, buf[0], &buf[1]);
|
||||
if (DEBUG_GetEP16(moduleAddr, module, *(WORD*)&buf[1 + buf[0]], &value.addr)) {
|
||||
DEBUG_AddSymbol(epname, &value, NULL, SYM_WIN32 | SYM_FUNC);
|
||||
}
|
||||
cpnt += buf[0] + 1 + sizeof(WORD);
|
||||
}
|
||||
GlobalUnlock16(module->nrname_handle);
|
||||
}
|
||||
#endif
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_LoadEntryPoints
|
||||
*
|
||||
* Load the entry points of all the modules into the hash table.
|
||||
*/
|
||||
int DEBUG_LoadEntryPoints(const char* pfx)
|
||||
{
|
||||
int first = 0;
|
||||
/* FIXME: with address space separation in space, this is plain wrong
|
||||
* it requires the 16 bit WOW debugging interface...
|
||||
*/
|
||||
#if 0
|
||||
MODULEENTRY entry;
|
||||
NE_MODULE module;
|
||||
void* moduleAddr;
|
||||
int rowcount = 0;
|
||||
int len;
|
||||
|
||||
/* FIXME: we assume that a module is never removed from memory */
|
||||
/* FIXME: this is (currently plain wrong when debugger is started by
|
||||
* attaching to an existing program => the 16 bit modules will
|
||||
* not be shared... not much to do on debugger side... sigh
|
||||
*/
|
||||
if (ModuleFirst16(&entry)) do {
|
||||
if (DEBUG_FindModuleByName(entry.szModule, DM_TYPE_UNKNOWN) ||
|
||||
!(moduleAddr = NE_GetPtr(entry.hModule)) ||
|
||||
!DEBUG_READ_MEM_VERBOSE(moduleAddr, &module, sizeof(module)) ||
|
||||
(module.flags & NE_FFLAGS_WIN32) /* NE module */)
|
||||
continue;
|
||||
if (!first) {
|
||||
if (pfx) DEBUG_Printf(pfx);
|
||||
DEBUG_Printf(" ");
|
||||
rowcount = 3 + (pfx ? strlen(pfx) : 0);
|
||||
first = 1;
|
||||
}
|
||||
|
||||
len = strlen(entry.szModule);
|
||||
if ((rowcount + len) > 76) {
|
||||
DEBUG_Printf("\n ");
|
||||
rowcount = 3;
|
||||
}
|
||||
DEBUG_Printf(" %s", entry.szModule);
|
||||
rowcount += len + 1;
|
||||
|
||||
DEBUG_LoadModule16(entry.hModule, &module, moduleAddr, entry.szModule);
|
||||
} while (ModuleNext16(&entry));
|
||||
#endif
|
||||
|
||||
if (first) DEBUG_Printf("\n");
|
||||
return first;
|
||||
}
|
||||
|
||||
void DEBUG_ReportDIL(enum DbgInfoLoad dil, const char* pfx, const char* filename, void *load_addr)
|
||||
{
|
||||
const char* fmt;
|
||||
|
||||
switch (dil) {
|
||||
case DIL_DEFERRED:
|
||||
fmt = "Deferring debug information loading for %s '%s' (%p)\n";
|
||||
break;
|
||||
case DIL_LOADED:
|
||||
fmt = "Loaded debug information from %s '%s' (%p)\n";
|
||||
break;
|
||||
case DIL_NOINFO:
|
||||
fmt = "No debug information in %s '%s' (%p)\n";
|
||||
break;
|
||||
case DIL_NOT_SUPPORTED:
|
||||
fmt = "Unsupported debug information in %s '%s' (%p)\n";
|
||||
break;
|
||||
case DIL_ERROR:
|
||||
fmt = "Can't find file for %s '%s' (%p)\n";
|
||||
break;
|
||||
default:
|
||||
WINE_ERR("Oooocch (%d)\n", dil);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_Printf(fmt, pfx, filename, load_addr);
|
||||
}
|
||||
|
||||
static const char* DEBUG_GetModuleType(enum DbgModuleType type)
|
||||
{
|
||||
switch (type) {
|
||||
case DMT_NE: return "NE";
|
||||
case DMT_PE: return "PE";
|
||||
case DMT_ELF: return "ELF";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
static const char* DEBUG_GetDbgInfo(enum DbgInfoLoad dil)
|
||||
{
|
||||
switch (dil) {
|
||||
case DIL_LOADED: return "loaded";
|
||||
case DIL_DEFERRED: return "deferred";
|
||||
case DIL_NOINFO: return "none";
|
||||
case DIL_NOT_SUPPORTED: return "not supported";
|
||||
case DIL_ERROR: return "error";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_ModuleCompare
|
||||
*
|
||||
* returns -1 is p1 < p2, 0 is p1 == p2, +1 if p1 > p2
|
||||
* order used is order on load_addr of a module
|
||||
*/
|
||||
static int DEBUG_ModuleCompare(const void* p1, const void* p2)
|
||||
{
|
||||
return (char*)(*((const DBG_MODULE**)p1))->load_addr -
|
||||
(char*)(*((const DBG_MODULE**)p2))->load_addr;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_IsContainer
|
||||
*
|
||||
* returns TRUE is wmod_child is contained (inside bounds) of wmod_cntnr
|
||||
*/
|
||||
static inline BOOL DEBUG_IsContainer(const DBG_MODULE* wmod_cntnr,
|
||||
const DBG_MODULE* wmod_child)
|
||||
{
|
||||
return wmod_cntnr->load_addr <= wmod_child->load_addr &&
|
||||
(DWORD)wmod_cntnr->load_addr + wmod_cntnr->size >=
|
||||
(DWORD)wmod_child->load_addr + wmod_child->size;
|
||||
}
|
||||
|
||||
static void DEBUG_InfoShareModule(const DBG_MODULE* module, int ident)
|
||||
{
|
||||
if (ident) DEBUG_Printf(" \\-");
|
||||
DEBUG_Printf("%s\t0x%08lx-%08lx\t%s\n",
|
||||
DEBUG_GetModuleType(module->type),
|
||||
(DWORD)module->load_addr, (DWORD)module->load_addr + module->size,
|
||||
module->module_name);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_InfoShare
|
||||
*
|
||||
* Display shared libarary information.
|
||||
*/
|
||||
void DEBUG_InfoShare(void)
|
||||
{
|
||||
DBG_MODULE** ref;
|
||||
int i, j;
|
||||
|
||||
ref = DBG_alloc(sizeof(DBG_MODULE*) * DEBUG_CurrProcess->num_modules);
|
||||
if (!ref) return;
|
||||
|
||||
DEBUG_Printf("Module\tAddress\t\t\tName\t%d modules\n",
|
||||
DEBUG_CurrProcess->num_modules);
|
||||
|
||||
memcpy(ref, DEBUG_CurrProcess->modules,
|
||||
sizeof(DBG_MODULE*) * DEBUG_CurrProcess->num_modules);
|
||||
qsort(ref, DEBUG_CurrProcess->num_modules, sizeof(DBG_MODULE*),
|
||||
DEBUG_ModuleCompare);
|
||||
for (i = 0; i < DEBUG_CurrProcess->num_modules; i++) {
|
||||
switch (ref[i]->type) {
|
||||
case DMT_ELF:
|
||||
DEBUG_InfoShareModule(ref[i], 0);
|
||||
for (j = 0; j < DEBUG_CurrProcess->num_modules; j++) {
|
||||
if (ref[j]->type != DMT_ELF && DEBUG_IsContainer(ref[i], ref[j]))
|
||||
DEBUG_InfoShareModule(ref[j], 1);
|
||||
}
|
||||
break;
|
||||
case DMT_NE:
|
||||
case DMT_PE:
|
||||
/* check module is not in ELF */
|
||||
for (j = 0; j < DEBUG_CurrProcess->num_modules; j++) {
|
||||
if (ref[j]->type == DMT_ELF &&
|
||||
DEBUG_IsContainer(ref[j], ref[i]))
|
||||
break;
|
||||
}
|
||||
if (j >= DEBUG_CurrProcess->num_modules)
|
||||
DEBUG_InfoShareModule(ref[i], 0);
|
||||
break;
|
||||
default:
|
||||
WINE_ERR("Unknown type (%d)\n", ref[i]->type);
|
||||
}
|
||||
}
|
||||
DBG_free(ref);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_DumpModule
|
||||
* Display information about a given module (DLL or EXE)
|
||||
*/
|
||||
void DEBUG_DumpModule(DWORD mod)
|
||||
{
|
||||
DBG_MODULE* wmod;
|
||||
|
||||
if (!(wmod = DEBUG_FindModuleByHandle((HANDLE)mod, DMT_UNKNOWN)) &&
|
||||
!(wmod = DEBUG_FindModuleByAddr((void*)mod, DMT_UNKNOWN))) {
|
||||
DEBUG_Printf("'0x%08lx' is not a valid module handle or address\n", mod);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_Printf("Module '%s' (handle=%p) 0x%08lx-0x%08lx (%s, debug info %s)\n",
|
||||
wmod->module_name, wmod->handle, (DWORD)wmod->load_addr,
|
||||
(DWORD)wmod->load_addr + wmod->size,
|
||||
DEBUG_GetModuleType(wmod->type), DEBUG_GetDbgInfo(wmod->dil));
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_WalkModules
|
||||
*
|
||||
* Display information about all modules (DLLs and EXEs)
|
||||
*/
|
||||
void DEBUG_WalkModules(void)
|
||||
{
|
||||
DBG_MODULE** amod;
|
||||
int i;
|
||||
|
||||
if (!DEBUG_CurrProcess)
|
||||
{
|
||||
DEBUG_Printf("Cannot walk classes while no process is loaded\n");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_Printf("Address\t\t\tModule\tName\n");
|
||||
|
||||
amod = DBG_alloc(sizeof(DBG_MODULE*) * DEBUG_CurrProcess->num_modules);
|
||||
if (!amod) return;
|
||||
|
||||
memcpy(amod, DEBUG_CurrProcess->modules,
|
||||
sizeof(DBG_MODULE*) * DEBUG_CurrProcess->num_modules);
|
||||
qsort(amod, DEBUG_CurrProcess->num_modules, sizeof(DBG_MODULE*),
|
||||
DEBUG_ModuleCompare);
|
||||
for (i = 0; i < DEBUG_CurrProcess->num_modules; i++) {
|
||||
if (amod[i]->type == DMT_ELF) continue;
|
||||
|
||||
DEBUG_Printf("0x%08lx-%08lx\t(%s)\t%s\n",
|
||||
(DWORD)amod[i]->load_addr,
|
||||
(DWORD)amod[i]->load_addr + amod[i]->size,
|
||||
DEBUG_GetModuleType(amod[i]->type), amod[i]->module_name);
|
||||
}
|
||||
DBG_free(amod);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,513 +0,0 @@
|
|||
/*
|
||||
* File pe.c - handle PE module information
|
||||
*
|
||||
* Copyright (C) 1996, Eric Youngdale.
|
||||
* Copyright (C) 1999, 2000, Ulrich Weigand.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "wine/port.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX MAX_PATH
|
||||
#endif
|
||||
#include "wine/exception.h"
|
||||
#include "wine/debug.h"
|
||||
#include "excpt.h"
|
||||
#include "debugger.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
|
||||
|
||||
#define MAX_PATHNAME_LEN 1024
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD from;
|
||||
DWORD to;
|
||||
|
||||
} OMAP_DATA;
|
||||
|
||||
typedef struct tagMSC_DBG_INFO
|
||||
{
|
||||
int nsect;
|
||||
PIMAGE_SECTION_HEADER sectp;
|
||||
int nomap;
|
||||
OMAP_DATA* omapp;
|
||||
|
||||
} MSC_DBG_INFO;
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_LocateDebugInfoFile
|
||||
*
|
||||
* NOTE: dbg_filename must be at least MAX_PATHNAME_LEN bytes in size
|
||||
*/
|
||||
static void DEBUG_LocateDebugInfoFile(const char* filename, char* dbg_filename)
|
||||
{
|
||||
char* str1 = DBG_alloc(MAX_PATHNAME_LEN);
|
||||
char* str2 = DBG_alloc(MAX_PATHNAME_LEN*10);
|
||||
const char* file;
|
||||
char* name_part;
|
||||
|
||||
file = strrchr(filename, '\\');
|
||||
if (file == NULL) file = filename; else file++;
|
||||
|
||||
if ((GetEnvironmentVariable("_NT_SYMBOL_PATH", str1, MAX_PATHNAME_LEN) &&
|
||||
(SearchPath(str1, file, NULL, MAX_PATHNAME_LEN*10, str2, &name_part))) ||
|
||||
(GetEnvironmentVariable("_NT_ALT_SYMBOL_PATH", str1, MAX_PATHNAME_LEN) &&
|
||||
(SearchPath(str1, file, NULL, MAX_PATHNAME_LEN*10, str2, &name_part))) ||
|
||||
(SearchPath(NULL, file, NULL, MAX_PATHNAME_LEN*10, str2, &name_part)))
|
||||
lstrcpyn(dbg_filename, str2, MAX_PATHNAME_LEN);
|
||||
else
|
||||
lstrcpyn(dbg_filename, filename, MAX_PATHNAME_LEN);
|
||||
DBG_free(str1);
|
||||
DBG_free(str2);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_MapDebugInfoFile
|
||||
*/
|
||||
void* DEBUG_MapDebugInfoFile(const char* name, DWORD offset, DWORD size,
|
||||
HANDLE* hFile, HANDLE* hMap)
|
||||
{
|
||||
DWORD g_offset; /* offset aligned on map granuality */
|
||||
DWORD g_size; /* size to map, with offset aligned */
|
||||
char* ret;
|
||||
|
||||
*hMap = 0;
|
||||
|
||||
if (name != NULL)
|
||||
{
|
||||
char filename[MAX_PATHNAME_LEN];
|
||||
|
||||
DEBUG_LocateDebugInfoFile(name, filename);
|
||||
if ((*hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!size)
|
||||
{
|
||||
DWORD file_size = GetFileSize(*hFile, NULL);
|
||||
if (file_size == (DWORD)-1) return NULL;
|
||||
size = file_size - offset;
|
||||
}
|
||||
|
||||
g_offset = offset & ~0xFFFF; /* FIXME: is granularity portable ? */
|
||||
g_size = offset + size - g_offset;
|
||||
|
||||
if ((*hMap = CreateFileMapping(*hFile, NULL, PAGE_READONLY, 0, 0, NULL)) == 0)
|
||||
return NULL;
|
||||
|
||||
if ((ret = MapViewOfFile(*hMap, FILE_MAP_READ, 0, g_offset, g_size)) != NULL)
|
||||
ret += offset - g_offset;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_UnmapDebugInfoFile
|
||||
*/
|
||||
void DEBUG_UnmapDebugInfoFile(HANDLE hFile, HANDLE hMap, const void* addr)
|
||||
{
|
||||
if (addr) UnmapViewOfFile((void*)addr);
|
||||
if (hMap) CloseHandle(hMap);
|
||||
if (hFile!=INVALID_HANDLE_VALUE) CloseHandle(hFile);
|
||||
}
|
||||
|
||||
/*========================================================================
|
||||
* Process DBG file.
|
||||
*/
|
||||
static enum DbgInfoLoad DEBUG_ProcessDBGFile(DBG_MODULE* module,
|
||||
const char* filename, DWORD timestamp)
|
||||
{
|
||||
enum DbgInfoLoad dil = DIL_ERROR;
|
||||
HANDLE hFile = INVALID_HANDLE_VALUE, hMap = 0;
|
||||
const BYTE* file_map = NULL;
|
||||
PIMAGE_SEPARATE_DEBUG_HEADER hdr;
|
||||
PIMAGE_DEBUG_DIRECTORY dbg;
|
||||
int nDbg;
|
||||
|
||||
WINE_TRACE("Processing DBG file %s\n", filename);
|
||||
|
||||
file_map = DEBUG_MapDebugInfoFile(filename, 0, 0, &hFile, &hMap);
|
||||
if (!file_map)
|
||||
{
|
||||
WINE_ERR("-Unable to peruse .DBG file %s\n", filename);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
hdr = (PIMAGE_SEPARATE_DEBUG_HEADER) file_map;
|
||||
|
||||
if (hdr->TimeDateStamp != timestamp)
|
||||
{
|
||||
WINE_ERR("Warning - %s has incorrect internal timestamp\n", filename);
|
||||
/*
|
||||
* Well, sometimes this happens to DBG files which ARE REALLY the right .DBG
|
||||
* files but nonetheless this check fails. Anyway, WINDBG (debugger for
|
||||
* Windows by Microsoft) loads debug symbols which have incorrect timestamps.
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
dbg = (PIMAGE_DEBUG_DIRECTORY)
|
||||
(file_map + sizeof(*hdr) + hdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)
|
||||
+ hdr->ExportedNamesSize);
|
||||
|
||||
nDbg = hdr->DebugDirectorySize / sizeof(*dbg);
|
||||
|
||||
dil = DEBUG_ProcessDebugDirectory(module, file_map, dbg, nDbg);
|
||||
|
||||
leave:
|
||||
DEBUG_UnmapDebugInfoFile(hFile, hMap, file_map);
|
||||
return dil;
|
||||
}
|
||||
|
||||
|
||||
/*========================================================================
|
||||
* Process MSC debug information in PE file.
|
||||
*/
|
||||
enum DbgInfoLoad DEBUG_RegisterMSCDebugInfo(DBG_MODULE* module, HANDLE hFile,
|
||||
void* _nth, unsigned long nth_ofs)
|
||||
{
|
||||
enum DbgInfoLoad dil = DIL_ERROR;
|
||||
PIMAGE_NT_HEADERS nth = (PIMAGE_NT_HEADERS)_nth;
|
||||
PIMAGE_DATA_DIRECTORY dir = nth->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_DEBUG;
|
||||
PIMAGE_DEBUG_DIRECTORY dbg = NULL;
|
||||
int nDbg;
|
||||
MSC_DBG_INFO extra_info = { 0, NULL, 0, NULL };
|
||||
HANDLE hMap = 0;
|
||||
const BYTE* file_map = NULL;
|
||||
|
||||
/* Read in section data */
|
||||
|
||||
module->msc_dbg_info = &extra_info;
|
||||
extra_info.nsect = nth->FileHeader.NumberOfSections;
|
||||
extra_info.sectp = DBG_alloc(extra_info.nsect * sizeof(IMAGE_SECTION_HEADER));
|
||||
if (!extra_info.sectp)
|
||||
goto leave;
|
||||
|
||||
if (!DEBUG_READ_MEM_VERBOSE((char*)module->load_addr +
|
||||
nth_ofs + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader) +
|
||||
nth->FileHeader.SizeOfOptionalHeader,
|
||||
extra_info.sectp,
|
||||
extra_info.nsect * sizeof(IMAGE_SECTION_HEADER)))
|
||||
goto leave;
|
||||
|
||||
/* Read in debug directory */
|
||||
|
||||
nDbg = dir->Size / sizeof(IMAGE_DEBUG_DIRECTORY);
|
||||
if (!nDbg)
|
||||
goto leave;
|
||||
|
||||
dbg = (PIMAGE_DEBUG_DIRECTORY) DBG_alloc(nDbg * sizeof(IMAGE_DEBUG_DIRECTORY));
|
||||
if (!dbg)
|
||||
goto leave;
|
||||
|
||||
if (!DEBUG_READ_MEM_VERBOSE((char*)module->load_addr + dir->VirtualAddress,
|
||||
dbg, nDbg * sizeof(IMAGE_DEBUG_DIRECTORY)))
|
||||
goto leave;
|
||||
|
||||
|
||||
/* Map in PE file */
|
||||
file_map = DEBUG_MapDebugInfoFile(NULL, 0, 0, &hFile, &hMap);
|
||||
if (!file_map)
|
||||
goto leave;
|
||||
|
||||
|
||||
/* Parse debug directory */
|
||||
|
||||
if (nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED)
|
||||
{
|
||||
/* Debug info is stripped to .DBG file */
|
||||
|
||||
PIMAGE_DEBUG_MISC misc = (PIMAGE_DEBUG_MISC)(file_map + dbg->PointerToRawData);
|
||||
|
||||
if (nDbg != 1 || dbg->Type != IMAGE_DEBUG_TYPE_MISC
|
||||
|| misc->DataType != IMAGE_DEBUG_MISC_EXENAME)
|
||||
{
|
||||
WINE_ERR("-Debug info stripped, but no .DBG file in module %s\n",
|
||||
module->module_name);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
dil = DEBUG_ProcessDBGFile(module, misc->Data, nth->FileHeader.TimeDateStamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Debug info is embedded into PE module */
|
||||
/* FIXME: the nDBG information we're manipulating comes from the debuggee
|
||||
* address space. However, the following code will be made against the
|
||||
* version mapped in the debugger address space. There are cases (for example
|
||||
* when the PE sections are compressed in the file and become decompressed
|
||||
* in the debuggee address space) where the two don't match.
|
||||
* Therefore, redo the DBG information lookup with the mapped data
|
||||
*/
|
||||
PIMAGE_NT_HEADERS mpd_nth = (PIMAGE_NT_HEADERS)(file_map + nth_ofs);
|
||||
PIMAGE_DATA_DIRECTORY mpd_dir;
|
||||
PIMAGE_DEBUG_DIRECTORY mpd_dbg = NULL;
|
||||
|
||||
/* sanity checks */
|
||||
if (mpd_nth->Signature != IMAGE_NT_SIGNATURE ||
|
||||
mpd_nth->FileHeader.NumberOfSections != nth->FileHeader.NumberOfSections ||
|
||||
(mpd_nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED) != 0)
|
||||
goto leave;
|
||||
mpd_dir = mpd_nth->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_DEBUG;
|
||||
|
||||
if ((mpd_dir->Size / sizeof(IMAGE_DEBUG_DIRECTORY)) != nDbg)
|
||||
goto leave;
|
||||
|
||||
mpd_dbg = (PIMAGE_DEBUG_DIRECTORY)(file_map + mpd_dir->VirtualAddress);
|
||||
|
||||
dil = DEBUG_ProcessDebugDirectory(module, file_map, mpd_dbg, nDbg);
|
||||
}
|
||||
|
||||
|
||||
leave:
|
||||
module->msc_dbg_info = NULL;
|
||||
|
||||
DEBUG_UnmapDebugInfoFile(0, hMap, file_map);
|
||||
if (extra_info.sectp) DBG_free(extra_info.sectp);
|
||||
if (dbg) DBG_free(dbg);
|
||||
return dil;
|
||||
}
|
||||
|
||||
|
||||
/*========================================================================
|
||||
* look for stabs information in PE header (it's how mingw compiler provides its
|
||||
* debugging information), and also wine PE <-> ELF linking through .wsolnk sections
|
||||
*/
|
||||
enum DbgInfoLoad DEBUG_RegisterStabsDebugInfo(DBG_MODULE* module, HANDLE hFile,
|
||||
void* _nth, unsigned long nth_ofs)
|
||||
{
|
||||
IMAGE_SECTION_HEADER pe_seg;
|
||||
unsigned long pe_seg_ofs;
|
||||
int i, stabsize = 0, stabstrsize = 0;
|
||||
unsigned int stabs = 0, stabstr = 0;
|
||||
PIMAGE_NT_HEADERS nth = (PIMAGE_NT_HEADERS)_nth;
|
||||
enum DbgInfoLoad dil = DIL_ERROR;
|
||||
|
||||
pe_seg_ofs = nth_ofs + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader) +
|
||||
nth->FileHeader.SizeOfOptionalHeader;
|
||||
|
||||
for (i = 0; i < nth->FileHeader.NumberOfSections; i++, pe_seg_ofs += sizeof(pe_seg))
|
||||
{
|
||||
if (!DEBUG_READ_MEM_VERBOSE((void*)((char*)module->load_addr + pe_seg_ofs),
|
||||
&pe_seg, sizeof(pe_seg)))
|
||||
continue;
|
||||
|
||||
if (!strcasecmp(pe_seg.Name, ".stab"))
|
||||
{
|
||||
stabs = pe_seg.VirtualAddress;
|
||||
stabsize = pe_seg.SizeOfRawData;
|
||||
}
|
||||
else if (!strncasecmp(pe_seg.Name, ".stabstr", 8))
|
||||
{
|
||||
stabstr = pe_seg.VirtualAddress;
|
||||
stabstrsize = pe_seg.SizeOfRawData;
|
||||
}
|
||||
}
|
||||
|
||||
if (stabstrsize && stabsize)
|
||||
{
|
||||
char* s1 = DBG_alloc(stabsize+stabstrsize);
|
||||
|
||||
if (s1)
|
||||
{
|
||||
if (DEBUG_READ_MEM_VERBOSE((char*)module->load_addr + stabs, s1, stabsize) &&
|
||||
DEBUG_READ_MEM_VERBOSE((char*)module->load_addr + stabstr,
|
||||
s1 + stabsize, stabstrsize))
|
||||
{
|
||||
dil = DEBUG_ParseStabs(s1, 0, 0, stabsize, stabsize, stabstrsize);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_Printf("couldn't read data block\n");
|
||||
}
|
||||
DBG_free(s1);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_Printf("couldn't alloc %d bytes\n", stabsize + stabstrsize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dil = DIL_NOINFO;
|
||||
}
|
||||
return dil;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_RegisterPEDebugInfo
|
||||
*/
|
||||
enum DbgInfoLoad DEBUG_RegisterPEDebugInfo(DBG_MODULE* wmod, HANDLE hFile,
|
||||
void* _nth, unsigned long nth_ofs)
|
||||
{
|
||||
DBG_VALUE value;
|
||||
char buffer[512];
|
||||
char bufstr[256];
|
||||
unsigned int i;
|
||||
IMAGE_SECTION_HEADER pe_seg;
|
||||
DWORD pe_seg_ofs;
|
||||
IMAGE_DATA_DIRECTORY dir;
|
||||
DWORD dir_ofs;
|
||||
const char* prefix;
|
||||
IMAGE_NT_HEADERS* nth = (PIMAGE_NT_HEADERS)_nth;
|
||||
void* base = wmod->load_addr;
|
||||
|
||||
value.type = NULL;
|
||||
value.cookie = DV_TARGET;
|
||||
value.addr.seg = 0;
|
||||
value.addr.off = 0;
|
||||
|
||||
/* Add start of DLL */
|
||||
value.addr.off = (unsigned long)base;
|
||||
if ((prefix = strrchr(wmod->module_name, '\\'))) prefix++;
|
||||
else prefix = wmod->module_name;
|
||||
|
||||
DEBUG_AddSymbol(prefix, &value, NULL, SYM_WIN32 | SYM_FUNC);
|
||||
|
||||
/* Add entry point */
|
||||
snprintf(buffer, sizeof(buffer), "%s.EntryPoint", prefix);
|
||||
value.addr.off = (unsigned long)base + nth->OptionalHeader.AddressOfEntryPoint;
|
||||
DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC);
|
||||
|
||||
/* Add start of sections */
|
||||
pe_seg_ofs = nth_ofs + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader) +
|
||||
nth->FileHeader.SizeOfOptionalHeader;
|
||||
|
||||
for (i = 0; i < nth->FileHeader.NumberOfSections; i++, pe_seg_ofs += sizeof(pe_seg))
|
||||
{
|
||||
if (!DEBUG_READ_MEM_VERBOSE((char*)base + pe_seg_ofs, &pe_seg, sizeof(pe_seg)))
|
||||
continue;
|
||||
snprintf(buffer, sizeof(buffer), "%s.%s", prefix, pe_seg.Name);
|
||||
value.addr.off = (unsigned long)base + pe_seg.VirtualAddress;
|
||||
DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC);
|
||||
}
|
||||
|
||||
/* Add exported functions */
|
||||
dir_ofs = nth_ofs +
|
||||
OFFSET_OF(IMAGE_NT_HEADERS,
|
||||
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
|
||||
if (DEBUG_READ_MEM_VERBOSE((char*)base + dir_ofs, &dir, sizeof(dir)) && dir.Size)
|
||||
{
|
||||
IMAGE_EXPORT_DIRECTORY exports;
|
||||
WORD* ordinals = NULL;
|
||||
void** functions = NULL;
|
||||
DWORD* names = NULL;
|
||||
unsigned int j;
|
||||
|
||||
if (DEBUG_READ_MEM_VERBOSE((char*)base + dir.VirtualAddress,
|
||||
&exports, sizeof(exports)) &&
|
||||
|
||||
((functions = DBG_alloc(sizeof(functions[0]) * exports.NumberOfFunctions))) &&
|
||||
DEBUG_READ_MEM_VERBOSE((char*)base + exports.AddressOfFunctions,
|
||||
functions, sizeof(functions[0]) * exports.NumberOfFunctions) &&
|
||||
|
||||
((ordinals = DBG_alloc(sizeof(ordinals[0]) * exports.NumberOfNames))) &&
|
||||
DEBUG_READ_MEM_VERBOSE((char*)base + (DWORD)exports.AddressOfNameOrdinals,
|
||||
ordinals, sizeof(ordinals[0]) * exports.NumberOfNames) &&
|
||||
|
||||
((names = DBG_alloc(sizeof(names[0]) * exports.NumberOfNames))) &&
|
||||
DEBUG_READ_MEM_VERBOSE((char*)base + (DWORD)exports.AddressOfNames,
|
||||
names, sizeof(names[0]) * exports.NumberOfNames))
|
||||
{
|
||||
|
||||
for (i = 0; i < exports.NumberOfNames; i++)
|
||||
{
|
||||
if (!names[i] ||
|
||||
!DEBUG_READ_MEM_VERBOSE((char*)base + names[i], bufstr, sizeof(bufstr)))
|
||||
continue;
|
||||
bufstr[sizeof(bufstr) - 1] = 0;
|
||||
snprintf(buffer, sizeof(buffer), "%s.%s", prefix, bufstr);
|
||||
value.addr.off = (unsigned long)base + (DWORD)functions[ordinals[i]];
|
||||
DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC);
|
||||
}
|
||||
|
||||
for (i = 0; i < exports.NumberOfFunctions; i++)
|
||||
{
|
||||
if (!functions[i]) continue;
|
||||
/* Check if we already added it with a name */
|
||||
for (j = 0; j < exports.NumberOfNames; j++)
|
||||
if ((ordinals[j] == i) && names[j]) break;
|
||||
if (j < exports.NumberOfNames) continue;
|
||||
snprintf(buffer, sizeof(buffer), "%s.%ld", prefix, i + exports.Base);
|
||||
value.addr.off = (unsigned long)base + (DWORD)functions[i];
|
||||
DEBUG_AddSymbol(buffer, &value, NULL, SYM_WIN32 | SYM_FUNC);
|
||||
}
|
||||
}
|
||||
DBG_free(functions);
|
||||
DBG_free(ordinals);
|
||||
DBG_free(names);
|
||||
}
|
||||
/* no real debug info, only entry points */
|
||||
return DIL_NOINFO;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_LoadPEModule
|
||||
*/
|
||||
void DEBUG_LoadPEModule(const char* name, HANDLE hFile, void* base)
|
||||
{
|
||||
IMAGE_NT_HEADERS pe_header;
|
||||
DWORD nth_ofs;
|
||||
DBG_MODULE* wmod = NULL;
|
||||
int i;
|
||||
IMAGE_SECTION_HEADER pe_seg;
|
||||
DWORD pe_seg_ofs;
|
||||
DWORD size = 0;
|
||||
enum DbgInfoLoad dil = DIL_ERROR;
|
||||
|
||||
/* grab PE Header */
|
||||
if (!DEBUG_READ_MEM_VERBOSE((char*)base + OFFSET_OF(IMAGE_DOS_HEADER, e_lfanew),
|
||||
&nth_ofs, sizeof(nth_ofs)) ||
|
||||
!DEBUG_READ_MEM_VERBOSE((char*)base + nth_ofs, &pe_header, sizeof(pe_header)))
|
||||
return;
|
||||
|
||||
pe_seg_ofs = nth_ofs + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader) +
|
||||
pe_header.FileHeader.SizeOfOptionalHeader;
|
||||
|
||||
for (i = 0; i < pe_header.FileHeader.NumberOfSections; i++, pe_seg_ofs += sizeof(pe_seg))
|
||||
{
|
||||
if (!DEBUG_READ_MEM_VERBOSE((char*)base + pe_seg_ofs, &pe_seg, sizeof(pe_seg)))
|
||||
continue;
|
||||
if (size < pe_seg.VirtualAddress + pe_seg.SizeOfRawData)
|
||||
size = pe_seg.VirtualAddress + pe_seg.SizeOfRawData;
|
||||
}
|
||||
|
||||
/* FIXME: we make the assumption that hModule == base */
|
||||
wmod = DEBUG_AddModule(name, DMT_PE, base, size, (HMODULE)base);
|
||||
|
||||
if (wmod)
|
||||
{
|
||||
dil = DEBUG_RegisterStabsDebugInfo(wmod, hFile, &pe_header, nth_ofs);
|
||||
if (dil != DIL_LOADED)
|
||||
dil = DEBUG_RegisterMSCDebugInfo(wmod, hFile, &pe_header, nth_ofs);
|
||||
if (dil != DIL_LOADED)
|
||||
dil = DEBUG_RegisterPEDebugInfo(wmod, hFile, &pe_header, nth_ofs);
|
||||
wmod->dil = dil;
|
||||
}
|
||||
|
||||
DEBUG_ReportDIL(dil, "32bit DLL", name, base);
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
/*
|
||||
* Debugger register handling
|
||||
*
|
||||
* Copyright 1995 Alexandre Julliard
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <string.h>
|
||||
#include "debugger.h"
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_Flags
|
||||
*
|
||||
* Return Flag String.
|
||||
*/
|
||||
static char *DEBUG_Flags( DWORD flag, char *buf )
|
||||
{
|
||||
#ifdef __i386__
|
||||
char *pt;
|
||||
|
||||
strcpy( buf, " - 00 - - - " );
|
||||
pt = buf + strlen( buf );
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00000001 ) *pt = 'C'; /* Carry Falg */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00000002 ) *pt = '1';
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00000004 ) *pt = 'P'; /* Parity Flag */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00000008 ) *pt = '-';
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00000010 ) *pt = 'A'; /* Auxiliary Carry Flag */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00000020 ) *pt = '-';
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00000040 ) *pt = 'Z'; /* Zero Flag */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00000080 ) *pt = 'S'; /* Sign Flag */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00000100 ) *pt = 'T'; /* Trap/Trace Flag */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00000200 ) *pt = 'I'; /* Interupt Enable Flag */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00000400 ) *pt = 'D'; /* Direction Indicator */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00000800 ) *pt = 'O'; /* Overflow Flag */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00001000 ) *pt = '1'; /* I/O Privilege Level */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00002000 ) *pt = '1'; /* I/O Privilege Level */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00004000 ) *pt = 'N'; /* Nested Task Flag */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00008000 ) *pt = '-';
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00010000 ) *pt = 'R'; /* Resume Flag */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00020000 ) *pt = 'V'; /* Vritual Mode Flag */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
if ( flag & 0x00040000 ) *pt = 'a'; /* Alignment Check Flag */
|
||||
if ( buf >= pt-- ) return( buf );
|
||||
return( buf );
|
||||
#else
|
||||
strcpy(buf, "unknown CPU");
|
||||
return buf;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_InfoRegisters
|
||||
*
|
||||
* Display registers information.
|
||||
*/
|
||||
void DEBUG_InfoRegisters(const CONTEXT* ctx)
|
||||
{
|
||||
DEBUG_Printf("Register dump:\n");
|
||||
|
||||
#ifdef __i386__
|
||||
/* First get the segment registers out of the way */
|
||||
DEBUG_Printf(" CS:%04x SS:%04x DS:%04x ES:%04x FS:%04x GS:%04x",
|
||||
(WORD)ctx->SegCs, (WORD)ctx->SegSs,
|
||||
(WORD)ctx->SegDs, (WORD)ctx->SegEs,
|
||||
(WORD)ctx->SegFs, (WORD)ctx->SegGs);
|
||||
if (DEBUG_CurrThread->dbg_mode != MODE_32)
|
||||
{
|
||||
char flag[33];
|
||||
|
||||
DEBUG_Printf("\n IP:%04x SP:%04x BP:%04x FLAGS:%04x(%s)\n",
|
||||
LOWORD(ctx->Eip), LOWORD(ctx->Esp),
|
||||
LOWORD(ctx->Ebp), LOWORD(ctx->EFlags),
|
||||
DEBUG_Flags(LOWORD(ctx->EFlags), flag));
|
||||
DEBUG_Printf(" AX:%04x BX:%04x CX:%04x DX:%04x SI:%04x DI:%04x\n",
|
||||
LOWORD(ctx->Eax), LOWORD(ctx->Ebx),
|
||||
LOWORD(ctx->Ecx), LOWORD(ctx->Edx),
|
||||
LOWORD(ctx->Esi), LOWORD(ctx->Edi));
|
||||
}
|
||||
else /* 32-bit mode */
|
||||
{
|
||||
char flag[33];
|
||||
|
||||
DEBUG_Printf("\n EIP:%08lx ESP:%08lx EBP:%08lx EFLAGS:%08lx(%s)\n",
|
||||
ctx->Eip, ctx->Esp,
|
||||
ctx->Ebp, ctx->EFlags,
|
||||
DEBUG_Flags(ctx->EFlags, flag));
|
||||
DEBUG_Printf(" EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n",
|
||||
ctx->Eax, ctx->Ebx,
|
||||
ctx->Ecx, ctx->Edx );
|
||||
DEBUG_Printf(" ESI:%08lx EDI:%08lx\n",
|
||||
ctx->Esi, ctx->Edi );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_ValidateRegisters
|
||||
*
|
||||
* Make sure all registers have a correct value for returning from
|
||||
* the signal handler.
|
||||
*/
|
||||
BOOL DEBUG_ValidateRegisters(void)
|
||||
{
|
||||
DBG_ADDR addr;
|
||||
char ch;
|
||||
|
||||
#ifdef __i386__
|
||||
if (DEBUG_context.EFlags & V86_FLAG) return TRUE;
|
||||
|
||||
#if 0
|
||||
/* Check that a selector is a valid ring-3 LDT selector, or a NULL selector */
|
||||
#define CHECK_SEG(seg,name) \
|
||||
if (((seg) & ~3) && ((((seg) & 7) != 7) || !DEBUG_IsSelector(seg))) { \
|
||||
DEBUG_Printf("*** Invalid value for %s register: %04x\n", \
|
||||
(name), (WORD)(seg) ); \
|
||||
return FALSE; \
|
||||
}
|
||||
|
||||
cs = __get_cs();
|
||||
ds = __get_ds();
|
||||
if (DEBUG_context.SegCs != cs) CHECK_SEG(DEBUG_context.SegCs, "CS");
|
||||
if (DEBUG_context.SegSs != ds) CHECK_SEG(DEBUG_context.SegSs, "SS");
|
||||
if (DEBUG_context.SegDs != ds) CHECK_SEG(DEBUG_context.SegDs, "DS");
|
||||
if (DEBUG_context.SegEs != ds) CHECK_SEG(DEBUG_context.SegEs, "ES");
|
||||
if (DEBUG_context.SegFs != ds) CHECK_SEG(DEBUG_context.SegFs, "FS");
|
||||
if (DEBUG_context.SegGs != ds) CHECK_SEG(DEBUG_context.SegGs, "GS");
|
||||
#endif
|
||||
|
||||
/* Check that CS and SS are not NULL */
|
||||
|
||||
if (!(DEBUG_context.SegCs & ~3))
|
||||
{
|
||||
DEBUG_Printf("*** Invalid value for CS register: %04x\n",
|
||||
(WORD)DEBUG_context.SegCs );
|
||||
return FALSE;
|
||||
}
|
||||
if (!(DEBUG_context.SegSs & ~3))
|
||||
{
|
||||
DEBUG_Printf("*** Invalid value for SS register: %04x\n",
|
||||
(WORD)DEBUG_context.SegSs );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#undef CHECK_SEG
|
||||
#else
|
||||
/* to be written */
|
||||
#endif
|
||||
|
||||
/* check if PC is correct */
|
||||
DEBUG_GetCurrentAddress(&addr);
|
||||
return DEBUG_READ_MEM_VERBOSE((void*)DEBUG_ToLinear(&addr), &ch, 1);
|
||||
}
|
|
@ -22,93 +22,76 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX MAX_PATH
|
||||
#endif
|
||||
|
||||
#include "debugger.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
struct searchlist
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
|
||||
|
||||
struct search_list
|
||||
{
|
||||
char * path;
|
||||
struct searchlist * next;
|
||||
char* path;
|
||||
struct search_list* next;
|
||||
};
|
||||
|
||||
|
||||
struct open_filelist
|
||||
struct open_file_list
|
||||
{
|
||||
char * path;
|
||||
char * real_path;
|
||||
struct open_filelist * next;
|
||||
unsigned int size;
|
||||
signed int nlines;
|
||||
unsigned int * linelist;
|
||||
char* path;
|
||||
char* real_path;
|
||||
struct open_file_list* next;
|
||||
unsigned int size;
|
||||
signed int nlines;
|
||||
unsigned int* linelist;
|
||||
};
|
||||
|
||||
static struct open_filelist * ofiles;
|
||||
static struct open_file_list* source_ofiles;
|
||||
|
||||
static struct searchlist * listhead;
|
||||
static char DEBUG_current_sourcefile[PATH_MAX];
|
||||
static int DEBUG_start_sourceline = -1;
|
||||
static int DEBUG_end_sourceline = -1;
|
||||
static struct search_list* source_list_head;
|
||||
static char source_current_file[PATH_MAX];
|
||||
static int source_start_line = -1;
|
||||
static int source_end_line = -1;
|
||||
|
||||
void
|
||||
DEBUG_ShowDir(void)
|
||||
void source_show_path(void)
|
||||
{
|
||||
struct searchlist * sl;
|
||||
struct search_list* sl;
|
||||
|
||||
DEBUG_Printf("Search list :\n");
|
||||
for(sl = listhead; sl; sl = sl->next)
|
||||
{
|
||||
DEBUG_Printf("\t%s\n", sl->path);
|
||||
}
|
||||
DEBUG_Printf("\n");
|
||||
dbg_printf("Search list:\n");
|
||||
for (sl = source_list_head; sl; sl = sl->next) dbg_printf("\t%s\n", sl->path);
|
||||
dbg_printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
DEBUG_AddPath(const char * path)
|
||||
void source_add_path(const char* path)
|
||||
{
|
||||
struct searchlist * sl;
|
||||
struct search_list* sl;
|
||||
|
||||
sl = (struct searchlist *) DBG_alloc(sizeof(struct searchlist));
|
||||
if( sl == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!(sl = HeapAlloc(GetProcessHeap(), 0, sizeof(struct search_list))))
|
||||
return;
|
||||
|
||||
sl->next = listhead;
|
||||
sl->path = DBG_strdup(path);
|
||||
listhead = sl;
|
||||
sl->next = source_list_head;
|
||||
sl->path = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(path) + 1), path);
|
||||
source_list_head = sl;
|
||||
}
|
||||
|
||||
void
|
||||
DEBUG_NukePath(void)
|
||||
void source_nuke_path(void)
|
||||
{
|
||||
struct searchlist * sl;
|
||||
struct searchlist * nxt;
|
||||
struct search_list* sl;
|
||||
struct search_list* nxt;
|
||||
|
||||
for(sl = listhead; sl; sl = nxt)
|
||||
for (sl = source_list_head; sl; sl = nxt)
|
||||
{
|
||||
nxt = sl->next;
|
||||
DBG_free(sl->path);
|
||||
DBG_free(sl);
|
||||
nxt = sl->next;
|
||||
HeapFree(GetProcessHeap(), 0, sl->path);
|
||||
HeapFree(GetProcessHeap(), 0, sl);
|
||||
}
|
||||
|
||||
listhead = NULL;
|
||||
source_list_head = NULL;
|
||||
}
|
||||
|
||||
static void* DEBUG_MapFile(const char* name, HANDLE* hMap, unsigned* size)
|
||||
static void* source_map_file(const char* name, HANDLE* hMap, unsigned* size)
|
||||
{
|
||||
HANDLE hFile;
|
||||
|
||||
|
@ -122,35 +105,33 @@ static void* DEBUG_MapFile(const char* name, HANDLE* hMap, unsigned* size)
|
|||
return MapViewOfFile(*hMap, FILE_MAP_READ, 0, 0, 0);
|
||||
}
|
||||
|
||||
static void DEBUG_UnmapFile(void* addr, HANDLE hMap)
|
||||
static void source_unmap_file(void* addr, HANDLE hMap)
|
||||
{
|
||||
UnmapViewOfFile(addr);
|
||||
CloseHandle(hMap);
|
||||
}
|
||||
|
||||
static struct open_filelist* DEBUG_SearchOpenFile(const char* name)
|
||||
static struct open_file_list* source_search_open_file(const char* name)
|
||||
{
|
||||
struct open_filelist* ol;
|
||||
struct open_file_list* ol;
|
||||
|
||||
for (ol = ofiles; ol; ol = ol->next)
|
||||
for (ol = source_ofiles; ol; ol = ol->next)
|
||||
{
|
||||
if (strcmp(ol->path, name) == 0) break;
|
||||
}
|
||||
return ol;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
DEBUG_DisplaySource(char * sourcefile, int start, int end)
|
||||
static int source_display(const char* sourcefile, int start, int end)
|
||||
{
|
||||
char* addr;
|
||||
int i;
|
||||
struct open_filelist* ol;
|
||||
struct open_file_list* ol;
|
||||
int nlines;
|
||||
char* basename = NULL;
|
||||
const char* basename = NULL;
|
||||
char* pnt;
|
||||
int rtn;
|
||||
struct searchlist* sl;
|
||||
struct search_list* sl;
|
||||
HANDLE hMap;
|
||||
DWORD status;
|
||||
char tmppath[PATH_MAX];
|
||||
|
@ -159,44 +140,41 @@ DEBUG_DisplaySource(char * sourcefile, int start, int end)
|
|||
* First see whether we have the file open already. If so, then
|
||||
* use that, otherwise we have to try and open it.
|
||||
*/
|
||||
ol = DEBUG_SearchOpenFile(sourcefile);
|
||||
ol = source_search_open_file(sourcefile);
|
||||
|
||||
if ( ol == NULL )
|
||||
if (ol == NULL)
|
||||
{
|
||||
/*
|
||||
* Try again, stripping the path from the opened file.
|
||||
*/
|
||||
basename = strrchr(sourcefile, '\\' );
|
||||
if ( !basename )
|
||||
basename = strrchr(sourcefile, '/' );
|
||||
if ( !basename )
|
||||
basename = sourcefile;
|
||||
else
|
||||
basename++;
|
||||
basename = strrchr(sourcefile, '\\');
|
||||
if (!basename) basename = strrchr(sourcefile, '/');
|
||||
if (!basename) basename = sourcefile;
|
||||
else basename++;
|
||||
|
||||
ol = DEBUG_SearchOpenFile(basename);
|
||||
ol = source_search_open_file(basename);
|
||||
}
|
||||
|
||||
if ( ol == NULL )
|
||||
if (ol == NULL)
|
||||
{
|
||||
/*
|
||||
* Crapola. We need to try and open the file.
|
||||
*/
|
||||
status = GetFileAttributes(sourcefile);
|
||||
if ( status != INVALID_FILE_ATTRIBUTES )
|
||||
if (status != INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
strcpy(tmppath, sourcefile);
|
||||
}
|
||||
else if ( (status = GetFileAttributes(basename)) != INVALID_FILE_ATTRIBUTES )
|
||||
else if ((status = GetFileAttributes(basename)) != INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
strcpy(tmppath, basename);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (sl = listhead; sl; sl = sl->next)
|
||||
for (sl = source_list_head; sl; sl = sl->next)
|
||||
{
|
||||
strcpy(tmppath, sl->path);
|
||||
if ( tmppath[strlen(tmppath)-1] != '/' && tmppath[strlen(tmppath)-1] != '\\' )
|
||||
if (tmppath[strlen(tmppath) - 1] != '/' && tmppath[strlen(tmppath) - 1] != '\\')
|
||||
{
|
||||
strcat(tmppath, "/");
|
||||
}
|
||||
|
@ -206,21 +184,21 @@ DEBUG_DisplaySource(char * sourcefile, int start, int end)
|
|||
strcat(tmppath, basename);
|
||||
|
||||
status = GetFileAttributes(tmppath);
|
||||
if ( status != INVALID_FILE_ATTRIBUTES ) break;
|
||||
if (status != INVALID_FILE_ATTRIBUTES) break;
|
||||
}
|
||||
|
||||
if ( sl == NULL )
|
||||
if (sl == NULL)
|
||||
{
|
||||
if (DEBUG_InteractiveP)
|
||||
if (dbg_interactiveP)
|
||||
{
|
||||
char zbuf[256];
|
||||
/*
|
||||
* Still couldn't find it. Ask user for path to add.
|
||||
*/
|
||||
snprintf(zbuf, sizeof(zbuf), "Enter path to file '%s': ", sourcefile);
|
||||
DEBUG_ReadLine(zbuf, tmppath, sizeof(tmppath));
|
||||
input_read_line(zbuf, tmppath, sizeof(tmppath));
|
||||
|
||||
if ( tmppath[strlen(tmppath)-1] != '/' )
|
||||
if (tmppath[strlen(tmppath) - 1] != '/')
|
||||
{
|
||||
strcat(tmppath, "/");
|
||||
}
|
||||
|
@ -237,20 +215,20 @@ DEBUG_DisplaySource(char * sourcefile, int start, int end)
|
|||
strcpy(tmppath, sourcefile);
|
||||
}
|
||||
|
||||
if ( status == INVALID_FILE_ATTRIBUTES )
|
||||
if (status == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
/*
|
||||
* OK, I guess the user doesn't really want to see it
|
||||
* after all.
|
||||
*/
|
||||
ol = (struct open_filelist *) DBG_alloc(sizeof(*ol));
|
||||
ol->path = DBG_strdup(sourcefile);
|
||||
ol = HeapAlloc(GetProcessHeap(), 0, sizeof(*ol));
|
||||
ol->path = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(sourcefile) + 1), sourcefile);
|
||||
ol->real_path = NULL;
|
||||
ol->next = ofiles;
|
||||
ol->next = source_ofiles;
|
||||
ol->nlines = 0;
|
||||
ol->linelist = NULL;
|
||||
ofiles = ol;
|
||||
DEBUG_Printf("Unable to open file %s\n", tmppath);
|
||||
source_ofiles = ol;
|
||||
dbg_printf("Unable to open file %s\n", tmppath);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
@ -258,56 +236,44 @@ DEBUG_DisplaySource(char * sourcefile, int start, int end)
|
|||
/*
|
||||
* Create header for file.
|
||||
*/
|
||||
ol = (struct open_filelist *) DBG_alloc(sizeof(*ol));
|
||||
ol->path = DBG_strdup(sourcefile);
|
||||
ol->real_path = DBG_strdup(tmppath);
|
||||
ol->next = ofiles;
|
||||
ol = HeapAlloc(GetProcessHeap(), 0, sizeof(*ol));
|
||||
ol->path = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(sourcefile) + 1), sourcefile);
|
||||
ol->real_path = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(tmppath) + 1), tmppath);
|
||||
ol->next = source_ofiles;
|
||||
ol->nlines = 0;
|
||||
ol->linelist = NULL;
|
||||
ol->size = 0;
|
||||
ofiles = ol;
|
||||
source_ofiles = ol;
|
||||
|
||||
addr = DEBUG_MapFile(tmppath, &hMap, &ol->size);
|
||||
if ( addr == (char *) -1 )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
addr = source_map_file(tmppath, &hMap, &ol->size);
|
||||
if (addr == (char*)-1) return FALSE;
|
||||
/*
|
||||
* Now build up the line number mapping table.
|
||||
*/
|
||||
ol->nlines = 1;
|
||||
pnt = addr;
|
||||
while (pnt < addr + ol->size )
|
||||
while (pnt < addr + ol->size)
|
||||
{
|
||||
if ( *pnt++ == '\n' )
|
||||
{
|
||||
ol->nlines++;
|
||||
}
|
||||
if (*pnt++ == '\n') ol->nlines++;
|
||||
}
|
||||
|
||||
ol->nlines++;
|
||||
ol->linelist = (unsigned int*) DBG_alloc( ol->nlines * sizeof(unsigned int) );
|
||||
ol->linelist = HeapAlloc(GetProcessHeap(), 0, ol->nlines * sizeof(unsigned int));
|
||||
|
||||
nlines = 0;
|
||||
pnt = addr;
|
||||
ol->linelist[nlines++] = 0;
|
||||
while(pnt < addr + ol->size )
|
||||
while (pnt < addr + ol->size)
|
||||
{
|
||||
if( *pnt++ == '\n' )
|
||||
{
|
||||
ol->linelist[nlines++] = pnt - addr;
|
||||
}
|
||||
if (*pnt++ == '\n') ol->linelist[nlines++] = pnt - addr;
|
||||
}
|
||||
ol->linelist[nlines++] = pnt - addr;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = DEBUG_MapFile(ol->real_path, &hMap, NULL);
|
||||
if ( addr == (char *) -1 )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
addr = source_map_file(ol->real_path, &hMap, NULL);
|
||||
if (addr == (char*)-1) return FALSE;
|
||||
}
|
||||
/*
|
||||
* All we need to do is to display the source lines here.
|
||||
|
@ -317,176 +283,92 @@ DEBUG_DisplaySource(char * sourcefile, int start, int end)
|
|||
{
|
||||
char buffer[1024];
|
||||
|
||||
if (i < 0 || i >= ol->nlines - 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (i < 0 || i >= ol->nlines - 1) continue;
|
||||
|
||||
rtn = TRUE;
|
||||
memset(&buffer, 0, sizeof(buffer));
|
||||
if ( ol->linelist[i+1] != ol->linelist[i] )
|
||||
if (ol->linelist[i+1] != ol->linelist[i])
|
||||
{
|
||||
memcpy(&buffer, addr + ol->linelist[i],
|
||||
(ol->linelist[i+1] - ol->linelist[i]) - 1);
|
||||
}
|
||||
DEBUG_Printf("%d\t%s\n", i + 1, buffer);
|
||||
dbg_printf("%d\t%s\n", i + 1, buffer);
|
||||
}
|
||||
|
||||
DEBUG_UnmapFile(addr, hMap);
|
||||
source_unmap_file(addr, hMap);
|
||||
return rtn;
|
||||
}
|
||||
|
||||
void
|
||||
DEBUG_List(struct list_id * source1, struct list_id * source2,
|
||||
int delta)
|
||||
void source_list(IMAGEHLP_LINE* src1, IMAGEHLP_LINE* src2, int delta)
|
||||
{
|
||||
int end;
|
||||
int rtn;
|
||||
int start;
|
||||
char * sourcefile;
|
||||
int end;
|
||||
int start;
|
||||
const char* sourcefile;
|
||||
|
||||
/*
|
||||
* We need to see what source file we need. Hopefully we only have
|
||||
* one specified, otherwise we might as well punt.
|
||||
*/
|
||||
if( source1 != NULL
|
||||
&& source2 != NULL
|
||||
&& source1->sourcefile != NULL
|
||||
&& source2->sourcefile != NULL
|
||||
&& strcmp(source1->sourcefile, source2->sourcefile) != 0 )
|
||||
/*
|
||||
* We need to see what source file we need. Hopefully we only have
|
||||
* one specified, otherwise we might as well punt.
|
||||
*/
|
||||
if (src1 && src2 && src1->FileName && src2->FileName &&
|
||||
strcmp(src1->FileName, src2->FileName) != 0)
|
||||
{
|
||||
DEBUG_Printf("Ambiguous source file specification.\n");
|
||||
return;
|
||||
dbg_printf("Ambiguous source file specification.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sourcefile = NULL;
|
||||
if( source1 != NULL && source1->sourcefile != NULL )
|
||||
{
|
||||
sourcefile = source1->sourcefile;
|
||||
}
|
||||
sourcefile = NULL;
|
||||
if (src1 && src1->FileName) sourcefile = src1->FileName;
|
||||
if (!sourcefile && src2 && src2->FileName) sourcefile = src2->FileName;
|
||||
if (!sourcefile) sourcefile = source_current_file;
|
||||
|
||||
if( sourcefile == NULL
|
||||
&& source2 != NULL
|
||||
&& source2->sourcefile != NULL )
|
||||
{
|
||||
sourcefile = source2->sourcefile;
|
||||
}
|
||||
/*
|
||||
* Now figure out the line number range to be listed.
|
||||
*/
|
||||
start = end = -1;
|
||||
|
||||
if( sourcefile == NULL )
|
||||
if (src1) start = src1->LineNumber;
|
||||
if (src2) end = src2->LineNumber;
|
||||
if (start == -1 && end == -1)
|
||||
{
|
||||
sourcefile = (char *) &DEBUG_current_sourcefile;
|
||||
}
|
||||
|
||||
if( sourcefile == NULL )
|
||||
{
|
||||
DEBUG_Printf("No source file specified.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now figure out the line number range to be listed.
|
||||
*/
|
||||
start = -1;
|
||||
end = -1;
|
||||
|
||||
if( source1 != NULL )
|
||||
{
|
||||
start = source1->line;
|
||||
}
|
||||
|
||||
if( source2 != NULL )
|
||||
{
|
||||
end = source2->line;
|
||||
}
|
||||
|
||||
if( start == -1 && end == -1 )
|
||||
{
|
||||
if( delta < 0 )
|
||||
if (delta < 0)
|
||||
{
|
||||
end = DEBUG_start_sourceline;
|
||||
start = end + delta;
|
||||
end = source_start_line;
|
||||
start = end + delta;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
start = DEBUG_end_sourceline;
|
||||
end = start + delta;
|
||||
start = source_end_line;
|
||||
end = start + delta;
|
||||
}
|
||||
}
|
||||
else if( start == -1 )
|
||||
{
|
||||
start = end + delta;
|
||||
}
|
||||
else if (end == -1)
|
||||
{
|
||||
end = start + delta;
|
||||
}
|
||||
else if (start == -1) start = end + delta;
|
||||
else if (end == -1) end = start + delta;
|
||||
|
||||
/*
|
||||
* Now call this function to do the dirty work.
|
||||
*/
|
||||
rtn = DEBUG_DisplaySource(sourcefile, start, end);
|
||||
/*
|
||||
* Now call this function to do the dirty work.
|
||||
*/
|
||||
source_display(sourcefile, start, end);
|
||||
|
||||
if( sourcefile != (char *) &DEBUG_current_sourcefile )
|
||||
{
|
||||
strcpy(DEBUG_current_sourcefile, sourcefile);
|
||||
}
|
||||
DEBUG_start_sourceline = start;
|
||||
DEBUG_end_sourceline = end;
|
||||
if (sourcefile != source_current_file)
|
||||
strcpy(source_current_file, sourcefile);
|
||||
source_start_line = start;
|
||||
source_end_line = end;
|
||||
}
|
||||
|
||||
DBG_ADDR DEBUG_LastDisassemble={0,0};
|
||||
|
||||
BOOL DEBUG_DisassembleInstruction(DBG_ADDR *addr)
|
||||
void source_list_from_addr(const ADDRESS* addr, int nlines)
|
||||
{
|
||||
char ch;
|
||||
BOOL ret = TRUE;
|
||||
IMAGEHLP_LINE il;
|
||||
DWORD lin;
|
||||
|
||||
DEBUG_PrintAddress(addr, DEBUG_CurrThread->dbg_mode, TRUE);
|
||||
DEBUG_Printf(": ");
|
||||
if (!DEBUG_READ_MEM_VERBOSE((void*)DEBUG_ToLinear(addr), &ch, sizeof(ch))) {
|
||||
DEBUG_Printf("-- no code --");
|
||||
ret = FALSE;
|
||||
} else {
|
||||
DEBUG_Disasm(addr, TRUE);
|
||||
}
|
||||
DEBUG_Printf("\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
DEBUG_Disassemble(const DBG_VALUE *xstart,const DBG_VALUE *xend,int offset)
|
||||
{
|
||||
int i;
|
||||
DBG_ADDR last;
|
||||
DBG_VALUE end,start;
|
||||
|
||||
if (xstart) {
|
||||
start = *xstart;
|
||||
DEBUG_GrabAddress(&start, TRUE);
|
||||
}
|
||||
if (xend) {
|
||||
end = *xend;
|
||||
DEBUG_GrabAddress(&end, TRUE);
|
||||
}
|
||||
if (!xstart && !xend) {
|
||||
last = DEBUG_LastDisassemble;
|
||||
if (!last.seg && !last.off)
|
||||
DEBUG_GetCurrentAddress( &last );
|
||||
|
||||
for (i=0;i<offset;i++)
|
||||
if (!DEBUG_DisassembleInstruction(&last)) break;
|
||||
DEBUG_LastDisassemble = last;
|
||||
return;
|
||||
}
|
||||
last = start.addr;
|
||||
if (!xend) {
|
||||
for (i=0;i<offset;i++)
|
||||
if (!DEBUG_DisassembleInstruction(&last)) break;
|
||||
DEBUG_LastDisassemble = last;
|
||||
return;
|
||||
}
|
||||
while (last.off <= end.addr.off)
|
||||
if (!DEBUG_DisassembleInstruction(&last)) break;
|
||||
DEBUG_LastDisassemble = last;
|
||||
return;
|
||||
if (!addr)
|
||||
{
|
||||
ADDRESS la;
|
||||
memory_get_current_pc(&la);
|
||||
lin = (unsigned long)memory_to_linear_addr(&la);
|
||||
}
|
||||
else lin = (unsigned long)memory_to_linear_addr(addr);
|
||||
|
||||
il.SizeOfStruct = sizeof(il);
|
||||
if (SymGetLineFromAddr(dbg_curr_process->handle, lin, NULL, &il))
|
||||
source_list(&il, NULL, nlines);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -27,450 +27,146 @@
|
|||
#include "debugger.h"
|
||||
#include "stackframe.h"
|
||||
#include "winbase.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
#ifdef __i386__
|
||||
/*
|
||||
* We keep this info for each frame, so that we can
|
||||
* find local variable information correctly.
|
||||
*/
|
||||
struct bt_info
|
||||
{
|
||||
unsigned int cs;
|
||||
unsigned int eip;
|
||||
unsigned int ss;
|
||||
unsigned int ebp;
|
||||
struct symbol_info frame;
|
||||
};
|
||||
|
||||
static int nframe;
|
||||
static struct bt_info * frames = NULL;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WORD bp;
|
||||
WORD ip;
|
||||
WORD cs;
|
||||
} FRAME16;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD bp;
|
||||
DWORD ip;
|
||||
WORD cs;
|
||||
} FRAME32;
|
||||
#endif
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
|
||||
|
||||
static int nframe;
|
||||
static IMAGEHLP_STACK_FRAME* frames = NULL;
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_InfoStack
|
||||
* stack_info
|
||||
*
|
||||
* Dump the top of the stack
|
||||
*/
|
||||
void DEBUG_InfoStack(void)
|
||||
void stack_info(void)
|
||||
{
|
||||
#ifdef __i386__
|
||||
DBG_VALUE value;
|
||||
struct dbg_lvalue lvalue;
|
||||
|
||||
value.type = NULL;
|
||||
value.cookie = DV_TARGET;
|
||||
value.addr.seg = DEBUG_context.SegSs;
|
||||
value.addr.off = DEBUG_context.Esp;
|
||||
lvalue.typeid = dbg_itype_none;
|
||||
lvalue.cookie = DLV_TARGET;
|
||||
/* FIXME: we assume stack grows the same way as on i386 */
|
||||
if (!memory_get_current_stack(&lvalue.addr))
|
||||
dbg_printf("Bad segment (%d)\n", lvalue.addr.Segment);
|
||||
|
||||
DEBUG_Printf("Stack dump:\n");
|
||||
switch (DEBUG_GetSelectorType(value.addr.seg))
|
||||
dbg_printf("Stack dump:\n");
|
||||
switch (lvalue.addr.Mode)
|
||||
{
|
||||
case MODE_32: /* 32-bit mode */
|
||||
DEBUG_ExamineMemory( &value, 24, 'x' );
|
||||
break;
|
||||
case MODE_16: /* 16-bit mode */
|
||||
case MODE_VM86:
|
||||
value.addr.off &= 0xffff;
|
||||
DEBUG_ExamineMemory( &value, 24, 'w' );
|
||||
case AddrModeFlat: /* 32-bit mode */
|
||||
case AddrMode1632: /* 32-bit mode */
|
||||
memory_examine(&lvalue, 24, 'x');
|
||||
break;
|
||||
case AddrModeReal: /* 16-bit mode */
|
||||
case AddrMode1616:
|
||||
memory_examine(&lvalue, 24, 'w');
|
||||
break;
|
||||
default:
|
||||
DEBUG_Printf("Bad segment (%ld)\n", value.addr.seg);
|
||||
}
|
||||
DEBUG_Printf("\n");
|
||||
#endif
|
||||
dbg_printf("\n");
|
||||
}
|
||||
|
||||
#ifdef __i386__
|
||||
static void DEBUG_ForceFrame(DBG_ADDR *stack, DBG_ADDR *code, int frameno, enum dbg_mode mode,
|
||||
int noisy, const char *caveat)
|
||||
int stack_set_frame(int newframe)
|
||||
{
|
||||
int theframe = nframe++;
|
||||
frames = (struct bt_info *)DBG_realloc(frames,
|
||||
nframe*sizeof(struct bt_info));
|
||||
if (noisy)
|
||||
DEBUG_Printf("%s%d ", (theframe == curr_frame ? "=>" : " "),
|
||||
frameno);
|
||||
frames[theframe].cs = code->seg;
|
||||
frames[theframe].eip = code->off;
|
||||
if (noisy)
|
||||
frames[theframe].frame = DEBUG_PrintAddressAndArgs( code, mode, stack->off, TRUE );
|
||||
else
|
||||
DEBUG_FindNearestSymbol( code, TRUE,
|
||||
&frames[theframe].frame.sym, stack->off,
|
||||
&frames[theframe].frame.list);
|
||||
frames[theframe].ss = stack->seg;
|
||||
frames[theframe].ebp = stack->off;
|
||||
if (noisy) {
|
||||
DEBUG_Printf((mode != MODE_32) ? " (bp=%04lx%s)\n" : " (ebp=%08lx%s)\n",
|
||||
stack->off, caveat ? caveat : "");
|
||||
}
|
||||
}
|
||||
ADDRESS addr;
|
||||
|
||||
static BOOL DEBUG_Frame16(DBG_THREAD* thread, DBG_ADDR *addr, unsigned int *cs, int frameno, int noisy)
|
||||
{
|
||||
unsigned int possible_cs = 0;
|
||||
FRAME16 frame;
|
||||
void* p = (void*)DEBUG_ToLinear(addr);
|
||||
DBG_ADDR code;
|
||||
dbg_curr_frame = newframe;
|
||||
if (dbg_curr_frame >= nframe) dbg_curr_frame = nframe - 1;
|
||||
if (dbg_curr_frame < 0) dbg_curr_frame = 0;
|
||||
|
||||
if (!p) return FALSE;
|
||||
|
||||
if (!DEBUG_READ_MEM(p, &frame, sizeof(frame))) {
|
||||
if (noisy) DEBUG_InvalAddr(addr);
|
||||
return FALSE;
|
||||
}
|
||||
if (!frame.bp) return FALSE;
|
||||
|
||||
if (frame.bp & 1) *cs = frame.cs;
|
||||
else {
|
||||
/* not explicitly marked as far call,
|
||||
* but check whether it could be anyway */
|
||||
if (((frame.cs&7)==7) && (frame.cs != *cs)) {
|
||||
LDT_ENTRY le;
|
||||
|
||||
if (GetThreadSelectorEntry( thread->handle, frame.cs, &le) &&
|
||||
(le.HighWord.Bits.Type & 0x08)) { /* code segment */
|
||||
/* it is very uncommon to push a code segment cs as
|
||||
* a parameter, so this should work in most cases */
|
||||
*cs = possible_cs = frame.cs;
|
||||
}
|
||||
}
|
||||
}
|
||||
code.seg = *cs;
|
||||
code.off = frame.ip;
|
||||
addr->off = frame.bp & ~1;
|
||||
DEBUG_ForceFrame(addr, &code, frameno, MODE_16, noisy,
|
||||
possible_cs ? ", far call assumed" : NULL );
|
||||
addr.Mode = AddrModeFlat;
|
||||
addr.Offset = frames[dbg_curr_frame].InstructionOffset;
|
||||
source_list_from_addr(&addr, 0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL DEBUG_Frame32(DBG_ADDR *addr, unsigned int *cs, int frameno, int noisy)
|
||||
int stack_get_frame(SYMBOL_INFO* symbol, IMAGEHLP_STACK_FRAME* ihsf)
|
||||
{
|
||||
FRAME32 frame;
|
||||
void* p = (void*)DEBUG_ToLinear(addr);
|
||||
DBG_ADDR code;
|
||||
DWORD old_bp = addr->off;
|
||||
|
||||
if (!p) return FALSE;
|
||||
|
||||
if (!DEBUG_READ_MEM(p, &frame, sizeof(frame))) {
|
||||
if (noisy) DEBUG_InvalAddr(addr);
|
||||
return FALSE;
|
||||
}
|
||||
if (!frame.ip) return FALSE;
|
||||
|
||||
code.seg = *cs;
|
||||
code.off = frame.ip;
|
||||
addr->off = frame.bp;
|
||||
DEBUG_ForceFrame(addr, &code, frameno, MODE_32, noisy, NULL);
|
||||
if (addr->off == old_bp) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* DEBUG_BackTrace
|
||||
*
|
||||
* Display a stack back-trace.
|
||||
*/
|
||||
void DEBUG_BackTrace(DWORD tid, BOOL noisy)
|
||||
{
|
||||
#ifdef __i386
|
||||
DBG_ADDR addr, sw_addr, code, tmp;
|
||||
unsigned int ss, cs;
|
||||
int frameno = 0, is16, ok;
|
||||
DWORD next_switch, cur_switch, p;
|
||||
STACK16FRAME frame16;
|
||||
STACK32FRAME frame32;
|
||||
char ch;
|
||||
CONTEXT ctx;
|
||||
DBG_THREAD* thread;
|
||||
|
||||
int copy_nframe = 0;
|
||||
int copy_curr_frame = 0;
|
||||
struct bt_info* copy_frames = NULL;
|
||||
|
||||
if (noisy) DEBUG_Printf("Backtrace:\n");
|
||||
|
||||
if (tid == DEBUG_CurrTid)
|
||||
{
|
||||
ctx = DEBUG_context;
|
||||
thread = DEBUG_CurrThread;
|
||||
|
||||
if (frames) DBG_free( frames );
|
||||
/* frames = (struct bt_info *) DBG_alloc( sizeof(struct bt_info) ); */
|
||||
}
|
||||
else
|
||||
{
|
||||
thread = DEBUG_GetThread(DEBUG_CurrProcess, tid);
|
||||
|
||||
if (!thread)
|
||||
{
|
||||
DEBUG_Printf("Unknown thread id (0x%08lx) in current process\n", tid);
|
||||
return;
|
||||
}
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_SEGMENTS;
|
||||
|
||||
if ( SuspendThread( thread->handle ) == -1 ||
|
||||
!GetThreadContext( thread->handle, &ctx ))
|
||||
{
|
||||
DEBUG_Printf("Can't get context for thread id (0x%08lx) in current process\n", tid);
|
||||
return;
|
||||
}
|
||||
/* need to avoid trashing stack frame for current thread */
|
||||
copy_nframe = nframe;
|
||||
copy_frames = frames;
|
||||
copy_curr_frame = curr_frame;
|
||||
curr_frame = 0;
|
||||
}
|
||||
|
||||
nframe = 0;
|
||||
frames = NULL;
|
||||
|
||||
cs = ctx.SegCs;
|
||||
ss = ctx.SegSs;
|
||||
|
||||
if (DEBUG_IsSelectorSystem(ss)) ss = 0;
|
||||
if (DEBUG_IsSelectorSystem(cs)) cs = 0;
|
||||
|
||||
/* first stack frame from registers */
|
||||
switch (DEBUG_GetSelectorType(ss))
|
||||
{
|
||||
case MODE_32:
|
||||
code.seg = cs;
|
||||
code.off = ctx.Eip;
|
||||
addr.seg = ss;
|
||||
addr.off = ctx.Ebp;
|
||||
DEBUG_ForceFrame( &addr, &code, frameno, MODE_32, noisy, NULL );
|
||||
if (!(code.seg || code.off)) {
|
||||
/* trying to execute a null pointer... yuck...
|
||||
* if it was a call to null, the return EIP should be
|
||||
* available at SS:ESP, so let's try to retrieve it */
|
||||
tmp.seg = ss;
|
||||
tmp.off = ctx.Esp;
|
||||
if (DEBUG_READ_MEM((void *)DEBUG_ToLinear(&tmp), &code.off, sizeof(code.off))) {
|
||||
DEBUG_ForceFrame( &addr, &code, ++frameno, MODE_32, noisy, ", null call assumed" );
|
||||
}
|
||||
}
|
||||
is16 = FALSE;
|
||||
break;
|
||||
case MODE_16:
|
||||
case MODE_VM86:
|
||||
code.seg = cs;
|
||||
code.off = LOWORD(ctx.Eip);
|
||||
addr.seg = ss;
|
||||
addr.off = LOWORD(ctx.Ebp);
|
||||
DEBUG_ForceFrame( &addr, &code, frameno, MODE_16, noisy, NULL );
|
||||
is16 = TRUE;
|
||||
break;
|
||||
default:
|
||||
if (noisy) DEBUG_Printf("Bad segment '%x'\n", ss);
|
||||
return;
|
||||
}
|
||||
|
||||
/* cur_switch holds address of curr_stack's field in TEB in debuggee
|
||||
* address space
|
||||
/*
|
||||
* If we don't have a valid backtrace, then just return.
|
||||
*/
|
||||
cur_switch = (DWORD)thread->teb + OFFSET_OF(TEB, cur_stack);
|
||||
if (!DEBUG_READ_MEM((void*)cur_switch, &next_switch, sizeof(next_switch))) {
|
||||
if (noisy) DEBUG_Printf("Can't read TEB:cur_stack\n");
|
||||
return;
|
||||
if (frames == NULL) return FALSE;
|
||||
|
||||
/*
|
||||
* If we don't know what the current function is, then we also have
|
||||
* nothing to report here.
|
||||
*/
|
||||
SymFromAddr(dbg_curr_process->handle, frames[dbg_curr_frame].InstructionOffset,
|
||||
NULL, symbol);
|
||||
if (ihsf) *ihsf = frames[dbg_curr_frame];
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void stack_backtrace(DWORD tid, BOOL noisy)
|
||||
{
|
||||
STACKFRAME sf;
|
||||
CONTEXT ctx;
|
||||
struct dbg_thread* thread;
|
||||
unsigned nf;
|
||||
|
||||
if (tid == dbg_curr_tid)
|
||||
{
|
||||
ctx = dbg_context; /* as StackWalk may modify it... */
|
||||
thread = dbg_curr_thread;
|
||||
if (frames) HeapFree(GetProcessHeap(), 0, frames);
|
||||
frames = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
thread = dbg_get_thread(dbg_curr_process, tid);
|
||||
if (!thread)
|
||||
{
|
||||
dbg_printf("Unknown thread id (0x%08lx) in current process\n", tid);
|
||||
return;
|
||||
}
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_SEGMENTS;
|
||||
|
||||
if (SuspendThread(thread->handle) == -1 ||
|
||||
!GetThreadContext(thread->handle, &ctx))
|
||||
{
|
||||
dbg_printf("Can't get context for thread 0x%lx in current process\n",
|
||||
tid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (is16) {
|
||||
if (!DEBUG_READ_MEM((void*)next_switch, &frame32, sizeof(STACK32FRAME))) {
|
||||
if (noisy) DEBUG_Printf("Bad stack frame 0x%08lx\n",
|
||||
(unsigned long)(STACK32FRAME*)next_switch );
|
||||
return;
|
||||
}
|
||||
cur_switch = (DWORD)frame32.frame16;
|
||||
sw_addr.seg = SELECTOROF(cur_switch);
|
||||
sw_addr.off = OFFSETOF(cur_switch);
|
||||
} else {
|
||||
tmp.seg = SELECTOROF(next_switch);
|
||||
tmp.off = OFFSETOF(next_switch);
|
||||
p = DEBUG_ToLinear(&tmp);
|
||||
nf = 0;
|
||||
memset(&sf, 0, sizeof(sf));
|
||||
memory_get_current_frame(&sf.AddrFrame);
|
||||
memory_get_current_pc(&sf.AddrPC);
|
||||
|
||||
if (!DEBUG_READ_MEM((void*)p, &frame16, sizeof(STACK16FRAME))) {
|
||||
if (noisy) DEBUG_Printf("Bad stack frame 0x%08lx\n",
|
||||
(unsigned long)(STACK16FRAME*)p );
|
||||
return;
|
||||
}
|
||||
cur_switch = (DWORD)frame16.frame32;
|
||||
sw_addr.seg = ss;
|
||||
sw_addr.off = cur_switch;
|
||||
}
|
||||
if (!DEBUG_READ_MEM((void*)DEBUG_ToLinear(&sw_addr), &ch, sizeof(ch))) {
|
||||
sw_addr.seg = (DWORD)-1;
|
||||
sw_addr.off = (DWORD)-1;
|
||||
}
|
||||
|
||||
for (ok = TRUE; ok;) {
|
||||
if ((frames[frameno].ss == sw_addr.seg) &&
|
||||
sw_addr.off && (frames[frameno].ebp >= sw_addr.off))
|
||||
if (noisy) dbg_printf("Backtrace:\n");
|
||||
while (StackWalk(IMAGE_FILE_MACHINE_I386, dbg_curr_process->handle,
|
||||
thread->handle, &sf, &ctx, NULL, SymFunctionTableAccess,
|
||||
SymGetModuleBase, NULL))
|
||||
{
|
||||
if (tid == dbg_curr_tid)
|
||||
{
|
||||
/* 16<->32 switch...
|
||||
* yes, I know this is confusing, it gave me a headache too */
|
||||
if (is16) {
|
||||
frames = dbg_heap_realloc(frames,
|
||||
(nf + 1) * sizeof(IMAGEHLP_STACK_FRAME));
|
||||
|
||||
if (!DEBUG_READ_MEM((void*)next_switch, &frame32, sizeof(STACK32FRAME))) {
|
||||
if (noisy) DEBUG_Printf("Bad stack frame 0x%08lx\n",
|
||||
(unsigned long)(STACK32FRAME*)next_switch );
|
||||
return;
|
||||
}
|
||||
|
||||
code.seg = 0;
|
||||
code.off = frame32.retaddr;
|
||||
|
||||
cs = 0;
|
||||
addr.seg = 0;
|
||||
addr.off = frame32.ebp;
|
||||
DEBUG_ForceFrame( &addr, &code, ++frameno, MODE_32, noisy, NULL );
|
||||
|
||||
next_switch = cur_switch;
|
||||
tmp.seg = SELECTOROF(next_switch);
|
||||
tmp.off = OFFSETOF(next_switch);
|
||||
p = DEBUG_ToLinear(&tmp);
|
||||
|
||||
if (!DEBUG_READ_MEM((void*)p, &frame16, sizeof(STACK16FRAME))) {
|
||||
if (noisy) DEBUG_Printf("Bad stack frame 0x%08lx\n",
|
||||
(unsigned long)(STACK16FRAME*)p );
|
||||
return;
|
||||
}
|
||||
cur_switch = (DWORD)frame16.frame32;
|
||||
sw_addr.seg = 0;
|
||||
sw_addr.off = cur_switch;
|
||||
|
||||
is16 = FALSE;
|
||||
} else {
|
||||
tmp.seg = SELECTOROF(next_switch);
|
||||
tmp.off = OFFSETOF(next_switch);
|
||||
p = DEBUG_ToLinear(&tmp);
|
||||
|
||||
if (!DEBUG_READ_MEM((void*)p, &frame16, sizeof(STACK16FRAME))) {
|
||||
if (noisy) DEBUG_Printf("Bad stack frame 0x%08lx\n",
|
||||
(unsigned long)(STACK16FRAME*)p );
|
||||
return;
|
||||
}
|
||||
|
||||
code.seg = frame16.cs;
|
||||
code.off = frame16.ip;
|
||||
|
||||
cs = frame16.cs;
|
||||
addr.seg = SELECTOROF(next_switch);
|
||||
addr.off = frame16.bp;
|
||||
DEBUG_ForceFrame( &addr, &code, ++frameno, MODE_16, noisy, NULL );
|
||||
|
||||
next_switch = cur_switch;
|
||||
if (!DEBUG_READ_MEM((void*)next_switch, &frame32, sizeof(STACK32FRAME))) {
|
||||
if (noisy) DEBUG_Printf("Bad stack frame 0x%08lx\n",
|
||||
(unsigned long)(STACK32FRAME*)next_switch );
|
||||
return;
|
||||
}
|
||||
cur_switch = (DWORD)frame32.frame16;
|
||||
sw_addr.seg = SELECTOROF(cur_switch);
|
||||
sw_addr.off = OFFSETOF(cur_switch);
|
||||
|
||||
is16 = TRUE;
|
||||
}
|
||||
if (!DEBUG_READ_MEM((void*)DEBUG_ToLinear(&sw_addr), &ch, sizeof(ch))) {
|
||||
sw_addr.seg = (DWORD)-1;
|
||||
sw_addr.off = (DWORD)-1;
|
||||
}
|
||||
} else {
|
||||
/* ordinary stack frame */
|
||||
ok = is16 ? DEBUG_Frame16( thread, &addr, &cs, ++frameno, noisy)
|
||||
: DEBUG_Frame32( &addr, &cs, ++frameno, noisy);
|
||||
}
|
||||
frames[nf].InstructionOffset = (unsigned long)memory_to_linear_addr(&sf.AddrPC);
|
||||
frames[nf].FrameOffset = (unsigned long)memory_to_linear_addr(&sf.AddrFrame);
|
||||
}
|
||||
if (noisy)
|
||||
{
|
||||
dbg_printf("%s%d ",
|
||||
(tid == dbg_curr_tid && nf == dbg_curr_frame ? "=>" : " "),
|
||||
nf + 1);
|
||||
print_addr_and_args(&sf.AddrPC, &sf.AddrFrame);
|
||||
dbg_printf(" (");
|
||||
print_bare_address(&sf.AddrFrame);
|
||||
dbg_printf(")\n");
|
||||
}
|
||||
nf++;
|
||||
}
|
||||
if (noisy) DEBUG_Printf("\n");
|
||||
|
||||
if (tid != DEBUG_CurrTid)
|
||||
if (tid == dbg_curr_tid)
|
||||
{
|
||||
ResumeThread( thread->handle );
|
||||
/* restore stack frame for current thread */
|
||||
if (frames) DBG_free( frames );
|
||||
frames = copy_frames;
|
||||
nframe = copy_nframe;
|
||||
curr_frame = copy_curr_frame;
|
||||
nframe = nf;
|
||||
}
|
||||
else
|
||||
{
|
||||
ResumeThread(thread->handle);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
DEBUG_SetFrame(int newframe)
|
||||
{
|
||||
#ifdef __i386__
|
||||
int rtn = FALSE;
|
||||
|
||||
curr_frame = newframe;
|
||||
|
||||
if( curr_frame >= nframe )
|
||||
{
|
||||
curr_frame = nframe - 1;
|
||||
}
|
||||
|
||||
if( curr_frame < 0 )
|
||||
{
|
||||
curr_frame = 0;
|
||||
}
|
||||
|
||||
if( frames && frames[curr_frame].frame.list.sourcefile != NULL )
|
||||
{
|
||||
DEBUG_List(&frames[curr_frame].frame.list, NULL, 0);
|
||||
}
|
||||
|
||||
rtn = TRUE;
|
||||
return (rtn);
|
||||
#else /* __i386__ */
|
||||
return FALSE;
|
||||
#endif /* __i386__ */
|
||||
}
|
||||
|
||||
int
|
||||
DEBUG_GetCurrentFrame(struct name_hash ** name, unsigned int * eip,
|
||||
unsigned int * ebp)
|
||||
{
|
||||
#ifdef __i386__
|
||||
/*
|
||||
* If we don't have a valid backtrace, then just return.
|
||||
*/
|
||||
if( frames == NULL )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we don't know what the current function is, then we also have
|
||||
* nothing to report here.
|
||||
*/
|
||||
if( frames[curr_frame].frame.sym == NULL )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*name = frames[curr_frame].frame.sym;
|
||||
*eip = frames[curr_frame].eip;
|
||||
*ebp = frames[curr_frame].ebp;
|
||||
|
||||
return TRUE;
|
||||
#else /* __i386__ */
|
||||
return FALSE;
|
||||
#endif /* __i386__ */
|
||||
}
|
||||
|
|
|
@ -0,0 +1,560 @@
|
|||
/*
|
||||
* Generate hash tables for Wine debugger symbols
|
||||
*
|
||||
* Copyright (C) 1993, 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
|
||||
*/
|
||||
|
||||
#define NONAMELESSUNION
|
||||
#define NONAMELESSSTRUCT
|
||||
|
||||
#include "config.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "debugger.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
|
||||
|
||||
static BOOL symbol_get_debug_start(DWORD mod_base, DWORD typeid, DWORD* start)
|
||||
{
|
||||
DWORD count, tag;
|
||||
char buffer[sizeof(TI_FINDCHILDREN_PARAMS) + 256 * sizeof(DWORD)];
|
||||
TI_FINDCHILDREN_PARAMS* fcp = (TI_FINDCHILDREN_PARAMS*)buffer;
|
||||
int i;
|
||||
|
||||
if (!types_get_info(mod_base, typeid, TI_GET_CHILDRENCOUNT, &count)) return FALSE;
|
||||
fcp->Start = 0;
|
||||
while (count)
|
||||
{
|
||||
fcp->Count = min(count, 256);
|
||||
if (types_get_info(mod_base, typeid, TI_FINDCHILDREN, fcp))
|
||||
{
|
||||
for (i = 0; i < min(fcp->Count, count); i++)
|
||||
{
|
||||
types_get_info(mod_base, fcp->ChildId[i], TI_GET_SYMTAG, &tag);
|
||||
if (tag != SymTagFuncDebugStart) continue;
|
||||
return types_get_info(mod_base, fcp->ChildId[i], TI_GET_ADDRESS, start);
|
||||
}
|
||||
count -= min(count, 256);
|
||||
fcp->Start += 256;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
struct sgv_data
|
||||
{
|
||||
#define NUMDBGV 100
|
||||
struct {
|
||||
/* FIXME: NUMDBGV should be made variable */
|
||||
struct dbg_lvalue lvalue;
|
||||
DWORD flags;
|
||||
} syms[NUMDBGV]; /* out : will be filled in with various found symbols */
|
||||
int num; /* out : number of found symbols */
|
||||
int num_thunks; /* out : number of thunks found */
|
||||
const char* name; /* in : name of symbol to look up */
|
||||
const char* filename; /* in (opt): filename where to look up symbol */
|
||||
int lineno; /* in (opt): line number in filename where to look up symbol */
|
||||
unsigned bp_disp : 1, /* in : whether if we take into account func address or func first displayable insn */
|
||||
do_thunks : 1; /* in : whether we return thunks tags */
|
||||
IMAGEHLP_STACK_FRAME ihsf; /* in : frame for local & parameter variables look up */
|
||||
};
|
||||
|
||||
static BOOL CALLBACK sgv_cb(SYMBOL_INFO* sym, ULONG size, void* ctx)
|
||||
{
|
||||
struct sgv_data* sgv = (struct sgv_data*)ctx;
|
||||
DWORD addr;
|
||||
IMAGEHLP_LINE il;
|
||||
unsigned cookie = DLV_TARGET, insp;
|
||||
|
||||
if (sym->Flags & SYMFLAG_REGISTER)
|
||||
{
|
||||
const struct dbg_internal_var* div;
|
||||
|
||||
if (dbg_curr_frame != 0)
|
||||
{
|
||||
dbg_printf(" %s (register): << cannot display, not in correct frame\n",
|
||||
sym->Name);
|
||||
return TRUE;
|
||||
}
|
||||
for (div = dbg_context_vars; div->name && div->val != sym->Register; div++);
|
||||
if (!div->name)
|
||||
{
|
||||
dbg_printf(" %s (register): couldn't find register %lu\n",
|
||||
sym->Name, sym->Register);
|
||||
return TRUE;
|
||||
}
|
||||
addr = (DWORD)div->pval;
|
||||
cookie = DLV_HOST;
|
||||
}
|
||||
else if (sym->Flags & SYMFLAG_FRAMEREL)
|
||||
{
|
||||
ULONG offset;
|
||||
types_get_info(sym->ModBase, sym->TypeIndex, TI_GET_OFFSET, &offset);
|
||||
addr = sgv->ihsf.FrameOffset + offset;
|
||||
}
|
||||
else if (sym->Flags & SYMFLAG_THUNK)
|
||||
{
|
||||
if (!sgv->do_thunks) return TRUE;
|
||||
sgv->num_thunks++;
|
||||
addr = sym->Address;
|
||||
}
|
||||
else
|
||||
{
|
||||
il.SizeOfStruct = sizeof(il);
|
||||
SymGetLineFromAddr(dbg_curr_process->handle, sym->Address, NULL, &il);
|
||||
if (sgv->filename && strcmp(sgv->filename, il.FileName))
|
||||
{
|
||||
WINE_FIXME("File name mismatch (%s / %s)\n", sgv->filename, il.FileName);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (sgv->lineno == -1)
|
||||
{
|
||||
if (!sgv->bp_disp ||
|
||||
!symbol_get_debug_start(sym->ModBase, sym->TypeIndex, &addr))
|
||||
addr = sym->Address;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = 0;
|
||||
do
|
||||
{
|
||||
if (sgv->lineno == il.LineNumber)
|
||||
{
|
||||
addr = il.Address;
|
||||
break;
|
||||
}
|
||||
} while (SymGetLineNext(dbg_curr_process->handle, &il));
|
||||
if (!addr)
|
||||
{
|
||||
WINE_FIXME("No line (%d) found for %s (setting to symbol)\n",
|
||||
sgv->lineno, sgv->name);
|
||||
addr = sym->Address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sgv->num >= NUMDBGV)
|
||||
{
|
||||
dbg_printf("Too many addresses for symbol '%s', limiting the first %d\n",
|
||||
sgv->name, NUMDBGV);
|
||||
return FALSE;
|
||||
}
|
||||
WINE_TRACE("==> %s %s%s%s%s%s%s\n",
|
||||
sym->Name,
|
||||
(sym->Flags & SYMFLAG_FUNCTION) ? "func " : "",
|
||||
(sym->Flags & SYMFLAG_FRAMEREL) ? "framerel " : "",
|
||||
(sym->Flags & SYMFLAG_REGISTER) ? "register " : "",
|
||||
(sym->Flags & SYMFLAG_REGREL) ? "regrel " : "",
|
||||
(sym->Flags & SYMFLAG_PARAMETER) ? "param " : "",
|
||||
(sym->Flags & SYMFLAG_THUNK) ? "thunk " : "");
|
||||
|
||||
/* always keep the thunks at end of the array */
|
||||
insp = sgv->num;
|
||||
if (sgv->num_thunks && !(sym->Flags & SYMFLAG_THUNK))
|
||||
{
|
||||
insp -= sgv->num_thunks;
|
||||
memmove(&sgv->syms[insp + 1], &sgv->syms[insp],
|
||||
sizeof(sgv->syms[0]) * sgv->num_thunks);
|
||||
}
|
||||
sgv->syms[insp].lvalue.addr.Mode = AddrModeFlat;
|
||||
sgv->syms[insp].lvalue.addr.Offset = addr;
|
||||
types_get_info(sym->ModBase, sym->TypeIndex, TI_GET_TYPE,
|
||||
&sgv->syms[insp].lvalue.typeid);
|
||||
sgv->syms[insp].lvalue.cookie = cookie;
|
||||
sgv->syms[insp].flags = sym->Flags;
|
||||
sgv->num++;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* symbol_get_lvalue
|
||||
*
|
||||
* Get the address of a named symbol.
|
||||
* Return values:
|
||||
* sglv_found: if the symbol is found
|
||||
* sglv_unknown: if the symbol isn't found
|
||||
* sglv_aborted: some error occurred (likely, many symbols of same name exist,
|
||||
* and user didn't pick one of them)
|
||||
*/
|
||||
enum sym_get_lval symbol_get_lvalue(const char* name, const int lineno,
|
||||
struct dbg_lvalue* rtn, BOOL bp_disp)
|
||||
{
|
||||
struct sgv_data sgv;
|
||||
int i = 0;
|
||||
char tmp[sizeof(SYMBOL_INFO) + 256];
|
||||
SYMBOL_INFO* si = (SYMBOL_INFO*)tmp;
|
||||
char buffer[512];
|
||||
|
||||
if (strlen(name) + 4 > sizeof(buffer))
|
||||
{
|
||||
WINE_WARN("Too long symbol (%s)\n", name);
|
||||
return sglv_unknown;
|
||||
}
|
||||
|
||||
sgv.num = 0;
|
||||
sgv.num_thunks = 0;
|
||||
sgv.name = &buffer[2];
|
||||
sgv.filename = NULL;
|
||||
sgv.lineno = lineno;
|
||||
sgv.bp_disp = bp_disp ? TRUE : FALSE;
|
||||
sgv.do_thunks = DBG_IVAR(AlwaysShowThunks);
|
||||
|
||||
buffer[0] = '*';
|
||||
buffer[1] = '!';
|
||||
strcpy(&buffer[2], name);
|
||||
|
||||
if (!SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv))
|
||||
return sglv_unknown;
|
||||
|
||||
if (!sgv.num && (name[0] != '_'))
|
||||
{
|
||||
buffer[2] = '_';
|
||||
strcpy(&buffer[3], name);
|
||||
if (!SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv))
|
||||
return sglv_unknown;
|
||||
}
|
||||
|
||||
/* now grab local symbols */
|
||||
si->SizeOfStruct = sizeof(*si);
|
||||
si->MaxNameLen = 256;
|
||||
if (stack_get_frame(si, &sgv.ihsf) && sgv.num < NUMDBGV)
|
||||
{
|
||||
if (SymSetContext(dbg_curr_process->handle, &sgv.ihsf, NULL))
|
||||
SymEnumSymbols(dbg_curr_process->handle, 0, name, sgv_cb, (void*)&sgv);
|
||||
}
|
||||
|
||||
if (!sgv.num)
|
||||
{
|
||||
dbg_printf("No symbols found for %s\n", name);
|
||||
return sglv_unknown;
|
||||
}
|
||||
|
||||
if (dbg_interactiveP)
|
||||
{
|
||||
if (sgv.num - sgv.num_thunks > 1 || /* many symbols non thunks (and showing only non thunks) */
|
||||
(sgv.num > 1 && DBG_IVAR(AlwaysShowThunks)) || /* many symbols (showing symbols & thunks) */
|
||||
(sgv.num == sgv.num_thunks && sgv.num_thunks > 1))
|
||||
{
|
||||
dbg_printf("Many symbols with name '%s', "
|
||||
"choose the one you want (<cr> to abort):\n", name);
|
||||
for (i = 0; i < sgv.num; i++)
|
||||
{
|
||||
if (sgv.num - sgv.num_thunks > 1 && (sgv.syms[i].flags & SYMFLAG_THUNK) && !DBG_IVAR(AlwaysShowThunks))
|
||||
continue;
|
||||
dbg_printf("[%d]: ", i + 1);
|
||||
if (sgv.syms[i].flags & SYMFLAG_LOCAL)
|
||||
{
|
||||
dbg_printf("local variable of %s\n", si->Name);
|
||||
}
|
||||
else if (sgv.syms[i].flags & SYMFLAG_PARAMETER)
|
||||
{
|
||||
dbg_printf("parameter of %s\n", si->Name);
|
||||
}
|
||||
else if (sgv.syms[i].flags & SYMFLAG_THUNK)
|
||||
{
|
||||
print_address(&sgv.syms[i].lvalue.addr, TRUE);
|
||||
/* FIXME: should display where the thunks points to */
|
||||
dbg_printf(" thunk %s\n", name);
|
||||
}
|
||||
else
|
||||
{
|
||||
print_address(&sgv.syms[i].lvalue.addr, TRUE);
|
||||
dbg_printf("\n");
|
||||
}
|
||||
}
|
||||
do
|
||||
{
|
||||
i = 0;
|
||||
if (input_read_line("=> ", buffer, sizeof(buffer)))
|
||||
{
|
||||
if (buffer[0] == '\0') return sglv_aborted;
|
||||
i = atoi(buffer);
|
||||
if (i < 1 || i > sgv.num)
|
||||
dbg_printf("Invalid choice %d\n", i);
|
||||
}
|
||||
} while (i < 1 || i > sgv.num);
|
||||
|
||||
/* The array is 0-based, but the choices are 1..n,
|
||||
* so we have to subtract one before returning.
|
||||
*/
|
||||
i--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_printf("More than one symbol named %s, picking the first one\n", name);
|
||||
i = 0;
|
||||
}
|
||||
*rtn = sgv.syms[i].lvalue;
|
||||
return sglv_found;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* symbol_read_symtable
|
||||
*
|
||||
* Read a symbol file into the hash table.
|
||||
*/
|
||||
void symbol_read_symtable(const char* filename, unsigned long offset)
|
||||
{
|
||||
dbg_printf("No longer supported\n");
|
||||
|
||||
#if 0
|
||||
/* FIXME: have to implement SymAddSymbol in dbghelp, but likely to link this with
|
||||
* a loaded module !!
|
||||
*/
|
||||
FILE* symbolfile;
|
||||
unsigned addr;
|
||||
char type;
|
||||
char* cpnt;
|
||||
char buffer[256];
|
||||
char name[256];
|
||||
|
||||
if (!(symbolfile = fopen(filename, "r")))
|
||||
{
|
||||
WINE_WARN("Unable to open symbol table %s\n", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
dbg_printf("Reading symbols from file %s\n", filename);
|
||||
|
||||
while (1)
|
||||
{
|
||||
fgets(buffer, sizeof(buffer), symbolfile);
|
||||
if (feof(symbolfile)) break;
|
||||
|
||||
/* Strip any text after a # sign (i.e. comments) */
|
||||
cpnt = strchr(buffer, '#');
|
||||
if (cpnt) *cpnt = '\0';
|
||||
|
||||
/* Quietly ignore any lines that have just whitespace */
|
||||
for (cpnt = buffer; *cpnt; cpnt++)
|
||||
{
|
||||
if (*cpnt != ' ' && *cpnt != '\t') break;
|
||||
}
|
||||
if (!*cpnt || *cpnt == '\n') continue;
|
||||
|
||||
if (sscanf(buffer, "%lx %c %s", &addr, &type, name) == 3)
|
||||
{
|
||||
if (value.addr.off + offset < value.addr.off)
|
||||
WINE_WARN("Address wrap around\n");
|
||||
value.addr.off += offset;
|
||||
SymAddSymbol(current_process->handle, BaseOfDll,
|
||||
name, addr, 0, 0);
|
||||
}
|
||||
}
|
||||
fclose(symbolfile);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* symbol_get_function_line_status
|
||||
*
|
||||
* Find the symbol nearest to a given address.
|
||||
*/
|
||||
enum dbg_line_status symbol_get_function_line_status(const ADDRESS* addr)
|
||||
{
|
||||
IMAGEHLP_LINE il;
|
||||
DWORD disp, start, size;
|
||||
DWORD lin = (DWORD)memory_to_linear_addr(addr);
|
||||
char buffer[sizeof(SYMBOL_INFO) + 256];
|
||||
SYMBOL_INFO* sym = (SYMBOL_INFO*)buffer;
|
||||
|
||||
il.SizeOfStruct = sizeof(il);
|
||||
sym->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
sym->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
|
||||
|
||||
/* do we have some info for lin address ? */
|
||||
if (!SymFromAddr(dbg_curr_process->handle, lin, NULL, sym))
|
||||
return dbg_no_line_info;
|
||||
|
||||
switch (sym->Tag)
|
||||
{
|
||||
case SymTagThunk:
|
||||
/* FIXME: so far dbghelp doesn't return the 16 <=> 32 thunks
|
||||
* and furthermore, we no longer take care of them !!!
|
||||
*/
|
||||
return dbg_in_a_thunk;
|
||||
case SymTagFunction:
|
||||
case SymTagPublicSymbol: break;
|
||||
default:
|
||||
WINE_FIXME("Unexpected sym-tag 0x%08lx\n", sym->Tag);
|
||||
case SymTagData:
|
||||
return dbg_no_line_info;
|
||||
}
|
||||
/* we should have a function now */
|
||||
if (!SymGetLineFromAddr(dbg_curr_process->handle, lin, &disp, &il))
|
||||
return dbg_no_line_info;
|
||||
|
||||
if (symbol_get_debug_start(sym->ModBase, sym->TypeIndex, &start) && lin < start)
|
||||
return dbg_not_on_a_line_number;
|
||||
if (!types_get_info(sym->ModBase, sym->TypeIndex, TI_GET_LENGTH, &size) || size == 0)
|
||||
size = 0x100000;
|
||||
if (il.FileName && il.FileName[0] && disp < size)
|
||||
return (disp == 0) ? dbg_on_a_line_number : dbg_not_on_a_line_number;
|
||||
|
||||
return dbg_no_line_info;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* symbol_get_line
|
||||
*
|
||||
* Find the symbol nearest to a given address.
|
||||
* Returns sourcefile name and line number in a format that the listing
|
||||
* handler can deal with.
|
||||
*/
|
||||
BOOL symbol_get_line(const char* filename, const char* name, IMAGEHLP_LINE* line)
|
||||
{
|
||||
struct sgv_data sgv;
|
||||
char buffer[512];
|
||||
|
||||
sgv.num = 0;
|
||||
sgv.num_thunks = 0;
|
||||
sgv.name = &buffer[2];
|
||||
sgv.filename = filename;
|
||||
sgv.lineno = -1;
|
||||
sgv.bp_disp = FALSE;
|
||||
sgv.do_thunks = FALSE;
|
||||
|
||||
buffer[0] = '*';
|
||||
buffer[1] = '!';
|
||||
strcpy(&buffer[2], name);
|
||||
|
||||
if (!SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv))
|
||||
return sglv_unknown;
|
||||
|
||||
if (!sgv.num && (name[0] != '_'))
|
||||
{
|
||||
buffer[2] = '_';
|
||||
strcpy(&buffer[3], name);
|
||||
if (!SymEnumSymbols(dbg_curr_process->handle, 0, buffer, sgv_cb, (void*)&sgv))
|
||||
return sglv_unknown;
|
||||
}
|
||||
|
||||
switch (sgv.num)
|
||||
{
|
||||
case 0:
|
||||
if (filename) dbg_printf("No such function %s in %s\n", name, filename);
|
||||
else dbg_printf("No such function %s\n", name);
|
||||
return FALSE;
|
||||
default:
|
||||
WINE_FIXME("Several found, returning first (may not be what you want)...\n");
|
||||
case 1:
|
||||
return SymGetLineFromAddr(dbg_curr_process->handle,
|
||||
(DWORD)memory_to_linear_addr(&sgv.syms[0].lvalue.addr),
|
||||
NULL, line);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK info_locals_cb(SYMBOL_INFO* sym, ULONG size, void* ctx)
|
||||
{
|
||||
DWORD tid;
|
||||
ULONG v, val;
|
||||
const char* explain = NULL;
|
||||
char buf[128];
|
||||
|
||||
dbg_printf("\t");
|
||||
types_get_info(sym->ModBase, sym->TypeIndex, TI_GET_TYPE, &tid);
|
||||
types_print_type(sym->ModBase, tid, FALSE);
|
||||
|
||||
if (sym->Flags & SYMFLAG_LOCAL) explain = "local";
|
||||
else if (sym->Flags & SYMFLAG_PARAMETER) explain = "parameter";
|
||||
else if (sym->Flags & SYMFLAG_REGISTER) explain = buf;
|
||||
|
||||
if (sym->Flags & SYMFLAG_REGISTER)
|
||||
{
|
||||
const struct dbg_internal_var* div;
|
||||
|
||||
if (dbg_curr_frame != 0)
|
||||
{
|
||||
dbg_printf(" %s (register): << cannot display, not in correct frame\n",
|
||||
sym->Name);
|
||||
return TRUE;
|
||||
}
|
||||
for (div = dbg_context_vars; div->name; div++)
|
||||
{
|
||||
if (div->val == sym->Register)
|
||||
{
|
||||
val = *div->pval;
|
||||
sprintf(buf, "local in register %s", div->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sym->Flags & SYMFLAG_FRAMEREL)
|
||||
{
|
||||
types_get_info(sym->ModBase, sym->TypeIndex, TI_GET_OFFSET, &v);
|
||||
v += ((IMAGEHLP_STACK_FRAME*)ctx)->FrameOffset;
|
||||
|
||||
dbg_read_memory_verbose((void*)v, &val, sizeof(val));
|
||||
}
|
||||
dbg_printf(" %s = 0x%8.8lx (%s)\n", sym->Name, val, explain);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int symbol_info_locals(void)
|
||||
{
|
||||
IMAGEHLP_STACK_FRAME ihsf;
|
||||
char buffer[sizeof(SYMBOL_INFO) + 256];
|
||||
SYMBOL_INFO* si = (SYMBOL_INFO*)buffer;
|
||||
|
||||
si->SizeOfStruct = sizeof(*si);
|
||||
si->MaxNameLen = 256;
|
||||
if (stack_get_frame(si, &ihsf))
|
||||
{
|
||||
dbg_printf("%s:\n", si->Name);
|
||||
if (SymSetContext(dbg_curr_process->handle, &ihsf, NULL))
|
||||
SymEnumSymbols(dbg_curr_process->handle, 0, NULL, info_locals_cb, &ihsf);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK symbols_info_cb(SYMBOL_INFO* sym, ULONG size, void* ctx)
|
||||
{
|
||||
DWORD type;
|
||||
|
||||
dbg_printf("%08lx: %s (", sym->Address, sym->Name);
|
||||
if (sym->TypeIndex != dbg_itype_none && sym->TypeIndex != 0 &&
|
||||
types_get_info(sym->ModBase, sym->TypeIndex, TI_GET_TYPE, &type))
|
||||
{
|
||||
types_print_type(sym->ModBase, type, FALSE);
|
||||
}
|
||||
dbg_printf(")\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void symbol_info(const char* str)
|
||||
{
|
||||
char buffer[512];
|
||||
|
||||
if (strlen(str) + 3 >= sizeof(buffer))
|
||||
{
|
||||
dbg_printf("Symbol too long (%s)\n", str);
|
||||
return;
|
||||
}
|
||||
buffer[0] = '*';
|
||||
buffer[1] = '!';
|
||||
strcpy(&buffer[2], str);
|
||||
SymEnumSymbols(dbg_curr_process->handle, 0, buffer, symbols_info_cb, NULL);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,370 @@
|
|||
.TH WINEDBG 1 "May 2004" "Wine Manpage" "Wine Developers Manual"
|
||||
.SH NAME
|
||||
winedbg \- Wine's debugger
|
||||
.SH SYNOPSIS
|
||||
.BR "winedbg " [ " --auto" " |"
|
||||
.BI "--gdb"
|
||||
.RI "[" " options " "] ] ["
|
||||
.BI "program name"
|
||||
.RI "["
|
||||
.BI "program arguments"
|
||||
.RI "] |"
|
||||
.BI "pid"
|
||||
.RI "]"
|
||||
.SH DESCRIPTION
|
||||
.B winedbg
|
||||
is a debugger for Wine. It allows:
|
||||
.RS 4
|
||||
+ debugging native Win32 applications
|
||||
.nf
|
||||
+ debugging Winelib applications.
|
||||
.nf
|
||||
+ being a drop-in replacement for Dr Watson
|
||||
.RE
|
||||
.PP
|
||||
|
||||
.SH MODES
|
||||
\fBwinedbg\fR can be used in three modes. The first argument to the
|
||||
program determines the mode winedbg will run in.
|
||||
.IP \fBdefault\fR
|
||||
Without any explicit mode, this is standard \fBwinedbg\fR operating
|
||||
mode. \fBwinedbg\fR will act as the front end for the user.
|
||||
.IP \fB--auto\fR
|
||||
This mode is used when \fBwinedbg\fR is setup in \fIAeDebug\fR
|
||||
registry entry as the default debugger. \fBwinedbg will then will
|
||||
display basic information about a\fR crash. This is usefull for users
|
||||
who don't want to debug a crash, but rather gather relevant
|
||||
information about the crash to be sent to developers.
|
||||
.IP \fB--gdb\fR
|
||||
\fBwinedbg\fR will be used as a proxy for \fBgdb\fR. \fBgdb\fR will be
|
||||
the front end for command handling, and \fBwinedbg\fR will proxy all
|
||||
debugging requests from \fBgdb\fR to the Win32 APIs.
|
||||
|
||||
.SH OPTIONS
|
||||
Only the \fBgdb\fR proxy mode allows some options:
|
||||
.PP
|
||||
.IP \fI--no-start\fR \fBgdb\fR will not be automatically
|
||||
started. Relevant information for starting \fBgdb\fR are printed on
|
||||
screen. This is somehow usefull when not directly using \fBgdb\fR but
|
||||
some graphical front-ends, like \fBddd\fR or \fBkgbd\fR.
|
||||
.IP \fI--with-xterm\fR
|
||||
This will run \fBgdb\fR in its own xterm instead of using the current
|
||||
Unix console for textual display.
|
||||
.PP
|
||||
The rest of the command line, when passed, is used to identify which
|
||||
programs, if any, has to debugged:
|
||||
.IP \fBprogram\ name\fR
|
||||
This is the name of an executable to start for a debugging
|
||||
session. \fBwinedbg\fR will actually create a process with this
|
||||
executable. If \fBprograms arguments\fR are also given, they will be
|
||||
used as arguments for creating the process to be debugged.
|
||||
.IP \fBpid\fR
|
||||
\fBgdb\fR will attach to the process which pid is \fBpid\fR (pids
|
||||
refer to Win32 pids, not Unix pids). Use the \fIinfo proc\fR
|
||||
\fBwinedbg\fR command to list running processes and their Win32 pids.
|
||||
.IP \fBdefaut\fR
|
||||
If nothing is specified, you will enter the debugger without any run
|
||||
nor attached process. You'll have to do the job yourself.
|
||||
|
||||
.SH COMMANDS
|
||||
.SS Default mode:
|
||||
.PP
|
||||
Most of commands used in \fBwinedbg\fR are similar to the ones from
|
||||
\fBgdb\fR. Please refer to the \fBgdb\fR documentations for some more
|
||||
details. See the \fIgdb\ differences\fR section later on to get a list
|
||||
of variations from \fBgdb\fR commands.
|
||||
.PP
|
||||
\fIMisc. commands\fR
|
||||
.IP \fBabort\fR
|
||||
Aborts the debugger.
|
||||
.IP \fBquit\fR
|
||||
Exits the debugger.
|
||||
.IP \fBattach\ N\fR
|
||||
Attach to a Wine-process (\fBN\fR is its ID, numeric or hexadecimal).
|
||||
IDs can be obtained using the \fBinfo\ process\fR command. Note the
|
||||
\fBinfo\ process\fR command returns hexadecimal values
|
||||
.IP
|
||||
.IP \fBdetach\fR
|
||||
Detach from a Wine-process.
|
||||
.PP
|
||||
\fIHelp commands\fR
|
||||
.IP \fBhelp\fR
|
||||
Prints some help on the commands.
|
||||
.IP \fBhelp\ info\fR
|
||||
Prints some help on info commands
|
||||
.PP
|
||||
\fIFlow control commands\fR
|
||||
.IP \fBcont\fR
|
||||
Continue execution until next breakpoint or exception.
|
||||
.IP \fBpass\fR
|
||||
Pass the exception event up to the filter chain.
|
||||
.IP \fBstep\fR
|
||||
Continue execution until next C line of code (enters function call)
|
||||
.IP \fBnext\fR
|
||||
Continue execution until next C line of code (doesn't enter function
|
||||
call)
|
||||
.IP \fBstepi\fR
|
||||
Execute next assembly instruction (enters function call)
|
||||
.IP \fBnexti\fR
|
||||
Execute next assembly instruction (doesn't enter function call)
|
||||
.IP \fBfinish\fR
|
||||
Excute until return of current function is reached.
|
||||
.PP
|
||||
\fBcont\fR, \fBstep\fR, \fBnext\fR, \fBstepi\fR, \fBnexti\fR can be
|
||||
postfixed by a number (N), meaning that the command must be executed N
|
||||
times before control is returned to the user.
|
||||
.PP
|
||||
\fIBreakpoints, watchpoints
|
||||
.IP \fBenable\ N\fR
|
||||
Enables (break|watch)-point #\fBN\fR
|
||||
.IP \fBdisable\fR
|
||||
Disables (break|watch)-point \fB#N\fR
|
||||
.IP \fBdelete\fR
|
||||
Deletes (break|watch)-point #\fBN\fR
|
||||
.IP \fBcond\ N\fR
|
||||
Removes any existing condition to (break|watch)-point \fBN\fR
|
||||
.IP \fBcond\ N\ <expr>\fR
|
||||
Adds condition \fB<expr>\fR to (break|watch)-point
|
||||
#\fBN\fR. \fB<expr>\fR will be evaluated each time the
|
||||
(break|watch)-point is hit. If the result is a zero value, the
|
||||
breakpoint isn't triggered.
|
||||
.IP \fBbreak\ *\ N\fR
|
||||
Adds a breakpoint at address \fBN\fR
|
||||
.IP \fBbreak\ <id>\fR
|
||||
Adds a breakpoint at the address of symbol \fB<id>\fR
|
||||
.IP \fBbreak <id> N\fR
|
||||
Adds a breakpoint at the line \fBN\fR inside symbol \fB<id>\fR.
|
||||
.IP \fBbreak\ N\fR
|
||||
Adds a breakpoint at line \fBN\fR of current source file.
|
||||
.IP \fBbreak\fR
|
||||
Adds a breakpoint at current \f$PC\fR address.
|
||||
.IP \fBwatch\ *\ N\fR
|
||||
Adds a watch command (on write) at address \fBN\fR (on 4 bytes).
|
||||
.IP \fBwatch\ <id>\fR
|
||||
Adds a watch command (on write) at the address of symbol
|
||||
\fB<id>\fR. Size depends on size of \fB<id>\fR.
|
||||
.IP \fBinfo\ break\fR
|
||||
Lists all (break|watch)-points (with their state).
|
||||
.PP
|
||||
You can use the symbol \fBEntryPoint\fR to stand for the entry point of the Dll.
|
||||
.PP
|
||||
When setting a (break|watch)-point by \fB<id>\fR, if the symbol cannot
|
||||
be found (for example, the symbol is contained in a not yet loaded
|
||||
module), \fBwinedbg\fR will recall the name of the symbol and will try
|
||||
to set the breakpoint each time a new module is loaded (until it succeeds).
|
||||
.PP
|
||||
\fIStack manipulation\fR
|
||||
.IP \fBbt\fR
|
||||
Print calling stack of current thread.
|
||||
.IP \fBbt\ N\fR
|
||||
Print calling stack of thread of ID \fBN\fR. Note: this doesn't change
|
||||
the position of the current frame as manipulated by the \fBup\fR &
|
||||
\fBdn\fR commands).
|
||||
.IP \fBup\fR
|
||||
Goes up one frame in current thread's stack
|
||||
.IP \fBup\ N\fR
|
||||
Goes up \fBN\fR frames in current thread's stack
|
||||
.IP \fBdn\fR
|
||||
Goes down one frame in current thread's stack
|
||||
.IP \fBdn\ N\fR
|
||||
Goes down \fBN\fR frames in current thread's stack
|
||||
.IP \fBframe N\fR
|
||||
Sets \fBN\fR as the current frame for current thread's stack.
|
||||
.IP \fBinfo\ locals\fR
|
||||
Prints information on local variables for current function frame.
|
||||
.PP
|
||||
\fIDirectory & source file manipulation\fR
|
||||
.IP \fBshow\ dir\fR
|
||||
Prints the list of dir:s where source files are looked for.
|
||||
.IP \fBdir\ <pathname>\fR
|
||||
Adds \fB<pathname>\fR to the list of dir:s where to look for source
|
||||
files
|
||||
.IP \fBdir\fR
|
||||
Deletes the list of dir:s where to look for source files
|
||||
.IP \fBsymbolfile\ <pathname>\fR
|
||||
Loads external symbol definition symbolfile \fB<pathname>\fR
|
||||
.IP \fBsymbolfile\ <pathname>\ N\fR
|
||||
Loads external symbol definition symbolfile \fB<pathname>\fR (applying
|
||||
an offset of \fBN\fR to addresses)
|
||||
.IP \fBlist\fR
|
||||
Lists 10 source lines forwards from current position.
|
||||
.IP \fBlist\ -\fR
|
||||
Lists 10 source lines backwards from current position
|
||||
.IP \fBlist\ N\fR
|
||||
Lists 10 source lines from line #\fBN\fR in current file
|
||||
.IP \fBlist\ <pathname>:N\fR
|
||||
Lists 10 source lines from line #\fBN\fR in file \fB<pathname>\fR
|
||||
.IP \fBlist\ <id>\fR
|
||||
Lists 10 source lines of function \fB<id>\fR
|
||||
.IP \fBlist\ *\ N\fR
|
||||
Lists 10 source lines from address \fBN\fR
|
||||
.PP
|
||||
You can specify the end target (to change the 10 lines value) using
|
||||
the ',' separator. For example:
|
||||
.nf
|
||||
.IP \fBlist\ 123,\ 234\fR
|
||||
lists source lines from line 123 up to line 234 in current file
|
||||
.nf
|
||||
.IP \fBlist\ foo.c:1,56\fR
|
||||
lists source lines from line 1 up to 56 in file foo.c
|
||||
.PP
|
||||
\fIDisplaying\fR
|
||||
.PP
|
||||
A display is an expression that's evaluated and printed after the
|
||||
execution of any \fBwinedbg\fR's command.
|
||||
.IP \fBdisplay\fR
|
||||
.IP \fBinfo\ display\fR
|
||||
Lists the active displays
|
||||
.IP \fBdisplay\ <expr>\fR
|
||||
Adds a display for expression \f<expr>\fR
|
||||
.IP \fBdisplay\ /fmt\ <expr>\fR
|
||||
Adds a display for expression \fB<expr>\fR. Printing evaluated
|
||||
\fB<expr>\fR is done using the given format (see \fBprint\ command\fR
|
||||
for more on formats)
|
||||
.IP \fBdel\ display\ N\fR
|
||||
.IP \fBundisplay\ N\fR
|
||||
Deletes display #\fBN\fR
|
||||
.PP
|
||||
\fIDisassembly\fR
|
||||
.IP \fBdisas\fR
|
||||
Disassemble from current position
|
||||
.IP \fBdisas\ <expr>\fR
|
||||
Disassemble from address \fB<expr>\fR
|
||||
.IP \fBdisas\ <expr>,<expr>\fR
|
||||
Disassembles code between addresses specified by the two \fB<expr>\fR:s
|
||||
.PP
|
||||
\fIMemory\ (reading,\ writing,\ typing)\fR
|
||||
.IP \fBx\ <expr>\fR
|
||||
Examines memory at \fB<expr>\fR address
|
||||
.IP \fBx\ /fmt\ <expr>\fR
|
||||
Examines memory at \fB<expr>\fR address using format \fI/fmt\fR
|
||||
.IP \fBprint\ <expr>\fR
|
||||
Prints the value of \fB<expr>\fR (possibly using its type)
|
||||
.IP \fBprint\ /fmt\ <expr>\fR
|
||||
Prints the value of \fB<expr>\fR (possibly using its type)
|
||||
.IP \fBset\ <var>\ =\ <expr>\fR
|
||||
Writes the value of \fB<expr>\fR in \fB<var>\fR variable.
|
||||
.IP \fBwhatis\ <expr>\fR
|
||||
Prints the C type of expression \fB<expr>\fR
|
||||
.PP
|
||||
.IP \fI/fmt\fR
|
||||
is either \fI/<letter>\fR or \fI/<count><letter>\fR. \fI<letter>\fR
|
||||
can be:
|
||||
.RS 4
|
||||
.IP s
|
||||
an ASCII string
|
||||
.IP u
|
||||
an Unicode UTF16 string
|
||||
.IP i
|
||||
instructions (disassemble)
|
||||
.IP x
|
||||
32 bit unsigned hexadecimal integer
|
||||
.IP d
|
||||
32 bit signed decimal integer
|
||||
.IP w
|
||||
16 bit unsigned hexadecimal integer
|
||||
.IP c
|
||||
character (only printable 0x20-0x7f are actually printed)
|
||||
.IP b
|
||||
8 bit unsigned hexadecimal integer
|
||||
.IP g
|
||||
Win32 GUID
|
||||
.RE
|
||||
.PP
|
||||
\fIExpressions\fR
|
||||
.PP
|
||||
Expressions in Wine Debugger are mostly written in a C form. However,
|
||||
there are a few discrepancies:
|
||||
.PP
|
||||
.RS 4
|
||||
Identifiers can take a '!' in their names. This allows mainly to
|
||||
specify a module where to look the module from: \fIUSER32!CreateWindowExA\fR.
|
||||
.PP
|
||||
In cast operation, when specifying a structure or an union, you must
|
||||
use the struct or union key word (even if your program uses a typedef).
|
||||
.RE
|
||||
.PP
|
||||
When specifying an identifier \fB<id>\fR, if several symbols with
|
||||
this name exist, the debugger will prompt for the symbol you want to
|
||||
use. Pick up the one you want from its number.
|
||||
.PP
|
||||
\fIInformation on Wine's internals\fR
|
||||
.IP \fBinfo\ class\fR
|
||||
Lists all Windows' class registered in Wine
|
||||
.IP \fBinfo\ class\ <id>\fR
|
||||
Prints information on Windows's class \fB<id>\fR
|
||||
.IP \fBinfo\ share\fR
|
||||
Lists all the dynamic libraries loaded in the debugged program
|
||||
(including .so files, NE and PE DLLs)
|
||||
.IP \fBinfo\ share\ N\fR
|
||||
Prints information on module at address \fBN\fR
|
||||
.IP \fBinfo\ regs\fR
|
||||
Prints the value of the CPU registers
|
||||
.IP \fBinfo\ segment\fR
|
||||
Lists all allocated segments (i386 only)
|
||||
.IP \fBinfo\ segment N\fR
|
||||
Prints information on segment \fBN\fR (i386 only)
|
||||
.IP \fBinfo\ stack\fR
|
||||
Prints the values on top of the stack
|
||||
.IP \fBinfo\ map\fR
|
||||
Lists all virtual mappings used by the debugged program
|
||||
.IP \fBinfo\ map\ N\fR
|
||||
Lists all virtual mappings used by the program of pid \fBN\fR
|
||||
.IP \fBinfo\ wnd\fR
|
||||
Lists all the window hierarchy starting from the desktop window
|
||||
.IP \fBinfo\ wnd\ N\fR
|
||||
Prints information of Window of handle \fBN\fR
|
||||
.IP \fBinfo\ process\fR
|
||||
Lists all w-processes in Wine session
|
||||
.IP \fBinfo\ thread\fR
|
||||
Lists all w-threads in Wine session
|
||||
.IP \fBinfo\ exception\fR
|
||||
Lists the exception frames (starting from current stack frame)
|
||||
.PP
|
||||
It is possible to turn on and off Wine's debug messages as you are
|
||||
debugging using the \fBset\fR command.
|
||||
.IP \fBset\ +\ warn\ win\fR
|
||||
Turns on warn on \fB'win'\fR channel
|
||||
.IP \fBset\ +\ win\fR
|
||||
Turns on warn/fixme/err/trace on \fB'win'\fR channel
|
||||
.IP \fBset\ -\ win\fR
|
||||
Turns off warn/fixme/err/trace on \fB'win'\fR channel
|
||||
.IP \fBset\ -\ fixme\fR
|
||||
Turns off the 'fixme' class on all channels
|
||||
.PP
|
||||
.SS Gdb mode:
|
||||
.PP
|
||||
See the \fBgdb\fR documentation for all the \fBgdb\fR commands.
|
||||
.PP
|
||||
However, a few Wine's extension are available, through the
|
||||
\fBmonitor\fR command:
|
||||
.IP \fBmonitor\ wnd\fR
|
||||
Lists all window in the Wine session
|
||||
.IP \fBmonitor proc\fR
|
||||
Lists all processes in the Wine session
|
||||
.IP \fBmonitor mem \fR
|
||||
Displays memory mapping of debugged process
|
||||
.PP
|
||||
.SS Auto mode:
|
||||
.PP
|
||||
Since no user input is possible, no commands are available.
|
||||
|
||||
.SH ENVIRONMENT
|
||||
.IP \fBWINE_GDB\fR
|
||||
When used in \fBgdb\fR proxy mode, \fBWINE_GDB\fR specifies the name
|
||||
(and the path) of the executable to be used for \fBgdb\fR. \fB"gdb"\fR
|
||||
is used by default.
|
||||
.SH FILES
|
||||
No specific files are used (yet).
|
||||
.SH BUGS
|
||||
A lot.
|
||||
.SH AUTHORS
|
||||
The first version was written by Eric Youngdale.
|
||||
.PP
|
||||
See Wine developer's list for the rest of contributors.
|
||||
.SH "SEE ALSO"
|
||||
.BR winedbg "'s README file"
|
||||
.nf
|
||||
The Winelib User Guide
|
||||
.nf
|
||||
The Wine Developers Guide
|
Loading…
Reference in New Issue