A gépi kód egy gépi nyelven írt számítógépes program. Egy adott számítógép-architektúra utasításkészletét használja. Általában binárisan írják. A gépi kód a szoftver legalacsonyabb szintje. Más programozási nyelveket lefordítanak gépi kódra, hogy a számítógép végre tudja hajtani őket.
Az utasítás megmondja a folyamatnak, hogy milyen műveletet hajtson végre. Minden utasítás egy opcode-ból (műveleti kód) és operandus(ok)ból áll. Az operandusok általában memóriacímek vagy adatok. Az utasításkészlet a számítógép számára rendelkezésre álló opkódok listája. A gépi kód az, amire az assembly kódot és más programozási nyelveket lefordítják, vagy amiként értelmezik.
A programkészítők a kódot egy másik nyelvre vagy gépi kódra alakítják át. A gépi kódot néha natív kódnak is nevezik. Ezt akkor használják, amikor olyan dolgokról beszélünk, amelyek csak néhány számítógépen működnek.
Mi történik a gépi kóddal futtatáskor?
Amikor egy processzor végrehajt egy programot, a memóriából olvasott bitek utasításként kerülnek értelmezésre. A CPU az utasításokat dekódolja, az opcod alapján kiválasztja a végrehajtandó műveletet, majd végrehajtja azt az operandusokkal (regiszterek, memóriahelyek vagy közvetlen értékek — az ún. immediate értékek). Sok processzor belső architektúrája regisztereket használ az adatok gyors tárolására és mozgatására.
Utasításformátumok és címzési módok
- Fix vagy változó hosszúság: Egyes utasításkészleteknél (például ARM, RISC-V) az utasítások egységes, rögzített bit-hosszúságúak, másoknál (például x86) változó hosszúságúak lehetnek.
- Címzési módok: Az operandus megadható közvetlen értékként (immediate), regiszterként, memóriacímmel, vagy számított címként (például indexelt vagy báziscímmel kombinálva).
- Little-endian vs. big-endian: Az adatok memóriairányú bájtsorrendje (endianness) befolyásolja, hogyan tárolódnak a többbájtos értékek.
Összetevők és eszközök
A gépi kód előállításához és kezeléséhez több eszköz is tartozik:
- Fordító (compiler): Magas szintű nyelveket fordít gépi kódot eredményező köztes vagy közvetlen kódra.
- Assembler: Az assembly nyelvet alakítja át gépi kóddá.
- Linker és loader: Több objektumfájlt összekapcsolnak és betöltenek a memóriába futtatás előtt.
- Disassembler és debugger: A gépi kód szöveges (assembly) formára visszaalakítására, valamint futás közbeni vizsgálatra szolgálnak.
Natív kód és hordozhatóság
A gépi kód gyakran natív kód néven is szerepel: ez azt jelenti, hogy közvetlenül az adott processzor-architektúra számára értelmezett. Ennek előnye a nagy teljesítmény, hátránya viszont a korlátozott hordozhatóság — egy x86-ra fordított bináris nem fut natívan egy ARM processzoron. Ezt a problémát cross-compile (távoli fordítás), emuláció vagy köztes formátumok (például bytecode és virtuális gépek) használatával lehet kezelni.
Architektúra és mikroarchitektúra
Fontos elkülöníteni az utasításkészletet (ISA) és a mikroarchitektúrát. Az ISA határozza meg, milyen utasítások érhetők el és milyen bináris formátumban, míg a mikroarchitektúra — a konkrét CPU implementáció — dönti el, hogyan valósítja meg ezeket az utasításokat belső áramkörök és technikák (pipeline, superscalar végrehajtás, spekulatív végrehajtás, microcode) segítségével.
Biztonság és elemzés
A gépi kód elemzése fontos a biztonság és hibakeresés szempontjából. A rosszindulatú kódok gyakran közvetlenül gépi kód szinten manipulálják a memóriát (például visszatérési címek felülírásával). A modern biztonsági gyakorlatok — mint például a végrehajtás-védelmi mechanizmusok, címrandomizálás (ASLR) és veremvédelmi technikák — részben a gépi kód és a végrehajtás módja miatt alakultak ki.
Példák és érdekességek
- Gyakori utasításkészletek: x86/x86-64, ARM, RISC-V, MIPS.
- A disassembler segítségével a bináris fájlokból assembly kódot készíthetünk, ami megkönnyíti a megértést és a hibakeresést.
- A fordítók és optimalizálók célja, hogy a magas szintű forráskódból hatékony gépi kódot állítsanak elő, kihasználva az adott architektúra sajátosságait.
Összefoglalva: a gépi kód a számítógép által közvetlenül végrehajtható bináris utasítások sorozata. Az utasításkészlet és a processzor megvalósítása határozza meg, hogyan néz ki ez a kód, milyen műveleteket lehet vele végezni, és mennyire hordozható más rendszerekre.

