Saturday, August 2, 2014

Problem on linking OpenSSL into your NDK application

This week, I tried to compile a simple NDK application and link it with the OpenSSL library. Most of libraries (including OpenSSL) are not supported by the NDK, what makes it a bit more complicated to use. So, in this post, I describe what I usually do to properly compile applications that need external libs.

The project structure is as follows:

my_project/
    + jni/my_code.c
    + jni/Android.mk
    + jni/Application.mk
    + libs/system

Problem #01: How my Android.mk looks like?

In this example, I need two libraries: libcrypto.so and libssl.so. So, the final Android.mk looks like this

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := my_exec
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := my_code.c
LOCAL_C_INCLUDES += $(ANDROID_SRC)/external/openssl/include \ 
                    $(ANDROID_SRC)/external/openssl/crypto \ 
                    $(KERNEL_SRC)/kernel/module
LOCAL_LDLIBS += -L$(LOCAL_PATH)/../libs/system
LOCAL_SHARED_LIBRARIES := libandroid libdl libz libcrypto libssl
LOCAL_LDLIBS += -landroid -ldl -lz


include $(BUILD_EXECUTABLE) 

See, as an example, that we also include the headers for the library (in ANDROID_SRC/external/openssl/include).

NDK does not provide support for libcrypto.so and libssl.so -- we need to have access to the libraries somehow. So, you should create a folder (for example, my_project/libs/system) and push the files /system/lib/libcrypto.so and /system/lib/libssl.so from the device to such folder.

Problem #02: Where do I get the libs from?

You shall get all of them (including libc.so) from your rooted device. The point is that, as I said, NDK does not provide support for libcrypto.so and libssl.so. Therefore, you need to get such libraries from the device. However, there's another problem: most likely, the libcrypto.so and libssl.so libraries don't recognize the symbols from the NDK libc.so and libstdc++.so libraries. Regarding this problem, it will be discussed on item #04.

Problem #03: Where do I get the headers from?

Usually, you get them from the android source code. For OpenSSL, they are located inside the folder ANDROID_SRC/external.

Problem #04: Why does my lib complain about libc symbols?

As I described in topic #02, you need to copy the libraries from the device. However, libcrypto.so and libssl.so do not recognize some symbols from the libc.so and libstdc++.so libraries (most likely, the libraries provided by NDK are not compatible with the ones in the device). Usually, you will have the following compilation error:

/home/raul/android-ndk-r10/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: /home/raul/my_project/jni/../libs/system/libcrypto.so: error: undefined reference to '__strlen_chk'
/home/raul/android_development/android-ndk-r10/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: /home/raul/my_project/jni/../libs/system/libcrypto.so: error: undefined reference to '__memcpy_chk'
/home/raul/android_development/android-ndk-r10/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: /home/raul/my_project/jni/../libs/system/libcrypto.so: error: undefined reference to '__memset_chk'
/home/raul/android_development/android-ndk-r10/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: 
/home/raul/my_project/jni/../libs/system/libcrypto.so: error: undefined reference to '__strchr_chk'
/home/raul/android_development/android-ndk-r10/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-
x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: /home/raul/my_project/jni/../libs/system/libcrypto.so: error: undefined reference to '__strcat_chk'

That's easy to solve. Here comes the trick: you have to replace NDK's libraries by those ones from the device. NDK contains several libraries for distinct platforms, as we can see from the path NDK_FOLDER/platforms:

@desktop:$ ls NDK_FOLDER/platforms
android-12  android-13  android-14  android-15  android-16  android-17  
android-18  android-19  android-3   android-4   android-5   android-8  android-9

 Considering that your application is using NDK for platform android-17 (you can define that in file Application.mk), replace the main libraries of folder NDK_PATH/platforms/android-17

@desktop:$ ls NDK_FOLDER/platforms/android-17/arch-arm/usr/lib
crtbegin_dynamic.o  crtend_android.o  libc.a    libEGL.so       libjnigraphics.so libm_hard.a         libOpenSLES.so    libthread_db.so
crtbegin_so.o       crtend_so.o       libc.so   libGLESv1_CM.so  liblog.so       libm.so             libstdc++.a       libz.so

crtbegin_static.o   libandroid.so     libdl.so  libGLESv2.so     libm.a           libOpenMAXAL.so     libstdc++.so

So, push the libraries /system/lib/libc.so, and /system/lib/libstdc++.so from the device to the folder NDK_FOLDER/platforms/android-17/arch-arm/usr/lib. After that, compile the application again and voilĂ  -- all problems solved :-) 


Monday, July 28, 2014

Android image/kernel building/flashing - A *VERY* short guide :-)

