The Android Open Source Project | 9f65adf | 2009-02-10 15:43:56 -0800 | [diff] [blame^] | 1 | Bionic C Library Overview: |
| 2 | ========================== |
| 3 | |
| 4 | Introduction: |
| 5 | |
| 6 | Core Philosophy: |
| 7 | |
| 8 | The core idea behind Bionic's design is: KEEP IT REALLY SIMPLE. |
| 9 | |
| 10 | This implies that the C library should only provide lightweight wrappers around kernel |
| 11 | facilities and not try to be too smart to deal with edge cases. |
| 12 | |
| 13 | The name "Bionic" comes from the fact that it is part-BSD and part-Linux: its source |
| 14 | code consists in a mix of BSD C library pieces with custom Linux-specific bits used |
| 15 | to deal with threads, processes, signals and a few others things. |
| 16 | |
| 17 | All original BSD pieces carry the BSD copyright disclaimer. Bionic-specific bits |
| 18 | carry the Android Open Source Project copyright disclaimer. And everything is released |
| 19 | under the BSD license. |
| 20 | |
| 21 | Architectures: |
| 22 | |
| 23 | Bionic currently supports the ARM and x86 instruction sets. In theory, it should be |
| 24 | possible to support more, but this may require a little work (e.g. adding system |
| 25 | call IDs to SYSCALLS.TXT, described below, or modifying the dynamic linker). |
| 26 | |
| 27 | The ARM-specific code is under arch-arm/ and the x86-specific one is under arch-x86/ |
| 28 | |
| 29 | Note that the x86 version is only meant to run on an x86 Android device. We make |
| 30 | absolutely no claim that you could build and use Bionic on a stock x86 Linux |
| 31 | distribution (though that would be cool, so patches are welcomed :-)) |
| 32 | |
| 33 | Syscall stubs: |
| 34 | |
| 35 | Each system call function is implemented by a tiny assembler source fragment |
| 36 | (called a "syscall stub"), which is generated automatically by tools/gensyscalls.py |
| 37 | which reads the SYSCALLS.TXT file for input. |
| 38 | |
| 39 | SYSCALLS.TXT contains the list of all syscall stubs to generate, along with |
| 40 | the corresponding syscall numeric identifier (which may differ between ARM and x86), |
| 41 | and its signature |
| 42 | |
| 43 | If you modify this file, you may want to use tools/checksyscalls.py which checks |
| 44 | its content against official Linux kernel header files, and will report errors when |
| 45 | invalid syscall ids are used. |
| 46 | |
| 47 | Sometimes, the C library function is really a wrapper that calls the corresponding |
| 48 | syscall with another name. For example, the exit() function is provided by the C |
| 49 | library and calls the _exit() syscall stub. |
| 50 | |
| 51 | See SYSCALLS.TXT for documentation and details. |
| 52 | |
| 53 | |
| 54 | time_t: |
| 55 | |
| 56 | time_t is 32-bit as defined by the kernel on 32-bit CPUs. A 64-bit version would |
| 57 | be preferrable to avoid the Y2038 bug, but the kernel maintainers consider that |
| 58 | this is not needed at the moment. |
| 59 | |
| 60 | Instead, Bionic provides a <time64.h> header that defines a time64_t type, and |
| 61 | related functions like mktime64(), localtime64(), etc... |
| 62 | |
| 63 | |
| 64 | Timezone management: |
| 65 | |
| 66 | The name of the current timezone is taken from the TZ environment variable, if defined. |
| 67 | Otherwise, the system property named 'persist.sys.timezone' is checked instead. |
| 68 | |
| 69 | The zoneinfo timezone database and index files are located under directory |
| 70 | /system/usr/share/zoneinfo, instead of the more Posix path of /usr/share/zoneinfo |
| 71 | |
| 72 | |
| 73 | off_t: |
| 74 | |
| 75 | For similar reasons, off_t is 32-bit. We define loff_t as the 64-bit variant due |
| 76 | to BSD inheritance, but off64_t should be available as a typedef to ease porting of |
| 77 | current Linux-specific code. |
| 78 | |
| 79 | |
| 80 | |
| 81 | Linux kernel headers: |
| 82 | |
| 83 | Bionic comes with its own set of "clean" Linux kernel headers to allow user-space |
| 84 | code to use kernel-specific declarations (e.g. IOCTLs, structure declarations, |
| 85 | constants, etc...). They are located in: |
| 86 | |
| 87 | ./kernel/common, |
| 88 | ./kernel/arch-arm |
| 89 | ./kernel/arch-x86 |
| 90 | |
| 91 | These headers have been generated by a tool (kernel/tools/update-all.py) to only |
| 92 | include the public definitions from the original Linux kernel headers. |
| 93 | |
| 94 | If you want to know why and how this is done, read kernel/README.TXT to get |
| 95 | all the (gory) details. |
| 96 | |
| 97 | |
| 98 | PThread implementation: |
| 99 | |
| 100 | Bionic's C library comes with its own pthread implementation bundled in. This is |
| 101 | different from other historical C libraries which: |
| 102 | |
| 103 | - place it in an external library (-lpthread) |
| 104 | - play linker tricks with weak symbols at dynamic link time |
| 105 | |
| 106 | The support for real-time features (a.k.a. -lrt) is also bundled in the C library. |
| 107 | |
| 108 | The implementation is based on futexes and strives to provide *very* short code paths |
| 109 | for common operations. Notable features are the following: |
| 110 | |
| 111 | - pthread_mutex_t, pthread_cond_t are only 4 bytes each. |
| 112 | |
| 113 | - Normal, recursive and error-check mutexes are supported, and the code path |
| 114 | is heavily optimized for the normal case, which is used most of the time. |
| 115 | |
| 116 | - Process-shared mutexes and condition variables are not supported. |
| 117 | Their implementation requires far more complexity and was absolutely |
| 118 | not needed for Android (which uses other inter-process synchronization |
| 119 | capabilities). |
| 120 | |
| 121 | Note that they could be added in the future without breaking the ABI |
| 122 | by specifying more sophisticated code paths (which may make the common |
| 123 | paths slightly slower though). |
| 124 | |
| 125 | - There is currently no support for read/write locks, priority-ceiling in |
| 126 | mutexes and other more advanced features. Again, the main idea being that |
| 127 | this was not needed for Android at all but could be added in the future. |
| 128 | |
| 129 | pthread_cancel(): |
| 130 | |
| 131 | pthread_cancel() will *not* be supported in Bionic, because doing this would |
| 132 | involve making the C library significantly bigger for very little benefit. |
| 133 | |
| 134 | Consider that: |
| 135 | |
| 136 | - A proper implementation must insert pthread cancellation checks in a lot |
| 137 | of different places of the C library. And conformance is very difficult to |
| 138 | test properly. |
| 139 | |
| 140 | - A proper implementation must also clean up resources, like releasing memory, |
| 141 | or unlocking mutexes, properly if the cancellation happens in a complex |
| 142 | function (e.g. inside gethostbyname() or fprintf() + complex formatting |
| 143 | rules). This tends to slow down the path of many functions. |
| 144 | |
| 145 | - pthread cancellation cannot stop all threads: e.g. it can't do anything |
| 146 | against an infinite loop |
| 147 | |
| 148 | - pthread cancellation itself has short-comings and isn't very portable |
| 149 | (see http://advogato.org/person/slamb/diary.html?start=49 for example). |
| 150 | |
| 151 | All of this is contrary to the Bionic design goals. If your code depends on |
| 152 | thread cancellation, please consider alternatives. |
| 153 | |
| 154 | Note however that Bionic does implement pthread_cleanup_push() and pthread_cleanup_pop(), |
| 155 | which can be used to handle cleanups that happen when a thread voluntarily exits |
| 156 | through pthread_exit() or returning from its main function. |
| 157 | |
| 158 | |
| 159 | pthread_once(): |
| 160 | |
| 161 | Do not call fork() within a callback provided to pthread_once(). Doing this |
| 162 | may result in a deadlock in the child process the next time it calls pthread_once(). |
| 163 | |
| 164 | Also, you can't throw a C++ Exception from the callback (see C++ Exception Support |
| 165 | below). |
| 166 | |
| 167 | The current implementation of pthread_once() lacks the necessary support of |
| 168 | multi-core-safe double-checked-locking (read and write barriers). |
| 169 | |
| 170 | |
| 171 | Thread-specific data |
| 172 | |
| 173 | The thread-specific storage only provides for a bit less than 64 pthread_key_t |
| 174 | objects to each process. The implementation provides 64 real slots but also |
| 175 | uses about 5 of them (exact number may depend on implementation) for its |
| 176 | own use (e.g. two slots are pre-allocated by the C library to speed-up the |
| 177 | Android OpenGL sub-system). |
| 178 | |
| 179 | Note that Posix mandates a minimum of 128 slots, but we do not claim to be |
| 180 | Posix-compliant. |
| 181 | |
| 182 | Except for the main thread, the TLS area is stored at the top of the stack. See |
| 183 | comments in bionic/libc/bionic/pthread.c for details. |
| 184 | |
| 185 | At the moment, thread-local storage defined through the __thread compiler keyword |
| 186 | is not supported by the Bionic C library and dynamic linker. |
| 187 | |
| 188 | |
| 189 | Multi-core support |
| 190 | |
| 191 | At the moment, Bionic does not provide or use read/write memory barriers. |
| 192 | This means that using it on certain multi-core systems might not be supported, |
| 193 | depending on its exact CPU architecture. |
| 194 | |
| 195 | |
| 196 | Android-specific features: |
| 197 | |
| 198 | Bionic provides a small number of Android-specific features to its clients: |
| 199 | |
| 200 | - access to system properties: |
| 201 | |
| 202 | Android provides a simple shared value/key space to all processes on the |
| 203 | system. It stores a liberal number of 'properties', each of them being a |
| 204 | simple size-limited string that can be associated to a size-limited string |
| 205 | value. |
| 206 | |
| 207 | The header <sys/system_properties.h> can be used to read system properties |
| 208 | and also defines the maximum size of keys and values. |
| 209 | |
| 210 | - Android-specific user/group management: |
| 211 | |
| 212 | There is no /etc/passwd or /etc/groups in Android. By design, it is meant to |
| 213 | be used by a single handset user. On the other hand, Android uses the Linux |
| 214 | user/group management features extensively to secure process permissions, |
| 215 | like access to various filesystem directories. |
| 216 | |
| 217 | In the Android scheme, each installed application gets its own uid_t/gid_t |
| 218 | starting from 10000; lower numerical ids are reserved for system daemons. |
| 219 | |
| 220 | getpwnam() recognizes some hard-coded subsystems names (e.g. "radio") and |
| 221 | will translate them to their low-user-id values. It also recognizes "app_1234" |
| 222 | as the synthetic name of the application that was installed with uid 10000 + 1234, |
| 223 | which is 11234. getgrnam() works similarly |
| 224 | |
| 225 | getgrouplist() will always return a single group for any user name, which is |
| 226 | the one passed as an input parameter. |
| 227 | |
| 228 | getgrgid() will similarly only return a structure that contains a single-element |
| 229 | members list, corresponding to the user with the same numerical value than the |
| 230 | group. |
| 231 | |
| 232 | See bionic/libc/bionic/stubs.c for more details. |
| 233 | |
| 234 | - getservent() |
| 235 | |
| 236 | There is no /etc/services on Android. Instead the C library embeds a constant |
| 237 | list of services in its executable, which is parsed on demand by the various |
| 238 | functions that depend on it. See bionic/libc/netbsd/net/getservent.c and |
| 239 | bionic/libc/netbsd/net/services.h |
| 240 | |
| 241 | The list of services defined internally might change liberally in the future. |
| 242 | This feature is mostly historically and is very rarely used. |
| 243 | |
| 244 | The getservent() returns thread-local data. getservbyport() and getservbyname() |
| 245 | are also implemented in a similar fashion. |
| 246 | |
| 247 | - getprotoent() |
| 248 | |
| 249 | There is no /etc/protocol on Android. Bionic does not currently implement |
| 250 | getprotoent() and related functions. If we add it, it will likely be done |
| 251 | in a way similar to getservent() |
| 252 | |
| 253 | DNS resolver: |
| 254 | |
| 255 | Bionic uses a NetBSD-derived resolver library which has been modified in the following |
| 256 | ways: |
| 257 | |
| 258 | - don't implement the name-server-switch feature (a.k.a. <nsswitch.h>) |
| 259 | |
| 260 | - read /system/etc/resolv.conf instead of /etc/resolv.conf |
| 261 | |
| 262 | - read the list of servers from system properties. the code looks for |
| 263 | 'net.dns1', 'net.dns2', etc.. Each property should contain the IP address |
| 264 | of a DNS server. |
| 265 | |
| 266 | these properties are set/modified by other parts of the Android system |
| 267 | (e.g. the dhcpd daemon). |
| 268 | |
| 269 | the implementation also supports per-process DNS server list, using the |
| 270 | properties 'net.dns1.<pid>', 'net.dns2.<pid>', etc... Where <pid> stands |
| 271 | for the numerical ID of the current process. |
| 272 | |
| 273 | - when performing a query, use a properly randomized Query ID (instead of |
| 274 | a incremented one), for increased security. |
| 275 | |
| 276 | - when performing a query, bind the local client socket to a random port |
| 277 | for increased security. |
| 278 | |
| 279 | - get rid of *many* unfortunate thread-safety issues in the original code |
| 280 | |
| 281 | Bionic does *not* expose implementation details of its DNS resolver; the content |
| 282 | of <arpa/nameser.h> is intentionally blank. The resolver implementation might |
| 283 | change completely in the future. |
| 284 | |
| 285 | |
| 286 | PThread Real-Time Timers: |
| 287 | |
| 288 | timer_create(), timer_gettime(), timer_settime() and timer_getoverrun() are |
| 289 | supported. |
| 290 | |
| 291 | Bionic also now supports SIGEV_THREAD real-time timers (see timer_create()). |
| 292 | The implementation simply uses a single thread per timer, unlike GLibc which |
| 293 | uses complex heuristics to try to use the less threads possible when several |
| 294 | timers with compatible properties are used. |
| 295 | |
| 296 | This means that if your code uses a lot of SIGEV_THREAD timers, your program |
| 297 | may consume a lot of memory. However, if your program needs many of these timers, |
| 298 | it'd better handle timeout events directly instead. |
| 299 | |
| 300 | Other timers (e.g. SIGEV_SIGNAL) are handled by the kernel and use much less |
| 301 | system resources. |
| 302 | |
| 303 | |
| 304 | Binary Compatibility: |
| 305 | |
| 306 | Bionic is *not* in any way binary-compatible with the GNU C Library, ucLibc or any |
| 307 | known Linux C library. This means several things: |
| 308 | |
| 309 | - You cannot expect to build something against the GNU C Library headers and have |
| 310 | it dynamically link properly to Bionic later. |
| 311 | |
| 312 | - You should *really* use the Android toolchain to build your program against Bionic. |
| 313 | The toolchain deals with many important details that are crucial to get something |
| 314 | working properly. |
| 315 | |
| 316 | Failure to do so will usually result in the inability to run or link your program, |
| 317 | or even runtime crashes. Several random web pages on the Internet describe how you |
| 318 | can succesfully write a "hello-world" program with the ARM GNU toolchain. These |
| 319 | examples usually work by chance, if anything else, and you should not follow these |
| 320 | instructions unless you want to waste a lot of your time in the process. |
| 321 | |
| 322 | Note however that you *can* generate a binary that is built against the GNU C Library |
| 323 | headers and then statically linked to it. The corresponding executable should be able |
| 324 | to run (if it doesn't use dlopen()/dlsym()) |
| 325 | |
| 326 | Dynamic Linker: |
| 327 | |
| 328 | Bionic comes with its own dynamic linker (just like ld.so on Linux really comes from |
| 329 | GLibc). This linker does not support all the relocations generated by other GCC ARM |
| 330 | toolchains. |
| 331 | |
| 332 | C++ Exceptions Support: |
| 333 | |
| 334 | At the moment, Bionic doesn't support C++ exceptions, what this really means is the |
| 335 | following: |
| 336 | |
| 337 | - If pthread_once() is called with a C++ callback that throws an exception, |
| 338 | then the C library will keep the corresponding pthread_once_t mutex locked. |
| 339 | Any further call to pthread_once() will result in a deadlock. |
| 340 | |
| 341 | A proper implementation should be able to register a C++ exception cleanup |
| 342 | handler before the callback to properly unlock the pthread_once_t. Unfortunately |
| 343 | this requires tricky assembly code that is highly dependent on the compiler. |
| 344 | |
| 345 | This feature is not planned to be supported anytime soon. |
| 346 | |
| 347 | - The same problem may arise if you throw an exception within a callback called |
| 348 | from the C library. Fortunately, these cases are very rare in the real-world, |
| 349 | but any callback you provide to the C library should *not* throw an exception. |
| 350 | |
| 351 | - Bionic lacks a few support functions to have exception support work properly. |
| 352 | |
| 353 | Include Paths: |
| 354 | |
| 355 | The Android build system should automatically provide the necessary include paths |
| 356 | required to build against the C library headers. However, if you want to do that |
| 357 | yourself, you will need to add: |
| 358 | |
| 359 | libc/arch-$ARCH/include |
| 360 | libc/include |
| 361 | libc/kernel/common |
| 362 | libc/kernel/arch-$ARCH |
| 363 | |
| 364 | to your C include path. |