// 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 "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 <time.h>
#include <malloc.h>
#include <string.h>
#include "e32image.h"
#include <e32std.h>
#include <e32std_private.h>
#include "pe_defs.h"
#include "pe_file.h"
#include "h_ver.h"
#include "h_utl.h"
int gAlignConstSection=FALSE;
TUint gConstSectionAddressMask=0;
static TUint gRequiredConstPadding;
extern char* gX86imp;
extern int gX86num_imp_dlls;
extern int gX86imp_size;
extern int gX86num_imports;
E32ImageFile* E32ImageFile::New()
{
return new E32ImageFile_PE;
}
E32ImageFile_PE::E32ImageFile_PE()
{
}
E32ImageFile_PE::~E32ImageFile_PE()
{
}
TUint E32ImageFile_PE::ImportAddressTableOffset()
//
// Return the offset of the iat
//
{
return iHdr->iTextSize;
}
TUint E32ImageFile_PE::ConstOffset()
//
// return the offset of the const data
//
{
return iConstOffset;
}
void E32ImageFile_PE::CreateExportDirectory(char *aPtr, PEFile &aPeFile)
//
// create a new format export directory
//
{
if (iHdr->iExportDirCount==0)
return;
TUint *src=(TUint *)aPeFile.iSectionData[KExportSection];
TUint *dst=(TUint *)aPtr;
PIMAGE_EXPORT_DIRECTORY dir=(PIMAGE_EXPORT_DIRECTORY)src;
src+=(((TInt)dir->AddressOfFunctions)-((TInt)aPeFile.iSectionHeader[KExportSection]->VirtualAddress))/4;
TUint i;
for (i=0; i<dir->NumberOfFunctions; i++)
{
TUint va=*src++;
dst[i]=va;
}
FixExportDirectory(dst, aPeFile);
}
void E32ImageFile_PE::FixExportDirectory(TUint *aExportDir, PEFile &aPeFile)
//
// Fix the export directory
//
{
TUint lb = aPeFile.iLinkedBase;
TUint *exportdir=aExportDir;
TInt n;
for (n=0; n<(TInt)iHdr->iExportDirCount; n++)
{
TUint va=*exportdir;
if (!gLittleEndian) ByteSwap(va);
// va is the address of an exported item, so assume it can't have been offset
TInt i=aPeFile.FindSectionByVa(va+lb);
if (i==KTextSection)
va=va-aPeFile.iSectionHeader[i]->VirtualAddress;
else if (i==KConstSection)
va=va-aPeFile.iSectionHeader[i]->VirtualAddress+ConstOffset();
else if (i==KDataSection)
va=va-aPeFile.iSectionHeader[i]->VirtualAddress+DataOffset();
else if (i==KBssSection)
va=va-aPeFile.iSectionHeader[i]->VirtualAddress+BssOffset();
else
{
if (va == 0)
Print(EWarning, "No export specified for ordinal %d\n", n+1, va);
else
Print(EError, "Export %d (address %08x) is not from .text, .rdata, or data sections\n", n+1, va);
}
if (!gLittleEndian) ByteSwap(va);
*exportdir++=va;
}
}
TInt E32ImageFile_PE::DoCodeHeader(PEFile &aPeFile)
//
// Calculate the code parts of the pefile
//
{
// .text
TInt size=ALIGN4(aPeFile.iSectionHeader[KTextSection]->Misc.VirtualSize);
// .rdata
iConstOffset=0;
if (gAlignConstSection)
{
// Compute the amount of padding to put before the
// const section to align it correctly
TUint oldAddressBits = aPeFile.iSectionHeader[KConstSection]->VirtualAddress & gConstSectionAddressMask;
TUint oldConstAddress = size;
TUint newConstAddress = oldConstAddress;
// slow but sure
while ((newConstAddress & gConstSectionAddressMask) != oldAddressBits)
{
newConstAddress++;
}
gRequiredConstPadding = newConstAddress - oldConstAddress;
size += gRequiredConstPadding;
}
if (aPeFile.iSectionHeader[KConstSection])
{
iConstOffset = size;
size += ALIGN4(aPeFile.iSectionHeader[KConstSection]->Misc.VirtualSize);
}
// .crt
iCrtOffset=0;
if (aPeFile.iSectionHeader[KCrtSection])
{
iCrtOffset = size;
size += ALIGN4(aPeFile.iSectionHeader[KCrtSection]->Misc.VirtualSize);
}
iHdr->iTextSize=size; // The "text" part of the E32 code section combines PE's .text + .rdata + .crt.
// The remainder of the E32 code section is the IAT + export directory.
// Import Address Table (IAT)
TInt nimports=gX86imp?gX86num_imports:aPeFile.NumberOfImports();
if (nimports!=0)
size+=nimports*4+4; // null terminated
// Export Dir
if (iHdr->iExportDirCount)
{
iHdr->iExportDirOffset = iHdr->iCodeOffset + size;
size += ALIGN4(iHdr->iExportDirCount*4);
}
iHdr->iCodeSize=size;
return size;
}
TInt E32ImageFile_PE::DoDataHeader(PEFile &aPeFile, TUint aDataBase)
//
//
//
{
if (aDataBase == (TUint)0)
aDataBase=iHdr->iCodeBase+iHdr->iCodeSize;
TInt size=0;
if (PEFile::HasInitialisedData(aPeFile.iSectionHeader[KDataSection]))
{
size=ALIGN4(aPeFile.iSectionHeader[KDataSection]->Misc.VirtualSize);
iHdr->iDataBase=aDataBase;
iHdr->iDataOffset = iHdr->iCodeOffset + iHdr->iCodeSize;
TInt bsssize=aPeFile.iSectionHeader[KDataSection]->Misc.VirtualSize-aPeFile.iSectionHeader[KDataSection]->SizeOfRawData;
// drop any uninitialised data
if (bsssize>0)
{
iHdr->iBssSize+=bsssize;
size=ALIGN4(aPeFile.iSectionHeader[KDataSection]->SizeOfRawData);
}
iHdr->iDataSize=size;
}
else if (aPeFile.iSectionHeader[KDataSection])
{ // just .bss
iHdr->iDataBase=aDataBase;
TInt bsssize=aPeFile.iSectionHeader[KDataSection]->Misc.VirtualSize;
iHdr->iBssSize+=bsssize;
}
if (aPeFile.iSectionHeader[KBssSection])
{
iHdr->iBssSize+=ALIGN4(aPeFile.iSectionHeader[KBssSection]->Misc.VirtualSize);
if (iHdr->iDataBase==0) // .bss but no .data
iHdr->iDataBase=aDataBase;
}
return size;
}
TInt E32ImageFile_PE::CopyCode(char *p, PEFile &aPeFile)
//
// Copies the files code sections to p
// returns the number of bytes copied or KErrGeneral
//
{
// text
TInt size=aPeFile.iSectionHeader[KTextSection]->Misc.VirtualSize;
memcpy(p, aPeFile.iSectionData[KTextSection], size);
TInt text_offset=ALIGN4(size);
p+=text_offset;
// rdata
if (aPeFile.iSectionData[KConstSection])
{
if (gAlignConstSection)
{
// add padding ahead of const section
p += gRequiredConstPadding;
}
TInt size=ALIGN4(aPeFile.iSectionHeader[KConstSection]->Misc.VirtualSize);
memcpy(p, aPeFile.iSectionData[KConstSection], size);
p+=size;
}
if (aPeFile.iSectionData[KCrtSection])
{
TInt size=ALIGN4(aPeFile.iSectionHeader[KCrtSection]->Misc.VirtualSize);
memcpy(p, aPeFile.iSectionData[KCrtSection], size);
p+=size;
}
// iat
TInt nimports=gX86imp?gX86num_imports:aPeFile.NumberOfImports();
// TInt nimports=aPeFile.NumberOfImports();
if (nimports)
{
if (gX86imp)
{
TUint *rdata=(TUint*)aPeFile.iSectionData[KConstSection];
int i;
int* s=(int*)gX86imp;
s++;
for (i=0; i<gX86num_imp_dlls; ++i)
{
++s;
int n=*s++;
while (n--)
{
*(int*)p=rdata[(*s)>>2]&0x7fffffffu; // rdata offset to ordinal
++s;
p+=4;
}
}
*(int*)p=0;
p+=4;
}
else
{
TInt r=CopyImportAddrTable(p, aPeFile);
p+=ALIGN4(nimports*4+4);
if (r!=KErrNone)
return Print(EError, "%s is importing symbols by name.\n", iFileName);
}
}
// export dir
CreateExportDirectory(p, aPeFile);
p+=iHdr->iExportDirCount*4;
return iHdr->iCodeSize;
}
TInt E32ImageFile_PE::CopyData(char *p, PEFile &aPeFile)
{
if (iHdr->iDataSize)
memcpy(p, aPeFile.iSectionData[KDataSection], iHdr->iDataSize);
return iHdr->iDataSize;
}
TInt E32ImageFile_PE::Translate(const char* aFileName, TUint aDataBase, TBool aAllowDllData, TBool /*aSymLkupEnabled*/)
//
// Translate a PE format file to a E32Image file
//
{
iSource = EPeFile;
PEFile pefile;
if (!pefile.Init((const char * const)aFileName))
return KErrGeneral;
TInt r=pefile.ReadSectionHeaders();
if (r!=KErrNone) return r;
r=pefile.ReadData();
if (r!=KErrNone) return r;
pefile.Close();
r=pefile.Normalise();
if (r!=KErrNone) return r;
iFileName = strdup(aFileName);
Adjust(ALIGN4(sizeof(E32ImageHeaderV))); // fixed for now because holes not supported
SetDefaultHeader();
if (gX86imp)
iHdr->iDllRefTableCount=gX86num_imp_dlls;
else
iHdr->iDllRefTableCount=pefile.NumberOfImportDlls();
iHdr->iExportDirCount=pefile.NumberOfExports();
iHdr->iCodeBase=pefile.iLinkedBase;
TInt nimports=gX86imp?gX86num_imports:pefile.NumberOfImports();
TInt importSectionSize;
char *newImportSection=CreateImportSection(pefile, importSectionSize);
TInt size = ALIGN4(sizeof(E32ImageHeaderV)); // fixed for now because holes not supported
iHdr->iCodeOffset = size;
TInt pos = size;
size+=DoCodeHeader(pefile);
TInt t=DoDataHeader(pefile, aDataBase);
if (t>0)
{
iHdr->iDataOffset = size;
size += t;
}
if (importSectionSize!=0)
{
iHdr->iImportOffset = size;
size += importSectionSize;
}
char *newCodeRelocs=NULL;
char *newDataRelocs=NULL;
TInt codeRelocSize=0, dataRelocSize=0;
TInt nrelocs=pefile.NumberOfRelocs();
if (nrelocs)
{
TUint *relocs=new TUint [nrelocs];
TUint *relocsection=new TUint [nrelocs];
pefile.GetRelocs(relocs, relocsection, nrelocs);
FixRelocs(pefile, relocs, relocsection, nrelocs);
newCodeRelocs=CreateCodeRelocs(relocs, relocsection, nrelocs, codeRelocSize);
newDataRelocs=CreateDataRelocs(relocs, relocsection, nrelocs, dataRelocSize);
if (codeRelocSize)
{
iHdr->iCodeRelocOffset = size;
size += codeRelocSize;
}
if (dataRelocSize)
{
iHdr->iDataRelocOffset = size;
size += dataRelocSize;
}
delete [] relocs;
delete [] relocsection;
}
Adjust(size);
t=CopyCode(iData + pos, pefile);
if (t<0)
return KErrGeneral;
pos += t;
pos += CopyData(iData + pos, pefile);
if (nimports)
{
memcpy(iData + pos, newImportSection, importSectionSize);
pos += importSectionSize;
}
if (codeRelocSize)
{
memcpy(iData + pos, newCodeRelocs, codeRelocSize);
pos += codeRelocSize;
}
if (dataRelocSize)
{
memcpy(iData + pos, newDataRelocs, dataRelocSize);
pos += dataRelocSize;
}
// locate the entry point
// entry point must be in the text section
TInt entryPointSectionIndex=pefile.FindSectionByVa(pefile.iEntryPoint+pefile.iLinkedBase);
TUint entryPointOffset=pefile.iEntryPoint-pefile.iSectionHeader[entryPointSectionIndex]->VirtualAddress;
if (entryPointSectionIndex!=KTextSection)
return Print(EError, "Entry Point not in code section\n");
// Arrange a header for this E32 Image
switch (pefile.iCpu)
{
case IMAGE_FILE_MACHINE_I386:
iHdr->iCpuIdentifier = (TUint16)ECpuX86;
break;
case 0x0a00:
iHdr->iCpuIdentifier = (TUint16)ECpuArmV4;
break;
case 0x0b00:
iHdr->iCpuIdentifier = (TUint16)ECpuMCore;
break;
default:
iHdr->iCpuIdentifier = (TUint16)ECpuUnknown;
break;
}
// Import format is PE-derived without redundant ordinal lists
// ABI is GCC98r2 ABI (on ARM)
iHdr->iFlags |= KImageImpFmt_PE2;
if (pefile.iImageIsDll)
{
iHdr->iFlags|=KImageDll;
if (iHdr->iDataSize && !aAllowDllData)
return Print(EError, "Dll '%s' has initialised data.\n", iFileName);
if (iHdr->iBssSize && !aAllowDllData)
return Print(EError, "Dll '%s' has uninitialised data.\n", iFileName);
}
iHdr->iHeapSizeMin=pefile.iHeapCommittedSize;
iHdr->iHeapSizeMax=pefile.iHeapReservedSize;
iHdr->iStackSize=pefile.iStackCommittedSize;
iHdr->iEntryPoint=entryPointOffset;
r = DetermineEntryPointType();
if (r == KErrCorrupt)
return Print(EError, "File '%s': Bad Entry Point.\n", iFileName);
else if (r == KErrNotSupported)
return Print(EError, "File '%s': Bad Entry Point Type.\n", iFileName);
delete [] newImportSection;
delete [] newCodeRelocs;
delete [] newDataRelocs;
return KErrNone;
}
TBool E32ImageFile_PE::Translate(PEFile &aPeFile)
//
//
//
{
return Translate((const char*)aPeFile.iFileName, (TUint)0, EFalse, EFalse);
}