- Elevator, debug bilgisi, kaynak kod veya ikili yerleşimi varsayımları olmadan x86-64 çalıştırılabilir dosyalarının tamamını statik olarak AArch64'e çevirir
- Kod/veri ayrımı için sezgisel yöntemler yerine, her baytın tüm olası yorumlarını içeren bir superset CFG oluşturur ve yalnızca sonlanma yollarını kaldırır
- x64 durumunu AArch64 yazmaçlarına bire bir eşler ve dolaylı dallanmaları, özgün adresten çevrilmiş koda giden bir arama tablosu ile işler
- Çevrimdışı tile bank, x64 komut anlambilimini C şablonları olarak yazar, ardından bunları LLVM 20 ile AArch64 bayt dizilerine derler
- Ortaya çıkan çıktı, çalışma anı çevirisi olmayan, kendi kendini içeren bir AArch64 ikilisidir ve SPECint 2006 üzerinde QEMU kullanıcı modu JIT ile aynı ya da daha iyi performans verir
Elevator'ın hedefi
- Elevator, x86-64 çalıştırılabilir dosyalarının tamamını AArch64'e taşıyan tam statik bir ikili çeviricidir
- Debug bilgisi, kaynak kod, özgün ikilinin kod kalıpları veya ikili yerleşimi hakkında varsayımlar kullanmaz
- Mevcut statik çeviriciler kod ile veriyi ayırmak için sezgisel yöntemlere veya çalışma anı geri dönüşlerine dayanırken, Elevator özgün çalıştırılabilir dosyanın tüm baytlarını olası her yorum için önceden çevirir
- Herhangi bir bayt veri, opcode'un bir parçası veya opcode bağımsız değişkeninin bir parçası olabileceğinden, mümkün tüm kontrol akışlarını içeren bir superset CFG oluşturur ve yalnızca istisnai program sonlandırmasına giden yolları kaldırır
- Çıktı; çevrilmiş kod, özgün x64 ikilisi, adres arama tablosu ve çalışma anı sürücüsünü içeren kendi kendini içeren bir AArch64 ikilisinden oluşur
- Çeviri tamamlandıktan sonra JIT veya çalışma anı çeviri desteği olmadan çalıştırılabilir
- Aynı girdi ikilisi iki kez çevrilirse aynı çıktı bit dizisi üretilir; test, doğrulama, sertifikasyon ve kriptografik imzalama için hedeflenen şey gerçek dağıtım koduyla aynıdır
- Başlıca maliyet kod boyutunun büyümesidir; bunun karşılığında emülatör veya JIT derleyicilere kıyasla dağıtımdan önce doğrulanabilirlik artar
- Değerlendirmede tam SPECint 2006 kıyaslaması ve elle hazırlanmış ikililer yer aldı; performans, JIT hızlandırmalı QEMU kullanıcı modu emülasyonu ile aynı ya da daha iyi düzeyde çıktı
- Araştırmacılar, proje tamamlandığında her şeyi açık kaynak olarak yayımlayacaklarını belirtiyor
Neden statik çeviri gerekli ve mevcut sınırlamalar neler
- Donanım bir ISA'dan başka bir ISA'ya geçtiğinde mevcut yazılımın yeni platforma taşınması gerekir ve elde kalan kaynak kodu yeniden derlemek tek başına yeterli olmayabilir
- Doğrulanmış veya sertifikalı eski kodlarda, kaynak kod değil, iyi test edilmiş belirli bir yetkili ikili çalıştırılabilir dosya çoğu zaman sertifikasyonun konusudur
- Daha sonra kaynaktan aynı ikiliyi bit düzeyinde yeniden üretmek için dönemin derleyici, bağlayıcı ve build sistemi sürümlerine ihtiyaç duyulabilir; bu da pratikte zordur
- Üretici kaynak koddan geçmeden doğrudan ikiliye yama uyguladıysa, arşivlenmiş kaynaktan yeniden build almak daha önce düzeltilmiş hataları geri getirebilir
- Mevcut doğrudan ikili işleme yaklaşımları emülasyon, statik çeviri ve dinamik çeviriyi birleştirir; ancak çevrilmiş programla birlikte çalışan ek sistem bileşenleri güven tabanına dahil olur
- Dinamik davranış, test sırasına veya girdilere göre değişebileceğinden, tüm güvenilirliği doğrulamak zordur
- Horspool ve Marovac, 1980'de çalıştırılabilir dosyaları tersine çevirmek için kod ile verinin kesin biçimde ayrılması gerektiğini ve çoğu mimaride bunun durdurma problemi ile eşdeğer olduğu için genel durumda çözülemez olduğunu gösterdi
- Mevcut statik ikili lifter'lar kod/veri ayrımını sezgisel olarak yaklaşıklar; özellikle dolaylı kontrol akışı aktarımı hedeflerini öngörmede sorun büyür
- LLBT, ARM komutlarını LLVM IR'a kaldırıp hedef mimari için yeniden derler; ancak dolaylı dal hedeflerini saptamak için sezgisel yöntemler kullanır ve girdi ikilisi hakkında çeşitli varsayımlarda bulunur
- İyi sezgisel yöntemler bile bazı girdilerde başarısız olur; tüm ikiliyi doğru kaldırmak için tüm kod/veri ayrımlarının doğru olması gerektiğinden, ikili büyüdükçe başarısızlık olasılığı artar
- Dinamik yaklaşımlar gerçekten yürütülen komut akışını izlediği için komut kurtarma ve dolaylı kontrol akışını işleyebilir; ancak somut yürütmede ulaşılamayan komutları kaldıramaz
- x64 gibi değişken uzunluklu komutlara sahip ISA'larda, bir komut dizisinin içine başka komut dizileri gömülebilir ve çok baytlı bir komutun ortasına dallanıldığında mevcut işlenenler ayrı komutlar olarak decode edilebilir
- ROP saldırıları ve kod obfuscation bu özelliği kullanabilir
- Apple'ın Rosetta II'si ve Microsoft'un Prism'i ön çeviri ile dinamik çeviri bileşenlerini birleştirir
- WYTIWYG ve Polynima, dinamik profillemeyle belirlenen kontrol akışı yolları boyunca statik lifting yapar ve görülmemiş hedef adreslere ulaşıldığında kontrol akışı bilgisini dinamik olarak toplayan bir fallback kullanır
- Elevator, herhangi bir baytın kod mu veri mi, komut sözcüğü mü yoksa bağımsız değişken mi olduğuna karar vermez; çalıştırılabilir dosyanın her baytını mümkün tüm yorumlarıyla ayrı kontrol akışı yollarına dahil eder
- Bu yaklaşım, superset disassembly yöntemini statik yeniden derleme ve çapraz ISA derlemeye uygular; decode hassasiyetini kod büyümesiyle takas eder
Kontrol akışı ve durumun korunması
- Elevator, çevrilmiş AArch64 kodu içinde x64 durumunun tamamını koruma ilkesine göre çalışır
- x64 yazmaçları ile AArch64 yazmaçlarını bire bir eşleyerek her x64 yazmaç durumunu karşılık gelen AArch64 yazmacında emüle eder
- x64 yığını doğrudan AArch64 yığını üzerinde emüle edilir ve yürütme sırasında olağan yığın genişlemesini işletim sistemi yönetir
- Girdi x64 ikilisinin ABI'sini analiz etmez; yalnızca yürütmenin dış koda geçtiği veya geri döndüğü noktalarda x64 System V ABI ile AArch64 Procedure Call Standard kurallarına göre ABI çevirisi yapar
- Tam durum koruması ve bire bir yazmaç eşlemesi sayesinde her x64 komutu, öncesindeki veya sonrasındaki komutları bilmeden bağımsız olarak çevrilebilir
- Özgün ikilideki her çalıştırılabilir bayt ofseti, aynı anda hem veri hem de potansiyel bir komut dizisinin başlangıç noktası olarak yorumlanır
- Dolaylı atlama, callback veya çalışma anı dispatch gibi statik olarak analiz edilemeyen tüm potansiyel hedefler için yeniden yazılmış ikilinin içinde karşılık gelen bir iniş noktası oluşturulur
- Çalışma anında, hedefleri çözümlemek için özgün komut adresinden çevrilmiş kod adresine giden bir arama tablosu son ikiliye gömülür
-
İç içe geçmiş komut örneği
Listing 1, decode işlemi.byte 0xB0adresinden başlatıldığındaMOV AL, 0xC3ardındanRETüretildiğini, bir bayt sonrakiReturnC2adresinden başlatıldığında ise yalnızcaRETüretildiğini gösterir- Her iki decode da önceki
jzüzerinden erişilebilir; çevirici bu iki bayt için yalnızca tek bir yorum seçerse yollardan birini kaçırır
-
Hesaplanmış dolaylı dal örneği
Listing 2,call Labelkomutunun tablo taban adresini oluşturduğunu,pop rsiile bunun geri alındığını, ardından girdiye bağlı ofset eklenerekjmp rsihedefinin oluşturulduğunu gösterir- Dal, encoding akışı içinde 2 bayt aralıklarla yerleştirilmiş dört
inc eaxkomutundan birine inebilir - Yalnızca statik olarak çözümlenebilen atlama hedeflerini yeniden yazan bir çeviricinin böyle bir dal için iniş yapacak yeri olmaz
-
Çağrı, dönüş ve dal
call,returnvebranchkomutları; dönüş adresi konumu, program sayacı ve koşul bayrağı yerleşimi x64 ile AArch64 arasında farklı olduğu için C tile'larıyla ifade edilemez- Doğrudan çağrılar, özgün x64 dönüş adresini emüle edilen yığına push eder ve callee'nin çevrilmiş tile'ına dallanır
- Dolaylı çağrılar, hedefin çevrilmiş ikili içinde mi yoksa harici bir kütüphanede mi olduğunu denetler; iç hedefler x64 ofset-tile tablosu ile çevrilerek ilgili tile'a dallanır
- Dış hedeflerde, AArch64 kütüphanesinin döneceği
X30içine ters ABI çeviri gadget adresi konur, çıkış ABI çevirisi yapılır ve ardından dış hedefe dallanılır - Dönüşte, emüle edilen yığından 8 baytlık dönüş adresi alınır, gömülü x64 ikili aralığıyla karşılaştırılır ve iç dönüşse adres arama tablosuyla çevrilerek ilgili tile'a dallanılır
- Doğrudan dallarda hedef çeviri zamanında bilinir; koşullu dallar,
X14içinde tutulan x64 bayrak bitlerini inceleyen AArch64 koşullu dalına çevrilir - Dolaylı dallar, dolaylı çağrı ve dönüştekiyle aynı bounds check'i üretir; hedef dışarıdaysa çıkış ABI çevirisi yapılır
Tile tabanlı çeviri hattı
- Elevator'ın çevirisi üç aşamaya ayrılır: çevrimdışı tile bank üretimi, girdi ikilisi başına yeniden yazım ve son paketleme
- Çevrimdışı aşama, x64 komut anlambilimini C fonksiyonlarıyla ifade eder; bunu sabit x64-to-AArch64 yazmaç eşlemesi altında işlenen kombinasyonlarına göre özelleştirir ve değiştirilmiş LLVM 20 ile derleyerek yeniden kullanılabilir AArch64 bayt dizileri üretir
- Girdi ikilisi başına aşama, superset disassembly gerçekleştirir ve bulunan her aday komut için ada göre tile arayıp AArch64 bayt dizilerini art arda ekler
- Kontrol akışı aktarımları ve ABI sınırları gibi C tile'larıyla ifade edilmesi zor komut kategorileri, elle hazırlanmış küçük şablonlarla işlenir
- Paketleme aşaması, çevrilmiş kodu, özgün x64 ikilisini, adres arama tablosunu ve çalışma anı sürücüsünü birleştirerek bağımsız çalıştırılabilir bir AArch64 ikilisi üretir
-
Çevrimdışı tile bank
- Her x64 komutu için eşdeğer AArch64 komut dizilerini elle yazmak pratik değildir
ADD Reg8, Reg8gibi tek bir şablon bile 256 somut yazmaç kombinasyonuna genişler; tüm x64 komut kümesinde yazmaç, bellek işleneni ve immediate adresleme çeşitleri çok fazladır- Elevator, her x64 komutunun anlambilimini küçük bir C fonksiyonu olarak yazar, ardından bunu somut işlenen kombinasyonlarına göre özelleştirir ve LLVM'in AArch64'e derlemesini sağlar
ADD Reg8, Reg8örneğinde şablon, hedef yazmacın alt 8 bitini 8 bitlik toplamla günceller ve üst 56 biti koruyarak x64'ün kısmi yazmaç yazma anlambilimini karşılar- x64
ADD Reg8, Reg8,RFLAGSiçindeki Carry, Parity, Auxiliary Carry, Zero, Sign ve Overflow bayraklarını da değiştirdiğinden, tek dönüş değerine sahip C fonksiyonu kısıtı nedeniyle bayrak güncellemesi ayrı bir bayrak tile'ı ile yakalanır - Bir x64 komutu bir veya birden çok tile'a karşılık gelebilir; üretim sırasında bunlar yeniden art arda yapıştırılarak tam anlambilim geri kurulur
aarch64_custom_regözniteliği, LLVM'e dönüş değerini ve her bağımsız değişkeni hangi AArch64 yazmaçlarına yerleştireceğini bildirir- Sabit eşleme; x64 System V ile AAPCS64'ün callee-saved ve caller-saved özelliklerini uyumlu hale getirmek, tamsayı bağımsız değişken yazmaçlarındaki yeniden sıralamayı azaltmak ve boşta kalan AArch64 callee-saved yazmaçlarını gelecekte gölge durum için ayırmak amacıyla seçilmiştir
- x64'ün
RFLAGSbitleri veXMMyazmaç dosyası da aynı bire bir ilke altında ayrılmış AArch64 yazmaçlarında tutulur - Değiştirilmiş LLVM 20, fonksiyon başına
aarch64_custom_regözniteliğini işler ve emüle edilen x64 durumunu tutan AArch64 yazmaçlarını register allocator içinde callee-saved olarak yeniden sınıflandırır TileGen, C şablonlarını dolaşarak izin verilen her işlenen kombinasyonu için özelleştirilmiş kopyalar üretir ve şablonun parametre konumları ile yazmaç eşlemesinden öznitelikleri mekanik olarak sentezler
-
Girdi ikilisi başına yeniden yazım
- Girdi x64 ikilisi verildiğinde ikili başına aşama superset disassembly yapar ve elde edilen CFG üzerinde dolaşır
- Her düğümde formatter, decode edilen komutun opcode ve işlenenlerinden tile adını oluşturur; birden çok tile gerektiren komutlar için birden çok adı birleştirir
- x64'te yığın işaretçisi hizalaması kısıtı yoktur, ancak AArch64 yığın işaretçisi bellek işleneninde kullanıldığında 16 bayt hizalama ister
RSPdoğrudanSP'ye eşlenirse, işlev prologlarındaki art ardaPUSHgibi yaygın x64 kod kalıpları AArch64'te hizalama istisnası oluşturabilir- Elevator, tile'ların yığına ayrı bir
X25yazmacı üzerinden erişmesini sağlar ve yalnızca gerçekten gerektiğindeSP'yi bunun içinde somutlaştırır - LLVM ile derlenmiş tile'lar girişte 16 baytlık
SPhizalaması beklediğinden, spill alanı ayırdığı saptanan tile'lar yürütülmeden önceSPaşağı hizalanır ve yürütmeden sonra geri yüklenir - Bayrak hesaplama tile'ları görece pahalı olduğundan, bayraklar sonraki post-dominating komutlarda okunmadan önce üzerine yazılıyorsa mevcut düğümdeki bayrak hesaplaması kaldırılır
- Şu anda desteklenmeyen komutlar çoğunlukla x64'ün AVX2 ve sonrası geniş vektör uzantılarıdır; bu konumlara tile yerine interrupt komutu eklenir
- Tam SPECint 2006 değerlendirmesinde, tüm kıyaslamaları çalıştırmak için tam x86-64 tamsayı ISA'sı ile SPECint'in kullandığı SSE alt kümesi yeterli oldu
- Ek komut desteği yeni tile'lar eklenerek genişletilebilir; ancak araştırmacılara göre daha fazla mühendislik çabası bilimsel içgörüye çok az katkı sağlayacaktır
ABI sınırlarının işlenmesi
- Elevator yalnızca dinamik bağlantılı ikilileri destekler
- Statik bağlantılı ikililer
CPUIDgibi mimariye özgü komutları doğrudan içerebilir; dinamik bağlantılı ikililer bunlarılibc'ye devrettiği için çeviri ihtiyacı azalır - Dinamik bağlantılı kütüphanelerle etkileşimde, emüle edilen x64 ortamı ile yerel AArch64 kütüphane kodu arasında gidip gelmek için x64 Linux ABI ile AArch64 Linux ABI arasındaki geçişleri destekler
- ABI çevirisi gerektiren temel öğeler bağımsız değişken yerleşimi ve dönüş adresinin konumudur
- System V x64 ABI,
RDI,RSI,RDX,RCX,R8,R9olmak üzere altı yazmacı bağımsız değişken yazmacı olarak kullanır ve ek bağımsız değişkenleri[RSP+8]konumundan başlayarak yığında geçirir - x64
CALL, dönüş adresini[RSP]konumuna yazar - AArch64 Procedure Call Standard,
X0-X7olmak üzere sekiz bağımsız değişken yazmacı kullanır, geri kalan bağımsız değişkenleri[SP]yığınında tutar ve dönüş adresiniX30içinde saklar -
Harici kütüphane çağrıları
- Çevrilmiş x64 çağrısı bir harici kütüphaneyi hedefliyorsa, bağımsız değişken yerleşimi AArch64 çağrı kuralına göre dönüştürülmelidir
- Önce
SP'den 8 çıkarılarak 16 bayt sınırına yeniden hizalanır ve daha önce yığında bulunan x64 dönüş adresi[SP+0x8]konumuna yerleştirilir [SP+0x10]ve[SP+0x18]konumlarındaki değerlerX6veX7içine yüklenerek x64 kodunun yığına koyduğu olası 7. ve 8. bağımsız değişkenlerin AArch64 kütüphanesi tarafından görülebilmesi sağlanır- Geri kalan olası yığın bağımsız değişkenleri
[SP+0x20]konumundan itibaren kalır; bu da AArch64'ün beklediği yerleşimle uyuşmaz - x64 dönüş adresini ve
X6,X7içine taşınan değerleri yığından kaldırmak güvenli değildir; çünkü bunlar gerçek bağımsız değişken değil, çağıranın spill alanı ya da yığına yerleştirilmiş bir yapının parçası olabilir - Elevator, çağıranın yığın yerleşimine dokunmadan ek
n×8bayt yığın alanı ayırır ve mevcut konumdan olasınadet 8 baytlık bağımsız değişkeni buraya kopyalar - Varsayılan
ndeğeri 10'dur; girdi ikilisi harici kütüphane fonksiyonlarına toplam 16'dan fazla bağımsız değişken geçiriyorsa bu değer yapılandırmayla artırılabilir - Son olarak, harici kütüphanenin döneceği gadget adresi
X30içine kaydedilir
-
Harici kütüphaneden geri dönüş
- Denetim, harici kütüphane çağrısından önce
X30içine kaydedilen gadget'a döndüğünde, daha önce kopyalanmış yığın bağımsız değişkenlerini temizlemek için yığın işaretçisinen×8eklenir - Harici kütüphane dönüş değeri
X0'dan, emüle edilen x64 kodununRAXiçin beklediği konum olanX9'a taşınır - Özgün x64 dönüş adresi ve ilgili padding yığından alınır, adres çevrilir ve oraya dallanılarak özgün
CALLsonrasındaki yürütmeye devam edilir
- Denetim, harici kütüphane çağrısından önce
-
Çevrilmiş koda gelen callback'ler
- Yerel AArch64 kodu çevrilmiş ikiliyi çağırdığında, AArch64 çağrı kuralı x64 çağrı kuralına dönüştürülmelidir
- Emüle edilen x64 kodu 7. ve 8. bağımsız değişkenleri
X6,X7içinde değil yığında beklediğinden, önceX7, ardındanX6push edilerek x64'ün beklediği yığın konumlarına yerleştirilir - Callee gerçekte 7. ve 8. bağımsız değişkenleri beklemiyorsa bu push edilen değerlerin etkisi olmaz
- Harici kütüphanenin AArch64 branch-and-link komutunun
X30içine koyduğu dönüş adresi, x64 dönüş komutunun beklediği yığın konumuna push edilir
-
Callback'ten harici kütüphaneye dönüş
- Çevrilmiş kod callback içinden harici kütüphaneye dönerken giriş süreci tersine çevrilir
- Dönüş adresi yığından alınır ve ayrılmış yığın alanı
X6ileX7push edilip ardından yığın işaretçisine0x10eklenerek temizlenir
1 yorum
Hacker News yorumları
QEMU’nun kullanıcı modu JIT’inin tam olarak ne yaptığını bilmiyorum ama iyileştirme için epey alan var gibi görünüyor
2013’te x86-64’ten aarch64’e çeviren bir JIT motoru yaptım ve o zaman Fedora’nın beta aarch64 ikililerini çalıştırarak x86_64 Linux üzerinde Fedora’nın aarch64 portunun büyük kısmını yeniden derleyebiliyordum
Ters yönde, yani aarch64 → x86-64 JIT de yaptım; eğlencesine aynı süreç içinde x86-64 → aarch64 → x86_64 şeklinde iki JIT’in birbirini loopback tarzında çalıştırdığını da göstermiştim
Yaptığım JIT, komutları ve CPU durumunu bire-çok eşliyordu ve yerel yeniden derlenmiş koda kıyasla yaklaşık 2 ila 5 kat daha yavaştı
Daha sonra QEMU JIT ile karşılaştırdığımda, QEMU’nun 10 ila 50 kat daha yavaş aralığında göründüğünü fark ettim
Ne yazık ki açık kaynak lisanslaması yapılmadığı için bunu kanıtlayacak kodu yayımlayamıyorum
Özellikle tasarımı “yalnızca x86’den aarch64’e” ve “yalnızca kullanıcı modu” için özelleştirebiliyorsanız ciddi performans kazanımı elde edilebilir
QEMU’nun kullanıcı modu desteği, sistem emülasyonu desteğine eklenmiş “bir şekilde çalışan” bir ek gibi ve genel JIT yapısı da “misafir → ara gösterim → ana sistem” biçiminde; bu, birçok misafir mimariyi ve birçok ana sistem mimarisini desteklemek için iyi, ancak “x86’in tamsayı register’ı az olduğu için sabit atama yapılabilir” ya da “aarch64 CPU uygun moda alınırsa karmaşık kayan nokta anlambilimi her zaman doğru olur” gibi belirli misafir/ana sistem eşleşmelerinin özelliklerinden yararlanmayı zorlaştırıyor
Ayrıca QEMU geliştirmesinde, performans optimizasyon fırsatları aramaktan çok “yeni mimari özelliği X’i emüle etmek” için zaman harcanıyor; çünkü geliştirme maliyetini üstlenenler buna daha fazla önem veriyor
.textbölümünün 50 kat büyümesi çok büyük, ama tamamen deterministik bir çeviri elde etmenin bedeli olarak yine de makul görünüyorÇoğu durumda boyut artışının yarattığı rahatsızlıktan çok, emülasyona kıyasla performans farkı daha önemli olacaktır
Çok iş parçacıklılığı ve istisna işlemenin imkânsız değil de bu projenin kapsamı dışında olması da ilginç
Bir sonraki adımın, olasılık uzayını sezgisel yöntemlerle budayıp ikili boyutunu küçültmek olup olmayacağını merak ediyorum
O zaman çeviri garantisi bozulur ama ikili taşınabilirliği pratikte daha iyi hale gelebilir
Bu çevirici Box64 veya FEX’ten çok daha yavaş ve herhangi bir nedenle JIT kullanamayan bir durumda değilseniz, düpedüz daha kötü bir seçenek
Çeviricinin dolaylı atlama işlemlerini nasıl ele aldığını hep merak etmişimdir
Bir ikiliyi analiz ederken yalnızca hedef adresi bilinen doğrudan atlamalarla bağlı kod bölümlerini keşfedebilirsiniz
Bu durumda her dolaylı atlamada hedef işlevi bulup, gerekirse çevirip sonra çevrilmiş koda geri dönmek gerekir; bu yavaş olmaz mı?
Daha hızlı bir yöntem olup olmadığını, çevrilmiş işlev adreslerinin özgün işlev adresleriyle eşleştirilip eşleştirilemeyeceğini ya da özgün adrese çevrilmiş koda giden bir atlama yerleştirilip yerleştirilmediğini merak ediyorum
jmpyapılırsa karşılık gelen blok Y konumundadır” diyen büyük bir tablo tutuyorBu yöntem, tablo kullanmayan doğrudan
jmp’den daha yavaş, ama özgün programda da dolaylı atlamalar zaten daha yavaştır ve genelde performans açısından kritik döngüler içinde sık görülmezÜstküme kontrol akışı grafiği fikrini gerçekten seviyorum, ancak yazıyı okumayı düşünenlerin şunları bilmesinde fayda var
Çalışma süresi yaklaşık 4,75 kat hızlanıyor (QEMU’dan hızlı ama Box64’ten belirgin biçimde yavaş), yürütülen komut sayısı 7 kat artıyor ve ikili boyutu 50 kat büyüyor
Dış çağrılara kadar x86 ABI emüle ediliyor
EFLAGS gibi x86 CPU durumunun büyük bir bölümü emüle edilmek zorunda ve karmaşık
movişlemleri de tek tek hesaplanmalıYalnızca tek iş parçacıklı ikililer destekleniyor
İstisna işleme ve yığın çözme (
unwinding) yokTüm komut kümesi desteklenmiyor
İlginç bir çalışma
Ayrıntılı bakmadım ama göreli ofsetler hâlâ sorun olabilir gibi geliyor
Sonuçta üretilen kodun boyutu farklı olacağından, bir tür çeviri katmanı ya da MMU gerekebilir; bunun da daha çok atlama tablolarını ve iç dallanmaları etkilemesi muhtemel
Ben daha çok 90’lardan kalma şeylerle uğraşıyorum ve ters assembler’lar kodun başlangıcı ve sonu hakkında çok sayıda varsayım yapıyor
Ama bazen sabit konumdaki giriş noktası işaretçileri gibi ön bilgi olmadan ikili parçasını bulmak mümkün olmuyor
Birkaç geçişten sonra ikiliyi “kesinlikle kod olan alanlar”a indirgemek mümkün olabilir gibi görünüyor
Eğer “Elevator her baytın tüm olası yorumlarını dikkate alıyor, her olası yorum için ayrı çeviriyi önceden üretiyor ve [...] yalnızca çöküşe götüren durumları buduyorsa”, çakışma ihtimali taşıyan gerçek programların hepsi budanmış olmuyor mu?
Böylece yine çakışma olur ama doğrudan yürütülen hatalı kodun çakışmasıyla aynı şey olmaz
Benim için en ilginç kısım sertifikasyon açısından
Havacılık ve tıbbi cihazlar gibi regülasyona tabi sektörlerde çalışan kodun sertifikalı kod olması gerekiyor; tam da bu yüzden JIT çoğu zaman kullanılamıyor
İmzalanabilir bir ikili üreten statik çeviri, kod şişmesini göze alsanız bile pratik bir atılım olabilir
Muhtemelen burada LLM’leri de büyük ölçekte uygulamanın bir yolu yoktur ama “iş dünyasında AI” gibi büyük anlatılarda bu kısım neredeyse hiç ele alınmıyor
50 kat makul değil, tam bir önbellek felaketi
JIT’ten kaçınarak elde edilen tüm performans kazancı ortadan kalkabilir
Sıcak kod bir araya toplanırsa kullanılmayan kodun hiç yüklenmemesi sağlanabilir
Komutlar sonuçta o kadar da büyük değil ve CPU çalışma sırasında zaten optimizasyon yapıyor
Kendini değiştiren kodu işleyebiliyor mu?
Ayrıca neden yalnızca x86_64 olduğunu da merak ediyorum
Eski oyunlar gibi 32 bit programları çevirmek daha anlamlı görünüyor
“Kendini değiştiren ve JIT derlenmiş kod. Elevator, tüm tamamen statik ikili yeniden yazıcılar gibi, kendini değiştiren kodu veya JIT derlenmiş kodu desteklemez”
Günümüzde
.textbölümü çoğunlukla salt okunur ve güvenlik gereksinimlerinin azalması da beklenmezBu temelde bir çelişki
Çünkü önbellek satırlarını ve ardışık düzen dallanma tahmini performansını bozar
Ayrıca W^X’i ihlal eder, bu yüzden genelde yalnızca JIT uyumlu bellek sayfalarında kullanılmalıdır
Bu nedenle neredeyse her zaman kaçınılması gerekir
486 veya P5 döneminde, sabit değerleri iç döngü değişkeni gibi kullanmak için bir ölçüde görülüyordu ama artık pek öyle değil
Neredeyse kusursuz emülasyon ya da çeviri elde etmek istiyorsanız, x86’in ele alınması gereken çok sayıda kirli istisna durumu var
Kaynak kodu nerede?