diff options
author | Chris Johns <chrisj@rtems.org> | 2012-05-07 08:47:11 +1000 |
---|---|---|
committer | Chris Johns <chrisj@rtems.org> | 2012-05-07 08:47:11 +1000 |
commit | 75bb1e696321cf1b9ac6b124312b57560a06ff39 (patch) | |
tree | 510f1acc95a08ef2761aab7efba82ef5af894c8a /rld-elf.cpp |
Add to git.
Diffstat (limited to 'rld-elf.cpp')
-rw-r--r-- | rld-elf.cpp | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/rld-elf.cpp b/rld-elf.cpp new file mode 100644 index 0000000..8f983b6 --- /dev/null +++ b/rld-elf.cpp @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2011, 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 + { + void error (const std::string& where) + { + throw rld::error (::elf_errmsg (-1), "elf:" + 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 int elf_object_class = ELFCLASSNONE; + static int elf_object_data = ELFDATANONE; + static int elf_object_machinetype = EM_NONE; + + /** + * 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) + error ("initialisation"); + libelf_initialised = true; + } + } + + /** + * Return the RTEMS target type given the ELF machine type. + */ + const std::string + machine_type () + { + struct types_and_labels + { + const char* name; //< The RTEMS label. + 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 }, + { "sh", EM_SH }, + { "sparc", EM_SPARC }, + { "sparc64", EM_SPARC }, + { 0, EM_NONE } + }; + + int m = 0; + while (types_to_labels[m].machinetype != EM_NONE) + { + if (elf_object_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"); + } + + section::section (int index, + std::string& name, + elf_scn* scn, + elf_shdr& shdr) + : index (index), + name (name), + scn (scn), + shdr (shdr) + { + data = ::elf_getdata (scn, NULL); + if (!data) + error ("elf_getdata"); + } + + section::section () + : index (-1), + scn (0), + data (0) + { + memset (&shdr, 0, sizeof (shdr)); + } + + #define rld_archive_fhdr_size (60) + + void + begin (rld::files::image& image) + { + libelf_initialise (); + + /* + * Begin's are not nesting. + */ + Elf* elf = image.elf (); + if (elf) + error ("begin: already done: " + image.name ().full ()); + + /* + * Is this image part of an archive ? + */ + elf = image.elf (true); + if (elf) + { + ssize_t offset = image.name ().offset () - rld_archive_fhdr_size; + + if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) + std::cout << "elf::rand: " << elf << " offset:" << offset + << ' ' << image.name ().full () << std::endl; + + if (::elf_rand (elf, offset) != offset) + error ("begin:" + image.name ().full ()); + } + + /* + * Note, the elf passed is either the archive or NULL. + */ + elf = ::elf_begin (image.fd (), ELF_C_READ, elf); + if (!elf) + error ("begin:" + image.name ().full ()); + + if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) + std::cout << "elf::begin: " << elf + << ' ' << image.name ().full () << std::endl; + + image.set_elf (elf); + + elf_kind ek = ::elf_kind (elf); + if (image.name ().is_archive ()) + { + if (ek != ELF_K_AR) + throw rld::error ("File not an ar archive", "libelf:" + image.name ().full ()); + } + else if (ek != ELF_K_ELF) + throw rld::error ("File format not ELF", "libelf:" + image.name ().full ()); + + /* + * 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) + { + int cl = ::gelf_getclass (elf); + + if (elf_object_class == ELFCLASSNONE) + elf_object_class = cl; + else if (cl != elf_object_class) + throw rld::error ("Mixed classes not allowed (32bit/64bit).", + "begin:" + image.name ().full ()); + + char* ident = elf_getident (elf, NULL); + + if (elf_object_data == ELFDATANONE) + elf_object_data = ident[EI_DATA]; + else if (elf_object_data != ident[EI_DATA]) + throw rld::error ("Mixed data types not allowed (LSB/MSB).", + "begin:" + image.name ().full ()); + } + } + + void + end (rld::files::image& image) + { + ::Elf* elf = image.elf (); + if (elf) + { + if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) + std::cout << "elf::end: " << elf + << ' ' << image.name ().full () << std::endl; + ::elf_end (elf); + } + image.set_elf (0); + } + + void + get_header (rld::files::image& image, elf_ehdr& ehdr) + { + if (::gelf_getehdr (image.elf (), &ehdr) == NULL) + error ("get-header:" + image.name ().full ()); + + if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_REL)) + throw rld::error ("Invalid ELF type (only ET_EXEC/ET_REL supported).", + "get-header:" + image.name ().full ()); + + if (elf_object_machinetype == EM_NONE) + elf_object_machinetype = ehdr.e_machine; + else if (elf_object_machinetype != ehdr.e_machine) + { + std::ostringstream oss; + oss << "get-header:" << image.name ().full () + << ": " << elf_object_machinetype << '/' << ehdr.e_machine; + throw rld::error ("Mixed machine types not supported.", oss.str ()); + } + } + + void + get_section_headers (rld::files::object& object, + sections& secs, + unsigned int type) + { + for (int sn = 0; sn < object.sections (); ++sn) + { + ::Elf_Scn* scn = ::elf_getscn (object.elf (), sn); + if (!scn) + error ("elf_getscn:" + object.name ().full ()); + ::GElf_Shdr shdr; + if (!::gelf_getshdr (scn, &shdr)) + error ("gelf_getshdr:" + object.name ().full ()); + if (shdr.sh_type == type) + { + std::string name = get_string (object, + object.section_strings (), + shdr.sh_name); + secs.push_back (section (sn, name, scn, shdr)); + } + } + } + + void + load_symbol_table (rld::symbols::table& exported, + rld::files::object& object, + section& sec, + bool local, + bool weak, + bool global) + { + int count = sec.shdr.sh_size / sec.shdr.sh_entsize; + for (int s = 0; s < count; ++s) + { + GElf_Sym esym; + if (!::gelf_getsym (sec.data, s, &esym)) + error ("gelf_getsym"); + std::string name = get_string (object, sec.shdr.sh_link, esym.st_name); + if (!name.empty ()) + { + int stype = GELF_ST_TYPE (esym.st_info); + int sbind = GELF_ST_BIND (esym.st_info); + if (rld::verbose () >= RLD_VERBOSE_TRACE) + { + rld::symbols::symbol sym (name, esym); + std::cout << "elf::symbol: "; + sym.output (std::cout); + std::cout << std::endl; + } + if ((stype == STT_NOTYPE) && (esym.st_shndx == SHN_UNDEF)) + object.unresolved_symbols ()[name] = rld::symbols::symbol (name, esym); + else if (((stype == STT_NOTYPE) || + (stype == STT_OBJECT) || + (stype == STT_FUNC)) && + ((local && (sbind == STB_LOCAL)) || + (weak && (sbind == STB_WEAK)) || + (global && (sbind == STB_GLOBAL)))) + { + exported[name] = rld::symbols::symbol (name, object, esym);; + object.external_symbols ().push_back (&exported[name]); + } + } + } + } + + void + load_symbols (rld::symbols::table& symbols, + rld::files::object& object, + bool local, + bool weak, + bool global) + { + sections sections; + get_section_headers (object, sections, SHT_SYMTAB); + for (sections::iterator si = sections.begin (); + si != sections.end (); + ++si) + load_symbol_table (symbols, object, *si, local, weak, global); + } + + std::string + get_string (rld::files::object& object, + int section, + size_t offset) + { + char* s = ::elf_strptr (object.elf (), section, offset); + if (!s) + error ("elf_strptr"); + return s; + } + + } +} |