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

Call C standard library function from asm in Visual Studio

我在从 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

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

Error LNK2019 unresolved external symbol _printf referenced in function _main@0

链接器输出参数:

/OUT:"C:\Users\apple\Documents\SP_Lab7\Debug\SP_Lab7_Demo.exe" /MANIFEST:NO /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: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" /ERRORREPORT:PROMPT /NOLOGO /TLBID:1

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

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

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

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

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

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

似乎可以使用 Visual Studio 2015 工具集并进行一些修改。

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

代码可能如下所示:

.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