X86-64 podle manuálů Intel

Zdroj: SOOM.cz [ISSN 1804-7270]
Autor: mazegen
Datum: 26.9.2016
Hodnocení/Hlasovalo: 3.7/37

Překvapuje mě, jak málo programátorů v assembleru při práci používá asi nejkvalitnější zdroj infomací - oficiální procesorové manuály, ať už od Intelu nebo AMD.

Proto vzniknula tato příručka, která by vás měla krok za krokem provést spletitostí manuálů od Intelu při poznávání architektury x86-64.

Většina těchto programátorů používá různéneoficiální reference a informace. Důvod bude asi ten, že vyznat se na webech intel.com nebo amd.com není zrovna nejjednodušší a přímé adresy pro stažení nejsou kupodivu moc rozšířené. Navíc, oficiální manuály jsou rozsáhlé a těžko se v nich ze začátku člověk orientuje. Tyto manuály zkrátka nejsou moc populární i přes to, že neoficiální zdroje většinou obsahují hodně neúplných a nepřesných informací.

Tato příručka předpokládá znalost programování v 32bitovém asembleru v chráněném módu. Je průvodcem po Intelovských manuálech s několika poznámkami navíc. Víc informací a kde je stáhnout jsou zde.

Článek je psán z pohledu aplikačního programátora v asembleru, proto se příliš nezabývá systémovými otázkami. Kódování instrukcí je stejnětak zmíněno jenom okrajově.

Poznámka. V době psaní používám poslední revize manuálů, která je pro Intel 022. Pokud máte Intel manuály starší než řekněme revizi 020, tak by se informace mohly hodně lišit. Proto doporučuji používat nejnovější. (Číslo revize je uvedeno na titulní stránce každého z manuálů jako poslední trojčíslí Order Number.)

x86-64, x64

Co se vlastně míní, když se řekne x86-64? Jde o rozšíření původní architektury x86-32, která přišla na svět s procesorem 80386. Intel tuto architekturu začal v poslední době nazývat Intel 64 Architecture (dřív se stále držel označení IA-32 Intel Architecture s 64bitovými rozšířeními), AMD tuto architekturu stabilně nazývá AMD64. Pro označení architektury nezávisle na výrobci se vžila zkratka x86-64 nebo kratší x64.

Architektura Intel 64

Stručné shrnutí základních vlastností této architektury najdeme v kapitole 2.2.7 Intel® 64 Architecture v manuálu Basic Architecture:

Intel 64 architecture increases the linear address space for software to 64 bits and supports physical address space up to 40 bits. The technology also introduces a new operating mode referred to as IA-32e mode.

IA-32e mode operates in one of two sub-modes: (1) compatibility mode enables a 64-bit operating system to run most legacy 32-bit software unmodified, (2) 64-bit mode enables a 64-bit operating system to run applications written to access 64-bit address space.

In the 64-bit mode, applications may access:

An Intel 64 architecture processor supports existing IA-32 software because it is able to run all non-64-bit legacy modes supported by IA-32 architecture. Most existing IA-32 applications also run in compatibility mode.

Mód IA-32e

Mód, v němž běží architektura Intel 64, se nazývá IA-32e mode a je popsán v kapitole 3.1.1 Intel® 64 Architecture v manuálu Basic Architecture. To, co nás zajímá, je 64bitový mód:

Intel 64 architecture adds IA-32e mode. IA-32e mode has two sub-modes. These are:

Úmyslně jsem vynechal popis Compatibility Mode, protože jde o režim, který je prakticky totožný s 32bitovým chráněným módem a proto nás nyní nezajímá.

Poznámka. V souvislosti s architekturou x64 je často je používán termín Long Mode. Tento termín používá AMD a nejde o nic jiného než o mód IA-32e. Tento termín je také často používán nesprávně, kdy se tímto termínem myslí 64bitový mód, což je termín společný jak pro Intel, tak pro AMD.

64bitový mód

Kapitola 3.2.1 64-Bit Mode Execution Environment v manuálu Basic Architecture shrnuje rozdíly v 64bitovém módu:

The execution environment for 64-bit mode is similar to that described in Section 3.2. The following paragraphs describe the differences that apply.

Pro programátora v assembleru je dost zajímavá změna šířky zásobníku, která je v 64bitovém módu fixovaná na 64 bitů.

