C kodunun derleyici uyumluluk sorunlarını ve portabilite zorluklarını gösteren karmaşık bir dijital illüstrasyon.

C Uzantıları, Portabilite ve Derleyici Çözümleri: Geliştirici Notları

C Uzantıları, Portabilite ve Alternatif Derleyiciler: Gerçek Dünyanın Zorlukları

C programlama dilinde tam ISO C standardına uygun kod yazmak, gerçek dünyada nadir rastlanan bir durumdur. Çoğu C kodu, derleyiciler ve kütüphanelerdeki hatalar veya eksiklikler nedeniyle standart dışı davranışlara ve dil uzantılarına güvenir. Kod tabanları genellikle önişlemci kontrolleri kullanarak farklı ortamları desteklemeye çalışsa da, bu girişimler en iyi ihtimalle geçici, en kötü ihtimalle tamamen bozuk olabilmektedir.

Kendi C derleyicisini geliştiren bir geliştiricinin gözünden, bu uyumluluk sorunlarının bazıları aşağıda detaylandırılmıştır.

libc Kütüphanelerinin Karmaşıklığı

glibc

Bir C derleyicisi için ilk engel, sistemin C kütüphanesi başlık dosyalarıdır. GNU/Linux sistemlerinde bu, glibc anlamına gelir. glibc, başlık dosyalarının GCC dışındaki derleyicilerle uyumluluğunu korumaya çalışsa da, bu çabalar bazen yetersiz kalır. Örneğin, ‘sys/cdefs.h’ dosyasındaki karmaşık önişlemci kontrolleri, derleyicilerin önceden tanımlanmış makrolarını kullanarak uzantı desteğini belirler. Ancak bu mekanizma bazen bozuktur:

  • ‘sys/epoll.h’ dosyasındaki ‘struct epoll_event’, GNU’nun ‘__attribute__((packed))’ özelliğini kullanır. Bu, struct düzenini değiştirdiği için göz ardı edilemez. Ancak ‘sys/cdefs.h’ içindeki bir kontrol, GCC, Clang veya TCC dışındaki derleyiciler için ‘__attribute__(xyz)’ özelliğini göz ardı eder, bu da uyumluluk sorunlarına yol açar.
  • Bazı C başlık dosyaları (örneğin ‘stddef.h’, ‘stdint.h’, ‘limits.h’) derleyici tarafından sağlanmalıdır. Ancak POSIX, ‘limits.h’ dosyasının standart C sabitlerine ek olarak POSIX’e özgü sabitler tanımlamasını gerektirir, bu da platforma özgü ek bir ‘limits.h’ ihtiyacı doğurur. glibc’nin ‘limits.h’ dosyası, gcc’ye özgü ‘#include_next’ uzantısını ve GCC’nin dahili ‘limits.h’ dosyasını kullanarak çalışır, bu da diğer derleyiciler için uyumluluk sorunları yaratır.

SDL

SDL_endian.h, bayt değiştirme işlevleri için sıra dışı bir özellik algılama mantığına sahiptir. Amacı, derleyici dahili işlevlerini veya satır içi assembly’yi kullanmak, son çare olarak genel bit düzeyinde işlemlere geri dönmektir. Ancak bu, şu mantıkla çalışır:

  1. Eğer GCC veya Clang ise ve ‘__has_builtin(__builtin_bswapX)’ destekleniyorsa, dahili işlevler kullanılır.
  2. Aksi takdirde, MSVC belirli bir sürümden büyükse, MSVC içsel ‘pragma’ kullanılır.
  3. Aksi takdirde, ISA’ya özgü makrolar (örneğin ‘__x86_64__’) tanımlıysa, satır içi assembly kullanılır.
  4. Aksi takdirde, genel bit düzeyinde işlemler kullanılır.

Bu durum, GCC veya Clang olmayan bir derleyicinin bswap dahili işlevlerini ve ‘__has_builtin’ operatörünü desteklemesine rağmen, ISA’ya özgü makro tanımlaması nedeniyle GCC tarzı genişletilmiş satır içi assembly kullanmaya çalışmasına neden olabilir.

