Skip to content

pylauncher: link against ucrt.lib instead of statically linking whole CRT#26383

Merged
RReverser merged 17 commits intomainfrom
tiny-exe
Mar 6, 2026
Merged

pylauncher: link against ucrt.lib instead of statically linking whole CRT#26383
RReverser merged 17 commits intomainfrom
tiny-exe

Conversation

@RReverser
Copy link
Collaborator

Reduces pylauncher.exe from 153 KB to 5 KB by dynamically linking against ucrtbase.dll (Universal CRT) instead of statically embedding the full CRT.

This avoids any dependency on a specific Visual C++ Redistributable version - which is the usual concern with dynamic build, as those versioned DLLs can be a pain to deal with - yet results in a much smaller executable size.

ucrtbase.dll ships as part of the OS since Windows 10 (2015) and is available via Windows Update for Vista/7/8/8.1, so it should be perfectly safe to depend on for any realistic developer machine.

… CRT

Reduces pylauncher.exe from 153 KB to 5 KB by dynamically linking against
ucrtbase.dll (Universal CRT) instead of statically embedding the full CRT.

This avoids any dependency on a specific Visual C++ Redistributable version - which is the usual concern with dynamic build, as those versioned DLLs can be a pain to deal with - yet results in a much smaller executable size.

ucrtbase.dll ships as part of the OS since Windows 10 (2015) and is available
via Windows Update for Vista/7/8/8.1, so it should be perfectly safe to depend on for any realistic developer machine.
Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic! Thanks

#undef ZeroMemory
#define ZeroMemory SecureZeroMemory

#pragma comment(lib, "shlwapi.lib")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why move this out of the source file an into the build scripts?

Copy link
Collaborator Author

@RReverser RReverser Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency - because the .sh version doesn't respect it anyway (it's MSVC specific pragma), so it's cleaner to use respective linking flags in sh and bat that mirror each other.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, mingw doesn't support this pragma?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah these pragmas are MSVC specific, and you use GCC over there.

We could try and standardise on one compiler (eg Clang is perfectly easy to use on Windows and Linux alike, and we already depend on it anyway) or just drop one of the scripts, but didn't want to do either in this PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So clang would support #pragma comment(lib, "shlwapi.lib") where mingw doesn't?

I think I'd rather build the official executable with cl.exe, for some reason it seems less likely to me to run into the security scanner issues (but perhaps that is misguided).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to say yes, because Clang on Windows supports MSVC ABI and pragmas - Microsoft poured a lot of investment into it in recent years and it's now an official first class citizen in MSVC alongside cl.exe.

However, it looks like MinGW would still need different libraries because it uses its own ABI so pragmas would be parsed by the Clang frontend but not used properly by the mingw linker.

Out of curiosity, why do you even care about supporting both? Supporting two different ABIs feels like asking for a debugging headache, especially for such a small tool...

Copy link
Collaborator

@sbc100 sbc100 Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only care because I don't have a windows machine myself so its useful to be able to quickly test "does it compile" locally without having to ssh into a circleci bot modify this code.

But not that its mostly written I don't really care about keeping the build.sh working .. I mean its nice to have, but non-essential.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edit: I only care because..

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean its nice to have, but non-essential.

Yeah I don't mind either one, I'd probably just say that eg we only support MinGW variant and .exe must be produced in WSL for Windows users, or vice versa.

But then, it's not that many flags for now so maybe not too difficult to keep both versions.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the Windows Python launcher build/link strategy to significantly reduce pylauncher.exe size by avoiding static CRT embedding, while keeping the launcher’s behavior the same (locate matching .py and run via python.exe).

Changes:

  • Switch MSVC build to /NODEFAULTLIB and link against ucrt.lib (dynamic ucrtbase.dll) for a smaller pylauncher.exe.
  • Adjust pylauncher.c to avoid vcruntime dependencies (e.g., ZeroMemory macro handling) and simplify main signature.
  • Update build scripts/docs and ignore paths to match the new tools/pylauncher location and linking approach.

Reviewed changes

Copilot reviewed 4 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tools/pylauncher/pylauncher.exe Updated checked-in launcher binary reflecting new link strategy.
tools/pylauncher/pylauncher.c Code tweaks to support ucrt-only linking and updated entry point expectations.
tools/pylauncher/build.sh MinGW build flags adjusted (timestamp) and removed -lshell32.
tools/pylauncher/build.bat New MSVC link setup using /NODEFAULTLIB, /ENTRY:main, and ucrt.lib.
tools/pylauncher/README.md Refresh formatting and document the two build modes (MinGW vs MSVC/ucrt).
.gitignore Fix ignored object output path for pylauncher.obj.
Comments suppressed due to low confidence (3)

