1 puan yazan GN⁺ 2024-07-11 | 1 yorum | WhatsApp'ta paylaş
  • Time Travel Debugging için CPU emülatörünü C++ ile yeniden yazma sürecinde, x86/amd64’te aynı davranışın bile kodlama, prefix ve çalışma moduna göre farklı ele alınması gerektiği görüldü
  • int 3 için tek baytlık CC kodlaması, ADD EAX, imm için kısa biçim, etkisiz REX prefix gibi performansı ve hata ayıklamayı etkileyen çok sayıda alternatif gösterim var
  • INC/DEC, CMPXCHG8B/CMPXCHG16B, shift/rotate komutlarında flag işleme biçimi sezgilerden farklı olduğundan emülatör hatalarına kolayca yol açabiliyor
  • Shift count, operand boyutu olduğu gibi uygulanmayıp maskelendiği için shr eax,20h, 32 bitlik register’ı 0 yapmaz; değeri korur
  • Segmentler, 32 bit ve 64 bit Windows’ta TEB erişimi için hâlâ kullanılır; FS/GS anlamı ve base belirleme yöntemindeki farklar disassembler ve emülatör uygulamasını doğrudan etkiler

TTD emülatörünün yeniden yazımında ortaya çıkan x86 ayrıntı kuralları

  • Time Travel Debugging’in bir bileşeni, bir sürecin tüm yürütmesini komut düzeyinde kaydeden CPU emülatörüdür
  • İlk sürüm olan iDNA’nın emülatörü neredeyse tamamen assembly ile yazılmıştı ve hızlıydı; ancak bakımı ve genişletilmesi zordu
  • İkinci sürümde emülasyon kısmı ve sonrasında diğer bölümlerin çoğu C++ ile yeniden yazıldı; assembly sürümünün performansının büyük bölümünü korurken daha yönetilebilir bir kod tabanı hedeflendi
  • CPU emülatörü yapmak için CPU davranışının tüm ayrıntılarını eksiksiz tutturmak gerekir; x86 deneyimi olanlara tanıdık gelen kurallar bile gerçek uygulamada yeniden doğrulanır

Aynı komutu birden fazla şekilde ifade eden x86 kodlaması

  • x86, aynı komutu birden fazla bayt dizisiyle ifade edebilir
  • int 3, CD 03 olarak kodlanabildiği gibi tek bayt CC olarak da kodlanabilir
    • Yazılım breakpoint’i olarak kullanıldığı için, bir sonraki sayfanın eşlenmediği bir bellek sayfasının sonundaki komut konumuna bile breakpoint koymayı mümkün kılar
  • Yaygın durumları kısaltmak için alternatif kodlamalar da vardır
    • add eax, imm, 05cccccccc gibi kısa biçimde ifade edilebilir
    • Aynı değeri ECX’e eklemek için 81c1cccccccc gibi 1 bayt daha gerekir
  • EAX’in “Accumulator register” olarak adlandırılması yalnızca bir gelenek değildir; gerçek bir kodlama farkı yaratır. Kısa komutlar ana bellekten taşınması gereken veriyi ve instruction cache kullanımını azaltarak performans açısından avantaj sağlayabilir
  • Derleyiciler mümkün olduğunda bu kısa kodlamalardan yararlanabilir

Prefix’ler ve 15 baytlık komut uzunluğu sınırı

  • x86 komutları, davranışı değiştiren prefix baytlarına sahip olabilir
  • 64 bit kodda sık kullanılan REX prefix’i, 32 bit koda göre daha geniş bir register aralığına erişmek için kullanılır
  • CPU, etkisi olmayan REX prefix’lerini de kabul eder
    • 4004cc, 8 bitlik add al,0CCh önüne REX baytı eklenmiş biçimdir; ancak bu durumda REX’in hiçbir etkisi yoktur
    • İki REX prefix’i eklense bile CPU bunu çalıştırabilir; WinDbg dahil birçok disassembler bundan dolayı karışabilir
  • x86 uyumlu CPU’larda mevcut komut uzunluğu için 15 bayt kesin üst sınırdır
    • 15 baytı aşan komut geçersiz komut sayılır ve exception üretir
    • Eski CPU’larda prefix’ler için başka sınırlamalar da vardır; LOCK prefix’i gibi kullanılabileceği koşullar daha sıkı olan prefix’ler de bulunur

Adres boyutu ve moda göre değişen yorumlama

  • Address override prefix’i, 64 bit modda 32 bit adrese başvurulmasını sağlayabilir
    • 488d0424, lea rax,[rsp] anlamına gelir
    • 67488d0424, 0x67 prefix’i nedeniyle lea rax,[esp] anlamına gelir
  • 32 bit kodda Address override, adres modunu 16 bit adrese çevirir
  • Aynı bayt dizisini doğru disassemble etmek veya yorumlamak için code segment’in varsayılan operand boyutunu ve adres boyutunu bilmek gerekir
    • 32 bit moddaki 8b0424, mov eax,dword ptr [esp] anlamına gelir
    • 64 bit moddaki 8b0424, mov eax,dword ptr [rsp] anlamına gelir
  • x86’da INC reg ve DEC reg için kullanılan 40~4F aralığı, x64’te REX prefix bytes olarak kullanılır
    • 32 bit modda 48 03 04 24, dec eax ve add eax,dword ptr [esp] olmak üzere iki komut olarak yorumlanır
    • 64 bit modda 48030424, tek bir add rax,qword ptr [rsp] komutu olarak yorumlanır
  • AMD64 tasarımcıları, 64 bit modda register kümesini genişletmek için INC/DEC’in geniş kodlama alanını yeni prefix için kullandı; bu komutlar için zaten hem register’ı hem de belleği destekleyen başka bir kodlama vardı

WinDbg ve 64 bit modda INC reg tuzağı

  • WinDbg komutları her zaman 32 bit moddaymış gibi assemble ettiği için, 64 bit kodda INC reg assemble etmeye çalışınca niyet edilenden farklı sonuçlar doğabilir
  • Örnekte inc eax, gerçek bir artırma komutu değil, sonraki komutu niteleyen işe yaramaz bir REX prefix’ine dönüşür
  • Sonuç olarak ilgili bayt dizisi inc değil, jmp komutunun önündeki prefix olarak yorumlanır

Flag davranışındaki istisnalar

  • INC EAX, ADD EAX, 1’e benzer görünür ama tamamen aynı değildir
    • ADD, carry flag’i günceller
    • INC, carry flag’i güncellemez
  • TTD emülatörü uygulanırken bu fark başta yanlış uygulanmış ve unit test ile yakalanmıştır
  • Çoğu aritmetik ve mantıksal işlem overflow, sign, zero, auxiliary carry, parity, carry flag’lerini ayarlar
  • CMPXCHG de bu flag’leri ayarlar; ancak CMPXCHG8B ve CMPXCHG16B yalnızca zero flag’i değiştirir
  • Bazı komutlar flag’lerin bir kısmını tanımsız durumda bırakır
    • Shift ve rotate komutları, shift amount 1’den büyükse overflow flag’i tanımsız durumda bırakır
    • Tanımsız flag’lerin gerçek davranışı, shift işleminin iç uygulamasıyla ilişkilidir ve mimariden mimariye değişebilir
    • Atom ailesi CPU’ların ALU’da bit shift’i daha ucuz ve daha yavaş bir yöntemle yaptığı, bu yüzden tanımsız flag değerlerinin farklılaştığı söylenir; ancak bu doğrudan test edilmemiştir

