Class Dosyalarının Anatomisi

Java dilinde yazdığımız programların derlendiğinde .class uzantılı dosyalara dönüştüğünü Java ile uğraşan hemen herkes bilir. Ancak class dosyaları bir çoğumuz için bugüne kadar hep içini bilmediğimiz, hakkında bilgi sahibi olmadığımız birer muamma olarak  kaldılar. Bu yazıda class dosyalarının yapısını inceleyip yazdığımız kodların derlendikten sonra nasıl bir şekle dönüştüğünü açıklamaya çalışacağım.

Class dosyaları kısaca yazdığımız .java uzantılı Java sınıflarının java derleyicisi tarafından derlenmesi sonucu oluşan ve içeriğini Java Sanal Makinesinin (JVM – Java Virtual Machine) anlayıp yorumlayabildiği dosyalardır. Bu dosyaların içinde JVM için tanımlanmış bir takım bilgiler ve aynı zamanda JVM’nin çalıştırdığı bytecode’lar yer alır. Bytecode JVM’nin çalıştırdığı özel komutlara verilen addır ve class dosyasının içinde belli bölümlerde yer almaktadırlar. Birçok kişinin sandığı gibi class dosyaları sadece bytecode’lardan oluşmaz. İçerisinde birçok ek tanımlamalar da mevcuttur.

Artık class dosyalarını incelemeye başlayabiliriz. Bunun için önce örnek bir “Merhaba Dünya” programı yazalım.

Daha sonra bu programı

javac MerhabaDunya.java

komutunu kullanarak veya bir IDE aracılığıyla derleyelim. Sonuçta elimizde MerhabaDunya.class isimli bir dosya olacaktır. Bu dosya ikili formatta (binary) kodlandığı için notepad benzeri bir programla açtığımızda anlamsız karakterler görürüz. Bu yüzden yardımcı bir program kullanmamız gerekecek. Ben bu adresteki Hex Editor Neo programını kullanacağım. Program kurulduktan sonra açarak MerhabaDunya.class isimli dosyamızı gösteriyoruz ve aşağıdaki manzarayla karşılaşıyoruz.

Bu şekilde elimizdeki MerhabaDunya.class dosyasının hexadecimal formatta kodlanmış halini görüyoruz.  Bilmeyenler için söyleyelim, yukarıdaki şekilde gördüğünüz her karakter hexadecimal olduğundan 4 bitten oluşmaktadır. Yan yana gruplandırılmış iki sayı 8 biti yani 1 baytı ifade etmektedir.

Dikkati çeken ilk şey ilk 4 baytın “CA”, “FE, “BA”, “BE”  olmasıdır. Bu bayt dizisi bütün class dosyalarının başında bulunmaktadır ve “Magic Number” olarak adlandırıır. Magic Number ikili formatta kodlanmış dosyaları birbirinden ayırmak için kullanılan bir bayt dizisi olarak tanımlanabilir. Örneğin class dosyalarının ilk 4 baytı 0xCAFEBABE iken JPEG dosyaları 0xFFD8 veya 0xFFD9 ile başlar.  Class dosyaları için 0xCAFEBABE seçilmesinin hikayesini James Gosling burada böyle açıklamış. Merak eden okuyabilir 🙂

Sun firması class dosyaları için standart bir dosya formatı belirlemiştir. Bu format aşağıdaki şekildedir.

    ClassFile {
    	u4 magic;
    	u2 minor_version;
    	u2 major_version;
    	u2 constant_pool_count;
    	cp_info constant_pool[constant_pool_count-1];
    	u2 access_flags;
    	u2 this_class;
    	u2 super_class;
    	u2 interfaces_count;
    	u2 interfaces[interfaces_count];
    	u2 fields_count;
    	field_info fields[fields_count];
    	u2 methods_count;
    	method_info methods[methods_count];
    	u2 attributes_count;
    	attribute_info attributes[attributes_count];
    }

Bu yapıda gördüğünüz “u4” o alanın 4 byte olduğunu “u2” ise 2 bayt olduğunu göstermektedir. Yapıda görüldüğü gibi 4 baytlık magic number alanından sonra 2 baytlık minor version ve major version alanları gelmektedir. Yukarıdaki tablomuza bakarak bu alanların değerlerini kolayca bulabiliriz. Tabloya göre ilk 4 bayttan sonra gelen minor version alanı hexdecimal olarak 0x0000, major version alanı ise 0x0031 olarak görülmektedir. Bu iki değer birlikte class dosyasının versiyonunu belirlemektedir. Örneğin 0x0031 değeri class dosyasının J2SE 5.0’e göre oluşturulduğunu göstermektedir.

Class formatımıza döndüğümüzde sonraki 2 baytın constant_pool_count isimli bir değişkene işaret ettiğini görüyoruz. Bu değer ise tablodan baktığımızda 0x001D’dir. Hexadecimal olan bu değeri onlu sisteme çevirdiğimizde 29 olduğunu görürüz. Bu değer class dosyasının içinde tanımlı olan constant_pool tablosundaki satır sayısının 1 fazlasıdır.(Bunun sebebi constant pool tablosunda 0. indexin kullanılmamasıdır) Yani bizim değerimiz 29 olduğuna göre class dosyasındaki constant_pool 28 satırdan oluşuyor demektir. Bunun hemen ardından da zaten constant_pool alanının başladığnı görüyoruz.

Constant pool class dosyasının en önemli parçalarından birisidir ve metod isimleri, sayılar, stringler, sınıf ve metod referansları gibi birçok değerin tutulduğu bir tablodur. Bu tablonun her satırının uzunluğu  sakladığı veriye göre değişkenlik gösterir. Tablonun her satırında 1 byte uzunluğunda bir tür değişkeni vardır ve bu değer o satırın tuttuğu değerin türünü göstermektedir. Örneğin bu tür bilgisi 1 ise bu satırda bir string tutuluyor demektir, 7 ise bir sınıf referansı tutuluyor demektir. Bizim örneğimizde constant pool 28 satırdan oluşmaktadır ancak bu satırların uzunluğunu bilemediğimizden tablonun büyüklüğünü de bilmemiz mümkün değil. Dolayısıyla yukarıdaki hexadecimal tabloda constant pool alanının nerede başladığı biliyor olsak da nerede bittiğini kestiremiyoruz. Aslında biraz zorlasak bunu da yapabiliriz ancak gerek yok, constant pool içeriğini görmek için yardımcı bir araç kullanacağız.

Hemen bu adrese gidip Class Editor isimli uygulamayı indiriyoruz ve aynen Hex Editor’de yaptığımız gibi programa MerhabaDunya.class dosyasını gösteriyoruz. Program  class dosyasının içeriğini class dosya formatına uygun olarak bize sunuyor. Constant pool sekmesine geçerek aşağıdaki görüntüyü elde edebiliriz.

Burada constant pool içindeki her bir satırın içeriğini görebiliyoruz. Program sadece constant pool değil, class formatı içerisinde gördüğünüz diğer bütün alanları gösterebiliyor. Class formatı içindeki diğer alanlardan da kısaca bahsedelim.

Constant pool’dan sonra gelen access_flags alanı bu sınıfın erişim haklarını belirlemeye yarar. Arayüzler (interface) de class formatında dosyalara çevrildiği için class dosyasının bir sınıfı mı yoksa bir arayüzü mü temsil ettiği bilgisi de burada tutulur. this_class alanı class dosyasının temsil ettiği sınıf hakkında bilgileri, super_class ise o sınıfın atasının bilgilerini tutar. Aslında this_class ve super_class alanlarında bu bilgiler direk olarak tutulmaz. Bu işlem constant pool tablosuna geçerli bir index değeri tutularak yapılır. Yani this_class ve super_class alanlarında sadece constant pool alanında geçerli olan bir index değeri tutulur. Bu index değerinin işaret ettiği constant pool satırında ise sınıf bilgisi vardır. Böylece dolaylı olarak sınıf bilgisi tutulmuş olur. super_class alanı için index değeri 0 ise o sınıfın atası object sınıfı demektir.

Daha sonra gelen interface_count alanı o sınıfın kaç arayüzü gerçekleştirdiğini (implement ettiğini) gösterir. Hemen ardından gelen interfaces dizisi ise gerçekleştirilen arayüzlerin isimlerini yine constant pool alanına referans ederek dolaylı olarak tutar. Yani bu dizinin her elemanı constant pool alanındaki bir satıra işaret eden bir index değeridir.

Field_count alanı tahmin edeceğiniz üzere sınıf içerisinde tanımlı alanların (değişkenlerin) sayısını göstermektedir. Her field, field_info isimli bir yapı ile temsil edilmektedir. Bu yapı aşağıdaki gibidir.

    field_info {
    	u2 access_flags;
    	u2 name_index;
    	u2 descriptor_index;
    	u2 attributes_count;
    	attribute_info attributes[attributes_count];
    }

