Programovanie v assembleri vo Windows x64 (x86-64): Rozdiel medzi revíziami
Smazaný obsah Přidaný obsah
2. Kapitola |
Revízia textu |
||
Riadok 1:
== Úvod ==
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/Objektov%C3%BD_k%C3%B3d Objektové súbory] sú zlinkované do spustiteľného exe súboru pomocou [https://cs.wikipedia.org/wiki/Linker linkeru] golink, alebo ld. Kompilátor as (GNU Assembler) a linker ld je súčasťou [[w:GNU_Compiler_Collection|gcc]].▼
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. Jedným z rozdielov týchto dvoch syntaxí je opačné poradie argumentov v niektorých inštrukciách.<ref>Ram Narayan (2007-10-17). [https://www.ibm.com/developerworks/library/l-gas-nasm/index.html "Linux assemblers: A comparison of GAS and NASM"]
</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 (NASM) pripomína priraďovací príkaz vyšších programovacích jazykov <code>AX=0</code>, AT&T syntax (GAS) skôr niečo ako <code>0->AX</code>.
▲[https://cs.wikipedia.org/wiki/Objektov%C3%BD_k%C3%B3d Objektové súbory] sú zlinkované do spustiteľného exe súboru pomocou [https://cs.wikipedia.org/wiki/Linker linkeru] golink, alebo ld. Kompilátor as (GNU Assembler) a linker ld
=== Volacie konvencie ===
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 (MS-DOS API). Tieto sa volali pomocou softvérového prerušenia inštrukciou INT, napríklad <code>INT 21h</code>. 64-bitové verzie OS Windows režim Virtual 8086 nepodporujú. Služby operačného systému je preto možné zabezpečiť jedine volaním funkcií Windows API (WinAPI).
Toto sú niektoré z najčastejšie používaných [[wikipedia:X86_calling_conventions|volacích konvencií (calling convention)]]
* [[wikipedia:X86_calling_conventions#cdecl|cdecl]] - C declaration, pochádza z jazyka C, parametre sú ukladané na vrchol zásobníka postupne sprava doľ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
Řádek 36 ⟶ 34:
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:
; rax = GetStdHandle(-11)
Řádek 61 ⟶ 66:
xor ecx, ecx ; UINT je 32 bit aj v 64 bitovom prostredi
call ExitProcess
</syntaxhighlight>Program síce nealokuje miesto v zásobníku, ako to vyžaduje volacia konvencia Microsoft x64 (podrobnosti ďalej), napriek tomu sa
▲message: db "Hello, World!",0xd,0xa
▲MESSAGE_LEN: equ $-message
▲ section .bss use64 ; neinicializovana datova oblast
▲lpNumberOfBytesWritten: resd 1
▲</syntaxhighlight>Program síce nealokuje miesto v zásobníku, ako to vyžaduje volacia konvencia Microsoft x64 (podrobnosti ďalej), napriek tomu sa dá zostaviť aj spustiť.
Kompilácia:<syntaxhighlight>
Řádek 80 ⟶ 77:
</syntaxhighlight>Ak kompilácia a linkovanie prebehli úspešne, môžme vyskúšať náš prvý 64-bitový program:<syntaxhighlight>
G:\>dir
15.07.2017 13:27 2
15.07.2017 13:27 1 536 HelloWorld.exe
15.07.2017 13:27 548 HelloWorld.obj
Řádek 94 ⟶ 91:
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.▼
Inštrukcie<syntaxhighlight lang="nasm" line="1" start="23">▼
Na adrese mesage je uložený text pozdravu. Pristupuje sa k nej síce ako k premennej, nachádza sa však v sekcii programu, preto je readonly.▼
Konštanta MESSAGE_LEN obsahuje počet bajtov (dĺžku textu), ktorý chceme vypísať. Konštanta, na rozdiel od premennej, nie je uložená na nejakej konkrétnej adrese, ale v čase kompilácie bude každý odkaz na ňu, napríklad <code>mov r8d, dword MESSAGE_LEN</code> nahradený jej skutočnou hodnotou <code>mov r8d, 15</code> (podobne ako <code>#define MESSAGE_LEN 15</code> v jazyku C).▼
mov ecx, -11
call GetStdHandle
Řádek 103 ⟶ 106:
Funkcia vráti v registry RAX (v súlade s volacou konvenciou) handle zariadenia STDOUT.
Inštrukcie<syntaxhighlight lang="nasm" line="1" start="
mov rcx, rax ; 1. param _In_ HANDLE hFile
mov rdx, qword message ; 2. param _In_ LPCVOID lpBuffer
Řádek 114 ⟶ 117:
Ďalšia inštrukcia <code>add rsp, 8</code> uvoľní miesto v zásobníku obsadené inštrukciou <code>push qword 0</code>.
Nakoniec<syntaxhighlight lang="nasm" line="1" start="
xor ecx, ecx
call ExitProcess
</syntaxhighlight>vynuluje obsah registra ECX a ukončí program. Jediným argumentom funkcie ExitProcess (uložený v registry ECX) je exit code programu.
Verzia pre GNU assembler:<syntaxhighlight lang="gas" line="1">
▲Na adrese mesage je uložený text pozdravu. Pristupuje sa k nej síce ako k premennej, nachádza sa však v sekcii programu, preto je readonly.
▲Konštanta MESSAGE_LEN obsahuje počet bajtov (dĺžku textu), ktorý chceme vypísať. Konštanta, na rozdiel od premennej, nie je uložená na nejakej konkrétnej adrese, ale v čase kompilácie bude každý odkaz na ňu, napríklad <code>mov r8d, dword MESSAGE_LEN</code> nahradený jej skutočnou hodnotou <code>mov r8d, 15</code> (ako <code>#define MESSAGE_LEN 15</code> v jazyku C).
▲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.
# HelloWorld.s
Řádek 136 ⟶ 133:
.global main
.section .bss▼
lpNumberOfBytesWritten: .space 4▼
.section .text
message: .ascii "Hello, World!\n"▼
MESSAGE_LEN = . - message▼
main:
/* rax = GetStdHandle(-11) */
Řádek 160 ⟶ 163:
xor %ecx, %ecx # 1. param _In_ UINT uExitCode UINT je 32 bit aj v 64 bitovom prostredi
call ExitProcess
▲message: .ascii "Hello, World!\n"
▲MESSAGE_LEN = . - message
▲ .section .bss
▲lpNumberOfBytesWritten: .space 4
</syntaxhighlight>Kompilácia:<syntaxhighlight>
Řádek 175 ⟶ 172:
</syntaxhighlight>Výsledok:<syntaxhighlight>
G:\>dir
15.07.2017 15:32 2
16.07.2017 12:48
16.07.2017 12:48 1 536 HelloWorld.exe
Řádek 184 ⟶ 181:
== Volacia konvencia Microsoft x64 ==
=== Zásobník ===
Zásobník (stack) je pamäťová štruktúra typu LIFO
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.
|