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 <sstream>

#include <spdlog/fmt/fmt.h>

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

#include "LIEF/PE/Parser.hpp"
#include "LIEF/PE/Binary.hpp"
#include "LIEF/PE/Section.hpp"
#include "LIEF/PE/LoadConfigurations/LoadConfiguration.hpp"
#include "LIEF/PE/LoadConfigurations/VolatileMetadata.hpp"
#include "LIEF/PE/LoadConfigurations/EnclaveConfiguration.hpp"
#include "LIEF/PE/LoadConfigurations/DynamicRelocation/DynamicRelocationV1.hpp"
#include "LIEF/PE/LoadConfigurations/CHPEMetadata/Metadata.hpp"

#include "PE/Structures.hpp"

#include "overload_cast.hpp"
#include "logging.hpp"
#include "frozen.hpp"
#include "internal_utils.hpp"
#include "fmt_formatter.hpp"

FMT_FORMATTER(LIEF::PE::LoadConfiguration::IMAGE_GUARD, LIEF::PE::to_string);


namespace LIEF::PE {

LoadConfiguration::LoadConfiguration() = default;
LoadConfiguration::LoadConfiguration(LoadConfiguration&&) = default;
LoadConfiguration& LoadConfiguration::operator=(LoadConfiguration&&) = default;
LoadConfiguration::~LoadConfiguration() = default;

static constexpr std::array IMAGE_GUARD_LIST = {
    LoadConfiguration::IMAGE_GUARD::NONE,
    LoadConfiguration::IMAGE_GUARD::CF_INSTRUMENTED,
    LoadConfiguration::IMAGE_GUARD::CFW_INSTRUMENTED,
    LoadConfiguration::IMAGE_GUARD::CF_FUNCTION_TABLE_PRESENT,
    LoadConfiguration::IMAGE_GUARD::SECURITY_COOKIE_UNUSED,
    LoadConfiguration::IMAGE_GUARD::PROTECT_DELAYLOAD_IAT,
    LoadConfiguration::IMAGE_GUARD::DELAYLOAD_IAT_IN_ITS_OWN_SECTION,
    LoadConfiguration::IMAGE_GUARD::CF_EXPORT_SUPPRESSION_INFO_PRESENT,
    LoadConfiguration::IMAGE_GUARD::CF_ENABLE_EXPORT_SUPPRESSION,
    LoadConfiguration::IMAGE_GUARD::CF_LONGJUMP_TABLE_PRESENT,
    LoadConfiguration::IMAGE_GUARD::RF_INSTRUMENTED,
    LoadConfiguration::IMAGE_GUARD::RF_ENABLE,
    LoadConfiguration::IMAGE_GUARD::RF_STRICT,
    LoadConfiguration::IMAGE_GUARD::RETPOLINE_PRESENT,
    LoadConfiguration::IMAGE_GUARD::EH_CONTINUATION_TABLE_PRESENT,
};

inline bool should_quit(uint64_t start, BinaryStream& stream, uint32_t size) {
  return (stream.pos() - start) >= size;
}

template<class T, class Func>
inline bool read_lc(Func f, LoadConfiguration* thiz, BinaryStream& stream,
                    uint32_t start, uint32_t size) {
  auto value = stream.read<T>();
  if (!value) {
    return false;
  }

  std::mem_fn(f)(thiz, *value);

  return !should_quit(start, stream, size);
}

template<class PE_T>
std::unique_ptr<LoadConfiguration> LoadConfiguration::parse(Parser& ctx,
                                                            BinaryStream& stream) {
  using ptr_t = typename PE_T::uint;

  const uint64_t start_off = stream.pos();

  auto Characteristics = stream.read<uint32_t>();
  if (!Characteristics) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto TimeDateStamp = stream.read<uint32_t>();
  if (!TimeDateStamp) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto MajorVersion = stream.read<uint16_t>();
  if (!MajorVersion) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto MinorVersion = stream.read<uint16_t>();
  if (!MinorVersion) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto GlobalFlagsClear = stream.read<uint32_t>();
  if (!GlobalFlagsClear) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto GlobalFlagsSet = stream.read<uint32_t>();
  if (!GlobalFlagsSet) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto CriticalSectionDefaultTimeout = stream.read<uint32_t>();
  if (!CriticalSectionDefaultTimeout) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto DeCommitFreeBlockThreshold = stream.read<ptr_t>();
  if (!DeCommitFreeBlockThreshold) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto DeCommitTotalFreeThreshold = stream.read<ptr_t>();
  if (!DeCommitTotalFreeThreshold) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto LockPrefixTable = stream.read<ptr_t>();
  if (!LockPrefixTable) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto MaximumAllocationSize = stream.read<ptr_t>();
  if (!MaximumAllocationSize) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto VirtualMemoryThreshold = stream.read<ptr_t>();
  if (!VirtualMemoryThreshold) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto ProcessAffinityMask = stream.read<ptr_t>();
  if (!ProcessAffinityMask) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto ProcessHeapFlags = stream.read<uint32_t>();
  if (!ProcessHeapFlags) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto CSDVersion = stream.read<uint16_t>();
  if (!CSDVersion) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto DependentLoadFlags = stream.read<uint16_t>();
  if (!DependentLoadFlags) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto EditList = stream.read<ptr_t>();
  if (!EditList) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto SecurityCookie = stream.read<ptr_t>();
  if (!SecurityCookie) {
    LIEF_DEBUG("LoadConfiguration error (line={})", __LINE__);
    return nullptr;
  }

  auto config = std::make_unique<LoadConfiguration>();

  (*config)
      .characteristics(*Characteristics)
      .timedatestamp(*TimeDateStamp)
      .major_version(*MajorVersion)
      .minor_version(*MinorVersion)
      .global_flags_clear(*GlobalFlagsClear)
      .global_flags_set(*GlobalFlagsSet)
      .critical_section_default_timeout(*CriticalSectionDefaultTimeout)
      .decommit_free_block_threshold(*DeCommitFreeBlockThreshold)
      .decommit_total_free_threshold(*DeCommitTotalFreeThreshold)
      .lock_prefix_table(*LockPrefixTable)
      .maximum_allocation_size(*MaximumAllocationSize)
      .virtual_memory_threshold(*VirtualMemoryThreshold)
      .process_affinity_mask(*ProcessAffinityMask)
      .process_heap_flags(*ProcessHeapFlags)
      .csd_version(*CSDVersion)
      .dependent_load_flags(*DependentLoadFlags)
      .editlist(*EditList)
      .security_cookie(*SecurityCookie);

  if (should_quit(start_off, stream, *Characteristics)) {
    return config;
  }

  /* V0 */ {
    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(&LoadConfiguration::se_handler_table),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }

    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(&LoadConfiguration::se_handler_count),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }
  }

  /* V1 */ {
    if (!read_lc<ptr_t>(overload_cast<uint64_t>(
                            &LoadConfiguration::guard_cf_check_function_pointer
                        ),
                        config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }

    if (!read_lc<ptr_t>(overload_cast<uint64_t>(
                            &LoadConfiguration::guard_cf_dispatch_function_pointer
                        ),
                        config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }

    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(&LoadConfiguration::guard_cf_function_table),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }

    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(&LoadConfiguration::guard_cf_function_count),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }

    if (!read_lc<uint32_t>(
            overload_cast<uint32_t>(&LoadConfiguration::guard_flags), config.get(),
            stream, start_off, *Characteristics
        ))
    {
      return config;
    }
  }

  /* V2 */ {
    auto code_integrity = CodeIntegrity::parse(ctx, stream);
    if (!code_integrity) {
      return config;
    }

    config->code_integrity(std::move(*code_integrity));

    if (should_quit(start_off, stream, *Characteristics)) {
      return config;
    }
  }

  /* V3 */ {
    if (!read_lc<ptr_t>(overload_cast<uint64_t>(
                            &LoadConfiguration::guard_address_taken_iat_entry_table
                        ),
                        config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }

    if (!read_lc<ptr_t>(overload_cast<uint64_t>(
                            &LoadConfiguration::guard_address_taken_iat_entry_count
                        ),
                        config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }

    if (!read_lc<ptr_t>(overload_cast<uint64_t>(
                            &LoadConfiguration::guard_long_jump_target_table
                        ),
                        config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }

    if (!read_lc<ptr_t>(overload_cast<uint64_t>(
                            &LoadConfiguration::guard_long_jump_target_count
                        ),
                        config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }
  }

  /* V4 */ {
    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(&LoadConfiguration::dynamic_value_reloc_table),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }

    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(&LoadConfiguration::hybrid_metadata_pointer),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }
  }

  /* V5 */ {
    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(&LoadConfiguration::guard_rf_failure_routine),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }

    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(
                &LoadConfiguration::guard_rf_failure_routine_function_pointer
            ),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }

    if (!read_lc<uint32_t>(overload_cast<uint32_t>(
                               &LoadConfiguration::dynamic_value_reloctable_offset
                           ),
                           config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }

    if (!read_lc<uint16_t>(overload_cast<uint16_t>(
                               &LoadConfiguration::dynamic_value_reloctable_section
                           ),
                           config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }

    if (!read_lc<uint16_t>(overload_cast<uint16_t>(&LoadConfiguration::reserved2),
                           config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }
  }

  /* V6 */ {
    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(
                &LoadConfiguration::guard_rf_verify_stackpointer_function_pointer
            ),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }

    if (!read_lc<uint32_t>(
            overload_cast<uint32_t>(&LoadConfiguration::hotpatch_table_offset),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }
  }

  /* V7 */ {
    if (!read_lc<uint32_t>(overload_cast<uint32_t>(&LoadConfiguration::reserved3),
                           config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }

    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(&LoadConfiguration::enclave_configuration_ptr),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }
  }

  /* V8 */ {
    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(&LoadConfiguration::volatile_metadata_pointer),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }
  }

  /* V9 */ {
    if (!read_lc<ptr_t>(overload_cast<uint64_t>(
                            &LoadConfiguration::guard_eh_continuation_table
                        ),
                        config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }

    if (!read_lc<ptr_t>(overload_cast<uint64_t>(
                            &LoadConfiguration::guard_eh_continuation_count
                        ),
                        config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }
  }

  /* V10 */ {
    if (!read_lc<ptr_t>(overload_cast<uint64_t>(
                            &LoadConfiguration::guard_xfg_check_function_pointer
                        ),
                        config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }

    if (!read_lc<ptr_t>(overload_cast<uint64_t>(
                            &LoadConfiguration::guard_xfg_dispatch_function_pointer
                        ),
                        config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }

    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(
                &LoadConfiguration::guard_xfg_table_dispatch_function_pointer
            ),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }
  }

  /* V11 */ {
    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(
                &LoadConfiguration::cast_guard_os_determined_failure_mode
            ),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }
  }

  /* V12 */ {
    if (!read_lc<ptr_t>(overload_cast<uint64_t>(
                            &LoadConfiguration::guard_memcpy_function_pointer
                        ),
                        config.get(), stream, start_off, *Characteristics))
    {
      return config;
    }
  }

  /* V13 */ {
    if (!read_lc<ptr_t>(
            overload_cast<uint64_t>(&LoadConfiguration::uma_function_pointers),
            config.get(), stream, start_off, *Characteristics
        ))
    {
      return config;
    }
  }
  return config;
}

ok_error_t LoadConfiguration::parse_seh_table(Parser& /*ctx*/,
                                              BinaryStream& stream,
                                              LoadConfiguration& config) {
  static constexpr auto MAX_RESERVE = 1000;
  const uint64_t count = config.se_handler_count().value_or(0);
  if (count == 0) {
    return ok();
  }

  const uint64_t available =
      stream.size() - std::min<uint64_t>(stream.pos(), stream.size());

  const uint64_t nb = std::min<uint64_t>(count, available / sizeof(uint32_t));

  LIEF_DEBUG("SEH Table parsing (#{}, bounded to #{})", count, nb);

  config.seh_rva_.reserve(std::min<uint64_t>(MAX_RESERVE, nb));
  for (size_t i = 0; i < nb; ++i) {
    auto rva = stream.read<uint32_t>();
    if (!rva) {
      return make_error_code(rva.error());
    }

    config.seh_rva_.push_back(*rva);
  }

  return ok();
}

ok_error_t LoadConfiguration::parse_guard_functions(
    Parser& /*ctx*/, BinaryStream& stream, LoadConfiguration& config, size_t count,
    guard_functions_t LoadConfiguration::* dst
) {
  static constexpr auto MAX_RESERVE = 1000;
  if (count == 0) {
    return ok();
  }

  const uint32_t stride = config.guard_flags().value_or(0) >> 28;


  uint64_t entry_size = sizeof(uint32_t);
  if (stride >= 1) {
    entry_size += stride;
  }

  const uint64_t available =
      stream.size() - std::min<uint64_t>(stream.pos(), stream.size());

  const uint64_t nb = std::min<uint64_t>(count, available / entry_size);

  LIEF_DEBUG("Parsing GF (#{}, bounded to #{}, stride={})", count, nb, stride);

  (config.*dst).reserve(std::min<uint64_t>(MAX_RESERVE, nb));

  bool once_stride = false;
  for (size_t i = 0; i < nb; ++i) {
    auto rva = stream.read<uint32_t>();
    if (!rva) {
      LIEF_DEBUG("Failed to read RVA at index {} ({}:{})", i, __FUNCTION__,
                 __LINE__);
      return make_error_code(rva.error());
    }

    uint32_t extra = 0;

    if (stride == 1) {
      auto value = stream.read<uint8_t>();
      if (!value) {
        LIEF_WARN("Failed to read stride for RVA: {:#x}", *rva);
        return make_error_code(value.error());
      }
      extra = *value;
    }

    (config.*dst).push_back({*rva, extra});

    if (stride > 1) {
      if (!once_stride) {
        LIEF_WARN("Stride '{}' is not supported", stride);
        once_stride = true;
      }
      stream.increment_pos(stride);
    }
  }

  return ok();
}

ok_error_t LoadConfiguration::parse_dyn_relocs(Parser& ctx,
                                               LoadConfiguration& lconf) {
  auto dyn_reloc_off = lconf.dynamic_value_reloctable_offset();
  uint16_t dyn_reloc_sec = lconf.dynamic_value_reloctable_section().value_or(0);

  if (dyn_reloc_sec < 1) {
    return ok();
  }

  if (!dyn_reloc_off) {
    return make_error_code(lief_errors::corrupted);
  }

  const uint16_t sec_idx = dyn_reloc_sec - 1;
  auto sections = ctx.bin().sections();
  if (sec_idx >= sections.size()) {
    return make_error_code(lief_errors::corrupted);
  }
  Section& sec = sections[sec_idx];

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

  if (*dyn_reloc_off >= content.size()) {
    return make_error_code(lief_errors::corrupted);
  }

  span<const uint8_t> payload = content.subspan(*dyn_reloc_off);
  SpanStream strm(payload);

  auto version = strm.read<uint32_t>();
  auto size = strm.read<uint32_t>();

  if (!version || !size) {
    return make_error_code(lief_errors::corrupted);
  }

  LIEF_DEBUG("Dynamic relocations. arch={}, version={}, size={:#x}",
             PE::to_string(ctx.bin().header().machine()), *version, *size);
  const bool supported_version = *version == 1 || *version == 2;
  if (!supported_version) {
    LIEF_WARN("Dynamic relocations version {} is not supported", *version);
    return make_error_code(lief_errors::not_supported);
  }

  if (ctx.bin().type() == PE_TYPE::PE32) {
    return *version == 1 ?
               parse_dyn_relocs_entries</*version=*/1, details::PE32>(ctx, strm,
                                                                      lconf,
                                                                      *size) :
               parse_dyn_relocs_entries</*version=*/2, details::PE32>(ctx, strm,
                                                                      lconf,
                                                                      *size);
  }
  assert(ctx.bin().type() == PE_TYPE::PE32_PLUS);

  return *version == 1 ?
             parse_dyn_relocs_entries</*version=*/1, details::PE64>(ctx, strm,
                                                                    lconf, *size) :
             parse_dyn_relocs_entries</*version=*/2, details::PE64>(ctx, strm,
                                                                    lconf, *size);
}


template<uint8_t version, class PE_T>
ok_error_t LoadConfiguration::parse_dyn_relocs_entries(Parser& ctx,
                                                       BinaryStream& stream,
                                                       LoadConfiguration& config,
                                                       size_t size) {
  const size_t stream_start = stream.pos();
  while (stream && stream.pos() < (stream_start + size)) {
    if constexpr (version == 1) {
      auto v1 = DynamicRelocationV1::parse<PE_T>(ctx, stream);
      if (v1 == nullptr) {
        LIEF_WARN("Dynamic (v1) relocation parsing failed at offset={:#06x}",
                  stream.pos());
        break;
      }
      config.dynamic_relocs_.push_back(std::move(v1));
      continue;
    }

    if constexpr (version == 2) {
      LIEF_WARN("Dynamic (v2) dynamic relocation parsing not implemented");
      break;
    }
  }
  return ok();
}

template<class PE_T>
ok_error_t LoadConfiguration::parse_enclave_config(Parser& ctx,
                                                   LoadConfiguration& config) {
  const uint64_t enclave_config_ptr =
      config.enclave_configuration_ptr().value_or(0);
  if (enclave_config_ptr == 0) {
    return ok();
  }

  const uint64_t imagebase = ctx.bin().optional_header().imagebase();

  if (enclave_config_ptr < imagebase) {
    LIEF_DEBUG("Error: {}:{}", __FUNCTION__, __LINE__);
    return make_error_code(lief_errors::corrupted);
  }

  uint64_t offset = ctx.bin().va_to_offset(enclave_config_ptr);

  ScopedStream stream(ctx.stream(), offset);
  auto enclave_config = EnclaveConfiguration::parse<PE_T>(ctx, *stream);
  if (enclave_config == nullptr) {
    return make_error_code(lief_errors::read_error);
  }

  config.enclave_config_ = std::move(enclave_config);
  return ok();
}


ok_error_t LoadConfiguration::parse_volatile_metadata(Parser& ctx,
                                                      LoadConfiguration& config) {
  const uint64_t volatile_metadata =
      config.volatile_metadata_pointer().value_or(0);
  if (volatile_metadata == 0) {
    return ok();
  }

  const uint64_t imagebase = ctx.bin().optional_header().imagebase();

  if (volatile_metadata < imagebase) {
    LIEF_DEBUG("Error: {}:{}", __FUNCTION__, __LINE__);
    return make_error_code(lief_errors::corrupted);
  }

  uint64_t offset = ctx.bin().va_to_offset(volatile_metadata);

  ScopedStream stream(ctx.stream(), offset);
  auto metadata = VolatileMetadata::parse(ctx, *stream);
  if (metadata == nullptr) {
    return make_error_code(lief_errors::read_error);
  }

  config.volatile_metadata_ = std::move(metadata);
  return ok();
}

LoadConfiguration::LoadConfiguration(const LoadConfiguration& other) :
  Object(other),
  characteristics_(other.characteristics_),
  timedatestamp_(other.timedatestamp_),
  major_version_(other.major_version_),
  minor_version_(other.minor_version_),
  global_flags_clear_(other.global_flags_clear_),
  global_flags_set_(other.global_flags_set_),
  critical_section_default_timeout_(other.critical_section_default_timeout_),
  decommit_free_block_threshold_(other.decommit_free_block_threshold_),
  decommit_total_free_threshold_(other.decommit_total_free_threshold_),
  lock_prefix_table_(other.lock_prefix_table_),
  maximum_allocation_size_(other.maximum_allocation_size_),
  virtual_memory_threshold_(other.virtual_memory_threshold_),
  process_affinity_mask_(other.process_affinity_mask_),
  process_heap_flags_(other.process_heap_flags_),
  csd_version_(other.csd_version_),
  reserved1_(other.reserved1_),
  editlist_(other.editlist_),
  security_cookie_(other.security_cookie_),
  se_handler_table_(other.se_handler_table_),
  se_handler_count_(other.se_handler_count_),
  guard_cf_check_function_pointer_(other.guard_cf_check_function_pointer_),
  guard_cf_dispatch_function_pointer_(other.guard_cf_dispatch_function_pointer_),
  guard_cf_function_table_(other.guard_cf_function_table_),
  guard_cf_function_count_(other.guard_cf_function_count_),
  flags_(other.flags_),
  code_integrity_(other.code_integrity_),
  guard_address_taken_iat_entry_table_(other.guard_address_taken_iat_entry_table_),
  guard_address_taken_iat_entry_count_(other.guard_address_taken_iat_entry_count_),
  guard_long_jump_target_table_(other.guard_long_jump_target_table_),
  guard_long_jump_target_count_(other.guard_long_jump_target_count_),
  dynamic_value_reloc_table_(other.dynamic_value_reloc_table_),
  hybrid_metadata_pointer_(other.hybrid_metadata_pointer_),
  guard_rf_failure_routine_(other.guard_rf_failure_routine_),
  guard_rf_failure_routine_function_pointer_(
      other.guard_rf_failure_routine_function_pointer_
  ),
  dynamic_value_reloctable_offset_(other.dynamic_value_reloctable_offset_),
  dynamic_value_reloctable_section_(other.dynamic_value_reloctable_section_),
  reserved2_(other.reserved2_),
  guardrf_verify_stackpointer_function_pointer_(
      other.guardrf_verify_stackpointer_function_pointer_
  ),
  hotpatch_table_offset_(other.hotpatch_table_offset_),
  reserved3_(other.reserved3_),
  enclave_configuration_ptr_(other.enclave_configuration_ptr_),
  volatile_metadata_pointer_(other.volatile_metadata_pointer_),
  guard_eh_continuation_table_(other.guard_eh_continuation_table_),
  guard_eh_continuation_count_(other.guard_eh_continuation_count_),
  guard_xfg_check_function_pointer_(other.guard_xfg_check_function_pointer_),
  guard_xfg_dispatch_function_pointer_(other.guard_xfg_dispatch_function_pointer_),
  guard_xfg_table_dispatch_function_pointer_(
      other.guard_xfg_table_dispatch_function_pointer_
  ),
  cast_guard_os_determined_failure_mode_(
      other.cast_guard_os_determined_failure_mode_
  ),
  guard_memcpy_function_pointer_(other.guard_memcpy_function_pointer_),
  uma_function_pointers_(other.uma_function_pointers_),

  seh_rva_(other.seh_rva_),
  guard_cf_functions_(other.guard_cf_functions_),
  guard_address_taken_iat_entries_(other.guard_address_taken_iat_entries_),
  guard_long_jump_targets_(other.guard_long_jump_targets_),
  guard_eh_continuation_functions_(other.guard_eh_continuation_functions_) {
  if (other.chpe_ != nullptr) {
    chpe_ = other.chpe_->clone();
  }

  if (other.enclave_config_ != nullptr) {
    enclave_config_ = other.enclave_config_->clone();
  }

  if (other.volatile_metadata_ != nullptr) {
    volatile_metadata_ = other.volatile_metadata_->clone();
  }

  if (other.dynamic_relocs_.empty()) {
    dynamic_relocs_.reserve(other.dynamic_relocs_.size());
    std::transform(other.dynamic_relocs_.begin(), other.dynamic_relocs_.end(),
                   std::back_inserter(dynamic_relocs_),
                   [](const std::unique_ptr<DynamicRelocation>& R) {
                     return R->clone();
                   });
  }
}

LoadConfiguration& LoadConfiguration::operator=(const LoadConfiguration& other) {
  if (&other == this) {
    return *this;
  }
  Object::operator=(other);
  characteristics_ = other.characteristics_;
  timedatestamp_ = other.timedatestamp_;
  major_version_ = other.major_version_;
  minor_version_ = other.minor_version_;
  global_flags_clear_ = other.global_flags_clear_;
  global_flags_set_ = other.global_flags_set_;
  critical_section_default_timeout_ = other.critical_section_default_timeout_;
  decommit_free_block_threshold_ = other.decommit_free_block_threshold_;
  decommit_total_free_threshold_ = other.decommit_total_free_threshold_;
  lock_prefix_table_ = other.lock_prefix_table_;
  maximum_allocation_size_ = other.maximum_allocation_size_;
  virtual_memory_threshold_ = other.virtual_memory_threshold_;
  process_affinity_mask_ = other.process_affinity_mask_;
  process_heap_flags_ = other.process_heap_flags_;
  csd_version_ = other.csd_version_;
  reserved1_ = other.reserved1_;
  editlist_ = other.editlist_;
  security_cookie_ = other.security_cookie_;
  se_handler_table_ = other.se_handler_table_;
  se_handler_count_ = other.se_handler_count_;
  guard_cf_check_function_pointer_ = other.guard_cf_check_function_pointer_;
  guard_cf_dispatch_function_pointer_ = other.guard_cf_dispatch_function_pointer_;
  guard_cf_function_table_ = other.guard_cf_function_table_;
  guard_cf_function_count_ = other.guard_cf_function_count_;
  flags_ = other.flags_;
  code_integrity_ = other.code_integrity_;
  guard_address_taken_iat_entry_table_ =
      other.guard_address_taken_iat_entry_table_;
  guard_address_taken_iat_entry_count_ =
      other.guard_address_taken_iat_entry_count_;
  guard_long_jump_target_table_ = other.guard_long_jump_target_table_;
  guard_long_jump_target_count_ = other.guard_long_jump_target_count_;
  dynamic_value_reloc_table_ = other.dynamic_value_reloc_table_;
  hybrid_metadata_pointer_ = other.hybrid_metadata_pointer_;
  guard_rf_failure_routine_ = other.guard_rf_failure_routine_;
  guard_rf_failure_routine_function_pointer_ =
      other.guard_rf_failure_routine_function_pointer_;
  dynamic_value_reloctable_offset_ = other.dynamic_value_reloctable_offset_;
  dynamic_value_reloctable_section_ = other.dynamic_value_reloctable_section_;
  reserved2_ = other.reserved2_;
  guardrf_verify_stackpointer_function_pointer_ =
      other.guardrf_verify_stackpointer_function_pointer_;
  hotpatch_table_offset_ = other.hotpatch_table_offset_;
  reserved3_ = other.reserved3_;
  enclave_configuration_ptr_ = other.enclave_configuration_ptr_;
  volatile_metadata_pointer_ = other.volatile_metadata_pointer_;
  guard_eh_continuation_table_ = other.guard_eh_continuation_table_;
  guard_eh_continuation_count_ = other.guard_eh_continuation_count_;
  guard_xfg_check_function_pointer_ = other.guard_xfg_check_function_pointer_;
  guard_xfg_dispatch_function_pointer_ =
      other.guard_xfg_dispatch_function_pointer_;
  guard_xfg_table_dispatch_function_pointer_ =
      other.guard_xfg_table_dispatch_function_pointer_;
  cast_guard_os_determined_failure_mode_ =
      other.cast_guard_os_determined_failure_mode_;
  guard_memcpy_function_pointer_ = other.guard_memcpy_function_pointer_;
  uma_function_pointers_ = other.uma_function_pointers_;

  seh_rva_ = other.seh_rva_;
  guard_cf_functions_ = other.guard_cf_functions_;
  guard_address_taken_iat_entries_ = other.guard_address_taken_iat_entries_;
  guard_long_jump_targets_ = other.guard_long_jump_targets_;
  guard_eh_continuation_functions_ = other.guard_eh_continuation_functions_;

  if (other.chpe_ != nullptr) {
    chpe_ = other.chpe_->clone();
  }

  if (other.enclave_config_ != nullptr) {
    enclave_config_ = other.enclave_config_->clone();
  }

  if (other.volatile_metadata_ != nullptr) {
    volatile_metadata_ = other.volatile_metadata_->clone();
  }

  if (other.dynamic_relocs_.empty()) {
    dynamic_relocs_.reserve(other.dynamic_relocs_.size());
    std::transform(other.dynamic_relocs_.begin(), other.dynamic_relocs_.end(),
                   std::back_inserter(dynamic_relocs_),
                   [](const std::unique_ptr<DynamicRelocation>& R) {
                     return R->clone();
                   });
  }
  return *this;
}

void LoadConfiguration::accept(Visitor& visitor) const {
  visitor.visit(*this);
}

std::string LoadConfiguration::to_string() const {
  static constexpr auto WIDTH = 65;
  std::ostringstream oss;
  oss << fmt::format("{:{}} {:#08x}\n", "Size:", WIDTH, size())
      << fmt::format("{:{}} {}\n", "Timestamp:", WIDTH, timedatestamp())
      << fmt::format("{:{}} {}.{}\n", "version:", WIDTH, major_version(),
                     minor_version())
      << fmt::format("{:{}} {}\n", "GlobalFlags Clear:", WIDTH,
                     global_flags_clear())
      << fmt::format("{:{}} {}\n", "GlobalFlags Set:", WIDTH, global_flags_set())
      << fmt::format("{:{}} {}\n", "Critical Section Default Timeout:", WIDTH,
                     critical_section_default_timeout())
      << fmt::format("{:{}} {}\n", "Decommit Free Block Threshold:", WIDTH,
                     decommit_free_block_threshold())
      << fmt::format("{:{}} {}\n", "Decommit Total Free Threshold:", WIDTH,
                     decommit_total_free_threshold())
      << fmt::format("{:{}} {}\n", "Lock Prefix Table:", WIDTH,
                     lock_prefix_table())
      << fmt::format("{:{}} {}\n", "Maximum Allocation Size:", WIDTH,
                     maximum_allocation_size())
      << fmt::format("{:{}} {}\n", "Virtual Memory Threshold:", WIDTH,
                     virtual_memory_threshold())
      << fmt::format("{:{}} {}\n", "Process Affinity Mask:", WIDTH,
                     process_affinity_mask())
      << fmt::format("{:{}} {}\n", "Process Heap Flags:", WIDTH,
                     process_heap_flags())
      << fmt::format("{:{}} {}\n", "CSD Version:", WIDTH, csd_version())
      << fmt::format("{:{}} {}\n", "Dependent Load Flag:", WIDTH,
                     dependent_load_flags())
      << fmt::format("{:{}} {}\n", "Edit List:", WIDTH, editlist())
      << fmt::format("{:{}} {:#018x}\n", "Security Cookie:", WIDTH,
                     security_cookie());

  if (auto val = se_handler_table()) {
    oss << fmt::format("{:{}} {:#08x}\n", "SEH Table:", WIDTH, *val);
  }

  if (auto val = se_handler_count()) {
    oss << fmt::format("{:{}} {}\n", "SEH Count:", WIDTH, *val);
  }

  if (auto val = guard_cf_check_function_pointer()) {
    oss << fmt::format("{:{}} {:#018x}\n",
                       "Guard CF address of check-function pointer:", WIDTH, *val);
  }

  if (auto val = guard_cf_dispatch_function_pointer()) {
    oss << fmt::format("{:{}} {:#018x}\n",
                       "Guard CF address of dispatch-function pointer:", WIDTH,
                       *val);
  }

  if (auto val = guard_cf_function_table()) {
    oss << fmt::format("{:{}} {:#018x}\n", "Guard CF function table:", WIDTH,
                       *val);
  }

  if (auto val = guard_cf_function_count()) {
    oss << fmt::format("{:{}} {}\n", "Guard CF function count:", WIDTH, *val);
  }

  if (auto val = guard_flags(); val && *val != 0) {
    oss << fmt::format("{:{}} {}\n", "Guard Flags:", WIDTH,
                       fmt::join(guard_cf_flags_list(), ","));
  }

  if (const CodeIntegrity* CI = code_integrity()) {
    oss << fmt::format("{:{}} {}\n", "Code Integrity Flags:", WIDTH, CI->flags())
        << fmt::format("{:{}} {:#010x}\n", "Code Integrity Catalog:", WIDTH,
                       CI->catalog())
        << fmt::format("{:{}} {:#010x}\n", "Code Integrity Catalog Offset:", WIDTH,
                       CI->catalog_offset())
        << fmt::format("{:{}} {}\n", "Code Integrity Reserved:", WIDTH,
                       CI->reserved());
  }

  if (auto val = guard_address_taken_iat_entry_table()) {
    oss << fmt::format("{:{}} {:#018x}\n",
                       "Guard CF address taken IAT entry table:", WIDTH, *val);
  }

  if (auto val = guard_address_taken_iat_entry_count()) {
    oss << fmt::format("{:{}} {}\n",
                       "Guard CF address taken IAT entry count:", WIDTH, *val);
  }

  if (auto val = guard_long_jump_target_table()) {
    oss << fmt::format("{:{}} {:#018x}\n",
                       "Guard CF long jump target table:", WIDTH, *val);
  }

  if (auto val = guard_long_jump_target_count()) {
    oss << fmt::format("{:{}} {}\n", "Guard CF long jump target count:", WIDTH,
                       *val);
  }

  if (auto val = dynamic_value_reloc_table()) {
    oss << fmt::format("{:{}} {:#018x}\n",
                       "Dynamic value relocation table:", WIDTH, *val);
  }

  if (auto val = hybrid_metadata_pointer()) {
    oss << fmt::format("{:{}} {:#018x}\n", "Hybrid metadata pointer:", WIDTH,
                       *val);
  }

  if (auto val = guard_rf_failure_routine()) {
    oss << fmt::format("{:{}} {:#018x}\n",
                       "Guard RF address of failure-function:", WIDTH, *val);
  }

  if (auto val = guard_rf_failure_routine_function_pointer()) {
    oss << fmt::format("{:{}} {:#018x}\n",
                       "Guard RF address of failure-function pointer:", WIDTH,
                       *val);
  }

  if (auto val = dynamic_value_reloctable_offset()) {
    oss << fmt::format("{:{}} {:#018x}\n",
                       "Dynamic value relocation table offset:", WIDTH, *val);
  }

  if (auto val = dynamic_value_reloctable_section()) {
    oss << fmt::format("{:{}} {}\n",
                       "Dynamic value relocation table section:", WIDTH, *val);
  }

  if (auto val = reserved2()) {
    oss << fmt::format("{:{}} {}\n", "Reserved2:", WIDTH, *val);
  }

  if (auto val = guard_rf_verify_stackpointer_function_pointer()) {
    oss << fmt::format(
        "{:{}} {:#018x}\n",
        "Guard RF address of stack pointer verification function pointer:", WIDTH,
        *val
    );
  }

  if (auto val = hotpatch_table_offset()) {
    oss << fmt::format("{:{}} {:#010x}\n", "Hot patching table offset:", WIDTH,
                       *val);
  }

  if (auto val = reserved3()) {
    oss << fmt::format("{:{}} {}\n", "Reserved3:", WIDTH, *val);
  }

  if (auto val = enclave_configuration_ptr()) {
    oss << fmt::format("{:{}} {:#018x}\n", "Enclave configuration pointer", WIDTH,
                       *val);
  }

  if (auto val = volatile_metadata_pointer()) {
    oss << fmt::format("{:{}} {:#018x}\n", "Volatile metadata pointer", WIDTH,
                       *val);
  }

  if (auto val = guard_eh_continuation_table()) {
    oss << fmt::format("{:{}} {:#018x}\n", "Guard EH continuation table", WIDTH,
                       *val);
  }

  if (auto val = guard_eh_continuation_count()) {
    oss << fmt::format("{:{}} {}\n", "Guard EH continuation count", WIDTH, *val);
  }

  if (auto val = guard_xfg_check_function_pointer()) {
    oss << fmt::format("{:{}} {:#018x}\n",
                       "Guard XFG address of check-function pointer", WIDTH, *val);
  }

  if (auto val = guard_xfg_dispatch_function_pointer()) {
    oss << fmt::format("{:{}} {:#018x}\n",
                       "Guard XFG address of dispatch-function pointer", WIDTH,
                       *val);
  }

  if (auto val = guard_xfg_table_dispatch_function_pointer()) {
    oss << fmt::format("{:{}} {:#018x}\n",
                       "Guard XFG address of dispatch-table-function pointer",
                       WIDTH, *val);
  }

  if (auto val = cast_guard_os_determined_failure_mode()) {
    oss << fmt::format("{:{}} {:#018x}\n", "CastGuard OS determined failure mode",
                       WIDTH, *val);
  }

  if (auto val = guard_memcpy_function_pointer()) {
    oss << fmt::format("{:{}} {:#018x}\n", "Guard memcpy function pointer", WIDTH,
                       *val);
  }

  if (auto val = uma_function_pointers()) {
    oss << fmt::format("{:{}} {:#018x}\n", "UMA function pointers", WIDTH, *val);
  }

  if (const CHPEMetadata* metadata = chpe_metadata()) {
    oss << "  CHPE Metadata:\n" << indent(metadata->to_string(), 4);
  }

  if (!seh_rva_.empty()) {
    oss << fmt::format("  SEH Table ({}) {{\n", seh_rva_.size());
    for (uint32_t RVA : seh_rva_) {
      oss << fmt::format("    {:#010x}\n", RVA);
    }
    oss << "}\n";
  }

  if (!guard_cf_functions_.empty()) {
    oss << fmt::format("  Guard CF Function ({}) {{\n",
                       guard_cf_functions_.size());
    for (const guard_function_t& F : guard_cf_functions_) {
      oss << fmt::format("    {:#010x} ({})\n", F.rva, F.extra);
    }
    oss << "}\n";
  }

  if (!guard_address_taken_iat_entries_.empty()) {
    oss << fmt::format("  Guard CF Address Taken IAT ({}) {{\n",
                       guard_address_taken_iat_entries_.size());
    for (const guard_function_t& F : guard_address_taken_iat_entries_) {
      oss << fmt::format("    {:#010x} ({})\n", F.rva, F.extra);
    }
    oss << "}\n";
  }

  if (!guard_long_jump_targets_.empty()) {
    oss << fmt::format("  Guard CF Long Jump Target ({}) {{\n",
                       guard_long_jump_targets_.size());
    for (const guard_function_t& F : guard_long_jump_targets_) {
      oss << fmt::format("    {:#010x} ({})\n", F.rva, F.extra);
    }
    oss << "}\n";
  }

  if (!guard_eh_continuation_functions_.empty()) {
    oss << fmt::format("  Guard EH Continuation ({}) {{\n",
                       guard_eh_continuation_functions_.size());
    for (const guard_function_t& F : guard_eh_continuation_functions_) {
      oss << fmt::format("    {:#010x} ({})\n", F.rva, F.extra);
    }
    oss << "}\n";
  }

  for (const DynamicRelocation& DR : dynamic_relocations()) {
    oss << DR << '\n';
  }

  if (const EnclaveConfiguration* enclave = enclave_config()) {
    oss << "  Enclave Configuration:\n" << indent(enclave->to_string(), 4);
  }

  if (const VolatileMetadata* metadata = volatile_metadata()) {
    oss << "  Volatile Metadata:\n" << indent(metadata->to_string(), 4);
  }

  return oss.str();
}

const char* to_string(LoadConfiguration::IMAGE_GUARD e) {
#define ENTRY(X) std::pair(LoadConfiguration::IMAGE_GUARD::X, #X)
  STRING_MAP enums2str{
      ENTRY(NONE),
      ENTRY(CF_INSTRUMENTED),
      ENTRY(CFW_INSTRUMENTED),
      ENTRY(CF_FUNCTION_TABLE_PRESENT),
      ENTRY(SECURITY_COOKIE_UNUSED),
      ENTRY(PROTECT_DELAYLOAD_IAT),
      ENTRY(DELAYLOAD_IAT_IN_ITS_OWN_SECTION),
      ENTRY(CF_EXPORT_SUPPRESSION_INFO_PRESENT),
      ENTRY(CF_ENABLE_EXPORT_SUPPRESSION),
      ENTRY(CF_LONGJUMP_TABLE_PRESENT),
      ENTRY(RF_INSTRUMENTED),
      ENTRY(RF_ENABLE),
      ENTRY(RF_STRICT),
      ENTRY(RETPOLINE_PRESENT),
      ENTRY(EH_CONTINUATION_TABLE_PRESENT),
      ENTRY(XFG_ENABLED),
      ENTRY(CASTGUARD_PRESENT),
      ENTRY(MEMCPY_PRESENT),
  };
#undef ENTRY

  if (auto it = enums2str.find(e); it != enums2str.end()) {
    return it->second;
  }
  return "UNKNOWN";
}

std::vector<LoadConfiguration::IMAGE_GUARD>
    LoadConfiguration::guard_cf_flags_list() const {
  std::vector<IMAGE_GUARD> flags_list;
  flags_list.reserve(3);
  std::copy_if(IMAGE_GUARD_LIST.begin(), IMAGE_GUARD_LIST.end(),
               std::back_inserter(flags_list),
               [this](IMAGE_GUARD f) { return has(f); });

  return flags_list;
}

template std::unique_ptr<LoadConfiguration>
    LoadConfiguration::parse<details::PE32>(Parser& ctx, BinaryStream& stream);

template std::unique_ptr<LoadConfiguration>
    LoadConfiguration::parse<details::PE64>(Parser& ctx, BinaryStream& stream);

template ok_error_t LoadConfiguration::parse_enclave_config<details::PE32>(
    Parser& ctx, LoadConfiguration& config
);

template ok_error_t LoadConfiguration::parse_enclave_config<details::PE64>(
    Parser& ctx, LoadConfiguration& config
);

} // namespace LIEF::PE