OpenBSD libc

Bazı OpenBSD başlıkları, derleyici tarafından optimize edilirken isteğe bağlı olarak kullanılmak üzere ‘inline’ fonksiyon tanımlamaları içerir. Bunlar ‘__only_inline’ makrosu ile tanımlanır ve derleyici tarafından satır içi yapılmazsa ‘gerçek’ harici sembole geri dönmesi beklenir. Ancak C99 standardı ve GCC’nin C99 öncesi davranışı arasındaki ‘inline’ semantik çatışması nedeniyle bu durum sorunludur. OpenBSD, eski GNU89 ‘inline’ semantiğini belirtmek için yeni GCC sürümlerinde açık bir ‘__attribute__’ kullanır. Ancak GNU olmayan derleyicilerde, ‘__only_inline’ makrosu ‘static’ bağlantı olarak tanımlanır, bu da çakışan bağlantıya sahip fonksiyon tanımlarına yol açar ve bozulmalara neden olur. Neyse ki, ‘_ANSI_LIBRARY’ makrosu tanımlandığında, bu sorunlu ‘__only_inline’ tanımlamaları atlanır.

bionic (Android’in libc’si)

Android’in libc’si olan bionic, başlık dosyalarında GCC yerine Clang’ı yoğun bir şekilde varsayar. Null kontrolü için ‘_Nonnull’ ve ‘_Null_unspecified’ gibi Clang’a özgü uzantılarla doludur. Bunlar genellikle komut satırı bayraklarıyla kolayca ‘#define’ edilerek devre dışı bırakılabilir.

Çözüm Yolları: Derleyici Geliştiricileri İçin Stratejiler

Bir derleyici yazan birinin bakış açısından, olası çözümler şunlardır:

  • Bu uyumsuzlukları ana akımda yamalamaya çalışmak.
  • Geliştiricilerin özel ‘#ifdef’ kontrolleri eklemesini ve derleyicinizde varsayılan olarak test etmesini sağlayacak kadar popülerlik kazanmak.
  • Bu sorunlarla ‘aşağı akımda’ başa çıkmak, belki de yamalarınızı dağıtmak.
  • GCC’nin (belirli bir sürümünü) taklit etmek ve uzantılarını uygulamak.

İlk strateji genellikle zorlu bir mücadele, üçüncü strateji en kolay olanıdır. Dördüncü strateji ise, derleyicinizin kullanıcıları ve kod tabanlarının geliştiricileri için minimum kesintiyle çok sayıda kod tabanını desteklemek için gerçekçi (ancak zorlu) bir yoldur. Örneğin, Clang, GCC 4.2.1 ile uyumluluğu iddia etmek için ‘__GNUC__=4’ (ve ‘__GNUC_MINOR__=2’, ‘__GNUC_PATCHLEVEL__=1’) tanımlar. Ancak bu stratejinin bir sorunu, birçok kod tabanının ‘#ifdef __GNUC__’ kontrolü yapıp sürüm kontrolleri olmadan daha yeni GCC uzantılarını serbestçe kullanmasıdır, bu da bir ‘yakalama’ oyununa yol açar.

İdeal olarak, derleyiciye özgü korumalar ve sürüm kontrolleri yerine ‘__has_builtin’, ‘__has_feature’, ‘__has_attribute’ ve hatta ‘__STDC_NO_VLA__’ gibi standart özellik test makroları daha yaygın olarak kullanılmalıdır. Şimdilik, *NIX dünyasında GCC/Clang yarı-düopolisi, iyi ya da kötü, mevcut durumu temsil etmektedir. Bağımsız C derleyicilerinin geliştiricilerini tebrik etmek gerekir: TCC, cproc, scc, vbcc, nwcc, kefir ve diğerleri.

Comments

No comments yet. Why don’t you start the discussion?

    Bir yanıt yazın

    E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir