deniz.in

Piyasalar

Hava durumu

Hava durumu yükleniyor

· kaynak dev.to (home feed)

.NET 8'in UnsafeAccessor'ı: JIT, private üyelere reflection olmadan nasıl bağlanıyor

dev.to üzerindeki derinlemesine bir inceleme, .NET 8'in UnsafeAccessor'ını decompile ederek static extern bildirimlerinin JIT zamanında private üyelere nasıl bağlandığını ortaya koyuyor; hedef adıysa yalnızca bir attribute blob'un içinde yaşıyor.

.NET 8'in UnsafeAccessor'ı: JIT, private üyelere reflection olmadan nasıl bağlanıyor

dev.to üzerindeki derinlemesine bir inceleme, koda private üyelere reflection olmadan desteklenen bir erişim yolu sunan .NET 8 özelliği [UnsafeAccessor]'ı ele alıyor ve bir accessor'ı çalışır kılan her şeyin talimatlarda değil, metadata'da yaşadığını gösteriyor.

Reflection yükü olmadan erişim

Yazıya göre pattern, UnsafeAccessorAttribute ile süslenmiş, istenen erişim türünü (field, method, constructor veya bunların static karşılıklarını) ve ilgili yerlerde hedef üyenin adını taşıyan bir static extern metot. Runtime, JIT zamanında bildirimi gerçek üyeyle eşleştiriyor ve metot gövdesini kendisi sağlıyor. Bunun getirisi, çağrı noktasının düpedüz bir doğrudan çağrıya dönüşmesi: ne MethodInfo nesnesi var, ne delegate ne de boxing. Yazar, serializer'ların, test araçlarının ve base class library'nin kendisinin bazı bölümlerinin, dokunmaya yetkili olduğu iç üyelere ulaşmak için bu mekanizmaya güvendiğini belirtiyor.

Gövdesi olmayan bir metot

Özelliği decompile etmeyi garipleştiren şey, derleyicinin accessor için hiçbir şey üretmemesi. RVA'sı sıfır ve IL içinde ne talimat var, ne local değişken ne de maxstack. Yazı bunu, runtime'ın gövdesiz bir managed metodu benzer şekilde tamamladığı P/Invoke'a benzetiyor ama ayırt edici ayrıntıya işaret ediyor: bir UnsafeAccessor metodu pinvokeimpl bayrağı ve ImplMap metadata satırı taşımıyor; bu da onu diğer yaygın gövdesiz extern türü olan DllImport'tan ayırıyor.

Private ad, attribute blob'unun içinde gizli

Yazı daha sonra custom attribute blob'unu bayt bayt çözümliyor, çünkü private üyenin adı var olan tek yer orası. İki baytlık bir prologun ardından, temel alınan tamsayı olarak serialize edilen UnsafeAccessorKind constructor argümanı geliyor — Field için 3, Method için 1, Constructor için 0 — ve ardından adlandırılmış argümanların sayısı, string türünde bir property'yi gösteren bir işaretçi, "Name" property adı ve son olarak "_secret" gibi uzunluk önekli bir UTF-8 string olarak üye adı geliyor. En kritik nokta şu: modüldeki hiçbir MemberRef veya FieldRef token hedef üyeye işaret etmiyor. Bağlama string ile yapılıyor ve JIT tarafından accessor'ın ilk parametresinin türüne karşı çözümleniyor.

Üç biçim, üç kural

Yazıya göre her accessor türünün kendi imza kuralları var:

  • Field accessor'ları managed bir byref döndürüyor — C#'taki ref string, IL'de string& oluyor — ve alanı hem okumayı hem yazmayı sağlayan da bu. Byref'i düşürüp düz string gösteren bir decompiler, bir kopya döndüren ve artık alanı alias'lamayan bir accessor üretir.
  • Method accessor'ları hedef instance'ı ilk parametre olarak alıyor, kalan parametreler ise private metodun imzasıyla birebir örtüşüyor. IL'deki bir çağrı yalnızca accessor'a referans verir, private metoda asla.
  • Constructor accessor'ları biçimi tersine çeviriyor: instance parametresi yok, dönüş tipi yapılandırılan türü adlandırıyor ve Name hiç kullanılmıyor — runtime, parametre listesini o türe karşı eşleştiriyor. Blob'da yalnızca tür ve sıfır sayıda adlandırılmış argüman var. Yazı, bir constructor accessor için Name üretmenin yanlış olacağını, bazı runtime'ların bunu reddettiğini uyarıyor.

StaticMethod ve StaticField varyantları ise, hedef static olduğu için instance parametresini çıkararak set'i tamamlıyor.

Decompiler'lar niyeti yeniden inşa etmek zorunda

Yazı, Glass.NET decompiler'ın her durumu nasıl ele aldığını adım adım anlatıyor: türü blob'dan okuyor, Name varsa onu geri getiriyor ve özgün static extern bildirimini yeniden üretiyor. Bu yeniden yapılandırma tam olmalı — parametre ve dönüş tipleri tam olarak korunmalı, yoksa runtime bağlaması yeniden derlemede başarısız olur. Ve üye referans tablosu da talimat akışı da accessor'ın hiçbir şeye referans vermediğini gösterdiği için, üye adı yalnızca serialize edilmiş blob'dan kurtarılabilir.

Neden önemli

UnsafeAccessor, .NET kodunun iç üyelere ulaşmasının beklenen biçiminde bir değişimi temsil ediyor: runtime reflection yerine, derleme zamanında bildirilen, JIT'te çözümlenen, doğrudan çağrılı bir yol ve bunun ima ettiği performans özellikleri. dev.to analizü, mekanizmanın aynı zamanda talimat akışları üzerine kurulmuş araçlara neredeyse görünmez olan bir metadata sözleşmesi olduğunu gösteriyor. Decompiler'lar, statik analiz araçları ve IL weaver'ların bu metotların gerçekte ne yaptığını anlamak için attribute blob'unu okumayı öğrenmeleri gerekiyor ve field accessor'lardaki byref dönüş ya da constructor'larda bilinçli olarak kullanılmayan Name gibi ayrıntılar, tam da saf araçların kırıldığı yerler.

  • #dotnet
  • #jit
  • #decompilation
  • #reflection
  • #il-metadata