How are shared libraries packed into apk? - android

I'm working on some script for custom build system and i need to pack some .so files in android apk (aars with native libs are not yet supported).
I've invoked ./gradlew assembleDebug --debug for the apk project that depends on aar with native libraries (.so) to get full log but did not find any .so files paths passed to aapt or any other android build tool (though i've seen .so stripping commands with ndk strip tool).
How is it done in details? Does aapt tool do it? Are there any aapt arguments for this or they should be just stored on some specific paths (eg. libs).

Yes, aapt via an invocation like aapt add -v MyApp.apk lib/armeabi/libMyLib.so.
Use aapt --list MyApp.apk to ensure that it is in the apk with that directory structure (a lib folder, an armeabi subfolder, and MyApp.apk within that).
After installing the app, look in /data/app/com.Company/android/MyApp-X/lib/arm/ for libMyLib.so to ensure it got copied.
If problems, logcat output is actually really useful, telling you the path tried, the function tried, etc.
Obviously, replace the app name, company name, and armeabi architecture to fit your projects' parameters.

Related

Checking preprocessor directives with Android Studio Gradle Build, NDK and CMake

I'm passing in preprocessor directives via CMakeLists.txt for the build of a native android library using android NDK.
add_definitions(-DMY_DIRECTIVE=1)
It would be great to double check that those preprocessor directives are actually finding their way into the calls to the compiler (llvm ?)
But the gradle build output doesn't seem to include the calls to the compiler, I just get:
Building C object CMakeFiles/my_project.dir/home/me/projects/my_proj/src/my_native.c.o
Is there a means to make the gradle output more verbose such that I can see the actual compiler calls and check those preprocessor directives are present?
The answer is to understand that Gradle utilises CMake to build the android NDK component (shared library) of an android project, and CMake utilises Ninja as a build system to handle the calls to the compiler. The compiler used by android NDK now defaults to LLVM->Clang.
So in order to actually see the Clang calls you have to find the build.ninja files for each target of your android project.
In my case I am only building for an armeabi-v7a target architecture. Therefore the relevant build.ninja files are found in:
/home/me/projects/my_proj/app/.externalNativeBuild/cmake/debug/armeabi-v7a
/home/me/projects/my_proj/app/.externalNativeBuild/cmake/release/armeabi-v7a
cd to either directory and run:
ninja -v
i.e. the -v option is the key to see all the calls to the clang compiler that the native build (android NDK) part of your android project generates.
Note if you have installed CMake via the android package manager, you may find that ninja is not installed in a location that is on your PATH. For me the ninja binary is located as follows:
~/Android/Sdk/cmake/3.6.3155560/bin/ninja
(same directory as cmake binary)
Therefore for me to see all the clang compiler calls for my android project's debug armeabi-v7a build I have to run:
cd /home/me/projects/my_proj/app/.externalNativeBuild/cmake/debug/armeabi-v7a
~/Android/Sdk/cmake/3.6.3155560/bin/ninja -v
Note if ninja tells you ninja: no work to do.
Then run:
~/Android/Sdk/cmake/3.6.3155560/bin/ninja clean
Relevant ninja documentation is -> https://ninja-build.org/manual.html#_extra_tools

How to run ranlib on an archive built through Android.mk?

This has come up on a couple of libraries I work with regularly. See, for example:
Error SSL archive symbol table (run ranlib)
no archive symbol table (run ranlib) while building libcryptopp.a through ndk-build
In the questions, the users created an Android.mk for the OpenSSL and Crypto++ libraries. The pain point seems to be users adding the Android.mk wrapper to the sources.
Outside of Android, each project is Makefile based, each project builds a static archive, and each project builds a shared object based on the static archive. Each project also runs ranlib on the static archive. Crypto++ is especially sensitive to the need for ranlib because its a C++ library and One Definition Rule violations lead to undefined behavior.
When using Android.mk to build a static archive, how do we run ranlib on an archive through Android.mk?
I was running into similar issues and found a command on this website which fixed it for me
# The -E is important. Root needs some of the user's environment
$ sudo -E make install CC=$ANDROID_TOOLCHAIN/arm-linux-androideabi-gcc RANLIB=$ANDROID_TOOLCHAIN/arm-linux-androideabi-ranlib

Android ndk-build not generating gdb.setup file

