GNU GRUB 为 Multiboot2 提供“错误:不支持的标签:0xc”

Jen*_*ger 2 x86 assembly osdev multiboot grub2

我尝试将以下代码用于 Multiboot2 兼容内核的标头,但是当我multiboot2在 grub 中尝试该命令时,它给出了以下错误消息:

错误:不支持的标签:0xc

我的 Multiboot2 标头定义为:

section .multiboot align=4096
mbhead: dd 0xe85250d6
        dd 0
        dd 76
        dd 0 - 76 - 0xe85250d6                  ; TODO the linker and assembler get angry if we calculate this with symbols, we need to do this by hand
        dw 1                                    ; multiboot information request
        dw 0
        dd 20
        dd 1
        dd 2
        dd 6
        dw 4                                    ; console flags
        dw 0
        dd 12
        dd 0x3
        dw 5                                    ; framebuffer settings
        dw 1
        dd 12
        dd 80
        dd 25
        dd 0
        dw 0                                    ; last tag
        dw 0
        dd 8
Run Code Online (Sandbox Code Playgroud)

我的项目存储库提供了完整的源代码。我用make test_longmode.iso. 我用 QEMU 测试。

导致标签错误的问题是什么,我该如何解决?

Mic*_*tch 6

主要问题

GRUB 错误:

错误:不支持的标签:0xc

是因为您没有按照Multiboot2 规范对每个标签进行 8 字节对齐:

3.1.3 通用标签结构

标签构成了一个结构缓冲区,在必要时相互填充,以便每个标签从 8 字节对齐的地址开始。标签以“0”类型和“8”大小的标签结束。每个结构都有以下格式

为了轻松解决这个问题,您可以align 8在每个标签之前使用,您可以让汇编程序处理对齐。这种对齐不会计算到标签的长度中。

您可以更好地计算校验和,以免通过让 NASM 使用截断来引起警告。这一行:

dd 0 - 76 - 0xe85250d6
Run Code Online (Sandbox Code Playgroud)

可以更好地表达为:

dd 0x100000000 - 76 - 0xe85250d6
Run Code Online (Sandbox Code Playgroud)

代码可读性

我认为关于这段代码片段的第一件事是它是多么不可读。引起我注意的是这条评论:

TODO 如果我们用符号计算它,链接器和汇编器会生气,我们需要手动完成

您似乎手动计算了值,因为您在尝试以其他方式进行计算时遇到了问题。在这里有帮助的 NASM 指令是EQU 指令

3.2.4 EQU:定义常量

EQU 将符号定义为给定的常量值:当使用 EQU 时,源代码行必须包含一个标签。EQU 的作用是将给定的标签名称定义为其(仅)操作数的值。这个定义是绝对的,以后不能改变。所以,例如,

message         db      'hello, world' 
msglen          equ     $-message
Run Code Online (Sandbox Code Playgroud)

将 msglen 定义为常量 12。 msglen 以后可能不会重新定义。这也不是预处理器定义:msglen 的值被评估一次,在定义点使用 $ 的值(有关 $ 的解释,请参阅第 3.5 节),而不是在任何被引用的地方评估并使用$ 在参考点。

通过使用 EQU,我们可以使代码更具可读性。

其次,您可以在每个标签的开头和结尾添加标签,然后让汇编程序为您计算每个标签的长度。长度是每个标签的结束标签和开始标签之间的差值。


改进的代码

具有上述建议更改的 NASM 汇编代码可能如下所示:

MB2_ARCH  EQU 0                                 ; 0 = x86/x86-64
MB2_LEN   EQU (mbend-mbhead)
MB2_MAGIC EQU 0xe85250d6

section .multiboot align=4096
mbhead:
        dd MB2_MAGIC                            ; Multiboot2 magic number
        dd MB2_ARCH                             ; Architecture
        dd MB2_LEN                              ; Multiboot header length
        dd 0x100000000 - MB2_LEN - MB2_ARCH - MB2_MAGIC
                                                ; Checksum

mb2_tag_info_start:
        dw 1                                    ; multiboot information request
        dw 0
        dd mb2_tag_info_end - mb2_tag_info_start
        dd 1
        dd 2
        dd 6
mb2_tag_info_end:

        align 8
mb2_tag_console_start:
        dw 4                                    ; console flags
        dw 0
        dd mb2_tag_console_end - mb2_tag_console_start
        dd 0x3
mb2_tag_console_end:

        align 8
mb2_tag_fb_start:
        dw 5                                    ; framebuffer settings
        dw 1
        dd mb2_tag_fb_end - mb2_tag_fb_start
        dd 80
        dd 25
        dd 0
mb2_tag_fb_end:

        align 8
mb2_tag_end_start:
        dw 0                                    ; last tag
        dw 0
        dd mb2_tag_end_end - mb2_tag_end_start
mb2_tag_end_end:

mbend:
Run Code Online (Sandbox Code Playgroud)

这可以进一步改进,但即使这样看起来也比原始代码片段更具可读性和自我记录性。如果您更改标签或标题的大小,则会为您计算所有长度。如果大小发生变化,该align 8指令将为您计算正确的对齐方式。这使得代码更易于维护。