Click here to Skip to main content
15,884,836 members
Articles / Mobile Apps

The StateWizard VC++ Add-in and Engine with Source Code

Rate me:
Please Sign up or sign in to vote.
4.73/5 (24 votes)
26 Mar 2009CPOL12 min read 190.1K   2.8K   132  
A cross-platform state-oriented application framework and a ClassWizard-like round-trip UML dynamic modeling/development tool that runs in popular IDEs. Aims at providing concurrent, distributed, and real-time application development tools for Win32/Linux
/* =============================================================================
 * Filename:    MapfileAnalysis.cpp
 * 
 * Copyright  Inc
 * All rights reserved.
 * -----------------------------------------------------------------------------
 * General description of this file:
 * A map file is a text file that contains the following information about the program being linked: 

 * The module name, which is the base name of the file
 * The timestamp from the program file header (not from the file system)
 * A list of groups in the program, with each group's start address (as section: offset), length, group name, and class
 * A list of public symbols, with each address (as section: offset), symbol name, flat address, and .OBJ file where the symbol is defined
 * The entry point (as section: offset)
 *
 * It seems that VC generated map-file includes all public and static function entry address.
 *

Executable code section, .text

The .bss section represents uninitialized data for the application, including
 all variables declared as static within a function or source module.

The .rdata section represents read-only data, such as literal strings,
 constants, and debug directory information.

.data 

All other variables (except automatic variables, which appear on the stack) are
stored in the .data section. Basically, these are application or module global
variables.

.CRT is another initialized data section utilized by the Microsoft C/C++ run-time libraries (hence the name). 
Why this data couldn't go into the standard .data section is beyond me.

 *
 * 
 * -----------------------------------------------------------------------------
 *                               Revision History
 * -----------------------------------------------------------------------------
 * Version   Date      Author          Revision Detail
 * 1.0.0    2004/12/24                 Initial
   7.3      2006/6/24                  Analyze .bss, .rdata .data
 * ===========================================================================*/

#include "stdafx.h"
#include "MapfileAnalysis.h"
//****************************************************************************//
//manage the MapFileAnalysis supplemental tools functions


//****************************************************************************//
//manage the MapFileAnalysis supplemental tools functions

/* Data structure

  First node: header(summary data) -> actual list.

*/
VAR_NODE_T* GetObjVarList(int nType, ListNode *p)
{
	if (p==NULL) return NULL;

	switch (nType)
	{
	case SEC_BSS:
		return p->pVarListBSS;
		break;
	case SEC_RDATA:
		return p->pVarListRData;
		break;
	case SEC_DATA:
		return p->pVarListData;
		break;
	}
	return NULL;
};

BOOL SetObjVarList(int nType, ListNode *p, VAR_NODE_T* pData)
{
	if (p==NULL) return FALSE;

	switch (nType)
	{
	case SEC_BSS:
		p->pVarListBSS = pData;
		break;
	case SEC_RDATA:
		p->pVarListRData = pData;
		break;
	case SEC_DATA:
		p->pVarListData = pData;
		break;
	}

	return TRUE;
};

DWORD GetObjMinAddr(int nType, ListNode *p)
{
	if (p==NULL) return 0;

	switch (nType)
	{
	case SEC_TEXT:
		return p->dwMin;
		break;
	case SEC_BSS:
		return p->dwMinBSS;
		break;
	case SEC_RDATA:
		return p->dwMinRData;
		break;
	case SEC_DATA:
		return p->dwMinData;
		break;
	}
	return 0;
}

BOOL SetObjMinAddr(int nType, ListNode *p, DWORD dwAddr)
{
	if (p==NULL) return FALSE;

	switch (nType)
	{
	case SEC_TEXT:
		p->dwMin = dwAddr;
		break;
	case SEC_BSS:
		p->dwMinBSS = dwAddr;
		break;
	case SEC_RDATA:
		p->dwMinRData = dwAddr;
		break;
	case SEC_DATA:
		p->dwMinData = dwAddr;
		break;
	}

	return TRUE;
};


DWORD GetObjLength(int nType, ListNode *p)
{
	if (p==NULL) return 0;

	switch (nType)
	{
	case SEC_TEXT:
		return p->dwLengthText;
		break;
	case SEC_BSS:
		return p->dwLengthBSS;
		break;
	case SEC_RDATA:
		return p->dwLengthRData;
		break;
	case SEC_DATA:
		return p->dwLengthData;
		break;
	}
	return 0;
}

