1450 lines
37 KiB
Plaintext
1450 lines
37 KiB
Plaintext
|
.HTML "How to Use the Plan 9 C Compiler
|
|||
|
.TL
|
|||
|
How to Use the Plan 9 C Compiler
|
|||
|
.AU
|
|||
|
Rob Pike
|
|||
|
rob@plan9.bell-labs.com
|
|||
|
.SH
|
|||
|
Introduction
|
|||
|
.PP
|
|||
|
The C compiler on Plan 9 is a wholly new program; in fact
|
|||
|
it was the first piece of software written for what would
|
|||
|
eventually become Plan 9 from Bell Labs.
|
|||
|
Programmers familiar with existing C compilers will find
|
|||
|
a number of differences in both the language the Plan 9 compiler
|
|||
|
accepts and in how the compiler is used.
|
|||
|
.PP
|
|||
|
The compiler is really a set of compilers, one for each
|
|||
|
architecture \(em MIPS, SPARC, Motorola 68020, Intel 386, etc. \(em
|
|||
|
that accept a dialect of ANSI C and efficiently produce
|
|||
|
fairly good code for the target machine.
|
|||
|
There is a packaging of the compiler that accepts strict ANSI C for
|
|||
|
a POSIX environment, but this document focuses on the
|
|||
|
native Plan 9 environment, that in which all the system source and
|
|||
|
almost all the utilities are written.
|
|||
|
.SH
|
|||
|
Source
|
|||
|
.PP
|
|||
|
The language accepted by the compilers is the core ANSI C language
|
|||
|
with some modest extensions,
|
|||
|
a greatly simplified preprocessor,
|
|||
|
a smaller library that includes system calls and related facilities,
|
|||
|
and a completely different structure for include files.
|
|||
|
.PP
|
|||
|
Official ANSI C accepts the old (K&R) style of declarations for
|
|||
|
functions; the Plan 9 compilers
|
|||
|
are more demanding.
|
|||
|
Without an explicit run-time flag
|
|||
|
.CW -B ) (
|
|||
|
whose use is discouraged, the compilers insist
|
|||
|
on new-style function declarations, that is, prototypes for
|
|||
|
function arguments.
|
|||
|
The function declarations in the libraries' include files are
|
|||
|
all in the new style so the interfaces are checked at compile time.
|
|||
|
For C programmers who have not yet switched to function prototypes
|
|||
|
the clumsy syntax may seem repellent but the payoff in stronger typing
|
|||
|
is substantial.
|
|||
|
Those who wish to import existing software to Plan 9 are urged
|
|||
|
to use the opportunity to update their code.
|
|||
|
.PP
|
|||
|
The compilers include an integrated preprocessor that accepts the familiar
|
|||
|
.CW #include ,
|
|||
|
.CW #define
|
|||
|
for macros both with and without arguments,
|
|||
|
.CW #undef ,
|
|||
|
.CW #line ,
|
|||
|
.CW #ifdef ,
|
|||
|
.CW #ifndef ,
|
|||
|
and
|
|||
|
.CW #endif .
|
|||
|
It
|
|||
|
supports neither
|
|||
|
.CW #if
|
|||
|
nor
|
|||
|
.CW ## ,
|
|||
|
although it does
|
|||
|
honor a few
|
|||
|
.CW #pragmas .
|
|||
|
The
|
|||
|
.CW #if
|
|||
|
directive was omitted because it greatly complicates the
|
|||
|
preprocessor, is never necessary, and is usually abused.
|
|||
|
Conditional compilation in general makes code hard to understand;
|
|||
|
the Plan 9 source uses it sparingly.
|
|||
|
Also, because the compilers remove dead code, regular
|
|||
|
.CW if
|
|||
|
statements with constant conditions are more readable equivalents to many
|
|||
|
.CW #ifs .
|
|||
|
To compile imported code ineluctably fouled by
|
|||
|
.CW #if
|
|||
|
there is a separate command,
|
|||
|
.CW /bin/cpp ,
|
|||
|
that implements the complete ANSI C preprocessor specification.
|
|||
|
.PP
|
|||
|
Include files fall into two groups: machine-dependent and machine-independent.
|
|||
|
The machine-independent files occupy the directory
|
|||
|
.CW /sys/include ;
|
|||
|
the others are placed in a directory appropriate to the machine, such as
|
|||
|
.CW /mips/include .
|
|||
|
The compiler searches for include files
|
|||
|
first in the machine-dependent directory and then
|
|||
|
in the machine-independent directory.
|
|||
|
At the time of writing there are thirty-one machine-independent include
|
|||
|
files and two (per machine) machine-dependent ones:
|
|||
|
.CW <ureg.h>
|
|||
|
and
|
|||
|
.CW <u.h> .
|
|||
|
The first describes the layout of registers on the system stack,
|
|||
|
for use by the debugger.
|
|||
|
The second defines some
|
|||
|
architecture-dependent types such as
|
|||
|
.CW jmp_buf
|
|||
|
for
|
|||
|
.CW setjmp
|
|||
|
and the
|
|||
|
.CW va_arg
|
|||
|
and
|
|||
|
.CW va_list
|
|||
|
macros for handling arguments to variadic functions,
|
|||
|
as well as a set of
|
|||
|
.CW typedef
|
|||
|
abbreviations for
|
|||
|
.CW unsigned
|
|||
|
.CW short
|
|||
|
and so on.
|
|||
|
.PP
|
|||
|
Here is an excerpt from
|
|||
|
.CW /68020/include/u.h :
|
|||
|
.P1
|
|||
|
#define nil ((void*)0)
|
|||
|
typedef unsigned short ushort;
|
|||
|
typedef unsigned char uchar;
|
|||
|
typedef unsigned long ulong;
|
|||
|
typedef unsigned int uint;
|
|||
|
typedef signed char schar;
|
|||
|
typedef long long vlong;
|
|||
|
|
|||
|
typedef long jmp_buf[2];
|
|||
|
#define JMPBUFSP 0
|
|||
|
#define JMPBUFPC 1
|
|||
|
#define JMPBUFDPC 0
|
|||
|
.P2
|
|||
|
Plan 9 programs use
|
|||
|
.CW nil
|
|||
|
for the name of the zero-valued pointer.
|
|||
|
The type
|
|||
|
.CW vlong
|
|||
|
is the largest integer type available; on most architectures it
|
|||
|
is a 64-bit value.
|
|||
|
A couple of other types in
|
|||
|
.CW <u.h>
|
|||
|
are
|
|||
|
.CW u32int ,
|
|||
|
which is guaranteed to have exactly 32 bits (a possibility on all the supported architectures) and
|
|||
|
.CW mpdigit ,
|
|||
|
which is used by the multiprecision math package
|
|||
|
.CW <mp.h> .
|
|||
|
The
|
|||
|
.CW #define
|
|||
|
constants permit an architecture-independent (but compiler-dependent)
|
|||
|
implementation of stack-switching using
|
|||
|
.CW setjmp
|
|||
|
and
|
|||
|
.CW longjmp .
|
|||
|
.PP
|
|||
|
Every Plan 9 C program begins
|
|||
|
.P1
|
|||
|
#include <u.h>
|
|||
|
.P2
|
|||
|
because all the other installed header files use the
|
|||
|
.CW typedefs
|
|||
|
declared in
|
|||
|
.CW <u.h> .
|
|||
|
.PP
|
|||
|
In strict ANSI C, include files are grouped to collect related functions
|
|||
|
in a single file: one for string functions, one for memory functions,
|
|||
|
one for I/O, and none for system calls.
|
|||
|
Each include file is protected by an
|
|||
|
.CW #ifdef
|
|||
|
to guarantee its contents are seen by the compiler only once.
|
|||
|
Plan 9 takes a different approach. Other than a few include
|
|||
|
files that define external formats such as archives, the files in
|
|||
|
.CW /sys/include
|
|||
|
correspond to
|
|||
|
.I libraries.
|
|||
|
If a program is using a library, it includes the corresponding header.
|
|||
|
The default C library comprises string functions, memory functions, and
|
|||
|
so on, largely as in ANSI C, some formatted I/O routines,
|
|||
|
plus all the system calls and related functions.
|
|||
|
To use these functions, one must
|
|||
|
.CW #include
|
|||
|
the file
|
|||
|
.CW <libc.h> ,
|
|||
|
which in turn must follow
|
|||
|
.CW <u.h> ,
|
|||
|
to define their prototypes for the compiler.
|
|||
|
Here is the complete source to the traditional first C program:
|
|||
|
.P1
|
|||
|
#include <u.h>
|
|||
|
#include <libc.h>
|
|||
|
|
|||
|
void
|
|||
|
main(void)
|
|||
|
{
|
|||
|
print("hello world\en");
|
|||
|
exits(0);
|
|||
|
}
|
|||
|
.P2
|
|||
|
The
|
|||
|
.CW print
|
|||
|
routine and its relatives
|
|||
|
.CW fprint
|
|||
|
and
|
|||
|
.CW sprint
|
|||
|
resemble the similarly-named functions in Standard I/O but are not
|
|||
|
attached to a specific I/O library.
|
|||
|
In Plan 9
|
|||
|
.CW main
|
|||
|
is not integer-valued; it should call
|
|||
|
.CW exits ,
|
|||
|
which takes a string argument (or null; here ANSI C promotes the 0 to a
|
|||
|
.CW char* ).
|
|||
|
All these functions are, of course, documented in the Programmer's Manual.
|
|||
|
.PP
|
|||
|
To use
|
|||
|
.CW printf ,
|
|||
|
.CW <stdio.h>
|
|||
|
must be included to define the function prototype for
|
|||
|
.CW printf :
|
|||
|
.P1
|
|||
|
#include <u.h>
|
|||
|
#include <libc.h>
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
void
|
|||
|
main(int argc, char *argv[])
|
|||
|
{
|
|||
|
printf("%s: hello world; argc = %d\en", argv[0], argc);
|
|||
|
exits(0);
|
|||
|
}
|
|||
|
.P2
|
|||
|
In practice, Standard I/O is not used much in Plan 9. I/O libraries are
|
|||
|
discussed in a later section of this document.
|
|||
|
.PP
|
|||
|
There are libraries for handling regular expressions, raster graphics,
|
|||
|
windows, and so on, and each has an associated include file.
|
|||
|
The manual for each library states which include files are needed.
|
|||
|
The files are not protected against multiple inclusion and themselves
|
|||
|
contain no nested
|
|||
|
.CW #includes .
|
|||
|
Instead the
|
|||
|
programmer is expected to sort out the requirements
|
|||
|
and to
|
|||
|
.CW #include
|
|||
|
the necessary files once at the top of each source file. In practice this is
|
|||
|
trivial: this way of handling include files is so straightforward
|
|||
|
that it is rare for a source file to contain more than half a dozen
|
|||
|
.CW #includes .
|
|||
|
.PP
|
|||
|
The compilers do their own register allocation so the
|
|||
|
.CW register
|
|||
|
keyword is ignored.
|
|||
|
For different reasons,
|
|||
|
.CW volatile
|
|||
|
and
|
|||
|
.CW const
|
|||
|
are also ignored.
|
|||
|
.PP
|
|||
|
To make it easier to share code with other systems, Plan 9 has a version
|
|||
|
of the compiler,
|
|||
|
.CW pcc ,
|
|||
|
that provides the standard ANSI C preprocessor, headers, and libraries
|
|||
|
with POSIX extensions.
|
|||
|
.CW Pcc
|
|||
|
is recommended only
|
|||
|
when broad external portability is mandated. It compiles slower,
|
|||
|
produces slower code (it takes extra work to simulate POSIX on Plan 9),
|
|||
|
eliminates those parts of the Plan 9 interface
|
|||
|
not related to POSIX, and illustrates the clumsiness of an environment
|
|||
|
designed by committee.
|
|||
|
.CW Pcc
|
|||
|
is described in more detail in
|
|||
|
.I
|
|||
|
APE\(emThe ANSI/POSIX Environment,
|
|||
|
.R
|
|||
|
by Howard Trickey.
|
|||
|
.SH
|
|||
|
Process
|
|||
|
.PP
|
|||
|
Each CPU architecture supported by Plan 9 is identified by a single,
|
|||
|
arbitrary, alphanumeric character:
|
|||
|
.CW k
|
|||
|
for SPARC,
|
|||
|
.CW q
|
|||
|
for Motorola Power PC 630 and 640,
|
|||
|
.CW v
|
|||
|
for MIPS,
|
|||
|
.CW 0
|
|||
|
for little-endian MIPS,
|
|||
|
.CW 1
|
|||
|
for Motorola 68000,
|
|||
|
.CW 2
|
|||
|
for Motorola 68020 and 68040,
|
|||
|
.CW 5
|
|||
|
for Acorn ARM 7500,
|
|||
|
.CW 6
|
|||
|
for AMD 64,
|
|||
|
.CW 7
|
|||
|
for DEC Alpha,
|
|||
|
.CW 8
|
|||
|
for Intel 386, and
|
|||
|
.CW 9
|
|||
|
for AMD 29000.
|
|||
|
The character labels the support tools and files for that architecture.
|
|||
|
For instance, for the 68020 the compiler is
|
|||
|
.CW 2c ,
|
|||
|
the assembler is
|
|||
|
.CW 2a ,
|
|||
|
the link editor/loader is
|
|||
|
.CW 2l ,
|
|||
|
the object files are suffixed
|
|||
|
.CW \&.2 ,
|
|||
|
and the default name for an executable file is
|
|||
|
.CW 2.out .
|
|||
|
Before we can use the compiler we therefore need to know which
|
|||
|
machine we are compiling for.
|
|||
|
The next section explains how this decision is made; for the moment
|
|||
|
assume we are building 68020 binaries and make the mental substitution for
|
|||
|
.CW 2
|
|||
|
appropriate to the machine you are actually using.
|
|||
|
.PP
|
|||
|
To convert source to an executable binary is a two-step process.
|
|||
|
First run the compiler,
|
|||
|
.CW 2c ,
|
|||
|
on the source, say
|
|||
|
.CW file.c ,
|
|||
|
to generate an object file
|
|||
|
.CW file.2 .
|
|||
|
Then run the loader,
|
|||
|
.CW 2l ,
|
|||
|
to generate an executable
|
|||
|
.CW 2.out
|
|||
|
that may be run (on a 680X0 machine):
|
|||
|
.P1
|
|||
|
2c file.c
|
|||
|
2l file.2
|
|||
|
2.out
|
|||
|
.P2
|
|||
|
The loader automatically links with whatever libraries the program
|
|||
|
needs, usually including the standard C library as defined by
|
|||
|
.CW <libc.h> .
|
|||
|
Of course the compiler and loader have lots of options, both familiar and new;
|
|||
|
see the manual for details.
|
|||
|
The compiler does not generate an executable automatically;
|
|||
|
the output of the compiler must be given to the loader.
|
|||
|
Since most compilation is done under the control of
|
|||
|
.CW mk
|
|||
|
(see below), this is rarely an inconvenience.
|
|||
|
.PP
|
|||
|
The distribution of work between the compiler and loader is unusual.
|
|||
|
The compiler integrates preprocessing, parsing, register allocation,
|
|||
|
code generation and some assembly.
|
|||
|
Combining these tasks in a single program is part of the reason for
|
|||
|
the compiler's efficiency.
|
|||
|
The loader does instruction selection, branch folding,
|
|||
|
instruction scheduling,
|
|||
|
and writes the final executable.
|
|||
|
There is no separate C preprocessor and no assembler in the usual pipeline.
|
|||
|
Instead the intermediate object file
|
|||
|
(here a
|
|||
|
.CW \&.2
|
|||
|
file) is a type of binary assembly language.
|
|||
|
The instructions in the intermediate format are not exactly those in
|
|||
|
the machine. For example, on the 68020 the object file may specify
|
|||
|
a MOVE instruction but the loader will decide just which variant of
|
|||
|
the MOVE instruction \(em MOVE immediate, MOVE quick, MOVE address,
|
|||
|
etc. \(em is most efficient.
|
|||
|
.PP
|
|||
|
The assembler,
|
|||
|
.CW 2a ,
|
|||
|
is just a translator between the textual and binary
|
|||
|
representations of the object file format.
|
|||
|
It is not an assembler in the traditional sense. It has limited
|
|||
|
macro capabilities (the same as the integral C preprocessor in the compiler),
|
|||
|
clumsy syntax, and minimal error checking. For instance, the assembler
|
|||
|
will accept an instruction (such as memory-to-memory MOVE on the MIPS) that the
|
|||
|
machine does not actually support; only when the output of the assembler
|
|||
|
is passed to the loader will the error be discovered.
|
|||
|
The assembler is intended only for writing things that need access to instructions
|
|||
|
invisible from C,
|
|||
|
such as the machine-dependent
|
|||
|
part of an operating system;
|
|||
|
very little code in Plan 9 is in assembly language.
|
|||
|
.PP
|
|||
|
The compilers take an option
|
|||
|
.CW -S
|
|||
|
that causes them to print on their standard output the generated code
|
|||
|
in a format acceptable as input to the assemblers.
|
|||
|
This is of course merely a formatting of the
|
|||
|
data in the object file; therefore the assembler is just
|
|||
|
an
|
|||
|
ASCII-to-binary converter for this format.
|
|||
|
Other than the specific instructions, the input to the assemblers
|
|||
|
is largely architecture-independent; see
|
|||
|
``A Manual for the Plan 9 Assembler'',
|
|||
|
by Rob Pike,
|
|||
|
for more information.
|
|||
|
.PP
|
|||
|
The loader is an integral part of the compilation process.
|
|||
|
Each library header file contains a
|
|||
|
.CW #pragma
|
|||
|
that tells the loader the name of the associated archive; it is
|
|||
|
not necessary to tell the loader which libraries a program uses.
|
|||
|
The C run-time startup is found, by default, in the C library.
|
|||
|
The loader starts with an undefined
|
|||
|
symbol,
|
|||
|
.CW _main ,
|
|||
|
that is resolved by pulling in the run-time startup code from the library.
|
|||
|
(The loader undefines
|
|||
|
.CW _mainp
|
|||
|
when profiling is enabled, to force loading of the profiling start-up
|
|||
|
instead.)
|
|||
|
.PP
|
|||
|
Unlike its counterpart on other systems, the Plan 9 loader rearranges
|
|||
|
data to optimize access. This means the order of variables in the
|
|||
|
loaded program is unrelated to its order in the source.
|
|||
|
Most programs don't care, but some assume that, for example, the
|
|||
|
variables declared by
|
|||
|
.P1
|
|||
|
int a;
|
|||
|
int b;
|
|||
|
.P2
|
|||
|
will appear at adjacent addresses in memory. On Plan 9, they won't.
|
|||
|
.SH
|
|||
|
Heterogeneity
|
|||
|
.PP
|
|||
|
When the system starts or a user logs in the environment is configured
|
|||
|
so the appropriate binaries are available in
|
|||
|
.CW /bin .
|
|||
|
The configuration process is controlled by an environment variable,
|
|||
|
.CW $cputype ,
|
|||
|
with value such as
|
|||
|
.CW mips ,
|
|||
|
.CW 68020 ,
|
|||
|
.CW 386 ,
|
|||
|
or
|
|||
|
.CW sparc .
|
|||
|
For each architecture there is a directory in the root,
|
|||
|
with the appropriate name,
|
|||
|
that holds the binary and library files for that architecture.
|
|||
|
Thus
|
|||
|
.CW /mips/lib
|
|||
|
contains the object code libraries for MIPS programs,
|
|||
|
.CW /mips/include
|
|||
|
holds MIPS-specific include files, and
|
|||
|
.CW /mips/bin
|
|||
|
has the MIPS binaries.
|
|||
|
These binaries are attached to
|
|||
|
.CW /bin
|
|||
|
at boot time by binding
|
|||
|
.CW /$cputype/bin
|
|||
|
to
|
|||
|
.CW /bin ,
|
|||
|
so
|
|||
|
.CW /bin
|
|||
|
always contains the correct files.
|
|||
|
.PP
|
|||
|
The MIPS compiler,
|
|||
|
.CW vc ,
|
|||
|
by definition
|
|||
|
produces object files for the MIPS architecture,
|
|||
|
regardless of the architecture of the machine on which the compiler is running.
|
|||
|
There is a version of
|
|||
|
.CW vc
|
|||
|
compiled for each architecture:
|
|||
|
.CW /mips/bin/vc ,
|
|||
|
.CW /68020/bin/vc ,
|
|||
|
.CW /sparc/bin/vc ,
|
|||
|
and so on,
|
|||
|
each capable of producing MIPS object files regardless of the native
|
|||
|
instruction set.
|
|||
|
If one is running on a SPARC,
|
|||
|
.CW /sparc/bin/vc
|
|||
|
will compile programs for the MIPS;
|
|||
|
if one is running on machine
|
|||
|
.CW $cputype ,
|
|||
|
.CW /$cputype/bin/vc
|
|||
|
will compile programs for the MIPS.
|
|||
|
.PP
|
|||
|
Because of the bindings that assemble
|
|||
|
.CW /bin ,
|
|||
|
the shell always looks for a command, say
|
|||
|
.CW date ,
|
|||
|
in
|
|||
|
.CW /bin
|
|||
|
and automatically finds the file
|
|||
|
.CW /$cputype/bin/date .
|
|||
|
Therefore the MIPS compiler is known as just
|
|||
|
.CW vc ;
|
|||
|
the shell will invoke
|
|||
|
.CW /bin/vc
|
|||
|
and that is guaranteed to be the version of the MIPS compiler
|
|||
|
appropriate for the machine running the command.
|
|||
|
Regardless of the architecture of the compiling machine,
|
|||
|
.CW /bin/vc
|
|||
|
is
|
|||
|
.I always
|
|||
|
the MIPS compiler.
|
|||
|
.PP
|
|||
|
Also, the output of
|
|||
|
.CW vc
|
|||
|
and
|
|||
|
.CW vl
|
|||
|
is completely independent of the machine type on which they are executed:
|
|||
|
.CW \&.v
|
|||
|
files compiled (with
|
|||
|
.CW vc )
|
|||
|
on a SPARC may be linked (with
|
|||
|
.CW vl )
|
|||
|
on a 386.
|
|||
|
(The resulting
|
|||
|
.CW v.out
|
|||
|
will run, of course, only on a MIPS.)
|
|||
|
Similarly, the MIPS libraries in
|
|||
|
.CW /mips/lib
|
|||
|
are suitable for loading with
|
|||
|
.CW vl
|
|||
|
on any machine; there is only one set of MIPS libraries, not one
|
|||
|
set for each architecture that supports the MIPS compiler.
|
|||
|
.SH
|
|||
|
Heterogeneity and \f(CWmk\fP
|
|||
|
.PP
|
|||
|
Most software on Plan 9 is compiled under the control of
|
|||
|
.CW mk ,
|
|||
|
a descendant of
|
|||
|
.CW make
|
|||
|
that is documented in the Programmer's Manual.
|
|||
|
A convention used throughout the
|
|||
|
.CW mkfiles
|
|||
|
makes it easy to compile the source into binary suitable for any architecture.
|
|||
|
.PP
|
|||
|
The variable
|
|||
|
.CW $cputype
|
|||
|
is advisory: it reports the architecture of the current environment, and should
|
|||
|
not be modified. A second variable,
|
|||
|
.CW $objtype ,
|
|||
|
is used to set which architecture is being
|
|||
|
.I compiled
|
|||
|
for.
|
|||
|
The value of
|
|||
|
.CW $objtype
|
|||
|
can be used by a
|
|||
|
.CW mkfile
|
|||
|
to configure the compilation environment.
|
|||
|
.PP
|
|||
|
In each machine's root directory there is a short
|
|||
|
.CW mkfile
|
|||
|
that defines a set of macros for the compiler, loader, etc.
|
|||
|
Here is
|
|||
|
.CW /mips/mkfile :
|
|||
|
.P1
|
|||
|
</sys/src/mkfile.proto
|
|||
|
|
|||
|
CC=vc
|
|||
|
LD=vl
|
|||
|
O=v
|
|||
|
AS=va
|
|||
|
.P2
|
|||
|
The line
|
|||
|
.P1
|
|||
|
</sys/src/mkfile.proto
|
|||
|
.P2
|
|||
|
causes
|
|||
|
.CW mk
|
|||
|
to include the file
|
|||
|
.CW /sys/src/mkfile.proto ,
|
|||
|
which contains general definitions:
|
|||
|
.P1
|
|||
|
#
|
|||
|
# common mkfile parameters shared by all architectures
|
|||
|
#
|
|||
|
|
|||
|
OS=v486xq7
|
|||
|
CPUS=mips 386 power alpha
|
|||
|
CFLAGS=-FVw
|
|||
|
LEX=lex
|
|||
|
YACC=yacc
|
|||
|
MK=/bin/mk
|
|||
|
.P2
|
|||
|
.CW CC
|
|||
|
is obviously the compiler,
|
|||
|
.CW AS
|
|||
|
the assembler, and
|
|||
|
.CW LD
|
|||
|
the loader.
|
|||
|
.CW O
|
|||
|
is the suffix for the object files and
|
|||
|
.CW CPUS
|
|||
|
and
|
|||
|
.CW OS
|
|||
|
are used in special rules described below.
|
|||
|
.PP
|
|||
|
Here is a
|
|||
|
.CW mkfile
|
|||
|
to build the installed source for
|
|||
|
.CW sam :
|
|||
|
.P1
|
|||
|
</$objtype/mkfile
|
|||
|
OBJ=sam.$O address.$O buffer.$O cmd.$O disc.$O error.$O \e
|
|||
|
file.$O io.$O list.$O mesg.$O moveto.$O multi.$O \e
|
|||
|
plan9.$O rasp.$O regexp.$O string.$O sys.$O xec.$O
|
|||
|
|
|||
|
$O.out: $OBJ
|
|||
|
$LD $OBJ
|
|||
|
|
|||
|
install: $O.out
|
|||
|
cp $O.out /$objtype/bin/sam
|
|||
|
|
|||
|
installall:
|
|||
|
for(objtype in $CPUS) mk install
|
|||
|
|
|||
|
%.$O: %.c
|
|||
|
$CC $CFLAGS $stem.c
|
|||
|
|
|||
|
$OBJ: sam.h errors.h mesg.h
|
|||
|
address.$O cmd.$O parse.$O xec.$O unix.$O: parse.h
|
|||
|
|
|||
|
clean:V:
|
|||
|
rm -f [$OS].out *.[$OS] y.tab.?
|
|||
|
.P2
|
|||
|
(The actual
|
|||
|
.CW mkfile
|
|||
|
imports most of its rules from other secondary files, but
|
|||
|
this example works and is not misleading.)
|
|||
|
The first line causes
|
|||
|
.CW mk
|
|||
|
to include the contents of
|
|||
|
.CW /$objtype/mkfile
|
|||
|
in the current
|
|||
|
.CW mkfile .
|
|||
|
If
|
|||
|
.CW $objtype
|
|||
|
is
|
|||
|
.CW mips ,
|
|||
|
this inserts the MIPS macro definitions into the
|
|||
|
.CW mkfile .
|
|||
|
In this case the rule for
|
|||
|
.CW $O.out
|
|||
|
uses the MIPS tools to build
|
|||
|
.CW v.out .
|
|||
|
The
|
|||
|
.CW %.$O
|
|||
|
rule in the file uses
|
|||
|
.CW mk 's
|
|||
|
pattern matching facilities to convert the source files to the object
|
|||
|
files through the compiler.
|
|||
|
(The text of the rules is passed directly to the shell,
|
|||
|
.CW rc ,
|
|||
|
without further translation.
|
|||
|
See the
|
|||
|
.CW mk
|
|||
|
manual if any of this is unfamiliar.)
|
|||
|
Because the default rule builds
|
|||
|
.CW $O.out
|
|||
|
rather than
|
|||
|
.CW sam ,
|
|||
|
it is possible to maintain binaries for multiple machines in the
|
|||
|
same source directory without conflict.
|
|||
|
This is also, of course, why the output files from the various
|
|||
|
compilers and loaders
|
|||
|
have distinct names.
|
|||
|
.PP
|
|||
|
The rest of the
|
|||
|
.CW mkfile
|
|||
|
should be easy to follow; notice how the rules for
|
|||
|
.CW clean
|
|||
|
and
|
|||
|
.CW installall
|
|||
|
(that is, install versions for all architectures) use other macros
|
|||
|
defined in
|
|||
|
.CW /$objtype/mkfile .
|
|||
|
In Plan 9,
|
|||
|
.CW mkfiles
|
|||
|
for commands conventionally contain rules to
|
|||
|
.CW install
|
|||
|
(compile and install the version for
|
|||
|
.CW $objtype ),
|
|||
|
.CW installall
|
|||
|
(compile and install for all
|
|||
|
.CW $objtypes ),
|
|||
|
and
|
|||
|
.CW clean
|
|||
|
(remove all object files, binaries, etc.).
|
|||
|
.PP
|
|||
|
The
|
|||
|
.CW mkfile
|
|||
|
is easy to use. To build a MIPS binary,
|
|||
|
.CW v.out :
|
|||
|
.P1
|
|||
|
% objtype=mips
|
|||
|
% mk
|
|||
|
.P2
|
|||
|
To build and install a MIPS binary:
|
|||
|
.P1
|
|||
|
% objtype=mips
|
|||
|
% mk install
|
|||
|
.P2
|
|||
|
To build and install all versions:
|
|||
|
.P1
|
|||
|
% mk installall
|
|||
|
.P2
|
|||
|
These conventions make cross-compilation as easy to manage
|
|||
|
as traditional native compilation.
|
|||
|
Plan 9 programs compile and run without change on machines from
|
|||
|
large multiprocessors to laptops. For more information about this process, see
|
|||
|
``Plan 9 Mkfiles'',
|
|||
|
by Bob Flandrena.
|
|||
|
.SH
|
|||
|
Portability
|
|||
|
.PP
|
|||
|
Within Plan 9, it is painless to write portable programs, programs whose
|
|||
|
source is independent of the machine on which they execute.
|
|||
|
The operating system is fixed and the compiler, headers and libraries
|
|||
|
are constant so most of the stumbling blocks to portability are removed.
|
|||
|
Attention to a few details can avoid those that remain.
|
|||
|
.PP
|
|||
|
Plan 9 is a heterogeneous environment, so programs must
|
|||
|
.I expect
|
|||
|
that external files will be written by programs on machines of different
|
|||
|
architectures.
|
|||
|
The compilers, for instance, must handle without confusion
|
|||
|
object files written by other machines.
|
|||
|
The traditional approach to this problem is to pepper the source with
|
|||
|
.CW #ifdefs
|
|||
|
to turn byte-swapping on and off.
|
|||
|
Plan 9 takes a different approach: of the handful of machine-dependent
|
|||
|
.CW #ifdefs
|
|||
|
in all the source, almost all are deep in the libraries.
|
|||
|
Instead programs read and write files in a defined format,
|
|||
|
either (for low volume applications) as formatted text, or
|
|||
|
(for high volume applications) as binary in a known byte order.
|
|||
|
If the external data were written with the most significant
|
|||
|
byte first, the following code reads a 4-byte integer correctly
|
|||
|
regardless of the architecture of the executing machine (assuming
|
|||
|
an unsigned long holds 4 bytes):
|
|||
|
.P1
|
|||
|
ulong
|
|||
|
getlong(void)
|
|||
|
{
|
|||
|
ulong l;
|
|||
|
|
|||
|
l = (getchar()&0xFF)<<24;
|
|||
|
l |= (getchar()&0xFF)<<16;
|
|||
|
l |= (getchar()&0xFF)<<8;
|
|||
|
l |= (getchar()&0xFF)<<0;
|
|||
|
return l;
|
|||
|
}
|
|||
|
.P2
|
|||
|
Note that this code does not `swap' the bytes; instead it just reads
|
|||
|
them in the correct order.
|
|||
|
Variations of this code will handle any binary format
|
|||
|
and also avoid problems
|
|||
|
involving how structures are padded, how words are aligned,
|
|||
|
and other impediments to portability.
|
|||
|
Be aware, though, that extra care is needed to handle floating point data.
|
|||
|
.PP
|
|||
|
Efficiency hounds will argue that this method is unnecessarily slow and clumsy
|
|||
|
when the executing machine has the same byte order (and padding and alignment)
|
|||
|
as the data.
|
|||
|
The CPU cost of I/O processing
|
|||
|
is rarely the bottleneck for an application, however,
|
|||
|
and the gain in simplicity of porting and maintaining the code greatly outweighs
|
|||
|
the minor speed loss from handling data in this general way.
|
|||
|
This method is how the Plan 9 compilers, the window system, and even the file
|
|||
|
servers transmit data between programs.
|
|||
|
.PP
|
|||
|
To port programs beyond Plan 9, where the system interface is more variable,
|
|||
|
it is probably necessary to use
|
|||
|
.CW pcc
|
|||
|
and hope that the target machine supports ANSI C and POSIX.
|
|||
|
.SH
|
|||
|
I/O
|
|||
|
.PP
|
|||
|
The default C library, defined by the include file
|
|||
|
.CW <libc.h> ,
|
|||
|
contains no buffered I/O package.
|
|||
|
It does have several entry points for printing formatted text:
|
|||
|
.CW print
|
|||
|
outputs text to the standard output,
|
|||
|
.CW fprint
|
|||
|
outputs text to a specified integer file descriptor, and
|
|||
|
.CW sprint
|
|||
|
places text in a character array.
|
|||
|
To access library routines for buffered I/O, a program must
|
|||
|
explicitly include the header file associated with an appropriate library.
|
|||
|
.PP
|
|||
|
The recommended I/O library, used by most Plan 9 utilities, is
|
|||
|
.CW bio
|
|||
|
(buffered I/O), defined by
|
|||
|
.CW <bio.h> .
|
|||
|
There also exists an implementation of ANSI Standard I/O,
|
|||
|
.CW stdio .
|
|||
|
.PP
|
|||
|
.CW Bio
|
|||
|
is small and efficient, particularly for buffer-at-a-time or
|
|||
|
line-at-a-time I/O.
|
|||
|
Even for character-at-a-time I/O, however, it is significantly faster than
|
|||
|
the Standard I/O library,
|
|||
|
.CW stdio .
|
|||
|
Its interface is compact and regular, although it lacks a few conveniences.
|
|||
|
The most noticeable is that one must explicitly define buffers for standard
|
|||
|
input and output;
|
|||
|
.CW bio
|
|||
|
does not predefine them. Here is a program to copy input to output a byte
|
|||
|
at a time using
|
|||
|
.CW bio :
|
|||
|
.P1
|
|||
|
#include <u.h>
|
|||
|
#include <libc.h>
|
|||
|
#include <bio.h>
|
|||
|
|
|||
|
Biobuf bin;
|
|||
|
Biobuf bout;
|
|||
|
|
|||
|
main(void)
|
|||
|
{
|
|||
|
int c;
|
|||
|
|
|||
|
Binit(&bin, 0, OREAD);
|
|||
|
Binit(&bout, 1, OWRITE);
|
|||
|
|
|||
|
while((c=Bgetc(&bin)) != Beof)
|
|||
|
Bputc(&bout, c);
|
|||
|
exits(0);
|
|||
|
}
|
|||
|
.P2
|
|||
|
For peak performance, we could replace
|
|||
|
.CW Bgetc
|
|||
|
and
|
|||
|
.CW Bputc
|
|||
|
by their equivalent in-line macros
|
|||
|
.CW BGETC
|
|||
|
and
|
|||
|
.CW BPUTC
|
|||
|
but
|
|||
|
the performance gain would be modest.
|
|||
|
For more information on
|
|||
|
.CW bio ,
|
|||
|
see the Programmer's Manual.
|
|||
|
.PP
|
|||
|
Perhaps the most dramatic difference in the I/O interface of Plan 9 from other
|
|||
|
systems' is that text is not ASCII.
|
|||
|
The format for
|
|||
|
text in Plan 9 is a byte-stream encoding of 16-bit characters.
|
|||
|
The character set is based on the Unicode Standard and is backward compatible with
|
|||
|
ASCII:
|
|||
|
characters with value 0 through 127 are the same in both sets.
|
|||
|
The 16-bit characters, called
|
|||
|
.I runes
|
|||
|
in Plan 9, are encoded using a representation called
|
|||
|
UTF,
|
|||
|
an encoding that is becoming accepted as a standard.
|
|||
|
(ISO calls it UTF-8;
|
|||
|
throughout Plan 9 it's just called
|
|||
|
UTF.)
|
|||
|
UTF
|
|||
|
defines multibyte sequences to
|
|||
|
represent character values from 0 to 65535.
|
|||
|
In
|
|||
|
UTF,
|
|||
|
character values up to 127 decimal, 7F hexadecimal, represent themselves,
|
|||
|
so straight
|
|||
|
ASCII
|
|||
|
files are also valid
|
|||
|
UTF.
|
|||
|
Also,
|
|||
|
UTF
|
|||
|
guarantees that bytes with values 0 to 127 (NUL to DEL, inclusive)
|
|||
|
will appear only when they represent themselves, so programs that read bytes
|
|||
|
looking for plain ASCII characters will continue to work.
|
|||
|
Any program that expects a one-to-one correspondence between bytes and
|
|||
|
characters will, however, need to be modified.
|
|||
|
An example is parsing file names.
|
|||
|
File names, like all text, are in
|
|||
|
UTF,
|
|||
|
so it is incorrect to search for a character in a string by
|
|||
|
.CW strchr(filename,
|
|||
|
.CW c)
|
|||
|
because the character might have a multi-byte encoding.
|
|||
|
The correct method is to call
|
|||
|
.CW utfrune(filename,
|
|||
|
.CW c) ,
|
|||
|
defined in
|
|||
|
.I rune (2),
|
|||
|
which interprets the file name as a sequence of encoded characters
|
|||
|
rather than bytes.
|
|||
|
In fact, even when you know the character is a single byte
|
|||
|
that can represent only itself,
|
|||
|
it is safer to use
|
|||
|
.CW utfrune
|
|||
|
because that assumes nothing about the character set
|
|||
|
and its representation.
|
|||
|
.PP
|
|||
|
The library defines several symbols relevant to the representation of characters.
|
|||
|
Any byte with unsigned value less than
|
|||
|
.CW Runesync
|
|||
|
will not appear in any multi-byte encoding of a character.
|
|||
|
.CW Utfrune
|
|||
|
compares the character being searched against
|
|||
|
.CW Runesync
|
|||
|
to see if it is sufficient to call
|
|||
|
.CW strchr
|
|||
|
or if the byte stream must be interpreted.
|
|||
|
Any byte with unsigned value less than
|
|||
|
.CW Runeself
|
|||
|
is represented by a single byte with the same value.
|
|||
|
Finally, when errors are encountered converting
|
|||
|
to runes from a byte stream, the library returns the rune value
|
|||
|
.CW Runeerror
|
|||
|
and advances a single byte. This permits programs to find runes
|
|||
|
embedded in binary data.
|
|||
|
.PP
|
|||
|
.CW Bio
|
|||
|
includes routines
|
|||
|
.CW Bgetrune
|
|||
|
and
|
|||
|
.CW Bputrune
|
|||
|
to transform the external byte stream
|
|||
|
UTF
|
|||
|
format to and from
|
|||
|
internal 16-bit runes.
|
|||
|
Also, the
|
|||
|
.CW %s
|
|||
|
format to
|
|||
|
.CW print
|
|||
|
accepts
|
|||
|
UTF;
|
|||
|
.CW %c
|
|||
|
prints a character after narrowing it to 8 bits.
|
|||
|
The
|
|||
|
.CW %S
|
|||
|
format prints a null-terminated sequence of runes;
|
|||
|
.CW %C
|
|||
|
prints a character after narrowing it to 16 bits.
|
|||
|
For more information, see the Programmer's Manual, in particular
|
|||
|
.I utf (6)
|
|||
|
and
|
|||
|
.I rune (2),
|
|||
|
and the paper,
|
|||
|
``Hello world, or
|
|||
|
Καλημέρα κόσμε, or\
|
|||
|
\f(Jpこんにちは 世界\f1'',
|
|||
|
by Rob Pike and
|
|||
|
Ken Thompson;
|
|||
|
there is not room for the full story here.
|
|||
|
.PP
|
|||
|
These issues affect the compiler in several ways.
|
|||
|
First, the C source is in
|
|||
|
UTF.
|
|||
|
ANSI says C variables are formed from
|
|||
|
ASCII
|
|||
|
alphanumerics, but comments and literal strings may contain any characters
|
|||
|
encoded in the native encoding, here
|
|||
|
UTF.
|
|||
|
The declaration
|
|||
|
.P1
|
|||
|
char *cp = "abcÿ";
|
|||
|
.P2
|
|||
|
initializes the variable
|
|||
|
.CW cp
|
|||
|
to point to an array of bytes holding the
|
|||
|
UTF
|
|||
|
representation of the characters
|
|||
|
.CW abcÿ.
|
|||
|
The type
|
|||
|
.CW Rune
|
|||
|
is defined in
|
|||
|
.CW <u.h>
|
|||
|
to be
|
|||
|
.CW ushort ,
|
|||
|
which is also the `wide character' type in the compiler.
|
|||
|
Therefore the declaration
|
|||
|
.P1
|
|||
|
Rune *rp = L"abcÿ";
|
|||
|
.P2
|
|||
|
initializes the variable
|
|||
|
.CW rp
|
|||
|
to point to an array of unsigned short integers holding the 16-bit
|
|||
|
values of the characters
|
|||
|
.CW abcÿ .
|
|||
|
Note that in both these declarations the characters in the source
|
|||
|
that represent
|
|||
|
.CW "abcÿ"
|
|||
|
are the same; what changes is how those characters are represented
|
|||
|
in memory in the program.
|
|||
|
The following two lines:
|
|||
|
.P1
|
|||
|
print("%s\en", "abcÿ");
|
|||
|
print("%S\en", L"abcÿ");
|
|||
|
.P2
|
|||
|
produce the same
|
|||
|
UTF
|
|||
|
string on their output, the first by copying the bytes, the second
|
|||
|
by converting from runes to bytes.
|
|||
|
.PP
|
|||
|
In C, character constants are integers but narrowed through the
|
|||
|
.CW char
|
|||
|
type.
|
|||
|
The Unicode character
|
|||
|
.CW ÿ
|
|||
|
has value 255, so if the
|
|||
|
.CW char
|
|||
|
type is signed,
|
|||
|
the constant
|
|||
|
.CW 'ÿ'
|
|||
|
has value \-1 (which is equal to EOF).
|
|||
|
On the other hand,
|
|||
|
.CW L'ÿ'
|
|||
|
narrows through the wide character type,
|
|||
|
.CW ushort ,
|
|||
|
and therefore has value 255.
|
|||
|
.PP
|
|||
|
Finally, although it's not ANSI C, the Plan 9 C compilers
|
|||
|
assume any character with value above
|
|||
|
.CW Runeself
|
|||
|
is an alphanumeric,
|
|||
|
so α is a legal, if non-portable, variable name.
|
|||
|
.SH
|
|||
|
Arguments
|
|||
|
.PP
|
|||
|
Some macros are defined
|
|||
|
in
|
|||
|
.CW <libc.h>
|
|||
|
for parsing the arguments to
|
|||
|
.CW main() .
|
|||
|
They are described in
|
|||
|
.I ARG (2)
|
|||
|
but are fairly self-explanatory.
|
|||
|
There are four macros:
|
|||
|
.CW ARGBEGIN
|
|||
|
and
|
|||
|
.CW ARGEND
|
|||
|
are used to bracket a hidden
|
|||
|
.CW switch
|
|||
|
statement within which
|
|||
|
.CW ARGC
|
|||
|
returns the current option character (rune) being processed and
|
|||
|
.CW ARGF
|
|||
|
returns the argument to the option, as in the loader option
|
|||
|
.CW -o
|
|||
|
.CW file .
|
|||
|
Here, for example, is the code at the beginning of
|
|||
|
.CW main()
|
|||
|
in
|
|||
|
.CW ramfs.c
|
|||
|
(see
|
|||
|
.I ramfs (1))
|
|||
|
that cracks its arguments:
|
|||
|
.P1
|
|||
|
void
|
|||
|
main(int argc, char *argv[])
|
|||
|
{
|
|||
|
char *defmnt;
|
|||
|
int p[2];
|
|||
|
int mfd[2];
|
|||
|
int stdio = 0;
|
|||
|
|
|||
|
defmnt = "/tmp";
|
|||
|
ARGBEGIN{
|
|||
|
case 'i':
|
|||
|
defmnt = 0;
|
|||
|
stdio = 1;
|
|||
|
mfd[0] = 0;
|
|||
|
mfd[1] = 1;
|
|||
|
break;
|
|||
|
case 's':
|
|||
|
defmnt = 0;
|
|||
|
break;
|
|||
|
case 'm':
|
|||
|
defmnt = ARGF();
|
|||
|
break;
|
|||
|
default:
|
|||
|
usage();
|
|||
|
}ARGEND
|
|||
|
.P2
|
|||
|
.SH
|
|||
|
Extensions
|
|||
|
.PP
|
|||
|
The compiler has several extensions to ANSI C, all of which are used
|
|||
|
extensively in the system source.
|
|||
|
First,
|
|||
|
.I structure
|
|||
|
.I displays
|
|||
|
permit
|
|||
|
.CW struct
|
|||
|
expressions to be formed dynamically.
|
|||
|
Given these declarations:
|
|||
|
.P1
|
|||
|
typedef struct Point Point;
|
|||
|
typedef struct Rectangle Rectangle;
|
|||
|
|
|||
|
struct Point
|
|||
|
{
|
|||
|
int x, y;
|
|||
|
};
|
|||
|
|
|||
|
struct Rectangle
|
|||
|
{
|
|||
|
Point min, max;
|
|||
|
};
|
|||
|
|
|||
|
Point p, q, add(Point, Point);
|
|||
|
Rectangle r;
|
|||
|
int x, y;
|
|||
|
.P2
|
|||
|
this assignment may appear anywhere an assignment is legal:
|
|||
|
.P1
|
|||
|
r = (Rectangle){add(p, q), (Point){x, y+3}};
|
|||
|
.P2
|
|||
|
The syntax is the same as for initializing a structure but with
|
|||
|
a leading cast.
|
|||
|
.PP
|
|||
|
If an
|
|||
|
.I anonymous
|
|||
|
.I structure
|
|||
|
or
|
|||
|
.I union
|
|||
|
is declared within another structure or union, the members of the internal
|
|||
|
structure or union are addressable without prefix in the outer structure.
|
|||
|
This feature eliminates the clumsy naming of nested structures and,
|
|||
|
particularly, unions.
|
|||
|
For example, after these declarations,
|
|||
|
.P1
|
|||
|
struct Lock
|
|||
|
{
|
|||
|
int locked;
|
|||
|
};
|
|||
|
|
|||
|
struct Node
|
|||
|
{
|
|||
|
int type;
|
|||
|
union{
|
|||
|
double dval;
|
|||
|
double fval;
|
|||
|
long lval;
|
|||
|
}; /* anonymous union */
|
|||
|
struct Lock; /* anonymous structure */
|
|||
|
} *node;
|
|||
|
|
|||
|
void lock(struct Lock*);
|
|||
|
.P2
|
|||
|
one may refer to
|
|||
|
.CW node->type ,
|
|||
|
.CW node->dval ,
|
|||
|
.CW node->fval ,
|
|||
|
.CW node->lval ,
|
|||
|
and
|
|||
|
.CW node->locked .
|
|||
|
Moreover, the address of a
|
|||
|
.CW struct
|
|||
|
.CW Node
|
|||
|
may be used without a cast anywhere that the address of a
|
|||
|
.CW struct
|
|||
|
.CW Lock
|
|||
|
is used, such as in argument lists.
|
|||
|
The compiler automatically promotes the type and adjusts the address.
|
|||
|
Thus one may invoke
|
|||
|
.CW lock(node) .
|
|||
|
.PP
|
|||
|
Anonymous structures and unions may be accessed by type name
|
|||
|
if (and only if) they are declared using a
|
|||
|
.CW typedef
|
|||
|
name.
|
|||
|
For example, using the above declaration for
|
|||
|
.CW Point ,
|
|||
|
one may declare
|
|||
|
.P1
|
|||
|
struct
|
|||
|
{
|
|||
|
int type;
|
|||
|
Point;
|
|||
|
} p;
|
|||
|
.P2
|
|||
|
and refer to
|
|||
|
.CW p.Point .
|
|||
|
.PP
|
|||
|
In the initialization of arrays, a number in square brackets before an
|
|||
|
element sets the index for the initialization. For example, to initialize
|
|||
|
some elements in
|
|||
|
a table of function pointers indexed by
|
|||
|
ASCII
|
|||
|
character,
|
|||
|
.P1
|
|||
|
void percent(void), slash(void);
|
|||
|
|
|||
|
void (*func[128])(void) =
|
|||
|
{
|
|||
|
['%'] percent,
|
|||
|
['/'] slash,
|
|||
|
};
|
|||
|
.P2
|
|||
|
.LP
|
|||
|
A similar syntax allows one to initialize structure elements:
|
|||
|
.P1
|
|||
|
Point p =
|
|||
|
{
|
|||
|
.y 100,
|
|||
|
.x 200
|
|||
|
};
|
|||
|
.P2
|
|||
|
These initialization syntaxes were later added to ANSI C, with the addition of an
|
|||
|
equals sign between the index or tag and the value.
|
|||
|
The Plan 9 compiler accepts either form.
|
|||
|
.PP
|
|||
|
Finally, the declaration
|
|||
|
.P1
|
|||
|
extern register reg;
|
|||
|
.P2
|
|||
|
.I this "" (
|
|||
|
appearance of the register keyword is not ignored)
|
|||
|
allocates a global register to hold the variable
|
|||
|
.CW reg .
|
|||
|
External registers must be used carefully: they need to be declared in
|
|||
|
.I all
|
|||
|
source files and libraries in the program to guarantee the register
|
|||
|
is not allocated temporarily for other purposes.
|
|||
|
Especially on machines with few registers, such as the i386,
|
|||
|
it is easy to link accidentally with code that has already usurped
|
|||
|
the global registers and there is no diagnostic when this happens.
|
|||
|
Used wisely, though, external registers are powerful.
|
|||
|
The Plan 9 operating system uses them to access per-process and
|
|||
|
per-machine data structures on a multiprocessor. The storage class they provide
|
|||
|
is hard to create in other ways.
|
|||
|
.SH
|
|||
|
The compile-time environment
|
|||
|
.PP
|
|||
|
The code generated by the compilers is `optimized' by default:
|
|||
|
variables are placed in registers and peephole optimizations are
|
|||
|
performed.
|
|||
|
The compiler flag
|
|||
|
.CW -N
|
|||
|
disables these optimizations.
|
|||
|
Registerization is done locally rather than throughout a function:
|
|||
|
whether a variable occupies a register or
|
|||
|
the memory location identified in the symbol
|
|||
|
table depends on the activity of the variable and may change
|
|||
|
throughout the life of the variable.
|
|||
|
The
|
|||
|
.CW -N
|
|||
|
flag is rarely needed;
|
|||
|
its main use is to simplify debugging.
|
|||
|
There is no information in the symbol table to identify the
|
|||
|
registerization of a variable, so
|
|||
|
.CW -N
|
|||
|
guarantees the variable is always where the symbol table says it is.
|
|||
|
.PP
|
|||
|
Another flag,
|
|||
|
.CW -w ,
|
|||
|
turns
|
|||
|
.I on
|
|||
|
warnings about portability and problems detected in flow analysis.
|
|||
|
Most code in Plan 9 is compiled with warnings enabled;
|
|||
|
these warnings plus the type checking offered by function prototypes
|
|||
|
provide most of the support of the Unix tool
|
|||
|
.CW lint
|
|||
|
more accurately and with less chatter.
|
|||
|
Two of the warnings,
|
|||
|
`used and not set' and `set and not used', are almost always accurate but
|
|||
|
may be triggered spuriously by code with invisible control flow,
|
|||
|
such as in routines that call
|
|||
|
.CW longjmp .
|
|||
|
The compiler statements
|
|||
|
.P1
|
|||
|
SET(v1);
|
|||
|
USED(v2);
|
|||
|
.P2
|
|||
|
decorate the flow graph to silence the compiler.
|
|||
|
Either statement accepts a comma-separated list of variables.
|
|||
|
Use them carefully: they may silence real errors.
|
|||
|
For the common case of unused parameters to a function,
|
|||
|
leaving the name off the declaration silences the warnings.
|
|||
|
That is, listing the type of a parameter but giving it no
|
|||
|
associated variable name does the trick.
|
|||
|
.SH
|
|||
|
Debugging
|
|||
|
.PP
|
|||
|
There are two debuggers available on Plan 9.
|
|||
|
The first, and older, is
|
|||
|
.CW db ,
|
|||
|
a revision of Unix
|
|||
|
.CW adb .
|
|||
|
The other,
|
|||
|
.CW acid ,
|
|||
|
is a source-level debugger whose commands are statements in
|
|||
|
a true programming language.
|
|||
|
.CW Acid
|
|||
|
is the preferred debugger, but since it
|
|||
|
borrows some elements of
|
|||
|
.CW db ,
|
|||
|
notably the formats for displaying values, it is worth knowing a little bit about
|
|||
|
.CW db .
|
|||
|
.PP
|
|||
|
Both debuggers support multiple architectures in a single program; that is,
|
|||
|
the programs are
|
|||
|
.CW db
|
|||
|
and
|
|||
|
.CW acid ,
|
|||
|
not for example
|
|||
|
.CW vdb
|
|||
|
and
|
|||
|
.CW vacid .
|
|||
|
They also support cross-architecture debugging comfortably:
|
|||
|
one may debug a 68020 binary on a MIPS.
|
|||
|
.PP
|
|||
|
Imagine a program has crashed mysteriously:
|
|||
|
.P1
|
|||
|
% X11/X
|
|||
|
Fatal server bug!
|
|||
|
failed to create default stipple
|
|||
|
X 106: suicide: sys: trap: fault read addr=0x0 pc=0x00105fb8
|
|||
|
%
|
|||
|
.P2
|
|||
|
When a process dies on Plan 9 it hangs in the `broken' state
|
|||
|
for debugging.
|
|||
|
Attach a debugger to the process by naming its process id:
|
|||
|
.P1
|
|||
|
% acid 106
|
|||
|
/proc/106/text:mips plan 9 executable
|
|||
|
|
|||
|
/sys/lib/acid/port
|
|||
|
/sys/lib/acid/mips
|
|||
|
acid:
|
|||
|
.P2
|
|||
|
The
|
|||
|
.CW acid
|
|||
|
function
|
|||
|
.CW stk()
|
|||
|
reports the stack traceback:
|
|||
|
.P1
|
|||
|
acid: stk()
|
|||
|
At pc:0x105fb8:abort+0x24 /sys/src/ape/lib/ap/stdio/abort.c:6
|
|||
|
abort() /sys/src/ape/lib/ap/stdio/abort.c:4
|
|||
|
called from FatalError+#4e
|
|||
|
/sys/src/X/mit/server/dix/misc.c:421
|
|||
|
FatalError(s9=#e02, s8=#4901d200, s7=#2, s6=#72701, s5=#1,
|
|||
|
s4=#7270d, s3=#6, s2=#12, s1=#ff37f1c, s0=#6, f=#7270f)
|
|||
|
/sys/src/X/mit/server/dix/misc.c:416
|
|||
|
called from gnotscreeninit+#4ce
|
|||
|
/sys/src/X/mit/server/ddx/gnot/gnot.c:792
|
|||
|
gnotscreeninit(snum=#0, sc=#80db0)
|
|||
|
/sys/src/X/mit/server/ddx/gnot/gnot.c:766
|
|||
|
called from AddScreen+#16e
|
|||
|
/n/bootes/sys/src/X/mit/server/dix/main.c:610
|
|||
|
AddScreen(pfnInit=0x0000129c,argc=0x00000001,argv=0x7fffffe4)
|
|||
|
/sys/src/X/mit/server/dix/main.c:530
|
|||
|
called from InitOutput+0x80
|
|||
|
/sys/src/X/mit/server/ddx/brazil/brddx.c:522
|
|||
|
InitOutput(argc=0x00000001,argv=0x7fffffe4)
|
|||
|
/sys/src/X/mit/server/ddx/brazil/brddx.c:511
|
|||
|
called from main+0x294
|
|||
|
/sys/src/X/mit/server/dix/main.c:225
|
|||
|
main(argc=0x00000001,argv=0x7fffffe4)
|
|||
|
/sys/src/X/mit/server/dix/main.c:136
|
|||
|
called from _main+0x24
|
|||
|
/sys/src/ape/lib/ap/mips/main9.s:8
|
|||
|
.P2
|
|||
|
The function
|
|||
|
.CW lstk()
|
|||
|
is similar but
|
|||
|
also reports the values of local variables.
|
|||
|
Note that the traceback includes full file names; this is a boon to debugging,
|
|||
|
although it makes the output much noisier.
|
|||
|
.PP
|
|||
|
To use
|
|||
|
.CW acid
|
|||
|
well you will need to learn its input language; see the
|
|||
|
``Acid Manual'',
|
|||
|
by Phil Winterbottom,
|
|||
|
for details. For simple debugging, however, the information in the manual page is
|
|||
|
sufficient. In particular, it describes the most useful functions
|
|||
|
for examining a process.
|
|||
|
.PP
|
|||
|
The compiler does not place
|
|||
|
information describing the types of variables in the executable,
|
|||
|
but a compile-time flag provides crude support for symbolic debugging.
|
|||
|
The
|
|||
|
.CW -a
|
|||
|
flag to the compiler suppresses code generation
|
|||
|
and instead emits source text in the
|
|||
|
.CW acid
|
|||
|
language to format and display data structure types defined in the program.
|
|||
|
The easiest way to use this feature is to put a rule in the
|
|||
|
.CW mkfile :
|
|||
|
.P1
|
|||
|
syms: main.$O
|
|||
|
$CC -a main.c > syms
|
|||
|
.P2
|
|||
|
Then from within
|
|||
|
.CW acid ,
|
|||
|
.P1
|
|||
|
acid: include("sourcedirectory/syms")
|
|||
|
.P2
|
|||
|
to read in the relevant definitions.
|
|||
|
(For multi-file source, you need to be a little fancier;
|
|||
|
see
|
|||
|
.I 2c (1)).
|
|||
|
This text includes, for each defined compound
|
|||
|
type, a function with that name that may be called with the address of a structure
|
|||
|
of that type to display its contents.
|
|||
|
For example, if
|
|||
|
.CW rect
|
|||
|
is a global variable of type
|
|||
|
.CW Rectangle ,
|
|||
|
one may execute
|
|||
|
.P1
|
|||
|
Rectangle(*rect)
|
|||
|
.P2
|
|||
|
to display it.
|
|||
|
The
|
|||
|
.CW *
|
|||
|
(indirection) operator is necessary because
|
|||
|
of the way
|
|||
|
.CW acid
|
|||
|
works: each global symbol in the program is defined as a variable by
|
|||
|
.CW acid ,
|
|||
|
with value equal to the
|
|||
|
.I address
|
|||
|
of the symbol.
|
|||
|
.PP
|
|||
|
Another common technique is to write by hand special
|
|||
|
.CW acid
|
|||
|
code to define functions to aid debugging, initialize the debugger, and so on.
|
|||
|
Conventionally, this is placed in a file called
|
|||
|
.CW acid
|
|||
|
in the source directory; it has a line
|
|||
|
.P1
|
|||
|
include("sourcedirectory/syms");
|
|||
|
.P2
|
|||
|
to load the compiler-produced symbols. One may edit the compiler output directly but
|
|||
|
it is wiser to keep the hand-generated
|
|||
|
.CW acid
|
|||
|
separate from the machine-generated.
|
|||
|
.PP
|
|||
|
To make things simple, the default rules in the system
|
|||
|
.CW mkfiles
|
|||
|
include entries to make
|
|||
|
.CW foo.acid
|
|||
|
from
|
|||
|
.CW foo.c ,
|
|||
|
so one may use
|
|||
|
.CW mk
|
|||
|
to automate the production of
|
|||
|
.CW acid
|
|||
|
definitions for a given C source file.
|
|||
|
.PP
|
|||
|
There is much more to say here. See
|
|||
|
.CW acid
|
|||
|
manual page, the reference manual, or the paper
|
|||
|
``Acid: A Debugger Built From A Language'',
|
|||
|
also by Phil Winterbottom.
|