Welcome to XDA

Search to go directly to your device's forum

Register an account

Unlock full posting privileges

Ask a question

No registration required
Post Reply

[DEV] [GUIDE] [LINUX] Comprehensive Guide to Cross-Compiling

OP JustArchi

19th April 2014, 04:35 AM   |  #1  
JustArchi's Avatar
OP Recognized Contributor / Recognized Developer
Flag Warsaw
Thanks Meter: 27,218
 
7,393 posts
Join Date:Joined: Mar 2013
Donate to Me
More
What is a Cross-Compiler?
Quote:

A cross compiler is a compiler capable of creating executable code for a platform other than the one on which the compiler is running. Cross compiler tools are used to generate executables for embedded system or multiple platforms. It is used to compile for a platform upon which it is not feasible to do the compiling, like microcontrollers that don't support an operating system. From wikipedia


How is that connected with an Android?
In order to create a native C/C++ binary for an Android, you must firstly compile the source code. Usually you can't do so on an Android itself due to lack of proper tools and environment, or hardware barriers, especially amount of RAM. This is why you should learn how to cross-compile, to create a binary on your PC, that your ARM-based Android will understand.


Why do I need it?
You need to learn cross-compiling technique if you want to run native C/C++ programs on an Android. Actually, if you've already built your own custom ROM from AOSP sources (i.e. CyanogenMod), then you used cross-compiling tools and methods even without noticing .
Building an AOSP ROM is fairly easy, there's one command like brunch, which does the job. However, what if you want to compile a custom, not natively included binary? This is the purpose of this tutorial.


What I will learn from this guide?
  • How to properly set C/C++ building environment
  • How to build a native C/C++ application for Android device
  • How to optimize native binaries for my device



Step 1 - The Beginning
You should start from installing any Linux-based OS, I highly suggest trying a Debian-based distro (such as Ubuntu), or even Debian itself, as this tutorial is based on it .

In general, I highly suggest to compile an AOSP ROM (such as CyanogenMod) for your device firstly. This will help you to get familiar with cross-compiling on Android. I also suggest to compile one or two programs from source for your Linux, but if you're brave enough to learn cross-compiling without doing any of these, you can skip those suggestions .


Step 2 - Setting up
Firstly you should make sure that you have all required compile tools already.

Quote:

root@ArchiDroid:~# apt-get update && apt-get install checkinstall

This should do the trick and install all required components.
I suggest creating a new folder and navigating to it, just to avoid a mess, but you can organize everything as you wish.

Start from downloading NDK from here.
Quote:

The NDK is a toolset that allows you to implement parts of your app using native-code languages such as C and C++.

Quote:

root@ArchiDroid:~# wget http://dl.google.com/android/ndk/and...x86_64.tar.bz2
root@ArchiDroid:~# tar xvf android-ndk-r9d-linux-x86_64.tar.bz2
root@ArchiDroid:~# mv android-ndk-r9d ndk

Now you should make a standalone toolchain, navigate to root of your ndk (this is important) and then build your toolchain:
Quote:

root@ArchiDroid:~# cd ndk/
root@ArchiDroid:~/ndk# build/tools/make-standalone-toolchain.sh --toolchain=arm-linux-androideabi-4.8 --platform=android-18 --install-dir=/root/ndkTC
Copying prebuilt binaries...
Copying sysroot headers and libraries...
Copying libstdc++ headers and libraries...
Copying files to: /root/ndkTC
Cleaning up...
Done.

You should edit bolded variables to your preferences. Toolchain is the version of GCC you want to use, 4.8 is currently the newest one, in the future it may be 4.9 and so on. Platform is a target API for your programs, this is important only for android-specific commands, such as logging to logcat. When compiling a native Linux program, this won't matter (but it's a good idea to set it properly, just in case). Install dir specifies destination of your toolchain, make sure that it's other than ndk (as you can see I have ndk in /root/ndk and toolchain in /root/ndkTC).

Now you need to download my exclusive cc.sh script from here and make it executable.

Quote:

root@ArchiDroid:~# wget https://dl.dropboxusercontent.com/u/...79/Files/cc.sh
root@ArchiDroid:~# chmod 755 cc.sh

This script is a very handy tool written by me to make your life easier while cross-compiling. Before running it make sure to edit "BASIC" options, especially NDK paths. Apart from that it's a good idea to take a look at DEVICEFLAGS and setting them properly for your device, or clearing for generic build. You don't need to touch other ones unless you want/need them.

Just for a reference, I'll include currently editable options:
Quote:

#############
### BASIC ###
#############

# Root of NDK, the one which contains $NDK/ndk-build binary
NDK="/root/ndk"

# Root of NDK toolchain, the one used in --install-dir from $NDK/build/tools/make-standalone-toolchain.sh. Make sure it contains $NDKTC/bin directory with $CROSS_COMPILE binaries
NDKTC="/root/ndkTC"

# Optional, may help NDK in some cases, should be equal to GCC version of the toolchain specified above
export NDK_TOOLCHAIN_VERSION=4.8

# This flag turns on ADVANCED section below, you should use "0" if you want easy compiling for generic targets, or "1" if you want to get best optimized results for specific targets
# In general it's strongly suggested to leave it turned on, but if you're using makefiles, which already specify optimization level and everything else, then of course you may want to turn it off
ADVANCED="1"

################
### ADVANCED ###
################

# Device CFLAGS, these should be taken from TARGET_GLOBAL_CFLAGS property of BoardCommonConfig.mk of your device, eventually leave them empty for generic non-device-optimized build
# Please notice that -march flag comes from TARGET_ARCH_VARIANT
DEVICECFLAGS="-march=armv7-a -mtune=cortex-a9 -mfpu=neon -mfloat-abi=softfp"

# This specifies optimization level used during compilation. Usually it's a good idea to keep it on "-O2" for best results, but you may want to experiment with "-Os", "-O3" or "-Ofast"
OLEVEL="-O2"

# This specifies extra optimization flags, which are not selected by any of optimization levels chosen above
# Please notice that they're pretty EXPERIMENTAL, and if you get any compilation errors, the first step is experimenting with them or disabling them completely, you may also want to try different O level
OPTICFLAGS="-s -flto=8 -ffunction-sections -fdata-sections -fvisibility=hidden -funswitch-loops -frename-registers -frerun-cse-after-loop -fomit-frame-pointer -fgcse-after-reload -fgcse-sm -fgcse-las -fweb -ftracer -fstrict-aliasing"

# This specifies extra linker optimizations. Same as above, in case of problems this is second step for finding out the culprit
LDFLAGS="-Wl,-O1 -Wl,--as-needed -Wl,--relax -Wl,--sort-common -Wl,--gc-sections"

# This specifies additional sections to strip, for extra savings on size
STRIPFLAGS="-s -R .note -R .comment -R .gnu.version -R .gnu.version_r"

# Additional definitions, which may help some binaries to work with android
DEFFLAGS="-DNDEBUG -D__ANDROID__"

##############
### EXPERT ###
##############

# This specifies host (target) for makefiles. In some rare scenarios you may also try "--host=arm-linux-androideabi"
# In general you shouldn't change that, as you're compiling binaries for low-level ARM-EABI and not Android itself
CONFIGANDROID="--host=arm-linux-eabi"

# This specifies the CROSS_COMPILE variable, again, in some rare scenarios you may also try "arm-eabi-"
# But beware, NDK doesn't even offer anything apart from arm-linux-androideabi one, however custom toolchains such as Linaro offer arm-eabi as well
CROSS_COMPILE="arm-linux-androideabi-"

# This specifies if we should also override our native toolchain in the PATH in addition to overriding makefile commands such as CC
# You should NOT enable it, unless your makefile calls "gcc" instead of "$CC" and you want to point "gcc" (and similar) to NDKTC
# However, in such case, you should either fix makefile yourself or not use it at all
# You've been warned, this is not a good idea
TCOVERRIDE="0"

# Workaround for some broken compilers with malloc problems (undefined reference to rpl_malloc and similar errors during compiling), don't uncomment unless you need it
#export ac_cv_func_malloc_0_nonnull=yes



