commands/patchdata/patchdata.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Sat, 31 Jul 2010 19:07:57 +0100
changeset 23 092bcc217d9d
parent 0 7f656887cf89
permissions -rw-r--r--
Tidied iocli exports, build macro tweaks. Removed 4 overloads of CCommandBase::RunCommand[L] that are no longer used at all, and changed one more to not be exported as it's only used internally to iocli.dll. fixed builds on platforms that don't support btrace or any form of tracing.

// patchdata.cpp
// 
// Copyright (c) 2008 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "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:
// Accenture - Initial contribution
//

#include <fshell/ioutils.h>
#include <f32image.h>
#include "sf_deflate.h"
#include <e32rom.h>
#include <fshell/memoryaccesscmd.h>

using namespace IoUtils;

IMPORT_C extern const TInt KHeapMinCellSize;

class CCmdPatchdata : public CMemoryAccessCommandBase
	{
public:
	static CCommandBase* NewLC();
	~CCmdPatchdata();
private:
	CCmdPatchdata();
	void HandleXipL();

private: // From CCommandBase.
	virtual const TDesC& Name() const;
	virtual void DoRunL();
	virtual void ArgumentsL(RCommandArgumentList& aArguments);
	virtual void OptionsL(RCommandOptionList& aOptions);
private:
	// The stuff from elsewhere
	void LoadFileInflateL(E32ImageHeaderComp* aHeader,TUint8* aRestOfFileData,TUint32 aRestOfFileSize);
	void LoadFileNoCompressL(E32ImageHeaderComp* aHeader,TUint8* aRestOfFileData,TUint32 aRestOfFileSize);
	TInt LoadFile(TUint32 aCompression,E32ImageHeaderComp* aHeader,TUint8* aRestOfFileData,TUint32 iRestOfFileSize);	

private:
	HBufC* iDll;
	TInt iOrdinal;
	TInt iNewValue;
	TFileName iPath;
	TFileName iNewPath;
	RFile iFile;
	TBool iVerbose;
	};


CCommandBase* CCmdPatchdata::NewLC()
	{
	CCmdPatchdata* self = new(ELeave) CCmdPatchdata();
	CleanupStack::PushL(self);
	self->BaseConstructL();
	return self;
	}

CCmdPatchdata::~CCmdPatchdata()
	{
	delete iDll;
	iFile.Close();
	}

CCmdPatchdata::CCmdPatchdata()
	{
	}

const TDesC& CCmdPatchdata::Name() const
	{
	_LIT(KName, "patchdata");	
	return KName;
	}

void CCmdPatchdata::ArgumentsL(RCommandArgumentList& aArguments)
	{
	aArguments.AppendStringL(iDll, _L("dll-name"));
	aArguments.AppendIntL(iOrdinal, _L("ordinal"));
	aArguments.AppendUintL((TUint&)iNewValue, _L("value"));
	}

void CCmdPatchdata::OptionsL(RCommandOptionList& aOptions)
	{
	aOptions.AppendBoolL(iVerbose, _L("verbose"));
	}

EXE_BOILER_PLATE(CCmdPatchdata)

