L2 (C Linker) v2.1 1 7 Dec 81 The Mark of the Unicorn Linker For BDS C v2.1 7 Dec 81 Linker and Docs by Scott W. Layson, Mark of the Unicorn L2 is an alternative to CLINK for linking BDS C programs. A program linked with CLINK will have a jump table at the beginning of each function; calls to other functions are made indirectly through the table. L2 eliminates these jump tables, and adjusts indirect calls through them to go directly to the target function. Besides making the code imperceptibly faster, this has two real advantages: one, it makes the code smaller by 4% to 10% (the latter has been observed in a program containing many small functions which do little besides call a few other functions), and it allows SID to display the name of the target function of a call, simplifying debugging. L2 seems to be complete enough to replace CLINK entirely. Its biggest advantage is that it's written in C, so that if you need some feature it doesn't have, you can just hack it in. However, its user interface and certain aspects of its behavior are rather different. The most important difference is the distinction it makes between "program" and "library" CRL files. Program files are loaded in their entirety; each function in a program file is linked in, whether or not it has been referenced by the time it is encountered (unless another function with the same name has already been loaded). Library files are scanned for needed functions; only those that have been referenced by another function are included in the object code. CLINK treats all CRL files as libraries in this sense (except see the -f option for v1.4). A typical command line is l2 foo bar -l bletch grotz -wa Given this command, L2 will load all the functions in FOO.CRL and BAR.CRL (the program files). Then it will scan the libraries BLETCH.CRL, GROTZ.CRL, DEFF.CRL, and DEFF2.CRL (in that order) for functions that have been referenced but not linked. If there remain unsatisfied references, L2 will display a list of the needed functions and prompt for the name of a CRL file to scan; it will L2 (C Linker) v2.1 2 7 Dec 81 repeat this process until all references are satisfied (just like CLINK). Then it will write the resulting code to FOO.COM, display the link statistics, and write a symbol table (with the link stats appended) to FOO.SYM. In more detail, then: here is a list of the available command-line options. Options consist of a dash followed by a (possibly one-letter) word, preceded and followed by a space. Unlike CLINK's, L2's options may not be combined; "-l -w", for example, may not be abbreviated "-lw"; and "-m fubar" may not be written "-mfubar". -f Reserves enough table size for functions. ( is in decimal.) The default is 200. If you often link programs with more than 200 functions, you may wish to change the default -- it's in setup() in L2.C. -l CRL file names before the first "-l" on the command line will be treated as program files; CRL files after the first "-l" are treated as libraries. Subsequent "-l"s have no effect. -m becomes the top-level function. This is the function initially called when the .COM file is run; by default, of course, it is "main". Note that, unlike with CLINK, the top-level function need not be the first function in the first CRL file; it can be anywhere. -m also works with -ovl (see below). -ovl An overlay segment will be built instead of a root segment; the overlay will be linked to run at base address (entered in hex). is the name of the root segment for which the overlay is being built; .SYM, a symbol table produced with either L2 or CLINK, will be read in *before* the CRL files, to allow overlay functions to call root functions. The name of the top-level function in the overlay -- i.e., the one that gets invoked by a call to the overlay base address -- is by default not "main", but rather , the name of the first CRL file in the L2 command line. The overlay segment is written to .OVL. (See example below.) -org This option is used to produce a root segment with base address , e.g., for use in generating code for ROMming. is entered in hex, and is the starting address of the code, not of RAM; the default is, of course, 0x100. (To link a program L2 (C Linker) v2.1 3 7 Dec 81 for a nonstandard CP/M, you need a C.CCC, DEFF.CRL, and DEFF2.CRL which have been assembled for that address. If you are running L2 on a nonstandard CP/M, you should change the default origin in setup() to 0x4300.) If you are using this option to generate code for ROM, be sure to use the "-t" option also (see below). -t Works just like the CLINK "-t" option: sets the stack pointer to the given address at the start of the run-time package. This option MUST ALWAYS BE USED when "-org" is used to generate code for ROM. IF "-t" is NOT used, then the first two instructions of the resulting COM file will be: lhld origin-100h+6 sphl (where "origin" is normally 0x100 or 0x4300) while using "-t" causes the first two instructions to be: lxi sp, nop -w A SID-compatible symbol table is written to .SYM, where is the name of the first CRL file listed in the command line. This table is normally produced in address order, not alphabetical order like CLINK's; see below for how to change this. -wa A variation on -w. The link statistics, which are always displayed on the console at the end of linking, are also appended to the .SYM file. If the resulting .SYM file is read into SID, SID will complain by issuing its typical verbose error message "?", but then will work correctly. The big advantage of putting the stats at the end of the .SYM file is that one can subsequently look at that file to see exactly how long the code was and where the externals started. -ws Another variation on -w. This one writes the symbol table to .SYM and the link statistics to .LNK. Because L2 is so large, it cannot always link large programs in a single pass. If it runs out of memory during linking, it will switch automatically to (very slow) two-pass mode. (If it says "Module won't fit in memory at all", you probably have a very large program file. Split it up or make it a library. If this doesn't L2 (C Linker) v2.1 4 7 Dec 81 work, you don't have enough memory to use L2.) L2 is built from the source files L2.C, SCOTT.C, and CHARIO.C. A typical compilation is cc l2.c -e4500 cc scott.c cc chario.c followed by either clink l2 chario scott or l2 l2 chario -l scott depending on whether an L2.COM is handy. If you want a slightly shorter version, comment out the "#define OVERLAYS" statement near the beginning of L2.C. You can then compile with -e4100; the resulting L2 will not accept the "-ovl" option. If you have a pre-release of MARC and want a MARC cross-linker, uncomment the "#define MARC". Then, when you use the "-marc" option, L2 will look for CM.CCC, DEFFM.CRL, and DEFF2M.CRL, which should be the MARC versions of these files. L2 will produce a .COM file which must be MFTPed to MARC, then converted to load format. L2 itself does not yet run under MARC; if anyone has the energy to convert it, let me know! L2 (C Linker) v2.1 5 7 Dec 81 Relocatable Overlay Manager MAKOVL is a variation on L2. It builds relocatable overlays and stores them in fixed-size slots in a single overlay file. Here is the (rather terse) documentation from the beginning of MAKOVL.C: Command format: makovl { [-l {}] [-nd]} Overlay descriptor file, .DES, contains: ... (default search list) ... (more default search list) ... (an entry for each top-level function in the overlay) ... ... (an entry for each overlay in the file) Overlay segments are of length bytes, of which the last bytes holds a list of relocation offsets. This is a null-terminated string of byte values giving the difference between successive addresses to be relocated; a value of 255 means to add 255 to the next byte value to get the offset. The first offset is relative to -1 (so that we can relocate the first word of the overlay). At the beginning of the overlay is a table of addresses of top-level functions, one address for each function listed for that overlay in the descriptor file. The -l option works as for L2: CRL files listed before L2 (C Linker) v2.1 6 7 Dec 81 the -l are loaded in entirety; those after are just scanned for needed functions. Any functions specified in the default search list in the overlay descriptor file are also scanned, unless the -nd (no default) option is given. The overlay, once created, is written into .OVL, at address * . Here is an example of a .DES file, with comments in brackets: 1024 128 [1K overlay slots; 128 relocation bytes] lib1 lib2 lib3 [default CRL libraries] OvlOne 0 [overlay name, slot number] Func11 [callable functions in the overlay] Func12 Func13 OvlTwo 1 [another overlay name & slot] Func21 Func22 Func23 Here is some code to load and execute the generated overlays: /* Overlay manager */ int curovl, ovlfd; #define OVLSIZE 1024 /* overlay space size in bytes */ #define RELOCSIZE 128 /* size of relocation info */ char overlay[OVLSIZE]; OvlInit() /* start overlay manager */ { curovl = -1; if ((ovlfd = open ("FUBAR.OVL", INPUT)) < 0) { puts ("Can't find the overlay file, FUBAR.OVL\n"); exit (1); } } OvlFini() /* clean up overlay manager */ { close (ovlfd); } L2 (C Linker) v2.1 7 7 Dec 81 OvlCall (ovlno, fun, arg1, arg2) /* call function in overlay */ int ovlno, fun, arg1, arg2; /* with args (, ) */ { int *ovltab, (*ovlfunct)(); /* note C bug: "int (**ovltab)()" doesn't work */ OvlLoad (ovlno); ovltab = overlay; ovlfunct = ovltab[fun]; return (*ovlfunct) (arg1, arg2); } OvlLoad (ovlno) /* load overlay */ int ovlno; { char reloc[RELOCSIZE]; if (ovlno == curovl) return; if (seek (ovlfd, ovlno * ((OVLSIZE + RELOCSIZE) / 128), ABSOLUTE) < 0 || read (ovlfd, overlay, (OVLSIZE / 128)) < 0 || read (ovlfd, reloc, (RELOCSIZE / 128)) < 0) { puts ("Read error in the overlay file, FUBAR.OVL\n"); exit (1); } OvlReloc (overlay, reloc); curovl = ovlno; } OvlReloc (code, rels) /* relocate an overlay */ char *code, *rels; { int *itemp; char *btemp; btemp = code - 1; while (*rels) { btemp += *rels; if (*rels++ == 255) btemp += *rels++; itemp = btemp; *itemp += code; } } To call these functions: L2 (C Linker) v2.1 8 7 Dec 81 #define FUNC11 0, 0 #define FUNC12 0, 1 #define FUNC13 0, 2 ... OvlCall (FUNC11, arg1, arg2); ... To make a MAKOVL.COM: cc makovl.c -e4100 cc chario.c (if necessary) cc scott.c (if necessary) l2 makovl chario -l scott Good luck Scott W. Layson Mark of the Unicorn P.O. Box 423 Arlington, MA 02174