Linux 64位下栈布局

来源:互联网 发布:身边有关大数据的例子 编辑:程序博客网 时间:2024/06/05 00:07

看了《coredump问题原理探究》系列博文后。在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
rax            0x3202152a20     214783306272
rbx            0x3201c1bbc0     214777838528
rcx            0x4      4
rdx            0x7fffb28031c8   140736188133832
rsi            0x7fffb28031b8   140736188133816
rdi            0x1      1
rbp            0x7fffb28030d0   0x7fffb28030d0
rsp            0x7fffb28030d0   0x7fffb28030d0
r8             0x3202151370     214783300464
r9             0x3201a0d640     214775682624
r10            0x0      0
r11            0x3201e33510     214780032272
r12            0x0      0
r13            0x7fffb28031b0   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的值。

原创粉丝点击