1

Consider the following assembly (MASM) code (helloworld.asm):

extern puts: PROC

.data
msg db 'hello world', 0Ah, 00

.code
main proc
    sub rsp, 40
    lea rcx, msg
    call puts
    add rsp, 40
    ret
main endp

End

It compiles fine under Windows 7 (after installing the necessary development tools and setting up the environment) and behaves as expected:

$ ml64 helloworld.asm /link ucrt.lib vcruntime.lib msvcrt.lib /subsystem:console
[..]

$ helloworld.exe
hello world

However, if I replace puts with printf in helloworld.asm on line 1 and line 10, the linker fails to resolve the reference to printf:

$ ml64 helloworld.asm /link ucrt.lib vcruntime.lib msvcrt.lib /subsystem:console
[..]
helloworld.obj : error LNK2019: unresolved external symbol "printf" referenced in function "main".
helloworld.exe : fatal error LNK1120: 1 unresolved externals

As a bonus: If i use the static library files libucrt.lib, libvcruntime.lib and libcmt.lib, it works with printf:

$ ml64 helloworld.asm /link libucrt.lib libvcruntime.lib libcmt.lib /subsystem:console
Microsoft (R) Macro Assembler (x64) Version 14.11.25547.0
Copyright (C) Microsoft Corporation.  All rights reserved.

 Assembling: helloworld.asm
Microsoft (R) Incremental Linker Version 14.11.25547.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/OUT:helloworld.exe
helloworld.obj
libucrt.lib
libvcruntime.lib
libcmt.lib
/subsystem:console

$ helloworld.exe
hello world

This does not make any sense to me. I can obviously compile a C-program with cl which uses the dynamic library files (by specifying the /MD flag) and the linker has no problems resolving the printf symbol.

What is happening here and how can it be solved?

user1658887
  • 449
  • 4
  • 15
  • Because Microsoft reworked part of the C runtime (as of VS 2015 I believe) so that some of the functionality was exposed via _C_ headers and not in libraries. It all depends on which toolset is being used. – Michael Petch Oct 26 '17 at 19:34
  • linker search name in input libs. if name not found - it simply not exist in this libs. you need pass lib to linker which containing this symbol. you need use *legacy_stdio_definitions.lib, legacy_stdio_wide_specifiers.lib, ucrt.lib* in this case. and better use `call [__imp_printf]` instead `call printf` – RbMm Oct 26 '17 at 20:26
  • One alternative is to have a project that includes one C source file in addition to the assembly source file(s). The C source file would include a dummy function that call printf(), which in turn creates an instance of printf() (in the compiled object file) that can then be called by the assembly source file. It won't matter that the dummy function in the C source file is never called, as the goal is to create a callable instance of printf(). I often have a mix of assembly and C, which is why I use this alternative. – rcgldr Oct 28 '17 at 17:01

0 Answers0