精华内容
下载资源
问答
  • 栈帧结构简析

    2021-06-09 17:11:02
    主要使用32位和64位 gcc编译器分别进行编译,并查看相应的栈帧结构。 2. aach32栈帧分析 //ARCH: armv7 //GCC版本:arm-linux-gnueabi-gcc (Linaro GCC 5.4-2017.01) 5.4.1 20161213 int fun(int a,int b) { int c =...

    1. 前言

    函数的栈帧可以显示栈当前的使用情况,对于理解栈的使用以及定位bug比较有帮助。本文就使用一个简单的函数调用示例来学习栈帧的分布。主要使用32位和64位 gcc编译器分别进行编译,并查看相应的栈帧结构。

    2. aach32栈帧分析

    //ARCH: armv7
    //GCC版本:arm-linux-gnueabi-gcc (Linaro GCC 5.4-2017.01) 5.4.1 20161213
    int fun(int a,int b)
    {
       int c = 1;
       int d = 2;
       
       return 0;
    }
    
    int main(int argc,char **argv)
    {
       int a = 0;
       int b = 1;
       fun(a,b);
    }
    

    arm-linux-gnueabihf-gc -d a.out
    反汇编后的结果:

    ......
    00010398 <fun>:
       //将fun函数的栈底指针r7入栈
       10398:       b480            push    {r7}
       //分配20个字节的栈空间
       1039a:       b085            sub     sp, #20
       //fun函数的帧指针指向栈底
       1039c:       af00            add     r7, sp, #0
       //形参0入栈
       1039e:       6078            str     r0, [r7, #4]
       //形参1入栈
       103a0:       6039            str     r1, [r7, #0]
       //局部变量c入栈
       103a2:       2301            movs    r3, #1
       103a4:       60fb            str     r3, [r7, #12]
       //局部变量d入栈
       103a6:       2302            movs    r3, #2
       103a8:       60bb            str     r3, [r7, #8]
       //保存返回值为0
       103aa:       2300            movs    r3, #0
       103ac:       4618            mov     r0, r3
       //恢复sp为main函数的栈指针
       103ae:       3714            adds    r7, #20
       103b0:       46bd            mov     sp, r7
       //恢复fp(r7)为main函数的帧指针
       103b2:       bc80            pop     {r7}
       //返回到main函数
       103b4:       4770            bx      lr
       103b6:       bf00            nop
    
    000103b8 <main>:
       //将main函数的栈底和lr入栈
       103b8:       b580            push    {r7, lr}
       //分配16字节的栈空间
       103ba:       b084            sub     sp, #16
       103bc:       af00            add     r7, sp, #0
       //形参argv入栈
       103be:       6078            str     r0, [r7, #4]
       //形参argc入栈
       103c0:       6039            str     r1, [r7, #0]
       //局部变量a入栈
       103c2:       2300            movs    r3, #0
       103c4:       60fb            str     r3, [r7, #12]
       //局部变量b入栈
       103c6:       2301            movs    r3, #1
       103c8:       60bb            str     r3, [r7, #8]
       //局部变量b存放到r1寄存器
       103ca:       68b9            ldr     r1, [r7, #8]
       //局部变量a存放到r0寄存器
       103cc:       68f8            ldr     r0, [r7, #12]
       //跳转到fun函数
       103ce:       f7ff ffe3       bl      10398 <fun>
       103d2:       2300            movs    r3, #0
       103d4:       4618            mov     r0, r3
       103d6:       3710            adds    r7, #16
       103d8:       46bd            mov     sp, r7
       103da:       bd80            pop     {r7, pc}
    ......
    

    栈帧结构:
    在这里插入图片描述

    需要注意的是从上面的示例可以看到栈是如何被使用的,通过反汇编也可以看到是如何恢复到调用函数的,但是通过如上分析并不能看出栈帧是如何回溯的,因此栈帧的回溯需要依赖其他的部分。

    3. aach64栈帧分析

    //ARCH: armv8
    //GCC版本:aarch64-linux-gnu-gcc (Linaro GCC 5.4-2017.01) 5.4.1 20161213
    int fun2(int c,int d)
    {
       return 0;
    }
    
    int fun1(int a,int b)
    {
       int c = 1;
       int d = 2;
       
       fun2(c, d);
       return 0;
    }
    
    int main(int argc,char **argv)
    {
       int a = 0;
       int b = 1;
       fun1(a,b);
    }
    

    aarch64-linux-gnu-objdump -d a.out
    反汇编后的结果为:

    0000000000400530 <fun2>:
      //更新sp到fun2的栈底
      400530:       d10043ff        sub     sp, sp, #0x10
      400534:       b9000fe0        str     w0, [sp,#12]
      400538:       b9000be1        str     w1, [sp,#8]
      40053c:       52800000        mov     w0, #0x0                        // #0
      400540:       910043ff        add     sp, sp, #0x10
      400544:       d65f03c0        ret
    
    0000000000400548 <fun1>:
      //分配48字节栈空间,先更新sp=sp-48, 再入栈x29, x30, 此时sp指向栈顶
      400548:       a9bd7bfd        stp     x29, x30, [sp,#-48]!
      40054c:       910003fd        mov     x29, sp
      //入栈fun1参数0
      400550:       b9001fa0        str     w0, [x29,#28]
      //入栈fun1参数1
      400554:       b9001ba1        str     w1, [x29,#24]
      //入栈fun1局部变量c
      400558:       52800020        mov     w0, #0x1                        // #1
      40055c:       b9002fa0        str     w0, [x29,#44]
      //入栈fun1局部变量d
      400560:       52800040        mov     w0, #0x2                        // #2
      400564:       b9002ba0        str     w0, [x29,#40]
      400568:       b9402ba1        ldr     w1, [x29,#40]
      40056c:       b9402fa0        ldr     w0, [x29,#44]
      //跳转到fun2
      400570:       97fffff0        bl      400530 <fun2>
      400574:       52800000        mov     w0, #0x0                        // #0
      400578:       a8c37bfd        ldp     x29, x30, [sp],#48
      40057c:       d65f03c0        ret
    
    0000000000400580 <main>:
      //分配48字节栈空间,先更新sp=sp-48, 再入栈x29, x30, 此时sp指向栈顶
      400580:       a9bd7bfd        stp     x29, x30, [sp,#-48]!
      //x29、sp指向栈顶
      400584:       910003fd        mov     x29, sp
      //入栈main参数0
      400588:       b9001fa0        str     w0, [x29,#28]
      //入栈main参数1
      40058c:       f9000ba1        str     x1, [x29,#16]
      //入栈变量a
      400590:       b9002fbf        str     wzr, [x29,#44]
      400594:       52800020        mov     w0, #0x1                        // #1
      //入栈变量b
      400598:       b9002ba0        str     w0, [x29,#40]
      40059c:       b9402ba1        ldr     w1, [x29,#40]
      4005a0:       b9402fa0        ldr     w0, [x29,#44]
      //跳转到fun1
      4005a4:       97ffffe9        bl      400548 <fun1>
      4005a8:       52800000        mov     w0, #0x0                        // #0
      4005ac:       a8c37bfd        ldp     x29, x30, [sp],#48
      4005b0:       d65f03c0        ret
      4005b4:       00000000        .inst   0x00000000 ; undefined
    

    对应栈帧结构为:
    在这里插入图片描述

    总结一下:
    通过对aarch64代码反汇编的分析,可以得出:

    1. 每个函数在入口处首先会分配栈空间,且一次分配,确定栈顶,之后sp将不再变化;
    2. 每个函数的栈顶部存放的是caller的栈顶指针,即fun1的栈顶存放的是main栈顶指针;
    3. 对于最后一级callee函数,由于x29保存了上一级caller的栈顶sp指针,因此不在需要入栈保存,如示例中fun2执行时,此时x29指向fun1的栈顶sp

    4. x86_64栈帧分析

    X86-64有16个64位寄存器,分别是:
    %rax,%rbx,%rcx,%rdx,%esi,%edi,%rbp,%rsp,%r8,%r9,%r10,%r11,%r12,%r13,%r14,%r15。
    其中:
    %rax 作为函数返回值使用。
    %rsp 栈指针寄存器,指向栈顶
    %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数…
    %rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防他被修改
    %r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值
    %rip:相当pc,指向下一条将要运行的指令的地址
    在这里插入图片描述
    注:rip相当于arm中的pc
    ref: :https://blog.csdn.net/zhbt1234/article/details/54019620

    //ARCH: x86_64
    //GCC版本:gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
    int fun2(int c,int d)
    {
       return 0;
    }
    
    int fun1(int a,int b)
    {
       int c = 1;
       int d = 2;
       
       func2(c, d);
       return 0;
    }
    
    int main(int argc,char **argv)
    {
       int a = 0;
       int b = 1;
       fun1(a,b);
    }
    

    objdump -d a.out
    反编译后的结果如下:

    00000000000005fa <fun2>:
    //rbp保存fun1函数的栈底,入栈
     5fa:   55                      push   %rbp
     //初始化fun2函数的帧指针为栈底
     5fb:   48 89 e5                mov    %rsp,%rbp
     //参数c入栈
     5fe:   89 7d fc                mov    %edi,-0x4(%rbp)
     //参数d入栈
     601:   89 75 f8                mov    %esi,-0x8(%rbp)
     //返回值0
     604:   b8 00 00 00 00          mov    $0x0,%eax
     //恢复fun1的栈底指针rbp
     609:   5d                      pop    %rbp
     60a:   c3                      retq   
    
    000000000000060b <fun1>:
     //rbp保存main函数的栈底,入栈
     60b:   55                      push   %rbp
     //初始化fun1函数的帧指针为栈底
     60c:   48 89 e5                mov    %rsp,%rbp
     //分配栈空间
     60f:   48 83 ec 18             sub    $0x18,%rsp
     //参数a入栈
     613:   89 7d ec                mov    %edi,-0x14(%rbp)
     //参数b入栈
     616:   89 75 e8                mov    %esi,-0x18(%rbp)
     //局部变量c入栈
     619:   c7 45 f8 01 00 00 00    movl   $0x1,-0x8(%rbp)
     //局部变量d入栈
     620:   c7 45 fc 02 00 00 00    movl   $0x2,-0x4(%rbp)
     //取出参数c,d分别存放到edi, esi寄存器准备调用fun1函数
     627:   8b 55 fc                mov    -0x4(%rbp),%edx
     62a:   8b 45 f8                mov    -0x8(%rbp),%eax
     62d:   89 d6                   mov    %edx,%esi
     62f:   89 c7                   mov    %eax,%edi
     //调用fun2
     631:   e8 c4 ff ff ff          callq  5fa <fun2>
     636:   b8 00 00 00 00          mov    $0x0,%eax
     63b:   c9                      leaveq 
     63c:   c3                      retq   
    
    000000000000063d <main>:
     //rbp保存上一个函数的栈底,入栈
     63d:   55                      push   %rbp
     //初始化main函数的帧指针为栈底
     63e:   48 89 e5                mov    %rsp,%rbp
     //分配0x20的栈空间
     641:   48 83 ec 20             sub    $0x20,%rsp
     //入栈参数0
     645:   89 7d ec                mov    %edi,-0x14(%rbp)
     //入栈参数1
     648:   48 89 75 e0             mov    %rsi,-0x20(%rbp)
     //入栈局部变量a
     64c:   c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
     //入栈局部变量b
     653:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
     //取出参数a,b分别存放到edi, esi寄存器准备调用fun1函数
     65a:   8b 55 fc                mov    -0x4(%rbp),%edx
     65d:   8b 45 f8                mov    -0x8(%rbp),%eax
     660:   89 d6                   mov    %edx,%esi
     662:   89 c7                   mov    %eax,%edi
     //调用fun1函数
     664:   e8 a2 ff ff ff          callq  60b <fun1>
     669:   b8 00 00 00 00          mov    $0x0,%eax
     66e:   c9                      leaveq 
     66f:   c3                      retq 
    

    根据如上反汇编代码,通过gdb单步实时查看当前栈的情况:

    main:
    -----------------------
    rsp:0x7fffffffe9c0 入栈main函数的父函数的rip 
        0x7fffffffe9b8 入栈main函数的父函数的rbp
              
    
    rbp: 0x7fffffffe9b8
    
    0x7fffffffe998: 0xffffeac8      0x00007fff      0x00000001      0x00000001
                    |     main参数1          |                      |main参数0|
    0x7fffffffe9a8: 0x555546cd      0x00005555      0x00000000      0x00000001
                                                    |局部变量a|      |局部变量b|
    0x7fffffffe9b8: 0xffffe9e0      0x00007fff      0xffffeac8      0x00007fff
                    |    main函数父函数的rbp  |      |     main函数父函数的rip  |
                    
         
    
    fun1:
    ----------------------
    rsp:0x7fffffffe990  入栈main函数的rip
        0x7fffffffe988  入栈main函数的rbp
        0x7fffffffe970  分配栈空间
    
    rbp:0x7fffffffe988  
    
    
    0x7fffffffe968: 0x00000000      0x00000000      0x00000001      0x00000000
                                                    |fun1参数b|     |fun1参数a|
    0x7fffffffe978: 0x756e6547      0x00000001      0x00000002      0x00000000
                                                    |局部变量c|      |局部变量d|
    0x7fffffffe988: 0xffffe9b8      0x00007fff      0x55554672      0x00005555
                    |    main函数的rbp        |      |     main函数的rip      |                                   
    
    
    func2:
    -----------------------
    rsp: 0x7fffffffe968 入栈fun1函数的rip
         0x7fffffffe960 入栈fun1函数的rbp
         
         
    
    rbp: 0x7fffffffe960
    
    
    0x7fffffffe960: 0xffffe988      0x00007fff      0x55554636      0x00005555
                    |    fun2函数的rbp       |      |     fun2函数的rip       | 
    
    
    fun2返回时:
    --------------------------
    rsp:0x7fffffffe968 出栈fun1函数的rbp
        0x7fffffffe970 出栈fun1函数的rip
    
    rbp:0x7fffffffe988
    
    fun1返回时:
    -------------------------
    rsp:0x7fffffffe990 出栈main函数的rbp
        0x7fffffffe998 出栈main函数的rip
    
    rbp:0x7fffffffe9b8
    

    栈帧结构为:
    在这里插入图片描述

    总结一下:
    通过对x86 64代码反汇编的分析,与aarch64还是有区别的,可以得出:

    1. 每个函数在入口处首先会分配栈空间,且一次分配,确定栈顶,之后rsp将不再变化;
    2. 每个函数的栈底部存放的是caller的栈底指针,即fun1的栈底存放的是main栈底指针;
    3. 对于最后一级callee函数,rbp指向自身栈底,如示例中fun2执行时,此时rbp指向fun2的栈底

    总结一句话:
    aarch64当前栈顶保存上一级函数的栈顶;x86_64当前栈底保存上一级函数的栈底

    参考文档

    1. https://gcc.gnu.org/gcc-5/changes.html#arm
    2. https://f5.pm/go-30007.html
    3. https://developer.arm.com/documentation/ihi0038/b/
    4. 内核中dump_stack的实现原理(1) —— 栈回溯
    展开全文
  • 栈帧结构

    2017-12-13 21:19:52
  • ARM64平台上的栈帧寄存器是FP,它记录的是一个函数执行过程中的栈顶(FP=SP),并且把父函数的FP保存在堆栈的栈顶,以便于回溯 X86-64平台上的栈帧寄存器是RBP,它记录的是一个函数执行过程中的栈底,并且把父函数的...

    x86-64

    在这里插入图片描述

    ARM64

    在这里插入图片描述

    差异点

    • ARM64平台上的栈帧寄存器是FP,它记录的是一个函数执行过程中的栈顶(FP=SP),并且把父函数的FP保存在堆栈的栈顶,以便于回溯
    • X86-64平台上的栈帧寄存器是RBP,它记录的是一个函数执行过程中的栈底,并且把父函数的RBP位置保存到本函数的栈底,以便于回溯
    展开全文
  • 详解栈帧结构

    千次阅读 2019-07-26 10:42:16
    栈帧结构 含义:C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。栈帧也叫过程活动记录,是编译器用来实现过程函数调用的一种数据结构。 从逻辑上讲,栈帧就是一个函数...

    https://www.1024do.com/?p=367

    栈帧结构

    含义:C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。栈帧也叫过程活动记录,是编译器用来实现过程函数调用的一种数据结构。

    从逻辑上讲,栈帧就是一个函数执行的环境:函数参数、函数的局部变量、函数执行完后返回到哪里等等。实现上有硬件方式和软件方式(有些体系不支持硬件栈)首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。

    注意:ebp指向当前位于系统栈最上边一个栈帧的底部,而不是系统栈的底部。严格说来,“栈帧底部”和“栈底”是不同的概念;esp所指的栈帧顶部和系统栈的顶部是同一个位置。

    首先画一个地址空间图给大家加深理解:

     

    既然今天分析的是栈帧,那么肯定是在栈区展开研究,下面为大家总体上画一张栈帧结构图:

     

    接下来,首先看一下变量压栈的次序以及对应的汇编代码:

     

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    #include <stdio.h>

    #include <windows.h>

    int fun(int x, int y)

    {

    int c = 0xcccccccc;

    printf("I am fun function!\n");

    return c;

    }

    int main()

    {

    int a = 0xaaaaaaaa;

    int b = 0xbbbbbbbb;

    int ret = fun(a, b);

    printf("you should running here!\n");

    system("pause");

    return 0;

    }

     

    在main函数调用fun函数之前,也就是main的栈帧结构:main函数的栈底ebp,栈顶esp。从低地址esp到高地址ebp,就是main函数的栈帧:

     

    现在开始调用fun函数:这里用到了一条汇编指令:call:调用函数。

    call有两大功能:①保存当前指令的下一条指令②修改栈底指针ebp。换句话说:call指令的效果是将返回地址入栈,并跳转到被调用过程的起始点。返回地址是在程序中紧跟call后面的那条指令的地址,这样被调用过程返回时,执行会从此处开始。

    同样的一点,我们首先看一下汇编代码:

     

    从代码和图中不难看出,在main函数里面,地址为008B14A4 的call指令调用函数fun,其中指明了栈帧esp和程序计算器pc的值,call指令随即将返回地址008B14A9压栈,并跳转到fun的第一条指令。到此main栈帧结束。

    Call命令出现预示着一个旧的栈帧的结束,也印证了新的栈帧的到来:(汇编代码过长,图中将取重要的点来说)

     

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    #include <stdio.h>

    #include <windows.h>

    int fun(int x, int y)

    {

    int c = 0xcccccccc;

    int *p = &x;

    printf("I am fun function!\n");

    p++;

    printf("before: %x\n", y);

    *p = 0xdddddddd;

    printf("after : %x\n", y);

     

    return c;

    }

     

    int main()

    {

    int a = 0xaaaaaaaa;

    int b = 0xbbbbbbbb;

    int ret = fun(a, b);

    printf("you should running here!\n");

    system("pause");

    return 0;

    }

     

     

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    int fun(int x, int y)

    {

    008B13A0  push        ebp  

    008B13A1  mov         ebp,esp  

    008B13A3  sub         esp,0D8h  

    008B13A9  push        ebx  

    008B13AA  push        esi  

    008B13AB  push        edi  

    008B13AC  lea         edi,[ebp-0D8h]  

    008B13B2  mov         ecx,36h  

    008B13B7  mov         eax,0CCCCCCCCh  

    008B13BC  rep stos    dword ptr es:[edi]  

    int c = 0xcccccccc;

    008B13BE  mov         dword ptr [c],0CCCCCCCCh  

    int *p = &x;

    008B13C5  lea         eax,[x]  

    008B13C8  mov         dword ptr [p],eax  

    printf("I am fun function!\n");

    008B13CB  mov         esi,esp  

    008B13CD  push        offset string "I am fun function!\n" (8B575Ch)  

    008B13D2  call        dword ptr [__imp__printf (8B82B8h)]  

    008B13D8  add         esp,4  

    008B13DB  cmp         esi,esp  

    008B13DD  call        @ILT+305(__RTC_CheckEsp) (8B1136h)  

    p++;

    008B13E2  mov         eax,dword ptr [p]  

    008B13E5  add         eax,4  

    008B13E8  mov         dword ptr [p],eax  

    printf("before: %x\n", y);

    008B13EB  mov         esi,esp  

    008B13ED  mov         eax,dword ptr [y]  

    008B13F0  push        eax  

    008B13F1  push        offset string "before: %x\n" (8B574Ch)  

    008B13F6  call        dword ptr [__imp__printf (8B82B8h)]  

    008B13FC  add         esp,8  

    008B13FF  cmp         esi,esp  

    008B1401  call        @ILT+305(__RTC_CheckEsp) (8B1136h)  

    *p = 0xdddddddd;

    008B1406  mov         eax,dword ptr [p]  

    008B1409  mov         dword ptr [eax],0DDDDDDDDh  

    printf("after : %x\n", y);

    008B140F  mov         esi,esp  

    008B1411  mov         eax,dword ptr [y]  

    008B1414  push        eax  

    008B1415  push        offset string "after : %x\n" (8B573Ch)  

    008B141A  call        dword ptr [__imp__printf (8B82B8h)]  

    008B1420  add         esp,8  

    008B1423  cmp         esi,esp  

    008B1425  call        @ILT+305(__RTC_CheckEsp) (8B1136h)  

     

    return c;

    008B142A  mov         eax,dword ptr [c]  

    }

    008B142D  pop         edi  

    008B142E  pop         esi  

    008B142F  pop         ebx  

    008B1430  add         esp,0D8h  

    008B1436  cmp         ebp,esp  

    008B1438  call        @ILT+305(__RTC_CheckEsp) (8B1136h)  

    008B143D  mov         esp,ebp  

    008B143F  pop         ebp  

    008B1440  ret

     

    展开全文
  • jvm栈帧结构

    2019-09-03 08:16:25
    栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区的虚拟机栈(Virtual Machine Stack)的栈元素。栈帧存储了方法的局部变量表,操作数栈,动态连接和方法返回地址等信息。...
  • JVM中的栈帧结构

    千次阅读 2019-08-19 22:10:02
    栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用...
  • C语言:函数的栈帧结构

    千次阅读 2018-05-10 12:55:17
    栈帧也就是函数的具体调用过程:函数的调用,参数的传递,函数执行完之后的返回等等! 我们先来一段简单的c语言代码:#include &lt;stdio.h&gt; #include &lt;windows.h&gt; int add(int A,int B)...
  • Java编译原理--运行时栈帧结构

    千次阅读 2018-09-05 21:20:42
    Java虚拟机规定了虚拟机执行字节码的概念模型,这个模型是各类虚拟机的外观结构,不同的虚拟机可以有不同的实现,但是从外部看起来它们都是统一的,输入的是二进制字节流,经过执行引擎处理之后,输出执行结果。...
  • 栈帧结构(程序调用栈结构)详解 知识点扫描 ESP:栈指针寄存器(extended stack pointer),放着一个指针,该指针永远指向系统栈最上面一个栈帧(栈帧不理解没关系)的栈顶。 EBP:基址指针寄存器(extended base ...
  • 栈帧结构详解

    千次阅读 2021-03-25 22:53:40
    Java虚拟机以方法作为基本的执行单位,“栈帧”是用于支持虚拟机进行方法调用和执行的数据结构,每一个方法从调用开始到执行结束,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程,栈帧也是虚拟机运行时数据区中...
  • 了解栈帧结构

    千次阅读 2018-05-13 16:38:46
    首先我们要知道研究栈帧的目的:函数调用时形成的临时变量的存储单元在栈上创建、释放 栈的概念: 下面有图解 每个任务(进程)有一个栈(stack),在这个进程中每个函数被调用时分别从这个栈占用一段区域,称为帧...
  • 这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器、硬件、指令集和操作系统层面上的,而虚拟机的执行引擎则是由自己实现的,因此可以自行制定指令集与执行引擎的结构体系,并且能够执行那些...
  • 函数的栈帧结构

    千次阅读 2016-11-22 12:28:50
    每一个函数都有一个自己的栈帧结构,但是, 在地址空间中,为单个过程(我们暂且将这个过程理解为函数)分配的那部分栈就叫做栈帧。栈帧的最顶端以两个指针界定——帧指针(寄存器ebp)和栈指针(esp)
  • 下图描述了栈帧的通用结构栈帧是一段有界限的内存区间,由最顶端的两个指针界定,寄存器%ebp为帧指针,而寄存器%esp为栈指针(也就是说寄存器%ebp保存了所分配内存的最高地址,寄存器%esp保存...
  • 欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。...你还不知道,赶紧去看看《Java虚拟机内存结构及编码实战》)这次要介绍的栈帧(Stack Frame),就是Java虚拟机中的虚拟机栈(Virtual Machine Stack)...
  • C语言函数调用及栈帧结构

    千次阅读 2016-11-17 19:10:11
    紧接着当被调用者执行完毕时将消除栈帧结构,调用pop指令。 在把程序控制权返还给调用者前,被调用者foo必须先把返回值保存在EAX寄存器中。其次,foo必须恢复EBX,ESI和EDI寄存器的值。进栈和出栈操作的次数必须...
  • 栈介绍&栈帧结构&堆栈操作

    千次阅读 2020-09-17 10:36:06
    栈是一种典型的后进先出( Last in First Out )的数据结构,其操作主要有压栈(push)与出栈(pop)两种操作,如下图所示(维基百科)。两种操作都操作栈顶,当然,它也有栈底。 高级语言在运行时都会被转换为汇编程序,...
  • 运行时栈帧结构

    2021-03-05 14:23:41
    【待完成】
  • 2.栈帧结构详解 2.1 局部变量表 (1)变量槽Slot (2)虚拟机使用局部变量表的方式 (3)实例方法的参数和局部变量的Slot分配顺序 (4)局部变量表Slot复用 (5)局部变量必须被初始化 2.2 操作数栈 2.3 ...
  • Java —— 运行时栈帧结构

    万次阅读 多人点赞 2018-01-21 16:06:06
    概述栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构。它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。
  • c语言--栈帧结构分析

    千次阅读 2018-04-02 12:41:18
    写在前边:栈帧结构对于初学C语言着,可能有些陌生。但是不能否认的是栈帧在C语言中扮演着很重要的角色,下边就栈帧分析下内存。如果真对C语言感兴趣的请耐心的看下边的叙述。栈帧说的简单点就是调用函数的过程中,...
  • 栈帧结构的细化(C语言)

    千次阅读 2016-12-20 16:20:59
    C语言中关于栈帧:由于在函数调用时往往会形成栈帧结构,为此我们经常有以下几个疑问: 1.只要给函数传递参数就会形成临时变量,这些临时变量会存在栈上,具体怎样存的? 2.函数内部定义的变量叫局部变量(自动变量...
  • arm gcc栈帧结构(1)

    2016-12-23 14:52:00
    寄存器 fp (桢指针)应当是零或者是指向栈回溯结构的列表中的最后一个结构,提供了一种追溯程序的方式,来反向跟踪调用的函数。 回溯结构是: 地址高端  保存代码指针 [fp] fp 指向这里  返回 lr 值...
  • 栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,他是虚拟机运行时数据区的虚拟机栈(Virtual Machine Stack)的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态链接库和方法返回地址等...
  • 14、运行时栈帧结构

    2020-03-26 23:00:06
    重叠:两个栈帧本来是完全相互独立的,jvm对栈帧进行了优化,让两个栈帧重叠在一起,这样就可以共用一部分数据,无需通过额外的参数进行传递。 slot的复用会影响垃圾回收行为: 我们在虚拟机运行参数中加上“-...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 44,159
精华内容 17,663
关键字:

栈帧结构