x64 çağırma kuralı

Bu bölümde, x64 kodunda bir işlevin (çağıran) başka bir işleve (çağıran) çağrı yapmak için kullandığı standart işlemler ve kurallar açıklanmaktadır.

Çağırma kuralı hakkında __vectorcall daha fazla bilgi için bkz . __vectorcall.

Çağırma kuralı varsayılanları

x64 Uygulama İkili Arabirimi (ABI), varsayılan olarak dört yazmaçlı bir hızlı çağrı çağırma kuralı kullanır. Arama yığınında, bu yazmaçları kaydetmek için arayanlar için gölge depo olarak alan ayrılır.

İşlev çağrısının bağımsız değişkenleri ile bu bağımsız değişkenler için kullanılan yazmaçlar arasında kesin bire bir yazışma vardır. 8 bayta sığmayan veya 1, 2, 4 veya 8 bayt olmayan bağımsız değişkenler başvuruyla geçirilmelidir. Tek bir bağımsız değişken hiçbir zaman birden çok yazmaç arasında yayılmaz.

x87 yazmaç yığını kullanılmamış. Çağıran tarafından kullanılabilir, ancak işlev çağrıları arasında geçici olduğunu düşünün. Tüm kayan nokta işlemleri 16 XMM yazmaç kullanılarak yapılır.

RcX, RDX, R8 ve R9 yazmaçlarında tamsayı bağımsız değişkenleri geçirilir. Kayan nokta bağımsız değişkenleri XMM0L, XMM1L, XMM2L ve XMM3L'de geçirilir. 16 baytlık bağımsız değişkenler başvuruyla geçirilir. Parametre geçirme, Parametre geçirme bölümünde ayrıntılı olarak açıklanmıştır. Bu yazmaçlar ve RAX, R10, R11, XMM4 ve XMM5 geçici olarak kabul edilir veya dönüşte çağrıyı alan kişi tarafından potansiyel olarak değiştirilebilir. Kayıt kullanımı, x64 yazmaç kullanımı ve Çağıran/çağıran kayıtlı yazmaçlar bölümünde ayrıntılı olarak belgelenmiştir.

Prototiplenen işlevler için, tüm bağımsız değişkenler geçirilmeden önce beklenen çağıran türlerine dönüştürülür. Çağıran, çağıranın parametreleri için alan ayırmadan sorumludur. Çağıran kişi bu kadar çok parametre almasa bile, çağıranın dört yazmaç parametresini depolamak için her zaman yeterli alan ayırması gerekir. Bu kural, hazırlanmamış C dili işlevleri ve vararg C/C++ işlevleri için desteği basitleştirir. Vararg veya unprototyped işlevleri için, tüm kayan nokta değerleri ilgili genel amaçlı yazmaçta çoğaltılmalıdır. İlk dörtten sonraki tüm parametreler, çağrıdan önce gölge depodan sonra yığında depolanmalıdır. Vararg işlevi ayrıntılarıNağlar'da bulunabilir. Unprototyped function information is detailed in Unprototyped functions.

Hizalama

Yapıların çoğu doğal hizalamalarına göre hizalanır. Birincil özel durumlar, performansa yardımcı olmak için hizalanmış 16 baytlık yığın işaretçisi ve malloc veya alloca bellektir. 16 bayt'ın üzerinde hizalama el ile yapılmalıdır. 16 bayt, XMM işlemleri için ortak bir hizalama boyutu olduğundan, bu değer çoğu kod için işe yaramalıdır. Yapı düzeni ve hizalama hakkında daha fazla bilgi için bkz . x64 türü ve depolama düzeni. Yığın düzeni hakkında bilgi için bkz . x64 yığın kullanımı.

Geri sarılabilirlik

