User Tools

Site Tools


documentation:porting:abi

Linux ABI Differences

Alpha's Linux ABI was modeled on DEC OSF/1, not on the i386 ABI that most later Linux ports copied. System call numbers, error numbers, signal numbers, ioctl encodings, and many flag values therefore differ from x86-64 and the generic ABI, and code that hard-codes any of them works on other 64-bit Linux architectures and fails on Alpha. This page lists the differences that most often matter to software that talks to the kernel directly, and other properties of the platform that affect portable code: the 8 KiB page size, the 128-bit long double, the va_list type, and the clock tick rate.

Programs that use the C library's headers and functions are not affected by any of the numeric differences below, since glibc and the kernel headers supply the right values. Code that is affected is typically a language runtime or JIT compiler that makes system calls without libc, a sanitizer runtime, an emulator or tracer that decodes system calls, or a program that copies constants into its own source.

Page size

Alpha uses 8 KiB pages; the kernel selects HAVE_PAGE_SIZE_8KB and has no other option. 1) Code that assumes 4096-byte pages fails in several ways: mmap() offsets and mprotect() addresses that are 4 KiB aligned but not 8 KiB aligned are rejected with EINVAL, guard pages end up the wrong size, and allocators that compute page counts get them wrong. The page size should always be obtained at run time with sysconf(_SC_PAGESIZE) (or getpagesize()), never from a constant.

long double

long double on Alpha/Linux is the 128-bit IEEE 754 quadruple precision format. It was 64 bits wide, the same as double, until glibc 2.4 in 2006, which moved Alpha to 128-bit long double as it did PowerPC, SPARC, and S/390; the functions whose ABI changed have new symbol versions, and the old 64-bit versions remain for existing binaries. 2) GCC uses the 128-bit format by default when it is configured against glibc 2.4 or later, and -mlong-double-64 selects the old format. 3)

No Alpha processor implements quadruple precision in hardware, so every long double operation is a library call (_OtsAddX, _OtsMulX and so on). 4) Code that uses long double for extra precision on x86, where it is the 80-bit extended format in hardware, is much slower on Alpha, and code that assumes long double is the x87 format is wrong. C++ name mangling uses g for the 128-bit type. 5)

va_list

va_list on Alpha is a structure, not a pointer: GCC defines it as a record with a pointer field __base and an integer field __offset, the byte offset of the next argument. 6) Code that treats a va_list as a pointer therefore fails to compile, most often by passing NULL or 0 where a va_list is expected. A configure test that called vsnprintf(0L, 0, 0L, 0L) failed with "conversion from `long int' to non-scalar type `__gnuc_va_list' requested", and so misdetected vsnprintf; the fix is to pass a real va_list variable. 7) A C++ function that passed NULL for a va_list parameter failed to build for the same reason. 8) As on every architecture, a va_list that is to be traversed twice has to be copied with va_copy.

Clock ticks

The clock tick rate reported to user space, USER_HZ, is 1024 on Alpha rather than the generic value of 100, which every other architecture uses. 9) 10) It is the unit of the clock_t values returned by times() and of the CPU times in /proc/pid/stat, 11) 12) and the kernel passes it to programs as AT_CLKTCK in the auxiliary vector. 13) glibc's sysconf(_SC_CLK_TCK) returns that value, and falls back to 1024 on Alpha. 14) Code that divides tick counts by a hard-coded 100 reports CPU times about ten times too large on Alpha; the rate should be obtained with sysconf(_SC_CLK_TCK). CLOCKS_PER_SEC, the unit of clock(), is not affected: glibc defines it as 1000000 on every architecture. 15)

System calls

The system call numbers follow OSF/1, and many slots are still named osf_ in the kernel's table. 16) Alpha does not use the generic system call table (asm-generic/unistd.h) that newer architectures share, so a system call number from x86-64 or arm64 is wrong on Alpha. Newer system calls added to every architecture at once, such as io_uring_setup and clone3, have the same number on all architectures that use per-architecture tables, offset by 110 on Alpha.

The calling convention is also different:

  • The system call number goes in $0 (v0) and the arguments in $16 to $21 (a0 to a5). The callsys PALcode instruction enters the kernel.
  • Errors are not returned as negative values. On return, $19 (a3) is 0 on success and 1 on failure, and on failure $0 holds the positive errno value. 17) Code that tests for a return value between -4095 and -1, as on x86-64, never sees an error on Alpha, and treats small positive error numbers as successful results.
  • Some system calls return two values, in $0 and $20 (a4), after the OSF/1 convention: getxpid returns the process and parent process IDs, getxuid and getxgid the real and effective IDs, and pipe both file descriptors. Separate getppid, geteuid and getegid system calls were added later, as numbers 530 to 532. 18)
  • Alpha has no vDSO, so gettimeofday() and clock_gettime() are always real system calls, and the auxiliary vector has no AT_SYSINFO_EHDR. 19)

Error numbers

Error numbers 1 to 34 are the same as on other architectures, except that EAGAIN and EDEADLK are swapped; from 35 up most of them differ, following OSF/1. 20) For example:

Name Alpha x86-64
EDEADLK 11 35
EAGAIN, EWOULDBLOCK 35 11
EINPROGRESS 36 115
ENOSYS 78 38

Code that stores errno values in files or sends them over the network, or that has tables indexed by error number, needs to use the names, not the numbers.

Signal numbers

Signal numbers also follow OSF/1. 21) Signals 1 to 6, 8, 9, 11, 13 to 15, 21, 22, and 24 to 28 have the same numbers as on x86; 7 is SIGEMT on Alpha and SIGBUS on x86. The others differ, for example:

Name Alpha x86-64
SIGBUS 10 7
SIGSYS 12 31
SIGSTOP 17 19
SIGCONT 19 18
SIGCHLD 20 17
SIGUSR1 30 10
SIGUSR2 31 12

Scripts that send signals by number, such as kill -10, send a different signal on Alpha.

ioctl numbers

The ioctl request encoding has a 13-bit size field and a 3-bit direction field, rather than the 14-bit size and 2-bit direction of the generic encoding, and the direction values differ: _IOC_NONE is 1, _IOC_READ 2, and _IOC_WRITE 4. 22) PowerPC, MIPS, and SPARC use the same layout. Every request number built with _IO, _IOR, _IOW, or _IOWR therefore differs from x86, even _IO requests without data, which carry a direction bit on Alpha and none on x86.

The terminal ioctl requests differ further. On x86 they are the old fixed values, TCGETS being 0x5401; on Alpha they use the _IOR/_IOW encoding, and TCGETS is _IOR('t', 19, struct termios). 23) Alpha's struct termios also differs, with c_line after c_cc rather than before it. 24)

Flags and constants

Many flag values passed to system calls differ as well. A few examples: 25) 26) 27)

Name Alpha x86-64
O_NONBLOCK 0x4 0x800
O_CREAT 0x200 0x40
MAP_ANONYMOUS 0x10 0x20
MAP_FIXED 0x100 0x10
SOL_SOCKET 0xffff 1
SO_REUSEADDR 0x4 0x2

The C library and the toolchain

Alpha's C library is libc.so.6.1 rather than libc.so.6; see Why Alpha Has libc.so.6.1. Its floating-point environment and -mieee are described in Floating Point.

The compiler side of the ABI, including the global offset table limits behind relocation truncated to fit errors and the absence of LLVM, is described on the Toolchains page.

documentation/porting/abi.txt ยท Last modified: by 127.0.0.1