Building Software on the Solaris OS

Introduction

Since the availability of Solaris 10 in January 2005, the popularity of the Solaris OS (using the number of registered users as a yardstick) has exploded: at the time of this writing (August 2006), in excess of 5 million copies were registered, more than all of the previous versions of Solaris combined. Further fuelling this frenzy was the release of OpenSolaris in June 2005. Given this surge in the number of users, more developers (commercial and open source alike) are seeing Solaris as a viable target for their software. This article explains why developers should make Solaris their primary development and deployment platform, and also explains how to develop software on Solaris.

This article assumes that C will be used as the programming language of choice (although much of it is relevant to other high level languages), and is aimed at two kinds of people:

  1. Non-developers who just want to build their favourite (perhaps open source) software on Solaris.
  2. Developers (commercial and otherwise) who are porting their code to Solaris (or are considering doing so).

The article also assumes the use of Solaris 10 (or newer), although most of it is relevant to previous versions.

Why Develop on Solaris?

One of the reasons why Solaris enjoyed a huge popularity boost was its price: $0 for everyone, for any use (commercial and non-commercial), on any machine (both SPARC and x86). Another reason was Sun’s promise (and delivering on that promise) of making the Solaris source code available under a OSI-approved open source license, the CDDL. But the most important reason for Solaris 10’s popularity is arguably the vast wealth of features it offers. In no particular order, these include Zones (the ability to partition a machine into numerous virtual machines, each of which is isolated from the others), DTrace (a comprehensive dynamic tracing tool for investigating system behaviour, safely on production machines), a new IP stack with vastly increased performance, and most recently, ZFS (a 128-bit state of the art file system, with end-to-end error checking and correction, a simple command line interface, and virtually limitless storage capacity).

All these features build on what long-time Solaris users have come to expect: rock-solid stability, huge scalability, high performance, and guaranteed backwards compatibility. The last of these is especially important to commercial software developers, because maintenance is usually the largest expense associated with a piece of software. With its backwards compatibility guarantee, software vendors know that (provided they stick to published APIs) software built for Solaris N will run correctly on Solaris N+1 and subsequent versions. (Contrast this with some other OSes, where incompatible changes to system components (e.g., libraries) are made without regard to the effect on applications. The net effect is application breakage, resulting in increased maintenance costs and frustration for application vendors and users.)

The preceding paragraphs give some reasons why we should develop for Solaris, but there are additional reasons as to why we should develop on Solaris. One is that Solaris is a multi-platform OS, supporting both SPARC and x86 architectures (a community-driven port to Power is in the works too). Although there was a hiccup a few years ago with Solaris x86, the fact that Sun has introduced a range of AMD-based servers and workstations demonstrates their commitment to x86. From the developer’s perspective, Solaris on SPARC and Solaris on x86 have the same feature set and APIs. This means that developers can concentrate on the other issues endemic to cross-platform development, like CPU endianess. SPARC is big endian and x86 is little endian, so an application that is developed and tested on Solaris has a high probability of being free from endian-related problems. Solaris also supports both 32-bit and 64-bit applications on both platforms, thus helping to eliminate bugs due to assumptions about word size and so on.

Perhaps the most compelling reason to develop software on Solaris is the wealth of professional-grade development tools available for it.

Development Tools on Solaris

Market size issues aside, one of the most important features of an OS from a developer’s point of view is the variety and quality of the development tools available. Compilers and debuggers are the most obvious examples of these tools, but other examples include code checkers (to ensure that our code is free from subtle errors the compiler might not catch), cross reference generators (to see which functions reference other functions and variables), performance analysers, and so on.

Sun’s Studio 11 suite is the product of choice for Solaris developers. Available as a free download from developers.sun.com, Studio 11 is a collection of professional-grade compilers and tools. It includes C, C++, and FORTRAN compilers, code analysis tools, an IDE (integrated development environment), the dbx source-level debugger, editors, and so on.

Other tools included with Studio 11 are: cscope (an interactive source browser), ctrace (a tool to generate a self-tracing version of our programs), cxref (a C program cross referencer), dmake (for distributed parallel makes), and lint (the C program checker—historically also known as a C program defluffer!).

