【发布时间】: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, %eaxmov %ax, (0xb8000)比movw $0x0248, (0xb8000)好。
标签: assembly nasm ld boot gnu-assembler