[Contents] [Prev. Chapter] [Next Section] [Next Chapter] [Index] [Help]

7    Migrating Your ULTRIX Application to a DIGITAL UNIX System

The best way to move your application from an ULTRIX system to a DIGITAL UNIX system is to migrate your source code to the DIGITAL UNIX system. When you port source code, the result is a native DIGITAL UNIX application that is easy to move to new versions of DIGITAL UNIX and new platforms. In addition, you can take advantage of DIGITAL UNIX features, such as 64-bit data types and addressing and shared libraries.

This chapter describes the tasks you perform to migrate source code from an ULTRIX to a DIGITAL UNIX system after you have transported the source files to the DIGITAL UNIX system by using rcp, ftp, or uucp commands, tar archives, Network File System (NFS) mounting, or any other appropriate method. This chapter also gives information about ULTRIX header files that are not supplied on a DIGITAL UNIX system, differences in using the C compiler on an ULTRIX and a DIGITAL UNIX system, and ULTRIX function libraries that are not supplied on a DIGITAL UNIX system.


[Contents] [Prev. Chapter] [Next Section] [Next Chapter] [Index] [Help]

7.1    Modifying Your Makefile

To allow you to conveniently build your application on a DIGITAL UNIX system, modify your makefile so that it works on the DIGITAL UNIX system. The following list describes differences between DIGITAL UNIX and ULTRIX systems that could affect your makefile:

For information about using make on a DIGITAL UNIX system, see make(1).

The ULTRIX make command is also in the /usr/opt/ultrix/usr/bin directory. To use the ULTRIX make command, edit the .login file and add the following line to the end of the file:

source /etc/ultrix_login

This entry modifies your PATH variable to allow access to the ULTRIX make command. For information about using the ULTRIX make command on a DIGITAL UNIX system, see make(1u).


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.2    Migrating References to Header Files

The set of header files on a DIGITAL UNIX system is slightly different from the set of header files on an ULTRIX system.

The contents of some DIGITAL UNIX header files differ from the contents of the equivalent ULTRIX header files. These differences can appear in a number of ways. For example, the interface to a service might be slightly different, structure definitions might be located in different header files, values might have changed to reflect the 64-bit Alpha architecture, or nearly identical structures or constants might have different names. For a list of differences in /usr/include header files, see Appendix B.

Some of the ULTRIX header files are unavailable. These header files are primarily:

For a list of the unavailable /usr/include header files, see Table B--3.

The DIGITAL UNIX header files are kept in a directory hierarchy descending from the /usr/include directory. Table 7-0 lists most of the directories containing standard header files.

Table 7-0:  Locations of Standard DIGITAL UNIX Header Files

Directory Description
/usr/include General C header files
  DPS Display PostScript System C header files
  DXm DIGITAL extensions to Motif C header files
  Mrm Motif resource manager C header files
  X11 X Toolkit header files
  Xm Motif C header files
  dec DIGITAL specific interface header files
  lvm C header files for Logical Volume Manager (LVM)
  mach Mach-specific C include files
  net Miscellaneous network C header files
  netimp C header files for IMP protocols
  netinet C header files for Internet standard protocols
  netns C header files for XNS standard protocols
  nfs C header files for Network File System (NFS)
  protocols C header files for Berkeley service protocols
  rpc C header files for remote procedure calls (RPCs)
  servers C header files for servers
  sys System C header files (kernel data structures)
  tli C header files for Transport Layer Interface (TLI)
  ufs C header files for UNIX File System (UFS)

The compiler can help you migrate your application by finding inconsistencies in the application's use of a symbol, function, or declarations in a header file. The DIGITAL UNIX C compiler issues error messages for the following conditions:

Because function declarations or prototypes are not required by the C language before a function call, the compiler cannot detect misuse of functions that did not have a preceding prototype declared. You might need to find differences in these cases by first determining which header files your application depends on, generating a list of the function declarations these header files contain, and then using this list of functions to generate a cross-reference for the needed header files on a DIGITAL UNIX system. Then you can cross-check the actual declarations for changes in the function interfaces and modify your source code where necessary. Doing this may require that you build short shell scripts to help search for the appropriate definitions in the list of header files. The compiler has features that might be of some use in these tasks:

See Section 7.6.1 for more information on libraries.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3    Migrating to a 64-Bit Environment

The 64-bit DIGITAL UNIX system is different from the 32-bit ULTRIX system in the size of addresses, the availability of 64-bit integer types, the data type alignment restrictions, byte and word accessibility, and interoperability between 32-bit and 64-bit systems. These differences affect the following areas in your programs:

The following sections discuss each of these areas and the changes you must make to your program to take full advantage of the 64-bit environment, and to permit interoperability with 32-bit systems.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.1    Pointers

