zig icon indicating copy to clipboard operation
zig copied to clipboard

Zig doesn't detect wWinMain symbol in C files.

Open eLeCtrOssSnake opened this issue 1 year ago • 12 comments

Zig Version

0.12.0-dev.2159+7916cf6f8

Steps to Reproduce and Observed Behavior

Compile some C code that has a wWinMain startup point. (my use case: C application built by build.zig) Observe that compiler ignores wWinMain definition and instead complains about the absence of main.

Side notes: This issue is not present on the 0.11.0 version. Declaring main instead of wWinMain works. This is the current workaround that I am using. This is observed on both x86-windows and x86_64-windows

Zig build log: https://paste.sr.ht/~electrosssnake/da3a6d579b24d868d2f32f75f3c7b95b43686142 Whole build.zig: https://paste.sr.ht/~electrosssnake/ce4e6a9b7f184d910e2b309381da62ccf871b922 C file(start.c) that contains windows startup symbol: https://paste.sr.ht/~electrosssnake/8ac72ced13aed44f53549fe354baa9adeb068b56

Expected Behavior

Zig correctly detects wWinMain and builds the application.

eLeCtrOssSnake avatar Jan 20 '24 03:01 eLeCtrOssSnake

@squeek502 related to https://github.com/ziglang/zig/pull/17763 ?

expikr avatar Jan 20 '24 17:01 expikr

@expikr, no, shouldn't be. My guess is https://github.com/ziglang/zig/pull/16109 or https://github.com/ziglang/zig/pull/13514.

Would be good to get a minimal failing test case for this that can be added to the standalone tests once this is fixed.

squeek502 avatar Jan 20 '24 18:01 squeek502

Minimal test case:

// wwinmain.c
#include <windows.h>

int APIENTRY wWinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PWSTR cmdline, int cmdshow) {
    return 0;
}

Fails:

> zig build-exe wwinmain.c -lc
error: lld-link: undefined symbol: main
    note: referenced by C:\Users\Ryan\Programming\Zig\zig\lib\libc\mingw\crt\crtexe.c:267
    note:               C:\Users\Ryan\AppData\Local\zig\o\01924fb85758874f67102f866bb06ff5\crt2.obj:(__tmainCRTStartup)

Targeting the MSVC ABI does work, so it's very likely MinGW related:

> zig build-exe wwinmain.c -lc -target native-windows-msvc

So probably caused by https://github.com/ziglang/zig/pull/16109, but still need to confirm that.

squeek502 avatar Jan 20 '24 19:01 squeek502

Hm, I can't actually get wWinMain to compile successfully with 0.11.0 either:

> "C:\Users\Ryan\Programming\Zig\zig-windows-x86_64-0.11.0\zig.exe" build-exe wwinmain.c -lc
error: lld-link: undefined symbol: WinMain
    note: referenced by C:\Users\Ryan\Programming\Zig\zig-windows-x86_64-0.11.0\lib\libc\mingw\crt\crt0_c.c:18
    note:               mingw32.lib(crt0_c.obj):(main)

Using WinMain works with 0.11.0, though:

#include <windows.h>

int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PSTR cmdline, int cmdshow) {
    return 0;
}

squeek502 avatar Jan 20 '24 21:01 squeek502

You didn't #define UNICODE before including windows.h

Paul-Dempsey avatar Jan 20 '24 23:01 Paul-Dempsey

Tried that, didn't seem to change anything and isn't done in the OP reproduction.