void CCmdPatchdata::DoRunL()
	{
	if (!iDll)
		{
		// Just print some standard ones
		Printf(_L("KHeapMinCellSize: %d\r\n"), KHeapMinCellSize);
		return;
		}

	TBool scan = !iArguments.IsPresent(1);
	TBool change = iArguments.IsPresent(2);

	// First figure out where the dll is - easiest way is to load it
#ifdef __WINS__
	PrintWarning(_L("On WINS, exe-name must be a complete path to a E32 DLL"));
	iPath = *iDll;
#else
	// Can't use RLibrary because we have all caps and the DLL won't
	TFindFile find(FsL());
	TInt found = find.FindByDir(*iDll, _L("Y:\\sys\\bin\\"));
	LeaveIfErr(found, _L("Couldn't find DLL %S"), iDll);
	iPath = find.File();

	if (FsL().IsFileInRom(iPath) != NULL)
		{
		HandleXipL();
		return;
		}
#endif

	iNewPath = iPath;
	TUint fileopenmode = EFileRead|EFileStream|EFileShareAny;

	if (change) fileopenmode = EFileWrite|EFileStream|EFileShareAny;

	if (change && iPath[0] != 'c')
		{
		// If file is already on c then we just patch in place

		// We have a new value to set - so copy the DLL to C in preparation for patching it
		iNewPath[0] = 'c';

		//CleanupStack::PushL(TCleanupItem(&DeleteModifiedBinary, this));

		TInt err = FsL().MkDirAll(iNewPath); // In case C:\sys\bin doesn't exist yet
		if (err && err != KErrAlreadyExists)
			{
			PrintError(err, _L("Couldn't create C:\\sys\\bin"));
			User::Leave(err);
			}

		CFileMan* fm = CFileMan::NewL(Fs());
		CleanupStack::PushL(fm);

		LeaveIfErr(fm->Copy(iPath, iNewPath), _L("Couldn't copy file from %S to %S"), &iPath, &iNewPath);
		// Clear the read-only bit in the case where we've copied from ROM
		LeaveIfErr(Fs().SetAtt(iNewPath, 0, KEntryAttReadOnly), _L("Couldn't unset read-only flag"));
		CleanupStack::PopAndDestroy(fm);
		}

	// Now start fiddling
	LeaveIfErr(iFile.Open(FsL(), iNewPath, fileopenmode), _L("Couldn't open file %S"), &iNewPath);

	///// Begin code lifted from chkdeps
	E32ImageHeaderV* imageHeader=new(ELeave)E32ImageHeaderV;
	CleanupStack::PushL(imageHeader);
	TPckg<E32ImageHeaderV> ptr(*imageHeader);
	LeaveIfErr(iFile.Read(ptr, sizeof(E32ImageHeaderV)), _L("Couldn't read E32ImageHeader"));

	if (!scan && (iOrdinal <= 0 || iOrdinal-1 >= imageHeader->iExportDirCount)) // -1 because ordinals are 1-based in DEF files
		{
		LeaveIfErr(KErrArgument, _L("ordinal out of range: only %d exports in file"), imageHeader->iExportDirCount);
		}
	if (imageHeader->HeaderFormat() >= KImageHdrFmt_V && imageHeader->iExportDescType != KImageHdr_ExpD_NoHoles)
		{
		LeaveIfErr(KErrNotSupported, _L("Don't understand files whose export table format isn't KImageHdr_ExpD_NoHoles (format is %d)"), imageHeader->iExportDescType);
		}

	TInt exportOffset = imageHeader->iExportDirOffset;

	// Decompress rest of image
	TUint32 compression = imageHeader->CompressionType();
	TInt restOfFileSize=0;
	TUint8* restOfFileData=NULL;
	//detect the size of import information
	if (compression != KFormatNotCompressed)
		{
		// Compressed executable
		// iCodeOffset	= header size for format V or above
		//				= sizeof(E32ImageHeader) for format J
		restOfFileSize = imageHeader->UncompressedFileSize() - imageHeader->iCodeOffset;
		}
	else
		{
		TInt FileSize;
		iFile.Size(FileSize); 		
		restOfFileSize = FileSize-imageHeader->TotalSize();
		}	
	//restOfFileSize -= imageHeader->iCodeSize; // the size of the exe less header & code
	
	//allocate memory for rest of file
	if (restOfFileSize >0)
		{
		restOfFileData = (TUint8*)User::AllocLC(restOfFileSize );		
		}

	LeaveIfErr(LoadFile(compression,imageHeader,restOfFileData,restOfFileSize), _L("Failed to load file data")); // Read import information in

	///// End code lifted from chkdeps

	const TInt headerSize = imageHeader->iCodeOffset;
	exportOffset -= headerSize; // we are indexing into restoffile which doesn't have the header

	TInt* exports = (TInt*)(restOfFileData + exportOffset);

	if (scan)
		{
		//Printf(_L("TextSize = %d\r\n"), imageHeader->iTextSize);
		//Printf(_L("CodeSize = %d\r\n"), imageHeader->iCodeSize);
		//Printf(_L("dataoffset = %d\r\n"), imageHeader->iDataOffset);
		//Printf(_L("importoffset= %d\r\n"), imageHeader->iImportOffset);
		//Printf(_L("exportdiroffset= %d\r\n"), imageHeader->iExportDirOffset);
		for (TInt i = 0; i < imageHeader->iExportDirCount; i++)
			{
			// No direct way of figuring out where code stops and const data starts. Export table sits between them though
			// so use this to distiguish. (Don't know if my logic here is right but it seems to mainly work...)
			TInt valoffset = exports[i] - imageHeader->iCodeBase;
			if (iVerbose)
				{
				Printf(_L("export[%d] = %d\r\n"), i, valoffset + headerSize);
				}
			if (valoffset >= (TInt)imageHeader->iExportDirOffset-headerSize)
				{
				TInt val = *(TInt*)(restOfFileData + valoffset);
				Printf(_L("Ordinal %d: 0x%08x (%d)\r\n"), i+1, val, val);
				}
			}
		}
	else
		{
		TInt valoffset = exports[iOrdinal-1] - imageHeader->iCodeBase; // Subtract one from ordinal as they're 1-based in DEF files
		TInt& val = *(TInt*)(restOfFileData + valoffset);

		if (change)
			{
			TInt oldVal = val;
			val = iNewValue; // This updates restOfFileData
			// Now write everything back to file. The data has to be written uncompressed as there is no compression
			// code we can easily rip off. We have to update the header irrespective to increment the version
 			iFile.SetSize(headerSize);
			
			imageHeader->iCompressionType = KFormatNotCompressed;
			// Update the version to sort out linking bug (probably not an issue for most uses of patchable data, but it could be in theory)
			if ((imageHeader->iModuleVersion & 0x0000ffffu) == 0x0000ffffu)
				{
				// Don't update if version is XXXXffff as incrementing it would raise the major version - which is considered incompatible
				PrintWarning(_L("Couldn't update DLL's minor version as it is already at its maximum value"));
				}
			else
				{
				imageHeader->iModuleVersion++;
				}

			// Update e32 checksum
			imageHeader->iHeaderCrc = KImageCrcInitialiser;
			TUint32 crc = 0;
			Mem::Crc32(crc, imageHeader, imageHeader->TotalSize());
			imageHeader->iHeaderCrc = crc;
			LeaveIfErr(iFile.Write(0, ptr), _L("Couldn't write updated header back to file"));

			TPtrC8 data(restOfFileData, restOfFileSize);
			LeaveIfErr(iFile.Write(headerSize, data), _L("Couldn't write patched DLL code back to file"));

			Printf(_L("Ordinal %d: old value was 0x%08x, new value is 0x%08x"), iOrdinal, oldVal, iNewValue);
			}
		else
			{
			Printf(_L("Ordinal %d: 0x%08x (%d)\r\n"), iOrdinal, val, val);
			}
		}

	CleanupStack::PopAndDestroy(2, imageHeader); // imageHeader, restOfFileData
	}