This section describes migration problems that some applications will encounter because they make assignments based on the assumption that pointers are the same length as int variables. This section also contains information on how to overcome problems with pointer-to-int assignments with little or no recoding. (Information about other types of pointer assignments that may require recoding is provided in Section 7.3.4.2.)

The following table shows the lengths of the data types that are used to hold addresses and that can, in some usage situations, cause problems when migrating an application to a DIGITAL UNIX system:

  ULTRIX DIGITAL UNIX
Pointer 32 bits 64 bits
int 32 bits 32 bits
long 32 bits 64 bits

Many C programs, especially older C programs that do not conform to currently accepted programming practices, assign pointers to int variables. Such assignments are not recommended, but they do produce correct results on systems in which pointers and int variables are the same size. However, on a DIGITAL UNIX system, this practice can produce incorrect results because the high-order 32 bits of a DIGITAL UNIX address are lost when a 64-bit pointer is assigned to a 32-bit int variable. The following code fragment shows this problem using DIGITAL UNIX:

{
char *x;   /* 64-bit pointer */
int z;     /* 32-bit int variable */

.
.
.
x = malloc(1024); /* get memory and store address in 64 bits */ z = x; /* assign low-order 32 bits of 64-bit pointer to 32-bit int variable */ }

Similar problems with the length of pointers occur in applications that consist of a mix of C and FORTRAN programs in which a pointer in a C program is declared as an INTEGER*4 variable in a FORTRAN program, leaving the conversion implicit and causing the loss of the 32 high-order bits in the pointer.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.1.1    Controlling Pointer Size and Allocation

The DIGITAL UNIX system has a set of compiler options and pragmas you can use to control pointer size and allocation, thereby allowing ULTRIX applications that may make assumptions about pointers being 32 bits to more easily migrate to a DIGITAL UNIX environment.

The set of options for the cc command is known as the xtaso option. Combined with the -taso linker option (which is required when the xtaso option is used), the xtaso option can prevent problems with invalid addressing and pointer truncation that could occur when migrating applications with 32-bit pointers to the DIGITAL UNIX system. There are limits to the use of the xtaso option. First, the option should only be used in end-user application programs, and not in library programs. Second, the end-user application should be known to have 32-bit dependencies.

This option is most useful for applications that have already been migrated to the DIGITAL UNIX system, but exhibit performance problems due to either memory limitations or the heavy use of dynamic memory allocation.

The elements of the xtaso option are:


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.1.2    Correcting the Pointer-to-int Assignment Problem

The most portable way to fix the problem presented by pointer-to-int assignments in existing source code is to modify the code to eliminate this type of assignment. However, in the case of large applications, this can be time consuming. (To find pointer-to-int assignments in existing source code, use the lint -Q command.)

Another way to overcome this problem is to use the Truncated Address Support Option (-taso option). The -taso option makes it unnecessary for the pointer-to-int assignments to be modified. It does this by causing a program's address space to be arranged so that all locations within the program when it starts execution can be expressed within the 31 low-order bits of a 64-bit address, including the addresses of routines and data coming from shared libraries.

The -taso option does not affect the sizes used for any of the data types supported by a DIGITAL UNIX system. Its only effect on any of the data types is to limit addresses in pointers to 31 bits (that is, the size of pointers remains at 64 bits but addresses use only the low-order 31 bits).

The 31-bit address limit is used to avoid the possibility of setting the sign bit (bit 31) in 32-bit int variables when pointer-to-int assignments are made. Allowing the sign bit to be set in an int variable by a pointer-to-int assignment would create a potential problem with sign extension. For example:

{
char *x;   /* 64-bit pointer */
int z;     /* 32-bit int variable */

.
.
.
/* address of named_obj = 0x0000 0000 8000 0000 */ x = &named_obj; /* 0x0000 0000 8000 0000 = original pointer value */ z = x; /* 0x8000 0000 = value created by pointer-to-int assignment */ x = z; /* 0xffff ffff 8000 0000 = value created by pointer- to-int-to-pointer or pointer-to-int-to-long assignment (32 high-order bits set to ones by sign extension) */ }


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.1.3    Use and Effects of the -taso Option

You can specify the -taso option on the cc or ld command lines used to create an application's object modules. (If you specifiy it on the cc command line, the option is passed to the ld linker.) The -taso option directs the linker to set a flag in object modules and this flag directs the loader to load the modules into 31-bit address space.

The -taso option ensures that text and data segments of an application are loaded into memory that can be reached by a 31-bit address. Thus, whenever a pointer is assigned to an int variable, the values of the 64-bit pointer and the 32-bit int variable will always be identical (except in the special situations described in Section 7.3.1.4).

Figure 7-1 is an example of a memory diagram of programs that use the -taso and -call_shared options (and do not use threads). (If you invoke the linker (ld) through the cc command, the default is -call_shared. If you invoke ld directly, the default is -non_shared.)

Figure 7-1:  Layout of Memory Under the -taso Option

