kernel/eka/drivers/debug/rmdebug/d_list_manager.cpp
changeset 0 a41df078684a
child 81 e7d2d738d3c2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/debug/rmdebug/d_list_manager.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1007 @@
+// Copyright (c) 2007-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:
+// Provides a class to manage the generation of lists
+// 
+//
+
+#include "d_list_manager.h"
+#include "d_process_tracker.h"
+#include "debug_utils.h"
+#include "plat_priv.h"
+#include "debug_logging.h"
+#include <arm.h>
+
+// make accessing DThread's MState more intuitive
+#define iMState iWaitLink.iSpare1
+// make accessing NThread's NState more intuitive
+#define iNState iSpare3
+
+//constants to match against a rom entry's attributes,
+//these are defined in the file server (can't be included kernel side)
+//and in the ROM tools (also inaccessible) so redefined here
+const TUint KEntryAttXIP=0x0080;
+const TUint KEntryAttDir=0x0010;
+
+using namespace Debug;
+
+/**
+  Get thread listing for the specified thread, if the thread data will not fit
+  in the buffer then an error is returned.
+
+  @param aBuffer buffer to put data in
+  @param aDataSize on return will contain size of data
+  @param aTargetThreadId thread ID to return listing for
+
+  @return KErrNone on success,
+  KErrTooBig if data won't fit in aBuffer
+  or one of the other system wide error codes on failure
+  */
+TInt TListManager::GetThreadListForThread(TDes8& aBuffer, TUint32& aDataSize, const TUint64 aTargetThreadId) const
+	{
+	LOG_MSG("TListManager::GetThreadListForThread()");
+
+	// open a handle to check whether the thread actually exists
+	DThread* thread = DebugUtils::OpenThreadHandle(aTargetThreadId);
+	if(!thread)
+		{
+		return KErrArgument;
+		}
+	DProcess* process = thread->iOwningProcess;
+	if(!process)
+		{
+		return KErrArgument;
+		}
+	TUint64 processId = process->iId;
+	thread->Close(NULL);
+
+	//request a process specific list
+	return GetThreadListForProcess(aBuffer, aDataSize, processId);
+	}
+
+TInt TListManager::GetThreadListForProcess(TDes8& aBuffer, TUint32& aDataSize, const TUint64 aTargetProcessId) const
+	{
+	LOG_MSG("TListManager::GetThreadListForProcess()");
+
+	// open a handle to check whether the process actually exists
+	DProcess* process = DebugUtils::OpenProcessHandle(aTargetProcessId);
+	if(!process)
+		{
+		return KErrArgument;
+		}
+	process->Close(NULL);
+
+	//request a process specific list
+	return GetThreadList(aBuffer, aDataSize, EFalse, aTargetProcessId);
+	}
+
+/**
+  Get global thread listing
+
+  @param aBuffer buffer to put data in
+  @param aDataSize on return will contain size of data
+
+  @return KErrNone on success,
+  KErrTooBig if data won't fit in aBuffer
+  or one of the other system wide error codes on failure
+  */
+TInt TListManager::GetGlobalThreadList(TDes8& aBuffer, TUint32& aDataSize) const
+	{
+	LOG_MSG("TListManager::GetGlobalThreadList()");
+
+	//request a global list
+	return GetThreadList(aBuffer, aDataSize, ETrue, 0);
+	}
+
+/**
+  Get thread listing, if the thread data will not fit
+  in the buffer then an error is returned.
+
+  @param aBuffer buffer to put data in
+  @param aDataSize on return will contain size of data
+  @param aGlobal whether or not the listing should be global or thread specific
+  @param aTargetProcessId process ID to return listing for, relevant only if aGlobal == ETrue
+
+  @return KErrNone on success,
+  KErrTooBig if data won't fit in aBuffer
+  or one of the other system wide error codes on failure
+  */
+TInt TListManager::GetThreadList(TDes8& aBuffer, TUint32& aDataSize, TBool aGlobal, const TUint64 aTargetProcessId) const
+	{
+
+	LOG_MSG("TListManager::GetThreadList\n");
+
+	//have to read the threads in a critical section
+	NKern::ThreadEnterCS();
+
+	//get a pointer to the kernel's thread list
+	DObjectCon *threads = Kern::Containers()[EThread];
+
+	//if can't get container then exit
+	if(threads == NULL)
+		{
+		NKern::ThreadLeaveCS();
+
+		return KErrGeneral;
+		}
+
+	//stop the thread list from changing while we are processing them
+	threads->Wait();
+
+	aDataSize = 0;
+	aBuffer.SetLength(0);
+	//iterate through the threads adding them to the buffer
+	for(TInt i=0; i<threads->Count(); i++)
+		{
+		DThread* thread = (DThread*)(*threads)[i];
+
+		//skip this thread pointer is the thread is NULL
+		if(thread)
+			{
+			NThread& nThread = thread->iNThread;
+
+			// if the thread is marked as being dead then don't return information about it in the listing
+#ifndef __SMP__
+			if((NThread::EDead != nThread.iNState) && (DThread::EDead != thread->iMState))
+#else
+ 			if((!nThread.IsDead()) && (DThread::EDead != thread->iMState))
+#endif
+				{
+				if( aGlobal || (aTargetProcessId == (TUint64)thread->iOwningProcess->iId))
+					{
+					//store the data in the buffer
+					AppendThreadData(aBuffer, aDataSize, thread);
+					}
+				}
+			}
+		}
+
+	//leave critical section
+	threads->Signal();
+	NKern::ThreadLeaveCS();
+
+	//return indication of whether the kernel's data was too big
+	return (aDataSize > aBuffer.Length()) ? KErrTooBig : KErrNone;
+	}
+
+/**
+  Helper function for writing thread data into a buffer
+
+  @pre call in a critical section
+  @pre call only on threads which have NThread state not equal to NThread::EDead
+
+  @param aBuffer buffer to put data in
+  @param aDataSize on return will contain size of data
+  @param aThread thread object to include information about
+
+  @return KErrNone on success, or one of the other system wide error codes
+*/
+void TListManager::AppendThreadData(TDes8& aBuffer, TUint32& aDataSize, DThread* aThread) const
+	{
+	//get aThread's name
+	TFileName fileName;
+	aThread->FullName(fileName);
+	TUint16 nameLength = fileName.Length();
+
+	//increase aDataSize by the size of this entry
+	aDataSize = Align4(aDataSize + (2*nameLength) + sizeof(TThreadListEntry) - sizeof(TUint16));
+	//if the data would not cause overflow then add it to the buffer
+	if(aDataSize <= aBuffer.MaxLength())
+		{
+		//Create a TThreadListEntry which references the buffer.
+		TThreadListEntry& entry = *(TThreadListEntry*)(aBuffer.Ptr()+aBuffer.Length());
+		//add data to entry
+		entry.iProcessId = (TUint64)aThread->iOwningProcess->iId;
+		entry.iThreadId = (TUint64)aThread->iId;
+		entry.iSupervisorStackBase = (TUint32)aThread->iSupervisorStack;
+		entry.iSupervisorStackBaseValid = ETrue;
+		entry.iSupervisorStackSize = aThread->iSupervisorStackSize;
+		entry.iSupervisorStackSizeValid = ETrue;
+		entry.iNameLength = nameLength;
+
+		//only ask for the supervisor stack pointer if aThread is suspended
+		entry.iSupervisorStackPtrValid = EInValid;
+		entry.iSupervisorStackPtr = 0;
+		if(TheDProcessTracker.CheckSuspended(aThread))
+			{
+			NThread& nThread = aThread->iNThread;
+
+			TArmRegSet regSet;
+			TUint32 flags;
+			NKern::ThreadGetSystemContext(&nThread, &regSet, flags);
+			entry.iSupervisorStackPtr = (TUint32)regSet.iR13;
+			//need to check that the stack pointer flag is valid
+			if(flags & (1<<EArmSp))
+				{
+				entry.iSupervisorStackPtrValid = EValid;
+				}
+			}
+
+		//copy name data into the buffer
+		TUint16* ptr = &(entry.iName[0]);
+		const TUint8* ptr8 = fileName.Ptr();
+		const TUint8* ptr8End = ptr8 + nameLength;
+		while(ptr8 < ptr8End)
+			{
+			*ptr++ = (TUint16)*ptr8++;
+			}
+ 
+		aBuffer.SetLength(aDataSize);
+		}
+	}
+
+/**
+  Get global process listing
+
+  @param aBuffer buffer to put data in
+  @param aDataSize on return will contain size of data
+
+  @return KErrNone on success,
+  KErrTooBig if data won't fit in aBuffer
+  or one of the other system wide error codes on failure
+  */
+TInt TListManager::GetProcessList(TDes8& aBuffer, TUint32& aDataSize) const
+	{
+	LOG_MSG("TListManager::GetProcessList()");
+
+	//get a pointer to the kernel's process list
+	DObjectCon* processes = Kern::Containers()[EProcess];
+
+	if(processes == NULL)
+		{
+		//if can't get container then something is seriously wrong
+		return KErrNotFound;
+		}
+
+	//have to read the processes in a critical section
+	NKern::ThreadEnterCS();
+	processes->Wait();
+
+	aDataSize = 0;
+	//iterate through the processes adding them to the buffer
+	for(TInt i=0; i<processes->Count(); i++)
+		{
+		DProcess* process = (DProcess*)(*processes)[i];
+		if(process)
+			{
+			//get process's file name length
+			DCodeSeg* codeSeg = process->iCodeSeg;
+			TUint16 fileNameLength = (codeSeg) ? (*codeSeg->iFileName).Length() : 0;
+
+			//get process's dynamic name length and name
+			TFullName fullName;
+			process->FullName(fullName);
+			TUint16 dynamicNameLength = fullName.Length();
+
+			//increase aDataSize to reflect size of entry
+			aDataSize = Align4(aDataSize + (2*fileNameLength) + (2*dynamicNameLength) + sizeof(TProcessListEntry) - sizeof(TUint16));
+			//if the data would not cause overflow then add it to the buffer
+			if(aDataSize <= aBuffer.MaxLength())
+				{
+				//Create a TProcessListEntry which references the buffer.
+				TProcessListEntry& entry = *(TProcessListEntry*)(aBuffer.Ptr() + aBuffer.Length());
+
+				//set values
+				entry.iProcessId = (TUint64)process->iId;
+				entry.iFileNameLength = fileNameLength;
+				entry.iDynamicNameLength = dynamicNameLength;
+				entry.iUid3 = process->iUids.iUid[2].iUid;
+
+				if(codeSeg)
+					{
+					//create TPtr to where the file name should be written
+					TPtr name = TPtr((TUint8*)&(entry.iNames[0]), fileNameLength*2, fileNameLength*2);
+					//copy the file name
+					TInt err = CopyAndExpandDes(*codeSeg->iFileName, name);
+					if(err != KErrNone)
+						{
+						processes->Signal();
+						NKern::ThreadLeaveCS();
+						return KErrGeneral;
+						}
+					}
+
+				//create TPtr to where the dynamic name should be written
+				TPtr name = TPtr((TUint8*)(&(entry.iNames[0]) + fileNameLength), dynamicNameLength*2, dynamicNameLength*2);
+				//copy the dynamic name
+				TInt err = CopyAndExpandDes(fullName, name);
+				if(err != KErrNone)
+					{
+					processes->Signal();
+					NKern::ThreadLeaveCS();
+					return KErrGeneral;
+					}
+
+				//set length same as aDataSize
+				aBuffer.SetLength(aDataSize);
+				}
+			}
+		}
+
+	//leave critical section
+	processes->Signal();
+	NKern::ThreadLeaveCS();
+
+	//return indication of whether the kernel's data was too big
+	return (aDataSize > aBuffer.Length()) ? KErrTooBig : KErrNone;
+	}
+
+/**
+  Copy the descriptor aSrc to aDest and converting each byte from aSrc
+  into the two-byte equivalent. For example if aSrc contains 'XYZ' then
+  aDest will be filled with 'X\0Y\0Z\0' where \0 is the null character.
+  The length of aDest is set to twice the length of aSrc.
+
+  @param aSrc source descriptor
+  @param aDest destination descriptor to copy and expand aSrc into
+
+  @return KErrNone on success,
+  KErrArgument if the max length of aDest is less than twice the length of aSrc
+  */
+TInt TListManager::CopyAndExpandDes(const TDesC& aSrc, TDes& aDest) const
+	{
+	//check bounds
+	if(aSrc.Length() * 2 > aDest.MaxLength())
+		{
+		return KErrArgument;
+		}
+
+	//get a pointer to the start of the destination descriptor
+	TUint16* destPtr = (TUint16*)aDest.Ptr();
+
+	//get pointers to the start and end of the aSrc descriptor
+	const TUint8* srcPtr = aSrc.Ptr();
+	const TUint8* srcEnd = srcPtr + aSrc.Length();
+
+	//copy the characters from aSrc into aDest, expanding to make them 16-bit characters
+	while(srcPtr < srcEnd)
+		{
+		*destPtr = (TUint16)*srcPtr;
+		destPtr++;
+		srcPtr++;
+		}
+
+	//set aDest's length to reflect the new contents
+	aDest.SetLength(2*aSrc.Length());
+	return KErrNone;
+	}
+
+/**
+  Get global code segment listing
+
+  @param aBuffer buffer to put data in
+  @param aDataSize on return will contain size of data
+
+  @return KErrNone on success,
+  KErrTooBig if data won't fit in aBuffer,
+  or one of the other system wide error codes
+  */
+TInt TListManager::GetGlobalCodeSegList(TDes8& aBuffer, TUint32& aDataSize) const
+	{
+	LOG_MSG("TListManager::GetGlobalCodeSegList()");
+
+	// Acquire code seg lock mutex
+	NKern::ThreadEnterCS();
+	DMutex* codeMutex = Kern::CodeSegLock();
+	Kern::MutexWait(*codeMutex);
+
+	//get global code seg list
+	SDblQue* codeSegList = Kern::CodeSegList();
+
+	//create a memory info object for use in the loop
+	TModuleMemoryInfo memoryInfo;
+
+	//iterate through the list
+	aDataSize = 0;
+	for (SDblQueLink* codeSegPtr= codeSegList->First(); codeSegPtr!=(SDblQueLink*) (codeSegList); codeSegPtr=codeSegPtr->iNext)
+		{
+		DEpocCodeSeg* codeSeg = (DEpocCodeSeg*)_LOFF(codeSegPtr,DCodeSeg, iLink);
+		//the code seg shouldn't be null as we're in critical section, ignore if it is null
+		if(codeSeg)
+			{
+			//get the memory info
+			TInt err = codeSeg->GetMemoryInfo(memoryInfo, NULL);
+			if(err != KErrNone)
+				{
+				// Release the codeseglock mutex again
+				Kern::MutexSignal(*codeMutex);
+				NKern::ThreadLeaveCS();
+
+				//there's been an error so return it
+				return err;
+				}
+			//calculate data values
+			TFileName fileName(codeSeg->iFileName->Ptr());
+			TBool isXip = (codeSeg->iXIP) ? ETrue : EFalse;
+
+			//get the code seg type, can ignore error as have already checked codeSeg is not NULL
+			TCodeSegType type = EUnknownCodeSegType;
+			err = GetCodeSegType(codeSeg, type);
+			if(err != KErrNone)
+				{
+				LOG_MSG("TListManager::GetGlobalCodeSegList() : code seg is NULL");
+				}
+
+			TUint32 uid3 = codeSeg->iUids.iUid[2].iUid;
+			//append data to buffer
+			err = AppendCodeSegData(aBuffer, aDataSize, memoryInfo, isXip, type, fileName, uid3);
+			if(err != KErrNone)
+				{
+				// Release the codeseglock mutex again
+				Kern::MutexSignal(*codeMutex);
+				NKern::ThreadLeaveCS();
+
+				return KErrGeneral;
+				}
+			}
+		}
+
+	// Release the codeseglock mutex again
+	Kern::MutexSignal(*codeMutex);
+	NKern::ThreadLeaveCS();
+
+	return (aDataSize > aBuffer.MaxLength()) ? KErrTooBig : KErrNone;
+	}
+
+/**
+  Get code segment list for a thread
+
+  @param aBuffer buffer to store data in
+  @param aDataSize size of kernel's data
+  @param thread ID to get listing for
+
+  @return KErrNone on success,
+  KErrTooBig if data won't fit in aBuffer,
+  or one of the other system wide error codes
+  */
+TInt TListManager::GetCodeSegListForThread(TDes8& aBuffer, TUint32& aDataSize, const TUint64 aTargetThreadId) const
+	{
+	LOG_MSG("TListManager::GetCodeSegListForThread()");
+
+	// open a handle to check whether the thread actually exists
+	DThread* thread = DebugUtils::OpenThreadHandle(aTargetThreadId);
+	if(!thread)
+		{
+		return KErrArgument;
+		}
+	DProcess* process = thread->iOwningProcess;
+	if(!process)
+		{
+		return KErrArgument;
+		}
+	TUint64 processId = process->iId;
+	thread->Close(NULL);
+
+	return GetCodeSegListForProcess(aBuffer, aDataSize, processId);
+	}
+/**
+  Get code segment list for a process
+
+  @param aBuffer buffer to store data in
+  @param aDataSize size of kernel's data
+  @param process ID to get listing for
+
+  @return KErrNone on success,
+  KErrTooBig if data won't fit in aBuffer,
+  or one of the other system wide error codes
+  */
+TInt TListManager::GetCodeSegListForProcess(TDes8& aBuffer, TUint32& aDataSize, const TUint64 aTargetProcessId) const
+	{
+	LOG_MSG("TListManager::GetCodeSegListForProcess()");
+
+	//get the process
+	DProcess* process = DebugUtils::OpenProcessHandle(aTargetProcessId);
+
+	if(!process)
+		{
+		return KErrArgument;
+		}
+
+	//enter thread critical section and acquire code segment mutex
+	Kern::AccessCode();
+
+	//memory info object to use in loop
+	TModuleMemoryInfo memoryInfo;
+
+	//get code seg list
+	SDblQue queue;
+	process->TraverseCodeSegs(&queue, NULL, DCodeSeg::EMarkDebug, DProcess::ETraverseFlagAdd);
+
+	//iterate through the list
+	aDataSize = 0;
+	for(SDblQueLink* codeSegPtr= queue.First(); codeSegPtr!=(SDblQueLink*) (&queue); codeSegPtr=codeSegPtr->iNext)
+		{
+		//get the code seg
+		DEpocCodeSeg* codeSeg = (DEpocCodeSeg*)_LOFF(codeSegPtr,DCodeSeg, iTempLink);
+
+		//the code seg shouldn't be null as we're in critical section, ignore if it is null
+		if(codeSeg)
+			{
+			TInt err = codeSeg->GetMemoryInfo(memoryInfo, NULL);
+			if(err != KErrNone)
+				{
+				process->Close(NULL);
+				return err;
+				}
+
+			TFileName fileName(codeSeg->iFileName->Ptr());
+			TBool isXip = (codeSeg->iXIP) ? ETrue : EFalse;
+
+			//get the code seg type, can ignore error as have already checked codeSeg is not NULL
+			TCodeSegType type = EUnknownCodeSegType;
+			err = GetCodeSegType(codeSeg, type);
+			if(err != KErrNone)
+				{
+				LOG_MSG("TListManager::GetCodeSegListForProcess() : code seg is NULL");
+				}
+
+			TUint32 uid3 = codeSeg->iUids.iUid[2].iUid;
+			//append data to buffer
+			err = AppendCodeSegData(aBuffer, aDataSize, memoryInfo, isXip, type, fileName, uid3);
+			if(err != KErrNone)
+				{
+				process->Close(NULL);
+				return KErrGeneral;
+				}
+			}
+		}
+
+	//un mark the code segs that we've iterated over
+	DCodeSeg::EmptyQueue(queue, DCodeSeg::EMarkDebug);
+
+	//release mutex and leave CS
+	Kern::EndAccessCode();
+
+	process->Close(NULL);
+	return (aDataSize > aBuffer.MaxLength()) ? KErrTooBig : KErrNone;
+	}
+
+/**
+  Appends data to a specified buffer and puts the resulting size in aDataSize.
+  If the data won't fit then aDataSize is updated to reflect what the new length
+  would be.
+
+  @param aBuffer buffer to append data to
+  @param aDataSize will contain buffer size (or the size the buffer would be) on return
+  @param aMemoryInfo info to append to buffer
+  @param aIsXip boolean indicating whether the code segment is XIP
+  @param aFileName file name to append to buffer
+
+  @return KErrNone on success, or one of the other system wide error codes
+  */
+TInt TListManager::AppendCodeSegData(TDes8& aBuffer, TUint32& aDataSize, const TModuleMemoryInfo& aMemoryInfo, const TBool aIsXip, const TCodeSegType aCodeSegType, const TDesC8& aFileName, const TUint32 aUid3) const
+	{
+	//get some data elements to put in buffer
+	TUint16 fileNameLength = aFileName.Length();
+
+	//calculate the resultant size
+	aDataSize = Align4(aDataSize + sizeof(TCodeSegListEntry) + (2*fileNameLength) - sizeof(TUint16));
+	if(aDataSize <= aBuffer.MaxLength())
+		{
+		//Create a TCodeSegListEntry which references the buffer.
+		TCodeSegListEntry& entry = *(TCodeSegListEntry*)(aBuffer.Ptr() + aBuffer.Length());
+		entry.iCodeBase = aMemoryInfo.iCodeBase;
+		entry.iCodeSize = aMemoryInfo.iCodeSize;
+		entry.iConstDataSize = aMemoryInfo.iConstDataSize;
+		entry.iInitialisedDataBase = aMemoryInfo.iInitialisedDataBase;
+		entry.iInitialisedDataSize = aMemoryInfo.iInitialisedDataSize;
+		entry.iUninitialisedDataSize = aMemoryInfo.iUninitialisedDataSize;
+		entry.iIsXip = aIsXip;
+		entry.iCodeSegType = aCodeSegType;
+		entry.iNameLength = fileNameLength;
+		entry.iUid3 = aUid3;
+
+		//have to convert the stored name to 16 bit unicode
+		TPtr name = TPtr((TUint8*)&(entry.iName[0]), fileNameLength*2, fileNameLength*2);
+		TInt err = CopyAndExpandDes(aFileName, name);
+		if(err != KErrNone)
+			{
+			return KErrGeneral;
+			}
+
+		//increase length
+		aBuffer.SetLength(aDataSize);
+		}
+
+	return KErrNone;
+	}
+
+/**
+  Get global XIP libraries list. The ROM file system is searched for files in
+  z:\sys\bin. The files are filtered to only include library files which
+  correspond to the correct hardware variant.
+
+  In the rom, a directory is represented as a list of TRomEntrys, corresponding to
+  the files and directories in that directory. A TRomEntry corresponding to a file
+  contains a pointer to that file's location in the rom. If the TRomEntry
+  corresponds to a directory then it contains a pointer to that directory in the
+  ROM header. As such, from a pointer to the root directory of the z: drive, it is
+  possible to extract the directory contents for a particular directory (i.e. z:\sys\bin)
+  by recursively finding the subdirectories (i.e. find 'sys' in 'z:', then 'bin' in 'sys')
+  and then listing the contents of that directory.
+
+  @param aBuffer buffer to store data in
+  @param aDataSize size of kernel's data
+
+  @return KErrNone on success,
+  KErrTooBig if data won't fit in aBuffer,
+  or one of the other system wide error codes
+  */
+TInt TListManager::GetXipLibrariesList(TDes8& aBuffer, TUint32& aDataSize) const
+	{
+	LOG_MSG("TListManager::GetXipLibrariesList()");
+
+	// z:\sys\bin expressed as 16 bit unicode..
+	_LIT(KZSysBin, "z\0:\0\\\0s\0y\0s\0\\\0b\0i\0n\0\\\0");
+
+	//array to store pointers to directory entries in
+	RPointerArray<TRomEntry> entries;
+	//get the entries in KZSysBin
+	TInt err = GetDirectoryEntries(entries, KZSysBin());
+	if(KErrNone != err)
+		{
+		entries.Close();
+		return err;
+		}
+
+	aDataSize = 0;
+	for(TInt i=0; i<entries.Count(); i++)
+		{
+		//if the entry is XIP and it's not a directory then it's a candidate to add
+		if( (entries[i]->iAtt & KEntryAttXIP) && ! (entries[i]->iAtt & KEntryAttDir) )
+			{
+			//get a reference to the dll's header
+			const TRomImageHeader& header = *(const TRomImageHeader*)(entries[i]->iAddressLin);
+
+			//check that it's uid1 value corresponds to that for a library
+			if(header.iUid1 == KDynamicLibraryUidValue)
+				{
+				//get the current hardware variant
+				TSuperPage& superPage = Kern::SuperPage();
+				TUint variant = superPage.iActiveVariant;
+				TUint cpu = (variant >> 16) & 0xff;
+				TUint asic = (variant >> 24);
+
+				//check this dll is compatible with the current variant
+				if(THardwareVariant(header.iHardwareVariant).IsCompatibleWith(cpu,asic,variant))
+					{
+					const TInt fileNameLength16 = entries[i]->iNameLength;
+					const TInt fullNameLength16 = (KZSysBin().Length() / 2) + fileNameLength16;
+					aDataSize += Align4((2 * fullNameLength16) + sizeof(TXipLibraryListEntry) - sizeof(TUint16));
+
+					if(aDataSize <= aBuffer.MaxLength())
+						{
+						//Create a TXipLibraryListEntry which references the buffer.
+						TXipLibraryListEntry& libraryInfo = *(TXipLibraryListEntry*)(aBuffer.Ptr() + aBuffer.Length());
+
+						//add the data
+						libraryInfo.iCodeBase = header.iCodeAddress;
+						libraryInfo.iCodeSize = header.iTextSize;
+						libraryInfo.iConstDataSize = header.iCodeSize - header.iTextSize;
+						libraryInfo.iInitialisedDataBase = header.iDataBssLinearBase;
+						libraryInfo.iInitialisedDataSize = header.iDataSize;
+						libraryInfo.iUninitialisedDataSize = header.iBssSize;
+						libraryInfo.iNameLength = fullNameLength16;
+
+						//create a TPtr8 to contain the fully qualified name (i.e. z:\sys\bin\ prefixed)
+						TPtr8 name((TUint8*)&(libraryInfo.iName[0]), 0, 2 * fullNameLength16);
+						name.Append(KZSysBin());
+						name.Append(TPtr8((TUint8*)&(entries[i]->iName), 2 * fileNameLength16, 2 * fileNameLength16));
+
+						//increase the buffer's length to reflect the new data size
+						aBuffer.SetLength(aDataSize);
+						}
+					}
+				}
+			}
+		}
+	entries.Close();
+	return (aDataSize == aBuffer.Length()) ? KErrNone : KErrTooBig;
+	}
+
+/**
+Get the list of TRomEntry objects in the specified directory aDirectory
+
+@param aRomEntryArray array to store pointers to the TRomEntry objects in
+@param aDirectoryName directory to get contents of. The passed in string should be
+16 bit unicode and should begin with z:. Single backslashes should be used as delimiters
+rather than forward slashes and a terminating backslash is optional.
+For example: z:\sys\bin
+
+@return KErrNone on success, or one of the other system wide error codes
+*/
+TInt TListManager::GetDirectoryEntries(RPointerArray<TRomEntry>& aRomEntryArray, const TDesC& aDirectoryName) const
+	{
+	LOG_MSG("TListManager::GetDirectoryEntries()");
+
+	//definition in 16 bit unicode
+	_LIT(KForwardSlash, "/\0");
+
+	//if directory has forward slashes then exit
+	if(aDirectoryName.Find(KForwardSlash()) != KErrNotFound)
+		{
+		return KErrArgument;
+		}
+
+	//create an array to hold the folders in aDirectoryName
+	RArray<TPtr8> folders;
+
+	//split the directory up into its folders, i.e. z:\sys\bin is split into { 'z:', 'sys', 'bin' }
+	TInt err = SplitDirectoryName(aDirectoryName, folders);
+	if(KErrNone != err)
+		{
+		folders.Close();
+		return err;
+		}
+
+	if(folders.Count() == 0)
+		{
+		folders.Close();
+		//empty string passed in
+		return KErrArgument;
+		}
+
+	// z: as 16 bit unicode
+	_LIT(KZColon, "z\0:\0");
+	if(folders[0].CompareF(KZColon()) != 0)
+		{
+		//first argument must be z: otherwise not in rom
+		folders.Close();
+		return KErrArgument;
+		}
+	//remove z: from array
+	folders.Remove(0);
+	for(TInt i=0; i<folders.Count(); i++)
+		{
+		if(folders[i].Length() == 0)
+			{
+			// there were two backslashes in a row
+			folders.Close();
+			return KErrArgument;
+			}
+		}
+
+	//get a pointer to the start of the rom root directory list
+	TLinAddr romRootDirectoryList = Epoc::RomHeader().iRomRootDirectoryList;
+
+	//the first 4 bytes of the rom root directory list is a count of how many sections (rom roots) there are
+	TUint32 rootDirectoryCount = (TUint32)*(TLinAddr*)romRootDirectoryList;
+
+	//rootDirectoryPointer will be shifted through the rom root directory list and will contain pointers to the sections in the rom
+	TLinAddr rootDirectoryPointer = romRootDirectoryList;
+	for(TInt i=0; i<rootDirectoryCount; i++)
+		{
+		//the address of the section is stored in the second four bytes of the 8 byte pair reserved for each section
+		rootDirectoryPointer += 8;
+
+		//romRoot contains the address of the root of the section
+		TLinAddr romRoot = *(TLinAddr*)rootDirectoryPointer;
+
+		//append the directory entries from romRoot's z:\sys\bin subdirectory
+		TInt err = GetDirectoryEntries(aRomEntryArray, folders, romRoot);
+		if(KErrNone != err)
+			{
+			folders.Close();
+			return err;
+			}
+		}
+	folders.Close();
+	return KErrNone;
+	}
+
+/**
+  Recursively finds the subdirectories in aArray and stores references to the
+  entries in the most derived subdirectory in aRomEntryArray
+
+  @param aRomEntryArray on return will contain the entries in the directory corresponding to aArray
+  @param aArray an array containing the directory to get the entries for, i.e. { 'sys', 'bin' }
+  @param aAddress address in rom to being searching from
+
+  @param KErrNone on success, or one of the other system wide error codes
+*/
+TInt TListManager::GetDirectoryEntries(RPointerArray<TRomEntry>& aRomEntryArray, RArray<TPtr8>& aArray, TLinAddr& aAddress) const
+	{
+	LOG_MSG2("TListManager::GetDirectoryEntries() aAddress: 0x%08x", aAddress);
+
+	//find the next subdirectory and store its address in aAddress, return error if we can't find it
+	TInt err = FindDirectory(aArray[0], aAddress);
+	if(err != KErrNone)
+		{
+		return err;
+		}
+
+	//if this is the most derived sub-directory (i.e. the bin of z:\sys\bin) then get the dir contents
+	if(aArray.Count() == 1)
+		{
+		return GetDirectoryContents(aRomEntryArray, aAddress);
+		}
+	else
+		{
+		//get the next subdirectory's contents
+		aArray.Remove(0);
+		return GetDirectoryEntries(aRomEntryArray, aArray, aAddress);
+		}
+	}
+
+/**
+Return the entries of a directory in the rom
+
+@param aRomEntryArray array to store the entries in
+@param aAddress address of a directory block in the rom
+*/
+TInt TListManager::GetDirectoryContents(RPointerArray<TRomEntry>& aRomEntryArray, const TLinAddr aAddress) const
+	{
+	LOG_MSG("TListManager::GetDirectoryContents()");
+
+	TLinAddr address = aAddress;
+
+	//get the size in bytes of the block of rom to iterate over
+	const TUint32 sizeInBytes = *(TUint32*)aAddress;
+
+	//get address of first TRomEntry
+	const TLinAddr initialAddress = aAddress + sizeof(TUint32);
+
+	//get pointer to subdir count
+	address = initialAddress + sizeInBytes;
+
+	//the upper two bytes of this entry contain the number of files in this directory, and the lower two bytes
+	//contains the number of subdirectories in this directory
+	TUint32 filesAndDirectories = *(TUint32*)address;
+
+	//get number of subdirectories in this directory
+	const TUint16 subDirCount = filesAndDirectories & 0xFFFF;
+
+	//get the number of files in this dir
+	const TUint16 filesCount = filesAndDirectories >> 16;
+
+	//get total number of entries in dir
+	const TUint numDirectoryEntries = subDirCount + filesCount;
+
+	//set address to start of first entry
+	address = initialAddress;
+
+	for(TInt i=0; i<numDirectoryEntries; i++)
+		{
+		TRomEntry* romEntry = (TRomEntry*)address;
+
+		//store the entry
+		TInt err = aRomEntryArray.Append(romEntry);
+		if(KErrNone != err)
+			{
+			return err;
+			}
+
+		//length of the name of the rom entry
+		TInt nameLength = romEntry->iNameLength;
+
+		//get the size of the entry including the name
+		TUint32 romEntrySize = sizeof(TRomEntry) - sizeof(romEntry->iName) + (2 * nameLength);
+		//adjust the address to the next entry
+		address += Align4(romEntrySize);
+		}
+	return KErrNone;
+	}
+
+/**
+  Finds the subdirectory with name aDirectory in the directory at aAddress
+
+  @param aDirectory name of subdirectory to search for (i.e. 'bin')
+  @param aAddress address in rom of containing directory (i.e. address of 'sys' directory)
+
+  @param KErrNone if aDirectory could be found in aAddress, KErrNotFound if it could not be found
+  */
+TInt TListManager::FindDirectory(const TDesC& aDirectory, TLinAddr& aAddress) const
+	{
+	LOG_MSG3("TListManager::FindDirectory() aDirectory: %S, aAddress: 0x%08x", &aDirectory, aAddress);
+
+	//get the directory's contents
+	RPointerArray<TRomEntry> dirContents;
+	TInt err = GetDirectoryContents(dirContents, aAddress);
+	if(KErrNone != err)
+		{
+		dirContents.Close();
+		return err;
+		}
+	for(TInt i=0; i<dirContents.Count(); i++)
+		{
+		//create a reference to the TRomEntry in the rom to access its attributes
+		TRomEntry& romEntry = *(dirContents[i]);
+		if(romEntry.iAtt & KEntryAttDir)
+			{
+			// this entry's a directory so check if it matches aDirectory
+			const TInt nameLength = romEntry.iNameLength;
+			TPtr8 name((TUint8*)&(romEntry.iName), nameLength * 2, nameLength * 2);
+			if(0 == aDirectory.CompareF(name))
+				{
+				// names matched so get the address of this directory's contents
+				aAddress = romEntry.iAddressLin;
+				dirContents.Close();
+				return KErrNone;
+				}
+			}
+		}
+	dirContents.Close();
+	//couldn't find it so return error
+	return KErrNotFound;
+	}
+
+/**
+  Helper function to get code seg type.
+
+  @param aCodeSeg code seg to get type of
+  @param aType will contain type on return
+
+  @return KErrNone on success, KErrNotFound if aCodeSeg is NULL
+  */
+TInt TListManager::GetCodeSegType(const DCodeSeg* aCodeSeg, TCodeSegType& aType) const
+	{
+	if(!aCodeSeg)
+		{
+		return KErrNotFound;
+		}
+
+	if(aCodeSeg->IsExe())
+		{
+		aType = EExeCodeSegType;
+		return KErrNone;
+		}
+
+	if(aCodeSeg->IsDll())
+		{
+		aType = EDllCodeSegType;
+		return KErrNone;
+		}
+
+	aType = EUnknownCodeSegType;
+	return KErrNone;
+	}
+
+
+/**
+  Split a directory name into its subdirectories, using a 16-bit backslash ('\\\0') as a delimiter.
+  For example z:\sys\bin would be split into { 'z:', 'sys', 'bin' }
+
+  @param aDirectoryName directory name to split into subdirectories
+  @param aSubDirectories array to store the subdirectories in
+  */
+TInt TListManager::SplitDirectoryName(const TDesC& aDirectoryName, RArray<TPtr8>& aSubDirectories) const
+	{
+	//definition in 16 bit unicode
+	_LIT(KBackSlash, "\\\0");
+
+	//split the directory up into its folders, i.e. z:\sys\bin is split into 
+	TPtr8 string((TUint8*)aDirectoryName.Ptr(), aDirectoryName.Length(), aDirectoryName.Length());
+	while(string.Ptr() < aDirectoryName.Ptr() + aDirectoryName.Length())
+		{
+		TInt offset = string.Find(KBackSlash());
+		if(offset == KErrNotFound)
+			{
+			//reached the end of the string
+			offset = string.Length();
+			}
+		//adjustedOffset takes account of the end of the string case
+		TInt adjustedOffset = (offset == string.Length()) ? offset : offset + KBackSlash().Length();
+		//add sub-folder name
+		TInt err = aSubDirectories.Append(TPtr8((TUint8*)string.Ptr(), offset, offset));
+		if(KErrNone != err)
+			{
+			return err;
+			}
+		//remove the sub-folder name and continue
+		string.Set((TUint8*)string.Ptr() + adjustedOffset, string.Length() - adjustedOffset, string.Length() - adjustedOffset);
+		}
+	return KErrNone;
+	}
+
+
+