/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    bldrthnk.c

Abstract:

    This module implements a program which generates code to thunk from
    32-bit to 64-bit structures.

    This code is generated as an aid to the AMD64 boot loader, which
    must generate 64-bit structures from 32-bit structures.

Author:

    Forrest C. Foltz (forrestf) 15-May-2000


To use:

Revision History:

--*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "bldrthnk.h"

//
// Internal type definitions follow
// 

typedef struct _OBJ {
    PCHAR Image;
    LONG ImageSize;
    PDEFINITIONS Definitions;
} OBJ, *POBJ;

typedef struct _TYPE_REC  *PTYPE_REC;
typedef struct _FIELD_REC *PFIELD_REC;

typedef struct _TYPE_REC {
    PTYPE_REC Next;
    PCHAR Name;
    ULONG Size32;
    ULONG Size64;
    PFIELD_REC FieldList;
    BOOL SignExtend;
    BOOL Fabricated;
    BOOL Only64;
} TYPE_REC;

typedef struct _FIELD_REC {
    PFIELD_REC Next;
    PCHAR Name;
    PCHAR TypeName;
    PCHAR SizeFormula;
    ULONG TypeSize32;
    ULONG TypeSize64;
    ULONG Offset32;
    ULONG Offset64;
    ULONG Size32;
    ULONG Size64;
    PTYPE_REC TypeRec;
} FIELD_REC;

//
// Static TYPE_REC describing a 64-bit pointer type
//

TYPE_REC pointer64_typerec = {
    NULL,
    "POINTER64",
    4,
    8,
    NULL,
    TRUE,
    TRUE };

//
// Inline routine to generate a 32-bit pointer value from a ULONGLONG
// value found in an .obj file.
//

__inline
PVOID
CalcPtr(
    IN ULONGLONG Address
    )
{
    return (PVOID)((ULONG_PTR)Address);
}

//
// Forward declarations follow
// 

VOID
ApplyFixupsToImage(
    IN PCHAR ObjImage
    );

VOID
__cdecl
CheckCondition(
    int Condition,
    const char *FormatString,
    ...
    );

VOID
FabricateMissingTypes(
    VOID
    );

PTYPE_REC
FindTypeRec(
    IN PCHAR Name
    );

PVOID
GetMem(
    IN ULONG Size
    );

VOID
NewGlobalType(
    IN PTYPE_REC TypeRec
    );

BOOL
ProcessStructure(
    IN ULONG StrucIndex
    );

void
ReadObj(
    IN PCHAR Path,
    OUT POBJ Obj
    );

int
Usage(
    void
    );

VOID
WriteCopyList(
    IN PTYPE_REC TypeRec
    );

VOID
WriteCopyListWorker(
    IN PTYPE_REC TypeRec,
    IN ULONG Offset32,
    IN ULONG Offset64,
    IN PCHAR ParentName
    );

VOID
WriteCopyRoutine(
    IN PTYPE_REC TypeRec
    );

VOID
WriteThunkSource(
    VOID
    );

//
// Global data follows.  
// 

OBJ Obj32;
OBJ Obj64;
PTYPE_REC TypeRecList = NULL;

int
__cdecl
main(
    IN int argc,
    IN char *argv[]
    )

/*++

Routine Description:

    This is the main entrypoint of bldrthnk.exe.  Two command-line arguments
    are expected: first the path to the 32-bit .obj module, the second to
    the path to the 64-bit .obj module.  The .objs are expected to be the
    result of compiling m4-generated structure definition code.

Arguments:

Return value:

    0 for success, non-zero otherwise.

--*/

{
    ULONG strucIndex;

    //
    // argv[1] is the name of the 32-bit obj, argv[2] is the name of the
    // 64-bit obj
    // 

    if (argc != 3) {
        return Usage();
    }

    //
    // Usage:
    //
    // bldrthnk <32-bit.obj> <64-bit.obj>
    //

    ReadObj( argv[1], &Obj32 );
    ReadObj( argv[2], &Obj64 );

    //
    // Process each STRUC_DEF structure
    //

    strucIndex = 0;
    while (ProcessStructure( strucIndex )) {
        strucIndex += 1;
    }

    FabricateMissingTypes();

    //
    // Write out the file
    //

    WriteThunkSource();

    return 0;
}

int
Usage(
    VOID
    )

/*++

Routine Description:

    Displays program usage.

Arguments:

Return value:

--*/

{
    fprintf(stderr, "Usage: bldrthnk.exe <32-bit obj> <64-bit obj>\n");
    return -1;
}

void
ReadObj(
    IN PCHAR Path,
    OUT POBJ Obj
    )

/*++

Routine Description:

    Allocates an appropriate buffer, and reads into it the supplied object
    image in its entirety.

Arguments:

    Path - Supplies the path of the object image to process.

    Obj - Supplies a pointer to an OBJ structure which upon return will
        updated with the appropriate data.

Return value:

    None.

--*/

{
    FILE *objFile;
    int result;
    long objImageSize;
    PCHAR objImage;
    PULONG sigPtr;
    PULONG srchEnd;
    PDEFINITIONS definitions;
    LONGLONG imageBias;

    //
    // Open the file
    //

    objFile = fopen( Path, "rb" );
    CheckCondition( objFile != NULL,
                    "Cannot open %s for reading.\n",
                    Path );

    //
    // 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",
                    Path );

    objImage = GetMem( objImageSize );

    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",
                    Path );

    fclose( objFile );

    //
    // Find the start of the "definitions" array by looking for
    // SIG_1 followed by SIG_2
    //

    srchEnd = (PULONG)(objImage + objImageSize - 2 * sizeof(SIG_1));
    sigPtr = (PULONG)objImage;
    definitions = NULL;

    while (sigPtr < srchEnd) {

        if (sigPtr[0] == SIG_1 && sigPtr[1] == SIG_2) {
            definitions = (PDEFINITIONS)sigPtr;
            break;
        }

        sigPtr = (PULONG)((PCHAR)sigPtr + 1);
    }
    CheckCondition( definitions != NULL,
                    "Error: could not find signature in %s\n",
                    Path );

    //
    // Perform fixups on the image
    //

    ApplyFixupsToImage( objImage );

    //
    // Fill in the output structure and return
    // 

    Obj->Image = objImage;
    Obj->ImageSize = objImageSize;
    Obj->Definitions = definitions;
}

VOID
__cdecl
CheckCondition(
    int Condition,
    const char *FormatString,
    ...
    )

/*++

Routine Description:

    Asserts that Condition is non-zero.  If Condition is zero, FormatString
    is processed and displayed, and the program is terminated.

Arguments:

    Condition - Supplies the boolean value to evaluate.

    FormatString, ... - Supplies the format string and optional parameters
        to display in the event of a zero Condition.

Return value:

    None.

--*/

{
    va_list(arglist);

    va_start(arglist, FormatString);

    if( Condition == 0 ){

        //
        // A fatal error was encountered.  Bail.
        //

        vprintf( FormatString, arglist );
        perror( "genxx" );
        exit(-1);
    }
}

BOOL
ProcessStructure(
    IN ULONG StrucIndex
    )

/*++

Routine Description:

    Processes a single pair of structure definitions, 32-bit and 64-bit,
    respectively.

    Processing includes generating a TYPE_REC and associated FIELD_RECs for
    the definition pair.

Arguments:

    StrucIndex - Supplies the index into the array of STRUC_DEF structures
        found within each of the object images.

Return value:

    TRUE if the processing was successful, FALSE otherwise (e.g. a terminating
    record was located).

--*/

