//===- lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h ----------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef LLD_READER_WRITER_ELF_AARCH64_AARCH64_TARGET_HANDLER_H
#define LLD_READER_WRITER_ELF_AARCH64_AARCH64_TARGET_HANDLER_H

#include "AArch64RelocationHandler.h"
#include "ELFReader.h"
#include "TargetLayout.h"
#include "lld/Core/Simple.h"

namespace lld {
namespace elf {

class AArch64LinkingContext;
class AArch64GOTSection;

class AArch64TargetLayout final : public TargetLayout<ELF64LE> {
  typedef llvm::object::Elf_Shdr_Impl<ELF64LE> Elf_Shdr;

public:
  AArch64TargetLayout(ELFLinkingContext &ctx);

  AtomSection<ELF64LE> *
  createSection(StringRef name, int32_t type,
                DefinedAtom::ContentPermissions permissions,
                TargetLayout<ELF64LE>::SectionOrder order) override;

  const std::vector<AArch64GOTSection *> &getGOTSections() const {
    return _gotSections;
  }

  uint64_t getTPOffset() {
    std::call_once(_tpOffOnce, [this]() {
      for (const auto &phdr : *_programHeader) {
        if (phdr->p_type == llvm::ELF::PT_TLS) {
          _tpOff = llvm::RoundUpToAlignment(TCB_SIZE, phdr->p_align);
          break;
        }
      }
      assert(_tpOff != 0 && "TLS segment not found");
    });
    return _tpOff;
  }

private:
  enum {
    TCB_SIZE = 16,
  };

private:
  std::vector<AArch64GOTSection *> _gotSections;
  uint64_t _tpOff = 0;
  std::once_flag _tpOffOnce;
};

class AArch64TargetHandler final : public TargetHandler {
public:
  AArch64TargetHandler(AArch64LinkingContext &ctx);

  const TargetRelocationHandler &getRelocationHandler() const override {
    return *_relocationHandler;
  }

  std::unique_ptr<Reader> getObjReader() override {
    return llvm::make_unique<ELFReader<ELFFile<ELF64LE>>>(_ctx);
  }

  std::unique_ptr<Reader> getDSOReader() override {
    return llvm::make_unique<ELFReader<DynamicFile<ELF64LE>>>(_ctx);
  }

  std::unique_ptr<Writer> getWriter() override;

private:
  AArch64LinkingContext &_ctx;
  std::unique_ptr<AArch64TargetLayout> _targetLayout;
  std::unique_ptr<AArch64TargetRelocationHandler> _relocationHandler;
};

} // end namespace elf
} // end namespace lld

#endif
