Programowanie w asemblerze Środowiska 64-bitowe 17 października 2017
Nieco historii najnowszej Intel wraz z HP rozpoczynaja pracę nad procesorem 64-bitowym z wykorzystaniem technologii VLIW. Powstaje procesor Itanium o architekturze oznaczanej IA-64. AMD rozwija własny procesor 64-bitowy, będacy minimalnym (przynajmniej zewnętrznie) rozszerzeniem wersji 32-bitowej. Powstaje procesor Opteron (Athlon 64) o architekturze oznaczanej x86-64. Jakkolwiek Itanium jest bardziej ciekawe technologicznie, to sprzedaje się słabo (być może dlatego, że jest za drogie ;-) i występuje głównie w serwerach zastapiło tam HP. Intel klonuje architekturę AMD (od Pentium 4 Xeon): różne dziwne nazwy, np. EM64T, IA-32e.
Architektura x86-64 5 trybów pracy, z czego 3 to stare tryby 32-bitowe. Compatibility Mode do uruchamiania programów skompilowanych 32-bitowo w środowisku 64-bitowego systemu operacyjnego 64-bit Mode: pełny 64-bitowy. Application Binary Interface (ABI) dla Linuxa definiowany przez amd64.org.
Rejestry w x86-64 Ogólnego przeznaczenia 64-bitowe: RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15 32-bitowe: EAX ESP oraz R8D, R9D, R10D, R11D, R12D, R13D, R14D, R15D 16-bitowe: AX SP oraz R8W, R9W, R10W, R11W, R12W, R13W, R14W, R15W 8-bitowe: AL DL, AH DH, SPL, BPL, SIL, DIL, R8B, R9B, R10B, R11B, R12B, R13B, R14B, R15B 16 128-bitowych rejestrów XMM (używane w SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, SSE5 i Advanced Vector Extensions) W trybie 64-bitowym 16 rejestrów 128-bitowych XMM0 XMM15
Operacje w x86-64 Wskaźniki (w rejestrach) zawsze 64-bitowe, ale tylko 48 bitów używane do adresów wirtualnych (co daje 256 TB), fizyczne adresy maks. 52-bitowe. Nowy tryb adresowy: względny do licznika rozkazów (RIP relative), używany już dla skoków itp. w IA-32. 32-bitowe przesunięcie (ze znakiem). Ułatwia generowanie Position Independent Code (PIC). Operacje 8- i 16-bitowe nie modyfikuja starszej części rejestru. Operacje 32-bitowe zeruja starsza część rejestru (pewnie żeby mieć wskaźniki 32- i 64-bitowe w trybach mieszanych), na przykład mov rax,100 oraz mov eax,100 to to samo. Prefiks REX żeby obsługiwać argumenty 64-bitowo.
Segmentacja w x86-64 CS jest używany tylko dla określenia poziomu ochrony kodu, adres bazowy zawsze 0, brak kontroli rozmiaru (limit). DS, ES, SS: ich zawartość jest ignorowana, wszystkie trzy utożsamiane z CS FS, GS używane tylko dla określenia adresu bazowego segmentu (potrzebne dla MS Windows)
Konwencje użycia rejestrów (ABI) Działa zarówno SYSCALL jaki i INT 0x80, ale inaczej. Dla instrukcji SYSCALL numer usługi w EAX, kolejne parametry w RDI, RSI, RDX, R10, R8, R9. Numery usług podane w /usr/src/linux/include/asm-x86_64/unistd.h. Wynik w RAX i RDX.
Konwencje użycia rejestrów (ABI) Wywołania funkcji (CALL): Argumenty w RDI, RSI, RDX, RCX, R8, R9 (liczby całkowite i wskaźniki). Można używać rejestrów czastkowych, jeśli argument o mniejszym rozmiarze. Argumenty zmiennopozycyjne w XMM0, XMM1, XMM2,..., XMM7. Na stosie (gdy więcej niż 6 pierwszego rodzaju lub 8 drugiego) zawsze pełny rozmiar (64 bity), ale można korzystać z dolnego kawałka (bo little-endian). Wartość funkcji zwracana w RAX (liczby całkowite i wskaźniki) albo w XMM0 (liczby zmiennopozycyjne).
Konwencje użycia rejestrów (ABI) Wywołania funkcji (CALL): W treści funkcji RSP zwykle ustalone, nie korzysta się z RBP do ramek. W GCC wyrównywanie stosu do 128 bitów przy wywołaniu funkcji ułatwia wrzucanie rejestrów FPU i SSE na stos. Można to robić samodzielnie w asemblerze and rsp, 15 Należy zachować RBX, RBP, ESP, R12, R13, R14, R15. Powyżej bieżacego wierzchołka stosu czerwona strefa (red zone) 128 bajtów dostępnych dla programów.
Problemy W x86-64 w trybie 64-bitowym segmenty nie działaja, jedynie stronicowanie. Ale stronicowanie nie rozróżnia poziomów 0 2 (Po co? Sa segmenty...). System operacyjny maszyn wirtualnych musi więc być na poziomie 3, a wtedy nie jest chroniony przed aplikacjami, albo na poziomie 0.
Intel/HP IA-64 (Itanium) 128 rejestrów integer (64-bitowe) 128 rejestrów floating-point (82-bitowe: 17/64) f0=0.0 f1=1.0 64 rejestry predykatów (1-bitowe), p0=1 8 branch registers (64-bitowe)??? 128 application registers???
Intel/HP IA-64 (Itanium) Część rejestrów każdego rodzaju rotating (np. r32 r127). Rodzaj stosu. Instruction Bundle: 3 41-bitowe sloty na instrukcje + 5 bitów na template.
Instrukcja CPUID Instrukcja CPUID zwraca informacje o procesorze. Podobne informacje sa dostępne w Linuksie przez cat /proc/cpuinfo Aby sprawdzić, czy procesor reaguje na CPUID, należy spróbować, czy da się zmodyfikować bit 21 w słowie stanu procesora (EFLAGS) pushfd ;wyjmujemy flagi pop eax mov ecx,eax ;zachowanie flag xor eax,200000h ;zmiana bitu 21 push eax ;z powrotem popfd pushfd ;jeszcze raz pobieramy pop eax cmp eax,ecx je niema
Instrukcja CPUID Niektóre procesory nie reaguja na flagę ID, ale posadaja instrukcję CPUID. Mozna to sprawdzać przejmujac chwilowo obsługę przerwania Invalid Opcode exception (int 6) i wykonujac instrukcję CPUID. Jeśli pojawi się wyjatek, to nie ma instrukcji CPUID. Teraz juz możemy użyć CPUID do identyfikacji procesora. Instrukcja zakłada, że w rejestrze EAX znajduje się numer funkcji ( level ). Wynik zwracany jest w rejestrach EAX, ECX, EDX i EBX.
Instrukcja CPUID Jeśli w rejestrze EAX umieścimy zero dostaniemy w EAX maks. numer funkcji (co najmniej 0 ;-) w EBX ECX EDX skrócona nazwa procesora, np. Genuine Intel Intel "GenuineIntel"(ebx= Genu, bl= G (47h)) AMD AuthenticAMD" Cyrix ĆyrixInstead" Rise ŻiseRiseRise" Centaur ĆentaurHauls" NexGen ŃexGenDriven" UMC "UMC UMC UMC "
Instrukcja CPUID Więcej informacji dostarcza inne poziomy, na przykład dla poziomu 1 dostaniemy flagi własności procesora; poziom 2 zwraca deskryptory cache i TLB.
Instrukcja CPUID Przykładowy kod badajacy, czy procesor wspiera MMX: ;; First check maximum available level xor eax, eax ;level 0 cpuid cmp eax, 0 jng no_way ;; Now check MMX support mov eax, 1 ;level 1 cpuid test edx, 00800000h ;bit 23 is set if MMX is s jz mmx_not_supported