{
    PSTRUC_DEF Struc32, Struc64;
    PFIELD_DEF Field32, Field64;

    ULONG strLen;
    ULONG strLen2;
    ULONG strLen3;
    PTYPE_REC typeRec;
    PFIELD_REC fieldRec;
    PFIELD_REC insertNode;
    BOOL only64;

    ULONG index;

    Struc32 = CalcPtr( Obj32.Definitions->Structures[ StrucIndex ] );
    Struc64 = CalcPtr( Obj64.Definitions->Structures[ StrucIndex ] );

    if (Struc64 == NULL) {
        return FALSE;
    }

    if (Struc32 == NULL) {
        only64 = TRUE;
    } else {
        only64 = FALSE;
    }

    CheckCondition( Struc64 != NULL &&
                    ((only64 != FALSE) ||
                     strcmp( Struc32->Name, Struc64->Name ) == 0),
                    "Mismatched structure definitions found.\n" );

    //
    // Allocate and build a TYPE_REC for this STRUC_DEF
    //

    strLen = strlen( Struc64->Name ) + sizeof(char);
    typeRec = GetMem( sizeof(TYPE_REC) + strLen );
    typeRec->Name = (PCHAR)(typeRec + 1);
    typeRec->Only64 = only64;

    memcpy( typeRec->Name, Struc64->Name, strLen );

    if (only64 == FALSE) {
        typeRec->Size32 = Struc32->Size;
    }

    typeRec->Size64 = Struc64->Size;
    typeRec->FieldList = NULL;
    typeRec->SignExtend = FALSE;
    typeRec->Fabricated = FALSE;

    //
    // Create the FIELD_RECs hanging off of this type
    //

    index = 0;
    while (TRUE) {

        if (only64 == FALSE) {
            Field32 = CalcPtr( Struc32->Fields[index] );
        }

        Field64 = CalcPtr( Struc64->Fields[index] );

        if (Field64 == NULL) {
            break;
        }

        if (only64 == FALSE) {
            CheckCondition( strcmp( Field32->Name, Field64->Name ) == 0 &&
                            strcmp( Field32->TypeName, Field64->TypeName ) == 0,
                            "Mismatched structure definitions found.\n" );
        }

        strLen = strlen( Field64->Name ) + sizeof(CHAR);
        strLen2 = strlen( Field64->TypeName ) + sizeof(CHAR);
        strLen3 = strlen( Field64->SizeFormula );
        if (strLen3 > 0) {
            strLen3 += sizeof(CHAR);
        }

        fieldRec = GetMem( sizeof(FIELD_REC) + strLen + strLen2 + strLen3 );
        fieldRec->Name = (PCHAR)(fieldRec + 1);
        fieldRec->TypeName = fieldRec->Name + strLen;

        memcpy( fieldRec->Name, Field64->Name, strLen );
        memcpy( fieldRec->TypeName, Field64->TypeName, strLen2 );

        if (strLen3 > 0) {
            fieldRec->SizeFormula = fieldRec->TypeName + strLen2;
            memcpy( fieldRec->SizeFormula, Field64->SizeFormula, strLen3 );
        } else {
            fieldRec->SizeFormula = NULL;
        }

        if (only64 == FALSE) {
            fieldRec->Offset32 = Field32->Offset;
            fieldRec->Size32 = Field32->Size;
            fieldRec->TypeSize32 = Field32->TypeSize;
        }

        fieldRec->Offset64 = Field64->Offset;
        fieldRec->TypeSize64 = Field64->TypeSize;
        fieldRec->Size64 = Field64->Size;

        fieldRec->Next = NULL;
        fieldRec->TypeRec = NULL;

        //
        // Insert at the end of the list
        //

        insertNode = CONTAINING_RECORD( &typeRec->FieldList,
                                        FIELD_REC,
                                        Next );
        while (insertNode->Next != NULL) {
            insertNode = insertNode->Next;
        }
        insertNode->Next = fieldRec;

        index += 1;
    }

    //
    // Insert it into the global list
    //

    CheckCondition( FindTypeRec( typeRec->Name ) == NULL,
                    "Duplicate definition for structure %s\n",
                    typeRec->Name );

    NewGlobalType( typeRec );

    return TRUE;
}

PTYPE_REC
FindTypeRec(
    IN PCHAR Name
    )

/*++

Routine Description:

    Searches the global list of TYPE_REC structures for one with a name
    that matches the supplied name.

Arguments:

    Name - pointer to a null-terminated string representing the name of
        the sought type.

Return value:

    A pointer to the matching TYPE_REC, or NULL if a match was not found.

--*/

