// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Symbian Foundation License v1.0" to Symbian Foundation members and "Symbian Foundation End User License Agreement v1.0" to non-members
// which accompanies this distribution, and is available
// at the URL "http://www.symbianfoundation.org/legal/licencesv10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// f32\inc\f32image.h
// 
//



/**
 @file f32\inc\f32image.h
 @internalTechnology
*/

#ifndef __F32IMAGE_H__
#define __F32IMAGE_H__
#include <e32cmn.h>

/**
Value used for E32ImageHeader::iCpuIdentifier.
*/
enum TCpu
	{
	ECpuUnknown=0, ECpuX86=0x1000, ECpuArmV4=0x2000, ECpuArmV5=0x2001, ECpuArmV6=0x2002, ECpuMCore=0x4000
	};

/**
Ordinal value of the first entry in an executables export directory.
@see E32ImageHeader::iExportDirOffset.
*/
const TInt KOrdinalBase=1;

/**
Value used to initialise E32ImageHeader::iHeaderCrc prior to CRC generation.
*/
const TUint32 KImageCrcInitialiser	= 0xc90fdaa2u;


/**
Byte offset from an executable's entrypoint to the code segment ID storage location.
*/
const TUint KCodeSegIdOffset = 12;

//
// Flags fields for E32ImageHeader::iFlags
//

const TUint KImageDll				= 0x00000001u;	///< Flag set if executable is a DLL, clear if an EXE.

const TUint KImageNoCallEntryPoint	= 0x00000002u;	///< Obsolete flag ignored since Symbian OS version 8.1b.

const TUint KImageFixedAddressExe	= 0x00000004u;	///< Executable's data should not move when running on the moving memory model.

const TUint KImageABIMask			= 0x00000018u;	///< Bitmask for ABI value.
const TInt	KImageABIShift			= 3;			///< Bit shift count for ABI value.
const TUint	KImageABI_GCC98r2		= 0x00000000u;	///< Obsolete ABI for ARM targets.
const TUint	KImageABI_EABI			= 0x00000008u;	///< ARM EABI

const TUint KImageEptMask			= 0x000000e0u;	///< Bitmask for Entrypoint value.
const TInt	KImageEptShift			= 5;			///< Bit shift count for Entrypoint value
const TUint KImageEpt_Eka1			= 0x00000000u;	///< @removed Obsolete format not used since Symbian OS version 8.1b.
const TUint KImageEpt_Eka2			= 0x00000020u;	///< Standard entrypoint for ARM executable.

const TUint KImageUnpaged			= 0x00000100u;	///< Executable image should not be demand paged. Exclusive with KImagePaged,
const TUint KImagePaged				= 0x00000200u;	///< Executable image should be demand paged. Exclusive with KImageUnpaged,

const TUint KImageNmdExpData		= 0x00000400u;	///< Flag to indicate when named symbol export data present in image

const TUint KImageDebuggable		= 0x00000800u;	///< Flag to indicate image is debuggable

const TUint KImageHWFloatMask		= 0x00f00000u;	///< Bitmask for Floating Point type.
const TInt	KImageHWFloatShift		= 20;			///< Bit shift count for Floating Point type.
const TUint	KImageHWFloat_None		= EFpTypeNone << KImageHWFloatShift;	///< No hardware floating point used.
const TUint KImageHWFloat_VFPv2		= EFpTypeVFPv2 << KImageHWFloatShift;	///< ARM VFPv2 floating point used.

const TUint KImageHdrFmtMask		= 0x0f000000u;	///< Bitmask for header format type.
const TInt	KImageHdrFmtShift		= 24;			///< Bit shift count for header format type.
const TUint KImageHdrFmt_Original	= 0x00000000u;	///< @removed Obsolete format not used since Symbian OS version 8.1b.
const TUint KImageHdrFmt_J			= 0x01000000u;	///< @removed Obsolete format not used since Symbian OS version 8.1b.
const TUint KImageHdrFmt_V			= 0x02000000u;	///< Header has format given by class E32ImageHeaderV.

const TUint KImageImpFmtMask		= 0xf0000000u;	///< Bitmask for import section format type.
const TInt	KImageImpFmtShift		= 28;			///< Bit shift count for import section format type.
const TUint KImageImpFmt_PE			= 0x00000000u;	///< PE-derived imports.
const TUint KImageImpFmt_ELF		= 0x10000000u;	///< ELF-derived imports.
const TUint KImageImpFmt_PE2		= 0x20000000u;	///< PE-derived imports without redundant copy of import ordinals.




// forward references...
class RFile;
class E32RelocSection;


/**
Structure for an executable image's header.
This is extended by E32ImageHeaderComp and E32ImageHeaderV.
All executables since Symbian OS version 8.1b have an header given by class E32ImageHeaderV.

Summary of an executable image structure...

- Header,			0..iCodeOffset-1
- Code part,		iCodeOffset..iCodeOffset+iCodeSize-1
	- .text section,				0 + iTextSize
	- Import Address Table (IAT),	iText + ?
	- Export Directory,				iExportDirOffset + iExportDirCount*4 (in .text Section)
- Rest of data,		iCodeOffset+iCodeSize..EOF
	- .data section,				iDataOffset + iDataSize
	- Import section, 				iImportOffset + sizeof(E32ImportSection)+?
	- Code relocation section,		iCodeRelocOffset + sizeof(E32RelocSection)+?
	- Data relocation section,		iDataRelocOffset + sizeof(E32RelocSection)+?
*/
class E32ImageHeader
	{
public:
	static TInt New(E32ImageHeader*& aHdr, RFile& aFile);
	static TInt New(E32ImageHeader*& aHdr, TUint8* aFileData, TUint32 aFileSize);
	TInt ValidateHeader(TInt aFileSize, TUint32& aUncompressedSize) const;

	inline static TUint ABIFromFlags(TUint aFlags);
	inline static TUint EptFromFlags(TUint aFlags);
	inline static TUint HdrFmtFromFlags(TUint aFlags);
	inline static TUint ImpFmtFromFlags(TUint aFlags);

	inline TUint ABI() const;
	inline TUint EntryPointFormat() const;
	inline TUint HeaderFormat() const;
	inline TUint ImportFormat() const;

	inline TUint32 CompressionType() const;
	inline TUint32 ModuleVersion() const;
	inline TInt TotalSize() const;
	inline TInt UncompressedFileSize() const;
	inline void GetSecurityInfo(SSecurityInfo& aInfo) const;
	inline TCpu CpuIdentifier() const;
	inline TProcessPriority ProcessPriority() const;
	inline TUint32 ExceptionDescriptor() const;
public:
	TUint32	iUid1;				///< KDynamicLibraryUidValue or KExecutableImageUidValue
	TUint32	iUid2;				///< Second UID for executable.
	TUint32	iUid3;				///< Third UID for executable.
	TUint32 iUidChecksum;		///< Checksum for iUid1, iUid2 and iUid3.
	TUint iSignature;			///< Contains 'EPOC'.
	TUint32	iHeaderCrc;			///< CRC-32 of entire header. @see #KImageCrcInitialiser.
	TUint32 iModuleVersion;		///< Version number for this executable (used in link resolution).
	TUint32 iCompressionType;	///< Type of compression used for file contents located after the header. (UID or 0 for none).
	TVersion iToolsVersion;		///< Version number of tools which generated this file.
	TUint32 iTimeLo;			///< Least significant 32 bits of the time of image creation, in milliseconds since since midnight Jan 1st, 2000.
	TUint32 iTimeHi;			///< Most significant 32 bits of the time of image creation, in milliseconds since since midnight Jan 1st, 2000.
	TUint iFlags;				///< Contains various bit-fields of attributes for the image.
	TInt iCodeSize;				///< Size of executables code. Includes import address table, constant data and export directory.
	TInt iDataSize;				///< Size of executables initialised data.
	TInt iHeapSizeMin;			///< Minimum size for an EXEs runtime heap memory.
	TInt iHeapSizeMax;			///< Maximum size for an EXEs runtime heap memory.
	TInt iStackSize;			///< Size for stack required by an EXEs initial thread.
	TInt iBssSize;				///< Size of executables uninitialised data.
	TUint iEntryPoint;			///< Offset into code of the entry point.
	TUint iCodeBase;			///< Virtual address that the executables code is linked for.
	TUint iDataBase;			///< Virtual address that the executables data is linked for.
	TInt iDllRefTableCount;		///< Number of executable against which this executable is linked. The number of files mention in the import section at iImportOffset.
	TUint iExportDirOffset;		///< Byte offset into file of the export directory.
	TInt iExportDirCount;		///< Number of entries in the export directory.
	TInt iTextSize;				///< Size of just the text section, also doubles as the offset for the Import Address Table w.r.t. the code section.
	TUint iCodeOffset;			///< Offset into file of the code section. Also doubles the as header size.
	TUint iDataOffset;			///< Offset into file of the data section.
	TUint iImportOffset;		///< Offset into file of the import section (E32ImportSection).
	TUint iCodeRelocOffset;		///< Offset into file of the code relocation section (E32RelocSection).
	TUint iDataRelocOffset;		///< Offset into file of the data relocation section (E32RelocSection).
	TUint16 iProcessPriority;	///< Initial runtime process priorty for an EXE. (Value from enum TProcessPriority.)
	TUint16 iCpuIdentifier;		///< Value from enum TCpu which indicates the CPU architecture for which the image was created
	};


/**
Extends E32ImageHeader.
*/
class E32ImageHeaderComp : public E32ImageHeader
	{
public:
	TUint32 iUncompressedSize;	///< Uncompressed size of file data after the header, or zero if file not compressed.
	};


/**
Extends E32ImageHeaderComp.
All Symbian OS executable files have a header in this format since OS version 8.1b.
*/
class E32ImageHeaderV : public E32ImageHeaderComp
	{
public:
	SSecurityInfo iS;				///< Platform Security information of executable.
	TUint32 iExceptionDescriptor;   ///< Offset in bytes from start of code section to Exception Descriptor, bit 0 set if valid.
	TUint32 iSpare2;				///< Reserved for future use. Set to zero.
	TUint16	iExportDescSize;		///< Size of export description stored in iExportDesc.
	TUint8	iExportDescType;		///< Type of description of holes in export table
	TUint8	iExportDesc[1];			///< Description of holes in export table, size given by iExportDescSize..
public:
	TInt ValidateWholeImage(TAny* aBufferStart, TUint aBufferSize) const;
	TInt ValidateHeader(TInt aFileSize, TUint32& aUncompressedSize) const;
	TInt ValidateExportDescription() const;
	TInt ValidateRelocations(TAny* aBufferStart, TUint aBufferSize, TUint aRelocationInfoOffset, TUint aRelocatedSectionSize, E32RelocSection*& aRelocationSection) const;
	TInt ValidateImports(TAny* aBufferStart, TUint aBufferSize, TUint& aBiggestImportCount) const;
	TInt ValidateAndAdjust(TUint32 aFileSize);
	};

// export description type E32ImageHeaderV::iExportDescType
const TUint	KImageHdr_ExpD_NoHoles			=0x00;	///< No holes, all exports present.
const TUint	KImageHdr_ExpD_FullBitmap		=0x01;	///< Full bitmap present at E32ImageHeaderV::iExportDesc
const TUint	KImageHdr_ExpD_SparseBitmap8	=0x02;	///< Sparse bitmap present at E32ImageHeaderV::iExportDesc, granularity 8
const TUint	KImageHdr_ExpD_Xip				=0xff;	///< XIP file


//
// inline getters for E32ImageHeader
//

/**
Extract ABI type from aFlags.
*/
inline TUint E32ImageHeader::ABIFromFlags(TUint aFlags)
	{
	return aFlags & KImageABIMask;
	}

/**
Extract ABI type from #iFlags.
*/
inline TUint E32ImageHeader::ABI() const
	{
	return ABIFromFlags(iFlags);
	}

/**
Extract entrypoint format from aFlags.
*/
inline TUint E32ImageHeader::EptFromFlags(TUint aFlags)
	{
	return aFlags & KImageEptMask;
	}

/**
Extract entrypoint format from #iFlags.
*/
inline TUint E32ImageHeader::EntryPointFormat() const
	{
	return EptFromFlags(iFlags);
	}

/**
Extract header format from aFlags.
*/
inline TUint E32ImageHeader::HdrFmtFromFlags(TUint aFlags)
	{
	return aFlags & KImageHdrFmtMask;
	}

