current commit
260929.11 · deps: update lief to 1.0.0
/* Copyright 2017 - 2026 R. Thomas
 * Copyright 2017 - 2026 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.
 */
#include <memory>
#include <unordered_set>
#include "logging.hpp"

#include "LIEF/BinaryStream/VectorStream.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"

#include "LIEF/ELF/hash.hpp"
#include "LIEF/ELF/Parser.hpp"
#include "LIEF/ELF/DynamicEntryFlags.hpp"
#include "LIEF/ELF/Relocation.hpp"
#include "LIEF/ELF/Segment.hpp"
#include "LIEF/ELF/Section.hpp"
#include "LIEF/ELF/GnuHash.hpp"
#include "LIEF/ELF/DynamicEntryLibrary.hpp"
#include "LIEF/ELF/DynamicEntryArray.hpp"
#include "LIEF/ELF/DynamicSharedObject.hpp"
#include "LIEF/ELF/DynamicEntryAuxiliary.hpp"
#include "LIEF/ELF/DynamicEntryFilter.hpp"
#include "LIEF/ELF/DynamicEntryRunPath.hpp"
#include "LIEF/ELF/DynamicEntryRpath.hpp"
#include "LIEF/ELF/SymbolVersionRequirement.hpp"
#include "LIEF/ELF/SymbolVersionDefinition.hpp"
#include "LIEF/ELF/SymbolVersionAuxRequirement.hpp"
#include "LIEF/ELF/SymbolVersionAux.hpp"
#include "LIEF/ELF/Symbol.hpp"
#include "LIEF/ELF/SymbolVersion.hpp"
#include "LIEF/ELF/Binary.hpp"

#include "ELF/Structures.hpp"
#include "ELF/DataHandler/Handler.hpp"
#include "ELF/SizingInfo.hpp"

#include "Object.tcc"


