Charles B. Haley
Dennis. M. Ritchie
This document discusses how to assemble or compile various parts of the UNIX system software. This may be necessary because a command or library is accidentally deleted or otherwise destroyed; also, it may be desirable to install a modified version of some command or library routine. A few commands depend to some degree on the current configuration of the system; thus in any new system modifications to some commands are advisable. Most of the likely modifications relate to the standard disk devices contained in the system. For example, the df(1) (`disk free') command has built into it the names of the standardly present disk storage drives (e.g. `/dev/rf0', `/dev/rp0'). Df(1) takes an argument to indicate which disk to examine, but it is convenient if its default argument is adjusted to reflect the ordinarily present devices. The companion document `Setting up UNIX' discusses which commands are likely to require changes.
The source files for commands and subroutines reside in several subdirectories of the directory /usr/src. These subdirectories, and a general description of their contents, are
The regeneration of most commands is straightforward. The `cmd' directory will contain either a source file for the command or a subdirectory containing the set of files that make up the command. If it is a single file the command
If the source files are in a subdirectory there will be a `makefile' (see make(1)) to control the regeneration. After changing to the proper directory (cd(1)) you type one of the following:
Some of the makefiles have other options. Print (cat(1)) the ones you are interested in to find out.
The assembler consists of two executable files: /bin/as and /lib/as2. The first is the 0-th pass: it reads the source program, converts it to an intermediate form in a temporary file `/tmp/atm0?', and estimates the final locations of symbols. It also makes two or three other temporary files which contain the ordinary symbol table, a table of temporary symbols (like 1:) and possibly an overflow intermediate file. The program /lib/as2 acts as an ordinary multiple pass assembler with input taken from the files produced by /bin/as.
The source files for /bin/as are named `/usr/src/cmd/as/as1?.s' (there are 9 of them); /lib/as2 is produced from the source files `/usr/src/cmd/as/as2?.s'; they likewise are 9 in number. Considerable care should be exercised in replacing either component of the assembler. Remember that if the assembler is lost, the only recourse is to replace it from some backup storage; a broken assembler cannot assemble itself.
The C compiler consists of seven routines: `/bin/cc', which calls the phases of the compiler proper, the compiler control line expander `/lib/cpp', the assembler (`as'), and the loader (`ld'). The phases of the C compiler are `/lib/c0', which is the first phase of the compiler; `/lib/c1', which is the second phase of the compiler; and `/lib/c2', which is the optional third phase optimizer. The loss of the C compiler is as serious as that of the assembler.
The source for /bin/cc resides in `/usr/src/cmd/cc.c'. Its loss alone (or that of c2) is not fatal. If needed, prog.c can be compiled by
The source for the compiler proper is in the directory /usr/src/cmd/c. The first phase (/lib/c0) is generated from the files c00.c, ..., c05.c, which must be compiled by the C compiler. There is also c0.h, a header file included by the C programs of the first phase. To make a new /lib/c0 use
The second phase of C (/lib/c1) is generated from the source files c10.c, ..., c13.c, the include-file c1.h, and a set of object-code tables combined into table.o. To generate a new second phase use
In a similar manner, the third phase of the C compiler (/lib/c2) is made up from the files c20.c and c21.c together with c2.h. Its loss is not critical since it is completely optional.
The set of tables mentioned above is generated from the file table.s. This `.s' file is not in fact assembler source; it must be converted by use of the cvopt program, whose source and object are located in the C directory. Normally this is taken care of by make(1). You might want to look at the makefile to see what it does.
The source and object programs for UNIX are kept in four subdirectories of /usr/sys. In the subdirectory h there are several files ending in `.h'; these are header files which are picked up (via `#include ...') as required by each system module. The subdirectory dev consists mostly of the device drivers together with a few other things. The subdirectory sys is the rest of the system. There are files of the form LIBx in the directories sys and dev. These are archives (ar(1)) which contain the object versions of the routines in the directory.
Subdirectory conf contains the files which control device configuration of the system. L.s specifies the contents of the interrupt vectors; c.c contains the tables which relate device numbers to handler routines. A third file, mch.s, contains all the machine-language code in the system. A fourth file, mch0.s, is generated by mkconf(1) and contains flags indicating what sort of tape drive is available for taking crash dumps.
There are two ways to recreate the system. Use
When the make is done, the new system is present in the current directory as `unix'. It should be tested before destroying the currently running `/unix', this is best done by doing something like
To install a new device driver, compile it and put it into its library. The best way to put it into the library is to use the command
Next, the device's interrupt vector must be entered in l.s. This is probably already done by the routine mkconf(1), but if the device is esoteric or nonstandard you will have to massage l.s by hand. This involves placing a pointer to a callout routine and the device's priority level in the vector. Use some other device (like the console) as a guide. Notice that the entries in l.s must be in order as the assembler does not permit moving the location counter `.' backwards. The assembler also does not permit assignation of an absolute number to `.', which is the reason for the `. = ZERO+100' subterfuge. If a constant smaller than 16(10) is added to the priority level, this number will be available as the first argument of the interrupt routine. This stratagem is used when several similar devices share the same interrupt routine (as in dl11's).
If you have to massage l.s, be sure to add the code to actually transfer to the interrupt routine. Again use the console as a guide. The apparent strangeness of this code is due to running the kernel in separate I&D space. The call routine saves registers as required and prepares a C-style call on the actual interrupt routine named after the `jmp' instruction. When the routine returns, call restores the registers and performs an rti instruction. As an aside, note that external names in C programs have an underscore (`_') prepended to them.
The second step which must be performed to add a device unknown to mkconf is to add it to the configuration table /usr/sys/conf/c.c. This file contains two subtables, one for block-type devices, and one for character-type devices. Block devices include disks, DECtape, and magtape. All other devices are character devices. A line in each of these tables gives all the information the system needs to know about the device handler; the ordinal position of the line in the table implies its major device number, starting at 0.
There are four subentries per line in the block device table, which give its open routine, close routine, strategy routine, and device table. The open and close routines may be nonexistent, in which case the name `nulldev' is given; this routine merely returns. The strategy routine is called to do any I/O, and the device table contains status information for the device.
For character devices, each line in the table specifies a routine for open, close, read, and write, and one which sets and returns device-specific status (used, for example, for stty and gtty on typewriters). If there is no open or close routine, `nulldev' may be given; if there is no read, write, or status routine, `nodev' may be given. Nodev sets an error flag and returns.
The final step which must be taken to install a device is to make a special file for it. This is done by mknod(1), to which you must specify the device class (block or character), major device number (relative line in the configuration table) and minor device number (which is made available to the driver at appropriate times).
The documents `Setting up Unix' and `The Unix IO system' may aid in comprehending these steps.
The library /lib/libc.a is where most of the subroutines described in sections 2 and 3 of the manual are kept. This library can be remade using the following commands:
The routines in /usr/src/cmd/libc/csu (C start up) are not in libc.a. These are separately assembled and put into /lib. The commands to do this are
Likewise, the directories containing the source for the other libraries have files compall (that recompiles everything) and mklib (that recreates the library).
There are several tunable parameters in the system. These set the size of various tables and limits. They are found in the file /usr/sys/h/param.h as manifests (`#define's). Their values are rather generous in the system as distributed. Our typical maximum number of users is about 20, but there are many daemon processes.
When any parameter is changed, it is prudent to recompile the entire system, as discussed above. A brief discussion of each follows: