1 Introduction#$+
K
1.1 What is lxrun ?#KLxrun is an emulator that allows the execution of Intel Linux binaries on Intel UNIX[reg   ] platforms. The currently supported platforms are SCO OpenServer 5 (UNIX SVR3), SCO UnixWare 2.x (UNIX SVR4) and  SCO UnixWare 7 (UNIX SVR5).
Lxrun works by remapping system calls on the fly. Since lxrun does its work  at the system call level, it requires copies of the Linux dynamic loader  (ld-linux.so.*) and whatever Linux shared libraries are required by the  program being run. The current development release of lxrun consists of approximately 6000 lines of code (146 Kilobytes).
Most programs that do not rely on Linux-specific quirks or deal directly with hardware should work under lxrun. Users of lxrun have reported success with raplayer (RealAudio client), xquake, the StarOffice suite, gcc (the GNU C compiler), smbclient, the AC3D modeller and a myriad of smaller applications and utilities.
1.2 Why should I use lxrun ?#KThe original impetus for writing lxrun was to be able to run Netscape Navigator 1.x on SCO OpenServer. When Netscape ported Navigator to SCO  platforms, the need was obviated. Later, work was resumed in an attempt to  run Adobe Acrobat Reader on OpenServer. The body of system call mappings in  lxrun grew gradually as users modified it to work with more and more  applications. By this process, lxrun became quite robust, able to handle  sophisticated X11, audio, and networking applications.
Lxrun promotes the interoperability of Linux with other UNIX and UNIX-like platforms. This improves the user's ability to combine the best aspects of available operating systems. For example, it allows a user to combine Linux's large freeware application base with SCO's:
· Large robust filesystem support
· Large installed base
· Renowned and Industry-proven scalability
· Enterprise applications base
· Installed Enterprise server deployment
· Network Computer / "Any Client" deployment of applications
· Ability to distribute and execute applications via  Tarantella
· Robust DOS/Windows '95 emulation capabilities
· Free UNIX offer makes for extremely inexpensive robust UNIX workstation
· SVR5 enhancements 
· Up to 64GB of main memory 
· 1 TB File Systems and disk partitions 
· 1 TB file sizes 
· Up to 76,800 TB of total storage supported 
· Up to 32 CPUs supported 
· Support for I2O Peripherals 
· 64-bit technology
· Advanced Clustering Support for up to 4 nodes 
· Hot Pluggable Disk and Tape Drives 
· Disk Mirroring and Spanning 
· Hot Plug PCI 
· Multi-path I/O 
· Real-time Data Management 
· UPS Monitoring
· Centralized, on-line backup
· Journaling File Systems for improved data recovery
Every UNIX vendor could construct such a "Why you should use my platform" list.  Lxrun allows the user to leverage the large base of precompiled Linux software without restricting the choice of platform.

2 Implementation#$+
KThe emulator needed to handle the following major issues (detailed below): system call handling along with remapping of arguments, flags, return values and error codes; library pathname lookup and remapping; ioctl mappings for the various major devices and differences in the tty subsystems.
2.1 Design goals#K
· Allow execution of Intel Linux binaries on other Intel UNIX platforms
· Exist entirely in user-space (no kernel modifications)
· No modification of the Linux binaries or libraries required
2.2 Difference in syscall handling#KSystem call handling is implemented differently between Linux and Intel UNIX System V platforms; that is each uses a different instruction to implement the switch to kernel mode.
Intel Unix System V uses a "lcall $0x07".  In Linux an "int $0x80" software interrupt instruction is used, which jumps to the  system-call-handling  portion of the Linux kernel. On SCO systems, "int $0x80" is an unused vector  and therefore causes a general protection trap resulting in a SIGSEGV signal. Lxrun intercepts these signals and calls the SCO equivalent of the system call that the Linux program attempted. In many cases this is a direct map or call of the equivalent native system call, in other cases some mapping or translation of arguments passed in and flags and error codes passed out is required. Where there is no equivalent system call available on the emulated system or the equivalent syscall mapping has not been implemented the system call fails and returns an errorcode of ENOSYS.
2.3 Pathname for loading Linux libraries #KBecause lxrun works at the system call level, any Linux shared libraries required by the application must be present in the emulation environment.  This leads to a possible filename-space conflict between native and Linux binaries.  To resolve this problem, lxrun remaps any pathnames beginning with /lib, /usr/lib, /usr/local/lib, or /usr/X11R6/lib by prepending them with a "Linux root" path.  This path is specified at compile time and can be overridden by setting the LINUX[lowbar]ROOT environment variable. This remapping allows the user to install a set of Linux libraries in a separate directory hierarchy from the native system libraries, thus avoiding conflicts.
2.4 Device major number mappings#KThe arguments to an ioctl call alone do not provide enough information to remap the command number correctly.  This is because the same command number can have different meanings to different drivers.
Lxrun works around this problem by maintaining a mapping from open file descriptors to drivers.  On the first ioctl call to a new file descriptor, lxrun determines the associated major device number.  It compares this with a table of drivers and major device numbers set up at run-time.  This mapping is then cached to improve performance on future ioctl calls to that file descriptor.  Lxrun can then take the driver into consideration when remapping ioctl command numbers.
2.5 Differences in kernel tty systems#KThe following is a discourse on lxrun tty handling from Robert Lipe (robertl@dgii.com), the principal author of lxrun's tty handling code:
"The kernel tty systems are very different. The user level tty systems are actually quite similar, fortunately for lxrun. Most of the members of things like termio and struct termios are the same size, alignment,  and offset. Even most of the bitfields fall into place. While it would have been much safer to do 
   if ( (lx[lowbar]tio->c[lowbar]cflag & LX[lowbar]CBAUD) == LX[lowbar]B50) 
                tio->c[lowbar]cflag |= B50; 
        if (lx[lowbar]tio->c[lowbar]iflag & LX[lowbar]ICANON)  
                tio->c[lowbar]iflag |= ICANON;

 and repeat this for each of a couple hundred flags, the reality was that it generated horrible code that would have performed poorly and been a nightmare to maintain. 
The only sticky spot was that Linux has four distinctly separate members in c[lowbar]cc (the control character array) for VMIN, VTIME, EOF,  and EOL. Most System V's including OpenServer (I can't recall what SVR[lsqb  ]45]  does here) use the same offset and multiplex this into two bytes. In reality,  since they can never both be active at the same time (if ICANON is set, EOF  and EOL are used. If ICANON is clear, VMIN, VTIME are used) this doesn't turn out to be much of a issue.
[lsqb  ]Since] TCGETA and TCSETA are by far the most frequently used, I implemented  them first (and their derivatives that wait and flush and the derivatives of  each of those for POSIX struct termios). With those 8 in hand, I started firing  up applications that were known to do wierd things to the tty. When elvis  (the Linux vi binary I had at the time) worked well enough, I submitted the  changes. Then, I just used more and more applications and picked up a few  stragglers like FIONREAD that mapped very simply. 
Are there hazards in any of this? Certainly. Each system has a few bits in each of the available ioctls that don't exist in the other system. There is some overlap. We haven't seen any real world failures becuase of this. The reality seems to be that the programs that drive serial ports to the crazy edge don't make sense to emulate anyway. For example, someone once asked me why Linux ecu (a kermit-like program) didn't work well. Since source for it is available and it supports OpenServer just fine, use the native binary instead. Someone once asked me about running Linux ppp, but that's similarly nonsensical, though doomed to failure for different reasons."
2.6 Miscellaneous issues#K
·  For local displays, SCO OpenServer uses ":0.0" and UnixWare uses "unix:0.0".  The former causes Linux X11 binaries to try to use shared memory, which won't work with the native X server. The latter causes a name lookup for the machine "unix" which probably fails. These two cases are detected and the DISPLAY is set to contain a valid machine name instead.
·  Documentation is supplied in the doc subdirectory of the lxrun source. This currently consists of this document, a FAQ, a document describing how to get the Linux StarOffice suite to run, and the system call mapping table.
·  Various packaging conveniences have been supplied. For instance, the necessary Linux shared libraries have been re-packaged in the native installation format. A shell script, lxfront, useful in the execution of Linux a.out and statically-linked ELF binaries, has been supplied. To use lxfront: 
·  Put lxfront and any Linux a.out or statically-linked ELF  binaries in /usr/local/linux/bin
·  Put lxrun in /usr/local/bin
·  Create links in /usr/local/bin to lxfront with the same  names as your Linux binaries. Executing these links should invoke lxrun on your Linux program. 