namespace LIEF::ELF {
template<typename ELF_T>
ok_error_t Parser::parse_binary() {

  LIEF_DEBUG("Start parsing");
  // Parse header
  // ============
  auto res = parse_header<ELF_T>();
  if (!res) {
    LIEF_WARN("ELF Header parsed with errors");
  }

  // Parse Sections
  // ==============
  if (binary_->header_.section_headers_offset() > 0) {
    parse_sections<ELF_T>();
  } else {
    if (binary_->header().file_type() != Header::FILE_TYPE::CORE) {
      LIEF_WARN("The current binary doesn't have a section header");
    }
  }

  // Parse segments
  // ==============
  if (binary_->header_.program_headers_offset() > 0) {
    parse_segments<ELF_T>();
  } else {
    if (binary_->header().file_type() != Header::FILE_TYPE::REL) {
      LIEF_WARN("Binary doesn't have a program header");
    }
  }

  if (Segment* seg_dyn = binary_->get(Segment::TYPE::DYNAMIC)) {
    if (!parse_dyn_table<ELF_T>(*seg_dyn)) {
      LIEF_WARN("PT_DYNAMIC parsing failed with error");
    }
  }

  process_dynamic_table<ELF_T>();

  if (const Section* sec_symbtab = binary_->get(Section::TYPE::SYMTAB)) {
    if (sec_symbtab->link() == 0 ||
        sec_symbtab->link() >= binary_->sections_.size())
    {
      LIEF_WARN("Invalid section link");
    } else {
      if (config_.parse_symtab_symbols) {
        // We should have:
        // nb_entries == section->information())
        // but lots of compilers do not respect this rule
        parse_symtab_symbols<ELF_T>(*sec_symbtab,
                                    *binary_->sections_[sec_symbtab->link()]);
      }
    }
  }


  // Parse Symbol hash
  // ==================
  if (DynamicEntry* dt_hash = binary_->get(DynamicEntry::TAG::HASH)) {
    if (auto res = binary_->virtual_address_to_offset(dt_hash->value())) {
      parse_symbol_sysv_hash(*res);
    } else {
      LIEF_WARN("Can't convert DT_HASH.virtual_address into an offset ({:#x})",
                dt_hash->value());
    }
  }


  if (DynamicEntry* dt = binary_->get(DynamicEntry::TAG::GNU_HASH)) {
    if (Segment* seg = binary_->segment_from_virtual_address(dt->value())) {
      const auto dynsymcount = (uint64_t)(binary_->dynamic_symbols_.size());
      std::unique_ptr<SpanStream> strm = seg->stream();
      const uint64_t addr = dt->value();
      if (strm != nullptr) {
        strm->set_endian_swap(stream_->should_swap());
        assert(addr >= seg->virtual_address());
        uint64_t offset = addr - seg->virtual_address();
        strm->setpos(offset);
        binary_->gnu_hash_ = GnuHash::parse<ELF_T>(*strm, dynsymcount);
        if (binary_->gnu_hash_ != nullptr) {
          binary_->sizing_info_->gnu_hash = binary_->gnu_hash_->original_size();
        }
      }
    }
  }

  if (config_.parse_notes) {
    // Parse Note segment
    // ==================
    for (const Segment& segment : binary_->segments()) {
      if (segment.type() != Segment::TYPE::NOTE) {
        continue;
      }
      parse_notes(segment.file_offset(), segment.physical_size());
    }

    // Parse Note Sections
    // ===================
    for (const Section& section : binary_->sections()) {
      if (section.type() != Section::TYPE::NOTE) {
        continue;
      }
      LIEF_DEBUG("Notes from section: {}", section.name());
      parse_notes(section.offset(), section.size());
    }
  }

  // Try to parse using sections
  // If we don't have any relocations, we parse all relocation sections
  // otherwise, only the non-allocated sections to avoid parsing dynamic
  // relocations (or plt relocations) twice.
  if (config_.parse_relocations) {
    bool skip_allocated_sections = !binary_->relocations_.empty();
    for (const Section& section : binary_->sections()) {
      if (skip_allocated_sections && section.has(Section::FLAGS::ALLOC)) {
        continue;
      }
      if (section.type() == Section::TYPE::REL) {
        parse_section_relocations<ELF_T, typename ELF_T::Elf_Rel>(section);
      } else if (section.type() == Section::TYPE::RELA) {
        parse_section_relocations<ELF_T, typename ELF_T::Elf_Rela>(section);
      }
    }
  }
  if (config_.parse_symbol_versions) {
    link_symbol_version();
  }

  if (config_.parse_overlay) {
    parse_overlay();
  }
  return ok();
}


template<class ELF_T>
ok_error_t Parser::parse_dyn_table(Segment& pt_dyn) {
  // Parse the dynamic table. To process this table, we can either process
  // the content of the PT_DYNAMIC segment or process the content of the PT_LOAD
  // segment that wraps the dynamic table. The second approach should be
  // preferred since it uses a more accurate representation.
  // (c.f. samples `issue_dynamic_table.elf` provided by @lebr0nli)
  using Elf_Off = typename ELF_T::Elf_Off;
  std::vector<Segment*> segments;

  // Find the PT_LOAD segment that wraps the PT_DYNAMIC table.
  // As demonstrated in the library: ELF32_x86_library_libshellx.so
  // we need to consider overlapping segments and take the "latest" one since
  // this is what the loader would do.
  for (const std::unique_ptr<Segment>& segment : binary_->segments_) {
    if (!segment->is_load()) {
      continue;
    }
    const uint64_t dyn_start = pt_dyn.virtual_address();
    const uint64_t dyn_end = dyn_start + pt_dyn.virtual_size();
    const uint64_t load_start = segment->virtual_address();
    const uint64_t load_end = load_start + segment->virtual_size();
    if (!(load_start <= dyn_start && dyn_start < load_end)) {
      continue;
    }

    if (!(load_start < dyn_end && dyn_end <= load_end)) {
      continue;
    }
    segments.push_back(segment.get());
  }

  binary_->sizing_info_->dynamic = pt_dyn.physical_size();

  // Usually #segments is 1 but we might have > 1 for overlapping segments
  LIEF_DEBUG("Nb segments: {}", segments.size());

  if (segments.empty()) {
    // No PT_LOAD segment wrapping up the PT_DYNAMIC table
    const Elf_Off offset = pt_dyn.file_offset();
    ScopedStream scoped(*stream_, offset);
    return parse_dynamic_entries<ELF_T>(*scoped);
  }
  const Segment& load_seg = *segments.back();
  LIEF_DEBUG("Dynamic content wrapped by segment LOAD: [{:#018x}, {:#018x}] "
             "[{:#018x}, {:#018x}]",
             load_seg.virtual_address(),
             load_seg.virtual_address() + load_seg.virtual_size(),
             load_seg.file_offset(),
             load_seg.file_offset() + load_seg.physical_size());

  span<const uint8_t> seg_content = load_seg.content();
  if (seg_content.empty()) {
    return make_error_code(lief_errors::corrupted);
  }

  int64_t rel_offset =
      (int64_t)pt_dyn.virtual_address() - (int64_t)load_seg.virtual_address();
  if (rel_offset < 0 || (uint64_t)rel_offset >= seg_content.size()) {
    return make_error_code(lief_errors::corrupted);
  }

  span<const uint8_t> dynamic_content = seg_content.subspan(rel_offset);
  SpanStream stream(dynamic_content);
  stream.set_endian_swap(stream_->should_swap());
  return parse_dynamic_entries<ELF_T>(stream);
}

template<typename ELF_T>
ok_error_t Parser::process_dynamic_table() {
  {
    DynamicEntry* dt_symtab = binary_->get(DynamicEntry::TAG::SYMTAB);
    DynamicEntry* dt_syment = binary_->get(DynamicEntry::TAG::SYMENT);

    if (dt_syment != nullptr &&
        dt_syment->value() != sizeof(typename ELF_T::Elf_Sym))
    {
      LIEF_WARN("DT_SYMTENT is corrupted");
    }

    if (dt_symtab != nullptr && config_.parse_dyn_symbols) {
      const uint64_t virtual_address = dt_symtab->value();
      if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
        parse_dynamic_symbols<ELF_T>(*res);
      } else {
        LIEF_WARN("Can't convert DT_SYMTAB.virtual_address into an offset ({:#x})",
                  virtual_address);
      }
    }
  }
  {
    DynamicEntry* dt_rela = binary_->get(DynamicEntry::TAG::RELA);
    DynamicEntry* dt_relasz = binary_->get(DynamicEntry::TAG::RELASZ);

    if (dt_rela != nullptr && dt_relasz != nullptr && config_.parse_relocations) {
      const uint64_t virtual_address = dt_rela->value();
      const uint64_t size = dt_relasz->value();
      if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
        parse_dynamic_relocations<ELF_T, typename ELF_T::Elf_Rela>(*res, size);
        binary_->sizing_info_->rela = size;
      } else {
        LIEF_WARN("Can't convert DT_RELA.virtual_address into an offset ({:#x})",
                  virtual_address);
      }
    }
  }
  {
    DynamicEntry* dt_rel = binary_->get(DynamicEntry::TAG::REL);
    DynamicEntry* dt_relsz = binary_->get(DynamicEntry::TAG::RELSZ);

    if (dt_rel != nullptr && dt_relsz != nullptr && config_.parse_relocations) {
      const uint64_t virtual_address = dt_rel->value();
      const uint64_t size = dt_relsz->value();
      if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
        parse_dynamic_relocations<ELF_T, typename ELF_T::Elf_Rel>(*res, size);
        binary_->sizing_info_->rela = size;
      } else {
        LIEF_WARN("Can't convert DT_REL.virtual_address into an offset ({:#x})",
                  virtual_address);
      }
    }
  }
  {
    DynamicEntry* dt_relr = binary_->get(DynamicEntry::TAG::RELR);
    DynamicEntry* dt_relrsz = binary_->get(DynamicEntry::TAG::RELRSZ);

    if (dt_relr != nullptr && dt_relrsz != nullptr && config_.parse_relocations) {
      const uint64_t virtual_address = dt_relr->value();
      const uint64_t size = dt_relrsz->value();
      if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
        parse_relative_relocations<ELF_T>(*res, size);
        binary_->sizing_info_->relr = size;
      } else {
        LIEF_WARN("Can't convert DT_RELR.virtual_address into an offset ({:#x})",
                  virtual_address);
      }
    }
  }
  {
    DynamicEntry* dt_relr = binary_->get(DynamicEntry::TAG::ANDROID_RELR);
    DynamicEntry* dt_relrsz = binary_->get(DynamicEntry::TAG::ANDROID_RELRSZ);

    if (dt_relr != nullptr && dt_relrsz != nullptr && config_.parse_relocations) {
      const uint64_t virtual_address = dt_relr->value();
      const uint64_t size = dt_relrsz->value();
      if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
        parse_relative_relocations<ELF_T>(*res, size);
        binary_->sizing_info_->relr = size;
      } else {
        LIEF_WARN("Can't convert (Android)DT_RELR.virtual_address into an offset "
                  "({:#x})",
                  virtual_address);
      }
    }
  }
  {
    DynamicEntry* dt_rela = binary_->get(DynamicEntry::TAG::ANDROID_RELA);
    DynamicEntry* dt_relasz = binary_->get(DynamicEntry::TAG::ANDROID_RELASZ);
    if (dt_rela == nullptr) {
      dt_rela = binary_->get(DynamicEntry::TAG::ANDROID_REL);
      dt_relasz = binary_->get(DynamicEntry::TAG::ANDROID_RELSZ);
    }

    if (dt_rela != nullptr && dt_relasz != nullptr && config_.parse_relocations) {
      const uint64_t virtual_address = dt_rela->value();
      const uint64_t size = dt_relasz->value();
      if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
        parse_packed_relocations<ELF_T>(*res, size);
        binary_->sizing_info_->android_rela = size;
      } else {
        LIEF_WARN("Can't convert DT_ANDROID_REL[A].virtual_address into an offset "
                  "({:#x})",
                  virtual_address);
      }
    }
  }
  {
    DynamicEntry* dt_jmprel = binary_->get(DynamicEntry::TAG::JMPREL);
    DynamicEntry* dt_pltrelsz = binary_->get(DynamicEntry::TAG::PLTRELSZ);

    if (dt_jmprel != nullptr && dt_pltrelsz != nullptr &&
        config_.parse_relocations)
    {
      const uint64_t virtual_address = dt_jmprel->value();
      const uint64_t size = dt_pltrelsz->value();
      DynamicEntry* dt_pltrel = binary_->get(DynamicEntry::TAG::PLTREL);
      auto type = DynamicEntry::TAG::UNKNOWN;
      if (dt_pltrel != nullptr) {
        type = DynamicEntry::from_value(dt_pltrel->value(),
                                        binary_->header().machine_type());
      } else {
        // Try to guess: We assume that on ELF64 -> DT_RELA and on ELF32 -> DT_REL
        if constexpr (std::is_same_v<ELF_T, details::ELF64>) {
          type = DynamicEntry::TAG::RELA;
        } else {
          type = DynamicEntry::TAG::REL;
        }
      }

      if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
        type == DynamicEntry::TAG::RELA ?
            parse_pltgot_relocations<ELF_T, typename ELF_T::Elf_Rela>(*res, size) :
            parse_pltgot_relocations<ELF_T, typename ELF_T::Elf_Rel>(*res, size);
        binary_->sizing_info_->jmprel = size;
      } else {
        LIEF_WARN("Can't convert DT_JMPREL.virtual_address into an offset ({:#x})",
                  virtual_address);
      }
    }
  }
  if (config_.parse_symbol_versions && config_.parse_dyn_symbols) {
    if (DynamicEntry* dt_versym = binary_->get(DynamicEntry::TAG::VERSYM)) {
      const uint64_t virtual_address = dt_versym->value();
      if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
        parse_symbol_version(*res);
        binary_->sizing_info_->versym =
            binary_->dynamic_symbols_.size() * sizeof(uint16_t);
      } else {
        LIEF_WARN("Can't convert DT_VERSYM.virtual_address into an offset ({:#x})",
                  virtual_address);
      }
    }
  }

  if (config_.parse_symbol_versions) {
    DynamicEntry* dt_verneed = binary_->get(DynamicEntry::TAG::VERNEED);
    DynamicEntry* dt_verneed_num = binary_->get(DynamicEntry::TAG::VERNEEDNUM);

    if (dt_verneed != nullptr && dt_verneed_num != nullptr) {
      const uint64_t virtual_address = dt_verneed->value();
      const uint32_t nb_entries =
          std::min(Parser::NB_MAX_SYMBOLS,
                   static_cast<uint32_t>(dt_verneed_num->value()));

      if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
        parse_symbol_version_requirement<ELF_T>(*res, nb_entries);
      } else {
        LIEF_WARN(
            "Can't convert DT_VERNEED.virtual_address into an offset ({:#x})",
            virtual_address
        );
      }
    }
  }

  if (config_.parse_symbol_versions) {
    DynamicEntry* dt_verdef = binary_->get(DynamicEntry::TAG::VERDEF);
    DynamicEntry* dt_verdef_num = binary_->get(DynamicEntry::TAG::VERDEFNUM);
    if (dt_verdef != nullptr && dt_verdef_num != nullptr) {
      const uint64_t virtual_address = dt_verdef->value();
      const uint32_t nb_entries =
          std::min<uint32_t>(NB_MAX_SYMBOLS, dt_verdef_num->value());

      if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
        parse_symbol_version_definition<ELF_T>(*res, nb_entries);
      } else {
        LIEF_WARN("Can't convert DT_VERDEF.virtual_address into an offset ({:#x})",
                  virtual_address);
      }
    }
  }

  return ok();
}