tools/pylauncher/pylauncher.c:15

  • The file header comment says the launcher is “Built with /NODEFAULTLIB linking only against ucrt.lib”, but this source is also built via build.sh (MinGW) where those MSVC flags don’t apply. Please clarify this comment as MSVC-specific (or describe both build modes) to avoid misleading future maintainers.
 *
 * Built with /NODEFAULTLIB linking only against ucrt.lib (ucrtbase.dll) to
 * avoid any dependency on a specific Visual C++ Redistributable version.
 */

tools/pylauncher/pylauncher.c:45

  • dbg uses va_list/va_start/va_end but this file doesn’t include <stdarg.h>. Even if this currently compiles via transitive includes, it’s brittle—please include <stdarg.h> explicitly for portability and clarity.
static void dbg(const char* format, ...) {
  if (launcher_debug) {
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);

tools/pylauncher/README.md:6

  • Minor grammar: “uses the its own name” should be “uses its own name”.
uses the its own name (the name of the currently running executable) to
determine which python script to run and serves the same purpose as the

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

RReverser and others added 3 commits March 3, 2026 20:38
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Clarified comment about disabling buffer security checks in build.bat.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 6 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (3)

tools/pylauncher/README.md:6

  • Grammar: "uses the its own name" should be "uses its own name".
uses the its own name (the name of the currently running executable) to
determine which python script to run and serves the same purpose as the

tools/pylauncher/pylauncher.c:45

  • dbg uses va_list/va_start but the file doesn't include <stdarg.h>. This may fail to compile on toolchains where stdio.h doesn't transitively include it; explicitly include <stdarg.h> with the other headers.
static void dbg(const char* format, ...) {
  if (launcher_debug) {
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);

tools/pylauncher/pylauncher.c:15

  • The header comment says the binary is built with /NODEFAULTLIB linking only against ucrt.lib, but this source is also built by build.sh (MinGW) where those flags don't apply. Consider scoping this note to the MSVC build (e.g., mention build.bat) so the comment doesn't misrepresent the MinGW build configuration.
 * Built with /NODEFAULTLIB linking only against ucrt.lib (ucrtbase.dll) to
 * avoid any dependency on a specific Visual C++ Redistributable version.
 */

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@sbc100
Copy link
Collaborator

sbc100 commented Mar 3, 2026

I have no idea why co-polit chimed in here.. we don't pay for it, and I don't normally see it chiming in. Maybe its turned on, but it only takes action when you (the PR author) has paid for it?

@RReverser
Copy link
Collaborator Author

RReverser commented Mar 3, 2026

Oh huh I do have it through the Github's OSS maintainer program, but never had it pop up like this in other repos, so I thought you "called" for it to review.

Oh well, at least it did find one minor bug (fixed now).

@sbc100
Copy link
Collaborator

sbc100 commented Mar 3, 2026

I disabled automatic co-pilot reviews

@sbc100
Copy link
Collaborator

sbc100 commented Mar 3, 2026

Thanks for working on this.

BTW, were you able to try this out locally? Did it work well for you?

@RReverser
Copy link
Collaborator Author

RReverser commented Mar 4, 2026

BTW, were you able to try this out locally? Did it work well for you?

I only did a few quick checks for now, not proper testing yet.

But no security issues if that's what you are asking about.

@RReverser
Copy link
Collaborator Author

FWIW I played a bit more with .exe variants, so far so good.

The CI seems to have gotten even more flaky than I remember from last time...

@RReverser RReverser enabled auto-merge (squash) March 6, 2026 03:30
@RReverser RReverser merged commit 42c0a51 into main Mar 6, 2026
39 checks passed
@RReverser RReverser deleted the tiny-exe branch March 6, 2026 18:34
@RReverser
Copy link
Collaborator Author

@sbc100 Btw I just realised that the Path* functions you are using here still only support paths up to MAX_PATH (260 characters) and would break if Emscripten is installed deeper.

The choices are to either switch to the newer PathCch* functions which don't have the same limitation but are supported "only" from Windows 8 (excluding Windows 7, OTOH it's long out of official support by now anyway) or switch to manual string manipulation.

@RReverser
Copy link
Collaborator Author

or switch to manual string manipulation

I'll try to do this one, shouldn't be too complex.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants