Search code examples
cassemblyx86fasm

Assembler program crashes after call to printf


I am writing a simple function to print a float value from stack. This function is generated, so it is not optimized. Program crashes at printf call.

;input: float32 as dword ptr ebp+8
printfloat32:
 push   ebp
  mov   ebp,    esp
  sub   esp,    16
;local ptr variable k at dword ptr ebp-4
  mov   dword ptr ebp-4,    lpcstr4 ;which is "%f"

movss       xmm0,           dword ptr ebp+8
cvtss2sd    xmm0,           xmm0
  sub       esp,            8
movsd       qword ptr esp,  xmm0
 push       dword ptr ebp-4
 call       printstr
  add       esp,            12

  mov   esp,    ebp
  pop   ebp
  ret

printstr is printf. Here is the full generated code: https://pastebin.com/g0Wff0JY


Solution

  • Looking at the image, I see what could be a potential issue, but I don't know fasm syntax:

            call    [printstr]     ;syntax used for the first call
            ...
            call    printstr       ;syntax used for the second call that fails
    

    If printstr is a memory based pointer to the function, then the second call syntax may be trying to make a call to where the pointer is stored, rather than calling the actual function by using the value in memory as a pointer to function.

    In the case of recent versions of Visual Studio, the default printf and scanf are effectively inlined into C/C++ code, with fairly complex syntax. Rather than deal with this, there are callable legacy versions available that require this includelib statement:

            includelib      legacy_stdio_definitions.lib    ;for scanf, printf, ...
    

    I converted the code from the question to masm syntax, change printstr to printf and tested a 32 bit build using Visual Studio 2015 on Windows 7 Pro 64 bit (the build is 32 bit so was run in 32 bit mode). I didn't have any issues with this code. I stepped through the code using debugger and didn't see any issues with how stuff was stored on the stack. I suspect the issue is with the second call to printstr without the brackets, which I corrected as part of the conversion to masm syntax.

            .data
    varf    real4   123.75
    lpcstr4 db      "%f",00ah,0             ;added new line
            .code
            extern  printf:near             ;instead of printstr
    
    printfloat32 proc
            push    ebp
            mov     ebp,esp
            sub     esp,16
            mov     dword ptr [ebp-4], offset lpcstr4
            movss   xmm0,dword ptr [ebp+8]
            cvtss2sd xmm0,xmm0
            sub     esp,8
            movsd   qword ptr [esp],xmm0
            push    dword ptr [ebp-4]
            call    printf                  ;was printstr
            add     esp,12
            mov     esp,ebp
            pop     ebp
            ret
    printfloat32 endp
    
    main    proc
            push    varf            ;test printfloat32 function
            call    printfloat32
            add     esp,4
            xor     eax,eax
            ret
    main    endp
            end
    

    Using printstr as a pointer to printf. Masm doesn't need brackets, since it knows printstr is a dd (pointer to printf).

            .code
            extern  printf:near
    printstr dd     printf          ;masm doesn't need brackets
    
    printfloat32 proc
    ;       ...
            call    printstr        ;masm doesn't need brackets
    ;       ...
    printfloat32 endp
    

    If printstr was external to this source file, the masm syntax would be

            extrn   printstr:ptr    ; or extern   printstr:dword