SYMEDIT.C

/*++ 

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

symedit.c

Abstract:


Author:

Wesley A. Witt (wesw) 19-April-1993

Environment:

Win32, User Mode

--*/

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "symcvt.h"
#include "cv.h"
#include "strings.h"

#include <imagehlp.h>

// prototypes for this module

BOOL CalculateOutputFilePointers( PIMAGEPOINTERS pi, PIMAGEPOINTERS po );
void ProcessCommandLineArgs( int argc, char *argv[] );
void PrintCopyright( void );
void PrintUsage( void );
void FatalError( int, ... );
BOOL MapOutputFile ( PPOINTERS p, char *fname, int );
void ComputeChecksum( char *szExeFile );
void ReadDebugInfo( PPOINTERS p );
void WriteDebugInfo( PPOINTERS p, BOOL);
void MungeDebugHeadersCoffToCv( PPOINTERS p, BOOL fAddCV );
void MungeExeName( PPOINTERS p, char * szExeName );
void DoCoffToCv(char *, char *, BOOL);
void DoSymToCv(char *, char *, char *, char *);
void DoNameChange(char *, char *, char *);
void DoExtract(char *, char *, char *);
void DoStrip(char *, char *);

IMAGE_DEBUG_DIRECTORY DbgDirSpare;

#define AdjustPtr(ptr) (((ptr) != NULL) ? \
((DWORD)ptr - (DWORD)pi->fptr + (DWORD)po->fptr) : \
((DWORD)(ptr)))


int _CRTAPI1
main(
int argc,
char * argv[]
)
/*++

Routine Description:

Shell for this utility.

Arguments:

argc - argument count
argv - argument pointers


Return Value:

0 - image was converted
>0 - image could not be converted

--*/

{
// Scan the command line and check what operations we are doing

ProcessCommandLineArgs( argc, argv );
return 0;
}

__inline void PrintCopyright( void )
{
puts( "\nMicrosoft(R) Windows NT SymEdit Version 1.0\n"
"(C) 1989-1995 Microsoft Corp. All rights reserved.\n");
}

__inline void PrintUsage( void )
{
PrintCopyright();
puts ("\nUsage: SYMEDIT <OPERATION> -q -o<file out> <file in>\n\n"
"\t<OPERATION> is:\n"
"\tC\tModify CodeView symbol information\n"
"\tN\tEdit name field\n"
"\tX\tExtract debug information\n"
"\tS\tStrip all debug information\n\n"
"Options:\n"
"\t-a\t\tAdd CodeView debug info to file\n"
"\t-n<name>\tName to change to\n"
"\t-o<file>\tspecify output file\n"
"\t-q\t\tquiet mode\n"
"\t-r\t\tReplace COFF debug info with CV info\n"
"\t-s<file>\tSym file source");
}

void
ProcessCommandLineArgs(
int argc,
char *argv[]
)

/*++

Routine Description:

Processes the command line arguments and sets global flags to
indicate the user's desired behavior.

Arguments:

argc - argument count
argv - argument pointers


Return Value:

void

--*/

{
int i;
BOOL fQuiet = FALSE;
BOOL fSilent = FALSE;
char * szOutputFile = NULL;
char * szInputFile = NULL;
char * szExeName = NULL;
char * szDbgFile = NULL;
char * szSymFile = NULL;
int iOperation;
BOOLEAN fAddCV = FALSE;

// Minimun number of of arguments is 2 -- program and operation

if (argc < 2 ||
(strcmp(argv[1], "-?") == 0) ||
(strcmp(argv[1], "?") == 0) )
{
PrintUsage();
exit(1);
}

// All operations on 1 character wide

if (argv[1][1] != 0) {
FatalError(ERR_OP_UNKNOWN, argv[1]);
}

// Validate the operation

switch( argv[1][0] ) {
case 'C':
case 'N':
case 'X':
case 'S':
iOperation = argv[1][0];
break;
default:
FatalError(ERR_OP_UNKNOWN, argv[1]);
}

// Parse out any other switches on the command line

for (i=2; i<argc; i++) {
if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
switch (toupper(argv[i][1])) {

// Add the CV debug information section rather than
// replace the COFF section with the CV info.

case 'A':
fAddCV = TRUE;
break;

// Specify the output name for the DBG file

case 'D':
if (argv[i][2] == 0) {
i += 1;
szDbgFile = argv[i];
} else {
szDbgFile = &argv[i][2];
}
break;

// Specify a new name to shove into the name of the
// debuggee field in the Misc. Debug info field

case 'N':
if (argv[i][2] == 0) {
i += 1;
szExeName = argv[i];
} else {
szExeName = &argv[i][2];
}
break;

// Specify the name of the output file

case 'O':
if (argv[i][2] == 0) {
i += 1;
szOutputFile = argv[i];
} else {
szOutputFile = &argv[i][2];
}
break;

// Be quite and don't put out the banner

case 'Q':
fQuiet = TRUE;
fSilent = TRUE;
break;

// Replace COFF debug information with CODEVIEW debug information

case 'R':
break;

// Convert a Symbol File to CV info

case 'S':
if (argv[i][2] == 0) {
i += 1;
szSymFile = argv[i];
} else {
szSymFile = &argv[i][2];
}
break;

// Print the command line options

case '?':
PrintUsage();
exit(1);
break;

// Unrecognized option

default:
FatalError( ERR_OP_UNKNOWN, argv[i] );
break;
}
} else {
// No leading switch character -- must be a file name

szInputFile = &argv[i][0];

// Process the file(s)

if (!fQuiet) {
PrintCopyright();
fQuiet = TRUE;
}

if (!fSilent) {
printf("processing file: %s\n", szInputFile );
}

// Do switch validation cheching and setup any missing global variables

switch ( iOperation ) {

// For conversions -- there are three types
//
// 1. Coff to CV -- add
// 2. Coff to CV -- replace
// 3. SYM to CV --- add
// 4. SYM to CV -- seperate file
//
// Optional input file (not needed for case 4)
// Optional output file
// Optional sym file (implys sym->CV)
// Optional DBG file

case 'C':
if (szSymFile == NULL) {
DoCoffToCv(szInputFile, szOutputFile, fAddCV);
} else {
DoSymToCv(szInputFile, szOutputFile, szDbgFile, szSymFile);
}
szInputFile = NULL;
szOutputFile = NULL;
szDbgFile = NULL;
szSymFile = NULL;
break;

// For changing the name of the debuggee --
// Must specify input file
// Must specify new name
// Optional output file

case 'N':
DoNameChange(szInputFile, szOutputFile, szExeName);
szInputFile = NULL;
szOutputFile = NULL;
szExeName = NULL;
break;

// For extraction of debug information
// Must specify input file
// Optional output file name
// Optional debug file name

case 'X':
DoExtract(szInputFile, szOutputFile, szDbgFile);
break;

// For full strip of debug information
// Must specify input file
// Optional output file

case 'S':
DoStrip(szInputFile, szOutputFile);
break;
}
}
}
return;
}


void
ReadDebugInfo(
PPOINTERS p
)

/*++

Routine Description:

This function will go out and read in all of the debug information
into memory -- this is required because the input and output
files might be the same, if so then writing out informaiton may
destory data we need at a later time.

Arguments:

p - Supplies a pointer to the structure describing the debug info file

Return Value:

None.

--*/

{
int i;
// int cb;
// char * pb;
// PIMAGE_COFF_SYMBOLS_HEADER pCoffDbgInfo;

// Allocate space to save pointers to debug info

p->iptrs.rgpbDebugSave = (PCHAR *) malloc(p->iptrs.cDebugDir * sizeof(PCHAR));
memset(p->iptrs.rgpbDebugSave, 0, p->iptrs.cDebugDir * sizeof(PCHAR));

// Check each possible debug type record

for (i=0; i<p->iptrs.cDebugDir; i++) {

// If there was debug information then copy over the
// description block and cache in the actual debug data.

if (p->iptrs.rgDebugDir[i] != NULL) {
p->iptrs.rgpbDebugSave[i] =
malloc( p->iptrs.rgDebugDir[i]->SizeOfData );
if (p->iptrs.rgpbDebugSave[i] == NULL) {
FatalError(ERR_NO_MEMORY);
}
__try {
memcpy(p->iptrs.rgpbDebugSave[i],
p->iptrs.fptr +
p->iptrs.rgDebugDir[i]->PointerToRawData,
p->iptrs.rgDebugDir[i]->SizeOfData );
} __except(EXCEPTION_EXECUTE_HANDLER ) {
free(p->iptrs.rgpbDebugSave[i]);
p->iptrs.rgpbDebugSave[i] = NULL;
}
}
}
return;
}



