This section will first take you on a tour of the source tree. After that, it will proceed to discuss several of the aspects surrounding the organization of our makefiles and where object files show up.
All sources are found under
usr/src. This includes both the sources used to
build the ON consolidation and sources for tools and other peripheral utilities
needed to build but not shipped as part of illumos. Because it includes only ON
sources, it does not contain Java, the windowing system, or packaging and
installation tools. Because of contractual obligations, it may not include all
code from third-party hardware vendors. The
usr/src directory has several
subdirectories which are described here.
This directory contains sources for the executable programs and scripts that are part of ON. It includes all the basic commands, daemons, startup scripts, and related data. Most subdirectories are named for the command or commands they provide; however, there are some exceptions listed here.
Miscellaneous key system files, such as crontabs and data installed in
Basic cryptographic utilities, such as elfsign and digest.
Network commands and daemons, including the Berkeley r-commands, PPP, telnet, the inetd super-server, and other network-related utilities.
Utilities for checking, mounting, unmounting, and analyzing filesystems.
Contains the sources for the modular debugger,
mdb(1), the kernel debugger,
kmdb(1), and the majority of the modules.
IP port definitions and name service switch configuration files installed into /etc.
Utilities for manipulating and observing processes; these are based on proc(4) and libproc interfaces.
Software Generation System. This directory contains binary utilities, such as
mcs(1), and development tools such as
yacc(1), and m4(1). Note that this directory also includes several libraries
and headers used by these tools.
Files which are common among
uts. These typically
include headers and sources to basic libraries used by both the kernel and user
Userland header files (kernel headers are in
uts/). Note that only libc headers
should be stored here; other libraries should have their headers in their own
Libraries. Most subdirectories are named for the library whose sources they contain or are otherwise self-explanatory.
Contains the manifests and package lists for building ips packages from the ON sources.
Sample files showing format and copyright notices.
Platform-specific modules. Currently this contains only OBP and most of the boot code.
Standalone environment code. This is used for booting; for example, code for reading from UFS and the network is here.
Development tools and sources. See README.tools for more information about each tool; the file should be updated as tools are added or removed.
Commands and daemons installed into
/usr/ucb (for SunOS 4.x compatibility).
Header files installed into
/usr/ucb (for SunOS 4.x compatibility).
Libraries installed into
/usr/ucb (for SunOS 4.x compatibility).
Kernel sources are here (UTS == UNIX Time Sharing). There are numerous subdirectories of uts which are of interest:
All platform-independent kernel sources. Nearly all of the illumos kernel is here; only a few small parts are architecture-dependent.
System configuration parameters.
Code to support process contracts. See
more information on process contracts.
CheckPoint-and-Resume support. This implements suspend and resume functionality.
Kernel cryptographic framework. See
cryptoadm(1M) for more information.
Kernel code for handling Compact C Type Format data.
Implements the old Data Encryption Standard. This is used by KCF.
Dispatcher, thread handling, and scheduling classes.
dtrace(7D) kernel support.
Code for handling userland binary executable types (a.out, ELF, etc).
IP networking subsystem, including IPv6.
I/O subsystem. Most of the code in this directory is device drivers (and pseudo-device drivers).
IP policy framework; includes QoS and other traffic management.
Kernel modular debugger driver. See
Kernel runtime linker/loader. This is responsible for handling loadable modules and symbol resolution; it is analogous to ld.so.1, and shares code with it.
Kernel TLI (Transport Layer Interface).
Header files; most are shipped in
Header files; most are shipped in
Network File System headers shipped in
Core operating system implementation. This includes such varied aspects as privileges, zones, timers, the DDI/DKI interfaces, and high-level locking mechanisms.
PCMCIA I/O subsystem and drivers.
Remote Procedure Call subsystem used by services such as NFS and NIS.
Generated RPC header files shipped in
Header files shipped in
/usr/include/sys. These same headers are used to build
the kernel as well (if the _KERNEL preprocessor symbol is defined).
System call implementations. Most syscalls are implemented in files matching
their names. Note that some system calls are implemented in
os/ or other
Virtual memory subsystem.
Architecture-dependent files for x86 machines. The architecture-dependent
sun4u) all have a set of subdirectories
similar to common/ above.
ISA-dependent, architecture-independent files for x86 machines. Note that all architecture-independent source files are built into objects in this hierarchy.
Code specific to the SpitFire memory management unit (UltraSPARC).
ISA-dependent, architecture-independent files for SPARC machines. Note that all architecture-independent source files are built into objects in this hierarchy.
Sources common to all Sun implementations. Currently this contains a small
number of device drivers and some headers shipped in
Sources common to all sun4* machine architectures.
Architecture-dependent sources for the sun4u architecture. Each system implementation has a subdirectory here.
You may note that we started inside of
usr/src. There is just one other
component which is outside of
usr/src, the directory
exception_lists contain several files that relate to exceptions to the
standard checks that we make. This covers things like packaging and cstyle.
Several of these exceptions exist for historical reasons. If you find yourself
making additions to these files, you should think twice about what you're doing.
There are two basic strategies that can be used in the creation of object files and finished binaries (executables and libraries):
(a) place objects in a dedicated directory hierarchy parallel to the sources
(b) place objects in the same directories as the sources they are built from
ON actually uses each of these approaches in different parts of the tree. Strategy (a) must be used for all kernel code and many libraries, is preferred for new code, and will be described in detail here. There are several legacy variations on strategy (a) as well as instances in which strategy (b) is still used; the majority of these are in the cmd hierarchy. The entire uts hierarchy has been converted to strategy (a) as described below, and you will see this same approach used throughout much of the rest of the tree.
First, each platform-independent module has zero or one build directory per
architecture. An architecture in this case can be a machine (
or a processor architecture (
sparc). The path to this location is
usr/src/uts/<platform>/<module>. The module name in this case is what's
/kernel/drv or a similar location, and in the case of device drivers
or STREAMS modules should always match the name of the man page describing that
driver or module.
The only files normally present in this directory for a clean tree are
makefiles. After a build, these directories contain one or more of
debug64 directories. These directories contain the
object files and finally linked modules that are later installed into the kernel
directory and other locations in the prototype.
'Implementation architecture'-independent modules are produced in individual
directories (one per module) under the 'instruction-set architecture' directory
sparc). Similarly, 'implementation architecture'-dependent modules are
produced in individual directories under the 'implementation architecture'
Platform-dependent modules (including 'unix') may be built more than once in different locations. For example, they'll be built under each of the different implementations of a platform. This is more common on sparc than x86.
The sources are not contained in these build directories.
Most libraries and some commands and daemons are built using makefiles very similar to those used to build the kernel. Accordingly, intermediate objects, shared objects, libraries, and executables are built by small makefile fragments and placed in dedicated ISA-specific subdirectories.
Other commands' build systems place objects in the same directories as the sources. See Component Anatomy, Creation, and Modification for more information on how commands are built.
Most of the cmd tree is directly based on the original System V Release 4 source, which uses strategy (b) described above. Since most commands and daemons do not need to provide both 32- and 64-bit versions, or do anything special when building for different architectures, this strategy is adequate and appropriate for most commands and has been applied even to new subdirectories. In situations in which architecture-dependent build options are needed or multiple ISA versions of a program must be delivered, this strategy is unworkable and the more general approach of multiple per-ISA object directories must be used instead. This latter approach is similar to the approach used for kernel modules.
The lib hierarchy is somewhat simpler; nearly all subdirectories must use per-ISA object file locations and makefiles.
A few directories do not appear to follow any rule or pattern, such as
cmd/agents. These are primarily historical artifacts of
Sun's internal project organization.
This discussion is intended to provide a step-by-step explanation of what
targets exist and how they are built by the makefiles. I ignore the
platform-specific module architecture because it is unlikely to be of
significant interest except to hardware support engineers. The three main
subtrees of interest are the kernel (
uts), commands and daemons (
lib). The next three subsections cover these three subtrees in
turn. There are also a handful of makefiles which apply to all builds:
This is the top-level makefile. It drives builds for various targets in each
subdirectory. It is aware of the specific targets that need to be built in each
subdirectory in order to perform a complete build, and itself knows how to
create a skeleton proto area for later use by
All linting from the top level is driven by this makefile. It contains long lists of directories known to be lint-clean and contains simple recursive rules for rebuilding each subdirectory's lint target. The actual linting is driven by the lower-level makefiles.
These two makefiles contain generic definitions, such as build and installation
tools locations, template macros for compilers, linkers, and other tools to be
used by other makefiles in defining rules, and global definitions such as the
ISA and machine names that apply to this build.
definitions specific to 64-bit builds that override the generic definitions.
Common targets for building message catalogues are defined here. Message catalogues provide translations of messages for g11n purposes.
This makefile defines the installation locations for platform-specific modules.
These are analogous to the other kernel module install locations
Installation target definitions for platform-specific modules are defined here. This instructs the build system how to install files into the directories defined by Makefile.psm.
This is a set of definitions for the owner, group, and permissions of each directory that will be created by the installation process. It also contains information about special symbolic links to be installed for some 64-bit library versions.
The driving makefile for any module is located in the leaf directory (build
directory) where the module and its component objects are built. After a
clobber operation, the makefile should be the only file remaining in that
directory. There are two other types of makefiles in the tree: suffixed and
non-suffixed. Common definitions and rules needed by all leaf makefiles are
contained in the suffixed makefiles; these are included by leaf makefiles.
Non-suffixed makefiles generally invoke multiple lower-level makefiles with the
same target so that many modules can be built with a single make invocation.
These makefiles generally are cognizant of the components made in subdirectories and invoke makefiles in those sub- directories to perform the actual build. Some targets (or pseudo-targets) may be directly built at this level (such as the cscope databases).
Contains common definitions for all possible architectures.
Contains common targets for all possible architectures.
These makefiles are divided into two sections. The first section defines the object lists which comprise each module. The second section defines the appropriate header search paths and other machine-specific global build parameters.
The files provide build rules (targets) which allow make to function in a multiple directory environment. Each source tree below the directory containing the makefile has a build rule in the file.
These makefiles contain the definitions specific (defaults) to the obvious 'implementation architecture'. These rules can be overridden in specific leaf node makefiles if necessary.
Main driving makefile for building unix.
uts/sun4u/MODULE/Makefile(for MODULE in
Main driving makefile for building MODULE.
Main driving makefile for building genunix.
Issuing the command
dmake in the
uts directory will cause all supported,
modularized kernels and modules to be built.
Issuing the command
dmake in a
uts/ARCHITECTURE directory (i.e.:
uts/sparc) will cause all supported, "implementation architecture"-independent
ARCHITECTURE to be built.
Issuing the command
dmake in a
uts/MACHINE directory (i.e.:
will cause that kernel and all supported, "implementation architecture"-
dependent modules for MACHINE to be built.
The makefiles are verbosely commented. It is desired that they should stay this way.
Most command and daemon subdirectories follow one of two general layout rules,
depending on where object files will be located (see Parts of a
Command). For ISA-dependent
programs, the layout is similar to that used by the kernel. Programs which do
not need ISA-dependent build behavior use a simplified makefile layout. In the
description here, we use the example of a generic command called
sources are located in
usr/src/cmd/foocmd. The makefiles relevant to building
Top-level driving makefile for all commands/daemons. This is a simple recursive makefile which is aware of which subdirectories should be built and will cause the given target to be rebuilt in each of them.
This makefile defines the installation directories and rules for installing executables into them.
Additional definitions specific to 64-bit builds are provided here.
This specialty makefile is used only by
dminfo. It provides some
generic boilerplate rules.
Basic target definitions for clobber, lint, and installation.
Driving makefile for foocmd. Normally defines
PROG but otherwise contains only
boilerplate definitions and targets. This is almost always copied from another
similar makefile. If foocmd does not require ISA-dependent build behavior, rules
will normally be specified directly, including those for the install target. If
foocmd does require ISA-dependent build behavior, this makefile will instead
define SUBDIRS to include the ISA-specific versions that must be built, and
define targets recursively. This will usually leave the install target
definition for each ISA makefile and cause a link to
$(ISAEXEC) to be created.
See section Handling Multiple
Architectures for more
information on platform dependencies and
SRCS, and includes
Makefile.cmd. May also contain
additional flags for compilation or linking. This makefile normally defines
lint; this portion is usually
generic and would be copied from another similar makefile.
ISA-specific makefiles, which may define additional ISA-specific flags or targets, and will generally include its own install target to install the ISA-specific program(s) it builds.
Most library subdirectories follow the same general layout, which is similar to the command layout. Unlike commands, most libraries are built for both 32- and 64-bit architecture variants, so ISA-specific directories will almost always be present. Therefore, the overall build structure for each library is similar to that of the kernel. We'll give an overview of the general makefiles for libraries as well as briefly explain the makefiles for a specific library. Later on in Component Anatomy, Creation, and Modification we'll go into more detail and show examples of building your own library and other components involved.
This contains the bulk of the macros for building shared objects.
This contains macros for building 64-bit objects, and should be included in Makefiles for 64-bit native ISAs.
This contains macro overrides for libraries that install into
/lib (rather than
This contains rules for building shared objects.
This is the library's top-level Makefile. It should contain rules for building any ISA-independent targets, such as installing header files and building message catalogs, but should defer all other targets to ISA-specific Makefiles.
This is the library's common Makefile. It should contain rules and macros which are common to all ISAs. This Makefile should never be built explicitly, but instead should be included (using the make include mechanism) by all of your ISA-specific Makefiles.
These are the library's ISA-specific Makefiles, one per ISA (usually
i386, and sometimes
amd64). These Makefiles should include your
common Makefile and then provide any needed ISA-specific rules and definitions,
perhaps overriding those provided in your common Makefile.
We've done a basic tour of what source code is located where inside of the gate as well as covered how we deal with object files. We've introduced our Makefile system and covered the basic layout for these components. In the next section we'll go through and detail all of the components of commands, libraries, and kernel modules, as well as go through examples of adding new ones.