打印堆栈框架

时间:2016-01-07 00:53:08

标签: c assembly stack x86-64

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

我想到了堆栈帧的下图(我可能错了):

|                                |  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:我不允许使用任何外部功能

1 个答案:

答案 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"

另请注意,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