| /* -*- 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 msdump2symdb.c code, released |
| * Jan 16, 2003. |
| * |
| * 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, 16-January-2003 |
| * |
| * 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 <errno.h> |
| |
| #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_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. |
| */ |
| { |
| const char* mProgramName; |
| FILE* mInput; |
| char* mInputName; |
| FILE* mOutput; |
| char* mOutputName; |
| int mHelp; |
| } |
| 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* gSwitches[] = { |
| &gInputSwitch, |
| &gOutputSwitch, |
| &gHelpSwitch |
| }; |
| |
| |
| typedef struct __struct_MSDump_Symbol |
| /* |
| ** Struct to hold infomration on a symbol. |
| ** |
| ** mSize Size of the symbol once all work is complete. |
| ** mOffset Offset of the symbol in the section. |
| ** mName Symbolic name. |
| */ |
| { |
| unsigned mSize; |
| unsigned mOffset; |
| char* mName; |
| } |
| MSDump_Symbol; |
| |
| |
| typedef struct __struct_MSDump_Section |
| /* |
| ** Struct for holding information on a section. |
| ** |
| ** mLength Length of the section in bytes. |
| ** mUsed Number of bytes used in the section thus far. |
| ** Should eventually match mLength after work is done. |
| ** mType Type of section, as string (.data, .text, et. al.) |
| ** mSymbols Symbols found inside the section. |
| ** mSymbolCount Number of symbols in array. |
| */ |
| { |
| unsigned mLength; |
| unsigned mUsed; |
| char* mType; |
| |
| MSDump_Symbol* mSymbols; |
| unsigned mSymbolCount; |
| } |
| MSDump_Section; |
| |
| |
| typedef struct __struct_MSDump_Object |
| /* |
| ** Struct for holding object's data. |
| */ |
| { |
| char* mObject; |
| |
| MSDump_Section* mSections; |
| unsigned mSectionCount; |
| } |
| MSDump_Object; |
| |
| |
| typedef struct __struct_MSDump_ReadState |
| /* |
| ** State flags while reading the input gives us hints on what to do. |
| ** |
| ** mSkipLines Number of lines to skip without parsing. |
| ** mSectionDetails Section information next, like line length. |
| ** mCurrentObject Object file we are dealing with. |
| */ |
| { |
| unsigned mSkipLines; |
| unsigned mSectionDetails; |
| MSDump_Object* mCurrentObject; |
| } |
| MSDump_ReadState; |
| |
| |
| typedef struct __struct_MSDump_Container |
| /* |
| ** Umbrella container for all data encountered. |
| */ |
| { |
| MSDump_ReadState mReadState; |
| |
| MSDump_Object* mObjects; |
| unsigned mObjectCount; |
| } |
| MSDump_Container; |
| |
| |
| 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; |
| } |
| } |
| } |
| |
| |
| const char* skipWhite(const char* inString) |
| /* |
| ** Return pointer to first non white space character. |
| */ |
| { |
| const char* retval = inString; |
| |
| while('\0' != *retval && isspace(*retval)) |
| { |
| retval++; |
| } |
| |
| return retval; |
| } |
| |
| |
| const char* skipNonWhite(const char* inString) |
| /* |
| ** Return pointer to first white space character. |
| */ |
| { |
| const char* retval = inString; |
| |
| while('\0' != *retval && !isspace(*retval)) |
| { |
| retval++; |
| } |
| |
| return retval; |
| } |
| |
| |
| void slash2bs(char* inString) |
| /* |
| ** Change any forward slash to a backslash. |
| */ |
| { |
| char* slash = inString; |
| |
| while(NULL != (slash = strchr(slash, '/'))) |
| { |
| *slash = '\\'; |
| slash++; |
| } |
| } |
| |
| |
| const char* skipToArg(const char* inString, unsigned inArgIndex) |
| /* |
| ** Return pointer either to the arg or NULL. |
| ** 1 indexed. |
| */ |
| { |
| const char* retval = NULL; |
| |
| while(0 != inArgIndex && '\0' != *inString) |
| { |
| inArgIndex--; |
| |
| inString = skipWhite(inString); |
| if(0 != inArgIndex) |
| { |
| inString = skipNonWhite(inString); |
| } |
| } |
| |
| if('\0' != *inString) |
| { |
| retval = inString; |
| } |
| |
| return retval; |
| } |
| |
| |
| const char* getLastArg(const char* inString) |
| /* |
| ** Return pointer to last arg in string. |
| */ |
| { |
| const char* retval = NULL; |
| int length = 0; |
| int sawString = 0; |
| |
| length = strlen(inString); |
| while(0 != length) |
| { |
| length--; |
| |
| if(0 == sawString) |
| { |
| if(0 == isspace(inString[length])) |
| { |
| sawString = __LINE__; |
| } |
| } |
| else |
| { |
| if(0 != isspace(inString[length])) |
| { |
| retval = inString + length + 1; |
| } |
| } |
| } |
| |
| return retval; |
| } |
| |
| |
| int processLine(Options* inOptions, MSDump_Container* inContainer, const char* inLine) |
| /* |
| ** Handle one line at a time. |
| ** Looking for several different types of lines. |
| ** Ignore all other lines. |
| ** The container is the state machine. |
| ** returns 0 on no error. |
| */ |
| { |
| int retval = 0; |
| |
| /* |
| ** Check to see if we were expecting section details. |
| */ |
| if(0 != inContainer->mReadState.mSectionDetails) |
| { |
| const char* length = NULL; |
| unsigned sectionIndex = 0; |
| |
| /* |
| ** Detail is a 1 based index.... |
| ** Reset. |
| */ |
| sectionIndex = inContainer->mReadState.mSectionDetails - 1; |
| inContainer->mReadState.mSectionDetails = 0; |
| |
| if(0 == strncmp(" Section length", inLine, 18)) |
| { |
| const char* sectionLength = NULL; |
| unsigned numericLength = 0; |
| char* endScan = NULL; |
| |
| sectionLength = skipWhite(inLine + 18); |
| |
| errno = 0; |
| numericLength = strtoul(sectionLength, &endScan, 16); |
| if(0 == errno && endScan != sectionLength) |
| { |
| inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mLength = numericLength; |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, inLine, "Cannot scan for section length."); |
| } |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, inLine, "Cannot parse section line."); |
| } |
| } |
| /* |
| ** Check for switching object file symbols. |
| */ |
| else if(0 == strncmp("Dump of file ", inLine, 13)) |
| { |
| const char* dupMe = inLine + 13; |
| char* dup = NULL; |
| |
| dup = strdup(dupMe); |
| if(NULL != dup) |
| { |
| void* growth = NULL; |
| |
| trimWhite(dup); |
| slash2bs(dup); |
| |
| |
| growth = realloc(inContainer->mObjects, (inContainer->mObjectCount + 1) * sizeof(MSDump_Object)); |
| if(NULL != growth) |
| { |
| unsigned int index = inContainer->mObjectCount; |
| |
| inContainer->mObjectCount++; |
| inContainer->mObjects = growth; |
| memset(inContainer->mObjects + index, 0, sizeof(MSDump_Object)); |
| |
| inContainer->mObjects[index].mObject = dup; |
| |
| /* |
| ** Reset the read state for this new object. |
| */ |
| memset(&inContainer->mReadState, 0, sizeof(MSDump_ReadState)); |
| |
| /* |
| ** Record our current object file. |
| */ |
| inContainer->mReadState.mCurrentObject = inContainer->mObjects + index; |
| |
| /* |
| ** We can skip a few lines. |
| */ |
| inContainer->mReadState.mSkipLines = 4; |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, dup, "Unable to grow object array."); |
| free(dup); |
| } |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, dupMe, "Unable to copy string."); |
| |
| } |
| } |
| /* |
| ** Check for a symbol dump or a section header. |
| */ |
| else if(isxdigit(*inLine) && isxdigit(*(inLine + 1)) && isxdigit(*(inLine + 2))) |
| { |
| const char* sectionString = NULL; |
| |
| /* |
| ** Determine the section for this line. |
| ** Ignore DEBUG sections. |
| */ |
| sectionString = skipToArg(inLine, 3); |
| if(NULL != sectionString) |
| { |
| if(0 != strncmp(sectionString, "DEBUG", 5) && 0 != strncmp(sectionString, "ABS", 3) && 0 != strncmp(sectionString, "UNDEF", 5)) |
| { |
| /* |
| ** MUST start with "SECT" |
| */ |
| if(0 == strncmp(sectionString, "SECT", 4)) |
| { |
| unsigned sectionIndex1 = 0; |
| |
| char *endScan = NULL; |
| |
| sectionString += 4; |
| |
| /* |
| ** Convert the remaining string to an index. |
| ** It will be 1 based. |
| */ |
| errno = 0; |
| sectionIndex1 = strtoul(sectionString, &endScan, 16); |
| if(0 == errno && endScan != sectionString && 0 != sectionIndex1) |
| { |
| unsigned sectionIndex = sectionIndex1 - 1; |
| |
| /* |
| ** Is this a new section? Assumed to be ascending. |
| ** Or is this a symbol in the section? |
| */ |
| if(sectionIndex1 > inContainer->mReadState.mCurrentObject->mSectionCount) |
| { |
| const char* typeArg = NULL; |
| |
| /* |
| ** New Section, figure out the type. |
| */ |
| typeArg = skipToArg(sectionString, 5); |
| if(NULL != typeArg) |
| { |
| char* typeDup = NULL; |
| |
| /* |
| ** Skip the leading period before duping. |
| */ |
| if('.' == *typeArg) |
| { |
| typeArg++; |
| } |
| typeDup = strdup(typeArg); |
| |
| if(NULL != typeDup) |
| { |
| void* moved = NULL; |
| char* nonWhite = NULL; |
| |
| /* |
| ** Terminate the duplicate after the section type. |
| */ |
| nonWhite = (char*)skipNonWhite(typeDup); |
| if(NULL != nonWhite) |
| { |
| *nonWhite = '\0'; |
| } |
| |
| /* |
| ** Create more space for the section in the object... |
| */ |
| moved = realloc(inContainer->mReadState.mCurrentObject->mSections, sizeof(MSDump_Section) * sectionIndex1); |
| if(NULL != moved) |
| { |
| unsigned oldCount = inContainer->mReadState.mCurrentObject->mSectionCount; |
| |
| inContainer->mReadState.mCurrentObject->mSections = (MSDump_Section*)moved; |
| inContainer->mReadState.mCurrentObject->mSectionCount = sectionIndex1; |
| memset(&inContainer->mReadState.mCurrentObject->mSections[oldCount], 0, sizeof(MSDump_Section) * (sectionIndex1 - oldCount)); |
| |
| /* |
| ** Other section details. |
| */ |
| inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mType = typeDup; |
| |
| |
| /* |
| ** Mark it so that we look for the length on the next line. |
| ** This happens on next entry into the read state. |
| */ |
| inContainer->mReadState.mSectionDetails = sectionIndex1; |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, inLine, "Unable to grow for new section."); |
| free(typeDup); |
| } |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, typeArg, "Unable to duplicate type."); |
| } |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, inLine, "Unable to determine section type."); |
| } |
| |
| } |
| else |
| { |
| const char* offsetArg = NULL; |
| const char* classArg = NULL; |
| unsigned classWords = 1; |
| const char* symbolArg = NULL; |
| |
| /* |
| ** This is an section we've seen before, and must list a symbol. |
| ** Figure out the things we want to know about the symbol, e.g. size. |
| ** We will ignore particular classes of symbols. |
| */ |
| |
| offsetArg = skipToArg(inLine, 2); |
| |
| classArg = skipToArg(offsetArg, 4); |
| if(0 == strncmp(classArg, "()", 2)) |
| { |
| classArg = skipToArg(classArg, 2); |
| } |
| if(0 == strncmp(classArg, ".bf or.ef", 9)) |
| { |
| classWords = 2; |
| } |
| |
| symbolArg = skipToArg(classArg, 3 + (classWords - 1)); |
| |
| /* |
| ** Skip particular lines/items. |
| */ |
| if( |
| 0 != strncmp(classArg, "Label", 5) && |
| 0 != strncmp(symbolArg, ".bf", 3) && |
| 0 != strncmp(symbolArg, ".lf", 3) && |
| 0 != strncmp(symbolArg, ".ef", 3) |
| ) |
| { |
| char* endOffsetArg = NULL; |
| unsigned offset = 0; |
| |
| /* |
| ** Convert the offset to something meaninful (size). |
| */ |
| errno = 0; |
| offset = strtoul(offsetArg, &endOffsetArg, 16); |
| if(0 == errno && endOffsetArg != offsetArg) |
| { |
| void* moved = NULL; |
| |
| /* |
| ** Increase the size of the symbol array in the section. |
| ** Assumed symbols are unique within each section. |
| */ |
| moved = realloc(inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols, sizeof(MSDump_Symbol) * (inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbolCount + 1)); |
| if(NULL != moved) |
| { |
| unsigned symIndex = 0; |
| |
| /* |
| ** Record symbol details. |
| ** Assumed symbols are encountered in order for their section (size calc depends on it). |
| */ |
| symIndex = inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbolCount; |
| inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbolCount++; |
| inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols = (MSDump_Symbol*)moved; |
| memset(&inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex], 0, sizeof(MSDump_Symbol)); |
| |
| inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex].mOffset = offset; |
| |
| /* |
| ** We could allocate smarter here if it ever mattered. |
| */ |
| inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex].mName = strdup(symbolArg); |
| if(NULL != inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex].mName) |
| { |
| char* trim = NULL; |
| |
| trim = (char*)skipNonWhite(inContainer->mReadState.mCurrentObject->mSections[sectionIndex].mSymbols[symIndex].mName); |
| if(NULL != trim) |
| { |
| *trim = '\0'; |
| } |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, inLine, "Unable to duplicate symbol name."); |
| } |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, inLine, "Unable to grow symbol array for section."); |
| } |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, inLine, "Unable to convert offset to a number."); |
| } |
| } |
| } |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, inLine, "Unable to determine section index."); |
| } |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, inLine, "No match for section prefix."); |
| } |
| } |
| } |
| else |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, inLine, "Unable to scan for section."); |
| } |
| } |
| |
| return retval; |
| } |
| |
| |
| void dumpCleanup(MSDump_Container* inContainer) |
| /* |
| ** Attempt to be nice and free up what we have allocated. |
| */ |
| { |
| unsigned objectLoop = 0; |
| unsigned sectionLoop = 0; |
| unsigned symbolLoop = 0; |
| |
| for(objectLoop = 0; objectLoop < inContainer->mObjectCount; objectLoop++) |
| { |
| for(sectionLoop = 0; sectionLoop < inContainer->mObjects[objectLoop].mSectionCount; sectionLoop++) |
| { |
| for(symbolLoop = 0; symbolLoop < inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount; symbolLoop++) |
| { |
| CLEANUP(inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mName); |
| } |
| inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount = 0; |
| CLEANUP(inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols); |
| CLEANUP(inContainer->mObjects[objectLoop].mSections[sectionLoop].mType); |
| } |
| inContainer->mObjects[objectLoop].mSectionCount = 0; |
| CLEANUP(inContainer->mObjects[objectLoop].mSections); |
| } |
| CLEANUP(inContainer->mObjects); |
| inContainer->mObjectCount = 0; |
| } |
| |
| |
| int qsortSymOffset(const void* in1, const void* in2) |
| /* |
| ** qsort callback to sort the symbols by their offset. |
| */ |
| { |
| MSDump_Symbol* sym1 = (MSDump_Symbol*)in1; |
| MSDump_Symbol* sym2 = (MSDump_Symbol*)in2; |
| int retval = 0; |
| |
| if(sym1->mOffset < sym2->mOffset) |
| { |
| retval = 1; |
| } |
| else if(sym1->mOffset > sym2->mOffset) |
| { |
| retval = -1; |
| } |
| |
| return retval; |
| } |
| |
| |
| int calcContainer(Options* inOptions, MSDump_Container* inContainer) |
| /* |
| ** Resposible for doing any size calculations based on the offsets known. |
| ** After this calculation, each sections mUsed will match mSize. |
| ** After this calculation, all symbols should know how big they are. |
| */ |
| { |
| int retval = 0; |
| unsigned objectLoop = 0; |
| unsigned sectionLoop = 0; |
| unsigned symbolLoop = 0; |
| |
| |
| /* |
| ** Need to sort all symbols by their offsets. |
| */ |
| for(objectLoop = 0; 0 == retval && objectLoop < inContainer->mObjectCount; objectLoop++) |
| { |
| for(sectionLoop = 0; 0 == retval && sectionLoop < inContainer->mObjects[objectLoop].mSectionCount; sectionLoop++) |
| { |
| qsort( |
| inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols, |
| inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount, |
| sizeof(MSDump_Symbol), |
| qsortSymOffset |
| ); |
| } |
| } |
| |
| |
| /* |
| ** Need to go through all symbols and calculate their size. |
| */ |
| for(objectLoop = 0; 0 == retval && objectLoop < inContainer->mObjectCount; objectLoop++) |
| { |
| for(sectionLoop = 0; 0 == retval && sectionLoop < inContainer->mObjects[objectLoop].mSectionCount; sectionLoop++) |
| { |
| for(symbolLoop = 0; 0 == retval && symbolLoop < inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount; symbolLoop++) |
| { |
| inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mSize = |
| inContainer->mObjects[objectLoop].mSections[sectionLoop].mLength - |
| inContainer->mObjects[objectLoop].mSections[sectionLoop].mUsed - |
| inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mOffset; |
| |
| inContainer->mObjects[objectLoop].mSections[sectionLoop].mUsed += |
| inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mSize; |
| } |
| } |
| } |
| |
| |
| return retval; |
| } |
| |
| |
| int reportContainer(Options* inOptions, MSDump_Container* inContainer) |
| /* |
| ** Display all symbols and their data. |
| ** We'll use a tsv format. |
| */ |
| { |
| int retval = 0; |
| unsigned objectLoop = 0; |
| unsigned sectionLoop = 0; |
| unsigned symbolLoop = 0; |
| int printRes = 0; |
| |
| for(objectLoop = 0; 0 == retval && objectLoop < inContainer->mObjectCount; objectLoop++) |
| { |
| for(sectionLoop = 0; 0 == retval && sectionLoop < inContainer->mObjects[objectLoop].mSectionCount; sectionLoop++) |
| { |
| for(symbolLoop = 0; 0 == retval && symbolLoop < inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbolCount; symbolLoop++) |
| { |
| printRes = fprintf(inOptions->mOutput, "%s\t%s\t%.8X\t%s\n", |
| inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mName, |
| inContainer->mObjects[objectLoop].mSections[sectionLoop].mType, |
| inContainer->mObjects[objectLoop].mSections[sectionLoop].mSymbols[symbolLoop].mSize, |
| inContainer->mObjects[objectLoop].mObject |
| ); |
| |
| if(0 > printRes) |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, inOptions->mOutputName, "Unable to write to file."); |
| } |
| } |
| } |
| } |
| |
| return retval; |
| } |
| |
| |
| int dump2symdb(Options* inOptions) |
| /* |
| ** Convert the input into the output, respecting the options. |
| ** Returns 0 on success. |
| */ |
| { |
| int retval = 0; |
| char lineBuffer[0x800]; |
| MSDump_Container container; |
| |
| memset(&container, 0, sizeof(container)); |
| |
| /* |
| ** Read the file line by line. |
| */ |
| while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput)) |
| { |
| if(0 != container.mReadState.mSkipLines) |
| { |
| container.mReadState.mSkipLines--; |
| continue; |
| } |
| retval = processLine(inOptions, &container, lineBuffer); |
| } |
| |
| /* |
| ** Perform whatever calculations desired. |
| */ |
| if(0 == retval) |
| { |
| retval = calcContainer(inOptions, &container); |
| } |
| |
| /* |
| ** Output what we know. |
| */ |
| if(0 == retval) |
| { |
| retval = reportContainer(inOptions, &container); |
| } |
| |
| /* |
| ** Cleanup what we've done. |
| */ |
| dumpCleanup(&container); |
| |
| 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 |
| { |
| retval = __LINE__; |
| ERROR_REPORT(retval, current->mLongName, "No handler for command line switch."); |
| } |
| } |
| } |
| |
| return retval; |
| } |
| |
| |
| void cleanOptions(Options* inOptions) |
| /* |
| ** Clean up any open handles. |
| */ |
| { |
| 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); |
| } |
| |
| 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 takes the output of \"dumpbin /symbols\" to produce a simple\n"); |
| printf("tsv db file of symbols and their respective attributes, like size.\n"); |
| } |
| |
| |
| 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) |
| { |
| retval = dump2symdb(&options); |
| } |
| |
| cleanOptions(&options); |
| return retval; |
| } |
| |