kerneltest/f32test/loader/t_ldrtst.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
child 43 c1f20ce4abcf
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 1999-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:
// f32test\loader\t_ldrtst.cpp
// 
//

#define __E32TEST_EXTENSION__

#include "t_hash.h"
#include "t_ldrtst.h"
#include "../../../e32test/mmu/d_memorytest.h"

#if defined(__WINS__)
	#include <e32wins.h>
	#include <emulator.h>
#elif defined(__EPOC32__)
	#include <f32image.h>
#endif

const TInt KNumberOfCorruptFiles = 2;

RTest test(_L("T_LDRTST"));

LoaderTest* TheLoaderTest;
RFs Fs;
#if defined (__X86__) || defined(__WINS__)
TBool NoRemovable=ETrue;
#else
TBool NoRemovable=EFalse;
#endif

/** Error code of simulated RFs error */
const TInt KRFsError = -99;

/**
	Number of drives which are identified by a numeric value,
	which means, e.g., run from an internal pageable drive.
 */
const TInt KSpecialDriveCount = 2;

/** The real drive letters corresponding to each special drive. */
static TFixedArray<TText, KSpecialDriveCount> SpecialDrives;

/** Bitmask of paged and unpaged module flags. */
const TUint32 KModulePagedCodeFlags = (KModuleFlagPagedCode | KModuleFlagUnpagedCode);

_LIT(KSysHash,"?:\\Sys\\Hash\\");

TInt GetModuleFlags(TInt aModule)
	{
	TInt f = ModuleFlags[aModule];
#ifdef __WINS__
	// paged and unpaged flags are not supported on the emulator
	f &= ~KModulePagedCodeFlags;
	// On emulator, treat all modules as XIP, all EXEs as fixed
	f |= KModuleFlagXIP;
	if (f & KModuleFlagExe)
		f|=KModuleFlagFixed;
#endif	// #ifdef __EPOC32__
	return f;
	}

TModuleSet::TModuleSet()
	{
	Mem::FillZ(this,sizeof(TModuleSet));
	}

void TModuleSet::Add(TInt aModule)
	{
	TUint8 m=(TUint8)(1<<(aModule&7));
	TInt i=aModule>>3;
	if (!(iBitMap[i]&m))
		{
		iBitMap[i]|=m;
		++iCount;
		}
	}

void TModuleSet::Remove(TInt aModule)
	{
	TUint8 m=(TUint8)(1<<(aModule&7));
	TInt i=aModule>>3;
	if (iBitMap[i]&m)
		{
		iBitMap[i]&=~m;
		--iCount;
		}
	}

TModuleSet::TModuleSet(const TModuleList& aList, TInt aMask, TInt aVal)
	{
	Mem::FillZ(this,sizeof(TModuleSet));
	TInt i;
	for (i=0; i<aList.iCount; ++i)
		{
		TInt m=aList.iInfo[i].iDllNum;
		if (((GetModuleFlags(m)&aMask)^aVal)==0)
			Add(aList.iInfo[i].iDllNum);
		}
	}

void TModuleSet::Remove(const TModuleList& aList)
	{
	TInt i;
	for (i=0; i<aList.iCount; ++i)
		Remove(aList.iInfo[i].iDllNum);
	}

void TModuleSet::Display(const TDesC& aTitle) const
	{
	TBuf<256> s=aTitle;
	TInt i;
	for (i=0; i<iCount; ++i)
		{
		if (Present(i))
			s.AppendFormat(_L("%3d "),i);
		}
	test.Printf(_L("%S\n"),&s);
	}

TModuleList::TModuleList()
	{
	iCount=0;
	Mem::Fill(iInfo, KNumModules*sizeof(SDllInfo), 0xff);
	}

void TModuleList::SetCount()
	{
	TInt i;
	for (i=0; i<KNumModules && iInfo[i].iDllNum>=0; ++i) {}
	iCount=i;
	}

void TModuleList::Display(const TDesC& aTitle) const
	{
	TBuf<256> s=aTitle;
	TInt i;
	for (i=0; i<iCount; ++i)
		{
		TInt modnum=iInfo[i].iDllNum;
		s.AppendFormat(_L("%3d "),modnum);
		}
	test.Printf(_L("%S\n"),&s);
	}

TBool TModuleList::IsPresent(TInt aModNum) const
	{
	return Find(aModNum)>=0;
	}

TInt TModuleList::Find(TInt aModNum) const
	{
	TInt i;
	for (i=iCount-1; i>=0 && iInfo[i].iDllNum!=aModNum; --i) {}
	return i;
	}

void TModuleList::Add(const SDllInfo& a)
	{
	iInfo[iCount++]=a;
	}


RMemoryTestLdd TestLdd;

TBool AddressReadable(TLinAddr a)
	{
	TUint32 value;
	return TestLdd.ReadMemory((TAny*)a,value)==KErrNone;
	}

TInt LoaderTest::DetermineDllLoadResult(TInt aDllNum, TInt aExeNum)
	{
	TBool proc_sym=(iMemModelAtt & (EMemModelAttrSameVA|EMemModelAttrSupportFixed))==EMemModelAttrSameVA;
	const TInt* exeinfo=ModuleExeInfo[aDllNum];
	TInt attp=exeinfo[0];
	TInt linkexe=exeinfo[1];
	TInt dllflags=GetModuleFlags(aDllNum);
	TInt exeflags=GetModuleFlags(aExeNum);

#ifdef __EPOC32__
	// if NP and DEFAULTPAGED or DEFAULTUNPAGED (not NOPAGING or ALWAYSPAGE) then
	// executable identified as corrupt, unless previous conditions in S3.1.3.2 cause
	// it to be paged or unpaged without examining the flags.

	TUint32 policy = E32Loader::PagingPolicy();
	test.Printf(_L("DetermineDllLoadResult,dll=%d,exe=%d,dllflags=0x%x,policy=0x%x\n"), aDllNum, aExeNum, dllflags, policy);

	TBool flagsChecked =
			policy != EKernelConfigCodePagingPolicyNoPaging					// 3.1.3.2.1, policy != no paging
		&&	(dllflags & KModuleFlagIDrive) != 0							// 3.1.3.2.2-3, pageable drive
		&&	(dllflags & (KModuleFlagUncompressed | KModuleFlagBytePair)) != 0	// 3.1.3.2.4 pageable format
		&&	policy != EKernelConfigCodePagingPolicyAlwaysPage;				// 3.1.3.2.5, policy != ALWAYS
	
	if (flagsChecked && (dllflags & KModulePagedCodeFlags) == KModulePagedCodeFlags)
		{
		TBool codePolDefUnpaged = (policy == EKernelConfigCodePagingPolicyDefaultUnpaged);
		TBool codePolDefPaged = (policy == EKernelConfigCodePagingPolicyDefaultPaged);
		if (codePolDefPaged || codePolDefUnpaged)
			return KErrCorrupt;
		}
#endif

	if (linkexe>=0 && linkexe!=aExeNum)
		return KErrNotSupported;	// if DLL links to a different EXE, no good
	if (!(dllflags&KModuleFlagDataInTree))
		return KErrNone;			// if no data in DLL tree, OK
	if (proc_sym)
		return KErrNone;			// if all user processes equivalent, OK
	if (!(dllflags&KModuleFlagXIPDataInTree))
		return KErrNone;			// if no XIP modules with data in DLL tree, OK

#ifdef __EPOC32__
	if (attp<0 || !(GetModuleFlags(attp)&KModuleFlagFixed))
		{
		// moving processes only
		if (!(exeflags&KModuleFlagFixed))
			return KErrNone;
		return KErrNotSupported;
		}
	// fixed attach process only
	if (aExeNum!=attp)
		return KErrNotSupported;
#else
	(void)attp;
	(void)exeflags;
#endif
	return KErrNone;
	}

