//===- Relocation.h ----------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_FRAGMENT_RELOCATION_H
#define MCLD_FRAGMENT_RELOCATION_H
#ifdef ENABLE_UNITTEST
#include <gtest.h>
#endif
#include <mcld/Config/Config.h>
#include <mcld/Fragment/FragmentRef.h>
#include <mcld/Support/GCFactoryListTraits.h>

#include <llvm/ADT/ilist_node.h>
#include <llvm/Support/DataTypes.h>

namespace mcld {

class ResolveInfo;
class Relocator;
class LinkerConfig;

class Relocation : public llvm::ilist_node<Relocation>
{
friend class RelocationFactory;
friend class GCFactoryListTraits<Relocation>;
friend class Chunk<Relocation, MCLD_RELOCATIONS_PER_INPUT>;

public:
  typedef uint64_t Address; // FIXME: use SizeTrait<T>::Address instead
  typedef uint64_t DWord;   // FIXME: use SizeTrait<T>::Word instead
  typedef int64_t  SWord;   // FIXME: use SizeTrait<T>::SWord instead
  typedef uint8_t  Type;
  typedef uint32_t Size;

private:
  Relocation();

  Relocation(Type pType,
             FragmentRef* pTargetRef,
             Address pAddend,
             DWord pTargetData);

  ~Relocation();

public:
  /// Initialize - set up the relocation factory
  static void SetUp(const LinkerConfig& pConfig);

  /// Clear - Clean up the relocation factory
  static void Clear();

  /// Create - produce an empty relocation entry
  static Relocation* Create();

  /// Create - produce a relocation entry
  /// @param pType    [in] the type of the relocation entry
  /// @param pFragRef [in] the place to apply the relocation
  /// @param pAddend  [in] the addend of the relocation entry
  static Relocation* Create(Type pType, FragmentRef& pFragRef,
                            Address pAddend = 0);

  /// Destroy - destroy a relocation entry
  static void Destroy(Relocation*& pRelocation);

  /// type - relocation type
  Type type() const
  { return m_Type; }

  /// symValue - S value - the symbol address
  Address symValue() const;

  /// addend - A value
  Address addend() const
  { return m_Addend; }

  /// place - P value - address of the place being relocated
  Address place() const;

  /// size - the size of the relocation in bit
  Size size(Relocator& pRelocator) const;

  /// symbol info - binding, type
  const ResolveInfo* symInfo() const { return m_pSymInfo; }
  ResolveInfo*       symInfo()       { return m_pSymInfo; }

  /// target - the target data to relocate
  const DWord& target() const { return m_TargetData; }
  DWord&       target()       { return m_TargetData; }

  /// targetRef - the reference of the target data
  const FragmentRef& targetRef() const { return m_TargetAddress; }
  FragmentRef&       targetRef()       { return m_TargetAddress; }

  void apply(Relocator& pRelocator);

  /// updateAddend - A relocation with a section symbol must update addend
  /// before reading its value.
  void updateAddend();

  /// ----- modifiers ----- ///
  void setType(Type pType);

  void setAddend(Address pAddend);

  void setSymInfo(ResolveInfo* pSym);

private:
  /// m_Type - the type of the relocation entries
  Type m_Type;

  /// m_TargetData - target data of the place being relocated
  DWord m_TargetData;

  /// m_pSymInfo - resolved symbol info of relocation target symbol
  ResolveInfo* m_pSymInfo;

  /// m_TargetAddress - FragmentRef of the place being relocated
  FragmentRef m_TargetAddress;

  /// m_Addend - the addend
  Address m_Addend;
};

} // namespace of mcld

#endif