如何通过查看堆栈值生成回溯? [英] How to generate the backtrace by looking at the stack values?

查看:195
本文介绍了如何通过查看堆栈值生成回溯?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

测验:如何通过查看堆栈值生成回溯?

0xf3e2de34 f3e2de70 c0135351 401ef021 00000000   p.bsQS...p......
0xf3e2de44 f3e2de81 00000021 f3e2c000 f7950924   ..bs......bs...w
0xf3e2de54 00000000 401ef000 00000246 00000246   .....p..F...F...
0xf3e2de64 00000001 00000001 f7950000 f3e2df18   ...........w..bs
0xf3e2de74 c02898b5 322d7875 23203235 00007820   5...ux.252...x..
0xf3e2de84 f3e2de98 00000000 f7950bd0 bffff0ec   ..bs....P..wlp..
0xf3e2de94 c70cf660 00000000 00000000 bffff0ec   .v.G........lp..
0xf3e2dea4 f3e2c000 f7950930 7fffffff 00000000   ..bs0..w........
0xf3e2deb4 00000000 00000001 00000000 bffff07b   .............p..
0xf3e2dec4 f5cfd880 bffff07b 00000000 f5f24740   .XOu.p.......Gru
0xf3e2ded4 c0124f87 00000000 00000000 00200200   .O..............
0xf3e2dee4 f3e2defc 067b3067 00000000 f5f24740   ..bsg0.......Gru
0xf3e2def4 c0124f87 f7950934 f7950934 c028331b   .O..4..w4..w.3..
0xf3e2df04 00000000 c01b0fe8 f5cfd880 f7950000   ....h....XOu...w
0xf3e2df14 fffffffb f3e2df50 c0283642 00000001   ....P.bsB6......
0xf3e2df24 00000001 00000001 f3e2c000 f6fe30d0   ..........bsP0.v

提示-始终可以通过EIP确定当前功能

我发现了这个问题在 kernelnewbies / ABI 文档中。

我真的不明白那里给出的提示吗?(也许是因为我不知道这件事)。

I found this question at kernelnewbies/ABI documentation.
I really dont understand the hint given there?(Maybe because i have no idea about this thing).

有人可以向我解释如何解决这些问题。

Could somebody explain me how to solve these kinda questions.

推荐答案

在这种情况下,由于该代码是在启用了帧指针的情况下编译的,因此可以非常可靠地完成此操作。

In this case, you can do it quite reliably, since that code has been compiled with frame pointers enabled.

您需要了解的内容:


  • EIP是指向下一条要执行的指令的寄存器。

  • 调用函数时,参数然后将EIP(因此被调用函数知道要返回的位置)保存在堆栈中。

  • EIP is a register that points at the next instruction to execute.
  • When calling a function, the arguments and then EIP (so the called function knows where to return to) are saved on the stack.

(明确或暗含)告知编译器执行以下操作时:使用帧指针,然后将帧指针(保存在EBP寄存器中)保存在堆栈中(以便以后可以将帧指针恢复为调用函数具有的值),然后将帧指针设置为指向当前指针堆栈的顶部。这样可以从已知的参考点(框架指针)轻松访问参数和局部变量,并大大简化调试。

When the compiler has been told (explicitly or implicitly) to use frame pointers, it then saves the frame pointer (in the EBP register) on the stack (so it can later restore the frame pointer to the value it had on the calling function), and then sets the frame pointer to point to the current top of the stack. This allows accessing easily arguments and local variables from a known point of reference (the frame pointer), and greatly simplifies debugging.

因此,要生成回溯,您将遵循框架指针,查看相应的已保存EIPS。因此:

So, to generate a backtrace, you would follow the frame pointers, looking at the corresponding saved EIPS. So:

current function was called from c0135351
follow f3e2de70 → was called from c02898b5
follow f3e2df18 → was called from c0283642

当然,这是简单的情况。如果没有框架指针,则必须猜测堆栈上的给定值是否对应于指令指针。

Of course, this was the easy case. When you don't have frame pointers, you have to guess whether a given value on the stack corresponds to an instruction pointer.

缺少的部分是如何转换这些数字功能名称。具有未剥离的 vmlinux (请注意 x ,而不是 z )文件非常宝贵。 System.map 仅包含一些符号,因此很多时候您只会知道相关的函数位于函数A和函数B之间。

The missing part is how to translate this numbers to function names. Having an unstripped vmlinux (note the x, not a z) file is invaluable. System.map just contains some symbols, so very often you'll only end up knowing that the relevant function was between function A and function B.

编辑:

x86上的函数调用类似于:

A function call on x86 looks something like:

                                        ...
int main()                              add  $-0x8,%esp ; alignment
{                                       push $0x2       ; arg 2
        ...                             push $0x1       ; arg 1
        func(1, 2);                     call func       ; function call
        ...                             add  $0x10,%esp ; pop args from stack
}                                       ...

被调用函数看起来像: / p>

And the called function looks something like:

void func(int arg1, int arg2)           push %ebp       ;\
{                                       mov  %esp,%ebp  ;/ create stack frame
        int local1;                     sub  $0x18,%esp ; reserves space
        ...                             ...
}                                       mov  %ebp,%esp  ;\
                                        pop  %ebp       ;/ destroys frame
                                        ret             ; returns

因此,堆栈看起来类似于:

So, the stack will look similar to:

          :           :
          +-----------+
          : alignment :
          +-----------+
12(%ebp)  |   arg2    |
          +-----------+
 8(%ebp)  |   arg1    |
          +-----------+
 4(%ebp)  |    ret    | -----> return address
          +-----------+
  (%ebp)  |    ebp    | -----> previous ebp
          +-----------+
-4(%ebp)  |  local1   | -----> local vars
          +-----------+
          : alignment :
          +-----------+
          :           :

(低地址在ASCII字符上较低)

(Lower addresses are lower on the ASCII-art)

因此,如果您继续遵循保存的 EBP 指针,则可以获得保存的 EIP 指针( ret ),它指向调用链中的指令(准确地说是在返回链中)。

So, if you keep following the saved EBP pointers, you can get the saved EIP pointers (ret above), which point to instructions in the call chain (in the return chain, to be precise).

这篇关于如何通过查看堆栈值生成回溯?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