void
WriteDebugInfo(
PPOINTERS p,
BOOL fAddCV
)
/*++

Routine Description:

This function will go out and read in all of the debug information
into memory -- this is required because the input and output
files might be the same, if so then writing out informaiton may
destory data we need at a later time.

Arguments:

p - Supplies a pointer to the structure describing the debug info file

Return Value:

None.

--*/

{
ULONG PointerToDebugData = 0; // Offset from the start of the file
// to the current location to write
// debug information out.
ULONG BaseOfDebugData = 0;
int i, flen;
PIMAGE_DEBUG_DIRECTORY pDir, pDbgDir = NULL;

if (p->optrs.debugSection) {
BaseOfDebugData = PointerToDebugData =
p->optrs.debugSection->PointerToRawData;
} else if (p->optrs.sepHdr) {
BaseOfDebugData = PointerToDebugData =
sizeof(IMAGE_SEPARATE_DEBUG_HEADER) +
p->optrs.sepHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) +
p->optrs.sepHdr->ExportedNamesSize;
}

// Step 2. If the debug information is mapped, we know this
// from the section headers, then we may need to write
// out a new debug director to point to the debug information

if (fAddCV) {
if (p->optrs.optHdr) {
p->optrs.optHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].
VirtualAddress = p->optrs.debugSection->VirtualAddress;
p->optrs.optHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size +=
sizeof(IMAGE_DEBUG_DIRECTORY);
} else if (p->optrs.sepHdr) {
p->optrs.sepHdr->DebugDirectorySize += sizeof(IMAGE_DEBUG_DIRECTORY);
} else {
exit(1);
}

if (p->optrs.sepHdr) {
pDbgDir = (PIMAGE_DEBUG_DIRECTORY) malloc(p->optrs.cDebugDir * sizeof(IMAGE_DEBUG_DIRECTORY));
for (i=0; i<p->optrs.cDebugDir; i++) {
if (p->optrs.rgDebugDir[i] != NULL) {
pDbgDir[i] = *(p->optrs.rgDebugDir[i]);
p->optrs.rgDebugDir[i] = &pDbgDir[i];
}
}
}
for (i=0; i<p->optrs.cDebugDir; i++) {
if (p->optrs.rgDebugDir[i]) {
pDir = (PIMAGE_DEBUG_DIRECTORY) (PointerToDebugData +
p->optrs.fptr);
*pDir = *(p->optrs.rgDebugDir[i]);
p->optrs.rgDebugDir[i] = pDir;
PointerToDebugData += sizeof(IMAGE_DEBUG_DIRECTORY);
}
}
}

// Step 3. For every debug info type, write out the debug information
// and update any header information required

for (i=0; i<p->optrs.cDebugDir; i++) {
if (p->optrs.rgDebugDir[i] != NULL) {
if (p->optrs.rgpbDebugSave[i] != NULL) {
p->optrs.rgDebugDir[i]->PointerToRawData =
PointerToDebugData;
if (p->optrs.debugSection) {
p->optrs.rgDebugDir[i]->AddressOfRawData =
p->optrs.debugSection->VirtualAddress +
PointerToDebugData - BaseOfDebugData;
}
memcpy(p->optrs.fptr + PointerToDebugData,
p->optrs.rgpbDebugSave[i],
p->optrs.rgDebugDir[i]->SizeOfData);

if ((i == IMAGE_DEBUG_TYPE_COFF) &&
(p->optrs.fileHdr != NULL)) {

PIMAGE_COFF_SYMBOLS_HEADER pCoffDbgInfo;
pCoffDbgInfo = (PIMAGE_COFF_SYMBOLS_HEADER)p->optrs.rgpbDebugSave[i];
p->optrs.fileHdr->PointerToSymbolTable =
PointerToDebugData + pCoffDbgInfo->LvaToFirstSymbol;
}
}
PointerToDebugData += p->optrs.rgDebugDir[i]->SizeOfData;
}
}

// Step 4. Clean up any COFF structures if we are replacing
// the coff information with CV info.

