feat: add linker directive for VHF lib + move static linker directives from wdk-build to wdk-sys - #685
feat: add linker directive for VHF lib + move static linker directives from wdk-build to wdk-sys#685Alan632 wants to merge 41 commits into
Conversation
… directives from wdk-build/src/lib.rs into wdk-sys/build.rs
There was a problem hiding this comment.
Pull request overview
This PR updates how native WDK libraries are linked by moving static rustc-link-lib directives out of wdk-build’s configure_binary_build prints and into wdk-sys’s generated bindings via #[link(...)] attributes. It also adds conditional linker directives for the VHF library when the hid feature is enabled, selecting VhfKm vs VhfUm based on driver model.
Changes:
- Emit base driver-model-specific native library link directives into bindgen output (
ntddk.rs/windows.rs) fromwdk-sys/build.rs. - Add
hid-gated#[link]directives forVhfKm(KMDF/WDM) andVhfUm(UMDF). - Remove the corresponding
cargo::rustc-link-lib=static=*emissions fromwdk-build::Config::configure_binary_build(leaving link-arg prints in place).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| crates/wdk-sys/build.rs | Adds a reusable #[link] directive helper and injects conditional link directives into generated bindings (including VHF for hid). |
| crates/wdk-build/src/lib.rs | Stops emitting static rustc-link-lib lines (now handled by wdk-sys), retaining cdylib link-args and documenting future move. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #685 +/- ##
==========================================
+ Coverage 80.47% 81.06% +0.59%
==========================================
Files 26 26
Lines 5720 5899 +179
Branches 5720 5899 +179
==========================================
+ Hits 4603 4782 +179
Misses 989 989
Partials 128 128 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…ts by utilizing ApiSubset similar to the headers
…indows-drivers-rs into vhf_lib_linker_args-v2
…utes out when building non-driver test wdk-sys-tests (wdk-sys-tests does not call configure_binary_build thus no search paths are emitted and ungated link attributes fail the build/test)
…on, library selection, and link directive string creation) -removed raw strings in lieu of types to tightly constrain LinkDirective creation w/o needing assert checks -removed and compacted unneeded functions from original implementation
… attributes to ensure wdk-sys builds correctly when compiled for test - add unit tests - add PartialEq and Eq traits to LinkKind, LinkModifier, and LinkDirective
Leon Durrenberger (leon-xd)
left a comment
There was a problem hiding this comment.
Great work. Few changes here and there but the overall shape is very solid!
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated no new comments.
Suppressed comments (4)
examples/sample-wdm-driver/src/lib.rs:82
- The new unit test calls
driver_exit(), which useswdk::println!. In WDM/KMDF, that routes throughwdk_sys::ntddk::DbgPrint, but this PR also cfg-suppresses the#[link]directives during tests (cfg(test)/omit-wdk-libs). That combination is likely to produce an unresolved external forDbgPrintwhen runningcargo teston this example. Prefer a test that only references bindgen types/consts (compile+link proof) without calling into WDK APIs.
fn test_driver_exit() {
use super::*;
driver_exit(core::ptr::null_mut())
}
examples/sample-kmdf-driver/src/lib.rs:195
- The new unit test calls
driver_exit(), which useswdk::println!and will ultimately reference WDK symbols (e.g.,DbgPrint) that are no longer linked in test builds due to cfg-suppressed#[link]directives. This is likely to breakcargo testfor this example. Consider switching the test to a pure type/const assertion that still proves the cdylib can host tests without requiring WDK libs.
#[test]
fn test_driver_exit() {
use super::*;
driver_exit(core::ptr::null_mut())
crates/wdk-sys/src/test_stubs.rs:36
- The module docs still say
WdfDriverGlobalsis intentionally not stubbed and that enablingtest-stubsmay fail to link because it’s missing, but this file now defines aWdfDriverGlobalsstub. Update the docs to reflect the current behavior and clarify that calling real WDF APIs is still unsupported under stubs.
//! - If "test-stubs" is enabled the Linker may complain that
//! `WdfDriverGlobals` is missing, it is intentionally not stubbed here
//! because it is needed when a WDF function is called, landing it outside
//! the intent of test-stubs.
tests/mixed-package-kmdf-workspace/crates/driver/Cargo.toml:26
- This comment contradicts the dependency directly below it: it says the "test-stubs" feature isn’t needed and should be switched on later, but the dev-dependency already enables it. Please update/remove the comment so it matches the actual configuration and intent for tests.
# The feature "test-stubs" isn't needed since gating the DriverEntry export_name keeps
# driver_entry from being codegen'd, so nothing references WDF functions. Switch to "test-stubs"
# if a test ever reaches a call_unsafe_wdf_function_binding! call site.
wdk-sys = { features = ["test-stubs"], workspace = true }
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated no new comments.
Suppressed comments (2)
crates/wdk-sys/src/test_stubs.rs:36
- The doc comment says
WdfDriverGlobalsis "intentionally not stubbed", but the module now defines aWdfDriverGlobalsstub below. This is misleading for users enablingtest-stubsand should be updated to match the current behavior (and ideally clarify that calling real WDF functions still requires proper mocks).
//! - If "test-stubs" is enabled the Linker may complain that
//! `WdfDriverGlobals` is missing, it is intentionally not stubbed here
//! because it is needed when a WDF function is called, landing it outside
//! the intent of test-stubs.
tests/mixed-package-kmdf-workspace/crates/driver/Cargo.toml:25
- This comment states that the "test-stubs" feature isn't needed, but the dependency below explicitly enables it. That contradiction makes it unclear whether this dev-dependency is intentional. Either remove the feature flag or update the comment to explain why
test-stubsis enabled for tests.
# The feature "test-stubs" isn't needed since gating the DriverEntry export_name keeps
# driver_entry from being codegen'd, so nothing references WDF functions. Switch to "test-stubs"
# if a test ever reaches a call_unsafe_wdf_function_binding! call site.
Melvin Wang (wmmc88)
left a comment
There was a problem hiding this comment.
minor nits, but otherwise good to go. Also create a bug documenting the all-targets of cdylib defect, and assign it to me
…k-linking" for clarity - add documentation for the cfg gate on driver_entry export_name for the sample drivers - add documentation for the dummy sample driver unit tests
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated no new comments.
Suppressed comments (5)
examples/sample-wdm-driver/src/lib.rs:86
- This unit test calls
driver_exit, which useswdk::println!(routes throughwdk_sys::ntddk::DbgPrint). Whenwdk-sysis built withtest-stubs/suppress-wdk-linking, the generated#[link]directives are suppressed, so tests may fail to link becauseDbgPrintisn't provided. Prefer a test that only references bindgen-generated types/constants (no calls into WDK).
fn test_driver_exit() {
use super::*;
driver_exit(core::ptr::null_mut())
}
examples/sample-kmdf-driver/src/lib.rs:200
- This unit test calls
driver_exit, which useswdk::println!(routes throughwdk_sys::ntddk::DbgPrint). Withwdk-sysbuilt usingtest-stubs/suppress-wdk-linking, the generated#[link]directives are suppressed, so tests may fail to link due to missingDbgPrint. Prefer a test that only references bindgen-generated types/constants (no calls into WDK/WDF).
fn test_driver_exit() {
use super::*;
driver_exit(core::ptr::null_mut())
}
crates/wdk-sys/src/test_stubs.rs:36
- The doc comment still says
WdfDriverGlobalsis intentionally not stubbed, but the module now defines a stubWdfDriverGlobals. This is misleading for users trying to understand whattest-stubsguarantees and what remains unsafe to call in tests.
//! - If "test-stubs" is enabled the Linker may complain that
//! `WdfDriverGlobals` is missing, it is intentionally not stubbed here
//! because it is needed when a WDF function is called, landing it outside
//! the intent of test-stubs.
examples/sample-umdf-driver/src/lib.rs:187
- This unit test calls
evt_driver_unload, which useswdk::println!(routes throughwdk_sys::windows::OutputDebugStringAin UMDF). Withwdk-sysbuilt usingtest-stubs/suppress-wdk-linking, the generated#[link]directives are suppressed, so tests may fail to link if the required system libs aren't linked. Prefer a test that only references bindgen-generated types/constants (no calls into UMDF/WDF).
fn test_driver_exit() {
use super::*;
evt_driver_unload(core::ptr::null_mut())
}
tests/mixed-package-kmdf-workspace/crates/driver/Cargo.toml:26
- The comment says
test-stubsisn't needed and suggests switching to it later, but the dependency below already enablesfeatures = ["test-stubs"]. This is confusing for readers trying to understand why the feature is enabled for tests.
# The feature "test-stubs" isn't needed since gating the DriverEntry export_name keeps
# driver_entry from being codegen'd, so nothing references WDF functions. Switch to "test-stubs"
# if a test ever reaches a call_unsafe_wdf_function_binding! call site.
wdk-sys = { features = ["test-stubs"], workspace = true }
Leon Durrenberger (leon-xd)
left a comment
There was a problem hiding this comment.
great work :)
716bd64
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated no new comments.
Suppressed comments (5)
crates/wdk-sys/src/test_stubs.rs:36
- The module docs explicitly say
WdfDriverGlobalsis intentionally not stubbed, but the code now stubs it (to a null pointer). Please update the NOTE to match the new behavior and clearly warn that any call into WDF using this stub will likely crash/UB unless tests provide a real mock forWdfDriverGlobals.
//! NOTE: Enabling fat LTO in your dev profile may lead to Linker errors
//! even if you sufficiently cfg gate WDF function usage. This is because
//! the dev profile defaults to `opt-level = 0`, and in combination with fat
//! LTO may cause dead code to not be optimized out (fat LTO merges all upstream
//! crates' code into the final binary rather than letting the linker pull in
//! only what's needed, so WDF wrappers from an ungated `use wdk::` get
//! included even if you never call them).
//! - If "test-stubs" is enabled the Linker may complain that
//! `WdfDriverGlobals` is missing, it is intentionally not stubbed here
//! because it is needed when a WDF function is called, landing it outside
//! the intent of test-stubs.
crates/wdk-sys/src/test_stubs.rs:87
- The module docs explicitly say
WdfDriverGlobalsis intentionally not stubbed, but the code now stubs it (to a null pointer). Please update the NOTE to match the new behavior and clearly warn that any call into WDF using this stub will likely crash/UB unless tests provide a real mock forWdfDriverGlobals.
/// Stubbed version of `WdfDriverGlobals` Symbol so that test targets will
/// link.
// SAFETY: WdfDriverGlobals is a required WDF symbol for test compilation.
// No other symbols in this crate export this name, preventing linker conflicts.
#[unsafe(no_mangle)]
pub static mut WdfDriverGlobals: PWDF_DRIVER_GLOBALS = core::ptr::null_mut();
tests/mixed-package-kmdf-workspace/crates/driver/Cargo.toml:26
- This comment contradicts the dependency declaration: it says
test-stubsisn't needed and should only be enabled if tests reach a WDF call site, buttest-stubsis enabled unconditionally here. Either remove/adjust the comment to reflect whytest-stubsis always enabled for tests, or drop the feature until it’s actually required.
# The feature "test-stubs" isn't needed since gating the DriverEntry export_name keeps
# driver_entry from being codegen'd, so nothing references WDF functions. Switch to "test-stubs"
# if a test ever reaches a call_unsafe_wdf_function_binding! call site.
wdk-sys = { features = ["test-stubs"], workspace = true }
examples/sample-kmdf-driver/src/lib.rs:200
- This test calls
driver_exit, which (per its definition) executeswdk::println!. That can pull in WDK-dependent symbols even when#[link]directives are suppressed, undermining the goal of 'without linking WDK libs' and potentially causing link failures (or runtime issues) in test builds. Prefer a test that only references bindgen-generated types/constants (like theWDF_DRIVER_CONFIGsize test in the mixed-package driver), or gate the printing path behind#[cfg(not(test))]so the test doesn't exercise WDK print paths.
/// Dummy test to ensure `test` targets compile correctly
/// without linking WDK libs
#[test]
fn test_driver_exit() {
use super::*;
driver_exit(core::ptr::null_mut())
}
examples/sample-umdf-driver/src/lib.rs:186
- Same issue as the KMDF sample:
evt_driver_unloadprints viawdk::println!, which can introduce WDK symbol dependencies in a test build that is intended to avoid linking WDK libs. Consider changing this to a non-WDK-symbol-requiring assertion (e.g., type size/invariants) or ensuring the print path is#[cfg(not(test))].
/// Dummy test to ensure `test` targets compile correctly
/// without linking WDK libs
#[test]
fn test_driver_exit() {
use super::*;
evt_driver_unload(core::ptr::null_mut())
}
There was a problem hiding this comment.
Review details
Suppressed comments (4)
examples/sample-wdm-driver/src/lib.rs:85
- This test calls
driver_exit, whoseprintln!expansion reacheswdk_sys::ntddk::DbgPrint. The enabledtest-stubsfeature suppresses the newntoskrnllink directive and does not defineDbgPrint, so this test binary has an unresolved external instead of demonstrating library-free linking. Keep this as a compile/link smoke test by asserting on a generated type (as the mixed-package test does), or add a realDbgPrintmock.
driver_exit(core::ptr::null_mut())
examples/sample-kmdf-driver/src/lib.rs:199
- This invokes
driver_exit, and itsprintln!calls ultimately referenceDbgPrint.test-stubssuppresses thentoskrnllink directive but does not stubDbgPrint, leaving this test executable with an unresolved symbol. Use a generated-type assertion for the intended compile/link smoke test, or provide a functionalDbgPrintmock.
driver_exit(core::ptr::null_mut())
crates/wdk-sys/src/test_stubs.rs:36
- This newly added note says
WdfDriverGlobalsis intentionally not stubbed, but this same change defines it at line 87. That contradiction obscures the actual limitation: the null stub permits linking but cannot support calls through the WDF function table.
//! - If "test-stubs" is enabled the Linker may complain that
//! `WdfDriverGlobals` is missing, it is intentionally not stubbed here
//! because it is needed when a WDF function is called, landing it outside
//! the intent of test-stubs.
tests/mixed-package-kmdf-workspace/crates/driver/Cargo.toml:25
- The comment says
test-stubsis not needed and suggests switching to it later, while the dependency immediately below already enables it. It also incorrectly states that removingexport_namepreventsdriver_entryfrom being code-generated;cfg_attronly removes the export attribute. Document the actual reasons for the feature and gate instead.
# The feature "test-stubs" isn't needed since gating the DriverEntry export_name keeps
# driver_entry from being codegen'd, so nothing references WDF functions. Switch to "test-stubs"
# if a test ever reaches a call_unsafe_wdf_function_binding! call site.
- Files reviewed: 19/19 changed files
- Comments generated: 0 new
- Review effort level: Balanced
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated no new comments.
Suppressed comments (4)
examples/sample-wdm-driver/src/lib.rs:85
- This test calls
driver_exit, whosewdk::println!expansion callswdk_sys::ntddk::DbgPrintfor WDM. Becausetest-stubsnow suppresses thentoskrnllink directive and does not defineDbgPrint, the test executable has an unresolved external instead of proving that it links without WDK libraries. Keep this as a compile/link test by asserting on a generated type without invoking driver code.
driver_exit(core::ptr::null_mut())
examples/sample-kmdf-driver/src/lib.rs:199
- This call reaches
wdk::println!, which calls the kernel-onlyDbgPrintsymbol for KMDF. Thetest-stubsfeature suppresses thentoskrnllink directive but does not stubDbgPrint, so this newly enabled test target cannot link. Use a generated-type invariant here, as the mixed-package test does, rather than executing driver code that invokes WDK APIs.
driver_exit(core::ptr::null_mut())
crates/wdk-sys/src/test_stubs.rs:36
- This note now contradicts the implementation immediately below:
WdfDriverGlobalsis stubbed at line 87. Update it to warn that the WDF globals/function table are null and therefore cannot be exercised, rather than claiming the symbol remains intentionally undefined.
//! - If "test-stubs" is enabled the Linker may complain that
//! `WdfDriverGlobals` is missing, it is intentionally not stubbed here
//! because it is needed when a WDF function is called, landing it outside
//! the intent of test-stubs.
tests/mixed-package-kmdf-workspace/crates/driver/Cargo.toml:25
- The comment says
test-stubsis not needed and recommends switching to it later, but the dependency immediately below already enables it. It is needed here to suppress the generated native-link directives and supply test-target symbols; document that reason and the separateDriverEntrycollision instead.
# The feature "test-stubs" isn't needed since gating the DriverEntry export_name keeps
# driver_entry from being codegen'd, so nothing references WDF functions. Switch to "test-stubs"
# if a test ever reaches a call_unsafe_wdf_function_binding! call site.
Summary
Adds conditionally compiled linker directives for the VHF library tied to the "hid" feature.
Moves build script emitted static linker directives (
cargo::rustc-link-lib=static=*) from wdk-build/src/lib.rs into bindgen generated files as conditionally compiled attributes. The Rust source insertion happens in wdk-sys/build.rs while the backend logic and Rust source string build lives in wdk-build/src/lib.rs (closely following the pattern the bindgen generated headers use). This allows conditional compilation of linker directives (and eventually link args) without having to rely on cross crate feature signaling, and guards against issues from version drift (in case multiple versions of wdk-build are used in one project).This PR is a redesign of and supersedes PR!653.
Verification
Verified both kmdf and umdf drivers built with "hid" and Vhf functions linked against VhfKm.lib and VhfUm.lib respectively. Additionally, inspected each driver's linker
.mapfile for evidence of the respective Vhf symbols.Bindgen generated files are also inspected post build for the presence of the link attributes.