After nearly 16 hours of debugging, I've found the culprit: `_iob` and `_imp___iob` should have been declared with either `__attribute__((dllimport))` or `__declspec(dllimport)`
To be more specific, lines 93 to 106 of <stdio.h> in TCC's ./include directory are currently written as follows: ```c #ifndef _STDIO_DEFINED #ifdef _WIN64 _CRTIMP FILE *__cdecl __iob_func(void); #else #ifdef _MSVCRT_ extern FILE _iob[]; /* A pointer to an array of FILE */ #define __iob_func() (_iob) #else extern FILE (*_imp___iob)[]; /* A pointer to an array of FILE */ #define __iob_func() (*_imp___iob) #define _iob __iob_func() #endif #endif #endif ``` To fix them so that it compiles under `-m32` just as fine as it does under `-m64`, you need to change them to the following: ```c #ifndef _STDIO_DEFINED #ifdef _WIN64 _CRTIMP FILE *__cdecl __iob_func(void); #else #ifdef _MSVCRT_ __attribute__((dllimport)) extern FILE _iob[]; /* A pointer to an array of FILE */ #define __iob_func() (_iob) #else __attribute__((dllimport)) extern FILE (*_imp___iob)[]; /* A pointer to an array of FILE */ #define __iob_func() (*_imp___iob) #define _iob __iob_func() #endif #endif #endif ``` (NOTE: As I said earlier, both `__attribute__((dllimport))` and `__declspec(dllimport)` work here. Either of them fix it for both _MSVCRT_ and non-_MSVCRT_ headers.) A few additional details on why this fix is even useful in the first place: - freopen() is optional in -subsystem=console (i.e., CLI) but absolutely required if you compile in -subsystem=windows (i.e., GUI) mode; if freopen() isn't called in the latter, printf and other std outputs don't show up on the allocated console; - freopen() depends on the value of `stdout`, and `stdout` is defined based on either _iob or _imp___iob in stdio.h; - _imp___iob is too old and no longer exists in newer msvcrt.dll or ucrtbase.dll, making _iob (and therefore defining _MSVCRT_) the go-to choice; and - #define'ing _MSVCRT_ before <windows.h> is akin to not defining it at all; I think <windows.h> undefines it somewhere, internally. I still find it strange that such a "fix" was required, because TCC works just fine under `-m64` anyway. For `-m32` builds, do you think we should patch the stdio.h in the mob branch ourselves? I'm not sure if this is a TCC-specific patch or if it actually affects any compiler that uses stdio.h from MinGW. (Also, I did compile the latest mob branch using "build-tcc.bat -x -c cl" but this stdio.h bug affected it the same way it affected 0.9.27.) On Sun, Nov 24, 2024 at 12:09 PM avih <avih...@yahoo.com> wrote: > > tcc 0.9.27 is few years old now, and hopefully 0.9.28 would be released > sooner rather than later, so you should try the latest version, which is the > mob (default) branch here: > https://repo.or.cz/tinycc.git > > You'll need to build it yourself. If you can't build it or can't find a > recent pre-build binary, then I can upload a version to some pastebin (do > ask, but do try to build it first if you can). > > You should also keep in mind that tcc is a not mingw gcc clone. It does use > (old) mingw headers, but you should not expect 100% compatibility, and the > headers set is intentionally stripped down to keep it minimal (although many > programs compile out of the box with the existing headers) > > Specifically about your issue. With latest tcc, if I comment out the line > with "freopen" in your sample program, or with a minimal program like this: > > #define _MSVCRT_ > #include <stdio.h> > > int main() { > printf("Hello, world\n"); > return 0; > } > > Then it compiles and works fine. > > But with your original program with freopen, tcc indeed complains about > undefined symbol '_iob'. > > However, if I move the _MSVCRT_ definition above #include <windows.h>, then > it does compile (I didn't try to run it, but I presume it would work). > > So I'd think largely it's OK. However, I was not aware of this _MSVCRT_ > thingy, so I don't know what differences to expect and I didn't try to > understand it further. > > I don't know whether this should be considered an issue that it works in tcc > only if _MSVCRT_ is defined before windows.h is included (to me that feels > reasonable, so do double check), but if it should be considered an issue, > then maybe you could help by trying to pinpoint the cause at the headers, and > report back. > > Cheers, > avih > > > On Sunday, November 24, 2024 at 07:43:30 PM GMT+2, Fereydoun Memarzanjany > via Tinycc-devel <tinycc-devel@nongnu.org> wrote: > > > If you use TinyCC in its 32-bit mode (`-m32`) to compile a sample > program that uses any CRT function/symbol from `msvcrt.dll` (such as > the snippet provided later in this message), you'll be met with > compilation failures: > > `tcc.exe -std=c11 -Wall -Werror -Wl,-subsystem=console -m32 .\main.c` > "tcc: error: undefined symbol '_iob', missing __declspec(dllimport)?" > > (This only happens under `-m32`, whereas `-m64` works perfectly fine.) > > `_iob` is not the only "unresolved" symbol, either; `printf`, > `freopen`, `freopen_s`, and basically everything from the CRT will > fail to link. > > Regardless of whether or not you use `-lmsvcrt`, `#pragma comment(lib, > "msvcrt")`, `_declspec(dllimport)`, `attribute ((dllimport))`, > `-static` or `-shared`, or even `-impdef` on > "C:\Windows\SysWow64\msvcrt.dll" (or earlier versions thereof: > "msvcrt40.dll"), TCC still complains. > > I've verified with `DUMPBIN.exe` that both 32- and 64-bit "msvcrt.dll" > do, in fact, define `_iob` and other symbols. > > By some arcane logic, the following works perfectly fine: `tcc.exe > -std=c11 -Wall -Werror -Wl,-subsystem=console -m64 .\main.c` > > `main.c` > ```c > //#pragma comment(lib, "msvcrt") > //__attribute__((dllimport)) extern __declspec(dllimport) FILE _iob[]; > > #include <windows.h> > > // _MSVCRT_ being defined will cause MinGW's stdio.h to use _iob as > // opposed to _imp___iob; only the former is defined in msvcrt.dll. > // However, even though _iob is exported by both the 32- and 64-bit > // versions of said dll, TinyCC still fails to find _iob in the former. > #define _MSVCRT_ > #include <stdio.h> > > void main() { > // AllocConsole() and basically everything from kernel32.dll or > // user32.dll work perfectly fine, both in -m32 and -m64; it's > // only msvcrt.dll that causes issues with TinyCC. > AllocConsole(); > > // Any CRT function (e.g., freopen, freopen_s, printf, etc.) > // fail to get linked properly ONLY in -m32; -m64 is fine. > // Even if I change the -I and -L paths to C:/Windows/SysWow64 > // and/or use tcc.exe -impdef to create .def files from them, > // TCC still fails in finding _iob and other symbols. > // Also, using #pragma comment(lib, "msvcrt") or -lmsvcrt > // doesn't help at all. Even if you do get TCC to somehow > // stop complaining about missing symbols, it'd just include > // a blank IAT.printf or IAT.freopen, causing segfaults. > freopen("CONOUT$", "w", stdout); > printf("This only compiles (and prints) under TCC in 64-bit mode."); > } > ``` > > As mentioned earlier, this error in `-m32` happens regardless of other > switches like `-std`, `-shared`, `-static`, `-lmsvcrt`, `-subsyetem`, > etc. So, at this point, I'm starting to think this might really be a > bug with TinyCC 0.9.27 (Win32 & Win64 builds) itself. > > _______________________________________________ > Tinycc-devel mailing list > Tinycc-devel@nongnu.org > https://lists.nongnu.org/mailman/listinfo/tinycc-devel _______________________________________________ Tinycc-devel mailing list Tinycc-devel@nongnu.org https://lists.nongnu.org/mailman/listinfo/tinycc-devel