GNU汇编语言:如何理解递归函数中的栈指针(阶乘计算)
GNU Assembly Language: How to understand the stack pointer in recursive function (factorial calculation)
我尝试通过GAS理解堆栈框架,这里展示了一个递归函数汇编案例,作为[=34=]的32位x86程序:
.section .data
.section .text
.globl _start
.globl factorial
_start:
pushl
call factorial
popl %ebx
movl %eax, %ebx
movl , %eax
int [=10=]x80
.type factorial, @function
factorial:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
cmpl , %eax
je end_factorial
decl %eax
pushl %eax
call factorial
popl %ebx
incl %ebx
imul %ebx, %eax
end_factorial:
movl %ebp, %esp
popl %ebp
ret
架构:Ubuntu 18.04 上的 x86-64 和 GCC 7.5。
使用
构建
as -o test.o powertest.s --32
ld -o test test.o -m elf_i386
我发现了一些与我的问题相关的类似问题,但我仍然停留在堆栈指针移动或 pop/push 返回。
参照本例,代码求出 3 的阶乘。
据我了解,伪代码应该是:
PUSH 3 -> PUSH RET -> PUSH EBP -> PUSH 2 -> PUSH RET -> PUSH EBP ->
PUSH 1 -> POP EBP -> POP RET -> POP EBX ->POP EBP -> (Stuck here)
当EAX迭代到1时,弹出内部EBP和RET。
然后,在函数 (EAX == 3) 中,应该弹出 POP EBX。
不过,根据上面的伪代码,我想也许应该弹出EBP。
代码真的能得到正确的结果,真是让我百思不得其解
我看过一些参考资料,他们说 push 和 pop mush 相互匹配。否则内存可能会崩溃。
我的问题是在这种情况下堆栈组件是如何弹出的?
伪代码在 PUSH 1
之后缺少 PUSH RET -> PUSH EBP
:
PUSH 3 -> PUSH RET ->
PUSH EBP -> PUSH 2 -> PUSH RET ->
PUSH EBP -> PUSH 1 -> PUSH RET ->
PUSH EBP
此时 eax == 1 所以它 returns 备份调用链
POP EBP ->
POP RET -> POP EBX -> POP EBP ->
POP RET -> POP EBX -> POP EBP ->
POP RET -> POP EBX
我尝试通过GAS理解堆栈框架,这里展示了一个递归函数汇编案例,作为[=34=]的32位x86程序:
.section .data
.section .text
.globl _start
.globl factorial
_start:
pushl
call factorial
popl %ebx
movl %eax, %ebx
movl , %eax
int [=10=]x80
.type factorial, @function
factorial:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
cmpl , %eax
je end_factorial
decl %eax
pushl %eax
call factorial
popl %ebx
incl %ebx
imul %ebx, %eax
end_factorial:
movl %ebp, %esp
popl %ebp
ret
架构:Ubuntu 18.04 上的 x86-64 和 GCC 7.5。 使用
构建as -o test.o powertest.s --32
ld -o test test.o -m elf_i386
我发现了一些与我的问题相关的类似问题,但我仍然停留在堆栈指针移动或 pop/push 返回。
参照本例,代码求出 3 的阶乘。 据我了解,伪代码应该是:
PUSH 3 -> PUSH RET -> PUSH EBP -> PUSH 2 -> PUSH RET -> PUSH EBP ->
PUSH 1 -> POP EBP -> POP RET -> POP EBX ->POP EBP -> (Stuck here)
当EAX迭代到1时,弹出内部EBP和RET。
然后,在函数 (EAX == 3) 中,应该弹出 POP EBX。
不过,根据上面的伪代码,我想也许应该弹出EBP。
代码真的能得到正确的结果,真是让我百思不得其解
我看过一些参考资料,他们说 push 和 pop mush 相互匹配。否则内存可能会崩溃。
我的问题是在这种情况下堆栈组件是如何弹出的?
伪代码在 PUSH 1
之后缺少 PUSH RET -> PUSH EBP
:
PUSH 3 -> PUSH RET ->
PUSH EBP -> PUSH 2 -> PUSH RET ->
PUSH EBP -> PUSH 1 -> PUSH RET ->
PUSH EBP
此时 eax == 1 所以它 returns 备份调用链
POP EBP ->
POP RET -> POP EBX -> POP EBP ->
POP RET -> POP EBX -> POP EBP ->
POP RET -> POP EBX