/*
* Copyright (c) 2001-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 <e32def.h>
#include <e32std.h>
#include "elftran.h"
#include "elfdefs.h"
#include "elffile.h"
#include "elfdll.h"
#include "h_utl.h"
#include <string.h>
#include <stdlib.h>

TBool hadText, hadReloc = EFalse;

ELFFile::ELFFile() 
    :	iHeapCommittedSize(0x1000), iHeapReservedSize(0x100000), iStackCommittedSize(0),
		iFileName(0), iFileHandle(-1) , iElfFile(0), 
  	iDynamicSegmentHdr(0), iDynamicSegmentIdx(0),
		iCodeSegmentHdr(0), iCodeSegmentIdx(0), 
		iDataSegmentHdr(0), iDataSegmentIdx(0),
  	iDllData(0), iCpu(ECpuUnknown)
  	{}

ELFFile::~ELFFile()
  //
  // Destructor
  //
	{
	if(iFileName)
		delete [] iFileName;
	delete iElfFile;
	delete iDllData;
	}


TBool ELFFile::Init(const char* aFileName)
//
// Read the ELF file into memory
//
 	{
	if(iFileName){
		delete [] iFileName;	
		iFileName = 0;
	}
	size_t length = strlen(aFileName) + 1 ;
	iFileName = new char[length];
	memcpy (iFileName, aFileName,length);

	TInt error = HFile::Open(iFileName, &iFileHandle);
	if (error!=0)
		return EFalse;

	TInt flength = HFile::GetLength(iFileHandle);

	iElfFile = (Elf32_Ehdr *)HMem::Alloc(0,flength);
	if (!iElfFile)
		{
		Print(EPeError,"Failed to allocate memory to read in file.\n");
		Close();
		return EFalse;
		}

	if (!HFile::Read(iFileHandle,iElfFile,flength))
		{
		Print(EPeError,"Unable to read file %s.\n",iFileName);
		Close();
		return EFalse;
		}

        Close();

	if (!IsValidFileHeader(iElfFile))
		{
		Print(EPeError,"Invalid file header.\n");
		return EFalse;
		}
	// we only support this....for the moment
	iCpu = ECpuArmV4;

	if (!InitHeaders()) return EFalse;
	
	if (!InitDllData()) return EFalse;

	iEntryPoint = iElfFile->e_entry;

	iCodeSize = GetCodeSize();
	iDataSize = GetDataSize();
	iBssSize = GetBssSize();

	iStackReservedSize = 0x2000;
	iStackCommittedSize = 0x2000;

 	iLinkedBase = iCodeSegmentHdr->p_vaddr;
	
	iImageIsDll = iDllData->ImageIsDll();

	return ETrue;
	}

char * ELFFile::CreateImportSection(TInt &aSize)
//
// get ELFDLLData to do it
//
	{
	TInt size;
	char * newSection = iDllData->CreateImportSection(size);
	aSize = size;
	return newSection;
	}

TUint ELFFile::GetExportTableOffset(void)
        {
	return iDllData->GetExportTableOffset();
	}

TBool ELFFile::InitHeaders(void)
        {
	TInt nphdrs = iElfFile->e_phnum;
	if (nphdrs)
	       {
	       // Find the dynamic segment
	       Elf32_Phdr * aPhdr = ELFADDR(Elf32_Phdr, iElfFile, iElfFile->e_phoff);
	       iPhdr = aPhdr;
	       for (TInt idx = 0; idx < nphdrs; idx++)
					{
					Elf32_Word ptype = aPhdr[idx].p_type;
					if (ptype == PT_DYNAMIC)
							{
							iDynamicSegmentHdr = &aPhdr[idx];
							iDynamicSegmentIdx = idx;
							}
      				else if (ptype == PT_LOAD && 
							 (aPhdr[idx].p_flags & (PF_X + PF_ARM_ENTRY)))
							{
							iCodeSegmentHdr = &aPhdr[idx];
							iCodeSegmentIdx = idx;
							}
      				else if (ptype == PT_LOAD && 
							 (aPhdr[idx].p_flags & (PF_W + PF_R)))
							{
							iDataSegmentHdr = &aPhdr[idx];
							iDataSegmentIdx = idx;
							}
					}
	       }
	
	// cache pointer to symbol table

	// Get section header table
	Elf32_Shdr * s = ELFADDR(Elf32_Shdr, iElfFile, iElfFile->e_shoff);
	// Index of section header for section header string table
	TInt stIdx = iElfFile->e_shstrndx;
	TInt symIdx = -1;
	// Section name string table
	char * stringtable = ELFADDR(char, iElfFile, s[stIdx].sh_offset);
	// the index at which we find '.symtab' is the index of the symtab section
	for (TInt idx = 0; idx < iElfFile->e_shnum; idx++)
	    {
		if (idx != stIdx)
		       {
		       if (!strcmp(&stringtable[s[idx].sh_name], ".symtab"))
			      {
			      symIdx = idx;
			      break;
			      }
		       }
		}
	if (symIdx == -1) return EFalse;

	// save section header table
	iSectionHeaderTable = s;
	// save the index
	iSymIdx = symIdx;	
	// here's the symbol table
	iSymTab = ELFADDR(Elf32_Sym, iElfFile, s[symIdx].sh_offset);
	return ETrue;
	}

TBool ELFFile::InitDllData(void)
	{
	if (!iDynamicSegmentHdr)
	       {
#if defined(_DEBUG)
	       Print(EWarning, "Image '%s' has no import/export data.\n", iFileName);
#endif
	       return ETrue;
	       }
	iDllData = new ELFDllData(this);
	if (!iDllData)
	       {
	       Print(EPeError, "Out of memory allocating DLL data\n");
	       return EFalse;
	       }

	Elf32_Dyn * dyn = ELFADDR(Elf32_Dyn, iElfFile, iDynamicSegmentHdr->p_offset);
	TInt idx = 0;
	TInt soNameOffset = 0;
	while(dyn[idx].d_tag != DT_NULL) // best to make it explicit
	       {
	       switch (dyn[idx].d_tag)
		      {
		      case DT_HASH:
			   iDllData->iHashTable = ELFADDR(Elf32_HashTable, dyn, dyn[idx].d_val);
			   break;
		      case DT_STRTAB:
			   iDllData->iDynStrTab = ELFADDR(char, dyn, dyn[idx].d_val);
			   break;
		      case DT_SYMTAB:
			   iDllData->iDynSymTab = ELFADDR(Elf32_Sym, dyn, dyn[idx].d_val);
			   break;
		      case DT_RELA:
			   iDllData->iRela = ELFADDR(Elf32_Rela, dyn, dyn[idx].d_val);
			   break;
		      case DT_RELASZ:
			   iDllData->iRelaSz = dyn[idx].d_val;
			   break;
		      case DT_RELAENT:
			   iDllData->iRelaSz = dyn[idx].d_val;
			   break;
		      case DT_STRSZ:
			   iDllData->iDynStrTabSize = dyn[idx].d_val;
			   break;
		      case DT_ARM_SYMTABSZ_21: //For RVCT2.1
			   //iDllData->iDynSymTabSize = dyn[idx].d_val;
		      case DT_ARM_SYMTABSZ:
			  /* This is same as DT_ARM_SYMTABSZ_21, but for RVCT 2.2
			   * The tag value has been changed for RVC2.2 from RVCT2.1.
			   * We just ignore this. i.e., we get the symbol table size
			   * from the nchain field of the hash table as noted in section
			   * 3.2.2.2 of the BPABI.
			   */
			   break;
		      case DT_SYMENT:
			   iDllData->iSymSize = dyn[idx].d_val;
			   break;
		      case DT_SONAME:
			   soNameOffset = dyn[idx].d_val;
			   break;
		      case DT_REL:
			   iDllData->iRel = ELFADDR(Elf32_Rel, dyn, dyn[idx].d_val);
			   break;
		      case DT_RELSZ:
			   iDllData->iRelSz = dyn[idx].d_val;
			   break;
		      case DT_RELENT:
			   iDllData->iRelEnt = dyn[idx].d_val;
			   break;
		      case DT_NEEDED:
			   iDllData->AddToDependency(dyn[idx].d_val);
			   break;
		      case DT_PLTRELSZ:
		      case DT_PLTGOT:
		      case DT_INIT:
		      case DT_FINI:
		      case DT_RPATH:
		      case DT_SYMBOLIC:
		      case DT_PLTREL:
		      case DT_DEBUG:
		      case DT_TEXTREL:
		      case DT_JMPREL:
		      case DT_BIND_NOW:
			   break;
		      default:
		           Print(EPeError,"Unrecognized Dyn Array tag in image '%s'.\n", iFileName);
			   return EFalse;
		      }
		      idx++;
	       }
	return iDllData->Init();
	}


