userlibandfileserver/fileserver/sfile/sf_lepoc.cpp
changeset 0 a41df078684a
child 21 e7d2d738d3c2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfile/sf_lepoc.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,3339 @@
+// Copyright (c) 1995-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:
+// f32\sfile\sf_lepoc.cpp
+// 
+//
+
+#include "sf_std.h"
+
+#include <e32std.h>
+#include <e32std_private.h>
+#include <e32base.h>
+#include <e32base_private.h>
+#include <e32math.h>
+#include <e32svr.h>
+#include <e32ver.h>
+#include <e32hal.h>
+#include <u32exec.h>
+#define INCLUDE_E32IMAGEHEADER_IMPLEMENTATION
+#include "sf_ldr.h"
+#include <f32image.h>
+#include "sf_image.h"
+#include <e32uid.h>
+#include <e32rom.h>
+#include "sf_cache.h"
+
+#include "sf_pgcompr.h"
+
+_LIT(KLitFinderInconsistent, "LDR-FINDER-INC");
+_LIT(KLitSysBinError, "LDR-SYS\\BIN ERR");
+_LIT8(KSysBin,":\\sys\\bin\\");
+
+#ifdef _DEBUG
+
+enum TLdrEpocPanic
+	{
+	EFuaiNoFixupTable = 0x10,
+	EBcbmNotCodePaged = 0x20,
+	ELfiCodePagingNotSupported = 0x30,
+	EFprUnexpectedFixup = 0x40,
+	};
+
+static void Panic(TLdrEpocPanic aPanic)
+	{
+	_LIT(KPanicCat, "LDR-PNC");
+	User::Panic(KPanicCat, aPanic);
+	}
+
+extern TRequestStatus* ProcessDestructStatPtr;
+extern TBool ProcessCreated;
+
+#endif
+
+extern void DumpImageHeader(const E32ImageHeader*);
+extern TDriveCacheHeader* gDriveFileNamesCache[];
+
+TBuf8<KMaxPath> gLoadeePath;
+TUint NextCodeSegId;
+
+const TInt KMaxHeaderSize = sizeof(E32ImageHeaderV) + 65536/8;
+
+
+#ifdef __X86__
+extern TInt UseFloppy;
+#endif
+
+
+
+// -------- demand paging --------
+
+/** Page size as a power of two. */
+const TUint32 KPageSizeShift = 12;
+/** Page size, as defined for code relocations.  This same page size is used for demand paging. */
+const TUint32 KPageSize = 1<<KPageSizeShift;
+/** Apply this mask to an address to get the page offset. */
+const TUint32 KPageOffsetMask = KPageSize - 1;
+
+/**
+Calculate the number of pages required to contain the supplied number of bytes.
+
+@param	aSizeInBytes	Size of are which has to be contained in whole blocks.
+@return					Number of KPageSize pages required to contain area.
+*/
+inline TInt SizeToPageCount(TInt aSizeInBytes)
+	{
+	return (aSizeInBytes + KPageOffsetMask) >> KPageSizeShift;
+	}
+
+
+/**
+Allocate a block which indexes the reallocations by page.  This can be used for demand paging.
+
+@param	aSection			Pointer to relocation section to process.
+@param	aAreaSize			Size in bytes of area described by reloc section.
+@param  aLoadAddress		Address of relocation section in memory
+@param	aProcessedBlock		On success (return == KErrNone) this is set to the processed
+							relocation section which is allocated on the current thread's heap.
+							The caller takes ownership.  The contents are undefined on failure.
+@return						KErrNoMemory if could not allocate memory for processed block
+							and auxiliary structures; KErrNone otherwise.
+ */
+TInt E32Image::AllocateRelocationData(E32RelocSection* aSection, TUint32 aAreaSize, TUint32 aLoadAddress, TUint32*& aProcessedBlock)
+	{
+	__IF_DEBUG(Printf("AllocateRelocationData"));
+
+	TUint32 sectionSize = aSection->iSize;
+	TUint32 numRelocs = aSection->iNumberOfRelocs;
+	TInt pageCount = SizeToPageCount(aAreaSize);
+
+	// The file format documentation (SOSI ch10) does not guarantee that each page has
+	// relocation information, or that the pages are listed in order, so store them in
+	// page order here.
+	
+	TUint8** subBlocks = (TUint8**)User::AllocZ(sizeof(TUint8*)*pageCount);
+	if(subBlocks == 0)
+		return KErrNoMemory;
+
+	const TUint8* subBlockPtr = (TUint8*)(aSection+1);
+	while(sectionSize > 0)
+		{
+		TUint32 pageOffset = *(TUint32*)(subBlockPtr);
+		TUint32 subBlockSize = *(TUint32*)(subBlockPtr+4);
+
+		subBlocks[pageOffset >> KPageSizeShift] = (TUint8*)subBlockPtr;
+		
+		sectionSize -= subBlockSize;
+		subBlockPtr += subBlockSize;	// move to next sub-block
+		}
+
+	// now have each relocation page in memory, build lookup table	
+	TUint32 indexSize = (pageCount + 1) * sizeof(TUint32);	// include sentinel
+	TUint32 totalRelocations = numRelocs;
+	iCodeRelocTableSize = indexSize + totalRelocations * sizeof(TUint16);
+	TUint8* table = (TUint8*) User::Alloc(iCodeRelocTableSize);
+
+	if(table == 0)
+		{
+		User::Free(subBlocks);
+		return KErrNoMemory;
+		}
+
+	// where sub-block positions are written to in the table
+	TUint32* destSubBlock = (TUint32*)table;
+	// where entries are written to in the table
+	TUint16* destEntry = (TUint16*)(table + indexSize);
+
+	TInt i;
+	for(i = 0; i < pageCount; ++i)
+		{
+		*destSubBlock++ = TUint32(destEntry) - TUint32(table);
+		
+		// see if a relocation page was defined for this page
+		const TUint8* subBlock = subBlocks[i];
+		if(subBlock == 0)
+			continue;
+		
+		// get number of entries in this sub-block, including padding
+		TUint32 sbEntryCount;
+		TUint32 pageOffset = *(TUint32*)subBlock;	// offset of page from start of section
+		sbEntryCount = *(TUint32*)(subBlock + 4);	// sub-block size
+		sbEntryCount -= 8;							// exclude sub-block header
+		sbEntryCount /= 2;							// each entry is two bytes
+		const TUint16* srcEntry = (TUint16*)(subBlock + 8);
+		 
+		while(sbEntryCount--)
+			{
+			TUint16 entry = *srcEntry++;
+			if(entry==0)		// ignore null padding values
+				continue;
+
+			// Replace inferred fixup type with actual fixup type
+			TUint type = entry & 0xf000;
+			if(type==KInferredRelocType)
+				{
+				TUint32* ptr = (TUint32*)(aLoadAddress + pageOffset + (entry & 0x0fff));
+				TUint32 word = *ptr;
+				type = (TUint(word - iHeader->iCodeBase) < TUint(iHeader->iCodeSize)) ? KTextRelocType : KDataRelocType;
+				entry = (entry & 0x0fff) | type;
+				}
+			
+			*destEntry++ = entry;
+			}
+		}
+	
+	// sentinel entry marks the byte following last sub-block in table
+	// This gives the size of the last processed sub-block.
+	*destSubBlock = TUint32(destEntry) - TUint32(table);
+
+	aProcessedBlock = (TUint32*) table;
+	User::Free(subBlocks);
+
+#ifdef _DEBUG
+	__IF_DEBUG(Printf("processed reloc table (size=%d,pageCount=%d)", iCodeRelocTableSize, pageCount));
+
+	// dump the import fixup table if loader tracing enabled
+	const TUint16* table16 = (const TUint16*)table;
+	const TInt halfWordsInTable = iCodeRelocTableSize / 2;
+	for(i = 0; i < halfWordsInTable; i += 4)
+		{
+		__IF_DEBUG(Printf(
+			"reloc %04x: %04x %04x %04x %04x",
+			i * 2, table16[i+0], table16[i+1], table16[i+2], table16[i+3]));
+		}
+#endif
+	return KErrNone;
+	}
+
+
+/*******************************************************************************
+ * These functions run in supervisor mode since they require access to the
+ * chunks of the newly-created process or DLL while they are still in the
+ * home section.
+ ******************************************************************************/
+
+/**
+Vector which ::ExecuteInSupervisorMode invokes.
+*/
+TInt (*ExecuteInSupervisorModeVector)(TSupervisorFunction, TAny*);
+
+/**
+Executute aFunction in supervisor mode (if the memory model requires this.)
+*/
+TInt ExecuteInSupervisorMode(TSupervisorFunction aFunction, TAny* aParameter)
+	{
+	return(*ExecuteInSupervisorModeVector)(aFunction, aParameter);
+	}
+
+/**
+Implementation of ::ExecuteInSupervisorMode which actually executes the
+function in user mode.
+*/
+TInt UserModeExecuteInSupervisorMode(TSupervisorFunction aFunction, TAny* aParameter)
+	{
+	return (*aFunction)(aParameter);
+	}
+
+/**
+Decide whether any Loader code actually needs to execute in supervisor mode
+and set ::ExecuteInSupervisorModeVector so that invocations of ::ExecuteInSupervisorMode
+call the appropriate function.
+*/
+void InitExecuteInSupervisorMode()
+	{
+	// work out if we need to really 'execute in supervisor mode'...
+	TUint32 memModelAttrs = (TUint32)UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, NULL, NULL);
+	TUint32 memModel = memModelAttrs & EMemModelTypeMask;
+	if(memModel==EMemModelTypeFlexible)
+		{
+		// we can do everything user side...
+		ExecuteInSupervisorModeVector = UserModeExecuteInSupervisorMode;
+		gExecutesInSupervisorMode = EFalse;
+		}
+	else
+		{
+		// we need to go kernel side...
+		ExecuteInSupervisorModeVector = UserSvr::ExecuteInSupervisorMode;
+		gExecutesInSupervisorMode = ETrue;
+		}
+	}
+
+
+/**
+Arguments for svRelocateSection.
+
+The relocation information (at iRelocsBuf) has list sub blocks, each referring to a 4kB
+page within the section. See E32RelocBlock.
+*/
+struct SRelocateSectionInfo
+	{
+	E32Image* iImage;		///< The executable being relocated.
+	TUint8* iRelocsBuf;		///< Pointer to relocation info.
+	TUint32 iNumRelocs;		///< Total number of relocations to apply.
+	TUint32 iLoadAddress; 	///< Virtual address where section is currently located in memory.
+	};
+
+/**
+Apply relocations to a code or data section.
+
+@param aPtr Pointer to SRelocateSectionInfo.
+*/
+TInt svRelocateSection(TAny* aPtr)
+	{
+	SRelocateSectionInfo& info=*(SRelocateSectionInfo*)aPtr;
+
+	E32Image& img = *(E32Image*)info.iImage;
+	TUint8* relocs = info.iRelocsBuf;
+	TUint32 numRelocs = info.iNumRelocs;
+	TUint32 loadAddress = info.iLoadAddress;
+
+	TUint32 codeStart = img.iHeader->iCodeBase;
+	TUint32 codeFinish = codeStart+img.iHeader->iCodeSize;
+	TUint32 codeDelta = img.iCodeDelta;
+	TUint32 dataDelta = img.iDataDelta;
+
+	while(numRelocs>0)
+		{
+		TUint32 pageAddress = ((TUint32*)relocs)[0];
+		TUint32 pageSize = ((TUint32*)relocs)[1];
+		TUint8* relocsEnd = relocs+pageSize;
+		relocs += 8;
+
+		while(relocs<relocsEnd)
+			{
+			TUint16 relocOffset = *(TUint16*)relocs;
+			relocs += 2;
+			if(!relocOffset)
+				continue;
+
+			TUint32 offset = pageAddress+(TUint32)(relocOffset&0x0fff);
+			TUint32* destPtr = (TUint32*)(loadAddress+offset);
+			TUint16 relocType = relocOffset&0xf000;
+
+			TUint32 relocAddr = *destPtr;
+			if(relocType==KTextRelocType)
+				relocAddr += codeDelta; // points to text/rdata section
+			else if(relocType==KDataRelocType)
+				relocAddr += dataDelta; // points to data section
+			else if (relocAddr>=codeStart && relocAddr<codeFinish)
+				relocAddr += codeDelta; // points to text/rdata section
+			else
+				relocAddr += dataDelta; // points to data section
+			*destPtr = relocAddr;
+
+			--numRelocs;
+			}
+		}
+	return 0;
+	}
+
+
+/**
+Fix up the export directory
+Only performed on PE images.  ELF image's exports are marked
+as relocatable and therefore relocated by svRelocateSection when the 
+text section is relocated up
+*/
+TInt svRelocateExports(TAny* aPtr)
+	{
+	E32Image* pI=(E32Image*)aPtr;
+	TUint32* destExport=(TUint32*)pI->iExportDirLoad;
+	TInt i=pI->iExportDirCount;
+	TUint32 codeBase=pI->iCodeRunAddress;
+	while (i-->0)
+		*destExport+++=codeBase;
+	return 0;
+	}
+
+
+struct SFixupImportAddressesInfo
+	{
+	TUint32* iIat;
+	TUint32* iExportDir;
+	TUint32 iExportDirEntryDelta;
+	TInt iNumImports;
+	E32Image* iExporter;
+	/**
+	For demand paging, this points to the buffer which is populated
+	so each page can be fixed up as it is loaded in.
+	*/
+	TUint64* iFixup64;
+	// For ElfDerived...
+	TUint32 iCodeLoadAddress;
+	TUint32* iImportOffsetList;
+	};
+
+
+/**
+Fix up the import address table, used for 'PE derived' executables.
+@param aPtr Pointer to function arguments (SFixupImportAddressesInfo structure).
+			SFixupImportAddressesInfo::iIat is updated by this function.
+*/
+TInt svFixupImportAddresses(TAny* aPtr)
+	{
+	SFixupImportAddressesInfo& info = *(SFixupImportAddressesInfo*)aPtr;
+
+	TUint32 maxOrdinal = (TUint32)info.iExporter->iExportDirCount;
+	TUint32 absentOrdinal = (TUint32)info.iExporter->iFileEntryPoint;
+
+	TUint32* exp_dir = info.iExportDir - KOrdinalBase; // address of 0th ordinal
+	TUint32 exp_delta = info.iExportDirEntryDelta;
+
+	TUint32* iat = info.iIat;
+	TUint32* iatE = iat+info.iNumImports;
+	for(; iat<iatE; ++iat)
+		{
+		TUint32 imp = *iat;
+		if(imp>maxOrdinal)
+			return KErrNotSupported;
+
+		TUint32 writeValue;
+		if(imp==0 && !(info.iExporter->iAttr&ECodeSegAttNmdExpData))
+			{
+			// attempt to import ordinal zero (symbol name data) from an executable
+			// which doesn't export this information, use NULL for imported value in this case...
+			writeValue = NULL;
+			}
+		else
+			{
+			// get imported value from exporter...
+			TUint32 exp_addr = exp_dir[imp];
+			if(exp_addr==0 || exp_addr==absentOrdinal)
+				return KErrNotSupported;
+			writeValue = exp_addr + exp_delta;
+			}
+
+		// if not code paging then directly fix up the import...
+		if (info.iFixup64 == 0)
+			*iat = writeValue;
+		else
+		// ...otherwise defer until the page is fixed up
+			{
+			TUint64 iat64 = reinterpret_cast<TUint64>(iat);
+			*info.iFixup64++ = (iat64 << 32) | writeValue;
+			}
+		}
+
+	info.iIat = iat;
+	return KErrNone;
+	}
+
+
+/**
+Fix up the import addresses, used for 'elf derived' executables.
+@param aPtr Pointer to function arguments (SFixupImportAddressesInfo structure).
+*/
+TInt svElfDerivedFixupImportAddresses(TAny* aPtr)
+	{
+	SFixupImportAddressesInfo& info = *(SFixupImportAddressesInfo*)aPtr;
+	TUint32 maxOrdinal = (TUint32)info.iExporter->iExportDirCount;
+	TUint32 absentOrdinal = (TUint32)info.iExporter->iFileEntryPoint;
+
+	TUint32* exp_dir = info.iExportDir - KOrdinalBase; // address of 0th ordinal
+	TUint32 exp_delta = info.iExportDirEntryDelta;
+	TUint32 code = info.iCodeLoadAddress;
+
+	TUint32* iol = info.iImportOffsetList;
+	TUint32* iolE = iol+info.iNumImports;
+	for(; iol<iolE; ++iol)
+		{
+		TUint32* impPtr = (TUint32*)(code+*iol);
+		TUint32 impd = *impPtr;
+		TUint32 imp = impd & 0xffff;
+		TUint32 offset = impd >> 16;
+		if(imp>maxOrdinal)
+			return KErrNotSupported;
+
+		TUint32 writeValue;
+		if(imp==0 && !(info.iExporter->iAttr&ECodeSegAttNmdExpData))
+			{
+			// attempt to import ordinal zero (symbol name data) from an executable
+			// which doesn't export this information, use NULL for imported value in this case...
+			writeValue = NULL;
+			}
+		else
+			{
+			// get imported value from exporter...
+			TUint32 exp_addr = exp_dir[imp];
+			if(exp_addr==0 || exp_addr==absentOrdinal)
+				return KErrNotSupported;
+			writeValue = exp_addr + exp_delta + offset;
+			}
+
+		// if not code paging then directly fix up the import...
+		if (info.iFixup64 == 0)
+			*impPtr = writeValue;
+		// ...otherwise defer until the page is fixed up
+		else
+			{
+			TUint64 impPtr64 = reinterpret_cast<TUint64>(impPtr);
+			*info.iFixup64++ = (impPtr64 << 32) | writeValue;
+			}
+		}
+	return KErrNone;
+	}
+
+
+/**
+Wrapper for memory copy arguments.
+*/
+struct SCopyDataInfo
+	{
+	TAny* iDest;
+	const TAny* iSource;
+	TInt iNumberOfBytes;
+	};
+
+
+/**
+Copies word aligned memory.
+@param aPtr Pointer to function arguments (SCopyDataInfo structure).
+*/
+TInt svWordCopy(TAny* aPtr)
+	{
+	SCopyDataInfo& info=*(SCopyDataInfo*)aPtr;
+	return (TInt) Mem::Move(info.iDest, info.iSource, info.iNumberOfBytes);
+	}
+
+
+/**
+Copies memory.
+@param aPtr Pointer to function arguments (SCopyDataInfo structure).
+*/
+TInt svMemCopy(TAny* aPtr)
+	{
+	SCopyDataInfo& info=*(SCopyDataInfo*)aPtr;
+	return (TInt) Mem::Copy(info.iDest, info.iSource, info.iNumberOfBytes);
+	}
+
+
+/**
+Argument for svElfDerivedGetImportInfo.
+*/
+struct SGetImportDataInfo
+	{
+	TInt iCount;					// number to extract
+	TUint32* iDest;					// destination address for data
+	TUint32 iCodeLoadAddress;		// address where code has been loaded
+	TUint32* iImportOffsetList;		// pointer to list of import offsets in E32ImportBlock
+	};
+
+/**
+Extract import ordinals/data
+@param aPtr Pointer to function arguments (SGetImportDataInfo structure).
+*/
+TInt svElfDerivedGetImportInfo(TAny* aPtr)
+	{
+	SGetImportDataInfo& info = *(SGetImportDataInfo*)aPtr;
+	TInt count = info.iCount;
+	TUint32* dest = info.iDest;
+	TUint32 code = info.iCodeLoadAddress;
+	TUint32* iol = info.iImportOffsetList;
+
+	TUint32* iolEnd = iol+count;
+	while(iol<iolEnd)
+		*dest++ = *(TUint32*)(code + *iol++);
+		
+	return 0;
+	}
+
+/*******************************************************************************
+ * End of supervisor mode functions
+ ******************************************************************************/
+
+
+/*******************************************************************************
+ * RImageInfo
+ ******************************************************************************/
+RImageInfo::RImageInfo()
+	{
+	memclr(this, sizeof(RImageInfo));
+	}
+
+void RImageInfo::Close()
+	{
+	iFile.Close();
+	delete iHeader;
+	iHeader=NULL;
+	gFileDataAllocator.Free(iFileData);
+	iFileData=NULL;
+	}
+
+void RImageInfo::Accept(RImageInfo& aInfo)
+	{
+	Close();
+	wordmove(this, &aInfo, sizeof(RImageInfo));
+	memclr(&aInfo.iFile, (_FOFF(RImageInfo,iFileSize) - _FOFF(RImageInfo,iFile)) );
+	}
+
+/*******************************************************************************
+ * EPOC executable file finders
+ ******************************************************************************/
+RImageFinder::RImageFinder()
+	:	iNameMatches(0), iUidFail(0), iCapFail(0), iMajorVersionFail(0), iImportFail(0),
+		iCurrentVersion(KModuleVersionNull), iCurrentDrive(0), iFindExact(0), iNewValid(0),
+		iReq(0), iExisting(0)
+	{
+	}
+
+TInt RImageFinder::Set(const RLdrReq& aReq)
+	{
+	iReq = &aReq;
+	TInt l = aReq.iFileNameInfo.BaseLen() + aReq.iFileNameInfo.ExtLen();
+	if (l > KMaxProcessName)
+		return KErrBadName;
+	aReq.iFileNameInfo.GetName(iRootName, TFileNameInfo::EIncludeBaseExt);
+	return KErrNone;
+	}
+
+void RImageFinder::Close()
+	{
+	iNew.Close();
+	}
+
+_LIT8(KDefaultPathSysBin, "sys\\bin");
+_LIT8(KDefaultPathSysBin2, "?:\\sys\\bin");
+_LIT8(KDefaultExePath, "sys\\bin;system\\bin;system\\programs;system\\libs");
+_LIT8(KDefaultDllPath, "sys\\bin;system\\bin;system\\libs");
+_LIT8(KDefaultExePath2, "?:\\sys\\bin;?:\\system\\bin;?:\\system\\programs;?:\\system\\libs");
+_LIT8(KDefaultDllPath2, "?:\\sys\\bin;?:\\system\\bin;?:\\system\\libs");
+
+TInt RImageFinder::Search()
+	{
+	__LDRTRACE(iReq->Dump(">RImageFinder::Search"));
+	TBool exe = (iReq->iRequestedUids[0] == KExecutableImageUid);
+	const TFileNameInfo& fi = iReq->iFileNameInfo;
+	TInt r = KErrNone;
+	if (fi.PathLen())
+		{
+		// path specified, so only look there
+		TPtrC8 drive_and_path(fi.DriveAndPath());
+		r = Search(&drive_and_path, 0);
+		}
+	else
+		{
+		TInt drv = -1;
+		if (fi.DriveLen())
+			{
+			// drive specified
+			drv = (*iReq->iFileName)[0];
+			}
+		// if a search path is specified look there
+		if (iReq->iPath)
+			r = Search(iReq->iPath, drv);
+		if (r == KErrNoMemory) // ignore other errors as they are a potential denial of service
+			{
+			__LDRTRACE(Dump("<RImageFinder::Search", r));
+			return r;
+			}
+		const TDesC8* defpath;
+		if(PlatSec::ConfigSetting(PlatSec::EPlatSecEnforceSysBin))
+			defpath = (drv<0) ? &KDefaultPathSysBin() : &KDefaultPathSysBin2();
+		else
+			{
+			if (drv<0)
+				defpath = exe ? &KDefaultExePath() : &KDefaultDllPath();
+			else
+				defpath = exe ? &KDefaultExePath2() : &KDefaultDllPath2();
+			}
+		r = Search(defpath, drv);
+		}
+	if (r == KErrNoMemory)  // ignore other errors as they are a potential denial of service
+		{
+		__LDRTRACE(Dump("<RImageFinder::Search", r));
+		return r;
+		}
+	if (iExisting || iNewValid)
+		r = KErrNone;			// found something suitable
+	else if (!iNameMatches)
+		r = KErrNotFound;		// nothing matched requested name
+	else if (iImportFail || iMajorVersionFail)
+		r = KErrNotSupported;	// something failed only on missing imports or version
+	else if (iCapFail)
+		r = KErrPermissionDenied;	// something failed capability check
+	else if (iUidFail)
+		r = KErrNotSupported;	// something failed UID check
+	else
+		r = KErrCorrupt;		// a file had the correct name but was not a valid E32Image file
+	__LDRTRACE(Dump("<RImageFinder::Search", r));
+	return r;
+	}
+
+TInt RImageFinder::Search(const TDesC8* aPath, TInt aDrive)
+	{
+	__IF_DEBUG(Printf(">Path %S Drive %02x", aPath, aDrive));
+	TInt ppos = 0;
+	TInt plen = aPath->Length();
+	while (ppos < plen)
+		{
+		TPtrC8 remain(aPath->Mid(ppos));
+		TInt pel = remain.Locate(';');
+		if (pel < 0)
+			{
+			pel = remain.Length();
+			ppos += pel;
+			}
+		else
+			{
+			ppos += pel + 1;
+			}
+		if (pel == 0)
+			continue;
+		TBool alldrives = EFalse;
+		if (pel<2 || remain[1]!=':')
+			alldrives = ETrue;
+		else if (remain[0]!='?')
+			aDrive = remain[0];
+		TInt drive = EDriveY;
+		if (!alldrives && RFs::CharToDrive(TChar(aDrive), drive)!=KErrNone)
+			continue;
+		iCurrentDrive = (TUint8)drive;
+		TInt startpos = alldrives ? 0 : 2;
+		iCurrentPath.Set(remain.Mid(startpos, pel - startpos));
+		do	{
+			TInt r;
+#ifdef __X86__
+			if (alldrives && iCurrentDrive<=EDriveB && iCurrentDrive!=UseFloppy)
+				goto bypass_drive;
+#endif
+			r = SearchSingleDir();
+			if (r == KErrNoMemory) // ignore other errors as they are a potential denial of service
+				{
+				__IF_DEBUG(Printf("OOM!"));
+				return r;
+				}
+#ifdef __X86__
+bypass_drive:
+#endif
+			if (!iCurrentDrive--)
+				iCurrentDrive = EDriveZ;
+			} while(alldrives && iCurrentDrive != EDriveY);
+		}
+	__IF_DEBUG(Printf("<Path %S Drive %02x", aPath, aDrive));
+	return KErrNone;
+	}
+
+// Can't be looking for main loadee here, so iReq->iImporter is never NULL
+// Also gExeAttr must be set up
+TInt RImageFinder::SearchExisting(const RImageArray& aArray)
+	{
+	__IF_DEBUG(Printf(">RImageFinder::SearchExisting"));
+	TUint required_abi = gExeAttr & ECodeSegAttABIMask;
+	TInt first, last, i;
+	aArray.Find(iRootName, first, last);
+	for (i=first; i<last; ++i)
+		{
+		E32Image* e = aArray[i];
+		if (CheckUids(e->iUids, iReq->iRequestedUids) != KErrNone)
+			continue;
+		if (iReq->CheckSecInfo(e->iS) != KErrNone)
+			continue;
+		TInt action = DetailedCompareVersions(e->iModuleVersion, iReq->iRequestedVersion, iCurrentVersion, EFalse);
+		if (action == EAction_Skip)
+			continue;
+		if (action == EAction_CheckImports || action == EAction_CheckLastImport)
+			{
+			// Never optimistically link to something with a different ABI
+			if ((e->iAttr & ECodeSegAttABIMask) != required_abi)
+				continue;
+			TInt r = CheckRequiredImports(iReq->iImporter, e, action);
+			if (r != KErrNone)
+				{
+				if (r != KErrNotSupported)
+					return r;
+				continue;
+				}
+			}
+		iExisting = e;
+		iCurrentVersion = e->iModuleVersion;
+		}
+	__IF_DEBUG(Printf("<RImageFinder::SearchExisting"));
+	return KErrNone;
+	}
+
+// Called for each file found with matching root name but which is not a valid E32ImageFile
+void RImageFinder::RecordCorruptFile()
+	{
+	__IF_DEBUG(Printf("RImageFinder::RecordCorruptFile"));
+	++iNameMatches;
+	}
+
+// Called for each valid E32Image file found with matching root name
+TInt RImageFinder::Try(RImageInfo& aInfo, const TDesC8& aRootName, const TDesC8& aDriveAndPath)
+	{
+	__IF_DEBUG(Printf(">RImageFinder::Try %S%S", &aDriveAndPath, &aRootName));
+	__IF_DEBUG(Printf(">MA:%08x MV:%08x RV:%08x CV:%08x", aInfo.iAttr, aInfo.iModuleVersion, iReq->iRequestedVersion, iCurrentVersion));
+	++iNameMatches;
+	if (iFindExact)
+		{
+		if ( ((aInfo.iAttr & ECodeSegAttExpVer) && aInfo.iModuleVersion==iReq->iRequestedVersion)
+			|| (!(aInfo.iAttr & ECodeSegAttExpVer) && iReq->iRequestedVersion==KModuleVersionWild)
+			)
+			{
+			__IF_DEBUG(Printf("<RImageFinder::Try Exact Match Found"));
+			iNewValid = 1;
+			iNew.Accept(aInfo);
+			SetName(aRootName, aDriveAndPath);
+			return KErrCompletion;
+			}
+		return KErrNotFound;
+		}
+	TUint required_abi = gExeAttr & ECodeSegAttABIMask;
+	TBool abi_mismatch = ((aInfo.iAttr & ECodeSegAttABIMask)!=required_abi);
+	TInt32* uid = (TInt32*)&iReq->iRequestedUids;
+	TBool dll_wanted = (uid[0] == KDynamicLibraryUidValue);
+	if (CheckUids(*(TUidType*)aInfo.iUid, iReq->iRequestedUids) != KErrNone)
+		{
+		++iUidFail;
+		__IF_DEBUG(Printf("<RImageFinder::Try UIDFAIL"));
+		return KErrNotFound;
+		}
+	if (iReq->CheckSecInfo(aInfo.iS) != KErrNone)
+		{
+		++iCapFail;
+		__IF_DEBUG(Printf("<RImageFinder::Try CAPFAIL"));
+		return KErrNotFound;
+		}
+	TInt action = DetailedCompareVersions(aInfo.iModuleVersion, iReq->iRequestedVersion, iCurrentVersion, !iReq->iImporter);
+	if (action == EAction_Skip)
+		{
+		if (DetailedCompareVersions(aInfo.iModuleVersion, iReq->iRequestedVersion) == EVersion_MajorSmaller)
+			++iMajorVersionFail;
+		__IF_DEBUG(Printf("<RImageFinder::Try VERFAIL"));
+		return KErrNotFound;
+		}
+	if (action == EAction_CheckImports || action == EAction_CheckLastImport)
+		{
+		// If we get here, can't be main loadee so gExeAttr must be valid
+		// Never optimistically link to something with a different ABI
+		if (abi_mismatch || CheckRequiredImports(iReq->iImporter, aInfo, action)!=KErrNone)
+			{
+			__IF_DEBUG(Printf("<RImageFinder::Try IMPFAIL"));
+			++iImportFail;
+			return KErrNotFound;
+			}
+		}
+	if (!iReq->iImporter && dll_wanted && abi_mismatch)
+		{
+		// Dynamically loading a DLL - ABI must match loading process
+		__IF_DEBUG(Printf("<RImageFinder::Try ABIFAIL"));
+		++iImportFail;
+		return KErrNotFound;
+		}
+	if(PlatSec::ConfigSetting(PlatSec::EPlatSecEnforceSysBin))
+		{
+		TChar driveLetter;
+		TInt driveNumber;
+		TInt r;
+		driveLetter=(TChar)aDriveAndPath[0];
+		RFs::CharToDrive(driveLetter,driveNumber);
+		TDriveCacheHeader* pDH=gDriveFileNamesCache[driveNumber];
+		TUint driveAtt=0;
+		if(pDH)
+			driveAtt=pDH->iDriveAtt;
+		else
+			{
+			TDriveInfo driveInfo;
+			if ((r=gTheLoaderFs.Drive(driveInfo,driveNumber)) != KErrNone)
+				{
+				__IF_DEBUG(Printf("<RImageFinder::Try DINFFAIL"));
+				++iImportFail;
+				return r;
+				}
+			driveAtt=driveInfo.iDriveAtt;
+			}
+
+		if(driveAtt & KDriveAttRemovable)
+			{
+			__IF_DEBUG(Printf("** RImageFinder::Try %S%S is on a removable drive", &aDriveAndPath, &aRootName));
+			// If the cache says we already checked the hash of this file, accept it without checking again
+			// as any *legitimate* change to the file would've triggered the cache to be rebuilt.
+			if (!(aInfo.iCacheStatus & TImageInfo::EHashChecked))
+				{
+				//We have to pass aDriveAndPath as aInfo may not contain Drive
+				TRAP(r,CompareHashL(aInfo, aDriveAndPath));
+				if (r == KErrNoMemory)
+					return r;
+				if(r!=KErrNone)
+					{
+					__IF_DEBUG(Printf("<RImageFinder::Try Compare Hash Failed"));
+					iCapFail++;
+					return r;
+					}
+				aInfo.iCacheStatus |= TImageInfo::EHashChecked;
+				}
+			else
+				{
+				// We've skipped hash checking as an optimisation, however someone could potentially have
+				// used external hardware to switch the data on the card since the cached hash check. Setting
+				// this mark means that if we actually load the file, we'll hash it then; but if it turns out
+				// to be already loaded, we can save the effort.
+				aInfo.iNeedHashCheck = 1;
+				}
+			}
+		}
+	iExisting = NULL;
+	iNew.Accept(aInfo);
+	iNewValid = 1;
+	iCurrentVersion = aInfo.iModuleVersion;
+	SetName(aRootName, aDriveAndPath);
+	__IF_DEBUG(Printf("<MV:%08x RV:%08x CV:%08x", aInfo.iModuleVersion, iReq->iRequestedVersion, iCurrentVersion));
+	__IF_DEBUG(Printf("<RImageFinder::Try OK"));
+	return KErrNone;
+	}
+
+void RImageFinder::CompareHashL(RImageInfo& aInfo, const TDesC8& aDriveAndPath)
+//
+//	Calculate hash and compare after checking if one already exists in c:/system/caps
+//
+	{
+	__IF_DEBUG(Printf(">RImageFinder::CompareHashL"));
+	
+	TInt extraFlag = 0;
+	TBuf8<KMaxFileName*sizeof(TText)> fileName;
+	TFileNameInfo fni = iReq->iFileNameInfo;
+	if (aInfo.iAttr & ECodeSegAttExpVer)
+		{
+		fni.iVersion = aInfo.iModuleVersion;
+		extraFlag = TFileNameInfo::EForceVer;				
+		}
+	
+	TFileName hashname(KSysHash);
+    hashname[0] = (TUint8) RFs::GetSystemDriveChar();
+	fileName.SetLength(0);
+	fni.GetName(fileName, TFileNameInfo::EIncludeBaseExt | extraFlag);
+	hashname.Append(fileName.Expand());
+
+	RFile fHash;
+	CleanupClosePushL(fHash);
+
+	__IF_DEBUG(Printf("RImageFinder::CompareHashL opening hash file %S ", &hashname));
+	User::LeaveIfError(fHash.Open(gTheLoaderFs,hashname,EFileRead|EFileReadDirectIO));
+
+	TBuf8<SHA1_HASH> installhash;
+	User::LeaveIfError(fHash.Read(installhash));
+	CleanupStack::PopAndDestroy(1);
+
+	// if we get this far, we have loaded a valid hash, so calculate the file's hash
+
+	CSHA1* hasher=CSHA1::NewL(); 
+	CleanupStack::PushL(hasher);
+
+	fileName.Copy(aDriveAndPath);
+	fni.GetName(fileName, TFileNameInfo::EIncludeBaseExt | extraFlag);
+
+	CleanupClosePushL(aInfo.iFile);
+	TBool b = aInfo.FileOpened();
+	if(!b)		
+		{
+		__IF_DEBUG(Printf("RImageFinder::CompareHashL opening the file %S", &fileName));
+		User::LeaveIfError(aInfo.iFile.Open(gTheLoaderFs, fileName.Expand(), EFileRead|EFileReadDirectIO));
+		}
+	
+	__IF_DEBUG(Printf("RImageFinder::CompareHashL calculate hash"));
+	TInt size;
+	User::LeaveIfError(aInfo.iFile.Size(size));
+	aInfo.iFileData = (TUint8*)gFileDataAllocator.Alloc(size);
+	if (aInfo.iFileData)
+		aInfo.iFileSize = size;
+	else
+		User::Leave(KErrNoMemory);
+	TPtr8 filedata(aInfo.iFileData, size);
+	User::LeaveIfError(aInfo.iFile.Read(0, filedata, size));
+	if (filedata.Length() != size)
+		User::Leave(KErrCorrupt);
+	CleanupStack::PopAndDestroy(1);	//the file handle only->aInfo.iFile.Close();
+	hasher->Update(filedata);
+		
+	TBuf8<SHA1_HASH> hash;
+	hash=hasher->Final(); 
+
+
+	__IF_DEBUG(Printf("RImageFinder::CompareHashL comparing hashes..."));
+	if(0 != hash.Compare(installhash))
+		User::Leave(KErrPermissionDenied);
+	CleanupStack::PopAndDestroy(1);
+	
+	// if we get this far the hash has passed and the file has been closed
+	// but some of the RImageInfo parameters will've been initialised by the cache
+	// and may be lies if we're being attacked, so compare them to be sure
+
+	// if we already had the header, throw it away: it's from untrusted data
+	if (aInfo.iHeader)
+		{
+		delete aInfo.iHeader;
+		aInfo.iHeader = NULL;
+		}
+
+	// make the header and validate the cached parameters against it
+	User::LeaveIfError(E32ImageHeader::New(aInfo.iHeader, aInfo.iFileData, aInfo.iFileSize));
+
+	SSecurityInfo secinfo;
+	aInfo.iHeader->GetSecurityInfo(secinfo);
+	TUint32 attr = (aInfo.iHeader->iFlags & ECodeSegAttFixed) | aInfo.iHeader->ABI();
+	if(aInfo.iHeader->iFlags&KImageNmdExpData)
+		attr |= ECodeSegAttNmdExpData;
+	if (Mem::Compare((TUint8*)aInfo.iUid, sizeof(aInfo.iUid), (TUint8*)&aInfo.iHeader->iUid1, sizeof(aInfo.iUid))
+			|| aInfo.iModuleVersion != aInfo.iHeader->ModuleVersion()
+			|| Mem::Compare((TUint8*)&aInfo.iS, sizeof(aInfo.iS), (TUint8*)&secinfo, sizeof(secinfo))
+			|| (aInfo.iAttr & ~ECodeSegAttExpVer) != attr)
+		User::Leave(KErrPermissionDenied);
+
+	__IF_DEBUG(Printf("<RImageFinder::CompareHashL passed"));
+	}
+
+void RImageFinder::SetName(const TDesC8& aRootName, const TDesC8& aDriveAndPath)
+	{
+	iNewFileName = aDriveAndPath;
+	iNewFileName.Append(aRootName);
+	}
+
+RImageArray::RImageArray()
+	:	RPointerArray<E32Image>(8, 2*256)
+	{
+	}
+
+TInt RImageArray::Add(E32Image* aImage)
+	{
+	return InsertInOrderAllowRepeats(aImage, &E32Image::Order);
+	}
+
+void RImageArray::Find(const TDesC8& aRootName, TInt& aFirst, TInt& aLast) const
+	{
+	TCodeSegCreateInfo name;
+	name.iFileName.Copy(aRootName);
+	name.iRootNameOffset = 0;
+	name.iRootNameLength = aRootName.Length();
+	aFirst = SpecificFindInOrder((const E32Image*)&name, &E32Image::Order, EArrayFindMode_First);
+	aLast = aFirst;
+	if (aFirst >= 0)
+		aLast = SpecificFindInOrder((const E32Image*)&name, &E32Image::Order, EArrayFindMode_Last);
+	}
+
+E32Image* RImageArray::Find(const TRomImageHeader* a) const
+	{
+	TInt c = Count();
+	if (!c)
+		return NULL;
+	E32Image* const * ee = &(*this)[0];
+	E32Image* const * eE = ee + c;
+	for (; ee<eE && (*ee)->iRomImageHeader != a; ++ee) {}
+	return (ee<eE) ? *ee : NULL;
+	}
+
+TInt E32Image::LoadProcess(const RLdrReq& aReq)
+	{
+	__LDRTRACE(aReq.Dump("E32Image::LoadProcess"));
+
+	RImageFinder finder;
+	TInt r = finder.Set(aReq);
+	if (r == KErrNone)
+		r = finder.Search();
+	if (r!=KErrNone)
+		{
+		finder.Close();
+		return r;
+		}
+	r = Construct(finder);	// needs to find it if it's already loaded
+	finder.Close();
+	if (r!=KErrNone)
+		{
+		return r;
+		}
+	if (iIsDll)
+		return KErrNotSupported;
+	r = aReq.iMsg->Client((RThread&)aReq.iClientThread);
+	if (r!=KErrNone)
+		{
+		return r;
+		}
+	iClientHandle=aReq.iClientThread.Handle();
+	
+	if(iStackSize < aReq.iMinStackSize)
+		iStackSize=aReq.iMinStackSize;		// If the process required larger stack than the default.
+
+	//initialise to zero
+#ifdef _DEBUG
+	iDestructStat = ProcessDestructStatPtr;
+#endif
+	iDebugAttributes = 0;
+	if (iRomImageHeader)
+		{
+		if (iRomImageHeader->iFlags & KRomImageDebuggable)
+			iDebugAttributes |= EDebugAllowed;
+		}
+	else if (iHeader)
+		{
+		if (iHeader->iFlags & KImageDebuggable)
+			iDebugAttributes |= EDebugAllowed;
+		}
+	
+	// Get the data paging flags and pass to the kernel.
+	__ASSERT_COMPILE(EDataPagingUnspecified == 0);
+	if (iRomImageHeader)
+		{
+		TUint dataPaging = iRomImageHeader->iFlags & KRomImageDataPagingMask;
+		if (dataPaging == KRomImageDataPagingMask)
+			RETURN_FAILURE(KErrCorrupt);
+		if (dataPaging == KRomImageFlagDataPaged)
+			iFlags |= EDataPaged;
+		if (dataPaging == KRomImageFlagDataUnpaged)
+			iFlags |= EDataUnpaged;
+		}
+	else if (iHeader)
+		{
+		TUint dataPaging = iHeader->iFlags & KImageDataPagingMask;
+		if (dataPaging == KImageDataPagingMask)
+			RETURN_FAILURE(KErrCorrupt);
+		if (dataPaging == KImageDataPaged)
+			iFlags |= EDataPaged;
+		if (dataPaging == KImageDataUnpaged)
+			iFlags |= EDataUnpaged;
+		}
+		
+	r=E32Loader::ProcessCreate(*this, aReq.iCmd);
+	__IF_DEBUG(Printf("Done E32Loader::ProcessCreate %d",r));
+	if (r!=KErrNone)
+		{
+		return r;
+		}
+#ifdef _DEBUG
+	ProcessCreated = ETrue;
+#endif
+	iClientProcessHandle=iProcessHandle;
+	if (!iAlreadyLoaded)
+		{
+		gExeCodeSeg=iHandle;	// implicitly linked DLLs must load into the new process
+		gExeAttr=iAttr;
+		if (!iRomImageHeader)
+			r=LoadToRam();
+		if (r==KErrNone)
+			r=ProcessImports();	// this sets up gLoadeePath
+		}
+	// transfers ownership of clamp handle to codeseg; nulls handle if successful
+	if (r==KErrNone)
+		{
+		r=E32Loader::ProcessLoaded(*this);
+		if ((r==KErrNone) && iUseCodePaging)
+			{
+			iFileClamp.iCookie[0]=0;// null handle to indicate 
+			iFileClamp.iCookie[1]=0;// transfer of ownership of clamp handle to proc's codeseg
+			}
+		}
+	__IF_DEBUG(Printf("Done E32Image::LoadProcess %d",r));
+	return r;
+	}
+
+// Load a code segment, plus all imports if main loadee
+TInt E32Image::LoadCodeSeg(const RLdrReq& aReq)
+	{
+	__LDRTRACE(aReq.Dump(">E32Image::LoadCodeSeg"));
+
+#ifdef __X86__
+	if (iMain==this && iClientProcessHandle)
+		{
+		RProcess p;
+		p.SetHandle(iClientProcessHandle);
+		TFileName f(p.FileName());
+		if (f.Length()>=2 && f[1]==':')
+			{
+			TInt d = f[0];
+			if (d=='a' || d=='A')
+				UseFloppy = EDriveA;
+			else if (d=='b' || d=='B')
+				UseFloppy = EDriveB;
+			}
+		}
+#endif
+
+	RImageFinder finder;
+	TInt r = finder.Set(aReq);
+	if (r == KErrNone)
+		r = finder.Search();
+	if (r!=KErrNone)
+		{
+		finder.Close();
+		return r;
+		}
+	return DoLoadCodeSeg(aReq, finder);
+	}
+
+// Load a code segment, plus all imports if main loadee
+TInt E32Image::DoLoadCodeSeg(const RLdrReq& aReq, RImageFinder& aFinder)
+	{
+	__LDRTRACE(aReq.Dump(">E32Image::DoLoadCodeSeg"));
+
+	TInt r = Construct(aFinder);	// needs to find it if it's already loaded
+	aFinder.Close();
+	if (r!=KErrNone)
+		{
+		return r;
+		}
+	__IF_DEBUG(Printf("epv=%x, fep=%x, codesize=%x, textsize=%x, uid3=%x",iEntryPtVeneer,iFileEntryPoint,iCodeSize,iTextSize,iUids[2]));
+	__IF_DEBUG(Printf("attr=%08x, gExeAttr=%08x",iAttr,gExeAttr));
+
+	// If EXE and not main loadee, EXE code segment must be the same as the client process or newly loaded process
+	if (gExeCodeSeg && !iIsDll && iMain!=this && iHandle!=gExeCodeSeg)
+		return KErrNotSupported;
+
+	// If DLL and main loadee, ABI must match the process
+	if (iIsDll && iMain==this && (iAttr & ECodeSegAttABIMask)!=(gExeAttr & ECodeSegAttABIMask) )
+		return KErrNotSupported;
+
+	// code segment already loaded
+	if (iAlreadyLoaded || (iMain!=this && AlwaysLoaded()) )
+		return KErrNone;
+
+	__IF_DEBUG(Printf("CodeSeg create"));
+	r=E32Loader::CodeSegCreate(*this);
+	if (r!=KErrNone)
+		return r;
+	
+	iCloseCodeSeg=iHandle;	// so new code segment is removed if the load fails
+	if (!iRomImageHeader)
+		r=LoadToRam();
+	if (r==KErrNone)
+		{
+		iCloseCodeSeg=NULL;
+		if (iMain==this)
+			{
+			r=ProcessImports();	// this sets up gLoadeePath
+			// transfers ownership of clamp handle to codeseg; nulls handle if successful
+			if (r==KErrNone)
+				{
+				r=E32Loader::CodeSegLoaded(*this);
+				if ((r==KErrNone) && iUseCodePaging)
+					{
+					iFileClamp.iCookie[0]=0;// null handle to indicate 
+					iFileClamp.iCookie[1]=0;// transfer of ownership of clamp handle to codeseg
+					}
+				}
+			}
+		}
+
+	__IF_DEBUG(Printf("<DoLoadCodeSeg, r=%d, iIsDll=%d",r,iIsDll));
+	return r;
+	}
+
+// Load a ROM XIP code segment as part of another load
+TInt E32Image::DoLoadCodeSeg(const TRomImageHeader& a)
+	{
+	__IF_DEBUG(Printf("E32Image::DoLoadCodeSeg ROM XIP @%08x",&a));
+	
+	Construct(a);
+	if (AlwaysLoaded())
+		{
+		GetRomFileName();
+		return KErrNone;
+		}
+	TInt r=CheckRomXIPAlreadyLoaded();
+	if (r!=KErrNone || iAlreadyLoaded)
+		{
+		return r;
+		}
+	GetRomFileName();
+	r=E32Loader::CodeSegCreate(*this);
+
+	__IF_DEBUG(Printf("<DoLoadCodeSeg, r=%d",r));
+	return r;
+	}
+
+/******************************************************************************
+ * EPOC specific E32Image functions
+ ******************************************************************************/
+
+/**
+Construct an image object which represents an XIP ROM executable.
+*/
+void E32Image::Construct(const TRomImageHeader& a)
+	{
+	__IF_DEBUG(Printf("E32Image::Construct ROM %08x",&a));
+
+	iRomImageHeader = &a;
+	iUids = *(const TUidType*)&a.iUid1;
+	iS = a.iS;
+	iCodeSize = a.iCodeSize;
+	iTextSize = a.iTextSize;
+	iDataSize = a.iDataSize;
+	iBssSize = a.iBssSize;
+	iTotalDataSize = a.iTotalDataSize;
+	iEntryPtVeneer = 0;
+	iFileEntryPoint = a.iEntryPoint;
+	iDepCount = a.iDllRefTable ? a.iDllRefTable->iNumberOfEntries : 0;
+	iExportDir = a.iExportDir;
+	iExportDirCount = a.iExportDirCount;
+	iCodeLoadAddress = (TUint32)&a;
+	iDataRunAddress = a.iDataBssLinearBase;	// for fixed processes
+	iHeapSizeMin = a.iHeapSizeMin;
+	iHeapSizeMax = a.iHeapSizeMax;
+	iStackSize = a.iStackSize;
+	iPriority = a.iPriority;
+	iIsDll = (a.iFlags & KImageDll)!=0;
+	if(iExportDirCount)
+		iExportDirLoad = iExportDir;
+
+	// setup attributes...
+	iAttr &= ~(ECodeSegAttKernel|ECodeSegAttGlobal|ECodeSegAttFixed|ECodeSegAttABIMask|ECodeSegAttNmdExpData);
+	if(a.iFlags&KRomImageFlagsKernelMask)
+		iAttr |= ECodeSegAttKernel;
+	else
+		iAttr |= ECodeSegAttGlobal;
+	if(a.iFlags&KRomImageFlagFixedAddressExe)
+		iAttr |= ECodeSegAttFixed;
+	iAttr |= (a.iFlags & KRomImageABIMask);
+	if(a.iFlags&KRomImageNmdExpData)
+		iAttr |= ECodeSegAttNmdExpData;
+	if(a.iFlags&KRomImageSMPSafe)
+		iAttr |= ECodeSegAttSMPSafe;
+	
+	iExceptionDescriptor = a.iExceptionDescriptor;
+	}
+
+
+TBool E32Image::AlwaysLoaded()
+	{
+	// If loaded from ROM and EXE or DLL with no static data or extension or variant, don't need code segment
+	TBool r=EFalse;
+	__IF_DEBUG(Printf(">E32Image::AlwaysLoaded %08x",iRomImageHeader));
+	if (iRomImageHeader)
+		{
+		if (iIsDll && (iRomImageHeader->iFlags & KRomImageFlagDataPresent)==0)
+			r=ETrue;
+		}
+	__IF_DEBUG(Printf("<E32Image::AlwaysLoaded %x",r));
+	return r;
+	}
+
+
+void E32Image::GetRomFileName()
+	{
+	TBuf8<KMaxFileName> fn = _S8("z:\\");
+	TFileNameInfo fni;
+	TPtr8 path_and_name(((TText8*)fn.Ptr())+3, 0, KMaxFileName-3);
+	const TRomDir& rootdir = *(const TRomDir*)UserSvr::RomRootDirectoryAddress();
+	if (!TraverseDirs(rootdir, iRomImageHeader, path_and_name))
+		*(const TAny**)1=iRomImageHeader;	// DIE!
+	fn.SetLength(path_and_name.Length()+3);
+	fni.Set(fn, 0);
+	iFileName.Zero();
+	fni.GetName(iFileName, TFileNameInfo::EIncludeDrivePathBaseExt);
+	if (fni.VerLen())
+		iAttr |= ECodeSegAttExpVer;
+	iRootNameOffset = fni.iBasePos;
+	iRootNameLength = fni.BaseLen() + fni.ExtLen();
+	iExtOffset = iFileName.Length() - fni.ExtLen();
+	__IF_DEBUG(Printf("GetRomFileName(%08x)->%S,%d,%d,%d Attr %08x",iRomImageHeader,&iFileName,iRootNameOffset,iRootNameLength,iExtOffset,iAttr));
+	}
+
+
+/**
+Starting from aDir, search for XIP executable specified by aHdr.
+If found, return true and set aName to file path and name, (will cause descriptor panics if max size of aName isn't big enough.)
+If not found, return false.
+*/
+TBool E32Image::TraverseDirs(const TRomDir& aDir, const TRomImageHeader* aHdr, TDes8& aName)
+	{
+	const TRomEntry* pE=&aDir.iEntry;
+	const TRomEntry* pEnd=(const TRomEntry*)((TUint8*)pE+aDir.iSize);
+	while(pE<pEnd)
+		{
+		if ( (pE->iAtt & KEntryAttXIP) && (pE->iAddressLin==(TLinAddr)aHdr) )
+			{
+			// ROM XIP file found
+			aName.Copy(TPtrC16((const TText*)pE->iName, pE->iNameLength));
+			return ETrue;
+			}
+		if (pE->iAtt & KEntryAttDir)
+			{
+			// subdirectory found
+			const TRomDir& subdir = *(const TRomDir*)pE->iAddressLin;
+			TText8* p = (TText8*)aName.Ptr();
+			TInt m = aName.MaxLength();
+			TInt nl = pE->iNameLength;
+			TPtr8 ptr(p+nl+1, 0, m-nl-1);
+			if (TraverseDirs(subdir, aHdr, ptr))
+				{
+				// match found in subdirectory
+				aName.SetLength(ptr.Length()+nl+1);
+				const TText* s = (const TText*)pE->iName;
+				p[nl]='\\';
+				while (nl--)
+					*p++ = (TText8)*s++;
+				return ETrue;
+				}
+			}
+		TInt entry_size = KRomEntrySize + pE->iNameLength*sizeof(TText);
+		entry_size = (entry_size+sizeof(TInt)-1)&~(sizeof(TInt)-1);
+		pE=(const TRomEntry*)((TUint8*)pE+entry_size);
+		}
+	return EFalse;
+	}
+
+
+/**
+Read data from a file.
+*/
+TInt FileRead(RFile& aFile, TUint8* aDest, TInt aSize)
+	{
+	TPtr8 p(aDest,aSize,aSize);
+	TInt r = aFile.Read(p,aSize);
+	if(r==KErrNone && p.Size()!=aSize)
+		RETURN_FAILURE(KErrCorrupt);
+	return r;
+	}
+
+
+/**
+Construct a new image header by reading a file. File must not be XIP.
+*/
+TInt E32ImageHeader::New(E32ImageHeader*& aHdr, RFile& aFile)
+	{
+	aHdr = NULL;
+
+	TInt fileSize;
+	TInt r = aFile.Size(fileSize);
+	if(r!=KErrNone)
+		return r;
+
+	E32ImageHeaderV tempHeader;
+	r = FileRead(aFile, (TUint8*)&tempHeader, sizeof(tempHeader));
+	if(r!=KErrNone)
+		return r;
+
+	TUint headerSize = tempHeader.TotalSize();
+	if(headerSize<sizeof(tempHeader) || headerSize>TUint(KMaxHeaderSize))
+		RETURN_FAILURE(KErrCorrupt);
+
+	E32ImageHeaderV* header = (E32ImageHeaderV*)User::Alloc(headerSize);
+	if(!header)
+		return KErrNoMemory;
+
+	wordmove(header, &tempHeader, sizeof(tempHeader));
+	if(headerSize>sizeof(tempHeader))
+		r = FileRead(aFile, ((TUint8*)header)+sizeof(tempHeader), headerSize-sizeof(tempHeader));
+
+	if(r==KErrNone)
+		r = header->ValidateAndAdjust(fileSize);
+
+	if(r==KErrNone)
+		aHdr = header;
+	else
+		delete header;
+
+	return r;
+	}
+
+
+/**
+Construct a new image header using data from the supplied buffer.
+*/
+TInt E32ImageHeader::New(E32ImageHeader*& aHdr, TUint8* aFileData, TUint32 aFileSize)
+	{
+	aHdr = NULL;
+
+	E32ImageHeaderV& tempHeader = *(E32ImageHeaderV*)aFileData;
+
+	if(aFileSize<sizeof(tempHeader))
+		RETURN_FAILURE(KErrCorrupt); // too small to contain a header
+
+	TUint headerSize = tempHeader.TotalSize();
+	if(headerSize<sizeof(tempHeader) || headerSize>TUint(KMaxHeaderSize))
+		RETURN_FAILURE(KErrCorrupt);
+	if(headerSize>aFileSize)
+		RETURN_FAILURE(KErrCorrupt);
+
+	E32ImageHeaderV* header = (E32ImageHeaderV*)User::Alloc(headerSize);
+	if(!header)
+		return KErrNoMemory;
+
+	wordmove(header, &tempHeader, headerSize);
+
+	TInt r = header->ValidateAndAdjust(aFileSize);
+	if(r==KErrNone)
+		aHdr = header;
+	else
+		delete header;
+
+	return r;
+	}
+
+
+/**
+Validate header, then adjust:
+- iUncompressedSize to contain size of data even when file is not compressed.
+- Platform security capability to include all disabled capabilities and exclude invalid ones.
+
+@param aFileSize Total size of the file containing the image data.
+*/
+TInt E32ImageHeaderV::ValidateAndAdjust(TUint32 aFileSize)
+	{
+	// check header is valid...
+	TUint32 uncompressedSize;
+	TInt r = ValidateHeader(aFileSize,uncompressedSize);
+	if(r!=KErrNone)
+		return r;
+
+	// set size of data when uncompressed...
+	iUncompressedSize = uncompressedSize;
+
+	// override capabilities in image to conform to system wide configuration...
+	for(TInt i=0; i<SCapabilitySet::ENCapW; i++)
+		{
+		iS.iCaps[i] |= DisabledCapabilities[i];
+		iS.iCaps[i] &= AllCapabilities[i];
+		}
+
+	return KErrNone;
+	}
+
+
+TInt E32Image::Construct(RImageFinder& aFinder)
+	{
+	__IF_DEBUG(Printf("E32Image::iMain=%08x", iMain));
+	__LDRTRACE(aFinder.Dump(">E32Image::Construct", 0));
+	__ASSERT_ALWAYS(aFinder.iNewValid, User::Panic(KLitFinderInconsistent, 0));
+
+	// fallback security check to ensure we don't try and load an executable from an insecure location...
+	if(PlatSec::ConfigSetting(PlatSec::EPlatSecEnforceSysBin))
+		{
+		__ASSERT_ALWAYS(aFinder.iNewFileName.Length()>=11, User::Panic(KLitSysBinError, 0));
+		__ASSERT_ALWAYS(KSysBin().CompareF(TPtrC8(aFinder.iNewFileName.Ptr()+1,10))==0, User::Panic(KLitSysBinError, 1));
+		}
+
+	TInt r = KErrNone;
+
+	// setup file name info...
+	iFileName.Copy(aFinder.iNewFileName);
+	TFileNameInfo fi;
+	fi.Set(iFileName, 0);
+	iRootNameOffset = fi.iBasePos;
+	iRootNameLength = fi.iLen - fi.iBasePos;
+	iExtOffset = fi.iExtPos;
+
+	// setup version...
+	iAttr |= aFinder.iNew.iAttr & ECodeSegAttExpVer;
+	iModuleVersion = aFinder.iNew.iModuleVersion;
+
+	if(aFinder.iNew.iRomImageHeader)
+		{
+		// we're 'loading' an XIP executable from ROM...
+		Construct(*aFinder.iNew.iRomImageHeader);
+		if(!AlwaysLoaded() || iMain==this)
+			r = CheckRomXIPAlreadyLoaded();
+		return r;
+		}
+
+	// setup more image info...
+	iAttr |= aFinder.iNew.iAttr & (ECodeSegAttFixed|ECodeSegAttABIMask|ECodeSegAttNmdExpData);
+	iUids = *(const TUidType*)&aFinder.iNew.iUid;
+	iIsDll = !(iUids[0].iUid == KExecutableImageUidValue);
+	iS = aFinder.iNew.iS;
+
+	// check if executable has already been loaded...
+	r = CheckAlreadyLoaded();
+	if(r!=KErrNone)
+		return r;
+
+	// if we are going to need to load it...
+	if(!iAlreadyLoaded || !iIsDll)
+		{
+		if (aFinder.iNew.iNeedHashCheck)
+			{
+			// we need to check the file hash; the check in RImageFinder::Try
+			// was skipped based on the cache. If it fails here, though, someone
+			// is tampering with us and we can just fail the load.
+			TRAP(r,aFinder.CompareHashL(aFinder.iNew, fi.DriveAndPath()));
+			if (r != KErrNone)
+				return r;
+			}
+
+		if(aFinder.iNew.iFileData)
+			{
+			// take ownership of the file data aFinder has already read in...
+			iFileData = aFinder.iNew.iFileData;
+			aFinder.iNew.iFileData = NULL;
+			iFileSize = aFinder.iNew.iFileSize;
+			}
+		else if(aFinder.iNew.FileOpened())
+			{
+			// take ownership of the file handle that aFinder has already opened...
+			iFile = aFinder.iNew.iFile;
+			memclr(&aFinder.iNew.iFile, sizeof(RFile));
+			}
+		else
+			{
+			// no resource obtained from aFinder, so create a file handle for ourselves...
+			r = OpenFile();
+			if(r!=KErrNone)
+				return r;
+			}
+
+		// take ownership of header...
+		iHeader = aFinder.iNew.iHeader;
+		aFinder.iNew.iHeader = NULL;
+
+		// if there wast't a header, then create one now...
+		if(!iHeader)
+			{
+			if(iFileData)
+				r = E32ImageHeader::New(iHeader, iFileData, iFileSize);
+			else
+				r = E32ImageHeader::New(iHeader, iFile);
+			if(r!=KErrNone)
+				return r;
+			}
+
+		// setup info needed for process creation...
+		iHeapSizeMin = iHeader->iHeapSizeMin;
+		iHeapSizeMax = iHeader->iHeapSizeMax;
+		iStackSize = iHeader->iStackSize;
+		iPriority = iHeader->ProcessPriority();
+		}
+
+	// if already loaded...
+	if(iAlreadyLoaded)
+		return KErrNone; // nothing more to do
+
+	// setup info needed to load an executable...
+	iDepCount = iHeader->iDllRefTableCount;
+	iExportDirCount = iHeader->iExportDirCount;
+	iExportDir = iHeader->iExportDirOffset-iHeader->iCodeOffset;
+	iTextSize = iHeader->iTextSize;
+	iCodeSize = iHeader->iCodeSize;
+	__IF_DEBUG(Printf("Code + const %x",iCodeSize));
+	iDataSize = iHeader->iDataSize;
+	__IF_DEBUG(Printf("Data %x",iDataSize));
+	iBssSize = iHeader->iBssSize;
+	__IF_DEBUG(Printf("Bss %x",iBssSize));
+	iTotalDataSize = iDataSize+iBssSize;
+
+	iFileEntryPoint = iHeader->iEntryPoint;	// just an offset at this stage
+	iEntryPtVeneer = 0;
+	iExceptionDescriptor = iHeader->ExceptionDescriptor();
+	if(iHeader->iExportDirOffset)
+		iExportDirLoad = iExportDir;		// only set this if not already loaded
+
+	// initialise the SMP safe flag from the image header
+	// this will get cleared during ProcessImports if any import is not SMP safe
+	if(iHeader->iFlags & KImageSMPSafe)
+		iAttr |= ECodeSegAttSMPSafe;
+	else
+		{
+		__IF_DEBUG(Printf("%S is not marked SMP safe", &iFileName));
+		iAttr &= ~ECodeSegAttSMPSafe;
+		}
+
+	// check if executable is to be demand paged...
+	r = ShouldBeCodePaged(iUseCodePaging);
+	__IF_DEBUG(Printf("ShouldBeCodePaged r=%d,iUseCodePaging=%d", r, iUseCodePaging));
+	if(iUseCodePaging==EFalse || r!=KErrNone)
+		return r;
+
+	// image needs demand paging, create the additional information needed for this...
+
+	// read compression info...
+	iCompressionType = iHeader->iCompressionType;
+	r = LoadCompressionData();
+	if(r==KErrNotSupported)
+		{
+		// Compression type not supported, so just load executable as normal, (without paging)...
+		iUseCodePaging = EFalse;
+		return KErrNone;
+		}
+	else if (r!=KErrNone)
+		return r;
+
+	// clamp file so it doesn't get modified whilst it is being demand paged...
+	r = iFileClamp.Clamp(iFile);
+	// The clamp API will return KErrNotSupported if the media is removable: 
+	// this implies that paging is not possible but the binary can still be loaded
+	if (r != KErrNone)
+		{
+		iUseCodePaging = EFalse;
+		return r == KErrNotSupported ? KErrNone : r;
+		}
+
+	// get blockmap data which indicates location of media where file contents are stored...
+	r = BuildCodeBlockMap();
+	__IF_DEBUG(Printf("BuildCodeBlockMap r=%d", r));
+	if(r==KErrNotSupported)
+		{
+		// media doesn't support demand paging, so just load executable as normal, (without paging)...
+		iUseCodePaging = EFalse;
+		iFileClamp.Close(gTheLoaderFs);
+		r = KErrNone;
+		}
+
+	return r;
+	}
+
+
+TInt E32Image::CheckRomXIPAlreadyLoaded()
+	{
+	__IF_DEBUG(Printf("ROM XIP %08x CheckAlreadyLoaded",iRomImageHeader));
+	TFindCodeSeg find;
+	find.iRomImgHdr=iRomImageHeader;
+	E32Loader::CodeSegDeferDeletes();
+	TAny* h=NULL;
+	TInt r=KErrNone;
+	E32Loader::CodeSegNext(h, find);
+	if (h)
+		{
+		iHandle=h;
+		r=E32Loader::CodeSegOpen(h, iClientProcessHandle);
+		if (r==KErrNone)
+			E32Loader::CodeSegInfo(iHandle, *this);
+		}
+	E32Loader::CodeSegEndDeferDeletes();
+	if (iHandle && r==KErrNone)
+		{
+		iAlreadyLoaded=ETrue;
+		__IF_DEBUG(Printf("ROM XIP %08x already loaded", iHandle));
+		}
+	__IF_DEBUG(Printf("ROM XIP CheckAlreadyLoaded returns %d",r));
+	return r;
+	}
+
+
+/**
+Read the E32Image file into its code and data chunks, relocating them
+as necessary.
+Create a dll reference table from the names of dlls referenced.
+Fix up the import address table and the export table for real addresses.
+*/
+TInt E32Image::LoadToRam()
+	{
+	__IF_DEBUG(Printf("E32Image::LoadToRam %S",&iFileName));
+
+	// offset of data after code which will be erad into iRestOfFileData...
+	iConversionOffset = iHeader->iCodeOffset + iHeader->iCodeSize;
+
+	// calculate sizes...
+	TUint totalSize = ((E32ImageHeaderV*)iHeader)->iUncompressedSize;
+	TUint remainder = totalSize-iConversionOffset;
+	if(remainder>totalSize)
+		RETURN_FAILURE(KErrCorrupt); // Fuzzer can't trigger this because header validation prevents it
+
+	iRestOfFileData = (TUint8*)User::Alloc(remainder);
+	if(!iRestOfFileData)
+		return KErrNoMemory;
+	iRestOfFileSize = remainder;
+
+	TInt r = LoadFile(); // Read everything in
+	if(r!=KErrNone)
+		return r;
+
+	__IF_DEBUG(Printf("iHeader->iCodeRelocOffset %d",iHeader->iCodeRelocOffset));
+	r = ((E32ImageHeaderV*)iHeader)->ValidateRelocations(iRestOfFileData,iRestOfFileSize,iHeader->iCodeRelocOffset,iHeader->iCodeSize,iCodeRelocSection);
+	if(r!=KErrNone)
+		return r;
+
+	__IF_DEBUG(Printf("iHeader->iDataRelocOffset %d",iHeader->iDataRelocOffset));
+	r = ((E32ImageHeaderV*)iHeader)->ValidateRelocations(iRestOfFileData,iRestOfFileSize,iHeader->iDataRelocOffset,iHeader->iDataSize,iDataRelocSection);
+	if(r!=KErrNone)
+		return r;
+		
+	iCodeDelta = iCodeRunAddress-iHeader->iCodeBase;
+	iDataDelta = iDataRunAddress-iHeader->iDataBase;
+	
+	if(r==KErrNone)
+	   	r = RelocateCode();
+	if(r==KErrNone)
+		r = LoadAndRelocateData();
+	if(r==KErrNone)
+    	r = ReadImportData();
+
+	return r;
+	}
+
+
+TInt E32Image::ShouldBeCodePaged(TBool& aPage)
+/**
+	Determine whether this binary should be paged.  Some of this
+	function is unimplemented because it requires the media pageable
+	attribute
+
+	@param	aPage			On success, this variable is set to
+							whether the binary should be paged.  Its
+							value is undefined if the return code is
+							not KErrNone.
+	@return					Symbian OS error code.
+
+	See S3.1.3.2 of PREQ1110 Design Sketch.
+ */
+	{
+	aPage = EFalse;
+
+	// kernel and global dlls can't be paged...
+	if(iAttr&(ECodeSegAttKernel|ECodeSegAttGlobal))
+		return KErrNone;
+
+	// 1. if paging policy is NOPAGING then executable is unpaged
+	TUint32 policy = E32Loader::PagingPolicy();
+
+	__IF_DEBUG(Printf("sbcp,policy=0x%x", policy));
+	if (policy == EKernelConfigCodePagingPolicyNoPaging)
+		return KErrNone;
+
+	// 2. if executable is on media without Pageable Media Attribute then unpaged
+	// 3. if executable is on removable media then unpaged
+	//	both superseded by the BlockMap API
+
+	// 3a. if executable has already been loaded into RAM for tamperproofing then
+	//     it can't be paged
+	if (iFileData != NULL)
+		return KErrNone;
+
+	// 4. if not compressed with bytepair or uncompressed then unpaged
+	__IF_DEBUG(Printf("sbcp,iHeader=0x%08x", iHeader));
+	TUint32 comp = iHeader->CompressionType();
+	__IF_DEBUG(Printf("sbcp,comp=0x%x", comp));
+	if (comp != KUidCompressionBytePair && comp != KFormatNotCompressed)
+		return KErrNone;
+
+	aPage = ETrue;
+
+	// 5. if policy is ALWAYSPAGE then page
+	if (policy == EKernelConfigCodePagingPolicyAlwaysPage)
+		return KErrNone;
+
+	// 6. 
+	TUint KPagedMask = (KImageCodePaged | KImageCodeUnpaged);
+	TUint pagedFlags = iHeader->iFlags & KPagedMask;
+	__IF_DEBUG(Printf("sbcp,iHeader->iFlags=0x%x,pagedFlags=0x%x", iHeader->iFlags, pagedFlags));
+
+	// if KImageCodePaged and KImageCodeUnpaged flags present then corrupt
+	if (pagedFlags == KPagedMask)
+		RETURN_FAILURE(KErrCorrupt);
+
+	// if KImageCodePaged set in executable then page
+	if (pagedFlags == KImageCodePaged)
+		return KErrNone;
+
+	// if KImageCodeUnpaged set in executable then do not page
+	if (pagedFlags == KImageCodeUnpaged)
+		{
+		aPage = EFalse;
+		return KErrNone;
+		}
+
+	// 7. otherwise (neither paged nor unpaged set) use paging policy
+
+	// policy must be EKernelConfigCodePagingPolicyDefaultUnpaged or EKernelConfigCodePagingPolicyDefaultPaged
+	aPage = (policy == EKernelConfigCodePagingPolicyDefaultPaged);
+	return KErrNone;
+	}
+
+TInt E32Image::BuildCodeBlockMap()
+/**
+	Use the block map API to build an array of TBlockMapInfo
+	objects which the kernel can use to page in code as required.
+
+	@return					Symbian OS error code.  KErrNotSupported means the
+							Block Map functionality does not support paging from
+							the binary's location.
+ */
+	{
+	__IF_DEBUG(Printf("BuildCodeBlockMap,iCodeStartInFile=%d,iCodeLengthInFile=%d", iCodeStartInFile, iCodeLengthInFile));
+
+	__ASSERT_DEBUG(iUseCodePaging, Panic(EBcbmNotCodePaged));
+
+	// do nothing if no code section
+	if (iCodeLengthInFile == 0)
+		return KErrNone;
+
+	// RFile::BlockMap populates an instance of this object.  Need to
+	// retain information such as granularity which applies to all entries.
+	SBlockMapInfo bmi;
+
+	TInt curEntriesSize = 0;
+	TUint8* entries8 = 0;		// points to heap cell containing TBlockMapEntryBase array
+
+	TInt64 bmPos = 0;
+	TInt64 bmEnd = iCodeStartInFile + iCodeLengthInFile;
+	TInt r;
+	do
+		{
+		__IF_DEBUG(Printf("lfbpu:BlockMap,in,bmPos=%ld,bmEnd=%ld", bmPos, bmEnd));
+		r = iFile.BlockMap(bmi, bmPos, bmEnd, EBlockMapUsagePaging);	// updates bmPos to end of mapped range
+		__IF_DEBUG(
+			Printf("lfbpu:BlockMap,out,r=%d,bmPos=%ld,bmEnd=%ld,maplen=%d(%d)",
+			r, bmPos, bmEnd, bmi.iMap.Length(), bmi.iMap.Length() / sizeof(TBlockMapEntryBase)));
+		__IF_DEBUG(
+			Printf("lfbpu:BlockMap,out,iBlockGranularity=%u,iBlockStartOffset=%u,iStartBlockAddress=%ld,iLocalDriveNumber=%d",
+			bmi.iBlockGranularity, bmi.iBlockStartOffset, bmi.iStartBlockAddress, bmi.iLocalDriveNumber));
+		if (r != KErrNone && r != KErrCompletion)
+			break;
+
+		// Copy info the first time round as this gets overwritten on subsequent passes
+		if (curEntriesSize == 0)
+			iCodeBlockMapCommon = bmi;	// slices the SBlockMapCommon subclass data
+		
+		// grow the buffer which contains the entries
+		TInt newEntriesSize = bmi.iMap.Length();
+		TInt newArraySize = curEntriesSize + newEntriesSize;
+		TUint8* newEntries8 = (TUint8*) User::ReAlloc(entries8, newArraySize);
+		if (newEntries8 == 0)
+			{
+			r = KErrNoMemory;
+			break;
+			}
+		entries8 = newEntries8;
+
+#ifdef _DEBUG
+		// dump the newly-returned block entries
+		for (TInt i = 0; i < newEntriesSize; i += sizeof(TBlockMapEntryBase))
+			{
+			const TBlockMapEntryBase& bme = *reinterpret_cast<const TBlockMapEntryBase*>(bmi.iMap.Ptr() + i);
+			__IF_DEBUG(Printf("lfbpu:bme,iNumberOfBlocks=%d,iStartBlock=%d", bme.iNumberOfBlocks, bme.iStartBlock));
+			}
+#endif
+
+		// append the new entries to the array.
+		Mem::Copy(entries8 + curEntriesSize, bmi.iMap.Ptr(), newEntriesSize);
+		curEntriesSize = newArraySize;
+		} while (r != KErrCompletion);
+
+	// r == KErrCompletion when mapped code section range
+	if (r != KErrCompletion)
+		{
+		User::Free(entries8);
+		return r;
+		}
+	
+#ifdef _DEBUG
+	// dump the block map table
+	__IF_DEBUG(Printf("lfbpu:endbme,r=%d,curEntriesSize=%d", r, curEntriesSize));
+	for (TInt i = 0; i < curEntriesSize; i += 8)
+		{
+		__IF_DEBUG(Printf(
+			"entries[0x%08x], %02x %02x %02x %02x %02x %02x %02x %02x",
+			entries8[i+0], entries8[i+1], entries8[i+2], entries8[i+3],
+			entries8[i+4], entries8[i+5], entries8[i+6], entries8[i+7]));
+		}
+#endif
+
+	iCodeBlockMapEntries = reinterpret_cast<TBlockMapEntryBase*>(entries8);
+	iCodeBlockMapEntriesSize = curEntriesSize;
+
+	return KErrNone;
+	}
+
+
+/**
+Get the compression data relevant to demand paging
+*/
+TInt E32Image::LoadCompressionData()
+	{
+	__IF_DEBUG(Printf("E32Image::LoadCompressionData %S 0x%08x",&iFileName,iHeader->CompressionType()));
+
+	TUint compression = iHeader->CompressionType();
+
+	TInt r = KErrNone;
+	if(compression==KFormatNotCompressed)
+		{
+		r = LoadCompressionDataNoCompress();
+		}
+	else if(compression==KUidCompressionBytePair)
+		{
+		TRAP(r,LoadCompressionDataBytePairUnpakL());
+		}
+	else
+		{
+		r = KErrNotSupported;
+		}
+
+	__IF_DEBUG(Printf("E32Image::LoadCompressionData exiting %S r=%d",&iFileName,r));
+	return r;	
+	}
+
+
+TInt E32Image::LoadCompressionDataNoCompress()
+	{
+	__IF_DEBUG(Printf("E32Image::LoadCompressionDataNoCompress %S",&iFileName));
+	if (iHeader->iCodeSize)
+		{
+		iCodeStartInFile = iHeader->iCodeOffset;
+		iCodeLengthInFile = iCodeSize;
+		}
+	return KErrNone;
+	}
+
+
+void E32Image::LoadCompressionDataBytePairUnpakL()
+	{
+	__IF_DEBUG(Printf("E32Image::LoadCompressionDataBytePairUnpakL %S",&iFileName));
+
+	if (iFileData)
+		User::Leave(KErrNotSupported); // if the file data has been loaded into RAM we can't page it!
+
+	TInt pos = iHeader->TotalSize();
+	User::LeaveIfError(iFile.Seek(ESeekStart,pos)); // Start at beginning of compressed data
+
+	CBytePairReader* reader = CBytePairFileReader::NewLC(iFile);
+
+	if (iHeader->iCodeSize)
+		{
+		__IF_DEBUG(Printf("Code & const size %x",iCodeSize));
+		__IF_DEBUG(Printf("Code & const offset %x",iHeader->iCodeOffset));
+		__IF_DEBUG(Printf("Code & const dest %x",iCodeLoadAddress));
+		
+		TInt pageCount;
+		reader->GetPageOffsetsL(pos, pageCount, iCodePageOffsets);
+
+#ifdef _DEBUG
+		for (TInt i = 0; i <= pageCount; ++i)
+			{
+			__IF_DEBUG(Printf("lfbpu:raw iCodePageOffsets[%d] = %d", i, iCodePageOffsets[i]));
+			}
+#endif
+
+		// record the code start position in the file and its compressed length
+		// so BuildCodeBlockMap can construct a block map for the kernel if this
+		// file is demand paged.
+		iCodeStartInFile = iCodePageOffsets[0];
+		iCodeLengthInFile = iCodePageOffsets[pageCount] - iCodePageOffsets[0];
+		}
+		
+	CleanupStack::PopAndDestroy(reader);
+	}
+
+
+/**
+Read all image data into memory, decompressing it using the method indicated in the image header..
+If code isn't being demand paged the code part is read into #iCodeLoadAddress.
+The rest of the file data after the code part is read into #iRestOfFileData.
+*/
+TInt E32Image::LoadFile()
+	{
+	__IF_DEBUG(Printf("E32Image::LoadFile %S 0x%08x",&iFileName,iHeader->CompressionType()));
+
+	TUint compression = iHeader->CompressionType();
+
+	TInt r=KErrNone;
+	if(compression==KFormatNotCompressed)
+		{
+		r = LoadFileNoCompress();
+		CHECK_FAILURE(r); // Fuzzer can't trigger this because it only happens on file i/o error
+		}
+	else if(compression==KUidCompressionDeflate)
+		{
+		TRAP(r,LoadFileInflateL());
+		CHECK_FAILURE(r);
+		}
+	else if(compression==KUidCompressionBytePair)
+		{
+		TRAP(r,LoadFileBytePairUnpakL());
+		CHECK_FAILURE(r);
+		}
+	else
+		{
+		r = KErrNotSupported;
+		CHECK_FAILURE(r); // Fuzzer can't trigger this because header validation ensures compression type is OK
+		}
+
+	// we're done with the file contents now, free up memory before resolving imports
+	if(iFileData)
+		{
+		gFileDataAllocator.Free(iFileData);
+		iFileData=NULL;
+		}
+
+	__IF_DEBUG(Printf("E32Image::LoadFile exiting %S r=%d",&iFileName,r));
+	return r;
+	}
+
+
+/**
+Read data from the image's file (or the preloaded data at #iFileData if present).
+*/
+TInt E32Image::Read(TUint aPos, TUint8* aDest, TUint aSize, TBool aSvPerms)
+	{
+	TPtr8 p(aDest,aSize,aSize);
+	if(iFileData)
+		{
+		// get data from pre-loaded image data...
+		if(aPos+aSize>iFileSize)
+			RETURN_FAILURE(KErrCorrupt); // Fuzzer can't trigger this because earlier validation prevents sizes being wrong
+		if (aSvPerms)
+			WordCopy(aDest,iFileData+aPos,aSize);
+		else
+			p.Copy(iFileData+aPos,aSize);
+		}
+	else
+		{
+		// get data from file...
+		TInt r = iFile.Read(aPos,p,aSize);
+		if(r!=KErrNone)
+			return r;
+		}
+
+	// check we got the amount of data requested...
+	if(TUint(p.Length())!=aSize)
+		{
+		__IF_DEBUG(Printf("E32Image::Read() Expected:%d, read:%d", aSize, p.Length() ));
+		RETURN_FAILURE(KErrCorrupt); // Fuzzer can't trigger this because requires file length to change during load
+		}
+
+	return KErrNone;
+	}
+
+
+/**
+Read all image data into memory.
+If code isn't being demand paged the code part is read into #iCodeLoadAddress.
+The rest of the file data after the code part is read into #iRestOfFileData.
+*/
+TInt E32Image::LoadFileNoCompress()
+	{
+	__IF_DEBUG(Printf("E32Image::LoadFileNoCompress exiting %S",&iFileName));
+	TInt r = KErrNone;
+	
+	if(iHeader->iCodeSize && !iUseCodePaging)
+		{
+		__IF_DEBUG(Printf("Code & const size %x",iCodeSize));
+		__IF_DEBUG(Printf("Code & const offset %x",iHeader->iCodeOffset));
+		__IF_DEBUG(Printf("Code & const dest %x",iCodeLoadAddress));
+		r = Read(iHeader->iCodeOffset, (TText8*)iCodeLoadAddress, iCodeSize, ETrue);
+		if(r!=KErrNone)
+			return r;
+		}
+
+	if(iRestOfFileSize)
+		r = Read(iConversionOffset, iRestOfFileData, iRestOfFileSize);
+	
+	return r;
+	}
+
+
+void FileCleanup(TAny* aPtr)
+	{
+	TFileInput* f=(TFileInput*)aPtr;
+	f->Cancel();
+	delete f;
+	}
+
+/**
+Read all image data into memory, decompressing it using the Inflate method.
+If code isn't being demand paged the code part is read into #iCodeLoadAddress.
+The rest of the file data after the code part is read into #iRestOfFileData.
+*/
+void E32Image::LoadFileInflateL()
+	{
+	__IF_DEBUG(Printf("E32Image::LoadFileInflateL %S",&iFileName));
+	__ASSERT_DEBUG(!iUseCodePaging, Panic(ELfiCodePagingNotSupported));
+	
+	TInt pos = iHeader->TotalSize();
+	TBitInput* file;
+	if(iFileData)
+		{
+		if(pos < 0)
+			User::Leave(KErrArgument);
+		file = new (ELeave) TBitInput(iFileData, iFileSize*8, pos*8);
+		CleanupStack::PushL(file);
+		}
+	else
+		{
+		User::LeaveIfError(iFile.Seek(ESeekStart,pos)); // Start at beginning of compressed data
+		file = new (ELeave) TFileInput(iFile);
+		CleanupStack::PushL(TCleanupItem(&FileCleanup,file));
+		}
+
+	CInflater* inflater=CInflater::NewLC(*file);
+	
+	if(iHeader->iCodeSize)
+		{
+		__IF_DEBUG(Printf("Code & const size %x",iCodeSize));
+		__IF_DEBUG(Printf("Code & const offset %x",iHeader->iCodeOffset));
+		__IF_DEBUG(Printf("Code & const dest %x",iCodeLoadAddress));
+
+		TInt count = inflater->ReadL((TUint8*)iCodeLoadAddress,iCodeSize,&WordCopy);
+		if(count!=iCodeSize)
+			User::Leave(KErrCorrupt);
+		}
+
+	if(iRestOfFileSize)
+		{
+		TUint32 count = inflater->ReadL(iRestOfFileData,iRestOfFileSize,&Mem::Copy);
+		if(count!=iRestOfFileSize)
+			User::Leave(KErrCorrupt);
+		}
+
+	CleanupStack::PopAndDestroy(2,file);
+	}
+	
+
+/**
+Read all image data into memory, decompressing it using the BytePair method.
+If code isn't being demand paged the code part is read into #iCodeLoadAddress.
+The rest of the file data after the code part is read into #iRestOfFileData.
+*/
+void E32Image::LoadFileBytePairUnpakL()
+	{
+	__IF_DEBUG(Printf("E32Image::LoadFileBytePairUnpak %S",&iFileName));
+
+	// code starts after header
+	TInt pos = iHeader->TotalSize();
+
+	CBytePairReader* reader;
+	if(iFileData)
+		reader = CBytePairReader::NewLC(iFileData+pos, iFileSize-pos);
+	else
+		{
+		iFile.Seek(ESeekStart, pos);
+		reader = CBytePairFileReader::NewLC(iFile);
+		}
+
+	TBool codeLoaded = false;
+	if(iHeader->iCodeSize && !iUseCodePaging)
+		{
+		__IF_DEBUG(Printf("Code & const size %x",iCodeSize));
+		__IF_DEBUG(Printf("Code & const offset %x",iHeader->iCodeOffset));
+		__IF_DEBUG(Printf("Code & const dest %x",iCodeLoadAddress));
+
+		TUint32 bytes = reader->DecompressPagesL((TUint8*)iCodeLoadAddress,iCodeSize,&WordCopy);
+
+		__IF_DEBUG(Printf("bytes:%x",bytes));
+		if((TInt)bytes!=iCodeSize)
+			User::Leave(KErrCorrupt);
+
+		codeLoaded = true;
+		}
+	
+	if(iRestOfFileSize)
+		{
+		if(!codeLoaded)
+			{
+			// skip past code part of file...
+			TInt pageCount = (iCodeSize + KPageOffsetMask) >> KPageSizeShift;
+		
+			TInt pos = 	KIndexTableHeaderSize
+					+	pageCount * sizeof(TUint16)
+					+   iCodeLengthInFile;
+	 
+			__IF_DEBUG(Printf("lfpbu:pos=%x", pos));
+			reader->SeekForwardL(pos);
+			}
+		
+		__IF_DEBUG(Printf("  iRestOfFileSize==%x, iRestOfFileData==%x", iRestOfFileSize, iRestOfFileData));
+		
+		TUint32 bytes = reader->DecompressPagesL(iRestOfFileData,iRestOfFileSize,NULL);
+		__IF_DEBUG(Printf("bytes:%x",bytes));
+		if(bytes!=iRestOfFileSize)
+			User::Leave(KErrCorrupt);
+		}
+		
+	CleanupStack::PopAndDestroy(reader);
+	}
+
+
+/**
+Relocate code.
+*/
+TInt E32Image::RelocateCode()
+	{
+	if(iHeader->iExportDirOffset)
+		iExportDirLoad += iCodeLoadAddress;	// only for RAM modules which are not already loaded
+
+	__IF_DEBUG(Printf("**EntryPointVeneer %08x FileEntryPoint %08x",iEntryPtVeneer,iFileEntryPoint));
+	__IF_DEBUG(Printf("**ExportDir load@%08x run@%08x",iExportDirLoad,iExportDir));
+	TInt r = KErrNone;	
+	if(iHeader->iCodeRelocOffset)
+		{
+		__IF_DEBUG(Printf("Relocate code & const"));
+
+		if(!iUseCodePaging)
+			r = RelocateSection(iCodeRelocSection, iCodeLoadAddress);
+		else
+			{
+			r = AllocateRelocationData(iCodeRelocSection, iHeader->iCodeSize, iCodeLoadAddress, iCodeRelocTable);
+			iExportDirEntryDelta = iCodeDelta; // so exports get relocated
+			}
+		}
+
+	if(r==KErrNone)
+		r = RelocateExports();
+
+	if(r==KErrNone)
+		{
+		// put a unique ID into the third word after the entry point
+
+		// address for ID...
+		TLinAddr csid_addr = iFileEntryPoint+KCodeSegIdOffset-iCodeRunAddress+iCodeLoadAddress;
+		__IF_DEBUG(Printf("csid_addr %08x", csid_addr));
+
+		// get existing ID...
+		TUint x;
+		WordCopy(&x, (const TAny*)csid_addr, sizeof(x));
+		if(x==0)
+			{
+			// generate next ID...
+			if(++NextCodeSegId == 0xffffffffu)
+				Fault(ELdrCsIdWrap);
+			__IF_DEBUG(Printf("NextCSID %08x", NextCodeSegId));
+			// store ID...
+			if(!iUseCodePaging)
+				WordCopy((TAny*)csid_addr, &NextCodeSegId, sizeof(NextCodeSegId));
+			else
+				{
+				// demand paged code needs modifying when paged in, so add ID as a new 'fixup'...
+				TUint64* fixup = ExpandFixups(1);
+				if(!fixup)
+					r = KErrNoMemory;
+				else
+					*fixup = MAKE_TUINT64(csid_addr,NextCodeSegId);
+				}
+			}
+		}
+
+	return r;
+	}
+
+
+/**
+Copy the data section from buffer #iRestOfFileData to the memory allocated at #iDataLoadAddress.
+Then relocate this data ready for use at the executables run addresses.
+*/
+TInt E32Image::LoadAndRelocateData()
+	{
+	__IF_DEBUG(Printf("E32Image::LoadAndRelocateData %S",&iFileName));
+	if(!iHeader->iDataOffset)
+		return KErrNone; // do data section
+
+	// copy data...
+	__IF_DEBUG(Printf("Read Data: size %x->%08x",iDataSize,iDataLoadAddress));
+	TUint32 bufferOffset=iHeader->iDataOffset-iConversionOffset;
+	TUint8* source=iRestOfFileData+bufferOffset;
+	MemCopy((TText8*)iDataLoadAddress,source,iDataSize);
+
+	// relocate data...
+	__IF_DEBUG(Printf("Relocate data section"));
+	__IF_DEBUG(Printf("iDataRelocOffset %08x",iHeader->iDataRelocOffset));
+	TInt r = KErrNone;	
+	if(iHeader->iDataRelocOffset)
+		r = RelocateSection(iDataRelocSection, iDataLoadAddress);
+
+	return r;
+	}
+
+
+/**
+Copies data from aDestination to aSource by running in supervisor mode.
+aDest, aSource & aNumberOfBytes must be word aligned.
+*/
+TUint8* E32Image::WordCopy(TAny* aDestination, const TAny* aSource, TInt aNumberOfBytes)
+	{
+	aNumberOfBytes &= ~3; // Avoid panics for corrupt data which is not word size
+	SCopyDataInfo info = {aDestination,aSource, aNumberOfBytes};
+	return (TUint8*) ExecuteInSupervisorMode(&svWordCopy, &info);
+	}
+
+
+/**
+Copies data from aDestination to aSource by running in supervisor mode.
+*/
+TUint8* E32Image::MemCopy(TAny* aDestination, const TAny* aSource, TInt aNumberOfBytes)
+	{
+	SCopyDataInfo info={aDestination,aSource, aNumberOfBytes};
+	return (TUint8*) ExecuteInSupervisorMode(&svMemCopy, &info);
+	}
+
+
+/**
+Relocate a section, applying relocations for run addresses to values currently at their load addresses.
+*/
+TInt E32Image::RelocateSection(E32RelocSection* aSection, TUint32 aLoadAddress)
+	{
+	if(!aSection)
+		return KErrNone;
+
+	__IF_DEBUG(Printf("Relocate: NRelocs:%08x LoadAddr:%08x", aSection->iNumberOfRelocs, aLoadAddress));
+
+	SRelocateSectionInfo info={this, (TUint8*)(aSection+1), aSection->iNumberOfRelocs, aLoadAddress};
+
+	// call function in supervisor mode to relocate the section
+	TInt r = ExecuteInSupervisorMode(&svRelocateSection, &info);
+
+	__IF_DEBUG(Printf("Relocate returning %d",r));
+	return r;
+	}
+
+
+/**
+Relocate the export directory for the code's run address
+*/
+TInt E32Image::RelocateExports()
+	{
+	// This only has to be done for PE-derived images, ELF marks all
+	// export table entries as 'relocations' so this job has already been done.
+	TUint impfmt = iHeader->ImportFormat();
+	if (impfmt == KImageImpFmt_ELF)
+		return KErrNone;
+
+	__IF_DEBUG(Printf("E32Image::RelocateExports %S",&iFileName));
+
+	if(iHeader->iExportDirOffset)
+		{
+		// call function in supervisor mode to fix up export directory
+		ExecuteInSupervisorMode(&svRelocateExports, this);
+		}
+	return KErrNone;
+	}
+
+
+/**
+Validate import section data structures in iRestOfFileData.
+Set iImportData to point to point to start of this.
+Allocate memory (iCurrentImportList) which is big enough to store imports for a single dependency.
+*/
+TInt E32Image::ReadImportData()
+	{
+	__IF_DEBUG(Printf("E32Image::ReadImportData %S",&iFileName));
+
+	if(!iHeader->iImportOffset)
+		return KErrNone;
+
+	TUint biggestImportCount; 
+	TInt r = ((E32ImageHeaderV*)iHeader)->ValidateImports(iRestOfFileData,iRestOfFileSize,biggestImportCount);
+	if(r!=KErrNone)
+		return r;
+
+	iImportData = (TUint32*)(iRestOfFileData+iHeader->iImportOffset-iConversionOffset);
+	iCurrentImportList = (TUint32*)User::Alloc(biggestImportCount * sizeof(TUint32));
+	__IF_DEBUG(Printf("E32Image::ReadImportData - alloc %d current import slots at %08x", biggestImportCount, iCurrentImportList));
+	if(!iCurrentImportList)
+		return KErrNoMemory;
+
+	return KErrNone;
+	}
+
+
+void E32Image::SortCurrentImportList()
+	{
+	if (!iCurrentImportListSorted)
+		{
+		RArray<TUint> array((TUint*)iCurrentImportList, iCurrentImportCount);
+		array.Sort();
+		iCurrentImportListSorted = (TUint8)ETrue;
+		}
+	}
+
+
+TInt CheckRomExports(const TRomImageHeader* aR, const E32Image* aI)
+	{
+	__IF_DEBUG(Printf("CheckRomExports"));
+	if (aR->iExportDirCount == 0)
+		return aI->iCurrentImportCount ? KErrNotSupported : KErrNone;
+	const TUint32* xd = (const TUint32*)aR->iExportDir;
+	const TUint32* p = aI->iCurrentImportList;
+	const TUint32* pE = p + aI->iCurrentImportCount;
+	for (; p<pE; ++p)
+		if (xd[*p] == 0)
+			return KErrNotSupported;
+	return KErrNone;
+	}
+
+
+TInt CheckRamExports(TUint aEDT, const TUint8* aED, TUint aEDC, E32Image* aI)
+	{
+	__IF_DEBUG(Printf("CheckRamExports"));
+	if (aEDC == 0)
+		return aI->iCurrentImportCount ? KErrNotSupported : KErrNone;
+	if (aEDT == KImageHdr_ExpD_NoHoles)
+		return KErrNone;	// nothing missing
+
+	const TUint32* p = aI->iCurrentImportList;
+	const TUint32* pE = p + aI->iCurrentImportCount;
+
+	if (aEDT == KImageHdr_ExpD_FullBitmap)
+		{
+		for (; p<pE; ++p)
+			{
+			TUint32 x = *p - 1;
+			if ( !(aED[x>>3] & (1u<<(x&7))) )
+				return KErrNotSupported;
+			}
+		return KErrNone;
+		}
+
+	if (aEDT != KImageHdr_ExpD_SparseBitmap8)
+		return KErrNotSupported;		// don't know what this is
+	aI->SortCurrentImportList();		// sort imports to increasing order
+	TUint32 memsz = (aEDC + 7) >> 3;	// size of complete bitmap
+	TUint32 mbs = (memsz + 7) >> 3;		// size of meta-bitmap
+	const TUint8* mptr = aED;
+	const TUint8* gptr = mptr + mbs;
+	const TUint8* mptrE = mptr + mbs;
+	TUint xlim = 64;
+	for (; mptr<mptrE && p<pE; ++mptr, xlim+=64)
+		{
+		TUint m = *mptr;
+		if (m==0)
+			{
+			// nothing missing in this block of 64 exports; step to next block
+			for (; p<pE && *p<=xlim; ++p) {}
+			continue;
+			}
+		// expand this block of 64
+		TUint32 g32[2] = {0xffffffffu, 0xffffffffu};
+		TUint8* g = (TUint8*)g32;
+		for (; m; m>>=1, ++g)
+			if (m&1)
+				*g = *gptr++;
+		g = (TUint8*)g32;
+		for (; p<pE && *p<=xlim; ++p)
+			{
+			TUint ix = *p - (xlim - 64) - 1;
+			if ( !(g[ix>>3] & (1u<<(ix&7))) )
+				return KErrNotSupported;
+			}
+		}
+	return KErrNone;
+	}
+
+
+TInt CheckRequiredImports(E32Image* aImporter, E32Image* aExporter, TInt aAction)
+	{
+	__IF_DEBUG(Printf("E32Image::CheckRequiredImports (existing) %d", aAction));
+	TInt last = aImporter->LastCurrentImport();
+	if (last > aExporter->iExportDirCount)
+		return KErrNotSupported;
+	if (aAction == EAction_CheckLastImport)
+		return KErrNone;
+	if (aExporter->iRomImageHeader)
+		return CheckRomExports(aExporter->iRomImageHeader, aImporter);
+	if (aExporter->iHeader)
+		{
+		E32ImageHeaderV* v = (E32ImageHeaderV*)aExporter->iHeader;
+		return CheckRamExports(v->iExportDescType, v->iExportDesc, v->iExportDirCount, aImporter);
+		}
+	TInt r = aExporter->ReadExportDirLoad();
+	if (r != KErrNone)
+		return r;				// could fail with OOM
+	TBool hasNmdExp = (aExporter->iAttr & ECodeSegAttNmdExpData);
+	const TUint32* p = aImporter->iCurrentImportList;
+	const TUint32* pE = p + aImporter->iCurrentImportCount;
+	const TUint32* pX = (const TUint32*)aExporter->iExportDirLoad - 1;
+	TUint32 xep = aExporter->iFileEntryPoint;
+	for (; p<pE; ++p)
+		{
+		TUint32 x = *p;
+		TUint32 xx = pX[x];
+		if ((xx==0 && (x!=0 || (x==0&&hasNmdExp))) || xx==xep)
+			return KErrNotSupported;
+		}
+	return KErrNone;
+	}
+
+
+TInt CheckRequiredImports(E32Image* aImporter, const RImageInfo& aExporter, TInt aAction)
+	{
+	__IF_DEBUG(Printf("E32Image::CheckRequiredImports (new) %d", aAction));
+	TInt last = aImporter->LastCurrentImport();
+	if (last > aExporter.iExportDirCount)
+		return KErrNotSupported;
+	if (aAction == EAction_CheckLastImport)
+		return KErrNone;
+	if (aExporter.iRomImageHeader)
+		return CheckRomExports(aExporter.iRomImageHeader, aImporter);
+	return CheckRamExports(aExporter.iExportDescType, aExporter.iExportDesc, aExporter.iExportDirCount, aImporter);
+	}
+
+
+TInt E32Image::GetCurrentImportList(const E32ImportBlock* a)
+	{
+	__IF_DEBUG(Printf("E32Image::GetCurrentImportList(E32ImportBlock* a:%08X)", a));
+	TInt r;
+	TInt n = a->iNumberOfImports;
+	iCurrentImportCount = n;
+	iCurrentImportListSorted = (TUint8)EFalse;
+	__IF_DEBUG(Printf("iCurrentImportCount:%d, iCurrentImportListSorted:%d)", iCurrentImportCount, iCurrentImportListSorted));
+	__IF_DEBUG(Printf("iHeader->ImportFormat() == KImageImpFmt_ELF:%d", (iHeader->ImportFormat() == KImageImpFmt_ELF) ));
+	
+	if (iHeader->ImportFormat() == KImageImpFmt_ELF)
+		{
+		SGetImportDataInfo info;
+		info.iCount = n;
+		info.iDest = iCurrentImportList;
+		info.iCodeLoadAddress = iCodeLoadAddress;
+		info.iImportOffsetList = (TUint32*)a->Imports();
+		r = ExecuteInSupervisorMode(&svElfDerivedGetImportInfo, &info);
+		}
+	else
+		{
+		TUint32* iat = (TUint32*)(iCodeLoadAddress + iTextSize);
+		WordCopy(iCurrentImportList, iat + iNextImportPos, n * sizeof(TUint32));
+		r = KErrNone;
+		}
+	iNextImportPos += n;
+	__IF_DEBUG(Printf("End of E32Image::GetCurrentImportList:%d)", r));
+	return r;
+	}
+
+
+TInt E32Image::LastCurrentImport()
+	{
+	TUint32 last = 0;
+	if (iCurrentImportListSorted)
+		last = iCurrentImportList[iCurrentImportCount - 1];
+	else
+		{
+		const TUint32* p = iCurrentImportList;
+		const TUint32* pE = p + iCurrentImportCount;
+		for (; p<pE; ++p)
+			if (*p > last) last = *p;
+		}
+	__IF_DEBUG(Printf("E32Image::LastCurrentImport = %d", last));
+	return last;
+	}
+
+
+TInt E32Image::ProcessImports()
+//
+//	This function is only ever called on the exe/dll which is loaded from 
+//	the RProcess/RLibrary load.
+//	It reads this DLL/EXE's imports section and builds up a table of dlls referenced.
+//	It never goes recursive.
+//
+	{
+	__IF_DEBUG(Printf("E32Image::ProcessImports %S",&iFileName));
+	__IF_DEBUG(Printf("DepCount=%d",iDepCount));
+	
+	if (iDepCount==0 || AlwaysLoaded())
+		return KErrNone;	// no imports
+
+	TFileNameInfo fi;
+	fi.Set(iFileName, 0);
+	gLoadeePath.Zero();
+	fi.GetName(gLoadeePath, TFileNameInfo::EIncludeDrivePath);
+	if (PlatSec::ConfigSetting(PlatSec::EPlatSecEnforceSysBin)
+			&& gLoadeePath.Length()==11
+			&& KSysBin().CompareF(TPtrC8(gLoadeePath.Ptr()+1,10))==0)
+		{
+		// Main loadee is in the default path, so unset this in order to
+		// search normally for dependents
+		gLoadeePath.Zero();
+		}
+#ifdef __X86__
+	if (gLoadeePath.Length()>=2 && gLoadeePath[1]==':')
+		{
+		TInt d = gLoadeePath[0];
+		if (d=='a' || d=='A')
+			UseFloppy = EDriveA;
+		else if (d=='b' || d=='B')
+			UseFloppy = EDriveB;
+		}
+#endif
+	RImageArray array;
+	TInt r = array.Add(this);
+	if (r==KErrNone)
+		r = LoadDlls(array);
+	if (r==KErrNone)
+		r = FixupDlls(array);
+	if (r==KErrNone)
+		r = FinaliseDlls(array);
+	CleanupDlls(array);
+	array.Close();
+
+	__IF_DEBUG(Printf("E32Image::ProcessImports returns %d",r));
+	return r;
+	}
+
+void E32Image::CleanupDlls(RImageArray& aArray)
+//
+// Free the space used in fixing up the dlls.
+// Don't free the entry corresponding to the main loadee.
+//
+	{
+
+	__IF_DEBUG(Printf("CleanupDlls"));
+	TInt n = aArray.Count();
+	TInt i;
+	for (i=0; i<n; ++i)
+		{
+		E32Image* e = aArray[i];
+		if (e != this)
+			delete e;
+		}
+	}
+
+TInt E32Image::FinaliseDlls(RImageArray& aArray)
+	{
+	__IF_DEBUG(Printf("E32Image::FinaliseDlls"));
+	TInt i;
+	TInt c = aArray.Count();
+	TInt r = KErrNone;
+	for(i=0; i<c && r==KErrNone; i++)
+		{
+		E32Image* e = aArray[i];
+		if(e!=this && !e->iAlreadyLoaded)
+			{
+			// transfers ownership of clamp handle to codeseg; nulls handle if successful
+			if(!e->AlwaysLoaded())
+				r = E32Loader::CodeSegLoaded(*e);
+			if(r==KErrNone && e->iUseCodePaging)
+				{
+				e->iFileClamp.iCookie[0]=0;// null handle to indicate 
+				e->iFileClamp.iCookie[1]=0;// transfer of ownership of clamp handle to codeseg
+				}
+			}
+		}
+	__IF_DEBUG(Printf("E32Image::FinaliseDlls returns %d",r));
+	return r;
+	}
+
+
+TInt E32Image::LoadDlls(RImageArray& aArray)
+//
+// Build a matrix of all DLLs referenced by the one we're loading, and
+// ensure they're all loaded.
+//
+	{
+	__IF_DEBUG(Printf("E32Image::LoadDlls"));
+	TInt r=KErrNone;
+	E32ImportSection* importSection=(E32ImportSection *)iImportData;
+	E32ImportBlock* block;
+	if(importSection)
+		block=(E32ImportBlock*)(importSection+1);
+	else
+		block=NULL;
+	const TRomImageHeader* const * pR=NULL;
+	if (iRomImageHeader)
+		pR=iRomImageHeader->iDllRefTable->iEntry;
+	iNextImportPos = 0;
+
+	// For each module referenced by this module
+	for (TInt i=0; i<iDepCount; ++i)
+		{
+		RImageFinder finder;
+		E32ImportBlock* thisBlock = block;
+		E32Image* e = NULL;	// will represent referenced module
+		const TRomImageHeader* rih = NULL;
+		RLdrReq req;		// new loader request to load referenced module
+		TBuf8<KMaxKernelName> rootname;
+		req.iFileName = (HBufC8*)&rootname;
+
+		if (pR)
+			{
+			// Processing imports for ROM XIP module
+			rih = *pR++;
+			__IF_DEBUG(Printf("Importing from ROM XIP %08x", rih));
+			e = aArray.Find(rih);
+			}
+		else
+			{
+			// Processing imports for RAM module
+			__IF_DEBUG(Printf("Import block address %08x",block));
+			TPtrC8 dllname = (const TText8*)((TUint32)iImportData + block->iOffsetOfDllName);
+			if (dllname.Length() > KMaxKernelName)
+				{
+				__IF_DEBUG(Printf("Import DLL name too big: %S",&dllname));
+				RETURN_FAILURE(KErrNotSupported);
+				}
+			TFileNameInfo fni;
+			r = fni.Set(dllname, TFileNameInfo::EAllowUid);
+			if (r!=KErrNone)
+				RETURN_FAILURE(KErrCorrupt);
+			fni.GetName(rootname, TFileNameInfo::EIncludeBaseExt);
+			TUint32* uid=(TUint32*)&req.iRequestedUids;
+			uid[2] = fni.Uid();
+			req.iRequestedVersion = fni.Version();
+			if (gLoadeePath.Length() > 0)
+				req.iPath = (HBufC8*)&gLoadeePath;
+			req.iPlatSecCaps = iS.iCaps;
+			req.iFileNameInfo.Set(rootname, 0);
+			req.iImporter = this;
+			r = GetCurrentImportList(block);	// get list of required exports from this exporter
+			if (r!=KErrNone)
+				{
+				return r;
+				}
+			TUint impfmt = iHeader->ImportFormat();
+			block = (E32ImportBlock*)block->NextBlock(impfmt);
+
+			r = finder.Set(req);
+			if (r == KErrNone)
+				r = finder.SearchExisting(aArray);	// see what we've already got
+			if (r == KErrNone)
+				{
+				TBool search = ETrue;
+				if (finder.iExisting)
+					{
+					// Found an existing DLL - check for an exact version match
+					if (DetailedCompareVersions(finder.iCurrentVersion, finder.iReq->iRequestedVersion) <= EVersion_Exact)
+						search = EFalse;		// if exact match, don't need to continue search
+					}
+				if (search)
+					r = finder.Search();		// see what else is available
+				}
+			if (r!=KErrNone)
+				{
+				finder.Close();
+				return r;
+				}
+			if (finder.iExisting)
+				e = finder.iExisting;			// already have the required module
+			}
+
+		// If it's already in the array, go on to the next module
+		if (e)
+		    {
+			__IF_DEBUG(Printf("Already there"));
+			}
+		else
+			{
+			//	Not already in the array
+			__IF_DEBUG(Printf("Not in array, add it"));
+			e = new E32Image;
+			if (!e)
+				{
+				finder.Close();
+				return KErrNoMemory;
+				}
+			e->iMain = iMain;
+			e->iClientProcessHandle = iMain->iClientProcessHandle;
+			if (iMain->iAttr & ECodeSegAttKernel)
+				e->iAttr |= ECodeSegAttKernel;
+			if (rih)
+				{
+				// loading a specified ROM XIP DLL
+				r = e->DoLoadCodeSeg(*rih);
+				}
+			else
+				{
+				// loading a DLL by name
+				r = e->DoLoadCodeSeg(req, finder); // also closes 'finder'
+				__IF_DEBUG(Printf("%S DoLoadCodeSeg returned %d",req.iFileName,r));
+				}
+
+			//	Add the new entry to the array
+			if (r==KErrNone)
+				{
+				__IF_DEBUG(Printf("Add to the array"));
+				r = aArray.Add(e);
+				}
+			if (r!=KErrNone)
+				{
+				delete e;
+				return r;
+				}
+						
+			//	Now go nice and recursive, and call LoadDlls on this latest dll, if it 
+			//	imports anything
+			//	This recursive horror *will* terminate because it is only called
+			//	on "new" dlls
+			if (e->iDepCount && !e->iAlreadyLoaded && e->iIsDll)
+				{
+				__IF_DEBUG(Printf("****Go recursive****"));
+				r = e->LoadDlls(aArray);
+				if (r!=KErrNone)
+					{
+					return r;
+					}
+				}
+
+			}
+
+		// If we added an SMP unsafe dependent, this image is SMP unsafe.
+		// This is done after recursing into LoadDlls, so a single unsafe
+		// dependent anywhere down the tree will poison everything above it.
+		// This isn't sufficient to deal with cycles, though, so the kernel
+		// also has to update the flag in DCodeSeg::FinaliseRecursiveFlags.
+		// It has to be done here first because the kernel doesn't know
+		// about XIP DLLs that don't have a codeseg created.
+		if (!(e->iAttr & ECodeSegAttSMPSafe))
+			{
+			__IF_DEBUG(Printf("%S is not SMP safe because it loads %S", &iFileName, &e->iFileName));
+			iAttr &= ~ECodeSegAttSMPSafe;
+			}
+
+		// If exporter is an EXE it must be the same as the client process or newly created process
+		__IF_DEBUG(Printf("Check EXE->EXE"));
+		if (gExeCodeSeg && !e->iIsDll && e->iHandle!=gExeCodeSeg)
+			return KErrNotSupported;
+
+		// A globally-visible module may only link to other globally visible modules
+		__IF_DEBUG(Printf("Check Global Attribute"));
+		if ( (iAttr&ECodeSegAttGlobal) && !(e->iAttr&ECodeSegAttGlobal) )
+			return KErrNotSupported;
+
+		// A ram-loaded globally-visible module may only link to ROM XIP modules with no static data
+		__IF_DEBUG(Printf("Check RAM Global"));
+		if ( (iAttr&ECodeSegAttGlobal) && !iRomImageHeader && e->iHandle)
+			return KErrNotSupported;
+
+		if (thisBlock)
+			thisBlock->iOffsetOfDllName=(TUint32)e;   // For easy access when fixing up imports
+		if (e->iHandle)
+			{
+			//	Record the dependence of this on e
+			r=E32Loader::CodeSegAddDependency(iHandle, e->iHandle);
+			if (r!=KErrNone)
+				{
+				return r;
+				}
+			}
+		}
+	__IF_DEBUG(Printf("E32Image::LoadDlls OK"));
+	return KErrNone;
+	}
+
+
+TInt E32Image::ReadExportDirLoad()
+	{
+	//	Get the exporter's export directory
+	__IF_DEBUG(Printf("ReadExportDirLoad exp_dir=%08x", iExportDirLoad));
+	if (!iExportDirLoad)
+		{
+		// already loaded nonglobal DLL - must read the export directory
+		if (iExportDirCount==0 && !(iAttr&ECodeSegAttNmdExpData))
+			return KErrGeneral; // DLL has no exports, something must be wrong
+		iCopyOfExportDir = (TUint32*)User::Alloc((iExportDirCount+1) * sizeof(TUint32));
+		if (!iCopyOfExportDir)
+			return KErrNoMemory;
+		__IF_DEBUG(Printf("Reading %d exports", iExportDirCount));
+		E32Loader::ReadExportDir(iHandle, iCopyOfExportDir);
+		iExportDirLoad = (TUint32)(iCopyOfExportDir+1);
+		}
+	return KErrNone;
+	}
+
+
+TInt E32Image::FixupDlls(RImageArray& aArray)
+//
+// Go through the array, fixing up the files
+//
+	{
+	__IF_DEBUG(Printf("E32Image::FixupDlls"));
+
+	// For each E32Image file in the array
+	TInt i;
+	TInt c = aArray.Count();
+
+	for (i=0; i<c; ++i)
+		{
+		TInt r;
+
+		E32Image* imp = aArray[i];
+		__IF_DEBUG(Printf("Dll number %d %S",i,&imp->iFileName));
+
+		const E32ImportSection* importSection = (const E32ImportSection*)imp->iImportData;
+		if (!importSection)
+			{
+			__IF_DEBUG(Printf("Has no imports to fixup"));
+			continue;	//	No imports, skip this dll (true of ALL ROM dlls)
+			}
+
+		const E32ImportBlock* block = (const E32ImportBlock*)(importSection + 1);
+
+		SFixupImportAddressesInfo info;
+		info.iIat = (TUint32*)(imp->iCodeLoadAddress + imp->iTextSize);
+		info.iCodeLoadAddress = imp->iCodeLoadAddress;
+
+		// fix up imports from each dependent DLL, building a table of all the imports for the binary
+		TInt depCount = imp->iDepCount;
+		while (depCount--)
+			{
+			// declare variables at start of loop body to prevent 'crosses initialization' errors
+			TUint impfmt;
+
+			// E32Image::LoadDlls() will have set iOffsetOfDllName of the 
+			// import block to point to the E32Image object of the exporter
+			// it's importing
+			E32Image* exp = (E32Image*)(block->iOffsetOfDllName);   // LoadDlls() set this to exporter
+
+			//	Get the exporter's export directory
+			r = exp->ReadExportDirLoad();
+			if (r != KErrNone)
+				return r;
+			info.iExportDir = (TUint32*)exp->iExportDirLoad;
+			info.iExportDirEntryDelta = exp->iExportDirEntryDelta;
+			info.iNumImports = block->iNumberOfImports;
+			info.iExporter = exp;
+
+			// if demand paging, expand the import fixup buffer for this next exporting DLL
+			if (! imp->iUseCodePaging)
+				info.iFixup64 = 0;
+			else
+				{
+				info.iFixup64 = imp->ExpandFixups(block->iNumberOfImports);
+				if (!info.iFixup64)
+					return KErrNoMemory;
+				}
+
+			// call function in supervisor mode to fix up the import addresses.
+			impfmt = imp->iHeader->ImportFormat();
+			if (impfmt == KImageImpFmt_ELF)
+				{
+				info.iImportOffsetList = (TUint32*)(block+1);
+				r = ExecuteInSupervisorMode(&svElfDerivedFixupImportAddresses, &info);
+				}
+			else
+				r = ExecuteInSupervisorMode(&svFixupImportAddresses, &info);
+
+			if (r != KErrNone)
+				{
+				__IF_DEBUG(Printf("svFixupImportAddresses returns %d", r));
+				return r;
+				}
+
+			// Next import block...
+			block = block->NextBlock(impfmt);
+			}	// while (depCount--)
+
+		if (imp->iUseCodePaging && imp->iFixupCount > 0)
+			{
+			// convert the <addr,val> pairs to an import fixup tab which can be used when
+			// the code is paged.
+			r = imp->BuildImportFixupTable();
+			if (r != KErrNone)
+				return r;
+			}
+		}
+
+	__IF_DEBUG(Printf("E32Image::FixupDlls OK"));
+	return KErrNone;
+	}
+
+
+/**
+This function is defined because RArray does not natively support
+sorting 64-bit integers.
+
+It is used by FixupDlls to order the import fixup locations in the image
+so they can be organized by page.
+
+@param	aLeft			64-bit unsigned integer to compare against aRight.
+@param	aRight			64-bit unsigned integer to compare against aLeft.
+@return					-1 if aLeft < aRight; 0 if aLeft == aRight; and
+						+1 if aLeft > aRight.  This conforms to the behavior
+						which is expected from a function used by TLinearOrder.
+*/
+static TInt Uint64LinearOrderFunc(const TUint64& aLeft, const TUint64& aRight)
+	{
+	if (aLeft < aRight)
+		return -1;
+	else if (aLeft > aRight)
+		return 1;
+	else
+		return 0;
+	}
+
+
+TUint64* E32Image::ExpandFixups(TInt aNumFixups)
+	{
+	__IF_DEBUG(Printf("ExpandFixups,%d+%d", iFixupCount,aNumFixups));
+	TInt newCount = iFixupCount+aNumFixups;
+	TUint64* fixups = (TUint64*) User::ReAlloc(iFixups, sizeof(TUint64) * newCount);
+	if(!fixups)
+		return 0;
+	TUint64* newFixups = fixups+iFixupCount;
+	iFixupCount = newCount;
+	iFixups = fixups;
+	return newFixups;
+	}
+
+
+/**
+Helper function for FixupImports.  Takes the set of
+64-bit <addr,val> fixups, and organizes them into pages.
+
+Each page is stored as fXXX YYYY ZZZZ where YYYY ZZZZ is written
+to the word at offset XXX.  (See PREQ1110 Design Sketch v1.0 S3.1.1.2.3.2.)
+
+On success iImportFixupTableSize is set to the table size in bytes,
+and iImportFixupTable is a cell containing the table.
+
+@return					Symbian OS error code.
+*/
+TInt E32Image::BuildImportFixupTable()
+	{
+	__IF_DEBUG(Printf(">BuildImportFixupTable,0x%08x,%d", iFixups, iFixupCount));
+
+	// sort the array in address order, to organize by page
+	RArray<TUint64> fixup64ToSort(sizeof(TUint64), iFixups, iFixupCount);
+	// SortUnsigned doesn't work on TUint64
+	fixup64ToSort.Sort(TLinearOrder<TUint64>(Uint64LinearOrderFunc));
+
+	// now have <address | new-value> pairs, organize into pages.
+	// Each page is stored as fXXX YYYY ZZZZ where YYYY ZZZZ is written
+	// to the word at offset XXX.  (See PREQ1110 Design Sketch v1.0 S3.1.1.2.3.2.)
+
+	TUint32 pageCount = SizeToPageCount(iCodeSize);
+	iImportFixupTableSize = (pageCount+1) * sizeof(TUint32) + iFixupCount * 3 * sizeof(TUint16);
+	iImportFixupTable = (TUint32*) User::Alloc(iImportFixupTableSize);
+	__IF_DEBUG(Printf("iImportFixupTable=0x%08x", iImportFixupTable));
+	if (iImportFixupTable == 0)
+		return KErrNoMemory;
+
+	// byte offsets of pages into the table are written as 32-bit words at
+	// the start of the table
+
+	TUint32 lastPage = 0;
+	// byte index of first 48-bit entry in the table, after sentinel index
+	iImportFixupTable[0] = (pageCount + 1) * sizeof(TUint32);;
+
+	// location to which 48-bit imports are written
+	TUint16* importOffset = (TUint16*)(iImportFixupTable + pageCount + 1);
+
+	// location from where 64-bit <addr,val> pairs are read
+	const TUint64* avEnd = iFixups + iFixupCount;
+
+	for (const TUint64* avPtr = iFixups; avPtr < avEnd; ++avPtr)
+		{
+		TUint64 addr_val = *avPtr;
+		TUint32 addr = I64HIGH(addr_val) - iCodeLoadAddress;
+		TUint32 page = addr >> 12;
+		if (page > lastPage)
+			{
+			// calculate new start index for current page
+			TUint32 newStart = TUint32(importOffset) - TUint32(iImportFixupTable);
+
+			__IF_DEBUG(Printf("page=%d, lastPage=%d, newStart=0x%08x", page, lastPage, newStart));
+
+			// mark intermediate pages as zero-length, starting and ending at
+			// current offset
+			while (++lastPage <= page)
+				iImportFixupTable[lastPage] = newStart;
+			--lastPage;
+			}
+
+		TUint16 offsetIntoPage;
+		offsetIntoPage = (addr & KPageOffsetMask);
+		*importOffset++ = offsetIntoPage;
+
+		TUint32 val = I64LOW(addr_val);
+		*importOffset++ = val;				// low halfword stored first (YYYY)
+		*importOffset++ = val >> 16;		// high halfword stored second (ZZZZ)
+		}
+
+	// sentinel value marks end of table
+	while (++lastPage <= pageCount)
+		iImportFixupTable[lastPage] = iImportFixupTableSize;
+
+	__IF_DEBUG(Printf("processed table (size=%d,pageCount=%d)", iImportFixupTableSize, pageCount));
+
+#ifdef _DEBUG
+	// dump the import fixup table if loader tracing enabled
+	const TUint16* table16 = (const TUint16*)iImportFixupTable;
+	const TInt halfWordsInTable = iImportFixupTableSize / 2;
+	for (TInt i = 0; i < halfWordsInTable; i += 4)
+		{
+		__IF_DEBUG(Printf(
+			"%04x: %04x %04x %04x %04x",
+			i * 2, table16[i+0], table16[i+1], table16[i+2], table16[i+3]));
+		}
+#endif
+
+	User::Free(iFixups);
+	iFixups = 0;
+	return KErrNone;
+	}
+
+
+TInt GetModuleInfo(RLdrReq& aReq)
+//
+//	Read capabilities from file found
+//
+	{
+	__IF_DEBUG(Printf("ReadModuleInfo %S",aReq.iFileName));
+	TFileNameInfo& fi = aReq.iFileNameInfo;
+	RImageFinder finder;
+	TInt r = finder.Set(aReq);
+	if (r == KErrNone)
+		{
+		finder.iFindExact = ETrue;
+
+		r = KErrNotSupported;
+
+		// must specify a fully qualified name
+		if (fi.DriveLen() && fi.PathLen())
+			{
+			if (fi.VerLen())
+				aReq.iRequestedVersion = fi.iVersion;
+			else
+				aReq.iRequestedVersion = KModuleVersionWild;
+			r = finder.Search();
+			if (r == KErrNone)
+				{
+				RLibrary::TInfo ret_info;
+				memclr(&ret_info,sizeof(ret_info));
+				ret_info.iModuleVersion = finder.iNew.iModuleVersion;
+				ret_info.iUids = *(const TUidType*)finder.iNew.iUid;
+				*(SSecurityInfo*)&ret_info.iSecurityInfo = finder.iNew.iS;
+				TPckgC<RLibrary::TInfo> ret_pckg(ret_info);
+				r = aReq.iMsg->Write(2, ret_pckg);
+				}
+			}
+		}
+	finder.Close();
+	return r;
+	}
+
+TInt GetInfoFromHeader(const RLoaderMsg& aMsg)
+	{
+	TInt r;
+
+	// Get size of header supplied by client
+	TInt size;
+	size = aMsg.GetDesLength(0);
+	if(size<0)
+		return size;
+	if(size>RLibrary::KRequiredImageHeaderSize)
+		size = RLibrary::KRequiredImageHeaderSize;
+	if((TUint)size<sizeof(E32ImageHeaderV))
+		return KErrUnderflow;
+
+	// Get header data
+	TUint8* data = new TUint8[size];
+	if(!data)
+		return KErrNoMemory;
+	TPtr8 ptr(data,size);
+	r = aMsg.Read(0,ptr);
+	if(r==KErrNone)
+		{
+		// Check header is valid
+		E32ImageHeaderV* header=(E32ImageHeaderV*)data;
+		if(header->TotalSize()>size)
+			r = KErrUnderflow;
+		else
+			{
+			TUint32 uncompressedSize;
+			r = header->ValidateHeader(-1,uncompressedSize);
+			}
+		if(r==KErrNone)
+			{
+			// Get info
+			RLibrary::TInfoV2 ret_info;
+			memclr(&ret_info,sizeof(ret_info));
+			ret_info.iModuleVersion = header->ModuleVersion();
+			ret_info.iUids = (TUidType&)header->iUid1;
+			header->GetSecurityInfo((SSecurityInfo&)ret_info.iSecurityInfo);
+			ret_info.iHardwareFloatingPoint = (header->iFlags & KImageHWFloatMask) >> KImageHWFloatShift;
+
+			ret_info.iDebugAttributes = 0;	// default
+			if (header->iFlags & KImageDebuggable)
+				ret_info.iDebugAttributes |= RLibrary::TInfoV2::EDebugAllowed;
+
+			TPckg<RLibrary::TInfoV2> ret_pckg(ret_info);
+			TInt max = aMsg.GetDesMaxLength(1);
+			if (ret_pckg.Length() > max)
+				ret_pckg.SetLength(max);
+			r = aMsg.Write(1, ret_pckg);
+			}
+		}
+
+	delete[] data;
+	return r;
+	}
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+void memory_dump(const TAny* a, TUint l)
+	{
+	TBuf8<80> buf;
+	const TUint8* s = (const TUint8*)a;
+	TInt n=0;
+	while (l)
+		{
+		buf.Append(' ');
+		buf.AppendNumFixedWidth(*s++, EHex, 2);
+		--l;
+		++n;
+		if (l==0 || n==16)
+			{
+			RDebug::Printf((const char*)buf.PtrZ());
+			buf.Zero();
+			n=0;
+			}
+		}
+	}
+
+void RImageFinder::Dump(const char* aTitle, TInt aR)
+	{
+	RDebug::Printf(aTitle);
+	RDebug::Printf("r=%d",aR);
+	if (iExisting)
+		{
+		RDebug::Printf("Existing image found");
+		RDebug::Printf("Filename=%S Attr=%08x", &iExisting->iFileName, iExisting->iAttr);
+		RDebug::Printf("SID %08x Caps %08x %08x", iExisting->iS.iSecureId, iExisting->iS.iCaps[1], iExisting->iS.iCaps[0]);
+		const TUint32* uid = (const TUint32*)&iExisting->iUids;
+		RDebug::Printf("UIDs %08x %08x %08x VER %08x", uid[0], uid[1], uid[2], iExisting->iModuleVersion);
+		RDebug::Printf("Rom %08x", iExisting->iRomImageHeader);
+		}
+	else if (iNewValid)
+		{
+		RDebug::Printf("New image found");
+		RDebug::Printf("Filename=%S Attr=%08x", &iNewFileName, iNew.iAttr);
+		RDebug::Printf("SID %08x Caps %08x %08x", iNew.iS.iSecureId, iNew.iS.iCaps[1], iNew.iS.iCaps[0]);
+		const TUint32* uid = (const TUint32*)iNew.iUid;
+		RDebug::Printf("UIDs %08x %08x %08x VER %08x", uid[0], uid[1], uid[2], iNew.iModuleVersion);
+		RDebug::Printf("Rom %08x", iNew.iRomImageHeader);
+		}
+	else
+		{
+		RDebug::Printf("No suitable image found");
+		RDebug::Printf("#NM=%d #UidFail=%d #CapFail=%d #MajVFail=%d #ImpFail=%d", iNameMatches, iUidFail, iCapFail, iMajorVersionFail, iImportFail);
+		}
+	}
+
+void DumpImageHeader(const E32ImageHeader* a)
+	{
+	RDebug::Printf("E32ImageHeader at %08x :", a);
+	TUint abi = a->ABI();
+	TUint hdrfmt = a->HeaderFormat();
+	TUint impfmt = a->ImportFormat();
+	TUint eptfmt = a->EntryPointFormat();
+	RDebug::Printf("Header format %d", hdrfmt>>KImageHdrFmtShift);
+	RDebug::Printf("Import format %d", impfmt>>KImageImpFmtShift);
+	RDebug::Printf("EntryPoint format %d", eptfmt>>KImageEptShift);
+	RDebug::Printf("ABI %d", abi>>KImageABIShift);
+	RDebug::Printf("UIDs %08x %08x %08x (%08x)", a->iUid1, a->iUid2, a->iUid3, a->iUidChecksum);
+	RDebug::Printf("Header CRC %08x", a->iHeaderCrc);
+	RDebug::Printf("Signature %08x", a->iSignature);
+	RDebug::Printf("CPU %08x", (TUint)a->CpuIdentifier());
+	RDebug::Printf("ModuleVersion %08x", a->ModuleVersion());
+	RDebug::Printf("Compression Type %08x", a->CompressionType());
+	RDebug::Printf("Tools Version %d.%02d(%d)", a->iToolsVersion.iMajor, a->iToolsVersion.iMinor, a->iToolsVersion.iBuild);
+	RDebug::Printf("Flags %08x", a->iFlags);
+	RDebug::Printf("Code Size %08x", a->iCodeSize);
+	RDebug::Printf("Text Size %08x", a->iTextSize);
+	RDebug::Printf("Data Size %08x", a->iDataSize);
+	RDebug::Printf("BSS Size %08x", a->iBssSize);
+	RDebug::Printf("Stack Size %08x", a->iStackSize);
+	RDebug::Printf("HeapSizeMin %08x", a->iHeapSizeMin);
+	RDebug::Printf("HeapSizeMax %08x", a->iHeapSizeMax);
+	RDebug::Printf("iEntryPoint %08x", a->iEntryPoint);
+	RDebug::Printf("iCodeBase %08x", a->iCodeBase);
+	RDebug::Printf("iDataBase %08x", a->iDataBase);
+	RDebug::Printf("DLL Ref Table Count %d", a->iDllRefTableCount);
+	RDebug::Printf("Export Dir Count %d", a->iExportDirCount);
+	RDebug::Printf("Code Offset %08x", a->iCodeOffset);
+	RDebug::Printf("Data Offset %08x", a->iDataOffset);
+	RDebug::Printf("Code Reloc Offset %08x", a->iCodeRelocOffset);
+	RDebug::Printf("Data Reloc Offset %08x", a->iDataRelocOffset);
+	RDebug::Printf("Import Offset %08x", a->iImportOffset);
+	RDebug::Printf("Export Dir Offset %08x", a->iExportDirOffset);
+	RDebug::Printf("Priority %d", (TUint)a->ProcessPriority());
+	// KImageHdrFmt_J
+	RDebug::Printf("iUncompressedSize %08x", ((E32ImageHeaderComp*)a)->iUncompressedSize);
+	// KImageHdrFmt_V
+	E32ImageHeaderV* v = (E32ImageHeaderV*)a;
+	RDebug::Printf("SID %08x VID %08x CAP %08x %08x", v->iS.iSecureId, v->iS.iVendorId, v->iS.iCaps[1], v->iS.iCaps[0]);
+	RDebug::Printf("iExportDescType %02x", v->iExportDescType);
+	RDebug::Printf("iExportDescSize %04x", v->iExportDescSize);
+	if (v->iExportDescSize)
+		memory_dump(v->iExportDesc, v->iExportDescSize);
+	}
+#endif
+