The Development Environment
This chapter explains the 64-bit application development environment. It describes the build environment, including header and library issues, compiler options, linking, and debugging tools. It also provides guidance on packaging issues.
Before you begin, though, it is important to determine whether your installed version of the operating system is 32-bit or 64-bit. If you have come this far, the assumption is that you are running on the 64-bit version. To confirm this, you can use the isainfo(1) command that was explained in Chapter 3, Comparing 32-bit and 64-bit Interfaces. Even if you are using the 32-bit operating environment, you can still build your 64-bit applications, provided you have the 64-bit library package on your system.
Build Environment
The build environment includes the system headers, compilation system, and libraries. These are explained in the sections that follow.
Headers
A single set of system headers supports both 32-bit and 64-bit compilation environments. You do not need to specify a different include path for the 64-bit compilation environment.
To better understand the changes made to the headers for support of the 64-bit environment, you should understand the various definitions in the header <sys/isa_defs.h>. This header contains a group of well known #defines and sets these for each instruction set architecture. Inclusion of <sys/types.h> automatically includes <sys/isa_defs.h>.
The symbols in the following table are defined by the compilation environment:
__sparc | Indicates any of the SPARC family of processor architectures. This includes SPARC V7, SPARC V8, and SPARC V9 architectures. The symbol sparc is a deprecated historical synonym for __sparc. |
__sparcv8 | Indicates the 32-bit SPARC V8 architecture as defined by Version 8 of the SPARC Architecture Manual. |
__sparcv9 | Indicates the 64-bit SPARC V9 architecture as defined by Version 9 of the SPARC Architecture Manual. |
__i386 | This symbol is generic for all processors implementing the Intel 386 instruction set or a superset of it. This includes all members of the 386, 486, and Pentium family of processors. |
The symbols __sparcv8 and __sparcv9 are mutually exclusive and are only relevant when the symbol __sparc is defined.
The following symbols are derived from some combination of the symbols above being defined:
_ILP32 | The data model where sizes of int, long, and pointer are all 32 bits. |
_LP64 | The data model where sizes of long and pointer are all 64 bits. |
The symbols _ILP32 and _LP64 are also mutually exclusive.
If writing completely portable code is not possible, and specific 32-bit versus 64-bit code is required, make the code conditional using _ILP32 or _LP64. This makes the compilation environment machine independent and maximizes the portability of the application to all 64-bit platforms.
Compilers
The Sun WorkShop C, C++, and Fortran compilation environments have been enhanced to support the creation of both 32-bit and 64-bit applications. The 5.0 release of the C compiler from Sun WorkShop provides 64-bit compilation support.
Native and cross-compilation modes are supported. The default compilation environment continues to produce 32-bit applications. While both modes are supported, they are still architecture-specific. It is not possible to create SPARC objects on Intel machines, nor Intel objects on SPARC machines with the Sun compilers. In the absence of a specification of the architecture or mode of compilation, the appropriate __sparc or __i386 symbol is defined by default, and as part of this, _ILP32 is also defined. This maximizes interoperability with the existing applications and hardware base.
Using the C compiler from Sun WorkShop as an example, to enable the 64-bit compilation environment on a SPARC machine, the -xarch=v9 flag needs to be given as an argument to cc(1).
This generates LP64 code in ELF64 objects. ELF64 is a 64-bit object file format supporting 64-bit processors and architectures. This is in contrast to the ELF32 object files generated when compiling in the default 32-bit mode.
The -xarch=v9 flag is used to generate code on either 32-bit or 64-bit systems. Using the 32-bit compiler you can build 64-bit objects on a 32-bit system (using -xarch=v9) though you cannot run them on a 32-bit system. You need not specify the library path for the 64-bit libraries. If the -l or -L option is used to specify an additional library or library path and that path points only to 32-bit libraries, the linker detects this and fails with an error.
Libraries
The Solaris operating environment provides shared libraries for both 32-bit and 64-bit compilation environments. While no 32-bit static libraries have been removed, no 64-bit static libraries are provided.
32-bit applications must link with 32-bit libraries, and 64-bit applications must link with 64-bit libraries. It is not possible to create or execute a 32-bit application using 64-bit libraries. The 32-bit libraries continue to be located in /usr/lib and /usr/ccs/lib. On SPARC platforms, the 64-bit libraries are located in a sparcv9 subdirectory of the appropriate lib directory. Because the placement of the 32-bit libraries has not changed, 32-bit applications built on prior releases are binary compatible.
In order to build 64-bit applications, you need 64-bit libraries. It is possible to do either native or cross-compilation, because the 64-bit libraries are available for both 32-bit and 64-bit environments. The compiler and other miscellaneous tools (for example; ld, ar, and as) are 32-bit programs capable of building 64-bit programs on 32-bit or 64-bit systems. Of course, a 64-bit program built on a system running the 32-bit operating system cannot execute in that 32-bit environment.
Linking
The linker remains a 32-bit application, but this should be transparent to most users, since it is normally invoked indirectly by the compiler driver, for example, cc(1). If the linker is presented with a collection of ELF32 object files as input, it creates an ELF32 output file; similarly, if it is presented with a collection of ELF64 object files as input, it creates an ELF64 output file. Attempts to mix ELF32 and ELF64 input files are rejected by the linker.
LD_LIBRARY_PATH
The two separate dynamic linker programs for 32-bit applications and for 64-bit applications are: /usr/lib/ld.so.1 and /usr/lib/sparcv9/ld.so.1.
At runtime, both dynamic linkers search the same list of colon-separated directories specified by the LD_LIBRARY_PATH environment variable. However, the 32-bit dynamic linker binds only to 32-bit libraries, while the 64-bit dynamic linker binds only to 64-bit libraries. So directories containing both 32-bit and 64-bit libraries can be specified via LD_LIBRARY_PATH, if needed.
The 64-bit dynamic linker's search path can be completely overridden using the LD_LIBRARY_PATH_64 environment variable.
$ORIGIN
A common technique for distributing and managing applications is to place related applications and libraries into a simple directory hierarchy. Typically, the libraries used by the applications reside in a lib subdirectory, while the applications themselves reside in a bin subdirectory of a base directory. This base directory can then be exported using NFS, Sun's distributed computing file system, and mounted on client machines. In some environments, the automounter and the name service can be used to distribute the applications, and to ensure the file-system namespace of the application hierarchy is the same on all clients. In such environments, the applications can be built using the -R flag to the linker to specify the absolute path names of the directories that should be searched for shared libraries at runtime.
However, in other environments, the file system namespace is not so well controlled, and developers have resorted to using a debugging tool -- the LD_LIBRARY_PATH environment variable -- to specify the library search path in a wrapper script. This is no longer necessary, since the $ORIGIN keyword can be used in path names specified to the linker -R option. The $ORIGIN keyword is expanded at runtime to be the name of the directory where the executable itself is located. This effectively means that the path name to the library directory can be specified using the pathname relative to $ORIGIN. This allows the application base directory to be relocated without having to set LD_LIBRARY_PATH at all.
This functionality is available for both 32-bit and 64-bit applications, and it is well worth considering when creating new applications to reduce the dependencies on users or scripts correctly configuring LD_LIBRARY_PATH.
See the Linker and Libraries Guide for further details.
Packaging
The following sections discuss packaging considerations for 32-bit and 64-bit applications.
Placement of Libraries and Programs
The placement of new libraries and programs follows the standard conventions described in "Libraries". The 32-bit libraries continue to be located in the same place, while the 64-bit libraries should be placed in the specific architecture-dependent directory under the normal default directories. Placement of 32-bit and 64-bit specific applications should be transparent to the user.
For SPARC machines, this means that 32-bit libraries should be placed in the same library directories. 64-bit libraries should be placed in the sparcv9 subdirectory under the appropriate lib directory.
Programs that require versions specific to 32-bit or 64-bit environments are a slightly different case. These should be placed in the appropriate sparcv7 or sparcv9 subdirectory of the directory where they are normally located.
See "Application Naming Conventions".
Packaging Guidelines
Packaging options include creating specific packages for 32-bit and 64-bit applications, or combining the 32-bit and 64-bit versions in a single package. In the case where a single package is created, you should use the subdirectory naming convention for the contents of the package, as described in this chapter.
Application Naming Conventions
Rather than having specific names for 32-bit and 64-bit versions of an application, such as foo32 and foo64, 32-bit and 64-bit applications can be placed in the appropriate platform-specific subdirectory, as explained in "Placement of Libraries and Programs". Wrappers, which are explained in the next section, can then be used to run the correct version of the application. One advantage is that the user does not need to know about the specific 32-bit and 64-bit version, since the correct version executes automatically, depending on the platform.
Wrappers
In the case where 32-bit and 64-bit specific versions of applications are required, shell-script wrappers can make the version transparent to the user. This is the case with a number of tools in the Solaris operating environment, where 32-bit and 64-bit versions are needed. A wrapper can use the isalist() command to determine the native instruction sets executable on a particular hardware platform, and run the appropriate version of the tool based on this.
This is an example of a native instruction set wrapper:
#! /bin/sh CMD=`basename $0` DIR=`dirname $0` EXEC= for isa in `/usr/bin/isalist`; do if [-x ${DIR}/${isa}/${CMD}]; then EXEC=${DIR}/${isa}/${CMD} break fi done if [-z "${EXEC}"]; then echo 1>&2 "$0: no executable for this architecture" exit 1 fi exec ${EXEC} "${@}" |
One problem with this example is that it expects the $0 argument to be a full pathname to its own executable. For this reason, a generic wrapper, isaexec(), has been created to address the problem of 32-bit and 64-bit specific applications. A description of this wrapper follows.