text: // elfcpp_file.h -- file access for elfcpp   -*- C++ -*-
  
  // Copyright (C) 2006-2018 Free Software Foundation, Inc.
  // Written by Ian Lance Taylor <iant@google.com>.
  
  // This file is part of elfcpp.
     
  // This program is free software; you can redistribute it and/or
  // modify it under the terms of the GNU Library General Public License
  // as published by the Free Software Foundation; either version 2, or
  // (at your option) any later version.
  
  // In addition to the permissions in the GNU Library General Public
  // License, the Free Software Foundation gives you unlimited
  // permission to link the compiled version of this file into
  // combinations with other programs, and to distribute those
  // combinations without any restriction coming from the use of this
  // file.  (The Library Public License restrictions do apply in other
  // respects; for example, they cover modification of the file, and
  /// distribution when not linked into a combined executable.)
  
  // This program is distributed in the hope that it will be useful, but
  // WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  // Library General Public License for more details.
  
  // You should have received a copy of the GNU Library General Public
  // License along with this program; if not, write to the Free Software
  // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
  // 02110-1301, USA.
  
  // This header file defines the class Elf_file which can be used to
  // read useful data from an ELF file.  The functions here are all
  // templates which take a file interface object as a parameter.  This
  // type must have a subtype View.  This type must support two methods:
  //     View view(off_t file_offset, off_t data_size)
  // returns a View for the specified part of the file.
  //     void error(const char* printf_format, ...)
  // prints an error message and does not return.  The subtype View must
  // support a method
  //     const unsigned char* data()
  // which returns a pointer to a buffer containing the requested data.
  // This general interface is used to read data from the file.  Objects
  // of type View will never survive longer than the elfcpp function.
  
  // Some of these functions must return a reference to part of the
  // file.  To use these, the file interface must support a subtype
  // Location:
  //    Location(off_t file_offset, off_t data_size)
  // To use this in conjunction with the accessors types Shdr, etc., the
  // file interface should support an overload of view:
  //    View view(Location)
  // This permits writing
  //    elfcpp::Shdr shdr(file, ef.section_header(n));
  
  #ifndef ELFCPP_FILE_H
  #define ELFCPP_FILE_H
  
  #include <string>
  #include <cstdio>
  #include <cstring>
  
  #include "elfcpp.h"
  
  namespace elfcpp
  {
  
  // A simple helper class to recognize if a file has an ELF header.
  
  class Elf_recognizer
  {
   public:
    // Maximum header size.  The user should try to read this much of
    // the file when using this class.
  
    static const int max_header_size = Elf_sizes<64>::ehdr_size;
  
    // Checks if the file contains the ELF magic.  Other header fields
    // are not checked.
  
    static bool
    is_elf_file(const unsigned char* ehdr_buf, int size);
  
    // Check if EHDR_BUF/BUFSIZE is a valid header of a 32-bit or
    // 64-bit, little-endian or big-endian ELF file.  Assumes
    // is_elf_file() has been checked to be true.  If the header is not
    // valid, *ERROR contains a human-readable error message.  If is is,
    // *SIZE is set to either 32 or 64, *BIG_ENDIAN is set to indicate
    // whether the file is big-endian.
  
    static bool
    is_valid_header(const unsigned char* ehdr_buf, off_t bufsize,
                    int* size, bool* big_endian,
                    std::string* error);
  };
  
  // This object is used to read an ELF file.
  //   SIZE: The size of file, 32 or 64.
  //   BIG_ENDIAN: Whether the file is in big-endian format.
  //   FILE: A file reading type as described above.
  
  template<int size, bool big_endian, typename File>
  class Elf_file
  {
   private:
    typedef Elf_file<size, big_endian, File> This;
  
   public:
    static const int ehdr_size = Elf_sizes<size>::ehdr_size;
    static const int phdr_size = Elf_sizes<size>::phdr_size;
    static const int shdr_size = Elf_sizes<size>::shdr_size;
    static const int sym_size = Elf_sizes<size>::sym_size;
    static const int rel_size = Elf_sizes<size>::rel_size;
    static const int rela_size = Elf_sizes<size>::rela_size;
  
    typedef Ehdr<size, big_endian> Ef_ehdr;
    typedef Phdr<size, big_endian> Ef_phdr;
    typedef Shdr<size, big_endian> Ef_shdr;
    typedef Sym<size, big_endian> Ef_sym;
  
    // Construct an Elf_file given an ELF file header.
    Elf_file(File* file, const Ef_ehdr& ehdr)
    { this->construct(file, ehdr); }
  
    // Construct an ELF file.
    inline
    Elf_file(File* file);
  
    // Return the file offset to the section headers.
    off_t
    shoff() const
    { return this->shoff_; }
  
    // Find the first section with an sh_type field equal to TYPE and
    // return its index.  Returns SHN_UNDEF if there is no such section.
    unsigned int
    find_section_by_type(unsigned int type);
  
    // Return the number of sections.
    unsigned int
    shnum()
    {
      this->initialize_shnum();
      return this->shnum_;
    }
  
    unsigned int
    shnum() const
    {
      if (this->shnum_ == 0 && this->shoff_ != 0)
        this->file_->error(_("ELF file has not been initialized yet"
  			   " (internal error)"));
      return this->shnum_;
    }
  
    // Return the section index of the section name string table.
    unsigned int
    shstrndx()
    {
      this->initialize_shnum();
      return this->shstrndx_;
    }
  
    unsigned int
    shstrndx() const
    {
      if (this->shstrndx_ == SHN_XINDEX && this->shoff_ != 0)
        {
  	this->file_->error(_("ELF file has not been initialized yet"
  			     " (internal error)"));
  	return 0;
        }
      return this->shstrndx_;
    }
  
    // Return the value to subtract from section indexes >=
    // SHN_LORESERVE.  See the comment in initialize_shnum.
    int
    large_shndx_offset()
    {
      this->initialize_shnum();
      return this->large_shndx_offset_;
    }
  
    int
    large_shndx_offset() const
    {
      if (this->shstrndx_ == SHN_XINDEX && this->shoff_ != 0)
        this->file_->error(_("ELF file has not been initialized yet"
  			   " (internal error)"));
      return this->large_shndx_offset_;
    }
  
    // Return the location of the header of section SHNDX.
    typename File::Location
    section_header(unsigned int shndx)
    {
      return typename File::Location(this->section_header_offset(shndx),
  				   shdr_size);
    }
  
    // Return the name of section SHNDX.
    std::string
    section_name(unsigned int shndx) const;
  
    // Return the location of the contents of section SHNDX.
    typename File::Location
    section_contents(unsigned int shndx);
  
    // Return the size of section SHNDX.
    typename Elf_types<size>::Elf_WXword
    section_size(unsigned int shndx);
  
    // Return the flags of section SHNDX.
    typename Elf_types<size>::Elf_WXword
    section_flags(unsigned int shndx);
  
    // Return the address of section SHNDX.
    typename Elf_types<size>::Elf_Addr
    section_addr(unsigned int shndx);
  
    // Return the type of section SHNDX.
    Elf_Word
    section_type(unsigned int shndx);
  
    // Return the link field of section SHNDX.
    Elf_Word
    section_link(unsigned int shndx);
  
    // Return the info field of section SHNDX.
    Elf_Word
    section_info(unsigned int shndx);
  
    // Return the addralign field of section SHNDX.
    typename Elf_types<size>::Elf_WXword
    section_addralign(unsigned int shndx);
  
   private:
    // Shared constructor code.
    void
    construct(File* file, const Ef_ehdr& ehdr);
  
    // Initialize shnum_ and shstrndx_.
    void
    initialize_shnum();
  
    // Return the file offset of the header of section SHNDX.
    off_t
    section_header_offset(unsigned int shndx) const;
  
    // The file we are reading.
    File* file_;
    // The file offset to the section headers.
    off_t shoff_;
    // The number of sections.
    unsigned int shnum_;
    // The section index of the section name string table.
    unsigned int shstrndx_;
    // Offset to add to sections larger than SHN_LORESERVE.
    int large_shndx_offset_;
  };
  
  // A small wrapper around SHT_STRTAB data mapped to memory. It checks that the
  // index is not out of bounds and the string is NULL-terminated.
  
  class Elf_strtab
  {
   public:
    // Construct an Elf_strtab for a section with contents *P and size SIZE.
    Elf_strtab(const unsigned char* p, size_t size);
  
    // Return the file offset to the section headers.
    bool
    get_c_string(size_t offset, const char** cstring) const
    {
      if (offset >= this->usable_size_)
        return false;
      *cstring = this->base_ + offset;
      return true;
    }
  
   private:
    // Contents of the section mapped to memory.
    const char* base_;
    // One larger that the position of the last NULL character in the section.
    // For valid SHT_STRTAB sections, this is the size of the section.
    size_t usable_size_;
  };
  
  // Inline function definitions.
  
  // Check for presence of the ELF magic number.
  
  inline bool
  Elf_recognizer::is_elf_file(const unsigned char* ehdr_buf, int size)
  {
    if (size < 4)
      return false;
  
    static unsigned char elfmagic[4] =
      {
        elfcpp::ELFMAG0, elfcpp::ELFMAG1,
        elfcpp::ELFMAG2, elfcpp::ELFMAG3
      };
    return memcmp(ehdr_buf, elfmagic, 4) == 0;
  }
  
  namespace
  {
  
  // Print a number to a string.
  
  inline std::string
  internal_printf_int(const char* format, int arg)
  {
    char buf[256];
    snprintf(buf, sizeof(buf), format, arg);
    return std::string(buf);
  }
  
  }  // End anonymous namespace.
  
  // Check the validity of the ELF header.
  
  inline bool
  Elf_recognizer::is_valid_header(
      const unsigned char* ehdr_buf,
      off_t bufsize,
      int* size,
      bool* big_endian,
      std::string* error)
  {
    if (bufsize < elfcpp::EI_NIDENT)
      {
        *error = _("ELF file too short");
        return false;
      }
  
    int v = ehdr_buf[elfcpp::EI_VERSION];
    if (v != elfcpp::EV_CURRENT)
      {
        if (v == elfcpp::EV_NONE)
          *error = _("invalid ELF version 0");
        else
          *error = internal_printf_int(_("unsupported ELF version %d"), v);
        return false;
      }
  
    int c = ehdr_buf[elfcpp::EI_CLASS];
    if (c == elfcpp::ELFCLASSNONE)
      {
        *error = _("invalid ELF class 0");
        return false;
      }
    else if (c != elfcpp::ELFCLASS32
             && c != elfcpp::ELFCLASS64)
      {
        *error = internal_printf_int(_("unsupported ELF class %d"), c);
        return false;
      }
  
    int d = ehdr_buf[elfcpp::EI_DATA];
    if (d == elfcpp::ELFDATANONE)
      {
        *error = _("invalid ELF data encoding");
        return false;
      }
    else if (d != elfcpp::ELFDATA2LSB
             && d != elfcpp::ELFDATA2MSB)
      {
        *error = internal_printf_int(_("unsupported ELF data encoding %d"), d);
        return false;
      }
  
    *big_endian = (d == elfcpp::ELFDATA2MSB);
  
    if (c == elfcpp::ELFCLASS32)
      {
        if (bufsize < elfcpp::Elf_sizes<32>::ehdr_size)
          {
            *error = _("ELF file too short");
            return false;
          }
        *size = 32;
      }
    else
      {
        if (bufsize < elfcpp::Elf_sizes<64>::ehdr_size)
          {
            *error = _("ELF file too short");
            return false;
          }
        *size = 64;
      }
  
    return true;
  }
  
  // Template function definitions.
  
  // Construct an Elf_file given an ELF file header.
  
  template<int size, bool big_endian, typename File>
  void
  Elf_file<size, big_endian, File>::construct(File* file, const Ef_ehdr& ehdr)
  {
    this->file_ = file;
    this->shoff_ = ehdr.get_e_shoff();
    this->shnum_ = ehdr.get_e_shnum();
    this->shstrndx_ = ehdr.get_e_shstrndx();
    this->large_shndx_offset_ = 0;
    if (ehdr.get_e_ehsize() != This::ehdr_size)
      file->error(_("bad e_ehsize (%d != %d)"),
  		ehdr.get_e_ehsize(), This::ehdr_size);
    if (ehdr.get_e_shentsize() != This::shdr_size)
      file->error(_("bad e_shentsize (%d != %d)"),
  		ehdr.get_e_shentsize(), This::shdr_size);
  }
  
  // Construct an ELF file.
  
  template<int size, bool big_endian, typename File>
  inline
  Elf_file<size, big_endian, File>::Elf_file(File* file)
  {
    typename File::View v(file->view(file_header_offset, This::ehdr_size));
    this->construct(file, Ef_ehdr(v.data()));
  }
  
  // Initialize the shnum_ and shstrndx_ fields, handling overflow.
  
  template<int size, bool big_endian, typename File>
  void
  Elf_file<size, big_endian, File>::initialize_shnum()
  {
    if ((this->shnum_ == 0 || this->shstrndx_ == SHN_XINDEX)
        && this->shoff_ != 0)
      {
        typename File::View v(this->file_->view(this->shoff_, This::shdr_size));
        Ef_shdr shdr(v.data());
  
        if (this->shnum_ == 0)
  	this->shnum_ = shdr.get_sh_size();
  
        if (this->shstrndx_ == SHN_XINDEX)
  	{
  	  this->shstrndx_ = shdr.get_sh_link();
  
  	  // Versions of the GNU binutils between 2.12 and 2.18 did
  	  // not handle objects with more than SHN_LORESERVE sections
  	  // correctly.  All large section indexes were offset by
  	  // 0x100.  Some information can be found here:
  	  // http://sourceware.org/bugzilla/show_bug.cgi?id=5900 .
  	  // Fortunately these object files are easy to detect, as the
  	  // GNU binutils always put the section header string table
  	  // near the end of the list of sections.  Thus if the
  	  // section header string table index is larger than the
  	  // number of sections, then we know we have to subtract
  	  // 0x100 to get the real section index.
  	  if (this->shstrndx_ >= this->shnum_)
  	    {
  	      if (this->shstrndx_ >= elfcpp::SHN_LORESERVE + 0x100)
  		{
  		  this->large_shndx_offset_ = - 0x100;
  		  this->shstrndx_ -= 0x100;
  		}
  	      if (this->shstrndx_ >= this->shnum_)
  		this->file_->error(_("bad shstrndx: %u >= %u"),
  				   this->shstrndx_, this->shnum_);
  	    }
  	}
      }
  }
  
  // Find section with sh_type equal to TYPE and return its index.
  // Returns SHN_UNDEF if not found.
  
  template<int size, bool big_endian, typename File>
  unsigned int
  Elf_file<size, big_endian, File>::find_section_by_type(unsigned int type)
  {
    unsigned int shnum = this->shnum();
    typename File::View v(this->file_->view(this->shoff_,
  					  This::shdr_size * shnum));
    for (unsigned int i = 0; i < shnum; i++)
      {
        Ef_shdr shdr(v.data() + This::shdr_size * i);
        if (shdr.get_sh_type() == type)
          return i;
      }
    return SHN_UNDEF;
  }
  
  // Return the file offset of the section header of section SHNDX.
  
  template<int size, bool big_endian, typename File>
  off_t
  Elf_file<size, big_endian, File>::section_header_offset(unsigned int shndx) const
  {
    if (shndx >= this->shnum())
      this->file_->error(_("section_header_offset: bad shndx %u >= %u"),
  		       shndx, this->shnum());
    return this->shoff_ + This::shdr_size * shndx;
  }
  
  // Return the name of section SHNDX.
  
  template<int size, bool big_endian, typename File>
  std::string
  Elf_file<size, big_endian, File>::section_name(unsigned int shndx) const
  {
    File* const file = this->file_;
  
    // Get the section name offset.
    unsigned int sh_name;
    {
      typename File::View v(file->view(this->section_header_offset(shndx),
  				     This::shdr_size));
      Ef_shdr shdr(v.data());
      sh_name = shdr.get_sh_name();
    }
  
    // Get the file offset for the section name string table data.
    off_t shstr_off;
    typename Elf_types<size>::Elf_WXword shstr_size;
    {
      const unsigned int shstrndx = this->shstrndx_;
      typename File::View v(file->view(this->section_header_offset(shstrndx),
  				     This::shdr_size));
      Ef_shdr shstr_shdr(v.data());
      shstr_off = shstr_shdr.get_sh_offset();
      shstr_size = shstr_shdr.get_sh_size();
    }
  
    if (sh_name >= shstr_size)
      file->error(_("bad section name offset for section %u: %u"),
  		shndx, sh_name);
  
    typename File::View v(file->view(shstr_off, shstr_size));
  
    const unsigned char* datau = v.data();
    const char* data = reinterpret_cast<const char*>(datau);
    const void* p = ::memchr(data + sh_name, '\0', shstr_size - sh_name);
    if (p == NULL)
      file->error(_("missing null terminator for name of section %u"),
  		shndx);
  
    size_t len = static_cast<const char*>(p) - (data + sh_name);
  
    return std::string(data + sh_name, len);
  }
  
  // Return the contents of section SHNDX.
  
  template<int size, bool big_endian, typename File>
  typename File::Location
  Elf_file<size, big_endian, File>::section_contents(unsigned int shndx)
  {
    File* const file = this->file_;
  
    if (shndx >= this->shnum())
      file->error(_("section_contents: bad shndx %u >= %u"),
  		shndx, this->shnum());
  
    typename File::View v(file->view(this->section_header_offset(shndx),
  				   This::shdr_size));
    Ef_shdr shdr(v.data());
    return typename File::Location(shdr.get_sh_offset(), shdr.get_sh_size());
  }
  
  // Get the size of section SHNDX.
  
  template<int size, bool big_endian, typename File>
  typename Elf_types<size>::Elf_WXword
  Elf_file<size, big_endian, File>::section_size(unsigned int shndx)
  {
    File* const file = this->file_;
  
    if (shndx >= this->shnum())
      file->error(_("section_size: bad shndx %u >= %u"),
  		shndx, this->shnum());
  
    typename File::View v(file->view(this->section_header_offset(shndx),
  				   This::shdr_size));
  
    Ef_shdr shdr(v.data());
    return shdr.get_sh_size();
  }
  
  // Return the section flags of section SHNDX.
  
  template<int size, bool big_endian, typename File>
  typename Elf_types<size>::Elf_WXword
  Elf_file<size, big_endian, File>::section_flags(unsigned int shndx)
  {
    File* const file = this->file_;
  
    if (shndx >= this->shnum())
      file->error(_("section_flags: bad shndx %u >= %u"),
  		shndx, this->shnum());
  
    typename File::View v(file->view(this->section_header_offset(shndx),
  				   This::shdr_size));
  
    Ef_shdr shdr(v.data());
    return shdr.get_sh_flags();
  }
  
  // Return the address of section SHNDX.
  
  template<int size, bool big_endian, typename File>
  typename Elf_types<size>::Elf_Addr
  Elf_file<size, big_endian, File>::section_addr(unsigned int shndx)
  {
    File* const file = this->file_;
  
    if (shndx >= this->shnum())
      file->error(_("section_flags: bad shndx %u >= %u"),
  		shndx, this->shnum());
  
    typename File::View v(file->view(this->section_header_offset(shndx),
  				   This::shdr_size));
  
    Ef_shdr shdr(v.data());
    return shdr.get_sh_addr();
  }
  
  // Return the type of section SHNDX.
  
  template<int size, bool big_endian, typename File>
  Elf_Word
  Elf_file<size, big_endian, File>::section_type(unsigned int shndx)
  {
    File* const file = this->file_;
  
    if (shndx >= this->shnum())
      file->error(_("section_type: bad shndx %u >= %u"),
  		shndx, this->shnum());
  
    typename File::View v(file->view(this->section_header_offset(shndx),
  				   This::shdr_size));
  
    Ef_shdr shdr(v.data());
    return shdr.get_sh_type();
  }
  
  // Return the sh_link field of section SHNDX.
  
  template<int size, bool big_endian, typename File>
  Elf_Word
  Elf_file<size, big_endian, File>::section_link(unsigned int shndx)
  {
    File* const file = this->file_;
  
    if (shndx >= this->shnum())
      file->error(_("section_link: bad shndx %u >= %u"),
  		shndx, this->shnum());
  
    typename File::View v(file->view(this->section_header_offset(shndx),
  				   This::shdr_size));
  
    Ef_shdr shdr(v.data());
    return shdr.get_sh_link();
  }
  
  // Return the sh_info field of section SHNDX.
  
  template<int size, bool big_endian, typename File>
  Elf_Word
  Elf_file<size, big_endian, File>::section_info(unsigned int shndx)
  {
    File* const file = this->file_;
  
    if (shndx >= this->shnum())
      file->error(_("section_info: bad shndx %u >= %u"),
  		shndx, this->shnum());
  
    typename File::View v(file->view(this->section_header_offset(shndx),
  				   This::shdr_size));
  
    Ef_shdr shdr(v.data());
    return shdr.get_sh_info();
  }
  
  // Return the sh_addralign field of section SHNDX.
  
  template<int size, bool big_endian, typename File>
  typename Elf_types<size>::Elf_WXword
  Elf_file<size, big_endian, File>::section_addralign(unsigned int shndx)
  {
    File* const file = this->file_;
  
    if (shndx >= this->shnum())
      file->error(_("section_addralign: bad shndx %u >= %u"),
  		shndx, this->shnum());
  
    typename File::View v(file->view(this->section_header_offset(shndx),
  				   This::shdr_size));
  
    Ef_shdr shdr(v.data());
    return shdr.get_sh_addralign();
  }
  
  inline
  Elf_strtab::Elf_strtab(const unsigned char* p, size_t size)
  {
    // Check if the section is NUL-terminated. If it isn't, we ignore
    // the last part to make sure we don't return non-NUL-terminated
    // strings.
    while (size > 0 && p[size - 1] != 0)
      size--;
    this->base_ = reinterpret_cast<const char*>(p);
    this->usable_size_ = size;
  }
  
  } // End namespace elfcpp.
  
  #endif // !defined(ELFCPP_FILE_H)
  