Aracılığıyla paylaş


ARM64 özel durum işleme

ARM64 üzerinde 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 Windows yapılandırılmış özel durum işleme üzerine oluşturulur. Bu belgede ARM64 üzerinde Windows'ta ö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

Veri geri sarma kurallarını ve bu açıklamayı kaldırma özel durumu şunlara yöneliktir:

  • 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.

  • Ön izlemenin ortasında ve ortasında geri sarmayı destekler.

    • Windows'ta geri sarma, özel durum işlemeden daha fazlası için kullanılır. Bir giriş veya kapsam kod dizisinin ortasındayken bile kodun doğru şekilde geri alabilmesi kritik önem taşır.
  • Çok az yer kaplar.

    • İkili boyutu önemli ölçüde artırmak için geri sarma kodları 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. Alan için çeşitli iyileştirmeler, en verimli veri paketlemeyi elde etmek için bu olguyu kullanır.

  • Kapsamlarda koşullu kod yoktur.

  • Ayrılmış çerçeve işaretçisi yazmaç: öğesi girişteki başka bir yazmaçta (x29) kaydedilirsesp, bu kayıt işlevin tamamında dokunulmaz kalır. Bu, orijinalinin sp herhangi bir zamanda kurtarılabileceği anlamına gelir.

  • sp başka bir kayıtta kaydedilmediği sürece, yığın işaretçisinin tüm işlemesi kesinlikle giriş ve kapsam 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

Diagram that shows the stack frame layout for functions.

