【问题标题】:GNU assembly boot loader won't bootGNU 程序集引导加载程序无法引导
【发布时间】:2016-10-25 22:13:44
【问题描述】:

我一直在学习以下教程:

http://intermezzos.github.io/book/multiboot-headers.html

不幸的是,它使用 NASM 而不是 GNU Assembler,因为我通常使用 GNU 工具链,所以我宁愿使用它。本教程有以下简单的 hello world 程序:

section .multiboot_header
header_start:
    dd 0xe85250d6                ; magic number
    dd 0                         ; protected mode code
    dd header_end - header_start ; header length

    ; checksum
    dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start))

    ; required end tag
    dw 0    ; type
    dw 0    ; flags
    dd 8    ; size
header_end:

它提供了多重引导标头以及以下内容:

global start
section .text
bits 32
start:
    mov word [0xb8000], 0x0248 ; H
    mov word [0xb8002], 0x0265 ; e
    mov word [0xb8004], 0x026c ; l
    mov word [0xb8006], 0x026c ; l
    mov word [0xb8008], 0x026f ; o
    mov word [0xb800a], 0x022c ; ,
    mov word [0xb800c], 0x0220 ;
    mov word [0xb800e], 0x0277 ; w
    mov word [0xb8010], 0x026f ; o
    mov word [0xb8012], 0x0272 ; r
    mov word [0xb8014], 0x026c ; l
    mov word [0xb8016], 0x0264 ; d
    mov word [0xb8018], 0x0221 ; !
    hlt

这些与以下链接器.ld 文件链接在一起:

ENTRY(start)

SECTIONS {
    . = 1M;

    .boot :
    {
        /* ensure that the multiboot header is at the beginning */
        *(.multiboot_header)
    }

    .text :
    {
        *(.text)
    }
}

编译和运行所有这些可以通过以下方式完成:

nasm -f elf64 boot_nasm.asm
nasm -f elf64 multiboot_header_nasm.asm
ld --nmagic --output=kernel.bin --script=linker.ld multiboot_header_nasm.o boot_nasm.o

查看教程如何使用内核制作 iso 映像以及如何使用 qemu 启动。现在我将源代码移植如下以使用 GNU 汇编器:

    .section .multiboot_header
header_start:
    .long 0xe85250d6             # magic number
    .long 0                      # protected mode code
    .long header_end - header_start

    # checksum
    .long 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start))

    # required end tag
    .word 0 # type
    .word 0 # flags
    .long 8 # size
header_end:

    .text
    .global start
    .code32
start:
    movw $0x0248, (0xb8000)      # H
    movw $0x0265, (0xb8002)      # e
    movw $0x026c, (0xb8004)      # l
    movw $0x026c, (0xb8006)      # l
    movw $0x026f, (0xb8008)      # o
    movw $0x022c, (0xb800a)      # ,
    movw $0x0220, (0xb800c)      #
    movw $0x0277, (0xb800e)      # w
    movw $0x026f, (0xb8010)      # o
    movw $0x0272, (0xb8012)      # r
    movw $0x026c, (0xb8014)      # l
    movw $0x0264, (0xb8016)      # d
    movw $0x0221, (0xb8018)      # !
    hlt

我已通过以下方式将它们编译并链接在一起:

as boot.S -o boot.o
as multiboot_header.S -o multiboot_header.o
ld --nmagic --output=kernel.bin --script=linker.ld multiboot_header.o boot.o

objdump 表明两种情况输出的kernel.bin 对于每个段中的代码都是相同的。唯一的区别是段所在的偏移量略有不同。主要区别在于 nasm 版本可以启动,而 GNU 汇编器版本不能

GNU 汇编器版本给了我error: no multiboot header found.

这是两个创建的二进制文件的 objdump 输出:

 % objdump -D -s kernel_nasm.bin 

kernel_nasm.bin:     file format elf64-x86-64

Contents of section .boot:
 100000 d65052e8 00000000 18000000 12afad17  .PR.............
 100010 00000000 08000000                    ........        
