使用NASM在Windows x64汇编程序中无法解析的外部符号printf

问题描述

我最近一直在尝试学习汇编语言,并遇到了这个post。作者使用NASM和Microsoft链接器来设置组装环境。我遵循相同的步骤并安装了NASM。然后,我开始编译hello world应用程序。编译成功,但是在链接阶段出现错误错误如下:

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

这是Microsoft链接器(link.exe)的输出。我按照文章中的描述运行来自Developer Command Prompt的链接命令,并且因为hello world是64位应用程序,所以我正确设置了LIB环境变量(即使文章中未提及)。

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 Studio\2019\Community\VC\Tools\MSVC\14.27.29110\ATLMFC\lib\x86;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\lib\x64;C:\Program Files (x86)\Windows Kits\NETFxsdk\4.8\lib\um\x86;C:\Program Files (x86)\Windows Kits\10\lib\10.0.19041.0\ucrt\x64;C:\Program Files (x86)\Windows Kits\10\lib\10.0.19041.0\um\x64

链接到可执行文件

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

出什么问题了?我有什么想念的吗?

解决方法

根据链接Microsoft has moved some standard C stuff into another library @Jester已共享。

所有printf和scanf函数的定义都已内联移动到和其他CRT标头中。此重大更改将导致在本地声明这些功能但不包括适当的CRT标头的任何程序的链接器错误(LNK2019,未解析的外部符号)。如果可能,应更新代码以包括CRT标头(即添加#include )和内联函数,但是如果您不想修改代码以包含这些头文件,则另一种解决方案是在链接器输入中添加一个额外的库legacy_stdio_definitions.lib。

因此,您需要链接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

最后,按如下所示运行链接器。

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 中运行链接命令,您可以在 Visual Studio 20XX 下从“开始”菜单启动该命令(教程中建议的 Developer Command Prompt for VS 20XX 不起作用)。

将其用作链接命令:

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 已添加