As you can notice, my magic script already contains bunch of optimizations, especially device-based optimizations, which are the most important. Now it's the time to run our script, but in current shell and not a new one.

Quote:

root@ArchiDroid:~# source cc.sh
Done setting your environment

CFLAGS: -O2 -march=armv7-a -mtune=cortex-a9 -mfpu=neon -mfloat-abi=softfp -s -flto=8 -ffunction-sections -fdata-sections -fvisibility=hidden -funswitch-loops -frename-registers -frerun-cse-after-loop -fomit-frame-pointer -fgcse-after-reload -fgcse-sm -fgcse-las -fweb -ftracer -fstrict-aliasing -DNDEBUG -D__ANDROID__
LDFLAGS: -Wl,-O1 -Wl,--as-needed -Wl,--relax -Wl,--sort-common -Wl,--gc-sections
CC points to arm-linux-androideabi-gcc and this points to /root/ndkTC/bin/arm-linux-androideabi-gcc

Use "$CC" command for calling gcc and "$CCC" command for calling our optimized CC
Use "$CXX" command for calling g++ and "$CCXX" for calling our optimized CXX
Use "$STRIP" command for calling strip and "$SSTRIP" command for calling our optimized STRIP

Example: "$CCC myprogram.c -o mybinary && $SSTRIP mybinary "

When using makefiles with configure options, always use "./configure $CONFIGANDROID" instead of using "./configure" itself
Please notice that makefiles may, or may not, borrow our CFLAGS and LFLAGS, so I suggest to double-check them and eventually append them to makefile itself
Pro tip: Makefiles with configure options always borrow CC, CFLAGS and LDFLAGS, so if you're using ./configure, probably you don't need to do anything else

Command "source cc.sh" executes cc.sh and "shares" the environment, which means that any exports will be exported to our current shell, and this is what we want. It acts the same as AOSP's ". build/envsetup.sh", so you can also use . instead of source.
As you can see above, my script should let you know if it properly set everything, especially if $CC points to our ndkTC. It also set a generic "$CCC" and "$CCXX" commands, which are optimized versions of standard $CC. $CC points to our cross-compiler, $CCC points to our cross-compiler and also includes our optimization flags.
Quote:

root@ArchiDroid:~# echo $CC
arm-linux-androideabi-gcc
root@ArchiDroid:~# echo $CCC
arm-linux-androideabi-gcc -O2 -march=armv7-a -mtune=cortex-a9 -mfpu=neon -mfloat-abi=softfp -s -flto=8 -ffunction-sections -fdata-sections -fvisibility=hidden -funswitch-loops -frename-registers -frerun-cse-after-loop -fomit-frame-pointer -fgcse-after-reload -fgcse-sm -fgcse-las -fweb -ftracer -fstrict-aliasing -DNDEBUG -D__ANDROID__ -Wl,-O1 -Wl,--as-needed -Wl,--relax -Wl,--sort-common -Wl,--gc-sections


Step 3 - Cross-Compiling
Now we'll compile our first program for Android!

Create a new file hello.c, and put inside:
Code:
#include <stdio.h>
int main (void)
{
   puts ("Hello World!");
   return 0;
}
Now you compile and strip it:
Quote:

root@ArchiDroid:~# $CCC hello.c -o hello && $SSTRIP hello

Remember that $CCC and $SSTRIP command will only work if you source'd cc.sh script explained above. $CCC command compiles source code to a binary with already optimized flags (device flags, optimization level, optimization flags, linker flags), while $SSTRIP command strips "bloat" from output binary, such as comments and notices. The purpose is to make a binary smaller and faster.

You can check if your binary has been compiled properly through readelf command.
Quote:

root@ArchiDroid:~# readelf -A hello
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "ARM v7"
Tag_CPU_arch: v7
Tag_CPU_arch_profile: Application
Tag_ARM_ISA_use: Yes
Tag_THUMB_ISA_use: Thumb-2
Tag_FP_arch: VFPv3
Tag_Advanced_SIMD_arch: NEONv1
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align_needed: 8-byte
Tag_ABI_enum_size: int
Tag_ABI_HardFP_use: SP and DP
Tag_ABI_optimization_goals: Aggressive Speed
Tag_CPU_unaligned_access: v6
Tag_DIV_use: Not allowed

