Programovanie v assembleri vo Windows x64 (x86-64): Rozdiel medzi revíziami
Smazaný obsah Přidaný obsah
Bez shrnutí editace |
formulacie, HelloWorld v.2 |
||
Riadok 1:
Toto sú poznámky k niekoľkým jednoduchým programom napísaných v jazyku [[w:Jazyk_symbolických_inštrukcií|symbolických adries]] (assembly language) určených pre [https://cs.wikipedia.org/wiki/Po%C4%8D%C3%ADta%C4%8Dov%C3%A1_platforma platformu] '''[[w:X86-64|x86-64]]''' – '''Windows x64'''
== Stručný prehľad ==
=== Syntax a kompilácia ===
Väčšinou sú tu uvedené dve verzie programov - prvá pre [[w:Prekladač|prekladač]] (kompilátor) [[w:Netwide_Assembler|Netwide Assembler]] (NASM), druhá pre [https://cs.wikipedia.org/wiki/GNU_Assembler GNU Assembler] (GAS). Kým NASM používa syntax Intelu, dominujúcu v prostredí MS-DOS a Windows, GNU Asembler používa syntax AT&T, prevládajúcu v Unixovom svete.
</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
Skompilovaním zdrojového súboru príslušným kompilátorom vznikne [https://cs.wikipedia.org/wiki/Objektov%C3%BD_k%C3%B3d
=== Volanie WinAPI ===
Riadok 68:
call ExitProcess
</syntaxhighlight>Program síce nealokuje miesto v zásobníku
Kompilácia:<syntaxhighlight>
Riadok 88:
</syntaxhighlight>
Obsah objektového aj spustiteľného súboru sa dá zobraziť programom objdump, napríklad:<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.▼
G:\>objdump -fhD HelloWorld.obj
▲</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>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.)
Řádek 191 ⟶ 193:
SUB RSP, 8
MOV [RSP], RAX
</syntaxhighlight>Keďže architektúra x86/x86-64 používa na ukladanie viac-bajtových hodnôt usporiadanie [[w:Endianita|little-endian]], t.j. na nižšej adrese je uložený menej významný/nižší bajt, v zásobníku bude register RAX uložený:<syntaxhighlight>
| |
+==============+
Řádek 221 ⟶ 223:
</syntaxhighlight>Ku hodnotám 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:<syntaxhighlight lang="nasm">
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.
SUB RSP, n
</syntaxhighlight>Nepotrebné miesto na vrchole zásobníka uvoľní:<syntaxhighlight lang="nasm">
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.
</syntaxhighlight>▼
=== Volacia konvencia Microsoft x64 ===
Volanie funkcie (podprogramu) sa realizuje inštrukciou CALL. Inštrukcia CALL najskôr
64-bitové verzie funkcií WindowsAPI používajú volaciu konvenciu Microsoft x64. Prvé štyri argumenty sú uložené v registroch (64-bitová architektúra x86-64 má oproti 32-bitovej architektúre x86 k dispozícii viac registrov). V prípade celočíselných hodnôt, vrátane ukazovateľov, v RCX, RDX, R8 a R9 (v tomto poradí), v prípade argumentov s pohyblivou desatinnou čiarkou v XMM0, XMM1, XMM2, XMM3. Prvý argument je teda uložený buď v registry RCX alebo v 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 a preto ich netreba nulovať. Napríklad prvý parameter typu integer (aj v x86-64 je to 32 bitové celé číslo) stačí uložiť do ECX. Ď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).
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)
Funkcia vracia celočíselný výsledok v registri RAX, desatinný v XMM0.<syntaxhighlight>
Řádek 259 ⟶ 261:
| volatile registre | /
+---------------------+
▲</syntaxhighlight>
=== Hello, World! v.2 ===
<syntaxhighlight lang="nasm" line="1">
; HelloWorld.asm
; kompilacia:
; nasm -f win64 HelloWorld.asm
; linkovanie:
; golink /console /ni /entry main HelloWorld.obj kernel32.dll
; alternativne linkovanie:
; ld -e main -s HelloWorld.obj -o HelloWorld.exe c:\windows\system32\kernel32.dll
global main
extern GetStdHandle
extern WriteFile
extern ExitProcess
section .bss use64 ; neinicializovana datova oblast
lpNumberOfBytesWritten: resd 1
section .text use64 ; Program code
message: db "Hello, World!",0xd,0xa
MESSAGE_LEN: equ $-message
main:
sub rsp, 28h ; rezervovanie miesta v zasobniku pre 5-ty argument, shadow space a zarovnanie
; rax = GetStdHandle(-11)
; HANDLE hStdHandle = WINAPI GetStdHandle (_In_ DWORD nStdHandle)
; nStdHandle: STD_INPUT_HANDLE=-10 , STD_OUTPUT_HANDLE=-11 , STD_ERROR_HANDLE=-12
mov ecx, -11 ; 1. param _In_ DWORD nStdHandle
call GetStdHandle
; rax = WriteFile(%rax, $message, $MESSAGE_LEN, %rsp-4, 0)
; BOOL bErrorFlag = WINAPI WriteFile (_In_ HANDLE hFile, _In_ LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Out_opt_ LPDWORD lpNumberOfBytesWritten, _Inout_opt_ LPOVERLAPPED lpOverlapped)
; WriteConsole(handle, &msg[0], 13, &written, 0)
mov rcx, rax ; 1. param _In_ HANDLE hFile
mov rdx, qword message ; 2. param _In_ LPCVOID lpBuffer
mov r8d, dword MESSAGE_LEN ; 3. param _In_ DWORD nNumberOfBytesToWrite
mov r9, lpNumberOfBytesWritten ; 4. param _Out_opt_ LPDWORD lpNumberOfBytesWritten
mov qword [rsp+20h], 0 ; 5. param _Inout_opt_ LPOVERLAPPED lpOverlapped
call WriteFile
; ExitProcess(0)
; VOID WINAPI ExitProcess( _In_ UINT uExitCode)
xor ecx, ecx ; UINT je 32 bit aj v 64 bitovom prostredi
call ExitProcess
add rsp, 28h ; uvolnenie rezervovaneho miesta
</syntaxhighlight>
Řádek 268 ⟶ 325:
* http://frdsa.fri.uniza.sk/~janosik/Kniha/ProgJSA.html
* https://www.pcrevue.sk/a/ASSEMBLER-pod-Windows--Uvod--1--cast
* https://www.zive.cz/clanky/vyznejte-se-v-procesoru--velky-prehled-technologii/historie-procesoru-cache-a-skalarni-procesory/sc-3-a-147124-ch-66129/default.aspx#articleStart
* https://forum.root.cz/index.php?topic=2388.0
* https://cs.wikipedia.org/wiki/Pipelining
* https://en.wikipedia.org/wiki/Branch_predictor
== Pozri aj ==
|