Added PE dump capabilities to specmaker and renamed specmaker into

winedump.
This commit is contained in:
Eric Pouech 2001-09-07 16:04:38 +00:00 committed by Alexandre Julliard
parent f876b5df5a
commit d786a12d58
26 changed files with 1820 additions and 2471 deletions

6
configure vendored
View File

@ -7155,10 +7155,9 @@ relay32/Makefile
scheduler/Makefile
server/Makefile
tools/Makefile
tools/cvdump/Makefile
tools/specmaker/Makefile
tools/winapi/Makefile
tools/winebuild/Makefile
tools/winedump/Makefile
tools/winelauncher
tools/wmc/Makefile
tools/wrc/Makefile
@ -7410,10 +7409,9 @@ relay32/Makefile
scheduler/Makefile
server/Makefile
tools/Makefile
tools/cvdump/Makefile
tools/specmaker/Makefile
tools/winapi/Makefile
tools/winebuild/Makefile
tools/winedump/Makefile
tools/winelauncher
tools/wmc/Makefile
tools/wrc/Makefile

View File

@ -1350,10 +1350,9 @@ relay32/Makefile
scheduler/Makefile
server/Makefile
tools/Makefile
tools/cvdump/Makefile
tools/specmaker/Makefile
tools/winapi/Makefile
tools/winebuild/Makefile
tools/winedump/Makefile
tools/winelauncher
tools/wmc/Makefile
tools/wrc/Makefile

View File

@ -10,15 +10,14 @@ MODULE = none
C_SRCS = makedep.c fnt2bdf.c bin2res.c
SUBDIRS = \
cvdump \
specmaker \
winebuild \
winedump \
wmc \
wrc
INSTALLSUBDIRS = \
specmaker \
winebuild \
winedump \
wmc \
wrc
@ -28,7 +27,7 @@ EXTRASUBDIRS = \
winapi_check/win32 \
wineconf.libs
all: $(PROGRAMS) specmaker winebuild wmc wrc
all: $(PROGRAMS) winebuild winedump wmc wrc
@MAKE_RULES@

View File

@ -1,18 +0,0 @@
TOPSRCDIR = @top_srcdir@
TOPOBJDIR = ../..
SRCDIR = @srcdir@
VPATH = @srcdir@
PROGRAMS = cvdump
MODULE = none
C_SRCS = cvcrunch.c cvdump.c cvload.c
all: $(PROGRAMS)
@MAKE_RULES@
cvdump: $(OBJS)
$(CC) $(CFLAGS) -o cvdump $(OBJS)
### Dependencies:

View File

