gecko-dev/tools/codesighs/msmap2tsv.c
blythe%netscape.com 7c49a8ef52 Not part of normal build.
Hopefully fix win32 tinderbox codesighs numbers.
Problem revolved around decoding string outside of printable range.
This was giving the scripts (sort) some problems.
2003-01-24 00:27:17 +00:00

2231 lines
79 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is msmap2tsv.c code, released
* Oct 3, 2002.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 2002 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Garrett Arch Blythe, 03-October-2002
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include "msmap.h"
#if defined(_WIN32)
#include <windows.h>
#include <imagehlp.h>
#define F_DEMANGLE 1
#define DEMANGLE_STATE_NORMAL 0
#define DEMANGLE_STATE_QDECODE 1
#define DEMANGLE_STATE_PROLOGUE_1 2
#define DEMANGLE_STATE_HAVE_TYPE 3
#define DEMANGLE_STATE_DEC_LENGTH 4
#define DEMANGLE_STATE_HEX_LENGTH 5
#define DEMANGLE_STATE_PROLOGUE_SECONDARY 6
#define DEMANGLE_STATE_DOLLAR_1 7
#define DEMANGLE_STATE_DOLLAR_2 8
#define DEMANGLE_STATE_START 9
#define DEMANGLE_STATE_STOP 10
#define DEMANGLE_SAFE_CHAR(eval) (isprint(eval) ? eval : ' ')
#else
#define F_DEMANGLE 0
#endif /* WIN32 */
#define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg));
#define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0)
typedef struct __struct_SymDB_Size
/*
** The size of the symbol.
** The size is nested withing a symbols structures to produce a fast
** lookup path.
** The objects are listed in case the client of the symdb needs to
** match the object name in the scenario where multiple symbol
** sizes are present.
**
** mSize The size of the symbol in these objects.
** mObjects A list of objects containing said symbol.
** mObjectCount Number of objects.
*/
{
unsigned mSize;
char** mObjects;
unsigned mObjectCount;
}
SymDB_Size;
typedef struct __struct_SymDB_Section
/*
** Each section for a symbol has a list of sizes.
** Should there be exactly one size for the symbol, then that
** is the size that should be accepted.
** If there is more than one size, then a match on the object
** should be attempted, held withing each size.
**
** mName The section name.
** mSizes The varoius sizes of the symbol in this section.
** mSizeCount The number of available sizes.
*/
{
char* mName;
SymDB_Size* mSizes;
unsigned mSizeCount;
}
SymDB_Section;
typedef struct __struct_SymDB_Symbol
/*
** Each symbol has at least one section.
** The section indicates what type of symbol a client may be looking for.
** If there is no match on the section, then the client should not trust
** the symbdb.
**
** mName The mangled name of the symbol.
** mSections Various sections this symbol belongs to.
** mSectionCount The number of sections.
*/
{
char* mName;
SymDB_Section* mSections;
unsigned mSectionCount;
}
SymDB_Symbol;
#define SYMDB_SYMBOL_GROWBY 0x1000 /* how many sybols to allocate at a time */
typedef struct __struct_SymDB_Container
/*
** The symbol DB container object.
** The goal of the symbol DB is to have exactly one SymDB_Symbol for each
** mangled name, no matter how ever many identical mangled names there
** are in the input.
** The input is already expected to be well sorted, futher this leads to
** the ability to binary search for symbol name matches.
**
** mSymbols The symbols.
** mSymbolCount The number of symbols in the DB.
** mSymbolCapacity The number of symbols we can hold (before realloc).
*/
{
SymDB_Symbol* mSymbols;
unsigned mSymbolCount;
unsigned mSymbolCapacity;
}
SymDB_Container;
typedef struct __struct_Options
/*
** Options to control how we perform.
**
** mProgramName Used in help text.
** mInput File to read for input.
** Default is stdin.
** mInputName Name of the file.
** mOutput Output file, append.
** Default is stdout.
** mOutputName Name of the file.
** mHelp Wether or not help should be shown.
** mMatchModules Array of strings which the module name should match.
** mMatchModuleCount Number of items in array.
** mSymDBName Symbol DB filename.
** mBatchMode Batch mode.
** When in batch mode, the input file contains a list of
** map files to process.
** Normally the input file is a single map file itself.
*/
{
const char* mProgramName;
FILE* mInput;
char* mInputName;
FILE* mOutput;
char* mOutputName;
int mHelp;
char** mMatchModules;
unsigned mMatchModuleCount;
char* mSymDBName;
SymDB_Container* mSymDB;
int mBatchMode;
}
Options;
typedef struct __struct_Switch
/*
** Command line options.
*/
{
const char* mLongName;
const char* mShortName;
int mHasValue;
const char* mValue;
const char* mDescription;
}
Switch;
#define DESC_NEWLINE "\n\t\t"
static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."};
static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."};
static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."};
static Switch gMatchModuleSwitch = {"--match-module", "-mm", 1, NULL, "Specify a valid module name." DESC_NEWLINE "Multiple specifications allowed." DESC_NEWLINE "If a module name does not match one of the names specified then no output will occur."};
static Switch gSymDBSwitch = {"--symdb", "-sdb", 1, NULL, "Specify a symbol tsv db input file." DESC_NEWLINE "Such a symdb is produced using the tool msdump2symdb." DESC_NEWLINE "This allows better symbol size approximations." DESC_NEWLINE "The symdb file must be pre-sorted."};
static Switch gBatchModeSwitch = {"--batch", "-b", 0, NULL, "Runs in batch mode." DESC_NEWLINE "The input file contains a list of map files." DESC_NEWLINE "Normally the input file is a map file itself." DESC_NEWLINE "This eliminates reprocessing the symdb for multiple map files."};
static Switch* gSwitches[] = {
&gInputSwitch,
&gOutputSwitch,
&gMatchModuleSwitch,
&gSymDBSwitch,
&gBatchModeSwitch,
&gHelpSwitch
};
typedef struct __struct_MSMap_ReadState
/*
** Keep track of what state we are while reading input.
** This gives the input context in which we absorb the datum.
*/
{
int mHasModule;
int mHasTimestamp;
int mHasPreferredLoadAddress;
int mHasSegmentData;
int mSegmentDataSkippedLine;
int mHasPublicSymbolData;
int mHasPublicSymbolDataSkippedLines;
int mHasEntryPoint;
int mFoundStaticSymbols;
}
MSMap_ReadState;
char* skipWhite(char* inScan)
/*
** Skip whitespace.
*/
{
char* retval = inScan;
while(isspace(*retval))
{
retval++;
}
return retval;
}
void trimWhite(char* inString)
/*
** Remove any whitespace from the end of the string.
*/
{
int len = strlen(inString);
while(len)
{
len--;
if(isspace(*(inString + len)))
{
*(inString + len) = '\0';
}
else
{
break;
}
}
}
char* lastWord(char* inString)
/*
** Finds and returns the last word in a string.
** It is assumed no whitespace is at the end of the string.
*/
{
int mod = 0;
int len = strlen(inString);
while(len)
{
len--;
if(isspace(*(inString + len)))
{
mod = 1;
break;
}
}
return inString + len + mod;
}
MSMap_Segment* getSymbolSection(MSMap_Module* inModule, MSMap_Symbol* inoutSymbol)
/*
** Perform a lookup for the section of the symbol.
** The function could cache the value.
*/
{
MSMap_Segment* retval = NULL;
if(NULL != inoutSymbol->mSection)
{
/*
** Use cached value.
*/
retval = inoutSymbol->mSection;
}
else
{
unsigned secLoop = 0;
/*
** Go through sections in module to find the match for the symbol.
*/
for(secLoop = 0; secLoop < inModule->mSegmentCount; secLoop++)
{
if(inoutSymbol->mPrefix == inModule->mSegments[secLoop].mPrefix)
{
if(inoutSymbol->mOffset >= inModule->mSegments[secLoop].mOffset)
{
if(inoutSymbol->mOffset < (inModule->mSegments[secLoop].mOffset + inModule->mSegments[secLoop].mLength))
{
/*
** We have the section.
*/
retval = &inModule->mSegments[secLoop];
break;
}
}
}
}
/*
** Cache the value for next time.
*/
inoutSymbol->mSection = retval;
}
return retval;
}
int readSymDB(const char* inDBName, SymDB_Container** outDB)
/*
** Intialize the symbol DB.
** Only call if the symbol DB should be initialized.
*/
{
int retval = 0;
/*
** Initialize out arguments.
*/
if(NULL != outDB)
{
*outDB = NULL;
}
if(NULL != outDB && NULL != inDBName)
{
FILE* symDB = NULL;
symDB = fopen(inDBName, "r");
if(NULL != symDB)
{
*outDB = (SymDB_Container*)calloc(1, sizeof(SymDB_Container));
if(NULL != *outDB)
{
char lineBuf[0x400];
char* symbol = NULL;
char* section = NULL;
char* object = NULL;
char* length = NULL;
unsigned lengthNum = 0;
char* endLength = NULL;
/*
** Read the file line by line.
*/
while(0 == retval && NULL != fgets(lineBuf, sizeof(lineBuf), symDB))
{
trimWhite(lineBuf);
/*
** Each line has four arguments. tab seperated values (tsv).
** Symbol
** Section
** Length
** Object
*/
symbol = skipWhite(lineBuf);
if(NULL == symbol)
{
retval = __LINE__;
ERROR_REPORT(retval, inDBName, "File does not appear to be a symbol DB.");
break;
}
section = strchr(symbol, '\t');
if(NULL == section)
{
retval = __LINE__;
ERROR_REPORT(retval, inDBName, "File does not appear to be a symbol DB.");
break;
}
*section = '\0';
section++;
length = strchr(section, '\t');
if(NULL == length)
{
retval = __LINE__;
ERROR_REPORT(retval, inDBName, "File does not appear to be a symbol DB.");
break;
}
*length = '\0';
length++;
object = strchr(length, '\t');
if(NULL == object)
{
retval = __LINE__;
ERROR_REPORT(retval, inDBName, "File does not appear to be a symbol DB.");
break;
}
*object = '\0';
object++;
/*
** Convert the length into a number.
*/
errno = 0;
lengthNum = strtoul(length, &endLength, 16);
if(0 == errno && endLength != length)
{
SymDB_Symbol* dbSymbol = NULL;
SymDB_Section* dbSection = NULL;
SymDB_Size* dbSize = NULL;
char* dbObject = NULL;
void* moved = NULL;
/*
** Are we looking at the same symbol as last line?
** This assumes the symdb is pre sorted!!!
*/
if(0 != (*outDB)->mSymbolCount)
{
unsigned index = (*outDB)->mSymbolCount - 1;
if(0 == strcmp((*outDB)->mSymbols[index].mName, symbol))
{
dbSymbol = &(*outDB)->mSymbols[index];
}
}
/*
** May need to create symbol.
*/
if(NULL == dbSymbol)
{
/*
** Could be time to grow the symbol pool.
*/
if((*outDB)->mSymbolCount >= (*outDB)->mSymbolCapacity)
{
moved = realloc((*outDB)->mSymbols, sizeof(SymDB_Symbol) * ((*outDB)->mSymbolCapacity + SYMDB_SYMBOL_GROWBY));
if(NULL != moved)
{
(*outDB)->mSymbols = (SymDB_Symbol*)moved;
memset(&(*outDB)->mSymbols[(*outDB)->mSymbolCapacity], 0, sizeof(SymDB_Symbol) * SYMDB_SYMBOL_GROWBY);
(*outDB)->mSymbolCapacity += SYMDB_SYMBOL_GROWBY;
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, inDBName, "Unable to grow symbol DB symbol array.");
break;
}
}
if((*outDB)->mSymbolCount < (*outDB)->mSymbolCapacity)
{
dbSymbol = &(*outDB)->mSymbols[(*outDB)->mSymbolCount];
(*outDB)->mSymbolCount++;
dbSymbol->mName = strdup(symbol);
if(NULL == dbSymbol->mName)
{
retval = __LINE__;
ERROR_REPORT(retval, symbol, "Unable to duplicate string.");
break;
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, symbol, "Unable to grow symbol DB for symbol.");
break;
}
}
/*
** Assume we have the symbol.
**
** Is this the same section as the last section in the symbol?
** This assumes the symdb was presorted!!!!
*/
if(0 != dbSymbol->mSectionCount)
{
unsigned index = dbSymbol->mSectionCount - 1;
if(0 == strcmp(dbSymbol->mSections[index].mName, section))
{
dbSection = &dbSymbol->mSections[index];
}
}
/*
** May need to create the section.
*/
if(NULL == dbSection)
{
moved = realloc(dbSymbol->mSections, sizeof(SymDB_Section) * (dbSymbol->mSectionCount + 1));
if(NULL != moved)
{
dbSymbol->mSections = (SymDB_Section*)moved;
dbSection = &dbSymbol->mSections[dbSymbol->mSectionCount];
dbSymbol->mSectionCount++;
memset(dbSection, 0, sizeof(SymDB_Section));
dbSection->mName = strdup(section);
if(NULL == dbSection->mName)
{
retval = __LINE__;
ERROR_REPORT(retval, section, "Unable to duplicate string.");
break;
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, section, "Unable to grow symbol sections for symbol DB.");
break;
}
}
/*
** Assume we have the section.
**
** Is this the same size as the last size?
** This assumes the symdb was presorted!!!
*/
if(0 != dbSection->mSizeCount)
{
unsigned index = dbSection->mSizeCount - 1;
if(dbSection->mSizes[index].mSize == lengthNum)
{
dbSize = &dbSection->mSizes[index];
}
}
/*
** May need to create the size in question.
*/
if(NULL == dbSize)
{
moved = realloc(dbSection->mSizes, sizeof(SymDB_Size) * (dbSection->mSizeCount + 1));
if(NULL != moved)
{
dbSection->mSizes = (SymDB_Size*)moved;
dbSize = &dbSection->mSizes[dbSection->mSizeCount];
dbSection->mSizeCount++;
memset(dbSize, 0, sizeof(SymDB_Size));
dbSize->mSize = lengthNum;
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, length, "Unable to grow symbol section sizes for symbol DB.");
break;
}
}
/*
** Assume we have the size.
**
** We assume a one to one correllation between size and object.
** Always try to add the new object name.
** As the symdb is assumed to be sorted, the object names should also be in order.
*/
moved = realloc(dbSize->mObjects, sizeof(char*) * (dbSize->mObjectCount + 1));
if(NULL != moved)
{
dbObject = strdup(object);
dbSize->mObjects = (char**)moved;
dbSize->mObjects[dbSize->mObjectCount] = dbObject;
dbSize->mObjectCount++;
if(NULL == dbObject)
{
retval = __LINE__;
ERROR_REPORT(retval, object, "Unable to duplicate string.");
break;
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, object, "Unable to grow symbol section size objects for symbol DB.");
break;
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, length, "Unable to convert symbol DB length into a number.");
break;
}
}
if(0 == retval && 0 != ferror(symDB))
{
retval = __LINE__;
ERROR_REPORT(retval, inDBName, "Unable to read file.");
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, inDBName, "Unable to allocate symbol DB.");
}
fclose(symDB);
symDB = NULL;
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, inDBName, "Unable to open symbol DB.");
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, "(NULL)", "Invalid arguments.");
}
return retval;
}
void cleanSymDB(SymDB_Container** inDB)
/*
** Free it all up.
*/
{
if(NULL != inDB && NULL != *inDB)
{
unsigned symLoop = 0;
unsigned secLoop = 0;
unsigned sizLoop = 0;
unsigned objLoop = 0;
for(symLoop = 0; symLoop < (*inDB)->mSymbolCount; symLoop++)
{
for(secLoop = 0; secLoop < (*inDB)->mSymbols[symLoop].mSectionCount; secLoop++)
{
for(sizLoop = 0; sizLoop < (*inDB)->mSymbols[symLoop].mSections[secLoop].mSizeCount; sizLoop++)
{
for(objLoop = 0; objLoop < (*inDB)->mSymbols[symLoop].mSections[secLoop].mSizes[sizLoop].mObjectCount; objLoop++)
{
CLEANUP((*inDB)->mSymbols[symLoop].mSections[secLoop].mSizes[sizLoop].mObjects[objLoop]);
}
CLEANUP((*inDB)->mSymbols[symLoop].mSections[secLoop].mSizes[sizLoop].mObjects);
}
CLEANUP((*inDB)->mSymbols[symLoop].mSections[secLoop].mName);
CLEANUP((*inDB)->mSymbols[symLoop].mSections[secLoop].mSizes);
}
CLEANUP((*inDB)->mSymbols[symLoop].mName);
CLEANUP((*inDB)->mSymbols[symLoop].mSections);
}
CLEANUP((*inDB)->mSymbols);
CLEANUP(*inDB);
}
}
int symDBLookup(const void* inKey, const void* inItem)
/*
** bsearch utility routine to find the symbol in the symdb.
*/
{
int retval = 0;
const char* key = (const char*)inKey;
const SymDB_Symbol* symbol = (const SymDB_Symbol*)inItem;
retval = strcmp(key, symbol->mName);
return retval;
}
int fillSymbolSizeFromDB(Options* inOptions, MSMap_Module* inModule, MSMap_Symbol* inoutSymbol, const char* inMangledName)
/*
** If we have a symbol DB, attempt to determine the real size of the symbol
** up front.
** This helps us later in the game to avoid performing size guesses by
** offset.
*/
{
int retval = 0;
/*
** May need to initialize symdb.
*/
if(NULL == inOptions->mSymDB && NULL != inOptions->mSymDBName)
{
retval = readSymDB(inOptions->mSymDBName, &inOptions->mSymDB);
}
/*
** Optional
*/
if(0 == retval && NULL != inOptions->mSymDB)
{
void* match = NULL;
/*
** Find the symbol.
*/
match = bsearch(inMangledName, inOptions->mSymDB->mSymbols, inOptions->mSymDB->mSymbolCount, sizeof(SymDB_Symbol), symDBLookup);
if(NULL != match)
{
SymDB_Symbol* symbol = (SymDB_Symbol*)match;
unsigned symDBSize = 0;
MSMap_Segment* mapSection = NULL;
/*
** We found the symbol.
**
** See if it has the section in question.
*/
mapSection = getSymbolSection(inModule, inoutSymbol);
if(NULL != mapSection)
{
unsigned secLoop = 0;
for(secLoop = 0; secLoop < symbol->mSectionCount; secLoop++)
{
if(0 == strcmp(mapSection->mSegment, symbol->mSections[secLoop].mName))
{
SymDB_Section* section = &symbol->mSections[secLoop];
/*
** We have a section match.
** Should there be a single size for the symbol,
** then we just default to that.
** If more than one size, we have to do an
** object match search.
** Should there be no object match, we do nothign.
*/
if(1 == section->mSizeCount)
{
symDBSize = section->mSizes[0].mSize;
}
else
{
char* mapObject = NULL;
/*
** Figure out the map object file name.
** Skip any colon.
** If it doesn't have a .obj in it, not worth continuing.
*/
mapObject = strrchr(inoutSymbol->mObject, ':');
if(NULL == mapObject)
{
mapObject = inoutSymbol->mObject;
}
else
{
mapObject++; /* colon */
}
if(NULL != strstr(mapObject, ".obj"))
{
unsigned sizLoop = 0;
unsigned objLoop = 0;
SymDB_Size* size = NULL;
for(sizLoop = 0; sizLoop < section->mSizeCount; sizLoop++)
{
size = &section->mSizes[sizLoop];
for(objLoop = 0; objLoop < size->mObjectCount; objLoop++)
{
if(NULL != strstr(size->mObjects[objLoop], mapObject))
{
/*
** As we matched the object, in a particular section,
** we'll go with this as the number.
*/
symDBSize = size->mSize;
break;
}
}
/*
** If the object loop broke early, we break too.
*/
if(objLoop < size->mObjectCount)
{
break;
}
}
}
}
break;
}
}
}
/*
** Put the size in.
*/
inoutSymbol->mSymDBSize = symDBSize;
}
}
return retval;
}
char* symdup(const char* inSymbol)
/*
** Attempts to demangle the symbol if appropriate.
** Otherwise acts like strdup.
*/
{
char* retval = NULL;
#if F_DEMANGLE
{
int isImport = 0;
if(0 == strncmp("__imp_", inSymbol, 6))
{
isImport = __LINE__;
inSymbol += 6;
}
if('?' == inSymbol[0])
{
char demangleBuf[0x200];
DWORD demangleRes = 0;
demangleRes = UnDecorateSymbolName(inSymbol, demangleBuf, sizeof(demangleBuf), UNDNAME_COMPLETE);
if(0 != demangleRes)
{
if (strcmp(demangleBuf, "`string'") == 0)
{
/* attempt manual demangling of string prefix.. */
/* first make sure we have enough space for the
updated string - the demangled string will
always be shorter than strlen(inSymbol) and the
prologue will always be longer than the
"string: " that we tack on the front of the string
*/
char *curresult = retval = malloc(strlen(inSymbol) + 11);
const char *curchar = inSymbol;
int state = DEMANGLE_STATE_START;
/* the hex state is for stuff like ?$EA which
really means hex value 0x40 */
char hex_state = 0;
char string_is_unicode = 0;
/* sometimes we get a null-termination before the
final @ sign - in that case, remember that
we've seen the whole string */
int have_null_char = 0;
/* stick our user-readable prefix on */
strcpy(curresult, "string: \"");
curresult += 9;
while (*curchar) {
// process current state
switch (state) {
/* the Prologue states are divided up so
that someday we can try to decode
the random letters in between the '@'
signs. Also, some strings only have 2
prologue '@' signs, so we have to
figure out how to distinguish between
them at some point. */
case DEMANGLE_STATE_START:
if (*curchar == '@')
state = DEMANGLE_STATE_PROLOGUE_1;
/* ignore all other states */
break;
case DEMANGLE_STATE_PROLOGUE_1:
switch (*curchar) {
case '0':
string_is_unicode=0;
state = DEMANGLE_STATE_HAVE_TYPE;
break;
case '1':
string_is_unicode=1;
state = DEMANGLE_STATE_HAVE_TYPE;
break;
/* ignore all other characters */
}
break;
case DEMANGLE_STATE_HAVE_TYPE:
if (*curchar >= '0' && *curchar <= '9') {
state = DEMANGLE_STATE_DEC_LENGTH;
} else if (*curchar >= 'A' && *curchar <= 'Z') {
state = DEMANGLE_STATE_HEX_LENGTH;
}
case DEMANGLE_STATE_DEC_LENGTH:
/* decimal lengths don't have the 2nd
field
*/
if (*curchar == '@')
state = DEMANGLE_STATE_NORMAL;
break;
case DEMANGLE_STATE_HEX_LENGTH:
/* hex lengths have a 2nd field
(though I have no idea what it is for)
*/
if (*curchar == '@')
state = DEMANGLE_STATE_PROLOGUE_SECONDARY;
break;
case DEMANGLE_STATE_PROLOGUE_SECONDARY:
if (*curchar == '@')
state = DEMANGLE_STATE_NORMAL;
break;
case DEMANGLE_STATE_NORMAL:
switch (*curchar) {
case '?':
state = DEMANGLE_STATE_QDECODE;
break;
case '@':
state = DEMANGLE_STATE_STOP;
break;
default:
*curresult++ = DEMANGLE_SAFE_CHAR(*curchar);
state = DEMANGLE_STATE_NORMAL;
break;
}
break;
/* found a '?' */
case DEMANGLE_STATE_QDECODE:
state = DEMANGLE_STATE_NORMAL;
/* there are certain shortcuts, like
"?3" means ":"
*/
switch (*curchar) {
case '1':
*curresult++ = '/';
break;
case '2':
*curresult++ = '\\';
break;
case '3':
*curresult++ = ':';
break;
case '4':
*curresult++ = '.';
break;
case '5':
*curresult++ = ' ';
break;
case '6':
*curresult++ = '\\';
*curresult++ = 'n';
break;
case '8':
*curresult++ = '\'';
break;
case '9':
*curresult++ = '-';
break;
/* any other arbitrary ASCII value can
be stored by prefixing it with ?$
*/
case '$':
state = DEMANGLE_STATE_DOLLAR_1;
}
break;
case DEMANGLE_STATE_DOLLAR_1:
/* first digit of ?$ notation. All digits
are hex, represented starting with the
capital leter 'A' such that 'A' means 0x0,
'B' means 0x1, 'K' means 0xA
*/
hex_state = (*curchar - 'A') * 0x10;
state = DEMANGLE_STATE_DOLLAR_2;
break;
case DEMANGLE_STATE_DOLLAR_2:
/* same mechanism as above */
hex_state += (*curchar - 'A');
if (hex_state) {
*curresult++ = DEMANGLE_SAFE_CHAR(hex_state);
have_null_char = 0;
}
else {
have_null_char = 1;
}
state = DEMANGLE_STATE_NORMAL;
break;
case DEMANGLE_STATE_STOP:
break;
}
curchar++;
}
/* add the appropriate termination depending
if we completed the string or not */
if (!have_null_char)
strcpy(curresult, "...\"");
else
strcpy(curresult, "\"");
} else {
retval = strdup(demangleBuf);
}
}
else
{
/*
** fall back to normal.
*/
retval = strdup(inSymbol);
}
}
else if('_' == inSymbol[0])
{
retval = strdup(inSymbol + 1);
}
else
{
retval = strdup(inSymbol);
}
/*
** May need to rewrite the symbol if an import.
*/
if(NULL != retval && isImport)
{
const char importPrefix[] = "__declspec(dllimport) ";
char importBuf[0x200];
int printRes = 0;
printRes = _snprintf(importBuf, sizeof(importBuf), "%s%s", importPrefix, retval);
free(retval);
retval = NULL;
if(printRes > 0)
{
retval = strdup(importBuf);
}
}
}
#else /* F_DEMANGLE */
retval = strdup(inSymbol);
#endif /* F_DEMANGLE */
return retval;
}
int readmap(Options* inOptions, MSMap_Module* inModule)
/*
** Read the input line by line, adding it to the module.
*/
{
int retval = 0;
char lineBuffer[0x400];
char* current = NULL;
MSMap_ReadState fsm;
int len = 0;
int forceContinue = 0;
memset(&fsm, 0, sizeof(fsm));
/*
** Read the map file line by line.
** We keep a simple state machine to determine what we're looking at.
*/
while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput))
{
if(forceContinue)
{
/*
** Used to skip anticipated blank lines.
*/
forceContinue--;
continue;
}
current = skipWhite(lineBuffer);
trimWhite(current);
len = strlen(current);
if(fsm.mHasModule)
{
if(fsm.mHasTimestamp)
{
if(fsm.mHasPreferredLoadAddress)
{
if(fsm.mHasSegmentData)
{
if(fsm.mHasPublicSymbolData)
{
if(fsm.mHasEntryPoint)
{
if(fsm.mFoundStaticSymbols)
{
/*
** A blank line means we've reached the end of all static symbols.
*/
if(len)
{
/*
** We're adding a new symbol.
** Make sure we have room for it.
*/
if(inModule->mSymbolCapacity == inModule->mSymbolCount)
{
void* moved = NULL;
moved = realloc(inModule->mSymbols, sizeof(MSMap_Symbol) * (inModule->mSymbolCapacity + MSMAP_SYMBOL_GROWBY));
if(NULL != moved)
{
inModule->mSymbolCapacity += MSMAP_SYMBOL_GROWBY;
inModule->mSymbols = (MSMap_Symbol*)moved;
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, inModule->mModule, "Unable to grow symbols.");
}
}
if(0 == retval && inModule->mSymbolCapacity > inModule->mSymbolCount)
{
MSMap_Symbol* theSymbol = NULL;
unsigned index = 0;
int scanRes = 0;
char symbolBuf[0x200];
index = inModule->mSymbolCount;
inModule->mSymbolCount++;
theSymbol = (inModule->mSymbols + index);
memset(theSymbol, 0, sizeof(MSMap_Symbol));
theSymbol->mScope = STATIC;
scanRes = sscanf(current, "%x:%x %s %x", (unsigned*)&(theSymbol->mPrefix), (unsigned*)&(theSymbol->mOffset), symbolBuf, (unsigned*)&(theSymbol->mRVABase));
if(4 == scanRes)
{
theSymbol->mSymbol = symdup(symbolBuf);
if(0 == retval)
{
if(NULL != theSymbol->mSymbol)
{
char *last = lastWord(current);
theSymbol->mObject = strdup(last);
if(NULL == theSymbol->mObject)
{
retval = __LINE__;
ERROR_REPORT(retval, last, "Unable to copy object name.");
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, symbolBuf, "Unable to copy symbol name.");
}
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, inModule->mModule, "Unable to scan static symbols.");
}
}
}
else
{
/*
** All done.
*/
break;
}
}
else
{
/*
** Static symbols are optional.
** If no static symbols we're done.
** Otherwise, set the flag such that it will work more.
*/
if(0 == strcmp(current, "Static symbols"))
{
fsm.mFoundStaticSymbols = __LINE__;
forceContinue = 1;
}
else
{
/*
** All done.
*/
break;
}
}
}
else
{
int scanRes = 0;
scanRes = sscanf(current, "entry point at %x:%x", (unsigned*)&(inModule->mEntryPrefix), (unsigned*)&(inModule->mEntryOffset));
if(2 == scanRes)
{
fsm.mHasEntryPoint = __LINE__;
forceContinue = 1;
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, current, "Unable to obtain entry point.");
}
}
}
else
{
/*
** Skip the N lines of public symbol data (column headers).
*/
if(2 <= fsm.mHasPublicSymbolDataSkippedLines)
{
/*
** A blank line indicates end of public symbols.
*/
if(len)
{
/*
** We're adding a new symbol.
** Make sure we have room for it.
*/
if(inModule->mSymbolCapacity == inModule->mSymbolCount)
{
void* moved = NULL;
moved = realloc(inModule->mSymbols, sizeof(MSMap_Symbol) * (inModule->mSymbolCapacity + MSMAP_SYMBOL_GROWBY));
if(NULL != moved)
{
inModule->mSymbolCapacity += MSMAP_SYMBOL_GROWBY;
inModule->mSymbols = (MSMap_Symbol*)moved;
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, inModule->mModule, "Unable to grow symbols.");
}
}
if(0 == retval && inModule->mSymbolCapacity > inModule->mSymbolCount)
{
MSMap_Symbol* theSymbol = NULL;
unsigned index = 0;
int scanRes = 0;
char symbolBuf[0x200];
index = inModule->mSymbolCount;
inModule->mSymbolCount++;
theSymbol = (inModule->mSymbols + index);
memset(theSymbol, 0, sizeof(MSMap_Symbol));
theSymbol->mScope = PUBLIC;
scanRes = sscanf(current, "%x:%x %s %x", (unsigned*)&(theSymbol->mPrefix), (unsigned*)&(theSymbol->mOffset), symbolBuf, (unsigned *)&(theSymbol->mRVABase));
if(4 == scanRes)
{
theSymbol->mSymbol = symdup(symbolBuf);
if(NULL != theSymbol->mSymbol)
{
char *last = lastWord(current);
theSymbol->mObject = strdup(last);
if(NULL != theSymbol->mObject)
{
/*
** Finally, attempt to lookup the actual size of the symbol
** if there is a symbol DB available.
*/
retval = fillSymbolSizeFromDB(inOptions, inModule, theSymbol, symbolBuf);
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, last, "Unable to copy object name.");
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, symbolBuf, "Unable to copy symbol name.");
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, inModule->mModule, "Unable to scan public symbols.");
}
}
}
else
{
fsm.mHasPublicSymbolData = __LINE__;
}
}
else
{
fsm.mHasPublicSymbolDataSkippedLines++;
}
}
}
else
{
/*
** Skip the first line of segment data (column headers).
** Mark that we've begun grabbing segement data.
*/
if(fsm.mSegmentDataSkippedLine)
{
/*
** A blank line means end of the segment data.
*/
if(len)
{
/*
** We're adding a new segment.
** Make sure we have room for it.
*/
if(inModule->mSegmentCapacity == inModule->mSegmentCount)
{
void* moved = NULL;
moved = realloc(inModule->mSegments, sizeof(MSMap_Segment) * (inModule->mSegmentCapacity + MSMAP_SEGMENT_GROWBY));
if(NULL != moved)
{
inModule->mSegmentCapacity += MSMAP_SEGMENT_GROWBY;
inModule->mSegments = (MSMap_Segment*)moved;
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, inModule->mModule, "Unable to grow segments.");
}
}
if(0 == retval && inModule->mSegmentCapacity > inModule->mSegmentCount)
{
MSMap_Segment* theSegment = NULL;
unsigned index = 0;
char classBuf[0x10];
char nameBuf[0x20];
int scanRes = 0;
index = inModule->mSegmentCount;
inModule->mSegmentCount++;
theSegment = (inModule->mSegments + index);
memset(theSegment, 0, sizeof(MSMap_Segment));
scanRes = sscanf(current, "%x:%x %xH %s %s", (unsigned*)&(theSegment->mPrefix), (unsigned*)&(theSegment->mOffset), (unsigned*)&(theSegment->mLength), nameBuf, classBuf);
if(5 == scanRes)
{
if('.' == nameBuf[0])
{
theSegment->mSegment = strdup(&nameBuf[1]);
}
else
{
theSegment->mSegment = strdup(nameBuf);
}
if(NULL != theSegment->mSegment)
{
if(0 == strcmp("DATA", classBuf))
{
theSegment->mClass = DATA;
}
else if(0 == strcmp("CODE", classBuf))
{
theSegment->mClass = CODE;
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, classBuf, "Unrecognized segment class.");
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, nameBuf, "Unable to copy segment name.");
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, inModule->mModule, "Unable to scan segments.");
}
}
}
else
{
fsm.mHasSegmentData = __LINE__;
}
}
else
{
fsm.mSegmentDataSkippedLine = __LINE__;
}
}
}
else
{
int scanRes = 0;
/*
** The PLA has a particular format.
*/
scanRes = sscanf(current, "Preferred load address is %x", (unsigned*)&(inModule->mPreferredLoadAddress));
if(1 == scanRes)
{
fsm.mHasPreferredLoadAddress = __LINE__;
forceContinue = 1;
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, current, "Unable to obtain preferred load address.");
}
}
}
else
{
int scanRes = 0;
/*
** The timestamp has a particular format.
*/
scanRes = sscanf(current, "Timestamp is %x", (unsigned*)&(inModule->mTimestamp));
if(1 == scanRes)
{
fsm.mHasTimestamp = __LINE__;
forceContinue = 1;
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, current, "Unable to obtain timestamp.");
}
}
}
else
{
/*
** The module is on a line by itself.
*/
inModule->mModule = strdup(current);
if(NULL != inModule->mModule)
{
fsm.mHasModule = __LINE__;
forceContinue = 1;
if(0 != inOptions->mMatchModuleCount)
{
unsigned matchLoop = 0;
/*
** If this module name doesn't match, then bail.
** Compare in a case sensitive manner, exact match only.
*/
for(matchLoop = 0; matchLoop < inOptions->mMatchModuleCount; matchLoop++)
{
if(0 == strcmp(inModule->mModule, inOptions->mMatchModules[matchLoop]))
{
break;
}
}
if(matchLoop == inOptions->mMatchModuleCount)
{
/*
** A match did not occur, bail out of read loop.
** No error, however.
*/
break;
}
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, current, "Unable to obtain module.");
}
}
}
if(0 == retval && 0 != ferror(inOptions->mInput))
{
retval = __LINE__;
ERROR_REPORT(retval, inOptions->mInputName, "Unable to read file.");
}
return retval;
}
static int qsortRVABase(const void* in1, const void* in2)
/*
** qsort callback to sort the symbols by their RVABase.
*/
{
MSMap_Symbol* sym1 = (MSMap_Symbol*)in1;
MSMap_Symbol* sym2 = (MSMap_Symbol*)in2;
int retval = 0;
if(sym1->mRVABase < sym2->mRVABase)
{
retval = -1;
}
else if(sym1->mRVABase > sym2->mRVABase)
{
retval = 1;
}
return retval;
}
static int tsvout(Options* inOptions, unsigned inSize, MSMap_SegmentClass inClass, MSMap_SymbolScope inScope, const char* inModule, const char* inSegment, const char* inObject, const char* inSymbol)
/*
** Output a line of map information seperated by tabs.
** Some items (const char*), if not present, will receive a default value.
*/
{
int retval = 0;
/*
** No need to output on no size.
** This can happen with zero sized segments,
** or an imported symbol which has multiple names (one will count).
*/
if(0 != inSize)
{
char objectBuf[0x100];
const char* symScope = NULL;
const char* segClass = NULL;
const char* undefined = "UNDEF";
/*
** Fill in unspecified values.
*/
if(NULL == inObject)
{
sprintf(objectBuf, "%s:%s:%s", undefined, inModule, inSegment);
inObject = objectBuf;
}
if(NULL == inSymbol)
{
inSymbol = inObject;
}
/*
** Convert some enumerations to text.
*/
switch(inClass)
{
case CODE:
segClass = "CODE";
break;
case DATA:
segClass = "DATA";
break;
default:
retval = __LINE__;
ERROR_REPORT(retval, "", "Unable to determine class for output.");
break;
}
switch(inScope)
{
case PUBLIC:
symScope = "PUBLIC";
break;
case STATIC:
symScope = "STATIC";
break;
case UNDEFINED:
symScope = undefined;
break;
default:
retval = __LINE__;
ERROR_REPORT(retval, "", "Unable to determine scope for symbol.");
break;
}
if(0 == retval)
{
int printRes = 0;
printRes = fprintf(inOptions->mOutput,
"%.8X\t%s\t%s\t%s\t%s\t%s\t%s\n",
inSize,
segClass,
symScope,
inModule,
inSegment,
inObject,
inSymbol
);
if(0 > printRes)
{
retval = __LINE__;
ERROR_REPORT(retval, inOptions->mOutputName, "Unable to output tsv data.");
}
}
}
return retval;
}
void cleanModule(MSMap_Module* inModule)
{
unsigned loop = 0;
for(loop = 0; loop < inModule->mSymbolCount; loop++)
{
CLEANUP(inModule->mSymbols[loop].mObject);
CLEANUP(inModule->mSymbols[loop].mSymbol);
}
CLEANUP(inModule->mSymbols);
for(loop = 0; loop < inModule->mSegmentCount; loop++)
{
CLEANUP(inModule->mSegments[loop].mSegment);
}
CLEANUP(inModule->mSegments);
CLEANUP(inModule->mModule);
memset(inModule, 0, sizeof(MSMap_Module));
}
int map2tsv(Options* inOptions)
/*
** Read all input.
** Output tab seperated value data.
*/
{
int retval = 0;
MSMap_Module module;
memset(&module, 0, sizeof(module));
/*
** Read in the map file.
*/
retval = readmap(inOptions, &module);
if(0 == retval)
{
unsigned symLoop = 0;
MSMap_Symbol* symbol = NULL;
unsigned secLoop = 0;
MSMap_Segment* section = NULL;
unsigned size = 0;
unsigned dbSize = 0;
unsigned offsetSize = 0;
unsigned endOffset = 0;
/*
** Quick sort the symbols via RVABase.
*/
qsort(module.mSymbols, module.mSymbolCount, sizeof(MSMap_Symbol), qsortRVABase);
/*
** Go through all the symbols (in order by sort).
** Output their sizes.
*/
for(symLoop = 0; 0 == retval && symLoop < module.mSymbolCount; symLoop++)
{
symbol = &module.mSymbols[symLoop];
section = getSymbolSection(&module, symbol);
/*
** Use the symbol DB size if available.
*/
dbSize = symbol->mSymDBSize;
/*
** Guess using offsets.
** Is there a next symbol available? If so, it's start offset is the end of this symbol.
** Otherwise, our section offset + length is the end of this symbol.
**
** The trick is, the DB size can not go beyond the offset size, for sanity.
*/
/*
** Try next symbol, but only if in same section.
** If still not, use the end of the segment.
** This implies we were the last symbol in the segment.
*/
if((symLoop + 1) < module.mSymbolCount)
{
MSMap_Symbol* nextSymbol = NULL;
MSMap_Segment* nextSection = NULL;
nextSymbol = &module.mSymbols[symLoop + 1];
nextSection = getSymbolSection(&module, nextSymbol);
if(section == nextSection)
{
endOffset = nextSymbol->mOffset;
}
else
{
endOffset = section->mOffset + section->mLength;
}
}
else
{
endOffset = section->mOffset + section->mLength;
}
/*
** Can now guess at size.
*/
offsetSize = endOffset - symbol->mOffset;
/*
** Now, determine which size to use.
** This is really a sanity check as well.
*/
size = offsetSize;
if(0 != dbSize)
{
if(dbSize < offsetSize)
{
size = dbSize;
}
}
/*
** Output the symbol with the size.
*/
retval = tsvout(inOptions,
size,
section->mClass,
symbol->mScope,
module.mModule,
section->mSegment,
symbol->mObject,
symbol->mSymbol
);
/*
** Make sure we mark this amount of space as used in the section.
*/
section->mUsed += size;
}
/*
** Go through the sections, and those whose length is longer than the
** amount of space used, output dummy filler values.
*/
for(secLoop = 0; 0 == retval && secLoop < module.mSegmentCount; secLoop++)
{
section = &module.mSegments[secLoop];
if(section->mUsed < section->mLength)
{
retval = tsvout(inOptions,
section->mLength - section->mUsed,
section->mClass,
UNDEFINED,
module.mModule,
section->mSegment,
NULL,
NULL
);
}
}
}
/*
** Cleanup.
*/
cleanModule(&module);
return retval;
}
int initOptions(Options* outOptions, int inArgc, char** inArgv)
/*
** returns int 0 if successful.
*/
{
int retval = 0;
int loop = 0;
int switchLoop = 0;
int match = 0;
const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
Switch* current = NULL;
/*
** Set any defaults.
*/
memset(outOptions, 0, sizeof(Options));
outOptions->mProgramName = inArgv[0];
outOptions->mInput = stdin;
outOptions->mInputName = strdup("stdin");
outOptions->mOutput = stdout;
outOptions->mOutputName = strdup("stdout");
if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName)
{
retval = __LINE__;
ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup.");
}
/*
** Go through and attempt to do the right thing.
*/
for(loop = 1; loop < inArgc && 0 == retval; loop++)
{
match = 0;
current = NULL;
for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++)
{
if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop]))
{
match = __LINE__;
}
else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop]))
{
match = __LINE__;
}
if(match)
{
if(gSwitches[switchLoop]->mHasValue)
{
/*
** Attempt to absorb next option to fullfill value.
*/
if(loop + 1 < inArgc)
{
loop++;
current = gSwitches[switchLoop];
current->mValue = inArgv[loop];
}
}
else
{
current = gSwitches[switchLoop];
}
break;
}
}
if(0 == match)
{
outOptions->mHelp = __LINE__;
retval = __LINE__;
ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch.");
}
else if(NULL == current)
{
outOptions->mHelp = __LINE__;
retval = __LINE__;
ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value.");
}
else
{
/*
** Do something based on address/swtich.
*/
if(current == &gInputSwitch)
{
CLEANUP(outOptions->mInputName);
if(NULL != outOptions->mInput && stdin != outOptions->mInput)
{
fclose(outOptions->mInput);
outOptions->mInput = NULL;
}
outOptions->mInput = fopen(current->mValue, "r");
if(NULL == outOptions->mInput)
{
retval = __LINE__;
ERROR_REPORT(retval, current->mValue, "Unable to open input file.");
}
else
{
outOptions->mInputName = strdup(current->mValue);
if(NULL == outOptions->mInputName)
{
retval = __LINE__;
ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
}
}
}
else if(current == &gOutputSwitch)
{
CLEANUP(outOptions->mOutputName);
if(NULL != outOptions->mOutput && stdout != outOptions->mOutput)
{
fclose(outOptions->mOutput);
outOptions->mOutput = NULL;
}
outOptions->mOutput = fopen(current->mValue, "a");
if(NULL == outOptions->mOutput)
{
retval = __LINE__;
ERROR_REPORT(retval, current->mValue, "Unable to open output file.");
}
else
{
outOptions->mOutputName = strdup(current->mValue);
if(NULL == outOptions->mOutputName)
{
retval = __LINE__;
ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
}
}
}
else if(current == &gHelpSwitch)
{
outOptions->mHelp = __LINE__;
}
else if(current == &gMatchModuleSwitch)
{
void* moved = NULL;
/*
** Add the value to the list of allowed module names.
*/
moved = realloc(outOptions->mMatchModules, sizeof(char*) * (outOptions->mMatchModuleCount + 1));
if(NULL != moved)
{
outOptions->mMatchModules = (char**)moved;
outOptions->mMatchModules[outOptions->mMatchModuleCount] = strdup(current->mValue);
if(NULL != outOptions->mMatchModules[outOptions->mMatchModuleCount])
{
outOptions->mMatchModuleCount++;
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, current->mValue, "Unable to duplicate string.");
}
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, current->mValue, "Unable to allocate space for string.");
}
}
else if(current == &gSymDBSwitch)
{
CLEANUP(outOptions->mSymDBName);
outOptions->mSymDBName = strdup(current->mValue);
if(NULL == outOptions->mSymDBName)
{
retval = __LINE__;
ERROR_REPORT(retval, current->mValue, "Unable to duplicate symbol db name.");
}
}
else if(current == &gBatchModeSwitch)
{
outOptions->mBatchMode = __LINE__;
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, current->mLongName, "No hanlder for command line switch.");
}
}
}
return retval;
}
void cleanOptions(Options* inOptions)
/*
** Clean up any open handles, et. al.
*/
{
CLEANUP(inOptions->mInputName);
if(NULL != inOptions->mInput && stdin != inOptions->mInput)
{
fclose(inOptions->mInput);
}
CLEANUP(inOptions->mOutputName);
if(NULL != inOptions->mOutput && stdout != inOptions->mOutput)
{
fclose(inOptions->mOutput);
}
while(0 != inOptions->mMatchModuleCount)
{
inOptions->mMatchModuleCount--;
CLEANUP(inOptions->mMatchModules[inOptions->mMatchModuleCount]);
}
CLEANUP(inOptions->mMatchModules);
cleanSymDB(&inOptions->mSymDB);
memset(inOptions, 0, sizeof(Options));
}
void showHelp(Options* inOptions)
/*
** Show some simple help text on usage.
*/
{
int loop = 0;
const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
const char* valueText = NULL;
printf("usage:\t%s [arguments]\n", inOptions->mProgramName);
printf("\n");
printf("arguments:\n");
for(loop = 0; loop < switchCount; loop++)
{
if(gSwitches[loop]->mHasValue)
{
valueText = " <value>";
}
else
{
valueText = "";
}
printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText);
printf("\t %s%s", gSwitches[loop]->mShortName, valueText);
printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription);
}
printf("This tool normalizes MS linker .map files for use by other tools.\n");
}
int batchMode(Options* inOptions)
/*
** Batch mode means that the input file is actually a list of map files.
** We simply swap out our input file names while we do this.
*/
{
int retval = 0;
char lineBuf[0x400];
FILE* realInput = NULL;
char* realInputName = NULL;
FILE* mapFile = NULL;
int finalRes = 0;
realInput = inOptions->mInput;
realInputName = inOptions->mInputName;
while(0 == retval && NULL != fgets(lineBuf, sizeof(lineBuf), realInput))
{
trimWhite(lineBuf);
/*
** Skip/allow blank lines.
*/
if('\0' == lineBuf[0])
{
continue;
}
/*
** Override what we believe to be the input for this line.
*/
inOptions->mInputName = lineBuf;
inOptions->mInput = fopen(lineBuf, "r");
if(NULL != inOptions->mInput)
{
int mapRes = 0;
/*
** Do it.
*/
mapRes = map2tsv(inOptions);
/*
** We report the first error that we encounter, but we continue.
** This is batch mode after all.
*/
if(0 == finalRes)
{
finalRes = mapRes;
}
/*
** Close the input file.
*/
fclose(inOptions->mInput);
}
else
{
retval = __LINE__;
ERROR_REPORT(retval, lineBuf, "Unable to open map file.");
break;
}
}
if(0 == retval && 0 != ferror(realInput))
{
retval = __LINE__;
ERROR_REPORT(retval, realInputName, "Unable to read file.");
}
/*
** Restore what we've swapped.
*/
inOptions->mInput = realInput;
inOptions->mInputName = realInputName;
/*
** Report first map file error if there were no other operational
** problems.
*/
if(0 == retval)
{
retval = finalRes;
}
return retval;
}
int main(int inArgc, char** inArgv)
{
int retval = 0;
Options options;
retval = initOptions(&options, inArgc, inArgv);
if(options.mHelp)
{
showHelp(&options);
}
else if(0 == retval)
{
if(options.mBatchMode)
{
retval = batchMode(&options);
}
else
{
retval = map2tsv(&options);
}
}
cleanOptions(&options);
return retval;
}