Carga ELF en EFI

1. Problema

Desde hace tiempo tenía el problema que al añadir funciones aleatorias en el kernel, solo por el hecho de añadir sus definiciones (no llamarlas, ni constructores globales ni nada) el bootloader dejaba de cargar el kernel.

2. Hotfix

Esto lo parcheaba de mala manera cambiando la dirección donde se cargaba el kernel, mágicamente y a base de prueba y error:

SECTIONS
{
	. = 0x300000; <---
	/*. = 0x1600000;*/ <---
	_start_addr = .;
	.text : ALIGN(4K)
	{
		*(.text)
	}
	.data : ALIGN(4K)
	{
		*(.data)
	}
	.rodata : ALIGN(4K)
	{
		*(.rodata)
	}
	.bss : ALIGN(4K)
	{
		*(COMMON)
		*(.bss)
	}
	_end_addr = .;
}

3. Motivo

Esto es porque como aprendí a cargar los kernel en EFI está mal, la guia estaba mal, no podemos hacer lo siguiente:

void
load_phdrs(const Elf64_Ehdr *const elf_header, const char *const memory)
{
    for (int i = 0; i < elf_header->e_phnum; i++) {
        Elf64_Phdr *prog_hdr =
          (Elf64_Phdr *)(memory + elf_header->e_phoff + elf_header->e_phentsize * i);
        switch (prog_hdr->p_type) {
            /* Loadable */
            case PT_LOAD: {
                info("loading program header %d at: 0x%p", i, prog_hdr->p_vaddr);

                memcpy((void *)prog_hdr->p_vaddr, memory + prog_hdr->p_offset, prog_hdr->p_filesz);
                memset((void *)(prog_hdr->p_vaddr + prog_hdr->p_filesz),
                       0,
                       prog_hdr->p_memsz - prog_hdr->p_filesz);
                break;
            }
            /* Ignore */
            default:
                info("program header %d of type %d ignored", i, prog_hdr->p_type);
        }
    }
}

Al hacer esto le decimos que copie directamente en direcciones que le hemos puesto en el .ld

memcpy((void *)prog_hdr->p_vaddr, memory + prog_hdr->p_offset, prog_hdr->p_filesz);

Esto me chirriaba pero supuse que tendríamos direcciones de memoria que sabemos que estarán libres de antemano o algo.

La cosa es que esto obviamente no es así, no debemos asumir nada, tenemos que consultar el mapa de memoria/reservar memoria.

cM98L6.png

En este mapa de memoria vemos como se segmentan los bloques de memoria en EFI (está recortada) y pues daba la casualidad de que yo, cuando dejaba de funcionar, miraba a mano bloque de memoria que estuviesen disponibles y usaba esa dirección para cargar el kernel ahí (como una PoC para ver si ese era mi problema) y seguía fallando.

Esto es porque claro tampoco reservamos la memoria, entonces al usar el primer bloque de memoria libre (por ejemplo) al hacer una reserva de memoria con EFI (probablemente) tome ese bloque de memoria para reservarlo y sobreescriba nuestros datos, F.

4. Solución

  1. por cada program header hacer un malloc (reserva de memoria)
  2. cargar el segmento ahí
  3. tocar en EFI (antes de saltar al kernel) la memoria virtual para mapear el segmento fisico del malloc con dirección virtual que salia en p_vaddr del segmento