Solaris 64-bit Developer's Guide

Chapter 5 The Development Environment

This chapter explains the 64-bit application development environment. The chapter describes the build environment, including header and library issues, compiler options, linking, and debugging tools. The information 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 Interfaces 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 system header files and 64–bit libraries on your system.

Build Environment

The build environment includes the system headers, compilation system, and libraries. These are explained in the sections that follow.

Header Files

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:

Symbol 

Description 

__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.

__x86

Indicates any of the x86 family of processor architectures. These architectures include the 386, 486, Pentium, IA-32, AMD64, and EM64T processors. 

__i386

Indicates the 32-bit i386 architecture. 

__amd64

Indicates the 64-bit amd64 architecture. 


Note –

__i386 and __amd64 are mutually exclusive. 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:

Symbol 

Description 

_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.


Note –

The symbols _ILP32 and _LP64 are 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.

Compiler Environments

The Sun Studio C, C++, and Fortran compilation environments have been enhanced to support the creation of both 32-bit and 64-bit applications. The 10.0 release of the C compiler from Sun Studio 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 x86 machines, nor x86 objects on SPARC machines with the Sun compilers. In the absence of a specification of the architecture or mode of compilation, the appropriate __sparcv8 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.

Starting with the Sun Studio 8 release, use the cc(1) -xarch=generic64 flag to enable the 64-bit compilation environment.

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=generic64 flag is used to generate 64-bit code on either 32-bit or 64-bit system. Using the 32-bit compiler you can build 64-bit objects on a 32-bit system; however, you cannot run the 64-bit objects 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.

For a description of compiler options, see the Sun Studio 10: C User's Guide.

32-bit and 64-bit Libraries

The Solaris operating environment provides shared libraries for both 32-bit and 64-bit compilation environments.

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. The 64-bit libraries are located in a 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. Portable Makefiles should refer to any library directories using the 64 symbolic link.

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 Object Files

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 Environment Variable

SPARC. 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.

x86. For the AMD64 architecture, the dynamic linker programs for 32-bit applications and 64-bit applications are: /usr/lib/ld.so.1 and /usr/lib/amd64/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 Keyword

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 NFSTM, 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 unnecessary, because 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 32-bit and 64-bit Applications

The following sections discuss packaging considerations for 32–bit and 64–bit applications.

Placement of Libraries and Programs

SPARC. The placement of new libraries and programs follows the standard conventions described in 32-bit and 64-bit 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.

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.

64-bit libraries should be placed in the amd64 subdirectory under the appropriate lib directory.

Programs that require versions specific to 32-bit or 64-bit environments should be placed in the appropriate i86 or amd64 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.

Shell-Script 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.

/usr/lib/isaexec Binary File

isaexec(3C) is a 32-bit executable binary file that performs the wrapper function outlined in the shell script wrapper presented in the immediately preceding description, but with precise preservation of the argument list. The executable's full pathname is /usr/lib/isaexec, but it is not designed to be executed by that name. Rather, it can be copied or linked (hard link, not soft link) to the primary name of a program that exists in more than one version, selected using isalist(1).

For example, in a SPARC environment, the command truss(1) exists as three executable files:

The executables in the sparcv7 and sparcv9 subdirectories are the real truss(1) executables, 32-bit and 64-bit respectively. The wrapper file, /usr/bin/truss, is a hard link to /usr/lib/isaexec.

In the x86 environment, the command truss(1) exists as three executable files:

The isaexec(3C) wrapper determines its own fully resolved symlink-free path name using getexecname(3C), independent of its argv[0] argument, gets the isalist(1) through sysinfo(SI_ISALIST, ...), and performs an exec(2) of the first executable file bearing its own name found in the resulting list of subdirectories of its own directory. It then passes the argument vector and environment vector unchanged. In this way, argv[0] passed to the final program image appears as first specified, not as transformed into a full path name modified to contain the name of the subdirectory.


Note –

Because wrappers might exist, you need to be careful when moving executables to different locations. You might move the wrapper rather than the actual program.


isaexec(3c) Interface

Many applications already use startup wrapper programs to set environment variables, clear temporary files, start daemons, and so on. The isaexec(3C) interface in libc(3LIB) allows the same algorithm used in the shell-based wrapper example above to be invoked directly from a custom wrapper program.

Debugging 64-bit Applications

All of the Solaris debugging tools have been updated to work with 64-bit applications. This includes the truss(1) command, the /proc tools (proc(1)) and mdb.

The dbx debugger, capable of debugging 64-bit applications, is available as part of the Sun Studio tool suites. The remaining tools are included with the Solaris release.

The options for all these tools are unchanged. A number of enhancements are available in mdb for debugging 64-bit programs. As expected, using “*” to dereference a pointer will dereference 8 bytes for 64-bit programs and 4 bytes for 32-bit programs. In addition, the following modifiers are available:

Additional ?, /, = modifiers:
	
g		(8) Display 8 bytes in unsigned octal	
G		(8) Display 8 bytes in signed octal
e		(8) Display 8 bytes in signed decimal	
E		(8) Display 8 bytes in unsigned decimal
J		(8) Display 8 bytes in hexadecimal
K		(n) Print pointer or long in hexadecimal
		  Display 4 bytes for 32-bit programs
		  and 8 bytes for 64-bit programs.
y		(8) Print 8 bytes in date format
 
Additional ? and / modifiers:
 
M <value> <mask>  Apply <mask> and compare for 8-byte value;
		  move '.' to matching location.
Z		(8) write 8 bytes