WINE 是否在 MSVCRT 中实现了“_printf”和类似的功能?
Does WINE implement "_printf" and similar functions in MSVCRT?
这是一个说明我的问题的示例程序,它可以在不使用链接器的情况下使用 FlatAssembler 进行编译:
format PE console
entry start
include 'win32a.inc'
section '.text' code executable
start:
mov dword [esp],_output1
call [printf]
mov dword [esp+4],first
mov dword [esp],_input
call [scanf]
mov dword [esp],_output2
call [printf]
mov dword [esp+4],second
mov dword [esp],_input
call [scanf]
finit
fld dword [first]
fabs
fld dword [second]
fxch
fld1
fxch
fyl2x
fldl2e
fdivp st1,st0
fmulp st1,st0
fldl2e
fmulp st1,st0
fld1
fscale
fxch
fld1
fxch
fprem
f2xm1
faddp st1,st0
fmulp st1,st0
fstp dword [result]
fld dword [result]
fst qword [esp+4]
mov dword [esp],_output
call [printf]
invoke system,_pause
invoke exit,0
_output1 db "Enter the first number: ",0
_output2 db "Enter the second number: ",0
_input db "%f",0
_pause db "PAUSE",0
_output db "The first number to the power of the second number is: %f.",10,0
section '.rdata' readable writable
result dd ?
first dd ?
second dd ?
section '.idata' data readable import
library msvcrt,'msvcrt.dll'
import msvcrt,printf,'printf',system,'system',exit,'exit',scanf,'scanf'
所以,预期的输出当然是这样的:
Enter the first number: -2.5
Enter the second number: -2
The first number to the power of the second number is: 0.16
这就是我在 运行 Windows 10 上 运行 该程序时确实得到的输出。但是,如果我尝试 运行 在 Oracle 上的 WINE 上 Linux 该程序],我得到的输出是:
000f:fixme:service:scmdatabase_autostart_services Auto-start service L"MountMgr" failed to start: 2
000f:fixme:service:scmdatabase_autostart_services Auto-start service L"WineBus" failed to start: 2
wine: Bad EXE format for Z:\home\teo.samarzija\Documents\Assembly\debug.exe.
知道发生了什么事吗?
我做了一些研究,但我找不到任何参考资料来确认 _printf
和 _scanf
甚至在 WINE 的 MSVCRT 中实现。但是,我不确定这是问题所在,如果是问题,那就是唯一的问题。
However, if I try to run that program on WINE on Oracle Linux, the
output I get is:
000f:fixme:service:scmdatabase_autostart_services Auto-start service L"MountMgr" failed to start: 2
000f:fixme:service:scmdatabase_autostart_services Auto-start service L"WineBus" failed to start: 2
wine: Bad EXE format for Z:\home\teo.samarzija\Documents\Assembly\debug.exe.
A "Bad EXE format" 错误完全不同。这并不意味着问题是缺少导入函数。装载机从来没有走那么远。它甚至无法读取您的二进制文件。这很可能是由位数不匹配引起的。例如,尝试在 32 位系统上 运行 64 位应用程序。
除了这个问题,值得指出的是,您尝试使用 C 运行time 库函数本质上是 non-portable。如果 Wine(或任何其他 运行time 环境)提供具有相同签名的函数,它 可能 工作,但它很可能不会。
我想我应该进一步澄清,因为调用 standard C 运行time 库函数 "non-portable" 可能会引起一些人的注意。这些函数在 source-code 级别是可移植的,但在 binary 级别是不可移植的。即使没有增加 Wine 的复杂性,C 运行time 库函数也是 non-portable,因为 Microsoft 的 CRT 是 versioned——你必须 link适当的版本并在 运行 时提供该 DLL,否则您的应用程序将无法运行。
这个确切的问题就是为什么 Windows 为这些标准功能提供包装器作为基本平台 API 的一部分,它是普遍可用的。如果你想完全移植到 Win32 环境的所有实现,并且你没有 linking 在你自己的 C 运行time 库副本中,那么你应该调用这些函数。
sprintf
函数的 Win32 版本是 wsprintf
。它具有与 sprintf
相同的接口,因此您可以以相同的方式调用它,作为 drop-in 的替代品。事实上,虽然你不应该依赖它,但它是由 Windows 实现的,作为 C 运行time 库的本地副本提供的 sprintf
版本的简单包装器。
如果你想要一个可以传递参数列表的版本(la vsprintf
),那么你可以调用 wvsprintf
.
请注意,与大多数 Windows API 函数不同,这些函数使用 __cdecl
调用约定,而不是 __stdcall
调用约定。确保您在汇编代码中遵守该规定。简而言之,这意味着从 right-to-left 传递参数并在调用站点清理堆栈。
但是,Microsoft 已弃用这些函数,因为它们并不完全安全(可能会发生缓冲区溢出等)。作为替代品,它们提供 StrSafe.h
header 中的功能。这些函数有两种变体:一种是计算字节数 (Cb
),另一种是计算字符数 (Cch
)。与此讨论相关的是 StringCbPrintfA
or StringCchPrintfA
。然而,这些在汇编语言中使用起来比较棘手,因为它们旨在通过简单地包含 StrSafe.h
header 文件来内联使用。您可以以库形式使用它们,但随后您需要将相应的 StrSafe.lib
存根传递给 link 人员。请注意,link访问此库意味着您的应用程序只能在 Windows SP2 或更高版本的 XP 上 运行。
这让你成功了一半。您实际上是在尝试调用 printf
,而不是 sprintf
。当然,差距在于将格式化的字符串写入控制台。一旦你有了格式化的字符串(由 wsprintf
、StringCchPrintfA
或其他什么生成),就可以通过调用 WriteConsole
函数来完成,它是用于写入的 Win32 API输出到控制台 window。如果你想要 STDOUT,那么你需要先用 GetStdHandle(STD_OUTPUT_HANDLE)
.
打开那个句柄
无论如何,我得到了答案:
简而言之,在 64 位 Oracle Linux 上,WINE 需要从源代码编译才能正常工作。
这是一个说明我的问题的示例程序,它可以在不使用链接器的情况下使用 FlatAssembler 进行编译:
format PE console
entry start
include 'win32a.inc'
section '.text' code executable
start:
mov dword [esp],_output1
call [printf]
mov dword [esp+4],first
mov dword [esp],_input
call [scanf]
mov dword [esp],_output2
call [printf]
mov dword [esp+4],second
mov dword [esp],_input
call [scanf]
finit
fld dword [first]
fabs
fld dword [second]
fxch
fld1
fxch
fyl2x
fldl2e
fdivp st1,st0
fmulp st1,st0
fldl2e
fmulp st1,st0
fld1
fscale
fxch
fld1
fxch
fprem
f2xm1
faddp st1,st0
fmulp st1,st0
fstp dword [result]
fld dword [result]
fst qword [esp+4]
mov dword [esp],_output
call [printf]
invoke system,_pause
invoke exit,0
_output1 db "Enter the first number: ",0
_output2 db "Enter the second number: ",0
_input db "%f",0
_pause db "PAUSE",0
_output db "The first number to the power of the second number is: %f.",10,0
section '.rdata' readable writable
result dd ?
first dd ?
second dd ?
section '.idata' data readable import
library msvcrt,'msvcrt.dll'
import msvcrt,printf,'printf',system,'system',exit,'exit',scanf,'scanf'
所以,预期的输出当然是这样的:
Enter the first number: -2.5
Enter the second number: -2
The first number to the power of the second number is: 0.16
这就是我在 运行 Windows 10 上 运行 该程序时确实得到的输出。但是,如果我尝试 运行 在 Oracle 上的 WINE 上 Linux 该程序],我得到的输出是:
000f:fixme:service:scmdatabase_autostart_services Auto-start service L"MountMgr" failed to start: 2
000f:fixme:service:scmdatabase_autostart_services Auto-start service L"WineBus" failed to start: 2
wine: Bad EXE format for Z:\home\teo.samarzija\Documents\Assembly\debug.exe.
知道发生了什么事吗?
我做了一些研究,但我找不到任何参考资料来确认 _printf
和 _scanf
甚至在 WINE 的 MSVCRT 中实现。但是,我不确定这是问题所在,如果是问题,那就是唯一的问题。
However, if I try to run that program on WINE on Oracle Linux, the output I get is:
000f:fixme:service:scmdatabase_autostart_services Auto-start service L"MountMgr" failed to start: 2 000f:fixme:service:scmdatabase_autostart_services Auto-start service L"WineBus" failed to start: 2 wine: Bad EXE format for Z:\home\teo.samarzija\Documents\Assembly\debug.exe.
A "Bad EXE format" 错误完全不同。这并不意味着问题是缺少导入函数。装载机从来没有走那么远。它甚至无法读取您的二进制文件。这很可能是由位数不匹配引起的。例如,尝试在 32 位系统上 运行 64 位应用程序。
除了这个问题,值得指出的是,您尝试使用 C 运行time 库函数本质上是 non-portable。如果 Wine(或任何其他 运行time 环境)提供具有相同签名的函数,它 可能 工作,但它很可能不会。
我想我应该进一步澄清,因为调用 standard C 运行time 库函数 "non-portable" 可能会引起一些人的注意。这些函数在 source-code 级别是可移植的,但在 binary 级别是不可移植的。即使没有增加 Wine 的复杂性,C 运行time 库函数也是 non-portable,因为 Microsoft 的 CRT 是 versioned——你必须 link适当的版本并在 运行 时提供该 DLL,否则您的应用程序将无法运行。
这个确切的问题就是为什么 Windows 为这些标准功能提供包装器作为基本平台 API 的一部分,它是普遍可用的。如果你想完全移植到 Win32 环境的所有实现,并且你没有 linking 在你自己的 C 运行time 库副本中,那么你应该调用这些函数。
sprintf
函数的 Win32 版本是 wsprintf
。它具有与 sprintf
相同的接口,因此您可以以相同的方式调用它,作为 drop-in 的替代品。事实上,虽然你不应该依赖它,但它是由 Windows 实现的,作为 C 运行time 库的本地副本提供的 sprintf
版本的简单包装器。
如果你想要一个可以传递参数列表的版本(la vsprintf
),那么你可以调用 wvsprintf
.
请注意,与大多数 Windows API 函数不同,这些函数使用 __cdecl
调用约定,而不是 __stdcall
调用约定。确保您在汇编代码中遵守该规定。简而言之,这意味着从 right-to-left 传递参数并在调用站点清理堆栈。
但是,Microsoft 已弃用这些函数,因为它们并不完全安全(可能会发生缓冲区溢出等)。作为替代品,它们提供 StrSafe.h
header 中的功能。这些函数有两种变体:一种是计算字节数 (Cb
),另一种是计算字符数 (Cch
)。与此讨论相关的是 StringCbPrintfA
or StringCchPrintfA
。然而,这些在汇编语言中使用起来比较棘手,因为它们旨在通过简单地包含 StrSafe.h
header 文件来内联使用。您可以以库形式使用它们,但随后您需要将相应的 StrSafe.lib
存根传递给 link 人员。请注意,link访问此库意味着您的应用程序只能在 Windows SP2 或更高版本的 XP 上 运行。
这让你成功了一半。您实际上是在尝试调用 printf
,而不是 sprintf
。当然,差距在于将格式化的字符串写入控制台。一旦你有了格式化的字符串(由 wsprintf
、StringCchPrintfA
或其他什么生成),就可以通过调用 WriteConsole
函数来完成,它是用于写入的 Win32 API输出到控制台 window。如果你想要 STDOUT,那么你需要先用 GetStdHandle(STD_OUTPUT_HANDLE)
.
无论如何,我得到了答案:
简而言之,在 64 位 Oracle Linux 上,WINE 需要从源代码编译才能正常工作。