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.