As you can see, I've compiled a binary optimized for ARM v7, with THUMB-2 instructions and NEON support. How nice! Is it because of device-specific flags? Let's check what happens if we use $CC instead of $CCC:
Quote:

root@ArchiDroid:~# readelf -A hello2
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "5TE"
Tag_CPU_arch: v5TE
Tag_ARM_ISA_use: Yes
Tag_THUMB_ISA_use: Thumb-1
Tag_FP_arch: VFPv2
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align_needed: 8-byte
Tag_ABI_enum_size: int
Tag_ABI_optimization_goals: Aggressive Speed
Tag_DIV_use: Not allowed

As you can see, if you do not specify flags, you'll lose major portion of optimizations. Of course binary will work properly, hence it has been cross-compiled for ARM, but we can always make it smaller and faster!


Step 4 - Testing
A favourite part of everything, tests!

Quote:

root@ADB:~/shared# adb shell
root@m0:/ # sysrw
root@m0:/ # exit
root@ADB:~/shared# adb push hello /system/bin/hello
95 KB/s (5124 bytes in 0.052s)
root@ADB:~/shared# adb shell
root@m0:/ # chmod 755 /system/bin/hello
root@m0:/ # chown root:system /system/bin/hello
root@m0:/ # exit

In above example I pushed my binary straight to /system/bin directory, which is in the Android's PATH. If you don't have rooted device that's not a problem, you can use /data/local directory or /storage/sdcard0. You can also upload your binary anywhere you want and download it as any other file, then run from /storage/sdcard0/Download, this way doesn't require even working ADB . Just don't forget about setting proper permissions afterwards!

Now let's try to run it!


If your binary is not in the PATH, you should write full path to your binary of course. As I pushed my binary to /system/bin, I don't need to do so.
If everything finished successfully and you got your very first Hello World response as above, congratulations. You've just compiled and ran your first native C/C++ program on Android device.


What to do next?

In theory, you can now compile anything you want. Here are some apps that I'm using in my ArchiDroid ROM:
These are only a few examples. You can compile anything you want, or even write your own native applications. Good luck!
Last edited by JustArchi; 20th April 2014 at 02:40 PM.
The Following 28 Users Say Thank You to JustArchi For This Useful Post: [ View ]
20th April 2014, 09:05 AM   |  #2  
dicksteele's Avatar
Senior Member
Flag California
Thanks Meter: 1,556
 
2,406 posts
Join Date:Joined: Sep 2010
More
Quote:
Originally Posted by JustArchi

What is a Cross-Compiler?



How is that connected with an Android?
In order to create a native C/C++ binary for an Android, you must firstly compile the source code. Usually you can't do so on an Android itself due to lack of proper tools and environment, or hardware barriers, especially amount of RAM. This is why you should learn how to cross-compile, to create a binary on your PC, that your ARM-based Android will understand.


Why do I need it?
You need to learn cross-compiling technique if you want to run native C/C++ programs on an Android. Actually, if you've already built your own custom ROM from AOSP sources (i.e. CyanogenMod), then you used cross-compiling tools and methods even without noticing .
Building an AOSP ROM is fairly easy, there's one command like brunch, which does the job. However, what if you want to compile a custom, not natively included binary? This is the purpose of this tutorial.


What I will learn from this guide?
  • How to properly set C/C++ building environment
  • How to build a native C/C++ application for Android device
  • How to optimize native binaries for my device



Step 1 - The Beginning
You should start from installing any Linux-based OS, I highly suggest trying a Debian-based distro (such as Ubuntu), or even Debian itself, as this tutorial is based on it .

In general, I highly suggest to compile an AOSP ROM (such as CyanogenMod) for your device firstly. This will help you to get familiar with cross-compiling on Android. I also suggest to compile one or two programs from source for your Linux, but if you're brave enough to learn cross-compiling without doing any of these, you can skip those suggestions .


