This article shows why your breakpoints in native Android code might fail and how to fix them.
First of all, let's get an overview of the structure of a typical Android
app containing native code:
The app code is stored inside a .apk file that is
essentially a Zip archive containing the classes.dex file (with
all the Java code) and one or more native libraries in the lib\<EABI
name> subdirectory. The typical lifecycle of an application with
native code is the following:
- The Android OS loads the Java code and starts executing
- The Java code calls
System.loadLibrary() to load a native library.
- The Java code starts calling functions from the native
Each native library exists in two versions:
- A "full" version debugging information that
associates code addresses inside the library with source
files and lines. This version resides in obj\local\armeabi
under your project directory.
- A "stripped" version without the debugging information
that resides in libs\armeabi and gets
packaged into the APK file.
While the Android device runs the smaller "stripped" version
of the library, the GDB debugger needs the larger "non-stripped"
version to map
source files into addresses and vice versa:
When you set a breakpoint on a given line of a source file,
the GDB debugger needs to perform certain computations before
the breakpoint can be physically created and starts triggering.
Assume libNative1 is loaded at address 0x10000
and the user is setting a breakpoint in line 24 of c:\main.cpp.
GDB will do the following before it can set a breakpoint:
- GDB starts searching all loaded libraries for the file
called "c:\main.cpp". In this example the Android OS only
reports the libNative1.so library.
- GDB looks inside obj\local\armeabi for
a file called libNative1.so which is the
"non-stripped" version of the library containing the debug
information. GDB reads the symbol table from it and finds
c:\main.cpp inside it.
- Based on the symbol table GDB computes that line 24
corresponds to offset +0x2. It adds 0x2 to the load address
of libNative1.so (0x10000)
and sets the breakpoint at 0x10002.
If any of the three steps fail, the breakpoint will not be
created, or will be created wrongly and will never be hit. The
next section explains how to diagnose and fix the related
Diagnosing the problems
This section provides detailed instructions how to check for
the most common problems with breakpoints caused by non-standard
configurations or missing files:
A. Ensure that the library is loaded
First thing to do is to determine if the native library has
been actually loaded and if GDB knows about it. It is done by
running the "info shared" command in GDB:
The output from the info shared command
should include three important lines:
- Loaded symbols for "linker"
- Loaded symbols for "libc.so"
- Loaded symbols for all native libraries you want to
Here's an example of those lines:
From To Syms Read Shared Object Library
0xb0001000 0xb00069d0 Yes C:/.../AndroidBinaryCache/.../linker
0x40073300 0x400a12fc Yes C:/.../AndroidBinaryCache/.../libc.so
0x5148fccc 0x51491198 Yes E:/.../obj/local/armeabi/libMyAndroidApp.so
If some of the libraries are not present, you can force
GDB to manually reload the symbols by running the following command in GDB:
If your .so library (e.g., libMyAndroidApp.so)
is listed, but the symbols are not loaded ("Syms read" states
"no"), GDB was not able to find a non-stripped version of the
library. To fix it please run the "show solib-search-path"
command in GDB:
The obj/local/armeabi directory of your
project should be present among the reported directories and it
should contain the .so file with symbols. If
not, copy the correct .so file into a directory
listed here and rerun the "sharedlibrary"
You can alternatively force GDB to search additional
directories for the .so file using the
set solib-search-path command.
If your .so library is not present in the list, it has not
been loaded by the Java code yet. Please ensure that
System.loadLibrary() is called from your Java code and
that it succeeds without any Java exceptions.
B. Ensure that you are using correct file paths
A common cause of many breakpoint problems is a configuration
when the directories using for building and debugging your
project are different. E.g., if the library was compiled from
c:\projects and then the sources were moved to
d:\projects, setting a breakpoint in
d:\projects\main.cpp will fail, as GDB will not accept
c:\projects\main.cpp as a substitute.
Those problems can be diagnosed by looking into the source
file lists and comparing them with the file used to set a
breakpoint. First of all, remove your breakpoint, try setting your
breakpoint again and watch what GDB command is issued by your IDE:
The -break-insert command used to set a breakpoint will specify a path to your source file.
Run the "info sources" command to see the source files
Copy the output of the command to the clipboard (Ctrl-A,
Ctrl-C), paste it into a text editor and search for the source
file you are trying to debug (e.g., MyAndroidApp.c):
If the file is not present in the list, you have loaded a
wrong version of the .so file (see previous
section). If the file is present, but the path is different from
the one used in -break-insert command, you have
found the cause of your problem. Rebuild your app using the new
path, or move the file back to an old location so that the path used for creating breakpoints matches the path reported by the "info
Note that GDB requires the file path to be EXACTLY the same
as reported by "info sources", including double slash before "jni".
C. Recheck file versions and do a clean build
If your breakpoints are set, but never hit, there is probably
a mismatch between the .so file version loaded
into the Android application and the .so file
version used by GDB to compute addresses from source lines. The
most common cause for it is the bug in the Android loader that
loads the armeabi library instead of the
armeabi-v7a version. If you suspect this to
happen, change your build configuration to build either
armeabi, or armeabi-v7a platform, but
not both at the same time and rebuild your application.