Sunday, September 15, 2013

Compiling a strict x86 executable on x86_64 architecture kernel

A quick post, recently I felt a need to compile a strict 32-bit binary executable code to test abnormal behavior of CUDA C source code. I wanted the host code of CUDA to be compiled as x86 architecture on RHEL 6.0 x86_64 OS. CUDA compiler nvcc uses gcc to link and compile in the back-end with added inputs, you can easily verify this by looking at common.mk file in Nvidia Computing SDK.

First make sure to create object files for elf32-i386 architecture. x86_64 system uses 64-bit ELF (Executable and Linkable Format) as default. GCC on x86_64 refers to machine architecture as x86_64 by default if not specified, so there is a "-m64" implicit flag. You can verify this by using verbosity flag in gcc while compiling. COLLECT_GCC_OPTIONS doesn't indicate "-march=1686" as in case of "-m32" explicit flag. GCC provides options as "-m<<32 or 64>>" for specifying the architecture.

"gcc -v test.c" results
"gcc -m32 -v test.c" results
Remember if you are going to generate object files first and then going to link it, those object files must be in elf32-i386 architecture too. If you try to link elf64-x86-64 object files with elf32-i386 compilation following error will occur.

Linking x86_64 object files to x86 linker
Object files can be verified for ELF architecture by using either objdump or file command.

ELF 32-bit format of object file
ELF64-x86-64 format of object file
You might have noticed additional ELF section .eh_frame in x86_64 ELF header of object file. GCC doesn't seem to include this section in object file creation for x86 machine architecture. However if you go ahead and create executable out of this, objdump reveals that both x86 & x86_64 executables have .eh-frame & .eh_frame_hdr in them. This is strange & I want to dig more when I will have time. Anyways .eh_frame contains information for frame unwinding during exception handling & .eh_frame_hdr contains a pointer to the .eh_frame section. The concept of Call Stack Unwinding is hard to describe in this short post, though I will try to explain in short in next section. For detailed Information refer to following Linux Foundation documentation & supporting links which I found useful.

Linux Foundation - Linux Standard Base Core Specification 4.1

SCO SYSTEM V Application Binary Interface

ELF Specification for x86

ELF64 Object File Format

Good Call Stack Reference for C

A call stack is mainly used for keeping track of the point to which control should return after execution of subroutine or in general a called function. In general, if you guys are families with push and pop operations, adding a subroutine's entry to the call stack i.e. push operation is called as Winding & removing entries from call stack is obviously called as Unwinding. Unwinding transfers control out of nested subroutine to previous subroutine which in turn restores proper context after popping many stack frames to assist error handling. In other words if exception handling is enabled, then when run-time exception is thrown the stack unwound until an exception handler is matched. So as we can see elf64-x86_64 by default adds exception handling headers to the object file, however the specification document doesn't clearly mention this & I assumed it to be so. Remember that exception handling for C & C++ is not the same. I would suggest you to read more about this on your own. One more important tool in Linux to dig around ELF is readelf.

On the side note, if you got following error while compiling x86 executable on x86_64 system, maybe you need to install glibc-devel-i386 package compatible with your existing glibc version.

No comments:

Post a Comment