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 "logging.hpp"

#include "LIEF/BinaryStream/BinaryStream.hpp"
#include "LIEF/BinaryStream/MemoryStream.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"
#include "LIEF/PE/LoadConfigurations.hpp"
#include "LIEF/PE/LoadConfigurations/CHPEMetadata.hpp"
#include "LIEF/PE/LoadConfigurations/LoadConfiguration.hpp"
#include "LIEF/PE/Parser.hpp"
#include "LIEF/PE/TLS.hpp"
#include "LIEF/PE/Binary.hpp"
#include "LIEF/PE/DataDirectory.hpp"
#include "LIEF/PE/Relocation.hpp"
#include "LIEF/PE/RelocationEntry.hpp"
#include "LIEF/PE/EnumToString.hpp"
#include "LIEF/PE/Section.hpp"
#include "LIEF/PE/ImportEntry.hpp"

#include "PE/Structures.hpp"

namespace LIEF::PE {

inline uint64_t delta(const Parser& parser) {
  assert(parser.bin().optional_header().imagebase() >= *parser.config().rebase);
  return parser.bin().optional_header().imagebase() - *parser.config().rebase;
}

bool inline warn_missing_section(const DataDirectory& dir) {
  return dir.type() != DataDirectory::TYPES::CERTIFICATE_TABLE &&
         dir.type() != DataDirectory::TYPES::BOUND_IMPORT;
}

template<typename PE_T>
ok_error_t Parser::parse() {
  if (!parse_headers<PE_T>()) {
    LIEF_WARN("Failed to parse regular PE headers");
    return make_error_code(lief_errors::parsing_error);
  }

  if (!parse_dos_stub()) {
    LIEF_WARN("Failed to parse DOS Stub");
  }

  if (!parse_rich_header()) {
    LIEF_WARN("Failed to parse rich header");
  }

  if (!parse_string_table()) {
    LIEF_WARN("Failed to parse COFF string table");
  }

  if (!parse_sections()) {
    LIEF_WARN("Failed to parse sections");
  }

  if (!parse_data_directories<PE_T>()) {
    LIEF_WARN("Failed to parse data directories");
  }


  if (!parse_load_config<PE_T>()) {
    LIEF_WARN("Failed to parse load config");
  }

  if (!parse_exceptions()) {
    LIEF_WARN("Failed to parse exception entries");
  }

  if (!parse_overlay()) {
    LIEF_WARN("Failed to parse overlay");
  }

  if (!parse_debug()) {
    LIEF_WARN("Failed to parse debug entries");
  }

  if (!parse_symbols()) {
    LIEF_WARN("Failed to parse symbols");
  }

  if (!parse_nested_relocated<PE_T>()) {
    LIEF_WARN("Nested PE ARM64X parsed with errors");
  }

  if (stream_->is_memory_view() && config_.rebase) {
    const uint64_t imagebase = binary_->optional_header().imagebase();
    if (imagebase < *config_.rebase) {
      LIEF_WARN("Can't revert the memory layout: the image base ({:#x}) is "
                "smaller than the rebase address ({:#x})",
                imagebase, *config_.rebase);
    } else {
      LIEF_DEBUG("Rebase delta: ({0:#010x})", delta(*this));
      undo_relocations<PE_T>();
      fix_iat<PE_T>();
      fix_tls<PE_T>();
      fix_load_config<PE_T>();

      binary_->optional_header().imagebase(*config_.rebase);
    }
  }

  return ok();
}

template<typename PE_T>
ok_error_t Parser::fix_load_config() {
  LoadConfiguration* lconf = binary_->load_configuration();
  if (lconf == nullptr) {
    return ok();
  }
  const uint64_t D = delta(*this);
  const uint64_t imagebase = binary_->optional_header().imagebase();

  if (uint64_t addr = lconf->security_cookie(); addr > 0 && addr >= imagebase) {
    lconf->security_cookie(addr - D);
  }

  if (uint64_t addr = lconf->lock_prefix_table(); addr > 0 && addr >= imagebase) {
    lconf->lock_prefix_table(addr - D);
  }

  if (uint64_t addr = lconf->se_handler_table().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->se_handler_table(addr - D);
  }

  if (uint64_t addr = lconf->guard_cf_check_function_pointer().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->guard_cf_check_function_pointer(addr - D);
  }

  if (uint64_t addr = lconf->guard_cf_dispatch_function_pointer().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->guard_cf_dispatch_function_pointer(addr - D);
  }

  if (uint64_t addr = lconf->guard_address_taken_iat_entry_table().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->guard_address_taken_iat_entry_table(addr - D);
  }

  if (uint64_t addr = lconf->guard_long_jump_target_table().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->guard_long_jump_target_table(addr - D);
  }

  if (uint64_t addr = lconf->dynamic_value_reloc_table().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->dynamic_value_reloc_table(addr - D);
  }

  if (uint64_t addr = lconf->hybrid_metadata_pointer().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->hybrid_metadata_pointer(addr - D);
  }

  if (uint64_t addr = lconf->guard_rf_failure_routine().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->guard_rf_failure_routine(addr - D);
  }

  if (uint64_t addr =
          lconf->guard_rf_failure_routine_function_pointer().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->guard_rf_failure_routine_function_pointer(addr - D);
  }

  if (uint64_t addr =
          lconf->guard_rf_verify_stackpointer_function_pointer().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->guard_rf_verify_stackpointer_function_pointer(addr - D);
  }

  if (uint64_t addr = lconf->enclave_configuration_ptr().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->enclave_configuration_ptr(addr - D);
  }

  if (uint64_t addr = lconf->volatile_metadata_pointer().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->volatile_metadata_pointer(addr - D);
  }

  if (uint64_t addr = lconf->guard_eh_continuation_table().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->guard_eh_continuation_table(addr - D);
  }

  if (uint64_t addr = lconf->guard_xfg_check_function_pointer().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->guard_xfg_check_function_pointer(addr - D);
  }

  if (uint64_t addr = lconf->guard_xfg_dispatch_function_pointer().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->guard_xfg_dispatch_function_pointer(addr - D);
  }

  if (uint64_t addr =
          lconf->guard_xfg_table_dispatch_function_pointer().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->guard_xfg_table_dispatch_function_pointer(addr - D);
  }

  if (uint64_t addr = lconf->cast_guard_os_determined_failure_mode().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->cast_guard_os_determined_failure_mode(addr - D);
  }

  if (uint64_t addr = lconf->guard_memcpy_function_pointer().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->guard_memcpy_function_pointer(addr - D);
  }

  if (uint64_t addr = lconf->uma_function_pointers().value_or(0);
      addr > 0 && addr >= imagebase)
  {
    lconf->uma_function_pointers(addr - D);
  }
  return ok();
}

template<typename PE_T>
ok_error_t Parser::fix_tls() {
  TLS* tls = binary_->tls();
  if (tls == nullptr) {
    return ok();
  }

  const uint64_t D = delta(*this);
  tls->addressof_index(tls->addressof_index() - D);
  tls->addressof_callbacks(tls->addressof_callbacks() - D);
  /* Address of raw data */ {
    const auto& [start, end] = tls->addressof_raw_data();
    tls->addressof_raw_data(std::make_pair(start - D, end - D));
  }
  /* Callbacks */ {
    std::vector<uint64_t> callbacks = tls->callbacks();
    for (size_t i = 0; i < callbacks.size(); ++i) {
      callbacks[i] -= D;
    }
    tls->callbacks(std::move(callbacks));
  }
  return ok();
}

template<typename PE_T>
ok_error_t Parser::fix_iat() {
  for (Import& imp : binary_->imports()) {
    for (ImportEntry& entry : imp.entries()) {
      entry.iat_value(entry.ilt_value());
    }
  }
  return ok();
}

template<typename PE_T>
ok_error_t Parser::undo_relocations() {
  const uint64_t D = delta(*this);
  for (const Relocation& R : binary_->relocations()) {
    for (const RelocationEntry& entry : R.entries()) {
      if (entry.type() == RelocationEntry::BASE_TYPES::ABS) {
        continue;
      }

      const uint64_t rva = entry.address();
      Section* sec = binary_->section_from_rva(rva);

      if (sec == nullptr) {
        continue;
      }

      span<uint8_t> buffer = sec->writable_content();
      const uint64_t rel_offset = rva - sec->virtual_address();

      switch (entry.type()) {
        case RelocationEntry::BASE_TYPES::DIR64:
        {
          if (rel_offset > buffer.size() ||
              buffer.size() - rel_offset < sizeof(uint64_t))
          {
            LIEF_WARN("Relocation at RVA {:#x} is out of the section's "
                      "content bounds",
                      rva);
            continue;
          }
          uint64_t value = 0;
          std::memcpy(&value, buffer.data() + rel_offset, sizeof(value));
          value -= D;
          std::memcpy(buffer.data() + rel_offset, &value, sizeof(value));
          break;
        }

        default:
          LIEF_WARN("Unsupported reloc type: {}", to_string(entry.type()));
          continue;
      }
    }
  }
  return ok();
}

template<typename PE_T>
ok_error_t Parser::parse_nested_relocated() {
  class RelocatedStream : public BinaryStream {
    public:
    RelocatedStream(Parser& parent) :
      parent_(&parent) {}

    ~RelocatedStream() override = default;

    RelocatedStream(const RelocatedStream&) = delete;
    RelocatedStream& operator=(const RelocatedStream&) = delete;

    RelocatedStream(RelocatedStream&& other) noexcept = default;
    RelocatedStream& operator=(RelocatedStream&& other) noexcept = default;

    uint64_t size() const override {
      return parent_->stream_->size();
    }

    ok_error_t peek_in(void* dst, uint64_t offset, uint64_t size,
                       uint64_t va = 0) const override {
      auto it_value = parent_->dyn_hdr_relocs_.lower_bound(offset);
      if (it_value == parent_->dyn_hdr_relocs_.end()) {
        return parent_->stream_->peek_in(dst, offset, size, va);
      }

      auto is_ok = parent_->stream_->peek_in(dst, offset, size, va);
      if (!is_ok) {
        return make_error_code(is_ok.error());
      }

      // Exact match
      if (it_value->first == offset && (offset + it_value->second.size) <= size) {
        const relocation_t& R = it_value->second;

        std::memcpy(dst, &R.value, R.size);
        return ok();
      }

      auto upper_bound = parent_->dyn_hdr_relocs_.upper_bound(offset + size);
      for (; it_value != upper_bound; ++it_value) {
        int64_t delta_offset = (int64_t)it_value->first - (int64_t)offset;
        assert(delta_offset >= 0);
        const relocation_t& R = it_value->second;

        if (delta_offset + R.size > size) {
          continue;
        }

        assert(delta_offset + R.size <= size);
        std::memcpy(reinterpret_cast<uint8_t*>(dst) + delta_offset, &R.value,
                    R.size);
      }
      return ok();
    }

    result<const void*> read_at(uint64_t /*offset*/, uint64_t /*size*/,
                                uint64_t /*va*/) const override {
      return make_error_code(lief_errors::not_supported);
    }

    private:
    Parser* parent_ = nullptr;
  };

  if (!config_.parse_arm64x_binary || dyn_hdr_relocs_.empty()) {
    return ok();
  }

  LIEF_DEBUG("Parsing nested binary");
  auto rstream = std::make_unique<RelocatedStream>(*this);
  config_.parse_arm64x_binary = false;
  std::unique_ptr<Binary> nested = parse(std::move(rstream), config_);
  config_.parse_arm64x_binary = true;

  if (nested == nullptr) {
    return make_error_code(lief_errors::parsing_error);
  }

  binary_->nested_ = std::move(nested);

  return ok();
}

template<typename PE_T>
ok_error_t Parser::parse_headers() {
  using pe_optional_header = typename PE_T::pe_optional_header;

  auto dos_hdr = stream_->peek<details::pe_dos_header>(0);
  if (!dos_hdr) {
    LIEF_ERR("Failed to read DOS header");
    return make_error_code(dos_hdr.error());
  }

  binary_->dos_header_ = *dos_hdr;
  const uint64_t addr_new_exe = binary_->dos_header().addressof_new_exeheader();

  {
    auto pe_header = stream_->peek<details::pe_header>(addr_new_exe);
    if (!pe_header) {
      LIEF_ERR("Failed to read PE header");
      return make_error_code(pe_header.error());
    }
    binary_->header_ = *pe_header;
  }

  {
    const uint64_t offset = addr_new_exe + sizeof(details::pe_header);
    auto opt_header = stream_->peek<pe_optional_header>(offset);
    if (!opt_header) {
      LIEF_ERR("Failed to read optional header");
      return make_error_code(opt_header.error());
    }
    binary_->optional_header_ = *opt_header;
  }

  return ok();
}

template<typename PE_T>
ok_error_t Parser::parse_data_directories() {
  using pe_optional_header = typename PE_T::pe_optional_header;
  const uint32_t directories_offset =
      binary_->dos_header().addressof_new_exeheader() +
      sizeof(details::pe_header) + sizeof(pe_optional_header);
  static constexpr auto DEFAULT_NB = DataDirectory::DEFAULT_NB;
  static constexpr auto MAX_DATA_DIRECTORIES = 30;
  binary_->data_directories_.reserve(DEFAULT_NB);

  stream_->setpos(directories_offset);
  // WARNING: The PE specifications require that the data directory table ends
  // with a null entry (RVA / Size, set to 0).
  //
  // Nevertheless it seems that this requirement is not enforced by the PE loader.
  // The binary bc203f2b6a928f1457e9ca99456747bcb7adbbfff789d1c47e9479aac11598af
  // contains a non-null final data directory (watermarking?)
  uint32_t nb_dir = binary_->optional_header().numberof_rva_and_size();
  if (nb_dir > MAX_DATA_DIRECTORIES) {
    LIEF_WARN("The number of data directories ({}) is larger than the maximum "
              "value ({}). The extra entries are ignored",
              nb_dir, MAX_DATA_DIRECTORIES);
    nb_dir = MAX_DATA_DIRECTORIES;
  }
  for (size_t i = 0; i < nb_dir; ++i) {
    auto raw_dir = stream_->read<details::pe_data_directory>();
    if (!raw_dir) {
      LIEF_ERR("Failed to read data directory #{}", i);
      return make_error_code(lief_errors::read_error);
    }
    const auto dir_type = DataDirectory::TYPES(i);
    auto directory = std::make_unique<DataDirectory>(*raw_dir, dir_type);

    if (directory->RVA() > 0) {
      const uint64_t offset = binary_->rva_to_offset(directory->RVA());
      directory->section_ = binary_->section_from_offset(offset);
      if (directory->section_ == nullptr && warn_missing_section(*directory)) {
        LIEF_WARN("Section not found for {}", to_string(dir_type));
      }
    }
    binary_->data_directories_.push_back(std::move(directory));
  }

  // Import Table
  if (DataDirectory* import_data_dir = binary_->import_dir()) {
    if (import_data_dir->RVA() > 0 && config_.parse_imports) {
      LIEF_DEBUG("Processing Import Table");
      parse_import_table<PE_T>();
    }
  }

  // Exports
  if (const DataDirectory* export_dir = binary_->export_dir()) {
    if (export_dir->RVA() > 0 && config_.parse_exports) {
      LIEF_DEBUG("Parsing Exports");
      parse_exports();
    }
  }

  // Signature
  if (const DataDirectory* dir = binary_->cert_dir()) {
    if (dir->RVA() > 0 && config_.parse_signature) {
      parse_signature();
    }
  }

  if (DataDirectory* dir = binary_->tls_dir()) {
    if (dir->RVA() > 0) {
      parse_tls<PE_T>();
    }
  }

  if (DataDirectory* dir = binary_->relocation_dir()) {
    if (dir->RVA() > 0 && config_.parse_reloc) {
      LIEF_DEBUG("Parsing Relocations");
      parse_relocations();
    }
  }

  if (DataDirectory* dir = binary_->rsrc_dir()) {
    if (dir->RVA() > 0 && config_.parse_rsrc) {
      LIEF_DEBUG("Parsing Resources");
      parse_resources();
    }
  }

  if (DataDirectory* dir = binary_->delay_dir()) {
    if (dir->RVA() > 0) {
      auto is_ok = parse_delay_imports<PE_T>();
      if (!is_ok) {
        LIEF_WARN("Delay imports parsing failed or incomplete ('{}')",
                  to_string(get_error(is_ok)));
      }
    }
  }

  return ok();
}

template<typename PE_T>
ok_error_t Parser::parse_import_table() {
  using uint = typename PE_T::uint;
  DataDirectory* import_dir = binary_->import_dir();
  DataDirectory* iat_dir = binary_->iat_dir();

  if (import_dir == nullptr || iat_dir == nullptr) {
    return make_error_code(lief_errors::not_found);
  }


  const uint32_t import_rva = import_dir->RVA();
  const uint64_t import_offset = binary_->rva_to_offset(import_rva);
  const size_t import_end = import_offset + import_dir->size();

  uint64_t last_imp_offset = 0;

  stream_->setpos(import_offset);
  result<details::pe_import> imp_res;

  while (stream_->pos() < import_end &&
         (imp_res = stream_->read<details::pe_import>()))
  {
    const auto raw_imp = *imp_res;
    if (BinaryStream::is_all_zero(raw_imp)) {
      break;
    }

    auto import = std::make_unique<Import>(raw_imp);
    import->directory_ = import_dir;
    import->iat_directory_ = iat_dir;
    import->type_ = type_;

    if (import->name_rva_ == 0) {
      LIEF_DEBUG("Name RVA is null");
      break;
    }

    // Offset to the Import (Library) name
    const uint64_t offset_name = binary_->rva_to_offset(import->name_rva_);

    if (auto res_name = stream_->peek_string_at(offset_name)) {
      import->name_ = std::move(*res_name);
    } else {
      LIEF_ERR("Failed to read import name (offset: {:#x})", offset_name);
      continue;
    }


    // We assume that a DLL name should be at least 4 characters long and printable
    const std::string& imp_name = import->name();
    if (!is_valid_dll_name(imp_name)) {
      if (!imp_name.empty()) {
        LIEF_WARN("Invalid import name '{}', discarding", imp_name);
        continue;
      }
      continue; // skip
    }

    last_imp_offset =
        std::max<uint64_t>(last_imp_offset, offset_name + imp_name.size() + 1);

    // Offset to import lookup table
    uint64_t LT_offset =
        import->import_lookup_table_rva() > 0 ?
            binary_->rva_to_offset(import->import_lookup_table_rva()) :
            0;


    // Offset to the import address table
    uint64_t IAT_offset =
        import->import_address_table_rva() > 0 ?
            binary_->rva_to_offset(import->import_address_table_rva()) :
            0;
    LIEF_DEBUG("IAT Offset: {:#010x}", IAT_offset);
    LIEF_DEBUG("IAT RVA:    {:#010x}", import->import_address_table_rva());
    LIEF_DEBUG("ILT Offset: {:#010x}", LT_offset);
    LIEF_DEBUG("ILT RVA:    {:#010x}", import->import_lookup_table_rva());

    uint IAT = 0;
    uint table = 0;

    if (IAT_offset > 0) {
      if (auto res_iat = stream_->peek<uint>(IAT_offset)) {
        IAT = *res_iat;
        table = IAT;
        IAT_offset += sizeof(uint);
      }
    }

    if (LT_offset > 0) {
      if (auto res_lt = stream_->peek<uint>(LT_offset)) {
        table = *res_lt;
        LT_offset += sizeof(uint);
      }
    }

    size_t idx = 0;

    while ((table != 0 || IAT != 0) && idx < Parser::MAX_IMPORT_ENTRIES) {
      auto entry = std::make_unique<ImportEntry>();
      entry->iat_value_ = IAT;
      entry->ilt_value_ = table;
      entry->data_ =
          table > 0 ? table : IAT; // In some cases, ILT can be corrupted
      entry->type_ = type_;
      entry->rva_ = import->iat_rva_ + sizeof(uint) * (idx++);

      LIEF_DEBUG("IAT: {:#010x} | ILT: {:#010x}", IAT, table);

      if (!entry->is_ordinal()) {
        const size_t hint_off = binary_->rva_to_offset(entry->hint_name_rva());
        const size_t name_off = hint_off + sizeof(uint16_t);
        if (auto entry_name =
                stream_->peek_string_at(name_off, MAX_IMPORT_NAME_SIZE))
        {
          entry->name_ = std::move(*entry_name);
        } else {
          LIEF_ERR("Failed to read import entry name");
        }
        if (auto hint = stream_->peek<uint16_t>(hint_off)) {
          entry->hint_ = *hint;
        } else {
          LIEF_INFO("Failed to read hint value @{:#x}", hint_off);
        }

        last_imp_offset = std::max<uint64_t>(last_imp_offset,
                                             name_off + entry->name().size() + 1);

        // Check that the import name is valid
        if (is_valid_import_name(entry->name())) {
          import->entries_.push_back(std::move(entry));
        } else if (!entry->name().empty()) {
          LIEF_INFO("Invalid import name '{}', discarding", entry->name());
        }
      } else {
        import->entries_.push_back(std::move(entry));
      }

      if (IAT_offset > 0) {
        if (auto iat = stream_->peek<uint>(IAT_offset)) {
          IAT = *iat;
          IAT_offset += sizeof(uint);
        } else {
          LIEF_ERR("Failed to read IAT value at {:#x}", IAT_offset);
          IAT = 0;
        }
      } else {
        IAT = 0;
      }

      if (LT_offset > 0) {
        last_imp_offset = std::max<uint64_t>(last_imp_offset, LT_offset);
        if (auto lt = stream_->peek<uint>(LT_offset)) {
          table = *lt;
          LT_offset += sizeof(uint);
        } else {
          LIEF_ERR("Failed to read lookup table value at {:#x}", LT_offset);
          table = 0;
        }
      } else {
        table = 0;
      }
    }

    if (idx >= MAX_IMPORT_ENTRIES) {
      LIEF_WARN(
          "Import '{}' exceeded max entries ({}), IAT may lack null terminator",
          import->name(), MAX_IMPORT_ENTRIES
      );
    }

    import->nb_original_func_ = import->entries_.size();
    binary_->imports_.push_back(std::move(import));
  }

  return ok();
}


template<class PE_T>
ok_error_t Parser::parse_delay_names_table(DelayImport& import,
                                           uint32_t names_offset,
                                           uint32_t iat_offset) {
  using ptr_t = typename PE_T::uint;
  ScopedStream nstream(*stream_, names_offset);

  auto entry_val = nstream->read<ptr_t>();
  if (!entry_val) {
    LIEF_ERR("Failed to read delay_imports.names_table[0]");
    return make_error_code(entry_val.error());
  }

  while (*nstream && entry_val && *entry_val != 0) {
    // Index of the current entry (-1 as we start with a read())
    const size_t index = (nstream->pos() - names_offset) / sizeof(ptr_t) - 1;

    if (index >= MAX_IMPORT_ENTRIES) {
      LIEF_WARN("Delay import '{}' exceeded the max number of entries ({})",
                import.name(), MAX_IMPORT_ENTRIES);
      break;
    }

    auto entry = std::make_unique<DelayImportEntry>(*entry_val, type_);
    const uint32_t iat_pos = index * sizeof(ptr_t);

    if (auto iat_value = stream_->peek<ptr_t>(iat_offset + iat_pos)) {
      entry->value_ = import.iat() + iat_pos; // Symbol's value for the base class
      entry->iat_value_ = *iat_value;
      LIEF_DEBUG("  [{}].iat : {:#012x}", index, entry->iat_value_);
    }

    if (!entry->is_ordinal()) {
      uint64_t hint_off = binary_->rva_to_offset(entry->hint_name_rva());
      const uint64_t name_off = hint_off + sizeof(uint16_t);

      if (auto entry_name =
              stream_->peek_string_at(name_off, MAX_IMPORT_NAME_SIZE))
      {
        entry->name_ = std::move(*entry_name);
      }

      if (auto hint = stream_->peek<uint16_t>(hint_off)) {
        entry->hint_ = *hint;
      }

      if (Parser::is_valid_import_name(entry->name())) {
        import.entries_.push_back(std::move(entry));
      }
    } else /* is ordinal */ {
      import.entries_.push_back(std::move(entry));
    }
    entry_val = nstream->read<ptr_t>();
    if (!entry_val) {
      break;
    }
  }
  return ok();
}

template<typename PE_T>
ok_error_t Parser::parse_delay_imports() {
  LIEF_DEBUG("Parsing Delay Import Table");

  const DataDirectory* dir = binary_->delay_dir();
  if (dir == nullptr) {
    return make_error_code(lief_errors::not_found);
  }

  if (dir->RVA() == 0 || dir->size() == 0) {
    return ok();
  }

  std::unique_ptr<SpanStream> stream = dir->stream();
  if (!stream) {
    return make_error_code(lief_errors::read_error);
  }

  while (stream) {
    auto import = stream->read<details::delay_imports>();
    if (!import) {
      LIEF_DEBUG("Failed to read delay import at {}:{}", __FUNCTION__, __LINE__);
      return make_error_code(import.error());
    }

    auto imp = std::make_unique<DelayImport>(*import, type_);

    if (BinaryStream::is_all_zero(*import)) {
      return ok();
    }

    uint64_t name_offset = binary_->rva_to_offset(import->name);
    auto dll_name = stream_->peek_string_at(name_offset);

    if (dll_name && !dll_name->empty() && is_valid_dll_name(*dll_name)) {
      imp->name_ = std::move(*dll_name);
    }

    LIEF_DEBUG("  delay_imports.name:       {}", imp->name_);
    LIEF_DEBUG("  delay_imports.attribute:  {}", import->attribute);
    LIEF_DEBUG("  delay_imports.handle:     {:#06x}", import->handle);
    LIEF_DEBUG("  delay_imports.iat:        {:#06x}", import->iat);
    LIEF_DEBUG("  delay_imports.name_table: {:#06x}", import->name_table);
    LIEF_DEBUG("  delay_imports.bound_iat:  {:#06x}", import->bound_iat);
    LIEF_DEBUG("  delay_imports.unload_iat: {:#06x}", import->unload_iat);
    LIEF_DEBUG("  delay_imports.timestamp:  {:#06x}", import->timestamp);

    // Offset to Delay Import Name Table
    uint64_t names_offset =
        import->name_table > 0 ? binary_->rva_to_offset(import->name_table) : 0;

    // Offset to the import address table
    uint64_t IAT_offset =
        import->iat > 0 ? binary_->rva_to_offset(import->iat) : 0;

    LIEF_DEBUG("  [IAT  ]: {:#06x}", IAT_offset);
    LIEF_DEBUG("  [Names]: {:#06x}", names_offset);

    if (names_offset > 0) {
      auto is_ok = parse_delay_names_table<PE_T>(*imp, names_offset, IAT_offset);
      if (!is_ok) {
        LIEF_WARN("Delay imports names table parsed with errors ('{}')",
                  to_string(get_error(is_ok)));
      }
    }

    binary_->delay_imports_.push_back(std::move(imp));
  }

  return ok();
}


template<typename PE_T>
ok_error_t Parser::parse_tls() {
  using pe_tls = typename PE_T::pe_tls;
  using uint = typename PE_T::uint;

  LIEF_DEBUG("Parsing TLS");

  DataDirectory* tls_dir = binary_->tls_dir();
  if (tls_dir == nullptr) {
    return make_error_code(lief_errors::not_found);
  }
  const uint32_t tls_rva = tls_dir->RVA();
  const uint64_t offset = binary_->rva_to_offset(tls_rva);

  stream_->setpos(offset);

  const auto tls_header = stream_->read<pe_tls>();

  if (!tls_header) {
    return make_error_code(lief_errors::read_error);
  }

  auto tls = std::make_unique<TLS>(*tls_header);

  const uint64_t imagebase = binary_->optional_header().imagebase();

  if (tls_header->RawDataStartVA >= imagebase &&
      tls_header->RawDataEndVA > tls_header->RawDataStartVA)
  {
    const uint64_t start_data_rva = tls_header->RawDataStartVA - imagebase;
    const uint64_t stop_data_rva = tls_header->RawDataEndVA - imagebase;

    const uint start_template_offset = binary_->rva_to_offset(start_data_rva);
    const uint end_template_offset = binary_->rva_to_offset(stop_data_rva);

    const size_t size_to_read = end_template_offset - start_template_offset;

    if (size_to_read > Parser::MAX_DATA_SIZE) {
      LIEF_DEBUG("TLS template is too large");
    } else {
      if (!stream_->peek_data(tls->data_template_, start_template_offset,
                              size_to_read))
      {
        LIEF_WARN("TLS template corrupted");
      }
    }
  }

  if (tls->addressof_callbacks() > imagebase) {
    uint64_t callbacks_offset =
        binary_->rva_to_offset(tls->addressof_callbacks() - imagebase);
    stream_->setpos(callbacks_offset);
    size_t count = 0;
    while (count++ < Parser::MAX_TLS_CALLBACKS) {
      auto res_callback_rva = stream_->read<uint>();
      if (!res_callback_rva) {
        break;
      }

      auto callback_rva = *res_callback_rva;

      if (static_cast<uint32_t>(callback_rva) == 0) {
        break;
      }
      tls->callbacks_.push_back(callback_rva);
    }
  }

  tls->directory_ = tls_dir;
  tls->section_ = tls_dir->section();
  binary_->sizing_info_.nb_tls_callbacks = tls->callbacks_.size();

  binary_->tls_ = std::move(tls);
  return ok();
}

template<typename PE_T>
ok_error_t Parser::parse_load_config() {
  const DataDirectory* lconf_dir = bin().load_config_dir();
  if (lconf_dir == nullptr) {
    return ok();
  }

  if (lconf_dir->RVA() == 0 || lconf_dir->size() == 0) {
    return ok();
  }

  std::unique_ptr<SpanStream> stream = lconf_dir->stream(/*sized=*/false);
  if (stream == nullptr) {
    return make_error_code(lief_errors::read_error);
  }

  auto config = LoadConfiguration::parse<PE_T>(*this, *stream);
  if (config == nullptr) {
    return make_error_code(lief_errors::read_error);
  }

  if (!process_load_config<PE_T>(*config)) {
    LIEF_DEBUG("Load configuration processing finished with errors");
  }

  binary_->sizing_info_.load_config_size = config->size();
  binary_->loadconfig_ = std::move(config);

  return ok();
}

template<typename PE_T>
ok_error_t Parser::process_load_config(LoadConfiguration& lconf) {
  if (uint64_t addr = lconf.se_handler_table().value_or((0)); addr > 0) {
    uint64_t offset = bin().va_to_offset(addr);
    ScopedStream stream(*stream_, offset);
    if (!LoadConfiguration::parse_seh_table(*this, *stream, lconf)) {
      LIEF_WARN("SEH table processing finished with errors");
    }
  }

  if (uint64_t addr = lconf.guard_cf_function_table().value_or((0)); addr > 0) {
    uint64_t offset = bin().va_to_offset(addr);
    ScopedStream stream(*stream_, offset);
    auto dst = &LoadConfiguration::guard_cf_functions_;
    const size_t count = lconf.guard_cf_function_count().value_or(0);

    auto is_ok = LoadConfiguration::parse_guard_functions(*this, *stream, lconf,
                                                          count, dst);
    if (!is_ok) {
      LIEF_WARN("Guard CF table processing finished with errors");
    }
  }

  if (uint64_t addr = lconf.guard_address_taken_iat_entry_table().value_or((0));
      addr > 0)
  {
    uint64_t offset = bin().va_to_offset(addr);
    ScopedStream stream(*stream_, offset);
    auto dst = &LoadConfiguration::guard_address_taken_iat_entries_;
    const size_t count = lconf.guard_address_taken_iat_entry_count().value_or(0);

    auto is_ok = LoadConfiguration::parse_guard_functions(*this, *stream, lconf,
                                                          count, dst);
    if (!is_ok) {
      LIEF_WARN("Guard CF Address Taken IAT Entry Table processing finished "
                "with errors");
    }
  }

  if (uint64_t addr = lconf.guard_long_jump_target_table().value_or((0)); addr > 0)
  {
    uint64_t offset = bin().va_to_offset(addr);
    ScopedStream stream(*stream_, offset);
    auto dst = &LoadConfiguration::guard_long_jump_targets_;
    const size_t count = lconf.guard_long_jump_target_count().value_or(0);

    auto is_ok = LoadConfiguration::parse_guard_functions(*this, *stream, lconf,
                                                          count, dst);

    if (!is_ok) {
      LIEF_WARN("Guard CF long jump target processing finished with errors");
    }
  }

  if (uint64_t addr = lconf.guard_eh_continuation_table().value_or((0)); addr > 0)
  {
    uint64_t offset = bin().va_to_offset(addr);
    ScopedStream stream(*stream_, offset);
    auto dst = &LoadConfiguration::guard_eh_continuation_functions_;
    const size_t count = lconf.guard_eh_continuation_count().value_or(0);

    auto is_ok = LoadConfiguration::parse_guard_functions(*this, *stream, lconf,
                                                          count, dst);

    if (!is_ok) {
      LIEF_WARN("Guard EH continuation function processing finished with errors");
    }
  }

  if (uint64_t addr = lconf.hybrid_metadata_pointer().value_or((0)); addr > 0) {
    uint64_t offset = bin().va_to_offset(addr);
    ScopedStream stream(*stream_, offset);
    auto metadata = CHPEMetadata::parse(*this, *stream);
    if (metadata != nullptr) {
      lconf.chpe_ = std::move(metadata);
    }
  }

  if (!LoadConfiguration::parse_dyn_relocs(*this, lconf)) {
    LIEF_WARN("Dynamic relocation processing finished with errors");
  }

  if (!LoadConfiguration::parse_enclave_config<PE_T>(*this, lconf)) {
    LIEF_WARN("Errors while processing enclave configuration");
  }

  if (!LoadConfiguration::parse_volatile_metadata(*this, lconf)) {
    LIEF_WARN("Errors while processing volatile metadata");
  }

  return ok();
}

}