GCC和MSVC的INITIALIZER的实现

熟悉linux内核编程的应该知道内核模块有一个宏叫 module_init,当内核模块被静态编译到内核后,会在内核init阶段调用每个被module_init声明过的函数。这是如何实现的呢?其实是用到了链接器的特性。具体可参考

https://blog.csdn.net/lu_embedded/article/details/51432616

大致就是告诉连接器将函数指针放到一个特定的程序段,然后在需要的时候遍历这个程序段,拿到每个函数指针,然后调用。

那么MSVC有没有这样的特性呢,搜索了一番,答案是有的,具体可参考

https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?redirectedfrom=MSDN&view=msvc-160

原理基本上是一样的,只不过是调用时机是VC runtime。

网上还找到了利用gcc runtime 和 vc runtime实现的跨平台INITIALIZER

    // Initializer/finalizer sample for MSVC and GCC/Clang.
    // 2010-2016 Joe Lowe. Released into the public domain.
#include <stdio.h>
#include <stdlib.h>

#ifdef __cplusplus
    #define INITIALIZER(f) \
        static void f(void); \
        struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
        static void f(void)
#elif defined(_MSC_VER)
    #pragma section(".CRT$XCU",read)
    #define INITIALIZER2_(f,p) \
        static void f(void); \
        __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
        __pragma(comment(linker,"/include:" p #f "_")) \
        static void f(void)
    #ifdef _WIN64
        #define INITIALIZER(f) INITIALIZER2_(f,"")
    #else
        #define INITIALIZER(f) INITIALIZER2_(f,"_")
    #endif
#else
    #define INITIALIZER(f) \
        static void f(void) __attribute__((constructor)); \
        static void f(void)
#endif

static void finalize(void)
{
    printf( "finalize\n");
}

INITIALIZER( initialize)
{
    printf( "initialize\n");
    atexit( finalize);
}

int main( int argc, char** argv)
{
    printf( "main\n");
    return 0;
}

另外,我也实现了一个简单的MSVC版本,如下。

#include <windows.h>

//以下内容可放到公共头文件
#pragma section(".MYINIT$A", read)
#pragma section(".MYINIT$P", read)
#pragma section(".MYINIT$Z", read)
typedef void (*PFN_INIT)(void);

#define _INIT_FN(fn, pre) \
    static void fn(void); \
    __declspec(allocate(".MYINIT$P")) PFN_INIT fn##_ = fn; \
    __pragma(comment(linker,"/include:" pre #fn "_")) \
    static void fn(void)
#ifdef _WIN64
#define INIT_FN(fn) _INIT_FN(fn, "")
#else
#define INIT_FN(fn) _INIT_FN(fn, "_")
#endif

//以下内容放到 main 文件
__declspec(allocate(".MYINIT$A")) int __myinit_a = 0;
__declspec(allocate(".MYINIT$Z")) int __myinit_z = 0;

void CallInitFn(void)
{
    PFN_INIT *start = (PFN_INIT *)&__myinit_a;
    PFN_INIT *end = (PFN_INIT *)&__myinit_z;

    while(start < end)
    {
        if(*start)
        {
            (*start)();
        }
        start++;
    }
}

int main(int argc, char *argv[])
{
    CallInitFn();
    Sleep(1000);
}

//以下INIT函数可分散放到不同的源文件
INIT_FN(init3)
{
    OutputDebugStringA("init3\n");
}

INIT_FN(init4)
{
    OutputDebugStringA("init4\n");
}

猜你喜欢

转载自blog.csdn.net/MengXP/article/details/110918062
今日推荐