Burada access_flags alanı, o değişken ile ilgili erişim bilgilerini saklar. Aynı zamanda değişkenin static veya final olup olmaması ile ilgili bilgileri de tutar. Yukarıda bahsettiğimiz access_flags alanı ise sınıfla ilgili erişim bilgilerini tutuyordu. Bu ikisi farklı şeyler. name_index ve descriptor_index alanları yine constant pool alanında tanımlı bir index değerine eşittir ve dolaylı olarak bilgi tutmaya yarar. Bir de değişkenlerle ilgili attribute (nitelik) denilen yapılar tutulmaktadır. Bunlara kısaca değinmek gerekirse ilgili olduğu değişken ile ilgili bir takım ek bilgilerin saklandığı yapılar diyebiliriz.

Değişkenler ile ilgili tanımlardan sonra metotların başladığını görüyoruz. Metotlar da değişkenler gibi method_info isimli bir yapı ile temsil edilirler. Bu yapı da aslında field_info yapısının aynısıdır.

    method_info {
    	u2 access_flags;
    	u2 name_index;
    	u2 descriptor_index;
    	u2 attributes_count;
    	attribute_info attributes[attributes_count];
    }

Bu yapı içerisindeki alanların görevleri de aynen değişkenlerde olduğu gibidir. Ancak buradaki access_flags metotlar için geçerli olduğundan biraz farklılık göstermektedir. Buradaki attribute yapıları da metotlara özgü bazı ek bilgileri saklamaya yarar. Bunların en önemlileri Code ve Exception nitelikleridir. Exception niteliği bir metodun fırlatabileceği exception’ları belirtirken, Code niteliği metot içerisinde bulunan ve JVM tarafından çalıştırılan komutları (bytecode) içerir.

Son olarak değişkenler ve metotlarda olduğu gibi sınıfın kendisi için de attribute isimli, sınıfa ait ek bilgiler tutulan yapılar mevcut. Ancak çok detay olduğu için bunlara değinmeyeceğim. İsteyenler bu linkten hem değişkenler, hem metotlar, hem de sınıflar için tanımlı tüm nitelikleri (attribute) inceleyip öğrenebilirler.

Bir class dosyasının anatomisi bu şekilde. Gördüğünüz gibi bizim yazdığımız bir java sınıfı bütün detaylarıyla JVM’nin anlayabileceği bir şekilde kodlanıyor. Zaten derleme denen şey de budur, insanın anlayabildiği bir kodu makinenin anlayacağı formata çevirmek. Sonraki yazılarda görüşmek üzere..

Share

3 Replies to “Class Dosyalarının Anatomisi”

  1. Merhaba, hocam anlatımınız geyet iyi ancak benim sorum farklı. derlenmiş bi java dosyasında yani .class dosyasında bazı satırların okuna bilirliğini engellemek için şifrelenmiş bir .class dosyam var bu .class dosyasını decode etmek için hangi yolu ve nasıl bi programmı kullanmalıyım yardımcı olurmusunuz ?

  2. merhabalar hocam benim javaya çok ilgim var yeni başlıyorum javaya özellikle de cep teleafonları için java oyun ve uygulamalar yapmayı çok istiyorum bunun için nasıl bir yol izlemeliyim hangi programlara ihtiyacım var özellikle de elimde birkaç tane java oyun var onları türkçeleştirmek istiyorum bunun için hangi programa ihtiyacım var nette çok araştırdım bilgisi olan yok gibi tek umudum sizsiniz inanın ta Amerikalara gidebildiğinize göre çok zeki birisiniz ALLAH yolunuzu açık etsin java daki jar uzantılı cep oyunlarını türkçeleştirme konusunda da yardımcı olursanız çok sevinirim şimdiden ilginize çok teşekkür ederim….

    1. Oncelikle sunu soyleyeyim mobil telefonlar icin uygulama gelistirecekseniz artik devir Android, iOS ve Windows 8 Phone devri. Bunlara yonelirseniz akilli telefonlar icin yazilim gelistirme sansiniz olur. Android icin uygulamalar Java dilinde yaziliyor belki ilginizi daha cok ceker. jar uzantili cep telefonu oyunlarini turkcelestirmek icin yazilimin kaynak kodunu bulmaniz gerekebilir. Eger yazilim acik kaynak kodluysa internette bulma sansiniz var ama degilse biraz zor. Java Decompiler kullanarak jar dosyasindan kaynak kodunu uretmeyi deneyebilirsiniz. Kolay gelsin.

Bir Cevap Yazın