# ICS学习笔记

# 一个简单递归函数的分析

为了研究栈空间是如何分配的,我先是写了这样一个小递归

int add(int n);

int main() {
    add(3);
    return 0;
}

int add(int n) {
    if (n <= 0) return 0;
    return n + add(n - 1);
}

使用objdump -d a.out整理后的结果如下

_main:
pushq	%rbp
movq	%rsp, %rbp
subq	$16, %rsp
movl	$0, -4(%rbp)
movl	$3, %edi
callq	23 <__Z3addi>
xorl	%edi, %edi
movl	%eax, -8(%rbp)
movl	%edi, %eax
addq	$16, %rsp
popq	%rbp
retq
nopw	%cs:(%rax,%rax)

__Z3addi:
pushq	%rbp
movq	%rsp, %rbp
subq	$16, %rsp
movl	%edi, -8(%rbp)
cmpl	$0, -8(%rbp)
jg	12 <__Z3addi+0x21>
movl	$0, -4(%rbp)
jmp	27 <__Z3addi+0x3c>
movl	-8(%rbp), %eax
movl	-8(%rbp), %ecx
subl	$1, %ecx
movl	%ecx, %edi
movl	%eax, -12(%rbp)
callq	-52 <__Z3addi>
movl	-12(%rbp), %ecx
addl	%eax, %ecx
movl	%ecx, -4(%rbp)
movl	-4(%rbp), %eax
addq	$16, %rsp
popq	%rbp
retq

# 开辟栈帧(Stack Frame)

每个函数的开头都有一段这样的代码

pushq	%rbp
movq	%rsp, %rbp
subq	$16, %rsp

其中:

  • rbp是64位的基址指针寄存器(base pointer),指向当前栈的基址。
  • rsp是64位的栈指针寄存器(stack pointer),指向当前栈的栈顶。

# 执行过程

  • push %rbp 将当前的基址压入栈顶,在完成这一动作后,rsp中的值将变为新的栈顶地址。
  • mov %rsp, %rbprsp中栈指针的值移动到基址指针寄存器rbp中。现在,rsprbp都指向栈顶
  • subq $16, %rsp这一命令创建了一个空间来储存局部变量。这个空间足够用来储存integer
  • 通过上面三个操作,计算机就完成了在内存中创建一个储存局部变量的空间,这一空间被称为栈帧(stack frame)一般来说,在内存中栈是从高地址向低地址延伸的。

# 递归过程分析

  • cmpl指令很容易看出是比较指令,它影响OF,ZF,SF,CF四个常用标志

    Overflow Flag: OF=1溢出

    Zero Flag:ZF=1运算结果为0

    Sign Flag:SF=1运算结果为负数

    Carry Flag:CF=1运算后有借位

  • jg命令是一个条件转移指令,

最后更新: 2021-12-11