/*++ Copyright (c) 1998 Microsoft Corporation Module Name: dumpoff.cxx Abstract: Structure dumper Author: Bilal Alam (balam) Oct-17-1998 Initial Revision --*/ #include "inetdbgp.h" #include "oemdbi.h" #include "cvinfo.h" #include "imagehlp.h" #define INVALID_LENGTH ((DWORD)-1) #define MAX_MEMBERNAME_LENGTH 256 #define MAX_TYPENAME_LENGTH 256 #define MAX_ARG_SIZE 256 typedef DWORD (*PFN_READ_MEMORY) ( VOID * address, DWORD cbBytes, VOID *pBuffer ); typedef DWORD (*PFN_PRINTF) ( CHAR * pszBuffer, DWORD cbBytes ); typedef struct _STRUCTURE_MEMBER { CHAR achMemberName[ MAX_MEMBERNAME_LENGTH + 1 ]; DWORD cbOffset; DWORD cbMaxSize; } STRUCTURE_MEMBER, *PSTRUCTURE_MEMBER; typedef struct _STRUCTURE_TEMPLATE { CHAR achName[ MAX_TYPENAME_LENGTH + 1 ]; PSTRUCTURE_MEMBER pMembers; DWORD cMembers; DWORD cUseful; DWORD cbTotalSize; DWORD Type; } STRUCTURE_TEMPLATE, *PSTRUCTURE_TEMPLATE; DWORD GetOffset( BYTE * pBuffer, DWORD * pcbOffset ); DWORD ReadFieldList( TPI * pTypeInterface, STRUCTURE_TEMPLATE* pStructure, lfFieldList * pFieldList, DWORD cbLen, DWORD dwFlags ); DWORD ReadBClass( TPI * pTypeInterface, lfBClass * pBClass, DWORD * pcbReturnSize, DWORD * pcbOffset, CHAR * pszBuffer, DWORD cbBuffer ); DWORD ReadMember( lfMember* pMember, DWORD * pcbReturnSize, DWORD * pcbOffset, CHAR * pszBuffer, DWORD cbBuffer ); DWORD ReadNestType( lfNestType* pNestType, DWORD * pcbReturnSize, DWORD * pcbOffset, CHAR * pszBuffer, DWORD cbBuffer ); DWORD ReadOneMethod( lfOneMethod* pOneMethod, DWORD * pcbReturnSize, DWORD * pcbOffset, CHAR * pszBuffer, DWORD cbBuffer ); DWORD ReadMethod( lfMethod* pMethod, DWORD * pcbReturnSize, DWORD * pcbOffset, CHAR * pszBuffer, DWORD cbBuffer ); DWORD ReadVTable( lfVFuncTab* pVTable, DWORD * pcbReturnSize, DWORD * pcbOffset, CHAR * pszBuffer, DWORD cbBuffer ); DWORD ReadStaticMember( lfSTMember* pStaticMember, DWORD * pcbReturnSize, DWORD * pcbOffset, CHAR * pszBuffer, DWORD cbBuffer ); DWORD InitializeStructureTemplate( PSTRUCTURE_TEMPLATE pTemplate ); DWORD TerminateStructureTemplate( PSTRUCTURE_TEMPLATE pTemplate ); VOID DumpoffUsage( VOID ); DWORD OutputTemplate( STRUCTURE_TEMPLATE * pTemplate, CHAR * pszMemberName, DWORD dwFlags, PVOID pvAddress, PFN_READ_MEMORY pfnReadMemory, PFN_PRINTF pfnPrintf ); DWORD BuildMemberList( IN PSTRUCTURE_TEMPLATE pTemplate, IN TPI * pTypeInterface, IN TI tiType, IN BOOL fTypeSizeOnly ); DWORD BuildMemberListForTypeName( IN PSTRUCTURE_TEMPLATE pTemplate, IN PDB * pPDB, IN LPSTR pszTypeName ); DWORD FindMembersOfTypeSize( IN PDB * pPDB, IN DWORD cbSize, IN PFN_PRINTF pfnPrintf ); DWORD OutputTemplate( STRUCTURE_TEMPLATE * pTemplate, CHAR * pszMemberName, DWORD dwFlags, PVOID pvAddress, PFN_READ_MEMORY pfnReadMemory, PFN_PRINTF pfnPrintf ) /*++ Routine Description: Output a structure template. If pvAddress is NULL, then this function will output a general template of the structure, listing each member along with its offset from the start of the structure. If pvAddress is non-NULL, then this function will output the structure with the memory at pvAddress cast as this type. If pszMemberName is NULL, then the above two statements apply to all members of the structure. If pszMember is not NULL, then the statements apply only the member whose name is pszMember. Arguments: pTemplate - Template to dump pszMemberName - Optional member name filter dwFlags - Flags describing Output() details. Currently not supported pvAddress - Optional address of memory to cast as type pfnReadMemory - Provides read memory functionality pfnPrintf - Provides printf functionality Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { DWORD cCounter; BOOL fDidSomething = FALSE; PBYTE pBuffer = NULL; DWORD dwError = ERROR_SUCCESS; CHAR achFormat[ 256 ]; DWORD cbRunningLength; BOOL fLastBitField = FALSE; DWORD cBitField = 0; DWORD dwTotalMask; DWORD cbSize; INT i; pBuffer = (PBYTE) LocalAlloc( LPTR, pTemplate->cbTotalSize ); if ( pBuffer == NULL ) { dwError = GetLastError(); goto Finished; } // // If address is specified, then read the amount required for this // structure. Otherwise, we are simply dumping the template and thus // output the size of the type // if ( pvAddress ) { dwError = pfnReadMemory( pvAddress, pTemplate->cbTotalSize, pBuffer ); if ( dwError != ERROR_SUCCESS ) { goto Finished; } } else { _snprintf( achFormat, sizeof( achFormat ), "sizeof( %s %s ) = 0x%X bytes (%d bytes)\n", pTemplate->Type == LF_CLASS ? "class" : "struct", pTemplate->achName, pTemplate->cbTotalSize, pTemplate->cbTotalSize ); pfnPrintf( achFormat, -1 ); } // // Iterate through consequential members of type // for( cCounter = 0; cCounter < pTemplate->cUseful; cCounter++ ) { // // Do filtering on member name if specified // if ( pszMemberName ) { if ( fDidSomething ) { break; } if ( strcmp( pTemplate->pMembers[ cCounter ].achMemberName, pszMemberName ) ) { continue; } } // // Dump member name // cbRunningLength = pfnPrintf( pTemplate->pMembers[ cCounter ].achMemberName, -1 ); // // Formatting junk // for ( i = cbRunningLength; i < 25; i++ ) { cbRunningLength += pfnPrintf( " ", -1 ); } cbRunningLength += pfnPrintf( " = ", -1 ); achFormat[ 0 ] = '\0'; cbSize = pTemplate->pMembers[ cCounter ].cbMaxSize; if ( !pvAddress ) { // // Just dumping template. Output the offset from the start of // the type // _snprintf( achFormat, sizeof( achFormat ), "+%8X", pTemplate->pMembers[ cCounter ].cbOffset ); } else if ( !cbSize || ( ( cbSize == sizeof( DWORD ) ) && fLastBitField ) ) { // // If the maxsize is 0, then this must be a bitfield // If the maxsize is 4, and the last item was a bit field, then // this must be the last bit of the bit field. // // TODO: Need to make this work for bit fields larger than 32 // if ( !fLastBitField ) { fLastBitField = TRUE; } cBitField++; dwTotalMask = (DWORD) *(DWORD*) ((PBYTE)pBuffer+ pTemplate->pMembers[ cCounter ].cbOffset); _snprintf( achFormat, sizeof( achFormat ), "%s", (dwTotalMask & (1 << ( cBitField - 1 ))) ? "TRUE" : "FALSE" ); if ( cbSize == sizeof( DWORD ) ) { fLastBitField = FALSE; cBitField = 0; } } else if ( cbSize != sizeof( DWORD ) ) { // // If this structure is not a DWORD in size, then assume we don't // know how to dump it affectly. In this case, we will simply // dump out the address at which the member data starts // _snprintf( achFormat, sizeof( achFormat ), "%016p..", (PBYTE) pvAddress + pTemplate->pMembers[ cCounter ].cbOffset ); } else { // // This is a DWORD sized member. We can dump out the value // effectively -> so we do it // _snprintf( achFormat, sizeof( achFormat ), "%08X", (DWORD) *(DWORD*) ((PBYTE)pBuffer+ pTemplate->pMembers[ cCounter ].cbOffset ) ); } cbRunningLength += pfnPrintf( achFormat, -1 ); // // Two column display. Given 80 columns, seems like the only // reasonable setting (maybe 1 column is useful?) // if ( pszMemberName || ( cCounter % 2 ) ) { pfnPrintf( "\n", -1 ); } else { for ( i = cbRunningLength; i < 40; i++ ) { pfnPrintf( " ", -1 ); } } // // Keep tabs on whether we actually dumped something // fDidSomething = TRUE; } if ( !fDidSomething ) { dwError = ERROR_FILE_NOT_FOUND; } pfnPrintf( "\n", -1 ); Finished: if ( pBuffer != NULL ) { LocalFree( pBuffer ); } return dwError; } DWORD BuildMemberListForTypeName( IN PSTRUCTURE_TEMPLATE pTemplate, IN PDB * pPDB, IN LPSTR pszTypeName ) /*++ Routine Description: Build the structure template for a given type Arguments: pTemplate - Template to populate (must have been previously inited) pPDB - PDB structure opened using MSDBI!PDBOpen pszTypeName - Name of type Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { TPI * pTypeInterface = NULL; TI RootTI; DWORD dwError = ERROR_SUCCESS; PB pb; if ( !pTemplate || !pPDB || !pszTypeName ) { dwError = ERROR_INVALID_PARAMETER; goto Finished; } // // Get the type interface // if ( !PDBOpenTpi( pPDB, pdbRead, &pTypeInterface ) ) { dwError = GetLastError(); goto Finished; } // // Does this PDB have the necessary type information? // if ( TypesQueryTiMinEx( pTypeInterface ) == TypesQueryTiMacEx( pTypeInterface ) ) { dwError = ERROR_NOT_SUPPORTED; goto Finished; } // // Lookup with specified type // if ( !TypesQueryTiForUDTEx( pTypeInterface, pszTypeName, TRUE, &RootTI) ) { dwError = ERROR_FILE_NOT_FOUND; goto Finished; } strncpy( pTemplate->achName, pszTypeName, sizeof( pTemplate->achName ) - 1 ); dwError = BuildMemberList( pTemplate, pTypeInterface, RootTI, FALSE ); Finished: if ( pTypeInterface != NULL ) { TypesClose( pTypeInterface ); } return dwError; } DWORD FindMembersOfTypeSize( IN PDB * pPDB, IN DWORD cbSize, IN PFN_PRINTF pfnPrintf ) /*++ Routine Description: Find members of a certain size. Output these types Arguments: pPDB - PDB structure opened using MSDBI!PDBOpen cbSize - Size in question pfnPrintf - Output routine Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { STRUCTURE_TEMPLATE Template; TPI * pTypeInterface = NULL; DWORD dwError = ERROR_SUCCESS; TI tiMin; TI tiMax; TI tiCursor; CHAR achLast[ MAX_TYPENAME_LENGTH ]; CHAR achBuffer[ 256 ]; if ( !pPDB || !pfnPrintf || !cbSize ) { dwError = ERROR_INVALID_PARAMETER; goto Finished; } // // Get the type interface // if ( !PDBOpenTpi( pPDB, pdbRead, &pTypeInterface ) ) { dwError = GetLastError(); goto Finished; } // // Get min/max type indices // tiMin = TypesQueryTiMinEx( pTypeInterface ); tiMax = TypesQueryTiMacEx( pTypeInterface ); if ( tiMin == tiMax ) { // // Probably no type info available in PDB // dwError = ERROR_NOT_SUPPORTED; goto Finished; } // // Cursor thru // achLast[ 0 ] = '\0'; for ( tiCursor = tiMin; tiCursor < tiMax; tiCursor++ ) { dwError = BuildMemberList( &Template, pTypeInterface, tiCursor, TRUE ); if ( dwError != ERROR_SUCCESS ) { if ( dwError == ERROR_NOT_SUPPORTED ) { // // Not a struct/class. Ignore // dwError = ERROR_SUCCESS; continue; } else { break; } } if ( Template.cbTotalSize == cbSize && strcmp( Template.achName, achLast ) ) { pfnPrintf( Template.Type == LF_CLASS ? "class " : "struct ", -1 ); pfnPrintf( Template.achName, -1 ); pfnPrintf( "\n", -1 ); strncpy( achLast, Template.achName, sizeof( achLast ) ); } } Finished: if ( pTypeInterface != NULL ) { TypesClose( pTypeInterface ); } return dwError; } DWORD BuildMemberList( IN PSTRUCTURE_TEMPLATE pTemplate, IN TPI * pTypeInterface, IN TI tiType, IN BOOL fTypeSizeOnly ) /*++ Routine Description: Build a template describing the given type. This template contains an array of members representing the member of the type. Arguments: pTemplate - Template to populate (must have been previously inited) pTypeInterface - Type interface tiType - Type ID to retrieve fStructSizeOnly - TRUE if we only need type size (and not the members) Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { TYPTYPE * pType = NULL; lfStructure * pStructure; lfFieldList * pFieldList; PB pb; DWORD dwError = ERROR_SUCCESS; DWORD cbTotalSize = 0; DWORD cbStructSize = 0; DWORD cUseful; DWORD cbNameOffset; TI RootTI; if ( !pTypeInterface || !pTemplate ) { dwError = ERROR_INVALID_PARAMETER; goto Finished; } RootTI = tiType; // // Parse root record of the type, verifying that it is of type // STRUCTURE or CLASS // if ( !TypesQueryPbCVRecordForTiEx( pTypeInterface, RootTI, &pb ) ) { dwError = ERROR_FILE_NOT_FOUND; goto Finished; } pType = (TYPTYPE*) pb; if ( ( pType->leaf != LF_CLASS ) && ( pType->leaf != LF_STRUCTURE ) ) { dwError = ERROR_NOT_SUPPORTED; goto Finished; } pTemplate->Type = pType->leaf; pStructure = (lfStructure*) &(pType->leaf); cbNameOffset = GetOffset( pStructure->data, &cbStructSize ); // // If we only need the overall structure size, then we can exit out now // if ( fTypeSizeOnly ) { pTemplate->cbTotalSize = cbStructSize; memset( pTemplate->achName, 0, sizeof( pTemplate->achName ) ); strncpy( pTemplate->achName, (LPSTR) pStructure->data + cbNameOffset + 1, min( (DWORD) *(CHAR*)(pStructure->data + cbNameOffset), sizeof( pTemplate->achName ) ) ); goto Finished; } // // In allocating # of members for the structure, get upper bound by // taking structure member count. // // OPTIMIZATION: Dynamically grow the list to avoid gross overestimation. // if ( pTemplate->cMembers < pStructure->count ) { pTemplate->pMembers = (PSTRUCTURE_MEMBER) LocalAlloc( LPTR, sizeof( STRUCTURE_MEMBER ) * pStructure->count ); if ( pTemplate->pMembers == NULL ) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto Finished; } pTemplate->cMembers = pStructure->count; } if ( !TypesQueryPbCVRecordForTi( pTypeInterface, pStructure->field, &pb ) ) { dwError = ERROR_FILE_NOT_FOUND; goto Finished; } pType = (TYPTYPE*)pb; pFieldList = (lfFieldList*) &(pType->leaf); // // Read the list of the fields in the type // dwError = ReadFieldList( pTypeInterface, pTemplate, pFieldList, pType->len, 0 ); cUseful = pTemplate->cUseful; if ( cUseful && ( dwError == ERROR_SUCCESS ) ) { pTemplate->pMembers[ cUseful - 1 ].cbMaxSize = cbStructSize - pTemplate->pMembers[ cUseful - 1 ].cbOffset; pTemplate->cbTotalSize = cbStructSize; } Finished: return dwError; } DWORD ReadFieldList( IN TPI * pTypeInterface, IN STRUCTURE_TEMPLATE * pTemplate, IN lfFieldList * pFieldList, IN DWORD cbLen, IN DWORD dwFlags ) /*++ Routine Description: Read the elements of the field list which represents the class/struct Arguments: Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { DWORD cbBytes = 0; PBYTE pBuffer; DWORD cbReturnSize = 0; BOOL fExit = FALSE; CHAR achMemberBuffer[ 256 ]; DWORD cbMemberBuffer; DWORD dwError = ERROR_SUCCESS; DWORD cFields = 0; DWORD cbLastOffset = 0; while ( cbBytes < cbLen ) { // // Account for padding the field list blob // for ( ; ; ) { pBuffer = (PBYTE) pFieldList->data + cbBytes; if ( *(BYTE*)pBuffer < LF_PAD0 ) { break; } cbBytes++; } // // After each padding block (if any), the first SHORT will contain // the field type of the next field in the struct/class. Handle // each type accordingly. If the handle function (Read*) returns // a cbReturnSize of -1 then this type will not contribute to the // offsets in the struct/class. For example, member functions. // achMemberBuffer[ 0 ] = '\0'; cbReturnSize = 0; cbMemberBuffer = sizeof( achMemberBuffer ); switch ( *(USHORT*) pBuffer ) { case LF_BCLASS: dwError = ReadBClass( pTypeInterface, (lfBClass*) pBuffer, &cbReturnSize, &cbBytes, achMemberBuffer, cbMemberBuffer ); break; case LF_MEMBER: dwError = ReadMember( (lfMember*) pBuffer, &cbReturnSize, &cbBytes, achMemberBuffer, cbMemberBuffer ); break; case LF_NESTTYPE: dwError = ReadNestType( (lfNestType*) pBuffer, &cbReturnSize, &cbBytes, achMemberBuffer, cbMemberBuffer ); break; case LF_ONEMETHOD: dwError = ReadOneMethod( (lfOneMethod*) pBuffer, &cbReturnSize, &cbBytes, achMemberBuffer, cbMemberBuffer ); break; case LF_METHOD: dwError = ReadMethod( (lfMethod*) pBuffer, &cbReturnSize, &cbBytes, achMemberBuffer, cbMemberBuffer ); break; case LF_STMEMBER: dwError = ReadStaticMember( (lfSTMember*) pBuffer, &cbReturnSize, &cbBytes, achMemberBuffer, cbMemberBuffer ); break; case LF_VFUNCTAB: dwError = ReadVTable( (lfVFuncTab*) pBuffer, &cbReturnSize, &cbBytes, achMemberBuffer, cbMemberBuffer ); break; default: fExit = TRUE; break; } if ( fExit ) { break; } if ( dwError != ERROR_SUCCESS || cbReturnSize == INVALID_LENGTH ) { continue; } // // We got a useful member of the struct/class. Add it to the // template. // pTemplate->cUseful++; strncpy( pTemplate->pMembers[ cFields ].achMemberName, achMemberBuffer, sizeof( pTemplate->pMembers[ cFields ].achMemberName ) - 1 ); pTemplate->pMembers[ cFields ].cbOffset = cbReturnSize; // // Calculate the maximum size of the previous member by taking the // difference between the start offset of the member and the start // offset of the previous member. Note that this is not necessarily // the exact size because of potential alignment padding. It is only // an upper bound on the size of the previous member. // if ( cFields ) { pTemplate->pMembers[ cFields - 1 ].cbMaxSize = cbReturnSize - cbLastOffset; } cbLastOffset = cbReturnSize; cFields++; } return dwError; } DWORD ReadBClass( IN TPI * pTypeInterface, IN lfBClass* pBClass, OUT DWORD * pcbReturnSize, OUT DWORD * pcbOffset, OUT CHAR * pszBuffer, IN DWORD cbBuffer ) /*++ Routine Description: Read the type info of base class field Arguments: pTypeInterface - MSDBI type interface pBClass - Points to a base class descriptor pcbReturnSize - Filled with offset from start of original type pcbOffset - Filled with new offset to result field traversal in field list handler pszBuffer - Filled with name of base class cbBuffer - Size of pszBuffer Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { PB pb; DWORD Offset; TYPTYPE* pType; lfStructure * pStructure; DWORD cbUnused; Offset = GetOffset( pBClass->offset, pcbReturnSize ); *pcbOffset += sizeof( lfBClass ) + Offset; // // We have to lookup the name of the base class explicitly by using the // index type in the base class descriptor // if ( !TypesQueryPbCVRecordForTiEx( pTypeInterface, pBClass->index, &pb ) ) { return ERROR_FILE_NOT_FOUND; } // // Process/munge/extract // pType = (TYPTYPE*)pb; pStructure = (lfStructure*) &(pType->leaf ); Offset = GetOffset( pStructure->data, &cbUnused ); memset( pszBuffer, 0, cbBuffer ); memcpy( pszBuffer, (CHAR*) pStructure->data + Offset + 1, min( (DWORD) *(CHAR*) ( pStructure->data + Offset ), cbBuffer ) ); return ERROR_SUCCESS; } DWORD ReadMember( IN lfMember * pMember, OUT DWORD * pcbReturnSize, OUT DWORD * pcbOffset, IN CHAR * pszBuffer, IN DWORD cbBuffer ) /*++ Routine Description: Read the type info of a member Arguments: pMember - Points to a member descriptor pcbReturnSize - Filled with offset from start of original type pcbOffset - Filled with new offset to result field traversal in field list handler pszBuffer - Filled with name of base class cbBuffer - Size of pszBuffer Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { DWORD Offset = GetOffset( pMember->offset, pcbReturnSize ); memset( pszBuffer, 0, cbBuffer ); memcpy( pszBuffer, (CHAR*) pMember->offset + Offset + 1, min( (DWORD) *(CHAR*) ( pMember->offset + Offset ), cbBuffer ) ); *pcbOffset += sizeof( lfMember ) + Offset + pMember->offset[Offset] + 1; return ERROR_SUCCESS; } DWORD ReadOneMethod( IN lfOneMethod * pOneMethod, OUT DWORD * pcbReturnSize, OUT DWORD * pcbOffset, IN CHAR * pszBuffer, IN DWORD cbBuffer ) /*++ Routine Description: Read the type info of a non-overloaded member function. We process this only to up the offset within the field list for traversal purposes. Member methods themselves have no affect on the offsets/size of the data structure Arguments: pOneMethod - Method type descriptor pcbReturnSize - Filled with offset from start of original type pcbOffset - Filled with new offset to result field traversal in field list handler pszBuffer - Filled with name of base class cbBuffer - Size of pszBuffer Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { CHAR * pszSource = NULL; pszSource = (CHAR*) pOneMethod + sizeof( lfOneMethod ); *pcbOffset += sizeof( lfOneMethod ); if ( ( pOneMethod->attr.mprop == CV_MTintro ) || ( pOneMethod->attr.mprop == CV_MTpureintro ) ) { *pcbOffset += sizeof( LONG ); pszSource += sizeof( LONG ); } *pcbOffset += *(CHAR*)pszSource + 1; *pcbReturnSize = INVALID_LENGTH; return ERROR_SUCCESS; } DWORD ReadMethod( IN lfMethod * pMethod, OUT DWORD * pcbReturnSize, OUT DWORD * pcbOffset, IN CHAR * pszBuffer, IN DWORD cbBuffer ) /*++ Routine Description: Read the type info of a member function. We process this only to up the offset within the field list for traversal purposes. Member methods themselves have no affect on the offsets/size of the data structure Arguments: pMethod - Method type descriptor pcbReturnSize - Filled with offset from start of original type pcbOffset - Filled with new offset to result field traversal in field list handler pszBuffer - Filled with name of base class cbBuffer - Size of pszBuffer Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { *pcbOffset += sizeof( lfMethod ) + pMethod->Name[ 0 ]; *pcbReturnSize = INVALID_LENGTH; return ERROR_SUCCESS; } DWORD ReadVTable( IN lfVFuncTab * pVTable, OUT DWORD * pcbReturnSize, OUT DWORD * pcbOffset, IN CHAR * pszBuffer, IN DWORD cbBuffer ) /*++ Routine Description: Read the vtable of the structure. Arguments: pVTable - Vtable type descriptor pcbReturnSize - Filled with offset from start of original type pcbOffset - Filled with new offset to result field traversal in field list handler pszBuffer - Filled with name of base class cbBuffer - Size of pszBuffer Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { *pcbOffset += sizeof( lfVFuncTab ); strncpy( pszBuffer, "'vftable'", cbBuffer - 1 ); // // Assume at the beginning of the data structure. // *pcbReturnSize = 0; return ERROR_SUCCESS; } DWORD ReadStaticMember( IN lfSTMember * pStaticMember, OUT DWORD * pcbReturnSize, OUT DWORD * pcbOffset, IN CHAR * pszBuffer, IN DWORD cbBuffer ) { *pcbOffset += sizeof( lfSTMember ) + pStaticMember->Name[ 0 ]; *pcbReturnSize = INVALID_LENGTH; return ERROR_SUCCESS; } DWORD ReadNestType( IN lfNestType * pNestType, OUT DWORD * pcbReturnSize, OUT DWORD * pcbOffset, IN CHAR * pszBuffer, IN DWORD cbBuffer ) { *pcbOffset += sizeof( lfNestType ) + pNestType->Name[ 0 ]; *pcbReturnSize = INVALID_LENGTH; return ERROR_SUCCESS; } DWORD GetOffset( BYTE * pBuffer, DWORD * pcbOffset ) /*++ Routine Description: Read the offset for the type record. Then advance the cursor. Arguments: pBuffer - Points to current position in field list buffer pcbOffset - Filled with offset of field member Return Value: Amount to advance cursor to next field member --*/ { USHORT leaf = *(USHORT*)pBuffer; if ( leaf < LF_NUMERIC ) { *pcbOffset = leaf; return sizeof( leaf ); } else { switch( leaf ) { case LF_CHAR: *pcbOffset = *((char*)pBuffer); return sizeof(leaf) + sizeof(char); case LF_SHORT: *pcbOffset = *(short*)pBuffer; return sizeof(leaf) + sizeof(short); case LF_USHORT: *pcbOffset = *(USHORT*)pBuffer; return sizeof(leaf) + sizeof(USHORT); case LF_LONG: *pcbOffset = *(long*)pBuffer; return sizeof(leaf) + sizeof(long); case LF_ULONG: *pcbOffset = *(ULONG*)pBuffer; return sizeof(leaf) + sizeof(ULONG); } } return 0; } DWORD InitializeStructureTemplate( IN PSTRUCTURE_TEMPLATE pTemplate ) /*++ Routine Description: Initialize structure template Arguments: pTemplate - Template buffer to be initialized Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { if ( pTemplate == NULL ) { return ERROR_INVALID_PARAMETER; } pTemplate->pMembers = NULL; pTemplate->cMembers = 0; pTemplate->achName[ 0 ] = '\0'; pTemplate->cUseful = 0; pTemplate->cbTotalSize = 0; pTemplate->Type = 0xFFFFFFFF; return ERROR_SUCCESS; } DWORD TerminateStructureTemplate( IN PSTRUCTURE_TEMPLATE pTemplate ) /*++ Routine Description: Terminate structure template Arguments: pTemplate - Template buffer to be terminated Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { if ( pTemplate == NULL ) { return ERROR_INVALID_PARAMETER; } if ( pTemplate->pMembers ) { LocalFree( pTemplate->pMembers ); pTemplate->pMembers = NULL; pTemplate->cMembers = 0; } return ERROR_SUCCESS; } DWORD OutputStructure( IN PDB * pDebug, IN CHAR * pszStructureType, IN CHAR * pszMemberName, IN DWORD dwFlags, IN VOID * pvAddress, IN PFN_READ_MEMORY pfnReadMemory, IN PFN_PRINTF pfnPrintf ) /*++ Routine Description: Top level call to output a structure Arguments: pDebug - PDB handle pszStructureType - Name of structure/class to dump pszMemberName - (optional) Name of particular member of structure to dump dwFlags - (not supported) pvAddress - (optional) Address of start of structure pfnReadMemory - (optional) Function to read memory. Not needed if pvAddress==NULL pfnPrintf - Output function Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { STRUCTURE_TEMPLATE Template; DWORD dwError; if ( !pDebug || !pszStructureType || !pfnReadMemory || !pfnPrintf ) { return ERROR_INVALID_PARAMETER; } dwError = InitializeStructureTemplate( &Template ); if ( dwError != ERROR_SUCCESS ) { return dwError; } dwError = BuildMemberListForTypeName( &Template, pDebug, pszStructureType ); if ( dwError != ERROR_SUCCESS ) { return dwError; } dwError = OutputTemplate( &Template, pszMemberName, dwFlags, pvAddress, pfnReadMemory, pfnPrintf ); // // CODEWORK: Cache the templates // TerminateStructureTemplate( &Template ); return dwError; } DWORD DoPrintf( CHAR * pszBuffer, DWORD cbBytes ) /*++ Routine Description: Print out buffer Arguments: pszBuffer - buffer to print cbBytes - Bytes to print Return Value: Number of bytes printed --*/ { dprintf( "%s", pszBuffer ); return strlen( pszBuffer ); } DWORD DoReadMemory( VOID * pvAddress, DWORD cbBytes, VOID * pBuffer ) /*++ Routine Description: Read debuggee memory into buffer Arguments: pvAddress - Address to read cbBytes - # of bytes to read pBuffer - Buffer to be filled Return Value: If successful ERROR_SUCCESS, else Win32 Error Code --*/ { if ( ReadMemory( pvAddress, pBuffer, cbBytes, NULL ) ) { return ERROR_SUCCESS; } else { return GetLastError(); } } VOID DumpoffUsage( VOID ) /*++ Routine Description: !dumpoff usage message Arguments: None Return Value: None --*/ { dprintf( "Usage: !dumpoff ![.] [expression]\n" " !dumpoff -s []\n" "\n" "pdb_file Un-qualified name of PDB file (eg. KERNEL32, NTDLL)\n" "type_name Name of type (struct/class) to dump, OR \n" " == to dump struct/classes of size cbHexSize\n" "member_name (optional) Name of member in type_name to dump\n" "expression (optional) Address of memory to dump as type_name\n" " if not present, offset(s) are dumped\n" "pdb_search_path Set the search path for PDBs\n" "\n" "Examples: !dumpoff ntdll!_RTL_CRITICAL_SECTION 14d4d0\n" " !dumpoff w3svc!HTTP_REQUEST._dwSignature w3svc!g_GlobalObj\n" " !dumpoff ntdll!_RTL_CRITICAL_SECTION\n" " !dumpoff w3svc!==840\n" " !dumpoff -s \\\\mydrive\\pdbs;c:\\local\\pdbs\n" ); } #if defined( _X86_ ) CHAR g_achPDBSearchPath[ 1024 ] = "\\\\x86fre\\symbols.pri\\retail\\dll"; #else CHAR g_achPDBSearchPath[ 1024 ] = "\\\\alphafre\\symbols.pri\\retail\\dll"; #endif DECLARE_API( dumpoff ) /*++ Routine Description: This function is called as an NTSD extension to dump a structure based on debug info in PDB Arguments: hCurrentProcess - Supplies a handle to the current process (at the time the extension was called). hCurrentThread - Supplies a handle to the current thread (at the time the extension was called). CurrentPc - Supplies the current pc at the time the extension is called. lpExtensionApis - Supplies the address of the functions callable by this extension. lpArgumentString - Supplies the asciiz string that describes the ansi string to be dumped. Return Value: None. --*/ { CHAR * pszPdb = NULL; CHAR * pszType = NULL; CHAR * pszAddress = NULL; CHAR * pszMember = NULL; CHAR * pszCursor = NULL; CHAR * pszNext = NULL; CHAR achArg1[ MAX_ARG_SIZE ]; CHAR achArg2[ MAX_ARG_SIZE ]; CHAR achBuffer[ MAX_ARG_SIZE ] = ""; CHAR achFileName[ MAX_PATH + 1 ]; CHAR achFullPath[ MAX_PATH + 1 ]; CHAR achSymPath[ MAX_PATH + 1 ]; BOOL fRet; EC ec; PDB * pDebug = NULL; DWORD dwError; CHAR * pszError = NULL; DWORD cArguments; DWORD cbSize; BOOL fRetry = TRUE; INIT_API(); // // get the debugger symbol path // fRet = SymGetSearchPath( hCurrentProcess, achSymPath, sizeof( achSymPath ) ); if (!fRet ) { // // If we couldn't get the default sym path, just use the SYSTEMROOT // dwError = GetEnvironmentVariable( "SYSTEMROOT", achSymPath, sizeof( achSymPath ) ); if ( dwError == 0 ) { _snprintf( achBuffer, sizeof( achBuffer ), "Unable to determine symbol path. Error = %d\n", GetLastError() ); goto Finished; } } // // parse out the argument style // ![.member] [address] // cArguments = sscanf( (CHAR*) lpArgumentString, "%256s%256s", achArg1, achArg2 ); if ( cArguments == EOF || cArguments == 0 ) { DumpoffUsage(); goto Finished; } // // Handle the !dumpoff -s [sympath] case // if ( ( achArg1[ 0 ] == '-' || achArg1[ 0 ] == '/' ) && ( achArg1[ 1 ] == 's' || achArg1[ 1 ] == 'S' ) ) { if ( cArguments == 2 ) { strncpy( g_achPDBSearchPath, achArg2, sizeof( g_achPDBSearchPath ) ); } dprintf( "PDB search path set to\n%s%s%s\n", g_achPDBSearchPath, *g_achPDBSearchPath ? "\n" : "", achSymPath ); goto Finished; } // // Parse the regular !dumpoff command // pszPdb = achArg1; pszCursor = strchr( achArg1, '!' ); if ( pszCursor == NULL ) { DumpoffUsage(); goto Finished; } *pszCursor = '\0'; pszType = pszCursor + 1; pszCursor = strchr( pszType, '.' ); if ( pszCursor != NULL ) { *pszCursor = '\0'; pszMember = pszCursor + 1; } if ( cArguments > 1 ) { pszAddress = achArg2; } // // done parsing, now get the PDB // strncpy( achFileName, pszPdb, MAX_ARG_SIZE ); strcat( achFileName, ".pdb"); // // Look for the PDB file. First in the PDB search path, then sympath // pszCursor = g_achPDBSearchPath; Retry: while ( pszCursor ) { pszNext = strchr( pszCursor, ';' ); if ( pszNext != NULL ) { *pszNext = '\0'; } fRet = SearchTreeForFile( pszCursor, achFileName, achFullPath ); if ( fRet ) { break; } if ( pszNext ) { pszCursor = pszNext + 1; } else { pszCursor = NULL; } } if ( !pszCursor && fRetry ) { fRetry = FALSE; // now try the debugger sympath pszCursor = achSymPath; goto Retry; } if ( !pszCursor ) { _snprintf( achBuffer, sizeof( achBuffer ), "Couldn't find PDB file %s\n", achFileName ); goto Finished; } // // Open the PDB file // if ( !PDBOpen( achFullPath, pdbRead, 0, &ec, achBuffer, &pDebug ) ) { _snprintf( achBuffer, sizeof( achBuffer ), "Error opening PDB file. Error = %d\n", ec ); goto Finished; } if ( pszType[ 0 ] == '=' && pszType[ 1 ] == '=' ) { // // Find all types of size after == // cbSize = strtoul( pszType + 2, NULL, 16 ); dwError = FindMembersOfTypeSize( pDebug, cbSize, DoPrintf ); } else { dwError = OutputStructure( pDebug, pszType, pszMember, 0, pszAddress ? (VOID*) GetExpression( pszAddress ) : NULL, DoReadMemory, DoPrintf ); } if ( dwError != ERROR_SUCCESS ) { switch ( dwError ) { case ERROR_FILE_NOT_FOUND: _snprintf( achBuffer, sizeof( achBuffer ), "Could not find type '%s' in PDB file '%s'\n", pszType, achFullPath ); break; case ERROR_NOT_SUPPORTED: _snprintf( achBuffer, sizeof( achBuffer ), "PDB file '%s' does not contain necessary type info\n", achFullPath ); break; default: _snprintf( achBuffer, sizeof( achBuffer ), "Error dumping structure. Error = %d\n", dwError ); } goto Finished; } Finished: if ( achBuffer[ 0 ] ) { dprintf( "%s", achBuffer ); } if ( pDebug ) { PDBClose( pDebug ); } } // DECLARE_API( dumpoff )