C etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
C etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

Sembolik Makine Dilinde Yazılmış Fonksiyonların C ve C++’tan Çağrılması

02:03 ,

Bir programın tamamının sembolik makine dilinde yazılması çok özel durumlar dışında tercih edilen bir yol değildir. Bunun nedenleri şöyle sıralanabilir:

  • Sembolik makine dilleri taşınabilir (portable) değildir. Makine komutları işlemciden işlemciye değişebildiği gibi genel sentaks da aynı işlemci söz konusu olduğunda bile derleyiciden derleyiciye değişebilmektedir.
  • Sembolik makine dilleri alçak seviyeli olduğu için bu dillerde programcıların hata yapması daha olasıdır.
  • Sembolik makine dillerinde kod yazım hızı daha düşüktür. Bu da üretkenliği düşürmektedir.
  • Sembolik makine dillerinde programda hata ayıklama daha zordur.

Bu nedenlerden dolayı pratikte kodun büyük bölümünün C, C++ ( bizim için bu diller yüksek seviye :) gibi yüksek seviyeli ve taşınabilir dillerde, yalnızca kritik yerlerin sembolik makine dilinde yazılması daha yaygın kullanım biçimidir.(Örneğin Linux çekirdeğinin yalnızca %3 kadarlık kısmı sembolik makine dilinde yazılmıştır.) Bunlar göz önüne alındığında sembolik makine dilinde yazılan kodların C ve C++ gibi dillerden çağrılmasının önemi anlaşılabilir. Bu yazıda bu işlemin nasıl yapıldığı detaylı bir şekilde anlatılmaya çalışılacaktır.

Sembolik makine dilinde yazılmış bir fonksiyonun C ya da C++’tan çağrılabilmesi için önce onun sembolik makine dili derleyicisiyle derlenmesi gerekir. Bu derleme işleminden bir amaç dosya (object file) elde edilir. (Amaç dosya uzantılarının Windos’ta ".obj" biçiminde, UNIX/Linux sistemlerinde ".o" biçimindedir.) Sonra bu fonksiyonu çağıran C ya da C++ kodu da bu dillerin derleyicileri ile derlenir. Bu işlemden de bir amaç dosya (object file) elde edilir. Nihayet bu amaç dosyalar birlikte link işlemine sokularak çalıştırılabilir (executable) dosya olurulur. Örneğin "run.c" isimli C programından "utility.s" isimli sembolik makine dilinde yazılmış olan fonksiyonlar çağrılmak istensin. Yapılacak işlemleri aşağıdaki şekille özetleyebiliriz:


Yukarıda özetlediğimiz adınmlardan sorunsuz geçilebilmesi için şu noktalara dikkat edilmesi gerekir:

  • Sembolik makine dilinde fonksiyonu yazarken onun için NASM’de "global" bildirimi yapmak gerekir.
  • Derleyiciler global fonksiyonların ve değişkenlerin isimlerini amaç dosyaya yazarken değiştirebilmektedir. Bu duruma "isim dekorasyonu (name decoration)" denilmektedir. Ancak burada şunu ifade etmek istiyoruz: İsim dekorasyonu çağırma biçimine göre, derleyiciye göre ve hatta dile göre değişebilmektedir. Örneğin Windows’ta Microsoft derleyicileri "cdecl" çağırma biçiminde global sembollerin (fonksiyon ve değişkenlerin) başına bir alt tire (underscore) eklemektedir. Halbuki Linux’ta gcc derleyicileri bu isimleri hiç değiştirmeden amaç koda yazmaktadır. (Dolayısıyla Windows’ta bizim de sembolik makine dilinde yazdığımız fonksiyonların başına alt tire eklememiz gerekir. Aksi takdirde linker iki modüldeki isimleri eşleyemez.)
  • C ya da C++’ta belirlenen çağırma biçimine sembolik makine dilinde kod yazarken uyulmalıdır. (Yani örneğin C’de "cdecl" çağırma biçimine sahip olarak çağırdığımız add fonksiyonunu sembolik makine dilinde yazarken parametrelerin sağdan sola stack’e atılacağını bilerek fonksiyonu yazmalıyız ve geri dönüş değerini de EAX yazmacında bırakmalıyız.)
  • Çağrılan fonksiyonun hangi yazmaçları saklaması gerektiği bilinmelidir ve fonksiyonu yazarken bu kurala uyulmalıdır. Örneğin cdecl ve stdcall çağırma biçimlerinde çağrılan fonksiyon EAX, ECD ve EDX yazmaçlarını bozma hakkına sahiptir. Fakat diğerlerinin değerlerini değiştirecekse önce onları stack’te saklamalı fonksiyon sonlanmadan önce geri almalıdır.
  • Semboik makine dilindeki kodlarla C ya da C++’taki kodların aynı bölüm (section) içerisinde bulunması gerekir.Örneğin C ve C++ derleyicileri Windows ve Linux sistemlerinde kodu ".text" isimli bölüme yerleştirmektedir. O halde bizim de fonksiyonları ".text" isimli bölümde yazmamız gerekir. Aynı isimli bölümler linker tarafından birleştirilmektedir. (Yani birleştirme işlemi sonucunda çalıştırılabilen dosyada tek bir ".text" bölümü bulunacaktır)
  • C ya da C++ kaynak programında sembolik makine dilinden çağrılan fonksiyonun prototip bildirimi/çağırma biçimi aynı olacak biçimde yapılmalıdır.


Assembly ile Sistem Fonksiyonu Çağırma [Linux]

06:50 , , , ,


Assembly programı içinden Linux sistem fonksiyonları çağrılırken uyulması gereken standart işlemler vardır. Linux System Call Table 'da görülebileceği gibi hangi register'ları set etmemiz gerektiği, parametreleri hangi register aracılığıyla göndermemiz gerektiği bilinmesi gerekmektedir. 80h kesmesinden sonra hangi fonksiyonun çağrılacağı bu registerlardaki değerler üzerinden belirlenmektedir.


Aşağıdaki örnekte ekrana "Hello world" yazan programı yazacağız:


Yukarıda görüldüğü gibi ;

  • eax'e hangi sistem fonksiyonu yazılacaksa onun numarası 
  • ebx'e birinci parametre olan file descripter değeri -stdout için 1- 
  • ecx'e yazılacak string'in adresi
  • edx'e ise  kaç karakter yazılacak ise onun uzunluğu girilmelidir.



Gerekli çağırma parametreleri girildikten sonra 80h kesmesiyle kernel mod'a geçiş yapar ve sistem fonksiyonları çağrılır.



Yukarıdaki kod derlenip,link edilip çalıştırıldığında ekrana Hello World yazısı çıkacaktır. Ekrana yazma işini sys_write sistem fonksiyonu ile stdout'a yazarak gerçekleştirilmiştir.

Assembly içinden C Fonksiyonu Çağırma [Linux]

03:23 , , , ,


Linux'te assembly içinden C fonksiyonu çağrılacak ise yapılması gereken temel işlemler aşağıdaki örnekte anlatıldığı gibidir:

  • Çağrılacak fonksiyon .text alanında extern bildirimi yapılması gerekmektedir. 
  • Fonksiyon çağrılmadan önce ise parametreler soldan sağa stack'e push edilmesi gerekmektedir.



sample.asm isimli dosya içine yukarıdaki gibi kod yazıldıktan sonra :
  • nasm -f elf32 sample.asm ile object dosya oluşturulur
  • gcc -m32 -o sample sample.o ardından gcc ile çalıştırılabilir dosya oluşturulur.



  • gcc -m32 veya -m64 flaglerini kullandığımızda hata veriyorsa gcc için kurulum yapılması gerekmektedir.




  • apt-get install gcc-multilib kurulumu ile 32 - 64 bit derleme işlemlerini gerçekleştirebiliriz.




sample isimli çalıştırılabilir dosyamız oluşturulmuş oldu. İleride gcc'yi de devre dışı bırakacağız ve doğrudan linker ile elf dosyamızı oluşturacağız.

Assembly' de yazılıp derlenen bir fonksiyonu Windows'ta çağırma

Windows sistemlerde assembly' de yazdığımız bir fonksiyon C içerisinden aşağıdaki gibi çağrılır;


Derleme işlemi yapıldıktan sonra oluşan .obj dosyasını Visual Studio'da açılan C projesine dahil edelim.


 Aşağıdaki gibi örnek bir kod derleme&link işleminin ardından çalıştırıldığında hatasız bir şekilde çalıştığı gözükecektir.


Kısaca fonksiyonun ne yaptığını anlatırsak:
  1. Parametreler stack'e sağdan sola doğru aktarılmış yani fonksiyonla gönderilen iki parametre stack'in üstüne sırasıyla konulmuş.
  2. EBP değeri stack'e konulmuş /*geri dönüş yapıldığında akışın sorunsuz devam etmesi için*/ 
  3. ESP değeri EBP değerine atanmış
  4. EDX'e sağdan ilk parametrenin adres değeri atanmış
  5. EDX'in içinde bir adres bulunmakta bu adresin gösterdiği değer EAX'e atanmış. ( Yani ilk parametrenin değeri EAX'e atanmış )
  6. EAX içindeki değer EBP-4'e konulmuş. ( birinci parametre'nin gösterdiği adresteki değer artık burada )
  7. İkinci parametrenin adresi ECX'e atanmış
  8. İkinci parametrenin değeri EAX'e atanmış
  9. İkinci parametre' nin değeri EDX (edx'in içinde 1. parametrenin adresi var) aracılığıyla 1. parametrenin gösterdiği yere atanmış. (ilk değişim gerçekleşti)
  10. Stack'te saklanan birinci parametre'nin değeri de [ EBP-4 ]  EAX yazmacına atandı. 
  11. ECX yazmacında ikinci parametrenin adresi olduğuna göre şimdi de EAX (birinci parametrenin değeri bunun içindeydi) yazmacındaki değer ikinci parametrenin gösterdiği yere aktarılmış oldu.
  12. EBP'nin değeri de ESP'ye aktarıldı. 
  13. Ardından stack'te tutulan eski ebp değeri de pop ile geri alınmış oldu.  /*geri dönüş yaptığında akışın sorunsuz devam etmesi için*/


Not1: Yukarıdaki işlemlerden anlayacağınız üzere stack'i düzenleme işi çağrılan (callee) fonksiyon tarafından gerçekleştirilmiştir. Fonksiyon çağrılarında stack düzenlemesi önemli bir konudur ve ileride daha detaylı anlatılacaktır.

Not2: Bu swap işlemi daha basit şekilde yazılabilmektedir. Bu yazıda en kısa biçimde kod yazılmaya çalışılmamıştır.