Intel for Android* Developers Learning Series #8: Building the Android* OS for Intel Processors

As there are currently no public engineering devices from Intel, we will work with two images for emulated targets (Note: Please refer to http://source.android.com/source/initializing.html and http://source.android.com/source/downloading.html for the details. The pages help to resolve common problems with tools and environment. You may need to have a Google account.): the target QEMU-based emulator and the target for VirtualBox.

1  Building Android* Images with the GNU* Compiler for Android

The instructions for building an emulator image for ARM* is directly applicable to the Intel x86 target. We briefly describe the steps here.

1.1  Workspace Preparation

mkdir <WORKSPACE_DIRECTORY>
cd <WORKSPACE_DIRECTORY>
repo init -u https://android.googlesource.com/platform/manifest -b android-4.1.1_r3

Note: You may try other branch, for example, master.

repo sync

1.2  Configuring the Build Environment


The Google page does not explicitly mention the full_x86 target (Note: Please refer to http://source.android.com/source/building.html for the details.) but it is shown if you run the lunch command without arguments:
source build/envsetup.sh
lunch full_x86-eng

1.3  Building the Image

Finally choose the number of parallel jobs and run make: 

make –j <NUM_JOBS>

To check that the build was successful, launch the emulator command. Locate information about the system: Applications->Settings->About Phone, as shown in Figure 8.1.

Figure 8.1   Locating system information with About Phone

To build the image for Virtual Box, specify the vbox_x86-eng parameter for the lunch command and 

source build/envsetup.sh

lunch vbox_x86-eng

make -j <NUM_JOBS> android_disk_vdi

 

2  Building Kernels

The instructions for building the kernel for the ARM* emulator are not directly applicable to the x86 targets. You need to correctly specify a compiler. Note that the default GCC compilers version 4.4.3 and 4.6 targeting Android cannot be used as-is for compiling the kernel, because the Android Application Binary Interface (ABI) differs from the Linux ABI. The important difference is that, by default, compilers for Android systems generate position-independent code. If you compile the kernel with GNU* or the Intel® C++ Compiler for Android, you need to add –mno-android or –fno-PIC options during compilation.

We briefly describe the steps to compile the kernel for the emulator. We assume that you have performed the emulator build in the <AOSP WORKSPACE> directory. Target compilers are taken from the workspace.

Prepare workspace:

git clone http://android.googlesource.com/kernel/goldfish.git goldfish-kernel

git branch goldfish remotes/origin/android-goldfish-2.6.29

git checkout goldfish

Configure kernel for emulator: 

make ARCH=x86 goldfish_defconfig

or for Virtual Box:

make ARCH=x86 vbox_defconfig.

ARCH=x86 was added to specify explicitly that we are going to build a kernel for a 32-bit system.

Now we are ready to build the kernel. In the next sections we will build the kernel using two compilers.

2.1  Building the Kernel using the GNU* Compiler for Android

To build the kernel, you need to specify the path to compiler and other tools, linker, assembler, and so on.

The easiest way configure all the paths is to define the CROSS_COMPILE environment variable. As noted before, we also need to add the –mno-android option to disable generation of position-independent code. We redefine the CC variable, so that –mno-andriod is used whenever the compiler is invoked.

export CROSS_COMPILE=<AOSP WORKSPACE>/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.4.3/bin/i686-linux-android-

make ARCH=x86 CC="${CROSS_COMPILE}gcc -mno-android" bzImage 

Run the emulator with the new kernel

emulator –kernel arch/x86/boot/bzImage

and note that the Android emulator runs the new kernel (see Figure 8.2).

Figure 8.2   The Android emulator runs the new kernel.

If you chose to work with the Virtual Box image, then you need to perform a full system image rebuild, specifying the new kernel as the make’s parameter:

cd <AOSP WORKSPACE >;

rm out/target/product/vbox_x86/kernel

make -j <NUM_JOBS> LOCAL_KERNEL=<path to goldfish-kernel directory>/arch/x86/boot/bzImage android_disk_vdi

2.2  Building the Kernel using the Intel® C++ Compiler for Android

The instructions for the Intel compiler are slightly more complicated. We assume that the kernel for the emulator is already configured.

export CROSS_COMPILE=<AOSP WORKSPACE>/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.4.3/bin/i686-linux-android-

export ANDROID_GNU_X86_TOOLCHAIN=$(dirname $CROSS_COMPILE)/../

make ARCH=x86 CC="<path to compiler>/icc -fno-PIC -falign-stack=assume-4-byte -diag-disable 47,1419,311,147,175 -mno-sse -mno-sse2" bzImage

The Intel compiler needs GCC header files and the ANDROID_GNU_X86_TOOLCHAIN environment variable specifies the root GCC installation for the Intel compiler. We also added several options:

·       -fno-PIC is needed to disable generation of position-independent code

·       -diag-disable 47,1419,311,147,175 is needed to avoid build failures due to the –Werror  option in kernel makefiles; Intel compiler often generates more warnings than GCC;

·       -mno-sse -mno-sse2 are required because some files in arch/x86/boot subdirectories are compiled without these flags. These options are specified for the majority of the source files; we reported this issue to kernel developers

If you run the emulator with the new kernel in verbose mode 

emulator –kernel arch/x86/boot/bzImage –show-kernel

you will find from the output that the kernel was built by icc in gcc 4.4.3 compatibility mode.

To enable Intel-specific optimizations for Intel® Atom™ you need to modify the arch/x86/Makefile_32.cpu file:

·       remove the line configuring cflags-$(CONFIG_X86_GENERIC);

·       add the line cflags-$(CONFIG_MCORE2)  += $(call cc-option,-xSSSE3_ATOM).

We cannot simply add -xSSSE3_ATOM to the remaining options, because they would be overridden by GNU* compiler options like –march=i686.

 3  Building Images with the Intel® C++ Compiler for Android

The Android build system is designed in such a way as to allow fine-grain component setup. For example, it is possible to specify special compiler options for improved performance on a component-by-component basis. You can override the C compiler in the component Android.mk file by defining a LOCAL_CC variable. The only restriction is that the compiler should support global compiler options. If the compiler does not support all the options, then special tweaks should be implemented in the Android build system

The Android build system also allows for switching compilers globally and the primary place to adjust for a specific compiler is the file build/core/combo/TARGET_linux-x86.mk. Paths to the compiler, assembler, other tools, and binary tools are located here. Default compiler options are specified in this file as well.

3.1  Intel Compiler Integration

We will make the Intel compiler to be the default. We start with the modifications in build/core/combo/TARGET_linux-x86.mk file.

First specify the root of compiler installation in the TARGET_TOOLCHAIN_ROOT variable. 

TARGET_TOOLCHAIN_ROOT := prebuilts/icc

We assume that the Intel compiler was copied to the prebuilts/icc directory. Then initialize the environment variable ANDROID_GNU_X86_TOOLCHAIN with the top-level directory of GNU tools:

export ANDROID_GNU_X86_TOOLCHAIN:=$(abspath prebuilts/gcc/$(HOST_PREBUILT_TAG)/x86/i686-android-linux-4.4.3)

You also need to change paths to tools and compilers:

TARGET_TOOLS_PREFIX :=$(TARGET_TOOLCHAIN_ROOT)/bin/

TARGET_CC:=$(TARGET_TOOLS_PREFIX)icc$(HOST_EXECUTABLE_SUFFIX)

TARGET_CXX:=$(TARGET_TOOLS_PREFIX)icpc$(HOST_EXECUTABLE_SUFFIX)

TARGET_AR:=$(TARGET_TOOLS_PREFIX)xiar$(HOST_EXECUTABLE_SUFFIX)

TARGET_LD:=$(TARGET_TOOLS_PREFIX)xild$(HOST_EXECUTABLE_SUFFIX)

TARGET_OBJCOPY:=$(ANDROID_GNU_X86_TOOLCHAIN)/bin/i686-android-linux-objcopy$(HOST_EXECUTABLE_SUFFIX)

TARGET_STRIP:=$(ANDROID_GNU_X86_TOOLCHAIN)/bin/i686-android-linux-strip$(HOST_EXECUTABLE_SUFFIX)

Review the TARGET_GLOBAL_CFLAGS variable, replace the GNU compiler options with the corresponding Intel compiler options, and add architecture-specific options.

When you modify TARGET_GLOBAL_CFLAGS variable, do not forget to fix options for the clang compiler. To remove the –xSSSE3_ATOM Intel-specific option for clang, insert the line $(call clang-flags-subst,-xSSSE3_ATOM) in the build/core/llvm_config.mk file. 

The next step is the configuration of prebuilt components corresponding to the Intel compiler’s libraries. For brevity we will consider only the shared version of the libimf library. Create the prebuilts/icc/Android.mk file and add the following lines to it:

LOCAL_PATH := /

include $(CLEAR_VARS)

LOCAL_SYSTEM_SHARED_LIBRARIES:=

LOCAL_MODULE := libimf

LOCAL_MODULE_SUFFIX:=.so

LOCAL_STRIP_MODULE:=true

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE_CLASS := SHARED_LIBRARIES

LOCAL_SRC_FILES := $(shell env ANDROID_GNU_X86_TOOLCHAIN=$(ANDROID_GNU_X86_TOOLCHAIN) $(TARGET_CC) -print-file-name=libimf.so)

include $(BUILD_PREBUILT)

 

For static libraries the LOCAL_MODULE_SUFFIX, LOCAL_SRC_FILES and LOCAL_MODULE_CLASS should be updated in a trivial way. We suggest having different LOCAL_MODULE values for shared and static versions of the same library, because the duplicate names of the prebuilt modules confused older Android build systems.

The value of LOCAL_PATH looks unusual. In most of the Android.mk files LOCAL_PATH is initialized to the value of the call to my-dir macro: $(call my-dir). LOCAL_PATH is initialized to ‘/’, because paths to libraries, specified by the LOCAL_SRC_FILES variable, are absolute. To avoid manual copying of the libraries, we invoke the compiler with the special option -print-file-name to query the location of a library.

As many shared libraries and nonstatically linked executable files will depend on shared Intel libraries, they should be found by the dependent libraries. For example, the installer image of the Virtual Box image should contain Intel libraries and you need to add them in the bootable/diskinstaller/config.mk file:

installer_base_files += libimf libintlc libsvml 

TARGET_DEFAULT_SYSTEM_SHARED_LIBRARIES environment from build/core/combo/TARGET_linux-x86.mk defines the list of shared libraries that are linked by default. All Intel shared libraries should be added to this variable.

Before you start to build the image, add the –ffreestanding option to LOCAL_CFLAGS variable for all modules from the bionic directory. This option makes the bionic libraries and dynamic linker independent of the compiler’s libraries. 

Now you can start to build the image:

make –j 4 SHOW_COMMANDS=1

The build is likely to fail. There are several reasons for it. The first reason is that some components will need Intel compiler libraries during linking. Adding Intel compiler libraries to TARGET_DEFAULT_SYSTEM_SHARED_LIBRARIES fixes most of the cases, but not all. You will need to add Intel compiler libraries to LOCAL_SYSTEM_SHARED_LIBRARIES or LOCAL_STATIC_LIBRARIES variables if the component is a statically-linked executable file or if the component overrides the default configuration.

The second issue is that the Intel compiler generates more warnings than GCC and some component fails to compile due to-Werror. Disable corresponding warnings in the TARGET_GLOBAL_CFLAGS variable from the build/core/combo/TARGET_linux-x86.mk file using the -diag-disable option.

The third issue is the source incompatibility between GNU and Intel* compilers. There are some nonstandard constructions in Android sources that are rejected by the Intel compiler. These were fixed and reported to Google. We also encountered binary incompatibility between the GNU compiler and the Intel compiler; a workaround was implemented in sources

3.2  Flexible Build System Configuration

The integration described in the previous section is straightforward and is not suitable for experiments, because changing the compiler for the component requires several steps to complete. After initial experiments we implemented simpler per-component reconfiguration of the compiler. 

A similar system is now implemented in Android sources for the LLVM clang compiler. To compile the component with clang, you need only set the LOCAL_CLANG variable to the value “true”. Enabling clang touches three files from the build directory,

·       core/llvm_config.mk – various global parameters and functions for clang;

·       core/clear_vars.mk – default value of LOCAL_CLANG variable;

·       core/binary.mk – all the logic related to compiler’s flags, linker’s flags, and so on is implemented here.

The changes for the clang compiler are self-explanatory and we encourage you to have a look at them.