{
    PTYPE_REC typeRec;

    typeRec = TypeRecList;
    while (typeRec != NULL) {

        if (strcmp( Name, typeRec->Name ) == 0) {
            return typeRec;
        }

        typeRec = typeRec->Next;
    }
    return NULL;
}

PVOID
GetMem(
    IN ULONG Size
    )

/*++

Routine Description:

    Memory allocator.  Works just like malloc() except that triggers a
    fatal error in the event of an out-of-memory condition.

Arguments:

    Size - number of bytes to allocate.

Return value:

    Returns a pointer to a block of memory of the specified size.

--*/

{
    PVOID mem;

    mem = malloc( Size );
    CheckCondition( mem != NULL,
                    "Out of memory.\n" );

    return mem;
}

VOID
FabricateMissingTypes(
    VOID
    )

/*++

Routine Description:

    Routine to generate TYPE_REC records for simple types referenced, but
    not defined, by a structure layout file.

Arguments:

    None.

Return value:

    None.

--*/

{
    PTYPE_REC typeRec;
    PTYPE_REC fieldTypeRec;
    PFIELD_REC fieldRec;
    PCHAR fieldTypeName;
    ULONG strLen;

    typeRec = TypeRecList;
    while (typeRec != NULL) {

        fieldRec = typeRec->FieldList;
        while (fieldRec != NULL) {

            fieldTypeRec = FindTypeRec( fieldRec->TypeName );
            if (fieldTypeRec == NULL) {

                if (typeRec->Only64 == FALSE) {
                    CheckCondition( (fieldRec->Size32 == fieldRec->Size64) ||
    
                                    ((fieldRec->Size32 == 1 ||
                                      fieldRec->Size32 == 2 ||
                                      fieldRec->Size32 == 4 ||
                                      fieldRec->Size32 == 8) &&
                                     (fieldRec->Size64 > fieldRec->Size32) &&
                                     (fieldRec->Size64 % fieldRec->Size32 == 0)),
    
                                    "Must specify type %s (%s)\n",
                                    fieldRec->TypeName,
                                    typeRec->Name );
                }

                //
                // No typerec exists for this type.  Assume it is a simple
                // type.
                //

                if ((typeRec->Only64 != FALSE &&
                     fieldRec->Size64 == sizeof(ULONGLONG) &&
                     *fieldRec->TypeName == 'P') ||

                    (fieldRec->Size32 == sizeof(PVOID) &&
                     fieldRec->Size64 == sizeof(ULONGLONG))) {

                    //
                    // Either a pointer or [U]LONG_PTR type.  Make
                    // it longlong.
                    //

                    fieldTypeRec = &pointer64_typerec;

                } else {

                    //
                    // Some other type.
                    // 

                    strLen = strlen( fieldRec->TypeName ) + sizeof(CHAR);
                    fieldTypeRec = GetMem( sizeof(TYPE_REC) + strLen );
                    fieldTypeRec->Name = (PCHAR)(fieldTypeRec + 1);
                    memcpy( fieldTypeRec->Name, fieldRec->TypeName, strLen );
                    fieldTypeRec->Size32 = fieldRec->Size32;
                    fieldTypeRec->Size64 = fieldRec->Size64;
                    fieldTypeRec->FieldList = NULL;
                    fieldTypeRec->SignExtend = TRUE;
                    fieldTypeRec->Fabricated = TRUE;

                    NewGlobalType( fieldTypeRec );
                }

            }
            fieldRec->TypeRec = fieldTypeRec;
            fieldRec = fieldRec->Next;
        }
        typeRec = typeRec->Next;
    }
}

VOID
WriteCopyRecord(
    IN ULONG Offset32,
    IN ULONG Offset64,
    IN PCHAR TypeName,
    IN ULONG Size32,
    IN ULONG Size64,
    IN BOOL SignExtend,
    IN PCHAR FieldName,
    IN BOOL Last
    )

/*++

Routine Description:

    Support routine to generate the text of a copy record.

Arguments:

    Offset32 - Offset of this field within a 32-bit structure layout

    Offset64 - Offset of this field within a 64-bit structure layout

    Size32 - Size of this field within a 32-bit structure layout

    Size64 - Size of this field within a 64-bit structure layout

    SignExtend - Indicates whether this type should be sign extended or not

    FieldName - Name of the field

    Last - Whether this is the last copy record in a zero-terminated list

Return value:

    None

--*/

{
    CHAR buf[ 255 ];

    buf[sizeof(buf) - 1] = 0;
    if (SignExtend) {
        _snprintf(buf, sizeof(buf) - 1, "IS_SIGNED_TYPE(%s)", TypeName);
    } else {
        _snprintf(buf, sizeof(buf) - 1, "FALSE");
    }

    printf("    { \t0x%x, \t0x%x, \t0x%x, \t0x%x, \t%5s }%s\n",
           Offset32,
           Offset64,
           Size32,
           Size64,
           buf,
           Last ? "" : "," );
}

VOID
WriteDefinition64(
    IN PTYPE_REC TypeRec
    )

/*++

Routine Description:

    Generates a structure definition that represents, to a 32-bit compiler,
    the layout of a 64-bit structure.

Arguments:

    TypeRec - Pointer to the TYPE_REC structure defining this type.

Return value:

    None.

--*/

{
    PFIELD_REC fieldRec;
    ULONG currentOffset;
    PTYPE_REC fieldTypeRec;
    ULONG padBytes;
    ULONG reservedCount;

    currentOffset = 0;
    reservedCount = 0;

    printf("typedef struct _%s_64 {\n", TypeRec->Name );

    fieldRec = TypeRec->FieldList;
    while (fieldRec != NULL) {

        fieldTypeRec = fieldRec->TypeRec;
        padBytes = fieldRec->Offset64 - currentOffset;
        if (padBytes > 0) {

            printf("    UCHAR Reserved%d[ 0x%x ];\n",
                   reservedCount,
                   padBytes );

            currentOffset += padBytes;
            reservedCount += 1;
        }

        printf("    %s%s %s",
            fieldTypeRec->Name,
            fieldTypeRec->Fabricated ? "" : "_64",
            fieldRec->Name );

        if (fieldRec->Size64 > fieldRec->TypeSize64) {

            CheckCondition( fieldRec->Size64 % fieldRec->TypeSize64 == 0,
                            "Internal error type %s.%s\n",
                            TypeRec->Name, fieldRec->Name );

            //
            // This field must be an array
            //

            printf("[%d]", fieldRec->Size64 / fieldRec->TypeSize64);
        }

        printf(";\n");

        currentOffset += fieldRec->Size64;

        fieldRec = fieldRec->Next;
    }

    padBytes = TypeRec->Size64 - currentOffset;
    if (padBytes > 0) {

        printf("    UCHAR Reserved%d[ 0x%x ];\n", reservedCount, padBytes );
        currentOffset += padBytes;
        reservedCount += 1;
    }

    printf("} %s_64, *P%s_64;\n\n", TypeRec->Name, TypeRec->Name );

    fieldRec = TypeRec->FieldList;
    while (fieldRec != NULL) {

        fieldTypeRec = fieldRec->TypeRec;
        printf("C_ASSERT( FIELD_OFFSET(%s_64,%s) == 0x%x);\n",
               TypeRec->Name,
               fieldRec->Name,
               fieldRec->Offset64);

        fieldRec = fieldRec->Next;
    }
    printf("\n");
}

VOID
WriteCopyList(
    IN PTYPE_REC TypeRec
    )

/*++

Routine Description:

    Generates the list of copy records necessary to copy the contents of
    each of the fields with TypeRec from their 32-bit layout to their
    64-bit layout.

Arguments:

    TypeRec - Pointer to the TYPE_REC structure that defines this type.

Return value:

    None.

--*/

{
    PFIELD_REC fieldRec;

    printf("COPY_REC cr3264_%s[] = {\n", TypeRec->Name);

    WriteCopyListWorker( TypeRec, 0, 0, NULL );

    WriteCopyRecord( 0,0,NULL,0,0,FALSE,NULL,TRUE );
    printf("};\n\n");
}

VOID
WriteCopyListWorker(
    IN PTYPE_REC TypeRec,
    IN ULONG Offset32,
    IN ULONG Offset64,
    IN PCHAR ParentName

    )

/*++

Routine Description:

    Recursively-called support routine for WriteCopyList.  This routine
    generates a copy record if this type is not composed of child types,
    otherwise it calls itself recursively for each child type.

Arguments:

    TypeRec - Pointer to the definition of the type to process.

    Offset32 - Current offset within the master structure being defined.

    Offset64 - Current offset within the master structure being defined.

    ParentName - Not currently used.

Return value:

    None.

--*/

{
    PFIELD_REC fieldRec;
    PTYPE_REC typeRec;
    CHAR fieldName[ 255 ];
    PCHAR fieldStart;
    int   result = 0;

    fieldName[sizeof(fieldName) - 1] = 0;

    fieldRec = TypeRec->FieldList;
    if (fieldRec == NULL) {

        WriteCopyRecord( Offset32,
                         Offset64,
                         TypeRec->Name,
                         TypeRec->Size32,
                         TypeRec->Size64,
                         TypeRec->SignExtend,
                         ParentName,
                         FALSE );
    } else {

        //
        // Build the field name
        //
    
        if (ParentName != NULL) {
            //strcpy( fieldName, ParentName);
            //strcat( fieldName, "." );
            
            result = _snprintf(fieldName, sizeof(fieldName) - 1,
                               "%s.", ParentName);
            if (result < 0) {
               //
               // Not much we can do
               //
               return;
            }
        } else {
            fieldName[0] = '\0';
        }
        fieldStart = &fieldName[ strlen(fieldName) ];

        do {
            strncpy( fieldStart, fieldRec->Name, sizeof(fieldName) - 1 - result );

            // typeRec = FindTypeRec( fieldRec->TypeName );
            typeRec = fieldRec->TypeRec;
            WriteCopyListWorker( typeRec,
                                 fieldRec->Offset32 + Offset32,
                                 fieldRec->Offset64 + Offset64,
                                 fieldName );
            fieldRec = fieldRec->Next;
        } while (fieldRec != NULL);
    }
}

VOID
WriteBufferCopies(
    IN PTYPE_REC TypeRec,
    IN PCHAR StrucName
    )
{
    PFIELD_REC fieldRec;
    PTYPE_REC typeRec;
    CHAR strucName[ 255 ];
    CHAR sizeFormula[ 255 ];
    PCHAR fieldPos;
    PCHAR src;
    PCHAR dst;
    int result;
    ULONG remainingLength;

    if (TypeRec == NULL) {
        return;
    }
    strucName[sizeof(strucName) - 1] = 0;
    sizeFormula[sizeof(sizeFormula) - 1] = 0;

    strncpy(strucName,StrucName,sizeof(strucName) - 2 );
    if (*StrucName != '\0') {
        strcat(strucName,".");
    }
    fieldPos = &strucName[ strlen(strucName) ];

    fieldRec = TypeRec->FieldList;
    while (fieldRec != NULL) {

        strncpy(fieldPos,fieldRec->Name,sizeof(strucName) - 1 - strlen(strucName));
        if (fieldRec->SizeFormula != NULL) {

            //
            // Perform substitution on the size formula
            //
        
            dst = sizeFormula;
            src = fieldRec->SizeFormula;
            remainingLength = sizeof(sizeFormula) - 1;
            do {
                if (*src == '%' &&
                    *(src+1) == '1') {

                    result = _snprintf(dst,
                                       remainingLength,
                                       "Source->%s%s",
                                       StrucName,
                                       *StrucName == '\0' ? "" : ".");
                    if (result < 0) {
                       //
                       // Not much we can do here...
                       //
                       break;
                    }  else {
                       dst += result;
                       remainingLength -= result;
                    }
                    src += 2;
                } else {
                    *dst++ = *src++;
                }
            } while (*src != '\0');
            *dst = '\0';

            printf("\n"
                   "    status = \n"
                   "        CopyBuf( Source->%s,\n"
                   "                 &Destination->%s,\n"
                   "                 %s );\n"
                   "    if (status != ESUCCESS) {\n"
                   "        return status;\n"
                   "    }\n",
                   strucName,
                   strucName,
                   sizeFormula);
        }

        typeRec = fieldRec->TypeRec;
        WriteBufferCopies( typeRec, strucName );

        fieldRec = fieldRec->Next;
    }
}


