Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
ARM64'te Windows, zaman uyumsuz donanım tarafından oluşturulan özel durumlar ve zaman uyumlu yazılım tarafından oluşturulan özel durumlar için aynı yapılandırılmış özel durum işleme mekanizmasını kullanır. Dile özgü özel durum işleyicileri, dil yardımcı işlevleri kullanılarak yapılandırılmış Windows özel durum işleme üzerine oluşturulur. Bu belgede ARM64'teki Windows özel durum işleme açıklanmaktadır. Microsoft ARM derleyicisi ve MSVC derleyicisi tarafından oluşturulan kod tarafından kullanılan dil yardımcılarını gösterir.
Hedefler ve motivasyon
İstisna geri alma veri kuralları ve bu açıklama şunları amaçlamaktadır:
Her durumda kod yoklama olmadan geri sarmaya izin vermek için yeterli açıklama sağlayın.
Kodun çözümlenmesi için kodun sayfalanması gerekir. Yararlı olduğu bazı durumlarda (izleme, örnekleme, hata ayıklama) geri sarmayı önler.
Kodu analiz etmek karmaşıktır; derleyicisi, yalnızca unwinder'ın kodunu çözebileceği yönergeleri oluşturmaya dikkat etmelidir.
Geri alma işlemi, geri alma kodları kullanılarak tam olarak açıklanamazsa, bazı durumlarda yönerge kod çözme işlemine geri dönmesi gerekir. Yönerge kod çözme genel karmaşıklığı artırır ve ideal olarak kaçınılmalıdır.
Prolog ve epilogun ortasında çözülmeyi destekler.
- Geri sarma, Windows'ta istisna işlemenin yanı sıra başka amaçlar için de kullanılır. Bir prolog veya epilog kod dizisinin ortasındayken bile kodun doğru şekilde geri çözülmesi kritik önem taşır.
Çok az yer kaplar.
Geri sarma kodları, ikili boyutu önemli ölçüde artıracak şekilde toplanmamalıdır.
Geri sarma kodlarının bellekte kilitlenme olasılığı yüksek olduğundan, küçük bir ayak izi, yüklenen her ikili dosya için minimum ek yük sağlar.
Varsayımlar
Bu varsayımlar özel durum işleme açıklamasında yapılır:
Prolog'lar ve epiloglar birbirini yansıtma eğilimindedir. Bu ortak özelliğin avantajlarından yararlanarak, geri almayı açıklamak için gereken meta verilerin boyutu büyük ölçüde azaltılabilir. İşlevin gövdesinde, prolog işlemlerinin geri alınıp alınmadığı veya epilog işlemlerinin ileriye doğru yapılması önemli değildir. Her ikisi de aynı sonuçları üretmelidir.
İşlevler bütünde nispeten küçük olma eğilimindedir. Veri yerleşiminin en verimli şekilde yapılabilmesi için uzayda yapılan çeşitli iyileştirmeler bu gerçeğe dayanır.
Epiloglarda koşullu kod yoktur.
Ayrılmış çerçeve işaretçisi yazmaç: girişte başka bir yazmaçta () kaydedilirse, o yazmaç işlev boyunca aynı kalır. Orijinalinin herhangi bir zamanda geri döndürülebileceği anlamına gelir.
başka bir kayıtta kaydedilmediği sürece, yığın işaretçisinin tüm manipülasyonu kesinlikle prolog ve epilog içinde gerçekleşir.
Yığın çerçevesi düzeni, sonraki bölümde açıklandığı gibi düzenlenmiştir.
ARM64 yığın çerçevesi düzeni
İşlevler için yığın çerçevesi düzenini gösteren diyagram.yığın çerçevesi düzeni
Çerçeve zincirleme işlevler için ve çifti, optimizasyon gereksinimlerine bağlı olarak yerel değişken alanında herhangi bir konuma kaydedilebilir. Amaç, çerçeve işaretçisi () veya yığın işaretçisi () kullanarak tek bir yönergeyle ulaşılabilecek yerellerin sayısını en üst düzeye çıkarmaktır. Ancak, işlevleri için zincirlenmeli ve yığının alt kısmına işaret etmelidir. Kayıt-çifti adresleme modu kapsamının daha iyi olmasını sağlamak için, kalıcı olmayan yazmaç kaydetme alanları Yerel alan yığınının en üstüne yerleştirilir. En verimli prolog dizilerinden birkaçını gösteren örnekler aşağıda verilmiştir. Açıklık ve daha iyi önbellek yerelliği sağlamak için, çağrıyla kaydedilen yazmaçların tüm kurallı prologlarda depolama düzeni "artan sırada" olmalıdır. aşağıda alanı hariç yığının tamamının boyutunu temsil eder. ve sırasıyla yerel alan boyutunu (çiftin kaydetme alanı dahil) ve giden parametre boyutunu belirtir.
Zincirlenmiş, #localsz = 512
stp x19,x20,[sp,#-96]! // pre-indexed, save in 1st FP/INT pair stp d8,d9,[sp,#16] // save in FP regs (optional) stp x0,x1,[sp,#32] // home params (optional) stp x2,x3,[sp,#48] stp x4,x5,[sp,#64] stp x6,x7,[sp,#82] stp x29,lr,[sp,#-localsz]! // save <x29,lr> at bottom of local area mov x29,sp // x29 points to bottom of local sub sp,sp,#outsz // (optional for #outsz != 0)Zincirli, #localsz 512
stp x19,x20,[sp,#-96]! // pre-indexed, save in 1st FP/INT pair stp d8,d9,[sp,#16] // save in FP regs (optional) stp x0,x1,[sp,#32] // home params (optional) stp x2,x3,[sp,#48] stp x4,x5,[sp,#64] stp x6,x7,[sp,#82] sub sp,sp,#(localsz+outsz) // allocate remaining frame stp x29,lr,[sp,#outsz] // save <x29,lr> at bottom of local area add x29,sp,#outsz // setup x29 points to bottom of local areaZincirsiz, yaprak işlevleri ( kaydedilmemiş)
stp x19,x20,[sp,#-80]! // pre-indexed, save in 1st FP/INT reg-pair stp x21,x22,[sp,#16] str x23,[sp,#32] stp d8,d9,[sp,#40] // save FP regs (optional) stp d10,d11,[sp,#56] sub sp,sp,#(framesz-80) // allocate the remaining local areaTüm yerel öğelere kullanılarak erişilir. önceki çerçeveyi gösterir. Çerçeve boyutu = 512 için, kayıt edilen alan yığının altına taşınırsa optimize edilebilir. Dezavantajı, yukarıdaki diğer düzenlerle tutarlı olmamasıdır. Ayrıca kaydedilen kayıtlar, çift kayıtlar ile önceden dizinlenmiş ve sonradan dizinlenmiş sapma adresleme modu alanının bir parçası olur.
Zincirsiz, yapraksız işlevler (Int kaydedilen alanda kaydeder)
stp x19,x20,[sp,#-80]! // pre-indexed, save in 1st FP/INT reg-pair stp x21,x22,[sp,#16] // ... stp x23,lr,[sp,#32] // save last Int reg and lr stp d8,d9,[sp,#48] // save FP reg-pair (optional) stp d10,d11,[sp,#64] // ... sub sp,sp,#(framesz-80) // allocate the remaining local areaAlternatif olarak, çift sayıda kaydedilen Int yazmaçları ile,
stp x19,x20,[sp,#-80]! // pre-indexed, save in 1st FP/INT reg-pair stp x21,x22,[sp,#16] // ... str lr,[sp,#32] // save lr stp d8,d9,[sp,#40] // save FP reg-pair (optional) stp d10,d11,[sp,#56] // ... sub sp,sp,#(framesz-80) // allocate the remaining local areaYalnızca kaydedildi:
sub sp,sp,#16 // reg save area allocation* stp x19,lr,[sp] // save x19, lr sub sp,sp,#(framesz-16) // allocate the remaining local area* Önceden dizinlenmiş
bir reg-lr , geri sarma kodları ile temsil edilmesi mümkün olmadığından, kayıt kaydetme alanı ayırma içine katlanmamıştır. Tüm yerel öğelere kullanılarak erişilir. önceki çerçeveyi gösterir.
Zincirli, #framesz = 512, #outsz = 0
stp x29,lr,[sp,#-framesz]! // pre-indexed, save <x29,lr> mov x29,sp // x29 points to bottom of stack stp x19,x20,[sp,#(framesz-32)] // save INT pair stp d8,d9,[sp,#(framesz-16)] // save FP pairYukarıdaki ilk prolog örneğine kıyasla, bu örneğin bir avantajı vardır: tüm yazmaç kaydetme yönergeleri yalnızca bir yığın ayırma yönergesi sonrasında yürütülmeye hazırdır. Bu, üzerinde yönerge düzeyi paralelliği engelleyen bir karşıt bağımlılık olmadığı anlamına gelir.
Zincirli, çerçeve boyutu 512 ( bulunmayan işlevler için isteğe bağlı)
stp x29,lr,[sp,#-80]! // pre-indexed, save <x29,lr> stp x19,x20,[sp,#16] // save in INT regs stp x21,x22,[sp,#32] // ... stp d8,d9,[sp,#48] // save in FP regs stp d10,d11,[sp,#64] mov x29,sp // x29 points to top of local area sub sp,sp,#(framesz-80) // allocate the remaining local areaİyileştirme amacıyla, "reg-pair" ve pre-/post-indexed offset adresleme modu için daha iyi kapsama alanı sağlamak üzere yerel bir bölgede herhangi bir konuma konumlandırılabilir. Çerçeve işaretçilerinin altındaki yerel değişkenlere temelinde erişilebilir.
Zincirli, çerçeve boyutu 4K, alloca() ile veya olmadan
stp x29,lr,[sp,#-80]! // pre-indexed, save <x29,lr> stp x19,x20,[sp,#16] // save in INT regs stp x21,x22,[sp,#32] // ... stp d8,d9,[sp,#48] // save in FP regs stp d10,d11,[sp,#64] mov x29,sp // x29 points to top of local area mov x15,#(framesz/16) bl __chkstk sub sp,sp,x15,lsl#4 // allocate remaining frame // end of prolog ... sub sp,sp,#alloca // more alloca() in body ... // beginning of epilog mov sp,x29 // sp points to top of local area ldp d10,d11,[sp,#64] ... ldp x29,lr,[sp],#80 // post-indexed, reload <x29,lr>
ARM64 özel durum işleme bilgileri
Kayıtlar
Kayıtlar , pe ikilisindeki her yığın işleme işlevini açıklayan sıralı bir sabit uzunluklu öğe dizisidir. "Yığın işleme" ifadesi önemlidir: Yerel depolama birimine ihtiyaç duymayan ve geçici olmayan kayıtları kaydetmeye/geri yüklemeye ihtiyaç duymayan yaprak işlevler bir kaydına ihtiyaç duymaz. Alan kazanmak için bu kayıtlar açıkça atlanmalıdır. Bu işlevlerden birinde geri sarma, dönüş adresini doğrudan üzerinden alarak çağırana taşıyabilir.
ARM64 için her kayıt 8 bayt uzunluğundadır. Her kaydın genel formatı, işlev başlangıcının 32 bitlik RVA'sını ilk kelimeye yerleştirir ve ardından ikinci kelime, ya değişken uzunlukta bir bloğun işaretçisini ya da kurallı bir fonksiyon geri sarma dizisini tanımlayan paketlenmiş bir kelime içerir.
.pdata kayıt düzeni..pdata kayıt düzeni
Alanlar aşağıdaki gibidir:
İşlev Başlatma RVA'sı , işlevin başlangıcının 32 bit RVA'dır.
Bayrak , ikinci sözcüğün kalan 30 bitinin nasıl yorumlandığını gösteren 2 bitlik bir alandır. Bayrak 0 ise, kalan bitler bir Özel Durum Bilgileri RVA'sı oluşturur (en düşük iki bit örtük olarak 0 olur). Bayrak sıfır değilse, kalan bitler Paketlenmiş Geri Sarma verilerinin yapısını oluşturur.
Özel Durum Bilgileri RVA , bölümünde depolanan değişken uzunluklu özel durum bilgisi yapısının adresidir. Bu verilerin 4 bayt hizalanmış olması gerekir.
Paketlenmiş Geri Sarma Verileri , kurallı bir form varsayılarak işlevden geri sarmak için gereken işlemlerin sıkıştırılmış bir açıklamasıdır. Bu durumda kaydı gerekmez.
Kayıtlar
Paketlenmiş geri sarma biçimi bir işlevin geri sarmasını açıklamak için yetersizse, değişken uzunlukta bir kayıt oluşturulmalıdır. Bu kaydın adresi, kaydın ikinci sözcüğünde depolanır. biçimi , paketlenmiş değişken uzunlukta bir sözcük kümesidir:
.xdata kayıt düzeni..xdata kayıt düzeni
Bu veriler dört bölüme ayrılır:
Yapının genel boyutunu açıklayan ve önemli işlev verileri sağlayan 1 sözcüklü veya 2 sözcüklü üst bilgi. İkinci sözcük yalnızca Epilog Sayısı ve Kod Sözcükleri alanları 0 olarak ayarlandıysa bulunur. Üst bilgi şu bit alanlarına sahiptir:
a. İşlev Uzunluğu 18 bitlik bir alandır. İşlevin toplam uzunluğunu bayt cinsinden ve 4'e bölünerek gösterir. bir işlev 1M'den büyükse, işlevi tanımlamak için birden çok ve kayıt kullanılmalıdır. Daha fazla bilgi için Büyük işlevler bölümüne bakın.
b. Vers , 2 bitlik bir alandır. Kalan sürümünü açıklar. Şu anda yalnızca sürüm 0 tanımlanmıştır, bu nedenle 1-3 değerlerine izin verilmez.
ç. X , 1 bitlik bir alandır. Özel durum verilerinin varlığını (1) veya yokluğunu (0) gösterir.
ö. E , 1 bitlik bir alandır. Tek bir epilogun açıklamasını içeren bilgilerin, ileride daha fazla kapsam kelimesi (0) gerektirmek yerine, üst bilgide (1) paketlendiğini gösterir.
e. Epilog Count, E bitin durumuna bağlı olarak iki anlamı olan 5 bitlik bir alandır:
E 0 ise, bölüm 2'de açıklanan toplam epilog kapsamlarının sayısını belirtir. İşlevde 31'den fazla kapsam varsa, uzantı sözcüğünün gerekli olduğunu belirtmek için Kod Sözcükleri alanının 0 olarak ayarlanması gerekir.
E 1 ise, bu alan yalnızca ve yalnızca epiloğu tanımlayan ilk kesme kodunun indeksini belirtir.
f. Kod Sözcükleri , bölüm 3'teki tüm geri sarma kodlarını içermek için gereken 32 bit sözcük sayısını belirten 5 bitlik bir alandır. 31'den fazla sözcük (yani 124 geri sarma kodu) gerekiyorsa, uzantı sözcüğünün gerekli olduğunu belirtmek için bu alanın 0 olması gerekir.
g. Genişletilmiş Kapsam Sayısı ve Genişletilmiş Kod Sözcükleri sırasıyla 16 bit ve 8 bit alanlardır. Alışılmadık derecede çok sayıda epilog veya çok sayıda geri sarma kodu sözcüğünü kodlamak için daha fazla alan sağlar. Bu alanları içeren uzantı sözcüğü yalnızca ilk üst bilgi sözcüğündeki Epilog Sayısı ile Kod Sözcükleri alanlarının her ikisi de 0 olduğunda bulunur.
Kapsam sayısı sıfır değilse, bir sözcükle paketlenmiş olan kapsam kapsamları hakkındaki bilgilerin listesi üst bilgiden ve isteğe bağlı genişletilmiş üst bilgiden sonra gelir. Başlangıç ofsetinin artış sırasına göre depolanırlar. Her kapsam aşağıdaki bitleri içerir:
a. Epilog Başlangıç Uzaklığı , işlevin başlangıcına göre, bölmenin bayt cinsinden uzaklığını 4'e bölen 18 bitlik bir alandır.
b. Res , gelecekteki genişletme için ayrılmış 4 bitlik bir alandır. Değeri 0 olmalıdır.
ç. Epilog Başlangıç Dizini 10 bitlik bir alandır (Genişletilmiş Kod Sözcüklerinden 2 bit daha fazla). Bu epilogu açıklayan ilk geri sarma kodunun bayt dizinini belirtir.
Epilog kapsamları listesinden sonra, ilerleyen bölümlerde ayrıntılı olarak açıklanan, çözme kodları içeren bir bayt dizisi gelir. Bu dizi, en yakın tam kelime sınırına kadar uçtan doldurulur. Geri sarma kodları bu diziye yazılır. İşlevin gövdesine en yakın olanla başlar ve işlevin kenarlarına doğru hareket eder. Her geri sarma kodunun baytları büyük uç sırada depolanır, böylece en önemli bayt ilk olarak getirilir ve bu da işlemi ve kodun geri kalanının uzunluğunu tanımlar.
Son olarak, geri alma kodu baytlarının ardından, üst bilgi içindeki X biti 1 olarak ayarlandıysa, özel durum işleyicisi bilgileri gelir. Bu, özel durum işleyicisinin adresini sağlayan tek Özel Durum İşleyici RVA'sı'ndan oluşur. Hemen ardından özel durum işleyicisi tarafından gereken değişken uzunlukta veri miktarı gelir.
kaydı, ilk 8 baytı elde etmeyi ve bunları kullanarak kaydın tam boyutunu, sonrasındaki değişken boyutlu özel durum verilerinin uzunluğu çıkarılarak hesaplamayı mümkün kılacak şekilde tasarlanmıştır. Aşağıdaki kod parçacığı kayıt boyutunu hesaplar:
ULONG ComputeXdataSize(PULONG Xdata)
{
ULONG Size;
ULONG EpilogScopes;
ULONG UnwindWords;
if ((Xdata[0] >> 22) != 0) {
Size = 4;
EpilogScopes = (Xdata[0] >> 22) & 0x1f;
UnwindWords = (Xdata[0] >> 27) & 0x1f;
} else {
Size = 8;
EpilogScopes = Xdata[1] & 0xffff;
UnwindWords = (Xdata[1] >> 16) & 0xff;
}
if (!(Xdata[0] & (1 << 21))) {
Size += 4 * EpilogScopes;
}
Size += 4 * UnwindWords;
if (Xdata[0] & (1 << 20)) {
Size += 4; // Exception handler RVA
}
return Size;
}
Prolog ve her bir epilog unwind kodları içinde kendi dizini olsa da, tablo bunlar arasında paylaşılır. Hepsinin aynı kodları paylaşabilmesi tamamen mümkündür (ve hiç de yaygın değildir). (Örnek 2'ye Örnekler bölümünde bakın.) Derleyici yazarları özellikle bu durum için iyileştirme yapmalıdır. Bunun nedeni, belirtilebilen en büyük dizinin 255 olmasıdır ve bu da belirli bir işlev için toplam geri sarma kodu sayısını sınırlar.
Çözme kodları
Çözülme kodları dizisi, prologun etkilerinin nasıl geri alındığını açıkça açıklayan bir dizi ardışık dizilim havuzudur. Bunlar, işlemlerin geri alınması gereken sırayla depolanır. Geri sarma kodları, bayt dizesi olarak kodlanmış küçük bir yönerge kümesi olarak düşünülebilir. Yürütme tamamlandığında, çağıran işlevin dönüş adresi yazmaçtadır. Ayrıca, geçici olmayan tüm yazmaçlar işlev çağrıldığında değerlerine geri yüklenir.
Eğer istisnaların yalnızca bir işlev gövdesi içinde gerçekleşeceği ve asla bir prolog veya epilog içinde olmayacağı garanti edilseydi, sadece bir dizi yeterli olurdu. Ancak, Windows geri sarma modeli, kodun kısmen yürütülen bir prolog veya epilog içinden çözülebilmesini gerektirir. Bu gereksinimi karşılamak için, geri alma kodları dikkatle tasarlanmıştır, böylece her ilgili opcode ile prolog ve epilogdaki 1:1 oranında kesin bir şekilde eşleşirler. Bu tasarımın çeşitli etkileri vardır:
Geri sarma kodlarının sayısını belirleyerek prolog ve epilog uzunluğunu hesaplamak mümkündür.
Bir epilog kapsamının başlangıcından itibaren yönergeleri sayarak, eşdeğer sayıda unwind kodunu atlayabilirsiniz. Epilog tarafından yapılan kısmen yürütülen geri alma işlemini tamamlamak için bir dizinin geri kalanını yürütebiliriz.
Prolog sonundan önceki yönergeleri sayarak, geri sarma kodlarının eşdeğer sayısını atlayabilirsiniz. Yalnızca yürütmeyi tamamlamış olan prolog bölümlerini geri almak için sıranın geri kalanını yürütebiliriz.
Geri sarma kodları aşağıdaki tabloya göre kodlanmıştır. Tüm geri sarma kodları, büyük bir yığın () ayıran kod dışında tek/çift bayttır. Toplamda 22 geri sarma kodu vardır. Her çözme kodu, kısmen yürütülen prolog ve epilogların geri alınabilmesi için prolog/epilog’daki bir talimata birebir karşılık gelir.
| Kodu geri sarma | Bit ve Yorumlama |
|---|---|
alloc_s |
000xxxx: boyutu 512 olan küçük yığın ayırın (2^5 * 16). |
save_r19r20_x |
001zzzz: önceden dizinlenmiş uzaklık = -248 konumunda çifti kaydedin |
save_fplr |
01zzzzzz: konumundaki çift, ofset = 504 olarak kaydediliyor. |
save_fplr_x |
10zzzzzz: çiftini konumunda kaydedin, önceden dizinlenmiş uzaklık = -512 |
alloc_m |
11000xxx'xxxxxxxx: boyutu 32K olan büyük yığın ayırın (2^11 * 16). |
save_regp |
110010xx'xxzzzz: save pair at , offset = 504 |
save_regp_x |
110011xx'xxzzzzzz: konumunda çifti ile kaydet, ön indekslenmiş uzaklık = -512 |
save_reg |
110100xx'xxzzzzzz: kayıt defterini kaydedin , uzaklık = 504 |
save_reg_x |
1101010x'xxxzzzzz: reg öğesini konumunda kaydet, ön indeksli ofset = -256 |
save_lrpair |
1101011x'xxzzzzzz: 'i konumuna kaydet, ofset = 504 |
save_fregp |
1101100x'xxzzzzzz: çift kaydet konumunda , konum farkı = 504 |
save_fregp_x |
1101101x'xxzzzzzz: öğesini konumunda kaydet, önceden indekslenmiş ofset = -512 |
save_freg |
1101110x'xxzzzzzz: 'ün kaydını konumunda sakla, kaydırma= 504 |
save_freg_x |
11011110'xxxzzzzz: kayıt defterini önceden dizine alınan uzaklık = -256 konumunda kaydedin |
alloc_z |
11011111'zzzzzzzz: boyutu ile yığın ayırma |
alloc_l |
11100000'xxxxxxxx'xxxxxxxx'xxxxxxxx: 256M boyutunda büyük yığın tahsisi (2^24 * 16) |
set_fp |
11100001: ile ayarla |
add_fp |
11100010'xxxxxxxx: , ile kurulum yap |
nop |
11100011: geri sarma işlemi gerekmez. |
end |
11100100: geri sarma kodunun sonu. Epilog bölümünde belirtir. |
end_c |
11100101: Geçerli zincirlenmiş kapsamdaki geri sarma kodunun sonu. |
save_next |
11100110: Bir sonraki kayıt çiftini kaydedin. |
save_any_xreg |
11100111'0pxrrrrr'00oooooo: yazmaçları (register) kaydet
|
save_any_dreg |
11100111'0pxrrrrr'01oooooo: kaydet kayıt(ları)
|
save_any_qreg |
11100111'0pxrrrrr'10oooooo: yazmaç(ları) sakla
|
save_zreg |
11100111'0oo0rrrr'11oooooo: değerindeki kaydı konumuna, ile arasına kaydedin. |
save_preg |
11100111'0oo1rrrr'11oooooooo: reg'i 'de konumuna kaydedin ( ile arasında; değerleri ayrılmıştır) |
| 11100111'1yyyyyyy': ayrılmış | |
| 11101xx: Yalnızca asm yordamları için oluşturulan aşağıdaki özel yığın örnekleri için ayrılmıştır | |
| 11101000: Özel yığın | |
| 11101001: için özel yığın | |
| 11101010: Özel yığın için | |
| 11101011: için özel yığın | |
| 11101100: için özel yığın | |
| 11101101: ayrılmış | |
| 11101110: rezerve | |
| 11101111: ayrılmış | |
| 11110xxx: ayrılmış | |
| 11111000'yyyyyyyy : ayrılmış | |
| 11111001'yyyyyyyy'yyyyyyyy : ayrılmış | |
| 11111010'yyyyyyyy'yyyyyyyy'yyyyyyyy : ayrılmış | |
| 11111011'yyyyyyyy'yyyyyyyy'yyyyyyyy'yyyyyyyy : rezerve edilmiş | |
pac_sign_lr |
11111100: İade adresini ile imzalayın |
| 11111101: ayrılmış | |
| 11111110: ayrılmış | |
| 11111111: ayrılmış |
Birden çok baytı kapsayan büyük değerler içeren yönergelerde, en önemli bitler önce depolanır. Bu tasarım, kodun yalnızca ilk baytını arayarak geri sarma kodunun bayt cinsinden toplam boyutunu bulmayı mümkün kılar. Her geri sarma kodu, bir prolog veya epilog'daki bir talimatla tam olarak eşleştirildiğinden, prolog veya epilog'un boyutunu hesaplayabilirsiniz. Sıra başından sonuna kadar yürüyün ve karşılık gelen opcode'un uzunluğunu belirlemek için bir arama tablosu veya benzer bir cihaz kullanın.
Bir prologda son dizinlenmiş uzaklık adreslemesine izin verilmez. Tüm uzaklık aralıkları (#Z), tüm kaydetme alanları için 248'in yeterli olduğu (10 Int yazmacı + 8 FP yazmaç + 8 giriş yazmacı) dışında adresleme kodlaması ile eşleşmektedir.
bir kayıt çifti için kaydetmeyi izlemelidir: , , , , veya başka bir . , veya ile yalnızca olduğunda kullanılabilir. Bir sonraki yazmaç çiftini artan sayısal sırayla bir sonraki bellek yığını alanına kaydeder. aynı türdeki son yazmaç dışında kullanılmamalıdır.
Normal dönüş ve atlama yönergelerinin boyutları aynı olduğundan, kuyruk çağrısı senaryolarında ayrı ayrı geri sarma koduna gerek yoktur.
, iyileştirme amacıyla bitişik olmayan işlev parçalarını işlemek üzere tasarlanmıştır. Geçerli kapsamdaki geri sarma kodlarının sonunu gösteren bir , bir gerçek ile biten başka bir geri sarma kodu serisiyle takip edilmelidir. ve arasındaki geri sarma kodları, üst bölgedeki prolog işlemlerini temsil eder (bir "hayalet" prolog). Diğer ayrıntılar ve örnekler aşağıdaki bölümde açıklanmıştır.
Paketlenmiş geri sarma verileri
Aşağıda açıklanan standart biçimi izleyen başlangıç ve bitiş bloklarına sahip işlevler için paketlenmiş çözülme verileri kullanılabilir. Kayıt gereksinimini tamamen ortadan kaldırır ve geriye doğru veri sağlama maliyetini önemli ölçüde azaltır. Standart ön ve son ekler, basit bir fonksiyonun yaygın gereksinimlerini karşılayacak şekilde tasarlanmıştır: Özel durum işleyici gerektirmeyen ve kurulum ile kaldırma işlemlerini standart sırada yapan bir fonksiyondur.
Paketlenmiş geri sarma verilerini içeren bir kaydın biçimi şöyle görünür:
Sıkıştırılmış geri sarma verileriyle .pdata kaydı.Sıkıştırılmış geri sarma verileriyle .pdata kaydı
Alanlar aşağıdaki gibidir:
- İşlev Başlatma RVA'sı , işlevin başlangıcının 32 bit RVA'dır.
- Bayrak , yukarıda açıklandığı gibi aşağıdaki anlamlara sahip 2 bitlik bir alandır:
- 00 = paketlenmiş geri alma verileri kullanılmaz; kalan bitler bir kayda işaret eder
- 01 = kapsamın başında ve sonunda tek bir prolog ve epilog ile kullanılan paketlenmiş geri sarma verileri
- 10 = herhangi bir giriş veya bitiş kodu olmadan kod için kullanılan paketlenmiş çözülme verileri. Ayrılmış işlev kesimlerini tanımlamak için kullanışlıdır
- 11 = ayrılmış.
- İşlev Uzunluğu , işlevin tamamının bayt cinsinden uzunluğunu 4'e bölen 11 bitlik bir alandır. İşlev 8k'den büyükse bunun yerine tam kayıt kullanılmalıdır.
- Çerçeve Boyutu , bu işlev için ayrılan yığın bayt sayısını 16'ya bölen 9 bitlik bir alandır. (8k-16) bayttan daha fazla yığın ayıran işlevlerin tam bir kaydı kullanması gerekir. Yerel değişken alanı, giden parametre alanı, çağıran tarafından kaydedilen Int ve FP alanı ve ana parametre alanını içerir. Dinamik ayırma alanını dışlar.
- CR , işlevin çerçeve zincirini ayarlamak ve bağlantı döndürmek için ek yönergeler içerip içermediğini gösteren 2 bitlik bir bayraktır:
- 00 = zincirsiz işlev, çift yığına kaydedilmez
- 01 = zincirsiz işlev, yığına kaydedilir
- 10 = işaretli iade adresiyle zincirlenmiş işlev
- 11 = zincirlenmiş işlev, bir depo/yük çifti yönergesi prolog/epilog içinde kullanılır
- H , işlevin tamsayı parametre yazmaçlarını (x0-x7) işlevin en başında depolayarak kaydedip barındırmadığını gösteren 1 bitlik bir bayraktır. (0 = ev kayıtları değil, 1 = ev kayıtları).
- RegI , kurallı yığın konumuna kaydedilen geçici olmayan INT yazmaçlarının (x19-x28) sayısını gösteren 4 bitlik bir alandır.
- RegF , kurallı yığın konumuna kaydedilen geçici olmayan FP yazmaçlarının (d8-d15) sayısını gösteren 3 bitlik bir alandır. (RegF=0: hiçbir FP yazmaç kaydedilmez; RegF0: RegF+1 FP yazmaçları kaydedilir). Paketlenmiş geri sarma verileri, yalnızca bir FP yazmaç kaydeden işlev için kullanılamaz.
Yukarıdaki bölümdeki 1, 2 (giden parametre alanı olmadan), 3 ve 4 kategorilerine giren kanonik prologlar, paketlenmiş unwind formatı ile temsil edilebilir. Kurallı işlevlerin epilogları benzer bir biçimi takip eder, ancak H'nin bir etkisi yoktur, talimatı eklenmez ve epilogda adımların sırası ve her bir adımda yer alan talimatlar ters çevrilir. Paketlenmiş algoritması aşağıdaki tabloda ayrıntılı olarak verilen şu adımları izler:
0. Adım: Her alanın boyutunu önceden hesaplama.
1. Adım: İade adresini imzalayın.
2. Adım: Int callee-saved yazmaçlarını kaydedin.
3. Adım: Bu adım, ilk bölümlerdeki tür 4'e özgüdür. int alanının sonuna kaydedilir.
4. Adım: FP çağrılı kayıtlı yazmaçları kaydedin.
5. Adım: Giriş bağımsız değişkenlerini ana parametre alanına kaydedin.
6. Adım: Yerel alan, çift ve giden parametre alanı da dahil olmak üzere kalan yığını ayırın. 6a kurallı tip 1'e karşılık gelir. 6b ve 6c kurallı tür 2 içindir. 6d ve 6e hem tür 3 hem de tür 4 içindir.
| Adım # | Bayrak değerleri | Talimat sayısı | Opcode | Kodu geri sarma |
|---|---|---|---|---|
| 0 | #intsz = RegI * 8;if (CR==01) #intsz += 8; // lr#fpsz = RegF * 8;if(RegF) #fpsz += 8;#savsz=((#intsz+#fpsz+8*8*H)+0xf)&~0xf)#locsz = #famsz - #savsz |
|||
| 1 | CR == 10 | 1 | pacibsp |
pac_sign_lr |
| 2 | 0 RegI= 10 | RegI / 2 + RegI % 2 |
stp x19,x20,[sp,#savsz]!stp x21,x22,[sp,#16]... |
save_regp_xsave_regp... |
| 3 | CR == 01* | 1 | str lr,[sp,#(intsz-8)]* |
save_reg |
| 4 | 0 RegF= 7 | (RegF + 1) / 2 + (RegF + 1) % 2) |
stp d8,d9,[sp,#intsz]**stp d10,d11,[sp,#(intsz+16)]...str d(8+RegF),[sp,#(intsz+fpsz-8)] |
save_fregp...save_freg |
| 5 | H == 1 | 4 | stp x0,x1,[sp,#(intsz+fpsz)]stp x2,x3,[sp,#(intsz+fpsz+16)]stp x4,x5,[sp,#(intsz+fpsz+32)]stp x6,x7,[sp,#(intsz+fpsz+48)] |
nopnopnopnop |
| 6a | (CR == 10 || CR == 11) & = 512 |
2 | stp x29,lr,[sp,#-locsz]!mov x29,sp*** |
save_fplr_xset_fp |
| 6b | (CR == 10 || CR == 11) & 512 = 4080 |
3 | sub sp,sp,#locszstp x29,lr,[sp,0]add x29,sp,0 |
alloc_msave_fplrset_fp |
| 6c | (CR == 10 || CR == 11) & 4080 |
4 | sub sp,sp,4080sub sp,sp,#(locsz-4080)stp x29,lr,[sp,0]add x29,sp,0 |
alloc_malloc_s/alloc_msave_fplrset_fp |
| 6d | (CR == 00 || CR == 01) & = 4080 |
1 | sub sp,sp,#locsz |
alloc_s/alloc_m |
| 6e | (CR == 00 || CR == 01) & 4080 |
2 | sub sp,sp,4080sub sp,sp,#(locsz-4080) |
alloc_malloc_s/alloc_m |
* Eğer CR == 01 ve RegI tek sayıysa, 3. adımla ve 2. adımdaki son birleşerek tek bir oluşturulur.
** RegI == 0, CR != 01 ve RegF != 0 ise, kayan nokta işlemi için ilk adım, FP/SIMD kaydetme alanı için alan ayırmak amacıyla sp'yi önceden azaltmaktır.
Epilogda öğesine karşılık gelen bir yönerge bulunmamaktadır. Paketlenmiş geri sarma verileri, bir işlev 'den geri yüklenmesini gerektiriyorsa kullanılamaz.
Kısmi prologları ve epilogları çözme
En yaygın çözme durumlarında, istisna veya çağrı, işlevin prolog ve tüm epiloglarından uzakta, gövdesinde gerçekleşir. Bu durumlarda, geri açma basit bir işlemdir: geri açıcı, kodları geri açma dizisinde yürütür. Dizin 0'dan başlar ve bir opcode algılanana kadar devam eder.
Bir prolog veya epilog yürütülürken bir özel durum veya kesinti oluşması durumunda doğru şekilde gevşemek daha zordur. Bu gibi durumlarda, yığın çerçevesi yalnızca kısmen oluşturulur. Sorun, tam olarak ne yapıldığını belirlemek ve doğru şekilde geri almaktır.
Örneğin, şu giriş ve epilog dizisini alın:
0000: stp x29,lr,[sp,#-256]! // save_fplr_x 256 (pre-indexed store)
0004: stp d8,d9,[sp,#224] // save_fregp 0, 224
0008: stp x19,x20,[sp,#240] // save_regp 0, 240
000c: mov x29,sp // set_fp
...
0100: mov sp,x29 // set_fp
0104: ldp x19,x20,[sp,#240] // save_regp 0, 240
0108: ldp d8,d9,[sp,224] // save_fregp 0, 224
010c: ldp x29,lr,[sp],#256 // save_fplr_x 256 (post-indexed load)
0110: ret lr // end
Her işlem kodunun yanında bu işlemi açıklayan uygun geri sarma kodu bulunur. Prolog için geri sarma kodları serisinin, epilog için geri sarma kodlarının tam bir ayna görüntüsü olduğunu görebilirsiniz (epilogun son yönergesi sayılmaz). Bu yaygın bir durum: Bu nedenle her zaman prolog için geri sarma kodlarının prolog'un yürütme sırasına göre ters sırada depolandığını varsayarız.
Bu nedenle hem prolog hem de epilog için ortak bir geri alma kodları kümesiyle baş başa kalırız:
, , , , ,
Normal düzende olduğundan, epilog olayı basittir. Epilog içindeki ofset 0'dan başlayarak (işlevdeki ofset 0x100'den başlar), henüz bir temizleme yapılmadığından tam geri sarma dizisinin yürütülmesini bekleriz. Eğer kendimizi epilogta ofset 2 olarak bir yönerge içinde bulursak, ilk geri alma kodunu atlayarak başarıyla geri alabiliriz. Bu durumu genelleştirebilir ve opkodlar ile unwind kodları arasında 1:1 eşleme olduğunu varsayabiliriz. Ardından, epilogdaki n yönergesinden geri sarmayı başlatmak için ilk n geri alma kodlarını atlayıp oradan yürütmeye başlamamız gerekir.
Bunun tersi dışında benzer bir mantığın prolog için çalıştığı ortaya çıktı. Prologdaki 0 ofsetten geri sarmaya başlarsak, hiçbir şey yürütmemeyi amaçlarız. 2 uzaklığından (bu da bir yönergedir) geri sarıyorsak, sonundan bir geri sarma kodu olan geri alma dizisini yürütmeye başlamak istiyoruz. (Kodların ters sırada depolandığını unutmayın.) Burada da genelleştirebiliriz: prologdaki n yönergesinden geri sarmayı kaldırmaya başlarsak, kod listesinin sonundan n geri sarma kodlarını yürütmeye başlamalıyız.
Prolog ve epilog kodları her zaman tam olarak eşleşmez, bu nedenle unwind dizisinin birkaç kod dizisi içermesi gerekebilir. Kodları işlemeye nereden başlayacağınızı belirlemek için aşağıdaki mantığı kullanın:
İşlevin gövdesinden geri sarıyorsanız, 0 dizininde geri sarma kodlarını yürütmeye başlayın ve bir işlem koduna gelene kadar devam edin.
Bir epilog içinden geri sarıyorsanız, başlangıç noktası olarak epilog kapsamıyla birlikte sağlanan kapsama özgü başlangıç dizinini kullanın. Söz konusu bilgisayarın, tanımlamanın başlangıcından itibaren kaç bayt olduğunu hesapla. Ardından, önceden yürütülen tüm yönergeler hesaba aktarılana kadar geri alma kodlarını atlayarak geri alma kodları arasında ilerleyin. Ardından bu noktadan yürütmeye başlayın.
Prolog içinden geri sarıyorsanız başlangıç noktanız olarak dizin 0'ı kullanın. Verilen dizideki prolog kodunun uzunluğunu hesaplayın ve ardından ilgili program sayacının prologun sonundan kaç bayt uzakta olduğunu hesaplayın. Ardından, henüz yürütülmemiş tüm yönergeler hesaba aktarılana kadar geri alma kodlarını atlayarak geri alma kodları arasında ilerleyin. Ardından bu noktadan yürütmeye başlayın.
Bu kurallar, prolog için geri sarma kodlarının dizide her zaman ilk olması gerektiği anlamına gelir. Ve bunlar, genel anlamda kod bloğunun içinden geri çekilmeyi sağlamak için kullanılan kodlardır. Epilog'a özgü kod dizileri hemen ardından gelmelidir.
İşlev parçaları
Kod iyileştirme amaçları ve diğer nedenlerle, bir işlevi ayrılmış parçalara bölmek (bölgeler olarak da adlandırılır) tercih edilebilir. Bölündüğünde, sonuçta elde edilen her işlev parçası kendi ayrı (ve büyük olasılıkla ) kaydını gerektirir.
Kendi prologu olan her ayrılmış ikincil parça için, prologunda yığın ayarlaması yapılmamış olması beklenir. İkincil bir bölge için gereken tüm yığın alanı, onun üst bölgesi (veya “konak bölgesi” olarak adlandırılır) tarafından önceden ayrılmalıdır. Bu ön atama, yığın işaretçisi işlemesini kesinlikle işlevin özgün prolog'unda tutar.
İşlev parçalarının tipik bir örneği, derleyicinin bir kod bölgesini konak işlevinin dışına taşıyabileceği "kod ayrımı"dır. Kod ayrımından kaynaklanabilir üç olağan dışı durum vardır.
Örnek
(bölge 1: başlangıç)
stp x29,lr,[sp,#-256]! // save_fplr_x 256 (pre-indexed store) stp x19,x20,[sp,#240] // save_regp 0, 240 mov x29,sp // set_fp ...(bölge 1: bitiş)
(bölge 3: başlangıç)
...(bölge 3: bitiş)
(bölge 2: başlangıç)
... mov sp,x29 // set_fp ldp x19,x20,[sp,#240] // save_regp 0, 240 ldp x29,lr,[sp],#256 // save_fplr_x 256 (post-indexed load) ret lr // end(bölge 2: bitiş)
Sadece Prolog (bölge 1: tüm epiloglar ayrılmış bölgelerdedir):
Yalnızca prolog açıklanmalıdır. Bu prolog, sıkıştırılmış biçimde temsil edilemiyor. Tam durumda, Epilog Sayısı = 0 ayarlanarak temsil edilebilir. Yukarıdaki örnekte 1. bölgeye bakın.
Geri sarma kodları: , , , .
Yalnızca epiloglar (bölge 2: prolog ana bölgededir)
Kontrol bu bölgeye atladığında, tüm prolog kodlarının yürütülmüş olduğu varsayılmaktadır. Kısmi çözülme, normal bir işlevdeki gibi epiloglarda da meydana gelebilir. Bu tür bir bölge, sıkıştırılmış ile temsil edilemez. Tam kayıt, ve arasında köşeli ayraçlarla belirtilen "sanal" bir giriş önsözüyle kodlanabilir. Öncül değeri, prolog boyutunun sıfır olduğunu gösterir. Tek bir epilogun başlangıç dizini öğesini işaret etmektedir .
Bölge 2 için geri sarma kodu: , , , , .
Ön bilgi veya epilog yok (Bölüm 3: ön bilgiler ve tüm epiloglar diğer parçalardadır).
Sıkıştırılmış biçim, Bayrak = 10 ayarıyla uygulanabilir. Tam kayıtla, Epilog Sayısı = 1. Geri sarma kodu yukarıdaki bölge 2'nin koduyla aynıdır, ancak Epilog Başlangıç Dizini de öğesine işaret eder. Kodun bu bölgesinde kısmi geri sarma hiçbir zaman gerçekleşmez.
İşlev parçalarının daha karmaşık bir diğer durumu "shrink wrapping"dir. Derleyici, bazı "callee-saved" yazmaçların kaydedilmesini işlev girişi prologunun dışına kadar geciktirmeyi seçebilir.
(bölge 1: başlangıç)
stp x29,lr,[sp,#-256]! // save_fplr_x 256 (pre-indexed store) stp x19,x20,[sp,#240] // save_regp 0, 240 mov x29,sp // set_fp ...(bölge 2: başlangıç)
stp x21,x22,[sp,#224] // save_regp 2, 224 ... ldp x21,x22,[sp,#224] // save_regp 2, 224(bölge 2: bitiş)
... mov sp,x29 // set_fp ldp x19,x20,[sp,#240] // save_regp 0, 240 ldp x29,lr,[sp],#256 // save_fplr_x 256 (post-indexed load) ret lr // end(bölge 1: bitiş)
Bölge 1'in başlangıcında yığın alanı önceden ayrılmıştır. 2. bölgenin ana bilgisayar işlevinden taşınsa bile aynı geri sarma koduna sahip olacağını görebilirsiniz.
Bölge 1: , , , . Epilog Başlangıç Dizini her zamanki gibi işaret etmektedir.
Bölge 2: , , , , , . Epilog Başlangıç Dizini, ilk geri sarma koduna işaret eder.
Büyük işlevler
Parçalar, üst bilgideki bit alanları tarafından uygulanan 1M sınırından daha büyük işlevleri açıklamak için kullanılabilir. Bunun gibi olağan dışı büyük bir işlevi tanımlamak için 1M'den küçük parçalara bölünmesi gerekir. Her parçanın, bir epilogu birden çok parçaya bölmemesi için ayarlanması gerekir.
İşlevin yalnızca ilk parçası bir prolog içerir; diğer tüm parçalar prologsuz işaretlenir. Mevcut epilog sayısına bağlı olarak, her parça sıfır veya daha fazla epilog içerebilir. Bir parçadaki her bir epilog kapsamının, işlevin başlangıcına değil, parçanın başlangıcına göre başlangıç ofsetini belirttiğini unutmayın.
Bir parçanın prologu ve epilogu yoksa, işlevin gövdesinden nasıl çözüleceğini açıklamak için yine de kendi (ve muhtemelen ) kaydına ihtiyaç duyar.
Örnekler
Örnek 1: Çerçeve zincirli, kompakt formda
|Foo| PROC
|$LN19|
str x19,[sp,#-0x10]! // save_reg_x
sub sp,sp,#0x810 // alloc_m
stp fp,lr,[sp] // save_fplr
mov fp,sp // set_fp
// end of prolog
...
|$pdata$Foo|
DCD imagerel |$LN19|
DCD 0x416101ed
;Flags[SingleProEpi] functionLength[492] RegF[0] RegI[1] H[0] frameChainReturn[Chained] frameSize[2080]
Örnek 2: Çerçeve zincirli, ayna Prolog ve Epilog ile tam form
|Bar| PROC
|$LN19|
stp x19,x20,[sp,#-0x10]! // save_regp_x
stp fp,lr,[sp,#-0x90]! // save_fplr_x
mov fp,sp // set_fp
// end of prolog
...
// begin of epilog, a mirror sequence of Prolog
mov sp,fp
ldp fp,lr,[sp],#0x90
ldp x19,x20,[sp],#0x10
ret lr
|$pdata$Bar|
DCD imagerel |$LN19|
DCD imagerel |$unwind$cse2|
|$unwind$Bar|
DCD 0x1040003d
DCD 0x1000038
DCD 0xe42291e1
DCD 0xe42291e1
;Code Words[2], Epilog Count[1], E[0], X[0], Function Length[6660]
;Epilog Start Index[0], Epilog Start Offset[56]
;set_fp
;save_fplr_x
;save_r19r20_x
;end
Epilog Başlangıç Dizini [0] aynı Prolog unwind kodu dizisine işaret eder.
Örnek 3: Değişken zincirsiz İşlev
|Delegate| PROC
|$LN4|
sub sp,sp,#0x50
stp x19,lr,[sp]
stp x0,x1,[sp,#0x10] // save incoming register to home area
stp x2,x3,[sp,#0x20] // ...
stp x4,x5,[sp,#0x30]
stp x6,x7,[sp,#0x40] // end of prolog
...
ldp x19,lr,[sp] // beginning of epilog
add sp,sp,#0x50
ret lr
AREA |.pdata|, PDATA
|$pdata$Delegate|
DCD imagerel |$LN4|
DCD imagerel |$unwind$Delegate|
AREA |.xdata|, DATA
|$unwind$Delegate|
DCD 0x18400012
DCD 0x200000f
DCD 0xe3e3e3e3
DCD 0xe40500d6
DCD 0xe40500d6
;Code Words[3], Epilog Count[1], E[0], X[0], Function Length[18]
;Epilog Start Index[4], Epilog Start Offset[15]
;nop // nop for saving in home area
;nop // ditto
;nop // ditto
;nop // ditto
;save_lrpair
;alloc_s
;end
Epilog Başlangıç Dizini [4], Prolog geri sarma kodunun ortasına işaret eder (geri sarma dizisinin bir kısmını yeniden kullanır).
Ayrıca bkz.
ARM64 ABI kurallarına genel bakış
ARM özel durum işleme