3 Development Issues#$+
K
3.1 Direct execution of Linux ELF binaries#K#KRather than running Linux binaries with the lxrun front-end, it is possible to turn lxrun into a program interpreter, ld-linux.so.* - the Linux runtime  linker. The path of the interpreter is actually embedded in every dynamic  linked ELF executable - for standard UnixWare / OpenServer ELF it is /usr/lib/libc.so.1, and for Linux it is /lib/ld-linux.so.1 (or, ld-linux.so.2 for executables linked with GNU libc v2). 
The direct execution of Linux binaries in this manner is in the latest  development releases of lxrun. At the time of this writing, support for this model of execution is available on UnixWare platforms only. SCO OpenServer places some rather strict limitations on what a program interpreter can be. Overcoming these restrictions is a current development topic.
The following discourse, from Mike Davidson (md@sco.com) the author of lxrun,  details some of the issues involved in the direct execution model:
"It's actually quite simple to build a version of lxrun which can be used as the initial program interpreter for Linux binaries. There are, however, a couple of problems: 
·  runtime linkers are usually ELF shared objects which don't have a fixed load address, and which have to do some rather delicate data relocations when they first start up - I avoided this issue by making the ld-linux.so.* version of lxrun be an actual ELF executable (rather than a shared library) bound to a fixed address which does not conflict with the addresses used by normal Linux binaries - while this is a bit of a kludge it works perfectly well
·  the interpreter program loaded by the kernel cannot itself require an interpreter - what this means is that if the program interpreter wants to do any dynamic loading of shared libraries it has to do it for itself - this isn't too much of a problem on OpenServer since you can just do a static link of everything that lxrun needs into a single binary - unfortunately this doesn't work on UnixWare 7 since libsocket is only available as a shared library. I think that there is a way round this, but I haven't had time to try it out yet. Essentially it looks something like this: 
·        ld-linux.so.* is created as a dynamic linked shared object with appropriate dependencies on other shared libraries (ie at least /usr/lib/libc.so.1 and /usr/lib/libsocket.so.1)
·        when ld-linux.so.* is loaded by the kernel and gets control, the first thing that it does is to fix up enough of it's own data relocations in order to be able to run
·        once that is done, it maps /usr/lib/libc.so.1 (ie the normal system runtime linker and standard library) into memory, fakes up a suitable aux vector and invokes the normal runtime linker, while pretending to be a normal executable program (this involves a lot of trickery - you have to fake up an appropriate set of program headers to give to the runtime linker, you have to fix up entries in .dynamic, .dynsym and .rel.* to reflect the actual address that ld-linux.so.* is actually loaded at, and you have to provide a fake entry point address so that the system runtime linker will give control back to ld-linux.so.* in the right place ....)
·        if all of that works, then ld-linux.so.* proceeds as normal, and now maps in the Linux runtime linker and passes control to it I realise that this all sounds hideously complicated, but all of the alternatives that I can think of are worse in some way.
At first I thought that we could avoid this by just porting a version of the Linux runtime linker to run native on UNIX, but this looks like it may be more trouble than it is worth - while the Linux runtime linker is quite well written it has to deal with some rather unpleasant limitations in the GNU tools - in particular it looks as if GNU ld doesn't support the equivalent of our -Bsymbolic option which makes writing the startup code for a runtime linker almost impossible (in fact the Linux runtime linker startup routine is just one massive function that uses nothing but local variables and which does all of it's system calls with chunks of inline assembler)."
3.2 SIGSEGV vs. software interrupt kernel module#KConsideration was given to implementing a software interrupt kernel module rather than relying on the "int $0x80" segmentation violation. Rather than sacrifice the elegance and portability of a "non-kernel" Linux emulation strategy, Mike Davidson has suggested that: quotationThis probably isn't really necessary. Assuming that we are really only interested in Linux ELF (and not Linux a.out) we can use the dynamic linker to "preload" the Linux runtime compatibility library in such a way that almost all of the system calls will be intercepted and handled directly by the compatibility library  without ever going down to an actual "int $0x80" instruction.quotation
3.3 Performance enhancements#KThus far, performance has not been an issue as very little negative impact has been detected.  The main cost of running a Linux application under lxrun is the overhead of catching the segmentation violation (int $0x80), fixing up the structures/errors/returns, and mapping the system call. Since much of what lxrun does is done in the normal course of executing a  system call natively, the SIGSEGV intercept is the main overhead.
In order to avoid catching SIGSEGV for every system call, current lxrun development plans on implementing a "pre-load" of the "Linux runtime  compatibility library" (see the previous subsection). With the direct  execution of Linux ELF binaries described above, it is possible to pre-load  this library on startup. That is, the binary has already been identified as a Linux ELF binary since it is attempting to load /lib/ld-linux.so.1. This "fake" program interpreter knows it will have to map system calls so, rather than waiting for the SIGSEGV to trigger the  mapping, the program interpreter can "pre-map". Thus, system calls made by the Linux ELF binary under the control of such a program interpreter would not cause general protection traps. In this scenario, nearly all of the  performance overhead of running Linux binaries with lxrun is eliminated.