I am currently trying to debug native code in Android via ndk-gdb but I am having some troubles.
Even if I start a very simple project (let's say for example a default cocos2d-x v3 project) and run
ndk-build NDK_DEBUG=1
I end up with the following folder structure inside my android project
...
libs/
armeabi/
libcocos2dcpp.so
...
instead of the expected:
...
libs/
armeabi/
gdb.setup
gdbserver
libcocos2dcpp.so
...
In order to use ndk-gdb I need those two gdb files.
I am using cocos version 3.2 and Android NDK version r9d.
Isn't NDK_DEBUG=1enough for the gdb files to be generated? I have also tried withandroid:debuggable="true" inside my manifest file but it didn't work.
Edit
After running the command suggested by Digit I found a very suspicious line
Android NDK: Application 'local' forced *not* debuggable through NDK_DEBUG
when running the command ndk-build NDK_LOG=1 NDK_DEBUG=1
BUT
if I change to ndk-build NDK_LOG=1 NDK_DEBUG=true I get
Android NDK: Application 'local' forced debuggable through NDK_DEBUG
So it is ok now, really weird though how =1 is not considered true.
Can you paste the output of 'ndk-build NDK_LOG=1 NDK_DEBUG=1', this should contain more information about what ndk-build is doing, and is likely to provide an explanation.

Android - APK disassembler

I'm trying to get the assembler code executed for an APK. It can be extracted dynamically like gdb dissas or statically like objdump.
I tried with gdb without any exit.
APKs are actually just archives, similar to a JAR.
Within an APK you will find a number of files with application metadata (such as a manifest and certificate information), along with resource files and source files. The source code within an APK are also not Assembly files; they are DEX files for the Dalvik VM.
You will not find Assembly in an APK.
Solved using GDB.
Emulator:
gdbserver :5039 --attach pid
PC:
adb forward tcp:5039 tcp:5039
gdb
=> target remote 127.0.0.1:5039
Now in gdb we can see asm with "layout asm" or EIB ass using "x/i 0xEIB"

Compiling against C++ standard libraries on the android toolchain

I have a really simple helloworld.cpp program
#include <iostream>
using namespace std;
int main ()
{
cout << "Hello World!";
return 0;
}
And I'm trying to compile it for android x86 with the cross-compiler from the toolchain:
/Users/me/android-ndk-r8/toolchains/x86-4.4.3/prebuilt/darwin-x86/bin/i686-android-linux-g++ helloworld.cpp -L "/Users/me/android-ndk-r8/sources/cxx-stl/stlport/libs/x86/" -lstlport_static
However, I get errors:
helloworld.cpp:2:20: error: iostream: No such file or directory
Any idea why?
Check the documentation.html file included with the NDK, under "Standalone Toolchain". It says that if you invoke the compiler in this way you won't be able to "use any C++ STL". However it is possible, as the documentation explains, if you first create a "customized" toolchain installation, using something like the following command:
$NDK/build/tools/make-standalone-toolchain.sh --platform=android-8 --install-dir=/tmp/my-android-toolchain --arch=x86
where $NDK is the path to your NDK directory. Note the --arch=x86 which means that the toolchain is prepared specifically for the x86 Android. This prepares what you need in one directory, including the STL headers and folders. You should then be able to use -lstdc++ to link against the STL (static version), i.e. something like:
/tmp/my-android-toolchain/bin/i686-android-linux-g++ helloworld.cpp -lstdc++
For a more complete explanation, please see the NDK documentation.
The NDK documentation is not entirely accurate, at least not currently. In fact, it states when using the prebuilt toolchain "You won't be able to use any C++ STL (either STLport or the GNU libstdc++) with it.", but this is out of date. I created a small hello world program using the include with the same error. It can be solved without creating your own toolchain though, which is nice if you don't want to have to add one more step to your configuration process and allows you to always use the latest SDK platform without creating a new toolchain every time.
The NDK ships with the source code for several versions of standard C++ libraries: GAbi++, STLport, and GNU STL. Each flavor comes with prebuilt shared and static libs as well. My example below will use stlport.
To use the stand-alone toolchain at its installed location, you can do something like this:
export CXX='$NDK_ROOT/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-g++ --sysroot="$NDK_ROOT/platforms/android-19/arch-arm"'
This, for example, would set your CXX compiler to compile ARM on the OS X system using SDK platform level 19. This much you probably already knew. Also, you would want to export your CC, CPP, LD, AR, and RANLIB if you use it. I also personally create an envar for READELF.
To add support for C++ libs, you could do something like follows:
$CXX helloworld.cpp -I$NDK_ROOT/sources/cxx-stl/stlport/stlport -L$NDK_ROOT/sources/cxx-stl/stlport/libs/armeabi -lstlport_shared
Note that this will link the libstlport_shared.so which will now be required at runtime, so you may need to add a relative path to the command above to support that, depending on your APK structure. If you want to just test this simple case, you can just run this on the emulator as follows:
adb push a.out /data
adb push $NDK_ROOT/sources/cxx-stl/stlport/libs/armeabi/libstlport_shared.so /data
adb shell
# su
# cd /data
# chmod 777 a.out
# ./a.out
To get rid of the headache of dealing with shared library paths, you can also statically link the C++ library in by changing "-lstlport_shared" to "-lstlport_static". There are some consequences of doing this, as explained in the NDK Dev Guide. The biggest issue is due to having the static library linked in multiple places, causing:
- memory allocated in one library, and freed in the other would leak or even corrupt the heap.
- exceptions raised in libfoo.so cannot be caught in libbar.so (and may simply crash the program).
- the buffering of std::cout not working properly
A useful tool is also included to see what dependencies your program has, the readelf tool.
To see what other shared libraries your program requires, you can run the following:
$NDK_ROOT/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-readelf -a a.out | grep NEEDED
Other cool standard tools:
addr2line - convert a stack trace address to a line of code
nm - Displays the symbol table
objdump - displays info on an object
i call one of the functions from gnustl after it runs fine from prebuilt aosp arm-linux-androideabi-gcc --std=c++11 and after crashing error i cant get a backtrace from logs or reason, my hope is turning to crossbuilt qemu-linux-user to run host compiled i686 binary on the arm, difficulty is interacting with crosshost libs aapt from adt always crashes on any other than platform specific libs, unless kernel module packaged update is possible...

Categories

Resources