/**
Extract header format from #iFlags.
*/
inline TUint E32ImageHeader::HeaderFormat() const
	{
	return HdrFmtFromFlags(iFlags);
	}

/**
Extract import format from aFlags.
*/
inline TUint E32ImageHeader::ImpFmtFromFlags(TUint aFlags)
	{
	return aFlags & KImageImpFmtMask;
	}

/**
Extract import format from #iFlags.
*/
inline TUint E32ImageHeader::ImportFormat() const
	{
	return ImpFmtFromFlags(iFlags);
	}

/**
Return #iCompressionType.
*/
inline TUint32 E32ImageHeader::CompressionType() const
	{
	return iCompressionType;
	}

/**
Return #iModuleVersion.
*/
inline TUint32 E32ImageHeader::ModuleVersion() const
	{
	return iModuleVersion;
	}

/**
Return size of this header.
*/
inline TInt E32ImageHeader::TotalSize() const
	{
	return iCodeOffset;
	}

/**
Return total size of file after decompression, or -1 if file not compressed.
*/
inline TInt E32ImageHeader::UncompressedFileSize() const
	{
	if(iCompressionType==0)
		return -1;			// not compressed
	else
		return ((E32ImageHeaderComp*)this)->iUncompressedSize + TotalSize();
	}

/**
Return copy of security info, #E32ImageHeaderV::iS.
*/
inline void E32ImageHeader::GetSecurityInfo(SSecurityInfo& aInfo) const
	{
	aInfo = ((E32ImageHeaderV*)this)->iS;
	}

/**
Return #iCpuIdentifier.
*/
inline TCpu E32ImageHeader::CpuIdentifier() const
	{
	return (TCpu)iCpuIdentifier;
	}

/**
Return #iProcessPriority.
*/
inline TProcessPriority E32ImageHeader::ProcessPriority() const
	{
	return (TProcessPriority)iProcessPriority;
	}

/**
Return fffset in bytes from start of code section for the Exception Descriptor.
Or zero if not present.
*/
inline TUint32 E32ImageHeader::ExceptionDescriptor() const
	{
	TUint32 xd = ((E32ImageHeaderV*)this)->iExceptionDescriptor;

	if((xd & 1) && (xd != 0xffffffffu))
		return (xd & ~1);

	return 0;
	}


/**
A block of imports from a single executable.
These structures are conatined in a images Import Section (E32ImportSection).
*/
class E32ImportBlock
	{
public:
	inline const E32ImportBlock* NextBlock(TUint aImpFmt) const;
	inline TInt Size(TUint aImpFmt) const;
	inline const TUint* Imports() const;	// import list if present
public:
	TUint32	iOffsetOfDllName;			///< Offset from start of import section for a NUL terminated executable (DLL or EXE) name.
	TInt	iNumberOfImports;			///< Number of imports from this executable.
//	TUint	iImport[iNumberOfImports];	///< For ELF-derived executes: list of code section offsets. For PE, list of imported ordinals. Omitted in PE2 import format
	};

/**
Return size of this import block.
@param aImpFmt Import format as obtained from image header.
*/
inline TInt E32ImportBlock::Size(TUint aImpFmt) const
	{
	TInt r = sizeof(E32ImportBlock);
	if(aImpFmt!=KImageImpFmt_PE2)
		r += iNumberOfImports * sizeof(TUint);
	return r;
	}

/**
Return pointer to import block which immediately follows this one.
@param aImpFmt Import format as obtained from image header.
*/
inline const E32ImportBlock* E32ImportBlock::NextBlock(TUint aImpFmt) const
	{
	const E32ImportBlock* next = this + 1;
	if(aImpFmt!=KImageImpFmt_PE2)
		next = (const E32ImportBlock*)( (TUint8*)next + iNumberOfImports * sizeof(TUint) );
	return next;
	}

/**
Return address of first import in this block.
For import format KImageImpFmt_ELF, imports are list of code section offsets.
For import format KImageImpFmt_PE, imports are a list of imported ordinals.
For import format KImageImpFmt_PE2, the import list is not present and should not be accessed.
*/
inline const TUint* E32ImportBlock::Imports() const
	{
	return (const TUint*)(this + 1);
	}


/**
Header for the Import Section in an image, as referenced by E32ImageHeader::iImportOffset.
Immediately following this structure are an array of E32ImportBlock structures.
The number of these is given by E32ImageHeader::iDllRefTableCount.
*/
class E32ImportSection
	{
public:
	TInt iSize;		///< Size of this section excluding 'this' structure
//	E32ImportBlock iImportBlock[iDllRefTableCount];
	};


/**
A block of relocations for a single page (4kB) of code/data.

Immediately following this structure are an array of TUint16 values
each representing a single value in the page which is to be relocated.
The lower 12 bits of each entry is the offset, in bytes, from start of this page.
The Upper 4 bits are the relocation type to be applied to the 32-bit value located
at that offset.
	- 1 means relocate relative to code section.
	- 2 means relocate relative to data section.
	- 3 means relocate relative to code or data section; calculate which.

A value of all zeros (0x0000) is ignored. (Used for padding structure to 4 byte alignment).
*/
class E32RelocBlock
	{
public:
	TUint32 iPageOffset;	///< Offset, in bytes, for the page being relocated; relative to the section start. Always a multiple of the page size: 4096 bytes.
	TUint32 iBlockSize;		///< Size, in bytes, for this block structure. Always a multiple of 4.
//	TUint16 iEntry[]
	};


/**
Header for a Relocation Section in an image, as referenced by E32ImageHeader::iCodeRelocOffset
or E32ImageHeader::iDataRelocOffset.

Immediately following this structure are an array of E32RelocBlock structures.
*/
class E32RelocSection
	{
public:
	TInt iSize;				///< Size of this relocation section including 'this' structure. Always a multiple of 4.
	TInt iNumberOfRelocs;	///< Number of relocations in this section.
//	E32RelocBlock iRelockBlock[];
	};


