Skip to content

Teardowns

Where Rust actually is in Windows 11

Two Windows recovery and deployment DLLs were already shipping Rust in the original 24H2 build, and on this machine Microsoft’s own symbol files attribute 96.6 and 96.2 percent of their code to rustc. Neither appears in any Rust-in-Windows write-up I could find. The components everyone does name, the GDI kernel driver, sudo, DirectWrite, were announced years ago; what was missing was a measurement. So this is a census of one Windows 11 25H2 installation, 13,988 live files and 44,889 more in the component store, with every Rust marker classified by hand and the linker’s own records used to count how many bytes of each binary rustc actually produced.

Taken from
Windows 11 Home x64 25H2, build 26200.9168, scanned 2026-09-02 to 2026-09-04
One maintained x64 laptop, not a clean image: NVIDIA and Intel GPU driver packages, WSL, Office and Edge are installed.
Scope
Every file named .dll, .exe or .sys under C:\Windows, in two populations: the 13,988 files of the live system (everything outside WinSxS, servicing and SoftwareDistribution), and the 44,889 files inside those three stores, censused separately because they hold superseded builds. None unreadable. Not scanned: other PE extensions (.cpl, .ocx, .scr, .efi), the ACL-protected WindowsApps store, and anything outside C:\Windows.
Tool
Binocular 1.1, a commercial PE viewer I make. Every screenshot is the Strings, Overview, Imports or folder view as the app draws it, and every count in a caption is the app’s own status line. Discovery and symbol attribution use freely available tools: the one-line grep shown below, Microsoft’s public symbol server, and LLVM’s PDB tools. The derived counts, crate lists and hashes behind every figure are attached as evidence files.
Evidence
method, full census with SHA-256 and marker counts, file hashes, compiler-hash comparison, the GitHub lookup of all ten compiler revisions, the component-store census and version history with its per-file table, the crate and source-file census with its per-file table and DWriteCore comparison, the symbol-file census (Rust versus C++ by contributing object) with its table, DWriteCore 1.6.1 to 2.1.1 diff and verification, the static evidence on how Narrator reaches MathCAT, and for the kernel driver the feature-flag analysis, the panic-site to function mapping, its symbol census, the C++ host’s Rust-facing symbols and the loaded-module check. For section 9, the toolchain-channel census, the MathCAT pair and the Rich header comparison. And to repeat the byte counts yourself, the symchk and llvm-pdbutil recipe.
Published
18 September 2026 · 19 min read

The short version

  • Two servicing DLLs I found no write-up of. CloudRecoveryDownloadTool.dll and UdiApiClient.dll have carried Rust since the first 24H2 build, are 96.6 and 96.2 percent Rust by the linker’s own accounting, and share seven internal crates. (Sections 3 and 4.)
  • DirectWrite, measured rather than quoted. DWriteCore is 57.9 percent Rust by code bytes in both shipped builds. System32\DWrite.dll is zero: no Rust strings among 30,073, no rustc objects among 454. (Section 7.)
  • Windows ships somebody else’s Rust library. NarratorMCAT.dll is MathCAT, Neil Soiffer’s open-source crate, compiled on Microsoft’s own internal toolchain and signed as a Windows component. No first-party crate path survives in it: 26 registry crates, not one of them Microsoft’s. (Section 5.)
  • A compiler channel called utc. The MathCAT component on this machine was built by a toolchain package named stable-utc, in a package dated four months before Microsoft publicly described a rustc backend called rustc_codegen_utc, and it is 14 percent smaller than the same crate version built by the llvm channel. This one is an inference from a package name, and section 9 says exactly how far it goes.
  • The kernel driver’s switch is not in the kernel driver. win32kbase_rs.sys is loaded, its own feature table is empty, and the Rust region gate lives in the C++ host. (Section 1.)
  • 83 files that look like Rust, with no Rust evidence in them. All 83 are Intel and NVIDIA driver binaries with an embedded shader compiler, and in every one the match is fully explained by LLVM. (Section 6.)
  • Counting Rust by symbol name badly undercounts it. DWriteCore 2.1.1 has zero Rust-mangled symbols among its 8,424 publics and is still 57.9 percent Rust. Count contributing object files instead. (Method.)

Already public, and credited as such: that win32kbase_rs.sys is Rust (announced in 2023, and Check Point demonstrated it executing in 2025), that sudo.exe is Rust (it is an open-source repository), that DWriteCore and DWrite.dll are different implementations (Microsoft’s own specification says so), that Narrator reads maths with MathCAT (Microsoft announced the feature in 2026, and MathCAT’s own project page says it is Rust), and that an internal Rust toolchain exists (named in public since 2024). What was missing from every one of them was a measurement.

What Microsoft has actually said

The public record is thinner than the discourse. At BlueHat IL in March 2023, David Weston said Windows would probably be booting with Rust in the kernel within several weeks or months. The Register’s April 2023 report of that talk put numbers on it: DWriteCore, the DirectWrite implementation in the Windows App SDK, at about 152,000 lines of Rust alongside 96,000 of C++, and the Win32 GDI port at 36,000 lines, booting but disabled behind a feature flag. In July 2023, Insider build 25905 shipped a kernel driver called win32kbase_rs.sys, which Microsoft described as a new implementation of GDI region and a small trial. In February 2024, Sudo for Windows arrived as an open-source Rust project.

The toolchain has a public record too, and it is easy to miss. A September 2024 issue in an unrelated Microsoft repository names msrustup and the Microsoft Rust toolchain in passing, so the existence of an internal distribution has been visible for two years. Then, a week after the census below was taken, Microsoft described it properly: on 10 September 2026 Victor Ciura published a Rust Foundation guest post announcing Rust as a Tier-1 language at Microsoft and naming rustc_codegen_utc, an alternative rustc code generation backend in the same architectural family as rustc_codegen_llvm that wires the Rust compiler into MSVC’s own UTC backend. It has been self-hosted since Rust 1.90, is used in more than a hundred Microsoft repositories, and is credited with cross-language inlining and hotpatch support. That post describes in the abstract a good deal of what the binaries below show concretely. Section 9 is where the two meet.