Note that stack and heap addresses will also fit into 31 bits. The stack grows downward from the bottom of the text segment, and the heap grows upward from the top of the data segment.

The -T and -D options (linker options that are used to set text and data segment addresses, respectively) can also be used to ensure that the text and data segments of an application are loaded into low memory. The -taso option, however, in addition to setting default addresses for text and data segments, also causes shared libraries linked outside the 31-bit address space to be appropriately relocated by the loader.

The default addresses used for the text and data segments are determined by the options that you specify on the cc command line:

Using these default values produces sufficient amounts of space for text and data segments for most applications (see the Assembly Language Programmer's Guide for details on the contents of text and data segments). The default values also allow an application to allocate a large amount of mmap space.

If you specify the -taso option and also specify text and data segment address values with -T and -D, the values specified override the -taso default addresses.

You can use the odump utility to check whether a program was built successfully within a 31-bit address space. To display the start addresses of the text, data, and bss segments, enter the following command:

% odump -ov obj_file_x.o

None of the addresses should have any bits set in bits 31 to 63; only bits 0 to 30 should ever be set.

Shared objects built with the -taso option cannot be linked with shared objects that were not built with the -taso option. If you attempt to link shared objects that way, the following error message is displayed:

Cannot mix 32 and 64 bit shared objects without the -taso option.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.1.4    Limits on the Effects of the -taso Option

The -taso option does not prevent a program from mapping addresses outside the 31-bit limit, and it does not issue warning messages if this is done. Such addresses could be established using any one of the following mechanisms:


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.2    Constants

Check the use of constants in your program, particularly if you are going to exchange data between 32-bit and 64-bit systems. Some constants might have different values between 32-bit and 64-bit systems, which might change the behavior of some operators. For example, hexadecimal constants are more likely to become long on DIGITAL UNIX systems. The following table lists some constants and their values:

C Constant Value Value (32-Bit) Value (64-Bit)
0xFFFFFFFF 232 -1 -1 4,294,967,295
4294967296 232 0 4,294,967,296
0x100000000 232 0 4,294,967,296
0xFFFFFFFFFFFFFFFF 264 -1 -1 -1


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.2.1    Integer and Long Constants--Assignment and Argument Passing

In C, an integer constant is specified as 543210. To specify a long int constant, use the letter suffix L or l. To specify an unsigned long, you use the UL or ul suffix. (L is preferred since lowercase l is easily confused with the number one.) Note the example where three different constants are passed to the function, labs():

labs(543210)
labs(543210L)
labs(543210UL)

On an Alpha system, 543210 is treated as a 4-byte constant, and 543210L (or 543210UL) is treated as an 8-byte constant. If the labs() function expects a long argument, each of these invocations would work as expected since the int constants would be converted to long. If the labs() function expects type int, the long constant would be truncated to an integer constant. This truncation would result in the loss of significant digits if the constant was greater than the maximum integer constant (INT_MAX) of +2147483647, or less than the minimum integer constant (INT_MIN) of -2147483648, or for unsigned constants greater than the maximum unsigned integer constant (UINT_MAX) of 4294967295. This problem would also be present in an assignment expression where a long integer constant was assigned to a variable of type int. In these cases, explicitly use the L or UL suffix and make sure the function arguments or variables being assigned to are of the appropriate long type.

When you need to pass zero to a pointer argument and no function prototype is visible, always use NULL (defined in the stdio.h file). Using zero will result in using a 4-byte zero instead of a 8-byte zero (0L). In a comparison, an assignment, or a function call where the correct function prototype is in scope, standard C promotion rules will be in effect and the correct value will be assigned.

To minimize assignment and argument errors in your code, use function prototypes because the number and type arguments are checked.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.2.2    Integer and Long Constants--Shift Operations

A bit shift operation on an integer constant will yield a 32-bit constant. If you need a result of type long, then you need to use the L or UL suffix for long integer constants. The following example results in value being assigned a 32-bit constant:

long value;
value = 10 << 2;

The top 32 bits of value will depend on the type of the value shifted. Signed values are sign extended; unsigned values are zero extended. If you want a 64-bit constant, be sure to use the L or the UL suffix. (Note that only the left operand of a shift operator determines the result type. The type of shift count operand is irrelevant.)


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.3    Structures

The 64-bit data size of the long and pointer types affects the size, member alignment, alignment, and bit fields of structures.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.3.1    Size

The size of structures and unions on DIGITAL UNIX systems can be different from those on 32-bit systems. For example, the following structure, TextNode, doubles in size on a 64-bit system because the pointer types are double in size (from 4 bytes to 8 bytes):

struct TextNode{
	char                     *text;
	struct TextNode          *left;
	struct TextNode          *right;
	};

If you are sharing data defined in structures between 32-bit and 64-bit systems, be careful about using the long and pointer data types as members in shared structures. These data types introduce sizes that are not available on 32-bit systems.

To increase your application's portability, do the following in your application:


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.3.2    Member Alignment

Members of structures and unions are said to be aligned on their natural boundaries. That is, char is aligned on a byte boundary, short on a word boundary, int on a longword boundary, and long and pointer on quadword boundaries.

This means that additional space can be used for padding member alignment in structures and unions. For example, on 32-bit systems the size of the following structure is 16 bytes. On 64-bit systems, the size of the structure is 32 bytes: 8 bytes for each pointer and 4 bytes of padding after the member, size, for the alignment of the pointer, left.

struct  TextCountNode {
         char                           *text;
         int size,
         struct TextCountNode           *left;
         struct TextCountNode           *right;
};


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.3.3    Alignment

In the 64-bit environment, structures are aligned according to the strictest aligned member. This aids in aligning other structure members on their required boundaries. Structures are also padded to ensure proper alignment. Padding can be added within the structure or at the end of the structure, to terminate the structure on the same alignment boundary on which it started. Therefore, observe the following alignment guidelines when working with structures in a 64-bit environment:

In the following example, the size of CountedString is 16 bytes (*text = 8 bytes, count = 4 bytes, tail padding = 4 bytes). This structure is aligned on a quadword boundary because the pointer requires quadword alignment. No additional padding (beyond 4 bytes of tail padding) is necessary because CountedString will naturally align on a quadword boundary.

struct  {
          char *text;
          int count;
          }CountedString;

In the following example, the inclusion of CountedString as a member in the definition forces the alignment of the beginning of the structure to be on a quadword boundary. Additional padding might be introduced (depending upon the value of MAX_LINE) to ensure proper quadword alignment for the structure member, string. Additional padding might also be introduced at the end of the structure, to ensure proper structure alignment for arrays of these structures.

CountedString CsArray[10]
struct {
          char line[MAX_LINE];
          struct CountedString string;
}TextAndString;

In the following example, the structure has a size of 24 bytes:

struct s{
           int count;
           struct s        *next;
           int total;
}

If this structure is reordered, the structure now has a size of 16 bytes.:

struct s{
          struct s         *next;
          int count;
          int total;
}


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.3.4    Bit Fields

Bit fields are allowed on any integral type on Alpha systems. (ANSI C only requires bit fields with int, signed int, and unsigned int types.) In a C declaration, if one bit field immediately follows another in a structure declaration, the second bit field will be packed into adjacent bits of the former unit. Since long is 64 bits in length on Alpha systems, adjacent declarations of bit fields of type long might contain multiple bit field definitions in cases that previously did not on RISC or VAX systems. This change might cause different results in operations on these bit fields.

To ensure the same behavior in operations on bit fields, change bit field definitions of type long to int.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.4    Variables

The 64-bit DIGITAL UNIX environment also changes assumptions about how you declare your variables, and how you use them in assignments and in function arguments.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.4.1    Declarations

To enable your application to work on both 32-bit and 64-bit systems, check your int and long declarations. If you have specific variables that need to be 32 bits in size on both ULTRIX MIPS and Alpha systems, define the type to be int. If the variable should be 32 bits on ULTRIX MIPS systems and 64 bits on Alpha systems, define the variable to be long. Remember, if the type specifier is missing from a declaration, it defaults to int type. For example, here are six declarations that declare the variables to be of size int and the function to be returning type int:

extern   e;
register n;
static   x;
unsigned i;
const    c;
function ();


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.4.2    Assignments and Function Arguments

Since pointer, int, and long are no longer the same size in the 64-bit DIGITAL UNIX environment, problems may arise depending on how the variables are assigned and used in your application. Use the following guidelines:


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.4.3    The sizeof Operator

The result of the sizeof operator is of type size_t, which is an unsigned long on Alpha systems.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.4.4    Pointer Subtraction

The length of the integer required to hold the difference between two pointers to members of the same array, ptrdiff_t ( stddef.h ), is a signed long on Alpha systems.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.4.5    Functions with a Variable Number of Arguments

When writing a routine that receives a variable (context-dependent) number of arguments, you must use the stdargs (stdarg.h) or varargs (varargs.h) mechanism. See varargs(3) for more information on the use of these macros.

Programs written using varargs that expect the va_list type to be a pointer, or that make assumptions about the stack layout of a routine's arguments, are nonportable. Such programs must be modified to correctly use the varargs(3) mechanism. Failure to do so will give compile-time errors, or incorrect run-time results.

See varargs(3) for more information.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.5    Library Calls

The 64-bit data types also affect the following library calls:

The following sections describe how these functions are affected.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.5.1    The printf and scanf Functions

When using the printf function for long types, you use the length modifier l (lowercase letter L) with the d, u, o, and x conversion characters to specify assignment of type long or unsigned long. For example, when printing a long as a signed decimal, use %ld instead of %d. When printing a long as a unsigned decimal, use %lu instead of %u. If the length modifier is not used, the type is assumed to be int, or unsigned int, depending upon the conversion character. In this case, the long types will be converted to the smaller int types and information might be lost.

When printing a pointer, use %p. If you want to print the pointer as a specific representation, the pointer should be cast to an appropriate integer type long before using the desired format specifier. For example, to print a pointer as a long unsigned decimal number, use %lu:

char *p;
printf ( "%p %lu\n",  (void *)p,  (long)p );

As a rule, to print an integer of arbitrary size, cast the integer to long or unsigned long, and use the %ld ( unsigned long) conversion character. For example:

printf ("%ld\n", (long) num ));


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.5.2    The malloc and calloc Functions