Solaris ships with the GNU C compiler, gcc, and its companion source-level debugger, gdb. (Solaris also ships with the very powerful modular debugger, mdb; however, mdb is not a source-level debugger. It is most useful when we are debugging kernel code, or performing post-mortem analysis on programs for which the source is not available. See [Sun 2002] and [McDougall, Mauro, and Gregg 2007] for more information about mdb.) Most (if not all) of the time, the Studio compiler produces better (i.e., faster) code than that generated by gcc, so the former should be used if at all possible. To ensure our code is portable between compilers, we should test that it compiles cleanly at the maximum warning level with both gcc and Sun’s compiler (i.e., the Sun Studio C compiler, cc), before using the latter for the final production build. (The author finds it disheartening that many supposedly portable open source projects rely on gccisms, and cannot be compiled with Sun’s compiler without a fair amount of work. It is the author’s opinion that the fact that gcc is available on many platforms is no excuse for such poor engineering.)

Now that we’ve explored the reasons why we would choose Solaris as our primary development platform, let’s explore how to actually go about doing so.

Building Software on Solaris

To successfully build software on Solaris, we must first ensure that all the tools we need are installed on our development machine. This means that when we install Solaris, we must select at least the “Developer” metacluster (the “Full” or “Full + OEM” metaclusters, being supersets of the Developer metacluster, will also work, and are probably the best option for people new to Solaris). An optional (but highly recommended) step is to install the Studio 11 compilers and tools.

The next step is to make sure that our PATH is correctly set: /usr/ccs/bin is required for various tools (including the linker, ld, sccs, and make), and /usr/sfw/bin is required for gcc and its companion tools (this directory also contains numerous programs that aren’t directly related to development). If the Studio tools are installed, their directory must also be in our PATH. By default, the Studio tools are installed in /opt/SUNWspro/bin, so this directory (or wherever the Studio tools were installed) must also be added to our PATH.

One problem that people new to Solaris sometimes come across is the inclusion of /usr/ucb in their PATH. This directory contains programs which are used to provide compatibility for legacy SunOS 4.x applications and scripts. SunOS shipped with a C compiler, so /usr/ucb contains cc, which is a script that invokes the C compiler with command line options suitable for generating programs that rely on BSD semantics and behaviour. For modern applications, this is most likely not what we want, so it is important to make sure that /usr/ucb is after /usr/ccs/bin and /opt/SUNWspro/bin in our PATH (or perhaps even better, not in our PATH at all).

With the right software installed, and our PATH appropriately set, we’re ready to start building programs. A program called make is used to build programs; it uses the concept of dependancies to determine which steps are required to produce the required results. For example, suppose our program consists of several C files which we compile. All the files will be compiled and the resulting object files will be linked together. If we subsequently modify one of the source files, make will determine that only the modified needs to be compiled, and will link the newly created object file with those from the previous build.

The rules and dependancies used to build our program are described in a file called Makefile, which the make program uses to determine its actions. However, make is pretty smart, and has a number of built in rules which it uses, even if there is no Makefile. One of these built in rules describes how to create an executable program from a C source file, a fact we take advantage of in the following examples.

The file /usr/share/lib/make/make.rules contains the standard rules and definitions used by make, and contains entries for programs written in C, C++, FORTRAN, Java, as well as some other languages. Some of the standard rules for programs written in C include:

  • Retrieving the source code from the SCCS source code control system
  • Using lint to inspect the source code for errors
  • Creating archive libraries (.a files)
  • Pre-processing the source code, leaving the output in files with a .i suffix

Let’s look at a few examples, using the simple counter program shown in the following program listing:

1 #include <stdio.h>

2 int main ()
3 {
4     int i;

5     for (i = 0; i < 10; i++)
6         printf ("i = %d\n", i);

7     return (0);
8 }

Example: Building with Sun’s compiler and make

Our first example runs Sun’s make using the defaults:

rich@marrakesh4098# make counter
cc    -o counter counter.c

Example: Building with Sun’s compiler and gmake

The next example runs GNU’s version of make, gmake, using the defaults:

rich@marrakesh4100# gmake counter
cc    counter.c   -o counter

Notice that even though the arguments to cc are reversed, the end result is the same.

A detailed look at make is beyond the scope of this article, but we should note that the default rules for make (and gmake) use the CC environment variable to determine the name of the compiler. If CC is unset, cc is used by default. (Interested readers are referred to the respective manual pages, and [Oram and Talbott 1991] for more details about make, gmake, and Makefiles.)

Example: Building with gcc and make

Our final example sets the CC variable before calling make:

rich@marrakesh4103# CC=gcc make counter
gcc    -o counter counter.c

Linking with Libraries