TInt LoaderTest::DetermineDllLoadResult(TInt aDllNum, TInt aExeNum1, TInt aExeNum2)
	{
	// Determine result of loading aDllNum into aExeNum2 given that it's already loaded into aExeNum1
	// return KErrNone if code segment can be shared, 1 if it must be duplicated
	
	TBool proc_sym=(iMemModelAtt & (EMemModelAttrSameVA|EMemModelAttrSupportFixed))==EMemModelAttrSameVA;
	const TInt* exeinfo=ModuleExeInfo[aDllNum];
//	TInt attp=exeinfo[0];
	TInt linkexe=exeinfo[1];
	TInt dllflags=GetModuleFlags(aDllNum);
	TInt exe1flags=GetModuleFlags(aExeNum1);
	TInt exe2flags=GetModuleFlags(aExeNum2);
	if (linkexe>=0 && linkexe!=aExeNum2)
		return KErrNotSupported;	// if DLL links to a different EXE, no good
	if (!(dllflags&KModuleFlagDataInTree))
		return KErrNone;			// if no data in DLL tree, OK
	if (proc_sym)
		return KErrNone;			// if all user processes equivalent, OK
	if (!((exe1flags|exe2flags)&KModuleFlagFixed))
		return KErrNone;			// if neither process fixed, OK
	if (!(dllflags&KModuleFlagXIPDataInTree))
		return 1;					// if no XIP modules with data in DLL tree, OK but can't share
#ifdef __WINS__
	return KErrNone;
#else
	return KErrNotSupported;
#endif
	}

TBool LoaderTest::IsRomAddress(TLinAddr a)
	{
	const TRomHeader& rh=*(const TRomHeader*)UserSvr::RomHeaderAddress();
	return (a>=rh.iRomBase && (a-rh.iRomBase)<rh.iRomSize);
	}

TBool LoaderTest::IsRamCodeAddress(TLinAddr a)
	{
	switch (iMemModelAtt & EMemModelTypeMask)
		{
		case EMemModelTypeDirect:
			return ETrue;
		case EMemModelTypeMoving:
			return (a>=0xc0000000u);
		case EMemModelTypeMultiple:
			return (a<0x80000000u);
		case EMemModelTypeFlexible:
			return (a<0x80000000u);
		case EMemModelTypeEmul:
			return (a<0x80000000u);
		default:
			return EFalse;
		}
	}

TBool LoaderTest::CheckDataAddress(TLinAddr a, TInt aDllNum, TInt aExeNum)
	{
	TInt xf=GetModuleFlags(aExeNum);
	TInt df=GetModuleFlags(aDllNum);
	switch (iMemModelAtt & EMemModelTypeMask)
		{
		case EMemModelTypeDirect:
			return ETrue;
		case EMemModelTypeMoving:
			{
			const TRomHeader& rh=*(const TRomHeader*)UserSvr::RomHeaderAddress();
			if (!(xf&KModuleFlagFixed))
				return (a<0x40000000u);
			if ((xf&KModuleFlagXIP) && (df&KModuleFlagXIP))
				return (a>=rh.iKernDataAddress && a<rh.iKernelLimit);
			return (a>=rh.iKernelLimit && a<0xc0000000u);
			}
		case EMemModelTypeMultiple:
			return (a<0x80000000u);
		case EMemModelTypeFlexible:
			return (a<0x80000000u);
		case EMemModelTypeEmul:
			return (a<0x80000000u);
		default:
			return EFalse;
		}
	}

void LoaderTest::DumpModuleInfo(const SDllInfo& aInfo, TInt aExeNum)
	{
	TInt flags=GetModuleFlags(aInfo.iDllNum);
	TUint32 mmtype=iMemModelAtt & EMemModelTypeMask;
	TAny* h=iDev.ModuleCodeSeg(aInfo.iModuleHandle);
	if (!h)
		{
#ifdef __EPOC32__
		test(flags & KModuleFlagXIP);
		test(IsRomAddress(aInfo.iEntryPointAddress));
		test.Printf(_L("Module handle %08x ROM XIP\n"),aInfo.iModuleHandle);
#endif
		test(!(flags & KModuleFlagData));
		return;
		}
	TCodeSegCreateInfo info;
	TInt r=iDev.GetCodeSegInfo(h, info);
	test(r==KErrNone);
	TFileName fn;
	fn.Copy(info.iFileName);
	test.Printf(_L("DCodeSeg@%08x Data=%08x+%x,%x File %S,attr=0x%x\n"),h,info.iDataRunAddress,info.iDataSize,info.iBssSize,&fn,info.iAttr);
	TInt total_data_size=info.iDataSize+info.iBssSize;
#ifndef __WINS__
	// Don't do check below for WINS because:
	// a. It doesn't work on code warrior since it puts constants into .data
	// b. On MSCV with c++ exceptions enabled we also get data
	if (flags & KModuleFlagData)
		test(total_data_size!=0);
	else
		test(total_data_size==0);

	// ensure code paged iff expected.  This implements the logic from
	// PREQ1110 Design Sketch SGL.TS0022.008 v1.0 S3.1.3.2

	TUint policy = E32Loader::PagingPolicy();

	TBool expected;
	TBool isCodePaged = (info.iAttr & ECodeSegAttCodePaged)!=0;

	// 1. If paging policy is NOPAGING then executable is Unpaged.
	TUint32 memModelAttributes=UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, NULL, NULL);
	if (policy == EKernelConfigCodePagingPolicyNoPaging || !(memModelAttributes&EMemModelAttrCodePaging))
		{
		test.Printf(_L("sbcpexp,1\n"));
		expected = false;
		}
	// 2. If ... media ... doesn't have Pageable Media Attribute then it is Unpaged.
	// (this has been superseded by BlockMap check on filesystem / media.  During these
	// tests, only the internal media supports paging.)
	else if ((flags & KModuleFlagIDrive) == 0)
		{
		test.Printf(_L("sbcpexp,2\n"));
		expected = false;
		}
	// 3. If ... removable media then it is Unpaged.
	// Not tested here because removable media (drive 1) covered by above case.
