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;
}