User Programs
Now that we can read files from the disk, we want to be able to run them, as is the point of an operating system. I tried build a c library but haven’t gotten it to work quite yet, so for now user programs are compiled with no library, and I supply a few helper functions.
The main programs are written as normal C programs, just without a library. The
syscalls are defined in an assembly file, which just loads the arguments and
calls the syscall interrupt. The normal c program starts with a main()
, as
usual, but main()
is not the start of a c program, it is actually _start
.
Usually this would be located in crt0.o
, which would set up argc and argv,
call main()
, handle the return value, and then exit the program. As I am not
using a c library I do this all myself too, with the syscalls. I compile and
link that all together and end up with an ELF file, which gets bundled in the
disk image when the vm is run.
To execute a program, the program is found by starting at the root directory
and parsing the path until the inode is found. We then map two different memory
sections: One section at 0x8000000
, where the program will be ran, and one at
0xC000000
, which is 4mb above that. Since physical memory is just one program
after another, I just temporarily use the space where the next program would
be. Once the entire file is loaded, the elf header and program headers
are read. The elf header is right at the beginning of the file, and holds
information about the file like any flags, what the target system and abi the
file is for, as well as some magic numbers to identify it. It also contains
the entry point, where execution should start. The location and size of
program headers are included too.
Program headers contain information about segments of the program, what type they are, where they should be loaded, etc. If there is only one program header than this is irrelevant, but in my cause I am using static variables which are located not on the stack but in a different section, which has a new program header. Each program header has its offset in the file, its size, and where in memory it wants to be. For each program header, I copy the appropriate data from the temporary mapping where the entire file is loaded into the location where it wants to be.
Once all the sections are loaded, I enter user mode at the entry point specified in the elf header and let it rip.
Entering user mode is a little weird, see here.