//	else if (MODULE_FILENAME(aInfo.iDllNum)[0] == '1')
//		{
//		test.Printf(_L("sbcpexp,2\n"));
//		expected = false;
//		}
	// 4. [If not bytepair [or uncompressed]] then Unpaged
	else if ((flags & (KModuleFlagBytePair | KModuleFlagUncompressed)) == 0)
		{
		test.Printf(_L("sbcpexp,3\n"));
		expected = false;
		}
	// 5. If the Paging Policy is ALWAYSPAGE then the executable is Paged.
	else if (policy == EKernelConfigCodePagingPolicyAlwaysPage)
		{
		test.Printf(_L("sbcpexp,4\n"));
		expected = true;
		}
	// 6. if KImageCodePaged and KImageCodePaged both set, should not reach here
	//	because load will have failed with KErrCorrupt.  If Paged on its own
	//	then paged; if unpaged on its own then unpaged
	else if ((flags & KModuleFlagPagedCode) != 0)
		{
		test.Printf(_L("sbcpexp,5\n"));
		expected = true;
		}
	else if ((flags & KModuleFlagUnpagedCode) != 0)
		{
		test.Printf(_L("sbcpexp,6\n"));
		expected = false;
		}
	// 7. Otherwise the PagingPolicy (DEFAULTPAGED or DEFAULTUNPAGED) determines
	//	how the executable is treated
	else
		{
		test.Printf(_L("sbcpexp,7\n"));
		expected = (policy == EKernelConfigCodePagingPolicyDefaultPaged);
		}

	test(expected == isCodePaged);
#endif
	if ((flags & KModuleFlagXIP) && mmtype!=EMemModelTypeEmul)
		test(IsRomAddress(aInfo.iEntryPointAddress));
	else
		{
		test(IsRamCodeAddress(aInfo.iEntryPointAddress));
		if(mmtype==EMemModelTypeFlexible)
			{
			// can't make assumtions about current processes address space
			}
		else if (mmtype==EMemModelTypeMultiple)
			{
			test(!AddressReadable(aInfo.iEntryPointAddress));
			}
		else
			{
			test(AddressReadable(aInfo.iEntryPointAddress));
			}
		}

	if (total_data_size!=0)
		test(CheckDataAddress(info.iDataRunAddress, aInfo.iDllNum, aExeNum));
	}

void LoaderTest::DumpModuleList(const TModuleList& aList, TInt aExeNum)
	{
	TInt i;
	for (i=0; i<aList.iCount; ++i)
		{
		TInt modnum=aList.iInfo[i].iDllNum;
		TInt entry=aList.iInfo[i].iEntryPointAddress;
		test.Printf(_L("MODULE %3d: ENTRY %08x "),modnum,entry);
		DumpModuleInfo(aList.iInfo[i],aExeNum);
		}
	}

void LoaderTest::CheckModuleList(TInt aRoot, const TModuleList& aList)
	{
	const TInt* deps=ModuleDependencies[aRoot];
	TInt ndeps=*deps++;
	TInt f=0;
	TInt i;
	for (i=0; i<ndeps; ++i)
		{
		TInt m=deps[i];
		f|=GetModuleFlags(m);
		}
	if (!(f&KModuleFlagDllInCycle))
		{
		i=0;		// indexes aList
		TInt j=0;	// indexes deps
		while(i<KNumModules)
			{
			if (j<ndeps)
				{
				if (!(GetModuleFlags(deps[j])&KModuleFlagExe))
					{
					test(aList.iInfo[i].iDllNum==deps[j]);
					++i;
					}
				++j;
				}
			else if (j==ndeps)
				{
				test(aList.iInfo[i].iDllNum==aRoot);
				++i;
				++j;
				}
			else
				{
				test(aList.iInfo[i].iDllNum<0);
				++i;
				}
			}
		}
	TModuleSet ml;
	TInt nd=ndeps;
	TBool root_included=EFalse;
	for (i=0; i<ndeps; ++i)
		{
		if (deps[i]==aRoot)
			root_included=ETrue;
		if (!(GetModuleFlags(deps[i])&KModuleFlagExe))
			ml.Add(deps[i]);
		else
			--nd;
		}
	test(ml.iCount==nd);
	for (i=0; i<KNumModules; ++i)
		{
		if (i<nd)
			{
			test(aList.iInfo[i].iDllNum>=0);
			ml.Remove(aList.iInfo[i].iDllNum);
			}
		else if (i==nd && !root_included)
			test(aList.iInfo[i].iDllNum==aRoot);
		else
			test(aList.iInfo[i].iDllNum<0);
		}
	test(ml.iCount==0);
	}

LoaderTest::LoaderTest()
	{
	Mem::Fill(iCmdLine, sizeof(iCmdLine), 0xff);
	iMemModelAtt=(TUint32)UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, NULL, NULL);
	test.Printf(_L("MemModelAttributes=%08x\n"),iMemModelAtt);
	}

LoaderTest::~LoaderTest()
	{
	iFs.Close();
	iDev.Close();
	}

void LoaderTest::Init()
	{
	test.Next(_L("Load device driver"));
	TInt r=User::LoadLogicalDevice(_L("D_LDRTST"));
	test(r==KErrNone || r==KErrAlreadyExists);
	r=iDev.Open();
	test(r==KErrNone);
	r=iFs.Connect();
	test(r==KErrNone);

	TBuf<256> cmdline;
	User::CommandLine(cmdline);
	TLex lex(cmdline);
	TInt i;
	for (i=0; i<8; ++i)
		{
		lex.SkipSpace();
		if (lex.Eos())
			break;
		lex.Val(iCmdLine[i]);
		}
	}

LoaderTest* LoaderTest::New()
	{
	LoaderTest* p=new LoaderTest;
	test(p!=NULL);
	p->Init();
	return p;
	}

void LoaderTest::Close()
	{
	delete this;
	}

void LoaderTest::TraceOn()
	{
	iFs.SetDebugRegister(KFLDR);
	User::SetDebugMask(0xefdfffff);
	}

void LoaderTest::TraceOff()
	{
	iFs.SetDebugRegister(0);
	User::SetDebugMask(0x80000000);
	}