None of that tells you what is on a machine today. So I checked one. The rules for this piece: every claim below is something the screenshots or the attached evidence files show, the build is named so you can repeat it, and where the evidence runs out it says so. Where I am inferring rather than reading, the sentence says that too.

Method

Rust binaries built with the standard toolchain usually carry fingerprints in their read-only data: panic-location strings that name .rs source files, standard-library paths like library\core\src, and a /rustc/<commit>/ prefix that identifies the source revision the compiler was built from. I ran one grep for those patterns over every .dll, .exe and .sys under C:\Windows, leaving out WinSxS, servicing and SoftwareDistribution as a scope decision (they are component stores that mostly hold copies of files found elsewhere, though not only that):

cd /c/Windows        # Git Bash. In WSL use: cd /mnt/c/Windows
find . -type f \( -iname '*.dll' -o -iname '*.exe' -o -iname '*.sys' \) \
  -not -path './WinSxS/*' -not -path './servicing/*' -not -path './SoftwareDistribution/*' -print0 \
  | xargs -0 grep -l -a -E 'rustc[/\\][0-9a-f]{40}|library[/\\](core|alloc|std)[/\\]src|core::panicking|rust_eh_personality|__rust_alloc'

That covered 13,988 files with no read errors and returned 92 hits. Most of the hits are one family of false positives, which gets its own section below. The rest I opened in Binocular one by one, plus the two DirectWrite binaries the press coverage made me curious about. Grep finds candidates. What follows is what the files say when you read them, including the ones where grep was wrong.

The same grep, without the three exclusions, went over the component store: 44,889 more files under WinSxS, servicing and SoftwareDistribution, in about four minutes. WinSxS keeps the superseded builds of every component that servicing has not yet cleaned up, so it doubles as the only history a single machine can offer. Its results are in section 8.

The other source is Microsoft itself. Its public symbol server carries debugging symbols for most Windows binaries, and it has them for every component here except the MathCAT DLL, whose symbol file is named by cargo rather than by Windows. A public symbol file has no source and no types, but it records which object file contributed each block of code, and rustc’s object files are unmistakable: with link-time optimisation the whole Rust side of a binary lands in a single .rcgu.o. That is a ruler the strings cannot supply, how many bytes of a binary rustc compiled and how many the C++ compiler did, and it does not depend on panic strings surviving. It measures the linker’s attribution, not source lines, and identical-code folding can merge a handful of functions across the line; the cross-checks that bound that are attached. The symbol files were fetched with the SDK’s symchk and read with LLVM’s llvm-pdbutil; the census and every per-object table are attached.

Why not just count Rust symbol names. Because the count bears almost no relation to the amount of Rust. DWriteCore 2.1.1 has zero Rust-mangled symbols among its 8,424 publics, and rustc still produced 57.9 percent of its code. Link-time optimisation folds the Rust half into one codegen unit, so almost every Rust function is internal and never reaches the publics table; the only Rust-mangled publics in those binaries are #[link_section] statics. That is not a universal rule. win32kbase_rs.sys is the exception here: it exports a plain C table rather than linking a staticlib into a C++ host, and its symbols do name 177 Rust functions. Why those survived and DWriteCore’s did not is a question about how each was linked, which is the point. A symbol count measures the link, not the language. The contributing object file is the thing to count, and a public PDB records it.

Two cautions before the reading starts. First, these markers are strong indicators that a binary contains Rust-origin code. They do not prove the whole binary is Rust, that the Rust code is on the live execution path, or that there was never a C++ version. Absence proves even less: a build that remaps its source paths, strips location detail, or removes strings after linking would show nothing. Second, filtering strings for .rs also matches Serbian domain names (it is a country code, and google.rs turned up) and LLVM’s MIPS intrinsics (llvm.mips.extr.rs.w). The Rust source paths in these files all have \src\ or library\ in them. Every screenshot below shows its filter in the search box so you can repeat it exactly.

1. The kernel driver: win32kbase_rs.sys

This is the one Microsoft announced, and it is still there in 25H2: a 152 KB driver (155,648 bytes), version 10.0.26100.8972, described in its own version resource as Base Win32k Kernel Driver. Filter its strings for anything ending in .rs and you get 21 hits.

win32kbase_rs.sys, Strings view filtered with /\.rs\b/. 21 of 1,780 extracted strings match, every one a Rust source path. The three tagged Paths are absolute paths from Microsoft's build machine: the OS source tree, the vendored toolchain and the crate cache.
win32kbase_rs.sys, Strings view filtered with /\.rs\b/. 21 of 1,780 extracted strings match, every one a Rust source path. The three tagged Paths are absolute paths from Microsoft's build machine: the OS source tree, the vendored toolchain and the crate cache.

The crate names are readable straight off the panic strings: gdi_rust, rgncore, gdi_alloc, seh_unwind, handle_manager, and a small crate called fallible_vec, which is what you reach for when a kernel allocation is allowed to fail. Microsoft’s symbol file confirms the list and adds two crates the strings do not show, gdi_bindings and gdi_region, plus a symbol prefix, cxxbridge1$, that names the generator of the bridge between the two languages: the cxx crate. Of the 177 Rust functions the symbol file names, 54 are in rgncore and 18 in gdi_rust, and the only standard library present is core and alloc, as a kernel driver’s should be. Binocular tags three of the 21 as Paths, absolute paths from Microsoft’s build machine. Two name the vendored toolchain under D:\os\tools\Rust\vpack and the crate cache that served fallible_vec from a Visual Studio package feed. The third is a complete path into Microsoft’s internal source tree:

d:\os\src\onecoreuap\windows\core\ntgdi\rust\rgncore\src\scan.rs