Yaprak işlevleri geçici olmayan kayıtları değiştirmeyen işlevlerdir. Yaprak olmayan bir işlev, örneğin bir işlev çağırarak geçici olmayan RSP'yi değiştirebilir. Alternatif olarak, yerel değişkenler için ek yığın alanı ayırarak RSP'yi değiştirebilir. Bir özel durum işlendiğinde geçici olmayan kayıtları kurtarmak için yaprak olmayan işlevlere statik verilerle açıklama eklenir. Veriler, rastgele bir yönergede işlevin nasıl düzgün bir şekilde geri sarıldığı açıklanır. Bu veriler pdata veya yordam verileri olarak depolanır ve bu da özel durum işleme verileri olan xdata'ya başvurur. xdata, geri sarmalama bilgilerini içerir ve ek pdata veya bir özel durum işleyici işlevine işaret edebilir.

Girişler ve kapsamlar, xdata'da düzgün bir şekilde açıklanabilmeleri için yüksek oranda kısıtlanmıştır. Yığın işaretçisi, yaprak işlevleri dışında bir giriş veya giriş parçası olmayan herhangi bir kod bölgesinde 16 bayt hizalanmış olarak kalmalıdır. Yaprak işlevleri yalnızca bir dönüş simülasyonu yaparak çözülebilir, bu nedenle pdata ve xdata gerekli değildir. İşlev girişlerinin ve kapsamlarının düzgün yapısı hakkında ayrıntılı bilgi için bkz . x64 giriş ve bitiş. Özel durum işleme ve özel durum işleme ve pdata ile xdata'nın geri alınması hakkında daha fazla bilgi için bkz . x64 özel durum işleme.

Parametre geçirme

Varsayılan olarak, x64 çağırma kuralı ilk dört bağımsız değişkeni yazmaçlardaki bir işleve geçirir. Bu bağımsız değişkenler için kullanılan yazmaçlar, bağımsız değişkenin konumuna ve türüne bağlıdır. Kalan bağımsız değişkenler sağdan sola sırasıyla yığına gönderilir.

En soldaki dört konumdaki tamsayı değerli bağımsız değişkenler sırasıyla RCX, RDX, R8 ve R9'da soldan sağa sırasıyla geçirilir. Beşinci ve daha yüksek bağımsız değişkenler daha önce açıklandığı gibi yığına geçirilir. Yazmaçlardaki tüm tamsayı bağımsız değişkenleri doğru iki yana yaslandığından çağıran, yazmaçların üst bitlerini yoksayabilir ve yalnızca gerekli yazmaç bölümüne erişebilir.

İlk dört parametredeki kayan nokta ve çift duyarlıklı bağımsız değişkenler, konuma bağlı olarak XMM0 - XMM3 arasında geçirilir. Kayan nokta değerleri yalnızca varargs bağımsız değişkenleri olduğunda RCX, RDX, R8 ve R9 tamsayı yazmaçlarına yerleştirilir. Ayrıntılar için bkz . Varargs. Benzer şekilde, karşılık gelen bağımsız değişken bir tamsayı veya işaretçi türü olduğunda XMM0 - XMM3 yazmaçları yoksayılır.

__m128 türleri, diziler ve dizeler hiçbir zaman anında değerle geçirilmedi. Bunun yerine, çağıran tarafından ayrılan belleğe bir işaretçi geçirilir. Boyut 8, 16, 32 veya 64 bit ve türlerin yapıları ve __m64 birleşimleri, aynı boyuttaki tamsayılar gibi geçirilir. Diğer boyutlardaki yapılar veya birleşimler, çağıran tarafından ayrılan belleğe işaretçi olarak geçirilir. gibi bir işaretçi __m128olarak geçirilen bu toplama türleri için çağıran tarafından ayrılan geçici bellek 16 bayt hizalanmış olmalıdır.

Yığın alanı ayırmaz ve diğer işlevleri çağırmaz, bazen başka yazmaç bağımsız değişkenleri geçirmek için diğer geçici yazmaçları kullanır. Bu iyileştirme, derleyici ile iç işlev uygulaması arasındaki sıkı bağlama ile mümkün hale gelir.

Çağıran, gerekirse yazmaç parametrelerinin gölge alanına dökümünü almaktan sorumludur.