void LoaderTest::TestOneByOne()
	{
	test.Next(_L("Test all single EXE/DLL combinations"));
	TInt i=0;
	TInt r=0;
	TInt x=0;
	for (x=0; x<KNumModules; ++x)
		{
		if (!(GetModuleFlags(x)&KModuleFlagExe))
			continue;
#ifdef __WINS__
		if (GetModuleFlags(x)&KModuleFlagTargetOnly)
			continue;
#endif
		RProcess p;
		TUint32 tt;
		r=LoadExe(x, 0, p, tt);
		test.Printf(_L("LoadExe(%d)->%d\n"),x,r);
		test.Printf(_L("BENCHMARK: LoadExe(%d)->%dms\n"),x,tt);
		test(r==KErrNone);
		RLoaderTest lt;
		r=lt.Connect(x);
		test.Printf(_L("Connect(%d)->%d\n"),x,r);
		test(r==KErrNone);
		TModuleList exe_info;
		r=lt.GetExeDepList(exe_info.iInfo);
		test(r==KErrNone);
		exe_info.SetCount();
		DumpModuleList(exe_info, x);
		CheckModuleList(x, exe_info);

		TInt m;
		for (m=0; m<KNumModules; ++m)
			{
			if (GetModuleFlags(m)&KModuleFlagExe)
				continue;
#ifdef __WINS__
			if (GetModuleFlags(m)&KModuleFlagTargetOnly)
				continue;
#endif

			if ((GetModuleFlags(m) & KModuleFlagVDrive) && NoRemovable)
				{
				test.Printf(_L("LoadDll: Not testing dll %d from removable media\n"),m);
				continue;
				}
			TInt predicted=DetermineDllLoadResult(m,x);
			if (x==iCmdLine[1] && m==iCmdLine[2])
				TraceOn();
			TModuleList dll_init_info;
			TModuleList dll_c_info;
			TModuleList dll_d_info;
			TInt h=lt.LoadDll(m, dll_init_info.iInfo);
			dll_init_info.SetCount();
			test.Printf(_L("LoadDll(%d)->%d (%d)\n"),m,h,predicted);

			test(Min(h,0)==predicted);
			if (h>=0)
				{
				DumpModuleList(dll_init_info, x);
				CheckModuleList(m, dll_init_info);
				test(lt.GetCDList(dll_c_info.iInfo)==KErrNone);
				dll_c_info.SetCount();
				dll_c_info.Display(_L("Construct: "));
				if (!(GetModuleFlags(m)&KModuleFlagDllInCycle))
					{
					TInt j=0;
					for (i=0; i<dll_init_info.iCount; ++i)
						{
						TInt modnum=dll_init_info.iInfo[i].iDllNum;
						if ((GetModuleFlags(modnum)&KModuleFlagData) && !exe_info.IsPresent(modnum))
							{
							test(modnum==dll_c_info.iInfo[j].iDllNum);
							++j;
							}
						}
					test(j==dll_c_info.iCount);
					}
				else
					{
					TModuleSet ms(dll_init_info, KModuleFlagData, KModuleFlagData);
					ms.Remove(exe_info);
					test(ms.iCount==dll_c_info.iCount);
					ms.Remove(dll_c_info);
					test(ms.iCount==0);
					}
				TInt y=(7*m+59);
				r=lt.CallRBlkI(h,y);
				r-=y;
				r/=INC_BLOCK_SZ;
				test.Printf(_L("DLL %d RBlkI->%d\n"),m,r);
				y=ModuleRBlkIParams[m][1]+ModuleRBlkIParams[m][0]*DLLNUMOFFSET;
				test(r==y);
				r=lt.CloseDll(h);
				test.Printf(_L("CloseDll(%d)->%d\n"),h,r);
				test(r==KErrNone);
				test(lt.GetCDList(dll_d_info.iInfo)==KErrNone);
				dll_d_info.SetCount();
				dll_d_info.Display(_L("Destruct:  "));
				test(dll_d_info.iCount==dll_c_info.iCount);
				for (i=0; i<dll_d_info.iCount; ++i)
					test(dll_d_info.iInfo[i].iDllNum==dll_c_info.iInfo[dll_c_info.iCount-i-1].iDllNum);
				}
			if (x==iCmdLine[1] && m==iCmdLine[2])
				TraceOff();
			}
		lt.Exit();
		p.Close();
		}
	}

// return KErrNone if shared code, 1 if not shared
TInt LoaderTest::DetermineLoadExe2Result(TInt aExeNum)
	{
	if ( (iMemModelAtt&(EMemModelAttrSameVA|EMemModelAttrSupportFixed))==EMemModelAttrSameVA )
		return KErrNone;	// multiple memory model always supports multiple instances
	TUint32 f=GetModuleFlags(aExeNum);
	if (!(f&KModuleFlagFixed))
		return KErrNone;	// not fixed, so share code segment
	if (!(f&KModuleFlagDataInTree))
		{
#ifdef __EPOC32__
		return KErrNone;	// fixed but no data, so share code segment
#else
		return 1;			// on emulator, never share EXE code segments
#endif
		}
#ifdef __EPOC32__
	if (!(f&KModuleFlagXIP))
		return 1;			// fixed but not XIP, data in tree - create second code segment
	// fixed, XIP, data in tree
	return KErrAlreadyExists;
#else
	if (f & KModuleFlagExports)
		return KErrAlreadyExists;
	if (!(f & KModuleFlagData))
		return KErrNone;
	return 1;
#endif
	}

