A symbol table of a program is basically just a list containing the program's symbols (function names, global variables, etc) and the corresponding addresses of these symbols. The reason to use a symbol table for your kernel can vary for every OSDever, but it is especially useful when loading kernel modules, because a module can be loaded ANYWHERE and it should know how to call the kernel (cleanly, not with dirty hardcode).
Extracting the Debug Symbol Table
If you want for example to get ALL the global symbols in the Kernel for debugging purposes, just use the nm utility as shown here.
$ nm mykernel.bin > mykernel.map
Important to note here that I'm assuming you are using ELF as the binary format with the symbol table section not stripped out. The resulting file contains three columns. The first is the address of the symbol, the second is what kind of symbol it is and the third is the name of the symbol.
The Modulation Problem
I will explain it from bottom. If you have a Modular and/or Microkernel, you must attach to a rule if you don't want to lose the modulation benefits. That simple rule is to have every single needed module (only needed ones) loaded at run-time. Did you notice the problems, no? Well, I will list them:
- That run-time modules include the PCI IDE driver, the driver corresponding to the filesystem in the root mount, and all the driver for all the ACPI says to exist. Some of this drivers are required to access a Massive Storage Unit, where the other modules are... Solution: A initrd.
- If the modules are loaded at dynamically selected locations (mandatory due to the posibility of hardware change), you must provide a Symbol Table to the modules, else they won't know how to call the kernel. (Don't even think on using a typical syscall, that would be too slow and expensive, leave it for applications) This would include all the fast and low-level API functions the drivers should use.
For the dynamic-loading problem, use this solution:
- #include all the symbols you want to export into a single place.
- Make a function pointer array. As you have different function return types and parameters, you should do this:
where you must replace 123 with your function amount.
- On setup, fill the table like this:
KRNLSYMTABLE = (uint32_t)&myFunction;
- When loading a module, you should (using paging) reference the KRNLSYMTABLE dedicated page (yes, it must be like that) to a fixed virtual memory page, so the module always knows where to find things.
Notice that this model uses a separate page directory as per driver. It isn't the best approach, but with this the kernel won't crash with a module.