// arm-reloc-property.h -- ARM relocation properties   -*- C++ -*-

// Copyright (C) 2010-2020 Free Software Foundation, Inc.
// Written by Doug Kwan <dougkwan@google.com>.

// This file is part of gold.

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.

// 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 General Public License for more details.

// You should have received a copy of the GNU 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.

#ifndef GOLD_ARM_RELOC_PROPERTY_H
#define GOLD_ARM_RELOC_PROPERTY_H

namespace gold
{
// The Arm_reloc_property class is to store information about a particular
// relocation code.

class Arm_reloc_property
{
 public:
  // Types of relocation codes.
  enum Reloc_type {
    RT_NONE,		// No relocation type.
    RT_STATIC,	// Relocations processed by static linkers.
    RT_DYNAMIC,	// Relocations processed by dynamic linkers.
    RT_PRIVATE,	// Private relocations, not supported by gold.
    RT_OBSOLETE	// Obsolete relocations that should not be used.
  };

  // Classes of relocation codes.
  enum Reloc_class {
    RC_NONE,	// No relocation class.
    RC_DATA,	// Data relocation.
    RC_ARM,	// ARM instruction relocation.
    RC_THM16,	// 16-bit THUMB instruction relocation.
    RC_THM32,	// 32-bit THUMB instruction relocation.
    RC_MISC	// Miscellaneous class.
  };

  // Types of bases of relative addressing relocation codes.
  enum Relative_address_base {
    RAB_NONE,		// Relocation is not relative addressing
    RAB_B_S,		// Address origin of output segment of defining symbol.
    RAB_DELTA_B_S,	// Change of address origin.
    RAB_GOT_ORG,	// Origin of GOT.
    RAB_P,		// Address of the place being relocated.
    RAB_Pa,		// Adjusted address (P & 0xfffffffc).
    RAB_TLS,		// Thread local storage.
    RAB_tp		// Thread pointer.
  };

  // Relocation code represented by this.
  unsigned int
  code() const
  { return this->code_; }

  // Name of the relocation code.
  const std::string&
  name() const
  { return this->name_; }
  
  // Type of relocation code.
  Reloc_type
  reloc_type() const
  { return this->reloc_type_; }

  // Whether this code is deprecated.
  bool
  is_deprecated() const
  { return this->is_deprecated_; }

  // Class of relocation code.
  Reloc_class
  reloc_class() const
  { return this->reloc_class_; }

  // Whether this code is implemented in gold.
  bool
  is_implemented() const
  { return this->is_implemented_; }

  // If code is a group relocation code, return the group number, otherwise -1.
  int
  group_index() const
  { return this->group_index_; }

  // Whether relocation checks for overflow.
  bool
  checks_overflow() const
  { return this->checks_overflow_; }

  // Return size of relocation.
  size_t
  size() const
  { return this->size_; }

  // Return alignment of relocation.
  size_t
  align() const
  { return this->align_; }

  // Whether relocation use a GOT entry.
  bool
  uses_got_entry() const
  { return this->uses_got_entry_; }

  // Whether relocation use a GOT origin.
  bool
  uses_got_origin() const
  { return this->uses_got_origin_; }
  
  // Whether relocation uses the Thumb-bit in a symbol address.
  bool
  uses_thumb_bit() const
  { return this->uses_thumb_bit_; }

  // Whether relocation uses the symbol base.
  bool
  uses_symbol_base() const
  { return this->uses_symbol_base_; }

  // Whether relocation uses the symbol.
  bool
  uses_symbol() const
  { return this->uses_symbol_; }

  // Return the type of relative address base or RAB_NONE if this
  // is not a relative addressing relocation.
  Relative_address_base
  relative_address_base() const
  { return this->relative_address_base_; } 

 protected:
  // These are protected.  We only allow Arm_reloc_property_table to
  // manage Arm_reloc_property. 
  Arm_reloc_property(unsigned int code, const char* name, Reloc_type rtype,
		     bool is_deprecated, Reloc_class rclass,
		     const std::string& operation, bool is_implemented,
		     int group_index, bool checks_overflow);

  friend class Arm_reloc_property_table;
  
 private:
  // Copying is not allowed.
  Arm_reloc_property(const Arm_reloc_property&);
  Arm_reloc_property& operator=(const Arm_reloc_property&);

  // The Tree_node class is used to represent parsed relocation operations. 
  // We look at Trees to extract information about relocation operations.
  class Tree_node
  {
   public:
    typedef std::vector<Tree_node*> Tree_node_vector;

    // Construct a leaf node.
    Tree_node(const char* name)
      : is_leaf_(true), name_(name), children_()
    { }

    // Construct an internal node.  A node owns all its children and is
    // responsible for releasing them at its own destruction.
    Tree_node(Tree_node_vector::const_iterator begin,
	      Tree_node_vector::const_iterator end)
      : is_leaf_(false), name_(), children_()
    {
      for (Tree_node_vector::const_iterator p = begin; p != end; ++p)
	this->children_.push_back(*p);
    }

    ~Tree_node()
    {
      for(size_t i = 0; i <this->children_.size(); ++i)
	delete this->children_[i];
    }

    // Whether this is a leaf node.
    bool
    is_leaf() const
    { return this->is_leaf_; }

    // Return name of this.  This is only valid for a leaf node.
    const std::string&
    name() const
    {
      gold_assert(this->is_leaf_);
      return this->name_;
    }

    // Return the number of children.  This is only valid for a non-leaf node.
    size_t
    number_of_children() const
    {
      gold_assert(!this->is_leaf_);
      return this->children_.size();
    }

