/* Copyright 2017 - 2022 R. Thomas
 * Copyright 2017 - 2022 Quarkslab
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef LIEF_ELF_BINARY_H_
#define LIEF_ELF_BINARY_H_

#include <vector>
#include <memory>

#include "LIEF/visibility.h"

#include "LIEF/iterators.hpp"

#include "LIEF/Abstract/Binary.hpp"

#include "LIEF/ELF/Header.hpp"

namespace LIEF {
//! Namespace related to the LIEF's ELF module
namespace ELF {
namespace DataHandler {
class Handler;
}

class Builder;
class DynamicEntry;
class ExeLayout;
class GnuHash;
class Layout;
class Note;
class ObjectFileLayout;
class Parser;
class Relocation;
class Section;
class Segment;
class Symbol;
class SymbolVersion;
class SymbolVersionDefinition;
class SymbolVersionRequirement;
class SysvHash;

//! Class which represents an ELF binary
class LIEF_API Binary : public LIEF::Binary {
  friend class Parser;
  friend class Builder;
  friend class ExeLayout;
  friend class Layout;
  friend class ObjectFileLayout;

  public:
  using string_list_t  = std::vector<std::string>;
  using overlay_t      = std::vector<uint8_t>;

  //! Internal container for storing notes
  using notes_t = std::vector<std::unique_ptr<Note>>;

  //! Iterator which outputs Note& object
  using it_notes = ref_iterator<notes_t&, Note*>;

  //! Iterator which outputs const Note& object
  using it_const_notes = const_ref_iterator<const notes_t&, const Note*>;

  //! Internal container for storing SymbolVersionRequirement
  using symbols_version_requirement_t = std::vector<std::unique_ptr<SymbolVersionRequirement>>;

  //! Iterator which outputs SymbolVersionRequirement& object
  using it_symbols_version_requirement = ref_iterator<symbols_version_requirement_t&, SymbolVersionRequirement*>;

  //! Iterator which outputs const SymbolVersionRequirement& object
  using it_const_symbols_version_requirement = const_ref_iterator<const symbols_version_requirement_t&, const SymbolVersionRequirement*>;

  //! Internal container for storing SymbolVersionDefinition
  using symbols_version_definition_t = std::vector<std::unique_ptr<SymbolVersionDefinition>>;

  //! Iterator which outputs SymbolVersionDefinition& object
  using it_symbols_version_definition       = ref_iterator<symbols_version_definition_t&, SymbolVersionDefinition*>;

  //! Iterator which outputs const SymbolVersionDefinition& object
  using it_const_symbols_version_definition = const_ref_iterator<const symbols_version_definition_t&, const SymbolVersionDefinition*>;

  //! Internal container for storing ELF's Segment
  using segments_t = std::vector<std::unique_ptr<Segment>>;

  //! Iterator which outputs Segment& object
  using it_segments = ref_iterator<segments_t&, Segment*>;

  //! Iterator which outputs const Segment& object
  using it_const_segments = const_ref_iterator<const segments_t&, const Segment*>;

  //! Internal container for storing ELF's DynamicEntry
  using dynamic_entries_t = std::vector<std::unique_ptr<DynamicEntry>>;

  //! Iterator which outputs DynamicEntry& object
  using it_dynamic_entries = ref_iterator<dynamic_entries_t&, DynamicEntry*>;

  //! Iterator which outputs const DynamicEntry& object
  using it_const_dynamic_entries = const_ref_iterator<const dynamic_entries_t&, const DynamicEntry*>;

  //! Internal container for storing ELF's SymbolVersion
  using symbols_version_t = std::vector<std::unique_ptr<SymbolVersion>>;

  //! Iterator which outputs SymbolVersion& object
  using it_symbols_version = ref_iterator<symbols_version_t&, SymbolVersion*>;

  //! Iterator which outputs const SymbolVersion& object
  using it_const_symbols_version = const_ref_iterator<const symbols_version_t&, const SymbolVersion*>;

  //! Internal container for storing ELF's Relocation
  using relocations_t = std::vector<std::unique_ptr<Relocation>>;

  //! Iterator which outputs plt/got Relocation& object
  using it_pltgot_relocations = filter_iterator<relocations_t&, Relocation*>;

  //! Iterator which outputs plt/got const Relocation& object
  using it_const_pltgot_relocations = const_filter_iterator<const relocations_t&, const Relocation*>;

  //! Iterator which outputs dynamic Relocation& object (not related to the PLT/GOT mechanism)
  using it_dynamic_relocations = filter_iterator<relocations_t&, Relocation*>;

  //! Iterator which outputs dynamic const Relocation& object (not related to the PLT/GOT mechanism)
  using it_const_dynamic_relocations = const_filter_iterator<const relocations_t&, const Relocation*>;

  //! Iterator which outputs Relocation& object found in object files (.o)
  using it_object_relocations = filter_iterator<relocations_t&, Relocation*>;

  //! Iterator which outputs const Relocation& object found in object files (.o)
  using it_const_object_relocations = const_filter_iterator<const relocations_t&, const Relocation*>;

  //! Iterator which outputs Relocation& object
  using it_relocations = ref_iterator<relocations_t&, Relocation*>;

  //! Iterator which outputs const Relocation& object
  using it_const_relocations = const_ref_iterator<const relocations_t&, const Relocation*>;

  //! Internal container for storing ELF's Symbol
  using symbols_t = std::vector<std::unique_ptr<Symbol>>;

  //! Iterator which outputs the Dynamic Symbol& object
  using it_dynamic_symbols = ref_iterator<symbols_t&, Symbol*>;

  //! Iterator which outputs the Dynamic const Symbol& object
  using it_const_dynamic_symbols = const_ref_iterator<const symbols_t&, const Symbol*>;

  //! Iterator which outputs the static/debug Symbol& object
  using it_static_symbols = ref_iterator<symbols_t&, Symbol*>;

  //! Iterator which outputs the static/debug const Symbol& object
  using it_const_static_symbols = const_ref_iterator<const symbols_t&, const Symbol*>;

  //! Iterator which outputs static and dynamic Symbol& object
  using it_symbols = ref_iterator<std::vector<Symbol*>>;

  //! Iterator which outputs static and dynamic const Symbol& object
  using it_const_symbols = const_ref_iterator<std::vector<Symbol*>>;

  //! Iterator which outputs exported Symbol& object
  using it_exported_symbols = filter_iterator<std::vector<Symbol*>>;

  //! Iterator which outputs exported const Symbol& object
  using it_const_exported_symbols = const_filter_iterator<std::vector<Symbol*>>;

  //! Iterator which outputs imported Symbol& object
  using it_imported_symbols = filter_iterator<std::vector<Symbol*>>;

  //! Iterator which outputs imported const Symbol& object
  using it_const_imported_symbols = const_filter_iterator<std::vector<Symbol*>>;

  //! Internal container for storing ELF's Section
  using sections_t = std::vector<std::unique_ptr<Section>>;

  //! Iterator which outputs Section& object
  using it_sections = ref_iterator<sections_t&, Section*>;

  //! Iterator which outputs const Section& object
  using it_const_sections = const_ref_iterator<const sections_t&, const Section*>;

  public:
  Binary& operator=(const Binary& ) = delete;
  Binary(const Binary& copy) = delete;

  //! Return binary's class (ELF32 or ELF64)
  ELF_CLASS type() const;

  //! Return @link ELF::Header Elf header @endlink
  Header&       header();
  const Header& header() const;

  //! Return the last offset used in binary
  //! according to sections table
  uint64_t last_offset_section() const;

  //! Return the last offset used in binary
  //! according to segments table
  uint64_t last_offset_segment() const;

  //! Return the next virtual address available
  uint64_t next_virtual_address() const;

  //! Return an iterator over the binary's sections
  it_sections       sections();
  it_const_sections sections() const;

  //! Return the binary's entrypoint
  uint64_t entrypoint() const override;

  //! Return binary's segments
  it_segments       segments();
  it_const_segments segments() const;

  //! Return binary's dynamic entries
  it_dynamic_entries       dynamic_entries();
  it_const_dynamic_entries dynamic_entries() const;

  //! Add the given dynamic entry and return the new entry
  DynamicEntry& add(const DynamicEntry& entry);

  //! Add the given note and return the created entry
  Note& add(const Note& note);

  //! Remove the given dynamic entry
  void remove(const DynamicEntry& entry);

  //! Remove **all** dynamic entries with the given tag
  void remove(DYNAMIC_TAGS tag);

  //! Remove the given section. The ``clear`` parameter
  //! can be used to zeroize the original content beforehand
  //!
  //! @param[in] section The section to remove
  //! @param[in] clear   Whether zeroize the original content
  void remove(const Section& section, bool clear = false);

  //! Remove the given note
  void remove(const Note& note);

  //! Remove **all** notes with the given type
  void remove(NOTE_TYPES type);

  //! Remove the given segment
  void remove(const Segment& seg);

  //! Return an iterator over the binary's dynamic symbols
  //! The dynamic symbols are those located in the ``.dynsym`` section
  it_dynamic_symbols       dynamic_symbols();
  it_const_dynamic_symbols dynamic_symbols() const;

  //! Return symbols which are exported by the binary
  it_exported_symbols       exported_symbols();
  it_const_exported_symbols exported_symbols() const;

  //! Return symbols which are imported by the binary
  it_imported_symbols       imported_symbols();
  it_const_imported_symbols imported_symbols() const;

  //! Return statics symbols.
  it_static_symbols       static_symbols();
  it_const_static_symbols static_symbols() const;

  //! Return the symbol versions
  it_symbols_version       symbols_version();
  it_const_symbols_version symbols_version() const;

  //! Return symbols version definition
  it_symbols_version_definition       symbols_version_definition();
  it_const_symbols_version_definition symbols_version_definition() const;

  //! Return Symbol version requirement
  it_symbols_version_requirement       symbols_version_requirement();
  it_const_symbols_version_requirement symbols_version_requirement() const;

  //! Return dynamic relocations
  it_dynamic_relocations       dynamic_relocations();
  it_const_dynamic_relocations dynamic_relocations() const;

  //! Add a new *dynamic* relocation.
  //!
  //! We consider a dynamic relocation as a relocation which is not plt-related
  //!
  //! See: add_pltgot_relocation
  Relocation& add_dynamic_relocation(const Relocation& relocation);

  //! Add a .plt.got relocation. This kind of relocation is usually
  //! associated with a PLT stub that aims at resolving the underlying symbol
  //!
  //! See also: add_dynamic_relocation
  Relocation& add_pltgot_relocation(const Relocation& relocation);

  //! Add relocation for object file (.o)
  //!
  //! The first parameter is the section to add while the second parameter
  //! is the LIEF::ELF::Section associated with the relocation.
  //!
  //! If there is an error, this function returns a ``nullptr``. Otherwise, it returns
  //! the relocation added.
  Relocation* add_object_relocation(const Relocation& relocation, const Section& section);

  //! Return `plt.got` relocations
  it_pltgot_relocations       pltgot_relocations();
  it_const_pltgot_relocations pltgot_relocations() const;

  //! Return relocations used in an object file (``*.o``)
  it_object_relocations       object_relocations();
  it_const_object_relocations object_relocations() const;

  //! Return **all** relocations present in the binary
  it_relocations       relocations();
  it_const_relocations relocations() const;

  //! Return relocation associated with the given address.
  //! It returns a ``nullptr`` if it is not found
  const Relocation* get_relocation(uint64_t address) const;
  Relocation*       get_relocation(uint64_t address);

  //! Return relocation associated with the given Symbol
  //! It returns a ``nullptr`` if it is not found
  const Relocation* get_relocation(const Symbol& symbol) const;
  Relocation*       get_relocation(const Symbol& symbol);

  //! Return relocation associated with the given Symbol name
  //! It returns a ``nullptr`` if it is not found
  const Relocation* get_relocation(const std::string& symbol_name) const;
  Relocation*       get_relocation(const std::string& symbol_name);

  //! ``true`` if GNU hash is used
  //!
  //! @see gnu_hash and use_sysv_hash
  bool use_gnu_hash() const;

  //! Return the GnuHash object in **readonly**
  //! If the ELF binary does not use the GNU hash table, return a nullptr
  const GnuHash* gnu_hash() const;

  //! ``true`` if SYSV hash is used
  //!
  //! @see sysv_hash and use_gnu_hash
  bool use_sysv_hash() const;

  //! Return the SysvHash object as a **read-only** object
  //! If the ELF binary does not use the legacy sysv hash table, return a nullptr
  const SysvHash* sysv_hash() const;

  //! Check if a section with the given name exists in the binary
  bool has_section(const std::string& name) const;

  //! Check if a section that handles the given offset exists
  bool has_section_with_offset(uint64_t offset) const;

  //! Check if a section that handles the given virtual address exists
  bool has_section_with_va(uint64_t va) const;

  //! Return Section with the given `name`. If the section can't be
  //! found, it returns a nullptr
  Section*       get_section(const std::string& name);
  const Section* get_section(const std::string& name) const;

  //! Return the `.text` section. If the section
  //! can't be found, it returns a nullptr
  Section* text_section();

  //! Return the `.dynamic` section. If the section
  //! can't be found, it returns a nullptr
  Section* dynamic_section();

  //! Return the hash section. If the section
  //! can't be found, it returns a nullptr
  Section* hash_section();

  //! Return section which holds static symbols. If the section
  //! can't be found, it returns a nullptr
  Section* static_symbols_section();

  //! Return program image base. For instance ``0x40000``
  //!
  //! To compute the image base, we look for the PT_PHDR segment header (phdr),
  //! and we return ``phdr->p_vaddr - phdr->p_offset``
  uint64_t imagebase() const override;

  //! Return the size of the mapped binary
  uint64_t virtual_size() const;

  //! Check if the binary uses a loader (also named linker or interpreter)
  //! @see interpreter
  bool has_interpreter() const;

  //! Return the ELF interpreter if any. (e.g. `/lib64/ld-linux-x86-64.so.2`)
  //! If the binary does not have an interpreter, it returns an empty string
  //!
  //! @see has_interpreter
  const std::string& interpreter() const;

  //! Change the interpreter
  void interpreter(const std::string& interpreter);

  //! Return an iterator on both static and dynamic symbols
  it_symbols       symbols();
  it_const_symbols symbols() const;

  //! Export the given symbol and create it if it doesn't exist
  Symbol& export_symbol(const Symbol& symbol);

  //! Export the symbol with the given name and create it if it doesn't exist
  Symbol& export_symbol(const std::string& symbol_name, uint64_t value = 0);

  //! Check if the symbol with the given ``name`` exists in the dynamic symbols table
  bool has_dynamic_symbol(const std::string& name) const;

  //! Get the dynamic symbol from the given name.
  //! Return a nullptr if it can't be found
  const Symbol* get_dynamic_symbol(const std::string& name) const;

  Symbol* get_dynamic_symbol(const std::string& name);

  //! Check if the symbol with the given ``name`` exists in the static symbol table
  bool has_static_symbol(const std::string& name) const;

  //! Get the static symbol from the given name
  //! Return a nullptr if it can't be found
  const Symbol* get_static_symbol(const std::string& name) const;

  Symbol* get_static_symbol(const std::string& name);

  //! Return list of the strings used by the ELF binary.
  //!
  //! Basically, this function looks for string in the ``.roadata`` section
  string_list_t strings(size_t min_size = 5) const;

  //! Remove symbols with the given name in both:
  //!   * dynamic symbols
  //!   * static symbols
  //!
  //! @see remove_static_symbol, remove_dynamic_symbol
  void remove_symbol(const std::string& name);

  //! Remove static symbols with the given name
  void remove_static_symbol(const std::string& name);
  void remove_static_symbol(Symbol* symbol);

  //! Remove dynamic symbols with the given name
  void remove_dynamic_symbol(const std::string& name);

  //! Remove the given symbol from the dynamic symbols table.
  //!
  //! As a side effect, it will remove any ELF::Relocation
  //! that refers to this symbol and the SymbolVersion (if any)
  //! associated with this symbol
  void remove_dynamic_symbol(Symbol* symbol);

  //! Return the address of the given function name
  uint64_t get_function_address(const std::string& func_name) const override;

  //! Return the address of the given function name
  //
  //! @param[in] func_name    The function's name target
  //! @param[in] demangled    Use the demangled name
  uint64_t get_function_address(const std::string& func_name, bool demangled) const;

  //! Add a new section in the binary
  //!
  //! @param[in] section    The section object to insert
  //! @param[in] loaded     Boolean value to indicate that section's data must be loaded
  //!                       by a PT_LOAD segment
  //!
  //! @return The section added. The `size` and the `virtual address` might change.
  Section& add(const Section& section, bool loaded = true);

  Section& extend(const Section& section, uint64_t size);

  //! Add a static symbol
  Symbol& add_static_symbol(const Symbol& symbol);

  //! Add a dynamic symbol with the associated SymbolVersion
  Symbol& add_dynamic_symbol(const Symbol& symbol, const SymbolVersion* version = nullptr);

  //! Create a symbol for the function at the given address and export it
  Symbol& add_exported_function(uint64_t address, const std::string& name = "");

  //! Add a library as dependency
  DynamicEntryLibrary& add_library(const std::string& library_name);

  //! Remove the given library from the dependencies
  void remove_library(const std::string& library_name);

  //! Get the library object (DynamicEntryLibrary) from the given name
  //! If the library can't be found, it returns a nullptr.
  DynamicEntryLibrary* get_library(const std::string& library_name);

  //! Get the library object (DynamicEntryLibrary) from the given name
  //! If the library can't be found, it returns a nullptr.
  const DynamicEntryLibrary* get_library(const std::string& library_name) const;

  //! Check if the given library name exists in the current binary
  bool has_library(const std::string& name) const;

  //! Add a new segment in the binary
  //!
  //! The segment is inserted at the end
  //!
  //! @return The segment added. `Virtual address` and `File Offset` might change.
  Segment& add(const Segment& segment, uint64_t base = 0);

  //! Replace the segment given in 2nd parameter with the segment given in the first one and return the updated segment.
  //!
  //! @warning The ``original_segment`` is no longer valid after this function
  Segment& replace(const Segment& new_segment, const Segment& original_segment, uint64_t base = 0);

  Segment& extend(const Segment& segment, uint64_t size);


  //! Patch the content at virtual address @p address with @p patch_value
  //!
  //! @param[in] address      Address to patch
  //! @param[in] patch_value  Patch to apply
  //! @param[in] addr_type    Specify if the address should be used as an absolute virtual address or an RVA
  void patch_address(uint64_t address, const std::vector<uint8_t>& patch_value,
                     LIEF::Binary::VA_TYPES addr_type = LIEF::Binary::VA_TYPES::AUTO) override;


  //! Patch the address with the given value
  //!
  //! @param[in] address        Address to patch
  //! @param[in] patch_value    Patch to apply
  //! @param[in] size           Size of the value in **bytes** (1, 2, ... 8)
  //! @param[in] addr_type      Specify if the address should be used as an absolute virtual address or an RVA
  void patch_address(uint64_t address, uint64_t patch_value,
                     size_t size = sizeof(uint64_t),
                     LIEF::Binary::VA_TYPES addr_type = LIEF::Binary::VA_TYPES::AUTO) override;

  //! Patch the imported symbol with the ``address``
  //!
  //! @param[in] symbol Imported symbol to patch
  //! @param[in] address New address
  void patch_pltgot(const Symbol& symbol, uint64_t address);


  //! Patch the imported symbol's name with the ``address``
  //!
  //! @param[in] symbol_name Imported symbol's name to patch
  //! @param[in] address New address
  void patch_pltgot(const std::string& symbol_name, uint64_t address);

  //! Strip the binary by removing static symbols
  void strip();

  //! Remove a binary's section.
  //!
  //! @param[in] name   The name of the section to remove
  //! @param[in] clear  Whether zeroize the original content
  void remove_section(const std::string& name, bool clear = false) override;

  //! Reconstruct the binary object and write it in `filename`
  //!
  //! @param filename Path for the written ELF binary
  void write(const std::string& filename) override;

  //! Reconstruct the binary object and return its content as a byte vector
  std::vector<uint8_t> raw();

  //! Convert a virtual address to a file offset
  uint64_t virtual_address_to_offset(uint64_t virtual_address) const;

  //! Convert the given offset into a virtual address.
  //!
  //! @param[in] offset   The offset to convert.
  //! @param[in] slide    If not 0, it will replace the default base address (if any)
  uint64_t offset_to_virtual_address(uint64_t offset, uint64_t slide = 0) const override;

  //! Check if the binary has been compiled with `-fpie -pie` flags
  //!
  //! To do so we check if there is a `PT_INTERP` segment and if
  //! the binary type is `ET_DYN` (Shared object)
  bool is_pie() const override;

  //! Check if the binary uses the ``NX`` protection (Non executable stack)
  bool has_nx() const override;

  //! Return the ELF::Section from the given @p offset. Return a nullptr
  //! if a section can't be found
  //!
  //! If @p skip_nobits is set (which is the case by default), this function won't
  //! consider section for which the type is ``SHT_NOBITS`` (like ``.bss, .tbss, ...``)
  const Section* section_from_offset(uint64_t offset, bool skip_nobits = true) const;
  Section*       section_from_offset(uint64_t offset, bool skip_nobits = true);

  //! Return the ELF::Section from the given @p address. Return a nullptr
  //! if a section can't be found.
  //!
  //! If @p skip_nobits is set (which is the case by default), this function won't
  //! consider section for which type is ``SHT_NOBITS`` (like ``.bss, .tbss, ...``)
  const Section* section_from_virtual_address(uint64_t address, bool skip_nobits = true) const;
  Section*       section_from_virtual_address(uint64_t address, bool skip_nobits = true);

  //! Return the ELF::Segment from the given @p address. Return a nullptr
  //! if a segment can't be found.
  const Segment* segment_from_virtual_address(uint64_t address) const;
  Segment*       segment_from_virtual_address(uint64_t address);

  //! Return the ELF::Segment from the @p offset. Return a nullptr
  //! if a segment can't be found.
  const Segment* segment_from_offset(uint64_t offset) const;
  Segment*       segment_from_offset(uint64_t offset);

  //! Return the **first** ELF::DynamicEntry associated with the given tag
  //! If the tag can't be found, it returns a nullptr
  const DynamicEntry* get(DYNAMIC_TAGS tag) const;
  DynamicEntry*       get(DYNAMIC_TAGS tag);

  //! Return the **first** ELF::Segment associated with the given type.
  //! If a segment can't be found, it returns a nullptr.
  const Segment* get(SEGMENT_TYPES type) const;
  Segment*       get(SEGMENT_TYPES type);

  //! Return the **first** ELF::Note associated with the given type
  //! If a note can't be found, it returns a nullptr.
  const Note* get(NOTE_TYPES type) const;
  Note*       get(NOTE_TYPES type);

  //! Return the **first** ELF::Section associated with the given type
  //! If a section can't be found, it returns a nullptr.
  const Section* get(ELF_SECTION_TYPES type) const;
  Section*       get(ELF_SECTION_TYPES type);

  //! Check if an ELF::DynamicEntry associated with the given tag exists.
  bool has(DYNAMIC_TAGS tag) const;

  //! Check if ELF::Segment associated with the given type exists.
  bool has(SEGMENT_TYPES type) const;

  //! Check if a ELF::Note associated with the given type exists.
  bool has(NOTE_TYPES type) const;

  //! Check if a ELF::Section associated with the given type exists.
  bool has(ELF_SECTION_TYPES type) const;

  //! Return the content located at virtual address
  std::vector<uint8_t> get_content_from_virtual_address(uint64_t virtual_address, uint64_t size,
                            LIEF::Binary::VA_TYPES addr_type = LIEF::Binary::VA_TYPES::AUTO) const override;

  //! Method associated with the visitor pattern.
  void accept(LIEF::Visitor& visitor) const override;

  //! Apply the given permutation on the dynamic symbols table
  void permute_dynamic_symbols(const std::vector<size_t>& permutation);

  //! List of binary constructors (typically, the functions located in the ``.init_array``)
  LIEF::Binary::functions_t ctor_functions() const override;

  //! List of the binary destructors (typically, the functions located in the ``.fini_array``)
  LIEF::Binary::functions_t dtor_functions() const;

  //! List of the functions found the in the binary.
  LIEF::Binary::functions_t functions() const;

  //! ``true`` if the binary embeds notes
  bool has_notes() const;

  //! Return an iterator over the ELF's LIEF::ELF::Note
  //!
  //! @see has_note
  it_const_notes notes() const;

  it_notes notes();

  //! Return the last offset used by the ELF binary according to both: the sections table
  //! and the segments table
  uint64_t eof_offset() const;

  //! True if data are present at the end of the binary
  bool has_overlay() const;

  //! Overlay data (if any)
  const overlay_t& overlay() const;

  //! Function to set the overlay
  void overlay(overlay_t overlay);

  size_t hash(const std::string& name);

  ~Binary() override;

  std::ostream& print(std::ostream& os) const override;

  bool operator==(const Binary& rhs) const;
  bool operator!=(const Binary& rhs) const;


  Binary& operator+=(const DynamicEntry& entry);
  Binary& operator+=(const Section& section);
  Binary& operator+=(const Segment& segment);
  Binary& operator+=(const Note& note);

  Binary& operator-=(const DynamicEntry& entry);
  Binary& operator-=(DYNAMIC_TAGS tag);

  Binary& operator-=(const Note& note);
  Binary& operator-=(NOTE_TYPES type);

  Segment*       operator[](SEGMENT_TYPES type);
  const Segment* operator[](SEGMENT_TYPES type) const;

  DynamicEntry*       operator[](DYNAMIC_TAGS tag);
  const DynamicEntry* operator[](DYNAMIC_TAGS tag) const;

  Note*       operator[](NOTE_TYPES type);
  const Note* operator[](NOTE_TYPES type) const;

  Section*       operator[](ELF_SECTION_TYPES type);
  const Section* operator[](ELF_SECTION_TYPES type) const;

  protected:
  struct phdr_relocation_info_t {
    uint64_t new_offset = 0;
    size_t nb_segments = 0;
    inline void clear() {
      new_offset = 0;
      nb_segments = 0;
    }
  };
  Binary();

  //! Return an abstraction of binary's section: LIEF::Section
  LIEF::Binary::sections_t get_abstract_sections() override;

  LIEF::Header get_abstract_header() const override;

  LIEF::Binary::functions_t get_abstract_exported_functions() const override;
  LIEF::Binary::functions_t get_abstract_imported_functions() const override;
  std::vector<std::string> get_abstract_imported_libraries() const override;
  LIEF::Binary::symbols_t     get_abstract_symbols() override;
  LIEF::Binary::relocations_t get_abstract_relocations() override;

  template<ELF::ARCH ARCH>
  void patch_relocations(uint64_t from, uint64_t shift);

  template<class T>
  void patch_addend(Relocation& relocatio, uint64_t from, uint64_t shift);

  void shift_sections(uint64_t from, uint64_t shift);
  void shift_segments(uint64_t from, uint64_t shift);
  void shift_dynamic_entries(uint64_t from, uint64_t shift);
  void shift_symbols(uint64_t from, uint64_t shift);
  void shift_relocations(uint64_t from, uint64_t shift);

  template<class ELF_T>
  void fix_got_entries(uint64_t from, uint64_t shift);

  LIEF::Binary::functions_t eh_frame_functions() const;
  LIEF::Binary::functions_t armexid_functions() const;

  template<E_TYPE OBJECT_TYPE, bool note = false>
  Segment& add_segment(const Segment& segment, uint64_t base);

  uint64_t relocate_phdr_table();
  uint64_t relocate_phdr_table_pie();
  uint64_t relocate_phdr_table_v1();
  uint64_t relocate_phdr_table_v2();

  template<SEGMENT_TYPES PT>
  Segment& extend_segment(const Segment& segment, uint64_t size);

  template<bool LOADED>
  Section& add_section(const Section& section);
  std::vector<Symbol*> static_dyn_symbols() const;

  std::string shstrtab_name() const;

  LIEF::Binary::functions_t tor_functions(DYNAMIC_TAGS tag) const;

  ELF_CLASS type_ = ELF_CLASS::ELFCLASSNONE;
  Header header_;
  sections_t sections_;
  segments_t segments_;
  dynamic_entries_t dynamic_entries_;
  symbols_t dynamic_symbols_;
  symbols_t static_symbols_;
  relocations_t relocations_;
  symbols_version_t symbol_version_table_;
  symbols_version_requirement_t symbol_version_requirements_;
  symbols_version_definition_t  symbol_version_definition_;
  notes_t notes_;
  std::unique_ptr<GnuHash> gnu_hash_;
  std::unique_ptr<SysvHash> sysv_hash_;
  std::unique_ptr<DataHandler::Handler> datahandler_;
  phdr_relocation_info_t phdr_reloc_info_;

  std::string interpreter_;
  overlay_t overlay_;
};

}
}
#endif
