Skip to content

Commit

Permalink
feat(hook): add calling convention support
Browse files Browse the repository at this point in the history
  • Loading branch information
Curve committed May 6, 2024
1 parent 1415a9a commit 1b22836
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 72 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.21)
project(lime LANGUAGES CXX VERSION 3.1)
project(lime LANGUAGES CXX VERSION 4.0)

# --------------------------------------------------------------------------------------------------------
# Library options
Expand Down
21 changes: 21 additions & 0 deletions include/lime/hooks/convention.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

namespace lime
{
enum class convention
{
automatic,
cdecl,
stdcall,
fastcall,
thiscall,
};

namespace detail
{
template <typename T, convention Convention>
struct calling_convention;
};
} // namespace lime

#include "convention.inl"
91 changes: 91 additions & 0 deletions include/lime/hooks/convention.inl
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#pragma once

#include "convention.hpp"

#include <utility>
#include <cstdint>

namespace lime
{
namespace detail
{
template <typename Ret, typename... Args>
struct calling_convention<Ret(Args...), convention::automatic>
{
using add = Ret(Args...);

template <auto Function>
static Ret wrapper(Args... args)
{
return Function(std::forward<Args>(args)...);
}
};

#if INTPTR_MAX == INT32_MAX
#ifdef _MSC_VER
#define LIME_CDECL __cdecl
#define LIME_STDCALL __stdcall
#define LIME_FASTCALL __fastcall
#define LIME_THISCALL __thiscall
#else
#define LIME_CDECL __attribute__((cdecl))
#define LIME_STDCALL __attribute__((stdcall))
#define LIME_FASTCALL __attribute__((fastcall))
#define LIME_THISCALL __attribute__((thiscall))
#endif

template <typename Ret, typename... Args>
struct calling_convention<Ret(Args...), convention::cdecl>
{
using add = Ret LIME_CDECL(Args...);

template <auto Function>
static Ret LIME_CDECL wrapper(Args... args)
{
return Function(std::forward<Args>(args)...);
}
};

template <typename Ret, typename... Args>
struct calling_convention<Ret(Args...), convention::stdcall>
{
using add = Ret LIME_STDCALL(Args...);

template <auto Function>
static Ret LIME_STDCALL wrapper(Args... args)
{
return Function(std::forward<Args>(args)...);
}
};

template <typename Ret, typename... Args>
struct calling_convention<Ret(Args...), convention::fastcall>
{
using add = Ret LIME_FASTCALL(Args...);

template <auto Function>
static Ret LIME_FASTCALL wrapper(Args... args)
{
return Function(std::forward<Args>(args)...);
}
};

template <typename Ret, typename... Args>
struct calling_convention<Ret(Args...), convention::thiscall>
{
using add = Ret LIME_THISCALL(Args...);

template <auto Function>
static Ret LIME_THISCALL wrapper(Args... args)
{
return Function(std::forward<Args>(args)...);
}
};
}; // namespace detail

#undef LIME_CDECL
#undef LIME_STDCALL
#undef LIME_FASTCALL
#undef LIME_THISCALL
#endif
} // namespace lime
47 changes: 22 additions & 25 deletions include/lime/hooks/hook.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#pragma once

#include "convention.hpp"

#include <memory>
#include <cstdint>
#include <concepts>
#include <functional>

#include <tl/expected.hpp>

Expand All @@ -11,36 +14,31 @@ namespace lime
namespace detail
{
template <typename T>
concept address = requires() { requires std::integral<T> or std::is_pointer_v<T>; };
concept is_std_function = requires(T value) {
[]<typename R>(std::function<R> &) {
}(value);
};

template <typename T>
concept function_pointer = requires() //
{
requires std::is_pointer_v<T>;
requires std::is_function_v<std::remove_pointer_t<T>>;
concept function_signature = requires() {
requires not std::is_pointer_v<T>;
requires std::is_function_v<T>;
};

template <typename T>
concept is_function = requires(T value) //
{
[]<typename F>(std::function<F> &) {
}(value);
concept function_pointer = requires() {
requires std::is_pointer_v<T> or std::is_reference_v<T>;
requires std::is_function_v<std::remove_pointer_t<std::remove_reference_t<T>>>;
};

template <typename T>
concept lambda_like = requires() //
{
concept address = requires() { requires std::integral<T> or function_pointer<T>; };

template <typename T>
concept lambda_like = requires() {
requires not address<T>;
requires not is_function<T>;
requires not function_pointer<T>;
requires not std::assignable_from<T, T>;
requires not is_std_function<T>;
};

template <typename T, typename Signature>
consteval auto can_invoke();

template <typename T, typename Signature>
concept invocable_lambda_like = requires() { requires lambda_like<T> and can_invoke<T, Signature>(); };
} // namespace detail

enum class hook_error
Expand Down Expand Up @@ -76,12 +74,12 @@ namespace lime
[[nodiscard]] static rtn_t create(std::uintptr_t, std::uintptr_t);
};

template <typename Signature>
template <detail::function_signature Signature, convention Convention = convention::automatic>
class hook : public hook_base
{
template <template <typename...> typename T>
using rtn_t = tl::expected<T<hook>, hook_error>;
using signature_t = std::conditional_t<std::is_pointer_v<Signature>, Signature, Signature *>;
using signature_t = std::add_pointer_t<typename detail::calling_convention<Signature, Convention>::add>;

public:
signature_t original() const;
Expand All @@ -91,9 +89,8 @@ namespace lime
[[nodiscard]] static rtn_t<std::unique_ptr> create(Source source, Target target);

public:
template <typename Callable>
requires detail::invocable_lambda_like<Callable, Signature>
static rtn_t<std::add_pointer_t> create(detail::address auto source, Callable &&target);
template <detail::address Source, detail::lambda_like Callable>
static rtn_t<std::add_pointer_t> create(Source source, Callable &&target);
};

template <detail::function_pointer Signature, typename Callable>
Expand Down
62 changes: 16 additions & 46 deletions include/lime/hooks/hook.inl
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,18 @@

#include "hook.hpp"

#include <cassert>
#include <boost/callable_traits.hpp>

namespace lime
{
template <typename Lambda, typename Signature>
consteval auto detail::can_invoke()
{
using args_t = boost::callable_traits::args_t<Signature>;
using rtn_t = boost::callable_traits::return_type_t<Signature>;

return std::apply(
[]<typename... T>(T &&...)
{
if constexpr (!std::invocable<Lambda, hook<Signature> *, T...>)
{
return false;
}
else
{
return std::is_same_v<std::invoke_result_t<Lambda, hook<Signature> *, T...>, rtn_t>;
}
},
args_t{});
}

template <typename Signature>
typename hook<Signature>::signature_t hook<Signature>::original() const
template <detail::function_signature Signature, convention Convention>
typename hook<Signature, Convention>::signature_t hook<Signature, Convention>::original() const
{
return reinterpret_cast<signature_t>(hook_base::original());
}

template <typename Signature>
template <detail::function_signature Signature, convention Convention>
template <detail::address Source, detail::address Target>
hook<Signature>::rtn_t<std::unique_ptr> hook<Signature>::create(Source source, Target target)
hook<Signature, Convention>::rtn_t<std::unique_ptr> hook<Signature, Convention>::create(Source source,
Target target)
{
auto source_address = reinterpret_cast<std::uintptr_t>(source);
auto target_address = reinterpret_cast<std::uintptr_t>(target);
Expand All @@ -52,28 +29,21 @@ namespace lime
return std::unique_ptr<hook>{static_cast<hook *>(ptr)};
}

template <typename Signature>
template <typename Callable>
requires detail::invocable_lambda_like<Callable, Signature>
hook<Signature>::rtn_t<std::add_pointer_t> hook<Signature>::create(detail::address auto source, Callable &&target)
template <detail::function_signature Signature, convention Convention>
template <detail::address Source, detail::lambda_like Callable>
hook<Signature, Convention>::rtn_t<std::add_pointer_t> hook<Signature, Convention>::create(Source source,
Callable &&target)
{
using args_t = boost::callable_traits::args_t<Signature>;
using rtn_t = boost::callable_traits::return_type_t<Signature>;

static hook<Signature> *rtn;
static hook<Signature, Convention> *rtn;
static auto lambda = std::forward<Callable>(target);

static constexpr auto wrapper = std::apply(
[]<typename... T>(T &&...)
{
return [](T... args) -> rtn_t
{
return lambda(rtn, std::forward<T>(args)...);
};
},
args_t{});
static constexpr auto dispatch = []<typename... T>(T &&...args)
{
return lambda(rtn, std::forward<T>(args)...);
};

auto result = create(source, +wrapper);
auto wrapper = detail::calling_convention<Signature, Convention>::template wrapper<dispatch>;
auto result = create(source, wrapper);

if (!result)
{
Expand Down

0 comments on commit 1b22836

Please sign in to comment.