void LoaderTest::TestMultipleExeInstances()
	{
	test.Next(_L("Test multiple instantiation of EXEs"));
	TInt i=0;
	TInt r=0;
	TInt x=0;
	for (x=0; x<KNumModules; ++x)
		{
		TUint32 f=GetModuleFlags(x);
		if (!(f&KModuleFlagExe))
			continue;
#ifdef __WINS__
		if (f&KModuleFlagTargetOnly)
			continue;
#endif
		RProcess p1, p2;
		RLoaderTest lt1, lt2;
		TModuleList exe_info1;
		TModuleList exe_info2;
		TUint32 tt;
		r=LoadExe(x, 0, p1, tt);
		test.Printf(_L("LoadExe1(%d)->%d\n"),x,r);
		test.Printf(_L("BENCHMARK: LoadExe1(%d)->%dms\n"),x,tt);
		test(r==KErrNone);
		r=lt1.Connect(x, 0);
		test.Printf(_L("Connect1(%d)->%d\n"),x,r);
		test(r==KErrNone);
		TInt s=DetermineLoadExe2Result(x);
		r=LoadExe(x, 1, p2, tt);
		test.Printf(_L("LoadExe2(%d)->%d (%d)\n"),x,r,s);
		if (s==KErrNone)
			test.Printf(_L("BENCHMARK: LoadExe2(%d)->%dms\n"),x,tt);
		test(r==Min(s,0));

		if (r==KErrNone)
			{
			r=lt2.Connect(x, 1);
			test.Printf(_L("Connect2(%d)->%d\n"),x,r);
			test(r==KErrNone);
			r=lt1.GetExeDepList(exe_info1.iInfo);
			test(r==KErrNone);
			exe_info1.SetCount();
			DumpModuleList(exe_info1, x);
			r=lt2.GetExeDepList(exe_info2.iInfo);
			test(r==KErrNone);
			exe_info2.SetCount();
			DumpModuleList(exe_info2, x);

			test(exe_info1.iCount==exe_info2.iCount);
			if (s==1)
				{
				TInt nm=exe_info1.iCount;
				test(exe_info1.iInfo[nm-1].iModuleHandle!=exe_info2.iInfo[nm-1].iModuleHandle);
				}
#ifdef __WINS__
			else if((GetModuleFlags(x) & KModuleFlagData))
#else
			else
#endif
				{
				for (i=0; i<exe_info1.iCount; ++i)
					test(exe_info1.iInfo[i].iModuleHandle==exe_info2.iInfo[i].iModuleHandle);
				}

			const TInt* tests=TC_ExeLoad;
			TInt ntests=*tests++;
			while(ntests--)
				{
				TInt m=*tests++;
				TModuleList dll_init_info1;
				TModuleList dll_c_info1;
				TModuleList dll_d_info1;
				TModuleList dll_init_info2;
				TModuleList dll_c_info2;
				TModuleList dll_d_info2;
				TInt h1=lt1.LoadDll(m, dll_init_info1.iInfo);
				dll_init_info1.SetCount();
				test.Printf(_L("LoadDll1(%d)->%d\n"),m,h1);
				if (h1>=0)
					{
					DumpModuleList(dll_init_info1, x);
					CheckModuleList(m, dll_init_info1);
					test(lt1.GetCDList(dll_c_info1.iInfo)==KErrNone);
					dll_c_info1.SetCount();
					dll_c_info1.Display(_L("Construct1: "));
					TInt y=(41*m+487);
					r=lt1.CallRBlkI(h1,y);
					r-=y;
					r/=INC_BLOCK_SZ;
					test.Printf(_L("DLL1 %d RBlkI->%d\n"),m,r);
					y=ModuleRBlkIParams[m][1]+ModuleRBlkIParams[m][0]*DLLNUMOFFSET;
					test(r==y);

					TInt s=DetermineDllLoadResult(m, x, x);
					TInt h2=lt2.LoadDll(m, dll_init_info2.iInfo);
					dll_init_info2.SetCount();
					test.Printf(_L("LoadDll2(%d)->%d (%d)\n"),m,h2,s);
					test(h2==Min(s,0));
					if (h2>=0)
						{
						DumpModuleList(dll_init_info2, x);
						CheckModuleList(m, dll_init_info2);
						test(lt2.GetCDList(dll_c_info2.iInfo)==KErrNone);
						dll_c_info2.SetCount();
						dll_c_info2.Display(_L("Construct2: "));
						y=(79*m+257);
						r=lt2.CallRBlkI(h2,y);
						r-=y;
						r/=INC_BLOCK_SZ;
						test.Printf(_L("DLL2 %d RBlkI->%d\n"),m,r);
						y=ModuleRBlkIParams[m][1]+ModuleRBlkIParams[m][0]*DLLNUMOFFSET;
						test(r==y);

						test(dll_init_info1.iCount==dll_init_info2.iCount);
#ifdef __WINS__
						if (s==1 && !(ModuleFlags[m]&KModuleFlagDataInTree))
#else
						if (s==1)
#endif
							{
							TInt nm=dll_init_info1.iCount;
							test(dll_init_info1.iInfo[nm-1].iModuleHandle!=dll_init_info2.iInfo[nm-1].iModuleHandle);
							}
						else
							{
							for (i=0; i<dll_init_info1.iCount; ++i)
								test(dll_init_info1.iInfo[i].iModuleHandle==dll_init_info2.iInfo[i].iModuleHandle);
							}

						r=lt2.CloseDll(h2);
						test.Printf(_L("CloseDll2(%d)->%d\n"),h2,r);
						test(r==KErrNone);
						test(lt2.GetCDList(dll_d_info2.iInfo)==KErrNone);
						dll_d_info2.SetCount();
						dll_d_info2.Display(_L("Destruct2:  "));
						test(dll_d_info2.iCount==dll_c_info2.iCount);
						for (i=0; i<dll_d_info2.iCount; ++i)
							test(dll_d_info2.iInfo[i].iDllNum==dll_c_info2.iInfo[dll_c_info2.iCount-i-1].iDllNum);
						}

					r=lt1.CloseDll(h1);
					test.Printf(_L("CloseDll1(%d)->%d\n"),h1,r);
					test(r==KErrNone);
					test(lt1.GetCDList(dll_d_info1.iInfo)==KErrNone);
					dll_d_info1.SetCount();
					dll_d_info1.Display(_L("Destruct1:  "));
					test(dll_d_info1.iCount==dll_c_info1.iCount);
					for (i=0; i<dll_d_info1.iCount; ++i)
						test(dll_d_info1.iInfo[i].iDllNum==dll_c_info1.iInfo[dll_c_info1.iCount-i-1].iDllNum);
					}
				}

			lt2.Exit();
			p2.Close();
			}
		lt1.Exit();
		p1.Close();
		}
	}

void SetLoaderFail(TInt aLdr, TInt aKern)
	{
	test.Printf(_L("ldr=%d, kern=%d\n"),aLdr,aKern);
	RLoader l;
	test(l.Connect()==KErrNone);
	test(l.DebugFunction(ELoaderDebug_SetHeapFail, aLdr, aKern, 0)==KErrNone);
	l.Close();
	}

void SetLoaderFailRFs(TInt aError, TInt aCount)
	{
	test.Printf(_L("SetLoaderFailRFs: error=%d, count=%d\n"),aError,aCount);
	RLoader l;
	test(l.Connect()==KErrNone);
	test(l.DebugFunction(ELoaderDebug_SetRFsFail, aError, aCount, 0)==KErrNone);
	l.Close();
	}

class TLoopOOM
	{
public:
	enum OomState{EInit, EKernelHeap, EUserHeap, ERFsError};

	TLoopOOM();
	void Reset();
	TBool Iterate(TInt aResult);
public:
	TInt iLdr;
	TInt iKern;
	TInt iRFsCount;
	OomState iState;
	};

