2016-09-16 17:45:35 +02:00
|
|
|
#ifndef ELFSPY_HOOK_H
|
|
|
|
#define ELFSPY_HOOK_H
|
|
|
|
|
|
|
|
#include "elfspy/HookImpl.h"
|
|
|
|
#include "elfspy/GOTEntry.h"
|
|
|
|
#include "elfspy/Function.h"
|
|
|
|
#include "elfspy/MethodInfo.h"
|
|
|
|
#include "elfspy/Thunk.h"
|
|
|
|
|
|
|
|
namespace spy
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @namespace spy
|
|
|
|
* @class Hook
|
|
|
|
* An instance is a function that is executed after a call to a
|
|
|
|
* function/method and before it is run, essentially injected between the two.
|
|
|
|
* It is very lightweight as it only holds reference(s) to static data members.
|
|
|
|
* Calling convention is unified between methods and functions so that methods
|
|
|
|
* are functions where the first type in Args... is the class type.
|
|
|
|
*/
|
|
|
|
|
|
|
|
template <typename CRTP, typename ReturnType, typename... ArgTypes>
|
|
|
|
class Hook : public HookImpl<CRTP, ReturnType, ArgTypes...>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using Result = ReturnType;
|
|
|
|
/**
|
|
|
|
* typedefs to re-export variadic ArgTypes to other variadic templates
|
|
|
|
*/
|
|
|
|
template <template <typename...> class Other, typename... Extra>
|
|
|
|
struct Export
|
|
|
|
{
|
|
|
|
using Type = Other<Extra..., ArgTypes...>;
|
|
|
|
};
|
|
|
|
template <template <size_t, typename...> class Other>
|
|
|
|
struct ExportN
|
|
|
|
{
|
|
|
|
template <size_t N>
|
|
|
|
using Type = Other<N, ArgTypes...>;
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* construct from function pointer
|
|
|
|
*/
|
2016-09-17 22:46:34 +02:00
|
|
|
Hook(const char* name, ReturnType (*function)(ArgTypes...));
|
2016-09-16 17:45:35 +02:00
|
|
|
/**
|
|
|
|
* construct from method pointer
|
|
|
|
*/
|
2016-09-17 22:46:34 +02:00
|
|
|
Hook(const char* name, MethodInfo<ReturnType, ArgTypes...> method);
|
2016-09-16 17:45:35 +02:00
|
|
|
/// restore original function
|
|
|
|
~Hook();
|
|
|
|
/**
|
|
|
|
* invoke the original function
|
|
|
|
* @param args arguments to pass
|
|
|
|
* @return return value of function
|
|
|
|
*/
|
|
|
|
static ReturnType invoke_real(ArgTypes... args);
|
|
|
|
|
|
|
|
operator const GOTEntry&() const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
static GOTEntry got_entry_;
|
2016-09-17 22:46:34 +02:00
|
|
|
static const char* name_;
|
2016-09-16 17:45:35 +02:00
|
|
|
using Base = HookImpl<CRTP, ReturnType, ArgTypes...>;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename CRTP, typename ReturnType, typename... ArgTypes>
|
|
|
|
GOTEntry Hook<CRTP, ReturnType, ArgTypes...>::got_entry_;
|
2016-09-17 22:46:34 +02:00
|
|
|
template <typename CRTP, typename ReturnType, typename... ArgTypes>
|
|
|
|
const char* Hook<CRTP, ReturnType, ArgTypes...>::name_ = "no name";
|
2016-09-16 17:45:35 +02:00
|
|
|
|
|
|
|
template <typename CRTP, typename ReturnType, typename... ArgTypes>
|
2016-09-17 22:46:34 +02:00
|
|
|
Hook<CRTP, ReturnType, ArgTypes...>::Hook(const char* name,
|
|
|
|
ReturnType (*function)(ArgTypes...))
|
2016-09-16 17:45:35 +02:00
|
|
|
{
|
2016-09-17 22:46:34 +02:00
|
|
|
name_ = name;
|
2016-09-16 17:45:35 +02:00
|
|
|
// use union to get and convert address of function
|
|
|
|
Function<ReturnType, ArgTypes...> converter;
|
|
|
|
converter.address_ = function;
|
2016-09-17 22:46:34 +02:00
|
|
|
got_entry_.set(converter.pointer_, name);
|
2016-09-16 17:45:35 +02:00
|
|
|
converter.address_ = &Base::thunk;
|
|
|
|
converter.pointer_ = got_entry_.spy_with(converter.pointer_);
|
|
|
|
Base::patch_ = Base::real_ = converter.address_;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CRTP, typename ReturnType, typename... ArgTypes>
|
|
|
|
inline Hook<CRTP, ReturnType, ArgTypes...>::
|
2016-09-17 22:46:34 +02:00
|
|
|
Hook(const char* name, MethodInfo<ReturnType, ArgTypes...> method)
|
2016-09-16 17:45:35 +02:00
|
|
|
{
|
2016-09-17 22:46:34 +02:00
|
|
|
name_ = name;
|
|
|
|
got_entry_.set(method.address_, name);
|
2016-09-16 17:45:35 +02:00
|
|
|
if (method.vtable_entry_) {
|
|
|
|
got_entry_.make_entry(method.vtable_entry_);
|
|
|
|
}
|
|
|
|
Function<ReturnType, ArgTypes...> converter;
|
|
|
|
converter.address_ = &Base::thunk;
|
|
|
|
converter.pointer_ = got_entry_.spy_with(converter.pointer_);
|
|
|
|
Base::patch_ = Base::real_ = converter.address_;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CRTP, typename ReturnType, typename... ArgTypes>
|
|
|
|
inline Hook<CRTP, ReturnType, ArgTypes...>::~Hook()
|
|
|
|
{
|
|
|
|
got_entry_.restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CRTP, typename ReturnType, typename... ArgTypes>
|
|
|
|
inline ReturnType Hook<CRTP, ReturnType, ArgTypes...>::invoke_real(ArgTypes... args)
|
|
|
|
{
|
|
|
|
return (*Base::real_)(args...);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CRTP, typename ReturnType, typename... ArgTypes>
|
|
|
|
inline Hook<CRTP, ReturnType, ArgTypes...>::operator const GOTEntry&() const
|
|
|
|
{
|
|
|
|
return got_entry_;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace spy
|
|
|
|
|
|
|
|
#endif
|