Java dili yüksek seviye, nesne yönelimli bir programlama dilidir, oysa Assembly dili düşük seviyeli, makineye yakın bir dildir. Aynı işlevi gerçekleştiren Java kodu ve onun Assembly dilindeki karşılığını göstermek oldukça karmaşık olabilir çünkü bu iki dil arasındaki farklar çok büyüktür. Ancak, basit bir örnek üzerinden gidelim.
Java'da yazılmış basit bir "Merhaba Dünya" programı şu şekilde olabilir:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Merhaba Dünya");
}
}
Bu Java kodunun Assembly dilinde bir karşılığı oluşturmak için, öncelikle hangi Assembly dilini (x86, ARM, vb.) ve işletim sistemini (Windows, Linux, vb.) kullanacağımızı belirlememiz gerekir. Farklı Assembly dilleri ve işletim sistemleri için yazım şekilleri değişiklik gösterebilir.
Örnek olarak, x86 Assembly dilinde ve Linux işletim sistemi için basit bir "Merhaba Dünya" programı aşağıdaki gibi olabilir:
section .data
msg db 'Merhaba Dünya',0xa
len equ $ - msg
section .text
global _start
_start:
mov eax, 4 ; syscall number for sys_write
mov ebx, 1 ; file descriptor 1 is stdout
mov ecx, msg ; message to write
mov edx, len ; message length
int 0x80 ; call kernel
mov eax, 1 ; syscall number for sys_exit
xor ebx, ebx ; return 0 status
int 0x80 ; call kernel
Bu Assembly kodu, basit bir "Merhaba Dünya" mesajını terminal üzerinde gösterir. Ancak, bu kodun Java koduyla aynı işlevi gerçekleştirdiğini söylemek zor çünkü Assembly, donanıma çok daha yakın bir düzeyde çalışır ve işletim sistemiyle doğrudan etkileşime girer. Java ise JVM (Java Sanal Makinesi) üzerinde çalışır ve birçok donanım ve işletim sistemi detayından soyutlanmıştır.
Linux işletim sistemi üzerinde çalışacak şekilde tasarlanmıştır. Her satırın ne yaptığını açıklayayım:
section .data
Bu satır, veri bölümünün başlangıcını işaret eder. Burada sabit veriler ve değişkenler tanımlanır.
msg db 'Merhaba Dünya',0xa
msg adında bir değişken tanımlar ve bu değişkene 'Merhaba Dünya' stringini ve bir satır sonu karakteri (0xa, yani newline) atar.
len equ $ - msg
len adında bir sabit tanımlar. Bu sabit, msg değişkeninin uzunluğunu hesaplar. $ işareti geçerli adresi, msg ise msg değişkeninin başlangıç adresini temsil eder. Bu fark, mesajın uzunluğunu verir.
section .text
Bu, programın kod bölümünün başlangıcını işaret eder. Burada programın çalıştırılabilir kodları yer alır.
global _start
Bu satır, _start etiketinin diğer dosyalar tarafından görülebilir olduğunu belirtir. Bu, programın giriş noktasını işaret eder.
_start:
Bu, programın başladığı yerdir.
mov eax, 4
EAX kaydedicisine 4 değerini yükler. Bu, sys_write sistem çağrısının syscall numarasıdır.
mov ebx, 1
EBX kaydedicisine 1 değerini yükler. Bu, standart çıktı dosya tanıtıcısını (stdout) temsil eder.
mov ecx, msg
ECX kaydedicisine msg değişkeninin adresini yükler. Bu, yazılacak mesajı gösterir.
mov edx, len
EDX kaydedicisine len sabitini yükler. Bu, yazılacak mesajın uzunluğunu belirtir.
int 0x80
Linux kerneline bir kesme çağrısı yapar. Bu çağrı, EAX'de belirtilen sistem çağrısını (bu durumda sys_write) gerçekleştirir.
mov eax, 1
EAX kaydedicisine 1 değerini yükler. Bu, sys_exit sistem çağrısının syscall numarasıdır.
xor ebx, ebx
EBX kaydedicisini sıfırlar. Programın çıkış durumu olarak 0 döndürülmesini sağlar.
int 0x80
Yine Linux kerneline bir kesme çağrısı yapar ve EAX'de belirtilen sistem çağrısını (bu durumda sys_exit) gerçekleştirir.
Bu kod, temel bir Assembly programının nasıl yazılacağı ve basit sistem çağrılarının nasıl kullanılacağı konusunda iyi bir örnektir.
Makina diline de çevirip yorumlar mısın?
Genel bir fikir vermek için kodunuzu makine dilinde nasıl temsil edilebileceği hakkında bazı yorumlar yapabilirim:
mov eax, 4
Bu komut, eax kaydedicisine 4 değerini yerleştirir. Makine dilinde, mov komutu genellikle kaynağı ve hedefi belirten belirli kodlarla temsil edilir. Örneğin, B8 04 00 00 00 gibi bir dizi bayt olabilir (bu sadece bir örnektir).
mov ebx, 1
Benzer şekilde, bu mov komutu ebx kaydedicisine 1 değerini atar. Makine dilinde bu, BB 01 00 00 00 gibi bir şey olabilir.
mov ecx, msg
Bu, ecx kaydedicisine msg değişkeninin adresini yükler. Makine dilindeki temsili, msg değişkeninin gerçek adresine bağlı olacaktır.
mov edx, len
edx kaydedicisine len değerini yerleştirir. Makine dilindeki temsili, len değerinin boyutuna bağlı olacaktır.
int 0x80
Bu, bir kesme çağrısıdır ve genellikle CD 80 gibi bir bayt dizisiyle temsil edilir.
xor ebx, ebx
Bu, ebx kaydedicisini sıfırlar. Makine dilinde, bu genellikle 31 DB gibi bir bayt dizisiyle temsil edilir.
Diğer mov ve int komutları
Benzer şekilde, bu komutlar da kendi makine dilindeki karşılıklarıyla temsil edilecektir.
Makine dilinde her bir komut, işlemcinin anlayabileceği spesifik bayt dizileriyle temsil edilir. Bu bayt dizileri, kullanılan işlemci mimarisine ve komutların belirli ayrıntılarına (kaynak, hedef, değerler vb.) bağlı olarak değişir.