TLoopOOM::TLoopOOM()
	{
	Reset();
	}

void TLoopOOM::Reset()
	{
	iLdr = 0;
	iKern = 0;
	iRFsCount = 0;
	iState = EInit;
	}

TBool TLoopOOM::Iterate(TInt aResult)
	{
	TBool noErrors = (aResult==KErrNone||aResult==KErrNotSupported);

	test.Printf(_L("%d %d %d %d\n"), iKern,iLdr,iRFsCount,aResult);

	switch(iState)
		{

	case EInit:
		iState = EKernelHeap;
		SetLoaderFail(iLdr,++iKern);
		return ETrue;

	case EKernelHeap:
		if (noErrors)
			{
			iKern = 0;
			iLdr = 1;
			iState = EUserHeap;
			}
		else
			++iKern;

		SetLoaderFail(iLdr,iKern);
		return ETrue;
	
	case EUserHeap:
		if (noErrors)
			{
			iLdr = 0;
			iState = ERFsError;
			SetLoaderFail(0,0);
			SetLoaderFailRFs(KRFsError, ++iRFsCount);
			}
		else
			SetLoaderFail(++iLdr,iKern);
		return ETrue;
	
	case ERFsError:
		if (noErrors)
			break;
		else
			{
			SetLoaderFailRFs(KRFsError, ++iRFsCount);
			return ETrue;
			}
		}

	SetLoaderFailRFs(KErrNone, 0);
	return EFalse;
	}

void LoaderTest::TestOOM()
	{
	test.Next(_L("Test OOM Handling"));
#ifdef _DEBUG
	TInt r=0;
	TInt x=0;
	TUint32 tt;
	for (x=0; x<KNumModules; ++x)
		{
		if (!(GetModuleFlags(x)&KModuleFlagExe))
			continue;
#ifdef __WINS__
		if (GetModuleFlags(x)&KModuleFlagTargetOnly)
			continue;
#endif

		if ((GetModuleFlags(x) & KModuleFlagVDrive) && NoRemovable)
			{
			test.Printf(_L("LoaderTest::TestOOM Not testing dll %d from removable media\n"),x);
			continue;
			}
		if (x==iCmdLine[1])
			TraceOn();
		TLoopOOM loom;
		RProcess p;
		RLoaderTest lt;
		while(loom.Iterate(r))
			{
			r=LoadExe(x, 0, p, tt);
			test.Printf(_L("LoadExe(%d)->%d\n"),x,r);
			test(r==KErrNone || (loom.iState!=TLoopOOM::ERFsError && r==KErrNoMemory) || 
				(loom.iState==TLoopOOM::ERFsError && r==KRFsError));
			if (r==KErrNone)
				{
				TInt s=lt.Connect(x);
				test.Printf(_L("Connect(%d)->%d\n"),x,s);
				test(s==KErrNone);
				lt.Exit();
				p.Close();
				}
			}
		SetLoaderFail(0,0);
		r=LoadExe(x, 0, p, tt);
		test(r==KErrNone);
		r=lt.Connect(x);
		test(r==KErrNone);
		const TInt* tests=TC_DllOOM;
		TInt ntests=*tests++;
		TModuleList list;
		while(ntests--)
			{
			TInt m=*tests++;
			if ((GetModuleFlags(m) & KModuleFlagVDrive) && NoRemovable)
				{
				test.Printf(_L("LoaderTest::TestOOM Not testing dll %d from removable media\n"),m);
				continue;
				}
			loom.Reset();
			r=KErrNone;
			while(loom.Iterate(r))
				{
				TInt h=lt.LoadDll(m, list.iInfo);
				r=Min(h,0);
				test.Printf(_L("%d:LoadDll(%d)->%d\n"),x,m,h);
				
				test(r==KErrNone || r==KErrNotSupported || KErrNoMemory || 
					(loom.iState==TLoopOOM::ERFsError && r==KRFsError) );
					
				if (r==KErrNone)
					{
					TInt s=lt.CloseDll(h);
					test(s==KErrNone);
					}
				}
			}
		lt.Exit();
		p.Close();
		if (x==iCmdLine[1])
			TraceOff();
		}
#else
	test.Printf(_L("Only on DEBUG builds\n"));
#endif
	}

class RLoaderTestHandle : public RSessionBase
	{
public:
	TInt Connect();
	void TryToGetPaniced();
	};

TInt RLoaderTestHandle::Connect()
	{
	return CreateSession(_L("!Loader"),TVersion(KLoaderMajorVersionNumber,KLoaderMinorVersionNumber,KE32BuildVersionNumber));
	}

void RLoaderTestHandle::TryToGetPaniced()
	{
	_LIT(KFoo,"foo");
	TLdrInfo info;
	TPckg<TLdrInfo> infoBuf(info);
	TIpcArgs args;
	args.Set(0,(TDes8*)&infoBuf);
	args.Set(1,&KFoo);
	args.Set(2,&KFoo);
	SendReceive(ELoadLibrary, args);
	}

TInt PanicTestThread(TAny*)
	{
	RLoaderTestHandle t;
	TInt r = t.Connect();
	if (r==KErrNone) t.TryToGetPaniced();
	return r;
	}


void TestCorruptedFiles()
	{
	test.Next(_L("Test corrupted files"));

	TInt numCorruptFiles=0;
	TInt r=0;
	for (TInt x=0; x<KNumModules; ++x)
		{
		if (!(GetModuleFlags(x)&KModuleFlagExe))
			continue;

		const TPtrC fn = MODULE_FILENAME(x);
		if (fn[1] != ':')
			continue;

		if (++numCorruptFiles > KNumberOfCorruptFiles)
			break;

		RProcess p;
		TUint32 tt;
		r=LoadExe(x, 0, p, tt);
		test.Printf(_L("LoadCorruptExe(%d)->%d\n"),x,r);
		test(r==KErrCorrupt);
		}
	}

// -------- copying files to non-ROM media --------