/**
Structure contained in the export directory in text section of the stdexe/stddll.
It contains information on the names of symbols exported by this stdexe/stddll and
pointers to a E32EpocExpSymInfoHdr structure of any stddlls that are dependencies of
this stdexe/stddll.

This is not used for emulator images see E32EmulExpSymInfoHdr below.
@see E32EmulExpSymInfoHdr
*/
class E32EpocExpSymInfoHdr
	{
public:
	TInt	iSize;						// size of this Table
	TInt16	iFlags; 
	TInt16	iSymCount;					// number of symbols
	TInt	iSymbolTblOffset;			// start of the symbol table - offset from byte 0 of this header
	TInt	iStringTableSz;				// size of the string table
	TInt	iStringTableOffset;			// start of the string table having names of the symbols - offset from byte 0 of this header
	TInt	iDllCount;					// Number of dependent DLLs
	TInt	iDepDllZeroOrdTableOffset;	// offset of the DLL dependency table - offset from byte 0 of this header.
	};


/**
Header of the structure contained in the 'KWin32SectionName_NmdExpData' 
segment of emulator stdexe & stddll images.
The segment contains addresses of symbols and NULL 
terminated ASCII strings of the names of static dependencies.
For a stdexe, this segment contains the following:
	a) symbol count (iSymCount) and static dependency count (iDllCount)
	b) iSymCount * symbol addresses
	c) iSymCount * symbol names
	d) iDllCount * dependency names
	
For a stddll, this segment contains the following:
	a) symbol count (iSymCout) is always 0
	b) static dependency count (iDllCount)
	c) iDllCount * dependency names
The symbol addresses and names are not required for a stddll as the Windows API,
GetProcAddress may be used to get the addresses for symbol names.
Since this API works only on DLL handles, we explicitly list them for stdexes.
This is used for emulator images only.
*/
class E32EmulExpSymInfoHdr
	{
public:
	TInt32	iSymCount;		// Number of symbols
	TInt32	iDllCount;		// Number of static dependency DLLs
	};



#ifdef INCLUDE_E32IMAGEHEADER_IMPLEMENTATION

// include code which implements validation functions...

#ifndef RETURN_FAILURE
#define RETURN_FAILURE(_r) return (_r)
#endif

#ifndef E32IMAGEHEADER_TRACE
#define E32IMAGEHEADER_TRACE(_t) ((void)0)
#endif


#include <e32uid.h>


/**
Validate this image header.

After successful validation the following are true:
	- File size is big enough to contain the entire header.
	- Values #iUidChecksum, #iSignature and #iHeaderCrc are correct.
	- CPU type (#iCpuIdentifier), ABI type (#iFlags&#KImageABIMask) and
	  entrypoint type (#iFlags&#KImageEptMask) are valid for this system.
	- Code part of file as specified by #iCodeOffset and #iCodeSize is fully within the file.
	- Text section size (#iTextSize) is within code part.
	- Entrypoint value (#iEntryPoint) lies within the code part and is aligned correctly.
	- Export directory as specified by #iExportDirCount and #iExportDirOffset is fully
	  within code part and is aligned correctly.
	- Exception description (E32ImageHeaderV::iExceptionDescriptor), if present,
	  lies within the code part.
	- Data part of file as specified by #iDataOffset and #iDataSize is fully within the file.
	  Or data is not present (#iDataOffset==#iDataSize==0).
	- Import section (class E32ImportSection at #iImportOffset) is within 'rest of data'
	  and aligned correctly. Data following the E32ImportSection header is NOT validated or
	  checked if it is fully contained within the file.
	- Code relocations (class E32RelocSection at #iCodeRelocOffset) is within 'rest of data'
	  and aligned correctly. Data following the E32RelocSection header is NOT validated or
	  checked if it is fully contained within the file.
	- Data relocations (class E32RelocSection at #iDataRelocOffset) is within 'rest of data'
	  and aligned correctly. Data following the E32RelocSection header is NOT validated or
	  checked if it is fully contained within the file.
	- Export description is validated by E32ImageHeaderV::ValidateExportDescription().
	- #iUid1 is consistant with #iFlags&#KImageDll. I.e. if flaged as a DLL, #iUid1 is
	  KDynamicLibraryUidValue, otherwise it is KExecutableImageUidValue.
	- Version number (#iModuleVersion) is valid. (Major and minor versions are <32768).
	- File compression type (#iCompressionType) is supported.
	- #iHeapSizeMax>=#iHeapSizeMin
	- All signed values in header are not negative.

@param		aFileSize			Total size of the file from which this header was created.
@param[out] aUncompressedSize	Returns the total size that the file data would be once decompressed.

@return KErrNone if no errors detected;
		KErrCorrupt if errors found;
		KErrNotSupported if image format not supported on this platform.
*/
TInt E32ImageHeader::ValidateHeader(TInt aFileSize, TUint32& aUncompressedSize) const
	{
	// check file is big enough for any header...
	if(TUint(aFileSize)<sizeof(*this))
		return KErrCorrupt;

	TUint hdrfmt = HeaderFormat();
	if(hdrfmt==KImageHdrFmt_V)
		return ((E32ImageHeaderV*)this)->ValidateHeader(aFileSize,aUncompressedSize);

	return KErrNotSupported; // header format unrecognised
	}

