/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    rebase.c

Abstract:

    Source file for the REBASE utility that takes a group of image files and
    rebases them so they are packed as closely together in the virtual address
    space as possible.

Author:

    Mark Lucovsky (markl) 30-Apr-1993

Revision History:

--*/

#include <private.h>

#define ROUNDUP(x, y) ((x + (y-1)) & ~(y-1))

VOID
RemoveRelocations(
    PCHAR ImageName
    );


#define REBASE_ERR 99
#define REBASE_OK  0
ULONG ReturnCode = REBASE_OK;

#define ROUND_UP( Size, Amount ) (((ULONG)(Size) + ((Amount) - 1)) & ~((Amount) - 1))

BOOL fVerbose;
BOOL fQuiet;
BOOL fGoingDown;
BOOL fSumOnly;
BOOL fRebaseSysfileOk;
BOOL fShowAllBases;
BOOL fCoffBaseIncExt;
FILE *CoffBaseDotTxt;
FILE *BaseAddrFile;
FILE *RebaseLog;
ULONG SplitFlags;
BOOL fRemoveRelocs;
BOOL fUpdateSymbolsOnly;

LPSTR BaseAddrFileName;

BOOL
ProcessGroupList(
    LPSTR ImagesRoot,
    LPSTR GroupListFName,
    BOOL  fReBase,
    BOOL  fOverlay
    );

BOOL
FindInIgnoreList(
    LPSTR chName
    );

ULONG64
FindInBaseAddrFile(
    LPSTR Name,
    PULONG pulSize
    );

VOID
ReBaseFile(
    LPSTR pstrName,
    BOOL  fReBase
    );

VOID
ParseSwitch(
    CHAR chSwitch,
    int *pArgc,
    char **pArgv[]
    );


VOID
ShowUsage(
    VOID
    );

typedef struct _GROUPNODE {
    struct _GROUPNODE *pgnNext;
    PCHAR chName;
} GROUPNODE, *PGROUPNODE;

PGROUPNODE pgnIgnoreListHdr, pgnIgnoreListEnd;

typedef BOOL (__stdcall *REBASEIMAGE64) (
    IN     PSTR CurrentImageName,
    IN     PSTR SymbolPath,
    IN     BOOL  fReBase,          // TRUE if actually rebasing, false if only summing
    IN     BOOL  fRebaseSysfileOk, // TRUE is system images s/b rebased
    IN     BOOL  fGoingDown,       // TRUE if the image s/b rebased below the given base
    IN     ULONG CheckImageSize,   // Max size allowed  (0 if don't care)
    OUT    ULONG *OldImageSize,    // Returned from the header
    OUT    ULONG64 *OldImageBase,  // Returned from the header
    OUT    ULONG *NewImageSize,    // Image size rounded to next separation boundary
    IN OUT ULONG64 *NewImageBase,  // (in) Desired new address.
                                   // (out) Next address (actual if going down)
    IN     ULONG TimeStamp         // new timestamp for image, if non-zero
    );

REBASEIMAGE64 pReBaseImage64;

UCHAR ImagesRoot[ MAX_PATH+1 ];
PCHAR SymbolPath;
UCHAR DebugFilePath[ MAX_PATH+1 ];

ULONG64 OriginalImageBase;
ULONG OriginalImageSize;
ULONG64 NewImageBase;
ULONG NewImageSize;

ULONG64 InitialBase = 0;
ULONG64 MinBase = (~((ULONG64)0));
ULONG64 TotalSize;

