10

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

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

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"
jtxkopt
  • 916
  • 1
  • 8
  • 21
  • 2
    [Microsoft has moved some standard C stuff into another library](https://learn.microsoft.com/en-us/cpp/porting/visual-cpp-change-history-2003-2015), namely `legacy_stdio_definitions.lib`. Make sure you link with that properly and you also might need to prepend a leading underscore – Jester Oct 18 '20 at 12:44
  • @Jester I added the prefix _ to printf, but it didn't fix the problem. Also I changed the command line arguments as `link hello_world.obj /subsystem:console /entry:main /out:hello_world_basic.exe "KERNEL32.LIB" "UCRT.LIB" "legacy_stdio_definitions.lib"` – jtxkopt Oct 18 '20 at 13:07
  • 1
    @Jester Thanks. I solved the problem. – jtxkopt Oct 18 '20 at 14:30
  • 1
    IIRC, 64-bit Windows doesn't prepend a leading _ to C symbol names, only 32-bit Windows does that. – Peter Cordes Oct 18 '20 at 15:54

4 Answers4

11

Managed to solve this after reading the answer from Darran Rowe in this discussion. He also explains a bit more why these things need to be done.

Here's the solution:

Run the link command in x64 Native Tools Command Prompt for VS 20XX that you can launch from Start menu under Visual Studio 20XX (Developer Command Prompt for VS 20XX suggested in the tutorial doesn't work).

Use this as the link command:

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

The changes here are:

  • /entry:main has been removed
  • kernel32.lib legacy_stdio_definitions.lib msvcrt.lib have been added
Hemaolle
  • 1,950
  • 18
  • 21
  • This works! But I wonder why `/entry:main` is removed? (I can confirm that with `/entry:main` it will generate errors, so it should indeed be removed - just wondering why) – Tom Charles Zhang Feb 13 '23 at 20:12
  • @TomCharlesZhang, quoting Darran Rowe from the discussion I linked: "because I am linking against the CRT, main is being called from the CRT entry point, hence I don't set the /entry option for the linker." – Hemaolle Feb 14 '23 at 21:10
8

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
jtxkopt
  • 916
  • 1
  • 8
  • 21
0

In one of the above answers it should be (so remove 'LIB=')

set 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

0

This works successfully on my Microsoft Windows Professional (all 1-liners)

@REM this LIB directory G:\LANGUAGE\COMPUTER\ASSEMBLER\NASM\LIB\ contains these files: kernel32.Lib, legacy_stdio_definitions.lib, legacy_stdio_wide_specifiers.lib, msvcrt.lib, ucrt.lib, vcruntime.lib

@REM Note: I had to use everything.exe to find all these files, they were not all found in the directories of Microsoft Visual Studio 2022

SET LIB=G:\LANGUAGE\COMPUTER\ASSEMBLER\NASM\LIB\

"g:\cygwin\bin\nasm.exe" -f win64 -o c:\temp\helloworld.obj c:\temp\helloworld.asm

"c:\program files\microsoft visual studio\2022\professional\vc\tools\msvc\14.34.31933\bin\hostx86\x64\link.exe" c:\temp\helloworld.obj /subsystem:console /entry:main /out:c:\temp\helloworld.exe "kernel32.lib" "ucrt.lib" "legacy_stdio_definitions.lib"

and this is the helloworld.asm used:

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

Then after creation of the .obj file it is linked using link.exe and it creates an executable helloworld.exe successfully.