Page 1 of 1
Design of a basic module loader
Posted: Thu Nov 11, 2004 2:56 pm
by beyondsociety
Im at the point where I want to start loading my system drivers as as modules and I am looking for some ideas on how to design a simple module loader. At this point Im not too concerned with different object formats, elf, coff, pe.
I have a few ideas but most of them require the use of a executable object format.
What steps do I need to take in order to write a basic module loader?
Thanks in advance.
Re:Design of a basic module loader
Posted: Thu Nov 11, 2004 5:55 pm
by df
I dont know if it was the right way or not, but I always aproached this as doing a linker. I more or less post-linked/relocated objects in memory. my main exec format had imports/exports/relocs etc. so then there became no difference from loadong a binary or a module...
the hard bit was interfacing into the kernel and all that goes with that
Re:Design of a basic module loader
Posted: Thu Nov 11, 2004 8:27 pm
by Brendan
Hi,
beyondsociety wrote:
Im at the point where I want to start loading my system drivers as as modules and I am looking for some ideas on how to design a simple module loader. At this point Im not too concerned with different object formats, elf, coff, pe.
I have a few ideas but most of them require the use of a executable object format.
What steps do I need to take in order to write a basic module loader?
I do the opposite to "df". My modules are all loaded at fixed addresses, and I made up a simple binary file format for them (same as the DOS *.com format, except it's 32 bit and there's a simple header at the start).
To start a module the kernel uses normal file IO to load the first 128 bytes (enough for the header) and checks if it's a valid module (and that the OS has enough resources, the user has permission, etc to run it). Then the kernel creates a new process and spawns a thread within the new process. This spawned thread runs more kernel code that also uses file IO to load the binary file into the process's address space (or eventually, optionally memory maps the file - when I get time to write the code), and uses "retf" to jump to it's entry point. The new thread becomes the first thread for the process/module.
Cheers,
Brendan
Re:Design of a basic module loader
Posted: Fri Nov 12, 2004 9:39 am
by aladdin
I think module loader depends on your kernel architechture,
if you want to have a monolitic kernel you will use df methode
if you want a microkernel you have to do something like brendan methode
(correct me if i'm wrong)
Re:Design of a basic module loader
Posted: Fri Nov 12, 2004 9:56 am
by df
I didnt say anything about micro or monolithic.
whether each module gets its own task/process space or you load many inside a single address space or not doesnt alter what I said.
Re:Design of a basic module loader
Posted: Mon Nov 15, 2004 4:12 am
by Pype.Clicker
aladdin wrote:
I think module loader depends on your kernel architechture,
if you want to have a monolitic kernel you will use df method
if you want a microkernel you have to do something like brendan method
hum. i don't think we should talk about "loading modules" for a microkernel system that loads a new service. Reason is the new service will only invoke the microkernel and will only do it through a collection of well-defined interrupts (or system traps or whatever).
Thus the "linkage" actually occurs in the "syscall.h" and in the system library or something where you will write the ASM stub and hardcode the parameters passing rules for the system calls.
Re:Design of a basic module loader
Posted: Mon Nov 15, 2004 4:31 am
by Pype.Clicker
beyondsociety wrote:
What steps do I need to take in order to write a basic module loader?
Knowing what you have to load: your module will have to include information about the size of the code to be loaded and the amount of space your module requires (e.g. don't forget the BSS section)
Knowing where you will load it: the easiest is to build your module so that it has a 'target address' that will be free at run-time. If you have enough virtual address, you can easily slice your kernel space so that each module has its own slice. However, this is not very eay-to-upgrade-later and thus i'd rather advocate for a relocation list, here. Check out the "Linkers and Loaders", it's not such a complex concept.
Knowing what you can use: your module is likely to need access to kernel (or other modules) functions. This is usually the
symbol table. You can decide that there are N 'symbols' pointing towards structures/functions and pass the address of the table where those symbols are stored to your module's initialization function.
Code: Select all
#define kprint kernelptrs[0]
#define kalloc kernelptrs[1]
...
function *kernelptrs=NULL;
status init(function *imports) {
kernelptrs=imports;
}
That table could also be an associative array of name=>pointer that the module loader will use to patch the executable.
Telling what you can do: the last step is to be able to export features (such as open/close/read/write for a device). You can for instance return an "object" structure that will contain pointers to functions that will perform what is expected, etc.
Code: Select all
//-- module.h
struct itable {
int id;
void* vtable;
};
#define END_OF_INTERFACES 0
void* search_itable_for_id(int id);
//-- blockdev.h
struct block_device_interface {
status (*open)(options);
status (*close)();
status (*read)(address, size, buffer);
status (*write)(address, size, buffer);
};
#define BLOCK_DEVICE_INTERFACE 0xb10c
//-- module.c
status myOpen(...) {...}
...
struct block_device_interface myBlockDevice {
open: myOpen,
close: myClose,
read: myRead,
write: myWrite,
};
itable myInterfaces[]={
{ BLOCK_DEVICE_INTERFACE, &myBlockDevice },
{ END_OF_INTERFACES, NULL}
};
itable* init(functions* import) {
... do stuff ...
return myInterfaces;
}