【发布时间】:2020-11-27 09:33:35
【问题描述】:
我想编写一个小的 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 具有相同签名和返回类型的函数的指针,但也有一些简单的日志记录代码(可以更花哨,但这也不是问题的重点)。
问题在于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);
}
但我无法在宏中生成它,因为我无法检索参数和返回类型。
可以使用 C++20,如果需要也可以使用 GCC 指令。
【问题讨论】:
-
The problem is that XOpenDisplay_ptr is a pointer to a function是的,所以一般来说,你的方法是有缺陷的,而不是要走的路。because I cannot retrieve the arguments and return type.那么,写GEN_WRAPPER(Display *, XOpenDisplay, const char *, display_name)就这么多吗?这不像功能会改变。为/usr/include/X11/Xlib.h编写一个简短的解析器来提取所有函数声明并生成你的包装器应该不会花费那么长时间。 -
解决实际问题当然不难。我还要求学习该语言。
-
返回类型并不是真正的问题(一些自定义类型特征可以完成这项工作),参数的问题是在 MACRO 时知道计数(我们只在编译时知道)。 KamilCuk 建议在
GEN_WRAPPER中传递额外的类型,我有GEN_WRAPPER_1的解决方案
标签: c++ ld-preload