首页
学习
活动
专区
圈层
工具
发布
21 篇文章
1
《coredump问题原理探究》Linux x86版6.3节有成员变量的类coredump例子
2
《coredump问题原理探究》Linux x86版6.5节虚函数的coredump例子
3
《coredump问题原理探究》Linux x86版6.8节多继承coredump例子
4
《coredump问题原理探究》Linux x86版7.2节vector coredump例子
5
《coredump问题原理探究》Linux x86版7.4节List coredump例子
6
《coredump问题原理探究》Linux x86版7.6节 Map coredump例子
7
《coredump问题原理探究》Linux x86版5.9节C风格数据结构内存布局之联合体
8
《coredump问题原理探究》Linux x86版6.1节C++风格数据结构内存布局之无成员变量的类
9
《coredump问题原理探究》Linux x86版6.2节C++风格数据结构内存布局之有成员变量的类
10
《coredump问题原理探究》Linux x86版4.5节函数的逆向之coredump例子
11
《coredump问题原理探究》Linux x86版5.1节C风格数据结构内存布局之引言
12
《coredump问题原理探究》Linux x86版5.2节C风格数据结构内存布局之基本数据类型
13
《coredump问题原理探究》Linux x86版5.3节C风格数据结构内存布局之数组
14
《coredump问题原理探究》Linux x86版5.4节C风格数据结构内存布局之数组coredump例子
15
《coredump问题原理探究》Linux x86版5.5节C风格数据结构内存布局之基本数据类型构成的结构体
16
《coredump问题原理探究》Linux x86版5.6节C风格数据结构内存布局之复合类型构成的结构体
17
《coredump问题原理探究》Linux x86版5.7节C风格数据结构内存布局之结构体数组
18
《coredump问题原理探究》Linux x86版5.8节C风格数据结构内存布局之结构体数组结构体coredump
19
《coredump问题原理探究》Linux x86版3.5节栈布局之-fomit-frame-pointer编译选项
20
《coredump问题原理探究》Linux x86版3.6节栈布局之gcc内嵌关键字
21
《coredump问题原理探究》Linux x86版3.8节栈布局之栈溢出coredump例子

《coredump问题原理探究》Linux x86版3.6节栈布局之gcc内嵌关键字

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1344457

在intel CPU平台下,可以在代码加上__attribute__((regparm(number ) ) )指定函数调用时通过寄存器eax,edx,ecx来传递参数。其中number就是指定有多少个参数是通过寄存器来传递。由于只有这3个寄存器可以传递参数,所以number最大值为3。

那么,使用这个属性会对栈布局产生什么样的影响呢?会不会影响之前探讨过的两个规律?

多说无谓,重要是写例子来实验。

先看一下例子:

代码语言:javascript
复制
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函数的汇编:

代码语言:javascript
复制
(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上打断点。

代码语言:javascript
复制
(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也是同样的结果。

下一篇
举报
领券