imgtools/romtools/rombuild/r_collapse.cpp
changeset 0 044383f39525
equal deleted inserted replaced
-1:000000000000 0:044383f39525
       
     1 /*
       
     2 * Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of the License "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 #include <e32std.h>
       
    20 #include <e32std_private.h>
       
    21 #include <e32uid.h>
       
    22 #include "h_utl.h"
       
    23 #include <string.h>
       
    24 #include <stdlib.h>
       
    25 #include "r_global.h"
       
    26 #include "r_obey.h"
       
    27 #include "r_rom.h"
       
    28 #include "r_dir.h"
       
    29 
       
    30 // Routines for optimising the ROM by improving the code
       
    31 //
       
    32 // NB. Largely untouched since ER5, so not likely to work without
       
    33 // some significant effort. Doesn't know about ARMv4T or ARMv5 instructions.
       
    34 
       
    35 
       
    36 TInt E32Rom::CollapseImportThunks(TRomBuilderEntry* aFile)
       
    37 //
       
    38 // Collapse 3-word import thunks into a single branch
       
    39 //	
       
    40 	{
       
    41 	TRomImageHeader *pI=aFile->iRomImageHeader;
       
    42 	TUint32 *pE=(TUint32*)RomToActualAddress(pI->iCodeAddress);	// address of code section
       
    43 	TUint32 codeSize=pI->iCodeSize;
       
    44 	TUint32 *pC=pE+(codeSize>>2)-3;
       
    45 	TUint32 low=0;
       
    46 	TUint32 high=0;
       
    47 	TUint32 romLow=0;
       
    48 	TUint32 romHigh=0;
       
    49 	TBool block=EFalse;
       
    50 	TInt blocknum=0;
       
    51 	TRACE(TCOLLAPSE1,Print(ELog,"CollapseImportThunks() File %s[%08x]\n",aFile->iFileName,
       
    52 						   pI->iHardwareVariant));
       
    53 	while(pC>=pE)
       
    54 		{
       
    55 		if (pC[0]==0xe59fc000 && pC[1]==0xe59cf000 && (pC[2]&0xf0000000)==0x50000000)
       
    56 			{
       
    57 // assume this is an import thunk
       
    58 			if (!block)
       
    59 				{
       
    60 				high=(TUint32)(pC+3);
       
    61 				block=ETrue;
       
    62 				}
       
    63 			pC-=3;
       
    64 			}
       
    65 		else
       
    66 			{
       
    67 			if (block)
       
    68 				{
       
    69 				low=(TUint32)(pC+3);
       
    70 				block=EFalse;
       
    71 				TInt numImports=(high-low)/12;
       
    72 				TRACE(TCOLLAPSE2,Print(ELog,"?Import thunk block %08x-%08x %d %d\n",ActualToRomAddress((TAny*)low),ActualToRomAddress((TAny*)high),numImports,aFile->iImportCount));
       
    73 				if (numImports==aFile->iImportCount)
       
    74 					{
       
    75 					if (blocknum==0)
       
    76 						{
       
    77 						romLow=(TUint32)ActualToRomAddress((TAny*)low);
       
    78 						romHigh=(TUint32)ActualToRomAddress((TAny*)high);
       
    79 						}
       
    80 					blocknum++;
       
    81 					}
       
    82 				}
       
    83 			pC--;
       
    84 			}
       
    85 		}
       
    86 	if (blocknum==0)
       
    87 		{
       
    88 		Print(EWarning,"Import thunk block for %s[%08x] not found\n",aFile->iFileName,
       
    89 			  pI->iHardwareVariant);
       
    90 		}
       
    91 	else if (blocknum==1)
       
    92 		{
       
    93 		low=(TUint32)RomToActualAddress(romLow);
       
    94 		high=(TUint32)RomToActualAddress(romHigh);
       
    95 		TRACE(TCOLLAPSE1,Print(ELog,"Import thunk block %08x-%08x\n",romLow,romHigh));
       
    96 		TUint32 *pX;
       
    97 		for (pX=(TUint32*)low; pX<(TUint32*)high; pX+=3)
       
    98 			{
       
    99 			TUint32 *pA=(TUint32*)RomToActualAddress(pX[2]);
       
   100 			TUint32 jumpAddr=*pA;
       
   101 			jumpAddr=FindFinalJumpDestination(jumpAddr);
       
   102 			TInt offset=(TInt)jumpAddr-(TInt)ActualToRomAddress(pX)-8;
       
   103 			if (offset<33554432 && offset>-33554432)
       
   104 				{
       
   105 				pX[0]=0xea000000 | ((offset&0x03ffffff)>>2);
       
   106 				iImportsFixedUp++;
       
   107 				}
       
   108 			}
       
   109 		aFile->iImportBlockStartAddress=romLow;
       
   110 		aFile->iImportBlockEndAddress=romHigh;
       
   111 		}
       
   112 	else
       
   113 		Print(EWarning,"??Import thunk block ambiguous - not changed\n");
       
   114 	return KErrNone;
       
   115 	}
       
   116 
       
   117 TInt E32Rom::CollapseBranches()
       
   118 //
       
   119 // Collapse chained branches
       
   120 //	
       
   121 	{
       
   122 
       
   123 	Print(ELog, "\nCollapsing Chained Branches.\n");
       
   124 	TInt i;
       
   125 	for (i=0; i<iObey->iNumberOfPeFiles; i++)
       
   126 		{
       
   127 		TRomBuilderEntry* file=iPeFiles[i];
       
   128 		if (file->iOrigHdr->iImportOffset && file->iImportBlockEndAddress!=0)
       
   129 			{
       
   130 			TInt r=CollapseBranches(file);
       
   131 			if (r!=KErrNone)
       
   132 				return r;
       
   133 			}
       
   134 		}
       
   135 	return KErrNone;
       
   136 	}
       
   137 
       
   138 inline void SetBit(TUint32* aBitmap, TInt aOffset)
       
   139 	{
       
   140 	aOffset>>=2;
       
   141 	aBitmap[aOffset>>5] |= (1<<(aOffset&0x1f));
       
   142 	}
       
   143 
       
   144 inline TInt BitTest(TUint32* aBitmap, TInt aOffset)
       
   145 	{
       
   146 	aOffset>>=2;
       
   147 	return(aBitmap[aOffset>>5]&(1<<(aOffset&0x1f)));
       
   148 	}
       
   149 
       
   150 TUint32 E32Rom::FindFinalJumpDestination(TUint32 ja)
       
   151 	{
       
   152 // follow a chain of branches to final destination
       
   153 	TUint32 initja=ja;
       
   154 	TUint8* aja=(TUint8*)RomToActualAddress(ja);
       
   155 	FOREVER
       
   156 			{
       
   157 			if ((*(TUint32*)aja &0xff000000)==0xea000000)
       
   158 				{
       
   159 // branch to an unconditional branch
       
   160 				TInt off=(*(TUint32*)aja & 0x00ffffff)<<8;
       
   161 				off>>=6;
       
   162 				if (off==-8)
       
   163 					{
       
   164 // branch to same address
       
   165 					break;
       
   166 					}
       
   167 				ja+=(off+8);
       
   168 				aja+=(off+8);
       
   169 				TRACE(TCOLLAPSE2,Print(ELog,"Chain branch %08x to %08x\n",initja,ja));
       
   170 				}
       
   171 			else
       
   172 				break;
       
   173 			}
       
   174 	return ja;
       
   175 	}
       
   176 
       
   177 
       
   178 TInt E32Rom::CollapseBranches(TRomBuilderEntry* aFile)
       
   179 	{
       
   180 // Main code section is between pI->iCodeAddress and aImportThunkStart. This contains
       
   181 // all the explicit code and interspersed literals.
       
   182 // aImportThunkStart-aImportThunkEnd contains import thunks (already collapsed)
       
   183 // aImportThunkEnd-iat contains template instantiations and virtual destructors
       
   184 // iat-rdata contains import addresses - no need to touch these
       
   185 // rdata-expdir contains constant data and vtables
       
   186 // expdir-end contains exported addresses - no need to touch these
       
   187 	TUint32 impStart=aFile->iImportBlockStartAddress;
       
   188 	TUint32 impEnd=aFile->iImportBlockEndAddress;
       
   189 	TRomImageHeader *pI=aFile->iRomImageHeader;
       
   190 	TRACE(TCOLLAPSE1,Print(ELog,"CollapseBranches() File %s[%08x]\n",aFile->iFileName,
       
   191 						   pI->iHardwareVariant));
       
   192 	TUint32 codeStart=pI->iCodeAddress;
       
   193 	TUint32 codeSize=pI->iCodeSize;
       
   194 	TUint32 codeEnd=codeStart+codeSize;
       
   195 	TUint32 *pC=(TUint32*)RomToActualAddress(codeStart);	// address of code section
       
   196 	TUint32 *pE=(TUint32*)RomToActualAddress(codeEnd);
       
   197 	TUint32 romIat=codeStart+aFile->iHdr->iTextSize;
       
   198 	TUint32 romRdata=romIat+aFile->iImportCount*4;
       
   199 	TUint32 exportDir=pI->iExportDir;
       
   200 	if (exportDir==0)
       
   201 		exportDir=codeEnd;
       
   202 	TRACE(TCOLLAPSE1,Print(ELog,"iat=%08x, rdata=%08x, expdir=%08x, end=%08x\n",romIat,romRdata,exportDir,codeEnd));
       
   203 	TUint32 *pD=new TUint32[(codeSize+127)>>7];
       
   204 	if (!pD)
       
   205 		return KErrNoMemory;
       
   206 	TInt rdataSize=TInt(exportDir)-TInt(romRdata);
       
   207 	TUint32 *pR=new TUint32[(rdataSize+127)>>7];
       
   208 	if (!pR)
       
   209 		return KErrNoMemory;
       
   210 	TInt i;
       
   211 	for (i=0; i<TInt((codeSize+127)>>7); i++)
       
   212 		pD[i]=0;
       
   213 	for (i=0; i<TInt((rdataSize+127)>>7); i++)
       
   214 		pR[i]=0;
       
   215 	TUint32 *pX;
       
   216 // go through code looking for data references
       
   217 	for (pX=pC; pX<pE; pX++)
       
   218 		{
       
   219 		if ((*pX&0x0f3f0000)==0x051f0000)
       
   220 			{
       
   221 // opcode is LDRcc Rn, [PC, #d]
       
   222 			TInt offset=*pX & 0xfff;
       
   223 			if ((*pX&0x00800000)==0)
       
   224 				offset=-offset;
       
   225 			TUint32 eff=(ActualToRomAddress(pX)+8+offset)&~3;
       
   226 			if (eff>=codeStart && eff<codeEnd)
       
   227 				{
       
   228 				SetBit(pD,eff-codeStart);
       
   229 				if (eff<codeEnd-4)
       
   230 					SetBit(pD,eff-codeStart+4);
       
   231 				TUint32 data=*(TUint32*)((TUint8*)pX+(offset&~3)+8);	// fetch data word
       
   232 				if (data>=romRdata && data<exportDir)
       
   233 					{
       
   234 // it's an address of something in .rdata, possibly a vtable
       
   235 					SetBit(pR,data-romRdata);
       
   236 					}
       
   237 				}
       
   238 			}
       
   239 		}
       
   240 
       
   241 	TUint32 *importStart=(TUint32*)RomToActualAddress(impStart);
       
   242 	TUint32 *iatStart=(TUint32*)RomToActualAddress(romIat);
       
   243 	if (iObey->iCollapseMode==ECollapseImportThunksAndVtables)
       
   244 		goto vtablesonly;
       
   245 
       
   246 	// go through .text looking for Bcc and BLcc intstructions
       
   247 	for (pX=pC; pX<importStart; pX++)
       
   248 		{
       
   249 		if ((*pX&0xfe000000)==0xea000000 && BitTest(pD,TInt(pX)-TInt(pC))==0 )
       
   250 			{
       
   251 			TInt off=(*pX & 0x00ffffff)<<8;
       
   252 			off>>=6;
       
   253 			TUint32 pc=ActualToRomAddress(pX)+8;
       
   254 			TUint32 ja=pc+off;
       
   255 			TUint32 initja=ja;
       
   256 			if (ja<codeStart || ja>=codeEnd)
       
   257 				{
       
   258 				TRACE(TCOLLAPSE2,Print(ELog,"??Branch at %08x to %08x??\n",pc-8,ja));
       
   259 				goto notthismodule;
       
   260 				}
       
   261 			TRACE(TCOLLAPSE4,Print(ELog,"Branch at %08x opcode %08x ja=%08x\n",pc-8,*pX,ja));
       
   262 			ja=FindFinalJumpDestination(ja);
       
   263 			if (ja!=initja)
       
   264 				{
       
   265 				off=(TInt(ja)-TInt(pc))>>2;
       
   266 				if (off>-33554432 && off<33554432)
       
   267 					{
       
   268 					TUint32 oldOpc=*pX;
       
   269 					*pX=(*pX & 0xff000000)|(off&0x00ffffff); // fix up branch
       
   270 					TRACE(TCOLLAPSE2,Print(ELog,"Opcode at %08x fixed up from %08x to %08x\n",pc-8,oldOpc,*pX));
       
   271 					iBranchesFixedUp++;
       
   272 					}
       
   273 				}
       
   274 		notthismodule: ;
       
   275 			}
       
   276 		}
       
   277 // go through template instantiations and virtual destructors
       
   278 // looking for Bcc and BLcc intstructions to import thunks
       
   279 	pX=(TUint32*)RomToActualAddress(impEnd);
       
   280 	for (; pX<iatStart; pX++)
       
   281 		{
       
   282 		if ((*pX&0xfe000000)==0xea000000 && BitTest(pD,TInt(pX)-TInt(pC))==0 )
       
   283 			{
       
   284 			TInt off=(*pX & 0x00ffffff)<<8;
       
   285 			off>>=6;
       
   286 			TUint32 pc=ActualToRomAddress(pX)+8;
       
   287 			TUint32 ja=pc+off;
       
   288 			TUint32 initja=ja;
       
   289 			TRACE(TCOLLAPSE4,Print(ELog,"Branch at %08x opcode %08x ja=%08x\n",pc-8,*pX,ja));
       
   290 			if (ja<codeStart || ja>=codeEnd)
       
   291 				{
       
   292 				TRACE(TCOLLAPSE2,Print(ELog,"??Branch at %08x to %08x??\n",pc-8,ja));
       
   293 				goto notthismodule2;
       
   294 				}
       
   295 			ja=FindFinalJumpDestination(ja);
       
   296 			if (ja!=initja)
       
   297 				{
       
   298 				off=(TInt(ja)-TInt(pc))>>2;
       
   299 				if (off>-33554432 && off<33554432)
       
   300 					{
       
   301 					TUint32 oldOpc=*pX;
       
   302 					*pX=(*pX & 0xff000000)|(off&0x00ffffff); // fix up branch
       
   303 					TRACE(TCOLLAPSE2,Print(ELog,"Opcode at %08x fixed up from %08x to %08x\n",pc-8,oldOpc,*pX));
       
   304 					iBranchesFixedUp++;
       
   305 					}
       
   306 				}
       
   307 		notthismodule2: ;
       
   308 			}
       
   309 		}
       
   310 vtablesonly:
       
   311 // go through rdata section looking for vtables with references to import thunks
       
   312 	TUint32 *expStart=(TUint32*)RomToActualAddress(exportDir);
       
   313 	TUint32* pW=(TUint32*)RomToActualAddress(romRdata);
       
   314 	pX=pW;
       
   315 	while(pX<expStart-1)
       
   316 		{
       
   317 // first look for reference to start of vtable
       
   318 // there are always two 0 words at the start of a vtable
       
   319 		if (BitTest(pR,TInt(pX)-TInt(pW)) && pX[0]==0 && pX[1]==0)
       
   320 			{
       
   321 			TRACE(TCOLLAPSE3,Print(ELog,"?vtable at %08x\n",ActualToRomAddress(pX)));
       
   322 // look for next reference - there are no references to
       
   323 // intermediate entries of vtable
       
   324 			TUint32* pY;
       
   325 			for (pY=pX+1; pY<expStart && BitTest(pR,TInt(pY)-TInt(pW))==0; pY++);
       
   326 
       
   327 // pY should now point to the end of the vtable
       
   328 // check all entries except the first two are valid ROM addresses in this module
       
   329 			TRACE(TCOLLAPSE3,Print(ELog,"?vtable at %08x to %08x\n",ActualToRomAddress(pX),ActualToRomAddress(pY)));
       
   330 			TUint32 *pZ;
       
   331 			for (pZ=pX+2; pZ<pY; pZ++)
       
   332 				{
       
   333 				if (*pZ<codeStart || *pZ>=codeEnd)
       
   334 					break;
       
   335 				}
       
   336 			if (pZ==pY)
       
   337 				{
       
   338 // this is a vtable
       
   339 // check each address to see if it is an import thunk and if so fix it up
       
   340 				TRACE(TCOLLAPSE3,Print(ELog,"!vtable at %08x to %08x\n",ActualToRomAddress(pX),ActualToRomAddress(pY)));
       
   341 				for (pZ=pX+2; pZ<pY; pZ++)
       
   342 					{
       
   343 					TUint32 ja=*pZ;
       
   344 					TUint32 initja=ja;
       
   345 					ja=FindFinalJumpDestination(ja);
       
   346 					if (ja!=initja)
       
   347 						{
       
   348 						*pZ=ja;
       
   349 						TRACE(TCOLLAPSE2,Print(ELog,"Vtable entry at %08x fixed up from %08x to %08x\n",ActualToRomAddress(pZ),initja,ja));
       
   350 						iVtableEntriesFixedUp++;
       
   351 						}
       
   352 					}
       
   353 				pX=pY;
       
   354 				}
       
   355 			else
       
   356 				pX++;
       
   357 			}
       
   358 		else
       
   359 			pX++;
       
   360 		}
       
   361 	delete[] pR;
       
   362 	delete[] pD;
       
   363 	return KErrNone;
       
   364 	}
       
   365