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");                                            \
    }