1 puan yazan GN⁺ 2024-09-30 | 1 yorum | WhatsApp'ta paylaş
  • 32 bit glibc sistemlerinde 2038 sonrasında geçerli zamanı sorgulama ya da stat() çağrıları başarısız olabilir; bu nedenle Gentoo’nun 64 bit time_t’ye güvenli biçimde geçiş için bir yola ihtiyacı var
  • glibc’nin time64 desteği Large File Support (LFS) ile birlikte kullanılmalı; 32 bit ortamda mevcut ABI, LFS ABI’si ve LFS+time64 ABI’si birlikte var olur
  • time_t API’lerde, yapılarda ve fonksiyon argümanlarında yer aldığında tür genişliğindeki değişim ABI kırılmasına yol açar; time32 ve time64 ikililerinin karıştırılması çalışma zamanında hatalı davranışlara ve güvenlik risklerine neden olur
  • Kaynak tabanlı bir dağıtım olan Gentoo’da @world yeniden derlemesi sırasında hatalar veya döngüsel bağımlılıklar yüzünden yarı geçiş yapılmış sistemler kalabilir
  • 2024-09-30 düzeltmesinden sonra yalnızca libdir geçişinin yeterli olmadığı anlaşıldı; dinamik yükleyici ve çeşitli dil araç zincirlerini de kapsayan time64 işareti temel kısıt olarak kaldı

2038 problemi ve time64 geçişinin kapsamı

  • 32 bit time_t kullanan 32 bit uygulamalar, 2038’de geçerli zaman yerine -1 hatası alabilir veya dosyalar üzerinde stat() çalıştıramayabilir
  • Geçişin temel yönü time_t’yi 64 bit türe dönüştürmektir
    • musl bu geçişi zaten yaptı
    • glibc bunu seçenek olarak destekliyor
    • Debian gibi bazı dağıtımlar geçişi zaten yaptı
  • Gentoo gibi kaynak tabanlı dağıtımlarda kullanıcı sistemi kendisi yeniden derlediği için paketlerin farklı ABI durumlarında kaldığı süreyi azaltmak gerekir
  • Temel risk, time_t genişliğinin değişmesinin ABI’yi bozmasıdır
    • Bir kütüphane API’sinde time_t varsa, o kütüphaneye bağlanan tüm kodların aynı tür genişliğini kullanması gerekir
    • Yalnızca bazı parçaları time64’e geçirmek güvenli değildir

LFS ve üç 32 bit alt ABI

  • 32 bit mimarilerde dosyayla ilgili tür genişliği sorunları eskiden beri vardı
    • off_t dosya ofsetleri için kullanılır
    • ino_t inode numaraları için kullanılır
    • Başlangıçta 32 bit genişliğinde olduklarından 2 GiB’den büyük dosyalar veya 32 bit aralığını aşan inode numaraları sorun yaratıyordu
  • Bunu çözmek için Large File Support (LFS) kullanıma alındı
    • off_t ve ino_t’yi 64 bit varyantlara çevirir
    • glibc’de bugün bile isteğe bağlıdır
    • Birçok paket upstream’de LFS’yi etkinleştirip ABI kırılmasını ele aldı, ancak sorun tamamen çözülmüş değildir
  • glibc’nin time64 desteği LFS kullanımını gerektirdiği için dosya boyutu ve zaman sorunlarını birlikte çözen bir yapı oluşur
  • 32 bit sistemlerde üç alt ABI bulunur
    • Mevcut ABI: 32 bit türler
    • LFS: 64 bit off_t, 64 bit ino_t, 32 bit time_t
    • time64: LFS + 64 bit time_t
  • Tek bir glibc derlemesi üç varyantla uyumludur, ancak API’lerinde bu türleri kullanan kütüphanelerde üç varyant birbirine karıştırılamaz