if ((p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_COFF] == NULL) &&
(p->optrs.fileHdr != NULL)) {

// Since there is no coff debug information -- clean out
// both fields pointing to the debug info

p->optrs.fileHdr->PointerToSymbolTable = 0;
p->optrs.fileHdr->NumberOfSymbols = 0;
}

// Step 5. Correct the alignments if needed. If there is a real .debug
// section in the file (i.e. it is mapped) then update it.

if (p->optrs.debugSection) {
p->optrs.debugSection->SizeOfRawData =
FileAlign(PointerToDebugData - BaseOfDebugData);

// update the optional header with the new image size

p->optrs.optHdr->SizeOfImage =
SectionAlign(p->optrs.debugSection->VirtualAddress +
p->optrs.debugSection->SizeOfRawData);
p->optrs.optHdr->SizeOfInitializedData +=
p->optrs.debugSection->SizeOfRawData;
}

// calculate the new file size

if (p->optrs.optHdr != NULL) {
flen = FileAlign(PointerToDebugData);
} else {
flen = PointerToDebugData;
}

// finally, update the eof pointer and close the file

UnmapViewOfFile( p->optrs.fptr );

if (!SetFilePointer( p->optrs.hFile, flen, 0, FILE_BEGIN )) {
FatalError( ERR_FILE_PTRS );
}

if (!SetEndOfFile( p->optrs.hFile )) {
FatalError( ERR_SET_EOF );
}

CloseHandle( p->optrs.hFile );

// Exit -- we are done.

return;
}



void
MungeDebugHeadersCoffToCv(
PPOINTERS p,
BOOL fAddCV
)

/*++

Routine Description:


Arguments:

p - pointer to a POINTERS structure (see symcvt.h)

Return Value:

void

--*/

{
if (!fAddCV) {
CV_DIR(&p->optrs) = COFF_DIR(&p->optrs);
COFF_DIR(&p->optrs) = 0;
} else {
CV_DIR(&p->optrs) = &DbgDirSpare;
*(COFF_DIR(&p->optrs)) = *(COFF_DIR(&p->iptrs));
};

*CV_DIR(&p->optrs) = *(COFF_DIR(&p->iptrs));
CV_DIR(&p->optrs)->Type = IMAGE_DEBUG_TYPE_CODEVIEW;
CV_DIR(&p->optrs)->SizeOfData = p->pCvStart.size;
p->optrs.rgpbDebugSave[IMAGE_DEBUG_TYPE_CODEVIEW] = p->pCvStart.ptr;

return;
}



BOOL
MapOutputFile (
PPOINTERS p,
char *fname,
int cb
)

/*++

Routine Description:

Maps the output file specified by the fname argument and saves the
file handle & file pointer in the POINTERS structure.


Arguments:

p - pointer to a POINTERS structure (see symcvt.h)
fname - ascii string for the file name


Return Value:

TRUE - file mapped ok
FALSE - file could not be mapped

--*/

{
BOOL rval;
HANDLE hMap = NULL;
DWORD oSize;

rval = FALSE;

p->optrs.hFile = CreateFile( fname,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
0,
NULL );

if (p->optrs.hFile == INVALID_HANDLE_VALUE) {
goto exit;
}

oSize = p->iptrs.fsize;
if (p->pCvStart.ptr != NULL) {
oSize += p->pCvStart.size;
}
oSize += cb;
oSize += p->iptrs.cDebugDir * sizeof(IMAGE_DEBUG_DIRECTORY);

hMap = CreateFileMapping( p->optrs.hFile, NULL, PAGE_READWRITE,
0, oSize, NULL );

if (hMap == NULL) {
goto exit;
}

p->optrs.fptr = MapViewOfFile( hMap, FILE_MAP_WRITE, 0, 0, 0 );

CloseHandle(hMap);

if (p->optrs.fptr == NULL) {
goto exit;
}
rval = TRUE;
exit:
return rval;
}

BOOL
CalculateOutputFilePointers(
PIMAGEPOINTERS pi,
PIMAGEPOINTERS po
)

