<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>展锐 on Hacper&#39;s Blog</title>
    <link>https://hacperme.com/tags/%E5%B1%95%E9%94%90/</link>
    <description>Recent content in 展锐 on Hacper&#39;s Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh</language>
    <lastBuildDate>Tue, 01 Mar 2022 00:55:59 +0800</lastBuildDate>
    <atom:link href="https://hacperme.com/tags/%E5%B1%95%E9%94%90/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>展锐 8910 平台 dump 抓取和分析专题</title>
      <link>https://hacperme.com/posts/series/dump/20220301_unisoc_8910_dump_series/</link>
      <pubDate>Tue, 01 Mar 2022 00:55:59 +0800</pubDate>
      <guid>https://hacperme.com/posts/series/dump/20220301_unisoc_8910_dump_series/</guid>
      <description>展锐 8910 平台 dump 分析培训分享内容。</description>
      <content:encoded><![CDATA[<h2 id="1-dump-抓取方法">1. dump 抓取方法</h2>
<ul>
<li>
<p>抓取dump的前提</p>
<p>抓取dump之前需要关闭模块的硬件看门狗，可以发at指令或者调用open sdk api</p>
<p>AT 指令：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">at+qdbgcfg=&#34;dumpcfg&#34;,0,1
</span></span></code></pre></td></tr></table>
</div>
</div><p>API:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">ql_dev_cfg_wdt</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>  <span class="c1">// 打开硬件看门狗 
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">ql_dev_cfg_wdt</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>  <span class="c1">// 关闭硬件看门狗
</span></span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p>使用coolwatcher工具抓取dump的步骤</p>
<p>在coolwatcher 工具里面选择tools-&gt;blue screen dump。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.6kl1y8cbopo0.png" alt=""  />
</p>
<p>elf 文件选择编译出来的 8915DM_cat1_open_core.elf，然后再选择dump的保存路径，点击start开始传输dump文件。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.7ba9cp8hy100.png" alt=""  />
</p>
</li>
<li>
<p>Trace32 加载dump文件步骤</p>
<p>解压压缩包T32.7z，把dump文件复制到8910dump目录下，同时需要复制编译固件的elf文件，同时放到8910dump目录下。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.1beoxyunshvk.png" alt=""  />
</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.68n5rt8flvc0.png" alt=""  />
</p>
<p>双击 T32_8910_Quectel_ap.bat 脚本进行解析</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.2gg1onaptj6s.png" alt=""  />
</p>
</li>
<li>
<p>抓取dump失败的常见问题</p>
<ol>
<li>设备的固件与解析的elf文件不匹配</li>
<li>使用串口抓取dump，需要串口支持921600波特率，否则只能用USB方式抓取。</li>
<li>dump 无法导出，尝试使用GDB launch 工具查看调用栈。</li>
<li>调用栈函数名字没有解析出来，可能是没有加载客户app的elf文件，重新加载app.elf或者根据函数地址查找map文件。</li>
</ol>
</li>
</ul>
<h2 id="2-trace32-工具的使用">2. TRACE32 工具的使用</h2>
<ul>
<li>
<p>查看调用栈</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.6197iyj56dw0.png" alt=""  />
</p>
</li>
<li>
<p>查看寄存器</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.5333jcu67bg0.png" alt=""  />
</p>
<p>R15&ndash;PC,</p>
<p>R14&ndash;LR,</p>
<p>R13&ndash;SP</p>
</li>
<li>
<p>dump 内存</p>
<p>输入16进制内存地址</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.1b610cfkslcw.png" alt=""  />
</p>
</li>
<li>
<p>线程列表</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.43dqywq15fw0.png" alt=""  />
</p>
<p>查看tcb</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.7bdb8l9q8vo0.png" alt=""  />
</p>
</li>
<li>
<p>查看变量</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.5jmpgb7kr3k0.png" alt=""  />
</p>
<p>使用命令 v.v 变量类型 变量地址</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.5xhx3po5k6o0.png" alt=""  />
</p>
</li>
<li>
<p>通过设置断点，根据函数地址查找函数名字</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.6j2i6nh6ioo0.png" alt=""  />
</p>
</li>
<li>
<p>查看类型，数据结构定义</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.4h67d8yjesq0.png" alt=""  />
</p>
</li>
<li>
<p>查看代码</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.3c77p6zotde0.png" alt=""  />
</p>
</li>
</ul>
<h2 id="3-典型问题分析方法">3. 典型问题分析方法</h2>
<h3 id="31-访问非法内存死机">3.1 访问非法内存死机</h3>
<p>怎么判断是否是访问非法内存呢？</p>
<p>通过查看死机时的汇编指令(通常是内存寻址指令)和寄存器来判定。</p>
<p>示例如下图：</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.30mm21bknm60.png" alt=""  />
</p>
<p>ldrb    r2,[r1,#0x5]  为死机的位置，ldrb 是ARM汇编中的一条内存寻址指令，作用是将从存储器中将一个<code>8</code>位的字节数据传送到目的寄存器中，同时将寄存器的高<code>24</code>位清零。</p>
<p>ldrb    r2,[r1,#0x5]  的意思是 将存储器地址为R1＋0x5的字节数据读入寄存器R2，并将R2的高24位清零。也就是该指令访问了R1寄存器所指向的内存地址，而查看R1寄存器的值发现R1为0，也就是访问了空指针（0地址）。</p>
<h3 id="32-栈溢出分析及栈溢出检测机制">3.2 栈溢出分析及栈溢出检测机制</h3>
<p>栈溢出问题可以分为两种类型，一是栈空间不够，在压栈的时候使用的空间超出了任务的栈空间大小；另一种是局部变量溢出，导致栈空间保存的数据被破坏。</p>
<ul>
<li>
<p>栈溢出检测机制</p>
<p>展锐的8910使用的是FreeRTOS， 栈的增长方向是从高地址向地址增长。其栈溢出检测的原理是，在任务栈初始化的时候，填充固定数据0xA5，kernel 会检查任务栈的最后16个字节的数据是否被篡改，如果发现这16个字节不是0xA5，则会触发ApplicationStackOverflowHook的调用，然后调用osiPanic() 主动死机。</p>
</li>
<li>
<p>栈溢出分析示例</p>
<p>栈空间大小分配不够导致栈溢出的问题的判断方法：</p>
<p>这种类型的栈溢出判断比较简单，直接看Trace32的调用栈里面是否有vApplicationStackOverflowHook的调用；或者查看死机task的TCB，查看其栈的起始地址对应的内存数据，看填充的A5A5数据是不是被篡改了，如果不是A5A5，则可以断定是栈溢出了。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.705487lxxtg0.png" alt=""  />
</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.1yppd8g70s9s.png" alt=""  />
</p>
<p>局部变量溢出导致dump的判断方法：</p>
<p>这类问题比较难定位到具体是哪个变量溢出，但表现的现象很有特点：看任务栈的空间没有超，且每次死机的位置不固定，或者PC寄存器指向的地址不是一个正常的函数地址。遇到这类情况可以往局部变量溢出导致栈被破坏这个方向排查问题。</p>
</li>
</ul>
<h3 id="33-内存写越界dump分析方法">3.3 内存写越界dump分析方法</h3>
<p>判断内存越界的方法，如果有导出heap report文件，可以先看下heap report里面有没有 block tail pattern error 的地方。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.19zvvjkrafuo.png" alt=""  />
</p>
<p>如果没有heap report文件，则可以从dump解析里面去判断，具体方法如下：</p>
<p>先看调用栈里面有没有prvFree.part.5的调用，如果有则有可能是检测到内存被破坏了。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.x7rh1xs5d5s.png" alt=""  />
</p>
<p>对于指定的内存地址，看有没有越界，还可以通过dump内存数据来判断，例如查看0x80BAEC90这个地址有没有被破坏：</p>
<p>在命令窗口执行如下命令，对内存地址转换为struct osiBlockHeader *变量来查看：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">v.v (struct osiBlockHeader *)(0x80BAEC90-8)
</span></span></code></pre></td></tr></table>
</div>
</div><p>注意对地址0x80BAEC90 向前偏移8个字节</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.3whwy6coo960.png" alt=""  />
</p>
<p>我们可以得到几个信息：</p>
<p>caller = 806162601</p>
<p>size = 4,</p>
<p>对caller = 806162601（十进制）乘以2，可以得到申请这段内存的函数地址，806162601*2=1612325202 （0x601A2152），通过设置断点的方式可以找到地址0x601A2152对应的函数是mlConvertStr。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.52wsocwbxq40.png" alt=""  />
</p>
<p>size = 4（十进制）乘以8，可以得到这段内存的大小，4*8=32 （0x20）</p>
<p>然后dump这段内存，查看这段内存的tail位置是否为fd，如果不是则表示越界了</p>
<p>查看方法，输入指令:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">d.dump (0x80BAEC90-0x08+0x20-0x01)
</span></span></code></pre></td></tr></table>
</div>
</div><p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.3vk1kk62we80.png" alt=""  />
</p>
<p>看箭头所指的地方是否为fd。</p>
<p>tail的位置的计算方法，内存地址-0x08+内存大小-0x01</p>
<p>上述dump内存的方法和导出的heap report是可以对应的上的：</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.3xcjcommx8o0.png" alt=""  />
</p>
<p>​	取一个正常的内存地址做对比，以dump中的地址0x80BAEC28为例：</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.1xhhsmxcpeow.png" alt=""  />
</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.3yp6fparxdm0.png" alt=""  />
</p>
<p>caller = 806521247,  806521247*2 = 1613042494（0x6025133E）</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.4ve7tv6clp80.png" alt=""  />
</p>
<p>size = 13,  13*8=104 (0x68)</p>
<p>查看tail</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">d.dump (0x80BAEC28-0x08+0x68-0x01)
</span></span></code></pre></td></tr></table>
</div>
</div><p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.3pptmlkjmgo0.png" alt=""  />
</p>
<p>内存的结构：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"> 8字节header(包含caller，size等信息)+数据区(malloc返回的地址)+8字节对齐的冗余数据+1字节的尾部（其中最后一个字节为tail: FD）
</span></span></code></pre></td></tr></table>
</div>
</div><p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/20210507/xxx.4spx3x78qqe0.png" alt=""  />
</p>
]]></content:encoded>
    </item>
    <item>
      <title>FreeRTOS 中的栈溢出检测机制</title>
      <link>https://hacperme.com/posts/series/dump/2021-04-11-freertos_stack_overflow_checking/</link>
      <pubDate>Sun, 11 Apr 2021 23:26:09 +0800</pubDate>
      <guid>https://hacperme.com/posts/series/dump/2021-04-11-freertos_stack_overflow_checking/</guid>
      <description>FreeRTOS中的任务栈结构介绍 在FreeRTOS中，创建任务A、B、C三个任务，以栈的生长方向从高到低为例，其任务栈结构如下图所示： 1 2</description>
      <content:encoded><![CDATA[<h2 id="freertos中的任务栈结构介绍">FreeRTOS中的任务栈结构介绍</h2>
<p>在FreeRTOS中，创建任务A、B、C三个任务，以栈的生长方向从高到低为例，其任务栈结构如下图所示：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">                           <span class="o">+-----------&gt;+--------+</span>        <span class="n">Low</span>
</span></span><span class="line"><span class="cl">                           <span class="o">|</span>            <span class="o">|</span>        <span class="o">|</span>         <span class="o">^</span>
</span></span><span class="line"><span class="cl">                           <span class="o">|</span>            <span class="o">|</span><span class="n">No</span> <span class="n">used</span> <span class="o">|</span>   <span class="n">SP</span>    <span class="o">|</span>
</span></span><span class="line"><span class="cl">                           <span class="o">|</span>            <span class="o">|--------|&lt;------</span>  <span class="o">|</span>
</span></span><span class="line"><span class="cl">                           <span class="o">|</span>            <span class="o">|</span>  <span class="n">regs</span>  <span class="o">|</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl">                           <span class="o">|</span>            <span class="o">+--------+</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl">                           <span class="o">|</span>            <span class="o">|</span> <span class="n">Func</span> <span class="mi">4</span> <span class="o">|</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"><span class="n">Low</span>   <span class="o">+---------------+</span>    <span class="o">|</span>            <span class="o">+--------+</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">^</span>    <span class="o">|</span>               <span class="o">|</span>    <span class="o">|</span>            <span class="o">|</span> <span class="n">Func</span> <span class="mi">3</span> <span class="o">|</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span> <span class="n">Task</span> <span class="n">C</span> <span class="n">stack</span>  <span class="o">|</span>    <span class="o">|</span>            <span class="o">+--------+</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">+-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-+</span>    <span class="o">|</span>            <span class="o">|</span> <span class="n">Func</span> <span class="mi">2</span> <span class="o">|</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span>     <span class="n">TCB</span>       <span class="o">|</span>    <span class="o">|</span>            <span class="o">+--------+</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">+---------------+----+</span>            <span class="o">|</span> <span class="n">Func</span> <span class="mi">1</span> <span class="o">|</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span>               <span class="o">|</span>           <span class="o">+----&gt;+--------+</span>        <span class="n">High</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span> <span class="n">Task</span> <span class="n">B</span> <span class="n">stack</span>  <span class="o">|</span>           <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">+-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-+-----------+</span>     <span class="n">Task</span> <span class="n">B</span> <span class="n">stack</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span>     <span class="n">TCB</span>       <span class="o">|</span>           
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">+---------------+</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span>               <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span> <span class="n">Task</span> <span class="n">A</span> <span class="n">stack</span>  <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">+-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-+</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span>     <span class="n">TCB</span>       <span class="o">|</span>
</span></span><span class="line"><span class="cl"><span class="n">High</span>  <span class="o">+---------------+</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>在内存上可以先简单看作是每个任务按任务控制块（TCB）+ stack 依次存储，任务在执行过程中的上下文数据会保存在栈中，比如函数的局部变量，传递参数，返回地址等都会存储在栈里面。在task B的栈空间里，函数调用关系是 Func 1 调用 → Func 2  调用 → Func 3 调用 → Func 4。在这样的栈存储结构，任务在运行的过程中可能会出现下面两个问题：</p>
<ol>
<li>栈溢出导致的问题，如果任务中函数调用的层级过深，或者函数内部有定义占用空间较大的局部变量，则有可能在压栈的时候使用的空间超出了任务的栈空间大小，例如task B 中的函数调用层级过多，会导致task B 的数据写到task C的堆栈空间里，从而破坏task C的栈空间。</li>
<li>局部变量溢出导致的问题，以task B为例，如果在Func 4函数里面操作局部变量，不小心把局部变量“写穿了”，则有可能会把返回地址给覆盖了，Func 4返回时会跳转的错误的地址而出现异常，Func 4执行完也就无法返回到Func 3了，如果覆盖的空间较大，则有可能将数据写到task A的栈空间，从而导致task A的栈空间也被破坏。</li>
</ol>
<h2 id="freertos的栈溢出检测机制">FreeRTOS的栈溢出检测机制</h2>
<p>根据FreeRTOS的官方文档介绍，FreeRTOS提供两种栈溢出的检测方法，可以通过配置configCHECK_FOR_STACK_OVERFLOW 这个宏来选择使用哪种方法，当kernel检测到栈溢出的情况之后，会调用钩子函数vApplicationStackOverflowHook，该函数的原型如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vApplicationStackOverflowHook</span><span class="p">(</span> <span class="n">TaskHandle_t</span> <span class="n">xTask</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                    <span class="kt">signed</span> <span class="kt">char</span> <span class="o">*</span><span class="n">pcTaskName</span> <span class="p">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="栈溢出检测方式1">栈溢出检测方式1</h3>
<p>第一种检测方式的原理是检查当前任务的栈顶指针是否在该任务的栈空间范围之内，如果栈顶指针不在任务的栈空间，则会触发钩子函数vApplicationStackOverflowHook，通过配置宏configCHECK_FOR_STACK_OVERFLOW为1使用这种检测方式，具体可以看下include/stack_macros.h路径的源码：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) &amp;&amp; ( portSTACK_GROWTH &lt; 0 ) )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* Only the current stack state is to be checked. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#define taskCHECK_FOR_STACK_OVERFLOW()                                                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">    {                                                                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">        </span><span class="cm">/* Is the currently saved stack pointer within the stack limit? */</span><span class="cp">                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">        if( pxCurrentTCB-&gt;pxTopOfStack &lt;= pxCurrentTCB-&gt;pxStack )                                     \
</span></span></span><span class="line"><span class="cl"><span class="cp">        {                                                                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">            vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB-&gt;pcTaskName ); \
</span></span></span><span class="line"><span class="cl"><span class="cp">        }                                                                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">    }
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif </span><span class="cm">/* configCHECK_FOR_STACK_OVERFLOW == 1 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="cm">/*-----------------------------------------------------------*/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) &amp;&amp; ( portSTACK_GROWTH &gt; 0 ) )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* Only the current stack state is to be checked. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#define taskCHECK_FOR_STACK_OVERFLOW()                                                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">    {                                                                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                                                                      \
</span></span></span><span class="line"><span class="cl"><span class="cp">        </span><span class="cm">/* Is the currently saved stack pointer within the stack limit? */</span><span class="cp">                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">        if( pxCurrentTCB-&gt;pxTopOfStack &gt;= pxCurrentTCB-&gt;pxEndOfStack )                                \
</span></span></span><span class="line"><span class="cl"><span class="cp">        {                                                                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">            vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB-&gt;pcTaskName ); \
</span></span></span><span class="line"><span class="cl"><span class="cp">        }                                                                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">    }
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif </span><span class="cm">/* configCHECK_FOR_STACK_OVERFLOW == 1 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="cm">/*-----------------------------------------------------------*/</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>在分析代码之前要留意一下栈的生长方向，其中宏 portSTACK_GROWTH 表示栈的生长方向，当配置portSTACK_GROWTH &lt; 0 时，说明栈的生长方向是从高地址向低地址生长，反之，当配置portSTACK_GROWTH &gt; 0 是，表示栈是从低地址向高地址增长。pxTopOfStack 记录了当前任务的栈顶指针，pxStack是当前任务栈的起始地址，而pxEndOfStack是当前任务栈的结束地址。从代码可以看出此方式是直接通过判断当前堆栈指针是否超出了任务栈空间的合法范围来检测溢出的，如果当前任务栈被其他任务破坏了，这种方式就检测不出来。</p>
<h3 id="栈溢出检测方式2">栈溢出检测方式2</h3>
<p>方式2的检测原理是在任务栈初始化的时候，填充固定数据0xA5，kernel 会检查任务栈的最后16个字节的数据是否被篡改，如果发现这16个字节不是0xA5，则会触发vApplicationStackOverflowHook的调用，通过配置configCHECK_FOR_STACK_OVERFLOW &gt;1 来使用此方式。以下是该检测方式的源码，分析时要注意栈的增长方向：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#if ( ( configCHECK_FOR_STACK_OVERFLOW &gt; 1 ) &amp;&amp; ( portSTACK_GROWTH &lt; 0 ) )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#define taskCHECK_FOR_STACK_OVERFLOW()                                                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">    {                                                                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">        const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB-&gt;pxStack;                       \
</span></span></span><span class="line"><span class="cl"><span class="cp">        const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5;                                        \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                                                                      \
</span></span></span><span class="line"><span class="cl"><span class="cp">        if( ( pulStack[ 0 ] != ulCheckValue ) ||                                                      \
</span></span></span><span class="line"><span class="cl"><span class="cp">            ( pulStack[ 1 ] != ulCheckValue ) ||                                                      \
</span></span></span><span class="line"><span class="cl"><span class="cp">            ( pulStack[ 2 ] != ulCheckValue ) ||                                                      \
</span></span></span><span class="line"><span class="cl"><span class="cp">            ( pulStack[ 3 ] != ulCheckValue ) )                                                       \
</span></span></span><span class="line"><span class="cl"><span class="cp">        {                                                                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">            vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB-&gt;pcTaskName ); \
</span></span></span><span class="line"><span class="cl"><span class="cp">        }                                                                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">    }
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif </span><span class="cm">/* #if( configCHECK_FOR_STACK_OVERFLOW &gt; 1 ) */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="cm">/*-----------------------------------------------------------*/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#if ( ( configCHECK_FOR_STACK_OVERFLOW &gt; 1 ) &amp;&amp; ( portSTACK_GROWTH &gt; 0 ) )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#define taskCHECK_FOR_STACK_OVERFLOW()                                                                                                \
</span></span></span><span class="line"><span class="cl"><span class="cp">    {                                                                                                                                     \
</span></span></span><span class="line"><span class="cl"><span class="cp">        int8_t * pcEndOfStack = ( int8_t * ) pxCurrentTCB-&gt;pxEndOfStack;                                                                  \
</span></span></span><span class="line"><span class="cl"><span class="cp">        static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE,   \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                        tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE,   \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                        tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE,   \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                        tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE,   \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                        tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                                                                                                          \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                                                                                                          \
</span></span></span><span class="line"><span class="cl"><span class="cp">        pcEndOfStack -= sizeof( ucExpectedStackBytes );                                                                                   \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                                                                                                          \
</span></span></span><span class="line"><span class="cl"><span class="cp">        </span><span class="cm">/* Has the extremity of the task stack ever been written over? */</span><span class="cp">                                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">        if( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 )                     \
</span></span></span><span class="line"><span class="cl"><span class="cp">        {                                                                                                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">            vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB-&gt;pcTaskName );                                     \
</span></span></span><span class="line"><span class="cl"><span class="cp">        }                                                                                                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">    }
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif </span><span class="cm">/* #if( configCHECK_FOR_STACK_OVERFLOW &gt; 1 ) */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="cm">/*-----------------------------------------------------------*/</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>在任务栈初始化的时候填充已知的固定数据0xA5，除了做栈溢出检测之外，还可以作为评估线程运行时占用栈空间大小的方法。其原理是在线程运行一段时间之后，从栈的低地址统计有多少字节的0xA5得到栈空间未使用的大小（假设栈从高地址向低地址生长），用栈的总大小减去未使用的空间便可得到该任务运行时栈可能的最大使用大小。</p>
<h2 id="kernel-在什么时候检查栈溢出呢">kernel 在什么时候检查栈溢出呢？</h2>
<p>在 tasks.c 文件可以查到taskCHECK_FOR_STACK_OVERFLOW在 void vTaskSwitchContext( void )函数中被调用，也就是在任务上下文切换的时候做检查，从这点可以看出软件检查栈溢出的方式具有一定的滞后性，任务栈被破坏了并不能马上检测到问题。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span><span class="lnt">68
</span><span class="lnt">69
</span><span class="lnt">70
</span><span class="lnt">71
</span><span class="lnt">72
</span><span class="lnt">73
</span><span class="lnt">74
</span><span class="lnt">75
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vTaskSwitchContext</span><span class="p">(</span> <span class="kt">void</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span> <span class="n">uxSchedulerSuspended</span> <span class="o">!=</span> <span class="p">(</span> <span class="n">UBaseType_t</span> <span class="p">)</span> <span class="n">pdFALSE</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* The scheduler is currently suspended - do not allow a context
</span></span></span><span class="line"><span class="cl"><span class="cm">         * switch. */</span>
</span></span><span class="line"><span class="cl">        <span class="n">xYieldPending</span> <span class="o">=</span> <span class="n">pdTRUE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">xYieldPending</span> <span class="o">=</span> <span class="n">pdFALSE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">traceTASK_SWITCHED_OUT</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cp">#if ( configGENERATE_RUN_TIME_STATS == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="cp">#ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>                    <span class="nf">portALT_GET_RUN_TIME_COUNTER_VALUE</span><span class="p">(</span> <span class="n">ulTotalRunTime</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="cp">#else
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>                    <span class="n">ulTotalRunTime</span> <span class="o">=</span> <span class="nf">portGET_RUN_TIME_COUNTER_VALUE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">                <span class="cm">/* Add the amount of time the task has been running to the
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * accumulated time so far.  The time the task started running was
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * stored in ulTaskSwitchedInTime.  Note that there is no overflow
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * protection here so count values are only valid until the timer
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * overflows.  The guard against negative values is to protect
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * against suspect run time stat counter implementations - which
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * are provided by the application, not the kernel. */</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span><span class="p">(</span> <span class="n">ulTotalRunTime</span> <span class="o">&gt;</span> <span class="n">ulTaskSwitchedInTime</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">pxCurrentTCB</span><span class="o">-&gt;</span><span class="n">ulRunTimeCounter</span> <span class="o">+=</span> <span class="p">(</span> <span class="n">ulTotalRunTime</span> <span class="o">-</span> <span class="n">ulTaskSwitchedInTime</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="k">else</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nf">mtCOVERAGE_TEST_MARKER</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="n">ulTaskSwitchedInTime</span> <span class="o">=</span> <span class="n">ulTotalRunTime</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#endif </span><span class="cm">/* configGENERATE_RUN_TIME_STATS */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* Check for stack overflow, if configured. */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">taskCHECK_FOR_STACK_OVERFLOW</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* Before the currently running task is switched out, save its errno. */</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#if ( configUSE_POSIX_ERRNO == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">pxCurrentTCB</span><span class="o">-&gt;</span><span class="n">iTaskErrno</span> <span class="o">=</span> <span class="n">FreeRTOS_errno</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* Select a new task to run using either the generic C or port
</span></span></span><span class="line"><span class="cl"><span class="cm">         * optimised asm code. */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">taskSELECT_HIGHEST_PRIORITY_TASK</span><span class="p">();</span> <span class="cm">/*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">traceTASK_SWITCHED_IN</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* After the new task is switched in, update the global errno. */</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#if ( configUSE_POSIX_ERRNO == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">FreeRTOS_errno</span> <span class="o">=</span> <span class="n">pxCurrentTCB</span><span class="o">-&gt;</span><span class="n">iTaskErrno</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">        <span class="cp">#if ( configUSE_NEWLIB_REENTRANT == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="cm">/* Switch Newlib&#39;s _impure_ptr variable to point to the _reent
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * structure specific to this task.
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * for additional information. */</span>
</span></span><span class="line"><span class="cl">                <span class="n">_impure_ptr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">(</span> <span class="n">pxCurrentTCB</span><span class="o">-&gt;</span><span class="n">xNewLib_reent</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#endif </span><span class="cm">/* configUSE_NEWLIB_REENTRANT */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="cm">/*-----------------------------------------------------------*/</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="栈溢出实例分析">栈溢出实例分析</h2>
<h3 id="任务控制块tcb中的与栈相关的成员介绍">任务控制块（TCB）中的与栈相关的成员介绍</h3>
<p>FreeRTOS的TCB数据结构在 tasks.c 文件中定义，源码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span><span class="lnt">68
</span><span class="lnt">69
</span><span class="lnt">70
</span><span class="lnt">71
</span><span class="lnt">72
</span><span class="lnt">73
</span><span class="lnt">74
</span><span class="lnt">75
</span><span class="lnt">76
</span><span class="lnt">77
</span><span class="lnt">78
</span><span class="lnt">79
</span><span class="lnt">80
</span><span class="lnt">81
</span><span class="lnt">82
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Task control block.  A task control block (TCB) is allocated for each task,
</span></span></span><span class="line"><span class="cl"><span class="cm"> * and stores task state information, including a pointer to the task&#39;s context
</span></span></span><span class="line"><span class="cl"><span class="cm"> * (the task&#39;s run time environment, including register values)
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">tskTaskControlBlock</span>       <span class="cm">/* The old naming convention is used to prevent breaking kernel aware debuggers. */</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">volatile</span> <span class="n">StackType_t</span> <span class="o">*</span> <span class="n">pxTopOfStack</span><span class="p">;</span> <span class="cm">/*&lt; Points to the location of the last item placed on the tasks stack.  THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( portUSING_MPU_WRAPPERS == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">xMPU_SETTINGS</span> <span class="n">xMPUSettings</span><span class="p">;</span> <span class="cm">/*&lt; The MPU settings are defined as part of the port layer.  THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="n">ListItem_t</span> <span class="n">xStateListItem</span><span class="p">;</span>                  <span class="cm">/*&lt; The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */</span>
</span></span><span class="line"><span class="cl">    <span class="n">ListItem_t</span> <span class="n">xEventListItem</span><span class="p">;</span>                  <span class="cm">/*&lt; Used to reference a task from an event list. */</span>
</span></span><span class="line"><span class="cl">    <span class="n">UBaseType_t</span> <span class="n">uxPriority</span><span class="p">;</span>                     <span class="cm">/*&lt; The priority of the task.  0 is the lowest priority. */</span>
</span></span><span class="line"><span class="cl">    <span class="n">StackType_t</span> <span class="o">*</span> <span class="n">pxStack</span><span class="p">;</span>                      <span class="cm">/*&lt; Points to the start of the stack. */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">char</span> <span class="n">pcTaskName</span><span class="p">[</span> <span class="n">configMAX_TASK_NAME_LEN</span> <span class="p">];</span> <span class="cm">/*&lt; Descriptive name given to the task when created.  Facilitates debugging only. */</span> <span class="cm">/*lint !e971 Unqualified char types are allowed for strings and single characters only. */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( ( portSTACK_GROWTH &gt; 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">StackType_t</span> <span class="o">*</span> <span class="n">pxEndOfStack</span><span class="p">;</span> <span class="cm">/*&lt; Points to the highest valid address for the stack. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( portCRITICAL_NESTING_IN_TCB == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">UBaseType_t</span> <span class="n">uxCriticalNesting</span><span class="p">;</span> <span class="cm">/*&lt; Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configUSE_TRACE_FACILITY == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">UBaseType_t</span> <span class="n">uxTCBNumber</span><span class="p">;</span>  <span class="cm">/*&lt; Stores a number that increments each time a TCB is created.  It allows debuggers to determine when a task has been deleted and then recreated. */</span>
</span></span><span class="line"><span class="cl">        <span class="n">UBaseType_t</span> <span class="n">uxTaskNumber</span><span class="p">;</span> <span class="cm">/*&lt; Stores a number specifically for use by third party trace code. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configUSE_MUTEXES == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">UBaseType_t</span> <span class="n">uxBasePriority</span><span class="p">;</span> <span class="cm">/*&lt; The priority last assigned to the task - used by the priority inheritance mechanism. */</span>
</span></span><span class="line"><span class="cl">        <span class="n">UBaseType_t</span> <span class="n">uxMutexesHeld</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configUSE_APPLICATION_TASK_TAG == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">TaskHookFunction_t</span> <span class="n">pxTaskTag</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS &gt; 0 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="kt">void</span> <span class="o">*</span> <span class="n">pvThreadLocalStoragePointers</span><span class="p">[</span> <span class="n">configNUM_THREAD_LOCAL_STORAGE_POINTERS</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configGENERATE_RUN_TIME_STATS == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="kt">uint32_t</span> <span class="n">ulRunTimeCounter</span><span class="p">;</span> <span class="cm">/*&lt; Stores the amount of time the task has spent in the Running state. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configUSE_NEWLIB_REENTRANT == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="cm">/* Allocate a Newlib reent structure that is specific to this task.
</span></span></span><span class="line"><span class="cl"><span class="cm">         * Note Newlib support has been included by popular demand, but is not
</span></span></span><span class="line"><span class="cl"><span class="cm">         * used by the FreeRTOS maintainers themselves.  FreeRTOS is not
</span></span></span><span class="line"><span class="cl"><span class="cm">         * responsible for resulting newlib operation.  User must be familiar with
</span></span></span><span class="line"><span class="cl"><span class="cm">         * newlib and must provide system-wide implementations of the necessary
</span></span></span><span class="line"><span class="cl"><span class="cm">         * stubs. Be warned that (at the time of writing) the current newlib design
</span></span></span><span class="line"><span class="cl"><span class="cm">         * implements a system-wide malloc() that must be provided with locks.
</span></span></span><span class="line"><span class="cl"><span class="cm">         *
</span></span></span><span class="line"><span class="cl"><span class="cm">         * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
</span></span></span><span class="line"><span class="cl"><span class="cm">         * for additional information. */</span>
</span></span><span class="line"><span class="cl">        <span class="k">struct</span>  <span class="n">_reent</span> <span class="n">xNewLib_reent</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configUSE_TASK_NOTIFICATIONS == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">ulNotifiedValue</span><span class="p">[</span> <span class="n">configTASK_NOTIFICATION_ARRAY_ENTRIES</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="k">volatile</span> <span class="kt">uint8_t</span> <span class="n">ucNotifyState</span><span class="p">[</span> <span class="n">configTASK_NOTIFICATION_ARRAY_ENTRIES</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* See the comments in FreeRTOS.h with the definition of
</span></span></span><span class="line"><span class="cl"><span class="cm">     * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) </span><span class="cm">/*lint !e731 !e9029 Macro has been consolidated for readability reasons. */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="kt">uint8_t</span> <span class="n">ucStaticallyAllocated</span><span class="p">;</span>                     <span class="cm">/*&lt; Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( INCLUDE_xTaskAbortDelay == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="kt">uint8_t</span> <span class="n">ucDelayAborted</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configUSE_POSIX_ERRNO == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="kt">int</span> <span class="n">iTaskErrno</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="p">}</span> <span class="n">tskTCB</span><span class="p">;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>这里仅介绍几个与任务栈相关的几个成员。</p>
<ul>
<li>pxTopOfStack</li>
</ul>
<p>pxTopOfStack 存储任务栈的栈顶地址。</p>
<ul>
<li>pxStack</li>
</ul>
<p>pxStack 存储任务栈空间的起始地址，也就栈的最低地址。</p>
<ul>
<li>pxEndOfStack</li>
</ul>
<p>pxEndOfStack 存储任务栈空间的结束地址，也就是栈的最高地址。</p>
<h3 id="ec200u-栈溢出导致memory-dump-分析">EC200U 栈溢出导致memory dump 分析</h3>
<p>EC200U模组（Unisoc 8910Dm）的open sdk使用的RTOS是FreeRTOS，模组死机时导出的memory dump 文件可以通过trace 32工具分析，以下是一个栈溢出的dump实例。</p>
<p>通过trace 32加载dump文件，首先看到死机前函数栈帧里有调用vApplicationStackOverflowHook函数，说明kernel在任务上下文切换时检测到了栈溢出问题。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled.3ljv1vf7grq0.png" alt=""  />
</p>
<p>查看线程列表，正在执行的task是rm_app_start这个线程，也就是在这个任务死机了。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-1.vqzir71hzpc.png" alt=""  />
</p>
<p>接着查看rm_app_start的任务控制块数据，其中有三个数据需要关注：</p>
<ol>
<li>TCB的起始地址→0x80bf78b8</li>
</ol>
<p>还记得前面介绍的FreeRTOS栈存储结构吗？是按照TCB+stack方式存储的。EC200U的栈增长方向是按高地址向地址增长的。</p>
<p>TCB+stack紧挨着，所以知道了TCB的起始地址也就知道了栈的结束地址（最高地址，栈底）。</p>
<ol start="2">
<li>栈顶地址 pxTopOfStack →0x80bf7504</li>
</ol>
<p>从栈底到栈顶这段空间存储了函数调用的地址，对分析函数调用关系挺有帮助。</p>
<ol start="3">
<li>栈的起始地址 pxStack → 0x80bf74a8</li>
</ol>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-2.3jp3qqf5xdc0.png" alt=""  />
</p>
<p>可以使用trace 32查看这些地址的内存数据，比如，先来看TCB的起始地址→0x80bf78b8的数据，下图右箭头所指的地方就是TCB的起始地址，我们可以看到开始的4个字节数据是0x80bf7504，也就是存储着pxTopOfStack的地址。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-3.3bfxtptmgl80.png" alt=""  />
</p>
<p>接着再看栈的最低地址0x80bf74a8的数据，通过前面栈溢出的检测机制介绍可以知道，栈在初始化的时候会填充默认值0xA5，然后kernel在任务上下文切换的时候会去比较栈起始地址开始的16个字节是不是保持着默认值0xA5，如果初始值被篡改，则会触发vApplicationStackOverflowHook。从下图的数据来看，栈最低地址0x80bf74a8的数据已经被破坏了。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-4.2hud1c5k2rs0.png" alt=""  />
</p>
<p>我们再找一个正常的task做对比，也就是下图的IDLE task。同样的方法，先查看TCB数据，找到栈的起始地址0x80992500, 栈的结束地址0x809934f8，栈顶地址0x8099342c。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-5.12q9ltcipo8g.png" alt=""  />
</p>
<p>查看栈的起始地址0x80992500的内存数据，开始的16个字节都是0xA5，说明栈空间够，没有溢出，且还有一段未曾使用的空间。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-6.mjuoih6h8hc.png" alt=""  />
</p>
<p>再看栈顶地址0x8099342c的数据，在0x8099342c之上有一段不是0xA5的数据，说明曾经有发生过函数调用，且成功返回了。栈保存的函数调用信息在出栈之后并不会主动清除，只会在下次入栈的时候数据覆盖，由此我们也可以通过这个信息查看这个任务曾经调用过什么函数。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-7.v4jbtnlhacw.png" alt=""  />
</p>
<p>从栈顶到栈底这段空间保存在函数调用的信息，我们可以使用trace 32来解析函数调用关系。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-8.5d2amkqv3u80.png" alt=""  />
</p>
<p>在栈顶与栈底这段内存数据里，我先找到其中的函数地址，再通过设置断点的方式根据地址找到函数名。比如下图依次找到两个程序地址：0x6014B6CF 和 0x6014B651，通过设置断点查到对应的函数名prvIdleTask和xTaskResumeAll，再根据栈的增长方向可以判断是 prvIdleTask  调用→ xTaskResumeAll。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-9.67ablvydo580.png" alt=""  />
</p>
<p>把栈空间所有程序地址找出来，按照同样的方法就能找到这个任务的函数调用痕迹了：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="mi">6014</span><span class="n">B795</span>	<span class="n">vTaskSwitchContext</span><span class="err">\</span><span class="mi">51</span><span class="o">+</span><span class="mh">0x1</span>
</span></span><span class="line"><span class="cl"><span class="mi">6014</span><span class="n">B795</span>	<span class="n">vTaskSwitchContext</span><span class="err">\</span><span class="mi">51</span><span class="o">+</span><span class="mh">0x1</span>
</span></span><span class="line"><span class="cl"><span class="mi">6014</span><span class="n">B795</span> 	<span class="n">vTaskSwitchContext</span><span class="err">\</span><span class="mi">51</span><span class="o">+</span><span class="mh">0x1</span>
</span></span><span class="line"><span class="cl"><span class="mi">6014</span><span class="n">B651</span>	<span class="n">xTaskResumeAll</span><span class="err">\</span><span class="mi">88</span><span class="o">+</span><span class="mh">0x1</span>
</span></span><span class="line"><span class="cl"><span class="mi">6014</span><span class="n">B6CF</span> 	<span class="n">prvIdleTask</span><span class="err">\</span><span class="mi">104</span><span class="o">+</span><span class="mh">0x5</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>有一个规律，EC200U的程序地址都是60XXX开头的，比如6014B6CF、6014B651、6014B795等，可以按照这个规律排查是否是程序地址。当然，如果你能确定内存布局，在栈的数据里判断是否是程序地址就不会出错了。</p>
<h2 id="总结">总结</h2>
<ol>
<li>从任务栈的结构的角度分析，便于理解栈溢出产生的原因和栈溢出导致的两类问题。</li>
<li>FreeRTOS提供两种软件栈溢出检测方式，两种方法各有优缺点，且不一定能够在任何时候都检测到所有类型的栈溢出。kernel 是在任务的上下文切换的时候进行栈溢出检查，通过软件检测栈溢出的方法，在发现问题上具有一定的“滞后性”。</li>
<li>在memory dump中分析栈问题有几个关键的信息点需要掌握：栈的生长方向、栈的起止地址、栈顶地址，栈的保护标记（栈空间初始化设置的固定值）以及通过栈空间的数据分析函数调用关系。</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.notion.so/FreeRTOS-stacks-and-stack-overflow-checking-df93402e952b4aa79b381d28693b2dc5">FreeRTOS - stacks and stack overflow checking</a></li>
<li><a href="https://github.com/FreeRTOS/FreeRTOS-Kernel">https://github.com/FreeRTOS/FreeRTOS-Kernel</a></li>
<li><a href="https://www.notion.so/Zephyr-cf671a64822a4bd7be4e200ba9240676">Zephyr使用的堆栈保护技术</a></li>
</ol>
]]></content:encoded>
    </item>
  </channel>
</rss>
