HOWTO: Calling into Mozilla C internals from JavaScript
July 13, 2012 § 10 Comments
I have been using js-ctypes to call into libxul from JavaScript, to I figured I should share the recipe.
Several versions ago, Firefox, Thunderbird and other applications using the Mozilla Platform (aka Gecko) started shipping with js-ctypes, a very powerful foreign function interface for JavaScript. Using this mechanism, privileged JavaScript code can call into native libraries. Since then, js-ctypes has been used to implement all sorts of tools, from interaction with MacOS X GUI to direct calls to the Linux kernel to scripting Dalvik, the Android JVM.
One of the interesting and less-known features of js-ctypes is that you can also use it to call into Gecko internals. You can use it to script a number of previously inaccessible Gecko features. I personally use it to permit access the Unicode conversion primitives of Gecko, and to help me with serialization of js-ctypes values, but there is no reason to not use it for other things, such as testing native libxul code from JavaScript.
Caveat This is a hack. Doing this may void your warranty. Also, if I were reviewing an add-on that depends on this, I would reject it. You have been warned.
Let’s see how. At the time of this writing, the following steps require a recent Nightly build of Firefox/Thunderbird/… but the feature should be available shortly to released versions.
Import js-ctypes and OS.Constants.
Components.utils.import("resource://gre/modules/ctypes.jsm");
Components.classes["@mozilla.org/net/osfileconstantsservice;1"].
getService(Components.interfaces.nsIOSFileConstantsService).
init();
If your code is executed in a worker thread, this extract is not required.
If you are running an older version of Firefox (i.e. anything except a recent Nightly), you will need to replace the references to nsIIOSFileConstantsService/OS.Constants with more verbose calls to xpcom, and this will work only on the main thread.
Open libxul
libxul is the native library that contains the Gecko internals. To open it,
let libxul = ctypes.open(OS.Constants.Path.libxul);
Play with it!
You are basically done. Now, you can import any C function (or a number of C++ functions, with a little more effort) to JavaScript. Let’s see how to import function DumpJSStack, for instance. This function is part of the JavaScript Virtual Machine. As its name implies, it prints the JavaScript stack. Not very useful for us at this stage, as JavaScript can print its own stack quite easily, but a good way to test our toy.
The definition of DumpJSStack is the following:
extern "C" void DumpJSStack()
In other words, this function takes no argument, returns nothing and is designed to be called from C (or from js-ctypes). This makes it an ideal candidate to import it with js-ctypes:
let dumpJSStack = libxul.declare("DumpJSStack",
ctypes.default_abi,
/*return*/ ctypes.void_t);
We can now use the function as if it were a regular JS function:
dumpJSStack();
(prints some information about how you got to this point – works only from the main thread)
Play with it: printf_stderr
We can similarly import function printf_stderr, which is often quite useful for debugging:
extern "C" void printf_stderr(char* fmt, ...);
To import it:
let printf_stderr = libxul.declare("printf_stderr",
ctypes.default_abi,
/*return*/ ctypes.void_t,
/*fmt*/ ctypes.char.ptr,
/* ... */ "...");
More features
There are many more features that can be accessed through libxul and that go beyond the scope of this blog entry. Let us simply list a few of them.
What about:
- walking the native stack using
NS_StackWalk? see file nsStackWalk.h for more details; - interacting with other processes using
PR_CreateProcess,PR_WaitProcess,PR_KillProcess? see prprocess.h for more details; - using the built-in cryptographic features of the Mozilla Platform? see nss/* for more details;
and certainly more…
Note that, by opposition to XPCOM-based calls, most js-ctypes based calls work from all threads.
Great Post!
It was not considered safe to use NSS from js-ctypes becasue you could not check for NSSShutdown. Recently, nsNSSShutdown was exported: http://mxr.mozilla.org/mozilla-central/source/security/manager/ssl/src/Makefile.in#93
So it might be safer now, but most likely will still be discouraged. That being said, I use js-ctypes (via the old WeaveCrypto + additional APIs I added) in the DOMCrypt extension:
https://github.com/daviddahl/domcrypt/blob/master/extension/domcrypt/content/domcrypt_worker.js#L535
I am currently working on a native / scriptable interface for KeyPair generation, encrypt, decrypt, sign, verify, hash and MAC here:
https://bugzilla.mozilla.org/show_bug.cgi?id=649154
https://wiki.mozilla.org/DOMCryptInternalAPI
s/Dvorak/Dalvik/
Otherwise, nice hack!
Oops, thanks
s/dvorak/dalvik
I knew there was something weird when I wrote that
Thanks.
Calling nss methods has already being done using js-ctypes in old weave project:
http://mxr.mozilla.org/mozilla-central/source/services/crypto/modules/WeaveCrypto.js#126
And this code seems to be still used for Sync.
While true, It is not recommended. That code is being replaced. https://bugzilla.mozilla.org/show_bug.cgi?id=743070
And about processes. You should definitely take a look at:
http://hg.mozilla.org/ipccode
This is the pure javascript implementation of subprocess library used by enigma thunderbird addon.
They prefered using OS native methods instead of NS ones.
Although this is cool hack value, I think it’s dangerous to advertise this in public. Since libxul changes every six weeks, shipping an extension that uses jsctypes to call into libxul is an almost certain way to cause grief when the Firefox updates the next time.
Good point. I’ll add something along these lines.