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 |