windowsvisual-c++x86masmcl

Failed to compile the exact assembly listing generated by the compiler


My environment is x86 Native Tools for VS 2022. The situation might be different in other platforms like x86-64 and ARM.

I am trying to compile the assembly listings generated by the compiler with command-lines like cl foo.c /Fa /FAs. The option /Fa means requiring a listing file, and /FAs means requiring corresponding C source lines in the listing file.

After I got the listing file, I tried to compile the listing file with command-line like ml foo.asm. I added corresponding link options like /link /subsystem:windows and /link User32.lib.

I have succeeded in some files, but failed when trying to do this to a minimal Windows API message loop.

That C file can be compiled and runs good. However, when I tried to compile its listing file, the assembler, ml, failed with an error:

win.asm(45) : error A2008:syntax error : flat

When I checked line 45, it is:

           voltbl   SEGMENT
           _volmd   DD  0ffffffffH
(LINE 45)           DDSymXIndex:    FLAT:_WinMain@16
                    DD  0dH
                    DD  0ecH
           voltbl   ENDS

I guess the FLAT in DDSymXIndex: FLAT:_WinMain@16 caused the problem.

However, this listing file is generated by cl, the compiler itself. Therefore, there should not be problems. Are there some command-line options I missed?

There is a related question. However, that question seems to be about the library linking, which is not my case. Link below.

Compile Assembly Output generated by VC++?

Attachment 1: A file whose listing files is successfully compiled.

This file is very simple. It only contains an empty main function, doing nothing.

#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
}

Attachment 2: Another file whose listing files is successfully compiled.

This file pops up a message-box. Its assembly listing file also gets successfully compiled.

I used the following command-lines to generate the listing file and compile:

> cl /Fa /FAs msgbox.c
> del msgbox.exe   (TO MAKE SURE A NEW EXECUTABLE IS CREATED)
> ml msgbox.asm /link User32.lib
               (^^^^^^^^^^^^^^^^ THIS PART IS IMPORTANT)
> msgbox
(IT RUNS AND WORKS)

Code:

#include <windows.h>
#pragma comment(lib, "User32.lib")

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
        MessageBox(NULL, "TEST", "TITLE",
                        MB_ICONEXCLAMATION | MB_OK);
}

Attachment 3: The C source of the minimal Windows API message loop file whose listing file failed to compile

This is a valid C file, can be compiled by the C compiler and the generated executable runs. However, the assembly listing file generated by this C file failed to be compiled. See above for details for compilation error.

#include <windows.h>
#pragma comment(lib, "User32.lib")
#pragma comment(lib, "Shell32.lib")
#pragma comment(lib, "gdi32.lib")

LRESULT CALLBACK wndproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
        switch (msg) {
        case WM_CLOSE:
                DestroyWindow(hwnd);
                break;
        case WM_DESTROY:
                PostQuitMessage(0);
                break;
        default:
                return DefWindowProc(hwnd, msg, wParam, lParam);
        }
        return 0;
}

BOOL register_window(const char * szClassName, HINSTANCE hInstance) {
        WNDCLASSEX wc;
        wc.cbSize = sizeof(WNDCLASSEX);
        wc.style = 0;
        wc.lpfnWndProc = wndproc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInstance;
        wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
        wc.lpszMenuName = NULL;
        wc.lpszClassName = szClassName;
        wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
        if (RegisterClassEx( & wc))
                return TRUE;
        MessageBox(NULL, "Window Registration Failed!", "Error!",
                MB_ICONEXCLAMATION | MB_OK);
        return FALSE;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
        HWND hwnd;
        MSG Msg;
        const char szClassName[] = "myWindowClass";

        if (!register_window(szClassName, hInstance))
                return 0;

        hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, szClassName, "MY WINDOW",
                WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
                640, 480, NULL, NULL,
                hInstance, NULL);

        if (!hwnd) {
                MessageBox(NULL, "Window Creation Failed!", "Error!",
                        MB_ICONEXCLAMATION | MB_OK);
                return 0;
        }

        ShowWindow(hwnd, nCmdShow);
        UpdateWindow(hwnd);

        while (GetMessage( & Msg, NULL, 0, 0) > 0) {
                TranslateMessage( & Msg);
                DispatchMessage( & Msg);
        }
        return Msg.wParam;
}

Attachment 4: The full listing file that failed to compile

The assembler failed to generate an object file, before reaching the linking stage. Therefore, currently it is unnecessary to check possible problems in the linking stage.