Many programs—especially those that are more than moderate in size and complexity—link with libraries other than the default C library. These libraries either come with Solaris, or are third party libraries installed by the system administrator (be they commercially supplied, downloaded from places like Blastwave.org or Sunfreeware.com, or compiled locally).

We link with these libraries, which are also called shared objects, by passing the name of the library to the compiler with the -l flag. For example, to link with libfoo.so, we would use -lfoo. This works fine for system libraries that are installed under /usr/lib, but what about other libraries?

The correct place for these libraries to be installed is a library-specific location under /opt, although many sites still adhere to the deprecated practice of storing them under /usr/local (see the filesystem(5) man page for more about this). However, “somewhere under /opt” leaves a lot of room for interpretation, so we need to tell the linker where to find the libraries we want our program to use. We do this by passing the -L flag to the linker; for example, -L/opt/local/lib would to tell the linker to look for libraries in /opt/local/lib in addition to the default locations.

The problem with -L is that it is only used when the program is linked. The loader needs to be able to find the libraries at run time. The correct way to do this is to set the library run path, by passing the -R flag to the linker, using the same syntax as the -L flag. The incorrect way to do this is to set the LD_LIBRARY_PATH environment variable to the appropriate location. Unfortunately, there is a lot of software that relies on this method of locating libraries. This problem is easily remedied for programs for which we have the source: we can simply edit the Makefile and rebuild it. For commercial applications, we must petition the vendor to build future versions of their programs correctly.

We should point out that there is one legitimate use of LD_LIBRARY_PATH: when we are developing a new version of a library. We can then use the environment variable to override the compiled-in run path. We can run our (correctly linked) programs with LD_LIBRARY_PATH unset, which will use the current version of the library, and then set it to our development area when we are ready to test the new version of the library. For the sake of emphasis, it worth repeating that we should not set LD_LIBRARY_PATH in our session start-up scripts (e.g., .profile or .login). If a program to which we don’t have the source relies on LD_LIBRARY_PATH, we should write a wrapper script which sets it before invoking the desired program.

Summary

This article has described why developers (both commercial and open source) should chose Solaris as their platform of choice, for both development and deployment. These reasons include: the large number of people using Solaris (more than five million at the time of writing); the fact that Solaris is now an open source operating system, the source code for which is available under the OSI-approved CDDL; the wealth of advanced technological features Solaris has, including DTrace, Zones, and improved IP stack, and ZFS; the fact that Solaris is a multi-platform OS (SPARC and x86 currently, with others in the works); Solaris’ commitment to long-term backwards compatibility, which protects end user’s investment in their software; and the wide dearth of professional-quality development tools, some of which are available for no charge.

We also described how we to build software on Solaris, using both Sun’s Studio compilers, and gcc. We described how to install Solaris in a manner suitable for software development, and how to ensure that our PATH is set correctly. We briefly discussed make and gmake, and ended with a discussion about shared objects (dynamically linked libraries), and how to avoid run time problems by not using LD_LIBRARY_PATH (unless we are developing a new version of a library).

Recommended Reading

The author’s book, Solaris Systems Programming [Teer 2005], is essential for readers developing Solaris applications. Many books in the Solaris 10 Software Developer Collection at docs.sun.com/app/docs/coll/45.20 are also worth a read—or at least, being aware of.

Bibliography

McDougall, Richard; Mauro, Jim; and Gregg, Brendan. 2007. Solaris Performance and Tools, ISBN 0-13-156819-1. See www.solarisinternals.com for more details.

Oram, Andrew, and Talbott, Steve. 1991. Managing Projects with Make, ISBN 0-937175-90-0. O’Reilly and Associates, Sebastopol, CA.

Sun Microsystems. 2002. Solaris Modular Debugger Guide, see Sun’s web site for more details: docs.sun.com/app/docs/doc/816-5041.

Teer, Rich. 2005. Solaris Systems Programming, ISBN 0-201-75039-2. See richteer.ca/ssp for more details.

Author’s Bio

Rich Teer is an independent Solaris consultant who has been an active member of the Solaris community for more than ten years. He is the author of the best-selling Sun Microsystems Press book, Solaris Systems Programming, and several Solaris-related articles. He was a member of the OpenSolaris pilot program, and currently serves on the OpenSolaris Community Advisory Board (CAB). Rich lives in Kelowna, British Columbia, with his wife, Jenny, and their canine child, Judge. His web site can be found at richteer.ca.