如何用C语言编写linux启动代码?

时间:2017-04-22 21:11:42

标签: c assembly linux-kernel boot x86-16

我是学习OS开发的新手。从我读过的书中,它说启动加载程序会将第一个MBR复制到0x7c00,然后从实模式开始。

并且,示例从16位汇编代码开始。 但是,当我查看今天的linux内核时,arch/x86/boot有'header.S'和'boot.h',但实际代码是在main.c中实现的。

这似乎对“不编写程序集”很有用。 但是,这是如何在Linux中专门完成的? 我可以粗略地想象可能有特殊的gcc选项和链接策略,但我看不到细节。

1 个答案:

答案 0 :(得分:7)

我更多地将这个问题作为X-Y问题阅读。在我看来,问题更多的是你是否可以在 C 中为自己的OS开发编写一个bootloader(启动代码)。简单的答案是 YES ,但不推荐。现代Linux内核可能不是创建用 C 编写的引导加载程序的最佳信息来源,除非您了解它们的代码在做什么。

如果使用 GCC ,则对生成的代码的操作有限制。在较新版本的 GCC 中,有-m16选项以这种方式记录:

  

-m16 选项与-m32相同,只是它在程序集输出的开头输出".code16gcc"程序集指令,以便二进制文件可以运行在16位模式下。

这有点欺骗性。虽然代码可以在16位实模式下运行,但后端生成的代码使用386地址和操作数前缀来使通常32位代码在16位实模式下执行。这意味着 GCC 生成的代码不能在386之前的处理器上使用(如8086/80186/80286等)。如果您想要一个可以在最广泛的硬件阵列上运行的引导加载程序,这可能是一个问题。如果您不关心386之前的系统,那么 GCC 将起作用。

使用 GCC 的Bootloader代码有另一个缺点。添加到许多指令的地址和操作数前缀加起来并且可以使引导加载程序膨胀。引导程序的第一阶段通常在空间上受到很大限制,因此这可能会成为一个问题。

您需要具有与硬件交互的函数的内联汇编语言或汇编语言对象。您无法在引导加载程序代码中访问Linux C 库(printf等)。例如,如果要写入视频显示器,则必须自己编写该功能,直接写入视频内存或通过BIOS中断。

要将它完全绑定并将内容放在可用作MBR的二进制文件中,您可能需要一个特制的链接描述文件。在大多数项目中,这些链接描述文件具有.ld扩展名。这推动了将所有目标文件以与传统BIOS引导过程兼容的方式(在0x07c00以实模式运行的代码)将它们放在一起的过程。

这样做有很多陷阱,我建议反对。如果您打算编写32位或64位内核,那么我建议不要编写自己的引导加载程序并使用现有的引导加载程序,如 GRUB 。在20世纪90年代的Linux版本中,它有自己的bootloader,可以从软盘执行。现代Linux依赖于第三方引导程序来完成大部分工作。特别是它支持符合Multiboot specification

的引导加载程序

互联网上有许多使用 GRUB 作为引导加载程序的教程。 OS Dev Wiki是宝贵的资源。他们有一个Bare Bones教程,它使用原始的多重引导规范(由GRUB支持)来引导基本内核。可以使用最少的汇编语言代码轻松开发Mulitboot规范。多引导兼容引导加载程序将自动将CPU置于保护模式,启用A20线,可用于获取内存映射,并且可以告诉您在引导时将您置于特定的视频模式。

去年有人在#Osdev聊天时询问是否写了一个位于完全用 GCC 开发的软盘(或磁盘映像)的前2个扇区中的2阶段引导加载程序和内联汇编。我不推荐这个,因为它相当复杂,内联汇编很难做到。非常easy to write bad inline assembly似乎有效,但不正确。

我使用了链接器脚本 C 并使用内联汇编来使用some sample code来处理BIOS中断以从磁盘读取并写入视频显示。如果有任何代码应该是一个例子,为什么要做你要求的事情是非常重要的。