Memory allocation library functions such as malloc() guarantee to return data aligned to the maximum alignment of any object. In the 64-bit DIGITAL UNIX environment, malloc() returns a pointer to memory that is at least quadword aligned.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.5.3    The lseek System Call

When calling the lseek() system call to set the current position in a file, use the off_t type defined in types.h for the file offset. Passing an int or long constant might work, but it is not portable and is not guaranteed to continue to work. The following example shows correct uses of lseek():

lseek function:
#include <unistd.h>
off_t offset, pos;
pos = lseek( fd, offset, SEEK_SET );
pos = lseek( fd, (off_t)0, SEEK_CUR);


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.3.5.4    The fsetpos and fgetpos Functions

When setting or getting the file postions for a file with the ANSI C functions of fsetpos() or fgetpos(), respectively, the file position is specified by a value of type fpos_t. This type is defined as a long in the 64-bit DIGITAL UNIX environment.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.4    Correcting C Syntax Errors

The C compiler on the DIGITAL UNIX system is different from the C compilers on the ULTRIX system. Because of differences in the compilers, you might encounter C syntax errors on DIGITAL UNIX systems that you did not encounter on ULTRIX systems. This section contains information to help you find and correct these errors. In particular, it includes a list of the DIGITAL UNIX predefined symbol names and their meanings. This section also provides information about using DIGITAL UNIX compiler options to get maximum compatibility with ULTRIX compilers, and information about differences between DIGITAL UNIX and ULTRIX C syntax for each of the ULTRIX compilers.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.4.1    Differences Between DIGITAL UNIX and ULTRIX Predefined Symbols

