如何用C编写linux启动代码? [英] How can linux boot code be written in C?

查看:32
本文介绍了如何用C编写linux启动代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是学习操作系统开发的新手.从我读的书中,它说引导加载程序会将第一个 MBR 复制到 0x7c00 中,并从那里以实模式启动.

I'm a newbie to learning OS development. From the book I read, it said that boot loader will copy first MBR into 0x7c00, and starts from there in real mode.

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

And, example starts with 16 bit assembly code. But, when I looked at today's linux kernel, arch/x86/boot has 'header.S' and 'boot.h', but actual code is implemented in main.c.

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

This seems to be useful by "not writing assembly." But, how is this done specifically in Linux? I can roughly imagine that there might be special gcc options and link strategy, but I can't see the detail.

推荐答案

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

I'm reading this question more as an X-Y problem. It seems to me the question is more about whether you can write a bootloader (boot code) in C for your own OS development. The simple answer is YES, but not recommended. Modern Linux kernels are probably not the best source of information for creating bootloaders written in C unless you have an understanding of what their code is doing.

如果使用 GCC,您可以使用生成的代码执行的操作会受到限制.在较新版本的 GCC 中有一个 -m16 选项,以这种方式记录:

If using GCC there are restrictions on what you can do with the generated code. In newer versions of GCC there is an -m16 option that is documented this way:

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

The -m16 option is the same as -m32, except for that it outputs the ".code16gcc" assembly directive at the beginning of the assembly output so that the binary can run in 16-bit mode.

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

This is a bit deceptive. Although the code can run in 16-bit real mode, the code generated by the back end uses 386 address and operand prefixes to make normally 32-bit code execute in 16-bit real mode. This means the code generated by GCC can't be used on processors earlier than the 386 (like the 8086/80186/80286 etc). This can be a problem if you want a bootloader that can run on the widest array of hardware. If you don't care about pre-386 systems then GCC will work.

使用 GCC 的引导加载程序代码还有另一个缺点.添加到许多指令中的地址和操作数前缀加起来会使引导加载程序膨胀.引导加载程序的第一阶段的空间通常非常有限,因此这可能会成为一个问题.

Bootloader code that uses GCC has another downside. The address and operand prefixes that get get added to many instructions add up and can make a bootloader bloated. The first stage of a bootloader is usually very constrained in space so this could potentially become a problem.

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

You will need inline assembly or assembly language objects with functions to interact with the hardware. You don't have access to the Linux C library (printf etc) in bootloader code. For example if you want to write to the video display you have to code that functionality yourself either writing directly to video memory or through BIOS interrupts.

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

To tie it altogether and place things in the binary file usable as an MBR you will likely need a specially crafted linker script. In most projects these linker scripts have an .ld extension. This drives the process of taking all the object files putting them together in a fashion that is compatible with the legacy BIOS boot process (code that runs in real mode at 0x07c00).

这样做有很多陷阱,我建议不要这样做.如果您打算编写 32 位或 64 位内核,那么我建议您不要编写自己的引导加载程序,而是使用像 GRUB 这样的现有引导加载程序.在 1990 年代的 Linux 版本中,它有自己的引导加载程序,可以从软盘执行.现代 Linux 现在依靠第三方引导加载程序来完成大部分工作.特别是它支持符合 多重引导规范

There are so many pitfalls in doing this that I recommend against it. If you are intending to write a 32-bit or 64-bit kernel then I'd suggest not writing your own bootloader and use an existing one like GRUB. In the versions of Linux from the 1990s it had its own bootloader that could be executed from floppy. Modern Linux relies on third party bootloaders to do most of that work now. In particular it supports bootloaders that conform to the Multiboot specification

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

There are many tutorials on the internet that use GRUB as a bootloader. OS Dev Wiki is an invaluable resource. They have a Bare Bones tutorial that uses the original Multiboot specification (supported by GRUB) to boot strap a basic kernel. The Mulitboot specification can easily be developed for using a minimal of assembly language code. Multiboot compatible bootloaders will automatically place the CPU in protected mode, enable the A20 line, can be used to get a memory map, and can be told to place you in a specific video mode at boot time.

去年有人在 #Osdev 聊天中询问如何编写位于第一个的 2 阶段引导加载程序软盘(或磁盘映像)的 2 个扇区完全在 GCC 和内联汇编中开发.我不推荐这样做,因为它相当复杂,而且内联汇编很难做到正确.很容易编写错误的内联汇编,这似乎有效但不正确.

Last year someone on the #Osdev chat asked about writing a 2 stage bootloader located in the first 2 sectors of a floppy disk (or disk image) developed entirely in GCC and inline assembly. I don't recommend this as it is rather complex and inline assembly is very hard to get right. It is very easy to write bad inline assembly that seems to work but isn't correct.

我提供了使用链接脚本的一些示例代码, C 使用内联汇编来处理 BIOS 中断以从磁盘读取并写入视频显示.如果有的话,这段代码应该是一个例子,为什么做你所要求的事情很重要.

I have made available some sample code that uses a linker script, C with inline assembly to work with the BIOS interrupts to read from the disk and write to the video display. If anything this code should be an example why it's non-trivial to do what you are asking.

这篇关于如何用C编写linux启动代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