// aarch64-reloc-property.h -- AArch64 relocation properties   -*- C++ -*-

// Copyright (C) 2014-2017 Free Software Foundation, Inc.
// Written by Han Shen <shenhan@google.com> and Jing Yu <jingyu@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_AARCH64_RELOC_PROPERTY_H
#define GOLD_AARCH64_RELOC_PROPERTY_H

#include<vector>
#include<string>

#include"aarch64.h"

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

class AArch64_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.
  };

  // Classes of relocation codes.
  enum Reloc_class {
    RC_NONE,		// No relocation class.
    RC_DATA,		// Data relocation.
    RC_AARCH64,		// Static AArch64 relocations
    RC_CFLOW,		// Control flow
    RC_TLS,		// Thread local storage
    RC_DYNAMIC,		// Dynamic relocation
  };

  // Instructions that are associated with relocations.
  enum Reloc_inst {
    INST_DATA = 0,
    INST_MOVW = 1,	// movz, movk, movn
    INST_LD = 2,	// ld literal
    INST_ADR = 3,	// adr
    INST_ADRP = 4,	// adrp
    INST_ADD = 5,	// add
    INST_LDST = 6,	// ld/st
    INST_TBZNZ = 7,	// tbz/tbnz
    INST_CONDB = 8,	// B.cond
    INST_B = 9,		// b  [25:0]
    INST_CALL = 10,	// bl [25:0]
    INST_NUM = 11,	// total number of entries in the table
  };

  // Types of bases of relative addressing relocation codes.
  // enum Relative_address_base {
  //   RAB_NONE,		// Relocation is not relative addressing
  // };

  typedef bool (*rvalue_checkup_func_p)(int64_t);
  typedef uint64_t (*rvalue_bit_select_func)(uint64_t);

  // 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_; }

  // 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_; }

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

  int
  reference_flags() const
  { return this->reference_flags_; }

  // Instruction associated with this relocation.
  Reloc_inst
  reloc_inst() const
  { return this->reloc_inst_; }

  // Check overflow of x
  bool checkup_x_value(int64_t x) const
  { return this->rvalue_checkup_func_(x); }

  // Return portions of x as is defined in aarch64-reloc.def.
  uint64_t select_x_value(uint64_t x) const
  { return this->rvalue_bit_select_func_(x); }

 protected:
  // These are protected.  We only allow AArch64_reloc_property_table to
  // manage AArch64_reloc_property.
  AArch64_reloc_property(unsigned int code, const char* name, Reloc_type rtype,
			 Reloc_class rclass,
			 bool is_implemented,
			 int group_index,
			 int reference_flags,
			 Reloc_inst reloc_inst,
			 rvalue_checkup_func_p rvalue_checkup_func,
			 rvalue_bit_select_func rvalue_bit_select);

  friend class AArch64_reloc_property_table;

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

  // Relocation code.
  const unsigned int code_;
  // Relocation name.
  const 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;
  const int reference_flags_;
  // Instruction associated with relocation.
  Reloc_inst reloc_inst_;
  rvalue_checkup_func_p rvalue_checkup_func_;
  rvalue_bit_select_func rvalue_bit_select_func_;
};

class AArch64_reloc_property_table
{
 public:
  AArch64_reloc_property_table();

  const AArch64_reloc_property*
  get_reloc_property(unsigned int code) const
  {
    unsigned int idx = code_to_array_index(code);
    return this->table_[idx];
  }

  // Like get_reloc_property but only return non-NULL if relocation code is
  // static and implemented.
  const AArch64_reloc_property*
  get_implemented_static_reloc_property(unsigned int code) const
  {
    unsigned int idx = code_to_array_index(code);
    const AArch64_reloc_property* arp = this->table_[idx];
    return ((arp != NULL
	     && (arp->reloc_type() == AArch64_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.
  AArch64_reloc_property_table(const AArch64_reloc_property_table&);
  AArch64_reloc_property_table& operator=(const AArch64_reloc_property_table&);

  // Map aarch64 rtypes into range(0,300) as following
  //   256 ~ 313 -> 0 ~ 57
  //   512 ~ 573 -> 128 ~ 189
  int
  code_to_array_index(unsigned int code) const
  {
    if (code == 0) return 0;
    if (!((code >= elfcpp::R_AARCH64_ABS64 &&
	   code <= elfcpp::R_AARCH64_LD64_GOTPAGE_LO15)
	  || (code >= elfcpp::R_AARCH64_TLSGD_ADR_PREL21 &&
	      code <= elfcpp::R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC)))
      {
	gold_error(_("Invalid/unrecognized reloc reloc %d."), code);
      }
    unsigned int rv = -1;
    if (code & (1 << 9))
      rv = 128 + code - 512;  // 512 - 573
    else if (code & (1 << 8))
      rv = code - 256;  // 256 - 313
    gold_assert(rv <= Property_table_size);
    return rv;
  }

  static const unsigned int Property_table_size = 300;
  AArch64_reloc_property* table_[Property_table_size];
};  // End of class AArch64_reloc_property_table

} // End namespace gold.

#endif // !defined(GOLD_AARCH64_RELOC_PROPERTY_H)
