Programovanie v assembleri vo Windows x64 (x86-64): Rozdiel medzi revíziami

Smazaný obsah Přidaný obsah
Fabcde (diskusia | príspevky)
dBez shrnutí editace
Fabcde (diskusia | príspevky)
2. Kapitola
Riadok 13:
 
Toto sú niektoré z najčastejšie používaných [[wikipedia:X86_calling_conventions|volacích konvencií (calling convention) vo svete Windows]]:
* [[wikipedia:X86_calling_conventions#cdecl|cdecl]] - C declaration, pochádza z jazyka C, parametre sú ukladané na zásobníkvrchol zásobníka postupne z prava dosprava ľavadoľava (kvôli podpore premenlivého počtu argumentov), výsledok je uložený buď v registry EAX (integer) alebo ST0 (float), zásobník čistí volajúca funkcia
 
* [[wikipedia:X86_calling_conventions#pascal|pascal]] - parametre sú ukladané na zásobníkvrchol zzásobníka ľavazľava do pravadoprava, zásobník čistí volaná funkcia (napríklad inštrukciou RET n)
* [[wikipedia:X86_calling_conventions#stdcall|stdcall]] - štandard pre Win32 API, parametre sú ukladané na zásobníkvrchol zzásobníka pravasprava do ľavadoľava (ako cdecl), ale zásobník čistí volaná funkcia (ako pascal)
* [[wikipedia:X86_calling_conventions#Microsoft_x64_calling_convention|Microsoft x64]] - volania WinAPI v 64-bitových programoch pre MS Windows, prvé štyri parametre sú uložené v RCX/XMM0, RDX/XMM1, R8/XMM2, R9/XMM3 (integer/float), zvyšné v zásobníku zsprava prava do ľavadoľava, výsledok je vrátený v registry RAX alebo XMM0, zásobník čistí volajúca funkcia
 
=== Hello, World! ===
Náš prvý program vypíše v príkazovom riadku krátky text a skončí. (verziaVerzia pre NASM):<syntaxhighlight lang="nasm" line="1">
; HelloWorld.asm
 
Riadok 88:
 
 
</syntaxhighlight>
</syntaxhighlight>Direktíva <code>global main</code> deklaruje návestie main ako globálne a linker ho môže použiť ako štartovaciu adresu programu.
 
Direktíva <code>global main</code> deklaruje návestie main ako globálne a linker ho môže použiť ako štartovaciu adresu programu.

Direktíva <code>extern GetStdHandle</code> deklaruje symbol GetStdHandle (WinAPI funkcia) ako externý, čiže nachádzajúci sa v niektorom z ďalších pripojených súborov, v tomto prípade v dynamicky linkovanej knižnici kernel32.dll. (Napriek tej 32 v názve, vo Windows x64 je to 64-bitová knižnica.)
 
Direktíva <code>section .text use64</code> uvádza nasledujúci segment ako programový, readonly.
Řádek 123 ⟶ 125:
A nakoniec, v oblasti neinicializovaných dát je vyhradený priestor 4 bajty pre uloženie počtu úspešne zapísaných bajtov. Program túto hodnotu ďalej nepoužíva.
 
NasledujeV ajnasledujúcej kapitole sa dozvieme viac o volacej konvencii Microsoft x64 a opravíme spomínanú chybu, ktorej sme sa dopustili v programe HelloWorld. Najskôr však ešte verzia pre GNU Assemblerassembler:<syntaxhighlight lang="gas" line="1">
# HelloWorld.s
 
Řádek 179 ⟶ 181:
G:\>HelloWorld.exe
Hello, World!
</syntaxhighlight>
 
== Volacia konvencia Microsoft x64 ==
Táto kapitola uvádza podrobnosti volacej konvencie Microsoft x64. Najskôr však niečo o zásobníku.
 
=== Zásobník ===
Zásobník (stack) je pamäťová štruktúra typu LIFO a vo všeobecnosti môže byť realizovaný rôznymi spôsobmi. Hardvérový zásobník je realizovaný priamo v operačnej pamäti, ako jedna súvislá oblasť, ku ktorej sa pristupuje pomocou inštrukcií procesora PUSH a POP. V ďalšom texte pod zásobníkom budeme rozumieť vždy hardvérový zásobník, nie nejakú jeho softvérovú implementáciu.
 
Zásobník používa inštrukcia CALL na uloženie návratovej adresy z podprogramu (funkcie). Je ideálny aj na ukladanie lokálnych premenných, často sa používa aj na odovzdávanie argumentov podprogramu.
 
Rovnako ako v architektúre x86, aj v x86-64 zásobník rastie smerom od vyššej adresy k nižšej. Na vrchol zásobníka, čiže poslednú (najnižšiu) adresu, na ktorej je niečo uložené, ukazuje register RSP. Novú hodnotu uložíme do zásobníka inštrukciou PUSH. Táto posunie vrchol zásobníka (zníži hodnotu uloženú v registri RSP) o príslušný počet bajtov a na takto vzniknuté miesto uloží novú hodnotu. Napríklad inštrukciu <code>PUSH RAX</code> si môžme predstaviť ako dvojicu inštrukcií<syntaxhighlight lang="nasm">
SUB RSP, 8
MOV [RSP], RAX
</syntaxhighlight>Keďže architektúra x86/x86-64 používa na ukladanie viac-bajtových hodnôt usporiadanie little-endian (na nižšej adrese je uložený menej významný/nižší bajt), v zásobníku bude uložené:<syntaxhighlight>
| |
+==============+
| AL (0. bajt) | <- RSP (Vrchol zásobníka) po
+--------------+
| AH (1. bajt) | ^
+--------------+ |
| (2. bajt) | |
+--------------+ |
| (3. bajt) | |
+--------------+ |
| (4. bajt) | | RSP - 8
+--------------+ |
| (5. bajt) | |
+--------------+ |
| (6. bajt) | |
+--------------+ |
| (7. bajt) | |
+==============+
| . | <- RSP (Vrchol zásobníka) pred
+--------------+
| . |
| . |
+==============+
<- Dno zásobníka
</syntaxhighlight>K dátam uloženým v zásobníku je možné stále pristupovať aj priamo, ako ku hocijakým iným dátam uloženým kdekoľvek v pamäti, napríklad pomocou relatívneho odkazu na vrchol zásobníka <code>MOV EAX, [RSP+4]</code>.
 
Z vrcholu zásobníka sa hodnoty vyberajú inštrukciou POP. Inštrukcia <code>POP RAX</code> z vrchola zásobníka prečíta hodnotu, vloží ju do registra RAX, a následne uvoľní miesto, podobne ako<syntaxhighlight lang="nasm">
MOV RAX, [RSP]
ADD RSP, 8
</syntaxhighlight>Pamäť zásobníka mimo rozsahu vymedzenom registrom RSP je nestála (volatile) a môže ju prepísať OS alebo debuger. Pri ukladaní údajov do zásobníka je preto nutné vždy najskôr alokovať potrebný priestor (napríklad SUB RSP, n) a až do takto alokovanej pamäte možno potom niečo bezpečne uložiť.
 
=== Microsoft x64 ===
Volacia konvencia Microsoft x64 sa používa pri volaní funkcií WindowsAPI v 64-bitovom režime.
 
Volanie podprogramu sa realizuje štandardne inštrukciou CALL. Inštrukcia CALL najskôr uloží na vrchol zásobníka hodnotu registra RIP, čiže adresu nasledujúcej inštrukcie a vzápätí zrealizuje skok na adresu podprogramu, zmenou hodnoty registra RIP. Podprogram sa končí inštrukciou RET, ktorá vyberie z vrcholu zásobníka návratovú adresu (uschovaný obsah registra RIP) a vloží ju späť do RIP.
 
Na rozdiel od volacej konvencie stdcall, používanej v 32-bitovom režime, sú prvé štyri argumenty uložené v registroch. (Architektúra x64 má oproti x32 k dispozícii viac registrov.) V prípade celočíselných argumentov (vrátane ukazovateľov) v registroch RCX, RDX, R8 a R9 (v tomto poradí), v prípade argumentov s pohyblivou desatinnou čiarkou v registroch XMM0, XMM1, XMM2, XMM3. Prvý argument je teda uložený buď v registry RCX alebo v registry XMM0, druhý v RDX alebo v XMM1, tretí v R8 alebo v XMM2, štvrtý v R9 alebo v XMM3. Parametre menšie než 64 bitov ignorujú vyššie bity, netreba ich nulovať. Napríklad prvý parameter typu integer (aj v x64 je to 32 bitové celé číslo) stačí uložiť do ECX. Prípadné ďalšie argumenty sa potom ukladajú do zásobníka, a to smerom sprava doľava.
 
Volacia konvencia Microsoft x64 vyžaduje, aby v zásobníku bolo alokovaných ďalších 32 bajtov. Volaná funkcia sem potom, v prípade potreby, môže uložiť hodnoty argumentov RCX, RDX, R8, R9. Tento 32 bajtový dodatočný priestor (shadow space) musí volajúci alokovať vždy, a to aj v prípade, že funkcia má menej ako štyri parametre.
 
Registre RAX, RCX, RDX, R8, R9, R10, R11 sú považované za nestále a volaná funkcia ich môže kedykoľvek trvalo zmeniť. Volajúci ich samozrejme môže po návrate z funkcie obnoviť, ak si predtým uschoval ich hodnoty. Naopak, registre RBX, RBP, RDI, RSI, RSP, R12, R13, R14 a R15 sú považované za stále, za ich obnovenie do pôvodného stavu (v prípade ich zmeny) je zodpovedná volaná funkcia.
 
Volaná funkcia vracia celočíselný výsledok v registri RAX, desatinný v XMM0.<syntaxhighlight>
| lokálne premenné |
| a |
| volatile registre |
+-------------------+
| návratová adresa | CALL
| z podprogramu (RIP) |
+-------------------+
| (RCX) | \
| rezervované (RDX) | |
| 32. bajtov (R8) | |
| (R9) | |
+-------------------+ |
| 5. argument | > zodpovednosť volajúceho za alokovanie a uvoľnenie
+-------------------+ |
| 6. argument | |
+-------------------+ |
| . | |
+-------------------+ |
| posledný argument | /
+-------------------+
| lokálne premenné |
| a |
| volatile registre |
+-------------------+
</syntaxhighlight>