diff options
Diffstat (limited to 'linkers/rld-rap.cpp')
-rw-r--r-- | linkers/rld-rap.cpp | 1668 |
1 files changed, 1668 insertions, 0 deletions
diff --git a/linkers/rld-rap.cpp b/linkers/rld-rap.cpp new file mode 100644 index 0000000..9b87279 --- /dev/null +++ b/linkers/rld-rap.cpp @@ -0,0 +1,1668 @@ +/* + * Copyright (c) 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. + * + * @todo Set the RAP alignment as the max of all alignments. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include <algorithm> +#include <list> +#include <iomanip> + +#include <rld.h> +#include <rld-compression.h> +#include <rld-rap.h> + +namespace rld +{ + namespace rap + { + + /** + * Output details or not. + */ + bool add_obj_details = true; + + /** + * Store the path of object files. + */ + std::string rpath; + + /** + * The names of the RAP sections. + */ + static const char* section_names[rap_secs] = + { + ".text", + ".const", + ".ctor", + ".dtor", + ".data", + ".bss" + }; + + /** + * RAP relocation record. This one does not have const fields. + */ + struct relocation + { + uint32_t offset; //< The offset in the section to apply the fixup. + uint32_t info; //< The ELF info record. + uint32_t addend; //< The ELF constant addend. + std::string symname; //< The symbol name if there is one. + uint32_t symtype; //< The type of symbol. + int symsect; //< The symbol's RAP section. + uint32_t symvalue; //< The symbol's default value. + uint32_t symbinding;//< The symbol's binding. + + /** + * Construct the relocation using the file relocation, the offset of the + * section in the target RAP section and the RAP section of the symbol. + */ + relocation (const files::relocation& reloc, const uint32_t offset); + }; + + /** + * Relocation records. + */ + typedef std::vector < relocation > relocations; + + /** + * Relocation symname sorter for the relocations container. + */ + class reloc_symname_compare + { + public: + bool operator () (const relocation& lhs, + const relocation& rhs) const { + return lhs.symname < rhs.symname; + } + }; + + /** + * Relocation offset sorter for the relocations container. + */ + class reloc_offset_compare + { + public: + bool operator () (const relocation& lhs, + const relocation& rhs) const { + if (lhs.symname == rhs.symname) + return lhs.offset < rhs.offset; + else return false; + } + }; + + /** + * An object section's offset, size and alignment. + */ + struct osection + { + std::string name; //< The name of the section. + uint32_t offset; //< The offset in the object file. + uint32_t size; //< The size of this section. + uint32_t align; //< The alignment. + uint32_t relocs; //< The number of relocations. + uint64_t flags; //< The flags. + + /** + * Construct the object section. + */ + osection (const std::string& name, + uint32_t offset, + uint32_t size, + uint32_t align, + uint32_t relocs, + uint64_t flags); + + /** + * Default constructor. + */ + osection (); + }; + + /** + * Map of object file section offsets keyed by the object file section + * index. This is used when adding the external symbols so the symbol's + * value can be adjusted by the offset of the section in the RAP section. + */ + typedef std::map < int, osection > osections; + + /** + * An ordered container of object section indexes. We need the same + * order so the alignments match up with the layout. + */ + typedef std::vector < int > osecindexes; + + /** + * Section detail will be written into RAP file + */ + struct section_detail + { + uint32_t name; //< The offset in the strtable. + uint32_t offset; //< The offset in the rap section. + uint32_t id; //< The rap id. + uint32_t size; //< The size of the section. + + /* Constructor */ + section_detail (uint32_t name, uint32_t offset, uint32_t id, uint32_t size); + }; + + /* + * A container of section detail + */ + typedef std::list < section_detail > section_details; + + /** + * The RAP section data. + */ + struct section + { + std::string name; //< The name of the section. + uint32_t offset; //< The offset of the section. + bool rela; //< The relocation record has an addend field. + relocations relocs; //< The relocations for this section. + osections osecs; //< The object section index. + osecindexes osindexes; //< The object section indexes in order. + + /** + * Default constructor. + */ + section (); + + /** + * Clear the section. + */ + void clear (); + + /** + * The size of the section given the offset. + */ + uint32_t size (uint32_t offset = 0) const; + + /** + * The alignment of the first section. + */ + uint32_t alignment () const; + + /** + * The alignment of the object section given its index. + */ + uint32_t alignment (int index) const; + + /** + * Set the offset of this section based on the previous section. + */ + void set_offset (const section& sec); + + /** + * Return the object section given the index. + */ + const osection& get_osection (int index) const; + + /** + * Output helper function to report the sections in an object file. This + * is useful when seeing the flags in the sections. + */ + void output (); + }; + + /** + * A symbol. This matches the symbol structure 'rtems_rtl_obj_sym_t' in the + * target code. + */ + struct external + { + /** + * Size of an external in the RAP file. + */ + static const uint32_t rap_size = sizeof (uint32_t) * 3; + + const uint32_t name; //< The string table's name index. + const sections sec; //< The section the symbols belongs to. + const uint32_t value; //< The offset from the section base. + const uint32_t data; //< The ELF st.info field. + + /** + * The constructor. + */ + external (const uint32_t name, + const sections sec, + const uint32_t value, + const uint32_t data); + + /** + * Copy constructor. + */ + external (const external& orig); + + }; + + /** + * A container of externals. + */ + typedef std::list < external > externals; + + /** + * The specific data for each object we need to collect to create the RAP + * format file. + */ + struct object + { + files::object& obj; //< The object file. + files::sections text; //< All executable code. + files::sections const_; //< All read only data. + files::sections ctor; //< The static constructor table. + files::sections dtor; //< The static destructor table. + files::sections data; //< All initialised read/write data. + files::sections bss; //< All uninitialised read/write data + files::sections symtab; //< All exported symbols. + files::sections strtab; //< All exported strings. + section secs[rap_secs]; //< The sections of interest. + + /** + * The constructor. Need to have an object file to create. + */ + object (files::object& obj); + + /** + * The copy constructor. + */ + object (const object& orig); + + /** + * Find the section type that matches the section index. + */ + sections find (const uint32_t index) const; + + /** + * The total number of relocations in the object file. + */ + uint32_t get_relocations () const; + + /** + * The total number of relocations for a specific RAP section in the + * object file. + */ + uint32_t get_relocations (int sec) const; + + /** + * Output the object file details.. + */ + void output (); + + private: + /** + * No default constructor allowed. + */ + object (); + }; + + /** + * A container of objects. + */ + typedef std::list < object > objects; + + /** + * The RAP image. + */ + class image + { + public: + /** + * Construct the image. + */ + image (); + + /** + * Load the layout data from the object files. + * + * @param app_objects The object files in the application. + * @param init The initialisation entry point label. + * @param fini The finish entry point label. + */ + void layout (const files::object_list& app_objects, + const std::string& init, + const std::string& fini); + + /** + * Collection the symbols from the object file. + * + * @param obj The object file to collection the symbol from. + */ + void collect_symbols (object& obj); + + /** + * Write the compressed output file. This is the top level write + * interface. + * + * @param comp The compressor. + */ + void write (compress::compressor& comp); + + /** + * Write the RAP section to the compressed output file given the object files. + * Check to make sure the size in the layout and the size written match. + * + * @param comp The compressor. + * @param sec The RAP setion to write. + */ + void write (compress::compressor& comp, sections sec); + + /** + * Write the sections to the compressed output file. The file sections + * are used to ensure the alignment. The offset is used to ensure the + * alignment of the first section of the object when it is written. + * + * @param comp The compressor. + * @param obj The object file the sections are part of. + * @param secs The container of file sections to write. + * @param offset The current offset in the RAP section. + */ + void write (compress::compressor& comp, + files::object& obj, + const files::sections& secs, + uint32_t& offset); + + /** + * Write the external symbols. + */ + void write_externals (compress::compressor& comp); + + /** + * Write the relocation records for all the object files. + */ + void write_relocations (compress::compressor& comp); + + /** + * Write the details of the files. + */ + void write_details (compress::compressor& comp); + + /** + * The total number of relocations for a specific RAP section in the + * image. + */ + uint32_t get_relocations (int sec) const; + + /** + * Clear the image values. + */ + void clear (); + + /** + * Report the RAP section's size. + */ + uint32_t section_size (sections sec) const; + + /** + * Find a symbol name in the string table. + */ + std::size_t find_in_strtab (const std::string& symname); + + private: + + objects objs; //< The RAP objects + uint32_t sec_size[rap_secs]; //< The sections of interest. + uint32_t sec_align[rap_secs]; //< The sections of interest. + bool sec_rela[rap_secs]; //< The sections of interest. + externals externs; //< The symbols in the image + uint32_t symtab_size; //< The size of the symbols. + std::string strtab; //< The strings table. + uint32_t relocs_size; //< The relocations size. + uint32_t init_off; //< The strtab offset to the init label. + uint32_t fini_off; //< The strtab offset to the fini label. + }; + + const char* + section_name (int sec) + { + if (sec < rap_secs) + return section_names[sec]; + throw rld::error ("Invalid section '" + rld::to_string (sec) + "'", + "rap::section-name"); + } + + /** + * Update the offset taking into account the alignment. + * + * @param offset The current offset. + * @param size The size to move the offset by. + * @param alignment The alignment of the offset. + * @return uint32_t The new aligned offset. + */ + uint32_t align_offset (uint32_t offset, uint32_t size, uint32_t alignment) + { + offset += size; + + if (alignment > 1) + { + uint32_t mask = alignment - 1; + if (offset & mask) + { + offset &= ~mask; + offset += alignment; + } + } + + return offset; + } + + relocation::relocation (const files::relocation& reloc, + const uint32_t offset) + : offset (reloc.offset + offset), + info (reloc.info), + addend (reloc.addend), + symname (reloc.symname), + symtype (reloc.symtype), + symsect (reloc.symsect), + symvalue (reloc.symvalue), + symbinding (reloc.symbinding) + { + } + + section_detail::section_detail (uint32_t name, + uint32_t offset, + uint32_t id, + uint32_t size) + : name (name), + offset (offset), + id (id), + size (size) + { + } + + osection::osection (const std::string& name, + uint32_t offset, + uint32_t size, + uint32_t align, + uint32_t relocs, + uint64_t flags) + : name (name), + offset (offset), + size (size), + align (align), + relocs (relocs), + flags (flags) + { + } + + osection::osection () + : offset (0), + size (0), + align (0), + relocs (0), + flags (0) + { + } + + section::section () + : offset (0), + rela (false) + { + } + + void + section::clear () + { + offset = 0; + rela = false; + } + + uint32_t + section::size (uint32_t offset_) const + { + uint32_t end = offset_; + if (end == 0) + end = offset; + for (size_t si = 0; si < osindexes.size (); ++si) + { + const osection& osec = get_osection (osindexes[si]); + end = align_offset (end, 0, osec.align); + end += osec.size; + } + return end - offset; + } + + uint32_t + section::alignment () const + { + if (!osindexes.empty ()) + { + const osection& osec = get_osection (osindexes[0]); + return osec.align; + } + return 0; + } + + uint32_t + section::alignment (int index) const + { + const osection& osec = get_osection (index); + return osec.align; + } + + void + section::set_offset (const section& sec) + { + uint32_t align = alignment (); + offset = align_offset (sec.offset, sec.size (), align); + if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) + std::cout << "rap:section::set-offset: " << name + << " offset=" << offset + << " size=" << size () + << " align=" << align + << " sec.offset=" << sec.offset + << " sec.size=" << sec.size (sec.offset) + << std::endl; + } + + const osection& + section::get_osection (int index) const + { + osections::const_iterator osi = osecs.find (index); + if (osi == osecs.end ()) + throw rld::error ("Invalid object seciton index in '" + name +"': index=" + + rld::to_string (index), + "rap::section"); + return (*osi).second; + } + + /** + * Output helper function to report the sections in an object file. This is + * useful when seeing the flags in the sections. + */ + void + section::output () + { + if (!osindexes.empty ()) + { + std::cout << ' ' << name + << ": size: " << size (offset) + << " offset: " << offset + << " rela: " << (rela ? "yes" : "no") + << std::endl; + + for (osecindexes::const_iterator osi = osindexes.begin (); + osi != osindexes.end (); + ++osi) + { + const osection& osec = get_osection (*osi); + + if (osec.size) + { + #define SF(f, i, c) if (osec.flags & (f)) flags[i] = c + + std::string flags ("--------------"); + + SF (SHF_WRITE, 0, 'W'); + SF (SHF_ALLOC, 1, 'A'); + SF (SHF_EXECINSTR, 2, 'E'); + SF (SHF_MERGE, 3, 'M'); + SF (SHF_STRINGS, 4, 'S'); + SF (SHF_INFO_LINK, 5, 'I'); + SF (SHF_LINK_ORDER, 6, 'L'); + SF (SHF_OS_NONCONFORMING, 7, 'N'); + SF (SHF_GROUP, 8, 'G'); + SF (SHF_TLS, 9, 'T'); + SF (SHF_AMD64_LARGE, 10, 'a'); + SF (SHF_ENTRYSECT, 11, 'e'); + SF (SHF_COMDEF, 12, 'c'); + SF (SHF_ORDERED, 13, 'O'); + + std::cout << " " << std::left + << std::setw (15) << osec.name + << " " << flags + << " size: " << std::setw (5) << osec.size + << " align: " << std::setw (3) << osec.align + << " relocs: " << std::setw (4) << osec.relocs + << " offset: " << std::setw (5) << osec.offset + << std::hex + << " image: 0x" << offset + osec.offset + << std::dec << std::right << std::endl; + } + } + } + } + + /** + * Helper for for_each to merge the related object sections into the RAP + * section. + */ + class section_merge: + public std::unary_function < const files::section, void > + { + public: + + section_merge (object& obj, section& sec); + + ~section_merge (); + + void operator () (const files::section& fsec); + + private: + + object& obj; + section& sec; + }; + + section_merge::section_merge (object& obj, section& sec) + : obj (obj), + sec (sec) + { + sec.offset = 0; + sec.rela = false; + } + + section_merge::~section_merge () + { + if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) + std::cout << "rap:section-merge: " << sec.name + << " size=" << sec.size () + << " offset=" << sec.offset + << " " << obj.obj.name ().full () << std::endl; + } + + void + section_merge::operator () (const files::section& fsec) + { + /* + * Align the size up to the next alignment boundary and use that as the + * offset for this object file section. + */ + uint32_t offset = align_offset (sec.size (), 0, fsec.alignment); + + if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) + std::cout << "rap:section-merge: " << fsec.name + << " sec-size=" << sec.size () + << " relocs=" << fsec.relocs.size () + << " offset=" << offset + << " fsec.size=" << fsec.size + << " fsec.alignment=" << fsec.alignment + << " fsec.rela=" << fsec.rela + << " " << obj.obj.name ().full () << std::endl; + + /* + * Add the object file's section offset to the map. This is needed + * to adjust the external symbol offsets. + */ + osection osec (fsec.name, + offset, + fsec.size, + fsec.alignment, + fsec.relocs.size (), + fsec.flags); + sec.osecs[fsec.index] = osec; + sec.osindexes.push_back (fsec.index); + + uint32_t rc = 0; + + for (files::relocations::const_iterator fri = fsec.relocs.begin (); + fri != fsec.relocs.end (); + ++fri, ++rc) + { + const files::relocation& freloc = *fri; + + if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) + std::cout << " " << std::setw (2) << sec.relocs.size () + << '/' << std::setw (2) << rc + << std::hex << ": reloc.info=0x" << freloc.info << std::dec + << " reloc.offset=" << freloc.offset + << " reloc.addend=" << freloc.addend + << " reloc.symtype=" << freloc.symtype + << " reloc.symsect=" << freloc.symsect + << " reloc.symbinding=" << freloc.symbinding + << std::endl; + + sec.relocs.push_back (relocation (freloc, offset)); + } + + std::stable_sort (sec.relocs.begin (), + sec.relocs.end (), + reloc_symname_compare ()); + std::stable_sort (sec.relocs.begin (), + sec.relocs.end (), + reloc_offset_compare ()); + + if (fsec.rela == true) + sec.rela = fsec.rela; + } + + external::external (const uint32_t name, + const sections sec, + const uint32_t value, + const uint32_t data) + : name (name), + sec (sec), + value (value), + data (data) + { + } + + external::external (const external& orig) + : name (orig.name), + sec (orig.sec), + value (orig.value), + data (orig.data) + { + } + + object::object (files::object& obj) + : obj (obj) + { + /* + * Set up the names of the sections. + */ + for (int s = 0; s < rap_secs; ++s) + secs[s].name = section_names[s]; + + /* + * Get the relocation records. Collect the various section types from the + * object file into the RAP sections. Merge those sections into the RAP + * sections. + */ + + obj.open (); + try + { + obj.begin (); + obj.load_relocations (); + obj.end (); + } + catch (...) + { + obj.close (); + throw; + } + obj.close (); + + obj.get_sections (text, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR); + obj.get_sections (const_, SHT_PROGBITS, SHF_ALLOC, SHF_WRITE | SHF_EXECINSTR); + obj.get_sections (ctor, ".ctors"); + obj.get_sections (dtor, ".dtors"); + obj.get_sections (data, SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); + obj.get_sections (bss, SHT_NOBITS, SHF_ALLOC | SHF_WRITE); + obj.get_sections (symtab, SHT_SYMTAB); + obj.get_sections (strtab, ".strtab"); + + std::for_each (text.begin (), text.end (), + section_merge (*this, secs[rap_text])); + std::for_each (const_.begin (), const_.end (), + section_merge (*this, secs[rap_const])); + std::for_each (ctor.begin (), ctor.end (), + section_merge (*this, secs[rap_ctor])); + std::for_each (dtor.begin (), dtor.end (), + section_merge (*this, secs[rap_dtor])); + std::for_each (data.begin (), data.end (), + section_merge (*this, secs[rap_data])); + std::for_each (bss.begin (), bss.end (), + section_merge (*this, secs[rap_bss])); + } + + object::object (const object& orig) + : obj (orig.obj), + text (orig.text), + const_ (orig.const_), + ctor (orig.ctor), + dtor (orig.dtor), + data (orig.data), + bss (orig.bss), + symtab (orig.symtab), + strtab (orig.strtab) + { + for (int s = 0; s < rap_secs; ++s) + secs[s] = orig.secs[s]; + } + + sections + object::find (const uint32_t index) const + { + const files::section* sec; + + sec = files::find (text, index); + if (sec) + return rap_text; + + sec = files::find (const_, index); + if (sec) + return rap_const; + + sec = files::find (ctor, index); + if (sec) + return rap_ctor; + + sec = files::find (dtor, index); + if (sec) + return rap_dtor; + + sec = files::find (data, index); + if (sec) + return rap_data; + + sec = files::find (bss, index); + if (sec) + return rap_bss; + + throw rld::error ("Section index '" + rld::to_string (index) + + "' not found: " + obj.name ().full (), "rap::object"); + } + + uint32_t + object::get_relocations () const + { + uint32_t relocs = 0; + for (int s = 0; s < rap_secs; ++s) + relocs += secs[s].relocs.size (); + return relocs; + } + + uint32_t + object::get_relocations (int sec) const + { + if ((sec < 0) || (sec >= rap_secs)) + throw rld::error ("Invalid section index '" + rld::to_string (sec), + "rap::relocations"); + return secs[sec].relocs.size (); + } + + void + object::output () + { + std::cout << "rap:object: " << obj.name ().full () << std::endl; + secs[rap_text].output (); + secs[rap_const].output (); + secs[rap_ctor].output (); + secs[rap_dtor].output (); + secs[rap_data].output (); + if (secs[rap_bss].size ()) + std::cout << " bss: size: " << secs[rap_bss].size () << std::endl; + } + + image::image () + { + clear (); + } + + void + image::layout (const files::object_list& app_objects, + const std::string& init, + const std::string& fini) + { + clear (); + + /* + * Create the local objects which contain the layout information. + */ + for (files::object_list::const_iterator aoi = app_objects.begin (); + aoi != app_objects.end (); + ++aoi) + { + files::object& app_obj = *(*aoi); + + if (!app_obj.valid ()) + throw rld::error ("Not valid: " + app_obj.name ().full (), + "rap::layout"); + + objs.push_back (object (app_obj)); + } + + for (objects::iterator oi = objs.begin (), poi = objs.begin (); + oi != objs.end (); + ++oi) + { + object& obj = *oi; + + /* + * Update the offsets in the object file. We need the object's offset + * to set the relocation offset's correctly as they are relative to the + * object file. + */ + if (oi != objs.begin ()) + { + object& pobj = *poi; + for (int s = 0; s < rap_secs; ++s) + { + obj.secs[s].set_offset (pobj.secs[s]); + sec_size[s] = obj.secs[s].offset + obj.secs[s].size (); + sec_align[s] = obj.secs[s].alignment (); + if (obj.secs[s].rela == true) + sec_rela[s] = obj.secs[s].rela; + } + ++poi; + } + else + { + for (int s = 0; s < rap_secs; ++s) + { + sec_size[s] = obj.secs[s].size (); + sec_align[s] = obj.secs[s].alignment (); + if (obj.secs[s].rela == true) + sec_rela[s] = true; + } + } + + collect_symbols (obj); + + relocs_size += obj.get_relocations (); + + if (rld::verbose () >= RLD_VERBOSE_DETAILS) + obj.output (); + } + + init_off = strtab.size () + 1; + strtab += '\0'; + strtab += init; + + fini_off = strtab.size () + 1; + strtab += '\0'; + strtab += fini; + + if (rld::verbose () >= RLD_VERBOSE_INFO) + { + uint32_t total = (sec_size[rap_text] + sec_size[rap_const] + + sec_size[rap_data] + sec_size[rap_bss] + + symtab_size + strtab.size() + relocs_size); + std::cout << "rap::layout: total:" << total + << " text:" << sec_size[rap_text] + << " const:" << sec_size[rap_const] + << " ctor:" << sec_size[rap_ctor] + << " dtor:" << sec_size[rap_dtor] + << " data:" << sec_size[rap_data] + << " bss:" << sec_size[rap_bss] + << " symbols:" << symtab_size << " (" << externs.size () << ')' + << " strings:" << strtab.size () + 1 + << " relocs:" << relocs_size + << std::endl; + } + } + + void + image::collect_symbols (object& obj) + { + symbols::pointers& esyms = obj.obj.external_symbols (); + for (symbols::pointers::const_iterator ei = esyms.begin (); + ei != esyms.end (); + ++ei) + { + const symbols::symbol& sym = *(*ei); + + if ((sym.type () == STT_OBJECT) || (sym.type () == STT_FUNC) || (sym.type () == STT_NOTYPE)) + { + if ((sym.binding () == STB_GLOBAL) || (sym.binding () == STB_WEAK)) + { + int symsec = sym.section_index (); + + /* Ignore section index 0 */ + if (symsec == 0) + continue; + /* Ignore sparc common section */ + if ((elf::object_machine_type () == EM_SPARC) && (symsec == 65522)) + continue; + + sections rap_sec = obj.find (symsec); + section& sec = obj.secs[rap_sec]; + std::size_t name; + + /* + * See if the name is already in the string table. + */ + name = find_in_strtab (sym.name ()); + + if (name == std::string::npos) + { + name = strtab.size () + 1; + strtab += '\0'; + strtab += sym.name (); + } + + /* + * The symbol's value is the symbols value plus the offset of the + * object file's section offset in the RAP section. + */ + externs.push_back (external (name, + rap_sec, + sec.offset + sec.osecs[symsec].offset + + sym.value (), + sym.info ())); + + symtab_size += external::rap_size; + } + } + } + } + + void + image::write (compress::compressor& comp) + { + /* + * Start with the machine type so the target can check the applicatiion + * is ok and can be loaded. Add the init and fini labels to the string + * table and add the references to the string table next. Follow this + * with the section details then the string table and symbol table then + * finally the relocation records. + */ + + if (rld::verbose () >= RLD_VERBOSE_INFO) + std::cout << "rap:output: machine=" << comp.transferred () << std::endl; + + comp << elf::object_machine_type () + << elf::object_datatype () + << elf::object_class (); + + /* + * The init and fini label offsets. Then the symbol table and string + * table sizes. + */ + + if (rld::verbose () >= RLD_VERBOSE_INFO) + std::cout << "rap:output: header=" << comp.transferred () << std::endl; + + comp << init_off + << fini_off + << symtab_size + << (uint32_t) strtab.size () + 1 + << (uint32_t) 0; + + /* + * Output file details + */ + if (add_obj_details) + { + write_details (comp); + } + else + { + comp << (uint32_t)0; /* No file details */ + } + + /* + * The sections. + */ + for (int s = 0; s < rap_secs; ++s) + comp << sec_size[s] + << sec_align[s]; + + /* + * Output the sections from each object file. + */ + write (comp, rap_text); + write (comp, rap_const); + write (comp, rap_ctor); + write (comp, rap_dtor); + write (comp, rap_data); + + if (rld::verbose () >= RLD_VERBOSE_INFO) + std::cout << "rap:output: strtab=" << comp.transferred () << std::endl; + + strtab += '\0'; + comp << strtab; + + if (rld::verbose () >= RLD_VERBOSE_INFO) + std::cout << "rap:output: symbols=" << comp.transferred () << std::endl; + + write_externals (comp); + + if (rld::verbose () >= RLD_VERBOSE_INFO) + std::cout << "rap:output: relocs=" << comp.transferred () << std::endl; + + write_relocations (comp); + } + + /** + * Helper for for_each to write out the various sections. + */ + class section_writer: + public std::unary_function < object, void > + { + public: + + section_writer (image& img, + compress::compressor& comp, + sections sec); + + void operator () (object& obj); + + private: + + image& img; + compress::compressor& comp; + sections sec; + uint32_t offset; + }; + + section_writer::section_writer (image& img, + compress::compressor& comp, + sections sec) + : img (img), + comp (comp), + sec (sec), + offset (0) + { + if (rld::verbose () >= RLD_VERBOSE_INFO) + std::cout << "rap:output: " << section_names[sec] + << ": offset=" << comp.transferred () + << " size=" << img.section_size (sec) << std::endl; + } + + void + section_writer::operator () (object& obj) + { + if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) + std::cout << "rap:writing: " << section_names[sec] << std::endl; + + switch (sec) + { + case rap_text: + img.write (comp, obj.obj, obj.text, offset); + break; + case rap_const: + img.write (comp, obj.obj, obj.const_, offset); + break; + case rap_ctor: + img.write (comp, obj.obj, obj.ctor, offset); + break; + case rap_dtor: + img.write (comp, obj.obj, obj.dtor, offset); + break; + case rap_data: + img.write (comp, obj.obj, obj.data, offset); + break; + default: + break; + } + } + + void + image::write (compress::compressor& comp, sections sec) + { + uint32_t image_offset = comp.transferred (); + + std::for_each (objs.begin (), objs.end (), + section_writer (*this, comp, sec)); + + uint32_t written = comp.transferred () - image_offset; + + if (written != sec_size[sec]) + { + std::string msg = "Image output size does not match layout size: "; + msg += section_names[sec]; + msg += ": layout-size=" + rld::to_string (sec_size[sec]); + msg += " image-size=" + rld::to_string (written); + throw rld::error (msg, "rap::write"); + } + } + + void + image::write (compress::compressor& comp, + files::object& obj, + const files::sections& secs, + uint32_t& offset) + { + uint32_t size = 0; + + obj.open (); + + try + { + obj.begin (); + + if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) + std::cout << "rap:write sections: " << obj.name ().full () << std::endl; + + for (files::sections::const_iterator si = secs.begin (); + si != secs.end (); + ++si) + { + const files::section& sec = *si; + uint32_t unaligned_offset = offset + size; + + offset = align_offset (offset, size, sec.alignment); + + if (offset != unaligned_offset) + { + char ee = '\xee'; + for (uint32_t p = 0; p < (offset - unaligned_offset); ++p) + comp.write (&ee, 1); + } + + comp.write (obj, sec.offset, sec.size); + + if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) + std::cout << " sec: " << sec.index << ' ' << sec.name + << " offset=" << offset + << " size=" << sec.size + << " align=" << sec.alignment + << " padding=" << (offset - unaligned_offset) << std::endl; + + size = sec.size; + } + + offset += size; + + if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) + std::cout << " total size=" << offset << std::endl; + + obj.end (); + } + catch (...) + { + obj.close (); + throw; + } + + obj.close (); + } + + void + image::write_externals (compress::compressor& comp) + { + int count = 0; + for (externals::const_iterator ei = externs.begin (); + ei != externs.end (); + ++ei, ++count) + { + const external& ext = *ei; + + if (rld::verbose () >= RLD_VERBOSE_TRACE) + std::cout << "rap:externs: " << count + << " name=" << &strtab[ext.name] << " (" << ext.name << ')' + << " section=" << section_names[ext.sec] + << " data=" << ext.data + << " value=0x" << std::hex << ext.value << std::dec + << std::endl; + + if ((ext.data & 0xffff0000) != 0) + throw rld::error ("Data value has data in bits higher than 15", + "rap::write-externs"); + + comp << (uint32_t) ((ext.sec << 16) | ext.data) + << ext.name + << ext.value; + } + } + + void + image::write_relocations (compress::compressor& comp) + { + for (int s = 0; s < rap_secs; ++s) + { + uint32_t count = get_relocations (s); + uint32_t sr = 0; + uint32_t header; + + if (rld::verbose () >= RLD_VERBOSE_TRACE) + std::cout << "rap:relocation: section:" << section_names[s] + << " relocs=" << count + << " rela=" << (char*) (sec_rela[s] ? "yes" : "no") + << std::endl; + + header = count; + header |= sec_rela[s] ? RAP_RELOC_RELA : 0; + + comp << header; + + for (objects::iterator oi = objs.begin (); + oi != objs.end (); + ++oi) + { + object& obj = *oi; + section& sec = obj.secs[s]; + relocations& relocs = sec.relocs; + uint32_t rc = 0; + + if (rld::verbose () >= RLD_VERBOSE_TRACE) + std::cout << " relocs=" << sec.relocs.size () + << " sec.offset=" << sec.offset + << " sec.size=" << sec.size () + << " sec.align=" << sec.alignment () + << " " << obj.obj.name ().full () << std::endl; + + for (relocations::const_iterator ri = relocs.begin (); + ri != relocs.end (); + ++ri, ++sr, ++rc) + { + const relocation& reloc = *ri; + uint32_t info = GELF_R_TYPE (reloc.info); + uint32_t offset; + uint32_t addend = reloc.addend; + bool write_addend = sec.rela; + bool write_symname = false; + + offset = sec.offset + reloc.offset; + + if ((reloc.symtype == STT_SECTION) || (reloc.symbinding == STB_LOCAL)) + { + int rap_symsect = obj.find (reloc.symsect); + + /* + * Bit 31 clear, bits 30:8 RAP section index. + */ + info |= rap_symsect << 8; + + addend += (obj.secs[rap_symsect].offset + + obj.secs[rap_symsect].osecs[reloc.symsect].offset + + reloc.symvalue); + + write_addend = true; + + if (rld::verbose () >= RLD_VERBOSE_TRACE) + std::cout << " " << std::setw (2) << sr + << '/' << std::setw (2) << rc + <<": rsym: sect=" << section_names[rap_symsect] + << " rap_symsect=" << rap_symsect + << " sec.offset=" << obj.secs[rap_symsect].offset + << " sec.osecs=" << obj.secs[rap_symsect].osecs[reloc.symsect].offset + << " (" << obj.obj.get_section (reloc.symsect).name << ')' + << " reloc.symsect=" << reloc.symsect + << " reloc.symvalue=" << reloc.symvalue + << " reloc.addend=" << reloc.addend + << " addend=" << addend + << std::endl; + } + else + { + /* + * Bit 31 must be set. Bit 30 determines the type of string and + * bits 29:8 the strtab offset or the size of the appended + * string. + */ + + info |= RAP_RELOC_STRING; + + std::size_t size = find_in_strtab (reloc.symname); + + if (size == std::string::npos) + { + /* + * Bit 30 clear, the size of the symbol name. + */ + info |= reloc.symname.size () << 8; + write_symname = true; + } + else + { + /* + * Bit 30 set, the offset in the strtab. + */ + info |= RAP_RELOC_STRING_EMBED | (size << 8); + } + } + + if (rld::verbose () >= RLD_VERBOSE_TRACE) + { + std::cout << " " << std::setw (2) << sr << '/' + << std::setw (2) << rc + << std::hex << ": reloc: info=0x" << info << std::dec + << " offset=" << offset; + if (write_addend) + std::cout << " addend=" << addend; + if ((info & RAP_RELOC_STRING) != 0) + { + std::cout << " symname=" << reloc.symname; + if (write_symname) + std::cout << " (appended)"; + } + std::cout << std::hex + << " reloc.info=0x" << reloc.info << std::dec + << " reloc.offset=" << reloc.offset + << " reloc.symtype=" << reloc.symtype + << std::endl; + } + + comp << info << offset; + + if (write_addend) + comp << addend; + + if (write_symname) + comp << reloc.symname; + } + } + } + } + + void image::write_details (compress::compressor& comp) + { + + std::string strtable; + uint32_t pos = 0; + + section_details s_details; + + + if (rld::verbose () >= RLD_VERBOSE_TRACE) + { + std::cout << "rap:file details" << std::endl + << " total " << objs.size () <<" files" << std::endl; + } + + comp << (uint32_t)(objs.size ()); + + /* rpath for rap file */ + if (rld::verbose () >= RLD_VERBOSE_TRACE) + { + std::cout << "rap:file rpath=" << rld::rap::rpath << std::endl; + } + + comp << (uint32_t)rld::rap::rpath.length (); + + if (rld::rap::rpath.length () > 0) + { + strtable += rld::rap::rpath; + } + + for (objects::iterator oi = objs.begin (); + oi != objs.end (); + ++oi) + { + object& obj = *oi; + + /* obj full name */ + strtable += obj.obj.name ().full (); + strtable += '\0'; + } + + pos = strtable.length (); + + uint32_t sec_num = 0; + for (objects::iterator oi = objs.begin (); + oi != objs.end (); + ++oi) + { + object& obj = *oi; + + if (rld::verbose () >= RLD_VERBOSE_TRACE) + std::cout << "file:" << obj.obj.name ().full () << std::endl; + + for (int s = 0; s < rap_secs; ++s) + { + section& sec = obj.secs[s]; + + if (rld::verbose () >= RLD_VERBOSE_TRACE) + { + std::cout << "rap:section: " << sec.name << " " + "offset= " << sec.offset << std::endl; + } + + for (size_t si = 0; si < sec.osindexes.size (); ++si) + { + const osection& osec = sec.get_osection (sec.osindexes[si]); + + strtable += osec.name; + strtable += '\0'; + + /* sec.offset + osec.offset is the offset in the rap section */ + s_details.push_back (section_detail (pos, + sec.offset + osec.offset, + s, + osec.size)); + + pos = strtable.length (); + + if (rld::verbose () >= RLD_VERBOSE_TRACE) + { + std::cout << "osec.name=" << osec.name << " " + << "osec.offset=" << osec.offset << " " + << "osec.size=" << osec.size << std::endl; + } + } + } + + /* Output section numbers*/ + comp << (uint32_t)((s_details.size () - sec_num)); + if (rld::verbose () >= RLD_VERBOSE_TRACE) + std::cout << "sec_num:" << s_details.size () - sec_num << std::endl; + sec_num = s_details.size (); + } + + comp << (uint32_t)(strtable.size ()); + if (rld::verbose () >= RLD_VERBOSE_TRACE) + std::cout << "total detail size:" << strtable.size () << std::endl; + + comp << strtable; + + for (section_details::const_iterator si = s_details.begin (); + si != s_details.end (); + ++si) + { + const section_detail& sec_detail = *si; + comp << (uint32_t)(sec_detail.name); + + if (sec_detail.id > 0xf) + std::cout << "Out max rap section id 15\n" << std::endl; + + comp << (uint32_t)((sec_detail.id << 28) | sec_detail.offset); + comp << (uint32_t)(sec_detail.size); + } + } + + uint32_t + image::get_relocations (int sec) const + { + if ((sec < 0) || (sec >= rap_secs)) + throw rld::error ("Invalid section index '" + rld::to_string (sec), + "rap::image::relocations"); + + uint32_t relocs = 0; + + for (objects::const_iterator oi = objs.begin (); + oi != objs.end (); + ++oi) + { + const object& obj = *oi; + relocs += obj.get_relocations (sec); + } + + return relocs; + } + + void + image::clear () + { + for (int s = 0; s < rap_secs; ++s) + { + sec_size[s] = 0; + sec_align[s] = 0; + sec_rela[s] = false; + } + symtab_size = 0; + strtab.clear (); + relocs_size = 0; + init_off = 0; + fini_off = 0; + } + + uint32_t + image::section_size (sections sec) const + { + if ((sec < 0) || (sec >= rap_secs)) + throw rld::error ("Invalid section index '" + rld::to_string (sec), + "rap::image::section_size"); + return sec_size[sec]; + } + + std::size_t + image::find_in_strtab (const std::string& symname) + { + std::size_t pos = 0; + while (pos < strtab.size ()) + { + std::size_t off = strtab.find (symname, pos); + if (off == std::string::npos) + break; + if (::strlen (strtab.c_str () + off) == symname.size ()) + return off; + pos = off + 1; + } + return std::string::npos; + } + + void + write (files::image& app, + const std::string& init, + const std::string& fini, + const files::object_list& app_objects, + const symbols::table& /* symbols */) /* Add back for incremental + * linking */ + { + std::string header; + + header = "RAP,00000000,0002,LZ77,00000000\n"; + app.write (header.c_str (), header.size ()); + + compress::compressor compressor (app, 2 * 1024); + image rap; + + rap.layout (app_objects, init, fini); + rap.write (compressor); + + compressor.flush (); + + std::ostringstream length; + + length << std::setfill ('0') << std::setw (8) + << header.size () + compressor.compressed (); + + header.replace (4, 8, length.str ()); + + app.seek (0); + app.write (header.c_str (), header.size ()); + + if (rld::verbose () >= RLD_VERBOSE_INFO) + { + int pcent = (compressor.compressed () * 100) / compressor.transferred (); + int premand = (((compressor.compressed () * 1000) + 500) / + compressor.transferred ()) % 10; + std::cout << "rap: objects: " << app_objects.size () + << ", size: " << compressor.compressed () + << ", compression: " << pcent << '.' << premand << '%' + << std::endl; + } + } + + } +} |