I switched my computer recently and since then, my makefile chain spits out a 512 byte binary with only 0x00s or the bootloader, but without everything else. I created the following as MRE:
boot.asm:
BITS 16
SECTION boot
GLOBAL _entry
EXTERN _start
_entry:
mov [disk],dl
mov ah, 0x2 ; read sectors
mov al, 6 ; amount = 6
mov ch, 0 ; zylinder = 0
mov cl, 2 ; first sector to read = 2
mov dh, 0 ; head = 0 (up)
mov dl, [disk] ; disk
mov bx, _start ; segment:offset address
int 0x13
cli
lgdt [GDT_POINTER]
mov eax, cr0
or al, 1
mov cr0, eax
mov ax, DATA_SEGMENT
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp CODE_SEGMENT:_start
disk: DB 0x00
GDT_POINTER:
DW GDT_EXIT - GDT_ENTRY
DD GDT_ENTRY
CODE_SEGMENT EQU GDT_CODE - GDT_ENTRY
DATA_SEGMENT EQU GDT_DATA - GDT_ENTRY
GDT_ENTRY:
DQ 0x00
GDT_CODE:
DW 0xffff
DW 0x0000
DB 0x00
DB 0x9a
DB 0xcf
DB 0x00
GDT_DATA:
DW 0xffff
DW 0x0000
DB 0x00
DB 0x92
DB 0xcf
DB 0x00
GDT_EXIT:
TIMES 510 - ($ - $$) DB 0x00
DW 0xAA55
kernel.c:
int _main() {
while(1) {}
}
linker16.ld:
ENTRY(_entry);
OUTPUT_FORMAT(elf32-i386);
OUTPUT_ARCH(i386);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
*(boot)
*(.text)
}
.data :
{
*(.bss);
*(.bss*);
*(.data);
*(.rodata*);
*(COMMON);
}
/DISCARD/ :
{
*(.note*);
*(.iplt*);
*(.igot*);
*(.rel*);
*(.comment);
}
}
linker32.ld:
ENTRY(_main);
OUTPUT_FORMAT(elf32-i386);
OUTPUT_ARCH(i386);
SECTIONS
{
. = 0x7E00;
.text : AT(0x7E00)
{
*(.text)
}
.data :
{
*(.bss);
*(.bss*);
*(.data);
*(.rodata*);
*(COMMON);
}
/DISCARD/ :
{
*(.note*);
*(.iplt*);
*(.igot*);
*(.rel*);
*(.comment);
}
}
Makefile:
all:
nasm -O32 -f elf -o boot.o boot.asm
gcc -m32 -c -g -ffreestanding -nostdlib -nostdinc -Wall -Werror -o kernel.o kernel.c
ld -static -nostdlib -build-id=none -relocatable -T linker16.ld -o boot.elf boot.o
ld -static -nostdlib -build-id=none -relocatable -T linker32.ld -o kernel.elf kernel.o
objcopy -O binary boot.elf boot.bin
objcopy -O binary kernel.elf kernel.bin
cat boot.bin kernel.bin > sys.bin~
rm *.o
rm *.elf
rm *.bin
cat sys.bin~ > sys.bin
rm sys.bin~
qemu-system-i386 sys.bin
qemu:
qemu-system-i386 sys.bin
The expected output is a blank screen, with a GDT set a few bytes after 0x7C00 when looked into compat monitor ("info registers" output). Instead it is stuck in a bootloop, since the bootloader is correctly compiled but everything after it (the while loop) is missing. Until the .o file, everything is as expected but the .elf and .bin are too short. Does someone have a solution? The versions i use are:
NASM version 2.14.02
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
GNU ld & objcopy (GNU Binutils for Ubuntu) 2.34
EDIT: The updated code instead produces a mess of zeros, 60 times the size it should be. The magic number is placed correctly but the kernel part is still unusable.
EDIT 2: I found out by trial and error that removing the -relocatable argument for the linker clears out most of the zeros, yet it still doesn't work as expected and sticks in a bootloop.
EDIT 3: If anyone gets the same problem as i did, i want the code to actually work. In the above code i fixed the GDT, since i made a mistake in it. I narrowed all DBs down to DD, but forgot that little endian reverses all bytes in it, therefore the used bit in all GDT descriptors was set to zero, making the jump impossible. In combination with fuz's answer, it is possible to get this nightmare running now.