windowsassemblyx86visual-studio-2019masm

How can I call printf function correctly from assembly?


I am trying to call an x86 C function, printf, in assembly, but there is a linking error: error LNK2019: unresolved external symbol _printf referenced in function _main.

.model flat, C
.code
extern printf: proc

main proc
    push offset msg
    push offset fmt
    call printf
    ret
main endp

.data
    msg db "Hello world!", 0
    fmt db "%s", 0
end

How can I solve that problem?


Solution

  • here can be several ways. if want use existing libs - _printf was in legacy_stdio_definitions.lib but if use it need also link to legacy_stdio_wide_specifiers.lib and ucrt.lib, call __initterm ( or set entry point to _wmainCRTStartup ) complete code in this case can look like

    .686
    
    .model flat
    
    extern _printf: PROC
    extern __imp__exit: DWORD
    extern __imp___initterm: DWORD
    
    .code
    
    ep proc
    
        push offset __xc_z
        push offset __xc_a
        call __imp___initterm
        add esp,8
        
        push offset msg
        push offset fmt
        call _printf
        add esp,8
        
        push eax
        call __imp__exit
        
        ret
    ep endp
    
    CRT$XIA SEGMENT ALIAS(".CRT$XIA")
    __xc_a DD 0
    CRT$XIA ENDS
    
    CRT$XIZ SEGMENT ALIAS(".CRT$XIZ")
    __xc_z DD 0
    CRT$XIZ ENDS
    
    .const
        msg db "Hello world!", 0
        fmt db "%s", 0
    end
    

    (note that better use .const section instead .data here for constand data )

    and link file with

    
    link.exe /MAP /MERGE:.CRT=.rdata /SUBSYSTEM:CONSOLE /ENTRY:ep /LARGEADDRESSAWARE /DYNAMICBASE /MACHINE:X86 /SAFESEH:NO /OPT:REF /OPT:ICF /NODEFAULTLIB /EMITPOGOPHASEINFO /EMITVOLATILEMETADATA:NO /LIBPATH:"C:\lib\um\x86" /LIBPATH:"C:\msvc\lib\x86" /LIBPATH:"C:\lib\ucrt\x86" test.obj legacy_stdio_definitions.lib legacy_stdio_wide_specifiers.lib ucrt.lib
    

    another way - build my self msvcrt.lib ( standard msvcrt.lib have no printf despite it was exported by msvcrt.dll and then use it ) minimal way

    
    .686
    .model flat
    API MACRO name
    name proc
    name endp
    ENDM
    .code
    API _printf
    end
    

    and msvcrt.def :

    EXPORTS
    printf
    

    build with

    link.exe /DLL /NOENTRY:ep /LARGEADDRESSAWARE /DYNAMICBASE /MACHINE:X86 /SAFESEH:NO /OPT:REF /OPT:ICF /NODEFAULTLIB /EMITPOGOPHASEINFO /EMITVOLATILEMETADATA:NO /DEF:msvcrt.def msvcrt.obj
    

    and than can use this

    .686
    
    .model flat
    
    extern __imp__printf: DWORD
    extern __imp__ExitProcess@4: DWORD
    
    .code
    
    ep proc
    
        push offset msg
        push offset fmt
        call __imp__printf
        add esp,8
        
        push eax
        call __imp__ExitProcess@4
        
        ret
    ep endp
    
    .const
        msg db "Hello world!", 0
        fmt db "%s", 0
    end
    

    link with:

    link.exe /MAP /SUBSYSTEM:CONSOLE /ENTRY:ep /LARGEADDRESSAWARE /DYNAMICBASE /MACHINE:X86 /SAFESEH:NO /OPT:REF /OPT:ICF /NODEFAULTLIB /EMITPOGOPHASEINFO /EMITVOLATILEMETADATA:NO /LIBPATH:"C:\lib\um\x86" /LIBPATH:"C:\msvc\lib\x86" /LIBPATH:"C:\lib\ucrt\x86" test2.obj msvcrt.lib kernel32.lib
    

    (more general way auto build lib for dll - here)