static void GetSpecialDrives()
/**
	Work out which physical drive corresponds to each
	numeric drive in the list of filenames.  This populates
	SpecialDrives.
	
	@see SpecialDrives
 */
	{
	test.Printf(_L("NoRemovable=%d\n"),NoRemovable);

	// mark each special drive as not present
	for (TInt i = 0; i < KSpecialDriveCount; ++i)
		{
		SpecialDrives[i] = '!';
		}
	
	// cannot load binaries from emulated removable drives
#if defined (__WINS__)
	SpecialDrives[1] = 'c';			// "removable"
#endif
	
	TBuf<12> hashDir;
	hashDir = KSysHash;
	hashDir[0] = (TUint8) RFs::GetSystemDriveChar();

	TInt r = Fs.MkDirAll(hashDir);
	test(r == KErrNone || r == KErrAlreadyExists);

	for (TInt d = 0; d <= (TInt)sizeof(SpecialDriveList); ++d)
		{
		TInt dr = SpecialDriveList[d];
		TDriveInfo di;
		test.Printf(_L("Drive %d\n"), dr);
		test(Fs.Drive(di, dr) == KErrNone);
		if (di.iType == EMediaNotPresent)
			continue;
		
		TChar ch0;
		test(RFs::DriveToChar(dr, ch0) == KErrNone);
		
		TText ch = static_cast<TText>(TUint(ch0));
		
		// drive 0 == internal
		if ((di.iDriveAtt & KDriveAttInternal) && SpecialDrives[0] == '!')
			{
			SpecialDrives[0] = ch;
			if (NoRemovable)
				SpecialDrives[1] = ch;
			}
		// drive 1 == removable
		else if ((di.iDriveAtt & KDriveAttRemovable) && SpecialDrives[1] == '!' && !NoRemovable)
			SpecialDrives[1] = ch;
		else
			{
			// drive not useful so continue and don't create \sys\bin
			continue;
			}

		TFileName fn;
		fn.Append(ch);
		fn.Append(_L(":\\sys\\bin\\"));
		r = Fs.MkDirAll(fn);
		test.Printf(_L("MkDirAll %S returns %d\n"), &fn, r);
		test(r == KErrNone || r == KErrAlreadyExists);
		}
	}

void GetNonZFileName(const TDesC& aOrigName, TDes& aNonZName)
/**
	Resolve a special drive to the target drive using the mappings in
	SpecialDrives.  This is used to load non-XIP binaries from pageable media.
	
	@param	aOrigName		Fully-qualified filename with special drive number.
							E.g., "3:\\sys\\bin\\dllt45.dll".
	@param	aNonZName		Descriptor to populate with aOrigName and the transformed
							drive, e.g. "c:\\sys\\bin\\dllt45.dll".
 */
	{
	test.Printf(_L(">GetNonZFileName,\"%S\"\n"), &aOrigName);
	test(aOrigName[1] == ':');
	aNonZName.Copy(aOrigName);
	TText replaceChar = SpecialDrives[aOrigName[0] - '0'];
	test(TChar(replaceChar).IsAlpha());
	aNonZName[0] = replaceChar;
	test.Printf(_L("<GetNonZFileName,\"%S\"\n"), &aNonZName);
	}

static void GetHashFileName(const TDesC& aOrigName, TDes& aHashName)
/**
	Get name of the hash file used for an EXE or DLL which has been
	copied to writable media.

	@param	aOrigName		Name of EXE or DLL which has been copied to
							writable media.  This does not have to be
							qualified because only the name and extension
							are used.
	@param	aHashName		On return this is set to the absolute filename
							which should contain the file's hash.  This
							function does not create the file, or its containing
							directory.
 */
	{
	aHashName.Copy(KSysHash);
	aHashName[0] = (TUint8) RFs::GetSystemDriveChar();
	const TParsePtrC ppc(aOrigName);
	aHashName.Append(ppc.NameAndExt());
	}

static void CopyExecutablesL(TBool aCorruptMode=EFalse)
/**
	Make a copy of each executable that should be copied
	to a writable drive.
	
	If aCorruptMode make KNumberOfCorruptFiles corrupted copies: truncated file and a file with corrupted header 

 */
	{
	TInt r;
	TInt numCorruptFiles = 0;

	GetSpecialDrives();
	
	CFileMan* fm = CFileMan::NewL(Fs);
	
	for (TInt i = 0; i < KNumModules; ++i)
		{
		if (aCorruptMode && numCorruptFiles==KNumberOfCorruptFiles)
			break;

		if (aCorruptMode && !(GetModuleFlags(i)&KModuleFlagExe))
			continue;

		const TPtrC fn = MODULE_FILENAME(i);
		
		// if this is an absolute filename then copy it to
		// the appropriate drive.
		if (fn[1] != ':')
			continue;
		
		TFileName fnDest;
		GetNonZFileName(fn, fnDest);

		
		TFileName fnSrc(fn);
		fnSrc[0] = 'z';

		test.Printf(_L("CopyExecutables;%S,%S\n"), &fnSrc, &fnDest);

#ifdef __WINS__
		const TParsePtrC sppc(fnSrc);
		TBuf<MAX_PATH> sName;
		r = MapEmulatedFileName(sName, sppc.NameAndExt());
		test(r == KErrNone);

		TBuf<MAX_PATH> dName;
		r = MapEmulatedFileName(dName, fnDest);
		test(r == KErrNone);

		BOOL b = Emulator::CopyFile((LPCTSTR)sName.PtrZ(),(LPCTSTR)dName.PtrZ(),FALSE);
		test(b);
#else
		r = fm->Copy(fnSrc, fnDest);
		test(r == KErrNone);
#endif

		r = Fs.SetAtt(fnDest, 0, KEntryAttReadOnly);
		test.Printf(_L("CopyExecutables:setatt=%d\n"), r);
		User::LeaveIfError(r);

#ifdef __EPOC32__
		TInt moduleFlags = GetModuleFlags(i);

		// modify the new destination file by applying the required paging flags
		RFile fNp;
		r = fNp.Open(Fs, fnDest, EFileWrite | EFileStream);
		User::LeaveIfError(r);
		CleanupClosePushL(fNp);

		// read the header and get the total number of bytes to checksum.
		// (This may be greater than sizeof(E32ImageHeader).
		TPckgBuf<E32ImageHeader> hdrBuf;
		r = fNp.Read(0, hdrBuf);
		User::LeaveIfError(r);
		TInt totalSize = hdrBuf().TotalSize();
		test.Printf(_L("np flags=0x%x,totalSize=%d\n"), hdrBuf().iFlags, totalSize);
		
		// read in the actual bytes to checksum
		TUint8* startBytes0 = (TUint8*) User::AllocLC(totalSize);
		TPtr8 startBytes(startBytes0, 0, totalSize);
		r = fNp.Read(0, startBytes);
		User::LeaveIfError(r);
		test(startBytes.Length() == totalSize);

		// apply the required paging flags to the header
		E32ImageHeader* hdr2 = reinterpret_cast<E32ImageHeader*>(startBytes0);
		TUint& flags = hdr2->iFlags;
		flags &= ~(KImageCodePaged | KImageCodeUnpaged);
		if (moduleFlags & KModuleFlagPagedCode)
			flags |= KImageCodePaged;
		if (moduleFlags & KModuleFlagUnpagedCode)
			flags |= KImageCodeUnpaged;
		test.Printf(_L("setting new image flags 0x%x\n"), flags);

		// corrupt header of the 2nd file
		if (aCorruptMode && numCorruptFiles==1 && (moduleFlags&KModuleFlagExe))
			{
			hdr2->iCodeBase += 3;
			hdr2->iDataBase += 1;
			hdr2->iImportOffset += 1;
			hdr2->iCodeRelocOffset += 3;
			hdr2->iDataRelocOffset += 3;

			++numCorruptFiles;
			}

		// recalculate the checksum
		hdr2->iHeaderCrc = KImageCrcInitialiser;
		TUint32 crc = 0;
		Mem::Crc32(crc, startBytes.Ptr(), totalSize);
		hdr2->iHeaderCrc = crc;
		r = fNp.Write(0, startBytes);
		User::LeaveIfError(r);

		// truncate 1st corrupted file
		if (aCorruptMode && numCorruptFiles==0 && (moduleFlags&KModuleFlagExe))
			{
			TInt size;
			r = fNp.Size(size);
			User::LeaveIfError(r);
			// if trncate by 1 it managed to load. if trancate by 3 it failed to load with KErrCorrupt as expected
			r = fNp.SetSize(size-3); 
			User::LeaveIfError(r);
			++numCorruptFiles;
			}

		CleanupStack::PopAndDestroy(2, &fNp);		// startBytes0, fNp
#endif

		// if copied to removable media, then generate hash
		if (fn[0] == '0')
			continue;

		CSHA1* sha1 = CSHA1::NewL();
		CleanupStack::PushL(sha1);

		RFile fDest;
		r = fDest.Open(Fs, fnDest, EFileRead | EFileStream);
		User::LeaveIfError(r);
		CleanupClosePushL(fDest);

		TBool done;
		TBuf8<512> content;
		do
			{
			r = fDest.Read(content);
			User::LeaveIfError(r);
			done = (content.Length() == 0);
			if (! done)
				sha1->Update(content);
			} while (! done);
		CleanupStack::PopAndDestroy(&fDest);

		// write hash to \sys\hash
		TBuf8<SHA1_HASH> hashVal = sha1->Final();

		// reuse fnSrc to save stack space
		GetHashFileName(fnDest, fnSrc);
		RFile fHash;
		r = fHash.Replace(Fs, fnSrc, EFileWrite | EFileStream);
		test.Printf(_L("hash file,%S,r=%d\n"), &fnSrc, r);
		User::LeaveIfError(r);
		CleanupClosePushL(fHash);
		r = fHash.Write(hashVal);
		User::LeaveIfError(r);

		CleanupStack::PopAndDestroy(2, sha1);
		}
	
	delete fm;
	}