ABI değişikliğinin pratikte nasıl kırıldığı

  • time_t 32 bitten 64 bite geçtiğinde yapı yerleşimi değişir
    • Örnek bir yapıda int a, time_t b, int c sırasıyla yer aldığında, c’nin ofseti 32 bit time_t ile 64 bit time_t arasında farklı olur
    • time32 ve time64 ikilileri karıştırılırsa yanlış alanlar okunup yazılır; sınır dışı erişimler de mümkün hale gelir
  • struct stat boyutu da ABI’ye göre değişir
    • 32 bit x86 glibc varsayılanı: 88 bayt
    • LFS: 96 bayt
    • LFS + time64: 108 bayt
  • Yapı kullanılmasa bile fonksiyon argümanlarında sorun çıkar
    • x86’da fonksiyon argümanları stack üzerinden iletilir
    • Argümanlardan biri time_t ise onu izleyen argümanların stack konumu değişir
  • Örnek deneyde, time32 programı time64 olarak yeniden derlenmiş bir kütüphaneye bağlandığında değerler bozulur
    • Normal çıktı a = 1, geçerli bir zaman değeri ve c = 3 olur
    • Yalnızca kütüphane -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ile yeniden derlenirse b ve c yanlış yorumlanır
  • Şu anda bu tür ABI karışımlarını engelleyen pratik bir koruma olmadığı için çalışma zamanı kırılmaları ve güvenlik sorunları ortaya çıkabilir

Gentoo geçişinin daha zor olmasının nedeni

  • İkili dağıtımlar tüm paketleri yeniden derledikten sonra kullanıcı görece atomik bir adımla yükseltme yapar
    • Üçüncü taraf depolar veya yerel olarak derlenmiş programlar varsa sorun çıkabilir, ancak genel süreç nispeten güvenlidir
  • Gentoo, @world’ü yeniden derlerken ABI’yi yerinde değiştirmek zorundadır
    • İki paket ayrı ayrı yeniden derlenirken arada uyumsuz ABI’ler karışabilir
    • Bazı yeniden derlemelerin başarısız olması sistemi yarı geçiş yapılmış durumda bırakabilir
    • Döngüsel bağımlılıklar yüzünden bağımlı paketler yeniden derlendiğinde derleme araçları bozulabilir ve sonraki yeniden derlemeler imkânsız hale gelebilir

İncelenen hafifletme önlemleri

  • Tartışılan yön üç başlıkta toplanıyor
    • Yeni ABI’yi mevcut 32 bit ABI’den ayırmak için platform tuple’ı olan CHOST’u değiştirmek
    • Yeni ABI’nin libdir’ini değiştirerek yeniden derlenmiş kütüphaneleri mevcut kütüphanelerden ayrı kurmak
    • Farklı alt ABI’lerin ikililerinin birbirine bağlanmasını engellemek için ikili düzeyde ABI ayrımı getirmek
  • Üç yöntem belli ölçüde bağımsız olarak uygulanabilir, ancak bazıları birbirine bağlı olabilir
  • Yazıdaki örnek dizgeler nihai çözümün gerçek dizgeleri olmayabilir

CHOST ile ABI ayrımı yapmak

  • Platform tuple’ı, araç zincirinin hedeflediği platformu tanımlar; Gentoo’da multilib desteği için ABI’leri benzersiz biçimde ayırt etmekte de kullanılır
  • Tuple dört bölümden oluşur: mimari, vendor, işletim sistemi ve libc
    • Örnek: i386-pc-linux-gnu
    • Örnek: i686-pc-linux-gnu
    • Örnek: i686-unknown-linux-gnu
  • Yeni ABI getirilirken vendor alanını değiştirmek veya libc alanına ABI gösterimi eklemek gibi yöntemler kullanılmıştı
    • ARM hardfloat ABI’sinde geçmişte armv7a-hardfloat-linux-gnueabi ve armv7a-unknown-linux-gnueabihf gibi biçimler kullanıldı
  • time64 ABI’si için de benzer seçenekler var
    • i686-gentoo_t64-linux-gnu
    • i686-pc-linux-gnut64
    • armv7a-gentoo_t64-linux-gnueabihf
    • armv7a-unknown-linux-gnueabihft64
  • Tuple değişikliğinin çok sayıda yama gerektirmeyeceği düşünülüyor
    • GNU toolchain ve GNU build system, libc alanında gnu sonrasındaki içeriği yok sayar
    • Clang’in tuple’a göre doğru ABI’yi otomatik seçmesi için yama gerekir

