imgtools/romtools/rombuild/r_collapse.cpp
changeset 0 044383f39525
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/imgtools/romtools/rombuild/r_collapse.cpp	Tue Oct 27 16:36:35 2009 +0000
@@ -0,0 +1,365 @@
+/*
+* 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;
+	}
+