Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 理解aarch64汇编函数调用,堆栈是如何操作的_Assembly_Callstack_Arm64_Abi - Fatal编程技术网

Assembly 理解aarch64汇编函数调用,堆栈是如何操作的

Assembly 理解aarch64汇编函数调用,堆栈是如何操作的,assembly,callstack,arm64,abi,Assembly,Callstack,Arm64,Abi,测试c(裸金属) 在为main布局堆栈时,我增加了对的理解,编译器必须满足以下约束: x29和x30需要保存在堆栈上。它们每个占用8个字节 局部变量x,y,z需要堆栈空间,每个4字节。(如果你在优化,你会看到它们被保存在寄存器中,或者被完全优化了而不存在。)这使我们总共得到了8+8+4+4+4=28字节 堆栈指针sp必须始终与16字节对齐;这是一个体系结构和ABI约束(操作系统可以选择放宽此要求,但通常不会)。所以我们不能从sp减去28;我们必须四舍五入到16的下一个倍数,即32 这就

测试c(裸金属)


在为
main
布局堆栈时,我增加了对
的理解,编译器必须满足以下约束:

  • x29
    x30
    需要保存在堆栈上。它们每个占用8个字节

  • 局部变量
    x,y,z
    需要堆栈空间,每个4字节。(如果你在优化,你会看到它们被保存在寄存器中,或者被完全优化了而不存在。)这使我们总共得到了
    8+8+4+4+4=28
    字节

  • 堆栈指针
    sp
    必须始终与16字节对齐;这是一个体系结构和ABI约束(操作系统可以选择放宽此要求,但通常不会)。所以我们不能从sp减去28;我们必须四舍五入到16的下一个倍数,即32

这就是您提到的32或
0x20
的来源。请注意,它完全用于
main
本身使用的堆栈内存。它不是一个普适常数;如果从
main
中添加或删除足够多的局部变量,则会看到它发生变化

它与
printf
所需的任何内容都无关。如果
printf
自身的局部变量需要堆栈空间,则
printf
中的代码必须相应地调整堆栈指针。编译
main
时,编译器不知道会有多少空间,也不在乎

现在,编译器需要在为自己创建的32字节堆栈空间内组织这五个对象。除了以下几点之外,选择放在哪里几乎是完全任意的

