Translation(s): none

X32 is an ABI for amd64/x86_64 CPUs using 32-bit integers, longs and pointers. The idea is to combine the smaller memory and cache footprint from 32-bit data types with the larger register set of x86_64. The 64-bit registers can make computation more efficient, and with 8 additional registers available, there is less pressure compared to i386/i686.


Compared to amd64, x32 offers significant memory savings, often on the order of 30%, and modest efficiency gains.

Compared to i386, there's no memory use difference, but speed increases are more pronounced, especially in code that's under register pressure, operates on 64-bit or floating-point variables. It also avoids i386's penalty for PIC code, where EBX is essentially reserved for the Global Offset Table (GOT).

There are three principal use cases:

Getting Started

If you're ok with using outside sources, there's an unofficial repository with porting patches applied. It does include working d-i images.

Before you can run X32 binaries, you will need to install a kernel with the appropriate support. This means a kernel of version 3.4.0 or later, configured with CONFIG_X86_X32=y.

Starting with Debian kernel version 3.14.15-1, the precompiled Debian kernels have X32 support, but it needs to be explicitly activated by passing syscall.x32=y on the command line. For example, to activate it in the default GRUB boot, edit /etc/default/grub and modify these lines:

GRUB_CMDLINE_LINUX_DEFAULT="syscall.x32=y quiet"

Once you're running a kernel with X32 support, you can create a chroot to start experimenting with:

apt-get install debian-ports-archive-keyring
debootstrap --arch=x32 --keyring=/usr/share/keyrings/debian-ports-archive-keyring.gpg unstable /path/to/chroot

In the above command, substitute a name like debian-x32 for /path/to/chroot. If using debian-x32, then your command would be chroot debian-x32.

The old multistrap way:

cat > x32-multistrap.conf << EOF
# same as --tidy-up option if set to true
# same as --no-auth option if set to true
# keyring packages listed in each debootstrap will
# still be installed.
# extract all downloaded archives (default is true)
# the order of sections is not important.
# the debootstrap option determines which repository
# is used to calculate the list of Priority: required packages.
debootstrap=Base Base-unreleased
aptsources=Base Base-unreleased

packages=apt build-essential fakeroot


multistrap -f ~/x32-multistrap.conf -d /root/x32-chroot/
chroot /root/x32-chroot/ /var/lib/dpkg/info/dash.preinst && \
chroot /root/x32-chroot/ dpkg --configure -a && \
chroot /root/x32-chroot/ /bin/bash -c 'dpkg -i /var/cache/apt/archives/*.deb')

Common Issues

Detecting X32

When X32 is being targeted, both X32 and x86_64 preprocessor macros are defined. To detect X32, you should use __ILP32__ rather than __LP64__ because __ILP32__ is specified by the X32 ABI, while __LP64__ is not specified by the AMD64 ABI.

Outdated libtool

This typically manifests with an error like:

/bin/bash ../libtool  --tag=CC   --mode=link gcc  -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnux32/dbus-1.0/include -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnux32/glib-2.0/include   -pthread -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnux32/glib-2.0/include -I/usr/include/gtk-2.0 -I/usr/lib/x86_64-linux-gnux32/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/pango-1.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/libpng12     -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wall -g -version-info 100:6:100 -export-dynamic -export-symbols-regex "^unique.*" -rpath /usr/lib -no-undefined -Wl,-z,relro -Wl,-z,defs -Wl,-O1 -Wl,--as-needed -o -rpath /usr/lib uniqueapp.lo uniquebackend.lo uniquemessage.lo    uniqueenumtypes.lo  uniquemarshal.lo ../unique/bacon/ ../unique/dbus/ -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgio-2.0 -lpangoft2-1.0 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo -lpango-1.0 -lfreetype -lfontconfig -lgobject-2.0 -lglib-2.0   -lX11   -ldbus-glib-1 -ldbus-1 -lgobject-2.0 -lglib-2.0   
../libtool: line 7510: test: : integer expression expected
../libtool: line 7510: test: : integer expression expected
../libtool: line 7615: test: : integer expression expected
../libtool: line 7687: test: : integer expression expected
../libtool: line 7687: test: : integer expression expected
../libtool: line 7687: test: : integer expression expected
../libtool: line 7687: test: : integer expression expected
libtool: link: /usr/bin/ld -m elf_i386 -r -o .libs/ .libs/uniqueapp.o 
/usr/bin/ld: Relocatable linking with relocations from format elf32-x86-64 (.libs/uniqueapp.o) to format elf32-i386 (.libs/ is not supported
make[5]: *** [] Error 1
make[5]: Leaving directory `/tmp/buildd/libunique-1.1.6/unique'
make[4]: *** [all-recursive] Error 1
make[4]: Leaving directory `/tmp/buildd/libunique-1.1.6/unique'
make[3]: *** [all] Error 2
make[3]: Leaving directory `/tmp/buildd/libunique-1.1.6/unique'
make[2]: *** [all-recursive] Error 1
make[2]: Leaving directory `/tmp/buildd/libunique-1.1.6'
make[1]: *** [all] Error 2
make[1]: Leaving directory `/tmp/buildd/libunique-1.1.6'
make: *** [debian/stamp-makefile-build] Error 2
dpkg-buildpackage: error: debian/rules build gave error exit status 2

Note the "-m elf_i386", which should be "-m elf32_x86_64" on X32. The fix is to re-libtoolize with libtool version at least 2.4.2-1.2 from sid. If you prefer to use something like dh_autoreconf to do this at build time, so much the better.

Use of sysctl(2)

The error message from this typically looks something like:

gcc -o jemalloc.o -c  -DOSTYPE=\"Linux3\" -DOSARCH=Linux  -I../../../memory/jemalloc -I. -I../../dist/include -I../../dist/include/nsprpub  -I/usr/include/nspr -I/usr/include/nss      -fPIC -D_FORTIFY_SOURCE=2 -Wall -W -Wno-unused -Wpointer-arith -Wdeclaration-after-statement -Wcast-align -W -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -fno-strict-aliasing -pthread -ffunction-sections -fdata-sections -pipe  -DNDEBUG -DTRIMMED -g -Os -freorder-blocks  -fomit-frame-pointer  -D_FORTIFY_SOURCE=2 -include ../../mozilla-config.h -DMOZILLA_CLIENT -MD -MF .deps/jemalloc.pp /tmp/buildd/iceweasel-10.0.11esr/memory/jemalloc/jemalloc.c
In file included from /usr/include/x86_64-linux-gnux32/sys/sysctl.h:79:0,
                 from /tmp/buildd/iceweasel-10.0.11esr/memory/jemalloc/jemalloc.c:327:
/usr/include/x86_64-linux-gnux32/bits/sysctl.h:19:3: error: #error "sysctl system call is unsupported in x32 kernel"

The sysctl(2) interface is unmaintained and deprecated even on architectures where it's currently provided, so it's better to update the code to read or write from the corresponding /proc/sys/kernel/* file directly.

amd64 assembly

If your code uses inline assembly based on #ifdef __x86_64__ or #ifdef __amd64__, etc., these conditions will also be triggered on X32 builds. Some of the time this will work properly; however, if some of the inputs or outputs of the assembly fragment are of pointer or long types, you might get operand size mismatches like this:

/tmp/buildd/php5-5.4.4/ext/standard/math.c: Assembler messages:
/tmp/buildd/php5-5.4.4/ext/standard/math.c:628: Error: incorrect register `%esi' used with `q' suffix
/tmp/buildd/php5-5.4.4/ext/standard/math.c:629: Error: incorrect register `%edx' used with `q' suffix
/tmp/buildd/php5-5.4.4/ext/standard/math.c:632: Error: incorrect register `%edx' used with `q' suffix
/tmp/buildd/php5-5.4.4/ext/standard/math.c:633: Error: incorrect register `%esi' used with `q' suffix
make[1]: *** [ext/standard/math.lo] Error 1

Depending on the situation, you might be able to get by with just removing explicit "q" suffixes from the opcodes and letting the GNU assembler infer the correct size suffix. Otherwise, you can distinguish between regular amd64 and X32 by testing if __ILP32__ is defined. (Note that it's recommended to use __ILP32__ rather than __LP64__.

If you are interacting with the stack, then you might receive an Error: invalid operand size for a push or a pop. In this case, you have to push or pop a 64-bit register even though ILP32 uses 32-bit integers, longs and pointers. For example:

#if __x86_64__ || __ILP32__
    "pushq    %rbx \n"
#else /* i386 */
    "pushl    %ebx \n"
#if __x86_64__ || __ILP32__
    "popq    %rbx \n"
#else /* i386 */
    "popl    %ebx \n"

Updating the X32 environment

Once you enter the X32 environment via chroot debian-x32, issuing apt-get update may result in an error similar to:

GPG error: unstable InRelease: The following signatures
couldn't be verified because the public key is not available: NO_PUBKEY A53AB45AC448326E

The fix is to install the debian-ports-archive-keyring in the X32 environment (in addition to the host's environment):

apt-get install debian-ports-archive-keyring

linux-thread-db.c and internal error

You might experience an error similar to:

(gdb) r
Starting program: .../cryptest.exe
warning: linux_ptrace_test_ret_to_nx: Cannot PTRACE_PEEKUSER: Input/output error
/build/gdb-gaG2aU/gdb-7.10/gdb/linux-thread-db.c:1675: internal-error:
find_new_threads_once: Assertion `!target_has_execution ||
thread_db_use_events ()' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n)

This is a known issue for C++ programs and GDB 7.10; see Debian Issue #799556: GDB cannot debug a program on the X32 platform (internal-error: find_new_threads_once...). The fix is to guard the assert in gdb/linux-thread-db.c, around line 1675:

#if !defined(__ILP32__) && !defined(_ILP32)
  /* See comment in thread_db_update_thread_list.  */
  gdb_assert (!target_has_execution || thread_db_use_events ());

Mailing List

There is no mailing list dedicated to the X32 platform. According to Would like a ports mailing list for x32, questions and discussions should be held on the debian-amd64 list.

You can find a comprehensive list of available Debian mailing lists at Debian Mailing Lists (Complete Index).

Bug Reports

I am currently using the usertag user for all X32-related bug reports. (I've requested such a mailing list, but it hasn't been created yet.) Some of the tags I'm currently using, or may use in the near future, include:

See also

DanielSchepler CategoryPorts