static void DeleteExecutables(TBool aCorruptMode=EFalse)
/**
	Delete any executables which were created by CopyExecutables.
	This function is defined so the test cleans up when it has finished.
 */
	{
	TInt numCorruptFiles = 0;

	for (TInt i = 0; i < KNumModules; ++i)
		{
		if (aCorruptMode && numCorruptFiles==KNumberOfCorruptFiles)
			break;

		if (aCorruptMode && !(GetModuleFlags(i)&KModuleFlagExe))
			continue;

		const TPtrC fn = MODULE_FILENAME(i);
		
		// if this is an absolute filename then copy it to
		// the appropriate drive.
		if (fn[1] != ':')
			continue;
		
		test.Printf(_L("DeleteExecutables:fn=%S\n"), &fn);
		TFileName fnDest;
		GetNonZFileName(fn, fnDest);
		
		TInt r;

		r = Fs.Delete(fnDest);
		test.Printf(_L("DeleteExecutables:fnDest=%S,del=%d\n"), &fnDest, r);
		test(r == KErrNone);

		// only need to delete hash files for binaries copied to removable media,
		// but simpler to delete and test for KErrNotFound
		TFileName fnHash;
		GetHashFileName(fnDest, fnHash);
		r = Fs.Delete(fnHash);
		test.Printf(_L("DeleteExecutables,h=%S,hdel=%d\n"), &fnHash, r);
		test(r == KErrPathNotFound || r == KErrNotFound || r == KErrNone);

		if (aCorruptMode)
			++numCorruptFiles;
		}
	}

GLDEF_C TInt E32Main()
	{
	RThread().SetPriority(EPriorityLess);

	test.Title();
	test.Start(_L("Setup"));

	RLoader l;
	test(l.Connect()==KErrNone);
	test(l.CancelLazyDllUnload()==KErrNone);
	l.Close();

	test(TestLdd.Open()==KErrNone);
	LoaderTest* pL=LoaderTest::New();
	TheLoaderTest=pL;

	TInt tm=pL->iCmdLine[0];
	TInt nr = (tm>>4)&3;
	if (nr==1)
		NoRemovable = ETrue;
	else if (nr==2)
		NoRemovable = EFalse;

	test(Fs.Connect() == KErrNone);

	// allocate a cleanup stack so can call CFileMan::NewL in CopyExecutables
	test.Printf(_L("CopyExecutablesL()\n"));
	CTrapCleanup* cleanup=CTrapCleanup::New();
	TRAPD(r, CopyExecutablesL());
	test(r == KErrNone);
	delete cleanup;

	if (tm&1)
		pL->TestOneByOne();
	if (tm&2)
		pL->TestMultipleExeInstances();
	if (tm&4)
		pL->TestOOM();
	if (tm&8)
		pL->TestMultipleLoads();

	pL->Close();

	// Test loader error handling - will panic the client thread
	test.Next(_L("Test loader error handling - will panic the client thread"));
	RThread t;
	t.Create(_L("Loader panic test"),PanicTestThread,KDefaultStackSize,0x1000,0x1000,NULL);
	TRequestStatus s;
	t.Logon(s);
	TBool justInTime=User::JustInTime(); 
	User::SetJustInTime(EFalse); 
	t.Resume();
	User::WaitForRequest(s);
	test(t.ExitType()==EExitPanic);
	test(t.ExitCategory().Compare(_L("LOADER"))==0);
	test(t.ExitReason()==0);
	t.Close();
	User::SetJustInTime(justInTime);

	DeleteExecutables();

#ifdef __EPOC32__
	// test corrupted files
	cleanup=CTrapCleanup::New();
	test.Next(_L("CopyExecutablesL(ETrue)"));
	TRAPD(rr, CopyExecutablesL(ETrue));
	test(rr == KErrNone);
	delete cleanup;
	test.Next(_L("TestCorruptedFiles()"));
	TestCorruptedFiles();
	test.Next(_L("DeleteExecutables()"));
	DeleteExecutables(ETrue);
#endif

	Fs.Close();

	test.End();
	return KErrNone;
	}