void FileCleanup(TAny* aPtr)
	{
	TFileInput* f=(TFileInput*)aPtr;
	f->Cancel();
	delete f;
	}

void CCmdPatchdata::LoadFileInflateL(E32ImageHeaderComp* aHeader,TUint8* aRestOfFileData,TUint32 aRestOfFileSize)
	{
	TInt pos = aHeader->TotalSize();
	User::LeaveIfError(iFile.Seek(ESeekStart,pos)); // Start at beginning of compressed data

	TFileInput* file = new (ELeave) TFileInput(iFile);
	CleanupStack::PushL(TCleanupItem(&FileCleanup,file));
	CInflater* inflater=CInflater::NewLC(*file);
	
	/*if (aHeader->iCodeSize)
		{
		TUint8* CodeData = (TUint8*)User::AllocLC(aHeader->iCodeSize );
		TInt count=inflater->ReadL((TUint8*)CodeData ,aHeader->iCodeSize,&Mem::Move);
		if(count!=aHeader->iCodeSize)
			User::Leave(KErrCorrupt);
		CleanupStack::PopAndDestroy(CodeData);
		}*/
	
	if (aRestOfFileSize)
		{
		TUint32 count=inflater->ReadL(aRestOfFileData,aRestOfFileSize,&Mem::Move);
		if(count!=aRestOfFileSize)
			User::Leave(KErrCorrupt);
		}
	CleanupStack::PopAndDestroy(2,file);
	}