; Listing generated by Microsoft (R) Optimizing Compiler Version 19.38.33130.0 

    TITLE   C:\...\win.obj
    .686P
    .XMM
    include listing.inc
    .model  flat

INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES

PUBLIC  _WinMain@16
PUBLIC  _wndproc@16
PUBLIC  _register_window
EXTRN   __imp__CreateSolidBrush@4:PROC
EXTRN   __imp__GetMessageA@16:PROC
EXTRN   __imp__TranslateMessage@4:PROC
EXTRN   __imp__DispatchMessageA@4:PROC
EXTRN   __imp__DefWindowProcA@16:PROC
EXTRN   __imp__PostQuitMessage@4:PROC
EXTRN   __imp__RegisterClassExA@4:PROC
EXTRN   __imp__CreateWindowExA@48:PROC
EXTRN   __imp__DestroyWindow@4:PROC
EXTRN   __imp__ShowWindow@8:PROC
EXTRN   __imp__UpdateWindow@4:PROC
EXTRN   __imp__MessageBoxA@16:PROC
EXTRN   __imp__LoadCursorA@8:PROC
EXTRN   __imp__LoadIconA@8:PROC
EXTRN   @__security_check_cookie@4:PROC
EXTRN   ___security_cookie:DWORD
_DATA   SEGMENT
$SG72129 DB 'Error!', 00H
    ORG $+1
$SG72130 DB 'Window Registration Failed!', 00H
$SG72148 DB 'myWindowClass', 00H
    ORG $+2
$SG72150 DB 'MY WINDOW', 00H
    ORG $+2
$SG72152 DB 'Error!', 00H
    ORG $+1
$SG72153 DB 'Window Creation Failed!', 00H
_DATA   ENDS
voltbl  SEGMENT
_volmd  DD  0ffffffffH
    DDSymXIndex:    FLAT:_WinMain@16
    DD  0dH
    DD  0ecH
voltbl  ENDS

; Function compile flags: /Odtp
; File C:\...\win.c
_TEXT   SEGMENT
_wc$ = -48                      ; size = 48
_szClassName$ = 8                   ; size = 4
_hInstance$ = 12                    ; size = 4
_register_window PROC

; 20   : BOOL register_window(const char * szClassName, HINSTANCE hInstance) {

    push    ebp
    mov ebp, esp
    sub esp, 48                 ; 00000030H

; 21   :         WNDCLASSEX wc;
; 22   :         wc.cbSize = sizeof(WNDCLASSEX);

    mov DWORD PTR _wc$[ebp], 48         ; 00000030H

; 23   :         wc.style = 0;

    mov DWORD PTR _wc$[ebp+4], 0

; 24   :         wc.lpfnWndProc = wndproc;

    mov DWORD PTR _wc$[ebp+8], OFFSET _wndproc@16

; 25   :         wc.cbClsExtra = 0;

    mov DWORD PTR _wc$[ebp+12], 0

; 26   :         wc.cbWndExtra = 0;

    mov DWORD PTR _wc$[ebp+16], 0

; 27   :         wc.hInstance = hInstance;

    mov eax, DWORD PTR _hInstance$[ebp]
    mov DWORD PTR _wc$[ebp+20], eax

; 28   :         wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

    push    32512                   ; 00007f00H
    push    0
    call    DWORD PTR __imp__LoadIconA@8
    mov DWORD PTR _wc$[ebp+24], eax

; 29   :         wc.hCursor = LoadCursor(NULL, IDC_ARROW);

    push    32512                   ; 00007f00H
    push    0
    call    DWORD PTR __imp__LoadCursorA@8
    mov DWORD PTR _wc$[ebp+28], eax

; 30   :         wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));

    push    0
    call    DWORD PTR __imp__CreateSolidBrush@4
    mov DWORD PTR _wc$[ebp+32], eax

; 31   :         wc.lpszMenuName = NULL;

    mov DWORD PTR _wc$[ebp+36], 0

; 32   :         wc.lpszClassName = szClassName;

    mov ecx, DWORD PTR _szClassName$[ebp]
    mov DWORD PTR _wc$[ebp+40], ecx

; 33   :         wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    push    32512                   ; 00007f00H
    push    0
    call    DWORD PTR __imp__LoadIconA@8
    mov DWORD PTR _wc$[ebp+44], eax

; 34   :         if (RegisterClassEx( & wc))

    lea edx, DWORD PTR _wc$[ebp]
    push    edx
    call    DWORD PTR __imp__RegisterClassExA@4
    movzx   eax, ax
    test    eax, eax
    je  SHORT $LN2@register_w

; 35   :                 return TRUE;

    mov eax, 1
    jmp SHORT $LN1@register_w
