Kod Referanslarının Ele Alınması
Bu bölüm daha sonraki incelemelerimize bir alt yapı niteliği taşımaktadır. İlk olarak derleme zamanında tanımı bulunan içsel (internal) referansların nasıl ele alındığına bakalım.
Kaynak kod içindeki referanslar kod ve data belleğindeki alanlara karşılık gelmektedir. Fonksiyon ve değişken isimleri, en nihayetinde birer adrese dönüşmesi beklenen, kaynak kod düzeyinde referanslardır. Derleme aşamasında, çoğu durumda, kaynak kod içerisindeki referanslar nihai adreslere dönüştürülemezler.
Derleyici oluşturduğu sembolik makina kodlarına, herhangi bir adres işlemiyle ilgilenmeksizin, yalnız referans isimlerini, tiplerini ve bilinirlik düzeylerini not etmektedir. Sonrasında assembler, .o uzantılı, ELF formatındaki, amaç dosyayı (relocatable object file) oluşturmaktadır. assembler, makina kodlarına ilave olarak, ELF dosyasında, ilgilendiği referanslara ilişkin kayıtların tutulduğu bir sembol tablosu da oluşturur. assembler çoğu durumda sembollere, bağlayıcının nihai adreslerle değiştirilmesi beklenen, geçici özel adresler atar. Dışsal bağlanıma kapalı statik fonksiyonlara ise nihai adresleri assembler tarafından atanabilmektedir. Aslında atanan bu adres fonksiyonun gerçek adresi değil, yer değişimini gösteren görece bir adrestir. Bu konuya daha sonra değineceğiz. Basit bir örnek üzerinden incelememize devam edelim.
Örnek kodları sırasıyla test.c, tar.c olarak isimlendirip 32 bit hedefli olarak derleyelim.
test.c uygulamasının sırasıyla foo, bar ve tar fonksiyonlarını çağırdığını görmekteyiz. tar fonksiyonunun tanımının başka modülde (tar.c) olduğuna ve bar fonksiyonunun static olarak tanımlandığına dikkat ediniz. test.c için üretilen sembolik makina kodlarında bu fonksiyonların nasıl geçtiğine bakalım.
Nokta ile başlayan komutlar, gerçek makina komutlarına karşılık gelmeyen, assembler'ı bilgilendirme amaçlı kullanılan direktiflerdir. Sembolik makina çıktısında .type ve .globl direktiflerini görmekteyiz. type ile referansın tipi, globl ile referansın bilinirlik alanı gösterilmektedir. Derleyici, test.c dosyasında foo ve bar tanımlarını gördüğü için ilgili referansların fonksiyon olduğunu not etmiş. Ayrıca static olarak tanımlanan bar fonksiyonunun dışsal bağlanıma kapalı olduğunu, foo fonksiyonunun ise .globl direktifi sayesinde global bilinirlik alanına sahip olduğunu görmekteyiz. Başka modüldeki tar fonksiyonuna ilişkin ise yalnız fonksiyon çağrısını görmekteyiz. Şimdi assembler'ın sembolik makina kodlarına ilişkin oluşturduğu gerçek makina kodlarına bakalım. Bu amaçla objdump aracını kullanacağız. test.o için makina kodları aşağıdaki gibidir.
Not: Sadeleştirme amacıyla, sembolik makina kodlarını incelerken .cfi ile başlayan assembler direktiflerini göz ardı edeceğiz.
Sol tarafta gerçek sağ tarafta ise sembolik makina kodları listelenmektedir. Sırasıyla foo, bar ve tar fonksiyon çağrılarına ilişkin komutlar aşağıdaki gibidir.
e8, sembolik call komutuna karşılık gelen gerçek makina kodu, sonraki 4 byte ise adres bilgisidir. e8 makina komutu, fonksiyonun mutlak adresini almak yerine, komut göstericisinin (Instruction Pointer, Program Counter) gösterdiği değere görece bir adres almaktadır (IP Relative Addressing, PC Relative Addressing). Komut göstericisi, bir komut işletilirken, bir sonraki komutun başlangıç adresini tutmaktadır.
Statik bar fonksiyonu için e0, diğer fonksiyonlar için ise fc ile başlayan ve ff ile devam sayılar görüyoruz. Bu gösterimde e8 komutunun sağındaki ilk byte en düşük anlamlı byte'ı göstermektedir (Little Endian). Bu sayılar işaretli olarak ele alınmaktadır, en yüksek anlamlı bit değerinin 1 olması sayının negatif olduğunu göstermektedir. Negatif sayılar bellekte ikiye tümleyen (Two's Complement) şeklinde tutulmaktadır. Bu durumda fc ff ff ff ve e0 ff ff ff sayıları sırasıyla -0x4 ve -0x20 sayılarına karşılık gelmektedir.
Not: Bir sayının ikiye tümleyenini bulmak için, ikili sayı sisteminde temsil edilen sayının, 1 olan bitleri 0 ve 0 olan bitleri 1 yapılarak önce bire tümleyeni alınır. Sonrasında elde edilen sonuç 1 ile toplanarak ikiye tümleyenine ulaşılır.
Amaç kodlar içinde oldukça sık rastlanan fc sayısının rastgele bir sayı olmadığına dikkat ediniz. Bir sonraki makina komutunun adresinden 4 byte geri geldiğimizde, ilgili makina komutunun başlangıç adresinden bir sonraki adrese ulaşmaktayız. Böyle bir çağrı yapılması durumunda işlemci üzerinde illegal instruction hatası oluşacağı açıktır. fc ile başlayan bu değerler, daha sonra bağlayıcı tarafından değiştirilmesi beklenen geçici değerlerdir.
foo ve tar fonksiyonları için bu değerler objdump tarafından hesaplanarak, call komutunun operandı olarak, 1c ve 26 şeklinde gösterilmektedir. Statik bar fonksiyonu için 5 değerine ulaşıldığına ve bar fonksiyonunun 5 numaralı adresten başladığına dikkat ediniz.
nm ve readelf araçları ile amaç dosya içindeki sembolleri aşağıdaki gibi listeleyebiliriz.
Tarif
Anlamı
t
Fonksiyon tanımının ilgili modül içinde olduğunu fakat dışsal bağlanıma kapalı olduğunu gösterir
T
Fonksiyon tanımının ilgili modül içinde olduğunu ve dışsal bağlanıma açık olduğunu gösterir
U
Fonksiyon tanımının ilgili modül içinde olmadığını gösterir
Benzer sonuçlara readelf ile aşağıdaki gibi ulaşabiliriz. Dışsal bağlanıma kapalı bar LOCAL olarak gösterilmektedir.
Dana önce, ELF formatındaki amaç dosya içinde assembler tarafından oluşturulan, bir sembol tablosu tutulduğunu söylemiştik. ELF içinde ayrıca, assembler tarafından geçici adres verilen referanslara ilişkin bilgilerin tutulduğu yeniden konumlandırma (relocation) bölümü de bulunmaktadır.
Bağlayıcı nihai adreslerini atayacağı sembolleri relocation bölümüne bakarak bulmaktadır. relocation bölümünü aşağıdaki gibi listeleyebiliriz. relocation bölümünde, statik bar fonksiyonunun bulunmadığına dikkat ediniz.
Şimdi bağlanma aşamasından sonra uygulamanın makina kodlarına bakalım.
Fonksiyonlara gerçek adresleri atanmasına karşın, bar fonksiyonu çağrısına ilişkin makina komutunun değişmediğini görüyoruz. İlgilendiğimiz fonksiyonların adresleri belirlendiğinden (symbol resolution) artık relocation bölümünde kayıtları bulunmamaktadır.
Bir uygulamanın paylaşımlı bir kütüphaneye bağımlı olması durumunda ise, uygulama içerisinde, tanımı kütüphanede olan, dışsal (external) referanslar olacaktır. Derleme zamanında bağlayıcı tarafıdan çözümlenemeyen bu referansların, yükleme zamanında dinamik bağlayıcı tarafından çözümlenmesi beklenmektedir.
Last updated
Was this helpful?