Saving 1 KiB on Rust executables targetting windows-gnu
While comparing a Rust executable for Windows targetting x86_64-pc-windows-gnu
and one targetting
x86_64-pc-windows-msvc
, I noticed that the -gnu
one included an embedded
application manifest
resource. This particular manifest does two things: setting
requestedExecutionLevel
to asInvoker
, and setting
supportedOS
es from Windows Vista to Windows 10.
As far as I can tell, the first part attempts to disable the Windows
installer detection heuristics. However, the documentation appears to indicate that these heuristics are only used on 32-bit binaries, and the
fact that the -msvc
executable doesn’t have the manifest reinforces the idea that it’s not needed.
The second part of the manifest is only useful if you want to indicate that you don’t support Windows versions prior to Vista. I think for most people that would be the default assumption these days.
These things considered, it looks to me that removing the manifest shouldn’t cause any issues. The problem is that there doesn’t seem to be any built-in way to do this provided by either the OS or the compiler toolchain. You may have to rely on a third-party tool to do this.
If you don’t mind deleting all embedded resources in the executable—by default there will just be the application
manifest—you can use this simple C code (replace file.exe
with your executable path):
#include <windows.h>
int main(void) {
return 1 - EndUpdateResourceW(BeginUpdateResourceW(L"file.exe", 1), 0);
}
Here’s a slightly more robust but longer Python alternative:
import ctypes
from ctypes import wintypes
import sys
kernel32 = ctypes.windll.kernel32
def check_trueish(result, *_):
if result:
return result
raise ctypes.WinError()
BeginUpdateResource = kernel32.BeginUpdateResourceW
BeginUpdateResource.restype = wintypes.HANDLE
BeginUpdateResource.argtypes = (ctypes.c_wchar_p, wintypes.BOOL)
BeginUpdateResource.errcheck = check_trueish
EndUpdateResource = kernel32.EndUpdateResourceW
EndUpdateResource.restype = wintypes.BOOL
EndUpdateResource.argtypes = (wintypes.HANDLE, wintypes.BOOL)
EndUpdateResource.errcheck = check_trueish
for arg in sys.argv[1:]:
file_name = ctypes.create_unicode_buffer(arg)
handle = BeginUpdateResource(file_name, True)
EndUpdateResource(handle, False)
The slightly bad news here is that, in my testing, removing this manifest only reduces the executable size by
exactly 1024 bytes. Considering the x86_64-pc-windows-gnu
target generally produces executables in the
hundreds of kilobytes at least, this is a fairly inconsequential saving which I probably won’t bother with.