X32 is an alternative ABI for x86-64 that uses the full 64-bit x86-64 instruction and register set and 32-bit pointers and longs. Using only 32-bit pointers limits the address space to 4 GB but uses half the space as 64-bit pointers and thus reduces memory overhead and can lead to better performance for some programs.
X32 support is added to GLIBC 2.16.
Contents
Requirements
To compile GLIBC for x32 target, you need:
- Linux x86-64 kernel 3.4 or above with CONFIG_X86_X32 set to y.
- GCC 4.7
- Binutils 2.22.
Building glibc
To configure GLIBC for x32:
- Make sure that CONFIG_SITE is unset.
$ ${srcdir}/configure BUILD_CC='gcc' CC='gcc -mx32' CXX='g++ -mx32'
Bootstrap X32 GCC and GLIBC
- Build and install x86-64 binutils with sysroot: --target=x86_64-linux --with-sysroot=/usr/gcc-x32-4.7/linux-x86-64 --enable-initfini-array --prefix=/usr/gcc-x32-4.7
- Build and install Linux x86-64 kernel 3.4 or above wth x32 enabled:
- Install exported kernel header files under /usr/gcc-x32-4.7/linux-x86-64.
- Build i686 and x86-64 GLIBC 2.16:
- Install both under /usr/gcc-x32-4.7/linux-x86-64.
- Configure GCC 4.7 or above with x32 enabled and sysroot: --target=x86_64-linux --with-multilib-list=m32,m64,mx32 --with-sysroot=/usr/gcc-x32-4.7/linux-x86-64:
- Libgcc build will fail:
- Missing gnu/stubs-x32.h fro m x32 glibc. (Just copy it from gnu-64.h for the time being).
- Missing x32 GLIBC.
Download and untar the seed x32 tar ball under the "gcc" directory in the GCC build tree.
- Libgcc build will fail:
- Configure and build X32 GLIBC with kernel 3.4 header files and the partially built GCC:
- CC="/export/build/gnu/gcc-x32-sysroot/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc-x32-sysroot/build-x86_64-linux/gcc/ -mx32" CXX="/export/build/gnu/gcc-x32-sysroot/build-x86_64-linux/gcc/g++ -B/export/build/gnu/gcc-x32-sysroot/build-x86_64-linux/gcc/ -mx32" CFLAGS="-O2 -g" /export/gnu/import/git/glibc-x32/configure --enable-add-ons=nptl --prefix=/usr --without-cvs --enable-multi-arch --enable-test-multi-arch --target=x86_64-x32-linux --build=x86_64-linux --host=x86_64-x32-linux --enable-check-abi --with-headers=/export/gnu/import/git/linux-2.6/usr/include
- Install x32 GLIBC under /usr/gcc-x32-4.7/linux-x86-64.
- Conitinue and finish GCC 4.7 build.
x86-64 vs. x32
The main differences between x86-64 and x32 are:
- Size of long and pointers is 8 bytes for x86-64 and 4 bytes for x32.
x86-64 C/C++ compiler predefines macro __LP64__ while x32 C/C++ compiler predefines macro __ILP32__.
- Indirect branch via memory always load 64-bit address from memory for both x86-64 and x32.
- To branch to a 32-bit address stored in memory on x32, you can load the 32-bit address into a 32-bit register, which will be automatically zero-extended to 64 bits, and then do indirect branch via the 64-bit register.
x32 and x86-64 can share the same assembly code as well as C code with inline asm statements:
- When dealing with longs and pointers in assembly code or inline asm statements, we need to use proper register names: 64-bit register for x86-64 and 32-bit register for x32, as well as proper size for long and pointers. GLIBC provides these macros:
RAX_LP, ..., R15_LP to access the proper register names for long and pointers.
LP_SIZE for size of long and pointers in bytes.
In assembly code, we don't need the q instruction suffix if there is a register operand. We just need to use the proper register and the assembler will take care of it. But when there is no register operand, we must use the proper instruction suffix, q for x86-64 and l for x32. GLIBC provides he macro LP_OP (insn) to add the proper instruction suffix to insn.
Also to take the address of a symbol foo in assembly code, x86-64 uses .quad foo but x32 uses .long foo. GLIBC provides the macro ASM_ADDR for the assembly directive when using the address of a symbol.
64-bit registers can be used to operate on pointers and unsigned long passed or returned in registers since the x32 calling conversion specifies pointers and unsigned long are zero-extended to 64 bits when passed or returned in registers.
Never use long or long int to specify 64-bit integers and always use:
int64_t or uint64_t (preferred)
long long int or unsigned long long int