<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>WWTD's Blog</title>
    <link>https://blog.gitk.site/</link>
    <description>Welcome to my blog</description>
    <language>zh-CN</language>
    <copyright>All rights reserved 2026, wwtd</copyright>
    <lastBuildDate>Mon, 01 Jun 2026 13:27:13 GMT</lastBuildDate>
    <generator>Hexo</generator>
    <atom:link href="https://blog.gitk.site/rss2.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>阅读 《ARM Cortex-M3 与 Cortex-M4 权威指南》 笔记 -- Part1</title>
      <link>https://blog.gitk.site/2026/05/23/cortex_m3_m4_note_1/</link>
      <description>
        <![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>对于MCU也就是微控制器而言，Arm cortex M系列的MCU几乎是市场里的标杆和模范生了。为了更深入的理解cortex M3&#x2F]]>
      </description>
      <author>wwtd</author>
      <category domain="https://blog.gitk.site/tags/%E8%AF%BB%E4%B9%A6%E8%AE%B0%E5%BD%95/">读书记录</category>
      <category domain="https://blog.gitk.site/tags/Arm-Cortex-M/">Arm Cortex-M</category>
      <category domain="https://blog.gitk.site/tags/%E3%80%8AARM-Cortex-M3-%E4%B8%8E-Cortex-M4-%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97%E3%80%8B/">《ARM Cortex-M3 与 Cortex-M4 权威指南》</category>
      <pubDate>Sat, 23 May 2026 07:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>对于MCU也就是微控制器而言，Arm cortex M系列的MCU几乎是市场里的标杆和模范生了。为了更深入的理解cortex M3&#x2F;M4及这一类型的MCU，因此选择了《ARM Cortex-M3 与 Cortex-M4 权威指南》第三版，来做深入的学习。</p><p>这本书我在不同的渠道收到多次推荐，应该是业界非常有名的指导书了，希望能有满意的收获。</p><p>在学习的过程中，可能会借由实验来验证或者加深理解，实验会尽可能通过虚拟环境来做。</p><h2 id="关于cortex-M3、M4"><a href="#关于cortex-M3、M4" class="headerlink" title="关于cortex M3、M4"></a>关于cortex M3、M4</h2><p>Cortex M3、M4均由ARM公司设计，两者都是32位的微处理器，其中M3发布于2005、2006年，M4发布于2010年。两者的主要差异在于，M4处理器支持浮点运算并拥有更好的DSP性能。<br><a href="https://developer.arm.com/documentation">ARM 文档中心</a></p><h1 id="实验环境搭建"><a href="#实验环境搭建" class="headerlink" title="实验环境搭建"></a>实验环境搭建</h1><p>得益于前人的智慧，对于cortex M的交叉编译、模拟、调试都有比较成熟的方案了，我的实验平台是一个x86的debian小主机，会主要通过arm-none-eabi-gcc系列交叉编译；通过qemu做模拟；通过gdb-multiarch做调试。<br>使用工具的具体版本如下：</p><table><thead><tr><th>工具</th><th>版本</th><th>用途</th></tr></thead><tbody><tr><td><code>qemu-system-arm</code></td><td>7.2.22</td><td>Cortex-M3&#x2F;M4 机器模拟</td></tr><tr><td><code>arm-none-eabi-gcc</code></td><td>12.2.1</td><td>ARM 裸机交叉编译器</td></tr><tr><td><code>gdb-multiarch</code></td><td>13.1</td><td>多架构 GDB 调试器</td></tr><tr><td><code>libnewlib-arm-none-eabi</code></td><td>3.3.0</td><td>裸机 C 库</td></tr></tbody></table><p>而对于实验code的基础布局如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">projects/01-env/</span><br><span class="line">├── Makefile        # 构建系统</span><br><span class="line">├── linker.ld       # 链接脚本</span><br><span class="line">├── startup.c       # 向量表 + 启动代码</span><br><span class="line">├── main.c          # 测试程序</span><br><span class="line">└── debug.gdb       # GDB 调试脚本</span><br></pre></td></tr></table></figure><p>各自细节如：</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#Makefile</span></span><br><span class="line">CFLAGS  = -Wall -Wextra -g -O0 -ffreestanding -nostdlib -mthumb</span><br><span class="line">LDFLAGS = -Wl,-T,linker.ld -nostartfiles -nostdlib -lgcc</span><br><span class="line"></span><br><span class="line">SRCS    = startup.c main.c</span><br><span class="line"></span><br><span class="line"><span class="meta"><span class="keyword">.PHONY</span>: all clean m3 m4 qemu-m3 qemu-m4 gdb-m3 gdb-m4</span></span><br><span class="line"></span><br><span class="line"><span class="section">all: m3 m4</span></span><br><span class="line"></span><br><span class="line"><span class="section">m3: test-m3.elf</span></span><br><span class="line"><span class="section">m4: test-m4.elf</span></span><br><span class="line"></span><br><span class="line"><span class="section">test-m3.elf: <span class="variable">$(SRCS)</span> linker.ld</span></span><br><span class="line">        arm-none-eabi-gcc <span class="variable">$(CFLAGS)</span> -mcpu=cortex-m3 <span class="variable">$(LDFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$(SRCS)</span></span><br><span class="line"></span><br><span class="line"><span class="section">test-m4.elf: <span class="variable">$(SRCS)</span> linker.ld</span></span><br><span class="line">        arm-none-eabi-gcc <span class="variable">$(CFLAGS)</span> -mcpu=cortex-m4 -mfloat-abi=soft <span class="variable">$(LDFLAGS)</span> -o <span class="variable">$@</span> <span class="variable">$(SRCS)</span></span><br><span class="line"></span><br><span class="line"><span class="section">qemu-m3: test-m3.elf</span></span><br><span class="line">        @echo <span class="string">&quot;=== Cortex-M3 (lm3s6965evb) ===&quot;</span></span><br><span class="line">        qemu-system-arm -M lm3s6965evb -kernel test-m3.elf -nographic</span><br><span class="line"></span><br><span class="line"><span class="section">qemu-m4: test-m4.elf</span></span><br><span class="line">        @echo <span class="string">&quot;=== Cortex-M4 (mps2-an386) ===&quot;</span></span><br><span class="line">        qemu-system-arm -M mps2-an386 -kernel test-m4.elf -nographic</span><br><span class="line"></span><br><span class="line"><span class="section">qemu-gdb-m3: test-m3.elf</span></span><br><span class="line">        @echo <span class="string">&quot;=== QEMU waiting for GDB on :1234 (M3) ===&quot;</span></span><br><span class="line">        qemu-system-arm -M lm3s6965evb -kernel test-m3.elf -S -gdb tcp::1234 -nographic</span><br><span class="line"></span><br><span class="line"><span class="section">qemu-gdb-m4: test-m4.elf</span></span><br><span class="line">        @echo <span class="string">&quot;=== QEMU waiting for GDB on :1234 (M4) ===&quot;</span></span><br><span class="line">        qemu-system-arm -M mps2-an386 -kernel test-m4.elf -S -gdb tcp::1234 -nographic</span><br><span class="line"></span><br><span class="line"><span class="section">gdb:</span></span><br><span class="line">        gdb-multiarch -q -ex <span class="string">&quot;target remote :1234&quot;</span> test-m3.elf</span><br><span class="line"></span><br><span class="line"><span class="section">clean:</span></span><br><span class="line">        rm -f *.o *.elf *.map</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"># linker.ld</span><br><span class="line">ENTRY(Reset_Handler)</span><br><span class="line"></span><br><span class="line">MEMORY</span><br><span class="line">&#123;</span><br><span class="line">    FLASH (rx)  : ORIGIN = 0x00000000, LENGTH = 256K</span><br><span class="line">    SRAM  (rwx) : ORIGIN = 0x20000000, LENGTH = 64K</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">SECTIONS</span><br><span class="line">&#123;</span><br><span class="line">    .vectors : &#123;</span><br><span class="line">        KEEP(*(.vectors))</span><br><span class="line">    &#125; &gt; FLASH</span><br><span class="line"></span><br><span class="line">    .text : &#123;</span><br><span class="line">        . = ALIGN(4);</span><br><span class="line">        *(.text*)</span><br><span class="line">        *(.rodata*)</span><br><span class="line">        . = ALIGN(4);</span><br><span class="line">        _etext = .;</span><br><span class="line">    &#125; &gt; FLASH</span><br><span class="line"></span><br><span class="line">    .data : &#123;</span><br><span class="line">        _sdata = .;</span><br><span class="line">        *(.data*)</span><br><span class="line">        . = ALIGN(4);</span><br><span class="line">        _edata = .;</span><br><span class="line">    &#125; &gt; SRAM AT &gt; FLASH</span><br><span class="line">    _sidata = LOADADDR(.data);</span><br><span class="line"></span><br><span class="line">    .bss : &#123;</span><br><span class="line">        _sbss = .;</span><br><span class="line">        __bss_start = _sbss;</span><br><span class="line">        *(.bss*)</span><br><span class="line">        *(COMMON)</span><br><span class="line">        . = ALIGN(4);</span><br><span class="line">        _ebss = .;</span><br><span class="line">        __bss_end = _ebss;</span><br><span class="line">    &#125; &gt; SRAM</span><br><span class="line"></span><br><span class="line">    . = ALIGN(4);</span><br><span class="line">    _end = .;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// startup.c</span></span><br><span class="line"><span class="keyword">extern</span> <span class="type">unsigned</span> <span class="type">int</span> _sdata, _edata, _sbss, _ebss;</span><br><span class="line"><span class="keyword">extern</span> <span class="type">unsigned</span> <span class="type">int</span> _sidata;</span><br><span class="line"><span class="keyword">extern</span> <span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"></span><br><span class="line">__attribute__((naked)) <span class="type">void</span> <span class="title function_">Reset_Handler</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> *src, *dst;</span><br><span class="line"></span><br><span class="line">    src = &amp;_sidata;</span><br><span class="line">    dst = &amp;_sdata;</span><br><span class="line">    <span class="keyword">while</span> (dst &lt; &amp;_edata)</span><br><span class="line">        *dst++ = *src++;</span><br><span class="line"></span><br><span class="line">    dst = &amp;_sbss;</span><br><span class="line">    <span class="keyword">while</span> (dst &lt; &amp;_ebss)</span><br><span class="line">        *dst++ = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    main();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">Default_Handler</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">NMI_Handler</span><span class="params">(<span class="type">void</span>)</span>         __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">HardFault_Handler</span><span class="params">(<span class="type">void</span>)</span>   __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">MemManage_Handler</span><span class="params">(<span class="type">void</span>)</span>   __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">BusFault_Handler</span><span class="params">(<span class="type">void</span>)</span>    __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">UsageFault_Handler</span><span class="params">(<span class="type">void</span>)</span>  __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">SVC_Handler</span><span class="params">(<span class="type">void</span>)</span>         __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">DebugMon_Handler</span><span class="params">(<span class="type">void</span>)</span>    __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">PendSV_Handler</span><span class="params">(<span class="type">void</span>)</span>      __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">SysTick_Handler</span><span class="params">(<span class="type">void</span>)</span>     __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"></span><br><span class="line">__attribute__((used, section(<span class="string">&quot;.vectors&quot;</span>)))</span><br><span class="line"><span class="type">void</span> *vector_table[<span class="number">16</span>] = &#123;</span><br><span class="line">    [<span class="number">0</span>]  = (<span class="type">void</span> *)<span class="number">0x20010000</span>,</span><br><span class="line">    [<span class="number">1</span>]  = (<span class="type">void</span> *)Reset_Handler,</span><br><span class="line">    [<span class="number">2</span>]  = (<span class="type">void</span> *)NMI_Handler,</span><br><span class="line">    [<span class="number">3</span>]  = (<span class="type">void</span> *)HardFault_Handler,</span><br><span class="line">    [<span class="number">4</span>]  = (<span class="type">void</span> *)MemManage_Handler,</span><br><span class="line">    [<span class="number">5</span>]  = (<span class="type">void</span> *)BusFault_Handler,</span><br><span class="line">    [<span class="number">6</span>]  = (<span class="type">void</span> *)UsageFault_Handler,</span><br><span class="line">    [<span class="number">11</span>] = (<span class="type">void</span> *)SVC_Handler,</span><br><span class="line">    [<span class="number">12</span>] = (<span class="type">void</span> *)DebugMon_Handler,</span><br><span class="line">    [<span class="number">14</span>] = (<span class="type">void</span> *)PendSV_Handler,</span><br><span class="line">    [<span class="number">15</span>] = (<span class="type">void</span> *)SysTick_Handler,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//main.c</span></span><br><span class="line"><span class="keyword">volatile</span> <span class="type">unsigned</span> <span class="type">int</span> counter;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line">    counter = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>) &#123;</span><br><span class="line">        counter++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"># debug.gdb</span><br><span class="line"># 启动方式:</span><br><span class="line">#   terminal 1: make qemu-gdb-m3   (或 qemu-gdb-m4)</span><br><span class="line">#   terminal 2: gdb-multiarch -x debug.gdb</span><br><span class="line">#</span><br><span class="line"># 然后即可调试:</span><br><span class="line">#   (gdb) break main        # 设置断点</span><br><span class="line">#   (gdb) continue          # 运行到断点</span><br><span class="line">#   (gdb) stepi             # 单步执行</span><br><span class="line">#   (gdb) info registers    # 查看寄存器</span><br><span class="line">#   (gdb) print counter     # 查看变量</span><br><span class="line">#   (gdb) x/4xw 0x20000000  # 查看内存</span><br><span class="line"></span><br><span class="line">file test-m3.elf</span><br><span class="line">target remote :1234</span><br></pre></td></tr></table></figure><h2 id="Makefile-目标"><a href="#Makefile-目标" class="headerlink" title="Makefile 目标"></a>Makefile 目标</h2><table><thead><tr><th>目标</th><th>说明</th></tr></thead><tbody><tr><td><code>make m3</code></td><td>编译 Cortex-M3 ELF</td></tr><tr><td><code>make m4</code></td><td>编译 Cortex-M4 ELF</td></tr><tr><td><code>make qemu-m3</code></td><td>直接运行 M3 (无调试)</td></tr><tr><td><code>make qemu-m4</code></td><td>直接运行 M4 (无调试)</td></tr><tr><td><code>make qemu-gdb-m3</code></td><td>M3 + GDB 等待连接 (:1234)</td></tr><tr><td><code>make qemu-gdb-m4</code></td><td>M4 + GDB 等待连接 (:1234)</td></tr><tr><td><code>make gdb</code></td><td>连接 GDB 到 QEMU</td></tr></tbody></table><h2 id="QEMU-机器选型"><a href="#QEMU-机器选型" class="headerlink" title="QEMU 机器选型"></a>QEMU 机器选型</h2><ul><li><strong>Cortex-M3</strong>: <code>lm3s6965evb</code> (TI Stellaris LM3S6965)</li><li><strong>Cortex-M4</strong>: <code>mps2-an386</code> (ARM MPS2 + AN386 FPGA)</li></ul><p>两者均将代码映射到 <code>0x00000000</code>，SRAM 映射到 <code>0x20000000</code>，便于用同一套链接脚本。</p><ul><li>如果希望使用其他机器，可以使用如下指令列出支持列表<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">qemu-system-arm -M <span class="built_in">help</span></span><br></pre></td></tr></table></figure></li></ul><h2 id="实验环境验证"><a href="#实验环境验证" class="headerlink" title="实验环境验证"></a>实验环境验证</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># terminal 1: 启动 QEMU (等待 GDB)</span></span><br><span class="line">make qemu-gdb-m3</span><br><span class="line"><span class="comment"># terminal 2: 连接 GDB</span></span><br><span class="line">make gdb</span><br></pre></td></tr></table></figure><h1 id="实验1：从-Reset-到-main——Cortex-M3-启动全流程跟踪"><a href="#实验1：从-Reset-到-main——Cortex-M3-启动全流程跟踪" class="headerlink" title="实验1：从 Reset 到 main——Cortex-M3 启动全流程跟踪"></a>实验1：从 Reset 到 main——Cortex-M3 启动全流程跟踪</h1><p><strong>目标</strong>：结合反汇编与 GDB，完整跟踪 Cortex-M3 从上电复位到用户 main 函数执行的每一步，观察向量表布局、启动代码对 .bss 的初始化、函数调用时 LR 的变化，以及寄存器在循环中的活动。<br><strong>前提</strong>：已完成环境搭建，能正常编译和启动 QEMU+GDB。</p><h2 id="中断向量表与Reset-Handler"><a href="#中断向量表与Reset-Handler" class="headerlink" title="中断向量表与Reset_Handler"></a>中断向量表与Reset_Handler</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 编译M3的elf</span></span><br><span class="line">make m3</span><br><span class="line"><span class="comment"># 查看向量表</span></span><br><span class="line">arm-none-eabi-objdump -s -j .vectors test-m3.elf</span><br><span class="line">test-m3.elf:     file format elf32-littlearm</span><br><span class="line">Contents of section .vectors:</span><br><span class="line"> 0000 00000120 41000000 89000000 89000000  ... A...........</span><br><span class="line"> 0010 89000000 89000000 89000000 00000000  ................</span><br><span class="line"> 0020 00000000 00000000 00000000 89000000  ................</span><br><span class="line"> 0030 89000000 00000000 89000000 89000000  ................</span><br></pre></td></tr></table></figure><p>因为cortex-M3 是小端序的，所以实际这个表阅读方式是</p><table><thead><tr><th>偏移</th><th>值 (LE)</th><th>对应异常</th><th>说明</th></tr></thead><tbody><tr><td><code>0x0000</code></td><td><code>0x20010000</code></td><td>初始 MSP</td><td>栈顶地址</td></tr><tr><td><code>0x0004</code></td><td><code>0x00000041</code></td><td>Reset_Handler</td><td>地址 <code>0x40</code>，bit 0 &#x3D; 1 (Thumb)</td></tr><tr><td><code>0x0008</code></td><td><code>0x00000089</code></td><td>NMI_Handler</td><td>地址 <code>0x88</code> (&#x3D; Default_Handler)</td></tr><tr><td><code>0x000C</code></td><td><code>0x00000089</code></td><td>HardFault_Handler</td><td>同上，弱符号 alias</td></tr><tr><td><code>0x002C</code></td><td><code>0x00000089</code></td><td>SVC_Handler</td><td></td></tr><tr><td><code>0x0030</code></td><td><code>0x00000089</code></td><td>DebugMon_Handler</td><td></td></tr><tr><td><code>0x0038</code></td><td><code>0x00000089</code></td><td>PendSV_Handler</td><td></td></tr><tr><td><code>0x003C</code></td><td><code>0x00000089</code></td><td>SysTick_Handler</td><td></td></tr></tbody></table><p>这里就是和我们startup.c里设置基本是对应的</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">__attribute__((naked)) <span class="type">void</span> <span class="title function_">Reset_Handler</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">  something</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">Default_Handler</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">NMI_Handler</span><span class="params">(<span class="type">void</span>)</span>         __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">HardFault_Handler</span><span class="params">(<span class="type">void</span>)</span>   __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">MemManage_Handler</span><span class="params">(<span class="type">void</span>)</span>   __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">BusFault_Handler</span><span class="params">(<span class="type">void</span>)</span>    __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">UsageFault_Handler</span><span class="params">(<span class="type">void</span>)</span>  __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">SVC_Handler</span><span class="params">(<span class="type">void</span>)</span>         __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">DebugMon_Handler</span><span class="params">(<span class="type">void</span>)</span>    __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">PendSV_Handler</span><span class="params">(<span class="type">void</span>)</span>      __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">SysTick_Handler</span><span class="params">(<span class="type">void</span>)</span>     __<span class="title function_">attribute__</span><span class="params">((weak, alias(<span class="string">&quot;Default_Handler&quot;</span>)))</span>;</span><br><span class="line"></span><br><span class="line">__attribute__((used, section(<span class="string">&quot;.vectors&quot;</span>)))</span><br><span class="line"><span class="type">void</span> *vector_table[<span class="number">16</span>] = &#123;</span><br><span class="line">    [<span class="number">0</span>]  = (<span class="type">void</span> *)<span class="number">0x20010000</span>,</span><br><span class="line">    [<span class="number">1</span>]  = (<span class="type">void</span> *)Reset_Handler,</span><br><span class="line">    [<span class="number">2</span>]  = (<span class="type">void</span> *)NMI_Handler,</span><br><span class="line">    [<span class="number">3</span>]  = (<span class="type">void</span> *)HardFault_Handler,</span><br><span class="line">    [<span class="number">4</span>]  = (<span class="type">void</span> *)MemManage_Handler,</span><br><span class="line">    [<span class="number">5</span>]  = (<span class="type">void</span> *)BusFault_Handler,</span><br><span class="line">    [<span class="number">6</span>]  = (<span class="type">void</span> *)UsageFault_Handler,</span><br><span class="line">    [<span class="number">11</span>] = (<span class="type">void</span> *)SVC_Handler,</span><br><span class="line">    [<span class="number">12</span>] = (<span class="type">void</span> *)DebugMon_Handler,</span><br><span class="line">    [<span class="number">14</span>] = (<span class="type">void</span> *)PendSV_Handler,</span><br><span class="line">    [<span class="number">15</span>] = (<span class="type">void</span> *)SysTick_Handler,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>稍微有一点值得提出的是，这里的函数地址bit0都被置为1了，这是因为cortex-M要求Thumb模式，而在取出PC后，硬件是会自动清除bit0的。<br>关于Reset_Handler，startup.c里我们写的其实是：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">extern</span> <span class="type">unsigned</span> <span class="type">int</span> _sdata, _edata, _sbss, _ebss;</span><br><span class="line"><span class="keyword">extern</span> <span class="type">unsigned</span> <span class="type">int</span> _sidata;</span><br><span class="line"><span class="keyword">extern</span> <span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line">__attribute__((naked)) <span class="type">void</span> <span class="title function_">Reset_Handler</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> *src, *dst;</span><br><span class="line"></span><br><span class="line">    src = &amp;_sidata;</span><br><span class="line">    dst = &amp;_sdata;</span><br><span class="line">    <span class="keyword">while</span> (dst &lt; &amp;_edata)</span><br><span class="line">        *dst++ = *src++;</span><br><span class="line"></span><br><span class="line">    dst = &amp;_sbss;</span><br><span class="line">    <span class="keyword">while</span> (dst &lt; &amp;_ebss)</span><br><span class="line">        *dst++ = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    main();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>而我们通过反汇编，看一下实际编译生成的汇编代码其实是：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arm-none-eabi-objdump -d test-m3.elf | grep -A 40 <span class="string">&#x27;&lt;Reset_Handler&gt;&#x27;</span></span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">00000040 &lt;Reset_Handler&gt;:</span><br><span class="line">  40:   4d0c            ldr     r5, [pc, #48]   @ (74 &lt;Reset_Handler+0x34&gt;)</span><br><span class="line">  42:   4c0d            ldr     r4, [pc, #52]   @ (78 &lt;Reset_Handler+0x38&gt;)</span><br><span class="line">  44:   e005            b.n     52 &lt;Reset_Handler+0x12&gt;</span><br><span class="line">  46:   462a            mov     r2, r5</span><br><span class="line">  48:   1d15            adds    r5, r2, #4</span><br><span class="line">  4a:   4623            mov     r3, r4</span><br><span class="line">  4c:   1d1c            adds    r4, r3, #4</span><br><span class="line">  4e:   6812            ldr     r2, [r2, #0]</span><br><span class="line">  50:   601a            str     r2, [r3, #0]</span><br><span class="line">  52:   4b0a            ldr     r3, [pc, #40]   @ (7c &lt;Reset_Handler+0x3c&gt;)</span><br><span class="line">  54:   429c            cmp     r4, r3</span><br><span class="line">  56:   d3f6            bcc.n   46 &lt;Reset_Handler+0x6&gt;</span><br><span class="line">  58:   4c09            ldr     r4, [pc, #36]   @ (80 &lt;Reset_Handler+0x40&gt;)</span><br><span class="line">  5a:   e003            b.n     64 &lt;Reset_Handler+0x24&gt;</span><br><span class="line">  5c:   4623            mov     r3, r4</span><br><span class="line">  5e:   1d1c            adds    r4, r3, #4</span><br><span class="line">  60:   2200            movs    r2, #0</span><br><span class="line">  62:   601a            str     r2, [r3, #0]</span><br><span class="line">  64:   4b07            ldr     r3, [pc, #28]   @ (84 &lt;Reset_Handler+0x44&gt;)</span><br><span class="line">  66:   429c            cmp     r4, r3</span><br><span class="line">  68:   d3f8            bcc.n   5c &lt;Reset_Handler+0x1c&gt;</span><br><span class="line">  6a:   f000 f811       bl      90 &lt;main&gt;</span><br><span class="line">  6e:   bf00            nop</span><br><span class="line">  70:   e7fd            b.n     6e &lt;Reset_Handler+0x2e&gt;</span><br><span class="line">  72:   bf00            nop</span><br><span class="line">  74:   000000ac        .word   0x000000ac</span><br><span class="line">  78:   20000000        .word   0x20000000</span><br><span class="line">  7c:   20000000        .word   0x20000000</span><br><span class="line">  80:   20000000        .word   0x20000000</span><br><span class="line">  84:   20000004        .word   0x20000004</span><br><span class="line"></span><br><span class="line">00000088 &lt;Default_Handler&gt;:</span><br><span class="line">  88:   b480            push    &#123;r7&#125;</span><br><span class="line">  8a:   af00            add     r7, sp, #0</span><br><span class="line">  8c:   bf00            nop</span><br><span class="line">  8e:   e7fd            b.n     8c &lt;Default_Handler+0x4&gt;</span><br><span class="line"></span><br><span class="line">00000090 &lt;main&gt;:</span><br><span class="line">  90:   b480            push    &#123;r7&#125;</span><br><span class="line">  92:   af00            add     r7, sp, #0</span><br></pre></td></tr></table></figure><p>代码并不算长，我们尝试解读一下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">00000040 &lt;Reset_Handler&gt;:</span><br><span class="line">  40:   4d0c            ldr     r5, [pc, #48]   @ r5 = _sidata (0xac)</span><br><span class="line">  42:   4c0d            ldr     r4, [pc, #52]   @ r4 = _sdata  (0x20000000)</span><br><span class="line">  44:   e005            b.n     52              @ 跳转到条件检查</span><br><span class="line">  ; .data 复制循环入口:</span><br><span class="line">  46:   462a            mov     r2, r5</span><br><span class="line">  48:   1d15            adds    r5, r2, #4      @ src++</span><br><span class="line">  4a:   4623            mov     r3, r4</span><br><span class="line">  4c:   1d1c            adds    r4, r3, #4      @ dst++</span><br><span class="line">  4e:   6812            ldr     r2, [r2, #0]    @ r2 = *src</span><br><span class="line">  50:   601a            str     r2, [r3, #0]    @ *dst = r2</span><br><span class="line"></span><br><span class="line">  52:   4b0a            ldr     r3, [pc, #40]   @ r3 = _edata (0x20000000)</span><br><span class="line">  54:   429c            cmp     r4, r3</span><br><span class="line">  56:   d3f6            bcc.n   46              @ if dst &lt; _edata, 继续复制</span><br><span class="line">  ; .bss 清零循环入口:</span><br><span class="line">  58:   4c09            ldr     r4, [pc, #36]   @ r4 = _sbss (0x20000000)</span><br><span class="line">  5a:   e003            b.n     64              @ 跳转到条件检查</span><br><span class="line"></span><br><span class="line">  5c:   4623            mov     r3, r4          @ r3 = dst</span><br><span class="line">  5e:   1d1c            adds    r4, r3, #4      @ dst += 4</span><br><span class="line">  60:   2200            movs    r2, #0          @ r2 = 0</span><br><span class="line">  62:   601a            str     r2, [r3, #0]    @ *dst = 0</span><br><span class="line"></span><br><span class="line">  64:   4b07            ldr     r3, [pc, #28]   @ r3 = _ebss (0x20000004)</span><br><span class="line">  66:   429c            cmp     r4, r3</span><br><span class="line">  68:   d3f8            bcc.n   5c              @ if dst &lt; _ebss, 继续清零</span><br><span class="line"></span><br><span class="line">  6a:   f000 f811       bl      90              @ 调用 main</span><br><span class="line">  6e:   bf00            nop</span><br><span class="line">  70:   e7fd            b.n     6e              @ main 返回后死循环</span><br><span class="line">  72:   bf00            nop</span><br><span class="line">  74:   000000ac        .word   0x000000ac</span><br><span class="line">  78:   20000000        .word   0x20000000</span><br><span class="line">  7c:   20000000        .word   0x20000000</span><br><span class="line">  80:   20000000        .word   0x20000000</span><br><span class="line">  84:   20000004        .word   0x20000004</span><br><span class="line"></span><br><span class="line">00000088 &lt;Default_Handler&gt;:</span><br><span class="line">  88:   b480            push    &#123;r7&#125;</span><br><span class="line">  8a:   af00            add     r7, sp, #0</span><br><span class="line">  8c:   bf00            nop</span><br><span class="line">  8e:   e7fd            b.n     8c &lt;Default_Handler+0x4&gt;</span><br><span class="line"></span><br><span class="line">00000090 &lt;main&gt;:</span><br><span class="line">  90:   b480            push    &#123;r7&#125;</span><br><span class="line">  92:   af00            add     r7, sp, #0</span><br></pre></td></tr></table></figure><p>这里因为用到了链接脚本中的几个变量，这些变量的值也可以通过符号表二次确认：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">arm-none-eabi-objdump -t test-m3.elf | grep -E <span class="string">&quot;counter|_s[bd]|_e[bd]|_sidata&quot;</span></span><br><span class="line">000000ac g       *ABS*  00000000 _sidata</span><br><span class="line">20000000 g       .bss   00000000 _sbss</span><br><span class="line">20000000 g       .data  00000000 _sdata</span><br><span class="line">20000004 g       .bss   00000000 _ebss</span><br><span class="line">20000000 g     O .bss   00000004 counter</span><br><span class="line">20000000 g       .data  00000000 _edata</span><br></pre></td></tr></table></figure><p>能看出来，bss段唯一的一个4字节变量就是counter，所以Reset_Handler 也就是把它清零了。</p><h2 id="通过GDB观察启动过程"><a href="#通过GDB观察启动过程" class="headerlink" title="通过GDB观察启动过程"></a>通过GDB观察启动过程</h2><p>两个终端，分别执行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">make qemu-gdb-m3</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gdb-multiarch -q</span><br></pre></td></tr></table></figure><p>然后在弹出来的gdb交互式窗口分别执行：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">file test-m3.elf</span><br><span class="line">target remote :1234</span><br><span class="line">info registers</span><br></pre></td></tr></table></figure><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">gdb-multiarch -q</span><br><span class="line">(gdb) file test-m3.elf</span><br><span class="line">Reading symbols from test-m3.elf...</span><br><span class="line">(gdb) target remote :1234</span><br><span class="line">Remote debugging using :1234</span><br><span class="line">Reset_Handler () at startup.c:8</span><br><span class="line">8           src = &amp;_sidata;</span><br><span class="line">(gdb) info registers</span><br><span class="line">r0             0x0                 0</span><br><span class="line">r1             0x0                 0</span><br><span class="line">r2             0x0                 0</span><br><span class="line">r3             0x0                 0</span><br><span class="line">r4             0x0                 0</span><br><span class="line">r5             0x0                 0</span><br><span class="line">r6             0x0                 0</span><br><span class="line">r7             0x0                 0</span><br><span class="line">r8             0x0                 0</span><br><span class="line">r9             0x0                 0</span><br><span class="line">r10            0x0                 0</span><br><span class="line">r11            0x0                 0</span><br><span class="line">r12            0x0                 0</span><br><span class="line">sp             0x20010000          0x20010000</span><br><span class="line">lr             0xffffffff          -1</span><br><span class="line">pc             0x40                0x40 &lt;Reset_Handler&gt;</span><br><span class="line">xpsr           0x41000000          1090519040</span><br></pre></td></tr></table></figure><p>这里其实就能看到，PC在0x40表示Reset_Handler。SP在0x20010000，是我们设置的初始栈指针。LR指向0xffffffff，表示在Thread模式使用MSP。XPSR在0x41000000，其bit24为1表示运行在Thumb模式。<br>接下来，可以用内存dump工具来看向量表在内存的布局。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">(gdb) x/16xw 0x00000000</span><br><span class="line">0x0 &lt;vector_table&gt;:     0x20010000      0x00000041      0x00000089      0x00000089</span><br><span class="line">0x10 &lt;vector_table+16&gt;: 0x00000089      0x00000089      0x00000089      0x00000000</span><br><span class="line">0x20 &lt;vector_table+32&gt;: 0x00000000      0x00000000      0x00000000      0x00000089</span><br><span class="line">0x30 &lt;vector_table+48&gt;: 0x00000089      0x00000000      0x00000089      0x00000089</span><br></pre></td></tr></table></figure><p>和objdump的是完全一致的。</p><ul><li>建立自动显示，让每次stepi后自动刷新关键值<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">display/i $pc</span><br><span class="line">display/4xw 0x20000000</span><br><span class="line">display $lr</span><br><span class="line">display $r4</span><br></pre></td></tr></table></figure>接下来就可以stepi，一路单步执行下去<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">stepi</span><br><span class="line">stepi</span><br><span class="line">stepi</span><br><span class="line">stepi</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">(gdb) display/i $pc</span><br><span class="line">1: x/i $pc</span><br><span class="line">=&gt; 0x40 &lt;Reset_Handler&gt;:        ldr     r5, [pc, #48]   @ (0x74 &lt;Reset_Handler+52&gt;)</span><br><span class="line">(gdb) display/4xw 0x20000000</span><br><span class="line">2: x/4xw 0x20000000</span><br><span class="line">0x20000000 &lt;counter&gt;:   0x00000000      0x00000000      0x00000000      0x00000000</span><br><span class="line">(gdb) display $lr</span><br><span class="line">3: $lr = -1</span><br><span class="line">(gdb) display $r4</span><br><span class="line">4: $r4 = 0</span><br><span class="line">(gdb) stepi</span><br><span class="line">9           dst = &amp;_sdata;</span><br><span class="line">1: x/i $pc</span><br><span class="line">=&gt; 0x42 &lt;Reset_Handler+2&gt;:      ldr     r4, [pc, #52]   @ (0x78 &lt;Reset_Handler+56&gt;)</span><br><span class="line">2: x/4xw 0x20000000</span><br><span class="line">0x20000000 &lt;counter&gt;:   0x00000000      0x00000000      0x00000000      0x00000000</span><br><span class="line">3: $lr = -1</span><br><span class="line">4: $r4 = 0</span><br><span class="line">(gdb) stepi</span><br><span class="line">10          while (dst &lt; &amp;_edata)</span><br><span class="line">1: x/i $pc</span><br><span class="line">=&gt; 0x44 &lt;Reset_Handler+4&gt;:      b.n     0x52 &lt;Reset_Handler+18&gt;</span><br><span class="line">2: x/4xw 0x20000000</span><br><span class="line">0x20000000 &lt;counter&gt;:   0x00000000      0x00000000      0x00000000      0x00000000</span><br><span class="line">3: $lr = -1</span><br><span class="line">4: $r4 = 536870912</span><br><span class="line">(gdb) stepi</span><br><span class="line">10          while (dst &lt; &amp;_edata)</span><br><span class="line">1: x/i $pc</span><br><span class="line">=&gt; 0x52 &lt;Reset_Handler+18&gt;:     ldr     r3, [pc, #40]   @ (0x7c &lt;Reset_Handler+60&gt;)</span><br><span class="line">2: x/4xw 0x20000000</span><br><span class="line">0x20000000 &lt;counter&gt;:   0x00000000      0x00000000      0x00000000      0x00000000</span><br><span class="line">3: $lr = -1</span><br><span class="line">4: $r4 = 536870912</span><br><span class="line">(gdb) stepi</span><br><span class="line">0x00000054      10          while (dst &lt; &amp;_edata)</span><br><span class="line">1: x/i $pc</span><br><span class="line">=&gt; 0x54 &lt;Reset_Handler+20&gt;:     cmp     r4, r3</span><br><span class="line">2: x/4xw 0x20000000</span><br><span class="line">0x20000000 &lt;counter&gt;:   0x00000000      0x00000000      0x00000000      0x00000000</span><br><span class="line">3: $lr = -1</span><br><span class="line">4: $r4 = 536870912</span><br></pre></td></tr></table></figure>reset 芯片，然后给程序的关键地址打上断点，重新观察流程<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">(gdb) monitor system_reset</span><br><span class="line">(gdb) info registers</span><br><span class="line">r0             0x0                 0</span><br><span class="line">r1             0x0                 0</span><br><span class="line">r2             0x0                 0</span><br><span class="line">r3             0x0                 0</span><br><span class="line">r4             0x0                 0</span><br><span class="line">r5             0xac                172</span><br><span class="line">r6             0x0                 0</span><br><span class="line">r7             0x0                 0</span><br><span class="line">r8             0x0                 0</span><br><span class="line">r9             0x0                 0</span><br><span class="line">r10            0x0                 0</span><br><span class="line">r11            0x0                 0</span><br><span class="line">r12            0x0                 0</span><br><span class="line">sp             0x20010000          0x20010000</span><br><span class="line">lr             0xffffffff          -1</span><br><span class="line">pc             0x42                0x42 &lt;Reset_Handler+2&gt;</span><br><span class="line">xpsr           0x41000000          1090519040</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line">(gdb) break *0x58</span><br><span class="line">Breakpoint 1 at 0x58: file startup.c, line 13.</span><br><span class="line">(gdb) break *0x5c</span><br><span class="line">Breakpoint 2 at 0x5c: file startup.c, line 15.</span><br><span class="line">(gdb) break *0x62</span><br><span class="line">Breakpoint 3 at 0x62: file startup.c, line 15.</span><br><span class="line">(gdb) break *0x6a</span><br><span class="line">Breakpoint 4 at 0x6a: file startup.c, line 17.</span><br><span class="line">(gdb) break main</span><br><span class="line">Breakpoint 5 at 0x94: file main.c, line 4.</span><br><span class="line">(gdb) continue</span><br><span class="line">Continuing.</span><br><span class="line"></span><br><span class="line">Breakpoint 1, Reset_Handler () at startup.c:13</span><br><span class="line">13          dst = &amp;_sbss;</span><br><span class="line">1: x/i $pc</span><br><span class="line">=&gt; 0x58 &lt;Reset_Handler+24&gt;:     ldr     r4, [pc, #36]   @ (0x80 &lt;Reset_Handler+64&gt;)</span><br><span class="line">2: x/4xw 0x20000000</span><br><span class="line">0x20000000 &lt;counter&gt;:   0x00000000      0x00000000      0x00000000      0x00000000</span><br><span class="line">3: $lr = -1</span><br><span class="line">4: $r4 = 536870912</span><br><span class="line">(gdb) continue</span><br><span class="line">Continuing.</span><br><span class="line"></span><br><span class="line">Breakpoint 2, Reset_Handler () at startup.c:15</span><br><span class="line">15              *dst++ = 0;</span><br><span class="line">1: x/i $pc</span><br><span class="line">=&gt; 0x5c &lt;Reset_Handler+28&gt;:     mov     r3, r4</span><br><span class="line">2: x/4xw 0x20000000</span><br><span class="line">0x20000000 &lt;counter&gt;:   0x00000000      0x00000000      0x00000000      0x00000000</span><br><span class="line">3: $lr = -1</span><br><span class="line">4: $r4 = 536870912</span><br><span class="line">(gdb) continue</span><br><span class="line">Continuing.</span><br><span class="line"></span><br><span class="line">Breakpoint 3, 0x00000062 in Reset_Handler () at startup.c:15</span><br><span class="line">15              *dst++ = 0;</span><br><span class="line">1: x/i $pc</span><br><span class="line">=&gt; 0x62 &lt;Reset_Handler+34&gt;:     str     r2, [r3, #0]</span><br><span class="line">2: x/4xw 0x20000000</span><br><span class="line">0x20000000 &lt;counter&gt;:   0x00000000      0x00000000      0x00000000      0x00000000</span><br><span class="line">3: $lr = -1</span><br><span class="line">4: $r4 = 536870916</span><br><span class="line">(gdb) continue</span><br><span class="line">Continuing.</span><br><span class="line"></span><br><span class="line">Breakpoint 4, Reset_Handler () at startup.c:17</span><br><span class="line">17          main();</span><br><span class="line">1: x/i $pc</span><br><span class="line">=&gt; 0x6a &lt;Reset_Handler+42&gt;:     bl      0x90 &lt;main&gt;</span><br><span class="line">2: x/4xw 0x20000000</span><br><span class="line">0x20000000 &lt;counter&gt;:   0x00000000      0x00000000      0x00000000      0x00000000</span><br><span class="line">3: $lr = -1</span><br><span class="line">4: $r4 = 536870916</span><br><span class="line">(gdb) continue</span><br><span class="line">Continuing.</span><br><span class="line"></span><br><span class="line">Breakpoint 5, main () at main.c:4</span><br><span class="line">4           counter = 0;</span><br><span class="line">1: x/i $pc</span><br><span class="line">=&gt; 0x94 &lt;main+4&gt;:       ldr     r3, [pc, #16]   @ (0xa8 &lt;main+24&gt;)</span><br><span class="line">2: x/4xw 0x20000000</span><br><span class="line">0x20000000 &lt;counter&gt;:   0x00000000      0x00000000      0x00000000      0x00000000</span><br><span class="line">3: $lr = 111</span><br><span class="line">4: $r4 = 536870916</span><br></pre></td></tr></table></figure></li></ul><p>在这样的流程里，基本上观察到了启动过程里cpu的行为。</p><h1 id="To-Be-Continued"><a href="#To-Be-Continued" class="headerlink" title="To Be Continued"></a>To Be Continued</h1><p>要学习和记录的内容比较多，先来一篇博文记录最开始的内容，后续会随着阅读的深入对各重点内容加以记录与实验。<br>感谢阅读~</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>阅读 AAPCS 笔记 —— 从寄存器到函数调用的 Arm 规范</title>
      <link>https://blog.gitk.site/2026/04/19/aapcs-guide/</link>
      <description>
        <![CDATA[<h1 id="AAPCS简介"><a href="#AAPCS简介" class="headerlink" title="AAPCS简介"></a>AAPCS简介</h1><p>AAPCS（Procedure Call Standard for Arm Architecture]]>
      </description>
      <author>wwtd</author>
      <category domain="https://blog.gitk.site/tags/AAPCS/">AAPCS</category>
      <category domain="https://blog.gitk.site/tags/%E8%AF%BB%E4%B9%A6%E8%AE%B0%E5%BD%95/">读书记录</category>
      <pubDate>Sun, 19 Apr 2026 12:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="AAPCS简介"><a href="#AAPCS简介" class="headerlink" title="AAPCS简介"></a>AAPCS简介</h1><p>AAPCS（Procedure Call Standard for Arm Architecture）,Arm架构下的调用标准，用于描述汇编语言与C代码交互时的行为规范。这里提到的PCS，本质是Arm 架构 ABI（Application Binary Interface）的核心组成。如果目光仅放到Arm-M 系列MCU搭配C语言的开发的话，PCS几乎就是ABI的全部内容。<br><a href="https://github.com/Arm-software/abi-aa/releases">官方发布页</a><br>当前最新的<a href="https://github.com/Arm-software/abi-aa/releases/download/2025Q4/aapcs32.pdf">aapcs32</a></p><h2 id="延伸内容"><a href="#延伸内容" class="headerlink" title="延伸内容"></a>延伸内容</h2><p>一般来说，每个架构都有自己的PCS。有的时候一个架构可能会有多种调用约定，比如x86 (32位)曾经有cdecl、stdcall、fastcall、pascal 等多种调用约定；而x86-64 (64位)也有System V ABI（Linux、macOS）和Microsoft x64 Calling Convention（Windows）。由于Arm公司的强势与统一，Arm早早定义了全套标准的AAPCS，因此这对开发者更加友好。<br>C++的ABI兼容是另一个问题，需要单独讨论了。</p><h1 id="AAPCS内容"><a href="#AAPCS内容" class="headerlink" title="AAPCS内容"></a>AAPCS内容</h1><p>以2025Q4的aapcs32 为例，先看目录，大致看看里面都包含了什么。</p><details><summary>Contents</summary><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line">Contents</span><br><span class="line">1 Preamble 2</span><br><span class="line">1.1 Abstract 2</span><br><span class="line">1.2 Keywords 2</span><br><span class="line">1.3 Latest release and defects report 2</span><br><span class="line">1.4 Licence 3</span><br><span class="line">1.5 About the license 3</span><br><span class="line">1.6 Contributions 3</span><br><span class="line">1.7 Trademark notice 3</span><br><span class="line">1.8 Copyright 3</span><br><span class="line">2 About This Document 6</span><br><span class="line">2.1 Change Control 6</span><br><span class="line">2.1.1 Current Status and Anticipated Changes 6</span><br><span class="line">2.1.2 Change History 6</span><br><span class="line">2.2 References 8</span><br><span class="line">2.3 Terms and Abbreviations 8</span><br><span class="line">2.4 Acknowledgements 10</span><br><span class="line">3 Scope 11</span><br><span class="line">4 Introduction 12</span><br><span class="line">4.1 Design Goals 12</span><br><span class="line">4.2 Conformance 12</span><br><span class="line">5 Data Types and Alignment 13</span><br><span class="line">5.1 Fundamental Data Types 13</span><br><span class="line">5.1.1 Half-precision Floating Point 13</span><br><span class="line">5.1.2 Containerized Vectors 14</span><br><span class="line">5.2 Endianness and Byte Ordering 14</span><br><span class="line">5.3 Composite Types 15</span><br><span class="line">5.3.1 Aggregates 15</span><br><span class="line">5.3.2 Unions 15</span><br><span class="line">5.3.3 Arrays 15</span><br><span class="line">5.3.4 Bit-fields 15</span><br><span class="line">5.3.5 Homogeneous Aggregates 16</span><br><span class="line">6 The Base Procedure Call Standard 17</span><br><span class="line">6.1 Machine Registers 17</span><br><span class="line">6.1.1 Core registers 17</span><br><span class="line">6.1.2 Co-processor Registers 19</span><br><span class="line">6.2 Processes, Memory and the Stack 19</span><br><span class="line">6.2.1 The Stack 20</span><br><span class="line">6.3 Subroutine Calls 22</span><br><span class="line">6.3.1 Use of IP by the linker 22</span><br><span class="line">6.4 Result Return 23</span><br><span class="line">6.5 Parameter Passing 23</span><br><span class="line">6.6 Interworking 25</span><br><span class="line">7 The Standard Variants 27</span><br><span class="line">7.1 VFP and SIMD vector Register Arguments 27</span><br><span class="line">7.1.1 Mapping between registers and memory format 27</span><br><span class="line">7.1.2 Procedure Calling 27</span><br><span class="line">7.2 Arm Alternative Format Half-precision Floating Point values 28</span><br><span class="line">7.3 Read-Write Position Independence (RWPI) 28</span><br><span class="line">7.4 Variant Compatibility 28</span><br><span class="line">7.4.1 VFP and Base Standard Compatibility 28</span><br><span class="line">7.4.2 RWPI and Base Standard Compatibility 29</span><br><span class="line">7.4.3 VFP and RWPI Standard Compatibility 29</span><br><span class="line">7.4.4 Half-precision Format Compatibility 29</span><br><span class="line">8 Arm C and C++ Language Mappings 30</span><br><span class="line">8.1 Data Types 30</span><br><span class="line">8.1.1 Arithmetic Types 30</span><br><span class="line">8.1.2 Pointer Types 32</span><br><span class="line">8.1.3 Enumerated Types 32</span><br><span class="line">8.1.4 Additional Types 33</span><br><span class="line">8.1.5 Volatile Data Types 33</span><br><span class="line">8.1.6 Structure, Union and Class Layout 33</span><br><span class="line">8.1.7 Bit-fields 33</span><br><span class="line">8.2 Argument Passing Conventions 37</span><br><span class="line">9 APPENDIX: Support for Advanced SIMD Extensions and MVE 38</span><br><span class="line">9.1 Introduction 38</span><br><span class="line">9.2 SIMD vector data types 38</span><br><span class="line">9.2.1 C++ Mangling 40</span><br></pre></td></tr></table></figure></details>跳过不感兴趣的部分，不难发现：- 第5章主要描述了数据类型与字节序之类的东西。- 第6章则主要描述了调用约定，寄存器、参数和返回值之类的。- 第7章则是一些浮点数与SIMD的东西。- 第8章是描述C/C++ 语言是怎么映射到底层的手册。<h2 id="数据类型与对齐"><a href="#数据类型与对齐" class="headerlink" title="数据类型与对齐"></a>数据类型与对齐</h2><h3 id="基础数据类型"><a href="#基础数据类型" class="headerlink" title="基础数据类型"></a>基础数据类型</h3><p>关于基础数据类型，主要的内容就是</p><img src="/2026/04/19/aapcs-guide/Fundamental_Data_Types.png" class=""><p>像整形、浮点型、指针这些都算比较常见的概念了，除此之外文档下面还单独提了一嘴半精度浮点数和容器化向量。<br>Arm架构支持三种半精度浮点数，IEEE754-2008、Arm替代格式、脑浮点数。前两种格式是互斥的，AAPCS的基础格式是IEEE754-2008，然后在调用过程中Arm替代格式也是允许的。<br>容器化向量的部分有点晦涩，跳过了。</p><h3 id="字节序与字节顺序"><a href="#字节序与字节顺序" class="headerlink" title="字节序与字节顺序"></a>字节序与字节顺序</h3><p>这部分描述是这样的</p><img src="/2026/04/19/aapcs-guide/Endianness_and_Byte_Ordering.png" class=""><p>本质就是系统寻址的最小单元是字节，但是数据单元可能是由多个字节组成的，这样在表达的时候就会需要规则来约束一下，多个字节组成的数据哪边是高位哪边是低位。</p><h3 id="复合数据类型"><a href="#复合数据类型" class="headerlink" title="复合数据类型"></a>复合数据类型</h3><p>复合数据类型是一个或者多个基本数据类型组成的集合，而在过程调用中复合数据类型是被认为一个整体。复合类型可能包括：Aggregates（类似struct，成员在内存中顺序排列）、union（成员在内存中重叠排列）、array（成员在内存中重复排列）。<br>并且，这些定义是递归的；也就是说，每种类型都可以包含一个复合类型作为其成员。</p><ul><li>对于复合变量成员的成员对齐，是指应用了任意语言修饰符之后的对齐方式。</li><li>对于复合变量成员的自然对齐，是指顶层成员的对齐方式的最大值，也就是对齐调整之前的值。</li></ul><h4 id="Aggregates"><a href="#Aggregates" class="headerlink" title="Aggregates"></a>Aggregates</h4><p>对于Aggregates，对齐方式应该为对齐度最高的组件的对齐方式。Aggregates实际的大小应该是其对齐方式的最小倍数，该倍数足以容纳其所有成员，前提是这些成员按照这些规则进行布局</p><h4 id="Unions"><a href="#Unions" class="headerlink" title="Unions"></a>Unions</h4><p>Unions的对齐方式应为其对齐程度最高的成员的对齐方式。联合体的大小应为其对齐方式的最小倍数，该倍数足以容纳其最大的成员。</p><h4 id="Arrays"><a href="#Arrays" class="headerlink" title="Arrays"></a>Arrays</h4><p>Arrays的对齐方式应为其基类型的对齐方式。数组的大小应为其基类型的大小乘以数组中元素的数量。</p><h4 id="Bit-fields"><a href="#Bit-fields" class="headerlink" title="Bit-fields"></a>Bit-fields</h4><p>聚合体中作为基本数据类型的成员可以细分为位域；如果此类成员存在未使用的部分，且足以使后续成员以其自然对齐方式开始，则后续成员可以使用未分配的部分。为了计算聚合的对齐方式，成员的类型应为位域所基于的基本数据类型。聚合中位域的布局由相应的语言绑定定义</p><h4 id="同构聚合"><a href="#同构聚合" class="headerlink" title="同构聚合"></a>同构聚合</h4><p>同构聚合是一种复合类型，其中构成该类型的所有基本数据类型都相同。同构性测试在数据布局完成后进行，并且不考虑访问控制或其他源语言限制。<br>如果由容器化向量类型组成的聚合的所有成员大小相同，即使容器化成员的内部格式不同，该聚合也被视为同构的。例如，一个包含 8 字节向量和一个 4 个半字向量的结构满足同构聚合的要求。<br>同构聚合具有一个基本类型，它是每个元素的基本数据类型。总大小是基本类型的大小乘以元素数量；其对齐方式与基本类型的对齐方式相同。</p><h4 id="comment"><a href="#comment" class="headerlink" title="comment"></a>comment</h4><p>这个部分其实是关于数据和内存的部分，倒是没有发现什么特别需要注意的内容。</p><h2 id="基础过程调用标准"><a href="#基础过程调用标准" class="headerlink" title="基础过程调用标准"></a>基础过程调用标准</h2><h3 id="寄存器"><a href="#寄存器" class="headerlink" title="寄存器"></a>寄存器</h3><p>寄存器又分为核心寄存器与协处理寄存器。</p><h4 id="核心寄存器"><a href="#核心寄存器" class="headerlink" title="核心寄存器"></a>核心寄存器</h4><p>原始的说法是这样的</p><img src="/2026/04/19/aapcs-guide/Core_Registers.png" class=""><p>有16个32位寄存器r0 - r15，这些寄存器对于汇编是不区分大小写的，但是特定角色的寄存器使用大写的。除了这16个，还有一个状态寄存器CPSR。<br>对于纯的ASM代码来说，直接操作寄存器就可以，比如像R0-R8 可能地位都是平等的，没有什么特别的。但是一旦涉及到C与ASM的混合编程，就需要一些约定来实现互相的约定与识别。怎么来互联互通，那就是这个core register的用法，其实这里也是我找到这篇文档来阅读的动机了。<br>逐个过一下：</p><ul><li>R15（PC）：这个是非常核心的寄存器了，它指示了当前程序运行到哪个指令。</li><li>R14（LR）：当前调用结束时，应该返回到哪里继续执行。</li><li>R13（SP）：栈指针，指示当前栈的使用状态</li><li>R12（IP）：过程内调用临时寄存器，用于远距离寻址与编译器临时使用。</li><li>R4-R11 ：寄存器，调用后需要恢复原样（R4-R8, R10, R11（v1-v5, v7-v8）：调用后必须保持原样。R9 为 platform-specific，当被定义为 v6 时才需保存）</li><li>R0-R3 ： 寄存器，传递参数与返回值</li></ul><p>对于一个典型的调用过程，存在调用方caller 与 被调用方callee。</p><ul><li>caller 在调用 callee 之前，会将callee 所需要的参数放置到R0-R3（如果参数超过了R0到R3的范围比如传8个参数，那前四个放R0到R3，后面的放栈里），然后LR 填caller 自己（BL的下一条指令），PC 填 callee（类似 BL callee）这样程序的控制权就转移给callee了。</li><li>callee进入后，它可以按照约定的方式获取到自己所需的参数（R0-R3、栈），然后开展自己所需要的计算或者业务逻辑。如果在callee的业务中需要使用额外的寄存器，callee可以使用其他的寄存器，但是需要保证（R4-R11，SP）的数据被记录下来，在返回caller的时候，要保持原样。所以我们看汇编源码的时候，很多的汇编函数进来就把（R4-R11，SP）push到栈里，然后结束逻辑之后pop回原位就是这个道理。</li><li>callee在做好现场保存后可以自由的使用R0-R11，SP来完成自己的业务，当运算结束现场也恢复后，通常是要给一个返回值的，而这个返回值一般是放到R0、R1里的。和参数传递类似，返回值传递也会有返回值超过R0、R1的场景，比如说返回值就是一个很大的数据结构，这种情况下R0、R1放不下，SP要保持现场没法往stack里塞，实际是怎么实施的呢？实际中这样的情况是由caller在调用callee之前分配出一块内存，然后将地址放到R0里，callee执行完将result填充一下就可以了。</li><li>callee完成所有的一切，结束生命周期时，只要设置一下PC &#x3D; LR，那程序控制权又转给caller了</li></ul><p>从典型流程里不难看出，16个寄存器基本上都很忙碌，大家各司其职数据进进出出。但是IP好像和别人都不一样，它不涉及调用的参数传递、返回值传递，也不像PC、LR、SP有单独的职责，那IP是干啥的呢？<br>IP 的职责有几种：</p><ul><li>远距离调用，Arm 的BL指令是有范围的，而超出范围的远距离寻址要依靠寄存器间接寻址。而其它寄存器各司其职都是没法用的，这个时候就可以用IP。</li><li>有的编译器会在函数入口做栈溢出检查，比如说，我知道callee中间会用100K stack，那我函数进来的时候就可以比较一下，SP - 100K &lt; stack_limit ? ,这样不就可以知道是不是栈溢出了吗。那这个时候又出现了，其他寄存器各司其职，只有IP无牵无挂，那就用IP来。</li><li>总的来说就是，IP是没有职责的草稿纸，谁想用就用，但是也没有任何保证。</li></ul><p>另外值得一提的是，R9 的定义是platform-specific的，只有被定为 v6 时才需保存。R4-R8、R10、R11、SP则是无条件保存。</p><p>CPSR也有一些自己的规则，这里跳过了。</p><p>上面提到的，如果参数或者返回值大于标准流程寄存器所表示的范围，可能会使用栈来传递参数，那么一个64 位的基础数据单元，算不算超过范围呢。<br>按照AAPCS的说法，对于超过32位的基础数据单元，如果是64位的也就是双字，可以使用两个连续寄存器，比如R0、R1或者R2、R3来传递，可以用一条单独的LDM从内存里取到数据。<br>对于128位的基础数据单元，也就是containerized vector，可以用4个连续的寄存器来传递，也是一条单独的LDM从内存load。</p><h4 id="协处理器寄存器"><a href="#协处理器寄存器" class="headerlink" title="协处理器寄存器"></a>协处理器寄存器</h4><p>VFP-v2协处理器有32个单精度寄存器，s0-s31，也可以作为16个双精度寄存器d0-d15访问（其中d0与s0、s1重叠；d1与s2、s3重叠；依此类推）。而其他的实现可能会增加更多的寄存器。比如VFP-v3 增加了16个双精度寄存器d16-d31，但是没有额外的单精度寄存器对应。<br>高级SIMD扩展和M型向量扩展 (MVE) 使用VFP寄存器集。高级SIMD扩展使用双精度寄存器表示64位向量，并进一步定义四字寄存器（q0与d0、d1重叠；q1与d2、d3重叠；依此类推）用于128位向量。MVE在相同的四字寄存器中使用128位向量。寄存器s16-s31（d8-d15, q4-q7）必须在子程序调用之间保持不变；寄存器s0-s15（d0-d7, q0-q3）无需保持不变（并且可以用于传递参数或在标准过程调用变体中返回结果）。寄存器 d16-d31（q8-q15），如果存在，也无需保持不变。FPSCR和VPR寄存器是唯一可能被符合规范的代码访问的状态寄存器。FPSCR和VPR又有各自的特点，但是跳过这里了。</p><h3 id="过程、内存与栈"><a href="#过程、内存与栈" class="headerlink" title="过程、内存与栈"></a>过程、内存与栈</h3><p>AAPCS适用于单个执行线程或进程（以下称为进程）。进程具有由底层机器寄存器和其可访问内存内容定义的程序状态。进程在执行过程中可访问的内存（而不会导致运行时错误）可能会有所变化。<br>一般，进程可以访问五种内存：</p><ul><li>代码段，可读但不需要可写</li><li>只读静态段，只读</li><li>读写静态段，读写</li><li>堆</li><li>栈</li></ul><p>读写静态段，又可被细分为已初始化、零初始化、未初始化。除了栈之外，其他的段都不要求内存单一连续。一个进程必须始终拥有一些代码和一个栈，但是其他类型的内存并非强制的。堆用于分配动态内存，而程序只应该执行代码段中的指令。</p><h4 id="栈"><a href="#栈" class="headerlink" title="栈"></a>栈</h4><p>stack是一个连续的内存区域，可以用来存储局部变量以及做调用的参数传递。<strong>stack是地址递减的</strong>，当前使用状态由SP（r13）来表示。通常来说，base 与 limit是用来描述stack的范围，一些时候limit是固定的，也有时候limit可以被动态调整。堆栈的维护规则包括通用规则与公共接口特定规则。<br>通用规则：</p><ul><li><strong>Stack-limit ≤ SP ≤ stack-base</strong></li><li><strong>SP mod 4 &#x3D; 0</strong></li><li><strong>进程只在[SP, stack base - 1] 范围存储数据</strong></li></ul><p>类似</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ldmxx reg, &#123;..., sp, ...&#125; // reg != sp</span><br></pre></td></tr></table></figure><p>在遇到中断的时候，就是有可能违反第三条原则的。比如说SP被更改后中断进来，中断进来第一件事就是压栈保存现场，但是这个时候SP就是飞的，压栈就有可能压到别的位置。<br>公共接口规则：</p><ul><li><strong>SP mod 8 &#x3D; 0</strong><br>所谓公共接口规则，就是在模块间边界。比如一个.o 暴露给外部可调用的符号。函数内部调用（inline、static）不算。为了更好的兼容性，公共接口要求更严格的对齐。任何时候SP都应该是4字节对齐的，也就是说栈只能4个字节4个字节的用。但是当进程调用的时候，需要把SP做成8字节对齐的。<br>实操的时候，尽量把一切按照严格的方法来。比如说，如果你要自己维护一个内存池，那别人从你这里分配内存的时候，你返回的就不能是按照4字节对齐来。<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Foo</span> &#123;</span></span><br><span class="line">    <span class="type">double</span> a;   <span class="comment">// 需要 8 字节对齐</span></span><br><span class="line">    <span class="type">int</span> b;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Foo</span> *<span class="title">f</span> =</span> my_malloc(<span class="keyword">sizeof</span>(<span class="keyword">struct</span> Foo));</span><br><span class="line">f-&gt;a = <span class="number">3.14</span>;</span><br><span class="line"><span class="comment">// 如果 my_malloc 返回 0x1004，a 的 offset = 0，但 0x1004 % 8 != 0 -&gt; fault。因为f-&gt;a 可能用了STRD，而STRD无条件要求地址与传输大小对齐。</span></span><br></pre></td></tr></table></figure>关于栈探测，是为了防止悄悄爆栈所以在申请大的stack空间时，先逐页读一下，比如说应用申请1M的stack，那就拆成4K的步长，依次步进读一个字节，这样就可以避免静默的溢出。<br>关于FP，也就是Frame Pointer，是用于调试和回溯的工具。当你想知道当前的程序是怎么一层层调进来的时候，FP可以帮助到你。每层调用在栈上放一个Frame Record（2个word），分别保存LR与前一个FP，这样就形成了一个调用链，而当前一个FP为0，调用链结束。</li></ul><h3 id="子调用"><a href="#子调用" class="headerlink" title="子调用"></a>子调用</h3><p>Arm 和 Thumb指令集都有一个BL指令，它就是将当前的BL的下一条指令放进LR，将callee放进PC。如果BL指令是Thumb的，那LR的首位将被置为1，Arm则为0.</p><h4 id="链接器使用IP"><a href="#链接器使用IP" class="headerlink" title="链接器使用IP"></a>链接器使用IP</h4><p>Arm 和 Thumb的BL均无法实现完整的32位寻址，因此需要linker在caller 和 callee之间插入一个veneer。而插入的veneer需要保留除IP（r12）和条件码之外的所有寄存器内容。</p><h3 id="返回值"><a href="#返回值" class="headerlink" title="返回值"></a>返回值</h3><p>返回值返回的方式取决于返回值的类型。</p><ul><li>半精度浮点返回值（16位），会被返回到r0的LSB</li><li>小于4字节的基础数据类型会被返回到R0（零扩展或者符号扩展到一个字）</li><li>刚好4字节的基础数据类型会被返回到R0</li><li>双字也就是8字节的基础数据类型会被返回到R0与R1</li><li>128位也就是16字节的基础数据类型会被返回到R0到R3</li><li>不超过4字节的复合数据类型会返回到R0，其格式如该结果存储在字对齐的地址然后通过LDR读取到R0一致。R0中其它位的值为未指定的</li><li>大于4字节的复合数据类型，或者其大小无法由caller、callee静态确定的类型，将在内存中做返回值传递。该内存作为一个额外参数在调用时传递，并由callee在调用过程中修改。</li></ul><h3 id="参数传递"><a href="#参数传递" class="headerlink" title="参数传递"></a>参数传递</h3><p>基本标准规定了在核心寄存器（r0-r3）和堆栈上传递参数。对于只接受少量参数的子程序，仅使用寄存器，从而大大减少调用开销。<br>参数传递被定义为一个两级的概念模型：</p><ul><li>将源语言的参数映射到机器类型</li><li>将机器类型整理成最终的参数列表<br>从源语言到机器类型的映射对于每种语言都是固定的，并且是单独描述的。最终的结果将会是一个有序的参数列表。<br>后面有一些非常细节的参数处理过程，跳过了。</li></ul><h4 id="comment-1"><a href="#comment-1" class="headerlink" title="comment"></a>comment</h4><p>这一章节其实是比较核心的内容，对典型场景的行为模式做了规范。</p><h1 id="Ending"><a href="#Ending" class="headerlink" title="Ending"></a>Ending</h1><p>AAPCS 整体上还是比较底层视角的描述，有很多的细节是应用开发者所无需了解的。但是，尤其是在深度的问题排查时，来自AAPCS的一些说明可以帮助我们更好的理解汇编源码。小册子本身也不长，读完至少能看懂反汇编里的出入栈、参数传递、返回值的处理。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>网站收藏夹</title>
      <link>https://blog.gitk.site/2026/03/26/bookmark/</link>
      <description>
        <![CDATA[<p>网上冲浪多了，经常看到很好的网站和博客，有时候欣赏一会就放进浏览器收藏夹吃灰了。转念一想，不如找个地方挂出来晒一晒，利己利他。</p>
<h1 id="ruanx-net"><a href="#ruanx-net" class="headerlink" title="rua]]>
      </description>
      <author>wwtd</author>
      <category domain="https://blog.gitk.site/tags/bookmark/">bookmark</category>
      <pubDate>Thu, 26 Mar 2026 12:50:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p>网上冲浪多了，经常看到很好的网站和博客，有时候欣赏一会就放进浏览器收藏夹吃灰了。转念一想，不如找个地方挂出来晒一晒，利己利他。</p><h1 id="ruanx-net"><a href="#ruanx-net" class="headerlink" title="ruanx.net"></a><a href="https://ruanx.net/">ruanx.net</a></h1><p>这里是一个很好的博客,最开始是搜索嵌入式相关的内容发现的。后来发现博主的其他领域内容也很好，读了下都很受益。博主可能比我还小两岁，汗颜哇。</p><h1 id="lujji-github-io-blog"><a href="#lujji-github-io-blog" class="headerlink" title="lujji.github.io&#x2F;blog&#x2F;"></a><a href="https://lujji.github.io/blog/">lujji.github.io&#x2F;blog&#x2F;</a></h1><p>这里是一个嵌入式相关的博客，我是被stm8相关bare mental编程的内容吸引过来的，博主的工作做的很扎实，偶尔贴的图也工工整整、赏心悦目。就是停更许久了。</p><h1 id="zhailog-com"><a href="#zhailog-com" class="headerlink" title="zhailog.com&#x2F;"></a><a href="https://zhailog.com/">zhailog.com&#x2F;</a></h1><p>这里是一个比较关注NFC的博主个站，我是搞proxmark3从B站发现的，那段时间对NFC的知识非常急需，从这个博主这里学了一些。但是需要指出，这个博主的方向其实更偏向日常的NFC卡片爱好者，就是告诉你当前爱好者主流工具有哪些，然后教你怎么用的。如果你需要一些用于商业用途的开发或者是产品，这个可以带你很好的入门。博主的图做的嘎嘎好</p><h1 id="vibevibe-cn"><a href="#vibevibe-cn" class="headerlink" title="vibevibe.cn&#x2F;"></a><a href="https://www.vibevibe.cn/">vibevibe.cn&#x2F;</a></h1><p>这里是一个关于vibe coding的可以说是布道网站吧，我觉得这个网站比较适合分享给对ai coding有兴趣但是没有coding经验的同学来启蒙，同时自行摸索的同学可以从这里刷一遍名词解释，中文网站阅读友好。</p><h1 id="csdiy-wiki"><a href="#csdiy-wiki" class="headerlink" title="csdiy.wiki&#x2F;"></a><a href="https://csdiy.wiki/">csdiy.wiki&#x2F;</a></h1><p>在AI显学的时代，如果你还要学一些古典的手工编码，对某个领域或者编程语言感兴趣，这里算是有一个小小的路书，帮你指下路。</p><h1 id="xuanxuanblingbling-github-io"><a href="#xuanxuanblingbling-github-io" class="headerlink" title="xuanxuanblingbling.github.io&#x2F;"></a><a href="https://xuanxuanblingbling.github.io/">xuanxuanblingbling.github.io&#x2F;</a></h1><p>实话说，有点忘记怎么翻到这个博客了，回顾了一下应该是偏安全方向的了，可能是之前做密码学的时候搜到的，他的博客名特别又好记，就一直有这个记忆。同样停更一段时间了</p><h1 id="ruanyifeng-com-blog"><a href="#ruanyifeng-com-blog" class="headerlink" title="ruanyifeng.com&#x2F;blog&#x2F;"></a><a href="https://www.ruanyifeng.com/blog/">ruanyifeng.com&#x2F;blog&#x2F;</a></h1><p>阮老师的博客那是不必多言了，有口皆碑了。前AI时代，如果你想学什么东西刚好阮老师有文章写了的话，那这篇文章大概率是质量远超CSDN的。然后科技周报栏目也不错，可以看看新鲜事。主要是一件事做的久了，自然就形成了口碑，这就是品牌的力量。</p><h1 id="安富莱嵌入式周报"><a href="#安富莱嵌入式周报" class="headerlink" title="安富莱嵌入式周报"></a><a href="https://forum.anfulai.cn//forum.php?mod=forumdisplay&fid=12&filter=typeid&typeid=104">安富莱嵌入式周报</a></h1><p>嵌入式方向的新鲜事汇总，也是坚持了很多期。可以看看别人都用嵌入式做了什么有意思的事情，有的时候灵感真的是比技术重要的，都看看别人在干嘛，不要老自己发呆。</p><h1 id="hackaday-com"><a href="#hackaday-com" class="headerlink" title="hackaday.com&#x2F;"></a><a href="https://hackaday.com/">hackaday.com&#x2F;</a></h1><p>国外的硬件骇客论坛，很多新鲜玩意儿。</p><h1 id="beginners-re"><a href="#beginners-re" class="headerlink" title="beginners.re&#x2F;"></a><a href="https://beginners.re/">beginners.re&#x2F;</a></h1><p>关于汇编的好东西</p><h1 id="hacker-news"><a href="#hacker-news" class="headerlink" title="hacker news"></a><a href="https://news.ycombinator.com/">hacker news</a></h1><p>权威，无需多言</p><h1 id="oshwhub-com"><a href="#oshwhub-com" class="headerlink" title="oshwhub.com&#x2F;"></a><a href="https://oshwhub.com/">oshwhub.com&#x2F;</a></h1><p>立创搞的硬件开源平台，实话说逼格不如hackaday，但是里面也有很多大佬。我愿意称之为更适合中国宝宝国情的硬件开源平台。</p><h1 id="qbitai-com"><a href="#qbitai-com" class="headerlink" title="qbitai.com&#x2F;"></a><a href="https://www.qbitai.com/">qbitai.com&#x2F;</a></h1><p>用于看LLM这一轮爆发的各种八卦的，不对，也算是行业进展吧。程序员可能是最先关注也最深刻被AI影响的行业，我们静观其变。</p><h1 id="latepost-com"><a href="#latepost-com" class="headerlink" title="latepost.com&#x2F;"></a><a href="https://www.latepost.com/">latepost.com&#x2F;</a></h1><p>这里主要是关注明星企业的行业故事了，包括新能源、互联网、大模型，增长见闻吧。</p><h1 id="BTFZ-SDR"><a href="#BTFZ-SDR" class="headerlink" title="BTFZ SDR"></a><a href="https://beautifulzzzz.com/gnuradio/tutorial/topic/1">BTFZ SDR</a></h1><p>关于SDR的一些教程，SDR本身是小众领域，在这个方向高质量的中文教程更加难得。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>怎么提交博客</title>
      <link>https://blog.gitk.site/2026/03/24/workflow/</link>
      <description>
        <![CDATA[<h1 id="怎么提交博客"><a href="#怎么提交博客" class="headerlink" title="怎么提交博客"></a>怎么提交博客</h1><p>当前部署的hexo，分mian 和 source两个分支。往source文件夹下写markdown，推到云端]]>
      </description>
      <author>wwtd</author>
      <category domain="https://blog.gitk.site/tags/hexo/">hexo</category>
      <pubDate>Tue, 24 Mar 2026 10:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="怎么提交博客"><a href="#怎么提交博客" class="headerlink" title="怎么提交博客"></a>怎么提交博客</h1><p>当前部署的hexo，分mian 和 source两个分支。往source文件夹下写markdown，推到云端后会自动触发流水线渲染到main分支。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">touch</span> <span class="built_in">source</span>/_posts/xxx.md</span><br><span class="line"><span class="comment"># write something</span></span><br><span class="line">hexo s</span><br><span class="line"><span class="comment"># feel ok</span></span><br><span class="line">git add <span class="built_in">source</span>/_posts/xxx.md</span><br><span class="line">git commit -m <span class="string">&quot;xxxx&quot;</span></span><br><span class="line">git push origin <span class="built_in">source</span></span><br></pre></td></tr></table></figure><p>超链接语法和原生markdown没区别</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[github](https://github.com)</span><br></pre></td></tr></table></figure><p>但是如果贴图的话，用Post Asset Folder + Hexo的img语法稳一点，类似</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;% asset_img xxx.png %&#125;</span><br></pre></td></tr></table></figure><p>需要配置_config.yml 中的post_asset_folder为true，同时将png放到md的同名目录下。</p>]]>
      </content:encoded>
    </item>
  </channel>
</rss>