Shift komutlarında count maskeleme

  • 66c1e810, shr ax,10h anlamına gelir ve AX’i 16 bit sağa shift eder
    • AX 16 bitlik register olduğu için sonuç 0 olur
  • c1e820, shr eax,20h anlamına gelir ve yüzeyde EAX’i 32 bit sağa shift eden bir komut gibi görünür
  • Gerçekte EAX değeri değişmez
    • Intel SDM’ye göre count, 1Fh ile maskelenir ve rotation’ın yalnızca alt 5 biti kullanılır
    • REX.W prefix’i kullanılırsa maske 3Fh olur ve maksimum shift değeri 63 bit’e çıkar
  • Microsoft mülakatında “32 bitlik bir register’ı tek komutla clear etmenin tüm yolları” sorusunda bu davranış gerçekten gündeme gelmişti
    • Mülakatı yapan kişi shift kullanılırsa bunun mümkün olduğunu düşünüyordu; ancak 32 bitlik register için bunun mümkün olmadığı yanıtı verildi

32 bit ve 64 bit kodda yaşamaya devam eden segmentler

  • Segment belleği 16 bit kodun bir kalıntısı gibi görünebilir; ancak 32 bit ve 64 bit kodda da gerçek etkisi vardır
  • Çoğu OS neredeyse flat bir bellek modeli kullanır ve segment base address’i 0’da tuttuğu için normalde pek fark edilmez
    • 64 bit modda CPU, CS, DS, ES, SS segment base’lerini her zaman 0 kabul eder
  • İstisna olarak thread local storage için FS veya GS gibi extra segment register’ları kullanılır
  • Düzeltme olarak, FS/GS segment’lerinin base’i yetkisiz koddan da rdfsbase, wrfsbase, rdgsbase, wrgsbase komutlarıyla okunabilir
    • Bu komutlar Ivy Bridge’den, yani 2012’den beri kullanılabilir

Windows TEB erişimi ve FS/GS

  • Windows’ta FS ve GS, TEB’e (Thread Execution Block) başvurmak için kullanılır
  • TEB yapısı, yapının başlangıç noktasının flat address’ini gösteren bir self pointer içerir; bu adres aynı zamanda ilgili segment’in base’idir
  • 32 bit süreçlerde TEB, FS ile konumlandırılır
    • GetLastError, fs:[00000018h] konumundan TEB.NtTib.Self değerini alır, ardından [eax+34h] konumundan LastErrorValue değerini okur
  • 64 bit süreçlerde TEB, GS ile konumlandırılır
    • GetLastError, gs:[30h] konumundan pointer’ı okur ve [rax+68h] konumundan değeri alır
  • 64 bit OS üzerinde çalışan 32 bit süreçler hem 32 bit TEB’e hem de 64 bit TEB’e sahiptir; 32 bit süreç içinde çalışan 64 bit WOW kodu gibi iki TEB’e de erişmenin yararlı olduğu bağlamlar vardır

Segment base’i belirleme yöntemi de moda göre değişir

  • FS ve GS base address’ini belirleyen CPU ayarı, 32 bit mod ile 64 bit modda farklıdır
  • 32 bit modda segment register’ının gerçek değeri, Global Descriptor Table ve Local Descriptor Table içinde tanımlanan segment descriptor’ını referans alır
  • 64 bit modda base iki MSR ile kontrol edilir
    • FS Base, Intel SDM’de IA32_FS_BASE
    • GS Base, Intel SDM’de IA32_GS_BASE
  • Bu yapı nedeniyle 64 bit modda FS ve GS’nin gerçek register değerinin kendisi önemli değildir
    • Önemli olan segment override prefix’idir
  • WinDbg’de 32 bit bir süreci debug ederken FS register değeri kullanılarak “FS segment” içeriği dump edilebilir
  • 64 bit süreçlerde aynı yöntem çalışmaz; segment değerinden çok segment override prefix’i anlam taşır