template<typename ELF_T>
ok_error_t Parser::parse_header() {
  using Elf_Half = typename ELF_T::Elf_Half;
  using Elf_Word = typename ELF_T::Elf_Word;
  using Elf_Addr = typename ELF_T::Elf_Addr;
  using Elf_Off = typename ELF_T::Elf_Off;

  LIEF_DEBUG("[+] Parsing Header");
  stream_->setpos(0);
  if (auto res = stream_->read<Header::identity_t>()) {
    binary_->header_.identity_ = *res;
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_ident");
    return make_error_code(lief_errors::read_error);
  }

  if (auto res = stream_->read<Elf_Half>()) {
    binary_->header_.file_type_ = Header::FILE_TYPE(*res);
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_type");
    return make_error_code(lief_errors::read_error);
  }

  if (auto res = stream_->read<Elf_Half>()) {
    binary_->header_.machine_type_ = static_cast<ARCH>(*res);
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_machine");
    return make_error_code(lief_errors::read_error);
  }

  if (auto res = stream_->read<Elf_Word>()) {
    binary_->header_.object_file_version_ = Header::VERSION(*res);
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_version");
    return make_error_code(lief_errors::read_error);
  }

  if (auto res = stream_->read<Elf_Addr>()) {
    binary_->header_.entrypoint_ = *res;
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_entry");
    return make_error_code(lief_errors::read_error);
  }

  if (auto res = stream_->read<Elf_Off>()) {
    binary_->header_.program_headers_offset_ = *res;
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_phoff");
    return make_error_code(lief_errors::read_error);
  }

  if (auto res = stream_->read<Elf_Off>()) {
    binary_->header_.section_headers_offset_ = *res;
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_shoff");
    return make_error_code(lief_errors::read_error);
  }

  if (auto res = stream_->read<Elf_Word>()) {
    binary_->header_.processor_flags_ = *res;
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_flags");
    return make_error_code(lief_errors::read_error);
  }

  if (auto res = stream_->read<Elf_Half>()) {
    binary_->header_.header_size_ = *res;
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_ehsize");
    return make_error_code(lief_errors::read_error);
  }

  if (auto res = stream_->read<Elf_Half>()) {
    binary_->header_.program_header_size_ = *res;
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_phentsize");
    return make_error_code(lief_errors::read_error);
  }

  if (auto res = stream_->read<Elf_Half>()) {
    binary_->header_.numberof_segments_ = *res;
  } else {
    if (auto res = stream_->read<uint8_t>()) {
      binary_->header_.numberof_segments_ = *res;
    } else {
      LIEF_ERR("Can't parse Elf_Ehdr.e_phnum");
      return make_error_code(lief_errors::read_error);
    }
  }

  if (auto res = stream_->read<Elf_Half>()) {
    binary_->header_.section_header_size_ = *res;
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_shentsize");
    return make_error_code(lief_errors::read_error);
  }

  if (auto res = stream_->read<Elf_Half>()) {
    binary_->header_.numberof_sections_ = *res;
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_shnum");
    return make_error_code(lief_errors::read_error);
  }

  if (auto res = stream_->read<Elf_Half>()) {
    binary_->header_.section_string_table_idx_ = *res;
  } else {
    LIEF_ERR("Can't parse Elf_Ehdr.e_shstrndx");
    return make_error_code(lief_errors::read_error);
  }

  return ok();
}


template<typename ELF_T>
result<uint32_t>
    Parser::get_numberof_dynamic_symbols(ParserConfig::DYNSYM_COUNT mtd) const {

  switch (mtd) {
    case ParserConfig::DYNSYM_COUNT::HASH: return nb_dynsym_hash<ELF_T>();
    case ParserConfig::DYNSYM_COUNT::SECTION: return nb_dynsym_section<ELF_T>();
    case ParserConfig::DYNSYM_COUNT::RELOCATIONS:
      return nb_dynsym_relocations<ELF_T>();

    case ParserConfig::DYNSYM_COUNT::AUTO:
    default:
    {
      uint32_t nb_reloc = 0;
      uint32_t nb_section = 0;
      uint32_t nb_hash = 0;

      if (auto res = get_numberof_dynamic_symbols<ELF_T>(
              ParserConfig::DYNSYM_COUNT::RELOCATIONS
          ))
      {
        nb_reloc = *res;
      }

      if (auto res = get_numberof_dynamic_symbols<ELF_T>(
              ParserConfig::DYNSYM_COUNT::SECTION
          ))
      {
        nb_section = *res;
      }

      if (auto res = get_numberof_dynamic_symbols<ELF_T>(
              ParserConfig::DYNSYM_COUNT::HASH
          ))
      {
        nb_hash = *res;
      }

      LIEF_DEBUG("#dynsym.reloc: {}", nb_reloc);
      LIEF_DEBUG("#dynsym.section: {}", nb_section);
      LIEF_DEBUG("#dynsym.hash: {}", nb_hash);

      if (nb_hash > 0 && nb_section == nb_hash) {
        return nb_hash;
      }

      uint32_t candidate = nb_reloc;
      if (nb_section < Parser::NB_MAX_SYMBOLS && nb_section > nb_reloc &&
          (nb_section - nb_reloc) < Parser::DELTA_NB_SYMBOLS)
      {
        candidate = nb_section;
      }

      if (nb_hash == 0) {
        return candidate;
      }

      if (nb_hash < Parser::NB_MAX_SYMBOLS && nb_hash > candidate &&
          (nb_hash - candidate) < Parser::DELTA_NB_SYMBOLS)
      {
        candidate = nb_hash;
      }

      return candidate;
    }
  }
}

template<typename ELF_T>
result<uint32_t> Parser::nb_dynsym_relocations() const {
  using rela_t = typename ELF_T::Elf_Rela;
  using rel_t = typename ELF_T::Elf_Rel;
  uint32_t nb_symbols = 0;

  // Dynamic Relocations
  // ===================

  // RELA
  // ----
  DynamicEntry* dt_rela = binary_->get(DynamicEntry::TAG::RELA);
  DynamicEntry* dt_relasz = binary_->get(DynamicEntry::TAG::RELASZ);
  if (dt_rela != nullptr && dt_relasz != nullptr) {
    const uint64_t virtual_address = dt_rela->value();
    const uint64_t size = dt_relasz->value();
    if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
      nb_symbols =
          std::max(nb_symbols, max_relocation_index<ELF_T, rela_t>(*res, size));
    }
  }


  // REL
  // ---
  DynamicEntry* dt_rel = binary_->get(DynamicEntry::TAG::REL);
  DynamicEntry* dt_relsz = binary_->get(DynamicEntry::TAG::RELSZ);

  if (dt_rel != nullptr && dt_relsz != nullptr) {
    const uint64_t virtual_address = dt_rel->value();
    const uint64_t size = dt_relsz->value();
    if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
      nb_symbols =
          std::max(nb_symbols, max_relocation_index<ELF_T, rel_t>(*res, size));
    }
  }

  // Parse PLT/GOT Relocations
  // ==========================

  DynamicEntry* dt_jmprel = binary_->get(DynamicEntry::TAG::JMPREL);
  DynamicEntry* dt_pltrelsz = binary_->get(DynamicEntry::TAG::PLTRELSZ);
  if (dt_jmprel != nullptr && dt_pltrelsz != nullptr) {
    const uint64_t virtual_address = dt_jmprel->value();
    const uint64_t size = dt_pltrelsz->value();
    DynamicEntry* dt_pltrel = binary_->get(DynamicEntry::TAG::PLTREL);
    auto type = DynamicEntry::TAG::UNKNOWN;
    if (dt_pltrel != nullptr) {
      type = DynamicEntry::from_value(dt_pltrel->value(),
                                      binary_->header().machine_type());
    } else {
      // Try to guess: We assume that on ELF64 -> DT_RELA and on ELF32 -> DT_REL
      if constexpr (std::is_same_v<ELF_T, details::ELF64>) {
        type = DynamicEntry::TAG::RELA;
      } else {
        type = DynamicEntry::TAG::REL;
      }
    }
    if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
      if (type == DynamicEntry::TAG::RELA) {
        nb_symbols =
            std::max(nb_symbols, max_relocation_index<ELF_T, rela_t>(*res, size));
      } else {
        nb_symbols =
            std::max(nb_symbols, max_relocation_index<ELF_T, rel_t>(*res, size));
      }
    }
  }

  return nb_symbols;
}

template<typename ELF_T, typename REL_T>
uint32_t Parser::max_relocation_index(uint64_t relocations_offset,
                                      uint64_t size) const {
  static_assert(std::is_same_v<REL_T, typename ELF_T::Elf_Rel> ||
                    std::is_same_v<REL_T, typename ELF_T::Elf_Rela>,
                "REL_T must be Elf_Rel || Elf_Rela");

  const uint8_t shift = ELF_T::r_info_shift;
  ;

  const auto nb_entries = static_cast<uint32_t>(size / sizeof(REL_T));

  uint32_t idx = 0;
  stream_->setpos(relocations_offset);
  for (uint32_t i = 0; i < nb_entries; ++i) {
    auto reloc_entry = stream_->read<REL_T>();
    if (!reloc_entry) {
      break;
    }
    idx = std::max(idx, static_cast<uint32_t>(reloc_entry->r_info >> shift));
  }
  return idx + 1;
} // max_relocation_index

template<typename ELF_T>
result<uint32_t> Parser::nb_dynsym_section() const {
  using Elf_Sym = typename ELF_T::Elf_Sym;
  using Elf_Off = typename ELF_T::Elf_Off;
  Section* dynsym_sec = binary_->get(Section::TYPE::DYNSYM);

  if (dynsym_sec == nullptr) {
    return 0;
  }

  const Elf_Off section_size = dynsym_sec->size();
  const auto nb_symbols = static_cast<uint32_t>((section_size / sizeof(Elf_Sym)));
  return nb_symbols;
}

