Skip to content

Latest commit

 

History

History
128 lines (113 loc) · 4.9 KB

7.md

File metadata and controls

128 lines (113 loc) · 4.9 KB

X86_64 Assembly'e merhaba deyin [bölüm 7]

X86_64 Assembly'a merhaba deyin 'in yedinci bölümü ve burada C ile birlikte assembly dilini nasıl kullanabileceğimize bakacağız.

Aslında birlikte kullanmanın 3 yoluna sahibiz:
•C kodundan assembly çağırma yöntemi
•Assembly kodundan C çağırma yöntemi
•C kodundan satıriçi assembly kullanımı

Assembly ve C'yi birlikte nasıl kullanacağımızı gösteren 3 basit Merhaba dünya programı yazalım.

C'den Assembly çağrısı

Öncelikle şöyle basit bir C programı yazalım:

#include <string.h>

extern void merhabaDunyaYazdir(char *str, int len);

int main() {
    char* str = "Merhaba Dunya\n";
    int len = strlen(str);
    merhabaDunyaYazdir(str, len);
    return 0;
}

Burada iki değişkeni tanımlayan C kodunu görebiliyoruz: stdout'a yazacağımız Hello world stringimiz ve bu stringin uzunluğu.Sonra 2 değişkeni parametre olarak merhabaDunyaYazdir assembly fonksiyonun çağırırız.X86_64 Linux kullandığımız için x86_64 Linux calling convetions bilmeliyiz böylece merhabaDunyaYazdir fonksiyonunu nasıl yazacağımızı bileceğiz.(gelen parametreler nasıl alınır vb.)Fonksiyonu çağırdığımızda ilk altı parametre rdi, rsi, rdx, rcx, r8 ve r9 genel amaçlı registerlara geçilir, başka bir stack boyunca.Böylece birinci ve ikinci parametreleri rdi ve rsi registerlarından alabiliriz ve write syscall'unu çağırabiliriz ve ret komutu ile fonksiyondan dönebiliriz.

global merhabaDunyaYazdir

section .text
merhabaDunyaYazdir:
    ;; 1 arg
    mov r10, rdi
    ;; 2 arg
    mov r11, rsi
    ;; write syscall çağır
    mov rax, 1
    mov rdi, 1
    mov rsi, r10
    mov rdx, r11
    syscall
    ret

Şimdi bununla build edebiliriz:

build:
    nasm -f elf64 -o casm.o casm.asm
    gcc casm.o casm.c -o casm

Inline(satır içi) assembly

Aşağıdaki yöntem, assembly kodunu doğrudan C koduna yazmaktır. Bunun için özel bir sözdizimi var. Genel bir görünümü var:

asm [volatile] ("assembly kodu" : output operand : input operand : clobbers);

Gcc belgelerinde okuyabileceğimiz gibi volatile kelimesinin anlamı:

Genişletilmiş asm ifadelerinin tipik kullanımı çıktı değerleri üretmek için girdi değerlerini manipüle etmektir.Ancak asm ifadeleriniz de yan etkilere neden olabilir.
Öyleyse belirli optimizasyonları devre dışı bırakmak için volatile niteleyicisini kullanmanız gerekebilir.

Her operand parantez içerisindeki C ifadeleri ile kısıtlı string olarak tanımlanır.Bir takım kısıtlamalar var:

• r - genel amaçlı registerda değişken değeri tutar.
• g - Any register, memory or immediate integer operand is allowed, except for registers that are not general registers.
• f - Floating point register
• m - Makinenin genel olarak desteklediği herhangi bir adrese sahip bir bellek operandına izin verilir.
• ve benzeri
Yani merhaba dünyamız böyle olacak:

#include <string.h>

int main() {
    char* str = "Merhaba Dunya\n";
    long len = strlen(str);
    int ret = 0;

    __asm__("movq $1, %%rax \n\t"
        "movq $1, %%rdi \n\t"
        "movq %1, %%rsi \n\t"
        "movl %2, %%edx \n\t"
        "syscall"
        : "=g"(ret)
        : "g"(str), "g" (len));

    return 0;
}

Burada, önceki örnekteki ve satır içi assembly tanımındakiyle aynı 2 değişkeni görebiliriz.Öncelikle rax ve rdi registerlarına 1 koyduk (write system call sayısı ve stdout) merhaba dünyamızda yaptığımız gibi.Daha sonra rsi ve rdi registerları ile benzer işlemleri yaparız, ancak ilk önce operandlar $ yerine % sembolüyle başlar.Str % 1 ile ifade edilen çıkış operandı ve len ise %2 ile ifade edilen ikinci çıkış operandıdır, bu nedenle str ve len değerlerini rsi ve rdi'ye % n notasyonu ile koyarız, burada n output(çıkış) operand sayısıdır.Ayrıca burada register ismine %% eklenmiştir.

 Bu, GCC'nin operandlar ve registerlar arasında ayrım yapmasına yardımcı olur. Operandlar prefix olarak tek % işaretini sahiptir.

Bununla build edebiliriz:

build:
   gcc casm.c -o casm

Assembly'den C Çağrısı

Ve son yöntem, C fonksiyonunu assembly kodundan çağırmaktır. Örneğin sadece tek bir fonksiyon ile Merhaba Dünya yazan basit C kodunu takip ediyoruz:

#include <stdio.h>

extern int yaz();

int yaz() {
    printf("Merhaba Dünya\n");
    return 0;
}

Şimdi bu fonksiyonu assembly kodumuza dışsal(extern) olarak tanımlayabilir ve daha önceki yazılarda çoğu zaman yaptığımız gibi çağrı komutu ile çağırabiliriz:

global _start

extern yaz

section .text

_start:
    call    yaz

    mov rax, 60
    mov rdi, 0
    syscall

bununla build edebiliriz:

build:
    gcc  -c casm.c -o c.o
    nasm -f elf64 casm.asm -o casm.o
    ld   -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc casm.o c.o -o casm

ve şimdi üçüncü merhaba dünyamızı çalıştırabiliriz.