EDIT: Also, wWinMain is detected and compiled as expected when targeting the MSVC ABI (without the need for #define UNICODE):

> zig build-exe wwinmain.c -lc -target native-windows-msvc
> dumpbin /headers wwinmain.exe
OPTIONAL HEADER VALUES
            13B0 entry point (00000001400013B0) wWinMainCRTStartup
               2 subsystem (Windows GUI)

squeek502 avatar Jan 20 '24 23:01 squeek502

In usual C windows app, wWinMain would be a "wide", meaning Unicode entry. For Unicode app, one #defines UNICODE or _UNICODE before including windows.h. (and pass the linker flag to indicate that the app is for the windows subsystem). Not sure what magic zig does for Wndows targets, though.

Paul-Dempsey avatar Jan 20 '24 23:01 Paul-Dempsey

Unless I'm mistaken, I think any magic being done is performed by the linker. From the /ENTRY docs:

If the /DLL or /SUBSYSTEM option is not specified, the linker selects a subsystem and entry point depending on whether main or WinMain is defined.

In this case, /ENTRY is not being set explicitly, so it's up to the linker to figure out the entry and subsystem. In both the -gnu and -msvc case, Zig is using lld-link.

Here's the --verbose-link of the successful -msvc target (that finds wWinMain and identifies the subsystem as Windows, see the edit in my post above):

lld-link -ERRORLIMIT:0 -NOLOGO -DEBUG -PDB:wwinmain.pdb -PDBALTPATH:wwinmain.pdb -STACK:16777216 -BASE:4194304 -MACHINE:X64 -OUT:wwinmain.exe -IMPLIB:wwinmain.lib -LIBPATH:C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\ucrt\x64 -LIBPATH:C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933\Lib\x64 -LIBPATH:C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64 C:\Users\Ryan\Programming\Zig\tmp\zig-cache\o\46f93a70bc677800051255e7f42ea4d3\wwinmain.obj libcmtd.lib libvcruntimed.lib libucrtd.lib legacy_stdio_definitions.lib kernel32.lib ntdll.lib C:\Users\Ryan\AppData\Local\zig\o\8438dc6e9ec5db159875bf6f754de588\compiler_rt.lib

And here's the --verbose-link of the failing -gnu target (this is Zig master):

lld-link -ERRORLIMIT:0 -NOLOGO -DEBUG -PDB:wwinmain.pdb -PDBALTPATH:wwinmain.pdb -STACK:16777216 -BASE:4194304 -MACHINE:X64 -INCLUDE:_tls_index -OUT:wwinmain.exe -IMPLIB:wwinmain.lib C:\Users\Ryan\Programming\Zig\tmp\zig-cache\o\1499d7be033dc90abbc0973f6b59191b\wwinmain.obj -lldmingw -ALTERNATENAME:__image_base__=__ImageBase C:\Users\Ryan\AppData\Local\zig\o\847d3e8ad61a4ab9750e0d4aa9146dca\crt2.obj C:\Users\Ryan\AppData\Local\zig\o\f12e821dbfd38e42aae511e9654fca07\mingw32.lib C:\Users\Ryan\AppData\Local\zig\o\c2392418c63a5ab2a03a19960edd8e69\mingwex.lib C:\Users\Ryan\AppData\Local\zig\o\8edafe95963d1cd8b407527ac6593287\uuid.lib C:\Users\Ryan\AppData\Local\zig\o\56e5f3e56df86bc4014af1a379e6e042\compiler_rt.lib C:\Users\Ryan\AppData\Local\zig\o\505bc0d71ff1285b1eb0cc64da28c44e\ucrtbase.lib C:\Users\Ryan\AppData\Local\zig\o\ee182b5bddf3e9a2675745d8c5ffe60b\advapi32.lib C:\Users\Ryan\AppData\Local\zig\o\790905d8de4800ac8c29de90cdead242\kernel32.lib C:\Users\Ryan\AppData\Local\zig\o\40eecd0ec8726abb8504993aba165a94\ntdll.lib C:\Users\Ryan\AppData\Local\zig\o\223ffca71a434db4d4da5510f10ff21f\shell32.lib C:\Users\Ryan\AppData\Local\zig\o\1cab3585b4c499d173061b2f01cddd5b\user32.lib

And here's the --verbose-link when using 0.11.0 and targeting -gnu (which is also failing for me but was reported to succeed in the OP):

lld-link -ERRORLIMIT:0 -NOLOGO -DEBUG -PDB:wwinmain.pdb -PDBALTPATH:wwinmain.pdb -STACK:16777216 -MACHINE:X64 -INCLUDE:_tls_index -OUT:wwinmain.exe -IMPLIB:wwinmain.lib C:\Users\Ryan\AppData\Local\zig\o\d76861145d4e143b4af210cbdfbc3200\wwinmain.obj -lldmingw -ALTERNATENAME:__image_base__=__ImageBase C:\Users\Ryan\AppData\Local\zig\o\829949829aa7326dafc4f1f0f6d9c46a\crt2.obj C:\Users\Ryan\AppData\Local\zig\o\008204667472725c754aa94752e9e2b6\mingw32.lib C:\Users\Ryan\AppData\Local\zig\o\66b415c51e4aeb7de6d40842bf1b4203\mingwex.lib C:\Users\Ryan\AppData\Local\zig\o\a08a47ce6962154e04236f0d5b665be3\msvcrt-os.lib C:\Users\Ryan\AppData\Local\zig\o\c26457baea6286ace30a53be12ab2fcc\uuid.lib C:\Users\Ryan\AppData\Local\zig\o\f1972e2c1c4c97624e9cef0904e8a65e\ssp.lib C:\Users\Ryan\AppData\Local\zig\o\583f619fc83310fca4d63e7a8e3ee6db\compiler_rt.lib C:\Users\Ryan\AppData\Local\zig\o\7efad3ef36db57a1639f59ece7a3aa42\advapi32.lib C:\Users\Ryan\AppData\Local\zig\o\7611b689f99fc5b3e10b0b40738df706\kernel32.lib C:\Users\Ryan\AppData\Local\zig\o\7c21b883223120ff9d0540f5b993eb68\msvcrt.lib C:\Users\Ryan\AppData\Local\zig\o\b4823eabe65fcca4946c88b20391d579\ntdll.lib C:\Users\Ryan\AppData\Local\zig\o\bea7906fdb568e9bcad4265a9e219c2b\shell32.lib C:\Users\Ryan\AppData\Local\zig\o\c913a797e5913938baf70100f03b0aaf\user32.lib

squeek502 avatar Jan 20 '24 23:01 squeek502

Observe that compiler ignores wWinMain definition and instead complains about the absence of main.

Have you tried putting

pub const wWinMain = void;

in your root source file?

andrewrk avatar Mar 12 '24 00:03 andrewrk

root source file

Note that this is a problem with compiling C files, not Zig files.

Minimal reproduction is

#include <windows.h>

int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PSTR cmdline, int cmdshow) {
    return 0;
}
zig build-exe winmain.c -lc -target native-windows-gnu --subsystem windows