Poznámka. V 32bitovém chráněném módu je implicitní velikost ukazatele zásobníku řízena příznakem B (big) v deskriptoru segmentu zásobníku, nezávisle na nastavení kódového segmentu. To může být například použito pro vynucení 16bitového zásobníku v 32bitovém módu.

Paměťový model

Paměťový model je popsán v kapitole 3.3.4 Modes of Operation vs. Memory Model v manuálu Basic Architecture (výtah):

Segmentation is generally (but not completely) disabled, creating a flat 64-bit linear-address space. Specifically, the processor treats the segment base of CS, DS, ES, and SS as zero in 64-bit mode (this makes a linear address equal an effective address). Segmented and real address modes are not available in 64-bit mode.

Segmentové registry

S novým paměťovým modelem souvisí odlišná interpretace segmentových registrů, která je detailně popsána v kaiptole 3.4.2.1 Segment Registers in 64-Bit Mode v manuálu Basic Architecture:

In 64-bit mode: CS, DS, ES, SS are treated as if each segment base is 0, regardless of the value of the associated segment descriptor base. This creates a flat address space for code, data, and stack. FS and GS are exceptions. Both segment registers may be used as additional base registers in linear address calculations (in the addressing of local data and certain operating system data structures).

Even though segmentation is generally disabled, segment register loads may cause the processor to perform segment access assists. During these activities, enabled processors will still perform most of the legacy checks on loaded values (even if the checks are not applicable in 64-bit mode). Such checks are needed because a segment register loaded in 64-bit mode may be used by an application running in compatibility mode.

Limit checks for CS, DS, ES, SS, FS, and GS are disabled in 64-bit mode.

Velikost operandu a adresy

Další důležitou změnou jsou pravidla pro implicitní velikost operandu a adresy, které jsou mnohem jednodušší než 32bitovém chráněném módu.

Poznámka. Implicitní velikost operandu a adresy v 32bitovém chráněném módu je dána příznakem D (default size) v deskriptoru kódového segmentu, takže teoreticky může současně existovat několik různých typů segmentů s různými velikostmi operandů a adres.

Tyto pravidla popisuje kapitola 3.6.1 Operand Size and Address Size in 64-Bit Mode v manuálu Basic Architecture (výtah):

In 64-bit mode, the default address size is 64 bits and the default operand size is 32 bits. Defaults can be overridden using prefixes. Address-size and operand-size prefixes allow mixing of 32/64-bit data and 32/64-bit addresses on an instruction-by-instruction basis. Table 3-4 shows valid combinations of the 66H instruction prefix and the REX.W prefix that may be used to specify operand-size overrides in 64-bit mode. Note that 16-bit addresses are not supported in 64-bit mode.

REX prefixes consist of 4-bit fields that form 16 different values. The W-bit field in the REX prefixes is referred to as REX.W. If the REX.W field is properly set, the prefix specifies an operand size override to 64 bits. Note that software can still use the operand-size 66H prefix to toggle to a 16-bit operand size. However, setting REX.W takes precedence over the operand-size prefix (66H) when both are used.

In the case of SSE/SSE2/SSE3/SSSE3 SIMD instructions: the 66H, F2H, and F3H prefixes are mandatory for opcode extensions. In such a case, there is no interaction between a valid REX.W prefix and a 66H opcode extension prefix.


Table 3-4. Effective Operand- and Address-Size Attributes in 64-Bit Mode
L Flag in Code Segment Descriptor11111111
REX.W Prefix00001111
Operand-Size Prefix 66HNNYYNNYY
Address-Size Prefix 67HNYNYNYNY
Effective Operand Size3232161664646464
Effective Address Size6432643264326432

Instrukční ukazatel

Změna velikosti instrukčního ukazatele je popsána v kapitole 3.5.1 Instruction Pointer in 64-Bit Mode v manuálu Basic Architecture:

In 64-bit mode, the RIP register becomes the instruction pointer. This register holds the 64-bit offset of the next instruction to be executed. 64-bit mode also supports a technique called RIP-relative addressing. Using this technique, the effective address is determined by adding a displacement to the RIP of the next instruction.

Byla zde také zmíněna nová adresace, relativní vůči RIP (RIP-relative addressing). Je to jedna z nejzajímavějších nových vlastností. Více o tom píše kapitola 3.7.5.1 Specifying an Offset in 64-Bit Mode v manuálu Basic Architecture:

The offset part of a memory address in 64-bit mode can be specified directly as a static value or through an address computation made up of one or more of the following components:

The base and index value can be specified in one of sixteen available general-purpose registers in most cases. See Chapter 2, Instruction Format, in the Intel® 64 and IA-32 Architectures Software Developer's Manual, Volume 3A.

The following unique combination of address components is also available.

K adresaci relativní vůči RIP bych raději ještě zdůraznil, že skutečně nelze používat adresování typu [RIP+EAX] ani nic podobného. Další věc, která není tak zřejmá je to, že tato adresace (jako každá jiná) reaguje na prefix změny velikosti adresy 67, takže jeho použitím je možno adresovat relativně vůči EIP:

67 8B 05 10 00 00 00   MOV EAX, [EIP+10h]

Toto není nikde v manuálech přímo popsáno, ani není používán pojem EIP-relative addressing.

Kalkulace adresy

Implicitní velikost adresy je tedy 64 bitů. Kapitola 3.3.7 Address Calculations in 64-Bit Mode v manuálu Basic Architecture popisuje nejprve kalkulace 64bitového instrukčního ukazatele:

In most cases, 64-bit mode uses flat address space for code, data, and stacks. In 64-bit mode (if there is no address-size override), the size of effective address calculations is 64 bits. An effective-address calculation uses a 64-bit base and index registers and sign-extend displacements to 64 bits.

In the flat address space of 64-bit mode, linear addresses are equal to effective addresses because the base address is zero. In the event that FS or GS segments are used with a non-zero base, this rule does not hold. In 64-bit mode, the effective address components are added and the effective address is truncated (See for example the instruction LEA) before adding the full 64-bit segment base. The base is never truncated, regardless of addressing mode in 64-bit mode.

The instruction pointer is extended to 64 bits to support 64-bit code offsets. The 64-bit instruction pointer is called the RIP. Table 3-1 shows the relationship between RIP, EIP, and IP.


Table 3-1. Instruction Pointer Sizes
Bits 63:32Bits 31:16Bits 15:0
16-bit instruction pointerNot ModifiedIP
32-bit instruction pointerZero ExtensionEIP
64-bit instruction pointerRIP

Tato kapitola dále také popisuje kalkulace ostatních adres a přímých hodnot:

Generally, displacements and immediates in 64-bit mode are not extended to 64 bits. They are still limited to 32 bits and sign-extended during effective-address calculations. In 64-bit mode, however, support is provided for 64-bit displacement and immediate forms of the MOV instruction.

All 16-bit and 32-bit address calculations are zero-extended in IA-32e mode to form 64-bit addresses. Address calculations are first truncated to the effective address size of the current mode (64-bit mode or compatibility mode), as overridden by any address-size prefix. The result is then zero-extended to the full 64-bit address width. Because of this, 16-bit and 32-bit applications running in compatibility mode can access only the low 4 GBytes of the 64-bit mode effective addresses. Likewise, a 32-bit address generated in 64-bit mode can access only the low 4 GBytes of the 64-bit mode effective addresses.

První z těchto dvou odstavců by asi bylo lepší víc vysvětlit. Co se týče kalkulace adresy, jedna věc je celkem zřejmá: pokud je součástí adresy přírůstek (displacement), jeho maximální velikost zůstává 32bitová a před kalkulací adresy dochází k jeho znaménkovému rozšíření. Méně zřejmým důsledkem je to, že adresa složená pouze z přírůstku nemůže adresovat rozsah 80000000h až FFFFFFFF_7FFFFFFFh včetně. Výjimkou jsou ovšem ty varianty instrukce MOV, jejichž jedním operandem je adresa zadána přímou hodnotou a druhým registr akumulátoru (operační znaky A0, A1, A2 a A3). V těchto případech je možné zadat celou 64bitovou adresu, takže tyto instrukce jsou svým způsobem privilegované:

48 A1 00 00 00 00 00 00 00 80   MOV RAX, [8000000000000000]

Co se týče přímých hodnot (immediates), jejich maximální velikost také zůstává 32bitová, ale opět s jednou výjimkou, kterou jsou ty varianty instrukce MOV, jejichž cílovým operandem je jeden ze všeobecných registrů (operační znaky B8BF):

48 BA 00 00 00 00 00 00 00 80   MOV RDX, 8000000000000000

Implicitní 64bitový operand

Implicitní velikost adresy je tedy 64 bitů. To je jasné. Implicitní velikost operandu je ovšem 32 bitů, a šířka zásobníku je 64 bitů. Tím vznikají další výjimky, popsané v kapitole 2.2.1.7 Default 64-Bit Operand Size v manuálu Instruction Set Reference, A-M:

In 64-bit mode, two groups of instructions have a default operand size of 64 bits (do not need a REX prefix for this operand size). These are:

To, že blízké skoky (near branches) jsou implicitně 64bitové, asi nikoho nepřekvapí (operandem je registr RIP). Mnohem více o této skupině pojednává kapitola 6.3.7 Branch Functions in 64-Bit Mode v manuálu Basic Architecture, tu ale není potřeba citovat.

Pro programátora v asembleru má ale důležité důsledky druhá skupina instrukcí, do které spadá např. PUSH atd. Znamená to totiž, že není možné používat instrukce jako PUSH EAX, ale jedině PUSH RAX (a s pomocí prefixu 66 také PUSH AX). Tyto instrukce mají totiž implicitní 64bitový operand a není způsob, jak kódovat 32bitový operand. Více se o tom zmiňuje také kapitola 6.2.5 Stack Behavior in 64-Bit Mode v manuálu Basic Architecture:

In 64-bit mode, address calculations that reference SS segments are treated as if the segment base is zero. Fields (base, limit, and attribute) in segment descriptor registers are ignored. SS DPL is modified such that it is always equal to CPL. This will be true even if it is the only field in the SS descriptor that is modified.

Registers E(SP), E(IP) and E(BP) are promoted to 64-bits and are re-named RSP, RIP, and RBP respectively. Some forms of segment load instructions are invalid (for example, LDS, POP ES).

PUSH/POP instructions increment/decrement the stack using a 64-bit width. When the contents of a segment register is pushed onto 64-bit stack, the pointer is automatically aligned to 64 bits (as with a stack that has a 32-bit width).

Všeobecné registry

Jak už bylo zmíněno, tyto registry byly rozšířeny na 64 bitů a přibylo osm nových. 64bitový mód má ovšem některé překvapivé vlastnosti, popsané v kapitole 3.4.1.1 General-Purpose Registers in 64-Bit Mode v manuálu Basic Architecture (zkráceno):

In 64-bit mode, there are 16 general purpose registers and the default operand size is 32 bits. However, general-purpose registers are able to work with either 32-bit or 64-bit operands. If a 32-bit operand size is specified: EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP, R8D - R15D are available. If a 64-bit operand size is specified: RAX, RBX, RCX, RDX, RDI, RSI, RBP, RSP, R8-R15 are available. R8D-R15D/R8-R15 represent eight new general-purpose registers. All of these registers can be accessed at the byte, word, dword, and qword level. REX prefixes are used to generate 64-bit operand sizes or to reference registers R8-R15.

In 64-bit mode, there are limitations on accessing byte registers. An instruction cannot reference legacy high-bytes (for example: AH, BH, CH, DH) and one of the new byte registers at the same time (for example: the low byte of the RAX register). However, instructions may reference legacy low-bytes (for example: AL, BL, CL or DL) and new byte registers at the same time (for example: the low byte of the R8 register, or RBP). The architecture enforces this limitation by changing high-byte references (AH, BH, CH, DH) to low byte references (BPL, SPL, DIL, SIL: the low 8 bits for RBP, RSP, RDI and RSI) for instructions using a REX prefix.

When in 64-bit mode, operand size determines the number of valid bits in the destination general-purpose register:


Table 3-2. Addressable General Purpose Registers
Register TypeWithout REXWith REX
Byte RegistersAL, BL, CL, DL, AH, BH, CH, DHAL, BL, CL, DL, DIL, SIL, BPL, SPL, R8L - R15L
Word RegistersAX, BX, CX, DX, DI, SI, BP, SPAX, BX, CX, DX, DI, SI, BP, SP, R8W - R15W
Doubleword RegistersEAX, EBX, ECX, EDX, EDI, ESI, EBP, ESPEAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP, R8D - R15D
Quadword RegistersN.A.RAX, RBX, RCX, RDX, RDI, RSI, RBP, RSP, R8 - R15

Asi nejpřekvapivější je fakt, že instrukce jako MOV EAX, EBX automaticky vynuluje horních 32 bitů registru RAX. Toto se ovšem netýká instrukcí, které cílový registr pouze čtou, jako například TEST EAX, EBX. V tomto případě zůstane registr RAX v původním stavu. Zde existuje jedna výjimka, instrukce CMOVcc, jako např. CMOVBE. Tyto instrukce vynulují tyto bity i v případě, že podmínka není splněna.

