Montážní jazyk X86 - X86 assembly language
![]() | tento článek potřebuje další citace pro ověření.Květen 2017) (Zjistěte, jak a kdy odstranit tuto zprávu šablony) ( |
x86 Jazyk sestavení je rodina zpětně kompatibilní montážní jazyky, které poskytují určitou úroveň kompatibility až k Intel 8008 představen v dubnu 1972. x86 montážní jazyky se používají k výrobě kód objektu pro x86 třída procesorů. Stejně jako všechny montážní jazyky používá zkratku mnemotechnika zastupovat základní pokyny, které procesor v počítači pochopit a následovat. Překladače někdy vytvoří překladový kód jako mezikrok při překladu programu na vysoké úrovni do strojový kód. Považováno za programovací jazyk, kódování sestavy je specifické pro stroj a nízká úroveň. Jazyky sestavení se běžněji používají pro podrobné a časově kritické aplikace, jako jsou malé reálný čas vestavěné systémy nebo operační systém jádra a ovladače zařízení.
Mnemotechnika a opcodes
Každá instrukce pro sestavení x86 je reprezentována a mnemotechnická pomůcka který, často v kombinaci s jedním nebo více operandy, se překládá do jednoho nebo více bytů nazývaných operační kód; the NOP instrukce překládá například na 0x90 a HLT instrukce překládá do 0xF4. Existuje potenciál opcodes bez zdokumentované mnemotechniky, které různé procesory mohou interpretovat odlišně, takže program, který je používá, se chová nekonzistentně nebo dokonce u některých procesorů generuje výjimku. Tyto kódy se často objevují v soutěžích o psaní kódu jako způsob, jak zmenšit, zrychlit, zjemnit kód nebo jen předvést autorovu zdatnost.
Syntax
x86 assembler má dva hlavní syntax větve: Intel syntax, původně používaný pro dokumentaci platforma x86, a AT&T syntax.[1] Syntaxe Intel je dominantní v DOS a Okna světě a syntaxe AT&T je dominantní v Unix svět, protože Unix byl vytvořen v AT&T Bell Labs.[2]Zde je souhrn hlavních rozdílů mezi Syntaxe Intel a Syntaxe AT&T:
AT&T | Intel | |
---|---|---|
Pořadí parametrů | Zdroj před cílem. movl $5, % eax | Cíl před zdrojem. mov eax, 5 |
Velikost parametru | Mnemotechnika má příponu s písmenem označujícím velikost operandů: q pro qword, l na dlouho (dword), w pro slovo a b pro bajt.[1] dopl $4, % zejm | Odvozeno od názvu použitého registru (např. rax, eax, ax, al naznačit q, l, w, b). přidat zejm, 4 |
Sigils | Okamžité hodnoty s předponou „$“, registruje předponu s „%“.[1] | Assembler automaticky detekuje typ symbolů; tj. ať už jsou to registry, konstanty nebo něco jiného. |
Efektivní adresy | Obecná syntaxe DISP (ZÁKLAD, INDEX, MĚŘÍTKO). Příklad: movl mem_location(% příliv,% ecx,4), % eax | Aritmetické výrazy v hranatých závorkách; navíc velikost klíčových slov jako byte, slovonebo dword musí být použity, pokud velikost nelze určit z operandů.[1] Příklad: mov eax, [odliv + ecx*4 + mem_location] |
Mnoho x86 assemblerů používá Syntaxe Intel, počítaje v to NASM, FASM, MASM, TASM, a YASM. PLYN, který původně používal Syntaxe AT&T, podporuje obě syntaxe od verze 2.10 přes .intel_syntax směrnice.[1][3][4] Podivností v syntaxi AT&T pro x86 je to, že operandy x87 jsou obráceny, zděděná chyba od původního AT&T assembleru.[5]
Registry
Procesory x86 mají k dispozici kolekci registrů, které lze použít jako úložiště binárních dat. Souhrnně se datové a adresní registry nazývají obecné registry. Každý registr má kromě toho, co mohou všichni dělat, také speciální účel:
- AX násobení / dělení, načítání řetězců a ukládání
- Registr indexu BX pro MOVE
- Počet CX pro řetězové operace a směny
- DX přístav adresa pro IN a OUT
- SP ukazuje na začátek zásobník
- BP ukazuje na základnu rámu stohu
- SI ukazuje na zdroj v operacích proudu
- DI ukazuje na cíl v proudových operacích
Spolu s obecnými registry existují navíc:
- Ukazatel instrukce IP
- VLAJKY
- segmentové registry (CS, DS, ES, FS, GS, SS), které určují, kde začíná 64k segment (žádné FS a GS v 80286 a dřívějších)
- další rozšiřující registry (MMX, 3DNow!, SSE atd.) (pouze Pentium a novější).
Registr IP ukazuje na offset paměti další instrukce v segmentu kódu (ukazuje na první bajt instrukce). Programátor nemá přímý přístup k registru IP.
Registry x86 lze použít pomocí MOV instrukce. Například v syntaxi Intel:
mov sekera, 1234h ; zkopíruje hodnotu 1234hex (4660d) do registru AX
mov bx, sekera ; zkopíruje hodnotu registru AX do registru BX
Segmentované adresování
The architektura x86 v nemovitý a virtuální režim 8086 používá proces známý jako segmentace adresovat paměť, ne model ploché paměti používá se v mnoha jiných prostředích. Segmentace zahrnuje skládání adresy paměti ze dvou částí, a segment a offset; segment ukazuje na začátek 64 kB skupiny adres a posun určuje, jak daleko od této počáteční adresy je požadovaná adresa. Při segmentovaném adresování jsou pro úplnou adresu paměti vyžadovány dva registry. Jeden drží segment, druhý drží offset. Aby bylo možné převést zpět na plochou adresu, je hodnota segmentu posunuta o čtyři bity doleva (ekvivalent násobení 24 nebo 16), poté se přidá k posunutí a vytvoří se úplná adresa, což umožňuje rozbití 64k bariéra díky chytrému výběru adres, i když je programování podstatně složitější.
v skutečný režim / protected pouze například pokud DS obsahuje hexadecimální číslo 0xDEAD a DX obsahuje číslo 0xCAFE, které by společně ukazovaly na adresu paměti 0xDEAD * 0x10 + 0xCAFE = 0xEB5CE. CPU proto může v reálném režimu adresovat až 1 048 576 bajtů (1 MB). Kombinováním segment a offset hodnoty najdeme 20bitovou adresu.
Původní programy IBM PC omezovaly programy na 640 kB, ale rozšířená paměť Specifikace byla použita k implementaci schématu přepínání bank, které vypadlo z používání, když pozdější operační systémy, jako je Windows, používaly větší rozsahy adres novějších procesorů a implementovaly vlastní schémata virtuální paměti.
Chráněný režim, počínaje Intel 80286, byl využíván společností OS / 2. Několik nedostatků, jako například nemožnost přístupu do systému BIOS a nemožnost přepnout zpět do reálného režimu bez resetování procesoru, zabránily rozsáhlému použití.[6] Model 80286 byl také stále omezen na adresování paměti v 16bitových segmentech, což znamená pouze 216 bajtů (64 kilobajtů ) bylo možné přistupovat najednou. Pro přístup k rozšířené funkčnosti 80286 by operační systém nastavil procesor do chráněného režimu, což by umožnilo 24bitové adresování a tedy 224 bajtů paměti (16 megabajtů ).
v chráněný režim, selektor segmentů lze rozdělit na tři části: 13bitový index, a Indikátor tabulky bit, který určuje, zda je položka v souboru GDT nebo LDT a 2-bit Požadovaná úroveň oprávnění; vidět x86 segmentace paměti.
Při odkazování na adresu se segmentem a posunem zápisu segment:offset se používá, takže ve výše uvedeném příkladu obyčejná adresa 0xEB5CE lze zapsat jako 0xDEAD: 0xCAFE nebo jako segmentový a offsetový registrový pár; DS: DX.
Existuje několik speciálních kombinací segmentových registrů a obecných registrů, které odkazují na důležité adresy:
- CS: IP (CS je Segment kódu, IP je Ukazatel instrukce) ukazuje na adresu, kde procesor načte další bajt kódu.
- SS: SP (SS je Segment zásobníku, SP je Ukazatel zásobníku) ukazuje na adresu horní části zásobníku, tj. naposledy odeslaný bajt.
- DS: SI (DS je Datový segment, SI je Zdrojový index) se často používá k označení řetězcových dat, která se mají zkopírovat do ES: DI.
- ES: DI (ES je Extra segment, DI je Cílový index) se obvykle používá k nasměrování na místo určení řetězcové kopie, jak je uvedeno výše.
Intel 80386 představoval tři provozní režimy: reálný režim, chráněný režim a virtuální režim. The chráněný režim který debutoval v 80286 byl rozšířen, aby umožnil 80386 oslovit až 4 GB paměti, zcela nový virtuální režim 8086 (VM86) umožnily spouštět jeden nebo více programů v reálném režimu v chráněném prostředí, které do značné míry emulovalo reálný režim, i když některé programy nebyly kompatibilní (obvykle v důsledku triků adresování paměti nebo použití nespecifikovaných operačních kódů).
32-bit model ploché paměti z 80386 Rozšířený chráněný režim může být nejdůležitější změnou funkcí pro rodinu procesorů x86 až do AMD propuštěn x86-64 v roce 2003, protože pomohl podpořit přijetí systému Windows 3.1 ve velkém měřítku (který se spoléhal na chráněný režim), protože systém Windows mohl nyní spouštět mnoho aplikací najednou, včetně aplikací DOS, pomocí virtuální paměti a jednoduchého multitaskingu.
Režimy provádění
Procesory x86 podporují pět režimů provozu pro kód x86, Skutečný režim, Chráněný režim, Dlouhý režim, Režim Virtual 86, a Režim správy systému, ve kterém jsou k dispozici některé pokyny a jiné nikoli. 16bitová podmnožina instrukcí je k dispozici na 16bitových procesorech x86, kterými jsou 8086, 8088, 80186, 80188 a 80286. Tyto pokyny jsou k dispozici v reálném režimu na všech procesorech x86 a v 16bitovém chráněném režimu (80286 dále) jsou k dispozici další pokyny týkající se chráněného režimu. Na 80386 a později jsou 32bitové pokyny (včetně pozdějších rozšíření) k dispozici také ve všech režimech, včetně skutečného; na těchto procesorech je přidán režim V86 a 32bitový chráněný režim, přičemž v těchto režimech jsou k dispozici další pokyny ke správě jejich funkcí. SMM s některými vlastními speciálními pokyny je k dispozici na některých procesorech Intel i386SL, i486 a novějších. Nakonec v dlouhém režimu (AMD Opteron dále), jsou k dispozici také 64bitové pokyny a další registry. Sada instrukcí je v každém režimu podobná, ale adresování paměti a velikost slova se liší, což vyžaduje různé programovací strategie.
Režimy, ve kterých lze kód x86 spustit, jsou:
- Skutečný režim (16 bitů)
- 20bitový segmentovaný adresní prostor paměti (což znamená, že pouze 1 MiB paměti lze adresovat - vlastně o něco více), přímý softwarový přístup k perifernímu hardwaru a žádný koncept ochrana paměti nebo multitasking na hardwarové úrovni. Počítače, které používají BIOS spusťte v tomto režimu.
- Chráněný režim (16bitová a 32bitová)
- Rozšiřuje adresovatelnost fyzická paměť do 16 MB a adresovatelný virtuální paměť až 1 GB. Poskytuje úrovně oprávnění a chráněná paměť, což zabraňuje vzájemnému poškození programů. 16bitový chráněný režim (používá se na konci DOS éry) používá složitý vícesegmentový paměťový model. 32bitový chráněný režim používá jednoduchý model ploché paměti.
- Dlouhý režim (64bitová)
- Většinou jde o rozšíření 32bitové instrukční sady (chráněný režim), ale na rozdíl od přechodu 16–32 bitů bylo mnoho instrukcí zrušeno v 64bitovém režimu. Průkopníkem AMD.
- Virtuální režim 8086 (16 bitů)
- Speciální hybridní provozní režim, který umožňuje provozování programů a operačních systémů v reálném režimu pod kontrolou operačního systému supervizora chráněného režimu
- Režim správy systému (16 bitů)
- Zpracovává funkce celého systému, jako je správa napájení, ovládání systémového hardwaru a vlastní kód navržený výrobcem OEM. Je určen k použití pouze firmwarem systému. Všechny běžné popravy, včetně operační systém, je pozastaven. Alternativní softwarový systém (který se obvykle nachází v počítači) firmware nebo s hardwarovou podporou debugger ) se poté provede s vysokými oprávněními.
Přepínání režimů
Procesor běží v reálném režimu ihned po zapnutí, takže operační systém jádro, nebo jiný program, musí výslovně přepnout do jiného režimu, pokud si přeje spustit v jakémkoli jiném než skutečném režimu. Přepínání režimů se provádí úpravou určitých bitů procesoru kontrolní registry po určité přípravě a po přepnutí může být nutné další nastavení.
Příklady
S běžícím počítačem BIOS, BIOS a zavaděč běží Skutečný režim, poté 64bitové jádro operačního systému zkontroluje a přepne CPU do dlouhého režimu a poté spustí nový režim jádra vlákna s 64bitovým kódem.
Se spuštěným počítačem UEFI, firmware UEFI (kromě CSM a starších Možnost ROM ), UEFI zavaděč a jádro operačního systému UEFI běží v dlouhém režimu.
Typy pokynů
Obecně platí, že rysy moderní sada instrukcí x86 jsou:
- Kompaktní kódování
- Nezávisle na proměnné délce a zarovnání (kódováno jako malý Endian, stejně jako všechna data v architektuře x86)
- Hlavně pokyny pro jednu adresu a dvě adresy, to znamená první operand je také cílem.
- Paměťové operandy jako zdroj i cíl jsou podporovány (často se používají ke čtení / zápisu prvků zásobníku adresovaných pomocí malých okamžitých posunů).
- Obecné i implicitní Registrovat používání; ačkoli všech sedm (počítáno
odliv
) obecné registry v 32bitovém režimu a všech patnáct (počítánorbp
) obecné registry v 64bitovém režimu, lze libovolně používat jako akumulátory nebo pro adresování je většina z nich také implicitně používané určitými (více či méně) zvláštními pokyny; ovlivněné registry proto musí být dočasně uchovány (normálně skládané), pokud jsou aktivní během těchto sekvencí instrukcí.
- Produkuje podmíněné příznaky implicitně prostřednictvím většiny celých čísel ALU instrukce.
- Podporuje různé režimy adresování včetně okamžitého, offsetového a škálovaného indexu, ale ne relativního k PC, kromě skoků (zavedeno jako vylepšení v x86-64 architektura).
- Zahrnuje plovoucí bod do hromady registrů.
- Obsahuje speciální podporu pro atomové read-modify-write pokyny (
xchg
,cmpxchg
/cmpxchg8b
,xadd
a celočíselné instrukce, které se kombinují számek
předpona) - SIMD instrukce (instrukce, které provádějí paralelní simultánní jednotlivé instrukce na mnoha operandech kódovaných v sousedních buňkách širších registrů).
Pokyny pro skládání
Architektura x86 má hardwarovou podporu pro mechanismus spouštěcího zásobníku. Pokyny jako tlačit
, pop
, volání
a ret
se používají se správně nastaveným zásobníkem k předávání parametrů, k přidělení prostoru pro místní data a k ukládání a obnově bodů zpětného volání. The ret
velikost instrukce je velmi užitečná pro efektivní (a rychlou) implementaci prostoru konvence volání kde volaný je zodpovědný za získání prostoru zásobníku obsazeného parametry.
Při nastavování a rám zásobníku uchovávat místní data a rekurzivní postup existuje několik možností; na vysoké úrovni vstoupit
instrukce (zavedená s 80186) trvá a hloubka vnoření procedury argument stejně jako a místní velikost argument, a smět být rychlejší než explicitnější manipulace s registry (jako např tlačit bp
; mov bp, sp
; sub sp, velikost
). Zda je rychlejší nebo pomalejší, závisí na konkrétní implementaci x86 procesoru a také na konvenci volání používané kompilátorem, programátorem nebo konkrétním programovým kódem; většina kódu x86 je určena pro běh na procesorech x86 od několika výrobců a na různých technologických generacích procesorů, což znamená velmi odlišné mikroarchitektury a mikrokód řešení i různé brána - a tranzistor -úrovňové možnosti designu.
Celá řada režimů adresování (včetně bezprostřední a základna + offset) i pro pokyny jako tlačit
a pop
, umožňuje přímé použití zásobníku pro celé číslo, plovoucí bod a adresa data jednoduchá, stejně jako vedení ABI specifikace a mechanismy relativně jednoduché ve srovnání s některými architekturami RISC (vyžadují podrobnější podrobnosti o zásobníku volání).
Celé číslo ALU instrukce
Sestava x86 má standardní matematické operace, přidat
, sub
, mul
, s idiv
; the logické operátory a
, nebo
, xor
, neg
; bitshift aritmetické a logické, sal
/sar
, shl
/shr
; otáčet s nošením i bez něj, rcl
/rcr
, rol
/ror
, doplněk BCD aritmetické pokyny, aaa
, aad
, daa
a další.
Pokyny s plovoucí desetinnou čárkou
Jazyk x86 assembleru obsahuje pokyny pro zásobníkovou jednotku s plovoucí desetinnou čárkou (FPU). FPU byl volitelný samostatný koprocesor pro 8086 až 80386, pro řadu 80486 to byla možnost na čipu a je to standardní funkce každého procesoru Intel x86 od 80486, počínaje Pentiem. Pokyny FPU zahrnují sčítání, odčítání, negaci, násobení, dělení, zbytek, odmocniny, zkrácení celého čísla, zkrácení zlomku a měřítko po dvou. Mezi operace patří také pokyny pro převod, které mohou načíst nebo uložit hodnotu z paměti v kterémkoli z následujících formátů: binárně kódované desítkové, 32bitové celé číslo, 64bitové celé číslo, 32bitová plovoucí čárka, 64bitová plovoucí bod nebo 80-bit s plovoucí desetinnou čárkou (po načtení se hodnota převede do aktuálně používaného režimu s plovoucí desetinnou čárkou). x86 také zahrnuje řadu transcendentálních funkcí, včetně sinus, kosinus, tangens, arkustangens, umocňování se základnou 2 a logaritmy k základnám 2, 10 nebo E.
Formát instrukcí registru registru do zásobníku je obvykle Fop st, st (n)
nebo Fop Svatý(n), Svatý
, kde Svatý
je ekvivalentní k st (0)
, a Svatý(n)
je jedním z 8 registrů zásobníku (st (0)
, sv (1)
, ..., sv (7)
). Stejně jako celá čísla je první operand první zdrojový operand a cílový operand. fsubr
a fdivr
by měl být vybrán jako první záměna zdrojových operandů před provedením odčítání nebo dělení. Pokyny pro sčítání, odčítání, násobení, dělení, ukládání a porovnávání zahrnují režimy instrukcí, které se po dokončení operace zobrazí v horní části zásobníku. Například faddp st (1), st
provede výpočet st (1) = st (1) + st (0)
, poté odstraní st (0)
z vrcholu zásobníku, čímž se vytvoří výsledek sv (1)
horní část zásobníku dovnitř st (0)
.
Pokyny SIMD
Moderní procesory x86 obsahují SIMD instrukce, které do značné míry provádějí stejnou operaci paralelně na mnoha hodnotách zakódovaných v širokém registru SIMD. Různé technologie výuky podporují různé operace na různých sadách registrů, ale jsou brány jako kompletní celek (od MMX na SSE4.2 ) zahrnují obecné výpočty na celé číslo nebo s plovoucí desetinnou čárkou (sčítání, odčítání, násobení, posun, minimalizace, maximalizace, porovnání, dělení nebo druhá odmocnina). Například paddw mm0, mm1
provádí 4 paralelní 16bitové (označeno w
) přidává celé číslo (označeno padd
) z mm0
hodnoty do mm1
a uloží výsledek do mm0
. Streamování rozšíření SIMD nebo SSE také zahrnuje režim s plovoucí desetinnou čárkou, ve kterém je ve skutečnosti upravena (rozbalena v) pouze první hodnota registrů SSE2 ). Byly přidány některé další neobvyklé pokyny, včetně a součet absolutních rozdílů (používá odhad pohybu v komprese videa, jako je tomu v MPEG ) a 16bitová instrukce akumulace (užitečné pro softwarové alfa míchání a digitální filtrování ). SSE (od SSE3 ) a 3DNow! rozšíření zahrnují pokyny pro sčítání a odčítání pro zpracování spárovaných hodnot s plovoucí desetinnou čárkou, jako jsou komplexní čísla.
Tyto sady instrukcí také obsahují četné pevné instrukce dílčích slov pro míchání, vkládání a extrahování hodnot v rámci registrů. Kromě toho existují pokyny pro přesun dat mezi celočíselnými registry a registry XMM (používané v SSE) / FPU (používané v MMX).
Pokyny pro manipulaci s daty
Procesor x86 také zahrnuje režimy komplexního adresování pro adresování paměti s okamžitým offsetem, registr, registr s offsetem, škálovaný registr s nebo bez offsetu a registr s volitelným offsetem a další škálovaný registr. Například lze kódovat mov eax, [Table + ebx + esi * 4]
jako jediná instrukce, která načte 32 bitů dat z adresy vypočítané jako (Tabulka + ebx + esi * 4)
offset od ds
selektor a uloží jej do eax
Registrovat. Procesory x86 obecně mohou načítat a používat paměť přizpůsobenou velikosti jakéhokoli registru, na kterém pracuje. (Pokyny SIMD obsahují také pokyny pro poloviční zatížení.)
Sada instrukcí x86 obsahuje pokyny pro načtení, uložení, přesunutí, skenování a porovnání řetězců (pánové
, stos
, mov
, scas
a cmps
) které provádějí každou operaci na zadanou velikost (b
pro 8bitový bajt, w
pro 16bitové slovo, d
pro 32bitové dvojité slovo), pak zvyšuje / snižuje (v závislosti na DF, směrovém příznaku) implicitní registr adres (si
pro pánové
, di
pro stos
a scas
, a to jak pro mov
a cmps
). Pro operace načítání, ukládání a skenování je implicitní registr cíle / zdroje / porovnání v al
, sekera
nebo eax
zaregistrovat (v závislosti na velikosti). Použité implicitní segmentové registry jsou ds
pro si
a es
pro di
. The cx
nebo ecx
registr se používá jako čítač snižování a operace se zastaví, když čítač dosáhne nuly nebo (pro skenování a srovnání), když je detekována nerovnost.
Zásobník je implementován s implicitně dekrementujícím (push) a přírůstkovým (pop) ukazatelem zásobníku. V 16bitovém režimu je tento implicitní ukazatel zásobníku adresován jako SS: [SP], v 32bitovém režimu je to SS: [ESP] a v 64bitovém režimu je to [RSP]. Ukazatel zásobníku ve skutečnosti ukazuje na poslední hodnotu, která byla uložena, za předpokladu, že jeho velikost bude odpovídat operačnímu režimu procesoru (tj. 16, 32 nebo 64 bitů), aby odpovídala výchozí šířce tlačit
/pop
/volání
/ret
instrukce. Zahrnuty jsou také pokyny vstoupit
a odejít
které rezervují a odstraňují data z horní části zásobníku při nastavování ukazatele rámce zásobníku dovnitř bp
/odliv
/rbp
. Přímé nastavení nebo sčítání a odčítání do sp
/zejm
/rsp
registr je také podporován, takže vstoupit
/odejít
pokyny jsou obecně zbytečné.
Tento kód na začátku funkce:
tlačit odliv ; uložit rámec zásobníku funkce volání (odliv) mov odliv, zejm ; vytvořte nový rám zásobníku na hromádku našeho volajícího sub zejm, 4 ; přidělit 4 bajty prostoru zásobníku pro místní proměnné této funkce
... je funkčně ekvivalentní právě:
vstoupit 4, 0
Mezi další pokyny pro manipulaci se zásobníkem patří pushf
/popf
pro ukládání a načítání registru (E) FLAGS. The pusha
/popa
instrukce uloží a načtou celý stav celočíselného registru do a ze zásobníku.
Předpokládá se, že hodnoty pro načtení nebo uložení SIMD budou zabaleny na sousedních pozicích pro registr SIMD a budou je zarovnány v postupném pořadí malého endianu. Některé pokyny pro načítání a ukládání SSE vyžadují pro správnou funkci zarovnání 16 bajtů. Sady instrukcí SIMD také obsahují instrukce „prefetch“, které provádějí načítání, ale necílí na žádný registr, používaný pro načítání mezipaměti. Sady instrukcí SSE také zahrnují instrukce dočasného úložiště, které provedou ukládání přímo do paměti bez provedení přidělení mezipaměti, pokud cíl již není uložen do mezipaměti (jinak se bude chovat jako normální úložiště.)
Většina obecných pokynů s celými čísly a plovoucí desetinnou čárkou (ale bez SIMD) může použít jeden parametr jako komplexní adresu jako druhý zdrojový parametr. Celé instrukce mohou také přijmout jeden parametr paměti jako cílový operand.
Průběh programu
Sestava x86 má bezpodmínečnou operaci skoku, jmp
, které mohou jako parametr brát okamžitou adresu, registr nebo nepřímou adresu (všimněte si, že většina procesorů RISC podporuje pouze skokový odkaz nebo krátké okamžité posunutí).
Podporováno je také několik podmíněných skoků, včetně jz
(skok na nulu), jnz
(skočit na nenulovou), jg
(skočit na větší než, podepsané), jl
(skočit na méně než, podepsáno), ja
(přeskočit nad / větší než, nepodepsané), jb
(skok pod / méně než, bez znaménka). Tyto podmíněné operace jsou založeny na stavu konkrétních bitů v (E) VLAJKY Registrovat. Mnoho aritmetických a logických operací nastavuje, vynuluje nebo doplňuje tyto příznaky v závislosti na jejich výsledku. Srovnání cmp
(porovnat) a test
instrukce nastavují příznaky, jako by prováděly odčítání nebo bitovou operaci AND, aniž by měnily hodnoty operandů. K dispozici jsou také pokyny, jako je clc
(průhledná vlajka) a cmc
(doplňková vlajka), které fungují na vlajkách přímo. Porovnání s plovoucí desetinnou čárkou se provádí pomocí fcom
nebo ficom
instrukce, které musí být nakonec převedeny na celočíselné příznaky.
Každá operace skoku má tři různé formy, v závislosti na velikosti operandu. A krátký jump používá 8bitový operand se znaménkem, což je relativní posun od aktuální instrukce. A u jump je podobný krátkému skoku, ale používá 16bitový podepsaný operand (v reálném nebo chráněném režimu) nebo 32bitový podepsaný operand (pouze v 32bitovém chráněném režimu). A daleko jump je ten, který používá úplnou část segmentu: offset hodnotu jako absolutní adresu. Existují také nepřímé a indexované formy každého z nich.
Kromě jednoduchých operací skoku existují volání
(zavolat podprogram) a ret
(návrat z podprogramu) pokyny. Před přenesením kontroly do podprogramu volání
posune adresu offsetu segmentu instrukce za volání
na hromádku; ret
vyskočí tuto hodnotu ze zásobníku a skočí na ni, čímž efektivně vrátí tok řízení do této části programu. V případě a vzdálené volání
, základna segmentu je tlačena za posunem; daleko ret
objeví offset a poté se vrátí segment segmentu.
Existují také dva podobné pokyny, int
(přerušit ), který uloží aktuální (E) VLAJKY zaregistrujte hodnotu na zásobníku a poté proveďte a vzdálené volání
kromě toho, že místo adresy používá znak vektor přerušení, index do tabulky adres obsluhy přerušení. Obslužný program přerušení obvykle ukládá všechny ostatní registry CPU, které používá, pokud nejsou použity k vrácení výsledku operace volajícímu programu (v softwaru zvaném přerušení). Odpovídající návrat z instrukce přerušení je iret
, který obnoví příznaky po návratu. Měkká přerušení výše popsaného typu používají některé operační systémy pro systémová volání, a lze jej také použít při ladění obslužných rutin pevných přerušení. Tvrdá přerušení jsou spuštěny událostmi externího hardwaru a musí zachovat všechny hodnoty registru, protože stav aktuálně prováděného programu není znám. V chráněném režimu může operační systém nastavit přerušení pro spuštění přepínače úloh, který automaticky uloží všechny registry aktivní úlohy.
Příklady
![]() | tento článek případně obsahuje původní výzkum.Březen 2013) (Zjistěte, jak a kdy odstranit tuto zprávu šablony) ( |
"Ahoj světe!" program pro DOS v sestavě stylu MASM
Použití přerušení 21h pro výstup - používají se jiné vzorky libc 's printf k tisku stdout.[7]
.Modelka malý.zásobník 100h.datazpráva db „Ahoj světe! $“.kódStart: mov ah, 09h ; Zobrazit zprávu Lea dx, zpráva int 21h mov sekera, 4C00h ; Ukončete spustitelný soubor int 21hkonec Start
"Ahoj světe!" program pro Windows v sestavě stylu MASM
; vyžaduje / coff přepínač na 6.15 a starších verzích.386.Modelka malý,C.zásobník 1 000 h.datazpráva db "Ahoj světe!",0.kódvč libcmt.libvč libvcruntime.libvč libucrt.libvč legacy_stdio_definitions.libextrn printf:uextrn výstup:uveřejnost hlavníhlavní proc tlačit offset zpráva volání printf tlačit 0 volání výstuphlavní endpkonec
"Ahoj světe!" program pro Windows v sestavování stylu NASM
; Základ obrázku = 0x00400000% definuje RVA (x) (x-0x00400000)sekce .texttlačit dword Ahojvolání dword [printf]tlačit byte +0volání dword [výstup]retsekce .dataAhoj db "Ahoj světe!"sekce .idatadd RVA(msvcrt_LookupTable)dd -1dd 0dd RVA(msvcrt_string)dd RVA(msvcrt_imports)krát 5 dd 0 ; ukončí tabulku deskriptorůmsvcrt_string dd "msvcrt.dll", 0tabulka msvcrt_Lookup:dd RVA(msvcrt_printf)dd RVA(msvcrt_exit)dd 0msvcrt_imports:printf dd RVA(msvcrt_printf)výstup dd RVA(msvcrt_exit)dd 0msvcrt_printf:dw 1dw "printf", 0msvcrt_exit:dw 2dw "výstup", 0dd 0
"Ahoj světe!" program pro Linux v sestavě stylu NASM
;; Tento program běží v 32bitovém chráněném režimu.; build: nasm -f elf -F bodne name.asm; link: ld -o name name.o;; V 64bitovém režimu můžete použít 64bitové registry (např. Rax místo eax, rbx místo ebx atd.); Také v příkazu build změňte „-f elf“ na „-f elf64“.;sekce .data ; sekce pro inicializovaná datastr: db 'Ahoj světe!', 0Ah ; řetězec zprávy s znakem nového řádku na konci (10 desetinných míst)str_len: ekv $ - str ; vypočítá délku řetězce (bajty) odečtením počáteční adresy str ; z této adresy (symbol $)sekce .text ; toto je sekce kóduglobální _Start ; _start je vstupním bodem a potřebuje, aby jej globální obor „viděl“ ; linker - ekvivalentní main () v C / C ++_Start: ; zde začíná definice procedury _start mov eax, 4 ; zadejte kód funkce sys_write (z vektorové tabulky OS) mov odliv, 1 ; specifikovat deskriptor souboru stdout - v GNU / Linuxu je vše považováno za soubor, ; dokonce i hardwarová zařízení mov ecx, str ; přesunout start _adresu_řetězcové zprávy do registru ecx mov edx, str_len ; přesunout délku zprávy (v bajtech) int 80h ; přerušit jádro k provedení systémového volání, které jsme právě nastavili - ; v gnu / linux jsou služby vyžadovány prostřednictvím jádra mov eax, 1 ; zadejte kód funkce sys_exit (z vektorové tabulky OS) mov odliv, 0 ; zadejte návratový kód pro OS (nula říká, že vše proběhlo v pořádku) int 80h ; přerušit jádro za účelem provedení systémového volání (pro ukončení)
"Ahoj světe!" program pro Linux v sestavě stylu NASM pomocí C standardní knihovna
;; Tento program běží v 32bitovém chráněném režimu.; gcc ve výchozím nastavení propojuje knihovnu standard-C; build: nasm -f elf -F bodne name.asm; link: gcc -o name name.o;; V 64bitovém režimu můžete použít 64bitové registry (např. Rax místo eax, rbx místo ebx atd.); Také v příkazu build změňte „-f elf“ na „-f elf64“.; globální hlavní ; main musí být definován tak, jak je kompilován proti knihovně C-Standard externí printf ; deklaruje použití externího symbolu, protože printf je deklarován v jiném modulu objektu. ; Linker vyřeší tento symbol později.segment .data ; sekce pro inicializovaná data tětiva db 'Ahoj světe!', 0Ah, 0 h ; řetězec zprávy s znakem nového řádku (10 desetinných míst) a zakončovacím znakem NULL ; řetězec nyní odkazuje na počáteční adresu, na které je uložen „Hello, World“.segment .texthlavní: tlačit tětiva ; posuňte adresu prvního znaku řetězce do zásobníku. Toto bude argument pro printf volání printf ; volá printf přidat zejm, 4 ; posune ukazatel zásobníku o 4 vyprázdnění argumentu tlačeného řetězce ret ;vrátit se
"Ahoj světe!" program pro 64bitový režim Linux v sestavě stylu NASM
; build: nasm -f elf64 -F trpasličí hello.asm; link: ld -o ahoj ahoj.oVÝCHOZÍ REL ; ve výchozím nastavení používat režimy adresování RIP relativní, takže [foo] = [rel foo]SEKCE .rodata ; data jen pro čtení lze přejít do sekce .rodata na GNU / Linux, například .rdata na WindowsAhoj: db "Ahoj světe!",10 ; 10 = ` n`.len_Hello: ekv $-Ahoj ; dostat NASM k výpočtu délky jako konstanty času sestavení;; write () trvá déle, takže řetězec ve stylu C zakončený 0 není potřeba. Bylo by to pro putySEKCE .textglobální _Start_Start: mov eax, 1 ; __NR_write číslo syscall z Linuxu asm / unistd_64.h (x86_64) mov edi, 1 ; int fd = STDOUT_FILENO Lea rsi, [rel Ahoj] ; x86-64 používá RIP relativní LEA k vložení statických adres do regů mov rdx, len_Ahoj ; size_t count = len_Hello Syscall ; write (1, Hello, len_Hello); volání do jádra, aby se skutečně uskutečnilo systémové volání ;; návratová hodnota v RAX. RCX a R11 jsou také přepsány pomocí syscall mov eax, 60 ; __NR_exit číslo volání (x86_64) xor edi, edi ; status = 0 (normální výstup) Syscall ; _exit (0)
Spuštění pod strace ověří, že v procesu nejsou prováděna žádná další systémová volání. Verze printf by provedla mnohem více systémových volání k inicializaci libc a dynamickému propojení. Ale toto je statický spustitelný soubor, protože jsme propojili pomocí ld bez -pie nebo jakýchkoli sdílených knihoven; jediné pokyny, které běží v uživatelském prostoru, jsou ty, které zadáte.
$ strace ./hello> / dev / null # bez přesměrování, standardní výstup vašeho programu je smíšené přihlašování strace na stderr. Což je normálně v pořádkuexecve ("./ hello", ["./hello"], 0x7ffc8b0b3570 / * 51 vars * /) = 0write (1, "Hello world! n", 13) = 13exit (0) =?+++ ukončeno s 0 +++
Použití příznaků zaregistrovat
Příznaky jsou často používány pro srovnání v architektuře x86. Když je provedeno srovnání mezi dvěma daty, CPU nastaví příslušný příznak nebo příznaky. V návaznosti na to lze instrukce podmíněného skoku použít ke kontrole příznaků a větvení kódu, který by měl běžet, např .:
cmp eax, odliv jne dělej něco ; ...dělej něco: ; něco tu udělejte
Příznaky se také v architektuře x86 používají k zapnutí a vypnutí určitých funkcí nebo režimů provádění. Chcete-li například zakázat všechna maskovatelná přerušení, můžete použít instrukci:
cli
Registr příznaků je také přímo přístupný. Lze načíst nízkých 8 bitů registru příznaků ah
za použití lahf
návod. Celý registr příznaků lze také přesunout na a ze zásobníku pomocí pokynů pushf
, popf
, int
(počítaje v to do
) a iret
.
Použití registru ukazatele instrukce
The ukazatel instrukce je nazýván ip
v 16bitovém režimu, eip
v 32bitovém režimu a rip
v 64bitovém režimu. Registr ukazatele instrukce ukazuje na adresu paměti, kterou se procesor pokusí provést dále; nelze k němu přistupovat přímo v 16bitovém nebo 32bitovém režimu, ale lze zadat následující sekvenci, aby byla uvedena adresa next_line
do eax
:
volání next_linenext_line: pop eax
Tato posloupnost pokynů se generuje kód nezávislý na poloze protože volání
vezme okamžitý operand relativní k instrukci ukazující popisující offset v bajtech cílové instrukce od další instrukce (v tomto případě 0).
Zápis do ukazatele instrukce je jednoduchý - a jmp
instrukce nastaví ukazatel instrukce na cílovou adresu, takže například posloupnost jako následující vloží obsah eax
do eip
:
jmp eax
V 64bitovém režimu mohou instrukce odkazovat na data relativně k ukazateli instrukce, takže je menší potřeba kopírovat hodnotu ukazatele instrukce do jiného registru.
Viz také
Reference
- ^ A b C d E Narayam, Ram (2007-10-17). „Linux assemblers: A comparison of GAS and NASM“. Archivovány od originál 3. října 2013. Citováno 2008-07-02.
- ^ „Vytvoření Unixu“. Archivovány od originál 2. dubna 2014.
- ^ Hyde, Randall. „Který Assembler je nejlepší?“. Citováno 2008-05-18.
- ^ „GNU Assembler News, v2.1 podporuje syntaxi Intel“. 2008-04-04. Citováno 2008-07-02.
- ^ "i386-Bugs (použití jako)". Dokumentace Binutils. Citováno 15. ledna 2020.
- ^ Mueller, Scott (24. března 2006). „Procesory P2 (286) druhé generace“. Aktualizace a opravy počítačů, 17. vydání (Rezervovat) (17 ed.). Que. ISBN 0-7897-3404-4. Citováno 2017-12-06.
- ^ „Právě jsem zahájil montáž“. daniweb.com. 2008.
Další čtení
Manuály
- Příručky pro vývojáře softwaru Intel 64 a IA-32
- Příručka programátora architektury AMD64 (svazek 1-5)
Knihy
- Ed, Jorgensen (květen 2018). Programování jazyka x86-64 assembleru s Ubuntu (PDF) (1.0.97 ed.). str. 367.
- Dennis Yurichev: Porozumění jazyku shromáždění