Windows x64 汇编编程 NASM 中未解析的外部符号 printf

Unresolved external symbol printf in Windows x64 Assembly Programming with NASM

我最近一直在尝试学习汇编,遇到了这个post。笔者使用NASM和微软linker搭建了汇编环境。我按照相同的步骤安装了 NASM。然后我开始编译 hello world 应用程序。编译成功,但是在link阶段报错。错误如下:

hello_world.obj : error LNK2001: unresolved external symbol printf
hello_world_basic.exe : fatal error LNK1120: 1 unresolved external

这是微软 linker(link.exe) 的输出。我 运行 link 来自 Developer Command Prompt 的命令,如 post 中所述,并且因为 hello world 是一个 64 位应用程序,我正确设置了 LIB 环境变量(即使在post ).

hello_world.asm

bits 64
default rel

segment .data
   msg db "Hello world!", 0xd, 0xa, 0

segment .text
global main
extern ExitProcess
extern printf




main:
   push    rbp
   mov     rbp, rsp
   sub     rsp, 32

   lea     rcx, [msg]
   call    printf

   xor     rax, rax
   call    ExitProcess

在 windows 命令提示符下编译程序。

nasm -f win64 -o hello_world.obj hello_world.asm

设置LIB环境变量。

set LIB=LIB=C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.27.29110\ATLMFC\lib\x86;C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.27.29110\lib\x64;C:\Program Files (x86)\Windows Kits\NETFXSDK.8\lib\um\x86;C:\Program Files (x86)\Windows Kits\lib.0.19041.0\ucrt\x64;C:\Program Files (x86)\Windows Kits\lib.0.19041.0\um\x64

然后 link 变成可执行文件。

link hello_world.obj /subsystem:console /entry:main /out:hello_world_basic.exe "KERNEL32.LIB"

问题是什么?有什么我遗漏的吗?

根据 link Microsoft has moved some standard C stuff into another library @Jester 分享。

The definitions of all of the printf and scanf functions have been moved inline into <stdio.h>, <conio.h>, and other CRT headers. This breaking change leads to a linker error (LNK2019, unresolved external symbol) for any programs that declared these functions locally without including the appropriate CRT headers.If possible, you should update the code to include the CRT headers (that is, add #include <stdio.h>) and the inline functions, but if you do not want to modify your code to include these header files, an alternative solution is to add an additional library to your linker input, legacy_stdio_definitions.lib.

因此你需要link legacy_stdio_definitions.lib来实现printf并且还需要初始化CRT所以把源码改成,

bits 64
default rel

segment .data
    msg db "Hello world!", 0xd, 0xa, 0

segment .text
global main
extern ExitProcess
extern _CRT_INIT

extern printf

main:
    push    rbp
    mov     rbp, rsp
    sub     rsp, 32

    call    _CRT_INIT

    lea     rcx, [msg]
    call    printf

    xor     rax, rax
    call    ExitProcess

最后,运行 linker 如下。

link hello_world.obj /subsystem:console /entry:main /out:hello_world_basic.exe kernel32.lib legacy_stdio_definitions.lib  msvcrt.lib

阅读 this discussion 中 Darran Rowe 的回答后设法解决了这个问题。他还解释了为什么需要做这些事情。

解决方法如下:

运行 x64 Native Tools Command Prompt for VS 20XX 中的 link 命令,您可以从 Visual Studio 20XX 下的“开始”菜单启动(教程中建议的 Developer Command Prompt for VS 20XX 不工作)。

将此用作 link 命令:

link hello_world.obj /subsystem:console /out:hello_world_basic.exe kernel32.lib legacy_stdio_definitions.lib msvcrt.lib

这里的变化是:

  • /entry:main 已删除
  • kernel32.lib legacy_stdio_definitions.lib msvcrt.lib 已添加