$LN2@register_w:

; 36   :         MessageBox(NULL, "Window Registration Failed!", "Error!",

    push    48                  ; 00000030H
    push    OFFSET $SG72129
    push    OFFSET $SG72130
    push    0
    call    DWORD PTR __imp__MessageBoxA@16

; 37   :                 MB_ICONEXCLAMATION | MB_OK);
; 38   :         return FALSE;

    xor eax, eax
$LN1@register_w:

; 39   : }

    mov esp, ebp
    pop ebp
    ret 0
_register_window ENDP
_TEXT   ENDS
; Function compile flags: /Odtp
; File C:\...\win.c
_TEXT   SEGMENT
tv64 = -4                       ; size = 4
_hwnd$ = 8                      ; size = 4
_msg$ = 12                      ; size = 4
_wParam$ = 16                       ; size = 4
_lParam$ = 20                       ; size = 4
_wndproc@16 PROC

; 6    : LRESULT CALLBACK wndproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {

    push    ebp
    mov ebp, esp
    push    ecx

; 7    :         switch (msg) {

    mov eax, DWORD PTR _msg$[ebp]
    mov DWORD PTR tv64[ebp], eax
    cmp DWORD PTR tv64[ebp], 2
    je  SHORT $LN5@wndproc
    cmp DWORD PTR tv64[ebp], 16         ; 00000010H
    je  SHORT $LN4@wndproc
    jmp SHORT $LN6@wndproc
$LN4@wndproc:

; 8    :         case WM_CLOSE:
; 9    :                 DestroyWindow(hwnd);

    mov ecx, DWORD PTR _hwnd$[ebp]
    push    ecx
    call    DWORD PTR __imp__DestroyWindow@4

; 10   :                 break;

    jmp SHORT $LN2@wndproc
$LN5@wndproc:

; 11   :         case WM_DESTROY:
; 12   :                 PostQuitMessage(0);

    push    0
    call    DWORD PTR __imp__PostQuitMessage@4

; 13   :                 break;

    jmp SHORT $LN2@wndproc
$LN6@wndproc:

; 14   :         default:
; 15   :                 return DefWindowProc(hwnd, msg, wParam, lParam);

    mov edx, DWORD PTR _lParam$[ebp]
    push    edx
    mov eax, DWORD PTR _wParam$[ebp]
    push    eax
    mov ecx, DWORD PTR _msg$[ebp]
    push    ecx
    mov edx, DWORD PTR _hwnd$[ebp]
    push    edx
    call    DWORD PTR __imp__DefWindowProcA@16
    jmp SHORT $LN1@wndproc
$LN2@wndproc:

; 16   :         }
; 17   :         return 0;

    xor eax, eax
$LN1@wndproc:

; 18   : }

    mov esp, ebp
    pop ebp
    ret 16                  ; 00000010H
_wndproc@16 ENDP
_TEXT   ENDS
; Function compile flags: /Odtp
; File C:\...\win.c
_TEXT   SEGMENT
_Msg$ = -52                     ; size = 28
_hwnd$ = -24                        ; size = 4
_szClassName$ = -20                 ; size = 14
__$ArrayPad$ = -4                   ; size = 4
_hInstance$ = 8                     ; size = 4
_hPrevInstance$ = 12                    ; size = 4
_lpCmdLine$ = 16                    ; size = 4
_nCmdShow$ = 20                     ; size = 4
_WinMain@16 PROC

