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 __m128
olarak 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
, __m128
ve 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 __m128
skaler 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.