ULONG SizeAdjustment;


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

    char chChar, *pchChar;
    envp;
    _tzset();

    pgnIgnoreListHdr = (PGROUPNODE) malloc( sizeof ( GROUPNODE ) );
    pgnIgnoreListHdr->chName = NULL;
    pgnIgnoreListHdr->pgnNext = NULL;
    pgnIgnoreListEnd = pgnIgnoreListHdr;

    pReBaseImage64 = (REBASEIMAGE64) GetProcAddress(GetModuleHandle("imagehlp.dll"), "ReBaseImage64");
    if (!pReBaseImage64) {
        puts("REBASE: Warning\n"
             "REBASE: Warning - unable to correctly rebase 64-bit images - update your imagehlp.dll\n"
             "REBASE: Warning");
        pReBaseImage64 = (REBASEIMAGE64) GetProcAddress(GetModuleHandle("imagehlp.dll"), "ReBaseImage");
    }

    fVerbose = FALSE;
    fQuiet = FALSE;
    fGoingDown = FALSE;
    fSumOnly = FALSE;
    fRebaseSysfileOk = FALSE;
    fShowAllBases = FALSE;

    ImagesRoot[ 0 ] = '\0';

    if (argc <= 1) {
        ShowUsage();
        }

    while (--argc) {
        pchChar = *++argv;
        if (*pchChar == '/' || *pchChar == '-') {
            while (chChar = *++pchChar) {
                ParseSwitch( chChar, &argc, &argv );
                }
            }
        else {
            if (*pchChar == '@') {
                // Inline response file with a list of files to rebase.
                FILE *hFiles =fopen(pchChar+1, "rt");
                int ScanRet;
                CHAR pchFileName[_MAX_PATH];
                if (hFiles == NULL) {
                    fprintf( stderr, "REBASE: fopen %s failed %d\n", pchChar+1, errno );
                    ExitProcess( REBASE_ERR );
                }

                ScanRet = fscanf( hFiles, "%s", pchFileName );
                while (ScanRet && ScanRet != EOF) {
                    if ( !FindInIgnoreList( pchFileName ) ) {
                        ReBaseFile( pchFileName, TRUE );
                    }
                    ScanRet = fscanf( hFiles, "%s", pchFileName );
                }
                fclose(hFiles);
            } else {
                if ( !FindInIgnoreList( pchChar ) ) {
                    ReBaseFile( pchChar, TRUE );
                    }
                }
            }
        }

    if ( !fQuiet ) {

        if ( BaseAddrFile ) {
            InitialBase = MinBase;
        }

        if ( fGoingDown ) {
            TotalSize = InitialBase - NewImageBase;
        }
        else {
            TotalSize = NewImageBase - InitialBase;
        }

        fprintf( stdout, "\n" );
        fprintf( stdout, "REBASE: Total Size of mapping 0x%016I64x\n", TotalSize );
        fprintf( stdout, "REBASE: Range 0x%016I64x -0x%016I64x\n",
                 min(NewImageBase, InitialBase), max(NewImageBase, InitialBase));

        if (RebaseLog) {
            fprintf( RebaseLog, "\nTotal Size of mapping 0x%016I64x\n", TotalSize );
            fprintf( RebaseLog, "Range 0x%016I64x -0x%016I64x\n\n",
                     min(NewImageBase, InitialBase), max(NewImageBase, InitialBase));
        }
    }

    if (RebaseLog) {
        fclose(RebaseLog);
        }

    if (BaseAddrFile){
        fclose(BaseAddrFile);
        }

    if (CoffBaseDotTxt){
        fclose(CoffBaseDotTxt);
        }

    return ReturnCode;
}


VOID
ShowUsage(
    VOID
    )
{
    fputs( "usage: REBASE [switches]\n"
           "              [-R image-root [-G filename] [-O filename] [-N filename]]\n"
           "              image-names... \n"
           "\n"
           "              One of -b and -i switches are mandatory.\n"
           "\n"
           "              [-a] Does nothing\n"
           "              [-b InitialBase] specify initial base address\n"
           "              [-c coffbase_filename] generate coffbase.txt\n"
           "                  -C includes filename extensions, -c does not\n"
           "              [-d] top down rebase\n"
           "              [-e SizeAdjustment] specify extra size to allow for image growth\n"
           "              [-f] Strip relocs after rebasing the image\n"
           "              [-i coffbase_filename] get base addresses from coffbase_filename\n"
           "              [-l logFilePath] write image bases to log file.\n"
           "              [-p] Does nothing\n"
           "              [-q] minimal output\n"
           "              [-s] just sum image range\n"
           "              [-u symbol_dir] Update debug info in .DBG along this path\n"
           "              [-v] verbose output\n"
           "              [-x symbol_dir] Same as -u\n"
           "              [-z] allow system file rebasing\n"
           "              [-?] display this message\n"
           "\n"
           "              [-R image_root] set image root for use by -G, -O, -N\n"
           "              [-G filename] group images together in address space\n"
           "              [-O filename] overlay images in address space\n"
           "              [-N filename] leave images at their origional address\n"
           "                  -G, -O, -N, may occur multiple times.  File \"filename\"\n"
           "                  contains a list of files (relative to \"image-root\")\n" ,
           stderr );

    exit( REBASE_ERR );
}