    // Return the i-th child of this.  This is only valid for a non-leaf node.
    Tree_node*
    child(size_t i) const
    {
      gold_assert(!this->is_leaf_ && i < this->children_.size());
      return this->children_[i];
    }

    // Parse an S-expression string and build a tree and return the root node.
    // Caller is responsible for releasing tree after use.
    static Tree_node*
    make_tree(const std::string&);

    // Convert a tree back to an S-expression string.
    std::string
    s_expression() const
    {
      if (this->is_leaf_)
	return this->name_;

      // Concatenate S-expressions of children. Enclose them with
      // a pair of parentheses and use space as token delimiters.
      std::string s("(");
      for(size_t i = 0; i <this->children_.size(); ++i)
	s = s + " " + this->children_[i]->s_expression();
      return s + " )";
    }

   private:
    // Whether this is a leaf node.
    bool is_leaf_;
    // Name of this if this is a leaf node.
    std::string name_;
    // Children of this if this a non-leaf node.
    Tree_node_vector children_;
  };

  // Relocation code.
  unsigned int code_;
  // Relocation name.
  std::string name_;
  // Type of relocation.
  Reloc_type reloc_type_;
  // Class of relocation.
  Reloc_class reloc_class_;
  // Group index (0, 1, or 2) if this is a group relocation or -1 otherwise.
  int group_index_; 
  // Size of relocation.
  size_t size_;
  // Alignment of relocation.
  size_t align_;
  // Relative address base.
  Relative_address_base relative_address_base_;
  // Whether this is deprecated.
  bool is_deprecated_ : 1;
  // Whether this is implemented in gold.
  bool is_implemented_ : 1;
  // Whether this checks overflow.
  bool checks_overflow_ : 1;
  // Whether this uses a GOT entry.
  bool uses_got_entry_ : 1;
  // Whether this uses a GOT origin.
  bool uses_got_origin_ : 1;
  // Whether this uses a PLT entry.
  bool uses_plt_entry_ : 1;
  // Whether this uses the THUMB bit in symbol address.
  bool uses_thumb_bit_ : 1;
  // Whether this uses the symbol base.
  bool uses_symbol_base_ : 1;
  // Whether this uses an addend.
  bool uses_addend_ : 1;
  // Whether this uses the symbol.
  bool uses_symbol_ : 1;
};

// Arm_reloc_property_table.  This table is used for looking up properties
// of relocation types.  The table entries are initialized using information
// from arm-reloc.def.

class Arm_reloc_property_table
{
 public:
  Arm_reloc_property_table();

  // Return an Arm_reloc_property object for CODE if it is a valid relocation
  // code or NULL otherwise.
  const Arm_reloc_property*
  get_reloc_property(unsigned int code) const
  {
    gold_assert(code < Property_table_size);
    return this->table_[code];
  }

  // Like get_reloc_property but only return non-NULL if relocation code is
  // static and implemented.
  const Arm_reloc_property*
  get_implemented_static_reloc_property(unsigned int code) const
  {
    gold_assert(code < Property_table_size);
    const Arm_reloc_property* arp = this->table_[code];
    return ((arp != NULL
	     && (arp->reloc_type() == Arm_reloc_property::RT_STATIC)
	     && arp->is_implemented())
	    ? arp
	    : NULL);
  }
  
  // Return a string describing the relocation code that is not
  // an implemented static reloc code.
  std::string
  reloc_name_in_error_message(unsigned int code);

 private:
  // Copying is not allowed.
  Arm_reloc_property_table(const Arm_reloc_property_table&);
  Arm_reloc_property_table& operator=(const Arm_reloc_property_table&);

  // The Parse_expression class is used to convert relocation operations in
  // arm-reloc.def into S-expression strings, which are parsed again to
  // build actual expression trees.  We do not build the expression trees
  // directly because the parser for operations in arm-reloc.def is simpler
  // this way.  Conversion from S-expressions to trees is simple.
  class Parse_expression
  {
   public:
    // Construction a Parse_expression with an S-expression string.
    Parse_expression(const std::string& s_expression)
      : s_expression_(s_expression)
    { }

    // Value of this expression as an S-expression string.
    const std::string&
    s_expression() const
    { return this->s_expression_; }

    // We want to overload operators used in relocation operations so
    // that we can execute operations in arm-reloc.def to generate
    // S-expressions directly.
#define DEF_OPERATOR_OVERLOAD(op) \
    Parse_expression \
    operator op (const Parse_expression& e) \
    { \
      return Parse_expression("( " #op " " + this->s_expression_ + " " + \
			      e.s_expression_ + " )"); \
    }

    // Operator appearing in relocation operations in arm-reloc.def.
    DEF_OPERATOR_OVERLOAD(+)
    DEF_OPERATOR_OVERLOAD(-)
    DEF_OPERATOR_OVERLOAD(|)
    
   private:
    // This represented as an S-expression string.
    std::string s_expression_;
  };

#define DEF_RELOC_FUNC(name) \
  static Parse_expression \
  (name)(const Parse_expression& arg) \
  { return Parse_expression("( " #name " " + arg.s_expression() + " )"); }

  // Functions appearing in relocation operations in arm-reloc.def.
  DEF_RELOC_FUNC(B)
  DEF_RELOC_FUNC(DELTA_B)
  DEF_RELOC_FUNC(GOT)
  DEF_RELOC_FUNC(Module)
  DEF_RELOC_FUNC(PLT)

  static const unsigned int Property_table_size = 256;

  // The property table.
  Arm_reloc_property* table_[Property_table_size];
};

} // End namespace gold.

#endif // !defined(GOLD_ARM_RELOC_PROPERTY_H)
