3621 lines
85 KiB
C
3621 lines
85 KiB
C
// Copyright (c) 1994-1999 Microsoft Corporation
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include "gen.h"
|
|
|
|
BOOLEAN bDebug = FALSE;
|
|
BOOLEAN bExitClean= TRUE;
|
|
|
|
char szNULL[]="";
|
|
char szVARGS[]="...";
|
|
char szCONST[] = "const";
|
|
char szVOLATILE[] = "volatile";
|
|
char szREGISTER[] = "register";
|
|
char szEXTERN[] = "extern";
|
|
char sz_CDECL[] = "__cdecl";
|
|
char szCDECL[] = "_cdecl";
|
|
char szSTDCALL[] = "__stdcall";
|
|
char sz__FASTCALL[] = "__fastcall";
|
|
char szUNALIGNED[] = "__unaligned";
|
|
char szTYPEDEF[] = "typedef";
|
|
char szCHAR[] = "char";
|
|
char szINT[] = "int";
|
|
char szLONG[] = "long";
|
|
char szSHORT[] = "short";
|
|
char szDOUBLE[] = "double";
|
|
char szENUM[] = "enum";
|
|
char szFLOAT[] = "float";
|
|
char szSTRUCT[] = "struct";
|
|
char szUNION[] = "union";
|
|
char szVOID[] = "void";
|
|
char szINT64[] = "_int64";
|
|
char sz_INT64[] = "__int64";
|
|
char sz__PTR64[] = "__ptr64";
|
|
char szFUNC[] = "()";
|
|
char szSIGNED[] = "signed";
|
|
char szUNSIGNED[] = "unsigned";
|
|
char szSTATIC[] = "static";
|
|
char szIN[] = "__in";
|
|
char szOUT[] = "__out";
|
|
char szINOUT[] = "__in __out";
|
|
char szGUID[] = "GUID";
|
|
char sz__W64[] = "__w64";
|
|
|
|
|
|
char szPragma[] = "#pragma";
|
|
char szPack[] = "pack";
|
|
char szPush[] = "push";
|
|
char szPop[] = "pop";
|
|
|
|
char szFUNCTIONS[] = "Functions";
|
|
char szSTRUCTURES[] = "Structures";
|
|
char szTYPEDEFS[] = "TypeDefs";
|
|
char szUNSIGNEDCHAR[] = "unsigned char";
|
|
char szUNSIGNEDSHORT[] = "unsigned short";
|
|
char szUNSIGNEDLONG[] = "unsigned long";
|
|
|
|
|
|
DEFBASICTYPES DefaultBasicTypes[] = {
|
|
{ "unsigned int" },
|
|
{ "int" },
|
|
{ "short int" },
|
|
{ "unsigned short int" },
|
|
{ "long int" },
|
|
{ "unsigned long int" },
|
|
{ "char" },
|
|
{ "unsigned char" },
|
|
{ szINT64 },
|
|
{ sz_INT64 },
|
|
{ szGUID },
|
|
{ szDOUBLE },
|
|
{ szFLOAT },
|
|
{ szENUM },
|
|
{ szSTRUCT },
|
|
{ szUNION },
|
|
{ szVOID },
|
|
{ szFUNC }
|
|
};
|
|
|
|
CHAR szVTBL[] = "VTBL";
|
|
|
|
#define NUMDEFBASICTYPES sizeof(DefaultBasicTypes)/sizeof(DEFBASICTYPES);
|
|
|
|
// List mapping TokenTypes to human-readable strings. TK_NONE, TK_IDENTIFIER,
|
|
// TK_NUMBER, and TK_STRING must be special-cased.
|
|
char *TokenString[] = {
|
|
"", // TK_NONE
|
|
"", // TK_IDENTIFIER
|
|
"", // TK_NUMBER
|
|
"+", // TK_PLUS
|
|
"-", // TK_MINUS
|
|
"*", // TK_STAR
|
|
"/", // TK_DIVIDE
|
|
"[", // TK_LSQUARE
|
|
"]", // TK_RSQUARE
|
|
"{", // TK_LBRACE
|
|
"}", // TK_RBRACE
|
|
"(", // TK_LPAREN
|
|
")", // TK_RPAREN
|
|
"...", // TK_VARGS
|
|
"const", // TK_CONST
|
|
"volatile", // TK_VOLATILE
|
|
"register", // TK_REGISTER
|
|
"extern", // TK_EXTERN
|
|
"__cdecl", // TK_CDECL
|
|
"__stdcall", // TK_STDCALL
|
|
"typedef", // TK_TYPEDEF
|
|
"static", // TK_STATIC
|
|
",", // TK_COMMA
|
|
";", // TK_SEMI
|
|
"struct", // TK_STRUCT
|
|
"union", // TK_UNION
|
|
"enum", // TK_ENUM
|
|
"__inline", // TK_INLINE
|
|
":", // TK_COLON
|
|
"=", // TK_ASSIGN
|
|
".", // TK_DOT
|
|
"<<", // TK_LSHIFT
|
|
">>", // TK_RSHIFT
|
|
"<", // TK_LESS
|
|
">", // TK_GREATER
|
|
"__unaligned", // TK_UNALIGNED
|
|
"__declspec", // TK_DECLSPEC
|
|
"__restrict", // TK_RESTRICT (MIPS-only keyword - a pointer modifier)
|
|
"__fastcall", // TK_FASTCALL
|
|
"__in", // TK_IN
|
|
"__out", // TK_OUT
|
|
"__in __out", // TK_INOUT
|
|
"&", // TK_BITWISE_AND
|
|
"|", // TK_BITWISE_OR
|
|
"&&", // TK_LOGICAL_AND
|
|
"||", // TK_LOGICAL_OR
|
|
"%", // TK_MOD
|
|
"^", // TK_XOR
|
|
"!", // TK_NOT
|
|
"~", // TK_TILDE
|
|
"", // TK_STRING
|
|
"sizeof", // TK_SIZEOF
|
|
"template", // TK_TEMPLATE
|
|
"__w64", // TK___W64
|
|
"" // TK_EOS
|
|
};
|
|
|
|
|
|
// List of keyword names. When an identifier is recognized, it is
|
|
// compared against this list, and if it matches, TK_IDENTIFIER is
|
|
// replaced by the appropriate keyword token id.
|
|
//
|
|
// NOTE: This must remain in sorted order.
|
|
TOKENMATCH KeywordList[] = {
|
|
{ TK_CDECL, "__cdecl" },
|
|
{ TK_DECLSPEC, "__declspec" },
|
|
{ TK_FASTCALL, "__fastcall" },
|
|
{ TK_INLINE, "__forceinline" },
|
|
{ TK_IN, "__in" },
|
|
{ TK_INLINE, "__inline" },
|
|
{ TK_OUT, "__out" },
|
|
{ TK_RESTRICT, "__restrict" },
|
|
{ TK_STDCALL, "__stdcall" },
|
|
{ TK_UNALIGNED, "__unaligned" },
|
|
{ TK___W64, "__w64" },
|
|
{ TK_CDECL, "_cdecl" },
|
|
{ TK_FASTCALL, "_fastcall" },
|
|
{ TK_INLINE, "_inline" },
|
|
{ TK_STRUCT, "class" },
|
|
{ TK_CONST, "const" },
|
|
{ TK_ENUM, "enum" },
|
|
{ TK_EXTERN, "extern" },
|
|
{ TK_INLINE, "inline" },
|
|
{ TK_REGISTER, "register" },
|
|
{ TK_SIZEOF, "sizeof" },
|
|
{ TK_STATIC, "static" },
|
|
{ TK_STRUCT, "struct" },
|
|
{ TK_TEMPLATE, "template" },
|
|
{ TK_TYPEDEF, "typedef" },
|
|
{ TK_UNION, "union" },
|
|
{ TK_VOLATILE, "volatile" },
|
|
{ TK_NONE, NULL }
|
|
};
|
|
|
|
|
|
LIST_ENTRY OpenFileHead= {&OpenFileHead, &OpenFileHead};
|
|
|
|
typedef struct _OpenFileEntry {
|
|
LIST_ENTRY FileEntry;
|
|
HANDLE hFile;
|
|
FILE *fp;
|
|
char FileName[MAX_PATH+1];
|
|
} OPENFILEENTRY, *POPENFILEENTRY;
|
|
|
|
TOKEN Tokens[MAX_TOKENS_IN_STATEMENT];
|
|
int CurrentTokenIndex;
|
|
|
|
void
|
|
CheckForKeyword(
|
|
PTOKEN Token
|
|
);
|
|
|
|
|
|
BOOL
|
|
ConsoleControlHandler(
|
|
DWORD dwCtrlType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called if user hits Ctrl+C or Ctrl+Break. Closes all open files,
|
|
allowing for a graceful exit.
|
|
|
|
Arguments:
|
|
|
|
dwCtrlType -- ????
|
|
|
|
Return Value:
|
|
|
|
????
|
|
|
|
--*/
|
|
{
|
|
CloseOpenFileList(TRUE);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AddOpenFile(
|
|
char *FileName,
|
|
FILE *fp,
|
|
HANDLE hFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Records that a file has been opened. If an error occurs within
|
|
the app, files in this list will be closed.
|
|
|
|
Arguments:
|
|
|
|
FileName -- name of open file
|
|
fp -- OPTIONAL file pointer
|
|
hFile -- OPTIONAL file handle
|
|
|
|
Return Value:
|
|
|
|
TRUE if file added to the list, FALSE if failure (probably out of memory)
|
|
|
|
--*/
|
|
{
|
|
POPENFILEENTRY pofe;
|
|
|
|
pofe = GenHeapAlloc(sizeof(OPENFILEENTRY));
|
|
if (!pofe) {
|
|
ErrMsg("AddOpenWriteFile: insuf memory: %s\n", strerror(errno));
|
|
return FALSE;
|
|
}
|
|
pofe->fp = fp;
|
|
pofe->hFile = hFile;
|
|
strcpy(pofe->FileName, FileName);
|
|
|
|
InsertHeadList(&OpenFileHead, &pofe->FileEntry);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
DelOpenFile(
|
|
FILE *fp,
|
|
HANDLE hFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes a file from the open file list. Note that the file is not
|
|
closed, the caller must do that.
|
|
|
|
Arguments:
|
|
|
|
fp -- OPTIONAL file pointer
|
|
hFile -- OPTIONAL file handle
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY Next;
|
|
POPENFILEENTRY pofe;
|
|
|
|
Next = OpenFileHead.Flink;
|
|
while (Next != &OpenFileHead) {
|
|
pofe = CONTAINING_RECORD(Next, OPENFILEENTRY, FileEntry);
|
|
if ((fp && pofe->fp == fp) || (hFile && pofe->hFile == hFile)) {
|
|
RemoveEntryList(&pofe->FileEntry);
|
|
GenHeapFree(pofe);
|
|
return;
|
|
}
|
|
|
|
Next= Next->Flink;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
CloseOpenFileList(
|
|
BOOL DeleteFiles
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes all open files and optionally deletes the files themselves.
|
|
|
|
Arguments:
|
|
|
|
DeleteFiles -- TRUE if open files are to be deleted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY Next;
|
|
POPENFILEENTRY pofe;
|
|
|
|
Next = OpenFileHead.Flink;
|
|
while (Next != &OpenFileHead) {
|
|
pofe = CONTAINING_RECORD(Next, OPENFILEENTRY, FileEntry);
|
|
if (pofe->fp) {
|
|
fclose(pofe->fp);
|
|
} else if (pofe->hFile) {
|
|
CloseHandle(pofe->hFile);
|
|
}
|
|
|
|
if (DeleteFiles && bExitClean) {
|
|
DeleteFile(pofe->FileName);
|
|
}
|
|
|
|
// cheat, skip mem cleanup since we know we are exiting
|
|
// GenHeapFree(pofe);
|
|
|
|
Next= Next->Flink;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
DumpKnownTypes(
|
|
PKNOWNTYPES pKnownTypes,
|
|
FILE *fp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Outputs the contents of a PKNOWNTYPES in a semi-readable format.
|
|
|
|
Arguments:
|
|
|
|
pKnownTypes -- type to output
|
|
fp -- destination of the output
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
fprintf(fp,"%2.1x|%2.1x|%2.1x|%2.1x|%s|%s|%s|%s|%s|\n",
|
|
pKnownTypes->Flags,
|
|
pKnownTypes->IndLevel,
|
|
pKnownTypes->RetIndLevel,
|
|
pKnownTypes->Size,
|
|
pKnownTypes->BasicType,
|
|
pKnownTypes->BaseName ? pKnownTypes->BaseName : szNULL,
|
|
pKnownTypes->FuncRet ? pKnownTypes->FuncRet : szNULL,
|
|
pKnownTypes->FuncMod ? pKnownTypes->FuncMod : szNULL,
|
|
pKnownTypes->TypeName
|
|
);
|
|
|
|
}
|
|
|
|
|
|
void
|
|
DumpTypesInfo(
|
|
PTYPESINFO pTypesInfo,
|
|
FILE *fp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Outputs the contents of a PTYPESINFO in a semi-readable format.
|
|
|
|
Arguments:
|
|
|
|
pTypesInfo -- type to output
|
|
fp -- destination of the output
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
fprintf(fp,"%2.1x|%2.1x|%2.1x|%2.1x|%s|%s|%s|%s|%s|\n",
|
|
pTypesInfo->Flags,
|
|
pTypesInfo->IndLevel,
|
|
pTypesInfo->RetIndLevel,
|
|
pTypesInfo->Size,
|
|
pTypesInfo->BasicType,
|
|
pTypesInfo->BaseName ? pTypesInfo->BaseName : szNULL,
|
|
pTypesInfo->FuncRet ? pTypesInfo->FuncRet : szNULL,
|
|
pTypesInfo->FuncMod ? pTypesInfo->FuncMod : szNULL,
|
|
pTypesInfo->TypeName
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
FreeTypesList(
|
|
PRBTREE ptree
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees an entire red-black tree.
|
|
|
|
Arguments:
|
|
|
|
ptree -- tree to free.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PKNOWNTYPES pNext, pNode;
|
|
|
|
pNode = ptree->pLastNodeInserted;
|
|
while (pNode) {
|
|
pNext = pNode->Next;
|
|
GenHeapFree(pNode);
|
|
pNode = pNext;
|
|
}
|
|
RBInitTree(ptree);
|
|
}
|
|
|
|
|
|
|
|
|
|
PKNOWNTYPES
|
|
GetBasicType(
|
|
char *sTypeName,
|
|
PRBTREE TypeDefsList,
|
|
PRBTREE StructsList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines the basic type of a typedef.
|
|
|
|
Arguments:
|
|
|
|
sTypeName -- type name to look up
|
|
TypeDefsList -- list of typedefs
|
|
StructsList -- list of structs
|
|
|
|
Return Value:
|
|
|
|
Ptr to the KNOWNTYPES for the basic type, or NULL if no basic type
|
|
found.
|
|
|
|
--*/
|
|
{
|
|
PKNOWNTYPES pkt, pktLast;
|
|
|
|
//
|
|
// go down the typedef list
|
|
//
|
|
pktLast = NULL;
|
|
for (pkt = GetNameFromTypesList(TypeDefsList, sTypeName);
|
|
(pkt != NULL) && (pkt != pktLast); ) {
|
|
pktLast = pkt;
|
|
pkt = GetNameFromTypesList(TypeDefsList, pktLast->BaseName);
|
|
}
|
|
|
|
//
|
|
// see what the the final typedef stands for
|
|
//
|
|
if (pktLast == NULL) {
|
|
pkt = GetNameFromTypesList(StructsList, sTypeName);
|
|
} else {
|
|
if (strcmp(pktLast->BasicType, szSTRUCT)) {
|
|
pkt = pktLast;
|
|
} else {
|
|
// if base type a struct get its definition
|
|
pkt = GetNameFromTypesList(StructsList, pktLast->BaseName);
|
|
}
|
|
}
|
|
|
|
return pkt;
|
|
}
|
|
|
|
|
|
PDEFBASICTYPES
|
|
GetDefBasicType(
|
|
char *pBasicType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if a typename is a basic type, and if so, which one.
|
|
|
|
Arguments:
|
|
|
|
pBasicType -- typename to examine
|
|
|
|
Return Value:
|
|
|
|
Ptr to the basic type info if pBasicType is a basic type.
|
|
NULL if the type is not a default basic type (int, sort, struct, etc.)
|
|
|
|
--*/
|
|
{
|
|
PDEFBASICTYPES pDefBasicTypes = DefaultBasicTypes;
|
|
int i = NUMDEFBASICTYPES;
|
|
|
|
do {
|
|
if (!strcmp(pDefBasicTypes->BasicType, pBasicType)) {
|
|
return pDefBasicTypes;
|
|
}
|
|
pDefBasicTypes++;
|
|
} while (--i);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PKNOWNTYPES
|
|
GetNameFromTypesList(
|
|
PRBTREE pKnownTypes,
|
|
char *pTypeName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Searches a type list for a type name.
|
|
|
|
Arguments:
|
|
|
|
pKnownType -- type list to search
|
|
pTypeName -- type name to look for
|
|
|
|
Return Value:
|
|
|
|
Ptr to the type info if pTypeName is in the list.
|
|
NULL if the type was not found.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Find the entry in the Red/Black tree
|
|
//
|
|
return RBFind(pKnownTypes, pTypeName);
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
TypesListMalloc(
|
|
ULONG Len
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Default memory allocator used to allocate a new KNOWNTYPES.
|
|
It can be overridden by setting fpTypesListMalloc.
|
|
|
|
Arguments:
|
|
|
|
Len -- number of bytes of memory to allocate.
|
|
|
|
Return Value:
|
|
|
|
Ptr to the memory or NULL of out-of-memory.
|
|
|
|
--*/
|
|
{
|
|
return GenHeapAlloc(Len);
|
|
}
|
|
|
|
PVOID (*fpTypesListMalloc)(ULONG Len) = TypesListMalloc;
|
|
|
|
VOID
|
|
ReplaceInfoInKnownTypes(
|
|
PKNOWNTYPES pKnownTypes,
|
|
PTYPESINFO pTypesInfo
|
|
)
|
|
{
|
|
|
|
BYTE *pNames;
|
|
int Len;
|
|
int SizeBasicType, SizeBaseName, SizeMembers, SizeFuncMod, SizeFuncRet;
|
|
int SizeTypeName, SizeBaseType, SizeMethods, SizeIMethods, SizeFileName;
|
|
|
|
SizeBasicType = strlen(pTypesInfo->BasicType) + 1;
|
|
SizeBaseName = strlen(pTypesInfo->BaseName) + 1;
|
|
SizeFuncRet = strlen(pTypesInfo->FuncRet) + 1;
|
|
SizeFuncMod = strlen(pTypesInfo->FuncMod) + 1;
|
|
SizeTypeName = strlen(pTypesInfo->TypeName) + 1;
|
|
SizeMembers = pTypesInfo->dwMemberSize;
|
|
SizeBaseType = strlen(pTypesInfo->BaseType) + 1;
|
|
SizeFileName = strlen(pTypesInfo->FileName) + 1;
|
|
SizeMethods = SizeOfMultiSz(pTypesInfo->Methods);
|
|
SizeIMethods = SizeOfMultiSz(pTypesInfo->IMethods);
|
|
|
|
// The extra sizeof(DWORD) allows the Members[] array to be DWORD-aligned
|
|
Len = SizeBasicType + SizeBaseName + SizeMembers + SizeFuncMod +
|
|
SizeFuncRet + SizeTypeName + SizeBaseType + SizeFileName + SizeMethods + SizeIMethods + sizeof(DWORD_PTR);
|
|
|
|
pNames = (*fpTypesListMalloc)(Len);
|
|
if (!pNames) {
|
|
fprintf(stderr, "%s pKnownTypes failed: ", ErrMsgPrefix, strerror(errno));
|
|
DumpTypesInfo(pTypesInfo, stderr);
|
|
ExitErrMsg(FALSE, "Out of memory!\n");
|
|
}
|
|
|
|
memset(pNames, 0, Len);
|
|
|
|
pKnownTypes->Flags = pTypesInfo->Flags;
|
|
pKnownTypes->IndLevel = pTypesInfo->IndLevel;
|
|
pKnownTypes->RetIndLevel = pTypesInfo->RetIndLevel;
|
|
pKnownTypes->Size = pTypesInfo->Size;
|
|
pKnownTypes->iPackSize = pTypesInfo->iPackSize;
|
|
pKnownTypes->gGuid = pTypesInfo->gGuid;
|
|
pKnownTypes->dwVTBLSize = pTypesInfo->dwVTBLSize;
|
|
pKnownTypes->dwVTBLOffset = pTypesInfo->dwVTBLOffset;
|
|
pKnownTypes->TypeId = pTypesInfo->TypeId;
|
|
pKnownTypes->LineNumber = pTypesInfo->LineNumber;
|
|
pKnownTypes->dwCurrentPacking = pTypesInfo->dwCurrentPacking;
|
|
pKnownTypes->dwScopeLevel = pTypesInfo->dwScopeLevel;
|
|
pKnownTypes->dwArrayElements = pTypesInfo->dwArrayElements;
|
|
pKnownTypes->dwBaseSize = pTypesInfo->dwBaseSize;
|
|
pKnownTypes->pTypedefBase = pTypesInfo->pTypedefBase;
|
|
Len = 0;
|
|
|
|
pKnownTypes->BasicType = pNames + Len;
|
|
strcpy(pKnownTypes->BasicType, pTypesInfo->BasicType);
|
|
Len += SizeBasicType;
|
|
|
|
pKnownTypes->BaseName = pNames + Len;
|
|
strcpy(pKnownTypes->BaseName, pTypesInfo->BaseName);
|
|
Len += SizeBaseName;
|
|
|
|
pKnownTypes->FuncRet = pNames + Len;
|
|
strcpy(pKnownTypes->FuncRet, pTypesInfo->FuncRet);
|
|
Len += SizeFuncRet;
|
|
|
|
pKnownTypes->FuncMod = pNames + Len;
|
|
strcpy(pKnownTypes->FuncMod, pTypesInfo->FuncMod);
|
|
Len += SizeFuncMod;
|
|
|
|
if (SizeFileName > 0) {
|
|
pKnownTypes->FileName = pNames + Len;
|
|
strcpy(pKnownTypes->FileName, pTypesInfo->FileName);
|
|
Len += SizeFileName;
|
|
}
|
|
else pKnownTypes->FileName = NULL;
|
|
|
|
// Ensure that Members[] is DWORD-aligned, so the structures within the
|
|
// Members[] are aligned.
|
|
Len = (Len+sizeof(DWORD_PTR)) & ~(sizeof(DWORD_PTR)-1);
|
|
|
|
if (SizeMembers == 0) {
|
|
pKnownTypes->Members = NULL;
|
|
pKnownTypes->pmeminfo = NULL;
|
|
pKnownTypes->pfuncinfo = NULL;
|
|
}
|
|
else {
|
|
pKnownTypes->Members = pNames + Len;
|
|
memcpy(pKnownTypes->Members, pTypesInfo->Members, SizeMembers);
|
|
|
|
//
|
|
// Fix up pointers within the Members data, so they point into the
|
|
// pKnownTypes data instead of the pTypesInfo.
|
|
//
|
|
pKnownTypes->pfuncinfo = RelocateTypesInfo(pKnownTypes->Members,
|
|
pTypesInfo);
|
|
|
|
if (pTypesInfo->TypeKind == TypeKindStruct) {
|
|
pKnownTypes->pmeminfo = (PMEMBERINFO)pKnownTypes->Members;
|
|
}
|
|
Len += SizeMembers;
|
|
}
|
|
|
|
if (SizeMethods == 0) pKnownTypes->Methods = NULL;
|
|
else {
|
|
pKnownTypes->Methods = pNames + Len;
|
|
memcpy(pKnownTypes->Methods, pTypesInfo->Methods, SizeMethods);
|
|
Len += SizeMethods;
|
|
}
|
|
|
|
if (SizeIMethods == 0) pKnownTypes->IMethods = NULL;
|
|
else {
|
|
pKnownTypes->IMethods = pNames + Len;
|
|
memcpy(pKnownTypes->IMethods, pTypesInfo->IMethods, SizeIMethods);
|
|
Len += SizeIMethods;
|
|
}
|
|
|
|
pKnownTypes->BaseType = pNames + Len;
|
|
strcpy(pKnownTypes->BaseType, pTypesInfo->BaseType);
|
|
Len += SizeBaseType;
|
|
|
|
pKnownTypes->TypeName = pNames + Len;
|
|
strcpy(pKnownTypes->TypeName, pTypesInfo->TypeName);
|
|
Len += SizeTypeName;
|
|
|
|
}
|
|
|
|
PKNOWNTYPES
|
|
AddToTypesList(
|
|
PRBTREE pTree,
|
|
PTYPESINFO pTypesInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a PTYPESINFO to the list of known types.
|
|
|
|
This function makes the following ASSUMPTIONS:
|
|
1. The MEMBERINFO buffer passed in the TYPESINFO structure is all
|
|
allocated from one contiguous block of memory, ie completely
|
|
contained within the Members[] buffer.
|
|
|
|
2. The MEMBERINFO buffer built in the KNOWNTYPESINFO structure is
|
|
also allocated from one contiguous block of memory.
|
|
|
|
The code requires this since it will block copy the entire data
|
|
structure and then "fixup" the pointers within the MEMBERINFO elements.
|
|
|
|
Arguments:
|
|
|
|
pTree -- types list to add the new type to
|
|
pTypesInfo -- the type to add.
|
|
|
|
Return Value:
|
|
|
|
Ptr to the new PKNOWNTYPES, or NULL if out-of-memory.
|
|
|
|
--*/
|
|
{
|
|
PKNOWNTYPES pKnownTypes;
|
|
|
|
pKnownTypes = (*fpTypesListMalloc)(sizeof(KNOWNTYPES));
|
|
if (!pKnownTypes) {
|
|
fprintf(stderr, "%s pKnownTypes failed: ", ErrMsgPrefix, strerror(errno));
|
|
DumpTypesInfo(pTypesInfo, stderr);
|
|
return pKnownTypes;
|
|
}
|
|
|
|
memset(pKnownTypes, 0, sizeof(KNOWNTYPES));
|
|
|
|
ReplaceInfoInKnownTypes(pKnownTypes, pTypesInfo);
|
|
|
|
RBInsert(pTree, pKnownTypes);
|
|
|
|
if (bDebug) {
|
|
DumpKnownTypes(pKnownTypes, stdout);
|
|
}
|
|
|
|
return pKnownTypes;
|
|
}
|
|
|
|
|
|
void
|
|
ReplaceInTypesList(
|
|
PKNOWNTYPES pKnownTypes,
|
|
PTYPESINFO pTypesInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Replaces an existing PKNOWNTYPES with a new PTYPESINFO. The old data
|
|
is overwritten with new data, so pointers to the old PKNOWNTYPES will
|
|
still be valid.
|
|
|
|
This function makes the following ASSUMPTIONS:
|
|
1. The MEMBERINFO buffer passed in the TYPESINFO structure is all
|
|
allocated from one contiguous block of memory, ie completely
|
|
contained within the Members[] buffer.
|
|
|
|
2. The MEMBERINFO buffer built in the KNOWNTYPESINFO structure is
|
|
also allocated from one contiguous block of memory.
|
|
|
|
The code requires this since it will block copy the entire data
|
|
structure and then "fixup" the pointers within the MEMBERINFO elements.
|
|
|
|
Arguments:
|
|
|
|
pKnownTypes -- type to overwrite
|
|
pTypesInfo -- the type to add.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
ReplaceInfoInKnownTypes(pKnownTypes, pTypesInfo);
|
|
|
|
if (bDebug) {
|
|
DumpKnownTypes(pKnownTypes, stdout);
|
|
}
|
|
}
|
|
|
|
|
|
PFUNCINFO
|
|
RelocateTypesInfo(
|
|
char *dest,
|
|
PTYPESINFO src
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adjusts pointers within the Members[] array which point back into
|
|
the Members[]. After a TYPESINFO is copied, the destination TYPESINFO
|
|
or KNOWNTYPES Members[] array must be relocated.
|
|
|
|
Arguments:
|
|
|
|
dest -- start of the destination Members[] data
|
|
src -- the source TYPESINFO from which the Members[] was copied
|
|
|
|
Return Value:
|
|
|
|
Address for first pfuncinfo within dest, NULL if dest does not contain
|
|
funcinfos. Destination Members[] data is relocated no matter what.
|
|
|
|
--*/
|
|
{
|
|
INT_PTR iPtrFix;
|
|
PMEMBERINFO pmeminfo;
|
|
PFUNCINFO pfuncinfo;
|
|
PFUNCINFO pfuncinfoRet = NULL;
|
|
|
|
iPtrFix = (INT_PTR)(dest - src->Members);
|
|
if (src->TypeKind == TypeKindStruct) {
|
|
|
|
pmeminfo = (PMEMBERINFO)dest;
|
|
|
|
while (pmeminfo != NULL) {
|
|
if (pmeminfo->pmeminfoNext != NULL) {
|
|
pmeminfo->pmeminfoNext = (PMEMBERINFO)
|
|
((char *)pmeminfo->pmeminfoNext + iPtrFix);
|
|
}
|
|
if (pmeminfo->sName != NULL) {
|
|
if (pmeminfo->sName < src->Members || pmeminfo->sName > &src->Members[FUNCMEMBERSIZE]) {
|
|
ExitErrMsg(FALSE, "RelocateTypesInfo: sName not within Members[]\n");
|
|
}
|
|
pmeminfo->sName += iPtrFix;
|
|
}
|
|
if (pmeminfo->sType != NULL) {
|
|
if (pmeminfo->sType < src->Members || pmeminfo->sType > &src->Members[FUNCMEMBERSIZE]) {
|
|
ExitErrMsg(FALSE, "RelocateTypesInfo: sType not within Members[]\n");
|
|
}
|
|
pmeminfo->sType += iPtrFix;
|
|
}
|
|
pmeminfo = pmeminfo->pmeminfoNext;
|
|
}
|
|
} else if (src->TypeKind == TypeKindFunc) {
|
|
|
|
//
|
|
// Make pfuncinfo point into the 'dest' array by fixing up the
|
|
// source pointer.
|
|
//
|
|
pfuncinfo = (PFUNCINFO)((INT_PTR)src->pfuncinfo + iPtrFix);
|
|
if ((char *)pfuncinfo < dest || (char *)pfuncinfo > dest+FUNCMEMBERSIZE) {
|
|
ExitErrMsg(FALSE, "RelocateTypesInfo: pfuncinfo bad\n");
|
|
}
|
|
pfuncinfoRet = pfuncinfo;
|
|
|
|
while (pfuncinfo != NULL) {
|
|
if (pfuncinfo->pfuncinfoNext) {
|
|
pfuncinfo->pfuncinfoNext = (PFUNCINFO)
|
|
((char *)pfuncinfo->pfuncinfoNext + iPtrFix);
|
|
}
|
|
if (pfuncinfo->sName != NULL) {
|
|
if (pfuncinfo->sName < src->Members || pfuncinfo->sName > &src->Members[FUNCMEMBERSIZE]) {
|
|
ExitErrMsg(FALSE, "RelocateTypesInfo: sName not within Members[]\n");
|
|
}
|
|
pfuncinfo->sName += iPtrFix;
|
|
}
|
|
if (pfuncinfo->sType != NULL) {
|
|
if (pfuncinfo->sType < src->Members || pfuncinfo->sType > &src->Members[FUNCMEMBERSIZE]) {
|
|
ExitErrMsg(FALSE, "RelocateTypesInfo: sType not within Members[]\n");
|
|
}
|
|
pfuncinfo->sType += iPtrFix;
|
|
}
|
|
pfuncinfo = pfuncinfo->pfuncinfoNext;
|
|
}
|
|
}
|
|
|
|
return pfuncinfoRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ParseTypes(
|
|
PRBTREE pTypesList,
|
|
PTYPESINFO pTypesInfo,
|
|
PKNOWNTYPES *ppKnownTypes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parses the Tokens[] and recognizes the following syntaxes:
|
|
BasicType
|
|
DerivedType
|
|
unsigned|signed <int type>
|
|
unsigned|signed
|
|
unsigned|signed short|long int
|
|
short|long int
|
|
|
|
Arguments:
|
|
|
|
pTypesList -- list of known types
|
|
pTypesInfo -- [OPTIONAL OUT] info about the type that was recognized
|
|
ppKnownTypes -- [OPTIONAL OUT] KNOWNTYPES info about the type
|
|
|
|
Return Value:
|
|
|
|
TRUE - type was recognized. pTypeInfo and ppKnownTypes are set,
|
|
CurrentToken() points to token following the type.
|
|
FALSE - type not recognized.
|
|
|
|
--*/
|
|
{
|
|
PKNOWNTYPES pkt;
|
|
char TypeName[MAX_PATH];
|
|
char *SizeMod = NULL;
|
|
char *SignMod = NULL;
|
|
BOOL fLoopMore;
|
|
|
|
if (pTypesInfo) {
|
|
memset(pTypesInfo, 0, sizeof(TYPESINFO));
|
|
}
|
|
|
|
switch (CurrentToken()->TokenType) {
|
|
case TK_STRUCT:
|
|
case TK_UNION:
|
|
case TK_ENUM:
|
|
ConsumeToken();
|
|
break;
|
|
|
|
case TK_VARGS:
|
|
pkt = GetNameFromTypesList(pTypesList, szVARGS);
|
|
ConsumeToken();
|
|
goto PKTExit;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Process 'long', 'short', 'signed' and 'unsigned' modifiers
|
|
//
|
|
while (CurrentToken()->TokenType == TK_IDENTIFIER) {
|
|
if (strcmp(CurrentToken()->Name, szLONG) == 0) {
|
|
SizeMod = szLONG;
|
|
} else if (strcmp(CurrentToken()->Name, szSHORT) == 0) {
|
|
SizeMod = szSHORT;
|
|
} else if (strcmp(CurrentToken()->Name, szUNSIGNED) == 0) {
|
|
SignMod = szUNSIGNED;
|
|
} else if (strcmp(CurrentToken()->Name, szSIGNED) == 0) {
|
|
SignMod = NULL;
|
|
} else {
|
|
break;
|
|
}
|
|
ConsumeToken();
|
|
}
|
|
|
|
//
|
|
// Convert the modifier list into a standardized type string and
|
|
// look it up.
|
|
//
|
|
TypeName[0] = '\0';
|
|
if (SignMod) {
|
|
strcpy(TypeName, SignMod);
|
|
}
|
|
if (SizeMod) {
|
|
if (TypeName[0]) {
|
|
strcat(TypeName, " ");
|
|
}
|
|
strcat(TypeName, SizeMod);
|
|
}
|
|
|
|
//
|
|
// Append the type name to the optional list of type modifiers
|
|
//
|
|
if (CurrentToken()->TokenType != TK_IDENTIFIER) {
|
|
if (TypeName[0] == '\0') {
|
|
return FALSE; // no qualifiers, so not a type
|
|
}
|
|
//
|
|
// Append the implict 'int' on the end of the type qualifiers
|
|
//
|
|
strcat(TypeName, " ");
|
|
strcat(TypeName, szINT);
|
|
} else {
|
|
char *Name = CurrentToken()->Name;
|
|
|
|
if (strcmp(Name, szVOID) == 0 ||
|
|
strcmp(Name, szINT) == 0 ||
|
|
strcmp(Name, szINT64) == 0 ||
|
|
strcmp(Name, sz_INT64) == 0 ||
|
|
strcmp(Name, szCHAR) == 0 ||
|
|
strcmp(Name, szFLOAT) == 0 ||
|
|
strcmp(Name, szDOUBLE) == 0) {
|
|
|
|
// Append the intrinsic type to the list of type modifiers
|
|
if (TypeName[0]) {
|
|
strcat(TypeName, " ");
|
|
}
|
|
strcat(TypeName, Name);
|
|
|
|
//
|
|
// Don't worry about explicitly disallowing things like
|
|
// 'unsigned double' or 'short char'. They won't be
|
|
// in the pTypesList, so the parse will fail.
|
|
//
|
|
|
|
ConsumeToken();
|
|
|
|
} else if (TypeName[0]) {
|
|
//
|
|
// The identifier is not an intrinsic type, and type modifiers
|
|
// were seen. The identifier is a variable name, not part of the
|
|
// type name. The type name is implicitly 'int'.
|
|
//
|
|
strcat(TypeName, " ");
|
|
strcat(TypeName, szINT);
|
|
|
|
} else {
|
|
//
|
|
// The identifier is not an intrinsic type, and no type
|
|
// modifiers have been seen. It is probably a typedef name.
|
|
//
|
|
strcpy(TypeName, Name);
|
|
ConsumeToken();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Look up the type name with all of its glorious modifiers
|
|
//
|
|
pkt = GetNameFromTypesList(pTypesList, TypeName);
|
|
if (!pkt) {
|
|
//
|
|
// Type not found
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
PKTExit:
|
|
if (pTypesInfo) {
|
|
BUFALLOCINFO bufallocinfo;
|
|
char *ps;
|
|
PFUNCINFO pfuncinfoSrc = pkt->pfuncinfo;
|
|
PMEMBERINFO pmeminfoSrc = pkt->pmeminfo;
|
|
|
|
BufAllocInit(&bufallocinfo, pTypesInfo->Members, sizeof(pTypesInfo->Members), 0);
|
|
|
|
pTypesInfo->Flags = pkt->Flags;
|
|
pTypesInfo->IndLevel = pkt->IndLevel;
|
|
pTypesInfo->Size = pkt->Size;
|
|
pTypesInfo->iPackSize = pkt->iPackSize;
|
|
strcpy(pTypesInfo->BasicType,pkt->BasicType);
|
|
if (pkt->BaseName) {
|
|
strcpy(pTypesInfo->BaseName,pkt->BaseName);
|
|
}
|
|
strcpy(pTypesInfo->TypeName,pkt->TypeName);
|
|
if (pfuncinfoSrc) {
|
|
PFUNCINFO pfuncinfoDest = NULL;
|
|
|
|
pTypesInfo->pfuncinfo = BufPointer(&bufallocinfo);
|
|
pTypesInfo->TypeKind = TypeKindFunc;
|
|
|
|
while (pfuncinfoSrc) {
|
|
pfuncinfoDest = AllocFuncInfoAndLink(&bufallocinfo, pfuncinfoDest);
|
|
if (!pfuncinfoDest) {
|
|
ExitErrMsg(FALSE, "ParseTypes - out of memory at line %d\n", __LINE__);
|
|
}
|
|
pfuncinfoDest->fIsPtr64 = pfuncinfoSrc->fIsPtr64;
|
|
pfuncinfoDest->tkPreMod = pfuncinfoSrc->tkPreMod;
|
|
pfuncinfoDest->tkSUE = pfuncinfoSrc->tkSUE;
|
|
pfuncinfoDest->tkPrePostMod = pfuncinfoSrc->tkPrePostMod;
|
|
pfuncinfoDest->IndLevel = pfuncinfoSrc->IndLevel;
|
|
pfuncinfoDest->tkPostMod = pfuncinfoSrc->tkPostMod;
|
|
|
|
ps = BufPointer(&bufallocinfo);
|
|
pfuncinfoDest->sType = ps;
|
|
strcpy(ps, pfuncinfoSrc->sType);
|
|
BufAllocate(&bufallocinfo, strlen(ps)+1);
|
|
|
|
if (pfuncinfoSrc->sName) {
|
|
ps = BufPointer(&bufallocinfo);
|
|
pfuncinfoDest->sName = ps;
|
|
strcpy(ps, pfuncinfoSrc->sName);
|
|
BufAllocate(&bufallocinfo, strlen(ps)+1);
|
|
}
|
|
|
|
pfuncinfoSrc = pfuncinfoSrc->pfuncinfoNext;
|
|
}
|
|
} else if (pmeminfoSrc) {
|
|
PMEMBERINFO pmeminfoDest = NULL;
|
|
|
|
pTypesInfo->TypeKind = TypeKindStruct;
|
|
|
|
while (pmeminfoSrc) {
|
|
pmeminfoDest = AllocMemInfoAndLink(&bufallocinfo, pmeminfoDest);
|
|
pmeminfoDest->dwOffset = pmeminfoSrc->dwOffset;
|
|
|
|
if (pmeminfoSrc->sName) {
|
|
ps = BufPointer(&bufallocinfo);
|
|
pmeminfoDest->sName = ps;
|
|
strcpy(ps, pmeminfoSrc->sName);
|
|
BufAllocate(&bufallocinfo, strlen(ps)+1);
|
|
}
|
|
|
|
if (pmeminfoSrc->sType) {
|
|
ps = BufPointer(&bufallocinfo);
|
|
pmeminfoDest->sType = ps;
|
|
strcpy(ps, pmeminfoSrc->sType);
|
|
BufAllocate(&bufallocinfo, strlen(ps)+1);
|
|
}
|
|
|
|
pmeminfoSrc = pmeminfoSrc->pmeminfoNext;
|
|
}
|
|
}
|
|
pTypesInfo->dwMemberSize = bufallocinfo.dwLen;
|
|
}
|
|
|
|
if (ppKnownTypes) {
|
|
*ppKnownTypes = pkt;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
__cdecl ErrMsg(
|
|
char *pch,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Displays an error message to stderr in a format that BUILD can find.
|
|
Use this instead of fprintf(stderr, ...).
|
|
|
|
Arguments:
|
|
|
|
pch -- printf-style format string
|
|
... -- printf-style args
|
|
|
|
Return Value:
|
|
|
|
None. Message formatted and sent to stderr.
|
|
|
|
--*/
|
|
{
|
|
va_list pArg;
|
|
|
|
fputs(ErrMsgPrefix, stderr);
|
|
va_start(pArg, pch);
|
|
vfprintf(stderr, pch, pArg);
|
|
}
|
|
|
|
|
|
void
|
|
__cdecl ExitErrMsg(
|
|
BOOL bSysError,
|
|
char *pch,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Displays an error message to stderr in a format that BUILD can find.
|
|
Use this instead of fprintf(stderr, ...).
|
|
|
|
Arguments:
|
|
|
|
bSysErr -- TRUE if the value of errno should be printed with the error
|
|
pch -- printf-style format string
|
|
... -- printf-style args
|
|
|
|
Return Value:
|
|
|
|
None. Message formatted and sent to stderr, open files closed and
|
|
deleted, process terminated.
|
|
|
|
--*/
|
|
{
|
|
va_list pArg;
|
|
if (bSysError) {
|
|
fprintf(stderr, "%s System ERROR %s", ErrMsgPrefix, strerror(errno));
|
|
} else {
|
|
fprintf(stderr, "%s ERROR ", ErrMsgPrefix);
|
|
}
|
|
|
|
va_start(pArg, pch);
|
|
vfprintf(stderr, pch, pArg);
|
|
|
|
CloseOpenFileList(TRUE);
|
|
|
|
//
|
|
// Flush stdout and stderr buffers, so that the last few printfs
|
|
// get sent back to BUILD before ExitProcess() destroys them.
|
|
//
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
|
|
ExitProcess(1);
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
__cdecl DbgPrintf(
|
|
char *pch,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Displays a message to stdout if bDebug is set.
|
|
|
|
Arguments:
|
|
|
|
pch -- printf-style format string
|
|
... -- printf-style args
|
|
|
|
Return Value:
|
|
|
|
None. Message formatted and sent to stderr.
|
|
|
|
--*/
|
|
{
|
|
va_list pArg;
|
|
|
|
if (!bDebug) {
|
|
return;
|
|
}
|
|
|
|
va_start(pArg, pch);
|
|
vfprintf(stdout, pch, pArg);
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
ReadEntireFile(
|
|
HANDLE hFile,
|
|
DWORD *pBytesRead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates memory on the local heap and reads an entire file into it.
|
|
|
|
Arguments:
|
|
|
|
hFile -- file to read in
|
|
bBytesRead -- [OUT] number of bytes read from the file
|
|
|
|
Return Value:
|
|
|
|
pointer to the memory allocated for the file, or NULL on error.
|
|
|
|
--*/
|
|
{
|
|
DWORD Bytes;
|
|
char *pch = NULL;
|
|
|
|
if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == 0xffffffff ||
|
|
(Bytes = GetFileSize(hFile, NULL)) == 0xffffffff) {
|
|
goto ErrorExit;
|
|
}
|
|
|
|
pch = GenHeapAlloc(Bytes);
|
|
if (!pch) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!ReadFile(hFile, pch, Bytes, pBytesRead, NULL) ||
|
|
*pBytesRead != Bytes) {
|
|
DbgPrintf("BytesRead %d Bytes %d\n", *pBytesRead, Bytes);
|
|
GenHeapFree(pch);
|
|
pch = NULL;
|
|
}
|
|
|
|
ErrorExit:
|
|
if (!pch) {
|
|
DbgPrintf("GetLastError %d\n", GetLastError());
|
|
}
|
|
|
|
return pch;
|
|
}
|
|
|
|
|
|
HANDLE
|
|
CreateTempFile(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates and opens a temporary file. It will be deleted when it is
|
|
closed.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
File handle, or INVALID_HANDLE_VALUE on error.
|
|
|
|
--*/
|
|
{
|
|
DWORD dw;
|
|
char PathName[MAX_PATH+1];
|
|
char FileName[2*MAX_PATH];
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
|
|
dw = GetTempPath(MAX_PATH, PathName);
|
|
if (!dw || dw > MAX_PATH) {
|
|
strcpy(PathName, ".");
|
|
}
|
|
|
|
dw = GetTempFileName(PathName, "thk", 0, FileName);
|
|
if (!dw) {
|
|
strcpy(PathName, ".");
|
|
dw = GetTempFileName(PathName, "thk", 0, FileName);
|
|
if (!dw) {
|
|
DbgPrintf("GetTempFileName %s GLE=%d\n", FileName, GetLastError());
|
|
}
|
|
}
|
|
|
|
hFile = CreateFile(FileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_TEMPORARY |
|
|
FILE_FLAG_DELETE_ON_CLOSE |
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
0
|
|
);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
DbgPrintf("Create %s GLE=%d\n", FileName, GetLastError());
|
|
}
|
|
|
|
return hFile;
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
CopyToken(
|
|
char *pDst,
|
|
char *pSrc,
|
|
size_t Size
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copies a token (a separator-delimited string) from pSrc to pDst.
|
|
|
|
Arguments:
|
|
|
|
pDst -- destination to write the token to
|
|
pSrc -- source to copy token from
|
|
Size -- number of bytes available at pDst.
|
|
|
|
Return Value:
|
|
|
|
Number of bytes copied from pSrc to pDst.
|
|
|
|
--*/
|
|
{
|
|
size_t i = 0;
|
|
|
|
while (!IsSeparator(*pSrc) && i < Size) {
|
|
i++;
|
|
*pDst++ = *pSrc++;
|
|
}
|
|
|
|
*pDst = '\0';
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
SkipKeyWord(
|
|
char *pSrc,
|
|
char *pKeyWord
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If the first word at pSrc matches the specified keyword, then skip
|
|
over that keyword.
|
|
|
|
Arguments:
|
|
|
|
pSrc -- source string to examine
|
|
pKeyWord -- keyword to try and match
|
|
|
|
Return Value:
|
|
|
|
pSrc unchanged if keyword not matched. If keyword matched, returns
|
|
ptr to text following the keyword after pSrc.
|
|
|
|
--*/
|
|
{
|
|
int LenKeyWord;
|
|
char *pch;
|
|
|
|
LenKeyWord = strlen(pKeyWord);
|
|
pch = pSrc + LenKeyWord;
|
|
|
|
if (!strncmp(pSrc, pKeyWord, LenKeyWord) && IsSeparator(*pch)) {
|
|
pSrc = GetNextToken(pch - 1);
|
|
}
|
|
|
|
return pSrc;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsSeparator(
|
|
char ch
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if a character is a separator or not.
|
|
over that keyword.
|
|
|
|
Arguments:
|
|
|
|
ch -- character to examine.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the character is a separator, FALSE if not.
|
|
|
|
--*/
|
|
{
|
|
switch (ch) {
|
|
case ' ':
|
|
case '|':
|
|
case '(':
|
|
case ')':
|
|
case '*':
|
|
case ',':
|
|
case '{':
|
|
case '}':
|
|
case ';':
|
|
case '[':
|
|
case ']':
|
|
case '=':
|
|
case '\n':
|
|
case '\r':
|
|
case ':':
|
|
case '.':
|
|
case '\0':
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* GetNextToken
|
|
*/
|
|
char *
|
|
GetNextToken(
|
|
char *pSrc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Scans the input string and returns the next separator-delimited string.
|
|
|
|
Arguments:
|
|
|
|
pSrc -- input string
|
|
|
|
Return Value:
|
|
|
|
Ptr to start of the next separator char which isn't a space.
|
|
|
|
--*/
|
|
{
|
|
if (!*pSrc) {
|
|
return pSrc;
|
|
}
|
|
|
|
if (!IsSeparator(*pSrc++)) {
|
|
while (*pSrc && !IsSeparator(*pSrc)) {
|
|
pSrc++;
|
|
}
|
|
}
|
|
|
|
while (*pSrc && *pSrc == ' ') {
|
|
pSrc++;
|
|
}
|
|
|
|
return pSrc;
|
|
}
|
|
|
|
|
|
void
|
|
DeleteAllocCvmHeap(
|
|
HANDLE hCvmHeap
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleans up the mapped shared memory.
|
|
|
|
Arguments:
|
|
|
|
hCvmHeap -- memory to clean up.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
CVMHEAPINFO *pcvmheap = (CVMHEAPINFO *)hCvmHeap;
|
|
|
|
Status = NtFreeVirtualMemory(NtCurrentProcess(),
|
|
(PVOID *)&pcvmheap->uBaseAddress,
|
|
&pcvmheap->uRegionSize,
|
|
MEM_RELEASE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrintf("Error freeing CVM %x", Status);
|
|
}
|
|
}
|
|
|
|
|
|
HANDLE
|
|
CreateAllocCvmHeap(
|
|
ULONG_PTR uBaseAddress,
|
|
ULONG_PTR uReserveSize,
|
|
ULONG_PTR uRegionSize,
|
|
ULONG_PTR uUncomitted,
|
|
ULONG_PTR uUnReserved,
|
|
ULONG_PTR uAvailable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a region of memory and makes it into a heap.
|
|
|
|
Arguments:
|
|
|
|
uBaseAddress -- base address to allocate the heap at
|
|
uReserveSize -- number of bytes to reserve
|
|
uRegionSize -- size of the region
|
|
uUncomitted -- amount of uncommitted memory
|
|
uUnReserved -- amount of unreserved memory
|
|
uAvailable -- amount of available memory
|
|
|
|
Return Value:
|
|
|
|
Handle to the heap, or NULL on error.
|
|
|
|
--*/
|
|
{
|
|
CVMHEAPINFO *pcvmheap;
|
|
NTSTATUS Status;
|
|
PULONG_PTR pBaseAddress= NULL;
|
|
|
|
pcvmheap = GenHeapAlloc(sizeof(CVMHEAPINFO));
|
|
if (pcvmheap == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
pcvmheap->uBaseAddress = uBaseAddress;
|
|
pcvmheap->uReserveSize = uReserveSize;
|
|
pcvmheap->uRegionSize = uRegionSize;
|
|
pcvmheap->uUncomitted = uUncomitted;
|
|
pcvmheap->uUnReserved = uUnReserved;
|
|
pcvmheap->uAvailable = uAvailable;
|
|
|
|
//
|
|
// Reserve enuf contiguous address space, for expected needs
|
|
//
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
(PVOID *)&pcvmheap->uBaseAddress,
|
|
0,
|
|
&pcvmheap->uReserveSize,
|
|
MEM_RESERVE,
|
|
PAGE_NOACCESS
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// May want to retry this, with a different base address
|
|
//
|
|
ErrMsg(
|
|
"Unable to reserve vm %x %x %x\n",
|
|
pcvmheap->uBaseAddress,
|
|
pcvmheap->uReserveSize,
|
|
Status
|
|
);
|
|
return NULL;
|
|
}
|
|
|
|
pcvmheap->uUnReserved = pcvmheap->uBaseAddress + pcvmheap->uReserveSize;
|
|
|
|
|
|
//
|
|
// Commit the first page, we will grow this a page at a time
|
|
// as its needed.
|
|
//
|
|
pcvmheap->uAvailable = pcvmheap->uBaseAddress;
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
(PVOID *)&pcvmheap->uAvailable,
|
|
0,
|
|
&pcvmheap->uRegionSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// May want to retry this, with a different base address
|
|
//
|
|
ErrMsg(
|
|
"Unable to commit vm %x %x %x\n",
|
|
pcvmheap->uBaseAddress,
|
|
pcvmheap->uReserveSize,
|
|
Status
|
|
);
|
|
return NULL;
|
|
}
|
|
|
|
pcvmheap->uUncomitted = pcvmheap->uBaseAddress + pcvmheap->uRegionSize;
|
|
|
|
|
|
// paranoia!
|
|
if (pcvmheap->uAvailable != pcvmheap->uBaseAddress) {
|
|
ErrMsg(
|
|
"commit pvAvailable(%x) != gBaseAddress(%x)\n",
|
|
pcvmheap->uAvailable,
|
|
pcvmheap->uBaseAddress
|
|
);
|
|
return NULL;
|
|
}
|
|
|
|
DbgPrintf("Ppm: BaseAddress %x\n", pcvmheap->uBaseAddress);
|
|
|
|
return pcvmheap;
|
|
}
|
|
|
|
|
|
PVOID
|
|
GetCvmHeapBaseAddress(
|
|
HANDLE hCvmHeap
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the base address of a heap.
|
|
|
|
Arguments:
|
|
|
|
hCvmHeap -- heap to examine
|
|
|
|
Return Value:
|
|
|
|
Base address, or NULL.
|
|
|
|
--*/
|
|
{
|
|
CVMHEAPINFO *pcvmheap = (CVMHEAPINFO *)hCvmHeap;
|
|
return pcvmheap == NULL ? NULL : (PVOID)pcvmheap->uBaseAddress;
|
|
}
|
|
|
|
|
|
PVOID
|
|
GetCvmHeapAvailable(
|
|
HANDLE hCvmHeap
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the number of bytes available in a heap.
|
|
|
|
Arguments:
|
|
|
|
hCvmHeap -- heap to examine
|
|
|
|
Return Value:
|
|
|
|
Bytes available, or NULL.
|
|
|
|
--*/
|
|
{
|
|
CVMHEAPINFO *pcvmheap = (CVMHEAPINFO *)hCvmHeap;
|
|
return pcvmheap == NULL ? NULL : (PVOID)pcvmheap->uAvailable;
|
|
}
|
|
|
|
|
|
PVOID
|
|
AllocCvm(
|
|
HANDLE hCvmHeap,
|
|
ULONG_PTR Size
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate memory from a heap.
|
|
|
|
Arguments:
|
|
|
|
hCvmHeam -- heap to allocate from
|
|
Size -- number of bytes to allocate
|
|
|
|
Return Value:
|
|
|
|
Ptr to allocated memory, or NULL of insufficient memory.
|
|
|
|
--*/
|
|
{
|
|
CVMHEAPINFO *pcvmheapinfo = (CVMHEAPINFO *)hCvmHeap;
|
|
NTSTATUS Status;
|
|
ULONG_PTR Available;
|
|
ULONG_PTR AlignedSize;
|
|
|
|
if (pcvmheapinfo == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Round the allocation up to the next-highest multiple of 8, so that
|
|
// allocations are correctly aligned.
|
|
//
|
|
AlignedSize = (Size + 7) & ~7;
|
|
|
|
Available = pcvmheapinfo->uAvailable;
|
|
pcvmheapinfo->uAvailable += AlignedSize;
|
|
|
|
if (pcvmheapinfo->uAvailable >= pcvmheapinfo->uUnReserved) {
|
|
ErrMsg("AllocCvm: Allocation Size exceeds reserved size\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (pcvmheapinfo->uAvailable >= pcvmheapinfo->uUncomitted) {
|
|
//
|
|
// Commit enuf pages to exceed the requested allocation size
|
|
//
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
(PVOID *)&pcvmheapinfo->uUncomitted,
|
|
0,
|
|
&Size,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ErrMsg(
|
|
"Unable to commit vm %x %x %x\n",
|
|
pcvmheapinfo->uBaseAddress,
|
|
Size,
|
|
Status
|
|
);
|
|
return NULL;
|
|
}
|
|
|
|
pcvmheapinfo->uUncomitted += Size;
|
|
}
|
|
|
|
return (PVOID)Available;
|
|
}
|
|
|
|
|
|
|
|
void ParseIndirection(
|
|
DWORD *pIndLevel,
|
|
DWORD *pdwSize,
|
|
DWORD *pFlags,
|
|
PTOKENTYPE ptkPrePostMod,
|
|
PTOKENTYPE ptkPostMod
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse any indirection level specificiations ('*') taking into
|
|
account const, volatile, and __ptr64 modifiers. For example:
|
|
void * const __ptr64 ** const * __ptr64 would be valid.
|
|
|
|
NOTE: the pointer is a 64-bit pointer only if the last pointer
|
|
declared is modified by __ptr64.
|
|
|
|
Arguments:
|
|
|
|
pIndlevel -- [OUT] indirection level (number of '*'s)
|
|
pdwSize -- [OUT] size of the type (4 or 8)
|
|
pFlags -- [OUT] BTI_ flags
|
|
ptkPrePostMod -- [OUT] TK_CONST, TK_VOLATILE, or TK_NONE, depending
|
|
on modifiers seen before the first '*'
|
|
ptkPostMod -- [OUT] TK_CONST, TK_VOLATILE, or TK_NONE, depending
|
|
on modifiers seen after the first '*'
|
|
|
|
Return Value:
|
|
|
|
None. May not consume any tokens if there are no levels of indirection.
|
|
|
|
--*/
|
|
{
|
|
int IndLevel = 0;
|
|
DWORD dwSize = 0;
|
|
DWORD Flags = 0;
|
|
BOOL fStopScanning = FALSE;
|
|
TOKENTYPE tkPrePostMod = TK_NONE;
|
|
TOKENTYPE tkPostMod = TK_NONE;
|
|
|
|
do {
|
|
switch (CurrentToken()->TokenType) {
|
|
case TK_BITWISE_AND:
|
|
////////////////////////////////////////////////////////////////////
|
|
//The ref operator in C++ is equilivalent to * const in C
|
|
//This implies that & should be treated as a * but add a postmod of const.
|
|
/////////////////////////////////////////////////////////////////////
|
|
tkPostMod = TK_CONST;
|
|
case TK_STAR:
|
|
IndLevel++;
|
|
dwSize = SIZEOFPOINTER;
|
|
Flags &= ~BTI_PTR64;
|
|
ConsumeToken();
|
|
break;
|
|
|
|
case TK_CONST:
|
|
case TK_VOLATILE:
|
|
//
|
|
// The caller may be interrested in whether the 'const' or
|
|
// 'volatile' keywords are before or after the '*'
|
|
//
|
|
if (IndLevel) {
|
|
tkPostMod = CurrentToken()->TokenType;
|
|
} else {
|
|
tkPrePostMod = CurrentToken()->TokenType;
|
|
}
|
|
ConsumeToken();
|
|
break;
|
|
|
|
case TK_IDENTIFIER:
|
|
if (strcmp(CurrentToken()->Name, sz__PTR64) == 0) {
|
|
dwSize = SIZEOFPOINTER64;
|
|
Flags |= BTI_PTR64;
|
|
ConsumeToken();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
fStopScanning = TRUE;
|
|
break;
|
|
}
|
|
} while (!fStopScanning);
|
|
|
|
if (pIndLevel != NULL) {
|
|
*pIndLevel += IndLevel;
|
|
}
|
|
if ((pdwSize != NULL) && (dwSize != 0)) {
|
|
*pdwSize = dwSize;
|
|
}
|
|
if (pFlags != NULL) {
|
|
*pFlags |= Flags;
|
|
}
|
|
if (ptkPostMod) {
|
|
*ptkPostMod = tkPostMod;
|
|
}
|
|
if (ptkPrePostMod) {
|
|
*ptkPrePostMod = tkPrePostMod;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
IsTokenSeparator(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if a token is a separator character or not.
|
|
|
|
Arguments:
|
|
|
|
None. Examines CurrentToken()->TokenType.
|
|
|
|
Return Value:
|
|
|
|
TRUE if CurrentToken() is a separator, FALSE if not.
|
|
|
|
--*/
|
|
{
|
|
switch (CurrentToken()->TokenType) {
|
|
case TK_LPAREN:
|
|
case TK_RPAREN:
|
|
case TK_STAR:
|
|
case TK_BITWISE_AND:
|
|
case TK_COMMA:
|
|
case TK_LBRACE:
|
|
case TK_RBRACE:
|
|
case TK_SEMI:
|
|
case TK_LSQUARE:
|
|
case TK_RSQUARE:
|
|
case TK_COLON:
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
ReleaseToken(
|
|
PTOKEN Token
|
|
)
|
|
{
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Releases any additional memory associated with a token.
|
|
|
|
Arguments:
|
|
|
|
dest - [IN] ptr to the token.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
if (Token->TokenType == TK_IDENTIFIER ||
|
|
Token->TokenType == TK_STRING) {
|
|
GenHeapFree(Token->Name);
|
|
}
|
|
Token->TokenType = TK_NONE;
|
|
Token->Value = 0;
|
|
Token->dwValue = 0;
|
|
}
|
|
|
|
void
|
|
ResetLexer(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resets the lexer in preparation to analyze a new statement.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None. Lexer's state reset.
|
|
|
|
--*/
|
|
{
|
|
int TokenCount;
|
|
|
|
for (TokenCount = 0;
|
|
TokenCount < MAX_TOKENS_IN_STATEMENT &&
|
|
Tokens[TokenCount].TokenType != TK_EOS;
|
|
++TokenCount) {
|
|
|
|
ReleaseToken(&Tokens[TokenCount]);
|
|
}
|
|
|
|
CurrentTokenIndex = 0;
|
|
}
|
|
|
|
__inline
|
|
VOID
|
|
InitializeToken(
|
|
PTOKEN Token
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize a token so the lexer can fill it in.
|
|
|
|
Arguments:
|
|
|
|
Token -- TOKEN to initialize
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
// The number parser expects Value to be 0.
|
|
Token->TokenType = TK_NONE;
|
|
Token->Value = 0;
|
|
Token->dwValue = 0;
|
|
}
|
|
|
|
void
|
|
ProcessEscapes(
|
|
char *String
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process escape characters, replacing them by the proper char.
|
|
|
|
Arguments:
|
|
|
|
String -- null-terminated string to process
|
|
|
|
Return Value:
|
|
|
|
None. Conversion is done in-place.
|
|
|
|
--*/
|
|
{
|
|
char *pDest;
|
|
char *pSrc;
|
|
char c;
|
|
int i;
|
|
|
|
pSrc = pDest = String;
|
|
while (*pSrc) {
|
|
if (*pSrc != '\\') {
|
|
*pDest = *pSrc;
|
|
pSrc++;
|
|
pDest++;
|
|
} else {
|
|
pSrc++;
|
|
switch (*pSrc) {
|
|
case 'n':
|
|
c = '\n';
|
|
break;
|
|
|
|
case 't':
|
|
c = '\t';
|
|
break;
|
|
|
|
case 'v':
|
|
c = '\v';
|
|
break;
|
|
|
|
case 'b':
|
|
c = '\b';
|
|
break;
|
|
|
|
case 'r':
|
|
c = '\r';
|
|
break;
|
|
|
|
case 'f':
|
|
c = '\f';
|
|
break;
|
|
|
|
case 'a':
|
|
c = '\a';
|
|
break;
|
|
|
|
case '\\':
|
|
c = '\\';
|
|
break;
|
|
|
|
case '?':
|
|
c = '\?';
|
|
break;
|
|
|
|
case '\'':
|
|
c = '\'';
|
|
break;
|
|
|
|
case '\"':
|
|
c = '\"';
|
|
break;
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
// Octal number
|
|
c = 0;
|
|
for (i=0; i<3;++i) {
|
|
c = (c * 8) + (*pSrc) - '0';
|
|
pSrc++;
|
|
if (*pSrc < '0' || *pSrc > '7') {
|
|
// hit end of number
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'x':
|
|
case 'X':
|
|
// Hex number
|
|
pSrc++;
|
|
c = 0;
|
|
for (i=0; i<3;++i) {
|
|
char digit;
|
|
|
|
digit = *pSrc;
|
|
if (digit >= '0' && digit <= '9') {
|
|
digit -= '0';
|
|
} else if (digit >= 'a' && digit <= 'f') {
|
|
digit = digit - 'a' + 10;
|
|
} else if (digit >= 'A' && digit <= 'A') {
|
|
digit = digit - 'A' + 10;
|
|
} else {
|
|
// hit end of number
|
|
break;
|
|
}
|
|
c = (c * 16) + digit;
|
|
pSrc++;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Parse error in the string literal.
|
|
goto Exit;
|
|
|
|
}
|
|
*pDest = c;
|
|
pDest++;
|
|
}
|
|
}
|
|
Exit:
|
|
// Write the new null-terminator in
|
|
*pDest = '\0';
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
LexOneLine(
|
|
char *p,
|
|
BOOL fStopAtStatement,
|
|
BOOL *pfLexDone
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs lexical analysis on a single line of input. The lexer
|
|
may stop before consuming an entire line of input, so the caller
|
|
must closely examine the return code before grabbing the next line.
|
|
|
|
__inline functions are deleted by the lexer. The lexer consumes input
|
|
until it encounters a '{' (assumed to be the start of the function
|
|
body), then consumes input until the matching '}' is found (assumed to
|
|
be the end of the function body).
|
|
|
|
"template" is deleted by the lexer and treated as if it was
|
|
an "__inline" keyword... it consumes everything upto '{' then
|
|
keeps consuming until a matching '}' is found. That makes unknwn.h
|
|
work.
|
|
|
|
Lexer unwraps extern "C" {} blocks.
|
|
|
|
'static' and '__unaligned' keywords are deleted by the lexer.
|
|
|
|
Preprocessor directives are handled via a callout to
|
|
HandlePreprocessorDirective().
|
|
|
|
Arguments:
|
|
|
|
p -- ptr into the line of input
|
|
fStopAtStatement -- TRUE if caller wants lexer to stop at ';' at
|
|
file-scope. FALSE if caller wants lexer to stop
|
|
at ')' at file-scope.
|
|
pfLexDone -- [OUT] lexer sets this to TRUE if the analysis
|
|
is complete. Lexer sets this to FALSE if
|
|
it needs another line of input from the caller.
|
|
|
|
Return Value:
|
|
|
|
ptr into the line of input where lexing left off, or NULL if entire
|
|
line was consumed.
|
|
|
|
CurrentTokenIndex is the index of the next element of the Tokens[]
|
|
array that the lexer will fill in.
|
|
|
|
Tokens[] is the array of tokens the lexer has generated.
|
|
|
|
--*/
|
|
{
|
|
static int NestingLevel=0; // level of nesting of braces and parens
|
|
static BOOL fInlineSeen=FALSE; // TRUE while deleting __inline functions
|
|
static int ExternCLevel=0; // tracks the number of extern "C" blocks
|
|
static int InlineLevel=0; // NestingLevel for the outermost __inline
|
|
int Digit; // a digit in a numeric constant
|
|
int NumberBase = 10; // assume numbers are base-10
|
|
PTOKEN Token; // ptr to current token being lexed
|
|
|
|
//
|
|
// Assume the lexical analysis is not done
|
|
//
|
|
*pfLexDone = FALSE;
|
|
|
|
//
|
|
// Pick up analysis where we left off...
|
|
//
|
|
Token = &Tokens[CurrentTokenIndex];
|
|
InitializeToken(Token);
|
|
|
|
//
|
|
// Loop over all characters in the line, or until a complete lexical
|
|
// unit is done (depends on fStopAtStatement).
|
|
//
|
|
while (*p) {
|
|
switch (*p) {
|
|
case ' ':
|
|
case '\t':
|
|
case '\r':
|
|
case '\n':
|
|
case '\v':
|
|
case '\f':
|
|
case '\b':
|
|
case '\a':
|
|
case '\\': // line-continuation characters are ignored
|
|
p++;
|
|
continue;
|
|
|
|
case '#':
|
|
//
|
|
// HandlePreprocessorDirective() is implemented in the
|
|
// app which links to genmisc.c.
|
|
//
|
|
HandlePreprocessorDirective(p);
|
|
CurrentTokenIndex = (int)(Token - Tokens);
|
|
return NULL;
|
|
|
|
case '+':
|
|
Token->TokenType = TK_PLUS;
|
|
break;
|
|
|
|
case '-':
|
|
Token->TokenType = TK_MINUS;
|
|
break;
|
|
|
|
case ':':
|
|
Token->TokenType = TK_COLON;
|
|
break;
|
|
|
|
case '=':
|
|
Token->TokenType = TK_ASSIGN;
|
|
break;
|
|
|
|
case ';':
|
|
if (NestingLevel == 0 && fStopAtStatement) {
|
|
//
|
|
// Found a ';' at file-scope. This token marks the
|
|
// end of the C-language statement.
|
|
//
|
|
p++;
|
|
if (*p == '\n') {
|
|
//
|
|
// ';' is at EOL - consume it now.
|
|
//
|
|
p++;
|
|
}
|
|
Token->TokenType = TK_EOS;
|
|
*pfLexDone = TRUE;
|
|
CurrentTokenIndex = (int)(Token - Tokens + 1);
|
|
return p;
|
|
}
|
|
Token->TokenType = TK_SEMI;
|
|
break;
|
|
|
|
case '*':
|
|
Token->TokenType = TK_STAR;
|
|
break;
|
|
|
|
case '/':
|
|
Token->TokenType = TK_DIVIDE;
|
|
break;
|
|
|
|
case ',':
|
|
Token->TokenType = TK_COMMA;
|
|
break;
|
|
|
|
case '<':
|
|
if (p[1] == '<') {
|
|
Token->TokenType = TK_LSHIFT;
|
|
p++;
|
|
} else {
|
|
Token->TokenType = TK_LESS;
|
|
}
|
|
break;
|
|
|
|
case '>':
|
|
if (p[1] == '>') {
|
|
Token->TokenType = TK_RSHIFT;
|
|
p++;
|
|
} else {
|
|
Token->TokenType = TK_GREATER;
|
|
}
|
|
break;
|
|
|
|
case '&':
|
|
if (p[1] == '&') {
|
|
Token->TokenType = TK_LOGICAL_AND;
|
|
p++;
|
|
} else {
|
|
Token->TokenType = TK_BITWISE_AND;
|
|
}
|
|
break;
|
|
|
|
case '|':
|
|
if (p[1] == '|') {
|
|
Token->TokenType = TK_LOGICAL_OR;
|
|
p++;
|
|
} else {
|
|
Token->TokenType = TK_BITWISE_OR;
|
|
}
|
|
break;
|
|
|
|
case '%':
|
|
Token->TokenType = TK_MOD;
|
|
break;
|
|
|
|
case '^':
|
|
Token->TokenType = TK_XOR;
|
|
break;
|
|
|
|
case '!':
|
|
Token->TokenType = TK_NOT;
|
|
break;
|
|
|
|
case '~':
|
|
Token->TokenType = TK_TILDE;
|
|
break;
|
|
|
|
case '[':
|
|
Token->TokenType = TK_LSQUARE;
|
|
break;
|
|
|
|
case ']':
|
|
Token->TokenType = TK_RSQUARE;
|
|
break;
|
|
|
|
case '(':
|
|
NestingLevel++;
|
|
Token->TokenType = TK_LPAREN;
|
|
break;
|
|
|
|
case ')':
|
|
NestingLevel--;
|
|
if (NestingLevel == 0 && !fStopAtStatement) {
|
|
//
|
|
// Found a ')' at file-scope, and we're lexing
|
|
// the contents of an @-command in genthnk.
|
|
// Time to stop lexing.
|
|
//
|
|
p++;
|
|
Token->TokenType = TK_EOS;
|
|
*pfLexDone = TRUE;
|
|
CurrentTokenIndex = (int)(Token - Tokens + 1);
|
|
return p;
|
|
} else if (NestingLevel < 0) {
|
|
ExitErrMsg(FALSE, "Parse Error: mismatched nested '(' and ')'\n");
|
|
}
|
|
Token->TokenType = TK_RPAREN;
|
|
break;
|
|
|
|
case '{':
|
|
//check for a 'extern "C" {}' or 'extern "C++" {}'
|
|
if (Token - Tokens >= 2 &&
|
|
Token[-2].TokenType == TK_EXTERN &&
|
|
Token[-1].TokenType == TK_STRING &&
|
|
(strcmp(Token[- 1].Name, "C") == 0 || strcmp(Token[-1].Name, "C++") == 0)) {
|
|
|
|
if (NestingLevel == 0 && fInlineSeen) {
|
|
ExitErrMsg(FALSE, "Extern \"C\" blocks only supported at file scope\n");
|
|
}
|
|
ExternCLevel++;
|
|
|
|
|
|
//remove the last 2 tokens and skip this token
|
|
ReleaseToken(Token - 2);
|
|
ReleaseToken(Token - 1);
|
|
Token -= 2;
|
|
p++;
|
|
continue;
|
|
}
|
|
|
|
NestingLevel++;
|
|
Token->TokenType = TK_LBRACE;
|
|
break;
|
|
|
|
case '.':
|
|
if (p[1] == '.' && p[2] == '.') {
|
|
Token->TokenType = TK_VARGS;
|
|
p+=2;
|
|
} else {
|
|
Token->TokenType = TK_DOT;
|
|
}
|
|
break;
|
|
|
|
case '}':
|
|
if (NestingLevel == 0 && ExternCLevel > 0) {
|
|
//omit this token since it is the end of an extern "C" block
|
|
ExternCLevel--;
|
|
p++;
|
|
continue;
|
|
}
|
|
NestingLevel--;
|
|
if (NestingLevel < 0) {
|
|
ExitErrMsg(FALSE, "Parse Error: mismatched nested '{' and '}'\n");
|
|
}
|
|
else if (NestingLevel == InlineLevel && fInlineSeen) {
|
|
//
|
|
// Found the closing '}' for the end of an inline
|
|
// function. Advance past the '}' and start lexing
|
|
// again as if the __inline was never there.
|
|
//
|
|
fInlineSeen = FALSE;
|
|
p++;
|
|
continue;
|
|
}
|
|
else {
|
|
Token->TokenType = TK_RBRACE;
|
|
}
|
|
break;
|
|
|
|
case '0':
|
|
if (p[1] == 'x' || p[1] == 'X') {
|
|
//
|
|
// Found '0x' prefix - the token is a hex constant
|
|
//
|
|
Token->TokenType = TK_NUMBER;
|
|
|
|
for (p+=2; *p != '\0'; p++) {
|
|
if (isdigit(*p)) {
|
|
int i;
|
|
i = *p - '0';
|
|
Token->Value = Token->Value * 16 + i;
|
|
Token->dwValue = Token->dwValue * 16 + i;
|
|
} else {
|
|
char c = (char)toupper(*p);
|
|
if (c >= 'A' && c <= 'F') {
|
|
int i;
|
|
i = c - 'A' + 10;
|
|
Token->Value = Token->Value * 16 + i;
|
|
Token->dwValue = Token->dwValue * 16 + i;
|
|
} else if (c == 'L') {
|
|
//
|
|
// Numeric constant ending in 'L' is a long-integer
|
|
// type.
|
|
//
|
|
break;
|
|
} else if (isalpha(c)) {
|
|
DumpLexerOutput(0);
|
|
ExitErrMsg(FALSE, "Parse Error in hex constant.\n");
|
|
} else {
|
|
p--;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
break;
|
|
} else if (isdigit(p[1])) {
|
|
//
|
|
// Found '0' followed by a valid number - the token is
|
|
// an octal constant.
|
|
//
|
|
NumberBase = 8;
|
|
|
|
}
|
|
// fall into general number processing code
|
|
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
Token->TokenType = TK_NUMBER;
|
|
|
|
for (; *p != '\0'; p++) {
|
|
Digit = *p - '0';
|
|
if (*p == 'l' || *p == 'L') {
|
|
//
|
|
// Numeric constant ending in 'l' is a long-integer
|
|
//
|
|
break;
|
|
} else if (Digit < 0 || Digit >= NumberBase) {
|
|
p--;
|
|
break;
|
|
}
|
|
Token->Value = Token->Value * NumberBase + Digit;
|
|
Token->dwValue = Token->dwValue * NumberBase + Digit;
|
|
}
|
|
break;
|
|
|
|
case '\'':
|
|
Token->TokenType = TK_NUMBER;
|
|
p++; //skip past beginning '
|
|
for(; *p != '\''; p++) {
|
|
if (*p == '\0') {
|
|
ExitErrMsg(FALSE, "\' without ending \'\n");
|
|
}
|
|
Token->Value = Token->Value << 8 | (UCHAR)*p;
|
|
Token->dwValue = Token->dwValue << 8 | (UCHAR)*p;
|
|
}
|
|
break;
|
|
|
|
case '"':
|
|
// A string literal. ie. char *p = "foo";
|
|
{
|
|
char *strStart;
|
|
|
|
Token->TokenType = TK_STRING;
|
|
strStart = ++p; //skip begining quote
|
|
|
|
//get a count of the number of characters
|
|
while (*p != '\0' && *p != '"') p++;
|
|
|
|
if ('\0' == *p || '\0' == *(p+1)) {
|
|
ExitErrMsg(FALSE, "String without ending quote\n");
|
|
}
|
|
p++; //skip past the ending quote
|
|
|
|
Token->Name = GenHeapAlloc(p - strStart); //1+strlen
|
|
if (Token->Name == NULL) {
|
|
ExitErrMsg(FALSE, "Out of memory in lexer\n");
|
|
}
|
|
|
|
memcpy(Token->Name, strStart, p-strStart-1);
|
|
Token->Name[p-strStart-1] = '\0';
|
|
p--;
|
|
ProcessEscapes(Token->Name);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (*p == '_' || isalpha(*p)) {
|
|
//
|
|
// An identifier or keyword
|
|
//
|
|
char *IdStart = p;
|
|
|
|
Token->TokenType = TK_IDENTIFIER;
|
|
|
|
while (*p == '_' || isalpha(*p) || isdigit(*p)) {
|
|
p++;
|
|
}
|
|
Token->Name = GenHeapAlloc(p - IdStart + 1);
|
|
if (Token->Name == NULL) {
|
|
ExitErrMsg(FALSE, "Out of memory in lexer\n");
|
|
}
|
|
memcpy(Token->Name, IdStart, p-IdStart);
|
|
Token->Name[p-IdStart] = '\0';
|
|
|
|
CheckForKeyword(Token);
|
|
if (Token->TokenType == TK_TEMPLATE) {
|
|
fInlineSeen = TRUE;
|
|
InlineLevel = NestingLevel; // want to get back to the same scope
|
|
} else if (Token->TokenType == TK_INLINE) {
|
|
if (NestingLevel) {
|
|
//
|
|
// __inline keyword embedded inside {}. It's
|
|
// technically an error but we want to allow it
|
|
// during inclusion of ntcb.h.
|
|
//
|
|
continue;
|
|
}
|
|
fInlineSeen = TRUE;
|
|
InlineLevel = 0; // want to get back to file scope
|
|
} else if (Token->TokenType == TK_STATIC ||
|
|
Token->TokenType == TK_UNALIGNED ||
|
|
Token->TokenType == TK_RESTRICT ||
|
|
Token->TokenType == TK___W64) {
|
|
// filter out 'static', '__restrict', '__unaligned' and '__w64'
|
|
// keywords
|
|
continue;
|
|
}
|
|
p--;
|
|
} else if (fInlineSeen) {
|
|
//
|
|
// While processing __inline functions, the lexer is
|
|
// going to encounter all sorts of weird characters
|
|
// in __asm blocks, etc. Just ignore them and keep
|
|
// consuming input.
|
|
//
|
|
p++;
|
|
continue;
|
|
} else {
|
|
ExitErrMsg(FALSE, "Lexer: unexpected char '%c' (0x%x) found\n", *p, *p);
|
|
}
|
|
} // switch
|
|
|
|
p++;
|
|
if (!fInlineSeen) {
|
|
Token++;
|
|
if (Token == &Tokens[MAX_TOKENS_IN_STATEMENT]) {
|
|
ExitErrMsg(FALSE, "Lexer internal error - too many tokens in this statement.");
|
|
}
|
|
InitializeToken(Token);
|
|
}
|
|
} // while (*p)
|
|
|
|
//
|
|
// Hit end-of-line. Indicate this to the caller
|
|
//
|
|
Token->TokenType = TK_EOS;
|
|
CurrentTokenIndex = (int)(Token - Tokens);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
CheckForKeyword(
|
|
PTOKEN Token
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a TK_INDENTIFIER token into a C-language keyword token, if
|
|
the identifier is in the KeywordList[].
|
|
|
|
Arguments:
|
|
|
|
Token -- Token to convert
|
|
|
|
Return Value:
|
|
|
|
None. Token->TokenType and Token->Name may be changed.
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
int r;
|
|
|
|
for (i=0; KeywordList[i].MatchString; ++i) {
|
|
r = strcmp(Token->Name, KeywordList[i].MatchString);
|
|
if (r == 0) {
|
|
GenHeapFree(Token->Name);
|
|
Token->Name = NULL;
|
|
Token->TokenType = KeywordList[i].Tk;
|
|
return;
|
|
} else if (r < 0) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DumpLexerOutput(
|
|
int FirstToken
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Debug routine to dump out the Token list as human-readable text.
|
|
|
|
Arguments:
|
|
|
|
FirstToken -- Index of the first token to list back.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<FirstToken; ++i) {
|
|
if (Tokens[i].TokenType == TK_EOS) {
|
|
fprintf(stderr, "DumpLexerOutput: FirstToken %d is after EOS at %d\n", FirstToken, i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "Lexer: ");
|
|
for (i=FirstToken; Tokens[i].TokenType != TK_EOS; ++i) {
|
|
switch (Tokens[i].TokenType) {
|
|
case TK_NUMBER:
|
|
fprintf(stderr, "0x%X ", Tokens[i].Value);
|
|
break;
|
|
|
|
case TK_IDENTIFIER:
|
|
case TK_STRING:
|
|
fprintf(stderr, "%s ", Tokens[i].Name);
|
|
break;
|
|
|
|
case TK_NONE:
|
|
fprintf(stderr, "<TK_NONE> ");
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "%s ", TokenString[(int)Tokens[i].TokenType]);
|
|
break;
|
|
}
|
|
}
|
|
fprintf(stderr, "<EOS>\n");
|
|
}
|
|
|
|
|
|
BOOL
|
|
UnlexToText(
|
|
char *dest,
|
|
int destlen,
|
|
int StartToken,
|
|
int EndToken
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a sequence of Tokens back into human-readable text.
|
|
|
|
Arguments:
|
|
|
|
dest -- ptr to destination buffer
|
|
destlen -- length of destination buffer
|
|
StartToken -- index of first token to list back
|
|
EndToken -- index of last token (this token is *not* listed back)
|
|
|
|
Return Value:
|
|
|
|
TRUE if Unlex successful. FALSE if failure (ie. buffer overflow).
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
int len;
|
|
char buffer[16];
|
|
char *src;
|
|
|
|
if (bDebug) {
|
|
for (i=0; i<StartToken; ++i) {
|
|
if (Tokens[i].TokenType == TK_EOS) {
|
|
ErrMsg("UnlexToText: StartToken %d is after EOS %d\n", StartToken, i);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i=StartToken; i<EndToken; ++i) {
|
|
switch (Tokens[i].TokenType) {
|
|
case TK_EOS:
|
|
return FALSE;
|
|
|
|
case TK_NUMBER:
|
|
sprintf(buffer, "%d", Tokens[i].Value);
|
|
src = buffer;
|
|
break;
|
|
|
|
case TK_IDENTIFIER:
|
|
case TK_STRING:
|
|
src = Tokens[i].Name;
|
|
break;
|
|
|
|
case TK_NONE:
|
|
src = "<TK_NONE>";
|
|
break;
|
|
|
|
default:
|
|
src = TokenString[(int)Tokens[i].TokenType];
|
|
break;
|
|
}
|
|
|
|
len = strlen(src);
|
|
if (len+1 > destlen) {
|
|
return FALSE;
|
|
}
|
|
strcpy(dest, src);
|
|
dest += len;
|
|
*dest = ' ';
|
|
dest++;
|
|
destlen -= len+1;
|
|
}
|
|
dest--; // back up over the trailing ' '
|
|
*dest = '\0'; // null-terminate
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PVOID
|
|
GenHeapAlloc(
|
|
INT_PTR Len
|
|
)
|
|
{
|
|
return RtlAllocateHeap(RtlProcessHeap(), 0, Len);
|
|
}
|
|
|
|
void
|
|
GenHeapFree(
|
|
PVOID pv
|
|
)
|
|
{
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pv);
|
|
}
|
|
|
|
|
|
TOKENTYPE
|
|
ConsumeDirectionOpt(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Comsumes a TK_IN or TK_OUT, if present in the lexer stream. TK_IN
|
|
followed by TK_OUT is converted to TK_INOUT.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TK_IN, TK_OUT, TK_INOUT, or TK_NONE.
|
|
|
|
--*/
|
|
{
|
|
TOKENTYPE t = CurrentToken()->TokenType;
|
|
|
|
switch (t) {
|
|
case TK_IN:
|
|
ConsumeToken();
|
|
if (CurrentToken()->TokenType == TK_OUT) {
|
|
ConsumeToken();
|
|
t = TK_INOUT;
|
|
}
|
|
break;
|
|
|
|
case TK_OUT:
|
|
ConsumeToken();
|
|
if (CurrentToken()->TokenType == TK_IN) {
|
|
ConsumeToken();
|
|
t = TK_INOUT;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
t = TK_NONE;
|
|
break;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
|
|
TOKENTYPE
|
|
ConsumeConstVolatileOpt(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Comsumes a TK_CONST or TK_VOLATILE, if present in the lexer stream.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TK_CONST, TK_VOLATILE, or TK_NONE.
|
|
|
|
--*/
|
|
{
|
|
TOKENTYPE t = CurrentToken()->TokenType;
|
|
|
|
switch (t) {
|
|
case TK_CONST:
|
|
case TK_VOLATILE:
|
|
ConsumeToken();
|
|
break;
|
|
|
|
default:
|
|
t = TK_NONE;
|
|
break;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
|
|
PMEMBERINFO
|
|
AllocMemInfoAndLink(
|
|
BUFALLOCINFO *pbufallocinfo,
|
|
PMEMBERINFO pmeminfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a new MEMBERINFO struct from the buffer
|
|
|
|
Arguments:
|
|
|
|
pbufallocinfo -- ptr to memory buffer to allocate from
|
|
pmeminfo -- ptr to list of MEMBERINFOs to link the new one into
|
|
|
|
Return Value:
|
|
|
|
Newly-allocated, initialized, linked-in MEMBERINFO struct (or NULL)
|
|
|
|
--*/
|
|
{
|
|
PMEMBERINFO pmeminfoNext;
|
|
|
|
pmeminfoNext = BufAllocate(pbufallocinfo, sizeof(MEMBERINFO));
|
|
if (pmeminfoNext) {
|
|
if (pmeminfo) {
|
|
pmeminfo->pmeminfoNext = pmeminfoNext;
|
|
}
|
|
memset(pmeminfoNext, 0, sizeof(MEMBERINFO));
|
|
}
|
|
return pmeminfoNext;
|
|
}
|
|
|
|
PFUNCINFO
|
|
AllocFuncInfoAndLink(
|
|
BUFALLOCINFO *bufallocinfo,
|
|
PFUNCINFO pfuncinfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a new FUNCINFO struct from the buffer
|
|
|
|
Arguments:
|
|
|
|
pbufallocinfo -- ptr to memory buffer to allocate from
|
|
pmeminfo -- ptr to list of FUNCINFOs to link the new one into
|
|
|
|
Return Value:
|
|
|
|
Newly-allocated, initialized, linked-in FUNCINFO struct (or NULL)
|
|
|
|
--*/
|
|
{
|
|
PFUNCINFO pfuncinfoNext;
|
|
|
|
pfuncinfoNext = BufAllocate(bufallocinfo, sizeof(FUNCINFO));
|
|
if ((pfuncinfoNext != NULL) && (pfuncinfo != NULL)) {
|
|
pfuncinfo->pfuncinfoNext = pfuncinfoNext;
|
|
pfuncinfoNext->sName = NULL;
|
|
pfuncinfoNext->sType = NULL;
|
|
}
|
|
return pfuncinfoNext;
|
|
}
|
|
|
|
DWORD
|
|
SizeOfMultiSz(
|
|
char *c
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines the number of bytes used by double '\0' terminated list.
|
|
|
|
Arguments:
|
|
|
|
c - [IN] ptr to the double '\0' termined list.
|
|
|
|
Return Value:
|
|
|
|
Bytes used.
|
|
--*/
|
|
DWORD dwSize = 1;
|
|
char cPrevChar = '\0'+1;
|
|
do {
|
|
dwSize++;
|
|
cPrevChar = *c;
|
|
} while(*++c != '\0' || cPrevChar != '\0');
|
|
return dwSize;
|
|
}
|
|
|
|
BOOL
|
|
CatMultiSz(
|
|
char *dest,
|
|
char *source,
|
|
DWORD dwMaxSize
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Concatinates two double '\0' terminated lists.
|
|
New list is stored at dest.
|
|
|
|
Arguments:
|
|
|
|
dest - [IN/OUT] ptr to the head double '\0' terminated list.
|
|
element - [IN] ptr to the head double '\0' terminated list.
|
|
dwMaxSize - [IN] max size of the new list in bytes.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success.
|
|
FALSE - Failure.
|
|
--*/
|
|
//Find end of MultiSz
|
|
DWORD dwLengthDest, dwLengthSource;
|
|
dwLengthDest = SizeOfMultiSz(dest);
|
|
if (2 == dwLengthDest) dwLengthDest = 0;
|
|
else dwLengthDest--;
|
|
dwLengthSource = SizeOfMultiSz(source);
|
|
if (dwLengthDest + dwLengthSource > dwMaxSize) return FALSE;
|
|
memcpy(dest + dwLengthDest, source, dwLengthSource);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
AppendToMultiSz(
|
|
char *dest,
|
|
char *source,
|
|
DWORD dwMaxSize
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a string to the end of a double '\0' terminated list.
|
|
|
|
Arguments:
|
|
|
|
dest - [IN/OUT] ptr to the double '\0' terminated list.
|
|
source - [IN] ptr to the string to add.
|
|
dwMaxSize - [IN] max number of bytes that can be used by the list.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success.
|
|
FALSE - Failure.
|
|
--*/
|
|
DWORD dwLengthDest, dwLengthSource;
|
|
dwLengthDest = SizeOfMultiSz(dest);
|
|
if (2 == dwLengthDest) dwLengthDest = 0;
|
|
else dwLengthDest--;
|
|
dwLengthSource = strlen(source) + 1;
|
|
if (dwLengthDest + dwLengthSource + 1 > dwMaxSize) return FALSE;
|
|
memcpy(dest + dwLengthDest, source, dwLengthSource);
|
|
*(dest + dwLengthDest + dwLengthSource) = '\0';
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL IsInMultiSz(
|
|
const char *multisz,
|
|
const char *element
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if a string exists in a double '\0' terminated list.
|
|
|
|
Arguments:
|
|
|
|
ppHead - [IN] ptr to the double '\0' terminated list.
|
|
element - [IN] ptr to the element to find.
|
|
Return Value:
|
|
|
|
TRUE - element is in the list.
|
|
FALSE - element is not in the list.
|
|
--*/
|
|
do {
|
|
if (strcmp(multisz, element) == 0) return TRUE;
|
|
//skip to end of string
|
|
while(*multisz++ != '\0');
|
|
} while(*multisz != '\0');
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
ConvertGuidCharToInt(
|
|
const char *pString,
|
|
DWORD *n,
|
|
unsigned short number
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Internal route to be called only from ConvertStringToGuid.
|
|
Converts segements of the GUID to numbers.
|
|
|
|
Arguments:
|
|
|
|
pString - [IN] ptr to the string segment to process.
|
|
n - [OUT] ptr to number representation of string segment.
|
|
number - [IN] size of string segment in characters.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success.
|
|
--*/
|
|
unsigned short base = 16; //guid numbers are in hex
|
|
*n = 0;
|
|
|
|
while(number-- > 0) {
|
|
int t;
|
|
|
|
if (*pString >= '0' && *pString <= '9') {
|
|
t = *pString++ - '0';
|
|
}
|
|
else if (*pString >= 'A' && *pString <= 'F') {
|
|
t = (*pString++ - 'A') + 10;
|
|
}
|
|
else if (*pString >= 'a' && *pString <= 'f') {
|
|
t = (*pString++ - 'a') + 10;
|
|
}
|
|
else return FALSE;
|
|
|
|
*n = (*n * base) + t;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ConvertStringToGuid(
|
|
const char *pString,
|
|
GUID *pGuid
|
|
)
|
|
{
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a string in the form found in _declspec(uuid(GUID)) to a GUID.
|
|
Braces around guid are acceptable and are striped before processing.
|
|
|
|
Arguments:
|
|
|
|
pString - [IN] ptr to the string that represents the guid.
|
|
pGuid - [OUT] ptr to the new guid.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success.
|
|
--*/
|
|
|
|
DWORD t;
|
|
unsigned int c;
|
|
unsigned int guidlength = 36;
|
|
char tString[37]; //guidlength + 1
|
|
|
|
t = strlen(pString);
|
|
if (guidlength + 2 == t) {
|
|
//string is surounded with braces
|
|
//check for braces and chop
|
|
if (pString[0] != '{' || pString[guidlength + 1] != '}') return FALSE;
|
|
memcpy(tString, pString + 1, guidlength);
|
|
tString[guidlength] = '\0';
|
|
pString = tString;
|
|
}
|
|
|
|
else if (t != guidlength) return FALSE;
|
|
|
|
if (!ConvertGuidCharToInt(pString, &t, 8)) return FALSE;
|
|
pString += 8;
|
|
pGuid->Data1 = t;
|
|
if (*pString++ != '-') return FALSE;
|
|
|
|
if (!ConvertGuidCharToInt(pString, &t, 4)) return FALSE;
|
|
pString += 4;
|
|
pGuid->Data2 = (unsigned short)t;
|
|
if (*pString++ != '-') return FALSE;
|
|
|
|
if (!ConvertGuidCharToInt(pString, &t, 4)) return FALSE;
|
|
pString += 4;
|
|
pGuid->Data3 = (unsigned short)t;
|
|
if (*pString++ != '-') return FALSE;
|
|
|
|
for(c = 0; c < 8; c++) {
|
|
if (!ConvertGuidCharToInt(pString, &t, 2)) return FALSE;
|
|
pString += 2;
|
|
pGuid->Data4[c] = (unsigned char)t;
|
|
if (c == 1)
|
|
if (*pString++ != '-') return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
IsDefinedPointerDependent(
|
|
char *pName
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if a typename is inharenty pointer size dependent.
|
|
The user is expected to check pointers and derived types.
|
|
|
|
Arguments:
|
|
|
|
pName - [IN] Type to check.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Type is pointer size dependent.
|
|
--*/
|
|
if (NULL == pName) return FALSE;
|
|
if (strcmp(pName, "INT_PTR") == 0) return TRUE;
|
|
if (strcmp(pName, "UINT_PTR") == 0) return TRUE;
|
|
if (strcmp(pName, "HALF_PTR") == 0) return TRUE;
|
|
if (strcmp(pName, "UHALF_PTR") == 0) return TRUE;
|
|
if (strcmp(pName, "LONG_PTR") == 0) return TRUE;
|
|
if (strcmp(pName, "ULONG_PTR") == 0) return TRUE;
|
|
if (strcmp(pName, "__int64") == 0) return TRUE;
|
|
if (strcmp(pName, "_int64") == 0) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
PCHAR
|
|
IsDefinedPtrToPtrDependent(
|
|
IN char *pName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if a typename is inharenty a pointer to a pointer
|
|
dependent type. The user is expected to check pointers to pointers and derived types.
|
|
All of these types have an indirection level of 1.
|
|
|
|
Arguments:
|
|
|
|
pName - [IN] Type to check.
|
|
|
|
Return Value:
|
|
|
|
Pointer to the name of the indirection of this type.
|
|
--*/
|
|
{
|
|
if (*pName != 'P') return NULL;
|
|
if (strcmp(pName, "PINT_PTR") == 0) return "INT_PTR";
|
|
if (strcmp(pName, "PUINT_PTR") == 0) return "UINT_PTR";
|
|
if (strcmp(pName, "PHALF_PTR") == 0) return "HALF_PTR";
|
|
if (strcmp(pName, "PUHALF_PTR") == 0) return "UHALF_PTR";
|
|
if (strcmp(pName, "PLONG_PTR") == 0) return "LONG_PTR";
|
|
if (strcmp(pName, "PULONG_PTR") == 0) return "ULONG_PTR";
|
|
return NULL;
|
|
}
|
|
|
|
static HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
static HANDLE hMapFile = NULL;
|
|
static void *pvMappedBase = NULL;
|
|
|
|
BOOL
|
|
ClosePpmFile(
|
|
BOOL bExitFailure
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes the opened ppm file.
|
|
|
|
Arguments:
|
|
|
|
bExitFailure - [IN] Terminate program on error
|
|
|
|
Return Value:
|
|
|
|
Error - FALSE
|
|
Success - TRUE
|
|
|
|
--*/
|
|
|
|
if (NULL != pvMappedBase) {
|
|
if(!UnmapViewOfFile(pvMappedBase)) {
|
|
if (bExitFailure) {
|
|
ErrMsg("ClosePpmFile: Unable to unmap ppm file, error %u\n", GetLastError());
|
|
ExitErrMsg(FALSE, _strerror(NULL));
|
|
}
|
|
return FALSE;
|
|
}
|
|
pvMappedBase = NULL;
|
|
}
|
|
if (NULL != hMapFile) {
|
|
if(!CloseHandle(hMapFile)) {
|
|
if (bExitFailure) {
|
|
ErrMsg("ClosePpmFile: Unable to close ppm file, error %u\n", GetLastError());
|
|
ExitErrMsg(FALSE, _strerror(NULL));
|
|
}
|
|
return FALSE;
|
|
}
|
|
hMapFile = NULL;
|
|
}
|
|
if (INVALID_HANDLE_VALUE != hFile) {
|
|
if(!CloseHandle(hFile)) {
|
|
if (bExitFailure) {
|
|
ErrMsg("ClosePpmFile: Unable to close ppm file, error %u\n", GetLastError());
|
|
ExitErrMsg(FALSE, _strerror(NULL));
|
|
}
|
|
return FALSE;
|
|
}
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
PCVMHEAPHEADER
|
|
MapPpmFile(
|
|
char *sPpmfile,
|
|
BOOL bExitFailure
|
|
)
|
|
{
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens a Ppm file and maps it.
|
|
|
|
Arguments:
|
|
|
|
pName - [IN] Name of the file to map.
|
|
bExitFailure - [IN] Terminate program on error
|
|
|
|
Return Value:
|
|
|
|
Error - NULL
|
|
Success - Pointer to the VCVMHEAPHEADER
|
|
|
|
--*/
|
|
void *pvBaseAddress;
|
|
DWORD dwBytesRead;
|
|
BOOL fSuccess;
|
|
ULONG Version;
|
|
DWORD dwErrorNo;
|
|
|
|
PCVMHEAPHEADER pHeader;
|
|
|
|
hFile = CreateFile(sPpmfile,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
if (!bExitFailure) goto fail;
|
|
ErrMsg("MapPpmFile: Unable to open %s, error %u\n", sPpmfile, GetLastError());
|
|
ExitErrMsg(FALSE, _strerror(NULL));
|
|
}
|
|
|
|
fSuccess = ReadFile(hFile,
|
|
&Version,
|
|
sizeof(ULONG),
|
|
&dwBytesRead,
|
|
NULL
|
|
);
|
|
|
|
if (! fSuccess || dwBytesRead != sizeof(ULONG)) {
|
|
if (!bExitFailure) goto fail;
|
|
ErrMsg("MapPpmFile: Unable to read version for %s, error %u\n", sPpmfile, GetLastError());
|
|
ExitErrMsg(FALSE, _strerror(NULL));
|
|
}
|
|
|
|
if (Version != VM_TOOL_VERSION) {
|
|
//SetLastError(ERROR_BAD_DATABASE_VERSION);
|
|
if (!bExitFailure) goto fail;
|
|
ExitErrMsg(FALSE, "MapPpmFile: Ppm file file has version %x, expect %x\n", Version, VM_TOOL_VERSION);
|
|
}
|
|
|
|
#if _WIN64
|
|
// Read and skip the padding between the version and the base
|
|
fSuccess = ReadFile(hFile,
|
|
&Version,
|
|
sizeof(ULONG),
|
|
&dwBytesRead,
|
|
NULL
|
|
);
|
|
|
|
if (! fSuccess || dwBytesRead != sizeof(ULONG)) {
|
|
if (!bExitFailure) goto fail;
|
|
ErrMsg("MapPpmFile: Unable to read version for %s, error %u\n", sPpmfile, GetLastError());
|
|
ExitErrMsg(FALSE, _strerror(NULL));
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
fSuccess = ReadFile(hFile,
|
|
&pvBaseAddress,
|
|
sizeof(pvBaseAddress),
|
|
&dwBytesRead,
|
|
NULL
|
|
);
|
|
|
|
if (! fSuccess || dwBytesRead != sizeof(pvBaseAddress)) {
|
|
if (!bExitFailure) goto fail;
|
|
ExitErrMsg(FALSE, "MapPpmFile: Unable to read base address of ppm file %s, error %u\n", sPpmfile, GetLastError());
|
|
ExitErrMsg(FALSE, _strerror(NULL));
|
|
}
|
|
|
|
|
|
hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0,NULL);
|
|
if (!hMapFile) {
|
|
if (!bExitFailure) goto fail;
|
|
ExitErrMsg(FALSE, "MapPpmfile: Unable to map %s, error %u\n", sPpmfile, GetLastError());
|
|
ExitErrMsg(FALSE, _strerror(NULL));
|
|
}
|
|
|
|
pvMappedBase = MapViewOfFileEx(hMapFile, FILE_MAP_READ, 0, 0, 0, pvBaseAddress);
|
|
if (! pvMappedBase || pvMappedBase != pvBaseAddress) {
|
|
// If the file can't be mapped at the expected base, we must fail
|
|
// since the memory is chock full o' pointers.
|
|
if (!bExitFailure) goto fail;
|
|
ExitErrMsg(FALSE, "MapPpmFile: Unable to map view of %s, error %u\n", sPpmfile, GetLastError());
|
|
ExitErrMsg(FALSE, _strerror(NULL));
|
|
}
|
|
|
|
NIL = &((PCVMHEAPHEADER)pvMappedBase)->NIL;
|
|
return (PCVMHEAPHEADER)pvMappedBase;
|
|
|
|
fail:
|
|
|
|
dwErrorNo = GetLastError();
|
|
ClosePpmFile(FALSE);
|
|
SetLastError(dwErrorNo);
|
|
return NULL;
|
|
|
|
}
|
|
|
|
char szHOSTPTR32[] = "/* 64 bit ptr */ _int64";
|
|
char szHOSTPTR64[] = "/* 32 bit ptr */ _int32";
|
|
|
|
char *GetHostPointerName(BOOL bIsPtr64) {
|
|
if (bIsPtr64)
|
|
return szHOSTPTR32;
|
|
else
|
|
return szHOSTPTR64;
|
|
}
|
|
|
|
char szHOSTUSIZE8[] = "unsigned _int8";
|
|
char szHOSTUSIZE16[] = "unsigned _int16";
|
|
char szHOSTUSIZE32[] = "unsigned _int32";
|
|
char szHOSTUSIZE64[] = "unsigned _int64";
|
|
char szHOSTSIZE8[] = "_int8";
|
|
char szHOSTSIZE16[] = "_int16";
|
|
char szHOSTSIZE32[] = "_int32";
|
|
char szHOSTSIZE64[] = "_int64";
|
|
char szHOSTSIZEGUID[] = "struct _GUID";
|
|
|
|
char *GetHostBasicTypeName(PKNOWNTYPES pkt) {
|
|
|
|
DWORD dwSize;
|
|
|
|
if (pkt->Flags & BTI_ISARRAY)
|
|
dwSize = pkt->dwBaseSize;
|
|
else
|
|
dwSize = pkt->Size;
|
|
|
|
if (pkt->Flags & BTI_UNSIGNED) {
|
|
switch(pkt->Size) {
|
|
case 1:
|
|
return szHOSTUSIZE8;
|
|
case 2:
|
|
return szHOSTUSIZE16;
|
|
case 4:
|
|
return szHOSTUSIZE32;
|
|
case 8:
|
|
return szHOSTUSIZE64;
|
|
default:
|
|
ExitErrMsg(FALSE, "Unknown type size of %d for type %s.\n", pkt->Size, pkt->TypeName);
|
|
return 0;
|
|
}
|
|
}
|
|
else {
|
|
switch(pkt->Size) {
|
|
case 0:
|
|
return szVOID;
|
|
case 1:
|
|
return szHOSTSIZE8;
|
|
case 2:
|
|
return szHOSTSIZE16;
|
|
case 4:
|
|
return szHOSTSIZE32;
|
|
case 8:
|
|
return szHOSTSIZE64;
|
|
case 16:
|
|
return szHOSTSIZEGUID;
|
|
default:
|
|
ExitErrMsg(FALSE, "Unknown type size of %d for type %s.\n", pkt->Size, pkt->TypeName);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
char *GetHostTypeName(PKNOWNTYPES pkt, char *pBuffer) {
|
|
if (pkt->IndLevel > 0) {
|
|
strcpy(pBuffer, GetHostPointerName(pkt->Flags & BTI_PTR64));
|
|
}
|
|
else if(!(BTI_NOTDERIVED & pkt->Flags)) {
|
|
if (strcmp(pkt->BaseName, "enum") == 0) {
|
|
strcpy(pBuffer, szHOSTSIZE32);
|
|
}
|
|
else if (strcmp(pkt->BaseName, "union") == 0 ||
|
|
strcmp(pkt->BaseName, "struct") == 0) {
|
|
strcpy(pBuffer, pkt->BaseName);
|
|
strcat(pBuffer, " NT32");
|
|
strcat(pBuffer, pkt->TypeName);
|
|
}
|
|
else {
|
|
strcpy(pBuffer, "NT32");
|
|
strcat(pBuffer, pkt->TypeName);
|
|
}
|
|
}
|
|
else {
|
|
strcpy(pBuffer, GetHostBasicTypeName(pkt));
|
|
}
|
|
return pBuffer;
|
|
}
|