imgtools/romtools/rofsbuild/r_rofs.cpp
author timothy.murphy@nokia.com
Thu, 25 Mar 2010 13:43:28 +0000
branchfix
changeset 408 a819f9223567
parent 0 044383f39525
child 590 360bd6b35136
permissions -rw-r--r--
fix: stop using "magic" numbers in string operations for the copyannofile2log feature fix: When using the copylogfromannofile workaround, extract the build ID and build duration and add to the log as these are useful for analysis. The log should now be identical to the stdout file. fix: Remove extra blank lines from output in copylogfromannofile mode.

/*
* Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/


#include <e32std.h>
#include <e32std_private.h>
#include <e32uid.h>
#include <f32file.h>
#include "h_utl.h"
#include <string.h>
#include <stdlib.h>
#include "r_obey.h"
#include "r_rofs.h"
#include "r_coreimage.h"
#include "memmap.h"
extern TInt gLogLevel;
extern TBool gLowMem;
extern TBool gFastCompress;
extern TInt gThreadNum;
////////////////////////////////////////////////////////////////////////



inline TUint32 AlignData(TUint32 anAddr)
	{
	return ((anAddr+0x0f)&~0x0f);
	}


////////////////////////////////////////////////////////////////////////

E32Rofs::E32Rofs(CObeyFile *aObey)
//
// Constructor
//
	: iObey( aObey ), iOverhead(0)
	{

	iSize=aObey->iRomSize;
	if(gLowMem)
	{
		iImageMap = new Memmap();

		if(iImageMap == NULL)
		{
			iSize = 0;
			Print(EError, "Out of memory.\n");
		}
		else
		{
			iImageMap->SetMaxMapSize(iSize);
			if(iImageMap->CreateMemoryMap(0, 0xff) == EFalse)
			{
				iSize = 0;
				Print(EError, "Failed to create image map object");

				iImageMap->CloseMemoryMap(ETrue);
				delete iImageMap;
				iImageMap = NULL;
			}
			else
			{
				iData = iImageMap->GetMemoryMapPointer();
			}
		}
	}
	else
	{
		iData=new char [iSize];
		if (iData==NULL)
			iSize=0;
		HMem::Set(iData, 0xff, iSize);
	}

	}

E32Rofs::~E32Rofs()
//
// Destructor
//
	{

	if(gLowMem)
	{
		iImageMap->CloseMemoryMap(ETrue);
		delete iImageMap;
	}
	else
		delete iData;
	}


TInt E32Rofs::CreateExtension(MRofsImage* aImage) 
	{

	TUint8* addr=(TUint8*)iData;

	TRomNode* pRootDir = aImage->RootDirectory();
	

	const TInt extensionRofsheaderSize = KExtensionRofsHeaderSize;
	
	// aImage->iSize contains the max size of the core image

	// Layout the directory structure. Does not actually write it
	// to the image. Returns the number of bytes used for the directory
	// structure within the image.
	TInt directoryOffset = extensionRofsheaderSize;
	const TInt directorySize = LayoutDirectory( pRootDir, aImage->Size()+directoryOffset );
	if( directorySize <= 0 )
		{
		Print(EError, "Failed laying out directories - return code %d\n", directorySize);
		return KErrGeneral;
		}

	// get offset to start of file data, rounded up to next word boundary
	TInt offs = extensionRofsheaderSize + directorySize;
	const TInt fileDataStartOffset = offs + ( (4 - offs) & 3);

	// Now we traverse the list of entries placing each file
	// This updates the directory entries to point to the correct offset
	// to the start of the file
	const TInt fileDataSize = PlaceFiles( pRootDir, addr + fileDataStartOffset, fileDataStartOffset + aImage->Size(), aImage->Size());
	if( fileDataSize <= 0 )
		{
		Print(EError, "Failed placing files - return code %d\n", fileDataSize);
		return KErrGeneral;
		}

	// and then put the directory into the image
	TInt err = PlaceDirectory( pRootDir, addr - aImage->Size() ); // offset pointer by size of core image
	if( err != KErrNone )
		{
		Print(EError, "Failed placing directory - return code %d\n", err);
		return err;
		}
		
	directoryOffset+=aImage->Size(); // offset offset by size of core image
	// Now write the header
	TExtensionRofsHeader * pHeader = (TExtensionRofsHeader*)iData;
	pHeader->iIdentifier[0] = 'R';
	pHeader->iIdentifier[1] = 'O';
	pHeader->iIdentifier[2] = 'F';
	pHeader->iIdentifier[3] = 'x';
	pHeader->iHeaderSize = KExtensionRofsHeaderSize;
	pHeader->iDirTreeOffset = directoryOffset;
	pHeader->iDirTreeSize = iTotalDirectoryBlockSize;
	pHeader->iDirFileEntriesOffset = directoryOffset + iTotalDirectoryBlockSize;
	pHeader->iDirFileEntriesSize = iTotalFileBlockSize;
	pHeader->iRofsFormatVersion = KRofsFormatVersion;
	pHeader->iTime = iObey->iTime;
	iSizeUsed = fileDataSize + fileDataStartOffset;
	pHeader->iImageSize = iSizeUsed;
	if (iObey->AutoSize())
		MakeAutomaticSize(iObey->AutoPageSize()); // change iSize to nearest page size
	pHeader->iMaxImageSize = iSize;
	pHeader->iCheckSum = 0;		// not used yet

	return KErrNone;
	}
	
void E32Rofs::MakeAutomaticSize(TUint32 aPageSize)
		{
		TUint32 size = iSizeUsed;
		if (iSizeUsed % aPageSize > 0)
			{
			//used size needs to be rounded up to nearest page size
			size = (iSizeUsed/aPageSize + 1)*aPageSize;
			}
		iSize = size;
		}

TInt E32Rofs::Create()
//
// This is the main entry point to the ROFS image creation
//
	{
	TUint8* addr=(TUint8*)iData;

	TRomNode* pRootDir = iObey->iRootDirectory;
	const TInt headerSize = KRofsHeaderSize;
	// Layout the directory structure. Does not actually write it
	// to the image. Returns the number of bytes used for the directory
	// structure within the image.
	const TInt directoryOffset = headerSize;
	const TInt directorySize = LayoutDirectory( pRootDir, directoryOffset );
	if( directorySize <= 0 )
		{
		Print(EError, "Failed laying out directories - return code %d\n", directorySize);
		return KErrGeneral;
		}

	// get offset to start of file data, rounded up to next word boundary
	TInt offs = headerSize + directorySize;
	const TInt fileDataStartOffset = offs + ( (4 - offs) & 3);

	// Now we traverse the list of entries placing each file
	// This updates the directory entries to point to the correct offset
	// to the start of the file
	const TInt fileDataSize = PlaceFiles( pRootDir, addr + fileDataStartOffset, fileDataStartOffset );
	if( fileDataSize < 0 )
		{
		Print(EError, "Failed placing files - return code %d\n", fileDataSize);
		return KErrGeneral;
		}

	// and then put the directory into the image
	TInt err = PlaceDirectory( pRootDir, addr );
	if( err != KErrNone )
		{
		Print(EError, "Failed placing directory - return code %d\n", err);
		return err;
		}
		
	// Now write the header
	TRofsHeader* pHeader = (TRofsHeader*)iData;
	pHeader->iIdentifier[0] = 'R';
	pHeader->iIdentifier[1] = 'O';
	pHeader->iIdentifier[2] = 'F';
	pHeader->iIdentifier[3] = 'S';
	pHeader->iHeaderSize = KExtensionRofsHeaderSize;
	pHeader->iDirTreeOffset = directoryOffset;
	pHeader->iDirTreeSize = iTotalDirectoryBlockSize;
	pHeader->iDirFileEntriesOffset = directoryOffset + iTotalDirectoryBlockSize;
	pHeader->iDirFileEntriesSize = iTotalFileBlockSize;
	pHeader->iRofsFormatVersion = KRofsFormatVersion;
	pHeader->iTime = iObey->iTime;
	iSizeUsed = fileDataSize + fileDataStartOffset;
	pHeader->iImageSize = iSizeUsed;
	pHeader->iImageVersion = iObey->iVersion;
	if (iObey->AutoSize())
		MakeAutomaticSize(iObey->AutoPageSize()); // change iSize to nearest page size
		
	pHeader->iMaxImageSize = iSize;
	pHeader->iCheckSum = 0;		// not used yet

	return KErrNone;
	}
void E32Rofs::DisplaySizes(TPrintType aWhere)
{
	Print(aWhere, "Summary of file sizes in rofs:\n");
	TRomBuilderEntry *file=iObey->FirstFile();
	while(file)
		{
		file->DisplaySize(aWhere);
		file=iObey->NextFile();
		}
	Print( aWhere, "Directory block size: %d\n"
					"File block size:      %d\n"
					"Total directory size: %d\n"
					"Total image size:     %d\n",
					iTotalDirectoryBlockSize,
					iTotalFileBlockSize,
					iTotalDirectoryBlockSize + iTotalFileBlockSize,
					iSizeUsed );

}

void E32Rofs::LogExecutableAttributes(E32ImageHeaderV *aHdr)
{
	Print(ELog, "Uids:                    %08x %08x %08x %08x\n", aHdr->iUid1, aHdr->iUid2, aHdr->iUid3, aHdr->iUidChecksum);
	Print(ELog, "Data size:               %08x\n", aHdr->iDataSize);
	Print(ELog, "Heap min:                %08x\n", aHdr->iHeapSizeMin);
	Print(ELog, "Heap max:                %08x\n", aHdr->iHeapSizeMax);
	Print(ELog, "Stack size:              %08x\n", aHdr->iStackSize);
	Print(ELog, "Secure ID:               %08x\n", aHdr->iS.iSecureId);
	Print(ELog, "Vendor ID:               %08x\n", aHdr->iS.iVendorId);
	Print(ELog, "Priority:                %d\n\n", aHdr->iProcessPriority);
}
class Worker : public boost::thread {
    public:
    static void thrd_func(E32Rofs* rofs){
        CBytePair bpe(gFastCompress);

        bool deferred = false;
        TPlacingSection* p = rofs->GetFileNode(deferred);
        while(p) {
            if(!deferred) 
                p->len = p->node->PlaceFile(p->buf, (TUint32)-1, 0, &bpe);
            p = rofs->GetFileNode(deferred);
        }
        rofs->ArriveDeferPoint();
        p = rofs->GetDeferredJob();
        while(p) {
            p->len = p->node->PlaceFile(p->buf, (TUint32)-1, 0, &bpe);
            p = rofs->GetDeferredJob();
        }
    }
    Worker(E32Rofs* rofs) : boost::thread(thrd_func,rofs) {
    }
};

TPlacingSection* E32Rofs::GetFileNode(bool &aDeferred) {
    //get a node from the node list, the node list is protected by mutex iMuxTree.
    //The iMuxTree also helps to make sure the order in iVPS is consistent with the node list.
    //And this is the guarantee of same outputs regardless of how many threads being used.
    boost::mutex::scoped_lock lock(iMuxTree);
    aDeferred = false;
    TRomNode* node = iLastNode;
    while(node) {
        if( node->IsFile()) {
            if(!node->iHidden) {
                iLastNode = node->NextNode();
                break;
            }
        }
        node = node->NextNode();
    }

    if(node && node->IsFile() && !node->iHidden) {
        TPlacingSection* pps = new TPlacingSection(node);
        iVPS.push_back(pps);
        if(node->iAlias) {
            iQueueAliasNode.push(pps);
            aDeferred =  true;
        }
        return pps;
    }
    return NULL;
}
TPlacingSection* E32Rofs::GetDeferredJob() {
    // waiting all the normal node have been placed.
    while(iWorkerArrived < gThreadNum)
        boost::this_thread::sleep(boost::posix_time::milliseconds(10));

    // now we can safely handle the alias nodes.
    boost::mutex::scoped_lock lock(iMuxTree);
    TPlacingSection* p = NULL;
    if(!iQueueAliasNode.empty()) {
        p = iQueueAliasNode.front();
        iQueueAliasNode.pop();
    }
    return p;
}
void E32Rofs::ArriveDeferPoint() {
    boost::mutex::scoped_lock lock(iMuxTree);
    ++iWorkerArrived;
}
TInt E32Rofs::PlaceFiles( TRomNode* /*aRootDir*/, TUint8* aDestBase, TUint aBaseOffset, TInt aCoreSize ) 
	//
	// Traverses all entries placing all files and updating directory entry pointers.
	// Returns number of bytes placed or -ve error code.
	//
	{
            iLastNode = TRomNode::FirstNode();
            iWorkerArrived = 0;

            boost::thread_group thrds;
            Worker** workers = new Worker*[gThreadNum];
            int i;
            for (i = 0; i < gThreadNum; ++i) {
                workers[i] = new Worker(this);
                thrds.add_thread(workers[i]);
            }

            thrds.join_all();
            delete [] workers;

            TUint offset = aBaseOffset;
            TUint8* dest = aDestBase;
            TBool aIgnoreHiddenAttrib = ETrue;
            TInt len = iVPS.size();
            TInt maxSize;
            for(i=0;i<len;i++) {
                maxSize = aCoreSize + iSize - offset;
                if(maxSize <= 0) {
                    // Image size is too low to proceed.
                    return maxSize;
                }
                if(iVPS[i]->node->iFileStartOffset != (TUint)KFileHidden) {
                    memcpy(dest,iVPS[i]->buf,iVPS[i]->len);
                    if(iVPS[i]->node->iEntry->iFileOffset == -1) {
                        iVPS[i]->node->iEntry->iFileOffset = offset;
                        iVPS[i]->node->iFileStartOffset = iVPS[i]->node->iEntry->iFileOffset;
                    }
                    else {
                        TRomBuilderEntry* aEntry = (TRomBuilderEntry*)(iVPS[i]->node->iFileStartOffset);
                        iVPS[i]->node->iFileStartOffset = aEntry->iFileOffset;
                    }
                }
                iVPS[i]->len += (4-iVPS[i]->len) & 3;// round up to next word boundary
                dest += iVPS[i]->len;
                offset += iVPS[i]->len;

                if(gLogLevel > DEFAULT_LOG_LEVEL )
                {
                    TRomNode* node = iVPS[i]->node;
                    TInt aLen = node->FullNameLength(aIgnoreHiddenAttrib);
                    char *aBuf = new char[aLen+1];
                    if(gLogLevel & LOG_LEVEL_FILE_DETAILS)
                    {
                        node->GetFullName(aBuf, aIgnoreHiddenAttrib);
                        if(node->iEntry->iFileName)
                            Print(ELog,"%s\t%d\t%s\t%s\n", node->iEntry->iFileName, node->iEntry->RealFileSize(), (node->iHidden || node->iEntry->iHidden)? "hidden":"", aBuf);
                        else
                            Print(ELog,"%s\t%s\n", (node->iHidden || node->iEntry->iHidden) ? "hidden":"", aBuf);
                    }

                    if(gLogLevel & LOG_LEVEL_FILE_ATTRIBUTES)
                    {
                        if(!node->iHidden && !node->iEntry->iHidden)
                            Print(ELog, "Device file name:        %s\n", aBuf);
                        if(node->iEntry->iExecutable)
                            LogExecutableAttributes((E32ImageHeaderV*)(dest-len));
                    }
                    delete[] aBuf;
                }
            }
	return offset - aBaseOffset;	// number of bytes used
	}






