// SPDX-FileCopyrightText: Copyright (c) 2022 merryhime // SPDX-License-Identifier: MIT #pragma once #include #include #include #include "oaknut/oaknut_exception.hpp" namespace oaknut { struct Label; namespace detail { constexpr std::uint64_t inverse_mask_from_size(std::size_t size) { return (~std::uint64_t{0}) << size; } constexpr std::uint64_t mask_from_size(std::size_t size) { return (~std::uint64_t{0}) >> (64 - size); } template constexpr std::uint64_t sign_extend(std::uint64_t value) { static_assert(bit_count != 0, "cannot sign-extend zero-sized value"); constexpr size_t shift_amount = 64 - bit_count; return static_cast(static_cast(value << shift_amount) >> shift_amount); } } // namespace detail template struct AddrOffset { AddrOffset(std::ptrdiff_t diff) : m_payload(encode(diff)) {} AddrOffset(Label& label) : m_payload(&label) {} AddrOffset(const void* ptr) : m_payload(ptr) {} static std::uint32_t encode(std::ptrdiff_t diff) { const std::uint64_t diff_u64 = static_cast(diff); if (detail::sign_extend(diff_u64) != diff_u64) throw OaknutException{ExceptionType::OffsetOutOfRange}; if (diff_u64 != (diff_u64 & detail::inverse_mask_from_size(alignment))) throw OaknutException{ExceptionType::OffsetMisaligned}; return static_cast((diff_u64 & detail::mask_from_size(bitsize)) >> alignment); } private: template friend class BasicCodeGenerator; std::variant m_payload; }; template struct PageOffset { PageOffset(const void* ptr) : m_payload(ptr) {} PageOffset(Label& label) : m_payload(&label) {} static std::uint32_t encode(std::uintptr_t current_addr, std::uintptr_t target) { std::uint64_t diff = static_cast((static_cast(target) >> shift_amount) - (static_cast(current_addr) >> shift_amount)); if (detail::sign_extend(diff) != diff) throw OaknutException{ExceptionType::OffsetOutOfRange}; diff &= detail::mask_from_size(bitsize); return static_cast(((diff & 3) << (bitsize - 2)) | (diff >> 2)); } static bool valid(std::uintptr_t current_addr, std::uintptr_t target) { std::uint64_t diff = static_cast((static_cast(target) >> shift_amount) - (static_cast(current_addr) >> shift_amount)); return detail::sign_extend(diff) == diff; } private: template friend class BasicCodeGenerator; std::variant m_payload; }; template struct SOffset { SOffset(std::int64_t offset) { const std::uint64_t diff_u64 = static_cast(offset); if (detail::sign_extend(diff_u64) != diff_u64) throw OaknutException{ExceptionType::OffsetOutOfRange}; if (diff_u64 != (diff_u64 & detail::inverse_mask_from_size(alignment))) throw OaknutException{ExceptionType::OffsetMisaligned}; m_encoded = static_cast((diff_u64 & detail::mask_from_size(bitsize)) >> alignment); } private: template friend class BasicCodeGenerator; std::uint32_t m_encoded; }; template struct POffset { POffset(std::int64_t offset) { const std::uint64_t diff_u64 = static_cast(offset); if (diff_u64 > detail::mask_from_size(bitsize)) throw OaknutException{ExceptionType::OffsetOutOfRange}; if (diff_u64 != (diff_u64 & detail::inverse_mask_from_size(alignment))) throw OaknutException{ExceptionType::OffsetMisaligned}; m_encoded = static_cast((diff_u64 & detail::mask_from_size(bitsize)) >> alignment); } private: template friend class BasicCodeGenerator; std::uint32_t m_encoded; }; } // namespace oaknut