Witam,
Jakiś czas temu stałem się posiadaczem systemu Windows 7 (64bit) i zapewne też tutaj są użytkownicy systemów 64-bitowych. Od razu zaciekawiła mnie rzecz, jak wygląda programowanie w Assemblerze pod tym systemem, zatem ruszyłem z pomocą do Google. W tym artykule chciałbym zebrać informacje, które mam nadzieję będą pomocne w rozpoczęciu programowania w systemach 64-bitowych. Materiałów w języku polskim nie znalazłem, to co wiem przeczytałem z anglojęzycznych for i dokumentacji. Tekst powstał na bazie mojego wcześniejszego artykułu: Wstęp do Assemblera dla architektury 64-bit


1. Wstęp

W systemach 64-bitowych nie jest możliwe już uruchamianie aplikacji dla systemu DOS. Można to zrobić jednak poprzez skorzystanie z emulatora DosBox, jednak jest to spore ograniczenie. Natomiast jeżeli chodzi o aplikacje dla systemu Win32 w większości przypadków powinieneś uruchomić je bezproblemowo z niewielką stratą na wydajności. Umożliwia to nakładka WOW64 (Windows on Windows 64). Loader uruchamiający pliki wykonywalne rozpoznaje czy jest on 32-bitowy. Jeżeli tak, uruchamiany jest on w ten sposób, że wywołania funkcji itp. są "tłumaczone" przez tą nakładkę i przekierowywane do bibliotek systemowych (64 bitowych).
Ilustracja:


Wraz z nową architekturą zwiększyła się przestrzeń adresowa, zmienił się format plików (nazywany PE32+), mamy nowe rejestry procesora, niektóre instrukcje nie są dostępne, ale doszły także nowe.


2. Zmiany w formacie plików wykonywalnych.

Pięć pól struktury pliku wykonywalnego zostało rozszerzonych, jedno zostało usunięte.
Przedstawia to poniższa tabela:



3. Rejestry procesora w architekturze x64

Dotychczas znane rejestry ogólnego przeznaczenia zostały rozszerzone do rozmiaru 64-bity i nazwane z prefixem REX, czyli EAX->RAX, EBX->RBX, ECX->RCX itd. Doszło także osiem nowych rejestrów ogólnego przeznaczenia nazwanych: R8, R9, R10, R11, R12, R13, R14, R15.
Dodane zostały też nowe rejestry 128-bitowe XMM (XMM8-XMM15).

Ilustracja uwzględniająca zmiany w rejestrach procesora:



4. Zmiany w instrukcjach

Instrukcje, które nie są dostępne (wystąpi wyjątek invalid-opcode):
AAA - ASCII Adjust After Addition
AAD - ASCII Adjust AX Before Division
AAM - ASCII Adjust AX After Multiply
AAS - ASCII Adjust AL After Substraction
BOUND - Check Array Bound
DAA - Decimal Adjust After Addition
DAS - Decimal Adjust After Substraction
INTO - Interrupt to Overflow Vector
LDS, LES - Load Far Pointer
POPA, POPAD - Pop All GPRs
POP DS/ES/SS - Pop instruction using DS, ES or SS register.
PUSH CS/DS/ES/SS - Push Onto Stack instruction using CS, DS, ES or SS register
PUSHA, PUSHAD - Push All GPRs onto Stack

Dodatkowo instrukcja SYSENTER/SYS.EXIT (w tej instrukcji nie ma być kropki w środku, ale musiałem tak napisać z powodu, że forum cenzuruje ten wyraz) jest dotępna tylko w tzw. Legacy Mode (inaczej wystąpi invalid-opcode exception).
W trybie Long Mode należy użyć instrukcji SYSCALL/SYSRET.

Instrukcje LODSB, LODSW, LODSD, CMPSB, CMPSQ, MOVSW, MOVSQ, SCASD, SCASQ, STOSQ zostały rozszerzone do obsługi adresów 64-bitowych oraz dodane zostały nowe instrukcje LODSQ, CMPSQ, MOVSQ, SCASQ, STOSQ.

Również instrukcje REP, REPZ, REPNZ, LOOP, LOOPZ, LOOPNZ korzystają teraz z rejestrów 64-bitowych.

Kompletna lista instrukcji:
General-Purpose and System Instructions


5. Konwencja wywołania

Zmieniła się również konwencja wywołania (calling convention). W systemie Win32 była konwencja STDCALL. Tutaj konwencja wywołania nazywa się FASTCALL i zamiast przekazywać parametry przez stos, pierwsze cztery przekazujemy przez specjalne rejestry (RCX, RDX, R8, R9), a resztę przez stos. Dodatkowo musimy zrobić także miejsce na stosie na parametry przekazywane przez rejestry (w praktyce wykonanie sub rsp, liczba).
Wywołanie funkcji MessageBox w Win32:
Kod:
push 0
push offset szCaption
push offset szText
push 0
call MessageBoxA
W systemie Win64 wywołanie tej samej funkcji wygląda to tak:
Kod:
sub rsp, 28h
mov r9d, 0
lea r8, szCaption
lea rdx, szText
mov rcx, 0
call MessageBoxA

6. Adresowanie

W Assemblerze x64 adresy do miejsc (danych i kodu), które znajdują się wewnątrz programu pozostają 32-bitowe, jest tak przez relatywne adresowanie (RIP-relative addressing). My podajemy adres pośredni, z którego adres bezpośredni zostaje utworzony dodając adres pośredni do aktualnego adresu. Jeżeli odwołujemy się do adresów zewnętrznych, np. funkcji z jakiejś biblioteki, adres wtedy jest 64-bitowy.


7. Kompilatory Assemblera x64

- MASM64
Jeżeli chodzi o kompilatory to możemy korzystać z MASM64 (ml64.exe) dołączonego do WINDDK (Windows Driver Development Kit) w katalogu:
\WINDDK\3790.1830\bin\win64\x86\amd64

Polecam jednak ściągnąć paczkę: kompilator wraz z includami i kilkoma przykładami:
MASM64 SDK

- jWasm
Możemy również skorzystać z tego kompilatora. Jest on kompatybilny z MASMem.
Oficjalna strona jWasm

- NASM
Assembler NASM od wersji 2.00 wspiera architekture 86-x64:
Strona assemblera NASM

- FASM
Z tego co możemy przeczytać na stronie, FASM również wspiera architekturę x64.
Strona assemblera FASM

- GoAsm
Strona GoAsm


8. Debuggery aplikacji 64-bitowych

- Debugging Tools for Windows 64-bit Version
- FDBG - AMD64 Assembly Debugger


9. Zakończenie

To już wszystko w tym artykule.

Na koniec kilka linków:
- CodeProject: Moving to Windows Vista x64
- AMD64 Architecture Tech Docs
- Microsoft Macro Assembler Reference - MASM for x64
- GoAsm: Writing 64-bit programs
- jWasm: x64 Hello World