TInt E32Rofs::LayoutDirectory( TRomNode* /*aRootDir*/, TUint aBaseOffset ) 
	//
	// Creates the directory layout but does not write it to the image.
	// All directories are given a location in the image.
	// Returns the total number of bytes used for the directory (rounded
	// up to the nearest word) or a -ve error code.
	//
	{
	TRomNode* node = TRomNode::FirstNode();

	TUint offset = aBaseOffset;
	while( node )
		{
		if( node->IsDirectory())
			{
			// it is a directory block so we have to give it a location
			node->SetImagePosition( offset );
		
			// work out how much space it requires for the directory block
			TInt dirLen;
			TInt fileLen;
			TInt err = node->CalculateDirectoryEntrySize( dirLen, fileLen );
			if( err != KErrNone )
				{
				return err;
				}
			Print( ELog, "Directory '%s' @offs=0x%x, size=%d\n", node->iName, offset, dirLen );
			dirLen += (4-dirLen) & 3;	// round up to next word boundary
			offset += dirLen;
			}

		node = node->NextNode();
		}

	TInt totalDirectoryBlockSize = offset - aBaseOffset;	// number of bytes used
	totalDirectoryBlockSize += (4 - totalDirectoryBlockSize) & 3;		// round up

	// Now go round again placing the file blocks
	offset = aBaseOffset + totalDirectoryBlockSize;
	const TUint fileBlockStartOffset = offset;
	node = TRomNode::FirstNode();
	while( node )
		{
		if( node->IsDirectory() )
			{
			// work out how much space it requires for the file block
			TInt dummy;
			TInt fileLen;
			TInt err = node->CalculateDirectoryEntrySize( dummy, fileLen );

			if( err != KErrNone )
				{
				return fileLen;
				}
			if( fileLen )
				{
				node->SetFileBlockPosition( offset );
				Print( ELog, "File block for dir '%s' @offs=0x%x, size=%d\n", node->iName, offset, fileLen );
				}
		
			fileLen += (4-fileLen) & 3;	// round up to next word boundary
			offset += fileLen;
			}

		node = node->NextNode();
		}

	TInt totalFileBlockSize = offset - fileBlockStartOffset;	// number of bytes used
	totalFileBlockSize += (4 - totalFileBlockSize) & 3;		// round up

	iTotalDirectoryBlockSize = totalDirectoryBlockSize;
	iTotalFileBlockSize = totalFileBlockSize;

	return totalDirectoryBlockSize + totalFileBlockSize;
	}


