Моя среда — это собственные инструменты x86 для VS 2022. В других странах ситуация может быть иной платформы, такие как x86-64 и ARM.
Я пытаюсь скомпилировать созданные списки сборок
компилятором с помощью командных строк, таких как
cl foo.c /Fa /FAs. Опция /Fa означает требование
файл листинга, а /FAs означает требование
соответствующие строки исходного кода C в файле листинга.
Получив файл листинга, я попытался скомпилировать
файл листинга с командной строкой типа
ml foo.asm. Я добавил соответствующие варианты ссылок
например /link /subsystem:windows и /link User32.lib.
Мне удалось создать некоторые файлы, но не удалось, когда пытаюсь сделать это с минимальным API Windows цикл сообщений.
Этот файл C можно скомпилировать и он работает хорошо.
Однако, когда я попытался скомпилировать его файл со списком,
ассемблер,
ml, не удалось с ошибкой:
win.asm(45) : error A2008:syntax error : flat
Когда я проверил строку 45, это:
voltbl SEGMENT
_volmd DD 0ffffffffH
(LINE 45) DDSymXIndex: FLAT:_WinMain@16
DD 0dH
DD 0ecH
voltbl ENDS
Я думаю, FLAT в DDSymXIndex: FLAT:_WinMain@16
вызвало проблему.
Однако этот файл списка создан cl,
сам компилятор. Следовательно, не следует
быть проблемы. Есть ли какие-нибудь параметры командной строки
Я пропустил?
Есть связанный с этим вопрос. Однако этот вопрос похоже, речь идет о связывании библиотеки, что не мой случай. Ссылка ниже.
Скомпилировать выходные данные сборки, сгенерированные VC++?
Этот файл очень простой. Он содержит только пустую основную функцию, ничего не делать.
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
}
Этот файл вызывает окно сообщения. Его файл листинга сборки также успешно компилируется.
Я использовал следующие командные строки чтобы сгенерировать файл листинга и скомпилировать:
> 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)
Код:
#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);
}
Это действительный файл C, его можно скомпилировать компилятором C и сгенерированным исполняемые файлы. Однако собрание файл листинга, созданный этим файлом C не удалось скомпилировать. См. выше Подробности об ошибке компиляции.
#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;
}
Ассемблеру не удалось сгенерировать объектный файл, прежде чем достичь этап связывания. Поэтому на данный момент это ненужно чтобы проверить возможные проблемы в этап связывания.
; 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
Обернуть:
cl.__imp__xxx это переменная размера указателя, которая содержит адрес импортируемой функции. и вам не нужно также call DWORD PTR __imp__xxx делать. просто call __imp__xxx@RbMm Спасибо, я тоже этого не знал. Это также было бы полезно, поскольку CALL используется очень часто.





Именно эти строки и вызвали проблему:
voltbl SEGMENT
_volmd DD 0ffffffffH
(LINE 45) DDSymXIndex: FLAT:_WinMain@16
DD 0dH
DD 0ecH
voltbl ENDS
Удалив эти строки, листинг сборки просто компилируется и работает:
>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 может быть что-то вроде «Символ отладки».
Поэтому я попытался удалить эти строки и
это просто работает.
Но я до сих пор не понимаю, почему компилятор C генерирует эти строки, которые не могут быть скомпилированы с помощью ассемблер. Возможно, я что-то пропустил параметры командной строки сообщить ассемблеру, как обрабатывать эту отладку символы. В любом случае я не буду сейчас этим заниматься.
Вывод сборки MSVC представляет собой «листинг», который, как я слышал, не предназначен для фактической сборки для создания пригодного для использования объектного файла. Иногда это происходит потому, что он включает ненужные функции, но я думаю, это может быть еще одним примером. GCC и clang -S создают исходный файл asm, который будет собран в тот же объектный файл, который они создали бы с помощью -c вместо -S.
@PeterCordes Спасибо, я этого не знал. Таким образом, я бы обратился к GCC и clang, чтобы получить исходный код сборки, который можно напрямую скомпилировать.
EXTRN __imp__xxx:DWORDдолжно быть, а неPROC(в x64QWORD)