Aşağıdaki tablo, parametrelerin türe ve soldan konuma göre nasıl geçirildiğini özetler:

Parametre türü beşinci ve üzeri dördüncü üçüncü saniye En sol -daki
kayan nokta yığın XMM3 XMM2 XMM1 XMM0
integer yığın R9 R8 RDX RCX
Toplamalar (8, 16, 32 veya 64 bit) ve __m64 yığın R9 R8 RDX RCX
İşaretçi olarak diğer toplamalar yığın R9 R8 RDX RCX
__m128, işaretçi olarak yığın R9 R8 RDX RCX

1 - tüm tamsayıları geçiren bağımsız değişken örneği

func1(int a, int b, int c, int d, int e, int f);
// a in RCX, b in RDX, c in R8, d in R9, f then e pushed on stack

2 - tüm kayan değerleri geçiren bağımsız değişken örneği

func2(float a, double b, float c, double d, float e, float f);
// a in XMM0, b in XMM1, c in XMM2, d in XMM3, f then e pushed on stack

3 - karma int'leri ve float'ları geçiren bağımsız değişken örneği

func3(int a, double b, int c, float d, int e, float f);
// a in RCX, b in XMM1, c in R8, d in XMM3, f then e pushed on stack

4 - __m64, __m128ve toplamlarını geçiren bağımsız değişken örneği

func4(__m64 a, __m128 b, struct c, float d, __m128 e, __m128 f);
// a in RCX, ptr to b in RDX, ptr to c in R8, d in XMM3,
// ptr to f pushed on stack, then ptr to e pushed on stack

Varargs

Parametreler varargs (örneğin, üç nokta bağımsız değişkenleri) aracılığıyla geçirilirse, normal yazmaç parametresi geçirme kuralı uygulanır. Bu kural beşinci ve sonraki bağımsız değişkenleri yığına dökmeyi içerir. Adresinin alındığı bağımsız değişkenlerin dökümünü almak çağıranın sorumluluğundadır. Yalnızca kayan nokta değerleri için, çağıranın tamsayı kayıtlarındaki değeri beklemesi durumunda hem tamsayı yazmaç hem de kayan nokta yazmaç değeri içermelidir.

Unprototyped işlevleri

Tam prototipi olmayan işlevler için çağıran tamsayı değerlerini tamsayı, kayan nokta değerlerini ise çift duyarlık olarak geçirir. Yalnızca kayan nokta değerleri için, çağıranın tamsayı kayıtlarındaki değeri beklemesi durumunda hem tamsayı yazmaç hem de kayan nokta yazmaç float değerini içerir.

func1();
func2() {   // RCX = 2, RDX = XMM1 = 1.0, and R8 = 7
   func1(2, 1.0, 7);
}

Dönüş değerleri

Türü de dahil olmak üzere __m64 64 bite sığabilen bir skaler dönüş değeri RAX aracılığıyla döndürülür. Kayanlar, çiftler ve gibi __m128i__m128d vektör türleri gibi __m128skaler olmayan türler XMM0'de döndürülür. RAX veya XMM0'de döndürülen değerde kullanılmayan bitlerin durumu tanımlanmamış.

Kullanıcı tanımlı türler, genel işlevlerden ve statik üye işlevlerinden değerle döndürülebilir. RAX'ta değere göre kullanıcı tanımlı bir tür döndürmek için uzunluğu 1, 2, 4, 8, 16, 32 veya 64 bit olmalıdır. Ayrıca kullanıcı tanımlı oluşturucu, yıkıcı veya kopyalama atama işlecine sahip olmamalıdır. Özel veya korumalı statik olmayan veri üyelerine ve başvuru türünün statik olmayan veri üyelerine sahip olamaz. Temel sınıflara veya sanal işlevlere sahip olamaz. Ayrıca yalnızca bu gereksinimleri karşılayan veri üyelerine de sahip olabilir. (Bu tanım temelde C++03 POD türüyle aynıdır. C++11 standardında tanım değiştiğinden, bu test için kullanılması std::is_pod önerilmez.) Aksi takdirde, çağıranın dönüş değeri için bellek ayırması ve ilk bağımsız değişken olarak buna bir işaretçi geçirmesi gerekir. Kalan bağımsız değişkenler daha sonra bir bağımsız değişken sağa kaydırılır. Aynı işaretçi RAX'ta çağıran tarafından döndürülmelidir.