This week, I had to go through the process of Android OS/Kernel building/installation. And it was a lot much better and 6 months ago (maybe, because I built it for a device and not for the emulator?). I compiled the images in Ubuntu 12.04 and I used a Samsung Galaxy Nexus device (maguro with tuna as kernel). Therefore, I decided to summarize the steps that I took. This mini-tutorial is a lot shorter and simpler (and really works!!).

Update (13/07.2015): info for arm64 => http://seandroid.bitbucket.org/BuildingKernels.html

1. Android OS

1.0 Setting up the building environment

Check this instructions (here and here) to set up the basic environment and download the code. I used the branch [android-4.3_r1.1].

1.1 Compiling the Android OS

a. Download and unpack the manufacturer drivers from this link. They have to be unpacked into the directory [android_source_code]/vendors -- but don't worry, as the .zip files contain a script that does all the work for you.

b. Once the drivers are in the proper place, run the following commands:

  @desktop:$ cd [android_source_code]
  @desktop:$ make clobber
  @desktop:$ lunch full_maguro-userdebug
  @desktop:$ make -j4

It takes a long time to compile the image.

After these steps, the Android OS is ready.

1.2 Flashing the device with the new Android OS

Now, you need two tools from the Android SDK: adb and fastboot. These tools are located in the folder [androis_sdk]/platform-tools.

a. Reboot the device in the bootloader mode -- hold VolumeDown and VolumeUp and then press the PowerUp button.

b. Connect the USB cable.

c. Run the following commands:

  @desktop:$ export PATH=$PATH:[android_sdk]/platform-tools
  @desktop:$ cd [android_source_code]
  @desktop:$ sudo fastboot format cache
  @desktop:$ sudo fastboot format userdata
  @desktop:$ sudo ANDROID_PRODUCT_OUT=[android_source_code]/out/target/product/maguro/ fastboot -w flashall

After these steps, reboot the device. A clean installation will take place. To check the new version of you device, go to "Settings" - - > "About Phone" and check "Model number": now, it should be "AOSP on Maguro" (check attached image)



2. Android Kernel

Ok. Now, we have the AOSP in place and we need to compile a new kernel. But why do you need to compile and install a new kernel? Oh, well, let's say that you want to apply some patches or that you need to change the kernel to enable Linux module support (the default Android Linux Kernel does not support modules).

2.0 Setting up the building environment

If you have built the Android OS before, you don't need anything special for the kernel building. I used the official code from https://android.googlesource.com/kernel/omap.git, branch android-omap-tuna-3.0-jb-mr2.

2.1 Compiling the Kernel

First, you need to set some variables that are important for the building process (ARCH and CROSS_COMPILE):

  @desktop:$ export ARCH=arm
  @desktop:$ export CROSS_COMPILE=[android_source_code]/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/bin/arm-eabi-

Now, you have to generate a .config which contains all the options for the kernel building. By running the following command you generate a basic .config file for Android.

  @desktop:$ cd [android_kernel_code]
  @desktio:$ make tuna_defconfig

Sometimes, you need to set some specific entries of the .config to enable/disable certain features of the kernel. For this specific example, let's set the option CONFIG_MODULES to y (the entry in the .config file should be CONFIG_MODULES=y). With CONFIG_MODULES set to y, it is possible to insert/remove kernel modules. Now, let's build the kernel

  @desktop:$ cd [android_kernel_code]
  @desktop:$ make

(it takes some time to compile the kernel)

2.2 Preparing the kernel for installation

The kernel image is almost ready: it's still necessary to wrap it up properly to flash it into the device. The Android source code contains scripts that do the work for us. Consider that the image was generated at [android_kernel_code]/arch/arm/boot/zImage.

  @desktop:$ cd [android_source_code]
  @desktop:$ export TARGET_PREBUILT_KERNEL= [android_kernel_code]/arch/arm/boot/zImage
  @desktop:$ make bootimage

At the end, a custom image is ready for installation at [android_source_code]/out/target/product/maguro/boot.img

2.3 Flashing the device with the new Kernel
 
Now, everything is in place and we can finally flash our kernel image. To do so:

a. You need to boot the device in bootloader mode (hold VolumeDown and VolumeUp and then press the PowerUp button)

b. Connect the USB cable

c. Run the following commands

  @desktop:$ cd [android_source_code]
  @desktop:$ sudo ANDROID_PRODUCT_OUT=[android_source_code]/out/target/product/maguro/ fastboot flash boot [android_source_code]/out/target/product/maguro/boot.img

After these steps, reboot the device. A clean installation will take place. To check the new version of you kernel, go to "Settings" - - > "About Phone" and check "Kernel version": you will see a different name for you kernel image (as for the previuos image).