简体   繁体   English

创建一个用grub2加载的简单多引导内核

[英]Creating a simple multiboot kernel loaded with grub2

I'm trying to follow the instructions here to build a simple OS kernel: http://mikeos.sourceforge.net/write-your-own-os.html 我试图按照这里的说明构建一个简单的操作系统内核: http//mikeos.sourceforge.net/write-your-own-os.html

Except, instead of booting from a floppy, I want to create a grub-based ISO image and boot a multiboot CD in the emulator. 除了从软盘启动,我想创建一个基于grub的ISO映像并在模拟器中启动多重启动CD。 I've added the following to the source listed at that page, for the multiboot header: 对于多引导头,我已将以下内容添加到该页面上列出的源:

MBALIGN     equ  1<<0                   ; align loaded modules on page boundaries
MEMINFO     equ  1<<1                   ; provide memory map
FLAGS       equ  MBALIGN | MEMINFO      ; this is the Multiboot 'flag' field
MAGIC       equ  0x1BADB002             ; 'magic number' lets bootloader find the header
CHECKSUM    equ -(MAGIC + FLAGS)        ; checksum of above, to prove we are multiboot
section .multiboot
align 4
    dd MAGIC
    dd FLAGS
    dd CHECKSUM

and I'm doing the following to create the image: 我正在做以下事情来创建图像:

nasm -felf32 -o init.bin  init.s
cp init.bin target/boot/init.bin
grub2-mkrescue -o init.iso target/

Then I run qemu to boot it: 然后我运行qemu来启动它:

qemu-system-x86_64 -cdrom ./init.iso 

After selecting 'myos' from the boot menu, I get the error 从启动菜单中选择“myos”后,我收到错误消息

error: invalid arch-dependent ELF magic

What does that mean, and how can I fix it? 这意味着什么,我该如何解决? I tried messing with the elf format, but only -felf32 seems to work... 我试过搞乱精灵格式,但只有-felf32似乎有效......

GRUB supports ELF32 and flat binaries. GRUB支持ELF32和扁平二进制文件。 Your header though implicitly says that you are providing an ELF binary. 你的标题隐含地表示你提供的是ELF二进制文件。

Using Flat Binary with Multiboot 使用Flat Binary和Multiboot

If you wish to tell the Multiboot loader (GRUB) that you are using a flat binary you must set bit 16 to 1: 如果您希望告诉Multiboot加载程序(GRUB)您使用的是平面二进制文件,则必须将第16位设置为1:

MULTIBOOT_AOUT_KLUDGE    equ  1 << 16
                              ;FLAGS[16] indicates to GRUB we are not
                              ;an ELF executable and the fields
                              ;header address,load address,load end address,
                              ;bss end address, and entry address will be
                              ;available in our Multiboot header

It isn't as simple as just specifying this flag. 它并不像指定这个标志那么简单。 You must provide a complete Multiboot header that provides the Multiboot loader the information to load our binary into memory. 您必须提供完整的Multiboot标头,为Multiboot加载程序提供将二进制文件加载到内存中的信息。 When using ELF format this information is in the ELF header that precedes our code so didn't have to be explicitly provided. 使用ELF格式时,此信息位于代码之前的ELF标头中,因此不必明确提供。 The Multiboot header is defined in the GRUB documentation in great detail. Multiboot标头在GRUB文档中详细定义。

When using NASM with -f bin it is important to note that we need to specify the origin point for our code. 使用带-f bin NASM时 ,请务必注意我们需要为代码指定原点。 Multiboot loaders load our kernel at physical address 0x100000 . 多引导加载程序在物理地址0x100000处加载我们的内核。 We must specify in our assembler file that our origin point is 0x100000 so that proper offsets etc. will get generated in our final flat binary image. 我们必须在汇编程序文件中指定我们的原点是0x100000以便在我们的最终平面二进制图像中生成适当的偏移等。

This is an example stripped and modified from one of my own projects that provides a simple header. 这是一个从我自己的项目中剥离和修改的示例,它提供了一个简单的标题。 The call to _Main is set up like a C call in the example, but you don't have to do it that way. _Main的调用在_Main中设置为C调用,但您不必这样做。 Usually I call into a function that takes a couple parameters on the stack (using C calling convention). 通常我调用一个函数,在堆栈上使用几个参数(使用C调用约定)。

[BITS 32]
[global _start]
[ORG 0x100000]                ;If using '-f bin' we need to specify the
                              ;origin point for our code with ORG directive
                              ;multiboot loaders load us at physical 
                              ;address 0x100000

MULTIBOOT_AOUT_KLUDGE    equ  1 << 16
                              ;FLAGS[16] indicates to GRUB we are not
                              ;an ELF executable and the fields
                              ;header address, load address, load end address;
                              ;bss end address and entry address will be available
                              ;in Multiboot header
MULTIBOOT_ALIGN          equ  1<<0   ; align loaded modules on page boundaries
MULTIBOOT_MEMINFO        equ  1<<1   ; provide memory map

MULTIBOOT_HEADER_MAGIC   equ  0x1BADB002
                              ;magic number GRUB searches for in the first 8k
                              ;of the kernel file GRUB is told to load

MULTIBOOT_HEADER_FLAGS   equ  MULTIBOOT_AOUT_KLUDGE|MULTIBOOT_ALIGN|MULTIBOOT_MEMINFO
CHECKSUM                 equ  -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)

KERNEL_STACK             equ  0x00200000  ; Stack starts at the 2mb address & grows down

_start:
        xor    eax, eax                ;Clear eax and ebx in the event
        xor    ebx, ebx                ;we are not loaded by GRUB.
        jmp    multiboot_entry         ;Jump over the multiboot header
        align  4                       ;Multiboot header must be 32
                                       ;bits aligned to avoid error 13
multiboot_header:
        dd   MULTIBOOT_HEADER_MAGIC    ;magic number
        dd   MULTIBOOT_HEADER_FLAGS    ;flags
        dd   CHECKSUM                  ;checksum
        dd   multiboot_header          ;header address
        dd   _start                    ;load address of code entry point
                                       ;in our case _start
        dd   00                        ;load end address : not necessary
        dd   00                        ;bss end address : not necessary
        dd   multiboot_entry           ;entry address GRUB will start at

multiboot_entry:
        mov    esp, KERNEL_STACK       ;Setup the stack
        push   0                       ;Reset EFLAGS
        popf

        push   eax                     ;2nd argument is magic number
        push   ebx                     ;1st argument multiboot info pointer
        call   _Main                   ;Call _Main 
        add    esp, 8                  ;Cleanup 8 bytes pushed as arguments

        cli
endloop:
        hlt
        jmp   endloop

_Main:  
        ret                            ; Do nothing

The Multiboot loader ( GRUB ) generally loads in the first 8k of your file (whether ELF or flat binary), looks for the Multiboot header on a 32 bit boundary. 多引导加载程序( GRUB )通常在文件的前8k(无论是ELF还是平面二进制文件)中加载,在32位边界上查找Multiboot标头。 If bit 16 of the Multiboot header FLAG is clear, it assumes you are providing an ELF image. 如果Multiboot标题FLAG的第16位清除,则假定您提供ELF图像。 It then parses the ELF header to retrieve the information it needs to load your kernel file into memory. 然后它解析ELF头以检索将内核文件加载到内存中所需的信息。 If bit 16 is set then a complete Multiboot header is required so that the loader has the information to read your kernel into memory, perform initialization,and then call into your kernel. 如果设置了第16位,则需要一个完整的Multiboot标头,以便加载器具有将内核读入内存,执行初始化,然后调用内核的信息。

You would then assemble your init.s to a flat binary with something like: 然后,您可以将init.s汇编为平面二进制文件,例如:

nasm -f bin -o init.bin init.s

Using ELF with Multiboot 使用ELF和多重启动

To tie in Jester's comments to your original question, you should have been able to boot with ELF and have it work, but it didn't because of one small detail. 为了将Jester的评论与原始问题联系起来,您应该能够使用ELF启动并让它工作,但它并不是因为一个小细节。 In your example you used this to make init.bin : 在您的示例中,您使用它来生成init.bin

nasm -f elf32 -o init.bin  init.s

When using -f elf32 , NASM generates object files (they aren't executable), that must be linked (with LD for example) to generate a final ELF (ELF32) executable. 当使用-f elf32NASM生成目标文件(它们不可执行),必须链接(例如,使用LD )以生成最终的ELF (ELF32)可执行文件。 It would have probably worked if you had done the assemble and link processes with something like: 如果你用以下方法完成了汇编和链接过程,它可能会有用:

nasm -f elf32 init.s -o init.o 
ld -Ttext=0x100000 -melf_i386 -o init.bin init.o

Please note that when using -f elf32 you must remove the ORG directive from init.s . 请注意,使用-f elf32 ,必须从init.s中删除ORG指令。 The ORG directive only applies when using -f bin . ORG指令仅在使用-f bin时适用。 Multiboot loaders will load us at physical address 0x100000 so we must make sure that the assembled and linked code are generated with that origin point. 多引导加载程序将加载物理地址0x100000因此我们必须确保使用该原点生成汇编和链接代码。 When using -f elf32 we specify the entry point with -Ttext=0x100000 on the linker ( LD ) command line. 使用-f elf32我们在链接器( LD )命令行上使用-Ttext=0x100000指定入口点。 Alternatively the origin point can be set in a linker script. 或者,可以在链接描述文件中设置原点。

Using NASM/LD/OBJCOPY to Generate Flat Binary Images 使用NASM / LD / OBJCOPY生成平面二进制图像

It is possible to use NASM / LD / OBJCOPY together to produce a final flat binary image rather than using -f bin with NASM . 可以一起使用NASM / LD / OBJCOPY来生成最终的平面二进制图像,而不是使用带有NASM的 -f bin If you remove the ORG directive from init.s and use these commands it should generate a flat binary init.bin : 如果从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 binary init.elf init.bin 

In this, NASM is told to generate ELF32 objects. 在此, NASM被告知生成ELF32对象。 We assemble init.s into an ELF object file called init.o . 我们组装init.s到名为init.o一个ELF目标文件。 We can then use the linker ( LD ) to generate an ELF executable from init.o called init.elf . 然后,我们可以使用连接器(LD),产生从init.o称为init.elf ELF可执行文件。 We use a special program called objcopy to strip all the ELF headers off and generate a flat binary executable called init.bin . 我们使用一个名为objcopy的特殊程序来删除所有ELF头文件并生成一个名为init.bin的平面二进制可执行文件。

This is a lot more involved than just using NASM with the -f bin option to generate the flat executable init.bin . 这比使用带有-f bin选项的NASM生成扁平可执行文件init.bin要多得多 Why bother then? 为什么打扰呢? With the method above you can tell NASM to generate debug information that can be utilized by gdb (the GNU debugger). 使用上面的方法,您可以告诉NASM生成可由gdb (GNU调试器)使用的调试信息。 If you attempt to use -g (enable debugging) with NASM using -f bin no debugging information gets generated. 如果您尝试使用-g (启用调试)和NASM使用-f bin不会生成调试信息。 You can generate debug information by altering the assembly sequence this way: 您可以通过以下方式更改装配顺序来生成调试信息:

nasm -g3 -F dwarf -f elf32 init.s -o init.o
ld -Ttext=0x100000 -melf_i386 -o init.elf init.o
objcopy -O binary init.elf init.bin

init.o will contain debug information (in dwarf format) that will be linked with LD into init.elf (which retains the debug information). init.o将包含将与LD链接到init.elf (保留调试信息)的调试信息(以dwarf格式)。 Flat binaries don't contain debug information because they are stripped off when you use objcopy with -O binary . 平面二进制文件不包含调试信息,因为当您将objcopy-O binary一起使用时,它们会被删除。 You can use init.elf if you enable the remote debugging facility in QEMU and use GDB for debugging. 如果在QEMU中启用远程调试工具并使用GDB进行调试,则可以使用init.elf This debug info in init.elf provides information to the debugger that allows you to single step through your code, access variables and labels by name, see the source assembler code etc. init.elf中的此调试信息为调试器提供了信息,允许您单步执行代码,按名称访问变量和标签,查看源汇编代码等。

Besides generating debug information, there is another reason to use the NASM / LD / OBJCOPY process to generate a kernel binary. 除了生成调试信息之外,还有另一个原因是使用NASM / LD / OBJCOPY过程来生成内核二进制文件。 LD is much for configurable. LD非常适合配置。 LD allows a person to create linker scripts that allow you to better tune how things get laid out in the final binary. LD允许一个人创建链接器脚本,使您可以更好地调整在最终二进制文件中布局的方式。 This can be useful for more complex kernels that may contain a mixture of code from different environments (C, Assembler etc). 这对于可能包含来自不同环境(C,汇编程序等)的代码混合的更复杂内核非常有用。 For a small toy kernel it may not be needed, but as a kernel grows in complexity the benefits of using a linker script will become more evident. 对于小型玩具内核,可能不需要它,但随着内核的复杂性增加,使用链接器脚本的好处将变得更加明显。

Remote debugging of QEMU with GDB 使用GDB远程调试QEMU

If you use the method in the previous section to generate debugging information inside an ELF executable ( init.elf ) you can launch QEMU and have it: 如果您使用上一节中的方法在ELF可执行文件( init.elf )中生成调试信息,您可以启动QEMU并拥有它:

  • Load the QEMU environment and halt the CPU at startup. 加载QEMU环境并在启动时暂停CPU。 From man page: 从手册页:

    -S Do not start CPU at startup (you must type 'c' in the monitor). -S不要在启动时启动CPU(必须在监视器中键入“c”)。

  • Make QEMU listen for a GDB remote connection on localhost:1234 . 使QEMU在localhost:1234上侦听GDB远程连接。 From man page: 从手册页:

    -s Shorthand for -gdb tcp::1234, ie open a gdbserver on TCP port 1234. -s -gdb tcp :: 1234的简写,即在TCP端口1234上打开gdbserver。

Then you just have to launch GDB so that it: 然后你只需启动GDB就可以了:

  • Launches GDB with our ELF executable ( init.elf ) with debug symbols and information 使用带有调试符号和信息的ELF可执行文件( init.elf )启动GDB
  • Connects to localhost:1234 where QEMU is listening 连接到本地主机:1234, QEMU正在收听
  • Sets up the debug layout of your choice 设置您选择的调试布局
  • Sets a break point to stop in our kernel (in this example multiboot_entry ) 设置要在我们的内核中停止的断点(在此示例中为multiboot_entry

Here is an example of launching our kernel from the CD-ROM image init.iso , and launching GDB to connect to it: 下面是从CD-ROM映像init.iso启动内核并启动GDB连接到它的示例:

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. 您应该能够像调试普通程序一样使用GDB This assumes you will not be debugging a 16-bit program (kernel). 这假设您不会调试16位程序(内核)。

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). 正如Jester所指出的,当使用像GRUB这样的多引导兼容加载器时,CPU处于32位保护模式(而不是16位实模式)。 Unlike booting right from the BIOS, you won't be able to use 16-bit code including most of the PC-BIOS interrupts. 与从BIOS启动不同,您将无法使用16位代码,包括大多数PC-BIOS中断。 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). 如果您需要处于实模式,则必须手动更改回实模式,或创建VM86任务(后者并非无足轻重)。

This is an important consideration since some of the code you linked to in MikeOS is 16-bit. 这是一个重要的考虑因素,因为您在MikeOS中链接的一些代码是16位。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM