@ 261107.7
current
/* 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 "LIEF/PE/Parser.hpp"
#include "LIEF/PE/Binary.hpp"
#include "LIEF/PE/Relocation.hpp"
#include "LIEF/PE/RelocationEntry.hpp"
#include "LIEF/PE/LoadConfigurations/DynamicRelocation/FunctionOverride.hpp"
#include "LIEF/PE/LoadConfigurations/DynamicRelocation/FunctionOverrideInfo.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"

#include "logging.hpp"
#include "internal_utils.hpp"

namespace LIEF::PE {
using image_bdd_info_t = FunctionOverride::image_bdd_info_t;

FunctionOverride::FunctionOverride() :
  DynamicFixup(KIND::FUNCTION_OVERRIDE) {}

FunctionOverride::FunctionOverride(const FunctionOverride& other) :
  DynamicFixup(other),
  bdd_info_(other.bdd_info_) {
  if (!other.overriding_info_.empty()) {
    overriding_info_.reserve(other.overriding_info_.size());
    overriding_info_.reserve(other.overriding_info_.size());
    std::transform(other.overriding_info_.begin(), other.overriding_info_.end(),
                   std::back_inserter(overriding_info_),
                   [](const std::unique_ptr<FunctionOverrideInfo>& func) {
                     return std::make_unique<FunctionOverrideInfo>(*func);
                   });
  }
}

FunctionOverride& FunctionOverride::operator=(const FunctionOverride& other) {
  if (this == &other) {
    return *this;
  }
  DynamicFixup::operator=(other);
  bdd_info_ = other.bdd_info_;

  if (!other.overriding_info_.empty()) {
    overriding_info_.reserve(other.overriding_info_.size());
    overriding_info_.reserve(other.overriding_info_.size());
    std::transform(other.overriding_info_.begin(), other.overriding_info_.end(),
                   std::back_inserter(overriding_info_),
                   [](const std::unique_ptr<FunctionOverrideInfo>& func) {
                     return std::make_unique<FunctionOverrideInfo>(*func);
                   });
  }
  return *this;
}

FunctionOverride::FunctionOverride(FunctionOverride&&) = default;
FunctionOverride& FunctionOverride::operator=(FunctionOverride&&) = default;

FunctionOverride::~FunctionOverride() = default;

std::string FunctionOverride::to_string() const {
  std::ostringstream oss;
  oss << fmt::format("Function Override ({}) {{\n", overriding_info_.size());
  for (const FunctionOverrideInfo& info : func_overriding_info()) {
    oss << indent(info.to_string(), 2);
    if (const image_bdd_info_t* bdd_info = find_bdd_info(info)) {
      oss << fmt::format("  BDD Version: {} ({} bytes, offset={:#010x})\n",
                         bdd_info->version, bdd_info->original_size,
                         bdd_info->original_offset);
      for (size_t i = 0; i < bdd_info->relocations.size(); ++i) {
        const image_bdd_dynamic_relocation_t& R = bdd_info->relocations[i];
        oss << fmt::format("    [{:04d}] L={:04d}, R={:04d}, V={:#010x}\n", i,
                           R.left, R.right, R.value);
      }
    } else {
      oss << "  <Missing IMAGE_BDD_INFO>\n";
    }
  }
  oss << "}\n";
  return oss.str();
}

std::unique_ptr<FunctionOverride> FunctionOverride::parse(Parser& ctx,
                                                          SpanStream& strm) {
  auto FuncOverrideSize = strm.read<uint32_t>();
  if (!FuncOverrideSize) {
    LIEF_DEBUG("Error: {}: {}", __FUNCTION__, __LINE__);
    return nullptr;
  }

  auto FuncOverrideInfo = strm.slice(strm.pos(), *FuncOverrideSize);
  if (FuncOverrideInfo) {
    strm.increment_pos(*FuncOverrideSize);
  } else {
    LIEF_WARN("Failed to slice FuncOverrideInfo");
  }

  auto BDDInfo = strm.slice(strm.pos());
  if (BDDInfo) {
    strm.increment_pos(BDDInfo->size());
  } else {
    LIEF_WARN("Failed to slice BDDInfo");
  }

  auto func = std::make_unique<FunctionOverride>();
  while (FuncOverrideInfo && *FuncOverrideInfo) {
    auto override_info = FunctionOverrideInfo::parse(ctx, *FuncOverrideInfo);
    if (override_info == nullptr) {
      LIEF_INFO("Failed to parse all overriding info");
      break;
    }
    func->overriding_info_.push_back(std::move(override_info));
  }

  while (BDDInfo && *BDDInfo) {
    if (!parse_bdd_info(ctx, *BDDInfo, *func)) {
      break;
    }
  }

  return func;
}

ok_error_t FunctionOverride::parse_bdd_info(Parser& /*ctx*/, SpanStream& strm,
                                            FunctionOverride& func) {
  image_bdd_info_t bdd_info;
  bdd_info.original_offset = strm.pos();

  auto Version = strm.read<uint32_t>();
  if (!Version) {
    LIEF_WARN("Failed to read IMAGE_BDD_INFO.Version");
    return make_error_code(Version.error());
  }
  bdd_info.version = *Version;

  auto BDDSize = strm.read<uint32_t>();
  if (!BDDSize) {
    LIEF_WARN("Failed to read IMAGE_BDD_INFO.BDDSize");
    return make_error_code(BDDSize.error());
  }

  if (*Version != 1) {
    LIEF_DEBUG("Unsupported IMAGE_BDD_INFO version: {}", *Version);

    if (!strm.read_data(bdd_info.payload, *BDDSize)) {
      LIEF_WARN("Failed to read IMAGE_BDD_INFO payload");
    }
    return ok();
  }

  LIEF_DEBUG("IMAGE_BDD_INFO version={}, size={}", *Version, *BDDSize);

  bdd_info.original_size = *BDDSize;

  auto payload = strm.slice(strm.pos(), *BDDSize);
  if (!payload) {
    LIEF_WARN("Failed to slice IMAGE_BDD_INFO payload");
    return make_error_code(payload.error());
  }

  while (payload) {
    auto Left = payload->read<uint16_t>();
    if (!Left) {
      break;
    }

    auto Right = payload->read<uint16_t>();
    if (!Right) {
      break;
    }

    auto Value = payload->read<uint32_t>();
    if (!Value) {
      break;
    }

    bdd_info.relocations.push_back({*Left, *Right, *Value});
  }
  func.bdd_info_.push_back(std::move(bdd_info));

  strm.increment_pos(*BDDSize);

  return ok();
}

image_bdd_info_t* FunctionOverride::find_bdd_info(uint32_t offset) {
  // The current `parse_bdd_info` function parses the bdd_info in sequence
  // with increasing offset on the stream. This means that the func.bdd_info_
  // is already sorted by image_bdd_info_t::original_offset. We can leverage
  // this property to perform a binary search in O(log2(N)) instead of an O(N)
  auto it = std::lower_bound(
      bdd_info_.begin(), bdd_info_.end(), image_bdd_info_t{0, 0, offset, {}, {}},
      [](const image_bdd_info_t& LHS, const image_bdd_info_t& RHS) {
        return LHS.original_offset < RHS.original_offset;
      }
  );

  if (it == bdd_info_.end()) {
    return nullptr;
  }
  return &*it;
}

image_bdd_info_t*
    FunctionOverride::find_bdd_info(const FunctionOverrideInfo& info) {
  return find_bdd_info(info.bdd_offset());
}
}