template<typename ELF_T>
result<uint32_t> Parser::nb_dynsym_hash() const {

  if (binary_->has(DynamicEntry::TAG::HASH)) {
    return nb_dynsym_sysv_hash<ELF_T>();
  }

  if (binary_->has(DynamicEntry::TAG::GNU_HASH)) {
    return nb_dynsym_gnu_hash<ELF_T>();
  }

  return 0;
}


template<typename ELF_T>
result<uint32_t> Parser::nb_dynsym_sysv_hash() const {
  using Elf_Off = typename ELF_T::Elf_Off;

  const DynamicEntry* dyn_hash = binary_->get(DynamicEntry::TAG::HASH);
  if (dyn_hash == nullptr) {
    LIEF_ERR("Can't find DT_GNU_HASH");
    return make_error_code(lief_errors::not_found);
  }
  Elf_Off sysv_hash_offset = 0;
  if (auto res = binary_->virtual_address_to_offset(dyn_hash->value())) {
    sysv_hash_offset = *res;
  } else {
    return make_error_code(res.error());
  }

  // From the doc: 'so nchain should equal the number of symbol table entries.'
  stream_->setpos(sysv_hash_offset + sizeof(uint32_t));
  auto nb_symbols = stream_->read<uint32_t>();
  if (nb_symbols) {
    return nb_symbols;
  }

  return 0;
}

template<typename ELF_T>
result<uint32_t> Parser::nb_dynsym_gnu_hash() const {
  const DynamicEntry* dyn_hash = binary_->get(DynamicEntry::TAG::GNU_HASH);
  if (dyn_hash == nullptr) {
    LIEF_ERR("Can't find DT_GNU_HASH");
    return make_error_code(lief_errors::not_found);
  }

  const uint64_t addr = dyn_hash->value();

  if (Segment* seg = binary_->segment_from_virtual_address(addr)) {
    std::unique_ptr<SpanStream> stream = seg->stream();
    if (stream == nullptr) {
      return make_error_code(lief_errors::read_error);
    }
    uint64_t rel_offset = addr - seg->virtual_address();
    stream->setpos(rel_offset);
    stream->set_endian_swap(stream_->should_swap());
    return GnuHash::nb_symbols<ELF_T>(*stream);
  }

  return make_error_code(lief_errors::not_found);
}

template<typename ELF_T>
ok_error_t Parser::parse_sections() {
  using Elf_Shdr = typename ELF_T::Elf_Shdr;

  using Elf_Off = typename ELF_T::Elf_Off;
  LIEF_DEBUG("Parsing Section");

  const Elf_Off shdr_offset = binary_->header_.section_headers_offset();
  const auto numberof_sections = binary_->header_.numberof_sections();

  const bool is_memory_stream = memory_address_ > 0;
  // Sections are usually not mapped so logging errors is not relevant for
  // in-memory parsing
  logging::Scoped scope(is_memory_stream ? logging::LEVEL::OFF :
                                           logging::get_level());

  stream_->setpos(shdr_offset);
  std::unordered_map<Section*, size_t> sections_names;
  DataHandler::Handler& handler = *binary_->datahandler_;
  const ARCH arch = binary_->header().machine_type();
  for (size_t i = 0; i < numberof_sections; ++i) {
    LIEF_DEBUG("  Elf_Shdr#{:02d}.offset: {:#x} ", i, stream_->pos());
    const auto shdr = stream_->read<Elf_Shdr>();
    if (!shdr) {
      LIEF_ERR("  Can't parse section #{:02d}", i);
      break;
    }

    auto section = std::unique_ptr<Section>(new Section(*shdr, arch));
    section->datahandler_ = binary_->datahandler_.get();

    const uint64_t section_start = section->file_offset();
    bool access_content = true;
    if (section_start > stream_->size() ||
        section->size() > stream_->size() - section_start)
    {
      access_content = false;
      if (section->type() != Section::TYPE::NOBITS) {
        LIEF_WARN("Can't access the content of section #{}", i);
      }
    }

    if (section->size() == 0 && section->file_offset() > 0 && access_content) {
      // Even if the size is 0, it is worth creating the node
      handler.create(section->file_offset(), 0, DataHandler::Node::SECTION);
    }

    // Only if it contains data (with bits)
    if (section->size() > 0 && access_content) {
      int64_t read_size = section->size();
      if (static_cast<int32_t>(read_size) < 0) {
        LIEF_WARN("Section #{} is {} bytes large. Only the first {} bytes will be "
                  "taken into account",
                  i, read_size, Section::MAX_SECTION_SIZE);
        read_size = Section::MAX_SECTION_SIZE;
      }
      if (read_size > Section::MAX_SECTION_SIZE) {
        LIEF_WARN("Section #{} is {} bytes large. Only the first {} bytes will be "
                  "taken into account",
                  i, read_size, Section::MAX_SECTION_SIZE);
        read_size = Section::MAX_SECTION_SIZE;
      }

      handler.create(section->file_offset(), read_size,
                     DataHandler::Node::SECTION);

      const Elf_Off offset_to_content = section->file_offset();
      auto alloc =
          binary_->datahandler_->reserve(section->file_offset(), read_size);
      if (!alloc) {
        LIEF_ERR("Can't allocate memory");
        break;
      }

      /* The DataHandlerStream interface references ELF data that are
       * located in the ELF::DataHandler. Therefore, we can skip reading
       * the data since they are already present in the data handler.
       * This optimization saves memory (which is also performed in
       * parse_segments<>(...))
       */
      if (stream_->type() != BinaryStream::STREAM_TYPE::ELF_DATA_HANDLER) {
        std::vector<uint8_t> sec_content;
        if (!stream_->peek_data(sec_content, offset_to_content, read_size)) {
          if (section->type() != Section::TYPE::NOBITS) {
            LIEF_WARN("  Unable to get content of section #{:d}", i);
          }
        } else {
          section->content(std::move(sec_content));
        }
      }
    }
    sections_idx_[i] = section.get();
    sections_names[section.get()] = shdr->sh_name;
    binary_->sections_.push_back(std::move(section));
  }

  LIEF_DEBUG("    Parse section names");
  // Parse name
  if (binary_->header_.section_name_table_idx() >= binary_->sections_.size()) {
    LIEF_WARN("The .shstr index is out of range of the section table");
    return ok();
  }

  const size_t section_string_index = binary_->header_.section_name_table_idx();
  const std::unique_ptr<Section>& string_section =
      binary_->sections_[section_string_index];
  for (std::unique_ptr<Section>& section : binary_->sections_) {
    const auto it_name_idx = sections_names.find(section.get());
    if (it_name_idx == sections_names.end()) {
      LIEF_WARN("Missing name_idx for section at offset {:#x}",
                section->file_offset());
      continue;
    }
    const size_t name_offset = it_name_idx->second;
    auto name =
        stream_->peek_string_at(string_section->file_offset() + name_offset);
    if (!name) {
      LIEF_ERR("Can't read section name for section {:#x}",
               section->file_offset());
      break;
    }
    section->name(*name);
  }
  return ok();
}

template<typename ELF_T>
ok_error_t Parser::parse_segments() {
  using Elf_Phdr = typename ELF_T::Elf_Phdr;
  using Elf_Off = typename ELF_T::Elf_Off;

  LIEF_DEBUG("Parsing segments");
  const Header& hdr = binary_->header();
  const Elf_Off segment_headers_offset = hdr.program_headers_offset();
  const auto nbof_segments =
      std::min<uint32_t>(hdr.numberof_segments(), Parser::NB_MAX_SEGMENTS);

  stream_->setpos(segment_headers_offset);

  const ARCH arch = hdr.machine_type();
  const Header::OS_ABI os = hdr.identity_os_abi();

  for (size_t i = 0; i < nbof_segments; ++i) {
    const auto elf_phdr = stream_->read<Elf_Phdr>();
    if (!elf_phdr) {
      LIEF_ERR("Can't parse segement #{:d}", i);
      break;
    }

    auto segment = std::unique_ptr<Segment>(new Segment(*elf_phdr, arch, os));
    segment->datahandler_ = binary_->datahandler_.get();

    if (0 < segment->physical_size() &&
        segment->physical_size() < Parser::MAX_SEGMENT_SIZE)
    {
      uint64_t read_size = segment->physical_size();
      if (read_size > Parser::MAX_SEGMENT_SIZE) {
        LIEF_WARN("Segment #{} is {} bytes large. Only the first {} bytes will be "
                  "taken into account",
                  i, read_size, Parser::MAX_SEGMENT_SIZE);
        read_size = Parser::MAX_SEGMENT_SIZE;
      }
      if (read_size > stream_->size()) {
        LIEF_WARN("Segment #{} has a physical size larger than the current stream "
                  "size ({} > {}). "
                  "The content will be truncated with the stream size.",
                  i, read_size, stream_->size());
        read_size = stream_->size();
      }

      segment->datahandler_->create(segment->file_offset(), read_size,
                                    DataHandler::Node::SEGMENT);
      segment->handler_size_ = read_size;

      const bool corrupted_offset =
          segment->file_offset() > stream_->size() ||
          (segment->file_offset() + read_size) > stream_->size();

      if (!corrupted_offset) {
        const Elf_Off offset_to_content = segment->file_offset();
        auto alloc =
            binary_->datahandler_->reserve(segment->file_offset(), read_size);
        if (!alloc) {
          LIEF_ERR("Can't allocate memory");
          break;
        }
        /* The DataHandlerStream interface references ELF data that are
         * located in the ELF::DataHandler. Therefore, we can skip reading
         * the data since they are already present in the data handler.
         * This optimization saves memory (which is also performed in
         * parse_sections<>(...))
         */
        if (stream_->type() != BinaryStream::STREAM_TYPE::ELF_DATA_HANDLER) {
          std::vector<uint8_t> seg_content;
          if (stream_->peek_data(seg_content, offset_to_content, read_size)) {
            segment->content(std::move(seg_content));
          } else {
            LIEF_ERR("Unable to get the content of segment #{:d}", i);
          }
        }

        if (segment->is_interpreter()) {
          auto interpreter = stream_->peek_string_at(offset_to_content, read_size);
          if (!interpreter) {
            LIEF_ERR("Can't read the interpreter string");
          } else {
            binary_->interpreter_ = *interpreter;
            binary_->sizing_info_->interpreter = read_size;
          }
        }
      }
    } else {
      segment->handler_size_ = segment->physical_size();
      segment->datahandler_->create(segment->file_offset(),
                                    segment->physical_size(),
                                    DataHandler::Node::SEGMENT);
    }

    for (std::unique_ptr<Section>& section : binary_->sections_) {
      if (check_section_in_segment(*section, *segment.get())) {
        section->segments_.push_back(segment.get());
        segment->sections_.push_back(section.get());
      }
    }
    binary_->segments_.push_back(std::move(segment));
  }
  return ok();
}