void ELFFile::Close()
//
// close the ELF file
//
	{
	HFile::Close(iFileHandle);
	}



TInt ELFFile::NumberOfImports() const
//
// Count the total number of imports for this image
//
	{
	return iDllData->NumberOfImports();
	}

TInt ELFFile::NumberOfImportDlls() const
//
// Count the number of referenced Dlls
//
	{
	return iDllData->NumberOfImportDlls();
	}

TInt ELFFile::NumberOfExports() const
//
// Count the number of exported symbols
//
	{
	return iDllData->NumberOfExports();
	}

TInt ELFFile::NumberOfCodeRelocs() 
	{ 
	return iDllData->NumberOfCodeRelocs(); 
	}

TInt ELFFile::NumberOfDataRelocs() 
	{ 
	return iDllData->NumberOfDataRelocs(); 
	}

Elf32_Phdr * ELFFile::GetSegmentFromAddr(Elf32_Addr addr)
	{
	TInt nphdrs = iElfFile->e_phnum;
	for (TInt idx = 0; idx < nphdrs; idx++)
	        {
		// take advantage of unsignedness 
		if ((addr - iPhdr[idx].p_vaddr) < iPhdr[idx].p_memsz) return &iPhdr[idx];
		}
	return NULL;
	}


TInt ELFFile::NumberOfRelocs()
	{
	return iDllData->NumberOfRelocs();
	}