4 System Call Mapping#$+
KLxrun emulates or maps most commonly-used system calls for which native  equivalents exist. Unimplemented calls return an error indication and set  ENOSYS.
The files doc/SysCallTable* list tables of supported, partially supported,  and unsupported system calls. The SyscallScript utility run (against the  source) will regenerate the text and HTML versions of this file. An on-line  copy of the currently supported system calls is available at  urlnam.
4.1 Directly mapped system calls#KThe following system calls are mapped directly:
exit() fork() creat() link() unlink() chdir() time() mknod() chmod() chown()  lseek() getpid() setuid() getuid() stime() alarm() pause() utime() access() nice() sync() rename()  mkdir() rmdir() dup() times() setgid() getgid() geteuid() getegid() setpgid() umask() chroot() dup2()  getppid() getpgrp() setsid() setreuid() setregid() sethostname() gettimeofday() settimeofday() symlink()  readlink() truncate() ftruncate() fchmod() fchown() setitimer() getitimer() fsync()  setdomainname() getpgid() fchdir() sysfs() getdents() readv() writev() getsid()
4.2 System calls with a non-stub emulation function#KFor the following system calls, lxrun either provides some remapping of arguments, return values, and error codes, or in cases where an analogous native call does not exist, emulates the call using native library functions.
nosys() read() write() open() close() waitpid() execve() oldstat() ptrace()  oldfstat() kill() pipe() brk() signal() fcntl() olduname() sigaction() sgetmask() ssetmask()  sigsuspend() sigpending() setrlimit() getrlimit() getgroups() setgroups() old[lowbar]select() oldlstat()  uselib() readdir() mmap() munmap() getpriority() setpriority() socketcall() syslog()  stat() lstat() fstat() uname() iopl() wait4() sysinfo() ipc() sigreturn() newuname()  mprotect() sigprocmask() personality() [lowbar]llseek() select()
4.3 Partial implementations#KThe following system calls are partially emulated:
ioctl() ioperm() fdatasync()
4.4 Unimplemented system calls#KThe list of unimplemented system calls is as follows:
mount() umount() getrusage() swapon() reboot() statfs() fstatfs() vhangup()  idle() vm86() swapoff() clone() modify[lowbar]ldt() adjtimex() create[lowbar]module()  init[lowbar]module() delete[lowbar]module() get[lowbar]kernel[lowbar]syms() quotactl() bdflush()  setfsuid() setfsgid() flock() msync() sysctl() mlock() munlock() mlockall() munlockall() sched[lowbar]setparam() sched[lowbar]getparam() sched[lowbar]setscheduler() sched[lowbar]getscheduler() sched[lowbar]yield() sched[lowbar]get[lowbar]priority[lowbar]max() sched[lowbar]get[lowbar]priority[lowbar]min() sched[lowbar]rr[lowbar]get[lowbar]interval()

5 Getting started#$+
K
5.1 How do I get lxrun ?#KLxrun is currently distributed as a component of SCO Skunkware urlnam, a free CD-ROM containing hundreds of megabytes of pre-compiled and pre-packaged  software for SCO platforms. A Skunkware CD-ROM can be obtained via urlnam and, beginning in 1998, all operating systems released by SCO will contain a Skunkware CD-ROM in the shrink-wrapped product. Lxrun may also be obtained via the SCO Skunkware web site at  urlnam. All SCO Skunkware software is freely redistributable.
5.2 Building lxrun from source#KDetailed instructions on building lxrun from source, configuring the build,  installing the emulation system and additional run-time components, installing  a Linux binary, runtime environment variables, and error messages are contained in the file INSTALL in the lxrun source distribution and on-line in the lxrun FAQ.
The lxrun source distribution contains a Makefile with support for compilation on SCO OpenServer 5, UnixWare 2.x, and UnixWare 7. The Makefile uses the output of "uname -r" to determine the platform. As additional platform support is added this will need to be augmented or the configuration modified to use autoconfig.
To build lxrun on one of the supported platforms, it is only necessary to issue the command "make". The command "make install" will both build lxrun and copy the resulting binaries and documentation into $(DESTDIR) which is set by default to the ./dist directory.
As a convenience this distribution includes a script called  lxfront which can be used with a symbolic link (see below) to provide  a wrapper around the invocation of lxrun the Linux binary name allowing them to be run directly.
Starting with lxrun 0.9.0 the build of lxrun will produce an ld-linux.so.1 as well as the lxrun binary. The ld-linux.so.1 is installed in /lib on the target system and provides support for direct execution of Linux binaries, thus deprecating the need for the lxrun binary front-end except for the execution of Linux a.out binaries.
The lxrun source has the following capability ifdefs: 
· DEF[lowbar]LINUX[lowbar]ROOT - Default root directory name for lxrun to searching under for any native Linux files.  Specifically when a Linux library tries to load a dynamic library, lxrun remaps the pathname to somewhere below this directory.   The value specified here becomes the internal default which can be  overridden by the LINUX[lowbar]ROOT environment variable. Set in Makefile with make macro LXROOT. Default value is "/usr/local/linux"
· TRACE - Flag for making a version of lxrun that emits system call traces  (to file /tmp/lxrun.nnn (where "nnn" is the process pid.)) This can be used for tracing a binaries use of an unimplemented  system call or other runtime problems. Its not enabled bu default  since it slows down the operation of lxrun and produces large log files. (build should be augmented to make a variantly named lxrun binary with this  on regardless). Set in Makefile with make macro TRACE. Default is disabled (off)
· ELF[lowbar]DEBUG - Flag to enable output to stderr of debug traces for the  ELF loader capability of lxrun. Outputs ELF Header information, interpreter remap values and open, load, mmap, mprotect values/status (should be modified to be integrated with TRACE logging) Set in Makefile via make macro DBG. Default is disabled (off)
· UNIDIRECTIONAL[lowbar]PIPES - Flag to enable use of unidirectional pipes (pipe() )  instead of bidirectional pipes (socketpair()) for the pipe() syscall emulation on platforms where pipes are not bidirectional. Not referenced in Makefile. Default is disabled (emulation will use bidirectional pipes)
· NO[lowbar]DISPLAY[lowbar]HACK - Flag to disable remapping of DISPLAY variable from a local server  specification to a full hostname specification. This remapping is done (by default) to address some  problems with local connections on OSr5 (at least) to some X servers. Not referenced in Makefile. Default is disabled (remapping will be done)
· LXRUN[lowbar]AUTO[lowbar]PATH[lowbar]BEHAVIOR - if enabled makes lxrun search for the Linux binary to be run in the  normal PATH rather than in the (expected) absolute pathname given. Not referenced in Makefile. Default is disabled (off)
· ELFMARK[lowbar]HACK - enables detection of binaries marked (with elfmark) as Linux binaries (mark value "LXRN" as an unsigned long). Not referenced in Makefile. Default is disabled (off) - status is experimental.
Platform defines for OpenServer 5 (OSR5), UnixWare 2.x (UNIXWARE)  and UnixWare 7 (GEMINI) are automatically setup in the Makefile.
5.3 Installing the emulation system#KLxrun expects to find all its (Linux) library files in a normal root hierarchy rooted under a single place called the LINUX[lowbar]ROOT. Unless respecified in the build this defaults internally in lxrun to /usr/local/linux.
You can respecify or change it at runtime with the environment variable LINUX[lowbar]ROOT (wherever it ends up this must be the place the Linux  libraries are placed under).
"make install" will install the built binary (lxrun) into /usr/local/bin, lxfront into $LINUX[lowbar]ROOT/bin, and the lxrun program interpreter ld-linux.so.1 into /lib. The HTML documents describing lxrun are placed in /usr/local/man/html/lxrun.
5.4 Installing Linux applications#K
With lxrun 0.9.0 and later, Linux ELF binaries can be installed anywhere in the standard execution path (e.g. /usr/local/bin). See section Direct execution of Linux ELF binaries for details on how this is done. Further, Linux applications distributed in  RPM format can be installed using either a native RPM port  or the Linux RPM run under the control of lxrun. Some additional arguments to RPM may be necessary. For instance, a native port of RPM for SCO OpenServer  is available at urlnam). Using the SCO OpenServer RPM it is necessary to invoke RPM as follows: 
    rpm --nodeps --ignorearch --ignoreos --prefix /usr/local ...

 The Skunkware distribution of RPM for OpenServer includes a shell script front-end rpm4sco which inserts these arguments for you.
