我已经开始了我的OS开发之旅。人们通常会大声说使用原始二进制而不是ELF(或其他结构化格式)是自定义操作系统中应用程序的常见错误。我可以说,因为ELF提供了额外的好处(存储元信息的地方,如符号表,.debug和.line)。但是,让我们考虑内核二进制本身一分钟。它应该是结构化的(如ELF),如果是,为什么?否则写一个ELF加载器并在stage1加载器之后立即挤压它似乎是浪费。
AFAIK Linux内核是一个ELF文件,但我不知道原因。
答案 0 :(得分:12)
我辩论过我是否应该深入研究一个能够找到自以为是的答案的广泛问题。我通常会投票决定这样的问题,但在这种情况下,我会提供一个可能对其他人有益的回应。如果你问为什么?我正在为这个问题做这件事 - 历史记录已经在Stackoverflow上显示,这个问题经常间接被问到是一个更具体的操作系统开发问题的一部分。
ELF对内核的一些优势?
<强>缺点吗
为什么我们不使用ELF作为最终引导扇区图像(MBR)?
主要原因是ELF格式在代码之前放置标题信息。传统BIOS(非EFI)无法理解它并开始将头信息作为代码执行。
您可以使用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位代码中的segment:offset对。为脚本提供了一个链接,以及示例 QEMU 用法。
与Multiboot加载程序一起使用时,ELF可执行文件是否有优势?
是的!多引导兼容引导加载程序(如 GRUB )的一大优势是它能够理解 ELF 图像。如果您正在编写受保护模式内核,并且为内核使用了正确构造的Multiboot兼容可执行文件 - 您可以节省设置受保护模式环境的苦差事(在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内核开发人员选择将这项工作留给第三方引导程序。
在现代桌面系统上,您会发现 GRUB 用作Muliboot加载程序;可以使用 ELILO ;在某些嵌入式系统上, U-Boot 成为首选的引导加载程序。多引导规范产生于引导Linux内核的需要,但是它独立于操作系统。互联网上的许多玩具内核示例被编码为用作ELF可执行文件,以便他们可以利用Multiboot兼容的引导加载程序提供的功能。
有关多重引导规范的更多信息,请参阅GRUB Documentation
答案 1 :(得分:1)
a.out可能是原始/平面二进制和ELF之间更好的折衷。在a.out中,您可以像在ELF中一样将代码与未正版化数据中的数据分开,并且您可以拥有重定位(和符号,如果您关心的话),在没有明确重新发明部分内容的情况下,您都不会使用原始/平面二进制文件。然而,a.out比ELF简单得多,几乎和raw / flat一样简单。很容易解析和加载(除非你像我在编译器的DPMI存根中那样在16位汇编代码中执行它)。
答案 2 :(得分:0)
我个人推荐平面二进制,因为它真的很容易加载。只需将文件复制到ram中,跳到代码中,就可以完成。 (如果您要制作自己的引导程序)