| /* -*- 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; |
| } |
| |