/*++

Routine Description:

This function calculates the output file pointers based on the
input file pointers. The same address is used but they are all
re-based off the output file's file pointer.

Arguments:

p - pointer to a IMAGEPOINTERS structure (see symcvt.h)


Return Value:

TRUE - pointers were created
FALSE - pointers could not be created

--*/
{
int i;

// fixup the pointers relative the fptr for the output file
po->dosHdr = (PIMAGE_DOS_HEADER) AdjustPtr(pi->dosHdr);
po->ntHdr = (PIMAGE_NT_HEADERS) AdjustPtr(pi->ntHdr);
po->fileHdr = (PIMAGE_FILE_HEADER) AdjustPtr(pi->fileHdr);
po->optHdr = (PIMAGE_OPTIONAL_HEADER) AdjustPtr(pi->optHdr);
po->sectionHdrs = (PIMAGE_SECTION_HEADER) AdjustPtr(pi->sectionHdrs);
po->sepHdr = (PIMAGE_SEPARATE_DEBUG_HEADER) AdjustPtr(pi->sepHdr);
po->debugSection = (PIMAGE_SECTION_HEADER) AdjustPtr(pi->debugSection);
po->AllSymbols = (PIMAGE_SYMBOL) AdjustPtr(pi->AllSymbols);
po->stringTable = (PUCHAR) AdjustPtr(pi->stringTable);

// move the data from the input file to the output file
memcpy( po->fptr, pi->fptr, pi->fsize );

po->cDebugDir = pi->cDebugDir;
po->rgDebugDir = malloc(po->cDebugDir * sizeof(po->rgDebugDir[0]));
memset(po->rgDebugDir, 0, po->cDebugDir * sizeof(po->rgDebugDir[0]));

for (i=0; i<po->cDebugDir; i++) {
po->rgDebugDir[i] = (PIMAGE_DEBUG_DIRECTORY) AdjustPtr(pi->rgDebugDir[i]);
}
po->rgpbDebugSave = pi->rgpbDebugSave;

return TRUE;
}


void
FatalError(
int idMsg,
...
)
/*++

Routine Description:

Prints a message string to stderr and then exits.

Arguments:

s - message string to be printed

Return Value:

void

--*/

{
va_list marker;
char rgchFormat[256];
char rgch[256];

LoadString(GetModuleHandle(NULL), idMsg, rgchFormat, sizeof(rgchFormat));

va_start(marker, idMsg);
vsprintf(rgch, rgchFormat, marker);
va_end(marker);

fprintf(stderr, "%s\n", rgch);

exit(1);
}


void
ComputeChecksum(
char *szExeFile
)

/*++

Routine Description:

Computes a new checksum for the image by calling imagehlp.dll

Arguments:

szExeFile - exe file name


Return Value:

void

--*/

{
DWORD dwHeaderSum = 0;
DWORD dwCheckSum = 0;
HANDLE hFile;
DWORD cb;
IMAGE_DOS_HEADER dosHdr;
IMAGE_NT_HEADERS ntHdr;

if (MapFileAndCheckSum(szExeFile, &dwHeaderSum, &dwCheckSum) != CHECKSUM_SUCCESS) {
FatalError( ERR_CHECKSUM_CALC );
}

hFile = CreateFile( szExeFile,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);

// seek to the beginning of the file
SetFilePointer( hFile, 0, 0, FILE_BEGIN );

// read in the dos header
if ((ReadFile(hFile, &dosHdr, sizeof(dosHdr), &cb, 0) == FALSE) || (cb != sizeof(dosHdr))) {
FatalError( ERR_CHECKSUM_CALC );
}

// read in the pe header
if ((dosHdr.e_magic != IMAGE_DOS_SIGNATURE) ||
(SetFilePointer(hFile, dosHdr.e_lfanew, 0, FILE_BEGIN) == -1L)) {
FatalError( ERR_CHECKSUM_CALC );
}

// read in the nt header
if ((!ReadFile(hFile, &ntHdr, sizeof(ntHdr), &cb, 0)) || (cb != sizeof(ntHdr))) {
FatalError( ERR_CHECKSUM_CALC );
}

if (SetFilePointer(hFile, dosHdr.e_lfanew, 0, FILE_BEGIN) == -1L) {
FatalError( ERR_CHECKSUM_CALC );
}

ntHdr.OptionalHeader.CheckSum = dwCheckSum;

if (!WriteFile(hFile, &ntHdr, sizeof(ntHdr), &cb, NULL)) {
FatalError( ERR_CHECKSUM_CALC );
}

CloseHandle(hFile);
return;
}


void
MungeExeName(
PPOINTERS p,
char * szExeName
)
/*++

Routine Description:

description-of-function.

Arguments:

argument-name - Supplies | Returns description of argument.
.
.

Return Value:

None.

--*/