TUint16 ELFFile::GetRelocType(Elf32_Rel *aReloc)
    {
	// We work out the type by figuring out the segment of the reloc
	TInt segmentIdx = ELF32_R_SYM(aReloc->r_info);
	
	// check to see if its a reserved or special index.
	if ((!segmentIdx) || ((segmentIdx >= SHN_LORESERVE) && (segmentIdx <= SHN_HIRESERVE)))
		// up until now these have been treated as KInferredRelocType, so lets continue...
		return KInferredRelocType;
			
	// need to see if this section is executable or writable
	if (iPhdr[segmentIdx-1].p_flags & PF_X) 
		return KTextRelocType;
	if (iPhdr[segmentIdx-1].p_flags & PF_W) 
		return KDataRelocType;
	// perhaps we should error here.
	return KInferredRelocType;
	}

TBool ELFFile::GetRelocs(Elf32_Rel **aCodeRelocs, Elf32_Rel **aDataRelocs)
        {
	return iDllData->GetRelocs(aCodeRelocs, aDataRelocs);
	}

TUint ELFFile::GetCodeSize()
    {
	return iCodeSegmentHdr->p_filesz;
	}

TBool ELFFile::HasInitialisedData()
	{
	return iDataSegmentHdr != NULL && iDataSegmentHdr->p_filesz != 0;
	}

TUint ELFFile::GetDataSize()
    {
	return iDataSegmentHdr != NULL ? iDataSegmentHdr->p_filesz : 0;
	}

TBool ELFFile::HasBssData()
	{
	return iDataSegmentHdr != NULL && (iDataSegmentHdr->p_memsz - iDataSegmentHdr->p_filesz) != 0;
	}

TUint ELFFile::GetBssSize()
    {
	return iDataSegmentHdr != NULL ? iDataSegmentHdr->p_memsz - iDataSegmentHdr->p_filesz: 0;
	}






