《coredump問題原理探究》Linux x86版3.6節棧佈局之gcc內嵌關鍵字

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/xuzhina/article/details/8535518

在intel CPU平臺下,可以在代碼加上__attribute__((regparm(number ) ) )指定函數調用時通過寄存器eax,edx,ecx來傳遞參數。其中number就是指定有多少個參數是通過寄存器來傳遞。由於只有這3個寄存器可以傳遞參數,所以number最大值爲3。

那麼,使用這個屬性會對棧佈局產生什麼樣的影響呢?會不會影響之前探討過的兩個規律?

多說無謂,重要是寫例子來實驗。

先看一下例子:

int add( int a, int b, int c,
         int d  )
{
     return a + b + c + d;
}
 
__attribute__( ( regparm( 1 ) ) ) int addOne( int a, int b, int c,
         int d )
{
     return a + b + c + d;
}
 
__attribute__( ( regparm( 2 ) ) ) int addTwo( int a, int b, int c,
         int d )
{
     return a + b + c + d ;
}
 
__attribute__( ( regparm( 3 ) ) ) int addThree( int a, int b, int c,
         int d )
{
     return a + b + c + d;
}
 
int main()
{
     int sum = 0;
     sum += add( 1,2,3,4);
     sum += addOne( 1,2,3,4);
     sum += addTwo( 1,2,3,4);
     sum += addThree( 1,2,3,4);

     return sum;
}

看一下main函數的彙編:

(gdb) disassemble main
Dump of assembler code for function main:
   0x080484e7 <+0>:     push   %ebp
   0x080484e8 <+1>:     mov    %esp,%ebp
   0x080484ea <+3>:     sub    $0x20,%esp
   0x080484ed <+6>:     movl   $0x0,-0x4(%ebp)
   0x080484f4 <+13>:    movl   $0x4,0xc(%esp)
   0x080484fc <+21>:    movl   $0x3,0x8(%esp)
   0x08048504 <+29>:    movl   $0x2,0x4(%esp)
   0x0804850c <+37>:    movl   $0x1,(%esp)
   0x08048513 <+44>:    call   0x8048470 <_Z3addiiii>
   0x08048518 <+49>:    add    %eax,-0x4(%ebp)

   0x0804851b <+52>:    movl   $0x4,0x8(%esp)
   0x08048523 <+60>:    movl   $0x3,0x4(%esp)
   0x0804852b <+68>:    movl   $0x2,(%esp)
   0x08048532 <+75>:    mov    $0x1,%eax
   0x08048537 <+80>:    call   0x8048487 <_Z6addOneiiii>

   0x0804853c <+85>:    add    %eax,-0x4(%ebp)

   0x0804853f <+88>:    movl   $0x4,0x4(%esp)
   0x08048547 <+96>:    movl   $0x3,(%esp)
   0x0804854e <+103>:   mov    $0x2,%edx
   0x08048553 <+108>:   mov    $0x1,%eax
   0x08048558 <+113>:   call   0x80484a4 <_Z6addTwoiiii>

   0x0804855d <+118>:   add    %eax,-0x4(%ebp)

   0x08048560 <+121>:   movl   $0x4,(%esp)
   0x08048567 <+128>:   mov    $0x3,%ecx
   0x0804856c <+133>:   mov    $0x2,%edx
   0x08048571 <+138>:   mov    $0x1,%eax
   0x08048576 <+143>:   call   0x80484c4 <_Z8addThreeiiii>

   0x0804857b <+148>:   add    %eax,-0x4(%ebp)
   0x0804857e <+151>:   mov    -0x4(%ebp),%eax
   0x08048581 <+154>:   leave  
   0x08048582 <+155>:   ret    
End of assembler dump.

由上面彙編可以看到:

1.      main函數調用addOne時,第一個參數通過eax傳遞,其它依次壓入棧裏。

2.      main函數調用addTwo時,第一個參數通過eax傳遞,第二個參數通過edx傳遞,其它依次壓入棧中。

3.      main函數調用addThree時,第一個參數通過eax傳遞,第二個參數通過edx傳遞,第三個參數通過ecx傳遞,其它依次壓入棧中。

可見,通過寄存器傳遞參數,會依次使用eax,edx,ecx。

再回憶一下上一節的規律,兩個相鄰的返回地址ret1,ret2,其中ret1屬於函數func1,ret2屬於函數func2,且func1調用func2。當func2調用func3時,ret2被壓入棧。其中func2的局部變量空間大小爲var_size,func3壓入棧中的參數大小爲par_size,那麼它們會滿足下面的條件:

1. addr(ret1)-addr(ret2)= var_size + par_size + 4

2. info symbol ret1, info symbolret2都能夠顯示出func1,func2

也就是說,__attribute__((regparm(number ) ) )對於par_size是有影響,函數參數個數減去number纔是真正的par_size。

運行一下程序來驗證一下結果,在main,addOne,addTwo,addThree上打斷點。

(gdb) tbreak main
Temporary breakpoint 1 at 0x80484ed
(gdb) tbreak addOne
Temporary breakpoint 2 at 0x804848d
(gdb) tbreak addTwo
Temporary breakpoint 3 at 0x80484aa
(gdb) tbreak addThree(int, int, int, int) 
Temporary breakpoint 4 at 0x80484ca
(gdb) r
Starting program: /home/buckxu/work/3/5/xuzhina_dump_c3_s5 

Temporary breakpoint 1, 0x080484ed in main ()
Missing separate debuginfos, use: debuginfo-install glibc-2.15-58.fc17.i686 libgcc-4.7.2-2.fc17.i686 libstdc++-4.7.2-2.fc17.i686
(gdb) i r ebp
ebp            0xbffff488       0xbffff488
(gdb) x /2x $ebp
0xbffff488:     0x00000000      0x4362f635
(gdb) info symbol 0x4362f635
__libc_start_main + 245 in section .text of /lib/libc.so.6
(gdb) c
Continuing.

Temporary breakpoint 2, 0x0804848d in addOne(int, int, int, int) ()
(gdb) x /2x $ebp
0xbffff460:     0xbffff488      0x0804853c
(gdb) info symbol 0x0804853c
main + 85 in section .text of /home/buckxu/work/3/5/xuzhina_dump_c3_s5

由於在main函數在執行時,壓入了函數楨指針,分配了0x20個字節的局部變量空間,在調用addOne時,沒有壓入參數,使用了局部變量空間。所以,main函數返回地址的單元和__libc_start_main返回地址的單元應該是相差0x20 + 4 + 4 = 0x28個字節。和上面的結果一樣。其它像addTwo,addThree也是同樣的結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章