libdir değişikliği ve preserved-libs

  • libdir, kütüphane kurulum dizininin varsayılan adıdır
    • Yaygın varsayılan değer libtir
    • 64 bit karşılığı olan mimarilerde geleneksel olarak çoğunlukla lib64 kullanılır
    • x86’ın x32 ABI’si libx32, MIPS n32 ABI’si ise lib32 kullanır
  • time64 için 32 bit ABI’de libdir’i lib yerine libt64 gibi bir değere değiştirme seçeneği değerlendiriliyor
  • Ayrı bir libdir, geçiş sırasında ABI karışımını azaltan bir mekanizma olur
    • time64 çalıştırılabilir dosyalarının yanlışlıkla time32 kütüphanelerine bağlanma riskini azaltır
    • Portage’ın preserved-libs özelliğiyle time32 kütüphaneleri korunabilir
    • İsteğe bağlı olarak time32 + time64 multilib profili sunulup mevcut time32 önceden derlenmiş uygulamalarla uyumluluk korunabilir
  • preserved-libs varsa mevcut çalıştırılabilir dosyalar yeniden derlenene kadar time32 kütüphanelerini kullanmaya devam eder; time64 olarak yeniden derlenen kütüphaneler ise yeni libdir’e kurulur
  • libdir değişikliği araç zinciri yamaları gerektirir
    • glibc, aynı kütüphane kümesi birden fazla alt ABI için geçerli olduğundan özel biçimde ele alınabilir
    • time64 çalıştırılabilir dosyalarının .interp alanının time64’e özel ld.soya işaret etmesi için ayrı bir ld.so gerekebilir
  • Düzgün multilib desteği için bu ABI’ye özgü bir platform tuple’ı da gerekir

İkili düzeyde uyumsuzluk işareti

  • Farklı ABI’lere ait ikililerin karıştırılmasını genellikle linker veya dinamik yükleyici engellemelidir
    • 64 bit bir programı 32 bit kütüphaneye bağlamaya çalışınca linker file in wrong format diyerek reddeder
    • Dinamik yükleyici de wrong ELF class: ELFCLASS32 gibi bir hatayla reddeder
  • Mevcut ABI ayrımlarında çeşitli mekanizmalar kullanılır
    • ELFCLASS32 ve ELFCLASS64
    • EM_386 ve EM_X86_64 gibi machine identifier’lar
    • ARM ve MIPS’te flags alanı
    • Mimariye özgü attribute section
  • time32 ve time64 için de benzer bir mekanizma gerekir, ancak bu basit değildir
    • Yeniden kullanılabilir genel bir mekanizma yok gibi görünüyor
    • Birden fazla mimariye uygun bir çözüm gerekir
    • Yeni bir ELF note section ekleyip araç zinciri desteği uygulamak gerçekçi bir aday gibi görünüyor
  • Kullanıcının koruma mekanizmasını devre dışı bırakma olasılığı da dikkate alınmalı
    • Kaynağı olmayan önceden derlenmiş yazılımlar, time_t kullanan API’leri çağırmıyorsa sistem kütüphaneleriyle çalışmaya devam edebilir
    • Bunu koşulsuz engelleyen bir reçete sorunun kendisinden daha kötü olabilir
  • Ayrı libdir kullanılırsa nispeten basit, ölümcül olmayan bir QA denetimi oluşturulabilir
    • .interp ile time64 çalıştırılabilir dosyaları ayırt edilir
    • time32 programlarının libt64 içinden kütüphane yüklemediği kontrol edilir
    • time64 programlarının lib içinden doğrudan kütüphane yüklemediği kontrol edilir

Önceden derlenmiş 32 bit uygulamaların sınırları

  • Kaynaktan derlenen paketlerden ayrı olarak x86 ve PowerPC’de yalnızca eski önceden derlenmiş ikilileri sunulan uygulamalar vardır
    • Özellikle tescilli yazılımlar ve eski oyunlar buna dahildir
  • Bunlar hem sistem kütüphaneleriyle uyumluluk sorunlarını hem de 2038 probleminin kendisini yaşar
  • Uyumluluk sorunları için mevcut multilib yapısı bir ölçüde çözüm sunar
    • amd64’te 32 bit yazılımları desteklemek için multilib yerleşimi ve birden fazla kütüphane sürümü derleme mekanizmaları zaten var
    • abi_x86_32 ile abi_x86_t64 ayrımı yapılacak şekilde genişletilebilir
    • Her iki ABI’yi de destekleyen yeni bir multilib x86 profili oluşturulabilir
  • 2038 sonrasında 32 bit programların kendilerinin başarısız olması daha zor bir sorun olarak kalır
    • faketime ile sistem zamanını kontrol etme yöntemi kullanılabilir
    • Zamanı geçmişe alınmış bir VM çalıştırma yöntemi de mümkündür

