首先了解下Wine初始化过程。
我们执行”wine WeChat.exe”命令,发生的过程是怎么样的?接下来从wine源码一步步分析函数调用过程。
在loader/目录下的源码编译,由main.c生成了“wine”Linux可执行文件;preloader.c生成了“pre-loader”Linux可执行文件。
假设在终端通过命令“wine WeChat.exe”启动微信;该过程涉及preloader,但是最终会执行到“wine”中的main函数。所以这里从wine的main函数调用wine_init开始分析。
wine_init函数如下图所示:
主要做了以下三件事:
1. 调用了mmap_init()函数,这个函数与预留地址空间相关。
2. 通过wine_dlopen()装入由Wine提供的动态连接库ntdll.dll。由于Wine特殊的系统结构,它不能使用微软的ntdll.dll,而只能使用Wine自己的DLL,这样的DLL称为“内置(built-in)”DLL。
3. 执行内置ntdll.dll中的函数__wine_process_init()。先通过wine_dlsym()取得这个函数的入口地址,然后通过指针init_func进行调用。之所以要以这样的方式调用,是因为这个函数在ntdll.dll中,而wine_dlopen()只是装入了这个DLL、却并未完成与此模块的动态链接。
一般而言,Wine既可以安装使用本身的“内置”动态库,也可以安装使用Windows上相应的“原装(native)”动态库。但是,其中有几个动态库是特殊的,因而只能使用Wine自己的版本,ntdll就是其一。实际上Windows上的ntdll.dll中根本不会有__wine_process_init()这么个函数。Wine自己的“内置”DLL实际上就是Linux的.so模块,是在Linux环境下由gcc产生的。GNU的C库glibc中提供了一组用来处理.so模块的函数,上面的wine_dlopen()和wine_dlsym()最终都是调用这些库函数(例如dlopen()和dlsym())来完成操作。
说明:
linux提供了加载和处理动态链接库的系统调用,dlopen以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程,dlsym通过句柄和连接符名称获取函数名或者变量名。__wine_process_init函数如下图所示:
主要做了以下四件事情:
1. thread_init函数的调用
2. 由load_builtin_dll("kernel32.dll")装入Wine的另一个“内置”动态连接库kernel32.dll。可见kernel32.dll也必须是个“built-in(内置)”DLL。
3. 由LdrGetProcedureAddress()从装入的kernel32.dll映像中取得函数__wine_kernel_init()的入口。
执行kernel32.dll中的函数__wine_kernel_init()。