Další překvapivou věcí je nemožnost používání 8bitových registrů AH, CH, DH a BH v jedné instrukci společně s jednou z nových vlastností instrukce 64bitového módu, která vyžaduje nový prefix REX, např. s některým z nových registrů. Například instrukci MOV AH, SIL nelze v 64bitovém módu kódovat, protože registr SIL vyžaduje prefix REX 40. Důvodem je to, že použití kteréhokoliv z REX prefixů způsobí přemapování původních registrů AH, CH, DH a BH na registry SPL, BPL, SIL a DIL:

-- 88 EC   MOV AH, CH 40 88 EC   MOV SPL, BPL

Registr RFLAGS

Registr EFLAGS byl rozšířen na 64 bitů do registru RFLAGS. Přesně to popisuje kapitola 3.4.3.4 RFLAGS Register in 64-Bit Mode v manuálu Basic Architecture:

In 64-bit mode, EFLAGS is extended to 64 bits and called RFLAGS. The upper 32 bits of RFLAGS register is reserved. The lower 32 bits of RFLAGS is the same as EFLAGS.

x87 FPU, MMX

Kapitoly 8.1.1 x87 FPU in 64-Bit Mode and Compatibility Mode a 9.2.1 MMX Technology in 64-Bit Mode and Compatibility Mode v manuálu Basic Architecture prostě jen říkají, že v těchto oblastech ke změnám prakticky nedošlo.

SSE/2/3, SSSE3

Kapitoly 10.2.1 SSE in 64-Bit Mode and Compatibility Mode, 11.2.1 SSE2 in 64-Bit Mode and Compatibility Mode a 12.1.1 SSE3/SSSE3 in 64-Bit Mode and Compatibility Mode v manuálu Basic Architecture pouze opakují, že přibylo osm nových XMM registrů, jak už bylo zmíněno výše.

Nové instrukce

Nový 64bitový mód přináší i několik nových instrukcí. Většina z nich ovšem vznikla zvětšením velikosti adresy na 64 bitů, takže nejsou úplně nové. Seznam těchto instrukcí je v kapitole 5.10 64-BIT MODE INSTRUCTIONS v manuálu Basic Architecture:

The following instructions are introduced in 64-bit mode. This mode is a sub-mode of IA-32e mode.
CDQEConvert doubleword to quadword
CMPSQCompare string operands
CMPXCHG16BCompare RDX:RAX with m128
LODSQLoad qword at address (R)SI into RAX
MOVSQMove qword from address (R)SI to (R)DI
MOVZX (64-bits)Move doubleword to quadword, zero-extension
STOSQStore RAX at address RDI
SWAPGSExchanges current GS base register value with value in MSR address C0000102H
SYSCALLFast call to privilege level 0 system procedures
SYSRETReturn from fast system call

Kódování instrukcí

Kódování instrukcí je podrobně popsáno především v kapitole CHAPTER 2 INSTRUCTION FORMAT v manuálu Instruction Set Reference, A-M. Toto téma už však překračuje rámec tohoto článku, proto ho už zde dál rozebírat nebudu.

Nepodporované instrukce

V Intel manuálech není žádný stručný seznam všech nepodporovaných instrukcí v 64bitovém módu. Pár poznámek je jenom v manuálu Basic Architecture. Tady je ten seznam (abecedně):


Unsupported (Invalid) Instructions in 64-bit Mode
Mnemonic Opcode
AAA37
AADD5
AAMD4
AAS3F
BOUND62
CALLF9A
DAA27
DAS2F
INTOCE
JMPFEA
LDSC5
LESC4
POP DS1F
POP ES07
POP SS17
POPAd61
PUSH CS0E
PUSH DS1E
PUSH ES06
PUSH SS16
PUSHAd60
Group 8080
SALCD6

Následující instrukce jsou také neplatné, ale současně má jejich operační znak nový význam.


Reassigned Instructions in 64-bit Mode
Original MnemonicOpcode64-bit Mode Mnemonic
ARPL63MOVSXD
INC40–47REX Prefixes
DEC48–4FREX Prefixes

Referenci k X86 Opcodu a instrukcím najdete také na adrese http://ref.x86asm.net.

Poznámka: Pokud máte zájem o napsání podobného článku v souvislosti s manuály AMD, dejte vědět. Pokud bude větší ohlas, uvažoval bych o tom.