{
PIMAGE_DEBUG_MISC pMiscIn;
PIMAGE_DEBUG_MISC pMiscOut;
int cb;
int i;

for (i=0; i<p->iptrs.cDebugDir; i++) {
if (p->optrs.rgDebugDir[i] != 0) {
*(p->optrs.rgDebugDir[i]) = *(p->iptrs.rgDebugDir[i]);
}
}

pMiscIn = (PIMAGE_DEBUG_MISC)
p->iptrs.rgpbDebugSave[IMAGE_DEBUG_TYPE_MISC];

if (p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC] == NULL) {
p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC] = &DbgDirSpare;
memset(&DbgDirSpare, 0, sizeof(DbgDirSpare));
}

pMiscOut = (PIMAGE_DEBUG_MISC)
p->optrs.rgpbDebugSave[IMAGE_DEBUG_TYPE_MISC] =
malloc(p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC]->SizeOfData +
strlen(szExeName));
cb = p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC]->SizeOfData;

while ( cb > 0 ) {
if (pMiscIn->DataType == IMAGE_DEBUG_MISC_EXENAME) {
pMiscOut->DataType = IMAGE_DEBUG_MISC_EXENAME;
pMiscOut->Length = (sizeof(IMAGE_DEBUG_MISC) +
strlen(szExeName) + 3) & ~3;
pMiscOut->Unicode = FALSE;
strcpy(&pMiscOut->Data[0], szExeName);
szExeName = NULL;
} else {
memcpy(pMiscOut, pMiscIn, pMiscIn->Length);
}

p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC]->SizeOfData +=
(pMiscOut->Length - pMiscIn->Length);

cb -= pMiscIn->Length;
pMiscIn = (PIMAGE_DEBUG_MISC) (((char *) pMiscIn) + pMiscIn->Length);
pMiscOut = (PIMAGE_DEBUG_MISC) (((char *) pMiscOut) + pMiscOut->Length);
}

if (szExeName) {
pMiscOut->DataType = IMAGE_DEBUG_MISC_EXENAME;
pMiscOut->Length = (sizeof(IMAGE_DEBUG_MISC) +
strlen(szExeName) + 3) & ~3;
pMiscOut->Unicode = FALSE;
strcpy(&pMiscOut->Data[0], szExeName);
}

return;
}


/*** DoCoffToCv
*
*
*/

void DoCoffToCv(
char * szInput,
char * szOutput,
BOOL fAddCV
)
{
POINTERS p;

// Do default checking

if (szOutput == NULL) {
szOutput = szInput;
}

// Open the input file name and setup the pointers into the file

if (!MapInputFile( &p, NULL, szInput )) {
FatalError( ERR_OPEN_INPUT_FILE, szInput );
}

// Now, if we thing we are playing with PE exes then we need
// to setup the pointers into the map file

if (!CalculateNtImagePointers( &p.iptrs )) {
FatalError( ERR_INVALID_PE, szInput );
}

// We are about to try and do the coff to cv symbol conversion.
//
// Verify that the operation is legal.
//
// 1. We need to have coff debug information to start with
// 2. If the debug info is not mapped then we must not
// be trying to add CodeView info.

if ((p.iptrs.AllSymbols == NULL) || (COFF_DIR(&p.iptrs) == NULL)) {
FatalError( ERR_NO_COFF );
}

if (fAddCV && (p.iptrs.debugSection == 0) && (p.iptrs.sepHdr == NULL)) {
FatalError( ERR_NOT_MAPPED );
}

// Now go out an preform the acutal conversion.

if (!ConvertCoffToCv( &p )) {
FatalError( ERR_COFF_TO_CV );
}

// Read in any additional debug information in the file

ReadDebugInfo(&p);

// Open the output file and adjust the pointers so that we are ok.

if (!MapOutputFile( &p, szOutput, 0 )) {
FatalError( ERR_MAP_FILE, szOutput );
}

CalculateOutputFilePointers( &p.iptrs, &p.optrs );

// Munge the various debug information structures to preform the correct
// operations

MungeDebugHeadersCoffToCv( &p, fAddCV );

// Free our handles on the input file

UnMapInputFile(&p);

// Write out the debug information to the end of the exe

WriteDebugInfo( &p, fAddCV );

// and finally compute the checksum

if (p.iptrs.fileHdr != NULL) {
ComputeChecksum( szOutput );
}

return;
}

