0

I have inserted some new functionality in the Android Runtime (ART), and now I want to expose it to the outside world via an interface. As it is native code, I will be using the JNI interface to call this new functionality, in a similar way with the Garbage Collector functionality: Runtime.getInstance().gc().

However, I don't care in building a new SDK that could be used by an IDE, as I will be manually injecting bytecode to .dex files that will make the call.

I have edited the Runtime.java in libcore/luni, and java_lang_Runtime.cc in art in a similar way with the gc() function. I am generating the new libart.so and core-libart.jar and I flash them on a device. However, when I try to reboot the device, I get the message:

Failed to register native method java.lang.Runtime.myMethod()V in /system/framework/core-libart.jar
...
----- class 'Ljava/lang/Runtime;' cl=0x0 -----
vtable (24 entries, 11 in super):
// 24 entries are listed here. My entry is missing.
...

In the Runtime.java I register the native method and supress some warnings on a full build using @hide. eg,

/** @hide */
public native void myMethod();

In the java_lang_Runtime.cc, I define the function (that will call the ART internal stuff) and register it to the gMethods[] array using a macro. eg,

static void Runtime_myMethod(JNIEnv*, jclass) {  
// body
}

NATIVE_METHOD(Runtime, myMethod, "()V")

The device is on a bootloop. Are there any other files that I should have edited? Should I build extra modules, or send any other files on the device?

BTW, I do NOT want to build a new SDK as for calling myMethod I will inject Dalvik bytecode to an APK file. Basically I will get the Runtime instance, and then call the method.

Paschalis
  • 11,929
  • 9
  • 52
  • 82

1 Answers1

0

The problem:

Android ships the factory images with framework components being pre-optimised. The file boot.oat contains pre-optimised code (or odex code), which can be accessed by reading pointers contained in the boot.art file.

These two files contain code only for the boot classpath. The rest of the framework, and the rest of the system applications (in app and priv-app) folders have standalone odex files.

During this pre-optimisation phase, the dex code is taken out of a framework module (a jar, or an apk), is compiled using dex2oat, and the resulting code resides in the files I just mentioned.

Some of the things I 've tried:

Shipping just the libart.so and core-libart.jar does not work, even though the latter contains the dex code. This is because the runtime is still attempting to read that information from boot.oat.

By modifying the device configuration to do the pre-optimisation, the aosp build system can generate boot.oat|art and the rest of the odex files (more here). I expected that flashing all these should work, but it didn't. (at least for the marshmallow-release branch, on a nexus6)

I 've tried flashing the whole aosp-generated build, and it didn't work, even with a custom kernel that I 've build with the security features (verity) disabled.

I am sure that some of these should have worked, but there must be something else that should be considered during building the OS.

Solution:

The last resort, was deodexing and thankfully it worked. I have written a small script that does it for the Nexus 6 on Marshmallow.

The script, during a 1st stage, it takes out all the oat/odex code from relevant places, and de-optimises it to dex code, thanks to the oat2dex and smali projects. On a 2nd stage, it packs the dex code back, in the framework modules (jars/apks).

Shipping a whole new core-libart.jar to the device still does not work (not sure why), but tinkering the dex files before packing them into the modules does the trick! :))

The libart.so can now find Runtime.myMethod(), which can be invoked by an application (smali tinkering again) to run my code inside ART.

Paschalis
  • 11,929
  • 9
  • 52
  • 82