Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1975 lines
48 KiB

/**
*
* SH.C - Symbol Handler: Low level management
*
* Copyright (C)1991, Microsoft Corporation
*
* Purpose: Provide layer between SH functions and linked list manager.
*
* Notes: Also included are fixup/unfixup functions until OSDEBUG
* is on-line.
*
* DESCRIPTION OF INITIALIZATION CALLING SEQUENCE
* ----------------------------------------------
* During startup of debugging a user application, a large number of
* Symbol Handler functions need to be called. Here is the order in
* which it makes sense to call them, and what they do:
*
* (1) SHCreateProcess
* To create a handle for the new debuggee process which
* is being debugged.
* (2) SHSetHpid
* This doesn't have to be called right now, but it should
* be called as soon as the HPID of the debuggee is known.
* (3) SHAddDll
* Call this a number of times: once for the EXE which is
* being debugged, and once for each DLL being debugged
* (e.g., in CodeView, for all the /L xxxx.DLL files).
* This doesn't load the symbolic information off disk;
* it just lets the SH know that these files are being
* debugged.
* (4) SHAddDllsToProcess
* This associates all added DLLs with the added EXE, so
* the SH knows that they are all being debugged as part
* of the same process.
* (5) SHLoadDll
* Call this once for the EXE and each DLL, passing FALSE
* for the fLoading parameter. This actually loads the
* symbolic information off disk.
* (6) Start debuggee running
* (7) SHLoadDll
* Call this for EXEs/DLLs as notifications are received
* indicating that they have been loaded into memory.
* This time, pass TRUE for the fLoading parameter.
* (8) SHUnloadDll
* Call this for EXEs/DLLs as notifications are received
* indicating that they have been unloaded from memory.
* This does not actually unload the symbolic information.
*
*
* Revision History:
* [untagged] 21-Oct-93 MarkBro
*
* Added SHUnloadSymbolHandler for NB10 notifications. Can
* also be used in the future to free up memory so symbol
* handler doesn't need to be free'd and reloaded.
*
* [02] 05-Mar-93 DanS
*
* Added critical section for win32 build.
*
* [01] 31-dec-91 DavidGra
*
* Fix bug with far addresses being unfixed up in the disassembler
* when passed through the symbol handler.
*
* [00] 11-dec-91 DavidGra
*
* Make SHAddDll return an she indicating file not found or
* out of memory or success. Create SHAddDllExt to handle
* the internal support and thunk it with SHAddDll to keep
* the API the same.
*
*/
#include "shinc.h"
#pragma hdrstop
#include "shwin32.h"
#include <io.h>
#include <fcntl.h>
#include <share.h>
#ifndef WIN32
#pragma optimize("",off)
#endif
extern CHAR nosymbols;
// This is required global information
HLLI hlliPds = (HLLI)NULL; // List of processes
HPDS hpdsCur = (HPDS)NULL; // Current process which is being debugged
static HLLI hlliExgDll;
static HLLI hlliExgExe;
static INT fhCur = -1 ;
static char rgchFile [ _MAX_CVPATH ];
#define CSEGMAX 255
// Our own prototypes
VOID FAR PASCAL LOADDS KillPdsNode ( LPV );
VOID FAR PASCAL LOADDS KillExsNode ( LPV );
int FAR PASCAL LOADDS CmpExsNode( LPV, LPV, LONG );
VOID FAR PASCAL KillMdsNode( LPV );
int FAR PASCAL LOADDS CmpMdsNode( LPV, LPV, LONG );
int PASCAL SYLoadSelectorTbl( HEXE, WORD );
VOID SYFixupSym( HEXE, int );
WORD PASCAL GetSelectorFromEmi( WORD, int );
HEXE PASCAL SHHexeFromEmi ( WORD emi );
VOID FAR PASCAL LOADDS KillExgNode ( LPV );
int FAR PASCAL LOADDS CmpExgNode ( LPV, LPV, LONG );
HPID hpidCurr = 0;
#define AllocAlign(cb) ( (LPV) (((ULONG) MHAlloc(cb+1) + 1) & 0xFFFFFFFE) )
#ifdef HOST32
#undef MHFreeHuge
#define MHFreeHuge(x) MHFree(x)
#endif // HOST32
/** SHCreateProcess
*
* Purpose: Create a handle for a new debuggee process. The debuggee
* process doesn't actually have to be running yet; this is
* just an abstract handle for the Symbol Handler's use, so
* that it can keep track of symbols for multiple debuggee
* processes at the same time.
*
* Input:
* None
*
* Output:
* Returns an HPDS, a handle to the new process, or 0 for failure.
*
* Exceptions:
*
* Notes:
*
*/
HPDS LOADDS PASCAL SHCreateProcess ( VOID ) {
HPDS hpds = SHFAddNewPds ( );
SHChangeProcess ( hpds );
return hpds;
}
/** SHSetHpid
*
* Purpose: Tell the SH what HPID to assign to the current process.
* Each debuggee process has an HPID, and this call associates
* an HPID with a HPDS in the SH.
*
* Input:
* hpid The HPID to make current.
*
* Output:
* None
*
* Exceptions:
*
* Notes:
*
*/
VOID LOADDS PASCAL SHSetHpid ( HPID hpid ) {
LPPDS lppds = LLLock ( hpdsCur );
lppds->hpid = hpidCurr = hpid;
LLUnlock ( hpdsCur );
}
/** SHDeleteProcess
*
* Purpose: Delete a debuggee process handle (HPDS). Removes it from
* the SH's internal list of HPDS's.
*
* Input:
* hpds The HPDS to delete.
*
* Output:
* TRUE for success, FALSE for failure.
*
* Exceptions:
*
* Notes:
*
*/
BOOL LOADDS PASCAL SHDeleteProcess ( HPDS hpds ) {
HPDS hpdsT = hpdsCur;
HPID hpidT = hpidCurr;
SHChangeProcess ( hpds );
LLDelete ( hlliPds, hpdsCur );
if ( hpdsT != hpdsCur ) {
hpdsCur = hpdsT;
hpidCurr = hpidT;
}
else {
hpdsCur = LLNext ( hlliPds, (HPDS) NULL );
if ( hpdsCur != 0 ) {
LPPDS lppds = LLLock ( hpdsCur );
hpidCurr = lppds->hpid;
LLUnlock ( hpdsCur );
}
}
return TRUE;
}
/** SHChangeProcess
*
* Purpose: Change the current debuggee process handle (HPDS). The SH
* can maintain symbols for multiple processes; this sets which
* one is current, so that symbol lookups will be done on the
* right set of symbolic information.
*
* Input:
* hpds The HPDS to make current.
*
* Output:
* None
*
* Exceptions:
*
* Notes:
*
*/
VOID LOADDS PASCAL SHChangeProcess ( HPDS hpds ) {
LPPDS lppds;
#if defined ( OS2 ) || defined ( WIN32 )
hpdsCur = hpds;
lppds = LLLock ( hpdsCur );
hpidCurr = lppds->hpid;
LLUnlock ( hpdsCur );
#else // !OS2 || !WIN32
Unreferenced ( hpds );
Unreferenced ( lppds );
#endif // !OS2 || !WIN32
}
BOOL FInitLists ( VOID ) {
// Create the pds list
hlliPds = LLInit ( sizeof ( PDS ), 0, KillPdsNode, CmpPdsNode );
hlliExgDll = LLInit ( sizeof ( EXG ), 0, KillExgNode, CmpExgNode );
hlliExgExe = LLInit ( sizeof ( EXG ), 0, KillExgNode, CmpExgNode );
return hlliPds && hlliExgDll && hlliExgExe;
}
/** KillPdsNode
*
* Purpose: Destroy private contents of a process node
*
* Input: FAR pointer to node data
*
* Output: N/A
*
* Exceptions: none.
*
* Notes: Only data in the pds structure to destroy is a list
* of exe's.
*
*/
VOID FAR PASCAL LOADDS KillPdsNode ( LPV lpvPdsNode ) {
LPPDS lppds = lpvPdsNode;
LLDestroy ( lppds->hlliExs );
}
int FAR PASCAL LOADDS CmpPdsNode ( LPPDS lppds, HPID FAR *lphpid, LONG l ) {
Unreferenced ( l );
return !( lppds->hpid == *lphpid );
}
void KillAlm(LPALM lpalm) {
if ( lpalm ) {
int ifop;
int cfop = (lpalm->cb + lpalm->cbBlock - 1) / lpalm->cbBlock;
for (ifop = 0; ifop < cfop; ++ifop) {
if ((lpalm->rgufop[ifop].lfo & 1) == 0) {
MHFree(lpalm->rgufop[ifop].lpv);
}
}
MHFree ( lpalm );
}
}
void KillSht(LPSHT lpsht) {
if ( lpsht ) {
if ( lpsht->rgib ) {
MHFree ( lpsht->rgib );
}
if ( lpsht->rgcib ) {
MHFree ( lpsht->rgcib );
}
KillAlm ( lpsht->lpalm );
}
}
void KillGst(LPGST lpgst) {
if ( lpgst ) {
KillSht ( &lpgst->shtName );
KillSht ( &lpgst->shtAddr );
KillAlm ( lpgst->lpalm );
}
}
/** KillExgNode
*
* Purpose: Destroy information contained in an exe node
*
* Input: far pointer to node data
*
* Output: N/A
*
* Exceptions: none.
*
* Notes:
*
*/
VOID FAR PASCAL LOADDS KillExgNode ( LPV lpvExgNode ) {
LPEXG lpexg = lpvExgNode;
// Destroy the module list
LLDestroy ( lpexg->hlliMds );
// Destroy the exsr list
LLDestroy ( lpexg->hlliExr );
// Free up memory associated with .exe/.com file name
if ( lpexg->lszName ) {
MHFree ( lpexg->lszName );
}
// Debug info file name
if ( lpexg->lszDebug && ( lpexg->lszDebug != lpexg->lszName ) ) {
MHFree ( lpexg->lszDebug );
}
// Pdb name
if ( lpexg->lszPdbName ) {
MHFree ( lpexg->lszPdbName );
}
// Type table
if ( lpexg->lpalmTypes ) {
MHFreeHuge( lpexg->lpalmTypes );
}
// Free up memory associated type info
if ( lpexg->rgitd ) {
MHFreeHuge ( lpexg->rgitd );
}
// Source module information
if ( lpexg->lpefi ) {
MHFree ( lpexg->lpefi );
}
// Free up memory associated with the dll name
if ( lpexg->lszModule ) {
MHFree ( lpexg->lszModule );
}
// OSDebug 4 FPO info
if ( lpexg->debugData.lpFpo ) {
MHFree( lpexg->debugData.lpFpo );
}
if ( lpexg->debugData.lpIrfe ) {
MHFree( lpexg->debugData.lpIrfe );
}
if ( lpexg->debugData.lpOmapTo ) {
MHFree ( lpexg->debugData.lpOmapTo );
}
if ( lpexg->debugData.lpOmapFrom ) {
MHFree ( lpexg->debugData.lpOmapFrom );
}
if ( lpexg->lpgsi ) {
MHFree ( lpexg->lpgsi );
}
// module map info
if ( lpexg->lpsgd ) {
MHFree ( lpexg->lpsgd );
}
if ( lpexg->lpsge ) {
MHFreeHuge ( lpexg->lpsge );
}
// module list
if ( lpexg->rghmod ) {
MHFree ( lpexg->rghmod );
}
KillGst(&lpexg->gstPublics);
KillGst(&lpexg->gstGlobals);
KillGst(&lpexg->gstStatics);
// If there's PDB info, clean up and close
if ( lpexg->ppdb ) {
if (lpexg->pgsiPubs) {
if (!GSIClose(lpexg->pgsiPubs)) {
assert(FALSE);
}
lpexg->pgsiPubs = 0;
}
if (lpexg->pgsiGlobs) {
if (!GSIClose(lpexg->pgsiGlobs)) {
assert(FALSE);
}
lpexg->pgsiGlobs = 0;
}
if (lpexg->pdbi) {
if (!DBIClose(lpexg->pdbi)) {
assert(FALSE);
}
lpexg->pdbi = 0;
}
if (lpexg->ptpi) {
if (!TypesClose(lpexg->ptpi)) {
assert(FALSE);
}
lpexg->ptpi = 0;
}
if (!PDBClose(lpexg->ppdb)) {
assert(FALSE);
}
lpexg->ppdb = 0;
}
}
/** CmpExgNode
*
* Purpose: Compare global exe nodes
*
* Input: far pointer to node data
*
* Output: N/A
*
* Exceptions: none.
*
* Notes:
*
*/
int FAR PASCAL LOADDS CmpExgNode ( LPV lpv1, LPV lpv2, LONG lParam ) {
LPEXG lpexg1 = lpv1;
LSZ lsz = lpv2;
Unreferenced ( lParam );
return _ftcsicmp ( lpexg1->lszName, lsz );
}
/** KillExsNode
*
* Purpose: Destroy information contained in an exe node
*
* Input: far pointer to node data
*
* Output: N/A
*
* Exceptions: none.
*
* Notes:
*
*/
VOID FAR PASCAL LOADDS KillExsNode ( LPV lpvExsNode ) {
LPEXS lpexs = lpvExsNode;
LPEXG lpexg = LLLock ( lpexs->hexg );
HLLE hlle = LLFind ( lpexg->hlliExr, 0, &hpdsCur, 0L );
if ( hlle ) {
LLRemove ( lpexg->hlliExr, hlle );
}
LLUnlock ( lpexs->hexg );
}
/** KillMdsNode
*
* Purpose: Free up memory allocations associated with node
*
* Input: FAR pointer to the mds node
*
* Output: N/A
*
* Exceptions: none.
*
* Notes: This needs to be filled in?!?
*
*/
VOID FAR PASCAL KillMdsNode ( LPV lpvMdsNode ) {
LPMDS lpmds = (LPMDS)lpvMdsNode;
// free( pSrcLn )
// free( symbols )
// free( types )
// free( agitd )
// free( name )
if ( lpmds->name ) {
MHFree ( lpmds->name );
}
if ( lpmds->lpsgc ) {
MHFree ( lpmds->lpsgc );
}
if ( lpmds->symbols ) {
MHFree ( lpmds->symbols );
}
if ( lpmds->hst ) {
MHFree ( lpmds->hst );
}
if ( lpmds->pmod ) {
if ( !ModClose( lpmds->pmod ) ) {
assert( FALSE );
}
lpmds->pmod = 0;
}
}
/** CmpMdsNode
*
* Purpose: To compare two mds nodes.
*
* Input:
* lpv1 far pointer to first node
* lpv2 far pointer to second node
* lParam comparison type ( MDS_INDEX is only valid one, for now)
*
* Output: Returns zero if imds are equal, else non-zero
*
* Exceptions:
*
* Notes:
*
*/
int FAR PASCAL LOADDS CmpMdsNode ( LPV lpv1, LPV lpv2, LONG lParam ) {
LPMDS lpmds1 = lpv1;
LPMDS lpmds2 = lpv2;
assert ( lParam == MDS_INDEX );
return lpmds1->imds != lpmds2->imds;
}
int FAR PASCAL LOADDS CmpExrNode ( LPV lpv1, LPV lpv2, LONG lParam ) {
LPEXR lpexr = lpv1;
HPDS FAR *lphpds = lpv2;
Unreferenced( lParam );
return !(lpexr->hpds == *lphpds);
}
/** SHHexgFromHmod
*
* Purpose: Get the hexg from the specified mds handle
*
* Input: handle to a VALID mds node
*
* Output: handle to the hmod's parent (hexe)
*
* Exceptions:
*
* Notes:
*
*/
HEXG PASCAL SHHexgFromHmod ( HMOD hmod ) {
HEXG hexg;
assert ( hmod );
hexg = ((LPMDS)LLLock(hmod))->hexg;
LLUnlock( hmod );
return hexg;
}
/** SHHexeFromHmod
*
* Purpose: Get the hexe from the specified module handle
*
* Input: handle to a VALID mds node
*
* Output: handle to the hmod's parent (hexe)
*
* Exceptions:
*
* Notes:
*
*/
HEXE LOADDS PASCAL SHHexeFromHmod ( HMOD hmod ) {
static HMOD hmodSave = (HMOD) NULL;
static HPDS hpdsSave = (HPDS) NULL;
static HEXE hexe = (HEXE) NULL;
HEXG hexg;
HEXR hexr;
LPEXG lpexg;
LPEXR lpexr;
if ( hmod == (HMOD)NULL ) {
return hexeNull;
}
else if ( hmod != hmodSave || hpdsSave != hpdsCur ) {
hexg = ( (LPMDS) LLLock ( hmod ) )->hexg;
LLUnlock ( hmod );
if ( hexg != (HEXG) NULL ) {
lpexg = LLLock ( hexg );
hexr = LLFind ( lpexg->hlliExr, 0, &hpdsCur, 0L );
LLUnlock ( hexg );
if ( hexr != (HEXR) NULL ) {
lpexr = LLLock ( hexr );
hexe = lpexr->hexe;
LLUnlock ( hexr );
hmodSave = hmod;
hpdsSave = hpdsCur;
}
}
}
return hexe;
}
/** SHLszFromHexe
*
* Purpose: Get the exe name for a specified hexe
*
* Input: handle to the exs node
*
* Output: far pointer to the exe's full path-name file
*
* Exceptions:
*
* Notes:
*
*/
LSZ LOADDS PASCAL SHGetExeName ( HEXE hexe ) {
LSZ lsz = NULL;
HEXG hexg;
assert( hpdsCur && hexe );
if (!hexe) {
return( lsz );
}
hexg = ( (LPEXS) LLLock ( hexe ) )->hexg;
LLUnlock ( hexe );
lsz = ( (LPEXG) LLLock ( hexg ) )->lszName;
LLUnlock ( hexg );
return lsz;
}
/** SHGetNextExe
*
* Purpose: Get the handle to the next node in the exe list for the CURRENT
* process. If the hexe is null, then get the first one in the list.
*
* Input: handle to the "previous" node. If null, get the first one in
* the exs list.
*
* Output: Returns a handle to the next node. Returns NULL if the end of
* the list is reached (ie: hexe is last node in the list)
*
* Exceptions:
*
* Notes:
*
*/
HEXE LOADDS PASCAL SHGetNextExe ( HEXE hexe ) {
HEXE hexeRet;
HLLI hlli;
// this assert isn't valid for the IDE -- there might not be an exe
// loaded when we try to bind a watch for instance -- the watch might have
// a context as part of it and then this will fail
//
// assert( hpdsCur ); // No process, no exe --- simple!
if (!hpdsCur) // check for non-null process [rm]
return (HEXE)NULL;
hlli = ( (LPPDS) LLLock ( hpdsCur ) )->hlliExs;
hexeRet = LLNext ( hlli, hexe );
LLUnlock ( hpdsCur );
return hexeRet;
}
#pragma message ("change header comments for shhmodgetnext")
/** SHHmodGetNext
*
* Purpose: Retrieve the next module in the list. If a hmod is specified,
* get the next in the list. If the hmod is NULL, then get the first module
* in the exe. If no hexe is specified, then get the first exe in the list.
*
* Input:
* hexe hexe containing list of hmod's. If NULL, get first in CURRENT
* process list.
* hmod module to get next in list. If NULL, get first in the list.
*
* Output: Returns an hmod of the next one in the list. NULL if the end of
* the list is reached.
*
* Exceptions:
*
* Notes:
*
*/
HMOD LOADDS PASCAL SHHmodGetNext ( HEXE hexe, HMOD hmod ) {
HMOD hmodRet = 0;
// In the IDE we try to bind the watches before the exe is actually
// loaded. Instead of asserting we should just return no MOD to
// indicate failure.
//assert ( hpdsCur ); // Must have a current process!
if ( !hpdsCur )
return hmodRet;
if ( hmod ) {
hmodRet = LLNext ( (HLLI) NULL, hmod );
}
else {
if ( hexe ) {
HEXG hexg = ( (LPEXS) LLLock ( hexe ) )->hexg;
LLUnlock ( hexe );
hmodRet = LLNext (
( (LPEXG) LLLock ( hexg ) )->hlliMds,
hmod
);
LLUnlock ( hexg );
}
}
return hmodRet;
}
/** SHFAddNewPds
*
* Purpose: Create a new process node and make it current!
*
* Input: Word value to identify pds indexing
*
* Output: Non-zero if successful, else zero.
*
* Exceptions:
*
* Notes: Creates a new node and initializes private list of exe's.
*
*/
HPDS PASCAL SHFAddNewPds ( void ) {
LPPDS lppds;
#if defined ( OS2 ) || defined ( WIN32 )
HPDS hpds = 0;
if ( hpds = (HIND) LLCreate ( hlliPds ) ) {
lppds = (LPPDS) LLLock ( hpds );
lppds->hlliExs = LLInit ( sizeof( EXS ), 0, KillExsNode, NULL );
// If the list create failed, destroy the node and return failure
if ( !lppds->hlliExs ) {
LLUnlock ( hpds );
MMFree ( (HDEP) hpds );
}
// Otherwise, add the pds to the list and return success
else {
LLUnlock( hpds );
LLAdd ( hlliPds, hpds );
hpdsCur = hpds;
}
}
return hpds;
#else //!OS2 || !WIN32
if ( hpdsCur == (HPDS)NULL ) {
hpdsCur = (HIND) LLCreate ( hlliPds );
lppds = (LPPDS) LLLock ( hpdsCur );
lppds->hlliExs = LLInit ( sizeof( EXS ), 0, KillExsNode, NULL );
// If the list create failed, destroy the node and return failure
if ( !lppds->hlliExs ) {
LLUnlock ( hpdsCur );
MMFree ( (HDEP) hpdsCur );
hpdsCur = (HPDS)NULL;
}
// Otherwise, add the pds to the list and return success
else {
LLUnlock( hpdsCur );
LLAdd ( hlliPds, hpdsCur );
}
}
return hpdsCur;
#endif // !OS2 || !WIN32
}
/** SHHexeAddNew
*
* Purpose: Create and initialize an exg node.
*
* Input:
* hpds Process hexe is assoceiated with
* hexg Symbol information for the exe.
*
* Output: Returns hexg of newly created node. NULL if OOM.
*
* Exceptions:
*
* Notes:
*
*/
HEXE PASCAL SHHexeAddNew ( HPDS hpds, HEXG hexg ) {
HEXE hexe;
LPEXS lpexs;
HLLI hlli;
BOOL fFound = FALSE;
if ( !hpds ) {
hpds = hpdsCur;
}
hlli = ((LPPDS)LLLock( hpds ))->hlliExs;
// Ensure that the hexg isn't already in the list
hexe = hexeNull;
while( !fFound && ( hexe = LLNext( hlli, hexe ) ) ) {
LPEXS lpexs = (LPEXS)LLLock( hexe );
fFound = (BOOL)( lpexs->hexg == hexg );
LLUnlock( hexe );
}
if ( !hexe && ( hexe = LLCreate ( hlli ) ) ) {
LPEXG lpexg = LLLock ( hexg );
HEXR hexr = LLCreate ( lpexg->hlliExr );
LPEXR lpexr = LLLock ( hexr );
lpexr->hpds = hpds;
lpexr->hexe = hexe;
LLUnlock ( hexr );
LLAdd ( lpexg->hlliExr, hexr );
lpexs = LLLock ( hexe );
lpexs->hexg = hexg;
LLUnlock ( hexg );
lpexs->hpds = hpdsCur;
LLAdd ( hlli, hexe );
LLUnlock ( hexe );
}
LLUnlock ( hpds );
return hexe;
}
/** SHHexeRemove
*
* Purpose: Remove an Hexg node
*
* Input:
* hpds Process hexe is assoceiated with
* hexg Symbol information for the exe.
*
* Output: Returns hexg of newly created node. NULL if OOM.
*
* Exceptions:
*
* Notes:
*
*/
VOID PASCAL SHHexeRemove ( HPDS hpds, HEXG hexg, HEXE hexe ) {
HLLI hlli;
LPEXG lpexg;
HLLE hlle;
assert ( hexe );
assert ( hexg );
if ( !hpds ) {
hpds = hpdsCur;
}
// remove from the process list
if ( hexe ) {
hlli = ((LPPDS)LLLock( hpds ))->hlliExs;
LLDelete ( hlli, hexe );
LLUnlock ( hpds );
}
// remove the hexr from the hlliExr list
if ( hexg ) {
lpexg = LLLock ( hexg );
hlle = LLFind ( lpexg->hlliExr, 0, &hpds, 0L );
LLDelete ( lpexg->hlliExr, hlle );
LLUnlock ( hexg );
}
}
/** SHAddDll
*
* Purpose: Notify the SH about an EXE/DLL for which symbolic information
* will need to be loaded later.
*
* During the startup of a debuggee application, this function
* will be called once for the EXE, and once for each DLL that
* is used by the EXE. After making these calls,
* SHAddDllsToProcess will be called to associate those DLLs
* with that EXE.
*
* See the comments at the top of this file for more on when
* this function should be called.
*
* Input:
* lsz Fully qualified path/file specification.
* fDll TRUE if this is a DLL, FALSE if it is an EXE.
*
* Output:
*
* Returns nonzero for success, zero for out of memory.
*
* Exceptions:
*
* Notes:
* This function does NOT actually load the symbolic information;
* SHLoadDll does that.
*
*/
SHE LOADDS PASCAL SHAddDll ( LSZ lsz, BOOL fDll ) { // [00]
HEXG hexg = hexgNull; // [00]
SHE sheRet; // [02]
// [02]
SHEnterCritSection(); // [02]
// [02]
sheRet = SHAddDllExt ( lsz, fDll, TRUE, &hexg ); // [00]
// [02]
SHLeaveCritSection(); // [02]
// [02]
return sheRet; // [02]
} // [00]
/** SHAddDllExt
*
* Purpose: Notify the SH about an EXE/DLL for which symbolic information
* will need to be loaded later.
*
* During the startup of a debuggee application, this function
* will be called once for the EXE, and once for each DLL that
* is used by the EXE. After making these calls,
* SHAddDllsToProcess will be called to associate those DLLs
* with that EXE.
*
* See the comments at the top of this file for more on when
* this function should be called.
*
* Input:
* lsz Fully qualified path/file specification.
* fDll TRUE if this is a DLL, FALSE if it is an EXE.
* fMustExist TRUE if success requires that the dll must be found
* i.e. the user asked for symbol info for this dll
* and would expect a warning if it isn't found.
*
* Output:
* [Public interface]
* Returns nonzero for success, zero for out of memory.
*
* [Private SAPI interface]
* Returns HEXG of newly created node, or NULL if out of memory.
*
* Exceptions:
*
* Notes:
* This function does NOT actually load the symbolic information;
* SHLoadDll does that.
*
* This function is used internally, AND it is also exported
* to the outside world. When exported, the return value should
* just be considered a BOOL: zero means out of memory, nonzero
* means success.
*
*/
SHE PASCAL SHAddDllExt ( // [00]
LSZ lsz, // [00]
BOOL fDll, // [00]
BOOL fMustExist, // [00]
HEXG FAR *lphexg // [00]
) { // [00]
HEXG hexg;
LPEXG lpexg;
CHAR szPath [ _MAX_CVPATH ];
struct _stat statT;
HLLI llexg;
HLLI llexgOther;
if ( fDll ) {
llexg = hlliExgDll;
llexgOther = hlliExgExe;
}
else {
llexg = hlliExgExe;
llexgOther = hlliExgDll;
}
if ( fDll ) {
CHAR szDir [ _MAX_DRIVE + _MAX_CVDIR ];
CHAR szFile [ _MAX_CVPATH ];
CHAR szExt [ _MAX_CVEXT ];
CHAR * lpszPath;
SHSplitPath ( lsz, szDir, szDir + _MAX_DRIVE, szFile, szExt );
if ( *szExt ) {
_ftcscat ( szFile, szExt );
}
else {
_ftcscat (szFile, ".DLL");
}
_ftcscat ( szDir, szDir + _MAX_DRIVE );
_fullpath ( szPath, szDir, _MAX_CVDIR );
_ftcsupr ( szPath );
lpszPath = szPath;
lpszPath = _ftcsdec( lpszPath, _ftcschr( lpszPath, '\0' ) );
if ( *lpszPath != '\\' ) {
_ftcscat( szPath, "\\" );
}
_ftcsupr ( szFile );
_ftcscat ( szPath, szFile );
if ( !*szDir || _stat ( szPath, &statT ) ) {
_searchenv ( szFile, "PATH", szPath );
}
if ( *szPath == 0 ) { // [00]
if ( fMustExist ) { // [00]
*lphexg = hexgNull; // [00]
return sheFileOpen; // [00]
} // [00]
else {
// Retain the full path instead of just the filname
// If we just remember the file name then during
// a restart when we get the same dll load again
// the IDE will report an error as the paths don't
// match.
_ftcscpy ( szPath, lsz );
}
}
}
else {
_ftcscpy( szPath, lsz );
}
hexg = LLFind ( llexg, 0, &szPath, 0L );
// If the dll/exe couldn't be found in the correct
// list, see if it's in the other list. If so, move
// it to this list since it was probably added generically
// before the caller knew that is was an exe or dll. Most
// common case is where the main exe is in the dll list.
if ( hexg == hexgNull ) {
hexg = LLFind ( llexgOther, 0, &szPath, 0L );
if ( hexg ) {
LLRemove( llexgOther, hexg );
LLAdd( llexg, hexg );
}
}
if ( hexg == hexgNull && ( hexg = LLCreate ( llexg ) ) != hexgNull ) {
lpexg = (LPEXG)LLLock( hexg );
lpexg->fOmfLoaded = FALSE;
MEMSET ( lpexg, 0, sizeof ( EXG ) );
if ( lpexg->lszModule = (LSZ) MHAlloc ( _ftcslen( lsz ) + 1 ) ) {
_ftcscpy ( lpexg->lszModule, lsz );
}
if ( lpexg->lszName = (LSZ) MHAlloc ( _ftcslen( szPath ) + 1 ) ) {
_ftcscpy ( lpexg->lszName, (LSZ) szPath );
}
lpexg->lszDebug = lpexg->lszName;
// Create mds list
lpexg->hlliMds = LLInit ( sizeof( MDS ), 0, KillMdsNode, CmpMdsNode );
// Create the references to exgs
lpexg->hlliExr = LLInit ( sizeof ( EXR ), 0, NULL, CmpExrNode );
// If any of the allocated fields are NULL, then destroy the node
// and return failure
if ( !lpexg->lszName || !lpexg->lszModule ||
! lpexg->hlliMds || ! lpexg->hlliExr ||
*lpexg->lszName == '\0' ) {
KillExgNode( (LPV)lpexg );
LLUnlock( hexg );
hexg = hexgNull;
}
// Otherwise, add the node to the list
else {
LLAdd ( llexg, hexg );
LLUnlock( hexg );
}
}
*lphexg = hexg; // [00]
return (hexg == hexgNull) ? sheOutOfMemory : sheNone; // [00]
}
/** SHHmodGetNextGlobal
*
* Purpose: Retrieve the next module in the current PROCESS.
*
* Input:
* phexe Pointer to hexe. This will be updated. If NULL, then
* start at the first exe in the current process.
* hmod Handle to mds. If NULL, set *phexe to the next process.
* and get the first module in it. Otherwise get the next
* module in the list.
*
* Output: Returns a handle to the next module in the proces list. Will
* return hmodNull if the end of the list is reached.
*
* Exceptions:
*
* Notes:
*
*/
HMOD LOADDS PASCAL SHHmodGetNextGlobal ( HEXE FAR *phexe, HMOD hmod ) {
assert( hpdsCur );
do {
// If either the hexe or hmod is NULL, then on to the next exs.
if ( !*phexe || !hmod ) {
*phexe = SHGetNextExe ( *phexe );
hmod = hmodNull; // Start at the beginning of the next one
}
// If we've got an exs, get the next module
if ( *phexe ) {
hmod = SHHmodGetNext( *phexe, hmod );
}
} while( !hmod && *phexe );
return hmod;
}
#ifdef NEVER
/** SYForAll
*
* Purpose: This API calls lpfn() with a module name and time-stamp for
* each exe in the list.
*
* Input:
* ppv Data for lpfn.
* lpfn function pointer to call for each exe in list
*
* Output: Returns a conjunction of all the lpfn return values.
*
* Exceptions:
*
* Notes: Windows ONLY!!
*
*/
int SYForAll ( VOID ** ppv, int (FAR *lpfn)( VOID **, char *, time_t ) ) {
HEXE hexe = (HEXE)NULL;
WORD fRet = TRUE;
char sz[ 255 ];
while( hexe = SHGetNextMod ( hexe ) ) {
LPEXS lpexs = (LPEXS) LLLock ( hexe );
HEXG hexg = lpexs->hexg;
LPEXG lpexg = LLLock ( hexg );
_ftcscpy ( (char FAR *)sz, lpexg->lszModule );
fRet &= (WORD)( (*lpfn)( ppv, sz, lpexs->timestamp ) );
LLUnlock ( hexe );
LLUnlock ( hexg );
}
return (int)fRet;
}
#endif
/*
* SHGetSymbol
*
* Searches for a symbol value in the symbol table containing the seg and off
*
* Entry conditions:
* paddrOp:
* segment and offset of the symbol to be found
* paddrLoc:
* assumes that the module variable startaddr is set to the beginning
* of the code line currently being disassembled.
* sop:
* symbol options: what kinds of symbols to match. (???)
* lpodr:
* pointer where delta to symbol will be stored as well as
* near/farness, FPO/NON-FPO, cbProlog and symbol name.
*
* Exit conditions:
* *lpdoff:
* offset from symbol to the address
* return value:
* ascii name of symbol, or NULL if no match found
*
*/
int PASCAL SHFindBpOrReg ( LPADDR, UOFFSET, WORD, char FAR * );
VOID PASCAL SHdNearestSymbol ( PCXT, SOP, LPODR );
PCXT LOADDS PASCAL SHSetCxtMod ( LPADDR, PCXT );
LSZ PASCAL LOADDS SHGetSymbol (
LPADDR paddrOp,
LPADDR paddrLoc,
SOP sop,
LPODR lpodr
) {
CXT cxt = {0};
ADDR addrT = *paddrLoc; // [01]
ADDR addrOp = *paddrOp; // [01]
if ( !ADDR_IS_FLAT( addrOp ) && GetAddrSeg ( addrOp ) == 0 ) {
return NULL;
}
SYUnFixupAddr ( &addrT ); // [01]
if ( sop & sopStack ) {
if ( SHFindBpOrReg (
&addrT, // [01]
GetAddrOff ( addrOp ),
#if defined(_MIPS_) || defined(_ALPHA_)
S_REGREL32,
#else
(WORD) (ADDR_IS_OFF32 ( *paddrOp ) ? S_BPREL32 : S_BPREL16),
#endif
lpodr->lszName ) ) {
lpodr->dwDeltaOff = 0;
return lpodr->lszName;
}
else {
return NULL;
}
}
else {
SYUnFixupAddr ( &addrOp );
}
cxt.hMod = 0;
// if ( GetAddrSeg ( addrOp ) != GetAddrSeg ( addrT ) ) // [01]
if ( sop & sopData ) {
SHSetCxtMod ( &addrT, &cxt ); // [01]
}
else {
SHSetCxtMod ( &addrOp, &cxt );
}
cxt.addr = addrOp;
// get the closest symbol, including locals
SHdNearestSymbol ( &cxt, sop, lpodr );
if ( ( sop & sopExact ) && lpodr->dwDeltaOff ) {
return NULL;
}
else {
return lpodr->lszName;
}
}
/* SHHexeFromName - private function */
HEXE PASCAL LOADDS SHHexeFromName ( LSZ lszName ) {
BOOL fFound = FALSE;
HEXE hexe = hexeNull;
// Find the hexe associated with the libname
while( !fFound && ( hexe = SHGetNextExe ( hexe ) ) ) {
HEXG hexg = ( (LPEXS)LLLock( hexe ) )->hexg;
LLUnlock ( hexe );
fFound = !_ftcsicmp ( ( (LPEXG)LLLock( hexg ) )->lszName, lszName );
LLUnlock ( hexg );
}
if ( fFound ) {
return hexe;
}
else {
return hexeNull;
}
}
/** SHUnloadDll
*
* Purpose: Mark an EXE/DLL as no longer resident in memory. The debugger
* should call this function when it receives a notification from
* the OS indicating that the module has been unloaded from
* memory. This does not unload the symbolic information for the
* module.
*
* See the comments at the top of this file for more on when this
* function should be called.
*
* Input:
* hexe The handle to the EXE/DLL which was unloaded. After
* getting a notification from the OS, the debugger can
* determine the HEXE by calling SHGethExeFromName.
*
* Output:
* None
*
* Exceptions:
*
* Notes:
*
*/
VOID PASCAL LOADDS SHUnloadDll ( HEXE hexe ) {
LPEXS lpexs = LLLock ( hexe );
LPEXG lpexg = LLLock ( lpexs->hexg );
lpexs->fIsLoaded = FALSE;
if ( ( fhCur != -1 ) && ( _ftcsicmp ( rgchFile, lpexg->lszName ) == 0 ) ) {
SYClose ( fhCur ) ;
fhCur = -1 ;
}
// Need to make sure any caches we maintain are invalidated,
// as they may no longer be valid.
InvalidateSLCache();
LLUnlock ( lpexs->hexg );
LLUnlock ( hexe );
}
/** SHLoadDll
*
* Purpose: This function serves two purposes:
*
* (1) Load symbolic information for an EXE/DLL into memory,
* so its symbols are available to the user.
* (2) Indicate to the SH whether the EXE/DLL itself is loaded
* into memory.
*
* Because it serves two purposes, this function may be called
* more than once for the same EXE/DLL. See the comments at
* the top of this file for more on when this function should
* be called.
*
* Input:
* lszName The name of the EXE or DLL.
* fLoading TRUE if the EXE/DLL itself is actually loaded at this
* time, FALSE if not.
*
* Output:
* Returns an SHE error code.
*
* Exceptions:
*
* Notes:
*
*/
SHE PASCAL LOADDS SHLoadDll ( LSZ lszName, BOOL fLoading ) {
SHE she = sheNone;
HEXE hexe;
HEXG hexg = hexgNull;
LSZ lsz = lszName;
LSZ lsz2 = NULL;
LSZ lsz3;
// VLDCHK vldChk= {0, 0};
HANDLE hfile = 0;
DWORD dllLoadAddress = 0;
SHEnterCritSection(); // [02]
/*
* Check for the possiblity that we have a module only name
*
* We may get two formats of names. The first is just a name,
* no other information. The other is a big long string
* for PE exes.
*/
if (*lszName == '|') {
lszName = &lsz[1];
for (lsz2 = lszName; *lsz2 && *lsz2 != '|'; lsz2 = _ftcsinc(lsz2));
if (*lsz2 == 0) {
lszName = lsz;
lsz2 = NULL;
} else {
*lsz2 = 0;
lsz3 = _ftcsinc( lsz2 );
// vldChk.TimeAndDateStamp = GetUIntOfPSz(&lsz3); /* time stamp */
for (;(*lsz3 != 0) && (*lsz3 != '|'); lsz3 = _ftcsinc( lsz3 )) {}
if (*lsz3 == '|') lsz3 = _ftcsinc( lsz3 );
if (*lsz3 != 0) {
// vldChk.Checksum = GetUIntOfPSz(&lsz3); /* checksum */
for (;(*lsz3 != 0) && (*lsz3 != '|'); lsz3 = _ftcsinc( lsz3 )) {}
if (*lsz3 == '|') lsz3 = _ftcsinc( lsz3 );
if (*lsz3 != 0) {
hfile = (HANDLE) strtoul(lsz3, &lsz3,16); /* hfile */
if (hfile) {
SHCloseHandle(hfile);
hfile = 0;
}
if (*lsz3 == '|') lsz3 = _ftcsinc( lsz3 );
if (*lsz3 != 0) {
dllLoadAddress = strtoul(lsz3, &lsz3,16); /* load address */
}
}
}
}
}
hexe = SHHexeFromName ( lszName );
if ( hexe != hexeNull ) {
hexg = ( (LPEXS) LLLock ( hexe ) )->hexg;
LLUnlock ( hexe );
}
else {
she = SHAddDllExt ( lszName, TRUE, FALSE, &hexg );
if ( she != sheOutOfMemory )
hexe = SHHexeAddNew ( hpdsCur, hexg );
}
if ( hexe == hexeNull ) {
she == sheOutOfMemory;
}
else {
she = OLLoadOmf ( hexg, dllLoadAddress );
}
if ( ( she != sheOutOfMemory ) &&
( she != sheFileOpen ) &&
fLoading
) {
( (LPEXS) LLLock ( hexe ) )->fIsLoaded = TRUE;
LLUnlock ( hexe );
}
else if ( she != sheNone && hexg != hexgNull && hexe == hexeNull ) {
// load failed, remove and destroy the hexe node
SHHexeRemove ( hpdsCur, hexg, hexe );
}
/*
* Restore damage to the input buffer
*/
if (lsz2 != NULL) {
*lsz2 = '|';
}
InvalidateSLCache();
SHLeaveCritSection(); // [02]
return she;
}
/** SHAddDllsToProcess
*
* Purpose: Associate all DLLs that have been loaded with the current EXE.
*
* The debugger, at init time, will call SHAddDll on one EXE
* and zero or more DLLs. Then it should call this function
* to indicate that those DLLs are associated with (used by)
* that EXE; thus, a user request for a symbol from the EXE
* will also search the symbolic information from those DLLs.
*
* Input:
* None
*
* Output:
* Returns an SHE error code. At this writing, the only legal values
* are sheNone and sheOutOfMemory.
*
* Exceptions:
*
* Notes:
*
*/
SHE LOADDS PASCAL SHAddDllsToProcess ( VOID ) {
SHE she = sheNone;
HEXG hexg;
if ( !SHHexeAddNew ( hpdsCur, LLLast ( hlliExgExe ) ) ) {
she = sheOutOfMemory;
}
if ( she == sheNone ) {
for ( hexg = LLNext ( hlliExgDll, hexgNull );
hexg != hexgNull && she == sheNone;
hexg = LLNext ( hlliExgDll, hexg ) ) {
if ( !SHHexeAddNew ( hpdsCur, hexg ) ) {
she = sheOutOfMemory;
break;
}
}
}
return she;
}
/*
* SHSplitPath
*
* Custom split path that allows parameters to be null
*
*/
VOID SHSplitPath (
LSZ lszPath,
LSZ lszDrive,
LSZ lszDir,
LSZ lszName,
LSZ lszExt
) {
#ifdef WIN32
// For 32-bit versions, there's no need for all the extra work
_splitpath(lszPath, lszDrive, lszDir, lszName, lszExt);
#else
static char rgchDrive[_MAX_CVDRIVE];
static char rgchDir[_MAX_CVDIR];
static char rgchName[_MAX_CVFNAME];
static char rgchExt[_MAX_CVEXT];
static char rgchPath[ _MAX_CVPATH ];
// copy argument into a near string so that it can be given to _splitpath
_ftcscpy( rgchPath, lszPath );
_splitpath( rgchPath, rgchDrive, rgchDir, rgchName, rgchExt );
if ( lszDrive != NULL ) {
_ftcscpy( lszDrive, rgchDrive );
}
if ( lszDir != NULL ) {
_ftcscpy( lszDir, rgchDir );
}
if ( lszName != NULL ) {
_ftcscpy( lszName, rgchName );
}
if ( lszExt != NULL ) {
_ftcscpy( lszExt, rgchExt );
}
#endif
}
LSZ PASCAL STRDUP ( LSZ lsz ) {
LSZ lszOut = MHAlloc ( _ftcslen ( lsz ) + 1 );
_ftcscpy ( lszOut, lsz );
return lszOut;
}
INT FHOpen ( LSZ lszFile ) {
if ( fhCur == -1 || _ftcsicmp ( lszFile, rgchFile ) ) {
if ( fhCur != -1 ) {
SYClose ( fhCur );
fhCur = -1 ;
}
fhCur = SYOpen ( lszFile );
if ( fhCur != -1 ) {
_ftcscpy ( rgchFile, lszFile );
}
}
return fhCur;
}
LPALM PASCAL BuildALM (
BOOL fSeq,
WORD btAlign,
LSZ lszFileName,
ULONG lfoBase,
ULONG cb,
WORD cbBlock
) {
WORD cufop = (WORD) ( cb / cbBlock ) + 1;
LPALM lpalm = AllocAlign (
sizeof ( ALM ) + ( cufop + 1 ) * sizeof ( UFOP ) + sizeof ( WORD )
);
LPUFOP rgufop = NULL;
WORD iufop = 0;
assert ( !( (long)lpalm & 1L ) );
if ( lpalm == NULL ) {
return NULL;
}
rgufop = lpalm->rgufop;
lpalm->fSeq = fSeq;
lpalm->btAlign = btAlign;
lpalm->lszFileName = lszFileName;
lpalm->cb = cb;
lpalm->cbBlock = cbBlock;
while ( cb >= cbBlock ) {
rgufop [ iufop ].lfo = lfoBase | 1;
cb -= cbBlock;
lfoBase += cbBlock;
iufop += 1;
}
rgufop [ cufop - 1 ].lfo = lfoBase | 1;
rgufop [ cufop ].lfo = 0;
*( (LPW) &rgufop [ cufop + 1 ] ) = (WORD) cb;
return lpalm;
}
VOID PASCAL FixAlign ( LPB lpb, LPV lpvNext, WORD btAlign ) {
ULONG FAR *lpl = (ULONG FAR *) ( lpb + cbAlign - sizeof ( ULONG ) );
ALIGNSYM FAR *lpAlignSym = NULL;
// Find the Align symbol
while ( *lpl == 0 ) {
lpl -= 1;
assert ( (LPB) lpl - lpb > cbAlign / 2 );
}
assert ( (LPB) lpl != lpb + cbAlign - sizeof ( ULONG ) );
lpAlignSym = (ALIGNSYM FAR *) lpl;
assert ( lpAlignSym->rectyp == S_ALIGN );
lpl += 1;
// Note: current implementation says lpvNext is ALWAYS a LPUFOP
*lpl = (ULONG) lpvNext;
assert ( !(lpAlignSym->reclen & 1) );
lpAlignSym->reclen |= btAlign;
}
BOOL PASCAL LoadAlignBlock (
LSZ lszFile,
LPUFOP lpufop,
WORD cb,
WORD btAlign
) {
LPV lpv = AllocAlign ( cb );
INT hfile = -1;
assert ( !((long)lpv & 1L ) );
if ( lpv == NULL ) {
return FALSE;
}
hfile = FHOpen ( lszFile );
FHSeek ( hfile, ( lpufop->lfo & 0xFFFFFFFE ) | btAlign );
if ( FHRead ( hfile, lpv, cb ) != cb ) {
return FALSE;
}
FHClose ( hfile );
lpufop->lpv = lpv;
return TRUE;
}
LPV PASCAL LpvFromAlmLfo ( LPALM lpalm, ULONG lfo ) {
WORD iufop = (WORD) ( lfo / lpalm->cbBlock );
WORD cb = (WORD) ( lfo % lpalm->cbBlock );
LPUFOP lpufop = &lpalm->rgufop [ iufop ];
// If the low bit of the ufop is set, this is an lfo, otherwise
// it is a far pointer to the memory block containing the
// symbols.
if ( lpufop->lfo & 1 ) {
BOOL fEnd = iufop == (WORD) ( (lpalm->cb - 1) / lpalm->cbBlock );
WORD cbRead;
assert ( lpalm->cb );
if ( fEnd ) {
cbRead = (WORD) (1 + ((lpalm->cb - 1) % lpalm->cbBlock));
}
else {
cbRead = lpalm->cbBlock;
}
if ( !LoadAlignBlock (
lpalm->lszFileName,
lpufop,
cbRead,
lpalm->btAlign
) ) {
return NULL;
}
if ( lpalm->fSeq && !fEnd ) {
FixAlign ( lpufop->lpv, lpufop + 1, (WORD) lpalm->btAlign );
}
}
return ( (LPB) lpufop->lpv ) + cb;
}
SYMPTR PASCAL GetNextSym ( LSZ lszFile, SYMPTR psym ) {
if ( psym->rectyp != S_ALIGN ) {
// Normal case - not crossing an align block border
return (SYMPTR) ( ( (LPB) psym ) + psym->reclen + sizeof ( WORD ) );
}
else {
LPUFOP lpufop = * (LPUFOP FAR *) &psym->data[0];
assert ( lpufop );
if ( (ULONG) lpufop == 0xffffffffL ) {
// End of table
return NULL;
}
else if ( lpufop->lfo & 1 ) {
// Next block has not been loaded, so load it & fix it
WORD cb = cbAlign;
BOOL fEnd = ( lpufop + 1 )->lfo == 0;
if ( fEnd ) {
cb = *( (LPW) ( lpufop + 2 ) );
}
if ( !LoadAlignBlock ( lszFile, lpufop, cb, (WORD) (psym->reclen & 1) ) ) {
return NULL;
}
if ( !fEnd ) {
FixAlign ( lpufop->lpv, lpufop + 1, (WORD) (psym->reclen & 1) );
}
return lpufop->lpv;
}
else {
// Next block has been loaded, so just grab it
return lpufop->lpv;
}
}
}
LPV PASCAL GetSymbols ( LPMDS lpmds ) {
LPV lpvRet = NULL;
if ( lpmds->symbols ) {
lpvRet = lpmds->symbols;
}
#if defined(HOST32)
else if (lpmds->pmod) {
// Allocate space for this module's local symbols, then load them
// from the PDB.
if (!ModQuerySymbols(lpmds->pmod, 0, &lpmds->cbSymbols) ||
!(lpmds->symbols = MHAlloc(lpmds->cbSymbols)))
return 0;
if (ModQuerySymbols(lpmds->pmod, lpmds->symbols, &lpmds->cbSymbols))
return lpmds->symbols;
else {
MHFree(lpmds->symbols);
lpmds->symbols = 0;
lpmds->cbSymbols = 0;
return 0;
}
}
#endif
else if ( lpmds->ulsym ) {
LPEXG lpexg = LLLock ( lpmds->hexg );
INT hfile = FHOpen ( lpexg->lszDebug );
UINT cb = (UINT) lpmds->cbSymbols;
if ( ( lpmds->symbols = MHAlloc ( cb ) ) == NULL ) {
LLUnlock ( lpmds->hexg );
return NULL;
}
FHSeek ( hfile, lpmds->ulsym );
if ( FHRead ( hfile, (LPB) lpmds->symbols, cb ) != cb ) {
LLUnlock ( lpmds->hexg );
MHFree ( lpmds->symbols );
lpmds->symbols = NULL;
return NULL;
}
FHClose ( hfile );
lpvRet = lpmds->symbols;
LLUnlock ( lpmds->hexg );
}
return lpvRet;
}
VOID LOADDS PASCAL SHUnloadSymbolHandler( BOOL fResetLists ) {
// Put code here to execute just before
// the symbol handler is Freed from memory (FreeLibrary...)
if ( hlliPds ) {
LLDestroy( hlliPds );
}
hpdsCur = hpdsNull;
hlliPds = (HLLI)NULL;
if ( hlliExgDll ) {
LLDestroy( hlliExgDll );
hlliExgDll = (HLLI)NULL;
}
if ( hlliExgExe ) {
LLDestroy( hlliExgExe );
hlliExgExe = (HLLI)NULL;
}
// If fResetLists, the symbol handler is NOT being unloaded,
// but the information in the linked lists are no longer valid
// so we will have destroyed the list info, just reinitialize
// the lists
if ( fResetLists ) {
FInitLists();
}
}
// Get the time stamp of the EXE (which has the CV info)
//
// Returns: sheFileOpen
// sheCorruptOmf
// sheNone
SHE LOADDS PASCAL SHGetExeTimeStamp(LSZ szExeName, ULONG *lplTimeStamp)
{
IMAGE_DOS_HEADER doshdr; /* Old format MZ header */
IMAGE_FILE_HEADER PEHeader;
BOOL fIsPE = FALSE;
DWORD dwMagic;
UINT hfile;
if ( ( hfile = SYOpen ( szExeName ) ) == -1 ) {
return sheFileOpen;
}
/* Go to beginning of file and read old EXE header */
if ( SYReadFar ( hfile, (LPB) &doshdr, sizeof (IMAGE_DOS_HEADER) ) !=
sizeof (IMAGE_DOS_HEADER))
{
SYClose ( hfile );
return sheCorruptOmf;
}
/* Go to beginning of new header, read it in and verify */
SYSeek ( hfile, doshdr.e_lfanew, SEEK_SET );
if ( SYReadFar ( hfile, (LPB) &dwMagic, sizeof ( dwMagic ) ) != sizeof ( dwMagic ) ||
( dwMagic != IMAGE_NT_SIGNATURE))
{
SYClose ( hfile );
return sheCorruptOmf;
}
//
// If this is a PE EXE then get the PE header
// so that we can get the TimeDateStamp
//
if (SYReadFar ( hfile, (LPB) &PEHeader, sizeof(IMAGE_FILE_HEADER)) !=
sizeof(IMAGE_FILE_HEADER)) {
SYClose ( hfile );
return sheCorruptOmf;
}
*lplTimeStamp = PEHeader.TimeDateStamp;
SYClose ( hfile );
return sheNone;
}