linux/src/mod/elf.cpp

244 lines
8.2 KiB
C++

/*
* elf.cpp
*
* Copyright (C) 2005 Bastian Blank <waldi@debian.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "elf.hpp"
#include "endian.hpp"
#include <stdexcept>
#include <fcntl.h>
#include <elf.h>
#include <sys/mman.h>
#include <sys/stat.h>
using namespace Elf;
namespace
{
template <typename _class>
struct _elfdef
{ };
template <>
struct _elfdef<file_class_32>
{
typedef Elf32_Ehdr Ehdr;
typedef Elf32_Shdr Shdr;
typedef Elf32_Sym Sym;
static inline uint8_t st_bind (uint8_t st_info) throw () { return ELF32_ST_BIND (st_info); }
static inline uint8_t st_type (uint8_t st_info) throw () { return ELF32_ST_TYPE (st_info); }
};
template <>
struct _elfdef<file_class_64>
{
typedef Elf64_Ehdr Ehdr;
typedef Elf64_Shdr Shdr;
typedef Elf64_Sym Sym;
static inline uint8_t st_bind (uint8_t st_info) throw () { return ELF64_ST_BIND (st_info); }
static inline uint8_t st_type (uint8_t st_info) throw () { return ELF64_ST_TYPE (st_info); }
};
}
file::file (const char *filename, void *mem, size_t len) throw (std::bad_alloc)
: filename (std::string (filename)), mem (mem), len (len)
{ }
file::~file () throw ()
{
::munmap (mem, len);
for (std::vector<section *>::iterator it = sections.begin (); it != sections.end (); ++it)
delete *it;
}
file *file::open (const char *filename) throw (std::bad_alloc, std::runtime_error)
{
struct stat buf;
int fd;
void *mem;
size_t len;
if ((fd = ::open (filename, O_RDONLY)) == -1)
throw std::runtime_error ("mapping failed");
try
{
if (::fstat (fd, &buf) == -1)
throw std::runtime_error ("mapping failed");
len = buf.st_size;
if ((mem = ::mmap (0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)) == MAP_FAILED)
throw std::runtime_error ("mapping failed");
const uint8_t *buf = static_cast <uint8_t *> (mem);
switch (buf[EI_CLASS])
{
case ELFCLASS32:
return open_class<file_class_32> (filename, buf, mem, len);
case ELFCLASS64:
return open_class<file_class_64> (filename, buf, mem, len);
default:
throw std::runtime_error ("Invalid file class");
}
}
catch (...)
{
::close (fd);
throw;
}
}
template<typename _class>
file *file::open_class (const char *filename, const uint8_t *buf, void * mem, size_t len) throw (std::bad_alloc, std::runtime_error)
{
switch (buf[EI_DATA])
{
case ELFDATA2LSB:
return new file_data<_class, file_data_2LSB> (filename, mem, len);
case ELFDATA2MSB:
return new file_data<_class, file_data_2MSB> (filename, mem, len);
default:
throw std::runtime_error ("Invalid file data");
}
}
template <typename _class, typename _data>
file_data<_class, _data>::file_data (const char *filename) throw (std::bad_alloc, std::runtime_error)
: file (filename)
{
construct ();
}
template <typename _class, typename _data>
file_data<_class, _data>::file_data (const char *filename, void *mem, size_t len) throw (std::bad_alloc, std::runtime_error)
: file (filename, mem, len)
{
construct ();
}
template <typename _class, typename _data>
void file_data<_class, _data>::construct () throw (std::bad_alloc, std::runtime_error)
{
uint8_t *buf = static_cast <uint8_t *> (this->mem);
if (buf[EI_CLASS] != _class::id)
throw std::runtime_error ("Wrong file class");
if (buf[EI_DATA] != _data::id)
throw std::runtime_error ("Wrong data encoding");
typedef typename _elfdef<_class>::Ehdr Ehdr;
Ehdr *ehdr = static_cast <Ehdr *> (this->mem);
this->type = convert<_data, typeof (ehdr->e_type )> () (ehdr->e_type );
this->machine = convert<_data, typeof (ehdr->e_machine )> () (ehdr->e_machine );
this->shoff = convert<_data, typeof (ehdr->e_shoff )> () (ehdr->e_shoff );
this->shnum = convert<_data, typeof (ehdr->e_shnum )> () (ehdr->e_shnum );
this->shstrndx = convert<_data, typeof (ehdr->e_shstrndx)> () (ehdr->e_shstrndx);
typedef typename _elfdef<_class>::Shdr Shdr;
Shdr *shdrs = static_cast <Shdr *> (static_cast <void *> (static_cast <char *> (this->mem) + this->shoff));
this->sections.reserve (this->shnum);
for (unsigned int i = 0; i < this->shnum; i++)
{
section *temp;
switch (convert<_data, typeof (shdrs[i].sh_type)> () (shdrs[i].sh_type))
{
case section_type_SYMTAB::id:
temp = new section_real<_class, _data, section_type_SYMTAB> (&shdrs[i], this->mem);
break;
default:
temp = new section_real<_class, _data, section_type_UNDEFINED> (&shdrs[i], this->mem);
break;
}
this->sections.push_back (temp);
}
for (unsigned int i = 0; i < this->shnum; i++)
this->sections[i]->update_string_table (this);
}
void section::update_string_table (file *file) throw (std::bad_alloc)
{
const section *section = file->get_section (file->get_shstrndx ());
this->name_string = std::string (static_cast <const char *> (section->_mem ()) + this->name);
}
template <typename _class, typename _data>
section_data<_class, _data>::section_data (void *header, void *mem) throw ()
{
typedef typename _elfdef<_class>::Shdr Shdr;
Shdr *shdr = static_cast <Shdr *> (header);
this->name = convert<_data, typeof (shdr->sh_name )> () (shdr->sh_name );
this->type = convert<_data, typeof (shdr->sh_type )> () (shdr->sh_type );
this->offset = convert<_data, typeof (shdr->sh_offset)> () (shdr->sh_offset);
this->size = convert<_data, typeof (shdr->sh_size )> () (shdr->sh_size );
this->link = convert<_data, typeof (shdr->sh_link )> () (shdr->sh_link );
this->mem = static_cast <void *> (static_cast <char *> (mem) + this->offset);
}
section_type<section_type_SYMTAB>::~section_type () throw ()
{
for (std::vector<symbol *>::iterator it = symbols.begin (); it != symbols.end (); ++it)
delete *it;
}
void section_type<section_type_SYMTAB>::update_string_table (file *file) throw (std::bad_alloc)
{
section::update_string_table (file);
for (unsigned int i = 0; i < symbols.size (); i++)
this->symbols[i]->update_string_table (file, link);
}
template <typename _class, typename _data>
section_real<_class, _data, section_type_SYMTAB>::section_real (void *header, void *mem) throw (std::bad_alloc)
: section_data<_class, _data> (header, mem)
{
if (this->type != SHT_SYMTAB)
throw std::logic_error ("Wrong section type");
typedef typename _elfdef<_class>::Sym Sym;
Sym *syms = static_cast <Sym *> (this->mem);
unsigned int max = this->size / sizeof (Sym);
this->symbols.reserve (max);
for (unsigned int i = 0; i < max; i++)
this->symbols.push_back (new symbol_data<_class, _data> (&syms[i]));
}
template <typename _class, typename _data>
symbol_data<_class, _data>::symbol_data (void *mem) throw ()
{
typedef typename _elfdef<_class>::Sym Sym;
Sym *sym = static_cast <Sym *> (mem);
this->name = convert<_data, typeof (sym->st_name )> () (sym->st_name);
this->info = convert<_data, typeof (sym->st_info )> () (sym->st_info);
this->shndx = convert<_data, typeof (sym->st_shndx)> () (sym->st_shndx);
this->value = convert<_data, typeof (sym->st_value)> () (sym->st_value);
this->size = convert<_data, typeof (sym->st_size )> () (sym->st_size);
this->bind = _elfdef<_class>::st_bind (this->info);
this->type = _elfdef<_class>::st_type (this->info);
}
template <typename _class, typename _data>
void symbol_data<_class, _data>::update_string_table (file *file, uint16_t s) throw (std::bad_alloc)
{
const section *section = file->get_section (s);
this->name_string = std::string (static_cast <const char *> (section->_mem ()) + this->name);
}