Bionic (software)
Bionic is an implementation of the standard C library, developed by Google for its Android operating system. It differs from the GNU C Library in being designed for devices with less memory and processor power than a typical Linux system. It is also released under a BSD license, rather than the GNU Lesser General Public License used for glibc. This difference was important in the early days of Android, when static linking was common, and is still helpful in introducing Android to software companies used to proprietary operating systems, who can be wary of the LGPL, and unclear about the differences between it and the full GPL.
Bionic is a C library for use with the Linux kernel, and provides libc, libdl, libm, and libpthread. This differs from the BSD C libraries which require a BSD kernel.
Original goals
The original publicly stated goals for Bionic were the following:- BSD-licensed: Google wanted to isolate Android applications from the effect of copyleft licenses to create a proprietary user-space and application ecosystem, but:
- * Android is based on the Linux kernel, which is subject to the copyleft GNU General Public License version 2.
- * The most widespread standard C library for the Linux kernel is the GNU C Library, which is subject to the GNU Lesser General Public License, also a copyleft license. In contrast to the GPL, the LGPL explicitly allows for dynamic linking but it does not allow static linking of proprietary software without providing source code or linkable object files.
- * The permissive BSD license is a non-copyleft license that is compatible in both directions. A BSD-licensed glibc substitute could act as an isolation layer between the copyleft core and the non-copyleft applications, and was therefore chosen by Google for its Bionic as a glibc substitute.
- Small size: Bionic was much smaller than the GNU C Library; more importantly its memory requirements were much lower.
- Speed: Bionic was designed for CPUs at relatively low clock frequencies.
Supported architectures
Components
Some parts of the libc source, such as stdio, are from the BSDs, whereas others, such as the pthread implementation, were written from scratch.The dynamic memory allocator implementation has changed over time. Before Lollipop there was a single native memory allocator, Doug Lea's dlmalloc. For Lollipop and Marshmallow there were two implementations: dlmalloc and jemalloc. jemalloc gives much higher performance than dlmalloc, but at the cost of extra memory required for bookkeeping. Most devices used jemalloc but low-memory devices still used dlmalloc. For Nougat and later releases, all devices uses jemalloc. Low-memory devices use a "svelte" configuration of jemalloc that disables the tcache to nearly match the lower memory overhead of dlmalloc while keeping most of the speed of jemalloc.
Some 64-bit devices, like the Nexus 9, are effectively low-memory devices because of the extra space requirements of 64-bit pointers and hosting of two zygotes.
The libm source is largely FreeBSD's, but with optimized assembler contributed by the various SoC vendors.
The dynamic linker were written from scratch.
Bionic doesn't include libthread_db, but the NDK does. The Android platform includes a statically-linked gdbserver, so that developers can use the latest gdb even on old devices.
There is no separate libpthread, libresolv, or librt on Android the functionality is all in libc. For libpthread, there's no attempt to optimize for the single-threaded case because apps are in a multi-threaded environment even before the first instruction of third-party code is ever run.
The Android platform uses libc++ for the C++ standard library. The NDK historically offered stlport and GNU libstdc++, but those were removed as of NDK 18. Note that if any native code in an Android app uses C++, all the C++ must use the same STL. The STL is not provided by the Android OS, and must be bundled with each app.
Differences from POSIX
Although bionic aims to implement all of C11 and POSIX, there are still about 70 POSIX functions missing from libc. There are also POSIX functions such as the endpwent/getpwent/setpwent family that are inapplicable to Android because it lacks a passwd database. As of Oreo, libm is complete.Some functions deliberately do not conform to the POSIX or C standards for security reasons, such as printf which does not support the
%n
format string.Many of the most-used GNU extensions are implemented in bionic, as are various BSD extensions.
Relationship to the NDK
Platform code uses bionic directly, but third-party developers use the Android Native Development Kit. Many third-party developers still target older OS releases, which contributes to a widespread belief that bionic lacks many features. Gingerbread exported 803 functions from libc but Oreo exports 1278.Historically the NDK and the platform diverged, but NDK r11 and later have replaced NDK forks with their current platform equivalents. This work initially focused on the GCC and clang compilers.
Prior to NDK r14, when "unified" headers were first offered on an opt-in basis, the NDK had forked copies of the platform headers for different API levels. This meant that header-only fixes weren't available to most NDK users because they'd be targeting an older API level, but platform fixes were only going in to the current platform headers. In the Oreo development period the platform headers were annotated with API level information so that the same set of headers can be used for all API levels, with only those functions available at the developer's targeted API level being visible. These are the so-called "unified" headers, and have been the default since NDK r15.
Prior to NDK r16, the NDK linked a library called libandroid_support.a to code using libc++. This provided functions required by libc++ that weren't in old OS releases. This wasn't the same code used by the platform and introduced numerous bugs. In NDK r16 libandroid_support.a still exists, but is now built directly from platform source.
Fortify source
As of Android Jelly Bean MR1, bionic supports similar functionality to glibc's_FORTIFY_SOURCE
, which is a feature where unsafe string and memory functions , strcat
, and memcpy
) include checks for buffer overruns. These checks are performed at compile time if the buffer sizes can be determined at compile time, or run-time otherwise. Because fortify relies on runtime support from libc, its portability to older Android releases is limited. The platform itself is built with _FORTIFY_SOURCE
enabled.Historically, one of the shortcomings of fortify has been that it's closely tied with gcc, which makes it very difficult to support well in other compilers, like Clang. This meant that when Android swapped to Clang as its default compiler, bionic's fortify implementation became substantially less useful. In Android Oreo, bionic's fortify was overhauled with Clang in mind, resulting in fortify on Clang providing an experience on par with fortify on gcc. Since this overhaul, some checks were added above and beyond glibc's to catch code that—while not necessarily causing undefined behavior—is obviously incorrect. Because this new implementation requires no more libc support than the prior one, the clang-specific enhancements are available to applications targeting versions of Android before Oreo.