我已经开始了我的OS开发之旅.人们通常会大声说使用原始二进制而不是ELF(或其他结构化格式)是自定义操作系统中应用程序的常见错误.我可以说,因为ELF提供了额外的好处(存储元信息的地方,如符号表,.debug和.line).但是,让我们考虑内核二进制本身一分钟.它应该是结构化的(如ELF),如果是,为什么?否则写一个ELF加载器并在stage1加载器之后立即挤压它似乎是浪费.
AFAIK Linux内核是一个ELF文件,但我不知道原因.
我辩论我是否应该深入研究一个能够找到自以为是的答案的广泛问题.我通常会投票决定这样的问题,但在这种情况下,我会提供一个可能对其他人有益的回应.如果你问为什么?我正在为这个问题做这件事 - 历史已经在Stackoverflow上表明,这个问题经常间接地被问到是一个更具体的操作系统开发问题的一部分.
内核的ELF的一些优点?
调试信息可以嵌入到对象中
ELF加载程序可以在内存中设置图像,自动将BSS部分清零等.
未初始化或零初始化的全局数据不占用图像内部的空间.
多引导兼容引导加载程序(如GRUB)可以加载正确设计的ELF可执行文件
可以设计为可重定位.
缺点是什么?
ELF头放置在可执行文件的开头,这可能会干扰可执行文件将在其中运行的目标环境(如引导加载程序)
对于小程序,ELF头文件对于某些用途来说可能太大(引导加载程序)
需要代码(最小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的答案中可以找到一个这样的例子.该示例是一个示例16位引导加载程序,可以使用GDB进行调试.GDB的 16位调试存在问题,因为它不了解16位代码中的segment:offset对.为脚本提供了一个链接,以及QEMU的使用示例.
与Multiboot加载程序一起使用时,ELF可执行文件是否有优势?
是! 像GRUB这样的Multiboot兼容引导加载程序的一大优势是它可以理解ELF图像.如果您正在编写受保护模式内核,并且为内核使用了正确构造的Multiboot兼容可执行文件 - 您可以节省设置受保护模式环境的苦差事(在x86系统上),A20 Gate启用,获取内存映射,并初始化启动视频模式模式.
QEMU可以直接启动兼容Multiboot的ELF内核可执行文件吗?
是的,使用该-kernel
选项使用适当的命令行是可能的.OS Dev Wiki有一个例子.
您可以使用带调试信息的ELF二进制文件调试32位保护模式吗?
是的,这比在实模式下运行的16位引导加载程序更简单.我在这个StackOverflow答案中提供了这种技术的一个例子.虽然该技术适用于使用ISO映像的QEMU,但您也可以使用该选项直接使用多引导内核加载QEMU-kernel
.
为什么现代版本的Linux使用ELF格式作为内核?
在Linux开发的古代,Linux有自己的引导程序,设置保护模式,启用A20门等.这个过程在不同的架构中是不同的.有一点是Linux内核开发人员选择将这项工作留给第三方引导程序.
在现代桌面系统上,您会发现GRUB用作Muliboot加载器; 可以使用ELILO ; 在某些嵌入式系统中,U-Boot成为首选的引导程序.多引导规范产生于引导Linux内核的需要,但是它独立于操作系统.互联网上的许多玩具内核示例被编码为用作ELF可执行文件,以便他们可以利用Multiboot兼容的引导加载程序提供的功能.
有关Multiboot规范的更多信息,请参阅GRUB文档