为什么TinyCC在32位模式下无法链接标准C运行时函数,但在64位模式下却可以工作?

问题描述 投票:0回答:1

如果您在 32 位模式 (-m32) 下使用

TinyCC
来编译使用 msvcrt.dll 中的任何 CRT 函数/符号的示例程序(例如本问题后面提供的代码片段),您'会遇到编译失败:

tcc.exe -std=c11 -Wall -Werror -Wl,-subsystem=console -m32 .\main.c

“tcc:错误:未定义符号'_iob',缺少__declspec(dllimport)?”

(这只发生在

-m32
下,而
-m64
工作得很好。)

_iob
也不是唯一“未解决”的符号;
printf
freopen
freopen_s
,基本上来自 CRT 的所有内容都将无法链接。

无论您是否使用

-lmsvcrt
#pragma comment(lib, "msvcrt")
_declspec(dllimport)
attribute ((dllimport))
-static
-shared
,甚至在
C:\Windows\SysWow64\msvcrt.dll
上使用-impdef (或其早期版本:msvcrt40.dll),TCC *仍然*抱怨。

我已经用

DUMPBIN.exe
验证了 32 位和 64 位 msvcrt.dlls do,实际上都定义了
_iob
和其他符号。

通过一些神秘的逻辑,以下内容完全可以正常工作:

tcc.exe -std=c11 -Wall -Werror -Wl,-subsystem=console -m64 .\main.c

main.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.");
}

如前所述,无论其他开关如

-m32
-std
-shared
-static
-lmsvcrt
等,
-subsyetem
中的错误都会发生。所以,在这一点上,我开始认为这可能确实是 TinyCC 0.9.27 (Win32 & Win64 构建)本身。

c linker mingw msvcrt tcc
1个回答
0
投票

经过近16个小时的调试,我找到了罪魁祸首:

_iob
_imp___iob
应该用
__attribute__((dllimport))
__declspec(dllimport)
来声明。

具体来说,TCC的./include目录中的第93106行目前写法如下:

#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

修复它们,使其在

-m32
下编译得一样好 在
-m64
下,您需要将它们更改为以下内容:

#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

(注意:正如我之前所说,

__attribute__((dllimport))
__declspec(dllimport)
在这里工作。 他们中的任何一个都可以解决这个问题
_MSVCRT_
和非
_MSVCRT_
标头。)

关于为什么此修复首先有用的一些其他详细信息:

  • freopen()
    -subsystem=console
    (即 CLI)中是可选的,但是 如果您在
    -subsystem=windows
    (即 GUI)中编译,则绝对需要 模式;如果后者没有调用
    freopen()
    ,则
    printf()
    和其他流 输出不会显示在分配的控制台上;
  • freopen()
    取决于
    stdout
    的值,
    stdout
    是 基于
    ;
    中的 _iob_imp___iob
  • 定义
  • _imp___iob
    太旧,不再存在于较新的 msvcrt.dllucrtbase.dll 中,使得
    _iob
    (因此定义
    _MSVCRT_
    )成为首选;和
  •  之前使用 
    #define 类似于根本不定义它;我认为  在内部某个地方取消了它的定义。
    
    
  • 我仍然觉得需要这样的“修复”很奇怪,因为无论如何,
TCC

_MSVCRT_下工作得很好。 我还不确定这是否是

TCC
特定的补丁,或者它是否实际上影响任何使用 MinGW 中的 的编译器; 我将在 TinyCC 的邮件列表中跟进其余内容。 (另外,我确实使用

-m64

从 2024 年 10 月 24 日起编译了最新的 mob 分支,但是这个

 错误对它的影响与影响 0.9.27 的方式相同。)
    
	

© www.soinside.com 2019 - 2024. All rights reserved.