V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
waruqi
V2EX  ›  程序员

xmake 新增对 WDK 驱动开发环境支持

  •  
  •   waruqi ·
    waruqi · 2018-06-15 14:21:09 +08:00 · 1980 次点击
    这是一个创建于 2357 天前的主题,其中的信息可能已经有所发展或是发生改变。

    xmake v2.2.1 新版本现已支持 WDK 驱动编译环境,我们可以直接在系统原生 cmd 终端下,执行 xmake 进行驱动编译,甚至配合 vscode, sublime text, IDEA 等编辑器+xmake 插件去开发 WDK 驱动。

    下面是 xmake 支持的一些编辑器插件,用户可以挑选自己喜欢的编辑器配合 xmake 来使用:

    WDK 环境介绍

    首先,我们先简单介绍下 WDK10 的编译环境的安装方式,我们可以看下微软的官方文档:Download the Windows Driver Kit (WDK)

    里面介绍了两种环境:

    1. 下载 WDK 开发包,直接安装到系统并集成到 VS 的开发环境中
    2. 下载 EWDK iso 镜像(内含完整 WDK 开发环境),直接挂载后,运行 LaunchBuildEnv 进入 cmd 环境

    xmake 对于这两种环境都是完全支持的,如果用户直接下载安装 WDK 环境到本地系统,那么不需要任何配置,只需要执行:

    $ xmake
    

    xmake 会自动检测到 WDK 的安装环境,然后编译相关驱动项目,如果用户是直接挂载的 EWDK iso 开发镜像,那么编译前配置下 WDK 所在路径即可:

    $ xmake f --wdk="G:\Program Files\Windows Kits\10" 
    $ xmake
    

    更多详情可以参考:#159

    WDK 驱动实例

    xmake 支持 umdf, kmdf, wdm 驱动项目的维护,也是采用一系列扩展的 WDK rule 规则来实现,类似:Qt 编译环境的支持

    目前支持的规则有如下这些:

    • rule("wdk.driver")

    • rule("wdk.binary")

    • rule("wdk.static")

    • rule("wdk.shared")

    • rule("wdk.env.kmdf")

    • rule("wdk.env.umdf")

    • rule("wdk.env.wdm")

    其中,wdk.env.*规则描述驱动编译的环境,wdk.driver, wdk.static描述编译的目标类型,两者可以互相结合使用,我们既可以用来编译驱动程序,也可以用来编译基于 wdk 环境的静态库、可执行程序。

    下面,通过一些例子可以简单看下使用方式,具体例子代码见wdk-examples,其中的项目代码是从Windows-driver-samples移植过来的。

    umdf 驱动程序

    我们通过同时应用wdk.driver, wdk.env.umdf规则,来描述这个 target 作为 umdf 驱动程序来编译:

    target("echo")
        add_rules("wdk.driver", "wdk.env.umdf")
        add_files("driver/*.c") 
        add_files("driver/*.inx")
        add_includedirs("exe")
    

    我们也可以通过wdk.binary, wdk.env.umdf规则,来描述一个基于 wdk/umdf 编译环境的上层可执行程序:

    target("app")
        add_rules("wdk.binary", "wdk.env.umdf")
        add_files("exe/*.cpp") 
    

    kmdf 驱动程序

    kmdf 的项目描述跟刚才的 umdf 类似,只需要把wdk.env.umdf换成wdk.env.kmdf的环境规则就行了。

    target("nonpnp")
        add_rules("wdk.driver", "wdk.env.kmdf")
        add_values("wdk.tracewpp.flags", "-func:TraceEvents(LEVEL,FLAGS,MSG,...)")
        add_values("wdk.tracewpp.flags", "-func:Hexdump((LEVEL,FLAGS,MSG,...))")
        add_files("driver/*.c", {rule = "wdk.tracewpp"}) 
        add_files("driver/*.rc")
    
    target("app")
        add_rules("wdk.binary", "wdk.env.kmdf")
        add_files("exe/*.c") 
        add_files("exe/*.inf")
    

    这个项目里面,需要特别注意的是,我们还用到了 tracewpp 对一些源文件的预处理,对于 tracewpp 任务的介绍,可以看下官方文档tracewpp-task,这里就不多做说明了。

    我们直接说下,如何在 xmake 的项目里应用 tracewpp 规则吧,由于这个规则并不是对当前 target 所有源文件都去处理的,因此我们只对需要的源文件进行应用这个规则,例如:

    add_files("driver/*.c", {rule = "wdk.tracewpp"}) 
    add_files("driver/dir/test.c", {rule = "wdk.tracewpp"}) 
    

    当然 tracewpp 还会有一些自己的特殊选项,用户有时候需要自己根据需要来设置,例如:

    add_values("wdk.tracewpp.flags", "-func:TraceEvents(LEVEL,FLAGS,MSG,...)")
    add_values("wdk.tracewpp.flags", "-func:Hexdump((LEVEL,FLAGS,MSG,...))")
    

    关于add_values的使用说明,可以看下文档:add_values 和 set_values 的使用说明,简单来说,就是用来给对应规则传递扩展参数设置的。

    wdm 驱动程序

    wdm 的项目描述也跟 umdf 类似,只需要把wdk.env.umdf换成wdk.env.wdm的环境规则就行了。

    target("kcs")
        add_rules("wdk.driver", "wdk.env.wdm")
        add_values("wdk.man.flags", "-prefix Kcs")
        add_values("wdk.man.resource", "kcsCounters.rc")
        add_values("wdk.man.header", "kcsCounters.h")
        add_values("wdk.man.counter_header", "kcsCounters_counters.h")
        add_files("*.c", "*.rc", "*.man") 
    

    上述代码,还添加了一些.man 文件用来预处理一些 manifest,具体相关的任务藐视,可以参考官方文档说明:ctrpp-task。 里面也有相关的一些特殊配置选项,目前 xmake 支持的配置有:

    add_values("wdk.man.flags", "-prefix Kcs")
    add_values("wdk.man.prefix", "Kcs")
    add_values("wdk.man.resource", "kcsCounters.rc")
    add_values("wdk.man.header", "kcsCounters.h")
    add_values("wdk.man.counter_header", "kcsCounters_counters.h")
    

    下面再贴个 wdm 驱动的例子,这个例子中,除了之前讲的 tracewpp,我们还加了.mof 的文件处理,对于.mof 文件,xmake 会自动应用内置的wdk.mof规则,详细说明见:mofcomp-task

    target("msdsm")
        add_rules("wdk.driver", "wdk.env.wdm")
        add_values("wdk.tracewpp.flags", "-func:TracePrint((LEVEL,FLAGS,MSG,...))")
        add_files("*.c", {rule = "wdk.tracewpp"}) 
        add_files("*.rc", "*.inf")
        add_files("*.mof|msdsm.mof")
        add_files("msdsm.mof", {values = {wdk_mof_header = "msdsmwmi.h"}}) 
    

    对于.mof 的配置选项,有些配置并不是全局应用于 target 的,对每个文件需要单独配置,这个时候,就不能直接使用set_valuesadd_values了,需要在add_files中设置相关 values。

    add_files("msdsm.mof", {values = {wdk_mof_header = "msdsmwmi.h"}}) 
    add_files("msdsm.mof", {values = {["wdk.mof.header"] = "msdsmwmi.h"}}) 
    

    上面两种设置方式都是有效的,由于受限于 lua 的语法,为了考虑可读性,xmake 通过_下划线来简化 key 的设置,这个设置相当于单独对 msdsm.mof 文件设置了set_values("wdk.mof.header", "msdsmwmi.h")

    生成驱动包

    如果平常开发调试通过后,我们也可以通过以下命令生成.cab 驱动包来发布驱动程序:

    $ xmake [p|package]
    $ xmake [p|package] -o outputdir
    

    输出的目录结构如下:

      - drivers
        - sampledsm
           - debug/x86/sampledsm.cab
           - release/x64/sampledsm.cab
           - debug/x86/sampledsm.cab
           - release/x64/sampledsm.cab
    

    驱动签名

    默认编译我们是禁用签名的,如果想要在编译的同时,启用签名,可以通过set_values("wdk.sign.mode", ...)设置签名模式来启用。 只要启用了签名,那么平常的驱动构建、包括打包生成的.cab 文件,都会自动对其进行签名。

    测试签名

    测试签名一般本机调试时候用,可以使用 xmake 自带的 test 证书来进行签名,例如:

    target("msdsm")
        add_rules("wdk.driver", "wdk.env.wdm")
        set_values("wdk.sign.mode", "test")
        add_files("src/*.c")
    

    不过这种情况下,需要用户手动在管理员模式下,执行一遍:$xmake l utils.wdk.testcert install,来生成和注册 test 证书到本机环境。 这个只需要执行一次就行了,后续就可以正常编译和签名了。

    当然也可以使用本机已有的有效证书去签名,例如直接从 sha1 来选择合适的证书进行签名:

    target("msdsm")
        add_rules("wdk.driver", "wdk.env.wdm")
        set_values("wdk.sign.mode", "test")
        set_values("wdk.sign.thumbprint", "032122545DCAA6167B1ADBE5F7FDF07AE2234AAA")
        add_files("src/*.c")
    

    或者从 store/company 来选择合适的证书进行签名:

    target("msdsm")
        add_rules("wdk.driver", "wdk.env.wdm")
        set_values("wdk.sign.mode", "test")
        set_values("wdk.sign.store", "PrivateCertStore")
        set_values("wdk.sign.company", "tboox.org(test)")
        add_files("src/*.c")
    

    正式签名

    对于正式签名,我们可以通过指定对应的正式签名证书文件进行签名:

    target("msdsm")
        add_rules("wdk.driver", "wdk.env.wdm")
        set_values("wdk.sign.mode", "release")
        set_values("wdk.sign.company", "xxxx")
        set_values("wdk.sign.certfile", path.join(os.projectdir(), "xxxx.cer"))
    

    生成低版本驱动

    如果想在 wdk10 环境编译生成 win7, win8 等低版本系统支持的驱动,我们可以通过设置wdk.env.winver来切换系统版本:

    set_values("wdk.env.winver", "win10")
    set_values("wdk.env.winver", "win10_rs3")
    set_values("wdk.env.winver", "win81")
    set_values("wdk.env.winver", "win8")
    set_values("wdk.env.winver", "win7")
    set_values("wdk.env.winver", "win7_sp1")
    set_values("wdk.env.winver", "win7_sp2")
    set_values("wdk.env.winver", "win7_sp3")
    

    如果觉得每次修改 xmake.lua 去切换编译非常繁琐,我们也可以手动指定编译的目标程序支持的 windows 版本,来快速切换到对应的版本进行编译:

    $ xmake f --wdk_winver=[win10_rs3|win8|win7|win7_sp1]
    $ xmake
    

    目前支持的一些版本有:nt4, win2k, winxp, ws03, win6, vista, ws08, longhorn, win7, win8, win81, winblue, win10

    然后通过_下划线,组合指定子版本:sp1, sp2, sp3, th2, rs1, rs2, rs3

    xmake 还提供了一些内置的版本值,在切换 winver 版本是,会自动改变,用于一些更加定制化的配置需求,例如:

    target("test")
        
        on_load(function (target)
            local winnt_version = target:values("wdk.env.winnt_version")
            if winnt_version > "0x0A000000" then
                target:add("defines", "TEST")
            end
        end)
    

    上述代码通过判断 WIN32_WINNT 的版本值,来定制添加一些相关配置,这个版本值会根据wdk.env.winver的配置自动适配更新,目前提供的这些内置版本值还有:

    target:values("wdk.env.winnt_version"): WIN32_WINNT
    target:values("wdk.env.ntddi_version"): NTDDI_VERSION
    target:values("wdk.env.winver_version"): WINVER
    

    关于更多 xmake 下 WDK 开发相关介绍,请参考文档:WDK 驱动程序开发

    2 条回复    2018-06-15 17:40:48 +08:00
    Mapleaph
        1
    Mapleaph  
       2018-06-15 17:17:17 +08:00
    是否支持 WDK7.1 ?
    waruqi
        2
    waruqi  
    OP
       2018-06-15 17:40:48 +08:00
    @Mapleaph 暂时只支持 WDK10,其他版本等有时间可以考虑加上
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2478 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 16:01 · PVG 00:01 · LAX 08:01 · JFK 11:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.