This worked in 0.11.0.

squeek502 avatar Mar 12 '24 00:03 squeek502

cc @ypsvlq on the off-chance you have some insight on this.

EDIT: This seems potentially relevant: https://sourceforge.net/p/mingw-w64/mailman/mingw-w64-public/thread/20221113114053.ze3h5pt4s7do53fi@pali/ but including crtexewin.c in crt2 doesn't seem to change the linker looking for the main symbol.

squeek502 avatar Mar 22 '24 19:03 squeek502

In msvc, the linker detects wWinMain/WinMain/wmain/main and chooses the appropriate startup code. In mingw, unicode is enabled by the -municode switch and WinMain is called by a fallback implementation of (w)main, which is (u)crtexewin from libmingw32.a.

Currently Zig doesn't build crtexewin, but other changes might be needed, I'll look in more detail later.

ypsvlq avatar Mar 22 '24 20:03 ypsvlq

I'm encoutering this using zig 0.11.0, and tested back on 0.10.0 this also appears. I don't think this was supported in zig and it should not be marked as regression?

$ zig version
0.11.0

$ zig c++ main.cpp
LLD Link... lld-link: error: undefined symbol: WinMain
>>> referenced by [...]\zig\current\lib\libc\mingw\crt\crt0_c.c:18
>>>               mingw32.lib(crt0_c.obj):(main)

$ zig version
0.10.0

$ zig c++ main.cpp
LLD Link... lld-link: error: undefined symbol: WinMain
>>> referenced by mingw32.lib(crt0_c.obj):(main)

minimal repro

