x264内核移植
本文简要介绍了将x264编码器移植到Windows内核态,编译成内核dll的方法。
编译x264
由于x264使用到大量gcc和C99的特性,想要在Windows下编译,必须使用mingw环境(Visual Studio也可以,但需要改动和删除一些代码)。
编译步骤如下:
通过配置选项,可以编译出x264.exe和x264的dll库。接下来介绍如何将x264编译为内核dll,也就是内核态下的动态链接库,以.sys文件形式存在。
内核dll
Tim Roberts在“DLLs in Kernel Mode”中介绍了构建内核dll的方法。在此引用并节选翻译。
内核dll和用户态dll的使用是类似的,在连接时包含一个导入库,这样,运行时就可以使用对应动态链接库的函数了。
内核dll与普通的内核态程序最大的区别在于,sources文件中的TARGETTYPE。为了创建一个内核dll,我们需要在sources文件中指定宏:TARGETTYPE=EXPORT_DRIVER
。
然后,内核dll必须包含一个DriverEntry,实际上该入口点不会被调用到,但为了编译通过,我们必须创建一个。
NTSTATUS
DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
return STATUS_SUCCESS;
}
如果需要在dll加载和卸载的时候做一些处理,那么我们需要加上DllInitialize和DllUnload这两个函数,在这里,我们且加个空函数备用。
NTSTATUS
DllInitialize(IN PUNICODE_STRING RegistryPath)
{
return STATUS_SUCCESS;
}
NTSTATUS
DllUnload(void)
{
return STATUS_SUCCESS;
}
除了以上三个函数外,我们还可以导出其他函数,可以在.def文件中添加函数名,或是在函数定义和声明时加上__declspec(dllexport)
前缀。当然,我们需要在sources文件中使用宏来指定.def文件的名字:DLLDEF=xxx.def
。
需要注意的是,DllInitialize和DllUnload必须标记为PRIVATE导出。
以上就是构建内核dll的基本要点,其他细节与普通的内核态程序基本一样。接下来介绍如何将x264编译为内核dll。
x264的内核移植
由于x264代码中有大量的gcc和C99特性,不便直接使用WDK环境进行编译,于是我们考虑使用mingw的gcc进行编译,然后使用WDK环境进行连接,生成.sys文件。编译的过程和在用户态下编译x264没有什么区别。
需要注意的是,有些函数在内核态是无法使用的,如文件操作和内存操作方面的函数,这时候,则需要我们找到替代函数或自己实现其功能。
在实现和替代了相关函数之后,需要为导出函数的声明和定义添加__declspec(dllexport) __stdcall
,由于WDK默认的调用约定是__stdcall,与gcc默认的不一致,此处需要添加__stdcall声明。
修改代码后,将编译得到的.o文件添加到sources文件的TARGETLIBS宏列表中,并添加一些必须的库,如libgcc.a,libquadmath.a等。
添加完成后,使用WDK环境进行build即可。
调试记录
初始化时蓝屏,用windbg打开dmp发现挂在了x264_cqm_init+0x10里,然后用IDA反汇编x264.sys,发现此处是调用了chkstk_ms。原因是,在局部变量超过4K时,gcc会自动加入堆栈检查代码。在执行chkstk_ms时,会扩充堆栈大小(过程中会触发page fault),
而内核中栈最大只能为4K,对于x86,内核栈最大只能为12K(x64是24K)。解决方法:将局部变量改为全局变量、静态变量或动态分配。
__chkstk_ms:
/* EAX = size to be allocated */
push ecx
push eax
/* ECX = top of the previous stack frame */
lea ecx, [esp + 12]
/* probe the desired memory, page by page */
cmp eax, PAGE_SIZE
jl .l_LessThanAPage
.l_MoreThanAPage:
/* raise the top of the stack by a page and probe */
sub ecx, PAGE_SIZE
test [ecx], eax
/* loop if still more than a page must be probed */
sub eax, PAGE_SIZE
cmp eax, PAGE_SIZE
jge .l_MoreThanAPage
.l_LessThanAPage:
/* raise the top of the stack by EAX bytes (size % 4096) and probe */
sub ecx, eax
test [ecx], eax
pop eax
pop ecx
ret
References
DLLs in Kernel Mode - Tim Roberts
浅谈如何使用gcc开发NT核心驱动
使用VC创建Windows NT下的内核dll和lib
设置编译内核lib驱动及应用层dll的sources文件
Win32 driver development using MinGW
Calling a DLL in a Kernel-Mode Driver
WinKVM: Windows Kernel-based Virtual Machine
chkstk_ms实现(x64和x86)
从_chkstk说起,谈谈用户栈的管理
Post Info
- Copyright Notice: Creative Commons BY-NC-ND 3.0