; 43   : {

    push    ebp
    mov ebp, esp
    sub esp, 52                 ; 00000034H
    mov eax, DWORD PTR ___security_cookie
    xor eax, ebp
    mov DWORD PTR __$ArrayPad$[ebp], eax

; 44   :         HWND hwnd;
; 45   :         MSG Msg;
; 46   :         const char szClassName[] = "myWindowClass";

    mov eax, DWORD PTR $SG72148
    mov DWORD PTR _szClassName$[ebp], eax
    mov ecx, DWORD PTR $SG72148+4
    mov DWORD PTR _szClassName$[ebp+4], ecx
    mov edx, DWORD PTR $SG72148+8
    mov DWORD PTR _szClassName$[ebp+8], edx
    mov ax, WORD PTR $SG72148+12
    mov WORD PTR _szClassName$[ebp+12], ax

; 47   : 
; 48   :         if (!register_window(szClassName, hInstance))

    mov ecx, DWORD PTR _hInstance$[ebp]
    push    ecx
    lea edx, DWORD PTR _szClassName$[ebp]
    push    edx
    call    _register_window
    add esp, 8
    test    eax, eax
    jne SHORT $LN4@WinMain

; 49   :                 return 0;

    xor eax, eax
    jmp $LN1@WinMain
$LN4@WinMain:

; 50   : 
; 51   :         hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, szClassName, "MY WINDOW",

    push    0
    mov eax, DWORD PTR _hInstance$[ebp]
    push    eax
    push    0
    push    0
    push    480                 ; 000001e0H
    push    640                 ; 00000280H
    push    -2147483648             ; 80000000H
    push    -2147483648             ; 80000000H
    push    13565952                ; 00cf0000H
    push    OFFSET $SG72150
    lea ecx, DWORD PTR _szClassName$[ebp]
    push    ecx
    push    512                 ; 00000200H
    call    DWORD PTR __imp__CreateWindowExA@48
    mov DWORD PTR _hwnd$[ebp], eax

; 52   :                 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
; 53   :                 640, 480, NULL, NULL,
; 54   :                 hInstance, NULL);
; 55   : 
; 56   :         if (!hwnd) {

    cmp DWORD PTR _hwnd$[ebp], 0
    jne SHORT $LN5@WinMain

; 57   :                 MessageBox(NULL, "Window Creation Failed!", "Error!",

    push    48                  ; 00000030H
    push    OFFSET $SG72152
    push    OFFSET $SG72153
    push    0
    call    DWORD PTR __imp__MessageBoxA@16

; 58   :                         MB_ICONEXCLAMATION | MB_OK);
; 59   :                 return 0;

    xor eax, eax
    jmp SHORT $LN1@WinMain
$LN5@WinMain:

; 60   :         }
; 61   : 
; 62   :         ShowWindow(hwnd, nCmdShow);

    mov edx, DWORD PTR _nCmdShow$[ebp]
    push    edx
    mov eax, DWORD PTR _hwnd$[ebp]
    push    eax
    call    DWORD PTR __imp__ShowWindow@8

; 63   :         UpdateWindow(hwnd);

    mov ecx, DWORD PTR _hwnd$[ebp]
    push    ecx
    call    DWORD PTR __imp__UpdateWindow@4
$LN2@WinMain:

; 64   : 
; 65   :         while (GetMessage( & Msg, NULL, 0, 0) > 0) {

    push    0
    push    0
    push    0
    lea edx, DWORD PTR _Msg$[ebp]
    push    edx
    call    DWORD PTR __imp__GetMessageA@16
    test    eax, eax
    jle SHORT $LN3@WinMain

; 66   :                 TranslateMessage( & Msg);

    lea eax, DWORD PTR _Msg$[ebp]
    push    eax
    call    DWORD PTR __imp__TranslateMessage@4

; 67   :                 DispatchMessage( & Msg);

    lea ecx, DWORD PTR _Msg$[ebp]
    push    ecx
    call    DWORD PTR __imp__DispatchMessageA@4

; 68   :         }

    jmp SHORT $LN2@WinMain
$LN3@WinMain:

; 69   :         return Msg.wParam;

    mov eax, DWORD PTR _Msg$[ebp+8]
$LN1@WinMain:

; 70   : }

    mov ecx, DWORD PTR __$ArrayPad$[ebp]
    xor ecx, ebp
    call    @__security_check_cookie@4
    mov esp, ebp
    pop ebp
    ret 16                  ; 00000010H
_WinMain@16 ENDP
_TEXT   ENDS
END

To wrap up:

  1. I am trying to compile the assembly listing generated by cl.
  2. I succeeded in two tiny test files.
  3. Although I use the same method, I failed for another test file.

Solution

  • It's these lines that caused the problem:

               voltbl   SEGMENT
               _volmd   DD  0ffffffffH
    (LINE 45)           DDSymXIndex:    FLAT:_WinMain@16
                        DD  0dH
                        DD  0ecH
               voltbl   ENDS
    

    By removing these lines, the assembly listing just compiles and works:

    >ml win.asm /link User32.lib Shell32.lib GDI32.lib
    Microsoft (R) Macro Assembler Version 14.38.33130.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
     Assembling: win.asm
    Microsoft (R) Incremental Linker Version 14.38.33130.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /OUT:win.exe
    win.obj
    User32.lib
    Shell32.lib
    GDI32.lib
    

    DDSym might be something like 'Debugging Symbol'. Therefore I tried to remove these lines, and it just works.

    But I still don't understand why the C compiler generates these lines that cannot be compiled by the assembler. Maybe I have missed some command-line options to tell the assembler how to handle these debugging symbols. Anyway I will not look into it at present.