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

// Copyright (C) 2014-2024 Free Software Foundation, Inc.
// Written by Han Shen <[email protected]> and Jing Yu <[email protected]>.

// 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.

#include "gold.h"

#include "aarch64-reloc-property.h"
#include "aarch64.h"

#include "symtab.h"

#include<stdio.h>

namespace gold
{

template<int L, int U>
bool
rvalue_checkup(int64_t x)
{
 // We save the extra_alignment_requirement bits on [31:16] of U.
 // "extra_alignment_requirement" could be 0, 1, 3, 7 and 15.
 unsigned short extra_alignment_requirement = (U & 0xFFFF0000) >> 16;
 // [15:0] of U indicates the upper bound check.
 int64_t u = U & 0x0000FFFF;
 if (u == 0)
   {
     // No requirement to check overflow.
     gold_assert(L == 0);
     return (x & extra_alignment_requirement) == 0;
   }

 // Check both overflow and alignment if needed.
 int64_t low_bound = -(L == 0 ? 0 : ((int64_t)1 << L));
 int64_t up_bound = ((int64_t)1 << u);
 return ((low_bound <= x && x < up_bound)
         && ((x & extra_alignment_requirement) == 0));
}

template<>
bool
rvalue_checkup<0, 0>(int64_t) { return true; }

namespace
{

template<int L, int U>
class Rvalue_bit_select_impl
{
public:
 static uint64_t
 calc(uint64_t x)
 {
   return (x & ((1ULL << (U+1)) - 1)) >> L;
 }
};

template<int L>
class Rvalue_bit_select_impl<L, 63>
{
public:
 static uint64_t
 calc(uint64_t x)
 {
   return x >> L;
 }
};

// By our convention, L=U=0 means that the whole value should be retrieved.
template<>
class Rvalue_bit_select_impl<0, 0>
{
public:
 static uint64_t
 calc(uint64_t x)
 {
   return x;
 }
};

} // End anonymous namespace.

template<int L, int U>
uint64_t
rvalue_bit_select(uint64_t x)
{
 return Rvalue_bit_select_impl<L, U>::calc(x);
}

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)
 : code_(code), name_(name), reloc_type_(rtype), reloc_class_(rclass),
   group_index_(group_index),
   is_implemented_(is_implemented),
   reference_flags_(reference_flags),
   reloc_inst_(reloc_inst),
   rvalue_checkup_func_(rvalue_checkup_func),
   rvalue_bit_select_func_(rvalue_bit_select)
{}

AArch64_reloc_property_table::AArch64_reloc_property_table()
{
 const bool Y(true), N(false);
 for (unsigned int i = 0; i < Property_table_size; ++i)
   table_[i] = NULL;

#define RL_CHECK_ALIGN2   (1  << 16)
#define RL_CHECK_ALIGN4   (3  << 16)
#define RL_CHECK_ALIGN8   (7  << 16)
#define RL_CHECK_ALIGN16  (15 << 16)

#undef ARD
#define ARD(rname, type, class, is_implemented, group_index, LB, UB, BSL, BSH, RFLAGS, inst) \
   do \
     { \
       int tidx = code_to_array_index(elfcpp::R_AARCH64_##rname); \
       AArch64_reloc_property * p = new AArch64_reloc_property( \
         elfcpp::R_AARCH64_##rname, "R_AARCH64_" #rname, \
         AArch64_reloc_property::RT_##type, \
         AArch64_reloc_property::RC_##class, \
         is_implemented, \
         group_index, \
         (RFLAGS), \
         AArch64_reloc_property::INST_##inst,  \
         rvalue_checkup<LB,UB>,    \
         rvalue_bit_select<BSL,BSH>);          \
       table_[tidx] = p; \
     } \
   while (0);
#include"aarch64-reloc.def"
#undef ARD
}

// Return a string describing a relocation code that fails to get a
// relocation property in get_implemented_static_reloc_property().

std::string
AArch64_reloc_property_table::reloc_name_in_error_message(unsigned int code)
{
 int tidx = code_to_array_index(code);
 const AArch64_reloc_property* arp = this->table_[tidx];

 if (arp == NULL)
   {
     char buffer[100];
     sprintf(buffer, _("invalid reloc %u"), code);
     return std::string(buffer);
   }

 // gold only implements static relocation codes.
 AArch64_reloc_property::Reloc_type reloc_type = arp->reloc_type();
 gold_assert(reloc_type == AArch64_reloc_property::RT_STATIC
             || !arp->is_implemented());

 const char* prefix = NULL;
 switch (reloc_type)
   {
   case AArch64_reloc_property::RT_STATIC:
     prefix = arp->is_implemented() ? _("reloc ") : _("unimplemented reloc ");
     break;
   case AArch64_reloc_property::RT_DYNAMIC:
     prefix = _("dynamic reloc ");
     break;
   default:
     gold_unreachable();
   }
 return std::string(prefix) + arp->name();
}

}