/*
* Copyright (c) 2006-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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <elfdefs.h>
#define DEFAULT_VERSION 2
#define ELFADDR(rtype, p, o) (rtype *)(((char *)p) + o)
FILE *OUTFILE;
void croak(char * s)
{
printf("GENINF ERROR: %s\n", s);
exit(EXIT_FAILURE);
}
void croak2(char * s1, char * s2)
{
printf("GENINF ERROR: %s %s\n", s1, s2);
exit(EXIT_FAILURE);
}
int GetFileSize(FILE * f, char * file)
{
int r = fseek(f, 0, SEEK_END);
if (r) croak2("can't seek to end of file:", file);
r = ftell(f);
rewind(f);
return r;
}
void EnsureElf(Elf32_Ehdr* eh)
{
if (!(eh->e_ident[EI_MAG0] == ELFMAG0 &&
eh->e_ident[EI_MAG1] == ELFMAG1 &&
eh->e_ident[EI_MAG2] == ELFMAG2 &&
eh->e_ident[EI_MAG3] == ELFMAG3))
croak("Invalid ELF magic.\n");
if (eh->e_ident[EI_CLASS] != ELFCLASS32)
croak("File is not a 32 bit ELF object file.\n");
if (eh->e_ident[EI_DATA] != ELFDATA2LSB)
croak("File data encoding is not Little Endian.\n");
if (eh->e_machine != EM_ARM)
croak("File does not target ARM/THUMB processors.\n");
}
Elf32_Ehdr* OpenElfFile (char* file)
{
Elf32_Ehdr* eh;
FILE* f = fopen(file, "rb");
if (!f)
croak2("can't open file:", file);
int fsize = GetFileSize(f, file);
eh = (Elf32_Ehdr*)malloc(fsize);
if (!eh)
croak("Out of memory");
if (fread(eh, fsize, 1, f) != 1)
croak2("Can't read file", file);
EnsureElf(eh);
return eh;
}
typedef struct NameList
{
NameList * nl_next;
char * nl_name;
unsigned int nl_size;
} NameList;
static NameList * ExportedData = 0;
static NameList * ExportedCode = 0;
//Workaround for compiler defect(To avoid export of static symbols)
static NameList * LocalSymbols = 0;
NameList* IsPresentInList(char * name, NameList * list)
{
NameList * e = list;
while (e) {
if (!strcmp(name, e->nl_name))
return e;
else
e = e->nl_next;
}
return 0;
}
NameList* IsExportedData(char * name)
{
return IsPresentInList(name,ExportedData);
}
NameList* IsExportedCode(char * name)
{
return IsPresentInList(name,ExportedCode);
}
//Workaround for compiler defect(To avoid export of local symbols)
NameList* IsLocalSymbol(char * name)
{
return IsPresentInList(name,LocalSymbols);
}
void AddToList(char * name, NameList *& list, unsigned int aSymSz)
{
NameList * ed = new NameList;
ed->nl_name = name;
ed->nl_size = aSymSz;
ed->nl_next = list;
list = ed;
}
void AddExportedData(char * name, unsigned int aSymSz)
{
AddToList(name, ExportedData, aSymSz);
}
void AddExportedCode(char * name, unsigned int aSymSz)
{
AddToList(name, ExportedCode, aSymSz);
}
//Workaround for compiler defect(To avoid export of local symbols)
void AddLocalSymbols(char * name)
{
AddToList(name, LocalSymbols, 0);
}
void InitSymbolsLists(Elf32_Ehdr * eh,Elf32_Shdr * shdr,int shnum)
{
Elf32_Sym * symtab = 0;
int nSyms = 0;
char * strtab = 0;
int i = 0;
for (i = 0; (i < shnum); i++) {
if (shdr[i].sh_type == SHT_SYMTAB) {
symtab = ELFADDR(Elf32_Sym, eh, shdr[i].sh_offset);
nSyms = shdr[i].sh_size / shdr[i].sh_entsize;
strtab = ELFADDR(char, eh, shdr[shdr[i].sh_link].sh_offset);
break;
}
}
for (i = 0; i < nSyms; i++) {
if (symtab[i].st_info == ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT)) {
char * name = ELFADDR(char, strtab, symtab[i].st_name);
AddExportedData(name, symtab[i].st_size);
} else if (symtab[i].st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC)) {
char * name = ELFADDR(char, strtab, symtab[i].st_name);
AddExportedCode(name, symtab[i].st_size);
}
else if (symtab[i].st_info == ELF32_ST_INFO(STB_LOCAL, STT_FUNC) ||
symtab[i].st_info == ELF32_ST_INFO(STB_LOCAL, STT_OBJECT)) {
// Workaround for compiler defect to avoid export of local symbols
char * name = ELFADDR(char, strtab, symtab[i].st_name);
AddLocalSymbols(name);
}
}
}
#define DIRECTIVE_TAG "#<SYMEDIT>#"
#define DIRECTIVE_TAG_SIZE (sizeof(DIRECTIVE_TAG) - 1)
#define EXPORT_DIRECTIVE "EXPORT "
#define EXPORT_DIRECTIVE_SIZE (sizeof(EXPORT_DIRECTIVE) - 1)
void PrintExportsFromSection(char * section, int size)
{
if (strncmp(DIRECTIVE_TAG, section, DIRECTIVE_TAG_SIZE) != 0)
croak("Unrecognized .directive tag");
section += (DIRECTIVE_TAG_SIZE + 1);
size -= (DIRECTIVE_TAG_SIZE + 1);
// The separator for the following entries is 0x0A (i.e. \n).
// We're only interested in lines starting with EXPORT
char eolchar = '\n';
NameList* aDataSymbol;
while (size > 0) {
char * eolp = (char *)memchr(section, eolchar, size);
int linelength = (eolp - section) + 1;
if (!strncmp(EXPORT_DIRECTIVE, section, EXPORT_DIRECTIVE_SIZE)) {
char * symbol = section + EXPORT_DIRECTIVE_SIZE;
int symbolsize = linelength - EXPORT_DIRECTIVE_SIZE - 1;
// null-terminate the string - doesn't matter that we side effect the
// section since we won't see it again and saves making a copy.
symbol[symbolsize] = 0;
if (IsExportedCode(symbol)) {
fprintf(OUTFILE, " %s\n", symbol);
} else if ( (aDataSymbol = IsExportedData(symbol)) != 0) {
fprintf(OUTFILE, " %s DATA %d\n", symbol, aDataSymbol->nl_size);
}
else {
//All those symbols included from static libs that are
//treated as exported because of the dll_runtime compiler
//option.Such symbols donot figure out in the symbol table.
//Hence are handled separately here.
//Workaround for compiler defect - To avoid export of local symbols
if(!IsLocalSymbol(symbol))
{
fprintf(OUTFILE, " %s\n", symbol);
}
}
}
size -= linelength;
section += linelength;
}
}
void PrintABIv1ExportSymbols (Elf32_Ehdr * eh)
{
int shoff = eh->e_shoff; // offset of section header table
if (shoff) {
Elf32_Shdr * shdr = ELFADDR(Elf32_Shdr, eh, shoff);
int shnum = eh->e_shnum; // number of section headers
// e_shnum will be 0 if the number of sections is >= SHN_LORESERVE (0xff00)
// If this is the case, sh_size contains the actual number of section headers.
// If sh_size is 0, there really aren't any section headers.
if (!shnum)
shnum = shdr->sh_size;
int shstrndx = eh->e_shstrndx;
// If the section name string table index is >= SHN_LORESERVE (0xff00), shstrndx
// contains SHN_XINDEX (0xffff).
// If this is the case, sh_link contains the actual index of the section name string
// table, otherwise sh_link is 0.
if (shstrndx >= 65535)
shstrndx = shdr->sh_link;
// Initialize list of global data symbols
InitSymbolsLists(eh,shdr,shnum);
int snameoffset = shdr[shstrndx].sh_offset; // offset in file of sections' names
char * sname = ELFADDR(char, eh, snameoffset);
for (int i = 0; i < shnum; i++) {
if (i != shstrndx) {
if (!strcmp(".directive", &sname[shdr[i].sh_name])) {
// we're in business. print the section to stdout
char * data = ELFADDR(char, eh, shdr[i].sh_offset);
int size = shdr[i].sh_size;
PrintExportsFromSection(data, size);
}
}
}
}
}
void PrintABIv2ExportSymbols(Elf32_Ehdr *eh)
{
int shoff = eh->e_shoff; // offset of section header table
if (shoff) {
Elf32_Shdr * shdr = ELFADDR(Elf32_Shdr, eh, shoff);
int shnum = eh->e_shnum; // number of section headers
// e_shnum will be 0 if the number of sections is >= SHN_LORESERVE (0xff00)
// If this is the case, sh_size contains the actual number of section headers.
// If sh_size is 0, there really aren't any section headers.
if (!shnum)
shnum = shdr->sh_size;
int shstrndx = eh->e_shstrndx;
// If the section name string table index is >= SHN_LORESERVE (0xff00), shstrndx
// contains SHN_XINDEX (0xffff).
// If this is the case, sh_link contains the actual index of the section name string
// table, otherwise sh_link is 0.
if (shstrndx >= 65535)
shstrndx = shdr->sh_link;
//Get the symbol table
Elf32_Sym * symtab = 0;
int nSyms = 0;
char * strtab = 0;
int i = 0;
for (i = 0; (i < shnum); i++) {
if (shdr[i].sh_type == SHT_DYNSYM) {
symtab = ELFADDR(Elf32_Sym, eh, shdr[i].sh_offset);
nSyms = shdr[i].sh_size / shdr[i].sh_entsize;
strtab = ELFADDR(char, eh, shdr[shdr[i].sh_link].sh_offset);
break;
}
}
for (i = 0; i < nSyms; i++) {
if (symtab[i].st_info == ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT)) {
char * name = ELFADDR(char, strtab, symtab[i].st_name);
char * CodeSection = ELFADDR(char, eh, shdr[symtab[i].st_shndx].sh_offset);
Elf32_Word *aLocation = ELFADDR(Elf32_Word, CodeSection, symtab[i].st_value);
int ordinal = *aLocation;
fprintf(OUTFILE, "%s %d DATA %d %d\n",name, ordinal, symtab[i].st_size);
} else if (symtab[i].st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC)) {
char * name = ELFADDR(char, strtab, symtab[i].st_name);
char * CodeSection = ELFADDR(char, eh, shdr[symtab[i].st_shndx].sh_offset);
Elf32_Word *aLocation = ELFADDR(Elf32_Word, CodeSection, symtab[i].st_value);
int ordinal = *aLocation;
fprintf(OUTFILE, "%s %d \n",name, ordinal);
}
}
}
}
void PrintSONAME(Elf32_Ehdr *eh)
{
int shoff = eh->e_shoff; // offset of section header table
if (shoff) {
Elf32_Shdr * shdr = ELFADDR(Elf32_Shdr, eh, shoff);
int shnum = eh->e_shnum; // number of section headers
// e_shnum will be 0 if the number of sections is >= SHN_LORESERVE (0xff00)
// If this is the case, sh_size contains the actual number of section headers.
// If sh_size is 0, there really aren't any section headers.
if (!shnum)
shnum = shdr->sh_size;
int shstrndx = eh->e_shstrndx;
char *aSHdrStrTab = ELFADDR(char, eh, shdr[shstrndx].sh_offset);
int i;
Elf32_Verdef *aVerDef = 0;
char *aStringTab = 0;
for(i = 0; i < shnum; i++) {
if(strcmp(aSHdrStrTab + shdr[i].sh_name, ".version_d") == 0) {
aVerDef = ELFADDR(Elf32_Verdef,eh , shdr[i].sh_offset );
aStringTab = ELFADDR(char, eh, shdr[shdr[i].sh_link].sh_offset);
break;
}
}
if(!aVerDef)
return;
char *aLinkAsName;
while(aVerDef) {
if(aVerDef->vd_ndx == DEFAULT_VERSION){
Elf32_Verdaux *aVerAux = ELFADDR(Elf32_Verdaux, aVerDef, aVerDef->vd_aux);
aLinkAsName = ELFADDR(char, aStringTab, aVerAux->vda_name);
fprintf(OUTFILE, "%s \n", aLinkAsName);
break;
}
aVerDef = ELFADDR(Elf32_Verdef, aVerDef, aVerDef->vd_next);
}
}
}
int main(int argc, char** argv)
{
if( argc < 2 )
{
return EXIT_FAILURE;
}
char* file = argv[argc - 1];//The last arg is the file name
int idx = 0;
bool outFileOpt = false;
bool proxyDso = false;
bool soname = false;
while(idx < argc )
{
if((stricmp(argv[idx], "-o") == 0) && ((idx + 1) < argc) )
{
idx++;
char *outfilename = argv[idx];
OUTFILE = fopen(outfilename, "wb");
outFileOpt = true;
}
else if(stricmp(argv[idx], "-d") == 0)
{
proxyDso = true;
}
else if(stricmp(argv[idx], "-s") == 0)
{
soname = true;
}
idx++;
}
if(!outFileOpt)
{
OUTFILE = stdout;
}
Elf32_Ehdr * eh = OpenElfFile(file);
if( soname ) {
PrintSONAME(eh);
}
else if( !proxyDso ) {
PrintABIv1ExportSymbols(eh);
}
else {
PrintABIv2ExportSymbols(eh);
}
return EXIT_SUCCESS;
}