operating-system - 我应该制作自己的操作系统内核 ELF 还是原始二进制文件?

标签 operating-system kernel elf bootloader osdev

我已经开始了我的操作系统开发之旅。人们通常会大喊使用原始二进制文件而不是 ELF(或其他结构化格式)是自定义操作系统中的应用程序的常见错误。由于 ELF 提供的额外好处(存储元信息的地方,例如符号表、 .debug 和 .line ),我可以支持这一点。但是,让我们花点时间考虑一下内核二进制文件本身。它是否应该结构化(如 ELF),如果是,为什么?否则编写一个 ELF 加载器并在 stage1 加载器之后立即压缩它似乎是一种浪费。

AFAIK Linux 内核是一个 ELF 文件,但我不知道原因。

最佳答案

我辩论是否应该深入研究一个广泛的问题,从而得出固执的答案。我通常会投票结束这样的问题,但在这种情况下,我将提供可能对其他人有益的回应。如果你问为什么?我正在为这个问题做这件事 - Stackoverflow 上的历史表明,这个问题经常作为更具体的操作系统开发问题的一部分被间接提出。

内核 ELF 的一些优点?

  • 调试信息可以嵌入对象
  • ELF 加载器可以在内存中设置图像,自动将 BSS 部分清零等。
  • 未初始化或零初始化的全局数据不占用图像内的空间。
  • 多引导兼容引导加载程序(如 GRUB)可以加载正确设计的 ELF 可执行文件
  • 可以设计为可重定位。

  • 缺点?
  • ELF 头文件位于可执行文件的开头,这可能会干扰可执行文件将在其中运行的预期目标环境(如引导加载程序)
  • 对于小程序,ELF header 对于某些用途(引导加载程序)来说可能太大
  • 需要代码(最小 ELF 加载程序)将可执行文件引导到内存中并开始执行它。


  • 为什么我们不将 ELF 用于最终引导扇区镜像 (MBR)?

    主要原因是 ELF 格式将头信息放在代码之前。传统 BIOS(非 EFI)不会理解它并开始将 header 信息作为代码执行。

    您可以使用 ELF 镜像来调试 16 位引导加载程序吗?

    这取决于环境和调试器。通过 QEMU 中的远程 GDB 调试,这是非常可能的。您可以在 NASM/GAS 等汇编器中生成一个 16 位实模式可执行文件作为 ELF 对象(带有 Dwarf 调试信息),将其链接到最终的 ELF 可执行文件,然后使用 objcopy 之类的程序去除 ELF 头文件以生成最终的平面二进制文件。

    如果您无论如何都要将其剥离为平面二进制文件,为什么还要为引导加载程序生成 ELF 对象呢?

    尽管精简后的二进制文件将在目标环境中运行,但具有远程调试功能的环境(如 QEMU)可以使用本地 ELF 二进制文件来解析变量名称、标签、常量,并允许导航原始源(不仅仅是原始程序集)。

    您能否为 16 位调试提供此技术的示例?

    是的,这种类型的问题以前出现过。我已经提供了一些答案,展示了如何使用 GDB 的远程调试服务和 QEMU 中的远程调试器来做到这一点。一个这样的例子可以在这个 StackOverflow answer 中找到。 .该示例是一个示例 16 位引导加载程序,可以使用 GDB 进行调试。 GDB 的 16 位调试存在问题,因为它不了解 16 位代码中的段:偏移对。提供了一个脚本链接,在这方面有帮助,以及示例 QEMU 用法。

    与多重引导加载程序一起使用时,ELF 可执行文件是否有优势?

    是的!像 GRUB 这样的 Multiboot 兼容引导加载程序的一大优势是它可以理解 ELF 镜像。如果您正在编写保护模式内核,并且您为您的内核使用了正确构造的多引导兼容可执行文件 - 您可以节省设置保护模式环境的苦差事(在 x86 系统上),启用 A20 Gate,获取内存映射, 并初始化启动视频模式模式。

    QEMU 可以直接启动符合 Multiboot 的 ELF 内核可执行文件吗?

    是的,使用正确的命令行使用 -kernel选项是可能的。 OS Dev Wiki有一个例子。

    您可以使用带有调试信息的 ELF 二进制文件来调试 32 位保护模式吗?

    是的,这比在实模式下运行的 16 位引导加载程序更简单。我在此 StackOverflow answer 中提供了此技术的示例.尽管该技术适用于使用 ISO 镜像的 QEMU,但您也可以使用 -kernel 直接使用多引导内核加载 QEMU。选项。

    为什么现代版本的 Linux 内核使用 ELF 格式?

    在 Linux 开发的远古时代,Linux 有自己的引导加载程序、设置保护模式、启用 A20 门等。这个过程因架构而异。 Linux 内核开发人员选择将这项工作留给 3rd 方引导加载程序。

    在现代桌面系统上,您会发现 GRUB 用作 Muliboot 加载程序;可以使用 ELILO;在某些嵌入式系统上,U-Boot 成为首选的引导加载程序。 Multiboot 规范是出于引导 Linux 内核的需要而产生的,但它独立于操作系统。互联网上的许多玩具内核示例都被编码为用作 ELF 可执行文件,以便它们可以利用符合 Multiboot 的引导加载程序所提供的功能。

    更多关于 Multiboot 规范的信息可以在 GRUB Documentation 中找到。

    关于operating-system - 我应该制作自己的操作系统内核 ELF 还是原始二进制文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34129461/

    相关文章:

    gcc - 为什么独立的 C hello 程序在用作动态链接器时会崩溃

    c - 如何解决此打印输出问题?

    linux - 如何计算内核中的活跃用户数

    android - 如何在android中延迟加载共享库

    c++ - 在 OpenCL 1.2 中的内核之间传递变量/内核之间的通信

    linux - 交叉编译GCC for linux i686-elf

    multithreading - 在多线程进程中跨线程共享程序状态的哪些组件?

    c - 管道上的 EOF 正在打印垃圾

    Android 4.2 引入了内核默认启用的 SELinux

    elf - ELF 中的 SHT_NULL 部分有什么用?