VOID
ParseSwitch(
    CHAR chSwitch,
    int *pArgc,
    char **pArgv[]
    )
{

    switch (toupper( chSwitch )) {

        case '?':
            ShowUsage();
            break;

        case 'A':
            break;

        case 'B':
            if (!--(*pArgc)) {
                ShowUsage();
                }
            (*pArgv)++;
            if (sscanf(**pArgv, "%I64x", &InitialBase) == 1) {
                NewImageBase = InitialBase;
            }
            break;

        case 'C':
            if (!--(*pArgc)) {
                ShowUsage();
                }
            (*pArgv)++;
            fCoffBaseIncExt = (chSwitch == 'C');
            CoffBaseDotTxt = fopen( *(*pArgv), "at" );
            if ( !CoffBaseDotTxt ) {
                fprintf( stderr, "REBASE: fopen %s failed %d\n", *(*pArgv), errno );
                ExitProcess( REBASE_ERR );
                }
            break;

        case 'D':
            fGoingDown = TRUE;
            break;

        case 'E':
            if (!--(*pArgc)) {
                ShowUsage();
            }
            (*pArgv)++;
            if (sscanf(**pArgv, "%x", &SizeAdjustment) != 1) {
                ShowUsage();
            }
            break;

        case 'F':
            fRemoveRelocs = TRUE;
            break;

        case 'G':
        case 'O':
        case 'N':
            if (!--(*pArgc)) {
                ShowUsage();
                }
            (*pArgv)++;
            if (!ImagesRoot[0]) {
                fprintf( stderr, "REBASE: -R must preceed -%c\n", chSwitch );
                exit( REBASE_ERR );
                }
            ProcessGroupList( (PCHAR) ImagesRoot,
                              *(*pArgv),
                              toupper(chSwitch) != 'N',
                              toupper(chSwitch) == 'O');
            break;

        case 'I':
            if (!--(*pArgc)) {
                ShowUsage();
                }
            (*pArgv)++;
            BaseAddrFileName = *(*pArgv);
            BaseAddrFile = fopen( *(*pArgv), "rt" );
            if ( !BaseAddrFile ) {
                fprintf( stderr, "REBASE: fopen %s failed %d\n", *(*pArgv), errno );
                ExitProcess( REBASE_ERR );
                }
            break;

        case 'L':
            if (!--(*pArgc)) {
                ShowUsage();
                }
            (*pArgv)++;
            RebaseLog = fopen( *(*pArgv), "at" );
            if ( !RebaseLog ) {
                fprintf( stderr, "REBASE: fopen %s failed %d\n", *(*pArgv), errno );
                ExitProcess( REBASE_ERR );
                }
            break;

        case 'P':
            break;

        case 'Q':
            fQuiet = TRUE;
            break;

        case 'R':
            if (!--(*pArgc)) {
                ShowUsage();
                }
            (*pArgv)++;
            strcpy( (PCHAR) ImagesRoot, *(*pArgv) );
            break;

        case 'S':
            fprintf(stdout,"\n");
            fSumOnly = TRUE;
            break;

        case 'U':
        case 'X':
            if (!--(*pArgc)) {
                ShowUsage();
                }
            (*pArgv)++;
            fUpdateSymbolsOnly = TRUE;
            SymbolPath = **pArgv;
            break;

        case 'V':
            fVerbose = TRUE;
            break;

        case 'Z':
            fRebaseSysfileOk = TRUE;
            break;

        default:
            fprintf( stderr, "REBASE: Invalid switch - /%c\n", chSwitch );
            ShowUsage();
            break;

        }
}


