windowsassemblyx86-64nasmlinker-errors

Unresolved external symbol printf in Windows x64 Assembly Programming with NASM


I've been trying to learn assembly lately, and came across this post. The author used NASM and Microsoft linker to set up the assembly working environment. I followed the same steps and installed NASM. Then I started to compile the hello world application. The compilation is successful, but I get an error at the link stage. The error is as follows:

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

The above is the output of Microsoft Linker (link.exe). I run the link commands from Developer Command Prompt as described in the post, and because hello world is a 64-bits application I set the LIB environment variable correctly (even though not mentioned on the post ).

Here is the sample program used as "Hello World" assembly program.

hello_world.asm:

bits 64
default rel          ; make [msg] default to RIP-relative, not 32-bit absolute

segment .data
   msg: db "Hello world!", 0xd, 0xa, 0    ; CR LF and terminating 0

segment .text
global main
extern ExitProcess
extern printf

main:
   push    rbp
   mov     rbp, rsp        ; frame pointer
   sub     rsp, 32         ; shadow space

   lea     rcx, [msg]
   call    printf

   xor     ecx, ecx
   call    ExitProcess    ; or better:   call exit
                          ; to flush stdout if redirected to a file

  ; leave              ; or just return from main
  ; xor    eax,eax     ; with main's return value becoming exit status
  ; ret

To reproduce the issue, execute the commands respectively.

1) To compile the program on windows command prompt.

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

2) To set LIB environment variable.

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

3) And to link into an executable.

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

Solution

  • According to the link Microsoft has moved some standard C stuff into another library @Jester has shared.

    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.

    You need to link against the library legacy_stdio_definitions.lib for the implementation of printf and also need to initialize CRT. Therefore, change the source code in the question to the following:

    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
    

    And finally, run the linker as follows.

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