Çerçeve zincirleme işlevler için ve lr çifti, fp iyileştirme konularına bağlı olarak yerel değişken alanındaki herhangi bir konumda kaydedilebilir. Amaç, çerçeve işaretçisine () veya yığınsp işaretçisine (x29) göre tek bir yönergeyle ulaşabileceğiniz yerel ayarların sayısını en üst düzeye çıkarmaktır. Ancak işlevler için alloca zincirlenmiş x29 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. Netlik ve daha iyi önbellek yerelliği için, çağrıya kayıtlı yazmaçları tüm kurallı girişlerde depolama sırası "büyüyen" sıradadır. #framesz aşağıda yığının tamamının boyutu (alan hariç) alloca temsil eder. #localsz ve #outsz sırasıyla yerel alan boyutunu (çiftin kaydetme alanı <x29, lr> dahil) ve giden parametre boyutunu belirtir.

  1. 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)
    
  2. 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 area
    
  3. Zincirsiz, yaprak işlevleri (lr 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 area
    

    Tüm yerel öğelere temelinde sperişilir. <x29,lr> önceki çerçeveyi gösterir. Çerçeve boyutu <= 512 için, sub sp, ... kaydedilen kayıt defterleri alanı yığının altına taşınırsa iyileştirilebilir. Dezavantajı, yukarıdaki diğer düzenlerle tutarlı olmamasıdır. Ayrıca kaydedilen kayıt defterleri, çift kayıt defterleri ile önceden ve sonra dizine alınan uzaklık adresleme modu aralığının bir parçası olur.

  4. Zincirsiz, yapraksız işlevler (Int kaydedilmiş alanında kaydeder lr )

        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 area
    

    Alternatif olarak, kayıtlı çift sayıyla Int yazmaçları,

        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 area
    

    Yalnızca x19 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ş stp bir reg-lr stp geri sarma kodlarıyla temsil edilemediğinden kayıt defteri kaydetme alanı ayırma içine katlanmamıştır.

    Tüm yerel öğelere temelinde sperişilir. <x29> önceki çerçeveyi gösterir.

  5. 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 pair
    

    Yukarı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, yönerge düzeyi paralelliğini önleyen bir bağımlılık karşıtlığı sp olmadığı anlamına gelir.

  6. Zincirli, çerçeve boyutu > 512 (olmayan allocaiş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, x29 "reg-pair" ve pre-/post-indexed offset adresleme modu için daha iyi bir kapsam sağlamak üzere yerel alanda herhangi bir konuma yerleştirilebilir. Çerçeve işaretçilerinin altındaki yerel ayarlara temel spalınarak erişilebilir.

  7. 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

.pdata Kayıt

Kayıtlar .pdata , 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 gerektirmeyen ve geçici olmayan kayıtları kaydetmesi/geri yüklemesi gerekmeyen yaprak işlevler kayıt gerektirmez .pdata . Alan kazanmak için bu kayıtlar açıkça atlanmalıdır. Bu işlevlerden birinin geri alınması, dönüş adresini doğrudan öğesinden lr alıp çağırana kadar taşıyabilir.

ARM64 için her .pdata kayıt 8 bayt uzunluğundadır. Her kaydın genel biçimi, işlevin 32 bit RVA'sını ilk sözcükte başlatır ve ardından değişken uzunlukta .xdata bir bloğun işaretçisini içeren ikinci bir sözcük ya da kurallı işlevin geri sarma dizisini açıklayan paketlenmiş bir sözcük içerir.

.pdata record layout.

Alanlar aşağıdaki gibidir:

  • İşlev Başlatma RVA'sı , işlevin başlangıcının 32 bit RVA'dır.

  • Bayrak , ikinci .pdata 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 .xdata 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 kayıt .xdata gerekmez.

.xdata Kayıt

Paketlenmiş geri sarma biçimi bir işlevin geri sarmasını açıklamak için yetersizse, değişken uzunlukta .xdata bir kayıt oluşturulmalıdır. Bu kaydın adresi, kaydın ikinci sözcüğünde .pdata depolanır. biçimi .xdata , paketlenmiş değişken uzunlukta bir sözcük kümesidir:

.xdata record layout.

Bu veriler dört bölüme ayrılır:

  1. 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 Hem Kapsam Sayısı hem de 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 .pdata ve .xdata 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 .xdatasü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.

    c. X , 1 bitlik bir alandır. Özel durum verilerinin varlığını (1) veya yokluğunu (0) gösterir.

    d. E , 1 bitlik bir alandır. Tek bir epilogu açıklayan bilgilerin daha sonra (0) daha fazla kapsam sözcüğü 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:

    1. E 0 ise, bölüm 2'de açıklanan toplam kapsam 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.

    2. E 1 ise, bu alan ilk geri sarma kodunun dizinini belirtir ve bu da tek ve tek tanımlamayı açıklar.

    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.

    r. Genişletilmiş Kapsam Sayısı ve Genişletilmiş Kod Sözcükleri sırasıyla 16 bit ve 8 bit alanlardır. Çok fazla sayıda epilog kodlamak için daha fazla alan veya çok fazla sayıda geri sarma kodu sözcüğü sağlar. Bu alanları içeren uzantı sözcüğü yalnızca ilk üst bilgi sözcüğündeki Hem Kapsam Sayısı hem de Kod Sözcükleri alanları 0 olduğunda bulunur.

  2. 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ıç uzaklığını artırmak için 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.

    c. Epilog Başlangıç Dizini 10 bitlik bir alandır (Genişletilmiş Kod Sözcüklerinden 2 bit daha fazla). Bu derlemeyi açıklayan ilk geri sarma kodunun bayt dizinini gösterir.

  3. Kapsam kapsamları listesi geldikten sonra, sonraki bir bölümde ayrıntılı olarak açıklanan, geri alma kodları içeren bir bayt dizisi gelir. Bu dizi, en yakın tam sözcük sınırına uçta 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.

  4. Son olarak, geri sarma kodu baytlarının ardından üst bilgideki X biti 1 olarak ayarlandıysa, özel durum işleyicisi bilgileri gelir. Özel durum işleyicisinin adresini sağlayan tek bir Özel Durum İşleyici RVA'sı oluşur. Hemen ardından özel durum işleyicisi tarafından gereken değişken uzunlukta veri miktarı gelir.

Kayıt .xdata , ilk 8 baytı getirmek ve izleyen değişken boyutlu özel durum verilerinin uzunluğu çıkarılarak kaydın tam boyutunu hesaplamak için bunları kullanmak üzere 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). (Örneğin, Örnekler bölümü.) Derleyici yazarları özellikle bu durum için iyileştirilmelidir. 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.

Geri sarma kodları

Geri sarma kodları dizisi, prologun etkilerinin tam olarak nasıl geri alındığını açıklayan bir dizi 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 lr . Ayrıca, geçici olmayan tüm yazmaçlar işlev çağrıldığında değerlerine geri yüklenir.

Özel durumların yalnızca bir işlev gövdesi içinde gerçekleşeceği garanti edilirse ve hiçbir zaman bir giriş veya kapsam içinde gerçekleşmezse, yalnızca tek bir dizi gerekir. Ancak Windows'un geri sarmalama modeli, kodun kısmen yürütülen bir giriş veya bitiş listesi içinden geri alabilmesini gerektirir. Bu gereksinimi karşılamak için, geri alma kodları dikkatle tasarlanmıştır, böylece 1:1'i giriş ve girişteki ilgili her opcode ile kesin bir şekilde eşlerler. Bu tasarımın çeşitli etkileri vardır:

  • Geri sarma kodlarının sayısını sayarak, giriş ve kapsam uzunluğunu hesaplamak mümkündür.

  • Bir kapsam kapsamının başındaki yönergelerin sayısını sayarak, geri alma kodlarının eşdeğer sayısını atlayabilirsiniz. Bölüm tarafından yapılan kısmen yürütülen geri sarmayı 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 (alloc_l) ayıran kod dışında tek/çift bayttır. Toplamda 22 geri sarma kodu vardır. Her geri sarma kodu, kısmen yürütülen prolog'ların ve kapsamların geri alınabilmesi için prolog/epilog'daki bir yönergeyi tam olarak eşler.

Kodu geri sarma Bitler 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 [sp-#Z*8]!çifti kaydedin <x19,x20>
save_fplr 01zzzzzz: , uzaklık <= 504 konumunda [sp+#Z*8]çift kaydedin<x29,lr>.
save_fplr_x 10zzzzzz: önceden dizinlenmiş uzaklık >= -512 konumunda [sp-(#Z+1)*8]!çift kaydedin <x29,lr>
alloc_m 11000xxx'xxxxxxxx: boyutu < 32K olan büyük yığın ayırın (2^11 * 16).
save_regp 110010xx'xxzzzz: save x(19+#X) pair at [sp+#Z*8], offset <= 504
save_regp_x 110011xx'xxzzzzzz: konumunda çift x(19+#X)[sp-(#Z+1)*8]!kaydetme, önceden dizine alınan uzaklık >= -512
save_reg 110100xx'xxzzzzzz: kayıt defterini x(19+#X) kaydedin [sp+#Z*8], uzaklık <= 504
save_reg_x 1101010x'xxxzzzzz: save reg x(19+#X) at [sp-(#Z+1)*8]!, önceden dizinlenmiş uzaklık >= -256
save_lrpair 1101011x'xxzzzzzz: save pair <x(19+2*#X),lr> at [sp+#Z*8], offset <= 504
save_fregp 1101100x'xxzzzzzz: save pair d(8+#X) at [sp+#Z*8], offset <= 504
save_fregp_x 1101101x'xxzzzzzz: konumunda çift d(8+#X)[sp-(#Z+1)*8]!kaydetme, önceden dizinlenmiş uzaklık >= -512
save_freg 1101110x'xxzzzzzz: save reg d(8+#X) at [sp+#Z*8], offset <= 504
save_freg_x 11011110'xxxzzzzz: kayıt defterini d(8+#X) önceden dizine alınan uzaklık >= -256 konumunda [sp-(#Z+1)*8]!kaydedin
alloc_l 11100000'xxxxxxxx'xxxxxx'xxxxxxxx: boyutu 256M olan < büyük yığın ayırma (2^24 * 16)
set_fp 11100001: ile ayarlama x29mov x29,sp
add_fp 11100010'xxxxxxxx: ile ayarlama x29add x29,sp,#x*8
nop 11100011: geri sarma işlemi gerekmez.
end 11100100: geri sarma kodunun sonu. Epilogda ima eder ret .
end_c 11100101: Geçerli zincirlenmiş kapsamdaki geri sarma kodunun sonu.
save_next 11100110: Sonraki geçici olmayan Int veya FP yazmaç çifti kaydedin.
11100111: 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 MSFT_OP_TRAP_FRAME
11101001: Için özel yığın MSFT_OP_MACHINE_FRAME
11101010: Için özel yığın MSFT_OP_CONTEXT
11101011: Için özel yığın MSFT_OP_EC_CONTEXT
11101100: Için özel yığın MSFT_OP_CLEAR_UNWOUND_TO_CALL
11101101: ayrılmış
11101110: ayrılmış
11101111: ayrılmış
11110xxx: ayrılmış
11111000'yy : ayrılmış
11111001'y'y : ayrılmış
11111010'y'y'y'y : ayrılmış
11111011'y'y'y'y'y : ayrılmış
pac_sign_lr 11111100: İade adresini lr ile oturum açın pacibsp
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 giriş veya kapsam içindeki bir yönergeyle tam olarak eşlendiğinden, prolog veya epilog 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 girişte dizine alınan uzaklık sonrası adresleme işlemine izin verilmez. Tüm uzaklık aralıkları (#Z), tüm kaydetme alanları için 248'in stp/str yeterli olduğu (10 Int yazmacı + 8 FP yazmaç + 8 giriş yazmacı) dışında save_r19r20_xadresleme kodlaması ile eşleşmektedir.

save_nextInt veya FP geçici kayıt çifti için kaydetmeyi izlemelidir: , , , save_fregp_x, , save_r19r20_xveya başka bir save_next. save_fregpsave_regp_xsave_regp Bir sonraki yazmaç çiftini sonraki 16 baytlık yuvada "büyüyen" sırada kaydeder. A save_next , son Int yazmaç çiftini save-next belirten FP yazmaç çiftini izlediğinde ilk FP yazmaç çiftini ifade eder.

Normal dönüş ve atlama yönergelerinin boyutları aynı olduğundan, kuyruk çağrısı senaryolarında ayrı end ayrı geri sarma koduna gerek yoktur.

end_c , iyileştirme amacıyla bitişik olmayan işlev parçalarını işlemek üzere tasarlanmıştır. end_c Geçerli kapsamdaki geri sarma kodlarının sonunu gösteren bir, gerçek endile biten başka bir geri sarma kodu serisi tarafından takip edilmelidir. ve arasındaki end_cend 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

Kapsamları ve kapsamları aşağıda açıklanan kurallı biçimi izleyen işlevler için paketlenmiş geri alma verileri kullanılabilir. Kayıt gereksinimini .xdata tamamen ortadan kaldırır ve geriye doğru veri sağlama maliyetini önemli ölçüde azaltır. Kurallı girişler ve kapsamlar, basit bir işlevin ortak gereksinimlerini karşılayacak şekilde tasarlanmıştır: Özel durum işleyici gerektirmeyen ve kurulum ve kaldırma işlemlerini standart sırada yapan bir işlevdir.

Paketlenmiş geri sarma verilerini içeren bir .pdata kaydın biçimi şöyle görünür:

.pdata record with packed unwind data.

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 .xdata kayda işaret eder
    • 01 = kapsamın başında ve sonunda tek bir giriş ve kapsam ile kullanılan paketlenmiş geri sarma verileri
    • 10 = herhangi bir giriş ve kapsam olmadan kod için kullanılan paketlenmiş geri sarma 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 .xdata 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. Yığından daha büyük (8k-16) bayt ayıran işlevlerin tam .xdata kayıt kullanması gerekir. Yerel değişken alanını, giden parametre alanını, aranan kaydedilmiş Int ve FP alanını ve giriş parametresi 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, <x29,lr> çift yığına kaydedilmez
    • 01 = zincirsiz işlev, <lr> yığına kaydedilir
    • 10 = işaretli iade adresiyle pacibsp zincirlenmiş işlev
    • 11 = zincirlenmiş işlev, bir depo/yük çifti yönergesi prolog/epilog içinde kullanılır <x29,lr>
  • 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; RegF>0: 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ümde 1, 2 (giden parametre alanı olmadan), 3 ve 4 kategorilerine giren kurallı prologlar paketlenmiş geri sarma biçimiyle temsil edilebilir. Kurallı işlevlerin epilogları, H'nin hiçbir etkisi olmaması, yönergenin atlanması ve adımların set_fp sırası ile her adımdaki yönergelerin epilogda ters çevrilmesi dışında benzer bir biçimde izlenir. Paketlenmiş .xdata 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. lr 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 giriş parametresi alanına kaydedin.

6. Adım: Yerel alan, <x29,lr> ç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 # of instructions Işlem kodu 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_x
save_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)]
nop
nop
nop
nop
6a (CR == 10 || CR == 11) &
#locsz<= 512
2 stp x29,lr,[sp,#-locsz]!
mov x29,sp***
save_fplr_x
set_fp
6b (CR == 10 || CR == 11) &
512 <#locsz<= 4080
3 sub sp,sp,#locsz
stp x29,lr,[sp,0]
add x29,sp,0
alloc_m
save_fplr
set_fp
6c (CR == 10 || CR == 11) &
#locsz> 4080
4 sub sp,sp,4080
sub sp,sp,#(locsz-4080)
stp x29,lr,[sp,0]
add x29,sp,0
alloc_m
alloc_s/alloc_m
save_fplr
set_fp
6d (CR == 00 || CR == 01) &
#locsz<= 4080
1 sub sp,sp,#locsz alloc_s/alloc_m
6e (CR == 00 || CR == 01) &
#locsz> 4080
2 sub sp,sp,4080
sub sp,sp,#(locsz-4080)
alloc_m
alloc_s/alloc_m

* CR == 01 ve RegI tek bir sayıysa, 2. adım ve 1. adımdaki son save_rep sayı tek bir save_regpiçinde birleştirilir.

** RegI == CR == 0 ve RegF != 0 ise, kayan noktanın ilki stp ön atamayı yapar.

Epilogda öğesine mov x29,sp karşılık gelen hiçbir yönerge yoktur. bir işlev'den x29geri yüklenmesini sp gerektiriyorsa paketlenmiş geri sarma verileri kullanılamaz.

Kısmi kapsamları ve kapsamları geri alma

En yaygın geri sarmalama durumlarında, özel durum veya çağrı işlevin gövdesinde, giriş ve tüm kapsamlardan uzakta gerçekleşir. Bu durumlarda, geri sarma basit bir işlemdir: geri sarmalayıcı, kodları unwind dizisinde yürütür. Dizin 0'da başlar ve bir opcode algılanana end kadar devam eder.

Bir prolog veya kapsam 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. Giriş için geri sarma kodları serisinin, giriş için geri sarma kodlarının tam bir ayna görüntüsü olduğunu görebilirsiniz (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:

set_fp, save_regp 0,240, save_fregp,0,224, save_fplr_x_256, , end

Normal düzende olduğundan, epilog olayı basittir. Epilog içindeki uzaklık 0'dan başlayarak (işlevdeki uzaklık 0x100 başlar), henüz temizleme yapılmadığından tam geri sarma dizisinin yürütülmesini bekleriz. Kendi kendimize bir yönerge bulursak (epilogda 2 uzaklığında), ilk geri alma kodunu atlayarak başarıyla geri alabiliriz. Bu durumu genelleştirebilir ve opcode'lar ile geri sarma kodları arasında 1:1 eşlemesi 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 uzaklığından geri sarmayı kaldırmaya başlarsak hiçbir şey yürütmek istemeyiz. 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:

  1. İşlevin gövdesinden geri sarıyorsanız, 0 dizininde geri sarma kodlarını yürütmeye başlayın ve bir end işlem koduna gelene kadar devam edin.

  2. 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 başlayarak yürütür.

  3. Prolog içinden geri sarıyorsanız başlangıç noktanız olarak dizin 0'ı kullanın. Dizideki prolog kodunun uzunluğunu hesaplayın ve ardından söz konusu bilgisayarın prologun sonundan kaç bayt 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 başlayarak yürütür.

Bu kurallar, prolog için geri sarma kodlarının dizide her zaman ilk olması gerektiği anlamına gelir. Ve bunlar aynı zamanda vücudun içinden geri sarmak için kullanılan kodlardır. Tüm epilog'a özgü kod dizileri hemen sonra izlenmelidir.

İş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ı .pdata (ve büyük olasılıkla .xdata) kaydını gerektirir.

Kendi girişine sahip her ayrılmış ikincil parça için, girişinde yığın ayarlaması yapılmamış olması beklenir. İkincil bölge için gereken tüm yığın alanı, üst bölgesi (veya konak bölgesi olarak adlandırılır) tarafından önceden ayrılmalıdır. Bu ön yükleme, yığın işaretçisi işlemesini kesinlikle işlevin özgün girişinde 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ş)

  1. Yalnızca giriş (bölge 1: tüm kapsamlar ayrılmış bölgelerdedir):

    Yalnızca prolog açıklanmalıdır. Bu prolog, sıkıştırılmış .pdata biçimde temsil edilemiyor. Tam .xdata durumda, Kapsam Sayısı = 0 ayarlanarak temsil edilebilir. Yukarıdaki örnekte 1. bölgeye bakın.

    Geri sarma kodları: set_fp, save_regp 0,240, save_fplr_x_256, end.

  2. Yalnızca epiloglar (bölge 2: prolog konak bölgesindedir)

    Zaman denetimi bu bölgeye atlayarak tüm prolog kodlarının yürütüldüğünü varsayılır. Kısmi geri sarma, normal bir işlevdeki gibi epiloglarda da oluşabilir. Bu tür bir bölge, sıkıştırılmış .pdataile temsil edilemez. Tam .xdata kayıtta, bir ve end geri sarma kod çifti tarafından köşeli ayraçlı bir end_c "hayalet" giriş listesiyle kodlanabilir. Baştaki end_c değer, giriş öğesinin boyutunun sıfır olduğunu gösterir. Tek bir epilogun başlangıç dizini öğesini işaret etmektedir set_fp.

    Bölge 2 için geri sarma kodu: end_c, set_fp, save_regp 0,240, save_fplr_x_256, end.

  3. Prolog veya epilog yok (bölge 3: girişler ve tüm kapsamlar diğer parçalardadır):

    Sıkıştırılmış .pdata biçim, Bayrak = 10 ayarıyla uygulanabilir. Tam .xdata kayıtla, Kapsam 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 end_c. 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 örneği de "küçültme sarmalama"dır. Derleyici, bazı callee-saved yazmaçlarını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 girişinde 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: set_fp, save_regp 0,240, save_fplr_x_256, end. Epilog Başlangıç Dizini her zamanki gibi işaret etmektedir set_fp .

Bölge 2: save_regp 2, 224, end_c, set_fp, save_regp 0,240, save_fplr_x_256, end. Epilog Başlangıç Dizini, ilk olarak kodunu save_regp 2, 224geri sarmaya işaret eder.

Büyük işlevler

Parçalar, üst bilgideki .xdata 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 bir giriş işaretine sahip değil olarak 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 kapsam kapsamının, işlevin başlangıcına değil, parçanın başlangıcına göre başlangıç uzaklığını belirttiğini unutmayın.

Bir parçanın prologu ve kapsamı yoksa, işlevin gövdesinden nasıl geri alındığını açıklamak için yine de kendi .pdata (ve muhtemelen .xdata) kaydını gerektirir.

Örnekler

Örnek 1: Çerçeve zincirli, kompakt biçimli

|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 (kısmen geri sarma dizisini yeniden kullanın).

Ayrıca bkz.

ARM64 ABI kurallarına genel bakış
ARM özel durum işleme