A cross-platform Windows resource-definition script (.rc) to resource file (.res) compiler. It has been merged into the Zig compiler (#17069, #17412), but it is also maintained as a separate standalone tool.
- This is a fully from-scratch and clean-room implementation, using fuzz testing as the primary method of determining how
rc.exeworks and how compatibleresinatoris with its implementation.- See this talk for some insight into this and this article for a full exploration of the topic
resinatorcan successfully compile every.rcfile in the Windows-classic-samples repo byte-for-byte identically to the Windows RC compiler when using the includes from MSVC/the Windows SDK. This is tested via win32-samples-rc-tests.resinatorhas zero external dependencies at runtime (it embeds Aro for preprocessing) and can be used to cross-compile from any system out-of-the-box (if Windows system include paths are not found, a complete set of MinGW include files are extracted on-demand).- Documentation (really just a dumping ground for documentation-adjacent stuff; to-be-improved-upon)
A Windows resource-definition file (.rc) is made up of both C/C++ preprocessor commands and resource definitions.
- The preprocessor commands are evaluated first via Aro
- The preprocessed
.rcfile is then compiled into a.resfile - The
.resfile can then be linked into an executable by a linker
resinator is similar to llvm-rc and GNU's windres, in that it aims to be a cross-platform alternative to the Windows rc.exe tool.
However, unlike llvm-rc and windres (see this section), resinator aims to get as close to 1:1 compatibility with the Windows rc.exe implementation as possible. That is, the ideal would be:
- The
.resoutput ofresinatorshould match the.resoutput of the Windowsrc.exein as many cases as possible (if not exactly, then functionally). However,resinatorwill not support all valid.rcfiles (i.e.#pragma code_pagesupport will be limited to particular code pages). resinatorshould fail to compile.rcfiles that the Windowsrc.exetool fails to compile.
In practice, though, 1:1 compatibility is not actually desirable, as there are quirks/bugs in the rc.exe implementation that resinator attempts to handle better.
resinator is a drop-in replacement for rc.exe with some improvements, some known exceptions, and some caveats.
resinator avoids some miscompilations of the Win32 RC compiler and generally handles edge cases better (e.g. resource data size overflow). See this exhaustive list of intentional differences between resinator and rc.exe.
Also, resinator will emit helpful warnings/errors for many of these differences:
This is a limitation of the preprocessor that is used by resinator (see this issue).
.rc files can #include files from MSVC/Windows SDKs (most commonly, windows.h). The paths for these files are typically provided via the INCLUDE environment variable or the /i command line option. resinator supports the same options, but it will also try to auto-detect system include paths on Windows, or extract a full set of MinGW includes on-demand (meaning #include "windows.h" in a .rc file will work out-of-the-box on Linux/MacOS/etc).
This behavior can be controlled with the /:auto-includes CLI option.
| Feature | resinator |
windres |
llvm-rc |
rc.exe |
|---|---|---|---|---|
| Cross-platform | ✅ | ✅ | ✅ | ❌ |
Identical win32-samples-rc-tests outputs as rc.exe |
✅ | ❌ | ❌ | ✅ |
Support for UTF-16 encoded .rc files |
❌ (TODO) | ❌ | ❌ | ✅ |
CLI compatibility with rc.exe |
✅ | ❌ | ✅ | ✅ |
| Includes preprocessor | ✅ | ❌ | ❌ | ✅ |
Support for outputting .rc files |
❌ | ✅ | ❌ | ❌ |
| Support for outputting COFF object files | ❌ (TODO) | ✅ | ❌ | ❌ |
Here is an example .rc script that is handled differently by each of windres, llvm-rc, and the canonical Windows rc.exe implementation:
// <id> <resource type> { <data> }
1 "FOO" { "bar" }rc.execompiles this to a.resfile with a resource of ID1that has the type"FOO"(the quotes are part of the user-defined resource type name)windrescompiles this to a.resfile with a resource of ID1that has the typeFOO(the"FOO"is parsed as a quoted string)llvm-rcerrors on this file with:Error: expected int or identifier, got "FOO"resinatormatches therc.exebehavior exactly in this case
This particular example is mostly inconsequential in terms of real-world .rc files, but it is indicative of how closely the different implementations conform with the rc.exe behavior. See win32-samples-rc-tests for a more wide-ranging comparison on real-world .rc files.
See the version of Zig used in CI to determine which version of Zig should be used to build resinator.
git clone https://github.com/squeek502/resinator
cd resinator
zig build
zig build test
The 'fuzzy' tests are a collection of tests that may be similar to either fuzz testing or property testing. With default settings, the more fuzzing-like tests will run for 1000 iterations. These tests rely on rc.exe being available on PATH since each test uses rc.exe as an oracle to determine if resinator behavior is expected or not.
zig build test_fuzzy
will run all of the 'fuzzy' tests.
Each 'fuzzy' test can be run individually, too. For example, this will run the fuzzy_numbers test infinitely:
zig build test_fuzzy_numbers -Diterations=0
- Requires winafl to be installed/built.
- Requires
rc.exeto be available on thePATH
To build the fuzzer:
zig build fuzz_winafl
To run the fuzzer, from within the winafl/build64/bin directory (replace C:\path\to\ with the actual path to the relevant directories):
set PATH_TO_INPUTS_DIR=C:\path\to\resinator\test\inputs
set PATH_TO_OUTPUTS_DIR=C:\path\to\resinator\test\outputs
set PATH_TO_DYNAMORIO_BIN=C:\path\to\DynamoRIO-Windows\bin64
set PATH_TO_RESINATOR_FUZZER=C:\path\to\resinator\zig-out\bin\fuzz_winafl.exe
afl-fuzz.exe -i "%PATH_TO_INPUTS_DIR%" -o "%PATH_TO_OUTPUTS_DIR%" -D "%PATH_TO_DYNAMORIO_BIN%" -t 20000 -- -coverage_module fuzz_winafl.exe -target_module fuzz_winafl.exe -target_method fuzzMain -fuzz_iterations 5000 -nargs 2 -- "%PATH_TO_RESINATOR_FUZZER%" @@
Currently, Linux fuzz testing only tests the resinator implementation for crashes/bugs, and does not check for correctness against rc.exe.
- Requires afl++ with
afl-clang-ltoto be installed.
zig build fuzz_rc
To run the fuzzer:
afl-fuzz -i test/inputs -o test/outputs -- ./zig-out/bin/fuzz_rc
Ideally, this fuzzer would compare the outputs against rc.exe but wine was not able to perfectly emulate rc.exe last I tried (need to investigate this more, I was using a rather old version of wine).

