mirror of
https://git.rtems.org/rtems-tools/
synced 2025-05-14 08:09:15 +08:00
1268 lines
30 KiB
C++
1268 lines
30 KiB
C++
/*
|
|
* Copyright (c) 2011-2012, Chris Johns <chrisj@rtems.org>
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
/**
|
|
* @file
|
|
*
|
|
* @ingroup rtems-ld
|
|
*
|
|
* @brief RTEMS Linker ELF module manages the ELF format images.
|
|
*
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <rld.h>
|
|
|
|
namespace rld
|
|
{
|
|
namespace elf
|
|
{
|
|
/**
|
|
* Throw an ELF error.
|
|
*
|
|
* @param where Where the error is raised.
|
|
*/
|
|
void libelf_error (const std::string& where)
|
|
{
|
|
throw rld::error (::elf_errmsg (-1), "libelf:" + where);
|
|
}
|
|
|
|
/**
|
|
* We record the first class, machine and .. type of object file we get the
|
|
* header of and all header must match. We cannot mix object module types.
|
|
*/
|
|
static unsigned int elf_object_class = ELFCLASSNONE;
|
|
static unsigned int elf_object_machinetype = EM_NONE;
|
|
static unsigned int elf_object_datatype = ELFDATANONE;
|
|
|
|
/**
|
|
* A single place to initialise the libelf library. This must be called
|
|
* before any libelf API calls are made.
|
|
*/
|
|
static void
|
|
libelf_initialise ()
|
|
{
|
|
static bool libelf_initialised = false;
|
|
if (!libelf_initialised)
|
|
{
|
|
if (::elf_version (EV_CURRENT) == EV_NONE)
|
|
libelf_error ("initialisation");
|
|
libelf_initialised = true;
|
|
}
|
|
}
|
|
|
|
relocation::relocation (const symbols::symbol& sym,
|
|
elf_addr offset,
|
|
elf_xword info,
|
|
elf_sxword addend)
|
|
: sym (&sym),
|
|
offset_ (offset),
|
|
info_ (info),
|
|
addend_ (addend)
|
|
{
|
|
}
|
|
|
|
relocation::relocation ()
|
|
: sym (0),
|
|
offset_ (0),
|
|
info_ (0),
|
|
addend_ (0)
|
|
{
|
|
}
|
|
|
|
elf_addr
|
|
relocation::offset () const
|
|
{
|
|
return offset_;
|
|
}
|
|
|
|
uint32_t
|
|
relocation::type () const
|
|
{
|
|
return GELF_R_TYPE (info_);
|
|
}
|
|
|
|
elf_xword
|
|
relocation::info () const
|
|
{
|
|
return info_;
|
|
}
|
|
|
|
elf_sxword
|
|
relocation::addend () const
|
|
{
|
|
return addend_;
|
|
}
|
|
|
|
const symbols::symbol&
|
|
relocation::symbol () const
|
|
{
|
|
if (sym)
|
|
return *sym;
|
|
throw rld::error ("no symbol", "elf:relocation");
|
|
}
|
|
|
|
section::section (file& file_,
|
|
int index_,
|
|
const std::string& name_,
|
|
elf_word type,
|
|
elf_xword alignment,
|
|
elf_xword flags,
|
|
elf_addr addr,
|
|
elf_off offset,
|
|
elf_xword size,
|
|
elf_word link,
|
|
elf_word info,
|
|
elf_xword entry_size)
|
|
: file_ (&file_),
|
|
index_ (index_),
|
|
name_ (name_),
|
|
scn (0),
|
|
data_ (0),
|
|
rela (false)
|
|
{
|
|
if (!file_.is_writable ())
|
|
throw rld::error ("not writable",
|
|
"elf:section" + file_.name () + " (" + name_ + ')');
|
|
|
|
scn = ::elf_newscn (file_.get_elf ());
|
|
if (!scn)
|
|
libelf_error ("elf_newscn: " + name_ + " (" + file_.name () + ')');
|
|
|
|
if (::gelf_getshdr(scn, &shdr) == 0)
|
|
libelf_error ("gelf_getshdr: " + name_ + " (" + file_.name () + ')');
|
|
|
|
shdr.sh_name = 0;
|
|
shdr.sh_type = type;
|
|
shdr.sh_flags = flags;
|
|
shdr.sh_addr = addr;
|
|
shdr.sh_offset = offset;
|
|
shdr.sh_size = size;
|
|
shdr.sh_link = link;
|
|
shdr.sh_info = info;
|
|
shdr.sh_addralign = alignment;
|
|
shdr.sh_entsize = entry_size;
|
|
|
|
if (type == SHT_NOBITS)
|
|
add_data (ELF_T_BYTE, alignment, size);
|
|
|
|
if (!gelf_update_shdr (scn, &shdr))
|
|
libelf_error ("gelf_update_shdr: " + name_ + " (" + file_.name () + ')');
|
|
}
|
|
|
|
section::section (file& file_, int index_)
|
|
: file_ (&file_),
|
|
index_ (index_),
|
|
scn (0),
|
|
data_ (0),
|
|
rela (false)
|
|
{
|
|
memset (&shdr, 0, sizeof (shdr));
|
|
|
|
scn = ::elf_getscn (file_.get_elf (), index_);
|
|
if (!scn)
|
|
libelf_error ("elf_getscn: " + file_.name ());
|
|
|
|
if (!::gelf_getshdr (scn, &shdr))
|
|
libelf_error ("gelf_getshdr: " + file_.name ());
|
|
|
|
if (shdr.sh_type != SHT_NULL)
|
|
{
|
|
name_ = file_.get_string (shdr.sh_name);
|
|
data_ = ::elf_getdata (scn, 0);
|
|
if (!data_)
|
|
{
|
|
data_ = ::elf_rawdata (scn, 0);
|
|
if (!data_)
|
|
libelf_error ("elf_getdata: " + name_ + '(' + file_.name () + ')');
|
|
}
|
|
}
|
|
|
|
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
|
|
std::cout << "elf::section: index=" << index ()
|
|
<< " name='" << name () << "'"
|
|
<< " size=" << size ()
|
|
<< " align=" << alignment ()
|
|
<< " flags=0x" << std::hex << flags () << std::dec
|
|
<< std::endl;
|
|
}
|
|
|
|
section::section (const section& orig)
|
|
: file_ (orig.file_),
|
|
index_ (orig.index_),
|
|
name_ (orig.name_),
|
|
scn (orig.scn),
|
|
shdr (orig.shdr),
|
|
data_ (orig.data_),
|
|
rela (orig.rela),
|
|
relocs (orig.relocs)
|
|
{
|
|
}
|
|
|
|
section::section ()
|
|
: file_ (0),
|
|
index_ (-1),
|
|
scn (0),
|
|
data_ (0),
|
|
rela (false)
|
|
{
|
|
memset (&shdr, 0, sizeof (shdr));
|
|
}
|
|
|
|
void
|
|
section::add_data (elf_type type,
|
|
elf_xword alignment,
|
|
elf_xword size,
|
|
void* buffer,
|
|
elf_off offset)
|
|
{
|
|
check_writable ("add_data");
|
|
|
|
data_ = ::elf_newdata(scn);
|
|
if (!data_)
|
|
libelf_error ("elf_newdata: " + name_ + " (" + file_->name () + ')');
|
|
|
|
data_->d_type = type;
|
|
data_->d_off = offset;
|
|
data_->d_size = size;
|
|
data_->d_align = alignment;
|
|
data_->d_version = EV_CURRENT;
|
|
data_->d_buf = buffer;
|
|
|
|
if (!gelf_update_shdr (scn, &shdr))
|
|
libelf_error ("gelf_update_shdr: " + name_ + " (" + file_->name () + ')');
|
|
}
|
|
|
|
int
|
|
section::index () const
|
|
{
|
|
check ("index");
|
|
return index_;
|
|
}
|
|
|
|
const std::string&
|
|
section::name () const
|
|
{
|
|
check ("name");
|
|
return name_;
|
|
}
|
|
|
|
elf_data*
|
|
section::data ()
|
|
{
|
|
check ("data");
|
|
return data_;
|
|
}
|
|
|
|
elf_word
|
|
section::type () const
|
|
{
|
|
check ("type");
|
|
return shdr.sh_type;
|
|
}
|
|
|
|
elf_xword
|
|
section::flags () const
|
|
{
|
|
check ("flags");
|
|
return shdr.sh_flags;
|
|
}
|
|
|
|
elf_addr
|
|
section::address () const
|
|
{
|
|
check ("address");
|
|
return shdr.sh_addr;
|
|
}
|
|
|
|
elf_xword
|
|
section::alignment () const
|
|
{
|
|
check ("alignment");
|
|
return shdr.sh_addralign;
|
|
}
|
|
|
|
elf_off
|
|
section::offset () const
|
|
{
|
|
check ("offset");
|
|
return shdr.sh_offset;
|
|
}
|
|
|
|
elf_word
|
|
section::link () const
|
|
{
|
|
check ("link");
|
|
return shdr.sh_link;
|
|
}
|
|
|
|
elf_word
|
|
section::info () const
|
|
{
|
|
check ("info");
|
|
return shdr.sh_info;
|
|
}
|
|
|
|
elf_xword
|
|
section::size () const
|
|
{
|
|
check ("size");
|
|
return shdr.sh_size;
|
|
}
|
|
|
|
elf_xword
|
|
section::entry_size () const
|
|
{
|
|
check ("entry_size");
|
|
return shdr.sh_entsize;
|
|
}
|
|
|
|
int
|
|
section::entries () const
|
|
{
|
|
return size () / entry_size ();
|
|
}
|
|
|
|
bool
|
|
section::get_reloc_type () const
|
|
{
|
|
return rela;
|
|
}
|
|
|
|
void
|
|
section::set_name (unsigned int index)
|
|
{
|
|
check_writable ("set_name");
|
|
shdr.sh_name = index;
|
|
if (!gelf_update_shdr (scn, &shdr))
|
|
libelf_error ("gelf_update_shdr: " + name_ + " (" + file_->name () + ')');
|
|
}
|
|
|
|
void
|
|
section::set_reloc_type (bool rela_)
|
|
{
|
|
rela = rela_;
|
|
}
|
|
|
|
void
|
|
section::add (const relocation& reloc)
|
|
{
|
|
relocs.push_back (reloc);
|
|
}
|
|
|
|
const relocations&
|
|
section::get_relocations () const
|
|
{
|
|
return relocs;
|
|
}
|
|
|
|
void
|
|
section::check (const char* where) const
|
|
{
|
|
if (!file_ || (index_ < 0) || !scn)
|
|
{
|
|
std::string w = where;
|
|
throw rld::error ("Section not initialised.", "section:check:" + w);
|
|
}
|
|
}
|
|
|
|
void
|
|
section::check_writable (const char* where) const
|
|
{
|
|
check (where);
|
|
if (!file_->is_writable ())
|
|
{
|
|
std::string w = where;
|
|
throw rld::error ("File is read-only.", "section:check:");
|
|
}
|
|
}
|
|
|
|
program_header::program_header ()
|
|
{
|
|
memset (&phdr, 0, sizeof (phdr));
|
|
}
|
|
|
|
program_header::~program_header ()
|
|
{
|
|
}
|
|
|
|
void
|
|
program_header::set (elf_word type,
|
|
elf_word flags,
|
|
elf_off offset,
|
|
elf_xword filesz,
|
|
elf_xword memsz,
|
|
elf_xword align,
|
|
elf_addr vaddr,
|
|
elf_addr paddr)
|
|
{
|
|
phdr.p_type = type;
|
|
phdr.p_flags = flags;
|
|
phdr.p_offset = offset;
|
|
phdr.p_vaddr = vaddr;
|
|
phdr.p_paddr = paddr;
|
|
phdr.p_filesz = filesz;
|
|
phdr.p_memsz = memsz;
|
|
phdr.p_align = align;
|
|
}
|
|
|
|
file::file ()
|
|
: fd_ (-1),
|
|
refs (0),
|
|
archive (false),
|
|
writable (false),
|
|
elf_ (0),
|
|
mtype (0),
|
|
oclass (0),
|
|
ident_str (0),
|
|
ident_size (0),
|
|
ehdr (0),
|
|
phdr (0)
|
|
{
|
|
}
|
|
|
|
file::~file ()
|
|
{
|
|
try
|
|
{
|
|
end ();
|
|
}
|
|
catch (rld::error re)
|
|
{
|
|
std::cerr << "error: rld::elf::file::~file: "
|
|
<< re.where << ": " << re.what
|
|
<< std::endl;
|
|
}
|
|
catch (...)
|
|
{
|
|
std::cerr << "error: rld::elf::file::~file: unhandled exception"
|
|
<< std::endl;
|
|
}
|
|
}
|
|
|
|
void
|
|
file::reference_obtain ()
|
|
{
|
|
++refs;
|
|
}
|
|
|
|
void
|
|
file::reference_release ()
|
|
{
|
|
if (refs > 0)
|
|
--refs;
|
|
}
|
|
|
|
void
|
|
file::begin (const std::string& name__, int fd__, const bool writable_)
|
|
{
|
|
begin (name__, fd__, writable_, 0, 0);
|
|
}
|
|
|
|
void
|
|
file::begin (const std::string& name__, file& archive_, off_t offset)
|
|
{
|
|
archive_.check ("begin:archive");
|
|
|
|
if (archive_.writable)
|
|
throw rld::error ("archive is writable", "elf:file:begin");
|
|
|
|
begin (name__, archive_.fd_, false, &archive_, offset);
|
|
}
|
|
|
|
#define rld_archive_fhdr_size (60)
|
|
|
|
void
|
|
file::begin (const std::string& name__,
|
|
int fd__,
|
|
const bool writable_,
|
|
file* archive_,
|
|
off_t offset_)
|
|
{
|
|
if (fd__ < 0)
|
|
throw rld::error ("No file descriptor", "elf:file:begin");
|
|
|
|
/*
|
|
* Begin's are not nesting.
|
|
*/
|
|
if (elf_ || (fd_ >= 0))
|
|
throw rld::error ("Already called", "elf:file:begin");
|
|
|
|
/*
|
|
* Cannot write directly into archive. Create a file then archive it.
|
|
*/
|
|
if (archive_ && writable_)
|
|
throw rld::error ("Cannot write into archives directly",
|
|
"elf:file:begin");
|
|
|
|
libelf_initialise ();
|
|
|
|
/*
|
|
* Is this image part of an archive ?
|
|
*/
|
|
if (archive_)
|
|
{
|
|
ssize_t offset = offset_ - rld_archive_fhdr_size;
|
|
if (::elf_rand (archive_->elf_, offset) != offset)
|
|
libelf_error ("rand: " + archive_->name_);
|
|
}
|
|
|
|
/*
|
|
* Note, the elf passed is either the archive or NULL.
|
|
*/
|
|
elf* elf__ = ::elf_begin (fd__,
|
|
writable_ ? ELF_C_WRITE : ELF_C_READ,
|
|
archive_ ? archive_->elf_ : 0);
|
|
if (!elf__)
|
|
libelf_error ("begin: " + name__);
|
|
|
|
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
|
|
std::cout << "elf::begin: " << elf__ << ' ' << name__ << std::endl;
|
|
|
|
elf_kind ek = ::elf_kind (elf__);
|
|
|
|
/*
|
|
* If this is inside an archive it must be an ELF file.
|
|
*/
|
|
|
|
if (archive_ && (ek != ELF_K_ELF))
|
|
throw rld::error ("File format in archive not ELF",
|
|
"elf:file:begin: " + name__);
|
|
|
|
if (ek == ELF_K_AR)
|
|
archive = true;
|
|
else if (ek == ELF_K_ELF)
|
|
archive = false;
|
|
else
|
|
throw rld::error ("File format not ELF or archive",
|
|
"elf:file:begin: " + name__);
|
|
|
|
if (!writable_)
|
|
{
|
|
/*
|
|
* If an ELF file make sure they all match. On the first file that
|
|
* begins an ELF session record its settings.
|
|
*/
|
|
if (ek == ELF_K_ELF)
|
|
{
|
|
oclass = ::gelf_getclass (elf__);
|
|
ident_str = ::elf_getident (elf__, &ident_size);
|
|
}
|
|
}
|
|
|
|
fd_ = fd__;
|
|
name_ = name__;
|
|
writable = writable_;
|
|
elf_ = elf__;
|
|
|
|
if (!archive && !writable)
|
|
{
|
|
load_header ();
|
|
load_sections ();
|
|
}
|
|
}
|
|
|
|
void
|
|
file::end ()
|
|
{
|
|
if (refs > 0)
|
|
throw rld::error ("References still held", "elf:file:end: " + name_);
|
|
|
|
if (fd_ >= 0)
|
|
{
|
|
if (!writable)
|
|
{
|
|
if (ehdr)
|
|
{
|
|
delete ehdr;
|
|
ehdr = 0;
|
|
}
|
|
if (phdr)
|
|
{
|
|
delete phdr;
|
|
phdr = 0;
|
|
}
|
|
}
|
|
|
|
fd_ = -1;
|
|
name_.clear ();
|
|
archive = false;
|
|
oclass = 0;
|
|
ident_str = 0;
|
|
ident_size = 0;
|
|
writable = false;
|
|
|
|
secs.clear ();
|
|
|
|
if (elf_)
|
|
{
|
|
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
|
|
std::cout << "libelf::end: " << elf_
|
|
<< ' ' << name_ << std::endl;
|
|
::elf_end (elf_);
|
|
elf_ = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
file::write ()
|
|
{
|
|
check_writable ("write");
|
|
|
|
std::string shstrtab;
|
|
|
|
for (section_table::iterator sti = secs.begin ();
|
|
sti != secs.end ();
|
|
++sti)
|
|
{
|
|
section& sec = (*sti).second;
|
|
int added_at = shstrtab.size ();
|
|
shstrtab += '\0' + sec.name ();
|
|
sec.set_name (added_at + 1);
|
|
}
|
|
|
|
unsigned int shstrtab_name = shstrtab.size () + 1;
|
|
|
|
/*
|
|
* Done this way to clang happy on darwin.
|
|
*/
|
|
shstrtab += '\0';
|
|
shstrtab += ".shstrtab";
|
|
|
|
/*
|
|
* Create the string table section.
|
|
*/
|
|
section shstrsec (*this,
|
|
secs.size () + 1, /* index */
|
|
".shstrtab", /* name */
|
|
SHT_STRTAB, /* type */
|
|
1, /* alignment */
|
|
SHF_STRINGS | SHF_ALLOC, /* flags */
|
|
0, /* address */
|
|
0, /* offset */
|
|
shstrtab.size ()); /* size */
|
|
|
|
shstrsec.add_data (ELF_T_BYTE,
|
|
1,
|
|
shstrtab.size (),
|
|
(void*) shstrtab.c_str ());
|
|
|
|
shstrsec.set_name (shstrtab_name);
|
|
|
|
::elf_setshstrndx (elf_, shstrsec.index ());
|
|
::elf_flagehdr (elf_, ELF_C_SET, ELF_F_DIRTY);
|
|
|
|
if (elf_update (elf_, ELF_C_NULL) < 0)
|
|
libelf_error ("elf_update:layout: " + name_);
|
|
|
|
::elf_flagphdr (elf_, ELF_C_SET, ELF_F_DIRTY);
|
|
|
|
if (::elf_update (elf_, ELF_C_WRITE) < 0)
|
|
libelf_error ("elf_update:write: " + name_);
|
|
}
|
|
|
|
void
|
|
file::load_header ()
|
|
{
|
|
check ("load_header");
|
|
|
|
if (!ehdr)
|
|
{
|
|
if (!writable)
|
|
ehdr = new elf_ehdr;
|
|
else
|
|
{
|
|
throw rld::error ("No ELF header; set the header first",
|
|
"elf:file:load_header: " + name_);
|
|
}
|
|
}
|
|
|
|
if (::gelf_getehdr (elf_, ehdr) == 0)
|
|
error ("gelf_getehdr");
|
|
}
|
|
|
|
unsigned int
|
|
file::machinetype () const
|
|
{
|
|
check_ehdr ("machinetype");
|
|
return ehdr->e_machine;
|
|
}
|
|
|
|
unsigned int
|
|
file::type () const
|
|
{
|
|
check_ehdr ("type");
|
|
return ehdr->e_type;
|
|
}
|
|
|
|
unsigned int
|
|
file::object_class () const
|
|
{
|
|
check ("object_class");
|
|
return oclass;
|
|
}
|
|
|
|
unsigned int
|
|
file::data_type () const
|
|
{
|
|
check ("data_type");
|
|
if (!ident_str)
|
|
throw rld::error ("No ELF ident str", "elf:file:data_type: " + name_);
|
|
return ident_str[EI_DATA];
|
|
}
|
|
|
|
bool
|
|
file::is_archive () const
|
|
{
|
|
check ("is_archive");
|
|
return archive;
|
|
}
|
|
|
|
bool
|
|
file::is_executable () const
|
|
{
|
|
check_ehdr ("is_executable");
|
|
return ehdr->e_type != ET_REL;
|
|
}
|
|
|
|
bool
|
|
file::is_relocatable() const
|
|
{
|
|
check_ehdr ("is_relocatable");
|
|
return ehdr->e_type == ET_REL;
|
|
}
|
|
|
|
int
|
|
file::section_count () const
|
|
{
|
|
check_ehdr ("section_count");
|
|
return ehdr->e_shnum;
|
|
}
|
|
|
|
void
|
|
file::load_sections ()
|
|
{
|
|
if (secs.empty ())
|
|
{
|
|
check ("load_sections_headers");
|
|
for (int sn = 0; sn < section_count (); ++sn)
|
|
{
|
|
section sec (*this, sn);
|
|
secs[sec.name ()] = sec;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
file::get_sections (sections& filtered_secs, unsigned int type)
|
|
{
|
|
load_sections ();
|
|
for (section_table::iterator si = secs.begin ();
|
|
si != secs.end ();
|
|
++si)
|
|
{
|
|
section& sec = (*si).second;
|
|
if ((type == 0) || (sec.type () == type))
|
|
filtered_secs.push_back (&sec);
|
|
}
|
|
}
|
|
|
|
section&
|
|
file::get_section (int index)
|
|
{
|
|
load_sections ();
|
|
for (section_table::iterator si = secs.begin ();
|
|
si != secs.end ();
|
|
++si)
|
|
{
|
|
section& sec = (*si).second;
|
|
if (index == sec.index ())
|
|
return sec;
|
|
}
|
|
|
|
throw rld::error ("section index '" + rld::to_string (index) + "'not found",
|
|
"elf:file:get_section: " + name_);
|
|
}
|
|
|
|
int
|
|
file::strings_section () const
|
|
{
|
|
check_ehdr ("strings_sections");
|
|
return ehdr->e_shstrndx;
|
|
}
|
|
|
|
void
|
|
file::load_symbols ()
|
|
{
|
|
if (symbols.empty ())
|
|
{
|
|
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
|
|
std::cout << "elf:symbol: " << name () << std::endl;
|
|
|
|
sections symbol_secs;
|
|
|
|
get_sections (symbol_secs, SHT_SYMTAB);
|
|
|
|
for (sections::iterator si = symbol_secs.begin ();
|
|
si != symbol_secs.end ();
|
|
++si)
|
|
{
|
|
section& sec = *(*si);
|
|
int syms = sec.entries ();
|
|
|
|
for (int s = 0; s < syms; ++s)
|
|
{
|
|
elf_sym esym;
|
|
|
|
if (!::gelf_getsym (sec.data (), s, &esym))
|
|
error ("gelf_getsym");
|
|
|
|
std::string name = get_string (sec.link (), esym.st_name);
|
|
symbols::symbol sym (s, name, esym);
|
|
|
|
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
|
|
std::cout << "elf:symbol: " << sym << std::endl;
|
|
|
|
symbols.push_back (sym);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
file::get_symbols (symbols::pointers& filtered_syms,
|
|
bool unresolved,
|
|
bool local,
|
|
bool weak,
|
|
bool global)
|
|
{
|
|
if (rld::verbose () >= RLD_VERBOSE_TRACE_SYMS)
|
|
std::cout << "elf:get-syms: unresolved:" << unresolved
|
|
<< " local:" << local
|
|
<< " weak:" << weak
|
|
<< " global:" << global
|
|
<< " " << name_
|
|
<< std::endl;
|
|
|
|
load_symbols ();
|
|
|
|
filtered_syms.clear ();
|
|
|
|
for (symbols::bucket::iterator si = symbols.begin ();
|
|
si != symbols.end ();
|
|
++si)
|
|
{
|
|
symbols::symbol& sym = *si;
|
|
|
|
int stype = sym.type ();
|
|
int sbind = sym.binding ();
|
|
|
|
/*
|
|
* If wanting unresolved symbols and the type is no-type and the
|
|
* section is undefined, or, the type is no-type or object or function
|
|
* and the bind is local and we want local symbols, or the bind is weak
|
|
* and we want weak symbols, or the bind is global and we want global
|
|
* symbols then add the filtered symbols container.
|
|
*/
|
|
bool add = false;
|
|
|
|
if ((stype == STT_NOTYPE) &&
|
|
(sbind == STB_GLOBAL) &&
|
|
(sym.section_index () == SHN_UNDEF))
|
|
{
|
|
if (unresolved)
|
|
add = true;
|
|
}
|
|
else
|
|
{
|
|
if (((stype == STT_NOTYPE) ||
|
|
(stype == STT_OBJECT) ||
|
|
(stype == STT_FUNC)) &&
|
|
((weak && (sbind == STB_WEAK)) ||
|
|
(!unresolved && ((local && (sbind == STB_LOCAL)) ||
|
|
(global && (sbind == STB_GLOBAL))))))
|
|
add = true;
|
|
}
|
|
|
|
if (add)
|
|
filtered_syms.push_back (&sym);
|
|
}
|
|
}
|
|
|
|
const symbols::symbol&
|
|
file::get_symbol (const int index) const
|
|
{
|
|
for (symbols::bucket::const_iterator si = symbols.begin ();
|
|
si != symbols.end ();
|
|
++si)
|
|
{
|
|
const symbols::symbol& sym = *si;
|
|
if (index == sym.index ())
|
|
return sym;
|
|
}
|
|
|
|
throw rld::error ("symbol index '" + rld::to_string (index) + "' not found",
|
|
"elf:file:get_symbol: " + name_);
|
|
}
|
|
|
|
void
|
|
file::load_relocations ()
|
|
{
|
|
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
|
|
std::cout << "elf:reloc: " << name () << std::endl;
|
|
|
|
sections rel_secs;
|
|
|
|
get_sections (rel_secs, SHT_REL);
|
|
get_sections (rel_secs, SHT_RELA);
|
|
|
|
for (sections::iterator si = rel_secs.begin ();
|
|
si != rel_secs.end ();
|
|
++si)
|
|
{
|
|
section& sec = *(*si);
|
|
section& targetsec = get_section (sec.info ());
|
|
int rels = sec.entries ();
|
|
bool rela = sec.type () == SHT_RELA;
|
|
|
|
targetsec.set_reloc_type (rela);
|
|
|
|
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
|
|
std::cout << "elf:reloc: " << sec.name ()
|
|
<< " -> " << targetsec.name ()
|
|
<< std::endl;
|
|
|
|
for (int r = 0; r < rels; ++r)
|
|
{
|
|
if (rela)
|
|
{
|
|
elf_rela erela;
|
|
|
|
if (!::gelf_getrela (sec.data (), r, &erela))
|
|
error ("gelf_getrela");
|
|
|
|
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
|
|
std::cout << "elf:reloc: rela: offset: " << erela.r_offset
|
|
<< " sym:" << GELF_R_SYM (erela.r_info)
|
|
<< " type:" << GELF_R_TYPE (erela.r_info)
|
|
<< " addend:" << erela.r_addend
|
|
<< std::endl;
|
|
|
|
/*
|
|
* The target section is updated with the fix up, and symbol
|
|
* section indicates the section offset being referenced by the
|
|
* fixup.
|
|
*/
|
|
|
|
const symbols::symbol& sym = get_symbol (GELF_R_SYM (erela.r_info));
|
|
|
|
relocation reloc (sym,
|
|
erela.r_offset,
|
|
erela.r_info,
|
|
erela.r_addend);
|
|
|
|
targetsec.add (reloc);
|
|
}
|
|
else
|
|
{
|
|
elf_rel erel;
|
|
|
|
if (!::gelf_getrel (sec.data (), r, &erel))
|
|
error ("gelf_getrel");
|
|
|
|
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
|
|
std::cout << "elf:reloc: rel: offset: " << erel.r_offset
|
|
<< " sym:" << GELF_R_SYM (erel.r_info)
|
|
<< " type:" << GELF_R_TYPE (erel.r_info)
|
|
<< std::endl;
|
|
|
|
const symbols::symbol& sym = get_symbol (GELF_R_SYM (erel.r_info));
|
|
|
|
relocation reloc (sym, erel.r_offset, erel.r_info);
|
|
|
|
targetsec.add (reloc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string
|
|
file::get_string (int section, size_t offset)
|
|
{
|
|
check ("get_string");
|
|
char* s = ::elf_strptr (elf_, section, offset);
|
|
if (!s)
|
|
error ("elf_strptr");
|
|
return s;
|
|
}
|
|
|
|
std::string
|
|
file::get_string (size_t offset)
|
|
{
|
|
check ("get_string");
|
|
char* s = ::elf_strptr (elf_, strings_section (), offset);
|
|
if (!s)
|
|
error ("elf_strptr");
|
|
return s;
|
|
}
|
|
|
|
void
|
|
file::set_header (elf_half type,
|
|
int class_,
|
|
elf_half machinetype,
|
|
unsigned char datatype)
|
|
{
|
|
check_writable ("set_header");
|
|
|
|
if (ehdr)
|
|
throw rld::error ("ELF header already set",
|
|
"elf:file:set_header: " + name_);
|
|
|
|
ehdr = (elf_ehdr*) ::gelf_newehdr (elf_, class_);
|
|
if (ehdr == 0)
|
|
error ("gelf_newehdr");
|
|
|
|
if (class_ == ELFCLASS32)
|
|
{
|
|
if((ehdr = (elf_ehdr*) ::elf32_getehdr (elf_)) == 0)
|
|
error ("elf32_getehdr");
|
|
}
|
|
else if (::gelf_getehdr (elf_, ehdr) == 0)
|
|
error ("gelf_getehdr");
|
|
|
|
if (class_ == ELFCLASS32)
|
|
{
|
|
((elf32_ehdr*)ehdr)->e_type = type;
|
|
((elf32_ehdr*)ehdr)->e_machine = machinetype;
|
|
((elf32_ehdr*)ehdr)->e_flags = 0;
|
|
((elf32_ehdr*)ehdr)->e_ident[EI_DATA] = datatype;
|
|
((elf32_ehdr*)ehdr)->e_version = EV_CURRENT;
|
|
}
|
|
else
|
|
{
|
|
ehdr->e_type = type;
|
|
ehdr->e_machine = machinetype;
|
|
ehdr->e_flags = 0;
|
|
ehdr->e_ident[EI_DATA] = datatype;
|
|
ehdr->e_version = EV_CURRENT;
|
|
}
|
|
|
|
::elf_flagphdr (elf_, ELF_C_SET , ELF_F_DIRTY);
|
|
}
|
|
|
|
void
|
|
file::add (section& sec)
|
|
{
|
|
check_writable ("add");
|
|
secs[sec.name ()] = sec;
|
|
}
|
|
|
|
void
|
|
file::add (program_header& phdr)
|
|
{
|
|
check_writable ("add");
|
|
phdrs.push_back (phdr);
|
|
}
|
|
|
|
elf*
|
|
file::get_elf ()
|
|
{
|
|
return elf_;
|
|
}
|
|
|
|
const std::string&
|
|
file::name () const
|
|
{
|
|
return name_;
|
|
}
|
|
|
|
bool
|
|
file::is_writable () const
|
|
{
|
|
return writable;
|
|
}
|
|
|
|
size_t
|
|
file::machine_size () const
|
|
{
|
|
size_t bytes;
|
|
switch (object_class ())
|
|
{
|
|
case ELFCLASS64:
|
|
bytes = sizeof (uint64_t);
|
|
break;
|
|
default:
|
|
bytes = sizeof (uint32_t);
|
|
break;
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
bool
|
|
file::is_little_endian () const
|
|
{
|
|
return data_type () == ELFDATA2LSB;
|
|
}
|
|
|
|
void
|
|
file::check (const char* where) const
|
|
{
|
|
if (!elf_ || (fd_ < 0))
|
|
{
|
|
std::string w = where;
|
|
throw rld::error ("No ELF file or file descriptor", "elf:file:" + w);
|
|
}
|
|
}
|
|
|
|
void
|
|
file::check_ehdr (const char* where) const
|
|
{
|
|
check (where);
|
|
if (!ehdr)
|
|
{
|
|
std::string w = where;
|
|
throw rld::error ("no elf header", "elf:file:" + w);
|
|
}
|
|
}
|
|
|
|
void
|
|
file::check_phdr (const char* where) const
|
|
{
|
|
check (where);
|
|
if (!phdr)
|
|
{
|
|
std::string w = where;
|
|
throw rld::error ("no elf program header", "elf:file:" + w);
|
|
}
|
|
}
|
|
|
|
void
|
|
file::check_writable (const char* where) const
|
|
{
|
|
check (where);
|
|
if (!writable)
|
|
{
|
|
std::string w = where;
|
|
throw rld::error ("not writable", "elf:file:" + w);
|
|
}
|
|
}
|
|
|
|
void
|
|
file::error (const char* where) const
|
|
{
|
|
std::string w = where;
|
|
libelf_error (w + ": " + name_);
|
|
}
|
|
|
|
const std::string
|
|
machine_type (unsigned int machinetype)
|
|
{
|
|
struct types_and_labels
|
|
{
|
|
const char* name; //< The RTEMS label.
|
|
unsigned int machinetype; //< The machine type.
|
|
};
|
|
types_and_labels types_to_labels[] =
|
|
{
|
|
{ "arm", EM_ARM },
|
|
{ "avr", EM_AVR },
|
|
{ "bfin", EM_BLACKFIN },
|
|
{ "h8300", EM_H8_300 },
|
|
{ "i386", EM_386 },
|
|
/* { "m32c", EM_M32C }, Not in libelf I imported */
|
|
{ "m32r", EM_M32R },
|
|
{ "m68k", EM_68K },
|
|
{ "m68k", EM_COLDFIRE },
|
|
{ "mips", EM_MIPS },
|
|
{ "powerpc", EM_PPC },
|
|
#ifndef EM_RISCV
|
|
{ "riscv", 243 }, /* If not in libelf yet */
|
|
#else
|
|
{ "riscv", EM_RISCV },
|
|
#endif
|
|
{ "sh", EM_SH },
|
|
{ "sparc", EM_SPARC },
|
|
{ "sparc64", EM_SPARC },
|
|
{ 0, EM_NONE }
|
|
};
|
|
|
|
int m = 0;
|
|
while (types_to_labels[m].machinetype != EM_NONE)
|
|
{
|
|
if (machinetype == types_to_labels[m].machinetype)
|
|
return types_to_labels[m].name;
|
|
++m;
|
|
}
|
|
|
|
std::ostringstream what;
|
|
what << "unknown machine type: " << elf_object_machinetype;
|
|
throw rld::error (what, "machine-type");
|
|
}
|
|
|
|
const std::string
|
|
machine_type ()
|
|
{
|
|
return machine_type (elf_object_machinetype);
|
|
}
|
|
|
|
unsigned int
|
|
object_class ()
|
|
{
|
|
return elf_object_class;
|
|
}
|
|
|
|
unsigned int
|
|
object_machine_type ()
|
|
{
|
|
return elf_object_machinetype;
|
|
}
|
|
|
|
unsigned int
|
|
object_datatype ()
|
|
{
|
|
return elf_object_datatype;
|
|
}
|
|
|
|
void
|
|
check_file(const file& file)
|
|
{
|
|
if (elf_object_machinetype == EM_NONE)
|
|
elf_object_machinetype = file.machinetype ();
|
|
else if (file.machinetype () != elf_object_machinetype)
|
|
{
|
|
std::ostringstream oss;
|
|
oss << "elf:check_file:" << file.name ()
|
|
<< ": " << elf_object_machinetype << '/' << file.machinetype ();
|
|
throw rld::error ("Mixed machine types not supported.", oss.str ());
|
|
}
|
|
|
|
if (elf_object_class == ELFCLASSNONE)
|
|
elf_object_class = file.object_class ();
|
|
else if (file.object_class () != elf_object_class)
|
|
throw rld::error ("Mixed classes not allowed (32bit/64bit).",
|
|
"elf:check_file: " + file.name ());
|
|
|
|
if (elf_object_datatype == ELFDATANONE)
|
|
elf_object_datatype = file.data_type ();
|
|
else if (elf_object_datatype != file.data_type ())
|
|
throw rld::error ("Mixed data types not allowed (LSB/MSB).",
|
|
"elf:check_file: " + file.name ());
|
|
}
|
|
|
|
}
|
|
}
|