zoukankan      html  css  js  c++  java
  • 每天学点GDB 10

    近两周在做一个trouble shooting,需要对函数调用栈进行分析以找出入参和局部变量。因为在编译生成可执行程序的时候,用gcc进行了O2的优化,许多假设的函数调用栈模型都不成立了。花了一番周折,终于正确的翻译出入参和局部变量,此一旅程中的一些经验还是值得记录下来。

    在32位x86系统上,函数调用栈的布局如下图所示。

    栈底在高地址段,栈顶在低地址段。

    从栈底到栈顶的内容分别为:

    1. 函数入参
    2. 返回地址
    3. 保存的寄存器值
    4. 被调用函数的局部变量

    如果带有调试信息,则要获取上述4个部分的值很容易,对应的指令分别如下。

    1. info args
    2. info frame
    3. info registers
    4. info locals

    如果没有调试信息,则可以根据这一模型并结合反汇编的结果来算出入参与局部变量的存储位置。针对32位的具体例子比较容易找到。

    现在专门提一提在x86 64位下的不同,在x86 64下,因为寄存器数量增多,为了提高效率,入参基本上都是通过寄存器来传递。示例程序入下。

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    
    int demo_func(char* msg, int a, int b); 
    
    
    int main(int argc, char** argv) {
        char* info = "just a demo";
        int a,b;
        a = 320;
        b = 100;
        demo_func(info, a, b);
        return 0;
    }
    
    
    int demo_func(char* msg, int a, int b) {
        int sum;
        sum = a + b;
        return  sum;
    }

    main函数反汇编结果如下

    Dump of assembler code for function main:
       0x00000000004004b0 <+0>:    push   %rbp
       0x00000000004004b1 <+1>:    mov    %rsp,%rbp
       0x00000000004004b4 <+4>:    sub    $0x20,%rsp
       0x00000000004004b8 <+8>:    mov    %edi,-0x14(%rbp)
       0x00000000004004bb <+11>:    mov    %rsi,-0x20(%rbp)
       0x00000000004004bf <+15>:    movq   $0x400594,-0x8(%rbp)
       0x00000000004004c7 <+23>:    movl   $0x140,-0xc(%rbp)
       0x00000000004004ce <+30>:    movl   $0x64,-0x10(%rbp)
       0x00000000004004d5 <+37>:    mov    -0x10(%rbp),%edx
       0x00000000004004d8 <+40>:    mov    -0xc(%rbp),%ecx
       0x00000000004004db <+43>:    mov    -0x8(%rbp),%rax
       0x00000000004004df <+47>:    mov    %ecx,%esi
       0x00000000004004e1 <+49>:    mov    %rax,%rdi
       0x00000000004004e4 <+52>:    callq  0x4004f0 <demo_func>
       0x00000000004004e9 <+57>:    mov    $0x0,%eax
       0x00000000004004ee <+62>:    leaveq 
       0x00000000004004ef <+63>:    retq   
    End of assembler dump.

    注意callq 0x4004f0 <demo_func>上的三行代码,它们表示demo_func的入参,可以看出入参是通过寄存器来进行传递的。
    而寄存器在demo_func中可以会被改写,于是在改写之前,这些入参会被再次保存,它们保存在stack frame中,具体位置需要根据反汇编结果进行计算。即需要根据被调用函数的反汇编代码来计算入参。

    demo_func的反汇编结果如下。

    Dump of assembler code for function demo_func:
       0x00000000004004f0 <+0>:    push   %rbp
       0x00000000004004f1 <+1>:    mov    %rsp,%rbp
       0x00000000004004f4 <+4>:    mov    %rdi,-0x18(%rbp)
       0x00000000004004f8 <+8>:    mov    %esi,-0x1c(%rbp)
       0x00000000004004fb <+11>:    mov    %edx,-0x20(%rbp)
       0x00000000004004fe <+14>:    mov    -0x20(%rbp),%eax
       0x0000000000400501 <+17>:    mov    -0x1c(%rbp),%edx
       0x0000000000400504 <+20>:    add    %edx,%eax
       0x0000000000400506 <+22>:    mov    %eax,-0x4(%rbp)
       0x0000000000400509 <+25>:    mov    -0x4(%rbp),%eax
       0x000000000040050c <+28>:    pop    %rbp
       0x000000000040050d <+29>:    retq   
    End of assembler dump.

    编译时如果加上-O2优化,则反汇编结果变为

    Dump of assembler code for function demo_func:
       0x00000000004004b0 <+0>:    lea    (%rsi,%rdx,1),%eax
       0x00000000004004b3 <+3>:    retq   
    End of assembler dump.

    内容极度简化,在具体计算时,一定要根据反汇编的结果来进行。

  • 相关阅读:
    Linux如何查找大文件或目录总结
    Linux下动态调整LVM文件系统大小
    ios学习路线图
    js模块,类,继承,命名空间,私有属性等相关概念梳理
    <代码大全2>记录
    2017读书计划
    Spring声明式事务
    Spring-Aop
    Spring静态工厂和扫描器
    Spring-IOC
  • 原文地址:https://www.cnblogs.com/hseagle/p/3114049.html
Copyright © 2011-2022 走看看