C kaynak kodu içinde, assembler ile yazılan 'Fasm' adlı bir işlevin çağrılması için aşağıdakilerin
gerçekleştirilmesi gerekir. Bu işlevi C ile, aynı modül içinde yazmış olsaydınız prototipinin
int Fasm(unsigned x, int i, int * val); olacağını düşününüz.
![]()
1. Fasm (assembler) işlevinin prototipini C kaynak kodu içinde veriniz:
extern "C" { void Fasm(unsigned x, int i, int * val); }
2. İşlev çağrılmadan önce, soldakinden başlanarak parametreler yığıt üzerine eklenir (C programındaki PUSH komutlarıyla. Bu işlemler C derleyicisinin ürettiği kod tarafından yapılır):
YIĞIT:
|
öncekiler |
|
x |
|
i |
|
val |
◄ EBP (Yığıt üzerindeki birim alanlar 4’er byte uzunluğundadır.
Offset değerlerini hesaplarken, yığıtın aşağıya doğru
uzadığını ve üstteki konumların büyük adreslerde
◄ ESP olduğunu hatırlayınız.)
3. Çağrı yapıldıktan hemen sonra, dönüş adresi stack üzerine eklenir (Bu PUSH işlemi, derlenmiş C kodundaki CALL komutunun işletilmesiyle yerine getirilir):
|
öncekiler |
|
x |
|
i |
|
val |
|
Dönüş adresi |
◄ EBP
öncekiler
◄ ESP
Yukarıdaki durum, assembler kodunun ilk satırının yığıt üzerinde bulacağı düzenlemedir. (Not: buradaki parametre değerleri birer değişkendir. işlev dönünceye kadar canlı durumdadırlar: POP ile kullanılamazlar, erişmek istediğinizde uygun adresleri kullanmak durumundasınız.)
4. Assembler kodu içinde: Çağrılan işlev EBP'yi (çağırma noktasındaki yapıyı ileride yeniden oluşturmak için) yığıt üzerine ekler (PUSH EBP). Aynı zamanda, MOV EBP,ESP komutu koda eklenir. Yazacağınız assembler yordamının ilk satırı bu iki işlemden sonra yer alır. Bu iki işlemden sonra, işlev içinde görülecek düzenleme:
|
öncekiler |
|
x |
|
i |
|
Val |
|
Dönüş adr. |
|
EBP (eski) |
◄ EBP, ESP
5. Elbette bundan sonra yığıt üzerine diğer değerleri PUSH edebilirsiniz (işlevden dönmeden önce, düzgün biçimde POP etmek kaydıyla.) Örneğin, yazmaçları saklamak isteyebilirsiniz:
(yazdığınız kod)
(Üretilen
makine kodu)
Fasm PROC ----> Fasm: PUSH ebp
PUSHAD MOV ebp, esp
PUSHAD
PUSHAD işleminden hemen önce yığıtın durumu yukarıdaki gibiyse, işlemden sonra yandaki gibi olacaktır:
val adresi: EBP + 8 , i : EBP + 12 , x : EBP + 16
|
öncekiler |
|
x |
|
i |
|
Val |
|
Dönüş adr. |
|
EBP (eski) |
|
PUSHAD ile eklenen 8 yazmaç(32 byte) |
Anlaşılacağı gibi, EBP işlev parametreleri için taban değeri görevi görmektedir ve ESP'deki değişikliklerden etkilenmez. Bu nedenle parametrelere erişimde EBP'den yararlanılmalıdır (parametrelere ESP ile de erişilebilir, ancak bu durumda ESP'de olması muhtemel değişiklikleri gözönüne almak gerekir; bu durumda, aynı parametreye işlevin farklı yerlerinde farklı offset değerleriyle erişmek gerekecektir).
6. Parametrelerin doğru biçimde yorumlanmaları gerekir. Örneğin adresi EBP+8 olan 'val' değeri aslında bir göstergedir. Bu değer bir adres olarak kullanılmalıdır. Yordamın, C gösterimindeki (i + *val) toplama işini yapacağını varsayalım. Aşağıda soldaki kodu yazarsanız bir mantık hatasına neden olursunuz (tamsayı + adres). Doğrusu sağda verilmiştir.
mov eax, [ebp+12] mov eax, [ebp+12]
add eax, [ebp+8] mov esi, [ebp+8]
add eax,
[esi]
|
Öncekiler |
|
x |
|
i |
|
val |
|
Dönüş adresi |
|
EBP (eski) |
|
PUSHAD ile eklenen 8 yazmaç (32 byte) |
|
0 (a) |
|
0 (b) |
7. Assembler kodu içinde yeni bir değişken alanı gerektiğinde bu alanı yığıt üzerine PUSH işlemiyle gerçekleştirebilirsiniz. Bu alanın EBP'ye göre offseti değişmeyecektir. Örneğin, a ve b sembolik adlarıyla iki tamsayı gerekiyorsa, iki 'PUSH 0' komutuyla bunu sağlayabilirsiniz
Bu değişkenlerin hangisinin a, hangisinin b olduğu EBP'ye göre offsetleriyle ayırdedilir.
a : EBP-36, b: EBP-40
8. Dönüş işlemleri: (STDCALL)
Aşağıdaki iki komut, programdaki 'ret' yerine, paragraf 4'deki iki işleme benzer biçimde, MASM tarafından otomatik olarak türetilir:
(yazdığınız
kod)
(Üretilen makine kodu)
ret -----> POP ebp
ret n
(STDCALL yöntemi belirlenmişse, n değeri dönüş adresi kullanıldıktan sonra yığıt üzerinde kalan değerlerin silinmesi için ESP'den çıkartılacak byte sayısıdır. Yukarıdaki örnekte: 12.)
Ancak, ’POP ebp’ işleminin gerçekten de yığıttaki (eski) EBP değerine denk düşmesi gerekir.
Kodunuzda PUSH ile yığıta eklenen diğer veriler varsa (yukarıdaki örnekte var), bunlar düzgün biçimde
POP edilmiş olmalıdır; bunu sağlamak programcıya düşer. Yığıtta bu biçimde toplam kaç ek byte
olduğu genel olarak çevirici tarafından hesaplanamaz (örneğin, PUSH işleminin bir döngü içinde
yapıldığını düşününüz.)
9. İşlev bir değer döndürüyorsa, bu değer eax içinde bırakılır. Doğru değerin döndürülmesi çağırılan
işlevin, dönen değerin yorumlanması (adres veya değer) çağırma noktasının sorumluluğundadır. Geriye
dönen adres değerleri, işlev tarafından oluşturulan bir yerel değer olamaz (Bu bir mantık hatasıdır. İşlev
parametreleri ve yığıta eklenen yerel değişkenler işlevden dönüldüğünde silinirler.)
10. Bu dökümanda eksik/yanlış olduğunu düşünüyorsanız, bana kısa bir açıklayıcı not atınız.
Kaynaklar :
NASM : http://www.akifakkus.comhttp://www.dosyalar.akifakkus.com/Bil236-Experiment5/Belgeler/Nasm.ppt
Bütün Kaynaklar : http://www.dosyalar.akifakkus.com/Bil236-Experiment5/Belgeler/
Ödev Kaynak Dosya : Bil236_0809_5_20624305.zip