Reputation: 13
I have a question regarding Multidex in Android. Given an application that consists of multiple Dex files (classes.dex, classes2.dex), how does invocation work in the bytecode?
Since the methods IDs referenced in the invoke instructions are still 16-bit (from https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions):
invoke-direct {vC, vD, vE, vF, vG}, meth@BBBB
A: argument word count (4 bits), B: method reference index (16 bits), C..G: argument registers (4 bits each)
So how does a method defined in classes2.dex reference or more importantly invoke a method defined in classes.dex or classes3.dex?
Regards,
Loran
Upvotes: 0
Views: 191
Reputation: 306
According to: "Basically, how does it know the code offset and how does it know which dex file the method is located?"
Please consider that these dex files are "intermediate" files interpreted or compiled by the VM (ART) or a compiler (dex2oat). This means, there is no "offset" or relocation table as in ELF or MACH-O files.
As Antimony explained the id in the invoke call is just an index in the ref table of a single dex file. This ref table maps the class name, method name and signature to the index. In the end the VM loads all dex files and creates table of classes and methods in memory. If invoke(..) get's called it checks the ref table of the dex file and fetches the taret method, class name and signature. Now it searches for those three values in memory and invokes.
Please consider that you have to deal with an VM that translates intermediate language into architecture dependend code at: * Runtime (intepreter mode) * Runtime but caches if neccessary ("Just in time") * During installation of your app ("Ahead of time")
Please check: https://source.android.com/devices/tech/dalvik/configure
Here you have another example but using the JVM (means java opcodes):
public final class org.chickenhook.binderfuzzy.BuildConfig
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
#1 = Methodref #6.#32 // java/lang/Object."<init>":()V
#2 = String #33 // true
#3 = Methodref #34.#35 // java/lang/Boolean.parseBoolean:(Ljava/lang/String;)Z
#4 = Fieldref #5.#36 // org/chickenhook/binderfuzzy/BuildConfig.DEBUG:Z
#5 = Class #37 // org/chickenhook/binderfuzzy/BuildConfig
#6 = Class #38 // java/lang/Object
#7 = Utf8 DEBUG
#8 = Utf8 Z
#9 = Utf8 APPLICATION_ID
#10 = Utf8 Ljava/lang/String;
#11 = Utf8 ConstantValue
Here you can see the "constant pool". At INDEX #3 you have the methodref to java/lang/Boolean.parseBoolean:(Ljava/lang/String;)Z (as I told Class, method and signature). The VM, jit or aot reads this value and checks in memory at runtime for the class in a table where all classes of all intermediate files are represented.
The important thing is that the classpath points to all neccessary intermediates. Otherwise you'll get a "ClassNotFound" exception. It remembers a li'l bit to runtime linkers searching for symbol names and filling the relocation table of a binary yes, but it's technically complete differently.
Upvotes: 1
Reputation: 39461
The method index is just an index to a table included elsewhere in the dex file, which gives the method name, type signature, and class. That method could be defined anywhere, even in another dex file or be part of one of the system classes. Any given dex file can only reference up to 65k methods, but multiple dex files can reference different sets of methods due to including their own tables of method descriptors.
Upvotes: 0