/*
 * 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 symbols manages the symbols from all the object files.
 *
 */

#if !defined (_RLD_SYMBOLS_H_)
#define _RLD_SYMBOLS_H_

#include <iostream>
#include <list>
#include <map>
#include <string>

#include <rld-elf-types.h>

namespace rld
{
  /**
   * Forward declarations.
   */
  namespace files
  {
    class object;
  }

  namespace symbols
  {
    /**
     * C++ demangler.
     */
    bool is_cplusplus (const std::string& name);
    bool demangle_name (const std::string& name, std::string& demangled);

    /**
     * Use a local type for the address.
     */
    typedef elf::elf_addr address;

    /**
     * A symbol.
     */
    class symbol
    {
    public:
      /**
       * Types of symbols.
       */
      enum type {
        st_notype = STT_NOTYPE,            /* unspecified type */
        st_object = STT_OBJECT,            /* data object */
        st_func = STT_FUNC,                /* executable code */
        st_section = STT_SECTION,          /* section */
        st_file = STT_FILE,                /* source file */
        st_common = STT_COMMON,            /* uninitialized common block */
        st_tls = STT_TLS,                  /* thread local storage */
        st_loos = STT_LOOS,                /* start of OS-specific types */
        st_gnu_ifunc = STT_GNU_IFUNC,      /* indirect function */
        st_hios = STT_HIOS,                /* end of OS-specific types */
        st_loproc = STT_LOPROC,            /* start of processor-specific types */
        st_arm_tfunc = STT_ARM_TFUNC,      /* Thumb function (GNU) */
        st_arm_16bit = STT_ARM_16BIT,      /* Thumb label (GNU) */
        st_sparc_reg = STT_SPARC_REGISTER, /* SPARC register information */
        st_hiproc = STT_HIPROC,            /* end of processor-specific types */
      };

      /**
       * Default constructor. No symbol has been defined.
       */
      symbol ();

      /**
       * Construct an exported symbol with a object file.
       */
      symbol (int                 index,
              const std::string&  name,
              files::object&      object,
              const elf::elf_sym& esym);

      /**
       * Construct a symbol with no object file and an ELF index.
       */
      symbol (int index, const std::string& name, const elf::elf_sym& esym);

      /**
       * Construct a linker symbol that is internally created.
       */
      symbol (const std::string&  name,
              const elf::elf_addr value = 0);

      /**
       * Construct a linker symbol that is internally created.
       */
      symbol (const char*   name,
              elf::elf_addr value = 0);

      /**
       * The symbol's index in the symtab section of the ELF file.
       */
      int index () const;

      /**
       * The symbol's name.
       */
      const std::string& name () const;

      /**
       * The symbol's demangled name.
       */
      const std::string& demangled () const;

      /**
       * Is the symbol a C++ name ?
       */
      bool is_cplusplus () const;

      /**
       * Is the symbol binding is local ?
       */
      bool is_local () const;

      /**
       * Is the symbol binding weak ?
       */
      bool is_weak () const;

      /**
       * Is the symbol binding global ?
       */
      bool is_global () const;

      /**
       * The symbol's type.
       */
      int type () const;

      /**
       * The symbol's binding, ie local, weak, or global.
       */
      int binding () const;

      /**
       * The symbol's section index.
       */
      int section_index () const;

      /**
       * The value of the symbol.
       */
      elf::elf_addr value () const;

      /**
       * The data of the symbol.
       */
      uint32_t info () const;

      /**
       * The symbol's object file name.
       */
      files::object* object () const;

      /**
       * Set the symbol's object file name. Used when resolving unresolved
       * symbols.
       */
      void set_object (files::object& obj);

      /**
       * The ELF symbol.
       */
      const elf::elf_sym& esym () const;

      /**
       * Return the number of references.
       */
      int references () const {
        return references_;
      }

      /**
       * Return the number of references.
       */
      void referenced ();

      /**
       * Less than operator for the map container.
       */
      bool operator< (const symbol& rhs) const;

      /**
       * Output to the a stream.
       */
      void output (std::ostream& out) const;

    private:

      int            index_;      //< The symbol's index in the ELF file.
      std::string    name_;       //< The name of the symbol.
      std::string    demangled_;  //< If a C++ symbol the demangled name.
      files::object* object_;     //< The object file containing the symbol.
      elf::elf_sym   esym_;       //< The ELF symbol.
      int            references_; //< The number of times if it referenced.
    };

    /**
     * Container of symbols. A bucket of symbols.
     */
    typedef std::list < symbol > bucket;

    /**
     * References to symbols. Should always point to symbols held in a bucket.
     */
    typedef std::list < symbol* > pointers;

    /**
     * A symbols table is a map container of symbols. Should always point to
     * symbols held in a bucket.
     */
    typedef std::map < std::string, symbol* > symtab;

    /**
     * An address table is a map container of symbols based on address. Should
     * always point to symbols held in a bucket.
     */
    typedef std::map < address, symbol* > addrtab;

    /**
     * A symbols contains a symbol table of global, weak and local symbols.
     */
    class table
    {
    public:
      /**
       * Construct a table.
       */
      table ();

      /**
       * Destruct a table.
       */
      ~table ();

      /**
       * Add a global symbol.
       */
      void add_global (symbol& sym);

      /**
       * Add a weak symbol.
       */
      void add_weak (symbol& sym);

      /**
       * Add a local symbol.
       */
      void add_local (symbol& sym);

      /**
       * Find a global symbol.
       */
      symbol* find_global (const std::string& name);

      /**
       * Find an weak symbol.
       */
      symbol* find_weak (const std::string& name);

      /**
       * Find an local symbol.
       */
      symbol* find_local (const std::string& name);

      /**
       * Return the size of the symbols loaded.
       */
      size_t size () const;

      /**
       * Return the globals symbol table.
       */
      const symtab& globals () const;

      /**
       * Return the weaks symbol table.
       */
      const symtab& weaks () const;

      /**
       * Return the locals symbol table.
       */
      const symtab& locals () const;

      /**
       * Return the globals symbol table keyed by address.
       */
      void globals (addrtab& addresses);

      /**
       * Return the weak symbol table keyed by address.
       */
      void weaks (addrtab& addresses);

      /**
       * Return the local symbol table keyed by address.
       */
      void locals (addrtab& addresses);

    private:

      /**
       * Cannot copy a table.
       */
      table (const table& orig);

      /**
       * A table of global symbols.
       */
      symtab globals_;

      /**
       * A table of weak symbols.
       */
      symtab weaks_;

      /**
       * A table of local symbols.
       */
      symtab locals_;
    };

    /**
     * Load a table from a buckey.
     */
    void load (bucket& bucket_, table& table_);

    /**
     * Load a table from a buckey.
     */
    void load (bucket& bucket_, symtab& table_);

    /**
     * Given a container of symbols return how many are referenced.
     */
    size_t referenced (pointers& symbols);

    /**
     * Output the symbol table.
     */
    void output (std::ostream& out, const table& symbols);

    /**
     * Output the symbol table.
     */
    void output (std::ostream& out, const symtab& symbols);
  }
}

/**
 * Output stream operator.
 */
static inline std::ostream& operator<< (std::ostream&               out,
                                        const rld::symbols::symbol& sym) {
  sym.output (out);
  return out;
}

#endif