template<typename ELF_T>
ok_error_t Parser::parse_packed_relocations(uint64_t offset, uint64_t size) {
  using Elf_Rela = typename ELF_T::Elf_Rela;
  static constexpr uint64_t GROUPED_BY_INFO_FLAG = 1 << 0;
  static constexpr uint64_t GROUPED_BY_OFFSET_DELTA_FLAG = 1 << 1;
  static constexpr uint64_t GROUPED_BY_ADDEND_FLAG = 1 << 2;
  static constexpr uint64_t GROUP_HAS_ADDEND_FLAG = 1 << 3;

  LIEF_DEBUG("Parsing Android packed relocations");
  if (size < 4) {
    LIEF_ERR("Invalid Android packed relocation header");
    return make_error_code(lief_errors::read_error);
  }

  if (offset >= stream_->size()) {
    LIEF_ERR("Android packed relocations: offset is out of range");
    return make_error_code(lief_errors::read_error);
  }
  size = std::min<uint64_t>(size, stream_->size() - offset);
  const auto* raw = stream_->peek_array<uint8_t>(offset, size);
  if (raw == nullptr) {
    LIEF_ERR("Can't read the Android packed relocation table");
    return make_error_code(lief_errors::read_error);
  }
  SpanStream table_stream(raw, size);
  ScopedStream rel_stream(table_stream);

  const auto H0 = rel_stream->read<char>().value_or(0);
  const auto H1 = rel_stream->read<char>().value_or(0);
  const auto H2 = rel_stream->read<char>().value_or(0);
  const auto H3 = rel_stream->read<char>().value_or(0);

  LIEF_DEBUG("Header: {} {} {} {}", H0, H1, H2, H3);

  // Check for the Magic: APS2
  if (H0 != 'A' || H1 != 'P' || H2 != 'S' || H3 != '2') {
    LIEF_ERR("Invalid Android packed relocation magic header: "
             "{} {} {} {}",
             H0, H1, H2, H3);
    return make_error_code(lief_errors::read_error);
  }

  auto res_nb_relocs = rel_stream->read_sleb128();
  if (!res_nb_relocs) {
    LIEF_ERR("Can't read number of relocations");
    return make_error_code(lief_errors::read_error);
  }
  auto res_rels_offset = rel_stream->read_sleb128();
  if (!res_rels_offset) {
    LIEF_ERR("Can't read offset");
    return make_error_code(lief_errors::read_error);
  }

  uint64_t nb_relocs = *res_nb_relocs;
  uint64_t r_offset = *res_rels_offset;
  uint64_t addend = 0;
  const ARCH arch = binary_->header().machine_type();

  LIEF_DEBUG("Nb relocs: {}", nb_relocs);

  if (nb_relocs > NB_MAX_RELOCATIONS) {
    LIEF_WARN("Android packed relocations: the number of relocations ({}) "
              "exceeds the limit ({}). The table is likely corrupted",
              nb_relocs, NB_MAX_RELOCATIONS);
    return make_error_code(lief_errors::corrupted);
  }

  while (nb_relocs > 0) {
    auto nb_reloc_group_r = rel_stream->read_sleb128();
    if (!nb_reloc_group_r) {
      break;
    }

    uint64_t nb_reloc_group = *nb_reloc_group_r;
    LIEF_DEBUG("  Nb relocs in group: {}", nb_reloc_group);

    if (nb_reloc_group > nb_relocs) {
      break;
    }

    nb_relocs -= nb_reloc_group;

    auto group_flag_r = rel_stream->read_sleb128();
    if (!group_flag_r) {
      LIEF_ERR("Can't read group flag");
      break;
    }
    uint64_t group_flag = *group_flag_r;

    const bool g_by_info = group_flag & GROUPED_BY_INFO_FLAG;
    const bool g_by_offset_delta = group_flag & GROUPED_BY_OFFSET_DELTA_FLAG;
    const bool g_by_addend = group_flag & GROUPED_BY_ADDEND_FLAG;
    const bool g_has_addend = group_flag & GROUP_HAS_ADDEND_FLAG;

    uint64_t group_off_delta =
        g_by_offset_delta ? rel_stream->read_sleb128().value_or(0) : 0;

    uint64_t groupr_info = g_by_info ? rel_stream->read_sleb128().value_or(0) : 0;

    if (g_by_addend && g_has_addend) {
      addend += rel_stream->read_sleb128().value_or(0);
    }

    if (!g_has_addend) {
      addend = 0;
    }

    for (size_t i = 0; i < nb_reloc_group; ++i) {
      if (!*rel_stream) {
        break;
      }
      r_offset += g_by_offset_delta ? group_off_delta :
                                      rel_stream->read_sleb128().value_or(0);
      uint64_t info =
          g_by_info ? groupr_info : rel_stream->read_sleb128().value_or(0);
      if (g_has_addend && !g_by_addend) {
        addend += rel_stream->read_sleb128().value_or(0);
      }

      Elf_Rela R;
      R.r_info = info;
      R.r_addend = addend;
      R.r_offset = r_offset;
      auto reloc = std::unique_ptr<Relocation>(new Relocation(
          R, Relocation::PURPOSE::DYNAMIC, Relocation::ENCODING::ANDROID_SLEB, arch
      ));
      bind_symbol(*reloc);
      insert_relocation(std::move(reloc));
    }
  }
  return ok();
}

template<typename ELF_T>
ok_error_t Parser::parse_relative_relocations(uint64_t offset, uint64_t size) {
  LIEF_DEBUG("Parsing relative relocations");
  using Elf_Relr = typename ELF_T::uint;
  using Elf_Addr = typename ELF_T::uint;
  ScopedStream rel_stream(*stream_, offset);

  Elf_Addr base = 0;
  const ARCH arch = binary_->header().machine_type();
  Relocation::TYPE type = Relocation::TYPE::UNKNOWN;
  switch (arch) {
    case ARCH::AARCH64: type = Relocation::TYPE::AARCH64_RELATIVE; break;
    case ARCH::X86_64: type = Relocation::TYPE::X86_64_RELATIVE; break;
    case ARCH::ARM: type = Relocation::TYPE::ARM_RELATIVE; break;
    case ARCH::HEXAGON: type = Relocation::TYPE::HEX_RELATIVE; break;
    case ARCH::PPC64: type = Relocation::TYPE::PPC64_RELATIVE; break;
    case ARCH::PPC: type = Relocation::TYPE::PPC_RELATIVE; break;
    case ARCH::I386:
    case ARCH::IAMCU: type = Relocation::TYPE::X86_RELATIVE; break;
    default: break;
  }

  size_t nb_relocs = 0;
  while (rel_stream->pos() < (offset + size)) {
    if (nb_relocs >= NB_MAX_RELOCATIONS) {
      LIEF_WARN("RELR relocations: the number of relocations exceeds the limit "
                "({}). The table is likely corrupted",
                NB_MAX_RELOCATIONS);
      break;
    }
    auto opt_relr = rel_stream->read<Elf_Relr>();
    if (!opt_relr) {
      break;
    }
    Elf_Relr rel = *opt_relr;
    if ((rel & 1) == 0) {
      Elf_Addr r_offset = rel;
      auto reloc =
          std::make_unique<Relocation>(r_offset, type, Relocation::ENCODING::RELR);
      reloc->purpose(Relocation::PURPOSE::DYNAMIC);
      insert_relocation(std::move(reloc));
      ++nb_relocs;
      base = rel + sizeof(Elf_Addr);
    } else {
      for (Elf_Addr offset = base; (rel >>= 1) != 0; offset += sizeof(Elf_Addr)) {
        if ((rel & 1) != 0) {
          Elf_Addr r_offset = offset;
          auto reloc = std::make_unique<Relocation>(r_offset, type,
                                                    Relocation::ENCODING::RELR);
          reloc->purpose(Relocation::PURPOSE::DYNAMIC);
          insert_relocation(std::move(reloc));
          ++nb_relocs;
        }
      }
      base += (8 * sizeof(Elf_Relr) - 1) * sizeof(Elf_Addr);
    }
  }
  return ok();
}


template<typename ELF_T, typename REL_T>
ok_error_t Parser::parse_dynamic_relocations(uint64_t relocations_offset,
                                             uint64_t size) {
  static_assert(std::is_same_v<REL_T, typename ELF_T::Elf_Rel> ||
                    std::is_same_v<REL_T, typename ELF_T::Elf_Rela>,
                "REL_T must be Elf_Rel || Elf_Rela");
  LIEF_DEBUG("Parsing dynamic relocations");

  // Already parsed
  if (binary_->dynamic_relocations().size() > 0) {
    return ok();
  }

  auto nb_entries = static_cast<uint32_t>(size / sizeof(REL_T));

  nb_entries = std::min<uint32_t>(nb_entries, Parser::NB_MAX_RELOCATIONS);
  binary_->relocations_.reserve(nb_entries);

  stream_->setpos(relocations_offset);
  const ARCH arch = binary_->header().machine_type();
  const Relocation::ENCODING enc = std::is_same_v<REL_T, typename ELF_T::Elf_Rel> ?
                                       Relocation::ENCODING::REL :
                                       Relocation::ENCODING::RELA;

  for (uint32_t i = 0; i < nb_entries; ++i) {
    const auto raw_reloc = stream_->read<REL_T>();
    if (!raw_reloc) {
      break;
    }

    auto reloc = std::unique_ptr<Relocation>(new Relocation(
        std::move(*raw_reloc), Relocation::PURPOSE::DYNAMIC, enc, arch
    ));
    bind_symbol(*reloc);
    insert_relocation(std::move(reloc));
  }
  return ok();
} // build_dynamic_reclocations

template<typename ELF_T>
ok_error_t Parser::parse_symtab_symbols(const Section& symtab_section,
                                        const Section& string_section) {
  static constexpr size_t MAX_RESERVED_SYMBOLS = 10000;
  using Elf_Sym = typename ELF_T::Elf_Sym;
  LIEF_DEBUG("Parsing symtab symbols");

  LIEF::SpanStream stream(symtab_section.content());
  stream.set_endian_swap(stream_->should_swap());

  const auto nb_symbols = static_cast<uint32_t>((symtab_section.size() /
                                                 sizeof(typename ELF_T::Elf_Sym)));
  const size_t nb_reserved = std::min<size_t>(nb_symbols, MAX_RESERVED_SYMBOLS);
  binary_->symtab_symbols_.reserve(nb_reserved);

  const ARCH arch = binary_->header().machine_type();
  size_t sym_idx = 0;
  while (stream) {
    const auto raw_sym = stream.read<Elf_Sym>();
    if (!raw_sym) {
      return make_error_code(raw_sym.error());
    }

    auto symbol = std::unique_ptr<Symbol>(new Symbol(std::move(*raw_sym), arch));
    const auto name_offset = string_section.file_offset() + raw_sym->st_name;

    if (auto symbol_name = stream_->peek_string_at(name_offset)) {
      symbol->name(std::move(*symbol_name));
    } else {
      LIEF_ERR("Can't read the symbol's name for symbol #{}", sym_idx);
    }

    link_symbol_section(*symbol);
    binary_->symtab_symbols_.push_back(std::move(symbol));
    ++sym_idx;
  }
  return ok();
}

template<typename ELF_T>
ok_error_t Parser::parse_dynamic_symbols(uint64_t offset) {
  using Elf_Sym = typename ELF_T::Elf_Sym;
  using Elf_Off = typename ELF_T::Elf_Off;
  static constexpr size_t MAX_RESERVED_SYMBOLS = 10000;

  LIEF_DEBUG("Parsing dynamic symbols");

  auto res = get_numberof_dynamic_symbols<ELF_T>(config_.count_mtd);
  if (!res) {
    LIEF_ERR("Fail to get the number of dynamic symbols with the current counting "
             "method");
    return make_error_code(lief_errors::parsing_error);
  }

  const uint32_t nb_symbols = res.value();

  const Elf_Off dynamic_symbols_offset = offset;
  const Elf_Off string_offset = get_dynamic_string_table();

  LIEF_DEBUG("    - Number of symbols counted: {:d}", nb_symbols);
  LIEF_DEBUG("    - Table Offset:              {:#x}", dynamic_symbols_offset);
  LIEF_DEBUG("    - String Table Offset:       {:#x}", string_offset);

  if (string_offset == 0) {
    LIEF_WARN("Unable to find the .dynstr section");
    return make_error_code(lief_errors::parsing_error);
  }

  size_t nb_reserved = std::min<size_t>(nb_symbols, MAX_RESERVED_SYMBOLS);
  binary_->dynamic_symbols_.reserve(nb_reserved);
  stream_->setpos(dynamic_symbols_offset);

  for (size_t i = 0; i < nb_symbols; ++i) {
    const auto symbol_header = stream_->read<Elf_Sym>();
    if (!symbol_header) {
      LIEF_DEBUG("Break on symbol #{:d}", i);
      break;
    }
    auto symbol =
        std::unique_ptr<Symbol>(new Symbol(std::move(*symbol_header),
                                           binary_->header().machine_type()));

    if (symbol_header->st_name > 0) {
      auto name = stream_->peek_string_at(string_offset + symbol_header->st_name);
      if (!name) {
        break;
      }

      if (name->empty() && i > 0) {
        LIEF_DEBUG("Symbol name #{:d} is empty", i);
      }

      symbol->name(std::move(*name));
    }
    link_symbol_section(*symbol);
    binary_->dynamic_symbols_.push_back(std::move(symbol));
  }
  binary_->sizing_info_->dynsym =
      binary_->dynamic_symbols_.size() * sizeof(Elf_Sym);
  if (const auto* dt_strsz = binary_->get(DynamicEntry::TAG::STRSZ)) {
    binary_->sizing_info_->dynstr = dt_strsz->value();
  }
  return ok();
}