BOOL
ProcessGroupList(
    LPSTR ImagesRoot,
    LPSTR GroupListFName,
    BOOL  fReBase,
    BOOL  fOverlay
    )
{

    PGROUPNODE pgn;
    FILE *GroupList;

    CHAR  chName[MAX_PATH+1];
    int   ateof;
    ULONG64 SavedImageBase;
    ULONG MaxImageSize=0;

    DWORD dw;
    CHAR  Buffer[ MAX_PATH+1 ];
    LPSTR FilePart;


    if (RebaseLog) {
        fprintf( RebaseLog, "*** %s\n", GroupListFName );
    }

    GroupList = fopen( GroupListFName, "rt" );
    if ( !GroupList ) {
        fprintf( stderr, "REBASE: fopen %s failed %d\n", GroupListFName, errno );
        ExitProcess( REBASE_ERR );
    }

    ateof = fscanf( GroupList, "%s", chName );

    SavedImageBase = NewImageBase;

    while ( ateof && ateof != EOF ) {

        dw = SearchPath( ImagesRoot, chName, NULL, sizeof(Buffer), Buffer, &FilePart );
        if ( dw == 0 || dw > sizeof( Buffer ) ) {
            if (!fQuiet) {
                fprintf( stderr, "REBASE: Could Not Find %s\\%s\n", ImagesRoot, chName );
            }
        }
        else {

            _strlwr( Buffer );  // Lowercase for consistency when displayed.

            pgn = (PGROUPNODE) malloc( sizeof( GROUPNODE ) );
            if ( NULL == pgn ) {
                fprintf( stderr, "REBASE: *** malloc failed.\n" );
                ExitProcess( REBASE_ERR );
            }
            pgn->chName = _strdup( Buffer );
            if ( NULL == pgn->chName ) {
                fprintf( stderr, "REBASE: *** strdup failed (%s).\n", Buffer );
                ExitProcess( REBASE_ERR );
            }
            pgn->pgnNext = NULL;
            pgnIgnoreListEnd->pgnNext = pgn;
            pgnIgnoreListEnd = pgn;

            ReBaseFile( Buffer, fReBase );

            if ( fOverlay ) {
                if ( MaxImageSize < NewImageSize ) {
                    MaxImageSize = NewImageSize;
                }
                NewImageBase = SavedImageBase;
            }
        }

        ateof = fscanf( GroupList, "%s", chName );
    }

    fclose( GroupList );

    if ( fOverlay ) {
        if ( fGoingDown ) {
            NewImageBase -= ROUND_UP( MaxImageSize, IMAGE_SEPARATION );
        }
        else {
            NewImageBase += ROUND_UP( MaxImageSize, IMAGE_SEPARATION );
        }
    }

    if (RebaseLog) {
        fprintf( RebaseLog, "\n" );
    }

    return TRUE;
}


BOOL
FindInIgnoreList(
    LPSTR chName
    )
{
    PGROUPNODE pgn;

    DWORD dw;
    CHAR  Buffer[ MAX_PATH+1 ];
    LPSTR FilePart;


    dw = GetFullPathName( chName, sizeof(Buffer), Buffer, &FilePart );
    if ( dw == 0 || dw > sizeof( Buffer ) ) {
        fprintf( stderr, "REBASE: *** GetFullPathName failed (%s).\n", chName );
        ExitProcess( REBASE_ERR );
        }

    for (pgn = pgnIgnoreListHdr->pgnNext;
         pgn != NULL;
         pgn = pgn->pgnNext) {

        if (!_stricmp( Buffer, pgn->chName ) ) {
            return TRUE;
            }

        }

    return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
/*
******************************************************************************
On a Hydra System, we don't want imaghlp.dll to load user32.dll since it
prevents CSRSS from exiting when running a under a debugger.
The following function has been copied from user32.dll so that we don't
link to user32.dll.
******************************************************************************
*/

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////


VOID
ReBaseFile(
    LPSTR CurrentImageName,
    BOOL fReBase
    )
{
    DWORD dw;
    CHAR  Buffer[ MAX_PATH+1 ];
    CHAR  Buffer2[ MAX_PATH+1 ];
    LPSTR FilePart;
    LPSTR LocalSymbolPath;
    ULONG ThisImageExpectedSize = 0;
    ULONG64 ThisImageRequestedBase = NewImageBase;
    ULONG TimeStamp;
    static char LastName = '\0';
    static ULONG LastTimeStamp = 0;
    BOOL FirstPass = TRUE;

    if ( !InitialBase && !BaseAddrFile ) {
        fprintf( stderr, "REBASE: -b switch must specify a non-zero base  --or--\n" );
        fprintf( stderr, "        -i must specify a filename\n" );
        exit( REBASE_ERR );
        }

    if ( BaseAddrFile && ( InitialBase || fGoingDown || CoffBaseDotTxt ) ) {
        fprintf( stderr, "REBASE: -i is incompatible with -b, -d, and -c\n" );
        exit( REBASE_ERR );
    }

    dw = GetFullPathName( CurrentImageName, sizeof(Buffer), Buffer, &FilePart );
    if ( dw == 0 || dw > sizeof(Buffer) ) {
        FilePart = CurrentImageName;
    }
    _strlwr( FilePart );  // Lowercase for consistency when displayed.

    if ( BaseAddrFile && !(NewImageBase = ThisImageRequestedBase = FindInBaseAddrFile( FilePart, &ThisImageExpectedSize )) ) {
        fprintf( stdout, "REBASE: %-16s Not listed in %s\n", FilePart, BaseAddrFileName );
    }

    if (fUpdateSymbolsOnly) {
        // On update, the symbol path is a semi-colon delimited path.  Find the one we want and
        // then fix the path for RebaseImage.
        HANDLE hDebugFile;
        CHAR Drive[_MAX_DRIVE];
        CHAR Dir[_MAX_DIR];
        PCHAR s;
        hDebugFile = FindDebugInfoFile(CurrentImageName, SymbolPath, DebugFilePath);
        if ( hDebugFile ) {
            CloseHandle(hDebugFile);
            _splitpath(DebugFilePath, Drive, Dir, NULL, NULL);
            _makepath(Buffer2, Drive, Dir, NULL, NULL);
            s = Buffer2 + strlen(Buffer2);
            s = CharPrev(Buffer2, s);
            if (*s == '\\') {
                *s = '\0';
            }
            LocalSymbolPath = Buffer2;
        } else {
            LocalSymbolPath = NULL;
        }
    } else {
        LocalSymbolPath = SymbolPath;
    }

    NewImageSize = (ULONG) -1;  // Hack so we can tell when system images are skipped.

    time( (time_t *) &TimeStamp );

    // Ensure all images with the same first letter have unique timestamps.

    if (!LastTimeStamp)
        LastTimeStamp = TimeStamp;

    if (LastName ==  *FilePart) {
        TimeStamp = LastTimeStamp++;
    } else {
        LastTimeStamp = TimeStamp;
        LastName = *FilePart;
    }

RebaseAgain:

    if (!(*pReBaseImage64)( CurrentImageName,
                      (PCHAR) LocalSymbolPath,
                      fReBase && !fSumOnly,
                      fRebaseSysfileOk,
                      fGoingDown,
                      ThisImageExpectedSize,
                      &OriginalImageSize,
                      &OriginalImageBase,
                      &NewImageSize,
                      &ThisImageRequestedBase,
                      TimeStamp ) ) {

        if (ThisImageRequestedBase == 0) {
            fprintf(stderr,
                    "REBASE: %-16s ***Grew too large (Size=0x%x; ExpectedSize=0x%x)\n",
                    FilePart,
                    OriginalImageSize,
                    ThisImageExpectedSize);
        } else {
            if (GetLastError() == ERROR_BAD_EXE_FORMAT) {
                if (fVerbose) {
                    fprintf( stderr,
                            "REBASE: %-16s DOS or OS/2 image ignored\n",
                            FilePart );
                }
            } else
            if (GetLastError() == ERROR_INVALID_ADDRESS) {
                fprintf( stderr,
                        "REBASE: %-16s Rebase failed.  Relocations are missing or new address is invalid\n",
                        FilePart );
                if (RebaseLog) {
                    fprintf( RebaseLog,
                             "%16s based at 0x%016I64x (size 0x%08x)  Unable to rebase. (missing relocations or new address is invalid)\n",
                             FilePart,
                             OriginalImageBase,
                             OriginalImageSize);
                }
            } else {
                fprintf( stderr,
                        "REBASE: *** RelocateImage failed (%s).  Image may be corrupted\n",
                        FilePart );
            }
        }

        ReturnCode = REBASE_ERR;
        return;

    } else {
        if (GetLastError() == ERROR_INVALID_DATA) {
            fprintf(stderr, "REBASE: Warning: DBG checksum did not match image.\n");
        }
    }

    // Keep track of the lowest base address.

    if (MinBase > NewImageBase) {
        MinBase = NewImageBase;
    }

    if ( fSumOnly || !fReBase ) {
        if (!fQuiet) {
            fprintf( stdout,
                     "REBASE: %16s mapped at %016I64x (size 0x%08x)\n",
                     FilePart,
                     OriginalImageBase,
                     OriginalImageSize);
        }
    } else {
        if (SizeAdjustment && FirstPass && (NewImageSize != (ULONG) -1)) {
            if ((OriginalImageSize + SizeAdjustment) > NewImageSize) {
                // If we were to add SizeAdjustment to the image (say as a ServicePack or QFE fix), we'd blow
                // out our space.  Make room so this isn't necessary.
    
                if (fGoingDown) {
                    // Going down - the requested base is where we need our image
                    // to end - adjust it down so we have room to grow and go again.
                    FirstPass = FALSE;
                    NewImageBase -= IMAGE_SEPARATION;
                    ThisImageRequestedBase = NewImageBase;
                    goto RebaseAgain;
                } else {
                    // Going up.  Move the next guy up so we can grow.
                    ThisImageRequestedBase += IMAGE_SEPARATION;
                }
            }
        }
    
        if (RebaseLog) {
            fprintf( RebaseLog,
                     "%16s rebased to 0x%016I64x (size 0x%08x)\n",
                     FilePart,
                     fGoingDown ? ThisImageRequestedBase : NewImageBase,
                     NewImageSize);
        }

        if ((NewImageSize != (ULONG) -1) &&
            (OriginalImageBase != (fGoingDown ? ThisImageRequestedBase : NewImageBase)) &&
            ( fVerbose || fQuiet )
           ) {
            if ( fVerbose ) {
                fprintf( stdout,
                         "REBASE: %16s initial base at 0x%016I64x (size 0x%08x)\n",
                         FilePart,
                         OriginalImageBase,
                         OriginalImageSize);
            }

            fprintf( stdout,
                     "REBASE: %16s rebased to 0x%016I64x (size 0x%08x)\n",
                     FilePart,
                     fGoingDown ? ThisImageRequestedBase : NewImageBase,
                     NewImageSize);

            if ( fVerbose && fUpdateSymbolsOnly && DebugFilePath[0]) {
                char szExt[_MAX_EXT];
                _splitpath(DebugFilePath, NULL, NULL, NULL, szExt);
                if (_stricmp(szExt, ".pdb")) {
                    fprintf( stdout, "REBASE: %16s updated image base in %s\n", FilePart, DebugFilePath );
                }
            }
        }

        if (fRemoveRelocs) {
            RemoveRelocations(CurrentImageName);
        }
    }

    if ( CoffBaseDotTxt ) {
        if ( !fCoffBaseIncExt ) {
            char *n;
            if ( n  = strrchr(FilePart,'.') ) {
                *n = '\0';
            }
        }

        fprintf( CoffBaseDotTxt,
                 "%-16s 0x%016I64x 0x%08x\n",
                 FilePart,
                 fSumOnly ? OriginalImageBase : (fGoingDown ? ThisImageRequestedBase : NewImageBase),
                 NewImageSize);
    }

    NewImageBase = ThisImageRequestedBase;   // Set up the next one...
}

ULONG64
FindInBaseAddrFile(
    LPSTR Name,
    PULONG pulSize
    )
{

    struct {
        CHAR  Name[MAX_PATH+1];
        ULONG64 Base;
        ULONG Size;
    } BAFileEntry;

    CHAR NameNoExt[MAX_PATH+1];
//    PCHAR pchExt;
    int ateof;


    strcpy(NameNoExt,Name);
//    if (pchExt = strrchr(NameNoExt,'.')) {
//        *pchExt = '\0';
//        }

    if (fseek(BaseAddrFile, 0, SEEK_SET)) {
        return 0;
    }

    ateof = fscanf(BaseAddrFile,"%s %I64x %x",BAFileEntry.Name,&BAFileEntry.Base,&BAFileEntry.Size);
    while ( ateof && ateof != EOF ) {
        if ( !_stricmp(NameNoExt,BAFileEntry.Name) ) {
            *pulSize = BAFileEntry.Size;
            return BAFileEntry.Base;
            }
        ateof = fscanf(BaseAddrFile,"%s %I64x %x",BAFileEntry.Name,&BAFileEntry.Base,&BAFileEntry.Size);
        }

    *pulSize = 0;
    return 0;
}

VOID
RemoveRelocations(
    PCHAR ImageName
    )
{
    // UnSafe...

    LOADED_IMAGE li;
    IMAGE_SECTION_HEADER RelocSectionHdr, *Section, *pRelocSecHdr;
    PIMAGE_DEBUG_DIRECTORY DebugDirectory;
    ULONG DebugDirectorySize, i, RelocSecNum;
    PIMAGE_OPTIONAL_HEADER32 OptionalHeader32 = NULL;
    PIMAGE_OPTIONAL_HEADER64 OptionalHeader64 = NULL;
    PIMAGE_FILE_HEADER FileHeader;

    if (!MapAndLoad(ImageName, NULL, &li, FALSE, FALSE)) {
        return;
    }

    FileHeader = &li.FileHeader->FileHeader;

    OptionalHeadersFromNtHeaders((PIMAGE_NT_HEADERS32)li.FileHeader, &OptionalHeader32, &OptionalHeader64);
    if (!OptionalHeader32 && !OptionalHeader64)
        return;

    // See if the image has already been stripped or there are no relocs.

    if ((FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) ||
        (!OPTIONALHEADER(DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size))) {
        UnMapAndLoad(&li);
        return;
    }

    for (Section = li.Sections, i = 0; i < li.NumberOfSections; Section++, i++) {
        if (Section->PointerToRawData != 0) {
            if (!_stricmp( (char *) Section->Name, ".reloc" )) {
                RelocSectionHdr = *Section;
                pRelocSecHdr = Section;
                RelocSecNum = i + 1;
            }
        }
    }

    RelocSectionHdr.Misc.VirtualSize = ROUNDUP(RelocSectionHdr.Misc.VirtualSize, OPTIONALHEADER(SectionAlignment));
    RelocSectionHdr.SizeOfRawData = ROUNDUP(RelocSectionHdr.SizeOfRawData, OPTIONALHEADER(FileAlignment));

    if (RelocSecNum != li.NumberOfSections) {
        // Move everything else up and fixup old addresses.
        for (i = RelocSecNum - 1, Section = pRelocSecHdr;i < li.NumberOfSections - 1; Section++, i++) {
            *Section = *(Section + 1);
            Section->VirtualAddress -= RelocSectionHdr.Misc.VirtualSize;
            Section->PointerToRawData -= RelocSectionHdr.SizeOfRawData;
        }
    }

    // Zero out the last one.

    RtlZeroMemory(Section, sizeof(IMAGE_SECTION_HEADER));

    // Reduce the section count.

    FileHeader->NumberOfSections--;

    // Set the strip bit in the header

    FileHeader->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;

    // If there's a pointer to the coff symbol table, move it back.

    if (FileHeader->PointerToSymbolTable) {
        FileHeader->PointerToSymbolTable -= RelocSectionHdr.SizeOfRawData;
    }

    // Clear out the base reloc entry in the data dir.

    OPTIONALHEADER_LV(DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) = 0;
    OPTIONALHEADER_LV(DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) = 0;

    // Reduce the Init Data size.

    OPTIONALHEADER_LV(SizeOfInitializedData) -= RelocSectionHdr.Misc.VirtualSize;

    // Reduce the image size.

    OPTIONALHEADER_LV(SizeOfImage) -=
        ((RelocSectionHdr.SizeOfRawData +
          (OPTIONALHEADER(SectionAlignment) - 1)
         ) & ~(OPTIONALHEADER(SectionAlignment) - 1));

    // Move the debug info up (if there is any).

    DebugDirectory = (PIMAGE_DEBUG_DIRECTORY)
                            ImageDirectoryEntryToData( li.MappedAddress,
                                                      FALSE,
                                                      IMAGE_DIRECTORY_ENTRY_DEBUG,
                                                      &DebugDirectorySize
                                                    );
    if (DebugDirectoryIsUseful(DebugDirectory, DebugDirectorySize)) {
        for (i = 0; i < (DebugDirectorySize / sizeof(IMAGE_DEBUG_DIRECTORY)); i++) {
            RtlMoveMemory(li.MappedAddress + DebugDirectory->PointerToRawData - RelocSectionHdr.SizeOfRawData,
                            li.MappedAddress + DebugDirectory->PointerToRawData,
                            DebugDirectory->SizeOfData);

            DebugDirectory->PointerToRawData -= RelocSectionHdr.SizeOfRawData;

            if (DebugDirectory->AddressOfRawData) {
                DebugDirectory->AddressOfRawData -= RelocSectionHdr.Misc.VirtualSize;
            }

            DebugDirectory++;
        }
    }

    // Truncate the image size

    li.SizeOfImage -= RelocSectionHdr.SizeOfRawData;

    // And we're done.

    UnMapAndLoad(&li);
}