如何使用C语言编写Linux引导代码?

8

我是一个学习操作系统开发的新手。从我所读的书中得知,引导加载程序将把第一个MBR复制到0x7c00,并在实模式下从那里启动。

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

这似乎通过“不写汇编”很有用。 但是,Linux是如何做到这一点的呢? 我大致能想象出可能有特殊的gcc选项和链接策略,但我看不到细节。


4
GCC 不能生成可用的16位代码,但它可以生成可用的32位代码,在386(或更高版本)处理器上可以在16位实模式下运行。.S文件是汇编文件(你将看到相当数量的汇编代码来调用C语言中的main函数)。header.S可以在编译期间用作实模式引导程序。真正的魔法在于创建一个链接器文件,在你的情况下是setup.ld,它可以正确地构建带有代码和数据的对象,并将它们放在特定的位置。所有的C语言代码都必须使用BIOS例程或直接硬件访问来完成。 - Michael Petch
大部分与BIOS和硬件相关的功能都在boot.h文件中。 - Michael Petch
1
你正在查看的代码并不是直接由BIOS引导的,BIOS会将MBR加载到0000:7c00并跳转到它。这段代码是要被类似GRUB这样的东西加载和执行的,GRUB会在MBR中有自己的代码。如果你正在查看的代码实际上是由BIOS加载的,它实际上不会启动,并告诉你要使用引导加载程序。 - Ross Ridge
1
我有一个基本的引导加载程序示例,完全使用_C_和内联汇编完成,但与Linux代码一样,只能在386(或更高版本)处理器上运行。大部分魔法都在链接器脚本和提供一些基本BIOS和端口访问的头文件中。可以在这里找到:http://capp-sysware.com/misc/ircasm/gccboot-2stage/。除非您确切知道自己在做什么,否则我不建议这样做。Openwatcom的_C_编译器更好,因为它们仍然支持16位实模式代码生成(您只需要适当的链接器来生成最终的二进制映像)。 - Michael Petch
1
由于你正在进行操作系统开发,我假设你想要编写自己的操作系统,并从Linux引导代码中汲取灵感。如果你打算编写自己的引导程序并希望使用_C_语言编写,我建议使用OpenWatcom C ,因为它可以生成适当的16位实模式代码。由于简单的引导程序通常不是很大,因此仅用纯汇编语言编写它们也不会太麻烦。如果你不想编写引导程序,并想使用像_GRUB_这样的东西,那么只需使用最少量的汇编语言代码即可完成。 - Michael Petch
显示剩余5条评论
1个回答

9
我认为这个问题更像是一个X-Y问题。对我来说,问题更多地关于你是否可以用C语言编写自己的操作系统开发引导程序(启动代码)。简单的答案是YES,但不建议这样做。现代Linux内核可能不是创建用C编写的引导程序的最佳信息来源,除非你了解它们的代码在做什么。
如果使用GCC,生成代码的操作受到限制。在较新版本的GCC中,有一个-m16选项,其文档如下:
“-m16选项与-m32相同,只是在汇编输出的开头输出“.code16gcc”汇编指令,以便二进制文件可以在16位模式下运行。”
这有点误导人。虽然代码可以在16位实模式下运行,但后端生成的代码使用386地址和操作数前缀使通常的32位代码在16位实模式下执行。这意味着由GCC生成的代码不能在早于386处理器(如8086/80186/80286等)上使用。如果你想要一个可以在最广泛的硬件上运行的引导程序,这可能会成为一个问题。如果您不关心早期的386系统,则GCC将起作用。
使用GCC的引导加载程序代码还有另一个缺点。许多指令添加的地址和操作数前缀会累加,使引导加载程序变得臃肿。引导程序的第一阶段通常非常受空间限制,因此这可能会成为一个潜在问题。
您需要内联汇编或汇编语言对象来与硬件交互。在引导程序代码中,您无法访问Linux C库(如printf等)。例如,如果您想要写入视频显示器,您必须自己编写该功能,直接写入视频内存或通过BIOS中断进行写入。
为了将所有内容绑定在一个二进制文件中,并使其可用作MBR,您可能需要一个特别制作的链接脚本。在大多数项目中,这些链接器脚本具有.ld扩展名。这驱动了将所有目标文件组合在一起的过程,并以与传统的BIOS引导过程(在0x07c00处以实模式运行的代码)兼容的方式进行。 这样做有很多陷阱,我建议不要这样做。如果您打算编写32位或64位内核,则建议不要编写自己的引导加载程序,而是使用现有的引导加载程序,例如GRUB。在20世纪90年代的Linux版本中,它有自己的引导加载程序,可以从软盘执行。现代Linux依赖于第三方引导加载程序来完成大部分工作。特别是它支持符合Multiboot规范的引导加载程序。

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


去年,#Osdev聊天室上有人询问如何编写一个二阶段引导加载程序,位于软盘(或磁盘映像)的前两个扇区中,完全使用GCC和内联汇编语言进行开发。我不建议这样做,因为它相当复杂,而且内联汇编语言很难正确编写。很容易编写错误的内联汇编代码(即使看起来似乎有效但实际上是错误的)
我提供了一些示例代码(使用链接脚本,带有内联汇编的C语言),以便与BIOS中断一起读取磁盘并将其写入视频显示。如果说什么,这段代码应该是为什么你所要求的事情并不简单的一个例子。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接