/*** DoSymToCv
*
*/

void
DoSymToCv(
char * szInput,
char * szOutput,
char * szDbg,
char * szSym
)
{
POINTERS p;
HANDLE hFile;
DWORD cb;
OFSTRUCT ofs;

// Open the input file name and setup the pointers into the file

if (!MapInputFile( &p, NULL, szSym )) {
FatalError(ERR_OPEN_INPUT_FILE, szSym);
}

// Now preform the desired operation

if ((szOutput == NULL) && (szDbg == NULL)) {
szOutput = szInput;
}

ConvertSymToCv( &p );

if (szOutput) {
if (szOutput != szInput) {
if (OpenFile(szInput, &ofs, OF_EXIST) == 0) {
FatalError(ERR_OPEN_INPUT_FILE, szInput);
}
if (CopyFile(szInput, szOutput, FALSE) == 0) {
FatalError(ERR_OPEN_WRITE_FILE, szOutput);
}
}
hFile = CreateFile(szOutput, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {

FatalError(ERR_OPEN_WRITE_FILE, szOutput); 
}
SetFilePointer(hFile, 0, 0, FILE_END);
} else if (szDbg) {
hFile = CreateFile(szDbg, GENERIC_WRITE, 0, NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
FatalError(ERR_OPEN_WRITE_FILE, szDbg);
}
}

WriteFile(hFile, p.pCvStart.ptr, p.pCvStart.size, &cb, NULL);
CloseHandle(hFile);

return;
}


void
DoNameChange(
char * szInput,
char * szOutput,
char * szNewName
)
{
POINTERS p;

// Open the input file name and setup the pointers into the file

if (!MapInputFile( &p, NULL, szInput )) {
FatalError(ERR_OPEN_INPUT_FILE, szInput);
}

// Now, if we thing we are playing with PE exes then we need
// to setup the pointers into the map file

if (!CalculateNtImagePointers( &p.iptrs )) {
FatalError(ERR_INVALID_PE, szInput);
}

// Now preform the desired operation

if (szOutput == NULL) {
szOutput = szInput;
}

if (szNewName == NULL) {
szNewName = szOutput;
}

if (p.iptrs.sepHdr != NULL) {
FatalError(ERR_EDIT_DBG_FILE);
}

// Read in all of the debug information

ReadDebugInfo(&p);

// Open the output file and adjust the pointers.

if (!MapOutputFile(&p, szOutput,
sizeof(szNewName) * 2 + sizeof(IMAGE_DEBUG_MISC))) {
FatalError(ERR_MAP_FILE, szOutput);
}

CalculateOutputFilePointers(&p.iptrs, &p.optrs);

// Munge the name of the file

MungeExeName(&p, szNewName);

// Close the input file

UnMapInputFile(&p);

// Write out the debug information to the end of the exe

WriteDebugInfo(&p, FALSE);

// and finally compute the checksum

if (p.iptrs.fileHdr != NULL) {
ComputeChecksum( szOutput );
}

return;
}


void
DoStrip(
char * szInput,
char * szOutput
)
{
char OutputFile[_MAX_PATH];

// Make sure we only have the path to the output file (it will always be
// named filename.DBG)

if (szOutput != NULL) {
CopyFileA(szInput, szOutput, FALSE);
}

SplitSymbols(szOutput, NULL, OutputFile, SPLITSYM_EXTRACT_ALL);

// Always delete the output file.

DeleteFileA(OutputFile);

return;
}

void
DoExtract(
char * szInput,
char * szOutput,
char * szDbgFile
)
{
char OutputFile[_MAX_PATH];
char szExt[_MAX_EXT];
char szFileName[_MAX_FNAME];

if (szOutput != NULL) {
CopyFileA(szInput, szOutput, FALSE);
szInput = strdup(szOutput);
_splitpath(szOutput, NULL, NULL, szFileName, szExt);
*(szOutput + strlen(szOutput) - strlen(szFileName) - strlen(szExt)) = '\0';
}

SplitSymbols(szInput, szOutput, OutputFile, 0);

CopyFileA(szDbgFile, OutputFile, TRUE);

if (szOutput) {
free(szInput);
}

return;
}