2024-09-30 düzeltmesi: yalnızca libdir yeterli değil

  • İlk taslak fazla iyimserdi; yalnızca libdir değişikliğiyle güvenilir bir ayrım yapmak zor
  • Tüm libdir’ler ld.so.conf içinde listelendiğinden, ld.so içine libdir yolunu hard-code etme yöntemine güvenilemez
    • Özel LLVM prefix’lerinde de yollar zaten ayarlanıyor; bu durumda da özel işlem gerekir
  • Bu nedenle libdir değişikliğinin ikili uyumsuzluk ayrımına dayanması gerekme olasılığı arttı
  • Karşılanması gereken üç temel hedef var
    • Dinamik yükleyici time32 ve time64 ikililerini ayırt etmeli
    • Açık time64 işareti olmayan tüm ikililer geriye uyumluluk için time32 sayılmalı
    • Yeni derlenen tüm ikililer açık time64 işaretine sahip olmalı; buna Rust gibi C dışı ortamlarda derlenen ikililer de dahil
  • Bu hedef, birden fazla dilin birden fazla araç zincirini yamamayı gerektirecek düzeyde bir iş
    • Gentoo’nun bunu yalnızca yerel olarak sürdürmesi zor; birden fazla tarafın işbirliği gerekiyor
    • Hedef mimariler genellikle legacy kabul ediliyor veya artık yeterince desteklenmiyor olabiliyor
  • Diğer araç zincirlerinin doğru time64 çalıştırılabilir dosyaları üretip üretmeyeceği de ayrı bir sorun
    • C programları gibi _TIME_BITS’e uyacak şekilde ayarlanmazsa belirli bir time_t genişliğini hard-code edip bozulabilir
  • Açık time64 işareti olmayan tüm ikililer time32 kütüphanelerini kullanacağından Gentoo, doğru işareti ekleyecek şekilde yamalanmamış üçüncü taraf çalıştırılabilir dosyaları çalıştıramaz hale gelir
  • Daha düşük bir hedef belirleyen alternatifler de değerlendiriliyor
    • Tüm time64 çalıştırılabilir dosyalarına RPATH enjekte ederek time64 libdir’ini doğrudan zorunlu kılmak
    • Bu yöntem dinamik yükleyicinin time32 kütüphanelerini kullanmasını tamamen engellemez, ancak büyük uyumluluk sorunu yaratmadan geçişe yardımcı olabilir
  • Buna karşılık time64 libdir’ini kalıcı olarak değiştirmeyip time32 libdir’ini geçici olarak değiştirme seçeneği de var
    • Mevcut programlara RPATH enjekte edilip libdir adı değiştirilir
    • Yeni time64 kütüphaneler mevcut libdir’e kurulur
    • Yeni time64 programlar time32 kütüphanelerini zorlayan RPATH’e sahip olmaz
    • Geçişi zaten yapmış diğer dağıtımlarla uyumluluğu koruma avantajı vardır

Kalan işler

  • Üç çözümün tamamı uygulanırsa glibc kullanan 32 bit Gentoo sistemleri için daha temiz ve nispeten güvenli bir geçiş yolu sunulabilir
  • Ancak bu çözümler ağırlıklı olarak kaynaktan derlenen paketlere uygulanır
  • Önceden derlenmiş 32 bit uygulamalarda ABI uyumluluğu korunsa bile 2038 problemi kalır
  • Genel tasarım hâlâ taslak aşamasındadır; deneyler, tartışmalar ve yama gönderimleri doğrultusunda değişmeye devam edebilir

