Jeff L wrote:
Fixes some things Eric pointed out. Have not tested the far calls as I
don't have anything that generates them.
This patch came about when I was looking at why single stepping seemed
to stuff up after a call. It breaks down the calls for 32 bit mode
calls but not necessarily 16 and not 64 bit calls. It is a fairly messy
area of knowledge and I could do with assistance as to how the 16/32/64
bit modes work.
Change log: Add code to analyse far calls in be_i386_is_func_call
instead of only near calls.
Jeff Latimer
a lot of things still look wrong to me...
- first of all, a lot of code should be factorized
- the callee should really be filled with the way the address of the
call is stored in the insn (for example, you return a modeflat for a
relative call from a 16 bit segment...)
- lots of computation for negative displacements are wrong
- segment is always expressed as an unsigned short (even in ADDRESS
structure), so you shouldn't convert it to an int...
- ...
A+
Index: programs/winedbg/be_i386.c
===================================================================
RCS file: /home/wine/wine/programs/winedbg/be_i386.c,v
retrieving revision 1.6
diff -u -r1.6 be_i386.c
--- programs/winedbg/be_i386.c 21 Mar 2006 19:22:51 -0000 1.6
+++ programs/winedbg/be_i386.c 8 Apr 2006 14:30:45 -0000
@@ -380,28 +380,243 @@
static unsigned be_i386_is_func_call(const void* insn, ADDRESS* callee)
{
+#define f_mod(byte) ((byte)>>6)
+#define f_reg(byte) (((byte)>>3)&0x7)
+#define f_rm(byte) ((byte)&0x7)
BYTE ch;
- int delta;
+ BYTE mod, reg, rm;
+ BYTE delta8;
+ short delta16;
+ int delta = 0;
+ int segment = 0;
+ unsigned short segment16;
if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
switch (ch)
{
- case 0xe8:
- dbg_read_memory((const char*)insn + 1, &delta, sizeof(delta));
-
+ case 0xe8: /* Call near, relative to next
instruction */
+ callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context, dbg_context.SegCs);
+ if (callee->Mode == AddrMode1616) {
+ if (!dbg_read_memory((const char*)insn + 1, &delta16,
sizeof(delta16)))
+ return FALSE;
+ delta = delta16; /* Align to 32 bits */
+ delta &= 0xffff;
in this case, the callee should be kept as AddrMode1616, not in flat mode.
you shouldn't either crop delta to a 16 bit value, especially it's wrong
when delta is negative
+ } else {
+ if (!dbg_read_memory((const char*)insn + 1, &delta, sizeof(delta)))
+ return FALSE;
+ }
callee->Mode = AddrModeFlat;
callee->Offset = (DWORD)insn;
- be_i386_disasm_one_insn(callee, FALSE);
+ be_i386_disasm_one_insn(callee, FALSE);
callee->Offset += delta;
return TRUE;
- case 0xff:
+
+ case 0xff: /* Call far, absolute address */
if (!dbg_read_memory((const char*)insn + 1, &ch, sizeof(ch)))
return FALSE;
- ch &= 0x38;
- if (ch != 0x10 && ch != 0x18) return FALSE;
- /* fall through */
- case 0x9a:
+ rm = f_rm(ch); /* Extract ModR/M byte */
+ reg = f_reg(ch);
+ mod = f_mod(ch);
+ if (reg != 0x02 && reg != 0x03) return FALSE;
+ if (reg == 0x02) { /* Same segment */
+ switch(mod)
+ {
+ case 0x00: /* Register operand */
+ callee->Mode = AddrModeFlat;
+ switch (rm)
+ {
+ case 0x00:
+ delta = dbg_context.Ebx + dbg_context.Esi;
+ break;
+ case 0x01:
+ delta = dbg_context.Ebx + dbg_context.Edi;
+ break;
+ case 0x02:
+ delta = dbg_context.Ebp + dbg_context.Esi;
+ break;
+ case 0x03:
+ delta = dbg_context.Ebp + dbg_context.Edi;
+ break;
+ case 0x04:
+ delta = dbg_context.Esi;
+ break;
+ case 0x05:
+ delta = dbg_context.Edi;
+ break;
+ case 0x06:
+ if (!dbg_read_memory((const char*)insn + 2, &delta,
sizeof(2))) /* disp16 */
+ return FALSE;
this is ugly. it'll work but, it would be cleaner to make it with
reading delta16 (as a short), then cast the short to an int
+ break;
+ case 0x07:
+ delta = dbg_context.Ebx;
+ break;
+ }
+ break;
+ case 0x01: /* Disp8 operand */
+ callee->Mode = AddrModeFlat;
+ if (!dbg_read_memory((const char*)insn + 2, &delta8,
sizeof(delta8)))
+ return FALSE;
+ delta = delta8;
+ switch (rm)
+ {
+ case 0x00:
+ delta += dbg_context.Ebx + dbg_context.Esi;
+ break;
+ case 0x01:
+ delta += dbg_context.Ebx + dbg_context.Edi;
+ break;
+ case 0x02:
+ delta += dbg_context.Ebp + dbg_context.Esi;
+ break;
+ case 0x03:
+ delta += dbg_context.Ebp + dbg_context.Edi;
+ break;
+ case 0x04:
+ delta += dbg_context.Esi;
+ break;
+ case 0x05:
+ delta += dbg_context.Edi;
+ break;
+ case 0x06:
+ delta += dbg_context.Ebp;
+ break;
+ case 0x07:
+ delta += dbg_context.Ebx;
+ break;
+ }
+ break;
+ case 0x02: /* Disp16 or Disp32 bit operand
*/
+ callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context, dbg_context.SegCs);
+ if (callee->Mode == AddrMode1616) {
+ if (!dbg_read_memory((const char*)insn + 2, &delta16,
sizeof(delta16)))
+ return FALSE;
+ delta = delta16; /* Align to 32 bits */
+ delta &= 0xffff;
+ } else {
+ if (!dbg_read_memory((const char*)insn + 2, &delta,
sizeof(delta)))
+ return FALSE;
+ }
+ callee->Mode = AddrModeFlat;
+ switch (rm)
+ {
+ case 0x00:
+ delta += dbg_context.Ebx + dbg_context.Esi;
+ break;
+ case 0x01:
+ delta += dbg_context.Ebx + dbg_context.Edi;
+ break;
+ case 0x02:
+ delta += dbg_context.Ebp + dbg_context.Esi;
+ break;
+ case 0x03:
+ delta += dbg_context.Ebp + dbg_context.Edi;
+ break;
+ case 0x04:
+ delta += dbg_context.Esi;
+ break;
+ case 0x05:
+ delta += dbg_context.Edi;
+ break;
+ case 0x06:
+ delta += dbg_context.Ebp;
+ break;
+ case 0x07:
+ delta += dbg_context.Ebx;
+ break;
+ }
+ break;
+ case 0x03: /* Register operand */
+ callee->Mode = AddrModeFlat;
+ switch (rm)
+ {
+ case 0x00:
+ delta = dbg_context.Eax;
+ break;
+ case 0x01:
+ delta = dbg_context.Ecx;
+ break;
+ case 0x02:
+ delta = dbg_context.Edx;
+ break;
+ case 0x03:
+ delta = dbg_context.Ebx;
+ break;
+ case 0x04:
+ delta = dbg_context.Esp;
+ break;
+ case 0x05:
+ delta = dbg_context.Ebp;
+ break;
+ case 0x06:
+ delta = dbg_context.Esi;
+ break;
+ case 0x07:
+ delta = dbg_context.Edi;
+ break;
+ }
+ break;
+ default: /* Disp32 bit operand */
+ if (!dbg_read_memory((const char*)insn + 2, &delta,
sizeof(delta)))
+ return FALSE;
+ break;
+ }
+
+ callee->Offset = delta;
+
+ return TRUE;
+ }
+ else if (reg == 0x03) /* Indirect Far call into other segment */
+ {
+ far char * faraddr;
you shouldn't need the far here (it brings nothing)
+ /* Extract the far address of the indirect address */
+ if (dbg_read_memory((const char*)insn + 2, &faraddr,
sizeof(faraddr)))
+ return FALSE;
+ /* Extract the far address of the callee */
+ if (dbg_read_memory((const char*)faraddr + sizeof(delta),
&segment16, sizeof(segment16)))
+ return FALSE;
+ segment = segment16;
+ callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context, (int) &segment);
+ if (callee->Mode == AddrMode1616) {
+ if (dbg_read_memory((const char*)faraddr, &delta16,
sizeof(delta16)))
+ return FALSE;
+ delta = delta16; /* Align to 32 bits */
+ delta &= 0xffff;
+ } else {
+ if (dbg_read_memory((const char*)faraddr, &delta,
sizeof(delta)))
+ return FALSE;
+ }
+ callee->Mode = AddrMode1632; /* We have made a 32 address so say
so */
+ callee->Segment = segment;
+ callee->Offset = delta; /* absolute address not an offset */
+
+ return TRUE;
+ }
+
+ WINE_FIXME("Unsupported yet call insn (0x%02x) at %p\n", ch, insn);
+ return FALSE;
+
+ case 0x9a: /* Call far, absolute address in operand */
+ if (dbg_read_memory((const char*)insn + 1 + sizeof(delta), &segment16,
sizeof(segment16)))
+ return FALSE;
+ segment = segment16;
+ callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context, dbg_context.SegCs);
+ if (callee->Mode == AddrMode1616) {
+ if (!dbg_read_memory((const char*)insn + 1, &delta16,
sizeof(delta16)))
+ return FALSE;
+ delta = delta16; /* Align to 32 bits */
+ delta &= 0xffff;
+ } else {
+ if (!dbg_read_memory((const char*)insn + 1, &delta, sizeof(delta)))
+ return FALSE;
+ }
+
+ callee->Mode = AddrMode1632; /* We have made a 32 address so say
so */
+ callee->Segment = segment;
+ callee->Offset = delta; /* absolute address not an offset */
+
+ return TRUE;
+
case 0xCD:
WINE_FIXME("Unsupported yet call insn (0x%02x) at %p\n", ch, insn);
/* fall through */
------------------------------------------------------------------------
--
Eric Pouech