函数的序言需要从堆栈指针中减去32,并将寄存器
x29、x30
存储在分配的空间中的某个位置。这一切都可以在一条指令中完成,使用预索引存储对指令
stp x29,x30,[sp,#-32]。它从
sp
中减去
32
,然后将
x29
x30
存储在从
sp
现在指向的地址开始的16个字节中。因此,为了使用此指令,我们必须接受将
x29
x30
放置在分配空间的底部,相对于
sp
的新值的偏移量
[sp+0]
[sp+8]
。把它们放在其他任何地方都需要额外的指导,效率也会降低

(事实上,因为这是最方便的方法,所以实际需要以这种方式设置堆栈帧,在使用堆栈帧时,堆栈帧按该顺序相邻(5.2.3)。)


我们仍然有16个字节从
[sp+16]
开始播放,其中必须放置
x,y,z
。编译器已选择将它们分别放在地址
[sp+28]、[sp+24]、[sp+20]
处。
[sp+16]
处的4个字节仍然没有使用,但是请记住,为了实现正确的堆栈对齐,我们不得不在某个地方浪费4个字节。排列这些对象以及保留未使用的插槽的选择完全是任意的,任何其他排列都会起作用。

“为什么?似乎是多余的。它已经在w0中了”您正在查看未优化的装配。这样的代码可能包含大量的冗余指令。是的。通常最好至少使用
-O1
进行编译,以避免奇怪的堆栈移动。您的分析看起来基本正确。请注意,使用
z
加载
w1
不是多余的,因为这是传递到
printf
所需的位置。然而,
movw1,w0
会做得更简单;或者,如果对代码进行了优化,
z
可以首先在
w1
中进行计算。但是您想问的是一个更具体的问题吗?当不进行优化时,编译器会生成代码,就像每个变量的“真正的主”都在内存中一样,因此对该变量的每个引用都应该相应地读取或写入内存。它没有注意到另一个寄存器(甚至是同一个寄存器!)可能已经包含该值的副本。因此,正如您所看到的,结果往往是非常低效和冗余的,对于如何编写自己的汇编代码来说,它不是一个好的模型。
x0
不是
w0
,而是字符串的地址。“填充”
%d
就是
printf
所做的,它解析格式字符串,
%d
告诉它在
w1
中取数字,转换为十进制数,然后输出。Re:未优化的代码:解释编译器在默认值
-O0
时为什么会这样做非常感谢。这解释了很多事情。我认为最让我困惑的部分是stp x29,x30,[sp,#-32]。我认为sp是由该指令保存的,但它不是。我不知道(可能忘了…)索引前和索引后的内容。@ChanKim:不客气。别忘了
stp
指令中的code>——这就是请求预索引的内容。我希望他们选择了更好的语法。
#include <stdio.h>

int add1(int a, int b)
{
int c;
c = a + b;
return c;
}

int main()
{
int x, y, z;
x = 3;
y = 4;
z = add1(x,y);
printf("z = %d\n", z);
}
00000000004002e0 <add1>:
  4002e0:   d10083ff    sub sp, sp, #0x20       <-- reduce sp by 0x20 (just above it are saved fp and lr of main)
  4002e4:   b9000fe0    str w0, [sp, #12]       <-- save first param x at sp + 12
  4002e8:   b9000be1    str w1, [sp, #8]        <-- save second param y at sp + 8
  4002ec:   b9400fe1    ldr w1, [sp, #12]       <-- load w1 with x
  4002f0:   b9400be0    ldr w0, [sp, #8]        <-- load w0 with y
  4002f4:   0b000020    add w0, w1, w0          <-- w0 = w1 + w0
  4002f8:   b9001fe0    str w0, [sp, #28]       <-- store x0 to sp+28
  4002fc:   b9401fe0    ldr w0, [sp, #28]       <-- load w0 with the result (seems redundant)
  400300:   910083ff    add sp, sp, #0x20       <-- increment sp by 0x20
  400304:   d65f03c0    ret
0000000000400308 <main>:
  400308:   a9be7bfd    stp x29, x30, [sp, #-32]!   <-- save x29(fp) and x30(lr) at sp - 0x20
  40030c:   910003fd    mov x29, sp                 <-- set fp to new sp, the base of stack growth(down)
  400310:   52800060    mov w0, #0x3                    // #3
  400314:   b9001fe0    str w0, [sp, #28]           <-- x is assigned in sp + #28
  400318:   52800080    mov w0, #0x4                    // #4
  40031c:   b9001be0    str w0, [sp, #24]           <-- y is assiged in sp + #24
  400320:   b9401be1    ldr w1, [sp, #24]            <-- load func param for y
  400324:   b9401fe0    ldr w0, [sp, #28]           <-- load func param for x
  400328:   97ffffee    bl  4002e0 <add1>           <-- call main1 (args are in w0, w1)
  40032c:   b90017e0    str w0, [sp, #20]           <-- store x0(result z) to sp+20
  400330:   b94017e1    ldr w1, [sp, #20]           <-- load w1 with the result (why? seems redundant. it's already in w0)
  400334:   d0000060    adrp    x0, 40e000 <__sfp_handle_exceptions+0x28>
  400338:   91028000    add x0, x0, #0xa0  <-- looks like loading param x0 for printf
  40033c:   940000e7    bl  4006d8 <printf>
  400340:   52800000    mov w0, #0x0                    // #0 <-- for main's return value..
  400344:   a8c27bfd    ldp x29, x30, [sp], #32  <-- recover x29 and x30 (look's like values in x29, x30 was used in the fuction who called main)
  400348:   d65f03c0    ret
  40034c:   d503201f    nop