1 /*
2  * winDumpExts.c --
3  * Author:   Gordon Chaffee, Scott Stanton
4  *
5  * History:  The real functionality of this file was written by
6  *           Matt Pietrek in 1993 in his pedump utility.  I've
7  *           modified it to dump the externals in a bunch of object
8  *           files to create a .def file.
9  *
10  * 10/12/95  Modified by Scott Stanton to support Relocatable Object Module
11  *	     Format files for Borland C++ 4.5.
12  *
13  * Notes:    Visual C++ puts an underscore before each exported symbol.
14  *           This file removes them.  I don't know if this is a problem
15  *           this other compilers.  If _MSC_VER is defined,
16  *           the underscore is removed.  If not, it isn't.  To get a
17  *           full dump of an object file, use the -f option.  This can
18  *           help determine the something that may be different with a
19  *           compiler other than Visual C++.
20  *----------------------------------------------------------------------
21  *
22  */
23 
24 #include <windows.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <process.h>
28 
29 #ifdef _ALPHA_
30 #define e_magic_number IMAGE_FILE_MACHINE_ALPHA
31 #else
32 #define e_magic_number IMAGE_FILE_MACHINE_I386
33 #endif
34 
35 /*
36  *----------------------------------------------------------------------
37  * GetArgcArgv --
38  *
39  *	Break up a line into argc argv
40  *----------------------------------------------------------------------
41  */
42 int
GetArgcArgv(char * s,char ** argv)43 GetArgcArgv(char *s, char **argv)
44 {
45     int quote = 0;
46     int argc = 0;
47     char *bp;
48 
49     bp = s;
50     while (1) {
51 	while (isspace(*bp)) {
52 	    bp++;
53 	}
54 	if (*bp == '\n' || *bp == '\0') {
55 	    *bp = '\0';
56 	    return argc;
57 	}
58 	if (*bp == '\"') {
59 	    quote = 1;
60 	    bp++;
61 	}
62 	argv[argc++] = bp;
63 
64 	while (*bp != '\0') {
65 	    if (quote) {
66 		if (*bp == '\"') {
67 		    quote = 0;
68 		    *bp = '\0';
69 		    bp++;
70 		    break;
71 		}
72 		bp++;
73 		continue;
74 	    }
75 	    if (isspace(*bp)) {
76 		*bp = '\0';
77 		bp++;
78 		break;
79 	    }
80 	    bp++;
81 	}
82     }
83 }
84 
85 /*
86  *  The names of the first group of possible symbol table storage classes
87  */
88 char * SzStorageClass1[] = {
89     "NULL","AUTOMATIC","EXTERNAL","STATIC","REGISTER","EXTERNAL_DEF","LABEL",
90     "UNDEFINED_LABEL","MEMBER_OF_STRUCT","ARGUMENT","STRUCT_TAG",
91     "MEMBER_OF_UNION","UNION_TAG","TYPE_DEFINITION","UNDEFINED_STATIC",
92     "ENUM_TAG","MEMBER_OF_ENUM","REGISTER_PARAM","BIT_FIELD"
93 };
94 
95 /*
96  * The names of the second group of possible symbol table storage classes
97  */
98 char * SzStorageClass2[] = {
99     "BLOCK","FUNCTION","END_OF_STRUCT","FILE","SECTION","WEAK_EXTERNAL"
100 };
101 
102 /*
103  *----------------------------------------------------------------------
104  * GetSZStorageClass --
105  *
106  *	Given a symbol storage class value, return a descriptive
107  *	ASCII string
108  *----------------------------------------------------------------------
109  */
110 PSTR
GetSZStorageClass(BYTE storageClass)111 GetSZStorageClass(BYTE storageClass)
112 {
113 	if ( storageClass <= IMAGE_SYM_CLASS_BIT_FIELD )
114 		return SzStorageClass1[storageClass];
115 	else if ( (storageClass >= IMAGE_SYM_CLASS_BLOCK)
116 		      && (storageClass <= IMAGE_SYM_CLASS_WEAK_EXTERNAL) )
117 		return SzStorageClass2[storageClass-IMAGE_SYM_CLASS_BLOCK];
118 	else
119 		return "???";
120 }
121 
122 /*
123  *----------------------------------------------------------------------
124  * GetSectionName --
125  *
126  *	Used by DumpSymbolTable, it gives meaningful names to
127  *	the non-normal section number.
128  *
129  * Results:
130  *	A name is returned in buffer
131  *----------------------------------------------------------------------
132  */
133 void
GetSectionName(WORD section,PSTR buffer,unsigned cbBuffer)134 GetSectionName(WORD section, PSTR buffer, unsigned cbBuffer)
135 {
136     char tempbuffer[10];
137 
138     switch ( (SHORT)section )
139     {
140       case IMAGE_SYM_UNDEFINED: strcpy(tempbuffer, "UNDEF"); break;
141       case IMAGE_SYM_ABSOLUTE:  strcpy(tempbuffer, "ABS  "); break;
142       case IMAGE_SYM_DEBUG:	  strcpy(tempbuffer, "DEBUG"); break;
143       default: wsprintf(tempbuffer, "%-5X", section);
144     }
145 
146     strncpy(buffer, tempbuffer, cbBuffer-1);
147 }
148 
149 /*
150  *----------------------------------------------------------------------
151  * DumpSymbolTable --
152  *
153  *	Dumps a COFF symbol table from an EXE or OBJ.  We only use
154  *	it to dump tables from OBJs.
155  *----------------------------------------------------------------------
156  */
157 void
DumpSymbolTable(PIMAGE_SYMBOL pSymbolTable,FILE * fout,unsigned cSymbols)158 DumpSymbolTable(PIMAGE_SYMBOL pSymbolTable, FILE *fout, unsigned cSymbols)
159 {
160     unsigned i;
161     PSTR stringTable;
162     char sectionName[10];
163 
164     fprintf(fout, "Symbol Table - %X entries  (* = auxillary symbol)\n",
165 	    cSymbols);
166 
167     fprintf(fout,
168      "Indx Name                 Value    Section    cAux  Type    Storage\n"
169      "---- -------------------- -------- ---------- ----- ------- --------\n");
170 
171     /*
172      * The string table apparently starts right after the symbol table
173      */
174     stringTable = (PSTR)&pSymbolTable[cSymbols];
175 
176     for ( i=0; i < cSymbols; i++ ) {
177 	fprintf(fout, "%04X ", i);
178 	if ( pSymbolTable->N.Name.Short != 0 )
179 	    fprintf(fout, "%-20.8s", pSymbolTable->N.ShortName);
180 	else
181 	    fprintf(fout, "%-20s", stringTable + pSymbolTable->N.Name.Long);
182 
183 	fprintf(fout, " %08X", pSymbolTable->Value);
184 
185 	GetSectionName(pSymbolTable->SectionNumber, sectionName,
186 		       sizeof(sectionName));
187 	fprintf(fout, " sect:%s aux:%X type:%02X st:%s\n",
188 	       sectionName,
189 	       pSymbolTable->NumberOfAuxSymbols,
190 	       pSymbolTable->Type,
191 	       GetSZStorageClass(pSymbolTable->StorageClass) );
192 #if 0
193 	if ( pSymbolTable->NumberOfAuxSymbols )
194 	    DumpAuxSymbols(pSymbolTable);
195 #endif
196 
197 	/*
198 	 * Take into account any aux symbols
199 	 */
200 	i += pSymbolTable->NumberOfAuxSymbols;
201 	pSymbolTable += pSymbolTable->NumberOfAuxSymbols;
202 	pSymbolTable++;
203     }
204 }
205 
206 /*
207  *----------------------------------------------------------------------
208  * DumpExternals --
209  *
210  *	Dumps a COFF symbol table from an EXE or OBJ.  We only use
211  *	it to dump tables from OBJs.
212  *----------------------------------------------------------------------
213  */
214 void
DumpExternals(PIMAGE_SYMBOL pSymbolTable,FILE * fout,unsigned cSymbols)215 DumpExternals(PIMAGE_SYMBOL pSymbolTable, FILE *fout, unsigned cSymbols)
216 {
217     unsigned i;
218     PSTR stringTable;
219     char *s, *f;
220     char symbol[1024];
221 
222     /*
223      * The string table apparently starts right after the symbol table
224      */
225     stringTable = (PSTR)&pSymbolTable[cSymbols];
226 
227     for ( i=0; i < cSymbols; i++ ) {
228 	if (pSymbolTable->SectionNumber > 0 && pSymbolTable->Type == 0x20) {
229 	    if (pSymbolTable->StorageClass == IMAGE_SYM_CLASS_EXTERNAL) {
230 		if (pSymbolTable->N.Name.Short != 0) {
231 		    strncpy(symbol, pSymbolTable->N.ShortName, 8);
232 		    symbol[8] = 0;
233 		} else {
234 		    s = stringTable + pSymbolTable->N.Name.Long;
235 		    strcpy(symbol, s);
236 		}
237 		s = symbol;
238 		f = strchr(s, '@');
239 		if (f) {
240 		    *f = 0;
241 		}
242 #if defined(_MSC_VER) && defined(_X86_)
243 		if (symbol[0] == '_') {
244 		    s = &symbol[1];
245 		}
246 #endif
247 		if ((stricmp(s, "DllEntryPoint") != 0)
248 			&& (stricmp(s, "DllMain") != 0)) {
249 		    fprintf(fout, "\t%s\n", s);
250 		}
251 	    }
252 	}
253 
254 	/*
255 	 * Take into account any aux symbols
256 	 */
257 	i += pSymbolTable->NumberOfAuxSymbols;
258 	pSymbolTable += pSymbolTable->NumberOfAuxSymbols;
259 	pSymbolTable++;
260     }
261 }
262 
263 /*
264  *----------------------------------------------------------------------
265  * DumpObjFile --
266  *
267  *	Dump an object file--either a full listing or just the exported
268  *	symbols.
269  *----------------------------------------------------------------------
270  */
271 void
DumpObjFile(PIMAGE_FILE_HEADER pImageFileHeader,FILE * fout,int full)272 DumpObjFile(PIMAGE_FILE_HEADER pImageFileHeader, FILE *fout, int full)
273 {
274     PIMAGE_SYMBOL PCOFFSymbolTable;
275     DWORD COFFSymbolCount;
276 
277     PCOFFSymbolTable = (PIMAGE_SYMBOL)
278 	((DWORD)pImageFileHeader + pImageFileHeader->PointerToSymbolTable);
279     COFFSymbolCount = pImageFileHeader->NumberOfSymbols;
280 
281     if (full) {
282 	DumpSymbolTable(PCOFFSymbolTable, fout, COFFSymbolCount);
283     } else {
284 	DumpExternals(PCOFFSymbolTable, fout, COFFSymbolCount);
285     }
286 }
287 
288 /*
289  *----------------------------------------------------------------------
290  * SkipToNextRecord --
291  *
292  *	Skip over the current ROMF record and return the type of the
293  *	next record.
294  *----------------------------------------------------------------------
295  */
296 
297 BYTE
SkipToNextRecord(BYTE ** ppBuffer)298 SkipToNextRecord(BYTE **ppBuffer)
299 {
300     int length;
301     (*ppBuffer)++;		/* Skip over the type.*/
302     length = *((WORD*)(*ppBuffer))++; /* Retrieve the length. */
303     *ppBuffer += length;	/* Skip over the rest. */
304     return **ppBuffer;		/* Return the type. */
305 }
306 
307 /*
308  *----------------------------------------------------------------------
309  * DumpROMFObjFile --
310  *
311  *	Dump a Relocatable Object Module Format file, displaying only
312  *	the exported symbols.
313  *----------------------------------------------------------------------
314  */
315 void
DumpROMFObjFile(LPVOID pBuffer,FILE * fout)316 DumpROMFObjFile(LPVOID pBuffer, FILE *fout)
317 {
318     BYTE type, length;
319     char symbol[1024], *s;
320 
321     while (1) {
322 	type = SkipToNextRecord(&(BYTE*)pBuffer);
323 	if (type == 0x90) {	/* PUBDEF */
324 	    if (((BYTE*)pBuffer)[4] != 0) {
325 		length = ((BYTE*)pBuffer)[5];
326 		strncpy(symbol, ((char*)pBuffer) + 6, length);
327 		symbol[length] = '\0';
328 		s = symbol;
329 		if ((stricmp(s, "DllEntryPoint") != 0)
330 			&& (stricmp(s, "DllMain") != 0)) {
331 		    if (s[0] == '_') {
332 			s++;
333 			fprintf(fout, "\t_%s\n\t%s=_%s\n", s, s, s);
334 		    } else {
335 			fprintf(fout, "\t%s\n", s);
336 		    }
337 		}
338 	    }
339 	} else if (type == 0x8B || type == 0x8A) { /* MODEND */
340 	    break;
341 	}
342     }
343 }
344 
345 /*
346  *----------------------------------------------------------------------
347  * DumpFile --
348  *
349  *	Open up a file, memory map it, and call the appropriate
350  *	dumping routine
351  *----------------------------------------------------------------------
352  */
353 void
DumpFile(LPSTR filename,FILE * fout,int full)354 DumpFile(LPSTR filename, FILE *fout, int full)
355 {
356     HANDLE hFile;
357     HANDLE hFileMapping;
358     LPVOID lpFileBase;
359     PIMAGE_DOS_HEADER dosHeader;
360 
361     hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
362 		       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
363 
364     if (hFile == INVALID_HANDLE_VALUE) {
365 	fprintf(stderr, "Couldn't open file with CreateFile()\n");
366 	return;
367     }
368 
369     hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
370     if (hFileMapping == 0) {
371 	CloseHandle(hFile);
372 	fprintf(stderr, "Couldn't open file mapping with CreateFileMapping()\n");
373 	return;
374     }
375 
376     lpFileBase = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
377     if (lpFileBase == 0) {
378 	CloseHandle(hFileMapping);
379 	CloseHandle(hFile);
380 	fprintf(stderr, "Couldn't map view of file with MapViewOfFile()\n");
381 	return;
382     }
383 
384     dosHeader = (PIMAGE_DOS_HEADER)lpFileBase;
385     if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
386 #if 0
387 	DumpExeFile( dosHeader );
388 #else
389 	fprintf(stderr, "File is an executable.  I don't dump those.\n");
390 	return;
391 #endif
392     }
393     /* Does it look like a i386 COFF OBJ file??? */
394     else if ((dosHeader->e_magic == e_magic_number)
395 	    && (dosHeader->e_sp == 0)) {
396 	/*
397 	 * The two tests above aren't what they look like.  They're
398 	 * really checking for IMAGE_FILE_HEADER.Machine == i386 (0x14C)
399 	 * and IMAGE_FILE_HEADER.SizeOfOptionalHeader == 0;
400 	 */
401 	DumpObjFile((PIMAGE_FILE_HEADER) lpFileBase, fout, full);
402     } else if (*((BYTE *)lpFileBase) == 0x80) {
403 	/*
404 	 * This file looks like it might be a ROMF file.
405 	 */
406 	DumpROMFObjFile(lpFileBase, fout);
407     } else {
408 	printf("unrecognized file format\n");
409     }
410     UnmapViewOfFile(lpFileBase);
411     CloseHandle(hFileMapping);
412     CloseHandle(hFile);
413 }
414 
415 void
main(int argc,char ** argv)416 main(int argc, char **argv)
417 {
418     char *fargv[1000];
419     char cmdline[10000];
420     int i, arg;
421     FILE *fout;
422     int pos;
423     int full = 0;
424     char *outfile = NULL;
425 
426     if (argc < 3) {
427       Usage:
428 	fprintf(stderr, "Usage: %s ?-o outfile? ?-f(ull)? <dllname> <object filenames> ..\n", argv[0]);
429 	exit(1);
430     }
431 
432     arg = 1;
433     while (argv[arg][0] == '-') {
434 	if (strcmp(argv[arg], "--") == 0) {
435 	    arg++;
436 	    break;
437 	} else if (strcmp(argv[arg], "-f") == 0) {
438 	    full = 1;
439 	} else if (strcmp(argv[arg], "-o") == 0) {
440 	    arg++;
441 	    if (arg == argc) {
442 		goto Usage;
443 	    }
444 	    outfile = argv[arg];
445 	}
446 	arg++;
447     }
448     if (arg == argc) {
449 	goto Usage;
450     }
451 
452     if (outfile) {
453 	fout = fopen(outfile, "w+");
454 	if (fout == NULL) {
455 	    fprintf(stderr, "Unable to open \'%s\' for writing:\n",
456 		    argv[arg]);
457 	    perror("");
458 	    exit(1);
459 	}
460     } else {
461 	fout = stdout;
462     }
463 
464     if (! full) {
465 	char *dllname = argv[arg];
466 	arg++;
467 	if (arg == argc) {
468 	    goto Usage;
469 	}
470 	fprintf(fout, "LIBRARY    %s\n", dllname);
471 	fprintf(fout, "EXETYPE WINDOWS\n");
472 	fprintf(fout, "CODE PRELOAD MOVEABLE DISCARDABLE\n");
473 	fprintf(fout, "DATA PRELOAD MOVEABLE MULTIPLE\n\n");
474 	fprintf(fout, "EXPORTS\n");
475     }
476 
477     for (; arg < argc; arg++) {
478 	if (argv[arg][0] == '@') {
479 	    FILE *fargs = fopen(&argv[arg][1], "r");
480 	    if (fargs == NULL) {
481 		fprintf(stderr, "Unable to open \'%s\' for reading:\n",
482 			argv[arg]);
483 		perror("");
484 		exit(1);
485 	    }
486 	    pos = 0;
487 	    for (i = 0; i < arg; i++) {
488 		strcpy(&cmdline[pos], argv[i]);
489 		pos += strlen(&cmdline[pos]) + 1;
490 		fargv[i] = argv[i];
491 	    }
492 	    fgets(&cmdline[pos], sizeof(cmdline), fargs);
493 	    fprintf(stderr, "%s\n", &cmdline[pos]);
494 	    fclose(fargs);
495 	    i += GetArgcArgv(&cmdline[pos], &fargv[i]);
496 	    argc = i;
497 	    argv = fargv;
498 	}
499 	DumpFile(argv[arg], fout, full);
500     }
501     exit(0);
502 }
503