/*++ Copyright (c) 1993 Microsoft Corporation Module Name: Osdebug.c Abstract: OSDebug version 4 API Author: Kent D. Forschmiedt (kentf) Environment: Win32, User Mode --*/ #include #include #include #include "cvinfo.h" #include "odtypes.h" #include "od.h" #include "dbgver.h" #include "odp.h" #include "odassert.h" #define WINDBG_POINTERS_MACROS_ONLY #include "sundown.h" #undef WINDBG_POINTERS_MACROS_ONLY #define CEXM_MDL_native 0x20 // debugger services vector static LPDBF lpdbf; // List roots // Processes static HLLI llpid; // EMs static HLLI llem; // TLs static HLLI lltl; #ifdef __cplusplus extern "C" { #endif extern AVS Avs; XOSD OSDDoCallBack ( DBC dbc, HPID hpid, HTID htid, DWORD dwModel, DWORD64 lParam1, DWORD64 lParam2 ); XOSD OSDDoCallBackToEM ( DBC emf, HPID hpid, HTID htid, DWORD64 lParam1, DWORD64 lParam2 ); XOSD CallEM ( EMF emf, HPID hpid, HTID htid, DWORD64 lParam, DWORD64 lpv ); XOSD CallTL ( TLF tlf, HPID hpid, DWORD64 lParam, DWORD64 lpv ); XOSD EMCallBackDB ( DBC dbc, HPID hpid, HTID htid, DWORD dwModel, DWORD64 cb, DWORD64 lpv ); XOSD EMCallBackTL ( TLF tlf, HPID hpid, DWORD64 cb, DWORD64 lpv ); XOSD EMCallBackNT ( EMF emf, HPID hpid, HTID htid, DWORD64 cb, DWORD64 lpv ); XOSD EMCallBackEM ( EMF emf, HPID hpid, HTID htid, DWORD dwModel, DWORD64 cb, DWORD64 lpv ); XOSD TLCallBack ( HPID hpid, DWORD64 lParam, DWORD64 lpv ); XOSD CreateProc ( LPFNSVC lpfnsvc, HEM hem, HTL htl, LPHPID lphpid ); XOSD CreateThd ( HPID hpid, HTID htid, LPHTID lphtid ); static EMCB emcb = { EMCallBackDB, EMCallBackTL, EMCallBackNT, EMCallBackEM }; static CRITICAL_SECTION CallbackCriticalSection; #define CheckErr(xosd) if ( xosd != xosdNone ) return xosd /* OSDebug Controller Initialization / Termination */ XOSD OSDAPI OSDInit ( LPDBF lpdbfT ) /*++ Routine Description: Initialize the internal structures for osdebug. Register the debugger services callback vector. Use list manager to create the three global lists used by osdebug. These are the list of processes ( llpid ), the list of transport layers ( lltl ), and the list of execution models ( llem ). Registering the debugger services with osdebug is just an assignment of a pointer to the function structure. If this function fails, it is catastrophic. No cleanup of partially allocated data is attempted. Arguments: lpdbfT - Supplies the debugger services structure Return Value: xosdNone - Function succeeded xosdOutOfMemory - List manager was unable to allocate room for its root structures. --*/ { XOSD xosd = xosdNone; assert ( lpdbfT != NULL ); InitializeCriticalSection( &CallbackCriticalSection ); lpdbf = lpdbfT; llpid = LLInit (sizeof ( PROCESSINFO ), llfNull, ODPDKill, (LPFNFCMPNODE) NULL ); lltl = LLInit ( sizeof ( TLS ), llfNull, TLKill, (LPFNFCMPNODE) NULL ); llem = LLInit ( sizeof ( EMS ), llfNull, EMKill, (LPFNFCMPNODE) NULL ); if ( llpid == 0 || lltl == 0 || llem == 0 ) { xosd = xosdOutOfMemory; } return xosd; } XOSD OSDAPI OSDTerm( VOID ) /*++ Routine Description: Deallocate resources used by OSD. At present, this only destroys critical sections used by osdebug. Arguments: None Return Value: xosd - always xosdNone --*/ { DeleteCriticalSection( &CallbackCriticalSection ); return xosdNone; } /* Execution Model loading, unloading and configuration */ XOSD OSDAPI OSDAddEM ( EMFUNC emfunc, LPDBF lpdbf, LPHEM lphem, EMTYPE emtype ) /*++ Routine Description: Create and initialize an execution model associated with the service function EMFunc and add it to the list of osdebug's available execution models ( llem ). Use list manager to create an execution model handle and add it to the execution model list ( llem ). Call the execution model service function ( lpfnsvcEM ) to initialize the execution model and to register the debugger service functions ( lpdbf ). Arguments: emfunc - Supplies a pointer to the execution model service function to be associated with the execution model that is being created. lpdbf - Supplies a pointer to the debugger services structure that will be registered with the execution model being created. lphem - Returns the execution model handle. emtype - Supplies the type of EM; emNative or emNonNative. Return Value: xosdNone - Success. xosdOutOfMemory - List manager was unable to allocate em or add it to the execution model list ( llem ). Other xosd failure codes may be returned from the new EM while it is being initialized. --*/ { HEM hem; HEM hemm = 0; LPEMS lpem; LPEMS lpemm; XOSD xosd = xosdNone; DWORD dwModel; assert ( emfunc != NULL ); assert ( lpdbf != NULL ); assert ( lphem != NULL ); hem = (HEM)LLCreate ( llem ); if ( !hem ) { return xosdOutOfMemory; } if ( emtype == emNative ) { // native inserted at the tail of the list LLAdd ( llem, (HLLE)hem ); } else { // non-native inserted at head of list LLAddHead ( llem, (HLLE)hem ); } lpem = (LPEMS) LLLock ( (HLLE)hem ); lpem->emfunc = (EMFUNC_ODP) emfunc; lpem->emtype = emtype; lpem->llhpid = LLInit ( sizeof ( HPID ), llfNull, NULL, EMHpidCmp ); if ( lpem->llhpid == 0 ) { xosd = xosdOutOfMemory; } xosd = (*lpem->emfunc) ( emfRegisterDBF, NULL, NULL, 0, (DWORD64) lpdbf ); CheckErr ( xosd ); xosd = (*lpem->emfunc) ( emfInit, NULL, NULL, 0, (DWORD64)&emcb ); CheckErr ( xosd ); xosd = (*lpem->emfunc) ( emfGetModel, NULL, NULL, 0, (DWORD64)&dwModel ); CheckErr ( xosd ); while ( hemm = (HEM)LLNext ( llem, (HLLE)hemm ) ) { lpemm = (LPEMS) LLLock ( (HLLE)hemm ); if ( lpemm->model == dwModel ) { // this is an error, cannot add the same model twice LLUnlock ( (HLLE)hemm ); LLUnlock ( (HLLE)hem ); return xosdDuplicate; } LLUnlock ( (HLLE)hemm ); } lpem->model = dwModel; LLUnlock ( (HLLE)hem ); *lphem = hem; return xosd; } XOSD OSDAPI OSDDeleteEM ( HEM hem ) /*++ Routine Description: Remove the execution model (hem) from os debug's list of available execution models ( llem ). Check the list of pids using this execution model. If it is zero indicating that no pids are using it, then call the list manager to delete it from the list of available execution models ( llem ). Arguments: hem - Supplies handle to EM which is being removed Return Value: xosdNone - Success. xosdInvalidEM - The execution model handle ( hem ) is invalid. xosdInUse - The execution model is still being used by some pid. OSDDiscardEM must be called on all of the pids using this particular em before OSDDeleteEM can be called without error. --*/ { XOSD xosd = xosdNone; LPEMS lpem; assert ( hem != NULL ); lpem = (LPEMS) LLLock ( (HLLE)hem ); if ( LLSize ( lpem->llhpid ) != 0 ) { LLUnlock ( (HLLE)hem ); xosd = xosdInUse; } else { // Tell the EM and DM that they're about to be discarded (lpem->emfunc) ( emfUnInit, NULL, NULL, 0, 0 ); LLUnlock ( (HLLE)hem ); if ( !LLDelete ( llem, (HLLE)hem ) ) { xosd = xosdInvalidParameter; } } return xosd; } XOSD OSDAPI OSDEMGetInfo( HEM hem, LPEMIS lpemis ) /*++ Routine Description: Arguments: Return Value: --*/ { XOSD xosd = xosdNone; LPEMS lpem; assert ( hem != NULL ); lpem = (LPEMS) LLLock ( (HLLE)hem ); xosd = (lpem->emfunc) ( emfGetInfo, NULL, NULL, 0, (DWORD64)lpemis ); LLUnlock( (HLLE)hem ); return xosd; } XOSD OSDAPI OSDEMSetup( HEM hem, LPEMSS lpemss ) /*++ Routine Description: Arguments: Return Value: --*/ { XOSD xosd = xosdNone; LPEMS lpem; assert ( hem != NULL ); lpem = (LPEMS) LLLock ( (HLLE)hem ); xosd = (lpem->emfunc) ( emfSetup, NULL, NULL, 0, (DWORD64)lpemss ); LLUnlock( (HLLE)hem ); return xosd; } /* EM Manipulation */ XOSD OSDAPI OSDGetCurrentEM ( HPID hpid, HTID htid, LPHEM lphem ) /*++ Routine Description: Get the handle for the current execution model associated with hpid:htid. Arguments: hpid - Supplies process htid - Supplies thread lphem - Returns the handle to the execution model. Return Value: xosdNone - Success xosdInvalidEM - No valid execution model for this hpid:htid --*/ { LPPROCESSINFO lppid; HEM hodem; LPEMP lpemp; LPEMS lpem; Unreferenced( htid ); assert ( lphem != NULL ); lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); if ( lppid == NULL ) { return xosdInvalidParameter; } if ( lppid->lastmodel == CEXM_MDL_native ) { lpemp = (LPEMP) LLLock ( (HLLE)lppid->hempNative ); *lphem = lpemp->hem; LLUnlock ( (HLLE)lppid->hempNative ); } else { hodem = NULL; while ( hodem = (HEM)LLNext ( llem, (HLLE)hodem ) ) { lpem = (LPEMS) LLLock ( (HLLE)hodem ); if ( lpem->model == lppid->lastmodel ) { *lphem = hodem; } LLUnlock ( (HLLE)hodem ); } } LLUnlock ( (HLLE)hpid ); return xosdNone; } XOSD OSDAPI OSDNativeOnly ( HPID hpid, HTID htid, DWORD fNat ) /*++ Routine Description: Force the use of the native em even where there is non-native code (ie pcode or emulator). Arguments: hpid - Supplies process htid - Supplies thread fNat - Supplies to set native only, false to return to normal mode of handling multiple em's Return Value: xosdNone - Success --*/ { HEMP hemp; HEMP hempTmp = NULL; XOSD xosd = xosdNone; LPEMP lpemp; LPPROCESSINFO lppid; assert ( hpid != NULL ); lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); if ( lppid->fNative == fNat ) { LLUnlock((HLLE)hpid); return xosd; } else if ( fNat ) { // get the native em hemp = lppid->hempNative; // tell all of the non-native models to disconnect, cleanup or // whatever they need to do while ( ( hempTmp = (HEMP)LLNext ( lppid->llemp, (HLLE)hempTmp ) ) && hempTmp != hemp ) { lpemp = (LPEMP) LLLock ( (HLLE)hempTmp ); xosd = (lpemp->emfunc) ( emfDetach, hpid, htid, 0, 0 ); LLUnlock ( (HLLE)hempTmp ); } // we then move the native em to the head of the list, forcing it // to be called first, until fNative is reset LLRemove ( lppid->llemp, (HLLE)hemp ); LLAddHead ( lppid->llemp, (HLLE)hemp ); // if current model used to be non-native, send notification if ( lppid->lastmodel != CEXM_MDL_native ) { OSDDoCallBack ( dbcEmChange, hpid, htid, lppid->lastmodel, CEXM_MDL_native, 0 ); lppid->lastmodel = CEXM_MDL_native; } // finally, set our global flag to true lppid->fNative = fNat; } else { // put the native em back at the end of the list hemp = lppid->hempNative; LLRemove ( lppid->llemp, (HLLE)hemp ); LLAdd ( lppid->llemp, (HLLE)hemp ); // tell all of the non-native models that they can re-connect xosd = xosdPunt; while ( ( hempTmp = (HEMP)LLNext ( lppid->llemp, (HLLE)hempTmp ) ) && hempTmp != hemp && xosd == xosdPunt) { DWORD dwModel; lpemp = (LPEMP) LLLock ( (HLLE)hempTmp ); xosd = (lpemp->emfunc) ( emfAttach, hpid, htid, 0, (DWORD64) &dwModel ); if ( xosd == xosdNone ) { // send a dbcEmChange notification OSDDoCallBack ( dbcEmChange, hpid, htid, lppid->lastmodel, dwModel, 0 ); lppid->lastmodel = dwModel; } LLUnlock ( (HLLE)hempTmp ); } if ( xosd == xosdPunt ) { xosd = xosdNone; } // reset our global flag to false lppid->fNative = fNat; } LLUnlock ( (HLLE)hpid ); return xosd; } XOSD OSDAPI OSDUseEM ( HPID hpid, HEM hem ) /*++ Routine Description: To tell osdebug that it should pass commands and callbacks for the process hpid through the execution model whose handle is hem. Arguments: hpid - Supplies process hem - Supplies handle to the execution model Return Value: xosdNone - Success xosdOutOfMemory - Not enough memory to create the reference to the execution model in the process structure. xosdInvalidEM - tried to add a native em when one was already present --*/ { LPPROCESSINFO lppid; HEMP hemp = NULL; LPEMS lpem; LPEMP lpemp; HPID hpidem; LPHPID lphpid; assert ( hem != NULL ); assert ( hpid != NULL ); // Add hem to pid's hem list lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); while ( hemp = (HEMP)LLNext ( lppid->llemp, (HLLE)hemp ) ) { lpemp = (LPEMP) LLLock ( (HLLE)hemp ); if ( lpemp->hem == hem ) { // this is an error, cannot add the same model twice LLUnlock ( (HLLE)hemp ); LLUnlock ( (HLLE)hpid ); return xosdDuplicate; } LLUnlock ( (HLLE)hemp ); } hemp = (HEMP)LLCreate ( lppid->llemp ); if ( hemp == NULL ) { LLUnlock ( (HLLE)hpid ); return xosdOutOfMemory; } lpem = (LPEMS) LLLock ( (HLLE)hem ); if ( lpem->emtype ) { LLAddHead ( lppid->llemp, (HLLE)hemp ); } else { // new native em LLAdd ( lppid->llemp, (HLLE)hemp ); lppid->hempNative = hemp; } LLUnlock ( (HLLE)hpid ); // add hpid to hem in llem hpidem = (HPID)LLCreate ( lpem->llhpid ); if ( hpidem == NULL ) { return xosdOutOfMemory; } lphpid = (LPHPID) LLLock ( (HLLE)hpidem ); // puts hpid in node *lphpid = hpid; LLUnlock ( (HLLE)hpidem ); LLUnlock ( (HLLE)hem ); LLAdd ( lpem->llhpid, (HLLE)hpidem ); return xosdNone; } XOSD OSDAPI OSDDiscardEM ( HPID hpid, HTID htid, HEM hem ) /*++ Routine Description: Remove an execution model from a process' list of available execution models. Arguments: hpid - Supplies process hem - Supplies handle to the execution model Return Value: xosdNone - Success xosdInvalidParameter - There is no execution model associated with hem. --*/ { LPPROCESSINFO lppid; HEM hemdis; LPEMS lpem; HEMP hemp = NULL; HEMP hempdis; LPEMP lpemp; HPID hpiddis; XOSD xosd = xosdNone; assert ( hpid != NULL ); // find the hem in the lppid's list lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); lpemp = (LPEMP) LLLock ( (HLLE)lppid->hempNative ); if ( lpemp->hem == hem ) { // trying to remove the native em lppid->hempNative = 0; } LLUnlock ( (HLLE)lppid->hempNative ); while (hemp = (HEMP)LLNext ( lppid->llemp, (HLLE)hemp ) ) { lpemp = (LPEMP) LLLock ( (HLLE)hemp ); if ( lpemp->hem == hem ) { hemdis = hem; hempdis = hemp; } LLUnlock ( (HLLE)hemp ); } if ( ! hemdis ) { // no em found, return error xosd = xosdInvalidParameter; } else { // delete the em from the hpid, then remove hpid from em in llem LLDelete ( lppid->llemp, (HLLE)hempdis ); lpem = (LPEMS) LLLock ( (HLLE)hemdis ); if ( hpiddis = (HPID)LLFind ( lpem->llhpid, 0, &hpid, 0 ) ) { LLDelete ( lpem->llhpid, (HLLE)hpiddis ); } else { // hpid not found: internal cosistancy error assert ( FALSE ); } LLUnlock ( (HLLE)hemdis ); } LLUnlock ( (HLLE)hpid ); return xosd; } /* Transport layer loading, unloading and configuration */ XOSD OSDAPI OSDAddTL ( TLFUNC tlfunc, LPDBF lpbdf, LPHTL lphtl ) /*++ Routine Description: Create and initialize a transport layer associated with the service function tlfunc and add it to the list of osdebug's available transport layers ( lltl ). Use list manager to create a transport layer handle and add it to the transport layer list ( lltl ). Call the transport layer service function ( tlfunc ) to initialize the transport layer and to register the debugger service functions ( lpdbf ) and OSDebug TL callback function ( TLCallBack ). Arguments: tlfunc - Supplies pointer to the transport layer service function to be associated with the transport layer that is being created. lpdbf - Supplies pointer to the debugger services structure that will be registered with the transport layer being created. lphtl - Returns the transport layer handle. Return Value: xosdNone - Success. xosdOutOfMemory - List manager was unable to allocate TL or add it to the transport layer list ( lltl ). Other xosd failure codes may be returned by the transport layer if its initialization fails. --*/ { HTL htl; LPTL lptl; XOSD xosd = xosdNone; assert ( tlfunc != NULL ); assert ( lpdbf != NULL ); assert ( lphtl != NULL ); htl = (HTL)LLCreate ( lltl ); if ( htl == NULL ) { xosd = xosdOutOfMemory; } else { LLAdd ( lltl, (HLLE)htl ); lptl = (LPTL) LLLock ( (HLLE)htl ); lptl->tlfunc = (TLFUNC_ODP) tlfunc; if (xosd == xosdNone) { xosd = lptl->tlfunc ( tlfInit, NULL, (UINT_PTR)lpdbf, (UINT_PTR)TLCallBack); } LLUnlock ( (HLLE)htl ); if (xosd != xosdNone) { LLDelete(lltl, (HLLE)htl); } else { *lphtl = htl; } } return xosd; } XOSD OSDAPI OSDStartTL( HTL htl ) { LPTL lptl; XOSD xosd; lptl = (LPTL) LLLock ( (HLLE)htl ); xosd = lptl->tlfunc ( tlfConnect, NULL, 0, 0 ); LLUnlock( (HLLE)htl ); return xosd; } UINT CountProcessesUsingTl( HTL htl ) /*++ Routine Description: This function counts the number of hpid's currently using the given TL. Arguments: htl - The TL to to count Hpids for. Return Value: The number of Hpids using the given TL. --*/ { HPID hPid = NULL; LPPROCESSINFO lppid; UINT nUsers = 0; HTL htlT; while (hPid = (HPID) LLNext (llpid, (HLLE) hPid)) { lppid = (LPPROCESSINFO) LLLock ((HLLE) hPid); htlT = lppid->htl; LLUnlock ((HLLE)hPid); if (htlT == htl) { nUsers++; } } return nUsers; } XOSD OSDAPI OSDDeleteTL ( HTL htl ) /*++ Routine Description: Remove the transport layer (htl) from OSDebug's list of available transport layers ( lltl ). Check the list of pids using this transport layer. If it is zero indicating that no pids are using it, then call the list manager to delete it from the list of available transport layers ( lltl ). Arguments: htl - A transport layer that has previously been returned by OSDAddTL. Return Value: xosdNone - Success. xosdInUse - The transport layer is still being used by some pid. OSDDiscardTL must be called on all of the pids using this particular em before OSDDeleteTL can be called without error. xosdInvalidParameter - The transport layer handle ( htl ) is invalid. --*/ { XOSD xosd = xosdNone; LPTL lptl; assert ( htl != NULL ); lptl = (LPTL) LLLock ( (HLLE)htl ); if ( CountProcessesUsingTl (htl) != 0 ) { LLUnlock ( (HLLE)htl ); xosd = xosdInUse; } else { (*lptl->tlfunc)(tlfDestroy, NULL, 0, 0); LLUnlock ( (HLLE)htl ); if ( !LLDelete ( lltl, (HLLE)htl ) ) { xosd = xosdInvalidParameter; } } return xosd; } XOSD OSDAPI OSDDiscardTL( HPID hpid, HTL htl ) /*++ Routine Description: Remove a reference to a tl by a pid. In particular, this allows us to remove a TL before we destroy the PID. This is sometimes necessary in error conditions. --*/ { HPID hpidT = NULL; LPPROCESSINFO lppid; lppid = (LPPROCESSINFO) LLLock ((HLLE) hpid); if (lppid->htl == htl) { lppid->htl = NULL; } LLUnlock ((HLLE) hpid); return xosdNone; } XOSD OSDAPI OSDTLGetInfo( HTL htl, LPTLIS lptlis ) /*++ Routine Description: Arguments: Return Value: --*/ { XOSD xosd; LPTL lptl; assert ( htl != NULL ); lptl = (LPTL) LLLock ( (HLLE)htl ); xosd = (*lptl->tlfunc) ( tlfGetInfo, NULL, 0, (UINT_PTR) lptlis ); LLUnlock ( (HLLE)htl ); return xosd; } XOSD OSDAPI OSDTLSetup( HTL htl, LPTLSS lptlss ) /*++ Routine Description: Arguments: Return Value: --*/ { XOSD xosd; LPTL lptl; assert ( htl != NULL ); lptl = (LPTL) LLLock ( (HLLE)htl ); xosd = (*lptl->tlfunc) ( tlfSetup, NULL, 0, (UINT_PTR) lptlss ); LLUnlock ( (HLLE)htl ); return xosd; } XOSD OSDAPI OSDDisconnect( HPID hpid, HTID htid ) /*++ Routine Description: Performs a brute force disconnect of the TL. This effectively simulates hanging up the phone or unplugging the network on a remote TL. Arguments: hpid - Supplies process to bind the TL htid - Supplies thread, ignored Return Value: xosdNone, usually. --*/ { if (!hpid) { return xosdUnknown; } return CallTL( tlfDisconnect, hpid, (UINT_PTR)htid, 0 ); } /* Target process initialization / deletion */ XOSD OSDAPI OSDCreateHpid ( LPFNSVC lpfnsvcCallBack, HEM hemNative, HTL htl, LPHPID lphpid ) /*++ Routine Description: Create the structures associated with a process Create the osdebug pid structure and add it to the list of processes ( llpid ). Notify the native execution model ( hem ) and the transport layer ( htl ) of the new process. When connecting to a remote transport layer, check the version signature to verify that the components are compatible. Arguments: lpfnsvcCallBack - Supplies a pointer to the debugger callback function. hemNative - Supplies a handle to the native execution model. htl - Supplies a handle to the transport layer. This may be NULL, in which case the new hpid can ONLY be used for operations which don't need to go to the remote side, such as querying (most) metrics. lphpid - Returns the handle to the pid that is generated. Return Value: xosdOutOfMemory - The List manager was unable to allocate memory for the structures to be created. Any error values that may be generated by the transport layer or execution model initialization of a process. --*/ { /* * * Use the list manager to create a process structure and add it to * * the process list ( llpid ). * * * * Initialize the fields of the process structure. * * * * Call the execution model service function for the native execution * * model with the emfCreatePid command and the initialization string * * ( lszEMData ). * * * * Call the transport layer service function for the process's * * transport layer with the tlfCreatePid command and the * * initialization string ( lszTLData ). */ HEM hodem; HPID oldhpid; AVS RemoteAvs; HPID hpid; LPEMS lpem; XOSD xosd = xosdNone; XOSD xosdSave = xosdNone; assert ( lpfnsvcCallBack != NULL ); assert ( hemNative != NULL ); assert ( lphpid != NULL ); assert ( llpid != 0 ); // Create and initialize the process structure and add to llpid xosd = CreateProc ( lpfnsvcCallBack, hemNative, htl, &hpid ); if (xosd != xosdNone) { return xosd; } // Notify the transport layer of the new process if (htl) { xosd = CallTL ( tlfConnect, hpid, 0, 0 ); switch(xosd = CallTL (tlfGetVersion, hpid, sizeof(Avs), (DWORD64)&RemoteAvs)) { case xosdNone: // got version info for remote side of transport... // verify it. if (Avs.rlvt != RemoteAvs.rlvt || Avs.iRmj != RemoteAvs.iRmj) { xosd = xosdBadVersion; // bogus version } break; case xosdNotRemote: xosd = xosdNone; break; case xosdBadVersion: default: break; } } if (xosd == xosdNone) { // Notify the native execution model of the new process lpem = (LPEMS) LLLock ( (HLLE)hemNative ); if (htl) { xosd = lpem->emfunc (emfConnect, hpid, NULL, 0, 0); } // BUGBUG: Need to verify that we have the right DM when // BUGBUG: xosd == xosdInUse. if (xosd == xosdInUse) { xosdSave = xosdInUse; xosd = xosdNone; } if (xosd == xosdNone) { xosd = lpem->emfunc (emfCreateHpid, hpid, NULL, 0, 0); } LLUnlock ( (HLLE)hemNative ); } if (xosd == xosdNone) { *lphpid = hpid; } else { if (htl) { CallTL(tlfDisconnect, hpid, 0, 0); } // remove hpid from the lists of hpids in llem for (hodem = (HEM)LLNext((HLLI) llem, NULL ); hodem; hodem = (HEM)LLNext( (HLLI)llem, (HLLE)hodem )) { lpem = (LPEMS) LLLock ( (HLLE)hodem ); if ( oldhpid = (HPID)LLFind ( lpem->llhpid, 0, &hpid, 0L ) ) { LLDelete ( lpem->llhpid, (HLLE)oldhpid ); } LLUnlock ( (HLLE)hodem ); } LLUnlock ( (HLLE)hodem ); LLDelete ( llpid, (HLLE)hpid ); } return xosd == xosdNone? xosdSave : xosd; } XOSD OSDAPI OSDDestroyHpid ( HPID hpid ) /*++ Routine Description: Destroy the structures associated with a process Delete the osdebug pid structure from the list of processes ( llpid ). Notify the native execution model ( hem ) and the transport layer ( htl ) that it's been deleted. Arguments: hpid - Supplies the process to destroy. Return Value: xosdInvalidProc - The hpid given was invalid. Any error values that may be generated by the transport layer or execution model during destruction of a process. --*/ { /* * * * Call the execution model service functions to let them know that * * this hpid is being destroyed. * * * * Use the list manager to delete the process structure from the * * process list ( llpid ). * * * */ XOSD xosd; HEM hodem; LPEMS lpem; HPID oldhpid; LPPROCESSINFO lppid; HTL htl, htlT; xosd = CallEM ( emfDestroyHpid, hpid, NULL, 0, 0 ); if (xosd == xosdNone) { lppid = (LPPROCESSINFO) LLLock((HLLE)hpid); htl = lppid->htl; LLUnlock((HLLE)hpid); // if this is the last Process using this TL, disconnect if (htl && CountProcessesUsingTl (htl) == 1) { xosd = CallEM ( emfDisconnect, hpid, 0, 0, 0 ); xosd = CallTL ( tlfDisconnect, hpid, 0, 0 ); } // remove hpid from the lists of hpids in llem for (hodem = (HEM)LLNext( (HLLI)llem, NULL ); hodem; hodem = (HEM)LLNext( (HLLI)llem, (HLLE)hodem )) { lpem = (LPEMS) LLLock ( (HLLE)hodem ); if ( oldhpid = (HPID)LLFind ( lpem->llhpid, 0, &hpid, 0L ) ) { LLDelete ( lpem->llhpid, (HLLE)oldhpid ); } LLUnlock ( (HLLE)hodem ); } LLDelete ( llpid, (HLLE)hpid ); } return xosd; } XOSD OSDAPI OSDDestroyHtid( HPID hpid, HTID htid ) /*++ Routine Description: Companion for OSDDestroyPID(); unhooks and deletes an htid. There is less to this than destroying a PID: the EM has to be notified, but the TL does not. Arguments: htid - osdebug htid Return Value: xosdNone, xosdInvalidProc, xosdInvalidThread --*/ { LPTHREADINFO lptid; LPPROCESSINFO lppid; XOSD xosd; assert( htid != NULL ); lptid = (LPTHREADINFO) LLLock((HLLE) htid ); if (!lptid) { return xosdInvalidHandle; } LLUnlock((HLLE) htid ); xosd = CallEM ( emfDestroyHtid, hpid, htid, 0, 0 ); lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); if (!lppid) { return xosdBadProcess; } LLDelete ( lppid->lltid, (HLLE)htid ); LLUnlock ( (HLLE)hpid ); return xosdNone; } /* Address manipulation */ XOSD OSDAPI OSDGetAddr ( HPID hpid, HTID htid, ADR adr, LPADDR lpaddr ) /*++ Routine Description: To get one of the special addresses in a process/thread which the EM can supply. Arguments: hpid - The process htid - The thread adr - The address to get. One of the following. adrPC - The program counter of the thread. adrBase - The frame base address of the thread. adrStack - The current stack pointer of the thread. adrData - The data area for the thread/process adrBaseProlog - The frame base address will be fixed up as if the prolog code of the existing function were already executed. This requires that the address passed in be the start of the function. adrTlsBase - The base of the thread local storage area. lpaddr - Returns the requested address. Return Value: xosdNone - Success Any error that may be generated by the emfGetAddr function of the execution model associated with hpid:htid. --*/ { assert ( lpaddr != NULL ); if ( htid == NULL && adr != adrData ) { _fmemset ( lpaddr, 0, sizeof ( ADDR ) ); return xosdNone; } else { XOSD xosd = CallEM ( emfGetAddr, hpid, htid, adr, (DWORD64)lpaddr ); assert(Is64PtrSE(lpaddr->addr.off)); return xosd; } } XOSD OSDAPI OSDSetAddr ( HPID hpid, HTID htid, ADR adr, LPADDR lpaddr ) /*++ Routine Description: Sets one of the addresses associated with a thread. Arguments: hpid - Supplies process htid - Supplies thread adr - Supplies id of the address to set, and may be one of the following: adrPC - The program counter of the thread. adrBase - The frame base address of the thread. adrStack - The current stack pointer of the thread. adrData - The data area for the thread/process adrBaseProlog - N/A ( can't be set ) adrTlsBase - N/A ( can't be set ) lpaddr - Supplies the address to set the hpid:htid with Return Value: xosdNone - Success. Any error that may be generated by the emfSetAddr function of the execution model associated with hpid:htid. --*/ { assert ( lpaddr != NULL ); assert(Is64PtrSE(lpaddr->addr.off)); if ( htid == NULL && adr != adrData ) { return xosdInvalidParameter; } else { return CallEM ( emfSetAddr, hpid, htid, adr, (DWORD64)lpaddr ); } } XOSD OSDAPI OSDFixupAddr ( HPID hpid, HTID htid, LPADDR lpaddr ) /*++ Routine Description: Take an unfixed-up (logical) address and transform it into the equivalent fixed up (physical) address. Uses emfFixupAddr. Arguments: hpid - Supplies the process to which the address is associated. htid - Supplies the thread to which the address is associated. lpaddr - Supplies the address which is to be fixed up. Return Value: xosdNone - Success xosdInvalidMTE - The logical address could not be found in any of the modules in the given process. --*/ { ADDR addrTmp = {0}; XOSD xosd = xosdNone; assert(Is64PtrSE(lpaddr->addr.off)); addrTmp = *lpaddr; xosd = CallEM( emfFixupAddr, hpid, htid, 0, (DWORD64)&addrTmp ); if (xosdNone == xosd) { *lpaddr = addrTmp; } return xosd; } XOSD OSDAPI OSDUnFixupAddr ( HPID hpid, HTID htid, LPADDR lpaddr ) /*++ Routine Description: Take a fixed up (physical) address and transform it into the equivalent unfixed up (logical) address. Note: It is essential that there be complete mapping (determined by the em) between logical and physical addresses associated with a process. Since it is often the case that part of the address space is not mapped via symbol information to any exe/dll, in order to work with the symbol handler, when these addresses are unfixed up, the hpid should be entered in the emi field. Uses emfUnfixupAddr. Arguments: hpid - Supplies the process of address to be unfixed up. htid - Supplies the thread lpaddr - Supplies the address to be unfixed up. Return Value: xosdNone - Success --*/ { ADDR addrTmp = *lpaddr; XOSD xosd = CallEM( emfUnFixupAddr, hpid, htid, 0, (DWORD64)&addrTmp ); if (xosdNone == xosd) { assert(Is64PtrSE(addrTmp.addr.off)); *lpaddr = addrTmp; } return xosd; } XOSD OSDAPI OSDCompareAddrs ( HPID hpid, LPADDR lpaddr1, LPADDR lpaddr2, LPDWORD lpResult ) /*++ Routine Description: Compare address *lpaddr1 to address *lpaddr2. Note: It is the case in some (DOS) supported operating systems address comparison may not be a simple matter of comparing segments and offsets. Therefore address comparison should be done through this function. Uses emfCompareAddrs. Arguments: hpid - Supplies the process with which the addresses are associated. lpaddr1 - Supplies the first address to compare. lpaddr2 - A pointer to the second address to compare. lpResult - Returns result of comparison Return Value: xosdNone: Success *lpResult < 0: *lpaddr1 < *lpaddr2 *lpResult == 0: *lpaddr1 == *lpaddr2 *lpResult >0: *lpaddr1 > *lpaddr2 --*/ { CAS cas; cas.lpaddr1 = lpaddr1; cas.lpaddr2 = lpaddr2; cas.lpResult = lpResult; assert(Is64PtrSE(lpaddr1->addr.off)); assert(Is64PtrSE(lpaddr2->addr.off)); return CallEM( emfCompareAddrs, hpid, NULL, 0, (DWORD64)&cas ); } XOSD OSDAPI OSDRegisterEmi ( HPID hpid, HEMI hemi, LPTSTR lsz ) /*++ Routine Description: Register an EMI and filename with the EM. This hooks up the symbol table info for a module to the EM's representation of the module in the process. Arguments: hpid - Supplies process hemi - Supplies handle from SH to symbolic info lsz - Supplies the name of the module Return Value: xosdNone on success. Current implementation never reports failure. --*/ { REMI remi; remi.hemi = hemi; remi.lsz = lsz; return CallEM ( emfRegisterEmi, hpid, NULL, 0, (DWORD64)&remi ); } XOSD OSDAPI OSDUnRegisterEmi ( HPID hpid, HEMI hemi ) /*++ Routine Description: As modules are loaded, address mapping and stack walking info are stored in the EM. This tells the EM to discard that info. Arguments: hpid - Supplies handle to the process hemi - Supplies emi for module being unloaded Return Value: xosdNone - Success --*/ { return CallEM ( emfUnRegisterEmi, hpid, NULL, 0, (DWORD64)hemi ); } XOSD OSDAPI OSDSetEmi ( HPID hpid, HTID htid, LPADDR lpaddr ) /*++ Routine Description: Arguments: Return Value: --*/ { return CallEM ( emfSetEmi, hpid, htid, 0, (DWORD64)lpaddr ); } XOSD OSDAPI OSDGetMemoryInformation( HPID hpid, HTID htid, LPMEMINFO lpMemInfo ) /*++ Routine Description: Arguments: Return Value: --*/ { return CallEM( emfGetMemoryInfo, hpid, htid, sizeof(MEMINFO), (DWORD64)lpMemInfo ); } /* Modules and Segments */ XOSD OSDAPI OSDGetModuleNameFromAddress ( IN HPID hpid, IN DWORD64 Address, OUT LPTSTR pszModuleName, IN size_t SizeOf ) /*++ Routine Description: Get a module name via the PID and address. This is done by using the EMs module list, and bypasses SAPI. Arguments: hpid - Supplies process Address - Address used to find the module. pszModuleName - If successful will contain the module name. Recommendation: it be at least MAX_PATH in length. SizeOf - Size of pszModuleName in characters. Return Value: xosdNone if successful. Other xosd codes indicate the cause of failure. --*/ { XOSD xosd; TCHAR szModName[_MAX_PATH]; assert(pszModuleName); *szModName = 0; xosd = CallEM ( emfGetModuleNameFromAddress, hpid, NULL, Address, (DWORD64)szModName ); if (xosdNone == xosd) { _tcsncpy(pszModuleName, szModName, SizeOf-1); pszModuleName[SizeOf-1] = 0; } return xosd; } XOSD OSDAPI OSDGetModuleList ( HPID hpid, HTID htid, LPTSTR lpModuleName, LPMODULE_LIST * lplpModuleList ) /*++ Routine Description: Get the list of modules loaded in a process, or look up one module. The returned module list contains one entry per segment per module. Arguments: hpid - Supplies process htid - Supplies thread. NULL is valid. lpModuleName - Supplies optional name of module to look for. If this arg is NULL, all modules will be found. lplpModuleList - Returns list of modules in provided pointer. This will be memory allocated with MHAlloc. Return Value: xosdNone if successful. Other xosd codes indicate the cause of failure. --*/ { return CallEM ( emfGetModuleList, hpid, htid, (DWORD64)lpModuleName, (DWORD64)lplpModuleList ); } XOSD OSDAPI OSDSpawnOrphan ( HPID hpid, LPCTSTR lszRemoteExe, LPCTSTR lszCmdLine, LPCTSTR lszRemoteDir, LPSPAWNORPHAN FAR lpso, DWORD dwFlags ) /*++ Routine Description: Create a process which is not a debuggee. This is used for VC++'s Project.Execute. Arguments: hpid - Supplies TL binding to target machine. Only used for determining which EM, TL, etc. to use; will NOT attach the new process to this HPID. You can think of this as sort of like OSDReadFile: it just does something useful on the target machine, without actually affecting the debuggee. lszRemoteExe - Supplies name of remote exe. * lszCmdLine - Supplies command line. * lszRemoteDir - OPTIONAL Supplies the initial directory of the program. This argument may be NULL or "", in which case the DM must choose a sensible default, such as the directory of the EXE. lpso - Returns a SPAWNORPHAN structure, in which data about the newly created process is returned. lpso->dwPid is set to the PID of the new process, and lpso->rgchErr[] is set to either "" for a successful spawn, or an error string which should be displayed to the user if the spawn failed. * dwFlags - Supplies flags that are passed to the EM. The following flags are defined; any or none may be supported by any EM/DM pair: ulfMinimizeApp - New process will be iconic ulfNoActivate - New process will not receive focus ulfInheritHandles - New app will inherit handles from debugger. This is useful when debugging an app which inherits handles from its parent. Return Value: Any xosd other than xosdNone indicates failure, and the debugger should display an appropriate error message (preferably depending on the XOSD returned). If xosdNone is returned, the caller must check lpso->rgchErr[0]; if this byte is '\0', then the spawn was successful, otherwise rgchErr contains an error message which should be displayed to the user. --*/ { SOS sos; XOSD xosd; #ifdef DEBUG // As long as we're un-consting the strings, we should verify that they're not changed unsigned cb = _ftcslen (lszRemoteExe) + _ftcslen (lszRemoteExe) +_ftcslen (lszRemoteExe); #endif assert (!(dwFlags & ulfMultiProcess)); assert (hpid); assert (lszRemoteExe); assert (lpso); memset (&sos, 0, sizeof(sos)); sos.dwChildFlags = dwFlags; sos.lszRemoteExe = (LPTSTR) lszRemoteExe; sos.lszCmdLine = (LPTSTR) lszCmdLine; sos.lszRemoteDir = (LPTSTR) lszRemoteDir; sos.lpso = lpso; xosd = CallEM ( emfSpawnOrphan, hpid, NULL, sizeof (sos), (DWORD64)&sos ); #ifdef DEBUG assert (cb == _ftcslen (lszRemoteExe) + _ftcslen (lszRemoteExe) +_ftcslen (lszRemoteExe)); #endif return xosd; } /* Target Application loading / unloading */ XOSD OSDAPI OSDProgramLoad ( HPID hpid, LPTSTR lszRemoteExe, LPTSTR lszCmdLine, LPTSTR lszRemoteDir, LPTSTR lszDebugger, DWORD dwFlags ) /*++ Routine Description: Send a program load request to the EM. If the process is created, the shell will be notified of a new process creation. Arguments: hpid - Supplies the process the process handle that binds to the EM and TL. This is not neccessarily the hpid that will correspond to the new process. lszRemoteExe - lszCmdLine - Supplies the command line that specifies the child program and its arguments. lszRemoteDir - lszDebugger - Supplies the name of the debugger to the EM. This is provided so that the DM can print the debugger's name in error messages, and so the DM can put it in the debuggee's window title. dwFlags - Supplies flags that are passed to the EM. The following flags are defined; any or none may be supported by any EM/DM pair: ulfMultiProcess - children of the new process will be debugged. ulfMinimizeApp - New process will be iconic ulfNoActivate - New process will not receive focus ulfInheritHandles - New app will inherit handles from debugger. This is useful when debugging an app which inherits handles from its parent. Return Value: xosdOutOfMemory - could not allocate a buffer to copy the arguments into xosdFileNotFound xosdAccessDenied xosdLoadChild --*/ { XOSD xosd = xosdNone; PRL prl; assert ( hpid != NULL ); assert ( lszRemoteExe != NULL ); memset (&prl, 0, sizeof(prl)); xosd = CallEM ( emfSetMulti, hpid, NULL, ( dwFlags & ulfMultiProcess ) != 0, 0 ); CheckErr(xosd); xosd = CallEM ( emfDebugger, hpid, NULL, (_ftcslen ( lszDebugger ) + 1) * sizeof(TCHAR), (DWORD64)lszDebugger ); CheckErr(xosd); prl.lszRemoteExe = lszRemoteExe; prl.lszCmdLine = lszCmdLine; prl.lszRemoteDir = lszRemoteDir; prl.dwChildFlags = dwFlags; prl.lpso = NULL; xosd = CallEM ( emfProgramLoad, hpid, NULL, sizeof( prl ), (DWORD64)&prl ); return xosd; } XOSD OSDAPI OSDDebugActive( HPID hpid, LPVOID lpvPrivate, DWORD cbData ) /*++ Routine Description: Tell the EM to debug a process which is already running and not being debugged. If this succeeds, the debugger will be notified of a new process, very similar to the creation of a child of a process already being debugged. Arguments: hpid - Supplies process to bind to the EM. If no process is already being debugged, this will be the "root" process, and will not yet have a real debuggee attached to it. Otherwise, this may be any process which is bound to the right native EM. cbData - Supplies size of data passed in lpvPrivate lpvPrivate - Supplies pointer to a private data structure recognized by the EM. Return Value: xosdNone --*/ { return CallEM( emfDebugActive, hpid, NULL, cbData, (DWORD64)lpvPrivate); } XOSD OSDAPI OSDSetPath( HPID hpid, DWORD fSet, LPTSTR lszPath ) /*++ Routine Description: Sets the search path in the DM Arguments: hpid - Supplies process to bind EM fSet - Supplies TRUE to set new path, FALSE to turn off path searching. lszPath - Supplies search path string Return Value: XOSD error code --*/ { return CallEM( emfSetPath, hpid, 0, fSet, (DWORD64)lszPath ); } XOSD OSDAPI OSDProgramFree ( HPID hpid ) /*++ Routine Description: Terminate and unload the program being debugged in process hpid. This does not guarantee termination of child processes. Arguments: hpid - Supplies the process to kill Return Value: xosdNone - Success Any error that can be generated by emfProgramFree --*/ { assert ( hpid != NULL); return CallEM ( emfProgramFree, hpid, NULL, 0, 0 ); } XOSD OSDAPI OSDNewSymbolsLoaded ( HPID hpid ) /*++ Routine Description: Notify the DM that new symbols have been loaded via a !reload. Arguments: hpid - Supplies the process whose EM function we should use. Return Value: xosdNone - Success Any xosd error --*/ { assert ( hpid != NULL); return CallEM ( emfNewSymbolsLoaded, hpid, NULL, 0, 0 ); } XOSD OSDAPI OSDSignalKernelLoadCompleted ( HPID hpid ) /*++ Routine Description: Notify the DM that the synchronous module load of the kernel is complete. Arguments: hpid - Supplies the process whose EM function we should use. Return Value: xosdNone - Success Any xosd error --*/ { assert ( hpid != NULL); return CallEM ( emfKernelLoaded, hpid, NULL, 0, 0 ); } /* Target execution */ XOSD OSDAPI OSDGo ( HPID hpid, HTID htid, LPEXOP lpExop ) /*++ Routine Description: Run the target program for the process hpid [thread htid]. uses emfGo. Note: This is an asynchronous API. Parameters: hpid - Supplies process to run htid - Supplies the thread to run lpExop - Supplies options to control execution Return Value: xosdNone: Success --*/ { return CallEM( emfGo, hpid, htid, 0, (DWORD64)lpExop ); } XOSD OSDAPI OSDSingleStep ( HPID hpid, HTID htid, LPEXOP lpExop ) /*++ Routine Description: Execute a single processor instruction of the process hpid and thread htid. uses emfSingleStep Note: This is an asynchronous API. Parameters: hpid - Supplies process htid - Supplies thread lpExop - Supplies options to control execution Return Value: xosdNone: Success --*/ { return CallEM( emfSingleStep, hpid, htid, 0, (DWORD64)lpExop ); } XOSD OSDAPI OSDRangeStep ( HPID hpid, HTID htid, LPADDR lpaddrMin, LPADDR lpaddrMax, LPEXOP lpExop ) /*++ Routine Description: Execute single processor instructions for hpid/htid while *lpaddrMin <= adrPC <= *lpaddrMax. Both of the boundary addresses are inclusive of the range. uses emfRangeStep. Note: This is an asynchronous API. Parameters: hpid - Supplies process htid - Supplies thread lpaddrMin - Supplies the lower boundary address lpaddrMax - Supplies the upper boundary address lpExop - Supplies options to control execution Return Value: xosdNone: Success --*/ { RSS rss; rss.lpaddrMin = lpaddrMin; rss.lpaddrMax = lpaddrMax; rss.lpExop = lpExop; assert(Is64PtrSE(lpaddrMin->addr.off)); assert(Is64PtrSE(lpaddrMax->addr.off)); return CallEM( emfRangeStep, hpid, htid, 0 , (DWORD64)&rss ); } XOSD OSDAPI OSDReturnStep ( HPID hpid, HTID htid, LPEXOP lpExop ) /*++ Routine Description: Execute the thread until it has returned to the caller. uses emfReturnStep Note: This is an asynchronous API. Parameters: hpid - Supplies process htid - Supplies thread lpExop - Supplies options to control execution Return Value: xosdNone: Success --*/ { return CallEM( emfReturnStep, hpid, htid, 0, (DWORD64)lpExop ); } XOSD OSDAPI OSDAsyncStop ( HPID hpid, DWORD fSetFocus ) /*++ Description: Halt the execution of the target process hpid. uses emfStop. Note: This is an asynchronous API. Parameters: hpid - Supplies process fSetFocus - Supplies option of changing focus to debuggee to avoid user having to nudge debuggee into stop Return Value: --*/ { return CallEM( emfStop, hpid, NULL, fSetFocus, 0 ); } /* Calling functions in the target */ XOSD OSDAPI OSDSetupExecute( HPID hpid, HTID htid, LPHIND lphind ) /*++ Routine Description: This routine is called to set up a thread for doing a function evaluation. This is passed on the the EM for processing. Arguments: hpid - Supplies the handle to the process htid - Supplies the handle to the thread lphind - Supplies a pointer to save the SAVEINFO handle at Return Value: XOSD error code --*/ { XOSD xosd; xosd = CallEM( emfSetupExecute, hpid, htid, 0, (DWORD64)lphind); return xosd; } /* OSDSetupExecute() */ XOSD OSDAPI OSDStartExecute( HPID hpid, HIND hind, LPADDR lpaddr, DWORD fIgnoreEvents, DWORD fFar ) /*++ Routine Description: Execute function evaluation. Arguments: hpid - Supplies the handle to the process hind - Supplies the exeute object handle lpaddr - Supplies the address to start evaluation at fIgnoreEvents - Supplies TRUE if sub-events are to be ignored fFar - Supplies TRUE if this is an _far function Return Value: return-value - Description of conditions needed to return value. - or - None. --*/ { EXECUTE_STRUCT es; assert(Is64PtrSE(lpaddr->addr.off)); es.addr = *lpaddr; es.fIgnoreEvents = fIgnoreEvents; es.fFar = fFar; return CallEM( emfStartExecute, hpid, 0, (DWORD64)hind, (DWORD64)&es); } /* OSDStartExecute() */ XOSD OSDAPI OSDCleanUpExecute ( HPID hpid, HIND hind ) /*++ Routine Description: This routine is called to clean up a current pending function evaluation. Arguments: hind Supplies the handle to the execute object Return Value: XOSD error code --*/ { return CallEM( emfCleanUpExecute, hpid, NULL, (DWORD64)hind, 0); } /* OSDCleanUpExecute() */ /* General target information */ XOSD OSDAPI OSDGetDebugMetric ( HPID hpid, HTID htid, MTRC mtrc, LPVOID lpv ) /*++ Routine Description: This is a facility for getting various information about the execution model associated with a process. Arguments: mtrc - Supplies metric to query, one of: mtrcAsync whether debuggee runs asynchronously from debugger mtrcWatchPoints whether watchpoints are supported on target mtrcPidSize size in bytes of an OS PID mtrcTidSize size in bytes of an OS TID mtrcPidValue get OS PID for the specified HPID mtrcTidValue get OS TID for the specified HPID/HTID pair mtrcProcessorType debuggee's processor (return value from enum MPT) mtrcProcessorLevel debuggee's processor level mtrcThreads whether target OS supports multiple threads mtrcEndian big Endian or little Endian mtrcCRegs Number of Registers mtrcCFlags Number of Flags mtrcAssembler Assembler exists for the Execution model hpid - Supplies process htid - Supplies thread (optional for some metrics) lpv - Returns requested info Return Value: xosdNone for success, other xosd codes describe the cause of failure. lpl must point to a LONG which will receive the info. --*/ { return CallEM ( emfMetric, hpid, htid, mtrc, (DWORD64)lpv ); } /* Target memory */ XOSD OSDAPI OSDReadMemory ( HPID hpid, HTID htid, LPADDR lpaddr, LPVOID lpbBuffer, DWORD cbBuffer, LPDWORD lpcbRead ) /*++ Routine Description: Read a stream of cbBuffer bytes from the target process hpid at the address *lpaddr into the buffer *lpbBuffer. A negative return value is an error, positive return values represent the number of bytes actually read. The return value will never be greater than the number of bytes requested. uses emfReadMemory Arguments: hpid - Supplies process htid - Supplies thread lpaddr - Supplies address to read from lpbBuffer - Returns the results cbBuffer - Supplies the number of bytes to read lpcbRead - Returns the nuber of bytes actually read Return Value: xosdNone: Success --*/ { RWMS rwms; assert(Is64PtrSE(lpaddr->addr.off)); rwms.lpaddr = lpaddr; rwms.lpbBuffer = (LPBYTE) lpbBuffer; rwms.cbBuffer = cbBuffer; rwms.lpcb = lpcbRead; return CallEM( emfReadMemory, hpid, htid, 0, (DWORD64)&rwms ); } XOSD OSDAPI OSDWriteMemory ( HPID hpid, HTID htid, LPADDR lpaddr, LPVOID lpbBuffer, DWORD cbBuffer, LPDWORD lpcbWritten ) /*++ Routine Description: Write a stream of cbBuffer bytes from the buffer *lpbBuffer to the target process specified by hpid at the address *lpaddr. uses emfWriteMemory Arguments: hpid - Supplies process htid - Supplies thread lpaddr - Supplies address to write data to lpbBuffer - Supplies data to write cbBuffer - Supplies the number of bytes to write lpcbWritten - Returns the number of bytes actually written Return Value xosdNone: Success --*/ { RWMS rwms; assert(Is64PtrSE(lpaddr->addr.off)); rwms.lpaddr = lpaddr; rwms.lpbBuffer = (LPBYTE) lpbBuffer; rwms.cbBuffer = cbBuffer; rwms.lpcb = lpcbWritten; return CallEM( emfWriteMemory, hpid, htid, 0, (DWORD64)&rwms ); } XOSD OSDAPI OSDGetObjectLength ( HPID hpid, HTID htid, LPADDR lpaddr, LPUOFFSET lpuoffStart, LPUOFFSET lpuoffLength ) /*++ Routine Description: Get the length of a given linear exe object Arguments: hpid - Supplies process htid - Supplies thread lpaddr - Supplies address within the object in question lpuoffStart - Returns linear address of beginning of object lpuoffLength - Returns the length, in bytes, of the object Return Value: xosdNone - Success Any value that can be returned by the emfGetObjLength of the execution model called. --*/ { GOL gol; assert ( lpaddr != NULL ); assert(Is64PtrSE(lpaddr->addr.off)); gol.lplBase = lpuoffStart; gol.lplLen = lpuoffLength; gol.lpaddr = lpaddr; return CallEM ( emfGetObjLength, hpid, htid, 0, (DWORD64)&gol ); } XOSD OSDAPI OSDGetFunctionInformation( HPID hpid, HTID htid, LPADDR lpaddr, LPFUNCTION_INFORMATION lpFunctionInformation ) /*++ Routine Description: Get information describing the layout of a function. The data available are platform dependent, but the FUNCTION_INFORMATION structure is portable. Arguments: hpid - Supplies process htid - Supplies thread lpaddr - Supplies address within the object in question lpFunctionInformation - Returns a packet describing the function Return Value: xosdNone - Success Any value that can be returned by the emfGetFunctionInfo of the execution model called. --*/ { GFI gfi; assert ( lpaddr != NULL ); assert(Is64PtrSE(lpaddr->addr.off)); gfi.lpaddr = lpaddr; gfi.lpFunctionInformation = lpFunctionInformation; return CallEM ( emfGetFunctionInfo, hpid, htid, 0, (DWORD64)&gfi ); } /* Register management */ XOSD OSDAPI OSDGetRegDesc ( HPID hpid, HTID htid, DWORD iReg, LPRD lprd ) /*++ Routine Description: Get the internal description of what a specific register looks like. This is provided for general UI interfaces. See the RD structure for more info. Arguments: hpid - Supplies process htid - Supplies thread (optional?) iReg - Supplies index of register lprd - Returns requested info in an RD structure Return Value: xosdNone. Results are undefined if iReg is out of range. --*/ { return CallEM ( emfGetRegStruct, hpid, htid, iReg, (DWORD64)lprd ); } XOSD OSDAPI OSDGetFlagDesc ( HPID hpid, HTID htid, DWORD iFlag, LPFD lpfd ) /*++ Routine Description: Get the internal description of what a specific flag looks like. This is provided for general UI interfaces. Arguments: hpid - Supplies process htid - Supplies thread (unused?) iReg - Supplies index of requested flag lprd - Returns FD structure describing flag Return Value: xosdNone. If iReg is out of range, behaviour is undefined. --*/ { return CallEM ( emfGetFlagStruct, hpid, htid, iFlag, (DWORD64)lpfd ); } XOSD OSDAPI OSDReadRegister ( HPID hpid, HTID htid, DWORD dwIndex, LPVOID lpv ) /*++ Routine Description: Read a register in a debuggee thread Arguments: hpid - Supplies process htid - Supplies thread dwIndex - Supplies register index lpv - Returns register value Return Value: xosdNone - Success The EM may return an xosd failure status if wIndex is invalid or for other reasons. --*/ { return CallEM ( emfGetReg, hpid, htid, dwIndex, (DWORD64)lpv ); } XOSD OSDAPI OSDWriteRegister ( HPID hpid, HTID htid, DWORD dwIndex, LPVOID lpv ) /*++ Routine Description: Write a register in a debuggee thread Arguments: hpid - Supplies process htid - Supplies thread dwIndex - Supplies register index lpv - Supplies register value Return Value: xosdNone - Success The EM may return an xosd failure status if wIndex is invalid or for other reasons. --*/ { return CallEM ( emfSetReg, hpid, htid, dwIndex, (DWORD64)lpv ); } XOSD OSDAPI OSDReadFlag ( HPID hpid, HTID htid, DWORD dwIndex, LPVOID lpv ) /*++ Routine Description: Read the flag specified by wIndex in the processor flag set for hpid:htid pair. Arguments: hpid - Supplies process htid - Supplies thread dwIndex - Supplies register index lpv - Returns value Return Value: xosdNone - Success Any error that may be generated by the emfGetFlag function of the execution model associated with hpid:htid. --*/ { assert ( lpv != NULL ); return CallEM ( emfGetFlag, hpid, htid, dwIndex, (DWORD64)lpv ); } XOSD OSDAPI OSDWriteFlag ( HPID hpid, HTID htid, DWORD dwIndex, LPVOID lpv ) /*++ Routine Description: Write the flag specified by wIndex in the processor flag set for hpid:htid pair. Arguments: hpid - Supplies process htid - Supplies thread dwIndex - Supplies register index lpv - Supplies new value Return Value: xosdNone - Success Any error that may be generated by the emfSetFlag function of the execution model associated with hpid:htid. --*/ { return CallEM ( emfSetFlag, hpid, htid, dwIndex, (DWORD64)lpv ); } XOSD OSDAPI OSDSaveRegs ( HPID hpid, HTID htid, LPHIND lphmem ) /*++ Routine Description: Save the register set of a thread so that it may be restored later. Arguments: hpid - Supplies process htid - Supplies thread lphmem - Returns a handle to the stored context Return Value: xosdNone - Success Any value that can be returned by the emfSaveRegs of the execution model called. --*/ { return CallEM ( emfSaveRegs, hpid, htid, 0, (DWORD64)lphmem ); } XOSD OSDAPI OSDRestoreRegs ( HPID hpid, HTID htid, HIND hmem ) /*++ Routine Description: Restore a thread context stored by OSDSaveRegs. Arguments: hpid - Supplies process htid - Supplies thread hmem - Supplies saved context Return Value: xosdNone - Success Any value that can be returned by the emfRestoreRegs of the execution model called. --*/ { return CallEM ( emfRestoreRegs, hpid, htid, 0, (DWORD64)hmem ); } /* Breakpoints */ XOSD OSDAPI OSDBreakpoint ( HPID hpid, LPBPS lpbps ) /*++ Routine Description: Arguments: Return Value: --*/ { return CallEM ( emfBreakPoint, hpid, NULL, SizeofBPS(lpbps), (DWORD64)lpbps ); } /* Assembly and Disassembly */ XOSD OSDAPI OSDUnassemble ( HPID hpid, HTID htid, LPSDI lpsdi ) /*++ Routine Description: Disassemble one machine instruction. The address of the instruction to disassemble is in the SDI structure pointed to by lpsdi. Arguments: hpid - Supplies process to get data from htid - Supplies thread lpsdi - Supplies and Returns disassembly info Return Value: xosdNone for success, other xosd codes describe the reason for failure. When the call succeeds, lpsdi->lsz will point to a static string containing the disassembled instruction. The SDI structure contains ADDR fields which will contain effective addresses computed for the instruction, and int fields which describe the length of each of the parts of the disassembly text, to facilitate formatting the text for display. The addr field will be updated to point to the next instruction in the stream. --*/ { assert ( lpsdi != NULL ); return CallEM ( emfUnassemble, hpid, htid, 0, (DWORD64)lpsdi ); } XOSD OSDAPI OSDAssemble ( HPID hpid, HTID htid, LPADDR lpaddr, LPTSTR lsz ) /*++ Routine Description: Assemble an instruction into the debuggee. Arguments: hpid - Supplies process htid - Supplies thread (optional?) lpaddr - Supplies address to assemble to lsz - Supplies instruction text Return Value: xosdNone for success, other xosd codes describe cause of failure. --*/ { assert(Is64PtrSE(lpaddr->addr.off)); return CallEM ( emfAssemble, hpid, htid, (DWORD64)lpaddr, (DWORD64)lsz ); } #define doffMax 120 // Double the Max number of instructions guaranteed // to get you back in sync. We use the second half // of the buffer for cache'n. static HPID hpidGPI = NULL; static BYTE rgbGPI [ doffMax ]; static ADDR addrGPI; XOSD GPIBuildCache ( HPID hpid, HTID htid, LPADDR lpaddr ) { XOSD xosd = xosdBadAddress; int fFound = FALSE; ADDR addr = *lpaddr; ADDR addrT; int ib = 0; _fmemset ( rgbGPI, 0, doffMax ); addrGPI = *lpaddr; hpidGPI = hpid; GetAddrOff ( addr ) -= min ( (UOFFSET) doffMax, GetAddrOff ( *lpaddr ) ); while ( !fFound && GetAddrOff ( addr ) < GetAddrOff ( *lpaddr ) ) { SDI sdi; sdi.dop = dopNone; sdi.addr = addr; addrT = addr; CallEM ( emfUnassemble, hpid, htid, sizeof( sdi ), (DWORD64)&sdi ); /* ** If we wrapped, because of disasm on the offset boundary */ if ( GetAddrOff ( sdi.addr ) < GetAddrOff ( addr ) ) { break; } addr = sdi.addr; rgbGPI [ ib ] = (BYTE) ( GetAddrOff ( addrGPI ) - GetAddrOff ( addr ) ); if ( GetAddrOff ( addr ) == GetAddrOff ( *lpaddr ) ) { xosd = xosdNone; *lpaddr= addrT; fFound = TRUE; } ib += 1; } // We haven't synced yet, so *lpaddr is probably pointing // to something that isn't really synchronous if ( !fFound ) { xosd = (XOSD) ( GetAddrOff ( *lpaddr ) - GetAddrOff ( addrT ) ); GetAddrOff ( *lpaddr ) -= xosd; if ( GetAddrOff ( *lpaddr ) != 0 ) { UOFFSET uoff; OSDGetPrevInst ( hpid, htid, lpaddr, &uoff ); } } return xosd; } void GPIShiftCache ( LPADDR lpaddr, int *pib ) { int doff = (int) ( GetAddrOff ( addrGPI ) - GetAddrOff ( *lpaddr ) ); int ib = 0; int ibRet = -1; for( ib = 0, ibRet = -1; ibRet == -1 && ib < doffMax; ++ib ) { rgbGPI [ ib ] = (BYTE) max ( (int) rgbGPI [ ib ] - doff, 0 ); if ( rgbGPI[ ib ] == 0 ) { ibRet = ib; } } if ( ib == doffMax ) { ibRet = doffMax - 1; } *pib = ibRet; addrGPI = *lpaddr; } XOSD GPIUseCache ( HPID hpid, HTID htid, LPADDR lpaddr ) { XOSD xosd = xosdBadAddress; int fFound = FALSE; ADDR addr = *lpaddr; int ib = 0; int ibCache= 0; int ibMax = 0; BYTE rgb [ doffMax ]; GPIShiftCache ( lpaddr, &ibMax ); _fmemset ( rgb, 0, doffMax ); GetAddrOff ( addr ) -= min ( (UOFFSET) doffMax, GetAddrOff ( *lpaddr ) ); while ( !fFound && GetAddrOff ( addr ) < GetAddrOff ( *lpaddr ) ) { ADDR addrT; BYTE doff = (BYTE) ( GetAddrOff ( *lpaddr ) - GetAddrOff ( addr ) ); // Attempt to align with the cache while ( doff < rgbGPI [ ibCache ] ) { ibCache += 1; } if ( doff == rgbGPI [ ibCache ] ) { // We have alignment with the cache addr = *lpaddr; addrT = addr; GetAddrOff ( addrT ) -= rgbGPI [ ibMax - 1 ]; } else { SDI sdi; sdi.dop = dopNone; sdi.addr = addr; addrT = addr; CallEM ( emfUnassemble, hpid, htid, sizeof( sdi ), (DWORD64)&sdi ); addr = sdi.addr; rgb [ ib ] = (BYTE) ( GetAddrOff ( addrGPI ) - GetAddrOff ( addr ) ); ib += 1; } if ( GetAddrOff ( addr ) == GetAddrOff ( *lpaddr ) ) { xosd = xosdNone; *lpaddr= addrT; fFound = TRUE; } } // Rebuild the cache _fmemmove ( &rgbGPI [ ib - 1 ], &rgbGPI [ ibCache ], ibMax - ibCache ); _fmemcpy ( rgbGPI, rgb, ib - 1 ); return xosd; } XOSD OSDAPI OSDGetPrevInst ( HPID hpid, HTID htid, LPADDR lpaddr, LPUOFFSET lpuoffset ) { if ( GetAddrOff ( *lpaddr ) == 0 ) { return xosdBadAddress; } else if ( hpid == hpidGPI && GetAddrSeg ( *lpaddr ) == GetAddrSeg ( addrGPI ) && GetAddrOff ( *lpaddr ) < GetAddrOff ( addrGPI ) && GetAddrOff ( *lpaddr ) > GetAddrOff ( addrGPI ) - doffMax / 2 ) { return GPIUseCache ( hpid, htid, lpaddr ); } else { return GPIBuildCache ( hpid, htid, lpaddr ); } } /* Stack Tracing */ XOSD OSDAPI OSDGetFrame ( HPID hpid, HTID htid, DWORD cFrame, LPHTID lphtid ) /*++ Routine Description: Put the frame on the stack previous to the frame specified by *lphtid into *lphtid. Arguments: hpid - Supplies process htid - Supplies thread cFrame - Used to tell the EM how many frames total the caller will be requesting. a value of zero indicates all frames. phTid - Pointer to real HTID, or last virtual HTID. If the HTID is real the routine will return a virtualized version of the HTID in the lphTid paramater. If the HTID is virtual then the routine will return the next virtualized HTID for the next frame. Return Value: xosdNone - Success. xosdEndOfStack - No more frames. Any value that can be returned by the Get Frame Function of the execution model called. --*/ { return CallEM ( emfGetFrame, hpid, htid, cFrame, (DWORD64)lphtid ); } /* Process and Thread manipulation */ XOSD OSDAPI OSGetThreadStatus ( HPID hpid, HTID htid, LPTST lptst ) /*++ Routine Description: Gets a thread state structure for the given hpid/htid. This routine can fail if an operation is invalid for the target architetecture. uses emfThreadStatus Arguments: hpid - Supplies process htid - Supplies thread lptst - Returns a thread state structure. Return Value: xosdNone: Success --*/ { return CallEM( emfThreadStatus, hpid, htid, 0, (DWORD64)lptst ); } XOSD OSDAPI OSDGetProcessStatus ( HPID hpid, LPPST lppst ) /*++ Routine Description: Returns a structure describing the process status for the given hpid. uses emfProcessStatus Arguments: hpid - Supplies process lppst - Returns process state structure Return Value: xosdNone: Success --*/ { return CallEM( emfProcessStatus, hpid, NULL, 0, (DWORD64)lppst ); } XOSD OSDAPI OSDGetThreadStatus ( HPID hpid, HTID htid, LPTST lptst ) /*++ Routine Description: Returns a structure describing the thread status for the given htid. uses emfThreadStatus Arguments: hpid - Supplies process htid - Supplies thread lptst - Returns thread state structure Return Value: xosdNone: Success --*/ { return CallEM( emfThreadStatus, hpid, htid, 0, (DWORD64)lptst ); } XOSD OSDAPI OSDFreezeThread ( HPID hpid, HTID htid, DWORD fFreeze ) /*++ Routine Description: Freezes or thaws the thread specified by hpid/htid. uses emfFreezeThread Arguments: hpid - Supplies process htid - Supplies thread fFreeze - Supplies TRUE to freeze, FALSE to thaw. Return Value: xosdNone: Success --*/ { return CallEM( emfFreezeThread, hpid, htid, fFreeze, 0 ); } XOSD OSDAPI OSDSetThreadPriority ( HPID hpid, HTID htid, DWORD dwPriority ) /*++ Routine Description: Sets the priority of the thread specified by hpid/htid to the given meta priority level. uses emfSetThreadPriority Arguments: hpid - Supplies process htid - Supplies thread dwPriority - Supplies the meta priority level to assign to the thread. Return Value: xosdNone: Success --*/ { return CallEM( emfSetThreadPriority, hpid, htid, dwPriority, 0 ); } /* File manipulation */ /* Exception Handling */ XOSD OSDAPI OSDGetExceptionState( HPID hpid, HTID htid, LPEXCEPTION_DESCRIPTION lpExd ) /*++ Routine Description: Given an address of an EXCEPTION_DESC filled in with the exception code look up the code in the table and return a pointer to the real exception info structure for that exception. Arguments: hpid - Supplies process htid - Supplies thread lpExd - A pointer to an exception structure to be filled in. The dwExceptionCode member of this structure must be set in order to use the exfSpecified or exfNext parmeter Return Value: xosdNone - Success --*/ { if (hpid == 0) { // an annoying little hack, forcing htid to be pointer sized: // if hpid is 0, there is no OSDebug bining yet, so htid is really // a pointer to an EMFunc. return ((XOSD (OSDAPI *)(EMF, HPID, HTID, DWORD64, DWORD64))htid) ( emfGetExceptionState, 0, 0, 0, SEPtrTo64(lpExd) ); } else { return CallEM ( emfGetExceptionState, hpid, htid, 0, (DWORD64)lpExd ); } } XOSD OSDAPI OSDSetExceptionState ( HPID hpid, HTID htid, LPEXCEPTION_DESCRIPTION lpExd ) /*++ Routine Description: Set the exception state for the given hpid/htid. First implementation of this API should ignore the HTID parameter as it is intended that it will only be able to Get/Set thread exception states on a per process basis rather than on a per thread basis. Should we choose in the future that there is a need to Get/Set exception states on a per thread basis we can make use of the HTID parameter at that time. At that time an htid of NULL would indicate all threads. Parameters: hpid - Supplies process htid - Supplies thread lpExd - Supplies an exception structure to be Set. Should this exception not be found in the current list of exceptions for the given process/thread it will be added. Return Value: xosdNone: Success --*/ { return CallEM( emfSetExceptionState, hpid, htid, 0, (DWORD64)lpExd ); } /* Message handling */ XOSD OSDAPI OSDGetMessageMap ( HPID hpid, HTID htid, LPMESSAGEMAP* lplpMessageMap ) /*++ Routine Description: Retrieves a table from the EM of the messages that may be used in the process represented by hpid. In the WIN32 implementation, this is a pointer to a static structure, and does not need to be freed or otherwise messed with. Arguments: hpid - Supplies process htid - Supplies thread lplpMessageMap - Returns pointer to MSGMAP structure Return Value: xosdNone for success, other xosd codes describe the cause of failure. If a Win32 EM is associated with hpid, this always succeeds. --*/ { return CallEM ( emfGetMessageMap, hpid, htid, 0, (DWORD64)lplpMessageMap ); } /**** OSDGetMessageMaskMap ****/ XOSD OSDAPI OSDGetMessageMaskMap ( HPID hpid, HTID htid, LPMASKMAP * lplpMaskMap ) /*++ Routine Description: Retrieves a table of message mask descriptions from the EM. The first element in the table represents the least significant bit in the mask. The second element represents the the next bit and so on. uses emfGetMessageMaskMap Arguments: hpid - Supplies process htid - Supplies thread lplpMaskMap - Returns a pointer to MSKMAP Return Value: xosdNone: Sucess --*/ { return CallEM ( emfGetMessageMaskMap, hpid, htid, 0, (DWORD64)lplpMaskMap ); } /* Screen manipulation */ XOSD OSDAPI OSDShowDebuggee ( HPID hpid, DWORD fShow ) /*++ Routine Description: Direct the debug monitor to set or not focus to the debuggee when executing. This should be called if the debug monitor is responsible for setting focus. See mtrcShowDebuggee in the General Target Information section. If the debugger host is responsible for setting focus then the DM will be calling back with a dbcShowDebuggee ( Formerly dbcFlipScreen ) to set focus when executing. uses emfShowDebuggee Arguments: hpid - Supplies process to bring to the foreground fShow - Supplies TRUE if the debug monitor should force the debuggee to have focus when executing, FALSE if not. Return Value: xosdNone: Success --*/ { return CallEM( emfShowDebuggee, hpid, NULL, fShow, 0 ); } /* I/O requests */ XOSD OSDAPI OSDInfoReply ( HPID hpid, HTID htid, LPVOID lpvData, DWORD cbData ) /*++ Routine Description: This routine is to allow the DM to request information from the host. The DM will callback with a dbcInfoRequest with one of the paramaters being a pointer to a string indiciating the request. This routine is use to convey the requested information to the DM. This routine is intended solely for RIP's on Windows/Window NT but can be used for other things. Need to examine subcatagories for types of requests that will be coming from dbcInfoRequest so the host can display and query for information in a non-console fashion. uses emfInfoReply Arguments: hpid - Supplies process htid - Supplies thread lpvData - Supplies pointer to data which is being sent back to the DM cbData - Supplies count in bytes of data being sent. Return Value: xosdNone: Success --*/ { return CallEM( emfInfoReply, hpid, htid, cbData, (DWORD64)lpvData ); } /* OS Specific services */ XOSD OSDAPI OSDGetTaskList ( HPID hpid, LPTASKLIST * lplpTaskList ) /*++ Routine Description: Get a list of processes from the target system. This is intended to give the debugger the ability to browse for a process to debug. uses emfGetTaskList Arguments: hpid - Supplies process. The process is not relevant, only the EM which it is bound to. Hpid may be NULL, and the EM selected will be the oldest native EM loaded. lplpTaskList - Returns the list of items. This will be allocated so that it can be freed with MHFree. Return Value: xosdNone xosdInvalidProc xosdOutOfMemory --*/ { return CallEM ( emfGetTaskList, hpid, NULL, 0, (DWORD64)lplpTaskList ); } XOSD OSDAPI OSDSetDebugMode ( HPID hpid, DBM dbmService, LPVOID lpvData, DWORD cbData ) /*++ Routine Description: If the target supports hard mode/ soft mode switching ( See mtrcHardSoftMode in the General Target Information Section ) then this routine can be called to set the target into hard or soft mode. The data provided is specific to the target machine and the host must be aware of the format of the data. uses emfSetDebugMode Parameters: hpid - Supplies process dbmService - Supplies new mode, either dmbSoftMode or dbmHardMode lpvData - Supplies a pointer to a DBMI cbData - Supplies size of a DBMI structure Return Value: xosdNone: Success --*/ { SDMS sdms; sdms.dbmService = dbmService; sdms.lpvData = lpvData; sdms.cbData = cbData; return CallEM( emfSetDebugMode, hpid, NULL, 0, (DWORD64)&sdms ); } XOSD OSDAPI OSDSystemService ( HPID hpid, HTID htid, SSVC ssvc, LPVOID lpvData, DWORD cbData, LPDWORD lpcbReturn ) /*++ Routine Description: Provide OS specific functionality. Calls the EM service function, requesting emfSystemService. Arguments: hpid - Supplies handle to the process to perform the operation on. Some SSVCs may not apply to a process, but the hpid is required to provide the hook to the EM and TL. htid - Supplies optional thread structure wFunction - Supplies the SSVC function index lpvData - Supplies and returns data. Use of this field depends on the SSVC. cbData - Supplies the number of bytes in the data packet lpcbReturn - Returns the number of bytes written to lpvData Return Value: xosd return value is supplied by the service function. --*/ { XOSD xosd = xosdNone; LPSSS lpsss = (LPSSS) malloc( sizeof( SSS ) + cbData ); DWORD cbr; if ( lpsss == NULL ) { return xosdOutOfMemory; } lpsss->ssvc = ssvc; lpsss->cbSend = cbData; if (cbData) { _fmemcpy ( lpsss->rgbData, lpvData, cbData ); } xosd = CallEM ((EMF) emfSystemService, hpid, htid, sizeof ( SSS ) + cbData, (DWORD64) lpsss ); if (xosd == xosdNone) { cbr = lpsss->cbReturned; if (cbr > cbData) { cbr = cbData; } if (cbr) { _fmemcpy ( lpvData, lpsss->rgbData, cbr); } if (lpcbReturn) { *lpcbReturn = cbr; } } _ffree ( lpsss ); return xosd; } /* * * * Services provided for execution model and transport layer * * * */ XOSD EMCallBackDB ( DBC dbc, HPID hpid, HTID htid, DWORD dwModel, DWORD64 cb, DWORD64 lparam ) /*++ Routine Description: Send callback messages from an execution model to the debugger. The messages get passed through all intervening higher-level execution models. If dbc is a dbco value, it creates the appropriate thread or process and returns the hpid or htid in *lpv. Otherwise, it calls OSDCallbackToEM to query all of the higher level EM's associated with the process. If none of them handled the callback, it then calls the debugger's callback function via OSDDoCallback. Arguments: dbc - Supplies the callback message hpid - Supplies handle to the process htid - Supplies handle to the thread dwModel - Supplies index of EM initiating the callback cb - Supplies the number of bytes pointed to by lpv lparam - Supplies data associated with the callback, Returns data for dbco callbacks. Return Value: xosdNone - Success Any value that can be returned by the debugger or higher level execution models. --*/ { XOSD xosd = xosdNone; // Several threads can execute this, so we protect it with a critical // section. EnterCriticalSection( &CallbackCriticalSection ); switch ( dbc ) { case dbcoCreateThread: xosd = CreateThd ( hpid, htid, &htid ); *( (LPHTID) lparam) = htid; goto Return; //return xosd; case dbcoNewProc: { LPPROCESSINFO lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); HPID hpidT = hpid; LPEMP lpemp = (LPEMP) LLLock ( (HLLE)(lppid->hempNative) ); xosd = CreateProc ( lppid->lpfnsvcCC, lpemp->hem, lppid->htl, &hpid ); *( (LPHPID) lparam) = hpid; LLUnlock ( (HLLE)(lppid->hempNative) ); LLUnlock ( (HLLE)hpidT ); } goto Return; //return xosd; } // hit all of the EMs with the DoCallback, a la CallEM, // hitting the debugger last xosd = OSDDoCallBackToEM ( dbc, hpid, htid, cb, lparam ); // if xosd is xosdPunt then all non-native EMs punted on the notification if ( xosd == xosdPunt ) { xosd = OSDDoCallBack ( dbc, hpid, htid, dwModel, cb, lparam ); } Return: LeaveCriticalSection( &CallbackCriticalSection ); return xosd; } XOSD EMCallBackTL ( TLF wCmd, HPID hpid, DWORD64 cb, DWORD64 lparam ) /*++ Routine Description: Send messages from the execution model (native) to the transport layer. Arguments: wCmd - Supplies the message hpid - Supplies process cb - Supplies the size of the buffer at lpv, in bytes lpv - Supplies or Returns data specific to the command Return Value: xosdNone for success, TL may return other xosd codes to describe failure. --*/ { return CallTL ( wCmd, hpid, cb, lparam ); } XOSD EMCallBackNT ( EMF emf, HPID hpid, HTID htid, DWORD64 cb, DWORD64 lparam ) /*++ Routine Description: Provides native em services to higher level EMs. Sends messages from the execution model (non-native) to the native execution model for the process hpid. Arguments: emf - Supplies the execution model function number hpid - Supplies process htid - Supplies thread cb - Supplies size of buffer at lpv in bytes lparam - Supplies and Returns data for EM service Return Value: xosdNone - Success xosdInvalidPID - hpid is not a handle to a process. Any value that can be returned by the native execution model. --*/ { XOSD xosd = xosdNone; LPPROCESSINFO lppid; HEMP hemp; LPEMP lpemp; lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); if ( lppid == NULL ) { return xosdInvalidParameter; } hemp = lppid->hempNative; LLUnlock ( (HLLE)hpid ); lpemp = (LPEMP) LLLock ( (HLLE)hemp ); xosd = (lpemp->emfunc) ( emf, hpid, htid, cb, lparam ); LLUnlock ( (HLLE)hemp ); return xosd; } XOSD EMCallBackEM ( EMF emf, HPID hpid, HTID htid, DWORD dwModel, DWORD64 cb, DWORD64 lpv ) /*++ Routine Description: Provides EM services to other EMs. Sends messages from one non-native EM to the next EM in the chain for the process hpid. Arguments: emf - Supplies EM function number hpid - Supplies handle to the process htid - Supplies handle to the thread dwModel - Supplies EM # of calling EM cb - Supplies size in bytes of buffer at lpv. lpv - Supplies and Returns data for EM service Return Value: xosd code returned by the EM that handles the service request. --*/ { XOSD xosd = xosdNone; LPPROCESSINFO lppid; HEMP hemp = 0; HEMP hempnext = 0; LPEMP lpemp; assert ( hpid != NULL ); //native ems can never make this callback! assert ( dwModel != CEXM_MDL_native ); lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); if ( lppid == NULL ) { return xosdInvalidParameter; } while ( hemp = (HEMP)LLNext ( (HLLI)(lppid->llemp), (HLLE)hemp ) ) { lpemp = (LPEMP) LLLock ( (HLLE)hemp ); if ( lpemp->model == dwModel ) { hempnext = (HEMP)LLNext ( (HLLI)(lppid->llemp), (HLLE)hemp ); break; } LLUnlock ( (HLLE)hemp ); } assert ( hempnext != 0 ); lpemp = (LPEMP) LLLock ( (HLLE)hempnext ); xosd = (lpemp->emfunc) ( emf, hpid, htid, cb, (DWORD64) lpv ); LLUnlock ( (HLLE)hempnext ); LLUnlock ( (HLLE)hpid ); return xosd; } XOSD TLCallBack ( HPID hpid, DWORD64 cb, DWORD64 lpv ) /*++ Routine Description: Call the native execution model for the process hpid with the package sent from the transport layer. This is how the DM sends things to its native EM. Arguments: hpid - Supplies handle to the process cb - Supplies size in bytes of the packet lpv - Supplies the packet Return Value: xosdNone - Success Any return value that can be generated by a native execution model. --*/ { XOSD xosd = xosdNone; LPEMP lpemp; LPPROCESSINFO lppid; EMFUNC_ODP emfunc; assert ( hpid != NULL ); lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); lpemp = (LPEMP) LLLock ( (HLLE)(lppid->hempNative) ); emfunc = lpemp->emfunc; LLUnlock ( (HLLE)(lppid->hempNative) ); LLUnlock ( (HLLE)hpid ); xosd = (*emfunc)( emfDebugPacket, hpid, NULL, cb, (DWORD64)lpv ); return xosd; } /* * * * Internal Support functions * * * */ /**** CallEM - Call the execution model for hpid:htid **** * * * PURPOSE: * * * * This function multiplexes on the hpid and htid values to call * * the most specific execution model possible. * * * * INPUTS: * * * * emf, wValue, lValue, lpxosd: These are all just passed through * * to the actual execution model service function. * * * * hpid - This is the process that the execution model service * * function is to handle. If it is null, the first execution * * model that was registered with osdebug is called. * * Hpid must not be invalid. * * * * htid - This is the thread that the execution model service * * function is to handle. If it is null, the native execution * * model for the pid is called. Htid must not be invalid. * * * * OUTPUTS: * * * * Return Value - This will return whatever the execution model * * service function that it calls returns. * * * * *lpxosd - The errors returned here are those defined by the * * execution model service function that was called. * * * * IMPLEMENTATION: * * * * Find the most specific em available. This is the current em * * for the pid:tid if both pid & tid are non-Null, the native * * execution model for the pid if the tid is null but the pid is * * non-Null, or the first em installed if both pid & tid are null. * * * * * */ XOSD CallEM( EMF emf, HPID hpid, HTID htid, DWORD64 wValue, DWORD64 lpv ) /*++ Routine Description: Arguments: Return Value: --*/ { XOSD xosd = xosdNone; HEMP hemp; LPPROCESSINFO lppid; LPEMP lpemp; HLLI llemx; if ( hpid == NULL ) { // get oldest native EM. if (llem != NULL) { hemp = (HEMP)LLLast ( llemx = llem ); } else { return(xosdInvalidParameter); } if (hemp == NULL) { return(xosdInvalidParameter); } } else { lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); assert ( lppid != NULL ); hemp = (HEMP)LLNext ( (llemx = lppid->llemp), NULL ); LLUnlock ( (HLLE)hpid ); } lpemp = (LPEMP) LLLock ( (HLLE)hemp ); if (lpemp->emfunc != NULL) { xosd = (lpemp->emfunc)( emf, hpid, htid, wValue, (DWORD64) lpv ); } else { xosd = xosdInvalidParameter; } LLUnlock ( (HLLE)hemp ); if (xosd == xosdPunt) { hemp = (HEMP)LLNext ( llemx, (HLLE)hemp ); do { lpemp = (LPEMP) LLLock ( (HLLE)hemp ); xosd = (lpemp->emfunc) ( emf, hpid, htid, wValue, (DWORD64) lpv ); LLUnlock ( (HLLE)hemp ); } while (xosd == xosdPunt && (hemp = (HEMP)LLNext ( llemx, (HLLE)hemp )) ); if (xosd == xosdPunt) { xosd = xosdUnsupported; } } return xosd; } /**** CallTL - Call the transport layer for hpid **** * * * PURPOSE: * * * * * * INPUTS: * * * * tlf, htid, wValue, lpv - These are all just passed * * through to the actual transport layer service function. * * * * hpid - This is the process that the transport layer service * * function is to handle. If it is null, the first transport * * layer that was registered with osdebug is called. * * Hpid must not be invalid. * * * * OUTPUTS: * * * * Return Value - This will return whatever the transport layer * * service function that it calls returns. * * * * *lpxosd - The errors returned here are those defined by the * * transport layer service function that was called. * * * * IMPLEMENTATION: * * * * Find the most specific tl available. This is the transport * * layer associated with the pid if the pid is non-Null, otherwise * * it is the first transport layer registered with osdebug. * * * * * */ XOSD CallTL ( TLF tlf, HPID hpid, DWORD64 wValue, DWORD64 lpv ) /*++ Routine Description: Call the transport layer associated with hpid, and send it a message and packet. Arguments: tlf - Supplies transport layer function number. hpid - Supplies process. wValue - Supplies function dependent data. lpv - Supplies and Returns data depending on function. Return Value: xosd status code, as returned by the TL. --*/ { XOSD xosd = xosdNone; HTL htl; LPTL lptl; if ( hpid == NULL ) { htl = (HTL)LLNext ( lltl, NULL ); } else { LPPROCESSINFO lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); htl = lppid->htl; LLUnlock ( (HLLE)hpid ); } if (!htl) { // Do NOT assert here! The debugger is allowed to call any // OSDebug function with an HPID which was created with (HTL)0; // if the debugger then calls some OSDebug function which // requires remote communication, the function will not assert, // but will return xosdInvalidParameter. The caller may check the // return code of the OSDebug call with an assertion if he so chooses. xosd = xosdInvalidParameter; } else { lptl = (LPTL) LLLock ( (HLLE)htl ); xosd = lptl->tlfunc ( tlf, hpid, wValue, (DWORD64)lpv ); LLUnlock ( (HLLE)htl ); } return xosd; } /**** OSDDOCALLBACK - Call the debug client with a notification message **** * * * PURPOSE: * * * * INPUTS: * * * * OUTPUTS: * * * * IMPLEMENTATION: * * * * * */ XOSD OSDDoCallBack ( DWORD wCommand, HPID hpid, HTID htid, DWORD dwModel, DWORD64 wValue, DWORD64 lValue ) /*++ Routine Description: Arguments: Return Value: --*/ { XOSD xosd = xosdNone; LPPROCESSINFO lppid; assert ( hpid != NULL ); lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); switch ( wCommand ) { case dbcBpt: case dbcProcTerm: case dbcThreadTerm: case dbcException: case dbcWatchPoint: case dbcStep: case dbcEntryPoint: if ( lppid->lastmodel != dwModel ) { // model has changed, issue dbcEmChange notification xosd = lppid->lpfnsvcCC ( dbcEmChange, hpid, htid, dwModel, 0 ); lppid->lastmodel = dwModel; } break; default: // same model as before, do nothing break; } xosd = lppid->lpfnsvcCC (( USHORT ) wCommand, hpid, htid, wValue, lValue ); LLUnlock ( (HLLE)hpid ); switch ( wCommand ) { case dbcDeleteThread: // The shell will call OSDDestroyTID(), instead of // us doing it here. break; case dbcProcTerm: // same here; the shell calls OSDDestroyPID() when it // is finished using the structures. break; } return xosd; } XOSD OSDDoCallBackToEM ( DBC wCommand, HPID hpid, HTID htid, DWORD64 wValue, DWORD64 lValue ) /*++ Routine Description: Call all of the non-native EMs with a notification message. Arguments: wCommand - Supplies the message hpid - Supplies process htid - Supplies thread wValue - Supplies message dependent data lValue - Supplies message dependent data Return Value: An xosd status code. xosdPunt means it was not handled by any EM. If an EM handles it, xosdNone or a failure status will be returned. --*/ { XOSD xosd = xosdPunt; HEMP hemp; LPEMP lpemp; LPPROCESSINFO lppid; DBCPK dbcpk; assert ( hpid != NULL ); // get handle to the native EM lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); if ( lppid->fNative ) { LLUnlock((HLLE) hpid ); // native only mode, return return xosd; } LLUnlock ( (HLLE)hpid ); dbcpk.dbc = wCommand; dbcpk.hpid = hpid; dbcpk.htid = htid; dbcpk.wValue = wValue; dbcpk.lValue = lValue; // M00BUG - this is not correct. we should be hitting the ems // in the reverse order that they are in the list. // This will bite us when we move to multiple nms hemp = NULL; while ( xosd == xosdPunt ) { if ( ! ( hemp = (HEMP)LLNext ( lppid->llemp, (HLLE)hemp ) ) ) { return xosd; } lpemp = (LPEMP) LLLock ( (HLLE)hemp ); if ( lpemp->emtype == emNative ) { LLUnlock ( (HLLE)hemp ); return xosd; } xosd = (lpemp->emfunc) ( emfDbc, hpid, htid, sizeof(DBCPK), (DWORD64)&dbcpk ); LLUnlock ( (HLLE)hemp ); } return xosd; } XOSD OSDGetLastTLError( HTL htl, HPID hpid, LPSTR Buffer, ULONG Length ) /*++ Routine Description: Get the last error from the TL. --*/ { LPTL lptl; XOSD xosd; assert (htl); lptl = (LPTL) LLLock ((HLLE)htl); xosd = lptl->tlfunc (tlfGetLastError, hpid, Length, (DWORD64)Buffer); LLUnlock ((HLLE) htl); return xosd; } XOSD OSDGetTimeStamp( HPID hpid, HTID htid, LPTSTR ImageName, PULONG TimeStamp, PULONG CheckSum ) { XOSD xosd; TCS tcs = {0}; tcs.ImageName = ImageName; xosd = CallEM (emfGetTimeStamp, hpid, htid, sizeof (tcs), (DWORD64)&tcs); if (TimeStamp) { *TimeStamp = tcs.TimeStamp; } if (CheckSum) { *CheckSum = tcs.CheckSum; } return xosd; } /**** CREATEPROC - Create a process data struct and add to proc list **** * * * PURPOSE: * * * * INPUTS: * * * * OUTPUTS: * * * * IMPLEMENTATION: * * * * * */ XOSD CreateProc ( LPFNSVC lpfnsvc, HEM hem, HTL htl, LPHPID lphpid ) { XOSD xosd = xosdNone; HPID hpid; LPPROCESSINFO lppid; HEMP hemp; LPEMP lpemp; HEM hodem = NULL; LPEMS lpem; HPID hpidem = NULL; LPHPID lphpidt; hpid = (HPID)LLCreate ((HLLI) llpid ); if ( hpid == NULL ) { return xosdOutOfMemory; } LLAdd ( (HLLI)llpid, (HLLE)hpid ); lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); lppid->htl = htl; lppid->fNative = FALSE; lppid->lastmodel = CEXM_MDL_native; lppid->lpfnsvcCC = lpfnsvc; lppid->lltid = LLInit ( sizeof ( THREADINFO ), llfNull, NULL, NULL ); lppid->llemp = LLInit ( sizeof ( EMP ), llfNull, EMPKill, NULL ); if ( lppid->llemp == 0 || lppid->lltid == 0 ) { xosd = xosdOutOfMemory; } // create llem in lppid while ( hodem = (HEM)LLNext ( (HLLI)llem, (HLLE)hodem ) ) { hemp = (HEMP)LLCreate ( (HLLI)(lppid->llemp) ); if ( hemp == NULL ) { return xosdOutOfMemory; } lpem = (LPEMS) LLLock ( (HLLE)hodem ); lpemp = (LPEMP) LLLock ( (HLLE)hemp ); // copy relevant info to hpid's emp lpemp->hem = hodem; lpemp->emfunc = lpem->emfunc; lpemp->emtype = lpem->emtype; if ( ! lpem->emtype && hodem == hem ) { lppid->hempNative = hemp; } lpemp->model = lpem->model; LLUnlock ( (HLLE)hemp ); LLUnlock ( (HLLE)hodem ); LLAdd ( (HLLI)(lppid->llemp), (HLLE)hemp ); } // add the hpid to all em's list of hpids hemp = NULL; hodem = NULL; while ( hodem = (HEM)LLNext ( (HLLI)llem, (HLLE)hodem ) ) { lpem = (LPEMS) LLLock ( (HLLE)hodem ); hpidem = (HPID)LLCreate ( (HLLI)(lpem->llhpid) ); if ( hpidem == NULL ) { LLUnlock((HLLE)hpid); return xosdOutOfMemory; } lphpidt = (LPHPID) LLLock ( (HLLE)hpidem ); // puts hpid in node *lphpidt = hpid; LLUnlock ( (HLLE)hpidem ); LLUnlock ( (HLLE)hodem ); LLAdd ( (HLLI)(lpem->llhpid), (HLLE)hpidem ); } // clean up LLUnlock ( (HLLE)hpid ); CheckErr ( xosd ); *lphpid = hpid; return xosd; } /**** CREATETHD - Create a thread data struct & add to process **** * * * PURPOSE: * * * * INPUTS: * * * * OUTPUTS: * * * * IMPLEMENTATION: * * * * * */ XOSD CreateThd ( HPID hpid, HTID htid, LPHTID lphtid ) { HTID htidT; LPTHREADINFO lptidT; LPPROCESSINFO lppid; Unreferenced( htid ); lppid = (LPPROCESSINFO) LLLock ( (HLLE)hpid ); htidT = (HTID)LLCreate ( (HLLI)(lppid->lltid) ); if ( htidT == NULL ) { LLUnlock ( (HLLE)hpid ); return xosdOutOfMemory; } LLAdd((HLLI)(lppid->lltid), (HLLE)htidT); lptidT = (LPTHREADINFO) LLLock ( (HLLE)htidT ); lptidT->hpid = hpid; LLUnlock ( (HLLE)htidT ); LLUnlock ( (HLLE)hpid ); *lphtid = htidT; return xosdNone; } /* * * * Kill and compare functions for the various lists used by osdebug * * * */ void ODPDKill ( LPVOID lpv ) { LPPROCESSINFO lppid = (LPPROCESSINFO) lpv; LLDestroy ( lppid->llemp ); LLDestroy ( lppid->lltid ); } void EMKill ( LPVOID lpv ) { LPEMS lpem = (LPEMS) lpv; LLDestroy ( lpem->llhpid ); } void EMPKill ( LPVOID lpv ) { Unreferenced( lpv ); return; } void TLKill ( LPVOID lpv ) { // nothing to destroy in the current implementation } int EMHpidCmp ( LPVOID lpv1, LPVOID lpv2, LONG lParam ) { Unreferenced( lParam ); if ( *( (LPHPID) lpv1) == *( (LPHPID) lpv2 ) ) { return fCmpEQ; } else { return fCmpLT; } } #ifdef __cplusplus } // cplusplus #endif