Both the DIGITAL UNIX and ULTRIX systems supply predefined symbols for the cc command. You use these symbols to write conditional code for different hardware platforms, different operating systems, and different programming environments. On DIGITAL UNIX systems, the _ _STDC_ _ symbol is defined as follows:

The predefined symbols on DIGITAL UNIX systems have different names from their equivalents on ULTRIX systems. Table 7--2 compares the DIGITAL UNIX and ULTRIX predefined symbols. If you use these symbols in your application, you must modify the symbol name in your source file before you recompile your application.

Table 7--2:  Comparison of DIGITAL UNIX and ULTRIX Predefined Symbols for the cc Command

Name for -std and -std1 Modes Name for -std0 Mode Name for ULTRIX on RISC Systems Name for ULTRIX on VAX Systems
String containing the host hardware name:
_ _alpha _ _alpha _ _host_mips_ _ vax
String containing the target hardware name:
_ _alpha _ _alpha mips vax
String containing the operating system name:
_ _osf_ _ _ _osf_ _ unix unix
_ _unix_ _ _ _unix_ _ ultrix ultrix
  unix bsd4_2 bsd4_2
String indicating that the host is a BSD system:
_SYSTYPE_BSD _SYSTYPE_BSD SYSTYPE_BSD Not applicable
  SYSTYPE_BSD    
String indicating that the application is written in C:
_ _LANGUAGE_C_ _ _ _LANGUAGE_C_ _ LANGUAGE_C Not applicable
  LANGUAGE_C    
String indicating that double floating-point format is used:
Not applicable Not applicable Not applicable GFLOAT


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.4.2    Differences Between DIGITAL UNIX C and ULTRIX C on RISC Systems

Note

This section describes the behavior of ULTRIX C on Versions 4.3 and earlier RISC ULTRIX systems, and not the behavior of ULTRIX C on Versions 4.3A or later systems. The reason is that Versions 4.3A and later systems employ the MIPS Version 3.0 compiler environment, which is more completely similar to the DIGITAL UNIX C compiler environment than the MIPS Version 2.10 compiler environment on earlier ULTRIX RISC systems, which is described here.

When you compile your ULTRIX application on a DIGITAL UNIX system, you may notice some differences in how the compilers operate. For example, the DIGITAL UNIX compiler might issue errors or warnings in cases for which the ULTRIX compiler does not. To minimize the effect of these differences, use the DIGITAL UNIX compiler option that provides the most compatibility, as shown in Table 7--2.