template<typename ELF_T>
ok_error_t Parser::parse_dynamic_entries(BinaryStream& stream) {
  using Elf_Dyn = typename ELF_T::Elf_Dyn;
  using uint__ = typename ELF_T::uint;
  using Elf_Addr = typename ELF_T::Elf_Addr;
  using Elf_Off = typename ELF_T::Elf_Off;

  LIEF_DEBUG("Parsing dynamic entries");

  uint32_t max_nb_entries = stream.size() / sizeof(Elf_Dyn);
  max_nb_entries =
      std::min<uint32_t>(max_nb_entries, Parser::NB_MAX_DYNAMIC_ENTRIES);

  Elf_Off dynamic_string_offset = get_dynamic_string_table(&stream);

  bool end_of_dynamic = false;
  uint32_t count = 0;
  while (stream && count < max_nb_entries) {
    const auto res_entry = stream.read<Elf_Dyn>();
    if (!res_entry) {
      break;
    }
    ++count;
    const auto entry = *res_entry;

    std::unique_ptr<DynamicEntry> dynamic_entry;

    const ARCH arch = binary_->header().machine_type();

    switch (DynamicEntry::from_value(entry.d_tag, arch)) {
      case DynamicEntry::TAG::NEEDED:
      {
        dynamic_entry = std::make_unique<DynamicEntryLibrary>(entry, arch);
        auto library_name = stream_->peek_string_at(dynamic_string_offset +
                                                    dynamic_entry->value());
        if (!library_name) {
          LIEF_ERR("Can't read library name for DT_NEEDED entry");
          break;
        }
        dynamic_entry->as<DynamicEntryLibrary>()->name(std::move(*library_name));
        break;
      }

      case DynamicEntry::TAG::SONAME:
      {
        dynamic_entry = std::make_unique<DynamicSharedObject>(entry, arch);
        auto sharename = stream_->peek_string_at(dynamic_string_offset +
                                                 dynamic_entry->value());
        if (!sharename) {
          LIEF_ERR("Can't read library name for DT_SONAME entry");
          break;
        }
        dynamic_entry->as<DynamicSharedObject>()->name(std::move(*sharename));
        break;
      }

      case DynamicEntry::TAG::AUXILIARY:
      {
        dynamic_entry = std::make_unique<DynamicEntryAuxiliary>(entry, arch);
        auto name = stream_->peek_string_at(dynamic_string_offset +
                                            dynamic_entry->value());
        if (!name) {
          LIEF_ERR("Can't read library name for DT_AUXILIARY entry");
          break;
        }
        dynamic_entry->as<DynamicEntryAuxiliary>()->name(std::move(*name));
        break;
      }

      case DynamicEntry::TAG::FILTER:
      {
        dynamic_entry = std::make_unique<DynamicEntryFilter>(entry, arch);
        auto name = stream_->peek_string_at(dynamic_string_offset +
                                            dynamic_entry->value());
        if (!name) {
          LIEF_ERR("Can't read library name for DT_FILTER entry");
          break;
        }
        dynamic_entry->as<DynamicEntryFilter>()->name(std::move(*name));
        break;
      }

      case DynamicEntry::TAG::RPATH:
      {
        dynamic_entry = std::make_unique<DynamicEntryRpath>(entry, arch);
        auto name = stream_->peek_string_at(dynamic_string_offset +
                                            dynamic_entry->value());
        if (!name) {
          LIEF_ERR("Can't read rpath string value for DT_RPATH");
          break;
        }
        dynamic_entry->as<DynamicEntryRpath>()->rpath(std::move(*name));
        break;
      }

      case DynamicEntry::TAG::RUNPATH:
      {
        dynamic_entry = std::make_unique<DynamicEntryRunPath>(entry, arch);
        auto name = stream_->peek_string_at(dynamic_string_offset +
                                            dynamic_entry->value());
        if (!name) {
          LIEF_ERR("Can't read runpath string value for DT_RUNPATH");
          break;
        }
        dynamic_entry->as<DynamicEntryRunPath>()->runpath(std::move(*name));
        break;
      }

      case DynamicEntry::TAG::FLAGS_1:
      case DynamicEntry::TAG::FLAGS:
      {
        dynamic_entry = std::make_unique<DynamicEntryFlags>(entry, arch);
        break;
      }

      case DynamicEntry::TAG::SYMTAB:
      case DynamicEntry::TAG::SYMENT:
      case DynamicEntry::TAG::RELA:
      case DynamicEntry::TAG::RELASZ:
      case DynamicEntry::TAG::REL:
      case DynamicEntry::TAG::RELSZ:
      case DynamicEntry::TAG::JMPREL:
      case DynamicEntry::TAG::PLTRELSZ:
      case DynamicEntry::TAG::PLTREL:
      case DynamicEntry::TAG::VERSYM:
      case DynamicEntry::TAG::VERNEED:
      case DynamicEntry::TAG::VERNEEDNUM:
      case DynamicEntry::TAG::VERDEF:
      case DynamicEntry::TAG::VERDEFNUM:
      {
        dynamic_entry = std::make_unique<DynamicEntry>(entry, arch);
        break;
      }

      case DynamicEntry::TAG::FINI_ARRAY:
      case DynamicEntry::TAG::INIT_ARRAY:
      case DynamicEntry::TAG::PREINIT_ARRAY:
      {
        dynamic_entry = std::make_unique<DynamicEntryArray>(entry, arch);
        break;
      }

      case DynamicEntry::TAG::DT_NULL_:
      {
        dynamic_entry = std::make_unique<DynamicEntry>(entry, arch);
        end_of_dynamic = true;
        break;
      }

      default:
      {
        dynamic_entry = std::make_unique<DynamicEntry>(entry, arch);
      }
    }

    if (dynamic_entry != nullptr) {
      binary_->dynamic_entries_.push_back(std::move(dynamic_entry));
    } else {
      LIEF_WARN("Null dynamic_entry");
    }

    if (end_of_dynamic) {
      break;
    }
  }

  // Check for INIT array
  // ====================
  if (DynamicEntry* dt_init_array = binary_->get(DynamicEntry::TAG::INIT_ARRAY)) {
    if (DynamicEntry* dt_init_arraysz =
            binary_->get(DynamicEntry::TAG::INIT_ARRAYSZ))
    {
      binary_->sizing_info_->init_array = dt_init_arraysz->value();
      std::vector<uint64_t>& array =
          dt_init_array->as<DynamicEntryArray>()->array();
      const auto nb_functions =
          static_cast<uint32_t>(dt_init_arraysz->value() / sizeof(uint__));
      if (auto offset = binary_->virtual_address_to_offset(dt_init_array->value()))
      {
        stream_->setpos(*offset);
        for (size_t i = 0; i < nb_functions; ++i) {
          if (auto val = stream_->read<Elf_Addr>()) {
            array.push_back(*val);
          } else {
            break;
          }
        }
      }
    } else {
      LIEF_WARN("The binary is not consistent. Found DT_INIT_ARRAY but missing "
                "DT_INIT_ARRAYSZ");
    }
  }


  // Check for FINI array
  // ====================
  if (DynamicEntry* dt_fini_array = binary_->get(DynamicEntry::TAG::FINI_ARRAY)) {
    if (DynamicEntry* dt_fini_arraysz =
            binary_->get(DynamicEntry::TAG::FINI_ARRAYSZ))
    {
      binary_->sizing_info_->fini_array = dt_fini_arraysz->value();
      std::vector<uint64_t>& array =
          dt_fini_array->as<DynamicEntryArray>()->array();

      const auto nb_functions =
          static_cast<uint32_t>(dt_fini_arraysz->value() / sizeof(uint__));

      if (auto offset = binary_->virtual_address_to_offset(dt_fini_array->value()))
      {
        stream_->setpos(*offset);
        for (size_t i = 0; i < nb_functions; ++i) {
          if (auto val = stream_->read<Elf_Addr>()) {
            array.push_back(*val);
          } else {
            break;
          }
        }
      }
    } else {
      LIEF_WARN("The binary is not consistent. Found DT_FINI_ARRAY but missing "
                "DT_FINI_ARRAYSZ");
    }
  }

  // Check for PREINIT array
  // =======================
  if (DynamicEntry* dt_preini_array =
          binary_->get(DynamicEntry::TAG::PREINIT_ARRAY))
  {
    if (DynamicEntry* dt_preinit_arraysz =
            binary_->get(DynamicEntry::TAG::PREINIT_ARRAYSZ))
    {
      binary_->sizing_info_->preinit_array = dt_preinit_arraysz->value();
      std::vector<uint64_t>& array =
          dt_preini_array->as<DynamicEntryArray>()->array();

      const auto nb_functions =
          static_cast<uint32_t>(dt_preinit_arraysz->value() / sizeof(uint__));

      if (auto offset =
              binary_->virtual_address_to_offset(dt_preini_array->value()))
      {
        stream_->setpos(static_cast<Elf_Off>(*offset));
        for (size_t i = 0; i < nb_functions; ++i) {
          if (auto val = stream_->read<Elf_Addr>()) {
            array.push_back(*val);
          } else {
            break;
          }
        }
      }
    } else {
      LIEF_WARN("The binary is not consistent. Found DT_PREINIT_ARRAY but missing "
                "DT_PREINIT_ARRAYSZ");
    }
  }
  return ok();
}


template<typename ELF_T, typename REL_T>
ok_error_t Parser::parse_pltgot_relocations(uint64_t offset, uint64_t size) {
  static_assert(std::is_same_v<REL_T, typename ELF_T::Elf_Rel> ||
                    std::is_same_v<REL_T, typename ELF_T::Elf_Rela>,
                "REL_T must be Elf_Rel or Elf_Rela");
  using Elf_Off = typename ELF_T::Elf_Off;

  // Already Parsed
  if (binary_->pltgot_relocations().size() > 0) {
    return ok();
  }

  const Elf_Off offset_relocations = offset;

  auto nb_entries = static_cast<uint32_t>(size / sizeof(REL_T));

  nb_entries = std::min<uint32_t>(nb_entries, Parser::NB_MAX_RELOCATIONS);

  const ARCH arch = binary_->header_.machine_type();
  const Relocation::ENCODING enc = std::is_same_v<REL_T, typename ELF_T::Elf_Rel> ?
                                       Relocation::ENCODING::REL :
                                       Relocation::ENCODING::RELA;
  stream_->setpos(offset_relocations);
  for (uint32_t i = 0; i < nb_entries; ++i) {
    const auto rel_hdr = stream_->read<REL_T>();
    if (!rel_hdr) {
      break;
    }

    auto reloc = std::unique_ptr<Relocation>(
        new Relocation(std::move(*rel_hdr), Relocation::PURPOSE::PLTGOT, enc, arch)
    );
    bind_symbol(*reloc);
    insert_relocation(std::move(reloc));
  }
  return ok();
}

struct RelocationSetEq {
  bool operator()(const Relocation* lhs, const Relocation* rhs) const {
    bool check = lhs->address() == rhs->address() && lhs->type() == rhs->type() &&
                 lhs->addend() == rhs->addend() && lhs->info() == rhs->info() &&
                 lhs->has_symbol() == rhs->has_symbol();

    if (!check) {
      return false;
    }

    if (lhs->has_symbol()) { // The fact that rhs->has_symbol is checked previously
      return lhs->symbol()->name() == rhs->symbol()->name();
    }
    return check;
  }
};

struct RelocationSetHash {
  size_t operator()(const Relocation* reloc) const {
    Hash hasher;
    hasher.process(reloc->address())
        .process(reloc->type())
        .process(reloc->info())
        .process(reloc->addend());

    const Symbol* sym = reloc->symbol();
    if (sym != nullptr) {
      hasher.process(sym->name());
    }
    return hasher.value();
  }
};

