Použití prostředí Visual Studio pro psaní, kompilování a ladění programů v assembleru

MS Visual Studio je velice všestranný nástroj a jeho použití může sahat až do oblastí, pro které nejspíš nebylo určeno. Tento článek ukazuje možnosti Visual Studia pro vývoj programu v assembleru. Na příkladu jednoduchého prográmku "Hello, World !!" napsaného v assembleru si ukážeme, že komfort vyvíjení programu v tomto nízkoúrovňovém jazyce je skoro tak vysoký jako při vyvíjení programu psaného v C nebo v C++.

Začneme s hotovým zdrojovým programem

Úkolem tohoto článku není popisovat programování v assembleru pod Windows, proto zde uvedu pouze výpis programu "Hello, world !!". Pro informace o assembleru, fóra a hlavičkové soubory zkuste třeba MasmForum.

.586
.MODEL FLAT,STDCALL

OPTION CASEMAP:NONE
UNICODE=0

include inc/windows.inc
include inc/user32.inc

includelib lib/user32.lib

?WINDOWS=1

.data

szMessage       db "Hello, world !!!", 0
szTitle         db "Assembler",0

.code

WinMain PROC    hInstance    :HANDLE,
                hPrevInstance:HANDLE,
                lpszCmdParam :LPSTR,
                nCmdShow     :WORD

        INVOKE  MessageBox, NULL, offset szMessage, offset szTitle, MB_OK
        ret
WinMain ENDP

end

Program je velice primitivní, zobrazí pouze jeden message box. Syntaxe souboru odpovídá překladači Macro Assembler od Microsoftu (ml.exe).

Vytvoření projektu

Vytvoříme nový projekt jako aplikaci pro Win32, nazveme jej třeba "AsmHello". Na další záložce zvolíme "An empty project". Visual Studio nám oznámí, že v tomto případě za nás nic neudělá a vytvoří prázdný projekt.

Do složky, která byla vytvořena, nakopírujte soubor AsmHello.asm a adresáře inc a lib (Získáte je z příkladu ke stažení). Do projektu ve Visual C++, do složky "Source Files" vložte soubor AsmHello.asm.

Nejdříve to zkompilovat ...

Pokud se pokusíte přeložit takto napsaný projekt, nedostanete sice žádnou chybu, ale ani žádný EXE soubor. Pro soubory soubory s příponou ASM totiž není definovaná žádná akce. Aby Visual Studio takový soubor přeložilo, musíte nadefinovat tzv. Custom Build Step, tedy postup, jak má prostředí se souborem naložit. Otevřete nastavení projektu (Menu "Project\Settings" a v levé části dialogu vyberte soubor AsmHello.asm. Do pole "Description" můžete napsat v podstatě cokoliv, Visual Studio tento text vypisuje při kompilaci souboru. Do pole "Commands" vepište příkazovou řádku pro zkompilování assemblerovkého souboru:

Pro Debug Verzi:

ml.exe /c /nologo /coff /Zd /Zi /Fo Debug/AsmHello.obj /I inc AsmHello.asm

Pro Release Verzi:

ml.exe /c /nologo /coff /Fo Release/AsmHello.obj /I inc AsmHello.asm

K tomu, aby překlad fungoval, musíte mít soubor ml.exe (Kompilátor assembleru) v nějakém adresáři, ke kterému je nastavena proměnná PATH. Význam jednotlivých parametrů zjistíte, pokud na příkazovou řádku napíšete

ml /?

Do pole "Outputs" vepište "Debug\AsmHello.obj" (pro Debug Verzi) a "Release\AsmHello.obj" (pro Release verzi). Toto pole slouží k tomu, aby Visual Studio vědělo, který soubor bude výstupem po zkompilování. Pokud bude vstupní soubor (AsmHello.asm) novější než výstupní soubor (Debug\AsmHello.obj), bude proveden překlad.

Nastavení kompilace souboru ASM

... pak slinkovat ...

Přeložení proběhne v pořádku, ale linker nahlásí chybu

--------------------Configuration: AsmHello - Win32 Debug--------------------
Kompiluji .\AsmHello.asm
 Assembling: AsmHello.asm
Linking...
LINK : error LNK2001: unresolved external symbol _WinMainCRTStartup
Debug/AsmHello.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.

AsmHello.exe - 2 error(s), 0 warning(s)

Tato chyba vznikla proto, že linker očekává někde v programu funkci WinMainCRTStartup, která je implicitně použita jako vstupní bod programu. V programech napsaných v C/C++ je tato funkce implementována ve startovacích knihovnách C a má na starosti převod příkazové řádky na proměnné __argc a __argv, spouštění funkcí před WinMain-em aj. V našem případě tyto funkce nepotřebujeme. Musíme ale sdělit linkeru, že má použít funkci WinMain, implementovanou v AsmHello.asm, jako vstupní bod programu. Toho docílíme v nastavení linkeru ("Project\Settings", karta "Link", kategorie "Output", do pole "Entry-point symbol" vepište WinMain). Nezapomeňte stejnou změnu provést také v Release verzi. Dále ještě zrušte volbu "Link incrementally" v kategorii "Customize". To způsobí, že linker nebude generovat tabulku odkazů, kvůli které by se při ladění otevíralo okno "Assembly". Tato volba bývá implicitně nastavena v Debug verzi programu.

... a spustit

No a zbývá spustit debugger klávesou F10 a kurzor se objeví na začátku funkce WinMain:

Ladíme program v assembleru

Celý příklad v assembleru si můžete stáhnout (241 KB). K souboru jsou také přibaleny hlavičkové soubory a knihovna.

Není ten EXE soubor nějak moc velký ?

Programy psané v assembleru jsou pověstné svojí malou velikostí. Náš má ale v Release verzi 16384 bytů, což je celkem dost na to, že volá jenom jednu API funkci. Je to způsobeno tím, že EXE soubory obsahují sekce, které jsou zarovnány na 8192 bytů (a jsou naplněny většinou nulami - zkuste program zkomprimovat do ZIPu !). Pokud ale toto zarovnání snížíme, snížíme tím i velikost výsledného EXE souboru. Do nastavení linkeru ("Project\Settings", karta "Link", pole "Project Options" na konec všech parametrů připište:

/ALIGN:0x1000 /OPT:REF

a to jak do Debug verze, tak i do Release verze. Nyní bude mít výsledný EXE soubor po sestavení něco málo přes 2500 bytů. Při tomto nastavení zarovnání bude linker hlásit varování

LINK : warning LNK4108: /ALIGN specified without /DRIVER or /VXD; image may not run

Pod Windows řady 9x je minimální funkční zarovnání 4096 bytů (0x1000), pod Windows řady NT lze program spustit i se zarovnáním 16 bytů (0x10). Velikost výsledného EXE souboru je pak extrémně malá, jen něco přes 700 bytů. To je asi limit, který se nám podaří u EXE souboru dosáhnout (hlavička EXE souboru má 512 bytů). Volba /OPT:REF říká linkeru, že by měl z výsledného souboru vyhodit odkazy na knihovny DLL, které nejsou použity.

Na závěr jeden trik: Pokud si myslíte, že v C není možné vytvořit takto malý EXE soubor, mýlíte se. Pokud do projektu vložíte jediný soubor C nebo CPP, obsahující pouze WinMain, nastavíte vstupní bod programu v nastavení linkeru na WinMain a nastavíte i stejné zarovnání, bude velikost výsledného souboru v Release verzi srovnatelná s velikostí programu napsaného v assembleru.