红联Linux门户
Linux帮助

Linux 64位下栈布局

发布时间:2016-02-16 10:10:04来源:linux网站作者:biosd

在Linux 64位下分析了栈分布情况,在函数参数,局部变量等上面和32位有一些差别。现记录下来,以供参考。

首先在64位下,寄存器esp变成了rsp,ebp变成了rbp,ip变成了rip。


环境:

1.Linux内核版本:

>cat /proc/version
Linux version 2.6.18-128.7.1.el5 (brewbuilder@norob.fnal.gov) (gcc version 4.1.2 20080704 (Red Hat 4.1.2-44)) #1 SMP Mon Aug 24 08:12:52 EDT 2009


2.Linux版本:

>lsb_release -a

LSB Version::core-3.1-amd64:core-3.1-ia32:core-3.1-noarch:graphics-3.1-amd64:graphics-3.1-ia32:graphics-3.1-noarch

Distributor ID: ScientificSL

Description:Scientific Linux SL release 5.3 (Boron)

Release:5.3

Codename:   Boron


3.G++版本:

>g++ -v
Using built-in specs.
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre --with-cpu=generic --host=x86_64-redhat-linux
Thread model: posix
gcc version 4.1.2 20080704 (Red Hat 4.1.2-44)


参数传递:

原程序:

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 );

}


在《coredump问题研究》Linux X86版3.4节栈布局之函数参数篇中指出,esp, esp+4 esp+8 存放着0x100,"hello"的地址,以及3.

然而,实际上在64位下,这三个参数是通过寄存器来传递的,请看main的反汇编程序:

(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400686 <main+0>:push   %rbp
0x0000000000400687 <main+1>:mov%rsp,%rbp
0x000000000040068a <main+4>:sub$0x10,%rsp
0x000000000040068e <main+8>:movl   $0x87654321,-0x4(%rbp)
0x0000000000400695 <main+15>:   mov$0x3,%edx
0x000000000040069a <main+20>:   mov$0x400818,%esi
0x000000000040069f <main+25>:   mov$0x100,%edi
0x00000000004006a4 <main+30>:   callq  0x400648 <_Z4funciPci>
0x00000000004006a9 <main+35>:   add-0x4(%rbp),%eax
0x00000000004006ac <main+38>:   leaveq
0x00000000004006ad <main+39>:   retq  
End of assembler dump


可以看到100, “hello”的地址以及3由edi,esi以及edx来传递。我们可以看Func是怎么取得这三个参数的。

disassemble func
Dump of assembler code for function _Z4funciPci:
0x0000000000400648 <_Z4funciPci+0>: push   %rbp
0x0000000000400649 <_Z4funciPci+1>: mov%rsp,%rbp
0x000000000040064c <_Z4funciPci+4>: mov%edi,-0x24(%rbp)
0x000000000040064f <_Z4funciPci+7>: mov%rsi,-0x30(%rbp)
0x0000000000400653 <_Z4funciPci+11>:mov%edx,-0x34(%rbp)
0x0000000000400656 <_Z4funciPci+14>:movl   $0x12345678,-0x14(%rbp)
0x000000000040065d <_Z4funciPci+21>:lea-0x14(%rbp),%rax
0x0000000000400661 <_Z4funciPci+25>:mov%rax,-0x10(%rbp)
0x0000000000400665 <_Z4funciPci+29>:mov-0x34(%rbp),%eax


一进入func()函数,把main函数的帧地址压栈后,直接从edi,esi和edx中取出参数放到func帧地址的下面(也是func的栈上)。

同时我们可以在main函数开始处同过edi,esi和edx来得到main函数的参数,argc,argv和envp。

>tbreak *0x000000000040068a

>r

(gdb) info r
rax0x3202152a20 214783306272
rbx0x3201c1bbc0 214777838528
rcx0x4  4
rdx0x7fffb28031c8   140736188133832
rsi0x7fffb28031b8   140736188133816
rdi0x1  1
rbp0x7fffb28030d0   0x7fffb28030d0
rsp0x7fffb28030d0   0x7fffb28030d0
r8 0x3202151370 214783300464
r9 0x3201a0d640 214775682624
r100x0  0
r110x3201e33510 214780032272
r120x0  0
r130x7fffb28031b0   140736188133808


rdi的值是1,我们不好确定是不是argc的值,那我们看看rsi的值是什么。

 x /8x 0x7fffb28031b8
0x7fffb28031b8: 0x00007fffb280485b  0x0000000000000000
0x7fffb28031c8: 0x00007fffb2804884  0x00007fffb2804897
0x7fffb28031d8: 0x00007fffb28048ad  0x00007fffb28048c6
0x7fffb28031e8: 0x00007fffb280498e  0x00007fffb28049b1
(gdb) x /s 0x00007fffb280485b
0x7fffb280485b:  "/local/work/coredump/c3_s4"


确实是我们这个程序的名字。

同样,我们可以从rdx中得到envp的值。


本文永久更新地址:http://www.linuxdiyf.com/linux/18111.html