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 */