熟悉linux内核编程的应该知道内核模块有一个宏叫 module_init,当内核模块被静态编译到内核后,会在内核init阶段调用每个被module_init声明过的函数。这是如何实现的呢?其实是用到了链接器的特性。具体可参考
https://blog.csdn.net/lu_embedded/article/details/51432616
大致就是告诉连接器将函数指针放到一个特定的程序段,然后在需要的时候遍历这个程序段,拿到每个函数指针,然后调用。
那么MSVC有没有这样的特性呢,搜索了一番,答案是有的,具体可参考
原理基本上是一样的,只不过是调用时机是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");
}