TBool ELFFile::IsValidFileHeader(Elf32_Ehdr * iElfFile)
 	{
        if (!(iElfFile->e_ident[EI_MAG0] == ELFMAG0 &&
	      iElfFile->e_ident[EI_MAG1] == ELFMAG1 &&
	      iElfFile->e_ident[EI_MAG2] == ELFMAG2 &&
	      iElfFile->e_ident[EI_MAG3] == ELFMAG3))
		{
                Print(EPeError,"Invalid ELF magic.\n");
		return EFalse;
		}

        if (iElfFile->e_ident[EI_CLASS] != ELFCLASS32)
		{
                Print(EPeError,"File is not a 32 bit object file.\n");
		return EFalse;
		}
        if (iElfFile->e_ident[EI_DATA] != ELFDATA2LSB)
		{
                Print(EPeError,"File data encoding is not Little Endian.\n");
		return EFalse;
		}

  	if (iElfFile->e_machine != EM_ARM)
		{
		Print(EPeError,"File does not target ARM/THUMB processors.\n");
		return EFalse;
		}

  	if (!(iElfFile->e_type == ET_EXEC || iElfFile->e_type == ET_DYN))
		{
		Print(EPeError,"File is neither an executable nor a shared object\n");
		return EFalse;
		}

	return ETrue;
	}


// Get details of the next import to fix-up in the current file. Fill in the name of the dll 
//it is imported from, the ordinal number and the address to write back to.
#define ORDINAL_DONE 0x40000000


// The following static functions are passed an array of PE files to operate on

Elf32_Sym * ELFFile::FindSymbol(const char* aName)
    {
	Elf32_Shdr * s = ELFADDR(Elf32_Shdr, iElfFile, iElfFile->e_shoff);	
	TInt symIdx = iSymIdx;
	Elf32_Sym * sym = iSymTab;
	TInt nSyms = s[symIdx].sh_size / s[symIdx].sh_entsize;
	char * symStringtable = ELFADDR(char, iElfFile, s[s[symIdx].sh_link].sh_offset);
	for (TInt jdx = 0; jdx < nSyms; jdx++)
	    {
		if (!strcmp(&symStringtable[sym[jdx].st_name], (char *)aName)) 
			return &sym[jdx];
		}
	return (Elf32_Sym *)0;
    }

TBool ELFFile::SymbolPresent(const char* aName)
    {
	return (FindSymbol(aName) != 0);
    }

TBool ELFFile::GetExceptionIndexInfo(TUint32 &aOffset)
    {
	Elf32_Sym * exidxBase = FindSymbol(".ARM.exidx$$Base");
	Elf32_Sym * exidxLimit = FindSymbol(".ARM.exidx$$Limit");
	if (exidxBase && exidxLimit && (exidxLimit->st_value - exidxBase->st_value)) 
	    {
		Elf32_Sym * aED = FindSymbol("Symbian$$CPP$$Exception$$Descriptor");
		if (aED) 
			{
			// Set bottom bit so 0 in header slot means an old binary.
			// The decriptor is always aligned on a 4 byte boundary.
			aOffset = (aED->st_value - iLinkedBase) | 0x00000001;
			return ETrue;
			}
		else
			{
			Print(EPeError,"Executable has exception table but no exception descriptor\n");
			exit(666);
			}
		}
	return EFalse;
    }

TBool ELFFile::SetUpLookupTable()
{
	if(!iDllData->CreateSymLookupTable() ) {
		Print(EPeError,"Failed to create named symbol lookup information\n");
		return FALSE;
	}
	if(!iDllData->CreateDependency()){
		Print(EPeError,"Failed to create dependency ordering for named symbol lookup\n");
		return FALSE;
	}

	iDllData->SetExportSymInfo();
	return TRUE;
}

void ELFFile::GetExportSymInfoHeader(E32EpocExpSymInfoHdr& aSymInfoHdr)
{
	iDllData->GetExportSymInfoHeader(aSymInfoHdr);
}

void ELFFile::SetLookupTblBase(TInt aBaseOffset)
{
	iDllData->SetLookupTblBase(aBaseOffset);
}

TInt ELFFile::GetLookupTblSize()
{
	return iDllData->GetLookupTblSize();
}

TUint ELFFile::GetSymLookupSection(char* aBuff)
{
	return iDllData->GetSymLookupSection(aBuff);
}

