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++?
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)
{
}
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);
}
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;
}
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:
cl
.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.