从 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.lib 和 legacy_stdio_wide_specifiers.lib,但您也可以选择使用较旧的 Visual Studio 2013 平台工具集与较旧的 C 库。
要更改平台工具集:下拉 Project
菜单; select Properties...
;转到 Configuration Properties
/General
,并将 Platform Toolset
更改为 Visual Studio 2013 (v120)
似乎可以使用 Visual Studio 2015 工具集并进行一些修改。
- 您需要将这些库添加到您的依赖项中:libcmt.lib、libvcruntime.lib、libucrt.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
我在从 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.lib 和 legacy_stdio_wide_specifiers.lib,但您也可以选择使用较旧的 Visual Studio 2013 平台工具集与较旧的 C 库。
要更改平台工具集:下拉 Project
菜单; select Properties...
;转到 Configuration Properties
/General
,并将 Platform Toolset
更改为 Visual Studio 2013 (v120)
似乎可以使用 Visual Studio 2015 工具集并进行一些修改。
- 您需要将这些库添加到您的依赖项中:libcmt.lib、libvcruntime.lib、libucrt.lib,legacy_stdio_definitions.lib。或者,您可以使用
includelib
将这些库包含在您的程序集文件中。 - 使用
PROC C
为您的 - 在文件末尾(这很重要)不要使用
end main
,仅使用end
。不解决这个问题可能会导致意外崩溃。 - 尽管我们可以使用 ExitProcess 退出我们的应用程序,我们也可以将 return 代码放入 EAX 并执行
ret
到 return。 C 运行时调用我们的main
函数,并将在 returning 时为我们调用关闭代码。
main
过程指定 C 调用约定
代码可能如下所示:
.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