/**
Validate this image header.

@param aFileSize				Total size of the file from which this header was created.
@param[out] aUncompressedSize	Returns the total size that the file data would be once decompressed.

@return KErrNone if no errors detected;
		KErrCorrupt if errors found;
		KErrNotSupported if image format not supported on this platform.
*/
TInt E32ImageHeaderV::ValidateHeader(TInt aFileSize, TUint32& aUncompressedSize) const
	{
	const TUint KMaxDesSize = 0x0fffffffu; // maximum size of descriptor
	if(aFileSize==-1)
		{
		// file size unknown, set to maximum valid so rest of validation works...
		aFileSize = KMaxDesSize;
		}
	if(TUint(aFileSize)>KMaxDesSize)
		RETURN_FAILURE(KErrCorrupt); // file size negative or too big

	aUncompressedSize = 0;

	// check file is big enough to contain this header...
	if(aFileSize<(TInt)sizeof(*this))
		RETURN_FAILURE(KErrCorrupt);

	// check header format version...
	if((iFlags&KImageHdrFmtMask)!=KImageHdrFmt_V)
		RETURN_FAILURE(KErrNotSupported);

	// check header size...
	TUint headerSize = iCodeOffset;
	if(headerSize>TUint(aFileSize))
		RETURN_FAILURE(KErrCorrupt); // Fuzzer can't trigger this because Loader will fail earlier when reading header from file

	// check iCpuIdentifier...
	TCpu cpu = (TCpu)iCpuIdentifier;
	TBool isARM = (cpu==ECpuArmV4 || cpu==ECpuArmV5 || cpu==ECpuArmV6);
#if defined(__CPU_ARM)
	if(!isARM)
		RETURN_FAILURE(KErrNotSupported);
#elif defined(__CPU_X86)
	if(cpu!=ECpuX86)
		RETURN_FAILURE(KErrNotSupported);
#endif
	TUint32 pointerAlignMask = isARM ? 3 : 0;	// mask of bits which must be zero for aligned pointers/offsets

	// check iUid1,iUid2,iUid3,iUidChecksum...
	TUidType uids = *(const TUidType*)&iUid1;
	TCheckedUid chkuid(uids);
	const TUint32* pChkUid = (const TUint32*)&chkuid; // need hackery to verify the UID checksum since everything is private
	if(pChkUid[3]!=iUidChecksum)
		RETURN_FAILURE(KErrCorrupt);

	// check iSignature...
	if(iSignature!=0x434f5045) // 'EPOC'
		RETURN_FAILURE(KErrCorrupt);

	// check iHeaderCrc...
	TUint32 supplied_crc = iHeaderCrc;
	((E32ImageHeaderV*)this)->iHeaderCrc = KImageCrcInitialiser;
	TUint32 crc = 0;
	Mem::Crc32(crc, this, headerSize);
	((E32ImageHeaderV*)this)->iHeaderCrc = supplied_crc;
	if(crc!=supplied_crc)
		RETURN_FAILURE(KErrCorrupt);

	// check iModuleVersion...
	TUint32 mv = iModuleVersion;
	if(mv>=0x80000000u || (mv&0x0000ffffu)>0x8000u)
		RETURN_FAILURE(KErrNotSupported);

	// check iCompressionType and get uncompressed size...
	TUint compression = iCompressionType;
	TUint uncompressedSize = aFileSize;
	if(compression!=KFormatNotCompressed)
		{
		if(compression!=KUidCompressionDeflate && compression!=KUidCompressionBytePair)
	        RETURN_FAILURE(KErrNotSupported);  // unknown compression method
		uncompressedSize = headerSize+iUncompressedSize;
		if(uncompressedSize<headerSize)
			RETURN_FAILURE(KErrCorrupt); // size overflowed 32 bits
		}

	// check sizes won't overflow the limit for a descriptor (many Loader uses won't like that).
	if(uncompressedSize>KMaxDesSize)
		RETURN_FAILURE(KErrCorrupt);

	// check KImageDll in iFlags...
	if(iFlags&KImageDll)
		{
		if(iUid1!=TUint32(KDynamicLibraryUidValue))
			RETURN_FAILURE(KErrNotSupported);
		}
	else if(iUid1!=TUint32(KExecutableImageUidValue))
		RETURN_FAILURE(KErrNotSupported);

	// check iFlags for ABI and entry point types...
	if(isARM)
		{
		if((iFlags&KImageEptMask)!=KImageEpt_Eka2)
			RETURN_FAILURE(KErrNotSupported);
		#if defined(__EABI__)
			if((iFlags&KImageABIMask)!=KImageABI_EABI)
				RETURN_FAILURE(KErrNotSupported);
		#elif defined(__GCC32__)
			if((iFlags&KImageABIMask)!=KImageABI_GCC98r2)
				RETURN_FAILURE(KErrNotSupported);
		#endif
		}
	else
		{
		if(iFlags&KImageEptMask)
			RETURN_FAILURE(KErrNotSupported); // no special entry point type allowed on non-ARM targets
		if(iFlags&KImageABIMask)
			RETURN_FAILURE(KErrNotSupported);
		}

	// check iFlags for import format...
	if((iFlags&KImageImpFmtMask)>KImageImpFmt_PE2)
		RETURN_FAILURE(KErrNotSupported);

	// check iHeapSizeMin...
	if(iHeapSizeMin<0)
		RETURN_FAILURE(KErrCorrupt);

	// check iHeapSizeMax...
	if(iHeapSizeMax<iHeapSizeMin)
		RETURN_FAILURE(KErrCorrupt);

	// check iStackSize...
	if(iStackSize<0)
		RETURN_FAILURE(KErrCorrupt);

	// check iBssSize...
	if(iBssSize<0)
		RETURN_FAILURE(KErrCorrupt);

	// check iEntryPoint...
	if(iEntryPoint>=TUint(iCodeSize))
		RETURN_FAILURE(KErrCorrupt);
	if(iEntryPoint+KCodeSegIdOffset+sizeof(TUint32)>TUint(iCodeSize))
		RETURN_FAILURE(KErrCorrupt);
	if(iEntryPoint&pointerAlignMask)
		RETURN_FAILURE(KErrCorrupt); // not aligned

	// check iCodeBase...
	if(iCodeBase&3)
		RETURN_FAILURE(KErrCorrupt); // not aligned

	// check iDataBase...
	if(iDataBase&3)
		RETURN_FAILURE(KErrCorrupt); // not aligned

	// check iDllRefTableCount...
	if(iDllRefTableCount<0)
		RETURN_FAILURE(KErrCorrupt);
	if(iDllRefTableCount)
		{
		if(!iImportOffset)
			RETURN_FAILURE(KErrCorrupt); // we link to DLLs but have no import data
		}

	// check iCodeOffset and iCodeSize specify region in file...
	TUint codeStart = iCodeOffset;
	TUint codeEnd = codeStart+iCodeSize;
	if(codeEnd<codeStart)
		RETURN_FAILURE(KErrCorrupt);
//	if(codeStart<headerSize)
//		RETURN_FAILURE(KErrCorrupt); // can't happen because headerSize is defined as iCodeOffset (codeStart)
	if(codeEnd>uncompressedSize)
		RETURN_FAILURE(KErrCorrupt);

	// check iDataOffset and iDataSize specify region in file...
	TUint dataStart = iDataOffset;
	TUint dataEnd = dataStart+iDataSize;
	if(dataEnd<dataStart)
		RETURN_FAILURE(KErrCorrupt);
	if(!dataStart)
		{
		// no data...
		if(dataEnd)
			RETURN_FAILURE(KErrCorrupt);
		}
	else
		{
		if(dataStart<codeEnd)
			RETURN_FAILURE(KErrCorrupt);
		if(dataEnd>uncompressedSize)
			RETURN_FAILURE(KErrCorrupt);
		if((dataStart-codeStart)&pointerAlignMask)
			RETURN_FAILURE(KErrCorrupt); // data not aligned with respect to code
		}


	// check total data size isn't too bit...
	TUint totalDataSize = iDataSize+iBssSize;
	if(totalDataSize>0x7fff0000)
		RETURN_FAILURE(KErrNoMemory);

	// check iExportDirOffset and iExportDirCount specify region in code part...
	if(TUint(iExportDirCount)>65535)
		RETURN_FAILURE(KErrCorrupt); // too many exports
	if(iExportDirCount)
		{
		TUint exportsStart = iExportDirOffset;
		TUint exportsEnd = exportsStart+iExportDirCount*sizeof(TUint32);
		if(iFlags&KImageNmdExpData)
			exportsStart -= sizeof(TUint32); // allow for 0th ordinal
		if(exportsEnd<exportsStart)
			RETURN_FAILURE(KErrCorrupt);
		if(exportsStart<codeStart)
			RETURN_FAILURE(KErrCorrupt);
		if(exportsEnd>codeEnd)
			RETURN_FAILURE(KErrCorrupt);
		if((exportsStart-codeStart)&pointerAlignMask)
			RETURN_FAILURE(KErrCorrupt); // not aligned within code section
		}

	// check iTextSize...
	if(TUint(iTextSize)>TUint(iCodeSize))
		RETURN_FAILURE(KErrCorrupt);

	// check iImportOffset...
	TUint start = iImportOffset;
	if(start)
		{
		TUint end = start+sizeof(E32ImportSection); // minimum valid size
		if(end<start)
			RETURN_FAILURE(KErrCorrupt);
		if(start<codeEnd)
			RETURN_FAILURE(KErrCorrupt);
		if(end>uncompressedSize)
			RETURN_FAILURE(KErrCorrupt);
		if((start-codeEnd)&pointerAlignMask)
			RETURN_FAILURE(KErrCorrupt); // not aligned within 'rest of data'
		}

	// check iCodeRelocOffset...
	start = iCodeRelocOffset;
	if(start)
		{
		TUint end = start+sizeof(E32RelocSection); // minimum valid size
		if(end<start)
			RETURN_FAILURE(KErrCorrupt);
		if(start<codeEnd)
			RETURN_FAILURE(KErrCorrupt);
		if(end>uncompressedSize)
			RETURN_FAILURE(KErrCorrupt);
		if((start-codeEnd)&pointerAlignMask)
			RETURN_FAILURE(KErrCorrupt); // not aligned within 'rest of data'
		}

	// check iDataRelocOffset...
	start = iDataRelocOffset;
	if(start)
		{
		TUint end = start+sizeof(E32RelocSection); // minimum valid size
		if(end<start)
			RETURN_FAILURE(KErrCorrupt);
		if(start<codeEnd)
			RETURN_FAILURE(KErrCorrupt);
		if(end>uncompressedSize)
			RETURN_FAILURE(KErrCorrupt);
		if((start-codeEnd)&pointerAlignMask)
			RETURN_FAILURE(KErrCorrupt); // not aligned within 'rest of data'
		}

	// check exception descriptor...
	if(iExceptionDescriptor&1) // if valid...
		if(iExceptionDescriptor>=TUint(iCodeSize))
			RETURN_FAILURE(KErrCorrupt);

	TInt r = ValidateExportDescription();
	if(r!=KErrNone)
		RETURN_FAILURE(r);

	// done...
	aUncompressedSize = uncompressedSize;
	return KErrNone;
	}


/**
Valdate that the export description is valid.
*/
TInt E32ImageHeaderV::ValidateExportDescription() const
	{
	TUint headerSize = iCodeOffset;

	// check export description...
	TUint edSize = iExportDescSize + sizeof(iExportDescSize) + sizeof(iExportDescType);
	edSize = (edSize+3)&~3;
	TUint edEnd = _FOFF(E32ImageHeaderV,iExportDescSize)+edSize;
	if(edEnd!=headerSize)
		RETURN_FAILURE(KErrCorrupt);

	// size of bitmap of exports...
	TUint bitmapSize = (iExportDirCount+7) >> 3;

	// check export description bitmap...
	switch(iExportDescType)
		{
	case KImageHdr_ExpD_NoHoles:
		// no bitmap to check...
		E32IMAGEHEADER_TRACE(("ValidateExportDescription NoHoles"));
		return KErrNone;

	case KImageHdr_ExpD_FullBitmap:
		// full bitmap present...
		E32IMAGEHEADER_TRACE(("ValidateExportDescription FullBitmap"));
		if(bitmapSize!=iExportDescSize)
			RETURN_FAILURE(KErrCorrupt);
		return KErrNone;

	case KImageHdr_ExpD_SparseBitmap8:
		{
		// sparse bitmap present...
		E32IMAGEHEADER_TRACE(("ValidateExportDescription SparseBitmap8"));

		// get size of meta-bitmap...
		TUint metaBitmapSize = (bitmapSize+7) >> 3;
		if(metaBitmapSize>iExportDescSize)
			RETURN_FAILURE(KErrCorrupt); // doesn't fit

		TUint totalSize = metaBitmapSize;

		// scan meta-bitmap counting extra bytes which should be present...
		const TUint8* metaBitmap = iExportDesc;
		const TUint8* metaBitmapEnd = metaBitmap + metaBitmapSize;
		while(metaBitmap<metaBitmapEnd)
			{
			TUint bits = *metaBitmap++;
			do
				{
				if(bits&1)
					++totalSize; // another byte is present in bitmap
				}
			while(bits>>=1);
			}

		if(totalSize!=iExportDescSize)
			RETURN_FAILURE(KErrCorrupt);
		}
		return KErrNone;

	default:
		E32IMAGEHEADER_TRACE(("ValidateExportDescription ?"));
		RETURN_FAILURE(KErrNotSupported);
		}
	}


