/*
* 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);
}