/*++ 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", where 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 #include #include #define SKIP_M4 #include // // 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("] [-s] [-o]\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; }