I've been going through the following tutorial:
http://intermezzos.github.io/book/multiboot-headers.html
Unfortunately, it uses NASM instead of the GNU Assembler and because I typically work with the GNU toolchain I rather use that instead. The tutorial has the following trivial hello world program:
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:
which provides the multiboot header as well as the following:
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
These are linked together with the the following linker.ld file:
ENTRY(start)
SECTIONS {
. = 1M;
.boot :
{
/* ensure that the multiboot header is at the beginning */
*(.multiboot_header)
}
.text :
{
*(.text)
}
}
Compiling and running all of this can be done by:
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
Check the tutorial how to make an iso image with the kernel and how to boot is using qemu. Now I've ported the source code as follows to use the GNU Assembler:
.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:
and
.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
I've compiled and linked these together by:
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 shows that the kernel.bin that is output by both cases is identical with respect to code in each segment. The only difference is that offsets where the segments are located are slightly different. The main difference is though that the nasm version boots while the GNU assembler version does not.
The GNU assembler version gives me error: no multiboot header found.
Here's the objdump output from the two created binaries:
% 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
Here's the version created by using the GNU assembler:
% 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
Now that I'm looking at it, it looks like the GNU Assembler version has an overlapping .boot and .text segment, so grub while loading the file probably overwrites the .boot section with the data of the .text section. Anyone have an idea how to fix this?
movw $0x0248, (0xb8000)will cause an LCP stall in 32/64 bit code. Using 16-bit immediates is a problem because it needs a length changing prefix that is slower to decode. It is best to move 4 bytes at a time to video memory, and if you have to move 2 bytes, move a 32-bit immediate (representing your 16-bit value) into a 32 bit register and mov the lower half of the 32-bit register to video memory.mov $0x0248, %eaxmov %ax, (0xb8000)is better thanmovw $0x0248, (0xb8000). - Michael Petch