Table 7--2:  Compilation Options that Are Compatible with ULTRIX C on RISC Systems

If You Use This ULTRIX Option Use This DIGITAL UNIX Option
Default -- K&R C with ANSI extensions. Default (-std0)--K&R C with ANSI extensions. Some ANSI extensions are implemented differently.
-std -std (Issues a warning message for certain non-ANSI syntax. This mode is stricter on a DIGITAL UNIX system, so you receive more warnings than you do on an ULTRIX system.)

Although the DIGITAL UNIX compiler options offer compatibility with the ULTRIX C for RISC compiler, some differences between the two compilers exist. The ULTRIX and DIGITAL UNIX compilers operate differently in some respects regardless of which DIGITAL UNIX compiler mode you use. Other differences occur only when you use the -std0 or the -std1 option. The rest of this section describes these differences.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.4.2.1    Differences that Apply to All Modes

The following list describes compilation differences between ULTRIX C on RISC systems and DIGITAL UNIX C. You might notice the following differences regardless of the compilation mode you use:


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.4.2.2    Differences that Apply to the Default Mode

The default DIGITAL UNIX C compilation mode (specified by the -std0 option) differs from ULTRIX C in the following ways that can affect migrating C source code from ULTRIX C:


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.4.2.3    Differences that Apply to Strict ANSI Mode

The strict ANSI C DIGITAL UNIX compilation mode (specified by the -std1 option) differs from ULTRIX C in the following ways that can affect migrating C source code from ULTRIX C:


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.4.3    Differences Between DIGITAL UNIX C and DEC C

When you compile an application on a DIGITAL UNIX system that is compiled with DEC C on an ULTRIX system, you should notice few differences in how the program is compiled. Both compilers comply with the ANSI C language definition. However, you might notice some differences that result from implementation-specific features, standards-compatible extensions, or differences in interpretations of the ANSI standard.

To minimize the effect of any differences, use the DIGITAL UNIX C compiler option that offers the most compatibility, as shown in Table 7--2.

Table 7--2:  Compilation Options that Are Compatible with DEC C

If You Use This ULTRIX Option Use This DIGITAL UNIX Option
Default (ANSI C with a few compatible extensions) -std (ANSI C with a few compatible extensions. Some differences exist between this mode and the c89 default mode.)
-std (Strict ANSI) -std1 (Strict ANSI.)
-common (K&R C) Default (-std0, which is K&R C with a few ANSI extensions.)
-vaxc

No equivalent.

For information about migrating applications written in the VAX C programming language on ULTRIX, see Section 7.4.5.

The following list describes some differences you might notice between the DIGITAL UNIX C compiler and the DEC C compiler:


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.4.4    Differences Between DIGITAL UNIX C and C on VAX Systems

If you compile an application you wrote for the cc compiler on VAX ULTRIX systems with the DIGITAL UNIX C compiler, you might notice some differences in the language definitions the compilers accept. Some of these differences are hardware specific, others are differences in how the compilers are implemented.

To minimize the effect of these differences, use the DIGITAL UNIX C compiler option that offers the most compatibility, as shown in Table 7--2.

Table 7--2:  Compilation Option that Is Compatible with C on VAX Systems

If You Use This ULTRIX Option Use This DIGITAL UNIX Option
Default (K&R C) Default (-std0)--K&R C with ANSI extensions. Some differences exist due to differences between VAX and RISC systems and differences between the compilers.

The following list details the differences between the DIGITAL UNIX C compiler when you use the -std0 option and the cc command on a VAX machine:


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.4.5    Differences Between DIGITAL UNIX C and VAX C (vcc) Software

If you compile an application you wrote for the cc compiler on VAX ULTRIX systems with the DIGITAL UNIX C compiler, you might notice some differences in how the compilers operate. Some of these differences are hardware specific, others are differences in how the compilers are implemented.

To minimize the effect of these differences, use the DIGITAL UNIX C compiler option that offers the most compatibility, as shown in Table 7--2.

Table 7--2:  Compilation Option for Compatibility with VAX C Software

If You Use This ULTRIX Option Use This DIGITAL UNIX Option
Default (VAX C on ULTRIX) Default (-std0)--K&R C with ANSI extensions. Some differences exist due to differences between VAX and RISC systems and differences between the compilers.

The following list details the differences between the DIGITAL UNIX C compiler when you use the -std0 option and the vcc command:


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.5    Running lint to Find Other Errors

After you create object files for your application, use the lint command to find other problems. The lint command gives you information about whether you use data types correctly in your application, whether you use routines and variables correctly, whether there are any 64-bit migration problems, and so on.

The -Q option is included as support for migrating ULTRIX applications to the DIGITAL UNIX system by identifying those programming techniques that might cause problems on a 64-bit DIGITAL UNIX system. The techniques identified include: pointer alignment; pointer and integer data type combinations; assignments that cause a truncation of long data types; assignments of long data types to another type; structure and pointer combinations; type castings; and format control strings in scanf and printf calls.