A file a few rows further down matters more than it looks. In January 2025 Check Point Research reported a bug to Microsoft that they had found by fuzzing: an out-of-bounds index in a function called region_from_path_mut() inside this exact driver. Microsoft’s fix first appeared in the 28 May 2025 preview update, KB5058499, and rolled out fully in June. The panic strings here name gdi_rust\src\region\from_path.rs, and Microsoft’s own symbol file closes the loop: the driver has a public function named gdi_rust::region::from_path::region_from_path_mut, and every one of the 22 panic sites that point at that source file sits inside it (the addresses are attached). What happened in that bug is the whole Rust argument in one line, in Microsoft’s own assessment as Check Point quotes it: the Rust code “correctly catches an out-of-bounds array access and triggers a panic, resulting in a Blue Screen of Death”. Microsoft rated it Moderate. Rust did not prevent the bug. It changed what the bug could do.

Imports, expanded. The C++ side of the boundary exports helpers with Rust in their names, and the driver can read Windows feature flags through the RtlQueryFeatureConfiguration family.
Imports, expanded. The C++ side of the boundary exports helpers with Rust in their names, and the driver can read Windows feature flags through the RtlQueryFeatureConfiguration family.

The import table shows the seam between the two languages. From win32kbase.sys, the C++ driver it plugs into, this module pulls Win32kBaseRustPatchCalloutHandler, RustOnZeroSizedScanCallback (a demangled C++ symbol), pool allocators, and the EPATHOBJ path-walking routines the region code needs. From the kernel it imports the RtlQueryFeatureConfiguration family, which is Windows’ feature-flag plumbing. The 2023 report said the Rust GDI code shipped behind a flag, and Check Point’s write-up describes a separate servicing flag that selects between the old and hardened edge logic in the 2025 fix. The symbol files say the Rust region gate is not in the driver: the Rust driver links the feature-staging runtime, but its own feature table is empty (the two symbols that bracket it sit at the same address), so it has nothing to ask about. That switch lives in the C++ host. win32kbase.sys carries more than a hundred named feature traits. One is the Rust region gate; a second is the likeliest candidate for the servicing flag, though the symbols do not establish that: Feature_Rust_GDI_REGION, feature 37356106, evaluated once when GDI initialises, and Feature_Servicing_GdiMsrc99105, feature 58181618, the GDI security-servicing flag. Asked through the same kernel API Windows uses, this machine has no configuration for either, at boot or at run time. Both run on their compiled defaults, which a public symbol file does not disclose; a control flag from the same table reads back as enabled, so the query is sound. The name Check Point gave the 2025 flag, Win32kRSPathToRegion, is not in this build’s symbols at all. Whether it became GdiMsrc99105 or was retired, the symbols cannot say.

What they can say is that the Rust driver is loaded. The kernel’s own module list, read through NtQuerySystemInformation without elevation, has win32kbase_rs.sys loaded beside win32k.sys, win32kbase.sys and win32kfull.sys. And the C++ host is built around it. Its symbols include a Win32kRS namespace of RegionCore_ wrappers, 27 of them public, functions named AllocateAndLoadBaseRustExports, GetBaseRustExports and HotswapBaseRustBinary, and region methods that take a RustAutoHotpatchLockSH argument: by its symbol names, the C++ side does not import the Rust driver the ordinary way, it loads a table of the Rust exports and passes a hot-patch lock into the region methods, which is what swapping the Rust binary under a running system would need. On the Rust side the table is the driver’s export directory, 79 plain C functions in Binocular’s Exports tab: 37 RegionCore_ entries with GDI’s old method names, RegionCore_bCopy, RegionCore_iCombine, RegionCore_vGetSubRect, Hungarian prefixes carried over from the C++ REGION class they stand in for, 39 more for the EXFORMOBJ transform class, two for the handle manager and one patch callout. Add Check Point’s crash, which reached ordinary 24H2 machines, and the picture is a module that is loaded and wired into its host. What this census cannot read is the compiled default of the gate, so whether the Rust region path is the one selected on this machine is not established here.

Overview, lower half. No embedded Authenticode signature, which is what a catalog-signed Windows component looks like, every applicable mitigation flag set, including CET shadow stacks and retpoline, and HOTPATCH_COMPATIBLE among the extended characteristics.
Overview, lower half. No embedded Authenticode signature, which is what a catalog-signed Windows component looks like, every applicable mitigation flag set, including CET shadow stacks and retpoline, and HOTPATCH_COMPATIBLE among the extended characteristics.

Three details from the Overview. First, the toolchain line says Microsoft Visual C++, linker 14.38, via Rich header. That is true, and it is not a language verdict. Rust on Windows links with MSVC’s link.exe by default, so the Rich header faithfully records the Microsoft linker and says nothing about what produced the objects it linked. The line names the artifact it was read from, which is the part that matters: any tool reading the Rich header will report the same thing about this file, and anyone who takes it for a language will call this a C++ driver. You need the strings. (A build linked with lld-link would typically carry no Rich header.) That limit is not hypothetical here: of the Rust-bearing binaries in this census, UdiApiClient.dll carries no Rich header at all, while the others do. Second, the signature line reads No (may be catalog-signed), which is the precise answer for a file like this: there is no Authenticode signature embedded in the bytes, because Windows signs its own components through the system catalog instead. The two are different questions, and a PowerShell Get-AuthenticodeSignature answers the second one, reporting a valid catalog signature. Third, the row of extended DLL characteristics, decoded from the debug directory, ends in HOTPATCH_COMPATIBLE: the header-level side of the hot-swap machinery the symbols showed from the C++ side, and a detail worth holding on to for section 9.

2. The command-line tool: sudo.exe

Sudo for Windows is open source, MIT-licensed, and written in Rust, so finding it was not a surprise. What the binary reveals is how Microsoft builds Rust internally.

sudo.exe 1.0.1, strings filtered for the build paths. The toolchain is an internal Microsoft Rust package, and every dependency path in view points at an Azure Artifacts feed rather than crates.io.
sudo.exe 1.0.1, strings filtered for the build paths. The toolchain is an internal Microsoft Rust package, and every dependency path in view points at an Azure Artifacts feed rather than crates.io.