template<typename ELF_T, typename REL_T>
ok_error_t Parser::parse_section_relocations(const Section& section) {
  using Elf_Rel = typename ELF_T::Elf_Rel;
  using Elf_Rela = typename ELF_T::Elf_Rela;

  static_assert(std::is_same_v<REL_T, Elf_Rel> || std::is_same_v<REL_T, Elf_Rela>,
                "REL_T must be Elf_Rel || Elf_Rela");

  // A relocation section can reference two other sections: a symbol table,
  // identified by the sh_link section header entry, and a section to modify,
  // identified by the sh_info
  // See Figure 4-12 in
  // https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html#sh_link
  Section* applies_to = nullptr;
  const size_t sh_info = section.information();
  if (sh_info > 0 && sh_info < binary_->sections_.size()) {
    applies_to = binary_->sections_[sh_info].get();
  }

  Section* symbol_table = nullptr;
  if (section.link() > 0 && section.link() < binary_->sections_.size()) {
    const size_t sh_link = section.link();
    symbol_table = binary_->sections_[sh_link].get();
  }

  constexpr uint8_t shift = ELF_T::r_info_shift;

  const ARCH arch = binary_->header_.machine_type();

  constexpr Relocation::ENCODING enc =
      std::is_same_v<REL_T, typename ELF_T::Elf_Rel> ? Relocation::ENCODING::REL :
                                                       Relocation::ENCODING::RELA;

  auto nb_entries = static_cast<uint32_t>(section.size() / sizeof(REL_T));
  nb_entries = std::min<uint32_t>(nb_entries, Parser::NB_MAX_RELOCATIONS);

  std::unordered_set<Relocation*, RelocationSetHash, RelocationSetEq> reloc_hash;

  SpanStream reloc_stream(section.content());
  reloc_stream.set_endian_swap(stream_->should_swap());

  const bool is_object_file =
      binary_->header().file_type() == Header::FILE_TYPE::REL &&
      binary_->segments_.empty();

  size_t count = 0;
  while (reloc_stream) {
    auto rel_hdr = reloc_stream.read<REL_T>();
    if (!rel_hdr) {
      LIEF_WARN("Can't parse relocation at offset: {:#06x} in {}",
                reloc_stream.pos(), section.name());
      break;
    }
    auto reloc = std::unique_ptr<Relocation>(
        new Relocation(*rel_hdr, Relocation::PURPOSE::NONE, enc, arch)
    );

    reloc->section_ = applies_to;
    reloc->symbol_table_ = symbol_table;

    if (is_object_file) {
      reloc->purpose(Relocation::PURPOSE::OBJECT);
    }

    const auto idx = static_cast<uint32_t>(rel_hdr->r_info >> shift);

    const bool is_from_dynsym =
        idx > 0 && idx < binary_->dynamic_symbols_.size() &&
        (symbol_table == nullptr || symbol_table->type() == Section::TYPE::DYNSYM);

    const bool is_from_symtab =
        idx < binary_->symtab_symbols_.size() &&
        (symbol_table == nullptr || symbol_table->type() == Section::TYPE::SYMTAB);
    if (is_from_dynsym) {
      reloc->symbol_ = binary_->dynamic_symbols_[idx].get();
    } else if (is_from_symtab) {
      reloc->symbol_ = binary_->symtab_symbols_[idx].get();
    }

    if (reloc_hash.insert(reloc.get()).second) {
      ++count;
      insert_relocation(std::move(reloc));
    }
  }
  LIEF_DEBUG("#{} relocations found in {}", count, section.name());
  return ok();
}

template<typename ELF_T>
ok_error_t Parser::parse_symbol_version_requirement(uint64_t offset,
                                                    uint32_t nb_entries) {
  using Elf_Verneed = typename ELF_T::Elf_Verneed;
  using Elf_Vernaux = typename ELF_T::Elf_Vernaux;

  LIEF_DEBUG("Parsing symbol version requirements");

  const uint64_t svr_offset = offset;

  LIEF_DEBUG("svr_offset: {:#x}", svr_offset);

  const uint64_t string_offset = get_dynamic_string_table();

  uint64_t next_symbol_offset = 0;

  for (size_t sym_idx = 0; sym_idx < nb_entries; ++sym_idx) {
    const auto header =
        stream_->peek<Elf_Verneed>(svr_offset + next_symbol_offset);
    if (!header) {
      break;
    }

    auto symbol_version_requirement =
        std::make_unique<SymbolVersionRequirement>(*header);
    if (string_offset != 0) {
      auto name = stream_->peek_string_at(string_offset + header->vn_file);
      if (name) {
        symbol_version_requirement->name(std::move(*name));
      }
    }

    const uint32_t nb_symbol_aux = header->vn_cnt;

    if (nb_symbol_aux > 0 && header->vn_aux > 0) {
      uint64_t next_aux_offset = 0;
      for (size_t j = 0; j < nb_symbol_aux; ++j) {
        const uint64_t aux_hdr_off =
            svr_offset + next_symbol_offset + header->vn_aux + next_aux_offset;

        const auto aux_header = stream_->peek<Elf_Vernaux>(aux_hdr_off);
        if (!aux_header) {
          break;
        }

        auto svar = std::make_unique<SymbolVersionAuxRequirement>(*aux_header);
        if (string_offset != 0) {
          auto name =
              stream_->peek_string_at(string_offset + aux_header->vna_name);
          if (name) {
            svar->name(std::move(*name));
          }
        }

        symbol_version_requirement->aux_requirements_.push_back(std::move(svar));
        if (aux_header->vna_next == 0) {
          break;
        }
        const uint64_t prev_aux_offset = next_aux_offset;
        next_aux_offset += aux_header->vna_next;
        if (next_aux_offset <= prev_aux_offset) {
          break;
        }
      }

      binary_->symbol_version_requirements_.push_back(
          std::move(symbol_version_requirement)
      );
    }

    if (header->vn_next == 0) {
      break;
    }
    const uint64_t prev_symbol_offset = next_symbol_offset;
    next_symbol_offset += header->vn_next;
    if (next_symbol_offset <= prev_symbol_offset) {
      break;
    }
  }


  // Associate Symbol Version with auxiliary symbol
  // Symbol version requirement is used to map
  // SymbolVersion::SymbolVersionAux <------> SymbolVersionAuxRequirement
  //
  // We mask the 15th (7FFF) bit because it indicates if this symbol is a hidden
  // one or not, but we don't care
  for (const std::unique_ptr<SymbolVersionRequirement>& svr :
       binary_->symbol_version_requirements_)
  {
    binary_->sizing_info_->verneed += sizeof(Elf_Verneed);
    for (std::unique_ptr<SymbolVersionAuxRequirement>& svar :
         svr->aux_requirements_)
    {
      binary_->sizing_info_->verneed += sizeof(Elf_Vernaux);
      for (const std::unique_ptr<SymbolVersion>& sv :
           binary_->symbol_version_table_)
      {
        if ((sv->value() & 0x7FFF) == svar->other()) {
          sv->symbol_aux_ = svar.get();
        }
      }
    }
  }
  return ok();
}

template<typename ELF_T>
ok_error_t Parser::parse_symbol_version_definition(uint64_t offset,
                                                   uint32_t nb_entries) {
  using Elf_Verdef = typename ELF_T::Elf_Verdef;
  using Elf_Verdaux = typename ELF_T::Elf_Verdaux;

  const uint64_t string_offset = get_dynamic_string_table();
  ScopedStream verdef_stream(*stream_, offset);
  uint64_t def_size = 0;

  for (size_t i = 0; i < nb_entries; ++i) {
    const auto svd_header = verdef_stream->peek<Elf_Verdef>();
    def_size =
        std::max(def_size, verdef_stream->pos() - offset + sizeof(Elf_Verdef));
    if (!svd_header) {
      break;
    }

    auto symbol_version_definition =
        std::make_unique<SymbolVersionDefinition>(*svd_header);
    uint32_t nb_aux_symbols = svd_header->vd_cnt;
    {
      ScopedStream aux_stream(*stream_, verdef_stream->pos() + svd_header->vd_aux);
      for (size_t j = 0; j < nb_aux_symbols; ++j) {
        const auto svda_header = aux_stream->peek<Elf_Verdaux>();
        def_size =
            std::max(def_size, aux_stream->pos() - offset + sizeof(Elf_Verdaux));
        if (!svda_header) {
          break;
        }

        if (string_offset != 0) {
          auto name =
              stream_->peek_string_at(string_offset + svda_header->vda_name);
          if (name) {
            symbol_version_definition->symbol_version_aux_.emplace_back(
                new SymbolVersionAux{std::move(*name)}
            );
          }
        }

        // Additional check
        if (svda_header->vda_next == 0) {
          break;
        }
        aux_stream->increment_pos(svda_header->vda_next);
      }
    }

    binary_->symbol_version_definition_.push_back(
        std::move(symbol_version_definition)
    );

    // Additional check
    if (svd_header->vd_next == 0) {
      break;
    }
    verdef_stream->increment_pos(svd_header->vd_next);
  }

  binary_->sizing_info_->verdef = def_size;

  // Associate Symbol Version with auxiliary symbol
  // We mask the 15th bit because it indicates if this symbol is a hidden one or
  // not, but we don't care
  for (std::unique_ptr<SymbolVersionDefinition>& svd :
       binary_->symbol_version_definition_)
  {
    for (std::unique_ptr<SymbolVersionAux>& sva : svd->symbol_version_aux_) {
      for (std::unique_ptr<SymbolVersion>& sv : binary_->symbol_version_table_) {
        if (svd->ndx() > 1 && (sv->value() & 0x7FFF) == svd->ndx() &&
            !sv->symbol_aux_)
        {
          sv->symbol_aux_ = sva.get();
        }
      }
    }
  }
  return ok();
}

}