[APACHE DOCUMENTATION]

Apache HTTP Server Version 1.3

Guide to 64-Bit Apache

Mike Abbott - mja@sgi.com


Apache/1.3.7 and beyond can be compiled as either a 32-bit or a 64-bit application, allowing use of a full 64-bit virtual address space and native 64-bit arithmetic. (However, neither 32-bit nor 64-bit Apache can serve files larger than about 2 GB; see below for details.) Currently Apache supports only SGI's 64-bit application binary interface (ABI) but undoubtedly other vendors will port it to their own 64-bit ABIs.

Compiling

By default Apache compiles as a 32-bit application. To compile Apache as a 64-bit application, just enable configure's IRIX64 rule and run make as usual:

  $ configure --enable-rule=IRIX64
  $ make

However, there are some complications.

Rules and ABIs

Apache can be compiled using any of SGI's three ABIs -- known as old 32-bit or O32, new 32-bit or N32, and 64-bit or 64 (sometimes called new 64-bit or N64) -- on platforms that support them. Two of configure's rules, IRIXN32 and IRIX64, select the ABI for which Apache is compiled. The IRIX64 rule overrides the IRIXN32 rule. By default IRIXN32 is enabled and IRIX64 is disabled so Apache is compiled for the N32 ABI.

Apache ABI IRIXN32 rule IRIX64 rule
64-bit (64 or N64) don't care enabled
new 32-bit (N32) enabled (default) disabled (default)
old 32-bit (O32) disabled disabled (default)

On non-SGI platforms Apache can be compiled using only a 32-bit ABI and the above rules do not apply.

Programming

SGI's 64-bit ABI -- and therefore 64-bit Apache -- uses an LP64 model, wherein long ints and all pointers are 64 bits wide (8 bytes) and ints are 32 bits wide (4 bytes). Other types remain the same.

Type 32-bit ABI Size 64-bit ABI Size
char 8 bits 8 bits
short 16 16
int 32 32
long 32 64
pointer 32 64
float 32 32
double 64 64

The discussion and examples below will help you to write code that works in both 32-bit and 64-bit Apache, but this is not a complete porting guide. There is no substitute for passing code through a picky 64-bit compiler and cleaning up the warnings and errors. I recommend always using the compiler option that produces the pickiest warnings and fixing all the warnings it generates. The option to SGI's compilers is -fullwarn, used by default.

Standard Typedefs

Programmers accustomed to 32-bit programming often assume that int and long are interchangeable, and that certain types defined by the development environment are defined in certain ways. Examples relevant to 64-bit Apache are the standard types size_t, ssize_t, off_t, and time_t.

Type Name Actual Type Assumed Type
size_t unsigned long unsigned int
ssize_t long int
off_t long int
time_t int long

All of the following examples compile and run fine under 32-bit ABIs but induce compiler errors or warnings and can produce erroneous results under the 64-bit ABI:

  int length = strlen(str);            /* oops: strlen() returns size_t */

  int r = read(fd, buf, n);            /* oops: read() returns ssize_t */

  struct stat sb;                      /* ok */
  stat(filename, &sb);                 /* ok */
  printf("%d\n", sb.st_size);          /* oops: sb.st_size is off_t */
       /* this produces a warning under SGI's N32 ABI too, see below */

  long now;
  time(&now);                          /* oops: time() takes (time_t *) */

Here are the same examples, corrected for clean 32-bit and 64-bit compilation and operation:

  size_t length = strlen(str);         /* better */
  int length = (int) strlen(str);      /* an alternative */
  int length = ap_strlen(str);         /* another alternative */

  int r = (int) read(fd, buf, n);      /* better - see below */

  struct stat sb;                      /* ok */
  stat(filename, &sb);                 /* ok */
  printf("%ld\n", (long) sb.st_size);  /* better - see below */

  time_t now;
  time(&now);                          /* better */

The general rule is: always use the appropriate type (size_t, etc.), not the base type you think that type is defined as. However, most character strings are shorter than 2 GB so using int to store a strlen() result (with a cast to appease the compiler) is just fine. ap_strlen() is a convenience macro that just casts strlen()'s result to int.

See the sections below for the rule exceptions for file sizes and printfs.

Apache Typedefs

Apache defines four types that are guaranteed to be the right size for certain uses. These types are defined in the source file src/include/ap_types.h.

Type Name Description Use
ap_int32 signed 32-bit integer when at most or exactly 32 bits are required
ap_uint32 unsigned 32-bit integer when at most or exactly 32 bits are required
ap_ptr unsigned pointer-sized integer
(size varies)
for storing and performing arithmetic on addresses
ap_atomic unsigned atomically-updatable integer
(at least as large as ap_ptr)
for atomic operations

Use ap_int32 and ap_uint32 instead of int/long and unsigned int/long when you need at most or exactly 32 bits. (You could use int or unsigned int in SGI's 64-bit ABI, but future 64-bit ABIs may define int as 64 bits.)

  struct binary_log_entry {     /* on-disk binary log entry format */
    ap_uint32  ip_addr;         /* IP address of requester */
    ap_int32   time;            /* time requested */
    ap_int32   nbytes;          /* number of body bytes in response */
      /* ... */
  };

  #define FLAG0  ((ap_uint32) 0x00000001)
  #define FLAG1  ((ap_uint32) 0x00000002)
    /* ... */
  #define FLAG30 ((ap_uint32) 0x40000000)
  #define FLAG31 ((ap_uint32) 0x80000000)
    /* (ap_uint32) cast avoids accidental 64-bit sign extension on FLAG31 */

That reminds me: Avoid using the L or UL suffix on constants; cast the constant to ap_int32 or ap_uint32 instead. Beware constants with bit 31 (0x80000000) set, including -1 and ~0. Broken examples:

  #define FOO    4096UL                    /* oops: scales */
  #define BIG    0xFFFFFFFFL               /* oops: sign-extends */

  /* oops: in 64-bit Apache INADDR_NONE is 0xFFFFFFFFFFFFFF but
   * inet_addr() returns 0xFFFFFFFF on failure */
  #define INADDR_NONE ((unsigned long) -1)
  unsigned long my_addr;
  if ((my_addr = inet_addr(w)) != INADDR_NONE) /* ... */;

Fixed examples:

  #define FOO    ((ap_uint32) 4096)        /* better */
  #define BIG    ((ap_uint32) 0xFFFFFFFF)  /* better */

  /* better */
  #define INADDR_NONE ((ap_uint32) -1)
  ap_uint32 my_addr;
  if ((my_addr = inet_addr(w)) != INADDR_NONE) /* ... */;

Use ap_ptr for storing and performing arithmetic on addresses. See below for details.

Use ap_atomic for atomically-updatable data.

Pointers

Pointers are the same size as ints in 32-bit but not 64-bit programming environments. The following common constructs cause problems:

  int length = string2 - string1;          /* oops: result is long */

  unsigned int address = (unsigned int) &anything;
                                           /* oops: pointer is long */

Corrected examples:

  size_t length = string2 - string1;       /* better */
  int length = (int) (string2 - string1);  /* an alternative */

  unsigned long address = (unsigned long) &anything;  /* better */
  ap_ptr address = (ap_ptr) &anything;     /* an alternative */

Use ap_ptr, size_t, or ptrdiff_t for pointer manipulation unless you're sure the result of pointer subtraction is a small integer in which case cast and assign the result to int.

To store a constant or integer value into a variable having a pointer type without compiler warnings, you must first cast the data to the scaling type ap_ptr and then cast that to the pointer type, like this:

  void *vp = (void *) (ap_ptr) 1;
  struct myfd *fp = (struct myfd *) (ap_ptr) fd;   /* fd is int */

(I think this is yucky but I couldn't find a better way.)

File Sizes

In SGI's 64-bit ABI file sizes (off_t) are 64 bits wide, i.e., files can be larger than 4 GB (theoretically up to 16 exabytes [EB]). (Incidentally, off_t is 64 bits wide in SGI's N32 ABI too, but not SGI's O32 ABI.) However, 64-bit Apache currently forbids serving files whose sizes overflow 32 bits. It is possible to enhance Apache to serve very large files but I chose not to do so. The function ap_oversized_file() returns nonzero if a file is too large to serve. Here is an example, heavily trimmed for clarity, of how to serve a file in both 32-bit and 64-bit Apache:

  struct stat sb;
  int fd, nbytes;

  stat(filename, &sb);
  if (ap_oversized_file(sb.st_size)) {
    ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, request,
      "%s: File too large", filename);
    return HTTP_NOT_IMPLEMENTED;
  }

  fd = open(filename, O_RDONLY);
  while ((nbytes = (int) read(fd, buf, bufsiz)) > 0)
    ap_rwrite(request, buf, nbytes);
  close(fd);
  return OK;

The example checks the validity of the file size first, and safely truncates the result from read() to an int.

Printf Conversions

At first glance it seems difficult to format output using printf(), sprintf(), and the like without a priori knowledge of type sizes. In a 32-bit ABI %d (and %o, %u, and %x) and %ld (and %lo, %lu, and %lx) convert a 32-bit integer, but in a 64-bit ABI %d (et al) converts a 32-bit integer and %ld (et al) converts a 64-bit integer. The following example is erroneous under both ABIs:

  struct stat sb;                            /* ok */
  stat(filename, &sb);                       /* ok */
  printf("size is %d\n", sb.st_size);        /* oops: st_size scales */
  printf("mod time is %ld\n", sb.st_mtime);  /* oops: st_mtime is int */

There are solutions that I consider messy, such as using #ifdef to select between different format strings or using #define to select conversion strings and string concatenation to insert them into the output string, and one that I think is simple and clean: Always use the long form conversions (%ld, %lx, etc.) and cast the data to long or unsigned long, no matter what its original type.

  struct stat sb;                            /* ok */
  stat(filename, &sb);                       /* ok */
  printf("size is %ld\n", (long) sb.st_size);  /* better */
  printf("mod time is %ld\n", (long) sb.st_mtime);  /* better */

  printf("pid is %ld\n", (long) getpid());
  printf("address is %#lx\n", (unsigned long) &mumble);
  printf("sent %lu bytes\n", (unsigned long) nbytes);

SGI's 64-bit compiler checks printf-like format conversions against the type of the corresponding arguments and generates warnings when they don't match (%d with an argument of type long, for instance). If you write your own formatting routine, preface its definition and declarations with the lint-style comment /*PRINTFLIKEn*/ to allow the compiler to compare the conversions with the arguments. n is the ordinal of the format string.

  /*PRINTFLIKE2*/
  extern int myprintf(int mystuff, const char *fmt, ...);
  myprintf(xyz, "this page visited %ld times\n", (long) count);

General

Beware shifting, anding or oring (<< >> & | operators) beyond 32 bits and performing arithmetic (+ - * / operators) that might over- or underflow 32 bits. Truncation to 32 bits is implicit in a 32-bit ABI but not in a 64-bit ABI.

SGI's struct timeval (defined in <sys/time.h>) is defined such that the tv_sec field is 32 bits and the tv_usec field is 64 bits. I have no idea why; it seems wrong to me. But anyway, this causes the compiler to warn about the following construct:

  tv.tv_sec = tv.tv_usec = 0;   /* oops: assigning 64 bits to 32 */

The simple workaround is to separate this into two lines:

  tv.tv_sec = 0;                /* better */
  tv.tv_usec = 0;

Performance

64-bit Apache actually runs slightly slower than 32-bit Apache due to the increased word size but it allows the mmap_static module to map enormous amounts of file data.

Porting to Other 64-bit ABIs

As mentioned above, the only 64-bit ABI Apache supports currently is SGI's. Porting to other 64-bit ABIs that use the LP64 model should be straightforward, probably consisting mostly of enabling the equivalent of the -fullwarn compiler option to identify problem areas and modifying Apache's source code to handle your system's type quirks (size_t, off_t, etc.) if they differ from SGI's. Porting to an ILP64 model, where ints are also 64 bits, will require a larger effort to change all uses of int.

See also

SGI's MIPSpro 64-Bit Porting and Transition Guide contains excellent detailed information about porting 32-bit applications to 64 bits.

Refer to your system's programming documentation for details about its 64-bit implementation.


Apache HTTP Server Version 1.3

Index