The compiler is named in the panic strings: rust.tools-x86_64-pc-windows-msvc.1.87.0-ms-20250722.10, installed under a directory called msrustup_home. That is Rust 1.87 as packaged by Microsoft’s own distribution, in a package dated 22 July 2025. The dependency paths all start with pkgs.dev.azure.com, an Azure Artifacts feed, not crates.io. The crates themselves are ordinary: clap_builder 4.5.1, windows 0.57.0, windows-registry 0.1.0, once_cell 1.19.0, which 6.0.0, anstream 0.6.12. A second copy sits in SysWOW64, byte-identical to the System32 one: the same x64 binary and the same SHA-256, not a 32-bit build. That is the folder a 32-bit process is redirected to when it asks for System32, so the copy is what lets 32-bit callers find sudo at all. There is no 32-bit sudo.

The symbol file puts numbers on it. Of sudo’s 640,659 attributed bytes of code, 622,778 came out of rustc, 97 percent; the rest is the C runtime plus two small RPC stubs. Inside the Rust, counting functions with a size record, sudo’s own crate is 86,370 bytes. The argument parser, clap, is 230,171 across its two crates, and the standard library another 238,204. The tool that made the headlines is a thin program on a thick stack of dependencies, which is what most Rust programs look like from the inside.

The file also names its compiler. Its standard library paths carry the commit hash of the rustc source revision, 035286e2. That hash matches none of the release manifests for stable Rust 1.84.0 through 1.96.1, point releases included; the upstream 1.87.0 release is 17067e9a. Asked directly, GitHub reports no such commit in rust-lang/rust, and its commit search finds it in no public repository it indexes, while the control query for the real 1.87.0 commit comes back at once. The same is true of every compiler revision this census could read: ten distinct hashes across the current Windows files, the older builds in the component store and the DWriteCore that WSL bundles, none of them in rust-lang/rust, none that GitHub can find in public (the lookups are attached). Not every binary here embeds a hash, so ten is what was observable, not a complete set. All ten, though, came from a source revision that does not appear in the open, which, together with the -ms- package names and the Azure feed, is the shape of a toolchain Microsoft builds for itself. As of September 2026 that is no longer only an inference: Microsoft says so, and says rather more besides.Section 9.

3 and 4. The servicing DLLs, Rust since the first 24H2 build

The next two hits are plumbing for Windows Update and recovery. Both carry Rust-origin code on toolchains from this spring.

CloudRecoveryDownloadTool.dll 0.3.28.0. Built with Microsoft's Rust 1.90 package from March 2026; 533 strings match the build-path filter.
CloudRecoveryDownloadTool.dll 0.3.28.0. Built with Microsoft's Rust 1.90 package from March 2026; 533 strings match the build-path filter.

CloudRecoveryDownloadTool.dll describes itself as the Microsoft Cloud Recovery Download Tool, the code behind the cloud reinstall option. It was compiled with rust.tools.stable-llvm-x86_64-pc-windows-msvc.1.90.0-ms-20260302.10, a March 2026 package. Its crate list reads like a small networked service: serde_core 1.0.228, futures-util 0.3.32, base64 0.22.1, bytes 1.11.1, com 0.6.0, crypto-hash 0.3.4, tracing-subscriber 0.3.23, encoding_rs 0.8.35, indexmap 2.14.0, and an internal crate called osget that turns up again in the next file.

UdiApiClient.dll 0.4.45.0, Microsoft Universal Download and Install. Rust 1.93.1 from April 2026, with its own log strings naming the source files and line numbers.
UdiApiClient.dll 0.4.45.0, Microsoft Universal Download and Install. Rust 1.93.1 from April 2026, with its own log strings naming the source files and line numbers.

UdiApiClient.dll is Microsoft Universal Download and Install, version 0.4.45.0, built with Rust 1.93.1 from a 16 April 2026 toolchain package. Its own log strings are unusually chatty: udi-api\src\updateapi_exports.rs:144, This API needs to run with elevated privilege, No hotpatch baseline found in recipe. That last one is a small window into the client side of Windows hotpatching, and that client code is Rust.

What these two do and do not show. They show that Rust-origin code with recovery and update strings ships in the Windows image today, on toolchains a few months old, in files with 0.x version numbers and no C++ predecessor named anywhere in them. The component store supplies the history this machine has (section 8): both DLLs are already Rust in their 26100.1 builds, the first 24H2 image, with 771 and 925 Rust source paths respectively, and both have been rebuilt on newer Microsoft toolchains since. The 24H2 builds carry only the source paths; the toolchain package names and compiler hashes appear from the 2025 and 2026 builds on. The oldest builds this machine holds already contain Rust, so it offers no C++ predecessor of the same name to compare against. Whether something else did this job before 24H2 is a question for an older image.

Both are Rust nearly to the byte. By contributing object file, rustc produced 96.2 percent of UdiApiClient’s code and 96.6 percent of CloudRecoveryDownloadTool’s; the remainder is the C runtime, and the symbol files attribute not one function in either to hand-written C++. Their first-party crates were compiled without debug records, so the symbol files name only the standard library’s functions and the crate names come from the strings instead: nine in one, eight in the other, seven of them shared (deploy-util, osget, async-util among them). One deployment code base, two components.

5. The one announced as accessibility, not as Rust: Narrator’s maths engine

NarratorMCAT.dll does not sit in System32 itself but in a subfolder of its own beneath it, next to a Rules directory, a REVISION.txt that reads MathCAT 0.7.0 / MathCATForC 0.7.0, and a Microsoft third-party NOTICE.txt. It has no version resource of its own and is signed with the Windows certificate. Microsoft did announce the feature it belongs to: from Windows 11 build 26200.7623, Narrator’s reading of native Office maths in Word and PowerPoint slide shows is improved “using MathCAT, the industry-leading technology for math reading”, in the words of the Microsoft 365 Insider blog. What the announcement does not say is what that means for the language census. The strings do.

NarratorMCAT.dll, strings filtered with /mathcat|msrustup/. 127 of 42,602 extracted strings match: MathCAT 0.7.0 crate paths and a Microsoft Rust 1.95 toolchain package dated 14 May 2026.
NarratorMCAT.dll, strings filtered with /mathcat|msrustup/. 127 of 42,602 extracted strings match: MathCAT 0.7.0 crate paths and a Microsoft Rust 1.95 toolchain package dated 14 May 2026.

The crate paths name mathcat-0.7.0 and mathcat_c-0.7.0, and the panic strings name braille.rs, tts.rs, navigate.rs and Unable to find MathCAT Rules directory. MathCAT, Math Capable Assistive Technology, is Neil Soiffer’s open-source Rust library that turns MathML into speech and braille, designed to replace MathPlayer and already the engine behind the NVDA add-on of the same name. This DLL is that library, compiled by Microsoft with rust.tools.stable-utc-x86_64-pc-windows-msvc.1.95.0-ms-20260514.2, a toolchain package from 14 May 2026, the newest dated toolchain in this piece. How Narrator loads it is a separate question, taken up below. What is not an inference: Windows now ships a third-party Rust library, built on Microsoft’s own toolchain, for its screen reader, and that fact appears in none of the Rust-in-Windows coverage I could find.

The file also says how it was built. Every MathCAT source path in it sits under a cargo registry directory, mathcat-0.7.0 and mathcat_c-0.7.0, fetched from an Azure Artifacts feed like every dependency path in the other binaries here. No first-party crate path survives in it: 26 registry crates, none of them Microsoft’s. The debug directory names its symbol file libmathcat_c-77bb253130fb3cac.pdb, which is the name cargo gives the build artefact of the mathcat_c crate, MathCAT’s C-API wrapper. So NarratorMCAT.dll is not a Microsoft component that uses MathCAT. It is MathCAT’s own C library, built as a crate dependency from a mirrored copy of the upstream package on Microsoft’s toolchain, then renamed and signed as Windows. Whether the mirror carries patches, or wrapper code without panic sites sits beside it, the strings cannot say.

How Narrator reaches it is the one thing this piece cannot show. Neither Narrator.exe nor SRH.dll, the engine it hosts, names the DLL statically, and nothing under System32 does either; the searches are in the evidence file. Windows ships it as a first-class component, accessibility-mathcat, and SRH.dll carries the maths-reading settings, so the placement and Microsoft’s announcement say plainly what it is for. The name is either built at run time, read from data, or supplied by a caller outside the files searched, and I did not run Narrator to watch. The load stays an inference.

6. The matches explained by LLVM: GPU driver binaries

My grep also flagged nvidia-pcc.exe, a 26.8 MB executable (28,094,184 bytes) in System32 placed there by the NVIDIA driver package and signed through the Windows Hardware Compatibility Publisher. It matched on the string rust_eh_personality. Open it in Binocular and the Strings view puts that match beside its neighbours, and the match falls apart on sight.

nvidia-pcc.exe. The Rust string sits between an Ada exception handler and a table of DWARF language tags. This is LLVM's own bookkeeping inside a shader-cache compiler, not Rust code.
nvidia-pcc.exe. The Rust string sits between an Ada exception handler and a table of DWARF language tags. This is LLVM's own bookkeeping inside a shader-cache compiler, not Rust code.

rust_eh_personality sits directly after __gnat_eh_personality, the Ada one, and a few rows above DW_LANG_Ada83, DW_LANG_Rust, DW_LANG_Swift and the C++ variants. That is the list of exception personalities and DWARF language tags that LLVM carries so it can handle other people’s binaries. The file is NVIDIA’s Pipeline Cache Compiler for Vulkan SC. It embeds LLVM. It contains zero Rust source paths, zero rustc commit hashes, and zero panic strings. The marker that matched is fully explained by LLVM, and nothing in the file affirmatively says Rust.

And it is not alone. The same marker turned up in 82 more files under System32\DriverStore, all of them Intel and NVIDIA graphics driver binaries that embed a shader compiler: igc64.dll, opencl-clang64.dll, nvvm64.dll, nvoglv64.dll, nvwgf2umx.dll, their 32-bit builds, repeated across the driver versions this machine has accumulated. Every one of the 83 carries LLVM strings (from 22 to 877 per file) and a DWARF language table (40 to 63 tags), and none carries a Rust source path or a compiler hash; the per-file counts are in the census file. A fingerprint scan on its own would have reported them all as Rust. This is why the second step exists.

7. Two DirectWrites, one of them measured

The most repeated number in this story is that DirectWrite has about 152,000 lines of Rust. Microsoft’s documentation is clear that this refers to DWriteCore in the Windows App SDK rather than to the classic DirectWrite DLL, so the two are separate implementations and always were. What has been missing is the measurement, in both files. Start with the negative control: open the DirectWrite DLL that ships in System32, DWrite.dll, and filter for .rs. (Its own version block says 10.0.26100.8972; Explorer will show you 10.0.26100.1, which comes from the separate MUI resource file. Same bytes either way, and the hash is in the evidence files.)

System32\DWrite.dll. Zero of 30,073 extracted strings match /\.rs\b/. Nothing in this file says Rust.
System32\DWrite.dll. Zero of 30,073 extracted strings match /\.rs\b/. Nothing in this file says Rust.

Zero. Not one Rust string in 30,073. The number is not wrong, though; it is about a different file. The rewrite Weston described is DWriteCore, and Microsoft’s documentation places DWriteCore in the Windows App SDK, not in the classic DirectWrite DLL. That placement invites a second wrong conclusion, that DWriteCore ships with apps rather than with Windows. It does ship with Windows: two copies live under C:\Windows\SystemApps in the inbox Windows App Runtime packages, one from App SDK 1.6.1 and one from 2.1.1, both signed with the Windows certificate.

The inbox DWriteCore.dll from C:\Windows\SystemApps: Windows App SDK 1.6.1.2406, the same MSVC 14.38 linker line as the kernel driver, and nothing in the headers to say it is Rust.
The inbox DWriteCore.dll from C:\Windows\SystemApps: Windows App SDK 1.6.1.2406, the same MSVC 14.38 linker line as the kernel driver, and nothing in the headers to say it is Rust.

Applications bundle their own copies as well. On this machine the WSL settings app carries App SDK 1.7.1, Edge’s Copilot component carries 2.1.1, and Office carries two, a 2.0.1 and a 2.1.1: six copies of DWriteCore.dll in all, none byte-identical. Filter any of them for .rs and this is what you get:

DWriteCore.dll from the Windows App SDK 1.7.1 as bundled with WSL. 205 extracted strings match /\.rs\b/: font_face.rs, cleartype.rs, font_fallback, langtag, plus bumpalo from a Visual Studio package feed and hashbrown vendored with the toolchain.
DWriteCore.dll from the Windows App SDK 1.7.1 as bundled with WSL. 205 extracted strings match /\.rs\b/: font_face.rs, cleartype.rs, font_fallback, langtag, plus bumpalo from a Visual Studio package feed and hashbrown vendored with the toolchain.

205 matches in the WSL copy, and the inbox copy gives the same list with two extra rows: rust\dwrite\src\font_face.rs, rust\glyph_rendering_subpixel\src\cleartype.rs, rust\font_fallback\src\builder.rs, rust\langtag, with bumpalo pulled in from a Visual Studio package feed and hashbrown vendored inside the toolchain. So both halves of the statement hold on this machine: DirectWrite has been substantially rewritten in Rust, that code ships inside Windows as DWriteCore, and none of it is in System32\DWrite.dll. The interesting part is not which file it is in. It is how much.

Six copies across four App SDK versions make a small longitudinal study (the per-copy crate lists are attached). By source-path strings, the Rust side of DWriteCore is 25 first-party crates in the 1.x builds and 24 in 2.x, and 172, 170 and 176 distinct source files in 1.6.1, 1.7.1 and 2.x, ten of them generated tables in each. Between 1.7.1 and 2.0.1 the crate set turns over rather than grows: bumpalo_pool and shaping_interop go, shaping_cache arrives, shaping_unicode_data gains a hand-written file beside its generated tables, layout gains two files and truetype two. The largest crates are shaping at 36 files and otls at 35. The three 2.1.1 copies, inbox, Edge and Office, are one build packaged three ways: identical code and data sections and the same link timestamp, the inbox copy catalog-signed, the other two carrying embedded signatures of their own, and a small difference in .rdata between them. The Office 2.0.1 has the same source set again. A string census counts files that still contain a panic site, so these are floors, and flat ones.

The symbol files settle the proportion. In both inbox builds the Rust side of DWriteCore is a single rustc object, dwrite_staticlib, linked into a C++ DLL, and the linker’s own record of who contributed what says rustc produced 57.9 percent of the attributed code bytes in each: 1,081,273 of 1,868,488 in 1.6.1 and 1,108,273 of 1,913,201 in 2.1.1, with the C++ compiler at 40 percent and the runtime the rest. The Register’s 2023 figure, 152,000 lines of Rust beside 96,000 of C++, is 61 percent by line. That is a different ruler from this one and the two should not be set side by side: lines are source, bytes are what the linker emitted after optimisation. The comparison worth making is between the two measurements taken the same way, and there the Rust share holds at 57.9 percent across both builds while each side grows a few percent in absolute terms. Underneath, the per-target table shows one small trade: an 11,750-byte C++ shaping module is gone from 2.1.1 and a new Rust shaping_cache crate of 25,588 bytes has arrived, while a C++ cache layer was renamed. The API crate is now called dwrite, with a residual api_impl, and the COM plumbing moved from the com crate to windows_core. By bytes the largest Rust crates are shaping, truetype, the API layer and layout; the C++ that remains is OpenType font parsing, the CFF rasteriser, the font API, streaming and cache layers, colour glyph data and rendering, and the Windows binding. The seam between the two shows as 79 public functions that resolve into the Rust object, nearly all with plain C names like Rust_CreateTextLayoutFactory and rust_truetype_get_outline, a few C++ names folded in by the linker. And DWrite.dll, read the same way: 454 object files, none from rustc, zero Rust bytes.

8. What the component store remembers

WinSxS answers the obvious objection to a scan that skipped it. The same grep over its 44,889 files returned 34 hits and no new Windows component: 21 are Rust-class (20 store entries, 17 distinct builds, of the six components above, and one superseded file discussed below), 13 are Edge WebView2 files carrying Chromium’s own Rust toolchain paths, and the GPU false positives do not appear because DriverStore lives under System32. Eleven hits are byte-identical to files already counted. The other 23 are superseded builds, and they are the history.

The kernel driver is in the store four times. Its 26100.1301 build, the oldest here, is 151,552 bytes with 10 Rust source-path strings and still embeds its compiler hash; the 8521, 8875 and 8972 builds are 155,648 bytes with 21 strings and no hash. Check Point’s May 2025 fix lands between the first and the second. The two servicing DLLs are in the store three times each, back to their 26100.1 builds (sections 3 and 4), and the current UdiApiClient is a third smaller by bytes than its 24H2 ancestor and carries 358 fewer source-path strings; those strings count surviving path matches, not code, so the size says more than the count does. The MathCAT DLL first appears in the 26100.7705 servicing build, dated inside the binary to December 2025 and compiled with Microsoft’s Rust 1.88 package; the current one was rebuilt in May 2026 on Rust 1.95. That is consistent with the build Microsoft named for the Narrator feature.

DWriteCore is in the store in four generations across two inbox channels: App SDK 1.5.0 and 1.6.1 on the stable channel, 1.8.1 and 2.1.1 on the next one. The source-path count barely moves (189, 197, 197, 197), which is why counting strings is the wrong ruler for growth; section 7 uses symbols instead. And the 24H2 RTM WebView2 package carried a file the current WebView2 package does not, dwritemin.dll: a trimmed DWriteCore 1.5 build under the name DWriteMin, with 84 Rust source paths in it, and no copy anywhere outside the store. Every build in the store, with its SHA-256, version and marker counts, is in the attached table.

