2013-04-01 37 views
5

C fonksiyonunun montaj kodunu anlamaya çalışıyorum. Neden andl -16 ana bilgisayarda yapıldığını anlayamadım. Yerel değişkenler için yer ayırmak için mi? Eğer öyleyse, ana için subl 32 neden yapılır.c fonksiyonunun montaj kodu

Ben func1 sökülmesi anlayamadık. Okunduğunda yığın, 8086 işlemcilerinin yüksek sipariş adresinden düşük sipariş adresine büyür. Öyleyse neden ebp'nin pozitif tarafındaki erişim (parametreler ofset) ve neden ebp'nin negatif tarafında olmasın. Func1 içindeki yerel değişkenler 3 + dönüş adresi + kaydedilmiş kayıtlardır - Yani 20 olmalı, ama neden 24? (subl $24,esp)

#include<stdio.h> 
int add(int a, int b){ 
int res = 0; 
res = a + b; 
return res; 
} 
int func1(int a){ 
int s1,s2,s3; 
s1 = add(a,a); 
s2 = add(s1,a); 
s3 = add(s1,s2); 
return s3; 
} 
int main(){ 
int a,b; 
a = 1;b = 2; 
b = func1(a); 
printf("\n a : %d b : %d \n",a,b); 
return 0; 
} 

montaj kodu: Ben C kaynağıyla interlisting verecektir objdump -S çıkışında da böyle çalışan öneririm

 .file "sample.c" 
     .text 
.globl add 
     .type add, @function 
add: 
     pushl %ebp 
     movl %esp, %ebp 
     subl $16, %esp 
     movl $0, -4(%ebp) 
     movl 12(%ebp), %eax 
     movl 8(%ebp), %edx 
     leal (%edx,%eax), %eax 
     movl %eax, -4(%ebp) 
     movl -4(%ebp), %eax 
     leave 
     ret 
     .size add, .-add 
.globl func1 
     .type func1, @function 
func1: 
     pushl %ebp 
     movl %esp, %ebp 
     subl $24, %esp 
     movl 8(%ebp), %eax 
     movl %eax, 4(%esp) 
     movl 8(%ebp), %eax 
     movl %eax, (%esp) 
     call add 
     movl %eax, -4(%ebp) 
     movl 8(%ebp), %eax 
     movl %eax, 4(%esp) 
     movl -4(%ebp), %eax 
     movl %eax, (%esp) 
     call add 
     movl %eax, -8(%ebp) 
     movl -8(%ebp), %eax 
     movl %eax, 4(%esp) 
     movl -4(%ebp), %eax 
     movl %eax, (%esp) 
             call add 
     movl %eax, -12(%ebp) 
     movl -12(%ebp), %eax 
     leave 
     ret 
     .size func1, .-func1 
     .section  .rodata 
.LC0: 
     .string "\n a : %d b : %d \n" 
     .text 
.globl main 
     .type main, @function 
main: 
     pushl %ebp 
     movl %esp, %ebp 
     andl $-16, %esp 
     subl $32, %esp 
     movl $1, 28(%esp) 
     movl $2, 24(%esp) 
     movl 28(%esp), %eax 
     movl %eax, (%esp) 
     call func1 
     movl %eax, 24(%esp) 
     movl $.LC0, %eax 
     movl 24(%esp), %edx 
     movl %edx, 8(%esp) 
     movl 28(%esp), %edx 
     movl %edx, 4(%esp) 
     movl %eax, (%esp) 
     call printf 
     movl $0, %eax 
     leave 
     ret 
     .size main, .-main 
     .ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5" 
     .section  .note.GNU-stack,"",@progbits 

cevap

5

düşük dört biti temizleyerek, 16 baytlık bir katına yığın işaretçisi hizalar.

pozitif uzaklıklar (%ebp) kullanılır tek yer parametre erişir vardır.

Hedef platformunuzun ne olduğunu veya hangi anahtarla derlediğinizi belirtmediniz. Montaj kodu, bazı Ubuntu tanımlayıcılarının eklendiğini gösteriyor, ancak kullandığı ABI'yı bilmiyorum, bunun ötesinde, muhtemelen Intel x86 mimarisiyle genel olarak kullanılan ABI'lere benziyor. Dolayısıyla, ABI'nın rutin çağrılarda 8 baytlık hizalama gerektirdiğini tahmin edeceğim ve böylece derleyici, 20 baytlık hizalamanın korunmasını sağlamak için 20 yerine func1 24 baytlık yığın çerçevesini oluşturur.

daha ayrıntılı bir durumda bu SSE 16 baytlık hizalama tercih talimatlar veya diğer işlemleri kullanılır, derleyici derleyici “tercih” bir tür olarak main başlangıcında 16 bayt yığın hizalanmış tahmin olacak 16 bayt hizalamayı tercih edin.

Yani elimizde:

main yılında

, andl $-16, %esp bir derleyici tercihi olarak 16 bayt katına yığını hizalar. main, 28(%esp) ve 24(%esp) geçici değerlere atıfta İçinde 8(%esp), 4(%esp) ve (%esp)func1 ve printf parametreleri geçirmek için kullanılır ise derleyici, yığın kaydeder. Bu değil doğru montaj kodudur: Biz montaj kodu printf çağırır ancak bu derleme kodu oluşturmak için kullanılan C kaynak kodundan farklıdır C kaynak kodu yapıştırdıysanız senin kodunda açıklanır gerçeği görmek C kaynak kodundan oluşturulur.

func1 olarak, 24 bayt 8 bayt hizalamasını korumak için yerine 20 istifi üzerine tahsis edilmiştir. func1 içinde, parametrelere 8(%ebp) ve 4(%ebp) aracılığıyla erişilir. Değişkenlerinizin değerlerini tutmak için -12(%ebp)'dan -4(%ebp)'a kadar olan yerler kullanılır. 4(%esp) ve (%esp) parametreleri add'a geçirmek için kullanılır.Burada

func1 yığın çerçevesi:

 
    - 4(%ebp) = 20(%esp): s1. 
    - 8(%ebp) = 16(%esp): s2. 
    -12(%ebp) = 12(%esp): s3. 
    -16(%ebp) = 8(%esp): Unused padding. 
    -20(%ebp) = 4(%esp): Passes second parameter of add. 
    -24(%ebp) = 0(%esp): Passes first parameter of add. 
+2

GCC manuel Bu alıntı faydalı olabilir: -mpreferred-yığın sınır = num: a 2 hizalanmış istif sınır tutmak için girişimi num yükseltilmiştir bayt sınırı. -mpreferred-stack-border belirtilmemişse, varsayılan 4'dür (16 bayt veya 128 bit). – teppic

+0

8 bayt hizalamayı hızlandırdığınız için teşekkür ederiz. Benim şüphem eklenti -> 1 (yerel değişken) + dönüş adresi => 2 * 8 = 16 doğrudur. Fakat func1 -> 3 (yerel değişkenler) + dönüş adresi => 4 * 8 = 32 orada olmalıdır. neden 24'tür? neden ana alt 32'de temel işaretçi için yapılır. Yapılan hesaplamalarla biraz kafam karıştı. – Angus

+1

@Angus: 'func1' boyutunun hesaplanmasında, neden 8 ile çarpıyorsunuz? Bu 32-bit mimaride, '' '' func1'' içindeki '' '' 'nesneleri ve adresleri dört bayttır. 'func1', s1 için dört bayt, s2 için dört bayt, s3 için dört bayt,' add''e iki baytlık iki parametre, ve dört tane daha bayt hizalama için kullanır. Bu 24 bayt. –