@ -1,185 +0,0 @@
/*
* Functions to process in-memory arrays of CodeView data sections
* (currently only contains sstSrcModule).
*
* Copyright 2000 John R. Sheets
*/
/* FIXME - Change to cvprint.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "cvinclude.h"
/************************ sstSrcModule ************************/
/* Print out stuff in OMFSourceModule block. Rather than using the supplied
* OMFSourceModule struct, we'll extract each piece of data separately from
* the block of memory (rawdata). This struct (and the others used in
* sstSrcModule sections) is pretty useless. We can't use sizeof() on it
* because it contains the first element of the file offset array (i.e. baseSrcFile),
* which we need to parse separately anyway. See below for problems with the
* other structs.
*
* The contents of this section look like this (the first two fields are
* extracted and passed back out):
*
* unsigned short cFile
* unsigned short cSeg
* unsigned long baseSrcFile[cFile]
* unsigned long segarray[cSeg * 2]
* unsigned short segindexarray[cSeg]
*/
int PrintSrcModuleInfo (BYTE* rawdata, short *filecount, short *segcount)
{
int i;
int datalen;
unsigned short cFile;
unsigned short cSeg;
unsigned long *baseSrcFile;
unsigned long *segarray;
unsigned short *segindexarray;
/* Get all our pointers straightened out
*/
cFile = *(short*)rawdata;
cSeg = *(short*)(rawdata + 2);
baseSrcFile = (long*)(rawdata + 4);
segarray = &baseSrcFile[cFile];
segindexarray = (short*)(&segarray[cSeg * 2]);
/* Pass # of segments and files back to calling function
*/
*filecount = cFile;
*segcount = cSeg;
printf ("\n Module table: Found %d file(s) and %d segment(s)\n", cFile, cSeg);
for (i = 0; i < cFile; i++)
{
printf (" File #%d begins at an offset of 0x%lx in this section\n",
i + 1, baseSrcFile[i]);
}
for (i = 0; i < cSeg; i++)
{
printf (" Segment #%d start = 0x%lx, end = 0x%lx, seg index = %d\n",
i + 1, segarray[i * 2], segarray[(i * 2) + 1], segindexarray[i]);
}
/* Return the total length of the data (in bytes) that we used, so
* we'll know how far to jump ahead for the next part of the sstSrcModule.
*/
datalen = ((BYTE*)(&segindexarray[cSeg]) - rawdata);
/* printf ("datalen before padding = %d\n", datalen); */
if (datalen % 4)
datalen += 4 - (datalen % 4);
/* printf ("datalen after padding = %d\n", datalen); */
return datalen;
}
/* Print out the contents of a OMFSourceFile block. Unfortunately, the official
* version of this struct (probably quite outdated) claims that the 'cFName' field
* is a short. Based on experimentation with MSVC 5.0 .DBG files, this field is
* quite clearly only a single byte. Yet another reason to do it all by hand
* and avoid the "official" structs.
*
* The contents of this section look like this (the first field is
* pre-extracted, and 'pad' is ignored):
*
* unsigned short cSeg
* unsigned short pad
* unsigned long baseSrcLn[cSeg]
* unsigned long segarray[cSeg * 2]
* char cFName
* char Name[cFName]
*/
int PrintSrcModuleFileInfo (BYTE* rawdata)
{
int i;
int datalen;
unsigned short cSeg;
unsigned long *baseSrcLn;
unsigned long *segarray;
unsigned char cFName;
char Name[256];
/* Get all our pointers straightened out
*/
cSeg = *(short*)(rawdata);
/* Skip the 'pad' field */
baseSrcLn = (long*)(rawdata + 4);
segarray = &baseSrcLn[cSeg];
cFName = *((char*)&segarray[cSeg * 2]);
snprintf (Name, cFName + 1, "%s", (char*)&segarray[cSeg * 2] + 1);
/* printf ("cSeg = %d\n", cSeg); */
printf ("\n File table: '%s'\n", Name);
for (i = 0; i < cSeg; i++)
{
printf (" Segment #%d start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
i + 1, segarray[i * 2], segarray[(i * 2) + 1], baseSrcLn[i]);
}
/* Return the total length of the data (in bytes) that we used, so
* we'll know how far to jump ahead for the next part of the sstSrcModule.
*/
datalen = ((BYTE*)(&segarray[cSeg * 2]) + cFName + 1 - rawdata);
/* printf ("datalen before padding = %d\n", datalen); */
if (datalen % 4)
datalen += 4 - (datalen % 4);
/* printf ("datalen after padding = %d\n", datalen); */
return datalen;
}
/* Print out the contents of a OMFSourceLine block. The contents of this section
* look like this:
*
* unsigned short Seg
* unsigned short cPair
* unsigned long offset[cPair]
* unsigned long linenumber[cPair]
*/
int PrintSrcModuleLineInfo (BYTE* rawdata, int tablecount)
{
int i;
int datalen;
unsigned short Seg;
unsigned short cPair;
unsigned long *offset;
unsigned short *linenumber;
Seg = *(short*)rawdata;
cPair = *(short*)(rawdata + 2);
offset = (long*)(rawdata + 4);
linenumber = (short*)&offset[cPair];
printf ("\n Line table #%d: Found %d line numbers for segment index %d\n",
tablecount, cPair, Seg);
for (i = 0; i < cPair; i++)
{
printf (" Pair #%2d: offset = [0x%8lx], linenumber = %d\n",
i + 1, offset[i], linenumber[i]);
}
/* Return the total length of the data (in bytes) that we used, so
* we'll know how far to jump ahead for the next part of the sstSrcModule.
*/
datalen = ((BYTE*)(&linenumber[cPair]) - rawdata);
/* printf ("datalen before padding = %d\n", datalen); */
if (datalen % 4)
datalen += 4 - (datalen % 4);
/* printf ("datalen after padding = %d\n", datalen); */
return datalen;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,34 +0,0 @@
/*
* Includes for cvdump tool.
*
* Copyright 2000 John R. Sheets
*/
/* #define VERBOSE */
#include "cvinclude.h"
typedef enum { CV_NONE, CV_DOS, CV_NT, CV_DBG } CVHeaderType;
/*
* Function Prototypes
*/
/* From cvload.c */
CVHeaderType GetHeaderType (FILE *debugfile);
int ReadDOSFileHeader (FILE *debugfile, IMAGE_DOS_HEADER *doshdr);
int ReadPEFileHeader (FILE *debugfile, IMAGE_NT_HEADERS *nthdr);
int ReadDBGFileHeader (FILE *debugfile, IMAGE_SEPARATE_DEBUG_HEADER *dbghdr);
int ReadSectionHeaders (FILE *debugfile, int numsects, IMAGE_SECTION_HEADER **secthdrs);
int ReadDebugDir (FILE *debugfile, int numdirs, IMAGE_DEBUG_DIRECTORY **debugdirs);
int ReadCodeViewHeader (FILE *debugfile, OMFSignature *sig, OMFDirHeader *dirhdr);
int ReadCodeViewDirectory (FILE *debugfile, int entrynum, OMFDirEntry **entries);
int ReadModuleData (FILE *debugfile, int entrynum, OMFDirEntry *entries,
int *module_count, OMFModuleFull **modules);
int ReadChunk (FILE *debugfile, void *dest, int length, int fileoffset);
/* From cvprint.c */
int PrintSrcModuleInfo (BYTE* rawdata, short *filecount, short *segcount);
int PrintSrcModuleFileInfo (BYTE* rawdata);
int PrintSrcModuleLineInfo (BYTE* rawdata, int tablecount);

View File

@ -1,430 +0,0 @@
/*
* Functions to read parts of a .DBG file into their respective struct's
*
* Copyright 2000 John R. Sheets
*/
/*
* .DBG File Layout:
*
* IMAGE_SEPARATE_DEBUG_HEADER
* IMAGE_SECTION_HEADER[]
* IMAGE_DEBUG_DIRECTORY[]
* OMFSignature
* debug data (typical example)
* - IMAGE_DEBUG_TYPE_MISC
* - IMAGE_DEBUG_TYPE_FPO
* - IMAGE_DEBUG_TYPE_CODEVIEW
* OMFDirHeader
* OMFDirEntry[]
*/
/*
* Descriptions:
*
* (hdr) IMAGE_SEPARATE_DEBUG_HEADER - .DBG-specific file header; holds info that
* applies to the file as a whole, including # of COFF sections, file offsets, etc.
* (hdr) IMAGE_SECTION_HEADER - list of COFF sections copied verbatim from .EXE;
* although this directory contains file offsets, these offsets are meaningless
* in the context of the .DBG file, because only the section headers are copied
* to the .DBG file...not the binary data it points to.
* (hdr) IMAGE_DEBUG_DIRECTORY - list of different formats of debug info contained in file
* (see IMAGE_DEBUG_TYPE_* descriptions below); tells where each section starts
* (hdr) OMFSignature (CV) - Contains "NBxx" signature, plus file offset telling how far
* into the IMAGE_DEBUG_TYPE_CODEVIEW section the OMFDirHeader and OMFDirEntry's sit
* (data) IMAGE_DEBUG_TYPE_MISC - usually holds name of original .EXE file
* (data) IMAGE_DEBUG_TYPE_FPO - Frame Pointer Optimization data; used for dealing with
* optimized stack frames (optional)
* (data) IMAGE_DEBUG_TYPE_CODEVIEW - *** THE GOOD STUFF ***
* This block of data contains all the symbol tables, line number info, etc.,
* that the Visual C++ debugger needs.
* (hdr) OMFDirHeader (CV) -
* (hdr) OMFDirEntry (CV) - list of subsections within CodeView debug data section
*/
/*
* The .DBG file typically has three arrays of directory entries, which tell
* the OS or debugger where in the file to look for the actual data
*
* IMAGE_SECTION_HEADER - number of entries determined by:
* (IMAGE_SEPARATE_DEBUG_HEADER.NumberOfSections)
*
* IMAGE_DEBUG_DIRECTORY - number of entries determined by:
* (IMAGE_SEPARATE_DEBUG_HEADER.DebugDirectorySize / sizeof (IMAGE_DEBUG_DIRECTORY))
*
* OMFDirEntry - number of entries determined by:
* (OMFDirHeader.cDir)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "cvdump.h"
extern DWORD g_dwStartOfCodeView;
/*
* Extract a generic block of data from debugfile (pass in fileoffset == -1
* to avoid the fseek()).
*/
int ReadChunk (FILE *debugfile, void *dest, int length, int fileoffset)
{
size_t bytes_read;
if (fileoffset >= 0)
fseek (debugfile, fileoffset, SEEK_SET);
bytes_read = fread (dest, 1, length, debugfile);
if (bytes_read < length)
{
printf ("ERROR: Only able to read %d bytes of %d-byte chunk!\n",
bytes_read, length);
return FALSE;
}
return TRUE;
}
/*
* Scan the next two bytes of a file, and see if they correspond to a file
* header signature. Don't forget to put the file pointer back where we
* found it...
*/
CVHeaderType GetHeaderType (FILE *debugfile)
{
WORD hdrtype;
CVHeaderType ret = CV_NONE;
int oldpos = ftell (debugfile);
#ifdef VERBOSE
printf (" *** Current file position = %lx\n", ftell (debugfile));
#endif
if (!ReadChunk (debugfile, &hdrtype, sizeof (WORD), -1))
{
fseek (debugfile, oldpos, SEEK_SET);
return CV_NONE;
}
if (hdrtype == 0x5A4D) /* "MZ" */
ret = CV_DOS;
else if (hdrtype == 0x4550) /* "PE" */
ret = CV_NT;
else if (hdrtype == 0x4944) /* "DI" */
ret = CV_DBG;
fseek (debugfile, oldpos, SEEK_SET);
#ifdef VERBOSE
printf ("Returning header type = %d [0x%x]\n", ret, hdrtype);
printf (" *** Current file position = %lx\n", ftell (debugfile));
#endif
return ret;
}
/*
* Extract the DOS file headers from an executable
*/
int ReadDOSFileHeader (FILE *debugfile, IMAGE_DOS_HEADER *doshdr)
{
size_t bytes_read;
bytes_read = fread (doshdr, 1, sizeof (IMAGE_DOS_HEADER), debugfile);
if (bytes_read < sizeof (IMAGE_DOS_HEADER))
{
printf ("ERROR: Only able to read %d bytes of %d-byte DOS file header!\n",
bytes_read, sizeof (IMAGE_DOS_HEADER));
return FALSE;
}
/* Skip over stub data, if present
*/
if (doshdr->e_lfanew)
fseek (debugfile, doshdr->e_lfanew, SEEK_SET);
return TRUE;
}
/*
* Extract the DOS and NT file headers from an executable
*/
int ReadPEFileHeader (FILE *debugfile, IMAGE_NT_HEADERS *nthdr)
{
size_t bytes_read;
bytes_read = fread (nthdr, 1, sizeof (IMAGE_NT_HEADERS), debugfile);
if (bytes_read < sizeof (IMAGE_NT_HEADERS))
{
printf ("ERROR: Only able to read %d bytes of %d-byte NT file header!\n",
bytes_read, sizeof (IMAGE_NT_HEADERS));
return FALSE;
}
return TRUE;
}
/*
* Extract the DBG file header from debugfile
*/
int ReadDBGFileHeader (FILE *debugfile, IMAGE_SEPARATE_DEBUG_HEADER *dbghdr)
{
size_t bytes_read;
bytes_read = fread (dbghdr, 1, sizeof (IMAGE_SEPARATE_DEBUG_HEADER), debugfile);
if (bytes_read < sizeof (IMAGE_SEPARATE_DEBUG_HEADER))
{
printf ("ERROR: Only able to read %d bytes of %d-byte DBG file header!\n",
bytes_read, sizeof (IMAGE_SEPARATE_DEBUG_HEADER));
return FALSE;
}
return TRUE;
}
/*
* Extract all of the file's COFF section headers into an array of
* IMAGE_SECTION_HEADER's. These COFF sections don't really apply to
* the .DBG file directly (they contain file offsets into the .EXE file
* which don't correspond to anything in the .DBG file). They are
* copied verbatim into this .DBG file to help make the debugging process
* more robust. By referencing these COFF section headers, the debugger
* can still function in the absence of the original .EXE file!
*
* NOTE: Do not bother pre-allocating memory. This function will
* allocate it for you. Don't forget to free() it when you're done,
* though.
*/
int ReadSectionHeaders (FILE *debugfile, int numsects, IMAGE_SECTION_HEADER **secthdrs)
{
size_t bytes_read;
/* Need a double-pointer so we can change the destination of the pointer
* and return the new allocation back to the caller.
*/
*secthdrs = calloc (numsects, sizeof (IMAGE_SECTION_HEADER));
bytes_read = fread (*secthdrs, sizeof (IMAGE_SECTION_HEADER), numsects, debugfile);
if (bytes_read < numsects)
{
printf ("ERROR while reading COFF headers: Only able to "
"read %d headers out of %d desired!\n",
bytes_read, sizeof (IMAGE_SECTION_HEADER));
return FALSE;
}
return TRUE;
}
/*
* Load in the debug directory table. This directory describes the various
* blocks of debug data that reside at the end of the file (after the COFF
* sections), including FPO data, COFF-style debug info, and the CodeView
* we are *really* after.
*/
int ReadDebugDir (FILE *debugfile, int numdirs, IMAGE_DEBUG_DIRECTORY **debugdirs)
{
size_t bytes_read;
/* Need a double-pointer so we can change the destination of the pointer
* and return the new allocation back to the caller.
*/
*debugdirs = calloc (numdirs, sizeof (IMAGE_DEBUG_DIRECTORY));
bytes_read = fread (*debugdirs, sizeof (IMAGE_DEBUG_DIRECTORY), numdirs, debugfile);
if (bytes_read < numdirs)
{
printf ("ERROR while reading Debug Directory: Only able to "
"read %d headers out of %d desired!\n",
bytes_read, numdirs);
return FALSE;
}
return TRUE;
}
/*
* Load in the CodeView-style headers inside the CodeView debug section.
* The 'sig' and 'dirhdr' parameters must point to already-allocated
* data structures.
*/
int ReadCodeViewHeader (FILE *debugfile, OMFSignature *sig, OMFDirHeader *dirhdr)
{
size_t bytes_read;
bytes_read = fread (sig, 1, sizeof (OMFSignature), debugfile);
if (bytes_read < sizeof (OMFSignature))
{
printf ("ERROR while reading CodeView Header Signature: Only "
"able to read %d bytes out of %d desired!\n",
bytes_read, sizeof (OMFSignature));
return FALSE;
}
/* Must perform a massive jump, almost to the end of the file, to find the
* CodeView Directory Header (OMFDirHeader), which is immediately followed
* by the array of entries (OMFDirEntry). We calculate the jump based on
* the beginning of the CodeView debug section (from the CodeView entry in
* the IMAGE_DEBUG_DIRECTORY array), with the added offset from OMGSignature.
*/
fseek (debugfile, sig->filepos + g_dwStartOfCodeView, SEEK_SET);
bytes_read = fread (dirhdr, 1, sizeof (OMFDirHeader), debugfile);
if (bytes_read < sizeof (OMFDirHeader))
{
printf ("ERROR while reading CodeView Directory Header: Only "
"able to read %d bytes out of %d desired!\n",
bytes_read, sizeof (OMFDirHeader));
return FALSE;
}
/* File pointer is now at first OMGDirEntry, so we can begin reading those now,
* with an immediate call to ReadCodeViewDirectory ().
*/
return TRUE;
}
/*
* Load in the CodeView directory entries, which each point to a CodeView
* subsection (e.g. sstModules, sstGlobalPub). The number of entries in
* this table is determined by OMFDirEntry.cDir.
*
* Strangely enough, this particular section comes immediately *after*
* the debug data (as opposed to immediately *before* the data as is the
* standard with the COFF headers).
*/
int ReadCodeViewDirectory (FILE *debugfile, int entrynum, OMFDirEntry **entries)
{
size_t bytes_read;
/* Need a double-pointer so we can change the destination of the pointer
* and return the new allocation back to the caller.
*/
/* printf ("Allocating space for %d entries\n", entrynum); */
*entries = calloc (entrynum, sizeof (OMFDirEntry));
/* printf ("Allocated memory at %p (%p)\n", *entries, entries); */
bytes_read = fread (*entries, sizeof (OMFDirEntry), entrynum, debugfile);
if (bytes_read < entrynum)
{
printf ("ERROR while reading CodeView Debug Directories: Only "
"able to read %d entries out of %d desired!\n",
bytes_read, entrynum);
return FALSE;
}
return TRUE;
}
/*
* Load in the data contents of all CodeView sstModule sub-sections in the file (likely a
* large array, as there is one sub-section for every code module... > 100 modules is normal).
* 'entrynum' should hold the total number of CV sub-sections, not the number of sstModule
* subsections. The function will ignore anything that isn't a sstModule.
*
* NOTE: 'debugfile' must already be pointing to the correct location.
*/
int ReadModuleData (FILE *debugfile, int entrynum, OMFDirEntry *entries,
int *module_count, OMFModuleFull **modules)
{
int i;
int segnum;
size_t bytes_read;
OMFSegDesc *segarray;
char namelen;
OMFModuleFull *module;
int pad;
/* How much of the OMFModuleFull struct can we pull directly from the file?
* (Kind of a hack, but not much else we can do...the 'SegInfo' and 'Name'
* fields will hold memory pointers, not the actual data from the file.)
*/
int module_bytes = (sizeof (unsigned short) * 3) + (sizeof (char) * 2);
if (entries == NULL)
return FALSE;
/* Find out how many sstModule sub-sections we have in 'entries'
*/
*module_count = 0;
for (i = 0; i < entrynum; i++)
{
if (entries[i].SubSection == sstModule)
(*module_count)++;
}
/* Need a double-pointer so we can change the destination of the pointer
* and return the new allocation back to the caller.
*/
*modules = calloc (*module_count, sizeof (OMFModuleFull));
for (i = 0; i < *module_count; i++)
{
/* Convenience pointer to current module
*/
module = &(*modules)[i];
/* Must extract each OMFModuleFull separately from file, because the 'SegInfo'
* and 'Name' fields also require separate allocations; the data for these
* fields is interspersed in the file, between OMFModuleFull blocks.
*/
bytes_read = fread (module, sizeof (char), module_bytes, debugfile);
if (bytes_read < module_bytes)
{
printf ("ERROR while reading CodeView Module Sub-section Data: "
"Only able to read %d bytes from entry %d!\n",
bytes_read, i);
return FALSE;
}
/* Allocate space for, and grab the entire 'SegInfo' array.
*/
segnum = module->cSeg;
segarray = calloc (segnum, sizeof (OMFSegDesc));
bytes_read = fread (segarray, sizeof (OMFSegDesc), segnum, debugfile);
if (bytes_read < segnum)
{
printf ("ERROR while reading CodeView Module SegInfo Data: "
"Only able to read %d segments from module %d!\n",
bytes_read, i);
return FALSE;
}
module->SegInfo = segarray;
/* Allocate space for the (length-prefixed) 'Name' field.
*/
bytes_read = fread (&namelen, sizeof (char), 1, debugfile);
if (bytes_read < 1)
{
printf ("ERROR while reading CodeView Module Name length!\n");
return FALSE;
}
/* Read 'Name' field from file. 'Name' must be aligned on a 4-byte
* boundary, so we must do a little extra math on the string length.
* (NOTE: Must include namelen byte in total padding length, too.)
*/
pad = ((namelen + 1) % 4);
if (pad)
namelen += (4 - pad);
module->Name = calloc (namelen + 1, sizeof (char));
bytes_read = fread (module->Name, sizeof (char), namelen, debugfile);
if (bytes_read < namelen)
{
printf ("ERROR while reading CodeView Module Name: "
"Only able to read %d chars from module %d!\n",
bytes_read, i);
return FALSE;
}
/* printf ("%s\n", module->Name); */
}
#ifdef VERBOSE
printf ("Done reading %d modules\n", *module_count);
#endif
return TRUE;
}

View File

@ -1,2 +0,0 @@
Makefile
specmaker

View File

@ -1,264 +0,0 @@
/*
* DLL symbol extraction
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
/* DOS/PE Header details */
#define DOS_HEADER_LEN 64
#define DOS_MAGIC 0x5a4d
#define DOS_PE_OFFSET 60
#define PE_HEADER_LEN 248
#define PE_MAGIC 0x4550
#define PE_COUNT_OFFSET 6
#define PE_EXPORTS_OFFSET 120
#define PE_EXPORTS_SIZE PE_EXPORTS_OFFSET + 4
#define SECTION_HEADER_LEN 40
#define SECTION_ADDR_OFFSET 12
#define SECTION_ADDR_SIZE SECTION_ADDR_OFFSET + 4
#define SECTION_POS_OFFSET SECTION_ADDR_SIZE + 4
#define ORDINAL_BASE_OFFSET 16
#define ORDINAL_COUNT_OFFSET 20
#define ORDINAL_NAME_OFFSET ORDINAL_COUNT_OFFSET + 16
#define EXPORT_COUNT_OFFSET 24
#define EXPORT_NAME_OFFSET EXPORT_COUNT_OFFSET + 8
/* Minimum memory needed to read both headers into a buffer */
#define MIN_HEADER_LEN (PE_HEADER_LEN * sizeof (unsigned char))
/* Normalise a pointer in the exports section */
#define REBASE(x) ((x) - exports)
/* Module globals */
typedef struct _dll_symbol {
size_t ordinal;
char *symbol;
} dll_symbol;
static FILE *dll_file = NULL;
static dll_symbol *dll_symbols = NULL;
static size_t dll_num_exports = 0;
static size_t dll_num_ordinals = 0;
static int dll_ordinal_base = 0;
static dll_symbol *dll_current_symbol = NULL;
static unsigned int dll_current_export = 0;
/* Get a short from a memory block */
static inline size_t get_short (const char *mem)
{
return *((const unsigned char *)mem) +
(*((const unsigned char *)mem + 1) << 8);
}
/* Get an integer from a memory block */
static inline size_t get_int (const char *mem)
{
assert (sizeof (char) == (size_t)1);
return get_short (mem) + (get_short (mem + 2) << 16);
}
/* Compare symbols by ordinal for qsort */
static int symbol_cmp(const void *left, const void *right)
{
return ((dll_symbol *)left)->ordinal > ((dll_symbol *)right)->ordinal;
}
static void dll_close (void);
/*******************************************************************
* dll_open
*
* Open a DLL and read in exported symbols
*/
void dll_open (const char *dll_name)
{
size_t code = 0, code_len = 0, exports, exports_len, count, symbol_data;
size_t ordinal_data;
char *buff = NULL;
dll_file = open_file (dll_name, ".dll", "r");
atexit (dll_close);
/* Read in the required DOS and PE Headers */
if (!(buff = (char *) malloc (MIN_HEADER_LEN)))
fatal ("Out of memory");
if (fread (buff, DOS_HEADER_LEN, 1, dll_file) != 1 ||
get_short (buff) != DOS_MAGIC)
fatal ("Error reading DOS header");
if (fseek (dll_file, get_int (buff + DOS_PE_OFFSET), SEEK_SET) == -1)
fatal ("Error seeking PE header");
if (fread (buff, PE_HEADER_LEN, 1, dll_file) != 1 ||
get_int (buff) != PE_MAGIC)
fatal ("Error reading PE header");
exports = get_int (buff + PE_EXPORTS_OFFSET);
exports_len = get_int (buff + PE_EXPORTS_SIZE);
if (!exports || !exports_len)
fatal ("No exports in DLL");
if (!(count = get_short (buff + PE_COUNT_OFFSET)))
fatal ("No sections in DLL");
if (VERBOSE)
printf ("DLL has %d sections\n", count);
/* Iterate through sections until we find exports */
while (count--)
{
if (fread (buff, SECTION_HEADER_LEN, 1, dll_file) != 1)
fatal ("Section read error");
code = get_int (buff + SECTION_ADDR_OFFSET);
code_len = get_int (buff + SECTION_ADDR_SIZE);
if (code <= exports && code + code_len > exports)
break;
}
if (!count)
fatal ("No export section");
code_len -= (exports - code);
if (code_len < exports_len)
fatal ("Corrupt exports");
/* Load exports section */
if (fseek (dll_file, get_int (buff + SECTION_POS_OFFSET)
+ exports - code, SEEK_SET) == -1)
fatal ("Export section seek error");
if (VERBOSE)
printf ("Export data size = %d bytes\n", code_len);
if (!(buff = (char *) realloc (buff, code_len)))
fatal ("Out of memory");
if (fread (buff, code_len, 1, dll_file) != 1)
fatal ("Read error");
dll_close();
/* Locate symbol names/ordinals */
symbol_data = REBASE( get_int (buff + EXPORT_NAME_OFFSET));
ordinal_data = REBASE( get_int (buff + ORDINAL_NAME_OFFSET));
if (symbol_data > code_len)
fatal ("Corrupt exports section");
if (!(dll_num_ordinals = get_int (buff + ORDINAL_COUNT_OFFSET)))
fatal ("No ordinal count");
if (!(dll_num_exports = get_int (buff + EXPORT_COUNT_OFFSET)))
fatal ("No export count");
if (!(dll_symbols = (dll_symbol *) malloc ((dll_num_exports + 1) * sizeof (dll_symbol))))
fatal ("Out of memory");
dll_ordinal_base = get_int (buff + ORDINAL_BASE_OFFSET);
if (dll_num_exports != dll_num_ordinals || dll_ordinal_base > 1)
globals.do_ordinals = 1;
/* Read symbol names into 'dll_symbols' */
count = 0;
while (count < dll_num_exports)
{
const int symbol_offset = get_int (buff + symbol_data + count * 4);
const char *symbol_name_ptr = REBASE (buff + symbol_offset);
const int ordinal_offset = get_short (buff + ordinal_data + count * 2);
assert(symbol_name_ptr);
dll_symbols[count].symbol = strdup (symbol_name_ptr);
assert(dll_symbols[count].symbol);
dll_symbols[count].ordinal = ordinal_offset + dll_ordinal_base;
count++;
}
if (NORMAL)
printf ("%d named symbols in DLL, %d total\n", dll_num_exports, dll_num_ordinals);
free (buff);
qsort( dll_symbols, dll_num_exports, sizeof(dll_symbol), symbol_cmp );
dll_symbols[dll_num_exports].symbol = NULL;
dll_current_symbol = dll_symbols;
dll_current_export = dll_ordinal_base;
/* Set DLL output names */
if ((buff = strrchr (globals.input_name, '/')))
globals.input_name = buff + 1; /* Strip path */
OUTPUT_UC_DLL_NAME = str_toupper( strdup (OUTPUT_DLL_NAME));
}
/*******************************************************************
* dll_next_symbol
*
* Get next exported symbol from dll
*/
int dll_next_symbol (parsed_symbol * sym)
{
char ordinal_text[256];
if (dll_current_export > dll_num_ordinals)
return 1;
assert (dll_symbols);
if (!dll_current_symbol->symbol || dll_current_export < dll_current_symbol->ordinal)
{
assert(globals.do_ordinals);
/* Ordinal only entry */
snprintf (ordinal_text, sizeof(ordinal_text), "%s_%d",
globals.forward_dll ? globals.forward_dll : OUTPUT_UC_DLL_NAME,
dll_current_export);
str_toupper(ordinal_text);
sym->symbol = strdup (ordinal_text);
}
else
{
sym->symbol = strdup (dll_current_symbol->symbol);
dll_current_symbol++;
}
sym->ordinal = dll_current_export;
dll_current_export++;
return 0;
}
/*******************************************************************
* dll_close
*
* Free resources used by DLL
*/
static void dll_close (void)
{
size_t i;
if (dll_file)
{
fclose (dll_file);
dll_file = NULL;
}
if (dll_symbols)
{
for (i = 0; i < dll_num_exports; i++)
if (dll_symbols [i].symbol)
free (dll_symbols [i].symbol);
free (dll_symbols);
dll_symbols = NULL;
}
}

View File

@ -1,281 +0,0 @@
/*
* Option processing and main()
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
_globals globals; /* All global variables */
static void do_include (const char *arg)
{
globals.directory = arg;
globals.do_code = 1;
}
static inline const char* strip_ext (const char *str)
{
char *ext = strstr(str, ".dll");
if (ext)
return str_substring (str, ext);
else
return strdup (str);
}
static void do_name (const char *arg)
{
globals.dll_name = strip_ext (arg);
}
static void do_input (const char *arg)
{
globals.input_name = strip_ext (arg);
}
static void do_demangle (const char *arg)
{
globals.do_demangle = 1;
globals.do_code = 1;
globals.input_name = arg;
}
static void do_code (void)
{
globals.do_code = 1;
}
static void do_trace (void)
{
globals.do_trace = 1;
globals.do_code = 1;
}
static void do_forward (const char *arg)
{
globals.forward_dll = arg;
globals.do_trace = 1;
globals.do_code = 1;
}
static void do_document (void)
{
globals.do_documentation = 1;
}
static void do_cdecl (void)
{
globals.do_cdecl = 1;
}
static void do_quiet (void)
{
globals.do_quiet = 1;
}
static void do_start (const char *arg)
{
globals.start_ordinal = atoi (arg);
if (!globals.start_ordinal)
fatal ("Invalid -s option (must be numeric)");
}
static void do_end (const char *arg)
{
globals.end_ordinal = atoi (arg);
if (!globals.end_ordinal)
fatal ("Invalid -e option (must be numeric)");
}
static void do_verbose (void)
{
globals.do_verbose = 1;
}
struct option
{
const char *name;
int has_arg;
void (*func) ();
const char *usage;
};
static const struct option option_table[] = {
{"-d", 1, do_input, "-d dll Use dll for input file (mandatory)"},
{"-S", 1, do_demangle, "-S sym Demangle C++ symbol 'sym' and exit"},
{"-h", 0, do_usage, "-h Display this help message"},
{"-I", 1, do_include, "-I dir Look for prototypes in 'dir' (implies -c)"},
{"-o", 1, do_name, "-o name Set the output dll name (default: dll)"},
{"-c", 0, do_code, "-c Generate skeleton code (requires -I)"},
{"-t", 0, do_trace, "-t TRACE arguments (implies -c)"},
{"-f", 1, do_forward, "-f dll Forward calls to 'dll' (implies -t)"},
{"-D", 0, do_document, "-D Generate documentation"},
{"-C", 0, do_cdecl, "-C Assume __cdecl calls (default: __stdcall)"},
{"-s", 1, do_start, "-s num Start prototype search after symbol 'num'"},
{"-e", 1, do_end, "-e num End prototype search after symbol 'num'"},
{"-q", 0, do_quiet, "-q Don't show progress (quiet)."},
{"-v", 0, do_verbose, "-v Show lots of detail while working (verbose)."},
{NULL, 0, NULL, NULL}
};
void do_usage (void)
{
const struct option *opt;
printf ("Usage: specmaker [options] [-d dll | -S sym]\n\nOptions:\n");
for (opt = option_table; opt->name; opt++)
printf (" %s\n", opt->usage);
puts ("\n");
exit (1);
}
/*******************************************************************
* parse_options
*
* Parse options from the argv array
*/
static void parse_options (char *argv[])
{
const struct option *opt;
char *const *ptr;
const char *arg = NULL;
ptr = argv + 1;
while (*ptr != NULL)
{
for (opt = option_table; opt->name; opt++)
{
if (opt->has_arg && !strncmp (*ptr, opt->name, strlen (opt->name)))
{
arg = *ptr + strlen (opt->name);
if (*arg == '\0')
{
ptr++;
arg = *ptr;
}
break;
}
if (!strcmp (*ptr, opt->name))
{
arg = NULL;
break;
}
}
if (!opt->name)
fatal ("Unrecognized option");
if (opt->has_arg && arg != NULL)
opt->func (arg);
else
opt->func ("");
ptr++;
}
if (!globals.do_demangle && globals.do_code && !globals.directory)
fatal ("-I must be used if generating code");
if (!globals.input_name)
fatal ("Option -d is mandatory");
if (VERBOSE && QUIET)
fatal ("Options -v and -q are mutually exclusive");
}
/*******************************************************************
* main
*/
#ifdef __GNUC__
int main (int argc __attribute__((unused)), char *argv[])
#else
int main (int argc, char *argv[])
#endif
{
parsed_symbol symbol;
int count = 0;
parse_options (argv);
memset (&symbol, 0, sizeof (parsed_symbol));
if (globals.do_demangle)
{
int result;
globals.uc_dll_name = "";
VERBOSE = 1;
symbol.symbol = strdup(globals.input_name);
result = symbol_demangle (&symbol);
if (symbol.flags & SYM_DATA)
printf (symbol.arg_text[0]);
else
output_prototype (stdout, &symbol);
fputc ('\n', stdout);
return result ? 1 : 0;
}
dll_open (globals.input_name);
output_spec_preamble ();
output_header_preamble ();
output_c_preamble ();
while (!dll_next_symbol (&symbol))
{
count++;
if (NORMAL)
printf ("Export %3d - '%s' ...%c", count, symbol.symbol,
VERBOSE ? '\n' : ' ');
if (globals.do_code && count >= globals.start_ordinal
&& (!globals.end_ordinal || count <= globals.end_ordinal))
{
/* Attempt to get information about the symbol */
int result = symbol_demangle (&symbol);
if (result)
result = symbol_search (&symbol);
if (!result && symbol.function_name)
/* Clean up the prototype */
symbol_clean_string (symbol.function_name);
if (NORMAL)
puts (result ? "[Not Found]" : "[OK]");
}
else if (NORMAL)
puts ("[Ignoring]");
output_spec_symbol (&symbol);
output_header_symbol (&symbol);
output_c_symbol (&symbol);
symbol_clear (&symbol);
}
output_makefile ();
output_install_script ();
if (VERBOSE)
puts ("Finished, Cleaning up...");
return 0;
}

View File

@ -1,2 +1,2 @@
Makefile
cvdump
winedump

View File

@ -4,15 +4,16 @@ TOPOBJDIR = ../..
SRCDIR = @srcdir@
VPATH = @srcdir@
PROGRAMS = specmaker
PROGRAMS = winedump
MODULE = none
C_SRCS = \
dll.c \
debug.c \
main.c \
misc.c \
msmangle.c \
output.c \
pe.c \
search.c \
symbol.c
@ -20,16 +21,16 @@ all: $(PROGRAMS)
@MAKE_RULES@
specmaker: $(OBJS)
$(CC) $(CFLAGS) -o specmaker $(OBJS) $(LDFLAGS)
winedump: $(OBJS)
$(CC) $(CFLAGS) -o winedump $(OBJS) $(LDFLAGS)
install:: $(PROGRAMS)
[ -d $(bindir) ] || $(MKDIR) $(bindir)
$(INSTALL_PROGRAM) specmaker $(bindir)/specmaker
$(INSTALL_PROGRAM) winedump $(bindir)/winedump
$(INSTALL_SCRIPT) $(SRCDIR)/function_grep.pl $(bindir)/function_grep.pl
uninstall::
$(RM) $(bindir)/specmaker
$(RM) $(bindir)/winedump
$(RM) $(bindir)/function_grep.pl
### Dependencies:

View File

@ -1,5 +1,5 @@
Specmaker - A Wine DLL tool
---------------------------
Winedump - A Wine DLL tool
--------------------------
Background
----------
@ -35,74 +35,61 @@ A: Reimplementing a Win32 DLL for use within Wine, or
B: Compiling a Win32 application with Winelib that uses x86 DLLs
Then you will need to create a .spec file (amongst other things). If you
won't be doing either of the above, then you won't need specmaker.
won't be doing either of the above, then you won't need winedump.
Creating a .spec file is a labour intensive task during which it is easy
to make a mistake. The idea of specmaker is to automate this task and create
to make a mistake. The idea of winedump is to automate this task and create
the majority of the support code needed for your DLL. In addition you can
have specmaker create code to help you reimplement a DLL, by providing
have winedump create code to help you reimplement a DLL, by providing
tracing of calls to the DLL, and (in some cases) automatically determining
the parameters, calling conventions, and return values of the DLLs functions.
You can think of specmaker as somewhat similar to the IMPLIB tool when
You can think of winedump as somewhat similar to the IMPLIB tool when
only its basic functionality is used.
Usage
-----
Specmaker is a command line tool. Running it with no arguments or passing
Winedump is a command line tool. Running it with no arguments or passing
it '-h' on the command line lists the available options:
Usage: specmaker [options] [-d dll | -S sym]
Options:
-d dll Use dll for input file (mandatory)
-S sym Demangle C++ symbol 'sym' and exit
-h Display this help message
-I dir Look for prototypes in 'dir' (implies -c)
-o name Set the output dll name (default: dll)
-c Generate skeleton code (requires -I)
-t TRACE arguments (implies -c)
-f dll Forward calls to 'dll' (implies -t)
-D Generate documentation
-C Assume __cdecl calls (default: __stdcall)
-s num Start prototype search after symbol 'num'
-e num End prototype search after symbol 'num'
-q Don't show progress (quiet).
-v Show lots of detail while working (verbose).
Winedump can be used for different usages:
- generating default source files (.spec, .c...) for using a native DLL in Wine
- demangling MSVC C++ symbol names
- dumping the 'PE' files contents
Usage: winedump [-h sym <sym> spec <dll> dump <dll>] [mode options]
When used in -h mode
-h Display this help message
When used in sym mode
sym <sym> Demangle C++ symbol <sym>' and exit
When used in spec mode
spec <dll> Use dll for input file and generate implementation code
-I dir Look for prototypes in 'dir' (implies -c)
-c Generate skeleton code (requires -I)
-t TRACE arguments (implies -c)
-f dll Forward calls to 'dll' (implies -t)
-D Generate documentation
-o name Set the output dll name (default: dll)
-C Assume __cdecl calls (default: __stdcall)
-s num Start prototype search after symbol 'num'
-e num End prototype search after symbol 'num'
-q Don't show progress (quiet).
-v Show lots of detail while working (verbose).
When used in dump mode
dump <dll> Dumps the content of the dll named <dll>
-C Turns on symbol demangling
-f Dumps file header information
-j sect_name Dumps only the content of section sect_name (import, export, debug)
-x Dumps everything
Basic options
-------------
OPTION: -S sym Demangle C++ symbol 'sym' and exit
The -S option is used to demangle a C++ symbol as it appears in the exports
section of a dll. This is useful for testing the demangler or implementing
C++ functions in partially implemented wine DLLS. As an example:
specmaker -S "??3@YAXPAX@Z"
Gives:
void __cdecl _global_operator_delete_1(void * arg0)
Which is enough information to begin implementing the function.
OPTION: -d dll Use dll for input file (mandatory)
The -d option tells specmaker which DLL you want to create a .spec file
for. You *must* give this option, unless you are demangling a single symbol
using the -S argument.
16 bit DLL's are not currently supported (Note that Winelib is intended
only for Win32 programs).
OPTION: -o name Set the output dll name (default: dll)
By default, if specmaker is run on DLL 'foo', it creates files called
By default, if winedump is run on DLL 'foo', it creates files called
'foo.spec', 'foo_main.c' etc, and prefixes any functions generated
with 'FOO_'. If '-o bar' is given, these will become 'bar.spec',
'bar_main.c' and 'BAR_' respectively.
@ -113,13 +100,13 @@ for more information.
OPTION: -q Don't show progress (quiet).
-v Show lots of detail while working (verbose).
There are 3 levels of output while specmaker is running. The default level,
There are 3 levels of output while winedump is running. The default level,
when neither -q or -v are given, prints the number of exported functions
found in the dll, followed by the name of each function as it is processed,
and a status indication of whether it was processed OK. With -v given, a
lot of information is dumped while specmaker works: this is intended to help
lot of information is dumped while winedump works: this is intended to help
debug any problems. Giving -q means nothing will be printed unless a fatal
error occurs, and could be used when calling specmaker from a script.
error occurs, and could be used when calling winedump from a script.
OPTION: -C Assume __cdecl calls (default: __stdcall)
@ -129,7 +116,7 @@ in the DLL. If specbuild cannot determine the convention, __stdcall is
used by default, unless this option has been given.
Unless -q is given, a warning will be printed for every function that
specmaker determines the calling convention for and which does not match
winedump determines the calling convention for and which does not match
the assumed calling convention.
@ -142,9 +129,9 @@ Winelib application to an x86 DLL, the above options are all you need.
As an example, lets assume the application you are porting uses functions
from a 3rd party dll called 'zipextra.dll', and the functions in the DLL
use the __stdcall calling convention. Copy zipextra.dll to an empty directory,
change to it, and run specmaker as follows:
change to it, and run winedump as follows:
specmaker -d zipextra (Note: this assumes specmaker is in your path)
winedump spec zipextra (Note: this assumes winedump is in your path)
The output will look something like the following:
@ -153,7 +140,7 @@ Export 1 - '_OpenZipFile' ... [Ignoring]
Export 2 - '_UnZipFile' ... [Ignoring]
...
"[Ignoring]" Just tells you that specmaker isn't trying to determine the
"[Ignoring]" Just tells you that winedump isn't trying to determine the
parameters or return types of the functions, its just creating stubs.
The following files are created:
@ -214,7 +201,7 @@ NOTE: **DO NOT** submit patches to Wine for 3rd party DLLs! Building DLLs
Advanced Options
----------------
This section discusses features of specmaker that are useful to Wine Hackers
This section discusses features of winedump that are useful to Wine Hackers
or developers looking to reimplement a Win32 DLL for Unix. Using these
features means you will need to be able to resolve compilation problems and
have a general understanding of Wine programming.
@ -222,7 +209,7 @@ have a general understanding of Wine programming.
OPTION: -I dir Look for prototypes in 'dir' (implies -c)
For all advanced functionality, you must give specmaker a directoryor file that
For all advanced functionality, you must give winedump a directoryor file that
contains prototypes for the DLL. In the case of Windows DLLs, this could be
either the standard include directory from your compiler, or an SDK include
directory. If you have a text document with prototypes (such as documentation)
@ -232,7 +219,7 @@ ensure that prototypes are parsed correctly.
The 'dir' argument can also be a file specification (e.g. "include/*"). If
it contains wildcards you must quote it to prevent the shell from expanding it.
If you have no prototypes, specify /dev/null for 'dir'. Specmaker may still
If you have no prototypes, specify /dev/null for 'dir'. Winedump may still
be able to generate some working stub code for you.
Once you have created your DLL, if you generated code (see below), you can
@ -241,11 +228,11 @@ should remove the DLLNAME_ prefix from the prototypes to make this work). This
allows you to add names to the function arguments, for example, so that the
comments and prototype in the regenerated DLL will be clearer.
Specmaker searches for prototypes using 'grep', and then retrieves each
Winedump searches for prototypes using 'grep', and then retrieves each
prototype by calling 'function_grep.pl', a Perl script. When you pass the -v
option on the command line, the calls to both of these programs are logged.
This allows you to see where each function definition has come from. Should
specmaker take an excessively long time to locate a prototype, you can check
winedump take an excessively long time to locate a prototype, you can check
that it is searching the right files; you may want to limit the number of files
searched if locating the prototype takes too long.
@ -256,7 +243,7 @@ You can compile function_grep.pl for a slight increase in performance; see
OPTION: -s num Start prototype search after symbol 'num'
-e num End prototype search after symbol 'num'
By passing the -s or -e options you can have specmaker try to generate code
By passing the -s or -e options you can have winedump try to generate code
for only some functions in your DLL. This may be used to generate a single
function, for example, if you wanted to add functionality to an existing DLL.
@ -265,19 +252,19 @@ They is also useful for debugging problems, in conjunction with -v.
OPTION: -D Generate documentation
By default, specmaker generates a standard comment at the header of each
function it generates. Passing this option makes specmaker output a full
By default, winedump generates a standard comment at the header of each
function it generates. Passing this option makes winedump output a full
header template for standard Wine documentation, listing the parameters
and return value of the function.
OPTION: -c Generate skeleton code (requires -I)
This option tells specmaker that you want to create function stubs for
This option tells winedump that you want to create function stubs for
each function in the DLL. This is the most basic level of code generation.
As specmaker reads each exported symbol from the source DLL, it first tries
As winedump reads each exported symbol from the source DLL, it first tries
to demangle the name. If the name is a C++ symbol, the arguments, class and
return value are all encoded into the symbol name. Specmaker converts this
return value are all encoded into the symbol name. Winedump converts this
information into a C function prototype. If this fails, the file(s) specified
in the -I argument are scanned for a function prototype. If one is found it
is used for the next step of the process, code generation.
@ -286,7 +273,7 @@ Note: C++ name demangling is currently under development. Since the algorithm
used is not documented, it must be decoded. Many simple prototypes are already
working however.
If specmaker does not find a prototype, it emits code like the following:
If winedump does not find a prototype, it emits code like the following:
In the .spec file:
@ -324,7 +311,7 @@ BOOL __stdcall ZIPEXTRA__OpenZipFile(LPCSTR pszFileName)
return 0;
}
Note that if the prototype does not contain argument names, specmaker will
Note that if the prototype does not contain argument names, winedump will
add them following the convention arg0, arg1 ... argN. If the function is
demangled C++, the first argument will be called '_this' if an implicit this
pointer is passed (i.e. the function is a non-static class member function).
@ -405,7 +392,7 @@ In the example I have been using, we probably should have used the -o option
to change the ouput name of our DLL to something else, and used the -f
option to forward to the real zipextra DLL:
specmaker -d zipextra -f zipextra -o myzipextra -I "~/zipextra/include/*h"
winedump spec zipextra -f zipextra -o myzipextra -I "~/zipextra/include/*h"
Then in the .spec file for our Winelib application, we add the line:
@ -427,7 +414,7 @@ when our code is the same do we need to remove the function pointer and the
call to the real DLL. A similar feature in wine is +relay debugging. Using a
fowarding DLL allows more granular reporting of arguments, because you can
write code to dump out the contents of types/structures rather than just
their address in memory. A future version of specmaker may generate this
their address in memory. A future version of winedump may generate this
code automatically for common Win32 types.
See below for more information on setting up a forwarding DLL.
@ -457,7 +444,7 @@ For third party (non C++) DLL's, the header(s) supplied with the DLL can
normally be added as an include to the generated DLL header. For other DLLs
I suggest creating a seperate header in the DLL directory and adding any
needed types to that. This allows you to rebuild the DLL at whim, for example
if a new version of specmaker brings increased functionality, then you
if a new version of winedump brings increased functionality, then you
only have to overwrite the generated files and re-include the header to take
advantage of it.
@ -468,10 +455,10 @@ about half were structures, so about 35 lines of code were needed. The only
change to the generated code was one line in the header to include the type
definitions.
To save some typing in case you don't have headers for your DLL type, specmaker
To save some typing in case you don't have headers for your DLL type, winedump
will dump dummy declarations for unknown classes and types it encounters,
if you use the -v option. These can be piped directly into a fix-up header
file for use in compiling your DLL. For example, if specmaker encounters the
file for use in compiling your DLL. For example, if winedump encounters the
(C++ ) symbol:
??0foobar@@QAE@ABV0@@Z (Which is a constructor for a foobar object)
@ -485,12 +472,12 @@ struct foobar { int _FIXME; };
The output should be piped through 'sort' and 'uniq' to remove multiple
declarations, e.g:
specmaker -d foo -c -I "inc/*.h" -v | grep FIXME | sort | uniq > fixup.h
winedump -d foo -c -I "inc/*.h" -v | grep FIXME | sort | uniq > fixup.h
By adding '#include "fixup.h"' to foobar_dll.h your compile errors will be
greatly reduced.
If specmaker encounters a type it doesnt know that is passed by value (as in
If winedump encounters a type it doesnt know that is passed by value (as in
the _cabs example above), it also prints a FIXME message like:
/* FIXME: By value type: Assumed 'int' */ typedef int ldiv_t;
@ -512,19 +499,19 @@ of reimplementing it).
First we create the forwarding DLL. We will rename the real msvcrt.dll on our
system to ms_msvcrt.dll, and our msvcrt implementation will call it:
specmaker -d msvcrt -C -f ms_msvcrt -I "inc/*.h"
winedump spec msvcrt -C -f ms_msvcrt -I "inc/*.h"
We then install this DLL into the Wine tree and add the types we need to
make it compile. Once the DLL compiles, we create a dummy ms_msvcrt DLL so
winebuild will resolve our forward calls to it (for the cases where specmaker
winebuild will resolve our forward calls to it (for the cases where winedump
couldn't generate code and has placed an '@forward' line in the .spec file):
specmaker -d msvcrt -C -o ms_msvcrt
winedump spec msvcrt -C -o ms_msvcrt
Install this DLL into the wine tree (since its a stub DLL, no changes are
needed to the code).
Now uncomment the line that specmaker inserted into msvcrt.spec:
Now uncomment the line that winedump inserted into msvcrt.spec:
#inport ms_msvcrt.dll
@ -556,7 +543,7 @@ on any aspect of the C++ mangling scheme, I would appreciate it. In particular
I want to know what _E and _G represent.
If you encounter a C++ symbol that doesn't demangle **AND** you have the
prototype for it, please send me the symbol as reported by specmaker and the
prototype for it, please send me the symbol as reported by winedump and the
prototype. The more examples I have the easier it is to decypher the scheme,
and generating them myself is very slow.
@ -575,3 +562,44 @@ References
[1] See the Wine and Wine.conf man pages for details on how to tell Wine
whether to use native (Win32) or internal DLLs.
Demangling
----------
If you need to demangle a single C++ symbol, you can use the demangling mode
of winedump. This is useful for testing the demangler or implementing
C++ functions in partially implemented wine DLLS. As an example:
winedump sym "??3@YAXPAX@Z"
Gives:
void __cdecl _global_operator_delete_1(void * arg0)
Which is enough information to begin implementing the function.
Dumping
-------
Another tool might be helpful digging into a 32bit DLL (and any PE image file):
pedump.
Usage:
-h Display this help message
-d <dll> Use dll for input file and generate implementation code
-C Turns on symbol demangling
-f Dumps file header information
-j dir_name Dumps only the content of directory dir_name (import, export, debug)
-x Dumps everything
The basic usage, to look everything in a file is:
winedump dump -d mydll.dll -x
It'll print any available information on the file. This information can be splitted
into sub-categories:
- file hedaers (request by -f or -x) are made of the standard PE header structures,
plus the COFF sections
- directories: you can print them one after the other using the -j switch. Currently,
only the import, export and debug directories are implemented.
- -x displays the file headers and any available directory.

View File

@ -24,16 +24,6 @@
#include "pshpack1.h"
/* FIXME: no idea if this is correct */
typedef struct
{
DWORD unknown1;
DWORD SizeOfData;
WORD unknown2;
WORD unknown3;
char Name[MAX_PATH];
} IMAGE_DEBUG_DIRECTORY_MISC;
/*
* CodeView headers
*/

464
tools/winedump/debug.c Normal file
View File

@ -0,0 +1,464 @@
/*
* Made after:
* CVDump - Parses through a Visual Studio .DBG file in CodeView 4 format
* and dumps the info to STDOUT in a human-readable format
*
* Copyright 2000 John R. Sheets
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include "winnt.h"
#include "winedump.h"
#include "pe.h"
#include "cvinclude.h"
/*
* .DBG File Layout:
*
* IMAGE_SEPARATE_DEBUG_HEADER
* IMAGE_SECTION_HEADER[]
* IMAGE_DEBUG_DIRECTORY[]
* OMFSignature
* debug data (typical example)
* - IMAGE_DEBUG_TYPE_MISC
* - IMAGE_DEBUG_TYPE_FPO
* - IMAGE_DEBUG_TYPE_CODEVIEW
* OMFDirHeader
* OMFDirEntry[]
*/
/*
* Descriptions:
*
* (hdr) IMAGE_SEPARATE_DEBUG_HEADER - .DBG-specific file header; holds info that
* applies to the file as a whole, including # of COFF sections, file offsets, etc.
* (hdr) IMAGE_SECTION_HEADER - list of COFF sections copied verbatim from .EXE;
* although this directory contains file offsets, these offsets are meaningless
* in the context of the .DBG file, because only the section headers are copied
* to the .DBG file...not the binary data it points to.
* (hdr) IMAGE_DEBUG_DIRECTORY - list of different formats of debug info contained in file
* (see IMAGE_DEBUG_TYPE_* descriptions below); tells where each section starts
* (hdr) OMFSignature (CV) - Contains "NBxx" signature, plus file offset telling how far
* into the IMAGE_DEBUG_TYPE_CODEVIEW section the OMFDirHeader and OMFDirEntry's sit
* (data) IMAGE_DEBUG_TYPE_MISC - usually holds name of original .EXE file
* (data) IMAGE_DEBUG_TYPE_FPO - Frame Pointer Optimization data; used for dealing with
* optimized stack frames (optional)
* (data) IMAGE_DEBUG_TYPE_CODEVIEW - *** THE GOOD STUFF ***
* This block of data contains all the symbol tables, line number info, etc.,
* that the Visual C++ debugger needs.
* (hdr) OMFDirHeader (CV) -
* (hdr) OMFDirEntry (CV) - list of subsections within CodeView debug data section
*/
/*
* The .DBG file typically has three arrays of directory entries, which tell
* the OS or debugger where in the file to look for the actual data
*
* IMAGE_SECTION_HEADER - number of entries determined by:
* (IMAGE_SEPARATE_DEBUG_HEADER.NumberOfSections)
*
* IMAGE_DEBUG_DIRECTORY - number of entries determined by:
* (IMAGE_SEPARATE_DEBUG_HEADER.DebugDirectorySize / sizeof (IMAGE_DEBUG_DIRECTORY))
*
* OMFDirEntry - number of entries determined by:
* (OMFDirHeader.cDir)
*/
static void* cv_base /* = 0 */;
static int dump_cv_sst_module(OMFDirEntry* omfde)
{
OMFModule* module;
OMFSegDesc* segDesc;
int i;
module = PRD(Offset(cv_base) + omfde->lfo, sizeof(OMFModule));
if (!module) {printf("Can't get the OMF-Module, aborting\n"); return FALSE;}
printf(" olvNumber: %u\n", module->ovlNumber);
printf(" iLib: %u\n", module->iLib);
printf(" cSeg: %u\n", module->cSeg);
printf(" Style: %c%c\n", module->Style[0], module->Style[1]);
printf(" Name: %.*s\n",
*(BYTE*)((char*)(module + 1) + sizeof(OMFSegDesc) * module->cSeg),
(char*)(module + 1) + sizeof(OMFSegDesc) * module->cSeg + 1);
segDesc = PRD(Offset(module + 1), sizeof(OMFSegDesc) * module->cSeg);
if (!segDesc) {printf("Can't get the OMF-SegDesc, aborting\n"); return FALSE;}
for (i = 0; i < module->cSeg; i++)
{
printf (" segment #%2d: offset = [0x%8lx], size = [0x%8lx]\n",
segDesc->Seg, segDesc->Off, segDesc->cbSeg);
segDesc++;
}
return TRUE;
}
static int dump_cv_sst_global_pub(OMFDirEntry* omfde)
{
long fileoffset;
OMFSymHash* header;
BYTE* symbols;
BYTE* curpos;
PUBSYM32* sym;
unsigned symlen;
int recordlen;
char nametmp[256];
fileoffset = Offset(cv_base) + omfde->lfo;
printf (" GlobalPub section starts at file offset 0x%lx\n", fileoffset);
printf (" Symbol table starts at 0x%lx\n", fileoffset + sizeof (OMFSymHash));
printf ("\n ----- Begin Symbol Table -----\n");
printf (" (type) (symbol name) (offset) (len) (seg) (ind)\n");
header = PRD(fileoffset, sizeof(OMFSymHash));
if (!header) {printf("Can't get OMF-SymHash, aborting\n");return FALSE;}
symbols = PRD(fileoffset + sizeof(OMFSymHash), header->cbSymbol);
if (!symbols) {printf("Can't OMF-SymHash details, aborting\n"); return FALSE;}
/* We don't know how many symbols are in this block of memory...only what
* the total size of the block is. Because the symbol's name is tacked
* on to the end of the PUBSYM32 struct, each symbol may take up a different
* # of bytes. This makes it harder to parse through the symbol table,
* since we won't know the exact location of the following symbol until we've
* already parsed the current one.
*/
for (curpos = symbols; curpos < symbols + header->cbSymbol; curpos += recordlen)
{
/* Point to the next PUBSYM32 in the table.
*/
sym = (PUBSYM32*)curpos;
if (sym->reclen < sizeof(PUBSYM32)) break;
symlen = sym->reclen - sizeof(PUBSYM32) + 1;
if (symlen > sizeof(nametmp)) {printf("\nsqueeze%d\n", symlen);symlen = sizeof(nametmp) - 1;}
memcpy(nametmp, curpos + sizeof (PUBSYM32) + 1, symlen);
nametmp[symlen] = '\0';
printf (" 0x%04x %-30.30s [0x%8lx] [0x%4x] %d %ld\n",
sym->rectyp, nametmp, sym->off, sym->reclen, sym->seg, sym->typind);
/* The entire record is null-padded to the nearest 4-byte
* boundary, so we must do a little extra math to keep things straight.
*/
recordlen = (sym->reclen + 3) & ~3;
}
return TRUE;
}
static int dump_cv_sst_global_sym(OMFDirEntry* omfde)
{
/*** NOT YET IMPLEMENTED ***/
return TRUE;
}
static int dump_cv_sst_static_sym(OMFDirEntry* omfde)
{
/*** NOT YET IMPLEMENTED ***/
return TRUE;
}
static int dump_cv_sst_libraries(OMFDirEntry* omfde)
{
/*** NOT YET IMPLEMENTED ***/
return TRUE;
}
static int dump_cv_sst_global_types(OMFDirEntry* omfde)
{
/*** NOT YET IMPLEMENTED ***/
return TRUE;
}
static int dump_cv_sst_seg_map(OMFDirEntry* omfde)
{
OMFSegMap* segMap;
OMFSegMapDesc* segMapDesc;
int i;
segMap = PRD(Offset(cv_base) + omfde->lfo, sizeof(OMFSegMap));
if (!segMap) {printf("Can't get SegMap, aborting\n");return FALSE;}
printf(" cSeg: %u\n", segMap->cSeg);
printf(" cSegLog: %u\n", segMap->cSegLog);
segMapDesc = PRD(Offset(segMap + 1), segMap->cSeg * sizeof(OMFSegDesc));
if (!segMapDesc) {printf("Can't get SegDescr array, aborting\n");return FALSE;}
for (i = 0; i < segMap->cSeg; i++)
{
printf(" SegDescr #%2d\n", i + 1);
printf(" flags: %04X\n", segMapDesc[i].flags);
printf(" ovl: %u\n", segMapDesc[i].ovl);
printf(" group: %u\n", segMapDesc[i].group);
printf(" frame: %u\n", segMapDesc[i].frame);
printf(" iSegName: %u\n", segMapDesc[i].iSegName);
printf(" iClassName: %u\n", segMapDesc[i].iClassName);
printf(" offset: %lu\n", segMapDesc[i].offset);
printf(" cbSeg: %lu\n", segMapDesc[i].cbSeg);
}
return TRUE;
}
static int dump_cv_sst_file_index(OMFDirEntry* omfde)
{
/*** NOT YET IMPLEMENTED ***/
return TRUE;
}
static int dump_cv_sst_src_module(OMFDirEntry* omfde)
{
int i, j;
BYTE* rawdata;
unsigned long* seg_info_dw;
unsigned short* seg_info_w;
unsigned ofs;
OMFSourceModule* sourceModule;
OMFSourceFile* sourceFile;
OMFSourceLine* sourceLine;
rawdata = PRD(Offset(cv_base) + omfde->lfo, omfde->cb);
if (!rawdata) {printf("Can't get srcModule subsection details, aborting\n");return FALSE;}
/* FIXME: check ptr validity */
sourceModule = (void*)rawdata;
printf (" Module table: Found %d file(s) and %d segment(s)\n",
sourceModule->cFile, sourceModule->cSeg);
for (i = 0; i < sourceModule->cFile; i++)
{
printf (" File #%2d begins at an offset of 0x%lx in this section\n",
i + 1, sourceModule->baseSrcFile[i]);
}
/* FIXME: check ptr validity */
seg_info_dw = (void*)((char*)(sourceModule + 1) +
sizeof(unsigned long) * (sourceModule->cFile - 1));
seg_info_w = (unsigned short*)(&seg_info_dw[sourceModule->cSeg * 2]);
for (i = 0; i < sourceModule->cSeg; i++)
{
printf (" Segment #%2d start = 0x%lx, end = 0x%lx, seg index = %u\n",
i + 1, seg_info_dw[i * 2], seg_info_dw[(i * 2) + 1],
seg_info_w[i]);
}
ofs = sizeof(OMFSourceModule) + sizeof(unsigned long) * (sourceModule->cFile - 1) +
sourceModule->cSeg * (2 * sizeof(unsigned long) + sizeof(unsigned short));
ofs = (ofs + 3) & ~3;
/* the OMFSourceFile is quite unpleasant to use:
* we have first:
* unsigned short number of segments
* unsigned short reservered
* unsigned long baseSrcLn[# segments]
* unsigned long offset[2 * #segments]
* odd indices are start offsets
* even indices are end offsets
* unsigned char string length for file name
* char file name (length is previous field)
*/
/* FIXME: check ptr validity */
sourceFile = (void*)(rawdata + ofs);
seg_info_dw = (void*)((char*)sourceFile + 2 * sizeof(unsigned short) +
sourceFile->cSeg * sizeof(unsigned long));
ofs += 2 * sizeof(unsigned short) + 3 * sourceFile->cSeg * sizeof(unsigned long);
printf(" File table: %.*s\n",
*(BYTE*)((char*)sourceModule + ofs), (char*)sourceModule + ofs + 1);
for (i = 0; i < sourceFile->cSeg; i++)
{
printf (" Segment #%2d start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
i + 1, seg_info_dw[i * 2], seg_info_dw[(i * 2) + 1], sourceFile->baseSrcLn[i]);
}
/* add file name length */
ofs += *(BYTE*)((char*)sourceModule + ofs) + 1;
ofs = (ofs + 3) & ~3;
for (i = 0; i < sourceModule->cSeg; i++)
{
sourceLine = (void*)(rawdata + ofs);
seg_info_dw = (void*)((char*)sourceLine + 2 * sizeof(unsigned short));
seg_info_w = (void*)(&seg_info_dw[sourceLine->cLnOff]);
printf (" Line table #%2d: Found %d line numbers for segment index %d\n",
i, sourceLine->cLnOff, sourceLine->Seg);
for (j = 0; j < sourceLine->cLnOff; j++)
{
printf (" Pair #%2d: offset = [0x%8lx], linenumber = %d\n",
j + 1, seg_info_dw[j], seg_info_w[j]);
}
ofs += 2 * sizeof(unsigned short) +
sourceLine->cLnOff * (sizeof(unsigned long) + sizeof(unsigned short));
ofs = (ofs + 3) & ~3;
}
return TRUE;
}
static int dump_cv_sst_align_sym(OMFDirEntry* omfde)
{
/*** NOT YET IMPLEMENTED ***/
return TRUE;
}
static void dump_codeview_all_modules(OMFDirHeader *omfdh)
{
int i;
OMFDirEntry *dirEntry;
const char* str;
if (!omfdh || !omfdh->cDir) return;
dirEntry = PRD(Offset(omfdh + 1), omfdh->cDir * sizeof(OMFDirEntry));
if (!dirEntry) {printf("Can't read DirEntry array, aborting\n"); return;}
for (i = 0; i < omfdh->cDir; i++)
{
switch (dirEntry[i].SubSection)
{
case sstModule: str = "sstModule"; break;
case sstAlignSym: str = "sstAlignSym"; break;
case sstSrcModule: str = "sstSrcModule"; break;
case sstLibraries: str = "sstLibraries"; break;
case sstGlobalSym: str = "sstGlobalSym"; break;
case sstGlobalPub: str = "sstGlobalPub"; break;
case sstGlobalTypes: str = "sstGlobalTypes"; break;
case sstSegMap: str = "sstSegMap"; break;
case sstFileIndex: str = "sstFileIndex"; break;
case sstStaticSym: str = "sstStaticSym"; break;
default: str = "<undefined>"; break;
}
printf("Module #%2d (%p)\n", i + 1, &dirEntry[i]);
printf(" SubSection: %04X (%s)\n", dirEntry[i].SubSection, str);
printf(" iMod: %d\n", dirEntry[i].iMod);
printf(" lfo: %ld\n", dirEntry[i].lfo);
printf(" cb: %lu\n", dirEntry[i].cb);
switch (dirEntry[i].SubSection)
{
case sstModule: dump_cv_sst_module(&dirEntry[i]); break;
case sstAlignSym: dump_cv_sst_align_sym(&dirEntry[i]); break;
case sstSrcModule: dump_cv_sst_src_module(&dirEntry[i]); break;
case sstLibraries: dump_cv_sst_libraries(&dirEntry[i]); break;
case sstGlobalSym: dump_cv_sst_global_sym(&dirEntry[i]); break;
case sstGlobalPub: dump_cv_sst_global_pub(&dirEntry[i]); break;
case sstGlobalTypes: dump_cv_sst_global_types(&dirEntry[i]); break;
case sstSegMap: dump_cv_sst_seg_map(&dirEntry[i]); break;
case sstFileIndex: dump_cv_sst_file_index(&dirEntry[i]); break;
case sstStaticSym: dump_cv_sst_static_sym(&dirEntry[i]); break;
default: printf("unsupported type %x\n", dirEntry[i].SubSection); break;
}
printf("\n");
}
return;
}
static void dump_codeview_headers(unsigned long base, unsigned long len)
{
OMFDirHeader *dirHeader;
OMFSignature *signature;
OMFDirEntry *dirEntry;
int i;
int modulecount = 0, alignsymcount = 0, srcmodulecount = 0, librariescount = 0;
int globalsymcount = 0, globalpubcount = 0, globaltypescount = 0;
int segmapcount = 0, fileindexcount = 0, staticsymcount = 0;
cv_base = PRD(base, len);
if (!cv_base) {printf("Can't get full debug content, aborting\n");return;}
signature = cv_base;
printf(" CodeView Data\n");
printf(" Signature: %.4s\n", signature->Signature);
printf(" Filepos: 0x%08lX\n", signature->filepos);
if (memcmp(signature->Signature, "NB10", 4) == 0)
{
struct {DWORD TimeStamp; DWORD Dunno; char Name[1];}* pdb_data;
pdb_data = (void*)(signature + 1);
printf(" TimeStamp: %08lX (%s)\n",
pdb_data->TimeStamp, get_time_str(pdb_data->TimeStamp));
printf(" Dunno: %08lX\n", pdb_data->Dunno);
printf(" Filename: %s\n", pdb_data->Name);
return;
}
if (memcmp(signature->Signature, "NB09", 4) != 0 && memcmp(signature->Signature, "NB11", 4) != 0)
{
printf("Unsupported signature, aborting\n");
return;
}
dirHeader = PRD(Offset(cv_base) + signature->filepos, sizeof(OMFDirHeader));
if (!dirHeader) {printf("Can't get debug header, aborting\n"); return;}
printf(" Size of header: 0x%4X\n", dirHeader->cbDirHeader);
printf(" Size per entry: 0x%4X\n", dirHeader->cbDirEntry);
printf(" # of entries: 0x%8lX (%ld)\n", dirHeader->cDir, dirHeader->cDir);
printf(" Offset to NextDir: 0x%8lX\n", dirHeader->lfoNextDir);
printf(" Flags: 0x%8lX\n", dirHeader->flags);
if (!dirHeader->cDir) return;
dirEntry = PRD(Offset(dirHeader + 1), sizeof(OMFDirEntry) * dirHeader->cDir);
if (!dirEntry) {printf("Can't get DirEntry array, aborting\n");return;}
for (i = 0; i < dirHeader->cDir; i++)
{
switch (dirEntry[i].SubSection)
{
case sstModule: modulecount++; break;
case sstAlignSym: alignsymcount++; break;
case sstSrcModule: srcmodulecount++; break;
case sstLibraries: librariescount++; break;
case sstGlobalSym: globalsymcount++; break;
case sstGlobalPub: globalpubcount++; break;
case sstGlobalTypes: globaltypescount++; break;
case sstSegMap: segmapcount++; break;
case sstFileIndex: fileindexcount++; break;
case sstStaticSym: staticsymcount++; break;
}
}
/* This one has to be > 0
*/
printf ("\nFound: %d sstModule subsections\n", modulecount);
if (alignsymcount > 0) printf (" %d sstAlignSym subsections\n", alignsymcount);
if (srcmodulecount > 0) printf (" %d sstSrcModule subsections\n", srcmodulecount);
if (librariescount > 0) printf (" %d sstLibraries subsections\n", librariescount);
if (globalsymcount > 0) printf (" %d sstGlobalSym subsections\n", globalsymcount);
if (globalpubcount > 0) printf (" %d sstGlobalPub subsections\n", globalpubcount);
if (globaltypescount > 0) printf (" %d sstGlobalTypes subsections\n", globaltypescount);
if (segmapcount > 0) printf (" %d sstSegMap subsections\n", segmapcount);
if (fileindexcount > 0) printf (" %d sstFileIndex subsections\n", fileindexcount);
if (staticsymcount > 0) printf (" %d sstStaticSym subsections\n", staticsymcount);
dump_codeview_all_modules(dirHeader);
}
void dump_codeview(unsigned long base, unsigned long len)
{
dump_codeview_headers(base, len);
}

341
tools/winedump/main.c Normal file
View File

@ -0,0 +1,341 @@
/*
* Option processing and main()
*
* Copyright 2000 Jon Griffiths
*/
#include "winedump.h"
_globals globals; /* All global variables */
static void do_include (const char *arg)
{
globals.directory = arg;
globals.do_code = 1;
}
static inline const char* strip_ext (const char *str)
{
char *ext = strstr(str, ".dll");
if (ext)
return str_substring (str, ext);
else
return strdup (str);
}
static void do_name (const char *arg)
{
globals.dll_name = strip_ext (arg);
}
static void do_spec (const char *arg)
{
if (globals.mode != NONE) fatal("Only one mode can be specified\n");
globals.mode = SPEC;
globals.input_name = strip_ext (arg);
}
static void do_demangle (const char *arg)
{
if (globals.mode != NONE) fatal("Only one mode can be specified\n");
globals.mode = DMGL;
globals.do_code = 1;
globals.input_name = arg;
}
static void do_dump (const char *arg)
{
if (globals.mode != NONE) fatal("Only one mode can be specified\n");
globals.mode = DUMP;
globals.do_code = 1;
globals.input_name = arg;
}
static void do_code (void)
{
globals.do_code = 1;
}
static void do_trace (void)
{
globals.do_trace = 1;
globals.do_code = 1;
}
static void do_forward (const char *arg)
{
globals.forward_dll = arg;
globals.do_trace = 1;
globals.do_code = 1;
}
static void do_document (void)
{
globals.do_documentation = 1;
}
static void do_cdecl (void)
{
globals.do_cdecl = 1;
}
static void do_quiet (void)
{
globals.do_quiet = 1;
}
static void do_start (const char *arg)
{
globals.start_ordinal = atoi (arg);
if (!globals.start_ordinal)
fatal ("Invalid -s option (must be numeric)");
}
static void do_end (const char *arg)
{
globals.end_ordinal = atoi (arg);
if (!globals.end_ordinal)
fatal ("Invalid -e option (must be numeric)");
}
static void do_verbose (void)
{
globals.do_verbose = 1;
}
static void do_symdmngl (void)
{
globals.do_demangle = 1;
}
static void do_dumphead (void)
{
globals.do_dumpheader = 1;
}
static void do_dumpsect (const char* arg)
{
globals.dumpsect = arg;
}
static void do_dumpall(void)
{
globals.do_dumpheader = 1;
globals.dumpsect = "ALL";
}
struct option
{
const char *name;
Mode mode;
int has_arg;
void (*func) ();
const char *usage;
};
static const struct option option_table[] = {
{"-h", NONE, 0, do_usage, "-h Display this help message"},
{"sym", DMGL, 2, do_demangle, "sym <sym> Demangle C++ symbol <sym>' and exit"},
{"spec", SPEC, 2, do_spec, "spec <dll> Use dll for input file and generate implementation code"},
{"-I", SPEC, 1, do_include, "-I dir Look for prototypes in 'dir' (implies -c)"},
{"-c", SPEC, 0, do_code, "-c Generate skeleton code (requires -I)"},
{"-t", SPEC, 0, do_trace, "-t TRACE arguments (implies -c)"},
{"-f", SPEC, 1, do_forward, "-f dll Forward calls to 'dll' (implies -t)"},
{"-D", SPEC, 0, do_document, "-D Generate documentation"},
{"-o", SPEC, 1, do_name, "-o name Set the output dll name (default: dll)"},
{"-C", SPEC, 0, do_cdecl, "-C Assume __cdecl calls (default: __stdcall)"},
{"-s", SPEC, 1, do_start, "-s num Start prototype search after symbol 'num'"},
{"-e", SPEC, 1, do_end, "-e num End prototype search after symbol 'num'"},
{"-q", SPEC, 0, do_quiet, "-q Don't show progress (quiet)."},
{"-v", SPEC, 0, do_verbose, "-v Show lots of detail while working (verbose)."},
{"dump", DUMP, 2, do_dump, "dump <dll> Dumps the content of the dll named <dll>"},
{"-C", DUMP, 0, do_symdmngl, "-C Turns on symbol demangling"},
{"-f", DUMP, 0, do_dumphead, "-f Dumps file header information"},
{"-j", DUMP, 1, do_dumpsect, "-j sect_name Dumps only the content of section sect_name (import, export, debug)"},
{"-x", DUMP, 0, do_dumpall, "-x Dumps everything"},
{NULL, NONE, 0, NULL, NULL}
};
void do_usage (void)
{
const struct option *opt;
printf ("Usage: winedump [-h sym <sym> spec <dll> dump <dll>] [mode options]\n");
printf ("When used in -h mode\n");
for (opt = option_table; opt->name; opt++)
if (opt->mode == NONE)
printf (" %s\n", opt->usage);
printf ("When used in sym mode\n");
for (opt = option_table; opt->name; opt++)
if (opt->mode == DMGL)
printf (" %s\n", opt->usage);
printf ("When used in spec mode\n");
for (opt = option_table; opt->name; opt++)
if (opt->mode == SPEC)
printf (" %s\n", opt->usage);
printf ("When used in dump mode\n");
for (opt = option_table; opt->name; opt++)
if (opt->mode == DUMP)
printf (" %s\n", opt->usage);
puts ("\n");
exit (1);
}
/*******************************************************************
* parse_options
*
* Parse options from the argv array
*/
static void parse_options (char *argv[])
{
const struct option *opt;
char *const *ptr;
const char *arg = NULL;
ptr = argv + 1;
while (*ptr != NULL)
{
for (opt = option_table; opt->name; opt++)
{
if (globals.mode != NONE && opt->mode != NONE && globals.mode != opt->mode)
continue;
if (((opt->has_arg == 1) && !strncmp (*ptr, opt->name, strlen (opt->name))) ||
((opt->has_arg == 2) && !strcmp (*ptr, opt->name)))
{
arg = *ptr + strlen (opt->name);
if (*arg == '\0') arg = *++ptr;
break;
}
if (!strcmp (*ptr, opt->name))
{
arg = NULL;
break;
}
}
if (!opt->name)
fatal ("Unrecognized option");
if (opt->has_arg && arg != NULL)
opt->func (arg);
else
opt->func ("");
ptr++;
}
if (globals.mode == SPEC && globals.do_code && !globals.directory)
fatal ("-I must be used if generating code");
if (VERBOSE && QUIET)
fatal ("Options -v and -q are mutually exclusive");
}
/*******************************************************************
* main
*/
#ifdef __GNUC__
int main (int argc __attribute__((unused)), char *argv[])
#else
int main (int argc, char *argv[])
#endif
{
parsed_symbol symbol;
int count = 0;
int result;
globals.mode = NONE;
parse_options (argv);
memset (&symbol, 0, sizeof (parsed_symbol));
switch (globals.mode)
{
case DMGL:
globals.uc_dll_name = "";
VERBOSE = 1;
symbol.symbol = strdup(globals.input_name);
result = symbol_demangle (&symbol);
if (symbol.flags & SYM_DATA)
printf (symbol.arg_text[0]);
else
output_prototype (stdout, &symbol);
fputc ('\n', stdout);
return result ? 1 : 0;
break;
case SPEC:
dll_open (globals.input_name);
output_spec_preamble ();
output_header_preamble ();
output_c_preamble ();
while (!dll_next_symbol (&symbol))
{
count++;
if (NORMAL)
printf ("Export %3d - '%s' ...%c", count, symbol.symbol,
VERBOSE ? '\n' : ' ');
if (globals.do_code && count >= globals.start_ordinal
&& (!globals.end_ordinal || count <= globals.end_ordinal))
{
/* Attempt to get information about the symbol */
int result = symbol_demangle (&symbol);
if (result)
result = symbol_search (&symbol);
if (!result && symbol.function_name)
/* Clean up the prototype */
symbol_clean_string (symbol.function_name);
if (NORMAL)
puts (result ? "[Not Found]" : "[OK]");
}
else if (NORMAL)
puts ("[Ignoring]");
output_spec_symbol (&symbol);
output_header_symbol (&symbol);
output_c_symbol (&symbol);
symbol_clear (&symbol);
}
output_makefile ();
output_install_script ();
if (VERBOSE)
puts ("Finished, Cleaning up...");
break;
case NONE:
do_usage();
break;
case DUMP:
globals.uc_dll_name = "";
dump_file(globals.input_name);
break;
}
return 0;
}

View File

@ -3,7 +3,7 @@
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
#include "winedump.h"
/*******************************************************************

View File

@ -3,7 +3,7 @@
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
#include "winedump.h"
/* Type for parsing mangled types */
typedef struct _compound_type

View File

@ -3,7 +3,7 @@
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
#include "winedump.h"
/* Output files */
static FILE *specfile = NULL;
@ -33,7 +33,7 @@ void output_spec_preamble (void)
puts ("Creating .spec preamble");
fprintf (specfile,
"# Generated from %s.dll by specmaker\nname %s\n"
"# Generated from %s.dll by winedump\nname %s\n"
"type win32\ninit %s_Init\n\nimport kernel32.dll\n"
"import ntdll.dll\n", globals.input_name, OUTPUT_DLL_NAME,
OUTPUT_UC_DLL_NAME);
@ -133,7 +133,7 @@ void output_header_preamble (void)
atexit (output_header_postamble);
fprintf (hfile,
"/*\n * %s.dll\n *\n * Generated from %s.dll by specmaker.\n *\n"
"/*\n * %s.dll\n *\n * Generated from %s.dll by winedump.\n *\n"
" * DO NOT SEND GENERATED DLLS FOR INCLUSION INTO WINE !\n * \n */"
"\n#ifndef __WINE_%s_DLL_H\n#define __WINE_%s_DLL_H\n\n#include "
"\"config.h\"\n#include \"windef.h\"\n#include \"debugtools.h\"\n"
@ -199,7 +199,7 @@ void output_c_preamble (void)
atexit (output_c_postamble);
fprintf (cfile,
"/*\n * %s.dll\n *\n * Generated from %s.dll by specmaker.\n *\n"
"/*\n * %s.dll\n *\n * Generated from %s.dll by winedump.\n *\n"
" * DO NOT SUBMIT GENERATED DLLS FOR INCLUSION INTO WINE!\n * \n */"
"\n\n#include \"%s_dll.h\"\n\nDEFAULT_DEBUG_CHANNEL(%s);\n\n",
OUTPUT_DLL_NAME, globals.input_name, OUTPUT_DLL_NAME,
@ -419,7 +419,7 @@ void output_makefile (void)
puts ("Creating makefile");
fprintf (makefile,
"# Generated from %s.dll by specmaker.\nTOPSRCDIR = @top_srcdir@\n"
"# Generated from %s.dll by winedump.\nTOPSRCDIR = @top_srcdir@\n"
"TOPOBJDIR = ../..\nSRCDIR = @srcdir@\nVPATH = @srcdir@\n"
"MODULE = %s\nEXTRALIBS = $(LIBUNICODE)\n\n"
"LDDLLFLAGS = @LDDLLFLAGS@\nSYMBOLFILE = $(MODULE).tmp.o\n\n"
@ -447,7 +447,7 @@ void output_install_script (void)
puts ("Creating install script");
fprintf (install_file,
"#!/bin/bash\n# Generated from %s.dll by specmaker.\n\n"
"#!/bin/bash\n# Generated from %s.dll by winedump.\n\n"
"if [ $# -ne 1 ] || [ ! -d $1 ] || [ ! -f"
" $1/AUTHORS ]; then\n\t[ $# -eq 1 ] && echo \"Invalid path\"\n"
"\techo \"Usage: $0 wine-base-dir\"\n\texit 1\nfi\n\n"

847
tools/winedump/pe.c Normal file
View File

@ -0,0 +1,847 @@
/*
* PE dumping utility
*
* Copyright 2001 Eric Pouech
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include "winnt.h"
#include "winedump.h"
#include "pe.h"
static void* base;
static long total_len;
static IMAGE_NT_HEADERS* nt_headers;
enum FileSig {SIG_UNKNOWN, SIG_DOS, SIG_PE, SIG_DBG};
char* get_time_str(DWORD _t)
{
time_t t = (time_t)_t;
static char buf[128];
/* FIXME: I don't get the same values from MS' pedump running under Wine...
* I wonder if Wine isn't broken wrt to GMT settings...
*/
strncpy(buf, ctime(&t), sizeof(buf));
buf[sizeof(buf) - 1] = '\0';
if (buf[strlen(buf)-1] == '\n')
buf[strlen(buf)-1] = '\0';
return buf;
}
static const char* get_machine_str(DWORD mach)
{
switch (mach)
{
case IMAGE_FILE_MACHINE_UNKNOWN: return "Unknown";
case IMAGE_FILE_MACHINE_I860: return "i860";
case IMAGE_FILE_MACHINE_I386: return "i386";
case IMAGE_FILE_MACHINE_R3000: return "R3000";
case IMAGE_FILE_MACHINE_R4000: return "R4000";
case IMAGE_FILE_MACHINE_R10000: return "R10000";
case IMAGE_FILE_MACHINE_ALPHA: return "Alpha";
case IMAGE_FILE_MACHINE_POWERPC: return "PowerPC";
}
return "???";
}
void* PRD(unsigned long prd, unsigned long len)
{
return (prd + len > total_len) ? NULL : (char*)base + prd;
}
unsigned long Offset(void* ptr)
{
if (ptr < base) {printf("<<<<<ptr below\n");return 0;}
if (ptr >= base + total_len) {printf("<<<<<ptr above\n");return 0;}
return ptr - base;
}
void* RVA(unsigned long rva, unsigned long len)
{
IMAGE_SECTION_HEADER* sectHead;
int i;
sectHead = (IMAGE_SECTION_HEADER*)((char*)nt_headers + sizeof(DWORD) +
sizeof(IMAGE_FILE_HEADER) +
nt_headers->FileHeader.SizeOfOptionalHeader);
if (rva == 0) return NULL;
for (i = nt_headers->FileHeader.NumberOfSections - 1; i >= 0; i--)
{
if (sectHead[i].VirtualAddress <= rva &&
rva + len <= (DWORD)sectHead[i].VirtualAddress + sectHead[i].SizeOfRawData)
break;
}
if (i < 0)
{
printf("rva not found in any section (%lu)\n", rva);
return NULL;
}
/* return image import directory offset */
return PRD(sectHead[i].PointerToRawData + rva - sectHead[i].VirtualAddress, len);
}
static void* get_dir(unsigned idx)
{
if (idx >= nt_headers->OptionalHeader.NumberOfRvaAndSizes)
return NULL;
return RVA(nt_headers->OptionalHeader.DataDirectory[idx].VirtualAddress,
nt_headers->OptionalHeader.DataDirectory[idx].Size);
}
static const char* DirectoryNames[16] = {
"EXPORT", "IMPORT", "RESOURCE", "EXCEPTION",
"SECURITY", "BASERELOC", "DEBUG", "ARCHITECTURE",
"GLOBALPTR", "TLS", "LOAD_CONFIG", "Bound IAT",
"IAT", "Delay IAT", "COM Descript", ""
};
static void dump_pe_header(void)
{
char *str;
IMAGE_FILE_HEADER *fileHeader;
IMAGE_OPTIONAL_HEADER *optionalHeader;
int i;
printf("File Header\n");
fileHeader = &nt_headers->FileHeader;
printf(" Machine: %04X (%s)\n",
fileHeader->Machine, get_machine_str(fileHeader->Machine));
printf(" Number of Sections: %d\n", fileHeader->NumberOfSections);
printf(" TimeDateStamp: %08lX (%s) offset %ld\n",
fileHeader->TimeDateStamp, get_time_str(fileHeader->TimeDateStamp),
Offset(&(fileHeader->TimeDateStamp)));
printf(" PointerToSymbolTable: %08lX\n", fileHeader->PointerToSymbolTable);
printf(" NumberOfSymbols: %08lX\n", fileHeader->NumberOfSymbols);
printf(" SizeOfOptionalHeader: %04X\n", fileHeader->SizeOfOptionalHeader);
printf(" Characteristics: %04X\n", fileHeader->Characteristics);
#define X(f,s) if (fileHeader->Characteristics & f) printf(" %s\n", s)
X(IMAGE_FILE_RELOCS_STRIPPED, "RELOCS_STRIPPED");
X(IMAGE_FILE_EXECUTABLE_IMAGE, "EXECUTABLE_IMAGE");
X(IMAGE_FILE_LINE_NUMS_STRIPPED, "LINE_NUMS_STRIPPED");
X(IMAGE_FILE_LOCAL_SYMS_STRIPPED, "LOCAL_SYMS_STRIPPED");
X(IMAGE_FILE_16BIT_MACHINE, "16BIT_MACHINE");
X(IMAGE_FILE_BYTES_REVERSED_LO, "BYTES_REVERSED_LO");
X(IMAGE_FILE_32BIT_MACHINE, "32BIT_MACHINE");
X(IMAGE_FILE_DEBUG_STRIPPED, "DEBUG_STRIPPED");
X(IMAGE_FILE_SYSTEM, "SYSTEM");
X(IMAGE_FILE_DLL, "DLL");
X(IMAGE_FILE_BYTES_REVERSED_HI, "BYTES_REVERSED_HI");
#undef X
printf("\n");
/* hope we have the right size */
printf("Optional Header\n");
optionalHeader = &nt_headers->OptionalHeader;
printf(" Magic 0x%-4X %u\n",
optionalHeader->Magic, optionalHeader->Magic);
printf(" linker version %u.%02u\n",
optionalHeader->MajorLinkerVersion, optionalHeader->MinorLinkerVersion);
printf(" size of code 0x%-8lx %lu\n",
optionalHeader->SizeOfCode, optionalHeader->SizeOfCode);
printf(" size of initialized data 0x%-8lx %lu\n",
optionalHeader->SizeOfInitializedData, optionalHeader->SizeOfInitializedData);
printf(" size of uninitialized data 0x%-8lx %lu\n",
optionalHeader->SizeOfUninitializedData, optionalHeader->SizeOfUninitializedData);
printf(" entrypoint RVA 0x%-8lx %lu\n",
optionalHeader->AddressOfEntryPoint, optionalHeader->AddressOfEntryPoint);
printf(" base of code 0x%-8lx %lu\n",
optionalHeader->BaseOfCode, optionalHeader->BaseOfCode);
printf(" base of data 0x%-8lX %lu\n",
optionalHeader->BaseOfData, optionalHeader->BaseOfData);
printf(" image base 0x%-8lX %lu\n",
optionalHeader->ImageBase, optionalHeader->ImageBase);
printf(" section align 0x%-8lx %lu\n",
optionalHeader->SectionAlignment, optionalHeader->SectionAlignment);
printf(" file align 0x%-8lx %lu\n",
optionalHeader->FileAlignment, optionalHeader->FileAlignment);
printf(" required OS version %u.%02u\n",
optionalHeader->MajorOperatingSystemVersion, optionalHeader->MinorOperatingSystemVersion);
printf(" image version %u.%02u\n",
optionalHeader->MajorImageVersion, optionalHeader->MinorImageVersion);
printf(" subsystem version %u.%02u\n",
optionalHeader->MajorSubsystemVersion, optionalHeader->MinorSubsystemVersion);
printf(" Win32 Version 0x%lX\n", optionalHeader->Win32VersionValue);
printf(" size of image 0x%-8lx %lu\n",
optionalHeader->SizeOfImage, optionalHeader->SizeOfImage);
printf(" size of headers 0x%-8lx %lu\n",
optionalHeader->SizeOfHeaders, optionalHeader->SizeOfHeaders);
printf(" checksum 0x%lX\n", optionalHeader->CheckSum);
switch (optionalHeader->Subsystem)
{
default:
case IMAGE_SUBSYSTEM_UNKNOWN: str = "Unknown"; break;
case IMAGE_SUBSYSTEM_NATIVE: str = "Native"; break;
case IMAGE_SUBSYSTEM_WINDOWS_GUI: str = "Windows GUI"; break;
case IMAGE_SUBSYSTEM_WINDOWS_CUI: str = "Windows CUI"; break;
case IMAGE_SUBSYSTEM_OS2_CUI: str = "OS/2 CUI"; break;
case IMAGE_SUBSYSTEM_POSIX_CUI: str = "Posix CUI"; break;
}
printf(" Subsystem 0x%X (%s)\n", optionalHeader->Subsystem, str);
printf(" DLL flags 0x%X\n", optionalHeader->DllCharacteristics);
printf(" stack reserve size 0x%-8lx %lu\n",
optionalHeader->SizeOfStackReserve, optionalHeader->SizeOfStackReserve);
printf(" stack commit size 0x%-8lx %lu\n",
optionalHeader->SizeOfStackCommit, optionalHeader->SizeOfStackCommit);
printf(" heap reserve size 0x%-8lx %lu\n",
optionalHeader->SizeOfHeapReserve, optionalHeader->SizeOfHeapReserve);
printf(" heap commit size 0x%-8lx %lu\n",
optionalHeader->SizeOfHeapCommit, optionalHeader->SizeOfHeapCommit);
printf(" loader flags 0x%lX\n", optionalHeader->LoaderFlags);
printf(" RVAs & sizes 0x%lX\n", optionalHeader->NumberOfRvaAndSizes);
printf("\n");
printf("Data Directory\n");
printf("%ld\n", optionalHeader->NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY));
for (i = 0; i < optionalHeader->NumberOfRvaAndSizes && i < 16; i++)
{
printf(" %-12s rva: 0x%-8lX size: %8lu\n",
DirectoryNames[i], optionalHeader->DataDirectory[i].VirtualAddress,
optionalHeader->DataDirectory[i].Size);
}
printf("\n");
}
static void dump_sections(void* addr, unsigned num_sect)
{
IMAGE_SECTION_HEADER* sectHead = addr;
int i;
printf("Section Table\n");
for (i = 0; i < num_sect; i++, sectHead++)
{
printf(" %02d %-8s VirtSize: %-8lu VirtAddr: %-8lu 0x%08lx\n",
i + 1, sectHead->Name, sectHead->Misc.VirtualSize, sectHead->VirtualAddress,
sectHead->VirtualAddress);
printf(" raw data offs: %-8lu raw data size: %-8lu\n",
sectHead->PointerToRawData, sectHead->SizeOfRawData);
printf(" relocation offs: %-8lu relocations: %-8u\n",
sectHead->PointerToRelocations, sectHead->NumberOfRelocations);
printf(" line # offs: %-8lu line #'s: %-8u\n",
sectHead->PointerToLinenumbers, sectHead->NumberOfLinenumbers);
printf(" characteristics: 0x$%08lx\n", sectHead->Characteristics);
printf(" ");
#define X(b,s) if (sectHead->Characteristics & b) printf(s " ")
/* #define IMAGE_SCN_TYPE_REG 0x00000000 - Reserved */
/* #define IMAGE_SCN_TYPE_DSECT 0x00000001 - Reserved */
/* #define IMAGE_SCN_TYPE_NOLOAD 0x00000002 - Reserved */
/* #define IMAGE_SCN_TYPE_GROUP 0x00000004 - Reserved */
/* #define IMAGE_SCN_TYPE_NO_PAD 0x00000008 - Reserved */
/* #define IMAGE_SCN_TYPE_COPY 0x00000010 - Reserved */
X(IMAGE_SCN_CNT_CODE, "CODE");
X(IMAGE_SCN_CNT_INITIALIZED_DATA, "INITIALIZED_DATA");
X(IMAGE_SCN_CNT_UNINITIALIZED_DATA, "UNINITIALIZED_DATA");
X(IMAGE_SCN_LNK_OTHER, "LNK_OTHER");
X(IMAGE_SCN_LNK_INFO, "LNK_INFO");
/* #define IMAGE_SCN_TYPE_OVER 0x00000400 - Reserved */
X(IMAGE_SCN_LNK_REMOVE, "LNK_REMOVE");
X(IMAGE_SCN_LNK_COMDAT, "LNK_COMDAT");
/* 0x00002000 - Reserved */
/* #define IMAGE_SCN_MEM_PROTECTED 0x00004000 - Obsolete */
X(IMAGE_SCN_MEM_FARDATA, "MEM_FARDATA");
/* #define IMAGE_SCN_MEM_SYSHEAP 0x00010000 - Obsolete */
X(IMAGE_SCN_MEM_PURGEABLE, "MEM_PURGEABLE");
X(IMAGE_SCN_MEM_16BIT, "MEM_16BIT");
X(IMAGE_SCN_MEM_LOCKED, "MEM_LOCKED");
X(IMAGE_SCN_MEM_PRELOAD, "MEM_PRELOAD");
X(IMAGE_SCN_ALIGN_1BYTES, "ALIGN_1BYTES");
X(IMAGE_SCN_ALIGN_2BYTES, "ALIGN_2BYTES");
X(IMAGE_SCN_ALIGN_4BYTES, "ALIGN_4BYTES");
X(IMAGE_SCN_ALIGN_8BYTES, "ALIGN_8BYTES");
X(IMAGE_SCN_ALIGN_16BYTES, "ALIGN_16BYTES");
X(IMAGE_SCN_ALIGN_32BYTES, "ALIGN_32BYTES");
X(IMAGE_SCN_ALIGN_64BYTES, "ALIGN_64BYTES");
/* 0x00800000 - Unused */
X(IMAGE_SCN_LNK_NRELOC_OVFL, "LNK_NRELOC_OVFL");
X(IMAGE_SCN_MEM_DISCARDABLE, "MEM_DISCARDABLE");
X(IMAGE_SCN_MEM_NOT_CACHED, "MEM_NOT_CACHED");
X(IMAGE_SCN_MEM_NOT_PAGED, "MEM_NOT_PAGED");
X(IMAGE_SCN_MEM_SHARED, "MEM_SHARED");
X(IMAGE_SCN_MEM_EXECUTE, "MEM_EXECUTE");
X(IMAGE_SCN_MEM_READ, "MEM_READ");
X(IMAGE_SCN_MEM_WRITE, "MEM_WRITE");
#undef X
printf("\n\n");
}
printf("\n");
}
static void dump_dir_exported_functions(void)
{
IMAGE_EXPORT_DIRECTORY *exportDir = get_dir(IMAGE_FILE_EXPORT_DIRECTORY);
unsigned i, j;
DWORD* pFunc;
DWORD* pName;
WORD* pOrdl;
DWORD* map;
parsed_symbol symbol;
if (!exportDir) return;
printf("Exports table:\n");
printf("\n");
printf(" Name: %s\n", (char*)RVA(exportDir->Name, sizeof(DWORD)));
printf(" Characteristics: %08lx\n", exportDir->Characteristics);
printf(" TimeDateStamp: %08lX %s\n",
exportDir->TimeDateStamp, get_time_str(exportDir->TimeDateStamp));
printf(" Version: %u.%02u\n", exportDir->MajorVersion, exportDir->MinorVersion);
printf(" Ordinal base: %lu\n", exportDir->Base);
printf(" # of functions: %lu\n", exportDir->NumberOfFunctions);
printf(" # of Names: %lu\n", exportDir->NumberOfNames);
printf("Adresses of functions: %08lX\n", exportDir->AddressOfFunctions);
printf("Adresses of name ordinals: %08lX\n", exportDir->AddressOfNameOrdinals);
printf("Adresses of names: %08lX\n", exportDir->AddressOfNames);
printf("\n");
printf(" Entry Pt Ordn Name\n");
pFunc = RVA(exportDir->AddressOfFunctions, exportDir->NumberOfFunctions * sizeof(DWORD));
if (!pFunc) {printf("Can't grab functions' address table\n"); return;}
pName = RVA(exportDir->AddressOfNames, exportDir->NumberOfNames * sizeof(DWORD));
if (!pName) {printf("Can't grab functions' name table\n"); return;}
pOrdl = RVA(exportDir->AddressOfNameOrdinals, exportDir->NumberOfNames * sizeof(WORD));
if (!pOrdl) {printf("Can't grab functions' ordinal table\n"); return;}
/* bit map of used funcs */
map = calloc(((exportDir->NumberOfFunctions + 31) & ~31) / 32, sizeof(DWORD));
if (!map) fatal("no memory");
for (i = 0; i < exportDir->NumberOfNames; i++)
{
char* name;
map[*pOrdl / 32] |= 1 << (*pOrdl % 32);
name = (char*)RVA(*pName++, sizeof(DWORD));
if (name && globals.do_demangle)
{
symbol.symbol = strdup(name);
symbol_demangle (&symbol);
printf(" %08lX %4lu ", *pFunc++, exportDir->Base + *pOrdl++);
if (symbol.flags & SYM_DATA)
printf (symbol.arg_text[0]);
else
output_prototype(stdout, &symbol);
printf("\n");
}
else
{
printf(" %08lX %4lu %s\n", *pFunc++, exportDir->Base + *pOrdl++, name);
}
}
pFunc = RVA(exportDir->AddressOfFunctions, exportDir->NumberOfFunctions * sizeof(DWORD));
if (!pFunc) {printf("Can't grab functions' address table\n"); return;}
j = exportDir->NumberOfNames;
for (i = 0; i < exportDir->NumberOfFunctions; i++)
{
if (!(map[i / 32] & (1 << (i % 32))))
{
printf(" %08lX %4lu <by ordinal>\n", pFunc[j++], exportDir->Base + i);
}
}
free(map);
printf("\n");
}
static void dump_dir_imported_functions(void)
{
IMAGE_IMPORT_DESCRIPTOR *importDesc = get_dir(IMAGE_FILE_IMPORT_DIRECTORY);
unsigned nb_imp, i;
if (!importDesc) return;
nb_imp = nt_headers->OptionalHeader.DataDirectory[IMAGE_FILE_IMPORT_DIRECTORY].Size /
sizeof(*importDesc);
if (!nb_imp) return;
printf("Import Table size: %lu\n",
nt_headers->OptionalHeader.DataDirectory[IMAGE_FILE_IMPORT_DIRECTORY].Size);/* FIXME */
for (i = 0; i < nb_imp - 1; i++) /* the last descr is set as 0 as a sentinel */
{
IMAGE_THUNK_DATA* il;
IMAGE_IMPORT_BY_NAME* iibn;
if (!importDesc->Name ||
(importDesc->u.OriginalFirstThunk == NULL && importDesc->FirstThunk == NULL))
{
/* FIXME */
printf("<<<<<<<null entry\n");
break;
}
printf(" offset %lu %s\n", Offset(importDesc), (char*)RVA(importDesc->Name, sizeof(DWORD)));
printf(" Hint/Name Table: %08lX\n", (DWORD)importDesc->u.OriginalFirstThunk);
printf(" TimeDataStamp: %08lX (%s)\n",
importDesc->TimeDateStamp, get_time_str(importDesc->TimeDateStamp));
printf(" ForwarderChain: %08lX\n", importDesc->ForwarderChain);
printf(" First thunk RVA: %08lX (delta: %u 0x%x)\n",
(DWORD)importDesc->FirstThunk, -1, -1); /* FIXME */
printf(" Ordn Name\n");
il = (importDesc->u.OriginalFirstThunk != 0) ?
RVA((DWORD)importDesc->u.OriginalFirstThunk, sizeof(DWORD)) :
RVA((DWORD)importDesc->FirstThunk, sizeof(DWORD));
if (!il) {printf("Can't grab thunk data, going to next imported DLL\n"); continue;}
for (; il->u1.Ordinal; il++)
{
if (IMAGE_SNAP_BY_ORDINAL(il->u1.Ordinal))
{
printf(" %4lu <by ordinal>\n", IMAGE_ORDINAL(il->u1.Ordinal));
}
else
{
iibn = RVA((DWORD)il->u1.AddressOfData, sizeof(DWORD));
if (!il)
{
printf("Can't grab import by name info, skipping to next ordinal\n");
}
else
{
printf(" %4u %s %lx\n", iibn->Hint, iibn->Name, (DWORD)il->u1.AddressOfData);
}
}
}
printf("\n");
importDesc++;
}
printf("\n");
}
static void dump_dir_debug_dir(IMAGE_DEBUG_DIRECTORY* idd, int idx)
{
const char* str;
printf("Directory %02u\n", idx + 1);
printf(" Characteristics: %08lX\n", idd->Characteristics);
printf(" TimeDateStamp: %08lX %s\n",
idd->TimeDateStamp, get_time_str(idd->TimeDateStamp));
printf(" Version %u.%02u\n", idd->MajorVersion, idd->MinorVersion);
switch (idd->Type)
{
default:
case IMAGE_DEBUG_TYPE_UNKNOWN: str = "UNKNOWN"; break;
case IMAGE_DEBUG_TYPE_COFF: str = "COFF"; break;
case IMAGE_DEBUG_TYPE_CODEVIEW: str = "CODEVIEW"; break;
case IMAGE_DEBUG_TYPE_FPO: str = "FPO"; break;
case IMAGE_DEBUG_TYPE_MISC: str = "MISC"; break;
case IMAGE_DEBUG_TYPE_EXCEPTION: str = "EXCEPTION"; break;
case IMAGE_DEBUG_TYPE_FIXUP: str = "FIXUP"; break;
case IMAGE_DEBUG_TYPE_OMAP_TO_SRC: str = "OMAP_TO_SRC"; break;
case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC:str = "OMAP_FROM_SRC"; break;
case IMAGE_DEBUG_TYPE_BORLAND: str = "BORLAND"; break;
case IMAGE_DEBUG_TYPE_RESERVED10: str = "RESERVED10"; break;
}
printf(" Type: %lu (%s)\n", idd->Type, str);
printf(" SizeOfData: %lu\n", idd->SizeOfData);
printf(" AddressOfRawData: %08lX\n", idd->AddressOfRawData);
printf(" PointerToRawData: %08lX\n", idd->PointerToRawData);
switch (idd->Type)
{
case IMAGE_DEBUG_TYPE_UNKNOWN:
break;
case IMAGE_DEBUG_TYPE_COFF:
break;
case IMAGE_DEBUG_TYPE_CODEVIEW:
dump_codeview(idd->PointerToRawData, idd->SizeOfData);
break;
case IMAGE_DEBUG_TYPE_FPO:
break;
case IMAGE_DEBUG_TYPE_MISC:
{
IMAGE_DEBUG_MISC* misc = PRD(idd->PointerToRawData, idd->SizeOfData);
if (!misc) {printf("Can't get misc debug information\n"); break;}
printf(" DataType: %lu (%s)\n",
misc->DataType,
(misc->DataType == IMAGE_DEBUG_MISC_EXENAME) ? "Exe name" : "Unknown");
printf(" Length: %lu\n", misc->Length);
printf(" Unicode: %s\n", misc->Unicode ? "Yes" : "No");
printf(" Data: %s\n", misc->Data);
}
break;
case IMAGE_DEBUG_TYPE_EXCEPTION:
break;
case IMAGE_DEBUG_TYPE_FIXUP:
break;
case IMAGE_DEBUG_TYPE_OMAP_TO_SRC:
break;
case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC:
break;
case IMAGE_DEBUG_TYPE_BORLAND:
break;
case IMAGE_DEBUG_TYPE_RESERVED10:
break;
}
printf("\n");
}
static void dump_dir_debug(void)
{
IMAGE_DEBUG_DIRECTORY* debugDir = get_dir(IMAGE_FILE_DEBUG_DIRECTORY);
unsigned nb_dbg, i;
if (!debugDir) return;
nb_dbg = nt_headers->OptionalHeader.DataDirectory[IMAGE_FILE_DEBUG_DIRECTORY].Size /
sizeof(*debugDir);
if (!nb_dbg) return;
printf("Debug Table (%u directories)\n", nb_dbg);
for (i = 0; i < nb_dbg; i++)
{
dump_dir_debug_dir(debugDir, i);
debugDir++;
}
printf("\n");
}
static void dump_separate_dbg(void)
{
IMAGE_SEPARATE_DEBUG_HEADER*separateDebugHead = PRD(0, sizeof(separateDebugHead));
unsigned nb_dbg;
unsigned i;
IMAGE_DEBUG_DIRECTORY* debugDir;
if (!separateDebugHead) {printf("Can't grab the separate header, aborting\n"); return;}
printf ("Signature: %.2s (0x%4X)\n",
(char*)&separateDebugHead->Signature, separateDebugHead->Signature);
printf ("Flags: 0x%04X\n", separateDebugHead->Flags);
printf ("Machine: 0x%04X (%s)\n",
separateDebugHead->Machine, get_machine_str(separateDebugHead->Machine));
printf ("Characteristics: 0x%04X\n", separateDebugHead->Characteristics);
printf ("TimeDateStamp: 0x%08lX (%s)\n",
separateDebugHead->TimeDateStamp, get_time_str(separateDebugHead->TimeDateStamp));
printf ("CheckSum: 0x%08lX\n", separateDebugHead->CheckSum);
printf ("ImageBase: 0x%08lX\n", separateDebugHead->ImageBase);
printf ("SizeOfImage: 0x%08lX\n", separateDebugHead->SizeOfImage);
printf ("NumberOfSections: 0x%08lX\n", separateDebugHead->NumberOfSections);
printf ("ExportedNamesSize: 0x%08lX\n", separateDebugHead->ExportedNamesSize);
printf ("DebugDirectorySize: 0x%08lX\n", separateDebugHead->DebugDirectorySize);
if (!PRD(sizeof(IMAGE_SEPARATE_DEBUG_HEADER),
separateDebugHead->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)))
{printf("Can't get the sections, aborting\n"); return;}
dump_sections(separateDebugHead + 1, separateDebugHead->NumberOfSections);
nb_dbg = separateDebugHead->DebugDirectorySize / sizeof(IMAGE_DEBUG_DIRECTORY);
debugDir = PRD(sizeof(IMAGE_SEPARATE_DEBUG_HEADER) +
separateDebugHead->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) +
separateDebugHead->ExportedNamesSize,
nb_dbg * sizeof(IMAGE_DEBUG_DIRECTORY));
if (!debugDir) {printf("Couldn't get the debug directory info, aborting\n");return;}
printf("Debug Table (%u directories)\n", nb_dbg);
for (i = 0; i < nb_dbg; i++)
{
dump_dir_debug_dir(debugDir, i);
debugDir++;
}
}
static void do_dump(void)
{
int all = (globals.dumpsect != NULL) && strcmp(globals.dumpsect, "ALL") == 0;
if (globals.do_dumpheader)
{
dump_pe_header();
/* FIX%E: should check ptr */
dump_sections((char*)nt_headers + sizeof(DWORD) +
sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader,
nt_headers->FileHeader.NumberOfSections);
}
else if (!globals.dumpsect)
{
/* show at least something here */
dump_pe_header();
}
if (globals.dumpsect)
{
if (all || !strcmp(globals.dumpsect, "import"))
dump_dir_imported_functions();
if (all || !strcmp(globals.dumpsect, "export"))
dump_dir_exported_functions();
if (all || !strcmp(globals.dumpsect, "debug"))
dump_dir_debug();
#if 0
/* FIXME: not implemented yet */
if (all || !strcmp(globals.dumpsect, "resource"))
dump_dir_resource();
if (all || !strcmp(globals.dumpsect, "reloc"))
dump_dir_reloc();
#endif
}
}
static enum FileSig check_headers(void)
{
WORD* pw;
DWORD* pdw;
IMAGE_DOS_HEADER* dh;
enum FileSig sig;
pw = PRD(0, sizeof(WORD));
if (!pw) {printf("Can't get main signature, aborting\n"); return 0;}
switch (*pw)
{
case IMAGE_DOS_SIGNATURE:
sig = SIG_DOS;
dh = PRD(0, sizeof(IMAGE_DOS_HEADER));
if (dh && dh->e_lfanew >= sizeof(*dh)) /* reasonable DOS header ? */
{
/* the signature is the first DWORD */
pdw = PRD(dh->e_lfanew, sizeof(DWORD));
if (pdw)
{
if (*pdw == IMAGE_NT_SIGNATURE)
{
nt_headers = PRD(dh->e_lfanew, sizeof(DWORD));
sig = SIG_PE;
}
else
{
printf("No PE Signature found\n");
}
}
else
{
printf("Can't get the extented signature, aborting\n");
}
}
break;
case 0x4944: /* "DI" */
sig = SIG_DBG;
break;
default:
printf("No known main signature (%.2s/%x), aborting\n", (char*)pw, *pw);
sig = SIG_UNKNOWN;
}
return sig;
}
int pe_analysis(const char* name, void (*fn)(void), enum FileSig wanted_sig)
{
int fd;
enum FileSig effective_sig;
int ret = 1;
setbuf(stdout, NULL);
fd = open(name, O_RDONLY);
if (fd == -1) fatal("Can't open file");
total_len = lseek(fd, 0, SEEK_END);
if (total_len < 0) fatal("Can't get size");
base = mmap(NULL, total_len, PROT_READ, MAP_PRIVATE, fd, 0);
if (base == (void*)-1) fatal("Can't map file");
effective_sig = check_headers();
if (effective_sig == SIG_UNKNOWN)
{
printf("Can't get a recognized file signature, aborting\n");
ret = 0;
}
else if (wanted_sig == SIG_UNKNOWN || wanted_sig == effective_sig)
{
switch (effective_sig)
{
case SIG_UNKNOWN: /* shouldn't happen... */
ret = 0; break;
case SIG_PE:
printf("Contents of \"%s\": %ld bytes\n\n", name, total_len);
do_dump();
break;
case SIG_DBG:
dump_separate_dbg();
break;
case SIG_DOS:
ret = 0; break;
}
}
else
{
printf("Can't get a suitable file signature, aborting\n");
ret = 0;
}
if (ret) printf("Done dumping %s\n", name);
munmap(base, total_len);
close(fd);
return ret;
}
void dump_file(const char* name)
{
pe_analysis(name, do_dump, SIG_UNKNOWN);
}
#if 0
int main(int argc, char* argv[])
{
if (argc != 2) fatal("usage");
pe_analysis(argv[1], do_dump);
}
#endif
typedef struct _dll_symbol {
size_t ordinal;
char *symbol;
} dll_symbol;
static dll_symbol *dll_symbols = NULL;
static dll_symbol *dll_current_symbol = NULL;
/* Compare symbols by ordinal for qsort */
static int symbol_cmp(const void *left, const void *right)
{
return ((dll_symbol *)left)->ordinal > ((dll_symbol *)right)->ordinal;
}
/*******************************************************************
* dll_close
*
* Free resources used by DLL
*/
static void dll_close (void)
{
dll_symbol* ds;
for (ds = dll_symbols; ds->symbol; ds++)
free(ds->symbol);
free (dll_symbols);
dll_symbols = NULL;
}
static void do_grab_sym(void)
{
IMAGE_EXPORT_DIRECTORY *exportDir = get_dir(IMAGE_FILE_EXPORT_DIRECTORY);
unsigned i;
DWORD* pName;
DWORD* pFunc;
WORD* pOrdl;
char* ptr;
DWORD* map;
if (!exportDir) return;
pName = RVA(exportDir->AddressOfNames, exportDir->NumberOfNames * sizeof(DWORD));
if (!pName) {printf("Can't grab functions' name table\n"); return;}
pOrdl = RVA(exportDir->AddressOfNameOrdinals, exportDir->NumberOfNames * sizeof(WORD));
if (!pOrdl) {printf("Can't grab functions' ordinal table\n"); return;}
dll_close();
if (!(dll_symbols = (dll_symbol *) malloc((exportDir->NumberOfFunctions + 1) *
sizeof (dll_symbol))))
fatal ("Out of memory");
if (exportDir->AddressOfFunctions != exportDir->NumberOfNames || exportDir->Base > 1)
globals.do_ordinals = 1;
/* bit map of used funcs */
map = calloc(((exportDir->NumberOfFunctions + 31) & ~31) / 32, sizeof(DWORD));
if (!map) fatal("no memory");
for (i = 0; i < exportDir->NumberOfNames; i++)
{
map[*pOrdl / 32] |= 1 << (*pOrdl % 32);
ptr = RVA(*pName++, sizeof(DWORD));
if (!ptr) ptr = "cant_get_function";
dll_symbols[i].symbol = strdup(ptr);
assert(dll_symbols[i].symbol);
}
pFunc = RVA(exportDir->AddressOfFunctions, exportDir->NumberOfFunctions * sizeof(DWORD));
if (!pFunc) {printf("Can't grab functions' address table\n"); return;}
for (i = 0; i < exportDir->NumberOfFunctions; i++)
{
if (!(map[i / 32] & (1 << (i % 32))))
{
char ordinal_text[256];
/* Ordinal only entry */
snprintf (ordinal_text, sizeof(ordinal_text), "%s_%lu",
globals.forward_dll ? globals.forward_dll : OUTPUT_UC_DLL_NAME,
exportDir->Base + i);
str_toupper(ordinal_text);
dll_symbols[i].symbol = strdup(ordinal_text);
assert(dll_symbols[i].symbol);
dll_symbols[i].ordinal = exportDir->Base + i;
}
}
free(map);
if (NORMAL)
printf("%lu named symbols in DLL, %lu total\n",
exportDir->NumberOfNames, exportDir->NumberOfFunctions);
qsort( dll_symbols, exportDir->NumberOfFunctions, sizeof(dll_symbol), symbol_cmp );
dll_symbols[exportDir->NumberOfFunctions].symbol = NULL;
dll_current_symbol = dll_symbols;
/* Set DLL output names */
if ((ptr = strrchr (globals.input_name, '/')))
globals.input_name = ptr + 1; /* Strip path */
OUTPUT_UC_DLL_NAME = str_toupper( strdup (OUTPUT_DLL_NAME));
}
/*******************************************************************
* dll_open
*
* Open a DLL and read in exported symbols
*/
void dll_open (const char *dll_name)
{
pe_analysis(dll_name, do_grab_sym, SIG_PE);
}
/*******************************************************************
* dll_next_symbol
*
* Get next exported symbol from dll
*/
int dll_next_symbol (parsed_symbol * sym)
{
if (!dll_current_symbol)
return 1;
assert (dll_symbols);
sym->symbol = strdup (dll_current_symbol->symbol);
sym->ordinal = dll_current_symbol->ordinal;
dll_current_symbol++;
return 0;
}

5
tools/winedump/pe.h Normal file
View File

@ -0,0 +1,5 @@
extern void dump_codeview(unsigned long ptr, unsigned long len);
extern void* PRD(unsigned long prd, unsigned long len);
extern unsigned long Offset(void* ptr);
extern char* get_time_str(DWORD _t);

View File

@ -3,7 +3,7 @@
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
#include "winedump.h"
static char *grep_buff = NULL;
static char *fgrep_buff = NULL;

View File

@ -3,7 +3,7 @@
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
#include "winedump.h"
/* Items that are swapped in arguments after the symbol structure

View File

@ -1,5 +1,5 @@
/*
* Specmaker - A Wine DLL tool
* Winedump - A Wine DLL tool
*
* Copyright 2000 Jon Griffiths
*
@ -22,8 +22,8 @@
* mistakes and some incorrect assumptions, but the lists of types
* are pure gold.
*/
#ifndef __WINE_SPECMAKER_H
#define __WINE_SPECMAKER_H
#ifndef __WINE_WINEDUMP_H
#define __WINE_WINEDUMP_H
#include <stdlib.h>
#include <stdio.h>
@ -58,6 +58,8 @@
#define SYM_THISCALL 0x4
#define SYM_DATA 0x8 /* Data, not a function */
typedef enum {NONE, DMGL, SPEC, DUMP} Mode;
/* Structure holding a parsed symbol */
typedef struct __parsed_symbol
{
@ -78,23 +80,37 @@ typedef struct __parsed_symbol
/* All globals */
typedef struct __globals
{
/* Options */
Mode mode; /* SPEC, DEMANGLE or DUMP */
/* Options: generic */
int do_quiet; /* -q */
int do_verbose; /* -v */
/* Option arguments: generic */
const char *input_name; /* */
/* Options: spec mode */
int do_code; /* -c, -t, -f */
int do_trace; /* -t, -f */
int do_cdecl; /* -C */
int do_quiet; /* -q */
int do_verbose; /* -v */
int do_documentation; /* -D */
int do_demangle; /* -S */
/* Option arguments */
/* Options: dump mode */
int do_demangle; /* -d */
int do_dumpheader; /* -f */
/* Option arguments: spec mode */
int start_ordinal; /* -s */
int end_ordinal; /* -e */
const char *directory; /* -I */
const char *input_name; /* -d */
const char *forward_dll; /* -f */
const char *dll_name; /* -o */
char *uc_dll_name; /* -o */
/* Option arguments: dump mode */
const char *dumpsect; /* -j */
/* internal options */
int do_ordinals;
} _globals;
@ -113,6 +129,8 @@ extern _globals globals;
/* Default calling convention */
#define CALLING_CONVENTION (globals.do_cdecl ? SYM_CDECL : SYM_STDCALL)
/* Image functions */
void dump_file(const char* name);
/* DLL functions */
void dll_open (const char *dll_name);
@ -174,11 +192,12 @@ FILE *open_file (const char *name, const char *ext, const char *mode);
#ifdef __GNUC__
void do_usage (void) __attribute__ ((noreturn));
void fatal (const char *message) __attribute__ ((noreturn));
#else
void do_usage (void);
void fatal (const char *message);
#endif
void fatal (const char *message);
#endif /* __WINE_SPECMAKER_H */
#endif /* __WINE_WINEDUMP_H */