c - 为什么调用堆栈是这样设置的?

标签 c x86 stack

我只是在玩调用堆栈,试图更改函数的返回地址等,最后用 C 语言编写了这个程序:

#include<stdio.h>

void trace(int);
void func3(int);
void func2(int);
void func1(int);

int main(){

    int a = 0xAAAA1111;

    func1(0xFCFCFC01);

    return 0;

}

void func1(int a){

    int loc = 0xBBBB1111;

    func2(0xFCFCFC02);

}

void func2(int a){

    int loc1 = 0xCCCC1111;
    int loc2 = 0xCCCC2222;

    func3(0xFCFCFC03);

}

void func3(int a){

    int loc1 = 0xDDDD1111;
    int loc2 = 0xDDDD2222;
    int loc3 = 0xDDDD3333;

    trace(0xFCFCFC04);

}

void trace(int a){

    int loc = 0xEEEE1111;

    int *ptr = &loc;

    do {
    printf("0x%08X : %08X\n", ptr, *ptr, *ptr);
    } while(*(ptr++) != 0xAAAA1111);

}

(抱歉有点长)

这产生了以下输出(带有我添加的评论):

0xBF8144D4 : EEEE1111 //local int in trace
0xBF8144D8 : BF8144F8 //beginning of trace stack frame
0xBF8144DC : 0804845A //return address for trace to func3
0xBF8144E0 : FCFCFC04 //int passed to trace
0xBF8144E4 : 08048230 //(possibly) uninitialized padding
0xBF8144E8 : 00000000 //padding
0xBF8144EC : DDDD3333 //local int in func3
0xBF8144F0 : DDDD2222 //local int in func3
0xBF8144F4 : DDDD1111 //local int in func3
0xBF8144F8 : BF814518 //beginning of func3 stack frame
0xBF8144FC : 08048431 //return address for func3 to func2
0xBF814500 : FCFCFC03 //parameter passed to func3
0xBF814504 : 00000000 //padding
0xBF814508 : 00000000 //padding
0xBF81450C : 00000000 //padding
0xBF814510 : CCCC2222 //local int in func2
0xBF814514 : CCCC1111 //local int in func2
0xBF814518 : BF814538 //beginning of func2 stack frame
0xBF81451C : 0804840F //return address for func2 to func1
0xBF814520 : FCFCFC02 //parameter passed to func2
0xBF814524 : 00000000 //padding
0xBF814528 : BF816728 //uninitialized padding
0xBF81452C : B7DF3F4E //uninitialized padding
0xBF814530 : B7EA61D9 //uninitialized padding
0xBF814534 : BBBB1111 //local int in func1
0xBF814538 : BF814558 //beginning of func1 stack frame
0xBF81453C : 080483E8 //return address for func1 to main
0xBF814540 : FCFCFC01 //parameter passed to func1
0xBF814544 : 08049FF4 //(maybe) padding
0xBF814548 : BF814568 //(maybe) padding
0xBF81454C : 080484D9 //(maybe) padding
0xBF814550 : AAAA1111 //local int in main

我想知道是否有人可以在这里填补我的空白...我正在运行使用 gcc 4.3.3 编译的 Ubuntu linux(在 x86 上——AMD Turion 64)

0804... 是什么数字?倒数第三个地址是什么?那是 main 的返回地址吗?如果是这样,为什么它与堆栈的其余部分相比是乱序的? 0x0804 数字是返回地址,或指向代码/数据或其他内容的指针,而 0xBF814 数字是堆栈指针

这是什么:

0xBF814524 : 00000000 //padding?
0xBF814528 : BF816728 //I have no idea
0xBF81452C : B7DF3F4E //????
0xBF814530 : B7EA61D9 //????

在 func1 的 local int 后面看到了吗?

好的,我的堆栈转储几乎已完全填满。

看起来编译器希望将参数压入堆栈,从 0x.......0 地址开始,之前函数的局部变量和函数的第一个参数之间的所有内容调用似乎是填充(无论是 0x00000000 还是一些未初始化的值)。其中一些我不确定,因为它们看起来像代码/数据段指针,但我看不到它们在代码中被使用:当堆栈指针减少时它们就在那里。

而且我知道在任何类型的项目中接触调用堆栈都是一个巨大的禁忌,但没关系。这很有趣,对吧?

还有

格雷格想看集会, 在这里

    .file   "stack.c"
    .text
.globl main
    .type   main, @function
main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ecx
    subl    $20, %esp
    movl    $-1431695087, -8(%ebp)
    movl    $-50529279, (%esp)
    call    func1
    movl    $0, %eax
    addl    $20, %esp
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
    .size   main, .-main
.globl func1
    .type   func1, @function
func1:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $-1145368303, -4(%ebp)
    movl    $-50529278, (%esp)
    call    func2
    leave
    ret
    .size   func1, .-func1
.globl func2
    .type   func2, @function
func2:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $-859041519, -4(%ebp)
    movl    $-859037150, -8(%ebp)
    movl    $-50529277, (%esp)
    call    func3
    leave
    ret
    .size   func2, .-func2
.globl func3
    .type   func3, @function
func3:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $-572714735, -4(%ebp)
    movl    $-572710366, -8(%ebp)
    movl    $-572705997, -12(%ebp)
    movl    $-50529276, (%esp)
    call    trace
    leave
    ret
    .size   func3, .-func3
    .section    .rodata
.LC0:
    .string "0x%08X : %08X\n"
    .text
.globl trace
    .type   trace, @function
trace:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $40, %esp
    movl    $-286387951, -4(%ebp)
    leal    -4(%ebp), %eax
    movl    %eax, -8(%ebp)
.L10:
    movl    -8(%ebp), %eax
    movl    (%eax), %edx
    movl    -8(%ebp), %eax
    movl    (%eax), %eax
    movl    %edx, 12(%esp)
    movl    %eax, 8(%esp)
    movl    -8(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    $.LC0, (%esp)
    call    printf
    movl    -8(%ebp), %eax
    movl    (%eax), %eax
    cmpl    $-1431695087, %eax
    setne   %al
    addl    $4, -8(%ebp)
    testb   %al, %al
    jne .L10
    leave
    ret
    .size   trace, .-trace
    .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
    .section    .note.GNU-stack,"",@progbits

最佳答案

像这样检查堆栈似乎离得太远了一步。我可能建议在调试器中加载您的程序,切换到汇编语言 View ,并单步执行每条机器指令。了解 CPU 堆栈必然需要了解在其上运行的机器指令,这将是一种更直接的方式来了解正在发生的事情。

正如其他人提到的,堆栈的结构也高度依赖于您正在使用的处理器架构。

关于c - 为什么调用堆栈是这样设置的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1224154/

相关文章:

c - 如果使用共享内存,进程是否仍然比线程有优势?

c - GNU make 和对象列表

c - NASM x86 使用 extern printf 打印整数

通过堆栈更改变量的值

c - 未指定的参数数量和传递给 C 函数的可变参数数量之间有什么区别?

有人能解释一下这段代码的输出吗?

assembly - 编写一个简单的引导加载程序来读取用户名

c - 将FPU与C内联汇编一起使用

r - geom_text 仅在堆积条形图的顶部

c - 堆栈溢出损坏 %ebp