VOID
WriteThunkSource(
    VOID
    )

/*++

Routine Description:

    Generates the source code and supporting definitions necessary to
    copy all or portions of the contents of 32-bit structures to the
    equivalent 64-bit layout.

Arguments:

    None.

Return value:

    None.

--*/

{
    PTYPE_REC typeRec;

    printf("//\n");
    printf("// Autogenerated file, do not edit\n");
    printf("//\n\n");

    printf("#include <bldrthnk.h>\n\n");
    printf("#pragma warning(disable:4296)\n\n");

    //
    // Output the 64-bit type definitions
    //

    printf("#pragma pack(push,1)\n\n");

    typeRec = TypeRecList;
    while (typeRec != NULL) {

        if (typeRec->Fabricated == FALSE) {
            WriteDefinition64( typeRec );
        }

        typeRec = typeRec->Next;
    }

    printf("#pragma pack(pop)\n\n");

    //
    // Output the copy records
    // 

    typeRec = TypeRecList;
    while (typeRec != NULL) {

        if (typeRec->Only64 == FALSE &&
            typeRec->Fabricated == FALSE) {
            WriteCopyList( typeRec );
        }

        typeRec = typeRec->Next;
    }

    //
    // Generate the copy routines
    //

    typeRec = TypeRecList;
    while (typeRec != NULL) {

        if (typeRec->Only64 == FALSE &&
            typeRec->Fabricated == FALSE) {
            WriteCopyRoutine( typeRec );
        }

        typeRec = typeRec->Next;
    }
    printf("\n");
}

VOID
WriteCopyRoutine(
    IN PTYPE_REC TypeRec
    )

/*++

Routine Description:

    Generates text that implements a function to copy the contents of a
    structure of the specified type from a 32-bit layout to a 64-bit
    layout.

Arguments:

    TypeRec - Pointer to the type for which the function should be generated.

Return value:

    None.

--*/

{
    PCHAR typeName;

    typeName = TypeRec->Name;

    printf("\n"
           "ARC_STATUS\n"
           "__inline\n"
           "static\n"
           "Copy_%s(\n"
           "    IN %s *Source,\n"
           "    OUT %s_64 *Destination\n"
           "    )\n"
           "{\n"
           "    ARC_STATUS status = ESUCCESS;"
           "\n"
           "    DbgPrint(\"BLAMD64: Copy %s->%s_64 (0x%%08x->0x%%08x)\\n\",\n"
           "             (ULONG)Source, (ULONG)Destination );\n"
           "\n"
           "    CopyRec( Source, Destination, cr3264_%s );\n",
           typeName,
           typeName,
           typeName,
           typeName,
           typeName,
           typeName );

    WriteBufferCopies( TypeRec, "" );
    printf("    return status;\n");
    printf("}\n\n");
}

VOID
ApplyFixupsToImage(
    IN PCHAR ObjImage
    )

/*++

Routine Description:

    Processes fixup records found within an object image.

Arguments:

    Pointer to a buffer containing the entire image.

Return value:

    None.

--*/

{
    //
    // 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 ];

        //
        // 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;
        }
    }
}

VOID
NewGlobalType(
    IN PTYPE_REC TypeRec
    )

/*++

Routine Description:

    Inserts a new TYPE_REC structure at the end of the global TYPE_REC
    list.

Arguments:

    TypeRec - Pointer to the TYPE_REC structure to insert.

Return value:

    None.

--*/

{
    PTYPE_REC insertNode;

    insertNode = CONTAINING_RECORD( &TypeRecList,
                                    TYPE_REC,
                                    Next );
    while (insertNode->Next != NULL) {
        insertNode = insertNode->Next;
    }
    insertNode->Next = TypeRec;
    TypeRec->Next = NULL;
}