Contents of section .text:
 100020 66c70500 800b0048 0266c705 02800b00  f......H.f......
 100030 650266c7 0504800b 006c0266 c7050680  e.f......l.f....
 100040 0b006c02 66c70508 800b006f 0266c705  ..l.f......o.f..
 100050 0a800b00 2c0266c7 050c800b 00200266  ....,.f...... .f
 100060 c7050e80 0b007702 66c70510 800b006f  ......w.f......o
 100070 0266c705 12800b00 720266c7 0514800b  .f......r.f.....
 100080 006c0266 c7051680 0b006402 66c70518  .l.f......d.f...
 100090 800b0021 02f4                        ...!..          

Disassembly of section .boot:

0000000000100000 <header_start>:
  100000:   d6                      (bad)  
  100001:   50                      push   %rax
  100002:   52                      push   %rdx
  100003:   e8 00 00 00 00          callq  100008 <header_start+0x8>
  100008:   18 00                   sbb    %al,(%rax)
  10000a:   00 00                   add    %al,(%rax)
  10000c:   12 af ad 17 00 00       adc    0x17ad(%rdi),%ch
  100012:   00 00                   add    %al,(%rax)
  100014:   08 00                   or     %al,(%rax)
    ...

Disassembly of section .text:

0000000000100020 <start>:
  100020:   66 c7 05 00 80 0b 00    movw   $0x248,0xb8000(%rip)        # 1b8029 <start+0xb8009>
  100027:   48 02 
  100029:   66 c7 05 02 80 0b 00    movw   $0x265,0xb8002(%rip)        # 1b8034 <start+0xb8014>
  100030:   65 02 
  100032:   66 c7 05 04 80 0b 00    movw   $0x26c,0xb8004(%rip)        # 1b803f <start+0xb801f>
  100039:   6c 02 
  10003b:   66 c7 05 06 80 0b 00    movw   $0x26c,0xb8006(%rip)        # 1b804a <start+0xb802a>
  100042:   6c 02 
  100044:   66 c7 05 08 80 0b 00    movw   $0x26f,0xb8008(%rip)        # 1b8055 <start+0xb8035>
  10004b:   6f 02 
  10004d:   66 c7 05 0a 80 0b 00    movw   $0x22c,0xb800a(%rip)        # 1b8060 <start+0xb8040>
  100054:   2c 02 
  100056:   66 c7 05 0c 80 0b 00    movw   $0x220,0xb800c(%rip)        # 1b806b <start+0xb804b>
  10005d:   20 02 
  10005f:   66 c7 05 0e 80 0b 00    movw   $0x277,0xb800e(%rip)        # 1b8076 <start+0xb8056>
  100066:   77 02 
  100068:   66 c7 05 10 80 0b 00    movw   $0x26f,0xb8010(%rip)        # 1b8081 <start+0xb8061>
  10006f:   6f 02 
  100071:   66 c7 05 12 80 0b 00    movw   $0x272,0xb8012(%rip)        # 1b808c <start+0xb806c>
  100078:   72 02 
  10007a:   66 c7 05 14 80 0b 00    movw   $0x26c,0xb8014(%rip)        # 1b8097 <start+0xb8077>
  100081:   6c 02 
  100083:   66 c7 05 16 80 0b 00    movw   $0x264,0xb8016(%rip)        # 1b80a2 <start+0xb8082>
  10008a:   64 02 
  10008c:   66 c7 05 18 80 0b 00    movw   $0x221,0xb8018(%rip)        # 1b80ad <start+0xb808d>
  100093:   21 02 
  100095:   f4                      hlt    

这是使用 GNU 汇编器创建的版本:

 % objdump -D -s kernel.bin     

kernel.bin:     file format elf64-x86-64

Contents of section .boot:
 100000 d65052e8 00000000 18000000 12afad17  .PR.............
 100010 00000000 08000000                    ........        
