所以我目前正在学习堆栈帧,我想实验打印一个函数的堆栈帧(手动)。
我想到了堆栈帧的下图(我可能错了):
| | 0xffff0fdc
+--------------------------------+
| ... | 0xffff0fd8
+--------------------------------+
| parameter 2 | 0xffff0fd4
+--------------------------------+
| parameter 1 | 0xffff0fd0
+--------------------------------+
| return address | 0xffff0fcc
+--------------------------------+
| local variable 2 | 0xffff0fc8
+--------------------------------+
| local variable 1 | 0xffff0fc4
+--------------------------------+
因此我首先编写了这个函数来实现上述结果并打印出来:
void func(int a,int b)
{
uint64_t loc = 0;
uint64_t *sp = &loc;
printf("%" PRIu64 "\n",*(sp));
printf("%" PRIu64 "\n",*(sp+4));
printf("%" PRIu64 "\n",*(sp+8));
printf("%" PRIu64 "\n",*(sp+12));
}
int main()
{
func(2,3);
return 0;
}
我得到了:
0
12884901890
51266344552759297
18034967110614932
绝对不是预期的
我还尝试通过堆栈“扫描”找到其中一个参数:
while (*sp != a) sp++
没有太大的成功。我的方法出了什么问题?
我还有另一个问题: 给定一个递归函数,简单地使用factorial(int n),我们如何发现基址指针在堆栈中的位置?
如果您需要汇编代码: 注意,这只包含函数“func”生成的汇编代码。 我在汇编代码与源代码的关系中添加了注释。
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl %edi, -20(%rbp)
movl %esi, -24(%rbp)
***// uint64_t loc = 0;***
movq $0, -16(%rbp)
***// uint64_t *sp = &loc;***
leaq -16(%rbp), %rax
movq %rax, -8(%rbp)
***// printf("%" PRIu64 "\n",*sp);***
movq -8(%rbp), %rax
movq (%rax), %rax
movq %rax, %rsi
movl $.LC0, %edi
movl $0, %eax
call printf
***printf("%" PRIu64 "\n",*(sp+8));***
movq -8(%rbp), %rax
addq $64, %rax
movq (%rax), %rax
movq %rax, %rsi
movl $.LC0, %edi
movl $0, %eax
call printf
***// printf("%" PRIu64 "\n",*(sp+16));***
movq -8(%rbp), %rax
subq $-128, %rax
movq (%rax), %rax
movq %rax, %rsi
movl $.LC0, %edi
movl $0, %eax
call printf
***// printf("%" PRIu64 "\n",*(sp+32));***
movq -8(%rbp), %rax
addq $256, %rax
movq (%rax), %rax
movq %rax, %rsi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
.cfi_def_cfa 7, 8
ret
任何帮助我更好地处理堆栈的建议都将非常感谢!
PS:我不允许使用任何外部功能
答案 0 :(得分:3)
x86-64没有传递堆栈上的前几个参数(类型允许),因此您没有机会打印它们。此外,本地的实际堆栈布局取决于编译器和设置。
由于您提供了汇编代码,我们可以检查如下所示的布局:
return address
rbp saved rbp
rbp-8 local variable "sp"
rbp-16 local variable "loc"
rbp-20 local copy of argument "a"
rbp-24 local copy of argument "b"
另请注意,a
和b
为4个字节,其余为8个。此外,C指针算术按项目大小进行缩放,因此*(sp+4)
不会4 * 8 = 32
个字节你可能想要4
。
如果堆栈布局未更改,您可以使用此代码作为插图:
#include <stdio.h>
#include <stdint.h>
int main();
void func(int a,int b)
{
uint64_t loc = 0;
char *sp = (char*)&loc;
printf("main = %p\n", main);
printf("return address = %p\n", *(void**)(sp + 24));
printf("saved rbp = %p\n", *(void**)(sp + 16));
printf("sp = %p\n", *(void**)(sp + 8));
printf("loc = %lld\n", *(uint64_t*)(sp));
printf("a = %d\n", *(int*)(sp - 4));
printf("b = %d\n", *(int*)(sp - 8));
}
int main()
{
func(2,3);
return 0;
}
示例输出:
main = 0x4005e6
return address = 0x4005f9
saved rbp = 0x7ffe057bf240
sp = 0x7ffe057bf220
loc = 0
a = 2
b = 3