• ARM C函数调用堆栈入栈顺序


    ARM C函数调用堆栈入栈顺序

    堆栈指针是在函数一开头就确认了的,比如如下的xxx_func.cfi函数,它在函数的开头就将sp自减了0x170,这个0x170是xxx_fun.cfi函数局部变量total size + 需要入栈的reg total size 

    然后会设置x29(fp,栈底指针),这里看到是sp - 0x110,可以看到需要入栈的reg total size为0x60,所以fp指向了函数局部变量列表的头部,它是不包含函数里的局部变量的

    00000000000441c8 <xxx_func.cfi>:
       441c8:    d105c3ff     sub    sp, sp, #0x170
       441cc:    a9117bfd     stp    x29, x30, [sp,#272]
       441d0:    a9126ffc     stp    x28, x27, [sp,#288]
       441d4:    a91367fa     stp    x26, x25, [sp,#304]
       441d8:    a9145ff8     stp    x24, x23, [sp,#320]
       441dc:    a91557f6     stp    x22, x21, [sp,#336]
       441e0:    a9164ff4     stp    x20, x19, [sp,#352]
       441e4:    910443fd     add    x29, sp, #0x110
       441e8:    90000008     adrp    x8, 0 <__stack_chk_guard>

     当函数返回时,从stack弹出值到之前保护reg而入栈的reg里,这里可以看到出栈时顺序恰好和入栈时相反,即是后入先出,将fp、lr从stack弹出到fp、lr即实现返回:

       447f4:    a9564ff4     ldp    x20, x19, [sp,#352]
       447f8:    a95557f6     ldp    x22, x21, [sp,#336]
       447fc:    a9545ff8     ldp    x24, x23, [sp,#320]
       44800:    a95367fa     ldp    x26, x25, [sp,#304]
       44804:    a9526ffc     ldp    x28, x27, [sp,#288]
       44808:    a9517bfd     ldp    x29, x30, [sp,#272]
       4480c:    9105c3ff     add    sp, sp, #0x170
       44810:    d65f03c0     ret

    函数调用stack变化,入栈顺序

    以main()里有int a、int b两个局部变量并call了一个test_func(int i, int j),在test_func()里有define一个int c变量为例,来看下调用test_func()时的堆栈变化:

    调用test_func时,test_func参数i、j先反序入栈,然后是给test_func的返回值预留空间,然后是test_func的返回地址;

    接下来是在test_func函数里将需要保存的reg入栈,其中x29、x30是必须要入栈的,此时会将fp指向当前的堆栈顶部,这里x29、x30是最后才入栈的;

    然后再是为test_func中的局部变量开辟堆栈空间,此时将sp指向此时的堆栈顶部:

                                    |--------------------|
                                    |.....               |
                                    |--------------------|
                                    |int a               |
                                    |--------------------|
                                    |int b               |
                                    |--------------------|
                                    |int j               |
                                    |--------------------|
                                    |int i               |
                                    |--------------------|
                                    |test_func返回值预留   |
                                    |--------------------|
                                    |test_func返回地址    |
                                    |--------------------|
                               fp-->|x29, x30            |     /*如果test_func还需要保护其它reg,将其它reg也入栈*/
                                    |--------------------|
                               sp-->|int c               |
                                    |--------------------|
    int test_func(int i, int j)
    {
        int c = i + j;
    return c; }
    int main() { int a = 3; int b =4; test_func(3, 4); return 0; }

    所以fp指针是指向的当前函数的stack底部、sp指向的是stack顶部,在fp和sp之间即是test_func的所有局部变量区间,如果test_func没有一个局部变量,fp、sp将指向同一个位置,即都指向当前函数堆栈顶部。

    如下是我自己在kernel里写的一个测试函数确认的入栈顺序:

    			--------	|-------------------------|	--------
    					|	                  |
    					|-------------------------|	0x10
    					|		x19       |
    					|-------------------------|
    			init_module()	|		x30       |
    			stack		|-------------------------|	0x10
    			size:	  fp->|		x29	 |
    			0x30		|-------------------------|
    					|  long __stack_chk_guard |
    					|-------------------------|	0x10
    				  sp->|	  |   int ret    |
    			--------	|-------------------------|	--------
    					|		x19       |
    					|-------------------------|	0x10
    					|		x20       |
    					|-------------------------|
    					|		x30       |
    					|-------------------------|	0x10
    			test_func() fp->|		x29       |
    			stack frame	|-------------------------|
    			size:		|  long __stack_chk_guard |
    			0x50		|-------------------------|	0x10
    					|  int b  |	int a     |
    					|-------------------------|
    					|	long c       |
    					|-------------------------|	0x10
    					|	long d         |
    					|-------------------------|
    					|	   |   int e     |
    					|-------------------------|	0x10
    				  sp->|		long fp   |
    			--------	|-------------------------|	--------

     对应C函数:

    static int __init hello_init(void){
        int ret;
    
        pr_info("ret's addr: %#px.
    ", &ret);
        test_func(3, 4);
    
        ret = register_chrdev(HELLO_MAJOR,DEVICE_NAME, &hello_flops);
        if (ret < 0) {
          printk(KERN_EMERG DEVICE_NAME " can't register major number.
    ");
          return ret;
        }
        printk(KERN_EMERG DEVICE_NAME " initialized.
    ");
    
        return 0;
    }
    int test_func(int a, int b)
    {
        long c = 10;
        long d = 10;
        int e = 11;
    
        unsigned long fp;
    
        asm ("mov %0, x29" : "=r" (fp));
        pr_info("a's addr: %#px.
    ", &a);
        pr_info("b's addr: %#px.
    ", &b);
    
    
        pr_info("fp is: %#lx.
    ", fp);
    
        pr_info("c's addr: %#px.
    ", &c);
        pr_info("d's addr: %#px.
    ", &d);
        pr_info("e's addr: %#px.
    ", &e);
        pr_info("fp's addr: %#px.
    ", &fp);
    
        c = a + b;
    
        pr_info("c: %d.
    ", c);
    
        return c;
    }

     对应汇编,下面init_module()即是hello_init()

    0000000000000000 <init_module>:
       0:   d100c3ff        sub     sp, sp, #0x30
       4:   a9017bfd        stp     x29, x30, [sp,#16]
       8:   f90013f3        str     x19, [sp,#32]
       c:   910043fd        add     x29, sp, #0x10
      10:   90000008        adrp    x8, 0 <__stack_chk_guard>
      14:   f9400108        ldr     x8, [x8]
      18:   f90007e8        str     x8, [sp,#8]
    0000000000000000 <test_func>:
       0:   d10143ff        sub     sp, sp, #0x50
       4:   a9037bfd        stp     x29, x30, [sp,#48]
       8:   a9044ff4        stp     x20, x19, [sp,#64]
       c:   9100c3fd        add     x29, sp, #0x30
      10:   90000008        adrp    x8, 0 <__stack_chk_guard>
      14:   f9400108        ldr     x8, [x8]
      18:   2a0103f3        mov     w19, w1
      1c:   2a0003f4        mov     w20, w0
      20:   f81f83a8        str     x8, [x29,#-8]
  • 相关阅读:
    13 Memcached 永久数据被踢现象
    PHP 学习内容
    12 Memcached 缓存无底洞现象
    Memcached 常用的方法
    PHP Memcached 面试题
    11 Memcached 缓存雪崩现象
    JQ报表插件
    (2.1)mysql升级与降级
    基于binlog恢复工具mysqlbinlog_flashback
    如何查看正在执行sql的语句及其父语句调用?如何查看正在执行SQL的具体参数值与执行计划?xml执行计划转为图形计划
  • 原文地址:https://www.cnblogs.com/aspirs/p/15542296.html
Copyright © 2020-2023  润新知