/**
Validate a relocation section.

@param aBufferStart				Start of buffer containing the data after the code part in the image file.
@param aBufferSize				Size of data at aBufferStart.
@param aRelocationInfoOffset	File offset for relocation section. (#iCodeRelocOffset or #iDataRelocOffset.)
@param aRelocatedSectionSize	Size of section being relocated. (#iCodeSize or #iDataSize.)
@param[out] aRelocationSection	Set to the start of the relocation section in the given buffer.

@return KErrNone if relocation section is valid, else KErrCorrupt.
*/
TInt E32ImageHeaderV::ValidateRelocations(TAny* aBufferStart, TUint aBufferSize, TUint aRelocationInfoOffset, TUint aRelocatedSectionSize, E32RelocSection*& aRelocationSection) const
	{
	aRelocationSection = 0;
	if(!aRelocationInfoOffset)
		return KErrNone; // no relocations

	// get alignment requirements...
	TCpu cpu = (TCpu)iCpuIdentifier;
	TBool isARM = (cpu==ECpuArmV4 || cpu==ECpuArmV5 || cpu==ECpuArmV6);
	TUint32 pointerAlignMask = isARM ? 3 : 0;	// mask of bits which must be zero for aligned pointers/offsets

	// buffer pointer to read relocation from...
	TUint8* bufferStart = (TUint8*)aBufferStart;
	TUint8* bufferEnd = bufferStart+aBufferSize;
	TUint baseOffset = iCodeOffset+iCodeSize; // file offset for aBufferStart
	TUint8* sectionStart = (bufferStart+aRelocationInfoOffset-baseOffset);
	TUint8* p = sectionStart;

	// read section header (ValidateHeader has alread checked this is OK)...
	E32RelocSection* sectionHeader = (E32RelocSection*)p;
	TUint size = sectionHeader->iSize;
	TUint relocsRemaining = sectionHeader->iNumberOfRelocs;
	E32IMAGEHEADER_TRACE(("E32RelocSection 0x%x %d",size,relocsRemaining));
	if(size&3)
		RETURN_FAILURE(KErrCorrupt); // not multiple of word size

	// calculate buffer range for block data...
	p = (TUint8*)(sectionHeader+1);  // start of first block
	TUint8* sectionEnd = p+size;
	if(sectionEnd<p)
		RETURN_FAILURE(KErrCorrupt); // math overflow
	if(sectionEnd>bufferEnd)
		RETURN_FAILURE(KErrCorrupt); // overflows buffer

	// process each block...
	while(p!=sectionEnd)
		{
		E32RelocBlock* block = (E32RelocBlock*)p;

		// get address of first entry in this block...
		TUint16* entryPtr = (TUint16*)(block+1);
		if((TUint8*)entryPtr<(TUint8*)block || (TUint8*)entryPtr>sectionEnd)
			RETURN_FAILURE(KErrCorrupt);  // overflows relocation section

		// read block header...
		TUint pageOffset = block->iPageOffset;
		TUint blockSize = block->iBlockSize;
		E32IMAGEHEADER_TRACE(("E32RelocSection block 0x%x 0x%x",pageOffset,blockSize));
		if(pageOffset&0xfff)
			RETURN_FAILURE(KErrCorrupt); // not page aligned
		if(blockSize<sizeof(E32RelocBlock))
			RETURN_FAILURE(KErrCorrupt); // blockSize must be at least that of the header just read
		if(blockSize&3)
			RETURN_FAILURE(KErrCorrupt); // not word aligned

		// caculate end of entries in this block...
		TUint16* entryEnd = (TUint16*)(p+blockSize);
		if(entryEnd<entryPtr)
			RETURN_FAILURE(KErrCorrupt); // math overflow
		if(entryEnd>(TUint16*)sectionEnd)
			RETURN_FAILURE(KErrCorrupt); // overflows relocation section

		// process each entry in this block...
		while(entryPtr<entryEnd)
			{
			TUint entry = *entryPtr++;
			E32IMAGEHEADER_TRACE(("E32RelocSection entry 0x%04x",entry));
			if(!entry)
				continue;

			// check relocation type...
			TUint entryType = entry&0xf000;
			if(entryType!=KTextRelocType && entryType!=KDataRelocType && entryType!=KInferredRelocType)
				RETURN_FAILURE(KErrCorrupt);

			// check relocation is within section being relocated...
			TUint offset = pageOffset+(entry&0x0fff);
			if(offset>=aRelocatedSectionSize || offset+4>aRelocatedSectionSize)
				RETURN_FAILURE(KErrCorrupt); // not within section
			if(offset&pointerAlignMask)
				RETURN_FAILURE(KErrCorrupt); // not aligned correctly

			// count each relocation processed...
			--relocsRemaining;
			}

		// next sub block...
		p = (TUint8*)entryEnd;
		}

	// check number of relocations in section header is correct...
	E32IMAGEHEADER_TRACE(("E32RelocSection relocsRemaining=%d",relocsRemaining));
	if(relocsRemaining)
		RETURN_FAILURE(KErrCorrupt); // incorrect number of entries

	aRelocationSection = sectionHeader;
	return KErrNone;
	}


/**
Validate an import section.

For PE format imports, this also verifies that the Import Address Table fits within the code
part of the image.

@param aBufferStart				Start of buffer containing the data after the code part in the image file.
@param aBufferSize				Size of data at aBufferStart.
@param[out] aBiggestImportCount	Largest number of imports the image has from any single dependency.

@return KErrNone if section is valid (or absent), else KErrCorrupt.
*/
TInt E32ImageHeaderV::ValidateImports(TAny* aBufferStart, TUint aBufferSize, TUint& aBiggestImportCount) const
	{
	if(!iImportOffset)
		{
		aBiggestImportCount = 0;
		return KErrNone; // no imports
		}

	// get alignment requirements...
	TCpu cpu = (TCpu)iCpuIdentifier;
	TBool isARM = (cpu==ECpuArmV4 || cpu==ECpuArmV5 || cpu==ECpuArmV6);
	TUint32 pointerAlignMask = isARM ? 3 : 0;	// mask of bits which must be zero for aligned pointers/offsets

	// buffer pointer to read imports from...
	TUint8* bufferStart = (TUint8*)aBufferStart;
	TUint8* bufferEnd = bufferStart+aBufferSize;
	TUint baseOffset = iCodeOffset+iCodeSize; // file offset for aBufferStart
	TUint8* sectionStart = (bufferStart+iImportOffset-baseOffset);
	TUint8* p = sectionStart;

	// read section header (ValidateHeader has alread checked this is OK)...
	E32ImportSection* sectionHeader = (E32ImportSection*)p;
	TUint size = sectionHeader->iSize;
	E32IMAGEHEADER_TRACE(("E32ImportSection 0x%x",size));

	// check section lies within buffer...
	p = (TUint8*)(sectionHeader+1);  // start of first import block
	TUint8* sectionEnd = sectionStart+size;
	if(sectionEnd<p)
		RETURN_FAILURE(KErrCorrupt); // math overflow or not big enough to contain header
	if(sectionEnd>bufferEnd)
		RETURN_FAILURE(KErrCorrupt); // overflows buffer

	// process each import block...
	TUint numDeps = iDllRefTableCount;
	TUint biggestImportCount = 0;
	TUint totalImports = 0;
	TUint importFormat = iFlags&KImageImpFmtMask;
	while(numDeps--)
		{
		// get block header...
		E32ImportBlock* block = (E32ImportBlock*)p;
		p = (TUint8*)(block+1);
		if(p<(TUint8*)block || p>sectionEnd)
			RETURN_FAILURE(KErrCorrupt); // overflows buffer

		E32IMAGEHEADER_TRACE(("E32ImportBlock 0x%x %d",block->iOffsetOfDllName,block->iNumberOfImports));

		// check import dll name is within section...
		TUint8* name = sectionStart+block->iOffsetOfDllName;
		if(name<sectionStart || name>=sectionEnd)
			RETURN_FAILURE(KErrCorrupt); // not within import section
		while(*name++ && name<sectionEnd)
			{}
		if(name[-1])
			RETURN_FAILURE(KErrCorrupt); // name overflows section
		E32IMAGEHEADER_TRACE(("E32ImportBlock %s",sectionStart+block->iOffsetOfDllName));

		// process import count...
		TUint numberOfImports = block->iNumberOfImports;
		if(numberOfImports>=0x80000000u/sizeof(TUint32))
			RETURN_FAILURE(KErrCorrupt); // size doesn't fit into a signed integer
		if(numberOfImports>biggestImportCount)
			biggestImportCount = numberOfImports;
		totalImports += numberOfImports;

		// process import data...

		// PE2 doesn't have any more data...
		if(importFormat==KImageImpFmt_PE2)
			continue;

		// get import data range...
		TUint32* imports = (TUint32*)p;
		TUint32* importsEnd = imports+numberOfImports;
		if(importsEnd<imports)
			RETURN_FAILURE(KErrCorrupt); // math overflow. Fuzzer can't trigger this because needs aBufferStart to be in to be >0x80000000
		if(importsEnd>(TUint32*)sectionEnd)
			RETURN_FAILURE(KErrCorrupt); // overflows buffer

		// move pointer on to next block...
		p = (TUint8*)importsEnd;

		if(importFormat==KImageImpFmt_ELF)
			{
			// check imports are in code section...
			TUint32 limit = iCodeSize-sizeof(TUint32);
			while(imports<importsEnd)
				{
				TUint32 i = *imports++;
				if(i>limit)
					RETURN_FAILURE(KErrCorrupt);
				if(i&pointerAlignMask)
					RETURN_FAILURE(KErrCorrupt); // not word aligned
				}
			}
		else if(importFormat==KImageImpFmt_PE)
			{
			// import data is not used, so don't bother checking it
			}
		else
			{
			RETURN_FAILURE(KErrCorrupt); // bad import format, Fuzzer can't trigger this because import format checked by header validation
			}

		// next block...
		p = (TUint8*)block->NextBlock(importFormat);
		}

	// done processing imports; for PE derived files now check import address table (IAT)...
	if(importFormat==KImageImpFmt_PE || importFormat==KImageImpFmt_PE2)
		{
		if(totalImports>=0x80000000u/sizeof(TUint32))
			RETURN_FAILURE(KErrCorrupt); // size doesn't fit into a signed integer
		TUint importAddressTable = iTextSize; // offset for IAT
		if(importAddressTable&pointerAlignMask)
			RETURN_FAILURE(KErrCorrupt); // Fuzzer can't trigger this because PE imports are for X86 which doesn't have alignment restrictions
		TUint importAddressTableEnd = importAddressTable+sizeof(TUint32)*totalImports;
		if(importAddressTableEnd<importAddressTable || importAddressTableEnd>TUint(iCodeSize))
			RETURN_FAILURE(KErrCorrupt); // import address table overflows code part of file
		E32IMAGEHEADER_TRACE(("E32ImportSection IAT offsets 0x%x..0x%x",importAddressTable,importAddressTableEnd));
		}

	aBiggestImportCount = biggestImportCount;
	return KErrNone;
	}




/**
Validate a whole executable image.

This runs all of the other validation methods in turn.

@param aBufferStart	Start of buffer containing the data after the header part of an image file.
@param aBufferSize	Size of data at aBufferStart.

@return KErrNone if image is valid, else KErrCorrupt or KErrNotSupported.
*/
TInt E32ImageHeaderV::ValidateWholeImage(TAny* aBufferStart, TUint aBufferSize) const
	{
	TUint32 dummyUncompressedSize;
	TInt r = ValidateHeader(TotalSize()+aBufferSize,dummyUncompressedSize);
	if(r!=KErrNone)
		return r;

	TInt endOfCodeOffset = iCodeSize;
	void* restOfFileData = ((TUint8*)aBufferStart)+endOfCodeOffset;
	TInt restOfFileSize = aBufferSize-endOfCodeOffset;

	E32RelocSection* dummy;
	r = ValidateRelocations(restOfFileData,restOfFileSize,iCodeRelocOffset,iCodeSize,dummy);
	if(r!=KErrNone)
		return r;
	r = ValidateRelocations(restOfFileData,restOfFileSize,iDataRelocOffset,iDataSize,dummy);
	if(r!=KErrNone)
		return r;

	TUint biggestImportCount; 
	r = ValidateImports(restOfFileData,restOfFileSize,biggestImportCount);
	if(r!=KErrNone)
		return r;

	return r;
	}


#endif	// INCLUDE_E32IMAGEHEADER_IMPLEMENTATION


#endif	// __F32IMAGE_H__
