| Index: third_party/codesighs/msmap2tsv.c
|
| ===================================================================
|
| --- third_party/codesighs/msmap2tsv.c (revision 0)
|
| +++ third_party/codesighs/msmap2tsv.c (revision 0)
|
| @@ -0,0 +1,2237 @@
|
| +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
| + *
|
| + * ***** BEGIN LICENSE BLOCK *****
|
| + * Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
| + *
|
| + * 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 the Initial Developer are Copyright (C) 2002
|
| + * the Initial Developer. All Rights Reserved.
|
| + *
|
| + * Contributor(s):
|
| + * Garrett Arch Blythe, 03-October-2002
|
| + *
|
| + * Alternatively, the contents of this file may be used under the terms of
|
| + * either the GNU General Public License Version 2 or later (the "GPL"), or
|
| + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
| + * in which case the provisions of the GPL or the LGPL are applicable instead
|
| + * of those above. If you wish to allow use of your version of this file only
|
| + * under the terms of either the GPL or the LGPL, and not to allow others to
|
| + * use your version of this file under the terms of the MPL, indicate your
|
| + * decision by deleting the provisions above and replace them with the notice
|
| + * and other provisions required by the GPL or the LGPL. If you do not delete
|
| + * the provisions above, a recipient may use your version of this file under
|
| + * the terms of any one of the MPL, the GPL or the LGPL.
|
| + *
|
| + * ***** END LICENSE BLOCK ***** */
|
| +
|
| +#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 Whether 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 separated 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 = §ion->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 separated 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 separated 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);
|
| + if (!section)
|
| + continue;
|
| +
|
| + /*
|
| + ** Use the symbol DB size if available.
|
| + */
|
| + dbSize = symbol->mSymDBSize;
|
| +
|
| + /*
|
| + ** Guess using offsets.
|
| + ** Is there a next symbol available? If so, its 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 && 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 handler 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;
|
| +}
|
| +
|
|
|
| Property changes on: third_party/codesighs/msmap2tsv.c
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|