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]
原文地址:https://www.cnblogs.com/aspirs/p/15542296.html