zoukankan      html  css  js  c++  java
  • x86架构调用栈分析

     以一个简单求阶乘的代码为例:

     1 #include <stdio.h>
     2 
     3 unsigned int fact(unsigned int n)
     4 {
     5     if (n == 0)
     6         return 1;
     7     return n * fact(n - 1); 
     8 }
     9 
    10 int main(void)
    11 {
    12     int c = 0;
    13     
    14     c = fact(5);
    15 
    16     return c;
    17 }
    View Code

     其反汇编:

    (gdb) disassemble main
    Dump of assembler code for function main:
       0x080483bb <+0>:    push   %ebp
       0x080483bc <+1>:    mov    %esp,%ebp
       0x080483be <+3>:    and    $0xfffffff0,%esp
       0x080483c1 <+6>:    sub    $0x20,%esp
       0x080483c4 <+9>:    movl   $0x0,0x1c(%esp)
       0x080483cc <+17>:    movl   $0x5,(%esp)
       0x080483d3 <+24>:    call   0x8048394 <fact>
       0x080483d8 <+29>:    mov    %eax,0x1c(%esp)
       0x080483dc <+33>:    mov    0x1c(%esp),%eax
       0x080483e0 <+37>:    leave  
       0x080483e1 <+38>:    ret    
    End of assembler dump.
    (gdb) disassemble fact
    Dump of assembler code for function fact:
       0x08048394 <+0>:    push   %ebp
    => 0x08048395 <+1>:    mov    %esp,%ebp
       0x08048397 <+3>:    sub    $0x18,%esp
       0x0804839a <+6>:    cmpl   $0x0,0x8(%ebp)
       0x0804839e <+10>:    jne    0x80483a7 <fact+19>
       0x080483a0 <+12>:    mov    $0x1,%eax
       0x080483a5 <+17>:    jmp    0x80483b9 <fact+37>
       0x080483a7 <+19>:    mov    0x8(%ebp),%eax
       0x080483aa <+22>:    sub    $0x1,%eax
       0x080483ad <+25>:    mov    %eax,(%esp)
       0x080483b0 <+28>:    call   0x8048394 <fact>
       0x080483b5 <+33>:    imul   0x8(%ebp),%eax
       0x080483b9 <+37>:    leave  
       0x080483ba <+38>:    ret  
    View Code

    fact函数中栈如下:

       |  args          | 参数列表

         |  return addr   | 返回地址,即fact函数的下一条指令地址 

    ebp->|  original ebp  | main函数中的栈帧指针

    ... 

    esp->|                          |

    因此,只要得到当前ebp, 那么(ebp)就是上一次函数调用的ebp, (ebp + 4)就是返回地址, (ebp + (i + 2) * 4)就是第i(i = 0, 1, 2...)个参数. 

     1 void backtrace(int level)
     2 {
     3     unsigned int *ebp = 0, *eip = 0, args[4] = {0};
     4     int i = 0, times = 0;  
     5     if (level < 0)
     6         level = 0x7fffffff;
     7 
     8     // get ebp
     9     __asm__ __volatile__(
    10         "mov %%ebp, %0	
    "
    11         : "=m"(ebp)
    12     );  
    13     eip = (unsigned int *)ebp[1]; // get eip and args
    14     for (i = 0; i < 4; i++)
    15         args[i] = ebp[i + 2]; 
    16 
    17     while (*ebp != NULL && times < level) 
    18     {   
    19         printf(" ebp:0x%08x eip:0x%08x args:0x%08x 0x%08x 0x%08x 0x%08x
    ",
    20                 ebp, eip, args[0], args[1], args[2], args[3]);
    21         ebp = (unsigned int *)ebp[0]; // get ebp
    22         eip = (unsigned int *)ebp[1];
    23         for (i = 0; i < 4; i++)
    24             args[i] = ebp[i + 2]; 
    25         times++;
    26     }   
    27 }
  • 相关阅读:
    并发与并行的区别
    Java 中的JSON 字符串
    java spark list 转为 RDD 转为 dataset 写入表中
    SparkConf和SparkContext
    Java 中清空map
    java JSON的使用和解析
    presto计算日期间隔天数或者小时间隔——date_diff函数使用
    Nginx 负载均衡配置
    Nginx 反向代理配置示例(conf文件配置)
    前端同学 linux常用指令汇总
  • 原文地址:https://www.cnblogs.com/ym65536/p/4180544.html
Copyright © 2011-2022 走看看