bintools/elftools/elftran/elf_file.cpp
author raptorbot <raptorbot@systemstesthead.symbian.intra>
Fri, 18 Dec 2009 19:57:42 +0000
branchwip
changeset 117 ecf683438dc6
parent 0 044383f39525
child 590 360bd6b35136
permissions -rw-r--r--
Don't mess around with EPOCROOT until actually entering raptor so we know what the original was Put the original epocroot back on the front of the whatcomp output. This allows what output to be either relative or absolute depending on what your epocroot is.

/*
* 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()
	{
	delete [] iFileName;
	delete iElfFile;
	delete iDllData;
	}


TBool ELFFile::Init(const TText * const aFileName)
//
// Read the ELF file into memory
//
 	{

 	delete [] iFileName;	
	iFileName = new TText[strlen((const char *)aFileName)+1];
	strcpy ((char *)iFileName, (const char *)aFileName);

	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 TText *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(TText *aName)
    {
	return (FindSymbol(aName) != 0);
    }

TBool ELFFile::GetExceptionIndexInfo(TUint32 &aOffset)
    {
    const TText * aBase = (TText *)".ARM.exidx$$Base";
    const TText * aLimit = (TText *)".ARM.exidx$$Limit";
	Elf32_Sym * exidxBase = FindSymbol(aBase);
	Elf32_Sym * exidxLimit = FindSymbol(aLimit);
	if (exidxBase && exidxLimit && (exidxLimit->st_value - exidxBase->st_value)) 
	    {
		const TText * aExceptionDescriptor = (TText *)"Symbian$$CPP$$Exception$$Descriptor";
		Elf32_Sym * aED = FindSymbol(aExceptionDescriptor);
		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);
}