《coredump问题原理探究》Linux x86版3.4节栈布局之函数参数

上面已经讨论当有局部变量空参数的函数时栈的布局,那么当函数具有局部变量和参数,那么栈布局又会怎样?

先看一个例子:

int func( int c, char* s, int off )
{
     int a = 0x12345678;
     int *p = &a;
     int res = c + *( s + off );

     return *p + res;
}
 
int main()
{
     int b = 0x87654321;
 
     return b + func( 0x100, "hello", 3 );
}

看一下main函数的汇编:

(gdb) disassemble main
Dump of assembler code for function main:
   0x08048405 <+0>:     push   %ebp
   0x08048406 <+1>:     mov    %esp,%ebp
   0x08048408 <+3>:     sub    $0x1c,%esp
   0x0804840b <+6>:     movl   $0x87654321,-0x4(%ebp)
   0x08048412 <+13>:    movl   $0x3,0x8(%esp)
   0x0804841a <+21>:    movl   $0x80484d4,0x4(%esp)
   0x08048422 <+29>:    movl   $0x100,(%esp)
   0x08048429 <+36>:    call   0x80483d0 <_Z4funciPci>
   0x0804842e <+41>:    mov    -0x4(%ebp),%edx
   0x08048431 <+44>:    add    %edx,%eax
   0x08048433 <+46>:    leave  
   0x08048434 <+47>:    ret    
End of assembler dump.

0x08048412 0x08048422 的汇编语句可以看到,esp, esp+4,esp+8这三个单元依次存放着0x100,0x80484d4,3。而0x80484d4存放的内容可以如下获得:

(gdb) x /s 0x80484d4
0x80484d4 <__dso_handle+4>:      "hello"

可见,刚好和依次传入func函数的参数0x100,”hello”, 3一样。可知正是那三个参数。

当执行

  0x08048429 <+36>:    call   0x80483d0 <_Z4funciPci>

进入func函数时,会把main函数的返回地址压入栈,这时,栈的布局应该如下:

esp:返回地址
esp+4:0x100
esp+8:”hello”的地址
esp+0xC:3

那么当func函数执行完它的开头特征指令

push   %ebp
mov    %esp,%ebp

栈的布局应该如下:

esp:main函数桢指针
esp+4:返回地址
esp+8:0x100
esp+0xC:“hello”的地址
esp+0x10:3


由于这时ebp和esp相等,而esp会用于push、pop、sub、add等操作来申请和释放局部变量空间。所以栈布局应该是ebp为基准。所以上面栈布局应该表述如下:

ebp:main函数桢指针
ebp+4:返回地址
ebp+8:0x100
ebp+0xC:“hello”的地址
ebp+0x10:3

在func打断点来看一下,是不是如此:

(gdb) tbreak func                  
Temporary breakpoint 1 at 0x80483d6
(gdb) r
Starting program: /home/buckxu/work/3/3/xuzhina_dump_c3_s3 

Temporary breakpoint 1, 0x080483d6 in func(int, char*, int) ()
(gdb) x /8x $ebp
0xbffff4b4:     0xbffff4d8      0x0804842e      0x00000100      0x080484d4
0xbffff4c4:     0x00000003      0x0804844b      0xb7fbeff4      0x08048440
(gdb) info symbol 0x0804842e
main + 41 in section .text of /home/buckxu/work/3/3/xuzhina_dump_c3_s3
(gdb) x /s 0x080484d4
0x80484d4 <__dso_handle+4>:      "hello"

果然如此。

 

由上面的探讨可知,函数的参数有如下规则:

1.      参数在栈上的排列和它的声明顺序是一致的。

2.      第一个参数是在ebp+8开始,第二个参数是放在ebp+0xC,其它依此类推。

到了这里,可以知道完整的栈布局应该如下图:

《coredump问题原理探究》Linux x86版3.4节栈布局之函数参数_第1张图片

从图上还可以得到这样的规律:

如果两个函数桢指针fp1,fp2,fp1正好指向fp2的单元。那么它们满足这样的关系:

1.      esp < fp1 < fp2

2.      fp1的下一个单元的内容ret1肯定是返回地址,fp2的下一个单元的内容ret2也肯定是返回地址。即ret1,ret2都可以用info symbol命令来获取它们位于哪些函数里。


探讨完了函数参数的知识,在这里探究一下main函数的参数。虽然main函数在写的时候,可以是

int main()

也可以是

int main( int argc, char* argv[] )

 

实际上,main函数的原型是这样的:

int main( int argc, char* argv[], char* envp[ ] )


从上面的

(gdb) tbreak func                  
Temporary breakpoint 1 at 0x80483d6
(gdb) r
Starting program: /home/buckxu/work/3/3/xuzhina_dump_c3_s3 

Temporary breakpoint 1, 0x080483d6 in func(int, char*, int) ()
(gdb) x /8x $ebp
0xbffff4b4:     0xbffff4d8      0x0804842e      0x00000100      0x080484d4
0xbffff4c4:     0x00000003      0x0804844b      0xb7fbeff4      0x08048440

可知,main函数桢指针是0xbffff4d8。现在看一下0xbffff4d8 +80xbffff4d8+0xC, 0xbffff4d8+0x10的内容是什么?

(gdb) x /4x 0xbffff4d8+8
0xbffff4e0:     0x00000001      0xbffff574      0xbffff57c      0xb7fdd6b0

由于程序运行没有参数,所以argc为1,而argv的值为0xbffff574,envp的值为0xbffff57c。


先看一下argv的内容:

(gdb) x /4x 0xbffff574
0xbffff574:     0xbffff6b9      0x00000000      0xbffff6e2      0xbffff6f3
(gdb) x /s 0xbffff6b9
0xbffff6b9:      "/home/buckxu/work/3/3/xuzhina_dump_c3_s3"

正好和程序的路径一样。




再看一下envp的内容:

(gdb) x /4x 0xbffff57c   
0xbffff57c:     0xbffff6e2      0xbffff6f3      0xbffff704      0xbffff714
(gdb) x /s 0xbffff6e2
0xbffff6e2:      "XDG_SESSION_ID=2"
(gdb) x /s 0xbffff6f3
0xbffff6f3:      "HOSTNAME=xuzhina"
(gdb) x /s 0xbffff704
0xbffff704:      "SHELL=/bin/bash"
(gdb) x /s 0xbffff714
0xbffff714:      "TERM=linux"

正好和

(gdb) shell set
COLUMNS=80
DIRSTACK=()
EUID=1000
GROUPS=()
HISTCONTROL=ignoredups
HISTSIZE=1000
HOME=/home/buckxu
HOSTNAME=xuzhina
HOSTTYPE=i686
PWD=/home/buckxu/work/3/3
QT_GRAPHICSSYSTEM_CHECKED=1
SHELL=/bin/bash 
SHELLOPTS=braceexpand:hashall:interactive-comments
SHLVL=2
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SSH_AUTH_SOCK=/tmp/ssh-gORyjvO849/agent.849
SSH_TTY=/dev/pts/0
TERM=linux
UID=1000
USER=buckxu
XDG_RUNTIME_DIR=/run/user/buckxu
XDG_SESSION_ID=2
_=/bin/gdb

一样。














你可能感兴趣的:(C++,C++,linux,linux,linux,X86,X86,调试技术,程序崩溃)