imgtools/romtools/rombuild/r_collapse.cpp
author timothy.murphy@nokia.com
Sun, 28 Feb 2010 21:18:07 +0200
branchfix
changeset 279 733464eaac50
parent 0 044383f39525
permissions -rw-r--r--
fix: make sure host attribute is set rather than blank in logs on windows by using the env var 'COMPUTERNAME' instead of 'HOSTNAME'. Thus make it less difficult to order recipes in the log by time.

/*
* Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/


#include <e32std.h>
#include <e32std_private.h>
#include <e32uid.h>
#include "h_utl.h"
#include <string.h>
#include <stdlib.h>
#include "r_global.h"
#include "r_obey.h"
#include "r_rom.h"
#include "r_dir.h"

// Routines for optimising the ROM by improving the code
//
// NB. Largely untouched since ER5, so not likely to work without
// some significant effort. Doesn't know about ARMv4T or ARMv5 instructions.


TInt E32Rom::CollapseImportThunks(TRomBuilderEntry* aFile)
//
// Collapse 3-word import thunks into a single branch
//	
	{
	TRomImageHeader *pI=aFile->iRomImageHeader;
	TUint32 *pE=(TUint32*)RomToActualAddress(pI->iCodeAddress);	// address of code section
	TUint32 codeSize=pI->iCodeSize;
	TUint32 *pC=pE+(codeSize>>2)-3;
	TUint32 low=0;
	TUint32 high=0;
	TUint32 romLow=0;
	TUint32 romHigh=0;
	TBool block=EFalse;
	TInt blocknum=0;
	TRACE(TCOLLAPSE1,Print(ELog,"CollapseImportThunks() File %s[%08x]\n",aFile->iFileName,
						   pI->iHardwareVariant));
	while(pC>=pE)
		{
		if (pC[0]==0xe59fc000 && pC[1]==0xe59cf000 && (pC[2]&0xf0000000)==0x50000000)
			{
// assume this is an import thunk
			if (!block)
				{
				high=(TUint32)(pC+3);
				block=ETrue;
				}
			pC-=3;
			}
		else
			{
			if (block)
				{
				low=(TUint32)(pC+3);
				block=EFalse;
				TInt numImports=(high-low)/12;
				TRACE(TCOLLAPSE2,Print(ELog,"?Import thunk block %08x-%08x %d %d\n",ActualToRomAddress((TAny*)low),ActualToRomAddress((TAny*)high),numImports,aFile->iImportCount));
				if (numImports==aFile->iImportCount)
					{
					if (blocknum==0)
						{
						romLow=(TUint32)ActualToRomAddress((TAny*)low);
						romHigh=(TUint32)ActualToRomAddress((TAny*)high);
						}
					blocknum++;
					}
				}
			pC--;
			}
		}
	if (blocknum==0)
		{
		Print(EWarning,"Import thunk block for %s[%08x] not found\n",aFile->iFileName,
			  pI->iHardwareVariant);
		}
	else if (blocknum==1)
		{
		low=(TUint32)RomToActualAddress(romLow);
		high=(TUint32)RomToActualAddress(romHigh);
		TRACE(TCOLLAPSE1,Print(ELog,"Import thunk block %08x-%08x\n",romLow,romHigh));
		TUint32 *pX;
		for (pX=(TUint32*)low; pX<(TUint32*)high; pX+=3)
			{
			TUint32 *pA=(TUint32*)RomToActualAddress(pX[2]);
			TUint32 jumpAddr=*pA;
			jumpAddr=FindFinalJumpDestination(jumpAddr);
			TInt offset=(TInt)jumpAddr-(TInt)ActualToRomAddress(pX)-8;
			if (offset<33554432 && offset>-33554432)
				{
				pX[0]=0xea000000 | ((offset&0x03ffffff)>>2);
				iImportsFixedUp++;
				}
			}
		aFile->iImportBlockStartAddress=romLow;
		aFile->iImportBlockEndAddress=romHigh;
		}
	else
		Print(EWarning,"??Import thunk block ambiguous - not changed\n");
	return KErrNone;
	}

TInt E32Rom::CollapseBranches()
//
// Collapse chained branches
//	
	{

	Print(ELog, "\nCollapsing Chained Branches.\n");
	TInt i;
	for (i=0; i<iObey->iNumberOfPeFiles; i++)
		{
		TRomBuilderEntry* file=iPeFiles[i];
		if (file->iOrigHdr->iImportOffset && file->iImportBlockEndAddress!=0)
			{
			TInt r=CollapseBranches(file);
			if (r!=KErrNone)
				return r;
			}
		}
	return KErrNone;
	}

inline void SetBit(TUint32* aBitmap, TInt aOffset)
	{
	aOffset>>=2;
	aBitmap[aOffset>>5] |= (1<<(aOffset&0x1f));
	}

inline TInt BitTest(TUint32* aBitmap, TInt aOffset)
	{
	aOffset>>=2;
	return(aBitmap[aOffset>>5]&(1<<(aOffset&0x1f)));
	}

TUint32 E32Rom::FindFinalJumpDestination(TUint32 ja)
	{
// follow a chain of branches to final destination
	TUint32 initja=ja;
	TUint8* aja=(TUint8*)RomToActualAddress(ja);
	FOREVER
			{
			if ((*(TUint32*)aja &0xff000000)==0xea000000)
				{
// branch to an unconditional branch
				TInt off=(*(TUint32*)aja & 0x00ffffff)<<8;
				off>>=6;
				if (off==-8)
					{
// branch to same address
					break;
					}
				ja+=(off+8);
				aja+=(off+8);
				TRACE(TCOLLAPSE2,Print(ELog,"Chain branch %08x to %08x\n",initja,ja));
				}
			else
				break;
			}
	return ja;
	}


TInt E32Rom::CollapseBranches(TRomBuilderEntry* aFile)
	{
// Main code section is between pI->iCodeAddress and aImportThunkStart. This contains
// all the explicit code and interspersed literals.
// aImportThunkStart-aImportThunkEnd contains import thunks (already collapsed)
// aImportThunkEnd-iat contains template instantiations and virtual destructors
// iat-rdata contains import addresses - no need to touch these
// rdata-expdir contains constant data and vtables
// expdir-end contains exported addresses - no need to touch these
	TUint32 impStart=aFile->iImportBlockStartAddress;
	TUint32 impEnd=aFile->iImportBlockEndAddress;
	TRomImageHeader *pI=aFile->iRomImageHeader;
	TRACE(TCOLLAPSE1,Print(ELog,"CollapseBranches() File %s[%08x]\n",aFile->iFileName,
						   pI->iHardwareVariant));
	TUint32 codeStart=pI->iCodeAddress;
	TUint32 codeSize=pI->iCodeSize;
	TUint32 codeEnd=codeStart+codeSize;
	TUint32 *pC=(TUint32*)RomToActualAddress(codeStart);	// address of code section
	TUint32 *pE=(TUint32*)RomToActualAddress(codeEnd);
	TUint32 romIat=codeStart+aFile->iHdr->iTextSize;
	TUint32 romRdata=romIat+aFile->iImportCount*4;
	TUint32 exportDir=pI->iExportDir;
	if (exportDir==0)
		exportDir=codeEnd;
	TRACE(TCOLLAPSE1,Print(ELog,"iat=%08x, rdata=%08x, expdir=%08x, end=%08x\n",romIat,romRdata,exportDir,codeEnd));
	TUint32 *pD=new TUint32[(codeSize+127)>>7];
	if (!pD)
		return KErrNoMemory;
	TInt rdataSize=TInt(exportDir)-TInt(romRdata);
	TUint32 *pR=new TUint32[(rdataSize+127)>>7];
	if (!pR)
		return KErrNoMemory;
	TInt i;
	for (i=0; i<TInt((codeSize+127)>>7); i++)
		pD[i]=0;
	for (i=0; i<TInt((rdataSize+127)>>7); i++)
		pR[i]=0;
	TUint32 *pX;
// go through code looking for data references
	for (pX=pC; pX<pE; pX++)
		{
		if ((*pX&0x0f3f0000)==0x051f0000)
			{
// opcode is LDRcc Rn, [PC, #d]
			TInt offset=*pX & 0xfff;
			if ((*pX&0x00800000)==0)
				offset=-offset;
			TUint32 eff=(ActualToRomAddress(pX)+8+offset)&~3;
			if (eff>=codeStart && eff<codeEnd)
				{
				SetBit(pD,eff-codeStart);
				if (eff<codeEnd-4)
					SetBit(pD,eff-codeStart+4);
				TUint32 data=*(TUint32*)((TUint8*)pX+(offset&~3)+8);	// fetch data word
				if (data>=romRdata && data<exportDir)
					{
// it's an address of something in .rdata, possibly a vtable
					SetBit(pR,data-romRdata);
					}
				}
			}
		}

	TUint32 *importStart=(TUint32*)RomToActualAddress(impStart);
	TUint32 *iatStart=(TUint32*)RomToActualAddress(romIat);
	if (iObey->iCollapseMode==ECollapseImportThunksAndVtables)
		goto vtablesonly;

	// go through .text looking for Bcc and BLcc intstructions
	for (pX=pC; pX<importStart; pX++)
		{
		if ((*pX&0xfe000000)==0xea000000 && BitTest(pD,TInt(pX)-TInt(pC))==0 )
			{
			TInt off=(*pX & 0x00ffffff)<<8;
			off>>=6;
			TUint32 pc=ActualToRomAddress(pX)+8;
			TUint32 ja=pc+off;
			TUint32 initja=ja;
			if (ja<codeStart || ja>=codeEnd)
				{
				TRACE(TCOLLAPSE2,Print(ELog,"??Branch at %08x to %08x??\n",pc-8,ja));
				goto notthismodule;
				}
			TRACE(TCOLLAPSE4,Print(ELog,"Branch at %08x opcode %08x ja=%08x\n",pc-8,*pX,ja));
			ja=FindFinalJumpDestination(ja);
			if (ja!=initja)
				{
				off=(TInt(ja)-TInt(pc))>>2;
				if (off>-33554432 && off<33554432)
					{
					TUint32 oldOpc=*pX;
					*pX=(*pX & 0xff000000)|(off&0x00ffffff); // fix up branch
					TRACE(TCOLLAPSE2,Print(ELog,"Opcode at %08x fixed up from %08x to %08x\n",pc-8,oldOpc,*pX));
					iBranchesFixedUp++;
					}
				}
		notthismodule: ;
			}
		}
// go through template instantiations and virtual destructors
// looking for Bcc and BLcc intstructions to import thunks
	pX=(TUint32*)RomToActualAddress(impEnd);
	for (; pX<iatStart; pX++)
		{
		if ((*pX&0xfe000000)==0xea000000 && BitTest(pD,TInt(pX)-TInt(pC))==0 )
			{
			TInt off=(*pX & 0x00ffffff)<<8;
			off>>=6;
			TUint32 pc=ActualToRomAddress(pX)+8;
			TUint32 ja=pc+off;
			TUint32 initja=ja;
			TRACE(TCOLLAPSE4,Print(ELog,"Branch at %08x opcode %08x ja=%08x\n",pc-8,*pX,ja));
			if (ja<codeStart || ja>=codeEnd)
				{
				TRACE(TCOLLAPSE2,Print(ELog,"??Branch at %08x to %08x??\n",pc-8,ja));
				goto notthismodule2;
				}
			ja=FindFinalJumpDestination(ja);
			if (ja!=initja)
				{
				off=(TInt(ja)-TInt(pc))>>2;
				if (off>-33554432 && off<33554432)
					{
					TUint32 oldOpc=*pX;
					*pX=(*pX & 0xff000000)|(off&0x00ffffff); // fix up branch
					TRACE(TCOLLAPSE2,Print(ELog,"Opcode at %08x fixed up from %08x to %08x\n",pc-8,oldOpc,*pX));
					iBranchesFixedUp++;
					}
				}
		notthismodule2: ;
			}
		}
vtablesonly:
// go through rdata section looking for vtables with references to import thunks
	TUint32 *expStart=(TUint32*)RomToActualAddress(exportDir);
	TUint32* pW=(TUint32*)RomToActualAddress(romRdata);
	pX=pW;
	while(pX<expStart-1)
		{
// first look for reference to start of vtable
// there are always two 0 words at the start of a vtable
		if (BitTest(pR,TInt(pX)-TInt(pW)) && pX[0]==0 && pX[1]==0)
			{
			TRACE(TCOLLAPSE3,Print(ELog,"?vtable at %08x\n",ActualToRomAddress(pX)));
// look for next reference - there are no references to
// intermediate entries of vtable
			TUint32* pY;
			for (pY=pX+1; pY<expStart && BitTest(pR,TInt(pY)-TInt(pW))==0; pY++);

// pY should now point to the end of the vtable
// check all entries except the first two are valid ROM addresses in this module
			TRACE(TCOLLAPSE3,Print(ELog,"?vtable at %08x to %08x\n",ActualToRomAddress(pX),ActualToRomAddress(pY)));
			TUint32 *pZ;
			for (pZ=pX+2; pZ<pY; pZ++)
				{
				if (*pZ<codeStart || *pZ>=codeEnd)
					break;
				}
			if (pZ==pY)
				{
// this is a vtable
// check each address to see if it is an import thunk and if so fix it up
				TRACE(TCOLLAPSE3,Print(ELog,"!vtable at %08x to %08x\n",ActualToRomAddress(pX),ActualToRomAddress(pY)));
				for (pZ=pX+2; pZ<pY; pZ++)
					{
					TUint32 ja=*pZ;
					TUint32 initja=ja;
					ja=FindFinalJumpDestination(ja);
					if (ja!=initja)
						{
						*pZ=ja;
						TRACE(TCOLLAPSE2,Print(ELog,"Vtable entry at %08x fixed up from %08x to %08x\n",ActualToRomAddress(pZ),initja,ja));
						iVtableEntriesFixedUp++;
						}
					}
				pX=pY;
				}
			else
				pX++;
			}
		else
			pX++;
		}
	delete[] pR;
	delete[] pD;
	return KErrNone;
	}