版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 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也是同樣的結果。