[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Tinycc-devel] Why does TinyCC fail to link standard C runtime funct
From: |
Fereydoun Memarzanjany |
Subject: |
Re: [Tinycc-devel] Why does TinyCC fail to link standard C runtime functions in 32-bit mode but works in 64-bit mode? |
Date: |
Sun, 24 Nov 2024 14:05:28 -0700 |
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 <avihpit@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
Re: [Tinycc-devel] Why does TinyCC fail to link standard C runtime functions in 32-bit mode but works in 64-bit mode?, avih, 2024/11/24