Bu örnekler, belirtilen bildirimlere sahip işlevler için parametrelerin ve dönüş değerlerinin nasıl geçirildiğini gösterir:

1 - 64 bit sonuç dönüş değeri örneği

__int64 func1(int a, float b, int c, int d, int e);
// Caller passes a in RCX, b in XMM1, c in R8, d in R9, e pushed on stack,
// callee returns __int64 result in RAX.

2 - 128 bit sonuç dönüş değeri örneği

__m128 func2(float a, double b, int c, __m64 d);
// Caller passes a in XMM0, b in XMM1, c in R8, d in R9,
// callee returns __m128 result in XMM0.

3 dönüş değeri örneği - işaretçiye göre kullanıcı türü sonucu

struct Struct1 {
   int j, k, l;    // Struct1 exceeds 64 bits.
};
Struct1 func3(int a, double b, int c, float d);
// Caller allocates memory for Struct1 returned and passes pointer in RCX,
// a in RDX, b in XMM2, c in R9, d pushed on the stack;
// callee returns pointer to Struct1 result in RAX.

4 dönüş değeri örneği - değere göre kullanıcı türü sonucu

struct Struct2 {
   int j, k;    // Struct2 fits in 64 bits, and meets requirements for return by value.
};
Struct2 func4(int a, double b, int c, float d);
// Caller passes a in RCX, b in XMM1, c in R8, and d in XMM3;
// callee returns Struct2 result by value in RAX.

Arayan/çağıran kayıtlı yazmaçlar

x64 ABI, RAX, RCX, RDX, R8, R9, R10, R11 ve XMM0-XMM5 geçici yazmaçlarını dikkate alır. Mevcut olduğunda, YMM0-YMM15 ve ZMM0-ZMM15'in üst kısımları da geçicidir. AVX512VL'da ZMM, YMM ve XMM 16-31 yazmaçları da geçicidir. AMX desteği mevcut olduğunda, TMM kutucuk yazmaçları geçicidir. Program iyileştirmesi gibi analizlerle aksi takdirde güvenlik kanıtlanabilir olmadığı sürece işlev çağrılarında yok edilen geçici yazmaçları göz önünde bulundurun.

x64 ABI, RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15 ve XMM6-XMM15'i kalıcı olarak kaydeder. Bunları kullanan bir işlev tarafından kaydedilmesi ve geri yüklenmesi gerekir.

İşlev işaretçileri

İşlev işaretçileri yalnızca ilgili işlevin etiketine yönelik işaretçilerdir. İşlev işaretçileri için içindekiler tablosu (İçindekiler) gereksinimi yoktur.

Eski kod için kayan nokta desteği

MMX ve kayan nokta yığın yazmaçları (MM0-MM7/ST0-ST7) bağlam anahtarları arasında korunur. Bu yazmaçlar için açık bir çağırma kuralı yoktur. Bu yazmaçların çekirdek modu kodunda kullanılması kesinlikle yasaktır.

FPCSR

Yazmaç durumu, x87 FPU denetim sözcüğünü de içerir. Çağırma kuralı bu kaydın kalıcı olmasını sağlar.

x87 FPU denetimi sözcük yazmaç, program yürütmenin başlangıcında aşağıdaki standart değerler kullanılarak ayarlanır:

