A few friends and I are working on a very simple kernel project. At the moment, we have a bootloader (using GNU assembly) and a simple kernel (C with no stdlib). I've been tasked with setting up a QEMU simulation to test the OS, but ran into several issues along the way.
I've gotten QEMU to boot, and I have created a bootable disk image.
Makefile:
%.o: %.S
as -o $@ -c $<
bootsect: boot.o
ld -o ./bin/bootsect.bin $^ -nostdlib -Ttext 0x7C00
img: bootsect
dd if=/dev/zero of=boot.img bs=512 count=2880
dd if=./bin/bootsect.bin of=boot.img conv=notrunc bs=512 count=1
I tried to run it with qemu-system-i386 -drive format=raw,file=boot.img
I have also tried various ways of booting the image, but always end up with the same error:
Booting from Hard Disk:
Boot failed: not a bootable disk
Here is the boot loader code if needed:
.code16
.org 0
.text
.global _start
_start:
cli
xorw %ax, %ax
movw %ax, %es
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw $0x3000, %sp
sti
movw $hello, %si
call print
print:
xorb %bh, %bh
movb $0x0E, %ah
lodsb
cmpb $0, %al
je 1f
int $0x10;
jmp print
1:
ret
hello:
.asciz "HELLO WORLD\n"
.org 510
.word 0xAA55
Where is the problem?
The linker is creating an ELF executable, not a raw binary. You need to extract the raw code contained in the .text
section into a stand-alone file, without any wrapping object code format. Only then you can splice that file into the disk image as the boot sector.
There are two ways you can go about doing this. One is to add an extra step to the Makefile that invokes objcopy
:
bootsect: bin/bootsect.bin
bin/bootsect.elf: boot.o
ld -o $@ $^ -nostdlib -Ttext=0x7c00
%.bin: %.elf
objcopy -Obinary -j.text $< $@
The other is to use a linker script. Put the following in a file named bootsect.ld
:
OUTPUT_FORMAT("binary")
SECTIONS {
.image 0x7c00 : {
*(.text)
}
}
And in the Makefile:
bin/bootsect.bin: boot.o bootsect.ld
ld -o $@ boot.o -nostdlib -Tbootsect.ld
To check that this works, run file bin/bootsect.bin
. It should output ‘DOS/MBR boot sector’.