diff -r 000000000000 -r 96e5fb8b040d userlibandfileserver/fileserver/inc/f32image.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/userlibandfileserver/fileserver/inc/f32image.h Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,1248 @@ +// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// f32\inc\f32image.h +// +// WARNING: This file contains some APIs which are internal and are subject +// to change without notice. Such APIs should therefore not be used +// outside the Kernel and Hardware Services package. +// + +/** + @file f32\inc\f32image.h + @internalTechnology +*/ + +#ifndef __F32IMAGE_H__ +#define __F32IMAGE_H__ +#include + +/** +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 KImageCodeUnpaged = 0x00000100u; ///< Executable image should not be demand paged. Exclusive with KImageCodePaged, +const TUint KImageCodePaged = 0x00000200u; ///< Executable image should be demand paged. Exclusive with KImageCodeUnpaged, + +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 KImageDataUnpaged = 0x00001000u; ///< Flag to indicate the image should not be data paged. Exclusive with KImageDataPaged. +const TUint KImageDataPaged = 0x00002000u; ///< Flag to indicate the image should be data paged. Exclusive with KImageDataUnpaged. +const TUint KImageDataPagingMask = KImageDataUnpaged | KImageDataPaged; ///< Mask for data paging flags. + +const TUint KImageSMPSafe = 0x00004000u; ///< Flag to indicate image is SMP safe + +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 + + +/** +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)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(uncompressedSizeKMaxDesSize) + 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=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(codeEnduncompressedSize) + RETURN_FAILURE(KErrCorrupt); + + // check iDataOffset and iDataSize specify region in file... + TUint dataStart = iDataOffset; + TUint dataEnd = dataStart+iDataSize; + if(dataEnduncompressedSize) + 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(exportsEndcodeEnd) + 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(enduncompressedSize) + 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(enduncompressedSize) + 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(enduncompressedSize) + 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>=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(sectionEndbufferEnd) + 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(TUint16*)sectionEnd) + RETURN_FAILURE(KErrCorrupt); // overflows relocation section + + // process each entry in this block... + while(entryPtr=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(sectionEndbufferEnd) + 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=sectionEnd) + RETURN_FAILURE(KErrCorrupt); // not within import section + while(*name++ && nameiOffsetOfDllName)); + + // 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(importsEnd0x80000000 + 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(importslimit) + 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(importAddressTableEndTUint(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__ + +