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

Smazaný obsah Přidaný obsah
d robot kozmetické zmeny
Fabcde (diskusia | príspevky)
d Revízia 19808 používateľa JAnDbot (diskusia) bola vrátená
Značka: Vrátenie
Riadok 8:
Skompilovaním zdrojového súboru príslušným kompilátorom (nasm resp. as) vznikne [[w:cs:Objektový kód|objektový súbor]], ktorý je následne pomocou [[w:cs:Linker|linkeru]] golink, resp. ld zlinkovaný do výsledného spustiteľného súboru. Kompilátor as (GNU Assembler) a linker ld sú súčasťou [[w:GNU_Compiler_Collection|gcc]]. Kvôli jednoduchšiemu rozlíšeniu majú tu uvedené [[w:Zdrojový_kód|zdrojové súbory]] programov určených pre NASM príponu .asm, objektové .obj, a pre GNU Assembler príponu .s, resp .o.
 
=== Volanie WinAPI ===
Programy bežiace v [[w:cs:Reálný režim|Reálnom móde]] (operačný systém MS-DOS) prípadne aj v móde (režime) [[w:cs:Virtual 8086 mode|Virtual 8086]] (operačný systém Windows) mohli využívať služby operačného systému MS-DOS prostredníctvom [[wikipedia:MS-DOS_API|MS-DOS API]]. Tieto sa volali pomocou [[w:cs:Přerušení#Softwarové přerušení|softvérového prerušenia]] inštrukciou INT, napríklad <code>INT 21h</code><ref>[http://www.cs.cmu.edu/~ralf/files.html Ralf Brown's Interrupt List]</ref>. Nakoľko 64-bitové verzie OS Windows režim Virtual 8086 nepodporujú, služby operačného systému je už možné zabezpečiť výlučne volaním funkcií [[w:cs:Windows API|Windows API]] (WinAPI).
 
Riadok 15:
Toto sú niektoré z najčastejšie používaných [[wikipedia:X86_calling_conventions|volacích konvencií v prostredí MS Windows]]:
* [[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 registri EAX (integer) alebo ST0 (float), zásobník čistí volajúca funkcia
 
* [[wikipedia:X86_calling_conventions#pascal|pascal]] - parametre sú ukladané na vrchol zásobníka zľava doprava, 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 vrchol zásobníka sprava doľava (ako cdecl), ale zásobník čistí volaná funkcia (ako pascal)
Řádek 21 ⟶ 20:
 
=== Hello, World! ===
Náš prvý program vypíše v príkazovom riadku krátky text a skončí.
 
'''Výpis 1a''' HelloWorld.asm (Verzia pre NASM):<syntaxhighlight lang="nasm" line="1">
Řádek 27 ⟶ 26:
 
; kompilacia:
; nasm -f win64 HelloWorld.asm
; linkovanie:
; golink /console /ni /entry main HelloWorld.obj kernel32.dll
; alternativne linkovanie:
; ld -e main -s -o HelloWorld.exe HelloWorld.obj c:\windows\system32\kernel32.dll
 
 
Řádek 42 ⟶ 41:
 
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:
Řádek 84 ⟶ 83:
</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 193 HelloWorld.asm
15.07.2017 13:27 1 536 HelloWorld.exe
15.07.2017 13:27 551 HelloWorld.obj
 
G:\>HelloWorld.exe
Řádek 96 ⟶ 95:
</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.)
 
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.), o čom sa môžeme presvedčiť ďalším užitočným programom 7-Zip:<syntaxhighlight>
Direktíva <code>section .bss use64</code> definuje oblasť neinicializovaných dát. V nej je vymedzený priestor 4 bajty pre uloženie počtu úspešne vypísaných bajtov textu pozdravu. Program túto hodnotu ďalej nepoužíva.
G:\>"C:\Program Files\7-Zip\7z.exe" l c:\windows\system32\kernel32.dll
</syntaxhighlight>Direktíva <code>section .bss use64</code> definuje oblasť neinicializovaných dát. V nej je vymedzený priestor 4 bajty pre uloženie počtu úspešne vypísaných bajtov textu pozdravu. Program túto hodnotu ďalej nepoužíva.
 
Direktíva <code>section .text use64</code> uvádza nasledujúci segment ako programový.
Riadok 135:
 
# kompilacia:
# as -o HelloWorld.o HelloWorld.s
# linkovanie:
# ld -e main -s -o HelloWorld.exe HelloWorld.o c:\windows\system32\kernel32.dll
# alternativna kompilacia+linkovanie:
# gcc -m64 -nostartfiles -Wl,-emain -o HelloWorld.exe HelloWorld.s c:\windows\system32\kernel32.dll
 
.global main
 
.section .bss
lpNumberOfBytesWritten: .space 4
 
.section .text
message: .ascii "Hello, World!\r\n"
MESSAGE_LEN = . - message
 
Riadok 174:
 
</syntaxhighlight>
V GAS je každý neznámy symbol defaultne považovaný za externý, preto nie je potrebné názvy API funkcií deklarovať direktívou extern. Nakoľko GAS assembler vo Windows nesprávne nahrádza escape sekvenciu pre nový riadok '\n' Unixovým LF (0x0a) namiesto správnej kombinácii CR+LF (0x0d,0x0a), bolo nutné hodnotu premennej message upraviť na "Hello, World!\r\n" (prípadne pomocou osmičkovej sústavy "Hello, World!\15\12").
 
Kompilácia:
Riadok 185:
</syntaxhighlight>Výsledok:<syntaxhighlight>
G:\>dir
15.07.2017 15:32 2 200 HelloWorld.s
16.07.2017 12:48 584 HelloWorld.o
16.07.2017 12:48 1 536 HelloWorld.exe
 
G:\>HelloWorld.exe
Riadok 204:
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 preto register RAX uložený:<syntaxhighlight>
| |
| |
+==============+
| 0. bajt | <- RSP = Vrchol zásobníka po (nižšia adresa)
+--------------+
| 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 (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]
Riadok 258:
 
''Obsah zásobníka po zavolaní WinAPI funkcie :''<syntaxhighlight>
+---------------------+ (nižšia adresa)
| zarovnanie, | \
| lokálne premenné a | > volaná funkcia
| volatile registre | /
+=====================+
| návratová adresa | CALL \
| z podprogramu (RIP) | |
+---------------------+ |
| 32. bajtov (RCX) | \ |
| shadow space (RDX) | | |
| (R8) | | |
| (R9) | | |
+---------------------+ | |
| 5. argument | | > volajúca funkcia
+---------------------+ | |
| 6. argument | > zodpovednosť volajúceho |
+---------------------+ | za alokovanie a uvoľnenie |
| . | | |
| . | | |
+---------------------+ | |
| posledný argument | | |
+---------------------+ | |
| zarovnanie, | | |
| lokálne premenné a | | |
| volatile registre | / /
+=====================+ (vyššia adresa)
</syntaxhighlight>
 
=== Hello, World! v.2 ===
Program HelloWorld opravený v súlade s volacou konvenciou.
 
'''Výpis 2a''' HelloWorld.asm (Verzia pre NASM):<syntaxhighlight lang="nasm" line="1">
Riadok 292:
 
; kompilacia:
; nasm -f win64 HelloWorld.asm
; linkovanie:
; golink /console /ni /entry main HelloWorld.obj kernel32.dll
; alternativne linkovanie:
; ld -e main -s -o HelloWorld.exe HelloWorld.obj c:\windows\system32\kernel32.dll
 
 
Riadok 307:
 
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:
Riadok 358:
 
# kompilacia:
# as -o HelloWorld.o HelloWorld.s
# linkovanie:
# ld -e main -s -o HelloWorld.exe HelloWorld.o c:\windows\system32\kernel32.dll
# alternativna kompilacia+linkovanie:
# gcc -m64 -nostartfiles -Wl,-emain -o HelloWorld.exe HelloWorld.s c:\windows\system32\kernel32.dll
 
.global main
 
.section .bss
lpNumberOfBytesWritten: .space 4
 
.section .text
message: .ascii "Hello, World!\15\12"
MESSAGE_LEN = . - message
 
Riadok 420:
 
; kompilacia:
; nasm -f win64 HelloWorld.asm
; linkovanie:
; ld -e main -s -o HelloWorld.exe HelloWorld.obj C:\opt\mingw64\x86_64-w64-mingw32\lib\libmsvcrt.a
; alternativne linkovanie:
; gcc -nostartfiles -Wl,-emain,-s -o HelloWorld.exe HelloWorld.obj C:\opt\mingw64\x86_64-w64-mingw32\lib\libmsvcrt.a
 
 
global main
 
extern printf
 
 
section .text use64 ; Program code
message: db "Hello, World!",0xd,0xa,0
 
main:
Riadok 462:
 
; kompilacia:
; nasm -f win64 IntToStr.asm
; linkovanie:
; golink /console /ni /entry main IntToStr.obj kernel32.dll
; alternativne linkovanie:
; ld -e main -s -o IntToStr.exe IntToStr.obj c:\windows\system32\kernel32.dll
 
 
Riadok 477:
 
section .data use64 ; Program code
buffer: times 20 db " " ; Najvacsie 64-bitove cislo bez znamienka ma 20 cifier (2**64 - 1 = 18446744073709551615)
enter: db 0xd,0xa
lpNumberOfBytesWritten: dd 0
BUFFER_LEN: equ enter-buffer
NEWLINE_LEN: equ lpNumberOfBytesWritten-enter
 
 
Riadok 534:
 
# kompilacia:
# as -o IntToHex.o IntToHex.s
# linkovanie:
# ld -e main -s -o IntToHex.exe IntToHex.o c:\windows\system32\kernel32.dll
# alternativna kompilacia+linkovanie:
# gcc -m64 -nostartfiles -Wl,-emain -o IntToHex.exe IntToHex.s c:\windows\system32\kernel32.dll
 
.global main
 
.section .data
buffer: .ascii " " /* Najvacsie 64-bitove cislo bez znamienka ma v setnastkovej sustave 16 cifier (2**64 - 1 = ffffffffffffffff) */
enter: .ascii "h\r\n"
lpNumberOfBytesWritten: .long 0
BUFFER_LEN = enter - buffer
NEWLINE_LEN = lpNumberOfBytesWritten - enter
 
 
Riadok 614:
<references />
 
== Ďalšie zdroje ==
* http://cs.lmu.edu/~ray/notes/x86assembly/
* http://frdsa.fri.uniza.sk/~janosik/Kniha/ProgJSA.html