Emülatör geliştiricilerinin çıkaracağı pratik dersler

  • Bir x86 emülatörü yapmak, komut kodlaması, prefix’ler, flag’ler, shift count ve segmentler gibi CPU’nun gerçek davranışlarını ayrıntılı biçimde ele almayı gerektirir
  • Bu kuralların çoğu normal kod yazarken neredeyse hiç işe yaramaz; ancak emülatör uygulaması için doğrudan gereksinim hâline gelir
  • Deneme yanılma ve mentorluk yoluyla çok şey öğrenilmiştir; eski emülatör deneyimine sahip Darek Mihocka ve emulators.com da ayrıca anılır
  • x86 optimizasyonu ve düşük seviye davranışlarla ilgileniyorsanız Agner Fog’s website üzerindeki kaynaklar yararlıdır

1 yorum

 
GN⁺ 2024-07-11
Hacker News yorumları
  • Bu arada BSF/BSR tarafında da tuhaflıklar var. Intel SDM, girdi 0 ise hedef değerin tanımsız olduğunu söylüyor; AMD ise bu durumda hedefin değiştirilmediğini belgeliyor.
    Fakat glibc, Intel’de de hedefin değiştirilmediğine dair belgelenmemiş gerçeği aynen kullanıyor [1]. Bu yüzden ikili dönüştürücümde sorunun nedenini bulmam epey uzun sürdü.
    Ayrıca TZCNT/LZCNT, F3 öneki eklenmiş BSF/BSR kodlamalarıdır; bu genişletmeyi desteklemeyen eski işlemcilerde önek sessizce yok sayılır. Bu nedenle aynı kod CPU’ya göre farklı davranır ama en azından bu belgelenmiş durumda.
    Kodlama tarafında insanlar önekleri sık sık kötüler ama kişisel olarak bence en kötüsü bu değil. İyi biliniyorlar ve bir ölçüde belgelenmiş durumdalar. Daha kötü tuhaflıklar da var. Örneğin REX/VEX/EVEX.RXB genişletme bitleri uygulanacak bir hedef yoksa yok sayılır, ancak maske register’ları k0-k7’de #UD’ye yol açar. Ama register ModRM.rm içinde kodlanırsa genişletme biti yine yok sayılır.
    APX tuhaflık seviyesini bir kademe daha yukarı taşıyor. REX2 öneki genel amaçlı register’lar r16-r31’i kodlayabilir ama xmm16-xmm31’i kodlayamaz; EVEX önekinin opcode’a bağlı olarak birden fazla yerleşimi vardır ve register için kullanılan genişletme bitleri de register türüne göre değişir. XMM register’ları X3:B3:rm ve V4:X3:idx kullanırken, genel amaçlı register’lar B4:B3:rm ve X4:X3:idx kullanır. 1 yıl geçtiği hâlde APX decoder’ını hâlâ bitiremediğim için tam listeyi veremem.
    [1]: https://sourceware.org/bugzilla/show_bug.cgi?id=31748

    • Geçen 1 yıl boyunca fırsat buldukça QEMU’nun x86 decoder’ını yeniden yazıyorum. Başta AVX desteği eklemek için gerekli bir işti; artık yeniden yazılacak sadece birkaç opcode kaldı ve ondan sonra APX desteğinin de çok zor olmayacağını düşünüyorum.
      EVEX için, opcode’u okuyup EVEX sınıfını belirleyene kadar ham bitleri koruyan bir yaklaşıma gitmeyi planlıyorum. Yani immediate değerden önce, muhtemelen ModRM’den önceki aşamaya kadar korumayı düşünüyorum.
      Decoder’ım büyük ölçüde kılavuzdaki tablolara dayanıyor ve kod genel olarak fena değil. Girintileme aşırı değil, aşamalar da çoğunlukla ayrılmış ya da kolayca ayırt edilebilir durumda. Çıktı JIT kodu olduğu için aşırı verimli olması gerekmiyor; okunabilir olması daha iyi. Zamanın çoğunun harcandığı yer de burası değil.
      Yine de kılavuzun hatalı olduğu ya da her şeyi söylemediği epey durum var. Tablolar da yıllardır güncellenmemiş; örneğin K register komutları bile yok. Bundan sonra daha fazla elle çalışma gerekecek gibi görünüyor.
      En üstteki yorumda durum biraz açıklanmış: https://github.com/qemu/qemu/blob/59084feb256c617063e0dbe7e6...
      Yukarıda söylediğim gibi, eski kodun hâlâ işlediği birkaç komut kaldı; özellikle BT/BTS/BTR/BTC bunlardan. Kodu yazdım ama henüz birleştirmedim.
    • 486 ve Pentium döneminden başka bir tuhaflık daha var. BSWAP EAX, little-endian ile big-endian arasında dönüşüm yapar ve başından beri 32 bitlik bir komuttu.
      Ancak 16 bit ve 32 bit modları arasında geçiş yapan 0x66 öneki var. Bunu BSWAP EAX’e uygularsanız tanımsız, garip bir şey olur.
      Bazı CPU mimarilerinde, Intel ve AMD farkındaki gibi önek basitçe yok sayılırdı; bazılarında ise benim “iç swap” dediğim davranış gerçekleşirdi. Örneğin EAX’te saklanan dört bayttan 1. ve 2. bayt yer değiştirirdi.
      0x11223344, 0x11332244 olurdu.
    • Tüm bu mantığı silikonda doğru, üstelik hızlı çalışacak şekilde yapmak gerektiğini düşünmek baş döndürücü.
      Eskiden x86 Intel’in hendeğiydi; artık taşımak zorunda olduğu kâbus gibi bir yük gibi görünüyor.
    • LZCNT’nin semantiği ile kodlama kombinasyonu kendi kalesine gol gibi hissettiriyor. Eski sistemlerde yok sayılan bir önek eklenmiş BSR komutu olarak kodlanıyor; 0 olmayan girdilerde ise dönen değer, operand boyutundan eski sürümün döndürdüğü değerin çıkarılmış hâli.
      clz() diye bir fonksiyon gerçekten var ama LZCNT, 0 girdisi semantiği dışında sadece BSR olsaydı, uyumluluk elde etmek için implementasyonda fazladan bir çıkarma yapmak küçük bir maliyet olurdu gibi geliyor.
    • Bu alanı pek bilmiyorum ama bir JTAG arayüzünü x86 CPU’ya bağlayıp komut bazında çalıştırarak tüm register değerlerini kaydetmek ilginç olurdu.
      Aynı programı emüle edilen CPU’da da çalıştırıp her komutta durumun aynı olup olmadığını doğrularsanız, emülatörün donanımı kusursuz biçimde taklit edip etmediğini test edebilirsiniz.
  • Harika biri. Assembly yazmak bana basit hissettiriyor ve dikey olarak uzanan estetiğini de seviyorum.
    OP’ninkine biraz olsun yaklaşan tek deneyimim, JS yapan bir arkadaşıma stack’i anlamasında yardımcı olmaya çalışırken küçük bir ISA’ya sahip mini bir VM’i birlikte yapmamızdı: https://gist.github.com/darighost/2d880fe27510e0c90f75680bfe...
    Daha derine inebilirdik ve keşke ben de inseydim; ama o zaman asıl eğitim amacından sapmış olurduk sanırım. Arkadaşıma hâlâ birlikte çalışmak isteyip istemediğini sormalıyım. O harika web geliştirme işleriyle çok para kazanıyor, bu yüzden derine inmeye zamanı yok; ben ise işsizim, zaman ve enerjim neredeyse uçsuz bucaksız bir deniz gibi, o yüzden kolay değil.

    • Bunu vesile edip arkadaştan JS öğrenmek de iyi olabilir.
    • Bilgisayar bilimi kökenli olmayan biri olarak bu kod, iç işleyişi anlamaya başlamak için iyi bir giriş kapısı oldu ve beni epey havalı ve zor bir assembly yolculuğuna taşıdı. Daha derine inmeyi düşünüyorum.
  • Justine Tunney ve onun emülatörü de görülmeye değer. https://justine.lol/blinkenlights/
    Dokümantasyon, CPU’nun nasıl çalıştığına dair harika bir tur sunuyor.

    • Şaşırtıcı. Her seferinde hayran bırakıyor.
    • Tunney adını, 2014 civarında evsiz gezip dolaştığı ve Occupy ile ilgili Twitter saçmalıkları yaptığı dönemden hatırlıyorum.
  • Önceki tartışma burada: https://news.ycombinator.com/item?id=34636699
    Aradan şimdiden 16 ay geçtiğine inanamıyorum. Zaman gerçekten hızlı akıyor

  • “CPU emülatörü yazmak, CPU’nun nasıl çalıştığını gerçekten anlamanın en iyi yoludur” görüşüne kesinlikle katılmıyorum
    En iyi yol, iyi bir bilgisayar bilimi dersinde yapıldığı gibi CPU’yu kapı düzeyinde inşa etmektir. Sıfırdan küçültülmüş bir ARM yapmak gerçekten eğlenceliydi

    • Bence ikisi de faydalı. Ancak modern bir CPU’yu kapı düzeyinde tasarlamak çoğu kişi için erişilebilir değil ve üniversitede tasarlanan CPU ile gerçek kod çalıştıran CPU arasında büyük bir uçurum var
      Modern bir CPU emülatörü yapmak biraz daha erişilebilir bir meydan okuma; yalnızca bir kısmı çalışsa bile hâlâ büyük bir eğitim değeri taşıyor
    • Katılıyorum. Mikrokod, pipeline, superscalar yapı, dal tahmini, L1 veri/komut önbelleği ve write-back L2 önbellek denetleyicisi olan temel bir işlemci bile hiç kolay değil
      Yazılım mühendislerinin çoğunun veri hazard’ları, önbellek geçersizleştirme ve pipeline duraklamaları konusundaki anlayışı eksik
    • Bence ikisi de doğru. 74xx çiplerini birbirine bağlamak çok tatmin edici ve elektriksel tarafı ile iç ödünleşimleri sezgisel olarak kavratıyor
      Ama anlamlı bir işte kullanmak isteyeceğiniz bir CPU’yu böyle yapmaya başladığınızda, o ayrıntılar ilginizi kaybettirmeye başlıyor. O noktada davranışın ve spesifikasyonun karmaşıklığı daha ilginç hale geliyor; emülatör yaklaşımı daha yönetilebilir oluyor ve daha fazla davranış türünü kapsayabiliyor
    • Nand2Tetris’i izleyerek kapı düzeyinden yukarı doğru inşa etme sürecindeyim; az önce VM emülatörü bölümünü bitirdim ve gerçekten çok uzun sürdü. Şimdi derleme aşamasına geçiyorum
    • Tersinden bakarsak, kapı düzeyinde bir CPU’da gerçekten bellek segmentasyonuna kadar uygulayacak mısınız? Bence gerçek anlayış için iki aşama da gerekli: önce gerçekten çalışan bir CPU yapmak, ardından kusurları da dahil gerçek bir CPU’yu emüle etmek
  • Oyuncak olmayan yaklaşık 12 mimari için hızlı emülatörler yazdım, birkaç JIT dönüştürücü de yaptım ama x86 hâlâ bende PTSD yaratıyor. Bu kadar dağınık bir mimari görmedim. Bir geçmişi ve gerekçeleri var, ama yine de fena halde kötü

    • x86 mimarisini çalışmak, çok sayıda düzensizlik ve körelmiş organ barındıran, birbiriyle yarışan sözdizimi sistemleri olan bir dili, mesela Fransızcayı öğrenmek gibi. RISC-V veya ARMv8 gibi diğer mimariler çok daha tutarlı
    • “Bundan daha dağınık bir mimari görmedim” diyorsanız Itanium var. Kılavuzu her açtığımda, özellikle aramasam bile “Bunu yaparken ne düşünüyorlardı ki?” dedirten yeni bir şey keşfediyorum
    • Katılıyorum. Belki de en baştan onunla başlamalıydım
  • Son dönemde bir yan proje olarak x86-64 decoder’ın hatırı sayılır bir bölümünü uyguladım [1] ve son zamanlarda daha da karmaşık hale gelmesine epey şaşırdım. Benim amacım için Sandpile.org [2] gerçekten faydalıydı
    [1] Daha doğrusu, henüz yayımlanmamış başka bir yan proje için yaptığım Fabian Giesen’in disfilter’ının x86-64 sürümü: https://gist.github.com/lifthrasiir/df47509caac2f065032ef72e...
    [2] https://sandpile.org/

  • Üniversitede yaptığım 68k disassembler, Neo’nun “Kung fu biliyorum” dediği an gibiydi. Yüksek seviyeli dilden transistörlere, oradan tekrar geriye dönüp kod hakkında akıl yürütmemi sağlayan eksik halkaydı
    Tam bir emülatör yazmanın bundan bir mertebe daha etkili olacağını düşünüyorum. Güzel yazı

    • ISA emülatörü yazmanın, modern superscalar CPU’ların nasıl çalıştığını anlamaya aslında pek yardımcı olmadığını düşünüyorum. Çünkü neredeyse her şey içeride gizlenmiş optimizasyonlardan ibaret
  • Galiba hafızam yanılmış. salsa20 varyantlarının ve makine kodunun aslen cryp.to’da olduğunu hatırlıyordum, ama Dan Bernstein’ın sitesi https://cr.yp.to/ imiş
    Bir startup’ta bekleyen verinin şifrelenmesi, akış şifrelemesi vb. konuları değerlendirdiğimiz dönemde Dan’in sayfalarında hedef çipset ve komut kümesine göre birden fazla uygulama vardı. Bunlar onun assembler gösteriminden çapraz derlenmişti
    VM’i deneyip 2000’lerin başı-ortası döneminde hangi komut kümelerinin desteklendiğini görmek ilginçti. Testler sırasında VM’in desteklediğini iddia ettiği ama uygulamanın tam desteklemediği şeyler yüzünden bazen sorunlar çıkıyordu

    • Dan Berstain’ın sitesinden bahsediyorsun herhalde… dur bir dakika
  • Burada RISC ile karşılaştırıldığında x86 assembly’nin ne kadar acı verici olduğundan çok bahsedilmesi ilginç. Ben kodu yeniden object file’lara ayırırken tam tersi sorunla karşılaşıyorum
    Bu kullanımda x86 analizi gerçekten kolay; MIPS ise kâbus gibiydi. Çünkü asıl ilgilendiğim şey kod ve veriye referanslar. x86’da pointer boyutunda immediate sabitler var; MIPS’te ise HI16/LO16 relocation çiftleri var ve bunlar register kullanım grafiği, kod akışı ve branch delay talimatlarıyla iç içe geçerek her türlü soruna yol açıyor
    Yine de x86’yı övüyor değilim

    • Evet. x86 tuhaf, ama değişken uzunluklu talimatlar metin biçimine açıldıktan sonra aslında güzel görünüyor ve anlaşılması kolay. Sorun, bir talimatın ortasına başka talimatlar gizlenebildiği için güvenli olmamasında
      x86 assembly ile C’yi karşılaştırarak öğrenilen en büyük şey, işaretli/işaretsiz olma durumunun bir tip değil, işlemin bir özelliği haline gelmesi
      Flag’lerden yararlanabilmek iyi olurdu; PPC veya armv7 gibi bazı mimarilerde bu daha kolay, ama x86’da flag’ler o kadar kolay üzerine yazılıyor ki değerlerinden yararlanmak çok zor