Search code examples
ldqemuosdev

Boot sector linked with ld has QEMU print "Hard Disk Boot Failed: not a bootable disk"


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?


Solution

  • 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’.