Android Multidex 方法调用

Android Multidex method invocation

我对 Android 中的 Multidex 有疑问。给定一个由多个 Dex 文件(classes.dex、classes2.dex)组成的应用程序,调用在字节码中是如何工作的?

由于调用指令中引用的方法 ID 仍然是 16 位的(来自 https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions):

直接调用 {vC, vD, vE, vF, vG}, meth@BBBB

A:参数字数(4位), B:方法参考索引(16位), C..G:参数寄存器(每个 4 位)

那么 classes2.dex 中定义的方法如何引用或更重要地调用 classes.dex 或 classes3.dex 中定义的方法?

此致,

罗兰

方法索引只是对包含在 dex 文件其他地方的 table 的索引,它给出了方法名称、类型签名和 class。该方法可以在任何地方定义,甚至可以在另一个 dex 文件中定义,或者是系统 classes 之一的一部分。任何给定的 dex 文件最多只能引用 65k 个方法,但是多个 dex 文件可以引用不同的方法集,因为它们包含自己的 table 方法描述符。

根据:"Basically, how does it know the code offset and how does it know which dex file the method is located?"

请考虑这些 dex 文件是 "intermediate" 由 VM (ART) 或编译器 (dex2oat) 解释或编译的文件。这意味着,没有 ELF 或 MACH-O 文件中的 "offset" 或重定位 table。

正如 Antimony 所解释的那样,invoke 调用中的 id 只是单个 dex 文件的 ref table 中的一个索引。此 ref table 将 class 名称、方法名称和签名映射到索引。最后,VM 加载所有 dex 文件并在内存中创建 table 个 class es 和方法。如果 invoke(..) get 被调用,它会检查 dex 文件的 ref table 并获取 taret 方法、class 名称和签名。现在它在内存中搜索这三个值并调用。

请考虑到您必须处理将中间语言转换为体系结构相关代码的 VM,网址为: * 运行时(解释器模式) * 运行时但必要时缓存 ("Just in time") * 在安装您的应用程序期间 ("Ahead of time")

请检查:https://source.android.com/devices/tech/dalvik/configure

这里有另一个示例,但使用了 JVM(意味着 java 操作码):

    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

这里可以看到"constant pool"。 在 INDEX #3 处,您有 java/lang/Boolean.parseBoolean:(Ljava/lang/String;)Z 的方法引用(正如我告诉 Class、方法和签名)。 VM、jit 或 aot 读取此值并在运行时在内存中检查 table 中的 class,其中表示所有中间文件的所有 classes。

重要的是class路径指向所有必要的中间体。否则你会得到一个 "ClassNotFound" 异常。它记得运行时链接器搜索符号名称并填充二进制文件的重定位 table 是的,但它在技术上完全不同。