Esta pagina se ve mejor con JavaScript habilitado

MercuryOS Construcción de la imagen del SO mediante gmake

 ·  🎃 kr0m

Hasta ahora cada vez que realizabamos alguna modificación en el bootloader o en nuestro SO teníamos que recompilar y linkar los ficheros a mano, a lo largo de la historia programadores anteriores ya se vieron en tal tesitura así que crearon una herramienta llamada make, mediante make podremos indicar dependencias entre ficheros y comandos a ejecutar en caso de que alguno de ellos haya sido modificado.

Antes de comenzar es recomendable que leas estos artículos anteriores:


Como vamos a utilizar el make de GNU tendremos que instalar gmake:

pkg install png gmake pkgconf

Un ejemplo básico podría ser este:

vi Makefile

kernel.o : kernel.c
    cc -m32 -ffreestanding -c kernel.c -o kernel.o

NOTA: Cuidado con los espacios y tabulaciones porque Make requiere que todo sean tabulaciones reales.

Podemos compilar el kernel con el comando:

gmake kernel.o
cc -m32 -ffreestanding -c kernel.c -o kernel.o

La belleza de Make reside en que solo ejecutará la orden si alguna de las dependencias ha sido modificada, si volvemos a ejecutar make nos indicará que ya tenemos la última versión y no es necesario recompilar:

gmake kernel.o

gmake: 'kernel.o' is up to date.

Un Makefile mas avanzados podría ser este, al ejecutar make kernel.bin si alguna de las dependencias requiere ser recompilada lo hará de forma automática:

vi Makefile

# Build the kernel binary
kernel.bin : kernel_entry.o kernel.o
    ld -o kernel.bin -Ttext 0x1000 kernel_entry.o kernel.o --oformat binary

# Build the kernel object file
kernel.o : kernel.c
    cc -m32 -ffreestanding -c kernel.c -o kernel.o

# Build the kernel entry object file .
kernel_entry.o : kernel_entry.asm
    nasm kernel_entry.asm -f elf -o kernel_entry.o

Make soporta varias variables especiales, de este modo el mantenimiento del script será mas sencillo conforme crezca el proyecto:

  • $^: Indica todas las dependencias
  • $<: Indica la primera dependencia
  • $@: Indica el fichero a generar

A continucación la versión final de mi Makefile:

vi Makefile

# Run Qemu to simulate booting of our code .
run : all
    qemu-system-x86_64 os-image

# Compile all elements
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_sect.bin 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.bin : kernel_entry.o kernel.o
    ld -o kernel.bin -Ttext 0x1000 $^ --oformat binary

# Build our kernel object file .
kernel.o : kernel.c
    cc -m32 -ffreestanding -c $< -o $@

# Build our kernel entry object file .
kernel_entry.o : kernel_entry.asm
    nasm $< -f elf -o $@

# Assemble the boot sector to raw machine code
# The -I options tells nasm where to find our useful assembly
# routines that we include in boot_sect . asm
boot_sect.bin : boot_sect.asm
    nasm -f bin $< -o $@

# Clear away all generated files .
clean :
    rm -fr *.bin *.dis *.o os-image *.map

# Disassemble our kernel - might be useful for debugging .
kernel dis : kernel.bin
    ndisasm -b 32 $< > $@

Hacemos limpieza y comprobamos que todo el proceso funcione:

gmake clean
rm -fr *.bin *.dis *.o os-image *.map

Compilamos todo lo necesario, generamos la imagen de disco y la cargamos en Qemu, todo esto en un solo comando.

gmake run

Excelente todo habrá funcionado como la seda y tendremos corriendo la última versión de nuestro bootloader/kernel en Qemu, cada vez que queramos modificar algo tan solo debemos darle a make run y nuestro script se encargará de recompilar todo lo necesario.

Si te ha gustado el artículo puedes invitarme a un RedBull aquí