问题描述
我试图按照指示在这里建立一个简单的OS内核:
除,而不是从软盘启动,我想创建一个基于GRUB的ISO映像和引导模拟器多重引导的CD。我已经添加了以下在该页面中列出,对于多重包头中的源:
MBALIGN EQU 1 LT;℃的;对齐页面边界上加载的模块
meminfo的EQU 1 LT;< 1;提供内存映射
FLAGS EQU MBALIGN | meminfo中;这是多引导标志字段
MAGIC EQU 0x1BADB002; 幻数让引导程序找到头
CHECKSUM EQU - (MAGIC +标志);以上校验,以证明我们是多重
部分.multiboot
4对齐
DD MAGIC
DD FLAGS
DD校验
和我做下面创建映像:
NASM -felf32 -o init.bin init.s
CP init.bin目标的/ boot / init.bin
GRUB2-mkrescue -o init.iso目标/
然后我跑QEMU来引导它:
的qemu-系统x86_64的-cdrom ./init.iso
从启动菜单中选择myos'后,我得到的错误
错误:无效的拱依赖ELF魔
这是什么意思,以及如何解决?我试着用ELF格式搞乱,但只有 -felf32
似乎工作...
GRUB支持的 ELF32 的平坦二进制文件。你的头,虽然含蓄地说,你是提供的 ELF 的二进制文件。
使用扁平二进制多重引导
如果您想告诉您正在使用一台二进制必须设置多重引导装载程序(GRUB)的第16位的为1:
MULTIBOOT_AOUT_KLUDGE EQU 1 LT;< 16
; FLAGS [16]指示GRUB我们不
; ELF可执行文件和田野
;头地址,加载地址,负载端地址,
; BSS结束地址,以及入口地址将是
;在我们的多重引导头可用
这是不是只是指定这个标志一样简单。你必须提供完整的多重引导头,提供了多重引导加载程序的信息,我们的二进制加载到内存中。当使用的 ELF 的格式信息是在 ELF 的标题是precedes我们code所以没有被明确规定。多重引导头在定义很详细。
在使用的 NASM 的有 -f斌
要注意的是,我们需要指定我们的code原点是很重要的。多重引导装载程序来载入我们的物理地址的0x100000
内核。我们必须在我们的汇编文件中指定我们的原点是的0x100000
,以便适当偏移等将在我们的最终平二值图像获取生成。
这是剥离,从我自己的项目之一,它提供了一个简单的头修饰的例子。到主程序
的呼叫建立比如上例中的C调用,但你不必这样做的。通常我调入,是以堆栈上的几个参数的函数(使用C调用约定)。
[BITS 32]
[全球_start]
[ORG的0x100000];如果使用-f本',我们需要指定
;原点为我们code。与ORG指令
;多重装载机加载在我们的身体
;地址0x100000处MULTIBOOT_AOUT_KLUDGE EQU 1 LT;< 16
; FLAGS [16]指示GRUB我们不
; ELF可执行文件和田野
;头地址,加载地址,负载端地址;
; BSS结束地址和入口地址将可用
;在多重引导头
MULTIBOOT_ALIGN EQU 1所述;℃下;对齐页面边界上加载的模块
MULTIBOOT_MEMINFO EQU 1所述;&所述; 1;提供内存映射MULTIBOOT_HEADER_MAGIC EQU 0x1BADB002
;在第一个8K的幻数GRUB搜索
;内核文件GRUB被告知要加载MULTIBOOT_HEADER_FLAGS EQU MULTIBOOT_AOUT_KLUDGE | MULTIBOOT_ALIGN | MULTIBOOT_MEMINFO
CHECKSUM EQU - (MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)KERNEL_STACK EQU 0x00200000;堆栈开始在2MB地址和放大器;向下增长_开始:
XOR EAX,EAX;清EAX和EBX事件
XOR EBX,EBX;我们不被GRUB加载。
JMP multiboot_entry;跃过多重引导头
调整4;多重引导头必须是32
;位对齐,以避免错误13
multiboot_header:
DD MULTIBOOT_HEADER_MAGIC;幻数
DD MULTIBOOT_HEADER_FLAGS;标志
DD校验,校验
DD multiboot_header;头地址
DD _start code入口点的加载地址
;在我们的例子_start
DD 00;负载端地址:没有必要
DD 00; BSS结束地址:没有必要
DD multiboot_entry;入口地址GRUB将开始multiboot_entry:
MOV ESP,KERNEL_STACK;设置堆栈
推0;复位EFLAGS
POPF 推EAX;第二个参数是神奇的数字
推EBX;第一个参数多重信息指针
调用_MAIN;调用_MAIN
ADD ESP,8;清理8个字节推作为参数 CLI
ENDLOOP:
HLT
JMP ENDLOOP_主要:
RET;没做什么
多重引导装载程序(的 GRUB 的)一般加载在你的文件的第一个8K(是否 ELF 的或扁平二进制),查找多重引导头中的32位边界上。如果的多重引导头标志的位16 的是明确的,它假定您提供的 ELF 的形象。然后它分析的 ELF 的标题检索,它需要你的内核文件加载到内存中的信息。如果的第16位的设置则需要一个完整的多重引导头,使装载机具有信息读取内核到内存,进行初始化,然后调用到你的内核。
您会再组装你的 init.s
来的东西像一个扁平二进制文件:
NASM -f -o init.bin init.s斌
使用ELF与多重
要在小丑的言论系在你原来的问题,您应该已经能够用的 ELF 的启动,它的工作,但它并没有因为一个小细节。在您的例子,你用它来制作的 init.bin 的:
NASM -f ELF32 -o init.bin init.s
在使用 -f ELF32
, NASM 的产生与目标文件(它们不是可执行的),必须链接( LD 的为例)生成最终的 ELF 的(ELF32)可执行文件。这本来可能的工作,如果你有喜欢的东西所做的汇编和链接的过程:
NASM -f ELF32 init.s -o init.o
LD -Ttext = 0x100000处-melf_i386 -o init.bin init.o
请注意,使用 -f ELF32
时,必须从的 init.s 的删除的 ORG 的指令。在 ORG 的指令只能使用 -f斌
时适用。多重引导加载器将在物理地址的0x100000
加载我们,我们必须确保组装和链接code与该原点产生。当使用 -f ELF32
我们指定与 -Ttext =的0x100000
的入口点链接器( LD 的)命令行。另外原点可以在链接脚本进行设置。
使用NASM / LD / objcopy把要生成平二值图像
有可能使用的 NASM / LD / objcopy把的在一起,产生最终的扁平二进制图像,而不是使用 -f斌
与 NASM 的。如果您的 init.s 的删除的 ORG 的指令,并使用这些命令,应该产生一个平坦二进制的 init.bin 的:
NASM -f ELF32 init.s -o init.o
LD -Ttext = 0x100000处-melf_i386 -o init.elf init.o
objcopy把-O二进制init.elf init.bin
在此, NASM 的被告知产生的 ELF32 的对象。我们集合 init.s 的成的 ELF 的对象调用的 init.o 的文件。然后我们可以使用连接器( LD 的)来生成的 init.o 的一个 ELF 的可执行文件名为 init.elf 的。我们使用一个名为 objcopy把的剥离特别节目所有的 ELF 的头关,并产生所谓的扁平二进制可执行文件的 init.bin 的。
这是一个很大的不仅仅是使用的 NASM 的使用 -f斌
选项生成可执行平更复杂的 init.bin 的。何苦呢?与上面的方法,你可以告诉的 NASM 的生成可以通过的 GDB 的(GNU调试器)被用来调试信息。如果您尝试使用 -g
(启用调试)用的 NASM 的使用 -f斌
没有调试信息获取生成。您可以通过改变装配顺序这种方式产生的调试信息:
NASM -g3 -F矮-f ELF32 init.s -o init.o
LD -Ttext = 0x100000处-melf_i386 -o init.elf init.o
objcopy把-O二进制init.elf init.bin
的 init.o 的将包含调试信息(侏儒的格式),将与的 LD 的成的 init.elf 的(它保留调试信息)。扁平的二进制文件不包含调试信息,因为它们被剥去当您使用的 objcopy把的有 -O二进制
。您可以使用的 init.elf 的,如果你启用的 QEMU 的远程调试设备,并使用的 GDB 的调试。在此调试信息的 init.elf 的提供信息,让您单步通过你的code,访问变量和标签的名字调试器,看源汇编code等。
除了生成调试信息,还有另外一个理由使用的 NASM / LD / objcopy把的过程中生成一个内核二进制文件。的 LD 的是多的配置。的 LD 的允许一个人创建连接器脚本,让您更好地调整得到的东西怎么在最终的二进制布局。这可以是更复杂的内核可能包含来自不同的环境(C,汇编等)code的混合物是有用的。对于一个小玩具内核它可能不被需要的,但作为内核生长在复杂性使用链接脚本将变得更加明显。
与QEMU GDB远程调试
如果您使用在previous节中的方法来生成调试的 ELF 的可执行文件中的信息( init.elf 的),你可以启动的 QEMU 的和有它:
- 加载的 QEMU 的环境,并在启动时停止CPU。从手册页:
- Make QEMU listen for a GDB remote connection on localhost:1234 . From man page:
Then you just have to launch GDB so that it:
- Launches GDB with our ELF executable (init.elf) with debug symbols and information
- Connects to localhost:1234 where QEMU is listening
- Sets up the debug layout of your choice
- Sets a break point to stop in our kernel (in this example multiboot_entry)
Here is an example of launching our kernel from the CD-ROM image init.iso, and launching GDB to connect to it:
qemu-system-x86_64 -cdrom ./init.iso -S -s &
gdb init.elf \
-ex 'target remote localhost:1234' \
-ex 'layout src' \
-ex 'layout regs' \
-ex 'break multiboot_entry' \
-ex 'continue'
You should be able to use GDB in much the same way as debugging a normal program. This assumes you will not be debugging a 16-bit program (kernel).
Important Considerations
As Jester points out, when using Multiboot compliant loaders like GRUB, the CPU is in 32-bit protected mode (not 16-bit real mode). Unlike booting right from the BIOS, you won't be able to use 16-bit code including most of the PC-BIOS interrupts. If you need to be in real mode you would have to change back to real mode manually, or create a VM86 task (the latter isn't trivial).
This is an important consideration since some of the code you linked to in MikeOS is 16-bit.
这篇关于创建一个简单的多重内核加载GRUB2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!