Be aware that if you never used lint on your ULTRIX application, it might give you quite a bit of information about your DIGITAL UNIX application, some of which will not be pertinent to the problems with porting your application.

For more information about using lint, see lint(1).


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.6    Linking Your Program

Use the cc compiler to link your application. The linker reports errors caused by routines that do not exist on a DIGITAL UNIX system or by global symbols that are undefined. In some cases, these errors occur because the DIGITAL UNIX system does not provide a routine or a global symbol definition. In other cases, the name of the routine or global symbol has changed.

To determine whether a routine exists, see the DIGITAL UNIX documentation. Check the documentation carefully because the DIGITAL UNIX system has some routines or symbols that use names different from those on the ULTRIX system. If a DIGITAL UNIX routine or symbol exists that performs the task that the ULTRIX routine or symbol performs, modify your program. Replace each reference to the ULTRIX routine or symbol name with the appropriate DIGITAL UNIX routine or symbol name. As you make this change, check each call to ensure that it passes the correct number of parameters in the correct order and that the parameters have the appropriate data type.

If no routine exists on the DIGITAL UNIX system, remove the routine from your application and make appropriate modifications to your applications.

Some ULTRIX libraries are unavailable on DIGITAL UNIX systems. In some cases, the routines that are in the ULTRIX libraries are available in a different DIGITAL UNIX library. In other cases, the ULTRIX library routines are unavailable on the DIGITAL UNIX system. Section 7.6.1 describes ULTRIX libraries that are unavailable on DIGITAL UNIX systems.

The DIGITAL UNIX system provides two libraries for compatibility with ULTRIX systems:

You might need to link your application with one of these libraries if it depends on the behavior of a BSD or System V library routine.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.6.1    Changes in Libraries

The following list summarizes differences between ULTRIX and DIGITAL UNIX system libraries:


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.6.2    ULTRIX BSD Compatibility Library

The DIGITAL UNIX system provides the libbsd.a library to allow you to use library routines that are compatible with ULTRIX BSD library routines. Table 7--2 lists the routines in the library and describes the BSD compatibility they offer. The most significant behavior of the routines in this library are siginterrupt() and signal(), which restart system calls that are interrupted by signals. (The default, in compliance with the POSIX standard, is not to restart system calls that are interrupted by signals.)

To use the BSD functions, use the -D_BSD and -lbsd options on the compilation line.

Table 7--2:  Routines in the ULTRIX BSD Compatibility Library

Routine Name Compatibility
int ftime(struct timeb *) Allows your application to continue to use the ftime function, which is not otherwise provided on DIGITAL UNIX systems. This feature has been made obsolete by the gettimeofday() function.
char *mktemp(char *) Constructs a unique file name; expects a string of at least six characters with trailing 'X' characters, and overwrites the 'X' characters with a unique encoding of the process's process identification (PID) and a pseudorandom number. Unlike the standard DIGITAL UNIX mktemp(), this routine is not thread safe.
int nice(int) Returns a value in the range from -20 to 20. By default, the DIGITAL UNIX system defines process priorities in the range from 0 to 39. This is the same range defined on System V systems. Additionally, if the libc version of nice() fails, errno may be set to the same values as by the setpriority() function.
int rand()void srand(u_int seed) The rand() routine returns a number in the range of 0 to 231 -1. The srand() routine provides a seed for the random number generator.
char *re_comp(char *) Converts a string into an internal form suitable for pattern matching. Returns 0 if the string was compiled successfully; otherwise, returns a pointer to an error message.
int re_exec(char *) Compares the string parameter with the last string passed to the re_comp() function. Returns 1 if the string matches the last compiled regular expression. (The default returns 1 when the string fails to match the regular expression.) Returns 0 if the string fails to match the last compiled regular expression. (The default returns 0 if the string does match the regular expression.) Returns -1 if the compiled regular expression is invalid (indicating an internal error).
int siginterrupt(int, int) Allows you to set the signal state so that system calls are restarted if they are interrupted by the specified signal and no data has been transferred.
sig_t signal(int, sig_t) Causes the system to preserve the value of the SA_RESTART flag if your process explicitly enables or disables system call restart by using the siginterrupt() call.
char *timezone(int, int) The arguments are the number of minutes of time you are westward from Greenwich and whether daylight saving time (DST) is in effect. Returns a string giving the name of the local time zone. Provided for compatibility only.
char * valloc(size_t) Allocates bytes aligned on a page boundary. Provided for compatibility only.
int vtimes(struct vtimes*, struct vtimes*) Returns accounting information for the current process and for the terminated child processes of the current process. Provided for compatibility only; superseded by the getrusage() function.
MINT * xtom(char *key)char * mtox(MINT *key)void mfree(MINT *a) Provided for BSD compatibility for performing arithmetic on integers of arbitrary length.
int wait(union wait *) Provides a wait call whose status_location parameter is of type union wait *.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.6.3    System V Compatibility Library