BOOL SetObjLength(int nType, ListNode *p, DWORD dwLength)
{
	if (p==NULL) return FALSE;

	switch (nType)
	{
	case SEC_TEXT:
		p->dwLengthText = dwLength;
		break;
	case SEC_BSS:
		p->dwLengthBSS = dwLength;
		break;
	case SEC_RDATA:
		p->dwLengthRData = dwLength;
		break;
	case SEC_DATA:
		p->dwLengthData = dwLength;
		break;
	}

	return TRUE;
}


///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Judge whether the Current ObjName is existed. 
// INPUT:  1) pHead: a Pointer to Head ListNode. 
//         2) lpObjName: a ObjName to search for.
// OUTPUT: a pointer to a ListNode.
// NOTE: If find the given ObjName return a pointer to the ListNode 
//       otherwise return NULl 
/////////////////////////////////////////////////////////////////////////////////
ListNode * FindObjName(ListNode *pHead, LPCTSTR lpObjName)
{
	ListNode *pNext = pHead->next;
	while (pNext && (CString(pNext->cName).Compare(lpObjName)))
		pNext = pNext->next;

	return pNext;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Analyze a given line to get the min/max function Starting Address in each Obj. 
// INPUT:  1) sLine: a line wanted to be analyzed
//         2) lpBegin: The leading string of one line for example 0001 for .text section.
//         3) pHead: a Pointer to Head ListNode. 
//         4) pCurrent: a Pointer to Current ListNode.
// OUTPUT: TRUE: accepted.
// NOTE: 
///////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL ParseALine(int nType, CString sLine, LPCTSTR lpBegin, ListNode *pHead)
{
	CString sBegin = "";
	CString sAddress = "";
	CString sObjName = "";
	CString sSymbol;
	CString sRVA_Base;
	CString sTemp;

	if (sLine.Find(':') != -1)
		sBegin = sLine.Left(sLine.Find(':'));
	
	int nPos = sLine.ReverseFind(' '); 
	if (nPos != -1)
		sObjName = sLine.Mid(nPos+1);

	// Make the Object name to Lower letter
	sObjName.TrimLeft();
	sObjName.TrimRight();
	sObjName.MakeLower();

	
	/*  Format:
	Address         Publics by Value              Rva+Base     Lib:Object 
	0001:00000620       _DllMain@12                10001620 f   App.obj
	*/

	if (!sBegin.Compare(lpBegin)) 
	{
		// ((sObjName.Find(".obj") != -1) || (sObjName.Find("<common>") != -1))) 
		
		TCHAR *end=NULL;
		sTemp = sLine.Left(sLine.Find(' '));
		sAddress = sTemp.Mid(sTemp.Find(':')+1);
		DWORD dwValue = strtoul(LPCTSTR(sAddress), &end, 16);

		/* If endptr is not NULL, a pointer to the character that stopped the scan is stored at the location pointed to by endptr.*/
		if (*end!=NULL) return FALSE;

		sTemp = sLine.Mid(sLine.Find(' ')+1);
		sTemp.TrimLeft();
		sSymbol = sTemp.Left(sTemp.Find(' '));
		if (sSymbol.IsEmpty()) return FALSE;
		sTemp=sTemp.Mid(sTemp.Find(' ') +1);
		sTemp.TrimLeft();
		sRVA_Base = sTemp.Left(sTemp.Find(' '));
		sRVA_Base.TrimLeft();

		strtoul(LPCTSTR(sAddress), &end, 16);
		/* If endptr is not NULL, a pointer to the character that stopped the scan is stored at the location pointed to by endptr.*/
		if (*end!=NULL) return FALSE;

		// Filter out other sections rather than .txt, .bss, .rdata, .data
		if (!(dwValue>=GetObjMinAddr(nType,pHead) && dwValue<=GetObjMinAddr(nType,pHead)+GetObjLength(nType,pHead)))
			return FALSE;

		ListNode *pFind = FindObjName(pHead, sObjName);
		if (!pFind)
		{
			ListNode *pNew = new(ListNode);
			memset(pNew, 0, sizeof(ListNode));
			pNew->dwMin =MAX_INT; // Initial value. If this value does not updated, there will be no any symbol in this section.
			pNew->dwMinRData =MAX_INT;
			pNew->dwMinBSS =MAX_INT;
			pNew->dwMinData =MAX_INT;

			pFind = pNew;

			strncpy(pNew->cName, (LPCTSTR)(sObjName), sizeof(pNew->cName)-1);
			if (nType == SEC_TEXT)
				pNew->dwFuncNum = 1;

			SetObjMinAddr(nType, pNew, dwValue);

			// Insert after the header.
			pNew->next = pHead->next;
			pHead->next = pNew;
		}
		else
		{
			// Record the function number
			if (nType == SEC_TEXT)
				pFind->dwFuncNum++;

			// Record the min starting function address in the obj.
			if (GetObjMinAddr(nType, pFind) > dwValue)
				SetObjMinAddr(nType, pFind, dwValue);
		}

		if (nType!=SEC_TEXT)
		{
			VAR_NODE_T *pVarListTail;
			VAR_NODE_T *pVarNode = new(VAR_NODE_T);
			memset(pVarNode, 0, sizeof(VAR_NODE_T));
			strncpy(pVarNode->sVar, (LPCTSTR)(sSymbol), sizeof(pVarNode->sVar)-1);
			pVarNode->dwAddr = dwValue;
			pVarNode->pNext = NULL;

			pVarListTail = GetObjVarList(nType, pFind);
			// New var list. 
			if (pVarListTail==NULL)
				SetObjVarList(nType, pFind, pVarNode);

			// Append the variable node in the end of list.
			if (pVarListTail!=NULL)
			{
				while (pVarListTail->pNext)
					pVarListTail = pVarListTail->pNext;
				pVarListTail->pNext = pVarNode;
			}

		}

		return TRUE;
	}

	return FALSE;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Delete a Link list.
// INPUT:  1) pHead: a pointer to the Head of the List link wanted to be deleted.
// OUTPUT: zero or nonzero.
// NOTE: 
///////////////////////////////////////////////////////////////////////////////////////////////////////
static BOOL DeleteVarList(VAR_NODE_T *pVarNodeHdr)
{
	VAR_NODE_T *pVarNode;
	while (pVarNodeHdr!=NULL)
	{
		pVarNode = pVarNodeHdr;
		pVarNodeHdr = pVarNodeHdr->pNext;
		delete pVarNode;
	}
	return TRUE;
}

BOOL DeleteList(ListNode *pHead)
{
	ListNode *pCurrent;

	while (pHead)
	{
		DeleteVarList(pHead->pVarListBSS);
		DeleteVarList(pHead->pVarListRData);
		DeleteVarList(pHead->pVarListData);
		
		pCurrent = pHead;
		pHead = pHead->next;
		delete(pCurrent);
	}
	return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Sort the given LinkList in given order. 
// INPUT:  1) pHead: a pointer to a Head of the LinkList wanted to be sorted
// OUTPUT: zero or nonzero.
// NOTE: 
///////////////////////////////////////////////////////////////////////////////////////////////////////
ListNode* SortList(int nOrderBy, ListNode *pHead)
{
	ListNode *pCurrent;
	ListNode *pPre;

	DWORD dwPreMin =0;
	DWORD dwCurMin =0;

	ListNode SwapNode;
	ListNode* pSwapNext;

	pPre = pHead->next;

	// select sort
	while(pPre)
	{
		pCurrent = pPre->next;
		while(pCurrent)
		{
			dwPreMin= GetObjMinAddr(nOrderBy, pPre);
			dwCurMin= GetObjMinAddr(nOrderBy, pCurrent);
			
			if (dwPreMin > dwCurMin)
			{
                pSwapNext = pPre->next;
				memcpy(&SwapNode, pPre, sizeof(ListNode));
				memcpy(pPre, pCurrent, sizeof(ListNode));
				pPre->next = pSwapNext;
                pSwapNext = pCurrent->next;
				memcpy(pCurrent, &SwapNode, sizeof(ListNode));
				pCurrent->next = pSwapNext;
			}
			pCurrent = pCurrent->next;
		}
		pPre = pPre->next;
	}

	pCurrent = pHead->next;
	int nCount=0;
	int dwSum = 0;

	// Calculate the length of each object in given section.
	while(pCurrent)
	{
		pPre = pCurrent;
		pCurrent = pCurrent->next;

		// Filter out invalid node in the object list.
		while (pCurrent!=NULL && GetObjMinAddr(nOrderBy, pCurrent) == MAX_INT)
			pCurrent = pCurrent->next;

		DWORD dwSize = 0;

		if (pCurrent)
		{
			if (GetObjMinAddr(nOrderBy, pCurrent) == MAX_INT)
				pPre->dwLengthText = 0;
			else
			{
				SetObjLength(nOrderBy, pPre, GetObjMinAddr(nOrderBy, pCurrent) - GetObjMinAddr(nOrderBy, pPre));
				dwSum += GetObjLength(nOrderBy, pPre);
			}
		}
		else
		{
			// the last object in the section
			if (GetObjMinAddr(nOrderBy, pPre)==MAX_INT) // no data
				SetObjLength(nOrderBy, pPre, 0);
			else
				SetObjLength(nOrderBy, pPre, GetObjLength(nOrderBy, pHead) - dwSum);
		}
	};

	return pHead;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Calculator the code size based on the mapfile. 
// INPUT:  1) lpMapFilePath: the full path of a mapfile.
//         2) dwRdata: record the size of the Read only variable data.
//         3) dwData: record the size of the Writable variable data. 
//         4) dwBss: record the size of the Un-initialized data.
// OUTPUT: the pointer to a Head of a generated LinkList.
// NOTE: 
///////////////////////////////////////////////////////////////////////////////////////////////////////
ListNode* CalculatorCodeSize(LPCTSTR lpMapFilePath, DWORD *dwRdata, DWORD *dwData, DWORD *dwBss, bool &bFileisNull, bool &bIsNotMapfile)
{
	if (lpMapFilePath == NULL)
		return NULL;

	// Open the mapfile to analyse
	CStdioFile MapFile;
	if (!MapFile.Open(lpMapFilePath, CFile::modeRead | CFile::typeText))
	{
		return NULL;
	}
	
	CString sLine;
	if (!MapFile.ReadString(sLine))
	{
		bFileisNull = TRUE;
		return NULL;
	}

	// Generate the head list and initialize it
	ListNode *pHead = new(ListNode);
	memset(pHead, 0, sizeof(ListNode));

	ListNode *pSumNode = pHead;
	strcpy(pHead->cName, "TotalCodeSize");
	pHead->dwFuncNum = 0;
	pHead->dwMin = 0; //ULONG_MAX;
	pHead->dwMinBSS =0;
	pHead->dwMinData =0;
	pHead->dwMinRData =0;

	pHead->next = NULL;

	BOOL bFindText = TRUE;
	BOOL bIsTimestamp = TRUE;
	BOOL bIsMapfile = TRUE;
	// Record the read only data
	CString sRdata;

	// Record the Writable variable data
	CString sData;

	// Record the Un-initialized data
	CString sBss;

	CString sBegin; // .text section leading string.
	CString sBeginerBSS; // .bss section leading string.
	CString sBeginerRData; // .rdata section leading string.
	CString sBeginerData; // .rdata section leading string.

	CString sStartAddr, sLength;

	BOOL bIsAddrTble = FALSE;

	enum { STAE_INIT, STATE_SEC_TBL, STATE_ADDR_TBL, STATE_END};
	int nState = STAE_INIT;

	while (TRUE)
	{
		if (!MapFile.ReadString(sLine))
			break;
		
		sLine.TrimLeft();
		sLine.TrimRight();

		if (sLine.IsEmpty()) // Pass by the blank line.
		{
			if (nState!=STATE_ADDR_TBL)
				continue;
			else
				break; //ends parsing.
		}

		if (sLine.Find("Timestamp ") != -1)
			bIsTimestamp = FALSE;

		if (sLine.Find("Preferred ") != -1)
			bIsMapfile = FALSE;
			
		if (!sLine.Compare("Start         Length     Name                   Class"))
		{
			nState=STATE_SEC_TBL;
			continue;
		};

		if (!sLine.Compare("Address         Publics by Value              Rva+Base     Lib:Object"))
		{
			nState=STATE_ADDR_TBL;

			do {
				if (!MapFile.ReadString(sLine))
				{
					break;
					break;
				}

				sLine.TrimLeft();
				sLine.TrimRight();
			} while (sLine.IsEmpty());
		}

		if (nState==STATE_SEC_TBL && sLine.Find(".text ") != -1)
		{
			bFindText = FALSE;

			// Get the leading text string of the line(0001);
			//  Start         Length     Name                   Class
			//  0001:00000000 0002108aH .text                   CODE

			sBegin = sLine.Left(sLine.Find(':'));

			// Get the total code size 
			TCHAR *end;

			// ":xxxx "
			// Stored the total code size into the head node
			sStartAddr = sLine.Mid(sLine.Find(':')+1);
			sStartAddr = sStartAddr.Left(sStartAddr.Find(' '));
			pHead->dwMin = strtoul(LPCTSTR(sStartAddr), &end, 16);
			
			// " xxxxH"
			sLength = sLine.Mid(sLine.Find(' ')+1);
			sLength = sLength.Left(sLength.Find('H'));

			pHead->dwLengthText = strtoul(LPCTSTR(sLength), &end, 16);

			continue;
		}

		// Get rdata(Read only variable data)
		if (nState==STATE_SEC_TBL && sLine.Find(".rdata ") != -1)
		{
			TCHAR *end;

			// ":xxxx "
			// Stored the total code size into the head node
			sStartAddr = sLine.Mid(sLine.Find(':')+1);
			sStartAddr = sStartAddr.Left(sStartAddr.Find(' '));
			pHead->dwMinRData = strtoul(LPCTSTR(sStartAddr), &end, 16);
			
			// " xxxxH"
			sLength = sLine.Mid(sLine.Find(' ')+1);
			sLength = sLength.Left(sLength.Find('H'));
			*dwRdata = strtoul(LPCTSTR(sLength), &end, 16);

			pHead->dwLengthRData = *dwRdata;

			sBeginerRData = sLine.Left(sLine.Find(':'));
			continue;

		}

		// Get data(Writable variable data)
		if (nState==STATE_SEC_TBL && sLine.Find(".data ") != -1)
		{
			TCHAR *end;

			// ":xxxx "
			// Stored the total code size into the head node
			sStartAddr = sLine.Mid(sLine.Find(':')+1);
			sStartAddr = sStartAddr.Left(sStartAddr.Find(' '));
			pHead->dwMinData = strtoul(LPCTSTR(sStartAddr), &end, 16);
			
			// " xxxxH"
			sLength = sLine.Mid(sLine.Find(' ')+1);
			sLength = sLength.Left(sLength.Find('H'));
			*dwData = strtoul(LPCTSTR(sLength), &end, 16);

			pHead->dwLengthData = *dwData;

			sBeginerData = sLine.Left(sLine.Find(':'));
			continue;
		}

		// Get (bss)Un-initialized data
		if (nState==STATE_SEC_TBL && sLine.Find(".bss ") != -1)
		{
			TCHAR *end;
			// ":xxxx "
			// Stored the total code size into the head node
			sStartAddr = sLine.Mid(sLine.Find(':')+1);
			sStartAddr = sStartAddr.Left(sStartAddr.Find(' '));
			pHead->dwMinBSS= strtoul(LPCTSTR(sStartAddr), &end, 16);
			
			// " xxxxH"
			sLength = sLine.Mid(sLine.Find(' ')+1);
			sLength = sLength.Left(sLength.Find('H'));
			*dwBss = strtoul(LPCTSTR(sLength), &end, 16);
			pHead->dwLengthBSS = *dwBss;

			sBeginerBSS = sLine.Left(sLine.Find(':'));
			continue;
		}

		if (nState==STATE_ADDR_TBL)
		{
			if (!ParseALine(SEC_TEXT, sLine, sBegin, pHead))
			{
				if (!ParseALine(SEC_BSS, sLine, sBeginerBSS, pHead)) 
					if (!ParseALine(SEC_RDATA, sLine, sBeginerRData, pHead)) 
						ParseALine(SEC_DATA, sLine, sBeginerData, pHead); 
			}

			continue;
		}

	}

	// Close the mapfile
	MapFile.Close();

	// The opened file is not the map file
	if (bIsTimestamp || bIsMapfile || bFindText)
	{
		bIsNotMapfile = TRUE;

		delete(pHead);
		return NULL;
	}

	// Sort by the dwMin of the ListNote
	SortList(SEC_BSS, pHead);
	SortList(SEC_RDATA, pHead);
	SortList(SEC_DATA, pHead);

	SortList(SEC_TEXT, pHead);
	return pHead;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Alex "Question is more important than the answer."

Comments and Discussions