9. The compiler that changed name

Most of the Rust binaries here name the toolchain package that built them, and the names follow one pattern: rust.tools.<channel>-x86_64-pc-windows-msvc.<version>-ms-<date>. Collected from the whole census, live files and component store together, they look like this:

no channel in the package name
  sudo.exe                       rust.tools-....1.87.0-ms-20250722.10
  DWriteCore.dll (1.8.1, 2.1.1)  rust.tools-....1.88.0-ms-20250913.5

stable-llvm
  NarratorMCAT.dll (26100.7705)  rust.tools.stable-llvm-....1.88.0-ms-20250913.6
  CloudRecoveryDownloadTool.dll  rust.tools.stable-llvm-....1.90.0-ms-20251106.9   (26100.8115)
  CloudRecoveryDownloadTool.dll  rust.tools.stable-llvm-....1.90.0-ms-20260302.10  (current)
  UdiApiClient.dll               rust.tools.stable-llvm-....1.93.1-ms-20260416.7

stable-utc
  NarratorMCAT.dll (current)     rust.tools.stable-utc-....1.95.0-ms-20260514.2

names no package
  win32kbase_rs.sys              D:\os\tools\Rust\vpack\rust.tools, no version string
  DWriteCore.dll (1.6.1, 1.7.1)  rustc hash only, no package path

The early packages carry no channel at all, and the convention arrives mid-stream: two packages share the date 13 September 2025, and the earlier build number has no channel while the later one is stable-llvm. From there stable-llvm is the norm. One package, and so far only one, says something else: stable-utc, dated 14 May 2026, and it built the NarratorMCAT.dll that is on this machine now. It is visible on a dozen rows of the strings screenshot in section 5, in the paths that begin msrustup_home\packages\rust.tools.stable-utc. Six days after this census was taken, Microsoft published the guest post described at the top of this piece, naming rustc_codegen_utc as an alternative rustc backend to rustc_codegen_llvm, targeting UTC, the native MSVC backend. The two channel names on this machine are stable-llvm and stable-utc.

That correspondence is the whole of the evidence, and I want to be exact about its weight. A package name is a label somebody typed, not a property of the code. I cannot read the backend out of the binary. The obvious place to look is the Rich header, MSVC’s own record of which tools touched the link, and it does not separate them: both MathCAT builds carry the same set of tool product IDs in the same order, with the linker moving from build 35219 to 36244 and the per-tool counts changing. Nothing in it marks one as having gone through a different code generator. That is a negative result and only that. It does not argue against the reading; it fails to settle it.

What the component store does offer is a near-matched pair. The same upstream crate, carrying the same version label, is present twice, built by the two different channels:

                   26100.7705               26100.8972 / .9278 / live
channel            stable-llvm              stable-utc
rustc              1.88.0-ms-20250913.6     1.95.0-ms-20260514.2
rustc revision     fa6ff70e...              32dfec32...
crate              mathcat 0.7.0            mathcat 0.7.0
                   mathcat_c 0.7.0          mathcat_c 0.7.0
size               4,382,296 bytes          3,768,656 bytes  (-613,640, -14.0%)
sha256             ae77944e39192eb7...      edd0987181c5ad5f...

The same library, carrying the same 0.7.0 version label, is 613,640 bytes smaller when built by the stable-utc package: a 14 percent reduction. That is a real, reproducible difference between two files anybody with this Windows build can hash. It is not proof of a backend, and the obvious confound is in the table: the compiler version moved from 1.88 to 1.95 at the same time, and seven releases of rustc change code size on their own. A clean test needs the same rustc version built both ways, which is not something a census of one machine can arrange. Both file hashes, the full package census, the Rich header comparison and a plain statement of what this does and does not support are in toolchain-channels.txt.

So the honest statement is narrow. A Windows component on this machine was built by a toolchain channel named utc, from a package dated 14 May 2026, four months before Microsoft publicly described a backend of that name, and it is markedly smaller than the same crate version built by the llvm channel. When that build reached users is a separate question this census does not answer: it can date the package, not the rollout. Two other things in this census sit oddly well beside the announcement, which credits the UTC backend with cross-language inlining and hotpatch support: the kernel driver carries HOTPATCH_COMPATIBLE and a C++ host built around hot-swapping it, and DWriteCore is one rustc object linked directly into a C++ binary with 79 functions crossing the seam. Neither names a utc package, and the driver names no package at all, so neither identifies a backend either way. They are the kind of thing the new backend is said to be for, which is a reason to keep watching these files rather than a finding. If you have a build where the channel name and the code disagree with any of this, I would like to see it.

What that adds up to

One more Microsoft-shipped file contains Rust and is left out of the count on purpose. msedge.dll under System32\Microsoft-Edge-WebView, the WebView2 runtime, carries Rust from Chromium’s own toolchain (its paths start with third_party/rust-toolchain), so it says something about Chromium rather than about Windows. The grouping below is mine: eight paths, seven unique files, six components, of which five are Microsoft-authored and one (MathCAT) is third-party Rust that Microsoft compiles and ships.