Register[bits] Ayar
FPCSR[0:6] Özel durum tüm 1'leri maskeler (tüm özel durumlar maskelenmiştir)
FPCSR[7] Ayrılmış - 0
FPCSR[8:9] Duyarlık Denetimi - 10B (çift duyarlık)
FPCSR[10:11] Yuvarlama denetimi - 0 (en yakına yuvarlama)
FPCSR[12] Sonsuzluk denetimi - 0 (kullanılmaz)

FPCSR içindeki alanlardan herhangi birini değiştiren bir çağıranın çağırana geri dönmeden önce bunları geri yüklemesi gerekir. Ayrıca, bu alanlardan herhangi birini değiştiren bir çağıranın çağıranı çağırmadan önce standart değerlerine geri yüklemesi gerekir, ancak sözleşme gereği arayan değiştirilen değerleri beklemez.

Denetim bayraklarının değişken olmamasıyla ilgili kurallarda iki özel durum vardır:

  • Verilen işlevin belgelenen amacının kalıcı olmayan FPCSR bayraklarını değiştirmek olduğu işlevlerde.

  • Bu kuralların ihlalinin, kuralları ihlal etmeyen bir programla aynı şekilde davranan bir programla (örneğin, tüm program analizi aracılığıyla) sonuçlandığının doğru olduğu durumlarda.

MXCSR

Kayıt durumu MXCSR'yi de içerir. Çağırma kuralı bu kaydı geçici bir bölüme ve geçici olmayan bir bölüme böler. Geçici bölüm, MXCSR'de altı durum bayrağından oluşur[0:5], yazmacın geri kalanı ise MXCSR[6:15], geçici değil olarak kabul edilir.

Geçici olmayan bölüm, program yürütmenin başlangıcında aşağıdaki standart değerlere ayarlanır:

Register[bits] Ayar
MXCSR[6] Normal olmayanlar sıfırdır - 0
MXCSR[7:12] Özel durum tüm 1'leri maskeler (tüm özel durumlar maskelenmiştir)
MXCSR[13:14] Yuvarlama denetimi - 0 (en yakına yuvarlama)
MXCSR[15] Maskelenmiş alt akış için sıfıra boşalt - 0 (kapalı)

MXCSR içindeki geçici olmayan alanlardan herhangi birini değiştiren bir çağıranın çağırana geri dönmeden önce bunları geri yüklemesi gerekir. Ayrıca, bu alanlardan herhangi birini değiştiren bir çağıranın çağıranı çağırmadan önce standart değerlerine geri yüklemesi gerekir, ancak sözleşme gereği arayan değiştirilen değerleri beklemez.

Denetim bayraklarının değişken olmamasıyla ilgili kurallarda iki özel durum vardır:

  • Verilen işlevin belgelenen amacının kalıcı olmayan MXCSR bayraklarını değiştirmek olduğu işlevlerde.

  • Bu kuralların ihlalinin, kuralları ihlal etmeyen bir programla aynı şekilde davranan bir programla (örneğin, tüm program analizi aracılığıyla) sonuçlandığının doğru olduğu durumlarda.

İşlev belgelerinde açıkça açıklanmadığı sürece MXCSR yazmacının geçici bölüm durumu hakkında bir işlev sınırı boyunca hiçbir varsayımda bulunun.

setjmp/longjmp

setjmpex.h veya setjmp.h eklediğinizde, öğesine yapılan tüm çağrılar setjmp veya longjmp yok edicileri ve __finally çağrıları çağıran bir geri sarmayla sonuçlanır. Bu davranış, setjmp.h dahil olmak üzere yan tümcelerin ve yıkıcıların çağrılmamasına neden __finally olan x86'dan farklıdır.

Geçerli yığın işaretçisini, geçici olmayan yazmaçları ve MXCSR yazmaçlarını korumaya yönelik bir çağrı setjmp . En son çağrı sitesine dönmek için longjmp çağrılar yapılır ve en son setjmp setjmp çağrı tarafından korunduğu şekilde yığın işaretçisi, geçici olmayan yazmaçlar ve MXCSR kayıtları duruma geri sıfırlanır.

Ayrıca bkz.

x64 yazılım kuralları