这里有一些您的问题中未指定的事项需要考虑(ABI、处理器架构、可执行文件格式等)。并非所有这些都是回答您的问题所必需的,但理解这一点可能会提高您对如何在各种可执行上下文中调用函数、方法或过程的总体理解。
ABI
特别是,不同的 CPU 架构、操作系统,甚至可执行的二进制格式可能具有不同的签名来处理程序输入。因为很明显您使用的是 AMD64 架构 CPU,所以您可能会发现 this wikipedia page 很有用。特别是,您似乎正在使用基于您的 sn-p 中的某些上下文的“System V x86-64 ABI”。 (稍后我们将对您的 sn-p 进行全面分析。)
堆栈
C 编程语言确实有任何堆栈的概念,因此尽管这与您的 sn-p 相关,但它不是 C 程序的要求,并且您的程序的可移植版本可能不会使用堆栈。事实上,虽然介绍性编译器课程似乎仍然倾向于使用堆栈在调用帧之间传递状态,但堆栈通常不用于 AMD64 上的 SysV ABI。
(在 x86 上执行此操作更为常见,因为 32 位架构受寄存器限制。在此类架构上使用寄存器传递状态的开销可能会更高,因为寄存器可能需要被复制到堆栈中以便它们可以被重用,因为很可能额外的函数调用需要它们保留。)
你的片段
SysV ABI 按顺序特别使用%rdi、%rsi、%rdx、%rcx、%r8、%r9 和%xmm0-7。
0x0000000000400f0c <+0>: push %rbp
0x0000000000400f0d <+1>: push %rbx
这通过将代表堆栈帧的寄存器推到堆栈顶部来保留调用者的堆栈帧。 %rbp 和 %rbx 是“被调用者保存”寄存器,这意味着被调用的函数必须保留它们的值,因为调用者需要它们的值来保持其状态。
0x0000000000400f0e <+2>: sub $0x28,%rsp
这会在堆栈上分配 40 字节的空间。为什么是 40 字节?我们已经将 16 个字节压入堆栈,保留 %rbp 和 %rbx。对于read_six_numbers,我们需要额外的 24 字节作为暂存空间,因此 16 + 24 == 40。
0x0000000000400f12 <+6>: mov %rsp,%rsi
这会将堆栈的基地址移动到%rsi。现在,因为我假设 SysV ABI,这意味着地址实际上是我们将要调用的函数的 second 参数。这个空间的内容是未定义的,很可能是随机值。这是read_six_numbers 使用的暂存空间。
0x0000000000400f15 <+9>: callq 0x4016e5 <read_six_numbers>
这会调用函数read_six_numbers。由于我们的暂存空间是第二个参数(根据 SysV ABI),这意味着我们的调用函数在 %rdi 中有一个值,它未经修改就被传递给 read_six_numbers。如果我不得不猜测,我会说这个值回答了你的问题,所以我们需要查看这个 phase_2 函数的调用者以获得进一步的了解。
0x0000000000400f1a <+14>: lea 0x4(%rsp),%rbx
read_six_numbers 读取 6 个 32 位数字,总共 24 个字节。起始编号为0x0(%rsp),lea 为我们提供了特定值的地址。因此,这给了我们一个指向数组中第二个值的指针,并将其放入%rbx。
0x0000000000400f1f <+19>: lea 0x18(%rsp),%rbp
数组的第一个值在0x0(%rsp),第6个在0x14(%rsp); 0x18(%rbp) 是我们数组末尾的第一个大小对齐地址。
0x0000000000400f24 <+24>: mov -0x4(%rbx),%eax
0x0000000000400f27 <+27>: add $0x5,%eax
0x0000000000400f2a <+30>: cmp %eax,(%rbx)
0x0000000000400f2c <+32>: je 0x400f33 <phase_2+39>
0x0000000000400f2e <+34>: callq 0x4016c3 <explode_bomb>
0x0000000000400f33 <+39>: add $0x4,%rbx
0x0000000000400f37 <+43>: cmp %rbp,%rbx
0x0000000000400f3a <+46>: jne 0x400f24 <phase_2+24>
用户 chqrlie 很好地解释了这个循环。如果前一个 (-0x4(%rbx)) 和 current + 5 相等,我们继续循环。否则我们调用explode_bomb。我要补充一点,虽然 chqrlie 说它不需要参数,但不能保证它不需要。我们实际上还没有接触过%rdi 或%rsi,所以它仍然可以使用上下文。要断言explode_bomb 不带参数,我们必须查看它的反汇编;这个上下文并不能证明它不需要参数。
但是,正在比较的实际值在此上下文中未定义。我们只是在这里循环内存。
0x0000000000400f3c <+48>: add $0x28,%rsp
0x0000000000400f40 <+52>: pop %rbx
0x0000000000400f41 <+53>: pop %rbp
这会恢复调用者上下文(请记住,我们在开始时保存了调用者堆栈的状态)并且...
0x0000000000400f42 <+54>: retq
返回调用者的下一个IP。
也许这里有一些你不知道的东西。否则,只是一个冗长的解释来告诉你 chqrlie 已经做了什么:循环的起始值是 read_six_numbers 填充的数组基数之后的 4 个字节。