A module might be part of the JOSBox environment.
Note: A native module plugs into a kernel, not a JOSBox. Therefore, it is more correct to think of modules as a JOSKernelModule. -- GilbertHerschberger (6 December 1999).
A classic JVM benefits from the native operating system. A native operating system already has a mechanism for shared (or dynamic link) libraries. Native code is dynamically linked into a JVM with the loadLibrary()
method.
Note: A significant change in the Java 2 Platform encourages a dynamic link library to be managed by a class loader, rather than a JVM.
A JOS JVM does not have another native operating system. It doesn't inherent any mechanism for shared libraries. How does the JVM load and dynamically link in any native code? The mechanism of dynamic linking requires three things:
well-known executable image format
In order for an executable image to be loaded and invoked correctly, it must put its code and data in the right place. JOS has yet to define an executable image format for native subroutines.
subrouting (or method) index
Each method must have a name and an entry point. The typical shared library mechanism assumes that a return exists in the code to return to the calling code. The CPU calls the code starting at the entry point. The entry point is the first op code of native subroutine.
reflection
Each public subroutine in a shared library must export its name and entry point. There are more assumptions about parameter passing, such as how many and what type of parameter is passed. The shared library mechanism uses a very low level reflection. The signature of each subroutine is verified at compile-time, not run-time.
Reflection provides a calling program a look-up mechanism. By using a well-positioned reflection subroutine, the names of all public subroutines can be determined dynamically and the entry point for each routine can be determined.
As this mechanism matured, the operating system loads an executable image and calls its init() method before using any other library method. As the operating system unloads an executable image, it calls its exit() method.
These mechanisms have been formalized in Java bytecode, with classes and other well-defined interfaces. And yet, the JOS kernel needs to load shared libraries without using another operating system. How is this done?
One way to instantly get shared libraries in JOS is to borrow heavily from the Linux shared library format. A Linux-compatible compiler might be used to compile the native code that goes into a JOS module. The Linux-compatible linker might be used to link native code into an executable image file.
A linker tool must be able to create the share library. A linker is responsible for putting the executable image on disk so that the kernel has no trouble loading it at runtime.
While something like the Linux file format should be used, the executable code inside won't match the Linux platform API. A Linux compiler might be able to create these shared libraries; but, they are incompatible. They cannot be invoked on Linux.
At least in theory, it is possible to pre-compile native methods using the class-file format, without a separate shared library. Unlike native methods that store a signature and no native code, native code can be stored in the attribute field of a bytecode method. Bytecode classes with inseparable native code is appealing. Both the bytecode and native code are stored together in a .class file.
It would require a special kind of linker to create a class-file from native compiled object files. Such an attribute would be ignored by other virtual machines.
The JOS kernel configuration determines which modules get loaded (and when). Like kernel modules for Linux, a JOSBox module is a kernel plug-in. It determines what a JOS machine is capable of doing.
A network module might be required, for example, to use a network card in hardware. Selecting a network module must be part of kernel configuration. For a JOS machine without network support, the network module is never loaded.
A serial-port module might be required, for example, to use a serial port in hardware. Selecting a serial port module must be part of kernel configuration. For a JOS machine without a serial port, the serial port module is never loaded.
A JVM module might be required, for example, to use additional instances of a virtual machine for Java 0 and 1 Platform support. Selecting a JVM module is part of kernel configuration. For a JOS machine that uses only one JVM, the JVM is compiled into the kernel; no additional JVM module is necessary.
JOSBox modules are used wherever shared libraries would be used in a classic JVM. Like shared libraries, a JVM is able to use the module to invoke native code.
Unlike shared libraries, modules are not unloaded at the end of a program. Since the JVM is the only "program", modules are unloaded when the operating system is shutdown.
All of the public (exported) subroutines in a shared library are numbered from 0 to n. The entry point into each subroutine is inside an array of entry points. The subroutine number is an index into that array.
The first group of subroutine in a shared library are required to have the same signature in every shared library. It is as if a shared library implements this a SharedLibrary
interface:
public interface SharedLibrary { public void init(); public void exit(); public int getSubroutineCount(); public byte[] getEntryPointArray(); public String[] getNameArray(); }
After reading an executable image from disk, the image must be prepared for invocation as native code. Parts of the executable image are installed as READ-ONLY-CODE, parts are READ-ONLY-DATA, parts are READ-WRITE-DATA.
Then, the shared library loader must invoke a shared library's init() method. This gives a shared library an opportunity to do housekeeping and load other shared libraries. The shared library loader invokes the init() method using something like this:
// using the C language void (*init)(); void invokeInit( void *exported ) { init = exported; init(); }
In order to invoke the exit() method, native code must perform something like this:
// using the C language void (*exit)(); void invokeExit( void *exported ) { exit = exported[ 1 ]; exit(); }