每天学点GDB(七)

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

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

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

0397c89f8884fe5cef54f7a1b3de79e2b7aed0c6

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

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

  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.

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

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
8月前
|
机器学习/深度学习 NoSQL Linux
【Linux系统编程】代码调试工具gdb--1
【Linux系统编程】代码调试工具gdb--1
|
8月前
|
机器学习/深度学习 存储 NoSQL
【Linux系统编程】代码调试工具gdb--2
【Linux系统编程】代码调试工具gdb--2
|
8月前
|
NoSQL IDE Linux
《Linux从练气到飞升》No.09 Linux调试器-gdb使用
《Linux从练气到飞升》No.09 Linux调试器-gdb使用
64 0
《Linux从练气到飞升》No.09 Linux调试器-gdb使用
|
开发框架 JavaScript 前端开发
CPython, Pypy, MicroPython...还在傻傻分不清楚?
CPython, Pypy, MicroPython...还在傻傻分不清楚?
|
NoSQL Linux 编译器
Linux工具学习之【gdb】
vim 可以编写代码,gcc/g++ 可以编译代码,此时只最后一件神器,就能进行完整的开发工作,那就是通过 gdb 调试代码,毕竟谁都不敢保证自己的代码没有问题,所以就有调试器这种东西帮助我们定位问题,进而解决问题
219 0
Linux工具学习之【gdb】
|
存储 NoSQL JavaScript
如何优♂雅地学习GDB调试(二)
本章我们将带着大家高雅的学一学令众多习惯图形化页面的朋友难受的 gdb 调试,这部分知识可以选择性学习学习,以后倘若遇到一些问题时能在 Linux 内简单调试,还是很香的。然后在讲讲 gcc 和 g++,系统讲解程序运行时的各个过程。
225 0
如何优♂雅地学习GDB调试(二)
|
NoSQL Linux C语言
如何优♂雅地学习GDB调试(一)
本章我们将带着大家高雅的学一学令众多习惯图形化页面的朋友难受的 gdb 调试,这部分知识可以选择性学习学习,以后倘若遇到一些问题时能在 Linux 内简单调试,还是很香的。然后在讲讲 gcc 和 g++,系统讲解程序运行时的各个过程。
254 0
如何优♂雅地学习GDB调试(一)
|
NoSQL C++ 容器
每天学点GDB(五)
本节分享使用GDB来进行STL容器的调试。
1826 0
|
NoSQL Python Windows
开源代码分析技巧之二——gdb单步调试
在海量的源码面前,如何更好的切入到源码,锁定我们需要的那一行,有时显得格外重要。
287 0
|
安全 数据安全/隐私保护 Windows