TInt E32Rofs::PlaceDirectory( TRomNode* /*aRootDir*/, TUint8* aDestBase ) 
	//
	// Writes the directory into the image. 
	// Returns KErrNone on success, or error code
	//
	{
	TRomNode* node = TRomNode::FirstNode();

	while( node )
		{
		if( node->IsDirectory() )
			{
			TInt err = node->Place( aDestBase );
			if( err != KErrNone )
				{
				return err;
				}
			}
		node = node->NextNode();
		}
	return KErrNone;
	}

TInt E32Rofs::WriteImage( TInt aHeaderType )
	{
	ofstream romFile((const char *)iObey->iRomFileName,ios::binary);
	if (!romFile)
		return Print(EError,"Cannot open ROM file %s for output\n",iObey->iRomFileName);
	Write(romFile, aHeaderType);
	romFile.close();

	return KErrNone;
	}

TRomNode* E32Rofs::CopyDirectory(TRomNode*& aLastExecutable)
	{
	return iObey->iRootDirectory->CopyDirectory(aLastExecutable);
	}


void E32Rofs::Write(ofstream &os, TInt aHeaderType)

// Output a rom image

	{

	switch (aHeaderType)
		{
	default:
	case 0:
		Print(EAlways, "\nWriting Rom image without");
		break;
	case 2:
		Print(EAlways, "\nWriting Rom image with PE-COFF");
			{
			unsigned char coffhead[0x58] = {0};  // zero all the elements

			// fill in the constant bits
			// this is supposed to be simple, remember
			coffhead[1] = 0x0a;
			coffhead[2] = 0x01;
			coffhead[0x10] = 0x1c;
			coffhead[0x12] = 0x0f;
			coffhead[0x13] = 0xa1;
			coffhead[0x14] = 0x0b;
			coffhead[0x15] = 0x01;
			coffhead[0x26] = 0x40;
			coffhead[0x2a] = 0x40;
			coffhead[0x30] = 0x2e;
			coffhead[0x31] = 0x74;
			coffhead[0x32] = 0x65;
			coffhead[0x33] = 0x78;
			coffhead[0x34] = 0x74;
			coffhead[0x3a] = 0x40;
			coffhead[0x3e] = 0x40;
			coffhead[0x44] = 0x58;
			coffhead[0x54] = 0x20;

			// now fill in the text segment size
			*(TUint32 *) (&coffhead[0x18]) = ALIGN4K(iSizeUsed);
			*(TUint32 *) (&coffhead[0x40]) = ALIGN4K(iSizeUsed);

			os.write(reinterpret_cast<char *>(coffhead), sizeof(coffhead));
			}
		break;
		}
	Print(EAlways, " header to file %s\n", iObey->iRomFileName);
	os.write( iData, iSizeUsed );
	}


TRomNode* E32Rofs::RootDirectory()
	{
	return iObey->iRootDirectory;
	}
void E32Rofs::SetRootDirectory(TRomNode* aDir)
	{
	iObey->iRootDirectory = aDir;
	}
TText* E32Rofs::RomFileName()
	{
	return iObey->iRomFileName;
	}
TInt E32Rofs::Size()
	{
	return iSize;
	}

///////////////////