Al

Alapok ebben a kurzusban: x86-64 Assembly

1 feladat

A(z) Alapok fogalomról

Megjegyzések

Az Exercism x86-64 kurzusa az NASM-t (The Netwide Assembler) használja assemblerként. A NASM-ban a megjegyzéseket pontosvessző (;) előzi meg.

A megjegyzések a program bármely pontján szerepelhetnek, és mindent, ami egy ; után következik, figyelmen kívül hagy az assembler.

Konstansok

A NASM-ban az equ direktívával definiálhatunk assembler-időben kiértékelt konstanst. Ez például egy UNIVERSE nevű konstanst definiál 42 értékkel:

UNIVERSE equ 42

A konstansok egyszer, a definiálásukkor értékelődnek ki, és később nem definiálhatók újra.

Általános célú regiszterek (GPR)

Assemblyben változók helyett jellemzően regisztereket használunk értékek tárolására. A CPU-regiszter egy gyors memóriaterület a számítógép processzorán belül. A számítások többsége a CPU regisztereiben zajlik.

Egyes regiszterek sokféle különböző számításhoz használhatók, ezeket általános célú regisztereknek (GPR) nevezzük. Másoknak különleges vagy kijelölt szerepük van.

Az x86-64-ben 16 darab 64 bites általános célú regiszter (GPR) van, amelyek 32, 16 vagy 8 bites formában is elérhetők. A GPR-eket az alábbi táblázat írja le, ahol az rn-ben az n 8 és 15 között van: r8, r9, r10, r11, r12, r13, r14 és r15.

64-bit 32-bit 16-bit 8-bit
rax eax ax ah/al
rbx ebx bx bh/bl
rcx ecx cx ch/cl
rdx edx dx dh/dl
rsi esi si sil
rdi edi di dil
rbp ebp bp bpl
rsp esp sp spl
rn rnd rnw rnb

Ha 64 bitnél kevesebbet használunk, az elért bitek általában a regiszter alsó részéből valók. Kivétel ez alól az ah, bh, ch és dh, amelyek a regiszter 16 bites részének felső 8 bitjét érik el.

Ábra arról, hogyan érhetők el a bitek a rax regiszter esetén:

+--------+---------------------------------------+
| 64-bit |                  rax                  |
+--------+-------------------+-------------------+
| 32-bit |                   |        eax        |
+--------+-------------------+---------+---------+
| 16-bit |                             |    ax   |
+--------+-----------------------------+----+----+
| 8-bit  |                             | ah | al |
+--------+-----------------------------+----+----+

Ezek közül néhány regisztert meg kell őrizni a függvényhívásokon keresztül: rbp, rsp, rbx, r12, r13, r14 és r15. Ha nem őrizzük meg őket, az hibához vagy nem definiált viselkedéshez vezethet.

A többieket nem kell megőrizni, és szabadon használhatók: rax, rcx, rdx, rdi, rsi, r8, r9, r10 és r11.

Note

A regisztereket általában a veremre mentve őrizzük meg egy push utasítással. Ezután a függvény vége előtt egy pop utasítással állítjuk vissza őket.

Utasítások

Az utasítások olyan számítások, amelyeket a CPU végre tud hajtani. Általában a következő formájuk van:

name destination, source

Az utasítás neve áll az első helyen, majd legalább egy szóköz, utána a céloperandus, egy vessző (,) és végül a forrásoperandus. Az utasítás általában nem módosítja a forrásoperandust, csak a céloperandust.

Mindkét operandusnak ugyanakkora méretűnek kell lennie, hacsak nincs másképp jelezve. Például ha a forrásoperandus 16 bites, akkor a céloperandusnak is 16 bitesnek kell lennie.

Ha például értéket akarunk tárolni egy regiszterben, használhatjuk a mov utasítást:

mov rax, rdx  ; rax = rdx
              ; mov is the name, rax is the destination operand and rdx is the source operand

A fenti kódrészlet a rdx forrásoperandus mind a 64 bitjének tartalmát a rax mind a 64 bitjébe másolja, amely az utasítás céloperandusa.

A legtöbb utasításban használhatunk konstans egész számokat forrásoperandusként (de céloperandusként nem):

mov rcx, 42 ; rcx = 42
            ; rcx is the destination operand and 42 is the source operand

Vegyük észre, hogy egy 32 bites regiszterbe írás a felső biteket is törli, tehát a mov eax, 42 ugyanaz, mint a mov rax, 42. Ez nem igaz a 16 és 8 bites regiszterekre.

Az összeadás, kivonás és szorzás aritmetikai műveleteihez az add, sub és imul utasításokat használhatjuk:

add rax, rsi ; rax = rax + rsi
imul rax, rdi ; rax = rax * rdi
sub rax, r8 ; rax = rax - r8

Függvények

Az utasítások függvényekbe szerveződnek. Minden függvény a forrásfájl section .text szakaszában kap helyet.

Egy függvénydeklaráció a következőkből áll:

  1. A függvény nevét viselő címke, amelyet egy : követ.
  2. A függvényt meghatározó utasítások.
  3. A visszatérő utasítás, a ret.

Külön konvenciók szabályozzák, hogyan adjuk át az argumentumokat egy függvénynek, és hogyan tér vissza belőle az érték. A két fő hívási konvenció a System V AMD64 ABI és a Microsoft x64.

Ez a kurzus a System V AMD64 ABI hívási konvenciót használja, amelyben az első hat egész argumentumot regiszterekben adjuk át a függvénynek. Átadásuk sorrendje a következő: rdi, rsi, rdx, rcx, r8 és r9.

Az egész értéket a függvény a rax regiszterben adja vissza.

Egy függvény meghívásához a call utasítást használjuk. Egy függvényt ugyanazon forrásfájl bármely pontjáról meg lehet hívni. Ahhoz, hogy egy függvény más forrásfájlokból is látható legyen, a global direktívát kell használni.

Ez például egy sum függvényt deklarál:

section .text ; functions are placed here

global sum ; sum is visible to other source files

sum:
    ; first argument is passed in rdi
    ; second argument is passed in rsi
    ; return value is placed in rax

    mov rax, rdi ; rax is now equal to rdi
    add rax, rsi ; rax = rax + rsi

    ret ; function return

Ez pedig meghívja a sum függvényünket a 3 és 5 argumentummal:

mov rdi, 3  ; First argument in rdi
mov rsi, 5  ; Second argument in rsi
call sum

; The rax register now contains the value 8 (3 + 5), after sum returns
Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg

Tanuld meg a(z) Alapok fogalmat