Contents of section .text:
 100000 66c70500 800b0048 0266c705 02800b00  f......H.f......
 100010 650266c7 0504800b 006c0266 c7050680  e.f......l.f....
 100020 0b006c02 66c70508 800b006f 0266c705  ..l.f......o.f..
 100030 0a800b00 2c0266c7 050c800b 00200266  ....,.f...... .f
 100040 c7050e80 0b007702 66c70510 800b006f  ......w.f......o
 100050 0266c705 12800b00 720266c7 0514800b  .f......r.f.....
 100060 006c0266 c7051680 0b006402 66c70518  .l.f......d.f...
 100070 800b0021 02f4                        ...!..          

Disassembly of section .boot:

0000000000100000 <header_start>:
  100000:   d6                      (bad)  
  100001:   50                      push   %rax
  100002:   52                      push   %rdx
  100003:   e8 00 00 00 00          callq  100008 <header_start+0x8>
  100008:   18 00                   sbb    %al,(%rax)
  10000a:   00 00                   add    %al,(%rax)
  10000c:   12 af ad 17 00 00       adc    0x17ad(%rdi),%ch
  100012:   00 00                   add    %al,(%rax)
  100014:   08 00                   or     %al,(%rax)
    ...

Disassembly of section .text:

0000000000100000 <start>:
  100000:   66 c7 05 00 80 0b 00    movw   $0x248,0xb8000(%rip)        # 1b8009 <header_end+0xb7ff1>
  100007:   48 02 
  100009:   66 c7 05 02 80 0b 00    movw   $0x265,0xb8002(%rip)        # 1b8014 <header_end+0xb7ffc>
  100010:   65 02 
  100012:   66 c7 05 04 80 0b 00    movw   $0x26c,0xb8004(%rip)        # 1b801f <header_end+0xb8007>
  100019:   6c 02 
  10001b:   66 c7 05 06 80 0b 00    movw   $0x26c,0xb8006(%rip)        # 1b802a <header_end+0xb8012>
  100022:   6c 02 
  100024:   66 c7 05 08 80 0b 00    movw   $0x26f,0xb8008(%rip)        # 1b8035 <header_end+0xb801d>
  10002b:   6f 02 
  10002d:   66 c7 05 0a 80 0b 00    movw   $0x22c,0xb800a(%rip)        # 1b8040 <header_end+0xb8028>
  100034:   2c 02 
  100036:   66 c7 05 0c 80 0b 00    movw   $0x220,0xb800c(%rip)        # 1b804b <header_end+0xb8033>
  10003d:   20 02 
  10003f:   66 c7 05 0e 80 0b 00    movw   $0x277,0xb800e(%rip)        # 1b8056 <header_end+0xb803e>
  100046:   77 02 
  100048:   66 c7 05 10 80 0b 00    movw   $0x26f,0xb8010(%rip)        # 1b8061 <header_end+0xb8049>
  10004f:   6f 02 
  100051:   66 c7 05 12 80 0b 00    movw   $0x272,0xb8012(%rip)        # 1b806c <header_end+0xb8054>
  100058:   72 02 
  10005a:   66 c7 05 14 80 0b 00    movw   $0x26c,0xb8014(%rip)        # 1b8077 <header_end+0xb805f>
  100061:   6c 02 
  100063:   66 c7 05 16 80 0b 00    movw   $0x264,0xb8016(%rip)        # 1b8082 <header_end+0xb806a>
  10006a:   64 02 
  10006c:   66 c7 05 18 80 0b 00    movw   $0x221,0xb8018(%rip)        # 1b808d <header_end+0xb8075>
  100073:   21 02 
  100075:   f4                      hlt    

现在我正在查看它,看起来 GNU Assembler 版本具有重叠的 .boot 和 .text 段,因此 grub 在加载文件时可能会用 .text 部分的数据覆盖 .boot 部分。有人知道如何解决这个问题吗?

【问题讨论】:

  • 希望您的引导加载程序能告诉您为什么它不喜欢您的内核。或者它确实喜欢它,并且它启动并且问题在其他地方,在这种情况下使用调试器来查看你的代码有多远。还要指定您使用的引导加载程序。
  • “唯一的区别是段所在的偏移量略有不同” - 如果这真的是唯一的区别,那么这一定是 GAS 版本无法启动的根本原因。究竟有什么区别?
  • 查看教程中的grub.cfg文件以及qemu引导的iso镜像是如何生成的。我已经用收到的错误消息更新了我的问题。这个特定的错误消息来自 grub,因此在这两种情况下它都会通过 BIOS。
  • 我在 objdump 输出中添加了一些额外的数据。我认为关键是在 GNU Assembler 版本中,这两个部分重叠,因此后者可能会在加载内核时在内存中被前者覆盖。
  • Peter Cordes 一定是在睡觉,因为他可能会向您指出 movw $0x0248, (0xb8000) 会导致 32/64 位代码中的 LCP 停顿。使用 16 位立即数是一个问题,因为它需要一个解码速度较慢的长度变化前缀。最好一次将 4 个字节移动到显存,如果必须移动 2 个字节,则将 32 位立即数(代表您的 16 位值)移动到 32 位寄存器中并移动 32 位的下半部分位寄存器到显存。 mov $0x0248, %eax mov %ax, (0xb8000)movw $0x0248, (0xb8000) 好。

标签: assembly nasm ld boot gnu-assembler


【解决方案1】:

除了 Jester 指出的对齐问题之外,我认为链接器脚本是可疑的。它将两个不同的输入部分放入两个不同的输出部分,这可能不是您想要的,并在一定程度上解释了您通过objdump 看到的差异。请尝试以下方法:

ENTRY(start)

SECTIONS {
    . = 1M;

    .text :
    {
        /* ensure that the multiboot header is at the beginning */
        *(.multiboot_header)
        *(.text)
    }
}

或者,强制gas 使“.multiboot_header”成为multiboot_header.s 中的可分配 部分:

.section .multiboot_header, "a"

【讨论】:

  • 不管是不是两个section,也不需要是可分配的,见鬼,它甚至不需要是一个section。它只需要在文件中,正确对齐,仅此而已。
  • @Jester 也许,但 Nasm 版本清楚地使它可加载。实际多重引导规范中的示例代码也是如此。由于 OP 正在遵循教程,因此教程的其余部分可能需要这样做。将这两个部分放在一起作为一个输出部分可以非常清楚最终的布局是什么,这有时很高兴知道。
  • @davmac :但是,如果您阅读了多引导规范(与那里的代码和博客无关),它会清楚地表明它只需要出现在前 8k 字节中并与 4 字节对齐。布局、部分和它们的属性在这个意义上没有意义。 GRUB 将打开文件并开始扫描以查找多重引导标头,无论它位于第一个 8k 中的哪个位置,或者它是如何放置在那里的。只要将多重引导标头放置在前 8k 中并以 4 字节对齐,您就可以对汇编器/链接器使用任何您想要的机制。
  • @MichaelPetch 您所说的绝不与我的回答相矛盾。我只是指出,拥有两个独立的输出部分可能不是 OP 想要的,特别是看到它与 Nasm 发生的情况不同。我认为指出这一点是合理的。
【解决方案2】:

多重引导规范说:

Multiboot 标头必须完全包含在第一个 8192 字节的 OS 映像,并且必须是长字(32 位)对齐的。

objdump 给我们:

Idx Name              Size      File off  Algn
  3 .multiboot_header 00000018  00000040  2**0 CONTENTS, READONLY

注意Algn=2**0

解决方案:指定对齐方式:

.section .multiboot_header
.balign 4
header_start:

nasm 版本也应该这样做,由于文件布局,它在那里工作只是偶然。

【讨论】:

  • 很高兴知道这一点。在我开始深入研究规范之前,我会花一些时间,因为原始文件中没有任何内容可以指出这个问题。谢谢!
  • @del 这是否完全解决了问题?如果确实如此,我会有点惊讶。请参阅下面的答案。
  • 现在可以了。但是,现在按照您的建议更改 linker.ld 文件会使部分偏移量不重叠,因此偏移量现在与 NASM 版本的最终结果一致。
猜你喜欢
  • 1970-01-01
  • 2016-10-22
  • 1970-01-01
  • 2014-10-03
  • 2021-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多