summaryrefslogtreecommitdiff
path: root/rld-elf.cpp
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2012-11-20 19:53:24 +1100
committerChris Johns <chrisj@rtems.org>2012-11-20 19:53:24 +1100
commit256c1455227fd1421e7c71b2902df6a07cd290f2 (patch)
tree46d193d73b30b891f2777394c9422deb8f3d3fae /rld-elf.cpp
parentd0a1bded76293012fbdb9af05bc9b333cf09398b (diff)
Add support to write a metadata ELF file.
This also adds support to the ELF classes that wrap libelf. While this is now done and seems to work I will not be using an ELF file to hold the metadata after all.
Diffstat (limited to 'rld-elf.cpp')
-rw-r--r--rld-elf.cpp344
1 files changed, 289 insertions, 55 deletions
diff --git a/rld-elf.cpp b/rld-elf.cpp
index 9cfa313..bc15288 100644
--- a/rld-elf.cpp
+++ b/rld-elf.cpp
@@ -45,8 +45,8 @@ namespace rld
* 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_data = ELFDATANONE;
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
@@ -64,6 +64,53 @@ namespace rld
}
}
+ 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)
+ {
+ 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_),
@@ -82,7 +129,7 @@ namespace rld
if (shdr.sh_type != SHT_NULL)
{
name_ = file_.get_string (shdr.sh_name);
- data_ = ::elf_getdata (scn, NULL);
+ data_ = ::elf_getdata (scn, 0);
if (!data_)
libelf_error ("elf_getdata: " + name_ + '(' + file_.name () + ')');
}
@@ -107,87 +154,111 @@ namespace rld
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 ();
+ check ("index");
return index_;
}
const std::string&
section::name () const
{
- check ();
+ check ("name");
return name_;
}
elf_data*
section::data ()
{
- check ();
+ check ("data");
return data_;
}
elf_word
section::type () const
{
- check ();
+ check ("type");
return shdr.sh_type;
}
elf_xword
section::flags () const
{
- check ();
+ check ("flags");
return shdr.sh_flags;
}
elf_addr
section::address () const
{
- check ();
+ check ("address");
return shdr.sh_addr;
}
elf_xword
section::alignment () const
{
- check ();
+ check ("alignment");
return shdr.sh_addralign;
}
elf_off
section::offset () const
{
- check ();
+ check ("offset");
return shdr.sh_offset;
}
elf_word
section::link () const
{
- check ();
+ check ("link");
return shdr.sh_link;
}
elf_word
section::info () const
{
- check ();
+ check ("info");
return shdr.sh_info;
}
elf_xword
section::size () const
{
- check ();
+ check ("size");
return shdr.sh_size;
}
elf_xword
section::entry_size () const
{
- check ();
+ check ("entry_size");
return shdr.sh_entsize;
}
@@ -198,10 +269,62 @@ namespace rld
}
void
- section::check () const
+ 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::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 ()
{
- if (!file_ || (index_ < 0))
- throw rld::error ("Invalid section.", "section:check:");
+ }
+
+ 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 ()
@@ -325,7 +448,7 @@ namespace rld
writable = writable_;
elf_ = elf__;
- if (!archive)
+ if (!archive && !writable)
load_header ();
}
@@ -338,46 +461,112 @@ namespace rld
std::cout << "libelf::end: " << elf_
<< ' ' << name_ << std::endl;
::elf_end (elf_);
+ elf_ = 0;
}
- fd_ = -1;
- name_.clear ();
- archive = false;
- elf_ = 0;
- oclass = 0;
- ident_str = 0;
- ident_size = 0;
-
- if (!writable)
+ if (fd_ >= 0)
{
- if (ehdr)
+ if (!writable)
{
- delete ehdr;
- ehdr = 0;
- }
- if (phdr)
- {
- delete phdr;
- phdr = 0;
+ if (ehdr)
+ {
+ delete ehdr;
+ ehdr = 0;
+ }
+ if (phdr)
+ {
+ delete phdr;
+ phdr = 0;
+ }
}
+
+ fd_ = -1;
+ name_.clear ();
+ archive = false;
+ elf_ = 0;
+ oclass = 0;
+ ident_str = 0;
+ ident_size = 0;
+ writable = false;
+ secs.clear ();
}
+ }
+
+ 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";
- writable = false;
+ /*
+ * 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);
- stab.clear ();
- secs.clear ();
+ if (::elf_update (elf_, ELF_C_WRITE) < 0)
+ libelf_error ("elf_update:write: " + name_);
}
void
file::load_header ()
{
- check ("get_header");
+ check ("load_header");
- if (!writable && !ehdr)
- ehdr = new elf_ehdr;
+ 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) == NULL)
- error ("get-header");
+ if (::gelf_getehdr (elf_, ehdr) == 0)
+ error ("gelf_getehdr");
}
unsigned int
@@ -445,7 +634,10 @@ namespace rld
{
check ("load_sections_headers");
for (int sn = 0; sn < section_count (); ++sn)
- secs.push_back (section (*this, sn));
+ {
+ section sec = section (*this, sn);
+ secs[sec.name ()] = sec;
+ }
}
}
@@ -454,12 +646,13 @@ namespace rld
{
load_sections ();
filtered_secs.clear ();
- for (sections::iterator si = secs.begin ();
+ for (section_table::iterator si = secs.begin ();
si != secs.end ();
++si)
{
- if ((type == 0) || ((*si).type () == type))
- filtered_secs.push_back (*si);
+ section& sec = (*si).second;
+ if ((type == 0) || (sec.type () == type))
+ filtered_secs.push_back (&sec);
}
}
@@ -476,7 +669,7 @@ namespace rld
si != symbol_secs.end ();
++si)
{
- section& sec = *si;
+ section& sec = *(*si);
int syms = sec.entries ();
for (int s = 0; s < syms; ++s)
@@ -599,16 +792,38 @@ namespace rld
{
check_writable ("set_header");
- ehdr = (elf_ehdr*) ::gelf_newehdr (elf_, class_);
+ 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 ("set-header");
+ error ("gelf_newehdr");
+
+ if (::gelf_getehdr (elf_, ehdr) == 0)
+ error ("gelf_getehdr");
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);
+ }
- //::gelf_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*
@@ -719,11 +934,30 @@ namespace rld
throw rld::error (what, "machine-type");
}
- const std::string 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)
{
@@ -743,9 +977,9 @@ namespace rld
throw rld::error ("Mixed classes not allowed (32bit/64bit).",
"elf:check_file: " + file.name ());
- if (elf_object_data == ELFDATANONE)
- elf_object_data = file.data_type ();
- else if (elf_object_data != file.data_type ())
+ 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 ());
}