Example #1
0
//--------------------------------------------------------------------------
bool pc_debmod_t::should_stop_appcall(thid_t tid, const debug_event_t *event, ea_t ea)
{
  if ( inherited::should_stop_appcall(tid, event, ea) )
    return true;

  // Check if the current instruction is a "RET" and then dereferences
  // the contents of SP to find the return address. IF it matches, it is
  // time to stop
  regvals_t regs;
  regs.resize(X86_NREGS);
  do
  {
    // Start by reading registers
    if ( dbg_read_registers(tid, X86_RC_GENERAL, regs.begin()) != 1 )
      break;

    // Get the opcodes
    uchar opcode;
    if ( dbg_read_memory((ea_t)regs[X86_REG_IP].ival, &opcode, 1) != 1 )
      break;
    // Check for "RET" and "RET n"
    if ( opcode != 0xC3 && opcode != 0xC2 )
      break;

    // Dereference value at ESP
    ea_t at_sp = BADADDR;
    if ( dbg_read_memory((ea_t)regs[X86_REG_SP].ival, &at_sp, sizeof(at_sp)) != sizeof(at_sp) )
      break;
    return ea == at_sp; // time to stop!
  } while ( false );
  return false;
}
Example #2
0
File: dbggw.c Project: HarryR/sanos
void read_memory(struct drpc_packet *pkt, char *buf)
{
  void *addr;
  int size;
  int rc;

  addr = *(void **) (buf + 8);
  size = *(int *) (buf + 16);

  rc = dbg_read_memory(session, addr, size, buf);
  if (rc < 0) pkt->result = E_FAIL;
  *(int *) (buf + ((size + 3) / 4) * 4) = size;
}
Example #3
0
BOOL memory_disasm_one_insn(ADDRESS64* 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;
}
Example #4
0
//--------------------------------------------------------------------------
// determine the future bpt size and read the original instruction from memory.
// if necessary, the bpt address may be adjusted.
// return the number of read bytes.
// this function is overloaded in arm debugger subclasses.
int debmod_t::read_bpt_orgbytes(ea_t *p_ea, int *p_len, uchar *buf, int bufsize)
{
  int len = *p_len;
  if ( (debugger_flags & DBG_FLAG_CAN_CONT_BPT) == 0 )
  { // we must save the original bytes before adding the bpt
    QASSERT(30017, bufsize >= len);
    if ( dbg_read_memory(*p_ea, buf, len) <= 0 )
      return -1;
  }
  else
  { // if the debuger can itself continue from bpts,
    // orgbytes will not be used by the kernel. however
    // we must return something because non-empty orgbytes mean
    // that a soft bpt is active. we return zeroes in b->orgbytes.
  }
  return len;
}
Example #5
0
/***********************************************************************
 *           get_watched_value
 *
 * Returns the value watched by watch point 'num'.
 */
static	BOOL	get_watched_value(int num, DWORD64* val)
{
    DWORD64     buf[1];

    if (!dbg_read_memory(memory_to_linear_addr(&dbg_curr_process->bp[num].addr),
                         buf, dbg_curr_process->bp[num].w.len + 1))
        return FALSE;

    switch (dbg_curr_process->bp[num].w.len + 1)
    {
    case 8:	*val = *(DWORD64*)buf;	break;
    case 4:	*val = *(DWORD*)buf;	break;
    case 2:	*val = *(WORD*)buf;	break;
    case 1:	*val = *(BYTE*)buf;	break;
    default: RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL);
    }
    return TRUE;
}
Example #6
0
/***********************************************************************
 *           memory_read_value
 *
 * Read a memory value.
 */
BOOL memory_read_value(const struct dbg_lvalue* lvalue, DWORD size, void* result)
{
    BOOL ret = FALSE;

    if (lvalue->cookie == DLV_TARGET)
    {
        void*   linear = memory_to_linear_addr(&lvalue->addr);
        if (!(ret = dbg_read_memory(linear, result, size)))
            memory_report_invalid_addr(linear);
    }
    else
    {
        if (lvalue->addr.Offset)
        {
            memcpy(result, (void*)(DWORD_PTR)lvalue->addr.Offset, size);
            ret = TRUE;
        }
    }
    return TRUE;
}
Example #7
0
/***********************************************************************
 *           break_add_break
 *
 * Add a breakpoint.
 */
BOOL break_add_break(const ADDRESS64* addr, BOOL verbose, BOOL swbp)
{
    int                         num;
    BYTE                        ch;
    struct dbg_breakpoint*      bp = dbg_curr_process->bp;
    int                         type = swbp ? be_xpoint_break : be_xpoint_watch_exec;

    if ((num = find_xpoint(addr, type)) >= 1)
    {
        bp[num].refcount++;
        dbg_printf("Breakpoint %d at ", num);
        print_address(&bp[num].addr, TRUE);
        dbg_printf(" (refcount=%d)\n", bp[num].refcount);
        return TRUE;
    }

    if (!dbg_read_memory(memory_to_linear_addr(addr), &ch, sizeof(ch)))
    {
        if (verbose)
        {
            dbg_printf("Invalid address ");
            print_bare_address(addr);
            dbg_printf(", can't set breakpoint\n");
        }
        return FALSE;
    }

    if ((num = init_xpoint(type, addr)) == -1)
        return FALSE;

    dbg_printf("Breakpoint %d at ", num);
    print_address(&bp[num].addr, TRUE);
    dbg_printf("\n");

    return TRUE;
}
Example #8
0
static DWORD dbg_handle_exception(const EXCEPTION_RECORD* rec, BOOL first_chance)
{
    BOOL                is_debug = FALSE;
    THREADNAME_INFO*    pThreadName;
    struct dbg_thread*  pThread;

    assert(dbg_curr_thread);

    WINE_TRACE("exception=%x first_chance=%c\n",
               rec->ExceptionCode, first_chance ? 'Y' : 'N');

    switch (rec->ExceptionCode)
    {
    case EXCEPTION_BREAKPOINT:
    case EXCEPTION_SINGLE_STEP:
        is_debug = TRUE;
        break;
    case EXCEPTION_NAME_THREAD:
        pThreadName = (THREADNAME_INFO*)(rec->ExceptionInformation);
        if (pThreadName->dwThreadID == -1)
            pThread = dbg_curr_thread;
        else
            pThread = dbg_get_thread(dbg_curr_process, pThreadName->dwThreadID);
        if(!pThread)
        {
            dbg_printf("Thread ID=%04x not in our list of threads -> can't rename\n", pThreadName->dwThreadID);
            return DBG_CONTINUE;
        }
        if (dbg_read_memory(pThreadName->szName, pThread->name, 9))
            dbg_printf("Thread ID=%04x renamed using MS VC6 extension (name==\"%s\")\n",
                       pThread->tid, pThread->name);
        return DBG_CONTINUE;
    }

    if (first_chance && !is_debug && !DBG_IVAR(BreakOnFirstChance) &&
            !(rec->ExceptionFlags & EH_STACK_INVALID))
    {
        /* pass exception to program except for debug exceptions */
        return DBG_EXCEPTION_NOT_HANDLED;
    }

    if (!is_debug)
    {
        /* print some infos */
        dbg_printf("%s: ",
                   first_chance ? "First chance exception" : "Unhandled exception");
        switch (rec->ExceptionCode)
        {
        case EXCEPTION_INT_DIVIDE_BY_ZERO:
            dbg_printf("divide by zero");
            break;
        case EXCEPTION_INT_OVERFLOW:
            dbg_printf("overflow");
            break;
        case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
            dbg_printf("array bounds");
            break;
        case EXCEPTION_ILLEGAL_INSTRUCTION:
            dbg_printf("illegal instruction");
            break;
        case EXCEPTION_STACK_OVERFLOW:
            dbg_printf("stack overflow");
            break;
        case EXCEPTION_PRIV_INSTRUCTION:
            dbg_printf("privileged instruction");
            break;
        case EXCEPTION_ACCESS_VIOLATION:
            if (rec->NumberParameters == 2)
                dbg_printf("page fault on %s access to 0x%08lx",
                           rec->ExceptionInformation[0] == EXCEPTION_WRITE_FAULT ? "write" :
                           rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT ? "execute" : "read",
                           rec->ExceptionInformation[1]);
            else
                dbg_printf("page fault");
            break;
        case EXCEPTION_DATATYPE_MISALIGNMENT:
            dbg_printf("Alignment");
            break;
        case DBG_CONTROL_C:
            dbg_printf("^C");
            break;
        case CONTROL_C_EXIT:
            dbg_printf("^C");
            break;
        case STATUS_POSSIBLE_DEADLOCK:
        {
            ADDRESS64       addr;

            addr.Mode   = AddrModeFlat;
            addr.Offset = rec->ExceptionInformation[0];

            dbg_printf("wait failed on critical section ");
            print_address(&addr, FALSE);
        }
        if (!DBG_IVAR(BreakOnCritSectTimeOut))
        {
            dbg_printf("\n");
            return DBG_EXCEPTION_NOT_HANDLED;
        }
        break;
        case EXCEPTION_WINE_STUB:
        {
            char dll[32], name[64];
            memory_get_string(dbg_curr_process,
                              (void*)rec->ExceptionInformation[0], TRUE, FALSE,
                              dll, sizeof(dll));
            if (HIWORD(rec->ExceptionInformation[1]))
                memory_get_string(dbg_curr_process,
                                  (void*)rec->ExceptionInformation[1], TRUE, FALSE,
                                  name, sizeof(name));
            else
                sprintf( name, "%ld", rec->ExceptionInformation[1] );
            dbg_printf("unimplemented function %s.%s called", dll, name);
        }
        break;
        case EXCEPTION_WINE_ASSERTION:
            dbg_printf("assertion failed");
            break;
        case EXCEPTION_VM86_INTx:
            dbg_printf("interrupt %02lx in vm86 mode", rec->ExceptionInformation[0]);
            break;
        case EXCEPTION_VM86_STI:
            dbg_printf("sti in vm86 mode");
            break;
        case EXCEPTION_VM86_PICRETURN:
            dbg_printf("PIC return in vm86 mode");
            break;
        case EXCEPTION_FLT_DENORMAL_OPERAND:
            dbg_printf("denormal float operand");
            break;
        case EXCEPTION_FLT_DIVIDE_BY_ZERO:
            dbg_printf("divide by zero");
            break;
        case EXCEPTION_FLT_INEXACT_RESULT:
            dbg_printf("inexact float result");
            break;
        case EXCEPTION_FLT_INVALID_OPERATION:
            dbg_printf("invalid float operation");
            break;
        case EXCEPTION_FLT_OVERFLOW:
            dbg_printf("floating point overflow");
            break;
        case EXCEPTION_FLT_UNDERFLOW:
            dbg_printf("floating point underflow");
            break;
        case EXCEPTION_FLT_STACK_CHECK:
            dbg_printf("floating point stack check");
            break;
        case CXX_EXCEPTION:
            if(rec->NumberParameters == 3 && rec->ExceptionInformation[0] == CXX_FRAME_MAGIC)
                dbg_printf("C++ exception(object = 0x%08lx, type = 0x%08lx)",
                           rec->ExceptionInformation[1], rec->ExceptionInformation[2]);
            else
                dbg_printf("C++ exception with strange parameter count %d or magic 0x%08lx",
                           rec->NumberParameters, rec->ExceptionInformation[0]);
            break;
        default:
            dbg_printf("0x%08x", rec->ExceptionCode);
            break;
        }
    }
    if( (rec->ExceptionFlags & EH_STACK_INVALID) ) {
        dbg_printf( ", invalid program stack" );
    }

    if (dbg_exception_prolog(is_debug, first_chance, rec))
    {
        dbg_interactiveP = TRUE;
        return 0;
    }
    dbg_exception_epilog();

    return DBG_CONTINUE;
}
Example #9
0
/***********************************************************************
 *           memory_examine
 *
 * Implementation of the 'x' command.
 */
