Kernel Loader

From Nintendo Switch Brew
Revision as of 04:18, 31 August 2019 by SciresM (talk | contribs) (Add KInitialPageAllocator member funcs)
Jump to navigation Jump to search

The Kernel Loader ("KernelLdr"/"Kernelldr") was added in 8.0.0. It is responsible for applying relocations to the Kernel, and mapping the Kernel's .text/.rodata/.data/.bss at a random slide.

Kernel Loader

KernelLdr is called immediately by the Kernel's crt0 (after it deprivileges from EL2 to EL1, if required), with the following signature:

   void KernelLoader_Main(uintptr_t kernel_base_address, KernelMap *kernel_map, uintptr_t ini1_base_address);

KernelLdr_Main

First, it clears BSS, and then sets SP = <BSS end>.

    for (uint64_t *i = __bss_start; i != __bss_end; i++) {
        *i = 0;
    }
    SP = __bss_end;

Next, it applies relocations to itself and calls its init array.

    KernelLdr_ApplyRelocations(&KernelLdr_Main, __dynamic_start);
    KernelLdr_libc_init_array();

Then, it calls the function which relocates the kernel, and jumps back to the kernel entrypoint.

    // KernelLdr_LoadKernel returns (relocated_kernel_base - original_kernel_base).
    uintptr_t kernel_relocation_offset = KernelLdr_LoadKernel(kernel_base, kernel_map, ini_base);
    
    // dtor called for static page allocator.
    g_InitialPageAllocator.~KInitialPageAllocator();
    
    // Jumps back to the kernel code that called KernelLdr_Main.
    ((void (*)(void))(kernel_relocation_offset + LR))();

KernelLdr_ApplyRelocations

TODO: Fill this out

KernelLdr_lib_init_array()

This is just standard libc init array code. .init_array is empty in all available binaries.

KernelLdr_LoadKernel

TODO: Fill this out

KInitialPageAllocator::KInitialPageAllocator

This sets the allocator's next address to 0 (guessed, since this is done statically in KernelLoader).

    constexpr KInitialPageAllocator::KInitialPageAllocator : next_address(0) {}

KInitialPageAllocator::~KInitialPageAllocator

This just clears the allocator's next address.

    this->next_address = 0;

KInitialPageAllocator::Initialize

This sets the allocator's next address (function inferred as it is (presumably) inlined and next_address is (presumably) private).

    this->next_address = address;

KInitialPageAllocator::Allocate

This linearly allocates a page.

    virtual void *KInitialPageAllocator::Allocate() {
        void *address = reinterpret_cast<void *>(this->next_address);
        if (address == nullptr) {
            // If called on uninitialized allocator, panic by infinite looping
            while (true) {}
        }
        this->next_address += 0x1000;
        memset(address, 0, 0x1000);
        return address;
    }

KInitialPageAllocator::Free

This frees a page (implemented as noop in KernelLoader)

    virtual void KInitialPageAllocator::Free(void *address) {
        // Does Nothing
    }

Structures

KernelMap

Offset Size Description
0x0 4 .text offset
0x4 4 .text end offset
0x8 4 .rodata end offset
0xC 4 .rodata end offset
0x10 4 .rwdata offset
0x14 4 .rwdata end offset
0x18 4 .bss offset
0x1C 4 .bss end offset
0x20 4 INI1 load offset
0x24 4 .dynamic end offset
0x28 4 .init_array end offset
0x2C 4 .init_array end offset

KInitialPageAllocator

KInitialPageAllocator is just a simple linear allocator.

Offset Size Description
0x0 8 vtable;
0x8 8 Next Address;

KInitialPageAllocator::vtable

Offset Size Description
0x0 8 void *(*Allocate)(KInitialPageAllocator *this);
0x8 8 void (*Free)(KInitialPageAllocator *this, void *address);