windows-nt/Source/XPSP1/NT/base/mvdm/wow32/genwowit.c

716 lines
17 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/* generate wowit.h and wowit.c from wow.it
*
* 20-Feb-1997 DaveHart created
*/
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <sys\stat.h>
#include <fcntl.h>
#include <windows.h>
VOID ErrorAbort(PSZ pszMsg);
BYTE GetReturnOpcode(PSZ *ppsz);
PSZ GetApiName(PSZ *ppsz);
PSZ GetApi32Name(PSZ *ppsz, PSZ pszApi16);
BYTE GetArgOpcode(PSZ *ppsz);
PSZ GetOpcodeName(BYTE bInstr);
PSZ GetLine(PSZ pszBuf, int cbBuf, FILE *fp);
VOID ReadTypeNames(FILE *fIn, PSZ szTypesPrefix, PSZ *OpcodeNamesArray, int *pnOpcodeNames);
PSZ DateTimeString(VOID);
#define IS_RET_OPCODE(b) (b & 0x80)
#define MAX_IT_INSTR 16
typedef struct tagITINSTR {
int cbInstr;
int offSwamp;
BYTE Instr[MAX_IT_INSTR];
} ITINSTR;
#define MAX_INSTR_TABLE_SIZE 512
ITINSTR InstrTable[MAX_INSTR_TABLE_SIZE];
int iNextInstrSlot = 0;
typedef struct tagTHUNKTABLESLOT {
PSZ pszAPI;
PSZ pszAPI32; // if Win32 routine name doesn't match pszAPI
int iInstrSlot;
int cbInstr; // how much of this slot we're using
} THUNKTABLESLOT;
#define MAX_THUNK_TABLE_SIZE 1024
THUNKTABLESLOT ThunkTable[MAX_THUNK_TABLE_SIZE];
int iNextThunkSlot = 0;
#define MAX_ARG_OPCODE_NAMES 32
PSZ ArgOpcodeNames[MAX_ARG_OPCODE_NAMES];
int nArgOpcodeNames;
#define MAX_RET_OPCODE_NAMES 32
PSZ RetOpcodeNames[MAX_RET_OPCODE_NAMES];
int nRetOpcodeNames;
static char szArgumentTypes[] = "Argument Types:";
static char szReturnTypes[] = "Return Types:";
int __cdecl main(int argc, char **argv)
{
FILE *fIn, *fOutH, *fOutC;
char szBuf[256], szOff1[32], szOff2[32];
PSZ psz, pszAPI, pszAPI32;
ITINSTR ThisInstr;
BYTE bRetInstr;
BYTE *pbInstr;
int i, iSwampOffset;
int iMaxArgs = 0;
int cbDiff;
if (argc != 2) {
ErrorAbort("Usage:\n genwowit <inputfile>\n");
}
if (!(fIn = fopen(argv[1], "rt"))) {
ErrorAbort("Unable to open input file\n");
}
//
// The input file (wow.it) uses # to begin comment lines.
// Aside from comments, it must begin with two special lines
// to define the available type names for arguments and
// function return values.
//
// They look like:
//
// Argument Types: WORD, INT, DWORD, LPDWORD, PTR, PTRORATOM, HGDI, HUSER, COLOR, HINST, HICON, POINT, 16ONLY, 32ONLY;
// Return Types: DWORD, WORD, INT, HGDI, HUSER, ZERO, HICON, ONE, HPRNDWP;
//
// Read these lines into the ArgOpcodeNames and RetOpcodeNames arrays.
//
ReadTypeNames(fIn, szArgumentTypes, ArgOpcodeNames, &nArgOpcodeNames);
ReadTypeNames(fIn, szReturnTypes, RetOpcodeNames, &nRetOpcodeNames);
//
// Each input line in the main part has a very restricted syntax:
//
// RETTYPE Api16[=Api32](TYPE1, TYPE2, ... TYPEn); # comment
//
// If Api32 isn't specified it's the same as Api16.
// The types come from the set above only.
//
// Actually everything following the ) is ignored now.
//
while (GetLine(szBuf, sizeof szBuf, fIn)) {
psz = szBuf;
//
// Pick up the return type, space-delimited
//
bRetInstr = GetReturnOpcode(&psz);
//
// Pick up the API name, leaving psz pointing past the open-paren
//
pszAPI = GetApiName(&psz);
//
// Pick up the 32-bit name if it exists
//
pszAPI32 = GetApi32Name(&psz, pszAPI);
//
// Pick up the arg types into Instr array
//
memset(&ThisInstr, 0, sizeof ThisInstr);
pbInstr = ThisInstr.Instr;
while (*psz && *psz != ')') {
*pbInstr++ = GetArgOpcode(&psz);
}
//
// Keep track of the max used args
//
iMaxArgs = max(iMaxArgs, (pbInstr - ThisInstr.Instr));
//
// Tack on the return opcode
//
*pbInstr++ = bRetInstr;
//
// Record instruction bytes used for this one.
//
ThisInstr.cbInstr = (pbInstr - ThisInstr.Instr);
//
// Make sure we haven't overrun
//
if ( ThisInstr.cbInstr > MAX_IT_INSTR ) {
printf("Thunk for %s too many args (%d) increase MAX_IT_INSTR beyond %d.\n",
pszAPI, ThisInstr.cbInstr, MAX_IT_INSTR);
ErrorAbort("Increase MAX_IT_INSTR in intthunk.h\n");
}
//
// Now we have a fully-formed opcode stream, see if we can pack it
// in with any previously recorded ones. Walk through the table
// from the start looking for any entry which already contains this
// opcode sequence (possibly as part of a longer sequence) or which
// is itself contained by this opcode sequence. If we find one,
// change it to be the longer sequence if needed and use it. We'll
// distinguish later between the multiple uses using the cbInstr in
// each thunk table entry. The logic here assumes the matches will
// always be at the end, since ret opcodes always have 0x80 bit set
// and no others do, and each sequence ends with one.
//
for (i = 0; i < iNextInstrSlot; i++) {
//if (0 == memcmp(Instr, InstrTable[i], sizeof Instr)) {
// break;
//}
//
// Is ThisInstr a subsequence of this table entry?
//
if (ThisInstr.cbInstr <= InstrTable[i].cbInstr &&
0 == memcmp(ThisInstr.Instr,
InstrTable[i].Instr + (InstrTable[i].cbInstr -
ThisInstr.cbInstr),
ThisInstr.cbInstr)) {
break;
}
//
// Is this table entry a subsequence of ThisInstr?
//
if (InstrTable[i].cbInstr < ThisInstr.cbInstr &&
0 == memcmp(InstrTable[i].Instr,
ThisInstr.Instr + (ThisInstr.cbInstr -
InstrTable[i].cbInstr),
InstrTable[i].cbInstr)) {
//
// Blast the longer ThisInstr over the existing shorter
// instruction.
//
memcpy(&InstrTable[i], &ThisInstr, sizeof InstrTable[i]);
break;
}
//
// Check the next instruction table entry.
//
}
//
// If we didn't find a match, add to the end.
//
if (i == iNextInstrSlot) {
memcpy(&InstrTable[i], &ThisInstr, sizeof InstrTable[i]);
iNextInstrSlot++;
if (iNextInstrSlot == MAX_INSTR_TABLE_SIZE) {
ErrorAbort("Increase MAX_INSTR_TABLE_SIZE in genwowit.c\n");
}
}
//
// Add this one to the thunk table.
//
ThunkTable[iNextThunkSlot].pszAPI = pszAPI;
ThunkTable[iNextThunkSlot].pszAPI32 = pszAPI32;
ThunkTable[iNextThunkSlot].iInstrSlot = i;
ThunkTable[iNextThunkSlot].cbInstr = ThisInstr.cbInstr;
iNextThunkSlot++;
if (iNextThunkSlot == MAX_THUNK_TABLE_SIZE) {
ErrorAbort("Increase MAX_THUNK_TABLE_SIZE in genwowit.c\n");
}
}
fclose(fIn);
//
// Now we're ready to output the results.
//
if (!(fOutH = fopen("wowit.h", "wt"))) {
ErrorAbort("Cannot open wowit.h output file\n");
}
fprintf(fOutH,
"//\n"
"// DO NOT EDIT.\n"
"//\n"
"// wowit.h generated by genwowit.exe from wow.it on\n"
"//\n"
"// %s\n"
"//\n\n", DateTimeString());
fprintf(fOutH, "#include \"intthunk.h\"\n\n");
fprintf(fOutH, "#define MAX_IT_ARGS %d\n\n", iMaxArgs);
//
// Spit out the two types of opcode manifests.
//
for (i = 0; i < nArgOpcodeNames; i++) {
fprintf(fOutH, "#define IT_%-20s ( (UCHAR) 0x%x )\n", ArgOpcodeNames[i], i);
}
fprintf(fOutH, "\n#define IT_RETMASK ( (UCHAR) 0x80 )\n");
for (i = 0; i < nRetOpcodeNames; i++) {
sprintf(szBuf, "%sRET", RetOpcodeNames[i]);
fprintf(fOutH, "#define IT_%-20s ( IT_RETMASK | (UCHAR) 0x%x )\n", szBuf, i);
}
fprintf(fOutH, "\n");
//
// ITID_ manifests map an API name to its slot
// in the thunk table. Each one looks like:
//
// #define ITID_ApiName 0
//
for (i = 0; i < iNextThunkSlot; i++) {
fprintf(fOutH, "#define ITID_%-40s %d\n", ThunkTable[i].pszAPI, i);
}
fprintf(fOutH, "\n#define ITID_MAX %d\n", i-1);
fclose(fOutH);
//
// wowit.c has two tables, the instruction table and
// the thunk table.
//
if (!(fOutC = fopen("wowit.c", "wt"))) {
ErrorAbort("Cannot open wowit.c output file\n");
}
fprintf(fOutC,
"//\n"
"// DO NOT EDIT.\n"
"//\n"
"// wowit.c generated by genwowit.exe from wow.it on\n"
"//\n"
"// %s\n"
"//\n\n", DateTimeString());
fprintf(fOutC, "#include \"precomp.h\"\n");
fprintf(fOutC, "#pragma hdrstop\n");
fprintf(fOutC, "#define WOWIT_C\n");
fprintf(fOutC, "#include \"wowit.h\"\n\n");
//
// Spit out the instruction table, packing bytes in the process
// and filling in the aoffInstrTable array with offsets for each
// entry in this program's InstrTable. Those offsets are used
// in writing the final thunk table.
//
iSwampOffset = 0;
fprintf(fOutC, "CONST BYTE InstrSwamp[] = {\n");
for (i = 0; i < iNextInstrSlot; i++) {
fprintf(fOutC, " /* %3d 0x%-3x */ ", i, iSwampOffset);
pbInstr = InstrTable[i].Instr;
InstrTable[i].offSwamp = iSwampOffset;
do {
fprintf(fOutC, "%s, ", GetOpcodeName(*pbInstr));
iSwampOffset++;
} while (!IS_RET_OPCODE(*pbInstr++));
fprintf(fOutC, "\n");
}
fprintf(fOutC, "};\n\n");
fprintf(fOutC, "CONST INT_THUNK_TABLEENTRY IntThunkTable[] = {\n");
for (i = 0; i < iNextThunkSlot; i++) {
//
// Concatenate the API name followed by a comma into
// szBuf, so the combination can be left-justified in the output.
//
sprintf(szBuf, "%s,", ThunkTable[i].pszAPI32);
//
// cbDiff is the offset into the instruction stream where
// this thunks instruction stream begins.
//
cbDiff = InstrTable[ ThunkTable[i].iInstrSlot ].cbInstr -
ThunkTable[i].cbInstr;
//
// Format the swamp offset so it can be left-justified in the output.
//
sprintf(szOff1, "%x",
InstrTable[ ThunkTable[i].iInstrSlot ].offSwamp + cbDiff);
//
// If this thunk table entry will point past the start of
// an instruction (because of sharing), format the offset
// past the start of the instruction into szOff2
//
if (cbDiff) {
sprintf(szOff2, "+ %d ", cbDiff);
} else {
szOff2[0] = '\0';
}
fprintf(fOutC,
" /* %3d */ { (FARPROC) %-32s InstrSwamp + 0x%-4s }, /* %d %s*/ \n",
i,
szBuf,
szOff1,
ThunkTable[i].iInstrSlot,
szOff2);
}
fprintf(fOutC, "};\n\n");
fclose(fOutC);
printf("Generated wowit.h and wowit.c from wow.it\n"
"%d thunks, %d unique instruction streams, %d instruction bytes, %d max args.\n",
iNextThunkSlot, iNextInstrSlot, iSwampOffset, iMaxArgs);
return 0;
}
BYTE GetReturnOpcode(PSZ *ppsz)
{
int i;
char szBuf[32];
PSZ psz;
//
// Copy the name up to the first space to szBuf,
// then skip any remaining spaces leaving caller's
// pointer pointing at API name.
//
psz = szBuf;
while (**ppsz != ' ') {
*psz++ = *((*ppsz)++);
};
*psz = 0;
while (**ppsz == ' ') {
(*ppsz)++;
};
i = 0;
while (i < nRetOpcodeNames &&
strcmp(szBuf, RetOpcodeNames[i])) {
i++;
}
if (i == nRetOpcodeNames) {
printf("%s is not a valid return type.\n", szBuf);
ErrorAbort("Invalid return type.\n");
}
return (BYTE)i | 0x80;
}
PSZ GetApiName(PSZ *ppsz)
{
char szBuf[128];
PSZ psz;
//
// Copy the name up to the first space or open-paren or equals sign
// to szBuf, then skip any remaining spaces and open-parens leaving caller's
// pointer pointing at first arg type or equals sign
//
psz = szBuf;
while (**ppsz != ' ' && **ppsz != '(' && **ppsz != '=') {
*psz++ = *((*ppsz)++);
};
*psz = 0;
while (**ppsz == ' ' || **ppsz == '(') {
(*ppsz)++;
};
if (!strlen(szBuf)) {
ErrorAbort("Empty API name\n");
}
return _strdup(szBuf);
}
PSZ GetApi32Name(PSZ *ppsz, PSZ pszApi16)
{
char szBuf[128];
PSZ psz;
if (**ppsz != '=') {
return pszApi16;
}
(*ppsz)++; // skip =
//
// Copy the name up to the first space or open-paren
// to szBuf, then skip any remaining spaces and open-parens leaving caller's
// pointer pointing at first arg type
//
psz = szBuf;
while (**ppsz != ' ' && **ppsz != '(') {
*psz++ = *((*ppsz)++);
};
*psz = 0;
while (**ppsz == ' ' || **ppsz == '(') {
(*ppsz)++;
};
if (!strlen(szBuf)) {
ErrorAbort("Empty API32 name\n");
}
return _strdup(szBuf);
}
BYTE GetArgOpcode(PSZ *ppsz)
{
char szBuf[32];
PSZ psz;
int i;
//
// Copy the name up to the first space or comma close-paren
// to szBuf, then skip any remaining spaces and commas,
// leaving caller's pointer pointing at next arg type
// or close-paren.
//
psz = szBuf;
while (**ppsz != ' ' && **ppsz != ',' && **ppsz != ')') {
*psz++ = *((*ppsz)++);
};
*psz = 0;
while (**ppsz == ' ' || **ppsz == ',') {
(*ppsz)++;
};
//
// szBuf has the type name, find it in the table.
//
i = 0;
while (i < nArgOpcodeNames &&
strcmp(szBuf, ArgOpcodeNames[i])) {
i++;
}
if (i == nArgOpcodeNames) {
printf("%s is not a valid arg type.\n", szBuf);
ErrorAbort("Invalid arg type.\n");
}
return (BYTE)i;
}
PSZ GetOpcodeName(BYTE bInstr)
{
char szBuf[64];
if (!IS_RET_OPCODE(bInstr)) {
sprintf(szBuf, "IT_%s", ArgOpcodeNames[bInstr]);
} else {
sprintf(szBuf, "IT_%sRET", RetOpcodeNames[bInstr & 0x7f]);
}
return _strdup(szBuf);
}
VOID ErrorAbort(PSZ pszMsg)
{
printf("GENWOWIT : fatal error GWI0001: Unable to process wow.it: %s\n", pszMsg);
exit(1);
}
//
// Read a line from the input file skipping
// comment lines with '#' in the first column.
//
PSZ GetLine(PSZ pszBuf, int cbBuf, FILE *fp)
{
do {
pszBuf = fgets(pszBuf, cbBuf, fp);
} while (pszBuf && '#' == *pszBuf);
return pszBuf;
}
//
// Read one of the two special lines at the start that
// define the available types.
//
VOID ReadTypeNames(FILE *fIn, PSZ pszTypesPrefix, PSZ *OpcodeNamesArray, int *pnOpcodeNames)
{
char chSave, szBuf[512];
PSZ psz, pszType;
if ( ! GetLine(szBuf, sizeof szBuf, fIn) ||
_memicmp(szBuf, pszTypesPrefix, strlen(pszTypesPrefix)) ) {
ErrorAbort("First line of input file must be 'Argument Types:', second 'Return Types:' ...\n");
}
psz = szBuf + strlen(pszTypesPrefix);
//
// Skip whitespace and commas
//
while (' ' == *psz || '\t' == *psz) {
psz++;
}
if ( ! *psz) {
ErrorAbort("No types found.\n");
}
do {
//
// Now we're looking at the first character of the type name.
//
pszType = psz;
//
// Find next whitespace, comma, semi, or null and turn it into a null.
// This turns this type name into a zero-terminated string.
//
while (*psz && ' ' != *psz && '\t' != *psz && ',' != *psz && ';' != *psz) {
psz++;
}
chSave = *psz;
*psz = 0;
OpcodeNamesArray[*pnOpcodeNames] = _strdup(pszType);
(*pnOpcodeNames)++;
*psz = chSave;
//
// Skip whitespace and commas
//
while (' ' == *psz || '\t' == *psz || ',' == *psz) {
psz++;
}
} while (*psz && ';' != *psz);
if ( ! *pnOpcodeNames) {
ErrorAbort("No types found.\n");
}
}
//
// Return a formatted date/time string for now.
// Only checks system time once so that wowit.c and wowit.h
// will have same date/time string.
//
PSZ DateTimeString(VOID)
{
static char sz[256];
static int fSetupAlready;
if (!fSetupAlready) {
time_t UnixTimeNow;
struct tm *ptmNow;
fSetupAlready = TRUE;
_tzset();
time(&UnixTimeNow);
ptmNow = localtime(&UnixTimeNow);
strftime(sz, sizeof sz, "%#c", ptmNow);
strcat(sz, " (");
strcat(sz, _strupr(_tzname[0])); // naughty me
strcat(sz, ")");
}
return sz;
}