Advanced AOSP Subsystems
3 min read

libc (Bionic)

Bionic: Android's C Library

Bionic is the standard C library (libc), math library (libm), and dynamic linker (linker) for the Android operating system. Unlike GNU C Library (glibc) used in most desktop Linux distributions, Bionic is designed specifically for the constraints and security requirements of mobile devices.

Bionic libc API Surface

Bionic provides the fundamental C POSIX APIs required by native code, including file I/O (open, read, write), string manipulation (strcpy, strlen), and basic system calls.

Key differences from glibc include:

  • Size: Bionic is significantly smaller, omitting many rarely used POSIX features and legacy compatibility layers.
  • Speed: It is optimized for mobile CPU architectures (ARM, ARM64).
  • Security: Bionic incorporates numerous security mitigations, such as fortified string functions (_FORTIFY_SOURCE) and strict dynamic linking rules.
  • Licensing: Bionic is BSD-licensed, preventing the GPL "viral" effect from impacting proprietary Android apps.

Threading: pthread Implementation in Bionic

Bionic provides the POSIX threads (pthread) implementation for Android. Every thread in an Android process (including Dalvik/ART managed threads) is fundamentally a Linux thread backed by Bionic's pthread_create.

#include <pthread.h>
#include <android/log.h>

void* thread_function(void* arg) {
    __android_log_print(ANDROID_LOG_INFO, "Bionic", "Running in a new thread!");
    return NULL;
}

void start_thread() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    pthread_join(thread, NULL);
}

Bionic's pthread implementation interacts closely with the kernel's clone syscall and manages thread-local storage (TLS), which is essential for storing per-thread data like the JNI JNIEnv pointer.

Bionic's Malloc Implementation

Memory allocation (malloc, free, calloc) is a critical function of the C library. Historically, Bionic used several different allocators:

  1. dlmalloc: An older, general-purpose allocator.
  2. jemalloc: Introduced in Android 5.0 (Lollipop) for improved multi-threading performance and reduced fragmentation.

These allocators manage the native heap, servicing requests from C/C++ code. Unlike the Java heap, memory allocated here must be explicitly freed, or the process will leak memory and eventually be killed by the kernel's Low Memory Killer (LMK).

Scudo Allocator: Hardened Malloc

Starting with Android 11, Bionic switched to Scudo as the default heap allocator. Scudo is a dynamically hardened allocator designed by the LLVM project.

Scudo prioritizes security over absolute maximum performance. It is designed to detect and mitigate common memory safety vulnerabilities:

  • Heap Buffer Overflows: Detects writes past the end of an allocated chunk.
  • Use-After-Free (UAF): Prevents reuse of recently freed memory blocks, reducing the window for UAF exploits.
  • Double Free: Detects attempts to free the same pointer twice.

When Scudo detects a memory violation, it deliberately crashes the process, generating a tombstone. This fail-fast behavior transforms potential security exploits into simple denial-of-service crashes, significantly hardening the Android OS against native layer attacks.