1 yorum

 
GN⁺ 2024-09-30
Hacker News görüşleri
  • Gentoo’da yazıda ele alınmayan birkaç seçenek daha var; muhtemelen Gentoo’nun sistem tasarımı gereği iş yükü büyük olduğu için dışarıda kalmış

    1. Bir paketi kurmadan, o pakete karşı derleme yapabilmek. Kilit nokta, Gentoo’da paket derleme ve kurulumun tek bir adım olması; bu yüzden birbirine bağımlı birden fazla öğeyi önce derleyip çıktıları atomik olarak kurmak mümkün değil. ABI değişimi içeren güncellemeler sırasında sistem kısmen bozulmaya çok açık oluyor
    2. Normal .so sürümlemeyi genişletip bağımlı paketlerin ABI değişimlerini de yansıtmak. Normalde paylaşımlı kütüphaneler libfoo.so.1.0.0 gibi dosya adı ve iç sürümde sürüm numarası taşır ve paketler kendi ABI kırılmalarını takip eder. 64 bit time_t desteği için, her .soya bağımlı ABI’nin kontrol ettiği bir sürüm bileşeni eklemek gerekir. Sonuç, yazıdaki “farklı libdir kullanımı”na benzer olurdu; ancak gelecekteki ABI değişikliklerinde de yeniden kullanılabilecek bir temel sağlayabilir, bunun karşılığında çok daha istilacı olma ihtimali yüksek
    • “Kurulum yapmadan derleme” için kısmi aşamalı güncelleme en uygun seçenek gibi görünüyor
      Birden fazla yeni paket derlemesini sıraya alıp sandbox içinde derleyebilir, sonra yeni derlemelerin union üzerinden önce sandbox’a bakıp sistemin kendisine fallback yapmasını sağlayabilirsiniz. Her şey derlendikten sonra sonuçlar paketlenip sandbox’tan gerçek sisteme taşınabilir. Bu, Gentoo güncellemesinin tamamını işlemsel hâle getirir ve başka açılardan da büyük faydalar sağlar
    • Gentoo, tamamlanmış imajların son kurulacağı dizini belirleyerek zaten 1. seçeneği destekliyor. Normalde / olarak ayarlanan ROOT değerini değiştirmeniz yeterli
      Tüm @system ve @world yeniden derlenip belirtilen alt dizine kurulabilir, ardından tek seferde eşzamanlanabilir. Mümkünse bunu bir live oturumda yapmak daha iyi; teoride yeni kurulan konumdaki bir alt dizine / bind mount edilip chroot’a girildikten sonra gerçek üst / ile eşzamanlama da yapılabilir
      https://devmanual.gentoo.org/ebuild-writing/variables/#root
    • Gentoo, tüm bağımlılıklar güncellenene kadar eski kütüphaneleri zaten koruyor; dolayısıyla ABI değişimini, aslında ABI değişikliklerinin kaydedilmesi gereken yer olan mimari ve SONAME içine kodlamak bu sorunu çözebilir
  • Mac OS X’in off_t ve ino_tyi ele alış biçimi bir ipucu olabilir. Eski çağrılar ve yapılar çalışmaya devam etti, 64 son ekli yeni çağrılar ve türler eklendi ve önişlemci makrolarıyla gerçekte neye başvurulacağı seçilebiliyordu; ama bunu doğrudan yazmak nadirdi
    Bunun yerine OS ve SDK sürümleniyordu ve derleme sırasında ikilinin çalışması gereken en eski OS sürümü belirtilebiliyordu. Header’lar buna göre uygun makroları otomatik seçiyordu; yeni API / kullanımdan kaldırılan API notları da aynı mekanizmayla weak link veya uyarı üretiyordu. Başta bu önişlemciyle yapıldı ama artık derleyici Apple’ın dediği API kullanılabilirliğini daha ayrıntılı anladığı için, aynı yaklaşım başka platformlarda da mümkün görünüyor

    • TFA’nın anlattığı temel sorunu çözmüyor. Farklı “derleme hedefi OS sürümü” kullanan uygulamalar artık birbirine linklenemez hâle geliyor
      OS v.B üzerinde çalışıyor olsa bile, OS v.B hedeflenerek bildirilen uygulama X, OS v.A hedeflenerek bildirilen uygulama Y ile linklenemeyebilir. Aslında bu yaklaşım, neredeyse tüm platformların zaten yaptığı şeye oldukça yakın; başka türlü yapılırsa mevcut ikili uyumluluk anında bozulur
    • glibc dışındaki yardımcı kütüphaneler, off_t boyutuna göre birden fazla fonksiyon kümesi tanımlamayacaktır; ayrıca istemci programın istediği tür boyutuna göre doğru fonksiyon kümesini header’da şeffaf biçimde seçecek bir anahtar da olmayacaktır
      Buna rağmen yazı, off_tye kıyasla time_tnin daha büyük bir sorun olduğunu vurguluyor. Makul açıklama şu: time_t çok daha yaygın kullanılıyor. off_t, görece az sayıda arayüzde yer alan bir POSIX türüyken time_t ISO C’nin parçası ve her yerde kullanılıyor. Üstelik pek çok C kodu time_tnin int ile aynı genişlikte bir tamsayı türü olduğunu varsayıyor; off_t için böyle varsayımlar daha seyrek
    • Zarif bir çözüm gibi geliyor ama pratikte korkunç bir hack gibi okunuyor. Türsüz makrolar, bir daha asla uğraşmak istemediğim bir kâbus
    • Yalnızca tüm platformu buna uymaya zorlayabildiğinizde işe yarıyor. İyi bir çözüm ama C kütüphanesini kontrol etmeniz gerekiyor. Gentoo libc’nin ne yaptığını kontrol edemiyor; kullanıcı ister GNU libc ister musl ya da başka bir şey kullanabilir
  • Debian için de çok sancılıydı. Hatta bazıları tükenmiş olabilir ve birçok kişi kaynak tabanlı dağıtımları gösterip “orada çok kolay olurdu” dedi

    • Debian’ın time64 geçişinin ne kadar sancılı geçtiğini ayrıntılı gösteren bir kaynak merak ediyorum. Dışarıdan bakınca görece tartışmasız ve pürüzsüz görünüyordu; örneğin /usr birleştirmesinden çok daha iyi duruyordu
    • m68k, powerpc, sh4 geçişlerini yaptım ve hppa’ya da kısmen yardım ettim; diğer Debian geliştiricilerinin yardımıyla hâlâ hayattayım
    • Eğer “kolay”dan kasıt “kullanıcıya her şeyi tek seferde yeniden derlemesini söylemek” ise, o zaman doğru olabilir
  • İnsanların bu tür sorunlarla uğraştığını her gördüğümde, FreeBSD'nin ilk amd64 portu sırasında bu işi zorla ilerletmiş olmamızın gerçekten ne kadar iyi olduğunu düşünüyorum. ABI'nin temel türlerini belirleyebiliyorduk ve geçmişten çok geleceği düşünmeye karar vermiştik
    amd64'te bu işi kolaylaştıran ilginç bir özellik vardı. Fonksiyon çağrıları sırasında 32 bit fonksiyon argümanlarının otomatik olarak 64 bite cast edilmesi sayesinde, 64 bit time_t bekleyen bir fonksiyona 32 bit zaman tamsayısı geçirseniz bile platformun ilk dönem çalışmalarında çoğu şey yine de çalışıyordu. Bu yüzden küçük düzeltmeleri sonraya bırakabildik
    O dönemde başka 64 bit platformlar da vardı ama 64 bit time_t yoktu; FreeBSD/amd64 bu ailede yaklaşık 2003~2005 civarında ilk örnekti. Hatırladığım kadarıyla sparc64 de 64 bit time_t'ye geçti
    O zamanki en büyük sorun, tzcode'un 64 bit güvenli olmamasıydı. struct tm normalleştirme algoritması, time_t(2^62) için gün/ay/yıl değerlerini yinelemeli olarak hesaplamaya çalıştığı türden yozlaşmış bir duruma düşüyordu. tzcode'u büyük ölçüde değiştirmek yerine, kabaca 1900 öncesi ya da 10000 sonrası için başarısız olacak şekilde ele aldığımızı hatırlıyorum. Muhtemelen bu sorun uzun zaman önce upstream'de düzeltilmiştir
    Birkaç yıl boyunca, üçüncü taraf kodların dosya ya da ağ veri yapılarında int/long/time_t türlerini gelişigüzel kullanmasından kaynaklanan 32/64 bit zaman karışıklıklarını köstebek vurma oyunu gibi tek tek düzelttik, ama genel olarak bu büyük bir sorun olmadı. İlk günden 64 bit time_t kullanmış olmak çoğu sorunu en baştan önledi ve bunu en başta yapmak kolaydı. Linux, amd64/x86_64 portunu başlatırken aynı şeyi yapmak için büyük bir fırsatı kaçırdı
    Ek olarak, o dönemde 64 bit ino_t işini tamamlayamadık. 32 bit inode numaraları dosya sisteminin disk üstü yapılarında, UFS dizin yapılarında ve daha pek çok yerde görünüyordu. FreeBSD/amd64 daha alt seviye bir platformken, diğer tier-1 mimarileri ciddi biçimde sarsmadan bunu en baştan çözmenin gerçekçi bir yolu yoktu. Bu işe iki kez başladım ama sonunda başkası bitirdi; mountpoint yol uzunluğu gibi fazla kısa kalmış sabitler de bununla birlikte düzeltildi

    • Benim anladığım kadarıyla tüm 64 bit Linux portları baştan beri 64 bit time_t, off_t, ino_t kullanıyordu. Şimdiki sorun, 32 bit Linux'u 64 bit time_t'ye geçirmek
    • FreeBSD off_t konusunda da daha cesurdu ve 2.0'dan itibaren 64 bit yaptı. Linux'un 32 bit sürümlerinde eski boyutun izleri hâlâ duruyor
      32 bit fonksiyon argümanlarının çağrı sırasında otomatik olarak 64 bite cast edilmesi kısmının yalnızca işaretsiz argümanlarda geçerli olduğunu düşünüyorum. Çünkü %edi'ye yükleme yapıldığında %rdi'nin üst kısmı sıfırlanıyor. x86-64 için SysV ABI tanımı, register ya da stack üzerindeki tüm değerlerin tam 64 bit değere genişletileceğini söylemiyor; boole için yalnızca alt 1 baytın anlamlı olduğuna dair notlar da bunun genel kural olduğunu düşündürüyor
    • Eğer amd64 çıktığında FreeBSD i386'ün time_t türünü de 64 bite taşımış demekse bu oldukça şaşırtıcı. Motorola 68000 ya da sparc32 gibi başka 32 bit mimarilerin de 64 bit time_t'ye geçirilip geçirilmediğini merak ediyorum
  • Eski büyük bir 32 bit Unix sisteminde gelecekteki tarihleri işlemek için, işaretli 32 bit time_t libc fonksiyonlarını işaretsiz 32 bit time_t karşılıklarıyla değiştirmiştim. Bununla 2038'den sonra 68 yıl daha kazandık; o zamana kadar ben zaten hayatta olmam
    Dezavantajı, Unix epoch'u olan 1970'ten önceki tarihleri ifade edememesi ama bu bir takvim/scheduling sistemi olduğu için sorun değildi. Geçmiş tarihler önemliyse epoch'u birkaç on yıl kaydırabilir ya da zaman çözünürlüğünü 1 saniyeden 2 saniyeye düşürebilirsiniz. Her seçeneğin kendine özgü ince sorunları var; yani kullanım durumuna bağlı

    • Tüm sistemi işaretliden işaretsize çevirebiliyorsanız, neden 64 bit yapmadığınızı merak ediyorum
  • Aslında BSD man sayfalarında tunefs için “Bugs” bölümünde şu meşhur şaka vardı: “You can tune a file system, but you can't tune a fish.”, ama “Expert C Programming”e göre bu man sayfasının kaynak kodunda şakanın yanında şu yorum yer alıyormuş
    “Bunu çıkarırsan UNIX daemon'u şimdi ile time_t wrap around olana kadar dört adım arkandan gelecektir.”
    Bu cümle 70'lerde yazıldığında, 2038'in hayal bile edilemeyecek kadar uzak bir gelecek olduğu açık
    https://progforperf.github.io/Expert_C_Programming.pdf

  • Bende en baskın kalan düşünce şu oldu: emeğe saygı duyuyorum ama kullanıcı olarak bu işi bitirmek için Debian gibi kaynak tabanlı olmayan bir dağıtıma geçmek isterdim

    • Kaynak tabanlı dağıtımların zorluğu, ABI ile uyumsuz değişiklikler yapılırken yerinde yükseltme denenmesinden kaynaklanıyor gibi görünüyor. Bu yüzden tamamen farklı bir dağıtıma geçmek, yeni ABI kullanan Gentoo'yu temiz kurmak kadar, hatta belki daha az zaman alsa bile, en az onun kadar sarsıcı olabilir
    • Gentoo'da da bunu kolay halletmenin yolu var. USB benzeri bir şeyden boot edip, / ve /usr bölümlerinde mkfs.ext4 ya da kullandığınız dosya sistemi aracını çalıştırın, sonra bağlayın, stage3'ü açın, chroot'a girin ve emerge $all-my-packages-that-where-installed-before-mkfs çalıştırın
      Artımlı yükseltme yapmak yerine yeni bir Gentoo kopyası kurabilirsiniz
    • Kaynak tabanlı olmayan bir dağıtıma geçmenin çözüm olduğu ayrımı biraz daha incelikli. NixOS gibi kaynak tabanlı dağıtımlarda aynı sorun yok. Esas mesele, kaynaktan derleme yapılıp yapılmamasından çok Gentoo'nun paketleri derleme ve kurma biçiminde yatıyor
      Üçüncü taraf kapalı kaynak yazılım varsa, ikili sistemlerde de hâlâ sorun çıkabilir. Ayrı aşamalarda ayrıca kurulan birinci taraf paketlerde de sorun yaşanabilir
  • C uzmanı değilim ama off_t gibi tür takma adlarının, sonradan değiştirilebilsin diye getirildiğini sanıyordum. Ama açıkça öyle çalışmıyor gibi görünüyor; acaba ben mi yanlış biliyorum diye merak ediyorum

    • Bu, kaynak uyumluluğu ile ikili uyumluluk arasındaki fark. off_t gibi bir typedef kullanırsanız genelde kodu yeniden yazmanız gerekmez, ama o türü kullanan her şeyi yeniden derlemeniz gerekir
    • Bir dereceye kadar işe yarar, ama kaynak tabanlı dağıtımlarda pek uygun değildir. off_t tanımını değiştirdikten sonra @world'ü atomik olarak yeniden derleyebiliyorsanız sorun olmazdı, ancak kaynak tabanlı dağıtımlar @world'ü atomik olarak yeniden derlemez; paketleri tek tek yeniden derler
      Bu durumda libc.so 64 bit off_t kullanırken gcc 32 bit off_t temel alınarak derlenmiş olabilir ve gcc kilitlenebilir. bash, coreutils, make, binutils gibi @world yeniden derlemesi için gerekli paketler de bozulabilir ve o noktada süreç tıkanır. Bu yüzden böyle yükseltmeler dikkat gerektirir
    • Bu, bulmacanın ilk adımı, belki de yarım adımı sadece. Yazıda dendiği gibi, off_t bir yapının içine girdiği, fonksiyon çağrılarında kullanıldığı ya da bir protokole entegre edildiği anda soyutlama ortadan kalkar ve gerçek boyut önem kazanmaya başlar
      Kütüphane yüklerken ya da bir protokol üzerinden haberleşirken eski kodla yeni kodu karıştırırsanız ofsetler kayar ve çakışmalar başlar. Sonuçta bu geçiş, herkesin programları “legacy” ve “port edilmiş ya da en azından gözden geçirilmiş” olarak ayırmasını gerektirir; bu da oldukça sancılıdır
    • Tür takma adları bunu yalnızca kaynak kod düzeyinde daha kolay hâle getirir. Gerçek, hele hele tam bir soyutlama değildir. Örneğin iç türü kayan noktalı bir türe çevirirseniz anlam ciddi biçimde değişir ve kullanıcı koduna tamamen yansır
      Anlamı benzer daha büyük bir türe geçseniz bile bozulabilir. Basit bir örnek olarak yapı padding'i vardır; ayrıca işaretçiyi tamsayıya çevirip sonra geri döndürme kullanım örnekleri de çoktur, bu yüzden iç temsil değişirse bozulması kaçınılmazdır. Bunun iyi bir pratik olup olmaması ayrı konu, ama nadir görülen bir şey değildir. Kilit nokta ABI uyumluluğudur
    • Çalışır, ama ABI değişikliklerinde sorun, değiştirme anında her yerin aynı anda değiştirilmesi gerekmesidir. Temelde 32 bit off_t ile derlenmiş bir kütüphane ile 64 bit off_t ile derlenmiş bir kütüphanenin bağlanmasını engelleyen bir mekanizma yoktur ve ortaya çıkan davranış son derece öngörülemez olabilir
  • Örnek yapıda 32 bit time_t için c ofsetinin 8, 64 bit tür içinse 12 olduğu söylenmiş ama aslında 16 olması gerekmiyor mu diye düşünüyorum. b'nin 64 bit hizalanması gerektiğinden a ile b arasına padding girmeli. Hatta bu, yazarın anlatmak istediği noktayı daha da güçlendiriyor

    • x86 ABI'lerinin çoğu, o dönemde 64 bit load olmadığı için 64 bit türler için padding dayatmaz
  • Tüm bunlara bakınca, Windows'un tuhaf zaman gösteriminin — yani Gregoryen takvime göre 1 Ocak 1601 00:00 GMT'den itibaren 100 ns birimlerle sayan 64 bit yaklaşımının — küçük de olsa bazı avantajları var. Çözünürlüğü de harika ve tüm galaksi fethedilene kadar bile çalışmaya devam eder gibi görünüyor