void CCmdPatchdata::LoadFileNoCompressL(E32ImageHeaderComp* aHeader,TUint8* aRestOfFileData,TUint32 aRestOfFileSize)
	{
	TInt pos = (aHeader->TotalSize() /*+aHeader->iCodeSize*/);
	if (aRestOfFileSize)
		{
		User::LeaveIfError(iFile.Seek(ESeekStart,pos));
		TPtr8 ptrToData((TText8*)(aRestOfFileData),aRestOfFileSize,aRestOfFileSize);
		User::LeaveIfError(iFile.Read(ptrToData, (TInt)aRestOfFileSize));	
		}
	}	
//function loads file's import information calling decompression routine if needed
TInt CCmdPatchdata::LoadFile(TUint32 aCompression,E32ImageHeaderComp* aHeader,TUint8* aRestOfFileData,TUint32 aRestOfFileSize)
	{
	TInt r=KErrNone;
	if(aCompression==KFormatNotCompressed)
		{
		TRAP(r,LoadFileNoCompressL(aHeader,aRestOfFileData,aRestOfFileSize));		
		}
	else if(aCompression==KUidCompressionDeflate)
		{
		TRAP(r,LoadFileInflateL(aHeader,aRestOfFileData,aRestOfFileSize));
		}
	else
		r=KErrNotSupported;

	return r;
	}

void CCmdPatchdata::HandleXipL()
	{
	TBool scan = !iArguments.IsPresent(1);
	TBool change = iArguments.IsPresent(2);
	TRomImageHeader* imageHeader = (TRomImageHeader*)FsL().IsFileInRom(iPath);

	if (!scan && (iOrdinal <= 0 || iOrdinal-1 >= imageHeader->iExportDirCount)) // -1 because ordinals are 1-based in DEF files
		{
		LeaveIfErr(KErrArgument, _L("ordinal out of range: only %d exports in file"), imageHeader->iExportDirCount);
		}

	TLinAddr* exports = (TLinAddr*)(imageHeader->iExportDir);

	if (scan)
		{
		for (TInt i = 0; i < imageHeader->iExportDirCount; i++)
			{
			TLinAddr valAddr = exports[i];
			if (iVerbose)
				{
				Printf(_L("export[%d] = 0x%08x\r\n"), i, valAddr);
				}
			valAddr &= ~3; // Clear thumb bit - code exports can have this set. No idea why some can have bit 1 set...
			// There really doesn't seem to be a way to figure out where code stops and const data starts, for core images. It generally looks to come after the export dir but it gives false positives
			if (valAddr > (TLinAddr)imageHeader)
				{
				TInt val = *(TInt*)valAddr;
				Printf(_L("Ordinal %d: 0x%08x (%d)\r\n"), i+1, val, val);
				}
			}
		}
	else
		{
		TLinAddr valAddr = exports[iOrdinal-1]; // Subtract one from ordinal as they're 1-based in DEF files
		TInt val = *(TInt*)(valAddr);

		if (change)
			{
			TInt oldVal = val;

#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
			LoadMemoryAccessL();
			TPckg<TInt> valPkg(iNewValue);
			LeaveIfErr(iMemAccess.WriteShadowMemory(valAddr, valPkg), _L("Couldn't write shadow for new value"));
#else
			LeaveIfErr(KErrNotSupported, _L("Can't update patchdata for DLLs in core image without memoryaccess support"));
#endif


			Printf(_L("Ordinal %d: old value was 0x%08x, new value is 0x%08x"), iOrdinal, oldVal, iNewValue);
			}
		else
			{
			Printf(_L("Ordinal %d: 0x%08x (%d)\r\n"), iOrdinal, val, val);
			}
		}
	}