• x86手推调用栈


    当程序crash的时候,我们可以通过coredump文件,来定位问题。比如使用bt命令可以完整的展开函数的调用栈。但是有些时候,部分栈的数据可能被损坏,导致gdb无法直接显示函数的调用栈。那么这时就需要我们手工展开函数栈。
     
    关于x86的函数调用栈的示意图基本如下图所示:
    img_38abab7e0d4e02fdc12c84323b15b8f4.gif
     
    关于参数的压栈顺序,上图为cdecl方式,这个可以通过编译选项修改。GCC默认使用cdecl。
     
    下面看一下例子:
    1. #include stdlib.h>
    2. #include stdio.h>
    3. static int test(int a, int b, int c)
    4. {
    5.     return a+b+c;
    6. }
    7. int main()
    8. {
    9.     int a = 1;
    10.     int b = 2;
    11.     int c = 3;
    12.     int d = test(a, b, c);
    13.     printf("%d\n", d);
    14.     return 0;
    15. }
    编译:gcc -g -Wall test.c
    进入test,查看函数调用栈:
    1. Breakpoint 1, test (a=1, b=2, c=3) at test.c:7
    2. 7 return a+b+c;
    3. Missing separate debuginfos, use: debuginfo-install glibc-2.11-2.i686
    4. (gdb) bt
    5. #0 test (a=1, b=2, c=3) at test.c:7
    6. #1 0x08048412 in main () at test.c:16
    那么现在查看一下寄存器:
    1. eax 0x1 1
    2. ecx 0x2c0187d8 738297816
    3. edx 0x1 1
    4. ebx 0x73fff4 7602164
    5. esp 0xbffff048 0xbffff048
    6. ebp 0xbffff048 0xbffff048
    7. esi 0x0 0
    8. edi 0x0 0
    9. eip 0x80483c7 0x80483c7 test+3>
    10. eflags 0x286 [ PF SF IF ]
    11. cs 0x73 115
    12. ss 0x7b 123
    13. ds 0x7b 123
    14. es 0x7b 123
    15. fs 0x0 0
    16. gs 0x33 51
    得到ebp的地址为0xbffff048,现在检查这个地址的内存
    1. (gdb) x /8x 0xbffff048
    2. 0xbffff048: 0xbffff078 0x08048412 0x00000001 0x00000002
    3. 0xbffff058: 0x00000003 0x0073fff4 0x00000001 0x00000002
    下面分析一下这些内存的内容:
    1. 0xbffff078:为test的调用者,即main函数的bp地址;BP地址即为该函数的栈顶指针。
    2. 0x08048412:为test的返回地址,与前面的bt的输出相符;
    3. 后面的0x00000001,0x00000002,0x00000003,为传给test的三个参数,且参数顺序为由右向左压栈——注意这个顺序是可以通过改变编译参数改变的。
     
    回到main中,验证一下bp寄存器的内容:
    1. 0x08048412 in main () at test.c:16
    2. 16 int d = test(a, b, c);
    3. Value returned is $2 = 6
    4. (gdb) info registers
    5. eax 0x6 6
    6. ecx 0x39ff7a48 973044296
    7. edx 0x1 1
    8. ebx 0x73fff4 7602164
    9. esp 0xbffff050 0xbffff050
    10. ebp 0xbffff078 0xbffff078
    可见BP的地址确实为0xbffff078,与之前的分析相符。
     
    注:关于压栈顺序,参数的传递方式等等,都可以通过编译选项来指定或者禁止的。本文的情况为GCC的默认行为。
    有时候,不小心知道了一些事,才发现自己所在乎的事是那么可笑。
  • 相关阅读:
    VMware虚拟机网络桥接模式下无法与主机ping通解决办法
    (一)编写Bootloader程序应该注意的一些问题
    使用TrueSTUDIO和MDK编译器生成.bin文件
    Linux开发板通过串口与电脑上位机通信
    STM32知识点
    仿真器SWD可不接复位引脚的原因
    ARM内核常用缩写含义
    三大范式(转)
    修改主键示例
    ROW_NUMBER用法详解
  • 原文地址:https://www.cnblogs.com/axjlxy/p/15711442.html
Copyright © 2020-2023  润新知