Step 2 - Setting up
Firstly you should make sure that you have all required compile tools already.



This should do the trick and install all required components.
I suggest creating a new folder and navigating to it, just to avoid a mess, but you can organize everything as you wish.

Start from downloading NDK from here.



Now you should make a standalone toolchain, navigate to root of your ndk (this is important) and then build your toolchain:


You should edit bolded variables to your preferences. Toolchain is the version of GCC you want to use, 4.8 is currently the newest one, in the future it may be 4.9 and so on. Platform is a target API for your programs, this is important only for android-specific commands, such as logging to logcat. When compiling a native Linux program, this won't matter (but it's a good idea to set it properly, just in case). Install dir specifies destination of your toolchain, make sure that it's other than ndk (as you can see I have ndk in /root/ndk and toolchain in /root/ndkTC).

Now you need to download my exclusive cc.sh script from here and make it executable.



This script is a very handy tool written by me to make your life easier while cross-compiling. Before running it make sure to edit "BASIC" options, especially NDK paths. Apart from that it's a good idea to take a look at DEVICEFLAGS and setting them properly for your device, or clearing for generic build. You don't need to touch other ones unless you want/need them.

Just for a reference, I'll include currently editable options:


As you can notice, my magic script already contains bunch of optimizations, especially device-based optimizations, which are the most important. Now it's the time to run our script, but in current shell and not a new one.



Command "source cc.sh" executes cc.sh and "shares" the environment, which means that any exports will be exported to our current shell, and this is what we want. It acts the same as AOSP's ". build/envsetup.sh", so you can also use . instead of source.
As you can see above, my script should let you know if it properly set everything, especially if $CC points to our ndkTC. It also set a generic "$CCC" and "$CCXX" commands, which are optimized versions of standard $CC. $CC points to our cross-compiler, $CCC points to our cross-compiler and also includes our optimization flags.



Step 3 - Cross-Compiling
Now we'll compile our first program for Android!

Create a new file hello.c, and put inside:
Code:
#include <stdio.h>
int main (void)
{
   puts ("Hello World!");
   return 0;
}
Now you compile and strip it:


Remember that $CCC and $SSTRIP command will only work if you source'd cc.sh script explained above. $CCC command compiles source code to a binary with already optimized flags (device flags, optimization level, optimization flags, linker flags), while $SSTRIP command strips "bloat" from output binary, such as comments and notices. The purpose is to make a binary smaller and faster.

You can check if your binary has been compiled properly through readelf command.


As you can see, I've compiled a binary optimized for ARM v7, with THUMB-2 instructions and NEON support. How nice! Is it because of device-specific flags? Let's check what happens if we use $CC instead of $CCC:


As you can see, if you do not specify flags, you'll lose major portion of optimizations. Of course binary will work properly, hence it has been cross-compiled for ARM, but we can always make it smaller and faster!


Step 4 - Testing
A favourite part of everything, tests!



In above example I pushed my binary straight to /system/bin directory, which is in the Android's PATH. If you don't have rooted device that's not a problem, you can use /data/local directory or /storage/sdcard0. You can also upload your binary anywhere you want and download it as any other file, then run from /storage/sdcard0/Download, this way doesn't require even working ADB . Just don't forget about setting proper permissions afterwards!

Now let's try to run it!


If your binary is not in the PATH, you should write full path to your binary of course. As I pushed my binary to /system/bin, I don't need to do so.
If everything finished successfully and you got your very first Hello World response as above, congratulations. You've just compiled and ran your first native C/C++ program on Android device.


What to do next?

In theory, you can now compile anything you want. Here are some apps that I'm using in my ArchiDroid ROM:
These are only a few examples. You can compile anything you want, or even write your own native applications. Good luck!

