Stonewheel 0.0.6 Burton Radons (loth@pacificcoast.net) ------------------------------------------------------------------------------- Stonewheel is yet another dynamic link library. It's main features are that it is small, fast, and requires no changes to source code on your part. Your main executable acts as a linker, exporting variables for the plug-ins to gobble up, which can cross-link between themselves. There is also an auto-compiler for easy management of any number of plug-ins. To make Stonewheel, you have to first edit "stone.h" and possibly change the definitions at the top. It should be obvious what to do, but don't change "stone_unix" to "stone_djgpp", since you have dirent in DJGPP and it's better. Then, in GCC: gcc -o stone.o -c stone.c gcc -o stonecom-dirent.o -c stonecom-dirent.c gcc -o system-linux.o -c system-linux.c ar rs libstone.a stone.o stonecom-dirent.o system-linux.o If you're in DJGPP, substitute "linux" above with "djgpp". I don't have DOS installed and cannot test it, so please mail me if there are any compilation problems. Now copy libstone.a to your library directory (\djgpp\lib or /usr/local/lib) and stone.h to your include directory (\djgpp\include or /usr/local/include). Then you can use it as a normal library file. For using Stonewheel, you should export any symbols from the core (The executable) that should be shared to your plugins. I usually use a setup function such as this: void setup (void) { #define e(NAME) stone_export (#NAME, &NAME) e (function); e (variable); #undef e } Then load in all the objects you'll be using, probably with stone_load_dir and stone_load_subdir to use the automatic compiler. stone_load_subdir is useful since it loads the sub-directories of the given directory, so that the current directory is not examined, while stone_load_dir considers the contents of this directory and it's subdirectories. Neither will look at any file or directory starting with a period ".". Once this is complete, use stone_link_all (). You'll want to check the return value for 0; by default, stone_report would have sent the appropriate error messages to stderr and you can just quit. From then in what you do with the symbols is up to you. In my paint program, I searched each loaded object for a symbol "init" and ran it. This can be implemented with: int (*init) (void); int c; for (c = 1; c < stone_nobj; c ++) if ((init = stone_sym_ptr (c, "init")) != NULL && !init ()) exit (1); And that's it! That wasn't so hard, was it? Supported Object Files ------------------------------------------------------------------------------- COFF ----------------------------------------------------------------------------- COFF is the Common Object File Format, the ancestor of ELF. There are lots of variants on dealing with it and some of the variations are very obscure. DJGPP and some Windows platforms use COFF. PE is the Portable Executable format, which it is not. It is COFF with some variations that are not clear. PE is used by Windows. One thing a user got into a bind over was how COFF stores symbol names and what this means for assemblers. If a COFF symbol name is less than 8 characters, it can store it using a pair of integers in the symbol entry. This saves no space, it saves no time, and it saves no code. If there are no symbol names greater than seven characters, the symbol table is not generated. If this is the case, the way to not include the symbol table varies. Some assemblers replace the length with a string; I assume that any string table above a megabyte is this sort, although that's extremely pessimistic. This user found one which didn't put the length field in at all with the header at the end of the file. And that embodies COFF. Endless variants doing the same thing. ELF ----------------------------------------------------------------------------- ELF is the Executable Linker Format. It is a unified format for expressing all types of objects. It is used by Linux and other Unices and is defined for different machine types. Stonewheel currently supports the i386 machine type only. OMF ----------------------------------------------------------------------------- OMF is the Object Module Format. It was used by Microsoft for their compiler and Borland for theirs, at least up to 5.0. Although a simple format, it has very complex semantics and I only support as much as was necessary to get it to run. For example, the iterated data chunk will fail if it is not patterned to make a series of 0s. Supporting the worst compression I have ever seen robustly seemed pointless in the primarily functional Stonewheel (The most expressive it ever gets is to give you the source filename of an object). If you have any further object formats you would like supported, please contact me and I'll tell you what to send. ------------------------------------------------------------------------------- int nstone; struct stone *stone; The number of and list of objects loaded. This is a simple list. The first object is the export object, holding the list of exported symbols. int stone_load (char *file); int stone_free (int obj); stone_load loads an object file. It returns an index into stone on success or -1 on failure. The object is not linked; you have to do that by yourself with the set described below. However, you should only link once you're prepared to, to allow maximum interoperability between objects. stone_free attempts to free an object. Whether this succeeds or not depends upon whether other objects have linked to this one. As long as there are dependents, this object will remain unfreed. Returns whether the stone was actually freed. For example, say you have three objects, a, b, and c. b links to a and c links to b. You free b; nothing happens, because c is still dependent. You free c. c is dropped and b is dropped. int stone_link (int obj); int stone_link_all (void); int stone_link_range (int low, int high); These link objects. stone_link links the indexed object and returns success. stone_link_all links all objects and returns success if they all succeeded. stone_link_range links a range of objects and returns success if they all succeeded. You can try relinking linked files; they won't do anything. int stone_export (char *name, void *ptr); Adds a symbol to the export object. Intrinsically returns success, although there's little reason for it to ever fail. A simple macro could help exporting: #define e(NAME) stone_export (#NAME, NAME) #define v(NAME) stone_export (#NAME, &(NAME)) Where e is used for functions and v is for variables. int /*[obj]*/ stone_sym_find (char *id, int *sym); void *stone_sym_ptr (int obj, char *id); void *stone_sym (char *id); These search for symbols. stone_sym_find is more for linking. stone_sym_ptr looks for a symbol in a single object, and returns a pointer to it or NULL. stone_sym finds a symbol in any object, using stone_sym_find, and returns a pointer to it. int (*stone_sym_proc) (char *id, int *sym); int stone_sym_default (char *id, int *sym); This is a vector that stone_sym_find calls. You could change this to handle more complex linking of your own. stone_sym_default is the default setting for this value, which searches objects one by one. int stone_sym_match (char *a, char *b); This returns whether two strings match one another. This is not a simple strcmp, but also checks whether they match if one or the other has an "_" removed from the front. void stone_report (char *str, ...); void (*stone_error) (char *str); int stone_numerror; The error reporting system in Stonewheel is very simple. stone_error is a vector that is called with an error message. What the vector does with the message is up to you. By default, it prints it out to stdout. Error messages do not have a newline attached. stone_report will send an error to stone_error, if it is not set to NULL, and will add to stone_numerror in any case. It's input is normal printf material. stone_numerror keeps track of how many errors have been reported. So how do you handle all this? You should continue operation as long as possible to get as many errors reported as possible. Once you have to do something with the objects you've loaded or lose control of them, then use stone_numerror to figure out what you should do next. int stone_load_dir (char *dir); int stone_load_subdir (char *base); These automatically compile and load files and directories, and subdirectories of those, to the exclusion of files or directories beginning with ".". stone_load_dir will load files and directories, while stone_load_subdir will call stone_load_dir with directories only. These return success, and also link all files once done. int stone_import (char *file); Loads a .o file or .c file, compiling if necessary for the latter. stone_load doesn't have this functionality because I wanted to allow stonecom.c to be sloughed off if it's not needed. This does need stonecom.c. int stone_inside (void *ptr, int *obj, int *sym); Searches the loaded objects and symbols for the nearest symbol to ptr and at a lower address than it. This can be used to find out what symbol a crash occured in, or other debugging details. If the symbol can be found, then it returns 1. Otherwise stone_inside returns 0. obj and sym are set only on success. char *stone_compile; This holds the compilation string for the auto-compiler. This string can have marks in it to insert various texts. The marks start with "&" and end with ";". "&&" is used to insert an ampersand. &dir; The directory holding the source file. &obj; The destination object file. Doesn't include directory. &obj-dir; The destination object file including directory. &option; In the options list, for anything the caller might like. &prefix; Adds any prefixes the caller might like, before the command. &src; The source file to be compiled. Doesn't include directory. &src-dir; The source file including the directory. &suffix; Adds any suffixes the caller would like. If you don't use this there will be problems. The default string is: &prefix;gcc -c &src-dir; -I. -W -Wall -Werror -Wno-unused -O3 \ -ffast-math -fomit-frame-pointer -m486 -o &obj-dir;&option;&suffix;