MASTG-KNOW-0008: Debugging Information and Debug Symbols
On Android, native libraries are usually developed in C or C++ with the NDK and compiled into ELF shared objects with a .so extension, which reside in the lib/
directory of the APK. These libraries often expose functionality to be used from Dalvik through the Java Native Interface (JNI). Debug symbols in these binaries provide details like function names, variable names, and source file mappings, which are useful for reverse engineering, debugging, and security analysis.
When compiling and linking programs, symbols represent functions or variables. In ELF (Executable and Linkable Format) files, symbols have different roles:
- Local symbols: Only visible inside the file where they're defined. Used internally. Not accessible from other files.
- Global symbols: Visible to other files. Used to share functions or variables across different object files.
- Weak symbols: Like global symbols, but lower priority. A strong (non-weak) symbol overrides a weak one if both exist.
In production builds, debug information must be stripped to reduce binary size and limit information disclosure. However, debug or internal builds may retain symbols either within the binary or in separate companion files.
Symbol visibility is often mishandled, leading to unintended external exposure of symbols and requiring manual inspection.
Symbol Tables and DWARF Sections¶
The ELF format defines which sections must be used to store symbol information:
.symtab
: The full symbol table used at link time, often removed in production binaries (DT_SYMTAB
dtag)..dynsym
: The dynamic symbol table, used for runtime linking. It is always present in shared objects.
DWARF is the standard debug format used in ELF binaries (but it is also used in other UNIX-based systems like for MACH-O binaries in the Apple ecosystem). Key sections include:
.debug_info
: Contains the main debugging information, including types, function definitions, and scopes..debug_line
: Maps machine code to source code line numbers..debug_str
: Stores strings used by DWARF entries..debug_loc
,.debug_ranges
,.debug_abbrev
, etc.: Support detailed debug metadata.
Additionally, some toolchains use zlib compression for DWARF data to reduce binary size (for example clang and gcc support this using the -gz
option). These sections are typically named using a .z
prefix (e.g.,.zdebug_info
, .zdebug_line
, .zdebug_str
, etc.) and contain the same information as their uncompressed counterparts. Some analysis tools that do not support these may incorrectly report the binary as stripped.
To check for the presence of these sections in a binary, you can use objdump - iOS (with the option -x
) or radare2 for Android (iS
command) and other tools like readelf
.
For example, using radare2:
[0x0003e360]> iS~debug,symtab,SYMTAB
23 0x000c418c 0x60 0x00000000 0x60 ---- 0x0 PROGBITS .debug_aranges
24 0x000c41ec 0x14d85c 0x00000000 0x14d85c ---- 0x0 PROGBITS .debug_info
25 0x00211a48 0xa14f 0x00000000 0xa14f ---- 0x0 PROGBITS .debug_abbrev
26 0x0021bb97 0x5d6a3 0x00000000 0x5d6a3 ---- 0x0 PROGBITS .debug_line
27 0x0027923a 0x7c26a 0x00000000 0x7c26a ---- 0x30 PROGBITS .debug_str
28 0x002f54a4 0x172883 0x00000000 0x172883 ---- 0x0 PROGBITS .debug_loc
29 0x00467d27 0x20 0x00000000 0x20 ---- 0x0 PROGBITS .debug_macinfo
30 0x00467d47 0x602d0 0x00000000 0x602d0 ---- 0x0 PROGBITS .debug_ranges
32 0x004c8018 0x27510 0x00000000 0x27510 ---- 0x0 SYMTAB .symtab
IMPORTANT: The presence of these sections doesn't necessarily indicate that the binary hasn't been stripped. Some toolchains may retain these sections even in stripped binaries, but they are often empty or contain minimal information. Ultimately, what matters is whether the symbols themselves are still present. See Obtaining Debugging Information and Symbols for more details on how to extract and analyze debugging symbols.
External Debug Symbol Files¶
The Android Developers documentation explains that native libraries in release builds are stripped by default. To enable symbolicated native crash reports, you must generate a separate debug symbols file—typically located at <variant>/native-debug-symbols.zip
—and upload it to the Google Play Console. This ZIP archive contains full unstripped .so
files with embedded DWARF debug information. The DWARF data is not split into separate files (such as .dwo
) but remains inside each .so
.
This symbolication process is analogous to uploading a
mapping.txt
file to deobfuscate stack traces for ProGuard or R8 obfuscated Java/Kotlin code.
In contrast, iOS uses an approach similar in spirit to split DWARF, familiar from Linux toolchains. According to the Apple Developer documentation, enabling the DWARF with dSYM File
option in Xcode generates separate debug symbol files (.dSYM
) for release builds. These can be uploaded to Apple's symbol servers for crash report symbolication.