The DIGITAL UNIX system provides the libsys5.a library to allow you to use library routines that are compatible with System V library routines. Table 7--2 lists the routines in the library and describes the System V compatibility they offer. This library contains routines for those libc routines whose behavior is incompatible with POSIX or X/Open standards. The ULTRIX system also provides a System V compatibility library, libcV.a, which supplies a number of features similar to those that libsys5.a provides. The most significant behavior of the routines in this library is the compatibility with System V nonblocked signals.

For more information about the System V (SVID-2) features in DIGITAL UNIX systems, see the System V Compatibility User's Guide.

Table 7--2:  Routines in the System V Compatibility Library

Routine Name Compatibility
int mknod(char *, int , int ) Supports passing of mode and dev as an int, instead of mode_t and dev_t, respectively.
char * mktemp(char *) Uses getpid() to generate a unique file name. Is not thread safe.
int mount(char *, char *, int, char *, char *, int) Does not support specifying the type of file system, mount flags, such as M_RDONLY and M_NOEXEC, or mount data. Allows you to specify whether the file system is a read-only or read/write system. Also provides SVID-2 compatibility via the MS_DATA flag.
int ptrace(int, int, int, int) Supports passing of pid as an int type rather than pid_t.
int rmdir(const char *) Sets the value of the global variable errno to EEXIST if the directory to be removed contains entries other than dot (.) and double dot (..).
int setjmp(jmp_buf) void longjmp(jmp_buf, int) Do not save and restore the signal state.
pid_t setpgrp(void) If this call is successful, it returns the new process identification (PID).
void (*signal(int, int(*func()) ) ) The specified signal is not blocked from delivery when the handler is entered, and the disposition of the signal reverts to SIG_DFL when the signal is delivered.
int unlink(const char *) An attempt to unlink nonempty directories will cause the unlink call to fail and set errno to ENOTEMPTY, even if the process has superuser privileges.
int umount(char *) Does not support the MNT_NOFORCE, MNT_WAIT, MNT_FORCE, or MNT_NOWAIT flags.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.7    Porting Terminal-Dependent Applications

The DIGITAL UNIX system supports two versions of the termcap library and two versions of the curses library. To use the default termcap library (similar to the BSD 4.3 termcap library), use the -ltermcap option in the compilation line. To use the BSD 4.3-5 termcap curses functions (similar to ULTRIX Version 4.2), use the -D_BSD and -lcurses options in the compilation line. The ULTRIX system supports one version of the termcap library and two versions of the curses library:

Table 7--2 helps to clarify how to port ULTRIX specific applications to the DIGITAL UNIX system.

Table 7--2:  Terminal Capability Differences

If You Use this ULTRIX Option Use this DIGITAL UNIX Option Library Used by C Compiler
-ltermcap or -ltermlib -D_BSD -ltermcap or -D_BSD-ltermlib BSD 4.2 termcap library (IBM AIX library on a DIGITAL UNIX system; similar to BSD 4.3 library)
-D_BSD -lcurses -ltermcap or -lcurses -ltermlib -D_BSD -lcurses -ltermlib BSD 4.2 termcap and curses libraries (BSD 4.3-5 curses and termcap functions on a DIGITAL UNIX system)
-lcursesX -lcurses X/Open curses library (System V Release 3 curses and terminfo functions on a DIGITAL UNIX system)
-lcurses -D_BSD -lcurses BSD 4.2 curses library (BSD 4.3-5 curses functions on a DIGITAL UNIX system)

In addition, the /usr/include/cursesX.h header file is replaced by /usr/include/curses.h, so that you must change all pertinent cursesX references in your source files and makefile.


[Contents] [Prev. Chapter] [Prev. Section] [Next Section] [Next Chapter] [Index] [Help]

7.8    Differences in Standard Interfaces

As described earlier, there are different versions of some library calls included for compatibility with the ULTRIX system. There are a few areas where ULTRIX specific library behavior is not in the DIGITAL UNIX system. The following list describes the known differences in library behavior that are not reflected by changes in the call interface or header file. These differences require that you change your source code.


[Contents] [Prev. Chapter] [Prev. Section] [Next Chapter] [Index] [Help]

7.9    Running Your Program

After your application links successfully, you are ready to run and test it. Correct run-time errors by using the dbx debugger as an aid.

After you correct the semantic errors, your application is ported to the DIGITAL UNIX system. In some cases, it might still not work properly. One possible problem area is differences in the way certain routines on DIGITAL UNIX systems are called or the return values. See Section 7.8 and Appendix B for more information.


[Contents] [Prev. Chapter] [Prev. Section] [Next Chapter] [Index] [Help]