The files above in one folder view after Verify Signatures and Audit Hardening. Three carry no embedded signature at all, win32kbase_rs.sys, DWrite.dll and the inbox DWriteCore.dll: Windows signs its own components through the system catalog, which an embedded-signature check cannot see. The copy of DWriteCore.dll bundled with WSL carries its own signature.
The files above in one folder view after Verify Signatures and Audit Hardening. Three carry no embedded signature at all, win32kbase_rs.sys, DWrite.dll and the inbox DWriteCore.dll: Windows signs its own components through the system catalog, which an embedded-signature check cannot see. The copy of DWriteCore.dll bundled with WSL carries its own signature.
FileWhereWhat it isRust?Evidence
win32kbase_rs.sysSystem32Kernel GDI region codeYes, Microsoft-authoredLoaded now; 177 Rust functions named in its symbols; gated by Feature_Rust_GDI_REGION in win32kbase.sys
sudo.exeSystem32, identical copy in SysWOW64Sudo for Windows 1.0.1Yes, Microsoft-authored97% of code bytes from rustc; Microsoft Rust 1.87 package, Azure crate mirror
CloudRecoveryDownloadTool.dllSystem32Cloud reinstall downloadYes, Microsoft-authored97% of code bytes from rustc; Rust since the first 24H2 build
UdiApiClient.dllSystem32Universal Download and InstallYes, Microsoft-authored96% of code bytes from rustc; Rust since the first 24H2 build
NarratorMCAT.dllSystem32\NarratorMCATMathCAT 0.7.0 for NarratorYes, third-party, Microsoft-builtMathCAT’s own C-API crate under a Windows name; Rust 1.95 (May 2026)
DWriteCore.dllSystemApps, two packagesDirectWrite, inbox App Runtime 1.6 and 2.1Yes, Microsoft-authored58% of code bytes from rustc in 1.6.1 and 2.1.1, one rustc object linked into C++
DWrite.dllSystem32DirectWrite, classicNo Rust evidence0 of 30,073 strings; 0 rustc objects among 454 in its symbol file
nvidia-pcc.exe and 82 driver filesSystem32, DriverStoreIntel and NVIDIA driver binaries with embedded compilersNo Rust evidenceLLVM language tables only; no paths, no rustc hash
msedge.dllSystem32\Microsoft-Edge-WebViewWebView2 runtime (Chromium)Rust, via ChromiumChromium toolchain paths; not counted

On this installation, among 13,988 live files and 44,889 more in the component store, the Windows image carries Rust-origin code in six Microsoft-signed components: one kernel driver that is loaded, hot-swappable and gated from the C++ side, one command-line tool that was Rust from day one, two servicing DLLs that have been Rust since the first 24H2 build, the screen reader’s maths engine, which is an upstream Rust library under a Windows name, and the DirectWrite rewrite riding inside the inbox App Runtime. Where the symbol files allow a measurement the Rust is not a sliver: 58 percent of DWriteCore by code bytes, 96 to 97 percent of the servicing DLLs and of sudo. No Rust fingerprint under System32\drivers. None in DWrite.dll, by strings or by symbols. This census gives no evidence of a wholesale rewrite. It cannot give evidence against one either: that both halves of DWriteCore grew between 1.6.1 and 2.1.1 says nothing about the direction of a programme, because code can be replaced in one place while it grows in another. What the census shows is narrower and firmer. A handful of deliberate placements, each measurable now, and new code landing in Rust, on a compiler Microsoft builds for itself and has now described in public.

Why this is checkable at all. Every finding above started from Rust’s habit of embedding panic-location strings in release builds. Those .rs paths are there so a crash can say where it happened. They are also optional: a build that strips location detail or uses the immediate-abort panic strategy would show none of this, path remapping would hide where it was built, and the Rich header would go on saying MSVC. If Microsoft ever turns that off, the grep goes blind. The symbol files would still name rustc’s objects, but only for binaries whose symbols Microsoft publishes, and a negative string scan on its own would prove nothing.

Repeat it on your machine

The numbers above describe one installation, read between 2 and 4 September 2026, with the toolchain comparison in section 9 taken on 18 September after a newer build of the MathCAT component appeared. Windows changes every Patch Tuesday, so yours will differ, and that is the point. From Git Bash (or WSL, with the path noted in the comment):

cd /c/Windows        # Git Bash. In WSL use: cd /mnt/c/Windows
find . -type f \( -iname '*.dll' -o -iname '*.exe' -o -iname '*.sys' \) \
  -not -path './WinSxS/*' -not -path './servicing/*' -not -path './SoftwareDistribution/*' -print0 \
  | xargs -0 grep -l -a -E 'rustc[/\\][0-9a-f]{40}|library[/\\](core|alloc|std)[/\\]src|core::panicking|rust_eh_personality|__rust_alloc'

Then open each hit, go to Strings, and filter for \src\ or library\. Source paths and a rustc hash are strong evidence of Rust-origin code. A language table with neither is LLVM. Expect the GPU driver family to dominate the raw hit list if you have a discrete graphics driver installed, and remember that a clean result proves only that no markers survived. Drop the three -not -path exclusions and the same command covers the component store, which on this machine meant 44,889 more files and about four minutes.

The percentages are worth repeating too, and the recipe is short: symchk from the Windows SDK, LLVM’s llvm-pdbutil, and the census script attached here. The raw PDB dumps are too large, and specific to the exact PDB builds, to republish, so REPRODUCE.txt gives the exact symchk and llvm-pdbutil invocations that generate the script’s inputs, the PDB signature prefixes this article used so you can check you are on the same builds, and what the measurement does and does not mean. Running census.py should reproduce every file published beside it, including verification.txt, which re-derives the parsed public-symbol counts against raw grep counts on the dumps. That catches a parsing failure in the symbol tables; it is a cross-check, not a proof that every section contribution parsed correctly.

If you find a Rust component this piece missed, a build where the numbers moved, or a stable-utc package on something that is not NarratorMCAT, tell me and I will add it here with credit.

Sources

Disclosure: Hunter Works Software makes Binocular, the tool used for every screenshot above. Each screenshot shows the primary evidence rather than a verdict: the Strings views give each string with its offset and section, so you can confirm any of them in a hex editor, and the other views show headers, imports and file metadata as parsed. The evidence files carry the hashes and counts for every file discussed, so the conclusions can be checked against the data.

ShareXBluesky

Do the second step yourself

Open the same binaries and read them yourself.

Binocular is the viewer used for every screenshot above. It opens PE, ELF and Mach-O files and shows every string with its section, offset and encoding, alongside imports, exports, headers and the debug directory, all offline. It runs on the same files this piece used.

Download the free trial

Windows 10/11 · signed installer · no signup