The k project

Binary loading

The path of the binary to load in the KFS is given via the command line. It’s an ELF file.

In K, since you just need to load an ELF file and execute it, only the segments are interesting. So the first thing is to get the program header table and its size, fortunately, those information are present in the ELF main header. After getting the program header table, you just have to load every segment marked as PT_LOAD at the good memory address.

Don’t forget that in K, the data segment and the code segment are separated, both in memory, with the help of the segmented memory, and in the ELF file. To determine if an ELF segment is a code one, take a look at PF_X defined in the elf.h header. Due to the sbrk() system call, the data segment should physically be located above the code segment, so that this segment could be expanded.

Syscall interface

NAME
    sbrk - change data segment size
SYNOPSIS
    void *sbrk(ssize_t increment);
DESCRIPTION
   sbrk() increments the program's data space by increment  bytes. Calling
   sbrk()  with an increment of 0 can be used to find the current location
   of the program break.
RETURN VALUE
    sbrk() return the old break value.

Jumping into userland

Once you’ve loaded the binary into memory, with separate segment for code and data, you should be able to execute the rom code. This code should run in ring3, so it means that it should be unprivilegied.

The pseudo-code that needs to be executed to launch an unprivilegied program is the following:

ds <- DS_USER | RING3_DPL
ss <- DS_USER | RING3_DPL
esp <- lower_address_of_elf_data_segment
eflags <- enable_interrupts
cs <- CS_USER | RING3_DPL
eip <- rom_entry

This code should be atomic which means that it should be run on one instruction. Unfortunately you cannot do that directly on x86. However it can be done with the help of the iret instruction. By simulating a return from interrupt, with an hand crafted stack, the processor will set cs, ss, esp, the eflags and eip from values taken on the stack. The only missing register here is ds which can be set just before executing the iret instruction as it doesn’t use it. The layout of the stack on interrupt can be found on the IDT page.

Possible bug causes