зеркало из https://github.com/mozilla/pjs.git
1143 строки
40 KiB
C
1143 строки
40 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is msmap2tsv.c code, released
|
|
* Oct 3, 2002.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 2002 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Garrett Arch Blythe, 03-October-2002
|
|
*
|
|
* Alternatively, the contents of this file may be used under the
|
|
* terms of the GNU Public License (the "GPL"), in which case the
|
|
* provisions of the GPL are applicable instead of those above.
|
|
* If you wish to allow use of your version of this file only
|
|
* under the terms of the GPL and not to allow others to use your
|
|
* version of this file under the MPL, indicate your decision by
|
|
* deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this
|
|
* file under either the MPL or the GPL.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
|
|
#include "msmap.h"
|
|
|
|
|
|
#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 Wether or not help should be shown.
|
|
** mAddress Wether or not to output addresses.
|
|
*/
|
|
{
|
|
const char* mProgramName;
|
|
FILE* mInput;
|
|
char* mInputName;
|
|
FILE* mOutput;
|
|
char* mOutputName;
|
|
int mHelp;
|
|
int mAddresses;
|
|
}
|
|
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 gAddressesSwitch = {"--addresses", "-a", 0, NULL, "Output segment addresses." DESC_NEWLINE "Helps reveal symbol ordering." DESC_NEWLINE "Lack of simplifies size diffing."};
|
|
|
|
static Switch* gSwitches[] = {
|
|
&gInputSwitch,
|
|
&gOutputSwitch,
|
|
&gAddressesSwitch,
|
|
&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;
|
|
}
|
|
|
|
|
|
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 = strdup(symbolBuf);
|
|
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 = strdup(symbolBuf);
|
|
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 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;
|
|
}
|
|
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, address inPrefix, address inOffset, const char* inObject, const char* inSymbol)
|
|
/*
|
|
** Output a line of map information seperated by tabs.
|
|
** Some items (const char*), if not present, will receive a default value.
|
|
*/
|
|
{
|
|
int retval = 0;
|
|
|
|
/*
|
|
** No need to output on no size.
|
|
** This can happen with zero sized segments,
|
|
** or an imported symbol which has multiple names (one will count).
|
|
*/
|
|
if(0 != inSize)
|
|
{
|
|
char objectBuf[0x100];
|
|
const char* symScope = NULL;
|
|
const char* segClass = NULL;
|
|
const char* undefined = "UNDEF";
|
|
|
|
/*
|
|
** Fill in unspecified values.
|
|
*/
|
|
if(NULL == inObject)
|
|
{
|
|
sprintf(objectBuf, "%s:%s:%s", undefined, inModule, inSegment);
|
|
inObject = objectBuf;
|
|
}
|
|
if(NULL == inSymbol)
|
|
{
|
|
inSymbol = inObject;
|
|
}
|
|
|
|
/*
|
|
** Convert some enumerations to text.
|
|
*/
|
|
switch(inClass)
|
|
{
|
|
case CODE:
|
|
segClass = "CODE";
|
|
break;
|
|
case DATA:
|
|
segClass = "DATA";
|
|
break;
|
|
default:
|
|
retval = __LINE__;
|
|
ERROR_REPORT(retval, "", "Unable to determine class for output.");
|
|
break;
|
|
}
|
|
|
|
switch(inScope)
|
|
{
|
|
case PUBLIC:
|
|
symScope = "PUBLIC";
|
|
break;
|
|
case STATIC:
|
|
symScope = "STATIC";
|
|
break;
|
|
case UNDEFINED:
|
|
symScope = undefined;
|
|
break;
|
|
default:
|
|
retval = __LINE__;
|
|
ERROR_REPORT(retval, "", "Unable to determine scope for symbol.");
|
|
break;
|
|
}
|
|
|
|
if(0 == retval)
|
|
{
|
|
int printRes = 0;
|
|
|
|
printRes = fprintf(inOptions->mOutput,
|
|
"%.8X\t%s\t%s\t%s\t%s\t%s\t%s",
|
|
inSize,
|
|
segClass,
|
|
symScope,
|
|
inModule,
|
|
inSegment,
|
|
inObject,
|
|
inSymbol
|
|
);
|
|
|
|
if(0 <= printRes && inOptions->mAddresses)
|
|
{
|
|
printRes = fprintf(inOptions->mOutput,
|
|
"\t%.4X:%.8X",
|
|
(unsigned)inPrefix,
|
|
(unsigned)inOffset
|
|
);
|
|
}
|
|
|
|
if(0 <= printRes)
|
|
{
|
|
printRes = fprintf(inOptions->mOutput, "\n");
|
|
}
|
|
|
|
if(0 > printRes)
|
|
{
|
|
retval = __LINE__;
|
|
ERROR_REPORT(retval, inOptions->mOutputName, "Unable to output tsv data.");
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
void cleanModule(MSMap_Module* inModule)
|
|
{
|
|
unsigned loop = 0;
|
|
|
|
for(loop = 0; loop < inModule->mSymbolCount; loop++)
|
|
{
|
|
CLEANUP(inModule->mSymbols[loop].mObject);
|
|
CLEANUP(inModule->mSymbols[loop].mSymbol);
|
|
}
|
|
CLEANUP(inModule->mSymbols);
|
|
|
|
for(loop = 0; loop < inModule->mSegmentCount; loop++)
|
|
{
|
|
CLEANUP(inModule->mSegments[loop].mSegment);
|
|
}
|
|
CLEANUP(inModule->mSegments);
|
|
|
|
CLEANUP(inModule->mModule);
|
|
|
|
memset(inModule, 0, sizeof(MSMap_Module));
|
|
}
|
|
|
|
|
|
int map2tsv(Options* inOptions)
|
|
/*
|
|
** Read all input.
|
|
** Output tab seperated value data.
|
|
*/
|
|
{
|
|
int retval = 0;
|
|
MSMap_Module module;
|
|
|
|
memset(&module, 0, sizeof(module));
|
|
|
|
/*
|
|
** Read in the map file.
|
|
*/
|
|
retval = readmap(inOptions, &module);
|
|
if(0 == retval)
|
|
{
|
|
unsigned segLoop = 0;
|
|
MSMap_Segment* segment = NULL;
|
|
unsigned symLoop = 0;
|
|
MSMap_Symbol* symbol = NULL;
|
|
MSMap_Symbol* firstSymbol = NULL;
|
|
MSMap_Symbol* matchSymbol = NULL;
|
|
MSMap_Symbol* prevSymbol = NULL;
|
|
|
|
/*
|
|
** Quick sort the symbols via RVABase.
|
|
*/
|
|
qsort(module.mSymbols, module.mSymbolCount, sizeof(MSMap_Symbol), qsortRVABase);
|
|
|
|
/*
|
|
** Go through each segment in order:
|
|
** Output symbols and sizes, already in order.
|
|
** Make up symbol names for those missing symbols,
|
|
** or for blank spots at the beginning of a segment.
|
|
*/
|
|
for(segLoop = 0; 0 == retval && segLoop < module.mSegmentCount; segLoop++)
|
|
{
|
|
segment = (module.mSegments + segLoop);
|
|
firstSymbol = NULL;
|
|
matchSymbol = NULL;
|
|
prevSymbol = NULL;
|
|
|
|
for(symLoop = 0; 0 == retval && symLoop < module.mSymbolCount; symLoop++)
|
|
{
|
|
symbol = (module.mSymbols + symLoop);
|
|
|
|
/*
|
|
** Symbol must fall in range of segment for consideration.
|
|
*/
|
|
if(symbol->mPrefix == segment->mPrefix)
|
|
{
|
|
if(symbol->mOffset >= segment->mOffset)
|
|
{
|
|
if(symbol->mOffset < (segment->mOffset + segment->mLength))
|
|
{
|
|
/*
|
|
** Matched.
|
|
*/
|
|
prevSymbol = matchSymbol;
|
|
matchSymbol = symbol;
|
|
|
|
if(NULL == firstSymbol)
|
|
{
|
|
firstSymbol = matchSymbol;
|
|
|
|
/*
|
|
** Check to see if we need to output a dummy
|
|
** to start the segment.
|
|
*/
|
|
if(0 == retval && firstSymbol->mOffset != segment->mOffset)
|
|
{
|
|
retval = tsvout(inOptions,
|
|
firstSymbol->mOffset - segment->mOffset,
|
|
segment->mClass,
|
|
UNDEFINED,
|
|
module.mModule,
|
|
segment->mSegment,
|
|
segment->mPrefix,
|
|
segment->mOffset,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Can now output previous symbol as can calculate size.
|
|
*/
|
|
if(0 == retval && NULL != prevSymbol)
|
|
{
|
|
retval = tsvout(inOptions,
|
|
matchSymbol->mOffset - prevSymbol->mOffset,
|
|
segment->mClass,
|
|
prevSymbol->mScope,
|
|
module.mModule,
|
|
segment->mSegment,
|
|
prevSymbol->mPrefix,
|
|
prevSymbol->mOffset,
|
|
prevSymbol->mObject,
|
|
prevSymbol->mSymbol
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** If there was no symbol, output a fake one for the entire segment.
|
|
** Otherwise, there is always one final match which we must output
|
|
** taking up the remainder of the segment.
|
|
*/
|
|
if(0 == retval)
|
|
{
|
|
if(NULL == firstSymbol)
|
|
{
|
|
retval = tsvout(inOptions,
|
|
segment->mLength,
|
|
segment->mClass,
|
|
UNDEFINED,
|
|
module.mModule,
|
|
segment->mSegment,
|
|
segment->mPrefix,
|
|
segment->mOffset,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
else
|
|
{
|
|
retval = tsvout(inOptions,
|
|
(segment->mOffset + segment->mLength) - matchSymbol->mOffset,
|
|
segment->mClass,
|
|
matchSymbol->mScope,
|
|
module.mModule,
|
|
segment->mSegment,
|
|
matchSymbol->mPrefix,
|
|
matchSymbol->mOffset,
|
|
matchSymbol->mObject,
|
|
matchSymbol->mSymbol
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** 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 == &gAddressesSwitch)
|
|
{
|
|
outOptions->mAddresses = __LINE__;
|
|
}
|
|
else if(current == &gHelpSwitch)
|
|
{
|
|
outOptions->mHelp = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
retval = __LINE__;
|
|
ERROR_REPORT(retval, current->mLongName, "No hanlder for command line switch.");
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
void cleanOptions(Options* inOptions)
|
|
/*
|
|
** Clean up any open handles.
|
|
*/
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
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 = map2tsv(&options);
|
|
}
|
|
|
|
cleanOptions(&options);
|
|
return retval;
|
|
}
|
|
|