Conforme vaya creciendo nuestro SO cada vez resultará mas inmanegable tener todos los ficheros en un único directorio, debemos definir una estructura, una froma sencilla de organizar el código sería esta, boot -> Cualquier código relacionado con el bootloader o subrutinas utilizadas por este, kernel -> Cualquier código relacionado con el kernel, drivers -> Código de drivers específicos.
Antes de comenzar es recomendable que leas estos artículos anteriores:
- Boot Sector
- Interrupciones
- Memoria
- Pila
- IF-ELSE
- Funciones
- Segmentación de memoria
- Lectura de datos desde disco
- Entrando a modo protegido 32bits
- Compilación, linkado, gestión de la pila y variables en C
- Punteros
- Kernel
- EntryPoint
- Gmake
Creamos la estructura de directorios:
mkdir -p Project/kernel/
mkdir -p Project/drivers/
Copiamos o movemos los ficheros necesarios:
cp kernel.c kernel_entry.asm Project/kernel
Make permite la utilización de wildcards, de este modo los scripts de compilación serán mas cortos y fáciles de mantener.
Podemos expandir los nombres de los ficheros que macheen un patrón:
C_SOURCES = $(wildcard kernel /*.c drivers /*.c)
Podemos crear una lista a partir de ciertos ficheros origen:
OBJ = $ {C_SOURCES : .c = .o}
Para utilizar la lista basta con hacer referencia a ella ${OBJ}:
kernel .bin : kernel/kernel_entry.o ${OBJ}
ld -o $@ -Ttext 0x1000 $^ --oformat binary
Ahora que hemos movido los ficheros hay que modificar los includes de boot_sect.asm
%include "boot/boot_sect_print.asm"
%include "boot/boot_sect_print_hex.asm"
%include "boot/boot_sect_disk.asm"
%include "boot/32bit-gdt.asm"
%include "boot/32bit-print.asm"
%include "boot/32bit-switch.asm"
El Makefile final quedaría del siguiente modo:
# Automatically generate lists of sources using wildcards.
C_SOURCES = $(wildcard kernel/*.c drivers/*.c)
HEADERS = $(wildcard kernel/*.h drivers/*.h)
# TODO : Make sources dep on all header files .
# Convert the *.c filenames to *.o to give a list of object files to build
OBJ = ${C_SOURCES:.c=.o}
# Run Qemu to simulate booting of our code.
run : all
qemu-system-x86_64 os-image
# Run Qemu to simulate booting of our code with debugging session.
# gdb -ex "set architecture i386:x86-64" -ex "set disassembly-flavor intel" -ex "target remote localhost:1234" -ex "symbol-file kernel/kernel.elf"
debug : all kernel/kernel.elf
qemu-system-x86_64 os-image -s -S &
# Used for debugging purposes
kernel/kernel.elf: boot/kernel_entry.o ${OBJ}
ld -o $@ -Ttext 0x1000 $^
# Defaul build target
all: os-image
# This is the actual disk image that the computer loads
# which is the combination of our compiled bootsector and kernel
os-image : boot/boot_sect.bin kernel/kernel.bin
cat $^ > os-image
# This builds the binary of our kernel from two object files :
# - the kernel_entry, which jumps to main () in our kernel
# - the compiled C kernel
kernel/kernel.bin : kernel/kernel_entry.o ${OBJ}
ld -o $@ -Ttext 0x1000 $^ --oformat binary
# Generic rule for compiling C code to an object file
# For simplicity , we C files depend on all header files.
%.o : %.c ${HEADERS}
cc -g -m32 -ffreestanding -c $< -o $@
# Assemble the kernel_entry.
%.o : %.asm
nasm -f elf $< -o $@
%.bin : %.asm
nasm -f bin $< -o $@
clean :
rm -fr *.bin *.dis *.o *.core os-image
rm -fr kernel/*.o boot/*.bin drivers/*.o
NOTA: Una vez mas remarcar la importancia de utilizar tabulaciones reales y no espacios en los ficheros Makefile.
Comprobamos que todo el proceso funcione:
gmake run