<?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 大飞的博客</title>
    <link>https://www.dafei.me/tags/%E9%93%BE%E6%8E%A5%E8%84%9A%E6%9C%AC/</link>
    <description>Recent content in 链接脚本 on 大飞的博客</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Wed, 06 May 2026 04:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.dafei.me/tags/%E9%93%BE%E6%8E%A5%E8%84%9A%E6%9C%AC/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从零写OS（四）：汇编交棒给 C，最关键的一跳</title>
      <link>https://www.dafei.me/posts/os-04-load-kernel/</link>
      <pubDate>Wed, 06 May 2026 04:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-04-load-kernel/</guid>
      <description>&lt;p&gt;前三章全是汇编。到这里，CPU 已经跑在 64 位长模式下了，但还是什么都做不了。&lt;/p&gt;
&lt;p&gt;这一章要完成最关键的一步：&lt;strong&gt;把控制权从汇编交给 C&lt;/strong&gt;。之后的内核全部用 C 写，汇编只在最必要的地方出现。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;磁盘要怎么布局&#34;&gt;磁盘要怎么布局&lt;/h2&gt;
&lt;p&gt;512 字节的 Bootloader 放不下 C 内核，所以内核单独编译成一个二进制文件，放在磁盘的后续扇区（&lt;strong&gt;扇区&lt;/strong&gt;，Sector，磁盘读写的最小单位，传统上每个 512 字节）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;扇区 0（512B）：Bootloader
扇区 1～8     ：内核二进制
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Bootloader 启动后，用 BIOS 的磁盘读取中断把扇区 1 开始的内容读到内存地址 &lt;code&gt;0x10000&lt;/code&gt;，然后跳过去执行。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一个容易踩的坑内核入口不在-0x10000&#34;&gt;一个容易踩的坑：内核入口不在 0x10000&lt;/h2&gt;
&lt;p&gt;把内核编译好、加载到 &lt;code&gt;0x10000&lt;/code&gt;，然后 &lt;code&gt;call 0x10000&lt;/code&gt;——看起来没问题，实际上很可能直接崩。&lt;/p&gt;
&lt;p&gt;用 &lt;code&gt;objdump&lt;/code&gt;（反汇编工具，可以查看二进制文件的结构和函数地址）一查才发现：&lt;code&gt;kernel_main&lt;/code&gt; 的实际地址是 &lt;code&gt;0x10109&lt;/code&gt;，不是 &lt;code&gt;0x10000&lt;/code&gt;。&lt;code&gt;0x10000&lt;/code&gt; 开头是另一个函数 &lt;code&gt;outb&lt;/code&gt;，跳过去执行完全是错的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案&lt;/strong&gt;：加一个 &lt;code&gt;entry.asm&lt;/code&gt; 作为内核最开头，内容只有一行：跳转到 &lt;code&gt;kernel_main&lt;/code&gt;。然后在&lt;strong&gt;链接脚本&lt;/strong&gt;（Linker Script，告诉链接器如何拼装各个目标文件、每段放在哪个地址）里把 &lt;code&gt;entry.o&lt;/code&gt; 排在最前面，保证 &lt;code&gt;_start&lt;/code&gt; 恰好落在 &lt;code&gt;0x10000&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这样 Bootloader 跳到 &lt;code&gt;0x10000&lt;/code&gt;，第一条指令就是 &lt;code&gt;jmp kernel_main&lt;/code&gt;，稳了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;还有一个坑栈&#34;&gt;还有一个坑：栈&lt;/h2&gt;
&lt;p&gt;进入 64 位模式之后，&lt;code&gt;rsp&lt;/code&gt;（栈顶指针寄存器）是未初始化的。C 函数一用栈就崩。&lt;/p&gt;
&lt;p&gt;在跳入内核之前，要先设好栈：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-nasm&#34; data-lang=&#34;nasm&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;mov&lt;/span&gt; rsp, &lt;span style=&#34;color:#ae81ff&#34;&gt;0x90000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;选 &lt;code&gt;0x90000&lt;/code&gt; 是因为这块内存没有被占用，往下增长也不会覆盖内核代码。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;内核编译参数&#34;&gt;内核编译参数&lt;/h2&gt;
&lt;p&gt;内核不能用普通方式编译，要加几个关键选项：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
