2016-01-07 127 views
1

所以我目前正在学习堆栈帧,并且我想实验打印函数的堆栈帧(手动)。打印堆栈帧

我有一个堆栈帧的心下面的图片(I可能是错误的):

|        | 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++ 

但没有成功。我的方法有什么问题?


我也有另外一个问题: 给出一个递归函数,采取简单的阶乘(INT N),我们怎么能其中基本指针位于堆栈中的地址?


的情况下,你需要的汇编代码: 注意,这仅包含生成的汇编代码的函数“功能”。 我在汇编代码与源代码相关的地方添加了注释。

    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:我是不允许使用任何外部函数

回答

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" 

还要注意的是ab是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 
+0

这是完美的,非常感谢! :) –