Programovanie v assembleri vo Windows x64 (x86-64): Rozdiel medzi revíziami
Smazaný obsah Přidaný obsah
d robot kozmetické zmeny |
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.
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
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
|