《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也是同样的结果。
- java之log4j的配置
- scala 学习笔记(02) 元组Tuple、数组Array、Map、文件读写、网页抓取示例
- scala 学习笔记(04) OOP(上)主从构造器/私有属性/伴生对象(单例静态类)/apply方法/嵌套类
- 使用jQuery封装实用函数
- scala 学习笔记(05) OOP(中)灵活的trait
- Web Fundamentsals学习1-Multiple-Screen-Site
- Vagrant使用
- java 中的异步回调
- 澳大利亚域名管理机构多年敲竹杠?
- ASP.NET中使用HttpWebRequest调用WCF
- scala 学习笔记(06) OOP(下)多重继承 及 AOP
- Angular企业级开发(4)-ngResource和REST介绍
- CSS魔法堂:"那不是bug,是你不懂我!" by inline-block
- scala 学习笔记(03) 参数缺省值、不定个数参数、类的属性(Property)、泛型初步
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- linux操作系统利用python实现任务管理器可视化功能
- Linux下安装PHP curl扩展的方法详解
- 各种OOM代码样例及解决方法
- Linux 中不输入密码运行 sudo 命令的方法
- Linux 查看远程服务器文件状态的方法
- 安装ubuntu时黑屏的解决办法(3种)
- linux输入yum后提示: -bash: /usr/bin/yum: No such file or directory的解决方法
- 分布式锁,三种实现方式,性能对比分析!
- Linux 中锁定和解锁用户帐户的三种方法
- centos8 安装 nginx的详细教程(图文)
- Ubuntu系统日志配置 /var/log/messages的方法
- 在Ubuntu 14 上安装 Nginx-RTMP 流媒体服务器的教程
- centos8自定义目录安装nginx(教程详解)
- VMware下Centos7桥接方式网络配置步骤详解
- Linux CentOS 6.5 卸载、tar安装MySQL的教程