windows-nt/Source/XPSP1/NT/base/tools/genxx/genxx.c
2020-09-26 16:20:57 +08:00

1159 lines
25 KiB
C

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
genxx.c
Abstract:
This module implements a program which generates structure offset
definitions for kernel structures that are accessed in assembly code.
Author:
Forrest C. Foltz (forrestf) 20-Jan-98
To use:
This program reads an OBJ file generated by the target platform's
compiler.
To generate such an OBJ, go to ke\up and do a "nmake UMAPPL=gen<plt>",
where <plt> is a platform identifier like i386, etc.
All you need from this latter step is the OBJ, the link phase will not
succeed which is fine.
Revision History:
--*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define SKIP_M4
#include <genxx.h>
//
// Internal structure definitions, macros, constants
//
#define ARRAY_SIZE( x ) (sizeof( x ) / sizeof( (x)[0] ))
typedef struct _OUTPUT_FILE *POUTPUT_FILE;
typedef struct _OUTPUT_FILE {
POUTPUT_FILE Next;
ULONG EnableMask;
BOOLEAN IncFormat;
FILE *File;
} OUTPUT_FILE;
//
// Function prototypes follow
//
VOID
ApplyFixupsToImage( VOID );
VOID
BuildHeaderFiles(
STRUC_ELEMENT UNALIGNED *StrucArray
);
VOID
__cdecl
CheckCondition(
int Condition,
const char *format,
...
);
VOID
AddNewOutputFile(
PUCHAR RootRelativePath,
ULONG Flags
);
VOID
AddNewAbsoluteOutputFile(
PUCHAR AbsolutePath,
ULONG Flags
);
VOID
CloseOutputFiles( VOID );
VOID
_cdecl
HeaderPrint(
ULONG EnableFlags,
ULONG Type,
...
);
PSTRUC_ELEMENT
FindStructureElementArray(
PCHAR Buffer,
ULONG BufferSize
);
VOID
GetEnvironment( VOID );
PSTRUC_ELEMENT
LoadObjImage(
PUCHAR ImagePath
);
PCHAR
StripWhiteSpace(
PCHAR String
);
VOID
Usage( VOID );
//
// Constant tables follow
//
const char *PreprocessedFormatStringArray[] = {
// SEF_EQUATE
"#define %s 0x%0x\n",
// SEF_EQUATE64
"#define %s 0x%016I64x\n",
// SEF_COMMENT
"\n"
"//\n"
"// %s\n"
"//\n"
"\n",
// SEF_STRING
"%s\n",
// SEF_BITFLD
"#define %s_MASK 0x%I64x\n"
"#define %s 0x%0x\n",
// SEF_BITALIAS
"#define %s 0x%0x\n",
// SEF_STRUCTURE
"struct %s {\n"
" UCHAR fill[ %d ];\n"
"}; // %s\n"
};
const char *Asm386FormatStringArray[] = {
// SEF_EQUATE
"%s equ 0%04XH\n",
// SEF_EQUATE64
"%s equ 0%016I64XH\n",
// SEF_COMMENT
"\n"
";\n"
"; %s\n"
";\n"
"\n",
// SEF_STRING
"%s",
// SEF_BITFLD
"%s_MASK equ 0%I64XH\n"
"%s equ 0%0XH\n",
// SEF_BITALIAS
"%s equ 0%08XH\n",
// SEF_STRUCTURE
"%s struc\n"
" db %d dup(0)\n"
"%s ends\n"
};
//
// Each platform contains a list of generated header files.
//
typedef struct {
PCHAR HeaderPath;
ULONG Flags;
} HEADERPATH, *PHEADERPATH;
HEADERPATH HeaderListi386[] = {
{ "public\\sdk\\inc\\ks386.inc", SEF_KERNEL | SEF_INC_FORMAT },
{ "base\\ntos\\inc\\hal386.inc", SEF_HAL | SEF_INC_FORMAT },
{ NULL, 0 }
};
HEADERPATH HeaderListIa64[] = {
{ "public\\sdk\\inc\\ksia64.h", SEF_KERNEL | SEF_H_FORMAT },
{ "base\\ntos\\inc\\halia64.h", SEF_HAL | SEF_H_FORMAT },
{ NULL, 0 }
};
HEADERPATH HeaderListVdm[] = {
{ "public\\internal\\base\\inc\\vdmtib.inc", SEF_INC_FORMAT },
{ NULL, 0 }
};
HEADERPATH HeaderListAmd64[] = {
{ "public\\sdk\\inc\\ksamd64.inc", SEF_KERNEL | SEF_INC_FORMAT },
{ "base\\ntos\\inc\\halamd64.inc", SEF_HAL | SEF_INC_FORMAT },
{ NULL, 0 }
};
typedef struct {
PCHAR PlatformName;
PCHAR ObjPath;
PHEADERPATH HeaderPathList;
} PLATFORM, *PPLATFORM;
PLATFORM PlatformList[] = {
{ "i386",
"base\\ntos\\ke\\up\\obj\\i386\\geni386.obj",
HeaderListi386 },
{ "ia64",
"base\\ntos\\ke\\up\\obj\\ia64\\genia64.obj",
HeaderListIa64 },
{ "vdm",
"base\\ntos\\vdm\\up\\obj\\i386\\genvdm.obj",
HeaderListVdm },
{ "amd64",
"base\\ntos\\ke\\up\\obj\\amd64\\genamd64.obj",
HeaderListAmd64 },
{ NULL, NULL, NULL }
};
const char MarkerString[] = MARKER_STRING;
//
// Global vars follow
//
POUTPUT_FILE OutputFileList;
PCHAR ObjImage;
CHAR HalHeaderPath[ MAX_PATH ];
CHAR KernelHeaderPath[ MAX_PATH ];
CHAR HeaderPath[ MAX_PATH ];
CHAR ObjectPath[ MAX_PATH ];
CHAR NtRoot[ MAX_PATH ];
CHAR TempBuf[ MAX_PATH ];
BOOL fOutputSpecified;
BOOL fHalHeaderPath;
BOOL fKernelHeaderPath;
BOOL fIncFormat;
//
// The actual code...
//
int
__cdecl
main(
int argc,
char *argv[]
)
{
int argNum;
char *arg;
int platformIndex;
int fileIndex;
BOOL validSwitch;
PSTRUC_ELEMENT strucArray;
PPLATFORM platform;
PHEADERPATH headerPath;
GetEnvironment();
//
// Assume no platform specified, then see if we can find one.
//
ObjectPath[ 0 ] = '\0';
for( argNum = 1; argNum < argc; argNum++ ){
validSwitch = FALSE;
arg = argv[ argNum ];
if( *arg == '/' || *arg == '-' ){
//
// A switch was passed. See what it is.
//
arg++;
switch( *arg ){
case 'o':
//
// Specified an output file
//
fOutputSpecified = TRUE;
strcpy( HeaderPath, arg+1 );
validSwitch = TRUE;
break;
case 's':
//
// Specified include file suffix (either 'h' or 'inc')
//
if( _stricmp( arg+1, "inc" ) == 0 ){
//
// We would like the "inc" format, thanks
//
fIncFormat = TRUE;
} else {
CheckCondition( _stricmp( arg+1, "h" ) == 0,
"Invalid suffix option: -s[inc|h]\n");
}
validSwitch = TRUE;
break;
case 'k':
//
// Kernel header path. Save off.
//
fKernelHeaderPath = TRUE;
strcpy( KernelHeaderPath, arg+1 );
validSwitch = TRUE;
break;
case 'h':
//
// Hal header path. Save off.
//
fHalHeaderPath = TRUE;
strcpy( HalHeaderPath, arg+1 );
validSwitch = TRUE;
break;
default:
//
// Check our platform list.
//
platform = PlatformList;
while( platform->PlatformName != NULL ){
if( _stricmp( platform->PlatformName,
arg ) == 0 ){
//
// Platform was specified, we will build the path to
// the obj.
//
sprintf( ObjectPath,
"%s\\%s",
NtRoot,
platform->ObjPath );
//
// Add the header paths too.
//
headerPath = platform->HeaderPathList;
while( headerPath->HeaderPath != NULL ){
if (fHalHeaderPath && (headerPath->Flags & SEF_HAL)) {
strcpy(TempBuf, HalHeaderPath);
AddNewAbsoluteOutputFile( HalHeaderPath, headerPath->Flags );
} else
if (fKernelHeaderPath && (headerPath->Flags & SEF_KERNEL)) {
strcpy(TempBuf, KernelHeaderPath);
AddNewAbsoluteOutputFile( KernelHeaderPath, headerPath->Flags );
} else {
AddNewOutputFile( headerPath->HeaderPath,
headerPath->Flags );
}
headerPath++;
}
validSwitch = TRUE;
break;
}
platform++;
}
break;
}
if( validSwitch == FALSE ){
Usage();
}
} else {
//
// We are dealing with something that is not a switch. The only
// possibility is the path to the object file.
//
strcpy( ObjectPath, arg );
}
}
CheckCondition( ObjectPath[0] != '\0',
"Object path not specified\n" );
if( fOutputSpecified != FALSE ){
//
// The output path was specified
//
AddNewAbsoluteOutputFile( HeaderPath,
fIncFormat ? SEF_INC_FORMAT : SEF_H_FORMAT );
}
strucArray = LoadObjImage( ObjectPath );
BuildHeaderFiles( strucArray );
CloseOutputFiles();
//
// Indicate success.
//
return 0;
}
VOID
AddNewAbsoluteOutputFile(
PUCHAR AbsolutePath,
ULONG Flags
)
{
POUTPUT_FILE outputFile;
outputFile = malloc( sizeof( OUTPUT_FILE ));
CheckCondition( outputFile != NULL, "Out of memory\n" );
outputFile->EnableMask = (ULONG)(Flags & SEF_ENABLE_MASK);
if( (Flags & SEF_INC_FORMAT_MASK) == SEF_INC_FORMAT ){
//
// This file will be created in '.inc' format for the 386 assembler.
//
outputFile->IncFormat = TRUE;
} else {
//
// This file will be created in '.h' format for the standard C
// preprocessor.
//
outputFile->IncFormat = FALSE;
}
outputFile->File = fopen( AbsolutePath, "w" );
CheckCondition( outputFile->File != NULL,
"Cannot open %s for writing.\n",
TempBuf );
printf("%s -> %s\n", ObjectPath, TempBuf );
//
// Link this structure into the list of output files
//
outputFile->Next = OutputFileList;
OutputFileList = outputFile;
}
VOID
AddNewOutputFile(
PUCHAR RootRelativePath,
ULONG Flags
)
{
//
// Create the canonoical file path and open the file.
//
sprintf( TempBuf,
"%s\\%s",
NtRoot,
RootRelativePath );
AddNewAbsoluteOutputFile( TempBuf, Flags );
}
VOID
CloseOutputFiles( VOID )
{
POUTPUT_FILE outputFile;
outputFile = OutputFileList;
while( outputFile != NULL ){
fclose( outputFile->File );
outputFile = outputFile->Next;
}
}
PSTRUC_ELEMENT
LoadObjImage(
PUCHAR ImagePath
)
{
long objImageSize;
int result;
PSTRUC_ELEMENT strucArray;
FILE * objFile;
//
// Open up and read the platform-specific .obj file.
//
objFile = fopen( ImagePath, "rb" );
CheckCondition( objFile != NULL,
"Cannot open %s for reading.\n"
"This file must have been created by the compiler for the "
"target platform.\n",
ImagePath );
//
// Get the file size, allocate a buffer, read it in, and close.
//
result = fseek( objFile, 0, SEEK_END );
CheckCondition( result == 0,
"fseek() failed, error %d\n",
errno );
objImageSize = ftell( objFile );
CheckCondition( objImageSize != -1L,
"ftell() failed, error %d\n",
errno );
CheckCondition( objImageSize > 0,
"%s appears to be corrupt\n",
ImagePath );
ObjImage = malloc( objImageSize );
CheckCondition( ObjImage != NULL,
"Out of memory\n" );
result = fseek( objFile, 0, SEEK_SET );
CheckCondition( result == 0,
"fseek() failed, error %d\n",
errno );
result = fread( ObjImage, 1, objImageSize, objFile );
CheckCondition( result == objImageSize,
"Error reading from %s\n",
ImagePath );
fclose( objFile );
//
// Even though this is just an .obj file, we want it "fixed up"
//
ApplyFixupsToImage();
//
// Got the image, find the beginning of the array.
//
strucArray = FindStructureElementArray( ObjImage,
objImageSize );
CheckCondition( strucArray != NULL,
"%s does not contain a structure description array.\n",
ImagePath );
return strucArray;
}
VOID
BuildHeaderFiles(
STRUC_ELEMENT UNALIGNED *StrucArray
)
{
STRUC_ELEMENT UNALIGNED *strucArray;
ULONG runningEnableMask;
ULONG enableMask;
ULONG sefType;
const char *formatString;
ULONG bitFieldStart;
PUINT64 bitFieldPtr;
UINT64 bitFieldData;
PCHAR name;
BOOLEAN finished;
//
// Process each element in the array. The first element is the
// marker string element, so it is skipped.
//
runningEnableMask = 0;
finished = FALSE;
strucArray = StrucArray;
do{
strucArray++;
sefType = (ULONG)(strucArray->Flags & SEF_TYPE_MASK);
if( sefType == SEF_BITFLD ){
//
// For bitfields, the enable mask is set explicitly
//
enableMask = (ULONG)(strucArray->Flags & SEF_ENABLE_MASK);
} else {
//
// For everything else, we use the current runningEnableMask
//
enableMask = runningEnableMask;
}
switch( sefType ){
case SEF_BITFLD:
//
// This kind of element is tricky. "Equate" is actually a
// pointer to a bitfield structure. This structure has had
// a portion of it (the bitfield) initialized to ones.
//
// It is the job of this case to poke around in that
// structure in order to determine where the bitfield landed.
//
bitFieldPtr = (PINT64)(strucArray->Equate);
bitFieldData = *bitFieldPtr;
//
// Determine the zero-based starting bitnumber of the field.
//
bitFieldStart = 0;
while( (bitFieldData & ((UINT64)1 << bitFieldStart)) == 0 ){
bitFieldStart++;
}
name = StripWhiteSpace( strucArray->Name );
if( *name != '\0'){
HeaderPrint( enableMask,
sefType,
name,
bitFieldData,
name,
bitFieldStart );
}
//
// A bitfield can be followed by any number of
// SEF_BITALIAS entries. These are alias names for the
// bitmask that was just defined.
//
while( TRUE ){
sefType = (ULONG)((strucArray+1)->Flags & SEF_TYPE_MASK);
if( sefType != SEF_BITALIAS ){
//
// No more aliases.
//
break;
}
//
// This is a bitmask alias field, process it.
//
strucArray++;
name = StripWhiteSpace( strucArray->Name );
HeaderPrint( enableMask,
sefType,
name,
bitFieldData );
}
break;
case SEF_END:
finished = TRUE;
break;
case SEF_EQUATE:
if( (LONG64)strucArray->Equate < 0 ){
//
// Negative constant
//
if( (LONG64)strucArray->Equate < LONG_MIN ){
//
// More negative than can be represented in 32 bits
//
sefType = SEF_EQUATE64;
} else {
//
// Falls within [LONG_MIN..0], Leave as SEF_EQUATE32
//
}
} else if( (ULONG64)strucArray->Equate > (ULONG_MAX) ){
//
// More positive than can be represented in 32 bits
//
sefType = SEF_EQUATE64;
}
//
// Fall through
//
case SEF_EQUATE64:
case SEF_COMMENT:
HeaderPrint( enableMask,
sefType,
strucArray->Name,
(UINT64)strucArray->Equate );
break;
case SEF_STRING:
HeaderPrint( enableMask,
sefType,
strucArray->Name,
strucArray->Equate );
break;
case SEF_STRUCTURE:
HeaderPrint( enableMask,
sefType,
strucArray->Name,
(ULONG)strucArray->Equate,
strucArray->Name );
break;
case SEF_SETMASK:
runningEnableMask |= strucArray->Equate;
break;
case SEF_CLRMASK:
runningEnableMask &= ~strucArray->Equate;
break;
case SEF_PATH:
//
// Add another output file to our list.
//
CheckCondition( fOutputSpecified == FALSE,
"setPath() in %s incompatible with -o flag\n",
ObjectPath );
AddNewOutputFile( strucArray->Name,
(ULONG)strucArray->Flags );
break;
default:
//
// Found an SEF_TYPE we don't know about. This is fatal.
//
CheckCondition( FALSE,
"Unknown structure type %d. "
"Need an updated genxx.exe?\n",
sefType );
break;
}
} while( finished == FALSE );
}
VOID
__cdecl
CheckCondition(
int Condition,
const char *FormatString,
...
)
{
va_list(arglist);
va_start(arglist, FormatString);
if( Condition == 0 ){
//
// A fatal error was encountered. Bail.
//
vprintf( FormatString, arglist );
perror( "genxx" );
exit(1);
}
}
VOID
_cdecl
HeaderPrint(
ULONG EnableFlags,
ULONG Type,
...
)
{
POUTPUT_FILE outputFile;
char const *formatString;
va_list arglist;
//
// Send the output to each output file as appropriate
//
outputFile = OutputFileList;
while( outputFile != NULL ){
va_start( arglist, Type );
if( outputFile->EnableMask == 0 ||
(outputFile->EnableMask & EnableFlags) != 0 ){
//
// Either this output file gets everything, or the mask
// matches. Figure out which format to use... '.h' or '.inc'
// style.
//
if( Type == SEF_STRING ){
//
// For SEF_STRING, strucArray->Name *is* the format string.
//
formatString = va_arg( arglist, PUCHAR );
} else if( outputFile->IncFormat != FALSE ){
//
// Use the ".inc" format
//
formatString = Asm386FormatStringArray[ Type ];
} else {
//
// Use the ".h" format
//
formatString = PreprocessedFormatStringArray[ Type ];
}
//
// Now send it
//
vfprintf( outputFile->File, formatString, arglist );
}
va_end( arglist );
//
// Process all current output files.
//
outputFile = outputFile->Next;
}
}
VOID
GetEnvironment( VOID )
{
char *ntDrive;
char *ntRoot;
//
// Set NtRoot = %_NTDRIVE%\%_NTROOT%
//
ntDrive = getenv( "_NTDRIVE" );
ntRoot = getenv( "_NTROOT" );
if( ntDrive != NULL && ntRoot != NULL ){
sprintf( NtRoot, "%s%s", ntDrive, ntRoot );
} else {
//
// If either _NTDRIVE or _NTROOT were not found in the environment,
// let's try with \nt.
//
strcpy( NtRoot, "\\nt" );
}
}
PSTRUC_ELEMENT
FindStructureElementArray(
PCHAR Buffer,
ULONG BufferSize
)
{
PCHAR searchPoint;
PCHAR searchEndPoint;
PSTRUC_ELEMENT strucElement;
//
// Search Buffer for the beginning of a structure element array.
// The first element in this array contains MARKER_STRING.
//
searchPoint = Buffer;
searchEndPoint = Buffer + BufferSize - sizeof( MarkerString );
do{
//
// We scan the buffer a character at a time until we find a character
// that matches the first character in MarkerString.
//
if( *searchPoint != MarkerString[ 0 ] ){
continue;
}
//
// When a matching char is found, the rest of the string is compared.
//
if( strcmp( searchPoint, MarkerString ) == 0 ){
//
// It matched too, we're done.
//
strucElement = CONTAINING_RECORD( searchPoint,
STRUC_ELEMENT,
Name );
return strucElement;
}
} while( searchPoint++ < searchEndPoint );
//
// Fell out of the loop, we couldn't find the string.
//
return NULL;
}
VOID
Usage( VOID ){
int platformIndex;
PPLATFORM platform;
printf("genxx: [");
platform = PlatformList;
while( platform->PlatformName != NULL ){
printf("-%s|", platform->PlatformName );
platform++;
}
printf("<objpath>] [-s<h|inc>] [-o<outputpath>]\n");
exit(1);
}
VOID
ApplyFixupsToImage( VOID )
{
//
// Applies fixups to the OBJ image loaded at ObjImage
//
PIMAGE_FILE_HEADER fileHeader;
PIMAGE_SECTION_HEADER sectionHeader;
PIMAGE_SECTION_HEADER sectionHeaderArray;
PIMAGE_SYMBOL symbolTable;
PIMAGE_SYMBOL symbol;
PIMAGE_RELOCATION reloc;
PIMAGE_RELOCATION relocArray;
ULONG sectionNum;
ULONG relocNum;
ULONG_PTR targetVa;
PULONG_PTR fixupVa;
fileHeader = (PIMAGE_FILE_HEADER)ObjImage;
//
// We need the symbol table to apply the fixups
//
symbolTable = (PIMAGE_SYMBOL)(ObjImage + fileHeader->PointerToSymbolTable);
//
// Get a pointer to the first element in the section header
//
sectionHeaderArray = (PIMAGE_SECTION_HEADER)(ObjImage +
sizeof( IMAGE_FILE_HEADER ) +
fileHeader->SizeOfOptionalHeader);
//
// Apply the fixups for each section
//
for( sectionNum = 0;
sectionNum < fileHeader->NumberOfSections;
sectionNum++ ){
sectionHeader = &sectionHeaderArray[ sectionNum ];
if (memcmp(sectionHeader->Name, ".data", sizeof(".data")+1)) {
// Not .data - don't bother with the fixup
continue;
}
//
// Apply each fixup in this section
//
relocArray = (PIMAGE_RELOCATION)(ObjImage +
sectionHeader->PointerToRelocations);
for( relocNum = 0;
relocNum < sectionHeader->NumberOfRelocations;
relocNum++ ){
reloc = &relocArray[ relocNum ];
//
// The relocation gives us the position in the image of the
// relocation modification (VirtualAddress). To find out what
// to put there, we have to look the symbol up in the symbol index.
//
symbol = &symbolTable[ reloc->SymbolTableIndex ];
targetVa =
sectionHeaderArray[ symbol->SectionNumber-1 ].PointerToRawData;
targetVa += symbol->Value;
targetVa += (ULONG_PTR)ObjImage;
fixupVa = (PULONG_PTR)(ObjImage +
reloc->VirtualAddress +
sectionHeader->PointerToRawData );
*fixupVa = targetVa;
}
}
}
BOOLEAN
IsWhiteSpace(
CHAR Char
)
{
if( Char == '\t' ||
Char == ' ' ||
Char == '\r' ||
Char == '\n' ){
return TRUE;
} else {
return FALSE;
}
}
PCHAR
StripWhiteSpace(
PCHAR String
)
{
PCHAR chr;
ULONG strLen;
strLen = strlen( String );
if( strLen == 0 ){
return String;
}
//
// Strip off trailing whitespace
//
chr = String + strLen - 1;
while( IsWhiteSpace( *chr )){
*chr = '\0';
chr--;
}
//
// Advance past leading whitespace
//
chr = String;
while( IsWhiteSpace( *chr )){
chr++;
}
return chr;
}