@ 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/Visitor.hpp"
#include "LIEF/BinaryStream/BinaryStream.hpp"


#include "PE/Structures.hpp"
#include "LIEF/PE/Binary.hpp"
#include "LIEF/PE/Parser.hpp"
#include "LIEF/PE/Relocation.hpp"
#include "LIEF/PE/RelocationEntry.hpp"

#include <spdlog/fmt/fmt.h>

#include "logging.hpp"


namespace LIEF::PE {

Relocation::Relocation(Relocation&&) = default;
Relocation& Relocation::operator=(Relocation&& other) = default;
Relocation::~Relocation() = default;

Relocation::Relocation() = default;

Relocation::Relocation(uint32_t base, uint32_t block_size) :
  Object(),
  block_size_(block_size),
  virtual_address_(base) {}

Relocation::Relocation(uint32_t base) :
  Relocation(base, 0) {}

Relocation::Relocation(const Relocation& other) :
  Object{other},
  block_size_{other.block_size_},
  virtual_address_{other.virtual_address_} {
  entries_.reserve(other.entries_.size());
  for (const std::unique_ptr<RelocationEntry>& r : other.entries_) {
    auto copy = std::make_unique<RelocationEntry>(*r);
    copy->relocation_ = this;
    entries_.push_back(std::move(copy));
  }
}

Relocation& Relocation::operator=(Relocation other) {
  swap(other);
  return *this;
}

Relocation::Relocation(const details::pe_base_relocation_block& header) :
  block_size_{header.BlockSize},
  virtual_address_{header.PageRVA} {}


void Relocation::swap(Relocation& other) {
  std::swap(block_size_, other.block_size_);
  std::swap(virtual_address_, other.virtual_address_);
  std::swap(entries_, other.entries_);
}

RelocationEntry& Relocation::add_entry(const RelocationEntry& entry) {
  auto newone = std::make_unique<RelocationEntry>(entry);
  newone->relocation_ = this;
  entries_.push_back(std::move(newone));
  return *entries_.back();
}

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

std::ostream& operator<<(std::ostream& os, const Relocation& relocation) {
  os << fmt::format("Page RVA: {:#010x} (SizeOfBlock: {} bytes)\n",
                    relocation.virtual_address(), relocation.block_size());
  for (const RelocationEntry& entry : relocation.entries()) {
    os << "  " << entry << '\n';
  }

  return os;
}

Relocation::relocations_t Relocation::parse(Parser& ctx, BinaryStream& stream) {
  relocations_t relocs;
  const Header::MACHINE_TYPES arch = ctx.bin().header().machine();
  while (stream) {
    auto PageRVA = stream.read<uint32_t>();
    if (!PageRVA) {
      LIEF_DEBUG("Failed to read page RVA at {}:{}", __FUNCTION__, __LINE__);
      return relocs;
    }

    auto BlockSize = stream.read<uint32_t>();
    if (!BlockSize) {
      LIEF_DEBUG("Failed to read block size at {}:{}", __FUNCTION__, __LINE__);
      return relocs;
    }

    auto relocation = std::make_unique<Relocation>(*PageRVA, *BlockSize);
    // Must be at least sizeof(PageRVA + BlockSize)
    if (*BlockSize < 2 * 4) {
      relocs.push_back(std::move(relocation));
      return relocs;
    }

    const size_t nb_entries = (*BlockSize - 8) / sizeof(uint16_t);
    {
      ScopedStream block_strm(stream);
      for (size_t i = 0; i < nb_entries; ++i) {
        auto data = block_strm->read<uint16_t>();
        if (!data) {
          LIEF_DEBUG("Failed to read entry at {}:{} (entry: {})", __FUNCTION__,
                     __LINE__, i);
          break;
        }

        uint16_t pos = RelocationEntry::get_position(*data);
        RelocationEntry::BASE_TYPES ty =
            RelocationEntry::type_from_data(arch, *data);
        auto entry = std::make_unique<RelocationEntry>(pos, ty);
        entry->relocation_ = relocation.get();
        relocation->entries_.push_back(std::move(entry));
      }
    }
    relocs.push_back(std::move(relocation));
    stream.increment_pos(*BlockSize - 8);
  }
  return relocs;
}

}