userlibandfileserver/fileserver/sfile/sf_lepoc.cpp
author John Imhofe
Mon, 19 Oct 2009 15:55:17 +0100
changeset 0 a41df078684a
child 21 e7d2d738d3c2
permissions -rw-r--r--
Convert Kernelhwsrv package from SFL to EPL kernel\eka\compsupp is subject to the ARM EABI LICENSE userlibandfileserver\fatfilenameconversionplugins\unicodeTables is subject to the Unicode license kernel\eka\kernel\zlib is subject to the zlib license

// 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