<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Backtrace on Lynskylate&#39;s Blog</title>
    <link>https://lynskylate.github.io/tags/backtrace/</link>
    <description>Recent content in Backtrace on Lynskylate&#39;s Blog</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <copyright>Copyright © 2019, lynskylate; all rights reserved.</copyright>
    <lastBuildDate>Sat, 06 Jul 2019 01:27:34 +0800</lastBuildDate>
    <atom:link href="https://lynskylate.github.io/tags/backtrace/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>StackTrace的实现</title>
      <link>https://lynskylate.github.io/post/stacktrace/</link>
      <pubDate>Sat, 06 Jul 2019 01:27:34 +0800</pubDate>
      <guid>https://lynskylate.github.io/post/stacktrace/</guid>
      <description>&lt;h1 id=&#34;stacktrace的实现&#34;&gt;StackTrace的实现&lt;/h1&gt;&#xA;&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;&#xA;&lt;p&gt;在python java这些语言中，一旦程序发生了异常，如下图，会打印异常发生时的调用栈，而在c/c++中如果需要实现类似的功能则要我们依靠libbacktrace之类的库去打印调用栈，&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://lynskylate.github.io/images/stacktrace/01.png&#34; alt=&#34;image.png&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;在c/c++中如何去实现一个类似libbacktrace这样的库来打印函数调用栈呢？本文将介绍一种naive的backtrace实现，讲解backtrace实现的原理。&lt;/p&gt;&#xA;&lt;h2 id=&#34;追踪调用栈的基础-栈帧stack-frame&#34;&gt;追踪调用栈的基础-栈帧(stack frame)&lt;/h2&gt;&#xA;&lt;p&gt;栈帧(stack frame)保存着函数调用信息，如下图它存储于c内存布局中的栈区。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://lynskylate.github.io/images/stacktrace/02.png&#34; alt=&#34;image.png&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;栈帧是实现backtrace的核心关键，它会存储着函数调用时的局部变量同时记录了函数调用的上下文如返回地址，其具体布局如下图。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://lynskylate.github.io/images/stacktrace/03.png&#34; alt=&#34;image.png&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;ebp寄存器存储着栈基址指针，esp寄存器存储着当前栈顶指针，我们将ebp-esp之间的内存称为栈帧。&lt;/p&gt;&#xA;&lt;p&gt;想要具体了解函数调用时，栈帧是如何变化的我们可以从汇编代码进行了解，我们将如下的代码进行反汇编&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;// file:naive.c&#xA;int add(int a, int b){&#xA;    return a + b;&#xA;}&#xA;&#xA;int main(int argc, char* argv[]){&#xA;    add(1,2);&#xA;}&#xA;// 反汇编命令 gcc -m32 -S -O0 -masm=intel naive.c&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;得到如下代码&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-assembly&#34;&gt;_add:                                   ## @add&#xA;&#x9;push&#x9;ebp&#xA;&#x9;mov&#x9;ebp, esp&#xA;&#x9;mov&#x9;eax, dword ptr [ebp + 12]&#xA;&#x9;add&#x9;eax, dword ptr [ebp + 8]&#xA;&#x9;pop&#x9;ebp&#xA;&#x9;ret&#xA;_main:                                  ## @main&#xA;&#x9;push&#x9;ebp&#xA;&#x9;mov&#x9;ebp, esp&#xA;&#x9;sub&#x9;esp, 24 # 预留栈空间存储局部变量&#xA;&#x9;mov&#x9;eax, dword ptr [ebp + 12]&#xA;&#x9;mov&#x9;ecx, dword ptr [ebp + 8]&#xA;&#x9;mov&#x9;dword ptr [esp], 1 # 设置局部变量1，2&#xA;&#x9;mov&#x9;dword ptr [esp + 4], 2&#xA;&#x9;mov&#x9;dword ptr [ebp - 4], eax ## 4-byte Spill&#xA;&#x9;mov&#x9;dword ptr [ebp - 8], ecx ## 4-byte Spill&#xA;    call&#x9;_add&#xA;&#x9;xor&#x9;ecx, ecx&#xA;&#x9;mov&#x9;dword ptr [ebp - 12], eax ## 4-byte Spill&#xA;&#x9;mov&#x9;eax, ecx&#xA;&#x9;add&#x9;esp, 24&#xA;&#x9;pop&#x9;ebp&#xA;&#x9;ret&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;在汇编中使用call _add时，它会将下一条地址推入栈中，并跳转至函数位置，即&lt;code&gt;call _add&lt;/code&gt;相当于两条指令,&lt;code&gt;push pc; jmp _add&lt;/code&gt;, 在使用call _add 指令后，此时栈顶(esp指向的地址)存储着xor ecx, ecx指令的地址。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