int wmain(int argc, wchar_t* argv[]) {
    return 0;
}

chawyehsu avatar Mar 26 '24 04:03 chawyehsu

@chawyehsu WinMain and wmain are two different things.

RossComputerGuy avatar Mar 26 '24 04:03 RossComputerGuy

@RossComputerGuy Did I misunderstand here? I'm talking about the issue of unicode entry against mingw here. I know the difference between WinMain and wmain.

chawyehsu avatar Mar 26 '24 04:03 chawyehsu

See https://github.com/ziglang/zig/pull/19399#issuecomment-2019420611: the regression is with regards to WinMain, and I think you're right that the unicode entry points have never been supported when targeting MinGW.

squeek502 avatar Mar 26 '24 06:03 squeek502

I'm getting similar with 0.12.0-dev.3674+a0de07760 Target also x86-windows-gnu Being driven from a zig.build which is a somewhat unwieldy translation of a (to me) complex Makefile as an attempt to build the commandline Tclsh shell. I supply -mconsole - but am unclear on where/how to try adding --subsystem arguments in this context.

error: lld-link: undefined symbol: WinMain note: referenced by C:\zig\lib\libc\mingw\crt\crtexewin.c:67 note: mingw32.lib(crtexewin.obj):(main) error: lld-link -ERRORLIMIT:0 -NOLOGO -DEBUG -PDB:C:\buildtcl\2024zig\build_tcl90\zig-cache\o\e62bac644c2625c5965792447010bbc5\tclsh.pdb -PDBALTPATH:tclsh.pdb -STACK:16777216 -BASE:4194304 -MACHINE:X64 -INCLUDE:_tls_index -OUT:C:\buildtcl\2024zig\build_tcl90\zig-cache\o\e62bac644c2625c5965792447010bbc5\tclsh.exe -IMPLIB:C:\buildtcl\2024zig\build_tcl90\zig-cache\o\e62bac644c2625c5965792447010bbc5\tclsh.lib ...

I note that a search of the zig-cache\o doesn't find the tclsh.pdb or tclsh.lib shown in the lld-link error line above - but I have generated dlls for this project where the .pdb files are output both in the zig-cache and zig-out.

juliannoble avatar Apr 19 '24 03:04 juliannoble

@juliannoble What main function are you expecting the compiler to use? See https://github.com/ziglang/zig/pull/19399#issuecomment-2016527584 for what you need to pass to zig build-exe for each possible main function and see https://github.com/ziglang/zig/blob/master/test/standalone/windows_entry_points/build.zig for the build.zig equivalents.

squeek502 avatar Apr 19 '24 04:04 squeek502

Sorry, I don't know enough about entry points and specifically how tclsh works to answer. I am using -municode at the appropriate place I think - and also -DUNICODE and -D_UNICODE tclsh requires compiling tclMain.c twice - once with unicode. The object names are then tclMain.o and tclMainW.o I couldn't find any zig.build way to do this other than by making another copy of tclMain.c as tclMainW.c tclAppInit.c also requires unicode flags and has something to do with it. ( _tmain ) My apologies if it's not appropriate to mention here - but I'm willing to pay for assistance either directly and/or donation to zig project - I'm just not sure how much of this problem is specific to zig vs perhaps peculiarities in the way this project operates.

juliannoble avatar Apr 19 '24 04:04 juliannoble

I think this is just a case of the error message being confusing. If I try to compile a completely empty C file (with no main function) I get the same error as you:

> zig build-exe empty.c -lc --subsystem console
error: lld-link: undefined symbol: WinMain
    note: referenced by C:\Users\Ryan\Programming\Zig\zig\lib\libc\mingw\crt\crtexewin.c:67
    note:               mingw32.lib(crtexewin.obj):(main)

So what's likely happening is that you're trying to compile an executable but the linker is unable to find any suitable main symbol at all.

If so, this is unrelated to this issue (this issue is about wanting to use WinMain/wWinMain as your main function) and I'd suggest asking for help with your problem in one of the community spaces.

squeek502 avatar Apr 19 '24 05:04 squeek502