/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libelf.h>
#include <map>
#include "elfsymboltablemanager.h"
void ElfFileSymbolFragments::AddSymbolTable(String & aPath, size_t aOffset, size_t aSize, size_t aFirstGlobal){
iPath = aPath;
// drop the inital 'undefined' symbol
iSymbolTableOffset = aOffset + sizeof(Elf32_Sym);
iSymbolTableSize = aSize - sizeof(Elf32_Sym);
iFirstGlobal = aFirstGlobal - 1;
}
void ElfFileSymbolFragments::AddStringTable(String & aPath, size_t aOffset, size_t aSize){
iPath = aPath;
// drop the inital "\0"
iStringTableOffset = aOffset + 1;
iStringTableSize = aSize - 1;
}
size_t ElfFileSymbolFragments::LookupSection(size_t ndx){
SectionNumberMap::iterator aMapping = iSectionNumberMap.begin();
SectionNumberMap::iterator end = iSectionNumberMap.end();
while (aMapping != end) {
if (aMapping->iOld == ndx)
return aMapping->iNew;
aMapping++;
}
return ndx;
}
int ElfFileSymbolFragments::LookupVaddrAddend(size_t ndx){
SectionVaddrAddendMap::iterator aMapping = iSectionVaddrAddendMap.begin();
SectionVaddrAddendMap::iterator end = iSectionVaddrAddendMap.end();
while (aMapping != end) {
if (aMapping->iSectionNumber == ndx)
return aMapping->iAddend;
aMapping++;
}
return 0;
}
size_t ElfSymTabStringTable::Size(){
return iElfSymbolTableManager.GetSymTabStringsSectionSize();
}
void ElfSymbolTableManager::Finalize(SectionNumberMap & aSectionNumberMap, SectionVaddrAddendMap & aSectionVaddrAddendMap ){
iCurrentFragment.SetSectionNumberMap(aSectionNumberMap);
iCurrentFragment.SetSectionVaddrAddendMap(aSectionVaddrAddendMap);
iCurrentFragment.Validate();
iSymbolFragments.push_back(iCurrentFragment);
iCurrentFragment.Reset();
}
// TODO: This could be done more efficiently and with out the use of the ElfStringTable object.
void ElfSymbolTableManager::GetFileFragmentData(FileFragmentData & aFileFragmentData ){
size_t symTabSize = GetSymTabSectionSize();
iData = new char[symTabSize];
Elf32_Sym * syms = (Elf32_Sym *)iData;
// set up UNDEF symbol
syms[0].st_info = 0;
syms[0].st_name = 0;
syms[0].st_other = 0;
syms[0].st_shndx = 0;
syms[0].st_size = 0;
syms[0].st_value = 0;
// set up 'cursors' into final symbol table so we put locals first
// and globals at the end
Elf32_Sym * lsym = &syms[1];
size_t firstGlobal = GetFirstNonLocalIndex();
Elf32_Sym * gsym = &syms[firstGlobal];
Elf32_Sym * lsymLim = gsym;
iStringTable.AllocateInitialNullString();
SymbolFragmentList::iterator aFrag = iSymbolFragments.begin();
SymbolFragmentList::iterator end = iSymbolFragments.end();
while (aFrag != end) {
//InputFile aFile((char *)(aFrag->GetPath().c_str()));
InputFile aFile(aFrag->GetPath());
aFile.SetOffset(aFrag->GetSymbolTableOffset());
size_t symSize = aFrag->GetSymbolTableSize();
size_t limit = symSize / sizeof(Elf32_Sym);
char * symtabData = aFile.GetData(symSize);
Elf32_Sym * symtab = (Elf32_Sym *)symtabData;
aFile.SetOffset(aFrag->GetStringTableOffset());
char * strtabx = aFile.GetData(aFrag->GetStringTableSize());
// set strtab back one to 'add' "\0" back in so indexs works with addition.
char * strtab = strtabx - 1;
size_t firstNonLocal = aFrag->GetFirstGlobal();
typedef std::map<size_t, size_t> SymbolNdxMap;
SymbolNdxMap symNdxMap;
for (size_t i = 0; i < limit; i++){
size_t strndx = symtab[i].st_name;
if (strndx != 0) {
// see if we've already seen this index
SymbolNdxMap::iterator res = symNdxMap.find(strndx);
size_t newndx;
if (res != symNdxMap.end()){
newndx = res->second;
symtab[i].st_name = newndx;
} else {
char * name = &strtab[strndx];
newndx = iStringTable.AddString(name);
symNdxMap[strndx] = symtab[i].st_name = newndx;
}
}
if (!(symtab[i].st_value || symtab[i].st_size)){
symtab[i].st_shndx = SHN_UNDEF;
} else {
size_t oldNdx = symtab[i].st_shndx;
// retrieve new section index
size_t newscnndx = aFrag->LookupSection(oldNdx);
// retrieve the vaddr adjustment to add to the symbol's value
int addend = aFrag->LookupVaddrAddend(oldNdx);
symtab[i].st_shndx = newscnndx;
symtab[i].st_value += addend;
}
if (i < firstNonLocal){
assert(lsym < lsymLim);
*(lsym++) = symtab[i];
} else {
*(gsym++) = symtab[i];
}
}
delete [] symtabData;
delete strtabx;
aFrag++;
}
SetFileFragmentData(aFileFragmentData, symTabSize, reinterpret_cast<char *>(iData));
}
size_t ElfSymbolTableManager::Size(){
return GetSymTabSectionSize();
}
void ElfSymbolTableManager::DeleteFileFragmentData(){
char * d = iData;
iData = NULL;
delete [] d;
}
void ElfSymbolTableManager::AddData(OutputFile & aOutputFile){
const FileFragment & aSectionFrag = iOutputFile.GetFileFragment(this);
SetOffset(aSectionFrag.GetOffset());
}
void ElfSymbolTableManager::AddSymbolTable(){
// The sym table section needs to record the index of its associated
// string table in its link field and record the index of the first non-local
// symbol in its info field
int symTabSize = GetSymTabSectionSize();
size_t firstNonLocal = GetFirstNonLocalIndex();
size_t nextSectionIndex = iElfSectionManager.NumSections();
ElfSectionElfData * aSymTabSectionData = new ElfSectionElfData(*this);
Elf32_Shdr symTabShdr;
symTabShdr.sh_name = 0; // for now.
symTabShdr.sh_type = SHT_SYMTAB;
symTabShdr.sh_flags = 0;
symTabShdr.sh_addr = 0;
symTabShdr.sh_offset = 0; // for now
symTabShdr.sh_size = symTabSize; // for now.
// symTabShdr will be @ index nextSectionIndex so the .strtab will
// be @ nextSectionIndex +1
symTabShdr.sh_link = nextSectionIndex + 1;
symTabShdr.sh_info = firstNonLocal;
symTabShdr.sh_addralign = 4;
symTabShdr.sh_entsize = sizeof(Elf32_Sym);
ElfSection aSymTabSection(aSymTabSectionData, ".symtab", symTabShdr);
iElfSectionManager.AddSection(aSymTabSection);
ElfSectionElfData * aStringTableSectionData = new ElfSectionElfData(iStringTable);
Elf32_Shdr shdr;
shdr.sh_name = 0; // for now.
shdr.sh_type = SHT_STRTAB;
shdr.sh_flags = 0;
shdr.sh_addr = 0;
shdr.sh_offset = 0; // for now
shdr.sh_size = GetSymTabStringsSectionSize();
shdr.sh_link = 0;
shdr.sh_info = 0;
shdr.sh_addralign = 0;
shdr.sh_entsize = 0;
ElfSection aStringTableSection(aStringTableSectionData, ".strtab", shdr);
iElfSectionManager.AddSection(aStringTableSection);
}
size_t ElfSymbolTableManager::GetSymTabSectionSize(){
int symTabSize = sizeof(Elf32_Sym); // add the 'undefined' symbols
SymbolFragmentList::iterator aFrag = iSymbolFragments.begin();
SymbolFragmentList::iterator end = iSymbolFragments.end();
while (aFrag != end) {
symTabSize += aFrag->GetSymbolTableSize();
aFrag++;
}
return symTabSize;
}
size_t ElfSymbolTableManager::GetSymTabStringsSectionSize(){
if (iSymbolStringTableSizeValid)
return iSymbolStringTableSize;
int stringsSize = 1; // add the leading "\0"
SymbolFragmentList::iterator aFrag = iSymbolFragments.begin();
SymbolFragmentList::iterator end = iSymbolFragments.end();
while (aFrag != end) {
stringsSize += aFrag->GetStringTableSize();
aFrag++;
}
iSymbolStringTableSizeValid = true;
return iSymbolStringTableSize = stringsSize;
}
size_t ElfSymbolTableManager::GetFirstNonLocalIndex(){
int ndx = 1; // add the 'undefined' symbols
SymbolFragmentList::iterator aFrag = iSymbolFragments.begin();
SymbolFragmentList::iterator end = iSymbolFragments.end();
while (aFrag != end) {
ndx += aFrag->GetFirstGlobal();
aFrag++;
}
return ndx;
}