[Mod Edit: Please don't quote the whole OP]

Fricking awesome. Worked perfect on my builduntu running in VirtualBox
Last edited by vanessaem; 25th April 2014 at 04:23 PM.
The Following User Says Thank You to dicksteele For This Useful Post: [ View ]
20th April 2014, 02:34 PM   |  #3  
JustArchi's Avatar
OP Recognized Contributor / Recognized Developer
Flag Warsaw
Thanks Meter: 27,218
 
7,393 posts
Join Date:Joined: Mar 2013
Donate to Me
More
Quote:
Originally Posted by dicksteele

Fricking awesome. Worked perfect on my builduntu running in VirtualBox

I'm very glad it worked for you .
25th April 2014, 05:22 PM   |  #4  
Dragoon Aethis's Avatar
Member
Thanks Meter: 53
 
53 posts
Join Date:Joined: Jun 2012
More
Maybe you happen to know which packages checkinstall depends on? I want to run this on Arch - pun not intended :P - and pacman doesn't exactly talk with debs.

(Przy okazji, świetny tutorial c: )
25th April 2014, 05:27 PM   |  #5  
JustArchi's Avatar
OP Recognized Contributor / Recognized Developer
Flag Warsaw
Thanks Meter: 27,218
 
7,393 posts
Join Date:Joined: Mar 2013
Donate to Me
More
Quote:
Originally Posted by Dragoon Aethis

Maybe you happen to know which packages checkinstall depends on? I want to run this on Arch - pun not intended :P - and pacman doesn't exactly talk with debs.

(Przy okazji, świetny tutorial c: )

Checkinstall makes sure that you have all required packages installed. You can achieve nearly the same by installing "build-essential, gcc, g++, make", and that should be enough I guess .
The Following User Says Thank You to JustArchi For This Useful Post: [ View ]
25th April 2014, 05:28 PM   |  #6  
JustArchi's Avatar
OP Recognized Contributor / Recognized Developer
Flag Warsaw
Thanks Meter: 27,218
 
7,393 posts
Join Date:Joined: Mar 2013
Donate to Me
More
Also, big kudos to @willverduzco for featuring my guide on XDA portal!
26th April 2014, 12:39 AM   |  #7  
DerRomtester's Avatar
Senior Member
Flag Neumarkt
Thanks Meter: 1,113
 
1,210 posts
Join Date:Joined: Aug 2012
More
I would like to see a guide for llvm/ clang.

Sent from my GT-I9000 using xda app-developers app
26th April 2014, 01:40 AM   |  #8  
Senior Member
Thanks Meter: 59
 
129 posts
Join Date:Joined: Jan 2014
maybe a bit irrelevant... but i wanted to learn how to cross compile/port a binary (for example "applypatch") for cygwin... any link to guide will be helpful

Thank You
26th April 2014, 02:19 AM   |  #9  
JustArchi's Avatar
OP Recognized Contributor / Recognized Developer
Flag Warsaw
Thanks Meter: 27,218
 
7,393 posts
Join Date:Joined: Mar 2013
Donate to Me
More
Quote:
Originally Posted by DerRomtester

I would like to see a guide for llvm/ clang.

Sent from my GT-I9000 using xda app-developers app

When making standalone toolchain you should use clang instead of gcc. You should also study my cc.sh script and adapt to your own. After that, steps are nearly the same.

Quote:
Originally Posted by EnerJon

maybe a bit irrelevant... but i wanted to learn how to cross compile/port a binary (for example "applypatch") for cygwin... any link to guide will be helpful

Thank You

Using Cygwin for such kind of things is... bad. Install VirtualBox and any Linux distro if you want to master cross-compile technique.
The Following 2 Users Say Thank You to JustArchi For This Useful Post: [ View ]
26th April 2014, 02:32 AM   |  #10  
Senior Member
Thanks Meter: 59
 
129 posts
Join Date:Joined: Jan 2014
Quote:
Originally Posted by JustArchi

Using Cygwin for such kind of things is... bad. Install VirtualBox and any Linux distro if you want to master cross-compile technique.

Actually i was making a tool for windows to generate/apply OTA for Android ROMs... i wanted to compile/port "IMGDIFF2" and "applypatch" from android sources...

Post Reply Subscribe to Thread

Tags
justarchi cross compiler compiling guide
Previous Thread Next Thread
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes