【问题标题】:Call C standard library function from asm in Visual Studio在 Visual Studio 中从 asm 调用 C 标准库函数
【发布时间】:2016-02-16 16:56:49
【问题描述】:

我在从 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 /增量:否 /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

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

【问题讨论】:

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

标签: visual-studio assembly x86 linker-errors masm


【解决方案1】:

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

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

代码可能如下所示:

.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

【讨论】:

    【解决方案2】:

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

    更改平台工具集:下拉Project 菜单;选择Properties...;转到Configuration Properties/General,并将Platform Toolset 更改为Visual Studio 2013 (v120)

    【讨论】:

      【解决方案3】:

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

      该库可能需要初始化,并且可能需要您在某处定义一堆缓冲区用于记账。

      我建议您坚持使用 Windows API,而不是遇到所有这些麻烦,在您的情况下使用 WriteConsole 函数。

      【讨论】:

      • 感谢指点使用 WinAPI 函数,因为我想要的主要函数是 strcspn 和类似的函数 StrSpn 可用。但是我还是不明白,为什么includelib msvcrt.lib不加载C库。
      猜你喜欢
      • 1970-01-01
      • 2018-09-27
      • 2021-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-08
      • 2015-03-15
      相关资源
      最近更新 更多