|
|
/*++
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; strncpy( HeaderPath, arg+1, sizeof(HeaderPath) - 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; strncpy( KernelHeaderPath, arg+1, sizeof(KernelHeaderPath) - 1 ); validSwitch = TRUE; break;
case 'h':
//
// Hal header path. Save off.
//
fHalHeaderPath = TRUE; strncpy( HalHeaderPath, arg+1, sizeof(HalHeaderPath) - 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.
//
_snprintf( ObjectPath, sizeof(ObjectPath) - 1, "%s\\%s", NtRoot, platform->ObjPath );
//
// Add the header paths too.
//
headerPath = platform->HeaderPathList; while( headerPath->HeaderPath != NULL ){
if (fHalHeaderPath && (headerPath->Flags & SEF_HAL)) { strncpy(TempBuf, HalHeaderPath, sizeof(TempBuf) - 1); AddNewAbsoluteOutputFile( HalHeaderPath, headerPath->Flags ); } else if (fKernelHeaderPath && (headerPath->Flags & SEF_KERNEL)) { strncpy(TempBuf, KernelHeaderPath, sizeof(TempBuf) - 1); 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.
//
strncpy( ObjectPath, arg, sizeof(ObjectPath) - 1 ); } }
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.
//
_snprintf( TempBuf, sizeof(TempBuf) - 1, "%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 ){
_snprintf( NtRoot, sizeof(NtRoot) - 1, "%s%s", ntDrive, ntRoot );
} else {
//
// If either _NTDRIVE or _NTROOT were not found in the environment,
// let's try with \nt.
//
strncpy( NtRoot, "\\nt", sizeof(NtRoot) - 1 ); } }
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 = §ionHeaderArray[ 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; }
|