在 Assembly 中将值推入堆栈:为什么代码使用特定顺序
Pushing value to stack in Assembly: why code is using particular order
最近我遇到了一段汇编函数代码,它首先将寄存器值保存在堆栈中(将 $sp
设置为 -12)并完成它的工作。但是在将第一个值压入堆栈时,它从基地址移动了 8 个单元,第二个移动了 4 个单元,最后一个移动了 0 个单元。但我不明白为什么它通过移动 8 个单位而不是 4 个来推动第一个值?!
Leaf_example:
addi $sp, $sp, -12
sw $t1, 8($sp)
sw $t0, 4($sp)
sw $s0, 0($sp)
add $t0, $a0, $a1
add $t1, $a2, $a3
sub $s0, $t0, $t1
add $v0, $s0, $zero
lw $s0, 0($sp)
lw $t0, 4($sp)
lw $t1, 8($sp)
addi $sp, $sp, 12
jr $ra
要存储3个寄存器。一个 MIPS 寄存器是 32 位 = 4 字节(内存是按字节寻址的),因此内存中存储 3 个寄存器需要(至少)12 字节。
开头的$sp
指向"top of the stack",已经被某个值占用,即地址sp+0
到sp+3
的字节组成一个字值在 "top of the stack".
然后代码通过 sp = sp - 12
保留额外的 12 个字节用于存储 3 个寄存器。之后地址sp+0
到sp+11
的内存是"free",sp+12
到sp+15
是前一个"top of the stack"字。这样就有足够的空间在 sp+0 .. sp+3
个地址、sp+4 .. sp+7
个地址和 sp+8 .. sp+11
个地址处存储三个单词。
代码如何选择哪个寄存器映射到哪个堆栈内存区域取决于原始程序员,他决定将 t1
存储在 sp+8 ... sp+11
字节中,但他也可以使用sp+0 .. sp+3
(但这也需要对恢复代码部分进行相同的更改)。
没有特别的理由说明为什么你不能保留 40 个字节(按 addi $sp, $sp, -40
)然后只使用 sp+20 .. sp+31
或任何其他部分的区域,或以不同的顺序,或存储t1, t0, s0
并将它们恢复为 s1, s2, s3
,等等...您可以自由编写任何您想要的合法 MIPS 指令。这是否有意义,是否实现你的目标是不同的问题......所以问为什么第一个寄存器存储在 +8 是没有用的,程序员确实想要这样。
But I don't get that why it' pushes the first value by moving 8 units and not 4?!
sw $t1, 4($sp)
sw $t0, 4($sp)
sw $s0, 0($sp)
这会将 t0
存储在已存储 t1
的同一内存中,因此您将只保留 t0
。所以我不明白您希望它如何使用 +4 作为第一个值。就像将 s0
存储在 -4
?如果您在整个代码中这样做,将 $sp-4 .. $sp-1
内存区域视为 "top of the stack",即被某个值占用,并避免被其他代码更改,那么您 可以 这样做,它会起作用。但通常 MIPS 程序集将 sp+0 .. sp+3
区域视为 "top of the stack",然后将某些内容存储在 -4
会使它容易受到其他不知道这一点的代码的攻击,但以常用方式使用堆栈时,它会期望 sp-4
为了自己的目的成为 "free"。
编辑,我在 OP 中注意到一件事:
(sets $sp
to -12)
addi $sp, $sp, -12
并没有设置sp
,而是通过加法调整,所以之前指向内存的地方,add
之后会指向12字节的地址"lower"。如果您按字面意思将 sp
设置为 -12
,它会将其用作无符号内存地址,指向理论地址结束前 12 个字节 space 可能(0xFFFFFFF4
32b 地址) .根据您的目标体系结构,那里可能有也可能没有任何内存,如果有,它可能用于不同的目的。但这看起来像是伪插件。 li $sp, -12
。可能只是你的措辞令人困惑,而你头脑中的理解是正确的,但我把这句话留在这里,以防有人想知道你原来的 post 以及那部分是如何工作的。
最近我遇到了一段汇编函数代码,它首先将寄存器值保存在堆栈中(将 $sp
设置为 -12)并完成它的工作。但是在将第一个值压入堆栈时,它从基地址移动了 8 个单元,第二个移动了 4 个单元,最后一个移动了 0 个单元。但我不明白为什么它通过移动 8 个单位而不是 4 个来推动第一个值?!
Leaf_example:
addi $sp, $sp, -12
sw $t1, 8($sp)
sw $t0, 4($sp)
sw $s0, 0($sp)
add $t0, $a0, $a1
add $t1, $a2, $a3
sub $s0, $t0, $t1
add $v0, $s0, $zero
lw $s0, 0($sp)
lw $t0, 4($sp)
lw $t1, 8($sp)
addi $sp, $sp, 12
jr $ra
要存储3个寄存器。一个 MIPS 寄存器是 32 位 = 4 字节(内存是按字节寻址的),因此内存中存储 3 个寄存器需要(至少)12 字节。
开头的$sp
指向"top of the stack",已经被某个值占用,即地址sp+0
到sp+3
的字节组成一个字值在 "top of the stack".
然后代码通过 sp = sp - 12
保留额外的 12 个字节用于存储 3 个寄存器。之后地址sp+0
到sp+11
的内存是"free",sp+12
到sp+15
是前一个"top of the stack"字。这样就有足够的空间在 sp+0 .. sp+3
个地址、sp+4 .. sp+7
个地址和 sp+8 .. sp+11
个地址处存储三个单词。
代码如何选择哪个寄存器映射到哪个堆栈内存区域取决于原始程序员,他决定将 t1
存储在 sp+8 ... sp+11
字节中,但他也可以使用sp+0 .. sp+3
(但这也需要对恢复代码部分进行相同的更改)。
没有特别的理由说明为什么你不能保留 40 个字节(按 addi $sp, $sp, -40
)然后只使用 sp+20 .. sp+31
或任何其他部分的区域,或以不同的顺序,或存储t1, t0, s0
并将它们恢复为 s1, s2, s3
,等等...您可以自由编写任何您想要的合法 MIPS 指令。这是否有意义,是否实现你的目标是不同的问题......所以问为什么第一个寄存器存储在 +8 是没有用的,程序员确实想要这样。
But I don't get that why it' pushes the first value by moving 8 units and not 4?!
sw $t1, 4($sp)
sw $t0, 4($sp)
sw $s0, 0($sp)
这会将 t0
存储在已存储 t1
的同一内存中,因此您将只保留 t0
。所以我不明白您希望它如何使用 +4 作为第一个值。就像将 s0
存储在 -4
?如果您在整个代码中这样做,将 $sp-4 .. $sp-1
内存区域视为 "top of the stack",即被某个值占用,并避免被其他代码更改,那么您 可以 这样做,它会起作用。但通常 MIPS 程序集将 sp+0 .. sp+3
区域视为 "top of the stack",然后将某些内容存储在 -4
会使它容易受到其他不知道这一点的代码的攻击,但以常用方式使用堆栈时,它会期望 sp-4
为了自己的目的成为 "free"。
编辑,我在 OP 中注意到一件事:
(sets
$sp
to -12)
addi $sp, $sp, -12
并没有设置sp
,而是通过加法调整,所以之前指向内存的地方,add
之后会指向12字节的地址"lower"。如果您按字面意思将 sp
设置为 -12
,它会将其用作无符号内存地址,指向理论地址结束前 12 个字节 space 可能(0xFFFFFFF4
32b 地址) .根据您的目标体系结构,那里可能有也可能没有任何内存,如果有,它可能用于不同的目的。但这看起来像是伪插件。 li $sp, -12
。可能只是你的措辞令人困惑,而你头脑中的理解是正确的,但我把这句话留在这里,以防有人想知道你原来的 post 以及那部分是如何工作的。