void memory_examine(const struct dbg_lvalue *lvalue, int count, char format)
{
    int			i;
    char                buffer[256];
    ADDRESS64           addr;
    void               *linear;

    types_extract_as_address(lvalue, &addr);
    linear = memory_to_linear_addr(&addr);

    if (format != 'i' && count > 1)
    {
        print_address(&addr, FALSE);
        dbg_printf(": ");
    }

    switch (format)
    {
    case 'u':
        if (count == 1) count = 256;
        memory_get_string(dbg_curr_process, linear, 
                          TRUE, TRUE, buffer, min(count, sizeof(buffer)));
        dbg_printf("%s\n", buffer);
        return;
    case 's':
        if (count == 1) count = 256;
        memory_get_string(dbg_curr_process, linear,
                          TRUE, FALSE, buffer, min(count, sizeof(buffer)));
        dbg_printf("%s\n", buffer);
        return;
    case 'i':
        while (count-- && memory_disasm_one_insn(&addr));
        return;
    case 'g':
        while (count--)
        {
            GUID guid;
            if (!dbg_read_memory(linear, &guid, sizeof(guid)))
            {
                memory_report_invalid_addr(linear);
                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]);
            linear = (char*)linear + sizeof(guid);
            addr.Offset += sizeof(guid);
            if (count)
            {
                print_address(&addr, FALSE);
                dbg_printf(": ");
            }
        }
        return;

#define DO_DUMP2(_t,_l,_f,_vv) {                                        \
            _t _v;                                                      \
            for (i = 0; i < count; i++) {                               \
                if (!dbg_read_memory(linear, &_v, sizeof(_t)))          \
                { memory_report_invalid_addr(linear); break; }          \
                dbg_printf(_f, (_vv));                                  \
                addr.Offset += sizeof(_t);                              \
                linear = (char*)linear + sizeof(_t);                    \
                if ((i % (_l)) == (_l) - 1 && i != count - 1)           \
                {                                                       \
                    dbg_printf("\n");                                   \
                    print_address(&addr, 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);
    }
}
Example #10
0
//--------------------------------------------------------------------------
int idaapi debmod_t::dbg_update_bpts(
      update_bpt_info_t *bpts,
      int nadd,
      int ndel)
{
  // Write breakpoints to the process
  int cnt = 0;
  update_bpt_info_t *b;
  update_bpt_info_t *end = bpts + nadd;
  for ( b=bpts; b != end; b++ )
  {
    int code = b->code;
    if ( code != BPT_OK )
      continue;
    uchar len;
    char buf[32];
    int nread = 0;
    ea_t ea = b->ea;
    if ( b->type == BPT_SOFT )
    {
      len = bpt_code.size();
#ifdef __ARM__
      if ( (ea & 1) != 0 ) // T bit is set, use a thumb breakpoint
      {
        ea--;
        len = 2;
      }
#endif
      nread = len;
      if ( (debugger_flags & DBG_FLAG_CAN_CONT_BPT) == 0 )
      { // we must save the original bytes before adding the bpt
        QASSERT(30017, sizeof(buf) >= len);
        if ( dbg_read_memory(ea, buf, len) <= 0 )
          code = BPT_READ_ERROR;
      }
    }
    else
    {
      len = b->size;
    }
    if ( code == BPT_OK )
      code = dbg_add_bpt(b->type, ea, len) ? BPT_OK : BPT_WRITE_ERROR;

    b->code = code;
    if ( code == BPT_OK )
    {
      cnt++;
      if ( nread > 0 )
        b->orgbytes = bytevec_t(buf, nread);
    }
  }

  // Delete breakpoints from the process.
  end += ndel;
  for ( ; b != end; b++ )
  {
    b->code = BPT_OK;
    int len = b->type == BPT_SOFT ? b->orgbytes.size() : b->size;
    if ( dbg_del_bpt(b->type, b->ea, b->orgbytes.begin(), len) > 0 )
      cnt++;
    else
      b->code = BPT_WRITE_ERROR;
  }

  return cnt;
}
Example #11
0
static BOOL fill_sym_lvalue(const SYMBOL_INFO* sym, ULONG_PTR base,
                            struct dbg_lvalue* lvalue, char* buffer, size_t sz)
{
    if (buffer) buffer[0] = '\0';
    if (sym->Flags & SYMFLAG_REGISTER)
    {
        DWORD_PTR* pval;

        if (!memory_get_register(sym->Register, &pval, buffer, sz))
            return FALSE;
        lvalue->cookie = DLV_HOST;
        lvalue->addr.Offset = (DWORD_PTR)pval;
    }
    else if (sym->Flags & SYMFLAG_REGREL)
    {
        DWORD_PTR* pval;
        size_t  l;

        *buffer++ = '['; sz--;
        if (!memory_get_register(sym->Register, &pval, buffer, sz))
            return FALSE;
        l = strlen(buffer);
        sz -= l;
        buffer += l;
        lvalue->cookie = DLV_TARGET;
        lvalue->addr.Offset = (ULONG64)*pval + sym->Address;
        if ((LONG_PTR)sym->Address >= 0)
            snprintf(buffer, sz, "+%ld]", (ULONG_PTR)sym->Address);
        else
            snprintf(buffer, sz, "-%ld]", -(LONG_PTR)sym->Address);
    }
    else if (sym->Flags & SYMFLAG_VALUEPRESENT)
    {
        struct dbg_type type;
        VARIANT         v;

        type.module = sym->ModBase;
        type.id = sym->info;

        if (!types_get_info(&type, TI_GET_VALUE, &v))
        {
            if (buffer) snprintf(buffer, sz, "Couldn't get full value information for %s", sym->Name);
            return FALSE;
        }
        else if (v.n1.n2.vt & VT_BYREF)
        {
            /* FIXME: this won't work for pointers or arrays, as we don't always
             * know, if the value to be dereferenced lies in debuggee or
             * debugger address space.
             */
            if (sym->Tag == SymTagPointerType || sym->Tag == SymTagArrayType)
            {
                if (buffer) snprintf(buffer, sz, "Couldn't dereference pointer for const value for %s", sym->Name);
                return FALSE;
            }
            /* this is likely Wine's dbghelp which passes const values by reference
             * (object is managed by dbghelp, hence in debugger address space)
             */
            lvalue->cookie = DLV_HOST;
            lvalue->addr.Offset = (DWORD_PTR)sym->Value;
        }
        else
        {
            DWORD* pdw = (DWORD*)lexeme_alloc_size(sizeof(*pdw));
            lvalue->cookie = DLV_HOST;
            lvalue->addr.Offset = (DWORD_PTR)pdw;
            *pdw = sym->Value;
        }
    }
    else if (sym->Flags & SYMFLAG_LOCAL)
    {
        lvalue->cookie = DLV_TARGET;
        lvalue->addr.Offset = base + sym->Address;
    }
    else if (sym->Flags & SYMFLAG_TLSREL)
    {
        PROCESS_BASIC_INFORMATION pbi;
        THREAD_BASIC_INFORMATION  tbi;
        DWORD_PTR                 addr;
        PEB                       peb;
        PEB_LDR_DATA              ldr_data;
        PLIST_ENTRY               head, current;
        LDR_MODULE                ldr_module;
        unsigned                  tlsindex = -1;

        if (NtQueryInformationProcess(dbg_curr_process->handle, ProcessBasicInformation,
                                      &pbi, sizeof(pbi), NULL) ||
            NtQueryInformationThread(dbg_curr_thread->handle, ThreadBasicInformation,
                                     &tbi, sizeof(tbi), NULL))
        {
        tls_error:
            if (buffer) snprintf(buffer, sz, "Cannot read TLS address\n");
            return FALSE;
        }
        addr = (DWORD_PTR)&(((TEB*)tbi.TebBaseAddress)->ThreadLocalStoragePointer);
        if (!dbg_read_memory((void*)addr, &addr, sizeof(addr)) ||
            !dbg_read_memory(pbi.PebBaseAddress, &peb, sizeof(peb)) ||
            !dbg_read_memory(peb.LdrData, &ldr_data, sizeof(ldr_data)))
            goto tls_error;
        current = ldr_data.InLoadOrderModuleList.Flink;
        head = &((PEB_LDR_DATA*)peb.LdrData)->InLoadOrderModuleList;
        do
        {
            if (!dbg_read_memory(CONTAINING_RECORD(current, LDR_MODULE, InLoadOrderModuleList),
                                 &ldr_module, sizeof(ldr_module))) goto tls_error;
            if ((DWORD_PTR)ldr_module.BaseAddress == sym->ModBase)
            {
                tlsindex = ldr_module.TlsIndex;
                break;
            }
            current = ldr_module.InLoadOrderModuleList.Flink;
        } while (current != head);

        addr += tlsindex * sizeof(DWORD_PTR);
        if (!dbg_read_memory((void*)addr, &addr, sizeof(addr))) goto tls_error;
        lvalue->cookie = DLV_TARGET;
        lvalue->addr.Offset = addr + sym->Address;
    }
    else
    {
        lvalue->cookie = DLV_TARGET;
        lvalue->addr.Offset = sym->Address;
    }
    lvalue->addr.Mode = AddrModeFlat;
    lvalue->type.module = sym->ModBase;
    lvalue->type.id = sym->TypeIndex;

    return TRUE;
}
Example #12
0
/***********************************************************************
 *           break_restart_execution
 *
 * Set the bp to the correct state to restart execution
 * in the given mode.
 */
void break_restart_execution(int count)
{
    ADDRESS64                   addr;
    enum dbg_line_status        status;
    enum dbg_exec_mode          mode, ret_mode;
    ADDRESS64                   callee;
    void*                       linear;

    memory_get_current_pc(&addr);
    linear = memory_to_linear_addr(&addr);

    /*
     * This is the mode we will be running in after we finish.  We would like
     * to be able to modify this in certain cases.
     */
    ret_mode = mode = dbg_curr_thread->exec_mode;

    /* we've stopped on a xpoint (other than step over) */
    if (dbg_curr_thread->stopped_xpoint > 0)
    {
	/*
	 * If we have set a new value, then save it in the BP number.
	 */
	if (count != 0 && mode == dbg_exec_cont)
        {
	    dbg_curr_process->bp[dbg_curr_thread->stopped_xpoint].skipcount = count;
        }
        /* If we've stopped on a breakpoint, single step over it (, then run) */
        if (is_xpoint_break(dbg_curr_thread->stopped_xpoint))
            mode = dbg_exec_step_into_insn;
    }
    else if (mode == dbg_exec_cont && count > 1)
    {
        dbg_printf("Not stopped at any breakpoint; argument ignored.\n");
    }

    if (mode == dbg_exec_finish && be_cpu->is_function_return(linear))
    {
	mode = ret_mode = dbg_exec_step_into_insn;
    }

    /*
     * See if the function we are stepping into has debug info
     * and line numbers.  If not, then we step over it instead.
     * FIXME - we need to check for things like thunks or trampolines,
     * as the actual function may in fact have debug info.
     */
    if (be_cpu->is_function_call(linear, &callee))
    {
	status = symbol_get_function_line_status(&callee);
#if 0
        /* FIXME: we need to get the thunk type */
	/*
	 * Anytime we have a trampoline, step over it.
	 */
	if ((mode == EXEC_STEP_OVER || mode == EXEC_STEPI_OVER)
	    && status == dbg_in_a_thunk)
        {
            WINE_WARN("Not stepping into trampoline at %p (no lines)\n", 
                      memory_to_linear_addr(&callee));
	    mode = EXEC_STEP_OVER_TRAMPOLINE;
        }
#endif
	if (mode == dbg_exec_step_into_line && status == dbg_no_line_info)
        {
            WINE_WARN("Not stepping into function at %p (no lines)\n",
                      memory_to_linear_addr(&callee));
	    mode = dbg_exec_step_over_line;
        }
    }

    if (mode == dbg_exec_step_into_line && 
        symbol_get_function_line_status(&addr) == dbg_no_line_info)
    {
        dbg_printf("Single stepping until exit from function,\n"
                   "which has no line number information.\n");
        ret_mode = mode = dbg_exec_finish;
    }

    switch (mode)
    {
    case dbg_exec_cont: /* Continuous execution */
        be_cpu->single_step(&dbg_context, FALSE);
        break_set_xpoints(TRUE);
        break;

#if 0
    case EXEC_STEP_OVER_TRAMPOLINE:
        /*
         * This is the means by which we step over our conversion stubs
         * in callfrom*.s and callto*.s.  We dig the appropriate address
         * off the stack, and we set the breakpoint there instead of the
         * address just after the call.
         */
        be_cpu->get_addr(dbg_curr_thread->handle, &dbg_context,
                         be_cpu_addr_stack, &addr);
        /* FIXME: we assume stack grows as on an i386 */
        addr.Offset += 2 * sizeof(unsigned int);
        dbg_read_memory(memory_to_linear_addr(&addr),
                        &addr.Offset, sizeof(addr.Offset));
        dbg_curr_process->bp[0].addr = addr;
        dbg_curr_process->bp[0].enabled = TRUE;
        dbg_curr_process->bp[0].refcount = 1;
        dbg_curr_process->bp[0].skipcount = 0;
        dbg_curr_process->bp[0].xpoint_type = be_xpoint_break;
        dbg_curr_process->bp[0].condition = NULL;
        be_cpu->single_step(&dbg_context, FALSE);
        break_set_xpoints(TRUE);
        break;
#endif

    case dbg_exec_finish:
    case dbg_exec_step_over_insn:  /* Stepping over a call */
    case dbg_exec_step_over_line:  /* Stepping over a call */
        if (be_cpu->is_step_over_insn(linear))
        {
            be_cpu->disasm_one_insn(&addr, FALSE);
            dbg_curr_process->bp[0].addr = addr;
            dbg_curr_process->bp[0].enabled = TRUE;
            dbg_curr_process->bp[0].refcount = 1;
	    dbg_curr_process->bp[0].skipcount = 0;
            dbg_curr_process->bp[0].xpoint_type = be_xpoint_break;
            dbg_curr_process->bp[0].condition = NULL;
            be_cpu->single_step(&dbg_context, FALSE);
            break_set_xpoints(TRUE);
            break;
        }
        /* else fall through to single-stepping */

    case dbg_exec_step_into_line: /* Single-stepping a line */
    case dbg_exec_step_into_insn: /* Single-stepping an instruction */
        be_cpu->single_step(&dbg_context, TRUE);
        break;
    default: RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL);
    }
    dbg_curr_thread->step_over_bp = dbg_curr_process->bp[0];
    dbg_curr_thread->exec_mode = ret_mode;
}