Programovanie v assembleri vo Windows x64 (x86-64): Rozdiel medzi revíziami
Smazaný obsah Přidaný obsah
formulacie, HelloWorld v.2 |
d formulacie |
||
Riadok 3:
== Stručný prehľad ==
=== Syntax a kompilácia ===
</ref> Napríklad inštrukcia "vlož hodnotu nula do registra AX" sa v NASM zapisuje <code>MOV AX, 0</code>, v GAS <code>MOV $0, %AX</code>. (Intelovská syntax pripomína priraďovací príkaz vyšších programovacích jazykov <code>AX = 0</code>, syntax AT&T skôr niečo ako <code>0 -> AX</code>.)
Riadok 9:
=== Volanie WinAPI ===
Programy bežiace v [https://cs.wikipedia.org/wiki/Re%C3%A1ln%C3%BD_re%C5%BEim Reálnom móde] (operačný systém MS-DOS) alebo v móde (režime) [https://cs.wikipedia.org/wiki/Virtual_8086_mode Virtual 8086] (operačný systém Windows) mohli využívať služby operačného systému MS-DOS
Toto sú niektoré z najčastejšie používaných [[wikipedia:X86_calling_conventions|volacích konvencií (calling convention)]] v prostredí MS Windows:
Riadok 19:
=== Hello, World! ===
Náš prvý program vypíše v príkazovom riadku krátky text a skončí.
Verzia pre NASM:<syntaxhighlight lang="nasm" line="1"> ; HelloWorld.asm
Řádek 65 ⟶ 67:
; ExitProcess(0)
; VOID WINAPI ExitProcess( _In_ UINT uExitCode)
xor ecx, ecx ; 1. param _In_ UINT uExitCode UINT je 32 bit aj v 64 bitovom prostredi
call ExitProcess
</syntaxhighlight>Program síce nealokuje miesto v zásobníku (shadow space, podrobnosti ďalej) tak ako to vyžaduje volacia konvencia Microsoft x64
Kompilácia:<syntaxhighlight>
Řádek 94 ⟶ 96:
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 .bss use64</code> definuje oblasť neinicializovaných dát. V nej je
Direktíva <code>section .text use64</code> uvádza nasledujúci segment ako programový.
Řádek 121 ⟶ 123:
Nakoniec<syntaxhighlight lang="nasm" line="1" start="46">
xor ecx, ecx ; 1. param _In_ UINT uExitCode UINT je 32 bit aj v 64 bitovom prostredi
call ExitProcess
</syntaxhighlight>vynuluje obsah registra ECX a ukončí program. Jediným argumentom funkcie ExitProcess (uložený v registry ECX) je exit code programu.
Řádek 184 ⟶ 186:
=== Zásobník ===
[https://cs.wikipedia.org/wiki/Z%C3%A1sobn%C3%ADk_%28datov%C3%A1_struktura%29 Zásobník] (stack) je pamäťová štruktúra typu LIFO. Hardvérový zásobník je realizovaný priamo v operačnej pamäti počítača. V ďalšom texte
Zásobník je možné používať ľubovoľne podľa potreby. Bežne sa používa na ukladanie lokálnych premenných,
V architektúrach x86 a x86-64 sa ku zásobníku pristupuje pomocou inštrukcií PUSH a POP. Zásobník rastie (plní sa) smerom od vyššej adresy k nižšej. Na jeho vrchol ukazuje register RSP
Inštrukcia PUSH vloží novú hodnotu na vrchol zásobníka tak, že zmenší hodnotu registra RSP o počet vložených bajtov, a na túto adresu uloží novú hodnotu. Napríklad inštrukciu PUSH RAX si môžme predstaviť ako dvojicu inštrukcií<syntaxhighlight lang="nasm">
Řádek 196 ⟶ 198:
| |
+==============+
| 0. bajt | <- RSP (Vrchol zásobníka) po ... nižšia adresa
+--------------+
| 1. bajt | ^
Řádek 217 ⟶ 219:
+--------------+
| . |
+==============+ <- Dno zásobníka ... vyššia adresa
</syntaxhighlight>Z vrcholu zásobníka sa hodnoty vyberajú inštrukciou POP. Inštrukcia <code>POP RAX</code> z vrcholu zásobníka prečíta hodnotu, vloží ju do registra RAX, a následne uvoľní miesto v zásobníku, podobne ako:<syntaxhighlight lang="nasm">
MOV RAX, [RSP]
ADD RSP, 8
</syntaxhighlight>Ku hodnotám uloženým v zásobníku je
MOV EAX, [RSP+4]
</syntaxhighlight>Pamäť zásobníka mimo rozsahu vymedzenom registrom RSP je nestála (volatile) a môže ju prepísať OS alebo debuger. Pre bezpečné uloženie údajov do zásobníka je preto nutné vždy najskôr alokovať potrebný priestor:<syntaxhighlight lang="nasm">
Řádek 228 ⟶ 230:
ADD RSP, n
</syntaxhighlight>Poznámka: Rozhranie X86-64 ABI použité v System V umožňuje používať aj [[wikipedia:Red_zone_(computing)|Red zone]] - oblasť 128 bajtov tesne nad vrcholom zásobníka.
=== Podprogram ===
Volanie funkcie (podprogramu) sa v jazyku symbolických adries realizuje inštrukciou CALL. Inštrukcia
=== Volacia konvencia Microsoft x64 ===
64-bitové verzie funkcií WindowsAPI používajú volaciu konvenciu Microsoft x64.
▲Volanie funkcie (podprogramu) sa realizuje inštrukciou CALL. Inštrukcia CALL najskôr vloží na vrchol zásobníka hodnotu registra RIP, v ktorom sa už nachádza adresa nasledujúcej inštrukcie. Následne zmenou hodnoty registra RIP sa zrealizuje skok na požadovanú adresu. Podprogram končí inštrukciou RET, ktorá vyberie z vrcholu zásobníka návratovú adresu (pôvodnú hodnotu registra RIP) a vloží ju späť do registra RIP.
Ďalšie argumenty sa ukladajú do zásobníka v poradí sprava doľava, rovnako ako pri stdcall.
Volacia konvencia Microsoft x64 vyžaduje, aby v zásobníku bolo alokovaných ďalších 32 bajtov, kam volaná API funkcia niekedy ukladá obsah registrov RCX, RDX, R8, R9. Tento 32 bajtový priestor (shadow space) musí volajúci alokovať vždy, a to aj v prípade, že funkcia má menej ako štyri parametre. Za uvoľnenie tohto miesta rovnako ako aj miesta pre ďalšie argumenty zodpovedá volajúci (na rozdiel od konvencie stdcall).▼
Non-leaf funkcia, čiže funkcia, ktorá tiež volá nejakú funkciu, vyžaduje zarovnanie zásobníka na 16 bajtov.
▲Volacia konvencia Microsoft x64 ďalej vyžaduje, aby v zásobníku bolo alokovaných
Registre RAX, RCX, RDX, R8, R9, R10, R11 sú považované za nestále (volatile) 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 (nonvolatile). Za ich obnovenie do pôvodného stavu (v prípade ich zmeny) zodpovedá volaná funkcia.
Funkcia vracia celočíselný výsledok v registri RAX, desatinný v XMM0.
| lokálne premenné a |▼
''Obsah zásobníka po zavolaní WinAPI funkcie :''<syntaxhighlight>
+---------------------+ ... nižšia adresa \
| lokálne premenné a | > volaná funkcia
| návratová adresa | CALL▼
| volatile registre | /▼
+=====================+
+---------------------+ ▼
▲| návratová adresa | CALL \
| z podprogramu (RIP) | |
| shadow space (RDX) | |▼
| 32. bajtov (RCX) | \
▲| shadow space (RDX) | | |
+---------------------+ |▼
|
| (R9) | | |
+---------------------+ |
| 5. argument | | > volajúca funkcia
+---------------------+ |
| 6. argument | > zodpovednosť volajúceho |
▲+---------------------+ | za alokovanie a uvoľnenie |
| posledný argument | |▼
| . | | |
▲+---------------------+ | |
▲| posledný argument | | |
▲| volatile registre | /
+---------------------+ | |
▲| lokálne premenné a | | |
| volatile registre | / /
+=====================+ ... vyššia adresa
</syntaxhighlight>
=== Hello, World! v.2 ===
Program HelloWorld opravený v súlade s volacou konvenciou.
<syntaxhighlight lang="nasm" line="1">▼
▲Verzia pre NASM:<syntaxhighlight lang="nasm" line="1">
; HelloWorld.asm
Řádek 311 ⟶ 325:
; ExitProcess(0)
; VOID WINAPI ExitProcess( _In_ UINT uExitCode)
xor ecx, ecx ; 1. param _In_ UINT uExitCode UINT je 32 bit aj v 64 bitovom prostredi
call ExitProcess
add rsp, 28h ; uvolnenie rezervovaneho miesta
</syntaxhighlight>Rozdiel v porovnaní s prvou verziou spočíva v rezervovaní miesta v zásobníku tak, aby sa sem vošli argumenty (8 bajtov pre piaty argument funkcie WriteFile) a shadow space (32 bajtov). Vďaka inštrukcii CALL, ktorá ešte na vrchol zásobníka vloží obsah RIP (8 bajtov) bude zásobník zarovnaný na požadovaných 16 bajtov:<syntaxhighlight lang="nasm" line="1" start="27">
sub rsp, 28h ; rezervovanie miesta v zasobniku pre 5-ty argument, shadow space a zarovnanie
</syntaxhighlight>Piaty argument potom samozrejme nie je možné vložiť na požadovanú pozíciu inštrukciou PUSH, ale:<syntaxhighlight lang="nasm" line="1" start="42">
mov qword [rsp+20h], 0 ; 5. param _Inout_opt_ LPOVERLAPPED lpOverlapped
</syntaxhighlight>Zásobník je pripravený aj pre funkciu ExitProcess, stačí ju zavolať a až potom uvoľniť rezervované miesto:<syntaxhighlight lang="nasm" line="1" start="50">
add rsp, 28h ; uvolnenie rezervovaneho miesta
</syntaxhighlight>
== Referencie ==
<references />
|