C++:定义一个不是函数指针的函数别名
C++: define a function alias which is not a function pointer
我想写一个小的 LD_PRELOAD
库来拦截和记录 X11 调用(X11 在这里并不重要:选择您想要记录调用的任何其他库)。我想设计一个宏 GEN_WRAPPER
自动为某个调用创建包装器。这是我目前的尝试:
#include <iostream>
#include <X11/Xlib.h>
#include <dlfcn.h>
#define GEN_WRAPPER(x) \
template<typename Func> \
struct wrapper_ ## x; \
template<typename T, typename... S> \
struct wrapper_ ## x<T(S...)> { \
static T exec(S... s) { \
static decltype(x) *orig = reinterpret_cast<decltype(orig)>(dlsym(RTLD_NEXT, #x)); \
std::cout << "Calling " #x; \
const auto ret = orig(std::move(s)...); \
std::cout << " -> " << ret << std::endl; \
return ret; \
} \
}; \
decltype(x) *x ## _ptr = wrapper_ ## x<decltype(x)>::exec;
/*decltype(x) x = wrapper_ ## x<decltype(x)>::exec;*/
GEN_WRAPPER(XOpenDisplay)
(忽略本例完美转发相关的问题,不是重点)
如果我调用 GEN_WRAPPER(XOpenDisplay)
,宏会生成一个名为 XOpenDisplay_ptr
的变量,它是一个指向与 XOpenDisplay
具有相同签名和 return 类型的函数的指针,但也有一些简单的日志记录代码(可以做得更花哨,但这也不是问题的重点)。
问题是XOpenDisplay_ptr
是指向函数的指针,不是函数。从 C++ 代码的角度来看,这几乎是一回事,但如果我想在 LD_PRELOAD
-ed 库中使用它,我还需要 ABI 兼容性,当然函数和函数之间的 ABI 是不同的指向函数的指针。我需要的是将函数 XOpenDisplay
定义为静态函数 wrapper_XOpenDisplay<decltype(XOpenDisplay)>::exec
(宏已经生成)的别名。类似于注释行,除了注释行不是有效的 C++。
从概念上讲(至少对于体系结构 x86_64),编译器要做的事情非常简单:它只需生成函数 XOpenDisplay
和一个简单的 jmp
包装函数(或复制它的主体)。但我不知道如何用 C++ 表达这一点,如果可能的话。
我知道的唯一解决方案是像这样显式编写函数:
Display *XOpenDisplay(const char *display_name) {
return wrapper_XOpenDisplay<decltype(XOpenDisplay)>::exec(display_name);
}
但我无法在宏中生成它,因为我无法检索参数和 return 类型。
可以使用 C++20,如果需要也可以使用 GCC 指令。
由于缺乏更好的答案,我提出了以下解决方案,它依赖于编译器、体系结构和 ABI,但在我的案例中完成了工作。
#if defined(__i386__)
#define JUMP_EAX "jmp *%eax"
#elif defined(__x86_64__)
#define JUMP_EAX "jmp *%rax"
#else
#error "Architecture unsupported"
#endif
#define GEN_WRAPPER(x) \
namespace gio::wrapper { \
template<typename Func> \
struct wrapper_ ## x; \
template<typename T, typename... S> \
struct wrapper_ ## x<T(S...)> { \
static inline T exec(S... s) noexcept { \
std::cout << "Backtrace for " #x << std::endl; \
backtrace(); \
const static decltype(x) *orig = reinterpret_cast<decltype(orig)>(dlsym(RTLD_NEXT, #x)); \
std::cout << #x "("; \
printer<S...>()(s...); \
std::cout << ")"; \
std::cout.flush(); \
const auto ret = orig(std::move(s)...); \
std::cout << " = "; \
print_one<T>()(ret); \
std::cout << std::endl; \
return ret; \
} \
}; \
__attribute__((unused)) static decltype(x) *__get_ ## x ## _ptr() asm("__get_" #x "_ptr"); \
static decltype(x) *__get_ ## x ## _ptr() { \
return &wrapper_ ## x<decltype(x)>::exec; \
} \
__asm__ (".global " #x "\n" \
#x ":\n" \
"call __get_" #x "_ptr\n" \
JUMP_EAX "\n"); \
}
我想写一个小的 LD_PRELOAD
库来拦截和记录 X11 调用(X11 在这里并不重要:选择您想要记录调用的任何其他库)。我想设计一个宏 GEN_WRAPPER
自动为某个调用创建包装器。这是我目前的尝试:
#include <iostream>
#include <X11/Xlib.h>
#include <dlfcn.h>
#define GEN_WRAPPER(x) \
template<typename Func> \
struct wrapper_ ## x; \
template<typename T, typename... S> \
struct wrapper_ ## x<T(S...)> { \
static T exec(S... s) { \
static decltype(x) *orig = reinterpret_cast<decltype(orig)>(dlsym(RTLD_NEXT, #x)); \
std::cout << "Calling " #x; \
const auto ret = orig(std::move(s)...); \
std::cout << " -> " << ret << std::endl; \
return ret; \
} \
}; \
decltype(x) *x ## _ptr = wrapper_ ## x<decltype(x)>::exec;
/*decltype(x) x = wrapper_ ## x<decltype(x)>::exec;*/
GEN_WRAPPER(XOpenDisplay)
(忽略本例完美转发相关的问题,不是重点)
如果我调用 GEN_WRAPPER(XOpenDisplay)
,宏会生成一个名为 XOpenDisplay_ptr
的变量,它是一个指向与 XOpenDisplay
具有相同签名和 return 类型的函数的指针,但也有一些简单的日志记录代码(可以做得更花哨,但这也不是问题的重点)。
问题是XOpenDisplay_ptr
是指向函数的指针,不是函数。从 C++ 代码的角度来看,这几乎是一回事,但如果我想在 LD_PRELOAD
-ed 库中使用它,我还需要 ABI 兼容性,当然函数和函数之间的 ABI 是不同的指向函数的指针。我需要的是将函数 XOpenDisplay
定义为静态函数 wrapper_XOpenDisplay<decltype(XOpenDisplay)>::exec
(宏已经生成)的别名。类似于注释行,除了注释行不是有效的 C++。
从概念上讲(至少对于体系结构 x86_64),编译器要做的事情非常简单:它只需生成函数 XOpenDisplay
和一个简单的 jmp
包装函数(或复制它的主体)。但我不知道如何用 C++ 表达这一点,如果可能的话。
我知道的唯一解决方案是像这样显式编写函数:
Display *XOpenDisplay(const char *display_name) {
return wrapper_XOpenDisplay<decltype(XOpenDisplay)>::exec(display_name);
}
但我无法在宏中生成它,因为我无法检索参数和 return 类型。
可以使用 C++20,如果需要也可以使用 GCC 指令。
由于缺乏更好的答案,我提出了以下解决方案,它依赖于编译器、体系结构和 ABI,但在我的案例中完成了工作。
#if defined(__i386__)
#define JUMP_EAX "jmp *%eax"
#elif defined(__x86_64__)
#define JUMP_EAX "jmp *%rax"
#else
#error "Architecture unsupported"
#endif
#define GEN_WRAPPER(x) \
namespace gio::wrapper { \
template<typename Func> \
struct wrapper_ ## x; \
template<typename T, typename... S> \
struct wrapper_ ## x<T(S...)> { \
static inline T exec(S... s) noexcept { \
std::cout << "Backtrace for " #x << std::endl; \
backtrace(); \
const static decltype(x) *orig = reinterpret_cast<decltype(orig)>(dlsym(RTLD_NEXT, #x)); \
std::cout << #x "("; \
printer<S...>()(s...); \
std::cout << ")"; \
std::cout.flush(); \
const auto ret = orig(std::move(s)...); \
std::cout << " = "; \
print_one<T>()(ret); \
std::cout << std::endl; \
return ret; \
} \
}; \
__attribute__((unused)) static decltype(x) *__get_ ## x ## _ptr() asm("__get_" #x "_ptr"); \
static decltype(x) *__get_ ## x ## _ptr() { \
return &wrapper_ ## x<decltype(x)>::exec; \
} \
__asm__ (".global " #x "\n" \
#x ":\n" \
"call __get_" #x "_ptr\n" \
JUMP_EAX "\n"); \
}