/* * Copyright (c) 2011, Chris Johns * * 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. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #if __WIN32__ #define CREATE_MODE (S_IRUSR | S_IWUSR) #define OPEN_FLAGS (O_BINARY) #else #define CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) #define OPEN_FLAGS (0) #endif namespace rld { namespace files { /** * Scan the decimal number returning the value found. */ uint64_t scan_decimal (const uint8_t* string, size_t len) { uint64_t value = 0; while (len && (*string != ' ')) { value *= 10; value += *string - '0'; ++string; --len; } return value; } void set_number (uint32_t value, uint8_t* string, size_t len, bool octal = false) { std::ostringstream oss; if (octal) oss << std::oct; oss << value; size_t l = oss.str ().length (); if (l > len) l = len; memcpy (string, oss.str ().c_str (), l); } file::file (const std::string& aname, const std::string& oname, off_t offset, size_t size) : aname_ (aname), oname_ (oname), offset_ (offset), size_ (size) { } file::file (const std::string& path, bool is_object) : offset_ (0), size_ (0) { set (path, is_object); } file::file () : offset_ (0), size_ (0) { } void file::set (const std::string& path, bool is_object) { /* * If there is a path look for a colon. If there is no colon we assume * it is an object file. If the colon is the last character in the path * it is just an archive. */ if (!path.empty ()) { bool get_size = false; if (is_object) { size_t colon = path.find_last_of (':'); if ((colon != std::string::npos) && (colon > RLD_DRIVE_SEPARATOR)) { aname_ = path.substr (0, colon - 1); oname_ = path.substr (colon + 1); // @todo Add offset scanning. } else { oname_ = path; get_size = true; } } else { aname_ = path; get_size = true; } if (get_size) { struct stat sb; if (::stat (path.c_str (), &sb) == 0) size_ = sb.st_size; } } } bool file::is_archive () const { return !aname_.empty () && oname_.empty (); } bool file::is_object () const { return !oname_.empty (); } bool file::is_valid () const { return !aname_.empty () || !oname_.empty (); } bool file::exists () const { /* * No name set returns false. */ bool result = false; const std::string p = path (); if (!p.empty ()) result = path::check_file (p); return result; } const std::string file::path () const { if (!aname_.empty ()) return aname_; return oname_; } const std::string file::full () const { std::string f; if (!aname_.empty ()) { f = aname_; if (!oname_.empty ()) f += ':'; } if (!oname_.empty ()) f += oname_; if (!aname_.empty () && !oname_.empty ()) f += '@' + rld::to_string (offset_); return f; } const std::string file::basename () const { return rld::path::basename (full ()); } const std::string& file::aname () const { return aname_; } const std::string& file::oname () const { return oname_; } off_t file::offset () const { return offset_; } size_t file::size () const { return size_; } image::image (file& name) : name_ (name), references_ (0), fd_ (-1), symbol_refs (0), writable (false), remove (false) { } image::image (const std::string& path, bool is_object) : name_ (path, is_object), references_ (0), fd_ (-1), symbol_refs (0), writable (false), remove (false) { } image::image () : references_ (0), fd_ (-1), symbol_refs (0), writable (false), remove (false) { } image::~image () { if (references_) std::cerr << "rtl:file:image: references when destructing" << std::endl; if (fd_ >= 0) { ::close (fd_); fd_= -1; if (writable && remove) { if (rld::verbose () >= RLD_VERBOSE_INFO) std::cout << "image::close: removing " << name ().full () << std::endl; ::unlink (name_.path ().c_str ()); } } try { elf_.end (); } catch (rld::error re) { std::cerr << "error: rld::files::image:::~image: " << re.where << ": " << re.what << std::endl; } catch (...) { std::cerr << "error: rld::files::image:::~image: unhandled exception" << std::endl; } } void image::open (file& name) { name_ = name; open (); } void image::open (bool writable_) { const std::string path = name_.path (); if (path.empty ()) throw rld::error ("No file name", "open:" + path); if (rld::verbose () >= RLD_VERBOSE_TRACE_FILE) std::cout << "image::open: " << name (). full () << " refs:" << references_ + 1 << " writable:" << (char*) (writable_ ? "yes" : "no") << std::endl; if (fd_ < 0) { writable = writable_; if (writable) fd_ = ::open (path.c_str (), OPEN_FLAGS | O_RDWR | O_CREAT | O_TRUNC, CREATE_MODE); else fd_ = ::open (path.c_str (), OPEN_FLAGS | O_RDONLY); if (fd_ < 0) throw rld::error (::strerror (errno), "open:" + path); } else { if (writable_ != writable) throw rld::error ("Cannot change write status", "open:" + path); } ++references_; } void image::close () { if (references_ > 0) { if (rld::verbose () >= RLD_VERBOSE_TRACE_FILE) std::cout << "image::close: " << name ().full () << " refs:" << references_ << std::endl; --references_; if (references_ == 0) { ::close (fd_); fd_ = -1; if (writable && remove) { if (rld::verbose () >= RLD_VERBOSE_INFO) std::cout << "image::close: removing " << name ().full () << std::endl; ::unlink (name_.path ().c_str ()); remove = false; } } } } ssize_t image::read (void* buffer_, size_t size) { uint8_t* buffer = static_cast (buffer_); size_t have_read = 0; size_t to_read = size; while (have_read < size) { const ssize_t rsize = ::read (fd (), buffer, to_read); if (rsize < 0) throw rld::error (strerror (errno), "read:" + name ().path ()); if (rsize == 0) break; have_read += rsize; to_read -= rsize; buffer += rsize; } return have_read; } ssize_t image::write (const void* buffer_, size_t size) { const uint8_t* buffer = static_cast (buffer_); size_t have_written = 0; size_t to_write = size; while (have_written < size) { const ssize_t wsize = ::write (fd (), buffer, to_write); if (wsize < 0) throw rld::error (strerror (errno), "write:" + name ().path ()); have_written += wsize; to_write -= wsize; buffer += wsize; } return have_written; } void image::seek (off_t offset) { if (::lseek (fd (), name_.offset () + offset, SEEK_SET) < 0) throw rld::error (strerror (errno), "lseek:" + name ().path ()); } bool image::seek_read (off_t offset, uint8_t* buffer, size_t size) { seek (offset); return size == (size_t) read (buffer, size); } bool image::seek_write (off_t offset, const void* buffer, size_t size) { seek (offset); return size == (size_t) write (buffer, size); } const file& image::name () const { return name_; } int image::references () const { return references_; } size_t image::size () const { return name ().size (); } int image::fd () const { return fd_; } rld::elf::file& image::elf () { return elf_; } byteorder image::get_byteorder () const { switch (elf_.data_type ()) { case ELFDATA2LSB: return little_endian; case ELFDATA2MSB: return big_endian; } throw rld::error ("invalid elf data type", "byteorder: " + name ().path ()); } void image::symbol_referenced () { ++symbol_refs; } int image::symbol_references () const { return symbol_refs; } void copy_file (image& in, image& out, size_t size) { #define COPY_FILE_BUFFER_SIZE (8 * 1024) uint8_t* buffer = 0; if (size == 0) size = in.name ().size (); try { buffer = new uint8_t[COPY_FILE_BUFFER_SIZE]; while (size) { /* * @fixme the reading and writing are not POSIX; sigints could split them. */ size_t l = size < COPY_FILE_BUFFER_SIZE ? size : COPY_FILE_BUFFER_SIZE; ssize_t r = ::read (in.fd (), buffer, l); if (r < 0) throw rld::error (::strerror (errno), "reading: " + in.name ().full ()); if (r == 0) { std::ostringstream oss; oss << "reading: " + in.name ().full () << " (" << size << ')'; throw rld::error ("input too short", oss.str ()); } ssize_t w = ::write (out.fd (), buffer, r); if (w < 0) throw rld::error (::strerror (errno), "writing: " + out.name ().full ()); if (w != r) throw rld::error ("output trucated", "writing: " + out.name ().full ()); size -= r; } } catch (...) { delete [] buffer; throw; } if (buffer) delete [] buffer; } /** * Defines for the header of an archive. */ #define rld_archive_ident "!\n" #define rld_archive_ident_size (sizeof (rld_archive_ident) - 1) #define rld_archive_fhdr_base rld_archive_ident_size #define rld_archive_fname (0) #define rld_archive_fname_size (16) #define rld_archive_mtime (16) #define rld_archive_mtime_size (12) #define rld_archive_uid (28) #define rld_archive_uid_size (6) #define rld_archive_gid (34) #define rld_archive_gid_size (6) #define rld_archive_mode (40) #define rld_archive_mode_size (8) #define rld_archive_size (48) #define rld_archive_size_size (10) #define rld_archive_magic (58) #define rld_archive_magic_size (2) #define rld_archive_fhdr_size (60) #define rld_archive_max_file_size (1024) archive::archive (const std::string& path) : image (path, false) { if (!name ().is_valid ()) throw rld_error_at ("name is empty"); if (!name ().is_archive ()) throw rld_error_at ("name is not an archive: " + name ().oname ()); } archive::~archive () { try { end (); close (); } catch (rld::error re) { std::cerr << "error: rld::files::archive::~archive: " << re.where << ": " << re.what << std::endl; } catch (...) { std::cerr << "error: rld::files::archive::~archive: unhandled exception" << std::endl; } } void archive::begin () { if (references () == 1) { elf ().begin (name ().full (), fd ()); /* * Make sure it is an archive. */ if (!elf ().is_archive ()) throw rld::error ("Not an archive.", "archive-begin:" + name ().full ()); } } void archive::end () { if (references () == 1) elf ().end (); } bool archive::is (const std::string& path) const { return name ().path () == path; } bool archive::is_valid () { open (); uint8_t header[rld_archive_ident_size]; seek_read (0, &header[0], rld_archive_ident_size); bool result = ::memcmp (header, rld_archive_ident, rld_archive_ident_size) == 0 ? true : false; close (); return result; } void archive::load_objects (objects& objs) { off_t extended_file_names = 0; off_t offset = rld_archive_fhdr_base; size_t size = 0; while (true) { uint8_t header[rld_archive_fhdr_size]; if (!read_header (offset, &header[0])) break; /* * The archive file headers are always aligned to an even address. */ size = (scan_decimal (&header[rld_archive_size], rld_archive_size_size) + 1) & ~1; /* * Check for the GNU extensions. */ if (header[0] == '/') { off_t extended_off; switch (header[1]) { case ' ': /* * Symbols table. Ignore the table. */ break; case '/': /* * Extended file names table. Remember. */ extended_file_names = offset + rld_archive_fhdr_size; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* * Offset into the extended file name table. If we do not have the * offset to the extended file name table find it. */ extended_off = scan_decimal (&header[1], rld_archive_fname_size); if (extended_file_names == 0) { off_t off = offset; while (extended_file_names == 0) { size_t esize = (scan_decimal (&header[rld_archive_size], rld_archive_size_size) + 1) & ~1; off += esize + rld_archive_fhdr_size; if (!read_header (off, &header[0])) throw rld::error ("No GNU extended file name section found", "get-names:" + name ().path ()); if ((header[0] == '/') && (header[1] == '/')) { extended_file_names = off + rld_archive_fhdr_size; break; } } } if (extended_file_names) { /* * We know the offset in the archive to the extended file. Read * the name from the table and compare with the name we are * after. */ char cname[rld_archive_max_file_size]; seek_read (extended_file_names + extended_off, (uint8_t*) &cname[0], rld_archive_max_file_size); add_object (objs, cname, offset + rld_archive_fhdr_size, size); } break; default: /* * Ignore the file because we do not know what it it. */ break; } } else { /* * Normal archive name. */ add_object (objs, (char*) &header[rld_archive_fname], offset + rld_archive_fhdr_size, size); } offset += size + rld_archive_fhdr_size; } } bool archive::operator< (const archive& rhs) const { return name ().path () < rhs.name ().path (); } bool archive::read_header (off_t offset, uint8_t* header) { if (!seek_read (offset, header, rld_archive_fhdr_size)) return false; if ((header[rld_archive_magic] != 0x60) || (header[rld_archive_magic + 1] != 0x0a)) throw rld::error ("Invalid header magic numbers at " + rld::to_string (offset), "read-header:" + name ().path ()); return true; } void archive::add_object (objects& objs, const char* path, off_t offset, size_t size) { const char* end = path; while ((*end != '\0') && (*end != '/') && (*end != '\n')) ++end; std::string str; str.append (path, end - path); if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) std::cout << "archive::add-object: " << str << std::endl; file n (name ().path (), str, offset, size); objs[n.full()] = new object (*this, n); } void archive::write_header (const std::string& name, uint32_t mtime, int uid, int gid, int mode, size_t size) { uint8_t header[rld_archive_fhdr_size]; memset (header, ' ', sizeof (header)); size_t len = name.length (); if (len > rld_archive_fname_size) len = rld_archive_fname_size; memcpy (&header[rld_archive_fname], &name[0], len); set_number (mtime, header + rld_archive_mtime, rld_archive_mtime_size); set_number (uid, header + rld_archive_uid, rld_archive_uid_size); set_number (gid, header + rld_archive_gid, rld_archive_gid_size); set_number (mode, header + rld_archive_mode, rld_archive_mode_size, true); set_number (size, header + rld_archive_size, rld_archive_size_size); header[rld_archive_magic] = 0x60; header[rld_archive_magic + 1] = 0x0a; write (header, sizeof (header)); } void archive::create (object_list& objects) { if (rld::verbose () >= RLD_VERBOSE_DETAILS) std::cout << "archive::create: " << name ().full () << ", objects: " << objects.size () << std::endl; open (true); try { seek_write (0, rld_archive_ident, rld_archive_ident_size); /* * GNU extended filenames. */ std::string extended_file_names; for (object_list::iterator oi = objects.begin (); oi != objects.end (); ++oi) { object& obj = *(*oi); const std::string& oname = path::basename (obj.name ().oname ()); if (oname.length () >= rld_archive_fname_size) extended_file_names += oname + '\n'; } if (!extended_file_names.empty ()) { if (extended_file_names.length () & 1) { extended_file_names += ' '; } write_header ("//", 0, 0, 0, 0, extended_file_names.length ()); write (extended_file_names.c_str (), extended_file_names.length ()); } for (object_list::iterator oi = objects.begin (); oi != objects.end (); ++oi) { object& obj = *(*oi); obj.open (); try { std::string oname = path::basename (obj.name ().oname ()); /* * Convert the file name to an offset into the extended file name * table if the file name is too long for the header. */ if (oname.length () >= rld_archive_fname_size) { size_t pos = extended_file_names.find (oname + '\n'); if (pos == std::string::npos) throw rld_error_at ("extended file name not found"); std::ostringstream oss; oss << '/' << pos; oname = oss.str (); } else oname += '/'; write_header (oname, 0, 0, 0, 0666, (obj.name ().size () + 1) & ~1); obj.seek (0); copy_file (obj, *this); if (obj.name ().size () & 1) write ("\n", 1); } catch (...) { obj.close (); throw; } obj.close (); } } catch (...) { close (); throw; } close (); } relocation::relocation (const elf::relocation& er) : offset (er.offset ()), type (er.type ()), info (er.info ()), addend (er.addend ()), symname (er.symbol ().name ()), symtype (er.symbol ().type ()), symsect (er.symbol ().section_index ()), symvalue (er.symbol ().value ()), symbinding (er.symbol ().binding ()) { } section::section (const elf::section& es) : name (es.name ()), index (es.index ()), type (es.type ()), size (es.size ()), alignment (es.alignment ()), link (es.link ()), info (es.info ()), flags (es.flags ()), offset (es.offset ()), address (es.address ()), rela (es.get_reloc_type ()) { } void section::load_relocations (const elf::section& es) { const elf::relocations& es_relocs = es.get_relocations (); for (elf::relocations::const_iterator ri = es_relocs.begin (); ri != es_relocs.end (); ++ri) { relocs.push_back (relocation (*ri)); } rela = es.get_reloc_type (); } size_t sum_sizes (const sections& secs) { size_t size = 0; for (sections::const_iterator si = secs.begin (); si != secs.end (); ++si) { const section& sec = *si; if ((size % sec.alignment) != 0) size -= (size % sec.alignment) + sec.alignment; size += sec.size; } return size; } const section* find (const sections& secs, const int index) { for (sections::const_iterator si = secs.begin (); si != secs.end (); ++si) { const section& sec = *si; if (index == sec.index) return &sec; } return 0; } object::object (archive& archive_, file& name_) : image (name_), archive_ (&archive_), valid_ (false), resolving_ (false), resolved_ (false) { if (!name ().is_valid ()) throw rld_error_at ("name is empty"); } object::object (const std::string& path) : image (path), archive_ (0), valid_ (false), resolving_ (false), resolved_ (false) { if (!name ().is_valid ()) throw rld_error_at ("name is empty"); } object::object () : archive_ (0), valid_ (false), resolving_ (false), resolved_ (false) { } object::~object () { try { end (); close (); } catch (rld::error re) { std::cerr << "error: rld::files::object::~object: " << re.where << ": " << re.what << std::endl; } catch (...) { std::cerr << "error: rld::files::object::~object: unhandled exception" << std::endl; } } void object::open (bool writable) { if (archive_) { if (writable) throw rld_error_at ("object files in archives are not writable"); archive_->open (); } else image::open (writable); } void object::close () { if (archive_) { archive_->end (); archive_->close (); } else { end (); image::close (); } } void object::begin () { /* * Begin a session. */ if (rld::verbose () >= RLD_VERBOSE_TRACE_FILE) std::cout << "object:begin: " << name ().full () << " in-archive:" << ((char*) (archive_ ? "yes" : "no")) << std::endl; if (archive_) elf ().begin (name ().full (), archive_->elf(), name ().offset ()); else elf ().begin (name ().full (), fd (), is_writable ()); /* * Cannot be an archive. */ if (elf ().is_archive ()) throw rld::error ("Is an archive not an object file.", "object-begin:" + name ().full ()); /* * We only support executable or relocatable ELF files. */ if (!is_writable ()) { if (!elf ().is_executable () && !elf ().is_relocatable ()) throw rld::error ("Invalid ELF type (only ET_EXEC/ET_REL supported).", "object-begin:" + name ().full ()); elf::check_file (elf ()); /** * We assume the ELF file is invariant over the linking process. */ if (secs.empty ()) { elf::sections elf_secs; elf ().get_sections (elf_secs, 0); for (elf::sections::const_iterator esi = elf_secs.begin (); esi != elf_secs.end (); ++esi) { secs.push_back (section (*(*esi))); } } } /* * This is a valid object file. The file format checks out. */ valid_ = true; } void object::end () { if (rld::verbose () >= RLD_VERBOSE_TRACE_FILE) std::cout << "object:end: " << name ().full () << std::endl; elf ().end (); } bool object::valid () const { return valid_; } void object::load_symbols (rld::symbols::table& symbols, bool local) { if (rld::verbose () >= RLD_VERBOSE_TRACE_SYMS) std::cout << "object:load-sym: " << name ().full () << std::endl; rld::symbols::pointers syms; if (local) { elf ().get_symbols (syms, false, true, false, false); if (rld::verbose () >= RLD_VERBOSE_TRACE_SYMS) std::cout << "object:load-sym: local: total " << syms.size () << std::endl; for (symbols::pointers::iterator si = syms.begin (); si != syms.end (); ++si) { symbols::symbol& sym = *(*si); if (rld::verbose () >= RLD_VERBOSE_TRACE_SYMS) std::cout << "object:load-sym: local: " << sym << std::endl; sym.set_object (*this); symbols.add_local (sym); } } elf ().get_symbols (syms, false, false, true, false); if (rld::verbose () >= RLD_VERBOSE_TRACE_SYMS) std::cout << "object:load-sym: weak: total " << syms.size () << std::endl; for (symbols::pointers::iterator si = syms.begin (); si != syms.end (); ++si) { symbols::symbol& sym = *(*si); if (rld::verbose () >= RLD_VERBOSE_TRACE_SYMS) std::cout << "object:load-sym: weak: " << sym << std::endl; sym.set_object (*this); symbols.add_weak (sym); externals.push_back (&sym); } elf ().get_symbols (syms, false, false, false, true); if (rld::verbose () >= RLD_VERBOSE_TRACE_SYMS) std::cout << "object:load-sym: global: total " << syms.size () << std::endl; for (symbols::pointers::iterator si = syms.begin (); si != syms.end (); ++si) { symbols::symbol& sym = *(*si); if (rld::verbose () >= RLD_VERBOSE_TRACE_SYMS) std::cout << "object:load-sym: global: " << sym << std::endl; sym.set_object (*this); symbols.add_global (sym); externals.push_back (&sym); } elf ().get_symbols (syms, true, false, true, true); if (rld::verbose () >= RLD_VERBOSE_TRACE_SYMS) std::cout << "object:load-sym: unresolved: total " << syms.size () << std::endl; for (symbols::pointers::iterator si = syms.begin (); si != syms.end (); ++si) { symbols::symbol& sym = *(*si); if (rld::verbose () >= RLD_VERBOSE_TRACE_SYMS) { std::cout << "object:load-sym: unresolved: "; sym.output (std::cout); std::cout << std::endl; } unresolved[sym.name ()] = &sym; } } void object::load_relocations () { if (rld::verbose () >= RLD_VERBOSE_TRACE) std::cout << "object:load-relocs: " << name ().full () << std::endl; elf ().load_relocations (); for (sections::iterator si = secs.begin (); si != secs.end (); ++si) { section& sec = *si; const elf::section& elf_sec = elf ().get_section (sec.index); sec.load_relocations (elf_sec); } } int object::references () const { if (archive_) return archive_->references (); return image::references (); } size_t object::size () const { if (archive_) return archive_->size (); return image::size (); } int object::fd () const { if (archive_) return archive_->fd (); return image::fd (); } void object::symbol_referenced () { image::symbol_referenced (); if (archive_) archive_->symbol_referenced (); } archive* object::get_archive () { return archive_; } rld::symbols::symtab& object::unresolved_symbols () { return unresolved; } rld::symbols::pointers& object::external_symbols () { return externals; } void object::get_sections (sections& filtered_secs, uint32_t type, uint64_t flags_in, uint64_t flags_out) { for (sections::const_iterator si = secs.begin (); si != secs.end (); ++si) { const section& sec = *si; if ((type == 0) || (type == sec.type)) { if ((flags_in == 0) || (((sec.flags & flags_in) == flags_in) && ((sec.flags & flags_out) == 0))) { filtered_secs.push_back (sec); } } } } void object::get_sections (sections& filtered_secs, const std::string& matching_name) { for (sections::const_iterator si = secs.begin (); si != secs.end (); ++si) { const section& sec = *si; if (sec.name == matching_name) { filtered_secs.push_back (sec); } } } const section& object::get_section (int index) const { for (sections::const_iterator si = secs.begin (); si != secs.end (); ++si) { const section& sec = *si; if (sec.index == index) return sec; } throw rld::error ("Section index '" + rld::to_string (index) + "' not found: " + name ().full (), "object::get-section"); } void object::resolve_set () { resolving_ = true; } void object::resolve_clear () { resolving_ = false; } bool object::resolving () const { return resolving_; } void object::resolved_set () { resolved_ = true; } bool object::resolved () const { return resolved_; } cache::cache () : opened (false) { } cache::~cache () { try { close (); } catch (rld::error re) { std::cerr << "error: rld::files:cache::~cache: " << re.where << ": " << re.what << std::endl; } catch (...) { std::cerr << "error: rld::files::cache::~cache: unhandled exception" << std::endl; } } void cache::open () { if (!opened) { collect_object_files (); archives_begin (); opened = true; } } void cache::close () { if (opened) { /* * Must delete the object first as they could depend on archives. */ for (objects::iterator oi = objects_.begin (); oi != objects_.end (); ++oi) delete (*oi).second; for (archives::iterator ai = archives_.begin (); ai != archives_.end (); ++ai) delete (*ai).second; opened = false; } } void cache::add (const std::string& path) { paths_.push_back (path); input (path); } void cache::add (path::paths& paths__) { for (path::paths::iterator pi = paths__.begin(); pi != paths__.end(); ++pi) add (*pi); } void cache::add_libraries (path::paths& paths__) { for (path::paths::iterator pi = paths__.begin(); pi != paths__.end(); ++pi) input (*pi); } void cache::archive_begin (const std::string& path) { archives::iterator ai = archives_.find (path); if (ai != archives_.end ()) { archive* ar = (*ai).second; if (!ar->is_open ()) { if (rld::verbose () >= RLD_VERBOSE_TRACE) std::cout << "cache:archive-begin: " << path << std::endl; ar->open (); ar->begin (); } } } void cache::archive_end (const std::string& path) { archives::iterator ai = archives_.find (path); if (ai != archives_.end ()) { archive* ar = (*ai).second; if (ar->is_open ()) { if (rld::verbose () >= RLD_VERBOSE_TRACE) std::cout << "cache:archive-end: " << path << std::endl; ar->end (); ar->close (); } } } void cache::archives_begin () { for (archives::iterator ai = archives_.begin (); ai != archives_.end (); ++ai) archive_begin (((*ai).second)->path ()); } void cache::archives_end () { for (archives::iterator ai = archives_.begin (); ai != archives_.end (); ++ai) archive_end (((*ai).second)->path ()); } void cache::collect_object_files () { for (path::paths::iterator ni = paths_.begin (); ni != paths_.end (); ++ni) collect_object_files (*ni); } void cache::collect_object_files (const std::string& path) { archive* ar = new archive (path); if (ar->is_valid ()) { try { ar->open (); ar->load_objects (objects_); ar->close (); archives_[path] = ar; } catch (...) { delete ar; throw; } } else { delete ar; object* obj = new object (path); if (!obj->name ().exists ()) { delete obj; throw rld::error ("'" + path + "', Not found or a regular file.", "file-check"); } try { obj->open (); obj->begin (); obj->end (); obj->close (); objects_[path] = obj; } catch (...) { delete obj; throw; } } } void cache::load_symbols (rld::symbols::table& symbols, bool local) { if (rld::verbose () >= RLD_VERBOSE_INFO) std::cout << "cache:load-sym: object files: " << objects_.size () << std::endl; for (objects::iterator oi = objects_.begin (); oi != objects_.end (); ++oi) { object* obj = (*oi).second; obj->open (); obj->begin (); obj->load_symbols (symbols, local); obj->end (); obj->close (); } if (rld::verbose () >= RLD_VERBOSE_INFO) std::cout << "cache:load-sym: symbols: " << symbols.size () << std::endl; } void cache::output_unresolved_symbols (std::ostream& out) { for (objects::iterator oi = objects_.begin (); oi != objects_.end (); ++oi) { object* obj = (*oi).second; if (obj) { out << obj->name ().full () << ':' << std::endl; rld::symbols::output (out, obj->unresolved_symbols ()); } } } archives& cache::get_archives () { return archives_; } objects& cache::get_objects () { return objects_; } void cache::get_objects (object_list& list) const { list.clear (); for (path::paths::const_iterator pi = paths_.begin (); pi != paths_.end (); ++pi) { objects::const_iterator oi = objects_.find (*pi); if (oi == objects_.end ()) throw rld::error ("Not located or a valid format", *pi); list.push_back ((*oi).second); } } const path::paths& cache::get_paths () const { return paths_; } int cache::archive_count () const { return archives_.size (); } int cache::object_count () const { return objects_.size (); } int cache::path_count () const { return paths_.size (); } void cache::get_archive_files (files& afiles) { for (archives::iterator ai = archives_.begin (); ai != archives_.end (); ++ai) afiles.push_back ((*ai).second->name ().full ()); } void cache::get_object_files (files& ofiles) { for (objects::iterator oi = objects_.begin (); oi != objects_.end (); ++oi) ofiles.push_back ((*oi).second->name ()); } void cache::output_archive_files (std::ostream& out) { for (archives::iterator ai = archives_.begin (); ai != archives_.end (); ++ai) out << ' ' << (*ai).second->name ().full () << std::endl; } void cache::output_object_files (std::ostream& out) { for (objects::iterator oi = objects_.begin (); oi != objects_.end (); ++oi) out << ' ' << (*oi).second->name ().full () << std::endl; } void cache::input (const std::string& path) { if (opened) { collect_object_files (path); archive_begin (path); } } void find_libraries (path::paths& libraries, path::paths& libpaths, path::paths& libs) { if (rld::verbose () >= RLD_VERBOSE_INFO) std::cout << "Finding libraries:" << std::endl; libraries.clear (); for (path::paths::size_type l = 0; l < libs.size (); ++l) { std::string lib = "lib" + libs[l] + ".a"; if (rld::verbose () >= RLD_VERBOSE_DETAILS) std::cout << " searching: " << lib << std::endl; bool found = false; for (path::paths::size_type p = 0; p < libpaths.size (); ++p) { std::string plib; path::path_join (libpaths[p], lib, plib); if (rld::verbose () >= RLD_VERBOSE_DETAILS) std::cout << " checking: " << plib << std::endl; if (path::check_file (plib)) { if (rld::verbose () >= RLD_VERBOSE_INFO) std::cout << " found: " << plib << std::endl; libraries.push_back (plib); found = true; break; } } if (!found) throw rld::error ("Not found", lib); } } } }