windows-nt/Source/XPSP1/NT/sdktools/debuggers/symtodbg/cvtodbg/cvtodbg.cpp
2020-09-26 16:20:57 +08:00

428 lines
11 KiB
C++

//
// cvtodbg.cpp
//
// Takes a PE file and a file containing CV info, and jams the CV info
// into the PE file (trashing it). However, The Jammed PE File when
// splitsym'ed gives a dbg file, which can be used for debugging.
//
//
#undef UNICODE
#include "windows.h"
#include "imagehlp.h"
#include "stdio.h"
#include "stdlib.h"
////////////////////////////////////////
//
// Data
//
char szImageName[MAX_PATH];
char szCVName[MAX_PATH];
char szPdbName[MAX_PATH];
char szPdbCurrentPath[MAX_PATH];
HANDLE hFile = INVALID_HANDLE_VALUE;
HANDLE hMappedFile = INVALID_HANDLE_VALUE;
LPVOID pvImageBase = NULL;
BOOL fVerbose = FALSE;
BOOL fForce = FALSE;
typedef struct NB10I // NB10 debug info
{
DWORD nb10; // NB10
DWORD off; // offset, always 0
DWORD sig;
DWORD age;
} NB10I;
typedef struct cvinfo
{
NB10I nb10;
char rgb[0x200 - sizeof(NB10I)];
} CVINFO;
////////////////////////////////////////
//
// Forward declarations
//
BOOL ParseArgs(int argc, WCHAR* argv[]);
void UpdateCodeViewInfo();
void Usage();
void Message(const char* szFormat, ...);
void Error(const char *sz, ...);
void ErrorThrow(DWORD, const char *sz, ...);
void Throw(DWORD);
void MapImage();
void UnmapImage(BOOL fTouch);
BOOL DebugDirectoryIsUseful(LPVOID, ULONG);
void RecalculateChecksum();
ULONG FileSize(HANDLE);
class FileMapping {
public:
FileMapping()
: hFile(NULL), hMapping(NULL), pView(NULL)
{
}
~FileMapping()
{
Cleanup();
}
void Cleanup()
{
if (pView != NULL)
UnmapViewOfFile(pView);
if (hMapping != NULL)
CloseHandle(hMapping);
if (hFile != NULL && hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
}
bool Open(LPCTSTR szFile)
{
hFile = CreateFile(szFile,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE) {
hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (hMapping != NULL) {
pView = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
return true;
}
}
Cleanup();
return false;
}
PVOID GetDataPtr()
{
return pView;
}
DWORD GetSize()
{
return GetFileSize(hFile, NULL);
}
bool IsValid()
{
return (pView != NULL);
}
private:
HANDLE hFile;
HANDLE hMapping;
PVOID pView;
};
////////////////////////////////////////
//
// Code
//
void __cdecl wmain(int argc, WCHAR* argv[])
// Main entry point
//
{
szPdbName[0] = 0;
szPdbCurrentPath[0] = 0;
if (ParseArgs(argc, argv))
{
__try
{
UpdateCodeViewInfo();
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
// nothing, just don't propagate it higher to the user
}
}
}
// find the code view info;
// if new info fits in old space, rewrite; else append new cv record and fix up
// debug directory to point to the new record; append cv info to file.
void UpdateCodeViewInfo()
{
PIMAGE_NT_HEADERS pntHeaders;
ULONG cbWritten;
MapImage();
FileMapping cvMapping;
if (!cvMapping.Open(szCVName))
ErrorThrow(666, "Couldn't open CV file");
pntHeaders = ImageNtHeader(pvImageBase);
if (pntHeaders)
{
if (pntHeaders->OptionalHeader.MajorLinkerVersion >= 3 ||
pntHeaders->OptionalHeader.MinorLinkerVersion >= 5)
{
// make it non vc generated image, we are trashing the binary anyway
if ( pntHeaders->OptionalHeader.MajorLinkerVersion > 5)
pntHeaders->OptionalHeader.MajorLinkerVersion = 5;
// put dbg info back in if its already stripped.
if (pntHeaders->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED)
pntHeaders->FileHeader.Characteristics ^= IMAGE_FILE_DEBUG_STRIPPED;
ULONG ibFileCvStart = FileSize(hFile);
SetFilePointer(hFile, 0, NULL, FILE_END);
while (ibFileCvStart & 7) // Align file length to 8-bytes. Slow, but clearly
{ // works! And for 7 bytes, who cares.
BYTE zero = 0;
WriteFile(hFile, &zero, 1, &cbWritten, NULL);
ibFileCvStart++;
}
// Write out the CV info.
WriteFile(hFile, cvMapping.GetDataPtr(), cvMapping.GetSize(), &cbWritten, NULL);
// Make up a debug directory
IMAGE_DEBUG_DIRECTORY dbgdirs[2];
dbgdirs[0].Characteristics = pntHeaders->FileHeader.Characteristics;
dbgdirs[0].TimeDateStamp = pntHeaders->FileHeader.TimeDateStamp;
dbgdirs[0].MajorVersion = 0;
dbgdirs[0].MinorVersion = 0;
dbgdirs[0].Type = IMAGE_DEBUG_TYPE_MISC;
dbgdirs[0].SizeOfData = 0;
dbgdirs[0].AddressOfRawData = ibFileCvStart;
dbgdirs[0].PointerToRawData = ibFileCvStart;
dbgdirs[1].Characteristics = pntHeaders->FileHeader.Characteristics;
dbgdirs[1].TimeDateStamp = pntHeaders->FileHeader.TimeDateStamp;
dbgdirs[1].MajorVersion = 0;
dbgdirs[1].MinorVersion = 0;
dbgdirs[1].Type = IMAGE_DEBUG_TYPE_CODEVIEW;
dbgdirs[1].SizeOfData = cvMapping.GetSize();
dbgdirs[1].AddressOfRawData = ibFileCvStart;
dbgdirs[1].PointerToRawData = ibFileCvStart;
// Find the beginning of the first section and stick the debug directory there
// (did we mention we're trashing the file?)
IMAGE_SECTION_HEADER* pFirstSection = IMAGE_FIRST_SECTION(pntHeaders);
memcpy((PBYTE)((DWORD)pvImageBase + pFirstSection->PointerToRawData), &dbgdirs, sizeof(dbgdirs));
pntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = pFirstSection->VirtualAddress;
pntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size = sizeof(dbgdirs);
}
}
UnmapImage(TRUE);
}
void MapImage()
// Map the image into memory for read-write. Caller MUST call
// Unmapimage to clean up even on failure.
{
if (fForce)
SetFileAttributesA(szImageName, FILE_ATTRIBUTE_NORMAL);
hFile = CreateFileA( szImageName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hFile == INVALID_HANDLE_VALUE)
{
ErrorThrow(GetLastError(), "unable to open '%s'\n", szImageName);
}
hMappedFile = CreateFileMapping( hFile,
NULL,
PAGE_READWRITE,
0,
0,
NULL
);
if (!hMappedFile)
{
ErrorThrow(GetLastError(), "un`able to create file mapping for '%s'\n", szImageName);
}
pvImageBase = MapViewOfFile(hMappedFile, FILE_MAP_WRITE, 0, 0, 0);
if (!pvImageBase)
{
ErrorThrow(GetLastError(), "unable to map view of '%s'\n", szImageName);
}
}
void UnmapImage(BOOL fTouch)
// Clean up whatever MapImage does
{
if (pvImageBase)
{
FlushViewOfFile(pvImageBase, 0);
UnmapViewOfFile(pvImageBase);
pvImageBase = NULL;
}
if (hMappedFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hMappedFile);
hMappedFile = INVALID_HANDLE_VALUE;
}
if (hFile != INVALID_HANDLE_VALUE)
{
if (fTouch)
{
TouchFileTimes(hFile, NULL);
}
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
}
BOOL ParseArgs(int argc, WCHAR* argv[])
// Parse the arguments and set our flags appropriately
{
WCHAR* wszString;
WCHAR c;
szImageName[0] = L'\0';
szCVName[0] = L'\0';
while (--argc)
{
wszString = *++argv;
if (*wszString == L'/' || *wszString == L'-')
{
while ((c = *++wszString) != L'\0')
{
switch (towupper( c ))
{
case L'?':
Usage();
return FALSE;
case L'V':
fVerbose = TRUE;
break;
default:
Error("invalid switch - /%c\n", c );
Usage();
return FALSE;
}
}
}
else
{
if (szImageName[0] == L'\0')
{
wcstombs(szImageName, wszString, MAX_PATH);
}
else if (szCVName[0] == L'\0')
{
wcstombs(szCVName, wszString, MAX_PATH);
}
else
{
Error("too many files specified\n");
Usage();
return FALSE;
}
}
}
if (szImageName==NULL)
{
Error("no image name specified\n");
Usage();
return FALSE;
}
if (szCVName==NULL)
{
Error("no CV filename specified\n");
Usage();
return FALSE;
}
return TRUE;
}
void Usage()
{
fprintf(stderr, "Usage: cvtodbg [options] imageName cvFile\n"
" [-?] display this message\n"
" [-f] overwrite readonly files\n");
}
void Message(const char* szFormat, ...)
{
va_list va;
va_start(va, szFormat);
fprintf (stdout, "resetpdb: ");
vfprintf(stdout, szFormat, va);
va_end(va);
}
void Error(const char* szFormat, ...)
{
va_list va;
va_start(va, szFormat);
fprintf (stderr, "resetpdb: error: ");
vfprintf(stderr, szFormat, va);
va_end(va);
}
void ErrorThrow(DWORD dw, const char* szFormat, ...)
{
va_list va;
va_start(va, szFormat);
fprintf (stderr, "resetpdb: error: ");
vfprintf(stderr, szFormat, va);
va_end(va);
Throw(dw);
}
void Throw(DWORD dw)
{
RaiseException(dw, EXCEPTION_NONCONTINUABLE, 0, NULL);
}
BOOL DebugDirectoryIsUseful(LPVOID Pointer, ULONG Size)
{
return (Pointer != NULL) &&
(Size >= sizeof(IMAGE_DEBUG_DIRECTORY)) &&
((Size % sizeof(IMAGE_DEBUG_DIRECTORY)) == 0);
}
ULONG FileSize(HANDLE h)
// Answer the size of the file with this handle
{
BY_HANDLE_FILE_INFORMATION info;
GetFileInformationByHandle(h, &info);
return info.nFileSizeLow;
}