Linux a.out and statically linked ELF binaries should be installed in  /usr/local/linux/bin and symbolic links by the name of the binary created  from /usr/local/bin to the lxfront shell script in /usr/local/linux/bin.
5.5 Error messages#Klinuxemul: fatal error: program load failed: No such file or directory
Indicates that the Linux binary couldn't run  (either lxrun could't find the Linux binary or the Linux binary  couldn't find the dynamic linker) It probably means your LINUX[lowbar]ROOT environment variable isn't set up  correctly or you don't have the required minimum Linux libraries.
progname: can't load library 'some[lowbar]library[lowbar]name.so'
Indicates you're missing a shared library that is needed to run a particular binary.  You can either try to find a compiled version of the library from a Linux ftp site (such as urlnam) or if you have access to a running Linux system, you can copy the library directly.  You should put the library in $LINUX[lowbar]ROOT/lib on your host system  (/usr/localLinuxlib by default on a SCO system ).
myprog: can't resolve symbol '[lowbar][lowbar]iob'\  myprog: can't resolve symbol '[lowbar][lowbar]iob'\  myprog: can't resolve symbol '[lowbar][lowbar]ctype'
Indicates that the Linux dynamic loader found a native SCO library and is using it instead of the corresponding Linux binary.   (You can find out exactly which library is causing the problem by examining the lxrun.log file produced by a debugging version of lxrun.)
This will only occur if you have native libraries installed that have the same names as a dependant Linux binary. If you have XFree86 installed, the /usr/X11R6/lib libraries are common culprits.
The best solution is to make sure no native libraries are available anywhere under the directory pointed to by $LINUX[lowbar]ROOT.
5.6 If you come across an unsupported binary#K
·  Go to urlnam and make sure you have the most recent version of lxrun.  If not, download the latest one and try it.  We are updating lxrun with new system calls all the time. 
·  Recompile lxrun with the TRACE option enabled.  (This requires modifying one line in the Makefile.)  This will cause lxrun to produce a history of all system calls used by the binary as it was run (similar to the "truss" and "trace" commands).  The trace dump will be created in a file called "/tmp/lxrun.nnn" where "nnn" is the process id. 
·  Try to narrow down exactly which system call failed. Most likely, the failure will be due to a system call that has not yet been implemented in lxrun. 
·  Implement the system call mapping.  This is usually pretty easy to do.  The vast majority of lxrun's code does mappings of this sort, so you can pick out almost any source file to see how it is done.  Chances are, the system call you need to remap is already in one of the lxrun source files, but its code looks something like this: int lx[lowbar]flock()          [lcub  ] errno=ENOSYS; return -1; [rcub  ]     This means that you're the first person who has gotten around to mapping that particular system call. 
·  After making your modification, recompile lxrun and see if it works.  You may have to remap more than one system call to get your binary working! 
·  E-mail your changes to  skunkware@sco.com. This way, we can put your changes into the next release of lxrun. 
·  If steps 1-5 seem beyond your programming ability, contact  skunkware@sco.com. and maybe one of the Skunkware team will have time to give you a hand with it. Make sure to tell us exactly what program you're having trouble with, and if possible, tell us where you got it.

6 Web Presence#$+
KThe lxrun web site is at  urlnam. Lxrun source is available at urlnam.
Any source changes made (augmentation or bug fixes) doc changes feature requests, questions or problem reports should be mailed to  skunkware@sco.com

7 Authors and Contributors#$+
KThe original author of lxrun was Michael Davidson, an engineer at SCO. Major initial followup work was done by Robert Lipe and Steve Ginzburg. Andrew Gallatin ported it to Solaris/x86 and the rest of the cast includes Bela Lubkin, John W. Temples, Mike Hopkirk, Ralf Gelfand,  Ronald Joe Record and Udo Monk.
Contributors to this document included Michael Davidson, Michael Hopkirk, Robert Lipe, Steve Ginzburg and the principal author - Ronald Record.

8 About This Document#$+
KThis document was created using SGML-Tools 1.0.6 in conjunction with TeX,  Version 3.14159 (Web2C 7.2) running on an SCO UnixWare 7 platform. 
The source to this document is maintained at urlnam. A Makefile and formatted varieties of this document are also available at urlnam. For instance, you will find a postscript version at urlnam.
This document is Copyright (C) 1998 by Ronald Joe Record. All rights reserved. Permission to use, copy, and distribute this  document for any purpose and without fee is hereby granted, provided  that the above copyright notice appear in all copies and that both that  copyright notice and this permission notice appear in supporting documentation.