在 Visual Studio 中从 asm 调用 C 标准库函数

Call C standard library function from asm in Visual Studio

提问人:Sunrise 提问时间:11/15/2015 最后编辑:Michael PetchSunrise 更新时间:9/11/2019 访问量:7064

问:

我从Visual Studio(Win10 x64,Visual Studio 2015)中创建的asm项目调用C函数时遇到问题。项目由一个 asm 文件组成:

.586
.model flat, stdcall
option casemap:none
includelib msvcrt.lib

ExitProcess PROTO return:DWORD
extern printf:near

.data
text BYTE "Text", 0

.code
main PROC
    push offset text
    call printf
    add esp,4
    invoke ExitProcess,0
main ENDP
end main

当我构建项目时,链接器输出错误:

错误LNK2019 中引用的未解析的外部符号_printf 功能_main@0

链接器输出参数:

/OUT:“C:\Users\apple\Documents\SP_Lab7\Debug\SP_Lab7_Demo.exe” /清单:否 /NXCOMPAT /PDB:“C:\Users\apple\Documents\SP_Lab7\Debug\SP_Lab7_Demo.pdb” /DYNAMICBASE “kernel32.lib”: “user32.lib”: “gdi32.lib”: “winspool.lib”, “comdlg32.lib”: “advapi32.lib”: “shell32.lib”: “ole32.lib”: “oleaut32.lib”, “uuid.lib”: “odbc32.lib”: “odbccp32.lib”, /MACHINE:X86, /SAFESEH:NO /INCREMENTAL:否 /PGD:“C:\Users\apple\Documents\SP_Lab7\Debug\SP_Lab7_Demo.pgd” /SUBSYSTEM:WINDOWS /MANIFESTUAC:“level='asInvoker' uiAccess='false'” /ManifestFile:“Debug\SP_Lab7_Demo.exe.intermediate.manifest” /错误报告:提示 /NOLOGO /TLBID:1

如果我注释,那么一切都正常执行(甚至是 Windows API 函数)。有没有办法从 asm 文件调用 C 函数而不创建包含的 cpp 文件? 可以做到吗?call print<cstdio>

visual-studio 程序集 x86 链接器错误 masm

评论

1赞 Michael Petch 11/16/2015
解决方法是将 VS 设置为 2013。为此,请下拉菜单,选择 。转到 /,然后更改为 Visual Studio 2013 (v120)platform toolsetProjectproperties...Configuration PropertiesGeneralPlatform Toolset
0赞 Sunrise 11/16/2015
@MichaelPetch 它真的很有效,谢谢
2赞 Michael Petch 11/16/2015
Microsoft重构了大部分C运行时。某些函数不再在库中导出(某些函数在 C 头文件中定义)。MS 有一些兼容性库 legacy_stdio_definitions.liblegacy_stdio_wide_specifiers.lib ,但我还没有让 printf 使用它们,所以我回退到 VS 2013 工具集。可能还有另一种解决方法,但我还没有看到它。
1赞 Michael Petch 11/16/2015
我添加了一个新答案,其中包含您可能有兴趣尝试的解决方案。它使用 Visual Studio 2015 工具集。在尝试之前,必须从 Visual Studio 2013 工具集切换到 2015。如果这对你有用,我非常谨慎。

答:

1赞 user3703887 11/15/2015 #1

您可以调用 C 函数,但随后需要与 C 库链接。具体如何完成将取决于您要链接的 C 库。我建议找到一个最小的 C 运行时,例如 WCRT 库

该库可能需要初始化,并且可能需要您在某处定义一堆缓冲区来保存其簿记。

与其麻烦,我建议你坚持使用 Windows API,在你的情况下使用 WriteConsole 函数。

评论

0赞 Sunrise 11/15/2015
感谢您指出使用 WinAPI 函数,因为我想要的主要函数是和类似的函数可用。但是我还是不明白,为什么不加载C库。strcspnStrSpnincludelib msvcrt.lib
12赞 Michael Petch 11/16/2015 #2

Microsoft 在 VS 2015 中重构了大部分 C 运行时和库。某些函数不再从 C 库导出(某些函数在 C 头文件中定义)。Microsoft 有一些兼容性库,如 legacy_stdio_definitions.liblegacy_stdio_wide_specifiers.lib,但你也可以选择将较旧的 Visual Studio 2013 平台工具集与较旧的 C 库一起使用。

要更改平台工具集:下拉菜单;选择;转到 /,然后更改为 Visual Studio 2013 (v120)ProjectProperties...Configuration PropertiesGeneralPlatform Toolset

10赞 Michael Petch 11/16/2015 #3

似乎可以通过一些修改来使用 Visual Studio 2015 工具集。

  • 您需要将这些库添加到依赖项中:libcmt.liblibvcruntime.lib、libucrt.lib legacy_stdio_definitions.lib或者,您可以使用这些库包含在程序集文件中。includelib
  • 使用指定过程的 C 调用约定mainPROC C
  • 在文件末尾(这很重要)不要使用 ,仅使用。如果不解决此问题,可能会导致意外崩溃。end mainend
  • 虽然我们可以使用 ExitProcess 退出我们的应用程序,但我们也可以将返回代码放在 EAX 中并执行 return 操作。C 运行时调用我们的函数,并在返回时为我们调用关闭代码。retmain

代码可能如下所示:

.586
.model flat, stdcall
option casemap:none

includelib libcmt.lib
includelib libvcruntime.lib
includelib libucrt.lib
includelib legacy_stdio_definitions.lib

ExitProcess PROTO return:DWORD
extern printf:NEAR

.data
text BYTE "Text", 0

.code
main PROC C                    ; Specify "C" calling convention
    push offset text
    call printf
    add  esp, 4
;   invoke ExitProcess,0       ; Since the C library called main (this function)
                               ; we can set eax to 0 and use ret`to have
                               ; the C runtime close down and return our error
                               ; code instead of invoking ExitProcess
    mov eax, 0
    ret
main ENDP
end                            ; Use `end` on a line by itself
                               ; We don't want to use `end main` as that would
                               ; make this function our program entry point
                               ; effectively skipping by the C runtime initialization

评论

0赞 rosshjb 8/7/2023
在 Visual Studio 2022 中似乎就足够了。learn.microsoft.com/en-us/cpp/porting/......includelib legacy_stdio_definitions.lib